// Fund Transfers — full page standalone page (KPI cards, date
//   filter, refresh, pagination) with the main table being a
//   per-Expense-Type roll-up sourced from General Information (Submitted records):
//     Expense Details=Expense Type · Estimate=Σestimate · Actual=Σactual
//     Variance=Actual−Estimate (signed) · Cost Variance=Variance/Estimate×100 (signed %)
//     Status=threshold chip on CV%: >+15% Critical · +5..15% Warning · ±5% On-track · <−5% Normal
const { useState, useEffect, useMemo, useRef } = React;

// Signed Variance + Cost Variance % cells — same number format as the table.
function VarianceCells({ estimate, actual, light, dense }){
  const varc = actual - estimate;
  const pct  = estimate !== 0 ? (varc/estimate)*100 : 0;
  const clr  = varc===0 ? '#c3c9da' : (varc>0 ? '#dc2626' : '#059669');
  const sign = varc>0 ? '+' : varc<0 ? '−' : '';
  const pad = light ? {padding:'3px 16px',fontSize:12} : dense ? {padding:'6px 16px',lineHeight:'26px'} : {};
  const wv = light ? 400 : (dense ? (varc===0 ? 400 : 700) : 800);
  const wc = light ? 400 : (dense ? (estimate===0 ? 400 : 700) : 800);
  return (
    <React.Fragment>
      <td className="num mono" style={{...pad,fontWeight:wv,color:clr}}>{varc===0?fmt(0):sign+fmt(Math.abs(varc))}</td>
      <td className="num mono" style={{...pad,fontWeight:wc,fontSize:light?12:12.5,color:{normal:'#16a34a',stable:'#d97706',warning:'#d97706',critical:'#dc2626'}[ftStatusKey(estimate,actual)]}}><span style={{display:'inline-flex',alignItems:'center',gap:7,justifyContent:'flex-end'}}><SparkIcon estimate={estimate} actual={actual}/>{estimate===0?'0.00%':sign+fmt(Math.abs(pct))+'%'}</span></td>
    </React.Fragment>
  );
}
// Cost-Variance % thresholds (pct = (actual−estimate)/estimate×100):
//   > +15%      → Critical  ·  +5% … +15% → Warning
//   −5% … +5%   → On-track   ·  < −5%      → Normal (under budget)
function ftStatusKey(estimate, actual){
  if (!(estimate > 0)) return actual > 0 ? 'critical' : 'stable';
  const pct = (actual - estimate) / estimate * 100;
  if (pct > 15) return 'critical';
  if (pct > 5)  return 'warning';
  if (pct >= -5) return 'stable';
  return 'normal';
}
function ftIsCritical(estimate, actual){ return ftStatusKey(estimate, actual) === 'critical'; }

// Threshold status chip: Critical>+15% · Warning +5..+15% · On-track ±5% · Normal <−5%.
// Spark + dots trend icon (moved into Cost Variance column). dir up = rising cost.
function SparkIcon({ estimate, actual }){
  const key = ftStatusKey(estimate, actual);
  const cfg = { normal:{c:'#16a34a',dir:'down'}, stable:{c:'#d97706',dir:'flat'}, warning:{c:'#d97706',dir:'up'}, critical:{c:'#dc2626',dir:'up'} }[key];
  const spk = cfg.dir==='up' ? [[2,15],[8,11],[14,12],[21,4]] : cfg.dir==='down' ? [[2,4],[8,9],[14,7],[21,16]] : [[2,10],[8,9],[14,11],[21,10]];
  const poly = spk.map(p=>p.join(',')).join(' ');
  return (
    <svg width="24" height="20" viewBox="0 0 24 20" style={{filter:`drop-shadow(0 0 3px ${cfg.c}80)`,flex:'none'}}>
      <polyline points={poly} fill="none" stroke={cfg.c} strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round" opacity=".55"/>
      {spk.map(([x,y],i)=><circle key={i} cx={x} cy={y} r="2" fill={cfg.c}/>)}
    </svg>
  );
}

// Clearance badge — dot + text (31 · Payment status text): จ่ายแล้ว / รอชำระ / ค้างชำระ.
function StatusChip({ estimate, actual }){
  const key = ftStatusKey(estimate, actual);
  const pay = {
    normal:   { c:'#16a34a', label:'จ่ายแล้ว' },
    stable:   { c:'#16a34a', label:'จ่ายแล้ว' },
    warning:  { c:'#d97706', label:'รอชำระ' },
    critical: { c:'#dc2626', label:'ค้างชำระ' },
  }[key];
  return (
    <span style={{display:'inline-flex',alignItems:'center',gap:6,padding:'6px 12px',borderRadius:999,fontSize:11.5,fontWeight:700,lineHeight:1,color:pay.c,background:pay.c+'14',boxShadow:'inset 0 0 0 1px '+pay.c+'33'}}>
      <span style={{width:6,height:6,borderRadius:99,background:pay.c,flex:'none'}}/>
      <span style={{letterSpacing:'.02em',whiteSpace:'nowrap'}}>{pay.label}</span>
    </span>
  );
}

