/* global React, ReactDOM, TASKS, STATUS_META, STATUS_ORDER, DEPTS, TODAY, CURRENT_USER, DashboardAPI */
/* global TweaksPanel, TweakSection, TweakRadio, TweakToggle, useTweaks */

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

// ── helpers ───────────────────────────────────────────────────────────
const initials = (name) => (name || "?").slice(-2);
const pad2 = (n) => String(n).padStart(2, "0");
const todayDots = () => {
  const n = new Date();
  return `${n.getFullYear()}.${pad2(n.getMonth() + 1)}.${pad2(n.getDate())}`;
};
const todayLabel = () => {
  const n = new Date();
  const dow = ["일", "월", "화", "수", "목", "금", "토"][n.getDay()];
  return `${n.getFullYear()}.${pad2(n.getMonth() + 1)}.${pad2(n.getDate())} (${dow})`;
};
const fmtDateK = (yyyymmdd) => {
  // "2026.05.15" → "5/15"
  const m = /^\d{4}\.(\d{2})\.(\d{2})$/.exec(yyyymmdd || "");
  if (!m) return yyyymmdd || "";
  return `${parseInt(m[1], 10)}/${parseInt(m[2], 10)}`;
};
const relTime = (yyyymmdd) => {
  if (!yyyymmdd) return "";
  const [y, m, d] = yyyymmdd.split(".").map(Number);
  if (!y || !m || !d) return yyyymmdd;
  const then = new Date(y, m - 1, d);
  const now = new Date();
  now.setHours(0, 0, 0, 0);
  const days = Math.round((now - then) / 86400000);
  if (days <= 0) return "오늘";
  if (days === 1) return "어제";
  if (days < 7) return `${days}일 전`;
  if (days < 30) return `${Math.floor(days / 7)}주 전`;
  return yyyymmdd.replace(/^\d{4}\./, "");
};

// ── Summary card ──────────────────────────────────────────────────────
function SumCard({ label, value, total, statusKey, alert, delta, deltaDir, isTotal }) {
  const meta = statusKey ? STATUS_META[statusKey] : null;
  const pct = total && value != null ? Math.round((value / total) * 100) : null;
  return (
    <div className={`sum-card ${isTotal ? "total" : ""} ${alert ? `alert ${alert}` : ""}`}>
      <div className="sum-lbl">
        {meta && (
          <span className="pip" style={{ background: `var(--st-${meta.key})` }} />
        )}
        <span>{label}</span>
      </div>
      <div className="sum-val">
        {value}
        <span className="unit">건</span>
      </div>
      <div className="sum-delta">
        {pct != null ? (
          <span>전체의 {pct}%</span>
        ) : (
          <span>전체 업무 수</span>
        )}
        {delta != null && (
          <span className={deltaDir === "up" ? "up" : deltaDir === "dn" ? "dn" : ""}>
            <span className="arrow">{deltaDir === "up" ? "▲" : deltaDir === "dn" ? "▼" : "·"}</span>
            {" "}지난주 대비 {delta > 0 ? "+" : ""}{delta}
          </span>
        )}
      </div>
    </div>
  );
}

// ── Status badge ──────────────────────────────────────────────────────
function StatusBadge({ status }) {
  const m = STATUS_META[status];
  if (!m) return null;
  return (
    <span className={`badge ${m.className}`}>
      <span className="pip" />
      {m.label}
    </span>
  );
}

