// admin.jsx — 관리자 대시보드
const { useState, useMemo } = React;

const A   = window.ADMIN;
const CAT = window.CATALOG;
const fmtKRW = (n) => "₩" + n.toLocaleString("ko-KR");
const fmt만 = (n) => (n / 10000).toLocaleString("ko-KR", { maximumFractionDigits: 0 }) + "만";
const CAT_LABEL = { road:"Road", gravel:"Gravel", mtb:"MTB", ebike:"E-bike", kids:"Kids", svc:"Service" };

// ─── HEADER ──────────────────────────────────────────────
function Header() {
  return (
    <header className="cat-header">
      <div className="cat-header-inner">
        <a className="cat-brand" href="#">
          <img className="cat-brand-mark" src="assets/symbol-brush.svg" alt="" />
          <div>
            <div className="cat-brand-name">김성호바이크 세종점</div>
            <div className="cat-brand-sub">Admin · 매니저 대시보드</div>
          </div>
        </a>
        <div className="cat-header-sep" />
        <div className="cat-header-title">관리자 <b>대시보드</b></div>
        <div className="cat-header-tools">
          <span><i className="adm-now-dot" />실시간 · <b>{A.meta.asOf}</b></span>
          <span>담당 <b>{A.meta.staff}</b></span>
        </div>
      </div>
    </header>
  );
}

// ─── PAGE NAV ────────────────────────────────────────────
function PageNav() {
  return (
    <nav className="cat-pagenav">
      <div className="cat-pagenav-inner">
        <a className="on" href="관리자.html">관리자 <small>Admin</small></a>
        <a href="스케줄러.html">스케줄러 <small>Scheduler</small></a>
        <a href="2026 카탈로그.html">카탈로그 <small>Catalog</small></a>
        <a href="재고관리.html">재고관리 <small>Inventory</small></a>
        <a href="서비스 견적서.html">서비스 견적서 <small>Estimate</small></a>
        {(window.AUTH?.role || "owner") === "owner" && (
          <a href="직원관리.html">👥 직원 관리 <small>Staff</small></a>
        )}
        <div className="cat-pagenav-r">
          <span>전 페이지 동기화 · <b>실시간</b></span>
        </div>
      </div>
    </nav>
  );
}

// ─── KPI ROW ─────────────────────────────────────────────
function KpiRow() {
  const { today, month, quotes, service, inventory } = A.kpi;
  const monthPct = Math.min(100, (month.sales / month.target) * 100);
  return (
    <div className="adm-kpi">
      <div className="adm-kpi-cell">
        <div className="adm-kpi-l">오늘 매출</div>
        <div className="adm-kpi-v">{fmtKRW(today.sales)}</div>
        <div className="adm-kpi-meta">
          <span>{today.orders}건 출고 · 어제 대비</span>
          <span className={"adm-kpi-delta " + (today.qDelta >= 0 ? "up":"down")}>
            {today.qDelta >= 0 ? "▲" : "▼"} {Math.abs(today.qDelta).toFixed(1)}%
          </span>
        </div>
      </div>
      <div className="adm-kpi-cell">
        <div className="adm-kpi-l">이번달 매출</div>
        <div className="adm-kpi-v">{fmt만(month.sales)}<small>원</small></div>
        <div className="adm-kpi-meta">
          <span>목표 {fmt만(month.target)}원 · {monthPct.toFixed(0)}%</span>
        </div>
        <div className="adm-kpi-bar"><i style={{ width: monthPct + "%" }} /></div>
      </div>
      <div className="adm-kpi-cell">
        <div className="adm-kpi-l">진행 견적 · 정비</div>
        <div className="adm-kpi-v">{quotes.pending + service.inProgress}<small>건</small></div>
        <div className="adm-kpi-meta">
          <span>견적 <b style={{color:"var(--info-500)"}}>{quotes.pending}</b>건 대기 · 정비 <b style={{color:"var(--red-500)"}}>{service.inProgress}</b>건 진행</span>
        </div>
      </div>
      <div className="adm-kpi-cell">
        <div className="adm-kpi-l">재고 이슈</div>
        <div className="adm-kpi-v" style={{color: inventory.out > 0 ? "var(--red-500)" : "var(--fg-primary)"}}>
          {inventory.low + inventory.out}<small>건</small>
        </div>
        <div className="adm-kpi-meta">
          <span>저재고 <b>{inventory.low}</b> · 품절 <b style={{color:"var(--red-500)"}}>{inventory.out}</b> · 평가액 {fmt만(inventory.totalValue)}원</span>
        </div>
      </div>
    </div>
  );
}

// ─── SALES CHART ─────────────────────────────────────────
function SalesChart() {
  const max = Math.max(...A.weeklySales);
  const total12w = A.weeklySales.reduce((a, b) => a + b, 0);
  const avg = total12w / A.weeklySales.length;
  const labels = ["W12","W11","W10","W9","W8","W7","W6","W5","W4","W3","W2","오늘"];
  return (
    <div className="adm-card">
      <div className="adm-card-head">
        <div className="adm-card-h">주간 매출 추이 <small>최근 12주</small></div>
        <a className="adm-card-link" href="#">전체 보고서 →</a>
      </div>
      <div className="adm-card-body">
        <div className="adm-chart">
          <div className="adm-chart-head">
            <div>
              <div className="adm-chart-stat-l">12주 누적</div>
              <div className="adm-chart-stat-v">{fmt만(total12w)}<span style={{fontSize:14,color:"var(--fg-tertiary)",fontWeight:500,marginLeft:4}}>원</span></div>
              <div className="adm-chart-stat-meta">주간 평균 {fmt만(avg)}원</div>
            </div>
            <div className="adm-chart-legend">
              <span><i style={{display:"inline-block",width:10,height:10,borderRadius:2,background:"var(--red-500)",marginRight:6,verticalAlign:"-1px"}} />이번주 <b style={{marginLeft:6}}>{fmt만(A.weeklySales[A.weeklySales.length-1])}원</b></span>
            </div>
          </div>
          <div className="adm-bars">
            {A.weeklySales.map((v, i) => {
              const h = (v / max) * 100;
              const isLast = i === A.weeklySales.length - 1;
              return (
                <div key={i} className={"adm-bar " + (isLast ? "cur":"")} style={{ height: h + "%" }}>
                  <div className="adm-bar-val">{fmt만(v)}원</div>
                  <div className="adm-bar-lbl">{labels[i]}</div>
                </div>
              );
            })}
          </div>
        </div>
      </div>
    </div>
  );
}

// ─── CATEGORY BREAKDOWN ──────────────────────────────────
function CategoryBreakdown() {
  const total = A.byCategory.reduce((a, b) => a + b.sales, 0);
  return (
    <div className="adm-card">
      <div className="adm-card-head">
        <div className="adm-card-h">카테고리별 매출 <small>이번달</small></div>
      </div>
      <div className="adm-card-body">
        <div className="adm-cat-stack">
          <div className="adm-cat-bar">
            {A.byCategory.map(c => {
              const pct = (c.sales / total) * 100;
              return <div key={c.id} className="adm-cat-seg" style={{ background: c.color, width: pct + "%" }} title={`${c.label} ${pct.toFixed(1)}%`} />;
            })}
          </div>
          <div className="adm-cat-rows">
            {A.byCategory.map(c => {
              const pct = (c.sales / total) * 100;
              return (
                <div className="adm-cat-row" key={c.id}>
                  <i style={{ background: c.color }} />
                  <b>{c.label}</b>
                  <em>{pct.toFixed(1)}%</em>
                  <strong>{fmt만(c.sales)}원</strong>
                </div>
              );
            })}
          </div>
        </div>
      </div>
    </div>
  );
}

