// Admin Dashboard — analytics + map view
const { useState: useState_ad, useMemo: useMemo_ad, useEffect: useEffect_ad } = React;

// ─── Donut chart (competitor share) ───────────────────────────────────
function Donut({ data, size = 200, thickness = 22 }) {
  const cx = size / 2, cy = size / 2;
  const r = (size - thickness) / 2;
  const total = data.reduce((s, d) => s + d.share, 0);
  let acc = 0;
  const segs = data.map((d) => {
    const start = (acc / total) * Math.PI * 2 - Math.PI / 2;
    acc += d.share;
    const end = (acc / total) * Math.PI * 2 - Math.PI / 2;
    const large = end - start > Math.PI ? 1 : 0;
    const x1 = cx + r * Math.cos(start), y1 = cy + r * Math.sin(start);
    const x2 = cx + r * Math.cos(end),   y2 = cy + r * Math.sin(end);
    return { d: `M ${x1} ${y1} A ${r} ${r} 0 ${large} 1 ${x2} ${y2}`, color: d.color, share: d.share };
  });
  return (
    <svg width={size} height={size} viewBox={`0 0 ${size} ${size}`}>
      {segs.map((s, i) => (
        <path key={i} d={s.d} fill="none" stroke={s.color} strokeWidth={thickness} strokeLinecap="butt" />
      ))}
    </svg>
  );
}

// ─── Trend line chart ─────────────────────────────────────────────────
function TrendChart({ data, width = 580, height = 220 }) {
  const pad = { l: 32, r: 16, t: 16, b: 28 };
  const w = width - pad.l - pad.r, h = height - pad.t - pad.b;
  const xs = data.map((_, i) => pad.l + (i / (data.length - 1)) * w);
  const allVals = data.flatMap((d) => [d.us, d.c1, d.c2]);
  const yMax = Math.ceil(Math.max(...allVals) / 5) * 5 + 5;
  const yMin = Math.max(0, Math.floor(Math.min(...allVals) / 5) * 5 - 5);
  const yScale = (v) => pad.t + h - ((v - yMin) / (yMax - yMin)) * h;

  const series = [
    { key: 'us', label: 'Kopi Kenanga', color: 'var(--primary)', dot: true },
    { key: 'c1', label: 'Janji Manis',  color: 'oklch(0.68 0.16 25)' },
    { key: 'c2', label: 'Tomoro',       color: 'oklch(0.62 0.14 145)' },
  ];

  const path = (key) => data.map((d, i) => `${i === 0 ? 'M' : 'L'} ${xs[i]} ${yScale(d[key])}`).join(' ');
  const area = data.map((d, i) => `${i === 0 ? 'M' : 'L'} ${xs[i]} ${yScale(d.us)}`).join(' ') +
               ` L ${xs[xs.length - 1]} ${pad.t + h} L ${xs[0]} ${pad.t + h} Z`;

  const yTicks = 4;
  return (
    <svg width="100%" viewBox={`0 0 ${width} ${height}`} style={{ display: 'block' }}>
      <defs>
        <linearGradient id="areaGrad" x1="0" y1="0" x2="0" y2="1">
          <stop offset="0%"  stopColor="var(--primary)" stopOpacity="0.18" />
          <stop offset="100%" stopColor="var(--primary)" stopOpacity="0" />
        </linearGradient>
      </defs>
      {/* y gridlines */}
      {Array.from({ length: yTicks + 1 }).map((_, i) => {
        const y = pad.t + (i / yTicks) * h;
        const v = yMax - (i / yTicks) * (yMax - yMin);
        return (
          <g key={i}>
            <line x1={pad.l} y1={y} x2={width - pad.r} y2={y} stroke="var(--line)" strokeDasharray="2 4" />
            <text x={8} y={y + 4} fontSize="10.5" fill="var(--ink-3)" fontFamily="var(--font-mono)">{v.toFixed(0)}</text>
          </g>
        );
      })}
      {/* x labels */}
      {data.map((d, i) => (
        <text key={i} x={xs[i]} y={height - 8} fontSize="11" fill="var(--ink-3)" textAnchor="middle">{d.m}</text>
      ))}
      {/* area */}
      <path d={area} fill="url(#areaGrad)" />
      {/* lines */}
      {series.map((s) => (
        <path key={s.key} d={path(s.key)} fill="none" stroke={s.color} strokeWidth={s.key === 'us' ? 2.4 : 1.6}
              strokeDasharray={s.key === 'us' ? '0' : '4 3'} strokeLinecap="round" strokeLinejoin="round" />
      ))}
      {/* dots on us */}
      {data.map((d, i) => (
        <circle key={i} cx={xs[i]} cy={yScale(d.us)} r={i === data.length - 1 ? 5 : 3}
                fill={i === data.length - 1 ? 'var(--primary)' : 'white'} stroke="var(--primary)" strokeWidth="2" />
      ))}
    </svg>
  );
}

