/* global React, I, UI, SSSData */
// =============== ลงเวลา QR · QR Time Clock ===============
// พนักงาน login แล้วลงเวลาได้เฉพาะของตัวเอง · QR เปลี่ยนทุกวัน · เข้างาน-พักเที่ยง-เลิกพัก-ออกงาน
// ขอลา (เช้า/บ่าย/ทั้งวัน) + แจ้งเข้าสาย (ก่อน 08:00 เท่านั้น) · ไม่ลงเวลา = ขาดงาน
// เชื่อมกับคำนวนเงินเดือนแบบรายวัน (เลือกช่วงวันที่แล้วดึงข้อมูล)
const { useState: useStateQC, useEffect: useEffectQC, useRef: useRefQC } = React;

const QC_TOKEN_KEY = "sss-qr-token";
const QC_CLOCK_KEY = "sss-time-clock";
const QC_RULES_KEY = "sss-work-rules";
const QC_REQ_KEY = "sss-time-requests";
const QC_MONTHS = ["ม.ค.", "ก.พ.", "มี.ค.", "เม.ย.", "พ.ค.", "มิ.ย.", "ก.ค.", "ส.ค.", "ก.ย.", "ต.ค.", "พ.ย.", "ธ.ค."];
const QC_WORK_START = "08:00";
function qcTodayISO() {return new Date().toISOString().slice(0, 10);}
function qcThaiOfISO(iso) {const [y, m, d] = String(iso).split("-").map(Number);return `${d} ${QC_MONTHS[m - 1]} ${y + 543}`;}
function qcTodayTh() {return qcThaiOfISO(qcTodayISO());}
function qcTimeNow() {const d = new Date();return `${String(d.getHours()).padStart(2, "0")}:${String(d.getMinutes()).padStart(2, "0")}`;}
function qcToMin(t) {if (!t) return 0;const [h, m] = String(t).split(":").map(Number);return h * 60 + m;}

function qcTokenLoad() {
  const d = window.SSSData;
  if (d && d.QRToken && d.QRToken.date) return d.QRToken;
  try {return JSON.parse(localStorage.getItem(QC_TOKEN_KEY) || "null");} catch {return null;}
}
function qcTokenSave(t) {
  const d = window.SSSData;
  if (d) { if (!d.QRToken) d.QRToken = {}; Object.assign(d.QRToken, t); }
  try {localStorage.setItem(QC_TOKEN_KEY, JSON.stringify(t));} catch {}
  window.dispatchEvent(new Event("sss-qr-updated"));
}
function qcClockLoad() {
  const d = window.SSSData;
  if (d && Array.isArray(d.QRClockRecords)) return d.QRClockRecords;
  try {return JSON.parse(localStorage.getItem(QC_CLOCK_KEY) || "[]");} catch {return [];}
}
function qcClockSave(list) {
  const d = window.SSSData;
  if (d) { d.QRClockRecords = list; }
  try {localStorage.setItem(QC_CLOCK_KEY, JSON.stringify(list));} catch {}
  window.dispatchEvent(new Event("sss-clock-saved"));
}
function qcReqLoad() {
  const d = window.SSSData;
  if (d && Array.isArray(d.TimeRequests)) return d.TimeRequests;
  try {return JSON.parse(localStorage.getItem(QC_REQ_KEY) || "[]");} catch {return [];}
}
function qcReqSave(list) {
  const d = window.SSSData;
  if (d) { d.TimeRequests = list; }
  try {localStorage.setItem(QC_REQ_KEY, JSON.stringify(list));} catch {}
  window.dispatchEvent(new Event("sss-req-saved"));
}
const QC_DEFAULT_RULES = `1. เวลาทำงาน 08:00–17:00 น. · พักกลางวัน 12:00–13:00 น.
2. สแกน QR ลงเวลา 4 ครั้ง: เข้างาน → พักเที่ยง → เลิกพัก → ออกงาน
3. เข้างานหลัง 08:00 ถือว่ามาสาย หักตามนาที — แจ้งขอเข้าสายได้ก่อน 08:00 เท่านั้น
4. ขอลาผ่านปุ่ม "ขอลา" (ลาเช้า/ลาบ่าย/ทั้งวัน) — ขอลาวันนี้ทำได้ก่อน 08:00 เท่านั้น เลยเวลานี้เลือกได้ตั้งแต่วันถัดไป · รอหัวหน้าอนุมัติ
5. ไม่ลงเวลาและไม่มีใบลา = ขาดงาน มีผลต่อเงินเดือนและ KPI`;
function qcRulesLoad() {try {return localStorage.getItem(QC_RULES_KEY) || QC_DEFAULT_RULES;} catch {return QC_DEFAULT_RULES;}}

const QC_CLOCK_TYPES = ["เข้างาน", "พักเที่ยง", "เลิกพัก", "ออกงาน"];
const QC_TYPE_EMOJI = { "เข้างาน": "🌅", "พักเที่ยง": "🍚", "เลิกพัก": "💪", "ออกงาน": "🌇" };

// สร้างรูป QR เป็น dataURL (qrcode-generator)
function qcQRDataUrl(text, cell) {
  try {
    if (!window.qrcode) return null;
    const q = window.qrcode(0, "M");
    q.addData(text);
    q.make();
    return q.createDataURL(cell || 8, 4);
  } catch (e) {return null;}
}

// ---------- พิมพ์ QR ประจำวัน ----------
function qcPrintQR(token, dataUrl) {
  document.getElementById("__print_modal")?.remove();
  document.body.classList.remove("printing-modal");
  const C = window.SSSData.Company;
  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">QR ลงเวลาประจำวัน · ${qcTodayTh()}</div>
      <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 class="print-modal-paper">
      <div style="background:white;color:#0c0a09;padding:40px;text-align:center;min-height:80vh;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:14px">
        <div style="font-weight:700;font-size:20px">${C.name}</div>
        <div style="font-size:26px;font-weight:800;color:#1d4ed8">สแกนลงเวลา เข้า–ออกงาน</div>
        <div style="font-size:15px;color:#57534e">ประจำวันที่ <b>${qcTodayTh()}</b> · QR ใช้ได้เฉพาะวันนี้เท่านั้น</div>
        ${dataUrl ? `<img src="${dataUrl}" style="width:340px;height:340px;border:6px solid #0c0a09;border-radius:14px;padding:10px;background:white"/>` : `<div style="font-family:monospace;font-size:18px;border:2px dashed #57534e;padding:30px">${token.token}</div>`}
        <div style="font-size:14px;color:#44403c;max-width:430px;line-height:1.7">เข้าระบบ SSS บนมือถือ → เมนู <b>ลงเวลา QR</b> → เลือก เข้างาน/พักเที่ยง/เลิกพัก/ออกงาน → กด <b>เปิดกล้องสแกน</b> เล็งที่ QR นี้</div>
        <div style="font-size:11px;color:#78716c;font-family:monospace">${token.token}</div>
        <div style="font-size:11px;color:#78716c">สร้างโดย ${token.createdBy} · ${token.createdAt} น.</div>
      </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);
}

// ---------- กล้องสแกน QR (jsQR) ----------
function QRScanModal({ onClose, onResult, demoToken }) {
  const { Btn } = window.UI;
  const videoRef = useRefQC(null);
  const canvasRef = useRefQC(null);
  const [err, setErr] = useStateQC(null);

  useEffectQC(() => {
    let stream = null,raf = 0,stopped = false;
    const tick = () => {
      if (stopped) return;
      const v = videoRef.current;
      if (v && v.readyState === 4 && window.jsQR && canvasRef.current) {
        const c = canvasRef.current;
        c.width = v.videoWidth;c.height = v.videoHeight;
        const ctx = c.getContext("2d", { willReadFrequently: true });
        ctx.drawImage(v, 0, 0);
        try {
          const d = ctx.getImageData(0, 0, c.width, c.height);
          const code = window.jsQR(d.data, c.width, c.height);
          if (code && code.data) {stopped = true;onResult(code.data);return;}
        } catch (e) {}
      }
      raf = requestAnimationFrame(tick);
    };
    if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
      navigator.mediaDevices.getUserMedia({ video: { facingMode: "environment" } }).
      then((s) => {
        if (stopped) {s.getTracks().forEach((t) => t.stop());return;}
        stream = s;
        if (videoRef.current) {videoRef.current.srcObject = s;videoRef.current.play();}
        tick();
      }).
      catch(() => setErr("เปิดกล้องไม่ได้ (ไม่ได้อนุญาต/ไม่มีกล้อง) — ใช้ปุ่มจำลองการสแกนด้านล่างได้"));
    } else {
      setErr("เบราว์เซอร์นี้ไม่รองรับกล้อง — ใช้ปุ่มจำลองการสแกนด้านล่าง");
    }
    return () => {stopped = true;cancelAnimationFrame(raf);stream && stream.getTracks().forEach((t) => t.stop());};
  }, []);

  return (
    <div className="scrim" style={{ display: "flex", alignItems: "center", justifyContent: "center", padding: 18, zIndex: 230 }} onClick={onClose}>
      <div style={{ background: "var(--surface)", borderRadius: 16, width: 380, maxWidth: "94vw", overflow: "hidden" }} onClick={(e) => e.stopPropagation()}>
        <div style={{ padding: "13px 16px", borderBottom: "1px solid var(--line)", display: "flex", alignItems: "center" }}>
          <div style={{ flex: 1, fontWeight: 700, fontSize: 14 }}>สแกน QR ลงเวลา</div>
          <button className="icon-btn" onClick={onClose}><I.close size={14} /></button>
        </div>
        <div style={{ position: "relative", background: "#000", aspectRatio: "1", display: "grid", placeItems: "center" }}>
          <video ref={videoRef} muted playsInline style={{ width: "100%", height: "100%", objectFit: "cover" }}></video>
          <canvas ref={canvasRef} style={{ display: "none" }}></canvas>
          <div style={{ position: "absolute", inset: "18%", border: "3px solid rgba(255,255,255,0.85)", borderRadius: 18, boxShadow: "0 0 0 999px rgba(0,0,0,0.35)" }}></div>
          {err && <div style={{ position: "absolute", left: 14, right: 14, top: 14, background: "rgba(0,0,0,0.75)", color: "white", fontSize: 12, padding: "8px 12px", borderRadius: 8, textAlign: "center" }}>{err}</div>}
          <div style={{ position: "absolute", bottom: 12, left: 0, right: 0, textAlign: "center", color: "white", fontSize: 12, textShadow: "0 1px 3px black" }}>เล็งกล้องไปที่ QR ประจำวัน</div>
        </div>
        <div style={{ padding: 12, display: "flex", flexDirection: "column", gap: 8 }}>
          <Btn kind="ghost" icon={I.qr} onClick={() => onResult(demoToken || "DEMO")}>จำลองการสแกนสำเร็จ (เดโม่)</Btn>
        </div>
      </div>
    </div>);

}

