/* WAREHOUSE 2.0 · 採購招標 — Swiss 版式,真後端 */
(() => {
const W2 = window.W2;
const { t } = window.W2_LANG;
window.W2_LANG.addEN({
  "採購招標": "Procurement",
  "流程化採購協作 · 招標評審 · 全程留痕": "Process-driven procurement · tender review · fully audited",
  "待辦 {a} 條 · 進行中 {b} 條 · 頁面只讀,操作交秘書": "{a} to-dos · {b} running · read-only, actions via Secretary",
  "詢價": "Inquiry",
  "發起流程": "Start process",
  "問秘書": "Ask Secretary",
  "刷新": "Refresh",
  "我要發起一個採購或招標流程,請追問流程類型、事由標題和預算金額,經我確認後執行": "Start a procurement or tender process for me — ask me for the workflow type, title and budget, then execute after my confirmation",
  "幫我做一輪詢價:請追問物資名稱、數量和意向供應商,整理成詢價單並跟進報價": "Run a round of price inquiry — ask me for the item, quantity and candidate suppliers, draft the RFQ and chase the quotes",
  "看看採購招標現在的待辦、流程阻塞和權限缺口,按優先級給我可執行的下一步": "Review procurement to-dos, process blockages and permission gaps, and give me prioritised executable next steps",
  "我的待辦": "My to-dos",
  "進行中流程": "Running processes",
  "已閉環": "Closed",
  "流程模板": "Workflow templates",
  "條": "", "個": "", "步": "",
  "{n} 條已逾期": "{n} overdue",
  "無逾期": "none overdue",
  "含已駁回 / 已取消 {n} 條": "incl. {n} rejected / cancelled",
  "全流水可追溯": "full trail retained",
  "招標 · 採購 · 審批鏈": "tender · purchase · approval chains",
  "限時待辦優先 · 推進交秘書": "due tasks first · advance via Secretary",
  "把我的採購待辦按緊急程度排序,逐條給我處理建議,經我確認後推進": "Rank my procurement to-dos by urgency, advise on each, and advance them after my confirmation",
  "全部交秘書梳理": "Triage all via Secretary",
  "填報": "Form", "審批": "Approval", "外部留痕": "External", "系統自動": "System",
  "簽章": "Sign-off", "條件分流": "Exclusive gateway", "並行分叉": "Parallel split", "並行匯聚": "Parallel join",
  "步 {n}": "step {n}",
  "限 {d}": "due {d}",
  "已逾期": "overdue",
  "推進": "Advance",
  "推進待辦「{node}」(流程「{title}」,單號 {no}):請先核對必需材料和上下文,建議通過還是駁回,經我確認後執行": "Advance task \"{node}\" (process \"{title}\", no. {no}): check required artifacts and context first, recommend approve or reject, and execute after my confirmation",
  "沒有待你處理的任務": "Nothing waiting on you",
  "流程走到你這一步時,待辦會第一時間出現在這裡。": "When a process reaches your step, the task will surface here first.",
  "採購 / 招標流水": "Procurement / tender ledger",
  "我發起的流程 · 點行看流轉": "processes I started · click a row for the trail",
  "全部": "All", "進行中": "Running", "已駁回": "Rejected", "已取消": "Cancelled", "等待": "Waiting",
  "單號": "No.", "事由": "Title", "流程": "Workflow", "狀態": "Status",
  "當前節點": "Current node", "發起時間": "Started", "交給秘書": "Secretary",
  "查進度": "Progress",
  "查一下流程「{title}」(單號 {no})的當前進度、卡在哪個節點,需要催辦就幫我催辦": "Check progress of \"{title}\" (no. {no}), find where it is stuck, and nudge the current owner if needed",
  "還沒有採購 / 招標流水": "No procurement / tender records yet",
  "對秘書說「發起採購」,第一條流水就從這裡開始。": "Tell the Secretary to start a procurement — the first record begins here.",
  "發起第一個流程": "Start the first process",
  "當前篩選下沒有流程": "No processes under current filter",
  "發起於": "Started at", "完成於": "Completed at",
  "流程時間線": "Timeline",
  "暫無時間線記錄": "No timeline entries yet",
  "已留存證明": "Artifacts on file",
  "詳情載入中…": "Loading detail…",
  "催辦這個流程": "Nudge this process",
  "流程「{title}」(單號 {no})推進得怎麼樣?卡住的話幫我催辦當前處理人": "How is process \"{title}\" (no. {no}) going? If it is stuck, nudge the current owner for me",
  "讓秘書登記材料": "File artifacts via Secretary",
  "幫流程「{title}」(單號 {no})登記證明材料:請追問材料類型和憑證編號/說明後登記": "File supporting artifacts for process \"{title}\" (no. {no}): ask me for the artifact kind and reference/notes, then register them",
  "發起": "Started", "激活": "Activated", "通過": "Approved", "駁回": "Rejected", "轉交": "Reassigned", "完成": "Completed", "提交": "Submitted",
  "批復文件": "Approval document", "採購文件": "Procurement document", "採購計劃": "Tender plan",
  "代理委派": "Agent assignment", "招標公告": "Tender notice", "開標記錄": "Bid opening record",
  "評標報告": "Evaluation report", "中標結果": "Award result", "合同草稿": "Contract draft",
  "審批單": "Approval sheet", "簽章憑證": "Signature proof",
  "流程拓撲": "Workflow topology",
  "{s} 階段 · {n} 節點 · 指派與流轉規則": "{s} stages · {n} nodes · assignment & routing rules",
  "秘書研判": "Secretary review",
  "結合流程「{wf}」的拓撲,檢查阻塞節點、權限缺口和外部留痕風險,給我下一步可執行動作": "Review the topology of \"{wf}\": check blocked nodes, permission gaps and external-trail risks, and give me executable next steps",
  "分析流程「{wf}」第 {step} 步「{node}」:處理要點、指派與權限規則、必需材料和下一步流轉": "Analyse step {step} \"{node}\" of workflow \"{wf}\": handling essentials, assignment & permission rules, required artifacts and next routing",
  "發起人": "Initiator", "指定用戶": "Assigned user", "指定角色": "Assigned role",
  "持有權限": "Permission holder", "需求部門負責人": "Requesting dept manager",
  "成本中心負責人": "Cost centre owner", "上下文字段": "Context field", "未配置": "Not configured",
  "未分組": "Ungrouped",
  "缺權限": "No permission",
  "會簽 {n}": "quorum {n}",
  "材料 {n}": "{n} artifacts",
  "SLA {n} 小時": "SLA {n}h",
  "待辦 {n}": "{n} to-dos",
  "流程完成": "process ends",
  "節點": "Node",
  "此流程暫無節點定義": "This workflow has no nodes defined",
  "尚無流程模板": "No workflow templates yet",
  "流程模板由系統預置或管理員配置。先問秘書採購流程怎麼走也可以。": "Workflow templates are preset by the system or configured by an admin. You can also just ask the Secretary how procurement runs.",
  "載入中…": "Loading…",
  "直接吩咐秘書": "Tell the Secretary",
  "2.0 約定:頁面只讀,改動經秘書確認執行,全程留痕。": "2.0 contract: pages are read-only; changes run through the Secretary with full audit.",
});
const { useState: _s, useEffect: _e, useMemo: _mm } = React;
const { Icon: I, Btn: B, Tag: T, Label: LB, Empty: EM, Kpi, Folio, Band, pad2, num } = W2;
const ask = (p) => W2.openSecretary(p);

const arr = (x) => (Array.isArray(x) ? x : []);
const wfDate = (v) => (v ? String(v).replace("T", " ").slice(0, 16) : "—");
const isOverdue = (v) => {
  if (!v) return false;
  const d = new Date(String(v).replace(" ", "T"));
  return Number.isFinite(d.getTime()) && d.getTime() < Date.now();
};

const KIND = { form: "填報", approval: "審批", external_placeholder: "外部留痕", system_auto: "系統自動", signoff: "簽章", gateway_exclusive: "條件分流", gateway_parallel: "並行分叉", gateway_join: "並行匯聚" };
const KIND_TONE = { approval: "plain", form: "plain", external_placeholder: "warn", system_auto: "plain", signoff: "plain", gateway_exclusive: "plain", gateway_parallel: "plain", gateway_join: "plain" };
const ASSIGN = { initiator: "發起人", user: "指定用戶", role: "指定角色", permission: "持有權限", dept_manager: "需求部門負責人", cost_center_owner: "成本中心負責人", from_context: "上下文字段" };
const ACT = { start: "發起", activate: "激活", approve: "通過", reject: "駁回", reassign: "轉交", complete: "完成", submit: "提交" };
const ISTAT = { running: ["plain", "進行中", true], completed: ["ok", "已閉環", true], rejected: ["bad", "已駁回", true], cancelled: ["plain", "已取消", false], waiting: ["warn", "等待", true] };
const ARTS = { batch_doc: "批復文件", procurement_doc: "採購文件", tender_plan: "採購計劃", agent_assignment: "代理委派", tender_notice: "招標公告", bid_opening_record: "開標記錄", eval_report: "評標報告", award_result: "中標結果", contract_draft: "合同草稿", approval_sheet: "審批單", signature_proof: "簽章憑證" };
const kindLabel = (k) => t(KIND[k] || k || "節點");
const artLabel = (k) => t(ARTS[k] || k || "—");
const assignText = (n) => {
  const label = t(ASSIGN[n.assign_rule] || n.assign_rule || "未配置");
  return n.assign_value ? label + " · " + n.assign_value : label;
};
const statTag = (s) => {
  const [tone, label, dot] = ISTAT[s] || ISTAT.running;
  return <T tone={tone} dot={dot && s === "running"}>{t(label)}</T>;
};

/* ── 流水詳情抽屜(GET /api/wf/instances/:id)── */
const InstDrawer = ({ item, wfName, onClose }) => {
  const [d, setD] = _s(null);
  _e(() => {
    let on = true;
    setD(null);
    W2.json("/api/wf/instances/" + item.id).then((x) => { if (on) setD(x || {}); }).catch(() => { if (on) setD({}); });
    return () => { on = false; };
  }, [item.id]);
  const inst = (d && d.instance) || {};
  const timeline = arr(d && d.timeline);
  const artifacts = arr(d && d.artifacts);
  const title = inst.title || item.title || "—";
  const no = inst.instance_no || item.instance_no || "—";
  const facts = [
    [t("流程"), wfName(inst.workflow_key || item.workflow_key)],
    [t("當前節點"), inst.current_node_key || item.current_node_key || "—"],
    [t("發起於"), wfDate(inst.created_at || item.created_at)],
    [t("完成於"), wfDate(inst.completed_at || item.completed_at)],
  ];
  return (
    <div className="drawer">
      <div style={{ padding: "16px 18px", borderBottom: "2px solid var(--rule)" }}>
        <div className="row spread" style={{ marginBottom: 10 }}>
          {statTag(inst.status || item.status)}
          <button className="btn ghost sm" style={{ padding: "0 7px" }} onClick={onClose} title="Esc"><I name="x" size={13}/></button>
        </div>
        <div style={{ fontSize: 18, fontWeight: 750, letterSpacing: "-.025em", lineHeight: 1.3 }}>{title}</div>
        <div className="num muted" style={{ fontSize: 11.5, marginTop: 5 }}>{no}</div>
      </div>
      <div style={{ padding: 18, maxHeight: "calc(100vh - 280px)", overflowY: "auto" }}>
        <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 14, marginBottom: 18 }}>
          {facts.map(([k, v]) => (
            <div key={k} className="col g4" style={{ borderTop: "1px solid var(--hair)", paddingTop: 8 }}>
              <LB dim style={{ fontSize: 8.5 }}>{k}</LB>
              <span className="num" style={{ fontSize: 13, fontWeight: 650, wordBreak: "break-all" }}>{v || "—"}</span>
            </div>
          ))}
        </div>

        <LB dim style={{ fontSize: 8.5, marginBottom: 8 }}>{t("流程時間線")}</LB>
        <div style={{ borderTop: "1px solid var(--hair)", paddingTop: 10, marginBottom: 18 }}>
          {d == null ? (
            <div className="muted" style={{ fontSize: 12 }}>{t("詳情載入中…")}</div>
          ) : timeline.length ? (
            <div className="col g8">
              {timeline.map((row, i) => (
                <div key={row.id || i} className="row g8" style={{ fontSize: 12, alignItems: "baseline" }}>
                  <span style={{ width: 7, height: 7, flexShrink: 0, background: row.action === "reject" ? "var(--red)" : row.action === "complete" ? "var(--ok)" : "var(--ink-4)" }}/>
                  <span style={{ fontWeight: 650 }}>{t(ACT[row.action] || row.action || "—")}</span>
                  {row.comment ? <span className="ink2" style={{ flex: 1, minWidth: 0 }}>「{row.comment}」</span> : <span style={{ flex: 1 }}/>}
                  <span className="num muted" style={{ fontSize: 10.5, flexShrink: 0 }}>{wfDate(row.created_at)}</span>
                </div>
              ))}
            </div>
          ) : (
            <div className="muted" style={{ fontSize: 12 }}>{t("暫無時間線記錄")}</div>
          )}
        </div>

        {!!artifacts.length && (
          <>
            <LB dim style={{ fontSize: 8.5, marginBottom: 8 }}>{t("已留存證明")}</LB>
            <div style={{ borderTop: "1px solid var(--hair)", paddingTop: 10, marginBottom: 18 }} className="col g8">
              {artifacts.map((a, i) => (
                <div key={a.id || i} className="row g8" style={{ fontSize: 12, alignItems: "baseline" }}>
                  <I name="clipboard" size={12} color="var(--ink-3)"/>
                  <span style={{ fontWeight: 650 }}>{artLabel(a.kind)}</span>
                  <span className="muted num" style={{ marginLeft: "auto", fontSize: 10.5, textAlign: "right", wordBreak: "break-all" }}>{a.content_text || a.title || "—"}</span>
                </div>
              ))}
            </div>
          </>
        )}

        <LB dim style={{ fontSize: 8.5, marginBottom: 8 }}>{t("直接吩咐秘書")}</LB>
        <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 8 }}>
          <button className="btn" style={{ height: 40, justifyContent: "flex-start", fontSize: 12.5 }}
            onClick={() => ask(t("流程「{title}」(單號 {no})推進得怎麼樣?卡住的話幫我催辦當前處理人", { title, no }))}>
            <I name="clock" size={14}/>{t("催辦這個流程")}
          </button>
          <button className="btn" style={{ height: 40, justifyContent: "flex-start", fontSize: 12.5 }}
            onClick={() => ask(t("幫流程「{title}」(單號 {no})登記證明材料:請追問材料類型和憑證編號/說明後登記", { title, no }))}>
            <I name="clipboard" size={14}/>{t("讓秘書登記材料")}
          </button>
        </div>
        <div className="muted" style={{ fontSize: 10.5, marginTop: 12, lineHeight: 1.6 }}>{t("2.0 約定:頁面只讀,改動經秘書確認執行,全程留痕。")}</div>
      </div>
    </div>
  );
};

