Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -4,7 +4,7 @@ milestone: v1.2
|
||||
milestone_name: Collection Power-Ups
|
||||
status: completed
|
||||
stopped_at: Completed 09-02-PLAN.md
|
||||
last_updated: "2026-03-16T14:22:28.171Z"
|
||||
last_updated: "2026-03-16T14:29:36.577Z"
|
||||
last_activity: 2026-03-16 -- Completed 09-02 weight breakdown visualization
|
||||
progress:
|
||||
total_phases: 3
|
||||
|
||||
@@ -0,0 +1,158 @@
|
||||
---
|
||||
phase: 09-weight-classification-and-visualization
|
||||
verified: 2026-03-16T15:00:00Z
|
||||
status: passed
|
||||
score: 9/9 must-haves verified
|
||||
re_verification: false
|
||||
---
|
||||
|
||||
# Phase 9: Weight Classification and Visualization Verification Report
|
||||
|
||||
**Phase Goal:** Users can classify gear by role and visualize weight distribution in setups
|
||||
**Verified:** 2026-03-16T15:00:00Z
|
||||
**Status:** passed
|
||||
**Re-verification:** No — initial verification
|
||||
|
||||
---
|
||||
|
||||
## Goal Achievement
|
||||
|
||||
### Observable Truths
|
||||
|
||||
| # | Truth | Status | Evidence |
|
||||
|----|---------------------------------------------------------------------------------------------------------------------|------------|-----------------------------------------------------------------------------------------------|
|
||||
| 1 | User can click a classification badge on any item card within a setup and it cycles through base weight, worn, consumable | VERIFIED | ClassificationBadge renders in $setupId.tsx per item; nextClassification cycles the three values; useUpdateItemClassification mutation fires PATCH call |
|
||||
| 2 | Items default to base weight classification when added to a setup | VERIFIED | schema.ts: `classification text NOT NULL DEFAULT 'base'`; syncSetupItems uses `classificationMap.get(itemId) ?? "base"` |
|
||||
| 3 | Same item in different setups can have different classifications | VERIFIED | Classification stored on setupItems join table (not items); test confirmed in setup.service.test.ts |
|
||||
| 4 | Classifications persist after adding/removing other items from the setup (syncSetupItems preserves them) | VERIFIED | syncSetupItems reads Map<itemId, classification> before delete, restores after re-insert; 2 tests confirm |
|
||||
| 5 | Setup detail view shows separate weight subtotals for base weight, worn weight, consumable weight, and total | VERIFIED | WeightSummaryCard computes baseWeight/wornWeight/consumableWeight/totalWeight and renders 4 SubtotalColumn components |
|
||||
| 6 | User can view a donut chart showing weight distribution by category in the setup | VERIFIED | WeightSummaryCard uses Recharts PieChart+Pie with innerRadius=55/outerRadius=80; default viewMode="category" |
|
||||
| 7 | User can toggle the chart between category breakdown and classification breakdown via pill toggle | VERIFIED | Pill toggle button array maps over VIEW_MODES ["category","classification"]; state switches chartData source |
|
||||
| 8 | Hovering a chart segment shows category/classification name, weight in selected unit, and percentage | VERIFIED | CustomTooltip renders name, formatWeight(weight, unit), (percent*100).toFixed(1)% |
|
||||
| 9 | Total weight displayed in the center of the donut hole | VERIFIED | `<Label value={formatWeight(totalWeight, unit)} position="center" .../>` inside Pie |
|
||||
|
||||
**Score:** 9/9 truths verified
|
||||
|
||||
---
|
||||
|
||||
### Required Artifacts
|
||||
|
||||
#### Plan 01 Artifacts
|
||||
|
||||
| Artifact | Expected | Status | Details |
|
||||
|---|---|---|---|
|
||||
| `src/db/schema.ts` | classification column on setupItems table | VERIFIED | `classification: text("classification").notNull().default("base")` at line 89 |
|
||||
| `src/shared/schemas.ts` | classificationSchema Zod enum and updateClassificationSchema | VERIFIED | Both exported at lines 78-82 |
|
||||
| `src/server/services/setup.service.ts` | updateItemClassification, classification-preserving syncSetupItems, classification field in getSetupWithItems | VERIFIED | All three implemented; syncSetupItems uses Map pattern; getSetupWithItems selects `classification: setupItems.classification` |
|
||||
| `src/server/routes/setups.ts` | PATCH /:id/items/:itemId/classification endpoint | VERIFIED | app.patch("/:id/items/:itemId/classification", ...) at line 78 with Zod validation and service call |
|
||||
| `src/client/components/ClassificationBadge.tsx` | Click-to-cycle classification badge component (min 30 lines) | VERIFIED | 30 lines; button with stopPropagation + onCycle; CLASSIFICATION_LABELS map |
|
||||
| `src/client/routes/setups/$setupId.tsx` | ClassificationBadge wired into item cards in setup view | VERIFIED | Imported and rendered per item inside `{categoryItems.map(...)}` with nextClassification helper |
|
||||
| `tests/services/setup.service.test.ts` | Tests for updateItemClassification, classification preservation, defaults | VERIFIED | 5 new tests: default "base", preservation on sync, new items default, cross-setup independence, classification update |
|
||||
| `tests/routes/setups.test.ts` | Integration test for PATCH classification route | VERIFIED | 2 new tests: valid PATCH updates+persists, invalid value returns 400 |
|
||||
|
||||
#### Plan 02 Artifacts
|
||||
|
||||
| Artifact | Expected | Status | Details |
|
||||
|---|---|---|---|
|
||||
| `src/client/components/WeightSummaryCard.tsx` | Summary card with weight subtotals, donut chart, pill toggle, and tooltips (min 100 lines) | VERIFIED | 265 lines; all four features present |
|
||||
| `src/client/routes/setups/$setupId.tsx` | WeightSummaryCard rendered below sticky bar when setup has items | VERIFIED | `<WeightSummaryCard items={setup.items} />` inside `{itemCount > 0 && (...)}` block at line 196 |
|
||||
| `package.json` | recharts dependency installed | VERIFIED | `"recharts": "^3.8.0"` at line 43 |
|
||||
|
||||
---
|
||||
|
||||
### Key Link Verification
|
||||
|
||||
#### Plan 01 Key Links
|
||||
|
||||
| From | To | Via | Status | Details |
|
||||
|---|---|---|---|---|
|
||||
| `ClassificationBadge.tsx` | `/api/setups/:id/items/:itemId/classification` | useUpdateItemClassification mutation hook (apiPatch) | VERIFIED | useSetups.ts exports useUpdateItemClassification which calls `apiPatch(.../classification, ...)`; $setupId.tsx imports and calls it |
|
||||
| `src/server/routes/setups.ts` | `src/server/services/setup.service.ts` | updateItemClassification service call | VERIFIED | Routes imports updateItemClassification from service; calls it in PATCH handler |
|
||||
| `src/server/services/setup.service.ts` | `src/db/schema.ts` | setupItems.classification column | VERIFIED | service.ts uses `setupItems.classification` in select (line 56) and `set({ classification })` in update (line 143) |
|
||||
| `src/client/routes/setups/$setupId.tsx` | `src/client/components/ClassificationBadge.tsx` | ClassificationBadge rendered on each ItemCard | VERIFIED | Imported at line 4; rendered inside item map at lines 235-245 |
|
||||
|
||||
#### Plan 02 Key Links
|
||||
|
||||
| From | To | Via | Status | Details |
|
||||
|---|---|---|---|---|
|
||||
| `WeightSummaryCard.tsx` | recharts | PieChart, Pie, Cell, Tooltip, Label, ResponsiveContainer imports | VERIFIED | All six named imports from "recharts" at lines 2-9 |
|
||||
| `WeightSummaryCard.tsx` | `src/client/lib/formatters.ts` | formatWeight for subtotals and tooltip display | VERIFIED | `formatWeight` imported at line 12; used in SubtotalColumn, CustomTooltip, and center Label |
|
||||
| `src/client/routes/setups/$setupId.tsx` | `WeightSummaryCard.tsx` | WeightSummaryCard rendered with setup.items prop | VERIFIED | Imported at line 7; rendered as `<WeightSummaryCard items={setup.items} />` at line 196 |
|
||||
|
||||
---
|
||||
|
||||
### Requirements Coverage
|
||||
|
||||
| Requirement | Source Plan | Description | Status | Evidence |
|
||||
|---|---|---|---|---|
|
||||
| CLAS-01 | 09-01 | User can classify each item within a setup as base weight, worn, or consumable | SATISFIED | ClassificationBadge + PATCH endpoint + updateItemClassification service all wired and tested |
|
||||
| CLAS-02 | 09-02 | Setup totals display base weight, worn weight, consumable weight, and total separately | SATISFIED | WeightSummaryCard renders 4 SubtotalColumn components with computed weights |
|
||||
| CLAS-03 | 09-01 | Items default to "base weight" classification when added to a setup | SATISFIED | DB default "base" + syncSetupItems fallback + test confirms default |
|
||||
| CLAS-04 | 09-01 | Same item can have different classifications in different setups | SATISFIED | Classification on join table; cross-setup test passes |
|
||||
| VIZZ-01 | 09-02 | User can view a donut chart showing weight distribution by category in a setup | SATISFIED | Recharts PieChart with buildCategoryChartData, default viewMode="category" |
|
||||
| VIZZ-02 | 09-02 | User can toggle chart between category view and classification view | SATISFIED | Pill toggle with VIEW_MODES array, setViewMode state updates chartData source |
|
||||
| VIZZ-03 | 09-02 | User can hover chart segments to see category name, weight, and percentage | SATISFIED | CustomTooltip renders all three fields; passed to PieChart as `content` prop |
|
||||
|
||||
No orphaned requirements — all 7 IDs declared in plan frontmatter and accounted for.
|
||||
|
||||
---
|
||||
|
||||
### Anti-Patterns Found
|
||||
|
||||
No blockers or warnings found in modified files. The only `return null` instance is a standard React guard clause in CustomTooltip (not a stub).
|
||||
|
||||
---
|
||||
|
||||
### Human Verification Required
|
||||
|
||||
The following items cannot be verified programmatically and require a running browser session:
|
||||
|
||||
#### 1. Click-to-cycle badge interaction and stopPropagation
|
||||
|
||||
**Test:** Open a setup with items. Click a classification badge on one item card.
|
||||
**Expected:** Badge label cycles Base Weight -> Worn -> Consumable -> Base Weight. The item edit panel does NOT open when clicking the badge.
|
||||
**Why human:** stopPropagation correctness and visual badge state update require browser execution.
|
||||
|
||||
#### 2. Donut chart renders with correct segment proportions
|
||||
|
||||
**Test:** Add items with different categories and weights to a setup. View the setup detail page.
|
||||
**Expected:** Donut chart segments are proportional to weight distribution. Total weight appears in the center hole.
|
||||
**Why human:** Chart rendering requires browser + Recharts layout.
|
||||
|
||||
#### 3. Pill toggle switches chart data
|
||||
|
||||
**Test:** Click the "Classification" pill on the WeightSummaryCard.
|
||||
**Expected:** Chart segments change from category-based colors to indigo/amber/emerald for base/worn/consumable. Tooltips show "Base Weight", "Worn", or "Consumable" labels.
|
||||
**Why human:** Visual and interactive behavior requires browser.
|
||||
|
||||
#### 4. Tooltip on hover
|
||||
|
||||
**Test:** Hover over a chart segment.
|
||||
**Expected:** Tooltip appears with segment name, formatted weight in the selected unit, and percentage.
|
||||
**Why human:** Hover state requires browser interaction.
|
||||
|
||||
#### 5. Weight unit propagation
|
||||
|
||||
**Test:** Toggle the weight unit in the top bar (g / oz / lb / kg). Observe WeightSummaryCard.
|
||||
**Expected:** All four subtotal columns and the donut center label update to the selected unit.
|
||||
**Why human:** useWeightUnit hook behavior and re-render requires browser.
|
||||
|
||||
---
|
||||
|
||||
### Test Suite Results
|
||||
|
||||
All 121 tests pass across 10 files (32 setup-specific tests across services and routes).
|
||||
|
||||
- `tests/services/setup.service.test.ts` — 5 new classification tests pass (default "base", preservation, new item default, cross-setup independence, update from base to worn)
|
||||
- `tests/routes/setups.test.ts` — 2 new PATCH classification tests pass (valid update + 400 for invalid value)
|
||||
|
||||
---
|
||||
|
||||
### Summary
|
||||
|
||||
Phase 9 goal is fully achieved. All 9 observable truths are verified against the actual codebase — no stubs, no orphaned artifacts, no broken links. The complete vertical slice from DB schema to UI component is wired and exercised by 7 automated tests. Human verification is needed only for visual/interactive browser behaviors (chart rendering, hover tooltips, click cycling), which are structurally sound in the code.
|
||||
|
||||
---
|
||||
|
||||
_Verified: 2026-03-16T15:00:00Z_
|
||||
_Verifier: Claude (gsd-verifier)_
|
||||
Reference in New Issue
Block a user