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>
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>
- 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
- 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
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>
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>
- 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>
- 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>
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>
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>
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>
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>
- 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
- 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
- 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
- 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>
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>
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>
Replace isPublic boolean with visibility enum (private/link/public) across
the full stack. Add shares table to schema for future share link support.
Update all services, routes, schemas, hooks, components, and tests.
Plan: 32-01 (Setup Sharing System - Schema Migration)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Moved the crop button from below the image into the ImageUpload
component as an absolute-positioned overlay next to the trash icon,
matching the visual pattern.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The crop icon button was in the view-mode branch but conditioned on
isEditing, making it unreachable. Moved it below ImageUpload in the
edit-mode branch where it belongs.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
ImageUpload was discarding the dominantColor returned by the upload
API. Now it passes the color through onChange and the item detail
page saves it to the item record immediately after upload.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The OIDC session token retains the old email after a Logto email
change. Now the server returns the new email in the response and
the frontend optimistically updates the auth cache.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
UserMenu now fetches the user's profile and displays their avatar
image in the nav button instead of the default circle-user icon.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Avatar images were rendered via /uploads/ which doesn't exist since
the S3 migration. Now the server enriches profile responses with
avatarImageUrl (presigned S3 URL) and the frontend uses it directly.
Also fixed the public profile page at /users/:id.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Selected hobby cards now use dark gray fill with inverted white
text/icon for clear visual distinction. Also fixes biome formatting
across all changed files.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Changed "Adjust framing" text to a crop icon button visible only in
edit mode. Replaced the X icon on the image remove button with a
trash icon for clearer semantics.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Added onCropChange and dominantColor props to ImageUpload in the item
detail page, so the crop editor opens automatically after uploading
a new image.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replaced the one-shot initialized flag with a dirty flag that allows
the useEffect to re-sync local state from server data after a
successful save. Previously, once initialized was set to true, the
effect never ran again so avatar changes were lost on refetch.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The flat list was missing dominantColor/crop props, and the grouped
view was also missing imageUrl entirely — causing images not to render
on collection cards.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
getAllItems and getItemById were not selecting dominantColor, cropZoom,
cropX, cropY from the database. GearImage was ignoring the dominantColor
prop. Now the fields flow end-to-end from DB to UI background fill.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
useLogout() returns { logout } but was assigned directly, causing
"r is not a function" when clicking sign out.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Swap old 4-step modal wizard with new full-screen, hobby-personalized
onboarding experience. Delete OnboardingWizard.tsx.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Implements 5-step onboarding: Welcome, Hobby Picker, Item Browser,
Review, and Done. Includes hobby card selection, popular item grid
with check/uncheck, review list with remove, CSS step transitions,
and responsive grid layout.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>