/* global React, I, UI, SSSData, bahtText */
const { Btn, Badge, Card, PageHead, Tabs, Drawer, Workflow, fmt0, fmtM } = window.UI;
const { useState } = React;

// ============ Generic doc list page ============
function DocListPage({ title, sub, columns, rows, statusFilter, onRowClick, onCreate, createLabel, kpis, docType, filterField }) {
  // จำตัวกรองไว้ต่อหน้า (sessionStorage) — เปิดใบแล้วกดกลับ ตัวกรองเดิมยังอยู่
  const DLP_MEM = "sss-dlp-" + (docType || title || "x");
  const _mem = (() => { try { return JSON.parse(sessionStorage.getItem(DLP_MEM) || "null") || {}; } catch { return {}; } })();
  const [tab, setTab] = useState(_mem.tab || "all");
  const [q, setQ] = useState(_mem.q || "");
  const [dateF, setDateF] = useState(_mem.dateF || { mode: "all" });
  const [pf, setPf] = useState(_mem.pf || "");   // ตัวกรองตามฟิลด์ (เช่น ผู้รับเงิน/ผู้ขาย)
  React.useEffect(() => { try { sessionStorage.setItem(DLP_MEM, JSON.stringify({ tab, q, dateF, pf })); } catch {} }, [tab, q, dateF, pf]);
  const [selected, setSelected] = useState(new Set());
  const [, force] = useState(0);
  // re-render เมื่อมีเอกสารใหม่/แก้ไข/ลบ หรือ sync จากเครื่องอื่น (ไม่ต้องรีเฟรช)
  React.useEffect(() => {
    const h = () => force(n => n + 1);
    const evts = ["sss-docs-changed", "sss-data-synced", "sss-doc-status-changed"];
    evts.forEach(e => window.addEventListener(e, h));
    return () => evts.forEach(e => window.removeEventListener(e, h));
  }, []);
  const statuses = statusFilter || [];

  const filtered = rows.filter(r => {
    const matchesTab = tab === "all" || r.status === tab;
    const matchesQ = !q || JSON.stringify(r).toLowerCase().includes(q.toLowerCase());
    const matchesDate = !window.DocDates || window.DocDates.match(dateF, r.date);
    const matchesPf = !filterField || !pf || (r[filterField.key] || "") === pf;
    return matchesTab && matchesQ && matchesDate && matchesPf;
  });
  const pfOptions = filterField ? [...new Set(rows.map(r => r[filterField.key]).filter(Boolean))].sort() : [];
  const selSum = [...selected].reduce((s, id) => { const r = rows.find(x => x[(columns.find(c => c.key === "no") ? "no" : columns[0].key)] === id); return s + (r ? (+r.amount || 0) : 0); }, 0);

  const idKey = columns.find(c => c.key === "no") ? "no" : columns[0].key;
  const allSelected = filtered.length > 0 && filtered.every(r => selected.has(r[idKey]));
  const someSelected = selected.size > 0 && !allSelected;

  const toggleAll = () => {
    if (allSelected) {
      setSelected(new Set());
    } else {
      setSelected(new Set(filtered.map(r => r[idKey])));
    }
  };
  const toggleOne = (id) => {
    const next = new Set(selected);
    if (next.has(id)) next.delete(id); else next.add(id);
    setSelected(next);
  };

  const printSelected = async () => {
    const opts = await window.printWithOptions();
    if (!opts) return;
    // Build combined HTML — render each selected row's doc paper if possible
    // Strategy: open the first .doc-paper in DOM as template; here we just show stack
    const selectedRows = filtered.filter(r => selected.has(r[idKey]));
    window.printBulkDocs && window.printBulkDocs(selectedRows, opts, docType, title);
  };

  const DLP_LIST_BY_DOCTYPE = { quotation: "Quotations", invoice: "Invoices", "delivery-note": "DeliveryNotes", "tax-invoice": "TaxInvoices", "billing-note": "BillingNotes", receipt: "Receipts", "purchase-order": "PurchaseOrders", "payment-voucher": "PaymentVouchers", "receipt-voucher": "ReceiptVouchers" };
  const deleteSelected = async () => {
    const listKey = DLP_LIST_BY_DOCTYPE[docType];
    if (!listKey || !window.DocCreateStore) { window.toast && window.toast("ลบไม่ได้ในหน้านี้"); return; }
    const sel = filtered.filter(r => selected.has(r[idKey]));
    const ok = await window.confirmDialog({ title: `ลบ ${sel.length} รายการ?`, message: "เอกสารจะถูกย้ายไป <b>ถังกู้คืน</b> เก็บไว้ 3 วัน — กู้คืนได้ที่เมนู \"กู้คืนเอกสาร\" · ซิงค์ทุกเครื่อง", confirmText: "ลบเอกสาร", danger: true });
    if (!ok) return;
    sel.forEach(r => {
      // ใบสำคัญรับเงินจากการเบิก: คืนยอดบัญชีที่หักไปด้วย
      if (listKey === "ReceiptVouchers" && r.fromAdvance && window.CompanyCashStore) {
        const mv = (window.CompanyCashStore.load().movements || []).find(m => m.ref === "ADV-" + r.fromAdvance);
        if (mv) window.CompanyCashStore.removeMovement(mv.id);
      }
      window.DocCreateStore.remove(listKey, r[idKey]);
    });
    if (listKey === "ReceiptVouchers") window.dispatchEvent(new Event("sss-company-pay-changed"));
    window.logActivity && window.logActivity("ลบเอกสาร", `${sel.length} รายการ`, "delete");
    window.toast && window.toast(`ลบ ${sel.length} รายการ → ถังกู้คืน (3 วัน)`);
    setSelected(new Set());
  };

  return (
    <>
      <PageHead
        title={title}
        sub={sub}
        right={<>
          <Btn icon={I.download} kind="ghost" onClick={() => window.exportRowsCSV(title.split("·")[0].trim(), columns, filtered)}>ส่งออก Excel</Btn>
          <Btn icon={I.plus} kind="primary" onClick={onCreate}>{createLabel}</Btn>
        </>}
      />
      {kpis && (
        <div className="grid cols-4 mb-lg">
          {kpis.map((k, i) => (
            <UI.Stat key={i} label={k.label} value={k.value} sub={k.sub} deltaDir={k.dir} delta={k.delta} icon={k.icon}/>
          ))}
        </div>
      )}
      <Card flush padded={false}>
        <div style={{padding: 12, borderBottom: "1px solid var(--line)", display: "flex", gap: 10, alignItems: "center"}}>
          <Tabs
            items={[{ key: "all", label: "ทั้งหมด", count: rows.length }, ...statuses.map(s => ({ key: s, label: s, count: rows.filter(r => r.status === s).length }))]}
            active={tab}
            onChange={setTab}
          />
          <span className="spacer"/>
          {selected.size > 0 && (
            <>
              <span style={{fontSize: 12, color: "var(--ink-3)"}}>เลือก {selected.size} รายการ · ยอดรวม <b style={{color: "var(--ink-1)"}}>฿{(window.UI.fmt0 || (n => n))(selSum)}</b></span>
              <Btn size="sm" kind="ghost" onClick={() => setSelected(new Set())}>ยกเลิก</Btn>
              <Btn size="sm" icon={I.print} kind="primary" onClick={printSelected}>พิมพ์ที่เลือก ({selected.size})</Btn>
              <Btn size="sm" icon={I.trash} kind="ghost" onClick={deleteSelected}>ลบที่เลือก ({selected.size})</Btn>
            </>
          )}
          {selected.size === 0 && <>
            <div className="search" style={{width: 260}}>
              <I.search size={14}/>
              <input placeholder="ค้นหา…" value={q} onChange={e => setQ(e.target.value)}/>
            </div>
            {filterField && (
              <select className="input" value={pf} onChange={e => setPf(e.target.value)} style={{width: 180, height: 34}}>
                <option value="">{filterField.label} : ทั้งหมด</option>
                {pfOptions.map(v => <option key={v} value={v}>{v}</option>)}
              </select>
            )}
            {window.DateFilter && <window.DateFilter value={dateF} onChange={setDateF}/>}
          </>}
        </div>
        <div className="table-wrap">
          <table className="t">
            <thead><tr>
              <th style={{width: 36, paddingLeft: 14}}>
                <input type="checkbox" checked={allSelected} ref={el => el && (el.indeterminate = someSelected)} onChange={toggleAll}/>
              </th>
              {columns.map(c => <th key={c.key} className={c.right ? "right" : ""}>{c.label}</th>)}
            </tr></thead>
            <tbody>
              {filtered.map((r, i) => (
                <tr key={i} className="row-clickable" onClick={(e) => {
                  if (e.target.type === "checkbox") return;
                  onRowClick && onRowClick(r);
                }}>
                  <td style={{paddingLeft: 14}} onClick={e => e.stopPropagation()}>
                    <input type="checkbox" checked={selected.has(r[idKey])} onChange={() => toggleOne(r[idKey])}/>
                  </td>
                  {columns.map(c => (
                    <td key={c.key} className={c.right ? "right" : c.cls || ""}>
                      {c.render ? c.render(r) : r[c.key]}
                    </td>
                  ))}
                </tr>
              ))}
            </tbody>
          </table>
        </div>
      </Card>
    </>
  );
}