// ---------- เปิดให้หน้าเงินเดือนดึงข้อมูลลงเวลารายวันมาใช้ ----------
window.QRTimeData = {
  dayRecord(empId, iso) {
    const clocks = qcClockLoad(), reqs = qcReqLoad();
    const st = qcDayStatus(empId, iso, clocks, reqs);
    if (st.inT) {
      let work, ot = 0;
      if (st.outT) { const tot = Math.max(0, (qcToMin(st.outT) - qcToMin(st.inT) - 60)) / 60; work = Math.round(Math.min(8, tot) * 100) / 100; ot = Math.max(0, Math.round((tot - 8) * 10) / 10); } else work = 8;
      return { found: true, in: st.inT, out: st.outT || "", work, ot, late: st.lateMin, status: ot > 0 ? "OT" : "ทำงาน" };
    }
    if (st.leave) return { found: true, in: "", out: "", work: 0, ot: 0, late: 0, status: st.leave.period === "ลาเช้า" ? "ลาเช้า" : st.leave.period === "ลาบ่าย" ? "ลาบ่าย" : "ลา" };
    return { found: false };
  }
};

// ---------- คำนวณสถานะรายวันของพนักงานหนึ่งคน ----------
function qcDayStatus(empId, iso, clocks, reqs) {
  const my = clocks.filter((c) => c.date === iso && c.empId === empId);
  const t = (type) => {const r = my.filter((c) => c.type === type).sort((a, b) => a.time < b.time ? -1 : 1);return r.length ? type === "ออกงาน" ? r[r.length - 1].time : r[0].time : "";};
  const inT = t("เข้างาน"),brT = t("พักเที่ยง"),rbT = t("เลิกพัก"),outT = t("ออกงาน");
  const leave = reqs.find((r) => r.type === "ขอลา" && r.empId === empId && r.date === iso && r.status === "อนุมัติแล้ว");
  const lateOk = reqs.some((r) => r.type === "แจ้งเข้าสาย" && r.empId === empId && r.date === iso && r.status !== "ปฏิเสธ");
  let status, tone;
  let lateMin = 0;
  if (inT) {
    lateMin = Math.max(0, qcToMin(inT) - qcToMin(QC_WORK_START));
    if (lateOk) lateMin = 0;
    status = lateMin > 0 ? `สาย ${lateMin} นาที` : lateOk && qcToMin(inT) > qcToMin(QC_WORK_START) ? "ปกติ (แจ้งสายล่วงหน้า)" : "ปกติ";
    tone = lateMin > 0 ? "warning" : "success";
  } else if (leave) {
    status = `ลา (${leave.period})`;tone = "info";
  } else {
    status = "ขาดงาน";tone = "danger";
  }
  return { inT, brT, rbT, outT, status, tone, lateMin, leave, lateOk };
}

// =============== หน้า ลงเวลา QR ===============
// ---------- เบิกระหว่างงวด (รอบวันพุธ/เสาร์): คำนวณค่าแรงจันทร์→วันนี้ เบิกได้≤ 80% ปัดลงเป็นหลักสิบ ----------
const QC_ADV_DAY = { 3: "พุธ", 6: "เสาร์" };
function qcShortName(name) {const p = String(name || "").trim().split(/\s+/);return p.length > 1 ? `${p[0]} ${p[1][0]}.` : p[0] || "—";}
function qcWeekWage(empObj) {
  const clocks = qcClockLoad();const reqs = qcReqLoad();
  const hourly = empObj.hourly || (empObj.salary ? Math.round(empObj.salary / 22 / 8) : 0);
  const d = new Date();const day = d.getDay();const diff = day === 0 ? -6 : 1 - day;
  const mon = new Date(d);mon.setDate(d.getDate() + diff);mon.setHours(0, 0, 0, 0);
  let hours = 0;const days = [];
  const cur = new Date(mon);
  while (cur <= d) {
    const iso = cur.toISOString().slice(0, 10);
    const st = qcDayStatus(empObj.id, iso, clocks, reqs);
    if (st.inT) {
      let w = st.outT ? Math.max(0, qcToMin(st.outT) - qcToMin(st.inT) - 60) / 60 : 8;
      w = Math.round(Math.min(8, w) * 100) / 100;
      hours += w;days.push({ iso, date: qcThaiOfISO(iso), in: st.inT, out: st.outT || "—", hours: w, pay: Math.round(w * hourly) });
    }
    cur.setDate(cur.getDate() + 1);
  }
  const wage = Math.round(hours * hourly);
  const maxAdv = Math.floor(wage * 0.8 / 10) * 10; // 80% ปัดลงเป็นหลักสิบ
  return { hourly, hours, days, wage, maxAdv };
}

// ---------- แปลงวันที่ไทย "13 มิ.ย. 2569" → ISO ----------
function qcParseThaiDate(s) {
  const m = String(s || "").trim().match(/^(\d{1,2})\s+(\S+)\s+(\d{4})$/);
  if (!m) return null;
  const monIdx = QC_MONTHS.indexOf(m[2]);
  if (monIdx < 0) return null;
  return `${(+m[3]) - 543}-${String(monIdx + 1).padStart(2, "0")}-${String(+m[1]).padStart(2, "0")}`;
}
// ---------- ประเภทการจ่าย: เงินเดือน (monthly) vs รายชั่วโมง (hourly) ----------
function qcPayType(emp) {
  if (emp && (emp.payType === "monthly" || emp.payType === "hourly")) return emp.payType;
  return emp && emp.salary > 0 && !emp.hourly ? "monthly" : "hourly";
}
// ยอดเบิกของพนักงานในเดือนปัจจุบัน (ใช้กับพนักงานเงินเดือน — ลิมิต = เงินเดือน)
function qcMonthAdvance(empId, advances) {
  const cm = new Date().toISOString().slice(0, 7);
  const period = `${QC_MONTHS[new Date().getMonth()]} ${new Date().getFullYear() + 543}`;
  return (advances || []).filter(a => a.empId === empId).reduce((s, a) => {
    const iso = qcParseThaiDate(a.date);
    const inMonth = iso ? iso.slice(0, 7) === cm : a.period === period;
    return inMonth ? s + (+a.amount || 0) : s;
  }, 0);
}

// ---------- บัญชีค่าแรง+เบิกรายสัปดาห์ของพนักงานคนหนึ่ง (จันทร์→เสาร์) ----------
const QC_WEEK_DOW = ["จ.", "อ.", "พ.", "พฤ.", "ศ.", "ส."];
function qcWeekLedger(empObj, clocks, reqs, advances) {
  const hourly = empObj.hourly || (empObj.salary ? Math.round(empObj.salary / 22 / 8) : 0);
  const monthly = qcPayType(empObj) === "monthly";
  const d = new Date(); const day = d.getDay(); const diff = day === 0 ? -6 : 1 - day;
  const mon = new Date(d); mon.setDate(d.getDate() + diff); mon.setHours(0, 0, 0, 0);
  const todayISO = new Date().toISOString().slice(0, 10);
  const days = [];
  let accrued = 0;
  for (let i = 0; i < 6; i++) {
    const c = new Date(mon); c.setDate(mon.getDate() + i);
    const iso = c.toISOString().slice(0, 10);
    const st = qcDayStatus(empObj.id, iso, clocks, reqs);
    let pay = 0, worked = false, hours = 0;
    if (st.inT) {
      worked = true;
      let w = st.outT ? Math.max(0, qcToMin(st.outT) - qcToMin(st.inT) - 60) / 60 : 8;
      w = Math.round(Math.min(8, w) * 100) / 100;
      hours = w; pay = Math.round(w * hourly); accrued += pay;
    }
    days.push({ iso, dow: QC_WEEK_DOW[i], worked, hours, pay, leave: !!st.leave, advDay: (i === 2 || i === 5), future: iso > todayISO });
  }
  // พนักงานเงินเดือน: เบิกได้ถึงยอดเงินเดือน (หักยอดที่เบิกไปแล้วในเดือนนี้) ไม่ติดลิมิตรายวัน/80%
  if (monthly) {
    const salary = empObj.salary || 0;
    const advTaken = qcMonthAdvance(empObj.id, advances);
    const remaining = Math.max(0, salary - advTaken);
    return { hourly, days, accrued: salary, advTaken, maxAdv: salary, remaining, monISO: days[0].iso, satISO: days[5].iso, monthly: true, salary };
  }
  const advTaken = (advances || []).filter(a => a.empId === empObj.id).reduce((s, a) => {
    const iso = qcParseThaiDate(a.date);
    return (iso && iso >= days[0].iso && iso <= days[5].iso) ? s + (+a.amount || 0) : s;
  }, 0);
  const maxAdv = Math.floor(accrued * 0.8 / 10) * 10;
  const remaining = Math.max(0, maxAdv - advTaken);
  return { hourly, days, accrued, advTaken, maxAdv, remaining, monISO: days[0].iso, satISO: days[5].iso, monthly: false };
}

