Group set cards by name with expandable rarity rows
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/push/woodpecker Pipeline was successful
This commit is contained in:
+55
-8
@@ -54,11 +54,10 @@ function SetCardRow({ card, zebra }) {
|
||||
|
||||
return (
|
||||
<div style={{
|
||||
display: 'grid', gridTemplateColumns: '1fr 120px 90px',
|
||||
gap: '8px', padding: '8px 16px', alignItems: 'center',
|
||||
background: zebra ? '#1e1e1e' : '#161616', borderBottom: '1px solid #1a1a1a',
|
||||
display: 'grid', gridTemplateColumns: '1fr 90px',
|
||||
gap: '8px', padding: '6px 16px 6px 32px', alignItems: 'center',
|
||||
background: zebra ? '#1a1a1a' : '#141414', borderBottom: '1px solid #1a1a1a',
|
||||
}}>
|
||||
<span style={{ fontSize: '13px', color: '#d8d8d8' }}>{card.name}</span>
|
||||
<span style={{ fontSize: '12px', color: '#777' }}>{card.rarity_name}</span>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: '6px', justifyContent: 'flex-end' }}>
|
||||
<button className="icon-btn" onClick={() => current > 0 && save(current - 1)} disabled={current === 0}>−</button>
|
||||
@@ -69,6 +68,38 @@ function SetCardRow({ card, zebra }) {
|
||||
);
|
||||
}
|
||||
|
||||
function CardGroup({ group, isExpanded, onToggle }) {
|
||||
const { ownedAmounts } = useContext(CardContext);
|
||||
const total = group.printings.reduce((sum, p) => {
|
||||
const key = `${p.set_id ?? ''}-${p.rarity_id}`;
|
||||
return sum + (ownedAmounts[group.id]?.[key] ?? p.amount_owned ?? 0);
|
||||
}, 0);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
onClick={onToggle}
|
||||
className="card-row-header"
|
||||
style={{
|
||||
display: 'grid', gridTemplateColumns: '1fr 90px',
|
||||
gap: '8px', padding: '8px 16px', alignItems: 'center',
|
||||
cursor: 'pointer', borderBottom: '1px solid #1a1a1a',
|
||||
background: isExpanded ? '#1c1c1c' : 'transparent',
|
||||
}}
|
||||
>
|
||||
<span style={{ fontSize: '13px', color: '#d8d8d8' }}>{group.name}</span>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: '8px', justifyContent: 'flex-end' }}>
|
||||
{total > 0 && <span style={{ fontSize: '12px', color: '#aaa' }}>×{total}</span>}
|
||||
<span style={{ fontSize: '9px', color: '#444' }}>{isExpanded ? '▲' : '▼'}</span>
|
||||
</div>
|
||||
</div>
|
||||
{isExpanded && group.printings.map((p, i) => (
|
||||
<SetCardRow key={`${group.id}-${p.rarity_id}`} card={p} zebra={i % 2 === 1} />
|
||||
))}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function SetsPage() {
|
||||
const [sets, setSets] = useState([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
@@ -76,6 +107,7 @@ function SetsPage() {
|
||||
const [setCards, setSetCards] = useState([]);
|
||||
const [cardsLoading, setCardsLoading] = useState(false);
|
||||
const [searchTerm, setSearchTerm] = useState('');
|
||||
const [expandedCardId, setExpandedCardId] = useState(null);
|
||||
const isMobile = useMediaQuery('(max-width: 768px)');
|
||||
|
||||
useEffect(() => {
|
||||
@@ -86,6 +118,7 @@ function SetsPage() {
|
||||
if (selectedSet?.id === set.id) { setSelectedSet(null); setSetCards([]); return; }
|
||||
setSelectedSet(set);
|
||||
setSetCards([]);
|
||||
setExpandedCardId(null);
|
||||
setCardsLoading(true);
|
||||
fetchSetCards(set.id).then(setSetCards).catch(console.error).finally(() => setCardsLoading(false));
|
||||
};
|
||||
@@ -95,6 +128,15 @@ function SetsPage() {
|
||||
return sets.filter(s => fuzzyMatch(s.set_name, searchTerm) || fuzzyMatch(s.set_code, searchTerm));
|
||||
}, [sets, searchTerm]);
|
||||
|
||||
const groupedCards = useMemo(() => {
|
||||
const map = new Map();
|
||||
for (const card of setCards) {
|
||||
if (!map.has(card.id)) map.set(card.id, { id: card.id, name: card.name, printings: [] });
|
||||
map.get(card.id).printings.push(card);
|
||||
}
|
||||
return [...map.values()];
|
||||
}, [setCards]);
|
||||
|
||||
const setDetail = selectedSet && (
|
||||
<div style={{ display: 'flex', flexDirection: 'column', height: '100%' }}>
|
||||
<div style={{ padding: '12px 16px', borderBottom: '1px solid #2a2a2a', flexShrink: 0 }}>
|
||||
@@ -109,15 +151,20 @@ function SetsPage() {
|
||||
: (
|
||||
<div style={{ flex: 1, overflowY: 'auto' }}>
|
||||
<div style={{
|
||||
display: 'grid', gridTemplateColumns: '1fr 120px 90px',
|
||||
display: 'grid', gridTemplateColumns: '1fr 90px',
|
||||
gap: '8px', padding: '5px 16px',
|
||||
fontSize: '10px', color: '#444', textTransform: 'uppercase', letterSpacing: '0.05em',
|
||||
borderBottom: '1px solid #222',
|
||||
}}>
|
||||
<span>Card</span><span>Rarity</span><span style={{ textAlign: 'right' }}>Owned</span>
|
||||
<span>Card</span><span style={{ textAlign: 'right' }}>Owned</span>
|
||||
</div>
|
||||
{setCards.map((card, i) => (
|
||||
<SetCardRow key={`${card.id}-${card.rarity_id}`} card={card} zebra={i % 2 === 1} />
|
||||
{groupedCards.map(group => (
|
||||
<CardGroup
|
||||
key={group.id}
|
||||
group={group}
|
||||
isExpanded={expandedCardId === group.id}
|
||||
onToggle={() => setExpandedCardId(expandedCardId === group.id ? null : group.id)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user