function LoginScreen({ onSignedIn }) {
  const [email, setEmail] = useState("");
  const [code, setCode] = useState("");
  const [phase, setPhase] = useState("email");
  const [busy, setBusy] = useState(false);
  const [error, setError] = useState("");

  const requestOtp = async (event) => {
    event.preventDefault();
    setBusy(true);
    setError("");
    try {
      await DashboardAPI.requestOtp(email);
      setPhase("code");
    } catch (err) {
      setError(err.message || "인증코드를 보내지 못했습니다.");
    } finally {
      setBusy(false);
    }
  };

  const verifyOtp = async (event) => {
    event.preventDefault();
    setBusy(true);
    setError("");
    try {
      const result = await DashboardAPI.verifyOtp(email, code);
      onSignedIn(result.user);
    } catch (err) {
      setError(err.message || "로그인하지 못했습니다.");
    } finally {
      setBusy(false);
    }
  };

  return (
    <div className="auth-shell">
      <form className="auth-card" onSubmit={phase === "email" ? requestOtp : verifyOtp}>
        <div className="eyebrow">Secure access</div>
        <h1>주요 업무 진행 현황</h1>
        <p>
          등록된 이메일로 인증코드를 받아 로그인합니다. 같은 브라우저에서 계속 사용하면 로그인 상태가 자동 연장됩니다.
        </p>

        <div className="auth-field">
          <label>이메일</label>
          <input
            className="auth-input"
            type="email"
            value={email}
            onChange={(event) => setEmail(event.target.value)}
            disabled={phase === "code" || busy}
            placeholder="name@company.com"
            autoComplete="email"
            required
          />
        </div>

        {phase === "code" && (
          <div className="auth-field">
            <label>인증코드</label>
            <input
              className="auth-input"
              value={code}
              onChange={(event) => setCode(event.target.value.replace(/\D/g, "").slice(0, 6))}
              placeholder="6자리 숫자"
              inputMode="numeric"
              autoComplete="one-time-code"
              required
            />
          </div>
        )}

        <div className="auth-error">{error}</div>
        <div className="auth-actions">
          {phase === "code" ? (
            <button type="button" className="auth-link" onClick={() => { setPhase("email"); setCode(""); }}>
              이메일 다시 입력
            </button>
          ) : <span />}
          <button className="btn primary" type="submit" disabled={busy}>
            {busy ? "처리 중" : phase === "email" ? "인증코드 받기" : "로그인"}
          </button>
        </div>
      </form>
    </div>
  );
}

// ── Task form modal (admin only) ──────────────────────────────────────
const EMPTY_TASK = {
  title: "",
  dept: "",
  directedBy: "",
  directedAt: "",
  status: "wait",
  summary: "",
  detail: "",
  memo: "",
  dueDate: "",
};

