From 1b492f2ac2fdd7ca934ab8a14d739fd137f23fb8 Mon Sep 17 00:00:00 2001 From: Jean-Luc Makiola Date: Fri, 3 Apr 2026 18:00:02 +0200 Subject: [PATCH] docs: add v1.4 Collection Tools design spec Co-Authored-By: Claude Opus 4.6 (1M context) --- ...2026-04-03-v1.4-collection-tools-design.md | 114 ++++++++++++++++++ 1 file changed, 114 insertions(+) create mode 100644 docs/superpowers/specs/2026-04-03-v1.4-collection-tools-design.md diff --git a/docs/superpowers/specs/2026-04-03-v1.4-collection-tools-design.md b/docs/superpowers/specs/2026-04-03-v1.4-collection-tools-design.md new file mode 100644 index 0000000..7ec4a4a --- /dev/null +++ b/docs/superpowers/specs/2026-04-03-v1.4-collection-tools-design.md @@ -0,0 +1,114 @@ +# v1.4 Collection Tools Design + +**Date:** 2026-04-03 +**Milestone:** v1.4 Collection Tools +**Scope:** Setup impact preview, item quantity, CSV import/export, item duplication + +## Feature 1: Setup Impact Preview + +Already fully designed in `.planning/phases/13-setup-impact-preview/`. Two plans exist: +- **13-01**: Pure `computeImpactDeltas` function + `useImpactDeltas` hook + uiStore state (TDD) +- **13-02**: `SetupImpactSelector` + `ImpactDeltaBadge` components wired into thread detail + +Execute the existing plans as-is. No design changes needed. + +## Feature 2: Item Quantity + +### Schema + +Add `quantity INTEGER NOT NULL DEFAULT 1` to `items` table via Drizzle migration. + +```ts +quantity: integer("quantity").notNull().default(1), +``` + +### Validation + +Add to `createItemSchema` in `src/shared/schemas.ts`: +```ts +quantity: z.number().int().positive().optional(), +``` + +Flows to `updateItemSchema` via `.partial()` automatically. + +### Service Layer + +No special business logic — quantity is a stored field. + +**Totals computation changes:** +- `totals.service.ts`: `getCategoryTotals()` and `getGlobalTotals()` must multiply `weightGrams * quantity` and `priceCents * quantity` in their SQL SUM aggregations. +- `setup.service.ts`: `getSetupWithItems()` and `getAllSetups()` — when computing setup totals, multiply item weight/price by the item's quantity. + +### UI + +- **ItemForm**: Number input for quantity (min=1), placed below price field. Defaults to 1. +- **ItemCard**: Show "x2" badge next to item name when quantity > 1. No badge when quantity is 1. +- **Totals**: Already computed server-side with the quantity multiplication. No client-side changes for totals. +- **Setup weight/cost**: The item's quantity determines its weight/cost contribution when included in a setup (one `setup_items` row, but totals reflect quantity). + +### Thread Resolution + +When a thread is resolved and a candidate is copied to an item, the new item gets `quantity: 1` (default). No special handling needed. + +## Feature 3: CSV Import/Export + +### Export + +**Endpoint:** `GET /api/items/export` +- Returns CSV with headers: `name,quantity,weightGrams,priceCents,category,notes,productUrl` +- `Content-Type: text/csv` +- `Content-Disposition: attachment; filename="gearbox-export.csv"` +- Weight in grams, price in cents (raw values, no formatting) +- Category column contains category name (not ID) + +**Service:** `exportItemsCsv(db)` returns a CSV string. Joins items with categories for name lookup. + +### Import + +**Endpoint:** `POST /api/items/import` +- Accepts multipart form upload (CSV file) +- Parses rows, validates required fields (name is required, others optional) +- Category matching: looks up by name (case-insensitive). Creates new category if not found. +- Quantity defaults to 1 if not present in CSV +- Returns `{ imported: number, created_categories: string[], errors: string[] }` +- Skips rows with errors, continues processing remaining rows + +**Service:** `importItemsCsv(db, csvContent: string)` parses and inserts items. + +### UI + +Settings page gets an "Import/Export" section: +- "Export CSV" button — triggers download via `GET /api/items/export` +- "Import CSV" file input — accepts .csv files, shows count of parsed rows, confirm button to upload +- Success/error feedback after import completes + +## Feature 4: Item Duplication + +### API + +**Endpoint:** `POST /api/items/:id/duplicate` +- Copies all fields from source item: name, weightGrams, priceCents, categoryId, notes, productUrl, imageFilename, imageSourceUrl, quantity +- Appends " (copy)" to the name +- New `createdAt`/`updatedAt` timestamps +- Returns the new item + +**Service:** `duplicateItem(db, id)` — fetches source item, inserts copy, returns new item. + +### UI + +- Add "Duplicate" action to ItemCard (alongside existing edit/delete actions) +- Duplicating opens the edit panel pre-filled with the new item so the user can rename or adjust + +## Phase Ordering + +1. **Item Quantity** — schema change first since CSV import/export and totals depend on it +2. **Setup Impact Preview** — execute existing Phase 13 plans +3. **Item Duplication** — small, self-contained +4. **CSV Import/Export** — depends on quantity field existing in schema + +## Out of Scope + +- Quantity per setup (setup_items.quantity) — items table quantity is sufficient for v1.4 +- CSV export with formatted weights/prices — raw values are more portable +- Image export/import via CSV — images are local files, not CSV-compatible +- Bulk edit from CSV preview — import creates, doesn't update existing items