// ---------- Modal ขอเบิกเงินระหว่างงวด ----------
function QCAdvanceModal({ me, dowName, onClose, onDone }) {
  const { Btn, fmt0 } = window.UI;
  const monthly = qcPayType(me) === "monthly";
  const advances = (() => { try { return JSON.parse(localStorage.getItem("sss-advances") || "[]") || []; } catch { return []; } })();
  const wInfo = qcWeekWage(me);
  const monthAdv = qcMonthAdvance(me.id, advances);
  const salary = me.salary || 0;
  // พนักงานเงินเดือน: ลิมิต = เงินเดือน − ที่เบิกไปแล้วในเดือนนี้ · รายชั่วโมง: 80% ของค่าแรงสัปดาห์
  const info = monthly
    ? { ...wInfo, monthly: true, salary, monthAdv, maxAdv: Math.max(0, salary - monthAdv) }
    : { ...wInfo, monthly: false };
  const [amount, setAmount] = useStateQC(info.maxAdv);
  const submit = () => {
    const amt = Math.floor((+amount || 0) / 10) * 10;
    if (amt <= 0) {window.toast && window.toast("กรอกจำนวนเงินที่ต้องการเบิก");return;}
    if (amt > info.maxAdv) {window.toast && window.toast(monthly ? `เบิกได้ไม่เกิน ฿${fmt0(info.maxAdv)} (เงินเดือนคงเหลือ)` : `เบิกได้ไม่เกิน ฿${fmt0(info.maxAdv)} (80% ของค่าแรง)`);return;}
    let list = [];try {list = JSON.parse(localStorage.getItem("sss-advances") || "[]") || [];} catch {}
    const d = new Date();const period = `${QC_MONTHS[d.getMonth()]} ${d.getFullYear() + 543}`;
    const reason = monthly ? `เบิกเงินเดือนล่วงหน้า (${period})` : `เบิกระหว่างงวด (รอบวัน${dowName}) · ค่าแรง ${info.hours} ชม.`;
    list.unshift({ date: qcTodayTh(), emp: qcShortName(me.name), empId: me.id, reason, amount: amt, status: "อนุมัติแล้ว", period });
    try {localStorage.setItem("sss-advances", JSON.stringify(list));} catch {}
    window.logActivity && window.logActivity("เบิกเงินระหว่างงวด", `${me.name} ฿${fmt0(amt)}`, "time");
    window.toast && window.toast(`บันทึกเบิกเงิน ฿${fmt0(amt)} — ส่วนที่เหลือจะถูกนำไปหักตอนคิดเงินเดือน`);
    onDone && onDone();onClose();
  };
  return (
    <>
      <div className="scrim" style={{ zIndex: 230 }} onClick={onClose}></div>
      <div style={{ position: "fixed", left: "50%", top: "50%", transform: "translate(-50%, -50%)", zIndex: 231, background: "var(--surface)", border: "1px solid var(--line)", borderRadius: 14, padding: 18, width: 420, maxWidth: "94vw", maxHeight: "92vh", overflowY: "auto", boxShadow: "var(--shadow-lg)" }}>
        <div style={{ fontSize: 15, fontWeight: 700, marginBottom: 2 }}>💸 {monthly ? "ขอเบิกเงินเดือนล่วงหน้า" : `ขอเบิกเงินระหว่างงวด · รอบวัน${dowName}`}</div>
        <div style={{ fontSize: 11.5, color: "var(--ink-3)", marginBottom: 12 }}>{me.name} · {monthly ? "พนักงานเงินเดือน" : `ค่าแรง ฿${fmt0(info.hourly)}/ชม.`}</div>
        {monthly ?
        <div>
          <div style={{ display: "flex", justifyContent: "space-between", padding: "10px 12px", background: "var(--surface-2)", borderRadius: 8, fontSize: 13 }}>
            <span className="muted">เงินเดือน</span><b className="amount">฿{fmt0(info.salary)}</b>
          </div>
          <div style={{ display: "flex", justifyContent: "space-between", padding: "8px 12px", marginTop: 4, fontSize: 13 }}>
            <span className="muted">เบิกไปแล้วในเดือนนี้</span><span className="amount" style={{ color: "var(--c-amber, #b45309)" }}>฿{fmt0(info.monthAdv)}</span>
          </div>
          <div style={{ display: "flex", justifyContent: "space-between", padding: "8px 12px", marginTop: 4, fontSize: 13, color: "var(--success)", fontWeight: 600 }}>
            <span>เบิกได้อีก (ไม่เกินเงินเดือน)</span><span className="amount">฿{fmt0(info.maxAdv)}</span>
          </div>
        </div> :
        <>
        {info.days.length === 0 ?
        <div style={{ padding: 14, textAlign: "center", color: "var(--ink-3)", fontSize: 12.5, background: "var(--surface-2)", borderRadius: 8 }}>สัปดาห์นี้ยังไม่มีข้อมูลลงเวลาจากการสแกน — เบิกได้เมื่อมีการลงเวลาจริง</div> :

        <table className="t" style={{ fontSize: 12 }}>
            <thead><tr><th>วัน</th><th>เข้า-ออก</th><th className="right">ชม.</th><th className="right">ค่าแรง</th></tr></thead>
            <tbody>
              {info.days.map((d) =>
            <tr key={d.iso}><td>{d.date}</td><td className="mono" style={{ fontSize: 11 }}>{d.in}–{d.out}</td><td className="right amount">{d.hours}</td><td className="right amount">฿{fmt0(d.pay)}</td></tr>
            )}
            </tbody>
          </table>
        }
        <div style={{ display: "flex", justifyContent: "space-between", padding: "10px 12px", background: "var(--surface-2)", borderRadius: 8, marginTop: 10, fontSize: 13 }}>
          <span className="muted">ค่าแรงรวม ({info.hours} ชม.)</span><b className="amount">฿{fmt0(info.wage)}</b>
        </div>
        <div style={{ display: "flex", justifyContent: "space-between", padding: "8px 12px", marginTop: 4, fontSize: 13, color: "var(--success)", fontWeight: 600 }}>
          <span>เบิกได้ไม่เกิน 80% (ปัดลงหลักสิบ)</span><span className="amount">฿{fmt0(info.maxAdv)}</span>
        </div>
        </>
        }
        <div className="field" style={{ marginTop: 10 }}><label>จำนวนที่ขอเบิก (บาท)</label>
          <input className="input" type="number" min="0" step="10" max={info.maxAdv} value={amount} onChange={(e) => setAmount(e.target.value)} style={{ fontSize: 16, fontWeight: 600 }}></input>
        </div>
        <div style={{ fontSize: 11, color: "var(--ink-3)", marginTop: 4 }}>{monthly ? "ยอดที่เบิกจะถูกหักออกจากเงินเดือนที่จ่ายในรอบนี้" : `ส่วนที่ไม่ได้เบิก (฿${fmt0(info.wage - Math.floor((+amount || 0) / 10) * 10)}) จะถูกนำไปคิดรวมในเงินเดือน`}</div>
        <div style={{ display: "flex", gap: 8, justifyContent: "flex-end", marginTop: 14 }}>
          <Btn kind="ghost" onClick={onClose}>ยกเลิก</Btn>
          <Btn kind="primary" icon={I.check} onClick={submit}>ขอเบิกเงิน</Btn>
        </div>
      </div>
    </>);

}

