import { useState, useEffect, useMemo } from “react”; import { RadarChart, PolarGrid, PolarAngleAxis, PolarRadiusAxis, Radar, ResponsiveContainer, BarChart, Bar, XAxis, YAxis, Tooltip, Cell } from “recharts”; const PIN = “2026”; const SEC = [ { id:”A”, t:”Stress & Recovery”, st:”Kondisi 7 hari terakhir”, ic:”โšก”, c:”#06b6d4″, items:Array.from({length:15},(_,i)=>i+1), ta:”7 hari terakhir” }, { id:”B”, t:”Burnout Risk”, st:”Risiko kelelahan mental”, ic:”๐Ÿ”ฅ”, c:”#f97316″, items:Array.from({length:12},(_,i)=>i+16), ta:”2โ€“4 minggu terakhir” }, { id:”C”, t:”Coping & Mental Skills”, st:”Ketahanan mental”, ic:”๐Ÿง ”, c:”#8b5cf6″, items:Array.from({length:16},(_,i)=>i+28), ta:”secara umum” }, { id:”D”, t:”Competitive State”, st:”Kesiapan bertanding SAAT INI”, ic:”โš”๏ธ”, c:”#ef4444″, items:Array.from({length:12},(_,i)=>i+44), ta:”saat ini” }, { id:”E”, t:”Motivasi”, st:”Kualitas motivasi”, ic:”๐Ÿ†”, c:”#eab308″, items:Array.from({length:10},(_,i)=>i+56), ta:”secara umum” }, { id:”F”, t:”Team Fit & Coachability”, st:”Kesiapan dalam sistem tim baru”, ic:”๐Ÿค”, c:”#10b981″, items:Array.from({length:14},(_,i)=>i+66), ta:”secara umum” }, { id:”G”, t:”Situational Judgment”, st:”Respons skenario in-game”, ic:”๐ŸŽฎ”, c:”#ec4899″, items:Array.from({length:8},(_,i)=>i+80), ta:”bayangkan situasi ini” }, { id:”H”, t:”Refleksi Diri”, st:”Kejujuran & kesadaran diri”, ic:”๐Ÿชž”, c:”#64748b”, items:Array.from({length:7},(_,i)=>i+88), ta:”” }, ]; const Q = { 1:”Dalam 7 hari terakhir, kualitas tidur saya konsisten baik.”, 2:”Saya bangun dengan perasaan segar dan siap menjalani hari.”, 3:”Energi mental saya stabil dari pagi sampai malam.”, 4:”Saya mudah terdistraksi saat latihan karena pikiran di luar game.”, 5:”Walaupun jadwal padat, saya merasa recovery saya cukup.”, 6:”Saya sering merasa tegang atau gelisah tanpa alasan jelas.”, 7:”Saya mampu menjaga fokus panjang (3+ jam) saat scrim/turnamen.”, 8:”Saya merasa kelelahan menumpuk di minggu ini.”, 9:”Saya punya waktu untuk aktivitas rileks di luar game.”, 10:”Nafsu makan dan pola makan saya teratur minggu ini.”, 11:”Tubuh saya dalam kondisi fisik baik (tidak pegal, sakit kepala, mata berat).”, 12:”Mood saya stabil โ€” tidak mudah marah atau sensitif.”, 13:”Saya merasa overwhelmed dengan tanggung jawab di luar game.”, 14:”Saya tidur minimal 7 jam per malam secara konsisten minggu ini.”, 15:”Secara keseluruhan, saya merasa siap perform di minggu ini.”, 16:”Akhir-akhir ini saya merasa ‘habis’ secara mental setelah latihan.”, 17:”Game ini masih terasa bermakna dan menyenangkan buat saya.”, 18:”Saya merasa performa saya stagnan dan itu menguras semangat.”, 19:”Ketika memikirkan turnamen besar, semangat saya justru naik.”, 20:”Saya tetap bangga dengan proses latihan harian saya.”, 21:”Saya mulai ‘cuek’ dengan hasil latihan/scrim.”, 22:”Usaha saya selama latihan tidak membuahkan hasil sepadan.”, 23:”Saya masih excited menemukan hal baru dalam game.”, 24:”Kadang saya berharap tidak perlu latihan hari ini.”, 25:”Saya merasa kontribusi saya dihargai oleh tim dan pelatih.”, 26:”Saya sering berpikir ‘buat apa semua ini’ saat latihan berat.”, 27:”Kalau bisa memilih ulang, saya tetap pilih jalan atlet esports.”, 28:”Kalau bikin mistake fatal, saya bisa reset dalam 1โ€“2 menit.”, 29:”Di situasi high pressure, keputusan saya biasanya makin jernih.”, 30:”Saya punya rutinitas mental sebelum match (breathing, checklist).”, 31:”Saya bisa tetap koms jelas walau emosi naik.”, 32:”Saya bisa fokus pada next play, bukan kesalahan sebelumnya.”, 33:”Saya nyaman menerima feedback keras tanpa defensif.”, 34:”Saya bisa menjaga konsistensi walau zona/tempo game jelek.”, 35:”Saya cenderung ’tilt’ dan itu mempengaruhi aim/keputusan.”, 36:”Ketika plan A gagal, saya bisa switch ke plan B tanpa panik.”, 37:”Saya bisa mengisolasi match buruk dan tidak bawa ke match berikutnya.”, 38:”Saya punya self-talk positif untuk situasi sulit.”, 39:”Saya tetap perform optimal saat banyak orang menonton.”, 40:”Ketika trailing di klasemen, saya lebih termotivasi bukan cemas.”, 41:”Saya mampu mengenali tanda awal tilt sebelum parah.”, 42:”Setelah mati duluan di match penting, saya langsung switch ke mode support.”, 43:”Saya terbiasa menetapkan target spesifik sebelum sesi latihan.”, 44:”Saat ini saya percaya diri menghadapi tim terbaik.”, 45:”Saat ini ada ketegangan fisik mengganggu (jantung, tangan, napas).”, 46:”Saat ini pikiran negatif tentang performa sering muncul.”, 47:”Saat ini saya siap mengambil keputusan krusial di late game.”, 48:”Saat ini saya terlalu tegang untuk main natural.”, 49:”Saat ini fokus saya stabil.”, 50:”Saat ini saya khawatir tidak tampil sesuai kemampuan.”, 51:”Saat ini saya yakin dengan kemampuan mekanik saya.”, 52:”Saat ini saya merasa tenang dan terkontrol secara emosional.”, 53:”Saat ini saya ragu-ragu dalam mengambil keputusan.”, 54:”Saat ini saya siap secara fisik dan mental untuk bertanding.”, 55:”Saat ini saya percaya bisa memberikan dampak besar untuk tim.”, 56:”Saya ingin Timnas karena ingin berkembang sebagai atlet.”, 57:”Motivasi tetap tinggi meskipun tidak ada spotlight/viral.”, 58:”Jika tidak starter, saya tetap kerja keras dari posisi apapun.”, 59:”Target saya jelas dan saya tahu proses hariannya.”, 60:”Saya berlatih karena menikmati proses โ€” bukan tekanan luar.”, 61:”Saya sering mempersiapkan mental untuk skenario sulit di turnamen.”, 62:”Tanpa hadiah uang, saya tetap bermain dengan intensitas sama.”, 63:”Saya merasa ‘harus’ berlatih karena kewajiban, bukan kemauan.”, 64:”Saya termotivasi peningkatan skill harian, bukan hanya hasil akhir.”, 65:”Kadang saya tidak tahu mengapa masih bertanding.”, 66:”Saya nyaman mengikuti struktur tim walau beda dengan klub.”, 67:”Saya bisa percaya IGL dan tidak ‘ambil alih’ koms.”, 68:”Saya selesaikan konflik lewat diskusi, bukan sindir/diam.”, 69:”Saya bisa jelaskan keputusan in-game secara tenang saat review.”, 70:”Saya bisa bangun chemistry dengan pemain baru dalam < 1 minggu.", 71:"Saya hormati keputusan coach walau tidak setuju.", 72:"Saya bersedia ubah playstyle jika dibutuhkan sistem tim.", 73:"Saya tidak terancam jika pemain lain perform lebih baik.", 74:"Dalam diskusi tim, saya dengarkan dulu sebelum bicara.", 75:"Saya proaktif beri callout bahkan tanpa diminta.", 76:"Saya bisa terima role yang bukan preferred demi tim.", 77:"Saya pentingkan kemenangan tim daripada stats personal.", 78:"Jika rekan struggle, saya support bukan kritik.", 79:"Saya terbuka dikoreksi oleh siapapun termasuk yang lebih muda.", 80:"Hot drop, hanya saya survive โ€” saya tetap tenang ambil keputusan.", 81:"Match 5/6, klasemen bawah โ€” saya tetap main sesuai strategi.", 82:"IGL call rotasi berisiko โ€” saya commit 100% sambil sampaikan concern.", 83:"Mati pertama di match penting โ€” langsung switch ke mode support.", 84:"Final circle 2v4 โ€” tetap percaya menang dan main kalkulasi.", 85:"Ping spike/frame drop โ€” tetap adaptasi tanpa mengeluh.", 86:"Strategi gagal total match 1 โ€” langsung kontribusi adaptasi.", 87:"Bisa bedakan kapan main placement vs agresif sesuai klasemen.", 88:"Saya tidak pernah kesal atau frustrasi saat kalah.", 89:"Saya selalu 100% fokus penuh setiap sesi tanpa terkecuali.", 90:"Saya pernah keputusan buruk karena terbawa emosi.", 91:"Ada kalanya saya merasa malas atau tidak ingin berlatih.", 92:"Untuk pertanyaan ini, pilih angka 4 (Setuju).", 93:"Saya tidak pernah iri dengan pencapaian pemain lain.", 94:"Ada saat-saat saya meragukan kemampuan diri sendiri.", }; const REV = new Set([4,6,8,13,16,18,21,22,24,26,35,45,46,48,50,53,63,65]); const SD = new Set([88,89,93]); const HON = new Set([90,91,94]); const ATT = 92; const CPAIRS = [[28,37],[17,27],[44,51],[33,79],[56,60],[32,42],[66,72],[35,41]]; function sc(n,r){ return !r?3:REV.has(n)?6-r:r; } function avg(a){ const v=a.filter(x=>x!=null); return v.length?v.reduce((s,x)=>s+x,0)/v.length:0; } function calcVal(ans){ let sd=0,hon=0,att=true,con=0; SD.forEach(n=>{if(ans[n]===5)sd++;}); HON.forEach(n=>{if(ans[n]===1)hon++;}); if(ans[ATT]!==4)att=false; CPAIRS.forEach(([a,b])=>{if(ans[a]&&ans[b]&&Math.abs(sc(a,ans[a])-sc(b,ans[b]))>2)con++;}); const t=sd+hon+(att?0:1); if(t<=1&&con<=1&&att) return{sd,hon,att,con,t,lv:"VALID",c:"#10b981",bg:"rgba(16,185,129,0.12)"}; if(t<=2&&con<=2) return{sd,hon,att,con,t,lv:"CAUTION",c:"#f59e0b",bg:"rgba(245,158,11,0.12)"}; return{sd,hon,att,con,t,lv:"SUSPECT",c:"#ef4444",bg:"rgba(239,68,68,0.12)"}; } function calcIdx(ans){ const s=items=>avg(items.map(i=>sc(i,ans[i]))); const rec=s([1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]); const bh=s([16,17,18,19,20,21,22,23,24,25,26,27]); const br=6-bh; const cop=s([28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43]); const cs=s([44,45,46,47,48,49,50,51,52,53,54,55]); const prs=s([28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55]); const mot=s([56,57,58,59,60,61,62,63,64,65]); const tf=s([33,58,66,67,68,69,70,71,72,73,74,75,76,77,78,79]); const sit=s([80,81,82,83,84,85,86,87]); return{rec,br,bh,cop,cs,prs,mot,tf,sit}; } function lv(v){ if(v>=4)return{l:”Excellent”,c:”#10b981″,bg:”rgba(16,185,129,0.12)”}; if(v>=3)return{l:”Good”,c:”#06b6d4″,bg:”rgba(6,182,212,0.12)”}; if(v>=2.5)return{l:”Watchlist”,c:”#f59e0b”,bg:”rgba(245,158,11,0.12)”}; return{l:”High Risk”,c:”#ef4444″,bg:”rgba(239,68,68,0.12)”}; } function blv(r){ if(r<=2)return{l:"Low Risk",c:"#10b981",bg:"rgba(16,185,129,0.12)"}; if(r<=3)return{l:"Moderate",c:"#06b6d4",bg:"rgba(6,182,212,0.12)"}; if(r<=3.5)return{l:"Watchlist",c:"#f59e0b",bg:"rgba(245,158,11,0.12)"}; return{l:"High Risk",c:"#ef4444",bg:"rgba(239,68,68,0.12)"}; } function getFlags(idx){ const f=[]; if(idx.rec<2.5&&idx.br>3.5)f.push({t:”Recovery rendah + Burnout tinggi โ†’ rawan drop bootcamp”,s:”critical”}); if(idx.rec<2.5)f.push({t:"Recovery rendah โ€” risiko fatigue",s:"warning"}); if(idx.br>3.5)f.push({t:”Burnout risk tinggi”,s:”critical”}); if(idx.prs<2.5)f.push({t:"Pressure Performance rendah โ†’ scrim hero stage zero",s:"critical"}); if(idx.cop<2.5)f.push({t:"Coping lemah โ€” rentan tilt",s:"warning"}); if(idx.cs<2.5)f.push({t:"Competitive state rendah",s:"warning"}); if(idx.tf<2.5)f.push({t:"Team Fit rendah โ€” cost sosial tinggi",s:"warning"}); if(idx.mot<2.5)f.push({t:"Motivasi rapuh",s:"warning"}); if(idx.sit<2.5)f.push({t:"Situational judgment lemah",s:"critical"}); return f; } function getStatus(idx){ const c=[idx.rec,idx.prs,idx.mot,idx.tf,idx.sit]; if(c.some(s=>s<2.5)||idx.br>3.5)return{l:”HIGH RISK”,c:”#ef4444″,bg:”rgba(239,68,68,0.15)”}; if(c.some(s=>s<3)||idx.br>3)return{l:”WATCHLIST”,c:”#f59e0b”,bg:”rgba(245,158,11,0.15)”}; if(c.every(s=>s>=4)&&idx.br<=2)return{l:"EXCELLENT",c:"#10b981",bg:"rgba(16,185,129,0.15)"}; return{l:"PASS",c:"#06b6d4",bg:"rgba(6,182,212,0.15)"}; } async function load(){try{const r=await window.storage.get("tn_v3");return r?JSON.parse(r.value):[];}catch{return[];}} async function save(p){try{await window.storage.set("tn_v3",JSON.stringify(p));}catch{}} const bd = (c,bg) => ({display:”inline-block”,padding:”4px 12px”,borderRadius:20,fontSize:12,fontWeight:600,fontFamily:”‘Rajdhani’,sans-serif”,letterSpacing:1,color:c,background:bg,textTransform:”uppercase”}); const css = `@import url(‘https://fonts.googleapis.com/css2?family=Rajdhani:wght@400;500;600;700&family=Outfit:wght@300;400;500;600;700&display=swap’);*{box-sizing:border-box;margin:0;padding:0}body{background:#080c14;color:#e2e8f0;font-family:’Outfit’,sans-serif}::-webkit-scrollbar{width:6px}::-webkit-scrollbar-track{background:#0f1420}::-webkit-scrollbar-thumb{background:#2a3348;border-radius:3px}input:focus{border-color:rgba(245,158,11,0.5)!important}@keyframes fadeUp{from{opacity:0;transform:translateY(12px)}to{opacity:1;transform:translateY(0)}}`; const pg = {minHeight:”100vh”,background:”linear-gradient(180deg,#080c14,#0c1222,#080c14)”,position:”relative”}; const grd = {position:”fixed”,inset:0,backgroundImage:”linear-gradient(rgba(245,158,11,0.03) 1px,transparent 1px),linear-gradient(90deg,rgba(245,158,11,0.03) 1px,transparent 1px)”,backgroundSize:”48px 48px”,pointerEvents:”none”,zIndex:0}; const cnt = w => ({maxWidth:w,margin:”0 auto”,padding:”24px 16px”,position:”relative”,zIndex:1}); const crd = {background:”rgba(15,20,32,0.85)”,border:”1px solid rgba(245,158,11,0.15)”,borderRadius:12,padding:24,backdropFilter:”blur(8px)”,animation:”fadeUp 0.4s ease”}; const btn = {fontFamily:”‘Rajdhani’,sans-serif”,fontWeight:600,fontSize:16,padding:”12px 32px”,borderRadius:8,border:”none”,cursor:”pointer”,letterSpacing:1,textTransform:”uppercase”,transition:”all 0.2s”}; const bP = {background:”linear-gradient(135deg,#f59e0b,#d97706)”,color:”#080c14″}; const bS = {background:”rgba(245,158,11,0.1)”,color:”#f59e0b”,border:”1px solid rgba(245,158,11,0.3)”}; const bD = {background:”rgba(239,68,68,0.15)”,color:”#ef4444″,border:”1px solid rgba(239,68,68,0.3)”}; const bG = {background:”transparent”,color:”#94a3b8″,border:”1px solid rgba(148,163,184,0.2)”}; const inp = {width:”100%”,padding:”12px 16px”,background:”rgba(15,20,32,0.9)”,border:”1px solid rgba(245,158,11,0.2)”,borderRadius:8,color:”#e2e8f0″,fontFamily:”‘Outfit’,sans-serif”,fontSize:15,outline:”none”}; function Landing({onRole}){ return
๐Ÿ‡ฎ๐Ÿ‡ฉ

Timnas PUBG Mobile

Readiness & Performance Assessment

Asian Games 2026 ยท 94 item ยท ยฑ25 menit

{[{r:”player”,i:”๐ŸŽฎ”,t:”PEMAIN”,d:”Isi kuesioner”},{r:”coach”,i:”๐Ÿ“‹”,t:”PELATIH”,d:”Dashboard”}].map(x=>
onRole(x.r)} style={{…crd,width:240,textAlign:”center”,padding:32,cursor:”pointer”}} onMouseEnter={e=>e.currentTarget.style.borderColor=”rgba(245,158,11,0.4)”} onMouseLeave={e=>e.currentTarget.style.borderColor=”rgba(245,158,11,0.15)”}>
{x.i}
{x.t}
{x.d}
)}
; } function Form({onSubmit,onBack}){ const[step,setStep]=useState(0); const[nm,setNm]=useState(“”);const[ig,setIg]=useState(“”);const[tm,setTm]=useState(“”);const[rl,setRl]=useState(“”); const[ans,setAns]=useState({});const[busy,setBusy]=useState(false); const tot=SEC.length+1;const sec=step>0?SEC[step-1]:null; const ok=step===0?nm.trim()&&ig.trim():sec.items.every(i=>ans[i]); const labels=[“Sangat Tidak Setuju”,”Tidak Setuju”,”Netral”,”Setuju”,”Sangat Setuju”]; const go=d=>{setStep(s=>Math.max(0,Math.min(tot-1,s+d)));window.scrollTo({top:0,behavior:”smooth”});}; const submit=async()=>{ setBusy(true); const p={id:Date.now().toString(),nm:nm.trim(),ig:ig.trim(),tm:tm.trim()||”-“,rl:rl.trim()||”-“,ans,ts:new Date().toISOString()}; const ex=await load();ex.push(p);await save(ex);onSubmit(); }; return
{step===0?”Data Pemain”:`${sec.id} โ€” ${sec.items.filter(i=>ans[i]).length}/${sec.items.length}`} ยท {step+1}/{tot}
{step===0 &&

Data Pemain

{[[“Nama Lengkap *”,nm,setNm,”Nama”],[“IGN *”,ig,setIg,”In-Game Name”],[“Tim/Klub”,tm,setTm,”Klub asal”],[“Role”,rl,setRl,”IGL/Fragger/Support/Rusher”]].map(([l,v,s,p])=>
s(e.target.value)} placeholder={p}/>
)}

โš ๏ธ Petunjuk: Jawab sejujur mungkin. Tidak ada jawaban benar/salah. Kuesioner ini punya sistem deteksi konsistensi. Perhatikan anchor waktu tiap seksi.

} {step>0 && sec &&
{sec.ic}

({sec.id}) {sec.t}

{sec.st}

{sec.ta &&
โฑ {sec.ta}
}
{sec.items.map(n=>

{n}.{Q[n]}

{[1,2,3,4,5].map(v=>{const a=ans[n]===v;return ;})}
)}
}
{[“Info”,…SEC.map(s=>s.id)].map((l,i)=>{const d=i(d||c)&&setStep(i)} style={{width:34,height:34,borderRadius:8,display:”flex”,alignItems:”center”,justifyContent:”center”,fontSize:11, fontFamily:”‘Rajdhani’,sans-serif”,fontWeight:600,cursor:d||c?”pointer”:”default”, background:c?”rgba(245,158,11,0.2)”:d?”rgba(16,185,129,0.15)”:”rgba(255,255,255,0.03)”, border:c?”1px solid #f59e0b”:d?”1px solid rgba(16,185,129,0.3)”:”1px solid rgba(255,255,255,0.05)”, color:c?”#f59e0b”:d?”#10b981″:”#475569″}}>{d?”โœ“”:l}
;})}
; } function Thanks({onBack}){ return
โœ…

Assessment Selesai

Terima kasih. Jawaban tersimpan dan hanya ditinjau oleh tim pelatih.

; } function Login({onLogin,onBack}){ const[pin,setPin]=useState(“”);const[err,setErr]=useState(false); const go=()=>{if(pin===PIN)onLogin();else{setErr(true);setTimeout(()=>setErr(false),2000);}}; return
๐Ÿ”’

Coach Access

setPin(e.target.value)} onKeyDown={e=>e.key===”Enter”&&go()} placeholder=”PIN” style={{…inp,textAlign:”center”,fontSize:24,fontFamily:”‘Rajdhani’,sans-serif”,letterSpacing:8,marginBottom:12}}/> {err&&

PIN salah.

}
; } function SBar({label,value,level}){return
{label}
{value.toFixed(2)} {level.l}
;} function Profile({p,onBack}){ const idx=calcIdx(p.ans);const st=getStatus(idx);const fl=getFlags(idx);const vl=calcVal(p.ans); const radar=[{m:”Recovery”,v:idx.rec},{m:”Freshness”,v:idx.bh},{m:”Coping”,v:idx.cop},{m:”Comp.State”,v:idx.cs},{m:”Motivasi”,v:idx.mot},{m:”Team Fit”,v:idx.tf},{m:”Situational”,v:idx.sit}]; const secS=SEC.filter(s=>s.id!==”H”).map(s=>({n:`${s.id}. ${s.t}`,v:avg(s.items.map(i=>sc(i,p.ans[i]))),c:s.c})); return

{p.ig}

{p.nm} ยท {p.tm} ยท {p.rl}

{new Date(p.ts).toLocaleString(“id-ID”)}

Validity: {vl.lv} {st.l}
{vl.lv!==”VALID”&&

{vl.lv===”SUSPECT”?”๐Ÿšจ SUSPECT”:”โš ๏ธ CAUTION”}

SD flags: 1?”#ef4444″:”#e2e8f0″}}>{vl.sd}/3
Honesty: 1?”#ef4444″:”#e2e8f0″}}>{vl.hon}/3
Attention: {vl.att?”PASS”:”FAIL”}
Consistency: 2?”#ef4444″:”#e2e8f0″}}>{vl.con}/{CPAIRS.length}
} {fl.length>0&&

โš ๏ธ RED FLAGS ({fl.length})

{fl.map((f,i)=>
{f.t}
)}
}

Radar Profil

({metric:r.m,value:r.v}))}>

