deckhearth/components/DeckBuilderDeckList.js
varutasu 22364de8c9
refactor(design): site-wide sweep — broken Tailwind tokens, rounded corners, SearchBar primitive (#117) (#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 14:06:22 -05:00

143 lines
5.3 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* eslint-disable @next/next/no-img-element -- External card image URLs; next/image migration is out of scope. */
import { ManaCost } from './ManaSymbols';
import { Button } from './ui';
/* 2026-06-04 design-sweep pass: same broken Tailwind tokens
(bg-bg-*, text-text-*, bg-accent-ember, hover:bg-accent-ember-dark)
that the other deck builder files had — none of those classes are
defined in tailwind.config.js, so this list rendered with no
visible card surfaces or hover affordances. Sweep replaces with
inline CSS variables + Button primitive. Rarity badges reuse the
neutral palette from DeckBuilderCardBrowser. */
const rarityBadgeStyle = (rarity) => {
switch (rarity) {
case 'mythic':
return { backgroundColor: 'var(--accent-ember)', color: '#ffffff' };
case 'rare':
return { backgroundColor: 'var(--accent-flame)', color: '#ffffff' };
case 'uncommon':
return { backgroundColor: 'var(--bg-tertiary)', color: 'var(--text-primary)' };
default:
return { backgroundColor: 'var(--bg-tertiary)', color: 'var(--text-secondary)' };
}
};
export default function DeckBuilderDeckList({
deckCards,
sidebarOpen,
onOpenSidebar,
manaSymbolSettings,
onAddCard,
onRemoveCard,
}) {
return (
<div className="flex-1 overflow-y-auto space-y-2">
{deckCards.length === 0 ? (
<div className="text-center py-12">
<div className="text-6xl mb-4">🃏</div>
<h3
className="text-xl font-semibold mb-2"
style={{ color: 'var(--text-primary)' }}
>
Empty Deck
</h3>
<p className="mb-4" style={{ color: 'var(--text-secondary)' }}>
Start building your deck by searching for cards
</p>
{!sidebarOpen && (
<Button variant="primary" onClick={onOpenSidebar}>
Open Card Browser
</Button>
)}
</div>
) : (
deckCards
.sort((a, b) => a.name.localeCompare(b.name))
.map((card) => (
<div
key={`${card.card_id}-${card.id}`}
className="flex items-center justify-between p-4 rounded-xl transition-colors nav-item-hover"
style={{ backgroundColor: 'var(--bg-primary)' }}
>
<div className="flex items-center space-x-4">
{card.image_url && (
<img
src={card.image_url}
alt={card.name}
className="w-14 h-20 object-cover rounded shadow-md"
/>
)}
<div>
<h4
className="font-semibold text-lg"
style={{ color: 'var(--text-primary)' }}
>
{card.name}
</h4>
<p
className="text-sm"
style={{ color: 'var(--text-secondary)' }}
>
{card.set_name}
</p>
<div className="flex items-center space-x-3 mt-1">
{card.mana_cost && (
<div
className="px-2 py-1 rounded"
style={{ backgroundColor: 'var(--bg-secondary)' }}
>
<ManaCost
cost={card.mana_cost}
size="sm"
useSVG={manaSymbolSettings.useSVG}
/>
</div>
)}
{card.rarity && (
<span
className="text-xs px-2 py-1 rounded capitalize"
style={rarityBadgeStyle(card.rarity)}
>
{card.rarity}
</span>
)}
</div>
</div>
</div>
<div className="flex items-center space-x-3">
<span
className="font-bold text-lg"
style={{ color: 'var(--text-primary)' }}
>
{card.quantity}x
</span>
<div className="flex items-center space-x-1">
<button
onClick={() => onRemoveCard(card.card_id, 1)}
className="w-8 h-8 rounded-full transition-colors flex items-center justify-center font-bold text-white"
style={{ backgroundColor: '#dc2626' }}
aria-label="Remove one"
>
</button>
<button
onClick={() => onAddCard(card, 1)}
className="w-8 h-8 rounded-full transition-transform hover:scale-105 active:scale-95 flex items-center justify-center font-bold text-white"
style={{
background:
'linear-gradient(135deg, var(--accent-ember) 0%, var(--accent-flame) 100%)',
boxShadow: 'var(--rim-light-inner), var(--ember-rim-subtle)',
}}
aria-label="Add one"
>
+
</button>
</div>
</div>
</div>
))
)}
</div>
);
}