docs: add v1.4 Collection Tools design spec

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-03 18:00:02 +02:00
parent 70466a9a1c
commit 1b492f2ac2

View File

@@ -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