// components/Trades.jsx const { useMemo: useMemoTr, useState: useStateTr, useEffect: useEffectTr } = React; // ── Helpers ────────────────────────────────────────────────────────────────── function getStickerLabel(id) { const { TEAMS, teamStickers, SPECIAL_STICKERS } = ALBUM_DATA; const spec = SPECIAL_STICKERS.find(s => s.id === id); if (spec) return { num: `FWC ${spec.num}`, name: spec.name, team: 'FIFA / Intro', teamId: 'FIFA' }; for (const t of TEAMS) { const found = (teamStickers[t.id]||[]).find(s => s.id === id); if (found) return { num: `${t.id} ${found.num}`, name: found.name, team: t.name, teamId: t.id }; } return { num: '??', name: id, team: '', teamId: '' }; } // ── Main Component ─────────────────────────────────────────────────────────── function Trades({ stickers, onUpdateSticker }) { const { TEAMS, teamStickers, SPECIAL_STICKERS } = ALBUM_DATA; const [tab, setTab] = useStateTr('suggestions'); // suggestions | offer | want | log // ── Computed analysis ───────────────────────────────────── const analysis = useMemoTr(() => { const offer = [], want = []; SPECIAL_STICKERS.forEach(s => { const q = stickers[s.id]||0; if (q >= 2) offer.push({ ...s, qty:q, extras:q-1, section:'FIFA / Intro', teamId:'FIFA' }); if (q === 0) want.push({ ...s, section:'FIFA / Intro', teamId:'FIFA' }); }); TEAMS.forEach(team => { (teamStickers[team.id]||[]).forEach(s => { const q = stickers[s.id]||0; if (q >= 2) offer.push({ ...s, qty:q, extras:q-1, section:team.name, teamId:team.id }); if (q === 0) want.push({ ...s, section:team.name, teamId:team.id }); }); }); // Build suggestions per team const suggestions = []; TEAMS.forEach(wantTeam => { const missing = (teamStickers[wantTeam.id]||[]).filter(s => (stickers[s.id]||0)===0); if (!missing.length) return; const offerFrom = TEAMS .filter(t => t.id !== wantTeam.id) .map(t => ({ team:t, extras:(teamStickers[t.id]||[]).filter(s=>(stickers[s.id]||0)>=2) })) .filter(x => x.extras.length > 0) .slice(0,3); if (offerFrom.length) suggestions.push({ want:{team:wantTeam, count:missing.length, examples:missing.slice(0,3)}, offer:offerFrom.map(({team,extras})=>({team,count:extras.length})) }); }); return { offer, want, suggestions: suggestions.slice(0,10) }; }, [stickers]); const totalOffer = analysis.offer.reduce((a,s) => a+s.extras, 0); function groupBySection(list) { const g = {}; list.forEach(s => { if (!g[s.section]) g[s.section] = { teamId:s.teamId, items:[] }; g[s.section].items.push(s); }); return g; } return (

Intercambios

