// components/Album.jsx const { useState: useAlbumState, useMemo: useAlbumMemo } = React; // ── Countries Grid ─────────────────────────────────────────────────────────── function Album({ stickers, onSelectCountry }) { const { TEAMS, CONFS, CONF_NAMES, teamStickers, SPECIAL_STICKERS, flagUrl } = ALBUM_DATA; const [search, setSearch] = useAlbumState(''); const [activeConf, setActiveConf] = useAlbumState('ALL'); const teamStats = useAlbumMemo(() => { const m = {}; TEAMS.forEach(t => { const sl = teamStickers[t.id] || []; const owned = sl.filter(s => (stickers[s.id]||0) >= 1).length; const dups = sl.reduce((a,s) => a + Math.max(0,(stickers[s.id]||0)-1), 0); m[t.id] = { owned, dups, total: sl.length, pct: Math.round(owned/sl.length*100) }; }); return m; }, [stickers]); const specialOwned = SPECIAL_STICKERS.filter(s => (stickers[s.id]||0) >= 1).length; const filtered = TEAMS.filter(t => t.name.toLowerCase().includes(search.toLowerCase()) && (activeConf === 'ALL' || t.conf === activeConf) ); const grouped = CONFS.reduce((acc, c) => { const teams = filtered.filter(t => t.conf === c); if (teams.length) acc[c] = teams; return acc; }, {}); return (

Álbum FIFA World Cup 2026™

setSearch(e.target.value)} />
{['ALL', ...CONFS].map(c => ( ))}
{/* Special section */} {activeConf === 'ALL' && !search && (
⭐ FIFA / Intro
)} {/* Team groups */} {Object.entries(grouped).map(([conf, teams]) => (
{CONF_NAMES[conf] || conf} {teams.length} selecciones
{teams.map(team => { const s = teamStats[team.id]; const done = s.pct === 100; return ( ); })}
))}
); } function TeamCount({ owned, total, pct, done }) { return (
{owned} /{total}
); } // ── Country Detail with Undo/Redo ──────────────────────────────────────────── function CountryDetail({ countryId, stickers, onUpdateSticker, onBack }) { const { TEAMS, teamStickers, SPECIAL_STICKERS, flagUrl } = ALBUM_DATA; const isSpecial = countryId === 'FIFA_SPECIAL'; const team = TEAMS.find(t => t.id === countryId); const stickerList = isSpecial ? SPECIAL_STICKERS.map(s => ({ ...s, type:'special' })) : (teamStickers[countryId] || []); // ── Undo/Redo history ────────────────────────────────────── const [history, setHistory] = useAlbumState([]); // [{id, from, to}] const [histIdx, setHistIdx] = useAlbumState(-1); function applyChange(id, newQty) { const from = stickers[id] || 0; const newHist = [...history.slice(0, histIdx + 1), { id, from, to: newQty }]; setHistory(newHist); setHistIdx(newHist.length - 1); onUpdateSticker(id, newQty); } function undo() { if (histIdx < 0) return; const action = history[histIdx]; onUpdateSticker(action.id, action.from); setHistIdx(histIdx - 1); } function redo() { if (histIdx >= history.length - 1) return; const action = history[histIdx + 1]; onUpdateSticker(action.id, action.to); setHistIdx(histIdx + 1); } function handleClick(s) { applyChange(s.id, (stickers[s.id]||0) + 1); } function handleRightClick(e, s) { e.preventDefault(); const q = stickers[s.id]||0; if (q > 0) applyChange(s.id, q - 1); } const owned = stickerList.filter(s => (stickers[s.id]||0) >= 1).length; const dups = stickerList.reduce((a,s) => a + Math.max(0,(stickers[s.id]||0)-1), 0); const missing = stickerList.filter(s => (stickers[s.id]||0) === 0).length; const pct = Math.round(owned / stickerList.length * 100); const canUndo = histIdx >= 0; const canRedo = histIdx < history.length - 1; return (
{/* Header */}
{/* Undo / Redo */}
{isSpecial ? 🏆 : {team?.name} }

{isSpecial ? 'Láminas Especiales' : team?.name}

Click izquierdo = agregar  ·  Click derecho = quitar

