/* ==========================================================================
 * ADMIN NEXT — page-banks.jsx  (แยกจาก index.html เดิม; โหลดผ่าน <script type="text/babel">)
 * โหลดตามลำดับใน index.html — ทุกไฟล์แชร์ scope เดียวกัน (babel-standalone)
 * 3 หน้าแยก: BankReceivePage, BankPayoutPage, BankCentralPage
 * ========================================================================== */

const BANK_CODE_OPTIONS = [
  ['01002','กรุงเทพ','bbl'],['01004','กสิกรไทย','kbank'],['01006','กรุงไทย','ktb'],
  ['01011','ทหารไทยธนชาต','ttb'],['01014','ไทยพาณิชย์','scb'],['01025','กรุงศรี','bay'],
  ['01069','เกียรตินาคินภัทร','kkp'],['01022','ซีไอเอ็มบี','cimb'],['01067','ทิสโก้','tisco'],
  ['01024','ยูโอบี','uob'],['01071','ไทยเครดิต','tcrb'],['01073','แลนด์ฯ','lh'],
  ['01070','ไอซีบีซี','icbc'],['01034','ธ.ก.ส.','baac'],['01030','ออมสิน','gsb'],
  ['01033','อาคารสงเคราะห์','ghb'],['01066','อิสลาม','isbt'],
];

const METHOD_LABELS_NX = {
  smsgateway:'SMS · เลขบัญชี 4 หลัก', bank_auto:'SMS · เลขบัญชี 4 หลัก', slip:'อัพโหลดสลิป',
  angpao:'ซองของขวัญ', tmnone:'TrueMoney (TMN)', sms:'SMS · ทศนิยม',
  truewallet_webhook:'TrueMoney (Webhook)', lineconnect:'LINE Connect',
};
const BANK_DEPOSIT_METHODS = [
  ['smsgateway','ฝากเงิน (SMSGATEWAY)'],['sms','ฝากเงิน (SMSGATEWAY KBANK ทศนิยม)'],['slip','อัพโหลดสลิป'],['lineconnect','LINE Connect (แจ้งเตือนธนาคารผ่าน LINE)'],
];
/* SMS ธนาคารมี 2 วิธีจับคู่ เก็บเป็น deposit_methods ตัวเดียวกับของเดิม
   smsgateway = jobs/bank-auto-deposit.js, sms = jobs/sms-deposit-confirm.js + หน้าฝากทศนิยมฝั่งลูกค้า */
const BANK_METHOD_GROUPS = [
  ['smsgroup','ฝากเงินผ่าน SMS ธนาคาร'],['slip','อัพโหลดสลิป'],['lineconnect','LINE Connect (แจ้งเตือนธนาคารผ่าน LINE)'],
];
const BANK_SMS_MATCH_MODES = [
  ['smsgateway','จับจากเลขบัญชี 4 หลัก','ลูกค้าโอนยอดเท่าไรก็ได้ ระบบเทียบเลขบัญชีผู้โอน 4 หลักท้ายใน SMS กับเลขบัญชีที่ลูกค้าสมัครไว้ แล้วเติมให้อัตโนมัติ'],
  ['sms','จับจากทศนิยม','ลูกค้ากรอกยอดในหน้าฝาก ระบบสุ่มเศษสตางค์ (.01-.99) ให้ แล้วจับคู่จากยอดใน SMS ที่ตรงเป๊ะ'],
];
const isBankSmsMethod = (m) => m === "smsgateway" || m === "sms";

function SmsMatchModePicker({ value, onChange }) {
  return (<div role="radiogroup" className="grid grid-cols-1 gap-2 sm:grid-cols-2">
    {BANK_SMS_MATCH_MODES.map(([k,title,desc])=>{
      const on=value===k;
      return (<button key={k} type="button" role="radio" aria-checked={on} onClick={()=>onChange(k)}
        className={"flex items-start gap-2.5 rounded-xl border p-3 text-left transition-colors duration-150 "+(on?"border-accent/50 bg-accent-soft":"border-line bg-sunken hover:bg-surface")}>
        <span className={"mt-0.5 grid h-4 w-4 shrink-0 place-items-center rounded-full border-2 "+(on?"border-accent":"border-line")}>
          {on&&<span className="h-2 w-2 rounded-full bg-accent"/>}
        </span>
        <span className="min-w-0">
          <span className={"block text-sm font-semibold "+(on?"text-accent":"text-fg")}>{title}</span>
          <span className="mt-0.5 block text-[11px] leading-relaxed text-faint">{desc}</span>
        </span>
      </button>);
    })}
  </div>);
}