// ============ Delivery Note Detail ============
function DeliveryNoteDetail({ navigate }) {
  const D = window.SSSData;
  const _dnid = (window.location.hash.replace("#/", "").split("/")[1] || "").trim();
  const dn = (D.DeliveryNotes || []).find(x => x.no === _dnid) || D.DeliveryNotes[0];
  if (!dn) return <div style={{padding: 48, textAlign: "center", color: "var(--ink-3)"}}>ไม่พบเอกสาร — อาจถูกลบแล้ว <button className="btn primary" style={{marginLeft: 8}} onClick={() => navigate("delivery-notes")}>กลับ</button></div>;
  const lines = (Array.isArray(dn.lines) && dn.lines.length > 0) ? dn.lines : [];
  const cust = (D.Customers || []).find(c => c.name === dn.customer) || {};
  const C = window.SSSData.Company;
  const S = window.DeliveryStore;
  const linkedDD = S ? S.load().find(x => x.ref === dn.no) : null;

  const createDailyJob = async () => {
    const list = S.load();
    const existing = list.find(x => x.ref === dn.no);
    if (existing) {
      const go = await window.confirmDialog({
        title: "มีงานส่งของที่ผูกกับใบนี้แล้ว",
        message: `ใบส่งสินค้า <b class="mono">${dn.no}</b> ผูกกับงานส่ง <b class="mono">${existing.no}</b> (${S.fmtThai(existing.date)}) อยู่แล้ว`,
        confirmText: "ไปที่ส่งของประจำวัน"
      });
      if (go) navigate("daily-delivery");
      return;
    }
    const ok = await window.confirmDialog({
      title: "สร้างงานส่งของประจำวัน",
      message: `สร้างงานส่งวันนี้จากใบส่งสินค้า <b class="mono">${dn.no}</b> — ${dn.customer}`,
      bullets: [
        `คัดลอกรายการสินค้าทั้งหมด ${lines.length} รายการ`,
        `ผูกอ้างอิงกับ ${dn.no} ให้อัตโนมัติ`,
        "แจ้งเตือนพนักงานทุกคนทุกหน้าจอ"
      ],
      confirmText: "สร้างงานส่งวันนี้"
    });
    if (!ok) return;
    const rec = S.normalize({
      id: "TD-" + Date.now(), no: S.genNo(list), date: S.todayStr(), time: "10:00",
      customer: dn.customer,
      address: cust.address || "",
      assignee: "", assigneeName: dn.driver || "",
      note: `จากใบส่งสินค้า ${dn.no}`, ref: dn.no, status: "รอส่ง",
      items: lines.map(l => ({ name: l.name, qty: l.qty, unit: l.unit, photo: null, checked: false })),
      photosBefore: [], photosAfter: [], signature: null, receiverName: "", deliveredAt: ""
    });
    S.save([...list, rec]);
    window.DeliveryAlertAPI && window.DeliveryAlertAPI.triggerNow();
    window.logActivity && window.logActivity("สร้างงานส่งของ", `${rec.no} จากใบส่งสินค้า ${dn.no}`, "delivery");
    window.toast && window.toast(`สร้างงานส่ง ${rec.no} จาก ${dn.no} แล้ว — แจ้งเตือนทุกคนแล้ว`);
    setTimeout(() => navigate("daily-delivery"), 450);
  };

  return (
    <>
      <PageHead
        title={<span style={{display: "flex", alignItems: "center", gap: 12}}>
          <Btn kind="ghost" size="sm" onClick={() => navigate("delivery-notes")}><I.chevronLeft size={14} className="icon"/>กลับ</Btn>
          ใบส่งสินค้า <span className="mono" style={{fontSize: 16, color: "var(--ink-3)", fontWeight: 500}}>{dn.no}</span>
        </span>}
        sub={`ลูกค้า: ${dn.customer} · ${dn.date}`}
        right={<>
          <Btn icon={I.truck} kind="ghost" onClick={createDailyJob}>{linkedDD ? `งานส่งวันนี้: ${linkedDD.no}` : "สร้างงานส่งประจำวัน"}</Btn>
          <Btn icon={I.arrowRight} kind="ghost" onClick={async () => {
            const ok = await window.confirmDialog({
              title: "แปลงเป็นใบแจ้งหนี้",
              message: `สร้างใบแจ้งหนี้จากใบส่งของ <b class="mono">${dn.no}</b> — ${dn.customer}`,
              bullets: [
                `คัดลอกรายการสินค้าทั้งหมด ${lines.length} รายการ`,
                `ผูกอ้างอิงกับ ${dn.no} ให้อัตโนมัติ`,
                "เปิดฟอร์มใบแจ้งหนี้ให้ตรวจแล้วบันทึก"
              ],
              confirmText: "แปลงเป็นใบแจ้งหนี้"
            });
            if (!ok) return;
            if (!window.openNewDoc) { window.toast && window.toast("ระบบออกเอกสารยังไม่พร้อม ลองรีเฟรช"); return; }
            window.openNewDoc("invoice", {
              fromDoc: dn.no,
              partyName: dn.customer,
              project: dn.project || "",
              projectName: dn.projectName || "",
              lines: lines.map(l => ({ sku: l.sku, name: l.name, qty: l.qty, unit: l.unit, price: l.price })),
              discount: Number(dn.discount) || 0,
              vatPct: dn.vatPct != null ? Number(dn.vatPct) : 7
            });
            window.DocStatus2Store && window.DocStatus2Store.set("delivery-note", dn.no, "แปลงเป็นใบแจ้งหนี้แล้ว");
            window.logActivity && window.logActivity("แปลงเอกสาร", `ใบส่งของ ${dn.no} → ใบแจ้งหนี้`, "create");
          }}>แปลงเป็นใบแจ้งหนี้</Btn>
          <Btn icon={I.msg} kind="ghost" onClick={() => window.toast && window.toast(`ส่งลิงก์ใบส่งสินค้า ${dn.no} ทาง LINE ให้ ${dn.customer} แล้ว`)}>แจ้งลูกค้า (LINE)</Btn>
          <Btn icon={I.print} kind="ghost" onClick={() => window.printDocPaperWithOptions()}>พิมพ์</Btn>
          <Btn icon={I.download} kind="ghost" onClick={() => window.printDocPaperWithOptions()}>PDF</Btn>
          {dn.status !== "ยกเลิกแล้ว" && <Btn icon={I.close} kind="ghost" onClick={() => window.cancelDocFlow && window.cancelDocFlow("DeliveryNotes", dn.no, "ใบส่งสินค้า")}>ยกเลิกเอกสาร</Btn>}
          <Btn icon={I.trash} kind="ghost" onClick={() => window.deleteDocFlow && window.deleteDocFlow("DeliveryNotes", dn.no, "ใบส่งสินค้า", navigate)}>ลบ</Btn>
          <Btn icon={I.check} kind="primary" onClick={async () => {
            const ok = await window.confirmDialog({
              title: "บันทึกการเซ็นรับสินค้า",
              message: `ยืนยันว่าลูกค้า <b>${dn.customer}</b> เซ็นรับสินค้าตาม <b class="mono">${dn.no}</b> ครบถ้วนแล้ว`,
              confirmText: "บันทึกเซ็นรับ"
            });
            if (!ok) return;
            window.logActivity && window.logActivity("เซ็นรับสินค้า", `${dn.no} — ${dn.customer}`, "update");
            window.toast && window.toast(`บันทึกการเซ็นรับ ${dn.no} แล้ว`);
          }}>บันทึกเซ็นรับ</Btn>
        </>}
      />

      <div className="grid" style={{gridTemplateColumns: "1fr 320px", gap: 14, alignItems: "flex-start"}}>
        <div>
          <window.DocRefPanel row={dn} navigate={navigate}/>
          <Card padded={false} className="mb-lg" flush>
            <div style={{padding: "14px 16px"}}>
              <Workflow steps={["เตรียมสินค้า", "ขึ้นรถ", "กำลังจัดส่ง", "ส่งถึงที่หมาย", "ลูกค้าเซ็นรับ"]} current={2}/>
            </div>
          </Card>

          {/* Tracking strip */}
          <Card title="ติดตามการจัดส่ง" sub="GPS realtime" className="mb-lg">
            <div style={{display: "flex", gap: 14}}>
              <div style={{flex: 1, height: 160, background: "var(--surface-2)", border: "1px dashed var(--line-strong)", borderRadius: 8, display: "grid", placeItems: "center", color: "var(--ink-3)", position: "relative", overflow: "hidden"}}>
                <div style={{position: "absolute", inset: 0, backgroundImage: "linear-gradient(0deg, var(--line) 1px, transparent 1px), linear-gradient(90deg, var(--line) 1px, transparent 1px)", backgroundSize: "30px 30px", opacity: 0.5}}/>
                <div style={{position: "absolute", left: "20%", top: "55%"}}>
                  <div style={{width: 24, height: 24, borderRadius: "50%", background: "var(--accent)", color: "var(--accent-ink)", display: "grid", placeItems: "center", boxShadow: "0 0 0 6px rgba(12,10,9,0.1)"}}><I.truck size={12}/></div>
                  <div style={{fontSize: 10, marginTop: 2, fontWeight: 600}}>รถ SSS-01</div>
                </div>
                <div style={{position: "absolute", left: "75%", top: "30%"}}>
                  <div style={{width: 16, height: 16, borderRadius: "50%", background: "var(--surface)", border: "2px solid var(--ink-1)"}}/>
                  <div style={{fontSize: 10, marginTop: 2}}>ปลายทาง</div>
                </div>
                <div style={{position: "absolute", left: "10%", top: "70%"}}>
                  <div style={{width: 12, height: 12, borderRadius: "50%", background: "var(--ink-3)"}}/>
                  <div style={{fontSize: 10, marginTop: 2, color: "var(--ink-3)"}}>โกดัง SSS</div>
                </div>
                <svg style={{position: "absolute", inset: 0, width: "100%", height: "100%"}}>
                  <path d="M 50 130 Q 200 60, 380 65" stroke="var(--ink-1)" strokeWidth="2" strokeDasharray="4 4" fill="none"/>
                </svg>
              </div>
              <div style={{width: 200, display: "flex", flexDirection: "column", gap: 10, fontSize: 12.5}}>
                <div><div className="label">ขับโดย</div><div style={{fontWeight: 500}}>{dn.driver}</div></div>
                <div><div className="label">ทะเบียน</div><div className="mono">{dn.plate}</div></div>
                <div><div className="label">ระยะทาง</div><div>34.2 กม. · 28 นาที</div></div>
                <div><div className="label">ETA</div><div style={{fontWeight: 600}}>14:48 น.</div></div>
                <Btn size="sm" icon={I.send} kind="ghost">ส่งลิงก์ติดตามให้ลูกค้า</Btn>
              </div>
            </div>
          </Card>

          {/* Doc preview */}
          <div className="doc-paper" style={{borderTop: `5px solid ${window.getDocColor("delivery-note")}`}}>
            <div style={{display: "flex", justifyContent: "space-between", alignItems: "flex-start", gap: 20, marginBottom: 24}}>
              <div style={{display: "flex", gap: 20}}>
                <div style={{background: window.getDocColor("delivery-note"), borderRadius: 6, alignSelf: "stretch", minHeight: 56, maxHeight: 80, display: "flex", alignItems: "center", justifyContent: "center", padding: "8px 16px", flexShrink: 0}}><img src={window.docLogo ? window.docLogo() : "assets/logo-ink.png"} alt={C.name} style={{width: 132, maxHeight: "100%", objectFit: "contain", display: "block"}}/></div>
                <div>
                  <div style={{fontWeight: 700, fontSize: 14}}>{C.name}</div>
                  <div style={{fontSize: 11, color: "#57534e"}}>{C.nameEn}</div>
                  <div style={{fontSize: 11, color: "#57534e", marginTop: 4, maxWidth: 320}}>{C.address}</div>
                  <div style={{fontSize: 11, color: "#57534e"}}>โทร {C.phone}</div>
                </div>
              </div>
              <div style={{textAlign: "right"}}>
                <h1 style={{color: window.getDocColor("delivery-note")}}>ใบส่งสินค้า</h1>
                <div style={{fontSize: 10, color: "#78716c", letterSpacing: "0.06em"}}>DELIVERY NOTE</div>
                <div style={{marginTop: 8, fontSize: 12}}>
                  <div>เลขที่ <b className="mono">{dn.no}</b></div>
                  <div>วันที่ <b>{dn.date}</b></div>
                </div>
              </div>
            </div>

            <div style={{display: "grid", gridTemplateColumns: "1fr 1fr", gap: 16, marginBottom: 16, fontSize: 12}}>
              <div style={{border: "1px solid #e7e5e4", padding: 10, borderRadius: 4}}>
                <div style={{fontSize: 10, color: "#78716c", textTransform: "uppercase", letterSpacing: "0.05em", marginBottom: 2}}>ผู้รับสินค้า</div>
                <div style={{fontWeight: 600}}>{dn.customer}</div>
                {cust.address && <div style={{color: "#57534e"}}>{cust.address}</div>}
                {cust.contact && <div style={{color: "#57534e"}}>ผู้ติดต่อ: {cust.contact}{cust.phone && cust.phone !== "—" ? " " + cust.phone : ""}</div>}
              </div>
              <div style={{border: "1px solid #e7e5e4", padding: 10, borderRadius: 4}}>
                <div style={{fontSize: 10, color: "#78716c", textTransform: "uppercase", letterSpacing: "0.05em", marginBottom: 2}}>การจัดส่ง</div>
                {dn.driver && <div>คนขับ: {dn.driver}</div>}
                {dn.plate && <div>ทะเบียน: {dn.plate}</div>}
                {dn.ref && <div>อ้างอิง: {dn.ref}</div>}
              </div>
            </div>

            <table>
              <thead><tr><th style={{width: 30}}>#</th><th>SKU</th><th>รายการ</th><th className="right" style={{width: 80}}>จำนวน</th><th style={{width: 60}}>หน่วย</th><th style={{width: 80}}>หมายเหตุ</th></tr></thead>
              <tbody>
                {lines.map((l, i) => (
                  <tr key={i}>
                    <td>{i + 1}</td>
                    <td className="mono">{l.sku}</td>
                    <td>{l.name}</td>
                    <td className="right">{l.qty}</td>
                    <td>{l.unit}</td>
                    <td></td>
                  </tr>
                ))}
              </tbody>
            </table>

            <div style={{marginTop: 14, padding: "8px 12px", background: "#fef9f3", border: "1px solid #fed7aa", borderRadius: 4, fontSize: 11.5, color: "#7c2d12"}}>
              <b>หมายเหตุ:</b> ลูกค้าโปรดตรวจรับสินค้าให้ครบถ้วนก่อนเซ็นรับ หากชำรุดเสียหายแจ้งทันที สินค้าที่รับเรียบร้อยแล้วถือว่าครบถ้วน
            </div>

            <div style={{display: "grid", gridTemplateColumns: "1fr 1fr 1fr", gap: 40, marginTop: 48, fontSize: 11.5}}>
              <div style={{textAlign: "center"}}>
                <div style={{borderTop: "1px solid #44403c", paddingTop: 6, marginTop: 30}}>ผู้ส่งของ · สุชาติ ขนส่ง</div>
              </div>
              <div style={{textAlign: "center"}}>
                <div style={{borderTop: "1px solid #44403c", paddingTop: 6, marginTop: 30}}>ผู้รับของ · ______________________</div>
              </div>
              <div style={{textAlign: "center"}}>
                <div style={{borderTop: "1px solid #44403c", paddingTop: 6, marginTop: 30}}>วันที่/เวลารับ · ______________</div>
              </div>
            </div>
          </div>
        </div>

        {/* Side */}
        <div style={{display: "flex", flexDirection: "column", gap: 14}}>
          <Card title="สถานะ" right={<Badge>{dn.status}</Badge>}>
            <div style={{display: "flex", flexDirection: "column", gap: 10, fontSize: 13}}>
              <Row label="ออกใบส่งของ" value={`${dn.date} 09:20`}/>
              <Row label="ขึ้นรถ" value={`${dn.date} 13:15`}/>
              <Row label="ออกจากโกดัง" value={`${dn.date} 14:20`}/>
              <Row label="คาดถึงปลายทาง" value="14:48 (ประมาณ)"/>
            </div>
          </Card>

          <Card title="ผูกกับ">
            <div style={{display: "flex", flexDirection: "column", gap: 8, fontSize: 13}}>
              <div><div className="label">ใบแจ้งหนี้</div><Badge>INV-2569-0204</Badge></div>
              <div><div className="label">โปรเจกต์</div><Badge>JOB-2569-020</Badge></div>
              <div><div className="label">ใบกำกับภาษี</div><Badge>TX-2569-0204</Badge></div>
              <div>
                <div className="label">งานส่งของประจำวัน</div>
                {linkedDD
                  ? <button className="badge outline mono" style={{cursor: "pointer"}} title="เปิดหน้าส่งของประจำวัน" onClick={() => navigate("daily-delivery")}>{linkedDD.no} · {linkedDD.status}</button>
                  : <div style={{display: "flex", alignItems: "center", gap: 8}}>
                      <span style={{fontSize: 12, color: "var(--ink-3)"}}>ยังไม่ได้สร้าง</span>
                      <Btn size="sm" kind="ghost" icon={I.plus} onClick={createDailyJob}>สร้างงานส่ง</Btn>
                    </div>}
              </div>
            </div>
          </Card>

          <Card title="หลักฐานการรับ" right={<Btn size="sm" kind="ghost" icon={I.plus}>เพิ่ม</Btn>}>
            <div className="attach-grid">
              <Attach name="signature.png" size="80 KB" ext="PNG"/>
              <Attach name="photo-delivery.jpg" size="1.2 MB" ext="JPG"/>
            </div>
          </Card>
        </div>
      </div>
    </>
  );
}

