2025-07-24 18:04:06 -04:00
import { useState , useEffect } from 'react' ;
2025-07-24 16:16:57 -04:00
import { useRouter } from 'next/router' ;
2025-07-24 18:04:06 -04:00
import dynamic from 'next/dynamic' ;
2025-07-23 22:26:54 -04:00
import Layout from '../../components/Layout' ;
2025-07-24 17:31:29 -04:00
import AdminProtected from '../../components/AdminProtected' ;
feat(design-system): sweep authenticated body-content panels to glass (#97)
PR #95/#96 shipped the Liquid Glass foundation (tokens, primitives, gates)
plus Layout shell, modals, landing, auth pages, and form CTAs — but body-
content panels on authenticated pages (admin Card Editor, admin Card
Import, admin Submissions, dashboard, my-cards, settings, scanner panels,
card detail price cards, popovers) were still rendering as flat
var(--bg-secondary) cards. Result: the admin Tools screen and several
core pages looked unchanged after the redesign.
This sweep adds a `.glass-panel` / `.glass-panel-strong` utility
(<GlassSurface tint=mid/high rim=subtle elevation=ambient/pronounced
blur=mid/high> in class form) and applies it across 18 surfaces:
* Admin Card Editor view, search panel, form (5 sections), preview
* Admin Card Import navigation + 3 body cards + sync panel
* Admin Card Submissions list items
* Dashboard stat cards + empty-state + grid items (5 surfaces)
* My-cards empty-state CTA card
* Settings panels (3)
* Scanner page settings + grid + queue + bulk toolbar + dialog
* Scanner destination picker + camera status banner + disambiguation
* Card detail price cards (Current / TCGPlayer / CardKingdom)
* Permission indicator tooltips
* Collections page header card
* Card detail view price cards
Also migrates the lingering admin Card Editor "Card Editor / Card Import"
nav buttons and the "Save Changes" / "Import Cards" / "Run catalog sync"
CTAs to the <Button> primitive (consistent loading + disabled states).
Page header bands (full-bleed strips with border-bottom on dashboard,
my-cards, cards, collections, community/collections, collection/[id])
are intentionally left solid — they're not card-shaped surfaces and
stacking glass-on-glass directly below the already-glass topbar would
muddy the hierarchy.
Tests: lint clean, vitest 104/104, build green. The visual diff
baseline will need refresh because the homepage spec is unaffected
(it targets the unauthenticated landing page) but the dashboard/
admin/scanner surfaces will diff if/when we add baselines for them.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-03 22:28:52 -04:00
import { Button } from '../../components/ui' ;
2025-07-23 22:26:54 -04:00
2025-07-24 18:04:06 -04:00
const CardImport = ( ) => {
2025-07-24 16:16:57 -04:00
const router = useRouter ( ) ;
2025-07-23 22:26:54 -04:00
const [ importType , setImportType ] = useState ( 'mtg' ) ;
const [ setCode , setSetCode ] = useState ( '' ) ;
const [ isImporting , setIsImporting ] = useState ( false ) ;
const [ importResult , setImportResult ] = useState ( null ) ;
2026-05-28 10:18:01 -04:00
const [ isSyncing , setIsSyncing ] = useState ( false ) ;
const [ syncResult , setSyncResult ] = useState ( null ) ;
2026-06-14 09:22:19 -04:00
const [ syncMode , setSyncMode ] = useState ( 'unified' ) ;
feat(catalog): Scryfall bulk data import + Tagger community tags
Add full Scryfall bulk data pipeline:
- Migration: 13 new columns on `cards` (oracle_id, illustration_id,
color_identity, keywords, legalities, flavor_text, artist, released_at,
layout, edhrec_rank, reserved, reprint, finishes) with GIN indexes
for JSONB search.
- Migration: `tags` + `card_tags` tables for Tagger community data.
- Script: `bulk-import-scryfall.js` — downloads Oracle Cards bulk file
(168 MB) and upserts all 36k+ MTG cards with rich metadata.
- Script: `import-scryfall-tags.js` — imports oracle tags (4.5k tags,
227k taggings) and art tags (11k tags, 458k taggings).
- Lib: `bulk-sync.js` — runtime bulk sync callable from the admin API.
- Admin UI: mode toggle (incremental vs bulk) on catalog sync panel.
Enables Commander deck validation (color_identity), format legality
checks, keyword search, EDHREC popularity ranking, and functional
card tagging ("removal", "ramp", "draw") for deck building assistance.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-14 08:51:39 -04:00
2026-05-28 10:18:01 -04:00
const handleCatalogSync = async ( ) => {
setIsSyncing ( true ) ;
setSyncResult ( null ) ;
try {
const response = await fetch ( '/api/admin/sync-catalog' , {
method : 'POST' ,
headers : {
'Content-Type' : 'application/json' ,
Authorization : ` Bearer ${ localStorage . getItem ( 'auth_token' ) } ` ,
} ,
feat(catalog): Scryfall bulk data import + Tagger community tags
Add full Scryfall bulk data pipeline:
- Migration: 13 new columns on `cards` (oracle_id, illustration_id,
color_identity, keywords, legalities, flavor_text, artist, released_at,
layout, edhrec_rank, reserved, reprint, finishes) with GIN indexes
for JSONB search.
- Migration: `tags` + `card_tags` tables for Tagger community data.
- Script: `bulk-import-scryfall.js` — downloads Oracle Cards bulk file
(168 MB) and upserts all 36k+ MTG cards with rich metadata.
- Script: `import-scryfall-tags.js` — imports oracle tags (4.5k tags,
227k taggings) and art tags (11k tags, 458k taggings).
- Lib: `bulk-sync.js` — runtime bulk sync callable from the admin API.
- Admin UI: mode toggle (incremental vs bulk) on catalog sync panel.
Enables Commander deck validation (color_identity), format legality
checks, keyword search, EDHREC popularity ranking, and functional
card tagging ("removal", "ramp", "draw") for deck building assistance.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-14 08:51:39 -04:00
body : JSON . stringify ( { mode : syncMode } ) ,
2026-05-28 10:18:01 -04:00
} ) ;
const result = await response . json ( ) ;
if ( response . ok ) {
setSyncResult ( { success : true , ... result } ) ;
} else {
setSyncResult ( {
success : false ,
message : result . error || 'Catalog sync failed' ,
} ) ;
}
} catch ( error ) {
setSyncResult ( {
success : false ,
message : ` Network error: ${ error . message } ` ,
} ) ;
} finally {
setIsSyncing ( false ) ;
}
} ;
2025-07-23 22:26:54 -04:00
const handleImport = async ( ) => {
if ( ! setCode . trim ( ) ) {
alert ( 'Please enter a set code' ) ;
return ;
}
setIsImporting ( true ) ;
setImportResult ( null ) ;
try {
const endpoint = importType === 'mtg'
? '/api/cards/import-mtg'
: '/api/cards/import-pokemon' ;
const response = await fetch ( endpoint , {
method : 'POST' ,
headers : {
'Content-Type' : 'application/json' ,
2026-05-24 23:59:59 -04:00
'Authorization' : ` Bearer ${ localStorage . getItem ( 'auth_token' ) } ` ,
2025-07-23 22:26:54 -04:00
} ,
body : JSON . stringify ( { setCode : setCode . trim ( ) } ) ,
} ) ;
const result = await response . json ( ) ;
if ( response . ok ) {
setImportResult ( {
success : true ,
message : result . message ,
imported : result . imported ,
skipped : result . skipped ,
total : result . total
} ) ;
} else {
setImportResult ( {
success : false ,
message : result . error || 'Import failed'
} ) ;
}
} catch ( error ) {
setImportResult ( {
success : false ,
message : 'Network error: ' + error . message
} ) ;
} finally {
setIsImporting ( false ) ;
}
} ;
const popularSets = {
mtg : [
{ code : 'neo' , name : 'Kamigawa: Neon Dynasty' } ,
{ code : 'vow' , name : 'Innistrad: Crimson Vow' } ,
{ code : 'mid' , name : 'Innistrad: Midnight Hunt' } ,
{ code : 'afr' , name : 'Adventures in the Forgotten Realms' } ,
{ code : 'stx' , name : 'Strixhaven: School of Mages' } ,
{ code : 'khm' , name : 'Kaldheim' } ,
{ code : 'znr' , name : 'Zendikar Rising' } ,
{ code : 'iko' , name : 'Ikoria: Lair of Behemoths' } ,
{ code : 'thb' , name : 'Theros Beyond Death' } ,
{ code : 'eld' , name : 'Throne of Eldraine' }
] ,
pokemon : [
{ code : 'swsh1' , name : 'Sword & Shield' } ,
{ code : 'swsh2' , name : 'Rebel Clash' } ,
{ code : 'swsh3' , name : 'Darkness Ablaze' } ,
{ code : 'swsh4' , name : 'Vivid Voltage' } ,
{ code : 'swsh5' , name : 'Battle Styles' } ,
{ code : 'swsh6' , name : 'Chilling Reign' } ,
{ code : 'swsh7' , name : 'Evolving Skies' } ,
{ code : 'swsh8' , name : 'Fusion Strike' } ,
{ code : 'swsh9' , name : 'Brilliant Stars' } ,
{ code : 'swsh10' , name : 'Astral Radiance' }
]
} ;
return (
2025-07-24 17:31:29 -04:00
< AdminProtected >
2025-07-24 18:04:06 -04:00
{ ( user ) => (
< Layout user = { user } >
2025-07-24 16:16:57 -04:00
< div className = "container mx-auto px-6 py-8" >
{ /* Admin Navigation */ }
feat(design-system): sweep authenticated body-content panels to glass (#97)
PR #95/#96 shipped the Liquid Glass foundation (tokens, primitives, gates)
plus Layout shell, modals, landing, auth pages, and form CTAs — but body-
content panels on authenticated pages (admin Card Editor, admin Card
Import, admin Submissions, dashboard, my-cards, settings, scanner panels,
card detail price cards, popovers) were still rendering as flat
var(--bg-secondary) cards. Result: the admin Tools screen and several
core pages looked unchanged after the redesign.
This sweep adds a `.glass-panel` / `.glass-panel-strong` utility
(<GlassSurface tint=mid/high rim=subtle elevation=ambient/pronounced
blur=mid/high> in class form) and applies it across 18 surfaces:
* Admin Card Editor view, search panel, form (5 sections), preview
* Admin Card Import navigation + 3 body cards + sync panel
* Admin Card Submissions list items
* Dashboard stat cards + empty-state + grid items (5 surfaces)
* My-cards empty-state CTA card
* Settings panels (3)
* Scanner page settings + grid + queue + bulk toolbar + dialog
* Scanner destination picker + camera status banner + disambiguation
* Card detail price cards (Current / TCGPlayer / CardKingdom)
* Permission indicator tooltips
* Collections page header card
* Card detail view price cards
Also migrates the lingering admin Card Editor "Card Editor / Card Import"
nav buttons and the "Save Changes" / "Import Cards" / "Run catalog sync"
CTAs to the <Button> primitive (consistent loading + disabled states).
Page header bands (full-bleed strips with border-bottom on dashboard,
my-cards, cards, collections, community/collections, collection/[id])
are intentionally left solid — they're not card-shaped surfaces and
stacking glass-on-glass directly below the already-glass topbar would
muddy the hierarchy.
Tests: lint clean, vitest 104/104, build green. The visual diff
baseline will need refresh because the homepage spec is unaffected
(it targets the unauthenticated landing page) but the dashboard/
admin/scanner surfaces will diff if/when we add baselines for them.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-03 22:28:52 -04:00
< div className = "glass-panel mb-8 p-4 rounded-xl" >
2025-07-24 16:16:57 -04:00
< div className = "flex items-center justify-between" >
< h1 className = "text-2xl font-bold" style = { { color : 'var(--text-primary)' } } >
Admin Tools
< / h 1 >
< div className = "flex gap-4" >
feat(design-system): sweep authenticated body-content panels to glass (#97)
PR #95/#96 shipped the Liquid Glass foundation (tokens, primitives, gates)
plus Layout shell, modals, landing, auth pages, and form CTAs — but body-
content panels on authenticated pages (admin Card Editor, admin Card
Import, admin Submissions, dashboard, my-cards, settings, scanner panels,
card detail price cards, popovers) were still rendering as flat
var(--bg-secondary) cards. Result: the admin Tools screen and several
core pages looked unchanged after the redesign.
This sweep adds a `.glass-panel` / `.glass-panel-strong` utility
(<GlassSurface tint=mid/high rim=subtle elevation=ambient/pronounced
blur=mid/high> in class form) and applies it across 18 surfaces:
* Admin Card Editor view, search panel, form (5 sections), preview
* Admin Card Import navigation + 3 body cards + sync panel
* Admin Card Submissions list items
* Dashboard stat cards + empty-state + grid items (5 surfaces)
* My-cards empty-state CTA card
* Settings panels (3)
* Scanner page settings + grid + queue + bulk toolbar + dialog
* Scanner destination picker + camera status banner + disambiguation
* Card detail price cards (Current / TCGPlayer / CardKingdom)
* Permission indicator tooltips
* Collections page header card
* Card detail view price cards
Also migrates the lingering admin Card Editor "Card Editor / Card Import"
nav buttons and the "Save Changes" / "Import Cards" / "Run catalog sync"
CTAs to the <Button> primitive (consistent loading + disabled states).
Page header bands (full-bleed strips with border-bottom on dashboard,
my-cards, cards, collections, community/collections, collection/[id])
are intentionally left solid — they're not card-shaped surfaces and
stacking glass-on-glass directly below the already-glass topbar would
muddy the hierarchy.
Tests: lint clean, vitest 104/104, build green. The visual diff
baseline will need refresh because the homepage spec is unaffected
(it targets the unauthenticated landing page) but the dashboard/
admin/scanner surfaces will diff if/when we add baselines for them.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-03 22:28:52 -04:00
< Button
variant = "secondary"
2025-07-24 16:16:57 -04:00
onClick = { ( ) => router . push ( '/admin/card-editor' ) }
feat(design-system): sweep authenticated body-content panels to glass (#97)
PR #95/#96 shipped the Liquid Glass foundation (tokens, primitives, gates)
plus Layout shell, modals, landing, auth pages, and form CTAs — but body-
content panels on authenticated pages (admin Card Editor, admin Card
Import, admin Submissions, dashboard, my-cards, settings, scanner panels,
card detail price cards, popovers) were still rendering as flat
var(--bg-secondary) cards. Result: the admin Tools screen and several
core pages looked unchanged after the redesign.
This sweep adds a `.glass-panel` / `.glass-panel-strong` utility
(<GlassSurface tint=mid/high rim=subtle elevation=ambient/pronounced
blur=mid/high> in class form) and applies it across 18 surfaces:
* Admin Card Editor view, search panel, form (5 sections), preview
* Admin Card Import navigation + 3 body cards + sync panel
* Admin Card Submissions list items
* Dashboard stat cards + empty-state + grid items (5 surfaces)
* My-cards empty-state CTA card
* Settings panels (3)
* Scanner page settings + grid + queue + bulk toolbar + dialog
* Scanner destination picker + camera status banner + disambiguation
* Card detail price cards (Current / TCGPlayer / CardKingdom)
* Permission indicator tooltips
* Collections page header card
* Card detail view price cards
Also migrates the lingering admin Card Editor "Card Editor / Card Import"
nav buttons and the "Save Changes" / "Import Cards" / "Run catalog sync"
CTAs to the <Button> primitive (consistent loading + disabled states).
Page header bands (full-bleed strips with border-bottom on dashboard,
my-cards, cards, collections, community/collections, collection/[id])
are intentionally left solid — they're not card-shaped surfaces and
stacking glass-on-glass directly below the already-glass topbar would
muddy the hierarchy.
Tests: lint clean, vitest 104/104, build green. The visual diff
baseline will need refresh because the homepage spec is unaffected
(it targets the unauthenticated landing page) but the dashboard/
admin/scanner surfaces will diff if/when we add baselines for them.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-03 22:28:52 -04:00
leadingIcon = { < span aria - hidden = "true" > 🖊 ️ < / s p a n > }
2025-07-24 16:16:57 -04:00
>
feat(design-system): sweep authenticated body-content panels to glass (#97)
PR #95/#96 shipped the Liquid Glass foundation (tokens, primitives, gates)
plus Layout shell, modals, landing, auth pages, and form CTAs — but body-
content panels on authenticated pages (admin Card Editor, admin Card
Import, admin Submissions, dashboard, my-cards, settings, scanner panels,
card detail price cards, popovers) were still rendering as flat
var(--bg-secondary) cards. Result: the admin Tools screen and several
core pages looked unchanged after the redesign.
This sweep adds a `.glass-panel` / `.glass-panel-strong` utility
(<GlassSurface tint=mid/high rim=subtle elevation=ambient/pronounced
blur=mid/high> in class form) and applies it across 18 surfaces:
* Admin Card Editor view, search panel, form (5 sections), preview
* Admin Card Import navigation + 3 body cards + sync panel
* Admin Card Submissions list items
* Dashboard stat cards + empty-state + grid items (5 surfaces)
* My-cards empty-state CTA card
* Settings panels (3)
* Scanner page settings + grid + queue + bulk toolbar + dialog
* Scanner destination picker + camera status banner + disambiguation
* Card detail price cards (Current / TCGPlayer / CardKingdom)
* Permission indicator tooltips
* Collections page header card
* Card detail view price cards
Also migrates the lingering admin Card Editor "Card Editor / Card Import"
nav buttons and the "Save Changes" / "Import Cards" / "Run catalog sync"
CTAs to the <Button> primitive (consistent loading + disabled states).
Page header bands (full-bleed strips with border-bottom on dashboard,
my-cards, cards, collections, community/collections, collection/[id])
are intentionally left solid — they're not card-shaped surfaces and
stacking glass-on-glass directly below the already-glass topbar would
muddy the hierarchy.
Tests: lint clean, vitest 104/104, build green. The visual diff
baseline will need refresh because the homepage spec is unaffected
(it targets the unauthenticated landing page) but the dashboard/
admin/scanner surfaces will diff if/when we add baselines for them.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-03 22:28:52 -04:00
Card Editor
< / B u t t o n >
< Button
variant = "primary"
2025-07-24 16:16:57 -04:00
onClick = { ( ) => router . push ( '/admin/card-import' ) }
feat(design-system): sweep authenticated body-content panels to glass (#97)
PR #95/#96 shipped the Liquid Glass foundation (tokens, primitives, gates)
plus Layout shell, modals, landing, auth pages, and form CTAs — but body-
content panels on authenticated pages (admin Card Editor, admin Card
Import, admin Submissions, dashboard, my-cards, settings, scanner panels,
card detail price cards, popovers) were still rendering as flat
var(--bg-secondary) cards. Result: the admin Tools screen and several
core pages looked unchanged after the redesign.
This sweep adds a `.glass-panel` / `.glass-panel-strong` utility
(<GlassSurface tint=mid/high rim=subtle elevation=ambient/pronounced
blur=mid/high> in class form) and applies it across 18 surfaces:
* Admin Card Editor view, search panel, form (5 sections), preview
* Admin Card Import navigation + 3 body cards + sync panel
* Admin Card Submissions list items
* Dashboard stat cards + empty-state + grid items (5 surfaces)
* My-cards empty-state CTA card
* Settings panels (3)
* Scanner page settings + grid + queue + bulk toolbar + dialog
* Scanner destination picker + camera status banner + disambiguation
* Card detail price cards (Current / TCGPlayer / CardKingdom)
* Permission indicator tooltips
* Collections page header card
* Card detail view price cards
Also migrates the lingering admin Card Editor "Card Editor / Card Import"
nav buttons and the "Save Changes" / "Import Cards" / "Run catalog sync"
CTAs to the <Button> primitive (consistent loading + disabled states).
Page header bands (full-bleed strips with border-bottom on dashboard,
my-cards, cards, collections, community/collections, collection/[id])
are intentionally left solid — they're not card-shaped surfaces and
stacking glass-on-glass directly below the already-glass topbar would
muddy the hierarchy.
Tests: lint clean, vitest 104/104, build green. The visual diff
baseline will need refresh because the homepage spec is unaffected
(it targets the unauthenticated landing page) but the dashboard/
admin/scanner surfaces will diff if/when we add baselines for them.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-03 22:28:52 -04:00
leadingIcon = { < span aria - hidden = "true" > 📥 < / s p a n > }
2025-07-24 16:16:57 -04:00
>
feat(design-system): sweep authenticated body-content panels to glass (#97)
PR #95/#96 shipped the Liquid Glass foundation (tokens, primitives, gates)
plus Layout shell, modals, landing, auth pages, and form CTAs — but body-
content panels on authenticated pages (admin Card Editor, admin Card
Import, admin Submissions, dashboard, my-cards, settings, scanner panels,
card detail price cards, popovers) were still rendering as flat
var(--bg-secondary) cards. Result: the admin Tools screen and several
core pages looked unchanged after the redesign.
This sweep adds a `.glass-panel` / `.glass-panel-strong` utility
(<GlassSurface tint=mid/high rim=subtle elevation=ambient/pronounced
blur=mid/high> in class form) and applies it across 18 surfaces:
* Admin Card Editor view, search panel, form (5 sections), preview
* Admin Card Import navigation + 3 body cards + sync panel
* Admin Card Submissions list items
* Dashboard stat cards + empty-state + grid items (5 surfaces)
* My-cards empty-state CTA card
* Settings panels (3)
* Scanner page settings + grid + queue + bulk toolbar + dialog
* Scanner destination picker + camera status banner + disambiguation
* Card detail price cards (Current / TCGPlayer / CardKingdom)
* Permission indicator tooltips
* Collections page header card
* Card detail view price cards
Also migrates the lingering admin Card Editor "Card Editor / Card Import"
nav buttons and the "Save Changes" / "Import Cards" / "Run catalog sync"
CTAs to the <Button> primitive (consistent loading + disabled states).
Page header bands (full-bleed strips with border-bottom on dashboard,
my-cards, cards, collections, community/collections, collection/[id])
are intentionally left solid — they're not card-shaped surfaces and
stacking glass-on-glass directly below the already-glass topbar would
muddy the hierarchy.
Tests: lint clean, vitest 104/104, build green. The visual diff
baseline will need refresh because the homepage spec is unaffected
(it targets the unauthenticated landing page) but the dashboard/
admin/scanner surfaces will diff if/when we add baselines for them.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-03 22:28:52 -04:00
Card Import
< / B u t t o n >
2025-07-24 16:16:57 -04:00
< / d i v >
< / d i v >
< / d i v >
{ /* Card Import Section */ }
2025-07-23 22:26:54 -04:00
< div className = "mb-6" >
2025-07-24 16:16:57 -04:00
< h2 className = "text-3xl font-bold mb-2" style = { { color : 'var(--text-primary)' } } >
2025-07-23 22:26:54 -04:00
Card Import Manager
2025-07-24 16:16:57 -04:00
< / h 2 >
2025-07-23 22:26:54 -04:00
< p className = "text-lg" style = { { color : 'var(--text-secondary)' } } >
Import cards from external APIs into your database
< / p >
< / d i v >
2026-05-28 10:18:01 -04:00
{ /* Catalog sync */ }
feat(design-system): sweep authenticated body-content panels to glass (#97)
PR #95/#96 shipped the Liquid Glass foundation (tokens, primitives, gates)
plus Layout shell, modals, landing, auth pages, and form CTAs — but body-
content panels on authenticated pages (admin Card Editor, admin Card
Import, admin Submissions, dashboard, my-cards, settings, scanner panels,
card detail price cards, popovers) were still rendering as flat
var(--bg-secondary) cards. Result: the admin Tools screen and several
core pages looked unchanged after the redesign.
This sweep adds a `.glass-panel` / `.glass-panel-strong` utility
(<GlassSurface tint=mid/high rim=subtle elevation=ambient/pronounced
blur=mid/high> in class form) and applies it across 18 surfaces:
* Admin Card Editor view, search panel, form (5 sections), preview
* Admin Card Import navigation + 3 body cards + sync panel
* Admin Card Submissions list items
* Dashboard stat cards + empty-state + grid items (5 surfaces)
* My-cards empty-state CTA card
* Settings panels (3)
* Scanner page settings + grid + queue + bulk toolbar + dialog
* Scanner destination picker + camera status banner + disambiguation
* Card detail price cards (Current / TCGPlayer / CardKingdom)
* Permission indicator tooltips
* Collections page header card
* Card detail view price cards
Also migrates the lingering admin Card Editor "Card Editor / Card Import"
nav buttons and the "Save Changes" / "Import Cards" / "Run catalog sync"
CTAs to the <Button> primitive (consistent loading + disabled states).
Page header bands (full-bleed strips with border-bottom on dashboard,
my-cards, cards, collections, community/collections, collection/[id])
are intentionally left solid — they're not card-shaped surfaces and
stacking glass-on-glass directly below the already-glass topbar would
muddy the hierarchy.
Tests: lint clean, vitest 104/104, build green. The visual diff
baseline will need refresh because the homepage spec is unaffected
(it targets the unauthenticated landing page) but the dashboard/
admin/scanner surfaces will diff if/when we add baselines for them.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-03 22:28:52 -04:00
< div className = "glass-panel mb-8 p-6 rounded-xl" >
2026-05-28 10:18:01 -04:00
< div className = "flex flex-col gap-4 lg:flex-row lg:items-start lg:justify-between" >
< div >
< h2 className = "text-xl font-semibold mb-2" style = { { color : 'var(--text-primary)' } } >
Catalog sync
< / h 2 >
< p className = "text-sm max-w-2xl" style = { { color : 'var(--text-secondary)' } } >
2026-06-14 09:22:19 -04:00
{ syncMode === 'unified'
? 'Run bulk sync for MTG (Scryfall), Pokémon (GitHub), and Lorcana (lorcana-api.com) in one job — the same weekly Vercel cron path. Per-game failures are isolated.'
: syncMode === 'bulk'
feat(catalog): Scryfall bulk data import + Tagger community tags
Add full Scryfall bulk data pipeline:
- Migration: 13 new columns on `cards` (oracle_id, illustration_id,
color_identity, keywords, legalities, flavor_text, artist, released_at,
layout, edhrec_rank, reserved, reprint, finishes) with GIN indexes
for JSONB search.
- Migration: `tags` + `card_tags` tables for Tagger community data.
- Script: `bulk-import-scryfall.js` — downloads Oracle Cards bulk file
(168 MB) and upserts all 36k+ MTG cards with rich metadata.
- Script: `import-scryfall-tags.js` — imports oracle tags (4.5k tags,
227k taggings) and art tags (11k tags, 458k taggings).
- Lib: `bulk-sync.js` — runtime bulk sync callable from the admin API.
- Admin UI: mode toggle (incremental vs bulk) on catalog sync panel.
Enables Commander deck validation (color_identity), format legality
checks, keyword search, EDHREC popularity ranking, and functional
card tagging ("removal", "ramp", "draw") for deck building assistance.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-14 08:51:39 -04:00
? 'Download Scryfall\'s full Oracle Cards bulk file and upsert all 36k+ MTG cards with rich metadata (legalities, keywords, color identity, tags). Takes ~30s.'
2026-06-14 09:22:19 -04:00
: 'Import up to three newest missing MTG and Pokémon sets. Pending scan submissions are auto-linked when a unique match exists.' }
2026-05-28 10:18:01 -04:00
< / p >
2026-06-14 09:22:19 -04:00
< div className = "flex flex-wrap gap-2 mt-3" >
< button
onClick = { ( ) => setSyncMode ( 'unified' ) }
className = "px-3 py-1.5 rounded-lg text-xs font-medium transition-all"
style = { {
backgroundColor : syncMode === 'unified' ? 'var(--accent-ember)' : 'var(--bg-tertiary)' ,
color : syncMode === 'unified' ? '#fff' : 'var(--text-secondary)' ,
} }
>
Unified ( all games )
< / b u t t o n >
feat(catalog): Scryfall bulk data import + Tagger community tags
Add full Scryfall bulk data pipeline:
- Migration: 13 new columns on `cards` (oracle_id, illustration_id,
color_identity, keywords, legalities, flavor_text, artist, released_at,
layout, edhrec_rank, reserved, reprint, finishes) with GIN indexes
for JSONB search.
- Migration: `tags` + `card_tags` tables for Tagger community data.
- Script: `bulk-import-scryfall.js` — downloads Oracle Cards bulk file
(168 MB) and upserts all 36k+ MTG cards with rich metadata.
- Script: `import-scryfall-tags.js` — imports oracle tags (4.5k tags,
227k taggings) and art tags (11k tags, 458k taggings).
- Lib: `bulk-sync.js` — runtime bulk sync callable from the admin API.
- Admin UI: mode toggle (incremental vs bulk) on catalog sync panel.
Enables Commander deck validation (color_identity), format legality
checks, keyword search, EDHREC popularity ranking, and functional
card tagging ("removal", "ramp", "draw") for deck building assistance.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-14 08:51:39 -04:00
< button
onClick = { ( ) => setSyncMode ( 'incremental' ) }
className = "px-3 py-1.5 rounded-lg text-xs font-medium transition-all"
style = { {
backgroundColor : syncMode === 'incremental' ? 'var(--accent-ember)' : 'var(--bg-tertiary)' ,
color : syncMode === 'incremental' ? '#fff' : 'var(--text-secondary)' ,
} }
>
Incremental ( new sets )
< / b u t t o n >
< button
onClick = { ( ) => setSyncMode ( 'bulk' ) }
className = "px-3 py-1.5 rounded-lg text-xs font-medium transition-all"
style = { {
backgroundColor : syncMode === 'bulk' ? 'var(--accent-ember)' : 'var(--bg-tertiary)' ,
color : syncMode === 'bulk' ? '#fff' : 'var(--text-secondary)' ,
} }
>
2026-06-14 09:22:19 -04:00
Bulk MTG only
feat(catalog): Scryfall bulk data import + Tagger community tags
Add full Scryfall bulk data pipeline:
- Migration: 13 new columns on `cards` (oracle_id, illustration_id,
color_identity, keywords, legalities, flavor_text, artist, released_at,
layout, edhrec_rank, reserved, reprint, finishes) with GIN indexes
for JSONB search.
- Migration: `tags` + `card_tags` tables for Tagger community data.
- Script: `bulk-import-scryfall.js` — downloads Oracle Cards bulk file
(168 MB) and upserts all 36k+ MTG cards with rich metadata.
- Script: `import-scryfall-tags.js` — imports oracle tags (4.5k tags,
227k taggings) and art tags (11k tags, 458k taggings).
- Lib: `bulk-sync.js` — runtime bulk sync callable from the admin API.
- Admin UI: mode toggle (incremental vs bulk) on catalog sync panel.
Enables Commander deck validation (color_identity), format legality
checks, keyword search, EDHREC popularity ranking, and functional
card tagging ("removal", "ramp", "draw") for deck building assistance.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-14 08:51:39 -04:00
< / b u t t o n >
< / d i v >
2026-05-28 10:18:01 -04:00
< / d i v >
feat(design-system): sweep authenticated body-content panels to glass (#97)
PR #95/#96 shipped the Liquid Glass foundation (tokens, primitives, gates)
plus Layout shell, modals, landing, auth pages, and form CTAs — but body-
content panels on authenticated pages (admin Card Editor, admin Card
Import, admin Submissions, dashboard, my-cards, settings, scanner panels,
card detail price cards, popovers) were still rendering as flat
var(--bg-secondary) cards. Result: the admin Tools screen and several
core pages looked unchanged after the redesign.
This sweep adds a `.glass-panel` / `.glass-panel-strong` utility
(<GlassSurface tint=mid/high rim=subtle elevation=ambient/pronounced
blur=mid/high> in class form) and applies it across 18 surfaces:
* Admin Card Editor view, search panel, form (5 sections), preview
* Admin Card Import navigation + 3 body cards + sync panel
* Admin Card Submissions list items
* Dashboard stat cards + empty-state + grid items (5 surfaces)
* My-cards empty-state CTA card
* Settings panels (3)
* Scanner page settings + grid + queue + bulk toolbar + dialog
* Scanner destination picker + camera status banner + disambiguation
* Card detail price cards (Current / TCGPlayer / CardKingdom)
* Permission indicator tooltips
* Collections page header card
* Card detail view price cards
Also migrates the lingering admin Card Editor "Card Editor / Card Import"
nav buttons and the "Save Changes" / "Import Cards" / "Run catalog sync"
CTAs to the <Button> primitive (consistent loading + disabled states).
Page header bands (full-bleed strips with border-bottom on dashboard,
my-cards, cards, collections, community/collections, collection/[id])
are intentionally left solid — they're not card-shaped surfaces and
stacking glass-on-glass directly below the already-glass topbar would
muddy the hierarchy.
Tests: lint clean, vitest 104/104, build green. The visual diff
baseline will need refresh because the homepage spec is unaffected
(it targets the unauthenticated landing page) but the dashboard/
admin/scanner surfaces will diff if/when we add baselines for them.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-03 22:28:52 -04:00
< Button
variant = "primary"
size = "lg"
2026-05-28 10:18:01 -04:00
onClick = { handleCatalogSync }
disabled = { isSyncing }
feat(design-system): sweep authenticated body-content panels to glass (#97)
PR #95/#96 shipped the Liquid Glass foundation (tokens, primitives, gates)
plus Layout shell, modals, landing, auth pages, and form CTAs — but body-
content panels on authenticated pages (admin Card Editor, admin Card
Import, admin Submissions, dashboard, my-cards, settings, scanner panels,
card detail price cards, popovers) were still rendering as flat
var(--bg-secondary) cards. Result: the admin Tools screen and several
core pages looked unchanged after the redesign.
This sweep adds a `.glass-panel` / `.glass-panel-strong` utility
(<GlassSurface tint=mid/high rim=subtle elevation=ambient/pronounced
blur=mid/high> in class form) and applies it across 18 surfaces:
* Admin Card Editor view, search panel, form (5 sections), preview
* Admin Card Import navigation + 3 body cards + sync panel
* Admin Card Submissions list items
* Dashboard stat cards + empty-state + grid items (5 surfaces)
* My-cards empty-state CTA card
* Settings panels (3)
* Scanner page settings + grid + queue + bulk toolbar + dialog
* Scanner destination picker + camera status banner + disambiguation
* Card detail price cards (Current / TCGPlayer / CardKingdom)
* Permission indicator tooltips
* Collections page header card
* Card detail view price cards
Also migrates the lingering admin Card Editor "Card Editor / Card Import"
nav buttons and the "Save Changes" / "Import Cards" / "Run catalog sync"
CTAs to the <Button> primitive (consistent loading + disabled states).
Page header bands (full-bleed strips with border-bottom on dashboard,
my-cards, cards, collections, community/collections, collection/[id])
are intentionally left solid — they're not card-shaped surfaces and
stacking glass-on-glass directly below the already-glass topbar would
muddy the hierarchy.
Tests: lint clean, vitest 104/104, build green. The visual diff
baseline will need refresh because the homepage spec is unaffected
(it targets the unauthenticated landing page) but the dashboard/
admin/scanner surfaces will diff if/when we add baselines for them.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-03 22:28:52 -04:00
loading = { isSyncing }
className = "shrink-0"
2026-05-28 10:18:01 -04:00
>
2026-06-14 09:22:19 -04:00
{ isSyncing ? 'Syncing catalog…' : syncMode === 'unified' ? 'Run unified sync' : syncMode === 'bulk' ? 'Run bulk MTG sync' : 'Run incremental sync' }
feat(design-system): sweep authenticated body-content panels to glass (#97)
PR #95/#96 shipped the Liquid Glass foundation (tokens, primitives, gates)
plus Layout shell, modals, landing, auth pages, and form CTAs — but body-
content panels on authenticated pages (admin Card Editor, admin Card
Import, admin Submissions, dashboard, my-cards, settings, scanner panels,
card detail price cards, popovers) were still rendering as flat
var(--bg-secondary) cards. Result: the admin Tools screen and several
core pages looked unchanged after the redesign.
This sweep adds a `.glass-panel` / `.glass-panel-strong` utility
(<GlassSurface tint=mid/high rim=subtle elevation=ambient/pronounced
blur=mid/high> in class form) and applies it across 18 surfaces:
* Admin Card Editor view, search panel, form (5 sections), preview
* Admin Card Import navigation + 3 body cards + sync panel
* Admin Card Submissions list items
* Dashboard stat cards + empty-state + grid items (5 surfaces)
* My-cards empty-state CTA card
* Settings panels (3)
* Scanner page settings + grid + queue + bulk toolbar + dialog
* Scanner destination picker + camera status banner + disambiguation
* Card detail price cards (Current / TCGPlayer / CardKingdom)
* Permission indicator tooltips
* Collections page header card
* Card detail view price cards
Also migrates the lingering admin Card Editor "Card Editor / Card Import"
nav buttons and the "Save Changes" / "Import Cards" / "Run catalog sync"
CTAs to the <Button> primitive (consistent loading + disabled states).
Page header bands (full-bleed strips with border-bottom on dashboard,
my-cards, cards, collections, community/collections, collection/[id])
are intentionally left solid — they're not card-shaped surfaces and
stacking glass-on-glass directly below the already-glass topbar would
muddy the hierarchy.
Tests: lint clean, vitest 104/104, build green. The visual diff
baseline will need refresh because the homepage spec is unaffected
(it targets the unauthenticated landing page) but the dashboard/
admin/scanner surfaces will diff if/when we add baselines for them.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-03 22:28:52 -04:00
< / B u t t o n >
2026-05-28 10:18:01 -04:00
< / d i v >
{ syncResult && (
< div
className = { ` mt-4 p-4 rounded-xl ${
syncResult . success
? 'bg-green-100 border border-green-300'
: 'bg-red-100 border border-red-300'
} ` }
>
< h3
className = { ` font-semibold mb-2 ${
syncResult . success ? 'text-green-800' : 'text-red-800'
} ` }
>
{ syncResult . success ? 'Catalog sync complete' : 'Catalog sync failed' }
< / h 3 >
{ syncResult . success ? (
< div className = "text-sm space-y-2 text-green-900" >
2026-06-14 09:22:19 -04:00
{ syncResult . mode === 'unified' ? (
< >
< p >
Unified sync finished in { ( syncResult . durationMs / 1000 ) . toFixed ( 1 ) } s .
< / p >
< ul className = "list-disc pl-5 space-y-1" >
{ syncResult . mtg && (
< li >
MTG : { ' ' }
{ syncResult . mtg . error
? ` failed — ${ syncResult . mtg . error } `
: ` ${ syncResult . mtg . upserted ? ? 0 } upserted ${ syncResult . mtg . errors ? ` ( ${ syncResult . mtg . errors } row errors) ` : '' } ` }
< / l i >
) }
{ syncResult . pokemon && (
< li >
Pokémon : { ' ' }
{ syncResult . pokemon . error
? ` failed — ${ syncResult . pokemon . error } `
: ` ${ syncResult . pokemon . upserted ? ? 0 } upserted across ${ syncResult . pokemon . setsProcessed ? ? 0 } sets ${ syncResult . pokemon . errors ? ` ( ${ syncResult . pokemon . errors } set errors) ` : '' } ` }
< / l i >
) }
{ syncResult . lorcana && (
< li >
Lorcana : { ' ' }
{ syncResult . lorcana . error
? ` failed — ${ syncResult . lorcana . error } `
: ` ${ syncResult . lorcana . upserted ? ? 0 } upserted ${ syncResult . lorcana . errors ? ` ( ${ syncResult . lorcana . errors } row errors) ` : '' } ` }
< / l i >
) }
< / u l >
< / >
) : syncResult . mode === 'bulk' ? (
feat(catalog): Scryfall bulk data import + Tagger community tags
Add full Scryfall bulk data pipeline:
- Migration: 13 new columns on `cards` (oracle_id, illustration_id,
color_identity, keywords, legalities, flavor_text, artist, released_at,
layout, edhrec_rank, reserved, reprint, finishes) with GIN indexes
for JSONB search.
- Migration: `tags` + `card_tags` tables for Tagger community data.
- Script: `bulk-import-scryfall.js` — downloads Oracle Cards bulk file
(168 MB) and upserts all 36k+ MTG cards with rich metadata.
- Script: `import-scryfall-tags.js` — imports oracle tags (4.5k tags,
227k taggings) and art tags (11k tags, 458k taggings).
- Lib: `bulk-sync.js` — runtime bulk sync callable from the admin API.
- Admin UI: mode toggle (incremental vs bulk) on catalog sync panel.
Enables Commander deck validation (color_identity), format legality
checks, keyword search, EDHREC popularity ranking, and functional
card tagging ("removal", "ramp", "draw") for deck building assistance.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-14 08:51:39 -04:00
< p >
Upserted { syncResult . upserted } MTG cards from Scryfall bulk data .
{ syncResult . backfilledOracleId > 0 && ` Backfilled oracle_id on ${ syncResult . backfilledOracleId } existing rows. ` }
{ syncResult . errors > 0 && ` ( ${ syncResult . errors } errors) ` }
< / p >
) : (
2026-05-28 10:18:01 -04:00
< p >
Imported { syncResult . imported } cards ( { syncResult . skipped } skipped ) .
< / p >
feat(catalog): Scryfall bulk data import + Tagger community tags
Add full Scryfall bulk data pipeline:
- Migration: 13 new columns on `cards` (oracle_id, illustration_id,
color_identity, keywords, legalities, flavor_text, artist, released_at,
layout, edhrec_rank, reserved, reprint, finishes) with GIN indexes
for JSONB search.
- Migration: `tags` + `card_tags` tables for Tagger community data.
- Script: `bulk-import-scryfall.js` — downloads Oracle Cards bulk file
(168 MB) and upserts all 36k+ MTG cards with rich metadata.
- Script: `import-scryfall-tags.js` — imports oracle tags (4.5k tags,
227k taggings) and art tags (11k tags, 458k taggings).
- Lib: `bulk-sync.js` — runtime bulk sync callable from the admin API.
- Admin UI: mode toggle (incremental vs bulk) on catalog sync panel.
Enables Commander deck validation (color_identity), format legality
checks, keyword search, EDHREC popularity ranking, and functional
card tagging ("removal", "ramp", "draw") for deck building assistance.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-14 08:51:39 -04:00
) }
2026-05-28 10:47:59 -04:00
{ syncResult . submissionsReconciled > 0 && (
< p >
Linked { syncResult . submissionsReconciled } pending scan submission
{ syncResult . submissionsReconciled === 1 ? '' : 's' } to catalog cards .
< / p >
) }
2026-05-28 10:18:01 -04:00
{ ( syncResult . pendingMtgSets != null || syncResult . pendingPokemonSets != null ) && (
< p >
Still pending : { syncResult . pendingMtgSets } MTG sets , { ' ' }
{ syncResult . pendingPokemonSets } Pokémon sets .
< / p >
) }
{ syncResult . lorcana && < p > { syncResult . lorcana } < / p > }
{ Array . isArray ( syncResult . setsProcessed ) && syncResult . setsProcessed . length > 0 && (
< ul className = "list-disc pl-5 space-y-1" >
{ syncResult . setsProcessed . map ( ( set ) => (
< li key = { ` ${ set . game } - ${ set . setCode } ` } >
{ set . name } ( { set . game } / { set . setCode } ) — { set . imported } imported
< / l i >
) ) }
< / u l >
) }
2026-05-28 10:47:59 -04:00
{ Array . isArray ( syncResult . reconciliation ) && syncResult . reconciliation . length > 0 && (
< ul className = "list-disc pl-5 space-y-1" >
{ syncResult . reconciliation . flatMap ( ( entry ) =>
entry . details . map ( ( detail ) => (
< li key = { ` submission- ${ detail . submissionId } ` } >
Submission # { detail . submissionId } : { detail . name }
{ detail . cardNumber ? ` ( ${ detail . cardNumber } ) ` : '' } → card # { detail . cardId }
< / l i >
) )
) }
< / u l >
) }
2026-05-28 10:18:01 -04:00
{ Array . isArray ( syncResult . errors ) && syncResult . errors . length > 0 && (
< ul className = "list-disc pl-5 space-y-1 text-red-800" >
{ syncResult . errors . map ( ( entry ) => (
2026-06-14 09:22:19 -04:00
< li key = { ` ${ entry . game } - ${ entry . setCode || entry . message } ` } >
{ entry . setCode
? ` ${ entry . game } / ${ entry . setCode } : ${ entry . message } `
: ` ${ entry . game } : ${ entry . message } ` }
2026-05-28 10:18:01 -04:00
< / l i >
) ) }
< / u l >
) }
< / d i v >
) : (
< p className = "text-sm text-red-800" > { syncResult . message } < / p >
) }
< / d i v >
) }
< / d i v >
2025-07-23 22:26:54 -04:00
< div className = "grid grid-cols-1 lg:grid-cols-2 gap-8" >
{ /* Import Form */ }
< div className = "space-y-6" >
feat(design-system): sweep authenticated body-content panels to glass (#97)
PR #95/#96 shipped the Liquid Glass foundation (tokens, primitives, gates)
plus Layout shell, modals, landing, auth pages, and form CTAs — but body-
content panels on authenticated pages (admin Card Editor, admin Card
Import, admin Submissions, dashboard, my-cards, settings, scanner panels,
card detail price cards, popovers) were still rendering as flat
var(--bg-secondary) cards. Result: the admin Tools screen and several
core pages looked unchanged after the redesign.
This sweep adds a `.glass-panel` / `.glass-panel-strong` utility
(<GlassSurface tint=mid/high rim=subtle elevation=ambient/pronounced
blur=mid/high> in class form) and applies it across 18 surfaces:
* Admin Card Editor view, search panel, form (5 sections), preview
* Admin Card Import navigation + 3 body cards + sync panel
* Admin Card Submissions list items
* Dashboard stat cards + empty-state + grid items (5 surfaces)
* My-cards empty-state CTA card
* Settings panels (3)
* Scanner page settings + grid + queue + bulk toolbar + dialog
* Scanner destination picker + camera status banner + disambiguation
* Card detail price cards (Current / TCGPlayer / CardKingdom)
* Permission indicator tooltips
* Collections page header card
* Card detail view price cards
Also migrates the lingering admin Card Editor "Card Editor / Card Import"
nav buttons and the "Save Changes" / "Import Cards" / "Run catalog sync"
CTAs to the <Button> primitive (consistent loading + disabled states).
Page header bands (full-bleed strips with border-bottom on dashboard,
my-cards, cards, collections, community/collections, collection/[id])
are intentionally left solid — they're not card-shaped surfaces and
stacking glass-on-glass directly below the already-glass topbar would
muddy the hierarchy.
Tests: lint clean, vitest 104/104, build green. The visual diff
baseline will need refresh because the homepage spec is unaffected
(it targets the unauthenticated landing page) but the dashboard/
admin/scanner surfaces will diff if/when we add baselines for them.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-03 22:28:52 -04:00
< div className = "glass-panel p-6 rounded-xl" >
2025-07-23 22:26:54 -04:00
< h2 className = "text-xl font-semibold mb-4" style = { { color : 'var(--text-primary)' } } >
Import Settings
< / h 2 >
< div className = "space-y-4" >
< div >
< label className = "block text-sm font-medium mb-2" style = { { color : 'var(--text-primary)' } } >
TCG Type
< / l a b e l >
< select
value = { importType }
onChange = { ( e ) => setImportType ( e . target . value ) }
className = "input-field w-full"
>
< option value = "mtg" > Magic : The Gathering < / o p t i o n >
< option value = "pokemon" > Pokemon TCG < / o p t i o n >
< / s e l e c t >
< / d i v >
< div >
< label className = "block text-sm font-medium mb-2" style = { { color : 'var(--text-primary)' } } >
Set Code
< / l a b e l >
< input
type = "text"
value = { setCode }
onChange = { ( e ) => setSetCode ( e . target . value ) }
placeholder = "e.g., neo, vow, swsh1"
className = "input-field w-full"
/ >
< p className = "text-xs mt-1" style = { { color : 'var(--text-secondary)' } } >
Enter the set code ( usually 3 - 4 characters )
< / p >
< / d i v >
feat(design-system): sweep authenticated body-content panels to glass (#97)
PR #95/#96 shipped the Liquid Glass foundation (tokens, primitives, gates)
plus Layout shell, modals, landing, auth pages, and form CTAs — but body-
content panels on authenticated pages (admin Card Editor, admin Card
Import, admin Submissions, dashboard, my-cards, settings, scanner panels,
card detail price cards, popovers) were still rendering as flat
var(--bg-secondary) cards. Result: the admin Tools screen and several
core pages looked unchanged after the redesign.
This sweep adds a `.glass-panel` / `.glass-panel-strong` utility
(<GlassSurface tint=mid/high rim=subtle elevation=ambient/pronounced
blur=mid/high> in class form) and applies it across 18 surfaces:
* Admin Card Editor view, search panel, form (5 sections), preview
* Admin Card Import navigation + 3 body cards + sync panel
* Admin Card Submissions list items
* Dashboard stat cards + empty-state + grid items (5 surfaces)
* My-cards empty-state CTA card
* Settings panels (3)
* Scanner page settings + grid + queue + bulk toolbar + dialog
* Scanner destination picker + camera status banner + disambiguation
* Card detail price cards (Current / TCGPlayer / CardKingdom)
* Permission indicator tooltips
* Collections page header card
* Card detail view price cards
Also migrates the lingering admin Card Editor "Card Editor / Card Import"
nav buttons and the "Save Changes" / "Import Cards" / "Run catalog sync"
CTAs to the <Button> primitive (consistent loading + disabled states).
Page header bands (full-bleed strips with border-bottom on dashboard,
my-cards, cards, collections, community/collections, collection/[id])
are intentionally left solid — they're not card-shaped surfaces and
stacking glass-on-glass directly below the already-glass topbar would
muddy the hierarchy.
Tests: lint clean, vitest 104/104, build green. The visual diff
baseline will need refresh because the homepage spec is unaffected
(it targets the unauthenticated landing page) but the dashboard/
admin/scanner surfaces will diff if/when we add baselines for them.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-03 22:28:52 -04:00
< Button
variant = "primary"
size = "lg"
2025-07-23 22:26:54 -04:00
onClick = { handleImport }
disabled = { isImporting || ! setCode . trim ( ) }
feat(design-system): sweep authenticated body-content panels to glass (#97)
PR #95/#96 shipped the Liquid Glass foundation (tokens, primitives, gates)
plus Layout shell, modals, landing, auth pages, and form CTAs — but body-
content panels on authenticated pages (admin Card Editor, admin Card
Import, admin Submissions, dashboard, my-cards, settings, scanner panels,
card detail price cards, popovers) were still rendering as flat
var(--bg-secondary) cards. Result: the admin Tools screen and several
core pages looked unchanged after the redesign.
This sweep adds a `.glass-panel` / `.glass-panel-strong` utility
(<GlassSurface tint=mid/high rim=subtle elevation=ambient/pronounced
blur=mid/high> in class form) and applies it across 18 surfaces:
* Admin Card Editor view, search panel, form (5 sections), preview
* Admin Card Import navigation + 3 body cards + sync panel
* Admin Card Submissions list items
* Dashboard stat cards + empty-state + grid items (5 surfaces)
* My-cards empty-state CTA card
* Settings panels (3)
* Scanner page settings + grid + queue + bulk toolbar + dialog
* Scanner destination picker + camera status banner + disambiguation
* Card detail price cards (Current / TCGPlayer / CardKingdom)
* Permission indicator tooltips
* Collections page header card
* Card detail view price cards
Also migrates the lingering admin Card Editor "Card Editor / Card Import"
nav buttons and the "Save Changes" / "Import Cards" / "Run catalog sync"
CTAs to the <Button> primitive (consistent loading + disabled states).
Page header bands (full-bleed strips with border-bottom on dashboard,
my-cards, cards, collections, community/collections, collection/[id])
are intentionally left solid — they're not card-shaped surfaces and
stacking glass-on-glass directly below the already-glass topbar would
muddy the hierarchy.
Tests: lint clean, vitest 104/104, build green. The visual diff
baseline will need refresh because the homepage spec is unaffected
(it targets the unauthenticated landing page) but the dashboard/
admin/scanner surfaces will diff if/when we add baselines for them.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-03 22:28:52 -04:00
loading = { isImporting }
fullWidth
2025-07-23 22:26:54 -04:00
>
{ isImporting ? 'Importing...' : 'Import Cards' }
feat(design-system): sweep authenticated body-content panels to glass (#97)
PR #95/#96 shipped the Liquid Glass foundation (tokens, primitives, gates)
plus Layout shell, modals, landing, auth pages, and form CTAs — but body-
content panels on authenticated pages (admin Card Editor, admin Card
Import, admin Submissions, dashboard, my-cards, settings, scanner panels,
card detail price cards, popovers) were still rendering as flat
var(--bg-secondary) cards. Result: the admin Tools screen and several
core pages looked unchanged after the redesign.
This sweep adds a `.glass-panel` / `.glass-panel-strong` utility
(<GlassSurface tint=mid/high rim=subtle elevation=ambient/pronounced
blur=mid/high> in class form) and applies it across 18 surfaces:
* Admin Card Editor view, search panel, form (5 sections), preview
* Admin Card Import navigation + 3 body cards + sync panel
* Admin Card Submissions list items
* Dashboard stat cards + empty-state + grid items (5 surfaces)
* My-cards empty-state CTA card
* Settings panels (3)
* Scanner page settings + grid + queue + bulk toolbar + dialog
* Scanner destination picker + camera status banner + disambiguation
* Card detail price cards (Current / TCGPlayer / CardKingdom)
* Permission indicator tooltips
* Collections page header card
* Card detail view price cards
Also migrates the lingering admin Card Editor "Card Editor / Card Import"
nav buttons and the "Save Changes" / "Import Cards" / "Run catalog sync"
CTAs to the <Button> primitive (consistent loading + disabled states).
Page header bands (full-bleed strips with border-bottom on dashboard,
my-cards, cards, collections, community/collections, collection/[id])
are intentionally left solid — they're not card-shaped surfaces and
stacking glass-on-glass directly below the already-glass topbar would
muddy the hierarchy.
Tests: lint clean, vitest 104/104, build green. The visual diff
baseline will need refresh because the homepage spec is unaffected
(it targets the unauthenticated landing page) but the dashboard/
admin/scanner surfaces will diff if/when we add baselines for them.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-03 22:28:52 -04:00
< / B u t t o n >
2025-07-23 22:26:54 -04:00
< / d i v >
< / d i v >
{ /* Import Result */ }
{ importResult && (
< div className = { ` p-6 rounded-xl ${
importResult . success
? 'bg-green-100 border border-green-300'
: 'bg-red-100 border border-red-300'
} ` }>
< h3 className = { ` font-semibold mb-2 ${
importResult . success ? 'text-green-800' : 'text-red-800'
} ` }>
{ importResult . success ? 'Import Successful' : 'Import Failed' }
< / h 3 >
< p className = "text-sm mb-2" >
{ importResult . message }
< / p >
{ importResult . success && (
< div className = "text-sm space-y-1" >
< p > Imported : { importResult . imported } < / p >
< p > Skipped : { importResult . skipped } < / p >
< p > Total : { importResult . total } < / p >
< / d i v >
) }
< / d i v >
) }
< / d i v >
{ /* Popular Sets */ }
< div className = "space-y-6" >
feat(design-system): sweep authenticated body-content panels to glass (#97)
PR #95/#96 shipped the Liquid Glass foundation (tokens, primitives, gates)
plus Layout shell, modals, landing, auth pages, and form CTAs — but body-
content panels on authenticated pages (admin Card Editor, admin Card
Import, admin Submissions, dashboard, my-cards, settings, scanner panels,
card detail price cards, popovers) were still rendering as flat
var(--bg-secondary) cards. Result: the admin Tools screen and several
core pages looked unchanged after the redesign.
This sweep adds a `.glass-panel` / `.glass-panel-strong` utility
(<GlassSurface tint=mid/high rim=subtle elevation=ambient/pronounced
blur=mid/high> in class form) and applies it across 18 surfaces:
* Admin Card Editor view, search panel, form (5 sections), preview
* Admin Card Import navigation + 3 body cards + sync panel
* Admin Card Submissions list items
* Dashboard stat cards + empty-state + grid items (5 surfaces)
* My-cards empty-state CTA card
* Settings panels (3)
* Scanner page settings + grid + queue + bulk toolbar + dialog
* Scanner destination picker + camera status banner + disambiguation
* Card detail price cards (Current / TCGPlayer / CardKingdom)
* Permission indicator tooltips
* Collections page header card
* Card detail view price cards
Also migrates the lingering admin Card Editor "Card Editor / Card Import"
nav buttons and the "Save Changes" / "Import Cards" / "Run catalog sync"
CTAs to the <Button> primitive (consistent loading + disabled states).
Page header bands (full-bleed strips with border-bottom on dashboard,
my-cards, cards, collections, community/collections, collection/[id])
are intentionally left solid — they're not card-shaped surfaces and
stacking glass-on-glass directly below the already-glass topbar would
muddy the hierarchy.
Tests: lint clean, vitest 104/104, build green. The visual diff
baseline will need refresh because the homepage spec is unaffected
(it targets the unauthenticated landing page) but the dashboard/
admin/scanner surfaces will diff if/when we add baselines for them.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-03 22:28:52 -04:00
< div className = "glass-panel p-6 rounded-xl" >
2025-07-23 22:26:54 -04:00
< h2 className = "text-xl font-semibold mb-4" style = { { color : 'var(--text-primary)' } } >
Popular Sets
< / h 2 >
< div className = "space-y-4" >
< div >
< h3 className = "font-medium mb-2" style = { { color : 'var(--text-primary)' } } >
Magic : The Gathering
< / h 3 >
< div className = "grid grid-cols-2 gap-2" >
{ popularSets . mtg . map ( ( set ) => (
< button
key = { set . code }
onClick = { ( ) => {
setImportType ( 'mtg' ) ;
setSetCode ( set . code ) ;
} }
className = "text-left p-2 rounded-lg text-sm hover:bg-opacity-20 transition-all duration-200"
style = { {
backgroundColor : 'var(--bg-tertiary)' ,
color : 'var(--text-secondary)'
} }
>
< div className = "font-medium" > { set . code . toUpperCase ( ) } < / d i v >
< div className = "text-xs opacity-75" > { set . name } < / d i v >
< / b u t t o n >
) ) }
< / d i v >
< / d i v >
< div >
< h3 className = "font-medium mb-2" style = { { color : 'var(--text-primary)' } } >
Pokemon TCG
< / h 3 >
< div className = "grid grid-cols-2 gap-2" >
{ popularSets . pokemon . map ( ( set ) => (
< button
key = { set . code }
onClick = { ( ) => {
setImportType ( 'pokemon' ) ;
setSetCode ( set . code ) ;
} }
className = "text-left p-2 rounded-lg text-sm hover:bg-opacity-20 transition-all duration-200"
style = { {
backgroundColor : 'var(--bg-tertiary)' ,
color : 'var(--text-secondary)'
} }
>
< div className = "font-medium" > { set . code . toUpperCase ( ) } < / d i v >
< div className = "text-xs opacity-75" > { set . name } < / d i v >
< / b u t t o n >
) ) }
< / d i v >
< / d i v >
< / d i v >
< / d i v >
{ /* Import Tips */ }
feat(design-system): sweep authenticated body-content panels to glass (#97)
PR #95/#96 shipped the Liquid Glass foundation (tokens, primitives, gates)
plus Layout shell, modals, landing, auth pages, and form CTAs — but body-
content panels on authenticated pages (admin Card Editor, admin Card
Import, admin Submissions, dashboard, my-cards, settings, scanner panels,
card detail price cards, popovers) were still rendering as flat
var(--bg-secondary) cards. Result: the admin Tools screen and several
core pages looked unchanged after the redesign.
This sweep adds a `.glass-panel` / `.glass-panel-strong` utility
(<GlassSurface tint=mid/high rim=subtle elevation=ambient/pronounced
blur=mid/high> in class form) and applies it across 18 surfaces:
* Admin Card Editor view, search panel, form (5 sections), preview
* Admin Card Import navigation + 3 body cards + sync panel
* Admin Card Submissions list items
* Dashboard stat cards + empty-state + grid items (5 surfaces)
* My-cards empty-state CTA card
* Settings panels (3)
* Scanner page settings + grid + queue + bulk toolbar + dialog
* Scanner destination picker + camera status banner + disambiguation
* Card detail price cards (Current / TCGPlayer / CardKingdom)
* Permission indicator tooltips
* Collections page header card
* Card detail view price cards
Also migrates the lingering admin Card Editor "Card Editor / Card Import"
nav buttons and the "Save Changes" / "Import Cards" / "Run catalog sync"
CTAs to the <Button> primitive (consistent loading + disabled states).
Page header bands (full-bleed strips with border-bottom on dashboard,
my-cards, cards, collections, community/collections, collection/[id])
are intentionally left solid — they're not card-shaped surfaces and
stacking glass-on-glass directly below the already-glass topbar would
muddy the hierarchy.
Tests: lint clean, vitest 104/104, build green. The visual diff
baseline will need refresh because the homepage spec is unaffected
(it targets the unauthenticated landing page) but the dashboard/
admin/scanner surfaces will diff if/when we add baselines for them.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-03 22:28:52 -04:00
< div className = "glass-panel p-6 rounded-xl" >
2025-07-23 22:26:54 -04:00
< h2 className = "text-xl font-semibold mb-4" style = { { color : 'var(--text-primary)' } } >
Import Tips
< / h 2 >
< ul className = "space-y-2 text-sm" style = { { color : 'var(--text-secondary)' } } >
< li > • Set codes are case - insensitive < / l i >
< li > • Duplicate cards will be skipped automatically < / l i >
< li > • Import may take several minutes for large sets < / l i >
< li > • Prices are fetched from TCGPlayer when available < / l i >
< li > • Images are stored as URLs to external sources < / l i >
< / u l >
< / d i v >
< / d i v >
< / d i v >
< / d i v >
2025-07-24 17:31:29 -04:00
< / L a y o u t >
2025-07-24 18:04:06 -04:00
) }
2025-07-24 17:31:29 -04:00
< / A d m i n P r o t e c t e d >
2025-07-23 22:26:54 -04:00
) ;
2025-07-24 18:04:06 -04:00
} ;
// Export with dynamic import to disable SSR
export default dynamic ( ( ) => Promise . resolve ( CardImport ) , { ssr : false } ) ;