const { useState, useEffect, useRef, useCallback } = React;

// ============ API helper ============
let TOKEN = localStorage.getItem('siiku_token') || null;
async function api(path, opts = {}) {
  const headers = { 'Content-Type': 'application/json', ...(opts.headers || {}) };
  if (TOKEN) headers.Authorization = 'Bearer ' + TOKEN;
  const res = await fetch('/api' + path, { ...opts, headers, body: opts.body ? JSON.stringify(opts.body) : undefined });
  if (res.status === 401) { logout(); throw new Error('Sesi berakhir'); }
  if (!res.ok) { let e; try { e = await res.json(); } catch { e = {}; } throw new Error(e.error || 'Terjadi kesalahan'); }
  const ct = res.headers.get('content-type') || '';
  return ct.includes('application/json') ? res.json() : res;
}
function logout() { TOKEN = null; localStorage.removeItem('siiku_token'); localStorage.removeItem('siiku_user'); location.reload(); }
async function downloadAuthed(path) {
  const res = await fetch('/api' + path, { headers: TOKEN ? { Authorization: 'Bearer ' + TOKEN } : {} });
  const ct = res.headers.get('content-type') || '';
  if (!res.ok || ct.includes('text/html')) {
    throw new Error('Gagal mengunduh — sesi mungkin berakhir atau server belum versi terbaru. Coba login ulang.');
  }
  const blob = await res.blob();
  const cd = res.headers.get('Content-Disposition') || '';
  const m = cd.match(/filename="(.+?)"/);
  const url = URL.createObjectURL(blob);
  const a = document.createElement('a'); a.href = url; a.download = m ? m[1] : 'backup.json';
  document.body.appendChild(a); a.click(); a.remove(); URL.revokeObjectURL(url);
}
const fmtRp = n => 'Rp ' + Number(n || 0).toLocaleString('id-ID');
const fmtNum = n => Number(n || 0).toLocaleString('id-ID');
const MONTHS = ['Januari','Februari','Maret','April','Mei','Juni','Juli','Agustus','September','Oktober','November','Desember'];
const DOW = ['Minggu','Senin','Selasa','Rabu','Kamis','Jumat','Sabtu'];

// ============ small UI atoms ============
function Btn({ children, onClick, color = 'brand', className = '', type = 'button', disabled }) {
  const colors = {
    brand: 'bg-gradient-to-r from-violet-600 to-pink-600 text-white hover:opacity-90',
    soft: 'bg-violet-100 text-violet-700 hover:bg-violet-200',
    ghost: 'bg-white text-slate-600 border border-slate-200 hover:bg-slate-50',
    danger: 'bg-rose-100 text-rose-700 hover:bg-rose-200',
    green: 'bg-emerald-500 text-white hover:bg-emerald-600',
  };
  return <button type={type} disabled={disabled} onClick={onClick}
    className={`px-4 py-2 rounded-xl font-semibold text-sm transition ${colors[color]} ${disabled?'opacity-50 cursor-not-allowed':''} ${className}`}>{children}</button>;
}
function Field({ label, children }) {
  return <label className="block mb-3"><span className="text-xs font-semibold text-slate-500 uppercase tracking-wide">{label}</span><div className="mt-1">{children}</div></label>;
}
const inputCls = "w-full px-3 py-2 rounded-xl border border-slate-200 focus:border-violet-500 focus:outline-none text-sm";
function Modal({ title, children, onClose, wide }) {
  useEffect(()=>{ const h=e=>{ if(e.key==='Escape') onClose&&onClose(); }; document.addEventListener('keydown',h); return ()=>document.removeEventListener('keydown',h); },[]);
  return ReactDOM.createPortal(
    <div className="fixed inset-0 z-[100] bg-black/50 flex items-center justify-center p-4 overflow-y-auto" onClick={onClose}>
      <div className={`card p-6 w-full ${wide?'max-w-3xl':'max-w-md'} max-h-[90vh] overflow-auto fade-in my-auto`} onClick={e=>e.stopPropagation()}>
        <div className="flex justify-between items-center mb-4"><h3 className="text-lg font-bold grad-text">{title}</h3><button onClick={onClose} className="text-slate-400 hover:text-slate-700 text-xl leading-none">✕</button></div>
        {children}
      </div>
    </div>, document.body);
}
function Toast({ msg, type }) {
  if (!msg) return null;
  return <div className={`fixed bottom-6 right-6 z-[60] px-5 py-3 rounded-xl text-white font-semibold shadow-lg fade-in ${type==='err'?'bg-rose-500':'bg-emerald-500'}`}>{msg}</div>;
}

// ============ Login ============
function Login({ onLogin }) {
  const [u, setU] = useState(''); const [p, setP] = useState(''); const [err, setErr] = useState(''); const [busy, setBusy] = useState(false);
  async function submit(e) {
    e.preventDefault(); setBusy(true); setErr('');
    try {
      const r = await api('/login', { method:'POST', body:{ username:u, password:p } });
      TOKEN = r.token; localStorage.setItem('siiku_token', r.token); localStorage.setItem('siiku_user', JSON.stringify(r.user));
      onLogin(r.user);
    } catch (e) { setErr(e.message); } finally { setBusy(false); }
  }
  return <div className="min-h-screen grad-bg flex items-center justify-center p-4">
    <div className="card p-8 w-full max-w-sm fade-in">
      <div className="text-center mb-6">
        <div className="w-16 h-16 mx-auto rounded-2xl grad-bg flex items-center justify-center text-white text-2xl font-extrabold mb-3">IKU</div>
        <h1 className="text-2xl font-extrabold grad-text">SIIKU</h1>
        <p className="text-slate-400 text-sm mt-1">PT. Insia Karya Utama</p>
        <p className="text-slate-400 text-xs">Media Agency · TikTok Host Dance</p>
      </div>
      <form onSubmit={submit}>
        <Field label="Username"><input className={inputCls} value={u} onChange={e=>setU(e.target.value)} autoFocus /></Field>
        <Field label="Password"><input type="password" className={inputCls} value={p} onChange={e=>setP(e.target.value)} /></Field>
        {err && <p className="text-rose-500 text-sm mb-3">{err}</p>}
        <Btn type="submit" className="w-full justify-center" disabled={busy}>{busy?'Memproses…':'Masuk'}</Btn>
      </form>
      <p className="text-center text-xs text-slate-300 mt-5">© 2026 PT. Insia Karya Utama</p>
    </div>
  </div>;
}

// ============ Period Picker ============
function PeriodPicker({ periods, value, onChange, onCreate, canCreate }) {
  const [adding, setAdding] = useState(false);
  const [y, setY] = useState(2026); const [m, setM] = useState(6);
  return <div className="flex items-center gap-2">
    <select className={inputCls + ' w-auto'} value={value||''} onChange={e=>onChange(e.target.value)}>
      <option value="">— Pilih Bulan —</option>
      {periods.map(p=><option key={p.id} value={p.id}>{p.label}</option>)}
    </select>
    {canCreate && <Btn color="soft" onClick={()=>setAdding(true)}>+ Bulan</Btn>}
    {adding && <Modal title="Tambah Periode Bulan" onClose={()=>setAdding(false)}>
      <Field label="Bulan"><select className={inputCls} value={m} onChange={e=>setM(+e.target.value)}>{MONTHS.map((mn,i)=><option key={i} value={i+1}>{mn}</option>)}</select></Field>
      <Field label="Tahun"><input type="number" className={inputCls} value={y} onChange={e=>setY(+e.target.value)} /></Field>
      <Btn className="w-full justify-center" onClick={async()=>{ await onCreate(y,m); setAdding(false); }}>Buat Periode</Btn>
    </Modal>}
  </div>;
}