function TaskFormModal({ initial, onSave, onCancel, busy, error }) {
  const isEdit = Boolean(initial?.id);
  const [form, setForm] = useState(() => ({ ...EMPTY_TASK, ...(initial || {}) }));

  const set = (k, v) => setForm((prev) => ({ ...prev, [k]: v }));

  const submit = (e) => {
    e.preventDefault();
    onSave({
      title: form.title.trim(),
      dept: form.dept.trim(),
      directedBy: form.directedBy.trim(),
      directedAt: form.directedAt.trim(),
      status: form.status,
      summary: form.summary,
      detail: form.detail,
      memo: form.memo,
      dueDate: form.dueDate.trim(),
    });
  };

  return (
    <div className="modal-backdrop" onClick={onCancel}>
      <form className="modal-card" onClick={(e) => e.stopPropagation()} onSubmit={submit}>
        <div className="modal-head">
          <h2>{isEdit ? `업무 편집 (${initial.id})` : "새 업무 추가"}</h2>
          <button type="button" className="modal-close" onClick={onCancel}>✕</button>
        </div>

        <div className="modal-body">
          <div className="form-grid">
            <label className="form-field span-2">
              <span className="form-lbl">제목 *</span>
              <input
                className="form-input"
                value={form.title}
                onChange={(e) => set("title", e.target.value)}
                required
              />
            </label>

            <label className="form-field">
              <span className="form-lbl">담당 부서 *</span>
              <input
                className="form-input"
                value={form.dept}
                onChange={(e) => set("dept", e.target.value)}
                required
              />
            </label>

            <label className="form-field">
              <span className="form-lbl">상태</span>
              <select
                className="form-input"
                value={form.status}
                onChange={(e) => set("status", e.target.value)}
              >
                {STATUS_ORDER.map((s) => (
                  <option key={s} value={s}>{STATUS_META[s].label}</option>
                ))}
              </select>
            </label>

            <label className="form-field">
              <span className="form-lbl">지시자</span>
              <input
                className="form-input"
                value={form.directedBy}
                onChange={(e) => set("directedBy", e.target.value)}
                placeholder="예: 경영진, 부대표"
              />
            </label>

            <label className="form-field">
              <span className="form-lbl">지시일</span>
              <input
                className="form-input"
                value={form.directedAt}
                onChange={(e) => set("directedAt", e.target.value)}
                placeholder="YYYY.MM.DD"
              />
            </label>

            <label className="form-field">
              <span className="form-lbl">목표 완료일</span>
              <input
                className="form-input"
                value={form.dueDate}
                onChange={(e) => set("dueDate", e.target.value)}
                placeholder="YYYY.MM.DD"
              />
            </label>

            <label className="form-field span-2">
              <span className="form-lbl">한줄 요약</span>
              <input
                className="form-input"
                value={form.summary}
                onChange={(e) => set("summary", e.target.value)}
              />
            </label>

            <label className="form-field span-2">
              <span className="form-lbl">상세 내용</span>
              <textarea
                className="form-input"
                rows={5}
                value={form.detail}
                onChange={(e) => set("detail", e.target.value)}
              />
            </label>

            <label className="form-field span-2">
              <span className="form-lbl">메모</span>
              <textarea
                className="form-input"
                rows={3}
                value={form.memo}
                onChange={(e) => set("memo", e.target.value)}
              />
            </label>
          </div>

          {error && <div className="form-error">{error}</div>}
        </div>

        <div className="modal-foot">
          <button type="button" className="btn" onClick={onCancel} disabled={busy}>취소</button>
          <button type="submit" className="btn primary" disabled={busy}>
            {busy ? "저장 중" : isEdit ? "수정 저장" : "추가"}
          </button>
        </div>
      </form>
    </div>
  );
}

