- Full detail page at /items/:id with hero image, name, spec badges, notes, product link - Edit mode toggle: read-only by default, editable inputs when Edit clicked - Save persists via useUpdateItem, Cancel reverts to read-only - Duplicate and Delete actions via existing hooks/dialogs - Back link to /collection, loading shimmer, error state - CategoryPicker and ImageUpload in edit mode
242 lines
12 KiB
Markdown
242 lines
12 KiB
Markdown
---
|
|
phase: 21-item-catalog-detail-pages
|
|
plan: 01
|
|
type: execute
|
|
wave: 1
|
|
depends_on: []
|
|
files_modified:
|
|
- src/client/routes/items/$itemId.tsx
|
|
- src/client/routes/global-items/$globalItemId.tsx
|
|
autonomous: true
|
|
requirements: [DETAIL-01, DETAIL-02, DETAIL-03]
|
|
must_haves:
|
|
truths:
|
|
- "Navigating to /items/:id renders a full detail page with all item data"
|
|
- "Item detail page shows hero image, name, weight/price/category badges, notes, quantity, purchase price, product link"
|
|
- "Clicking Edit toggles fields to editable inputs; Save persists via updateItem mutation"
|
|
- "Navigating to /global-items/:id shows enhanced catalog page with Add to Collection button"
|
|
- "Catalog detail page shows hero image, brand, model, specs, description, owner count"
|
|
artifacts:
|
|
- path: "src/client/routes/items/$itemId.tsx"
|
|
provides: "Private item detail page with edit mode"
|
|
exports: ["Route"]
|
|
- path: "src/client/routes/global-items/$globalItemId.tsx"
|
|
provides: "Enhanced catalog detail page with Add to Collection button"
|
|
exports: ["Route"]
|
|
key_links:
|
|
- from: "src/client/routes/items/$itemId.tsx"
|
|
to: "useItem hook"
|
|
via: "useItem(Number(itemId))"
|
|
pattern: "useItem\\(Number"
|
|
- from: "src/client/routes/items/$itemId.tsx"
|
|
to: "useUpdateItem mutation"
|
|
via: "updateItem.mutate"
|
|
pattern: "useUpdateItem"
|
|
- from: "src/client/routes/global-items/$globalItemId.tsx"
|
|
to: "Add to Collection button"
|
|
via: "button element"
|
|
pattern: "Add to Collection"
|
|
---
|
|
|
|
<objective>
|
|
Create the private item detail page at `/items/:id` with edit mode toggle, and enhance the existing catalog detail page at `/global-items/:id` with an "Add to Collection" button and improved layout.
|
|
|
|
Purpose: These are the navigation targets that card click handlers will point to in Plan 03. They must exist before rewiring.
|
|
Output: Two route files — one new (`items/$itemId.tsx`), one enhanced (`global-items/$globalItemId.tsx`)
|
|
</objective>
|
|
|
|
<execution_context>
|
|
@$HOME/.claude/get-shit-done/workflows/execute-plan.md
|
|
@$HOME/.claude/get-shit-done/templates/summary.md
|
|
</execution_context>
|
|
|
|
<context>
|
|
@.planning/PROJECT.md
|
|
@.planning/ROADMAP.md
|
|
@.planning/STATE.md
|
|
@.planning/phases/21-item-catalog-detail-pages/21-CONTEXT.md
|
|
@.planning/phases/21-item-catalog-detail-pages/21-RESEARCH.md
|
|
|
|
@src/client/routes/global-items/$globalItemId.tsx
|
|
@src/client/hooks/useItems.ts
|
|
@src/client/hooks/useGlobalItems.ts
|
|
@src/client/hooks/useFormatters.ts
|
|
@src/client/components/ItemForm.tsx
|
|
@src/client/components/CategoryPicker.tsx
|
|
@src/client/components/ImageUpload.tsx
|
|
|
|
<interfaces>
|
|
<!-- Key hooks the executor needs -->
|
|
|
|
From src/client/hooks/useItems.ts:
|
|
```typescript
|
|
export function useItem(id: number | null): UseQueryResult<ItemWithCategory>;
|
|
export function useUpdateItem(): UseMutationResult<ItemWithCategory, Error, { id: number } & Partial<CreateItem>>;
|
|
export function useDeleteItem(): UseMutationResult<{ success: boolean }, Error, number>;
|
|
export function useDuplicateItem(): UseMutationResult<Item, Error, number>;
|
|
|
|
interface ItemWithCategory {
|
|
id: number; name: string; weightGrams: number | null; priceCents: number | null;
|
|
quantity: number; categoryId: number; notes: string | null; productUrl: string | null;
|
|
imageFilename: string | null; createdAt: string; updatedAt: string;
|
|
categoryName: string; categoryIcon: string;
|
|
}
|
|
```
|
|
|
|
From src/client/hooks/useGlobalItems.ts:
|
|
```typescript
|
|
export function useGlobalItem(id: number): UseQueryResult<GlobalItemWithDetails>;
|
|
// GlobalItemWithDetails includes: id, brand, model, description, category, weightGrams, priceCents, imageUrl, ownerCount
|
|
```
|
|
|
|
From src/client/hooks/useFormatters.ts:
|
|
```typescript
|
|
export function useFormatters(): { weight: (g: number) => string; price: (cents: number) => string; };
|
|
```
|
|
|
|
From src/client/components/ItemForm.tsx:
|
|
```typescript
|
|
interface FormData {
|
|
name: string; weightGrams: string; priceDollars: string; quantity: number;
|
|
categoryId: number; notes: string; productUrl: string; imageFilename: string | null;
|
|
}
|
|
```
|
|
</interfaces>
|
|
</context>
|
|
|
|
<tasks>
|
|
|
|
<task type="auto">
|
|
<name>Task 1: Create private item detail page with edit mode toggle</name>
|
|
<files>src/client/routes/items/$itemId.tsx</files>
|
|
<read_first>
|
|
- src/client/routes/global-items/$globalItemId.tsx (existing detail page pattern to follow)
|
|
- src/client/components/ItemForm.tsx (form fields, validation logic, FormData interface to reuse)
|
|
- src/client/hooks/useItems.ts (useItem, useUpdateItem, useDeleteItem, useDuplicateItem hooks)
|
|
- src/client/hooks/useFormatters.ts (weight/price formatting)
|
|
- src/client/components/CategoryPicker.tsx (category selection for edit mode)
|
|
- src/client/components/ImageUpload.tsx (image upload for edit mode)
|
|
- src/client/stores/uiStore.ts (openConfirmDelete for delete action, openExternalLink for product links)
|
|
</read_first>
|
|
<action>
|
|
Create `src/client/routes/items/$itemId.tsx` with `createFileRoute("/items/$itemId")`.
|
|
|
|
Per D-01: Route at `/items/:id`, full page showing all item data.
|
|
Per D-02: Hero section — large product image (or placeholder icon using LucideIcon with categoryIcon), item name as h1 title, key specs as badges (weight via `useFormatters().weight`, price via `useFormatters().price`, category name).
|
|
Per D-03: Personal section below hero — notes (rendered as paragraph), quantity (if > 1), purchase price (priceCents formatted), product link (as external link button), image. For reference items, COALESCE merge is transparent — useItem already returns merged data, no special handling needed.
|
|
Per D-05: Page is read-only by default. Clean aesthetic, spacious gallery-like layout.
|
|
Per D-06: Back navigation at top — `<Link to="/collection">← Back to collection</Link>`.
|
|
Per D-07: Actions section — "Edit" button (top-right), "Duplicate" button (uses `useDuplicateItem`), "Delete" button (uses `useUIStore.openConfirmDelete` which triggers the existing ConfirmDialog).
|
|
|
|
Per D-04: Edit mode toggle. Local `useState<boolean>(false)` for `isEditing`. When "Edit" clicked, `setIsEditing(true)`. In edit mode:
|
|
- Name becomes text input
|
|
- Weight becomes number input (display in grams, store as string like ItemForm does)
|
|
- Price becomes dollar input (convert cents to dollars for display, back to cents on save)
|
|
- Quantity becomes number input
|
|
- Category becomes CategoryPicker component
|
|
- Notes becomes textarea
|
|
- Product URL becomes text input
|
|
- Image becomes ImageUpload component
|
|
- Show Save and Cancel buttons. Save calls `useUpdateItem().mutate({ id, name, weightGrams: parsed, priceCents: parsed, quantity, categoryId, notes, productUrl, imageFilename })` with `onSuccess: () => setIsEditing(false)`. Cancel calls `setIsEditing(false)`.
|
|
- Initialize form state from item data when entering edit mode (in the toggle handler or via useEffect when isEditing becomes true AND item data is available).
|
|
|
|
Layout: `max-w-3xl mx-auto px-4 sm:px-6 lg:px-8 py-6` (matches existing global item detail page).
|
|
Loading state: shimmer placeholders (same pattern as `$globalItemId.tsx`).
|
|
Error state: "Item not found" with back link.
|
|
Image placeholder when no image: centered LucideIcon with categoryIcon, bg-gray-50 container.
|
|
|
|
Do NOT import from UIStore for panel state (openEditPanel, closePanel, etc.) — this page replaces the panel.
|
|
</action>
|
|
<verify>
|
|
<automated>cd /home/jean-luc-makiola/Development/projects/GearBox && bun run lint 2>&1 | tail -5</automated>
|
|
</verify>
|
|
<acceptance_criteria>
|
|
- grep -q "createFileRoute.*items.*itemId" src/client/routes/items/\$itemId.tsx
|
|
- grep -q "useItem" src/client/routes/items/\$itemId.tsx
|
|
- grep -q "useUpdateItem" src/client/routes/items/\$itemId.tsx
|
|
- grep -q "isEditing" src/client/routes/items/\$itemId.tsx
|
|
- grep -q "Back to collection" src/client/routes/items/\$itemId.tsx
|
|
- grep -q "useDuplicateItem\|useDeleteItem" src/client/routes/items/\$itemId.tsx
|
|
- grep -q "CategoryPicker" src/client/routes/items/\$itemId.tsx
|
|
- grep -q "ImageUpload" src/client/routes/items/\$itemId.tsx
|
|
</acceptance_criteria>
|
|
<done>
|
|
- `/items/:id` route renders item detail with hero image, name, badges, personal fields
|
|
- Edit button toggles between read-only and editable states
|
|
- Save persists changes via useUpdateItem and exits edit mode
|
|
- Cancel exits edit mode without saving
|
|
- Duplicate and Delete actions work via existing hooks/dialogs
|
|
- Back link navigates to /collection
|
|
- Loading and error states handled
|
|
</done>
|
|
</task>
|
|
|
|
<task type="auto">
|
|
<name>Task 2: Enhance catalog detail page with Add to Collection button and improved layout</name>
|
|
<files>src/client/routes/global-items/$globalItemId.tsx</files>
|
|
<read_first>
|
|
- src/client/routes/global-items/$globalItemId.tsx (current implementation to enhance)
|
|
- src/client/hooks/useGlobalItems.ts (useGlobalItem hook)
|
|
- src/client/routes/items/$itemId.tsx (just created — follow same layout proportions)
|
|
</read_first>
|
|
<action>
|
|
Enhance the existing `src/client/routes/global-items/$globalItemId.tsx` file.
|
|
|
|
Per D-08: Enhance existing route — add "Add to Collection" button and improve layout.
|
|
Per D-09: Layout improvements:
|
|
- Hero image section: if `item.imageUrl` exists, show in `aspect-[16/9] bg-gray-50 rounded-xl overflow-hidden` (already exists). If no image, show a placeholder div with a package icon.
|
|
- Brand as small uppercase label above model title (already exists).
|
|
- Model as h1 title (already exists).
|
|
- Manufacturer specs as badges: weight, price, category (already exists).
|
|
- Description section (already exists).
|
|
- Owner count badge (already exists).
|
|
|
|
Per D-10: "Add to Collection" button — prominent placement after the header/badges section. Styled as `bg-gray-700 text-white rounded-lg px-5 py-2.5 text-sm font-medium hover:bg-gray-800 transition-colors`. This is a STUB — onClick shows a console.log("Add to collection — wired in Phase 22") or a brief toast/alert. The actual flow is Phase 22. Match the same stub pattern as the Add button in CatalogSearchOverlay.
|
|
|
|
Per D-11: No edit functionality on this page. Read-only display only.
|
|
Per D-12: This page is accessible from catalog search overlay — no changes needed here (overlay card click wiring happens in Plan 03).
|
|
|
|
Specific enhancements to the existing page:
|
|
1. Add image placeholder when no imageUrl (currently the image section is skipped entirely — add a placeholder div)
|
|
2. Add "Add to Collection" button between badges and description
|
|
3. Ensure layout is consistent with the new item detail page from Task 1 (same max-width, padding, spacing)
|
|
4. Add tags display if the item has tags (optional, Claude's discretion — subtle tag chips below badges)
|
|
</action>
|
|
<verify>
|
|
<automated>cd /home/jean-luc-makiola/Development/projects/GearBox && bun run lint 2>&1 | tail -5</automated>
|
|
</verify>
|
|
<acceptance_criteria>
|
|
- grep -q "Add to Collection" src/client/routes/global-items/\$globalItemId.tsx
|
|
- grep -q "button" src/client/routes/global-items/\$globalItemId.tsx
|
|
- grep -q "createFileRoute.*global-items.*globalItemId" src/client/routes/global-items/\$globalItemId.tsx
|
|
</acceptance_criteria>
|
|
<done>
|
|
- Catalog detail page shows "Add to Collection" button (stub, not wired to actual add flow)
|
|
- Image placeholder shown when no image exists
|
|
- Layout is clean and consistent with item detail page styling
|
|
- All existing functionality preserved (back link, specs, owner count)
|
|
</done>
|
|
</task>
|
|
|
|
</tasks>
|
|
|
|
<verification>
|
|
- `bun run lint` passes with no errors
|
|
- `bun run dev` starts and both routes render correctly:
|
|
- Navigate to `/items/1` — shows item detail with edit toggle
|
|
- Navigate to `/global-items/1` — shows catalog detail with Add to Collection button
|
|
- Edit mode on item detail: clicking Edit shows form fields, Save persists, Cancel reverts
|
|
</verification>
|
|
|
|
<success_criteria>
|
|
- Item detail page at `/items/:id` renders all item data in gallery-like layout
|
|
- Edit mode toggle works: read-only by default, editable when toggled
|
|
- Catalog detail page shows "Add to Collection" button (stub)
|
|
- Both pages follow consistent layout patterns (max-w-3xl, same spacing)
|
|
- Lint passes cleanly
|
|
</success_criteria>
|
|
|
|
<output>
|
|
After completion, create `.planning/phases/21-item-catalog-detail-pages/21-01-SUMMARY.md`
|
|
</output>
|