// ─── RECENT QUOTES TABLE ─────────────────────────────────
function QuotesTable() {
  return (
    <div className="adm-card">
      <div className="adm-card-head">
        <div className="adm-card-h">최근 견적 <small>{A.quotes.length}건</small></div>
        <a className="adm-card-link" href="서비스 견적서.html">새 견적 발행 →</a>
      </div>
      <table className="adm-table">
        <thead>
          <tr>
            <th style={{width:140}}>견적번호</th>
            <th>고객 · 차량</th>
            <th className="num" style={{width:120}}>금액</th>
            <th style={{width:80}}>상태</th>
            <th style={{width:110}}>발행일</th>
          </tr>
        </thead>
        <tbody>
          {A.quotes.map(q => (
            <tr key={q.no}>
              <td className="mono">{q.no}</td>
              <td>
                <b>{q.customer}</b>
                <small>{q.bike}</small>
              </td>
              <td className="num"><b>{fmtKRW(q.total)}</b></td>
              <td><span className={"adm-tag " + q.statusTone}>{q.status}</span></td>
              <td style={{color:"var(--fg-tertiary)", fontSize:12}}>{q.date}</td>
            </tr>
          ))}
        </tbody>
      </table>
    </div>
  );
}

// ─── SERVICE QUEUE TABLE ─────────────────────────────────
function ServiceTable() {
  return (
    <div className="adm-card">
      <div className="adm-card-head">
        <div className="adm-card-h">정비 큐 <small>{A.services.length}건</small></div>
        <a className="adm-card-link" href="#">정비 일정 →</a>
      </div>
      <table className="adm-table">
        <thead>
          <tr>
            <th style={{width:90}}>오더</th>
            <th>고객 · 차량</th>
            <th>작업</th>
            <th style={{width:120}}>예상 완료</th>
            <th style={{width:70}}>상태</th>
          </tr>
        </thead>
        <tbody>
          {A.services.map(s => (
            <tr key={s.id}>
              <td className="mono">{s.id}</td>
              <td>
                <b>{s.customer}</b>
                <small>{s.bike}</small>
              </td>
              <td>{s.job}</td>
              <td style={{color:"var(--fg-tertiary)", fontSize:12}}>{s.eta}</td>
              <td><span className={"adm-tag " + s.stageTone}>{s.stage}</span></td>
            </tr>
          ))}
        </tbody>
      </table>
    </div>
  );
}

// ─── ACTIVITY FEED ───────────────────────────────────────
const FEED_ICON = {
  sale:    <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5"><path d="M20 6 9 17l-5-5"/></svg>,
  quote:   <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/></svg>,
  service: <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5"><path d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z"/></svg>,
  stock:   <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5"><path d="M16 2v5h5"/><path d="M3 8v14h18V11l-5-5H6a3 3 0 0 0-3 3z"/></svg>,
};
function ActivityFeed() {
  return (
    <div className="adm-card">
      <div className="adm-card-head">
        <div className="adm-card-h">활동 피드 <small>최근 24h</small></div>
        <a className="adm-card-link" href="#">전체 →</a>
      </div>
      <div className="adm-card-body">
        <div className="adm-feed">
          {A.activity.map((a, i) => (
            <div key={i} className="adm-feed-item">
              <div className={"adm-feed-icon " + a.type}>{FEED_ICON[a.type]}</div>
              <div className="adm-feed-body">
                <div className="adm-feed-msg">{a.msg}</div>
                <div className="adm-feed-meta">
                  <span>고객 · <b>{a.who}</b></span>
                  <span className="adm-feed-time">{a.t}</span>
                </div>
              </div>
              <div className="adm-feed-amt">{a.amount ? fmtKRW(a.amount) : ""}</div>
            </div>
          ))}
        </div>
      </div>
    </div>
  );
}

// ─── TASKS ───────────────────────────────────────────────
// v3.16: 오늘의 할 일 — 체크박스 + 일괄 완료/삭제 + 입력 + localStorage 영속
function Tasks() {
  const myKey = "sejong-tasks-" + (window.AUTH?.user?.email || "guest");
  const [tasks, setTasks] = React.useState(() => {
    try {
      const raw = localStorage.getItem(myKey);
      if (raw) return JSON.parse(raw);
    } catch {}
    return [];
  });
  const [selected, setSelected] = React.useState(new Set());
  const [input, setInput] = React.useState("");
  const [filter, setFilter] = React.useState("all"); // all | active | done

  React.useEffect(() => {
    try { localStorage.setItem(myKey, JSON.stringify(tasks)); } catch {}
  }, [tasks, myKey]);

  const filtered = React.useMemo(() => {
    if (filter === "active") return tasks.filter(t => !t.done);
    if (filter === "done")   return tasks.filter(t =>  t.done);
    return tasks;
  }, [tasks, filter]);

  const counts = React.useMemo(() => ({
    all: tasks.length,
    active: tasks.filter(t => !t.done).length,
    done: tasks.filter(t => t.done).length,
  }), [tasks]);

  const add = () => {
    const txt = input.trim();
    if (!txt) return;
    setTasks(prev => [{ id: "t-" + Date.now(), msg: txt, done: false, createdAt: new Date().toISOString() }, ...prev]);
    setInput("");
  };

  const toggleSelect = (id) => {
    setSelected(prev => {
      const next = new Set(prev);
      if (next.has(id)) next.delete(id); else next.add(id);
      return next;
    });
  };

  const toggleDone = (id) => {
    setTasks(prev => prev.map(t => t.id === id ? { ...t, done: !t.done, doneAt: !t.done ? new Date().toISOString() : null } : t));
  };

  const bulkComplete = () => {
    setTasks(prev => prev.map(t => selected.has(t.id) ? { ...t, done: true, doneAt: new Date().toISOString() } : t));
    setSelected(new Set());
  };

  const bulkDelete = () => {
    if (!confirm(`선택된 ${selected.size}건을 삭제하시겠습니까?`)) return;
    setTasks(prev => prev.filter(t => !selected.has(t.id)));
    setSelected(new Set());
  };

  const selectAll = () => {
    if (selected.size === filtered.length) setSelected(new Set());
    else setSelected(new Set(filtered.map(t => t.id)));
  };

  return (
    <div className="adm-card">
      <div className="adm-card-head">
        <div className="adm-card-h">오늘의 할 일 <small>{counts.active}건 진행 · {counts.done}건 완료</small></div>
      </div>
      <div className="adm-card-body">

        {/* 입력 */}
        <div style={{display:"flex",gap:6,marginBottom:10}}>
          <input
            type="text"
            value={input}
            onChange={e=>setInput(e.target.value)}
            onKeyDown={e=>{ if (e.key === "Enter") add(); }}
            placeholder="새 할 일 입력 후 Enter (예: 본사 발주 요청)"
            style={{flex:1,padding:"8px 12px",border:"1px solid var(--grey-300,#D1D6DB)",borderRadius:8,fontSize:13}}
          />
          <button onClick={add} disabled={!input.trim()} style={{
            padding:"8px 14px",border:"none",borderRadius:8,
            background: input.trim() ? "var(--red-500,#E31E26)" : "var(--grey-300,#D1D6DB)",
            color:"#fff",cursor: input.trim() ? "pointer" : "not-allowed",fontSize:13,fontWeight:600,
          }}>＋ 추가</button>
        </div>

        {/* 필터 + 일괄 액션 */}
        <div style={{display:"flex",alignItems:"center",gap:6,marginBottom:8,flexWrap:"wrap"}}>
          {[
            { id:"all", label:"전체" },
            { id:"active", label:"진행" },
            { id:"done", label:"완료" },
          ].map(f => (
            <button key={f.id} onClick={()=>setFilter(f.id)} style={{
              padding:"4px 10px",borderRadius:6,border:"1px solid "+(filter===f.id?"var(--grey-900,#191F28)":"var(--grey-300,#D1D6DB)"),
              background: filter===f.id?"var(--grey-900,#191F28)":"#fff",
              color: filter===f.id?"#fff":"var(--grey-700,#4E5968)",
              fontSize:12,cursor:"pointer",
            }}>{f.label} <small style={{opacity:0.7,marginLeft:2}}>{counts[f.id]}</small></button>
          ))}
          {selected.size > 0 && (
            <React.Fragment>
              <span style={{flex:1}}/>
              <span style={{fontSize:12,color:"var(--blue-700,#0049BD)",fontWeight:600}}>선택 {selected.size}건</span>
              <button onClick={bulkComplete} style={{padding:"4px 10px",borderRadius:6,border:"none",background:"var(--green-500,#0E8F5F)",color:"#fff",fontSize:12,cursor:"pointer",fontWeight:600}}>✓ 일괄 완료</button>
              <button onClick={bulkDelete} style={{padding:"4px 10px",borderRadius:6,border:"none",background:"var(--red-500,#E31E26)",color:"#fff",fontSize:12,cursor:"pointer",fontWeight:600}}>🗑 일괄 삭제</button>
            </React.Fragment>
          )}
        </div>

        {/* 전체 선택 */}
        {filtered.length > 0 && (
          <div style={{display:"flex",alignItems:"center",gap:6,padding:"4px 0 8px",fontSize:11,color:"var(--grey-500,#8B95A1)",borderBottom:"1px solid var(--grey-100,#F2F4F6)",marginBottom:6}}>
            <input type="checkbox" checked={selected.size === filtered.length && filtered.length > 0} onChange={selectAll} />
            <span>전체 선택 ({filtered.length}건)</span>
          </div>
        )}

        {/* 리스트 */}
        {filtered.length === 0 ? (
          <div style={{padding:"24px 12px",textAlign:"center",color:"var(--grey-500,#8B95A1)",fontSize:13}}>
            {filter === "done" ? "✓ 완료된 항목이 없습니다" :
             filter === "active" ? "✨ 진행 중인 할 일이 없습니다" :
             "+ 새 할 일을 추가해보세요"}
          </div>
        ) : (
          <div className="adm-tasks">
            {filtered.map(t => (
              <div key={t.id} className="adm-task" style={{
                display:"flex",alignItems:"center",gap:8,padding:"8px 6px",
                borderBottom:"1px solid var(--grey-50,#F9FAFB)",
                opacity: t.done ? 0.55 : 1,
              }}>
                <input type="checkbox" checked={selected.has(t.id)} onChange={()=>toggleSelect(t.id)} />
                <input type="checkbox" checked={!!t.done} onChange={()=>toggleDone(t.id)} title="완료 표시"
                  style={{accentColor:"var(--green-500,#0E8F5F)"}}/>
                <div style={{
                  flex:1,fontSize:13,color:"var(--grey-900,#191F28)",
                  textDecoration: t.done ? "line-through" : "none",
                }}>{t.msg}</div>
                <button onClick={()=>setTasks(prev=>prev.filter(x=>x.id!==t.id))}
                  style={{background:"none",border:"none",color:"var(--grey-400,#B0B8C1)",cursor:"pointer",fontSize:16,padding:"0 4px"}}
                  title="개별 삭제">×</button>
              </div>
            ))}
          </div>
        )}
      </div>
    </div>
  );
}

