diff --git a/apps/client/src/botsu/tcg/BotsuTcgPage.tsx b/apps/client/src/botsu/tcg/BotsuTcgPage.tsx index 6204d5a..30462b7 100644 --- a/apps/client/src/botsu/tcg/BotsuTcgPage.tsx +++ b/apps/client/src/botsu/tcg/BotsuTcgPage.tsx @@ -142,6 +142,10 @@ function CollectionView() { const [rarity, setRarity] = useState(undefined); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); + const [selected, setSelected] = useState>(new Set()); + const [burnMode, setBurnMode] = useState(false); + const [burning, setBurning] = useState(false); + const [burnResult, setBurnResult] = useState<{ burned: number; reward: number; balance: number } | null>(null); const load = useCallback(async () => { setLoading(true); @@ -160,6 +164,38 @@ function CollectionView() { load().catch(() => undefined); }, [load]); + const toggleSelect = (instanceId: string) => { + setSelected((prev) => { + const next = new Set(prev); + if (next.has(instanceId)) { + next.delete(instanceId); + } else if (next.size < 5) { + next.add(instanceId); + } + return next; + }); + }; + + const doBurn = async () => { + if (selected.size !== 5) return; + setBurning(true); + setError(null); + try { + const result = await controller.burnCards([...selected]); + setBurnResult({ burned: result.burned.length, reward: result.reward, balance: result.balance }); + setSelected(new Set()); + setBurnMode(false); + await load(); + } catch (e) { + setError((e as Error).message); + } finally { + setBurning(false); + } + }; + + const selectedCount = selected.size; + const canBurn = selectedCount === 5; + return (
@@ -182,7 +218,33 @@ function CollectionView() { {RARITY_LABELS[r]} ))} +
+ {burnMode && ( +
+ Sélectionne 5 cartes communes à brûler ({selectedCount}/5) + +
+ )} + {burnResult && ( +
+ {burnResult.burned} cartes brûlées. Récompense: {burnResult.reward} cookies. Solde: {burnResult.balance} +
+ )} {error &&
{error}
} {loading &&
Chargement…
} {page && !loading && ( @@ -192,7 +254,21 @@ function CollectionView() { ) : (
{page.cards.map((card) => ( - +
toggleSelect(card.instanceId) : undefined} + onKeyDown={ + burnMode && card.rarity === 'common' + ? (e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); toggleSelect(card.instanceId); } } + : undefined + } + style={burnMode && card.rarity !== 'common' ? { opacity: 0.3, pointerEvents: 'none' } : undefined} + > + +
))}
)} diff --git a/apps/client/src/botsu/tcg/tcg.css b/apps/client/src/botsu/tcg/tcg.css index 5427598..0806c5f 100644 --- a/apps/client/src/botsu/tcg/tcg.css +++ b/apps/client/src/botsu/tcg/tcg.css @@ -329,4 +329,50 @@ border-radius: var(--botsu-radius); text-transform: uppercase; font-weight: 700; +} + +/* Burn mode */ +.botsu-tcg-burn-bar { + display: flex; + align-items: center; + justify-content: space-between; + gap: 1rem; + padding: 0.8rem 1rem; + background: color-mix(in srgb, #e55 10%, var(--botsu-color-surface)); + border: 1px solid color-mix(in srgb, #e55 40%, transparent); + border-radius: var(--botsu-radius); + margin-bottom: 0.5rem; +} + +.botsu-tcg-burn-bar span { + font-size: 0.85rem; + color: var(--botsu-color-text-muted); +} + +.botsu-tcg-burn-bar button { + font-size: 0.8rem; + padding: 0.5rem 1.5rem; +} + +.botsu-tcg-burn-result { + padding: 0.6rem 1rem; + background: color-mix(in srgb, #4a9 10%, var(--botsu-color-surface)); + border: 1px solid color-mix(in srgb, #4a9 40%, transparent); + border-radius: var(--botsu-radius); + font-size: 0.85rem; + margin-bottom: 0.5rem; +} + +.botsu-tcg-cards > [data-selected="true"] .botsu-tcg-card { + border-color: #e55; + box-shadow: 0 0 8px color-mix(in srgb, #e55 30%, transparent); +} + +.botsu-tcg-cards > [data-selected="true"]::before { + content: '🔥'; + position: absolute; + margin-left: -0.3rem; + margin-top: -0.3rem; + font-size: 1rem; + z-index: 1; } \ No newline at end of file