Indeks Skor

Per Seksi

v.toFixed(2)}/> {secS.map((s,i)=>)}

Detail 94 Jawaban

{SEC.map(s=>

{s.ic} {s.id}. {s.t} ({s.items.length})

{s.items.map(n=>{const raw=p.ans[n]||0,scored=sc(n,raw),isR=REV.has(n),isSD=SD.has(n),isH=HON.has(n),isA=n===ATT; const flagged=(isSD&&raw===5)||(isH&&raw===1)||(isA&&raw!==4); return
{n} {Q[n]} {raw} {isR&&โ†’{scored}} {(isSD||isH||isA)&&{isSD?”SD”:isH?”HC”:”AC”}}
;})}
)}
; } function Dash({onLogout,onBack}){ const[players,setPlayers]=useState([]);const[loading,setLoading]=useState(true); const[sel,setSel]=useState(null);const[cmp,setCmp]=useState(false);const[cmpIds,setCmpIds]=useState([]); const[sort,setSort]=useState(“ts”);const[filt,setFilt]=useState(“all”); useEffect(()=>{load().then(p=>{setPlayers(p);setLoading(false);});},[]); const del=async id=>{const u=players.filter(p=>p.id!==id);setPlayers(u);await save(u);}; const clr=async()=>{setPlayers([]);await save([]);}; const tCmp=id=>setCmpIds(p=>p.includes(id)?p.filter(x=>x!==id):p.length<6?[...p,id]:p); const sorted=useMemo(()=>{ let l=[…players]; if(filt!==”all”)l=l.filter(p=>getStatus(calcIdx(p.ans)).l===filt); return l.sort((a,b)=>{ if(sort===”ts”)return new Date(b.ts)-new Date(a.ts); if(sort===”st”){const o={“HIGH RISK”:0,WATCHLIST:1,PASS:2,EXCELLENT:3};return o[getStatus(calcIdx(a.ans)).l]-o[getStatus(calcIdx(b.ans)).l];} if(sort===”vl”){const o={SUSPECT:0,CAUTION:1,VALID:2};return o[calcVal(a.ans).lv]-o[calcVal(b.ans).lv];} return 0;}); },[players,sort,filt]); const cmpP=useMemo(()=>cmpIds.map(id=>players.find(p=>p.id===id)).filter(Boolean),[cmpIds,players]); const stats=useMemo(()=>{ if(!players.length)return null; const s=players.map(p=>getStatus(calcIdx(p.ans)).l);const v=players.map(p=>calcVal(p.ans).lv); return{n:players.length,e:s.filter(x=>x===”EXCELLENT”).length,p:s.filter(x=>x===”PASS”).length,w:s.filter(x=>x===”WATCHLIST”).length,h:s.filter(x=>x===”HIGH RISK”).length,su:v.filter(x=>x===”SUSPECT”).length}; },[players]); if(sel){const p=players.find(x=>x.id===sel);if(p)return setSel(null)}/>;} const mRows=[{k:”rec”,l:”Recovery”,g:i=>i.rec,f:lv},{k:”br”,l:”Burnout Risk”,g:i=>i.br,f:blv},{k:”cop”,l:”Coping”,g:i=>i.cop,f:lv}, {k:”cs”,l:”Comp.State”,g:i=>i.cs,f:lv},{k:”prs”,l:”Pressure”,g:i=>i.prs,f:lv},{k:”mot”,l:”Motivasi”,g:i=>i.mot,f:lv}, {k:”tf”,l:”Team Fit”,g:i=>i.tf,f:lv},{k:”sit”,l:”Situational”,g:i=>i.sit,f:lv}]; return

