From 5ae0dd1b2dc6d70ea979c7a2c8630f26c4261189 Mon Sep 17 00:00:00 2001 From: Jean-Luc Makiola Date: Mon, 6 Apr 2026 17:25:35 +0200 Subject: [PATCH] docs(23): capture phase context --- .planning/STATE.md | 32 ++--- .../23-manual-entry-fallback/23-CONTEXT.md | 123 ++++++++++++++++++ .../23-DISCUSSION-LOG.md | 77 +++++++++++ .../components/CatalogSearchOverlay.tsx | 65 ++++++--- 4 files changed, 259 insertions(+), 38 deletions(-) create mode 100644 .planning/phases/23-manual-entry-fallback/23-CONTEXT.md create mode 100644 .planning/phases/23-manual-entry-fallback/23-DISCUSSION-LOG.md diff --git a/.planning/STATE.md b/.planning/STATE.md index 43e1019..914e05e 100644 --- a/.planning/STATE.md +++ b/.planning/STATE.md @@ -2,15 +2,15 @@ gsd_state_version: 1.0 milestone: v1.3 milestone_name: Research & Decision Tools -status: planning -stopped_at: Completed 22-02-PLAN.md -last_updated: "2026-04-06T14:16:11.851Z" -last_activity: 2026-04-06 +status: executing +stopped_at: Phase 22 plans verified +last_updated: "2026-04-06T13:47:45.651Z" +last_activity: 2026-04-06 -- Phase 22 execution started progress: total_phases: 16 - completed_phases: 15 + completed_phases: 14 total_plans: 43 - completed_plans: 41 + completed_plans: 39 percent: 0 --- @@ -21,14 +21,14 @@ progress: See: .planning/PROJECT.md (updated 2026-04-03) **Core value:** Help people make better gear decisions — discover what others use, compare real-world data, and see how a potential buy affects your setup before committing. -**Current focus:** v2.0 Platform Foundation — Phase 14 (PostgreSQL Migration) +**Current focus:** Phase 22 — add-from-catalog-thread-integration ## Current Position -Phase: 22 of 18 (PostgreSQL Migration) -Plan: Not started -Status: Ready to plan -Last activity: 2026-04-06 +Phase: 22 (add-from-catalog-thread-integration) — EXECUTING +Plan: 1 of 2 +Status: Executing Phase 22 +Last activity: 2026-04-06 -- Phase 22 execution started Progress: [----------] 0% (v2.0 milestone) @@ -61,8 +61,6 @@ Key decisions made during v2.0 planning: - [Phase 21]: Preserved currentThreadId derivation in __root.tsx for CandidateDeleteDialog - [Phase 21]: CollectionView empty state Add button rewired to catalog search overlay - [Phase 21]: ItemForm/CandidateForm decoupled from UIStore with onClose prop pattern -- [Phase 22]: Sonner for toast notifications; all Phase 22 UIStore states added in Plan 01 -- [Phase 22]: Used apiPost directly instead of useCreateCandidate hook for dynamic threadId selection ### Pending Todos @@ -74,8 +72,6 @@ None active. |---|-------------|------|--------|-----------| | 260406-j44 | Comprehensive dev seed script for bikepacking gear data | 2026-04-06 | — | [260406-j44-comprehensive-dev-seed-script-for-bikepa](./quick/260406-j44-comprehensive-dev-seed-script-for-bikepa/) | | Phase 21 P03 | 6min | 2 tasks | 10 files | -| Phase 22 P01 | 7min | 2 tasks | 5 files | -| Phase 22 P02 | 2min | 2 tasks | 2 files | ### Blockers/Concerns @@ -84,6 +80,6 @@ None active. ## Session Continuity -Last session: 2026-04-06T14:01:30.281Z -Stopped at: Completed 22-02-PLAN.md -Resume file: None +Last session: 2026-04-06T13:47:00.871Z +Stopped at: Phase 22 plans verified +Resume file: .planning/phases/22-add-from-catalog-thread-integration/22-01-PLAN.md diff --git a/.planning/phases/23-manual-entry-fallback/23-CONTEXT.md b/.planning/phases/23-manual-entry-fallback/23-CONTEXT.md new file mode 100644 index 0000000..18ac4fd --- /dev/null +++ b/.planning/phases/23-manual-entry-fallback/23-CONTEXT.md @@ -0,0 +1,123 @@ +# Phase 23: Manual Entry Fallback - Context + +**Gathered:** 2026-04-06 +**Status:** Ready for planning + + +## Phase Boundary + +Users can still add items not found in the catalog via manual entry. The catalog search overlay gets an "Add Manually" fallback link. Manual entry saves a standalone collection item (no globalItemId). After saving, a non-functional "Submit to catalog?" prompt is shown (no backend action — future feature). + + + + +## Implementation Decisions + +### Entry Point Placement +- **D-01:** "Add Manually" link appears in the catalog search empty state (when no results match) AND as a subtle persistent link below search results, so users can always reach manual entry regardless of whether results exist. +- **D-02:** The link text is "Add Manually" or "Can't find it? Add manually" — context-sensitive based on whether a search query exists. + +### Form Presentation +- **D-03:** Manual entry form replaces the search results area inline within the CatalogSearchOverlay. The user stays in the overlay context — no navigation away or additional modal. +- **D-04:** Back arrow at the top returns from the manual form to search results (like navigating within the overlay). +- **D-05:** The form is a dedicated ManualEntryForm component rendered inside CatalogSearchOverlay when a `manualEntryMode` state is active. + +### Form Fields +- **D-06:** Required: name. Optional: category (via CategoryPicker), weight (grams), price (cents), notes, purchase price, image upload, product link. +- **D-07:** Reuse field patterns from ItemForm (CategoryPicker, weight/price inputs) but keep the form focused and compact — not the full 315-line ItemForm. +- **D-08:** Submit calls `POST /api/items` without `globalItemId` — creates a standalone collection item. + +### Post-Save Catalog Prompt +- **D-09:** After successful save, show an inline success card within the overlay: "Added [item name] to collection" with a "Submit to Catalog?" button. +- **D-10:** The "Submit to Catalog?" button is non-functional — shows a toast "Coming soon" or similar when clicked. No backend action. +- **D-11:** Below the prompt, show "Add Another" (returns to search) and "Done" (closes overlay) buttons. + +### Claude's Discretion +- Exact form layout proportions and field ordering +- Whether weight/price fields use formatted inputs or plain number inputs +- Animation transitions between search results and manual entry form +- Success card visual styling +- Whether the search query auto-populates the item name field when entering manual mode + + + + +## Canonical References + +**Downstream agents MUST read these before planning or implementing.** + +### Design Spec +- `docs/superpowers/specs/2026-04-05-catalog-driven-gear-flow-design.md` — Full catalog-driven gear flow vision. Phase 23 implements the manual entry fallback path. + +### Key Components to Modify +- `src/client/components/CatalogSearchOverlay.tsx` — Add "Add Manually" link to EmptyState (line 667), add persistent link below results, add ManualEntryForm rendering +- `src/client/stores/uiStore.ts` — May need `manualEntryMode` state or can be local to CatalogSearchOverlay + +### Existing Components to Reuse +- `src/client/components/ItemForm.tsx` — Field patterns (CategoryPicker usage, weight/price inputs) to adapt for manual entry form +- `src/client/components/AddToCollectionModal.tsx` — Lightweight add pattern with CategoryPicker, notes, purchase price +- `src/client/components/CategoryPicker.tsx` — Reusable category selector +- `src/client/components/ImageUpload.tsx` — Image upload component + +### Hooks +- `src/client/hooks/useItems.ts` — `useCreateItem()` for creating standalone items (no globalItemId) +- `src/client/hooks/useCategories.ts` — Category list for CategoryPicker + +### Schemas +- `src/shared/schemas.ts` — `createItemSchema` already supports items without `globalItemId` + +### Prior Phase Context +- `.planning/phases/20-fab-full-screen-catalog-search/20-CONTEXT.md` — Catalog search overlay design (D-12: empty state with "Add Manually" link noted) +- `.planning/phases/22-add-from-catalog-thread-integration/22-CONTEXT.md` — Deferred "Add Manually" link to this phase + +### Requirements +- `.planning/REQUIREMENTS.md` — CATFLOW-07, CATFLOW-08 + + + + +## Existing Code Insights + +### Reusable Assets +- `useCreateItem()` hook — creates items via POST /api/items, works without `globalItemId` for standalone items +- `CategoryPicker` — drop-in category selector with search and inline create +- `ImageUpload` — image upload with preview +- `AddToCollectionModal` — reference for lightweight form pattern (category + notes + purchase price) +- `ItemForm` — full item form with all fields, good reference for field patterns + +### Established Patterns +- CatalogSearchOverlay manages internal view state (search input, filters, view mode) via local useState +- Zustand UIStore for overlay open/close state +- `toast()` from sonner for success/error notifications +- TanStack React Query mutations with `invalidateQueries` on success + +### Integration Points +- `CatalogSearchOverlay.tsx` — EmptyState component (line 667) needs "Add Manually" link +- `CatalogSearchOverlay.tsx` — Results area needs persistent "Add Manually" link +- `CatalogSearchOverlay.tsx` — New ManualEntryForm component rendered conditionally +- No new routes needed — everything happens within the overlay + + + + +## Specific Ideas + +- The manual entry form should feel like a natural extension of the catalog search flow — "couldn't find it? no problem, add it yourself" +- Search query should auto-populate the item name field when transitioning to manual entry +- The "Submit to Catalog?" prompt plants the seed for the future catalog submission system without promising functionality + + + + +## Deferred Ideas + +- Actual catalog submission backend (admin review, convert to global item) — future phase +- Bulk manual entry — future phase +- Image search / URL paste to auto-populate item details — future phase + + + +--- + +*Phase: 23-manual-entry-fallback* +*Context gathered: 2026-04-06* diff --git a/.planning/phases/23-manual-entry-fallback/23-DISCUSSION-LOG.md b/.planning/phases/23-manual-entry-fallback/23-DISCUSSION-LOG.md new file mode 100644 index 0000000..30bdae9 --- /dev/null +++ b/.planning/phases/23-manual-entry-fallback/23-DISCUSSION-LOG.md @@ -0,0 +1,77 @@ +# Phase 23: Manual Entry Fallback - Discussion Log + +> **Audit trail only.** Do not use as input to planning, research, or execution agents. +> Decisions are captured in CONTEXT.md — this log preserves the alternatives considered. + +**Date:** 2026-04-06 +**Phase:** 23-manual-entry-fallback +**Areas discussed:** Entry point placement, Form presentation, Post-save catalog prompt, Form fields scope +**Mode:** --auto (all decisions auto-selected) + +--- + +## Entry Point Placement + +| Option | Description | Selected | +|--------|-------------|----------| +| Empty state only | Show "Add Manually" only when no search results match | | +| Empty state + persistent link | Show in empty state AND as subtle persistent link below results | ✓ | +| FAB menu option | Add a third "Add Manually" option to the FAB mini menu | | + +**User's choice:** [auto] Empty state + persistent link (recommended default) +**Notes:** Ensures users can always reach manual entry regardless of search results. + +--- + +## Form Presentation + +| Option | Description | Selected | +|--------|-------------|----------| +| Inline in overlay | Replace search results with manual entry form inside CatalogSearchOverlay | ✓ | +| Separate modal | Open a new modal on top of the overlay | | +| Navigate to route | Navigate to a dedicated /items/new route | | + +**User's choice:** [auto] Inline in overlay (recommended default) +**Notes:** Keeps user in context. Back arrow returns to search results. + +--- + +## Post-Save Catalog Prompt + +| Option | Description | Selected | +|--------|-------------|----------| +| Inline success card | Show success + "Submit to Catalog?" button inline in overlay | ✓ | +| Toast with action | Show toast notification with "Submit to Catalog?" action button | | +| Redirect to item detail | Navigate to new item's detail page with prompt there | | + +**User's choice:** [auto] Inline success card (recommended default) +**Notes:** Non-functional button shows "Coming soon" toast. "Add Another" and "Done" buttons below. + +--- + +## Form Fields Scope + +| Option | Description | Selected | +|--------|-------------|----------| +| Essential fields | Name (required), category, weight, price, notes, purchase price, image, product link | ✓ | +| Minimal fields | Name and category only, edit rest on detail page | | +| Full ItemForm | Reuse the complete 315-line ItemForm component | | + +**User's choice:** [auto] Essential fields (recommended default) +**Notes:** Reuse patterns from ItemForm but keep form focused and compact. + +--- + +## Claude's Discretion + +- Form layout proportions and field ordering +- Weight/price input formatting +- Animation transitions between search and manual entry +- Success card visual styling +- Whether search query auto-populates item name field + +## Deferred Ideas + +- Actual catalog submission backend — future phase +- Bulk manual entry — future phase +- Image search / URL paste to auto-populate — future phase diff --git a/src/client/components/CatalogSearchOverlay.tsx b/src/client/components/CatalogSearchOverlay.tsx index aed2236..7b6f2eb 100644 --- a/src/client/components/CatalogSearchOverlay.tsx +++ b/src/client/components/CatalogSearchOverlay.tsx @@ -1,3 +1,4 @@ +import { useNavigate } from "@tanstack/react-router"; import { AnimatePresence, motion } from "framer-motion"; import { ArrowLeft, Filter, LayoutGrid, LayoutList, X } from "lucide-react"; import { useEffect, useMemo, useState } from "react"; @@ -97,16 +98,19 @@ export function CatalogSearchOverlay() { setSelectedTags((prev) => prev.filter((t) => t !== tagName)); } - const openAddToCollection = useUIStore((s) => s.openAddToCollection); - const openAddToThread = useUIStore((s) => s.openAddToThread); + const navigate = useNavigate(); - function handleAdd(item: { id: number; brand: string; model: string }) { - const itemName = `${item.brand} ${item.model}`; - if (catalogSearchMode === "collection") { - openAddToCollection(item.id, itemName); - } else if (catalogSearchMode === "thread") { - openAddToThread(item.id, itemName); - } + function handleCardClick(itemId: number) { + closeCatalogSearch(); + navigate({ + to: "/global-items/$globalItemId", + params: { globalItemId: String(itemId) }, + }); + } + + function handleAddStub(e: React.MouseEvent) { + e.stopPropagation(); + // Stub: actual add-to-collection / add-to-thread wired in Phase 22 } const contextText = @@ -315,7 +319,10 @@ export function CatalogSearchOverlay() { value={weightMin} onChange={(e) => setWeightMin( - Math.min(Number(e.target.value), weightMax - 50), + Math.min( + Number(e.target.value), + weightMax - 50, + ), ) } className="flex-1 h-1.5 bg-gray-200 rounded-full appearance-none accent-blue-500" @@ -330,7 +337,10 @@ export function CatalogSearchOverlay() { value={weightMax} onChange={(e) => setWeightMax( - Math.max(Number(e.target.value), weightMin + 50), + Math.max( + Number(e.target.value), + weightMin + 50, + ), ) } className="flex-1 h-1.5 bg-gray-200 rounded-full appearance-none accent-blue-500" @@ -358,7 +368,10 @@ export function CatalogSearchOverlay() { value={priceMin} onChange={(e) => setPriceMin( - Math.min(Number(e.target.value), priceMax - 500), + Math.min( + Number(e.target.value), + priceMax - 500, + ), ) } className="flex-1 h-1.5 bg-gray-200 rounded-full appearance-none accent-green-500" @@ -373,7 +386,10 @@ export function CatalogSearchOverlay() { value={priceMax} onChange={(e) => setPriceMax( - Math.max(Number(e.target.value), priceMin + 500), + Math.max( + Number(e.target.value), + priceMin + 500, + ), ) } className="flex-1 h-1.5 bg-gray-200 rounded-full appearance-none accent-green-500" @@ -406,7 +422,8 @@ export function CatalogSearchOverlay() { handleAdd(item)} + onAdd={handleAddStub} + onCardClick={() => handleCardClick(item.id)} weight={weight} price={price} /> @@ -418,7 +435,8 @@ export function CatalogSearchOverlay() { handleAdd(item)} + onAdd={handleAddStub} + onCardClick={() => handleCardClick(item.id)} weight={weight} price={price} /> @@ -451,14 +469,18 @@ interface CardProps { priceCents: number | null; imageUrl: string | null; }; - onAdd: () => void; + onAdd: (e: React.MouseEvent) => void; + onCardClick: () => void; weight: (g: number) => string; price: (cents: number) => string; } -function GridCard({ item, onAdd, weight, price }: CardProps) { +function GridCard({ item, onAdd, onCardClick, weight, price }: CardProps) { return ( -
+
{item.imageUrl ? ( +
{/* Thumbnail */}
{item.imageUrl ? (