// ─── BESTSELLERS ─────────────────────────────────────────
function Bestsellers() {
  return (
    <div className="adm-card">
      <div className="adm-card-head">
        <div className="adm-card-h">베스트셀러 <small>이번달</small></div>
        <a className="adm-card-link" href="2026 카탈로그.html">카탈로그 →</a>
      </div>
      <div className="adm-card-body">
        <div className="adm-best">
          {A.bestsellers.map((b, i) => (
            <div key={b.sku} className="adm-best-row">
              <div className="adm-best-rank">{String(i+1).padStart(2,"0")}</div>
              <div className="adm-best-name">
                <b>{b.name}</b>
                <small>{CAT_LABEL[b.cat]}</small>
              </div>
              <div className="adm-best-num">
                <b>{b.units} 대</b>
                <small>{fmt만(b.revenue)}원</small>
              </div>
            </div>
          ))}
        </div>
      </div>
    </div>
  );
}

// ─── QUICK LINKS ─────────────────────────────────────────
function QuickLinks() {
  return (
    <div className="adm-links">
      <a className="adm-link red" href="서비스 견적서.html">
        <div className="adm-link-icon">
          <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/><line x1="12" y1="18" x2="12" y2="12"/><line x1="9" y1="15" x2="15" y2="15"/></svg>
        </div>
        <div className="adm-link-h">새 견적서 발행</div>
        <div className="adm-link-d">라이카 스타일 · 인쇄 가능</div>
      </a>
      <a className="adm-link" href="재고관리.html">
        <div className="adm-link-icon">
          <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"/><polyline points="3.27 6.96 12 12.01 20.73 6.96"/><line x1="12" y1="22.08" x2="12" y2="12"/></svg>
        </div>
        <div className="adm-link-h">재고 관리</div>
        <div className="adm-link-d">위치 · 입고 · 발주 · CSV</div>
      </a>
      <a className="adm-link" href="2026 카탈로그.html">
        <div className="adm-link-icon">
          <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><path d="M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2z"/><path d="M22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h7z"/></svg>
        </div>
        <div className="adm-link-h">2026 카탈로그</div>
        <div className="adm-link-d">16 모델 · 비교 · 무게 차트</div>
      </a>
    </div>
  );
}

// ─── FOOTER ──────────────────────────────────────────────
function Footer() {
  return (
    <footer className="cat-footer">
      <div className="cat-inner cat-footer-inner">
        <div className="cat-footer-brand">
          <img className="cat-footer-mark" src="assets/symbol-brush-sejong.svg" alt="" />
          <div>
            <div className="cat-footer-name">관리자 콘솔 · 김성호바이크 세종점</div>
            <div className="cat-footer-sub">관리자 전용 · 페이지 외부 공유 금지</div>
          </div>
        </div>
        <div className="cat-footer-meta">
          <span>본 페이지는 매장 매니저(김성호)에게만 표시됩니다</span>
          <img className="cat-footer-wordmark" src="assets/wordmark-specialized.svg" alt="Specialized" />
        </div>
      </div>
    </footer>
  );
}

// ─── LOCATION OVERVIEW — 매장 재고 현황판 (위치별) ───
const LOC_META = {
  A: { label: "쇼룸 1F",  short: "1F", color: "#E31E26", desc: "신상 · 카본 라이드 전시" },
  B: { label: "쇼룸 2F",  short: "2F", color: "#FF6B00", desc: "MTB · 그래블 · 트레일" },
  W: { label: "창고",     short: "W",  color: "#74777F", desc: "보조 재고 · 발주 대기" },
};
const CAT_NAME = { road:"Road", gravel:"Gravel", mtb:"MTB", ebike:"E-bike", kids:"Kids" };
const CAT_COLOR = { road:"#1B1B1F", gravel:"#FF6B00", mtb:"#74777F", ebike:"#3B82F6", kids:"#22C55E" };

function LocationOverview() {
  // 각 위치별로 카테고리/모델 합계 계산
  const stats = useMemo(() => {
    const out = {};
    Object.keys(LOC_META).forEach(loc => {
      const byCat = {};
      const byModel = [];
      let total = 0;
      let value = 0;
      CAT.bikes.forEach(b => {
        if (!b.stockMatrix) return;
        let modelStock = 0;
        b.sizes.forEach(sz => {
          const n = b.stockMatrix[sz]?.[loc] || 0;
          modelStock += n;
        });
        if (modelStock > 0) {
          byCat[b.cat] = (byCat[b.cat] || 0) + modelStock;
          byModel.push({ sku: b.sku, name: b.name, cat: b.cat, gradient: b.gradient, stock: modelStock });
          total += modelStock;
          value += modelStock * b.priceShop;
        }
      });
      byModel.sort((a, b) => b.stock - a.stock);
      out[loc] = { total, value, byCat, byModel };
    });
    return out;
  }, []);

  const grandTotal = Object.values(stats).reduce((a, s) => a + s.total, 0);
  const maxLoc = Math.max(1, ...Object.values(stats).map(s => s.total));

  return (
    <div className="adm-card adm-loc-chart-card">
      <div className="adm-card-head">
        <div className="adm-card-h">매장 재고 현황판 <small>위치별 보유 대수 · 전체 {grandTotal}대</small></div>
        <a className="adm-card-link" href="재고관리.html">재고관리 →</a>
      </div>
      <div className="adm-card-body">
        <div className="adm-loc-rows">
          {Object.entries(LOC_META).map(([k, m]) => {
            const s = stats[k];
            return (
              <div key={k} className="adm-loc-row" style={{borderLeftColor: m.color}}>
                <div className="adm-loc-row-l">
                  <i style={{background: m.color}} />
                  <div>
                    <b>{m.label}</b>
                    <small>{k} · {m.desc}</small>
                  </div>
                </div>
                <div className="adm-loc-row-models">
                  {s.byModel.slice(0, 4).map(b => (
                    <span key={b.sku} className="adm-loc-row-model">
                      <em>{b.name}</em>
                      <b>{b.stock}</b>
                    </span>
                  ))}
                  {s.byModel.length > 4 && <span className="adm-loc-row-more">+{s.byModel.length - 4}</span>}
                </div>
                <div className="adm-loc-row-val">
                  <b>{s.total}<em>대</em></b>
                  <small>{"₩" + (s.value / 10000).toLocaleString("ko-KR", {maximumFractionDigits:0}) + "만"}</small>
                </div>
              </div>
            );
          })}
        </div>
      </div>
    </div>
  );
}