// ============ Dashboard ============
function StatusDot({ status }) {
  const c = status==='GREEN'?'bg-emerald-500':status==='YELLOW'?'bg-amber-400':'bg-rose-500';
  return <span className={`inline-block w-2.5 h-2.5 rounded-full ${c}`}></span>;
}
function Dashboard({ periodId, user }) {
  const [data, setData] = useState(null);
  useEffect(()=>{ if(periodId) api('/dashboard?periodId='+periodId).then(setData).catch(()=>{}); }, [periodId]);
  if (!periodId) return <Empty msg="Pilih periode bulan di atas untuk melihat dashboard." />;
  if (!data) return <Loading />;
  const totalDiamond = data.groups.reduce((s,g)=>s+g.groupTotal,0);
  const totalBonus = data.groups.reduce((s,g)=>s+g.bonusTotal,0);
  return <div className="fade-in">
    <div className="grid grid-cols-1 md:grid-cols-4 gap-4 mb-6">
      <StatCard title="Total Diamond" value={fmtNum(totalDiamond)} sub={data.period.label} grad="from-violet-500 to-purple-600" icon="💎" />
      <StatCard title="Total Bonus Harian" value={fmtRp(totalBonus)} sub="Akumulasi bulan ini" grad="from-pink-500 to-rose-600" icon="🎁" />
      <StatCard title="Jumlah Group" value={data.groups.length} sub="Group dance aktif" grad="from-amber-400 to-orange-500" icon="👥" />
      <StatCard title="Talent Tercapai" value={data.groups.reduce((s,g)=>s+g.reachedTarget,0)} sub="Sudah capai target" grad="from-emerald-400 to-teal-500" icon="✅" />
    </div>
    <div className="grid grid-cols-1 lg:grid-cols-2 gap-5">
      {data.groups.map(g=> <GroupCard key={g.id} g={g} />)}
    </div>
  </div>;
}
function StatCard({ title, value, sub, grad, icon }) {
  return <div className={`rounded-2xl p-5 text-white bg-gradient-to-br ${grad} shadow-lg`}>
    <div className="flex justify-between items-start"><span className="text-3xl">{icon}</span></div>
    <div className="text-3xl font-extrabold mt-3">{value}</div>
    <div className="text-sm font-semibold opacity-90">{title}</div>
    <div className="text-xs opacity-75 mt-1">{sub}</div>
  </div>;
}
function GroupCard({ g }) {
  const pct = Math.min(100, Math.round(g.pct*100));
  return <div className="card p-5">
    <div className="flex justify-between items-center mb-3">
      <div className="flex items-center gap-2"><span className="w-3 h-3 rounded-full" style={{background:g.color||'#7C3AED'}}></span><h3 className="font-bold text-slate-800">{g.name}</h3></div>
      <span className="text-xs bg-violet-100 text-violet-700 px-2 py-1 rounded-lg font-semibold">{g.talentCount} talent</span>
    </div>
    <div className="mb-1 flex justify-between text-xs text-slate-500"><span>Target Group</span><span>{fmtNum(g.groupTotal)} / {fmtNum(g.monthlyTarget)} 💎</span></div>
    <div className="h-3 bg-slate-100 rounded-full overflow-hidden mb-3"><div className="h-full bg-gradient-to-r from-violet-500 to-pink-500" style={{width:pct+'%'}}></div></div>
    <div className="grid grid-cols-2 gap-2 text-sm mb-3">
      <div className="bg-slate-50 rounded-xl p-2"><div className="text-xs text-slate-400">Bonus Harian</div><div className="font-bold text-slate-700">{fmtRp(g.bonusTotal)}</div></div>
      <div className="bg-slate-50 rounded-xl p-2"><div className="text-xs text-slate-400">Bonus MC</div><div className="font-bold text-slate-700">{fmtRp(g.mc.total)}</div></div>
    </div>
    <div className="space-y-1">
      {g.talents.map((t,i)=><div key={i} className="flex justify-between items-center text-sm py-1 border-b border-slate-50">
        <span className="flex items-center gap-2"><StatusDot status={t.status} />{t.name}</span>
        <span className="text-slate-500">{fmtNum(t.total)} 💎 · {fmtRp(t.bonus)}</span>
      </div>)}
    </div>
  </div>;
}

// ============ Point Grid (Excel-like) ============
function PointGrid({ periodId, user, groups, toast }) {
  const [groupId, setGroupId] = useState(groups[0]?.id || '');
  const [grid, setGrid] = useState(null);
  const [saving, setSaving] = useState(false);
  const load = useCallback(()=>{ if(periodId && groupId) api(`/points/grid?periodId=${periodId}&groupId=${groupId}`).then(setGrid).catch(e=>toast(e.message,'err')); }, [periodId, groupId]);
  useEffect(load, [load]);
  useEffect(()=>{ if(!groupId && groups[0]) setGroupId(groups[0].id); }, [groups]);

  if (!periodId) return <Empty msg="Pilih periode bulan di atas untuk input point." />;

  async function saveCell(talentId, day, session, value) {
    setSaving(true);
    try {
      await api('/points', { method:'POST', body:{ periodId, groupId, talentId, day, session, diamonds: value===''?null:Number(value) } });
      load();
    } catch(e){ toast(e.message,'err'); } finally { setSaving(false); }
  }
  const days = grid ? grid.daysInMonth : 30;
  const statusBg = s => s==='GREEN'?'bg-emerald-50 text-emerald-700':s==='YELLOW'?'bg-amber-50 text-amber-700':'bg-rose-50 text-rose-700';

  return <div className="fade-in">
    <div className="flex flex-wrap items-center gap-3 mb-4">
      <select className={inputCls+' w-auto'} value={groupId} onChange={e=>setGroupId(e.target.value)}>
        {groups.map(g=><option key={g.id} value={g.id}>{g.name}</option>)}
      </select>
      <div className="flex items-center gap-3 text-xs ml-2">
        <span className="flex items-center gap-1"><span className="w-3 h-3 rounded bg-rose-400"></span>Belum capai</span>
        <span className="flex items-center gap-1"><span className="w-3 h-3 rounded bg-amber-400"></span>Hampir</span>
        <span className="flex items-center gap-1"><span className="w-3 h-3 rounded bg-emerald-400"></span>Tercapai</span>
      </div>
      <div className="ml-auto flex gap-2">
        <Btn color="green" onClick={()=>downloadAuthed(`/export/points.xlsx?periodId=${periodId}&groupId=${groupId}`).catch(e=>toast(e.message,'err'))}>⬇ Export Excel</Btn>
      </div>
    </div>
    {!grid ? <Loading/> : <>
    <div className="card overflow-auto" style={{maxHeight:'62vh'}}>
      <table className="text-xs border-collapse" style={{minWidth:'max-content'}}>
        <thead className="sticky top-0 z-20">
          <tr className="bg-gradient-to-r from-violet-600 to-pink-600 text-white">
            <th className="sticky left-0 z-30 bg-violet-600 px-3 py-2 text-left" style={{minWidth:130}}>NAMA</th>
            <th className="px-2 py-2">SESI</th>
            {Array.from({length:days},(_,i)=>i+1).map(d=><th key={d} className="px-1 py-2 w-10">{d}</th>)}
            <th className="px-2 py-2 bg-violet-700">TOTAL</th>
            <th className="px-2 py-2 bg-violet-700">TARGET</th>
            <th className="px-2 py-2 bg-violet-700">SISA</th>
          </tr>
        </thead>
        <tbody>
          {grid.rows.map(r=> [1,2].map(session=>
            <tr key={r.talentId+'-'+session} className="border-b border-slate-100 hover:bg-violet-50/40">
              {session===1 && <td rowSpan={2} className="sticky left-0 z-10 bg-white px-3 py-1 font-semibold text-slate-700 border-r border-slate-100" style={{minWidth:130}}>
                <div>{r.name}</div><div className="text-[10px] text-slate-400">{r.stageName}</div>
              </td>}
              <td className="text-center font-bold text-slate-400 bg-slate-50">{session}</td>
              {Array.from({length:days},(_,i)=>i+1).map(d=>{
                const val = (r.byDay[d] && r.byDay[d][session]) || '';
                return <td key={d} className="p-0 border-r border-slate-50">
                  <input className="cell w-10 text-center py-1 text-[11px] bg-transparent" defaultValue={val}
                    onBlur={e=>{ if(String(e.target.value)!==String(val)) saveCell(r.talentId,d,session,e.target.value); }}
                    onKeyDown={e=>{ if(e.key==='Enter') e.target.blur(); }} />
                </td>;
              })}
              {session===1 && <>
                <td rowSpan={2} className={`text-center font-bold px-2 ${statusBg(r.status)}`}>{fmtNum(r.total)}</td>
                <td rowSpan={2} className="text-center text-slate-500 px-2">{fmtNum(r.target)}</td>
                <td rowSpan={2} className={`text-center font-semibold px-2 ${r.sisa>0?'text-rose-500':'text-emerald-600'}`}>{r.sisa>0?fmtNum(r.sisa):'✓ Capai'}</td>
              </>}
            </tr>
          ))}
        </tbody>
      </table>
    </div>
    {/* daily bonus + MC summary */}
    <div className="grid grid-cols-1 md:grid-cols-3 gap-4 mt-4">
      <div className="card p-4">
        <h4 className="font-bold text-slate-700 mb-2">🎁 Bonus Harian Talent</h4>
        {grid.rows.map(r=> r.bonusTotal>0 && <div key={r.talentId} className="flex justify-between text-sm py-1 border-b border-slate-50"><span>{r.name}</span><span className="font-semibold text-emerald-600">{fmtRp(r.bonusTotal)}</span></div>)}
        {grid.rows.every(r=>!r.bonusTotal) && <p className="text-sm text-slate-400">Belum ada bonus.</p>}
      </div>
      <div className="card p-4">
        <h4 className="font-bold text-slate-700 mb-2">🎤 Bonus MC / Operator</h4>
        <Row k="Total diamond group" v={fmtNum(grid.mc.monthTotal)+' 💎'} />
        <Row k="Hari capai target harian" v={grid.mc.daysHit.length+' hari'} />
        <Row k="Bonus harian MC" v={fmtRp(grid.mc.dailyBonusTotal)} />
        <Row k="Bonus bulanan MC" v={fmtRp(grid.mc.monthlyBonus)} hi />
        <Row k="Total bonus MC" v={fmtRp(grid.mc.total)} hi />
      </div>
      <div className="card p-4">
        <h4 className="font-bold text-slate-700 mb-2">ℹ️ Info Group</h4>
        <Row k="Hari kerja efektif" v={grid.effectiveDays+' hari'} />
        <Row k="Target group bulanan" v={fmtNum(grid.group.monthlyDiamondTarget)+' 💎'} />
        <Row k="Aturan bonus" v={`${fmtNum(grid.group.dailyBonus.threshold)} 💎 → ${fmtRp(grid.group.dailyBonus.reward)} / ${grid.group.dailyBonus.basis==='DAY'?'hari':'sesi'}`} />
        {saving && <p className="text-xs text-violet-500 mt-2">Menyimpan…</p>}
      </div>
    </div>
    </>}
  </div>;
}
function Row({ k, v, hi }) { return <div className={`flex justify-between text-sm py-1 border-b border-slate-50 ${hi?'font-bold text-violet-700':''}`}><span className="text-slate-500">{k}</span><span>{v}</span></div>; }