{pct}%
{/* Sticker grid */}
{stickerList.map(s => { const qty = stickers[s.id] || 0; const state = qty === 0 ? 'missing' : qty === 1 ? 'owned' : 'dup'; return ( ); })}
{/* Quick actions */}
); } function MiniStatD({ label, val, color }) { return (
{val}
{label}
); } const aCSS = { page: { padding:'28px 24px', maxWidth:960, margin:'0 auto' }, heading: { fontSize:22, fontWeight:700, color:'var(--text)', marginBottom:20 }, controls: { marginBottom:24, display:'flex', flexDirection:'column', gap:12 }, search: { padding:'10px 14px', background:'var(--surface)', border:'1px solid var(--border-md)', borderRadius:10, color:'var(--text)', fontSize:14, outline:'none' }, confTabs: { display:'flex', gap:8, flexWrap:'wrap' }, confTab: { padding:'6px 14px', background:'var(--surface)', border:'1px solid var(--border)', borderRadius:99, color:'var(--text-muted)', fontSize:12, cursor:'pointer', fontWeight:500 }, confTabActive:{ background:'var(--green)', color:'#fff', borderColor:'var(--green)' }, section: { marginBottom:28 }, secHeader: { display:'flex', alignItems:'center', justifyContent:'space-between', marginBottom:12 }, secLabel: { fontSize:12, fontWeight:700, color:'var(--text-muted)', textTransform:'uppercase', letterSpacing:'0.07em' }, grid: { display:'grid', gridTemplateColumns:'repeat(auto-fill,minmax(220px,1fr))', gap:10 }, teamCard: { display:'flex', alignItems:'center', gap:12, background:'var(--surface)', border:'1px solid var(--border)', borderRadius:12, padding:'13px 14px', cursor:'pointer', textAlign:'left', position:'relative', overflow:'hidden' }, teamDone: { borderColor:'var(--gold-brd)', background:'var(--gold-bg)' }, teamName: { color:'var(--text)', fontSize:13, fontWeight:600, marginBottom:6, whiteSpace:'nowrap', overflow:'hidden', textOverflow:'ellipsis' }, miniBarWrap:{ height:3, background:'var(--border)', borderRadius:99, overflow:'hidden' }, miniBarFill:{ height:'100%', background:'var(--green)', borderRadius:99, transition:'width .4s' }, dupBadge: { position:'absolute', top:6, right:28, background:'var(--gold)', color:'#000', fontSize:10, fontWeight:700, borderRadius:99, padding:'1px 6px' }, }; const dCSS2 = { page: { padding:'20px 24px', maxWidth:960, margin:'0 auto' }, header: { marginBottom:24 }, navRow: { display:'flex', justifyContent:'space-between', alignItems:'center', marginBottom:12 }, backBtn: { background:'none', border:'none', color:'var(--text-muted)', fontSize:14, cursor:'pointer', padding:0 }, undoRow: { display:'flex', gap:8 }, undoBtn: { padding:'6px 14px', background:'var(--surface)', border:'1px solid var(--border)', borderRadius:8, color:'var(--text-muted)', fontSize:12, cursor:'pointer', fontWeight:600, transition:'opacity .2s' }, titleRow: { display:'flex', alignItems:'center', gap:16, marginBottom:16 }, flag: { fontSize:48 }, title: { color:'var(--text)', fontSize:24, fontWeight:800, margin:0 }, hint: { color:'var(--text-dimmer)', fontSize:12, margin:'4px 0 0' }, statsRow: { display:'flex', gap:32, alignItems:'center', marginBottom:10 }, progressBar:{ height:6, background:'var(--border)', borderRadius:99, overflow:'hidden' }, progressFill:{ height:'100%', borderRadius:99, transition:'width .4s' }, grid: { display:'grid', gridTemplateColumns:'repeat(auto-fill,minmax(80px,1fr))', gap:8, marginBottom:24 }, card: { display:'flex', flexDirection:'column', alignItems:'center', justifyContent:'center', padding:'10px 6px', borderRadius:10, border:'1px solid', cursor:'pointer', transition:'all .15s', position:'relative', minHeight:80, gap:4 }, missing: { background:'var(--miss-bg)', borderColor:'var(--miss-brd)', color:'var(--miss-color)' }, owned: { background:'var(--green-bg)', borderColor:'var(--green-brd)', color:'var(--text)' }, dup: { background:'var(--gold-bg)', borderColor:'var(--gold-brd)', color:'var(--text)' }, isoLabel: { fontSize:14, lineHeight:1 }, num: { fontSize:16, fontWeight:800, lineHeight:1 }, sname: { fontSize:9, textAlign:'center', lineHeight:1.3, overflow:'hidden', maxHeight:24, opacity:.7 }, checkMark: { position:'absolute', top:4, left:4, fontSize:10 }, qtyBadge: { position:'absolute', top:4, right:4, background:'var(--gold)', color:'#000', fontSize:9, fontWeight:700, borderRadius:99, padding:'1px 4px' }, actions: { display:'flex', gap:10, flexWrap:'wrap' }, actionGreen:{ padding:'10px 18px', background:'var(--green-bg)', border:'1px solid var(--green-brd)', borderRadius:8, color:'var(--green)', fontSize:13, fontWeight:600, cursor:'pointer' }, actionRed: { padding:'10px 18px', background:'var(--red-bg)', border:'1px solid var(--red-brd)', borderRadius:8, color:'var(--red)', fontSize:13, fontWeight:600, cursor:'pointer' }, }; Object.assign(window, { Album, CountryDetail });