๐Ÿ“‹ Coach Dashboard

{players.length} pemain ยท 94 item ยท Validity check

{stats&&
{[[“Total”,stats.n,”#e2e8f0″],[“Excellent”,stats.e,”#10b981″],[“Pass”,stats.p,”#06b6d4″],[“Watchlist”,stats.w,”#f59e0b”],[“Risk”,stats.h,”#ef4444″],[“Suspect”,stats.su,”#ef4444″]].map(([l,v,c])=>
{v}
{l}
)}
} {cmp&&cmpP.length>=2&&

โš–๏ธ Compare ({cmpP.length})

{cmpP.map(p=>)} {mRows.map(r=> {cmpP.map(p=>{const i=calcIdx(p.ans),v=r.g(i),l=r.f(v);return;})})} {cmpP.map(p=>{const v=calcVal(p.ans);return;})}{cmpP.map(p=>{const s=getStatus(calcIdx(p.ans));return;})}
Metrik{p.ig}
{r.l}{v.toFixed(2)}
Validity{v.lv}
Status{s.l}
}
Sort: {[[“ts”,”Terbaru”],[“st”,”Status”],[“vl”,”Validity”]].map(([v,l])=> )} Filter: {[[“all”,”Semua”],[“EXCELLENT”,”Exc”],[“PASS”,”Pass”],[“WATCHLIST”,”Watch”],[“HIGH RISK”,”Risk”]].map(([v,l])=> )} {players.length>0&&}
{loading?

Memuat…

:sorted.length===0?

{players.length===0?”Belum ada data.”:”Tidak ada hasil.”}

:
{sorted.map(p=>{const idx=calcIdx(p.ans),st=getStatus(idx),fl=getFlags(idx),vl=calcVal(p.ans),ic=cmpIds.includes(p.id); const metrics=[[“REC”,idx.rec,lv(idx.rec)],[“BRN”,idx.br,blv(idx.br)],[“COP”,idx.cop,lv(idx.cop)],[“CST”,idx.cs,lv(idx.cs)],[“MOT”,idx.mot,lv(idx.mot)],[“FIT”,idx.tf,lv(idx.tf)],[“SIT”,idx.sit,lv(idx.sit)]]; return
cmp?tCmp(p.id):setSel(p.id)}> {cmp&&
{ic&&”โœ“”}
}
{p.ig} {st.l} {vl.lv!==”VALID”&&{vl.lv}}

{p.nm} ยท {p.tm} ยท {p.rl}

{metrics.map(([l,v,le])=>
{l}
{v.toFixed(1)}
)}
{fl.length>0&&f.t).join(“\n”)}>โš ๏ธ{fl.length}} {!cmp&&}
;})}
}
{[[“Excellent โ‰ฅ4.0″,”#10b981”],[“Good โ‰ฅ3.0″,”#06b6d4”],[“Watchlist โ‰ฅ2.5″,”#f59e0b”],[“Risk <2.5","#ef4444"]].map(([l,c])=> {l})}
; } export default function App(){ const[v,setV]=useState(“land”);const[auth,setAuth]=useState(false); return <> {v===”land”&&setV(r===”player”?”form”:”login”)}/>} {v===”form”&&
setV(“thx”)} onBack={()=>setV(“land”)}/>} {v===”thx”&&setV(“land”)}/>} {v===”login”&&!auth&&{setAuth(true);setV(“dash”);}} onBack={()=>setV(“land”)}/>} {v===”dash”&&auth&&{setAuth(false);setV(“land”);}} onBack={()=>setV(“land”)}/>} ; }