Commit Graph

354 Commits

Author SHA1 Message Date
c8ebbf8139 feat(25-01): Zod schemas, upsert service functions, passing tests
- Add upsertGlobalItemSchema and bulkUpsertGlobalItemsSchema to schemas.ts
- Add UpsertGlobalItemInput and BulkUpsertGlobalItemsInput types to types.ts
- Implement upsertGlobalItem with onConflictDoUpdate and tag sync
- Implement bulkUpsertGlobalItems processing array in single transaction
- Fix migration 0003 to only add new columns + unique constraint
- All 21 tests pass including 8 new upsert operation tests
2026-04-10 10:58:36 +02:00
39ef9cc433 feat(25-01): add attribution columns and unique constraint to globalItems
- Add sourceUrl, imageCredit, imageSourceUrl nullable columns
- Add unique constraint on (brand, model) pair
- Generate migration 0003_loving_serpent_society.sql
2026-04-10 10:55:55 +02:00
e1afd542ac fix(24): add withImageUrls to public setup endpoint
Public setup view was missing image URL enrichment, causing item images
to be absent for anonymous visitors. Matches the private endpoint pattern.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 10:17:32 +02:00
7b0efae0c4 feat(24-02): render-first root layout, guarded write actions, public setup viewing
- Remove authLoading spinner gate — app renders immediately for all visitors
- Expand isPublicRoute to include /, /global-items/*, /setups/*, /users/, /login
- Replace hard window.location.href redirect with soft navigate() after auth resolves
- Remove onboarding loading spinner — pass isAuthenticated as enabled to guard query
- Add AuthPromptModal to root JSX for global availability
- Guard Add to Collection and Add to Thread buttons with isAuthenticated check
- Rework setup detail page to use usePublicSetup for anonymous visitors
- Wrap all write action UI (Add Items, Delete, Public toggle, remove/classify) in isAuthenticated guards
2026-04-10 10:09:41 +02:00
5619016e41 feat(24-01): apply tiered rate limits to public GET endpoints
- Import createRateLimit in server index
- Create browseTier (120 req/min) for list/search endpoints
- Create detailTier (60 req/min) for individual resource endpoints
- Apply browseTier to /api/global-items and /api/tags GET routes
- Apply detailTier to /api/global-items/:id, /api/setups/:id/public, /api/users/:id/profile GET routes
- Rate limits placed before auth middleware per D-07, D-08
2026-04-10 10:07:38 +02:00
cd85715d05 feat(24-02): add auth prompt state, modal, usePublicSetup hook, guard onboarding
- Extend uiStore with showAuthPrompt/openAuthPrompt/closeAuthPrompt state
- Create AuthPromptModal component with sign in/sign up CTAs pointing to /login
- Add usePublicSetup hook to useSetups for anonymous setup viewing via public API
- Rework useOnboardingComplete to accept enabled param (guards auth-gated call)
2026-04-10 10:06:59 +02:00
afab8175f9 feat(24-01): refactor rateLimit to factory pattern with createRateLimit
- Add createRateLimit(maxAttempts, windowMs) factory function
- Rewrite rateLimit export to delegate to factory (backward compatible)
- Keep shared store, getClientIp, cleanup, and _resetForTesting unchanged
- Add createRateLimit factory test suite with 5 test cases
- All existing rateLimit middleware tests still pass
2026-04-10 10:06:19 +02:00
570be6fcc1 fix: prevent crash on login when user has no active threads
All checks were successful
CI / ci (push) Successful in 1m4s
CI / e2e (push) Has been skipped
CI / deploy (push) Successful in 13s
activeThreads[0].id in useEffect dependency array threw when the array
was empty. Use optional chaining to safely handle the empty case.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 22:48:29 +02:00
3b29248845 fix: return database user ID from /api/auth/me instead of Logto sub
Some checks failed
CI / ci (push) Failing after 1m8s
CI / deploy (push) Has been skipped
CI / e2e (push) Has been skipped
The /me endpoint was returning auth.sub (Logto's opaque string) as the
user ID, but the frontend and other API endpoints expect numeric DB IDs.
This caused "can't access property 'id', w[0] is undefined" after login.

Also documents Logto OIDC setup requirements (scopes, env vars) in
CLAUDE.md.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 22:16:59 +02:00
9dca657ab1 fix: add OIDC startup diagnostic and fix HTTPException handling
All checks were successful
CI / ci (push) Successful in 1m4s
CI / e2e (push) Has been skipped
CI / deploy (push) Successful in 25s
The @hono/oidc-auth middleware catches all errors and rethrows as
"Invalid session", hiding the real cause. This adds a startup probe
to OIDC discovery endpoint so the actual error appears in logs.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 21:33:59 +02:00
d519a83cc4 infra: migrate deployment to Coolify with Garage S3
Some checks failed
CI / ci (push) Failing after 19s
CI / deploy (push) Has been skipped
CI / e2e (push) Has been skipped
- Remove docker-compose files (Coolify manages services individually)
- Replace MinIO with Garage (S3-compatible, actively maintained)
- Add CI deploy job: build+push :develop image on every green Develop push
- Add Coolify webhook trigger for automatic redeployment
- Update README, .env.example, and storage references
- Rename migrate script to provider-agnostic name

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 15:28:43 +02:00
41e58d0153 wip: in-progress feature work (manual entry, collection view)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 15:28:34 +02:00
7dbbfcb915 fix: resolve all 13 remaining test failures
Some checks failed
CI / ci (push) Failing after 56s
CI / e2e (push) Has been skipped
- OAuth: add userId to oauth_codes schema and migration, derive userId
  from stored auth code/token record instead of passing separately
- Auth middleware tests: destructure {db, userId} from createTestDb,
  pass userId to createApiKey, fix error message assertion
- MCP tests: add missing await on getCollectionSummary and
  createSecondTestUser calls

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 20:25:41 +02:00
3f3c08c512 fix: format phase 22 worktree files that were committed unformatted
Some checks failed
CI / ci (push) Failing after 12s
CI / e2e (push) Has been skipped
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 19:49:09 +02:00
3638e7b240 fix: resolve all lint errors across source and test files
Some checks failed
CI / ci (push) Failing after 11s
CI / e2e (push) Has been skipped
- Fix unused function parameters (prefix with _)
- Fix unused imports in test files
- Fix import ordering in test files
- Auto-fix formatting issues across 22 files

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 19:39:47 +02:00
e19d40e232 refactor: strip stats and unit switcher from top bar
Some checks failed
CI / ci (push) Failing after 19s
CI / e2e (push) Has been skipped
Remove weight unit switcher pills and collection stats (items, weight,
spent) from TotalsBar. Top bar now shows only logo/title and user menu.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 19:32:02 +02:00
024e9f909b fix: make image read-only for reference items, rename delete to remove
- Reference items show catalog image as read-only in edit mode (no upload)
- "Delete" button renamed to "Remove from Collection" for reference items

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 19:27:16 +02:00
69308e293f fix: restrict edit mode for reference items to personal fields only
Reference items (linked to global catalog) now show name, brand, weight,
and MSRP as read-only in edit mode with "from the catalog" hint. Only
personal fields (notes, category, quantity, image, product URL) are
editable. Standalone items retain full edit access.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 19:22:09 +02:00
56b81ee8ab fix(23): resolve UAT issues — duplicate header, image position, catalog submit style
- Remove duplicate back arrow/header from ManualEntryForm (overlay already shows it)
- Move ImageUpload to top of ManualEntryForm for visual cohesion
- Change "Submit to Catalog?" from text link to checkbox-style toggle

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 19:17:05 +02:00
f0e1cf4b9b feat(23-01): wire ManualEntryForm into CatalogSearchOverlay
- Add manualEntryMode and savedItemName local state with resets on overlay close
- Back arrow is context-sensitive: returns to search when in manual mode, closes overlay otherwise
- Header text updates to 'Manual Entry' or 'Item Added' in manual entry mode
- Search input, filters, and view toggles hidden when in manual entry mode
- EmptyState now accepts onAddManually callback with context-sensitive link text
- Persistent 'Can't find it? Add manually' link shown below search results
- ManualEntryForm rendered inline when manualEntryMode is active
- Success card shown after save with 'Submit to Catalog?' toast-only button
- 'Add Another' resets to search, 'Done' closes overlay
2026-04-06 17:56:41 +02:00
153b6cb76a feat(23-01): create ManualEntryForm component
- Compact form with name, category, weight, price, purchase price, product URL, notes, image
- Uses CategoryPicker and ImageUpload reusable components
- Calls useCreateItem without globalItemId for standalone item creation
- Back arrow (ArrowLeft) calls onBack prop to return to search results
- Converts price strings to cents via Math.round(Number(val) * 100)
- No toast.success on save — success card in overlay handles feedback
2026-04-06 17:38:13 +02:00
5ae0dd1b2d docs(23): capture phase context 2026-04-06 17:25:35 +02:00
c33b7c7bdc feat(22-02): build AddToThreadModal with thread picker and new thread flow
- Create AddToThreadModal with pick/create modes for thread selection
- Support existing thread selection with category display
- Support new thread creation with candidate in one step
- Pre-select session thread via catalogSessionThreadId
- Auto-switch to create mode when no active threads exist
- Wire AddToThreadModal at root layout level
2026-04-06 16:00:34 +02:00
ed76236294 feat(22-01): wire catalog search and detail page to collection/thread modals
- Replace handleAddStub with handleAdd dispatching to correct modal by mode
- Global item detail page: add both "Add to Collection" and "Add to Thread" buttons
- Remove console.log stub from detail page
- Import useUIStore in both components

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 15:56:40 +02:00
f309c73304 feat(22-01): add UIStore modal states, AddToCollectionModal, and sonner toasts
- Extend UIStore with addToCollectionModal, addToThreadModal, catalogSessionThreadId
- Create AddToCollectionModal with category dropdown, notes, purchase price
- Install sonner and add Toaster + AddToCollectionModal to root layout
- closeCatalogSearch now resets catalogSessionThreadId

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 15:55:44 +02:00
a62357c063 Merge branch 'worktree-agent-a00c5cfa' into Develop
# Conflicts:
#	.planning/REQUIREMENTS.md
#	.planning/STATE.md
#	src/client/components/CatalogSearchOverlay.tsx
#	src/client/routes/threads/$threadId.tsx
2026-04-06 15:15:57 +02:00
4c79735426 feat(21-03): remove slide-out panels from root layout and clean UIStore
- Remove Item and Candidate SlideOutPanel instances from __root.tsx
- Remove SlideOutPanel, ItemForm, CandidateForm imports from root
- Remove panel state (panelMode, editingItemId, candidatePanelMode, editingCandidateId) from UIStore
- Remove panel actions (openEditPanel, openAddPanel, closePanel, etc.) from UIStore
- Preserve currentThreadId derivation for CandidateDeleteDialog
- Update CollectionView empty state to use catalog search instead of add panel
- Update ItemForm/CandidateForm with onClose prop replacing store panel close
- Clean all dead panel references across src/client/
2026-04-06 15:12:59 +02:00
1f79c5ca3c feat(21-03): rewire card click handlers to navigate to detail pages
- ItemCard navigates to /items/$itemId instead of opening edit panel
- CandidateCard navigates to /threads/$threadId/candidates/$candidateId
- CandidateListItem navigates to candidate detail page
- CatalogSearchOverlay cards navigate to /global-items/$globalItemId
- Add button on catalog cards uses stopPropagation to prevent navigation
2026-04-06 15:07:51 +02:00
a5a40b2068 Merge branch 'worktree-agent-a4608610' into Develop
# Conflicts:
#	.planning/ROADMAP.md
#	.planning/STATE.md
#	src/client/routes/threads/$threadId.tsx
2026-04-06 15:04:13 +02:00
6474033414 Merge branch 'worktree-agent-a1363a63' into Develop
# Conflicts:
#	.planning/ROADMAP.md
#	.planning/STATE.md
2026-04-06 15:04:00 +02:00
52c9ec3fe2 docs: stage before wave 1 merge 2026-04-06 15:03:45 +02:00
47b416effd feat(21-02): replace slide-out panel with add-candidate modal on thread page
- Add local AddCandidateModal component with all candidate form fields
- Remove openCandidateAddPanel UIStore dependency
- Modal includes image upload, category picker, pros/cons, validation
2026-04-06 15:02:13 +02:00
408025bb36 feat(21-01): enhance catalog detail page with Add to Collection button
- Add image placeholder (package icon) when no imageUrl exists
- Add 'Add to Collection' button stub between specs and description
- Button styled per D-10, logs to console (actual flow wired in Phase 22)
- Consistent layout with item detail page
2026-04-06 15:02:07 +02:00
3228bcadbe feat(21-01): create private item detail page with edit mode toggle
- Full detail page at /items/:id with hero image, name, spec badges, notes, product link
- Edit mode toggle: read-only by default, editable inputs when Edit clicked
- Save persists via useUpdateItem, Cancel reverts to read-only
- Duplicate and Delete actions via existing hooks/dialogs
- Back link to /collection, loading shimmer, error state
- CategoryPicker and ImageUpload in edit mode
2026-04-06 15:01:10 +02:00
cecaf78ead feat(21-02): restructure thread route and create candidate detail page
- Move $threadId.tsx to $threadId/index.tsx for nested route support
- Create candidate detail page at /threads/:threadId/candidates/:candidateId
- Edit mode toggle with form fields for all candidate properties
- Back navigation, pick-as-winner, and delete actions
2026-04-06 15:00:25 +02:00
eb7f37fe28 feat(quick-260406-j44): add idempotent dev seed runner and db:seed:dev script
- Seed runner inserts user, categories, global items, tags, user items,
  threads with candidates, setups, and settings in FK order
- Idempotent: checks for dev-user-seed logtoSub before running
- Reuses seedGlobalItems() for base catalog data
- Added db:seed:dev npm script to package.json

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 13:52:45 +02:00
24f3a8a8a2 feat(quick-260406-j44): add dev seed data constants for bikepacking gear
- 10 categories, 36 global items with realistic weights/prices
- 17 user items (10 catalog-linked, 7 standalone)
- 3 threads with candidates, 2 setups, tag assignments, settings

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 13:50:58 +02:00
e13f9584fa feat(20-02): wire FabMenu and CatalogSearchOverlay into root layout
- 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
2026-04-06 08:04:10 +02:00
720460852c feat(20-02): add FabMenu and CatalogSearchOverlay components
- FabMenu with animated mini menu (Add to Collection, Start Thread, New Setup)
- CatalogSearchOverlay with debounced search, tag chip filtering, result cards
- Loading skeleton grid and empty state
- Framer Motion animations for menu entrance/exit and overlay transitions
2026-04-06 08:02:59 +02:00
62249b5b48 Merge branch 'worktree-agent-adbc35a5' into Develop
# Conflicts:
#	.planning/STATE.md
#	drizzle-pg/meta/0002_snapshot.json
#	drizzle-pg/meta/_journal.json
#	src/db/schema.ts
2026-04-06 08:00:04 +02:00
67facea338 feat(20-01): extend UIStore with FAB/catalog state, add useTags hook, update useGlobalItems
- Add fabMenuOpen, openFabMenu, closeFabMenu to UIStore
- Add catalogSearchOpen, catalogSearchMode, openCatalogSearch, closeCatalogSearch
- openCatalogSearch also closes FAB menu (natural flow)
- Create useTags hook with 5-min staleTime cache
- Add optional tags parameter to useGlobalItems for tag filtering
2026-04-06 07:57:47 +02:00
2ec1276849 feat(20-01): add tags table, tag service/route, register global-items route
- Create tags table in schema with id, name (unique), createdAt
- Generate migration for tags table
- Create tag.service.ts with getAllTags (id+name, alphabetical order)
- Create tags.ts route with GET / handler
- Register /api/global-items and /api/tags routes in index.ts
- Add auth skip for GET /api/tags and GET /api/global-items
2026-04-06 07:56:40 +02:00
0a233c754d feat(19-03): add COALESCE merge for reference items in secondary services
- Setup service: LEFT JOIN globalItems in getAllSetups totals and getSetupWithItems
- Totals service: LEFT JOIN globalItems in getCategoryTotals and getGlobalTotals
- Profile service: LEFT JOIN globalItems in getPublicProfile totals and getPublicSetupWithItems
- CSV service: LEFT JOIN globalItems in exportItemsCsv for merged name/weight/price
2026-04-06 00:26:13 +02:00
ecc6ac689a feat(19-03): add tag filtering to global item search and migrate owner count
- searchGlobalItems now accepts tagNames param with AND intersection logic
- Owner count uses items.globalItemId instead of removed itemGlobalLinks
- Removed linkItemToGlobal and unlinkItemFromGlobal functions
- Route handlers now async with tags query param support
- Rewrote tests to async PGlite pattern, added tag filtering tests

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 20:55:36 +02:00
8a5ee731d0 feat(19-02): add catalog-linked candidates, branched resolution, remove link/unlink routes
- getThreadWithCandidates LEFT JOINs globalItems with COALESCE for name, weight, price, image
- createCandidate accepts and stores globalItemId
- resolveThread branches: reference item (globalItemId set) vs standalone (full data copy)
- Removed link/unlink endpoints from items route (replaced by direct globalItemId FK)
- 6 new tests for catalog-linked candidates and branched resolution
2026-04-05 20:49:56 +02:00
d1ffd79bbb feat(19-02): add COALESCE merge for reference items in item service
- getAllItems and getItemById LEFT JOIN globalItems with COALESCE for name, weight, price, image
- createItem accepts globalItemId and purchasePriceCents, stores brand+model as fallback name
- duplicateItem preserves globalItemId and purchasePriceCents
- updateItem type includes globalItemId and purchasePriceCents
- 10 new tests for reference item creation and merged data retrieval
2026-04-05 20:34:54 +02:00
e9baa8d7e0 feat(19-01): update Zod schemas, types, and seed script for reference model
- Add globalItemId and purchasePriceCents to createItemSchema
- Add globalItemId to createCandidateSchema
- Add tags param to searchGlobalItemsSchema
- Remove linkItemSchema from schemas and types
- Replace ItemGlobalLink with Tag and GlobalItemTag types
- Convert seedGlobalItems to async, add seedTags with 30 curated tags
2026-04-05 20:27:51 +02:00
5df513c138 feat(19-01): update schema with reference item model and tags tables
- Add globalItemId and purchasePriceCents columns to items table
- Add globalItemId column to threadCandidates table
- Add tags and globalItemTags tables for tag system
- Remove itemGlobalLinks table (replaced by direct FK)
- Generate migration with data migration step before table drop
2026-04-05 20:25:59 +02:00
574a12e6fa fix: OIDC auth flow, Vite proxy, and PostgreSQL query compat
- 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>
2026-04-05 18:25:31 +02:00
b2936b098e Merge branch 'worktree-agent-af80e237' into Develop
# Conflicts:
#	.planning/REQUIREMENTS.md
#	.planning/STATE.md
2026-04-05 13:21:56 +02:00