Commit Graph

41 Commits

Author SHA1 Message Date
5b4026d36f feat(12-01): wire compare toggle and ComparisonTable into thread detail
- Extend uiStore candidateViewMode union to include "compare" value
- Add columns-3 compare toggle button, shown only when thread has 2+ candidates
- Hide "Add Candidate" button when in compare view (read-only intent)
- Import and render ComparisonTable when candidateViewMode === "compare"
- Pass displayItems so compare view reflects any pending reorder state
- Existing list/grid views unchanged; all 135 tests pass
2026-03-17 15:30:38 +01:00
e442b33a59 feat(12-01): add ComparisonTable component
- Side-by-side tabular comparison with all 10 attribute rows (Image, Name, Rank, Weight, Price, Status, Link, Notes, Pros, Cons)
- useMemo delta computation: blue-50 highlight on lightest weight, green-50 on cheapest price
- Gray delta string (+Xg, +$X.XX) shown below non-best cells
- Sticky left column with bg-white to prevent bleed-through on horizontal scroll
- Amber tint + trophy icon on winner column for resolved threads
- Em dash for missing weight/price data (never zero)
- Declarative ATTRIBUTE_ROWS array pattern for clean, maintainable row rendering
2026-03-17 15:29:30 +01:00
7e06c8526b fix(11): wire handleDragEnd to Reorder.Group for active threads
onPointerUp was incorrectly placed on the resolved-thread div instead
of the active-thread Reorder.Group, causing drag reorder to not persist.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-16 22:36:40 +01:00
94c07e79c2 feat(11-02): add view toggle, Reorder.Group drag-to-reorder, and rank badges in grid view
- Thread detail page: list/grid view toggle with LayoutList/LayoutGrid icons
- List view (active threads): Reorder.Group with CandidateListItem for drag-to-reorder
- List view (resolved threads): static CandidateListItem with rank badges, no drag handles
- Grid view: CandidateCard components with rank badges (gold/silver/bronze)
- tempItems pattern prevents React Query flicker during drag
- handleDragEnd fires PATCH /candidates/reorder after drag completes
- View toggle defaults to list view via uiStore candidateViewMode
2026-03-16 22:28:53 +01:00
acfa99516d feat(11-02): add useReorderCandidates hook, candidateViewMode, and CandidateListItem component
- Add useReorderCandidates mutation hook with apiPatch to /candidates/reorder endpoint
- Add candidateViewMode (list|grid) state and setCandidateViewMode to uiStore
- Create CandidateListItem component with drag handle, rank badge, horizontal layout
- Export RankBadge helper (gold/silver/bronze medal icons for top 3)
- Add style prop support to LucideIcon component
- Add pros/cons fields to CandidateWithCategory in useThreads.ts
2026-03-16 22:27:18 +01:00
4f2aefe7a4 feat(10-01): wire pros/cons through client hooks, form, and card indicator
- CandidateResponse: add pros/cons string|null fields
- CandidateForm: add pros/cons to FormData, INITIAL_FORM, pre-fill, payload
- CandidateForm: add Pros/Cons textarea inputs (after Notes, before Product Link)
- CandidateCard: add pros/cons props, render purple +/- Notes badge when present
- Thread detail route: pass pros/cons props to CandidateCard
2026-03-16 21:36:10 +01:00
9647f5759d feat: redesign weight summary legend and add currency selector
Redesign WeightSummaryCard stats from a disconnected 4-column grid to a
compact legend-style list with color dots, percentages, and a divider
before the total row. Switch chart and legend colors to a neutral gray
palette.

