Initial curated Unraid-AppOps-Gateway source from 3bd6f6fee310125bfbb4d4650389a817f498724d

This commit is contained in:
Public Source Publisher committed 2026-10-01 01:35:57 +02:00
commit 1dfbca08a1
528 files changed
+131054

No files matched your search

+142
View File
@@ -0,0 +1,142 @@
"use client";
import * as React from "react";
import { useNavigate } from "react-router-dom";
import { AppWindow, Box, ShieldCheck, Zap } from "lucide-react";
import { appopsClient } from "@/api/appopsClient";
import {
CommandDialog,
CommandEmpty,
CommandGroup,
CommandInput,
CommandItem,
CommandList,
CommandSeparator,
CommandShortcut,
} from "@/components/ui/command";
import { buildCommandPaletteGroups } from "@/lib/commandPaletteNavigation";
import { isCommandPaletteShortcut } from "@/lib/commandPaletteShortcut";
import { navigationGroups } from "@/lib/navigationGroups";
const commandPaletteGroups = buildCommandPaletteGroups(navigationGroups);
export function CommandPalette() {
const [open, setOpen] = React.useState(false);
const [liveItems, setLiveItems] = React.useState<{
apps: Awaited<ReturnType<typeof appopsClient.getApps>>;
containers: Awaited<ReturnType<typeof appopsClient.getContainers>>;
policies: Awaited<ReturnType<typeof appopsClient.getPolicies>>;
actions: Awaited<ReturnType<typeof appopsClient.getActionRegistry>>["items"];
}>({ apps: [], containers: [], policies: [], actions: [] });
const navigate = useNavigate();
React.useEffect(() => {
const down = (e: KeyboardEvent) => {
if (isCommandPaletteShortcut(e)) {
e.preventDefault();
setOpen((open) => !open);
}
};
document.addEventListener("keydown", down);
return () => document.removeEventListener("keydown", down);
}, []);
React.useEffect(() => {
if (!open) return;
let active = true;
Promise.allSettled([
appopsClient.getApps(),
appopsClient.getContainers(),
appopsClient.getPolicies(),
appopsClient.getActionRegistry(),
]).then(([apps, containers, policies, actions]) => {
if (!active) return;
setLiveItems({
apps: apps.status === "fulfilled" ? apps.value : [],
containers: containers.status === "fulfilled" ? containers.value : [],
policies: policies.status === "fulfilled" ? policies.value : [],
actions: actions.status === "fulfilled" ? actions.value.items : [],
});
});
return () => { active = false; };
}, [open]);
const runCommand = (command: () => void) => {
setOpen(false);
command();
};
return (
<CommandDialog open={open} onOpenChange={setOpen}>
<CommandInput placeholder="Typ een commando of zoek..." className="border-none focus:ring-0" />
<CommandList className="bg-slate-950 text-slate-200 border-t border-slate-800">
<CommandEmpty>Geen resultaten gevonden.</CommandEmpty>
{commandPaletteGroups.map((group, groupIndex) => (
<React.Fragment key={group.label}>
{groupIndex > 0 && <CommandSeparator className="bg-slate-800" />}
<CommandGroup heading={group.label}>
{group.items.map((item) => (
<CommandItem
key={item.path}
value={`${item.searchLabel} ${group.label} ${item.path}`}
onSelect={() => runCommand(() => navigate(item.path))}
>
<item.icon className="mr-2 h-4 w-4" />
<span>{item.searchLabel}</span>
</CommandItem>
))}
</CommandGroup>
</React.Fragment>
))}
{liveItems.apps.length > 0 && (
<>
<CommandSeparator className="bg-slate-800" />
<CommandGroup heading="Apps">
{liveItems.apps.map((app) => (
<CommandItem key={`app-${app.id}`} value={`${app.name} ${app.id} ${app.category} app`} onSelect={() => runCommand(() => navigate(`/apps/${encodeURIComponent(app.id)}`))}>
<AppWindow className="mr-2 h-4 w-4" /><span>{app.name}</span><CommandShortcut>{app.status}</CommandShortcut>
</CommandItem>
))}
</CommandGroup>
</>
)}
{liveItems.containers.length > 0 && (
<>
<CommandSeparator className="bg-slate-800" />
<CommandGroup heading="Containers">
{liveItems.containers.map((container) => (
<CommandItem key={`container-${container.id}`} value={`${container.name} ${container.image} ${container.appId || ""} container`} onSelect={() => runCommand(() => navigate(`/containers?query=${encodeURIComponent(container.name)}`))}>
<Box className="mr-2 h-4 w-4" /><span>{container.name}</span><CommandShortcut>{container.state}</CommandShortcut>
</CommandItem>
))}
</CommandGroup>
</>
)}
{liveItems.actions.length > 0 && (
<>
<CommandSeparator className="bg-slate-800" />
<CommandGroup heading="Acties">
{liveItems.actions.map((action) => (
<CommandItem key={`action-${action.id}-${action.target}`} value={`${action.label} ${action.id} ${action.target} ${action.category}`} onSelect={() => runCommand(() => navigate(`/command-center?query=${encodeURIComponent(action.id)}`))}>
<Zap className="mr-2 h-4 w-4" /><span>{action.label}</span><CommandShortcut>{action.policyDecision}</CommandShortcut>
</CommandItem>
))}
</CommandGroup>
</>
)}
{liveItems.policies.length > 0 && (
<>
<CommandSeparator className="bg-slate-800" />
<CommandGroup heading="Policies">
{liveItems.policies.map((policy) => (
<CommandItem key={`policy-${policy.id}`} value={`${policy.action} ${policy.target} ${policy.reason || ""} policy`} onSelect={() => runCommand(() => navigate(`/policies?query=${encodeURIComponent(policy.id)}`))}>
<ShieldCheck className="mr-2 h-4 w-4" /><span>{policy.action}</span><CommandShortcut>{policy.manual}</CommandShortcut>
</CommandItem>
))}
</CommandGroup>
</>
)}
</CommandList>
</CommandDialog>
);
}