// Violation points system — per-user synthesized data + dialogs
// Points cap at 3 for threshold; dismiss by admin with reason + lock decision

const VIOLATION_CATEGORIES = [
  { key: "no_payment", label: "未付款", desc: "訂單成立後逾期未付款或偽造付款證明", weight: 1 },
  { key: "delay", label: "拖延交付", desc: "逾期交付 > 7 天且未主動告知", weight: 1 },
  { key: "misconduct", label: "不當言論", desc: "侮辱、騷擾、歧視性言論", weight: 2 },
  { key: "scam", label: "詐欺行為", desc: "私下交易、假匯款、盜用他人身份", weight: 3 },
  { key: "copyright", label: "版權疑慮", desc: "抄襲、盜用作品、未授權使用素材", weight: 2 },
  { key: "no_show", label: "失聯", desc: "超過 7 日未回應委託方或平台", weight: 1 },
  { key: "quality", label: "品質問題", desc: "交付品質明顯不符合約", weight: 1 },
  { key: "other", label: "其他", desc: "其他違規行為", weight: 1 },
];

const LOCKABLE_FEATURES = [
  { key: "place_order", label: "下訂單", desc: "無法發起新委託", icon: "order", applies: ["client", "artist"] },
  { key: "publish", label: "發布作品 / 接單", desc: "無法上架作品或接新案", icon: "palette", applies: ["artist"] },
  { key: "message", label: "站內訊息", desc: "無法主動傳訊給其他用戶", icon: "message", applies: ["client", "artist"] },
  { key: "comment", label: "評論 / 留言", desc: "無法在作品或貼文下評論", icon: "edit", applies: ["client", "artist"] },
  { key: "withdraw", label: "提領", desc: "凍結提領,資金仍可累積", icon: "coin", applies: ["artist"] },
  { key: "login", label: "登入", desc: "完全禁止登入 (等同暫時封鎖)", icon: "logout", applies: ["client", "artist"] },
];

// Derive deterministic violation state from user id
const getViolationData = (user) => {
  const code = user.id.charCodeAt(0) + user.id.charCodeAt(2) + user.id.charCodeAt(5);
  let points = 0;
  if (user.status === "suspended") points = 3;
  else if (user.status === "warning") points = (code % 2) + 1; // 1-2
  else if (code % 7 === 0) points = 2; // a few active users with 2 points
  else if (code % 11 === 0) points = 1;

  const pickedCats = [];
  const seedReasons = [
    { cat: "no_payment", detail: "訂單 #DD820453 成立後 5 日未付款,經多次催繳未回應" },
    { cat: "delay", detail: "2 件訂單逾期超過 7 天,委託人主動申訴" },
    { cat: "misconduct", detail: "聊天紀錄中對委託人使用侮辱性字眼,系統已截圖備份" },
    { cat: "copyright", detail: "交付作品疑似 Pixiv @origin_user 作品修改,已由版權方舉報" },
    { cat: "no_show", detail: "階段 2 交付後失聯 10 天,委託人提交申訴" },
    { cat: "quality", detail: "交付稿件明顯與需求不符,委託人申請退款成功" },
  ];
  for (let i = 0; i < points; i++) {
    pickedCats.push(seedReasons[(code + i * 3) % seedReasons.length]);
  }

  const entries = pickedCats.map((r, i) => ({
    id: `V${code}${i}`,
    category: r.cat,
    points: VIOLATION_CATEGORIES.find(c => c.key === r.cat)?.weight || 1,
    reason: r.detail,
    relatedOrder: i === 0 ? MOCK.ORDERS[(code + i) % MOCK.ORDERS.length]?.id : null,
    issuedBy: ["林佩芸", "陳冠翰", "黃思婷"][(code + i) % 3],
    issuedAt: ["3 天前", "1 週前", "2 週前", "3 週前"][i % 4],
    dismissed: false,
  }));

  // If user hit 3+, assume some feature locks already applied
  const locks = points >= 3 ? {
    features: user.role === "artist" ? ["publish", "withdraw"] : ["place_order", "message"],
    lockedBy: "林佩芸",
    lockedAt: "3 天前",
    expiresAt: "永久 · 需人工解除",
    reason: "累計違規 3 點 · 多次逾期交付導致委託人投訴",
  } : null;

  return { points, entries, locks };
};

// ============== Pill / chip components ==============

