add separate checkboxes for variants
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
import React, { useEffect, useMemo, useState } from 'react';
|
||||
import type { TcgCard } from '@/types/pokemon';
|
||||
import { loadChecklist, saveChecklist } from '@/lib/checklist';
|
||||
import type { ChecklistV2, VariantKey } from '@/lib/checklist';
|
||||
import CardGrid from '@/components/CardGrid';
|
||||
import Header from '@/components/Header';
|
||||
import SetFilter from '@/components/SetFilter';
|
||||
@@ -11,13 +12,13 @@ export default function Page() {
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [query, setQuery] = useState('');
|
||||
const [collected, setCollected] = useState<Set<string>>(new Set());
|
||||
const [checklist, setChecklist] = useState<ChecklistV2>({});
|
||||
const [setId, setSetId] = useState('');
|
||||
const [note, setNote] = useState<string | null>(null);
|
||||
const [tab, setTab] = useState<'uncollected' | 'collected'>('uncollected');
|
||||
|
||||
useEffect(() => {
|
||||
setCollected(loadChecklist());
|
||||
setChecklist(loadChecklist());
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -39,15 +40,34 @@ export default function Page() {
|
||||
load();
|
||||
}, []);
|
||||
|
||||
function toggleCollected(id: string) {
|
||||
setCollected((prev) => {
|
||||
const next = new Set(prev);
|
||||
if (next.has(id)) next.delete(id); else next.add(id);
|
||||
function toggleCollected(id: string, key: VariantKey) {
|
||||
setChecklist((prev) => {
|
||||
const next: ChecklistV2 = { ...prev };
|
||||
const current = { ...(next[id] || {}) };
|
||||
current[key] = !current[key];
|
||||
// Clean up empty records to keep storage tidy
|
||||
const hasAny = Object.values(current).some(Boolean);
|
||||
if (hasAny) next[id] = current; else delete next[id];
|
||||
saveChecklist(next);
|
||||
return next;
|
||||
});
|
||||
}
|
||||
|
||||
function availableVariants(card: TcgCard): VariantKey[] {
|
||||
const list: VariantKey[] = ['base'];
|
||||
const hasHolo = !!(card.variants?.holofoil || (card as any).tcgplayer?.prices?.holofoil);
|
||||
const hasReverse = !!(card.variants?.reverseHolofoil || (card as any).tcgplayer?.prices?.reverseHolofoil);
|
||||
if (hasHolo) list.push('holofoil');
|
||||
if (hasReverse) list.push('reverseHolofoil');
|
||||
return list;
|
||||
}
|
||||
|
||||
function isFullyCollected(card: TcgCard): boolean {
|
||||
const needed = availableVariants(card);
|
||||
const state = checklist[card.id] || {};
|
||||
return needed.every((k) => !!state[k]);
|
||||
}
|
||||
|
||||
const filtered = useMemo(() => {
|
||||
const byQuery = (() => {
|
||||
if (!query.trim()) return cards;
|
||||
@@ -64,14 +84,14 @@ 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 filteredCollectedCount = useMemo(() => filtered.filter((c) => isFullyCollected(c)).length, [filtered, checklist]);
|
||||
const filteredUncollectedCount = useMemo(() => filtered.length - filteredCollectedCount, [filtered, filteredCollectedCount]);
|
||||
|
||||
const displayed = useMemo(() => {
|
||||
return tab === 'collected'
|
||||
? filtered.filter((c) => collected.has(c.id))
|
||||
: filtered.filter((c) => !collected.has(c.id));
|
||||
}, [filtered, collected, tab]);
|
||||
? filtered.filter((c) => isFullyCollected(c))
|
||||
: filtered.filter((c) => !isFullyCollected(c));
|
||||
}, [filtered, checklist, tab]);
|
||||
|
||||
const setOptions = useMemo(() => {
|
||||
const map = new Map<string, string>();
|
||||
@@ -87,7 +107,7 @@ export default function Page() {
|
||||
query={query}
|
||||
onQueryChange={setQuery}
|
||||
total={cards.length}
|
||||
collectedCount={collected.size}
|
||||
collectedCount={useMemo(() => cards.filter((c) => isFullyCollected(c)).length, [cards, checklist])}
|
||||
/>
|
||||
|
||||
<div className="mb-4 flex flex-wrap items-center gap-3">
|
||||
@@ -138,7 +158,7 @@ export default function Page() {
|
||||
<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} />
|
||||
<CardGrid cards={displayed} checklist={checklist} onToggle={toggleCollected} />
|
||||
</>
|
||||
)}
|
||||
</main>
|
||||
|
||||
Reference in New Issue
Block a user