// ─── Live response-count chart ────────────────────────────────────────
// Used when real trend data is available (replaces the demo TrendChart).
// Single series: response count per bucket. Bucket labels come from the API.
function ResponseTrendChart({ points, width = 580, height = 220 }) {
  const pad = { l: 36, r: 16, t: 16, b: 28 };
  const w = width - pad.l - pad.r, h = height - pad.t - pad.b;
  if (points.length === 0) {
    return <div style={{ padding: 40, textAlign: 'center', color: 'var(--ink-3)', fontSize: 13 }}>Belum ada respons.</div>;
  }
  // Pad single point so we can still draw a sensible line
  const data = points.length === 1
    ? [{ ...points[0], bucket: '' }, points[0]]
    : points;

  const xs = data.map((_, i) => pad.l + (i / Math.max(1, data.length - 1)) * w);
  const yMax = Math.max(1, Math.ceil(Math.max(...data.map((d) => d.n)) * 1.15));
  const yScale = (v) => pad.t + h - (v / yMax) * h;
  const linePath = data.map((d, i) => `${i === 0 ? 'M' : 'L'} ${xs[i]} ${yScale(d.n)}`).join(' ');
  const areaPath = linePath + ` L ${xs[xs.length - 1]} ${pad.t + h} L ${xs[0]} ${pad.t + h} Z`;

  return (
    <svg width="100%" viewBox={`0 0 ${width} ${height}`} style={{ display: 'block' }}>
      <defs>
        <linearGradient id="respAreaGrad" x1="0" y1="0" x2="0" y2="1">
          <stop offset="0%" stopColor="var(--primary)" stopOpacity="0.22" />
          <stop offset="100%" stopColor="var(--primary)" stopOpacity="0" />
        </linearGradient>
      </defs>
      {[0, 0.25, 0.5, 0.75, 1].map((t, i) => {
        const y = pad.t + t * h;
        const v = Math.round(yMax * (1 - t));
        return (
          <g key={i}>
            <line x1={pad.l} y1={y} x2={width - pad.r} y2={y} stroke="var(--line)" strokeDasharray="2 4" />
            <text x={8} y={y + 4} fontSize="10.5" fill="var(--ink-3)" fontFamily="var(--font-mono)">{v}</text>
          </g>
        );
      })}
      {data.map((d, i) => (
        <text key={i} x={xs[i]} y={height - 8} fontSize="11" fill="var(--ink-3)" textAnchor="middle">
          {String(d.bucket || '').slice(-7)}
        </text>
      ))}
      <path d={areaPath} fill="url(#respAreaGrad)" />
      <path d={linePath} fill="none" stroke="var(--primary)" strokeWidth="2.4" strokeLinecap="round" />
      {data.map((d, i) => (
        <circle key={i} cx={xs[i]} cy={yScale(d.n)} r={i === data.length - 1 ? 5 : 3.5}
                fill={i === data.length - 1 ? 'var(--primary)' : 'white'} stroke="var(--primary)" strokeWidth="2" />
      ))}
    </svg>
  );
}

// ─── Stylized Surabaya map ────────────────────────────────────────────
function SurabayaMap({ regions, hovered, setHovered }) {
  // shape approximating Surabaya outline (very stylized)
  const outline = "M 20 30 Q 18 22 26 18 L 40 14 Q 50 12 62 14 L 78 18 Q 86 22 88 30 L 86 42 Q 90 50 88 60 L 84 70 Q 82 78 76 80 L 60 82 Q 50 84 40 82 L 24 80 Q 16 76 18 68 L 22 56 Q 18 50 20 42 Z";
  const max = Math.max(...regions.map((r) => r.n));
  return (
    <div style={{ position: 'relative', width: '100%', aspectRatio: '1 / 0.95' }}>
      <svg viewBox="0 0 100 95" width="100%" height="100%" style={{ display: 'block' }}>
        <defs>
          <pattern id="dots" width="3" height="3" patternUnits="userSpaceOnUse">
            <circle cx="1" cy="1" r="0.4" fill="oklch(0.88 0.01 265)" />
          </pattern>
          <radialGradient id="hot">
            <stop offset="0%" stopColor="oklch(0.52 0.18 265 / 0.55)" />
            <stop offset="60%" stopColor="oklch(0.52 0.18 265 / 0.18)" />
            <stop offset="100%" stopColor="oklch(0.52 0.18 265 / 0.00)" />
          </radialGradient>
        </defs>
        <path d={outline} fill="url(#dots)" stroke="oklch(0.78 0.02 265)" strokeWidth="0.4" />
        <path d={outline} fill="oklch(0.52 0.18 265 / 0.05)" />

        {/* heatmap blobs */}
        {regions.map((r, i) => (
          <circle key={'h'+i} cx={r.x} cy={r.y} r={4 + (r.n / max) * 8} fill="url(#hot)" />
        ))}

        {/* response markers */}
        {regions.map((r, i) => {
          const radius = 1.5 + (r.n / max) * 2.8;
          const isHot = hovered === r.name;
          return (
            <g key={i} onMouseEnter={() => setHovered(r.name)} onMouseLeave={() => setHovered(null)} style={{ cursor: 'pointer' }}>
              <circle cx={r.x} cy={r.y} r={radius + 1.5} fill="white" />
              <circle cx={r.x} cy={r.y} r={radius} fill="var(--primary)"
                      style={{ filter: isHot ? 'brightness(1.2)' : 'none', transition: 'all 160ms' }} />
              {isHot && <circle cx={r.x} cy={r.y} r={radius + 3} fill="none" stroke="var(--primary)" strokeWidth="0.4" opacity="0.6" />}
              <text x={r.x} y={r.y - radius - 1.6} fontSize="2.4" fill="var(--ink-2)" textAnchor="middle" fontWeight="500">
                {r.name}
              </text>
            </g>
          );
        })}
      </svg>

      {/* Hover tooltip */}
      {hovered && (() => {
        const r = regions.find((x) => x.name === hovered);
        return (
          <div style={{
            position: 'absolute', left: `${r.x}%`, top: `${r.y}%`,
            transform: 'translate(-50%, -130%)',
            background: 'var(--surface)', border: '1px solid var(--line)',
            borderRadius: 10, padding: '8px 10px', minWidth: 130,
            boxShadow: 'var(--shadow-md)', pointerEvents: 'none',
            fontSize: 12,
          }}>
            <div style={{ fontWeight: 600, color: 'var(--ink)' }}>{r.name}</div>
            <div style={{ display: 'flex', justifyContent: 'space-between', gap: 12, marginTop: 4, color: 'var(--ink-3)' }}>
              <span>Responden</span><span className="mono" style={{ color: 'var(--ink)' }}>{r.n}</span>
            </div>
            <div style={{ display: 'flex', justifyContent: 'space-between', gap: 12, color: 'var(--ink-3)' }}>
              <span>Brand share</span><span className="mono" style={{ color: 'var(--primary)' }}>{r.share}%</span>
            </div>
          </div>
        );
      })()}
    </div>
  );
}

