/* global React, Icon, Button, Field, Select, Tag, STATUS, Check, useToast, Textarea, Dialog, ModeToggle, ChipFilter,
   ROLES, PRODUCT_MASTER, SALE_STATUS_LABELS, computeAggStatus, AggStatusDisplay,
   CellEditor, TableGroupHeader, MetaCard, SubmitConfirmPage, RowActions, BulkBar, DeleteRowBtn, InfoTip, TruncCell, PageHead, ImportBanner, UserChip, SkuToolbar, EmptyState,
   ShowErrorsProvider, useFormValidation, RowErrTip, ADJ_TRACKING_COLS, renderAdjTrackingCell, isPrototypeSuperUser, canActAsPurchase, canActAsJoanna */
// ADJ-B L2 — 價格調整 批次明細

const { useState, useMemo, useCallback, useEffect } = React;

const ADD_LABEL = "新增品項";

// ── Column definitions ──────────────────────────────────────────────────────
const pc = (key, label, w, group, opts = {}) => ({ key, label, w, group, ...opts });

const PA_NUM_COL_W = 32;

const PA_EDIT_FULL_COLS = [
  pc("#",                    "#",              PA_NUM_COL_W, null, { stickyNum: true }),
  pc("barcode",              "品號",            160, "A",  { mono: true, sticky2nd: true }),
  pc("name",                 "品名",            200, "A"),
  pc("mainCat",              "主類別",            90, "B"),
  pc("subCat",               "次類別",            90, "B"),
  pc("segment3",             "子分類(分析用)",   180, "B"),
  pc("saleStatus",        "商品狀態",          80, "C"),
  pc("productImage",         "商品主圖",         180, "C"),
  pc("taxType",              "含稅別",            70, "C"),
  pc("origCostExTax",        "[原] 未稅進價",    110, "D",  { mono: true }),
  pc("origCostInTax",        "[原] 含稅進價",    110, "D",  { mono: true }),
  pc("newCostExTax",         "新進價(未稅)",     110, "D",  { mono: true }),
  pc("newCostInTax",         "新進價(含稅)",     110, "D",  { mono: true }),
  pc("newCostEffectiveDate", "新進價生效日",     130, "D"),
  pc("origPrice",            "[原] 售價",         90, "E",  { mono: true }),
  pc("newPrice",             "新售價",            90, "E",  { mono: true }),
  pc("newPriceEffectiveDate","新售價生效日",     130, "E"),
  pc("newCostPromoEndDate",  "促銷到期日",       130, "E"),
  pc("origMargin",           "原毛利(%)",         80, "G"),
  pc("newMargin",            "新毛利(%)",         80, "G"),
  pc("activityName",         "活動名稱",         140, "F"),
  pc("activityStartDate",    "活動開始日",       130, "F"),
  pc("activityEndDate",      "活動結束日",       130, "F"),
  pc("adjNote",              "商品調整備註",     150, "F"),
  pc("_action",              "操作",              44, null, { actionCol: true }),
];

const PA_REVIEW_EXTRA_COLS = [
  pc("finalCostInTax","調整後進價(含稅)",  110, "G", { mono: true }),
  pc("finalPrice",    "調整後售價",          90, "G", { mono: true }),
  pc("priceChange",   "售價增減",            80, "G", { mono: true }),
  pc("rowStatus",     "審核狀態",            90, "H"),
  pc("reviewPass",    "審核結果",           130, "H"),
  pc("rejectReason",  "退回原因",           180, "H"),
  ...ADJ_TRACKING_COLS({ group: "H", erpHint: "ERP 系統完成價格調整後，由系統管理員手動勾選確認。" }),
  pc("createdAt",     "建立時間",           120, "H", { mono: true }),
  pc("updatedAt",     "更新時間",           120, "H", { mono: true }),
];

const PA_REVIEW_FULL_COLS = [
  ...PA_EDIT_FULL_COLS.filter(c => c.key !== "_action" && c.key !== "adjNote"),
  ...PA_REVIEW_EXTRA_COLS,
  { ...PA_EDIT_FULL_COLS.find(c => c.key === "adjNote"), group: "H" },
];

const PA_COL_GROUP_LABELS = { A: "商品識別", B: "分類", C: "商品資訊", D: "進價調整", E: "售價調整", F: "其他", G: "毛利試算", H: "審核" };

// Compact key sets
const PA_COMPACT_EDIT_KEYS = ["#", "barcode", "name", "origCostExTax", "origCostInTax", "newCostExTax", "newCostInTax", "newCostEffectiveDate", "origPrice", "newPrice", "newPriceEffectiveDate", "newCostPromoEndDate", "origMargin", "newMargin", "adjNote", "_action"];
const PA_COMPACT_REVIEW_KEYS = ["#", "barcode", "name", "origCostExTax", "origCostInTax", "newCostExTax", "newCostInTax", "newCostEffectiveDate", "newCostPromoEndDate", "origPrice", "newPrice", "newPriceEffectiveDate", "origMargin", "newMargin", "priceChange", "adjNote", "rowStatus", "reviewPass", "rejectReason"];
const PA_COMPACT_SAMANTHA_KEYS = ["#", "barcode", "name", "newCostInTax", "newPrice", "newMargin", "rowStatus", "erpUpdated"];

// Editable field definitions for CellEditor
const PA_FIELDS = {
  barcode:              { key: "barcode",              type: "str",  label: "品號",         mono: true, placeholder: "輸入品號…" },
  newCostExTax:         { key: "newCostExTax",         type: "num",  label: "新進價(未稅)",  unit: "NT$", validate: (row) => (!row.newCostExTax && !row.newPrice) ? "新進價(未稅)或新售價至少填一項" : null },
  newCostInTax:         { key: "newCostInTax",         type: "num",  label: "新進價(含稅)",  unit: "NT$" },
  newCostEffectiveDate: { key: "newCostEffectiveDate", type: "date", label: "新進價生效日", validate: (row) => (!row.newCostEffectiveDate && !row.newPriceEffectiveDate) ? "進價或售價生效日至少填一項" : null },
  newCostPromoEndDate:  { key: "newCostPromoEndDate",  type: "date", label: "促銷到期日" },
  newPrice:             { key: "newPrice",             type: "num",  label: "新售價",        unit: "NT$", validate: (row) => (!row.newCostExTax && !row.newPrice) ? "新進價(未稅)或新售價至少填一項" : null },
  newPriceEffectiveDate:{ key: "newPriceEffectiveDate",type: "date", label: "新售價生效日", validate: (row) => (!row.newCostEffectiveDate && !row.newPriceEffectiveDate) ? "進價或售價生效日至少填一項" : null },
  activityName:         { key: "activityName",         type: "str",  label: "活動名稱",     placeholder: "選填…" },
  activityStartDate:    { key: "activityStartDate",    type: "date", label: "活動開始日" },
  activityEndDate:      { key: "activityEndDate",      type: "date", label: "活動結束日" },
  adjNote:              { key: "adjNote",              type: "text", label: "商品調整備註",  placeholder: "選填備註…" },
};