// ─── APP ─────────────────────────────────────────────────
// ─── v3.9 어드민 보강 모듈 5건 (기획서 §6 Phase B/C) ────────────────

// B5: 권한별 마스킹 헬퍼 — owner만 매출/원가 보임
function maskMoney(amount) {
  const role = window.AUTH?.role || "owner";
  if (role !== "owner") return "₩***";
  return "₩" + (amount || 0).toLocaleString("ko-KR");
}

// A3: 메인 KPI 큰 카드 + 작은 3개
function MainKpiHero() {
  // v3.16 fix: A = window.ADMIN, KPI는 A.kpi 하위. 옵셔널 체이닝 + 폴백
  const K = (A && A.kpi) || {};
  const today = K.today || { sales: 0, orders: 0, qDelta: 0 };
  const month = K.month || { sales: 0, orders: 0, target: 1 };
  const inventory = K.inventory || { low: 0, out: 0, totalValue: 0 };
  const quotes = K.quotes || { pending: 0, accepted: 0, expired: 0 };
  const service = K.service || { inProgress: 0, queued: 0, completedToday: 0 };
  const role = window.AUTH?.role || "owner";
  const monthPct = Math.min(100, Math.round((month.sales / Math.max(1, month.target)) * 100));

  // 미니 sparkline (최근 7일)
  const weeklySpark = (A.weeklySales || []).slice(-7);
  const maxSpark = Math.max(...weeklySpark, 1);

  return (
    <div style={{display:"grid",gridTemplateColumns:"1.6fr 1fr 1fr 1fr",gap:12,marginBottom:20}}>
      {/* 메인 카드 — 오늘 매출 (큼직) */}
      <div style={{background:"linear-gradient(135deg, #1B1B1F, #2A2C30)",color:"#fff",
                   padding:"24px 28px",borderRadius:16,position:"relative",overflow:"hidden"}}>
        <div style={{fontSize:12,fontWeight:600,opacity:0.7,letterSpacing:"0.06em",textTransform:"uppercase"}}>
          오늘 매출
        </div>
        <div style={{fontSize:38,fontWeight:700,letterSpacing:"-0.02em",margin:"4px 0",
                     fontFeatureSettings:'"tnum" 1'}}>
          {maskMoney(today.sales)}
        </div>
        <div style={{display:"flex",alignItems:"center",gap:8,fontSize:13,opacity:0.85}}>
          <span style={{color: today.qDelta >= 0 ? "#4ADE80" : "#F87171"}}>
            {today.qDelta >= 0 ? "▲" : "▼"} {Math.abs(today.qDelta)}%
          </span>
          <span>· 전일 대비</span>
        </div>
        {/* Sparkline */}
        <div style={{display:"flex",alignItems:"flex-end",gap:3,height:32,marginTop:14}}>
          {weeklySpark.map((v,i)=>(
            <div key={i} style={{flex:1,height:(v/maxSpark*100)+"%",minHeight:4,
                                 background: i === weeklySpark.length-1 ? "var(--red-500,#E31E26)" : "rgba(255,255,255,0.25)",
                                 borderRadius:2}}/>
          ))}
        </div>
        <div style={{position:"absolute",top:0,right:0,bottom:0,width:120,
                     background:"radial-gradient(circle at 100% 0%, rgba(227,30,38,0.15), transparent 70%)"}}/>
      </div>

      {/* 작은 3개 */}
      <div className="adm-kpi-cell" style={{padding:20}}>
        <div className="adm-kpi-l">이번달 진척</div>
        <div className="adm-kpi-v">{monthPct}<small>%</small></div>
        <div className="adm-kpi-meta">{maskMoney(month.sales)} / {maskMoney(month.target)}</div>
        <div className="adm-kpi-bar"><i style={{ width: monthPct + "%" }} /></div>
      </div>
      <div className="adm-kpi-cell" style={{padding:20}}>
        <div className="adm-kpi-l">진행 견적·정비</div>
        <div className="adm-kpi-v">{quotes.pending + service.inProgress}<small>건</small></div>
        <div className="adm-kpi-meta">견적 {quotes.pending} · 정비 {service.inProgress}</div>
      </div>
      <div className="adm-kpi-cell" style={{padding:20}}>
        <div className="adm-kpi-l">재고 이슈</div>
        <div className="adm-kpi-v" style={{color: inventory.out > 0 ? "var(--red-500)" : "var(--fg-primary)"}}>
          {inventory.low + inventory.out}<small>건</small>
        </div>
        <div className="adm-kpi-meta">품절 {inventory.out} · 저재고 {inventory.low}</div>
      </div>
    </div>
  );
}

// B1: 목표 대시보드 — 일/주/월/분기
function GoalsBoard() {
  // v3.16 fix: A.kpi 하위 + 옵셔널
  const K = (A && A.kpi) || {};
  const today = K.today || { sales: 0 };
  const month = K.month || { sales: 0 };
  const goals = { daily: 1000000, weekly: 7000000, monthly: 30000000, quarterly: 90000000 };
  const actual = {
    daily: today.sales,
    weekly: (A?.weeklySales || []).slice(-1)[0] || today.sales * 5,
    monthly: month.sales,
    quarterly: month.sales * 3,
  };
  const items = [
    { id:"daily",     label:"일 목표",   goal: goals.daily,     actual: actual.daily,     emoji:"☀️" },
    { id:"weekly",    label:"주 목표",   goal: goals.weekly,    actual: actual.weekly,    emoji:"📅" },
    { id:"monthly",   label:"월 목표",   goal: goals.monthly,   actual: actual.monthly,   emoji:"🌙" },
    { id:"quarterly", label:"분기 목표", goal: goals.quarterly, actual: actual.quarterly, emoji:"📊" },
  ];
  const role = window.AUTH?.role || "owner";
  if (role !== "owner") return null; // owner만 노출

  return (
    <section style={{marginBottom:20}}>
      <div style={{display:"flex",alignItems:"center",justifyContent:"space-between",marginBottom:10}}>
        <span style={{fontSize:14,fontWeight:600,color:"var(--grey-700)"}}>🎯 목표 진척률</span>
        <button onClick={()=>alert("목표 수정 — Phase 5 직전 출시")}
          style={{background:"none",border:"none",color:"var(--grey-500)",fontSize:12,cursor:"pointer"}}>
          목표 수정 →
        </button>
      </div>
      <div style={{display:"grid",gridTemplateColumns:"repeat(4,1fr)",gap:10}}>
        {items.map(it => {
          const pct = Math.min(100, Math.round((it.actual / Math.max(1, it.goal)) * 100));
          const tone = pct >= 100 ? "green" : pct >= 70 ? "yellow" : "red";
          const color = tone === "green" ? "var(--green-500,#22C55E)" : tone === "yellow" ? "#FF9500" : "var(--red-500,#E31E26)";
          return (
            <div key={it.id} style={{padding:"14px 16px",background:"#fff",
                                     border:"1px solid var(--grey-200,#E5E8EB)",borderRadius:10}}>
              <div style={{display:"flex",alignItems:"center",justifyContent:"space-between",marginBottom:6}}>
                <span style={{fontSize:12,color:"var(--grey-600)"}}>{it.emoji} {it.label}</span>
                <span style={{fontSize:14,fontWeight:700,color}}>{pct}%</span>
              </div>
              <div style={{fontSize:15,fontWeight:600,color:"var(--grey-900)",
                          fontFeatureSettings:'"tnum" 1',marginBottom:4}}>
                {(it.actual/10000).toFixed(0)}만 / {(it.goal/10000).toFixed(0)}만
              </div>
              <div style={{height:4,background:"var(--grey-100,#F2F4F6)",borderRadius:2,overflow:"hidden"}}>
                <div style={{width:pct+"%",height:"100%",background:color,transition:"width .5s"}}/>
              </div>
            </div>
          );
        })}
      </div>
    </section>
  );
}

