feat(01-03): add slide-out panel, item form, category picker, and collection page

- CategoryPicker combobox with search, select, and inline create
- SlideOutPanel with backdrop, escape key, and slide animation
- ItemForm with all fields, validation, dollar-to-cents conversion
- Root layout with TotalsBar, panel, confirm dialog, and floating add button
- Collection page with card grid grouped by category, empty state

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-14 22:46:24 +01:00
parent b099a47eb4
commit 12fd14ff41
5 changed files with 736 additions and 2 deletions

View File

@@ -1,14 +1,64 @@
import { createRootRoute, Outlet } from "@tanstack/react-router";
import "../app.css";
import { TotalsBar } from "../components/TotalsBar";
import { SlideOutPanel } from "../components/SlideOutPanel";
import { ItemForm } from "../components/ItemForm";
import { ConfirmDialog } from "../components/ConfirmDialog";
import { useUIStore } from "../stores/uiStore";
export const Route = createRootRoute({
component: RootLayout,
});
function RootLayout() {
const panelMode = useUIStore((s) => s.panelMode);
const editingItemId = useUIStore((s) => s.editingItemId);
const openAddPanel = useUIStore((s) => s.openAddPanel);
const closePanel = useUIStore((s) => s.closePanel);
const isOpen = panelMode !== "closed";
return (
<div className="min-h-screen bg-gray-50">
<TotalsBar />
<Outlet />
{/* Slide-out Panel */}
<SlideOutPanel
isOpen={isOpen}
onClose={closePanel}
title={panelMode === "add" ? "Add Item" : "Edit Item"}
>
{panelMode === "add" && <ItemForm mode="add" />}
{panelMode === "edit" && (
<ItemForm mode="edit" itemId={editingItemId} />
)}
</SlideOutPanel>
{/* Confirm Delete Dialog */}
<ConfirmDialog />
{/* Floating Add Button */}
<button
type="button"
onClick={openAddPanel}
className="fixed bottom-6 right-6 z-20 w-14 h-14 bg-blue-600 hover:bg-blue-700 text-white rounded-full shadow-lg hover:shadow-xl transition-all flex items-center justify-center"
title="Add new item"
>
<svg
className="w-6 h-6"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M12 4v16m8-8H4"
/>
</svg>
</button>
</div>
);
}