// ── Helpers ─────────────────────────────────────────────────────────────────
const fmtPrice = (v) => v && +v > 0 ? `NT$${(+v).toLocaleString("zh-TW")}` : "—";
const fmtMargin = (price, cost) => {
  if (!price || !cost || +price <= 0 || +cost <= 0) return null;
  return (((+price - +cost) / +price) * 100).toFixed(1) + "%";
};

// ── SysCheck logic ──────────────────────────────────────────────────────────
const paSysCheckRow = (row) => {
  if (!row.newCostExTax && !row.newPrice)
    return { pass: false, msg: "新進價(未稅)或新售價至少填寫其中一項" };
  if (row.newCostExTax && +row.newCostExTax <= 0)
    return { pass: false, msg: "新進價(未稅)必須大於 0" };
  if (row.newPrice && +row.newPrice <= 0)
    return { pass: false, msg: "新售價必須大於 0" };
  if (!row.newCostEffectiveDate && !row.newPriceEffectiveDate)
    return { pass: false, msg: "新進價生效日或新售價生效日至少填寫其中一項" };
  return { pass: true, msg: "✓ 檢查通過" };
};

// ── PriceAdjMetaCard ─────────────────────────────────────────────────────────
const PriceAdjMetaCard = ({ batch }) => {
  const agg = computeAggStatus(batch);
  const statusText = agg.type === "single"
    ? (STATUS[agg.key]?.label || agg.key)
    : "混合審核狀態";
  const summary = `${batch.batchName} · ${batch.applicant} · ${batch.rows.length} 筆 · ${statusText}`;

  return (
    <MetaCard summary={summary} sectionTitle="批次資訊">
      <div className="meta-card__row">
        <dt>批次名稱</dt>
        <dd>{batch.batchName}</dd>
      </div>
      <div className="meta-card__row">
        <dt>申請人</dt>
        <dd><UserChip name={batch.applicant} /></dd>
      </div>
      <div className="meta-card__row">
        <dt>更新時間</dt>
        <dd><span className="mono">{batch.updatedAt}</span></dd>
      </div>
      <div className="meta-card__row">
        <dt>品項筆數</dt>
        <dd><b>{batch.rows.length}</b> 筆</dd>
      </div>
      <div className="meta-card__row">
        <dt>審核狀態</dt>
        <dd><AggStatusDisplay batch={batch} /></dd>
      </div>
    </MetaCard>
  );
};

// ── DoubleCheck view ─────────────────────────────────────────────────────────
const PA_DC_COLS = [
  { key: "#",                    label: "#",             w: 48,  group: null, stickyNum: true },
  { key: "_product",             label: "品號 / 品名",   w: 200, group: null, sticky2nd: true },
  { key: "origCostExTax",        label: "[原] 未稅進價", w: 110, group: "D",  mono: true },
  { key: "origCostInTax",        label: "[原] 含稅進價", w: 110, group: "D",  mono: true },
  { key: "newCostExTax",         label: "新進價(未稅)",  w: 110, group: "D",  mono: true },
  { key: "newCostInTax",         label: "新含稅進價",    w: 110, group: "D",  mono: true },
  { key: "origPrice",            label: "[原] 售價",     w: 100, group: "E",  mono: true },
  { key: "newPrice",             label: "新售價",        w: 100, group: "E",  mono: true },
  { key: "origMargin",           label: "原毛利",         w: 90, group: "G" },
  { key: "newMargin",            label: "新毛利",         w: 90, group: "G" },
  { key: "newCostEffectiveDate", label: "進價生效日",    w: 110, group: "F" },
  { key: "newPriceEffectiveDate",label: "售價生效日",    w: 110, group: "F" },
  { key: "adjNote",              label: "商品調整備註",  w: 180, group: "F" },
];
const PA_DC_GROUP_LABELS = { D: "進價調整（原 → 新）", E: "售價調整（原 → 新）", G: "毛利試算", F: "調整資訊" };