// ── Task row ──────────────────────────────────────────────────────────
function TaskRow({ task, idx, expanded, onToggle, onUpdate, isAdmin, onEdit, onDelete }) {
  const [memoDraft, setMemoDraft] = useState(task.memo);
  const [statusDraft, setStatusDraft] = useState(task.status);

  useEffect(() => {
    setMemoDraft(task.memo);
    setStatusDraft(task.status);
  }, [task.memo, task.status, expanded]);

  const dirty = memoDraft !== task.memo || statusDraft !== task.status;

  const rowCls =
    "tbl-row" +
    (expanded ? " expanded" : "") +
    (task.status === "late" ? " issue" : "") +
    (task.status === "hold" ? " hold" : "");

  return (
    <>
      <div className={rowCls} onClick={onToggle}>
        <div className="num">{String(idx + 1).padStart(2, "0")}</div>
        <div><StatusBadge status={task.status} /></div>
        <div className="title" title={task.title}>{task.title}</div>
        <div className="dept"><span className="dept-chip">{task.dept}</span></div>
        <div className="editor" title={`${task.editor.name} · ${task.editedAt}`}>
          <span className={`av ${task.editor.color}`}>{initials(task.editor.name)}</span>
          <span>
            <span className="name">{task.editor.name}</span>
            {" "}
            <span className="date">· {relTime(task.editedAt)}</span>
          </span>
        </div>
        <div className="editor" style={{ color: "var(--muted)" }}>
          마감 {fmtDateK(task.dueDate)}
        </div>
        <div className="chev">⌃</div>
      </div>

      {expanded && (
        <div className="expand" onClick={(e) => e.stopPropagation()}>
          <div className="expand-l">
            <h4>업무 상세</h4>
            <p>{task.detail}</p>

            <h4>메모</h4>
            <textarea
              className="memo-edit"
              value={memoDraft}
              onChange={(e) => setMemoDraft(e.target.value)}
              placeholder="진행 상황, 이슈, 다음 액션 등을 기록합니다."
            />
            <div className="actions">
              <button
                className="btn primary"
                disabled={!dirty}
                onClick={() => {
                  onUpdate({
                    memo: memoDraft,
                    status: statusDraft,
                  });
                }}
              >
                저장
              </button>
              <button
                className="btn"
                disabled={!dirty}
                onClick={() => {
                  setMemoDraft(task.memo);
                  setStatusDraft(task.status);
                }}
              >
                되돌리기
              </button>
              <span style={{ fontSize: 11.5, color: "var(--muted)", marginLeft: 4, whiteSpace: "nowrap" }}>
                {dirty ? "수정 사항이 있습니다" : "최신 상태입니다"}
              </span>
            </div>

            {isAdmin && (
              <div className="admin-actions">
                <button className="btn" onClick={() => onEdit(task)}>전체 편집</button>
                <button className="btn danger" onClick={() => onDelete(task)}>삭제</button>
              </div>
            )}
          </div>

          <div className="expand-r">
            <h4>상태 변경</h4>
            <div className="stt-grid">
              {STATUS_ORDER.map((s) => {
                const m = STATUS_META[s];
                const active = statusDraft === s;
                return (
                  <button
                    key={s}
                    className={`stt-opt ${m.className} ${active ? "active" : ""}`}
                    onClick={() => setStatusDraft(s)}
                  >
                    <span className="pip" />
                    {m.label}
                  </button>
                );
              })}
            </div>

            <div className="meta-line">
              <div className="row">
                <span className="k">업무 번호</span>
                <span className="v">{task.id}</span>
              </div>
              <div className="row">
                <span className="k">담당 사업부</span>
                <span className="v">{task.dept}</span>
              </div>
              <div className="row">
                <span className="k">지시자</span>
                <span className="v">{task.directedBy}</span>
              </div>
              <div className="row">
                <span className="k">지시일</span>
                <span className="v">{task.directedAt}</span>
              </div>
              <div className="row">
                <span className="k">목표 완료</span>
                <span className="v">{task.dueDate}</span>
              </div>
              <div className="row">
                <span className="k">최종 수정자</span>
                <span className="v">{task.editor.name}</span>
              </div>
              <div className="row">
                <span className="k">최종 수정일</span>
                <span className="v">{task.editedAt}</span>
              </div>
            </div>
          </div>
        </div>
      )}
    </>
  );
}