// ─── Stat card ────────────────────────────────────────────────────────
function Stat({ label, value, delta, suffix, hint }) {
  const positive = delta >= 0;
  return (
    <div className="card" style={{ padding: 18 }}>
      <div style={{ fontSize: 12.5, color: 'var(--ink-3)', fontWeight: 500 }}>{label}</div>
      <div style={{ display: 'flex', alignItems: 'baseline', gap: 8, marginTop: 6 }}>
        <div className="mono tnum" style={{ fontSize: 28, fontWeight: 600, letterSpacing: '-0.02em' }}>
          {value}{suffix && <span style={{ fontSize: 16, color: 'var(--ink-3)', marginLeft: 2 }}>{suffix}</span>}
        </div>
        {delta != null && (
          <span className="mono" style={{
            fontSize: 12, padding: '2px 6px', borderRadius: 6,
            background: positive ? 'var(--success-soft)' : 'var(--danger-soft)',
            color: positive ? 'oklch(0.42 0.12 155)' : 'oklch(0.50 0.18 25)',
            display: 'inline-flex', alignItems: 'center', gap: 3,
          }}>
            {positive ? '↑' : '↓'} {Math.abs(delta).toFixed(1)}
          </span>
        )}
      </div>
      {hint && <div style={{ fontSize: 12, color: 'var(--ink-3)', marginTop: 4 }}>{hint}</div>}
    </div>
  );
}

// ─── Insight card ─────────────────────────────────────────────────────
function InsightCard({ insight }) {
  const tones = {
    positive: { bg: 'var(--success-soft)', fg: 'oklch(0.42 0.12 155)', icon: '↑' },
    warning:  { bg: 'oklch(0.96 0.05 75)', fg: 'oklch(0.96 0.05 75)', icon: '⚡' },
    info:     { bg: 'var(--primary-50)', fg: 'var(--primary)', icon: 'ⓘ' },
  };
  const t = tones[insight.tone];
  return (
    <div style={{
      padding: 14, borderRadius: 12, border: '1px solid var(--line)',
      background: 'var(--surface)', display: 'flex', gap: 12,
    }}>
      <div style={{
        width: 28, height: 28, borderRadius: 8, flexShrink: 0,
        background: t.bg, color: t.fg, display: 'grid', placeItems: 'center',
        fontWeight: 600, fontSize: 14,
      }}>{t.icon}</div>
      <div>
        <div style={{ fontSize: 13.5, fontWeight: 600, color: 'var(--ink)', lineHeight: 1.35 }}>
          {insight.title}
        </div>
        <div style={{ fontSize: 12.5, color: 'var(--ink-2)', marginTop: 4, lineHeight: 1.55 }}>
          {insight.body}
        </div>
      </div>
    </div>
  );
}