// B2: CS 응대 큐 — 답변 대기 견적 + 시간 추적
function CustomerQueue() {
  // mock: 실제로는 quotes 테이블에서 status='draft'/'issued' 가져옴
  const items = [
    { no:"Q-260524-204", customer:"이도현", bike:"Tarmac SL7", waitedHours: 26, tone:"red" },
    { no:"Q-260525-101", customer:"박세영", bike:"Roubaix SL8", waitedHours: 14, tone:"yellow" },
    { no:"Q-260525-095", customer:"한지훈", bike:"Diverge", waitedHours: 6, tone:"gray" },
  ];
  return (
    <section style={{marginBottom:20,padding:"16px 20px",background:"#fff",
                     border:"1px solid var(--grey-200,#E5E8EB)",borderRadius:12}}>
      <div style={{display:"flex",alignItems:"center",justifyContent:"space-between",marginBottom:12}}>
        <span style={{fontSize:14,fontWeight:600,color:"var(--grey-900)"}}>
          📞 CS 응대 큐 <span style={{color:"var(--grey-500)",fontWeight:400,marginLeft:6}}>{items.length}건 대기</span>
        </span>
        <a href="견적서목록.html" style={{fontSize:12,color:"var(--fg-brand,#E31E26)",textDecoration:"none"}}>
          전체 보기 →
        </a>
      </div>
      {items.length === 0 ? (
        <div style={{padding:20,textAlign:"center",color:"var(--grey-500)"}}>응대 대기 견적 없음 ✨</div>
      ) : (
        <div style={{display:"flex",flexDirection:"column",gap:8}}>
          {items.map(it => {
            const tone = it.waitedHours >= 24 ? "red" : it.waitedHours >= 12 ? "yellow" : "gray";
            const color = tone === "red" ? "var(--red-500)" : tone === "yellow" ? "#DD7D02" : "var(--grey-500)";
            const bg = tone === "red" ? "var(--red-50,#FFE5E6)" : tone === "yellow" ? "#FFF9E7" : "var(--grey-50)";
            return (
              <div key={it.no} style={{display:"flex",alignItems:"center",gap:12,padding:"10px 12px",
                                       background:bg,borderRadius:8,borderLeft:`3px solid ${color}`}}>
                <span style={{fontFamily:"var(--font-mono)",fontSize:12,color:"var(--grey-700)",minWidth:120}}>{it.no}</span>
                <span style={{flex:1,fontSize:13,color:"var(--grey-900)",fontWeight:500}}>
                  {it.customer} · <span style={{color:"var(--grey-600)"}}>{it.bike}</span>
                </span>
                <span style={{fontSize:12,fontWeight:600,color}}>
                  {it.waitedHours >= 24 ? "🔴" : it.waitedHours >= 12 ? "🟡" : "⚪"} {it.waitedHours}시간 대기
                </span>
                <a href={"견적서목록.html#" + it.no}
                   style={{fontSize:12,padding:"4px 10px",background:"#fff",border:"1px solid var(--grey-200)",
                          borderRadius:6,textDecoration:"none",color:"var(--grey-800)"}}>
                  답변 →
                </a>
              </div>
            );
          })}
        </div>
      )}
    </section>
  );
}

// C1: 인사이트 카드 — 자동 패턴 감지 (mock 기반 통계)
function SmartInsights() {
  // 실제로는 quotes + inventory_lots 데이터로 자동 분석
  // 여기선 mock 데이터 기반 예시 인사이트
  const bikes = window.CATALOG?.bikes || [];
  const insights = [
    bikes.length > 0 && {
      tone:"green", emoji:"📈",
      title:"이번 주 강세 모델",
      msg: `${bikes[0]?.name || 'Tarmac SL8'}이 평소 대비 ↑180% 판매. 추가 입고 검토.`
    },
    bikes.filter(b=>b.stock===0).length > 0 && {
      tone:"red", emoji:"⚠️",
      title:"품절 모델 발견",
      msg: `${bikes.filter(b=>b.stock===0).length}종 품절. 본사 발주 예상 7일.`
    },
    {
      tone:"blue", emoji:"💡",
      title:"재고 회전율 단축",
      msg:"Roubaix 시리즈 평균 보유일수 12일 → 7일. 시즌 강세."
    },
    {
      tone:"yellow", emoji:"🔍",
      title:"고객 패턴",
      msg:"이번 주 신규 견적 5건 중 4건이 한지영 매니저 응대."
    },
  ].filter(Boolean);

  const toneColors = {
    green: { bg:"#F0FAF6", border:"#A7E0BF", text:"#029359" },
    red:   { bg:"var(--red-50,#FFE5E6)", border:"#FEAFB4", text:"#9A0E14" },
    yellow:{ bg:"#FFF9E7", border:"#F5C97A", text:"#DD7D02" },
    blue:  { bg:"#E8F3FF", border:"#90C2FF", text:"#1B64DA" },
  };

  return (
    <section style={{marginBottom:20}}>
      <div style={{display:"flex",alignItems:"center",justifyContent:"space-between",marginBottom:10}}>
        <span style={{fontSize:14,fontWeight:600,color:"var(--grey-700)"}}>💡 스마트 인사이트 <span style={{color:"var(--grey-500)",fontWeight:400,marginLeft:6}}>자동 분석</span></span>
      </div>
      <div style={{display:"grid",gridTemplateColumns:"repeat(auto-fill, minmax(260px, 1fr))",gap:10}}>
        {insights.map((it,i)=>{
          const c = toneColors[it.tone];
          return (
            <div key={i} style={{padding:"14px 16px",background:c.bg,border:`1px solid ${c.border}`,borderRadius:10}}>
              <div style={{display:"flex",alignItems:"center",gap:6,marginBottom:6}}>
                <span style={{fontSize:18,lineHeight:1}}>{it.emoji}</span>
                <span style={{fontSize:13,fontWeight:700,color:c.text}}>{it.title}</span>
              </div>
              <p style={{margin:0,fontSize:13,color:"var(--grey-800)",lineHeight:1.5}}>{it.msg}</p>
            </div>
          );
        })}
      </div>
    </section>
  );
}

// ─── v3.8 어드민 보강 모듈 5건 (기획서 §8 Phase A) ────────────────

