refactor(deck): extract grouped card list panel (Brief 2) (#143)

Move the grouped card list and empty-deck state into DeckDetailCardList;
page header + stats sidebar remain inline for Brief 3.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
varutasu 2026-06-13 01:11:50 -05:00 committed by GitHub
parent bce62c3716
commit 7e0e6bffc1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 93 additions and 91 deletions

View file

@ -0,0 +1,91 @@
/* eslint-disable @next/next/no-img-element -- External card image URLs; next/image migration is out of scope. */
import { ManaCost } from './ManaSymbols';
export default function DeckDetailCardList({ cards, groupedCards }) {
const isEmpty = !cards || cards.length === 0;
return (
<div className="lg:col-span-3">
<div className="glass-panel rounded-2xl p-6">
{isEmpty ? (
<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 style={{ color: 'var(--text-secondary)' }}>
This deck doesn&apos;t have any cards yet
</p>
</div>
) : (
<div className="space-y-6">
{Object.entries(groupedCards)
.sort(([a], [b]) => a.localeCompare(b))
.map(([group, groupCards]) => (
<div key={group}>
<h3
className="text-lg font-semibold mb-3 border-b pb-2"
style={{
color: 'var(--text-primary)',
borderColor: 'var(--border)',
}}
>
{group} ({groupCards.reduce((sum, card) => sum + card.quantity, 0)})
</h3>
<div className="grid grid-cols-1 md:grid-cols-2 gap-3">
{groupCards
.sort((a, b) => a.name.localeCompare(b.name))
.map((card) => (
<div
key={`${card.card_id}-${card.id}`}
className="flex items-center space-x-3 p-3 rounded-xl transition-colors nav-item-hover"
style={{ backgroundColor: 'var(--bg-primary)' }}
>
{card.image_url && (
<img
src={card.image_url}
alt={card.name}
className="w-12 h-16 object-cover rounded"
/>
)}
<div className="flex-1 min-w-0">
<div className="flex items-center justify-between">
<h4
className="font-medium truncate"
style={{ color: 'var(--text-primary)' }}
>
{card.name}
</h4>
<span
className="font-medium ml-2"
style={{ color: 'var(--text-primary)' }}
>
{card.quantity}x
</span>
</div>
<p className="text-sm" style={{ color: 'var(--text-secondary)' }}>
{card.set_name}
</p>
<div
className="flex items-center space-x-2 text-xs"
style={{ color: 'var(--text-secondary)' }}
>
{card.mana_cost && (
<ManaCost cost={card.mana_cost} size="xs" />
)}
{card.rarity && (
<span className="capitalize">{card.rarity}</span>
)}
</div>
</div>
</div>
))}
</div>
</div>
))}
</div>
)}
</div>
</div>
);
}

View file

@ -1,10 +1,9 @@
/* eslint-disable @next/next/no-img-element -- External or generated image URLs; next/image migration is out of scope. */
import { useState, useEffect } from 'react'; import { useState, useEffect } from 'react';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import Link from 'next/link'; import Link from 'next/link';
import Layout from '../../components/Layout'; import Layout from '../../components/Layout';
import DeckDetailCardList from '../../components/DeckDetailCardList';
import DeckDetailStatsSidebar from '../../components/DeckDetailStatsSidebar'; import DeckDetailStatsSidebar from '../../components/DeckDetailStatsSidebar';
import { ManaCost } from '../../components/ManaSymbols';
import { Button } from '../../components/ui'; import { Button } from '../../components/ui';
import { computeDeckStats } from '../../lib/deck-builder-stats.js'; import { computeDeckStats } from '../../lib/deck-builder-stats.js';
import { groupDeckCards } from '../../lib/deck-detail-grouping.js'; import { groupDeckCards } from '../../lib/deck-detail-grouping.js';
@ -164,95 +163,7 @@ export default function DeckDetail() {
onGroupByChange={setGroupBy} onGroupByChange={setGroupBy}
/> />
{/* Card List */} <DeckDetailCardList cards={deck.cards} groupedCards={groupedCards} />
<div className="lg:col-span-3">
<div className="glass-panel rounded-2xl p-6">
{deck.cards && deck.cards.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 style={{ color: 'var(--text-secondary)' }}>
This deck doesn&apos;t have any cards yet
</p>
</div>
) : (
<div className="space-y-6">
{Object.entries(groupedCards)
.sort(([a], [b]) => a.localeCompare(b))
.map(([group, cards]) => (
<div key={group}>
<h3
className="text-lg font-semibold mb-3 border-b pb-2"
style={{
color: 'var(--text-primary)',
borderColor: 'var(--border)',
}}
>
{group} ({cards.reduce((sum, card) => sum + card.quantity, 0)})
</h3>
<div className="grid grid-cols-1 md:grid-cols-2 gap-3">
{cards
.sort((a, b) => a.name.localeCompare(b.name))
.map((card) => (
<div
key={`${card.card_id}-${card.id}`}
className="flex items-center space-x-3 p-3 rounded-xl transition-colors nav-item-hover"
style={{ backgroundColor: 'var(--bg-primary)' }}
>
{card.image_url && (
<img
src={card.image_url}
alt={card.name}
className="w-12 h-16 object-cover rounded"
/>
)}
<div className="flex-1 min-w-0">
<div className="flex items-center justify-between">
<h4
className="font-medium truncate"
style={{ color: 'var(--text-primary)' }}
>
{card.name}
</h4>
<span
className="font-medium ml-2"
style={{ color: 'var(--text-primary)' }}
>
{card.quantity}x
</span>
</div>
<p
className="text-sm"
style={{ color: 'var(--text-secondary)' }}
>
{card.set_name}
</p>
<div
className="flex items-center space-x-2 text-xs"
style={{ color: 'var(--text-secondary)' }}
>
{card.mana_cost && (
<ManaCost cost={card.mana_cost} size="xs" />
)}
{card.rarity && (
<span className="capitalize">{card.rarity}</span>
)}
</div>
</div>
</div>
))}
</div>
</div>
))}
</div>
)}
</div>
</div>
</div> </div>
</div> </div>
</Layout> </Layout>