Frontend
Frontend maintenance — implementation spec for plan phase 8
Feeds plan phase 8. The apps/web refactor is done (features/ structure, TanStack Query, RHF+zod, middleware auth, HttpOnly-cookie BFF, socket realtime, next/image, tests + CI). What's left is maintenance, found by the 2026-07-02 review — ordered below, one PR per step.
1. Quick wins (no behavior change)
lib/api/server.ts:24-29: the errormessageparsed from the response body is never used — include it in the thrownErrorso server components stop losing backend error details.- Dead
historyRefin both review screens (features/admin/products/components/product-review-content.tsx:42,113,128,features/admin/scraper/components/scraped-product-review-content.tsx:24,59,78): collected but never read — the "Back" button doesn't restore the previous status. Either implement undo or delete the ref. - Fix the 8 eslint warnings:
react-hooks/set-state-in-effect×6 (confirm-dialog.tsx:58,edit-product-form.tsx:92,admin-scraper-content.tsx:44,use-picker-search.ts:19-20,use-chat.tsx:76),no-img-element(hero-section.tsx:72), unused eslint-disable (image-upload.tsx:39). - Root
README.mdstack table: replace "GraphQL (code-first)" / "Apollo Client" with REST + TanStack Query (stale since the GraphQL removal).
2. Formatter
No Prettier → recently merged files use double-quotes+semicolons (product-review-content.tsx, use-product-filters.ts) while the rest is single-quote/no-semi. Add Prettier (or Biome) + one format-only commit + CI check. Do this before step 3 so the refactor lands formatted.
3. Review-queue refactor (the one structural item)
product-review-content.tsx (453 lines) and scraped-product-review-content.tsx are ~80% the same pattern, both regressed to effect-fetching:
- Effect-fetch + serial load:
loadQueue()inuseEffectwith manualloadingflags loops up to 20 pages × 50 items sequentially before rendering (product-review-content.tsx:76-101; same shape atscraped-product-review-content.tsx:49). Replace with a paginated/infiniteuseQueryfetching ahead as the reviewer advances. - No cache invalidation: status changes call
adminProductsApi.updateProductdirectly instead offeatures/admin/products/mutations.ts—qk.products.*never invalidates, admin lists go stale after a review session. - Auto-validate survives unmount (
product-review-content.tsx:149-193): the loop keeps mutating statuses and callingsetStateafter navigation; only the Stop button setsstopAutoRef. Add anAbortController/unmount guard. - Extract a shared
useReviewQueuehook (queue, index, progress, undo history, auto-advance) consumed by both screens; splitproduct-review-content.tsxinto hook + header + duplicates panel + action bar, each ≤250 lines.
4. Dedup
features/admin/deals/components/deal-status-pill.tsxre-declares the deal status map thatfeatures/deals/constants.tsowns — and they've diverged (constants.ts:25uses rawyellow-500, the pill useswarningtokens). Fold intoDealStatusBadge/constants, keyed on the Prisma enum type instead ofRecord<string, …>.- Two create-product dialogs:
features/deals/components/product-picker/create-product-dialog.tsx(218 lines, multi-useState— violates the RHF rule) vsfeatures/admin/products/components/create-product-dialog.tsx(136, RHF). Converge on one RHF component.
5. Consistency sweep
-
Inline query keys
['scraper', 'products']in ~10 spots (scraped-product-row.tsx,use-bulk-actions.ts,scraper-results-toolbar.tsx) →qk.scraper.products; add the missingqk.scraper.bulkJobskey. -
Raw Tailwind palette (
text-amber-600,bg-red-50,text-green-500…) in ~10 admin files / ~48 occurrences → tokens (text-warning,bg-success/10…). Extend the CI color gate to raw palette classes so they can't return:! grep -rnE "(text|bg|border)-(amber|green|red|blue|yellow)-[0-9]" apps/web/app apps/web/features apps/web/components --include="*.tsx" -
formatPricebypassed withNumber(price).toFixed(0)in 6 files:app/(guest)/deal/[id]/page.tsx:21,features/admin/deals/components/{deal-detail-view.tsx:90, queue-row.tsx:17, admin-deals-content.tsx:124},features/admin/products/components/admin-product-detail.tsx:189,features/profile/components/profile-deal-row.tsx:51.
6. Build hermeticity + Next upkeep
— fixed: the public cached lookups (next buildfails without a live backendgetCategories,getRootCategories,getBrands,getSpecTypes,getRecentDealsinlib/api/server.ts) go throughserverFetchWithFallback, which logs and falls back to[]instead of throwing./and/productsprerender with empty sections when the API is unreachable at build time; ISR (revalidate60s/300s) fills in real data once the API is up.build-backend/build-frontendin CI now depend onlint, nottest, so they run independently of the (currently broken) test job.- Next 16 deprecation: rename
middleware.ts→proxy.tsfile convention with the next Next bump.
7. Test expansion (ongoing)
- Add MSW; first targets: the new
useReviewQueuehook anduse-bulk-actions(highest churn — both review screens regressed unnoticed), then queries/mutations invalidation behavior. - One admin e2e flow (product review), and keep e2e selectors scoped (a global
getByPlaceholder(/search/i)broke when header search shipped — see plan phase 0).