Add shared UI primitives, contexts, and reusable components

Add shadcn components: avatar, breadcrumb, checkbox, collapsible,
command, dialog, dropdown-menu, label, popover, scroll-area, sheet,
skeleton, tabs, textarea, tooltip. Add shared components: BreadcrumbBar,
CommandPalette, CompanySwitcher, CommentThread, EmptyState, EntityRow,
FilterBar, InlineEditor, MetricCard, PageSkeleton, PriorityIcon,
PropertiesPanel, StatusIcon, SidebarNavItem/Section. Add contexts for
breadcrumbs, dialogs, and side panels. Add keyboard shortcut hook and
utility helpers. Update layout, sidebar, and main app shell.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Forgotten
2026-02-17 09:57:00 -06:00
parent 22e7930d0b
commit fad1bd27ce
42 changed files with 2534 additions and 69 deletions

View File

@@ -1,43 +1,52 @@
import { useState, useCallback } from "react";
import { Outlet } from "react-router-dom";
import { Sidebar } from "./Sidebar";
import { useCompany } from "../context/CompanyContext";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import { BreadcrumbBar } from "./BreadcrumbBar";
import { PropertiesPanel } from "./PropertiesPanel";
import { CommandPalette } from "./CommandPalette";
import { NewIssueDialog } from "./NewIssueDialog";
import { useDialog } from "../context/DialogContext";
import { usePanel } from "../context/PanelContext";
import { useKeyboardShortcuts } from "../hooks/useKeyboardShortcuts";
import { cn } from "../lib/utils";
export function Layout() {
const { companies, selectedCompanyId, setSelectedCompanyId } = useCompany();
const [sidebarOpen, setSidebarOpen] = useState(true);
const { openNewIssue } = useDialog();
const { panelContent, closePanel } = usePanel();
const toggleSidebar = useCallback(() => setSidebarOpen((v) => !v), []);
const togglePanel = useCallback(() => {
if (panelContent) closePanel();
}, [panelContent, closePanel]);
useKeyboardShortcuts({
onNewIssue: () => openNewIssue(),
onToggleSidebar: toggleSidebar,
onTogglePanel: togglePanel,
});
return (
<div className="flex h-screen bg-background text-foreground">
<Sidebar />
<div className="flex-1 overflow-auto">
<header className="bg-card border-b border-border px-8 py-3 flex items-center justify-end">
<label className="text-xs text-muted-foreground mr-2">Company</label>
<Select
value={selectedCompanyId ?? ""}
onValueChange={(value) => setSelectedCompanyId(value)}
>
<SelectTrigger className="w-48 h-8 text-sm">
<SelectValue placeholder="No companies" />
</SelectTrigger>
<SelectContent>
{companies.map((company) => (
<SelectItem key={company.id} value={company.id}>
{company.name}
</SelectItem>
))}
</SelectContent>
</Select>
</header>
<main className="p-8">
<Outlet />
</main>
<div
className={cn(
"transition-all duration-200 ease-in-out shrink-0 overflow-hidden",
sidebarOpen ? "w-60" : "w-0"
)}
>
<Sidebar />
</div>
<div className="flex-1 flex flex-col overflow-hidden">
<BreadcrumbBar />
<div className="flex flex-1 overflow-hidden">
<main className="flex-1 overflow-auto p-6">
<Outlet />
</main>
<PropertiesPanel />
</div>
</div>
<CommandPalette />
<NewIssueDialog />
</div>
);
}