From cca99778a453d0717107aad549cc72052faa4e59 Mon Sep 17 00:00:00 2001 From: Jean-Luc Makiola Date: Mon, 6 Apr 2026 17:34:14 +0200 Subject: [PATCH] docs(23): create phase plan for manual entry fallback --- .planning/ROADMAP.md | 4 +- .../23-manual-entry-fallback/23-01-PLAN.md | 445 ++++++++++++++++++ 2 files changed, 448 insertions(+), 1 deletion(-) create mode 100644 .planning/phases/23-manual-entry-fallback/23-01-PLAN.md diff --git a/.planning/ROADMAP.md b/.planning/ROADMAP.md index 499c4ce..d9a4d96 100644 --- a/.planning/ROADMAP.md +++ b/.planning/ROADMAP.md @@ -242,7 +242,9 @@ Plans: 1. User can fall back to manual entry from catalog search via "Add Manually" link 2. Manual entry saves a standalone collection item (no globalItemId) 3. "Submit to catalog?" prompt appears after manual save but takes no backend action -**Plans**: TBD +**Plans:** 1 plan +Plans: +- [ ] 23-01-PLAN.md -- ManualEntryForm + CatalogSearchOverlay wiring **UI hint**: yes ## Progress diff --git a/.planning/phases/23-manual-entry-fallback/23-01-PLAN.md b/.planning/phases/23-manual-entry-fallback/23-01-PLAN.md new file mode 100644 index 0000000..22c0b65 --- /dev/null +++ b/.planning/phases/23-manual-entry-fallback/23-01-PLAN.md @@ -0,0 +1,445 @@ +--- +phase: 23-manual-entry-fallback +plan: 01 +type: execute +wave: 1 +depends_on: [] +files_modified: + - src/client/components/ManualEntryForm.tsx + - src/client/components/CatalogSearchOverlay.tsx +autonomous: true +requirements: + - CATFLOW-07 + - CATFLOW-08 + +must_haves: + truths: + - "User can click 'Add Manually' from catalog search empty state to enter manual entry mode" + - "User can click a persistent 'Add Manually' link below search results to enter manual entry mode" + - "User sees a compact form with name (required), category, weight, price, purchase price, notes, product link, and image upload fields" + - "User can submit the form to create a standalone collection item (no globalItemId)" + - "After saving, user sees a success card with 'Submit to Catalog?' button, 'Add Another', and 'Done'" + - "'Submit to Catalog?' button shows a 'Coming soon' toast and takes no backend action" + - "Back arrow returns from manual form to search results without closing the overlay" + - "Search query auto-populates the item name field when entering manual mode" + artifacts: + - path: "src/client/components/ManualEntryForm.tsx" + provides: "Compact manual item entry form" + exports: ["ManualEntryForm"] + - path: "src/client/components/CatalogSearchOverlay.tsx" + provides: "Entry points, mode switching, success card rendering" + contains: "manualEntryMode" + key_links: + - from: "src/client/components/CatalogSearchOverlay.tsx" + to: "src/client/components/ManualEntryForm.tsx" + via: "conditional render when manualEntryMode && !savedItemName" + pattern: "manualEntryMode.*ManualEntryForm" + - from: "src/client/components/ManualEntryForm.tsx" + to: "src/client/hooks/useItems.ts" + via: "useCreateItem() mutation" + pattern: "useCreateItem" + - from: "src/client/components/CatalogSearchOverlay.tsx" + to: "sonner toast" + via: "Submit to Catalog button" + pattern: 'toast.*Coming soon' +--- + + +Add manual item entry fallback to the catalog search flow so users can add gear not found in the catalog. + +Purpose: Complete the catalog-driven gear flow by ensuring users are never stuck when a catalog item doesn't exist. The "Add Manually" path creates standalone collection items and plants the seed for future catalog submissions. + +Output: ManualEntryForm component + CatalogSearchOverlay wired with entry points, inline form rendering, and post-save success card. + + + +@$HOME/.claude/get-shit-done/workflows/execute-plan.md +@$HOME/.claude/get-shit-done/templates/summary.md + + + +@.planning/PROJECT.md +@.planning/ROADMAP.md +@.planning/STATE.md +@.planning/phases/23-manual-entry-fallback/23-CONTEXT.md +@.planning/phases/23-manual-entry-fallback/23-RESEARCH.md + +@src/client/components/CatalogSearchOverlay.tsx +@src/client/components/AddToCollectionModal.tsx +@src/client/components/ItemForm.tsx +@src/client/hooks/useItems.ts +@src/client/hooks/useCategories.ts +@src/shared/schemas.ts + + + + +From src/client/hooks/useItems.ts: +```typescript +export function useCreateItem(): UseMutationResult; +// CreateItem comes from src/shared/types.ts, inferred from createItemSchema +// globalItemId is optional — omitting it creates a standalone item +``` + +From src/shared/schemas.ts: +```typescript +export const createItemSchema = z.object({ + name: z.string().min(1).max(200), + categoryId: z.number().int().positive(), + weightGrams: z.number().int().nonnegative().optional(), + priceCents: z.number().int().nonnegative().optional(), + notes: z.string().max(2000).optional(), + productUrl: z.string().url().max(500).optional(), + imageFilename: z.string().optional(), + globalItemId: z.number().int().positive().optional(), + purchasePriceCents: z.number().int().nonnegative().optional(), + quantity: z.number().int().positive().optional(), +}); +``` + +From src/client/components/CatalogSearchOverlay.tsx: +```typescript +// Local state pattern — all view state managed via useState +const [searchInput, setSearchInput] = useState(""); +const catalogSearchOpen = useUIStore((s) => s.catalogSearchOpen); +const catalogSearchMode = useUIStore((s) => s.catalogSearchMode); +const closeCatalogSearch = useUIStore((s) => s.closeCatalogSearch); + +// EmptyState at line 667 — currently no onAddManually prop +function EmptyState({ hasQuery }: { hasQuery: boolean }) { ... } + +// Reset effect at line 76 — must add new state resets here +useEffect(() => { if (!catalogSearchOpen) { /* resets */ } }, [catalogSearchOpen]); +``` + +From src/client/components/CategoryPicker.tsx: +```typescript +interface CategoryPickerProps { + value: number; + onChange: (categoryId: number) => void; +} +``` + +From src/client/components/ImageUpload.tsx: +```typescript +interface ImageUploadProps { + value: string | null; + onChange: (filename: string | null) => void; +} +``` + + + + + + + Task 1: Create ManualEntryForm component + src/client/components/ManualEntryForm.tsx + + + src/client/components/AddToCollectionModal.tsx + src/client/components/ItemForm.tsx + src/client/hooks/useItems.ts + src/client/hooks/useCategories.ts + src/shared/schemas.ts + src/client/components/CategoryPicker.tsx + src/client/components/ImageUpload.tsx + + + +Create `src/client/components/ManualEntryForm.tsx` — a compact, focused form for adding items manually (per D-05, D-06, D-07). + +**Component interface (per D-05):** +```typescript +interface ManualEntryFormProps { + initialName?: string; // Auto-populated from search query + onSuccess: (itemName: string) => void; // Called after successful save + onBack: () => void; // Returns to search results (per D-04) +} +``` + +**Form state fields (per D-06):** +- `name` (string, required) — initialized from `initialName` prop +- `categoryId` (number | null) — pre-select first category once loaded (follow AddToCollectionModal pattern to avoid categoryId=0 Zod error) +- `weightGrams` (string, for input) — plain number input, converted to integer on submit +- `priceDollars` (string, for input) — plain number input, converted to cents via `Math.round(Number(val) * 100)` +- `purchasePrice` (string, for input) — same cents conversion +- `notes` (string) +- `productUrl` (string) +- `imageFilename` (string | null) — managed via `ImageUpload` component +- `error` (string | null) — validation/API error display + +**Hooks to use:** +- `useCategories()` from `@/client/hooks/useCategories` for CategoryPicker data +- `useCreateItem()` from `@/client/hooks/useItems` for the mutation + +**Form layout (top to bottom):** +1. Back arrow row: ` + + ); +} +``` +Update the `` of the grid at line 431 or list at line 444), but still inside the results conditional block (`items && items.length > 0`), add: +```typescript +
+ +
+``` + +**9. Create inline SuccessCard (per D-09, D-10, D-11):** +Add a local component or inline JSX for the success card, rendered when `manualEntryMode && savedItemName`: +```typescript +
+
+ + + +
+

