/* global React, I, UI, SSSData */
const { useState: useStateI, useMemo: useMemoI, useRef: useRefI, useEffect: useEffectI } = React;

// ---------- รูปสินค้า: อัพโหลด / ถ่ายรูป ----------
function resizeProductImage(file, max = 360) {
  return new Promise(resolve => {
    if (!file) { resolve(null); return; }
    const reader = new FileReader();
    reader.onload = () => {
      const dataUrl = reader.result;
      const img = new Image();
      img.onload = () => {
        try {
          const scale = Math.min(1, max / Math.max(img.width, img.height));
          const c = document.createElement("canvas");
          c.width = Math.max(1, Math.round(img.width * scale));
          c.height = Math.max(1, Math.round(img.height * scale));
          c.getContext("2d").drawImage(img, 0, 0, c.width, c.height);
          resolve(c.toDataURL("image/jpeg", 0.62));
        } catch (e) { resolve(dataUrl); }   // ย่อไม่ได้ — เก็บไฟล์เดิม
      };
      img.onerror = () => resolve(dataUrl); // decode ไม่ได้ (เช่น HEIC จาก iPhone) — เก็บ dataURL เดิมไว้ก่อน
      img.src = dataUrl;
    };
    reader.onerror = () => resolve(null);
    reader.readAsDataURL(file);
  });
}

function CameraCaptureModal({ onClose, onCapture, title = "ถ่ายรูปสินค้า" }) {
  const { Btn } = window.UI;
  const videoRef = useRefI(null);
  const streamRef = useRefI(null);
  const [shot, setShot] = useStateI(null);   // dataURL after snap
  const [err, setErr] = useStateI(null);

  useEffectI(() => {
    let cancelled = false;
    (async () => {
      try {
        const stream = await navigator.mediaDevices.getUserMedia({ video: { facingMode: "environment" }, audio: false });
        if (cancelled) { stream.getTracks().forEach(t => t.stop()); return; }
        streamRef.current = stream;
        if (videoRef.current) videoRef.current.srcObject = stream;
      } catch (e) {
        if (!cancelled) setErr("เปิดกล้องไม่ได้ — กรุณาอนุญาตการใช้กล้อง หรือแนบรูปจากเครื่องแทน");
      }
    })();
    return () => {
      cancelled = true;
      if (streamRef.current) streamRef.current.getTracks().forEach(t => t.stop());
    };
  }, []);

  const snap = () => {
    const v = videoRef.current;
    if (!v || !v.videoWidth) return;
    const max = 800;
    const scale = Math.min(1, max / Math.max(v.videoWidth, v.videoHeight));
    const c = document.createElement("canvas");
    c.width = Math.round(v.videoWidth * scale);
    c.height = Math.round(v.videoHeight * scale);
    c.getContext("2d").drawImage(v, 0, 0, c.width, c.height);
    setShot(c.toDataURL("image/jpeg", 0.78));
  };

  const fallbackPick = async (e) => {
    const f = e.target.files && e.target.files[0];
    e.target.value = "";
    if (!f) return;
    const url = await resizeProductImage(f, 480);
    if (url) { onCapture(url); onClose(); }
  };

  return (
    <>
      <div className="scrim" style={{zIndex: 80}} onClick={onClose}></div>
      <div style={{position: "fixed", left: "50%", top: "50%", transform: "translate(-50%, -50%)", zIndex: 81, background: "var(--surface)", border: "1px solid var(--line)", borderRadius: 12, padding: 16, width: "min(520px, calc(100vw - 32px))", boxShadow: "var(--shadow-lg)"}}>
        <div style={{display: "flex", alignItems: "center", gap: 8, marginBottom: 12}}>
          <I.camera size={16}/>
          <div style={{fontSize: 14, fontWeight: 600, flex: 1}}>{title}</div>
          <button className="icon-btn" onClick={onClose}><I.close size={14}/></button>
        </div>

        {err ? (
          <div style={{padding: 20, textAlign: "center", border: "1.5px dashed var(--line-strong)", borderRadius: 10, background: "var(--surface-2)"}}>
            <div style={{fontSize: 13, color: "var(--ink-2)", marginBottom: 12}}>{err}</div>
            <label className="btn primary" style={{cursor: "pointer"}}>
              <input type="file" accept="image/*" capture="environment" style={{display: "none"}} onChange={fallbackPick}></input>
              ถ่าย/เลือกรูปจากอุปกรณ์
            </label>
          </div>
        ) : shot ? (
          <>
            <img src={shot} alt="preview" style={{width: "100%", borderRadius: 10, border: "1px solid var(--line)", display: "block"}}/>
            <div style={{display: "flex", gap: 8, justifyContent: "flex-end", marginTop: 12}}>
              <Btn kind="ghost" onClick={() => setShot(null)}>ถ่ายใหม่</Btn>
              <Btn kind="primary" icon={I.check} onClick={() => { onCapture(shot); onClose(); }}>ใช้รูปนี้</Btn>
            </div>
          </>
        ) : (
          <>
            <video ref={videoRef} autoPlay playsInline muted style={{width: "100%", aspectRatio: "4 / 3", objectFit: "cover", borderRadius: 10, background: "#000", display: "block"}}></video>
            <div style={{display: "flex", gap: 8, justifyContent: "flex-end", marginTop: 12}}>
              <Btn kind="ghost" onClick={onClose}>ยกเลิก</Btn>
              <Btn kind="primary" icon={I.camera} onClick={snap}>ถ่ายภาพ</Btn>
            </div>
          </>
        )}
      </div>
    </>
  );
}

