/* global React, I, UI, SSSData */
const { useState: useStateP3, useMemo: useMemoP3 } = React;

const STATUS_COLORS = {
  "ส่งงาน": "#15803d",
  "รอเก็บเงิน": "#0d9488",
  "กำลังผลิต": "#1d4ed8",
  "ตรวจสอบคุณภาพ": "#7c3aed",
  "ออกแบบ 3D": "#c2410c",
  "ร่าง": "#78716c"
};

const UPDATE_ICONS = {
  milestone: "🏁",
  progress: "▸",
  issue: "⚠",
  complete: "✓",
  purchase: "📦",
  cost: "💰"
};

// =============== Helpers ===============
function parseISO(s) { const [y, m, d] = s.split("-").map(Number); return new Date(y, m - 1, d); }
function fmtThaiDate(s) {
  const months = ["ม.ค.","ก.พ.","มี.ค.","เม.ย.","พ.ค.","มิ.ย.","ก.ค.","ส.ค.","ก.ย.","ต.ค.","พ.ย.","ธ.ค."];
  const d = parseISO(s);
  return `${d.getDate()} ${months[d.getMonth()]} ${d.getFullYear() + 543}`;
}
function daysBetween_d(a, b) { return Math.round((b - a) / 86400000); }

// =============== Gantt chart ===============
function GanttChart({ projects, onSelect, today }) {
  const THMON = ["ม.ค.","ก.พ.","มี.ค.","เม.ย.","พ.ค.","มิ.ย.","ก.ค.","ส.ค.","ก.ย.","ต.ค.","พ.ย.","ธ.ค."];
  const todayDate = today || new Date(); todayDate.setHours(0, 0, 0, 0);
  // ช่วงวันที่ — รวมวันนี้เข้าไปด้วย เผื่อทุกงานอยู่อนาคต/อดีต
  const allDates = projects.flatMap(p => [parseISO(p.start), parseISO(p.end)]).concat([todayDate]);
  let min = new Date(Math.min(...allDates));
  let max = new Date(Math.max(...allDates));
  const rawSpan = daysBetween_d(min, max) + 1;
  // ช่วงสั้น (≤ ~10 สัปดาห์) → แสดงเป็นรายสัปดาห์ให้ดูง่าย · ช่วงยาว → รายเดือน
  const weekMode = rawSpan <= 70;

  const cols = [];
  if (weekMode) {
    const startDow = (min.getDay() + 6) % 7;             // 0 = จันทร์
    min = new Date(min); min.setDate(min.getDate() - startDow);
    const endDow = (max.getDay() + 6) % 7;
    max = new Date(max); max.setDate(max.getDate() + (6 - endDow));
    let cur = new Date(min);
    while (cur <= max) {
      const ws = new Date(cur);
      cols.push({ days: 7, startDay: daysBetween_d(min, ws), label: `${ws.getDate()} ${THMON[ws.getMonth()]}`, sub: "สัปดาห์" });
      cur.setDate(cur.getDate() + 7);
    }
  } else {
    min.setDate(1);
    max = new Date(max.getFullYear(), max.getMonth() + 1, 0);
    let cur = new Date(min);
    while (cur <= max) {
      const ms = new Date(cur);
      const me = new Date(cur.getFullYear(), cur.getMonth() + 1, 0);
      cols.push({ days: (me - ms) / 86400000 + 1, startDay: daysBetween_d(min, ms), label: THMON[ms.getMonth()], sub: String(ms.getFullYear() + 543) });
      cur.setMonth(cur.getMonth() + 1);
    }
  }
  const totalDays = daysBetween_d(min, max) + 1;
  const todayOffset = daysBetween_d(min, todayDate);
  const minBarPct = weekMode ? 2.5 : 1.3;   // กันงานสั้นๆ จนมองไม่เห็น

  return (
    <div style={{display: "grid", gridTemplateColumns: "320px 1fr", border: "1px solid var(--line)", borderRadius: 8, overflow: "hidden", background: "var(--surface)"}}>
      {/* Header */}
      <div style={{borderRight: "1px solid var(--line)", borderBottom: "1px solid var(--line)", padding: "10px 12px", fontSize: 11, fontWeight: 600, textTransform: "uppercase", letterSpacing: "0.04em", color: "var(--ink-3)", background: "var(--surface-2)", display: "flex", alignItems: "center"}}>โปรเจกต์ · {weekMode ? "รายสัปดาห์" : "รายเดือน"}</div>
      <div style={{borderBottom: "1px solid var(--line)", background: "var(--surface-2)", position: "relative", overflow: "hidden"}}>
        <div style={{display: "flex", height: "100%"}}>
          {cols.map((m, i) => (
            <div key={i} style={{
              flexGrow: m.days,
              flexBasis: 0,
              borderLeft: i === 0 ? "0" : "1px solid var(--line)",
              padding: "8px 8px",
              fontSize: 11,
              fontWeight: 600,
              color: "var(--ink-2)",
              display: "flex",
              flexDirection: "column",
              whiteSpace: "nowrap",
              overflow: "hidden"
            }}>
              <span>{m.label}</span>
              <span style={{color: "var(--ink-4)", fontWeight: 400, fontSize: 9.5}}>{m.sub}</span>
            </div>
          ))}
        </div>
        {/* วันนี้ — ป้ายบนหัวตารางจุดเดียว */}
        {todayOffset >= 0 && todayOffset <= totalDays && (
          <div style={{position: "absolute", top: 4, left: `${(todayOffset / totalDays) * 100}%`, transform: "translateX(-50%)", fontSize: 9, color: "white", background: "var(--danger)", fontWeight: 700, padding: "1px 6px", borderRadius: 99, zIndex: 2, whiteSpace: "nowrap"}}>วันนี้</div>
        )}
      </div>

      {/* Body rows */}
      {projects.map((p, idx) => {
        const startOff = Math.max(0, daysBetween_d(min, parseISO(p.start)));
        const dur = daysBetween_d(parseISO(p.start), parseISO(p.end)) + 1;
        let left = (startOff / totalDays) * 100;
        let width = Math.max((dur / totalDays) * 100, minBarPct);
        if (left + width > 100) left = Math.max(0, 100 - width);
        const color = STATUS_COLORS[p.status] || "#78716c";
        return (
          <React.Fragment key={p.code}>
            <div style={{borderRight: "1px solid var(--line)", borderBottom: idx < projects.length-1 ? "1px solid var(--line-soft)" : 0, padding: "10px 12px", cursor: "pointer", display: "flex", alignItems: "center", gap: 8}} onClick={() => onSelect(p)}>
              <div style={{flex: 1, minWidth: 0}}>
                <div className="mono" style={{fontSize: 10.5, color: "var(--ink-3)"}}>{p.code}</div>
                <div style={{fontWeight: 500, fontSize: 12.5, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap", marginTop: 2}}>{p.name}</div>
                <div style={{fontSize: 10.5, color: "var(--ink-3)", marginTop: 1}}>{p.customer} · {p.lead}</div>
              </div>
            </div>
            <div style={{borderBottom: idx < projects.length-1 ? "1px solid var(--line-soft)" : 0, position: "relative", height: 56, padding: "8px 0"}}>
              {/* Grid lines */}
              <div style={{position: "absolute", inset: 0, display: "flex", pointerEvents: "none"}}>
                {cols.map((m, i) => (
                  <div key={i} style={{flexGrow: m.days, flexBasis: 0, borderLeft: i === 0 ? "0" : "1px dashed var(--line)"}}/>
                ))}
              </div>
              {/* Today line (เส้นเดียว ไม่มีป้ายซ้ำทุกแถว) */}
              {todayOffset >= 0 && todayOffset <= totalDays && (
                <div style={{position: "absolute", top: 0, bottom: 0, left: `${(todayOffset/totalDays)*100}%`, borderLeft: "1.5px dashed var(--danger)", zIndex: 1}}/>
              )}
              {/* Bar */}
              <div onClick={() => onSelect(p)} style={{
                position: "absolute",
                left: `${left}%`,
                width: `${width}%`,
                top: 10,
                bottom: 10,
                background: color + "26",
                border: `1.5px solid ${color}`,
                borderRadius: 5,
                cursor: "pointer",
                overflow: "hidden",
                display: "flex",
                alignItems: "center",
                padding: "0 8px",
                gap: 6
              }}>
                {/* Progress fill */}
                <div style={{position: "absolute", left: 0, top: 0, bottom: 0, width: `${p.progress}%`, background: color, opacity: 0.55}}/>
                <span style={{position: "relative", zIndex: 1, fontSize: 10.5, color, fontWeight: 600, whiteSpace: "nowrap"}}>{p.progress}%</span>
                <span style={{position: "relative", zIndex: 1, fontSize: 10.5, color: "var(--ink-1)", fontWeight: 500, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap"}}>{p.status}</span>
              </div>
            </div>
          </React.Fragment>
        );
      })}
    </div>
  );
}

// =============== Month calendar (redesigned) ===============
function MonthCalendar({ projects, onSelect, monthDate, onChangeMonth }) {
  const year = monthDate.getFullYear();
  const m = monthDate.getMonth();
  const firstDay = new Date(year, m, 1);
  const lastDay = new Date(year, m + 1, 0);
  const calStart = new Date(firstDay);
  calStart.setDate(firstDay.getDate() - firstDay.getDay());
  const calEnd = new Date(lastDay);
  calEnd.setDate(lastDay.getDate() + (6 - lastDay.getDay()));
  const weeks = Math.round((calEnd - calStart) / 86400000 / 7) + 1;
  const todayDate = new Date();

  const sorted = [...projects].sort((a, b) => parseISO(a.start) - parseISO(b.start));
  const visible = sorted.filter(p => {
    const ps = parseISO(p.start), pe = parseISO(p.end);
    return pe >= calStart && ps <= calEnd;
  });

  // Per-week segments with compact lane packing (no empty lanes)
  const weekData = [];
  for (let w = 0; w < weeks; w++) {
    const ws = new Date(calStart); ws.setDate(calStart.getDate() + w * 7);
    const we = new Date(ws); we.setDate(ws.getDate() + 6);
    const segs = [];
    visible.forEach(p => {
      const ps = parseISO(p.start), pe = parseISO(p.end);
      if (pe < ws || ps > we) return;
      const segStart = ps < ws ? ws : ps;
      const segEnd = pe > we ? we : pe;
      segs.push({
        project: p,
        colStart: segStart.getDay(),
        colSpan: segEnd.getDay() - segStart.getDay() + 1,
        startsHere: ps.getTime() === segStart.getTime(),
        endsHere: pe.getTime() === segEnd.getTime()
      });
    });
    segs.sort((a, b) => a.colStart - b.colStart || b.colSpan - a.colSpan);
    const laneEnd = [];
    segs.forEach(s => {
      let lane = laneEnd.findIndex(end => end < s.colStart);
      if (lane === -1) { lane = laneEnd.length; laneEnd.push(0); }
      laneEnd[lane] = s.colStart + s.colSpan - 1;
      s.lane = lane;
    });
    weekData.push({ segs, lanes: laneEnd.length });
  }

  const monthNames = ["มกราคม","กุมภาพันธ์","มีนาคม","เมษายน","พฤษภาคม","มิถุนายน","กรกฎาคม","สิงหาคม","กันยายน","ตุลาคม","พฤศจิกายน","ธันวาคม"];
  const headH = 32, barH = 24, barGap = 4;

  return (
    <div>
      {/* Toolbar */}
      <div style={{display: "flex", alignItems: "center", gap: 10, marginBottom: 14}}>
        <div style={{display: "flex", alignItems: "center", gap: 2, border: "1px solid var(--line)", borderRadius: 8, padding: 2, background: "var(--surface)"}}>
          <button className="icon-btn" onClick={() => onChangeMonth(-1)}><I.chevronLeft size={14}/></button>
          <button className="icon-btn" onClick={() => onChangeMonth(1)}><I.chevronRight size={14}/></button>
        </div>
        <div style={{fontSize: 17, fontWeight: 700, letterSpacing: "-0.01em"}}>{monthNames[m]} <span style={{color: "var(--ink-3)", fontWeight: 500}}>{year + 543}</span></div>
        <span className="spacer"/>
        <span style={{fontSize: 11.5, color: "var(--ink-3)"}}>{visible.length} โปรเจกต์ในเดือนนี้</span>
        <button className="btn sm" onClick={() => onChangeMonth("today")}>วันนี้</button>
      </div>

      <div style={{border: "1px solid var(--line)", borderRadius: 12, overflow: "hidden", background: "var(--surface)", boxShadow: "var(--shadow-sm, 0 1px 2px rgba(0,0,0,0.04))"}}>
        {/* Weekday header */}
        <div style={{display: "grid", gridTemplateColumns: "repeat(7, 1fr)", background: "var(--surface-2)", borderBottom: "1px solid var(--line)"}}>
          {["อาทิตย์","จันทร์","อังคาร","พุธ","พฤหัสฯ","ศุกร์","เสาร์"].map((d, i) => (
            <div key={d} style={{padding: "9px 12px", fontSize: 11, fontWeight: 700, color: (i === 0 || i === 6) ? "var(--ink-4)" : "var(--ink-3)", letterSpacing: "0.04em", borderLeft: i === 0 ? 0 : "1px solid var(--line-soft)", textAlign: "center"}}>{d}</div>
          ))}
        </div>

        {/* Week rows */}
        {weekData.map((wd, w) => {
          const rowH = Math.max(86, headH + wd.lanes * (barH + barGap) + 10);
          return (
            <div key={w} style={{position: "relative", borderBottom: w < weeks - 1 ? "1px solid var(--line)" : 0, height: rowH}}>
              {/* Day cells */}
              <div style={{display: "grid", gridTemplateColumns: "repeat(7, 1fr)", position: "absolute", inset: 0}}>
                {[...Array(7)].map((_, d) => {
                  const dayDate = new Date(calStart);
                  dayDate.setDate(calStart.getDate() + w * 7 + d);
                  const inMonth = dayDate.getMonth() === m;
                  const isToday = dayDate.toDateString() === todayDate.toDateString();
                  const weekend = d === 0 || d === 6;
                  return (
                    <div key={d} style={{
                      borderLeft: d === 0 ? 0 : "1px solid var(--line-soft)",
                      background: !inMonth ? "var(--surface-2)" : isToday ? "var(--info-bg)" : weekend ? "color-mix(in oklab, var(--surface-2) 55%, transparent)" : "transparent",
                      padding: "6px 8px",
                      display: "flex",
                      justifyContent: "flex-end",
                      alignItems: "flex-start"
                    }}>
                      <div style={{
                        minWidth: 24, height: 24,
                        borderRadius: 99,
                        background: isToday ? "var(--accent)" : "transparent",
                        color: isToday ? "var(--accent-ink, white)" : !inMonth ? "var(--ink-4)" : "var(--ink-2)",
                        display: "grid", placeItems: "center",
                        fontWeight: isToday ? 700 : 500,
                        fontSize: 12,
                        fontVariantNumeric: "tabular-nums",
                        padding: "0 4px"
                      }}>{dayDate.getDate()}</div>
                    </div>
                  );
                })}
              </div>

              {/* Bars */}
              <div style={{
                position: "absolute",
                top: headH,
                left: 4, right: 4,
                display: "grid",
                gridTemplateColumns: "repeat(7, 1fr)",
                gridAutoRows: `${barH}px`,
                rowGap: barGap,
                columnGap: 2,
                pointerEvents: "none"
              }}>
                {wd.segs.map((s, i) => {
                  const color = STATUS_COLORS[s.project.status] || "#78716c";
                  return (
                    <div
                      key={s.project.code + i}
                      onClick={() => onSelect(s.project)}
                      style={{
                        gridRowStart: s.lane + 1,
                        gridColumnStart: s.colStart + 1,
                        gridColumnEnd: s.colStart + 1 + s.colSpan,
                        background: color,
                        color: "white",
                        borderRadius: s.startsHere && s.endsHere ? 7 : s.startsHere ? "7px 2px 2px 7px" : s.endsHere ? "2px 7px 7px 2px" : 2,
                        padding: s.startsHere ? "0 8px 0 5px" : "0 8px",
                        fontSize: 11,
                        fontWeight: 500,
                        cursor: "pointer",
                        pointerEvents: "all",
                        overflow: "hidden",
                        display: "flex",
                        alignItems: "center",
                        gap: 6,
                        boxShadow: "0 1px 2px rgba(0,0,0,0.12)"
                      }}
                      title={`${s.project.code} · ${s.project.name}\n${fmtThaiDate(s.project.start)} → ${fmtThaiDate(s.project.end)}\nสถานะ: ${s.project.status} (${s.project.progress}%)`}
                    >
                      {s.startsHere ? (
                        <span style={{fontSize: 9, fontWeight: 700, background: "rgba(255,255,255,0.25)", padding: "1.5px 6px", borderRadius: 99, flexShrink: 0, letterSpacing: "0.03em"}}>#{s.project.code.split("-").pop()}</span>
                      ) : (
                        <span style={{fontSize: 9, opacity: 0.7, flexShrink: 0}}>‹</span>
                      )}
                      <span style={{overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap", flex: 1}}>{s.project.name}</span>
                      {s.endsHere && (
                        <span style={{marginLeft: "auto", fontSize: 9.5, fontWeight: 700, background: "rgba(255,255,255,0.25)", padding: "1.5px 6px", borderRadius: 99, flexShrink: 0}}>{s.project.progress}%</span>
                      )}
                    </div>
                  );
                })}
              </div>
            </div>
          );
        })}
      </div>

      {/* Project key */}
      {visible.length > 0 && (
        <div style={{marginTop: 12, display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(230px, 1fr))", gap: 6}}>
          {visible.map(p => {
            const color = STATUS_COLORS[p.status] || "#78716c";
            return (
              <div key={p.code} onClick={() => onSelect(p)} style={{
                display: "flex", alignItems: "center", gap: 8,
                padding: "7px 10px",
                background: "var(--surface)",
                border: "1px solid var(--line)",
                borderRadius: 7,
                cursor: "pointer",
                fontSize: 12
              }}>
                <span style={{width: 10, height: 10, borderRadius: 3, background: color, flexShrink: 0}}></span>
                <span className="mono" style={{fontSize: 10.5, color: "var(--ink-3)", flexShrink: 0}}>#{p.code.split("-").pop()}</span>
                <span style={{flex: 1, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap"}}>{p.name}</span>
                <span style={{fontSize: 10.5, color, fontWeight: 700, flexShrink: 0}}>{p.progress}%</span>
              </div>
            );
          })}
        </div>
      )}
    </div>
  );
}

// =============== Project detail drawer ===============
function ProjectDetailDrawer({ project, onClose, onChanged }) {
  const D = window.SSSData;
  const PX = window.ProjX;
  const { Btn, Badge, Card, Drawer, fmt0 } = window.UI;
  const [updateText, setUpdateText] = useStateP3("");
  const [extra] = useStateP3(() => PX.pxGet(project.code));
  const [userUpdates, setUserUpdates] = useStateP3(extra.updates || []);
  const [editIdx, _setEditIdx] = useStateP3(-1);
  const editIdxRef = React.useRef(-1);
  const setEditIdx = (v) => { editIdxRef.current = v; _setEditIdx(v); };
  const [editText, setEditText] = useStateP3("");
  const [editProg, setEditProg] = useStateP3(0);
  const [photos, setPhotos] = useStateP3([]);
  const [showCam, setShowCam] = useStateP3(false);
  const [galleryCam, setGalleryCam] = useStateP3(false);
  const [gallery, setGallery] = useStateP3(extra.gallery || []);
  const [workOrders, setWorkOrders] = useStateP3(extra.workOrders || []);
  const [extraExpenses, setExtraExpenses] = useStateP3(extra.expenses || []);
  // เทียบรายการเดียวกัน (ใช้ id ถ้ามี ไม่งั้นเทียบเนื้อหา) — สำหรับแก้/ลบกับข้อมูลสดโดยไม่พึ่ง index
  const sameRec = (a, b) => a === b || (a && b && a.id && b.id ? a.id === b.id : JSON.stringify(a) === JSON.stringify(b));
  // ดึงค่าอาเรย์ล่าสุดจาก store ณ เวลาเขียน — กันสถานะเก่าของหน้าจอทับรายการที่เครื่องอื่นเพิ่งเพิ่ม
  const freshArr = (field, fallback) => {
    const v = PX.pxGet(project.code)[field];
    return Array.isArray(v) ? v : (fallback || []);
  };
  const [showWO, setShowWO] = useStateP3(false);
  const [showEdit, setShowEdit] = useStateP3(false);
  const [showExp, setShowExp] = useStateP3(false);
  const [evalFor, setEvalFor] = useStateP3(null);
  const [ddFor, setDdFor] = useStateP3(null);
  const [selWO, setSelWO] = useStateP3(() => new Set());
  const toggleWO = (no) => setSelWO(prev => { const n = new Set(prev); n.has(no) ? n.delete(no) : n.add(no); return n; });
  // รวมรายการส่งมอบจากหลายใบสั่งงาน → ใบส่งของประจำวันใบเดียว (รวมจำนวนของชื่อ+หน่วยเดียวกัน)
  const buildCombinedWO = (wos) => {
    const map = new Map();
    wos.forEach(w => (w.items || []).forEach(it => {
      const name = (it.name || "").trim();
      if (!name) return;
      const key = name + "|" + (it.unit || "ชิ้น");
      const ex = map.get(key);
      if (ex) ex.qty += (Number(it.qty) || 0);
      else map.set(key, { name, qty: Number(it.qty) || 1, unit: it.unit || "ชิ้น" });
    }));
    return {
      no: wos.map(w => w.no).join(", "),
      items: [...map.values()],
      assigneeIds: [...new Set(wos.flatMap(w => w.assigneeIds || []))],
      assignees: [...new Set(wos.flatMap(w => w.assignees || []))],
      detail: wos.map(w => `[${w.no}] ${w.detail || ""}`.trim()).join("\n")
    };
  };
  const openMultiDD = () => {
    const chosen = workOrders.filter(w => selWO.has(w.no));
    if (!chosen.length) { window.toast && window.toast("ติ๊กเลือกใบสั่งงานอย่างน้อย 1 ใบก่อน"); return; }
    setDdFor(chosen.length === 1 ? chosen[0] : buildCombinedWO(chosen));
  };
  const [editExp, setEditExp] = useStateP3(null);
  const [evals, setEvals] = useStateP3(() => window.WorkEvalStore ? window.WorkEvalStore.byProject(project.code) : []);
  const [progress, setProgress] = useStateP3(project.progress);
  const [statusVal, setStatusVal] = useStateP3(project.status);

  const updates = [...userUpdates, ...(project.updates || [])];
  const linkedExtra = (window.ProjX.pxGet(project.code).linkedExpenses) || [];
  const expenses = D.Expenses.filter(e => (project.expenseLinks || []).includes(e.no) || linkedExtra.includes(e.no));
  const totalExpense = expenses.reduce((s, e) => s + e.amount, 0) + extraExpenses.reduce((s, e) => s + (+e.amount || 0), 0);
  const totalCost = (project.actual || 0) + totalExpense;   // ต้นทุนจริง = ที่ลงไว้ + ค่าใช้จ่ายที่เบิก/ผูก
  const left = project.budget - totalCost;                  // กำไรขั้นต้น = มูลค่างาน − ต้นทุนรวม
  const overBudget = totalCost > project.budget;

  // คืนสต็อก + ปลดสถานะรอซื้อ เมื่อลบค่าใช้จ่าย
  const reverseExpenseEffects = (e) => {
    const D = window.SSSData;
    const now = new Date().toLocaleString("th-TH", { day: "2-digit", month: "2-digit", year: "2-digit", hour: "2-digit", minute: "2-digit" });
    const stockLines = (e.lines && e.lines.length)
      ? e.lines.filter(l => l.sku && (Number(l.qty) || 0) > 0).map(l => ({ sku: l.sku, name: l.name, qty: Number(l.qty) || 0 }))
      : (e.sku && (Number(e.used || e.qty) || 0) > 0 ? [{ sku: e.sku, name: e.vendor, qty: Number(e.used || e.qty) || 0 }] : []);
    let touched = false;
    stockLines.forEach(l => {
      const prod = (D.Products || []).find(p => p.sku === l.sku);
      if (prod) {
        prod.stock = Math.round(((Number(prod.stock) || 0) + l.qty) * 100) / 100;
        D.InventoryMoves = D.InventoryMoves || [];
        D.InventoryMoves.unshift({ date: now, type: "รับคืน", sku: l.sku, name: l.name, qty: l.qty, ref: project.code + " (ยกเลิกค่าใช้จ่าย)", by: window.actorName ? window.actorName() : "" });
        touched = true;
      }
    });
    if (touched) { window.dispatchEvent(new Event("sss-products-changed")); window.dispatchEvent(new Event("sss-stock-changed")); }
    // ปลดสถานะรอซื้อกลับเป็น "รอซื้อ"
    const prIds = (e.lines || []).filter(l => l.prId).map(l => l.prId);
    if (prIds.length && window.SSSData.PurchaseRequests) {
      const set = new Set(prIds);
      window.SSSData.PurchaseRequests = window.SSSData.PurchaseRequests.map(r => set.has(r.id) ? { ...r, status: "รอซื้อ", expenseRef: "", boughtAt: "" } : r);
      window.dispatchEvent(new Event("sss-purchase-requests-changed"));
    }
  };
  const removeExtraExpense = async (i) => {
    const e = extraExpenses[i];
    const ok = await (window.confirmDelete ? window.confirmDelete(`ค่าใช้จ่าย ${e.vendor || ""}`, `฿${fmt0(+e.amount || 0)}`) : Promise.resolve(confirm("ลบ?")));
    if (!ok) return;
    reverseExpenseEffects(e);
    const list = freshArr("expenses", extraExpenses).filter(x => !sameRec(x, e));
    setExtraExpenses(list);
    PX.pxSet(project.code, { expenses: list });
    window.toast && window.toast("ลบค่าใช้จ่ายแล้ว — คืนสต็อก/สถานะให้อัตโนมัติ");
  };
  const saveEditExp = (next, i) => {
    const old = extraExpenses[i];
    const fresh = freshArr("expenses", extraExpenses);
    const list = fresh.some(x => sameRec(x, old)) ? fresh.map(x => sameRec(x, old) ? next : x) : [next, ...fresh];
    setExtraExpenses(list);
    PX.pxSet(project.code, { expenses: list });
    setEditExp(null);
    window.toast && window.toast("บันทึกการแก้ไขค่าใช้จ่ายแล้ว");
  };
  // ใบส่งของประจำวันของงานนี้ (รีเฟรชเมื่อมีการเปลี่ยน)
  const [, forceDD] = useStateP3(0);
  React.useEffect(() => {
    const h = () => forceDD(n => n + 1);
    window.addEventListener("sss-deliveries-changed", h);
    window.addEventListener("sss-data-synced", h);
    return () => { window.removeEventListener("sss-deliveries-changed", h); window.removeEventListener("sss-data-synced", h); };
  }, []);
  // รับข้อมูลโปรเจกต์ที่เครื่องอื่นเพิ่ม/แก้เข้ามาแสดงสด — กันหน้าจอค้างของเก่าแล้วเขียนทับ
  React.useEffect(() => {
    const h = () => {
      const ex = PX.pxGet(project.code);
      if (Array.isArray(ex.workOrders)) setWorkOrders(ex.workOrders);
      if (Array.isArray(ex.gallery)) setGallery(ex.gallery);
      if (Array.isArray(ex.expenses)) setExtraExpenses(ex.expenses);
      // อัปเดตงาน: ไม่รีเฟรชระหว่างกำลังแก้ inline (index จะเพี้ยน)
      if (Array.isArray(ex.updates) && editIdxRef.current < 0) setUserUpdates(ex.updates);
    };
    window.addEventListener("sss-localstore-polled", h);
    return () => window.removeEventListener("sss-localstore-polled", h);
  }, []);
  const projDeliveries = (window.SSSData.DailyDeliveries || []).filter(d => d.ref === project.code || (d.note || "").includes(project.code));

  // ออกเอกสารขายจากโปรเจกต์ — ดึงลูกค้า + รายการส่งมอบจากใบสั่งงาน แล้วผูกกับงานนี้
  const openSalesDoc = (type) => {
    const woItems = (workOrders || []).flatMap(wo => (wo.items || []).map(it => ({ name: it.name, qty: it.qty || 1, unit: it.unit || "ชิ้น", price: 0 })));
    const blank = { name: "", qty: 1, unit: "ชิ้น", price: 0 };
    const lines = woItems.length ? [...woItems, blank] : [{ name: project.name, qty: 1, unit: "งาน", price: project.budget || 0 }, blank];
    const note = (workOrders || []).map(wo => (wo.detail || "").trim()).filter(Boolean).join("\n");
    onClose && onClose();
    setTimeout(() => window.openNewDoc && window.openNewDoc(type, { partyName: project.customer, project: project.code, projectName: project.name, lines, notes: note || undefined }), 220);
  };
  const color = STATUS_COLORS[project.status] || "#78716c";

  const days = daysBetween_d(parseISO(project.start), parseISO(project.end)) + 1;
  const elapsed = Math.max(0, daysBetween_d(parseISO(project.start), new Date(2026, 4, 22)));
  const remaining = days - elapsed;

  const addUpdate = () => {
    const changed = updateText.trim() || photos.length > 0 || progress !== project.progress || statusVal !== project.status;
    if (!changed) { window.toast && window.toast("ยังไม่มีอะไรเปลี่ยนแปลง"); return; }
    if (photos.length === 0) {
      window.toast && window.toast("📷 ต้องถ่ายรูป/แนบรูปหน้างานอย่างน้อย 1 รูป ก่อนบันทึกอัปเดต");
      return;
    }
    const progressNote = progress !== project.progress ? ` · อัปเดต ${project.progress}% → ${progress}%` : "";
    const statusNote = statusVal !== project.status ? ` · เปลี่ยนสถานะ "${project.status}" → "${statusVal}"` : "";
    const text = (updateText.trim() || "อัปเดตความคืบหน้า") + progressNote + statusNote;
    const u = {
      id: "u" + Date.now().toString(36) + Math.random().toString(36).slice(2, 6),   // identity ตอน merge — กันอัปเดตวันเดียวกัน/ข้อความเดียวกันถูกยุบรวม
      date: PX.pxTodayISO(), who: (window.Session && window.Session.get() && window.Session.get().name) || (window.SSSData.Company.currentUser && window.SSSData.Company.currentUser.name) || "", text,
      type: progress === 100 ? "complete" : statusVal !== project.status ? "milestone" : "progress",
      photos: [...photos], progressFrom: project.progress, progressTo: progress
    };
    const nextUser = [u, ...freshArr("updates", userUpdates)];
    setUserUpdates(nextUser);
    PX.pxSet(project.code, { updates: nextUser, progress, status: statusVal });
    project.progress = progress;
    project.status = statusVal;
    setUpdateText("");
    setPhotos([]);
    onChanged && onChanged();
    window.logActivity && window.logActivity("อัปเดตโปรเจกต์", `${project.code} → ${progress}% (แนบ ${u.photos.length} รูป)`, "update");
    window.toast && window.toast("บันทึกอัปเดตพร้อมรูป " + u.photos.length + " รูปแล้ว");
  };

  // แก้ไข/ลบ ได้เฉพาะอัปเดตที่ผู้ใช้สร้างเอง (userUpdates) — อัปเดตตั้งต้นของโปรเจกต์อ่านอย่างเดียว
  const persistUpdates = (next) => {
    setUserUpdates(next);
    // คำนวณ % ปัจจุบันใหม่จากอัปเดตล่าสุดที่ยังเหลือและมีค่า progressTo (ไม่มีเลย = 0%)
    const all = [...next, ...(project.updates || [])];
    const withProg = all.find(u => u && u.progressTo !== undefined && u.progressTo !== null);
    const newProg = withProg ? withProg.progressTo : 0;
    const patch = { updates: next };
    if (newProg !== project.progress) {
      project.progress = newProg;
      setProgress(newProg);
      patch.progress = newProg;
    }
    PX.pxSet(project.code, patch);
    onChanged && onChanged();
  };
  const startEditUpdate = (i) => {
    if (i >= userUpdates.length) return;   // อ่านอย่างเดียว
    const u = userUpdates[i];
    setEditIdx(i);
    setEditText(u.text || "");
    setEditProg(u.progressTo !== undefined ? u.progressTo : project.progress);
  };
  const cancelEditUpdate = () => { setEditIdx(-1); setEditText(""); };
  const saveEditUpdate = () => {
    if (editIdx < 0 || editIdx >= userUpdates.length) return;
    const target = userUpdates[editIdx];
    const p = Math.max(0, Math.min(100, Number(editProg) || 0));
    const edited = { ...target, text: editText.trim() || target.text, progressTo: p, edited: true };
    // แก้กับข้อมูลสด — จับคู่ด้วย id/เนื้อหา ไม่ใช่ index (เครื่องอื่นอาจเพิ่มรายการระหว่างนี้)
    const fresh = freshArr("updates", userUpdates);
    const next = fresh.some(x => sameRec(x, target)) ? fresh.map(x => sameRec(x, target) ? edited : x) : [edited, ...fresh];
    persistUpdates(next);
    setEditIdx(-1);
    setEditText("");
    window.toast && window.toast("แก้ไขอัปเดตแล้ว");
  };
  const deleteUpdate = async (i) => {
    if (i >= userUpdates.length) return;   // ลบได้เฉพาะที่สร้างเอง
    const u = userUpdates[i];
    const ok = await (window.confirmDelete ? window.confirmDelete("ลบรายการอัปเดตนี้?", (u.text || "").slice(0, 60)) : Promise.resolve(window.confirm("ลบรายการอัปเดตนี้?")));
    if (!ok) return;
    const next = freshArr("updates", userUpdates).filter(x => !sameRec(x, u));
    persistUpdates(next);
    if (editIdx === i) { setEditIdx(-1); setEditText(""); }
    window.toast && window.toast("ลบอัปเดตแล้ว");
  };

  const resize = (f, max) => window.DeliveryStore && window.DeliveryStore.resizeImage
    ? window.DeliveryStore.resizeImage(f, max)
    : new Promise(res => { const r = new FileReader(); r.onload = () => res(r.result); r.readAsDataURL(f); });

  const onPickFile = async (e) => {
    const files = [...e.target.files].slice(0, 6);
    e.target.value = "";
    const loaded = [];
    for (const f of files) {
      const url = await resize(f, 600);
      if (url) loaded.push({ name: f.name, url });
    }
    setPhotos(p => [...p, ...loaded].slice(0, 6));
  };

  // เทียบ g (ผลจากหน้าจอ) กับสถานะเดิม → ได้ "เพิ่มอะไร/ลบอะไร" แล้วนำไปใช้กับข้อมูลสดจาก store
  // กันรูปที่เครื่องอื่นเพิ่งอัปโหลดหายเพราะหน้าจอนี้ค้างข้อมูลเก่า
  const saveGallery = (g) => {
    const urlOf = (x) => (x && x.url) || "";
    const prevUrls = new Set(gallery.map(urlOf));
    const nextUrls = new Set(g.map(urlOf));
    const removed = new Set([...prevUrls].filter(u => !nextUrls.has(u)));
    removed.forEach(u => { if (u && window.markPhotoDeleted) window.markPhotoDeleted(u); });
    const fresh = freshArr("gallery", gallery).filter(x => !removed.has(urlOf(x)));
    const haveUrls = new Set(fresh.map(urlOf));
    const merged = [...fresh, ...g.filter(x => !prevUrls.has(urlOf(x)) && !haveUrls.has(urlOf(x)))];
    setGallery(merged);
    PX.pxSet(project.code, { gallery: merged });
  };
  const onPickGallery = async (e) => {
    const files = [...e.target.files].slice(0, 12);
    e.target.value = "";
    const loaded = [];
    for (const f of files) {
      const url = await resize(f, 600);
      if (url) loaded.push({ name: f.name, url, date: PX.pxToday() });
    }
    saveGallery([...gallery, ...loaded]);
    if (loaded.length) window.toast && window.toast(`เพิ่ม ${loaded.length} รูปเข้าโปรเจกต์แล้ว`);
  };

  return (
    <Drawer open={true} onClose={onClose} title={`${project.code} · ${project.name}`} wide
      footer={<>
        <Btn icon={I.edit} onClick={() => setShowEdit(true)}>แก้ไขโปรเจกต์</Btn>
        <Btn icon={I.doc} onClick={() => setShowWO(true)}>ออกใบสั่งงาน</Btn>
        <Btn kind="primary" icon={I.print} onClick={() => PX.printJobSummaryDoc(project)}>สรุปงาน</Btn>
      </>}>
      {/* Hero */}
      <div style={{padding: 14, background: color + "10", border: `1px solid ${color}50`, borderRadius: 8, marginBottom: 14}}>
        <div style={{display: "flex", justifyContent: "space-between", alignItems: "flex-start", gap: 12, marginBottom: 10}}>
          <div>
            <div className="mono" style={{fontSize: 11, color: "var(--ink-3)"}}>{project.code}</div>
            <div style={{fontSize: 16, fontWeight: 600, marginTop: 2}}>{project.name}</div>
            <div style={{fontSize: 12, color: "var(--ink-3)", marginTop: 2}}>{project.customer} · หัวหน้างาน {project.lead}</div>
          </div>
          <Badge tone="info" style={{background: color, color: "white"}}>{project.status}</Badge>
        </div>
        <div style={{display: "flex", alignItems: "center", gap: 12, marginTop: 8}}>
          <div style={{flex: 1, height: 8, background: "var(--surface)", borderRadius: 999, overflow: "hidden", border: "1px solid var(--line)"}}>
            <div style={{height: "100%", width: `${project.progress}%`, background: color, borderRadius: 999}}/>
          </div>
          <span style={{fontSize: 13, fontWeight: 600}}>{project.progress}%</span>
        </div>
      </div>

      {/* KPI grid */}
      <div className="grid cols-4 mb-lg" style={{gap: 8}}>
        <KPI label="งบประมาณ" value={`฿${fmt0(project.budget)}`}/>
        <KPI label="ต้นทุนใช้ไป" value={`฿${fmt0(project.actual)}`} tone="warning"/>
        <KPI label={overBudget ? "เกินงบ" : "คงเหลือ"} value={`฿${fmt0(Math.abs(left))}`} tone={overBudget ? "danger" : "success"}/>
        <KPI label="กำไรคาดการณ์" value={`฿${fmt0(left * 0.85)}`} tone="success"/>
      </div>

      <div className="grid cols-3 mb-lg" style={{gap: 8}}>
        <KPI label="เริ่ม" value={fmtThaiDate(project.start)}/>
        <KPI label="ครบกำหนด" value={fmtThaiDate(project.end)} tone={project.progress < 100 && remaining < 7 ? "warning" : ""}/>
        <KPI label="ระยะเวลา" value={`${days} วัน · เหลือ ${Math.max(0, remaining)}`}/>
      </div>

      {/* Work orders */}
      <Card title="ใบสั่งงาน · Work Orders" sub={`${workOrders.length} ใบ${selWO.size ? ` · เลือก ${selWO.size}` : ""}`} className="mb-lg" flush
        right={<div style={{display: "flex", gap: 6, alignItems: "center"}}>
          {selWO.size > 0 && <Btn size="sm" icon={I.truck} onClick={openMultiDD}>ออกใบส่งของ ({selWO.size})</Btn>}
          <Btn size="sm" icon={I.plus} onClick={() => setShowWO(true)}>ออกใบสั่งงาน</Btn>
        </div>}>
        {workOrders.length === 0 ? (
          <div style={{padding: 16, textAlign: "center", color: "var(--ink-3)", fontSize: 12.5}}>ยังไม่มีใบสั่งงาน — กด "ออกใบสั่งงาน" เพื่อกำหนดผู้รับผิดชอบ รายละเอียดงาน และกำหนดส่ง</div>
        ) : (
          <div style={{display: "flex", flexDirection: "column", gap: 10, padding: 12}}>
            {workOrders.map((wo, i) => {
              const priColor = wo.priority === "ด่วนมาก" ? "var(--danger)" : wo.priority === "ด่วน" ? "var(--warning)" : "var(--ink-4)";
              const empPhotos = (window.lsSyncGet && window.lsSyncGet("sss-employee-photos")) || {};
              const people = (wo.assignees && wo.assignees.length ? wo.assignees : (wo.assignee ? String(wo.assignee).split(", ") : []));
              const ids = wo.assigneeIds || [];
              const nickOf = (n) => { const m = /\(([^)]+)\)/.exec(n || ""); return m ? m[1] : String(n || "").trim().split(/\s+/)[0]; };
              const avatarOf = (name, idx) => {
                const prof = window.ChatProfiles ? window.ChatProfiles.get(name) : null;
                const photo = (ids[idx] && empPhotos[ids[idx]]) || (prof && prof.avatar) || "";
                const color = (prof && prof.color) || "var(--accent)";
                return photo
                  ? <img key={idx} src={photo} alt={name} title={name} style={{width: 34, height: 34, borderRadius: "50%", objectFit: "cover", border: `2px solid ${color}`}}/>
                  : <span key={idx} title={name} style={{width: 34, height: 34, borderRadius: "50%", background: color, color: "white", display: "grid", placeItems: "center", fontSize: 12, fontWeight: 700}}>{String(name || "?").slice(0, 2)}</span>;
              };
              const abtn = { display: "inline-flex", alignItems: "center", gap: 6, padding: "7px 12px", borderRadius: 8, border: "1px solid var(--line-strong)", background: "var(--surface)", color: "var(--ink-2)", fontSize: 12.5, fontWeight: 600, cursor: "pointer", fontFamily: "inherit" };
              return (
                <div key={wo.no + i} style={{border: `1px solid ${selWO.has(wo.no) ? "var(--info)" : "var(--line)"}`, borderLeft: `5px solid ${priColor}`, borderRadius: 10, background: selWO.has(wo.no) ? "var(--surface-2)" : "var(--surface)", padding: "10px 12px"}}>
                  {/* แถวบน: เลือก + เลขที่ + กำหนดส่ง + ความสำคัญ */}
                  <div style={{display: "flex", alignItems: "center", gap: 10, flexWrap: "wrap"}}>
                    <input type="checkbox" style={{width: 17, height: 17}} checked={selWO.has(wo.no)} onChange={() => toggleWO(wo.no)} title="เลือกเพื่อออกใบส่งของรวม"/>
                    <b className="mono" style={{fontSize: 14.5}}>{wo.no}</b>
                    <Badge tone={wo.priority === "ด่วนมาก" ? "danger" : wo.priority === "ด่วน" ? "warning" : "outline"}>{wo.priority}</Badge>
                    <span style={{fontSize: 12.5, color: "var(--ink-3)"}}>📅 ส่ง <b style={{color: "var(--ink-1)"}}>{PX.pxThaiDate(wo.due)}</b></span>
                  </div>
                  {/* ผู้รับผิดชอบ: รูปหน้า + ชื่อเล่น บรรทัดเดียว */}
                  <div style={{display: "flex", alignItems: "center", gap: 6, flexWrap: "wrap", marginTop: 9}}>
                    {people.slice(0, 8).map((n, j) => avatarOf(n, j))}
                    {people.length > 8 && <span style={{width: 34, height: 34, borderRadius: "50%", background: "var(--surface-2)", color: "var(--ink-3)", display: "grid", placeItems: "center", fontSize: 11, fontWeight: 800}}>+{people.length - 8}</span>}
                    <span style={{fontSize: 12, color: "var(--ink-3)", marginLeft: 4}}>{people.map(nickOf).join(" · ")}</span>
                  </div>
                  {/* รายละเอียด */}
                  {wo.detail && <div style={{fontSize: 12.5, color: "var(--ink-2)", marginTop: 8, whiteSpace: "pre-wrap", lineHeight: 1.5, maxHeight: 60, overflow: "hidden"}}>{wo.detail}</div>}
                  {/* ปุ่มใหญ่ มีป้ายชื่อ */}
                  <div style={{display: "flex", gap: 8, flexWrap: "wrap", marginTop: 10}}>
                    <button style={abtn} onClick={() => setDdFor(wo)}><I.truck size={15}/> ออกใบส่งของ</button>
                    <button style={abtn} onClick={() => PX.printWorkOrderDoc(project, wo)}><I.print size={15}/> พิมพ์</button>
                    <button style={abtn} onClick={() => setShowWO(wo)}><I.edit size={15}/> แก้ไข</button>
                    <button style={abtn} onClick={() => setEvalFor(wo)}><I.sparkle size={15}/> ประเมินงาน</button>
                    <button style={{...abtn, color: "var(--danger)", marginLeft: "auto"}} onClick={async () => {
                      const ok = await window.confirmDelete(`ใบสั่งงาน ${wo.no}`, wo.detail.slice(0, 60));
                      if (!ok) return;
                      const list = freshArr("workOrders", workOrders).filter(w => w.no !== wo.no);
                      setWorkOrders(list);
                      PX.pxSet(project.code, { workOrders: list });
                    }}><I.trash size={15}/> ลบ</button>
                  </div>
                </div>
              );
            })}
          </div>
        )}
      </Card>

      {/* ใบประเมินงาน */}
      <Card title="ใบประเมินงาน · Evaluations" sub={`${evals.length} ใบ · ออกหลังงานเสร็จ — คะแนนรวมเข้า KPI อัตโนมัติ`} className="mb-lg" flush>
        {evals.length === 0 ? (
          <div style={{padding: 14, textAlign: "center", color: "var(--ink-3)", fontSize: 12.5}}>ยังไม่มีใบประเมินงาน — กดปุ่ม ✧ ที่ใบสั่งงานด้านบนเพื่อให้คะแนนผู้รับผิดชอบ</div>
        ) : (
          <table className="t">
            <thead><tr><th>เลขที่</th><th>ใบสั่งงาน</th><th>วันที่</th><th>ผู้ถูกประเมิน</th><th className="right">คะแนนเฉลี่ย</th><th></th></tr></thead>
            <tbody>
              {evals.map(ev => (
                <tr key={ev.no}>
                  <td className="mono">{ev.no}</td>
                  <td className="mono muted">{ev.woNo}</td>
                  <td>{ev.date}</td>
                  <td className="muted" style={{fontSize: 12, maxWidth: 200, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap"}}>{ev.rows.map(r => r.name).join(", ")}</td>
                  <td className="right"><b style={{color: ev.avg >= 4 ? "var(--success)" : ev.avg >= 3 ? "var(--warning)" : "var(--danger)"}}>{Number(ev.avg).toFixed(1)}/5</b></td>
                  <td>
                    <div style={{display: "flex", gap: 2, justifyContent: "flex-end"}}>
                      <button className="icon-btn" title="พิมพ์ใบประเมิน" onClick={() => window.printWorkEvalDoc && window.printWorkEvalDoc(ev)}><I.print size={13}/></button>
                      <button className="icon-btn" title="ลบ" onClick={async () => {
                        const ok = await window.confirmDelete(`ใบประเมินงาน ${ev.no}`, ev.woNo);
                        if (!ok) return;
                        window.WorkEvalStore.remove(ev.no);
                        setEvals(window.WorkEvalStore.byProject(project.code));
                      }}><I.trash size={13}/></button>
                    </div>
                  </td>
                </tr>
              ))}
            </tbody>
          </table>
        )}
      </Card>

      {/* Status updates */}
      <Card title="ความคืบหน้า · Status Updates" sub={`${updates.length} รายการ · ต้องแนบรูปทุกครั้งที่อัปเดต`} className="mb-lg">
        <div style={{padding: 14, background: "var(--surface-2)", border: "1px solid var(--line)", borderRadius: 8, marginBottom: 12}}>
          <div style={{display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: 8}}>
            <div className="label">ความคืบหน้าปัจจุบัน</div>
            <div style={{fontSize: 22, fontWeight: 700, color, fontVariantNumeric: "tabular-nums"}}>{progress}%</div>
          </div>
          <div style={{position: "relative", marginBottom: 6}}>
            <input
              type="range" min="0" max="100" step="1" value={progress}
              onChange={e => setProgress(+e.target.value)}
              className="progress-range"
              style={{
                width: "100%", height: 18, cursor: "pointer",
                appearance: "none", WebkitAppearance: "none",
                background: `linear-gradient(to right, ${color} 0%, ${color} ${progress}%, var(--surface-3) ${progress}%, var(--surface-3) 100%)`,
                borderRadius: 999,
                outline: "none"
              }}
            />
          </div>
          <div style={{display: "flex", justifyContent: "space-between", fontSize: 10, color: "var(--ink-3)", marginBottom: 12, padding: "0 2px"}}>
            {[0, 25, 50, 75, 100].map(t => (
              <button key={t} onClick={() => setProgress(t)} style={{background: "transparent", border: 0, color: progress >= t ? color : "var(--ink-3)", cursor: "pointer", fontSize: 10, fontWeight: progress === t ? 700 : 400, padding: 0}}>{t}%</button>
            ))}
          </div>

          <div style={{display: "grid", gridTemplateColumns: "1fr 1fr", gap: 10, marginBottom: 10}}>
            <div className="field"><label style={{fontSize: 11, color: "var(--ink-3)", marginBottom: 4, display: "block"}}>สถานะ</label>
              <select className="input" value={statusVal} onChange={e=>setStatusVal(e.target.value)} style={{height: 32}}>
                <option>ร่าง</option>
                <option>ออกแบบ 3D</option>
                <option>กำลังผลิต</option>
                <option>ตรวจสอบคุณภาพ</option>
                <option>รอเก็บเงิน</option>
                <option>ส่งงาน</option>
              </select>
            </div>
            <div style={{display: "flex", alignItems: "flex-end", gap: 6}}>
              {[25, 50, 75, 100].map(t => (
                <button key={t} onClick={() => setProgress(t)}
                  className="btn sm"
                  style={{flex: 1, ...(progress === t ? {background: color, color: "white", border: 0} : {})}}>
                  {t}%
                </button>
              ))}
            </div>
          </div>

          <div style={{display: "flex", flexDirection: "column", gap: 8}}>
            <textarea className="textarea" placeholder="พิมพ์โน้ตอัปเดต (ไม่บังคับ)…" value={updateText} onChange={e=>setUpdateText(e.target.value)} style={{minHeight: 60}}/>
            <div style={{display: "flex", gap: 8, alignItems: "center", flexWrap: "wrap"}}>
              <Btn size="sm" icon={I.camera} onClick={() => setShowCam(true)}>ถ่ายรูป</Btn>
              <label className="btn sm" style={{cursor: "pointer"}}>
                <I.paperclip className="icon" size={13}/>
                แนบรูป
                <input type="file" accept="image/*" multiple style={{display: "none"}} onChange={onPickFile}/>
              </label>
              <span style={{fontSize: 11, color: photos.length === 0 ? "var(--warning)" : "var(--success)", fontWeight: 500}}>
                {photos.length === 0 ? "* ต้องมีรูป ณ ตอนอัพเดทอย่างน้อย 1 รูป" : `✓ ${photos.length} รูปพร้อมบันทึก`}
              </span>
              <span className="spacer"/>
              <Btn kind="primary" icon={I.check} onClick={addUpdate}>บันทึกอัปเดต</Btn>
            </div>
            {photos.length > 0 && (
              <div style={{display: "flex", gap: 6, flexWrap: "wrap"}}>
                {photos.map((p, i) => (
                  <div key={i} style={{position: "relative", width: 60, height: 60, borderRadius: 4, overflow: "hidden", border: "1px solid var(--line)"}}>
                    <img src={p.url} alt={p.name} style={{width: "100%", height: "100%", objectFit: "cover"}}/>
                    <button onClick={() => setPhotos(ph => { const t = ph[i]; if (t && t.url && window.markPhotoDeleted) window.markPhotoDeleted(t.url); return ph.filter((_, j) => j !== i); })}
                      style={{position: "absolute", top: 2, right: 2, width: 16, height: 16, padding: 0, borderRadius: "50%", background: "rgba(0,0,0,0.7)", color: "white", border: 0, cursor: "pointer", fontSize: 10}}>×</button>
                  </div>
                ))}
              </div>
            )}
          </div>
        </div>

        {/* Timeline */}
        <div style={{position: "relative", paddingLeft: 18}}>
          <div style={{position: "absolute", left: 6, top: 6, bottom: 6, width: 2, background: "var(--line)"}}/>
          {updates.map((u, i) => {
            const editable = i < userUpdates.length;   // เฉพาะอัปเดตที่ผู้ใช้สร้างเอง
            const editing = editable && editIdx === i;
            return (
            <div key={i} style={{position: "relative", padding: "10px 0", display: "flex", gap: 10}}>
              <div style={{position: "absolute", left: -16, top: 14, width: 14, height: 14, borderRadius: "50%", background: u.type === "complete" ? "var(--success)" : u.type === "issue" ? "var(--danger)" : u.type === "milestone" ? color : "var(--ink-4)", border: "3px solid var(--surface)"}}/>
              <div style={{flex: 1}}>
                {editing ? (
                  <div style={{display: "flex", flexDirection: "column", gap: 6}}>
                    <textarea value={editText} onChange={e => setEditText(e.target.value)} rows={2}
                      style={{width: "100%", padding: "6px 8px", borderRadius: 6, border: "1px solid var(--line-strong)", fontSize: 13, fontFamily: "inherit", resize: "vertical"}}
                      placeholder="รายละเอียดอัปเดต"/>
                    <div style={{display: "flex", alignItems: "center", gap: 8, flexWrap: "wrap"}}>
                      <label style={{fontSize: 12, color: "var(--ink-3)"}}>ความคืบหน้า</label>
                      <input type="number" min={0} max={100} value={editProg} onChange={e => setEditProg(e.target.value)}
                        style={{width: 70, padding: "4px 6px", borderRadius: 6, border: "1px solid var(--line-strong)", fontSize: 13}}/>
                      <span style={{fontSize: 12, color: "var(--ink-3)"}}>%</span>
                      <div style={{flex: 1}}/>
                      <Btn size="sm" onClick={saveEditUpdate}>บันทึก</Btn>
                      <Btn size="sm" variant="ghost" onClick={cancelEditUpdate}>ยกเลิก</Btn>
                    </div>
                  </div>
                ) : (
                <>
                <div style={{display: "flex", alignItems: "flex-start", gap: 8}}>
                  <div style={{fontSize: 13, flex: 1}}>
                    <span style={{marginRight: 4}}>{UPDATE_ICONS[u.type] || "•"}</span>
                    <b>{u.who}</b> · <span>{u.text}</span>
                    {u.edited && <span style={{fontSize: 10, color: "var(--ink-4)", marginLeft: 4}}>(แก้ไขแล้ว)</span>}
                  </div>
                  {editable && (
                    <div style={{display: "flex", gap: 2, flexShrink: 0}}>
                      <button title="แก้ไข" onClick={() => startEditUpdate(i)}
                        style={{width: 26, height: 26, padding: 0, borderRadius: 6, border: "1px solid var(--line)", background: "var(--surface)", cursor: "pointer", color: "var(--ink-3)", fontSize: 13, lineHeight: 1}}>✎</button>
                      <button title="ลบ" onClick={() => deleteUpdate(i)}
                        style={{width: 26, height: 26, padding: 0, borderRadius: 6, border: "1px solid var(--line)", background: "var(--surface)", cursor: "pointer", color: "var(--danger)", fontSize: 13, lineHeight: 1}}>🗑</button>
                    </div>
                  )}
                </div>
                <div style={{fontSize: 11, color: "var(--ink-3)", marginTop: 2}}>{fmtThaiDate(u.date)}</div>
                {u.progressTo !== undefined && u.progressFrom !== undefined && u.progressTo !== u.progressFrom && (
                  <div style={{marginTop: 6, padding: "5px 9px", background: color + "18", borderRadius: 4, fontSize: 11, color: color, display: "inline-flex", alignItems: "center", gap: 6}}>
                    📈 {u.progressFrom}% → <b>{u.progressTo}%</b>
                  </div>
                )}
                {u.photos && u.photos.length > 0 && (
                  <div style={{display: "flex", gap: 6, flexWrap: "wrap", marginTop: 8}}>
                    {u.photos.map((p, j) => (
                      <img key={j} src={p.url} alt={p.name}
                        style={{width: 80, height: 80, objectFit: "cover", borderRadius: 4, border: "1px solid var(--line)", cursor: "pointer"}}
                        onClick={() => window.open(p.url, "_blank")}/>
                    ))}
                  </div>
                )}
                </>
                )}
              </div>
            </div>
            );
          })}
        </div>
      </Card>

      {/* Project photo gallery */}
      <Card title="รูปภาพโปรเจกต์ · Photos" sub={`${gallery.length} รูป`} className="mb-lg"
        right={<div style={{display: "flex", gap: 6}}>
          <Btn size="sm" icon={I.camera} onClick={() => setGalleryCam(true)}>ถ่ายรูป</Btn>
          <label className="btn sm" style={{cursor: "pointer"}}>
            <input type="file" accept="image/*" multiple style={{display: "none"}} onChange={onPickGallery}></input>
            อัพโหลดรูป
          </label>
        </div>}>
        {gallery.length === 0 ? (
          <div style={{padding: 18, textAlign: "center", color: "var(--ink-3)", fontSize: 12.5, border: "1.5px dashed var(--line-strong)", borderRadius: 8, background: "var(--surface-2)"}}>
            ยังไม่มีรูปในโปรเจกต์ — อัพโหลดรูปแบบ, หน้างาน, ผลงาน ได้ที่นี่
          </div>
        ) : (
          <div style={{display: "grid", gridTemplateColumns: "repeat(auto-fill, minmax(96px, 1fr))", gap: 8}}>
            {gallery.map((g, i) => (
              <div key={i} style={{position: "relative", aspectRatio: "1", borderRadius: 6, overflow: "hidden", border: "1px solid var(--line)"}}>
                <img src={g.url} alt={g.name} style={{width: "100%", height: "100%", objectFit: "cover", cursor: "pointer"}} onClick={() => window.open(g.url, "_blank")}/>
                <button onClick={() => saveGallery(gallery.filter((_, j) => j !== i))}
                  style={{position: "absolute", top: 3, right: 3, width: 18, height: 18, padding: 0, borderRadius: "50%", background: "rgba(0,0,0,0.72)", color: "white", border: 0, cursor: "pointer", fontSize: 11, lineHeight: "16px"}}>×</button>
              </div>
            ))}
          </div>
        )}
      </Card>

      {/* Expenses */}
      <Card title="ค่าใช้จ่ายของโปรเจกต์" sub={`${expenses.length + extraExpenses.length} รายการ · รวม ฿${fmt0(totalExpense)}`} className="mb-lg" right={<Btn size="sm" icon={I.plus} onClick={() => setShowExp(true)}>เพิ่มค่าใช้จ่าย</Btn>} flush>
        {(expenses.length + extraExpenses.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 className="right">ยอด</th><th>สถานะ</th><th></th></tr></thead>
            <tbody>
              {expenses.map(e => (
                <tr key={e.no}>
                  <td className="mono">{e.no}</td>
                  <td>{e.date}</td>
                  <td>{e.category}</td>
                  <td className="muted">{e.vendor}</td>
                  <td className="right amount">฿{fmt0(e.amount)}</td>
                  <td><Badge>{e.status}</Badge></td>
                  <td></td>
                </tr>
              ))}
              {extraExpenses.map((e, i) => (
                <tr key={"x" + i}>
                  <td className="mono muted">—</td>
                  <td>{e.date}</td>
                  <td>{e.category}{e.lines && e.lines.length > 1 ? <span className="muted" style={{fontSize: 11}}> · {e.lines.length} รายการ</span> : null}</td>
                  <td className="muted">{e.vendor}</td>
                  <td className="right amount">฿{fmt0(+e.amount || 0)}</td>
                  <td><Badge tone="info">{e.status}</Badge></td>
                  <td>
                    <div style={{display: "flex", gap: 2}}>
                      <button className="icon-btn" title="แก้ไข" onClick={() => setEditExp({ exp: e, i })}><I.edit size={13}/></button>
                      <button className="icon-btn" title="ลบ" onClick={() => removeExtraExpense(i)}><I.trash size={13}/></button>
                    </div>
                  </td>
                </tr>
              ))}
            </tbody>
          </table>
        )}
      </Card>

      {/* เอกสารการขายของงานนี้ */}
      <Card title="เอกสารการขาย" sub="ออกเอกสารขายของงานนี้ — ดึงลูกค้า + รายการส่งมอบให้อัตโนมัติ" className="mb-lg">
        <div style={{display: "flex", flexWrap: "wrap", gap: 8}}>
          <Btn size="sm" icon={I.doc} onClick={() => openSalesDoc("quotation")}>ใบเสนอราคา</Btn>
          <Btn size="sm" icon={I.truck} onClick={() => openSalesDoc("delivery-note")}>ใบส่งสินค้า</Btn>
          <Btn size="sm" icon={I.invoice} onClick={() => openSalesDoc("invoice")}>ใบแจ้งหนี้</Btn>
          <Btn size="sm" icon={I.tax} onClick={() => openSalesDoc("tax-invoice")}>ใบกำกับภาษี</Btn>
          <Btn size="sm" icon={I.check} onClick={() => openSalesDoc("receipt")}>ใบเสร็จรับเงิน</Btn>
        </div>
        <div style={{fontSize: 11.5, color: "var(--ink-3)", marginTop: 8}}>เปิดฟอร์มออกเอกสารพร้อมลูกค้า “{project.customer}” และรายการส่งมอบจากใบสั่งงาน · เอกสารจะผูกกับงานนี้</div>
      </Card>

      {/* ใบส่งของประจำวันของงานนี้ */}
      <Card title="ใบส่งของประจำวัน" sub={`${projDeliveries.length} รายการของงานนี้`} className="mb-lg" flush
        right={<Btn size="sm" icon={I.truck} onClick={() => setDdFor({ items: (workOrders || []).flatMap(wo => (wo.items || []).map(it => ({ name: it.name, qty: it.qty, unit: it.unit }))), assignees: [], assigneeIds: [], detail: "" })}>ออกใบส่งของ</Btn>}>
        {projDeliveries.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>
              {projDeliveries.map(d => (
                <tr key={d.id} className="row-clickable" onClick={() => { window.location.hash = "#/daily-delivery"; }}>
                  <td className="mono">{d.no}</td>
                  <td>{window.DeliveryStore ? window.DeliveryStore.fmtThai(d.date) : d.date}{d.time ? ` · ${d.time}` : ""}</td>
                  <td className="muted">{d.customer}</td>
                  <td className="muted" style={{fontSize: 12}}>{(d.items || []).length} รายการ</td>
                  <td><Badge tone={d.status === "ส่งแล้ว" ? "success" : "warning"}>{d.status}</Badge></td>
                </tr>
              ))}
            </tbody>
          </table>
        )}
      </Card>

      {/* Summary */}
      <Card title="สรุปโปรเจกต์ · Summary" className="mb-lg" right={<Btn size="sm" icon={I.print} onClick={() => PX.printJobSummaryDoc(project)}>พิมพ์ใบสรุปงาน</Btn>}>
        <table style={{width: "100%", fontSize: 13}}>
          <tbody>
            <tr><td className="muted" style={{padding: "6px 0"}}>มูลค่างาน (Revenue)</td><td className="right amount">฿{fmt0(project.budget)}</td></tr>
            <tr><td className="muted" style={{padding: "6px 0"}}>ต้นทุนที่ลงไว้</td><td className="right amount">฿{fmt0(project.actual)}</td></tr>
            <tr><td className="muted" style={{padding: "6px 0"}}>ค่าใช้จ่ายที่เบิก/ผูก</td><td className="right amount">฿{fmt0(totalExpense)}</td></tr>
            <tr><td className="muted" style={{padding: "6px 0", fontWeight: 600}}>ต้นทุนรวมทั้งหมด</td><td className="right amount" style={{fontWeight: 600}}>฿{fmt0(totalCost)}</td></tr>
            <tr style={{borderTop: "1px solid var(--line)"}}><td style={{padding: "8px 0", fontWeight: 600}}>กำไรขั้นต้น</td><td className="right amount" style={{fontWeight: 600, color: overBudget ? "var(--danger)" : "var(--success)"}}>฿{fmt0(left)}</td></tr>
            <tr><td className="muted" style={{padding: "6px 0"}}>Margin</td><td className="right amount" style={{color: overBudget ? "var(--danger)" : "var(--ink-1)"}}>{project.budget > 0 ? ((left / project.budget) * 100).toFixed(1) : "0.0"}%</td></tr>
            <tr><td className="muted" style={{padding: "6px 0"}}>ความคืบหน้า vs เวลาที่ผ่าน</td><td className="right">
              <span style={{color: project.progress >= (elapsed/days*100) ? "var(--success)" : "var(--warning)"}}>
                {project.progress}% / เวลาผ่าน {Math.round(elapsed/days*100)}%
              </span>
            </td></tr>
            <tr><td className="muted" style={{padding: "6px 0"}}>เอกสารที่เกี่ยวข้อง</td><td className="right" style={{fontSize: 12}}>
              <span className="badge outline mono" style={{marginLeft: 4}}>{project.code.replace("JOB", "QO")}</span>
              <span className="badge outline mono" style={{marginLeft: 4}}>{project.code.replace("JOB", "INV")}</span>
            </td></tr>
          </tbody>
        </table>
      </Card>

      {/* Sub-modals */}
      {showCam && window.CameraCaptureModal && (
        <window.CameraCaptureModal onClose={() => setShowCam(false)} onCapture={(url) => setPhotos(p => [...p, { url, name: "camera-" + Date.now() }].slice(0, 6))}/>
      )}
      {galleryCam && window.CameraCaptureModal && (
        <window.CameraCaptureModal onClose={() => setGalleryCam(false)} onCapture={(url) => saveGallery([...gallery, { url, name: "camera-" + Date.now(), date: PX.pxToday() }])}/>
      )}
      {showWO && <window.WorkOrderModal project={project} initial={showWO === true ? null : showWO} onClose={() => setShowWO(false)} onSaved={(list) => setWorkOrders(list)}/>}
      {evalFor && window.WorkEvalModal && <window.WorkEvalModal project={project} wo={evalFor} onClose={() => setEvalFor(null)} onSaved={() => setEvals(window.WorkEvalStore.byProject(project.code))}/>}
      {showEdit && <window.ProjectFormModal project={project} onClose={() => setShowEdit(false)} onSaved={(rec) => { Object.assign(project, rec); onChanged && onChanged(); }}/>}
      {showExp && <window.ProjectExpenseModal project={project} onClose={() => setShowExp(false)} onSaved={(list) => setExtraExpenses(list)}/>}
      {editExp && <ExpenseEditModal exp={editExp.exp} onClose={() => setEditExp(null)} onSave={(next) => saveEditExp(next, editExp.i)}/>}
      {ddFor && window.DDEditModal && (() => {
        const D = window.SSSData;
        const cust = (D.Customers || []).find(c => c.name === project.customer) || {};
        // ที่อยู่: บันทึกลูกค้า → ที่อยู่ส่งครั้งก่อนของลูกค้ารายนี้
        const addr = cust.address || cust.addr || (window.DeliveryStore ? window.DeliveryStore.lastAddress(project.customer) : "");
        // ผู้ส่ง: คนแรกในใบสั่งงาน (ถ้ามี id พนักงานจริง) ไม่งั้นค่าเริ่มต้น
        const firstEmp = (ddFor.assigneeIds || []).find(id => (D.Employees || []).some(e => e.id === id));
        // รายการส่งมอบ: ดึงจากใบสั่งงานถ้ามี ไม่งั้นใช้ชื่องานของโปรเจกต์เป็นรายการตั้งต้น
        const items = (ddFor.items && ddFor.items.length)
          ? ddFor.items.map(it => ({ name: it.name, qty: it.qty != null ? it.qty : 1, unit: it.unit || "ชิ้น", photo: null, checked: false }))
          : [{ name: project.name || project.code || "", qty: 1, unit: "งาน", photo: null, checked: false }];
        const prefill = {
          customer: project.customer || (D.Customers[0] && D.Customers[0].name) || "",
          address: addr || "",
          time: "10:00",
          assignee: firstEmp || "EMP-024",
          assigneeName: (ddFor.assignees && ddFor.assignees[0]) || "",
          note: `${project.code} · ${project.name || ""}${ddFor.detail ? "\n" + ddFor.detail : ""}`.trim(),
          ref: "",
          items
        };
        return <window.DDEditModal initial={prefill} onClose={() => setDdFor(null)}
          onSave={(f) => { window.createDelivery({ ...f, ref: f.ref || project.code }); setDdFor(null); setSelWO(new Set()); }}/>;
      })()}
    </Drawer>
  );
}

function KPI({ label, value, tone }) {
  const c = tone === "success" ? "var(--success)" : tone === "warning" ? "var(--warning)" : tone === "danger" ? "var(--danger)" : "var(--ink-1)";
  return (
    <div style={{padding: 10, background: "var(--surface-2)", border: "1px solid var(--line)", borderRadius: 6}}>
      <div className="label">{label}</div>
      <div style={{fontSize: 16, fontWeight: 600, marginTop: 2, color: c, fontVariantNumeric: "tabular-nums"}}>{value}</div>
    </div>
  );
}

// =============== Main Projects page ===============
// แก้ไขค่าใช้จ่ายของโปรเจกต์ (หมวด/ผู้ขาย/ยอด — รองรับหลายรายการ)
function ExpenseEditModal({ exp, onClose, onSave }) {
  const { Btn, fmt0 } = window.UI;
  const [category, setCategory] = useStateP3(exp.category || "อื่น ๆ");
  const [vendor, setVendor] = useStateP3(exp.vendor || "");
  const hasLines = !!(exp.lines && exp.lines.length);
  const [lines, setLines] = useStateP3(() => hasLines ? exp.lines.map(l => ({ ...l })) : null);
  const [amount, setAmount] = useStateP3(+exp.amount || 0);
  const total = hasLines ? lines.reduce((s, l) => s + (+l.amount || 0), 0) : (+amount || 0);
  const save = () => {
    if (!total || total <= 0) { window.toast && window.toast("ยอดเงินต้องมากกว่า 0"); return; }
    const next = { ...exp, category, vendor: vendor.trim() || "—", amount: total };
    if (hasLines) next.lines = lines.map(l => ({ ...l, amount: +l.amount || 0 }));
    onSave(next);
  };
  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: 18, width: 460, maxWidth: "96vw", maxHeight: "92vh", overflow: "auto", boxShadow: "var(--shadow-lg)"}}>
        <div style={{fontSize: 14.5, fontWeight: 700, marginBottom: 12}}>แก้ไขค่าใช้จ่าย</div>
        <div className="field-row" style={{display: "grid", gridTemplateColumns: "1fr 1fr", gap: 10, marginBottom: 12}}>
          <div className="field"><label>หมวด</label>
            <select className="input" value={category} onChange={e => setCategory(e.target.value)}>
              {["ค่าวัสดุ/วัตถุดิบ", "ค่าแรงภายนอก", "ค่าขนส่ง", "ค่าเครื่องมือ/อุปกรณ์", "อื่น ๆ"].map(c => <option key={c}>{c}</option>)}
            </select>
          </div>
          <div className="field"><label>ผู้ขาย / เจ้าหนี้</label><input className="input" value={vendor} onChange={e => setVendor(e.target.value)}/></div>
        </div>
        {hasLines ? (
          <div style={{display: "flex", flexDirection: "column", gap: 8}}>
            <div className="label">รายการ</div>
            {lines.map((l, i) => (
              <div key={i} style={{display: "grid", gridTemplateColumns: "1fr 110px", gap: 8, alignItems: "center"}}>
                <div style={{fontSize: 13}}>{l.name}<span className="muted" style={{fontSize: 11}}> · {l.qty} {l.unit}</span></div>
                <input className="input" type="number" min="0" value={l.amount} onChange={e => setLines(ls => ls.map((x, j) => j === i ? { ...x, amount: e.target.value } : x))}/>
              </div>
            ))}
          </div>
        ) : (
          <div className="field"><label>จำนวนเงิน (บาท)</label><input className="input" type="number" min="0" value={amount} onChange={e => setAmount(e.target.value)}/></div>
        )}
        <div style={{display: "flex", alignItems: "center", justifyContent: "space-between", marginTop: 14, paddingTop: 12, borderTop: "1px solid var(--line)"}}>
          <div style={{fontSize: 13}}>รวม <b style={{fontSize: 16, color: "var(--accent)"}}>฿{fmt0(total)}</b></div>
          <div style={{display: "flex", gap: 8}}>
            <Btn kind="ghost" onClick={onClose}>ยกเลิก</Btn>
            <Btn kind="primary" icon={I.check} onClick={save}>บันทึก</Btn>
          </div>
        </div>
      </div>
    </>
  );
}

// งานที่มอบหมาย — รวมงานในทุกโปรเจกต์ที่แต่ละคนได้รับมอบหมาย (เห็นภาระงานรวมของแต่ละคน)
function DailyQueueView({ projects, onSelect }) {
  const D = window.SSSData;
  const PX = window.ProjX;
  const { Badge, Card } = window.UI;
  const isoToday = () => { const d = new Date(); return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`; };
  const [date, setDate] = useStateP3(isoToday);
  const [hideDone, setHideDone] = useStateP3(false);
  const [q, setQ] = useStateP3("");
  const shiftDay = (n) => setDate(d => { const x = new Date(d + "T00:00:00"); x.setDate(x.getDate() + n); return `${x.getFullYear()}-${String(x.getMonth() + 1).padStart(2, "0")}-${String(x.getDate()).padStart(2, "0")}`; });

  const emps = (D.Employees || []).filter(e => e.status !== "ลาออกแล้ว");
  const byId = {}; emps.forEach(e => byId[e.id] = { emp: e, items: [] });
  const push = (id, item) => { if (byId[id]) byId[id].items.push(item); };

  const _m = ["ม.ค.", "ก.พ.", "มี.ค.", "เม.ย.", "พ.ค.", "มิ.ย.", "ก.ค.", "ส.ค.", "ก.ย.", "ต.ค.", "พ.ย.", "ธ.ค."];
  const thDate = (iso) => { const p = String(iso || "").split("-").map(Number); return (p.length === 3 && p[1]) ? `${p[2]} ${_m[p[1] - 1]} ${p[0] + 543}` : ""; };

  // งานของวันที่เลือก = งานที่ "กำลังอยู่ในช่วงทำ" วันนั้น (วันเริ่ม→วันส่ง ครอบคลุมวันที่เลือก)
  // + งานเลยกำหนดที่ยังไม่เสร็จ เด้งมาที่ "วันนี้" + งานที่ไม่กำหนดวันโชว์ที่วันนี้
  const todayIso = isoToday();
  projects.forEach(p => {
    if ((p.progress || 0) >= 100) return;                          // โปรเจกต์เสร็จ 100% → ไม่เอามาลงคิว
    const ex = PX.pxGet(p.code);
    (ex.workOrders || []).forEach(wo => (wo.steps || []).forEach((s, si) => {
      const done = !!s.done;                                        // เสร็จจริง = ติ๊ก "ทำเสร็จแล้ว" เท่านั้น (ไม่ใช่ "กำหนดเสร็จ")
      let start = s.planDate || wo.startDate || "";
      let end = s.doneDate || s.planDate || wo.due || "";           // doneDate = กำหนดเสร็จ → ใช้เป็นวันสิ้นสุดช่วงงาน
      if (start && end && start > end) { const t = start; start = end; end = t; }
      let show;
      if (start && end) show = date >= start && date <= end;       // อยู่ในช่วงทำงาน
      else if (end) show = date <= end;                            // มีแต่กำหนดส่ง → ค้างจนถึงวันส่ง
      else if (start) show = date >= start;                        // มีแต่วันเริ่ม
      else show = date === todayIso;                               // ไม่กำหนดวัน → โผล่วันนี้
      if (!done && end && date === todayIso && todayIso > end) show = true; // เลยกำหนด+ยังไม่เสร็จ → เด้งมาวันนี้
      if (!show) return;                                           // (แบบ B: โชว์ขั้นที่เสร็จด้วย ขีดฆ่าให้เห็น — ตัดเฉพาะโปรเจกต์ 100%)
      const overdue = !done && end && date >= todayIso && todayIso > end;
      const mk = (id, isHelper) => push(id, {
        code: p.code, name: p.name, project: p,
        label: (s.duty || `${wo.no} ลำดับ ${si + 1}`) + (isHelper ? " (ผู้ช่วย)" : "") + (overdue ? " ⚠️ เลยกำหนด" : ""),
        due: end, done,
        color: overdue ? "var(--danger)" : (STATUS_COLORS[p.status] || "var(--c-violet)")
      });
      (s.empIds || []).forEach(id => mk(id, false));
      (s.helperIds || []).forEach(id => mk(id, true));
    }));
  });
  (D.DailyTasks || []).forEach(t => { if ((t.date || "") !== date || t.status === "ทำเสร็จ") return; (t.assignees || []).forEach(id => push(id, { kind: "งานประจำวัน", color: "var(--c-amber)", label: t.title || "งานประจำวัน" })); });
  (D.DailyDeliveries || []).forEach(dd => { if ((dd.date || "") !== date || dd.status === "ส่งแล้ว") return; if (dd.assignee) push(dd.assignee, { kind: "ส่งของ", color: "var(--c-blue)", label: `ส่ง ${dd.customer}` }); });

  let rows = Object.values(byId).map(r => ({ ...r, open: r.items.filter(i => !i.done).length }));
  if (q.trim()) { const ql = q.trim().toLowerCase(); rows = rows.filter(r => (r.emp.name + " " + r.emp.role + " " + r.emp.dept).toLowerCase().includes(ql)); }
  rows.sort((a, b) => a.items.length - b.items.length);
  const totalJobs = rows.reduce((s, r) => s + r.items.length, 0);
  const busy = rows.filter(r => r.items.length > 0).length;

  return (
    <Card title="คิวงานรายวัน" sub="เลือกวันที่เพื่อดูคิวงานของแต่ละคนในวันนั้น — เรียงคนว่างขึ้นก่อน เพื่อมอบงานเพิ่มได้ถูกคน" flush
      right={<div style={{display: "flex", alignItems: "center", gap: 6, flexWrap: "wrap"}}>
        <input className="input" placeholder="ค้นหาพนักงาน…" value={q} onChange={e => setQ(e.target.value)} style={{width: 130, height: 32}}/>
        <button className="icon-btn" onClick={() => shiftDay(-1)}><I.chevronLeft size={14}/></button>
        <input className="input" type="date" value={date} onChange={e => setDate(e.target.value)} style={{width: 150, height: 32}}/>
        <button className="icon-btn" onClick={() => shiftDay(1)}><I.chevronRight size={14}/></button>
        <button className="btn sm" onClick={() => setDate(isoToday())}>วันนี้</button>
        <label style={{display: "flex", alignItems: "center", gap: 4, fontSize: 12, cursor: "pointer", whiteSpace: "nowrap"}}>
          <input type="checkbox" checked={hideDone} onChange={e => setHideDone(e.target.checked)}/> ซ่อนงานเสร็จ
        </label>
      </div>}>
      <div style={{padding: "8px 14px", fontSize: 12, color: "var(--ink-3)"}}>{thDate(date)} · รวม {totalJobs} งาน · มีงาน {busy} คน · ว่าง {rows.length - busy} คน</div>
      <table className="t">
        <thead><tr><th>พนักงาน</th><th style={{width: 90}} className="right">จำนวนคิว</th><th>งานในวันนี้</th></tr></thead>
        <tbody>
          {rows.map(({ emp, items }) => {
            const show = hideDone ? items.filter(i => !i.done) : items;
            return (
              <tr key={emp.id}>
                <td><b>{emp.name}</b><div style={{fontSize: 11, color: "var(--ink-3)"}}>{emp.role} · {emp.dept}</div></td>
                <td className="right"><Badge tone={items.length === 0 ? "success" : items.length >= 3 ? "danger" : "warning"}>{items.length === 0 ? "ว่าง" : items.length + " งาน"}</Badge></td>
                <td>
                  {show.length === 0 ? <span className="muted" style={{fontSize: 12}}>— ไม่มีคิวงานในวันนี้ —</span> : (
                    <div style={{display: "flex", flexWrap: "wrap", gap: 6}}>
                      {show.map((it, i) => (
                        <span key={i} onClick={() => it.project && onSelect && onSelect(it.project)}
                          style={{display: "inline-flex", alignItems: "center", gap: 5, fontSize: 11.5, padding: "3px 9px", borderRadius: 999, background: "color-mix(in oklab, " + it.color + " 14%, var(--surface))", border: "1px solid " + it.color, color: "var(--ink-1)", cursor: it.project ? "pointer" : "default", textDecoration: it.done ? "line-through" : "none", opacity: it.done ? 0.55 : 1}}>
                          <span style={{width: 6, height: 6, borderRadius: "50%", background: it.color}}/>
                          {it.code ? <span className="mono" style={{fontSize: 10}}>{it.code}</span> : it.kind} · {it.label}
                        </span>
                      ))}
                    </div>
                  )}
                </td>
              </tr>
            );
          })}
        </tbody>
      </table>
    </Card>
  );
}

function ProjectsPage({ navigate }) {
  const D = window.SSSData;
  const PX = window.ProjX;
  const { Btn, Badge, Card, PageHead, Tabs, fmt0 } = window.UI;

  const mergeAll = () => {
    const users = PX.upLoad();
    const base = D.Projects.filter(p => !users.some(u => u.code === p.code));
    return [...users, ...base].map(p => {
      const x = PX.pxGet(p.code);
      return { ...p, ...(x.fields || {}), progress: x.progress !== undefined ? x.progress : p.progress, status: x.status || p.status };
    });
  };

  const [projects, setProjects] = useStateP3(mergeAll);
  const refresh = () => setProjects(mergeAll());
  const [tab, setTab] = useStateP3("gantt");
  const [selected, setSelected] = useStateP3(null);
  const [showNew, setShowNew] = useStateP3(false);
  const [woFor, setWoFor] = useStateP3(null);
  const [monthDate, setMonthDate] = useStateP3(() => { const n = new Date(); return new Date(n.getFullYear(), n.getMonth(), 1); }); // เดือนปัจจุบัน
  const changeMonth = (dir) => {
    if (dir === "today") { const n = new Date(); setMonthDate(new Date(n.getFullYear(), n.getMonth(), 1)); }
    else setMonthDate(d => new Date(d.getFullYear(), d.getMonth() + dir, 1));
  };

  // ต้นทุนจริงต่อโปรเจกต์ = ที่ลงไว้ + ค่าใช้จ่ายที่ผูก/เบิก
  const projCost = (p) => {
    const ex = PX.pxGet(p.code);
    const linkedExtra = ex.linkedExpenses || [];
    const exTotal = (ex.expenses || []).reduce((s, e) => s + (+e.amount || 0), 0)
      + (D.Expenses || []).filter(e => (p.expenseLinks || []).includes(e.no) || linkedExtra.includes(e.no)).reduce((s, e) => s + e.amount, 0);
    return (p.actual || 0) + exTotal;
  };
  // เงินแบบอ่านง่าย — ใช้ k เฉพาะเลขใหญ่ (กันโชว์ "฿0k" ดูเหมือนบั๊ก)
  const money = (n) => (n >= 100000 ? "฿" + (n / 1000).toFixed(0) + "k" : "฿" + fmt0(n || 0));
  const totalBudget = projects.reduce((s, p) => s + p.budget, 0);
  const totalActual = projects.reduce((s, p) => s + projCost(p), 0);
  const active = projects.filter(p => p.status !== "ส่งงาน" && p.progress < 100).length;

  const exportJobCosting = () => {
    window.exportRowsCSV && window.exportRowsCSV("job-costing", [
      { key: "code", label: "รหัส" }, { key: "name", label: "โปรเจกต์" }, { key: "customer", label: "ลูกค้า" },
      { key: "lead", label: "หัวหน้างาน" }, { key: "budget", label: "งบประมาณ" }, { key: "actual", label: "ต้นทุนใช้ไป" },
      { key: "progress", label: "ความคืบหน้า %" }, { key: "status", label: "สถานะ" }, { key: "due", label: "ครบกำหนด" }
    ], projects);
    window.toast && window.toast("ส่งออกรายงาน Job Costing แล้ว");
  };

  return (
    <>
      <PageHead title="โปรเจกต์/งาน · Job Costing" sub="Timeline แบบ Gantt · ใบสั่งงาน · อัปเดตสถานะพร้อมรูป · สรุปค่าแรง-ค่าวัสดุ"
        right={<>
          <Btn icon={I.download} kind="ghost" onClick={exportJobCosting}>รายงาน Job Costing</Btn>
          <Btn icon={I.plus} kind="primary" onClick={() => setShowNew(true)}>สร้างโปรเจกต์</Btn>
        </>}/>

      <div className="grid cols-4 mb-lg">
        <window.UI.Stat label="โปรเจกต์เปิดอยู่" value={active} icon={I.briefcase}/>
        <window.UI.Stat label="งบประมาณรวม" value={`฿${fmt0(totalBudget)}`} sub="WIP value" icon={I.calc}/>
        <window.UI.Stat label="ต้นทุนใช้ไป" value={`฿${fmt0(totalActual)}`} sub={totalBudget > 0 ? `${(totalActual/totalBudget*100).toFixed(0)}% ของงบ` : ""} icon={I.receipt}/>
        <window.UI.Stat label="กำไรขั้นต้นโดยรวม" value={`฿${fmt0(totalBudget - totalActual)}`} sub={totalBudget > 0 ? `Margin ${((1 - totalActual/totalBudget) * 100).toFixed(1)}%` : ""} dir="up" icon={I.money}/>
      </div>

      <Tabs items={[
        { key: "gantt", label: "Gantt Timeline", count: projects.length },
        { key: "calendar", label: "ปฏิทินรายเดือน" },
        { key: "queue", label: "คิวงานรายวัน" },
        { key: "board", label: "Status Board" },
        { key: "list", label: "ตาราง" }
      ]} active={tab} onChange={setTab}/>

      {tab === "queue" && <DailyQueueView projects={projects} onSelect={setSelected}/>}

      {tab === "gantt" && (
        <Card title="ไทม์ไลน์โปรเจกต์" sub="คลิกแถบเพื่อดูรายละเอียด · เส้นประแดง = วันนี้">
          <GanttChart projects={projects} onSelect={setSelected}/>
          <div style={{display: "flex", gap: 14, marginTop: 14, fontSize: 11.5, flexWrap: "wrap"}}>
            {Object.entries(STATUS_COLORS).map(([s, c]) => (
              <div key={s} style={{display: "flex", alignItems: "center", gap: 6, color: "var(--ink-3)"}}>
                <div style={{width: 12, height: 12, background: c, borderRadius: 3}}/>
                {s}
              </div>
            ))}
          </div>
        </Card>
      )}

      {tab === "calendar" && (
        <Card title="ปฏิทินโปรเจกต์ · Calendar View" sub="คลิกแถบหรือรายชื่อด้านล่างเพื่อเปิดรายละเอียด">
          <MonthCalendar projects={projects} monthDate={monthDate} onChangeMonth={changeMonth} onSelect={setSelected}/>
          <div style={{display: "flex", gap: 14, marginTop: 14, fontSize: 11.5, flexWrap: "wrap"}}>
            {Object.entries(STATUS_COLORS).map(([s, c]) => (
              <div key={s} style={{display: "flex", alignItems: "center", gap: 6, color: "var(--ink-3)"}}>
                <div style={{width: 12, height: 12, background: c, borderRadius: 3}}/>
                {s}
              </div>
            ))}
          </div>
        </Card>
      )}

      {tab === "board" && (
        <div style={{display: "grid", gridTemplateColumns: "repeat(5, 1fr)", gap: 10}}>
          {["ออกแบบ 3D", "กำลังผลิต", "ตรวจสอบคุณภาพ", "รอเก็บเงิน", "ส่งงาน"].map(col => {
            const items = projects.filter(p => p.status === col);
            const color = STATUS_COLORS[col];
            return (
              <div key={col} style={{background: "var(--surface-2)", borderRadius: 8, padding: 8, minHeight: 200}}>
                <div style={{display: "flex", alignItems: "center", gap: 6, padding: "4px 6px", marginBottom: 8}}>
                  <div style={{width: 8, height: 8, borderRadius: 2, background: color}}/>
                  <span style={{fontSize: 12, fontWeight: 600}}>{col}</span>
                  <span style={{fontSize: 11, color: "var(--ink-3)", marginLeft: "auto"}}>{items.length}</span>
                </div>
                {items.map(p => (
                  <div key={p.code} onClick={() => setSelected(p)} style={{padding: 10, background: "var(--surface)", border: "1px solid var(--line)", borderRadius: 6, marginBottom: 6, cursor: "pointer", borderLeft: `3px solid ${color}`}}>
                    <div className="mono" style={{fontSize: 10, color: "var(--ink-3)"}}>{p.code}</div>
                    <div style={{fontSize: 12, fontWeight: 500, marginTop: 2}}>{p.name.length > 40 ? p.name.slice(0,40)+"…" : p.name}</div>
                    <div style={{fontSize: 11, color: "var(--ink-3)", marginTop: 2}}>{p.customer}</div>
                    <div style={{height: 4, background: "var(--surface-3)", borderRadius: 999, marginTop: 8, overflow: "hidden"}}>
                      <div style={{height: "100%", width: `${p.progress}%`, background: color, borderRadius: 999}}/>
                    </div>
                    <div style={{display: "flex", justifyContent: "space-between", marginTop: 6, fontSize: 10.5, color: "var(--ink-3)"}}>
                      <span>{money(projCost(p))} / {money(p.budget)}</span>
                      <span>ครบ {(p.due || "").split(" ").slice(0,2).join(" ")}</span>
                    </div>
                  </div>
                ))}
              </div>
            );
          })}
        </div>
      )}

      {tab === "list" && (
        <Card flush>
          <table className="t">
            <thead><tr><th>รหัส</th><th>โปรเจกต์</th><th>ลูกค้า</th><th>หัวหน้างาน</th><th className="right">งบประมาณ</th><th className="right">ใช้ไป</th><th className="right">คงเหลือ</th><th>ความคืบหน้า</th><th>ครบกำหนด</th><th>สถานะ</th></tr></thead>
            <tbody>
              {projects.map(p => {
                const ex = PX.pxGet(p.code);
                const linkedExtra = ex.linkedExpenses || [];
                const exTotal = (ex.expenses || []).reduce((s, e) => s + (+e.amount || 0), 0)
                  + (D.Expenses || []).filter(e => (p.expenseLinks || []).includes(e.no) || linkedExtra.includes(e.no)).reduce((s, e) => s + e.amount, 0);
                const cost = (p.actual || 0) + exTotal;
                const left = p.budget - cost;
                const overBudget = cost > p.budget;
                return (
                  <tr key={p.code} className="row-clickable" onClick={() => setSelected(p)}>
                    <td className="mono">{p.code}</td>
                    <td>{p.name}</td>
                    <td className="muted">{p.customer}</td>
                    <td>{p.lead}</td>
                    <td className="right amount">฿{fmt0(p.budget)}</td>
                    <td className="right amount">฿{fmt0(cost)}</td>
                    <td className="right amount" style={{color: overBudget ? "var(--danger)" : "var(--ink-1)"}}>฿{fmt0(left)}</td>
                    <td style={{minWidth: 140}}><window.ProgressBar pct={p.progress}/></td>
                    <td className="muted">{p.due}</td>
                    <td><Badge>{p.status}</Badge></td>
                  </tr>
                );
              })}
            </tbody>
          </table>
        </Card>
      )}

      {selected && <ProjectDetailDrawer project={selected} onClose={() => { setSelected(null); refresh(); }} onChanged={refresh}/>}

      {showNew && <window.ProjectFormModal onClose={() => setShowNew(false)} onSaved={(rec, thenWO) => {
        refresh();
        if (thenWO) setTimeout(() => setWoFor(rec), 150);
      }}/>}

      {woFor && <window.WorkOrderModal project={woFor} onClose={() => setWoFor(null)} onSaved={() => refresh()}/>}
    </>
  );
}

window.Projects = ProjectsPage;