function QRClockPage({ navigate }) {
  const D = window.SSSData;
  const { Btn, Badge, Card, PageHead, Tabs, Stat } = window.UI;
  const sess = window.Session ? window.Session.get() : null;
  const me = window.Session ? window.Session.employee() : null;
  const isAdmin = sess ? sess.isAdmin : true;
  // สิทธิ์รายหน้าย่อย: สรุปรายวัน (ทุกคน) · สร้าง QR + กฎ + อนุมัติลา
  const canRecords = window.Perms ? window.Perms.canSee("qr-records") : true;
  const canAdmin = window.Perms ? window.Perms.canSee("qr-admin") : true;

  const [tabRaw, setTab] = useStateQC("scan");
  const tabAllowed = (k) => k === "scan" || k === "req" || k === "records" && canRecords || k === "admin" && canAdmin;
  const tab = tabAllowed(tabRaw) ? tabRaw : "scan";
  const [token, setToken] = useStateQC(qcTokenLoad);
  const [records, setRecords] = useStateQC(qcClockLoad);
  const [reqs, setReqs] = useStateQC(qcReqLoad);
  const [rules, setRules] = useStateQC(qcRulesLoad);
  const [clockType, setClockType] = useStateQC("เข้างาน");
  const [scanning, setScanning] = useStateQC(false);
  const [result, setResult] = useStateQC(null);
  const [reqModal, setReqModal] = useStateQC(null); // "leave" | "late"
  const [advModal, setAdvModal] = useStateQC(false);
  const [editEmp, setEditEmp] = useStateQC(null); // { emp, st } — แก้เวลาแมนนวล
  const dowName = QC_ADV_DAY[new Date().getDay()];

  useEffectQC(() => {
    const refresh = () => {
      setRecords([...qcClockLoad()]);
      setReqs([...qcReqLoad()]);
    };
    window.addEventListener('sss-db-loaded', refresh);
    window.addEventListener('sss-qrclock-polled', refresh);
    window.addEventListener('sss-timereq-polled', refresh);
    return () => {
      window.removeEventListener('sss-db-loaded', refresh);
      window.removeEventListener('sss-qrclock-polled', refresh);
      window.removeEventListener('sss-timereq-polled', refresh);
    };
  }, []);

  const todayToken = token && token.date === qcTodayISO() ? token : null;
  const qrUrl = todayToken ? qcQRDataUrl(todayToken.token, 8) : null;
  const emps = (D.Employees || []).filter((e) => e.id !== "EMP-001");
  const todayISO = qcTodayISO();
  const beforeWork = qcTimeNow() < QC_WORK_START;
  const myToday = me ? qcDayStatus(me.id, todayISO, records, reqs) : null;
  const todaySummary = emps.map((e) => ({ emp: e, st: qcDayStatus(e.id, todayISO, records, reqs) }));
  const advancesAll = (() => { try { return JSON.parse(localStorage.getItem("sss-advances") || "[]") || []; } catch { return []; } })();
  const weekLedger = emps.map((e) => ({ emp: e, lg: qcWeekLedger(e, records, reqs, advancesAll) }));
  const weekRangeLabel = weekLedger.length ? `${qcThaiOfISO(weekLedger[0].lg.monISO)} – ${qcThaiOfISO(weekLedger[0].lg.satISO)}` : "";
  const cameCount = todaySummary.filter((x) => x.st.inT).length;
  const absentCount = todaySummary.filter((x) => x.st.status === "ขาดงาน").length;
  const pendingReqs = reqs.filter((r) => r.status === "รออนุมัติ");

  const saveReqs = (list) => {setReqs(list);qcReqSave(list);};
  const commitRecords = (list) => {setRecords(list);qcClockSave(list);};

  // ── แก้ไขเวลาแมนนวล (ผู้มีสิทธิ์) — แทนที่ปั๊มของ emp+วันนี้ด้วยค่าใหม่ ──
  const saveManualTimes = (emp, times) => {
    const others = records.filter((c) => !(c.empId === emp.id && c.date === todayISO));
    const mine = [];
    QC_CLOCK_TYPES.forEach((type) => {
      const v = String(times[type] || "").trim();
      if (/^\d{1,2}:\d{2}$/.test(v)) {
        mine.push({
          id: "MC-" + emp.id + "-" + type + "-" + Date.now() + "-" + Math.random().toString(36).slice(2, 5),
          empId: emp.id, name: emp.name, role: emp.role,
          date: todayISO, type, time: v.length === 4 ? "0" + v : v,
          manual: true, by: sess ? sess.name : "Admin", at: qcTodayTh() + " " + qcTimeNow()
        });
      }
    });
    commitRecords([...others, ...mine]);
    window.logActivity && window.logActivity("แก้ไขเวลาแมนนวล", emp.name + " · " + qcTodayTh(), "users");
    window.toast && window.toast("บันทึกเวลาของ " + emp.name + " แล้ว");
    setEditEmp(null);
  };

  // ── สร้าง QR ──
  const createToken = async () => {
    if (todayToken) {
      const ok = await window.confirmDialog({
        title: "สร้าง QR ใหม่แทนของเดิม",
        message: `วันนี้มี QR อยู่แล้ว (สร้างเมื่อ ${todayToken.createdAt} น.) — สร้างใหม่จะทำให้ QR เดิม<b>ใช้ไม่ได้ทันที</b>`,
        confirmText: "สร้าง QR ใหม่"
      });
      if (!ok) return;
    }
    const t = {
      date: todayISO,
      token: `SSS|CLOCK|${todayISO}|${Math.random().toString(36).slice(2, 8).toUpperCase()}`,
      createdBy: sess ? sess.name : "Admin",
      createdAt: qcTimeNow()
    };
    qcTokenSave(t);setToken(t);
    window.logActivity && window.logActivity("สร้าง QR ลงเวลา", `ประจำวันที่ ${qcTodayTh()}`, "create");
    window.toast && window.toast(`สร้าง QR ลงเวลาประจำวันที่ ${qcTodayTh()} แล้ว — ใช้ได้ถึงเที่ยงคืนวันนี้`);
  };

  // ── สแกน ──
  const handleScan = (text) => {
    setScanning(false);
    if (!me) {setResult({ ok: false, msg: "บัญชีของคุณยังไม่ผูกกับพนักงาน — ให้ Admin ผูกที่หน้า ผู้ใช้และสิทธิ์" });return;}
    if (text !== "DEMO") {
      const parts = String(text).split("|");
      if (parts.length !== 4 || parts[0] !== "SSS" || parts[1] !== "CLOCK" || parts[2] !== todayISO) {
        setResult({ ok: false, msg: "QR ไม่ถูกต้องหรือหมดอายุแล้ว — สแกน QR ที่ Admin สร้างสำหรับวันที่ " + qcTodayTh() + " เท่านั้น" });
        return;
      }
    }
    const dup = records.find((r) => r.date === todayISO && r.empId === me.id && r.type === clockType);
    if (dup) {setResult({ ok: false, msg: `วันนี้คุณลงเวลา "${clockType}" ไปแล้วเมื่อ ${dup.time} น.` });return;}
    const time = qcTimeNow();
    const lateOk = reqs.some((r) => r.type === "แจ้งเข้าสาย" && r.empId === me.id && r.date === todayISO && r.status !== "ปฏิเสธ");
    const late = clockType === "เข้างาน" && time > QC_WORK_START && !lateOk;
    const rec = { date: todayISO, dateTh: qcTodayTh(), time, empId: me.id, name: me.name, role: me.role, type: clockType, late, lateOk, method: "QR มือถือ" };
    commitRecords([rec, ...records]);
    window.logActivity && window.logActivity("ลงเวลา QR", `${me.name} ${clockType} ${time}${late ? " (สาย)" : ""}`, "time");
    setResult({ ok: true, rec });
    // ขั้นถัดไปอัตโนมัติ
    const nextIdx = QC_CLOCK_TYPES.indexOf(clockType) + 1;
    if (nextIdx < QC_CLOCK_TYPES.length) setClockType(QC_CLOCK_TYPES[nextIdx]);
  };

  // ── ส่งคำขอ ลา / เข้าสาย ──
  const submitReq = (req) => {
    // ขอลาวันนี้ทำได้ก่อน 08:00 เท่านั้น (เหมือนการขอเข้าสาย) — เลยเวลาแล้วต้องเลือกวันถัดไป
    if (req.type === "ขอลา" && req.date === todayISO && !beforeWork) {
      window.toast && window.toast(`เลย ${QC_WORK_START} แล้ว — ขอลาวันนี้ไม่ได้ ให้เลือกตั้งแต่วันพรุ่งนี้`);
      return;
    }
    const r = { id: "RQ-" + Date.now(), empId: me.id, name: me.name, role: me.role, status: "รออนุมัติ", createdAt: `${qcTodayTh()} ${qcTimeNow()}`, ...req };
    saveReqs([r, ...reqs]);
    window.logActivity && window.logActivity(req.type, `${me.name} · ${req.type === "ขอลา" ? req.period : "ถึง " + req.expected} · ${qcThaiOfISO(req.date)}`, "time");
    window.toast && window.toast(`ส่งคำขอ${req.type === "ขอลา" ? "ลา" : "เข้างานสาย"}แล้ว — รอหัวหน้าอนุมัติ`);
    setReqModal(null);
  };
  const decideReq = (id, status) => {
    saveReqs(reqs.map((r) => r.id === id ? { ...r, status, decidedBy: sess ? sess.name : "Admin" } : r));
    window.toast && window.toast(`${status}คำขอแล้ว`);
  };

  return (
    <>
      <PageHead
        title="ลงเวลา QR"
        sub={me ? `${me.name} · ${me.role} — ลงเวลาได้เฉพาะของตัวเอง · ไม่ลงเวลา = ขาดงาน` : "เข้าระบบแล้วลงเวลาได้เฉพาะของตัวเอง"}
        right={canAdmin ? <Btn icon={I.payroll} kind="ghost" onClick={() => navigate("payroll")}>ไปหน้าคำนวนเงินเดือน</Btn> : null} />
      

      {canRecords &&
      <div className="grid cols-4 mb-lg">
        <Stat label="QR วันนี้" value={todayToken ? "พร้อมใช้ ✓" : "ยังไม่สร้าง"} sub={todayToken ? `สร้างเมื่อ ${todayToken.createdAt} น.` : "ผู้มีสิทธิ์สร้างที่แท็บ QR"} icon={I.qr} />
        <Stat label="มาแล้ววันนี้" value={`${cameCount}/${emps.length} คน`} sub={qcTodayTh()} icon={I.check} />
        <Stat label="ยังไม่ลงเวลา (ขาด)" value={`${absentCount} คน`} sub="ไม่ลงเวลาและไม่มีใบลา = ขาดงาน" icon={I.bell} />
        <Stat label="คำขอรออนุมัติ" value={`${pendingReqs.length} รายการ`} sub="ขอลา / แจ้งเข้าสาย" icon={I.clock} />
      </div>
      }

      <Tabs items={[
      { key: "scan", label: "📱 ลงเวลา (ของฉัน)" },
      { key: "req", label: "ขอลา / แจ้งเข้าสาย", count: me ? reqs.filter((r) => r.empId === me.id).length : 0 },
      ...(canRecords ? [{ key: "records", label: "สรุปรายวัน", count: pendingReqs.length || undefined }] : []),
      ...(canAdmin ? [{ key: "admin", label: "QR + กฎบริษัท (ผู้มีสิทธิ์)" }] : [])]
      } active={tab} onChange={(k) => {setTab(k);setResult(null);}} />

      {/* ════ ลงเวลาของฉัน ════ */}
      {tab === "scan" &&
      <div style={{ display: "flex", justifyContent: "center" }}>
          <div style={{ width: 420, maxWidth: "100%", display: "flex", flexDirection: "column", gap: 12 }}>
            <Card>
              {!me ?
            <div style={{ padding: 10, fontSize: 13, color: "var(--ink-2)", lineHeight: 1.8 }}>
                  บัญชี <b>{sess ? sess.username : "—"}</b> ยังไม่ผูกกับพนักงาน จึงลงเวลาไม่ได้<br />
                  ให้ Admin ผูกที่หน้า <a style={{ textDecoration: "underline", cursor: "pointer" }} onClick={() => navigate("users")}>ผู้ใช้และสิทธิ์</a>
                </div> :
            !result ?
            <div style={{ display: "flex", flexDirection: "column", gap: 12 }}>
                  <div style={{ display: "flex", alignItems: "center", gap: 12, padding: "4px 0" }}>
                    {window.RankAvatar ? <window.RankAvatar name={me.name} size={46} /> : null}
                    <div style={{ flex: 1 }}>
                      <div style={{ fontSize: 15, fontWeight: 700 }} data-comment-anchor="65ca98a28a-div-378-23">{me.name}</div>
                      <div style={{ fontSize: 12, color: "var(--ink-3)" }}>{me.role} · {me.dept}</div>
                    </div>
                    <div style={{ textAlign: "right" }}>
                      <div style={{ fontSize: 19, fontWeight: 800, fontVariantNumeric: "tabular-nums" }}>{qcTimeNow()}</div>
                      <div style={{ fontSize: 11, color: "var(--ink-3)" }}>{qcTodayTh()}</div>
                    </div>
                  </div>

                  {/* สถานะ 4 ช่องของวันนี้ */}
                  <div style={{ display: "grid", gridTemplateColumns: "repeat(4, 1fr)", gap: 6 }}>
                    {QC_CLOCK_TYPES.map((t) => {
                  const v = { "เข้างาน": myToday.inT, "พักเที่ยง": myToday.brT, "เลิกพัก": myToday.rbT, "ออกงาน": myToday.outT }[t];
                  return (
                    <div key={t} style={{ textAlign: "center", padding: "8px 4px", borderRadius: 10, border: v ? "1.5px solid var(--success)" : "1px dashed var(--line-strong)", background: v ? "var(--success-bg, var(--surface-2))" : "var(--surface-2)" }}>
                          <div style={{ fontSize: 15 }}>{QC_TYPE_EMOJI[t]}</div>
                          <div style={{ fontSize: 10, color: "var(--ink-3)" }}>{t}</div>
                          <div style={{ fontSize: 12.5, fontWeight: 700, color: v ? "var(--success)" : "var(--ink-4)" }}>{v || "—"}</div>
                        </div>);

                })}
                  </div>

                  {/* เลือกประเภท + สแกน */}
                  <div className="payseg" style={{ display: "flex", width: "100%" }}>
                    {QC_CLOCK_TYPES.map((t) =>
                <button key={t} type="button" className={"payseg-btn" + (clockType === t ? " active" : "")} style={{ flex: 1, height: 38, fontSize: 12.5 }} onClick={() => setClockType(t)}>{QC_TYPE_EMOJI[t]} {t}</button>
                )}
                  </div>
                  <button onClick={() => setScanning(true)} style={{ height: 56, borderRadius: 12, border: "none", background: "var(--accent)", color: "var(--accent-ink)", fontSize: 16, fontWeight: 700, cursor: "pointer", fontFamily: "var(--font)", display: "flex", alignItems: "center", justifyContent: "center", gap: 10 }}>
                    <I.camera size={20} /> เปิดกล้องสแกน — {clockType}
                  </button>

                  {/* ปุ่มขอลา / แจ้งสาย */}
                  <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 8 }}>
                    <button onClick={() => setReqModal("leave")} style={{ height: 42, borderRadius: 10, border: "1px solid var(--line-strong)", background: "var(--surface)", fontSize: 13.5, fontWeight: 600, cursor: "pointer", fontFamily: "var(--font)", color: "var(--ink-1)" }}>🌴 ขอลา</button>
                    <button onClick={() => beforeWork ? setReqModal("late") : window.toast && window.toast("เลย 08:00 แล้ว — แจ้งขอเข้าสายไม่ได้ ต้องแจ้งก่อนเวลาเข้างานเท่านั้น")}
                style={{ height: 42, borderRadius: 10, border: "1px solid var(--line-strong)", background: "var(--surface)", fontSize: 13.5, fontWeight: 600, cursor: "pointer", fontFamily: "var(--font)", color: beforeWork ? "var(--ink-1)" : "var(--ink-4)" }}
                title={beforeWork ? "แจ้งขอเข้างานสายวันนี้" : "แจ้งได้ก่อน 08:00 เท่านั้น"}>
                      ⏰ ขอเข้าสาย {beforeWork ? "" : "(ปิด หลัง 08:00)"}
                    </button>
                  </div>

                  {/* ปุ่มขอเบิกเงินระหว่างงวด — เฉพาะวันพุธ/เสาร์ */}
                  {dowName &&
              <button onClick={() => setAdvModal(true)} style={{ height: 46, borderRadius: 10, border: "none", background: "var(--c-amber, #b45309)", color: "white", fontSize: 14.5, fontWeight: 700, cursor: "pointer", fontFamily: "var(--font)", display: "flex", alignItems: "center", justifyContent: "center", gap: 8 }}>
                      💸 ขอเบิกเงินระหว่างงวด (รอบวัน{dowName})
                    </button>
              }

                  {/* กฎการทำงานบริษัท */}
                  <div style={{ border: "1px solid var(--line)", borderRadius: 10, padding: "10px 14px", background: "var(--surface-2)" }}>
                    <div style={{ fontSize: 11, fontWeight: 700, color: "var(--ink-3)", textTransform: "uppercase", letterSpacing: "0.05em", marginBottom: 6 }}>📋 กฎการทำงานของบริษัท</div>
                    <div style={{ fontSize: 12, color: "var(--ink-2)", whiteSpace: "pre-wrap", lineHeight: 1.7 }}>{rules}</div>
                  </div>
                </div> :
            result.ok ?
            <div style={{ textAlign: "center", padding: "10px 0", display: "flex", flexDirection: "column", gap: 10, alignItems: "center" }}>
                  <div style={{ width: 72, height: 72, borderRadius: "50%", background: "var(--success)", color: "white", display: "grid", placeItems: "center", fontSize: 36 }}>✓</div>
                  <div style={{ fontSize: 17, fontWeight: 700 }}>{QC_TYPE_EMOJI[result.rec.type]} ลงเวลา{result.rec.type}สำเร็จ</div>
                  <div style={{ fontSize: 30, fontWeight: 800, fontVariantNumeric: "tabular-nums" }}>{result.rec.time} น.</div>
                  {result.rec.late ?
              <Badge tone="warning">มาสาย — เข้าหลัง {QC_WORK_START}</Badge> :
              result.rec.lateOk && result.rec.type === "เข้างาน" ?
              <Badge tone="info">แจ้งสายล่วงหน้าแล้ว — ไม่นับสาย</Badge> :
              <Badge tone="success">บันทึกแล้ว</Badge>}
                  <div style={{ fontSize: 11.5, color: "var(--ink-3)" }}>{result.rec.dateTh} · ส่งเข้าระบบเงินเดือน/KPI รายวันแล้ว</div>
                  <Btn kind="primary" onClick={() => setResult(null)}>เสร็จสิ้น</Btn>
                </div> :

            <div style={{ textAlign: "center", padding: "10px 0", display: "flex", flexDirection: "column", gap: 10, alignItems: "center" }}>
                  <div style={{ width: 72, height: 72, borderRadius: "50%", background: "var(--danger)", color: "white", display: "grid", placeItems: "center", fontSize: 34 }}>✕</div>
                  <div style={{ fontSize: 16, fontWeight: 700 }}>สแกนไม่สำเร็จ</div>
                  <div style={{ fontSize: 13, color: "var(--ink-2)", maxWidth: 300, lineHeight: 1.6 }}>{result.msg}</div>
                  <div style={{ display: "flex", gap: 8 }}>
                    <Btn kind="ghost" onClick={() => setResult(null)}>กลับ</Btn>
                    <Btn kind="primary" icon={I.camera} onClick={() => {setResult(null);setScanning(true);}}>สแกนใหม่</Btn>
                  </div>
                </div>
            }
            </Card>
          </div>
        </div>
      }

      {/* ════ ขอลา / แจ้งเข้าสาย ════ */}
      {tab === "req" &&
      <div style={{ display: "flex", justifyContent: "center" }}>
          <div style={{ width: 560, maxWidth: "100%", display: "flex", flexDirection: "column", gap: 12 }}>
            {me &&
          <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 8 }}>
                <Btn icon={I.doc} onClick={() => setReqModal("leave")}>🌴 ขอลา (เช้า/บ่าย/ทั้งวัน)</Btn>
                <Btn icon={I.clock} kind={beforeWork ? "primary" : "ghost"} onClick={() => beforeWork ? setReqModal("late") : window.toast && window.toast("เลย 08:00 แล้ว — แจ้งขอเข้าสายไม่ได้")}>
                  ⏰ ขอเข้าสาย{beforeWork ? " (วันนี้)" : " — ปิดหลัง 08:00"}
                </Btn>
              </div>
          }
            <Card title="คำขอของฉัน" sub="ลา / แจ้งเข้าสาย — รอหัวหน้าอนุมัติ" flush>
              {(me ? reqs.filter((r) => r.empId === me.id) : []).length === 0 ?
            <div style={{ padding: 16, textAlign: "center", color: "var(--ink-3)", fontSize: 12.5 }}>ยังไม่มีคำขอ</div> :

            <table className="t">
                  <thead><tr><th>ประเภท</th><th>วันที่</th><th>รายละเอียด</th><th>เหตุผล</th><th>สถานะ</th></tr></thead>
                  <tbody>
                    {reqs.filter((r) => r.empId === me.id).map((r) =>
                <tr key={r.id}>
                        <td><Badge tone={r.type === "ขอลา" ? "info" : "warning"}>{r.type}</Badge></td>
                        <td>{qcThaiOfISO(r.date)}</td>
                        <td>{r.type === "ขอลา" ? r.period : `คาดว่าถึง ${r.expected} น.`}</td>
                        <td className="muted" style={{ fontSize: 12, maxWidth: 180 }}>{r.reason}</td>
                        <td><Badge tone={r.status === "อนุมัติแล้ว" ? "success" : r.status === "ปฏิเสธ" ? "danger" : "warning"} dot>{r.status}</Badge></td>
                      </tr>
                )}
                  </tbody>
                </table>
            }
            </Card>
          </div>
        </div>
      }

      {/* ════ สรุปรายวัน ════ */}
      {tab === "records" && canRecords &&
      <div style={{ display: "flex", flexDirection: "column", gap: 14 }}>
          <Card title={`สรุปลงเวลารายวัน · ${qcTodayTh()}`} sub={"ไม่ลงเวลาและไม่มีใบลา = ขาดงาน · ข้อมูลนี้ส่งเข้าคำนวนเงินเดือนแบบรายวัน" + (canAdmin ? " · กด ✎ แก้ไขเวลาแมนนวลได้" : "")}
            right={<Btn size="sm" kind="ghost" icon={I.refresh || I.sync} onClick={async () => {
              if (window.SSSSync) {
                await window.SSSSync.load();
                setRecords([...qcClockLoad()]);
                setReqs([...qcReqLoad()]);
                window.toast && window.toast('โหลดข้อมูลลงเวลาจาก Supabase แล้ว · ' + records.length + ' → ' + qcClockLoad().length + ' รายการ');
              }
            }}>ซิงค์ข้อมูล</Btn>}
            flush>
            <div className="table-wrap">
              <table className="t">
                <thead><tr><th>พนักงาน</th><th className="right">เข้างาน</th><th className="right">พักเที่ยง</th><th className="right">เลิกพัก</th><th className="right">ออกงาน</th><th>สถานะ</th>{canAdmin && <th className="right">แก้ไข</th>}</tr></thead>
                <tbody>
                  {todaySummary.map(({ emp, st }) =>
                <tr key={emp.id}>
                      <td>
                        <div style={{ display: "flex", alignItems: "center", gap: 9 }}>
                          {window.RankAvatar ? <window.RankAvatar name={emp.name} size={26} /> : null}
                          <div><div style={{ fontWeight: 600, fontSize: 13 }}>{emp.name}</div><div style={{ fontSize: 10.5, color: "var(--ink-3)" }}>{emp.role}</div></div>
                        </div>
                      </td>
                      <td className="right amount">{st.inT || "—"}</td>
                      <td className="right amount muted">{st.brT || "—"}</td>
                      <td className="right amount muted">{st.rbT || "—"}</td>
                      <td className="right amount">{st.outT || "—"}</td>
                      <td><Badge tone={st.tone} dot>{st.status}</Badge></td>
                      {canAdmin &&
                  <td className="right">
                        <button className="icon-btn" title={`แก้ไขเวลาของ ${emp.name}`} onClick={() => setEditEmp({ emp, st })}><I.edit size={14} /></button>
                      </td>
                }
                    </tr>
                )}
                </tbody>
              </table>
            </div>
          </Card>

          <Card title="ค่าแรงรายสัปดาห์ · เบิกระหว่างงวด" sub={`${weekRangeLabel} — วันที่มาทำงาน + ค่าแรงต่อวัน · เบิกได้ 80% ของค่าแรงสะสม (วันเบิก: พุธ/เสาร์)`} flush>
            <div className="table-wrap">
              <table className="t" style={{ minWidth: 760 }}>
                <thead><tr>
                  <th>พนักงาน</th>
                  {QC_WEEK_DOW.map((d, i) => <th key={i} className="right" style={(i === 2 || i === 5) ? { color: "var(--c-amber, #b45309)" } : null}>{d}{(i === 2 || i === 5) ? " 💸" : ""}</th>)}
                  <th className="right">ค่าแรงสะสม</th>
                  <th className="right">เบิกแล้ว</th>
                  <th className="right">เบิกได้อีก</th>
                </tr></thead>
                <tbody>
                  {weekLedger.map(({ emp, lg }) => (
                    <tr key={emp.id}>
                      <td>
                        <div style={{ display: "flex", alignItems: "center", gap: 9 }}>
                          {window.RankAvatar ? <window.RankAvatar name={emp.name} size={26} /> : null}
                          <div><div style={{ fontWeight: 600, fontSize: 13 }}>{emp.name}</div><div style={{ fontSize: 10.5, color: "var(--ink-3)" }}>฿{window.UI.fmt0(lg.hourly)}/ชม.</div></div>
                        </div>
                      </td>
                      {lg.days.map((dy, i) => (
                        <td key={i} className="right amount" style={{ fontSize: 12, background: dy.advDay ? "color-mix(in srgb, var(--warning-bg, #fef3c7) 50%, transparent)" : null }}>
                          {dy.worked
                            ? <span style={{ color: "var(--success)", fontWeight: 600 }}>฿{window.UI.fmt0(dy.pay)}</span>
                            : dy.leave
                              ? <span style={{ color: "var(--info)", fontSize: 11 }}>ลา</span>
                              : <span style={{ color: dy.future ? "var(--ink-4)" : "var(--ink-3)" }}>{dy.future ? "·" : "—"}</span>}
                        </td>
                      ))}
                      <td className="right amount" style={{ fontWeight: 600 }}>฿{window.UI.fmt0(lg.accrued)}</td>
                      <td className="right amount" style={{ color: lg.advTaken > 0 ? "var(--c-amber, #b45309)" : "var(--ink-3)" }}>{lg.advTaken > 0 ? `฿${window.UI.fmt0(lg.advTaken)}` : "—"}</td>
                      <td className="right amount" style={{ fontWeight: 700, color: lg.remaining > 0 ? "var(--success)" : "var(--ink-3)" }}>฿{window.UI.fmt0(lg.remaining)}</td>
                    </tr>
                  ))}
                </tbody>
              </table>
            </div>
            <div style={{ padding: "10px 14px", borderTop: "1px solid var(--line)", fontSize: 11.5, color: "var(--ink-3)", lineHeight: 1.7 }}>
              ค่าแรงต่อวัน = ชั่วโมงทำงานจริง (สูงสุด 8 ชม./วัน) × ค่าแรงต่อชั่วโมง · <b style={{ color: "var(--success)" }}>เบิกได้อีก</b> = 80% ของค่าแรงสะสม − ยอดที่เบิกไปแล้วในสัปดาห์ · ช่อง "ลา" = อนุมัติลา · "—" = ไม่มาทำงาน
            </div>
          </Card>

          <Card title="คำขอลา / แจ้งเข้าสาย" sub={canAdmin ? "ผู้มีสิทธิ์กดอนุมัติ/ปฏิเสธได้ที่นี่" : "สถานะคำขอของทุกคน"} flush>
            {reqs.length === 0 ?
          <div style={{ padding: 16, textAlign: "center", color: "var(--ink-3)", fontSize: 12.5 }}>ยังไม่มีคำขอ</div> :

          <table className="t">
                <thead><tr><th>พนักงาน</th><th>ประเภท</th><th>วันที่</th><th>รายละเอียด</th><th>เหตุผล</th><th>สถานะ</th>{canAdmin && <th></th>}</tr></thead>
                <tbody>
                  {reqs.map((r) =>
              <tr key={r.id}>
                      <td style={{ fontWeight: 500 }}>{r.name}</td>
                      <td><Badge tone={r.type === "ขอลา" ? "info" : "warning"}>{r.type}</Badge></td>
                      <td>{qcThaiOfISO(r.date)}</td>
                      <td>{r.type === "ขอลา" ? r.period : `คาดว่าถึง ${r.expected} น.`}</td>
                      <td className="muted" style={{ fontSize: 12, maxWidth: 200 }}>{r.reason}</td>
                      <td><Badge tone={r.status === "อนุมัติแล้ว" ? "success" : r.status === "ปฏิเสธ" ? "danger" : "warning"} dot>{r.status}</Badge></td>
                      {canAdmin &&
                <td>
                          {r.status === "รออนุมัติ" &&
                  <div style={{ display: "flex", gap: 4, justifyContent: "flex-end" }}>
                              <Btn size="sm" kind="primary" onClick={() => decideReq(r.id, "อนุมัติแล้ว")}>อนุมัติ</Btn>
                              <Btn size="sm" kind="ghost" onClick={() => decideReq(r.id, "ปฏิเสธ")}>ปฏิเสธ</Btn>
                            </div>
                  }
                        </td>
                }
                    </tr>
              )}
                </tbody>
              </table>
          }
          </Card>
        </div>
      }

      {/* ════ Admin: QR + กฎบริษัท ════ */}
      {tab === "admin" && canAdmin &&
      <div className="grid" style={{ gridTemplateColumns: "minmax(0, 400px) 1fr", gap: 14, alignItems: "flex-start" }}>
          <Card title={`QR ประจำวันที่ ${qcTodayTh()}`} sub="เปลี่ยนใหม่ทุกวัน · ใช้ได้ถึงเที่ยงคืน">
            {!todayToken ?
          <div style={{ textAlign: "center", padding: "18px 0", display: "flex", flexDirection: "column", gap: 12, alignItems: "center" }}>
                <div style={{ width: 200, height: 200, border: "2px dashed var(--line-strong)", borderRadius: 14, display: "grid", placeItems: "center", color: "var(--ink-3)" }}>
                  <div style={{ textAlign: "center" }}><I.qr size={42} /><div style={{ fontSize: 12, marginTop: 8 }}>ยังไม่มี QR ของวันนี้</div></div>
                </div>
                <Btn kind="primary" icon={I.qr} onClick={createToken}>สร้าง QR ของวันนี้</Btn>
                {token && token.date !== todayISO && <div style={{ fontSize: 11.5, color: "var(--warning)" }}>QR ล่าสุดเป็นของวันก่อน — หมดอายุแล้ว</div>}
              </div> :

          <div style={{ textAlign: "center", display: "flex", flexDirection: "column", gap: 10, alignItems: "center" }}>
                <div style={{ padding: 12, background: "white", borderRadius: 14, border: "1px solid var(--line)" }}>
                  {qrUrl ?
              <img src={qrUrl} alt="QR ลงเวลาประจำวัน" style={{ width: 232, height: 232, display: "block", imageRendering: "pixelated" }} /> :
              <div className="mono" style={{ width: 232, height: 232, display: "grid", placeItems: "center", fontSize: 12, color: "#0c0a09", padding: 10, textAlign: "center", wordBreak: "break-all" }}>{todayToken.token}</div>}
                </div>
                <div className="mono" style={{ fontSize: 10.5, color: "var(--ink-3)", wordBreak: "break-all" }}>{todayToken.token}</div>
                <div style={{ fontSize: 12, color: "var(--ink-3)" }}>สร้างโดย <b style={{ color: "var(--ink-1)" }}>{todayToken.createdBy}</b> · {todayToken.createdAt} น.</div>
                <div style={{ display: "flex", gap: 8, flexWrap: "wrap", justifyContent: "center" }}>
                  <Btn icon={I.print} kind="primary" onClick={() => qcPrintQR(todayToken, qcQRDataUrl(todayToken.token, 16))}>พิมพ์ติดหน้างาน</Btn>
                  <Btn icon={I.qr} kind="ghost" onClick={createToken}>สร้างใหม่ (ยกเลิกอันเดิม)</Btn>
                </div>
              </div>
          }
          </Card>

          <Card title="กฎการทำงานของบริษัท" sub="แสดงให้พนักงานเห็นในหน้าลงเวลา — แก้ไขได้เฉพาะผู้มีสิทธิ์"
        right={<Btn size="sm" kind="primary" icon={I.check} onClick={() => {
          try {localStorage.setItem(QC_RULES_KEY, rules);} catch {}
          window.logActivity && window.logActivity("แก้ไขกฎการทำงาน", `โดย ${sess ? sess.name : "Admin"}`, "update");
          window.toast && window.toast("บันทึกกฎการทำงานแล้ว — แสดงให้พนักงานทุกคนทันที");
        }}>บันทึกกฎ</Btn>}>
            <textarea className="textarea" value={rules} onChange={(e) => setRules(e.target.value)} style={{ minHeight: 200, fontSize: 13, lineHeight: 1.8 }}></textarea>
            <div style={{ fontSize: 11.5, color: "var(--ink-3)", marginTop: 8, lineHeight: 1.7 }}>
              ผู้แก้ไข: <b style={{ color: "var(--ink-1)" }}>{sess ? sess.name : "Admin"}</b> · กฎจะแสดงในกรอบบนหน้า "ลงเวลา (ของฉัน)" ของพนักงานทุกคน รวมถึงบนมือถือ
            </div>
          </Card>
        </div>
      }

      {/* ── Modal ขอลา ── */}
      {reqModal === "leave" && me &&
      <QCRequestModal
        title="🌴 ขอลา"
        note={beforeWork
          ? `ขอลาวันนี้ (${qcThaiOfISO(todayISO)}) ทำได้ก่อน ${QC_WORK_START} — ตอนนี้ ${qcTimeNow()} น. · หรือเลือกลาวันถัดไปล่วงหน้าได้`
          : `เลย ${QC_WORK_START} แล้ว — ขอลาวันนี้ไม่ได้ เลือกได้ตั้งแต่วันพรุ่งนี้เป็นต้นไป`}
        defaultDate={beforeWork ? todayISO : undefined}
        minDate={beforeWork ? todayISO : (() => {const d = new Date();d.setDate(d.getDate() + 1);return d.toISOString().slice(0, 10);})()}
        onClose={() => setReqModal(null)}
        onSubmit={(f) => submitReq({ type: "ขอลา", date: f.date, period: f.period, reason: f.reason })}
        fields={{ date: true, period: true, reason: "เหตุผลการลา เช่น ลากิจไปติดต่อราชการ / ลาป่วย" }} />

      }
      {/* ── Modal ขอเข้าสาย ── */}
      {reqModal === "late" && me &&
      <QCRequestModal
        title="⏰ ขอเข้างานสาย (วันนี้)"
        note={`แจ้งได้ก่อน ${QC_WORK_START} เท่านั้น — ตอนนี้ ${qcTimeNow()} น. · เมื่อหัวหน้ารับทราบ จะไม่นับเป็นมาสาย`}
        onClose={() => setReqModal(null)}
        onSubmit={(f) => submitReq({ type: "แจ้งเข้าสาย", date: todayISO, expected: f.expected, reason: f.reason })}
        fields={{ expected: true, reason: "เหตุผล เช่น รถเสีย / พาลูกไปหาหมอ" }} />

      }

      {scanning && <QRScanModal onClose={() => setScanning(false)} onResult={handleScan} demoToken={todayToken ? todayToken.token : null} />}
      {advModal && me && <QCAdvanceModal me={me} dowName={dowName} onClose={() => setAdvModal(false)} onDone={() => {}} />}
      {editEmp && canAdmin && <QCEditTimeModal emp={editEmp.emp} st={editEmp.st} onClose={() => setEditEmp(null)} onSave={saveManualTimes} />}
    </>);

}

