// 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™
{/* 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
?
🏆
:
})
}
{isSpecial ? 'Láminas Especiales' : team?.name}
Click izquierdo = agregar · Click derecho = quitar
{/* 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 (
);
}
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 });