// อ่านวิธีฝากให้รองรับทั้ง PostgreSQL array, JSON array string และค่ารูปแบบเดิม
// ป้องกัน modal กลับไปใช้ค่าเริ่มต้น SMS เมื่อ API ส่ง deposit_methods มาเป็น string
function normalizeBankDepositMethod(value) {
  let methods = [];
  if (Array.isArray(value)) {
    methods = value;
  } else if (typeof value === "string") {
    const raw = value.trim();
    if (raw.startsWith("[")) {
      try {
        const parsed = JSON.parse(raw);
        if (Array.isArray(parsed)) methods = parsed;
      } catch (_) {}
    }
    if (!methods.length && raw) {
      const unwrapped = raw.startsWith("{") && raw.endsWith("}")
        ? raw.slice(1, -1)
        : raw;
      methods = unwrapped.split(",");
    }
  }
  const aliases = {
    bank_auto: "smsgateway",
    sms_gateway: "smsgateway",
    upload_slip: "slip",
    "upload-slip": "slip",
    bank_slip: "slip",
  };
  const supported = BANK_DEPOSIT_METHODS.map(([key]) => key);
  const normalized = methods
    .map((m) => String(m == null ? "" : m).trim().replace(/^["']|["']$/g, "").toLowerCase())
    .filter(Boolean)
    .map((m) => aliases[m] || m);
  return normalized.find((m) => supported.includes(m)) || "smsgateway";
}
const WALLET_DEPOSIT_METHODS = [
  ['truewallet_webhook','TrueMoney (WebHook)'],['tmnone','TrueMoney (TMN)'],['angpao','TrueWallet ซองของขวัญ'],
];

/* ---------- Shared modal pieces (layout ตาม UI เก่า) ---------- */
const M_INP="h-10 w-full min-w-0 rounded-lg border border-line bg-surface px-3 text-sm text-fg placeholder:text-faint focus:border-accent focus:outline-none";
function ModalFrame({ title, onClose, children, footer, note, maxW="max-w-[480px]" }) {
  useEffect(()=>{const h=(e)=>e.key==="Escape"&&onClose(); document.addEventListener("keydown",h); return ()=>document.removeEventListener("keydown",h);},[onClose]);
  // portal ไป body กัน parent ที่มี transform/animation ทำให้ fixed เพี้ยนตำแหน่ง
  return ReactDOM.createPortal(<div className="fixed inset-0 z-50 flex items-center justify-center p-4 fade-in" onMouseDown={onClose}>
    <div className="absolute inset-0 bg-black/55"/>
    <div className={"pop relative flex max-h-[90vh] w-full flex-col rounded-2xl border border-line bg-surface2 shadow-lg "+maxW} onMouseDown={e=>e.stopPropagation()}>
      <div className="border-b border-line px-5 py-4"><h3 className="flex items-center gap-2 text-[15px] font-semibold">{title}</h3></div>
      <div className="min-h-0 flex-1 space-y-3 overflow-y-auto px-5 py-4">{children}</div>
      <div className="space-y-3 border-t border-line px-5 py-4">
        <div className="flex gap-2">{footer}</div>
        {note&&<div className="flex items-start gap-2 rounded-lg border border-line bg-warn-soft px-3 py-2 text-xs text-warn"><span className="font-semibold">หมายเหตุ:</span><span className="min-w-0">{note}</span></div>}
      </div>
    </div>
  </div>, document.body);
}
function MField({ label, hint, children }) {
  return (<div className="min-w-0">
    <label className="mb-1 block text-xs font-semibold text-fg-2">{label}{hint&&<span className="ml-1 font-normal text-faint">({hint})</span>}</label>
    {children}
  </div>);
}
// HH:MM แบบพิมพ์เอง (เหมือน UI เก่า) — รับ "0800" / "8:00" แล้วจัดรูปให้ตอน blur
function normTime(v){
  const t=String(v||"").trim(); if(!t) return "";
  const m=t.match(/^(\d{1,2}):?(\d{2})$/); if(!m) return t;
  const h=+m[1], mi=+m[2]; if(h>23||mi>59) return t;
  return String(h).padStart(2,"0")+":"+String(mi).padStart(2,"0");
}
function TimeRange({ from, to, onFrom, onTo, phFrom="00:00 (เช่น 08:00)", phTo="23:59 (เช่น 18:00)" }) {
  const box=(val,on,ph)=>(<input type="text" inputMode="numeric" maxLength={5} value={val} placeholder={ph}
    onChange={e=>on(e.target.value.replace(/[^0-9:]/g,""))} onBlur={e=>on(normTime(e.target.value))} className={M_INP+" flex-1"}/>);
  return (<div className="flex items-center gap-2.5">{box(from,onFrom,phFrom)}<span className="shrink-0 text-sm text-muted">ถึง</span>{box(to,onTo,phTo)}</div>);
}
function ModalButtons({ onSave, onClose, saving, disabled }) {
  return (<>
    <button onClick={onSave} disabled={saving||disabled} className="h-10 flex-1 rounded-lg bg-accent text-sm font-semibold text-accent-fg hover:bg-accent-h disabled:opacity-50">{saving?"กำลังบันทึก…":"บันทึก"}</button>
    <button onClick={onClose} className="h-10 flex-1 rounded-lg border border-line bg-surface text-sm font-medium text-fg-2 hover:bg-sunken">ยกเลิก</button>
  </>);
}

function MethodTags({ methods, type }) {
  const arr = Array.isArray(methods) ? methods : [];
  if (!arr.length) return <span className="text-[11px] text-faint">-</span>;
  return (<div className="flex flex-wrap gap-1">{arr.map(m=>(
    <span key={m} className="inline-flex items-center rounded-full bg-info-soft px-2 py-0.5 text-[10px] font-medium text-info">
      {METHOD_LABELS_NX[m]||m}
    </span>))}</div>);
}

function ScheduleDisplay({ from, to }) {
  if (!from || !to) return <span className="text-[11px] text-info">24 ชม.</span>;
  return <span className="text-[11px] text-warn font-medium">{String(from).slice(0,5)}–{String(to).slice(0,5)}</span>;
}

function TmnKeyBadge({ a }) {
  const isTmn = a.type==='TMNDepo' || (Array.isArray(a.deposit_methods) && a.deposit_methods.includes('tmnone'));
  if (!isTmn) return null;
  const ok = !!(a.tmnone_key_id && a.tmnone_login_token && a.tmnone_pin && a.tmnone_tmn_id);
  return ok ? <Badge tone="success">TMN key ✓</Badge> : <Badge tone="warn">TMN fallback</Badge>;
}

/* ---------- Card: บัญชีรับเงิน ---------- */
// เครื่องที่ผูกไว้ / เครื่องที่ส่ง SMS ล่าสุด — แดงเมื่อ SMS ล่าสุดไม่ได้มาจากเครื่องหรือผู้ส่งที่ผูก
function SmsSourceLine({ a }) {
  const bound=[a.sms_device_id,a.sms_sender].filter(Boolean).join(" · ");
  const last=[a.sms_last_device,a.sms_last_sender].filter(Boolean).join(" · ");
  if(!bound&&!last) return null;
  const mismatch=(a.sms_device_id&&a.sms_last_device&&a.sms_device_id!==a.sms_last_device)
    ||(a.sms_sender&&a.sms_last_sender&&a.sms_sender!==a.sms_last_sender);
  return (<div className="mt-1 flex flex-wrap items-center gap-1.5 text-[11px] text-faint">
    {bound?<span>ผูกเครื่อง <span className="mono text-fg-2">{bound}</span></span>:<span>SMS ล่าสุดจาก <span className="mono text-fg-2">{last}</span></span>}
    {mismatch&&<Badge tone="danger">SMS ล่าสุดมาจาก {last}</Badge>}
  </div>);
}

function AccountStatCard({ a, onEdit, onToggle, onToggleFrontend, onDelete, canWrite }) {
  const wallet = String(a.type||"").toLowerCase()!=="bank" && !['LINEAbd'].includes(a.type);
  const isLine = a.type==='LINEAbd';
  const number = a.account_no || a.wallet_phone || "-";
  return (<div className={"rounded-xl border border-line bg-surface p-3.5 shadow-sm"+(a.is_active?"":" opacity-60")}>
    <div className="flex items-center gap-2.5">
      <BankLogo code={isLine?"line":(wallet?"truewallet":a.bank_code)} size={32}/>
      <div className="min-w-0 flex-1">
        <div className="truncate text-sm font-semibold">{a.account_name||number}</div>
        <div className="truncate mono text-[11px] text-faint">{number}{isLine&&a.bank_name?" · "+a.bank_name:""} · {ACCT_TYPE_LABEL[a.type]||a.type}</div>
      </div>
      <div className="flex flex-col items-end gap-1">
        <button onClick={()=>onToggle(a)} disabled={!canWrite}>{a.is_active ? <Badge tone="success">เปิด</Badge> : <Badge tone="danger">ปิด</Badge>}</button>
        <button onClick={()=>onToggleFrontend(a)} disabled={!canWrite}>{a.show_on_frontend!==false ? <Badge tone="success">แสดง</Badge> : <Badge tone="warn">ซ่อน</Badge>}</button>
      </div>
    </div>
    <div className="mt-2 flex flex-wrap items-center gap-1.5">
      <MethodTags methods={a.deposit_methods} type={a.type}/>
      <TmnKeyBadge a={a}/>
      {isLine && a.line_user_id && <Badge tone="success">LINE</Badge>}
      {a.sort_order>0 && <span className="text-[10px] text-faint">ลำดับ {a.sort_order}</span>}
    </div>
    <div className="mt-2 text-[11px] text-faint"><ScheduleDisplay from={a.active_from} to={a.active_to}/></div>
    <div className="mt-3 grid grid-cols-3 gap-2 text-center">
      <div className="rounded-lg bg-sunken p-2"><div className="text-[10px] text-muted">วันนี้</div><div className="mt-0.5 text-sm font-semibold tnum">{money2(a.deposit_total_today)}</div><div className="text-[10px] text-faint">{N(a.deposit_count_today)} รายการ</div></div>
      <div className="rounded-lg bg-sunken p-2"><div className="text-[10px] text-muted">เมื่อวาน</div><div className="mt-0.5 text-sm font-semibold tnum">{money2(a.deposit_total_yesterday)}</div><div className="text-[10px] text-faint">{N(a.deposit_count_yesterday)} รายการ</div></div>
      <div className="rounded-lg bg-sunken p-2"><div className="text-[10px] text-muted">สะสม</div><div className="mt-0.5 text-sm font-semibold tnum">{money2(a.deposit_total)}</div><div className="text-[10px] text-faint">{N(a.deposit_count)} รายการ</div></div>
    </div>
    {a.sms_last_balance!=null && <div className="mt-2 text-[11px] text-info">ยอดคงเหลือ (SMS) {money2(a.sms_last_balance)}</div>}
    <SmsSourceLine a={a}/>
    {canWrite&&<div className="mt-2 flex items-center justify-end gap-2">
      <button onClick={()=>onEdit(a)} className="text-xs text-accent hover:underline">แก้ไข</button>
      <button onClick={()=>onDelete(a)} className="text-xs text-danger hover:underline">ลบ</button>
    </div>}
  </div>);
}

/* ---------- Card: บัญชีกลาง ---------- */
function CentralAccountCard({ a, onEdit, onToggle, onDelete, canWrite }) {
  const wallet = String(a.type||"").toLowerCase()==="wallet";
  const number = wallet ? (a.wallet_phone||"-") : (a.account_no||"-");
  return (<div className="rounded-xl border border-line bg-surface p-3.5 shadow-sm">
    <div className="flex items-center gap-2.5">
      <BankLogo code={wallet?"truewallet":a.bank_code} size={32}/>
      <div className="min-w-0 flex-1"><div className="truncate text-sm font-semibold">{a.account_name||a.bank_name||number}</div><div className="truncate mono text-[11px] text-faint">{number}{a.note?" · "+a.note:""}</div></div>
      <button onClick={()=>onToggle(a)} disabled={!canWrite}>{a.is_active ? <Badge tone="success">เปิด</Badge> : <Badge tone="danger">ปิด</Badge>}</button>
    </div>
    {canWrite&&<div className="mt-2 flex items-center justify-end gap-2">
      <button onClick={()=>onEdit(a)} className="text-xs text-accent hover:underline">แก้ไข</button>
      <button onClick={()=>onDelete(a)} className="text-xs text-danger hover:underline">ลบ</button>
    </div>}
  </div>);
}

/* ---------- Card: บัญชีถอน ---------- */
function PayoutAccountCard({ a, onEdit, onToggle, onDelete, onRefreshBal, canWrite, balance }) {
  const isWallet = ['wallet','TMNDepo','WHdepo','TMNWD'].includes(a.type);
  const number = isWallet ? (a.wallet_phone||"-") : (a.account_no||"-");
  const typeLabel = a.type==='TMNWD'?'TMN ถอน (TMNWD)':isWallet?'วอลเล็ท':'ธนาคาร';
  return (<div className={"rounded-xl border border-line bg-surface p-3.5 shadow-sm"+(a.is_active?"":" opacity-60")}>
    <div className="flex items-center gap-2.5">
      <BankLogo code={isWallet?"truewallet":a.bank_code} size={32}/>
      <div className="min-w-0 flex-1">
        <div className="truncate text-sm font-semibold">{a.account_name||number}</div>
        <div className="truncate mono text-[11px] text-faint">{number} · {typeLabel}</div>
      </div>
      <button onClick={()=>onToggle(a)} disabled={!canWrite}>{a.is_active ? <Badge tone="success">เปิด</Badge> : <Badge tone="danger">ปิด</Badge>}</button>
    </div>
    {a.type==='TMNWD'&&(()=>{
      const ok=!!(a.tmnone_key_id&&a.tmnone_login_token&&a.tmnone_pin&&a.tmnone_tmn_id);
      return <div className="mt-1.5">{ok?<Badge tone="success">TMN key ✓</Badge>:<Badge tone="warn">TMN fallback</Badge>}</div>;
    })()}
    <div className="mt-3 grid grid-cols-2 gap-2 text-center">
      <div className="rounded-lg bg-sunken p-2"><div className="text-[10px] text-muted">ถอน 24 ชม.</div><div className="mt-0.5 text-sm font-semibold tnum">{money2(a.withdraw_total_24h)}</div></div>
      <div className="rounded-lg bg-sunken p-2"><div className="text-[10px] text-muted">ถอนรวม</div><div className="mt-0.5 text-sm font-semibold tnum">{money2(a.withdraw_total)}</div></div>
    </div>
    {isWallet&&<div className="mt-2 flex items-center gap-2 text-[11px]">
      <span className="text-muted">ยอดคงเหลือ:</span>
      {balance===undefined?<span className="text-faint"><Ic d={I.refresh} size={12} cls="animate-spin inline"/></span>
       :balance===null?<span className="text-danger">เรียกไม่ได้</span>
       :<span className="font-semibold text-info">{(Number(balance)||0).toLocaleString("th-TH",{minimumFractionDigits:2,maximumFractionDigits:2})} บาท</span>}
      <button onClick={()=>onRefreshBal(a.id)} className="text-accent hover:underline text-[10px]">รีเฟรช</button>
    </div>}
    {a.note&&<div className="mt-1 text-[11px] text-faint">หมายเหตุ: {a.note}</div>}
    {canWrite&&<div className="mt-2 flex items-center justify-end gap-2">
      <button onClick={()=>onEdit(a)} className="text-xs text-accent hover:underline">แก้ไข</button>
      <button onClick={()=>onDelete(a)} className="text-xs text-danger hover:underline">ลบ</button>
    </div>}
  </div>);
}

/* ---------- ตัวอย่างสมุดบัญชี / หน้าบัญชีอิเล็กทรอนิกส์ (ช่วยแอดมินกรอกชื่อบนสลิป) ---------- */
const PASSBOOK_BANKS = {
  "01002":["Bangkok Bank","#1e4598"], "01004":["KASIKORNBANK","#138f2d"], "01006":["Krungthai","#1ba5e1"],
  "01011":["ttb","#0050f0"], "01014":["SCB","#4e2e7f"], "01025":["Krungsri","#fec43b"],
  "01069":["KKP","#635b8f"], "01022":["CIMB Thai","#7e2f36"], "01067":["TISCO","#12549f"],
  "01024":["UOB","#0b3b8c"], "01071":["Thai Credit","#f7941d"], "01073":["LH Bank","#6d6e70"],
  "01070":["ICBC (Thai)","#c50f1f"], "01034":["BAAC","#4b9b3d"], "01030":["GSB","#eb198d"],
  "01033":["GH Bank","#f58025"], "01066":["Islamic Bank","#184d23"],
};
function PassbookSampleModal({ bankCode, onClose }) {
  // capture + stopImmediatePropagation: Esc ปิดแค่ภาพตัวอย่าง ไม่ปิด modal ฟอร์มที่อยู่ข้างใต้
  useEffect(()=>{const h=(e)=>{if(e.key==="Escape"){e.stopImmediatePropagation();onClose();}}; document.addEventListener("keydown",h,true); return ()=>document.removeEventListener("keydown",h,true);},[onClose]);
  const code=PASSBOOK_BANKS[bankCode]?bankCode:"01004";
  const [en,color]=PASSBOOK_BANKS[code];
  const th=(BANK_CODE_OPTIONS.find(b=>b[0]===code)||[])[1]||"";
  const row=(lth,len,vth,ven,mono)=>(<div className="flex items-start justify-between gap-3">
    <div className="shrink-0"><div className="text-[15px] font-semibold text-slate-800">{lth}</div><div className="text-xs text-slate-500">{len}</div></div>
    <div className="min-w-0 text-right"><div className={"text-[15px] font-semibold text-slate-800"+(mono?" tnum":"")}>{vth}</div><div className="text-xs font-medium text-slate-600">{ven}</div></div>
  </div>);
  return ReactDOM.createPortal(<div className="fixed inset-0 z-[60] flex items-center justify-center p-4 fade-in" onMouseDown={onClose}>
    <div className="absolute inset-0 bg-black/55"/>
    <div className="pop relative flex max-h-[92vh] w-full max-w-[400px] flex-col rounded-2xl border border-line bg-surface2 shadow-lg" onMouseDown={e=>e.stopPropagation()}>
      <div className="flex items-start gap-3 px-5 pt-4 pb-2">
        <h3 className="flex-1 text-[15px] font-semibold">ตัวอย่างสมุดบัญชี / หน้าบัญชีอิเล็กทรอนิกส์</h3>
        <button onClick={onClose} className="-mr-1 grid h-7 w-7 shrink-0 place-items-center rounded-md border border-line text-muted hover:bg-sunken" aria-label="ปิด"><Ic d={I.x} size={14}/></button>
      </div>
      <div className="min-h-0 flex-1 overflow-y-auto px-5 pb-4">
        <p className="mb-3 text-xs leading-relaxed text-muted">กรอกช่อง <b className="text-fg">ภาษาไทย</b> ตามบรรทัด <b className="text-fg">ชื่อบัญชี</b> และช่อง <b className="text-fg">ภาษาอังกฤษ</b> ตามบรรทัด <b className="text-fg">Account Name</b> ให้ตรงตัวสะกดของธนาคาร รวมคำนำหน้า (นาย / MR.)</p>
        <div className="overflow-hidden rounded-xl border border-line shadow-sm">
          <div className="relative h-[150px] overflow-hidden" style={{background:"linear-gradient(180deg,#bfe9f3 0%,#e3f6fa 70%,#f4fbfd 100%)"}}>
            <div className="absolute right-0 top-3 flex items-center gap-2 rounded-l-md py-1.5 pl-3 pr-2 text-white" style={{background:color}}>
              <div className="text-right leading-tight"><div className="text-[12px] font-bold">ธนาคาร{th}</div><div className="text-[8px] font-medium uppercase tracking-wide opacity-90">{en}</div></div>
              <BankLogo code={code} size={26}/>
            </div>
            <div className="absolute -left-6 top-12 h-10 w-28 rounded-full bg-white/80 blur-[1px]"/>
            <div className="absolute left-16 top-6 h-8 w-20 rounded-full bg-white/70 blur-[1px]"/>
            <div className="absolute -bottom-8 left-0 right-0 h-16 rounded-[100%] bg-white/90"/>
            <div className="absolute bottom-6 left-5">
              <div className="text-[17px] font-semibold text-slate-800">บัญชีเงินฝากออมทรัพย์อิเล็กทรอนิกส์</div>
              <div className="text-[12px] text-slate-600">e-Savings Account</div>
            </div>
          </div>
          <div className="space-y-4 px-5 py-5" style={{background:"repeating-linear-gradient(135deg,#f6f7f5 0 6px,#f0f2ef 6px 7px)"}}>
            {row("ชื่อบัญชี:","Account Name:","นาย สมชาย ใจดี","MR. SOMCHAI JAIDEE")}
            {row("เลขที่บัญชี:","Account No.:","268-2-87878-2","",true)}
            {row("สาขา:","Branch:","สาขาสยามพารากอน","Siam Paragon Branch")}
          </div>
        </div>
        <p className="mt-2 text-[11px] text-faint">ภาพตัวอย่างเท่านั้น ชื่อและเลขบัญชีในภาพเป็นข้อมูลสมมติ</p>
      </div>
    </div>
  </div>, document.body);
}

/* ---------- Modal: บัญชีธนาคาร ---------- */
/* ---------- ผูกบัญชีกับเครื่องส่ง SMS + ผู้ส่ง SMS (ตัวเลือกมาจาก SMS ที่เข้ามาจริง 30 วัน) ---------- */
function smsSourceOptions(sources, key, current) {
  const byValue=new Map();
  for(const s of sources){
    const v=s[key]; if(!v) continue;
    const prev=byValue.get(v)||{value:v,count:0,last_at:null,for_account:false};
    prev.count+=Number(s.count||0);
    prev.for_account=prev.for_account||!!s.for_account;
    if(!prev.last_at||s.last_at>prev.last_at) prev.last_at=s.last_at;
    byValue.set(v,prev);
  }
  const list=[...byValue.values()].sort((a,b)=>(b.for_account-a.for_account)||(b.count-a.count));
  if(current&&!byValue.has(current)) list.unshift({value:current,count:0,last_at:null,for_account:true,stale:true});
  return list;
}

function SmsSourceSelect({ label, hint, value, onChange, options }) {
  const label1=(o)=>o.stale?`${o.value} (ผูกไว้เดิม ไม่มี SMS ใน 30 วัน)`:`${o.value} · ${N(o.count)} รายการ · ล่าสุด ${fmtDT(o.last_at)}`;
  const mine=options.filter(o=>o.for_account), others=options.filter(o=>!o.for_account);
  return (<MField label={label} hint={hint}>
    <select value={value} onChange={e=>onChange(e.target.value)} className={M_INP}>
      <option value="">— ไม่ผูก —</option>
      {mine.length>0&&<optgroup label="ส่ง SMS เข้าบัญชีนี้">{mine.map(o=><option key={o.value} value={o.value}>{label1(o)}</option>)}</optgroup>}
      {others.length>0&&<optgroup label="เครื่อง/ผู้ส่งอื่น (30 วัน)">{others.map(o=><option key={o.value} value={o.value}>{label1(o)}</option>)}</optgroup>}
    </select></MField>);
}

function SmsSourceFields({ accountNo, device, sender, onDevice, onSender }) {
  const [sources,setSources]=useState(null);
  useEffect(()=>{
    let alive=true;
    const t=setTimeout(async()=>{
      try{ const d=await api("/deposit-accounts/sms-sources?accountNo="+encodeURIComponent(accountNo||"")); if(alive) setSources(Array.isArray(d)?d:[]); }
      catch{ if(alive) setSources([]); }
    },400);
    return ()=>{ alive=false; clearTimeout(t); };
  },[accountNo]);
  const devices=useMemo(()=>smsSourceOptions(sources||[],"device_id",device),[sources,device]);
  const senders=useMemo(()=>smsSourceOptions(sources||[],"sender",sender),[sources,sender]);
  return (<div className="rounded-xl border border-dashed border-line p-3">
    <div className="text-xs font-semibold text-fg-2">เครื่องที่ส่ง SMS เข้าบัญชีนี้ <span className="font-normal text-faint">(ไม่บังคับ)</span></div>
    <div className="mt-0.5 text-[11px] leading-relaxed text-faint">ใช้ดูว่ายอดเข้าบัญชีนี้มาจากเครื่องไหน ถ้า SMS มาจากเครื่องหรือผู้ส่งอื่น หน้าประวัติ SMS จะขึ้น "ไม่ตรงที่ผูก"</div>
    {sources===null ? <div className="mt-2.5 skel h-10 w-full"/>
    : sources.length===0 ? <div className="mt-2.5 text-[11px] text-faint">ยังไม่มี SMS เข้ามาใน 30 วัน</div>
    : <div className="mt-2.5 grid grid-cols-1 gap-3 sm:grid-cols-2">
        <SmsSourceSelect label="เครื่อง (deviceID)" value={device} onChange={onDevice} options={devices}/>
        <SmsSourceSelect label="ผู้ส่ง SMS" hint="เบอร์/ชื่อที่ธนาคารใช้ส่ง" value={sender} onChange={onSender} options={senders}/>
      </div>}
  </div>);
}

function BankEditModal({ data, onSave, onClose, saving }) {
  const [f,setF]=useState(()=>({
    id:data.id||"", bank_code:data.bank_code||"01004", account_no:data.account_no||"",
    account_name:data.account_name||"", account_name_th:data.account_name_th||"", account_name_en:data.account_name_en||"",
    deposit_method:normalizeBankDepositMethod(data.deposit_methods ?? data.depositMethods ?? data.deposit_method),
    active_from:data.active_from?String(data.active_from).slice(0,5):"",
    active_to:data.active_to?String(data.active_to).slice(0,5):"",
    sms_active_from:data.sms_active_from?String(data.sms_active_from).slice(0,5):"",
    sms_active_to:data.sms_active_to?String(data.sms_active_to).slice(0,5):"",
    angpao_active_from:data.angpao_active_from?String(data.angpao_active_from).slice(0,5):"",
    angpao_active_to:data.angpao_active_to?String(data.angpao_active_to).slice(0,5):"",
    sort_order:data.sort_order||0,
    line_match_mode:data.line_match_mode||"decimal",
    user_bank_account:data.user_bank_account||"",
    user_phone:data.user_phone||"",
    sms_device_id:data.sms_device_id||"",
    sms_sender:data.sms_sender||"",
  }));
  const [sample,setSample]=useState(false);
  const set=(k,v)=>setF(p=>({...p,[k]:v}));
  const submit=()=>{
    const bankOpt=BANK_CODE_OPTIONS.find(b=>b[0]===f.bank_code);
    onSave({
      id:f.id||undefined, type:"bank", bank_code:f.bank_code,
      bank_name:bankOpt?bankOpt[1]:f.bank_code,
      account_no:f.account_no, account_name:f.account_name,
      account_name_th:f.account_name_th.trim(), account_name_en:f.account_name_en.trim().toUpperCase(),
      deposit_methods:[f.deposit_method], sort_order:+f.sort_order||0,
      active_from:f.active_from||null, active_to:f.active_to||null,
      sms_active_from:f.sms_active_from||null, sms_active_to:f.sms_active_to||null,
      angpao_active_from:f.angpao_active_from||null, angpao_active_to:f.angpao_active_to||null,
      line_match_mode:f.line_match_mode, user_bank_account:f.user_bank_account||null, user_phone:f.user_phone||null,
      sms_device_id:f.sms_device_id||null, sms_sender:f.sms_sender||null,
    });
  };
  const showSms=f.deposit_method==="sms";
  const showAngpao=f.deposit_method==="smsgateway"||f.deposit_method==="bank_auto";
  const showLine=f.deposit_method==="lineconnect";
  const inp=M_INP;
  return (<ModalFrame title={(f.id?"แก้ไข":"เพิ่ม")+"บัญชีธนาคาร"} onClose={onClose} maxW="max-w-[560px]"
    footer={<ModalButtons onSave={submit} onClose={onClose} saving={saving}/>}
    note="หากยังไม่เชื่อมต่อ API จะไม่แสดงข้อมูลธนาคารที่หน้าฝากเล่น">
    <MField label="ธนาคาร">
      <div className="flex items-center gap-2"><BankLogo key={f.bank_code} code={f.bank_code} size={40}/>
      <select value={f.bank_code} onChange={e=>set("bank_code",e.target.value)} className={inp}>
        {BANK_CODE_OPTIONS.map(([c,n])=><option key={c} value={c}>{n}</option>)}
      </select></div></MField>
    <MField label="เลขบัญชี"><input value={f.account_no} onChange={e=>set("account_no",e.target.value)} placeholder="เลขบัญชี" className={inp}/></MField>
    <MField label="ชื่อบัญชี" hint="ตามสมุดบัญชี"><input value={f.account_name} onChange={e=>set("account_name",e.target.value)} placeholder="ชื่อ-นามสกุล" className={inp}/></MField>
    <div className="rounded-xl border border-dashed border-line p-3">
      <div className="flex items-start gap-2">
        <div className="min-w-0 flex-1 text-xs font-semibold text-fg-2">ชื่อบัญชีตามที่ธนาคารแสดงบนสลิป <span className="font-normal text-faint">(ไม่บังคับ)</span></div>
        <button type="button" onClick={()=>setSample(true)} className="flex shrink-0 items-center gap-1 rounded-md bg-info-soft px-2 py-1 text-[11px] font-medium text-info hover:opacity-80"><Ic d={["M5 4h14a1 1 0 0 1 1 1v14a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1V5a1 1 0 0 1 1-1z","M4 16l5-5 4 4 2-2 5 5","M15.5 8.5h.01"]} size={13}/> ดูตัวอย่างสมุดบัญชี</button>
      </div>
      <div className="mt-1.5 text-xs font-semibold text-danger">เพิ่มโอกาสสำเร็จอย่างมาก เมื่อใส่ทั้งภาษาไทยและอังกฤษ</div>
      <div className="mt-0.5 text-[11px] leading-relaxed text-faint">ระบบใช้ชื่อเหล่านี้ตรวจสลิปโดยเทียบกับชื่อบนสลิปในภาษาเดียวกัน กรอกให้ตรงกับที่ธนาคารสะกดจริง รวมคำนำหน้า จะช่วยลดสลิปที่ตกเทียบชื่อ</div>
      <div className="mt-2.5 grid grid-cols-1 gap-3 sm:grid-cols-2">
        <MField label="ภาษาไทย"><input value={f.account_name_th} onChange={e=>set("account_name_th",e.target.value)} placeholder="เช่น นาย สมชาย ใจดี" className={inp}/></MField>
        <MField label="ภาษาอังกฤษ"><input value={f.account_name_en} onChange={e=>set("account_name_en",e.target.value)} placeholder="เช่น MR. SOMCHAI JAIDEE" className={inp+" uppercase placeholder:normal-case"}/></MField>
      </div>
    </div>
    {sample&&<PassbookSampleModal bankCode={f.bank_code} onClose={()=>setSample(false)}/>}
    <MField label="ลำดับแสดง" hint="ตัวเลขน้อย = ขึ้นก่อน"><input type="number" min="0" value={f.sort_order} onChange={e=>set("sort_order",e.target.value)} className={inp}/></MField>
    <MField label="รูปแบบฝากที่รองรับ">
      <select value={isBankSmsMethod(f.deposit_method)?"smsgroup":f.deposit_method}
        onChange={e=>set("deposit_method",e.target.value==="smsgroup"?"smsgateway":e.target.value)} className={inp}>
        {BANK_METHOD_GROUPS.map(([k,l])=><option key={k} value={k}>{l}</option>)}
      </select></MField>
    {isBankSmsMethod(f.deposit_method)&&<MField label="วิธีจับคู่ยอดจาก SMS">
      <SmsMatchModePicker value={f.deposit_method} onChange={v=>set("deposit_method",v)}/></MField>}
    {showLine&&<>
      <MField label="โหมดการฝาก LINE Connect">
        <select value={f.line_match_mode} onChange={e=>set("line_match_mode",e.target.value)} className={inp}>
          <option value="decimal">ทศนิยม — ลูกค้ากรอกยอด ระบบสุ่มเศษสตางค์ จับคู่จากยอดที่ตรงเป๊ะ</option>
          <option value="whole">จำนวนเต็ม — จับคู่จากเลขบัญชีผู้โอน</option>
        </select></MField>
      {f.line_match_mode==="whole"?<>
        <MField label="เลขบัญชี User" hint="ที่จะโอนเข้ามา"><input value={f.user_bank_account} onChange={e=>set("user_bank_account",e.target.value)} placeholder="เช่น 3740947415" maxLength={20} className={inp}/></MField>
        <MField label="เบอร์โทร User" hint="ใช้ค้นหา user ในระบบ"><input value={f.user_phone} onChange={e=>set("user_phone",e.target.value)} placeholder="เช่น 0653815929" inputMode="tel" maxLength={15} className={inp}/></MField>
        <div className="rounded-lg border border-line bg-info-soft p-2.5 text-xs text-info"><b>คำอธิบาย:</b> เมื่อมีการโอนเงินเข้า เช่น จาก MR YANASAKDA X-7415 → ระบบจะ match เลข 7415 กับบัญชี user (3740947415) และเพิ่มเงินให้อัตโนมัติ</div>
      </>:<div className="rounded-lg border border-line bg-success-soft p-2.5 text-xs text-success"><b>โหมดทศนิยม:</b> ลูกค้ากรอกยอดในหน้าฝาก ระบบจะเติมเศษสตางค์ (.01-.99) ให้เป็นยอดเฉพาะ แล้วจับคู่จากยอดที่โอนเข้ามาตรงเป๊ะให้อัตโนมัติ</div>}
    </>}
    <MField label="แสดงในช่วงเวลา (ทั่วไป)" hint="เว้นว่างเพื่อแสดงตลอด 24 ชม.">
      <TimeRange from={f.active_from} to={f.active_to} onFrom={v=>set("active_from",v)} onTo={v=>set("active_to",v)}/></MField>
    {showSms&&<MField label={'ช่วงเวลาเฉพาะ "ฝากทศนิยม"'} hint="ถ้าไม่ระบุจะใช้ช่วงเวลาทั่วไป">
      <TimeRange from={f.sms_active_from} to={f.sms_active_to} onFrom={v=>set("sms_active_from",v)} onTo={v=>set("sms_active_to",v)} phFrom="00:00" phTo="23:59"/></MField>}
    {showAngpao&&<MField label={'ช่วงเวลาเฉพาะ "ฝากเงิน"'} hint="ถ้าไม่ระบุจะใช้ช่วงเวลาทั่วไป">
      <TimeRange from={f.angpao_active_from} to={f.angpao_active_to} onFrom={v=>set("angpao_active_from",v)} onTo={v=>set("angpao_active_to",v)} phFrom="00:00" phTo="23:59"/></MField>}
    {(showSms||showAngpao)&&<SmsSourceFields accountNo={f.account_no} device={f.sms_device_id} sender={f.sms_sender}
      onDevice={v=>set("sms_device_id",v)} onSender={v=>set("sms_sender",v)}/>}
  </ModalFrame>);
}

/* ---------- Modal: วอลเล็ท / TMN ---------- */
function WalletEditModal({ data, onSave, onClose, saving, toast }) {
  const [f,setF]=useState(()=>({
    id:data.id||"", wallet_phone:data.wallet_phone||"", account_name:data.account_name||"",
    deposit_method:(Array.isArray(data.deposit_methods)&&data.deposit_methods[0])||"truewallet_webhook",
    active_from:data.active_from?String(data.active_from).slice(0,5):"",
    active_to:data.active_to?String(data.active_to).slice(0,5):"",
    sort_order:data.sort_order||0,
    angpao_link:(()=>{
      const m=Array.isArray(data.deposit_methods)?data.deposit_methods:[];
      const prim=m[0]||"truewallet_webhook";
      return prim!=="angpao"&&m.includes("angpao");
    })(),
    tmnone_key_id:data.tmnone_key_id||"", tmnone_login_token:data.tmnone_login_token||"",
    tmnone_pin:"", tmnone_tmn_id:data.tmnone_tmn_id||"", tmnone_device_id:data.tmnone_device_id||"",
  }));
  const [testing,setTesting]=useState(false);
  const [testResult,setTestResult]=useState(null);
  const set=(k,v)=>setF(p=>({...p,[k]:v}));
  const isTmn=f.deposit_method==="tmnone";

  const testConnection=async()=>{
    setTesting(true); setTestResult(null);
    try{
      const body={walletPhone:f.wallet_phone,tmnoneKeyId:f.tmnone_key_id||null,tmnoneLoginToken:f.tmnone_login_token||null,
        tmnoneTmnId:f.tmnone_tmn_id||null,tmnoneDeviceId:f.tmnone_device_id||null};
      if(f.tmnone_pin) body.tmnonePin=f.tmnone_pin;
      if(f.id) body.depositAccountId=f.id;
      const r=await api("/tmnone/test-connection",{method:"POST",body});
      setTestResult(r.ok?{ok:true,msg:"เชื่อมต่อสำเร็จ เบอร์ "+(r.mobile||"-")}:{ok:false,msg:r.error||"ไม่สำเร็จ"});
    }catch(e){ setTestResult({ok:false,msg:e.message}); }
    finally{ setTesting(false); }
  };

  const submit=()=>{
    const methods=[f.deposit_method];
    if(f.deposit_method!=="angpao"&&f.angpao_link) methods.push("angpao");
    const body={
      id:f.id||undefined,
      type:f.deposit_method==="tmnone"?"TMNDepo":(f.deposit_method==="truewallet_webhook"?"WHdepo":"wallet"),
      wallet_phone:f.wallet_phone, account_name:f.account_name,
      deposit_methods:methods, sort_order:+f.sort_order||0,
      active_from:f.active_from||null, active_to:f.active_to||null,
    };
    if(isTmn){
      body.tmnone_key_id=f.tmnone_key_id||null; body.tmnone_login_token=f.tmnone_login_token||null;
      body.tmnone_tmn_id=f.tmnone_tmn_id||null; body.tmnone_device_id=f.tmnone_device_id||null;
      if(f.tmnone_pin) body.tmnone_pin=f.tmnone_pin;
    }
    onSave(body);
  };
  const inp=M_INP;
  return (<ModalFrame maxW="max-w-[440px]" title={<><BankLogo code="truewallet" size={22}/> {f.id?"แก้ไข":"เพิ่ม"}บัญชี TrueWallet</>} onClose={onClose}
    footer={<ModalButtons onSave={submit} onClose={onClose} saving={saving}/>}
    note="หากยังไม่เชื่อมต่อ API จะไม่แสดงข้อมูลวอลเล็ทที่หน้าฝากเล่น">
    <MField label="เบอร์บัญชีวอลเล็ท"><input value={f.wallet_phone} onChange={e=>set("wallet_phone",e.target.value)} placeholder="0812345678" inputMode="tel" className={inp}/></MField>
    <MField label="ชื่อ/หมายเหตุ"><input value={f.account_name} onChange={e=>set("account_name",e.target.value)} placeholder="ชื่อบัญชี" className={inp}/></MField>
    <MField label="ลำดับแสดง"><input type="number" min="0" value={f.sort_order} onChange={e=>set("sort_order",e.target.value)} className={inp}/></MField>
    <MField label="รูปแบบฝากที่รองรับ">
      <select value={f.deposit_method} onChange={e=>set("deposit_method",e.target.value)} className={inp}>
        {WALLET_DEPOSIT_METHODS.map(([k,l])=><option key={k} value={k}>{l}</option>)}
      </select></MField>
    {isTmn&&<div className="space-y-3 rounded-lg border border-line bg-sunken p-3">
      <div className="text-xs font-semibold text-fg-2">คีย์ TMNOne</div>
      <MField label="Key ID"><input value={f.tmnone_key_id} onChange={e=>set("tmnone_key_id",e.target.value)} placeholder="Key ID จาก TMNOne" className={inp}/></MField>
      <MField label="Login Token"><input value={f.tmnone_login_token} onChange={e=>set("tmnone_login_token",e.target.value)} placeholder="L-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" className={inp}/></MField>
      <MField label="TMN ID"><input value={f.tmnone_tmn_id} onChange={e=>set("tmnone_tmn_id",e.target.value)} placeholder="tmn.10000000000" className={inp}/></MField>
      <div className="grid grid-cols-1 gap-3 sm:grid-cols-2">
        <MField label="PIN (6 หลัก)"><input type="password" value={f.tmnone_pin} onChange={e=>set("tmnone_pin",e.target.value)} placeholder="เว้นว่าง = คงค่าเดิม" maxLength={6} inputMode="numeric" className={inp}/></MField>
        <MField label="Device ID"><input value={f.tmnone_device_id} onChange={e=>set("tmnone_device_id",e.target.value)} placeholder="เว้นว่างได้" className={inp}/></MField>
      </div>
      <div className="flex flex-wrap items-center gap-2">
        <button onClick={testConnection} disabled={testing} className="flex items-center gap-1.5 rounded-lg border border-line bg-surface px-3 py-1.5 text-xs font-medium text-fg-2 hover:bg-sunken disabled:opacity-50">
          {testing?<><Ic d={I.refresh} size={14} cls="animate-spin"/> ทดสอบ...</>:"ทดสอบคีย์บัญชีนี้"}
        </button>
        {testResult&&<span className={"text-xs font-medium "+(testResult.ok?"text-success":"text-danger")}>{testResult.msg}</span>}
      </div>
      <div className="text-[11px] text-faint">หากไม่กรอก จะใช้ค่าเริ่มต้นจากหน้าตั้งค่า API</div>
    </div>}
    {f.deposit_method!=="angpao"&&<MField label="ควบกับระบบอังเปาวอเลท" hint="ใช้เบอร์นี้รับซองอังเปาได้ด้วย">
      <select value={f.angpao_link?"1":"0"} onChange={e=>set("angpao_link",e.target.value==="1")} className={inp}>
        <option value="0">ไม่ควบ — รับเฉพาะรูปแบบด้านบน</option>
        <option value="1">ควบ — รับซองอังเปาด้วยเบอร์นี้ได้</option>
      </select></MField>}
    <MField label="แสดงในช่วงเวลา" hint="เว้นว่างเพื่อแสดงตลอด 24 ชม.">
      <TimeRange from={f.active_from} to={f.active_to} onFrom={v=>set("active_from",v)} onTo={v=>set("active_to",v)}/></MField>
  </ModalFrame>);
}

/* ---------- Modal: LINE Connect ---------- */
function LineEditModal({ data, onSave, onClose, saving }) {
  const [f,setF]=useState(()=>({
    id:data.id||"",
    bank_code:data.bank_code||"01004", account_no:data.account_no||"", account_name:data.account_name||"",
    sort_order:data.sort_order||0,
    active_from:data.active_from?String(data.active_from).slice(0,5):"",
    active_to:data.active_to?String(data.active_to).slice(0,5):"",
    line_session_id:data.line_session_id||"",
  }));
  const [sessions,setSessions]=useState(null);
  const set=(k,v)=>setF(p=>({...p,[k]:v}));

  useEffect(()=>{
    api("/line-abd/sessions").then(r=>setSessions(r.sessions||[])).catch(()=>setSessions([]));
  },[]);

  const submit=()=>{
    const sel=sessions?.find(s=>s.session_id===f.line_session_id);
    const bankOpt=BANK_CODE_OPTIONS.find(b=>b[0]===f.bank_code);
    onSave({
      id:f.id||undefined, type:"LINEAbd",
      bank_code:f.bank_code, bank_name:bankOpt?bankOpt[1]:f.bank_code,
      account_no:f.account_no.trim(), account_name:f.account_name.trim(),
      deposit_methods:["line_abd"], sort_order:+f.sort_order||0,
      active_from:f.active_from||null, active_to:f.active_to||null,
      line_session_id:f.line_session_id,
      line_mid:sel?.mid||data.line_mid||"", line_display_name:sel?.display_name||data.line_display_name||f.account_name,
    });
  };
  const inp=M_INP;
  return (<ModalFrame maxW="max-w-[440px]" title={<><span className="grid h-[22px] w-[22px] place-items-center rounded text-[11px] font-bold text-white" style={{background:"#06C755"}}>L</span> {f.id?"แก้ไข":"เพิ่ม"}บัญชี LINE Connect</>} onClose={onClose}
    footer={<ModalButtons onSave={submit} onClose={onClose} saving={saving} disabled={!f.line_session_id||!f.account_no.trim()||!f.account_name.trim()}/>}
    note="กรุณาเชื่อมต่อ LINE Session ที่หน้า LINE Connect ก่อน">
    <MField label="LINE Session" hint="เลือกจาก LINE ที่เชื่อมต่อแล้ว">
      {sessions===null?<div className="text-xs text-muted">กำลังโหลด...</div>:
      <select value={f.line_session_id} onChange={e=>set("line_session_id",e.target.value)} className={inp}>
        <option value="">เลือก LINE Session</option>
        {sessions.map(s=><option key={s.session_id} value={s.session_id}>{s.display_name||s.mid||s.session_id}</option>)}
      </select>}
    </MField>
    <div className="space-y-3 rounded-lg border border-line bg-sunken p-3">
      <div className="text-xs font-semibold text-fg-2">บัญชีธนาคารที่ผูกกับ LINE นี้ <span className="font-normal text-faint">(บัญชีที่ LINE ได้รับแจ้งเตือนเงินเข้า)</span></div>
      <MField label="ธนาคาร">
        <select value={f.bank_code} onChange={e=>set("bank_code",e.target.value)} className={inp}>
          {BANK_CODE_OPTIONS.map(([c,n])=><option key={c} value={c}>{n}</option>)}
        </select></MField>
      <MField label="เลขบัญชี"><input value={f.account_no} onChange={e=>set("account_no",e.target.value)} placeholder="เลขบัญชี" inputMode="numeric" className={inp}/></MField>
      <MField label="ชื่อบัญชี" hint="ตามสมุดบัญชี"><input value={f.account_name} onChange={e=>set("account_name",e.target.value)} placeholder="ชื่อ-นามสกุล" className={inp}/></MField>
    </div>
    <MField label="ลำดับแสดง"><input type="number" min="0" value={f.sort_order} onChange={e=>set("sort_order",e.target.value)} className={inp}/></MField>
    <MField label="แสดงในช่วงเวลา" hint="เว้นว่างเพื่อแสดงตลอด 24 ชม.">
      <TimeRange from={f.active_from} to={f.active_to} onFrom={v=>set("active_from",v)} onTo={v=>set("active_to",v)}/></MField>
  </ModalFrame>);
}

/* ---------- Modal: บัญชีถอน (ตาม UI เก่า: ประเภทถูกกำหนดจากปุ่มที่กด / บัญชีที่แก้ไข) ---------- */
function PayoutEditModal({ data, onSave, onClose, saving }) {
  const isEdit=!!data.id;
  const type=data.type||"bank";
  const isWallet=['wallet','TMNDepo','WHdepo','TMNWD'].includes(type);
  const [f,setF]=useState(()=>({
    id:data.id||"",
    bank_code:data.bank_code||"01004", account_no:data.account_no||"", account_name:data.account_name||"",
    wallet_phone:data.wallet_phone||"", note:data.note||"", sort_order:data.sort_order||0,
  }));
  const set=(k,v)=>setF(p=>({...p,[k]:v}));
  const inp=M_INP;

  const submit=()=>{
    const bankOpt=BANK_CODE_OPTIONS.find(b=>b[0]===f.bank_code);
    onSave({
      id:f.id||undefined, type,
      bank_code:isWallet?null:f.bank_code,
      bank_name:isWallet?null:(bankOpt?bankOpt[1]:f.bank_code),
      account_no:isWallet?null:f.account_no,
      account_name:isWallet?(f.account_name||null):f.account_name,
      wallet_phone:isWallet?f.wallet_phone:null,
      note:f.note||null, sort_order:+f.sort_order||0,
    });
  };

  const title=(isEdit?"แก้ไข":"เพิ่ม")+(type==="TMNWD"?"บัญชี TMN ถอน (TMNWD)":isWallet?"บัญชีวอลเล็ท":"บัญชีถอน");
  return (<ModalFrame title={isWallet?<><BankLogo code="truewallet" size={22}/> {title}</>:title} onClose={onClose}
    footer={<ModalButtons onSave={submit} onClose={onClose} saving={saving}/>}>
    {!isWallet?<>
      <MField label="ธนาคาร">
        <select value={f.bank_code} onChange={e=>set("bank_code",e.target.value)} className={inp}>
          {BANK_CODE_OPTIONS.map(([c,n])=><option key={c} value={c}>{n}</option>)}
        </select></MField>
      <MField label="เลขบัญชี"><input value={f.account_no} onChange={e=>set("account_no",e.target.value)} placeholder="เลขบัญชี" className={inp}/></MField>
      <MField label="ชื่อบัญชี"><input value={f.account_name} onChange={e=>set("account_name",e.target.value)} placeholder="ชื่อ-นามสกุล" className={inp}/></MField>
    </>:<>
      <MField label="เบอร์บัญชี TMN"><input value={f.wallet_phone} onChange={e=>set("wallet_phone",e.target.value)} placeholder="0812345678" inputMode="tel" className={inp}/></MField>
      <MField label="ชื่อ/หมายเหตุ"><input value={f.account_name} onChange={e=>set("account_name",e.target.value)} placeholder="ชื่อบัญชี" className={inp}/></MField>
      <div className="rounded-lg border border-line bg-info-soft p-2.5 text-xs text-info">ตั้งค่าคีย์ TMNOne และผูกบัญชีถอนที่ <b>จัดการ API → API ฝาก → TMNOne</b></div>
    </>}
    <MField label="หมายเหตุ" hint="ไม่บังคับ"><input value={f.note} onChange={e=>set("note",e.target.value)} placeholder="หมายเหตุ..." className={inp}/></MField>
    <MField label="ลำดับแสดง"><input type="number" min="0" value={f.sort_order} onChange={e=>set("sort_order",e.target.value)} className={inp}/></MField>
  </ModalFrame>);
}

/* ---------- Add-account type picker ---------- */
function AddAccountMenu({ onPick, onClose }) {
  return (<><div className="fixed inset-0 z-40" onClick={onClose}/>
    <div className="absolute right-0 top-full mt-1 z-50 w-56 rounded-xl border border-line bg-surface p-1 shadow-lg pop">
      <button onClick={()=>onPick("bank")} className="flex w-full items-center gap-2 rounded-lg px-3 py-2 text-sm text-fg hover:bg-sunken"><BankLogo code="kbank" size={20}/> ธนาคาร</button>
      <button onClick={()=>onPick("wallet")} className="flex w-full items-center gap-2 rounded-lg px-3 py-2 text-sm text-fg hover:bg-sunken"><BankLogo code="truewallet" size={20}/> TrueWallet / TMN</button>
      <button onClick={()=>onPick("line")} className="flex w-full items-center gap-2 rounded-lg px-3 py-2 text-sm text-fg hover:bg-sunken"><span className="grid h-5 w-5 place-items-center rounded text-[10px] font-bold text-white" style={{background:"#06C755"}}>L</span> LINE Connect</button>
    </div>
  </>);
}

/* ========================================================================== */
/* หน้า 1: บัญชีรับเงิน */
/* ========================================================================== */
function BankReceivePage({ toast }) {
  const [accounts,setAccounts]=useState(null);
  const [loading,setLoading]=useState(true);
  const [err,setErr]=useState(null);
  const [modal,setModal]=useState(null);
  const [saving,setSaving]=useState(false);
  const [delTarget,setDelTarget]=useState(null);
  const [addMenu,setAddMenu]=useState(false);
  const canWrite = canAny(["accounts","bank-receive"]);

  const load=useCallback(async()=>{
    setLoading(true); setErr(null);
    try {
      const r=await api("/deposit-accounts/stats");
      setAccounts(Array.isArray(r)?r:(r.rows||[]));
    } catch(e) {
      if(e.status===401) window.__nxAuthFail&&window.__nxAuthFail();
      setErr(e.status===403?"บัญชีนี้ไม่มีสิทธิ์ดูบัญชีรับเงิน":"โหลดไม่สำเร็จ");
    } finally { setLoading(false); }
  },[]);
  useEffect(()=>{ load(); },[load]);

  const saveDeposit=async(form)=>{ setSaving(true);
    try{
      const body={
        type:form.type, bankCode:form.bank_code, bankName:form.bank_name,
        accountNo:form.account_no, accountName:form.account_name,
        accountNameTh:form.account_name_th, accountNameEn:form.account_name_en,
        walletPhone:form.wallet_phone, depositMethods:form.deposit_methods,
        sortOrder:form.sort_order, activeFrom:form.active_from, activeTo:form.active_to,
        smsActiveFrom:form.sms_active_from, smsActiveTo:form.sms_active_to,
        angpaoActiveFrom:form.angpao_active_from, angpaoActiveTo:form.angpao_active_to,
        lineMatchMode:form.line_match_mode, userBankAccount:form.user_bank_account, userPhone:form.user_phone,
        smsDeviceId:form.sms_device_id, smsSender:form.sms_sender,
        lineSessionId:form.line_session_id, lineMid:form.line_mid, lineDisplayName:form.line_display_name,
        tmnoneKeyId:form.tmnone_key_id, tmnoneLoginToken:form.tmnone_login_token,
        tmnonePin:form.tmnone_pin, tmnoneTmnId:form.tmnone_tmn_id, tmnoneDeviceId:form.tmnone_device_id,
        // แก้ไขแล้วคงสถานะเปิด/ปิดเดิม (modal ไม่ได้ส่ง is_active มา)
        isActive:form.is_active!==undefined?form.is_active!==false:(form.id?(accounts||[]).find(x=>x.id===form.id)?.is_active!==false:true),
      };
      if(form.id){ body.id=form.id; await api(`/deposit-accounts/${form.id}`,{method:"PATCH",body}); }
      else { await api("/deposit-accounts",{method:"POST",body}); }
      toast("บันทึกแล้ว"); setModal(null); load();
    }catch(e){ toast(e.message||"บันทึกไม่สำเร็จ"); } finally{ setSaving(false); } };

  const toggleActive=async(a)=>{
    try{ await api(`/deposit-accounts/${a.id}`,{method:"PATCH",body:{isActive:!a.is_active}}); toast("อัพเดตแล้ว"); load(); }
    catch(e){ toast(e.message||"อัพเดตไม่สำเร็จ"); } };

  const toggleFrontend=async(a)=>{
    try{ await api(`/deposit-accounts/${a.id}/show-frontend`,{method:"PATCH",body:{show:!(a.show_on_frontend)}}); toast("อัพเดตแล้ว"); load(); }
    catch(e){ toast(e.message||"อัพเดตไม่สำเร็จ"); } };

  const doDelete=async()=>{
    if(!delTarget) return;
    try{ await api(`/deposit-accounts/${delTarget.id}`,{method:"DELETE"}); toast("ลบแล้ว"); setDelTarget(null); load(); }
    catch(e){ toast(e.message||"ลบไม่สำเร็จ"); } };

  const openEditModal=(a)=>{
    if(a.type==='LINEAbd') setModal({type:"line",data:{...a}});
    else if(['wallet','TMNDepo','WHdepo'].includes(a.type)) setModal({type:"wallet",data:{...a}});
    else setModal({type:"bank",data:{...a}});
  };
  const openAddModal=(type)=>{
    setAddMenu(false);
    if(type==="bank") setModal({type:"bank",data:{}});
    else if(type==="wallet") setModal({type:"wallet",data:{}});
    else if(type==="line") setModal({type:"line",data:{}});
  };

  const list = accounts||[];

  return (<div className="space-y-4">
    <div className="flex flex-wrap items-center gap-2">
      <button onClick={load} className="flex h-9 items-center gap-1.5 rounded-lg border border-line bg-surface px-3 text-sm font-medium text-fg-2 hover:bg-sunken"><Ic d={I.refresh} size={16} cls={loading?"animate-spin":""}/> รีเฟรช</button>
      {canWrite&&<div className="relative">
        <button onClick={()=>setAddMenu(!addMenu)} className="flex h-9 items-center gap-1.5 rounded-lg bg-accent px-4 text-sm font-medium text-accent-fg hover:bg-accent-h"><Ic d={I.plus} size={16}/> เพิ่มบัญชี</button>
        {addMenu&&<AddAccountMenu onPick={openAddModal} onClose={()=>setAddMenu(false)}/>}
      </div>}
      <div className="ml-auto text-xs text-muted tnum">{list.length} บัญชี</div>
    </div>
    {err ? <HistErr msg={err} onRetry={load}/>
      : loading ? (<div className="grid grid-cols-1 gap-3 sm:grid-cols-2 xl:grid-cols-3">{Array.from({length:3}).map((_,i)=><div key={i} className="skel h-36 w-full"/>)}</div>)
      : list.length===0 ? (<div className="rounded-xl border border-dashed border-line bg-surface py-16 text-center text-sm text-muted">ยังไม่มีบัญชีรับเงิน</div>)
      : (<div className="grid grid-cols-1 gap-3 sm:grid-cols-2 xl:grid-cols-3">{list.map((a,i)=><AccountStatCard key={a.id||i} a={a} onEdit={openEditModal} onToggle={toggleActive} onToggleFrontend={toggleFrontend} onDelete={(r)=>setDelTarget(r)} canWrite={canWrite}/>)}</div>)}

    {modal?.type==="bank"&&<BankEditModal data={modal.data} onSave={saveDeposit} onClose={()=>setModal(null)} saving={saving}/>}
    {modal?.type==="wallet"&&<WalletEditModal data={modal.data} onSave={saveDeposit} onClose={()=>setModal(null)} saving={saving} toast={toast}/>}
    {modal?.type==="line"&&<LineEditModal data={modal.data} onSave={saveDeposit} onClose={()=>setModal(null)} saving={saving}/>}
    {delTarget&&<SimpleConfirmModal title="ลบบัญชี" msg={"ยืนยันลบบัญชี "+(delTarget.account_name||delTarget.account_no||"")+"?"} onOk={doDelete} onCancel={()=>setDelTarget(null)}/>}
  </div>);
}

/* ========================================================================== */
/* หน้า 2: บัญชีถอนได้ */
/* ========================================================================== */
function BankPayoutPage({ toast }) {
  const [accounts,setAccounts]=useState(null);
  const [balances,setBalances]=useState({});
  const [loading,setLoading]=useState(true);
  const [err,setErr]=useState(null);
  const [modal,setModal]=useState(null);
  const [saving,setSaving]=useState(false);
  const [delTarget,setDelTarget]=useState(null);
  const canWrite = canAny(["accounts","bank-payout"]);

  const loadBalances=useCallback(async(accts,force)=>{
    const wallets=(accts||[]).filter(a=>['wallet','TMNDepo','WHdepo','TMNWD'].includes(a.type));
    if(!wallets.length) return;
    const next={}; wallets.forEach(w=>{next[w.id]=undefined;});
    setBalances(prev=>({...prev,...next}));
    for(const w of wallets){
      try{
        const r=await api(`/withdrawal-accounts/${w.id}/balance${force?"?force=1":""}`);
        setBalances(prev=>({...prev,[w.id]:r.balance??r.amount??0}));
      }catch{ setBalances(prev=>({...prev,[w.id]:null})); }
    }
  },[]);

  const refreshBalance=useCallback(async(id)=>{
    setBalances(prev=>({...prev,[id]:undefined}));
    try{
      const r=await api(`/withdrawal-accounts/${id}/balance?force=1`);
      setBalances(prev=>({...prev,[id]:r.balance??r.amount??0}));
    }catch{ setBalances(prev=>({...prev,[id]:null})); }
  },[]);

  const load=useCallback(async()=>{
    setLoading(true); setErr(null);
    try {
      const r=await api("/withdrawal-accounts");
      const pa=Array.isArray(r)?r:(r.rows||[]);
      setAccounts(pa);
      loadBalances(pa,false);
    } catch(e) {
      if(e.status===401) window.__nxAuthFail&&window.__nxAuthFail();
      setErr(e.status===403?"ไม่มีสิทธิ์ดูบัญชีถอน":"โหลดไม่สำเร็จ");
    } finally { setLoading(false); }
  },[loadBalances]);
  useEffect(()=>{ load(); },[load]);

  const savePayout=async(form)=>{ setSaving(true);
    try{
      const body={type:form.type,bankCode:form.bank_code,bankName:form.bank_name,
        accountNo:form.account_no,accountName:form.account_name,
        walletPhone:form.wallet_phone,note:form.note,sortOrder:form.sort_order};
      if(form.id){ await api(`/withdrawal-accounts/${form.id}`,{method:"PATCH",body}); }
      else { await api("/withdrawal-accounts",{method:"POST",body}); }
      toast("บันทึกแล้ว"); setModal(null); load();
    }catch(e){ toast(e.message||"บันทึกไม่สำเร็จ"); } finally{ setSaving(false); } };

  const toggleActive=async(a)=>{
    try{ await api(`/withdrawal-accounts/${a.id}`,{method:"PATCH",body:{isActive:!a.is_active}}); toast("อัพเดตแล้ว"); load(); }
    catch(e){ toast(e.message||"อัพเดตไม่สำเร็จ"); } };

  const doDelete=async()=>{
    if(!delTarget) return;
    try{ await api(`/withdrawal-accounts/${delTarget.id}`,{method:"DELETE"}); toast("ลบแล้ว"); setDelTarget(null); load(); }
    catch(e){ toast(e.message||"ลบไม่สำเร็จ"); } };

  const list = accounts||[];
  const totals=useMemo(()=>{
    let w24=0,wt=0,bal=0;
    list.forEach(a=>{w24+=Number(a.withdraw_total_24h)||0;wt+=Number(a.withdraw_total)||0;
      const b=balances[a.id]; if(typeof b==="number") bal+=b;});
    return {w24,wt,bal};
  },[list,balances]);

  return (<div className="space-y-4">
    <div className="flex flex-wrap items-center gap-2">
      <button onClick={load} className="flex h-9 items-center gap-1.5 rounded-lg border border-line bg-surface px-3 text-sm font-medium text-fg-2 hover:bg-sunken"><Ic d={I.refresh} size={16} cls={loading?"animate-spin":""}/> รีเฟรช</button>
      {canWrite&&<>
        <button onClick={()=>setModal({type:"payout",data:{type:"bank"}})} className="flex h-9 items-center gap-1.5 rounded-lg bg-accent px-4 text-sm font-medium text-accent-fg hover:bg-accent-h"><Ic d={I.plus} size={16}/> เพิ่มบัญชีธนาคาร</button>
        <button onClick={()=>setModal({type:"payout",data:{type:"TMNWD"}})} className="flex h-9 items-center gap-1.5 rounded-lg bg-accent px-4 text-sm font-medium text-accent-fg hover:bg-accent-h"><BankLogo code="truewallet" size={18}/> เพิ่มบัญชี TrueWallet</button>
      </>}
      <div className="ml-auto text-xs text-muted tnum">{list.length} บัญชี</div>
    </div>
    {err ? <HistErr msg={err} onRetry={load}/>
      : loading ? (<div className="grid grid-cols-1 gap-3 sm:grid-cols-2 xl:grid-cols-3">{Array.from({length:3}).map((_,i)=><div key={i} className="skel h-36 w-full"/>)}</div>)
      : list.length===0 ? (<div className="rounded-xl border border-dashed border-line bg-surface py-16 text-center text-sm text-muted">ยังไม่มีบัญชีถอน</div>)
      : (<>
        <div className="grid grid-cols-3 gap-3">
          <StatMini k="ถอน 24 ชม." v={money2(totals.w24)}/>
          <StatMini k="ถอนสะสม" v={money2(totals.wt)}/>
          <StatMini k="ยอดคงเหลือรวม" v={money2(totals.bal)} color="var(--info)"/>
        </div>
        <div className="grid grid-cols-1 gap-3 sm:grid-cols-2 xl:grid-cols-3">{list.map((a,i)=><PayoutAccountCard key={a.id||i} a={a} onEdit={(r)=>setModal({type:"payout",data:{...r}})} onToggle={toggleActive} onDelete={(r)=>setDelTarget(r)} onRefreshBal={refreshBalance} canWrite={canWrite} balance={balances[a.id]}/>)}</div>
      </>)}

    {modal?.type==="payout"&&<PayoutEditModal data={modal.data} onSave={savePayout} onClose={()=>setModal(null)} saving={saving}/>}
    {delTarget&&<SimpleConfirmModal title="ลบบัญชี" msg={"ยืนยันลบบัญชี "+(delTarget.account_name||delTarget.account_no||"")+"?"} onOk={doDelete} onCancel={()=>setDelTarget(null)}/>}
  </div>);
}

/* ========================================================================== */
/* หน้า 3: บัญชีกลาง */
/* ========================================================================== */
function BankCentralPage({ toast }) {
  const [accounts,setAccounts]=useState(null);
  const [loading,setLoading]=useState(true);
  const [err,setErr]=useState(null);
  const [editForm,setEditForm]=useState(null);
  const [saving,setSaving]=useState(false);
  const [delTarget,setDelTarget]=useState(null);
  const canWrite = canAny(["accounts","bank-central"]);

  const load=useCallback(async()=>{
    setLoading(true); setErr(null);
    try {
      const r=await api("/central-accounts");
      setAccounts(Array.isArray(r)?r:(r.rows||[]));
    } catch(e) {
      if(e.status===401) window.__nxAuthFail&&window.__nxAuthFail();
      setErr(e.status===403?"ไม่มีสิทธิ์ดูบัญชีกลาง":"โหลดไม่สำเร็จ");
    } finally { setLoading(false); }
  },[]);
  useEffect(()=>{ load(); },[load]);

  const saveCentral=async(form)=>{ setSaving(true);
    try{
      const body={type:form.type,bankCode:form.bank_code,bankName:form.bank_name,accountNo:form.account_no,accountName:form.account_name,walletPhone:form.wallet_phone,note:form.note,isActive:form.is_active!==false,sortOrder:form.sort_order};
      if(form.id){ await api(`/central-accounts/${form.id}`,{method:"PATCH",body}); }
      else { await api("/central-accounts",{method:"POST",body}); }
      toast("บันทึกแล้ว"); setEditForm(null); load();
    }catch(e){ toast(e.message||"บันทึกไม่สำเร็จ"); } finally{ setSaving(false); } };

  const toggleActive=async(a)=>{
    try{ await api(`/central-accounts/${a.id}`,{method:"PATCH",body:{isActive:!a.is_active}}); toast("อัพเดตแล้ว"); load(); }
    catch(e){ toast(e.message||"อัพเดตไม่สำเร็จ"); } };

  const doDelete=async()=>{
    if(!delTarget) return;
    try{ await api(`/central-accounts/${delTarget.id}`,{method:"DELETE"}); toast("ลบแล้ว"); setDelTarget(null); load(); }
    catch(e){ toast(e.message||"ลบไม่สำเร็จ"); } };

  const newForm={type:"bank",bank_code:"",bank_name:"",account_no:"",account_name:"",wallet_phone:"",note:"",is_active:true,sort_order:0};
  const list = accounts||[];
  const inp="w-full rounded-lg border border-line bg-surface px-3 py-2 text-sm";

  return (<div className="space-y-4">
    <div className="flex flex-wrap items-center gap-2">
      <button onClick={load} className="flex h-9 items-center gap-1.5 rounded-lg border border-line bg-surface px-3 text-sm font-medium text-fg-2 hover:bg-sunken"><Ic d={I.refresh} size={16} cls={loading?"animate-spin":""}/> รีเฟรช</button>
      {canWrite&&<button onClick={()=>setEditForm({...newForm})} className="flex h-9 items-center gap-1.5 rounded-lg bg-accent px-4 text-sm font-medium text-accent-fg hover:bg-accent-h"><Ic d={I.plus} size={16}/> เพิ่มบัญชีกลาง</button>}
      <div className="ml-auto text-xs text-muted tnum">{list.length} บัญชี</div>
    </div>
    {err ? <HistErr msg={err} onRetry={load}/>
      : loading ? (<div className="grid grid-cols-1 gap-3 sm:grid-cols-2 xl:grid-cols-3">{Array.from({length:3}).map((_,i)=><div key={i} className="skel h-36 w-full"/>)}</div>)
      : list.length===0 ? (<div className="rounded-xl border border-dashed border-line bg-surface py-16 text-center text-sm text-muted">ยังไม่มีบัญชีกลาง</div>)
      : (<div className="grid grid-cols-1 gap-3 sm:grid-cols-2 xl:grid-cols-3">{list.map((a,i)=><CentralAccountCard key={a.id||i} a={a} onEdit={(r)=>setEditForm({...r})} onToggle={toggleActive} onDelete={(r)=>setDelTarget(r)} canWrite={canWrite}/>)}</div>)}

    {editForm&&(<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/40 fade-in" onClick={()=>setEditForm(null)}>
      <div className="w-full max-w-md rounded-xl bg-surface p-6 shadow-lg pop" onClick={e=>e.stopPropagation()}>
        <h3 className="mb-4 text-sm font-semibold">{editForm.id?"แก้ไข":"เพิ่ม"}บัญชีกลาง</h3>
        <div className="mb-3"><label className="mb-1 block text-xs font-semibold text-fg-2">ประเภท</label>
          <select value={editForm.type||"bank"} onChange={e=>setEditForm(p=>({...p,type:e.target.value}))} className={inp}>
            <option value="bank">ธนาคาร</option><option value="wallet">วอลเล็ท</option>
          </select></div>
        {(editForm.type||"bank")==="bank"&&<div className="mb-3"><label className="mb-1 block text-xs font-semibold text-fg-2">ธนาคาร</label>
          <select value={editForm.bank_code||""} onChange={e=>{const b=BANK_CODE_OPTIONS.find(x=>x[0]===e.target.value);setEditForm(p=>({...p,bank_code:e.target.value,bank_name:b?b[1]:""}));}} className={inp}>
            <option value="">เลือกธนาคาร</option>
            {BANK_CODE_OPTIONS.map(([c,n])=><option key={c} value={c}>{n}</option>)}
          </select></div>}
        {[["account_no","เลขบัญชี"],["account_name","ชื่อบัญชี"],["wallet_phone","เบอร์วอลเล็ท"],["note","หมายเหตุ"]].map(([k,l])=>(
          <div key={k} className="mb-3"><label className="mb-1 block text-xs font-semibold text-fg-2">{l}</label>
          <input type="text" value={editForm[k]||""} onChange={e=>setEditForm(p=>({...p,[k]:e.target.value}))} className={inp}/></div>))}
        <div className="mb-3"><label className="mb-1 block text-xs font-semibold text-fg-2">ลำดับ</label>
          <input type="number" value={editForm.sort_order||0} onChange={e=>setEditForm(p=>({...p,sort_order:+e.target.value||0}))} className={inp}/></div>
        <div className="flex justify-end gap-2 mt-4">
          <button onClick={()=>setEditForm(null)} className="rounded-lg border border-line px-4 py-2 text-sm text-fg-2 hover:bg-sunken">ยกเลิก</button>
          <button onClick={()=>saveCentral(editForm)} disabled={saving} className="rounded-lg bg-accent px-4 py-2 text-sm font-medium text-accent-fg hover:bg-accent-h disabled:opacity-50">{saving?"...":"บันทึก"}</button>
        </div>
      </div>
    </div>)}

    {delTarget&&<SimpleConfirmModal title="ลบบัญชี" msg={"ยืนยันลบบัญชี "+(delTarget.account_name||delTarget.account_no||"")+"?"} onOk={doDelete} onCancel={()=>setDelTarget(null)}/>}
  </div>);
}