{/* Summary */}
{totalOffer}
láminas para ofrecer
{analysis.want.length}
láminas que necesitas
{/* Tabs */}
{[['suggestions','💡 Sugerencias'],['offer','🟡 Mis repetidas'],['want','🔵 Me faltan'],['log','📝 Registrar']].map(([v,l]) => ( ))}
{/* Suggestions */} {tab==='suggestions' && ( analysis.suggestions.length === 0 ? :
{analysis.suggestions.map((sug,i) => (
🔵 Necesitas
{sug.want.team.flag} {sug.want.team.name}
{sug.want.count} lámina{sug.want.count!==1?'s':''}
{sug.want.examples.map(s => `#${String(s.num).padStart(2,'0')} ${s.name}`).join(' · ')}{sug.want.count>3?'…':''}
🟡 Puedes dar
{sug.offer.map(({team,count}) => (
{team.flag} {team.name} ×{count}
))}
))}
)} {/* Offer list */} {tab==='offer' && } {/* Want list */} {tab==='want' && } {/* Trade Log */} {tab==='log' && }
); } // ── Sticker Group List ──────────────────────────────────────────────────────── function StickerGroupList({ groups, type, emptyMsg }) { const entries = Object.entries(groups); if (!entries.length) return ; return (
{entries.map(([section,{flag,items}]) => (
{flag} {section} {items.length} láminas
{items.map(s => ( #{String(s.num||s.id.split('-')[1]).padStart(2,'0')} {s.name} {s.extras>0 && ×{s.extras+1}} ))}
))}
); } // ── Trade Log ───────────────────────────────────────────────────────────────── function TradeLog({ stickers, onUpdateSticker }) { const { TEAMS, teamStickers, SPECIAL_STICKERS } = ALBUM_DATA; const [giveTeam, setGiveTeam] = useStateTr(''); const [giveNum, setGiveNum] = useStateTr(''); const [recvTeam, setRecvTeam] = useStateTr(''); const [recvNum, setRecvNum] = useStateTr(''); const [partner, setPartner] = useStateTr(''); const [logs, setLogs] = useStateTr(() => { try { return JSON.parse(localStorage.getItem('mona26_trade_log')||'[]'); } catch { return []; } }); const [flash, setFlash] = useStateTr(''); function getTeamStickers(teamId) { if (teamId === 'FIFA') return SPECIAL_STICKERS.map(s=>({...s,type:'special'})); return teamStickers[teamId] || []; } // Solo láminas con repetidas (qty >= 2) para el lado "di" const giveStickers = giveTeam ? getTeamStickers(giveTeam).filter(s => (stickers[s.id]||0) >= 2) : []; // Solo láminas que faltan (qty === 0) para el lado "recibí" const recvStickers = recvTeam ? getTeamStickers(recvTeam).filter(s => (stickers[s.id]||0) === 0) : []; // Solo equipos que tienen al menos una lámina faltante const teamsWithMissing = [ { id:'FIFA', name:'FIFA / Intro', flag:'🏆' }, ...TEAMS, ].filter(t => { const sl = t.id === 'FIFA' ? SPECIAL_STICKERS.map(s=>({...s,type:'special'})) : (teamStickers[t.id]||[]); return sl.some(s => (stickers[s.id]||0) === 0); }); // Solo equipos que tienen al menos una repetida const teamsWithDups = [ { id:'FIFA', name:'FIFA / Intro', flag:'🏆' }, ...TEAMS, ].filter(t => { const sl = t.id === 'FIFA' ? SPECIAL_STICKERS.map(s=>({...s,type:'special'})) : (teamStickers[t.id]||[]); return sl.some(s => (stickers[s.id]||0) >= 2); }); const giveSticker = giveTeam ? getTeamStickers(giveTeam).find(s => s.id === giveNum) : null; const recvSticker = recvStickers.find(s => s.id === recvNum); function canRegister() { return giveNum && recvNum && giveNum !== recvNum; } function registerTrade() { if (!canRegister()) return; const giveQty = stickers[giveNum]||0; const recvQty = stickers[recvNum]||0; // Update collection if (giveQty > 0) onUpdateSticker(giveNum, giveQty - 1); onUpdateSticker(recvNum, recvQty + 1); // Log const entry = { id: Date.now(), gave: { id: giveNum, label: getStickerLabel(giveNum) }, received: { id: recvNum, label: getStickerLabel(recvNum) }, partner: partner.trim() || null, date: new Date().toLocaleDateString('es-CO', { day:'numeric', month:'short' }), }; const newLogs = [entry, ...logs].slice(0, 100); setLogs(newLogs); localStorage.setItem('mona26_trade_log', JSON.stringify(newLogs)); setGiveTeam(''); setGiveNum(''); setRecvTeam(''); setRecvNum(''); setPartner(''); setFlash('✅ Intercambio registrado y colección actualizada'); setTimeout(() => setFlash(''), 3000); } function deleteLog(id) { const updated = logs.filter(l => l.id !== id); setLogs(updated); localStorage.setItem('mona26_trade_log', JSON.stringify(updated)); } const sel = { ...tCSS.select }; return (
{/* Form */}

📝 Registrar un intercambio

Al registrar, tu colección se actualiza automáticamente: se quita la lámina que diste y se agrega la que recibiste.

{/* GIVE */}
🟡 Lámina que di
{teamsWithDups.length === 0 ?
No tienes láminas repetidas aún.
: } {giveTeam && ( )} {giveSticker && (
#{String(giveSticker.num).padStart(2,'0')} {giveSticker.name} Tienes ×{stickers[giveNum]||0}
)}
{/* RECEIVE */}
🔵 Lámina que recibí
{recvTeam && recvStickers.length === 0 && (
✅ ¡Equipo completo! No te falta ninguna.
)} {recvTeam && recvStickers.length > 0 && ( )} {recvSticker && (
#{String(recvSticker.num).padStart(2,'0')} {recvSticker.name} Te falta
)}
{/* Partner (optional) */}
setPartner(e.target.value)} />
{flash &&
{flash}
}
{/* Log history */} {logs.length > 0 && (

📋 Historial de intercambios

{logs.map(entry => (
#{entry.gave.label.num} {entry.gave.label.flag} {entry.gave.label.name}
#{entry.received.label.num} {entry.received.label.flag} {entry.received.label.name}
{entry.partner && ( 👤 {entry.partner} )} {entry.date}
))}
)} {logs.length === 0 && }
); } function EmptyTr({ msg }) { return (
🃏

{msg}

); } const tCSS = { page: { padding:'28px 24px', maxWidth:900, margin:'0 auto' }, heading: { fontSize:22, fontWeight:700, color:'var(--text)', marginBottom:20 }, summaryRow: { display:'grid', gridTemplateColumns:'1fr 1fr', gap:14, marginBottom:24 }, sumCard: { background:'var(--surface)', border:'1px solid var(--border)', borderRadius:14, padding:20, textAlign:'center' }, sumLabel: { color:'var(--text-muted)', fontSize:13, marginTop:4 }, tabs: { display:'flex', gap:8, marginBottom:24, flexWrap:'wrap' }, tab: { padding:'8px 16px', background:'var(--surface)', border:'1px solid var(--border)', borderRadius:99, color:'var(--text-muted)', fontSize:13, cursor:'pointer', fontWeight:500 }, tabActive: { background:'var(--surface2)', borderColor:'var(--green)', color:'var(--text)' }, sugCard: { background:'var(--surface)', border:'1px solid var(--border)', borderRadius:14, padding:20 }, sugRow: { display:'flex', alignItems:'flex-start', gap:16 }, sugSide: { flex:1 }, sugLabel: { fontSize:11, fontWeight:700, color:'var(--text-dim)', textTransform:'uppercase', letterSpacing:'0.06em', marginBottom:8 }, arrow: { fontSize:24, color:'var(--text-dimmer)', alignSelf:'center', flexShrink:0 }, exNames: { color:'var(--text-dim)', fontSize:11, marginTop:4, lineHeight:1.5 }, offerItem: { display:'flex', alignItems:'center', gap:8, marginBottom:6 }, offerBadge: { background:'var(--gold-bg)', color:'var(--gold)', fontSize:11, fontWeight:700, borderRadius:99, padding:'2px 8px', border:'1px solid var(--gold-brd)' }, group: { marginBottom:20 }, groupHeader:{ display:'flex', alignItems:'center', gap:10, marginBottom:10, paddingBottom:8, borderBottom:'1px solid var(--border)' }, chipList: { display:'flex', flexWrap:'wrap', gap:6 }, chip: { display:'flex', alignItems:'center', gap:5, borderRadius:99, padding:'5px 10px', border:'1px solid' }, chipOffer: { background:'var(--gold-bg)', borderColor:'var(--gold-brd)', color:'var(--gold)' }, chipWant: { background:'var(--blue-bg)', borderColor:'var(--blue-brd)', color:'var(--blue)' }, chipExtra: { background:'var(--gold-bg)', borderRadius:99, padding:'1px 5px', fontSize:10, fontWeight:700, color:'var(--gold)' }, // Log logCard: { background:'var(--surface)', border:'1px solid var(--border)', borderRadius:16, padding:24, marginBottom:28 }, logTitle: { color:'var(--text)', fontSize:16, fontWeight:700, marginBottom:8 }, logHint: { color:'var(--text-dim)', fontSize:13, lineHeight:1.5, marginBottom:20 }, logGrid: { display:'grid', gridTemplateColumns:'1fr auto 1fr', gap:16, alignItems:'start' }, logCol: { display:'flex', flexDirection:'column' }, colHeader: { fontSize:12, fontWeight:700, textTransform:'uppercase', letterSpacing:'0.06em', marginBottom:10 }, logArrow: { fontSize:28, color:'var(--text-dimmer)', alignSelf:'center', paddingTop:24 }, select: { padding:'10px 12px', background:'var(--input-bg)', border:'1px solid var(--border-md)', borderRadius:8, color:'var(--text)', fontSize:13, outline:'none', width:'100%', cursor:'pointer' }, preview: { marginTop:10, display:'flex', alignItems:'center', gap:8, background:'var(--surface2)', border:'1px solid', borderRadius:8, padding:'10px 12px' }, previewNum: { fontWeight:800, fontSize:16, flexShrink:0 }, previewName:{ color:'var(--text)', fontSize:13, flex:1 }, previewQty: { fontWeight:700, fontSize:13, flexShrink:0 }, partnerLabel:{ display:'block', fontSize:12, fontWeight:600, color:'var(--text-muted)', textTransform:'uppercase', letterSpacing:'0.05em', marginBottom:6 }, partnerInput:{ width:'100%', padding:'10px 12px', background:'var(--input-bg)', border:'1px solid var(--border-md)', borderRadius:8, color:'var(--text)', fontSize:13, outline:'none' }, flash: { marginTop:14, padding:'10px 14px', background:'var(--green-bg)', border:'1px solid var(--green-brd)', borderRadius:8, color:'var(--green)', fontSize:13, fontWeight:600 }, registerBtn:{ marginTop:16, padding:'12px 24px', background:'var(--green)', border:'none', borderRadius:10, color:'#fff', fontSize:14, fontWeight:700, cursor:'pointer', transition:'opacity .2s' }, histTitle: { fontSize:15, fontWeight:700, color:'var(--text-muted)', marginBottom:14, textTransform:'uppercase', letterSpacing:'0.06em' }, logEntry: { display:'flex', alignItems:'center', gap:10, background:'var(--surface)', border:'1px solid var(--border)', borderRadius:12, padding:'12px 16px' }, logEntryMain:{ flex:1, display:'flex', alignItems:'center', gap:10, flexWrap:'wrap' }, logChipGave:{ display:'flex', gap:6, alignItems:'center', background:'var(--gold-bg)', border:'1px solid var(--gold-brd)', borderRadius:99, padding:'4px 10px', color:'var(--gold)', fontSize:12 }, logChipRecv:{ display:'flex', gap:6, alignItems:'center', background:'var(--blue-bg)', border:'1px solid var(--blue-brd)', borderRadius:99, padding:'4px 10px', color:'var(--blue)', fontSize:12 }, logPartner: { color:'var(--text-dim)', fontSize:12 }, logDate: { color:'var(--text-dimmer)', fontSize:11, marginLeft:'auto' }, deleteBtn: { background:'none', border:'none', color:'var(--text-dimmer)', cursor:'pointer', fontSize:14, padding:'4px 6px', borderRadius:6, flexShrink:0 }, }; Object.assign(window, { Trades });