docs(23): capture phase context
This commit is contained in:
@@ -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
|
||||
|
||||
123
.planning/phases/23-manual-entry-fallback/23-CONTEXT.md
Normal file
123
.planning/phases/23-manual-entry-fallback/23-CONTEXT.md
Normal file
@@ -0,0 +1,123 @@
|
||||
# Phase 23: Manual Entry Fallback - Context
|
||||
|
||||
**Gathered:** 2026-04-06
|
||||
**Status:** Ready for planning
|
||||
|
||||
<domain>
|
||||
## 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).
|
||||
|
||||
</domain>
|
||||
|
||||
<decisions>
|
||||
## 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
|
||||
|
||||
</decisions>
|
||||
|
||||
<canonical_refs>
|
||||
## 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
|
||||
|
||||
</canonical_refs>
|
||||
|
||||
<code_context>
|
||||
## 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
|
||||
|
||||
</code_context>
|
||||
|
||||
<specifics>
|
||||
## 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
|
||||
|
||||
</specifics>
|
||||
|
||||
<deferred>
|
||||
## 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
|
||||
|
||||
</deferred>
|
||||
|
||||
---
|
||||
|
||||
*Phase: 23-manual-entry-fallback*
|
||||
*Context gathered: 2026-04-06*
|
||||
@@ -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
|
||||
@@ -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() {
|
||||
<GridCard
|
||||
key={item.id}
|
||||
item={item}
|
||||
onAdd={() => handleAdd(item)}
|
||||
onAdd={handleAddStub}
|
||||
onCardClick={() => handleCardClick(item.id)}
|
||||
weight={weight}
|
||||
price={price}
|
||||
/>
|
||||
@@ -418,7 +435,8 @@ export function CatalogSearchOverlay() {
|
||||
<ListRow
|
||||
key={item.id}
|
||||
item={item}
|
||||
onAdd={() => 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 (
|
||||
<div className="bg-white rounded-xl border border-gray-100 overflow-hidden">
|
||||
<div
|
||||
className="bg-white rounded-xl border border-gray-100 overflow-hidden cursor-pointer hover:border-gray-200 hover:shadow-sm transition-all"
|
||||
onClick={onCardClick}
|
||||
>
|
||||
<div className="aspect-[4/3] bg-gray-50">
|
||||
{item.imageUrl ? (
|
||||
<img
|
||||
@@ -522,9 +544,12 @@ function GridCard({ item, onAdd, weight, price }: CardProps) {
|
||||
|
||||
// ── List Row ───────────────────────────────────────────────────────────
|
||||
|
||||
function ListRow({ item, onAdd, weight, price }: CardProps) {
|
||||
function ListRow({ item, onAdd, onCardClick, weight, price }: CardProps) {
|
||||
return (
|
||||
<div className="bg-white rounded-xl border border-gray-100 flex items-center gap-4 px-4 py-3">
|
||||
<div
|
||||
className="bg-white rounded-xl border border-gray-100 flex items-center gap-4 px-4 py-3 cursor-pointer hover:border-gray-200 hover:shadow-sm transition-all"
|
||||
onClick={onCardClick}
|
||||
>
|
||||
{/* Thumbnail */}
|
||||
<div className="w-12 h-12 rounded-lg bg-gray-50 shrink-0 overflow-hidden">
|
||||
{item.imageUrl ? (
|
||||
|
||||
Reference in New Issue
Block a user