deckhearth/components/DeckBuilderView.js
Randall Stillwell 1769d4576a refactor(design): site-wide sweep — broken Tailwind tokens, rounded corners, SearchBar primitive (#117)
Comprehensive design sweep across the rest of the app following the
shipped Liquid Glass + corner-border-light system (#116).

## Three classes of finding

### 1. Broken Tailwind token classes (HIGH — pages were unstyled)

The decks / deck-builder / deck-detail cluster relied on Tailwind
classes that don't exist in `tailwind.config.js` (no `bg-bg-*`,
`text-text-*`, `border-border`, `bg-accent-ember`,
`focus:ring-accent-ember`, `hover:bg-accent-ember-dark`). Those classes
produced ZERO CSS — backgrounds were transparent, borders invisible,
hover states absent.

Rewrote with inline `style={{ ... CSS vars ... }}` + the `<Button>` /
`<SearchBar>` primitives + `glass-panel` surfaces:

- `pages/decks.js` (full page)
- `pages/deck/[id].js` (header, stats sidebar, group-by controls,
  card list)
- `pages/deck-builder.js` (loading spinner)
- `components/DeckBuilderView.js` (toolbar + main panel)
- `components/DeckBuilderCardBrowser.js` (full rewrite; integrated
  `<SearchBar>` for the card-picker input)
- `components/DeckBuilderDeckList.js` (full rewrite)
- `components/DeckBuilderStatsBar.js`
- `components/ManaSymbolSettings.js`
- `components/ManaSymbols.js` (single `text-text-secondary`)
- `pages/admin/card-editor.js` cluster was already clean

### 2. Duplicative / stale page searches

Replaced raw `<input>` search controls with the `<SearchBar>` primitive
(adds clear button, ember focus ring, system-consistent rounded
corners). Kept page-specific filter searches (they filter the visible
list — distinct from the global TopSearchBar command palette):

- `pages/my-cards.js`
- `pages/community/collections.js`
- `components/CardsPageView.js`
- `components/CollectionPageView.js`
- `components/DeckBuilderCardBrowser.js`

`pages/my-cards.js` filter wrapper also lifted into a `glass-panel`
chip instead of a solid `var(--bg-primary)` band.

### 3. Square corners + stale palette in shared views

- `components/CollectionPageView.js`: 10 action buttons (`rounded-lg`
  + `hover:bg-gray-50`) → `rounded-xl` + `nav-item-hover`; 4 filter
  selects (`focus:ring-purple-500 rounded-lg`) → `.input-field`;
  view-mode toggle (`bg-white text-gray-900` — invisible in dark mode)
  → tokenised; SYSTEM badge gradient (`from-blue-500 to-purple-600`)
  → ember↔flame; tooltip (`bg-gray-900`) → `glass-panel-strong`;
  search-results dropdown (`bg-white border-gray-200` — invisible in
  dark mode) → `glass-panel-strong`; Activity / game-count /
  TCG-game badges palette-aligned.
- `components/CardsPageView.js`: "Load More Cards" button
  (`bg-gradient-to-r from-blue-500 to-purple-600 rounded-lg`) →
  `<Button variant="primary" size="lg">`.
- `components/CollectionsPageView.js`: matching SYSTEM badge +
  tooltip cleanup.
- `components/ShareModal.js`: user-search dropdown
  (`border-gray-200 hover:bg-gray-50`) and email-invite card moved
  onto `glass-panel` + `nav-item-hover`; social-share buttons
  `rounded-lg hover:bg-gray-50` → `rounded-xl nav-item-hover`.
- `components/Layout.js`: profile-menu dropdown row
  (`hover:bg-gray-50 dark:hover:bg-gray-700`) → `nav-item-hover`.
- `components/CardItem.js`: bulk-select checkbox
  `focus:ring-purple-500` → ember.

### 4. `dark:` modifier classes (broken with `[data-theme]` theming)

This app uses `[data-theme="dark"]` CSS selector theming, not
Tailwind's `class` strategy, so `dark:bg-green-900/20` etc. produced
no CSS in dark mode. Affected alerts on `pages/settings.js` and
`pages/profile.js` — replaced with `glass-panel` + semantic border
colour (flame for success, #dc2626 for error).

`pages/settings.js` sidebar nav also moved off its hardcoded full-ember
fill onto the system `nav-item` / `nav-item-active` / `nav-item-hover`
pattern for consistency with the global sidebar.

## Verification

- `npm run build` — green (Next 16 + Turbopack)
- `npm run lint` — 0 errors, 1 unrelated pre-existing warning
- `npm run test:run` — 113/113 pass (no test changes needed)

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-04 12:55:29 -05:00

134 lines
4.6 KiB
JavaScript

import Link from 'next/link';
import DeckBuilderCardBrowser from './DeckBuilderCardBrowser';
import DeckBuilderDeckList from './DeckBuilderDeckList';
import DeckBuilderStatsBar from './DeckBuilderStatsBar';
import { Button } from './ui';
import { computeDeckStats } from '../lib/deck-builder-stats';
/* 2026-06-04 design-sweep pass: replaced broken Tailwind token
classes (bg-bg-secondary, text-text-primary, bg-accent-ember, etc.
— none of which are defined in tailwind.config.js and produced
zero CSS) with inline style={{ ... CSS vars ... }} + the Button
primitive + `glass-panel` + `rounded-2xl` so this view actually
renders with the Liquid Glass design system. */
export default function DeckBuilderView({
addCardToDeck,
clearFilters,
deck,
deckCards,
filters,
manaSymbolSettings,
removeCardFromDeck,
searchLoading,
searchQuery,
searchResults,
selectedCard,
setFilters,
setManaSymbolSettings,
setSearchQuery,
setSelectedCard,
setShowFilters,
setShowSettings,
setSidebarOpen,
setViewMode,
showFilters,
showSettings,
sidebarOpen,
toggleColorFilter,
viewMode,
}) {
const stats = computeDeckStats(deckCards);
return (
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
<div className="flex justify-between items-center mb-6">
<div>
<div className="flex items-center space-x-3">
<Link
href="/decks"
className="hover:underline"
style={{ color: 'var(--accent-ember)' }}
>
Back to Decks
</Link>
</div>
<h1 className="text-3xl font-bold mt-2" style={{ color: 'var(--text-primary)' }}>
{deck.name}
</h1>
<p style={{ color: 'var(--text-secondary)' }}>
{deck.format} {stats.totalCards}/100 cards
</p>
</div>
<div className="flex space-x-3">
<Button type="button" variant="secondary">
Save Deck
</Button>
<Link href={`/deck/${deck.id}`}>
<Button variant="primary">View Deck</Button>
</Link>
</div>
</div>
<div className="flex gap-6 h-[calc(100vh-12rem)]">
<div className="flex-1 transition-all duration-300">
<div className="glass-panel rounded-2xl p-6 h-full flex flex-col">
<div className="flex justify-between items-center mb-6">
<h2 className="text-xl font-semibold" style={{ color: 'var(--text-primary)' }}>
Deck Cards ({stats.totalCards})
</h2>
<Button
type="button"
variant="primary"
onClick={() => setSidebarOpen(!sidebarOpen)}
trailingIcon={<span aria-hidden="true">{sidebarOpen ? '→' : '←'}</span>}
>
{sidebarOpen ? 'Hide' : 'Show'} Browser
</Button>
</div>
<DeckBuilderStatsBar stats={stats} />
<DeckBuilderDeckList
deckCards={deckCards}
sidebarOpen={sidebarOpen}
onOpenSidebar={() => setSidebarOpen(true)}
manaSymbolSettings={manaSymbolSettings}
onAddCard={(card, quantity) => addCardToDeck(card, quantity)}
onRemoveCard={(cardId, quantity) => removeCardFromDeck(cardId, quantity)}
/>
</div>
</div>
<div className={`transition-all duration-300 ${sidebarOpen ? 'w-96' : 'w-0 overflow-hidden'}`}>
<DeckBuilderCardBrowser
selectedCard={selectedCard}
onSelectCard={setSelectedCard}
onClearSelectedCard={() => setSelectedCard(null)}
viewMode={viewMode}
onViewModeChange={setViewMode}
showSettings={showSettings}
onToggleSettings={() => setShowSettings(!showSettings)}
onCollapseSidebar={() => setSidebarOpen(false)}
searchQuery={searchQuery}
onSearchQueryChange={setSearchQuery}
showFilters={showFilters}
onToggleFilters={() => setShowFilters(!showFilters)}
filters={filters}
onFiltersChange={setFilters}
onClearFilters={clearFilters}
onToggleColorFilter={toggleColorFilter}
searchLoading={searchLoading}
searchResults={searchResults}
manaSymbolSettings={manaSymbolSettings}
onManaSymbolSettingsChange={setManaSymbolSettings}
onAddCardToDeck={(card) => {
addCardToDeck(card);
setSelectedCard(null);
}}
/>
</div>
</div>
</div>
);
}