// ── App ───────────────────────────────────────────────────────────────
function App() {
  const [t, setTweak] = useTweaks(TWEAK_DEFAULTS);

  const [tasks, setTasks] = useState(TASKS);
  const [currentUser, setCurrentUser] = useState(CURRENT_USER);
  const [authState, setAuthState] = useState("checking");
  const [apiMode, setApiMode] = useState("checking");
  const [statusFilter, setStatusFilter] = useState("all");
  const [deptFilter, setDeptFilter] = useState("all");
  const [issueOnly, setIssueOnly] = useState(false);
  const [search, setSearch] = useState("");
  const [expandedId, setExpandedId] = useState(null);
  const [flash, setFlash] = useState("");
  const [formMode, setFormMode] = useState(null); // null | "create" | { type: "edit", task }
  const [formBusy, setFormBusy] = useState(false);
  const [formError, setFormError] = useState("");

  const isAdmin = currentUser?.role === "admin";

  const loadLiveData = async (signedInUser) => {
    const taskData = await DashboardAPI.listTasks();
    setTasks(taskData.tasks);
    setCurrentUser({
      name: signedInUser.name,
      email: signedInUser.email,
      role: signedInUser.role,
      color: "av-c1",
    });
    setApiMode("live");
    setAuthState("signed_in");
  };

  useEffect(() => {
    let active = true;

    async function boot() {
      try {
        const me = await DashboardAPI.me();
        if (!active) return;
        await loadLiveData(me.user);
      } catch (err) {
        if (!active) return;
        if (err.status === 401) {
          setApiMode("live");
          setAuthState("signed_out");
          return;
        }
        setApiMode("mock");
        setAuthState("signed_in");
        setFlash("목데이터로 실행 중입니다");
        setTimeout(() => setFlash(""), 1800);
      }
    }

    boot();
    return () => { active = false; };
  }, []);

  // density on body
  useEffect(() => {
    document.body.className = t.density === "compact" ? "density-compact" : "";
  }, [t.density]);

  const handleSaveForm = async (data) => {
    if (apiMode !== "live") {
      setFormError("로그인 후에만 추가/편집할 수 있습니다.");
      return;
    }
    setFormBusy(true);
    setFormError("");
    try {
      if (formMode === "create") {
        const result = await DashboardAPI.createTask(data);
        setTasks((prev) => [result.task, ...prev]);
        setFlash("새 업무를 추가했습니다");
      } else if (formMode && formMode.type === "edit") {
        const result = await DashboardAPI.replaceTask(formMode.task.id, data);
        setTasks((prev) => prev.map((x) => (x.id === result.task.id ? result.task : x)));
        setFlash("업무를 수정했습니다");
      }
      setFormMode(null);
      setTimeout(() => setFlash(""), 1600);
    } catch (err) {
      setFormError(err.message || "저장하지 못했습니다.");
    } finally {
      setFormBusy(false);
    }
  };

  const handleDeleteTask = async (task) => {
    if (apiMode !== "live") return;
    if (!window.confirm(`"${task.title}" 업무를 삭제하시겠습니까?\n변경 이력은 보존되지만 업무 본체는 복구할 수 없습니다.`)) return;
    try {
      await DashboardAPI.deleteTask(task.id);
      setTasks((prev) => prev.filter((x) => x.id !== task.id));
      if (expandedId === task.id) setExpandedId(null);
      setFlash("업무를 삭제했습니다");
      setTimeout(() => setFlash(""), 1600);
    } catch (err) {
      setFlash(err.message || "삭제하지 못했습니다");
      setTimeout(() => setFlash(""), 2200);
    }
  };

  const handleLogout = async () => {
    try {
      await DashboardAPI.logout();
    } catch (_) {
      // ignore — we'll reset state regardless
    }
    setAuthState("signed_out");
    setTasks([]);
    setCurrentUser({ name: "", color: "av-c1" });
    setExpandedId(null);
  };

  // unique dept list derived from tasks (works for both live and mock data)
  const deptOptions = useMemo(() => {
    const seen = new Set();
    const list = [];
    tasks.forEach((x) => {
      if (x.dept && !seen.has(x.dept)) {
        seen.add(x.dept);
        list.push(x.dept);
      }
    });
    return list.sort((a, b) => a.localeCompare(b, "ko"));
  }, [tasks]);

  // counts
  const counts = useMemo(() => {
    const c = { all: tasks.length, wait: 0, prog: 0, done: 0, hold: 0, late: 0 };
    tasks.forEach((x) => { c[x.status]++; });
    return c;
  }, [tasks]);

  // filter
  const filtered = useMemo(() => {
    let out = tasks;
    if (issueOnly) out = out.filter((x) => x.status === "late" || x.status === "hold");
    if (statusFilter !== "all") out = out.filter((x) => x.status === statusFilter);
    if (deptFilter !== "all") out = out.filter((x) => x.dept === deptFilter);
    if (search.trim()) {
      const q = search.trim().toLowerCase();
      out = out.filter((x) =>
        (x.title + x.summary + x.detail + x.memo + x.dept + x.editor.name)
          .toLowerCase()
          .includes(q)
      );
    }
    // sort
    if (t.sortBy === "issue") {
      const rank = { late: 0, hold: 1, prog: 2, wait: 3, done: 4 };
      out = [...out].sort((a, b) => rank[a.status] - rank[b.status]);
    } else if (t.sortBy === "date") {
      out = [...out].sort((a, b) => (b.editedAt > a.editedAt ? 1 : -1));
    } else if (t.sortBy === "dept") {
      out = [...out].sort((a, b) => a.dept.localeCompare(b.dept, "ko"));
    }
    return out;
  }, [tasks, issueOnly, statusFilter, deptFilter, search, t.sortBy]);

  // group by dept (optional)
  const grouped = useMemo(() => {
    if (!t.groupByDept) return [{ key: "_all", items: filtered }];
    const map = new Map();
    filtered.forEach((x) => {
      if (!map.has(x.dept)) map.set(x.dept, []);
      map.get(x.dept).push(x);
    });
    return [...map.entries()].map(([dept, items]) => ({ key: dept, label: dept, items }));
  }, [filtered, t.groupByDept]);

  const updateTask = async (id, patch) => {
    const localPatch = {
      ...patch,
      editor: currentUser,
      editedAt: todayDots(),
    };

    if (apiMode === "live") {
      try {
        const result = await DashboardAPI.updateTask(id, patch);
        setTasks((prev) => prev.map((x) => (x.id === id ? result.task : x)));
        setFlash("저장되었습니다");
      } catch (err) {
        setFlash(err.message || "저장하지 못했습니다");
      }
      setTimeout(() => setFlash(""), 1600);
      return;
    }

    setTasks((prev) => prev.map((x) => (x.id === id ? { ...x, ...localPatch } : x)));
    setFlash("저장되었습니다");
    setTimeout(() => setFlash(""), 1600);
  };

  const STATUS_CHIPS = [
    { key: "all", label: "전체", ct: counts.all },
    ...STATUS_ORDER.map((s) => ({
      key: s,
      label: STATUS_META[s].label,
      ct: counts[s],
      cls: STATUS_META[s].className,
    })),
  ];

  if (authState === "checking") {
    return (
      <div className="auth-shell">
        <div className="auth-card">
          <div className="eyebrow">Loading</div>
          <h1>주요 업무 진행 현황</h1>
          <p>로그인 상태와 업무 데이터를 확인하고 있습니다.</p>
        </div>
      </div>
    );
  }

  if (authState === "signed_out") {
    return (
      <LoginScreen
        onSignedIn={async (user) => {
          await loadLiveData(user);
          setFlash("로그인되었습니다");
          setTimeout(() => setFlash(""), 1600);
        }}
      />
    );
  }

  return (
    <div className="app" data-screen-label="01 대시보드">
      {/* Header */}
      <header className="hdr">
        <div className="hdr-l">
          {t.showEyebrow && <div className="eyebrow">Executive Directives · 경영진 지시사항 이행 트래킹</div>}
          <h1>주요 업무 진행 현황</h1>
        </div>
        <div className="hdr-meta">
          <span><span className="dot" />기준일 {todayLabel()}</span>
          <span className="who">
            <span className={`avatar`}>{initials(currentUser.name)}</span>
            {currentUser.name} · {isAdmin ? "관리자" : "편집"} 권한
          </span>
          {isAdmin && apiMode === "live" && (
            <button className="btn primary add-task-btn" onClick={() => { setFormError(""); setFormMode("create"); }}>
              + 업무 추가
            </button>
          )}
          {apiMode === "live" && (
            <button className="auth-link logout-btn" onClick={handleLogout} title="로그아웃">
              로그아웃
            </button>
          )}
        </div>
      </header>

      {/* Summary */}
      <section className="summary">
        <SumCard label="전체 업무" value={counts.all} isTotal delta={2} deltaDir="up" />
        <SumCard label="진행중"   value={counts.prog} total={counts.all} statusKey="prog" delta={1} deltaDir="up" />
        <SumCard label="지연"     value={counts.late} total={counts.all} statusKey="late" alert="late" delta={1} deltaDir="dn" />
        <SumCard label="보류"     value={counts.hold} total={counts.all} statusKey="hold" alert="hold" delta={0} />
        <SumCard label="완료"     value={counts.done} total={counts.all} statusKey="done" delta={2} deltaDir="up" />
      </section>

      {/* List section header */}
      <div className="sec-h">
        <h2>업무 목록</h2>
        <div className="count">
          <b>{filtered.length}</b>건 표시 · 전체 {tasks.length}건
        </div>
      </div>

      {/* Filters */}
      <div className="filters">
        <div className="grp">
          <span className="lbl">상태</span>
          {STATUS_CHIPS.map((s) => (
            <button
              key={s.key}
              className={`chip ${statusFilter === s.key ? "active" : ""}`}
              onClick={() => setStatusFilter(s.key)}
            >
              {s.cls && <span className="pip" style={{ background: `var(--st-${s.cls})` }} />}
              {s.label}
              <span className="ct">{s.ct}</span>
            </button>
          ))}
        </div>

        <div className="grp">
          <span className="lbl">사업부</span>
          <select
            className="select"
            value={deptFilter}
            onChange={(e) => setDeptFilter(e.target.value)}
          >
            <option value="all">전체 사업부</option>
            {deptOptions.map((d) => <option key={d} value={d}>{d}</option>)}
          </select>
        </div>

        <div className="grp">
          <input
            className="search"
            placeholder="업무명, 메모 검색"
            value={search}
            onChange={(e) => setSearch(e.target.value)}
          />
        </div>

        <button
          className={`issue-toggle ${issueOnly ? "on" : ""}`}
          onClick={() => setIssueOnly((v) => !v)}
          title="지연/보류 업무만 표시"
        >
          <span className="switch" />
          이슈만 보기
        </button>
      </div>

      {/* List */}
      {grouped.map((g) => (
        <div key={g.key} style={{ marginBottom: 24 }}>
          {g.label && (
            <div style={{
              fontSize: 12, fontWeight: 600, color: "var(--muted)",
              padding: "10px 4px 8px", letterSpacing: "0.04em",
              textTransform: "uppercase",
            }}>
              {g.label} <span style={{ color: "var(--muted-2)", marginLeft: 6 }}>{g.items.length}</span>
            </div>
          )}
          <div className="tbl">
            <div className="tbl-head">
              <div>#</div>
              <div>상태</div>
              <div>업무 내용</div>
              <div>사업부</div>
              <div className="col-edit">최종 수정</div>
              <div className="col-date">마감일</div>
              <div></div>
            </div>
            {g.items.length === 0 ? (
              <div className="empty">조건에 해당하는 업무가 없습니다.</div>
            ) : g.items.map((task, i) => (
              <TaskRow
                key={task.id}
                task={task}
                idx={tasks.indexOf(task)}
                expanded={expandedId === task.id}
                onToggle={() => setExpandedId(expandedId === task.id ? null : task.id)}
                onUpdate={(patch) => updateTask(task.id, patch)}
                isAdmin={isAdmin && apiMode === "live"}
                onEdit={(target) => { setFormError(""); setFormMode({ type: "edit", task: target }); }}
                onDelete={handleDeleteTask}
              />
            ))}
          </div>
        </div>
      ))}

      {/* Save flash */}
      <div className={`saved-flash ${flash ? "show" : ""}`}>
        ✓ {flash}
      </div>

      {/* Admin form modal */}
      {formMode && (
        <TaskFormModal
          initial={formMode.type === "edit" ? formMode.task : null}
          onSave={handleSaveForm}
          onCancel={() => { setFormMode(null); setFormError(""); }}
          busy={formBusy}
          error={formError}
        />
      )}

      {/* Tweaks */}
      <TweaksPanel title="Tweaks">
        <TweakSection label="표시" />
        <TweakRadio
          label="밀도"
          value={t.density}
          options={["regular", "compact"]}
          onChange={(v) => setTweak("density", v)}
        />
        <TweakToggle
          label="섹션 부제목 표시"
          value={t.showEyebrow}
          onChange={(v) => setTweak("showEyebrow", v)}
        />
        <TweakToggle
          label="사업부별 그룹"
          value={t.groupByDept}
          onChange={(v) => setTweak("groupByDept", v)}
        />
        <TweakSection label="정렬" />
        <TweakRadio
          label="기준"
          value={t.sortBy}
          options={["issue", "date", "dept"]}
          onChange={(v) => setTweak("sortBy", v)}
        />
      </TweaksPanel>
    </div>
  );
}

ReactDOM.createRoot(document.getElementById("root")).render(<App />);