// Critical Items pop-up body: header (Period + date range) + paginated table (12/page).
function CriticalItemsModal({ list, from, to }){
  const [pg,setPg]=React.useState(0);
  const PER=10;
  const pages=Math.max(1,Math.ceil(list.length/PER));
  const page=list.slice(pg*PER,pg*PER+PER);
  const MA=['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'];
  const dmy=(iso)=>{ const p=(iso||'').split('-'); return p.length===3?`${p[2]}/${p[1]}/${p[0]}`:iso; };
  const lbl=(iso)=>{ const p=(iso||'').split('-'); return p.length===3?`${MA[+p[1]-1]} ${p[0]}`:iso; };
  const period = lbl(from)===lbl(to) ? lbl(from) : `${lbl(from)} – ${lbl(to)}`;
  const pill={display:'inline-flex',alignItems:'center',gap:6,fontSize:12.5,fontWeight:600,color:'var(--ink-2)'};
  return (
    <div>
      <div style={{display:'flex',alignItems:'center',gap:18,flexWrap:'wrap',justifyContent:'flex-end',marginBottom:12}}>
        <span style={pill}><I.Cal size={14}/><b style={{color:'var(--ink)'}}>Period:</b> {period}</span>
        <span style={pill}><span style={{width:1,height:14,background:'#e2e6f0'}}/></span>
        <span style={pill}><b style={{color:'var(--ink)'}}>Start Date:</b> {dmy(from)}</span>
        <span style={pill}><b style={{color:'var(--ink)'}}>End Date:</b> {dmy(to)}</span>
      </div>
      <div className="tbl-wrap">
        <table className="t" style={{tableLayout:'fixed'}}>
          <colgroup>
            <col style={{width:'7%'}}/><col style={{width:'16%'}}/><col style={{width:'16%'}}/>
            <col style={{width:'16%'}}/><col style={{width:'16%'}}/><col style={{width:'16%'}}/><col style={{width:'13%'}}/>
          </colgroup>
          <thead><tr>
            <th style={{textAlign:'center'}}>Index</th>
            <th>Expense Details</th>
            <th className="num">Estimate</th>
            <th className="num">Actual</th>
            <th className="num">Variance</th>
            <th className="num">Cost Variance</th>
            <th style={{textAlign:'center'}}>Status</th>
          </tr></thead>
          <tbody>
            {page.map((r,i)=>{ const v=r.actual-r.estimate; const cv=r.estimate!==0?(v/r.estimate*100):0; return (
              <tr key={r.group}>
                <td className="mono" style={{padding:'5px 16px',textAlign:'center',color:'#9aa2b8'}}>{pg*PER+i+1}</td>
                <td className="thai" style={{padding:'5px 16px',color:'var(--ink)'}}>{r.group}</td>
                <td className="num mono" style={{padding:'5px 16px',color:'#0f766e',fontWeight:700}}>{fmt(r.estimate)}</td>
                <td className="num mono" style={{padding:'5px 16px',color:'#1f2a8e',fontWeight:700}}>{fmt(r.actual)}</td>
                <td className="num mono" style={{padding:'5px 16px',color:'#dc2626',fontWeight:700}}>+{fmt(v)}</td>
                <td className="num mono" style={{padding:'5px 16px',color:r.estimate===0?'#c3c9da':'#dc2626',fontWeight:700}}>{r.estimate===0?'0.00%':'+'+fmt(cv)+'%'}</td>
                <td style={{padding:'5px 16px',textAlign:'center'}}><StatusChip estimate={r.estimate} actual={r.actual}/></td>
              </tr>
            );})}
          </tbody>
        </table>
      </div>
      <div style={{display:'flex',alignItems:'center',justifyContent:'space-between',marginTop:0,paddingTop:12,borderTop:'1px solid #eef1f7'}}>
        <span style={{fontSize:12.5,color:'var(--muted)'}}>แสดง {pg*PER+1}–{Math.min((pg+1)*PER,list.length)} จาก {list.length} รายการ</span>
        <div style={{display:'flex',alignItems:'center',gap:14}}>
          <div className="row" style={{gap:4}}>
            <button className="btn sm" disabled={pg===0} onClick={()=>setPg(p=>Math.max(0,p-1))}>Previous</button>
            {Array.from({length:pages}).map((_,i)=>(
              <button key={i} className={"btn sm"+(pg===i?' primary':'')} onClick={()=>setPg(i)}>{i+1}</button>
            ))}
            <button className="btn sm" disabled={pg>=pages-1} onClick={()=>setPg(p=>Math.min(pages-1,p+1))}>Next</button>
          </div>
          <button className="btn" onClick={()=>window.closeModal()}>Close</button>
        </div>
      </div>
    </div>
  );
}