// A1. 알림 센터 — 우상단 종 아이콘 + 드롭다운
function NotificationCenter() {
  const [open, setOpen] = React.useState(false);
  // 알림 자동 수집 (mock + 향후 Supabase 통합 지점)
  const items = [];
  const bikes = window.CATALOG?.bikes || [];
  const lowStock = bikes.filter(b => b.stock > 0 && b.stock <= 1).length;
  const outStock = bikes.filter(b => b.stock === 0).length;
  if (outStock > 0) items.push({ tone:"red", icon:"📦", title:`품절 ${outStock}건`, msg:"즉시 발주 검토 필요", href:"재고관리.html" });
  if (lowStock > 0) items.push({ tone:"yellow", icon:"⚠️", title:`저재고 ${lowStock}건`, msg:"안전 재고 미달", href:"재고관리.html" });

  // v3.11: 직원 가입 승인 대기 알림 (owner 전용)
  if ((window.AUTH?.role || "owner") === "owner") {
    try {
      const raw = localStorage.getItem("sejong-pending-signups");
      const queue = raw ? JSON.parse(raw) : [];
      if (queue.length > 0) {
        items.unshift({
          tone:"red", icon:"⏳",
          title:`직원 가입 승인 대기 ${queue.length}건`,
          msg:"직원관리 페이지에서 승인 또는 거절",
          href:"직원관리.html",
        });
      }
    } catch {}
  }

  const lastBackup = localStorage.getItem("sejong-last-backup");
  if (!lastBackup || (Date.now() - Number(lastBackup)) > 7 * 86400_000) {
    items.push({ tone:"yellow", icon:"💾", title:"백업 권고", msg:"7일 이상 백업 없음", href:"#csv" });
  }
  items.push({ tone:"blue", icon:"🧾", title:"새 견적 답변 대기", msg:"Q-260524-204 (1일째)", href:"견적서목록.html" });

  return (
    <div style={{position:"relative",display:"inline-block"}}>
      <button onClick={()=>setOpen(!open)}
        style={{position:"relative",background:"transparent",border:"1px solid var(--grey-200,#E5E8EB)",
                borderRadius:8,padding:"6px 10px",cursor:"pointer",fontSize:16,height:36}}>
        🔔
        {items.length > 0 && (
          <span style={{position:"absolute",top:-4,right:-4,background:"var(--red-500,#E31E26)",
                       color:"#fff",fontSize:10,fontWeight:700,minWidth:18,height:18,
                       borderRadius:9,padding:"0 4px",display:"flex",alignItems:"center",justifyContent:"center"}}>
            {items.length}
          </span>
        )}
      </button>
      {open && (
        <div style={{position:"absolute",top:42,right:0,width:320,background:"#fff",
                     border:"1px solid var(--grey-200,#E5E8EB)",borderRadius:12,
                     boxShadow:"0 8px 24px rgba(0,0,0,0.12)",zIndex:100,overflow:"hidden"}}>
          <div style={{padding:"12px 16px",borderBottom:"1px solid var(--grey-100,#F2F4F6)",
                       display:"flex",justifyContent:"space-between",alignItems:"center"}}>
            <b style={{fontSize:14}}>알림 {items.length}건</b>
            <button onClick={()=>setOpen(false)} style={{background:"none",border:"none",cursor:"pointer",color:"var(--grey-500)"}}>✕</button>
          </div>
          <div style={{maxHeight:360,overflowY:"auto"}}>
            {items.length === 0
              ? <div style={{padding:24,textAlign:"center",color:"var(--grey-500)",fontSize:14}}>알림 없음 ✨</div>
              : items.map((it,i)=>(
                  <a key={i} href={it.href} style={{display:"flex",gap:10,padding:"12px 16px",
                      borderBottom:"1px solid var(--grey-50,#F9FAFB)",textDecoration:"none",color:"inherit",
                      transition:"background .15s"}}
                     onMouseEnter={e=>e.currentTarget.style.background="var(--grey-50)"}
                     onMouseLeave={e=>e.currentTarget.style.background=""}>
                    <span style={{fontSize:20,lineHeight:1}}>{it.icon}</span>
                    <span style={{flex:1,minWidth:0}}>
                      <span style={{display:"block",fontSize:13,fontWeight:600,color:"var(--grey-900)"}}>{it.title}</span>
                      <span style={{display:"block",fontSize:12,color:"var(--grey-600)",marginTop:2}}>{it.msg}</span>
                    </span>
                  </a>
                ))}
          </div>
        </div>
      )}
    </div>
  );
}

// A2. 오늘의 운영 메모 — localStorage 자동 저장
function DailyNote() {
  const today = new Date().toISOString().slice(0,10);
  const key = "sejong-daily-note-" + today;
  const [note, setNote] = React.useState(() => localStorage.getItem(key) || "");
  const [saved, setSaved] = React.useState(false);
  const save = (v) => {
    setNote(v);
    localStorage.setItem(key, v);
    setSaved(true);
    setTimeout(()=>setSaved(false), 1500);
  };
  return (
    <section style={{marginBottom:20,padding:"14px 16px",background:"#FFFEF7",
                     border:"1px dashed #F5C97A",borderRadius:10}}>
      <div style={{display:"flex",alignItems:"center",justifyContent:"space-between",marginBottom:6}}>
        <b style={{fontSize:13,color:"var(--grey-700)"}}>📝 오늘의 운영 메모 <span style={{color:"var(--grey-500)",fontWeight:400,marginLeft:6}}>{today}</span></b>
        {saved && <span style={{fontSize:11,color:"var(--green-500,#22C55E)"}}>✓ 저장됨</span>}
      </div>
      <textarea value={note} onChange={e=>save(e.target.value)}
        placeholder="예) 13시 김ㅇㅇ 시승 / 한지영 휴무 / Tarmac SL8 본사 발주 예정"
        style={{width:"100%",minHeight:48,border:"none",background:"transparent",
                resize:"vertical",font:"14px/1.5 var(--font-sans)",color:"var(--grey-800)",outline:"none"}}/>
    </section>
  );
}

// A4. 역할별 view 토글 — localStorage 저장
function RoleViewToggle({ value, onChange }) {
  const VIEWS = [
    { id:"owner",    label:"사장님 뷰", emoji:"👑" },
    { id:"mechanic", label:"정비 뷰",   emoji:"🔧" },
    { id:"manager",  label:"매니저 뷰", emoji:"📦" },
  ];
  return (
    <div style={{display:"inline-flex",background:"var(--grey-100,#F2F4F6)",padding:4,borderRadius:10,gap:0}}>
      {VIEWS.map(v => (
        <button key={v.id} onClick={()=>{ onChange(v.id); localStorage.setItem("sejong-role-view", v.id); }}
          style={{padding:"6px 12px",border:0,borderRadius:8,
                  background: value === v.id ? "#fff" : "transparent",
                  color: value === v.id ? "var(--grey-900)" : "var(--grey-600)",
                  fontWeight: value === v.id ? 600 : 500,
                  fontSize:13,cursor:"pointer",
                  boxShadow: value === v.id ? "0 1px 3px rgba(0,0,0,0.08)" : "none",
                  transition:"all .15s"}}>
          <span style={{marginRight:4}}>{v.emoji}</span>{v.label}
        </button>
      ))}
    </div>
  );
}

// A5. 백업 상태 표시 — localStorage 기반
function BackupStatus() {
  const last = Number(localStorage.getItem("sejong-last-backup") || 0);
  const daysSince = last ? Math.floor((Date.now() - last) / 86400_000) : null;
  const tone = !last || daysSince > 7 ? "red" : daysSince > 3 ? "yellow" : "green";
  const color = tone === "red" ? "var(--red-500)" : tone === "yellow" ? "#DD7D02" : "var(--green-500,#029359)";
  const label = !last ? "백업 기록 없음" : daysSince === 0 ? "오늘 백업 ✓" : `${daysSince}일 전 백업`;
  return (
    <span style={{fontSize:12,color,display:"inline-flex",alignItems:"center",gap:4}}>
      <span style={{width:8,height:8,borderRadius:4,background:color,display:"inline-block"}} />
      💾 {label}
    </span>
  );
}