// ============ Groups management ============
function Groups({ toast }) {
  const [list, setList] = useState([]); const [edit, setEdit] = useState(null);
  const load=()=>api('/groups').then(setList);
  useEffect(()=>{ load(); },[]);
  const blank = { name:'', code:'', color:'#7C3AED', liburWeekday:0, monthlyDiamondTarget:0, defaultTalentMonthlyTarget:0, dailyBonus:{threshold:10000,reward:100000,basis:'SESSION'}, mc:{dailyTarget:0,dailyBonus:0,monthlyTarget:0,monthlyBonus:0} };
  return <div className="fade-in">
    <div className="flex justify-between mb-4"><h2 className="text-xl font-bold text-slate-800">Group Dance</h2><Btn onClick={()=>setEdit(blank)}>+ Group</Btn></div>
    <div className="grid md:grid-cols-2 lg:grid-cols-3 gap-4">
      {list.map(g=><div key={g.id} className="card p-4">
        <div className="flex justify-between"><div className="flex items-center gap-2"><span className="w-3 h-3 rounded-full" style={{background:g.color}}></span><b>{g.name}</b></div><button className="text-violet-500 text-sm" onClick={()=>setEdit(g)}>Edit</button></div>
        <div className="text-xs text-slate-500 mt-2 space-y-1">
          <div>Libur: {g.liburWeekday!=null?DOW[g.liburWeekday]:'—'}</div>
          <div>Target group: {fmtNum(g.monthlyDiamondTarget)} 💎</div>
          <div>Target/talent: {fmtNum(g.defaultTalentMonthlyTarget)} 💎</div>
          <div>Bonus: {fmtNum(g.dailyBonus?.threshold)}💎 → {fmtRp(g.dailyBonus?.reward)}/{g.dailyBonus?.basis==='DAY'?'hari':'sesi'}</div>
          <div>MC harian: {fmtRp(g.mc?.dailyBonus)} · bulanan: {fmtRp(g.mc?.monthlyBonus)}</div>
        </div>
      </div>)}
    </div>
    {edit && <GroupModal g={edit} onClose={()=>setEdit(null)} onSaved={()=>{setEdit(null);load();toast('Group disimpan');}} toast={toast} />}
  </div>;
}
function GroupModal({ g, onClose, onSaved, toast }) {
  const [f, setF] = useState(JSON.parse(JSON.stringify(g)));
  const set=(k,v)=>setF(p=>({...p,[k]:v}));
  const setN=(grp,k,v)=>setF(p=>({...p,[grp]:{...p[grp],[k]:v}}));
  async function save(){ try{ if(f.id) await api('/groups/'+f.id,{method:'PUT',body:f}); else await api('/groups',{method:'POST',body:f}); onSaved(); }catch(e){toast(e.message,'err');} }
  return <Modal title={f.id?'Edit Group':'Group Baru'} wide onClose={onClose}>
    <div className="grid grid-cols-2 gap-3">
      <Field label="Nama Group"><input className={inputCls} value={f.name} onChange={e=>set('name',e.target.value)} /></Field>
      <Field label="Kode"><input className={inputCls} value={f.code} onChange={e=>set('code',e.target.value)} /></Field>
      <Field label="Warna"><input type="color" className="w-full h-10 rounded-xl" value={f.color||'#7C3AED'} onChange={e=>set('color',e.target.value)} /></Field>
      <Field label="Hari Libur"><select className={inputCls} value={f.liburWeekday??''} onChange={e=>set('liburWeekday',e.target.value===''?null:+e.target.value)}><option value="">— tidak ada —</option>{DOW.map((d,i)=><option key={i} value={i}>{d}</option>)}</select></Field>
      <Field label="Target Diamond Group / Bulan"><input type="number" className={inputCls} value={f.monthlyDiamondTarget} onChange={e=>set('monthlyDiamondTarget',+e.target.value)} /></Field>
      <Field label="Target Diamond / Talent / Bulan"><input type="number" className={inputCls} value={f.defaultTalentMonthlyTarget} onChange={e=>set('defaultTalentMonthlyTarget',+e.target.value)} /></Field>
      <Field label="Bonus Capai Target / Talent (Rp)"><input type="number" className={inputCls} value={f.talentTargetBonus||0} onChange={e=>set('talentTargetBonus',+e.target.value)} /></Field>
    </div>
    <div className="bg-violet-50 rounded-xl p-3 my-2">
      <p className="text-xs font-bold text-violet-700 mb-2">BONUS DIAMOND HARIAN</p>
      <div className="grid grid-cols-3 gap-3">
        <Field label="Per (diamond)"><input type="number" className={inputCls} value={f.dailyBonus.threshold} onChange={e=>setN('dailyBonus','threshold',+e.target.value)} /></Field>
        <Field label="Dapat (Rp)"><input type="number" className={inputCls} value={f.dailyBonus.reward} onChange={e=>setN('dailyBonus','reward',+e.target.value)} /></Field>
        <Field label="Basis"><select className={inputCls} value={f.dailyBonus.basis} onChange={e=>setN('dailyBonus','basis',e.target.value)}><option value="SESSION">Per Sesi</option><option value="DAY">Per Hari (2 sesi)</option></select></Field>
      </div>
    </div>
    <div className="bg-pink-50 rounded-xl p-3 my-2">
      <p className="text-xs font-bold text-pink-700 mb-2">TARGET & BONUS MC / OPERATOR</p>
      <div className="grid grid-cols-2 gap-3">
        <Field label="Target Harian (💎)"><input type="number" className={inputCls} value={f.mc.dailyTarget} onChange={e=>setN('mc','dailyTarget',+e.target.value)} /></Field>
        <Field label="Bonus Harian (Rp)"><input type="number" className={inputCls} value={f.mc.dailyBonus} onChange={e=>setN('mc','dailyBonus',+e.target.value)} /></Field>
        <Field label="Target Bulanan (💎)"><input type="number" className={inputCls} value={f.mc.monthlyTarget} onChange={e=>setN('mc','monthlyTarget',+e.target.value)} /></Field>
        <Field label="Bonus Bulanan (Rp)"><input type="number" className={inputCls} value={f.mc.monthlyBonus} onChange={e=>setN('mc','monthlyBonus',+e.target.value)} /></Field>
      </div>
    </div>
    <Btn className="w-full justify-center" onClick={save}>Simpan Group</Btn>
  </Modal>;
}

