- Replace old single-action FAB with FabMenu component
- Add CatalogSearchOverlay to root layout
- FAB now visible on all authenticated non-public routes
- Detect setups page for conditional New Setup menu item
- Remove unused openAddPanel reference
- Add auth redirect in root layout for unauthenticated users
- Proxy OIDC routes (/login, /callback, /logout) through Vite dev server
- Strip Secure flag from OIDC cookies in dev mode (HTTP localhost)
- Disable retry on auth query to prevent stale cookie loops
- Fix SQLite .get()/.all()/.run() calls in category and global-item
services for PostgreSQL compatibility
- Add userId scoping to category service functions
- Add OIDC error logging in auth middleware
- Apply linter auto-formatting across affected files
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Create public profile page at /users/$userId with avatar, name, bio, setups
- Create PublicSetupCard component for profile page setup listing
- Add isPublic toggle button on setup detail page
- Add Public badge to SetupCard in list view
- Update useSetups hook with isPublic field on interfaces
- Replace all /uploads/ path construction with imageUrl presigned URLs
- Add imageUrl prop to ItemCard, CandidateCard, CandidateListItem, ComparisonTable
- Update ImageUpload to use presigned URLs + local preview for new uploads
- Pass imageUrl through from parent components (CollectionView, forms, routes)
- Login page redirects to Logto instead of showing credential form
- AuthState uses string id (Logto sub claim) instead of number
- Remove useLogin, useSetup, useChangePassword hooks
- useLogout redirects to /logout (server-side OIDC logout)
- Remove ChangePasswordSection from settings page
- Update UserMenu to use new useLogout API
- Settings page shows API keys section when authenticated
Two fixes:
- Remove onSettled clearing tempItems before refetch completes,
let useEffect clear it when fresh server data arrives
- Track isDragging ref to suppress edit panel click after drag
- Remove layout="position" which interfered with reorder detection
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Remove transition-all from list items (fights framer-motion layout)
- Add layout="position" to Reorder.Item for proper sibling tracking
- Replace CSS gap with marginBottom (gap confuses layout engine)
- Add explicit short transition duration for snappy reorder
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The previous approach used onPointerUp on the Reorder.Group which
fired unreliably — triggering on non-drag clicks and sometimes not
at all after a drag. Moving to onDragEnd on each Reorder.Item gives
clean, predictable drag-to-reorder behavior.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Adds Gear/Planning/Setups pill tabs to the collection page so users
can switch tabs without going back to the dashboard. Also skips
React Query retries on 404 responses for immediate error display.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Adds export (GET /api/items/export) and import (POST /api/items/import) routes
backed by a pure csv.service with no external deps, plus useExportItems/useImportItems
hooks and an Import/Export section in the Settings page.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds SetupImpactSelector dropdown and ImpactDeltaBadge inline badge, wired into the thread detail page. Delta badges appear on CandidateListItem, CandidateCard, and ComparisonTable (Weight Impact / Price Impact rows) whenever a setup is selected for comparison.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- totals.service: multiply weight/cost sums by quantity in category and global totals
- setup.service: multiply by quantity in getAllSetups SQL subqueries; expose quantity in getSetupWithItems item list
- thread.service: explicitly pass quantity: 1 when inserting resolved item
- ItemForm: add Quantity number input (min=1, default=1) after price field
- ItemCard: show ×N badge next to item name when quantity > 1
- CollectionView: pass quantity prop to ItemCard in both filtered and grouped views
- $setupId.tsx: pass quantity to ItemCard; multiply by quantity in client-side per-setup totals
- WeightSummaryCard: multiply by quantity in all chart and legend weight calculations
- useItems / useSetups: add quantity to ItemWithCategory / SetupItemWithCategory interfaces
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Created useFormatters() combining useWeightUnit + useCurrency + formatWeight/formatPrice
into a single hook returning weight(grams) and price(cents) bound functions plus
raw unit and currency values. Updated all 14 consumer files to use the new hook,
removing the repeated 4-import + 2-hook-call pattern from each.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Moves CollectionView, PlanningView, and SetupsView out of the 634-line collection/index.tsx into dedicated component files. Pure extraction — zero logic changes.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds a TanStack Router error boundary to the root route so rendering errors or uncaught React Query failures show a friendly error page instead of white-screening the app. The error boundary displays a professional error message with a "Try again" button that resets state and invalidates router data.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Exclude drizzle/ and .planning/ from Biome (generated files with
incompatible formatting). Auto-fix import ordering and formatting
in existing source files.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The wizard creates categories via POST which requires auth.
Gate the wizard on isAuthenticated so users create their
account first via Sign In, then the wizard appears.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- 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
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>
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>
- 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>
- 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
- 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>
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>
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>
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>
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>
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>
- 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>
- 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>
- 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
- 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>
- 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>
- 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>