const ViolationPointsPill = ({ points, size = "md", onClick }) => {
  if (points === 0) return null;
  const tone = points >= 3 ? "danger" : points >= 2 ? "warn" : "default";
  const bg = points >= 3 ? "var(--danger)" : points >= 2 ? "var(--warn-soft)" : "var(--surface-2)";
  const color = points >= 3 ? "#fff" : points >= 2 ? "var(--warn)" : "var(--ink-3)";
  const sizes = {
    sm: { fs: 10.5, pad: "2px 7px", gap: 3, dot: 4 },
    md: { fs: 11.5, pad: "3px 9px", gap: 4, dot: 5 },
    lg: { fs: 12.5, pad: "4px 10px", gap: 5, dot: 6 },
  }[size];
  return (
    <button onClick={onClick} style={{
      display: "inline-flex", alignItems: "center", gap: sizes.gap,
      padding: sizes.pad, borderRadius: 999,
      background: bg, color, fontSize: sizes.fs,
      fontWeight: 500, lineHeight: 1,
      cursor: onClick ? "pointer" : "default",
      border: points < 3 ? "1px solid var(--border)" : "none",
    }}>
      <Icon name="alert" size={sizes.fs}/>
      <span>違規 {points}/3</span>
      {points >= 3 && <Icon name="lock" size={sizes.fs - 1}/>}
    </button>
  );
};

// ============== Violation tab content ==============

