- Create admin-tags.ts with GET list, GET single, POST, PUT (cycle guard → 400), DELETE
- Register /tags route in admin.ts
- Add 13-test integration suite covering CRUD, cycle detection, orphan behavior
- Add parentId self-ref FK to tags table (ON DELETE SET NULL)
- Generate Drizzle migration 0010_yielding_random.sql
- Extend tag.service.ts with getAdminTags, getTagWithCounts, createTag, updateTag, deleteTag, isDescendant
- Add service tests (14 tests, all pass)
- Add listGlobalItemsForAdmin: paginated with batched tag/ownerCount queries
- Add updateGlobalItemById: partial update in transaction, syncs tags
- Add deleteGlobalItem: nullifies FK refs, removes tag associations before delete
- Create src/server/routes/admin-items.ts with GET/GET:id/PUT/DELETE endpoints
- Mount adminItemRoutes at /items in admin.ts (protected by requireAuth+requireAdmin)
- Extend global-item.service.test.ts with 13 new tests (all passing)
Closes ADMN-02, ADMN-03, ADMN-04 (server side)
- 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