// ---------- Modal แก้ไขเวลาแมนนวล (ผู้มีสิทธิ์) ----------
function QCEditTimeModal({ emp, st, onClose, onSave }) {
  const { Btn } = window.UI;
  const [t, setT] = useStateQC({
    "เข้างาน": st.inT || "", "พักเที่ยง": st.brT || "", "เลิกพัก": st.rbT || "", "ออกงาน": st.outT || ""
  });
  const set = (k, v) => setT((p) => ({ ...p, [k]: v }));
  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: 18, width: 380, maxWidth: "94vw", boxShadow: "var(--shadow-lg)" }}>
        <div style={{ fontSize: 15, fontWeight: 700, marginBottom: 2 }}>แก้ไขเวลา — {emp.name}</div>
        <div style={{ fontSize: 11.5, color: "var(--ink-3)", marginBottom: 12 }}>{qcTodayTh()} · เว้นว่าง = ลบเวลานั้น · สถานะ/สาย จะคำนวณใหม่อัตโนมัติ</div>
        <div style={{ display: "flex", flexDirection: "column", gap: 10 }}>
          {QC_CLOCK_TYPES.map((type) =>
          <div className="field" key={type}>
              <label>{QC_TYPE_EMOJI[type]} {type}</label>
              <div style={{ display: "flex", gap: 6, alignItems: "center" }}>
                <input className="input" type="time" value={t[type]} onChange={(e) => set(type, e.target.value)} style={{ height: 38, flex: 1 }} />
                {t[type] && <button className="icon-btn" title="ล้างเวลานี้" onClick={() => set(type, "")}><I.close size={13} /></button>}
              </div>
            </div>
          )}
        </div>
        <div style={{ display: "flex", gap: 8, justifyContent: "flex-end", marginTop: 16 }}>
          <Btn kind="ghost" onClick={onClose}>ยกเลิก</Btn>
          <Btn kind="primary" icon={I.check} onClick={() => onSave(emp, t)}>บันทึก</Btn>
        </div>
      </div>
    </>);

}