function ProductPhotoInput({ value, onChange, size = 110 }) {
  const { Btn } = window.UI;
  const [showCam, setShowCam] = useStateI(false);
  const pick = async (e) => {
    const f = e.target.files && e.target.files[0];
    e.target.value = "";
    if (!f) return;
    const url = await resizeProductImage(f, 480);
    if (url) onChange(url);
    else window.toast && window.toast("อ่านรูปไม่สำเร็จ — ลองรูปไฟล์ JPG/PNG อีกครั้ง");
  };
  return (
    <div className="field">
      <label>รูปสินค้า</label>
      <div style={{display: "flex", gap: 12, alignItems: "flex-start"}}>
        {value ? (
          <div style={{position: "relative", width: size, height: size, flexShrink: 0}}>
            <img src={value} alt="รูปสินค้า" style={{width: size, height: size, objectFit: "cover", borderRadius: 10, border: "1px solid var(--line)", display: "block"}}/>
            <button title="ลบรูป" onClick={() => onChange(null)} style={{position: "absolute", top: 4, right: 4, width: 20, height: 20, borderRadius: "50%", background: "rgba(0,0,0,0.72)", color: "white", border: 0, cursor: "pointer", fontSize: 12, lineHeight: "18px", padding: 0}}>×</button>
          </div>
        ) : (
          <div style={{width: size, height: size, flexShrink: 0, border: "1.5px dashed var(--line-strong)", borderRadius: 10, display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center", gap: 6, color: "var(--ink-3)", background: "var(--surface-2)"}}>
            <I.camera size={22}/>
            <span style={{fontSize: 10.5}}>ยังไม่มีรูป</span>
          </div>
        )}
        <div style={{display: "flex", flexDirection: "column", gap: 6}}>
          <label className="btn sm" style={{cursor: "pointer", justifyContent: "center"}}>
            <input type="file" accept="image/*" style={{display: "none"}} onChange={pick}></input>
            อัพโหลดรูป
          </label>
          <Btn size="sm" icon={I.camera} onClick={() => setShowCam(true)}>ถ่ายรูป</Btn>
          <div style={{fontSize: 10.5, color: "var(--ink-3)", maxWidth: 150}}>รองรับ JPG/PNG · ระบบย่อรูปให้อัตโนมัติ</div>
        </div>
      </div>
      {showCam && <CameraCaptureModal onClose={() => setShowCam(false)} onCapture={onChange}/>}
    </div>
  );
}

// Persist user-side product changes in localStorage
function loadOverrides() { try { return JSON.parse(localStorage.getItem("sss-products") || "null"); } catch { return null; } }
function saveOverrides(list) { try { localStorage.setItem("sss-products", JSON.stringify(list)); } catch {} }

// ===== คลังรูปสินค้า (เก็บแยกตาม sku — ไม่ฝังใน Products เพื่อกันก้อนใหญ่/รูปหายตอน sync) =====
function loadProductPhotos() {
  if (window.lsSyncGet) return window.lsSyncGet("sss-product-photos") || {};
  try { return JSON.parse(localStorage.getItem("sss-product-photos") || "{}"); } catch { return {}; }
}
function setProductPhotoStore(sku, url) {
  const all = { ...loadProductPhotos() };
  if (url) all[sku] = url; else delete all[sku];
  // เก็บในหน่วยความจำ + sync ขึ้น Supabase (ไม่ผ่าน localStorage → ไม่เต็มโควต้า)
  if (window.lsSyncSet) { window.lsSyncSet("sss-product-photos", all); }
  else { const ok = (() => { try { localStorage.setItem("sss-product-photos", JSON.stringify(all)); return true; } catch (e) { return false; } })(); if (!ok) { window.toast && window.toast("พื้นที่จัดเก็บเต็มมาก — ลบรูปเก่าบางรูปก่อน"); return false; } }
  window.dispatchEvent(new Event("sss-products-changed"));
  return true;
}
function productPhoto(p) { if (!p) return null; const all = loadProductPhotos(); return all[p.sku] || p.photo || null; }

// ===== หมวดหมู่สินค้า (ผู้ใช้จัดการเอง + sync ทุกเครื่อง) =====
const INV_CAT_KEY = "sss-product-categories";
function invCatsLoad() {
  try { const s = JSON.parse(localStorage.getItem(INV_CAT_KEY) || "null"); if (Array.isArray(s)) return s; } catch {}
  // ครั้งแรก: ดึงหมวดที่มีอยู่จริงในสินค้า (ไม่ใส่หมวดเริ่มต้นแบบเดิม)
  return [...new Set((window.SSSData.Products || []).map(p => p.category).filter(Boolean))];
}
function invCatsSave(list) {
  const clean = [...new Set(list.map(c => String(c).trim()).filter(Boolean))];
  try { localStorage.setItem(INV_CAT_KEY, JSON.stringify(clean)); } catch {}
  window.dispatchEvent(new Event("sss-product-cats-changed"));
  return clean;
}

// ===== Modal จัดการหมวดหมู่ =====
function CategoryManagerModal({ onClose }) {
  const { Btn, Drawer } = window.UI;
  const [cats, setCats] = useStateI(invCatsLoad);
  const [adding, setAdding] = useStateI("");
  const prods = window.SSSData.Products || [];
  const countOf = (c) => prods.filter(p => p.category === c).length;
  const add = () => {
    const v = adding.trim();
    if (!v) return;
    if (cats.includes(v)) { window.toast && window.toast("มีหมวดนี้อยู่แล้ว"); return; }
    const next = invCatsSave([...cats, v]); setCats(next); setAdding("");
    window.toast && window.toast("เพิ่มหมวด " + v);
  };
  const remove = async (c) => {
    const n = countOf(c);
    if (n > 0) { window.toast && window.toast(`ลบไม่ได้ — มีสินค้า ${n} รายการอยู่ในหมวดนี้ ย้ายหมวดก่อน`); return; }
    const next = invCatsSave(cats.filter(x => x !== c)); setCats(next);
  };
  return (
    <Drawer open={true} onClose={onClose} title="จัดการหมวดหมู่สินค้า"
      footer={<Btn kind="primary" icon={I.check} onClick={onClose}>เสร็จสิ้น</Btn>}>
      <div style={{display: "flex", flexDirection: "column", gap: 12}}>
        <div className="field"><label>เพิ่มหมวดหมู่ใหม่</label>
          <div style={{display: "flex", gap: 6}}>
            <input className="input" value={adding} onChange={e=>setAdding(e.target.value)} placeholder="เช่น แผ่นสแตนเลส, อะไหล่, บริการ"
              onKeyDown={e=>{ if(e.key==="Enter") add(); }} style={{flex: 1}}/>
            <Btn kind="primary" icon={I.plus} onClick={add}>เพิ่ม</Btn>
          </div>
        </div>
        <div className="field"><label>หมวดหมู่ทั้งหมด ({cats.length})</label>
          {cats.length === 0 && <div style={{padding: 14, textAlign: "center", color: "var(--ink-3)", fontSize: 12.5, border: "1px dashed var(--line)", borderRadius: 8}}>ยังไม่มีหมวดหมู่ — เพิ่มด้านบน</div>}
          <div style={{display: "flex", flexDirection: "column", gap: 6}}>
            {cats.map(c => (
              <div key={c} style={{display: "flex", alignItems: "center", gap: 8, padding: "8px 12px", border: "1px solid var(--line)", borderRadius: 8}}>
                <span style={{flex: 1, fontSize: 13}}>{c}</span>
                <span style={{fontSize: 11, color: "var(--ink-3)"}}>{countOf(c)} รายการ</span>
                <button className="icon-btn" title="ลบหมวด" onClick={() => remove(c)}><I.trash size={13}/></button>
              </div>
            ))}
          </div>
        </div>
      </div>
    </Drawer>
  );
}

function InventoryProducts({ navigate }) {
  const D = window.SSSData;
  const { Btn, Badge, Card, PageHead, Drawer, BarcodeVisual, fmt0 } = window.UI;
  // ✅ ใช้ D.Products (ซิงค์ผ่าน Supabase) เป็นแหล่งข้อมูลหลัก — ไม่อ่าน localStorage ทับ
  const [products, setProducts] = useStateI(() => [...(D.Products || [])]);
  const [active, setActive] = useStateI(null);
  const [editMode, setEditMode] = useStateI(false);
  const [showNew, setShowNew] = useStateI(false);
  const [showStock, setShowStock] = useStateI(null); // product to adjust
  const [q, setQ] = useStateI("");
  const [cat, setCat] = useStateI("ทั้งหมด");
  const [userCats, setUserCats] = useStateI(invCatsLoad);
  const [showCats, setShowCats] = useStateI(false);

  // เขียนกลับ D.Products + แจ้ง sync เมื่อแก้ในเครื่องนี้
  React.useEffect(() => {
    if (JSON.stringify(D.Products) === JSON.stringify(products)) return;
    D.Products.length = 0;
    D.Products.push(...products);
    saveOverrides(products);
    window.dispatchEvent(new Event("sss-products-changed"));
  }, [products]);

  // รีเฟรชเมื่อสินค้าเปลี่ยนจากเครื่องอื่น (poll/realtime) หรือส่วนอื่นแก้สต็อก
  React.useEffect(() => {
    const h = () => { if (JSON.stringify(D.Products) !== JSON.stringify(products)) setProducts([...(D.Products || [])]); };
    window.addEventListener("sss-products-changed", h);
    window.addEventListener("sss-data-synced", h);
    return () => { window.removeEventListener("sss-products-changed", h); window.removeEventListener("sss-data-synced", h); };
  });

  // โหลดหมวดหมู่ใหม่เมื่อมีการเปลี่ยน/sync
  React.useEffect(() => {
    const h = () => setUserCats(invCatsLoad());
    window.addEventListener("sss-product-cats-changed", h);
    window.addEventListener("sss-data-synced", h);
    return () => { window.removeEventListener("sss-product-cats-changed", h); window.removeEventListener("sss-data-synced", h); };
  }, []);

  const cats = ["ทั้งหมด", ...userCats];
  const filtered = products.filter(p => (cat === "ทั้งหมด" || p.category === cat) && (!q || (p.name + p.sku + p.barcode).toLowerCase().includes(q.toLowerCase())));

  const addProduct = (p) => {
    D.Products = D.Products || [];
    if (!D.Products.some(x => x.sku === p.sku)) D.Products.unshift(p);
    setProducts([...(D.Products || [])]);
    saveOverrides(D.Products);
    window.dispatchEvent(new Event("sss-products-changed"));
    setShowNew(false);
    window.toast && window.toast("เพิ่มสินค้า " + p.sku + " แล้ว");
  };
  const updateProduct = (sku, patch) => {
    const prod = (D.Products || []).find(p => p.sku === sku);
    if (prod) Object.assign(prod, patch);
    setProducts([...(D.Products || [])]);
    if (active && active.sku === sku) setActive({ ...active, ...patch });
    saveOverrides(D.Products);
    window.dispatchEvent(new Event("sss-products-changed"));
  };
  const deleteProduct = async (sku) => {
    const p = products.find(x => x.sku === sku);
    const ok = await window.confirmDelete("สินค้า " + sku, p?.name);
    if (!ok) return;
    window.markDeleted && window.markDeleted("Products", sku);   // กันสินค้ากลับมาจาก sync poll
    D.Products = (D.Products || []).filter(x => x.sku !== sku);
    setProducts([...D.Products]);
    saveOverrides(D.Products);
    setProductPhotoStore(sku, null);   // ลบรูปออกจากคลังรูปด้วย (คืนพื้นที่)
    window.dispatchEvent(new Event("sss-products-changed"));
    window.SSSSync && window.SSSSync.save && window.SSSSync.save();   // push การลบขึ้น server ทันที
    setActive(null);
    window.toast && window.toast("ลบสินค้าแล้ว");
  };
  const adjustStock = (sku, delta, meta) => {
    meta = (typeof meta === "string") ? { reason: meta } : (meta || {});
    // เขียน D.Products ตรงๆ แล้วค่อย sync state — กันค่าถูก setProducts รอบอื่นทับจนหาย
    const product = (D.Products || []).find(p => p.sku === sku);
    if (product) {
      product.stock = Math.max(0, Math.round(((Number(product.stock) || 0) + delta) * 100) / 100);
      if (meta.byPercent) product.byPercent = true;   // จำว่าสินค้านี้เบิกเป็น % (วัสดุสิ้นเปลือง)
      D.InventoryMoves = D.InventoryMoves || [];
      D.InventoryMoves.unshift({
        date: new Date().toLocaleString("th-TH", { day: "2-digit", month: "2-digit", year: "2-digit", hour: "2-digit", minute: "2-digit" }),
        type: delta > 0 ? "รับเข้า" : "เบิกใช้งาน",
        sku, name: product.name, qty: delta,
        ref: meta.projectCode || meta.reason || (delta > 0 ? "ADJ-IN" : "ADJ-OUT"),
        by: meta.by || (window.actorName ? window.actorName() : "")
      });
      // ลิงค์การเบิกเข้ากับโปรเจกต์ → บันทึกเป็นค่าใช้จ่ายของโปรเจกต์ (โชว์ในต้นทุนงานทันที)
      if (delta < 0 && meta.projectCode && window.ProjX) {
        const PX = window.ProjX;
        const used = Math.round(Math.abs(delta) * 100) / 100;
        const amount = Math.round((product.cost || 0) * used);
        const exp = { date: PX.pxToday(), category: "ค่าวัสดุ/วัตถุดิบ", vendor: product.name, amount, status: "เบิกแล้ว", sku, qty: meta.rawQty || used, percent: meta.percent || 100, byPercent: !!meta.byPercent, used, unit: product.unit, by: meta.by || "" };
        const cur = PX.pxGet(meta.projectCode).expenses || [];
        PX.pxSet(meta.projectCode, { expenses: [...cur, exp] });
      }
    }
    setProducts([...(D.Products || [])]);
    if (active && active.sku === sku && product) setActive({ ...product });
    saveOverrides(D.Products);
    window.dispatchEvent(new Event("sss-products-changed"));
    window.dispatchEvent(new Event("sss-stock-changed"));
    window.toast && window.toast((delta > 0 ? "+" : "") + (Math.round(delta * 100) / 100) + " " + sku + (meta.projectCode ? " · ผูกกับ " + meta.projectCode : ""));
  };

  return (
    <>
      <PageHead
        title="สินค้าและสต็อก"
        sub={`จัดการสินค้า · พิมพ์บาร์โค้ด · ติดตามคงคลังแบบเรียลไทม์ (${products.length} SKU)`}
        right={<>
          <Btn icon={I.qr} kind="ghost" onClick={() => navigate("barcode")}>สแกน/พิมพ์บาร์โค้ด</Btn>
          <Btn icon={I.download} kind="ghost" onClick={() => window.exportRowsCSV("products", [
            { key: "sku", label: "SKU" }, { key: "name", label: "สินค้า" }, { key: "category", label: "หมวด" },
            { key: "location", label: "Location" }, { key: "cost", label: "ต้นทุน" }, { key: "price", label: "ราคาขาย" },
            { key: "stock", label: "คงเหลือ" }, { key: "unit", label: "หน่วย" }
          ], filtered)}>นำเข้า/ส่งออก</Btn>
          <Btn icon={I.plus} kind="primary" onClick={() => setShowNew(true)}>เพิ่มสินค้า</Btn>
        </>}
      />

      <div className="grid cols-4 mb-lg">
        <window.UI.Stat label="สินค้าทั้งหมด" value={products.length} sub={`${userCats.length} หมวด · ${products.filter(p => p.category === "บริการ").length} เป็นบริการ`} icon={I.box}/>
        <window.UI.Stat label="มูลค่าสต็อกรวม" value={`฿${fmt0(products.filter(p=>p.category!=='บริการ').reduce((s,p)=>s+p.stock*p.cost,0))}`} sub="ตามต้นทุน" icon={I.warehouse}/>
        <window.UI.Stat label="สต็อกใกล้หมด" value={products.filter(p=>p.stock<=p.min&&p.category!=='บริการ').length} sub="ต้องสั่งซื้อเพิ่ม" dir="down" icon={I.bell}/>
        <window.UI.Stat label="การเคลื่อนไหววันนี้" value={D.InventoryMoves.filter(m=>m.date.startsWith("22")).length} sub="ดูประวัติเต็มที่หน้าการเคลื่อนไหว" icon={I.move}/>
      </div>

      <Card flush>
        <div style={{padding: 12, display: "flex", gap: 10, alignItems: "center", flexWrap: "wrap", borderBottom: "1px solid var(--line)"}}>
          <div style={{display: "flex", gap: 4, flexWrap: "wrap", alignItems: "center"}}>
            {cats.map(c => (
              <button key={c} className={`btn sm ${cat === c ? "primary" : "ghost"}`} onClick={() => setCat(c)}>{c}</button>
            ))}
            <Btn size="sm" kind="ghost" icon={I.cog} onClick={() => setShowCats(true)}>จัดการหมวดหมู่</Btn>
          </div>
          <span className="spacer"/>
          <div className="search" style={{width: 280}}>
            <I.search size={14}/>
            <input placeholder="ค้นหาชื่อ / SKU / บาร์โค้ด…" value={q} onChange={e=>setQ(e.target.value)}/>
          </div>
        </div>

        <div className="table-wrap">
          <table className="t">
            <thead>
              <tr>
                <th>SKU</th>
                <th>สินค้า</th>
                <th>หมวด</th>
                <th>Location</th>
                <th className="right">ต้นทุน</th>
                <th className="right">ราคาขาย</th>
                <th className="right">คงเหลือ</th>
                <th>สถานะ</th>
                <th></th>
              </tr>
            </thead>
            <tbody>
              {filtered.map(p => {
                const low = p.stock <= p.min && p.category !== "บริการ";
                const out = p.stock <= 0 && p.category !== "บริการ";
                return (
                  <tr key={p.sku} className="row-clickable" onClick={() => setActive(p)}>
                    <td className="mono">{p.sku}</td>
                    <td style={{maxWidth: 340}}>
                      <div style={{display: "flex", alignItems: "center", gap: 10, minWidth: 0}}>
                        {productPhoto(p)
                          ? <img src={productPhoto(p)} alt="" style={{width: 32, height: 32, objectFit: "cover", borderRadius: 6, border: "1px solid var(--line)", flexShrink: 0, display: "block"}}/>
                          : <div style={{width: 32, height: 32, borderRadius: 6, border: "1px dashed var(--line-strong)", background: "var(--surface-2)", display: "grid", placeItems: "center", color: "var(--ink-3)", flexShrink: 0}}><I.camera size={13}/></div>}
                        <span style={{overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap"}}>{p.name}</span>
                      </div>
                    </td>
                    <td className="muted">{p.category}</td>
                    <td className="mono" style={{fontSize: 12}}>{p.location}</td>
                    <td className="right amount muted">{p.cost > 0 ? `฿${fmt0(p.cost)}` : "—"}</td>
                    <td className="right amount">฿{fmt0(p.price)}</td>
                    <td className="right amount" style={{color: out ? "var(--danger)" : low ? "var(--warning)" : "var(--ink-1)"}}>
                      {p.category === "บริการ" ? "∞" : `${p.stock} ${p.unit}`}
                    </td>
                    <td>
                      {p.category === "บริการ" ? <Badge tone="outline">บริการ</Badge>
                        : out ? <Badge tone="danger">หมด</Badge>
                        : low ? <Badge tone="warning">ใกล้หมด</Badge>
                        : <Badge tone="success">พร้อมขาย</Badge>}
                    </td>
                    <td onClick={e => e.stopPropagation()}>
                      <div style={{display: "flex", gap: 2}}>
                        {p.category !== "บริการ" && (
                          <>
                            <button className="icon-btn" title="รับเข้า" onClick={() => setShowStock({ product: p, mode: "in" })}><I.plus size={12}/></button>
                            <button className="icon-btn" title="เบิกออก" onClick={() => setShowStock({ product: p, mode: "out" })}><I.arrowDown size={12}/></button>
                          </>
                        )}
                        <button className="icon-btn" title="แก้ไข" onClick={() => { setActive(p); setEditMode(true); }}><I.edit size={12}/></button>
                      </div>
                    </td>
                  </tr>
                );
              })}
              {filtered.length === 0 && (
                <tr><td colSpan={9} style={{padding: 24, textAlign: "center", color: "var(--ink-3)"}}>ไม่พบสินค้า · <button className="btn sm" onClick={() => setShowNew(true)}>เพิ่มสินค้าใหม่</button></td></tr>
              )}
            </tbody>
          </table>
        </div>
      </Card>

      {/* Product detail drawer with edit */}
      <Drawer open={!!active} onClose={() => { setActive(null); setEditMode(false); }} title={active ? (editMode ? "แก้ไขสินค้า " + active.sku : "สินค้า " + active.sku) : ""} wide
        footer={editMode ? (
          <>
            <Btn kind="ghost" onClick={() => setEditMode(false)}>ยกเลิก</Btn>
            <Btn kind="danger" icon={I.trash} onClick={() => deleteProduct(active.sku)}>ลบสินค้า</Btn>
            <Btn kind="primary" icon={I.check} onClick={() => { setEditMode(false); window.toast && window.toast("บันทึกแล้ว"); }}>บันทึก</Btn>
          </>
        ) : (
          <>
            <Btn kind="ghost" icon={I.print} onClick={() => navigate("barcode")}>พิมพ์บาร์โค้ด</Btn>
            <Btn kind="primary" icon={I.edit} onClick={() => setEditMode(true)}>แก้ไข</Btn>
          </>
        )}>
        {active && (editMode ? <ProductEdit p={active} update={updateProduct}/> : <ProductView p={active} adjust={adjustStock}/>)}
      </Drawer>

      {/* New product modal */}
      {showNew && <NewProductModal onClose={() => setShowNew(false)} onSave={addProduct}/>}

      {/* Stock adjust modal */}
      {showStock && <StockAdjustModal target={showStock} onClose={() => setShowStock(null)} onApply={(delta, meta) => {
        if (delta < 0) issueStockApply(meta.sku, delta, meta); else adjustStock(meta.sku, delta, meta);
        setProducts([...(D.Products || [])]);
        if (active) { const a = (D.Products || []).find(p => p.sku === active.sku); if (a) setActive({ ...a }); }
        setShowStock(null);
      }}/>}

      {showCats && <CategoryManagerModal onClose={() => setShowCats(false)}/>}
    </>
  );
}

function ProductView({ p, adjust }) {
  const D = window.SSSData;
  const { fmt0, BarcodeVisual } = window.UI;
  return (
    <div style={{display: "flex", flexDirection: "column", gap: 16}}>
      <div style={{display: "grid", gridTemplateColumns: "1fr 200px", gap: 16, alignItems: "flex-start"}}>
        <div>
          {productPhoto(p) && (
            <img src={productPhoto(p)} alt={p.name} style={{width: "100%", maxWidth: 360, maxHeight: 220, objectFit: "cover", borderRadius: 10, border: "1px solid var(--line)", display: "block", marginBottom: 12}}/>
          )}
          <div className="label">ชื่อสินค้า</div>
          <div style={{fontSize: 16, fontWeight: 600, marginBottom: 12}}>{p.name}</div>
          <div className="field-row cols-2">
            <div><div className="label">SKU</div><div className="mono">{p.sku}</div></div>
            <div><div className="label">หมวด</div><div>{p.category}</div></div>
            <div><div className="label">หน่วยนับ</div><div>{p.unit}</div></div>
            <div><div className="label">ตำแหน่งจัดเก็บ</div><div className="mono">{p.location}</div></div>
            <div><div className="label">ต้นทุน</div><div className="amount">฿{fmt0(p.cost)}</div></div>
            <div><div className="label">ราคาขาย</div><div className="amount">฿{fmt0(p.price)}</div></div>
          </div>
        </div>
        <div style={{textAlign: "center", padding: "8px 0"}}>
          {p.barcode !== "—" && <BarcodeVisual code={p.barcode} width={180}/>}
          {p.barcode === "—" && <div style={{padding: 30, color: "var(--ink-3)", textAlign: "center", fontSize: 12, border: "1px dashed var(--line)", borderRadius: 8}}>บริการ — ไม่มีบาร์โค้ด</div>}
        </div>
      </div>

      <div className="grid cols-3" style={{gap: 10}}>
        <div className="stat"><div className="label">คงเหลือปัจจุบัน</div><div className="value">{p.stock} {p.unit}</div></div>
        <div className="stat"><div className="label">ขั้นต่ำ / สูงสุด</div><div className="value" style={{fontSize: 17}}>{p.min} / {p.max}</div></div>
        <div className="stat"><div className="label">มูลค่าสต็อก</div><div className="value">฿{fmt0(p.cost * p.stock)}</div></div>
      </div>

      {p.category !== "บริการ" && (
        <div className="card" style={{padding: 14}}>
          <div className="h2 mb">ปรับสต็อกด่วน</div>
          <div style={{display: "grid", gridTemplateColumns: "1fr 1fr 1fr 1fr", gap: 6}}>
            <button className="btn" onClick={() => adjust(p.sku, +1)}>+1</button>
            <button className="btn" onClick={() => adjust(p.sku, +10)}>+10</button>
            <button className="btn" onClick={() => adjust(p.sku, -1)}>−1</button>
            <button className="btn" onClick={() => adjust(p.sku, -10)}>−10</button>
          </div>
        </div>
      )}

      <div>
        <div className="h2 mb">การเคลื่อนไหวล่าสุด</div>
        <div className="card" style={{padding: 0}}>
          <table className="t">
            <thead><tr><th>วันที่</th><th>ประเภท</th><th className="right">จำนวน</th><th>อ้างอิง</th><th>โดย</th></tr></thead>
            <tbody>
              {D.InventoryMoves.filter(m => m.sku === p.sku).slice(0, 6).map((m, i) => (
                <tr key={i}>
                  <td style={{fontSize: 12}}>{m.date}</td>
                  <td><window.UI.Badge>{m.type}</window.UI.Badge></td>
                  <td className="right amount" style={{color: m.qty > 0 ? "var(--success)" : "var(--danger)"}}>{m.qty > 0 ? "+" : ""}{m.qty} {p.unit}</td>
                  <td className="mono" style={{fontSize: 12}}>{m.ref}</td>
                  <td className="muted" style={{fontSize: 12}}>{m.by}</td>
                </tr>
              ))}
              {D.InventoryMoves.filter(m => m.sku === p.sku).length === 0 && (
                <tr><td colSpan={5} style={{padding: 16, textAlign: "center", color: "var(--ink-3)"}}>ยังไม่มีการเคลื่อนไหว</td></tr>
              )}
            </tbody>
          </table>
        </div>
      </div>
    </div>
  );
}

function ProductEdit({ p, update }) {
  const [f, setF] = useStateI(p);
  const [photoState, setPhotoState] = useStateI(productPhoto(p));
  const upd = (k, v) => { const next = { ...f, [k]: v }; setF(next); update(p.sku, { [k]: v }); };
  const changePhoto = (url) => { setPhotoState(url); setProductPhotoStore(p.sku, url); };  // เก็บในคลังรูปแยก (productPhoto จะดึงคลังรูปก่อน)
  return (
    <div style={{display: "flex", flexDirection: "column", gap: 12}}>
      <ProductPhotoInput value={photoState} onChange={changePhoto}/>
      <div className="field"><label>ชื่อสินค้า</label><input className="input" value={f.name} onChange={e=>upd("name", e.target.value)}/></div>
      <div className="field-row cols-2">
        <div className="field"><label>SKU</label><input className="input mono" value={f.sku} readOnly style={{background: "var(--surface-2)"}}/></div>
        <div className="field"><label>บาร์โค้ด</label><input className="input mono" value={f.barcode} onChange={e=>upd("barcode", e.target.value)}/></div>
      </div>
      <div className="field-row cols-2">
        <div className="field"><label>หมวด</label>
          <select className="input" value={f.category} onChange={e=>upd("category", e.target.value)}>
            {[...new Set([f.category, ...invCatsLoad()].filter(Boolean))].map(c => <option key={c}>{c}</option>)}
            {invCatsLoad().length === 0 && !f.category && <option value="">— ยังไม่มีหมวด เพิ่มที่หน้าสินค้า —</option>}
          </select>
        </div>
        <div className="field"><label>หน่วย</label><input className="input" value={f.unit} onChange={e=>upd("unit", e.target.value)}/></div>
      </div>
      <div className="field-row cols-2">
        <div className="field"><label>ต้นทุน (บาท)</label><input className="input" type="number" value={f.cost} onChange={e=>upd("cost", +e.target.value)}/></div>
        <div className="field"><label>ราคาขาย (บาท)</label><input className="input" type="number" value={f.price} onChange={e=>upd("price", +e.target.value)}/></div>
      </div>
      <div className="field-row cols-3">
        <div className="field"><label>คงเหลือ</label><input className="input" type="number" value={f.stock} onChange={e=>upd("stock", +e.target.value)}/></div>
        <div className="field"><label>ขั้นต่ำ</label><input className="input" type="number" value={f.min} onChange={e=>upd("min", +e.target.value)}/></div>
        <div className="field"><label>สูงสุด</label><input className="input" type="number" value={f.max} onChange={e=>upd("max", +e.target.value)}/></div>
      </div>
      <div className="field"><label>ตำแหน่งจัดเก็บ</label><input className="input mono" value={f.location} onChange={e=>upd("location", e.target.value)}/></div>
    </div>
  );
}

function NewProductModal({ onClose, onSave }) {
  const { Btn, Drawer, BarcodeVisual } = window.UI;
  const [f, setF] = useStateI({
    source: "internal", sku: "", barcode: "", name: "", category: "", unit: "ชิ้น",
    cost: 0, price: 0, stock: 0, min: 5, max: 100, location: "", photo: null
  });
  const upd = (k, v) => setF(p => ({ ...p, [k]: v }));
  const internal = f.source === "internal";

  // ===== Auto code generators for internal (ผลิตเอง) products =====
  const eanCheck = (d12) => {
    let sum = 0;
    for (let i = 0; i < 12; i++) sum += (+d12[i]) * (i % 2 === 0 ? 1 : 3);
    return String((10 - (sum % 10)) % 10);
  };
  const genInternalCodes = () => {
    const nums = (window.SSSData.Products || [])
      .map(p => (String(p.sku).match(/^FG-(\d+)$/) || [])[1])
      .filter(Boolean).map(Number);
    const n = (nums.length ? Math.max(...nums) : 0) + 1;
    const sku = "FG-" + String(n).padStart(4, "0");
    const base12 = "8851000" + String(n).padStart(5, "0"); // 885=TH GS1, 1000=SSS prefix
    const barcode = base12 + eanCheck(base12);
    return { sku, barcode };
  };
  const regen = () => { const c = genInternalCodes(); setF(p => ({ ...p, ...c })); };
  // สร้างรหัสสินค้าสำหรับสินค้าซื้อมาขาย (กรอกเอง) — prefix จากหมวด ไม่งั้น PRD + เลขรัน
  const genExternalSKU = () => {
    const catCode = ((String(f.category || "").match(/[A-Za-z0-9]+/g) || []).join("").toUpperCase().slice(0, 5)) || "PRD";
    const re = new RegExp("^" + catCode + "-(\\d+)$");
    const nums = (window.SSSData.Products || []).map(p => (String(p.sku).match(re) || [])[1]).filter(Boolean).map(Number);
    const n = (nums.length ? Math.max(...nums) : 0) + 1;
    const sku = catCode + "-" + String(n).padStart(4, "0");
    setF(p => {
      const next = { ...p, sku };
      if (!p.barcode) { const base12 = "8851000" + String(Date.now()).slice(-5); next.barcode = base12 + eanCheck(base12); }
      return next;
    });
    window.toast && window.toast("สร้างรหัสสินค้า " + sku + " ให้แล้ว");
  };
  React.useEffect(() => {
    if (internal) setF(p => ({ ...p, ...genInternalCodes() }));
    else setF(p => ({ ...p, sku: "", barcode: "" }));
  }, [f.source]);

  const save = () => {
    if (!f.name) { window.toast && window.toast("กรุณากรอกชื่อสินค้า"); return; }
    if (!internal && !f.sku) { window.toast && window.toast("กรุณากรอก SKU สำหรับสินค้าซื้อมาขาย"); return; }
    const { source, photo, ...rest } = f;
    const sku = String(f.sku).toUpperCase();
    if (photo) setProductPhotoStore(sku, photo);   // เก็บรูปในคลังรูปแยก (กันก้อน Products ใหญ่/รูปหายตอน sync)
    onSave({ ...rest, sku, internal });
  };
  return (
    <Drawer open={true} onClose={onClose} title="เพิ่มสินค้าใหม่" wide
      footer={<><Btn kind="ghost" onClick={onClose}>ยกเลิก</Btn><Btn kind="primary" icon={I.check} onClick={save}>บันทึก</Btn></>}>
      <div style={{display: "flex", flexDirection: "column", gap: 12}}>
        <div className="field">
          <label>ประเภทสินค้า</label>
          <div className="payseg" style={{display: "flex", width: "100%"}}>
            <button type="button" className={"payseg-btn" + (internal ? " active" : "")} style={{flex: 1}} onClick={() => upd("source", "internal")}>ผลิตเอง / สินค้าภายใน</button>
            <button type="button" className={"payseg-btn" + (!internal ? " active" : "")} style={{flex: 1}} onClick={() => upd("source", "external")}>ซื้อมาขาย</button>
          </div>
        </div>
        <div className="field"><label>ชื่อสินค้า *</label><input className="input" value={f.name} onChange={e=>upd("name", e.target.value)} placeholder="เช่น ราวบันไดสแตนเลส 304"/></div>
        <ProductPhotoInput value={f.photo} onChange={url => upd("photo", url)}/>
        <div className="field-row cols-2">
          <div className="field">
            <label style={{display: "flex", alignItems: "center", gap: 6}}>รหัสสินค้า (SKU){internal && <span className="auto-tag">อัตโนมัติ</span>}</label>
            {internal ? (
              <input className="input mono" value={f.sku} readOnly placeholder="" style={{background: "var(--surface-2)", cursor: "default"}}/>
            ) : (
              <div style={{display: "flex", gap: 6}}>
                <input className="input mono" value={f.sku} onChange={e=>upd("sku", e.target.value)} placeholder="SS304-SH-1.5" style={{flex: 1}}/>
                <Btn size="sm" kind="ghost" icon={I.sparkle || I.plus} onClick={genExternalSKU} title="สร้างรหัสสินค้าอัตโนมัติ">สร้างรหัส</Btn>
              </div>
            )}
          </div>
          <div className="field">
            <label style={{display: "flex", alignItems: "center", gap: 6}}>บาร์โค้ด (EAN-13){internal && <span className="auto-tag">สร้างให้อัตโนมัติ</span>}</label>
            <input className="input mono" value={f.barcode} readOnly={internal} onChange={e=>upd("barcode", e.target.value)} placeholder={internal ? "" : "8851000000000"} style={internal ? {background: "var(--surface-2)", cursor: "default"} : {}}/>
          </div>
        </div>
        {internal && (
          <div style={{display: "flex", alignItems: "center", gap: 14, padding: "10px 14px", background: "var(--brand-bg)", border: "1px solid var(--brand-soft)", borderRadius: 10}}>
            {BarcodeVisual && <BarcodeVisual code={f.barcode} height={34} width={120}/>}
            <div style={{flex: 1, minWidth: 0}}>
              <div style={{fontSize: 12.5, fontWeight: 600, color: "var(--brand-strong)"}}>ระบบออกรหัสและบาร์โค้ดให้อัตโนมัติ</div>
              <div style={{fontSize: 11.5, color: "var(--ink-3)"}}>SKU <b className="mono" style={{color: "var(--ink-1)"}}>{f.sku}</b> · บาร์โค้ด <b className="mono" style={{color: "var(--ink-1)"}}>{f.barcode}</b></div>
            </div>
            <Btn size="sm" kind="ghost" icon={I.sparkle} onClick={regen}>สร้างใหม่</Btn>
          </div>
        )}
        <div className="field-row cols-2">
          <div className="field"><label>หมวด</label>
            <select className="input" value={f.category} onChange={e=>upd("category", e.target.value)}>
              <option value="">— เลือกหมวด —</option>
              {[...new Set([f.category, ...invCatsLoad()].filter(Boolean))].map(c => <option key={c}>{c}</option>)}
            </select>
          </div>
          <div className="field"><label>หน่วย</label>
            <select className="input" value={f.unit} onChange={e=>upd("unit", e.target.value)}>
              {["ชิ้น", "แผ่น", "เส้น", "ใบ", "ชุด", "ม้วน", "lot", "ชม."].map(u => <option key={u}>{u}</option>)}
            </select>
          </div>
        </div>
        <div className="field-row cols-2">
          <div className="field"><label>ต้นทุน (บาท)</label><input className="input" type="number" value={f.cost} onChange={e=>upd("cost", +e.target.value)}/></div>
          <div className="field"><label>ราคาขาย (บาท)</label><input className="input" type="number" value={f.price} onChange={e=>upd("price", +e.target.value)}/></div>
        </div>
        <div className="field-row cols-3">
          <div className="field"><label>สต็อกเริ่มต้น</label><input className="input" type="number" value={f.stock} onChange={e=>upd("stock", +e.target.value)}/></div>
          <div className="field"><label>ขั้นต่ำ</label><input className="input" type="number" value={f.min} onChange={e=>upd("min", +e.target.value)}/></div>
          <div className="field"><label>สูงสุด</label><input className="input" type="number" value={f.max} onChange={e=>upd("max", +e.target.value)}/></div>
        </div>
        <div className="field"><label>ตำแหน่งจัดเก็บ</label><input className="input mono" value={f.location} onChange={e=>upd("location", e.target.value)} placeholder="A-01-01"/></div>
      </div>
    </Drawer>
  );
}

// ผืนผ้าวาดสี่เหลี่ยมบน "แผ่น" — ลากนิ้ว/เมาส์เพื่อระบุชิ้นที่เบิก คืนค่า % ของพื้นที่
function SheetCutCanvas({ onPercent, accent = "#dc2b2b" }) {
  const wrapRef = useRefI(null);
  const dragRef = useRefI(null);
  const [rect, setRect] = useStateI(null); // {x,y,w,h} หน่วย 0..1
  const getPt = (e) => {
    const r = wrapRef.current.getBoundingClientRect();
    const t = e.touches && e.touches[0] ? e.touches[0] : e;
    return { x: Math.min(1, Math.max(0, (t.clientX - r.left) / r.width)), y: Math.min(1, Math.max(0, (t.clientY - r.top) / r.height)) };
  };
  const down = (e) => { e.preventDefault(); const p = getPt(e); dragRef.current = p; setRect({ x: p.x, y: p.y, w: 0, h: 0 }); onPercent(0); };
  const move = (e) => {
    if (!dragRef.current) return; e.preventDefault();
    const p = getPt(e), s = dragRef.current;
    const x = Math.min(s.x, p.x), y = Math.min(s.y, p.y), w = Math.abs(p.x - s.x), h = Math.abs(p.y - s.y);
    setRect({ x, y, w, h });
    onPercent(Math.min(100, Math.round(w * h * 100)));
  };
  const up = () => { dragRef.current = null; };
  return (
    <div>
      <div ref={wrapRef}
        onMouseDown={down} onMouseMove={move} onMouseUp={up} onMouseLeave={up}
        onTouchStart={down} onTouchMove={move} onTouchEnd={up}
        style={{position: "relative", width: "100%", aspectRatio: "3 / 4", maxHeight: "44vh", margin: "0 auto", background: "#fff", border: "2px solid #111", borderRadius: 6, touchAction: "none", cursor: "crosshair", userSelect: "none", overflow: "hidden"}}>
        {rect && rect.w > 0 && <div style={{position: "absolute", left: rect.x * 100 + "%", top: rect.y * 100 + "%", width: rect.w * 100 + "%", height: rect.h * 100 + "%", background: "color-mix(in oklab, " + accent + " 28%, transparent)", border: "2px solid " + accent}}/>}
        {!rect && <div style={{position: "absolute", inset: 0, display: "grid", placeItems: "center", color: "#bbb", fontSize: 12.5, pointerEvents: "none", textAlign: "center", padding: 12}}>แตะแล้วลากวาดสี่เหลี่ยม<br/>ส่วนที่เบิกออกจากแผ่น</div>}
      </div>
      <div style={{textAlign: "center", marginTop: 6}}><button className="btn sm" type="button" onClick={() => { setRect(null); onPercent(0); }}>ล้างที่วาด</button></div>
    </div>
  );
}

function StockAdjustModal({ target, onClose, onApply }) {
  const { Btn, ProductAutocomplete, fmt0 } = window.UI;
  const D = window.SSSData;
  const PX = window.ProjX;
  const isOut = target.mode === "out";
  const [product, setProduct] = useStateI(target.product || null);
  const [pName, setPName] = useStateI(target.product ? target.product.name : "");
  const [scanning, setScanning] = useStateI(false);
  // โหมดการเบิก: full = เต็มจำนวน · percent = บางส่วน % · draw = วาดบนแผ่น
  const looksSheet = (p) => p && /แผ่น|sheet|เพลท|plate/i.test((p.name || "") + " " + (p.category || ""));
  const [wmode, setWmode] = useStateI(() => (target.product && target.product.byPercent) ? "percent" : "full");
  const [qty, setQty] = useStateI(1);
  const [percent, setPercent] = useStateI(50);
  const [drawPct, setDrawPct] = useStateI(0);
  const [offcut, setOffcut] = useStateI(false);
  const emps = D.Employees || [];
  const actor = window.actorName ? window.actorName() : "";
  const [byId, setById] = useStateI(() => (emps.find(e => e.name === actor) || {}).id || "");
  const [proj, setProj] = useStateI("");
  const [reason, setReason] = useStateI("");

  const projects = [...(PX && PX.upLoad ? PX.upLoad() : []), ...(D.Projects || [])];
  const byName = (emps.find(e => e.id === byId) || {}).name || actor;
  const unit = product ? (product.unit || "หน่วย") : "หน่วย";

  // คำนวณตามโหมด
  const pct = !isOut ? 100 : (wmode === "draw" ? drawPct : (wmode === "percent" ? (Number(percent) || 0) : 100));
  const partial = isOut && (wmode === "percent" || wmode === "draw") && pct > 0 && pct < 100;
  const offcutOn = partial && offcut;
  const usedFrac = pct / 100;
  const units = !isOut ? (Number(qty) || 0) : (wmode === "draw" ? 1 : (offcutOn ? 1 : (Number(qty) || 0)));
  const effective = Math.round(units * usedFrac * 100) / 100;   // จำนวนที่ใช้/คิดเงินกับงาน
  const removeUnits = !isOut ? effective : (offcutOn ? 1 : effective); // ตัดออกจากสต็อกเดิม
  const remainFrac = offcutOn ? Math.round((1 - usedFrac) * 100) / 100 : 0;
  const projName = (projects.find(p => p.code === proj) || {}).name || "";

  const pick = (p) => { setProduct(p); setPName(p.name); if (isOut && (p.byPercent || looksSheet(p))) setWmode(looksSheet(p) ? "draw" : "percent"); if (looksSheet(p)) setOffcut(true); };
  const onScan = (code) => {
    setScanning(false);
    const p = (D.Products || []).find(x => (x.barcode && x.barcode === code) || x.sku === code);
    if (p) { pick(p); window.toast && window.toast("พบสินค้า: " + p.name); }
    else window.toast && window.toast("ไม่พบสินค้าที่ตรงกับรหัสนี้");
  };

  const apply = () => {
    if (!product) { window.toast && window.toast("กรุณาเลือกสินค้าก่อน"); return; }
    if (isOut && !byId) { window.toast && window.toast("กรุณาเลือกผู้เบิก"); return; }
    if (!effective || effective <= 0) { window.toast && window.toast("กรุณาระบุจำนวน/เปอร์เซ็นต์ให้ถูกต้อง"); return; }
    const delta = isOut ? -removeUnits : effective;
    onApply(delta, {
      sku: product.sku, reason, by: byName, byId,
      projectCode: isOut ? proj : "", projectName: projName,
      percent: pct, rawQty: units, byPercent: isOut && wmode !== "full",
      chargeUsed: effective,
      offcut: offcutOn ? { remainFrac } : null
    });
  };

  const segBtn = (v, label) => (
    <button type="button" className={"payseg-btn" + (wmode === v ? " active" : "")} style={{flex: 1, padding: "9px 4px"}} onClick={() => setWmode(v)}>{label}</button>
  );

  return (
    <>
      <div className="scrim" style={{zIndex: 80}} onClick={onClose}/>
      <div style={{position: "fixed", left: "50%", top: "50%", transform: "translate(-50%, -50%)", zIndex: 81, background: "var(--surface)", border: "1px solid var(--line)", borderRadius: 14, padding: 16, width: 460, maxWidth: "96vw", maxHeight: "94vh", overflow: "auto", boxShadow: "var(--shadow-lg)"}}>
        <div style={{display: "flex", alignItems: "center", gap: 8, marginBottom: 12}}>
          <div style={{fontSize: 15, fontWeight: 700, flex: 1}}>{isOut ? "เบิกสินค้าออก" : "รับสินค้าเข้า"}{product ? " · " + product.sku : ""}</div>
          <button className="icon-btn" onClick={onClose}><I.close size={14}/></button>
        </div>

        {/* เลือกสินค้า + สแกน */}
        {!target.product && (
          <div className="field mb"><label>เลือกสินค้า — พิมพ์ค้นหา หรือสแกนบาร์โค้ด</label>
            <div style={{display: "flex", gap: 8, alignItems: "stretch"}}>
              <div style={{flex: 1, minWidth: 0}}>
                <ProductAutocomplete value={pName} placeholder="เช่น เหล็กแผ่น 1.2 มม."
                  onChange={v => { setPName(v); setProduct(null); }} onPick={pick}/>
              </div>
              <Btn kind="ghost" icon={I.qr} onClick={() => setScanning(true)} style={{flexShrink: 0}}>สแกน</Btn>
            </div>
          </div>
        )}

        {product && (
          <div style={{display: "flex", alignItems: "center", gap: 10, padding: "10px 12px", background: "var(--surface-2)", border: "1px solid var(--line)", borderRadius: 10, marginBottom: 12}}>
            <div style={{flex: 1, minWidth: 0}}>
              <div style={{fontWeight: 600, fontSize: 13.5}}>{product.name}</div>
              <div style={{fontSize: 12, color: "var(--ink-3)"}}>คงเหลือ <b style={{color: "var(--ink-1)"}}>{product.stock} {unit}</b>{product.cost != null && <> · ทุน ฿{fmt0(product.cost)}/{unit}</>}</div>
            </div>
          </div>
        )}

        {/* ผู้เบิก + โปรเจกต์ (เบิกออก) — มือถือเรียงลงล่าง */}
        {isOut && (
          <div className="field-row" style={{display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(150px, 1fr))", gap: 10, marginBottom: 12}}>
            <div className="field"><label>ผู้เบิก *</label>
              <select className="input" value={byId} onChange={e => setById(e.target.value)}>
                <option value="">— เลือกพนักงาน —</option>
                {emps.map(e => <option key={e.id} value={e.id}>{e.name}{e.role ? ` (${e.role})` : ""}</option>)}
              </select>
            </div>
            <div className="field"><label>เบิกให้โปรเจกต์</label>
              <select className="input mono" value={proj} onChange={e => setProj(e.target.value)}>
                <option value="">— ไม่ผูกโปรเจกต์ —</option>
                {projects.map(p => <option key={p.code} value={p.code}>{p.code} · {p.name}</option>)}
              </select>
            </div>
          </div>
        )}

        {/* โหมดการเบิก */}
        {isOut && product && (
          <div className="field mb"><label>วิธีเบิก</label>
            <div className="payseg" style={{display: "flex", width: "100%", gap: 0}}>
              {segBtn("full", "เต็มจำนวน")}
              {segBtn("percent", "บางส่วน %")}
              {segBtn("draw", "วาดบนแผ่น")}
            </div>
          </div>
        )}

        {/* เต็มจำนวน หรือ บางส่วน % : ช่องจำนวน */}
        {(!isOut || wmode !== "draw") && (
          <div className="field-row" style={{display: "grid", gridTemplateColumns: (isOut && wmode === "percent") ? "1fr 1fr" : "1fr", gap: 10, marginBottom: (isOut && wmode === "percent") ? 10 : 12}}>
            <div className="field"><label>{isOut && wmode === "percent" ? `จำนวน${offcutOn ? " (ทีละ 1 แผ่น)" : "แผ่น/หน่วย"}` : `จำนวน (${unit})`}</label>
              <input className="input" type="number" min="0" step="any" value={offcutOn ? 1 : qty} disabled={offcutOn} onChange={e => setQty(Math.max(0, +e.target.value || 0))} autoFocus={!!target.product} style={{fontSize: 16}}/>
            </div>
            {isOut && wmode === "percent" && <div className="field"><label>คิดเป็นทุน</label><input className="input" value={product ? "฿" + fmt0(Math.round((product.cost || 0) * effective)) : "—"} readOnly style={{background: "var(--surface-2)"}}/></div>}
          </div>
        )}

        {/* บางส่วน % : แถบเลื่อน */}
        {isOut && wmode === "percent" && (
          <div className="field mb">
            <label>เบิกไปกี่ % ของแต่ละหน่วย — <b style={{color: "var(--accent)", fontSize: 15}}>{percent}%</b> · เหลือ {100 - (Number(percent) || 0)}%</label>
            <input type="range" min="0" max="100" step="5" value={percent} onChange={e => setPercent(e.target.value)} style={{width: "100%", height: 28, accentColor: "var(--accent)"}}/>
            <div style={{fontSize: 11, color: "var(--ink-3)"}}>เช่น เบิกเหล็กครึ่งเส้น = 50%</div>
          </div>
        )}

        {/* วาดบนแผ่น */}
        {isOut && wmode === "draw" && (
          <div className="field mb">
            <label>วาดส่วนที่เบิกบนแผ่น — เบิกไป <b style={{color: "var(--accent)", fontSize: 15}}>{drawPct}%</b> · เหลือ {100 - drawPct}%</label>
            <SheetCutCanvas onPercent={setDrawPct} accent="var(--accent)"/>
          </div>
        )}

        {/* แยกเศษเป็นสินค้าใหม่ (ถูกแบ่ง) */}
        {partial && (
          <label style={{display: "flex", alignItems: "flex-start", gap: 8, cursor: "pointer", padding: "9px 11px", border: "1px solid var(--line)", borderRadius: 8, background: offcut ? "color-mix(in oklab, var(--accent) 8%, var(--surface))" : "var(--surface)", marginBottom: 12}}>
            <input type="checkbox" checked={offcut} onChange={e => setOffcut(e.target.checked)} style={{marginTop: 2, width: 18, height: 18, accentColor: "var(--accent)"}}/>
            <span style={{fontSize: 12.5, lineHeight: 1.45}}>
              <b>แยกเศษที่เหลือเป็นสินค้าใหม่ (ถูกแบ่ง)</b>
              <div style={{color: "var(--ink-3)", fontSize: 11.5}}>ตัดออก 1 {unit}เต็ม · ใช้กับงาน {pct}% · เศษ {100 - pct}% บันทึกเป็นสินค้าใหม่ไว้ใช้ครั้งต่อไป</div>
            </span>
          </label>
        )}

        <div className="field mb"><label>หมายเหตุ (ไม่บังคับ)</label>
          <input className="input" value={reason} onChange={e => setReason(e.target.value)} placeholder={isOut ? "เช่น เบิกไปหน้างาน / งานเร่ง" : "เช่น รับเข้าจาก PO"}/>
        </div>

        {product && isOut && (
          <div style={{fontSize: 12.5, color: "var(--ink-2)", marginBottom: 12, padding: "10px 12px", background: "var(--surface-2)", borderRadius: 8}}>
            ตัดสต็อก <b>{removeUnits} {unit}</b> · คงเหลือ <b>{Math.max(0, Math.round((product.stock - removeUnits) * 100) / 100)} {unit}</b>
            {offcutOn && <><br/>เศษที่เหลือ → สินค้าใหม่ <b>“{product.name} (ถูกแบ่ง ~{100 - pct}%)”</b></>}
            {proj && <><br/>ลงต้นทุนงาน <b className="mono">{proj}</b> ฿{fmt0(Math.round((product.cost || 0) * effective))}</>}
          </div>
        )}

        <div style={{display: "flex", gap: 8}}>
          <Btn kind="ghost" onClick={onClose} style={{flex: "0 0 auto"}}>ยกเลิก</Btn>
          <Btn kind="primary" onClick={apply} style={{flex: 1, justifyContent: "center", padding: "12px"}}>{isOut ? `เบิกออก ${effective || 0} ${unit}` : `รับเข้า ${qty} ${unit}`}</Btn>
        </div>
      </div>
      {scanning && <BarcodeScanModal onClose={() => setScanning(false)} onDetected={onScan}/>}
    </>
  );
}

// ============ Stock Movements ============
function StockMovements({ navigate }) {
  const D = window.SSSData;
  const { Btn, Badge, Card, PageHead, fmt0 } = window.UI;
  const [moves, setMoves] = useStateI(D.InventoryMoves);
  const [showNew, setShowNew] = useStateI(false);

  const addMove = (m) => {
    const entry = {
      date: new Date().toLocaleString("th-TH", { day: "2-digit", month: "2-digit", year: "2-digit", hour: "2-digit", minute: "2-digit" }),
      type: m.type,
      sku: m.sku,
      name: m.name,
      qty: m.type === "รับเข้า" ? Math.abs(m.qty) : -Math.abs(m.qty),
      ref: m.ref || "MV-MANUAL",
      by: window.actorName ? window.actorName() : ""
    };
    D.InventoryMoves.unshift(entry);
    setMoves([...D.InventoryMoves]);
    // Reflect in product stock
    const prod = D.Products.find(p => p.sku === m.sku);
    if (prod) prod.stock = Math.max(0, prod.stock + entry.qty);
    setShowNew(false);
    window.logActivity && window.logActivity("บันทึกการเคลื่อนไหว", `${entry.type} ${entry.sku} (${entry.qty > 0 ? "+" : ""}${entry.qty})`, "create");
    window.toast && window.toast(`บันทึก ${entry.type} ${entry.sku} แล้ว`);
  };

  return (
    <>
      <PageHead title="การเคลื่อนไหวสินค้า · Stock Movements"
        sub="ทุกการรับเข้า เบิกใช้งาน ขาย และปรับสต็อก"
        right={<><Btn icon={I.download} kind="ghost" onClick={() => window.exportRowsCSV("stock-movements", [
          { key: "date", label: "วันที่/เวลา" }, { key: "type", label: "ประเภท" }, { key: "sku", label: "SKU" },
          { key: "name", label: "สินค้า" }, { key: "qty", label: "จำนวน" }, { key: "ref", label: "อ้างอิง" }, { key: "by", label: "ผู้บันทึก" }
        ], moves)}>ส่งออก</Btn><Btn icon={I.plus} kind="primary" onClick={() => setShowNew(true)}>บันทึกการเคลื่อนไหว</Btn></>}/>
      <Card flush>
        <div className="table-wrap">
          <table className="t">
            <thead><tr><th>วันที่/เวลา</th><th>ประเภท</th><th>SKU</th><th>สินค้า</th><th className="right">จำนวน</th><th>อ้างอิง</th><th>ผู้บันทึก</th></tr></thead>
            <tbody>
              {moves.map((m, i) => (
                <tr key={i}>
                  <td className="mono" style={{fontSize: 12}}>{m.date}</td>
                  <td><Badge>{m.type}</Badge></td>
                  <td className="mono">{m.sku}</td>
                  <td>{m.name}</td>
                  <td className="right amount" style={{color: m.qty > 0 ? "var(--success)" : "var(--danger)"}}>{m.qty > 0 ? "+" : ""}{m.qty}</td>
                  <td className="mono" style={{fontSize: 12}}>{m.ref}</td>
                  <td className="muted">{m.by}</td>
                </tr>
              ))}
            </tbody>
          </table>
        </div>
      </Card>
      {showNew && <MovementModal onClose={() => setShowNew(false)} onSave={addMove}/>}
    </>
  );
}

function MovementModal({ onClose, onSave }) {
  const D = window.SSSData;
  const { Btn, Drawer } = window.UI;
  const products = D.Products;
  const [f, setF] = useStateI({
    type: "รับเข้า", sku: products[0]?.sku || "", qty: 1, ref: ""
  });
  const upd = (k, v) => setF(p => ({ ...p, [k]: v }));
  const prod = products.find(p => p.sku === f.sku);
  const types = ["รับเข้า", "เบิกใช้งาน", "ขาย", "ปรับสต็อก", "คืนสินค้า"];
  const isIn = f.type === "รับเข้า" || f.type === "คืนสินค้า";
  const save = () => {
    if (!f.sku) { window.toast && window.toast("เลือกสินค้าก่อน"); return; }
    if (!f.qty || f.qty <= 0) { window.toast && window.toast("กรอกจำนวนให้ถูกต้อง"); return; }
    onSave({ ...f, name: prod?.name || f.sku });
  };
  return (
    <Drawer open={true} onClose={onClose} title="บันทึกการเคลื่อนไหวสต็อก" wide
      footer={<><Btn kind="ghost" onClick={onClose}>ยกเลิก</Btn><Btn kind="primary" icon={I.check} onClick={save}>บันทึก</Btn></>}>
      <div style={{display: "flex", flexDirection: "column", gap: 12}}>
        <div className="field"><label>ประเภทการเคลื่อนไหว</label>
          <select className="input" value={f.type} onChange={e=>upd("type", e.target.value)}>
            {types.map(t => <option key={t}>{t}</option>)}
          </select>
        </div>
        <div className="field"><label>สินค้า (SKU)</label>
          <select className="input" value={f.sku} onChange={e=>upd("sku", e.target.value)}>
            {products.map(p => <option key={p.sku} value={p.sku}>{p.sku} · {p.name}</option>)}
          </select>
        </div>
        {prod && <div style={{fontSize: 12.5, color: "var(--ink-3)"}}>คงเหลือปัจจุบัน: <b style={{color: "var(--ink-1)"}}>{prod.stock} {prod.unit}</b> → หลังบันทึก: <b style={{color: "var(--accent)"}}>{Math.max(0, prod.stock + (isIn ? Math.abs(f.qty) : -Math.abs(f.qty)))} {prod.unit}</b></div>}
        <div className="field-row cols-2">
          <div className="field"><label>จำนวน {isIn ? "(เพิ่ม)" : "(ลด)"}</label><input className="input" type="number" min="1" value={f.qty} onChange={e=>upd("qty", +e.target.value)}/></div>
          <div className="field"><label>เลขที่อ้างอิง</label><input className="input mono" value={f.ref} onChange={e=>upd("ref", e.target.value)} placeholder={isIn ? "PO-2569-XXXX" : "JOB-2569-XXX"}/></div>
        </div>
      </div>
    </Drawer>
  );
}

// ---------- สแกนบาร์โค้ดด้วยกล้อง (ZXing — รองรับ iOS/Android) ----------
function BarcodeScanModal({ onClose, onDetected }) {
  const { Btn } = window.UI;
  const videoRef = useRefI(null);
  const readerRef = useRefI(null);
  const [err, setErr] = useStateI(null);
  const [ready, setReady] = useStateI(false);
  const [manual, setManual] = useStateI("");

  useEffectI(() => {
    let stopped = false;
    const Z = window.ZXing;
    if (!Z || !Z.BrowserMultiFormatReader) { setErr("ไม่พบไลบรารีสแกน — กรุณาพิมพ์รหัสเอง"); return; }
    const reader = new Z.BrowserMultiFormatReader();
    readerRef.current = reader;
    const cb = (result) => {
      if (stopped || !result) return;
      const text = result.getText ? result.getText() : result.text;
      if (text) { stopped = true; try { reader.reset(); } catch (e) {} onDetected(String(text)); }
    };
    const onErr = () => { if (!stopped) setErr("เปิดกล้องไม่ได้ — กรุณาอนุญาตให้ใช้กล้อง หรือพิมพ์รหัสเอง"); };
    const constraints = { video: { facingMode: { ideal: "environment" } }, audio: false };
    try {
      const started = typeof reader.decodeFromConstraints === "function" ?
        reader.decodeFromConstraints(constraints, videoRef.current, cb) :
        reader.decodeFromVideoDevice(null, videoRef.current, cb);
      Promise.resolve(started).then(() => { if (!stopped) setReady(true); }).catch(onErr);
    } catch (e) { onErr(); }
    return () => { stopped = true; try { reader.reset(); } catch (e) {} };
  }, []);

  const submitManual = () => { const t = manual.trim(); if (t) { try { readerRef.current && readerRef.current.reset(); } catch (e) {} onDetected(t); } };

  return (
    <>
      <div className="scrim" style={{ zIndex: 80 }} onClick={onClose}></div>
      <div style={{ position: "fixed", left: "50%", top: "50%", transform: "translate(-50%, -50%)", zIndex: 81, background: "var(--surface)", border: "1px solid var(--line)", borderRadius: 14, padding: 16, width: "min(440px, 92vw)", boxShadow: "var(--shadow-lg)" }}>
        <div style={{ display: "flex", alignItems: "center", gap: 8, marginBottom: 12 }}>
          <I.qr size={18} />
          <div style={{ fontSize: 15, fontWeight: 700, flex: 1 }}>สแกนบาร์โค้ด</div>
          <button className="icon-btn" onClick={onClose}><I.close size={14} /></button>
        </div>
        {err ?
        <div style={{ padding: 14, background: "var(--danger-bg)", color: "var(--danger)", borderRadius: 8, fontSize: 13, lineHeight: 1.6 }}>{err}</div> :
        <div style={{ position: "relative", background: "#000", borderRadius: 10, overflow: "hidden", aspectRatio: "4 / 3" }}>
            <video ref={videoRef} autoPlay playsInline muted style={{ width: "100%", height: "100%", objectFit: "cover", display: "block" }}></video>
            <div style={{ position: "absolute", left: "8%", right: "8%", top: "50%", height: 2, background: "rgba(255,90,90,0.9)", boxShadow: "0 0 8px rgba(255,90,90,0.9)", transform: "translateY(-50%)" }}></div>
            <div style={{ position: "absolute", inset: "14% 8%", border: "2px solid rgba(255,255,255,0.85)", borderRadius: 12 }}></div>
            {!ready && <div style={{ position: "absolute", inset: 0, display: "grid", placeItems: "center", color: "#fff", fontSize: 13 }}>กำลังเปิดกล้อง…</div>}
          </div>
        }
        <div style={{ fontSize: 12, color: "var(--ink-3)", marginTop: 10, textAlign: "center" }}>เล็งกล้องไปที่บาร์โค้ดหรือ QR — ระบบอ่านอัตโนมัติ</div>
        <div style={{ display: "flex", gap: 8, marginTop: 12 }}>
          <input className="input" placeholder="หรือพิมพ์รหัสเอง…" value={manual} onChange={e => setManual(e.target.value)} onKeyDown={e => e.key === "Enter" && submitManual()}></input>
          <Btn kind="primary" onClick={submitManual}>ใช้รหัส</Btn>
        </div>
      </div>
    </>);

}

// ============ Barcode scan + print ============
function Barcode({ navigate }) {
  const D = window.SSSData;
  const { Btn, Card, PageHead, BarcodeVisual, fmt0 } = window.UI;
  const [scanned, setScanned] = useStateI(null);
  const [sku, setSku] = useStateI(D.Products.find(p => p.barcode !== "—")?.sku || "");
  const [count, setCount] = useStateI(20);
  const [orient, setOrient] = useStateI("landscape"); // landscape = 40×30 (แนวนอน) · portrait = 30×40 (แนวตั้ง)
  const [code, setCode] = useStateI("");
  const [scanning, setScanning] = useStateI(false);

  const selected = D.Products.find(p => p.sku === sku);
  const land = orient === "landscape"; // ป้ายแนวนอน 40×30

  const onScan = () => {
    const p = D.Products.find(p => p.barcode === code || p.sku === code);
    if (p) { setScanned(p); window.toast && window.toast("สแกนสำเร็จ: " + p.sku); }
    else window.toast && window.toast("ไม่พบสินค้า");
    setCode("");
  };

  // กล้องอ่านรหัสได้ → ค้นหาสินค้า
  const handleDetected = (text) => {
    setScanning(false);
    const t = String(text || "").trim();
    setCode(t);
    const p = D.Products.find(p => p.barcode === t || p.sku === t);
    if (p) { setScanned(p); window.toast && window.toast("สแกนสำเร็จ: " + p.name); }
    else window.toast && window.toast(`อ่านรหัสได้: ${t} — ไม่พบในระบบ`);
  };

  const printLabels = () => {
    if (!selected) return;
    // พิมพ์ลงสติกเกอร์ฉลากโดยตรง — 1 ดวงต่อ 1 หน้ากระดาษ (เครื่องพิมพ์ฉลาก/ลาเบล)
    const land = orient === "landscape";
    const PW = land ? 40 : 30, PH = land ? 30 : 40; // มม.
    const labelsHTML = [...Array(count)].map(() => `
      <div class="lbl">
        <div class="lbl-name">${selected.name.slice(0, 30)}</div>
        <div class="lbl-price">฿${selected.price.toLocaleString()}</div>
        <svg viewBox="0 0 100 24" preserveAspectRatio="none" class="lbl-bc">${generateBarcodeSVG(selected.barcode)}</svg>
        <div class="lbl-code">${selected.barcode}</div>
      </div>
    `).join("");
    document.getElementById("__print_modal")?.remove();
    document.body.classList.remove("printing-modal");
    const modal = document.createElement("div");
    modal.id = "__print_modal";
    modal.className = "print-modal";
    modal.innerHTML = `
      <div class="print-modal-bar no-print">
        <div style="display:flex;align-items:center;gap:12px;flex:1">
          <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M6 9V3h12v6"/><rect x="3" y="9" width="18" height="9" rx="2"/><rect x="6" y="14" width="12" height="7"/></svg>
          <span style="font-weight:600;font-size:14px">พิมพ์ป้ายบาร์โค้ด — สติกเกอร์ลาเบล ${PW}×${PH} มม. (${land ? "แนวนอน" : "แนวตั้ง"} · ${count} ดวง · 1 ดวง/แผ่น)</span>
        </div>
        <div style="display:flex;gap:8px">
          <button id="__print_close" class="pm-btn pm-btn-ghost">ปิด (Esc)</button>
          <button id="__print_btn" class="pm-btn pm-btn-primary">🖨 พิมพ์</button>
        </div>
      </div>
      <div class="print-modal-paper" style="padding:0;background:#e7e5e4">
        <style>
          /* บังคับขนาดกระดาษ = สติกเกอร์ ${PW}×${PH} มม. พอดี ไม่มีขอบ */
          @media print { @page { size: ${PW}mm ${PH}mm; margin: 0; } }
          .labels { display: flex; flex-direction: column; align-items: center; gap: 6mm; padding: 6mm 0; }
          .lbl { width: ${PW}mm; height: ${PH}mm; padding: ${land ? "1.6mm 1.5mm" : "1.8mm 1.5mm"}; display: flex; flex-direction: column; align-items: center; justify-content: space-between; box-sizing: border-box; color: #0c0a09; background: white; border: 0.4pt dashed #d6d3d1; }
          .lbl-name { font-size: ${land ? "6.5pt" : "7pt"}; font-weight: 500; text-align: center; line-height: 1.1; max-height: ${land ? "7mm" : "9mm"}; overflow: hidden; }
          .lbl-price { font-size: ${land ? "12pt" : "12pt"}; font-weight: 700; margin: 0; }
          .lbl-bc { width: ${land ? "36mm" : "26mm"}; height: ${land ? "9mm" : "8mm"}; display: block; }
          .lbl-code { font-family: monospace; font-size: ${land ? "6pt" : "6pt"}; letter-spacing: 0.5px; }
          @media print {
            .labels { display: block; gap: 0; padding: 0; }
            .lbl { border: 0; page-break-after: always; break-after: page; }
            .lbl:last-child { page-break-after: avoid; break-after: avoid; }
          }
        </style>
        <div class="labels">${labelsHTML}</div>
      </div>
    `;
    document.body.appendChild(modal);
    document.body.classList.add("printing-modal");
    const cleanup = () => { modal.remove(); document.body.classList.remove("printing-modal"); document.removeEventListener("keydown", onKey); };
    const onKey = (e) => { if (e.key === "Escape") cleanup(); };
    document.addEventListener("keydown", onKey);
    modal.addEventListener("click", e => { if (e.target === modal) cleanup(); });
    document.getElementById("__print_btn").addEventListener("click", () => window.print());
    document.getElementById("__print_close").addEventListener("click", cleanup);
  };

  return (
    <>
      <PageHead title="บาร์โค้ด · Barcode" sub="สแกนเพื่อค้นหา/นับสต็อก · พิมพ์ป้ายติดสินค้า"/>

      <div className="grid" style={{gridTemplateColumns: "1fr 1fr", gap: 14}}>
        <Card title="สแกนบาร์โค้ด" sub="ใช้กล้องโทรศัพท์/เครื่องสแกน หรือพิมพ์รหัส">
          <div style={{padding: 20, background: "var(--surface-2)", borderRadius: 10, border: "1.5px dashed var(--line-strong)", textAlign: "center", marginBottom: 12}}>
            <I.qr size={80} stroke="var(--ink-3)"/>
            <div style={{marginTop: 8, fontSize: 13, color: "var(--ink-3)"}}>เปิดกล้องเพื่อสแกน หรือพิมพ์รหัสด้านล่าง</div>
            <Btn icon={I.qr} kind="primary" size="sm" style={{marginTop: 10}} onClick={() => setScanning(true)}>เปิดกล้องสแกน</Btn>
          </div>
          <div style={{display: "flex", gap: 8}}>
            <input className="input" placeholder="พิมพ์ / Scan barcode (เช่น 8851000010012)" value={code} onChange={e=>setCode(e.target.value)} onKeyDown={e=>e.key==='Enter'&&onScan()}/>
            <Btn kind="primary" onClick={onScan}>ค้นหา</Btn>
          </div>
          <div style={{marginTop: 12}}>
            <div className="label mb">บาร์โค้ดตัวอย่าง (คลิกเพื่อทดลอง)</div>
            <div style={{display: "flex", gap: 6, flexWrap: "wrap"}}>
              {D.Products.filter(p => p.barcode !== "—").slice(0, 5).map(p => (
                <button key={p.sku} className="btn sm ghost" style={{fontFamily: "var(--font-mono)", fontSize: 11}}
                  onClick={() => { setCode(p.barcode); setScanned(p); }}>{p.barcode}</button>
              ))}
            </div>
          </div>

          {scanned && (
            <div style={{marginTop: 14, padding: 14, background: "var(--success-bg)", borderRadius: 8, border: "1px solid var(--success)"}}>
              <div style={{display: "flex", justifyContent: "space-between", alignItems: "flex-start"}}>
                <div>
                  <div style={{fontSize: 12, color: "var(--success)", fontWeight: 600, marginBottom: 2}}>✓ สแกนสำเร็จ</div>
                  <div style={{fontWeight: 600, fontSize: 14}}>{scanned.name}</div>
                  <div className="mono muted" style={{fontSize: 12}}>{scanned.sku} · ตำแหน่ง {scanned.location}</div>
                  <div style={{marginTop: 6, fontSize: 12}}>คงเหลือ <b>{scanned.stock} {scanned.unit}</b> · ราคา ฿{fmt0(scanned.price)}</div>
                </div>
                <div style={{display: "flex", flexDirection: "column", gap: 4}}>
                  <Btn size="sm">+ เพิ่มเข้า</Btn>
                  <Btn size="sm">− เบิกออก</Btn>
                </div>
              </div>
            </div>
          )}
        </Card>

        <Card title="พิมพ์ป้ายบาร์โค้ด" sub="ขนาดมาตรฐาน 30×40 มม. (สำหรับสติกเกอร์หรือเครื่องพิมพ์ฉลาก)">
          <div className="field mb"><label>เลือกสินค้า</label>
            <select className="input" value={sku} onChange={e=>setSku(e.target.value)}>
              {D.Products.filter(p => p.barcode !== "—").map(p => <option key={p.sku} value={p.sku}>{p.sku} — {p.name}</option>)}
            </select>
          </div>
          <div className="field-row cols-2 mb">
            <div className="field"><label>จำนวนป้าย</label><input className="input" type="number" min="1" value={count} onChange={e=>setCount(Math.max(1, +e.target.value || 1))}/></div>
            <div className="field"><label>แนววางป้าย / ขนาด</label>
              <select className="input" value={orient} onChange={e=>setOrient(e.target.value)}>
                <option value="landscape">แนวนอน · 40 × 30 มม.</option>
                <option value="portrait">แนวตั้ง · 30 × 40 มม.</option>
              </select>
            </div>
          </div>
          <div className="label mb">ตัวอย่างป้าย (พิมพ์ตามจำนวนที่ระบุ)</div>
          <div style={{background: "white", padding: 14, borderRadius: 8, border: "1px solid var(--line)", display: "flex", flexWrap: "wrap", gap: 10}}>
            {selected && [...Array(Math.min(count, 6))].map((_, i) => (
              <div key={i} style={{
                width: land ? 151 : 113, height: land ? 113 : 151,
                padding: land ? "7px 6px" : 6,
                border: "1px dashed #d6d3d1",
                borderRadius: 3,
                background: "white",
                color: "#0c0a09",
                display: "flex",
                flexDirection: "column",
                alignItems: "center",
                justifyContent: "space-between",
                textAlign: "center"
              }}>
                <div style={{fontSize: land ? 8.5 : 9, fontWeight: 500, lineHeight: 1.1, maxHeight: land ? 24 : 30, overflow: "hidden"}}>{selected.name.slice(0, 28)}</div>
                <div style={{fontSize: land ? 16 : 16, fontWeight: 700}}>฿{fmt0(selected.price)}</div>
                <BarcodeVisual code={selected.barcode} width={land ? 138 : 96} height={land ? 34 : 28}/>
              </div>
            ))}
          </div>
          <div style={{marginTop: 8, fontSize: 11, color: "var(--ink-3)"}}>แสดง {Math.min(count, 6)} ป้ายแรก · พิมพ์จริง {count} ดวง — 1 ดวงต่อ 1 สติกเกอร์ลาเบล {land ? "40 × 30 มม. (แนวนอน)" : "30 × 40 มม. (แนวตั้ง)"} · เครื่องพิมพ์ฉลาก</div>
          <div style={{marginTop: 12, display: "flex", gap: 8, justifyContent: "flex-end"}}>
            <Btn icon={I.print} kind="primary" onClick={printLabels}>พิมพ์ {count} ป้าย</Btn>
          </div>
        </Card>
      </div>
      {scanning && <BarcodeScanModal onClose={() => setScanning(false)} onDetected={handleDetected} />}
    </>
  );
}

// Helper: barcode SVG bars (deterministic from code)
function generateBarcodeSVG(code) {
  if (!code) return "";
  let seed = 0;
  for (let i = 0; i < code.length; i++) seed += code.charCodeAt(i);
  let r = seed, x = 0, i = 0;
  const bars = [];
  // เติมแท่งบาร์โค้ดให้เต็มความกว้าง viewBox (0–100) — บาร์โค้ดจึงอยู่กึ่งกลางป้ายพอดี
  while (x < 100) {
    r = (r * 9301 + 49297) % 233280;
    const w = (r % 4 + 1) * 0.7;
    if (i % 2 === 0) {
      const ww = Math.min(w, 100 - x);
      bars.push(`<rect x="${x.toFixed(2)}" y="0" width="${ww.toFixed(2)}" height="24" fill="#0c0a09"/>`);
    }
    x += w + 0.5;
    i++;
  }
  return bars.join("");
}

// ============ เบิกสินค้า (เมนูแยก + ผูกโปรเจกต์ + ผู้เบิก + เบิกบางส่วน %) ============
// เขียนตรงไปที่ D.Products / InventoryMoves แล้วยิง event ให้หน้าคลังรีเฟรชเอง
function issueStockApply(sku, delta, meta) {
  meta = meta || {};
  const D = window.SSSData;
  const product = (D.Products || []).find(p => p.sku === sku);
  if (!product) { window.toast && window.toast("ไม่พบสินค้าในคลัง"); return; }
  product.stock = Math.max(0, Math.round(((Number(product.stock) || 0) + delta) * 100) / 100);
  if (meta.byPercent) product.byPercent = true;
  const now = new Date().toLocaleString("th-TH", { day: "2-digit", month: "2-digit", year: "2-digit", hour: "2-digit", minute: "2-digit" });
  D.InventoryMoves = D.InventoryMoves || [];
  D.InventoryMoves.unshift({
    id: "MV-" + Date.now() + "-" + Math.random().toString(36).slice(2, 6),
    date: now,
    type: delta > 0 ? "รับเข้า" : "เบิกใช้งาน",
    sku, name: product.name, qty: delta,
    ref: meta.projectCode || meta.reason || "ADJ-OUT",
    by: meta.by || (window.actorName ? window.actorName() : "")
  });
  // แยกเศษที่เหลือเป็นสินค้าใหม่ "(ถูกแบ่ง)" — ไว้ใช้ครั้งต่อไป
  if (delta < 0 && meta.offcut && meta.offcut.remainFrac > 0) {
    const remainPct = Math.round(meta.offcut.remainFrac * 100);
    const seq = (D.Products || []).filter(p => p.parentSku === sku).length + 1;
    const offProd = {
      sku: sku + "-C" + seq,
      name: product.name + " (ถูกแบ่ง ~" + remainPct + "%)",
      category: product.category, unit: product.unit,
      cost: Math.round((product.cost || 0) * meta.offcut.remainFrac),
      price: Math.round((product.price || 0) * meta.offcut.remainFrac),
      stock: 1, min: 0, max: 0, location: product.location || "",
      barcode: "", photo: product.photo || null,
      offcut: true, parentSku: sku, fraction: meta.offcut.remainFrac, byPercent: true
    };
    D.Products.push(offProd);
    D.InventoryMoves.unshift({ date: now, type: "รับเข้า", sku: offProd.sku, name: offProd.name, qty: 1, ref: "แบ่งจาก " + sku, by: meta.by || "" });
  }
  // ผูกการเบิกเข้ากับโปรเจกต์ → ลงเป็นค่าใช้จ่ายวัสดุของงานนั้น (โชว์ในต้นทุนงานทันที)
  if (delta < 0 && meta.projectCode && window.ProjX) {
    const PX = window.ProjX;
    const used = meta.chargeUsed != null ? meta.chargeUsed : Math.round(Math.abs(delta) * 100) / 100;
    const amount = Math.round((product.cost || 0) * used);
    const exp = { date: PX.pxToday(), category: "ค่าวัสดุ/วัตถุดิบ", vendor: product.name, amount, status: "เบิกแล้ว", sku, qty: meta.rawQty || used, percent: meta.percent || 100, byPercent: !!meta.byPercent, used, unit: product.unit, by: meta.by || "" };
    const cur = PX.pxGet(meta.projectCode).expenses || [];
    PX.pxSet(meta.projectCode, { expenses: [...cur, exp] });
  }
  window.dispatchEvent(new Event("sss-products-changed"));
  window.dispatchEvent(new Event("sss-stock-changed"));
  window.logActivity && window.logActivity("เบิกสินค้า", `${product.name} ${Math.abs(delta)} ${product.unit || ""}${meta.projectCode ? " → " + meta.projectCode : ""} โดย ${meta.by || ""}`, "inventory");
  window.toast && window.toast(`เบิก ${product.name} แล้ว${meta.offcut ? " · แยกเศษเป็นสินค้าใหม่" : ""}${meta.projectCode ? " · ผูกกับ " + meta.projectCode : ""}`);
}

// แปลงวันที่ในรายการเคลื่อนไหว (th "DD/MM/YY ...") → Date เทียบช่วงได้
function issueMoveDate(m) {
  const dp = (m.date || "").split(" ")[0];
  const [dd, mm, yy] = dp.split("/").map(Number);
  if (!dd || !mm) return null;
  const year = (yy < 100 ? 2500 + yy : yy) - 543;
  return new Date(year, mm - 1, dd);
}
const issueKeyOf = (m) => m.id || ((m.date || "") + "|" + m.sku + "|" + m.qty);

// พิมพ์รายงานการเบิกสินค้า
function printStockReport(rows, title) {
  const fmt0 = window.UI.fmt0;
  const C = (window.SSSData && window.SSSData.Company) || {};
  const total = rows.reduce((s, m) => s + Math.abs(m.qty || 0), 0);
  const body = rows.map((m, i) => `<tr>
    <td style="border:1px solid #ddd;padding:5px 8px">${i + 1}</td>
    <td style="border:1px solid #ddd;padding:5px 8px">${m.date || ""}</td>
    <td style="border:1px solid #ddd;padding:5px 8px">${(m.name || "").replace(/</g, "&lt;")} <span style="color:#888">· ${m.sku || ""}</span></td>
    <td style="border:1px solid #ddd;padding:5px 8px;text-align:right">${Math.abs(m.qty || 0)}</td>
    <td style="border:1px solid #ddd;padding:5px 8px">${(m.by || "—").replace(/</g, "&lt;")}</td>
    <td style="border:1px solid #ddd;padding:5px 8px">${(m.ref || "—").replace(/</g, "&lt;")}</td>
  </tr>`).join("");
  const html = `<div style="background:white;color:#0c0a09;padding:28px 32px;font-family:'Bai Jamjuree',sans-serif">
    <div style="display:flex;justify-content:space-between;align-items:flex-start;border-bottom:2px solid #18181b;padding-bottom:10px;margin-bottom:14px">
      <div><div style="font-weight:700;font-size:16px">${C.name || "SSS"}</div><div style="font-size:10.5px;color:#666;max-width:340px">${C.address || ""}</div></div>
      <div style="text-align:right"><div style="font-weight:700;font-size:15px">รายงานการเบิกสินค้า</div><div style="font-size:11px;color:#666">${title}</div></div>
    </div>
    <table style="width:100%;border-collapse:collapse;font-size:12px">
      <thead><tr style="background:#f3f4f6">
        <th style="border:1px solid #ddd;padding:6px 8px;text-align:left;width:34px">#</th>
        <th style="border:1px solid #ddd;padding:6px 8px;text-align:left">วันที่/เวลา</th>
        <th style="border:1px solid #ddd;padding:6px 8px;text-align:left">สินค้า</th>
        <th style="border:1px solid #ddd;padding:6px 8px;text-align:right">จำนวน</th>
        <th style="border:1px solid #ddd;padding:6px 8px;text-align:left">ผู้เบิก</th>
        <th style="border:1px solid #ddd;padding:6px 8px;text-align:left">ผูกกับ</th>
      </tr></thead>
      <tbody>${body || `<tr><td colspan="6" style="border:1px solid #ddd;padding:16px;text-align:center;color:#999">ไม่มีรายการในช่วงนี้</td></tr>`}</tbody>
    </table>
    <div style="margin-top:10px;font-size:12.5px;text-align:right;font-weight:600">รวม ${rows.length} รายการ · จำนวนเบิกรวม ${fmt0(total)}</div>
    <div style="margin-top:22px;font-size:10.5px;color:#888;border-top:1px solid #ddd;padding-top:6px">พิมพ์เมื่อ ${new Date().toLocaleString("th-TH")} · ออกโดยระบบ SSS</div>
  </div>`;
  document.getElementById("__print_modal")?.remove();
  const modal = document.createElement("div");
  modal.id = "__print_modal";
  modal.className = "print-modal";
  modal.innerHTML = `<div class="print-modal-bar no-print"><div style="font-weight:600;font-size:14px;flex:1">รายงานการเบิก · ${title}</div><button id="__pr_close" class="pm-btn pm-btn-ghost">ปิด (Esc)</button><button id="__pr_print" class="pm-btn pm-btn-primary">🖨 พิมพ์ / บันทึก PDF</button></div><div class="print-modal-paper">${html}</div>`;
  document.body.appendChild(modal);
  document.body.classList.add("printing-modal");
  const cleanup = () => { modal.remove(); document.body.classList.remove("printing-modal"); document.removeEventListener("keydown", onKey); };
  const onKey = (e) => { if (e.key === "Escape") cleanup(); };
  document.addEventListener("keydown", onKey);
  modal.addEventListener("click", e => { if (e.target === modal) cleanup(); });
  document.getElementById("__pr_print").addEventListener("click", () => window.print());
  document.getElementById("__pr_close").addEventListener("click", cleanup);
}

function StockIssueEditModal({ move, onClose, onSave }) {
  const { Btn } = window.UI;
  const [qty, setQty] = useStateI(Math.abs(move.qty || 0));
  const [by, setBy] = useStateI(move.by || "");
  const [ref, setRef] = useStateI(move.ref || "");
  const prod = (window.SSSData.Products || []).find(p => p.sku === move.sku);
  return (
    <>
      <div className="scrim" style={{zIndex: 80}} onClick={onClose}/>
      <div style={{position: "fixed", left: "50%", top: "50%", transform: "translate(-50%, -50%)", zIndex: 81, background: "var(--surface)", border: "1px solid var(--line)", borderRadius: 12, padding: 18, width: 400, maxWidth: "94vw", boxShadow: "var(--shadow-lg)"}}>
        <div style={{fontSize: 14.5, fontWeight: 700, marginBottom: 4}}>แก้ไขรายการเบิก</div>
        <div style={{fontSize: 12.5, color: "var(--ink-3)", marginBottom: 12}}>{move.name} · {move.sku}{prod ? ` · คงเหลือ ${prod.stock} ${prod.unit || ""}` : ""}</div>
        <div className="field mb"><label>จำนวนที่เบิก ({prod ? prod.unit || "หน่วย" : "หน่วย"})</label><input className="input" type="number" min="0" step="any" value={qty} onChange={e => setQty(e.target.value)} autoFocus/></div>
        <div className="field mb"><label>ผู้เบิก</label><input className="input" value={by} onChange={e => setBy(e.target.value)}/></div>
        <div className="field mb"><label>ผูกกับ (โปรเจกต์/หมายเหตุ)</label><input className="input mono" value={ref} onChange={e => setRef(e.target.value)}/></div>
        <div style={{display: "flex", gap: 8, justifyContent: "flex-end"}}>
          <Btn kind="ghost" onClick={onClose}>ยกเลิก</Btn>
          <Btn kind="primary" icon={I.check} onClick={() => onSave({ qty: Math.max(0, Number(qty) || 0), by: by.trim(), ref: ref.trim() })}>บันทึก</Btn>
        </div>
      </div>
    </>
  );
}

function StockIssuePage({ navigate }) {
  const D = window.SSSData;
  const { Btn, Badge, Card, PageHead, Stat, fmt0 } = window.UI;
  const [open, setOpen] = useStateI(false);
  const [editing, setEditing] = useStateI(null);
  const [sel, setSel] = useStateI([]);
  const allMoves = () => (D.InventoryMoves || []).filter(m => m.type === "เบิกใช้งาน");
  const [moves, setMoves] = useStateI(allMoves);
  React.useEffect(() => {
    const h = () => setMoves(allMoves());
    window.addEventListener("sss-products-changed", h);
    window.addEventListener("sss-stock-changed", h);
    window.addEventListener("sss-data-synced", h);
    return () => { window.removeEventListener("sss-products-changed", h); window.removeEventListener("sss-stock-changed", h); window.removeEventListener("sss-data-synced", h); };
  });
  const canIssue = !window.Perms || ["สร้าง", "ทั้งหมด"].includes(window.Perms.level("stock-issue")) || !!(window.Session && window.Session.get() && window.Session.get().isAdmin);
  const now = new Date();
  const todayCount = moves.filter(m => { const d = issueMoveDate(m); return d && d.toDateString() === now.toDateString(); }).length;
  const linkedCount = moves.filter(m => /^JOB-/.test(m.ref || "")).length;

  const toggleSel = (k) => setSel(s => s.includes(k) ? s.filter(x => x !== k) : [...s, k]);

  const removeMove = async (m) => {
    const prod = (D.Products || []).find(p => p.sku === m.sku);
    const ok = await (window.confirmDelete ? window.confirmDelete(`ลบรายการเบิก ${m.name}`, `${Math.abs(m.qty)} ${prod ? prod.unit || "" : ""}`) : Promise.resolve(confirm("ลบ?")));
    if (!ok) return;
    if (prod) prod.stock = Math.round(((Number(prod.stock) || 0) + Math.abs(m.qty || 0)) * 100) / 100;   // คืนสต็อก
    window.markDeleted && window.markDeleted("InventoryMoves", m.id || JSON.stringify(m));   // กัน poll ดึงกลับมาก่อน save
    D.InventoryMoves = (D.InventoryMoves || []).filter(x => m.id ? x.id !== m.id : x !== m);
    window.dispatchEvent(new Event("sss-products-changed"));
    window.dispatchEvent(new Event("sss-stock-changed"));
    window.logActivity && window.logActivity("ลบรายการเบิก", `${m.name} ${Math.abs(m.qty)} (คืนสต็อก)`, "inventory");
    window.toast && window.toast("ลบรายการเบิกแล้ว — คืนสต็อกให้อัตโนมัติ");
  };
  const saveEdit = (patch) => {
    const m = editing;
    const prod = (D.Products || []).find(p => p.sku === m.sku);
    const oldAbs = Math.abs(m.qty || 0);
    if (prod) prod.stock = Math.max(0, Math.round(((Number(prod.stock) || 0) + oldAbs - patch.qty) * 100) / 100);   // ปรับสต็อกตามส่วนต่าง
    D.InventoryMoves = (D.InventoryMoves || []).map(x => (m.id ? x.id === m.id : x === m) ? { ...x, qty: -patch.qty, by: patch.by, ref: patch.ref } : x);
    window.dispatchEvent(new Event("sss-products-changed"));
    window.dispatchEvent(new Event("sss-stock-changed"));
    setEditing(null);
    window.toast && window.toast("บันทึกการแก้ไขแล้ว — ปรับสต็อกให้อัตโนมัติ");
  };

  const printScope = (scope) => {
    let rows = moves, title = "";
    if (scope === "today") { rows = moves.filter(m => { const d = issueMoveDate(m); return d && d.toDateString() === now.toDateString(); }); title = "วันนี้ · " + now.toLocaleDateString("th-TH"); }
    else if (scope === "week") {
      const mon = new Date(now); mon.setDate(now.getDate() - ((now.getDay() + 6) % 7)); mon.setHours(0, 0, 0, 0);
      const sun = new Date(mon); sun.setDate(mon.getDate() + 6); sun.setHours(23, 59, 59, 999);
      rows = moves.filter(m => { const d = issueMoveDate(m); return d && d >= mon && d <= sun; }); title = "สัปดาห์นี้";
    } else if (scope === "month") {
      rows = moves.filter(m => { const d = issueMoveDate(m); return d && d.getMonth() === now.getMonth() && d.getFullYear() === now.getFullYear(); });
      title = "เดือน " + now.toLocaleDateString("th-TH", { month: "long", year: "numeric" });
    } else if (scope === "selected") {
      if (sel.length === 0) { window.toast && window.toast("ยังไม่ได้ติ๊กเลือกรายการ"); return; }
      rows = moves.filter(m => sel.includes(issueKeyOf(m))); title = `ที่เลือก (${sel.length} รายการ)`;
    } else { title = "ทั้งหมด"; }
    printStockReport(rows, title);
  };

  return (
    <>
      <PageHead title="เบิกสินค้า · Stock Issue"
        sub="เบิกวัสดุ/สินค้าออกจากคลัง — ระบุผู้เบิก ผูกกับโปรเจกต์ และเบิกบางส่วนเป็น %"
        right={canIssue ? <Btn icon={I.arrowDown} kind="primary" onClick={() => setOpen(true)}>เบิกสินค้า</Btn> : null}/>

      <div className="stats-row" style={{display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(180px, 1fr))", gap: 12, marginBottom: 16}}>
        <Stat label="เบิกทั้งหมด" value={moves.length} sub="รายการเบิกใช้งานสะสม" icon={I.arrowDown}/>
        <Stat label="เบิกวันนี้" value={todayCount} sub="รายการเบิกของวันนี้" icon={I.move}/>
        <Stat label="ผูกกับโปรเจกต์" value={linkedCount} sub="เบิกเข้างาน (Job)" icon={I.cube3d}/>
      </div>

      {/* พิมพ์รายงาน */}
      <div style={{display: "flex", flexWrap: "wrap", gap: 8, alignItems: "center", marginBottom: 14, padding: "10px 12px", background: "var(--surface-2)", border: "1px solid var(--line)", borderRadius: 10}}>
        <span style={{fontSize: 12.5, fontWeight: 600, marginRight: 4}}><I.print size={13}/> พิมพ์รายงานการเบิก:</span>
        <Btn size="sm" kind="ghost" onClick={() => printScope("today")}>วันนี้</Btn>
        <Btn size="sm" kind="ghost" onClick={() => printScope("week")}>สัปดาห์นี้</Btn>
        <Btn size="sm" kind="ghost" onClick={() => printScope("month")}>เดือนนี้</Btn>
        <Btn size="sm" kind="ghost" onClick={() => printScope("all")}>ทั้งหมด</Btn>
        <Btn size="sm" kind="primary" icon={I.print} onClick={() => printScope("selected")}>ที่ติ๊กเลือก ({sel.length})</Btn>
      </div>

      <Card title="ประวัติการเบิกล่าสุด" sub={`${moves.length} รายการ`} flush>
        {moves.length === 0 ? (
          <div style={{padding: 18, textAlign: "center", color: "var(--ink-3)", fontSize: 12.5}}>ยังไม่มีการเบิกสินค้า — กดปุ่ม "เบิกสินค้า" เพื่อเริ่ม</div>
        ) : (
          <table className="t">
            <thead><tr><th style={{width: 30}}></th><th>วันที่/เวลา</th><th>สินค้า</th><th className="right">จำนวน</th><th>ผู้เบิก</th><th>ผูกกับ</th><th></th></tr></thead>
            <tbody>
              {moves.slice(0, 200).map((m) => { const k = issueKeyOf(m); return (
                <tr key={k}>
                  <td><input type="checkbox" checked={sel.includes(k)} onChange={() => toggleSel(k)} style={{width: 16, height: 16, accentColor: "var(--accent)"}}/></td>
                  <td className="muted" style={{fontSize: 12}}>{m.date}</td>
                  <td>{m.name}<span className="mono muted" style={{fontSize: 11}}> · {m.sku}</span></td>
                  <td className="right amount">{Math.abs(m.qty)}</td>
                  <td>{m.by || "—"}</td>
                  <td>{/^JOB-/.test(m.ref || "") ? <Badge tone="info">{m.ref}</Badge> : <span className="muted" style={{fontSize: 12}}>{m.ref || "—"}</span>}</td>
                  <td>
                    {canIssue && <div style={{display: "flex", gap: 2}}>
                      <button className="icon-btn" title="แก้ไข" onClick={() => setEditing(m)}><I.edit size={13}/></button>
                      <button className="icon-btn" title="ลบ + คืนสต็อก" onClick={() => removeMove(m)}><I.trash size={13}/></button>
                    </div>}
                  </td>
                </tr>
              ); })}
            </tbody>
          </table>
        )}
      </Card>

      {open && <StockAdjustModal target={{product: null, mode: "out"}} onClose={() => setOpen(false)}
        onApply={(delta, meta) => { issueStockApply(meta.sku, delta, meta); setOpen(false); }}/>}
      {editing && <StockIssueEditModal move={editing} onClose={() => setEditing(null)} onSave={saveEdit}/>}
    </>
  );
}

window.CameraCaptureModal = CameraCaptureModal;
window.InventoryProducts = InventoryProducts;
window.StockMovements = StockMovements;
window.Barcode = Barcode;
window.StockIssuePage = StockIssuePage;