const ViolationTab = ({ user, onAddPoint, onLockFeatures, onDismissPoint, onUnlock, violationState }) => {
  const { points, entries, locks } = violationState;
  const atThreshold = points >= 3;

  return (
    <div style={{ display: "flex", flexDirection: "column", gap: 16 }}>
      {/* Threshold banner */}
      {atThreshold && (
        <div style={{
          border: "1px solid var(--danger)", borderRadius: 12, overflow: "hidden",
          background: "linear-gradient(135deg, var(--danger-soft), transparent)",
        }}>
          <div style={{ padding: "14px 16px", display: "flex", gap: 12, alignItems: "flex-start" }}>
            <div style={{
              width: 40, height: 40, borderRadius: 10,
              background: "var(--danger)", color: "#fff", flexShrink: 0,
              display: "flex", alignItems: "center", justifyContent: "center",
            }}><Icon name="alert" size={20}/></div>
            <div style={{ flex: 1 }}>
              <div style={{ fontSize: 14, fontWeight: 600, color: "var(--danger)" }}>已達違規上限 3/3</div>
              <div style={{ fontSize: 12.5, color: "var(--ink-2)", marginTop: 4, lineHeight: 1.55 }}>
                此用戶已累計 3 次違規,系統已自動通知承辦管理員。請儘速裁定要鎖定哪些功能,或選擇永久封鎖。
              </div>
            </div>
          </div>
          {locks && (
            <div style={{ padding: "12px 16px", background: "var(--surface)", borderTop: "1px solid var(--border)" }}>
              <div style={{ fontSize: 11, color: "var(--ink-3)", fontWeight: 600, letterSpacing: 0.8, textTransform: "uppercase", marginBottom: 8 }}>目前已鎖定的功能</div>
              <div style={{ display: "flex", gap: 6, flexWrap: "wrap", marginBottom: 8 }}>
                {locks.features.map(f => {
                  const feat = LOCKABLE_FEATURES.find(l => l.key === f);
                  return (
                    <div key={f} style={{
                      display: "inline-flex", alignItems: "center", gap: 5,
                      padding: "4px 10px", borderRadius: 999,
                      background: "var(--danger-soft)", color: "var(--danger)",
                      fontSize: 12, fontWeight: 500,
                    }}>
                      <Icon name="lock" size={11}/>{feat?.label}
                    </div>
                  );
                })}
              </div>
              <div style={{ fontSize: 11.5, color: "var(--ink-3)" }}>
                由 <b style={{ color: "var(--ink-2)" }}>{locks.lockedBy}</b> 於 {locks.lockedAt} 鎖定 · {locks.expiresAt}
              </div>
            </div>
          )}
          <div style={{ padding: "10px 16px", display: "flex", gap: 8, justifyContent: "flex-end", background: "var(--surface)", borderTop: "1px solid var(--border)" }}>
            {locks ? (
              <>
                <Button size="sm" variant="secondary" onClick={onLockFeatures}>修改鎖定範圍</Button>
                <Button size="sm" variant="secondary" onClick={onUnlock} style={{ color: "var(--info)" }}>解除所有鎖定</Button>
              </>
            ) : (
              <Button size="sm" variant="primary" icon="lock" onClick={onLockFeatures} style={{ background: "var(--danger)" }}>立即鎖定功能</Button>
            )}
          </div>
        </div>
      )}

      {/* Points progress */}
      <div>
        <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", marginBottom: 10 }}>
          <div style={{ fontSize: 11.5, fontWeight: 600, color: "var(--ink-3)", letterSpacing: 0.8, textTransform: "uppercase" }}>
            違規點數累計
          </div>
          <Button size="sm" variant="secondary" icon="plus" onClick={onAddPoint}>新增違規計點</Button>
        </div>
        <div style={{ display: "flex", gap: 8 }}>
          {[1, 2, 3].map(n => {
            const filled = n <= points;
            const isCritical = n === 3 && filled;
            return (
              <div key={n} style={{
                flex: 1, height: 52, borderRadius: 10,
                border: "1px solid " + (filled ? (isCritical ? "var(--danger)" : "var(--warn)") : "var(--border)"),
                background: filled ? (isCritical ? "var(--danger)" : "var(--warn-soft)") : "var(--surface)",
                display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center",
                gap: 2,
                color: filled ? (isCritical ? "#fff" : "var(--warn)") : "var(--ink-4)",
              }}>
                <div className="serif" style={{ fontSize: 22, lineHeight: 1 }}>{n}</div>
                <div style={{ fontSize: 10.5, fontWeight: 500, letterSpacing: 0.4 }}>
                  {n === 1 ? "第 1 次" : n === 2 ? "第 2 次" : "達上限"}
                </div>
              </div>
            );
          })}
        </div>
      </div>

      {/* Entries list */}
      <div>
        <div style={{ fontSize: 11.5, fontWeight: 600, color: "var(--ink-3)", letterSpacing: 0.8, textTransform: "uppercase", marginBottom: 10 }}>
          違規紀錄 ({entries.length})
        </div>
        {entries.length === 0 && (
          <div style={{ padding: "50px 0", textAlign: "center", color: "var(--ink-4)" }}>
            <Icon name="shield" size={26} style={{ color: "var(--success)", marginBottom: 8 }}/>
            <div style={{ fontSize: 13, color: "var(--ink-3)" }}>無違規紀錄</div>
            <div style={{ fontSize: 11.5, marginTop: 4 }}>保持良好行為 ✓</div>
          </div>
        )}
        <div style={{ display: "flex", flexDirection: "column", gap: 8 }}>
          {entries.map((e, i) => {
            const cat = VIOLATION_CATEGORIES.find(c => c.key === e.category);
            return (
              <div key={e.id} style={{
                padding: 14, border: "1px solid var(--border)", borderRadius: 10,
                background: "var(--surface)", display: "flex", gap: 12,
              }}>
                <div style={{
                  width: 28, height: 28, borderRadius: 8, flexShrink: 0,
                  background: "var(--warn-soft)", color: "var(--warn)",
                  display: "flex", alignItems: "center", justifyContent: "center",
                  fontSize: 12, fontWeight: 600,
                }}>
                  +{e.points}
                </div>
                <div style={{ flex: 1, minWidth: 0 }}>
                  <div style={{ display: "flex", alignItems: "center", gap: 6, marginBottom: 4, flexWrap: "wrap" }}>
                    <span style={{ fontSize: 13, fontWeight: 500 }}>{cat?.label}</span>
                    <Badge tone="default" size="sm">第 {entries.length - i} 次</Badge>
                    {e.relatedOrder && (
                      <span className="mono" style={{ fontSize: 11.5, color: "var(--info)" }}>{e.relatedOrder}</span>
                    )}
                  </div>
                  <div style={{ fontSize: 12.5, color: "var(--ink-2)", lineHeight: 1.55 }}>{e.reason}</div>
                  <div style={{ fontSize: 11.5, color: "var(--ink-4)", marginTop: 6 }}>
                    由 {e.issuedBy} 於 {e.issuedAt}核定
                  </div>
                </div>
                <button onClick={() => onDismissPoint(e)} style={{
                  padding: "4px 10px", fontSize: 11.5, color: "var(--ink-3)",
                  background: "var(--surface-2)", borderRadius: 6, alignSelf: "flex-start",
                }}>撤銷</button>
              </div>
            );
          })}
        </div>
      </div>

      {/* Policy */}
      <div style={{ fontSize: 11.5, color: "var(--ink-3)", lineHeight: 1.6, padding: "12px 14px", background: "var(--surface-2)", borderRadius: 8 }}>
        <Icon name="info" size={11} style={{ verticalAlign: -1, marginRight: 4 }}/>
        <b>計點機制</b> · 累計滿 3 點會觸發系統通知所有管理員。管理員需主動裁定鎖定範圍或選擇封鎖。點數每 6 個月自動衰減 1 點。
      </div>
    </div>
  );
};