// C7: CSV 추출 모달 (디렉터 권고 P1, v1.0 최종크리틱)
// ─────────────────────────────────────────────────────────────
// 매출/재고/견적 CSV 일괄 다운로드. owner 전용. 기간 필터 + 시트 선택.
function ExportModal({ open, onClose }) {
  const [from, setFrom] = React.useState(() => {
    const d = new Date(); d.setDate(1); // 이번달 1일
    return d.toISOString().slice(0,10);
  });
  const [to, setTo] = React.useState(() => new Date().toISOString().slice(0,10));
  const [picks, setPicks] = React.useState({ sales:true, inventory:true, quotes:true });
  const [busy, setBusy] = React.useState(false);
  const [log, setLog] = React.useState([]);

  if (!open) return null;

  // CSV 직렬화 (Excel 호환 BOM + ko-KR 포맷)
  const toCsv = (rows) => {
    if (!rows || rows.length === 0) return "";
    const cols = Object.keys(rows[0]);
    const esc = (v) => {
      if (v === null || v === undefined) return "";
      const s = String(v).replace(/"/g, '""');
      return /[",\n]/.test(s) ? `"${s}"` : s;
    };
    return [cols.join(","), ...rows.map(r => cols.map(c => esc(r[c])).join(","))].join("\n");
  };

  const downloadCsv = (name, csv) => {
    const blob = new Blob(["﻿" + csv], { type:"text/csv;charset=utf-8" });
    const url = URL.createObjectURL(blob);
    const a = document.createElement("a");
    a.href = url; a.download = name;
    document.body.appendChild(a); a.click(); a.remove();
    setTimeout(() => URL.revokeObjectURL(url), 1000);
  };

  const handleExport = async () => {
    setBusy(true); setLog([]);
    const stamp = new Date().toISOString().slice(0,10).replaceAll("-","");
    const append = (msg) => setLog(prev => [...prev, msg]);

    try {
      // 1) 매출 — DAL 또는 mock
      if (picks.sales) {
        let rows = [];
        try {
          if (window.DAL?.listSalesRange) {
            rows = await window.DAL.listSalesRange(from, to);
          } else {
            const orders = (window.ADMIN?.recentOrders) || [];
            rows = orders.map(o => ({
              날짜: o.date, 주문번호: o.id, 고객: o.customer,
              모델: o.bike, 금액: o.amount, 담당: o.staff, 상태: o.status,
            }));
          }
        } catch (e) { append(`⚠ 매출: ${e.message}`); }
        if (rows.length === 0) {
          append("ℹ 매출: 데이터 없음 (헤더만 다운로드)");
          rows = [{ 날짜:"", 주문번호:"", 고객:"", 모델:"", 금액:"", 담당:"", 상태:"" }];
        }
        downloadCsv(`매출_${from}~${to}_${stamp}.csv`, toCsv(rows));
        append(`✓ 매출 ${rows.length}건 추출`);
      }

      // 2) 재고
      if (picks.inventory) {
        let rows = [];
        try {
          const bikes = (window.CATALOG?.bikes) || [];
          rows = bikes.map(b => ({
            SKU: b.sku || b.base_sku, 모델: b.name, 카테고리: b.category,
            가격: b.price, 재고: b.stock ?? 0, 상태: b.status || "active",
          }));
        } catch (e) { append(`⚠ 재고: ${e.message}`); }
        if (rows.length === 0) rows = [{ SKU:"", 모델:"", 카테고리:"", 가격:"", 재고:"", 상태:"" }];
        downloadCsv(`재고_${stamp}.csv`, toCsv(rows));
        append(`✓ 재고 ${rows.length}건 추출`);
      }

      // 3) 견적
      if (picks.quotes) {
        let rows = [];
        try {
          if (window.DAL?.listQuotes) {
            const all = await window.DAL.listQuotes();
            rows = all.filter(q => q.created_at >= from && q.created_at <= to + "T23:59:59")
              .map(q => ({
                일시: q.created_at, 견적번호: q.id, 고객: q.customer_name,
                금액: q.total, 상태: q.status, 담당: q.staff_name || "",
              }));
          } else if (window.QUOTES) {
            rows = window.QUOTES.map(q => ({
              일시: q.created_at, 견적번호: q.id, 고객: q.customer_name,
              금액: q.total, 상태: q.status, 담당: q.staff_name || "",
            }));
          }
        } catch (e) { append(`⚠ 견적: ${e.message}`); }
        if (rows.length === 0) rows = [{ 일시:"", 견적번호:"", 고객:"", 금액:"", 상태:"", 담당:"" }];
        downloadCsv(`견적_${from}~${to}_${stamp}.csv`, toCsv(rows));
        append(`✓ 견적 ${rows.length}건 추출`);
      }

      append("📥 모든 파일 다운로드 폴더 확인");
    } finally {
      setBusy(false);
    }
  };

  return (
    <div style={{
      position:"fixed",inset:0,background:"rgba(0,0,0,0.45)",
      display:"flex",alignItems:"center",justifyContent:"center",zIndex:9999,
      padding:16,
    }} onClick={onClose}>
      <div onClick={e=>e.stopPropagation()} style={{
        background:"#fff",borderRadius:16,maxWidth:520,width:"100%",
        padding:24,boxShadow:"0 20px 60px rgba(0,0,0,0.2)",
      }}>
        <div style={{display:"flex",alignItems:"center",justifyContent:"space-between",marginBottom:8}}>
          <h2 style={{margin:0,font:"700 20px/1.3 var(--font-sans)"}}>📥 CSV 일괄 추출</h2>
          <button onClick={onClose} style={{
            background:"none",border:"none",fontSize:24,cursor:"pointer",color:"var(--grey-500,#8B95A1)",
          }}>×</button>
        </div>
        <p style={{margin:"0 0 16px",fontSize:13,color:"var(--grey-600,#6B7684)"}}>
          월말 회계 · 본사 보고용. Excel에서 바로 열리는 BOM 포함 UTF-8 CSV로 저장됩니다.
        </p>

        <div style={{display:"grid",gridTemplateColumns:"1fr 1fr",gap:12,marginBottom:16}}>
          <label style={{display:"flex",flexDirection:"column",gap:4,fontSize:13}}>
            <span style={{color:"var(--grey-700,#4E5968)",fontWeight:500}}>시작일</span>
            <input type="date" value={from} onChange={e=>setFrom(e.target.value)}
              style={{padding:"8px 10px",border:"1px solid var(--grey-300,#D1D6DB)",borderRadius:8,fontSize:14}} />
          </label>
          <label style={{display:"flex",flexDirection:"column",gap:4,fontSize:13}}>
            <span style={{color:"var(--grey-700,#4E5968)",fontWeight:500}}>종료일</span>
            <input type="date" value={to} onChange={e=>setTo(e.target.value)}
              style={{padding:"8px 10px",border:"1px solid var(--grey-300,#D1D6DB)",borderRadius:8,fontSize:14}} />
          </label>
        </div>

        <div style={{marginBottom:16}}>
          <div style={{fontSize:13,color:"var(--grey-700,#4E5968)",fontWeight:500,marginBottom:8}}>추출 시트</div>
          <div style={{display:"flex",gap:8,flexWrap:"wrap"}}>
            {[
              {key:"sales", label:"매출 (기간)", emoji:"💰"},
              {key:"inventory", label:"재고 (현재)", emoji:"📦"},
              {key:"quotes", label:"견적 (기간)", emoji:"🧾"},
            ].map(s => (
              <label key={s.key} style={{
                display:"flex",alignItems:"center",gap:6,padding:"8px 12px",
                border:"1px solid "+(picks[s.key]?"var(--red-500,#E31E26)":"var(--grey-300,#D1D6DB)"),
                borderRadius:8,cursor:"pointer",fontSize:13,
                background: picks[s.key] ? "var(--red-50,#FFE5E6)" : "#fff",
              }}>
                <input type="checkbox" checked={picks[s.key]}
                  onChange={e=>setPicks({...picks,[s.key]:e.target.checked})} />
                <span>{s.emoji} {s.label}</span>
              </label>
            ))}
          </div>
        </div>

        {log.length > 0 && (
          <div style={{
            background:"var(--grey-50,#F9FAFB)",border:"1px solid var(--grey-200,#E5E8EB)",
            borderRadius:8,padding:"10px 12px",marginBottom:16,
            fontSize:12,fontFamily:"monospace",maxHeight:120,overflowY:"auto",
          }}>
            {log.map((m,i) => <div key={i}>{m}</div>)}
          </div>
        )}

        <div style={{display:"flex",gap:8,justifyContent:"flex-end"}}>
          <button onClick={onClose} disabled={busy} style={{
            padding:"10px 18px",border:"1px solid var(--grey-300,#D1D6DB)",
            borderRadius:8,background:"#fff",cursor:"pointer",fontSize:14,
          }}>닫기</button>
          <button onClick={handleExport} disabled={busy || !Object.values(picks).some(Boolean)} style={{
            padding:"10px 18px",border:"none",borderRadius:8,
            background:"var(--red-500,#E31E26)",color:"#fff",cursor: busy?"wait":"pointer",
            fontSize:14,fontWeight:600,opacity:busy?0.6:1,
          }}>{busy ? "추출 중..." : "📥 다운로드"}</button>
        </div>
      </div>
    </div>
  );
}

// U-B1: 어드민 퀵 액션 패널 (v3.7 UX기획서 v2.0 + RBAC v1.1 권한 게이팅)
function QuickActions() {
  // RBAC v1.1: can(key)로 게이팅. AUTH.can이 없으면 hasRole 폴백 (호환 레이어).
  const can = (key) => window.AUTH?.can?.(key) ?? (window.AUTH?.role === "owner");
  const [showExport, setShowExport] = React.useState(false);
  const cardStyle = {
    display: "flex", flexDirection: "column", alignItems: "flex-start", gap: 4,
    padding: "14px 16px", background: "#fff",
    border: "1px solid var(--grey-200,#E5E8EB)",
    borderRadius: 12, cursor: "pointer", flex: 1, minWidth: 0,
    transition: "transform .15s, box-shadow .15s, border-color .15s",
    textAlign: "left",
  };
  const items = [
    can("catalog.create")    && { emoji:"🆕", title:"신상 등록", sub:"새 자전거 추가", onClick:()=>location.href="재고관리.html#new-bike" },
    can("inventory.in")      && { emoji:"📦", title:"입고 등록", sub:"빠른 재고 처리", onClick:()=>location.href="재고관리.html#quick-in" },
    can("quote.create")      && { emoji:"🧾", title:"견적 발행", sub:"새 견적서 작성", onClick:()=>location.href="서비스 견적서.html" },
    can("sales.export")      && { emoji:"📥", title:"CSV 추출", sub:"매출·재고·견적", onClick:()=>setShowExport(true) },
    can("activity.view_all") && { emoji:"📊", title:"활동 로그", sub:"직원별 작업 내역", onClick:()=>location.href="활동로그.html" },
    can("staff.view")        && { emoji:"👥", title:"직원 관리", sub:"권한·비밀번호", onClick:()=>location.href="직원관리.html" },
    can("settings.role_edit")&& { emoji:"🔐", title:"권한 설정", sub:"역할·권한 편집", onClick:()=>location.href="권한설정.html" },
    can("settings.edit")     && { emoji:"🏪", title:"매장 정보", sub:"등기·영업시간", onClick:()=>location.href="매장설정.html" },
  ].filter(Boolean);

  return (
    <React.Fragment>
      <section style={{marginBottom: 20}}>
        <div style={{display:"flex",alignItems:"center",gap:8,marginBottom:10}}>
          <span style={{fontSize:14,fontWeight:600,color:"var(--grey-700,#4E5968)"}}>⚡ 퀵 액션</span>
          <span style={{fontSize:12,color:"var(--grey-500,#8B95A1)"}}>· 자주 쓰는 작업을 한 번에</span>
        </div>
        <div style={{display:"grid",gridTemplateColumns:`repeat(${items.length},1fr)`,gap:8}}>
          {items.map((it, i) => (
            <button key={i} style={cardStyle} onClick={it.onClick}
              onMouseEnter={e=>{e.currentTarget.style.transform="translateY(-2px)";e.currentTarget.style.boxShadow="0 4px 12px rgba(0,0,0,0.06)";e.currentTarget.style.borderColor="var(--grey-300,#D1D6DB)";}}
              onMouseLeave={e=>{e.currentTarget.style.transform="";e.currentTarget.style.boxShadow="";e.currentTarget.style.borderColor="var(--grey-200,#E5E8EB)";}}>
              <span style={{fontSize:24,lineHeight:1}}>{it.emoji}</span>
              <span style={{fontSize:14,fontWeight:600,color:"var(--grey-900,#191F28)",marginTop:6}}>{it.title}</span>
              <span style={{fontSize:12,color:"var(--grey-500,#8B95A1)"}}>{it.sub}</span>
            </button>
          ))}
        </div>
      </section>
      <ExportModal open={showExport} onClose={()=>setShowExport(false)} />
    </React.Fragment>
  );
}

// U-B3: 빈 DB 환영 마법사 (카탈로그가 0건일 때만 노출)
function WelcomeWizard() {
  const isEmpty = (window.CATALOG?.bikes || []).length === 0;
  if (!isEmpty) return null;
  return (
    <section style={{
      padding:"32px",margin:"0 0 20px",
      background:"linear-gradient(135deg, var(--red-50,#FFE5E6), #fff)",
      border:"1px solid var(--red-200,#FEAFB4)", borderRadius:16,
      display:"flex",flexDirection:"column",gap:12,alignItems:"flex-start"
    }}>
      <div style={{fontSize:14,fontWeight:600,color:"var(--red-700,#9A0E14)"}}>🎉 환영합니다 — 첫 운영 시작</div>
      <h2 style={{margin:0,font:"700 24px/1.3 var(--font-sans)",letterSpacing:"-0.02em"}}>카탈로그가 비어있어요</h2>
      <p style={{margin:0,color:"var(--grey-700,#4E5968)",fontSize:15,lineHeight:1.6}}>
        첫 모델을 등록하면 카탈로그/재고/견적서가 살아납니다.<br/>
        한 건씩 정성껏 등록하거나, 본사 카탈로그 CSV로 한 번에 일괄 등록할 수 있어요.
      </p>
      <div style={{display:"flex",gap:8,marginTop:8,flexWrap:"wrap"}}>
        <button className="cat-print-btn"
          style={{background:"var(--red-500,#E31E26)",color:"#fff",borderColor:"var(--red-500,#E31E26)",padding:"10px 20px"}}
          onClick={()=>location.href="재고관리.html#new-bike"}>
          🆕 한 건씩 등록 (5분/건)
        </button>
        <button className="cat-print-btn" style={{padding:"10px 20px"}}
          onClick={()=>alert("CSV 일괄 등록 — v1.3 출시 예정")}>
          📋 CSV 일괄 등록
        </button>
        <button className="cat-print-btn" style={{padding:"10px 20px",background:"transparent",border:"none",color:"var(--grey-600,#6B7684)"}}
          onClick={()=>{ document.querySelector("section[data-welcome]")?.remove(); }}>
          나중에
        </button>
      </div>
    </section>
  );
}

function App() {
  // 역할별 view 상태 (localStorage 영속)
  const [roleView, setRoleView] = React.useState(() =>
    localStorage.getItem("sejong-role-view") || (window.AUTH?.role || "owner")
  );

  return (
    <div className="cat-app">
      <Header />
      <PageNav />
      <main className="adm-main">
        <div className="cat-inner">
          <div className="adm-page-head">
            <div>
              <h1>관리자 대시보드</h1>
              <p>매장 매출 · 견적 · 정비 · 재고를 한 화면에서. 데이터는 다른 페이지와 실시간 동기화됩니다.</p>
              <div style={{marginTop:10,display:"flex",alignItems:"center",gap:12,flexWrap:"wrap"}}>
                {/* A4: 역할별 view 토글 */}
                <RoleViewToggle value={roleView} onChange={setRoleView} />
                {/* A5: 백업 상태 */}
                <BackupStatus />
              </div>
            </div>
            <div className="adm-page-head-tools">
              <span>현재 <b>{window.AUTH?.user?.email || A.meta.staff}</b> 로그인</span>
              {/* A1: 알림 센터 */}
              <NotificationCenter />
              <button className="cat-print-btn" onClick={() => window.print()} style={{height:36}}>
                일일 보고서
              </button>
              {/* v3.17: 로그아웃 (전역 헤더) */}
              <button
                className="cat-print-btn"
                onClick={async () => {
                  if (!confirm("로그아웃하시겠습니까?")) return;
                  try { await window.DAL?.signOut?.(); } catch {}
                  try { localStorage.removeItem("sejong-bike-auth"); } catch {}
                  location.href = "로그인.html";
                }}
                title="로그아웃"
                style={{height:36, background:"var(--grey-50,#F9FAFB)", color:"var(--grey-700,#4E5968)", border:"1px solid var(--grey-300,#D1D6DB)"}}>
                🚪 로그아웃
              </button>
            </div>
          </div>

          {/* A2: 오늘의 운영 메모 (owner만 노출) */}
          {roleView === "owner" && <DailyNote />}

          {/* U-B3: 빈 DB 환영 마법사 (빈 DB일 때만) */}
          <div data-welcome><WelcomeWizard /></div>

          {/* U-B1: 어드민 퀵 액션 패널 */}
          <QuickActions />

          {/* A3: 메인 KPI 큰 카드 + 작은 3개 (KpiRow 대체) */}
          <MainKpiHero />

          {/* B1: 목표 진척률 (owner만) */}
          <GoalsBoard />

          {/* B2: CS 응대 큐 */}
          <CustomerQueue />

          {/* C1: 스마트 인사이트 (owner/mechanic 모두) */}
          <SmartInsights />

          <LocationOverview />

          <QuickLinks />

          <div className="adm-grid" style={{marginTop:20}}>
            <div className="adm-col">
              <SalesChart />
              <QuotesTable />
              <ServiceTable />
            </div>
            <div className="adm-col">
              <Tasks />
              <CategoryBreakdown />
              <Bestsellers />
              <ActivityFeed />
            </div>
          </div>
        </div>
      </main>
      <Footer />
    </div>
  );
}

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