// ─── Sidebar ──────────────────────────────────────────────────────────
// Workspace switcher — dropdown styled like Linear / Notion. Renders a
// trigger button (avatar + name) at the top of the sidebar; clicking opens
// a popover with the workspace list, role badges, and a "Buat workspace baru"
// action. Click outside (or Esc) closes it.
function WorkspaceSwitcher({ workspaces, activeWorkspace, onSwitch, onCreated }) {
  const [open, setOpen] = React.useState(false);
  const [creating, setCreating] = React.useState(false);
  const [newName, setNewName] = React.useState('');
  const [busy, setBusy] = React.useState(false);
  const [error, setError] = React.useState(null);
  const ref = React.useRef(null);

  React.useEffect(() => {
    if (!open) return;
    const onDoc = (e) => { if (!ref.current?.contains(e.target)) setOpen(false); };
    const onKey = (e) => { if (e.key === 'Escape') setOpen(false); };
    document.addEventListener('mousedown', onDoc);
    document.addEventListener('keydown', onKey);
    return () => {
      document.removeEventListener('mousedown', onDoc);
      document.removeEventListener('keydown', onKey);
    };
  }, [open]);

  async function handleCreate(e) {
    e.preventDefault();
    if (!newName.trim()) return;
    setBusy(true); setError(null);
    try {
      const { workspace: ws } = await window.api.createWorkspace(newName.trim());
      setNewName(''); setCreating(false); setOpen(false);
      await onCreated?.(ws.id);
    } catch (err) {
      setError(err.message);
    } finally { setBusy(false); }
  }

  if (!activeWorkspace) return null;
  const memberCount = workspaces?.length ?? 1;

  return (
    <div ref={ref} style={{ position: 'relative', padding: '12px 12px 8px' }}>
      <button
        onClick={() => setOpen((v) => !v)}
        style={{
          width: '100%', display: 'flex', alignItems: 'center', gap: 10,
          padding: '8px 10px', border: '1px solid var(--line-2)',
          borderRadius: 10, background: open ? 'var(--bg-2)' : 'var(--surface)',
          cursor: 'pointer', textAlign: 'left',
        }}
      >
        <Avatar src={activeWorkspace.image} name={activeWorkspace.name} seed={activeWorkspace.id} size={32} radius={8} fontSize={12} />
        <div style={{ flex: 1, minWidth: 0 }}>
          <div style={{ fontSize: 13, fontWeight: 600, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>
            {activeWorkspace.name}
          </div>
          <div style={{ fontSize: 11, color: 'var(--ink-3)' }}>
            {memberCount} workspace · {activeWorkspace.plan}
          </div>
        </div>
        <I.ChevronDown size={14} style={{ color: 'var(--ink-3)', flexShrink: 0 }} />
      </button>

      {open && (
        <div style={{
          position: 'absolute', top: 'calc(100% + 4px)', left: 12, right: 12,
          background: 'var(--surface)', border: '1px solid var(--line)',
          borderRadius: 12, boxShadow: 'var(--shadow-lg)', zIndex: 20,
          padding: '8px 0', maxHeight: 360, overflow: 'auto',
        }} className="scroll">
          <div style={{
            padding: '8px 14px 4px', fontSize: 10.5, fontWeight: 700,
            color: 'var(--ink-4)', textTransform: 'uppercase', letterSpacing: '0.08em',
          }}>
            Workspaces
          </div>

          {workspaces.map((w) => {
            const isActive = w.id === activeWorkspace.id;
            return (
              <button
                key={w.id}
                onClick={() => { onSwitch?.(w.id); setOpen(false); }}
                style={{
                  width: '100%', display: 'flex', alignItems: 'center', gap: 10,
                  padding: '8px 14px', border: 'none', cursor: 'pointer',
                  background: isActive ? 'var(--primary-50)' : 'transparent',
                  textAlign: 'left',
                }}
              >
                <Avatar src={w.image} name={w.name} seed={w.id} size={26} radius={6} fontSize={11} />
                <div style={{ flex: 1, minWidth: 0 }}>
                  <div style={{
                    fontSize: 13, fontWeight: isActive ? 600 : 500,
                    color: isActive ? 'var(--primary)' : 'var(--ink)',
                    whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis',
                  }}>{w.name}</div>
                </div>
                <span style={{
                  fontSize: 11, color: 'var(--ink-3)',
                  textTransform: 'capitalize', flexShrink: 0,
                }}>{w.role}</span>
              </button>
            );
          })}

          <div style={{ height: 1, background: 'var(--line)', margin: '6px 0' }} />

          {creating ? (
            <form onSubmit={handleCreate} style={{ padding: '4px 14px 8px' }}>
              <input
                className="input"
                autoFocus
                value={newName}
                onChange={(e) => setNewName(e.target.value)}
                placeholder="Nama workspace…"
                style={{ fontSize: 13, padding: '8px 10px' }}
                disabled={busy}
              />
              {error && (
                <div style={{ fontSize: 11.5, color: 'var(--danger)', marginTop: 6 }}>{error}</div>
              )}
              <div style={{ display: 'flex', gap: 6, marginTop: 8 }}>
                <button type="submit" className="btn btn-primary" style={{ padding: '6px 10px', fontSize: 12 }} disabled={busy || !newName.trim()}>
                  {busy ? 'Membuat…' : 'Buat'}
                </button>
                <button type="button" className="btn btn-ghost" style={{ padding: '6px 10px', fontSize: 12 }}
                  onClick={() => { setCreating(false); setNewName(''); setError(null); }} disabled={busy}>
                  Batal
                </button>
              </div>
            </form>
          ) : (
            <button
              onClick={() => setCreating(true)}
              style={{
                width: '100%', display: 'flex', alignItems: 'center', gap: 8,
                padding: '8px 14px', border: 'none', cursor: 'pointer',
                background: 'transparent', color: 'var(--primary)',
                fontSize: 13, fontWeight: 500, textAlign: 'left',
              }}
            >
              <I.Plus size={14} /> Buat workspace baru
            </button>
          )}
        </div>
      )}
    </div>
  );
}

function Sidebar({ active, onNav, userOverride, workspaces, activeWorkspace, onWorkspaceSwitch, onSignOut }) {
  const u = userOverride
    ? {
        avatar: (userOverride.name || userOverride.email || '?').slice(0, 2).toUpperCase(),
        name: userOverride.name || userOverride.email,
        business: activeWorkspace?.name || userOverride.businessName || 'Survey TOM',
        role: userOverride.role,
      }
    : DEMO.user;
  // Sidebar grouped into three sections — analytics (consume data), campaign
  // (build / collect), workspace (settings). Mirrors the user's mental flow:
  // see results → make new study → manage org.
  const groups = [
    {
      label: 'Analytics',
      items: [
        { key: 'dash',     icon: <I.Chart size={17} />,     label: 'Dashboard' },
        { key: 'growth',   icon: <I.Trend size={17} />,     label: 'Growth Tracker' },
        { key: 'insight',  icon: <I.Sparkle size={17} />,   label: 'AI Auto Insight' },
        { key: 'launch',   icon: <I.Lightning size={17} />, label: 'Launch Suite' },
      ],
    },
    {
      label: 'Campaign',
      items: [
        { key: 'builder',   icon: <I.Plus size={17} />,  label: 'Riset Baru' },
        { key: 'responses', icon: <I.Bell size={17} />,  label: 'Responses' },
        { key: 'landing',   icon: <I.Image size={17} />, label: 'Landing Page' },
        { key: 'reward',    icon: <I.Gift size={17} />,  label: 'Rewards' },
      ],
    },
    {
      label: 'Workspace',
      items: [
        { key: 'brand',    icon: <I.Star size={17} />,     label: 'Brands' },
        { key: 'members',  icon: <I.Users size={17} />,    label: 'Members' },
        { key: 'settings', icon: <I.Settings size={17} />, label: 'Settings' },
        ...(u.role === 'super_admin'
          ? [{ key: 'super', icon: <I.Settings size={17} />, label: 'Super Admin', meta: 'AI Config' }]
          : []),
      ],
    },
  ];
  return (
    <div style={{
      width: 220, background: 'var(--surface)', borderRight: '1px solid var(--line)',
      display: 'flex', flexDirection: 'column', flexShrink: 0,
    }}>
      {/* Workspace switcher takes the role of the old brand block — fits the
          Linear/Notion pattern: workspace identity at the top, app nav below. */}
      <WorkspaceSwitcher
        workspaces={workspaces ?? []}
        activeWorkspace={activeWorkspace}
        onSwitch={onWorkspaceSwitch}
        onCreated={(id) => onWorkspaceSwitch?.(id)}
      />

      <div style={{ padding: '4px 10px', flex: 1, overflowY: 'auto' }} className="scroll">
        {groups.map((g, gi) => (
          <div key={g.label} style={{ marginTop: gi === 0 ? 4 : 12 }}>
            <div style={{
              fontSize: 10.5, color: 'var(--ink-4)',
              textTransform: 'uppercase', letterSpacing: '0.08em',
              padding: '6px 10px 4px', fontWeight: 600,
            }}>
              {g.label}
            </div>
            {g.items.map((it) => (
              <button key={it.key} onClick={() => onNav?.(it.key)}
                style={{
                  width: '100%', display: 'flex', alignItems: 'center', gap: 10,
                  padding: '8px 10px', border: 'none', borderRadius: 8,
                  background: active === it.key ? 'var(--primary-50)' : 'transparent',
                  color: active === it.key ? 'var(--primary)' : 'var(--ink-2)',
                  fontSize: 13.5, fontWeight: active === it.key ? 600 : 500,
                  textAlign: 'left', cursor: 'pointer',
                }}>
                {it.icon}<span style={{ flex: 1 }}>{it.label}</span>
                {it.meta && (
                  <span style={{
                    fontSize: 9.5, padding: '1px 5px', borderRadius: 3,
                    background: 'var(--accent-soft)', color: 'oklch(0.42 0.12 155)',
                    fontWeight: 600, letterSpacing: '0.03em',
                  }}>{it.meta}</span>
                )}
              </button>
            ))}
          </div>
        ))}
      </div>

      <div style={{ padding: 12, borderTop: '1px solid var(--line)' }}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 10, padding: 6 }}>
          <Avatar src={userOverride?.image} name={userOverride?.name || userOverride?.email} seed={userOverride?.id} size={30} radius={'50%'} fontSize={12} />
          <div style={{ minWidth: 0, flex: 1 }}>
            <div style={{ fontSize: 12.5, fontWeight: 600, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{u.name}</div>
            <div style={{ fontSize: 11, color: 'var(--ink-3)' }}>{u.business}</div>
          </div>
          {onSignOut && (
            <button
              onClick={onSignOut}
              title="Keluar"
              style={{
                background: 'transparent', border: '1px solid var(--line-2)',
                borderRadius: 6, padding: '4px 8px', fontSize: 11,
                color: 'var(--ink-3)', cursor: 'pointer',
              }}>Keluar</button>
          )}
        </div>
      </div>
    </div>
  );
}

// ─── Top bar ──────────────────────────────────────────────────────────
function TopBar({ title, subtitle, right }) {
  return (
    <div style={{
      padding: '16px 24px', borderBottom: '1px solid var(--line)',
      display: 'flex', alignItems: 'center', gap: 16, background: 'var(--surface)',
    }}>
      <div style={{ flex: 1 }}>
        <div style={{ fontSize: 17, fontWeight: 600 }}>{title}</div>
        {subtitle && <div style={{ fontSize: 12.5, color: 'var(--ink-3)', marginTop: 2 }}>{subtitle}</div>}
      </div>
      {right}
    </div>
  );
}

// ─── Dashboard view ───────────────────────────────────────────────────
function AdminDashboard({ onNew, onEditCampaign } = {}) {
  const [hovered, setHovered] = useState_ad(null);
  const [filter, setFilter] = useState_ad('30d');

  // Live data — loaded from /api/campaigns + /api/campaigns/:id/analytics
  const [campaigns, setCampaigns] = useState_ad([]);
  const [campaignId, setCampaignId] = useState_ad(null);
  const [analytics, setAnalytics] = useState_ad(null);
  const [recent, setRecent] = useState_ad([]);
  const [trendPoints, setTrendPoints] = useState_ad([]);
  const [competitorList, setCompetitorList] = useState_ad([]);
  const [loadingCampaigns, setLoadingCampaigns] = useState_ad(true);

  useEffect_ad(() => {
    window.api
      .listCampaigns()
      .then((r) => {
        const list = r.campaigns || [];
        setCampaigns(list);
        const active = list.find((c) => c.status === 'active') || list[0];
        if (active) setCampaignId(active.id);
      })
      .catch(() => {})
      .finally(() => setLoadingCampaigns(false));
  }, []);

  useEffect_ad(() => {
    if (!campaignId) return;
    Promise.all([
      window.api.analytics(campaignId).catch(() => null),
      window.api.listResponses(campaignId, 6).catch(() => ({ responses: [] })),
      window.api.trend(campaignId, 'month').catch(() => ({ points: [] })),
      window.api.getCampaign(campaignId).catch(() => ({ competitors: [] })),
    ])
      .then(([a, r, t, c]) => {
        setAnalytics(a);
        setRecent(r?.responses || []);
        setTrendPoints(t?.points || []);
        setCompetitorList((c?.competitors || []).map((x) => x.name));
      });
  }, [campaignId]);

  const camp = campaigns.find((c) => c.id === campaignId);
  const totalRespon = analytics?.totalResponses ?? 0;
  const hasResponses = totalRespon > 0;

  // ─── Empty state — belum ada campaign sama sekali ──────────────────
  if (!loadingCampaigns && campaigns.length === 0) {
    return (
      <div style={{ display: 'flex', flexDirection: 'column', height: '100%' }}>
        <TopBar title="Dashboard" subtitle="Buat campaign pertamamu untuk lihat insight" />
        <div style={{ flex: 1, display: 'grid', placeItems: 'center', padding: 32 }}>
          <div className="card" style={{ padding: 32, textAlign: 'center', maxWidth: 420 }}>
            <div style={{
              width: 64, height: 64, borderRadius: 16, margin: '0 auto 16px',
              background: 'var(--primary-50)', color: 'var(--primary)',
              display: 'grid', placeItems: 'center',
            }}><I.Chart size={28} /></div>
            <div style={{ fontSize: 17, fontWeight: 700, marginBottom: 6 }}>Belum ada campaign</div>
            <div style={{ fontSize: 13.5, color: 'var(--ink-3)', marginBottom: 18, lineHeight: 1.55 }}>
              Mulai dari memilih jenis riset, AI bantu generate pertanyaan,
              sebar via WhatsApp, dapat insight otomatis.
            </div>
            <button className="btn btn-primary" onClick={onNew}>
              <I.Plus size={14} /> Buat Riset Baru
            </button>
          </div>
        </div>
      </div>
    );
  }

  // KPI values — semua zero sampai ada respons real
  const target = camp?.targetResponses ?? 0;
  const claimed = analytics
    ? Math.round((analytics.claimRate || 0) * (analytics.totalResponses || 0))
    : 0;
  const claimPct = analytics
    ? Math.round((analytics.claimRate || 0) * 100)
    : 0;
  const npsScore = analytics?.nps?.score ?? null;
  const topBrand = analytics?.brandShare?.[0];
  const tomShare = topBrand ? (topBrand.share * 100).toFixed(1) : null;
  const tomBrand = topBrand?.name ?? '—';

  // Brand-share data for donut — kosong saat belum ada respons
  const palette = ['var(--primary)', 'oklch(0.68 0.16 25)', 'oklch(0.62 0.14 145)', 'oklch(0.66 0.13 230)', 'oklch(0.72 0.01 265)'];
  const donutData = (analytics?.brandShare ?? []).slice(0, 5).map((b, i) => ({
    name: b.name,
    share: Math.round(b.share * 1000) / 10,
    color: palette[i] || 'oklch(0.72 0.01 265)',
    delta: null,
  }));

  return (
    <div style={{ display: 'flex', flexDirection: 'column', height: '100%', overflow: 'hidden' }}>
      <TopBar
        title={camp?.title || 'Dashboard'}
        subtitle={
          camp
            ? `${camp.industryContext || '—'} · ${camp.targetRegion || '—'} · ${totalRespon.toLocaleString('id-ID')} respons`
            : 'Pilih campaign untuk lihat insight'
        }
        right={
          <div style={{ display: 'flex', gap: 8, alignItems: 'center' }}>
            {campaigns.length > 1 && (
              <select
                className="select"
                value={campaignId || ''}
                onChange={(e) => setCampaignId(e.target.value)}
                style={{ padding: '6px 10px', fontSize: 12.5, minWidth: 200 }}
              >
                {campaigns.map((c) => (
                  <option key={c.id} value={c.id}>{c.title}</option>
                ))}
              </select>
            )}
            {camp && (
              <span className={`badge ${camp.status === 'active' ? 'badge-success' : ''}`}>
                <span style={{ width: 6, height: 6, borderRadius: '50%', background: 'oklch(0.62 0.16 155)' }}></span>
                {camp.status}
              </span>
            )}
            {camp && onEditCampaign && (
              <button
                className="btn btn-soft"
                onClick={() => onEditCampaign(camp.id)}
                title="Edit campaign"
                style={{ padding: '6px 10px', fontSize: 12.5 }}
              >
                <I.Edit size={13} /> Edit
              </button>
            )}
            <div style={{ display: 'flex', background: 'var(--bg-2)', borderRadius: 8, padding: 2 }}>
              {['7d', '30d', '90d'].map((f) => (
                <button key={f} onClick={() => setFilter(f)} style={{
                  border: 'none', background: filter === f ? 'var(--surface)' : 'transparent',
                  padding: '5px 10px', borderRadius: 6, fontSize: 12, fontWeight: 500,
                  color: filter === f ? 'var(--ink)' : 'var(--ink-3)',
                  boxShadow: filter === f ? 'var(--shadow-sm)' : 'none', cursor: 'pointer',
                }}>{f}</button>
              ))}
            </div>
          </div>
        }
      />

      <div className="scroll" style={{ flex: 1, overflow: 'auto', padding: 20, background: 'var(--bg)' }}>
        {/* KPI row — semua angka real, "—" / 0 kalau belum ada respons */}
        <div style={{ display: 'grid', gridTemplateColumns: 'repeat(4, 1fr)', gap: 12, marginBottom: 16 }}>
          <Stat
            label="Top of Mind Share"
            value={tomShare ?? '—'}
            suffix={tomShare ? '%' : ''}
            delta={null}
            hint={tomShare ? `${tomBrand} paling sering disebut` : 'Belum ada respons'}
          />
          <Stat
            label="Total Responden"
            value={totalRespon.toLocaleString('id-ID')}
            delta={null}
            hint={target ? `Target ${target.toLocaleString('id-ID')}` : 'sejak campaign aktif'}
          />
          <Stat
            label="Net Promoter Score"
            value={npsScore != null ? String(npsScore) : '—'}
            delta={null}
            hint={analytics?.nps?.total
              ? `dari ${analytics.nps.total} responden (P${analytics.nps.promoters}/D${analytics.nps.detractors})`
              : 'Belum cukup respons skala'}
          />
          <Stat
            label="Hadiah Diklaim"
            value={claimed.toLocaleString('id-ID')}
            delta={null}
            hint={hasResponses ? `${claimPct}% dari total responden` : 'Belum ada respons'}
          />
        </div>

        {/* Charts row */}
        <div style={{ display: 'grid', gridTemplateColumns: '1.4fr 1fr', gap: 12, marginBottom: 16 }}>
          <div className="card" style={{ padding: 18 }}>
            <div style={{ marginBottom: 10 }}>
              <div style={{ fontSize: 13, fontWeight: 600 }}>Respons Per Bulan</div>
              <div style={{ fontSize: 11.5, color: 'var(--ink-3)' }}>
                {trendPoints.length > 0
                  ? `Total ${totalRespon.toLocaleString('id-ID')} respons sejak campaign aktif`
                  : 'Tren akan muncul setelah ada respons masuk'}
              </div>
            </div>
            {trendPoints.length > 0 ? (
              <ResponseTrendChart points={trendPoints} />
            ) : (
              <ChartEmptyState message="Belum ada respons. Bagikan link survey ke responden untuk mulai melihat tren." />
            )}
          </div>

          <div className="card" style={{ padding: 18 }}>
            <div style={{ fontSize: 13, fontWeight: 600 }}>Brand Share</div>
            <div style={{ fontSize: 11.5, color: 'var(--ink-3)' }}>
              {donutData.length > 0 ? 'Top picks dari respons' : 'Belum ada data'}
            </div>
            {donutData.length > 0 ? (
              <div style={{ display: 'flex', alignItems: 'center', gap: 16, marginTop: 16 }}>
                <div style={{ position: 'relative' }}>
                  <Donut data={donutData} size={150} thickness={18} />
                  <div style={{ position: 'absolute', inset: 0, display: 'grid', placeItems: 'center', textAlign: 'center' }}>
                    <div>
                      <div className="mono" style={{ fontSize: 22, fontWeight: 700, letterSpacing: '-0.02em' }}>
                        {donutData[0].share.toFixed(1)}%
                      </div>
                      <div style={{ fontSize: 10.5, color: 'var(--ink-3)', maxWidth: 90, lineHeight: 1.2, overflow: 'hidden', textOverflow: 'ellipsis' }}>
                        {donutData[0].name}
                      </div>
                    </div>
                  </div>
                </div>
                <div style={{ flex: 1, display: 'flex', flexDirection: 'column', gap: 6 }}>
                  {donutData.map((b) => (
                    <div key={b.name} style={{ display: 'flex', alignItems: 'center', gap: 8, fontSize: 12 }}>
                      <span style={{ width: 8, height: 8, borderRadius: 2, background: b.color }}></span>
                      <span style={{ flex: 1, color: 'var(--ink-2)', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{b.name}</span>
                      <span className="mono" style={{ fontWeight: 600 }}>{b.share.toFixed(1)}%</span>
                    </div>
                  ))}
                </div>
              </div>
            ) : (
              <ChartEmptyState message="Brand share dihitung dari pertanyaan choice. Tunggu respons masuk." compact />
            )}
          </div>
        </div>

        {/* Competitor editor + AI insight row */}
        <div style={{ display: 'grid', gridTemplateColumns: '1.4fr 1fr', gap: 12, marginBottom: 16 }}>
          <CompetitorEditor
            campaignId={campaignId}
            initial={competitorList}
            onSaved={(names) => setCompetitorList(names)}
          />

          <div className="card" style={{ padding: 18 }}>
            <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 10 }}>
              <div style={{ fontSize: 13, fontWeight: 600, display: 'flex', alignItems: 'center', gap: 6 }}>
                <I.Sparkle size={14} style={{ color: 'var(--primary)' }} /> Insight Otomatis
              </div>
              <span className="badge">AI</span>
            </div>
            <div style={{ fontSize: 12, color: 'var(--ink-3)', lineHeight: 1.55 }}>
              {hasResponses
                ? <>Buka <b>AI Auto Insight</b> di sidebar untuk lihat analisis lengkap berdasarkan respons saat ini.</>
                : 'Insight AI akan muncul di sini setelah ada minimal 5 respons masuk.'}
            </div>
          </div>
        </div>

        {/* Demographics + recent — semua dari respons real, kosong saat 0 */}
        <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr 1.2fr', gap: 12 }}>
          {(() => {
            const ageBuckets = ['18-24', '25-34', '35-44', '45+'];
            const genderBuckets = [
              { id: 'perempuan', label: 'Perempuan', color: 'var(--primary)' },
              { id: 'laki-laki', label: 'Laki-laki', color: 'var(--accent)' },
              { id: 'lainnya',   label: 'Lainnya',   color: 'var(--ink-4)' },
            ];
            const byAge = analytics?.byAge ?? [];
            const byGender = analytics?.byGender ?? [];
            const ageTotal = byAge.reduce((s, b) => s + b.n, 0);
            const genderTotal = byGender.reduce((s, b) => s + b.n, 0);
            const ageRows = ageBuckets.map((label) => {
              const hit = byAge.find((b) => b.ageRange === label);
              const v = ageTotal > 0 ? Math.round(((hit?.n || 0) / ageTotal) * 100) : 0;
              return { label, v };
            });
            const genderRows = genderBuckets.map((g) => {
              const hit = byGender.find((b) => b.gender === g.id);
              const v = genderTotal > 0 ? Math.round(((hit?.n || 0) / genderTotal) * 100) : 0;
              return { ...g, v };
            });

            return (
              <>
                <div className="card" style={{ padding: 18 }}>
                  <div style={{ fontSize: 13, fontWeight: 600 }}>Usia Responden</div>
                  <div style={{ fontSize: 11.5, color: 'var(--ink-3)', marginBottom: 14 }}>
                    {ageTotal > 0 ? `${ageTotal} responden mengisi usia` : 'Belum ada respons usia'}
                  </div>
                  {ageTotal > 0 ? (
                    <div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
                      {ageRows.map((a) => (
                        <div key={a.label}>
                          <div style={{ display: 'flex', justifyContent: 'space-between', fontSize: 12, marginBottom: 4 }}>
                            <span>{a.label}</span>
                            <span className="mono" style={{ fontWeight: 600 }}>{a.v}%</span>
                          </div>
                          <div style={{ height: 7, borderRadius: 999, background: 'var(--bg-2)', overflow: 'hidden' }}>
                            <div style={{ width: a.v + '%', height: '100%', background: 'var(--primary)', borderRadius: 999 }}></div>
                          </div>
                        </div>
                      ))}
                    </div>
                  ) : (
                    <ChartEmptyState compact message="Aktifkan field 'Rentang umur' di Profil Responden saat bikin campaign." />
                  )}
                </div>

                <div className="card" style={{ padding: 18 }}>
                  <div style={{ fontSize: 13, fontWeight: 600 }}>Gender</div>
                  <div style={{ fontSize: 11.5, color: 'var(--ink-3)', marginBottom: 14 }}>
                    {genderTotal > 0 ? `${genderTotal} responden mengisi gender` : 'Belum ada respons gender'}
                  </div>
                  {genderTotal > 0 ? (
                    <>
                      <div style={{ display: 'flex', height: 8, borderRadius: 4, overflow: 'hidden', marginBottom: 12 }}>
                        {genderRows.map((g, i) => (
                          <div key={i} style={{ width: g.v + '%', background: g.color }} />
                        ))}
                      </div>
                      {genderRows.map((g) => (
                        <div key={g.label} style={{ display: 'flex', justifyContent: 'space-between', fontSize: 12, padding: '4px 0', color: 'var(--ink-2)' }}>
                          <span style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
                            <span style={{ width: 8, height: 8, borderRadius: 2, background: g.color }}></span>
                            {g.label}
                          </span>
                          <span className="mono" style={{ fontWeight: 600 }}>{g.v}%</span>
                        </div>
                      ))}
                    </>
                  ) : (
                    <ChartEmptyState compact message="Aktifkan field 'Gender' di Profil Responden saat bikin campaign." />
                  )}
                </div>
              </>
            );
          })()}

          <div className="card" style={{ padding: 18 }}>
            <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 12 }}>
              <div>
                <div style={{ fontSize: 13, fontWeight: 600 }}>Respons Terbaru</div>
                <div style={{ fontSize: 11.5, color: 'var(--ink-3)' }}>
                  {recent.length > 0 ? 'Live · update tiap menit' : 'Belum ada respons masuk'}
                </div>
              </div>
              {recent.length > 0 && (
                <span style={{ fontSize: 10.5, color: 'oklch(0.55 0.15 155)', display: 'flex', alignItems: 'center', gap: 4 }}>
                  <span style={{ width: 6, height: 6, borderRadius: '50%', background: 'oklch(0.62 0.16 155)' }}></span>LIVE
                </span>
              )}
            </div>
            {recent.length > 0 ? (
              <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
                {recent.map((r) => {
                  const name = r.respondentName || 'Anonim';
                  return (
                    <div key={r.id} style={{ display: 'flex', alignItems: 'center', gap: 10, fontSize: 12 }}>
                      <div style={{ width: 26, height: 26, borderRadius: '50%', background: 'var(--primary-50)', color: 'var(--primary)', display: 'grid', placeItems: 'center', fontWeight: 600, fontSize: 11 }}>
                        {name[0].toUpperCase()}
                      </div>
                      <div style={{ flex: 1, minWidth: 0 }}>
                        <div style={{ fontWeight: 500, color: 'var(--ink)' }}>
                          {name} <span style={{ color: 'var(--ink-3)', fontWeight: 400 }}>· {r.region || '—'}</span>
                        </div>
                        <div style={{ color: 'var(--ink-3)', fontSize: 11 }}>
                          {new Date(r.submittedAt).toLocaleString('id-ID', { day: '2-digit', month: 'short', hour: '2-digit', minute: '2-digit' })}
                          {r.rewardCode && <> · <span className="mono" style={{ color: 'var(--primary)' }}>{r.rewardCode}</span></>}
                        </div>
                      </div>
                    </div>
                  );
                })}
              </div>
            ) : (
              <ChartEmptyState compact message="Bagikan link survey ke responden — respons baru muncul di sini secara live." />
            )}
          </div>
        </div>
      </div>
    </div>
  );
}

