feat(03-02): setup list page, detail page, and item picker

- Create SetupCard component with name, item count, weight, cost display
- Build setups list page with inline create form and grid layout
- Build setup detail page with category-grouped items and sticky totals bar
- Create ItemPicker component in SlideOutPanel with category-grouped checkboxes
- Add onRemove prop to ItemCard for non-destructive setup item removal
- Setup detail has delete confirmation dialog with collection safety note
- Empty states for both setup list and setup detail pages

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-15 12:50:54 +01:00
parent 86a7a0def1
commit 67099551d0
5 changed files with 552 additions and 8 deletions

View File

@@ -9,6 +9,7 @@ interface ItemCardProps {
categoryName: string;
categoryEmoji: string;
imageFilename: string | null;
onRemove?: () => void;
}
export function ItemCard({
@@ -19,6 +20,7 @@ export function ItemCard({
categoryName,
categoryEmoji,
imageFilename,
onRemove,
}: ItemCardProps) {
const openEditPanel = useUIStore((s) => s.openEditPanel);
@@ -26,8 +28,30 @@ export function ItemCard({
<button
type="button"
onClick={() => openEditPanel(id)}
className="w-full text-left bg-white rounded-xl border border-gray-100 hover:border-gray-200 hover:shadow-sm transition-all overflow-hidden"
className="relative w-full text-left bg-white rounded-xl border border-gray-100 hover:border-gray-200 hover:shadow-sm transition-all overflow-hidden group"
>
{onRemove && (
<span
role="button"
tabIndex={0}
onClick={(e) => {
e.stopPropagation();
onRemove();
}}
onKeyDown={(e) => {
if (e.key === "Enter" || e.key === " ") {
e.stopPropagation();
onRemove();
}
}}
className="absolute top-2 right-2 z-10 w-6 h-6 flex items-center justify-center rounded-full bg-gray-100/80 text-gray-400 hover:bg-red-100 hover:text-red-500 opacity-0 group-hover:opacity-100 transition-all cursor-pointer"
title="Remove from setup"
>
<svg className="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
</svg>
</span>
)}
{imageFilename && (
<div className="aspect-[4/3] bg-gray-50">
<img