Commit Graph

339 Commits

Author SHA1 Message Date
053d56236f fix(35-01): replace login page card UI with immediate useEffect redirect (FIX-04)
- Remove auth check, useNavigate, useTranslation, and full card UI
- LoginPage now renders only "Signing in..." and immediately navigates to server /login
- Server /login route handles Logto OIDC redirect; no client-side logic needed
2026-04-19 19:41:57 +02:00
b43a932217 fix(35-01): extend ItemWithCategory with image and currency fields (FIX-02)
- Add imageUrl, dominantColor, cropZoom, cropX, cropY, priceCurrency to interface
- Server already returns these fields via withImageUrls(); type was just incomplete
2026-04-19 19:41:44 +02:00
7fca92985a fix(35-01): wire Add Candidate button to CatalogSearchOverlay, delete AddCandidateModal
- Replace setAddCandidateOpen(true) with openCatalogSearch("thread") + setCatalogSessionThreadId
- Remove addCandidateOpen useState
- Delete entire AddCandidateModal component (~300 lines of dead code)
- Remove imports only used by the deleted modal: useCreateCandidate, useCurrency, ImageUpload, CategoryPicker
2026-04-19 19:41:33 +02:00
beaea46e92 fix: use CategoryPicker in AddToThreadModal new-thread create mode
Replaces plain <select> with CategoryPicker for consistency with
ManualEntryForm, CreateThreadModal, and other thread creation flows.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-19 17:00:03 +02:00
5f63e6f75d fix: resolve Bun mock isolation contamination across test files
Some checks failed
CI / ci (push) Failing after 1m35s
CI / e2e (push) Has been skipped
CI / deploy (push) Has been skipped
- storage.service.ts: use dynamic import() inside each function so the
  current @aws-sdk mock is always picked up regardless of module load order
- images.test.ts + image.service.test.ts: replace module-level storage.service
  mock with @aws-sdk/client-s3 mock to avoid contaminating storage.service.test.ts
- routes/auth.test.ts: remove unnecessary oauth.service mock (no test uses
  verifyAccessToken) which was contaminating oauth.service.test.ts
- middleware/auth.test.ts: complete oauth.service mock shape with all exports