// ============== Dialogs ==============

const AddViolationDialog = ({ user, onClose, onConfirm, currentPoints }) => {
  const [cat, setCat] = useState("delay");
  const [pts, setPts] = useState(1);
  const [reason, setReason] = useState("");
  const [relatedOrder, setRelatedOrder] = useState("");
  const selected = VIOLATION_CATEGORIES.find(c => c.key === cat);
  const newTotal = currentPoints + pts;
  const willHitThreshold = newTotal >= 3 && currentPoints < 3;

  useEffect(() => {
    if (selected) setPts(selected.weight);
  }, [cat]);

  return (
    <Modal open onClose={onClose} width={540}>
      <div style={{ padding: 22 }}>
        <div style={{ display: "flex", alignItems: "center", gap: 12, marginBottom: 6 }}>
          <div style={{
            width: 38, height: 38, borderRadius: 10,
            background: "var(--warn-soft)", color: "var(--warn)",
            display: "flex", alignItems: "center", justifyContent: "center",
          }}><Icon name="alert" size={18}/></div>
          <div style={{ flex: 1 }}>
            <div style={{ fontSize: 16, fontWeight: 600 }}>新增違規計點</div>
            <div style={{ fontSize: 12, color: "var(--ink-3)", marginTop: 2 }}>
              針對 <b style={{ color: "var(--ink-2)" }}>{user.name}</b> · 目前 {currentPoints}/3 點
            </div>
          </div>
        </div>

        {willHitThreshold && (
          <div style={{
            marginTop: 14, padding: "10px 12px", borderRadius: 8,
            background: "var(--danger-soft)", border: "1px solid var(--danger)",
            fontSize: 12, color: "var(--danger)", display: "flex", gap: 8, alignItems: "flex-start",
          }}>
            <Icon name="alert" size={14} style={{ flexShrink: 0, marginTop: 1 }}/>
            <span>送出後將使用戶達到 <b>{newTotal}/3</b> 觸發通知,接下來會進入「鎖定功能」步驟。</span>
          </div>
        )}

        <div style={{ display: "flex", flexDirection: "column", gap: 14, marginTop: 16 }}>
          {/* Category */}
          <div>
            <label style={{ fontSize: 12, color: "var(--ink-2)", fontWeight: 500, display: "block", marginBottom: 6 }}>
              違規類別
            </label>
            <div style={{ display: "grid", gridTemplateColumns: "repeat(4, 1fr)", gap: 6 }}>
              {VIOLATION_CATEGORIES.map(c => (
                <button key={c.key} onClick={() => setCat(c.key)} style={{
                  padding: "10px 8px", borderRadius: 8,
                  border: "1px solid " + (cat === c.key ? "var(--ink)" : "var(--border)"),
                  background: cat === c.key ? "var(--ink)" : "var(--surface)",
                  color: cat === c.key ? "#fff" : "var(--ink)",
                  fontSize: 12, fontWeight: cat === c.key ? 500 : 400,
                  textAlign: "left",
                }}>
                  <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between" }}>
                    <span>{c.label}</span>
                    <span style={{ fontSize: 10, opacity: 0.7 }}>+{c.weight}</span>
                  </div>
                </button>
              ))}
            </div>
            <div style={{ fontSize: 11.5, color: "var(--ink-3)", marginTop: 6, lineHeight: 1.5 }}>
              <Icon name="info" size={11} style={{ verticalAlign: -1 }}/> {selected?.desc}
            </div>
          </div>

          {/* Points */}
          <div>
            <label style={{ fontSize: 12, color: "var(--ink-2)", fontWeight: 500, display: "block", marginBottom: 6 }}>
              計點數 (預設依類別權重 · 可調整 1-3)
            </label>
            <div style={{ display: "flex", gap: 6 }}>
              {[1, 2, 3].map(n => (
                <button key={n} onClick={() => setPts(n)} style={{
                  flex: 1, padding: "10px", borderRadius: 8,
                  border: "1px solid " + (pts === n ? "var(--warn)" : "var(--border)"),
                  background: pts === n ? "var(--warn-soft)" : "var(--surface)",
                  color: pts === n ? "var(--warn)" : "var(--ink-2)",
                  fontSize: 14, fontWeight: 500,
                }}>+{n} 點</button>
              ))}
            </div>
          </div>

          {/* Reason */}
          <div>
            <label style={{ fontSize: 12, color: "var(--ink-2)", fontWeight: 500, display: "block", marginBottom: 6 }}>
              詳細原因 (將顯示給用戶 · 會寄送通知)
            </label>
            <textarea
              value={reason}
              onChange={e => setReason(e.target.value)}
              placeholder={`例:訂單 #DD820453 於 2026/04/10 到期,逾期 8 日仍未交付...`}
              style={{
                width: "100%", minHeight: 76, padding: 10, borderRadius: 8,
                border: "1px solid var(--border-strong)", resize: "vertical",
                fontSize: 13, lineHeight: 1.55, outline: "none",
                fontFamily: "inherit",
              }}
            />
          </div>

          {/* Related order (optional) */}
          <div>
            <label style={{ fontSize: 12, color: "var(--ink-2)", fontWeight: 500, display: "block", marginBottom: 6 }}>
              關聯訂單 (選填)
            </label>
            <Input placeholder="#DD820453" value={relatedOrder} onChange={e => setRelatedOrder(e.target.value)}/>
          </div>
        </div>

        <div style={{ marginTop: 18, padding: 12, background: "var(--surface-2)", borderRadius: 8, fontSize: 11.5, color: "var(--ink-3)", display: "flex", gap: 8 }}>
          <Icon name="scroll" size={12} style={{ flexShrink: 0, marginTop: 2 }}/>
          <span>此操作會寫入審計日誌,並發送違規通知到用戶 Email 與站內信。用戶可以申訴,主管需於 48 小時內回覆。</span>
        </div>

        <div style={{ marginTop: 16, display: "flex", gap: 8, justifyContent: "flex-end" }}>
          <Button variant="secondary" onClick={onClose}>取消</Button>
          <Button
            variant="primary"
            disabled={!reason.trim() || !cat}
            onClick={() => onConfirm({ category: cat, points: pts, reason: reason.trim(), relatedOrder })}
            style={{ background: willHitThreshold ? "var(--danger)" : "var(--ink)" }}
          >
            {willHitThreshold ? `確認新增 +${pts} 點 (達上限)` : `確認新增 +${pts} 點`}
          </Button>
        </div>
      </div>
    </Modal>
  );
};

