// Foto de jugadora con soporte de upload desde la pagina function PPlayerPhoto({ player, size=64, radius="50%", style={}, editable=false }) { const key = `tormentas_photo_${player.id}`; const [src, setSrc] = React.useState(() => localStorage.getItem(key)); const [fileErr, setFileErr] = React.useState(false); const [hover, setHover] = React.useState(false); const inputRef = React.useRef(null); const posColor = posColorsP[player.pos] || T.accent; const handleUpload = e => { const file = e.target.files?.[0]; if (!file) return; const reader = new FileReader(); reader.onload = ev => { localStorage.setItem(key, ev.target.result); setSrc(ev.target.result); setFileErr(false); }; reader.readAsDataURL(file); }; const imgSrc = src || (!fileErr ? `tormentas/photos/${player.id}.jpg` : null); const baseStyle = { width:size, height:size, borderRadius:radius, objectFit:"cover", objectPosition:"top center", flexShrink:0, display:"block" }; if (editable) { return (
setHover(true)} onMouseLeave={() => setHover(false)} onClick={() => inputRef.current?.click()} > {imgSrc ? { setFileErr(true); setSrc(null); }} style={{...baseStyle}} alt={player.name} /> : }
{hover && πŸ“·}
); } if (imgSrc) { return { setFileErr(true); setSrc(null); }} style={{...baseStyle,...style}} alt={player.name} />; } return ; } // ── LISTA DE JUGADORAS ────────────────────────────────────────────────────── function ProJugadoras({ selectedPlayer, onPlayerSelect, onBack }) { const { players } = window.TData; const [search, setSearch] = React.useState(""); const [filterPos, setFilterPos] = React.useState("Todas"); const [viewMode, setViewMode] = React.useState("cards"); const positions = ["Todas","Base","Escolta","Alero","Ala-PΓ­vot","PΓ­vot"]; if (selectedPlayer) return ; const filtered = players.filter(p => { const ms = p.name.toLowerCase().includes(search.toLowerCase()) || String(p.num).includes(search); return ms && (filterPos==="Todas" || p.pos===filterPos); }); const cols = [ {key:"player", label:"Jugadora"}, {key:"pos", label:"PosiciΓ³n"}, {key:"num", label:"#", right:true, color:T.muted}, {key:"pts", label:"Pts", right:true, bold:true, color:T.accent}, {key:"reb", label:"Reb", right:true}, {key:"ast", label:"Ast", right:true}, {key:"stl", label:"Rob", right:true}, {key:"fg", label:"FG%", right:true}, {key:"tl", label:"TL%", right:true}, {key:"min", label:"Min", right:true}, {key:"att", label:"Asist.", right:true}, ]; const rows = filtered.map(p=>({ _player: p, player: (
{p.name}
), pos: , num: p.num, pts: p.avgPts, reb: p.avgReb, ast: p.avgAst, stl: p.avgStl, fg: `${p.fgPct}%`, tl: `${p.ftPct}%`, min: `${p.avgMin}'`, att: ( =85?T.green:p.attendancePct>=70?T.yellow:T.red}}> {p.attendancePct}% ), })); return (
{/* Header */}

Jugadoras

{filtered.length} de {players.length}

{[["cards","β–¦"],["table","≑"]].map(([id,lbl])=>( ))}
setSearch(e.target.value)} placeholder="Buscar..." style={{ padding:"7px 12px 7px 32px",borderRadius:6,border:`1px solid ${T.border}`, fontSize:13,color:T.text,outline:"none",fontFamily:"inherit",width:160,background:"#fff" }} /> βŒ•
{positions.map(pos=>( ))}
{viewMode === "table" ? ( onPlayerSelect(row._player)} />
) : (
{filtered.map(p => { const posColor = posColorsP[p.pos] || T.accent; const devScore = window.TDataExt?.computeDevScore(p.id) || 0; const attOk = p.attendancePct >= 85; const attWarn = p.attendancePct >= 70; return (
onPlayerSelect(p)} style={{ position:"relative", background:`linear-gradient(150deg, ${posColor} 0%, ${posColor}cc 100%)`, borderRadius:16, overflow:"hidden", cursor:"pointer", transition:"transform 0.18s, box-shadow 0.18s", boxShadow:`0 4px 16px ${posColor}40`, minHeight:210, }} onMouseEnter={e=>{e.currentTarget.style.transform="translateY(-4px) scale(1.01)";e.currentTarget.style.boxShadow=`0 12px 32px ${posColor}60`;}} onMouseLeave={e=>{e.currentTarget.style.transform="";e.currentTarget.style.boxShadow=`0 4px 16px ${posColor}40`;}}> {/* NΓΊmero de fondo */}
{p.num}
{/* Top row: foto + dev */}
{devScore}
DEV
{/* Nombre y posiciΓ³n */}
{p.name}
#{p.num} Β· {p.pos}
{/* Stats */}
{[{l:"PTS",v:p.avgPts},{l:"REB",v:p.avgReb},{l:"AST",v:p.avgAst}].map(s=>(
{s.v}
{s.l}
))}
{/* Asistencia */}
Asistencia {p.attendancePct}%
); })}
)}
); } // ── PERFIL DE JUGADORA ────────────────────────────────────────────────────── function ProPlayerProfile({ player:p, onBack }) { const [tab, setTab] = React.useState("stats"); const { games, rawStats } = window.TData; const physData = window.TDataExt?.physicalData?.[p.id]; const gameLabels = games.map(g=>g.rival.slice(0,4)); const gamePts = p.gamesPts.map(x=>x.pts); const attColor = p.attendancePct>=85?T.green:p.attendancePct>=70?T.yellow:T.red; const accentColor = posColorsP[p.pos]||T.accent; const devScore = window.TDataExt?.computeDevScore(p.id) || 0; return (
{/* ── HERO BANNER ────────────────────────────────────── */}
{/* NΓΊmero de fondo gigante */}
{p.num}
{/* Contenido principal */}
{p.pos}