const PriceAdjDoubleCheckView = ({ batch, onBack, onConfirm }) => {
  const rows = batch.rows;
  const [sysChecked, setSysChecked] = useState(false);

  const checks   = useMemo(() => rows.map(r => paSysCheckRow(r)), [rows]);
  const errCount = checks.filter(c => !c.pass).length;
  const allPass  = rows.length > 0 && errCount === 0;

  const handleConfirm = () => {
    setSysChecked(true);
    if (allPass) onConfirm();
  };

  const handleBack = () => {
    if (sysChecked && !allPass) {
      const errMap = {};
      rows.forEach((r, i) => { if (!checks[i].pass) errMap[r.uid] = checks[i].msg; });
      onBack(errMap);
    } else {
      onBack(null);
    }
  };

  const renderDCCell = (col, row, idx) => {
    const k = col.key;
    const base = {};
    if (k === "#") {
      const dcErrMsg = sysChecked && !checks[idx].pass ? checks[idx].msg : null;
      return (
        <td key={k} style={{ ...base, textAlign: "center", color: "var(--fg-3)", position: "sticky", left: 0, background: "var(--bg-surface)", zIndex: 1 }}>
          {dcErrMsg && <RowErrTip msg={dcErrMsg} />}
          {idx + 1}
        </td>
      );
    }
    if (k === "_product") return (
      <td key={k} style={{ position: "sticky", left: 48, background: "var(--bg-surface)", zIndex: 1, boxShadow: "2px 0 4px rgba(0,0,0,.06)" }}>
        <div className="mono" style={base}>{row.barcode}</div>
        <div style={{ fontSize: 11, color: "var(--fg-3)", marginTop: 2 }}>{row.name}</div>
      </td>
    );
    const oldStyle = { ...base, color: "var(--fg-3)", fontFamily: "var(--font-mono)" };
    if (k === "origCostExTax") return <td key={k} style={oldStyle}>{fmtPrice(row.origCostExTax)}</td>;
    if (k === "origCostInTax") return <td key={k} style={oldStyle}>{fmtPrice(row.origCostInTax)}</td>;
    if (k === "origPrice")     return <td key={k} style={oldStyle}>{fmtPrice(row.origPrice)}</td>;
    if (k === "origMargin") {
      const m = fmtMargin(row.origPrice, row.origCostInTax);
      return <td key={k} style={{ ...base, color: "var(--fg-3)" }}>{m || "—"}</td>;
    }
    const mkNew = (newVal, oldVal, display) => {
      const isChanged = String(oldVal || "") !== String(newVal || "") && newVal;
      return <td key={k} style={{ ...base, color: isChanged ? "var(--tag-approved-fg)" : "var(--fg-2)", fontWeight: isChanged ? 600 : 400, fontFamily: col.mono ? "var(--font-mono)" : undefined }}>{display || "—"}</td>;
    };
    if (k === "newCostExTax") return mkNew(row.newCostExTax, row.origCostExTax, fmtPrice(row.newCostExTax));
    if (k === "newCostInTax") return mkNew(row.newCostInTax, row.origCostInTax, fmtPrice(row.newCostInTax));
    if (k === "newPrice")     return mkNew(row.newPrice, row.origPrice, fmtPrice(row.newPrice));
    if (k === "newMargin") {
      const finalCost  = row.newCostInTax  || row.origCostInTax;
      const finalPrice = row.newPrice      || row.origPrice;
      const m = fmtMargin(finalPrice, finalCost);
      const origM = fmtMargin(row.origPrice, row.origCostInTax);
      const isChanged = m && m !== origM;
      return <td key={k} style={{ ...base, color: isChanged ? "var(--tag-approved-fg)" : "var(--fg-2)", fontWeight: isChanged ? 600 : 400 }}>{m || "—"}</td>;
    }
    if (k === "newCostEffectiveDate" || k === "newPriceEffectiveDate") {
      return <td key={k} className="mono" style={base}>{row[k] || <span style={{ color: "var(--fg-4)" }}>—</span>}</td>;
    }
    if (k === "adjNote") return <td key={k} style={{ ...base, color: "var(--fg-2)" }}>{row.adjNote || <span style={{ color: "var(--fg-4)" }}>—</span>}</td>;
    return <td key={k}>—</td>;
  };

  return (
    <SubmitConfirmPage
      moduleName="價格調整" batchName={batch.batchName}
      subtitle={sysChecked && !allPass
        ? "系統檢查未通過，請修正後重新送出。"
        : "請確認以下品項的進價與售價調整無誤，確認後將進行系統檢查並送審。"
      }
      onBack={handleBack} onConfirm={handleConfirm}
      confirmDisabled={sysChecked && !allPass}
    >
      {sysChecked && !allPass && (
        <ImportBanner
          phase="error"
          titlePrefix="系統檢查："
          titleSuffix="品項需要修正"
          desc="請修正後點擊「返回修改」，重新送出。"
          errorCount={errCount}
        />
      )}
      <div className="card">
        <div className="tbl-wrap">
          <table className="tbl" style={{ borderCollapse: "separate", borderSpacing: 0 }}>
            <thead>
              <TableGroupHeader cols={PA_DC_COLS} groupLabels={PA_DC_GROUP_LABELS} />
              <tr>
                {PA_DC_COLS.map(c => {
                  const s = { width: c.w, minWidth: c.w };
                  if (c.stickyNum) return <th key={c.key} style={{ ...s, position: "sticky", left: 0, zIndex: 2 }}>{c.label}</th>;
                  if (c.sticky2nd) return <th key={c.key} style={{ ...s, position: "sticky", left: 48, zIndex: 2, boxShadow: "2px 0 4px rgba(0,0,0,.06)" }}>{c.label}</th>;
                  return <th key={c.key} style={s}>{c.label}</th>;
                })}
              </tr>
            </thead>
            <tbody>
              {rows.map((row, idx) => (
                <tr key={row.uid}>
                  {PA_DC_COLS.map(col => renderDCCell(col, row, idx))}
                </tr>
              ))}
            </tbody>
          </table>
        </div>
      </div>
    </SubmitConfirmPage>
  );
};

// ── BulkApprove view ─────────────────────────────────────────────────────────
const PriceAdjBulkApproveView = ({ batch, onBack, onConfirm }) => {
  const pendingRows = batch.rows.filter(r => r.rowStatus === "adjPending");

  const renderCell = (col, row, idx) => {
    const k = col.key;
    const base = {};
    if (k === "#") return (
      <td key={k} style={{ ...base, textAlign: "center", color: "var(--fg-3)", position: "sticky", left: 0, background: "var(--bg-surface)", zIndex: 1 }}>{idx + 1}</td>
    );
    if (k === "_product") return (
      <td key={k} style={{ position: "sticky", left: 48, background: "var(--bg-surface)", zIndex: 1, boxShadow: "2px 0 4px rgba(0,0,0,.06)" }}>
        <div className="mono" style={base}>{row.barcode}</div>
        <div style={{ fontSize: 11, color: "var(--fg-3)", marginTop: 2 }}>{row.name}</div>
      </td>
    );
    const oldStyle = { ...base, color: "var(--fg-3)", fontFamily: "var(--font-mono)" };
    if (k === "origCostExTax") return <td key={k} style={oldStyle}>{fmtPrice(row.origCostExTax)}</td>;
    if (k === "origCostInTax") return <td key={k} style={oldStyle}>{fmtPrice(row.origCostInTax)}</td>;
    if (k === "origPrice")     return <td key={k} style={oldStyle}>{fmtPrice(row.origPrice)}</td>;
    if (k === "origMargin") return <td key={k} style={{ ...base, color: "var(--fg-3)" }}>{fmtMargin(row.origPrice, row.origCostInTax) || "—"}</td>;
    const mkNew = (newVal, oldVal, display) => {
      const isChanged = String(oldVal || "") !== String(newVal || "") && newVal;
      return <td key={k} style={{ ...base, color: isChanged ? "var(--tag-approved-fg)" : "var(--fg-2)", fontWeight: isChanged ? 600 : 400, fontFamily: col.mono ? "var(--font-mono)" : undefined }}>{display || "—"}</td>;
    };
    if (k === "newCostExTax") return mkNew(row.newCostExTax, row.origCostExTax, fmtPrice(row.newCostExTax));
    if (k === "newCostInTax") return mkNew(row.newCostInTax, row.origCostInTax, fmtPrice(row.newCostInTax));
    if (k === "newPrice")     return mkNew(row.newPrice, row.origPrice, fmtPrice(row.newPrice));
    if (k === "newMargin") {
      const finalCost  = row.newCostInTax  || row.origCostInTax;
      const finalPrice = row.newPrice      || row.origPrice;
      const m = fmtMargin(finalPrice, finalCost);
      const origM = fmtMargin(row.origPrice, row.origCostInTax);
      const isChanged = m && m !== origM;
      return <td key={k} style={{ ...base, color: isChanged ? "var(--tag-approved-fg)" : "var(--fg-2)", fontWeight: isChanged ? 600 : 400 }}>{m || "—"}</td>;
    }
    if (k === "newCostEffectiveDate" || k === "newPriceEffectiveDate") {
      return <td key={k} className="mono" style={base}>{row[k] || "—"}</td>;
    }
    if (k === "adjNote") return <td key={k} style={{ ...base, color: "var(--fg-2)" }}>{row.adjNote || <span style={{ color: "var(--fg-4)" }}>—</span>}</td>;
    return <td key={k}>—</td>;
  };

  return (
    <SubmitConfirmPage
      moduleName="價格調整" batchName={batch.batchName}
      pageTitle="批准確認"
      subtitle={`確認以下 ${pendingRows.length} 筆品項批准通過，送出後無法撤回。`}
      confirmLabel="確認批准"
      onBack={onBack} onConfirm={onConfirm}>
      <div className="card">
        <div className="tbl-wrap">
          <table className="tbl" style={{ borderCollapse: "separate", borderSpacing: 0 }}>
            <thead>
              <TableGroupHeader cols={PA_DC_COLS} groupLabels={PA_DC_GROUP_LABELS} />
              <tr>
                {PA_DC_COLS.map(c => {
                  const s = { width: c.w, minWidth: c.w };
                  if (c.stickyNum) return <th key={c.key} style={{ ...s, position: "sticky", left: 0, zIndex: 2 }}>{c.label}</th>;
                  if (c.sticky2nd) return <th key={c.key} style={{ ...s, position: "sticky", left: 48, zIndex: 2, boxShadow: "2px 0 4px rgba(0,0,0,.06)" }}>{c.label}</th>;
                  return <th key={c.key} style={s}>{c.label}</th>;
                })}
              </tr>
            </thead>
            <tbody>
              {pendingRows.map((row, idx) => (
                <tr key={row.uid}>
                  {PA_DC_COLS.map(col => renderCell(col, row, idx))}
                </tr>
              ))}
            </tbody>
          </table>
        </div>
      </div>
    </SubmitConfirmPage>
  );
};