const LockFeaturesDialog = ({ user, onClose, onConfirm, existing }) => {
  const applicable = LOCKABLE_FEATURES.filter(f => f.applies.includes(user.role));
  const [selected, setSelected] = useState(new Set(existing?.features || []));
  const [reason, setReason] = useState(existing?.reason || "");
  const [duration, setDuration] = useState(existing ? "permanent" : "7d");

  const toggle = (k) => {
    const next = new Set(selected);
    if (next.has(k)) next.delete(k); else next.add(k);
    setSelected(next);
  };

  return (
    <Modal open onClose={onClose} width={560}>
      <div style={{ padding: 22 }}>
        <div style={{ display: "flex", alignItems: "center", gap: 12, marginBottom: 6 }}>
          <div style={{
            width: 38, height: 38, borderRadius: 10,
            background: "var(--danger)", color: "#fff",
            display: "flex", alignItems: "center", justifyContent: "center",
          }}><Icon name="lock" size={18}/></div>
          <div style={{ flex: 1 }}>
            <div style={{ fontSize: 16, fontWeight: 600 }}>
              {existing ? "修改功能鎖定範圍" : "鎖定功能權限"}
            </div>
            <div style={{ fontSize: 12, color: "var(--ink-3)", marginTop: 2 }}>
              {user.name} · 用戶仍可登入與查看紀錄,但無法執行勾選的功能
            </div>
          </div>
        </div>

        <div style={{ marginTop: 16 }}>
          <label style={{ fontSize: 12, color: "var(--ink-2)", fontWeight: 500, display: "block", marginBottom: 8 }}>
            要鎖定的功能 (可複選)
          </label>
          <div style={{ display: "flex", flexDirection: "column", gap: 6 }}>
            {applicable.map(f => {
              const on = selected.has(f.key);
              return (
                <button key={f.key} onClick={() => toggle(f.key)} style={{
                  display: "flex", alignItems: "center", gap: 12,
                  padding: "12px 14px", borderRadius: 10,
                  border: "1px solid " + (on ? "var(--danger)" : "var(--border)"),
                  background: on ? "var(--danger-soft)" : "var(--surface)",
                  textAlign: "left",
                }}>
                  <div style={{
                    width: 20, height: 20, borderRadius: 5,
                    border: "1.5px solid " + (on ? "var(--danger)" : "var(--border-strong)"),
                    background: on ? "var(--danger)" : "var(--surface)",
                    color: "#fff", flexShrink: 0,
                    display: "flex", alignItems: "center", justifyContent: "center",
                  }}>
                    {on && <Icon name="check" size={12}/>}
                  </div>
                  <div style={{
                    width: 32, height: 32, borderRadius: 8,
                    background: "var(--surface-2)", color: "var(--ink-3)",
                    display: "flex", alignItems: "center", justifyContent: "center", flexShrink: 0,
                  }}>
                    <Icon name={f.icon} size={15}/>
                  </div>
                  <div style={{ flex: 1, minWidth: 0 }}>
                    <div style={{ fontSize: 13.5, fontWeight: 500, color: on ? "var(--danger)" : "var(--ink)" }}>{f.label}</div>
                    <div style={{ fontSize: 11.5, color: "var(--ink-3)", marginTop: 2 }}>{f.desc}</div>
                  </div>
                </button>
              );
            })}
          </div>
        </div>

        <div style={{ marginTop: 16 }}>
          <label style={{ fontSize: 12, color: "var(--ink-2)", fontWeight: 500, display: "block", marginBottom: 6 }}>
            鎖定期限
          </label>
          <div style={{ display: "grid", gridTemplateColumns: "repeat(4, 1fr)", gap: 6 }}>
            {[
              { value: "7d", label: "7 天" },
              { value: "14d", label: "14 天" },
              { value: "30d", label: "30 天" },
              { value: "permanent", label: "永久 · 需人工解除" },
            ].map(d => (
              <button key={d.value} onClick={() => setDuration(d.value)} style={{
                padding: "8px 10px", borderRadius: 8, fontSize: 12,
                border: "1px solid " + (duration === d.value ? "var(--ink)" : "var(--border)"),
                background: duration === d.value ? "var(--ink)" : "var(--surface)",
                color: duration === d.value ? "#fff" : "var(--ink-2)",
              }}>{d.label}</button>
            ))}
          </div>
        </div>

        <div style={{ marginTop: 16 }}>
          <label style={{ fontSize: 12, color: "var(--ink-2)", fontWeight: 500, display: "block", marginBottom: 6 }}>
            裁定理由 (會寄給用戶)
          </label>
          <textarea
            value={reason}
            onChange={e => setReason(e.target.value)}
            placeholder="例:累計違規 3 點 · 逾期交付 + 品質不符,暫時鎖定發布與提領功能,請儘速處理進行中訂單"
            style={{
              width: "100%", minHeight: 76, padding: 10, borderRadius: 8,
              border: "1px solid var(--border-strong)", resize: "vertical",
              fontSize: 13, lineHeight: 1.55, outline: "none",
              fontFamily: "inherit",
            }}
          />
        </div>

        <div style={{ marginTop: 16, display: "flex", alignItems: "center", gap: 10 }}>
          <div style={{ fontSize: 11.5, color: "var(--ink-3)", flex: 1 }}>
            <Icon name="scroll" size={11} style={{ verticalAlign: -1 }}/> 用戶會立即收到通知並看到功能被鎖定的提示。
          </div>
          <Button variant="secondary" onClick={onClose}>取消</Button>
          <Button
            variant="primary"
            disabled={selected.size === 0 || !reason.trim()}
            onClick={() => onConfirm({
              features: Array.from(selected),
              duration, reason: reason.trim(),
            })}
            style={{ background: "var(--danger)" }}
          >
            {existing ? "更新鎖定" : `鎖定 ${selected.size} 項功能`}
          </Button>
        </div>
      </div>
    </Modal>
  );
};

Object.assign(window, {
  VIOLATION_CATEGORIES, LOCKABLE_FEATURES,
  getViolationData,
  ViolationPointsPill, ViolationTab,
  AddViolationDialog, LockFeaturesDialog,
});
