add collected page and move the checkbox
This commit is contained in:
@@ -14,6 +14,7 @@ export default function Page() {
|
||||
const [collected, setCollected] = useState<Set<string>>(new Set());
|
||||
const [setId, setSetId] = useState('');
|
||||
const [note, setNote] = useState<string | null>(null);
|
||||
const [tab, setTab] = useState<'uncollected' | 'collected'>('uncollected');
|
||||
|
||||
useEffect(() => {
|
||||
setCollected(loadChecklist());
|
||||
@@ -63,6 +64,15 @@ export default function Page() {
|
||||
return bySet;
|
||||
}, [cards, query, setId]);
|
||||
|
||||
const filteredCollectedCount = useMemo(() => filtered.filter((c) => collected.has(c.id)).length, [filtered, collected]);
|
||||
const filteredUncollectedCount = useMemo(() => filtered.filter((c) => !collected.has(c.id)).length, [filtered, collected]);
|
||||
|
||||
const displayed = useMemo(() => {
|
||||
return tab === 'collected'
|
||||
? filtered.filter((c) => collected.has(c.id))
|
||||
: filtered.filter((c) => !collected.has(c.id));
|
||||
}, [filtered, collected, tab]);
|
||||
|
||||
const setOptions = useMemo(() => {
|
||||
const map = new Map<string, string>();
|
||||
for (const c of cards) {
|
||||
@@ -95,10 +105,40 @@ export default function Page() {
|
||||
{note}
|
||||
</div>
|
||||
) : null}
|
||||
<div className="mb-4 text-sm text-slate-600">
|
||||
Showing {filtered.length} of {cards.length} cards.
|
||||
<div className="mb-3 flex items-center gap-2">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setTab('uncollected')}
|
||||
className={`px-3 py-1.5 text-sm rounded border transition ${
|
||||
tab === 'uncollected'
|
||||
? 'bg-sky-600 text-white border-sky-600'
|
||||
: 'bg-white text-slate-700 border-slate-300 hover:bg-slate-50'
|
||||
}`}
|
||||
aria-pressed={tab === 'uncollected'}
|
||||
>
|
||||
Uncollected <span className="ml-1 inline-block rounded bg-white/20 px-1.5 py-0.5 text-xs border border-white/30">
|
||||
{filteredUncollectedCount}
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setTab('collected')}
|
||||
className={`px-3 py-1.5 text-sm rounded border transition ${
|
||||
tab === 'collected'
|
||||
? 'bg-sky-600 text-white border-sky-600'
|
||||
: 'bg-white text-slate-700 border-slate-300 hover:bg-slate-50'
|
||||
}`}
|
||||
aria-pressed={tab === 'collected'}
|
||||
>
|
||||
Collected <span className="ml-1 inline-block rounded bg-white/20 px-1.5 py-0.5 text-xs border border-white/30">
|
||||
{filteredCollectedCount}
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
<CardGrid cards={filtered} collected={collected} onToggle={toggleCollected} />
|
||||
<div className="mb-4 text-sm text-slate-600">
|
||||
Showing {displayed.length} of {filtered.length} cards in this tab.
|
||||
</div>
|
||||
<CardGrid cards={displayed} collected={collected} onToggle={toggleCollected} />
|
||||
</>
|
||||
)}
|
||||
</main>
|
||||
|
||||
@@ -7,18 +7,6 @@ import SetBadge from './SetBadge';
|
||||
export default function CardItem({ card, checked, onToggle }:{ card: TcgCard; checked: boolean; onToggle: (id:string)=>void }) {
|
||||
return (
|
||||
<div className="group relative overflow-hidden rounded-lg border border-slate-200 bg-white shadow-sm hover:shadow-md transition">
|
||||
<div className="absolute right-2 top-2 z-10">
|
||||
<label className="inline-flex items-center gap-2 text-xs bg-white/90 px-2 py-1 rounded shadow border border-slate-200">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={checked}
|
||||
onChange={() => onToggle(card.id)}
|
||||
className="h-4 w-4 rounded border-slate-300 text-sky-600 focus:ring-sky-500"
|
||||
aria-label={`Mark ${card.name} #${card.number} as collected`}
|
||||
/>
|
||||
<span>Collected</span>
|
||||
</label>
|
||||
</div>
|
||||
<div className="relative aspect-[3/4] w-full bg-slate-100">
|
||||
<Image
|
||||
src={card.images.small}
|
||||
@@ -34,6 +22,18 @@ export default function CardItem({ card, checked, onToggle }:{ card: TcgCard; ch
|
||||
{card.rarity ? <span className="text-[0.7rem] rounded bg-slate-100 px-2 py-0.5 text-slate-600">{card.rarity}</span> : null}
|
||||
</div>
|
||||
<SetBadge set={card.set} />
|
||||
<div className="pt-1">
|
||||
<label className="inline-flex items-center gap-2 text-xs bg-white px-2 py-1 rounded border border-slate-200">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={checked}
|
||||
onChange={() => onToggle(card.id)}
|
||||
className="h-4 w-4 rounded border-slate-300 text-sky-600 focus:ring-sky-500"
|
||||
aria-label={`Mark ${card.name} #${card.number} as collected`}
|
||||
/>
|
||||
<span>Collected</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user