// ── JoannaReviewConfirmView（送出審核確認頁）─────────────────────────────────
const PriceAdjJoannaConfirmView = ({ batch, reviewDrafts, onBack, onConfirm }) => {
  const draftedRows = batch.rows.filter(r => reviewDrafts[r.uid]);
  return (
    <SubmitConfirmPage
      moduleName="價格調整" batchName={batch.batchName}
      pageTitle="送出審核"
      subtitle={`請確認以下 ${draftedRows.length} 筆品項的審核結果，送出後申請人將收到通知；退回的品項可重新送出。`}
      confirmLabel="確認送出"
      onBack={onBack} onConfirm={onConfirm}>
      <div className="card">
        <div className="tbl-wrap">
          <table className="tbl" style={{ borderCollapse: "separate", borderSpacing: 0 }}>
            <thead>
              <tr>
                <th style={{ width: 48, position: "sticky", left: 0, zIndex: 2 }}>#</th>
                <th style={{ width: 200, position: "sticky", left: 48, zIndex: 2, boxShadow: "2px 0 4px rgba(0,0,0,.06)" }}>品號 / 品名</th>
                <th style={{ width: 110 }}>[原] 未稅進價</th>
                <th style={{ width: 110 }}>新進價(未稅)</th>
                <th style={{ width: 100 }}>[原] 售價</th>
                <th style={{ width: 100 }}>新售價</th>
                <th style={{ width: 100 }}>審核結果</th>
                <th style={{ width: 200 }}>退回原因</th>
              </tr>
            </thead>
            <tbody>
              {draftedRows.map((row, idx) => {
                const d = reviewDrafts[row.uid];
                const isPass = d.reviewPass === "yes";
                const fmtP = v => v && +v > 0 ? `NT$${(+v).toLocaleString("zh-TW")}` : "—";
                return (
                  <tr key={row.uid}>
                    <td style={{ textAlign: "center", color: "var(--fg-3)", position: "sticky", left: 0, background: "var(--bg-surface)", zIndex: 1 }}>{idx + 1}</td>
                    <td style={{ position: "sticky", left: 48, background: "var(--bg-surface)", zIndex: 1, boxShadow: "2px 0 4px rgba(0,0,0,.06)" }}>
                      <div className="mono" style={{ fontSize: 12 }}>{row.barcode}</div>
                      <div style={{ fontSize: 11, color: "var(--fg-3)", marginTop: 2 }}>{row.name}</div>
                    </td>
                    <td className="mono" style={{ color: "var(--fg-3)" }}>{fmtP(row.origCostExTax)}</td>
                    <td className="mono">{fmtP(row.newCostExTax)}</td>
                    <td className="mono" style={{ color: "var(--fg-3)" }}>{fmtP(row.origPrice)}</td>
                    <td className="mono">{fmtP(row.newPrice)}</td>
                    <td style={{ color: isPass ? "var(--tag-approved-fg)" : "var(--danger)", fontWeight: 600 }}>
                      {isPass ? "✓ 通過" : "✗ 退回"}
                    </td>
                    <td style={{ color: "var(--fg-2)" }}>{d.rejectReason || <span style={{ color: "var(--fg-4)" }}>—</span>}</td>
                  </tr>
                );
              })}
            </tbody>
          </table>
        </div>
      </div>
    </SubmitConfirmPage>
  );
};


// ── thLabel ──────────────────────────────────────────────────────────────────
const paThLabel = (c) => c.info
  ? <InfoTip label={c.label} text={c.info} />
  : c.label;