// ============ Talents & Staff management ============
function Talents({ toast, groups }) {
  const [list, setList] = useState([]); const [edit, setEdit] = useState(null);
  const [gf, setGf] = useState(''); const [tf, setTf] = useState('');
  const load=()=>api('/talents').then(setList);
  useEffect(()=>{ load(); },[]);
  const gname = id => groups.find(g=>g.id===id)?.name || '—';
  const blankTalent = { name:'', stageName:'', groupId:groups[0]?.id||'', type:'TALENT', jobDesk:'Talent Dancer', baseSalary:4500000, monthlyTarget:'', payMode:'FIXED', active:true, dailyBonus:null, payroll:{} };
  const blankStaff = { name:'', stageName:'', groupId:null, type:'STAFF', jobDesk:'', baseSalary:4000000, monthlyTarget:'', payMode:'FIXED', active:true, dailyBonus:null, payroll:{} };
  const shown = list.filter(t => (!tf || (t.type||'TALENT')===tf) && (!gf || t.groupId===gf));
  return <div className="fade-in">
    <div className="flex flex-wrap justify-between mb-4 items-center gap-2">
      <h2 className="text-xl font-bold text-slate-800">Talent & Karyawan</h2>
      <div className="flex flex-wrap gap-2">
        <select className={inputCls+' w-auto'} value={tf} onChange={e=>setTf(e.target.value)}><option value="">Semua tipe</option><option value="TALENT">Talent</option><option value="STAFF">Manajemen/Staff</option></select>
        <select className={inputCls+' w-auto'} value={gf} onChange={e=>setGf(e.target.value)}><option value="">Semua group</option>{groups.map(g=><option key={g.id} value={g.id}>{g.name}</option>)}</select>
        <Btn onClick={()=>setEdit(blankTalent)}>+ Talent</Btn>
        <Btn color="soft" onClick={()=>setEdit(blankStaff)}>+ Karyawan</Btn>
      </div>
    </div>
    <div className="card overflow-auto">
      <table className="w-full text-sm">
        <thead className="bg-slate-50 text-slate-500 text-xs uppercase"><tr>
          <th className="text-left px-4 py-3">Nama</th><th className="text-left px-4 py-3">Tipe</th><th className="text-left px-4 py-3">Group / Dept</th><th className="text-left px-4 py-3">Job</th><th className="text-right px-4 py-3">Gaji Pokok</th><th className="text-right px-4 py-3">Target 💎</th><th className="px-4 py-3"></th>
        </tr></thead>
        <tbody>
          {shown.map(t=><tr key={t.id} className="border-t border-slate-50 hover:bg-violet-50/40">
            <td className="px-4 py-3 font-semibold">{t.name} <span className="text-slate-400 text-xs">{t.stageName}</span></td>
            <td className="px-4 py-3"><span className={`text-[10px] px-2 py-0.5 rounded-full ${t.type==='STAFF'?'bg-pink-100 text-pink-700':'bg-violet-100 text-violet-700'}`}>{t.type==='STAFF'?'Manajemen':'Talent'}</span></td>
            <td className="px-4 py-3">{t.type==='STAFF'?'Manajemen':gname(t.groupId)}</td>
            <td className="px-4 py-3 text-slate-500">{t.jobDesk} {t.payMode==='REVSHARE' && <span className="text-[10px] bg-amber-100 text-amber-700 px-1 rounded">Revshare</span>}</td>
            <td className="px-4 py-3 text-right">{fmtRp(t.baseSalary)}</td>
            <td className="px-4 py-3 text-right">{t.type==='STAFF'?'—':(t.monthlyTarget?fmtNum(t.monthlyTarget):'(default)')}</td>
            <td className="px-4 py-3 text-right"><button className="text-violet-500" onClick={()=>setEdit(t)}>Edit</button></td>
          </tr>)}
        </tbody>
      </table>
    </div>
    {edit && <TalentModal t={edit} groups={groups} onClose={()=>setEdit(null)} onSaved={()=>{setEdit(null);load();toast('Data disimpan');}} toast={toast} />}
  </div>;
}
function TalentModal({ t, groups, onClose, onSaved, toast }) {
  const [f, setF] = useState(JSON.parse(JSON.stringify({ payroll:{}, ...t })));
  const set=(k,v)=>setF(p=>({...p,[k]:v}));
  const setPay=(k,v)=>setF(p=>({...p,payroll:{...(p.payroll||{}),[k]:+v}}));
  const [customBonus, setCustomBonus] = useState(!!t.dailyBonus);
  const isStaff = f.type==='STAFF';
  async function save(){ try{ const body={...f}; if(isStaff||!customBonus) body.dailyBonus=null; if(f.id) await api('/talents/'+f.id,{method:'PUT',body}); else await api('/talents',{method:'POST',body}); onSaved(); }catch(e){toast(e.message,'err');} }
  async function del(){ if(confirm('Hapus data ini?')){ await api('/talents/'+f.id,{method:'DELETE'}); onSaved(); } }
  return <Modal title={f.id?'Edit Data':(isStaff?'Karyawan Baru':'Talent Baru')} wide onClose={onClose}>
    <Field label="Tipe"><select className={inputCls} value={f.type} onChange={e=>set('type',e.target.value)}><option value="TALENT">Talent Dancer</option><option value="STAFF">Manajemen / Staff</option></select></Field>
    <div className="grid grid-cols-2 gap-3">
      <Field label="Nama"><input className={inputCls} value={f.name} onChange={e=>set('name',e.target.value)} /></Field>
      {!isStaff && <Field label="Nama Panggung"><input className={inputCls} value={f.stageName} onChange={e=>set('stageName',e.target.value)} /></Field>}
      {!isStaff && <Field label="Group"><select className={inputCls} value={f.groupId||''} onChange={e=>set('groupId',e.target.value)}>{groups.map(g=><option key={g.id} value={g.id}>{g.name}</option>)}</select></Field>}
      <Field label="Job Desk"><input className={inputCls} value={f.jobDesk} onChange={e=>set('jobDesk',e.target.value)} placeholder={isStaff?'mis. Finance & Accounting':'Talent Dancer'} /></Field>
      <Field label="Gaji Pokok / Bulan"><input type="number" className={inputCls} value={f.baseSalary} onChange={e=>set('baseSalary',+e.target.value)} /></Field>
      {!isStaff && <Field label="Target Diamond / Bulan"><input type="number" className={inputCls} placeholder="kosong = default group" value={f.monthlyTarget||''} onChange={e=>set('monthlyTarget',e.target.value)} /></Field>}
      {!isStaff && <Field label="Bonus Capai Target (Rp)"><input type="number" className={inputCls} placeholder="kosong = default group" value={f.monthlyTargetBonus||''} onChange={e=>set('monthlyTargetBonus',e.target.value===''?'':+e.target.value)} /></Field>}
      {!isStaff && <Field label="Mode Gaji"><select className={inputCls} value={f.payMode} onChange={e=>set('payMode',e.target.value)}><option value="FIXED">Gaji Tetap (per hari)</option><option value="REVSHARE">Bagi Hasil Diamond</option></select></Field>}
    </div>
    <div className="bg-slate-50 rounded-xl p-3 my-2">
      <p className="text-xs font-bold text-slate-600 mb-2">TUNJANGAN TETAP (masuk total gaji bulanan)</p>
      <div className="grid grid-cols-4 gap-3">
        <Field label="Insentif"><input type="number" className={inputCls} value={f.payroll?.insentif||0} onChange={e=>setPay('insentif',e.target.value)} /></Field>
        <Field label="Makan"><input type="number" className={inputCls} value={f.payroll?.makan||0} onChange={e=>setPay('makan',e.target.value)} /></Field>
        <Field label="Transport"><input type="number" className={inputCls} value={f.payroll?.transport||0} onChange={e=>setPay('transport',e.target.value)} /></Field>
        <Field label="Lainnya"><input type="number" className={inputCls} value={f.payroll?.tunjanganLain||0} onChange={e=>setPay('tunjanganLain',e.target.value)} /></Field>
      </div>
    </div>
    {!isStaff && <>
    <label className="flex items-center gap-2 my-2 text-sm"><input type="checkbox" checked={customBonus} onChange={e=>{setCustomBonus(e.target.checked); if(e.target.checked&&!f.dailyBonus) set('dailyBonus',{threshold:10000,reward:100000,basis:'SESSION'});}} /> Bonus harian khusus talent ini (override group)</label>
    {customBonus && f.dailyBonus && <div className="grid grid-cols-3 gap-3 bg-violet-50 rounded-xl p-3">
      <Field label="Per 💎"><input type="number" className={inputCls} value={f.dailyBonus.threshold} onChange={e=>set('dailyBonus',{...f.dailyBonus,threshold:+e.target.value})} /></Field>
      <Field label="Dapat Rp"><input type="number" className={inputCls} value={f.dailyBonus.reward} onChange={e=>set('dailyBonus',{...f.dailyBonus,reward:+e.target.value})} /></Field>
      <Field label="Basis"><select className={inputCls} value={f.dailyBonus.basis} onChange={e=>set('dailyBonus',{...f.dailyBonus,basis:e.target.value})}><option value="SESSION">Per Sesi</option><option value="DAY">Per Hari</option></select></Field>
    </div>}
    </>}
    <div className="flex gap-2 mt-4">
      <Btn className="flex-1 justify-center" onClick={save}>Simpan</Btn>
      {f.id && <Btn color="danger" onClick={del}>Hapus</Btn>}
    </div>
  </Modal>;
}