function Sparkline({ data, color="#1f2a8e", h=30, w=100, fill=true }){
  if (!data || data.length < 2){
    // No data yet — render a flat baseline so KPI cards stay tidy
    return (
      <svg className="spark" viewBox={`0 0 ${w} ${h}`}>
        <line x1="0" y1={h-2} x2={w} y2={h-2} stroke={color} strokeWidth="1.5" strokeDasharray="2 3" opacity=".3"/>
      </svg>
    );
  }
  const max = Math.max(...data), min = Math.min(...data);
  const range = (max-min) || 1;
  const stepX = w/(data.length-1);
  const pts = data.map((v,i)=>[i*stepX, h - ((v-min)/range)*(h-4) - 2]);
  const d = pts.map((p,i)=> (i?'L':'M')+p[0].toFixed(1)+' '+p[1].toFixed(1)).join(' ');
  const area = d + ` L ${w} ${h} L 0 ${h} Z`;
  const gid = "spk-"+color.replace('#','');
  return (
    <svg className="spark" viewBox={`0 0 ${w} ${h}`}>
      <defs>
        <linearGradient id={gid} x1="0" x2="0" y1="0" y2="1">
          <stop offset="0" stopColor={color} stopOpacity=".35"/>
          <stop offset="1" stopColor={color} stopOpacity="0"/>
        </linearGradient>
      </defs>
      {fill && <path d={area} fill={`url(#${gid})`}/>}
      <path d={d} fill="none" stroke={color} strokeWidth="1.8" strokeLinejoin="round" strokeLinecap="round"/>
      <circle cx={pts[pts.length-1][0]} cy={pts[pts.length-1][1]} r="2.5" fill={color}/>
    </svg>
  );
}

