- Import eq from drizzle-orm and users from schema
- Export requireAdmin(c, next) that returns 401 if userId not in context, 403 if user.isAdmin is falsy
- Add useState(false) loaded state to ItemCard, CandidateCard, GlobalItemCard
- Show bg-gray-100 animate-pulse skeleton overlay while image loads
- Fade in image via transition-opacity duration-200 on onLoad callback
- No-image placeholders (icon on bg-gray-50) unchanged
- Add import { useState } from react to all three files with correct Biome import order
- Add optional onLoad prop to GearImageProps interface
- Destructure onLoad in function signature
- Forward loading="lazy" and onLoad to all three img render paths (cover, hasCrop, default)
- 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
- Add imageUrl, dominantColor, cropZoom, cropX, cropY, priceCurrency to interface
- Server already returns these fields via withImageUrls(); type was just incomplete
- 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
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>
- 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>
- 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>
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>
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>