// ---------- Modal คำขอ (ลา / เข้าสาย) ----------
function QCRequestModal({ title, note, fields, onClose, onSubmit, defaultDate, minDate }) {
  const { Btn } = window.UI;
  const tomorrow = (() => {const d = new Date();d.setDate(d.getDate() + 1);return d.toISOString().slice(0, 10);})();
  const [f, setF] = useStateQC({ date: defaultDate || tomorrow, period: "ลาทั้งวัน", expected: "09:00", reason: "" });
  const upd = (k, v) => setF((p) => ({ ...p, [k]: v }));
  const submit = () => {
    if (!f.reason.trim()) {window.toast && window.toast("กรุณากรอกเหตุผล");return;}
    onSubmit({ ...f, reason: f.reason.trim() });
  };
  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: 18, width: 420, maxWidth: "94vw", boxShadow: "var(--shadow-lg)" }}>
        <div style={{ fontSize: 15, fontWeight: 700, marginBottom: 4 }}>{title}</div>
        {note && <div style={{ fontSize: 11.5, color: "var(--ink-3)", marginBottom: 10 }}>{note}</div>}
        <div style={{ display: "flex", flexDirection: "column", gap: 10, marginTop: 6 }}>
          {fields.date &&
          <div className="field"><label>วันที่ลา</label>
              <input className="input" type="date" value={f.date} min={minDate || undefined} onChange={(e) => upd("date", e.target.value)} style={{ height: 38 }}></input>
            </div>
          }
          {fields.period &&
          <div className="field"><label>ช่วงที่ลา</label>
              <div className="payseg" style={{ display: "flex", width: "100%" }}>
                {["ลาเช้า", "ลาบ่าย", "ลาทั้งวัน"].map((p) =>
              <button key={p} type="button" className={"payseg-btn" + (f.period === p ? " active" : "")} style={{ flex: 1, height: 36 }} onClick={() => upd("period", p)}>{p}</button>
              )}
              </div>
            </div>
          }
          {fields.expected &&
          <div className="field"><label>คาดว่าจะถึงที่ทำงานเวลา</label>
              <input className="input" type="time" value={f.expected} onChange={(e) => upd("expected", e.target.value)} style={{ height: 38 }}></input>
            </div>
          }
          <div className="field"><label>เหตุผล *</label>
            <textarea className="textarea" value={f.reason} onChange={(e) => upd("reason", e.target.value)} placeholder={fields.reason} style={{ minHeight: 64 }} autoFocus></textarea>
          </div>
        </div>
        <div style={{ display: "flex", gap: 8, justifyContent: "flex-end", marginTop: 14 }}>
          <Btn kind="ghost" onClick={onClose}>ยกเลิก</Btn>
          <Btn kind="primary" icon={I.send} onClick={submit}>ส่งคำขอ</Btn>
        </div>
      </div>
    </>);

}