// ── Main PriceAdjDetail ───────────────────────────────────────────────────────
const PriceAdjDetail = ({ batch, role, onBack, onPatchBatch, onSubmitBatch, onDeleteBatch, onDirtyChange }) => {
  const [tableMode, setTableMode]   = useState("compact");
  const [pageMode, setPageMode]     = useState("view");
  const [selectedRows, setSelectedRows] = useState(new Set());
  const [dirty, setDirty]           = useState(false);
  const [reviewDrafts, setReviewDrafts] = useState({});
  const [editingCell, setEditingCell] = useState(null);
  const [fRowStatus, setFRowStatus] = useState([]);
  const [fErpStatus, setFErpStatus] = useState([]);
  const { push: toast }             = useToast();

  useEffect(() => { onDirtyChange?.(dirty); }, [dirty, onDirtyChange]);
  useEffect(() => () => onDirtyChange?.(false), [onDirtyChange]);

  const toggleRow = (uid) => setSelectedRows(s => {
    const next = new Set(s); next.has(uid) ? next.delete(uid) : next.add(uid); return next;
  });
  const toggleAll = (checked) =>
    setSelectedRows(checked ? new Set(batch.rows.map(r => r.uid)) : new Set());
  const deleteSelected = () => {
    selectedRows.forEach(uid => onPatchBatch(batch.id, uid, "_deleteRow", null));
    setSelectedRows(new Set());
    setDirty(true);
  };

  const isReview    = batch.submitted;
  const canEdit     = !batch.submitted || batch.rows.some(r => r.rowStatus === "adjRejected");
  const hasRejected = batch.rows.some(r => r.rowStatus === "adjRejected");

  // ── Row status filter ───────────────────────────────────────────────────
  const statusOptions = useMemo(() => {
    if (!isReview) return [];
    const seen = new Set(batch.rows.map(r => r.rowStatus).filter(Boolean));
    return [
      { value: "adjPending",  label: "待審核" },
      { value: "adjApproved", label: "審核通過" },
      { value: "adjRejected", label: "已退回" },
    ].filter(o => seen.has(o.value));
  }, [batch.rows, isReview]);

  const filteredRows = useMemo(() => {
    let rows = batch.rows;
    if (isReview && fRowStatus.length > 0)
      rows = rows.filter(r => fRowStatus.includes(r.rowStatus));
    if (role === "samantha" && fErpStatus.length > 0)
      rows = rows.filter(r => {
        if (r.rowStatus !== "adjApproved") return false;
        const pending = !r.erpUpdated;
        return fErpStatus.some(v => v === "erpPending" ? pending : !pending);
      });
    return rows;
  }, [batch.rows, isReview, role, fRowStatus, fErpStatus]);

  // ── Column selection ────────────────────────────────────────────────────
  const visibleCols = useMemo(() => {
    const baseCols = isReview ? PA_REVIEW_FULL_COLS : PA_EDIT_FULL_COLS;
    if (tableMode === "compact") {
      const keys = !isReview ? PA_COMPACT_EDIT_KEYS
        : role === "samantha" ? PA_COMPACT_SAMANTHA_KEYS
        : PA_COMPACT_REVIEW_KEYS;
      return baseCols.filter(c => keys.includes(c.key));
    }
    return baseCols;
  }, [tableMode, isReview, role]);

  const useGroups = visibleCols.length >= 10;
  const totalWidth = (!batch.submitted ? 28 : 0) + visibleCols.reduce((s, c) => s + c.w, 0);

  // ── Auto-fill from PRODUCT_MASTER ───────────────────────────────────────
  const autoFillFromMaster = useCallback((barcode) => {
    const master = PRODUCT_MASTER.find(p => p.barcode === barcode);
    if (!master) return {};
    return {
      name:          master.name,
      saleStatus: master.saleStatus || "normal",
      productImage:  master.imageUrl   || "",
      mainCat:       master.mainCat    || "",
      subCat:        master.subCat     || "",
      segment3:      master.segment3   || "",
      taxType:       master.taxRate    || "",
      origCostExTax: master.cost       || 0,
      origCostInTax: master.cost       || 0,
      origPrice:     master.listPrice  || 0,
    };
  }, []);

  // ── Row patching ─────────────────────────────────────────────────────────
  const patchRow = (uid, key, value) => {
    if (key !== "erpUpdated" && key !== "reviewPass" && key !== "rejectReason") setDirty(true);
    const now = new Date();
    const stamp = now.getFullYear().toString()
      + String(now.getMonth() + 1).padStart(2, "0")
      + String(now.getDate()).padStart(2, "0") + " "
      + String(now.getHours()).padStart(2, "0") + ":"
      + String(now.getMinutes()).padStart(2, "0");

    if (key === "barcode") {
      const filled = autoFillFromMaster(value);
      onPatchBatch(batch.id, uid, "_multiPatch", { barcode: value, ...filled, updatedAt: stamp });
    } else {
      onPatchBatch(batch.id, uid, key, value);
    }
  };

  const addRow = () => {
    setDirty(true);
    const uid = "pa-r" + Math.random().toString(36).slice(2, 9);
    const now = new Date();
    const stamp = now.getFullYear().toString()
      + String(now.getMonth() + 1).padStart(2, "0")
      + String(now.getDate()).padStart(2, "0") + " "
      + String(now.getHours()).padStart(2, "0") + ":"
      + String(now.getMinutes()).padStart(2, "0");
    const emptyRow = {
      uid, barcode: "", name: "", saleStatus: "", productImage: "",
      origCostExTax: 0, origCostInTax: 0,
      newCostExTax: 0, newCostInTax: 0,
      newCostEffectiveDate: "", newCostPromoEndDate: "",
      origPrice: 0, newPrice: 0, newPriceEffectiveDate: "",
      activityName: "", activityStartDate: "", activityEndDate: "",
      adjNote: "", rowStatus: "", reviewPass: "", rejectReason: "",
      erpUpdated: false, createdAt: stamp, updatedAt: stamp,
    };
    onPatchBatch(batch.id, null, "_addRow", emptyRow);
  };

  const deleteRow = (uid) => { setDirty(true); onPatchBatch(batch.id, uid, "_deleteRow", null); };

  // ── Submit flow ──────────────────────────────────────────────────────────
  const handleSaveDraft     = () => { setDirty(false); toast({ kind: "success", msg: "草稿已儲存" }); };
  const [sysCheckErrMap, setSysCheckErrMap] = useState(null);
  const { showErrors, submitGuard } = useFormValidation(batch.rows, Object.values(PA_FIELDS).filter(f => f.validate), "送審");
  const handleSend = () => submitGuard(() => { setSysCheckErrMap(null); setPageMode("doubleCheck"); });
  const handleDoubleCheckBack = (errMap) => {
    if (errMap) setSysCheckErrMap(errMap);
    setPageMode("view");
  };
  const handleSysCheckConfirm = () => {
    onSubmitBatch(batch.id);
    setSysCheckErrMap(null);
    setPageMode("view");
    toast({ kind: "success", msg: "已送審，待審核" });
  };
  const handleResubmit = () => submitGuard(() => {
    onPatchBatch(batch.id, null, "_resubmit", null);
    setPageMode("doubleCheck");
  });
  const handleBulkApproveConfirm = () => {
    batch.rows
      .filter(r => r.rowStatus === "adjPending")
      .forEach(r => onPatchBatch(batch.id, r.uid, "_multiPatch", { rowStatus: "adjApproved", reviewPass: "yes" }));
    setReviewDrafts({});
    setDirty(false);
    setPageMode("view");
    toast({ kind: "success", msg: "已全部批准" });
  };

  const handleJoannaReviewConfirm = () => {
    Object.entries(reviewDrafts).forEach(([uid, draft]) => {
      onPatchBatch(batch.id, uid, "_multiPatch", {
        rowStatus: draft.reviewPass === "yes" ? "adjApproved" : "adjRejected",
        reviewPass: draft.reviewPass,
        rejectReason: draft.rejectReason || "",
      });
    });
    setReviewDrafts({});
    setDirty(false);
    setPageMode("view");
    toast({ kind: "success", msg: "審核結果已送出" });
  };

  // ── page__actions ────────────────────────────────────────────────────────
  const hasPendingRows = batch.rows.some(r => r.rowStatus === "adjPending");
  const hasDraftRejections = Object.values(reviewDrafts).some(d => d.reviewPass === "no");

  const renderPageActions = () => {
    if (canActAsPurchase(role)) {
      if (!batch.submitted && canEdit) return (
        <>
          <Button variant="secondary" onClick={handleSaveDraft}>儲存草稿</Button>
          <Button variant="primary" icon="send" disabled={batch.rows.length === 0} onClick={handleSend}>送審</Button>
        </>
      );
      if (hasRejected) return (
        <>
          <Button variant="secondary" onClick={handleSaveDraft}>儲存草稿</Button>
          <Button variant="primary" icon="send" onClick={handleResubmit}>重新送出</Button>
        </>
      );
    }
    if (canActAsJoanna(role) && hasPendingRows) return (
      <Button variant="primary" icon="send"
        onClick={() => setPageMode(hasDraftRejections ? "joannaReviewConfirm" : "bulkApprove")}>
        {hasDraftRejections ? "送出" : "全部批准"}
      </Button>
    );
    return null;
  };

  // ── Cell rendering ───────────────────────────────────────────────────────
  const renderCell = (col, row, idx) => {
    const k = col.key;
    const isRowEditable = canActAsPurchase(role) && (!batch.submitted || row.rowStatus === "adjRejected");
    const canReview     = canActAsJoanna(role);
    const canErpUpdate  = role === "samantha" && row.rowStatus === "adjApproved";
    const chkOffset     = !batch.submitted ? 28 : 0;

    if (k === "#") return (
      <td key={k} className="sticky-num" style={{ textAlign: "center", color: "var(--fg-3)", position: "sticky", left: chkOffset, background: "var(--bg-surface)", zIndex: 1, padding: "0 4px" }}>{idx + 1}</td>
    );

    if (k === "barcode") return (
      <td key={k} style={{ position: "sticky", left: chkOffset + PA_NUM_COL_W, background: "var(--bg-surface)", zIndex: 1, boxShadow: "2px 0 4px rgba(0,0,0,.06)" }}>
        {isRowEditable
          ? <CellEditor alwaysOn field={PA_FIELDS.barcode} value={row.barcode} row={row}
              onCommit={val => patchRow(row.uid, "barcode", val)} />
          : <span className="mono" style={{ fontSize: 12, padding: "0 8px" }}>{row.barcode || <span style={{ color: "var(--fg-4)" }}>—</span>}</span>
        }
      </td>
    );

    if (k === "_action") return (
      <td key={k} style={{ textAlign: "center", padding: "0 4px", verticalAlign: "middle" }}>
        {isRowEditable && <DeleteRowBtn onClick={() => deleteRow(row.uid)} />}
      </td>
    );

    if (k === "saleStatus") {
      const ps = row.saleStatus;
      const bad = ps === "outOfStock" || ps === "discontinued";
      return (
        <td key={k}>
          {ps
            ? <span style={{ fontSize: 11, padding: "1px 6px", borderRadius: 2,
                background: bad ? "var(--danger-bg)" : "var(--success-bg)",
                color: bad ? "var(--danger)" : "var(--tag-approved-fg)" }}>
                {SALE_STATUS_LABELS[ps] || ps}
              </span>
            : <span style={{ color: "var(--fg-4)" }}>—</span>
          }
        </td>
      );
    }

    if (k === "productImage") {
      return (
        <td key={k} style={{ padding: "0 8px" }}>
          <TruncCell text={row.productImage || ""} style={{ color: "var(--fg-3)", fontFamily: "var(--font-mono)", fontSize: 11 }} />
        </td>
      );
    }

    // [原] readonly money columns
    if (k === "origCostExTax" || k === "origCostInTax" || k === "origPrice") {
      const v = row[k];
      return <td key={k} className="mono" style={{ color: "var(--fg-3)" }}>
        {v ? fmtPrice(v) : "—"}
      </td>;
    }

    // Editable input fields (new cost/price/dates/activity/adjNote)
    const editableKeys = ["newCostExTax","newCostInTax","newCostEffectiveDate","newCostPromoEndDate","newPrice","newPriceEffectiveDate","activityName","activityStartDate","activityEndDate","adjNote"];
    if (editableKeys.includes(k)) {
      if (!isRowEditable) {
        const v = row[k];
        const isNum = ["newCostExTax","newCostInTax","newPrice"].includes(k);
        const isMono = isNum || ["newCostEffectiveDate","newCostPromoEndDate","newPriceEffectiveDate","activityStartDate","activityEndDate"].includes(k);
        const isText = !isNum && !isMono;
        if (isText) return (
          <td key={k} style={{ padding: "0 8px" }}>
            <TruncCell text={v || ""} />
          </td>
        );
        return <td key={k} className={isMono ? "mono" : ""} style={{ padding: "0 8px" }}>
          {v ? (isNum ? fmtPrice(v) : v) : <span style={{ color: "var(--fg-4)" }}>—</span>}
        </td>;
      }
      return (
        <td key={k}>
          <CellEditor alwaysOn field={PA_FIELDS[k]} value={row[k]} row={row}
            onCommit={val => patchRow(row.uid, k, val)} />
        </td>
      );
    }

    // Computed margin / final columns (review only)
    if (k === "origMargin") {
      const m = fmtMargin(row.origPrice, row.origCostInTax);
      return <td key={k} style={{ color: "var(--fg-3)" }}>{m || "—"}</td>;
    }
    if (k === "newMargin") {
      const finalCost  = row.newCostInTax  || row.origCostInTax;
      const finalPrice = row.newPrice      || row.origPrice;
      const m = fmtMargin(finalPrice, finalCost);
      const origM = fmtMargin(row.origPrice, row.origCostInTax);
      const improved = m && origM && parseFloat(m) > parseFloat(origM);
      return <td key={k} style={{ color: improved ? "var(--tag-approved-fg)" : "var(--fg-1)", fontWeight: m !== origM ? 600 : 400 }}>{m || "—"}</td>;
    }
    if (k === "finalCostInTax") {
      const v = row.newCostInTax || row.origCostInTax;
      return <td key={k} className="mono">{fmtPrice(v)}</td>;
    }
    if (k === "finalPrice") {
      const v = row.newPrice || row.origPrice;
      return <td key={k} className="mono">{fmtPrice(v)}</td>;
    }
    if (k === "priceChange") {
      const finalP = +(row.newPrice || row.origPrice || 0);
      const origP  = +(row.origPrice || 0);
      if (!origP) return <td key={k} style={{ color: "var(--fg-4)" }}>—</td>;
      const delta = finalP - origP;
      const color = delta > 0 ? "var(--danger)" : delta < 0 ? "var(--tag-approved-fg)" : "var(--fg-3)";
      const sign  = delta > 0 ? "+" : "";
      return <td key={k} className="mono" style={{ color, fontWeight: delta !== 0 ? 600 : 400 }}>
        {delta !== 0 ? `${sign}NT$${Math.abs(delta).toLocaleString("zh-TW")}` : "—"}
      </td>;
    }

    // Review columns
    if (k === "rowStatus") {
      return <td key={k}>{row.rowStatus ? <Tag status={row.rowStatus} /> : <span style={{ color: "var(--fg-4)" }}>—</span>}</td>;
    }
    if (k === "reviewPass") {
      const isPending = row.rowStatus === "adjPending";
      const draftPass = reviewDrafts[row.uid]?.reviewPass;
      if (canReview && isPending) return (
        <td key={k}>
          <div className="review-pass-ctrl">
            <button className={"review-pass-btn review-pass-btn--yes" + (draftPass === "yes" ? " active" : "")}
              onClick={() => { setReviewDrafts(d => ({ ...d, [row.uid]: { ...(d[row.uid] || {}), reviewPass: "yes" } })); setDirty(true); }}>
              ✓ 通過
            </button>
            <button className={"review-pass-btn review-pass-btn--no" + (draftPass === "no" ? " active" : "")}
              onClick={() => { setReviewDrafts(d => ({ ...d, [row.uid]: { ...(d[row.uid] || {}), reviewPass: "no" } })); setDirty(true); }}>
              ✗ 退回
            </button>
          </div>
        </td>
      );
      if (row.reviewPass === "yes") return <td key={k} style={{ color: "var(--tag-approved-fg)", fontWeight: 600 }}>✓ 通過</td>;
      if (row.reviewPass === "no")  return <td key={k} style={{ color: "var(--danger)", fontWeight: 600 }}>✗ 退回</td>;
      return <td key={k} style={{ color: "var(--fg-4)" }}>—</td>;
    }
    if (k === "rejectReason") {
      const isPending = row.rowStatus === "adjPending";
      const showInput = canReview && isPending && reviewDrafts[row.uid]?.reviewPass === "no";
      if (showInput) return (
        <td key={k}>
          <Textarea variant="reject" rows={2}
            placeholder="請填寫退回原因…"
            value={reviewDrafts[row.uid]?.rejectReason ?? ""}
            onChange={e => { setReviewDrafts(d => ({ ...d, [row.uid]: { ...(d[row.uid] || {}), rejectReason: e.target.value } })); setDirty(true); }} />
        </td>
      );
      return (
        <td key={k}>
          <TruncCell text={row.rejectReason || ""} style={{ color: row.rejectReason ? "var(--danger)" : "var(--fg-4)" }} />
        </td>
      );
    }
    const _trk = renderAdjTrackingCell(k, row, { canErpUpdate, patchRow });
    if (_trk) return _trk;

    if (k === "createdAt" || k === "updatedAt") {
      return <td key={k} className="mono" style={{ fontSize: 11, color: "var(--fg-3)" }}>{row[k] || "—"}</td>;
    }

    const v = row[k];
    if (typeof v === "string" && !col.mono) return (
      <td key={k}>
        <TruncCell text={v} />
      </td>
    );
    return (
      <td key={k}>
        {v !== undefined && v !== "" && v !== null
          ? (col.mono ? <span className="mono">{v}</span> : v)
          : <span style={{ color: "var(--fg-4)" }}>—</span>
        }
      </td>
    );
  };

  const rowClass = (row) => {
    const isPending = row.rowStatus === "adjPending";
    const draftPass = isPending ? reviewDrafts[row.uid]?.reviewPass : undefined;
    if (draftPass === "yes") return "adj-row--approved";
    if (draftPass === "no")  return "adj-row--rejected";
    if (row.rowStatus === "adjApproved") return "adj-row--approved";
    if (row.rowStatus === "adjRejected") return "adj-row--rejected";
    if (row.rowStatus === "adjPending")  return "adj-row--pending";
    return "";
  };

  // ── Transient page modes ─────────────────────────────────────────────────
  if (pageMode === "bulkApprove") return (
    <PriceAdjBulkApproveView batch={batch} onBack={() => setPageMode("view")} onConfirm={handleBulkApproveConfirm} />
  );
  if (pageMode === "joannaReviewConfirm") return (
    <PriceAdjJoannaConfirmView batch={batch} reviewDrafts={reviewDrafts}
      onBack={() => setPageMode("view")} onConfirm={handleJoannaReviewConfirm} />
  );
  if (pageMode === "doubleCheck") return (
    <PriceAdjDoubleCheckView batch={batch} onBack={handleDoubleCheckBack} onConfirm={handleSysCheckConfirm} />
  );

  // ── Main view ────────────────────────────────────────────────────────────
  return (
    <>
      <PageHead
        crumbs={[
          { label: "商品調整" },
          { label: "商品價格調整", onClick: onBack },
          { label: batch.batchName, current: true },
        ]}
        title={<>{batch.batchName} <AggStatusDisplay batch={batch} /></>}
        dirty={dirty}
        actions={renderPageActions()}
      />

      <PriceAdjMetaCard batch={batch} />

      {!batch.submitted && sysCheckErrMap && Object.keys(sysCheckErrMap).length > 0 && (
        <ImportBanner
          phase="error"
          titlePrefix="系統檢查："
          titleSuffix="品項需要修正"
          desc="下方標示的列有問題，修正後請重新送出。"
          errorCount={Object.keys(sysCheckErrMap).length}
          onView={(idx) => { const inds = document.querySelectorAll('.row-err-ind'); inds[idx % (inds.length || 1)]?.closest('tr')?.scrollIntoView({ block: 'center', behavior: 'smooth' }); }}
          onClose={() => setSysCheckErrMap(null)}
        />
      )}

      {isReview && (statusOptions.length > 0 || role === "samantha") && (
        <div className="filter-bar">
          {statusOptions.length > 0 && (
            <ChipFilter label="審核狀態" value={fRowStatus} options={statusOptions}
              onChange={v => setFRowStatus(v)} multi />
          )}
          {role === "samantha" && (
            <ChipFilter label="ERP 狀態" value={fErpStatus}
              options={[
                { value: "erpPending", label: "ERP 待確認" },
                { value: "erpDone",    label: "ERP 已完成" },
              ]}
              onChange={v => setFErpStatus(v)} multi />
          )}
        </div>
      )}

      <div className="card">
        <SkuToolbar>
          {!batch.submitted && selectedRows.size > 0 ? (
            <BulkBar label={<>已選 <b>{selectedRows.size}</b> 筆品項</>} variant="danger">
              <Button variant="danger" size="sm" icon="trash" onClick={deleteSelected}>
                刪除已選 {selectedRows.size} 筆品項
              </Button>
              <Button variant="text" size="sm" onClick={() => setSelectedRows(new Set())}>取消選取</Button>
            </BulkBar>
          ) : (
            <>
              <ModeToggle mode={tableMode} onChange={setTableMode} />
              <span style={{ color: "var(--fg-2)", fontSize: 13, whiteSpace: "nowrap" }}>
                共 <b style={{ color: "var(--fg-1)" }}>{filteredRows.length}</b>
                {(fRowStatus.length > 0 || fErpStatus.length > 0) && <> / {batch.rows.length}</>} 筆
              </span>
              {!batch.submitted && (
                <Button variant="default" size="sm" icon="plus" onClick={addRow} style={{ marginLeft: "auto" }}>{ADD_LABEL}</Button>
              )}
            </>
          )}
        </SkuToolbar>
        {!batch.submitted && batch.rows.length === 0 ? (
          <EmptyState icon="sliders" title="尚無品項" desc="點「新增品項」開始輸入。">
            <Button variant="primary" icon="plus" onClick={addRow}>{ADD_LABEL}</Button>
          </EmptyState>
        ) : (
        <ShowErrorsProvider value={showErrors}>
        <>
        <div className="tbl-wrap">
          <table className="tbl adj-tbl" style={{ borderCollapse: "separate", borderSpacing: 0, width: "100%", minWidth: totalWidth }}>
            <colgroup>
              {!batch.submitted && <col style={{ minWidth: 28 }} />}
              {visibleCols.map(c => <col key={c.key} style={{ minWidth: c.w }} />)}
            </colgroup>
            <thead>
              {useGroups ? (() => {
                const chkW = 28;
                const numCol    = visibleCols.find(c => c.stickyNum);
                const actionCol = visibleCols.find(c => c.actionCol);
                const dataCols  = visibleCols.filter(c => !c.stickyNum && !c.actionCol);
                return (
                  <>
                    <TableGroupHeader
                      cols={dataCols}
                      groupLabels={PA_COL_GROUP_LABELS}
                      leadingSlot={<>
                        {!batch.submitted && (
                          <th rowSpan={2} style={{ width: chkW, textAlign: "center", position: "sticky", left: 0, zIndex: 3, background: "var(--gray-50)", padding: 0 }}>
                            <input type="checkbox"
                              checked={batch.rows.length > 0 && batch.rows.every(r => selectedRows.has(r.uid))}
                              onChange={e => toggleAll(e.target.checked)} />
                          </th>
                        )}
                        {numCol && (
                          <th rowSpan={2} style={{ width: numCol.w, minWidth: numCol.w, position: "sticky", left: !batch.submitted ? chkW : 0, zIndex: 2, padding: "0 4px" }}>
                            {numCol.label}
                          </th>
                        )}
                      </>}
                      actionCol={actionCol}
                    />
                    <tr>
                      {dataCols.map(c => {
                        const s = { width: c.w, minWidth: c.w };
                        if (c.sticky2nd) return (
                          <th key={c.key} style={{ ...s, position: "sticky", left: !batch.submitted ? 28 + PA_NUM_COL_W : PA_NUM_COL_W, zIndex: 2, boxShadow: "2px 0 4px rgba(0,0,0,.06)" }}>{paThLabel(c)}</th>
                        );
                        return <th key={c.key} className={c.group === "H" ? "th-review-sub" : ""} style={s}>{paThLabel(c)}</th>;
                      })}
                    </tr>
                  </>
                );
              })() : (
                <tr>
                  {!batch.submitted && (
                    <th style={{ width: 28, textAlign: "center", position: "sticky", left: 0, zIndex: 3, background: "var(--gray-50)", padding: 0 }}>
                      <input type="checkbox"
                        checked={batch.rows.length > 0 && batch.rows.every(r => selectedRows.has(r.uid))}
                        onChange={e => toggleAll(e.target.checked)} />
                    </th>
                  )}
                  {visibleCols.map(c => {
                    const baseStyle = { width: c.w, minWidth: c.w };
                    if (c.stickyNum) return (
                      <th key={c.key} style={{ ...baseStyle, position: "sticky", left: batch.submitted ? 0 : 28, zIndex: 2, padding: "0 4px" }}>{paThLabel(c)}</th>
                    );
                    if (c.sticky2nd) return (
                      <th key={c.key} style={{ ...baseStyle, position: "sticky", left: batch.submitted ? PA_NUM_COL_W : 28 + PA_NUM_COL_W, zIndex: 2, boxShadow: "2px 0 4px rgba(0,0,0,.06)" }}>{paThLabel(c)}</th>
                    );
                    return <th key={c.key} className={c.group === "H" ? "th-review-sub" : ""} style={baseStyle}>{paThLabel(c)}</th>;
                  })}
                </tr>
              )}
            </thead>
            <tbody>
              {filteredRows.length === 0 && (
                <tr>
                  <td colSpan={visibleCols.length + (!batch.submitted ? 1 : 0) + 1}>
                    <div className="empty" style={{ padding: "40px 20px" }}>
                      <div className="empty__icon"><Icon name="sliders" size={32} /></div>
                      <div className="empty__title">沒有符合篩選條件的品項</div>
                      <div className="empty__desc">試試清除篩選條件。</div>
                    </div>
                  </td>
                </tr>
              )}
              {filteredRows.map((row, idx) => (
                <tr key={row.uid} className={rowClass(row)}>
                  {!batch.submitted && (
                    <td style={{ textAlign: "center", width: 28, position: "sticky", left: 0, background: "var(--bg-surface)", zIndex: 1, padding: 0 }}>
                      {sysCheckErrMap?.[row.uid] && <RowErrTip msg={sysCheckErrMap[row.uid]} />}
                      <input type="checkbox"
                        checked={selectedRows.has(row.uid)}
                        onChange={() => toggleRow(row.uid)} />
                    </td>
                  )}
                  {visibleCols.map(c => renderCell(c, row, idx))}
                </tr>
              ))}
            </tbody>
          </table>
        </div>

        {!batch.submitted && (
          <button className="tbl-add-row" onClick={addRow}>
            <Icon name="plus" size={16} /> 新增品項
          </button>
        )}
        </>
        </ShowErrorsProvider>
        )}

      </div>
    </>
  );
};

Object.assign(window, { PriceAdjDetail });