Added {savedItemName} to collection

+ +
+ + +
+
+``` + +**"Submit to Catalog?" button (per D-10):** Calls `toast("Coming soon — catalog submissions are on the roadmap!")` only. No loading state, no disabled, no API call. + +**Do NOT:** +- Add any state to Zustand UIStore +- Show a separate `toast.success()` when the item is saved (the success card IS the feedback) +- Make "Add Manually" only visible in collection mode — show it in both modes (per research open question 2, D-08 locks to POST /api/items regardless) +
+ + + bun run lint + + + + - `CatalogSearchOverlay.tsx` contains `import { ManualEntryForm }` from `./ManualEntryForm` + - `CatalogSearchOverlay.tsx` contains `import { toast } from "sonner"` + - `CatalogSearchOverlay.tsx` contains `useState(false)` for `manualEntryMode` + - `CatalogSearchOverlay.tsx` contains `useState(null)` for `savedItemName` + - `CatalogSearchOverlay.tsx` contains `setManualEntryMode(false)` inside the reset effect (the `if (!catalogSearchOpen)` block) + - `CatalogSearchOverlay.tsx` contains `setSavedItemName(null)` inside the reset effect + - `EmptyState` function signature contains `onAddManually` + - `EmptyState` renders text "Can't find it? Add manually" (per D-02) + - `EmptyState` renders text "Add Manually" (per D-02) + - `CatalogSearchOverlay.tsx` contains ` + + CatalogSearchOverlay has "Add Manually" links in both empty state and below results, renders ManualEntryForm inline when manualEntryMode is active, shows success card with non-functional "Submit to Catalog?" button after save, and properly resets state on overlay close. +
+ +
+ + +1. `bun run lint` passes with no errors +2. `bun test` passes (no backend changes, existing tests unaffected) +3. Manual verification: open catalog search, search for non-existent item, see "Can't find it? Add manually" link, click it, fill form, submit, see success card, click "Submit to Catalog?" and see toast, click "Add Another" to return to search, click "Done" to close overlay + + + +- ManualEntryForm.tsx exists as a focused, compact form using CategoryPicker and ImageUpload +- CatalogSearchOverlay renders "Add Manually" links in empty state and below results +- Clicking "Add Manually" shows the form inline, pre-populated with search query as item name +- Submitting the form creates a standalone item (no globalItemId) via useCreateItem +- Success card shows with "Submit to Catalog?" (toast only), "Add Another", and "Done" +- Back arrow returns to search results, not closes overlay +- All state resets when overlay closes +- No new Zustand store state added +- Lint and existing tests pass + + + +After completion, create `.planning/phases/23-manual-entry-fallback/23-01-SUMMARY.md` +