// ─── Helpers ─────────────────────────────────────────────────────────

// Reusable empty-state untuk chart/card kosong. Kompak (no border) atau full
// (centered icon + text) tergantung flag.
function ChartEmptyState({ message, compact }) {
  if (compact) {
    return (
      <div style={{
        fontSize: 11.5, color: 'var(--ink-3)', fontStyle: 'italic',
        padding: '20px 6px', textAlign: 'center', lineHeight: 1.5,
      }}>{message}</div>
    );
  }
  return (
    <div style={{
      display: 'grid', placeItems: 'center', minHeight: 200,
      padding: 20, textAlign: 'center', color: 'var(--ink-3)',
    }}>
      <div style={{ maxWidth: 320 }}>
        <div style={{
          width: 44, height: 44, borderRadius: 11, margin: '0 auto 12px',
          background: 'var(--bg-2)', display: 'grid', placeItems: 'center',
        }}><I.Trend size={20} style={{ color: 'var(--ink-4)' }} /></div>
        <div style={{ fontSize: 12.5, lineHeight: 1.55 }}>{message}</div>
      </div>
    </div>
  );
}

// Inline editor list kompetitor untuk campaign aktif. Bulk-replace via
// PUT /api/campaigns/:id/competitors. Disabled saat campaignId belum ada.
function CompetitorEditor({ campaignId, initial, onSaved }) {
  const [items, setItems] = useState_ad(initial || []);
  const [draft, setDraft] = useState_ad('');
  const [busy, setBusy] = useState_ad(false);
  const [error, setError] = useState_ad(null);
  const [savedAt, setSavedAt] = useState_ad(null);

  // Sync saat campaign / initial berubah (campaign switch).
  useEffect_ad(() => { setItems(initial || []); }, [initial, campaignId]);

  const dirty = JSON.stringify(items) !== JSON.stringify(initial || []);

  const add = () => {
    const v = draft.trim();
    if (!v || items.includes(v) || items.length >= 20) return;
    setItems([...items, v]);
    setDraft('');
  };
  const remove = (i) => setItems(items.filter((_, idx) => idx !== i));

  const save = async () => {
    if (!campaignId) return;
    setBusy(true); setError(null);
    try {
      const r = await window.api.putCompetitors(campaignId, items);
      const names = (r.competitors || []).map((c) => c.name);
      setItems(names);
      setSavedAt(Date.now());
      onSaved?.(names);
    } catch (e) {
      setError(e.message || 'Gagal menyimpan');
    } finally { setBusy(false); }
  };

  return (
    <div className="card" style={{ padding: 18 }}>
      <div style={{ display: 'flex', alignItems: 'flex-start', justifyContent: 'space-between', marginBottom: 4 }}>
        <div>
          <div style={{ fontSize: 13, fontWeight: 600 }}>Daftar Kompetitor</div>
          <div style={{ fontSize: 11.5, color: 'var(--ink-3)' }}>
            Brand pembanding yang dipakai di pertanyaan survei. Maks 20.
          </div>
        </div>
        {savedAt && !dirty && (
          <span style={{ fontSize: 11, color: 'oklch(0.55 0.15 155)', display: 'flex', alignItems: 'center', gap: 4 }}>
            <I.Check size={12} /> Tersimpan
          </span>
        )}
      </div>

      <div style={{ display: 'flex', flexWrap: 'wrap', gap: 6, marginTop: 14, marginBottom: 10, minHeight: 32 }}>
        {items.map((n, i) => (
          <span key={i} style={{
            display: 'inline-flex', alignItems: 'center', gap: 6,
            padding: '5px 10px', borderRadius: 999, fontSize: 12, fontWeight: 500,
            background: 'var(--bg-2)', border: '1px solid var(--line-2)',
            color: 'var(--ink-2)',
          }}>
            {n}
            <button
              onClick={() => remove(i)}
              style={{ background: 'transparent', border: 'none', padding: 0, cursor: 'pointer', display: 'flex', color: 'var(--ink-3)' }}
              title="Hapus"
            >
              <I.X size={11} />
            </button>
          </span>
        ))}
        {items.length === 0 && (
          <span style={{ fontSize: 12, color: 'var(--ink-4)', fontStyle: 'italic', padding: '4px 0' }}>
            Belum ada kompetitor. Tambahkan dari input di bawah.
          </span>
        )}
      </div>

      <div style={{ display: 'flex', gap: 6 }}>
        <input
          className="input"
          value={draft}
          onChange={(e) => setDraft(e.target.value)}
          onKeyDown={(e) => e.key === 'Enter' && (e.preventDefault(), add())}
          placeholder="Tambahkan kompetitor lalu tekan Enter"
          disabled={!campaignId || items.length >= 20}
          style={{ flex: 1 }}
        />
        <button
          className="btn btn-soft"
          onClick={add}
          disabled={!campaignId || !draft.trim() || items.length >= 20}
          style={{ padding: '8px 12px' }}
        >
          <I.Plus size={13} />Tambah
        </button>
      </div>

      {error && (
        <div style={{ marginTop: 10, fontSize: 12, color: 'var(--danger)', background: 'var(--danger-soft)', padding: '6px 10px', borderRadius: 6 }}>
          {error}
        </div>
      )}

      {dirty && (
        <div style={{ marginTop: 12, display: 'flex', gap: 6, justifyContent: 'flex-end' }}>
          <button className="btn btn-ghost" onClick={() => setItems(initial || [])} disabled={busy} style={{ fontSize: 12.5, padding: '6px 12px' }}>
            Batal
          </button>
          <button className="btn btn-primary" onClick={save} disabled={busy} style={{ fontSize: 12.5, padding: '6px 12px' }}>
            {busy ? 'Menyimpan…' : 'Simpan perubahan'}
          </button>
        </div>
      )}
    </div>
  );
}

Object.assign(window, { AdminDashboard, Sidebar, TopBar });
