feat(phase2): planned badge, Cmd+K palette, per-project dashboard cards

- Project.status (planned/wired/active) + seed updates
- ProjectBadge component
- CommandPalette (⌘K) wired in PanelShell with project + nav + sign-out
- Dashboard '/' shows per-project cards + KPI strip
- Added doner312 to seeded projects
This commit is contained in:
Semih
2026-05-13 10:54:07 +00:00
parent 7b83ef8f11
commit e455ebbfce
14 changed files with 782 additions and 57 deletions

View File

@@ -0,0 +1,94 @@
"use client";
import { useEffect, useState } from "react";
import { useRouter } from "next/navigation";
import {
CommandDialog,
CommandEmpty,
CommandGroup,
CommandInput,
CommandItem,
CommandList,
CommandSeparator,
} from "@/components/ui/command";
import {
ActivityIcon,
FolderIcon,
LayoutDashboardIcon,
LogOutIcon,
ScrollTextIcon,
Settings2Icon,
TerminalIcon,
} from "lucide-react";
import { signOut } from "@/lib/auth-client";
type ProjectLite = { key: string; name: string; status: string };
export function CommandPalette({ projects }: { projects: ProjectLite[] }) {
const router = useRouter();
const [open, setOpen] = useState(false);
useEffect(() => {
const onKey = (e: KeyboardEvent) => {
if ((e.metaKey || e.ctrlKey) && e.key === "k") {
e.preventDefault();
setOpen((v) => !v);
}
};
window.addEventListener("keydown", onKey);
return () => window.removeEventListener("keydown", onKey);
}, []);
const go = (path: string) => {
setOpen(false);
router.push(path);
};
return (
<CommandDialog open={open} onOpenChange={setOpen} title="Command palette" description="Quick navigation">
<CommandInput placeholder="Jump to project, page, or action…" />
<CommandList>
<CommandEmpty>No match.</CommandEmpty>
<CommandGroup heading="Navigation">
<CommandItem onSelect={() => go("/")}><LayoutDashboardIcon /> Overview</CommandItem>
<CommandItem onSelect={() => go("/projects")}><FolderIcon /> Projects</CommandItem>
<CommandItem onSelect={() => go("/operations")}><TerminalIcon /> Operations</CommandItem>
<CommandItem onSelect={() => go("/events")}><ActivityIcon /> Events</CommandItem>
<CommandItem onSelect={() => go("/audit")}><ScrollTextIcon /> Audit</CommandItem>
<CommandItem onSelect={() => go("/settings")}><Settings2Icon /> Settings</CommandItem>
</CommandGroup>
<CommandSeparator />
<CommandGroup heading="Projects">
{projects.map((p) => (
<CommandItem
key={p.key}
keywords={[p.key, p.name, p.status]}
onSelect={() => go(`/projects/${p.key}`)}
>
<FolderIcon />
<span>{p.name}</span>
<span className="ml-auto text-xs text-muted-foreground">{p.status}</span>
</CommandItem>
))}
</CommandGroup>
<CommandSeparator />
<CommandGroup heading="Account">
<CommandItem
onSelect={async () => {
setOpen(false);
await signOut();
router.replace("/login");
}}
>
<LogOutIcon /> Sign out
</CommandItem>
</CommandGroup>
</CommandList>
</CommandDialog>
);
}