All 464 tests now pass in a single bun test run.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-19 16:19:39 +02:00
4ccbb2b070 fix: wire catalog add buttons, fix Trans bold rendering, lint cleanup
Some checks failed
CI / ci (push) Failing after 1m44s
CI / e2e (push) Has been skipped
CI / deploy (push) Has been skipped
- CatalogSearchOverlay: replace handleAddStub with real openAddToCollection/openAddToThread routing based on catalogSearchMode
- ConfirmDialog + __root.tsx: swap t() for Trans component on deleteItemMessage, deleteCandidateMessage, pickWinnerMessage — fixes <bold> rendering as literal text
- Biome format pass: fix 23 lint/format errors across scripts, services, tests
- Planning: mark all UAT and verification gaps resolved for phases 07, 11, 16, 20, 21, 22, 24, 32, 34; close debug sessions

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-19 15:36:16 +02:00
44b1eac0ba feat(catalog): migrate dev seed data to manufacturer-slug-based global items
Replace brand text field with manufacturerSlug in DEV_GLOBAL_ITEMS,
global-items-seed.json, and seed-global-items.ts. Add DEV_MANUFACTURERS
for dev-only brands not in SEED_MANUFACTURERS. Expand SEED_MANUFACTURERS
with 8 additional manufacturers referenced by seed JSON (Nemo, Therm-a-Rest,
Toaks, Katadyn, HydraPak, Nitecore, Outdoor Research, Exposure Lights).
Update dev-seed.ts to resolve slug→id before insert and use manufacturerId
as the deduplication key.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-18 16:37:27 +02:00
0b4715b80c fix: update all tests and MCP catalog tool for manufacturerId schema migration 2026-04-18 16:30:11 +02:00
a508773809 feat: all services join manufacturers for global item brand display 2026-04-18 16:24:24 +02:00
2924c2269c feat: item service joins manufacturers for brand display 2026-04-18 16:22:10 +02:00
12b3f8e380 feat: upsertGlobalItemSchema — brand → manufacturerSlug 2026-04-18 16:21:32 +02:00
5037350aa0 feat: global-item service uses manufacturerSlug, joins manufacturers for brand 2026-04-18 16:21:25 +02:00
8ff680ef92 feat: migrate globalItems — drop brand text, add manufacturerId FK 2026-04-18 16:19:31 +02:00
f868bbdecf feat: seed manufacturers list, update seedGlobalItems to resolve by name
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-18 16:16:52 +02:00
ec27df1d0f feat: manufacturers route — list, get, create
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-18 16:16:27 +02:00
8c1b19f07d feat: manufacturer service with list, get, create
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-18 16:15:40 +02:00
7de3e9e957 feat: add manufacturers table to schema
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-18 14:54:23 +02:00
bea386e7db style(i18n): fix lint — formatting and import ordering across 21 files
All checks were successful
CI / ci (push) Successful in 1m21s
CI / e2e (push) Has been skipped
CI / deploy (push) Successful in 1m15s
Biome auto-fix for formatting (line length, ternary wrapping) and
import organization in files touched by phase 34 i18n work.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-18 14:49:10 +02:00
dbab84ef2a fix(i18n): wire useTranslation into SetupsView — close verification gap
Replace hardcoded English strings in SetupsView.tsx with t() calls
using existing setups namespace keys. Closes the 1 gap found during
phase 34 verification.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-18 14:41:23 +02:00
31297a3921 fix(34-05): add missing German translation keys to collection namespace
- Add form.msrp, form.purchasePrice, form.itemNamePlaceholder, form.optionalNotes
- Fixes key parity test failure in tests/i18n/locales.test.ts
2026-04-18 14:08:51 +02:00
bf64b8f6a5 chore: merge executor worktree (worktree-agent-a1291d63 — plan 34-02) 2026-04-18 14:04:14 +02:00
2aa156a6b7 feat(34-02): extract hardcoded strings from modals, routes, and catalog
- AddToCollectionModal: all labels, placeholders, toast messages
- collection/index.tsx: tab labels (Gear/Planning)
- threads/$threadId/index.tsx: thread detail page and AddCandidateModal
- items/$itemId.tsx: back links, action buttons, field labels, metadata
- setups/$setupId.tsx: all setup detail strings and confirm dialog
- users/$userId.tsx: public profile page strings
- global-items/index.tsx: discover/catalog filter UI strings
- Added catalog.json namespace (en + de) and registered in i18n.ts
- Extended en/de threads, setups, collection, common locales with missing keys
2026-04-18 14:01:09 +02:00
6fd8874970 feat(34-02): extract hardcoded strings from thread/candidate components
- CandidateCard: replace all hardcoded titles and badge text with t()
- CandidateListItem: add useTranslation, replace winner/delete/open labels and +/- Notes badge
- CandidateForm: add useTranslation, replace all form labels, placeholders, validation errors, submit button
- ComparisonTable: move STATUS_LABELS inside component with t(), replace all ATTRIBUTE_ROWS labels, View button, impact row labels
- StatusBadge: refactor STATUS_CONFIG to STATUS_ICONS + runtime STATUS_LABELS via t()
- CreateThreadModal: replace title, thread name label, category label, placeholder, cancel/submit buttons, error messages
- AddToThreadModal: replace modal titles, labels, placeholders, back/cancel/submit buttons, error messages
- threads.json: extend candidateForm with category, notes, pros, cons, product link labels and all placeholders
2026-04-18 13:44:26 +02:00
c5af1247c0 feat(34-02): i18n collection and item components
- CollectionView: t() for empty state, stats labels, filter text
- ItemCard: t() for tooltip title attributes
- ItemForm: t() for all form labels, placeholders, error messages, buttons
- CategoryPicker: t() for search placeholder, create button, no results
- CategoryFilterDropdown: t() for all categories label, search placeholder
- CategoryHeader: t() for save/cancel buttons, item count
- WeightSummaryCard: t() for title, legend labels, view mode toggle
- ItemPicker: t() for panel title, empty state, action buttons
- ManualEntryForm: t() for all form labels, error messages, submit button
- LinkToGlobalItem: t() for all UI chrome strings
- ProfileSection: t() for all form labels, messages, buttons
- collection.json: added new keys for categoryPicker, categoryFilter, weightSummary, itemPicker, categoryHeader, linkToGlobal, manualEntry, profileSection, itemCard
2026-04-18 13:35:59 +02:00
23172f794f fix(34-08): add 58 missing German translations to 5 de/*.json locale files
- de/common.json: add home, imageUpload, profile sections (34 keys)
- de/settings.json: add currency.suggestion, currency.switch, showConversions (4 keys)
- de/threads.json: add card.candidates, card.candidates_one, planning section (11 keys)
- de/setups.json: add card.by, card.anonymous, impact.compareWith (3 keys)
- de/collection.json: add tabs.setups, totals, classificationBadge (6 keys)
- Fixed JSON syntax error: replaced smart quotes in dangerZoneDescription with single quotes
- All German text uses proper Unicode umlauts throughout
- bun test tests/i18n/locales.test.ts: 19 pass, 0 fail
2026-04-18 13:29:12 +02:00
1963faea84 fix(34-07): replace ASCII umlaut fallbacks with proper Unicode in all German locale files
- common.json: Löschen, Schließen, Zurück, Bestätigen, Änderungen, Überspringen, Gegenstände, etc.
- collection.json: Ausrüstung, Gegenstände, Zusätzliche, Hinzufügen
- threads.json: wählen, Kategorie, hinzufügen, Sammlung, hinzugefügt
- setups.json: Ausrüstung, Gegenstände, Öffentlich, Läuft, können, Zurückschalten
- onboarding.json: Ausrüstung, Gegenstände, wählen, fügen, überspringen, prüfen, Stöbern
- settings.json: Schlüssel, Währung, Wählen, Ändern, Gegenstände, Ausrüstung
2026-04-17 20:30:48 +02:00
480abdd17f feat(34-06): wire useTranslation into 10 remaining components
- ThreadTabs: tab labels (gear, planning, setups) via collection namespace
- PlanningView: section title, tab labels, empty state steps, CTAs via threads namespace
- TotalsBar: 'Sign in' link via common.auth.signIn
- ThreadCard: resolved badge and candidate count (plural) via threads namespace
- PublicSetupCard: by/anonymous and item count (plural) via setups namespace
- SetupImpactSelector: compare dropdown placeholder via setups.impact.compareWith
- ClassificationBadge: base/worn/consumable labels via collection.classificationBadge
- ImpactDeltaBadge: add mode label via setups.impact.adding
- ImageUpload: click-to-add, error messages via common.imageUpload
- DashboardCard: skipped (renders props only, no hardcoded UI strings)
- Add card, planning keys to en/de threads.json
- Add classificationBadge, tabs, totals keys to en/de collection.json
- Add card.by, card.anonymous, impact.compareWith to en/de setups.json
- Add imageUpload keys to en/de common.json
- Build passes, all 19 i18n parity tests pass
2026-04-17 20:26:50 +02:00
755c0ab89f feat(34-06): wire useTranslation into routes and settings currency suggestion
- Add useTranslation to routes/index.tsx: home section headings use t()
- Add useTranslation to routes/profile.tsx: all profile/security/danger zone strings use t()
- Wire currency suggestion banner in settings.tsx with t() interpolation
- Wire showConversions section title/description in settings.tsx
- Add home and profile keys to en/common.json
- Add currency.suggestion, currency.switch, showConversions to en/settings.json
- Add corresponding German translations with proper umlauts to de/common.json and de/settings.json
2026-04-17 20:21:54 +02:00
28dfef555c feat: wire currency conversion into price display
All checks were successful
CI / ci (push) Successful in 1m22s
CI / e2e (push) Has been skipped
CI / deploy (push) Successful in 14s
useFormatters().price() now accepts an optional sourceCurrency param.
When showConversions is enabled and the source differs from the user's
currency, it converts via ECB rates and shows dual format:
"€200.00 (~$218.00)". ItemCard and CollectionView pass priceCurrency
through from API data. Setup detail items also pass priceCurrency.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 21:44:32 +02:00
c4ddc573d4 fix: price labels use user's selected currency instead of hardcoded $
All checks were successful
CI / ci (push) Successful in 1m21s
CI / e2e (push) Has been skipped
CI / deploy (push) Successful in 15s
Replaced hardcoded "Price ($)" labels across 6 components and 2 locale
files to display the user's selected currency (EUR, GBP, USD, etc.).
AddToCollectionModal also updated to show correct currency.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 21:33:32 +02:00
23027551b4 fix: currency suggestion uses region detection, seed adds market prices
All checks were successful
CI / ci (push) Successful in 1m24s
CI / e2e (push) Has been skipped
CI / deploy (push) Successful in 15s
- Currency auto-suggestion now uses locale region subtag (en-US → US → USD,
  en-DE → DE → EUR) instead of language prefix. Fixes wrong suggestion for
  users with English browser locale in European countries.
- Added dismiss button (X) to suggestion banner
- Dev seed script now clears existing dev data before re-seeding (safe to
  run repeatedly without manual DB cleanup)
- Added DEV_MARKET_PRICES with multi-market UVP data for 10 global items
  (EU/US/UK prices) and community prices for 5 owned items

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 21:27:57 +02:00
51c8703a3d fix: share modal UX improvements and creator name fallback
All checks were successful
CI / ci (push) Successful in 1m26s
CI / e2e (push) Has been skipped
CI / deploy (push) Successful in 21s
- Share links section always visible (not just in link/public mode),
  supporting future write-access link shares on public setups
- Link list layout improved: URL and expiration stacked vertically,
  action buttons have hover backgrounds, trash icon replaces X
- Public setup cards show "by Anonymous" when creator has no display name

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 20:49:28 +02:00
4c80e9aa3c fix: allow unauthenticated access to /items/* with setup context
All checks were successful
CI / ci (push) Successful in 1m23s
CI / e2e (push) Has been skipped
CI / deploy (push) Successful in 15s
Items accessed via ?setup= or ?share= query params are now treated as
public routes, preventing the auth redirect to /login.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 20:34:13 +02:00
4b26a6c88e feat: public item detail view for shared and public setups
All checks were successful
CI / ci (push) Successful in 1m23s
CI / e2e (push) Has been skipped
CI / deploy (push) Successful in 15s
Items in shared/public setups are now viewable without auth. Clicking
an item in a shared setup navigates to /items/:id?setup=:setupId&share=token
which fetches the item via a public endpoint authorized by the setup's
visibility or share token. Read-only mode hides all owner controls.

- Added getSetupItemById service function
- Added GET /api/shared/:token/items/:itemId endpoint
- Added GET /api/setups/:setupId/items/:itemId/public endpoint
- Added usePublicSetupItem and useSharedSetupItem hooks
- Item detail page detects setup context and switches to public fetch
- Back link returns to setup instead of collection in setup context

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 20:17:54 +02:00
731d677da6 fix: shared setup items link to catalog instead of requiring auth
Items with a globalItemId now link to /global-items/:id (public) in
shared and public setup views. Items without a catalog link are not
clickable. Owner view behavior unchanged.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 20:10:02 +02:00
1fbd9bc609 fix: inject db context for /s/* short share URL route
All checks were successful
CI / ci (push) Successful in 1m22s
CI / e2e (push) Has been skipped
CI / deploy (push) Successful in 15s
The /s/:token route was registered outside the /api/* db middleware
scope, causing db to be undefined and a 500 error on share link access.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 20:01:48 +02:00
e21e1ec523 fix: allow visibility-only setup updates without name
All checks were successful
CI / ci (push) Successful in 1m24s
CI / e2e (push) Has been skipped
CI / deploy (push) Successful in 14s
updateSetupSchema required name as mandatory, causing ZodError when
ShareModal sent visibility-only updates. Made name optional in update
schema and guarded against setting undefined name in service layer.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 18:43:10 +02:00
8d7a668da4 fix: resolve lint errors from phase 32/33/34 execution
All checks were successful
CI / ci (push) Successful in 1m23s
CI / e2e (push) Has been skipped
CI / deploy (push) Successful in 1m20s
Auto-fixed formatting issues and removed unused imports introduced
by background execution agents across currency, i18n, and sharing code.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 18:32:32 +02:00
5e731b436b feat(i18n): add German translations and key parity test
- Create all 6 German namespace JSON files (common, collection, threads, setups, onboarding, settings)
- Register German locale in i18n configuration with supportedLngs
- Add key parity test ensuring en/de have identical key structures
- All 19 locale parity tests pass, all 15 formatter tests pass

Phase 34, Plan 05
2026-04-13 18:23:45 +02:00
46715cc793 feat(i18n): add language picker to settings and sync i18n with persisted preference
- Add language picker (English/Deutsch) to settings page using pill-toggle pattern
- Import useLanguage hook and i18n instance in settings
- Language change persists via updateSetting and calls i18n.changeLanguage
- Add useEffect in RootLayout to sync i18n language with DB setting on load
- Language labels use native names (English, Deutsch) for identification

Phase 34, Plan 04
2026-04-13 18:21:30 +02:00
f759dd0fde feat(i18n): locale-aware formatters and useLanguage hook
- Create useLanguage() hook following useCurrency/useWeightUnit pattern
- Update formatPrice() to use Intl.NumberFormat for locale-aware currency display
- Update formatWeight() to use Intl.NumberFormat for locale-aware number formatting
- Update formatDualPrice() to pass locale through
- Update useFormatters() to pass locale to all formatters
- Add formatter tests for en/de locales (15 tests passing)

Phase 34, Plan 03
2026-04-13 18:20:23 +02:00
672b17fd13 feat(i18n): extract strings from navigation, dialogs, onboarding, settings, and login
- Add useTranslation() to TopNav, BottomTabBar, FabMenu, UserMenu
- Internationalize ConfirmDialog, AuthPromptModal, ExternalLinkDialog
- Extract all onboarding flow strings (Welcome, HobbyPicker, ItemBrowser, Review, Done)
- Internationalize settings page (weight unit, currency, API keys, import/export)
- Internationalize login page and root error boundary
- All dialogs in __root.tsx use t() for UI chrome

Phase 34, Plan 02 (core navigation and global UI)
2026-04-13 18:19:29 +02:00
8c0fb31df2 feat(i18n): install react-i18next, create English locale files, and initialize i18n framework
- Install i18next, react-i18next, i18next-browser-languagedetector
- Create 6 namespace JSON files (common, collection, threads, setups, onboarding, settings)
- Initialize i18n with language detection (localStorage + navigator)
- Wire i18n import in main.tsx before React rendering

Phase 34, Plan 01
2026-04-13 18:13:55 +02:00
37edd0edfd feat(33-06): add market prices section to catalog detail page
- Add useGlobalItemPrices and useGlobalItemCommunityStats hooks
- Add MarketPricesSection component with user's market MSRP prominent
- Show community price stats per market with median and report count
- Collapsible "Other Markets" section (collapsed by default)
- Import useCurrency, useExchangeRates, formatPrice for market display

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 18:09:56 +02:00
02fcae12f0 feat(33-05): market/currency selector, dual price format, conversion toggle
- Add formatDualPrice() with ~prefix for approximate conversions (D-14)
- Evolve useCurrency() to return CurrencyContext with currency, market, showConversions
- Create useExchangeRates hook + convertClientPrice utility
- Redesign settings: Market & Currency selector, Show Converted Prices toggle
- Add locale-based auto-suggestion banner for first-time currency selection (D-13)
- Update useFormatters to destructure from new CurrencyContext

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 18:08:53 +02:00
3df9eece83 feat(33-04): add community price service, API routes, and setup currency metadata
- Create community-price.service.ts with ownership validation, upsert, median aggregation
- Create community-prices route (GET stats public, POST requires auth + ownership)
- Register community-prices route with public GET access
- Add priceCurrency to both getSetupWithItems and getSetupWithItemsById
- Aggregation uses PERCENTILE_CONT(0.5) with 3-report minimum threshold

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 18:06:48 +02:00
52dce7b72b feat(33-03): add market prices API, exchange rates endpoint, currency context
- Create market-price.service.ts with getMarketPrices, upsertMarketPrice
- Create exchange-rates route (GET /api/exchange-rates, public)
- Create market-prices route (GET/POST /api/market-prices/global-items/:id/prices)
- Register new routes in server index with public GET access
- Add priceCurrency to item service getAllItems/getItemById/createItem
- Add foundPriceCents/Currency/Date to thread candidate select and create/update

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 18:05:24 +02:00
0b46eff243 feat: add shared setup viewer with token detection and read-only mode
Detect ?share=token query param on setup detail page, fetch via
/api/shared/:token, and display read-only view with "Shared setup"
banner. Hide all owner controls (add items, share, delete, classification)
in shared view. Show "Link not available" error for invalid tokens.

Plan: 32-04 (Setup Sharing System - Shared Setup Viewer)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 18:04:41 +02:00
7003e998f9 feat: add share modal with visibility picker and link management
Create ShareModal component with three-tier visibility picker
(private/link/public), share link creation with configurable expiration,
clipboard copy, and link revocation. Wire into setup detail page
replacing the static visibility badge with an interactive share button.

Plan: 32-03 (Setup Sharing System - Share Modal UI)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 18:02:41 +02:00
e10f0eda3d feat(33-02): generate migration for market_prices, community_prices tables
- CREATE TABLE market_prices with unique(global_item_id, market, currency)
- CREATE TABLE community_prices with unique(global_item_id, user_id, source_type)
- ALTER TABLE items ADD COLUMN price_currency
- ALTER TABLE thread_candidates ADD COLUMN found_price_cents, found_price_currency, found_price_date
- Note: db:push requires running PostgreSQL — apply on deployment

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 18:02:40 +02:00