/* ── 頁面 ── */
const Page = ({ boot }) => {
  const [tasks, setTasks] = _s([]);
  const [insts, setInsts] = _s([]);
  const [flows, setFlows] = _s(null);
  const [flowKey, setFlowKey] = _s("");
  const [flowMap, setFlowMap] = _s(null);
  const [scope, setScope] = _s("all");
  const [sel, setSel] = _s(null);
  const [tick, setTick] = _s(0);

  _e(() => {
    W2.json("/api/wf/inbox").then((d) => setTasks(arr(d && d.tasks))).catch(() => setTasks([]));
    W2.json("/api/wf/my-instances").then((d) => setInsts(arr(d && d.instances))).catch(() => setInsts([]));
    W2.json("/api/wf/workflows").then((d) => {
      const list = arr(d && d.workflows);
      setFlows(list);
      setFlowKey((k) => (k && list.some((w) => w.workflow_key === k)) ? k : ((list[0] && list[0].workflow_key) || ""));
    }).catch(() => setFlows([]));
  }, [tick]);

  _e(() => {
    if (!flowKey) { setFlowMap(null); return; }
    let on = true;
    setFlowMap(null);
    W2.json("/api/wf/workflows/" + flowKey + "/map").then((m) => { if (on) setFlowMap(m || {}); }).catch(() => { if (on) setFlowMap({}); });
    return () => { on = false; };
  }, [flowKey, tick]);

  _e(() => {
    const h = (e) => { if (e.key === "Escape") setSel(null); };
    window.addEventListener("keydown", h);
    return () => window.removeEventListener("keydown", h);
  }, []);

  const wfName = (key) => {
    const w = arr(flows).find((x) => x.workflow_key === key);
    return (w && w.name) || key || "—";
  };

  const running = insts.filter((x) => x.status === "running" || x.status === "waiting");
  const closed = insts.filter((x) => x.status === "completed");
  const dead = insts.filter((x) => x.status === "rejected" || x.status === "cancelled");
  const overdueTasks = tasks.filter((x) => isOverdue(x.due_at));

  const shown = _mm(() => {
    let list = insts;
    if (scope === "running") list = running;
    if (scope === "completed") list = closed;
    if (scope === "rejected") list = list.filter((x) => x.status === "rejected");
    return list;
  }, [insts, scope]);

  /* 流程拓撲:按階段分組 */
  const mapNodes = arr(flowMap && flowMap.nodes);
  const mapStages = arr(flowMap && flowMap.stages);
  const nodeByKey = _mm(() => {
    const m = {};
    mapNodes.forEach((n) => { if (n && n.node_key) m[n.node_key] = n; });
    return m;
  }, [flowMap]);
  const todoByNode = _mm(() => {
    const m = {};
    tasks.filter((x) => x.workflow_key === flowKey).forEach((x) => { m[x.node_key] = (m[x.node_key] || 0) + 1; });
    return m;
  }, [tasks, flowKey]);
  const groups = _mm(() => {
    if (!mapNodes.length) return [];
    const sorted = [...mapNodes].sort((a, b) => num(a.step_no) - num(b.step_no));
    const gs = mapStages.map((s) => ({ key: s.key, name: s.name, nodes: sorted.filter((n) => n.stage_key === s.key) })).filter((g) => g.nodes.length);
    const known = new Set(gs.map((g) => g.key));
    const loose = sorted.filter((n) => !known.has(n.stage_key));
    if (loose.length) gs.push({ key: "_loose", name: gs.length ? t("未分組") : "", nodes: loose });
    return gs;
  }, [flowMap, mapStages]);

  let nodeCounter = 0;

  return (
    <>
      <Folio no="10" en="PROCUREMENT" title={t("採購招標")}
        sub={t("流程化採購協作 · 招標評審 · 全程留痕") + " · " + t("待辦 {a} 條 · 進行中 {b} 條 · 頁面只讀,操作交秘書", { a: tasks.length, b: running.length })}
        right={<>
          <B icon="refresh" onClick={() => { setSel(null); setTick((x) => x + 1); }}>{t("刷新")}</B>
          <B icon="search" onClick={() => ask(t("幫我做一輪詢價:請追問物資名稱、數量和意向供應商,整理成詢價單並跟進報價"))}>{t("詢價")}</B>
          <B icon="plus" onClick={() => ask(t("我要發起一個採購或招標流程,請追問流程類型、事由標題和預算金額,經我確認後執行"))}>{t("發起流程")}</B>
          <B kind="primary" icon="sparkle" onClick={() => ask(t("看看採購招標現在的待辦、流程阻塞和權限缺口,按優先級給我可執行的下一步"))}>{t("問秘書")}</B>
        </>}/>

      <div className="kpi-band">
        <Kpi label={t("我的待辦")} value={tasks.length} unit={t("條")} red={tasks.length > 0} delay={0}
          foot={overdueTasks.length
            ? <T tone="bad" dot>{t("{n} 條已逾期", { n: overdueTasks.length })}</T>
            : <span className="muted" style={{ fontSize: 11.5 }}>{t("無逾期")}</span>}/>
        <Kpi label={t("進行中流程")} value={running.length} unit={t("條")} delay={.05}
          foot={<span className="muted" style={{ fontSize: 11.5 }}>{t("招標 · 採購 · 審批鏈")}</span>}/>
        <Kpi label={t("已閉環")} value={closed.length} unit={t("條")} delay={.1}
          foot={dead.length
            ? <span className="muted" style={{ fontSize: 11.5 }}>{t("含已駁回 / 已取消 {n} 條", { n: dead.length })}</span>
            : <span className="muted" style={{ fontSize: 11.5 }}>{t("全流水可追溯")}</span>}/>
        <Kpi label={t("流程模板")} value={arr(flows).length} unit={t("個")} delay={.15}
          foot={<T tone="plain">WORKFLOW</T>}/>
      </div>

      {/* A · 我的待辦 */}
      <Band no="A" title={t("我的待辦")} sub={t("限時待辦優先 · 推進交秘書")} delay={.1}
        right={tasks.length ? <B size="sm" icon="sparkle" onClick={() => ask(t("把我的採購待辦按緊急程度排序,逐條給我處理建議,經我確認後推進"))}>{t("全部交秘書梳理")}</B> : null}>
        {tasks.length ? (
          <div style={{ borderTop: "2px solid var(--rule)" }}>
            {tasks.map((task, i) => {
              const od = isOverdue(task.due_at);
              return (
                <div key={task.id || i} className="ledger-row">
                  <span className="lr-idx">{pad2(i + 1)}</span>
                  <div className="col g4" style={{ flex: 1.5, minWidth: 0 }}>
                    <span className="row g8 wrap" style={{ fontWeight: 650, fontSize: 13.5 }}>
                      {task.node_name || task.node_key || "—"}
                      <span className="mono muted" style={{ fontSize: 9.5, letterSpacing: ".1em" }}>{t("步 {n}", { n: task.step_no == null ? "—" : task.step_no })}</span>
                    </span>
                    <span className="muted" style={{ fontSize: 12 }}>{task.instance_title || "—"}<span className="num"> · {task.instance_no || "—"}</span></span>
                  </div>
                  <T tone={KIND_TONE[task.node_kind] || "plain"}>{kindLabel(task.node_kind)}</T>
                  <span className="num" style={{ fontSize: 11.5, width: 150, flexShrink: 0, color: od ? "var(--red)" : "var(--ink-3)", textAlign: "right" }}>
                    {task.due_at ? t("限 {d}", { d: wfDate(task.due_at) }) + (od ? " · " + t("已逾期") : "") : "—"}
                  </span>
                  <B size="sm" icon="check" onClick={() => ask(t("推進待辦「{node}」(流程「{title}」,單號 {no}):請先核對必需材料和上下文,建議通過還是駁回,經我確認後執行", { node: task.node_name || task.node_key || "—", title: task.instance_title || "—", no: task.instance_no || "—" }))}>{t("推進")}</B>
                </div>
              );
            })}
          </div>
        ) : (
          <EM icon="checkCircle" title={t("沒有待你處理的任務")} sub={t("流程走到你這一步時,待辦會第一時間出現在這裡。")}/>
        )}
      </Band>

      {/* B · 採購 / 招標流水 */}
      <Band no="B" title={t("採購 / 招標流水")} sub={t("我發起的流程 · 點行看流轉")} delay={.15}
        right={<div className="seg">
          {[["all", "全部"], ["running", "進行中"], ["completed", "已閉環"], ["rejected", "已駁回"]].map(([id, label]) => (
            <button key={id} className={scope === id ? "on" : ""} onClick={() => { setScope(id); setSel(null); }}>{t(label)}</button>
          ))}
        </div>}>
        {insts.length ? (
          <div style={{ display: "flex", gap: 24, alignItems: "flex-start" }}>
            <div style={{ flex: 1, minWidth: 0, overflowX: "auto" }}>
              <table className="tbl2">
                <thead><tr>
                  <th>{t("單號")}</th><th>{t("事由")}</th><th>{t("流程")}</th><th>{t("狀態")}</th><th>{t("當前節點")}</th><th>{t("發起時間")}</th><th style={{ width: 90 }}>{t("交給秘書")}</th>
                </tr></thead>
                <tbody>
                  {shown.map((it, i) => (
                    <tr key={it.id || i} className={sel && sel.id === it.id ? "on" : ""} style={{ cursor: "pointer" }} onClick={() => setSel(it)}>
                      <td><span className="num" style={{ fontWeight: 600, fontSize: 12 }}>{it.instance_no || "—"}</span></td>
                      <td><span style={{ fontWeight: 650 }}>{it.title || "—"}</span></td>
                      <td className="muted" style={{ fontSize: 12.5 }}>{wfName(it.workflow_key)}</td>
                      <td>{statTag(it.status)}</td>
                      <td><span className="num muted" style={{ fontSize: 11.5 }}>{it.current_node_key || "—"}</span></td>
                      <td><span className="num muted" style={{ fontSize: 11.5 }}>{wfDate(it.created_at)}</span></td>
                      <td onClick={(e) => e.stopPropagation()}>
                        <B size="sm" icon="clock" onClick={() => ask(t("查一下流程「{title}」(單號 {no})的當前進度、卡在哪個節點,需要催辦就幫我催辦", { title: it.title || "—", no: it.instance_no || "—" }))}>{t("查進度")}</B>
                      </td>
                    </tr>
                  ))}
                </tbody>
              </table>
              {!shown.length && <EM icon="search" title={t("當前篩選下沒有流程")}/>}
            </div>
            {sel && <InstDrawer item={sel} wfName={wfName} onClose={() => setSel(null)}/>}
          </div>
        ) : (
          <EM icon="doc" title={t("還沒有採購 / 招標流水")} sub={t("對秘書說「發起採購」,第一條流水就從這裡開始。")}
            action={<B icon="plus" onClick={() => ask(t("我要發起一個採購或招標流程,請追問流程類型、事由標題和預算金額,經我確認後執行"))}>{t("發起第一個流程")}</B>}/>
        )}
      </Band>

      {/* C · 流程拓撲 */}
      <Band no="C" title={t("流程拓撲")}
        sub={mapNodes.length ? t("{s} 階段 · {n} 節點 · 指派與流轉規則", { s: mapStages.length, n: mapNodes.length }) : ""}
        delay={.2}
        right={flowKey ? <B size="sm" icon="sparkle" onClick={() => ask(t("結合流程「{wf}」的拓撲,檢查阻塞節點、權限缺口和外部留痕風險,給我下一步可執行動作", { wf: wfName(flowKey) }))}>{t("秘書研判")}</B> : null}>
        {flows == null ? (
          <div className="muted" style={{ fontSize: 12.5, padding: "20px 0" }}>{t("載入中…")}</div>
        ) : !arr(flows).length ? (
          <EM icon="layers" title={t("尚無流程模板")} sub={t("流程模板由系統預置或管理員配置。先問秘書採購流程怎麼走也可以。")}
            action={<B icon="sparkle" onClick={() => ask(t("看看採購招標現在的待辦、流程阻塞和權限缺口,按優先級給我可執行的下一步"))}>{t("問秘書")}</B>}/>
        ) : (
          <>
            <div className="row g6 wrap" style={{ marginBottom: 16 }}>
              {arr(flows).map((w) => (
                <button key={w.workflow_key} className={"chip" + (flowKey === w.workflow_key ? " on" : "")} onClick={() => setFlowKey(w.workflow_key)}>
                  {w.name || w.workflow_key}
                </button>
              ))}
            </div>
            {flowMap == null ? (
              <div className="muted" style={{ fontSize: 12.5, padding: "12px 0" }}>{t("載入中…")}</div>
            ) : !mapNodes.length ? (
              <EM icon="layers" title={t("此流程暫無節點定義")}/>
            ) : (
              groups.map((g) => (
                <div key={g.key} style={{ marginBottom: 6 }}>
                  {g.name ? <LB dim style={{ padding: "12px 0 6px", borderBottom: "1px solid var(--hair)" }}>{g.name}</LB> : null}
                  {g.nodes.map((n) => {
                    nodeCounter += 1;
                    const next = n.on_approve_next ? nodeByKey[n.on_approve_next] : null;
                    const todo = todoByNode[n.node_key] || 0;
                    const arts = arr(n.artifactKinds);
                    return (
                      <div key={n.node_key || nodeCounter} className="ledger-row">
                        <span className="lr-idx">{pad2(num(n.step_no) || nodeCounter)}</span>
                        <div className="col g4" style={{ flex: 1.7, minWidth: 0 }}>
                          <span className="row g8 wrap" style={{ fontWeight: 650, fontSize: 13 }}>
                            {n.name || n.node_key || "—"}
                            {todo > 0 && <T tone="redinv">{t("待辦 {n}", { n: todo })}</T>}
                          </span>
                          <span className="muted num" style={{ fontSize: 10.5 }}>
                            {n.node_key || "—"}{" → "}{next ? (next.name || next.node_key) : t("流程完成")}
                          </span>
                        </div>
                        <T tone={KIND_TONE[n.node_kind] || "plain"}>{kindLabel(n.node_kind)}</T>
                        <span className="ink2" style={{ fontSize: 12, flex: 1, minWidth: 0 }}>{assignText(n)}</span>
                        <span className="col g4" style={{ width: 170, flexShrink: 0, alignItems: "flex-end" }}>
                          {n.required_permission
                            ? (n.permissionSatisfied === false
                              ? <T tone="bad" dot>{t("缺權限")}</T>
                              : <span className="mono muted" style={{ fontSize: 9.5, wordBreak: "break-all", textAlign: "right" }}>{n.required_permission}</span>)
                            : null}
                          <span className="mono muted" style={{ fontSize: 9.5 }}>
                            {[num(n.quorum) > 1 ? t("會簽 {n}", { n: n.quorum }) : "", arts.length ? t("材料 {n}", { n: arts.length }) : "", n.sla_hours ? t("SLA {n} 小時", { n: n.sla_hours }) : ""].filter(Boolean).join(" · ")}
                          </span>
                        </span>
                        <B size="sm" icon="sparkle" onClick={() => ask(t("分析流程「{wf}」第 {step} 步「{node}」:處理要點、指派與權限規則、必需材料和下一步流轉", { wf: wfName(flowKey), step: n.step_no == null ? "—" : n.step_no, node: n.name || n.node_key || "—" }))}/>
                      </div>
                    );
                  })}
                </div>
              ))
            )}
          </>
        )}
      </Band>
    </>
  );
};

window.W2.PAGES["procurement"] = Page;
})();