function FundTransfersView({ onNav }){
  const today = '2026-05-07';
  const clock = useSystemClock();
  const [tick, setTick] = useState(0);   // bumps when settings change to force re-render
  const [refreshing, setRefreshing] = useState(false);
  const [from, setFrom] = useState('');
  const [to, setTo]     = useState('');
  const [grain, setGrain] = useState('');
  const [company, setCompany] = useState('');   // '' = all companies
  const [page, setPage] = useState(1);
  const [showFilters, setShowFilters] = useState(true);
  const pageSize = 7;

  const datesReady = !!from && !!to;
  const MIN_DATE = '2026-01-01';
  const MAX_DATE = `${new Date().getFullYear()+1}-12-31`;   // up to next calendar year
  // If a newly-picked Start Date is after the current End Date, clear End so it can't stay < Start.
  useEffect(()=>{ if (from && to && to < from) setTo(''); }, [from]);
  const addDays = (iso,n)=>{ const d=new Date(iso+'T00:00:00'); d.setDate(d.getDate()+n); return d.toISOString().slice(0,10); };

  // Reset to first page whenever the filter scope changes.
  useEffect(()=>{ setPage(1); }, [from, to, company]);

  // Re-render when any General Information / Sales activity is logged (this tab or another).
  useEffect(()=>{
    const bump = ()=> setTick(t=>t+1);
    const onStorage = (e)=>{ if (!e || [null,'finx_sales_requests_v1','finx_expense_requests_v1','finx_expense_desc'].includes(e.key)) bump(); };
    window.addEventListener('finx-activity', bump);
    window.addEventListener('finx-cashflow-dirty', bump);
    window.addEventListener('storage', onStorage);
    return ()=>{ window.removeEventListener('finx-activity', bump); window.removeEventListener('finx-cashflow-dirty', bump); window.removeEventListener('storage', onStorage); };
  }, []);

  const newPeriod = (g) => {
    setGrain(g);
    const now = new Date();
    const y = now.getFullYear(), mo = now.getMonth();
    const pad = (d)=>String(d).padStart(2,'0');
    const iso = (d)=>`${d.getFullYear()}-${pad(d.getMonth()+1)}-${pad(d.getDate())}`;
    const today = iso(now);
    if (g==='day'){   setFrom(today); setTo(today); }                        // วันปัจจุบัน
    if (g==='week'){  // สัปดาห์ปัจจุบัน จันทร์–อาทิตย์
      const dow = (now.getDay()+6)%7;                                        // 0=Mon … 6=Sun
      const mon = new Date(y, mo, now.getDate()-dow);
      const sun = new Date(y, mo, now.getDate()-dow+6);
      setFrom(iso(mon)); setTo(iso(sun));
    }
    if (g==='month'){ const last=new Date(y, mo+1, 0).getDate();             // ทั้งเดือนปัจจุบัน
                      setFrom(`${y}-${pad(mo+1)}-01`); setTo(`${y}-${pad(mo+1)}-${pad(last)}`); }
    setPage(1);
  };

  // ── Fund Transfers table data: aggregate SUBMITTED General Information records by
  //    Expense Type, scoped to the selected date range. Drives the same pagination. ──
  // Companies available across Submitted expense records (for the picker).
  const ftCompanies = React.useMemo(()=>{
    let gdb={}; try{ gdb=JSON.parse(localStorage.getItem('finx_expense_requests_v1')||'{}'); }catch(_){ gdb={}; }
    const s=new Set();
    Object.values(gdb).forEach(rec=>{ if((rec.status||'')==='Submitted' && (rec.company||'').trim()) s.add(rec.company.trim()); });
    return [...s].sort();
  },[tick]);
  const ftAll = React.useMemo(()=>{
    if (!datesReady) return [];
    let gdb={}; try{ gdb=JSON.parse(localStorage.getItem('finx_expense_requests_v1')||'{}'); }catch(_){ gdb={}; }
    const map={};
    Object.values(gdb).forEach(rec=>{
      if((rec.status||'')!=='Submitted') return;
      if(company && (rec.company||'').trim()!==company) return;
      (rec.rows||[]).forEach(row=>{
        const iso=((row&&row.date)||'').slice(0,10);
        if(!iso || iso<from || iso>to) return;
        const key=(row.expenseGroup||'').trim()||'—';
        if(!map[key]) map[key]={group:key,estimate:0,actual:0,items:[]};
        map[key].estimate+=Number(row.estimate)||0;
        map[key].actual+=Number(row.amount)||0;
        map[key].items.push({
          date:iso,
          name:((row.accountName||'').trim())||'—',
          payable:((row.payable||'').trim())||'',
          estimate:Number(row.estimate)||0,
          amount:Number(row.amount)||0,
        });
      });
    });
    return Object.values(map).sort((a,b)=>b.actual-a.actual);
  },[datesReady,from,to,tick,company]);
  const ftTotalPages=Math.max(1,Math.ceil(ftAll.length/pageSize));
  const ftPage=Math.min(page,ftTotalPages);
  const ftStart=(ftPage-1)*pageSize;
  const ftRows=ftAll.slice(ftStart,ftStart+pageSize);
  // Card range: falls back to the current month when no dates are selected (table stays empty).
  const cardFrom = datesReady ? from : `${new Date().getFullYear()}-${String(new Date().getMonth()+1).padStart(2,'0')}-01`;
  const cardTo   = datesReady ? to   : (()=>{ const d=new Date(),l=new Date(d.getFullYear(),d.getMonth()+1,0); return `${l.getFullYear()}-${String(l.getMonth()+1).padStart(2,'0')}-${String(l.getDate()).padStart(2,'0')}`; })();
  const ftCards = React.useMemo(()=>{
    let gdb={}; try{ gdb=JSON.parse(localStorage.getItem('finx_expense_requests_v1')||'{}'); }catch(_){ gdb={}; }
    const map={};
    Object.values(gdb).forEach(rec=>{
      if((rec.status||'')!=='Submitted') return;
      if(company && (rec.company||'').trim()!==company) return;
      (rec.rows||[]).forEach(row=>{
        const iso=((row&&row.date)||'').slice(0,10);
        if(!iso || iso<cardFrom || iso>cardTo) return;
        const key=(row.expenseGroup||'').trim()||'—';
        if(!map[key]) map[key]={group:key,estimate:0,actual:0};
        map[key].estimate+=Number(row.estimate)||0;
        map[key].actual+=Number(row.amount)||0;
      });
    });
    return Object.values(map);
  },[cardFrom,cardTo,tick,company]);
  const ftTotEst=ftCards.reduce((s,r)=>s+r.estimate,0);
  const ftTotAct=ftCards.reduce((s,r)=>s+r.actual,0);
  const ftVariance=ftTotAct-ftTotEst;
  const ftCVpct=ftTotEst!==0 ? (ftVariance/ftTotEst*100) : 0;
  const ftCriticalList=ftCards.filter(r=>ftIsCritical(r.estimate,r.actual)).sort((a,b)=>(b.actual-b.estimate)-(a.actual-a.estimate));
  const ftCritical=ftCriticalList.length;
  const ftPendingRows=ftCards.filter(r=>['critical','warning'].includes(ftStatusKey(r.estimate,r.actual)));
  const ftPendingSum=ftPendingRows.reduce((s,r)=>s+r.actual,0);
  const ftPendingCount=ftPendingRows.length;
  const openCritical = ()=>{
    if(!ftCritical){ window.toast && window.toast('ไม่มีรายการ Critical ในช่วงนี้'); return; }
    window.openModal({
      title:<span style={{display:'inline-flex',alignItems:'center',gap:10}}><span style={{display:'inline-flex',alignItems:'center',justifyContent:'center',width:30,height:30,borderRadius:'50%',background:'linear-gradient(135deg,#f87171,#dc2626)',color:'#fff'}}><I.Info size={16}/></span>Critical Items <span style={{color:'#dc2626'}}>· {ftCritical} รายการ</span></span>,
      size:'lg',
      width:1060,
      message:<CriticalItemsModal list={ftCriticalList} from={cardFrom} to={cardTo}/>,
    });
  };

  // Expand / collapse per Expense Type to reveal its underlying line items.
  const [expanded,setExpanded]=React.useState(()=>new Set());
  const toggleExp=(g)=>setExpanded(s=>{ const n=new Set(s); n.has(g)?n.delete(g):n.add(g); return n; });
  const ftDMY=(iso)=>{ if(!iso) return '—'; const p=iso.split('-'); return p.length===3?`${p[2]}/${p[1]}/${p[0]}`:iso; };

  // Daily sparkline series + previous-window totals for KPI deltas (Daily Cash Flow look).
  const ftKpi = React.useMemo(()=>{
    const empty={spkAct:[],spkEst:[],spkVar:[],spkCV:[],prev:{act:0,est:0,variance:0,cvpct:0,critical:0}};
    let gdb={}; try{ gdb=JSON.parse(localStorage.getItem('finx_expense_requests_v1')||'{}'); }catch(_){ gdb={}; }
    const win=(a,b)=>{
      const day={}, grp={};
      Object.values(gdb).forEach(rec=>{
        if((rec.status||'')!=='Submitted') return;
        if(company && (rec.company||'').trim()!==company) return;
        (rec.rows||[]).forEach(row=>{
          const iso=((row&&row.date)||'').slice(0,10);
          if(!iso||iso<a||iso>b) return;
          const est=Number(row.estimate)||0, act=Number(row.amount)||0;
          if(!day[iso]) day[iso]={est:0,act:0};
          day[iso].est+=est; day[iso].act+=act;
          const g=(row.expenseGroup||'').trim()||'—';
          if(!grp[g]) grp[g]={est:0,act:0};
          grp[g].est+=est; grp[g].act+=act;
        });
      });
      const gs=Object.values(grp);
      const act=gs.reduce((s,x)=>s+x.act,0), est=gs.reduce((s,x)=>s+x.est,0);
      return {day, act, est, variance:act-est, cvpct: est!==0?((act-est)/est*100):0, critical:gs.filter(x=>ftIsCritical(x.est,x.act)).length};
    };
    const cur=win(cardFrom,cardTo);
    const isos=Object.keys(cur.day).sort();
    const spkAct=isos.map(d=>cur.day[d].act);
    const spkEst=isos.map(d=>cur.day[d].est);
    const spkVar=isos.map(d=>cur.day[d].act-cur.day[d].est);
    const spkCV =isos.map(d=>cur.day[d].est!==0?((cur.day[d].act-cur.day[d].est)/cur.day[d].est*100):0);
    const span=Math.round((new Date(cardTo)-new Date(cardFrom))/86400000);
    const prev=win(addDays(cardFrom,-(span+1)), addDays(cardFrom,-1));
    return {spkAct,spkEst,spkVar,spkCV,prev};
  },[cardFrom,cardTo,tick,company]);
  const mkDelta=(cur,prev)=>{ const pct = prev!==0 ? (cur-prev)/Math.abs(prev)*100 : (cur!==0?100:0); return {pct, dir: cur>=prev?'up':'down'}; };

  // Cards summarise the same Submitted + date-range data as the table below.
  const KPIS = [
    { tone:'blue',    label:'Total Transfers', display:fmt(ftTotAct), icon:'Income', color:'#1f2a8e', spk:ftKpi.spkAct, delta:mkDelta(ftTotAct,ftKpi.prev.act) },
    { tone:'green',   label:'Total Budget',    display:fmt(ftTotEst), icon:'MoneyWallet',  color:'#16a34a', spk:ftKpi.spkEst, delta:mkDelta(ftTotEst,ftKpi.prev.est) },
    { tone:'amber',   label:'Payment Variance',        display:<span style={{display:'inline-flex',alignItems:'baseline',gap:12}}>{(ftVariance>0?'+':ftVariance<0?'−':'')+fmt(Math.abs(ftVariance))}<span style={{fontSize:'.52em',fontWeight:800,color:ftCVpct>0?'#dc2626':ftCVpct<0?'#16a34a':'#94a0b8'}}>({(ftCVpct>0?'+':'')+fmt(ftCVpct)+'%'})</span></span>, icon:'Monitor', color:'#c2841f', valColor:ftVariance>0?'#dc2626':ftVariance<0?'#16a34a':undefined, spk:ftKpi.spkVar, delta:mkDelta(ftVariance,ftKpi.prev.variance) },
    { tone:'magenta', label:'Pending Payments',         display:fmt(ftPendingSum), icon:'Profit', color:'#c026d3', valColor:ftPendingSum>0?'#c026d3':undefined, spk:ftKpi.spkCV, delta:mkDelta(ftPendingSum,ftKpi.prev.variance) },
    { tone:'red',     label:'Unpaid Items',            display:String(ftPendingCount), icon:'Info', color:'#dc2626', valColor:ftPendingCount>0?'#dc2626':undefined, spk:ftKpi.spkVar, delta:mkDelta(ftPendingCount,ftKpi.prev.critical) },
  ];

  return (
    <div className="page">
      <div className="page-head">
        <div>
          <div className="page-title">Fund Transfers Overview</div>
          <div className="page-sub">{(()=>{
            const MON=['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'];
            const lbl=(iso)=>{ if(!iso) return null; const [y,m]=iso.split('-'); return `${MON[(+m)-1]} ${y}`; };
            const a=lbl(from), b=lbl(to);
            const range = (a&&b) ? (a===b?a:`${a} – ${b}`) : 'select a date range';
            return `Fiscal · ${range} · all amounts in THB`;
          })()}</div>
        </div>
        <div className="row" style={{gap:10}}>
          <button className="btn" disabled={refreshing} onClick={()=>{
            setRefreshing(true);
            window.showLoading('กำลังโหลดข้อมูลล่าสุด…');
            setTimeout(()=>{ try {
              setTick(t=>t+1);
            } finally { window.hideLoading(); setRefreshing(false); } }, 700);
          }}>
            <span style={{display:'inline-flex'}}>
              <I.Refresh size={14}/>
            </span>
            Refresh
          </button>
        </div>
      </div>

      <div className="kpi-grid">
        {KPIS.map((k,i)=>(
          <div className={"kpi "+k.tone} key={i} style={{cursor:k.onClick?'pointer':'default'}} onClick={k.onClick} title={k.onClick?'คลิกเพื่อดูรายการ':undefined}>
            <div className="label">
              <span>{k.label}</span>
              <span className="badge">{k.icon ? React.createElement(I[k.icon],{size:18}) : 'THB'}</span>
            </div>
            <div className="value mono" style={k.valColor ? {color:k.valColor} : undefined}>{k.display}</div>
            <div className="foot">
              <span className={"delta "+k.delta.dir} title="เทียบกับช่วงก่อนหน้า">
                {k.delta.dir==='up' ? <I.TrendUp size={12}/> : <I.TrendDown size={12}/>}
                {(k.delta.pct>=0?'+':'') + Number(k.delta.pct).toLocaleString('en-US',{minimumFractionDigits:1,maximumFractionDigits:1})}% vs prev.
              </span>
              <Sparkline data={k.spk} color={k.color}/>
            </div>
          </div>
        ))}
      </div>

      <div>
        <div className="card elev">
          <div className="card-head">
            <div className="row" style={{gap:10}}>
              <span style={{width:8,height:22,borderRadius:3,background:'linear-gradient(180deg,#2cb8b0,#1f2a8e)'}}/>
              <div className="card-title">Fund Transfers</div>
              <span className="chip">{ftAll.length} types</span>
            </div>
            <div className="row" style={{gap:8}}>
              <div className="seg">
                <button className={grain==='day'?'on':''}   onClick={()=>newPeriod('day')}>Day</button>
                <button className={grain==='week'?'on':''}  onClick={()=>newPeriod('week')}>Week</button>
                <button className={grain==='month'?'on':''} onClick={()=>newPeriod('month')}>Month</button>
              </div>
              <button className="btn sm primary" onClick={()=> onNav && onNav('general')}>View All <I.Chev size={12}/></button>
            </div>
          </div>
          {showFilters && (
          <div style={{padding:'14px 20px',display:'flex',gap:14,alignItems:'flex-end',flexWrap:'wrap'}}>
            <div className="field" style={{flex:'1 1 180px'}}>
              <label>Start Date</label>
              <div className="dateinput">
                <I.Cal size={14} stroke="#7b87a8"/>
                <input type="date" value={from} min={MIN_DATE} max={[to, MAX_DATE].filter(Boolean).sort()[0]} onChange={e=>setFrom(e.target.value)}/>
              </div>
            </div>
            <div className="field" style={{flex:'1 1 180px'}}>
              <label>End Date</label>
              <div className="dateinput">
                <I.Cal size={14} stroke="#7b87a8"/>
                <input type="date" value={to} min={from||MIN_DATE} max={MAX_DATE} onChange={e=>setTo(e.target.value)}/>
              </div>
            </div>
            <div className="field" style={{flex:'1 1 200px'}}>
              <label>Company name</label>
              <select className="select thai" value={company} onChange={e=>{setCompany(e.target.value); setPage(1);}}>
                <option value="">ทุกบริษัท (All Companies)</option>
                {ftCompanies.map(c=><option key={c} value={c}>{c}</option>)}
              </select>
            </div>
            <div className="row" style={{gap:8,marginLeft:'auto'}}>
              <button className="btn" onClick={()=>{setFrom(''); setTo(''); setCompany(''); setPage(1); window.toast('Form reset · ล้างค่า Start-End Date และ Company แล้ว');}}><I.Reset size={14}/> Reset</button>
            </div>
          </div>
          )}

          <div className="tbl-wrap" style={{padding:'0 8px 8px'}}>
            <table className="t" style={{minWidth:680}}>
              <colgroup>
                <col style={{width:'7%'}}/>
                <col style={{width:'24%'}}/>
                <col style={{width:'15%'}}/>
                <col style={{width:'15%'}}/>
                <col style={{width:'14%'}}/>
                <col style={{width:'14%'}}/>
                <col style={{width:'11%'}}/>
              </colgroup>
              <thead>
                <tr>
                  <th style={{textAlign:'center'}}>Index</th>
                  <th>Expense Details</th>
                  <th className="num">Estimate</th>
                  <th className="num">Actual</th>
                  <th className="num">Variance</th>
                  <th className="num">Cost Variance</th>
                  <th style={{textAlign:'center'}}>Clearance</th>
                </tr>
              </thead>
              <tbody>
                {ftRows.map((r,i)=>{
                  const varc=r.actual-r.estimate;
                  const isOpen=expanded.has(r.group);
                  return (
                  <React.Fragment key={r.group}>
                  <tr onClick={()=>toggleExp(r.group)} style={{animation:`fadein .25s ${Math.min(i,30)*30}ms both`,cursor:'pointer',transition:'background .15s'}}>
                    <td className="mono" style={{padding:'6px 16px',lineHeight:'26px',textAlign:'center',color:'var(--ink)',fontWeight:400}}>{ftStart+i+1}</td>
                    <td className="thai" style={{padding:'6px 16px',lineHeight:'26px',fontWeight:400,color:'var(--ink)'}}>
                      <span style={{display:'inline-flex',alignItems:'center',gap:10,minWidth:0}}>
                        <span style={{display:'grid',placeItems:'center',width:20,height:20,flex:'none',boxSizing:'border-box',borderRadius:'50%',background:isOpen?'#eef1fb':'transparent',boxShadow:isOpen?'none':'inset 0 0 0 1px #dfe3ee',color:isOpen?'#1f2a8e':'#9aa3ba',transition:'all .2s cubic-bezier(.4,0,.2,1)'}}>
                          <svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.4" strokeLinecap="round" style={{display:'block',gridArea:'1/1',transform:isOpen?'rotate(135deg)':'rotate(0deg)',transition:'transform .25s cubic-bezier(.4,0,.2,1)'}}><line x1="6" y1="12" x2="18" y2="12"/><line x1="12" y1="6" x2="12" y2="18"/></svg>
                        </span>
                        <span title={r.group}>{r.group}</span>
                        <span style={{flex:'none',display:'inline-block',textAlign:'center',minWidth:26,fontSize:10,fontWeight:400,color:'#9aa3ba',background:'transparent',border:'1px solid #e6e9f2',borderRadius:9,padding:'0 8px',lineHeight:'17px',letterSpacing:'.01em',fontFamily:'var(--mono)'}}>{r.items.length}</span>
                      </span>
                    </td>
                    <td className="num mono" style={{padding:'6px 16px',lineHeight:'26px',fontWeight:r.estimate===0?400:700,color:r.estimate===0?'#c3c9da':'#0f766e'}}>{fmt(r.estimate)}</td>
                    <td className="num mono" style={{padding:'6px 16px',lineHeight:'26px',fontWeight:r.actual===0?400:700,color:r.actual===0?'#c3c9da':'#1d4ed8'}}>{fmt(r.actual)}</td>
                    <VarianceCells estimate={r.estimate} actual={r.actual} dense/>
                    <td style={{padding:'6px 16px',lineHeight:'26px',textAlign:'center'}}><StatusChip estimate={r.estimate} actual={r.actual}/></td>
                  </tr>
                  {isOpen && r.items.map((it,j)=>{
                  const dv=it.amount-it.estimate;
                  return (
                  <tr key={r.group+'-'+j} style={{background:'#fafbfd',animation:'fadein .18s both'}}>
                    <td style={{padding:'3px 16px'}}></td>
                    <td className="thai" style={{paddingLeft:44,paddingTop:3,paddingBottom:3}}>
                      <span style={{display:'flex',gap:6,lineHeight:1.2}}>
                        <span className="mono" style={{fontSize:12,color:'#9aa2b8',fontWeight:400,flex:'none'}}>{j+1}.</span>
                        <span style={{display:'flex',flexDirection:'column',minWidth:0}}>
                          <span style={{fontSize:12,fontWeight:400,color:'var(--ink)'}}>{it.name}</span>
                          <span style={{fontSize:11.5,color:'var(--muted)',marginTop:2}}>{it.payable?it.payable+' · ':''}{ftDMY(it.date)}</span>
                        </span>
                      </span>
                    </td>
                    <td className="num mono" style={{padding:'3px 16px',fontSize:12,fontWeight:400,color:it.estimate===0?'#b3bcd4':'#0f766e'}}>{fmt(it.estimate)}</td>
                    <td className="num mono" style={{padding:'3px 16px',fontSize:12,fontWeight:400,color:it.amount===0?'#b3bcd4':'#1f2a8e'}}>{fmt(it.amount)}</td>
                    <VarianceCells estimate={it.estimate} actual={it.amount} light/>
                    <td style={{padding:'3px 16px',textAlign:'center'}}><StatusChip estimate={it.estimate} actual={it.amount}/></td>
                  </tr>
                  );
                  })}
                  </React.Fragment>
                  );
                })}
                {ftRows.length===0 && (
                  <tr><td colSpan="7" style={{textAlign:'center',padding:40,color:'var(--muted)'}}>
                    {!datesReady
                      ? <div style={{display:'flex',flexDirection:'column',alignItems:'center',gap:8}}>
                          <I.Cal size={28} stroke="#cfd5e8"/>
                          <div style={{fontWeight:600,color:'var(--ink-2)'}}>กรุณาเลือก Start Date และ End Date</div>
                          <div style={{fontSize:12}}>เลือกช่วงวันที่ที่ต้องการดูข้อมูล Fund Transfers</div>
                        </div>
                      : 'ไม่มีรายการ Submitted ในช่วงวันที่ที่เลือก'}
                  </td></tr>
                )}
              </tbody>
              {datesReady && ftAll.length>0 && (
                  <tfoot>
                    <tr style={{background:'#f4f6fc',fontWeight:800,borderTop:'2px solid var(--line)'}}>
                      <td></td>
                      <td style={{fontWeight:800}}>Total</td>
                      <td className="num mono" style={{fontWeight:800,color:ftTotEst===0?'#c3c9da':'#0f766e'}}>{fmt(ftTotEst)}</td>
                      <td className="num mono" style={{fontWeight:800,color:ftTotAct===0?'#c3c9da':'#1d4ed8'}}>{fmt(ftTotAct)}</td>
                      <VarianceCells estimate={ftTotEst} actual={ftTotAct}/>
                      <td style={{textAlign:'center'}}></td>
                    </tr>
                  </tfoot>
              )}
            </table>
          </div>

          <div style={{display:'flex',justifyContent:'space-between',alignItems:'center',padding:'14px 20px',borderTop:'1px solid var(--line)'}}>
            <div className="muted" style={{fontSize:12.5}}>
              Showing {ftAll.length===0?0:ftStart+1} to {Math.min(ftStart+pageSize, ftAll.length)} of {ftAll.length} types
            </div>
            <div className="row" style={{gap:4}}>
              <button className="btn sm" disabled={page<=1} onClick={()=>setPage(p=>p-1)}>Previous</button>
              {Array.from({length: ftTotalPages}).slice(0,5).map((_,i)=>(
                <button key={i} className={"btn sm"+(page===i+1?' primary':'')} onClick={()=>setPage(i+1)}>{i+1}</button>
              ))}
              <button className="btn sm" disabled={page>=ftTotalPages} onClick={()=>setPage(p=>p+1)}>Next</button>
            </div>
          </div>
        </div>
      </div>

      <div style={{marginTop:18,display:'flex',justifyContent:'space-between',color:'var(--muted)',fontSize:12}}>
        <span>{clock}</span>
        <span>v 2.4.1 · build a91f</span>
      </div>
    </div>
  );
}

window.FundTransfersView = FundTransfersView;
