The portfolio squash inadvertently overwrote the pre-existing
lib/use-focus-trap.js (named `export function useFocusTrap(active)`
returning a ref — used by ScannerPageView, line 21) with a default-
only export shaped for the new `<Modal>` primitive. Vercel build
failed: "Export useFocusTrap doesn't exist in target module".
Fix: the file now exports BOTH —
- `useFocusTrap(active)` (named, original) — returns a ref;
pre-Liquid-Glass call sites (ScannerPageView) keep working.
- `useFocusTrapContainer({ active, containerRef, ... })` (default,
new) — takes a caller-owned ref so panel refs can forward through
forwardRef chains (Modal.js consumes this shape).
Both hooks are commented to document which to use when. Modal.js
imports default already, so no change needed there.
Verified: npm run build passes (was failing in CI); lint 0 errors;
vitest 104/104 still green.
Co-authored-by: Cursor <cursoragent@cursor.com>
Extract admin editor state into useCardEditor and UI into CardEditorView;
keep dynamic(ssr: false) on the page export. Page is ~40 lines.
Co-authored-by: Cursor <cursoragent@cursor.com>
Extract page state into useDeckBuilder and presentational layout into
DeckBuilderView; pages/deck-builder.js is a thin auth-gated composer.
Co-authored-by: Cursor <cursoragent@cursor.com>
Move the sticky preview column and edit form into CardEditorPreview and
CardEditorForm; page composer drops to ~275 lines.
Co-authored-by: Cursor <cursoragent@cursor.com>
Split the main deck panel list and right-hand card browser sidebar into
DeckBuilderDeckList and DeckBuilderCardBrowser; page is ~408 lines.
Co-authored-by: Cursor <cursoragent@cursor.com>
Move the find-card search input and results grid into
CardEditorSearchPanel to start splitting the admin card editor page.
Co-authored-by: Cursor <cursoragent@cursor.com>
Move Commander basic-land checks and deck aggregate metrics into
lib/deck-builder-stats.js with unit tests; render the summary row via
DeckBuilderStatsBar to shrink the page god-component.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Extract useCollectionsPage hook and CollectionsPageView (Brief 3).
Moves list index logic into a hook and view; CollectionsThumbnail is a shared presentational component.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Default hook params for prerender safety
Co-authored-by: Cursor <cursoragent@cursor.com>
---------
Co-authored-by: Cursor <cursoragent@cursor.com>
Moves create-list modal UI to components/CollectionsCreateModal.js and tag input helpers to lib/collection-tag-input.js (shared with edit modal).
Co-authored-by: Cursor <cursoragent@cursor.com>
Moves card-browser state and handlers to lib/use-cards-page.js and markup to components/CardsPageView.js, leaving a thin ProtectedRoute composer in pages/cards.js.
Co-authored-by: Cursor <cursoragent@cursor.com>
Moves the anonymous /cards landing UI into components/PublicCardsView.js so the page file can focus on AuthenticatedCards.
Co-authored-by: Cursor <cursoragent@cursor.com>
Moves ~530 lines of 3D card rendering into components/Card3D.js so the cards page god-component split can continue incrementally.
Co-authored-by: Cursor <cursoragent@cursor.com>
Move scanner page markup into ScannerPageView.js. pages/scanner.js is now
a thin composer of session state, useScannerQueue, and the view.
Mark pages/scanner.js resolved in ship-readiness god-component-split table.
Co-authored-by: Cursor <cursoragent@cursor.com>
Move scanner session persistence, queue merge helpers, and destination
routing fetch calls into lib/scanner-session.js and lib/scanner-route-api.js.
Load collections/decks on mount (were defined but never invoked).
Remove unused mana-symbol imports and dead select-all helpers.
Co-authored-by: Cursor <cursoragent@cursor.com>
Move camera feed markup, overlays, and disambiguation dialog wiring into
CameraScannerView.js. CameraScanner is now a ~45-line hook composer.
Update ship-readiness god-component-split CameraScanner slice as resolved.
Co-authored-by: Cursor <cursoragent@cursor.com>
Move verify/disambiguation/review flow into lib/use-scanner-identification.js.
CameraScanner is now a thin composition of two hooks plus view markup.
Co-authored-by: Cursor <cursoragent@cursor.com>
Move Layer-1/Layer-2 identify flow, outcome resolution, disambiguation
refine helpers, and scan-for-review API calls into lib/scanner-card-identify.js.
Remove unused manaSymbolSettings state from CameraScanner.
Co-authored-by: Cursor <cursoragent@cursor.com>
Move OpenCV shape detection, coordinate conversion, overlap checks,
and tracked-card merge logic from CameraScanner into lib/scanner-card-detection.js
with unit tests for the pure helpers.
Co-authored-by: Cursor <cursoragent@cursor.com>
Extract shared 401/403 gate into permission-middleware and sweep the
four inline admin checks (import MTG/Pokemon, sync-catalog, card-submissions).
Co-authored-by: Cursor <cursoragent@cursor.com>
Brief 1 of god-component-split: move ScanDisambiguationDialog and
scan-capture-upload lib out of CameraScanner (~90 lines) without
behavior changes.
Co-authored-by: Cursor <cursoragent@cursor.com>
Close stale convoy frontmatter for merged scanner, lint, and hygiene work;
record P1 #11.5 and queued follow-ups as RESOLVED with PR references.
Co-authored-by: Cursor <cursoragent@cursor.com>
* ci: run migrations against Postgres service container in CI
Add a migrate job that applies node-pg-migrate against an ephemeral
Postgres 16 service container so broken migrations fail at PR time.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(migrations): retimestamp scan tables after initial-schema.
Fresh CI/postgres runs failed because 1748365200000 sorted before
initial-schema. Renamed to 1779853647566 with IF NOT EXISTS guards intact.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(migrations): ensure is_system_collection exists before description backfill.
Fresh CI Postgres runs initial-schema without this column (added historically
via scripts); ADD COLUMN IF NOT EXISTS makes the data migration safe on new
and existing envs.
Co-authored-by: Cursor <cursoragent@cursor.com>
---------
Co-authored-by: Cursor <cursoragent@cursor.com>
Lazy-init theme from localStorage, hoist checkAuth with useCallback,
named config exports for PostCSS/Tailwind, and remove the non-blocking
|| true wrapper from ci.yml (requires #61 + #62 merged first).
Co-authored-by: Cursor <cursoragent@cursor.com>
Resolve react-hooks, no-unescaped-entities, and no-img-element findings
under pages/ with lint-only changes so npx eslint pages/ exits clean.
Co-authored-by: Cursor <cursoragent@cursor.com>
Existing is_system_collection rows may carry stale copy; align them with VOCAB.SYSTEM_COLLECTION_SEED_DESCRIPTION so UI matches new signups.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Remove dead Lorcana import route and CLI script
The admin card-import UI never wired Lorcana; catalog sync uses
pages/api/admin instead. Drop the unused API route, CLI helper, and
stale docs references to import-lorcana.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Remove orphaned import-lorcana-simple CLI script.
It POSTed to the deleted /api/cards/import-lorcana endpoint; no remaining callers.
Co-authored-by: Cursor <cursoragent@cursor.com>
---------
Co-authored-by: Cursor <cursoragent@cursor.com>
Commit home.png generated in mcr.microsoft.com/playwright:v1.60.0-noble
against https://tcg-vault.vercel.app so Screenshot diff CI matches production.
Co-authored-by: Cursor <cursoragent@cursor.com>
Migrate setup-neon-db.js and reset-db.js to @vercel/postgres tagged templates so the runtime uses a single SQL client; historical add-*/fix-*/seed-* scripts remain unchanged per no-go-zone.
Co-authored-by: Cursor <cursoragent@cursor.com>
Drop alice/bob password prefill from the login page, add a regression
test, and improve bulk-toolbar and disambiguation accessible names.
Co-authored-by: Cursor <cursoragent@cursor.com>
Correct dashboard title (My Collection overview, not Lists), sweep
remaining marketing/auth copy, update system-list seed description,
add vocabulary unit tests, and close the convoy record.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Align UI copy with My Collection vs Lists vocabulary.
Replace stale ownership/list labels across pages and components, add
lib/collection-vocabulary.js as the single copy source, document the
taxonomy in AGENTS.md, and gate retired strings in CI.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Fix remaining list/collection copy gaps from review.
Sweep community, settings, share modal, scanner create-list modal,
and invite flows for vocabulary consistency before merge.
Co-authored-by: Cursor <cursoragent@cursor.com>
---------
Co-authored-by: Cursor <cursoragent@cursor.com>
Share card-number normalization across reconcile and identify paths, retry set/name matches when OCR uses leading-zero collector numbers, and extend in-flight locks to all scanner destination actions with disabled Mark Owned feedback.
Co-authored-by: Cursor <cursoragent@cursor.com>
Replace pokemontcg.io API discovery and import with raw JSON from PokemonTCG/pokemon-tcg-data; format collector numbers as number/printedTotal and drop the API key dependency for catalog sync.
Co-authored-by: Cursor <cursoragent@cursor.com>
When a set lands via runCatalogSync, match pending card_submissions by set/name/number to catalog rows and approve them with promoted_card_id instead of leaving them in the admin queue.
Co-authored-by: Cursor <cursoragent@cursor.com>
Expose POST /api/admin/sync-catalog for authenticated admins (import rate limit, 300s timeout) and wire a Run catalog sync control on /admin/card-import.
Co-authored-by: Cursor <cursoragent@cursor.com>
The cron was importing oldest MTG sets first and never reaching recent Pokémon releases like Perfect Order; merge MTG and Pokémon by release date descending instead.
Co-authored-by: Cursor <cursoragent@cursor.com>