{p.name}

#{p.num} Mano {p.hand || "Derecha"} =85?"#4ade80":p.attendancePct>=70?"#fbbf24":"#f87171", fontWeight:700 }}>{p.attendancePct}% asist.
{/* Dev score badge */}
{devScore}
DEV
{/* Stats strip */}
{[ {l:"PTS", v:p.avgPts, hot:true}, {l:"REB", v:p.avgReb}, {l:"AST", v:p.avgAst}, {l:"FG%", v:`${p.fgPct}%`}, {l:"MIN", v:`${p.avgMin}'`}, ].map((s,i) => (
0 ? "1px solid rgba(255,255,255,0.08)" : "none", }}>
{s.v}
{s.l}
))}
{/* ── DETALLE ────────────────────────────────────────── */}
{/* Columna izquierda */}
{/* Medidas */} Medidas y perfil
{[ {l:"Talla", v: physData ? `${physData.latest.altura} cm` : "β€”"}, {l:"Peso", v: physData ? `${physData.latest.peso} kg` : "β€”"}, {l:"Masa muscular", v: physData ? `${physData.latest.musculo}%` : "β€”"}, {l:"Grasa corporal", v: physData ? `${physData.latest.grasa}%` : "β€”"}, {l:"Mano dominante", v: p.hand || "Derecha"}, ].map(s=>(
{s.l} {s.v}
))}
{/* Asistencia */}
Asistencia {p.attendancePct}%
{/* Logros */} {p.achievements.length > 0 && ( Logros
{p.achievements.map((a,i)=>(
{a}
))}
)}
{/* Columna derecha: tabs */}
{tab==="stats" && (
=45?T.green:T.text} />
)} {tab==="progreso" && (
{[ {l:"Tiro de campo (FG%)", v:p.fgPct, c:accentColor}, {l:"Tiro libre (TL%)", v:p.ftPct, c:T.green}, {l:"Asistencia a entrenos", v:p.attendancePct, c:attColor}, ].map(s=>(
{s.l} {s.v}%
))}
)} {tab==="partidos" && (
EstadΓ­sticas por partido
{ const s=rawStats[g.id]?.[p.id]||[0,0,0,0,0,0,0,0,0,0,0]; const win=g.result==="W"; return { game:`vs ${g.rival}`, res:{win?"V":"D"}, pts:s[0],reb:s[1],ast:s[2], fg:s[7]?`${s[6]}/${s[7]}`:"β€”", tl:s[5]?`${s[4]}/${s[5]}`:"β€”", min:`${s[9]}'`,fouls:s[8] }; })} />
)} {tab==="metas" && ( Objetivos de temporada {p.goals.length===0 && (
Sin metas asignadas aΓΊn.
)}
{p.goals.map((g,i)=>(
{g.done?"βœ“":""}
{g.text}
{g.done && βœ“}
))}
{p.goals.length>0 && (
Progreso {p.goals.filter(g=>g.done).length}/{p.goals.length}
g.done).length} max={p.goals.length} color={T.accent} height={5} />
)}
)} {tab==="asistencia" && (
Asistencia a entrenamientos {p.attendance.filter(a=>a.present).length}/{p.attendance.length} Β· {p.attendancePct}%
{p.attendance.map((a,i)=>(
{a.present?"βœ“":"–"} {a.date.slice(5)}
))}
)}
); } window.ProJugadoras = ProJugadoras;