// =============== ดึงข้อมูลลงเวลา QR เข้าเงินเดือน (เลือกช่วงวัน) ===============
function PayrollQRImport({ timeRecs, setTimeRecs, month, setMonth }) {
  const D = window.SSSData;
  const { Btn, Card } = window.UI;
  const ym = month || (() => {const d = new Date();return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}`;})();
  const lastDayOf = (m) => {const [y, mo] = m.split("-").map(Number);return new Date(y, mo, 0).getDate();};
  const [from, setFrom] = useStateQC(ym + "-01");
  const [to, setTo] = useStateQC(ym + "-" + String(lastDayOf(ym)).padStart(2, "0"));
  const [last, setLast] = useStateQC(null);
  useEffectQC(() => {setFrom(ym + "-01");setTo(ym + "-" + String(lastDayOf(ym)).padStart(2, "0"));}, [ym]);

  const pull = () => {
    if (!from || !to || to < from) {window.toast && window.toast("เลือกช่วงวันที่ให้ถูกต้อง");return;}
    const clocks = qcClockLoad();
    const reqs = qcReqLoad();
    const emps = (D.Employees || []).filter((e) => e.id !== "EMP-001");
    const out = [];
    let came = 0,absent = 0,leave = 0,lateCnt = 0;
    const cur = new Date(from + "T12:00:00");
    const end = new Date(to + "T12:00:00");
    while (cur <= end) {
      const iso = cur.toISOString().slice(0, 10);
      const dow = cur.getDay();
      emps.forEach((e) => {
        const st = qcDayStatus(e.id, iso, clocks, reqs);
        // วันอาทิตย์ = วันหยุด (ข้ามถ้าไม่มีการลงเวลา)
        if (dow === 0 && !st.inT) return;
        let work = 0,ot = 0,status;
        if (st.inT) {
          if (st.outT) {
            const tot = Math.max(0, qcToMin(st.outT) - qcToMin(st.inT) - 60) / 60;
            work = Math.round(Math.min(8, tot) * 100) / 100;
            ot = Math.max(0, Math.round((tot - 8) * 10) / 10);
          } else work = 8;
          status = ot > 0 ? "OT" : "ทำงาน";
          came++;if (st.lateMin > 0) lateCnt++;
        } else if (st.leave) {
          status = st.leave.period === "ลาเช้า" ? "ลาเช้า" : st.leave.period === "ลาบ่าย" ? "ลาบ่าย" : "ลา";leave++;
        } else {
          status = "ขาด";absent++;
        }
        out.push({
          iso, emp: e.id, name: e.name, date: qcThaiOfISO(iso),
          in: st.inT, out: st.outT, break: st.inT ? 60 : 0,
          work, ot, late: st.lateMin, excuseLate: false, status, source: "QR"
        });
      });
      cur.setDate(cur.getDate() + 1);
    }
    setTimeRecs((prev) => {
      const keep = prev.filter((t) => !out.some((o) => (t.iso || "") === o.iso && t.emp === o.emp));
      return [...out, ...keep];
    });
    setLast({ from, to, total: out.length, came, absent, leave, lateCnt });
    if (setMonth && from.slice(0, 7)) setMonth(from.slice(0, 7));
    window.logActivity && window.logActivity("ดึงข้อมูลลงเวลา QR", `${qcThaiOfISO(from)} → ${qcThaiOfISO(to)} · ${out.length} รายการ`, "update");
    window.toast && window.toast(out.length ?
    `ดึงข้อมูลลงเวลา ${out.length} รายการ — มา ${came} · ลา ${leave} · ขาด ${absent} · สาย ${lateCnt} · แสดงในรายการด้านล่างแล้ว` :
    "ช่วงวันที่เลือกยังไม่มีพนักงานสแกน QR — ให้พนักงานสแกนที่หน้า ลงเวลา QR ก่อน");
    window.toast && window.toast(`ดึงข้อมูลลงเวลา ${out.length} รายการแล้ว — มา ${came} · ลา ${leave} · ขาด ${absent} · สาย ${lateCnt} — กด "คำนวนใหม่" เพื่อคิดเงินเดือน`);
  };

  return (
    <Card className="mb-lg" padded={false} flush>
      <div style={{ padding: "12px 14px", display: "flex", alignItems: "center", gap: 12, flexWrap: "wrap" }}>
        <div style={{ display: "flex", alignItems: "center", gap: 8 }}>
          <I.qr size={16} />
          <b style={{ fontSize: 13.5 }}>ดึงข้อมูลจากลงเวลา QR (รายวัน)</b>
        </div>
        <div style={{ display: "flex", alignItems: "center", gap: 6, fontSize: 12.5 }}>
          จาก <input className="input" type="date" value={from} onChange={(e) => setFrom(e.target.value)} style={{ height: 32, width: 145 }}></input>
          ถึง <input className="input" type="date" value={to} onChange={(e) => setTo(e.target.value)} style={{ height: 32, width: 145 }}></input>
        </div>
        <Btn icon={I.download} kind="primary" size="sm" onClick={pull}>ดึงข้อมูลการลงเวลา</Btn>
        <span className="spacer" />
        <span style={{ fontSize: 11.5, color: "var(--ink-3)" }}>
          {last ?
          <>ดึงล่าสุด {qcThaiOfISO(last.from)} → {qcThaiOfISO(last.to)} · <b style={{ color: "var(--ink-1)" }}>{last.total} รายการ</b> (มา {last.came} · ลา {last.leave} · <span style={{ color: "var(--danger)" }}>ขาด {last.absent}</span> · สาย {last.lateCnt})</> :
          "ไม่ลงเวลาและไม่มีใบลา = ขาดงาน · ลาที่อนุมัติแล้วนับเป็น \"ลา\" · แจ้งสายล่วงหน้าไม่หักสาย"}
        </span>
      </div>
    </Card>);

}
window.PayrollQRImport = PayrollQRImport;

window.QRClock = QRClockPage;