// ============ Receipts (ใบเสร็จรับเงิน) ============
function Receipts({ navigate }) {
  const D = window.SSSData;
  const total = D.Receipts.reduce((s, r) => s + r.amount, 0);
  return (
    <DocListPage
      title="ใบเสร็จรับเงิน · Receipts"
      sub="ออกใบเสร็จเมื่อได้รับเงินจากลูกค้า · เชื่อมกับใบแจ้งหนี้อัตโนมัติ"
      docType="receipt" rows={D.Receipts}
      statusFilter={["ออกแล้ว", "ยกเลิกแล้ว"]}
      createLabel="ออกใบเสร็จ"
      onCreate={() => window.openNewDoc && window.openNewDoc("receipt")}
      onRowClick={(r) => navigate("receipts/" + r.no)}
      kpis={[
        { label: "ใบเสร็จเดือนนี้", value: D.Receipts.length, sub: `ทุกใบ ออกครบ`, icon: I.receipt },
        { label: "ยอดรับเงินรวม", value: `฿${fmt0(total)}`, sub: "ทุกใบเสร็จ", icon: I.money },
        { label: "ช่องทางรับเงิน", value: `${D.Receipts.filter(r=>/โอน/.test(r.method||'')).length} โอน`, sub: `${D.Receipts.filter(r=>/สด/.test(r.method||'')).length} เงินสด · ${D.Receipts.filter(r=>/เช็ค/.test(r.method||'')).length} เช็ค`, icon: I.calc },
        { label: "เฉลี่ยต่อใบ", value: `฿${fmt0(D.Receipts.length ? total / D.Receipts.length : 0)}`, sub: "ต่อใบเสร็จ", icon: I.invoice }
      ]}
      columns={[
        { key: "no", label: "เลขที่", render: r => <span className="mono">{r.no}</span> },
        { key: "date", label: "วันที่" },
        { key: "customer", label: "ลูกค้า", render: r => <div>{r.customer}{r.detail && <div style={{fontSize: 11, color: "var(--ink-4)", marginTop: 1, maxWidth: 280, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap"}}>{r.detail}</div>}</div> },
        { key: "ref", label: "อ้างอิงใบแจ้งหนี้", render: r => <span className="mono" style={{fontSize: 11, color: "var(--ink-3)"}}>{r.ref}</span> },
        { key: "method", label: "วิธีรับชำระ" },
        { key: "amount", label: "ยอดรับ", right: true, render: r => <span className="amount">฿{fmt0(r.amount)}</span> },
        { key: "status", label: "สถานะ", render: r => <Badge tone="success">{r.status}</Badge> }
      ]}
    />
  );
}

// ============ Receipt Detail (printable) ============
function ReceiptDetail({ navigate }) {
  const D = window.SSSData;
  const _rid = (window.location.hash.replace("#/", "").split("/")[1] || "").trim();
  const r = (D.Receipts || []).find(x => x.no === _rid) || D.Receipts[0];
  if (!r) return <div style={{padding: 48, textAlign: "center", color: "var(--ink-3)"}}>ไม่พบเอกสาร — อาจถูกลบแล้ว <button className="btn primary" style={{marginLeft: 8}} onClick={() => navigate("receipts")}>กลับ</button></div>;
  const C = D.Company;
  // รายการสินค้าจากที่กรอกตอนสร้าง (ถ้ามี) — ไม่งั้นแสดงรายการรวมแบบเดิม
  const rcLines = Array.isArray(r.lines) && r.lines.length ? r.lines : null;
  const rcSubtotal = rcLines ? rcLines.reduce((s, l) => s + (Number(l.qty) || 0) * (Number(l.price) || 0), 0) : 0;
  const rcDiscount = Number(r.discount) || 0;
  const rcVatPct = r.vatPct != null ? Number(r.vatPct) : 7;
  const rcAfterDisc = rcSubtotal - rcDiscount;
  // ยึด r.amount เป็นยอดจริงที่บันทึก (กัน rounding เพี้ยน)
  const rcGrand = r.amount;
  // ยอดก่อนภาษี/ยอดภาษี: ใช้ค่าที่บันทึกจริงตอนสร้างก่อน (ถูกทั้งแบบราคารวมและไม่รวม VAT)
  // ใบเก่าที่ไม่มีข้อมูล → คำนวณกลับจากรายการ/ยอดรวมแบบเดิม
  const vat = r.vat != null ? Number(r.vat)
    : (rcLines ? (rcVatPct > 0 ? Math.max(0, rcGrand - rcAfterDisc) : 0) : r.amount * 7 / 107);
  const base = r.base != null ? Number(r.base)
    : (rcLines ? rcAfterDisc : r.amount - vat);
  // หัก ณ ที่จ่าย — ใช้ค่าที่บันทึกติดใบเสร็จ (ใบเก่าไม่มี = ไม่หัก)
  const rcWhtPct = r.whtPct != null ? Number(r.whtPct) : 0;
  const rcWht = r.wht != null ? Number(r.wht) : (rcWhtPct > 0 ? base * rcWhtPct / 100 : 0);
  // ข้อมูลลูกค้า: snapshot ที่บันทึกติดเอกสาร (ใหม่) > โปรไฟล์ CRM ตามชื่อ — ให้ที่อยู่/เลขภาษี/ผู้ติดต่อครบ
  const cust = Object.assign({}, (D.Customers || []).find(c => c.name === r.customer) || {}, r.partySnap || {});
  const issuerName = r.issuedBy || (window.Session && window.Session.get() && window.Session.get().name) || (C.currentUser && C.currentUser.name) || "—";
  // ลายเซ็น/ตราประทับจริง (เมื่อบันทึกใบไว้แบบ "แนบลายเซ็น+ตรา")
  const rcSig = r.signStamp ? (((window.lsSyncGet && window.lsSyncGet("sss-user-signs")) || {})[issuerName] || "") : "";
  const rcStamp = r.signStamp ? (((window.lsSyncGet && window.lsSyncGet("sss-doc-sign-assets")) || {}).stamp || "") : "";

  return (
    <>
      <PageHead
        title={<span style={{display: "flex", alignItems: "center", gap: 12}}>
          <Btn kind="ghost" size="sm" onClick={() => navigate("receipts")}><I.chevronLeft size={14} className="icon"/>กลับ</Btn>
          ใบเสร็จรับเงิน <span className="mono" style={{fontSize: 16, color: "var(--ink-3)", fontWeight: 500}}>{r.no}</span>
        </span>}
        sub={`รับจาก: ${r.customer} · ${r.date}`}
        right={<>
          <Btn icon={I.edit} kind="ghost" onClick={() => window.editDocFlow(r, "receipt", () => window.openNewDoc && window.openNewDoc("receipt", { editing: true, docNo: r.no, date: r.date, partyName: r.customer, method: r.method, discount: Number(r.discount) || 0, vatPct: r.vatPct != null ? Number(r.vatPct) : 7, lines: (rcLines || []).map(l => ({ sku: l.sku, name: l.name, qty: l.qty, unit: l.unit, price: l.price })) }), navigate)}>แก้ไขเอกสาร</Btn>
          <Btn icon={I.msg} kind="ghost" onClick={() => window.toast && window.toast(`ส่งใบเสร็จ ${r.no} ทางอีเมลให้ ${r.customer} แล้ว`)}>ส่งอีเมล</Btn>
          <Btn icon={I.print} kind="ghost" onClick={() => window.printDocPaperWithOptions()}>พิมพ์</Btn>
          <Btn icon={I.download} kind="primary" onClick={() => window.printDocPaperWithOptions()}>ดาวน์โหลด PDF</Btn>
          {r.status !== "ยกเลิกแล้ว" && <Btn icon={I.close} kind="ghost" onClick={() => window.cancelDocFlow && window.cancelDocFlow("Receipts", r.no, "ใบเสร็จรับเงิน")}>ยกเลิกเอกสาร</Btn>}
          <Btn icon={I.trash} kind="ghost" onClick={() => window.deleteDocFlow && window.deleteDocFlow("Receipts", r.no, "ใบเสร็จรับเงิน", navigate)}>ลบ</Btn>
        </>}
      />

      <window.DocRefPanel row={r} navigate={navigate}/>
      <div className="grid" style={{gridTemplateColumns: "1fr 320px", gap: 14, alignItems: "flex-start"}}>
        <div className="doc-paper" style={{borderTop: `5px solid ${window.getDocColor("receipt")}`}}>
          <div style={{display: "flex", justifyContent: "space-between", alignItems: "flex-start", gap: 20, marginBottom: 24}}>
            <div style={{display: "flex", gap: 20}}>
              <div style={{background: window.getDocColor("receipt"), borderRadius: 6, alignSelf: "stretch", minHeight: 56, maxHeight: 80, display: "flex", alignItems: "center", justifyContent: "center", padding: "8px 16px", flexShrink: 0}}><img src={window.docLogo ? window.docLogo() : "assets/logo-ink.png"} alt={C.name} style={{width: 132, maxHeight: "100%", objectFit: "contain", display: "block"}}/></div>
              <div>
                <div style={{fontWeight: 700, fontSize: 14}}>{C.name}</div>
                <div style={{fontSize: 11, color: "#57534e"}}>{C.nameEn}</div>
                <div style={{fontSize: 11, color: "#57534e", marginTop: 4, maxWidth: 320}}>{C.address}</div>
                <div style={{fontSize: 11, color: "#57534e"}}>โทร {C.phone} · เลขผู้เสียภาษี {C.taxId}</div>
              </div>
            </div>
            <div style={{textAlign: "right"}}>
              <h1 style={{color: window.getDocColor("receipt")}}>ใบเสร็จรับเงิน</h1>
              <div style={{fontSize: 10, color: "#78716c", letterSpacing: "0.06em"}}>OFFICIAL RECEIPT</div>
              <div style={{marginTop: 8, fontSize: 12}}>
                <div>เลขที่ <b className="mono">{r.no}</b></div>
                <div>วันที่ <b>{r.date}</b></div>
              </div>
              <div style={{marginTop: 12}}><span className="doc-stamp">PAID · ชำระแล้ว</span></div>
            </div>
          </div>

          <div style={{display: "grid", gridTemplateColumns: "1fr 1fr", gap: 16, marginBottom: 16, fontSize: 12}}>
            <div style={{border: "1px solid #e7e5e4", padding: 10, borderRadius: 4}}>
              <div style={{fontSize: 10, color: "#78716c", textTransform: "uppercase", letterSpacing: "0.05em", marginBottom: 2}}>ได้รับเงินจาก</div>
              <div style={{fontWeight: 600}}>{r.customer}</div>
              {r.detail && <div style={{fontSize: 11.5, color: "#44403c", marginTop: 2}}>รายละเอียด : {r.detail}</div>}
              <div style={{color: "#57534e", lineHeight: 1.6}}>
                {cust.address && <div>{cust.address}</div>}
                <div>เลขผู้เสียภาษี <span className="mono" style={{color: "#0c0a09"}}>{cust.taxId || r.taxId || "—"}</span></div>
                {cust.contact && <div>ผู้ติดต่อ: {cust.contact}</div>}
                {cust.phone && cust.phone !== "—" && <div className="mono">โทร {cust.phone}</div>}
              </div>
            </div>
            <div style={{border: "1px solid #e7e5e4", padding: 10, borderRadius: 4}}>
              <div style={{fontSize: 10, color: "#78716c", textTransform: "uppercase", letterSpacing: "0.05em", marginBottom: 2}}>วิธีรับชำระ</div>
              <div style={{fontWeight: 600}}>{r.method}</div>
              <div style={{color: "#57534e", lineHeight: 1.6}}>
                {r.ref && <div>อ้างอิง: <span className="mono">{r.ref}</span></div>}
                {r.fromDoc && !r.ref && <div>อ้างอิง: <span className="mono">{r.fromDoc}</span></div>}
                <div>ผู้ออกเอกสาร: <b style={{color: "#0c0a09"}}>{issuerName}</b></div>
              </div>
            </div>
          </div>

          <table>
            <thead><tr><th style={{width: 30}}>#</th><th>รายการ</th><th className="right" style={{width: 70}}>จำนวน</th><th className="right" style={{width: 90}}>ราคา/หน่วย</th><th className="right" style={{width: 120}}>จำนวนเงิน (บาท)</th></tr></thead>
            <tbody>
              {rcLines ? rcLines.map((l, i) => (
                <tr key={i}><td>{i + 1}</td>
                  <td>
                    <div style={{fontWeight: 500}}>{l.name || "—"}</div>
                    {l.sku && <div className="mono" style={{fontSize: 10, color: "#78716c"}}>{l.sku}</div>}
                  </td>
                  <td className="right">{l.qty} {l.unit || ""}</td>
                  <td className="right">{fmtM(Number(l.price) || 0)}</td>
                  <td className="right">{fmtM((Number(l.qty) || 0) * (Number(l.price) || 0))}</td>
                </tr>
              )) : (
                <tr><td>1</td>
                  <td colSpan={3}>
                    <div style={{fontWeight: 500}}>รับชำระค่าสินค้าและบริการ</div>
                    <div style={{fontSize: 10, color: "#78716c"}}>ตามใบแจ้งหนี้ {r.ref}</div>
                  </td>
                  <td className="right">{fmtM(r.amount)}</td>
                </tr>
              )}
            </tbody>
          </table>

          <div style={{display: "flex", justifyContent: "flex-end", marginTop: 14}}>
            <div className="totals">
              {rcDiscount > 0 && <div className="row"><span>รวมเป็นเงิน</span><span>{fmtM(rcSubtotal)}</span></div>}
              {rcDiscount > 0 && <div className="row"><span>ส่วนลด</span><span>−{fmtM(rcDiscount)}</span></div>}
              <div className="row"><span>ราคารวมก่อนภาษี</span><span>{fmtM(base)}</span></div>
              <div className="row"><span>ภาษีมูลค่าเพิ่ม (VAT {rcLines ? rcVatPct : 7}%)</span><span>{fmtM(vat)}</span></div>
              <div className="row grand"><span>รวมรับเงินทั้งสิ้น</span><span>฿ {fmtM(rcGrand)}</span></div>
              {rcWht > 0 && <div className="row"><span>หัก ภาษี ณ ที่จ่าย {rcWhtPct}%</span><span style={{color: "#b91c1c"}}>−{fmtM(rcWht)}</span></div>}
              {rcWht > 0 && <div className="row" style={{fontWeight: 700}}><span>ยอดรับชำระสุทธิ</span><span>฿ {fmtM(rcGrand - rcWht)}</span></div>}
            </div>
          </div>

          <div style={{marginTop: 10, padding: "8px 12px", background: "#f7f6f4", borderRadius: 4, fontSize: 11.5, color: "#44403c", fontStyle: "italic"}}>
            ({window.UI.bahtText(rcGrand)})
          </div>

          <div style={{marginTop: 16, padding: "10px 12px", background: "#f0f9f4", border: "1px solid #86efac", borderRadius: 4, fontSize: 11.5, color: "#166534"}}>
            <b>✓ ได้รับเงินครบถ้วนเรียบร้อยแล้ว</b> — ขอบคุณที่ใช้บริการ {C.shortName || C.name}
          </div>

          <div style={{display: "grid", gridTemplateColumns: "1fr 1fr", gap: 40, marginTop: 24, fontSize: 11.5}}>
            <div style={{textAlign: "center"}}>
              <div style={{height: 112, display: "flex", alignItems: "flex-end", justifyContent: "center"}}>
                {rcSig ? <img src={rcSig} alt="ลายเซ็น" style={{maxHeight: 78, maxWidth: "70%", objectFit: "contain"}}/> : null}
              </div>
              <div style={{borderTop: "1px solid #44403c", paddingTop: 6}}>ผู้รับเงิน · {issuerName}</div>
            </div>
            <div style={{textAlign: "center"}}>
              <div style={{height: 112, display: "flex", alignItems: "flex-end", justifyContent: "center"}}>
                {rcStamp ? <img src={rcStamp} alt="ตราประทับ" style={{maxHeight: 108, objectFit: "contain"}}/> : null}
              </div>
              <div style={{borderTop: "1px solid #44403c", paddingTop: 6}}>ตราประทับบริษัท</div>
            </div>
          </div>
        </div>

        <div style={{display: "flex", flexDirection: "column", gap: 14}}>
          <Card title="รายละเอียดการรับเงิน">
            <div style={{display: "flex", flexDirection: "column", gap: 8, fontSize: 13}}>
              <Row label="ยอดรับ" value={`฿${fmtM(r.amount)}`}/>
              <Row label={`VAT ${rcLines ? rcVatPct : 7}%`} value={`฿${fmtM(vat)}`}/>
              <Row label="วิธีรับ" value={r.method || "—"}/>
              {r.accountId && window.CompanyCashStore && <Row label="เข้าบัญชี" value={`${window.CompanyCashStore.accEmoji(r.accountId)} ${window.CompanyCashStore.accName(r.accountId)}`}/>}
              {r.accountId && <Row label="สถานะ" value={<span style={{color: "var(--success)"}}>✓ เพิ่มเข้าบัญชีอัตโนมัติแล้ว</span>}/>}
            </div>
          </Card>

          <Card title="กระทบยอด" sub="reconciliation">
            <div style={{fontSize: 13, lineHeight: 1.7}}>
              <div>✓ ใบแจ้งหนี้: <b className="mono">{r.ref}</b></div>
              <div>✓ ตรงกับ statement KBank</div>
              <div>✓ บันทึกเข้าระบบบัญชีแล้ว</div>
              <div>✓ อัพเดทยอดลูกหนี้แล้ว</div>
            </div>
          </Card>

          <Card title="ส่งใบเสร็จให้ลูกค้า">
            <div style={{display: "flex", flexDirection: "column", gap: 8}}>
              <Btn icon={I.msg} kind="ghost" onClick={() => window.toast && window.toast(`ส่งใบเสร็จ ${r.no} ทางอีเมลแล้ว`)}>ส่งอีเมล</Btn>
              <Btn icon={I.send} kind="ghost" onClick={() => window.toast && window.toast(`ส่งใบเสร็จ ${r.no} ทาง LINE แล้ว`)}>ส่งทาง LINE</Btn>
              <Btn icon={I.download} onClick={() => window.printDocPaperWithOptions()}>ดาวน์โหลด PDF</Btn>
            </div>
          </Card>

          {window.DocHistoryPanel && <window.DocHistoryPanel listKey="Receipts" docNo={r.no}/>}
        </div>
      </div>
    </>
  );
}

window.DeliveryNoteDetail = DeliveryNoteDetail;
window.Receipts = Receipts;
window.ReceiptDetail = ReceiptDetail;

// ============ Quotation list ============
function Quotations({ navigate }) {
  const D = window.SSSData;
  return (
    <DocListPage
      title="ใบเสนอราคา · Quotations"
      sub="ออก ติดตาม และแปลงเป็นใบสั่งซื้อหรือใบแจ้งหนี้"
      docType="quotation" rows={D.Quotations}
      statusFilter={["ร่าง", "ส่งแล้ว", "รออนุมัติ", "อนุมัติแล้ว", "แปลงเป็นใบสั่ง", "ปฏิเสธ", "ยกเลิกแล้ว"]}
      createLabel="สร้างใบเสนอราคา"
      onCreate={() => window.openNewDoc && window.openNewDoc("quotation")}
      onRowClick={(r) => navigate("quotations/" + r.no)}
      kpis={[
        { label: "เปิดอยู่ทั้งหมด", value: D.Quotations.length, sub: "เดือนนี้", icon: I.doc },
        { label: "รออนุมัติ", value: D.Quotations.filter(q => q.status === "รออนุมัติ").length, sub: `฿${fmt0(D.Quotations.filter(q => q.status === "รออนุมัติ").reduce((s,q)=>s+q.amount,0))}`, icon: I.clock },
        { label: "อนุมัติแล้ว", value: D.Quotations.filter(q => q.status === "อนุมัติแล้ว").length, sub: "รอแปลงเป็นใบสั่ง", icon: I.check },
        { label: "อัตราแปลงเป็นใบสั่ง", value: `${D.Quotations.length ? Math.round(D.Quotations.filter(q=>q.status==='แปลงเป็นใบสั่ง').length / D.Quotations.length * 100) : 0}%`, sub: "QO → ใบสั่ง", icon: I.arrowRight }
      ]}
      columns={[
        { key: "no", label: "เลขที่", cls: "doc-no", render: r => <span className="mono">{r.no}</span> },
        { key: "date", label: "วันที่ออก" },
        { key: "customer", label: "ลูกค้า" },
        { key: "owner", label: "ผู้สร้าง" },
        { key: "validity", label: "ยืนราคา" },
        { key: "amount", label: "จำนวนเงิน", right: true, render: r => <span className="amount">฿{fmt0(r.amount)}</span> },
        { key: "status", label: "สถานะ", render: r => <Badge>{r.status}</Badge> }
      ]}
    />
  );
}

// ============ Quotation detail (also doubles as invoice/PO doc preview) ============
function QuotationDetail({ navigate }) {
  const D = window.SSSData;
  const [tab, setTab] = useState("preview");
  const [, forceQ] = useState(0);
  const _qid = (window.location.hash.replace("#/", "").split("/")[1] || "").trim();
  const q = (D.Quotations || []).find(x => x.no === _qid) || D.Quotations[0];
  if (!q) return <div style={{padding: 48, textAlign: "center", color: "var(--ink-3)"}}>ไม่พบเอกสาร — อาจถูกลบแล้ว <button className="btn primary" style={{marginLeft: 8}} onClick={() => navigate("quotations")}>กลับ</button></div>;
  // รายการสินค้าจริงที่กรอกตอนสร้าง — ถ้าไม่มี (ใบ seed) ใช้ตัวอย่างเดิม
  const hasLines = Array.isArray(q.lines) && q.lines.length > 0;
  const DEMO_LINES = [
    { sku: "SS316-SH-2.0", name: "แผ่นสแตนเลส 316L หนา 2.0 มม. 4x8 ฟุต", qty: 22, unit: "แผ่น", price: 11200 },
    { sku: "SS-TUBE-32", name: "ท่อสแตนเลส 304 1-1/4\" ยาว 6 ม.", qty: 18, unit: "เส้น", price: 1620 },
    { sku: "SVC-CAD-3D", name: "ค่าบริการออกแบบ 3D CAD — Layout ครัวอุตสาหกรรม", qty: 24, unit: "ชม.", price: 1200 },
    { sku: "SVC-CNC-LASER", name: "ค่าบริการตัด CNC Fiber Laser", qty: 36, unit: "ชม.", price: 850 },
    { sku: "SVC-WELD-TIG", name: "ค่าบริการเชื่อม TIG — ประกอบโครงเคาน์เตอร์", qty: 80, unit: "ชม.", price: 650 }
  ];
  const lines = (hasLines ? q.lines : DEMO_LINES).map(l => ({ ...l, total: (Number(l.qty) || 0) * (Number(l.price) || 0) }));
  const subtotal = lines.reduce((s, l) => s + l.total, 0);
  const discount = hasLines ? (Number(q.discount) || 0) : 4000;
  const afterDisc = subtotal - discount;
  const vatPct = q.vatPct != null ? Number(q.vatPct) : 7;
  // ใบจริง: ยึด q.amount เป็นยอดสุทธิ · ใบ demo: คำนวณ
  const grand = hasLines && q.amount ? q.amount : afterDisc + (vatPct > 0 ? afterDisc * vatPct / 100 : 0);
  const vat = vatPct > 0 ? Math.max(0, grand - afterDisc) : 0;
  // หัก ณ ที่จ่าย — ใช้ค่าที่บันทึกติดใบเสนอราคา (ใบเก่าไม่มี = ไม่หัก)
  const qWhtPct = q.whtPct != null ? Number(q.whtPct) : 0;
  const qWht = q.wht != null ? Number(q.wht) : (qWhtPct > 0 ? afterDisc * qWhtPct / 100 : 0);
  const cust = (D.Customers || []).find(c => c.name === q.customer) || {};
  const issuer = q.issuedBy || (D.Company.currentUser && D.Company.currentUser.name) || "";

  return (
    <>
      <PageHead
        title={<span style={{display: "flex", alignItems: "center", gap: 12}}>
          <Btn kind="ghost" size="sm" onClick={() => navigate("quotations")}><I.chevronLeft size={14} className="icon"/>กลับ</Btn>
          ใบเสนอราคา <span className="mono" style={{fontSize: 16, color: "var(--ink-3)", fontWeight: 500}}>{q.no}</span>
        </span>}
        sub={`ลูกค้า: ${q.customer} · สร้างเมื่อ ${q.date}`}
        right={<>
          <Btn icon={I.edit} kind="ghost" onClick={() => window.editDocFlow(q, "quotation", () => window.openNewDoc && window.openNewDoc("quotation", { editing: true, docNo: q.no, date: q.date, partyName: q.customer, notes: "", lines: lines.map(l => ({ sku: l.sku, name: l.name, qty: l.qty, unit: l.unit, price: l.price })) }), navigate)}>แก้ไขเอกสาร</Btn>
          <Btn icon={I.msg} kind="ghost" onClick={() => window.toast && window.toast(`ส่งใบเสนอราคา ${q.no} ทางอีเมลให้ ${q.customer} แล้ว`)}>ส่งทางอีเมล</Btn>
          <Btn icon={I.print} kind="ghost" onClick={() => window.printDocPaperWithOptions()}>พิมพ์</Btn>
          <Btn icon={I.download} kind="ghost" onClick={() => window.printDocPaperWithOptions()}>PDF</Btn>
          {q.status !== "ยกเลิกแล้ว" && <Btn icon={I.close} kind="ghost" onClick={() => window.cancelDocFlow && window.cancelDocFlow("Quotations", q.no, "ใบเสนอราคา", () => forceQ(n => n + 1))}>ยกเลิกเอกสาร</Btn>}
          <Btn icon={I.trash} kind="ghost" onClick={() => window.deleteDocFlow && window.deleteDocFlow("Quotations", q.no, "ใบเสนอราคา", navigate)}>ลบ</Btn>
          <Btn icon={I.truck} kind="ghost" onClick={async () => {
            const ok = await window.confirmDialog({
              title: "แปลงใบเสนอราคาเป็นใบส่งของ",
              message: `สร้างใบส่งสินค้าจาก <b class="mono">${q.no}</b> — ${q.customer}`,
              bullets: ["คัดลอกรายการสินค้าทั้งหมด", `ผูกอ้างอิงกับ ${q.no}`, "เปิดฟอร์มใบส่งสินค้าให้ตรวจแล้วบันทึก"],
              confirmText: "สร้างใบส่งของ"
            });
            if (!ok) return;
            if (!window.openNewDoc) { window.toast && window.toast("ระบบออกเอกสารยังไม่พร้อม ลองรีเฟรช"); return; }
            window.openNewDoc("delivery-note", {
              fromDoc: q.no, partyName: q.customer, project: q.project || "", projectName: q.projectName || "",
              lines: lines.map(l => ({ sku: l.sku, name: l.name, qty: l.qty, unit: l.unit, price: l.price }))
            });
            window.DocStatus2Store && window.DocStatus2Store.set("quotation", q.no, "แปลงเป็นใบส่งสินค้าแล้ว");
            window.logActivity && window.logActivity("แปลงเอกสาร", "ใบเสนอราคา " + q.no + " → ใบส่งสินค้า", "create");
            forceQ(n => n + 1);
          }}>แปลงเป็นใบส่งของ</Btn>
          <Btn icon={I.arrowRight} kind="primary" onClick={() => {
            if (!window.openNewDoc) { window.toast && window.toast("ระบบออกเอกสารยังไม่พร้อม ลองรีเฟรช"); return; }
            window.openNewDoc("invoice", {
              fromDoc: q.no, partyName: q.customer, project: q.project || "", projectName: q.projectName || "",
              lines: lines.map(l => ({ sku: l.sku, name: l.name, qty: l.qty, unit: l.unit, price: l.price })),
              discount: Number(q.discount) || 0, vatPct: q.vatPct != null ? Number(q.vatPct) : 7
            });
            window.DocStatusStore && window.DocStatusStore.set("quotation", q.no, "แปลงเป็นใบสั่ง");
            window.DocStatus2Store && window.DocStatus2Store.set("quotation", q.no, "แปลงเป็นใบแจ้งหนี้แล้ว");
            forceQ(n => n + 1);
          }}>แปลงเป็นใบแจ้งหนี้</Btn>
        </>}
      />

      <div className="grid" style={{gridTemplateColumns: "1fr 320px", gap: 14, alignItems: "flex-start"}}>
        <div>
          <window.DocRefPanel row={q} navigate={navigate}/>
          {/* Workflow */}
          <Card padded={false} className="mb-lg" flush>
            <div style={{padding: "14px 16px"}}>
              <Workflow steps={["ร่าง", "ส่งให้ลูกค้า", "รออนุมัติ", "อนุมัติ", "แปลงเป็นใบสั่ง/ใบแจ้งหนี้"]} current={({"ร่าง": 0, "ส่งแล้ว": 1, "รออนุมัติ": 2, "อนุมัติแล้ว": 3, "แปลงเป็นใบสั่ง": 4, "ปฏิเสธ": 2})[q.status] ?? 2}/>
            </div>
          </Card>

          <Tabs
            items={[
              { key: "preview", label: "ตัวอย่างเอกสาร" },
              { key: "items", label: "รายการสินค้า" },
              { key: "history", label: "ประวัติ" },
              { key: "comments", label: "ความเห็น" }
            ]}
            active={tab}
            onChange={setTab}
          />

          {tab === "preview" && <DocPaper q={q} lines={lines} subtotal={subtotal} discount={discount} afterDisc={afterDisc} vat={vat} grand={grand} wht={qWht} whtPct={qWhtPct} docType="ใบเสนอราคา" docTypeEn="QUOTATION"/>}
          {tab === "items" && <ItemsEditor lines={lines}/>}
          {tab === "history" && <HistoryList q={q} issuer={issuer}/>}
          {tab === "comments" && <Comments docNo={q.no}/>}
        </div>

        {/* Side panel */}
        <div style={{display: "flex", flexDirection: "column", gap: 14}}>
          <Card title="สถานะ" right={<Badge>{q.status || "ร่าง"}</Badge>}>
            {(() => { const s2 = window.DocStatus2Store && window.DocStatus2Store.get("quotation", q.no); return s2 ? (
              <div style={{display: "flex", alignItems: "center", gap: 8, padding: "7px 10px", marginBottom: 10, background: "var(--success-bg, var(--surface-2))", border: "1px solid var(--success)", borderRadius: 8, fontSize: 12.5}}>
                <I.check size={14} stroke="var(--success)"/>
                <span style={{flex: 1}}><b style={{color: "var(--success)"}}>สถานะที่ 2:</b> {window.docLinkify ? window.docLinkify(s2, navigate) : s2}</span>
              </div>
            ) : null; })()}
            <div className="field">
              <label style={{fontSize: 11, color: "var(--ink-3)", display: "block", marginBottom: 6}}>เปลี่ยนสถานะ — คลิกเดียว</label>
              {window.DocStatusBar
                ? <window.DocStatusBar docType="quotation" no={q.no} status={q.status || "รออนุมัติ"} onChange={() => forceQ(n => n + 1)}/>
                : null}
            </div>
          </Card>
          <Card title="สรุปยอด">
            <div style={{display: "flex", flexDirection: "column", gap: 6, fontSize: 13}}>
              <Row label="รวมเป็นเงิน" value={`฿${fmtM(subtotal)}`}/>
              <Row label="ส่วนลด" value={`฿${fmtM(discount)}`}/>
              <Row label="หลังหักส่วนลด" value={`฿${fmtM(afterDisc)}`}/>
              <Row label="ภาษีมูลค่าเพิ่ม 7%" value={`฿${fmtM(vat)}`}/>
              {qWht > 0 && <Row label={`หัก ณ ที่จ่าย ${qWhtPct}%`} value={`−฿${fmtM(qWht)}`}/>}
              <div style={{borderTop: "1px solid var(--line)", marginTop: 6, paddingTop: 8, display: "flex", justifyContent: "space-between", fontWeight: 600, fontSize: 16}}>
                <span>ยอดรวมสุทธิ</span>
                <span className="amount">฿{fmtM(grand)}</span>
              </div>
              {qWht > 0 && <div style={{display: "flex", justifyContent: "space-between", fontWeight: 600, fontSize: 13.5, color: "var(--accent)"}}>
                <span>ยอดรับชำระหลังหัก ณ ที่จ่าย</span>
                <span className="amount">฿{fmtM(grand - qWht)}</span>
              </div>}
            </div>
          </Card>

          <Card title="ลูกค้า" right={<Btn size="sm" kind="ghost" onClick={() => navigate("customers")}>เปิดโปรไฟล์</Btn>}>
            <div style={{fontSize: 13, lineHeight: 1.6}}>
              <div style={{fontWeight: 500, marginBottom: 4}}>{q.customer}</div>
              {cust.taxId && <div className="muted">เลขผู้เสียภาษี {cust.taxId}</div>}
              {cust.contact && <div className="muted">{cust.contact}</div>}
              {cust.phone && <div className="muted">{cust.phone}</div>}
              {cust.credit && <div className="muted">เครดิต {cust.credit} วัน</div>}
            </div>
          </Card>

          <Card title="ผู้ดำเนินการ">
            <div style={{display: "flex", alignItems: "center", gap: 8, fontSize: 13}}>
              {window.RankAvatar ? <window.RankAvatar name={issuer} size={28}/> : <div className="avatar" style={{width: 28, height: 28, fontSize: 10}}>{(issuer || "?").slice(0, 2)}</div>}
              <div><div style={{fontWeight: 500}}>{issuer || "—"}</div><div className="muted" style={{fontSize: 11}}>ผู้สร้าง · {q.date}</div></div>
            </div>
          </Card>

          {window.DocHistoryPanel && <window.DocHistoryPanel listKey="Quotations" docNo={q.no}/>}
        </div>
      </div>
    </>
  );
}

function Row({ label, value }) {
  return <div style={{display: "flex", justifyContent: "space-between"}}><span className="muted">{label}</span><span className="amount">{value}</span></div>;
}

function Attach({ name, size, ext }) {
  return (
    <div className="attach">
      <div className="ext">{ext}</div>
      <div className="name" style={{overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap"}}>{name}</div>
      <div className="meta">{size}</div>
    </div>
  );
}

function DocPaper({ q, lines, subtotal, discount, afterDisc, vat, grand, wht, whtPct, docType, docTypeEn }) {
  const D = window.SSSData;
  const C = D.Company;
  const cust = (D.Customers || []).find(c => c.name === q.customer) || {};
  const realTax = cust.taxId && cust.taxId !== "—" ? cust.taxId : "";
  const color = window.getDocColor("quotation");
  return (
    <div className="doc-paper" style={{borderTop: `5px solid ${color}`}}>
      <div style={{display: "flex", justifyContent: "space-between", alignItems: "flex-start", gap: 20, marginBottom: 24}}>
        <div style={{display: "flex", gap: 20}}>
          <div style={{background: color, borderRadius: 6, alignSelf: "stretch", minHeight: 56, maxHeight: 80, display: "flex", alignItems: "center", justifyContent: "center", padding: "8px 16px", flexShrink: 0}}><img src={window.docLogo ? window.docLogo() : "assets/logo-ink.png"} alt={C.name} style={{width: 132, maxHeight: "100%", objectFit: "contain", display: "block"}}/></div>
          <div>
            <div style={{fontWeight: 700, fontSize: 14}}>{C.name}</div>
            <div style={{fontSize: 11, color: "#57534e"}}>{C.nameEn}</div>
            <div style={{fontSize: 11, color: "#57534e", marginTop: 4, maxWidth: 320}}>{C.address}</div>
            <div style={{fontSize: 11, color: "#57534e"}}>โทร {C.phone} · เลขผู้เสียภาษี {C.taxId}</div>
          </div>
        </div>
        <div style={{textAlign: "right"}}>
          <h1 style={{color}}>{docType}</h1>
          <div style={{fontSize: 10, color: "#78716c", letterSpacing: "0.06em"}}>{docTypeEn}</div>
          <div style={{marginTop: 8, fontSize: 12}}>
            <div>เลขที่ <b className="mono">{q.no}</b></div>
            <div>วันที่ <b>{q.date}</b></div>
            <div>ยืนราคา <b>{q.validity}</b></div>
          </div>
        </div>
      </div>

      <div style={{display: "grid", gridTemplateColumns: "1fr 1fr", gap: 16, marginBottom: 16, fontSize: 12}}>
        <div style={{border: "1px solid #e7e5e4", padding: 10, borderRadius: 4}}>
          <div style={{fontSize: 10, color: "#78716c", textTransform: "uppercase", letterSpacing: "0.05em", marginBottom: 2}}>ลูกค้า</div>
          <div style={{fontWeight: 600}}>{q.customer}</div>
          {cust.address && <div style={{color: "#57534e"}}>{cust.address}</div>}
          {realTax && <div style={{color: "#57534e"}}>เลขผู้เสียภาษี {realTax}</div>}
        </div>
        <div style={{border: "1px solid #e7e5e4", padding: 10, borderRadius: 4}}>
          <div style={{fontSize: 10, color: "#78716c", textTransform: "uppercase", letterSpacing: "0.05em", marginBottom: 2}}>ติดต่อ / เงื่อนไข</div>
          {cust.contact && <div>ผู้ติดต่อ: {cust.contact}</div>}
          {cust.phone && cust.phone !== "—" && <div>โทร: {cust.phone}</div>}
          {cust.credit != null && <div>เงื่อนไขการชำระ: เครดิต {cust.credit} วัน</div>}
        </div>
      </div>

      <table>
        <thead>
          <tr>
            <th style={{width: 30}}>#</th>
            <th>รายการ</th>
            <th style={{width: 60}} className="right">จำนวน</th>
            <th style={{width: 60}}>หน่วย</th>
            <th style={{width: 90}} className="right">ราคา/หน่วย</th>
            <th style={{width: 110}} className="right">รวมเป็นเงิน</th>
          </tr>
        </thead>
        <tbody>
          {lines.map((l, i) => (
            <tr key={i}>
              <td>{i + 1}</td>
              <td>
                <div style={{fontWeight: 500}}>{l.name}</div>
                <div style={{fontSize: 10, color: "#78716c"}} className="mono">{l.sku}</div>
              </td>
              <td className="right">{l.qty}</td>
              <td>{l.unit}</td>
              <td className="right">{fmtM(l.price)}</td>
              <td className="right">{fmtM(l.total)}</td>
            </tr>
          ))}
        </tbody>
      </table>

      <div style={{display: "flex", justifyContent: "space-between", marginTop: 16, gap: 30}}>
        <div style={{flex: 1, fontSize: 11.5, color: "#57534e", lineHeight: 1.55}}>
          <div style={{textTransform: "uppercase", fontSize: 10, fontWeight: 600, color: "#44403c", letterSpacing: "0.05em", marginBottom: 4}}>หมายเหตุ</div>
          <div>• ราคาข้างต้นเป็นราคาก่อนภาษีมูลค่าเพิ่ม</div>
          <div>• ระยะเวลาผลิตประมาณ 25-30 วันทำการ หลังได้รับ PO + เงินมัดจำ 50%</div>
          <div>• ส่วนที่เหลือชำระภายใน 60 วันหลังส่งมอบงาน</div>
          <div>• แบบ 3D ที่ส่งให้ลูกค้าถือเป็นทรัพย์สินของ SSS จนกว่าจะชำระครบ</div>
        </div>
        <div className="totals">
          <div className="row"><span>รวมเป็นเงิน</span><span>{fmtM(subtotal)}</span></div>
          <div className="row"><span>ส่วนลด</span><span>{fmtM(discount)}</span></div>
          <div className="row"><span>หลังหักส่วนลด</span><span>{fmtM(afterDisc)}</span></div>
          <div className="row"><span>ภาษีมูลค่าเพิ่ม 7%</span><span>{fmtM(vat)}</span></div>
          <div className="row grand"><span>ยอดสุทธิ</span><span>฿ {fmtM(grand)}</span></div>
          {Number(wht) > 0 && <div className="row"><span>หัก ภาษี ณ ที่จ่าย {whtPct}%</span><span style={{color: "#b91c1c"}}>−{fmtM(wht)}</span></div>}
          {Number(wht) > 0 && <div className="row" style={{fontWeight: 700}}><span>ยอดชำระสุทธิ</span><span>฿ {fmtM(grand - wht)}</span></div>}
        </div>
      </div>

      <div style={{marginTop: 10, padding: "8px 12px", background: "#f7f6f4", borderRadius: 4, fontSize: 11.5, color: "#44403c", fontStyle: "italic"}}>
        ({bahtText(grand)})
      </div>

      {/* ช่องลงนามแบบใบกำกับภาษี — ซ้าย: ลูกค้า · กลาง: ตราประทับ · ขวา: บริษัท */}
      {(() => {
        const qIssuer = q.issuedBy || (window.Session && window.Session.get() && window.Session.get().name) || (C.currentUser && C.currentUser.name) || "";
        const qSig = q.signStamp ? (((window.lsSyncGet && window.lsSyncGet("sss-user-signs")) || {})[qIssuer] || "") : "";
        const qStamp = q.signStamp ? (((window.lsSyncGet && window.lsSyncGet("sss-doc-sign-assets")) || {}).stamp || "") : "";
        const sblock = (org, role, img, dateTxt) => (
          <div style={{textAlign: "center"}}>
            <div style={{fontSize: 10, color: "#57534e", marginBottom: 2}}>ในนาม</div>
            <div style={{fontWeight: 600, color: "#0c0a09", fontSize: 11, minHeight: 26, lineHeight: 1.3}}>{org || "—"}</div>
            <div style={{height: 46, display: "flex", alignItems: "flex-end", justifyContent: "center"}}>
              {img ? <img src={img} alt="ลายเซ็น" style={{maxHeight: 44, maxWidth: "72%", objectFit: "contain"}}/> : null}
            </div>
            <div style={{borderTop: "1px dashed #57534e", margin: "0 16px 0", paddingTop: 6}}>
              <div style={{fontWeight: 600, color: "#0c0a09"}}>{role}</div>
              <div style={{color: "#78716c", fontSize: 10}}>วันที่ {dateTxt || "______________"}</div>
            </div>
          </div>
        );
        return (
          <div style={{display: "grid", gridTemplateColumns: "1fr 130px 1fr", gap: 18, marginTop: 40, fontSize: 11.5, alignItems: "start"}}>
            {sblock(q.customer, "ผู้อนุมัติสั่งซื้อ", null, null)}
            <div style={{display: "flex", alignItems: "center", justifyContent: "center", paddingTop: 24}}>
              {qStamp
                ? <img src={qStamp} alt="ตราประทับ" style={{width: 104, height: 104, objectFit: "contain"}}/>
                : <div style={{width: 104, height: 104, border: "1.5px dashed #d6d3d1", borderRadius: "50%", display: "grid", placeItems: "center", textAlign: "center", color: "#c9c5c1", fontSize: 9, lineHeight: 1.5}}><div>ตราประทับ<div>บริษัท</div></div></div>}
            </div>
            {sblock(C.name, `ผู้เสนอราคา · ${qIssuer}`, qSig, qSig ? q.date : null)}
          </div>
        );
      })()}
    </div>
  );
}

function ItemsEditor({ lines }) {
  return (
    <Card flush>
      <div className="table-wrap">
        <table className="t">
          <thead><tr><th>SKU</th><th>รายการ</th><th className="right">จำนวน</th><th>หน่วย</th><th className="right">ราคา/หน่วย</th><th className="right">รวม</th><th></th></tr></thead>
          <tbody>
            {lines.map((l, i) => (
              <tr key={i}>
                <td className="mono">{l.sku}</td>
                <td>{l.name}</td>
                <td className="right">{l.qty}</td>
                <td>{l.unit}</td>
                <td className="right">฿{fmtM(l.price)}</td>
                <td className="right">฿{fmtM(l.total)}</td>
                <td><Btn kind="ghost" size="sm" icon={I.edit}/></td>
              </tr>
            ))}
          </tbody>
        </table>
      </div>
      <div style={{padding: 12, display: "flex", gap: 8}}>
        <Btn icon={I.plus} size="sm">เพิ่มรายการ</Btn>
        <Btn icon={I.barcode} size="sm" kind="ghost">สแกนบาร์โค้ดเพิ่ม</Btn>
      </div>
    </Card>
  );
}

function HistoryList({ q, issuer }) {
  const status = (q && q.status) || "ร่าง";
  const items = [{ who: issuer || "—", what: "สร้างเอกสาร", when: (q && q.date) || "" }];
  const s2 = window.DocStatus2Store && q && window.DocStatus2Store.get("quotation", q.no);
  if (status !== "ร่าง") items.push({ who: "—", what: "สถานะปัจจุบัน: " + status, when: "" });
  if (s2) items.push({ who: "—", what: s2, when: "" });
  return (
    <Card>
      <div style={{display: "flex", flexDirection: "column", gap: 14, position: "relative"}}>
        {items.map((h, i) => (
          <div key={i} style={{display: "flex", gap: 12}}>
            <div className="avatar" style={{width: 28, height: 28, fontSize: 10}}>{String(h.who).slice(0,2)}</div>
            <div><div style={{fontSize: 13}}><b>{h.who}</b> · {h.what}</div>{h.when && <div className="muted" style={{fontSize: 11}}>{h.when}</div>}</div>
          </div>
        ))}
      </div>
    </Card>
  );
}

const CMT_MONTHS = ["ม.ค.","ก.พ.","มี.ค.","เม.ย.","พ.ค.","มิ.ย.","ก.ค.","ส.ค.","ก.ย.","ต.ค.","พ.ย.","ธ.ค."];
function Comments({ docNo }) {
  const D = window.SSSData;
  // คอมเมนต์เก็บในแมพรวม sss-doc-comments (sync ทุกเครื่อง) — ของเก่า sss-comments-* ถูกย้ายอัตโนมัติ
  const CID = docNo || "doc";
  const cmtGet = () => {
    const map = (window.lsSyncGet && window.lsSyncGet("sss-doc-comments")) || {};
    if (Array.isArray(map[CID])) return map[CID];
    try { return JSON.parse(localStorage.getItem("sss-comments-" + CID) || "null") || []; } catch { return []; }
  };
  const [list, setList] = useState(cmtGet);
  const [msg, setMsg] = useState("");
  const [imgs, setImgs] = useState([]);
  const [docs, setDocs] = useState([]);
  const [picker, setPicker] = useState(null); // "people" | "docs"
  const [viewer, setViewer] = useState(null);

  const people = [...(D.Employees || []), ...((D.Users || []).filter(u => !(D.Employees || []).some(e => e.name === u.name)))];
  const allDocs = [...(D.Quotations || []), ...(D.Invoices || []), ...(D.TaxInvoices || []), ...(D.PurchaseOrders || [])].map(d => d.no).filter(Boolean);
  const save = (l) => {
    setList(l);
    const map = Object.assign({}, (window.lsSyncGet && window.lsSyncGet("sss-doc-comments")) || {});
    map[CID] = l;
    if (window.lsSyncSet) window.lsSyncSet("sss-doc-comments", map);
    else { try { localStorage.setItem("sss-comments-" + CID, JSON.stringify(l)); } catch {} }
  };

  const pickImg = async (e) => {
    const fs = [...e.target.files]; e.target.value = "";
    const urls = await Promise.all(fs.map(f => window.DeliveryStore ? window.DeliveryStore.resizeImage(f, 1000) : new Promise(r => { const rd = new FileReader(); rd.onload = () => r(rd.result); rd.readAsDataURL(f); })));
    setImgs(p => [...p, ...urls.map((u, i) => ({ url: u, name: fs[i].name }))]);
  };
  const send = () => {
    if (!msg.trim() && !imgs.length && !docs.length) { window.toast && window.toast("พิมพ์ความเห็นหรือแนบรูป/เอกสาร"); return; }
    const sess = window.Session ? window.Session.get() : null;
    const me = (sess && sess.name) || (D.Company.currentUser && D.Company.currentUser.name) || "ฉัน";
    const n = new Date();
    const at = `${n.getDate()} ${CMT_MONTHS[n.getMonth()]} ${n.getFullYear() + 543} ${String(n.getHours()).padStart(2, "0")}:${String(n.getMinutes()).padStart(2, "0")}`;
    save([...cmtGet(), { id: Date.now(), by: me, at, text: msg.trim(), imgs, docs }]);   // ต่อท้ายข้อมูลสด — คอมเมนต์จากเครื่องอื่นไม่หาย
    const mentioned = people.filter(p => msg.includes("@" + p.name));
    if (mentioned.length) window.toast && window.toast(`ส่งความเห็นและแจ้งเตือน ${mentioned.map(p => p.name).join(", ")} แล้ว`);
    else window.toast && window.toast("ส่งความเห็นแล้ว");
    setMsg(""); setImgs([]); setDocs([]);
  };
  const renderText = (t) => {
    if (!t) return null;
    const tokens = t.split(/(@[^\s@#]+(?:\s[^\s@#]+)?|#[A-Z]+-[0-9-]+)/g);
    return tokens.map((tok, i) => {
      if (people.some(p => "@" + p.name === tok)) return <span key={i} style={{ color: "var(--brand, #7c3aed)", fontWeight: 600 }}>{tok}</span>;
      if (/^#[A-Z]+-[0-9-]+$/.test(tok)) return <span key={i} style={{ color: "var(--info)", fontWeight: 600 }}>{tok}</span>;
      return <React.Fragment key={i}>{tok}</React.Fragment>;
    });
  };

  return (
    <Card>
      <div style={{ display: "flex", flexDirection: "column", gap: 12 }}>
        {list.map(c => (
          <div key={c.id} className="activity-item" style={{ alignItems: "flex-start" }}>
            {window.RankAvatar ? <window.RankAvatar name={c.by} size={30}/> : <div className="avatar" style={{ width: 30, height: 30, fontSize: 10 }}>{c.by.slice(0, 2)}</div>}
            <div style={{ flex: 1, minWidth: 0 }}>
              <div style={{ fontSize: 13 }}><b>{c.by}</b> <span className="muted" style={{ fontSize: 11 }}>· {c.at}</span></div>
              {c.text && <div style={{ fontSize: 13, lineHeight: 1.5, whiteSpace: "pre-wrap" }}>{renderText(c.text)}</div>}
              {(c.docs || []).length > 0 && <div style={{ display: "flex", gap: 6, flexWrap: "wrap", marginTop: 5 }}>{c.docs.map(d => <span key={d} className="badge outline mono" style={{ fontSize: 11, cursor: "pointer" }} onClick={() => window.toast && window.toast("อ้างอิงเอกสาร " + d)}>📎 {d}</span>)}</div>}
              {(c.imgs || []).length > 0 && <div style={{ display: "flex", gap: 6, flexWrap: "wrap", marginTop: 6 }}>{c.imgs.map((im, j) => <img key={j} src={im.url} alt={im.name} onClick={() => setViewer(im.url)} style={{ width: 72, height: 72, objectFit: "cover", borderRadius: 6, border: "1px solid var(--line)", cursor: "zoom-in" }}/>)}</div>}
            </div>
          </div>
        ))}

        {/* แถบแนบที่จะส่ง */}
        {(imgs.length > 0 || docs.length > 0) && (
          <div style={{ display: "flex", gap: 6, flexWrap: "wrap", padding: "6px 0" }}>
            {docs.map(d => <span key={d} className="badge outline mono" style={{ fontSize: 11 }}>📎 {d} <span style={{ cursor: "pointer" }} onClick={() => setDocs(p => p.filter(x => x !== d))}>×</span></span>)}
            {imgs.map((im, j) => <span key={j} style={{ position: "relative" }}><img src={im.url} alt="" style={{ width: 44, height: 44, objectFit: "cover", borderRadius: 5, border: "1px solid var(--line)" }}/><span onClick={() => setImgs(p => p.filter((_, k) => k !== j))} style={{ position: "absolute", top: -6, right: -6, width: 16, height: 16, borderRadius: "50%", background: "var(--ink-1)", color: "white", fontSize: 11, display: "grid", placeItems: "center", cursor: "pointer" }}>×</span></span>)}
          </div>
        )}

        <div style={{ position: "relative" }}>
          <div style={{ display: "flex", gap: 6, marginBottom: 6 }}>
            <button className="icon-btn" title="แท็กพนักงาน/ผู้ใช้" onClick={() => setPicker(picker === "people" ? null : "people")}>@</button>
            <button className="icon-btn" title="อ้างอิงเอกสาร" onClick={() => setPicker(picker === "docs" ? null : "docs")}>#</button>
            <label className="icon-btn" title="แนบรูป" style={{ cursor: "pointer" }}><I.camera size={14}/><input type="file" accept="image/*" multiple style={{ display: "none" }} onChange={pickImg}/></label>
          </div>
          {picker === "people" && (
            <div style={{ position: "absolute", bottom: "calc(100% - 0px)", left: 0, zIndex: 30, background: "var(--surface)", border: "1px solid var(--line-strong)", borderRadius: 10, boxShadow: "var(--shadow-lg)", width: 260, maxHeight: 240, overflowY: "auto", padding: 4 }}>
              {people.map(p => (
                <button key={p.id} onClick={() => { setMsg(m => (m ? m + " " : "") + "@" + p.name + " "); setPicker(null); }} style={{ display: "flex", alignItems: "center", gap: 8, width: "100%", padding: "6px 8px", border: 0, background: "transparent", cursor: "pointer", borderRadius: 6, textAlign: "left", fontFamily: "var(--font)" }}>
                  {window.RankAvatar ? <window.RankAvatar name={p.name} size={24}/> : null}
                  <span style={{ fontSize: 12.5 }}>{p.name} <span className="muted" style={{ fontSize: 11 }}>{p.role}</span></span>
                </button>
              ))}
            </div>
          )}
          {picker === "docs" && (
            <div style={{ position: "absolute", bottom: "100%", left: 0, zIndex: 30, background: "var(--surface)", border: "1px solid var(--line-strong)", borderRadius: 10, boxShadow: "var(--shadow-lg)", width: 240, maxHeight: 240, overflowY: "auto", padding: 4 }}>
              {allDocs.map(d => (
                <button key={d} onClick={() => { setDocs(p => p.includes(d) ? p : [...p, d]); setPicker(null); }} className="mono" style={{ display: "block", width: "100%", padding: "6px 8px", border: 0, background: "transparent", cursor: "pointer", borderRadius: 6, textAlign: "left", fontSize: 12 }}>📎 {d}</button>
              ))}
            </div>
          )}
          <div style={{ display: "flex", gap: 8 }}>
            <input className="input" placeholder="พิมพ์ความเห็น… ใช้ @ แท็กคน · # แนบเอกสาร" value={msg} onChange={e => setMsg(e.target.value)} onKeyDown={e => e.key === "Enter" && send()}/>
            <Btn icon={I.send} kind="primary" onClick={send}>ส่ง</Btn>
          </div>
        </div>
      </div>
      {viewer && <div className="scrim" style={{ display: "flex", alignItems: "center", justifyContent: "center", padding: 24, zIndex: 240 }} onClick={() => setViewer(null)}><img src={viewer} alt="" style={{ maxWidth: "92vw", maxHeight: "90vh", borderRadius: 8 }}/></div>}
    </Card>
  );
}

// ============ Invoices ============
function Invoices({ navigate }) {
  const D = window.SSSData;
  const outstanding = D.Invoices.filter(i => i.status !== "ชำระแล้ว").reduce((s, i) => s + (i.amount - i.paid), 0);
  const overdue = D.Invoices.filter(i => i.status === "เกินกำหนด").reduce((s, i) => s + (i.amount - i.paid), 0);
  return (
    <DocListPage
      title="ใบส่งสินค้า / ใบแจ้งหนี้ · Delivery Note / Invoice"
      sub="ติดตามลูกหนี้และการชำระเงิน"
      docType="invoice" rows={D.Invoices}
      statusFilter={["ค้างชำระ", "ชำระบางส่วน", "ชำระแล้ว", "เกินกำหนด", "ยกเลิกแล้ว"]}
      createLabel="สร้างใบส่งสินค้า/ใบแจ้งหนี้"
      onCreate={() => window.openNewDoc && window.openNewDoc("invoice")}
      onRowClick={(r) => navigate("invoices/" + r.no)}
      kpis={[
        { label: "ลูกหนี้ค้างชำระทั้งหมด", value: `฿${fmt0(outstanding)}`, sub: `${D.Invoices.filter(i=>i.status!=='ชำระแล้ว').length} ใบ`, icon: I.invoice },
        { label: "เกินกำหนด", value: `฿${fmt0(overdue)}`, sub: `${D.Invoices.filter(i=>i.status==='เกินกำหนด').length} ใบ · ต้องติดตาม`, dir: "down", icon: I.bell },
        { label: "เก็บได้เดือนนี้", value: `฿${fmt0(D.Invoices.filter(i=>i.status==='ชำระแล้ว').reduce((s,i)=>s+i.amount,0))}`, sub: `${D.Invoices.filter(i=>i.status==='ชำระแล้ว').length} ใบ ชำระแล้ว`, dir: "up", icon: I.check },
        { label: "ใบแจ้งหนี้ทั้งหมด", value: D.Invoices.length, sub: `฿${fmt0(D.Invoices.reduce((s,i)=>s+(i.amount||0),0))}`, icon: I.clock }
      ]}
      columns={[
        { key: "no", label: "เลขที่", render: r => <span className="mono">{r.no}</span> },
        { key: "date", label: "วันที่ออก" },
        { key: "due", label: "ครบกำหนด" },
        { key: "customer", label: "ลูกค้า" },
        { key: "amount", label: "จำนวนเงิน", right: true, render: r => <span className="amount">฿{fmt0(r.amount)}</span> },
        { key: "paid", label: "ชำระแล้ว", right: true, render: r => <span className="amount muted">฿{fmt0(r.paid)}</span> },
        { key: "outstanding", label: "คงเหลือ", right: true, render: r => <span className="amount">฿{fmt0(r.amount - r.paid)}</span> },
        { key: "status", label: "สถานะ", render: r => <Badge>{r.status}</Badge> }
      ]}
    />
  );
}

// ============ Delivery Notes ============
function DeliveryNotes({ navigate }) {
  const D = window.SSSData;
  return (
    <DocListPage
      title="ใบส่งสินค้า · Delivery Notes"
      sub="ติดตามการจัดส่งและการเซ็นรับ"
      docType="delivery-note" rows={D.DeliveryNotes}
      statusFilter={["กำลังจัดส่ง", "ส่งแล้ว", "ลูกค้ามารับเอง", "ยกเลิกแล้ว"]}
      createLabel="สร้างใบส่งสินค้า"
      onCreate={() => window.openNewDoc && window.openNewDoc("delivery-note")}
      onRowClick={(r) => navigate("delivery-notes/" + r.no)}
      kpis={[
        { label: "ใบส่งทั้งหมด", value: D.DeliveryNotes.length, sub: "ทุกสถานะ", icon: I.truck },
        { label: "กำลังจัดส่ง", value: D.DeliveryNotes.filter(d=>d.status==='กำลังจัดส่ง').length, sub: "ติดตามตำแหน่งรถ", icon: I.move },
        { label: "เซ็นรับแล้ว", value: D.DeliveryNotes.filter(d=>d.status==='ส่งแล้ว').length, sub: "ส่งสำเร็จ", icon: I.check },
        { label: "ลูกค้ามารับเอง", value: D.DeliveryNotes.filter(d=>d.status==='ลูกค้ามารับเอง').length, sub: "รับที่โรงงาน", icon: I.user }
      ]}
      columns={[
        { key: "no", label: "เลขที่", render: r => <span className="mono">{r.no}</span> },
        { key: "date", label: "วันที่" },
        { key: "customer", label: "ลูกค้า" },
        { key: "items", label: "จำนวนรายการ", right: true },
        { key: "driver", label: "คนขับ" },
        { key: "plate", label: "ทะเบียนรถ" },
        { key: "status", label: "สถานะ", render: r => <Badge>{r.status}</Badge> }
      ]}
    />
  );
}

// ============ Tax Invoices ============
function TaxInvoices({ navigate }) {
  const D = window.SSSData;
  // ฐานภาษี/VAT: ใช้ค่าที่บันทึกไว้ ถ้าไม่มีให้ถอดจากยอดรวม (amount = ฐาน + VAT)
  const txBase = (t) => { if (t.base != null) return Number(t.base) || 0; const pct = t.vatPct != null ? Number(t.vatPct) : 7; return Math.round((Number(t.amount) || 0) / (1 + pct / 100)); };
  const txVat = (t) => { if (t.vat != null) return Number(t.vat) || 0; return Math.round((Number(t.amount) || 0) - txBase(t)); };
  return (
    <DocListPage
      title="ใบกำกับภาษี · Tax Invoices"
      sub="ใบกำกับภาษีเต็มรูป / อย่างย่อ — รวมเข้า ภ.พ.30 อัตโนมัติ"
      docType="tax-invoice" rows={D.TaxInvoices}
      statusFilter={[]}
      createLabel="สร้างใบกำกับภาษี"
      onCreate={() => window.openNewDoc && window.openNewDoc("tax-invoice")}
      onRowClick={(r) => navigate("tax-invoices/" + r.no)}
      kpis={[
        { label: "ใบกำกับภาษีเดือนนี้", value: D.TaxInvoices.length, sub: "ออกครบ", icon: I.receipt },
        { label: "ฐานภาษี (ก่อน VAT)", value: `฿${fmt0(D.TaxInvoices.reduce((s,t)=>s+txBase(t),0))}`, sub: "ขายในประเทศ", icon: I.calc },
        { label: "VAT ขาย (output)", value: `฿${fmt0(D.TaxInvoices.reduce((s,t)=>s+txVat(t),0))}`, sub: "ภาษีขายรวม", icon: I.tax },
        { label: "ยอดรวมทั้งสิ้น (รวม VAT)", value: `฿${fmt0(D.TaxInvoices.reduce((s,t)=>s+(Number(t.amount)||txBase(t)+txVat(t)),0))}`, sub: "ทุกใบกำกับ", icon: I.bell }
      ]}
      columns={[
        { key: "no", label: "เลขที่", render: r => <span className="mono">{r.no}</span> },
        { key: "date", label: "วันที่" },
        { key: "customer", label: "ลูกค้า" },
        { key: "type", label: "ประเภท", render: r => <Badge tone={r.type === "เต็มรูป" ? "" : "outline"}>{r.type}</Badge> },
        { key: "base", label: "ฐานภาษี", right: true, render: r => <span className="amount">฿{fmtM(txBase(r))}</span> },
        { key: "vat", label: "VAT 7%", right: true, render: r => <span className="amount">฿{fmtM(txVat(r))}</span> },
        { key: "amount", label: "รวมทั้งสิ้น", right: true, render: r => <span className="amount">฿{fmtM(r.amount)}</span> }
      ]}
    />
  );
}

// ============ Billing Notes ============
function BillingNotes({ navigate }) {
  const D = window.SSSData;
  return (
    <DocListPage
      title="ใบวางบิล · Billing Notes"
      sub="รวมใบแจ้งหนี้หลายใบ ส่งวางบิลลูกค้า"
      docType="billing-note" rows={D.BillingNotes}
      statusFilter={["ส่งแล้ว", "ลูกค้านัดรับ", "เก็บเงินแล้ว", "ยกเลิกแล้ว"]}
      createLabel="สร้างใบวางบิล"
      onCreate={() => window.openNewDoc && window.openNewDoc("billing-note")}
      onRowClick={(r) => navigate("billing-notes/" + r.no)}
      kpis={[
        { label: "ใบวางบิลเปิดอยู่", value: D.BillingNotes.filter(b=>b.status!=='เก็บเงินแล้ว').length, sub: `฿${fmt0(D.BillingNotes.filter(b=>b.status!=='เก็บเงินแล้ว').reduce((s,b)=>s+b.amount,0))}`, icon: I.invoice },
        { label: "ลูกค้านัดรับ", value: D.BillingNotes.filter(b=>b.status==='ลูกค้านัดรับ').length, sub: "รอเข้าเก็บเงิน", icon: I.clock },
        { label: "เก็บได้เดือนนี้", value: `฿${fmt0(D.BillingNotes.filter(b=>b.status==='เก็บเงินแล้ว').reduce((s,b)=>s+b.amount,0))}`, sub: `${D.BillingNotes.filter(b=>b.status==='เก็บเงินแล้ว').length} ใบ`, dir: "up", icon: I.check },
        { label: "ใบวางบิลทั้งหมด", value: D.BillingNotes.length, sub: `฿${fmt0(D.BillingNotes.reduce((s,b)=>s+(b.amount||0),0))}`, icon: I.calc }
      ]}
      columns={[
        { key: "no", label: "เลขที่", render: r => <span className="mono">{r.no}</span> },
        { key: "date", label: "วันที่" },
        { key: "customer", label: "ลูกค้า" },
        { key: "invoices", label: "Invoice รวม", render: r => <div style={{display: "flex", gap: 4, flexWrap: "wrap"}}>{r.invoices.map(i => <span key={i} className="badge outline mono" style={{fontSize: 10}}>{i}</span>)}</div> },
        { key: "amount", label: "ยอดรวม", right: true, render: r => <span className="amount">฿{fmt0(r.amount)}</span> },
        { key: "dueDate", label: "กำหนดเก็บ" },
        { key: "status", label: "สถานะ", render: r => <Badge>{r.status}</Badge> }
      ]}
    />
  );
}

window.Quotations = Quotations;
window.QuotationDetail = QuotationDetail;
window.Invoices = Invoices;
window.DeliveryNotes = DeliveryNotes;
window.TaxInvoices = TaxInvoices;
window.BillingNotes = BillingNotes;
window.DocListPage = DocListPage;
window.DocPaper = DocPaper;