Add a currency selector to settings (USD, EUR, GBP, JPY, CAD, AUD) that
changes the displayed symbol across the app. This is visual only — no
value conversion is performed.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-16 20:33:07 +01:00
d098277797 feat(09-02): add WeightSummaryCard with donut chart and classification subtotals
- Install recharts dependency for donut chart visualization
- Create WeightSummaryCard component with pill toggle (category/classification views)
- Compute base/worn/consumable/total weight subtotals from items array
- Render donut chart with colored segments, center total, and hover tooltips
- Wire WeightSummaryCard into setup detail page below sticky bar
2026-03-16 15:20:41 +01:00
fb738d7cc2 feat(09-01): add classification API route, client hook, badge component, and setup detail wiring
- Add PATCH /:id/items/:itemId/classification endpoint with Zod validation
- Add apiPatch helper to client API library
- Add useUpdateItemClassification mutation hook
- Add classification field to SetupItemWithCategory interface
- Create ClassificationBadge click-to-cycle component (base/worn/consumable)
- Wire ClassificationBadge into setup detail page item grid
- Add integration tests for PATCH classification route (valid + invalid)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-16 15:13:08 +01:00
25956ed3ee feat(08-01): create StatusBadge component and wire into CandidateCard
- StatusBadge: clickable pill badge with popup menu (researching/ordered/arrived)
- Muted gray styling, LucideIcon per status, click-outside dismiss, Escape key support
- CandidateCard: status + onStatusChange props, StatusBadge in pill row after category
- Thread detail page: passes candidate.status and useUpdateCandidate for onStatusChange
- Fix Biome formatting for candidateStatusSchema enum

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-16 14:12:02 +01:00
5f89acd503 feat(08-02): add search/filter toolbar to gear tab and upgrade planning filter
- Sticky search/filter toolbar with text input and CategoryFilterDropdown
- useMemo-based filtering by name (search) and categoryId (dropdown)
- "Showing X of Y items" count when filters active
- Flat grid (no category headers) when any filter is active
- "No items match your search" empty state for filtered results
- Replace PlanningView native select with CategoryFilterDropdown
2026-03-16 14:09:51 +01:00
ca1c2a2e57 feat(08-01): add status column to threadCandidates and wire through backend
- Schema: status TEXT NOT NULL DEFAULT 'researching' on thread_candidates
- Zod: candidateStatusSchema enum (researching/ordered/arrived) added to createCandidateSchema
- Service: getThreadWithCandidates selects status, createCandidate sets status, updateCandidate accepts status
- Client hooks: CandidateWithCategory and CandidateResponse types include status field
- Migration generated and applied

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-16 14:09:18 +01:00
9e1a875581 feat(08-02): create CategoryFilterDropdown component
- Searchable dropdown with Lucide icons per category option
- "All categories" as first option with null value
- Click-outside and Escape key dismissal
- Clear button on trigger when category selected
- Auto-focus search input when dropdown opens
- State reset (search text) when dropdown closes
2026-03-16 14:07:34 +01:00
faa437896f feat(07-02): add weight unit toggle and wire all formatWeight call sites
- Add segmented g/oz/lb/kg toggle to TotalsBar with settings persistence
- Pass unit parameter to all 8 formatWeight call sites across components and routes
- Import useWeightUnit hook in ItemCard, CandidateCard, CategoryHeader, SetupCard, ItemPicker, Dashboard, SetupDetail

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-16 12:23:19 +01:00
ada37916b1 feat(07-01): create useWeightUnit convenience hook
- Wraps useSetting("weightUnit") with typed WeightUnit return
- Validates stored value against known units (g, oz, lb, kg)
- Defaults to "g" when no setting exists (backward compatible)
- Fix config.json formatting (tabs per biome config)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-16 12:16:11 +01:00
6cac0a32bc feat(07-01): implement formatWeight with WeightUnit parameter
- Export WeightUnit type ("g" | "oz" | "lb" | "kg")
- Add conversion constants (GRAMS_PER_OZ, GRAMS_PER_LB, GRAMS_PER_KG)
- Switch-based formatting: g=0dp, oz=1dp, lb=2dp, kg=2dp
- Default parameter "g" preserves backward compatibility
- formatPrice left untouched
- All 21 tests pass

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-16 12:15:32 +01:00
94ebd84cc7 refactor: move setups list into collection page as third tab
All checks were successful
CI / ci (push) Successful in 13s
Setups now lives alongside My Gear and Planning under /collection?tab=setups
instead of its own /setups route. Dashboard card updated to link to the new
tab. Setup detail pages (/setups/:id) remain unchanged.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-16 00:07:48 +01:00
5938a686c7 feat: add package icon as favicon and in top bar title
All checks were successful
CI / ci (push) Successful in 12s
Add Lucide package icon as SVG favicon (white stroke) and display it
next to the GearBox title in the TotalsBar.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-15 22:57:43 +01:00
9bcdcc7168 style: replace blue accent with gray and mute card badge colors
Switch all interactive UI elements (buttons, focus rings, active tabs,
FAB, links, spinners) from blue to gray to match icon colors for a
more cohesive look. Mute card badge text colors to pastels (blue-400,
green-500, purple-500) to keep the focus on card content.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-15 22:42:38 +01:00
b496462df5 chore: auto-fix Biome formatting and configure lint rules
All checks were successful
CI / ci (push) Successful in 15s
Run biome check --write --unsafe to fix tabs, import ordering, and
non-null assertions across entire codebase. Disable a11y rules not
applicable to this single-user app. Exclude auto-generated routeTree.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 19:51:34 +01:00
87fe94037e feat: add external link confirmation dialog for product URLs
Show an external link icon on ItemCard and CandidateCard that opens a
confirmation dialog before navigating to product URLs.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 18:48:27 +01:00
7c3740fc72 refactor: replace remaining emojis with Lucide icons
Replace all raw emoji characters in dashboard cards, empty states,
and onboarding wizard with LucideIcon components for visual consistency.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 18:47:50 +01:00
9fcb07c96b chore(06-03): update auto-generated route tree and clean up emoji references
- Route tree regenerated after icon migration changes
- Old EmojiPicker.tsx and emojiData.ts files removed (were untracked)
- Zero emoji references remain in src/

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 17:58:57 +01:00
570bcea5c9 feat(06-02): replace EmojiPicker with IconPicker across all category components
- CategoryPicker shows LucideIcon prefix and uses IconPicker for inline create
- CategoryHeader displays LucideIcon in view mode and IconPicker in edit mode
- OnboardingWizard uses IconPicker for category creation step
- CreateThreadModal drops emoji from category select options
- Fixed categoryEmoji -> categoryIcon in routes and useCategories hook

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 17:57:56 +01:00
615c8944c4 feat(06-03): update display components to use categoryIcon with LucideIcon
- Rename categoryEmoji to categoryIcon in ItemCard, CandidateCard, ThreadCard, ItemPicker
- Import and render LucideIcon at appropriate sizes (36px placeholder, 14-16px badges)
- Update hook interfaces to match server API (categoryIcon instead of categoryEmoji)
- Rename iconData.ts to iconData.tsx (contains JSX)
- Update useCategories mutation type to use icon instead of emoji

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 17:57:21 +01:00
59d1c891f9 feat(06-02): create IconPicker component with search and group tabs
- Portal-based popover with Lucide icon grid organized by 8 groups
- Search filters icons by name and keywords across all groups
- Click-outside and Escape key close the popover
- Trigger button displays selected icon or "+" placeholder

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 17:53:55 +01:00
fca1eb7d34 feat(06-01): install lucide-react and create icon data with LucideIcon component
- Install lucide-react v0.577.0
- Create iconData.ts with 119 curated icons across 8 groups
- Add LucideIcon component with kebab-to-PascalCase conversion and fallback
- Export EMOJI_TO_ICON_MAP for migration compatibility
- Build succeeds with new dependency

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 17:49:56 +01:00
acf34c33d9 feat(05-02): add always-visible 4:3 image area with placeholders to ItemCard and CandidateCard
- Replace conditional image rendering with always-present 4:3 aspect ratio area
- Show category emoji centered on gray background when no image exists
- Ensures consistent card heights across grid layouts

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 17:14:20 +01:00
3243be433f feat(05-01): redesign ImageUpload as hero area and move to top of forms
- Full-width 4:3 aspect ratio hero image area with rounded corners
- Placeholder state: gray background with ImagePlus icon and helper text
- Preview state: object-cover image with circular X remove button
- Upload state: semi-transparent spinner overlay
- Entire area clickable to upload/replace image
- Moved ImageUpload to first element in both ItemForm and CandidateForm
- Removed redundant "Image" label wrappers

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 17:11:18 +01:00
d05aac0687 feat(04-02): overhaul PlanningView with empty state, pill tabs, and category filter
- Replace inline thread creation form with modal trigger
- Add educational empty state with 3-step workflow guide
- Add Active/Resolved pill tab selector replacing checkbox
- Add category filter dropdown for thread list
- Display category emoji + name badge on ThreadCard
- Add aria-hidden to decorative SVG icons for a11y
- Auto-format pre-existing indentation issues (spaces to tabs)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 16:38:48 +01:00
eb79ab671e feat(04-02): add CreateThreadModal and uiStore modal state
- Add createThreadModalOpen state to uiStore with open/close actions
- Create CreateThreadModal with name input and category dropdown
- Modal submits via useCreateThread with name + categoryId
- Fix pre-existing formatting in uiStore.ts (spaces to tabs)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 16:36:51 +01:00
ed8508110f feat(04-01): update thread service, routes, and hooks for categoryId
- createThread now inserts categoryId from data
- getAllThreads joins categories table, returns categoryName/categoryEmoji
- updateThread accepts optional categoryId
- ThreadListItem interface includes category fields
- useCreateThread hook sends categoryId
- Fix test files to pass categoryId when creating threads

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 16:31:48 +01:00
67099551d0 feat(03-02): setup list page, detail page, and item picker
- Create SetupCard component with name, item count, weight, cost display
- Build setups list page with inline create form and grid layout
- Build setup detail page with category-grouped items and sticky totals bar
- Create ItemPicker component in SlideOutPanel with category-grouped checkboxes
- Add onRemove prop to ItemCard for non-destructive setup item removal
- Setup detail has delete confirmation dialog with collection safety note
- Empty states for both setup list and setup detail pages

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 12:50:54 +01:00
86a7a0def1 feat(03-02): navigation restructure, TotalsBar refactor, and setup hooks
- Move collection view from / to /collection with gear/planning tabs
- Rewrite / as dashboard with three summary cards (Collection, Planning, Setups)
- Refactor TotalsBar to accept optional stats/linkTo/title props
- Create DashboardCard component for dashboard summary cards
- Create useSetups hooks (CRUD + sync/remove item mutations)
- Update __root.tsx with route-aware TotalsBar, FAB visibility, resolve navigation
- Add item picker and setup delete UI state to uiStore
- Invalidate setups queries on item update/delete for stale data prevention
- Update routeTree.gen.ts with new collection/setups routes

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 12:49:03 +01:00
7d043a8585 feat(02-02): add thread detail page with candidate CRUD and resolution flow
- Create CandidateCard with edit, delete, and pick winner actions
- Create CandidateForm with same fields as ItemForm for candidate add/edit
- Build thread detail page with candidate grid and resolution banner
- Update root layout with candidate panel, delete dialog, and resolve dialog
- Hide FAB on thread detail pages, keep it for gear tab
- Resolution navigates back to planning tab after success
2026-03-15 11:46:19 +01:00
a9d624dc83 feat(02-02): add thread hooks, UI store, tab navigation, and thread list
- Create useThreads/useCandidates TanStack Query hooks
- Extend uiStore with candidate panel and resolve dialog state
- Add ThreadTabs component for gear/planning tab switching
- Add ThreadCard component with candidate count and price range chips
- Refactor index.tsx to tabbed HomePage with PlanningView
- Create placeholder thread detail route for navigation target
2026-03-15 11:44:17 +01:00
55d47d4e33 fix(01): align image upload field name and wrap category delete in transaction 2026-03-14 22:58:41 +01:00
9fcbf0bab5 feat(01-04): add onboarding wizard with settings API and persisted state
- Settings API: GET/PUT /api/settings/:key with SQLite persistence
- useSettings hook with TanStack Query for settings CRUD
- OnboardingWizard: 3-step modal overlay (welcome, create category, add item)
- Root layout checks onboarding completion flag before rendering wizard
- Skip option available at every step, all paths persist completion to DB

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 22:51:25 +01:00
12fd14ff41 feat(01-03): add slide-out panel, item form, category picker, and collection page
- CategoryPicker combobox with search, select, and inline create
- SlideOutPanel with backdrop, escape key, and slide animation
- ItemForm with all fields, validation, dollar-to-cents conversion
- Root layout with TotalsBar, panel, confirm dialog, and floating add button
- Collection page with card grid grouped by category, empty state

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 22:46:24 +01:00
b099a47eb4 feat(01-03): add data hooks, utilities, UI store, and foundational components
- API fetch wrapper with error handling and multipart upload
- Weight/price formatters for display
- TanStack Query hooks for items, categories, and totals with cache invalidation
- Zustand UI store for panel and confirm dialog state
- TotalsBar, CategoryHeader, ItemCard, ConfirmDialog, ImageUpload components

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 22:44:48 +01:00
67ff86039f feat(01-01): scaffold project with Vite, Hono, TanStack Router, Tailwind, and Drizzle config
- Initialize bun project with all frontend/backend dependencies
- Configure Vite with TanStack Router plugin, React, and Tailwind v4
- Create Hono server with health check and static file serving
- Set up TanStack Router file-based routes with root layout
- Add Drizzle config, Biome linter, and proper .gitignore

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 22:33:28 +01:00