// ============ Payroll ============
function Payroll({ periodId, toast, groups }) {
  const [data, setData] = useState(null); const [gf, setGf] = useState('');
  useEffect(()=>{ if(periodId) api(`/payroll?periodId=${periodId}${gf?'&groupId='+gf:''}`).then(setData).catch(e=>toast(e.message,'err')); }, [periodId,gf]);
  if(!periodId) return <Empty msg="Pilih periode bulan untuk payroll." />;
  if(!data) return <Loading/>;
  const total = data.lines.reduce((s,l)=>s+l.transfer,0);
  return <div className="fade-in">
    <div className="flex flex-wrap justify-between items-center mb-4 gap-2">
      <div><h2 className="text-xl font-bold text-slate-800">Payroll — {data.period.label}</h2><p className="text-xs text-slate-400">Bonus tidak termasuk di gaji — lihat menu Bonus.</p></div>
      <div className="flex gap-2">
        <select className={inputCls+' w-auto'} value={gf} onChange={e=>setGf(e.target.value)}><option value="">Semua group</option>{groups.map(g=><option key={g.id} value={g.id}>{g.name}</option>)}</select>
        <Btn color="green" onClick={()=>downloadAuthed(`/export/payroll.xlsx?periodId=${periodId}${gf?'&groupId='+gf:''}`).catch(e=>toast(e.message,'err'))}>⬇ Excel</Btn>
        <Btn color="soft" onClick={()=>downloadAuthed(`/export/payroll.pdf?periodId=${periodId}${gf?'&groupId='+gf:''}`).catch(e=>toast(e.message,'err'))}>⬇ PDF</Btn>
      </div>
    </div>
    <div className="card overflow-auto">
      <table className="text-xs w-full" style={{minWidth:900}}>
        <thead className="bg-gradient-to-r from-violet-600 to-pink-600 text-white">
          <tr>{['NO','NAMA','DEPT','JOB','HADIR','GAJI POKOK','GAJI/HARI','TOTAL GAJI','POTONGAN','TRANSFER',''].map((h,hi)=><th key={hi} className="px-3 py-2 text-left whitespace-nowrap">{h}</th>)}</tr>
        </thead>
        <tbody>
          {data.lines.map((l,i)=><tr key={l.talentId} className="border-t border-slate-50 hover:bg-violet-50/40">
            <td className="px-3 py-2">{i+1}</td>
            <td className="px-3 py-2 font-semibold whitespace-nowrap">{l.name}</td>
            <td className="px-3 py-2 whitespace-nowrap"><span className={`text-[10px] px-2 py-0.5 rounded-full ${l.type==='STAFF'?'bg-pink-100 text-pink-700':'bg-violet-100 text-violet-700'}`}>{l.department}</span></td>
            <td className="px-3 py-2 text-slate-500 whitespace-nowrap">{l.jobDesk}</td>
            <td className="px-3 py-2">{l.kehadiran}</td>
            <td className="px-3 py-2">{fmtRp(l.gajiPokok)}</td>
            <td className="px-3 py-2">{fmtRp(l.gajiPerHari)}</td>
            <td className="px-3 py-2 font-bold">{fmtRp(l.totalGaji)}</td>
            <td className="px-3 py-2 text-rose-500">{fmtRp(l.potongan)}</td>
            <td className="px-3 py-2 font-bold text-violet-700">{fmtRp(l.transfer)}</td>
            <td className="px-3 py-2"><button className="text-violet-500" onClick={()=>openAdj(l)}>edit</button></td>
          </tr>)}
        </tbody>
        <tfoot><tr className="bg-violet-50 font-bold"><td colSpan={9} className="px-3 py-2 text-right">TOTAL TRANSFER</td><td className="px-3 py-2 text-violet-700">{fmtRp(total)}</td><td></td></tr></tfoot>
      </table>
    </div>
    <PayAdj />
  </div>;
  function openAdj(l){ window.__adj = l; document.dispatchEvent(new Event('openadj')); }
  function PayAdj(){
    const [l,setL]=useState(null);
    useEffect(()=>{ const h=()=>setL(window.__adj); document.addEventListener('openadj',h); return ()=>document.removeEventListener('openadj',h); },[]);
    if(!l) return null;
    const [f,setF]=[null,null];
    return <AdjModal line={l} onClose={()=>setL(null)} onSaved={()=>{setL(null); api(`/payroll?periodId=${periodId}${gf?'&groupId='+gf:''}`).then(setData);}} toast={toast} />;
  }
}
function AdjModal({ line, onClose, onSaved, toast }) {
  const [f,setF]=useState({ insentif:line.insentif, makan:line.makan, transport:line.transport, tunjanganLain:line.tunjanganLain, overtime:line.overtime, potongan:line.potongan, bpjs:line.bpjs });
  const set=(k,v)=>setF(p=>({...p,[k]:+v}));
  async function save(){ try{ await api('/payroll/'+line.talentId,{method:'PUT',body:f}); onSaved(); }catch(e){toast(e.message,'err');} }
  return <Modal title={'Komponen Gaji — '+line.name} onClose={onClose}>
    <div className="grid grid-cols-2 gap-3">
      <Field label="Insentif"><input type="number" className={inputCls} value={f.insentif} onChange={e=>set('insentif',e.target.value)} /></Field>
      <Field label="Tunj. Makan"><input type="number" className={inputCls} value={f.makan} onChange={e=>set('makan',e.target.value)} /></Field>
      <Field label="Tunj. Transport"><input type="number" className={inputCls} value={f.transport} onChange={e=>set('transport',e.target.value)} /></Field>
      <Field label="Tunjangan Lain"><input type="number" className={inputCls} value={f.tunjanganLain} onChange={e=>set('tunjanganLain',e.target.value)} /></Field>
      <Field label="Over Time"><input type="number" className={inputCls} value={f.overtime} onChange={e=>set('overtime',e.target.value)} /></Field>
      <Field label="Potongan (kasbon/pph/telat)"><input type="number" className={inputCls} value={f.potongan} onChange={e=>set('potongan',e.target.value)} /></Field>
      <Field label="BPJS"><input type="number" className={inputCls} value={f.bpjs} onChange={e=>set('bpjs',e.target.value)} /></Field>
    </div>
    <Btn className="w-full justify-center mt-2" onClick={save}>Simpan Komponen</Btn>
  </Modal>;
}

// ============ Bonus (Harian + Bulanan) ============
function Bonus({ periodId, user, groups, toast }) {
  const isMgmt = user.role==='ADMIN' || user.role==='MANAGEMENT';
  const [tab,setTab]=useState('harian');
  if(!periodId) return <Empty msg="Pilih periode bulan untuk melihat bonus." />;
  const pill = on => `px-4 py-2 rounded-xl text-sm font-semibold ${on?'bg-gradient-to-r from-violet-600 to-pink-600 text-white':'bg-white border border-slate-200 text-slate-600'}`;
  return <div className="fade-in">
    <div className="flex gap-2 mb-4">
      <button onClick={()=>setTab('harian')} className={pill(tab==='harian')}>Bonus Harian</button>
      {isMgmt && <button onClick={()=>setTab('bulanan')} className={pill(tab==='bulanan')}>Bonus Bulanan (tgl 8)</button>}
    </div>
    {tab==='harian' ? <BonusHarian periodId={periodId} groups={groups} toast={toast} canClaim={isMgmt} /> : <BonusBulanan periodId={periodId} toast={toast} />}
  </div>;
}
function BonusHarian({ periodId, groups, toast, canClaim }) {
  const [groupId,setGroupId]=useState(groups[0]?.id||'');
  const [data,setData]=useState(null);
  const load=useCallback(()=>{ if(periodId&&groupId) api(`/bonus/daily?periodId=${periodId}&groupId=${groupId}`).then(setData).catch(e=>toast(e.message,'err')); },[periodId,groupId]);
  useEffect(load,[load]);
  useEffect(()=>{ if(!groupId&&groups[0]) setGroupId(groups[0].id); },[groups]);
  async function toggle(talentId,day,claimed){ try{ await api('/bonus/claim',{method:'POST',body:{periodId,talentId,day,claimed}}); load(); }catch(e){toast(e.message,'err');} }
  return <div>
    <div className="flex flex-wrap items-center gap-3 mb-4">
      <select className={inputCls+' w-auto'} value={groupId} onChange={e=>setGroupId(e.target.value)}>{groups.map(g=><option key={g.id} value={g.id}>{g.name}</option>)}</select>
      {data && <div className="ml-auto flex flex-wrap gap-2 text-xs">
        <span className="px-3 py-1.5 rounded-lg bg-violet-100 text-violet-700 font-semibold">Total {fmtRp(data.totalAll)}</span>
        <span className="px-3 py-1.5 rounded-lg bg-emerald-100 text-emerald-700 font-semibold">Sudah diambil {fmtRp(data.totalClaimed)}</span>
        <span className="px-3 py-1.5 rounded-lg bg-amber-100 text-amber-700 font-semibold">Belum {fmtRp(data.totalUnclaimed)}</span>
      </div>}
    </div>
    {!canClaim && <p className="text-xs text-slate-400 mb-3">Status \"sudah diambil\" hanya dapat diubah oleh Manajemen/Admin.</p>}
    {!data ? <Loading/> : data.rows.length===0 ? <Empty msg="Belum ada bonus harian pada periode & group ini." /> :
    <div className="space-y-3">
      {data.rows.map(r=> <div key={r.talentId} className="card p-4">
        <div className="flex justify-between items-center mb-3"><b className="text-slate-700">{r.name} <span className="text-slate-400 text-xs">{r.stageName}</span></b><span className="text-sm text-slate-500">Total {fmtRp(r.total)}</span></div>
        <div className="flex flex-wrap gap-2">
          {r.items.map(it=>{
            const cls = `px-3 py-2 rounded-xl text-center transition ${it.claimed?'bg-emerald-500 text-white':'bg-amber-50 text-amber-700 border border-amber-200'} ${canClaim?'hover:opacity-90 cursor-pointer':'cursor-default'}`;
            const inner = <><div className="text-[10px] opacity-80">Tgl {it.day}</div><div className="font-bold text-sm">{fmtRp(it.amount)}</div><div className="text-[10px]">{it.claimed?'✓ Diambil':'Belum diambil'}</div></>;
            return canClaim
              ? <button key={it.day} onClick={()=>toggle(r.talentId,it.day,!it.claimed)} className={cls}>{inner}</button>
              : <div key={it.day} className={cls} title="Hanya manajemen yang dapat mengubah status">{inner}</div>;
          })}
        </div>
      </div>)}
    </div>}
  </div>;
}
function BonusBulanan({ periodId, toast }) {
  const [d,setD]=useState(null);
  useEffect(()=>{ api('/bonus/monthly?periodId='+periodId).then(setD).catch(e=>toast(e.message,'err')); },[periodId]);
  if(!d) return <Loading/>;
  return <div>
    <div className="flex flex-wrap justify-between items-center mb-3 gap-2">
      <p className="text-sm text-slate-500">Laporan bonus dikeluarkan setiap <b>tanggal {d.cutoff}</b>. Bonus terpisah dari gaji.</p>
      <Btn color="green" onClick={()=>downloadAuthed(`/export/bonus-monthly.xlsx?periodId=${periodId}`).catch(e=>toast(e.message,'err'))}>⬇ Export Excel</Btn>
    </div>
    <h3 className="font-bold text-slate-700 mb-2">Bonus Target Talent & Universe</h3>
    <div className="card overflow-auto mb-5"><table className="w-full text-sm">
      <thead className="bg-violet-600 text-white"><tr>{['NAMA','GROUP','DIAMOND','TARGET','CAPAI','BONUS TARGET','UNIVERSE','TOTAL'].map((h,i)=><th key={i} className="px-3 py-2 text-left whitespace-nowrap">{h}</th>)}</tr></thead>
      <tbody>{d.talents.map(t=><tr key={t.talentId} className="border-t border-slate-50">
        <td className="px-3 py-2 font-semibold">{t.name}</td><td className="px-3 py-2 text-slate-500">{t.group}</td>
        <td className="px-3 py-2">{fmtNum(t.diamondTotal)}</td><td className="px-3 py-2 text-slate-400">{fmtNum(t.target)}</td>
        <td className="px-3 py-2">{t.reached?<span className="text-emerald-600 font-bold">YA</span>:<span className="text-rose-400">-</span>}</td>
        <td className="px-3 py-2 text-violet-700">{fmtRp(t.targetBonus)}</td><td className="px-3 py-2 text-amber-600">{fmtRp(t.universeBonus)}</td>
        <td className="px-3 py-2 font-bold">{fmtRp(t.total)}</td>
      </tr>)}</tbody>
    </table></div>
    <h3 className="font-bold text-slate-700 mb-2">Bonus MC / Operator (Bulanan)</h3>
    <div className="card overflow-auto"><table className="w-full text-sm">
      <thead className="bg-pink-600 text-white"><tr>{['GROUP','TOTAL DIAMOND','TARGET BULANAN','CAPAI','BONUS BULANAN','BONUS HARIAN (akum)','HARI CAPAI'].map((h,i)=><th key={i} className="px-3 py-2 text-left whitespace-nowrap">{h}</th>)}</tr></thead>
      <tbody>{d.mc.map((m,i)=><tr key={i} className="border-t border-slate-50">
        <td className="px-3 py-2 font-semibold">{m.group}</td><td className="px-3 py-2">{fmtNum(m.monthTotal)}</td>
        <td className="px-3 py-2 text-slate-400">{fmtNum(m.monthlyTarget)}</td>
        <td className="px-3 py-2">{m.monthlyReached?<span className="text-emerald-600 font-bold">YA</span>:<span className="text-rose-400">-</span>}</td>
        <td className="px-3 py-2 text-pink-700 font-bold">{fmtRp(m.monthlyBonus)}</td><td className="px-3 py-2">{fmtRp(m.dailyBonusTotal)}</td><td className="px-3 py-2">{m.daysHit} hari</td>
      </tr>)}</tbody>
    </table></div>
    <div className="mt-4 text-right text-lg font-bold grad-text">Grand Total Bonus: {fmtRp(d.grandTotal)}</div>
  </div>;
}

// ============ Users management ============
function Users({ toast, groups }) {
  const [list,setList]=useState([]); const [edit,setEdit]=useState(null);
  const load=()=>api('/users').then(setList); useEffect(()=>{load();},[]);
  const blank={ name:'', username:'', password:'', role:'OPERATOR', groupId:'', active:true };
  return <div className="fade-in">
    <div className="flex justify-between mb-4"><h2 className="text-xl font-bold text-slate-800">Pengguna & Akses</h2><Btn onClick={()=>setEdit(blank)}>+ User</Btn></div>
    <div className="card overflow-auto"><table className="w-full text-sm">
      <thead className="bg-slate-50 text-slate-500 text-xs uppercase"><tr><th className="text-left px-4 py-3">Nama</th><th className="text-left px-4 py-3">Username</th><th className="text-left px-4 py-3">Peran</th><th className="text-left px-4 py-3">Group</th><th className="px-4 py-3"></th></tr></thead>
      <tbody>{list.map(u=><tr key={u.id} className="border-t border-slate-50">
        <td className="px-4 py-3 font-semibold">{u.name}</td><td className="px-4 py-3">{u.username}</td>
        <td className="px-4 py-3"><span className="text-xs px-2 py-1 rounded-lg bg-violet-100 text-violet-700">{u.role}</span></td>
        <td className="px-4 py-3 text-slate-500">{groups.find(g=>g.id===u.groupId)?.name||'—'}</td>
        <td className="px-4 py-3 text-right"><button className="text-violet-500" onClick={()=>setEdit({...u,password:''})}>Edit</button></td>
      </tr>)}</tbody>
    </table></div>
    {edit && <UserModal u={edit} groups={groups} onClose={()=>setEdit(null)} onSaved={()=>{setEdit(null);load();toast('User disimpan');}} toast={toast} />}
  </div>;
}
function UserModal({ u, groups, onClose, onSaved, toast }) {
  const [f,setF]=useState({...u}); const set=(k,v)=>setF(p=>({...p,[k]:v}));
  async function save(){ try{ if(f.id) await api('/users/'+f.id,{method:'PUT',body:f}); else await api('/users',{method:'POST',body:f}); onSaved(); }catch(e){toast(e.message,'err');} }
  return <Modal title={f.id?'Edit User':'User Baru'} onClose={onClose}>
    <Field label="Nama"><input className={inputCls} value={f.name} onChange={e=>set('name',e.target.value)} /></Field>
    {!f.id && <Field label="Username"><input className={inputCls} value={f.username} onChange={e=>set('username',e.target.value)} /></Field>}
    <Field label={f.id?'Password baru (kosong = tetap)':'Password'}><input type="password" className={inputCls} value={f.password||''} onChange={e=>set('password',e.target.value)} /></Field>
    <Field label="Peran"><select className={inputCls} value={f.role} onChange={e=>set('role',e.target.value)}><option value="ADMIN">Admin</option><option value="MANAGEMENT">Manajemen</option><option value="OPERATOR">Operator / MC</option></select></Field>
    {f.role==='OPERATOR' && <Field label="Group"><select className={inputCls} value={f.groupId||''} onChange={e=>set('groupId',e.target.value)}><option value="">— pilih —</option>{groups.map(g=><option key={g.id} value={g.id}>{g.name}</option>)}</select></Field>}
    <Btn className="w-full justify-center" onClick={save}>Simpan</Btn>
  </Modal>;
}

// ============ Periods management ============
function Periods({ toast }) {
  const [list,setList]=useState([]); const load=()=>api('/periods').then(setList); useEffect(()=>{load();},[]);
  async function saveDays(p,gid,val){ const wd={...(p.workingDays||{})}; wd[gid]=+val; await api('/periods/'+p.id,{method:'PUT',body:{workingDays:wd}}); load(); }
  const [groups,setGroups]=useState([]); useEffect(()=>{api('/groups').then(setGroups);},[]);
  return <div className="fade-in">
    <h2 className="text-xl font-bold text-slate-800 mb-4">Master Hari Kerja per Bulan</h2>
    <p className="text-sm text-slate-500 mb-4">Atur jumlah hari kerja efektif tiap group per bulan (fleksibel, mis. Juni 24 hari, Agustus 26 hari).</p>
    <div className="space-y-3">{list.map(p=><div key={p.id} className="card p-4">
      <h3 className="font-bold text-slate-700 mb-2">{p.label}</h3>
      <div className="grid md:grid-cols-3 gap-3">
        <Field label="Default (hari)"><input type="number" className={inputCls} defaultValue={p.defaultWorkingDays} onBlur={e=>api('/periods/'+p.id,{method:'PUT',body:{defaultWorkingDays:+e.target.value}}).then(load)} /></Field>
        {groups.map(g=><Field key={g.id} label={g.name+' (hari)'}><input type="number" className={inputCls} defaultValue={(p.workingDays&&p.workingDays[g.id])??p.defaultWorkingDays} onBlur={e=>saveDays(p,g.id,e.target.value)} /></Field>)}
      </div>
    </div>)}</div>
  </div>;
}

// ============ Backup & Data (Admin) ============
function BackupData({ toast }) {
  const [stats, setStats] = useState(null);
  const [busy, setBusy] = useState(false);
  const fileRef = useRef(null);
  const load = () => api('/admin/stats').then(setStats).catch(()=>{});
  useEffect(()=>{ load(); }, []);
  const labels = { users:'Pengguna', groups:'Group', talents:'Talent & Staff', periods:'Periode', points:'Sel Point', attendance:'Absensi', universeGifts:'Universe', bonusClaims:'Klaim Bonus', payrollAdjust:'Penyesuaian' };

  async function doBackup(){ try{ setBusy(true); await downloadAuthed('/admin/backup'); toast('Backup berhasil diunduh'); }catch(e){ toast(e.message,'err'); } finally{ setBusy(false); } }
  async function doRestore(e){
    const file = e.target.files[0]; if(!file) return;
    if(!confirm('Restore akan MENGGANTI seluruh data saat ini dengan isi file backup. Lanjutkan?')){ fileRef.current.value=''; return; }
    try{
      setBusy(true);
      const text = await file.text();
      let data;
      try { data = JSON.parse(text); }
      catch { toast('File ini bukan file backup JSON yang valid. Gunakan file hasil tombol Download Backup.','err'); setBusy(false); fileRef.current.value=''; return; }
      if (!data || data.app !== 'SIIKU' || !data.collections) { toast('File ini bukan backup SIIKU yang valid.','err'); setBusy(false); fileRef.current.value=''; return; }
      await api('/admin/restore', { method:'POST', body:data });
      toast('Restore berhasil. Memuat ulang...'); setTimeout(()=>location.reload(), 1200);
    }catch(err){ toast('Gagal restore: '+err.message,'err'); } finally{ setBusy(false); fileRef.current.value=''; }
  }
  async function cleanTx(){
    if(!confirm('Hapus SEMUA data transaksi (point, absensi, bonus)? Master & akun tetap aman. Disarankan backup dulu.')) return;
    try{ setBusy(true); const r=await api('/admin/clean',{method:'POST',body:{scope:'TRANSACTIONS'}}); toast(r.note); load(); }catch(e){ toast(e.message,'err'); } finally{ setBusy(false); }
  }
  async function cleanAll(){
    const t = prompt('PERINGATAN: ini menghapus SEMUA data (termasuk akun & master). Ketik RESET untuk konfirmasi:');
    if(t!=='RESET') return;
    try{ setBusy(true); const r=await api('/admin/clean',{method:'POST',body:{scope:'ALL'}}); alert(r.note); logout(); }catch(e){ toast(e.message,'err'); setBusy(false); }
  }

  return <div className="fade-in max-w-3xl">
    <h2 className="text-xl font-bold text-slate-800 mb-1">Backup & Data</h2>
    <p className="text-sm text-slate-400 mb-5">Cadangkan, pulihkan, atau bersihkan data. Data tersimpan aman di SQLite.</p>

    {stats && <div className="grid grid-cols-3 md:grid-cols-5 gap-2 mb-6">
      {Object.keys(labels).map(k=> <div key={k} className="bg-slate-50 rounded-xl p-3 text-center">
        <div className="text-lg font-extrabold grad-text">{fmtNum(stats[k]||0)}</div>
        <div className="text-[11px] text-slate-500">{labels[k]}</div>
      </div>)}
    </div>}

    <div className="card p-5 mb-4">
      <h3 className="font-bold text-slate-700 mb-1">Backup</h3>
      <p className="text-sm text-slate-500 mb-3">Unduh seluruh data ke satu file <b>.json</b>. Simpan di tempat aman / cloud.</p>
      <Btn color="green" disabled={busy} onClick={doBackup}>⬇ Download Backup</Btn>
    </div>

    <div className="card p-5 mb-4">
      <h3 className="font-bold text-slate-700 mb-1">Restore</h3>
      <p className="text-sm text-slate-500 mb-3">Pulihkan data dari file backup. <span className="text-rose-500 font-semibold">Mengganti seluruh data saat ini.</span></p>
      <input ref={fileRef} type="file" accept="application/json,.json" onChange={doRestore} disabled={busy}
        className="text-sm file:mr-3 file:px-4 file:py-2 file:rounded-xl file:border-0 file:bg-violet-100 file:text-violet-700 file:font-semibold" />
    </div>

    <div className="card p-5 border border-rose-100">
      <h3 className="font-bold text-rose-600 mb-1">Bersihkan Data (Hati-hati)</h3>
      <p className="text-sm text-slate-500 mb-3">Disarankan melakukan backup sebelum membersihkan data.</p>
      <div className="flex flex-wrap gap-2">
        <Btn color="danger" disabled={busy} onClick={cleanTx}>Hapus Data Transaksi</Btn>
        <button disabled={busy} onClick={cleanAll} className="px-4 py-2 rounded-xl font-semibold text-sm bg-rose-600 text-white hover:bg-rose-700">Reset Total (semua data)</button>
      </div>
      <p className="text-xs text-slate-400 mt-2">Hapus Transaksi = point/absensi/bonus dihapus, master & akun tetap. Reset Total = semua dihapus, akun admin direset.</p>
    </div>
  </div>;
}

// ============ Settings ============
function Settings({ toast }) {
  const [f,setF]=useState({old:'',n1:'',n2:''});
  async function save(){ if(f.n1!==f.n2) return toast('Konfirmasi password tidak sama','err'); try{ await api('/change-password',{method:'POST',body:{oldPassword:f.old,newPassword:f.n1}}); toast('Password diubah'); setF({old:'',n1:'',n2:''}); }catch(e){toast(e.message,'err');} }
  return <div className="fade-in max-w-md">
    <h2 className="text-xl font-bold text-slate-800 mb-4">Pengaturan Akun</h2>
    <div className="card p-5">
      <Field label="Password Lama"><input type="password" className={inputCls} value={f.old} onChange={e=>setF({...f,old:e.target.value})} /></Field>
      <Field label="Password Baru"><input type="password" className={inputCls} value={f.n1} onChange={e=>setF({...f,n1:e.target.value})} /></Field>
      <Field label="Ulangi Password Baru"><input type="password" className={inputCls} value={f.n2} onChange={e=>setF({...f,n2:e.target.value})} /></Field>
      <Btn className="w-full justify-center" onClick={save}>Ubah Password</Btn>
    </div>
  </div>;
}

// ============ shared placeholders ============
function Loading(){ return <div className="flex items-center justify-center py-20 text-violet-400">Memuat…</div>; }
function Empty({ msg }){ return <div className="card p-10 text-center text-slate-400">{msg}</div>; }

// ============ Shell ============
function App() {
  const [user, setUser] = useState(()=>{ try{return JSON.parse(localStorage.getItem('siiku_user'));}catch{return null;} });
  const [tab, setTab] = useState('dashboard');
  const [periods, setPeriods] = useState([]); const [periodId, setPeriodId] = useState('');
  const [groups, setGroups] = useState([]);
  const [toastMsg, setToastMsg] = useState(null);
  const toast = (msg,type)=>{ setToastMsg({msg,type}); setTimeout(()=>setToastMsg(null),2600); };
  const [collapsed, setCollapsed] = useState(()=> localStorage.getItem('siiku_collapsed')==='1');
  const toggleSidebar = ()=> setCollapsed(c=>{ const n=!c; localStorage.setItem('siiku_collapsed', n?'1':'0'); return n; });

  useEffect(()=>{ document.getElementById('boot').style.display='none'; }, []);
  useEffect(()=>{ if(user){ api('/periods').then(ps=>{ setPeriods(ps); if(ps[0]) setPeriodId(ps[0].id); }); api('/groups').then(setGroups); } }, [user]);

  if (!user) return <Login onLogin={setUser} />;
  const isAdmin = user.role==='ADMIN';
  const isMgmt = user.role==='ADMIN' || user.role==='MANAGEMENT';
  const nav = isMgmt
    ? [['dashboard','Dashboard','📊'],['points','Input Point','📝'],['groups','Group','👥'],['talents','Talent & Staff','💃'],['periods','Hari Kerja','📅'],['payroll','Payroll','💰'],['bonus','Bonus','🎁'],['users','Pengguna','🔐'],['data','Backup & Data','💾'],['settings','Akun','⚙️']]
    : [['dashboard','Dashboard','📊'],['points','Input Point','📝'],['bonus','Bonus','🎁'],['settings','Akun','⚙️']];

  return <div className="min-h-screen flex">
    {/* sidebar */}
    <aside className={`${collapsed?'w-16':'w-60'} bg-white border-r border-slate-100 flex flex-col fixed h-full z-40 transition-all duration-200`}>
      <div className="px-3 flex items-center gap-3 border-b border-slate-100" style={{height:'57px'}}>
        <div className="w-10 h-10 rounded-xl grad-bg flex items-center justify-center text-white font-extrabold shrink-0">IKU</div>
        {!collapsed && <div><div className="font-extrabold grad-text leading-none">SIIKU</div><div className="text-[10px] text-slate-400">Insia Karya Utama</div></div>}
      </div>
      <nav className="flex-1 p-2 space-y-1 overflow-auto">
        {nav.filter(n=> n[0]!=='data' || isAdmin).map(([k,label,icon])=><button key={k} onClick={()=>setTab(k)} title={label} className={`w-full flex items-center ${collapsed?'justify-center':'gap-3'} px-3 py-2.5 rounded-xl text-sm font-semibold transition ${tab===k?'bg-gradient-to-r from-violet-600 to-pink-600 text-white shadow':'text-slate-500 hover:bg-slate-50'}`}><span className="text-base leading-none">{icon}</span>{!collapsed && <span>{label}</span>}</button>)}
      </nav>
      <div className="p-3 border-t border-slate-100">
        {!collapsed && <><div className="text-sm font-semibold text-slate-700 truncate">{user.name}</div><div className="text-xs text-slate-400 mb-2">{user.role}</div></>}
        <button onClick={logout} title="Keluar" className="text-xs text-rose-500 font-semibold flex items-center gap-1">{collapsed ? <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"/><polyline points="16 17 21 12 16 7"/><line x1="21" y1="12" x2="9" y2="12"/></svg> : <span>Keluar →</span>}</button>
      </div>
    </aside>
    {/* main */}
    <main className={`flex-1 min-w-0 ${collapsed?'ml-16':'ml-60'} transition-all duration-200`}>
      <header className="bg-white/80 backdrop-blur border-b border-slate-100 px-4 py-3 flex items-center justify-between sticky top-0 z-30">
        <div className="flex items-center gap-3 min-w-0">
          <button onClick={toggleSidebar} title="Perkecil / lebarkan menu" className="w-9 h-9 rounded-xl hover:bg-slate-100 flex items-center justify-center text-slate-500 shrink-0"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round"><line x1="3" y1="6" x2="21" y2="6"/><line x1="3" y1="12" x2="21" y2="12"/><line x1="3" y1="18" x2="21" y2="18"/></svg></button>
          <h1 className="font-bold text-slate-700 truncate">{nav.find(n=>n[0]===tab)?.[1]}</h1>
        </div>
        <PeriodPicker periods={periods} value={periodId} onChange={setPeriodId} canCreate={isMgmt}
          onCreate={async(y,m)=>{ const p=await api('/periods',{method:'POST',body:{year:y,month:m}}); const ps=await api('/periods'); setPeriods(ps); setPeriodId(p.id); }} />
      </header>
      <div className="p-6">
        {tab==='dashboard' && <Dashboard periodId={periodId} user={user} />}
        {tab==='points' && <PointGrid periodId={periodId} user={user} groups={groups} toast={toast} />}
        {tab==='groups' && isMgmt && <Groups toast={toast} />}
        {tab==='talents' && isMgmt && <Talents toast={toast} groups={groups} />}
        {tab==='periods' && isMgmt && <Periods toast={toast} />}
        {tab==='payroll' && isMgmt && <Payroll periodId={periodId} toast={toast} groups={groups} />}
        {tab==='bonus' && <Bonus periodId={periodId} user={user} groups={groups} toast={toast} />}
        {tab==='users' && isMgmt && <Users toast={toast} groups={groups} />}
        {tab==='data' && isAdmin && <BackupData toast={toast} />}
        {tab==='settings' && <Settings toast={toast} />}
      </div>
    </main>
    <Toast msg={toastMsg?.msg} type={toastMsg?.type} />
  </div>;
}

ReactDOM.createRoot(document.getElementById('root')).render(<App />);
