const { useState, useEffect, useCallback } = React;

// ---------- утилиты ----------
async function api(path, opts = {}) {
  const res = await fetch('/api' + path, {
    headers: { 'Content-Type': 'application/json' },
    credentials: 'same-origin',
    ...opts,
    body: opts.body ? JSON.stringify(opts.body) : undefined,
  });
  const data = await res.json().catch(() => ({}));
  if (!res.ok) throw new Error(data.error || 'Ошибка ' + res.status);
  return data;
}
const fmtDate = (s) => s ? s.slice(0, 10).split('-').reverse().join('.') : '—';
const fmtMoney = (n) => new Intl.NumberFormat('ru-RU', { maximumFractionDigits: 2 }).format(Number(n) || 0) + ' ₽';
const KIND_LABEL = { 'НБ': 'НБ', 'пр': 'пролонгация' };

// ---------- вход ----------
function Login({ onOk }) {
  const [username, setU] = useState('');
  const [password, setP] = useState('');
  const [err, setErr] = useState('');
  const [busy, setBusy] = useState(false);
  const submit = async (e) => {
    e.preventDefault();
    setBusy(true); setErr('');
    try { onOk(await api('/login', { method: 'POST', body: { username, password } })); }
    catch (ex) { setErr(ex.message); }
    finally { setBusy(false); }
  };
  return (
    <div className="login card">
      <h2 style={{ marginTop: 0 }}>CRM Диана</h2>
      <div className="mute" style={{ marginBottom: 14 }}>Страхование · клиентская база</div>
      <form onSubmit={submit}>
        <div className="field" style={{ marginBottom: 10 }}>
          <label>Логин</label>
          <input autoFocus value={username} onChange={(e) => setU(e.target.value)} style={{ width: '100%' }} />
        </div>
        <div className="field" style={{ marginBottom: 14 }}>
          <label>Пароль</label>
          <input type="password" value={password} onChange={(e) => setP(e.target.value)} style={{ width: '100%' }} />
        </div>
        {err && <div className="warn" style={{ marginBottom: 10 }}>{err}</div>}
        <button className="primary" disabled={busy} style={{ width: '100%' }}>{busy ? 'Вхожу…' : 'Войти'}</button>
      </form>
    </div>
  );
}

// ---------- карточка клиента ----------
function ClientModal({ id, isAdmin, onClose, onSaved }) {
  const [data, setData] = useState(null);
  const [edit, setEdit] = useState(false);
  const [form, setForm] = useState({});
  const [err, setErr] = useState('');
  const [newPolicy, setNewPolicy] = useState(null);

  const load = useCallback(async () => {
    try {
      const d = await api('/clients/' + id);
      setData(d);
      setForm({
        full_name: d.client.full_name,
        phones: (d.client.phones || []).join(', '),
        birthday: d.client.birthday || '',
        notes: d.client.notes || '',
        prolong_note: d.client.prolong_note || '',
      });
    } catch (ex) { setErr(ex.message); }
  }, [id]);
  useEffect(() => { load(); }, [load]);

  if (!data) return <div className="modalbg" onClick={onClose}><div className="modal">Загрузка…</div></div>;
  const c = data.client;

  const saveClient = async () => {
    setErr('');
    try {
      await api('/clients/' + id, { method: 'PUT', body: form });
      setEdit(false);
      await load();
      onSaved && onSaved();
    } catch (ex) { setErr(ex.message); }
  };
  const savePolicy = async () => {
    setErr('');
    try {
      await api('/policies', { method: 'POST', body: { ...newPolicy, client_id: id } });
      setNewPolicy(null);
      await load();
      onSaved && onSaved();
    } catch (ex) { setErr(ex.message); }
  };

  const f = (label, key, type) => (
    <div className="field" style={{ minWidth: 160, flex: 1 }}>
      <label>{label}</label>
      <input type={type || 'text'} value={form[key] ?? ''} onChange={(e) => setForm({ ...form, [key]: e.target.value })} style={{ width: '100%' }} />
    </div>
  );

  return (
    <div className="modalbg" onClick={(e) => { if (e.target === e.currentTarget) onClose(); }}>
      <div className="modal">
        <div className="cardhead">
          <h2>{c.full_name}</h2>
          <div style={{ display: 'flex', gap: 6 }}>
            {isAdmin && !edit && <button className="ghost" onClick={() => setEdit(true)}>✎ Редактировать</button>}
            {edit && <button className="primary" onClick={saveClient}>Сохранить</button>}
            <button className="ghost" onClick={onClose}>Закрыть</button>
          </div>
        </div>
        {err && <div className="warn" style={{ marginBottom: 10 }}>{err}</div>}
        {edit ? (
          <div>
            <div className="row" style={{ marginBottom: 14 }}>
              {f('ФИО', 'full_name')}
              {f('Телефоны (через запятую)', 'phones')}
              {f('День рождения', 'birthday', 'date')}
            </div>
            <div className="field" style={{ marginBottom: 14 }}>
              <label>Комментарий к пролонгации (добавляется к сообщению в Telegram)</label>
              <textarea value={form.prolong_note ?? ''} onChange={(e) => setForm({ ...form, prolong_note: e.target.value })} style={{ width: '100%', minHeight: 46 }} />
            </div>
            <div className="field">
              <label>Заметки (внутренние, в Telegram не уходят)</label>
              <textarea value={form.notes} onChange={(e) => setForm({ ...form, notes: e.target.value })} style={{ width: '100%', minHeight: 46 }} />
            </div>
          </div>
        ) : (
          <div style={{ display: 'flex', gap: 24, flexWrap: 'wrap', marginBottom: 14, fontSize: 13 }}>
            <div><div className="mute">Телефоны</div><div className="num">{(c.phones || []).join(', ') || '—'}</div></div>
            <div><div className="mute">День рождения</div><div>{c.birthday ? fmtDate(c.birthday) : '—'}</div></div>
            {c.prolong_note && <div style={{ flex: 1, minWidth: 220 }}><div className="mute">Комментарий к пролонгации</div><div>💬 {c.prolong_note}</div></div>}
            <div style={{ flex: 1, minWidth: 200 }}><div className="mute">Заметки</div><div>{c.notes || '—'}</div></div>
          </div>
        )}

        <div className="cardhead" style={{ marginTop: 8 }}>
          <h2 style={{ fontSize: 14 }}>История страхования ({data.policies.length})</h2>
          {isAdmin && !newPolicy && <button className="ghost" onClick={() => setNewPolicy({ number: '', product: '', kind: '', status: 'Выпущен', start_date: '', end_date: '', premium: '', payment_status: '', agent: '' })}>+ Полис</button>}
        </div>
        {newPolicy && (
          <div className="row" style={{ marginBottom: 12, background: '#f6f8f7', padding: 10, borderRadius: 8 }}>
            <div className="field"><label>№</label><input value={newPolicy.number} onChange={(e) => setNewPolicy({ ...newPolicy, number: e.target.value })} style={{ width: 120 }} /></div>
            <div className="field"><label>Продукт</label><input value={newPolicy.product} onChange={(e) => setNewPolicy({ ...newPolicy, product: e.target.value })} style={{ width: 150 }} /></div>
            <div className="field"><label>Тип</label>
              <select value={newPolicy.kind} onChange={(e) => setNewPolicy({ ...newPolicy, kind: e.target.value })}>
                <option value="">—</option><option value="НБ">НБ</option><option value="пр">пролонгация</option>
              </select></div>
            <div className="field"><label>Начало</label><input type="date" value={newPolicy.start_date} onChange={(e) => setNewPolicy({ ...newPolicy, start_date: e.target.value })} /></div>
            <div className="field"><label>Окончание</label><input type="date" value={newPolicy.end_date} onChange={(e) => setNewPolicy({ ...newPolicy, end_date: e.target.value })} /></div>
            <div className="field"><label>Премия</label><input value={newPolicy.premium} onChange={(e) => setNewPolicy({ ...newPolicy, premium: e.target.value })} style={{ width: 100 }} /></div>
            <button className="primary" onClick={savePolicy}>Сохранить</button>
            <button className="ghost" onClick={() => setNewPolicy(null)}>Отмена</button>
          </div>
        )}
        <div className="tablewrap">
          <table>
            <thead><tr><th>№</th><th>Продукт</th><th>Тип</th><th>Начало</th><th>Окончание</th>{isAdmin && <th>Премия</th>}{isAdmin && <th>Оплата</th>}{isAdmin && <th>Агент</th>}</tr></thead>
            <tbody>
              {data.policies.map((p) => (
                <tr key={p.id}>
                  <td className="num">{p.number || '—'}</td>
                  <td>{p.product || '—'}</td>
                  <td>{KIND_LABEL[p.kind] || '—'}</td>
                  <td className="num">{fmtDate(p.start_date)}</td>
                  <td className="num">{fmtDate(p.end_date)}</td>
                  {isAdmin && <td className="num">{fmtMoney(p.premium)}</td>}
                  {isAdmin && <td><span className={'badge ' + (/не оплачен/i.test(p.payment_status) ? 'red' : /оплачен/i.test(p.payment_status) ? 'green' : '')}>{p.payment_status || '—'}</span></td>}
                  {isAdmin && <td>{p.agent || '—'}</td>}
                </tr>
              ))}
              {!data.policies.length && <tr><td colSpan={isAdmin ? 8 : 5} className="mute" style={{ textAlign: 'center' }}>Полисов нет</td></tr>}
            </tbody>
          </table>
        </div>
      </div>
    </div>
  );
}

// ---------- клиенты ----------
function ClientsTab({ me, openClient }) {
  const [q, setQ] = useState('');
  const [product, setProduct] = useState('');
  const [agent, setAgent] = useState('');
  const [filters, setFilters] = useState({ products: [], agents: [] });
  const [data, setData] = useState({ items: [], total: 0 });

  useEffect(() => { api('/meta/filters').then(setFilters).catch(() => {}); }, []);
  const load = useCallback(async () => {
    try {
      setData(await api('/clients?q=' + encodeURIComponent(q) + '&product=' + encodeURIComponent(product) + '&agent=' + encodeURIComponent(agent)));
    } catch (e) { /* ignore */ }
  }, [q, product, agent]);
  useEffect(() => { const t = setTimeout(load, 250); return () => clearTimeout(t); }, [load]);

  return (
    <div className="card">
      <div className="cardhead">
        <h2>Клиенты ({data.total})</h2>
      </div>
      <div className="row" style={{ marginBottom: 12 }}>
        <div className="field"><label>Поиск (ФИО / телефон / № договора)</label><input value={q} onChange={(e) => setQ(e.target.value)} style={{ width: 250 }} /></div>
        <div className="field"><label>Вид страхования</label>
          <select value={product} onChange={(e) => setProduct(e.target.value)}>
            <option value="">Все виды</option>
            {filters.products.map((p) => <option key={p} value={p}>{p}</option>)}
          </select></div>
        <div className="field"><label>Агент</label>
          <select value={agent} onChange={(e) => setAgent(e.target.value)}>
            <option value="">Все агенты</option>
            {filters.agents.map((a) => <option key={a} value={a}>{a}</option>)}
          </select></div>
      </div>
      <div className="tablewrap">
        <table>
          <thead><tr><th>ФИО</th><th>Телефоны</th><th>ДР</th><th>Полисов</th><th>Ближайшее окончание</th><th>Комментарий</th></tr></thead>
          <tbody>
            {data.items.map((c) => (
              <tr key={c.id} className="click" onClick={() => openClient(c.id)}>
                <td style={{ fontWeight: 500 }}>{c.full_name}</td>
                <td className="num">{(c.phones || []).join(', ') || '—'}</td>
                <td className="num">{c.birthday ? fmtDate(c.birthday) : '—'}</td>
                <td className="num">{c.policies_count}</td>
                <td className="num">{c.next_end ? fmtDate(c.next_end) : '—'}</td>
                <td className="mute">{c.prolong_note || '—'}</td>
              </tr>
            ))}
            {!data.items.length && <tr><td colSpan={6} className="mute" style={{ textAlign: 'center' }}>Ничего не найдено</td></tr>}
          </tbody>
        </table>
      </div>
    </div>
  );
}

// ---------- полисы ----------
function PoliciesTab({ me }) {
  const [q, setQ] = useState('');
  const [product, setProduct] = useState('');
  const [agent, setAgent] = useState('');
  const [filters, setFilters] = useState({ products: [], agents: [] });
  const [payStatus, setPayStatus] = useState('');
  const [expDays, setExpDays] = useState('');
  const [data, setData] = useState({ items: [], total: 0 });
  const isAdmin = me.role === 'admin';

  useEffect(() => { api('/meta/filters').then(setFilters).catch(() => {}); }, []);
  useEffect(() => {
    const t = setTimeout(async () => {
      const params = new URLSearchParams({ q, product, agent, payStatus, expDays });
      try { setData(await api('/policies?' + params)); } catch (e) { /* ignore */ }
    }, 250);
    return () => clearTimeout(t);
  }, [q, product, agent, payStatus, expDays]);

  const products = [...new Set(data.items.map((p) => p.product).filter(Boolean))].sort();
  const donutParts = [...data.items.reduce((m, p) => {
    const key = p.product || 'Прочее';
    m.set(key, (m.get(key) || 0) + Number(p.premium || 0));
    return m;
  }, new Map())].map(([label, value], i) => ({ label: `${label} · ${Math.round(value).toLocaleString('ru-RU')} ₽`, value, color: PALETTE[i % PALETTE.length] }))
    .sort((a, b) => b.value - a.value).slice(0, 8);

  return (
    <div>
      {isAdmin && donutParts.length > 0 && (
        <div className="card">
          <div className="cardhead"><h2>Структура портфеля по видам (в выборке)</h2></div>
          <Donut parts={donutParts} center={<span>по премии</span>} />
        </div>
      )}
      <div className="card">
        <div className="cardhead"><h2>Полисы ({data.total})</h2></div>
        <div className="row" style={{ marginBottom: 12 }}>
          <div className="field"><label>Поиск (№ / ФИО)</label><input value={q} onChange={(e) => setQ(e.target.value)} style={{ width: 200 }} /></div>
          <div className="field"><label>Продукт</label>
            <select value={product} onChange={(e) => setProduct(e.target.value)}>
              <option value="">Все</option>
              {filters.products.map((p) => <option key={p} value={p}>{p}</option>)}
            </select></div>
          <div className="field"><label>Агент</label>
            <select value={agent} onChange={(e) => setAgent(e.target.value)}>
              <option value="">Все агенты</option>
              {filters.agents.map((a) => <option key={a} value={a}>{a}</option>)}
            </select></div>
          {isAdmin && (
            <div className="field"><label>Оплата</label>
              <select value={payStatus} onChange={(e) => setPayStatus(e.target.value)}>
                <option value="">Все</option>
                <option value="Оплачен">Оплачен</option>
                <option value="Условно оплачен">Условно оплачен</option>
                <option value="Не оплачен">Не оплачен</option>
              </select></div>
          )}
          <div className="field"><label>Истекают, дней</label>
            <select value={expDays} onChange={(e) => setExpDays(e.target.value)}>
              <option value="">—</option><option>7</option><option>14</option><option>30</option><option>60</option>
            </select></div>
        </div>
      <div className="tablewrap">
        <table>
          <thead><tr><th>№</th><th>Клиент</th><th>Продукт</th><th>Тип</th><th>Начало</th><th>Окончание</th>{isAdmin && <th>Премия</th>}{isAdmin && <th>Оплата</th>}{isAdmin && <th>Агент</th>}</tr></thead>
          <tbody>
            {data.items.map((p) => (
              <tr key={p.id}>
                <td className="num">{p.number || '—'}</td>
                <td>{p.client_name || '—'}</td>
                <td>{p.product || '—'}</td>
                <td>{KIND_LABEL[p.kind] || '—'}</td>
                <td className="num">{fmtDate(p.start_date)}</td>
                <td className="num">{fmtDate(p.end_date)}</td>
                {isAdmin && <td className="num">{fmtMoney(p.premium)}</td>}
                {isAdmin && <td><span className={'badge ' + (/не оплачен/i.test(p.payment_status) ? 'red' : /оплачен/i.test(p.payment_status) ? 'green' : '')}>{p.payment_status || '—'}</span></td>}
                {isAdmin && <td>{p.agent || '—'}</td>}
              </tr>
            ))}
            {!data.items.length && <tr><td colSpan={isAdmin ? 9 : 6} className="mute" style={{ textAlign: 'center' }}>Ничего не найдено</td></tr>}
          </tbody>
        </table>
      </div>
      </div>
    </div>
  );
}

// ---------- импорт ----------
function ImportTab() {
  const [kind, setKind] = useState('auto');
  const [owner, setOwner] = useState('');
  const [result, setResult] = useState(null);
  const [history, setHistory] = useState({ imports: [], conflicts: [] });
  const [busy, setBusy] = useState(false);
  const [err, setErr] = useState('');

  const loadHistory = useCallback(async () => { try { setHistory(await api('/imports')); } catch (e) {} }, []);
  useEffect(() => { loadHistory(); }, [loadHistory]);

  const upload = async (e) => {
    const file = e.target.files[0];
    if (!file) return;
    setBusy(true); setErr(''); setResult(null);
    try {
      const fd = new FormData();
      fd.append('file', file);
      fd.append('kind', kind);
      fd.append('owner', owner);
      const res = await fetch('/api/import', { method: 'POST', body: fd, credentials: 'same-origin' });
      const data = await res.json();
      if (!res.ok) throw new Error(data.error || 'Ошибка импорта');
      setResult(data);
      loadHistory();
    } catch (ex) { setErr(ex.message); }
    finally { setBusy(false); e.target.value = ''; }
  };
  const resolve = async (id) => {
    try { await api('/import-conflicts/' + id + '/resolve', { method: 'POST' }); loadHistory(); }
    catch (e) { setErr(e.message); }
  };

  const c = result && result.counters;
  return (
    <div>
      <div className="card">
        <div className="cardhead"><h2>Загрузка файла</h2></div>
        <div className="mute" style={{ marginBottom: 12 }}>
          Данные объединяются с существующими по номеру договора: сходятся — обновляются, расходятся — попадают в конфликты (их видите вы и Таня). Можно безопасно загружать один и тот же файл повторно.
        </div>
        <div className="row">
          <div className="field"><label>Тип файла</label>
            <select value={kind} onChange={(e) => setKind(e.target.value)}>
              <option value="auto">Определить автоматически</option>
              <option value="dogovora">Договоры.xlsx (выгрузка Ингосстраха)</option>
              <option value="ingos">Отчёт Ингосстраха «Report» (ИФЛ, КИС, журнал)</option>
              <option value="prolong">Пролонгации (spreadsheet.xls)</option>
              <option value="office">Офисные продажи (журнал)</option>
            </select>
          </div>
          <div className="field"><label>Привязать договоры к</label>
            <select value={owner} onChange={(e) => setOwner(e.target.value)}>
              <option value="">— без привязки —</option>
              <option value="vika">Вике (её список)</option>
              <option value="tanya">Тане (офисный пул)</option>
            </select>
          </div>
          <label className="ghost" style={{ display: 'inline-block', cursor: 'pointer', padding: '8px 12px' }}>
            {busy ? 'Загружаю…' : '⬆ Выбрать файл .xlsx / .xls'}
            <input type="file" accept=".xlsx,.xls" style={{ display: 'none' }} onChange={upload} />
          </label>
        </div>
        {err && <div className="warn" style={{ marginTop: 10 }}>{err}</div>}
        {c && (
          <div style={{ marginTop: 14, fontSize: 13 }}>
            Тип: <b>{result.kind}</b> · <span className="ok">✓ Новых полисов: {c.rows_inserted}</span> · обновлено: {c.rows_updated} · новых клиентов: {c.clients_created} · телефонов добавлено: {c.phones_added}{c.rows_skipped ? <span className="warn"> · пропущено: {c.rows_skipped}</span> : null}
            {c.conflicts ? <div className="warn" style={{ marginTop: 6 }}>⚠ Конфликтов (номер совпал, данные разные): {c.conflicts} — список ниже, уведомление отправлено в Telegram</div> : null}
            {result.errors && result.errors.length > 0 && (
              <div className="warn" style={{ marginTop: 8 }}>{result.errors.slice(0, 5).join('; ')}</div>
            )}
          </div>
        )}
      </div>

      <div className="card">
        <div className="cardhead"><h2>Конфликты ({history.conflicts.length})</h2></div>
        <div className="tablewrap"><table>
          <thead><tr><th>№ договора</th><th>Расхождение</th><th>Дата</th><th></th></tr></thead>
          <tbody>
            {history.conflicts.map((cf) => (
              <tr key={cf.id}>
                <td className="num">{cf.number || '—'}</td>
                <td className="warn">{cf.reason}</td>
                <td className="num">{new Date(cf.created_at).toLocaleString('ru-RU')}</td>
                <td><button className="ghost" onClick={() => resolve(cf.id)}>Принять как есть</button></td>
              </tr>
            ))}
            {!history.conflicts.length && <tr><td colSpan={4} className="mute" style={{ textAlign: 'center' }}>Конфликтов нет</td></tr>}
          </tbody>
        </table></div>
      </div>

      <div className="card">
        <div className="cardhead"><h2>История загрузок</h2></div>
        <div className="tablewrap">
          <table>
            <thead><tr><th>Дата</th><th>Файл</th><th>Строк</th><th>Новых</th><th>Обновлено</th><th>Клиентов</th></tr></thead>
            <tbody>
              {history.imports.map((h) => (
                <tr key={h.id}>
                  <td className="num">{new Date(h.created_at).toLocaleString('ru-RU')}</td>
                  <td>{h.filename}</td>
                  <td className="num">{h.rows_total}</td>
                  <td className="num ok">{h.rows_inserted}</td>
                  <td className="num">{h.rows_updated}</td>
                  <td className="num">{h.clients_created}</td>
                </tr>
              ))}
              {!history.imports.length && <tr><td colSpan={6} className="mute" style={{ textAlign: 'center' }}>Загрузок ещё не было</td></tr>}
            </tbody>
          </table>
        </div>
      </div>
    </div>
  );
}

// ---------- агенты (в настройках) ----------
function AgentsCard({ notify }) {
  const [agents, setAgents] = useState([]);
  const [form, setForm] = useState({ name: '', bot_token: '' });
  const [editing, setEditing] = useState(null); // {id, bot_token}

  const load = useCallback(async () => { try { setAgents(await api('/agents')); } catch (e) {} }, []);
  useEffect(() => { load(); }, [load]);

  const add = async () => {
    try {
      await api('/agents', { method: 'POST', body: form });
      setForm({ name: '', bot_token: '' });
      notify('Агент добавлен');
      load();
    } catch (e) { notify(e.message); }
  };
  const saveToken = async (a) => {
    try {
      await api('/agents/' + a.id, { method: 'PUT', body: { bot_token: editing?.bot_token ?? undefined } });
      setEditing(null);
      notify('Токен сохранён');
      load();
    } catch (e) { notify(e.message); }
  };
  const capture = async (a) => {
    try {
      const r = await api('/agents/' + a.id + '/capture', { method: 'POST' });
      notify(r.ok ? 'Бот агента подключён!' : r.error);
      load();
    } catch (e) { notify(e.message); }
  };
  const test = async (a) => {
    try {
      const r = await api('/agents/' + a.id + '/test', { method: 'POST' });
      notify(r.sent ? 'Отправлено агенту' : r.reason);
    } catch (e) { notify(e.message); }
  };
  const toggle = async (a) => {
    await api('/agents/' + a.id, { method: 'PUT', body: { active: !a.active } });
    load();
  };
  const remove = async (a) => {
    if (!window.confirm('Удалить агента «' + a.name + '» из списка рассылки?')) return;
    await api('/agents/' + a.id, { method: 'DELETE' });
    load();
  };

  return (
    <div className="card">
      <div className="cardhead"><h2>Агенты и их боты</h2></div>
      <div className="mute" style={{ marginBottom: 10 }}>
        Каждый агент создаёт своего бота через @BotFather и присылает вам токен. Внесите имя (как в договорах) и токен, затем агент нажимает /start в своём боте — и вы нажимаете «Подключить». Напоминания о пролонгации придут агенту только по его договорам.
      </div>
      <div className="tablewrap">
        <table>
          <thead><tr><th>Агент</th><th>Договоров</th><th>Действующих</th><th>Бот</th><th></th></tr></thead>
          <tbody>
            {agents.map((a) => (
              <tr key={a.id}>
                <td style={{ fontWeight: 500 }}>{a.name}</td>
                <td className="num">{a.policies_count}</td>
                <td className="num">{a.active_count}</td>
                <td>
                  {!a.bot_token ? <span className="badge red">нет токена</span>
                    : !a.chat_id ? <span className="badge">токен есть, нет /start</span>
                    : <span className="badge green">подключён</span>}
                  {a.bot_token ? <span className="pill num">{a.bot_token}</span> : null}
                </td>
                <td style={{ whiteSpace: 'nowrap' }}>
                  {editing?.id === a.id ? (
                    <>
                      <input value={editing.bot_token} placeholder="токен бота" onChange={(e) => setEditing({ id: a.id, bot_token: e.target.value })} style={{ width: 220 }} />{' '}
                      <button className="primary" onClick={() => saveToken(a)}>ОК</button>
                    </>
                  ) : (
                    <>
                      <button className="ghost" onClick={() => setEditing({ id: a.id, bot_token: '' })}>{a.bot_token ? 'Токен' : 'Токен'}</button>{' '}
                      <button className="ghost" onClick={() => capture(a)}>Подключить</button>{' '}
                      {a.chat_id && <button className="ghost" onClick={() => test(a)}>Тест</button>}{' '}
                      <button className="ghost" onClick={() => toggle(a)}>{a.active ? 'Выкл' : 'Вкл'}</button>{' '}
                      <button className="ghost" onClick={() => remove(a)}>×</button>
                    </>
                  )}
                </td>
              </tr>
            ))}
            {!agents.length && <tr><td colSpan={5} className="mute" style={{ textAlign: 'center' }}>Агентов пока нет</td></tr>}
          </tbody>
        </table>
      </div>
      <div className="row" style={{ marginTop: 14 }}>
        <div className="field"><label>Имя агента (как в договорах)</label><input value={form.name} onChange={(e) => setForm({ ...form, name: e.target.value })} style={{ width: 260 }} /></div>
        <div className="field"><label>Токен его бота</label><input value={form.bot_token} onChange={(e) => setForm({ ...form, bot_token: e.target.value })} style={{ width: 300 }} placeholder="123456:AA..." /></div>
        <button className="primary" onClick={add}>Добавить агента</button>
      </div>
    </div>
  );
}

// ---------- настройки ----------
function SettingsTab({ me }) {
  const [users, setUsers] = useState([]);
  const [settings, setSettings] = useState({});
  const [tgStatus, setTgStatus] = useState(null);
  const [msg, setMsg] = useState('');
  const [newUser, setNewUser] = useState({ username: '', password: '', display_name: '', role: 'office' });

  const load = useCallback(async () => {
    const [u, s, t] = await Promise.all([api('/users'), api('/settings'), api('/telegram/status')]);
    setUsers(u); setSettings(s); setTgStatus(t);
  }, []);
  useEffect(() => { load(); }, [load]);
  const notify = (m) => setMsg(m + ' (' + new Date().toLocaleTimeString('ru-RU') + ')');

  const addUser = async () => {
    setMsg('');
    try {
      await api('/users', { method: 'POST', body: newUser });
      setNewUser({ username: '', password: '', display_name: '', role: 'office' });
      setMsg('Пользователь создан');
      load();
    } catch (e) { setMsg(e.message); }
  };
  const toggleUser = async (u) => {
    await api('/users/' + u.id, { method: 'PUT', body: { active: !u.active } });
    load();
  };
  const deleteUser = async (u) => {
    if (!window.confirm('Удалить пользователя ' + u.username + '? Действие необратимо.')) return;
    try { await api('/users/' + u.id, { method: 'DELETE' }); load(); }
    catch (e) { setMsg(e.message); }
  };
  const changePassword = async (u) => {
    const p = prompt('Новый пароль для ' + u.username + ' (минимум 8 символов):');
    if (!p) return;
    try { await api('/users/' + u.id, { method: 'PUT', body: { password: p } }); setMsg('Пароль обновлён'); }
    catch (e) { setMsg(e.message); }
  };
  const saveOffsets = async () => {
    await api('/settings', { method: 'PUT', body: { reminder_offsets: settings.reminder_offsets || [] } });
    setMsg('Напоминания сохранены');
  };
  const testTg = async () => {
    try {
      const r = await api('/telegram/test', { method: 'POST' });
      setMsg(r.sent ? 'Отправлено — проверьте Telegram' : r.reason);
    } catch (e) { setMsg(e.message); }
  };

  return (
    <div>
      {msg && <div className="card ok">{msg}</div>}
      <SalarySettingsCard notify={notify} />
      <div className="card">
        <div className="cardhead"><h2>Ваш Telegram-бот</h2>
          {tgStatus && <span className={'badge ' + (tgStatus.connected ? 'green' : 'red')}>{tgStatus.connected ? 'подключён' : 'не подключён'}</span>}
        </div>
        <div className="mute" style={{ marginBottom: 10 }}>
          {!tgStatus?.connected
            ? 'Откройте бота в Telegram и нажмите «Запустить» (или напишите /start) — связь установится автоматически в течение 5 минут.'
            : 'Chat ID: ' + tgStatus.chatId}
        </div>
        <button className="ghost" onClick={testTg}>Отправить тестовое сообщение</button>
      </div>

      <AgentsCard notify={notify} />

      <div className="card">
        <div className="cardhead"><h2>Напоминания о пролонгации</h2></div>
        <div className="mute" style={{ marginBottom: 10 }}>За сколько дней до окончания полиса присылать сообщение (вам и агентам). Порядок не важен.</div>
        <div className="row" style={{ alignItems: 'center' }}>
          {[30, 14, 7, 3, 1].map((d) => {
            const arr = settings.reminder_offsets || [];
            const on = arr.includes(d);
            return (
              <button key={d} className="ghost" style={{ borderColor: on ? 'var(--teal)' : undefined, color: on ? 'var(--teal)' : undefined }}
                onClick={() => setSettings({ ...settings, reminder_offsets: on ? arr.filter((x) => x !== d) : [...arr, d].sort((a, b) => b - a) })}>
                {on ? '✓ ' : ''}за {d} дн.
              </button>
            );
          })}
          <button className="primary" onClick={saveOffsets}>Сохранить</button>
        </div>
      </div>

      <div className="card">
        <div className="cardhead"><h2>Пользователи</h2></div>
        <div className="tablewrap">
          <table>
            <thead><tr><th>Логин</th><th>Имя</th><th>Роль</th><th>Статус</th><th>Telegram</th><th></th></tr></thead>
            <tbody>
              {users.map((u) => (
                <tr key={u.id}>
                  <td>{u.username}</td>
                  <td>{u.display_name || '—'}</td>
                  <td>{{ admin: 'полный доступ', vika: 'Вика — личный кабинет', tanya: 'Таня — офисный пул', office: 'офис (устаревшая)' }[u.role] || u.role}</td>
                  <td>{u.active ? <span className="badge green">активен</span> : <span className="badge red">отключён</span>}</td>
                  <td>{u.chat_bound ? <span className="badge green">привязан</span> : <span className="badge">нет</span>}</td>
                  <td style={{ whiteSpace: 'nowrap' }}>
                    <button className="ghost" onClick={() => changePassword(u)}>Пароль</button>{' '}
                    {u.id !== me.id && <button className="ghost" onClick={() => toggleUser(u)}>{u.active ? 'Отключить' : 'Включить'}</button>}{' '}
                    {u.id !== me.id && <button className="ghost" onClick={() => deleteUser(u)}>Удалить</button>}
                  </td>
                </tr>
              ))}
            </tbody>
          </table>
        </div>
        <div className="row" style={{ marginTop: 14 }}>
          <div className="field"><label>Логин</label><input value={newUser.username} onChange={(e) => setNewUser({ ...newUser, username: e.target.value })} /></div>
          <div className="field"><label>Имя</label><input value={newUser.display_name} onChange={(e) => setNewUser({ ...newUser, display_name: e.target.value })} /></div>
          <div className="field"><label>Пароль (8+)</label><input value={newUser.password} onChange={(e) => setNewUser({ ...newUser, password: e.target.value })} /></div>
          <div className="field"><label>Роль</label>
            <select value={newUser.role} onChange={(e) => setNewUser({ ...newUser, role: e.target.value })}>
              <option value="vika">Вика — личный кабинет</option>
              <option value="tanya">Таня — офисный пул</option>
              <option value="admin">полный доступ</option>
            </select>
          </div>
          <button className="primary" onClick={addUser}>Добавить</button>
        </div>
      </div>
    </div>
  );
}

// ==================== БУХГАЛТЕРИЯ ====================
const EMP_LABEL = { diana: 'Диана', vika: 'Вика', tanya: 'Таня' };
const KIND_RU = { 'НБ': 'НБ', 'пр': 'пролонгация' };
const CONTROL_LABEL = {
  ok: ['✓ Верно', 'ok'], split_ok: ['✓ Разбито верно', 'ok'], partial: ['⏳ Частично — ждём остаток', ''],
  over: ['⚠ Переначисление', 'red'], ambiguous: ['⚠ Неоднозначно', 'red'], no_rate: ['Нет ставки', 'red'],
};

function PeriodPicker({ from, to, set }) {
  const monthStart = () => new Date().toISOString().slice(0, 7) + '-01';
  const prevMonth = () => {
    const d = new Date(); d.setDate(1); d.setMonth(d.getMonth() - 1);
    const s = d.toISOString().slice(0, 7) + '-01';
    const e = new Date(d.getFullYear(), d.getMonth() + 1, 0).toISOString().slice(0, 10);
    return [s, e];
  };
  return (
    <div className="row" style={{ marginBottom: 14, alignItems: 'flex-end' }}>
      <div className="field"><label>Период с</label><input type="date" value={from} onChange={(e) => set({ from: e.target.value, to })} /></div>
      <div className="field"><label>по</label><input type="date" value={to} onChange={(e) => set({ from, to: e.target.value })} /></div>
      <button className="ghost" onClick={() => set({ from: monthStart(), to: new Date().toISOString().slice(0, 10) })}>Этот месяц</button>
      <button className="ghost" onClick={() => set({ from: prevMonth()[0], to: prevMonth()[1] })}>Прошлый месяц</button>
      <button className="ghost" onClick={() => set({ from: '2000-01-01', to: '2099-12-31' })}>Всё время</button>
    </div>
  );
}

// ---------- Офис ----------
function OfficeTab({ period, notify }) {
  const [data, setData] = useState({ items: [] });
  const [form, setForm] = useState({ contract_date: new Date().toISOString().slice(0, 10), number: '', client_name: '', product: '', kind: 'НБ', premium: '' });
  const load = useCallback(async () => {
    try { setData(await api('/sal/office?from=' + period.from + '&to=' + period.to)); }
    catch (e) { notify(e.message); }
  }, [period.from, period.to]);
  useEffect(() => { load(); }, [load]);
  const add = async () => {
    try { await api('/sal/office', { method: 'POST', body: form }); setForm({ ...form, number: '', client_name: '', premium: '' }); load(); }
    catch (e) { notify(e.message); }
  };
  const del = async (id) => {
    if (!window.confirm('Удалить запись?')) return;
    try { await api('/sal/office/' + id, { method: 'DELETE' }); load(); }
    catch (e) { notify(e.message); }
  };
  const totals = data.items.reduce((s, x) => ({ d: s.d + x.dianaNet, v: s.v + x.vikaNet, t: s.t + x.tanyaNet, p: s.p + Number(x.premium || 0) }), { d: 0, v: 0, t: 0, p: 0 });
  return (
    <div className="card">
      <div className="cardhead"><h2>Офис — договоры ({data.items.length})</h2></div>
      <PeriodPicker from={period.from} to={period.to} set={period.set} />
      <div className="row" style={{ marginBottom: 12 }}>
        <div className="field"><label>Дата</label><input type="date" value={form.contract_date} onChange={(e) => setForm({ ...form, contract_date: e.target.value })} /></div>
        <div className="field"><label>№ договора</label><input value={form.number} onChange={(e) => setForm({ ...form, number: e.target.value })} style={{ width: 130 }} /></div>
        <div className="field"><label>Клиент</label><input value={form.client_name} onChange={(e) => setForm({ ...form, client_name: e.target.value })} style={{ width: 190 }} /></div>
        <div className="field"><label>Продукт</label><input value={form.product} onChange={(e) => setForm({ ...form, product: e.target.value })} style={{ width: 140 }} /></div>
        <div className="field"><label>Тип</label>
          <select value={form.kind} onChange={(e) => setForm({ ...form, kind: e.target.value })}>
            <option value="НБ">НБ</option><option value="пр">пролонгация</option><option value="">—</option>
          </select></div>
        <div className="field"><label>Премия</label><input value={form.premium} onChange={(e) => setForm({ ...form, premium: e.target.value })} style={{ width: 100 }} /></div>
        <button className="primary" onClick={add}>Добавить</button>
      </div>
      <div className="tablewrap"><table>
        <thead><tr><th>Дата</th><th>№</th><th>Клиент</th><th>Продукт</th><th>Тип</th><th>Премия</th><th>КВ</th><th>Таня</th><th>Комиссия</th><th>Таня</th><th>Диана</th><th>Вика</th><th></th></tr></thead>
        <tbody>
          {data.items.map((x) => (
            <tr key={x.id}>
              <td className="num">{fmtDate(x.contract_date)}</td>
              <td className="num">{x.number || '—'}</td>
              <td>{x.client_name || '—'}</td>
              <td>{x.product}{!x.found && <span className="warn"> ⚠ нет ставки</span>}{x.invalidRate && <span className="warn"> ⚠ Таня&gt;КВ</span>}</td>
              <td>{KIND_RU[x.kind] || '—'}</td>
              <td className="num">{fmtMoney(x.premium)}</td>
              <td className="num">{x.found ? Math.round(x.kv * 100) + '%' : '—'}</td>
              <td className="num">{x.found ? x.tanyaPct * 100 + '%' : '—'}</td>
              <td className="num">{fmtMoney(x.profit)}</td>
              <td className="num">{fmtMoney(x.tanyaNet)}</td>
              <td className="num">{fmtMoney(x.dianaNet)}</td>
              <td className="num">{fmtMoney(x.vikaNet)}</td>
              <td><span className="del" onClick={() => del(x.id)}>×</span></td>
            </tr>
          ))}
          {data.items.length > 0 && (
            <tr style={{ fontWeight: 600 }}><td colSpan={5}>Итого</td><td className="num">{fmtMoney(totals.p)}</td><td colSpan={3}></td>
              <td className="num">{fmtMoney(totals.t)}</td><td className="num">{fmtMoney(totals.d)}</td><td className="num">{fmtMoney(totals.v)}</td><td colSpan={2}></td></tr>
          )}
          {!data.items.length && <tr><td colSpan={13} className="mute" style={{ textAlign: 'center' }}>Записей за период нет</td></tr>}
        </tbody>
      </table></div>
    </div>
  );
}

// ---------- 50/50 ----------
function FiftyTab({ period, notify }) {
  const [data, setData] = useState({ items: [] });
  const [form, setForm] = useState({ contract_date: new Date().toISOString().slice(0, 10), number: '', client_name: '', product: '', kind: '', premium: '' });
  const load = useCallback(async () => {
    try { setData(await api('/sal/fifty?from=' + period.from + '&to=' + period.to)); }
    catch (e) { notify(e.message); }
  }, [period.from, period.to]);
  useEffect(() => { load(); }, [load]);
  const add = async () => {
    try { await api('/sal/fifty', { method: 'POST', body: form }); setForm({ ...form, number: '', client_name: '', premium: '' }); load(); }
    catch (e) { notify(e.message); }
  };
  const del = async (id) => { if (!window.confirm('Удалить запись?')) return; try { await api('/sal/fifty/' + id, { method: 'DELETE' }); load(); } catch (e) { notify(e.message); } };
  const t = data.items.reduce((s, x) => ({ p: s.p + Number(x.premium || 0), d: s.d + x.dianaNet, v: s.v + x.vikaNet }), { p: 0, d: 0, v: 0 });
  return (
    <div className="card">
      <div className="cardhead"><h2>50/50 — договоры ({data.items.length})</h2></div>
      <PeriodPicker from={period.from} to={period.to} set={period.set} />
      <div className="row" style={{ marginBottom: 12 }}>
        <div className="field"><label>Дата</label><input type="date" value={form.contract_date} onChange={(e) => setForm({ ...form, contract_date: e.target.value })} /></div>
        <div className="field"><label>№ договора</label><input value={form.number} onChange={(e) => setForm({ ...form, number: e.target.value })} style={{ width: 130 }} /></div>
        <div className="field"><label>Клиент</label><input value={form.client_name} onChange={(e) => setForm({ ...form, client_name: e.target.value })} style={{ width: 190 }} /></div>
        <div className="field"><label>Продукт</label><input value={form.product} onChange={(e) => setForm({ ...form, product: e.target.value })} style={{ width: 140 }} /></div>
        <div className="field"><label>Премия</label><input value={form.premium} onChange={(e) => setForm({ ...form, premium: e.target.value })} style={{ width: 100 }} /></div>
        <button className="primary" onClick={add}>Добавить</button>
      </div>
      <div className="tablewrap"><table>
        <thead><tr><th>Дата</th><th>№</th><th>Клиент</th><th>Продукт</th><th>Премия</th><th>КВ</th><th>Комиссия</th><th>Диана</th><th>Вика</th><th></th></tr></thead>
        <tbody>
          {data.items.map((x) => (
            <tr key={x.id}>
              <td className="num">{fmtDate(x.contract_date)}</td><td className="num">{x.number || '—'}</td><td>{x.client_name || '—'}</td>
              <td>{x.product}{!x.found && <span className="warn"> ⚠ нет ставки</span>}</td>
              <td className="num">{fmtMoney(x.premium)}</td><td className="num">{x.found ? Math.round(x.kv * 100) + '%' : '—'}</td>
              <td className="num">{fmtMoney(x.commission)}</td><td className="num">{fmtMoney(x.dianaNet)}</td><td className="num">{fmtMoney(x.vikaNet)}</td>
              <td><span className="del" onClick={() => del(x.id)}>×</span></td>
            </tr>
          ))}
          {data.items.length > 0 && <tr style={{ fontWeight: 600 }}><td colSpan={4}>Итого</td><td className="num">{fmtMoney(t.p)}</td><td></td><td></td><td className="num">{fmtMoney(t.d)}</td><td className="num">{fmtMoney(t.v)}</td><td></td></tr>}
          {!data.items.length && <tr><td colSpan={10} className="mute" style={{ textAlign: 'center' }}>Записей за период нет</td></tr>}
        </tbody>
      </table></div>
    </div>
  );
}

// ---------- Сопровождение (акты) ----------
function ActsTab({ period, notify }) {
  const [acts, setActs] = useState({ items: [] });
  const [control, setControl] = useState({ items: [] });
  const [preview, setPreview] = useState(null);
  const [busy, setBusy] = useState(false);
  const [open, setOpen] = useState(null);

  const load = useCallback(async () => {
    try {
      const [a, c] = await Promise.all([
        api('/sal/acts?from=' + period.from + '&to=' + period.to),
        api('/sal/commission-control'),
      ]);
      setActs(a); setControl(c);
    } catch (e) { notify(e.message); }
  }, [period.from, period.to]);
  useEffect(() => { load(); }, [load]);

  const uploadPdf = async (e) => {
    const file = e.target.files[0];
    if (!file) return;
    setBusy(true);
    try {
      const fd = new FormData();
      fd.append('file', file);
      const res = await fetch('/api/sal/acts/pdf', { method: 'POST', body: fd, credentials: 'same-origin' });
      const data = await res.json();
      if (!res.ok) throw new Error(data.error || 'Ошибка разбора PDF');
      setPreview(data);
      notify(`Акт ${data.actNumber}: строк ${data.rows.length}${data.duplicate ? ' · ⚠ акт с таким номером уже сохранён!' : ''}`);
    } catch (ex) { notify(ex.message); }
    finally { setBusy(false); e.target.value = ''; }
  };
  const saveAct = async () => {
    try {
      await api('/sal/acts/save', { method: 'POST', body: { act: preview, items: preview.rows } });
      setPreview(null);
      notify('Акт сохранён');
      load();
    } catch (e) { notify(e.message); }
  };
  const delAct = async (id) => {
    if (!window.confirm('Удалить акт со всеми строками?')) return;
    try { await api('/sal/acts/' + id, { method: 'DELETE' }); load(); }
    catch (e) { notify(e.message); }
  };

  return (
    <div>
      <div className="card">
        <div className="cardhead"><h2>Загрузка акта PDF</h2>
          <label className="ghost" style={{ display: 'inline-block', cursor: 'pointer', padding: '7px 12px' }}>
            {busy ? 'Читаю PDF…' : '⬆ Выбрать PDF-акт'}
            <input type="file" accept="application/pdf" style={{ display: 'none' }} onChange={uploadPdf} />
          </label>
        </div>
        <div className="mute">Номер акта, период, суммы и строки распознаются автоматически; продукт — по префиксу номера договора. Проверка процента — по справочнику «КВ ГА за сопровождение».</div>
        {preview && (
          <div style={{ marginTop: 12 }}>
            <div style={{ display: 'flex', gap: 16, flexWrap: 'wrap', fontSize: 13, marginBottom: 8 }}>
              <span><b>№:</b> {preview.actNumber}</span>
              <span><b>Дата:</b> {preview.actDate}</span>
              <span><b>Период:</b> {preview.periodFrom} — {preview.periodTo}</span>
              <span><b>Акт:</b> {fmtMoney(preview.amount)}</span>
              <span><b>По строкам:</b> {fmtMoney(preview.rows.reduce((s, r) => s + r.actual_reward, 0))}</span>
              {Math.abs(preview.rows.reduce((s, r) => s + r.actual_reward, 0) - preview.amount) > 0.02
                ? <span className="warn">⚠ расхождение {fmtMoney(preview.rows.reduce((s, r) => s + r.actual_reward, 0) - preview.amount)}</span>
                : <span className="ok">✓ суммы сходятся</span>}
              {preview.duplicate && <span className="warn">⚠ акт с таким номером уже есть</span>}
            </div>
            <div className="tablewrap"><table>
              <thead><tr><th>№</th><th>№ договора</th><th>ФИО</th><th>Продукт</th><th>Премия</th><th>% в акте</th><th>Ожидается</th><th>Проверка</th></tr></thead>
              <tbody>
                {preview.rows.map((r, i) => (
                  <tr key={i}>
                    <td className="num">{r.row_no}</td><td className="num">{r.contract_number}</td><td>{r.client_name}</td>
                    <td>{r.product || <span className="warn">неизвестный код</span>}</td>
                    <td className="num">{fmtMoney(r.premium)}</td>
                    <td className="num">{r.actual_rate}%</td>
                    <td>{r.expected_rate}</td>
                    <td>{r.rate_status === 'ok' ? <span className="ok">✓ {r.kind_guess || 'Верно'}</span> : <span className="warn">✕ {r.kind_guess || 'Проверить'}</span>}</td>
                  </tr>
                ))}
              </tbody>
            </table></div>
            <div style={{ display: 'flex', gap: 8, marginTop: 10 }}>
              <button className="primary" onClick={saveAct}>Сохранить акт</button>
              <button className="ghost" onClick={() => setPreview(null)}>Отмена</button>
            </div>
          </div>
        )}
      </div>

      <div className="card">
        <div className="cardhead"><h2>Акты сопровождения ({acts.items.length})</h2></div>
        <PeriodPicker from={period.from} to={period.to} set={period.set} />
        <div className="tablewrap"><table>
          <thead><tr><th>Дата</th><th>№ акта</th><th>Сумма</th><th>После налога</th><th>Пул</th><th>Диана</th><th>Вика</th><th>Таня</th><th>Строки</th><th></th></tr></thead>
          <tbody>
            {acts.items.map((a) => {
              const mismatch = Math.abs((a.items_sum || 0) - a.amount) > 0.02;
              return (
                <React.Fragment key={a.id}>
                  <tr>
                    <td className="num">{fmtDate(a.act_date)}</td>
                    <td className="num" style={{ cursor: a.items.length ? 'pointer' : 'default', color: a.items.length ? 'var(--teal)' : undefined }} onClick={() => setOpen(open === a.id ? null : a.id)}>{a.number}</td>
                    <td className="num">{fmtMoney(a.amount)}</td>
                    <td className="num">{fmtMoney(a.calc.afterTax)}</td>
                    <td className="num">{fmtMoney(a.calc.pool)}</td>
                    <td className="num">{fmtMoney(a.calc.split.diana)}</td>
                    <td className="num">{fmtMoney(a.calc.split.vika)}</td>
                    <td className="num">{fmtMoney(a.calc.split.tanya)}</td>
                    <td>{a.items.length ? `${a.items.length} (${fmtMoney(a.items_sum)}${mismatch ? ' ⚠' : ''})` : '—'}</td>
                    <td><span className="del" onClick={() => delAct(a.id)}>×</span></td>
                  </tr>
                  {!a.calc.shareOk && <tr><td colSpan={10} className="warn">Сумма долей {a.calc.shareSum}% ≠ 100% — проверьте настройки</td></tr>}
                  {open === a.id && a.items.length > 0 && (
                    <tr><td colSpan={10} style={{ padding: 0, background: 'var(--paper)' }}>
                      <div style={{ padding: '10px 12px' }}>
                        <div className="tablewrap"><table>
                          <thead><tr><th>№ договора</th><th>ФИО</th><th>Продукт</th><th>Тип</th><th>Премия</th><th>% в акте</th><th>Ожидается</th><th>Проверка</th></tr></thead>
                          <tbody>
                            {a.items.map((it) => (
                              <tr key={it.id}>
                                <td className="num">{it.contract_number}</td><td>{it.client_name}</td><td>{it.product || '—'}</td>
                                <td>{it.kind_display}</td><td className="num">{fmtMoney(it.premium)}</td>
                                <td className="num">{it.actual_rate}%</td><td>{it.expected_display || '—'}</td>
                                <td>{it.check_status === 'ok' ? <span className="ok">✓</span> : <span className="warn">✕ {it.kind_display}</span>}</td>
                              </tr>
                            ))}
                          </tbody>
                        </table></div>
                      </div>
                    </td></tr>
                  )}
                </React.Fragment>
              );
            })}
            {!acts.items.length && <tr><td colSpan={10} className="mute" style={{ textAlign: 'center' }}>Актов за период нет</td></tr>}
          </tbody>
        </table></div>
      </div>

      <div className="card">
        <div className="cardhead"><h2>Контроль комиссий ({control.items.length})</h2></div>
        <div className="mute" style={{ marginBottom: 8 }}>Договоры с разбитой комиссией и замечаниями. Итог по договору сверяется со ставкой его типа (тип берётся из CRM) или с допустимыми ставками продукта.</div>
        <div className="tablewrap"><table>
          <thead><tr><th>№ договора</th><th>ФИО</th><th>Продукт</th><th>Акты и %</th><th>Итого %</th><th>Допустимо</th><th>Статус</th></tr></thead>
          <tbody>
            {control.items.map((g, i) => (
              <tr key={i}>
                <td className="num">{g.number}</td><td>{g.client_name}</td><td>{g.product || '—'}</td>
                <td className="num">{g.acts.map((x) => x.rate + '%').join(' + ')}</td>
                <td className="num" style={{ fontWeight: 600 }}>{g.totalRate}%</td>
                <td className="num">{g.allowedMin === g.allowedMax ? g.allowedMax + '%' : g.allowedMin + '–' + g.allowedMax + '%'}</td>
                <td><span className={CONTROL_LABEL[g.status]?.[1] === 'ok' ? 'ok' : 'warn'}>{CONTROL_LABEL[g.status]?.[0] || g.status}{g.hasItemError ? ' · есть строка с ошибкой' : ''}</span></td>
              </tr>
            ))}
            {!control.items.length && <tr><td colSpan={7} className="mute" style={{ textAlign: 'center' }}>Всё сходится</td></tr>}
          </tbody>
        </table></div>
      </div>
    </div>
  );
}

// ---------- Смены ----------
function ShiftsTab({ period, notify }) {
  const [data, setData] = useState({ items: [], calc: { totalShifts: 0, amount: 0, delta: {} }, shift_rate: 0 });
  const [form, setForm] = useState({ shift_date: new Date().toISOString().slice(0, 10), count: 1, comment: '' });
  const load = useCallback(async () => {
    try { setData(await api('/sal/shifts?from=' + period.from + '&to=' + period.to)); }
    catch (e) { notify(e.message); }
  }, [period.from, period.to]);
  useEffect(() => { load(); }, [load]);
  const add = async () => {
    try { await api('/sal/shifts', { method: 'POST', body: form }); load(); }
    catch (e) { notify(e.message); }
  };
  const del = async (id) => { try { await api('/sal/shifts/' + id, { method: 'DELETE' }); load(); } catch (e) { notify(e.message); } };
  return (
    <div className="card">
      <div className="cardhead"><h2>Смены (ставка {fmtMoney(data.shift_rate)})</h2></div>
      <PeriodPicker from={period.from} to={period.to} set={period.set} />
      <div className="row" style={{ marginBottom: 12 }}>
        <div className="field"><label>Дата</label><input type="date" value={form.shift_date} onChange={(e) => setForm({ ...form, shift_date: e.target.value })} /></div>
        <div className="field"><label>Смен</label><input value={form.count} onChange={(e) => setForm({ ...form, count: e.target.value })} style={{ width: 60 }} /></div>
        <div className="field"><label>Комментарий</label><input value={form.comment} onChange={(e) => setForm({ ...form, comment: e.target.value })} /></div>
        <button className="primary" onClick={add}>Добавить</button>
      </div>
      <div className="tablewrap"><table>
        <thead><tr><th>Дата</th><th>Смен</th><th>Комментарий</th><th></th></tr></thead>
        <tbody>
          {data.items.map((s) => (
            <tr key={s.id}><td className="num">{fmtDate(s.shift_date)}</td><td className="num">{s.count}</td><td>{s.comment || '—'}</td><td><span className="del" onClick={() => del(s.id)}>×</span></td></tr>
          ))}
          {!data.items.length && <tr><td colSpan={4} className="mute" style={{ textAlign: 'center' }}>Смен за период нет</td></tr>}
        </tbody>
        {data.items.length > 0 && <tfoot><tr style={{ fontWeight: 600 }}><td>Итого</td><td className="num">{data.calc.totalShifts} смен · {fmtMoney(data.calc.amount)}</td><td colSpan={2} className="num">Таня +{fmtMoney(data.calc.delta.tanya)} · Диана {fmtMoney(data.calc.delta.diana)} · Вика {fmtMoney(data.calc.delta.vika)}</td></tr></tfoot>}
      </table></div>
    </div>
  );
}

// ---------- Сводка ----------
function SummaryTab({ period, notify }) {
  const [data, setData] = useState(null);
  const [showDetails, setShowDetails] = useState(false);
  const load = useCallback(async () => {
    try { setData(await api('/sal/summary?from=' + period.from + '&to=' + period.to)); }
    catch (e) { notify(e.message); }
  }, [period.from, period.to]);
  useEffect(() => { load(); }, [load]);
  if (!data) return <div className="card mute">Загрузка…</div>;
  return (
    <div>
      <div className="card">
        <div className="cardhead"><h2>Сводка по сотрудникам</h2>
          <a className="ghost" style={{ textDecoration: 'none', padding: '7px 12px' }}
             href={'/api/sal/export.xlsx?from=' + period.from + '&to=' + period.to}>⬇ Excel-отчёт</a>
        </div>
        <PeriodPicker from={period.from} to={period.to} set={period.set} />
        {data.missingRates.length > 0 && (
          <div className="warn" style={{ marginBottom: 10 }}>Нет ставок для продуктов: {data.missingRates.join(', ')} — эти договоры не участвуют в расчёте. Добавьте ставки в Настройках.</div>
        )}
        <div className="tablewrap"><table>
          <thead><tr><th>Сотрудник</th><th>Офис</th><th>Сопровождение</th><th>50/50</th><th>Смены</th><th>Итого</th></tr></thead>
          <tbody>
            {['diana', 'vika', 'tanya'].map((emp) => {
              const s = data.summary[emp];
              return (
                <tr key={emp}>
                  <td style={{ fontWeight: 500 }}>{EMP_LABEL[emp]}</td>
                  <td className="num">{fmtMoney(s.office)}</td>
                  <td className="num">{fmtMoney(s.agents)}</td>
                  <td className="num">{fmtMoney(s.fifty)}</td>
                  <td className="num">{fmtMoney(s.shift)}</td>
                  <td className="num" style={{ fontWeight: 600 }}>{fmtMoney(s.total)}</td>
                </tr>
              );
            })}
            <tr style={{ fontWeight: 600 }}><td>Баланс (сумма всех)</td><td colSpan={4}></td><td className="num">{fmtMoney(data.summary.grandTotal)}</td></tr>
          </tbody>
        </table></div>
        <div className="mute" style={{ marginTop: 8 }}>
          Договоров: офис {data.counts.office} · 50/50 {data.counts.fifty} · актов {data.counts.acts} · смен {data.counts.shifts}.
          «Итого» по столбцам — начислено каждому сотруднику; общая сумма равна совокупному доходу команды за период.
        </div>
      </div>
      <div className="card">
        <div className="cardhead"><h2>Детализация «Офис» ({data.details.office.length})</h2>
          <button className="ghost" onClick={() => setShowDetails(!showDetails)}>{showDetails ? 'Скрыть' : 'Показать'}</button>
        </div>
        {showDetails && (
          <div className="tablewrap"><table>
            <thead><tr><th>Дата</th><th>№</th><th>Клиент</th><th>Продукт</th><th>Тип</th><th>Премия</th><th>КВ</th><th>Таня</th><th>Диана</th><th>Вика</th></tr></thead>
            <tbody>
              {data.details.office.map((x) => (
                <tr key={x.id}>
                  <td className="num">{fmtDate(x.contract_date)}</td><td className="num">{x.number || '—'}</td><td>{x.client_name}</td>
                  <td>{x.product}</td><td>{KIND_RU[x.kind] || '—'}</td><td className="num">{fmtMoney(x.premium)}</td>
                  <td className="num">{x.kv * 100}%</td><td className="num">{fmtMoney(x.tanyaNet)}</td>
                  <td className="num">{fmtMoney(x.dianaNet)}</td><td className="num">{fmtMoney(x.vikaNet)}</td>
                </tr>
              ))}
            </tbody>
          </table></div>
        )}
      </div>
    </div>
  );
}

// ---------- Аналитика ----------
function AnalyticsTab({ period, notify }) {
  const [data, setData] = useState(null);
  const [sub, setSub] = useState('total');
  const load = useCallback(async () => {
    try { setData(await api('/sal/analytics?from=' + period.from + '&to=' + period.to)); }
    catch (e) { notify(e.message); }
  }, [period.from, period.to]);
  useEffect(() => { load(); }, [load]);
  if (!data) return <div className="card mute">Загрузка…</div>;
  const sections = [
    ['office', 'Офис', data.office], ['agents', 'Сопровождение', data.agents], ['fifty', '50/50', data.fifty],
  ];
  const Structure = ({ rows }) => (
    <div className="tablewrap"><table>
      <thead><tr><th>Продукт</th><th>Договоров</th><th>НБ</th><th>Пролонг.</th><th>Премия</th><th>Доля портфеля</th><th>Комиссия</th></tr></thead>
      <tbody>
        {rows.map((p) => (
          <tr key={p.product}>
            <td>{p.product}</td><td className="num">{p.count}</td><td className="num">{p.nb}</td><td className="num">{p.pr}</td>
            <td className="num">{fmtMoney(p.premium)}</td><td className="num">{p.share}%</td><td className="num">{fmtMoney(p.commission)}</td>
          </tr>
        ))}
        {!rows.length && <tr><td colSpan={7} className="mute" style={{ textAlign: 'center' }}>Данных нет</td></tr>}
      </tbody>
    </table></div>
  );
  return (
    <div className="card">
      <div className="cardhead"><h2>Аналитика</h2></div>
      <PeriodPicker from={period.from} to={period.to} set={period.set} />
      <div className="tabs" style={{ marginBottom: 14 }}>
        {[['total', 'Общая'], ['office', 'Офис'], ['agents', 'Сопровождение'], ['fifty', '50/50']].map(([id, label]) => (
          <button key={id} className="tab" onClick={() => setSub(id)}
            style={{ background: sub === id ? 'var(--ink)' : 'transparent', color: sub === id ? 'var(--paper)' : 'var(--mute)' }}>{label}</button>
        ))}
      </div>
      {sub === 'total' && (
        <div className="tablewrap"><table>
          <thead><tr><th>Направление</th><th>Договоров</th><th>Премия</th><th>Комиссия/доход</th><th>После налога</th></tr></thead>
          <tbody>
            <tr><td>Офис</td><td className="num">{data.office.count}</td><td className="num">{fmtMoney(data.office.premium)}</td><td className="num">{fmtMoney(data.office.commission)}</td><td className="num">{fmtMoney(data.office.net)}</td></tr>
            <tr><td>Сопровождение</td><td className="num">{data.agents.count}</td><td className="num">{fmtMoney(data.agents.premium)}</td><td className="num">{fmtMoney(data.agents.amount)}</td><td className="num">{fmtMoney(data.agents.afterTax)}</td></tr>
            <tr><td>50/50</td><td className="num">{data.fifty.count}</td><td className="num">{fmtMoney(data.fifty.premium)}</td><td className="num">{fmtMoney(data.fifty.commission)}</td><td className="num">{fmtMoney(data.fifty.net)}</td></tr>
          </tbody>
        </table></div>
      )}
      {sub === 'office' && <Structure rows={data.office.structure} />}
      {sub === 'fifty' && <Structure rows={data.fifty.structure} />}
      {sub === 'agents' && (
        <div className="tablewrap"><table>
          <thead><tr><th>Показатель</th><th>Значение</th></tr></thead>
          <tbody>
            <tr><td>Актов</td><td className="num">{data.agents.count}</td></tr>
            <tr><td>Премия по заголовкам актов</td><td className="num">{fmtMoney(data.agents.premium)}</td></tr>
            <tr><td>Сумма актов</td><td className="num">{fmtMoney(data.agents.amount)}</td></tr>
            <tr><td>После налога</td><td className="num">{fmtMoney(data.agents.afterTax)}</td></tr>
            <tr><td>Пул к распределению</td><td className="num">{fmtMoney(data.agents.net)}</td></tr>
          </tbody>
        </table></div>
      )}
    </div>
  );
}

// ---------- карточки настроек зарплаты ----------
function SalarySettingsCard({ notify }) {
  const [ctx, setCtx] = useState(null);
  const load = useCallback(async () => {
    try { setCtx(await api('/sal/context')); } catch (e) { notify(e.message); }
  }, []);
  useEffect(() => { load(); }, [load]);
  if (!ctx) return <div className="card mute">Загрузка…</div>;
  const s = ctx.settings;
  const put = async (patch) => {
    try { await api('/sal/settings', { method: 'PUT', body: patch }); notify('Сохранено'); load(); }
    catch (e) { notify(e.message); }
  };
  const shareSum = (Number(s.share_diana) || 0) + (Number(s.share_vika) || 0) + (Number(s.share_tanya) || 0);
  const numField = (label, key, width) => (
    <div className="field"><label>{label}</label>
      <input value={s[key]} onChange={(e) => setCtx({ ...ctx, settings: { ...s, [key]: e.target.value } })}
        onBlur={() => put({ [key]: Number(s[key]) || 0 })} style={{ width }} />
    </div>
  );
  const addRate = async () => { try { await api('/sal/rates', { method: 'POST', body: { product: 'Новый вид', kind: 'НБ', kv: 0.1, tanya: 0.05 } }); load(); } catch (e) { notify(e.message); } };
  const updRate = async (id, field, value) => {
    setCtx({ ...ctx, rates: ctx.rates.map((r) => r.id === id ? { ...r, [field]: value } : r) });
    try { await api('/sal/rates', { method: 'POST', body: { ...ctx.rates.find((r) => r.id === id), [field]: value } }); notify('Ставка обновлена и журналирована'); }
    catch (e) { notify(e.message); }
  };
  const delRate = async (id) => { if (!window.confirm('Удалить ставку?')) return; try { await api('/sal/rates/' + id, { method: 'DELETE' }); load(); } catch (e) { notify(e.message); } };
  const addAgentRate = async () => { try { await api('/sal/agent-rates', { method: 'POST', body: { product: 'Новый вид', kind: 'оба', kv_ga: 0.05 } }); load(); } catch (e) { notify(e.message); } };
  const updAgentRate = async (id, field, value) => {
    try { await api('/sal/agent-rates/' + id, { method: 'PUT', body: { [field]: value } }); load(); notify('Сохранено'); }
    catch (e) { notify(e.message); }
  };
  const delAgentRate = async (id) => { if (!window.confirm('Удалить ставку КВ ГА?')) return; try { await api('/sal/agent-rates/' + id, { method: 'DELETE' }); load(); } catch (e) { notify(e.message); } };
  const addCode = async () => {
    const code = prompt('Префикс номера (например PL):');
    if (!code) return;
    const product = prompt('Вид страхования:');
    if (!product) return;
    try { await api('/sal/codes', { method: 'POST', body: { code, product } }); load(); } catch (e) { notify(e.message); }
  };
  const delCode = async (id) => { try { await api('/sal/codes/' + id, { method: 'DELETE' }); load(); } catch (e) { notify(e.message); } };
  const closeMonth = async () => {
    const m = prompt('Месяц для закрытия (ГГГГ-ММ), например 2026-10:');
    if (!m) return;
    try { await api('/sal/close', { method: 'POST', body: { month: m } }); notify('Месяц закрыт'); load(); }
    catch (e) { notify(e.message); }
  };
  const reopenMonth = async (m) => {
    if (!window.confirm('Открыть месяц ' + m + ' для изменений?')) return;
    try { await api('/sal/close/' + m, { method: 'DELETE' }); load(); } catch (e) { notify(e.message); }
  };

  return (
    <div>
      <div className="card">
        <div className="cardhead"><h2>Зарплата: налоги, доли, смены</h2></div>
        <div className="row">
          {numField('Налог «Сопровождение», %', 'tax_agents', 70)}
          {numField('Налог «Офис», %', 'tax_contracts', 70)}
          {numField('Налог «50/50», %', 'tax_fiftyfifty', 70)}
          {numField('Фиксированный % пула сопровождения', 'agents_fixed_percent', 70)}
          {numField('Ставка за смену, ₽', 'shift_rate', 90)}
        </div>
        <div className="row" style={{ marginTop: 10 }}>
          {numField('Доля Дианы, %', 'share_diana', 70)}
          {numField('Доля Вики, %', 'share_vika', 70)}
          {numField('Доля Тани, %', 'share_tanya', 70)}
          <div style={{ alignSelf: 'flex-end', paddingBottom: 6 }}>
            {shareSum === 100 ? <span className="ok">сумма долей 100% ✓</span> : <span className="warn">сумма долей {shareSum}% ≠ 100%</span>}
          </div>
        </div>
        <div className="mute" style={{ marginTop: 8 }}>Изменения сохраняются при уходе из поля и записываются в журнал (кто/когда/старое→новое).</div>
      </div>

      <div className="card">
        <div className="cardhead"><h2>Ставки «Офиса» (КВ и % Тани)</h2><button className="ghost" onClick={addRate}>+ Ставка</button></div>
        <div className="tablewrap"><table>
          <thead><tr><th>Вид</th><th>Тип</th><th>КВ, %</th><th>Таня, %</th><th></th></tr></thead>
          <tbody>
            {ctx.rates.map((r) => (
              <tr key={r.id}>
                <td><input value={r.product} onChange={(e) => updRate(r.id, 'product', e.target.value)} style={{ width: 170 }} /></td>
                <td>
                  <select value={r.kind} onChange={(e) => updRate(r.id, 'kind', e.target.value)}>
                    <option value="НБ">НБ</option><option value="пр">пролонгация</option>
                  </select>
                </td>
                <td><input value={Math.round(r.kv * 100)} onChange={(e) => updRate(r.id, 'kv', (Number(e.target.value) || 0) / 100)} style={{ width: 60 }} /></td>
                <td><input value={Math.round((Number(r.tanya) || 0) * 10000) / 100} onChange={(e) => updRate(r.id, 'tanya', (Number(e.target.value) || 0) / 100)} style={{ width: 70 }} />
                  {Number(r.tanya) > Number(r.kv) && <div className="warn">Таня &gt; КВ</div>}</td>
                <td><span className="del" onClick={() => delRate(r.id)}>×</span></td>
              </tr>
            ))}
          </tbody>
        </table></div>
      </div>

      <div className="card">
        <div className="cardhead"><h2>КВ ГА за сопровождение</h2><button className="ghost" onClick={addAgentRate}>+ Ставка</button></div>
        <div className="mute" style={{ marginBottom: 8 }}>Проверка PDF-актов идёт только по этому справочнику. «Не зависит от типа» — одна ставка для НБ и пролонгации.</div>
        <div className="tablewrap"><table>
          <thead><tr><th>Вид</th><th>Тип</th><th>КВ ГА, %</th><th>Условие/примечание</th><th></th></tr></thead>
          <tbody>
            {ctx.agentRates.map((r) => (
              <tr key={r.id}>
                <td><input value={r.product} onChange={(e) => updAgentRate(r.id, 'product', e.target.value)} style={{ width: 170 }} /></td>
                <td>
                  <select value={r.kind} onChange={(e) => updAgentRate(r.id, 'kind', e.target.value)}>
                    <option value="НБ">НБ</option><option value="пр">Пролонгация</option><option value="оба">Не зависит от типа</option>
                  </select>
                </td>
                <td><input value={Number(r.kv_ga) * 100} onChange={(e) => updAgentRate(r.id, 'kv_ga', (Number(e.target.value) || 0) / 100)} style={{ width: 70 }} /></td>
                <td><input value={r.note || ''} onChange={(e) => updAgentRate(r.id, 'note', e.target.value)} style={{ width: 240 }} /></td>
                <td><span className="del" onClick={() => delAgentRate(r.id)}>×</span></td>
              </tr>
            ))}
          </tbody>
        </table></div>
      </div>

      <div className="card">
        <div className="cardhead"><h2>Префиксы договоров → продукт</h2><button className="ghost" onClick={addCode}>+ Код</button></div>
        <div className="tablewrap"><table>
          <thead><tr><th>Начало номера</th><th>Вид страхования</th><th></th></tr></thead>
          <tbody>
            {ctx.codes.map((r) => (
              <tr key={r.id}><td className="num" style={{ fontWeight: 600 }}>{r.code}</td><td>{r.product}</td>
                <td><span className="del" onClick={() => delCode(r.id)}>×</span></td></tr>
            ))}
          </tbody>
        </table></div>
      </div>

      <div className="card">
        <div className="cardhead"><h2>Закрытые месяцы</h2><button className="ghost" onClick={closeMonth}>🔒 Закрыть месяц</button></div>
        <div className="mute" style={{ marginBottom: 8 }}>В закрытом месяце нельзя менять договоры, акты и смены. Открытие — с кнопки ниже.</div>
        <div style={{ display: 'flex', gap: 8, flexWrap: 'wrap' }}>
          {ctx.closed.map((c) => (
            <span key={c.month} className="badge red" style={{ cursor: 'pointer' }} onClick={() => reopenMonth(c.month)}>🔒 {c.month} ✕</span>
          ))}
          {!ctx.closed.length && <span className="mute">Закрытых месяцев нет</span>}
        </div>
      </div>
    </div>
  );
}

// ==================== ДИАГРАММЫ ====================
const PALETTE = ['#2F6F62', '#B8892B', '#A34632', '#5B7DA3', '#7B5BA3', '#3F8F6B', '#A3795B', '#6B7A75'];

function Donut({ parts, size = 150, center }) {
  const total = parts.reduce((s, p) => s + p.value, 0);
  if (!total) return <div className="mute">Нет данных</div>;
  let acc = 0;
  const stops = parts.map((p, i) => {
    const from = acc / total * 100;
    acc += p.value;
    const to = acc / total * 100;
    return `${p.color || PALETTE[i % PALETTE.length]} ${from}% ${to}%`;
  });
  return (
    <div style={{ display: 'flex', gap: 16, alignItems: 'center', flexWrap: 'wrap' }}>
      <div style={{ width: size, height: size, borderRadius: '50%', background: `conic-gradient(${stops.join(',')})`, position: 'relative', flexShrink: 0 }}>
        <div style={{ position: 'absolute', inset: size * 0.22, background: 'var(--card)', borderRadius: '50%', display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: 12, textAlign: 'center', padding: 6 }}>{center}</div>
      </div>
      <div style={{ fontSize: 12 }}>
        {parts.map((p, i) => (
          <div key={p.label} style={{ display: 'flex', alignItems: 'center', gap: 6, marginBottom: 3 }}>
            <span style={{ width: 10, height: 10, borderRadius: 2, background: p.color || PALETTE[i % PALETTE.length], display: 'inline-block' }} />
            <span>{p.label}</span>
            <b className="num">{p.value.toLocaleString('ru-RU')}</b>
            <span className="mute">({Math.round(100 * p.value / total)}%)</span>
          </div>
        ))}
      </div>
    </div>
  );
}

function MonthBars({ items }) {
  if (!items.length) return <div className="mute">Нет данных</div>;
  const max = Math.max(...items.map((i) => i.premium), 1);
  const monthName = (m) => ['янв', 'фев', 'мар', 'апр', 'май', 'июн', 'июл', 'авг', 'сен', 'окт', 'ноя', 'дек'][+m.slice(5, 7) - 1];
  return (
    <div style={{ display: 'flex', gap: 6, alignItems: 'flex-end', height: 140, overflowX: 'auto' }}>
      {items.map((m) => (
        <div key={m.month} style={{ textAlign: 'center', minWidth: 38 }} title={`${m.month}: ${m.premium.toLocaleString('ru-RU')} ₽ · ${m.count} шт.`}>
          <div style={{ height: Math.max(3, Math.round(110 * m.premium / max)), background: 'var(--teal)', borderRadius: '4px 4px 0 0', opacity: 0.85 }} />
          <div style={{ fontSize: 10, color: 'var(--mute)' }}>{monthName(m.month)}</div>
        </div>
      ))}
    </div>
  );
}

// ==================== ДАШБОРД (админ) ====================
function DashboardTab({ me, openClient }) {
  const [d, setD] = useState(null);
  useEffect(() => { api('/dashboard').then(setD).catch(() => {}); }, []);
  if (!d) return <div className="card mute">Загрузка…</div>;
  const structureParts = d.structure.slice(0, 8).map((s, i) => ({ label: `${s.product} · ${Math.round(s.premium).toLocaleString('ru-RU')} ₽`, value: s.premium, color: PALETTE[i % PALETTE.length] }));
  const other = d.structure.slice(8).reduce((s, x) => s + x.premium, 0);
  if (other > 0) structureParts.push({ label: 'остальные', value: other });
  const nbPrParts = d.nbPr.map((x, i) => ({ label: `${x.kind} · ${x.count}`, value: x.count }));
  return (
    <div>
      <div className="statgrid">
        <div className="statbox"><div className="n">{d.counts.clients}</div><div className="t">Клиентов</div></div>
        <div className="statbox"><div className="n">{d.counts.policies}</div><div className="t">Полисов</div></div>
        <div className="statbox"><div className="n" style={{ color: 'var(--rust)' }}>{d.counts.expiring30}</div><div className="t">Окончание ≤ 30 дней</div></div>
        <div className="statbox"><div className="n">{d.birthdays.length}</div><div className="t">ДР за 7 дней</div></div>
      </div>
      <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(320px, 1fr))', gap: 16 }}>
        <div className="card">
          <div className="cardhead"><h2>Структура портфеля по видам</h2></div>
          <Donut parts={structureParts} center={<span>портфель<br />по премии</span>} />
        </div>
        <div className="card">
          <div className="cardhead"><h2>НБ и пролонгации</h2></div>
          <Donut parts={nbPrParts} size={120} center={<span>договоры</span>} />
          <div className="mute" style={{ marginTop: 8 }}>Всего договоров: {d.counts.policies}</div>
        </div>
        <div className="card" style={{ gridColumn: '1 / -1' }}>
          <div className="cardhead"><h2>Премия по месяцам заключения (12 мес.)</h2></div>
          <MonthBars items={d.months} />
        </div>
        <div className="card" style={{ gridColumn: '1 / -1' }}>
          <div className="cardhead"><h2>Топ агентов по премии</h2></div>
          <div className="tablewrap"><table>
            <thead><tr><th>Агент</th><th>Договоров</th><th>Премия</th></tr></thead>
            <tbody>
              {d.topAgents.map((a) => (
                <tr key={a.agent}><td>{a.agent}</td><td className="num">{a.count}</td><td className="num">{fmtMoney(a.premium)}</td></tr>
              ))}
              {!d.topAgents.length && <tr><td colSpan={3} className="mute">Нет данных</td></tr>}
            </tbody>
          </table></div>
        </div>
      </div>
      <div className="card">
        <div className="cardhead"><h2>Дни рождения в ближайшие 7 дней</h2></div>
        {d.birthdays.length ? (
          <div style={{ display: 'flex', gap: 14, flexWrap: 'wrap' }}>
            {d.birthdays.map((b) => <span key={b.id} className="badge green" style={{ fontSize: 12, padding: '4px 10px' }}>🎂 {b.full_name} — {fmtDate(b.birthday)}</span>)}
          </div>
        ) : <div className="mute">Дней рождения нет</div>}
      </div>
    </div>
  );
}

// ==================== БУХГАЛТЕРИЯ (объединённый блок) ====================
function AccountingTab({ period }) {
  const [sub, setSub] = useState('acts');
  const notify = (m) => console.log(m);
  return (
    <div>
      <div className="tabs" style={{ marginBottom: 14 }}>
        {[['acts', 'Сопровождение'], ['office', 'Офис'], ['fifty', '50/50'], ['shifts', 'Смены']].map(([id, label]) => (
          <button key={id} className="tab" onClick={() => setSub(id)}
            style={{ background: sub === id ? 'var(--ink)' : 'transparent', color: sub === id ? 'var(--paper)' : 'var(--mute)' }}>{label}</button>
        ))}
      </div>
      {sub === 'acts' && <ActsTab period={period} notify={notify} />}
      {sub === 'office' && <OfficeTab period={period} notify={notify} />}
      {sub === 'fifty' && <FiftyTab period={period} notify={notify} />}
      {sub === 'shifts' && <ShiftsTab period={period} notify={notify} />}
    </div>
  );
}

// ==================== ЛИЧНЫЕ КАБИНЕТЫ ====================
function ProfileCard({ me, onBound }) {
  const [p, setP] = useState(null);
  useEffect(() => { api('/my/profile').then(setP).catch(() => {}); }, []);
  if (!p) return null;
  if (p.chatBound) return <div className="card ok">✅ Telegram привязан — напоминания о ваших договорах будут приходить в бот.</div>;
  return (
    <div className="card">
      <div className="cardhead"><h2>Привяжите Telegram</h2></div>
      <div style={{ fontSize: 13 }}>
        1. Откройте нашего бота в Telegram (тот же, что присылает напоминания Диане).<br />
        2. Отправьте ему свой персональный код: <b className="num" style={{ fontSize: 18, letterSpacing: 2 }}>{p.bindCode}</b><br />
        3. Через пару минут чат привяжется автоматически — эта подсказка исчезнет.
      </div>
      <button className="ghost" style={{ marginTop: 10 }} onClick={() => { api('/my/profile').then(setP); onBound && onBound(); }}>Я отправил код — проверить</button>
    </div>
  );
}

function MyDashboardTab({ me }) {
  const [month, setMonth] = useState(new Date().toISOString().slice(0, 7));
  const [m, setM] = useState(null);
  const [conf, setConf] = useState({ items: [] });
  const load = useCallback(async () => {
    const [mm, cc] = await Promise.all([api('/my/metrics?month=' + month), api('/my/conflicts')]);
    setM(mm); setConf(cc);
  }, [month]);
  useEffect(() => { load(); }, [load]);
  if (!m) return <div className="card mute">Загрузка…</div>;
  return (
    <div>
      <ProfileCard me={me} />
      <div className="card">
        <div className="cardhead">
          <h2>{me.role === 'tanya' ? 'Договоры офиса' : 'Мои договоры'} — итоги месяца</h2>
          <input type="month" value={month} onChange={(e) => setMonth(e.target.value)} />
        </div>
        <div className="statgrid">
          <div className="statbox"><div className="n">{m.новый}</div><div className="t">Новых договоров</div></div>
          <div className="statbox"><div className="n" style={{ color: 'var(--teal)' }}>{m.пролонгация}</div><div className="t">Продлено</div></div>
          <div className="statbox"><div className="n" style={{ color: 'var(--gold)' }}>{m.доп}</div><div className="t">Доп. продукты</div></div>
          {me.role === 'tanya' && <div className="statbox"><div className="n" style={{ color: m.conflicts ? 'var(--rust)' : undefined }}>{m.conflicts}</div><div className="t">Конфликтов к проверке</div></div>}
        </div>
        <div className="mute">Считается по дате заключения договора: новый / пролонгация / доп. продукт — статус можно менять в списке договоров.</div>
      </div>
      {conf.items.length > 0 && (
        <div className="card">
          <div className="cardhead"><h2>Конфликты импорта ({conf.items.length})</h2></div>
          <div className="tablewrap"><table>
            <thead><tr><th>№ договора</th><th>Проблема</th><th>Дата</th></tr></thead>
            <tbody>
              {conf.items.map((c) => (
                <tr key={c.id}><td className="num">{c.number || '—'}</td><td className="warn">{c.reason}</td><td className="num">{new Date(c.created_at).toLocaleDateString('ru-RU')}</td></tr>
              ))}
            </tbody>
          </table></div>
          <div className="mute">Разрешает конфликты Диана в разделе «Импорт».</div>
        </div>
      )}
    </div>
  );
}

function MyContractsTab({ me, notify }) {
  const [items, setItems] = useState([]);
  const [q, setQ] = useState('');
  const [wt, setWt] = useState('');
  const [form, setForm] = useState({ number: '', client_name: '', product: '', kind: 'НБ', work_type: 'новый', work_kind: me.role === 'tanya' ? 'офис' : 'личный', start_date: new Date().toISOString().slice(0, 10), end_date: '' });
  const [msg, setMsg] = useState('');
  const [upBusy, setUpBusy] = useState(false);
  const [upResult, setUpResult] = useState(null);
  const load = useCallback(async () => {
    try { const d = await api('/my/contracts'); setItems(d.items); } catch (e) { notify(e.message); }
  }, []);
  useEffect(() => { load(); }, [load]);
  const upload = async (e) => {
    const file = e.target.files[0];
    if (!file) return;
    setUpBusy(true); setMsg(''); setUpResult(null);
    try {
      const fd = new FormData();
      fd.append('file', file);
      const res = await fetch('/api/my/import', { method: 'POST', body: fd, credentials: 'same-origin' });
      const data = await res.json();
      if (!res.ok) throw new Error(data.error || 'Ошибка импорта');
      setUpResult(data);
      load();
    } catch (ex) { setMsg(ex.message); }
    finally { setUpBusy(false); e.target.value = ''; }
  };
  const add = async () => {
    setMsg('');
    try {
      const r = await api('/my/contracts', { method: 'POST', body: form });
      setMsg(r.merged ? 'Договор с таким номером уже был — данные объединены' : 'Договор добавлен');
      setForm({ ...form, number: '', client_name: '', end_date: '' });
      load();
    } catch (e) { setMsg(e.message); }
  };
  const toggleKind = async (item) => {
    const wk = item.work_kind === 'офис' ? 'личный' : 'офис';
    try { await api('/my/contracts/' + item.id + '/workkind', { method: 'PUT', body: { work_kind: wk } }); load(); }
    catch (e) { notify(e.message); }
  };
  const setWorkType = async (item, wt) => {
    try {
      await api('/my/contracts', { method: 'POST', body: { number: item.number, client_name: item.client_name, product: item.product, kind: item.kind, work_type: wt, start_date: item.start_date, end_date: item.end_date, work_kind: item.work_kind } });
      load();
    } catch (e) { notify(e.message); }
  };
  const filtered = items.filter((x) =>
    (!q || (x.number || '').toLowerCase().includes(q.toLowerCase()) || (x.client_name || '').toLowerCase().includes(q.toLowerCase()))
    && (!wt || x.work_type === wt));
  return (
    <div>
      <div className="card">
        <div className="cardhead">
          <h2>{me.role === 'tanya' ? 'Договоры офиса' : 'Мои договоры'} ({filtered.length})</h2>
          <label className="ghost" style={{ display: 'inline-block', cursor: 'pointer', padding: '6px 10px' }}>
            {upBusy ? 'Загружаю…' : '⬆ Загрузить Excel с договорами'}
            <input type="file" accept=".xlsx,.xls" style={{ display: 'none' }} onChange={upload} />
          </label>
        </div>
        {upResult && (
          <div style={{ marginBottom: 10, fontSize: 13 }}>
            Файл распознан как <b>{upResult.kind}</b> · <span className="ok">новых: {upResult.counters.rows_inserted}</span> · обновлено: {upResult.counters.rows_updated}
            {upResult.counters.conflicts ? <span className="warn"> · конфликтов: {upResult.counters.conflicts} (показаны на Дашборде, Диана уведомлена)</span> : null}
          </div>
        )}
        {me.role === 'vika' && <div className="mute" style={{ marginBottom: 8 }}>Переключайте «личный / офис» напротив договора: офисные попадают в список Тани.</div>}
        <div className="row" style={{ marginBottom: 10 }}>
          <div className="field"><label>Поиск</label><input value={q} onChange={(e) => setQ(e.target.value)} style={{ width: 220 }} placeholder="№ или ФИО" /></div>
          <div className="field"><label>Статус</label>
            <select value={wt} onChange={(e) => setWt(e.target.value)}>
              <option value="">Все</option><option value="новый">Новые</option><option value="пролонгация">Пролонгации</option><option value="доп">Доп. продукты</option>
            </select></div>
        </div>
        <div className="tablewrap"><table>
          <thead><tr><th>№</th><th>Клиент</th><th>Продукт</th><th>Тип</th><th>Начало</th><th>Окончание</th>
            {me.role === 'vika' ? <th>Личный / офис</th> : <th>Категория</th>}
            <th>Статус работы</th></tr></thead>
          <tbody>
            {filtered.map((x) => (
              <tr key={x.id}>
                <td className="num">{x.number || '—'}</td>
                <td>{x.client_name || '—'}<div className="num mute">{(x.client_phones || []).join(', ')}</div></td>
                <td>{x.product || '—'}</td>
                <td>{KIND_RU[x.kind] || '—'}</td>
                <td className="num">{fmtDate(x.start_date)}</td>
                <td className="num">{fmtDate(x.end_date)}</td>
                {me.role === 'vika'
                  ? <td><button className="ghost" style={{ borderColor: x.work_kind === 'офис' ? 'var(--gold)' : 'var(--teal)', color: x.work_kind === 'офис' ? 'var(--gold)' : 'var(--teal)' }} onClick={() => toggleKind(x)}>{x.work_kind === 'офис' ? 'офис' : 'личный'}</button></td>
                  : <td>{x.work_kind === 'офис' ? <span className="badge">офис</span> : <span className="badge">личный</span>}</td>}
                <td>
                  <select value={x.work_type || ''} onChange={(e) => setWorkType(x, e.target.value)}>
                    <option value="">—</option><option value="новый">новый</option><option value="пролонгация">пролонгация</option><option value="доп">доп. продукт</option>
                  </select>
                </td>
              </tr>
            ))}
            {!filtered.length && <tr><td colSpan={8} className="mute" style={{ textAlign: 'center' }}>Договоров нет</td></tr>}
          </tbody>
        </table></div>
      </div>
      <div className="card">
        <div className="cardhead"><h2>Добавить договор вручную</h2></div>
        <div className="row">
          <div className="field"><label>№ договора</label><input value={form.number} onChange={(e) => setForm({ ...form, number: e.target.value })} style={{ width: 140 }} /></div>
          <div className="field"><label>Клиент</label><input value={form.client_name} onChange={(e) => setForm({ ...form, client_name: e.target.value })} style={{ width: 190 }} /></div>
          <div className="field"><label>Продукт</label><input value={form.product} onChange={(e) => setForm({ ...form, product: e.target.value })} style={{ width: 130 }} /></div>
          <div className="field"><label>Тип</label>
            <select value={form.kind} onChange={(e) => setForm({ ...form, kind: e.target.value })}>
              <option value="НБ">НБ (новый)</option><option value="пр">пролонгация</option>
            </select></div>
          <div className="field"><label>Статус</label>
            <select value={form.work_type} onChange={(e) => setForm({ ...form, work_type: e.target.value })}>
              <option value="новый">новый</option><option value="пролонгация">пролонгация</option><option value="доп">доп. продукт</option>
            </select></div>
          {me.role === 'vika' && (
            <div className="field"><label>Категория</label>
              <select value={form.work_kind} onChange={(e) => setForm({ ...form, work_kind: e.target.value })}>
                <option value="личный">личный</option><option value="офис">офис</option>
              </select></div>
          )}
          <div className="field"><label>Начало</label><input type="date" value={form.start_date} onChange={(e) => setForm({ ...form, start_date: e.target.value })} /></div>
          <div className="field"><label>Окончание</label><input type="date" value={form.end_date} onChange={(e) => setForm({ ...form, end_date: e.target.value })} /></div>
          <button className="primary" onClick={add}>Добавить</button>
        </div>
        {msg && <div className={msg.includes('конфликт') || msg.includes('уже') ? 'warn' : 'ok'} style={{ marginTop: 8 }}>{msg}</div>}
        <div className="mute" style={{ marginTop: 6 }}>Дубли по номеру объединяются автоматически, если данные сходятся; расходящиеся уходят в конфликты Диане.</div>
      </div>
    </div>
  );
}

function MySalaryTab({ me, notify }) {
  const [period, setPeriod] = useState({ from: new Date().toISOString().slice(0, 7) + '-01', to: new Date().toISOString().slice(0, 10) });
  const [cur, setCur] = useState(null);
  const [hist, setHist] = useState(null);
  const [year, setYear] = useState(new Date().getFullYear());
  const load = useCallback(async () => {
    try {
      const [c, h] = await Promise.all([
        api('/my/summary?from=' + period.from + '&to=' + period.to),
        api('/my/salary-history?year=' + year),
      ]);
      setCur(c); setHist(h);
    } catch (e) { notify(e.message); }
  }, [period.from, period.to, year]);
  useEffect(() => { load(); }, [load]);
  if (!cur || !hist) return <div className="card mute">Загрузка…</div>;
  const s = cur.total;
  const rows = [['Офис', s.office], ['Сопровождение', s.agents], ['50/50', s.fifty], ['Смены', s.shift]];
  const maxTotal = Math.max(...hist.history.map((h) => h.total), 1);
  return (
    <div>
      <ProfileCard me={me} />
      <div className="card">
        <div className="cardhead"><h2>Заработано за выбранный период</h2></div>
        <PeriodPicker from={period.from} to={period.to} set={setPeriod} />
        <div className="tablewrap"><table>
          <thead><tr><th>Источник</th><th>Сумма</th></tr></thead>
          <tbody>
            {rows.map(([label, v]) => <tr key={label}><td>{label}</td><td className="num">{fmtMoney(v)}</td></tr>)}
            <tr style={{ fontWeight: 600 }}><td>Итого к выплате</td><td className="num">{fmtMoney(s.total)}</td></tr>
          </tbody>
        </table></div>
        <div className="mute" style={{ marginTop: 6 }}>Ставка смены: {fmtMoney(cur.shiftRate)} · смен за период: {cur.counts.shifts}</div>
      </div>
      <div className="card">
        <div className="cardhead"><h2>История по месяцам {year}</h2>
          <input type="number" value={year} onChange={(e) => setYear(+e.target.value || year)} style={{ width: 90 }} />
        </div>
        <div className="tablewrap"><table>
          <thead><tr><th>Месяц</th><th>Офис</th><th>Сопровождение</th><th>50/50</th><th>Смены</th><th>Итого</th><th style={{ width: 120 }}></th></tr></thead>
          <tbody>
            {hist.history.filter((h) => h.total !== 0 || h.month.startsWith(String(year))).map((h) => (
              <tr key={h.month}>
                <td>{h.month}</td>
                <td className="num">{fmtMoney(h.office)}</td><td className="num">{fmtMoney(h.agents)}</td>
                <td className="num">{fmtMoney(h.fifty)}</td><td className="num">{fmtMoney(h.shift)}</td>
                <td className="num" style={{ fontWeight: 600 }}>{fmtMoney(h.total)}</td>
                <td><div style={{ height: 8, background: 'var(--teal)', borderRadius: 4, width: `${Math.max(2, Math.round(100 * h.total / maxTotal))}%`, opacity: 0.8 }} /></td>
              </tr>
            ))}
          </tbody>
        </table></div>
      </div>
    </div>
  );
}

// ---------- приложение ----------
function App() {
  const [me, setMe] = useState(null);
  const [loading, setLoading] = useState(true);
  const [tab, setTab] = useState('clients');
  const [clientId, setClientId] = useState(null);
  const [period, setPeriod] = useState({
    from: new Date().toISOString().slice(0, 7) + '-01',
    to: new Date().toISOString().slice(0, 10),
  });

  useEffect(() => {
    api('/me').then(setMe).catch(() => setMe(null)).finally(() => setLoading(false));
  }, []);
  useEffect(() => { document.title = 'CRM Диана' + (me ? ' — ' + me.displayName : ''); }, [me]);
  // стартовая вкладка по роли
  useEffect(() => {
    if (!me) return;
    if (me.role !== 'admin' && !['myc', 'mydash', 'mysal'].includes(tab)) setTab('myc');
  }, [me]);

  if (loading) return <div style={{ padding: 40, textAlign: 'center', color: 'var(--mute)' }}>Загрузка…</div>;
  if (!me) return <Login onOk={setMe} />;

  const isAdmin = me.role === 'admin';
  const isEmployee = me.role === 'vika' || me.role === 'tanya';
  const periodArg = { from: period.from, to: period.to, set: setPeriod };
  const tabs = isAdmin
    ? [['clients', 'Клиенты'], ['dash', 'Дашборд'], ['policies', 'Полисы'], ['acc', 'Бухгалтерия'], ['summary', 'Сводка'], ['analytics', 'Аналитика'], ['import', 'Импорт'], ['settings', 'Настройки']]
    : [['myc', 'Договоры'], ['mydash', 'Дашборд'], ['mysal', 'Моя зарплата']];

  return (
    <div>
      <div className="topline">
        <div>
          <h1 style={{ fontSize: 20, margin: 0, fontWeight: 600 }}>CRM Диана</h1>
          <div className="mute">{me.displayName} · {isAdmin ? 'полный доступ' : isEmployee ? 'личный кабинет' : 'ограниченный доступ'}</div>
        </div>
        <div style={{ display: 'flex', gap: 12, alignItems: 'center' }}>
          <div className="tabs">
            {tabs.map(([id, label]) => (
              <button key={id} className="tab" onClick={() => setTab(id)}
                style={{ background: tab === id ? 'var(--ink)' : 'transparent', color: tab === id ? 'var(--paper)' : 'var(--mute)' }}>
                {label}
              </button>
            ))}
          </div>
          <button className="ghost" onClick={async () => { await api('/logout', { method: 'POST' }); setMe(null); }}>Выйти</button>
        </div>
      </div>
      {tab === 'clients' && isAdmin && <ClientsTab me={me} openClient={setClientId} />}
      {tab === 'dash' && isAdmin && <DashboardTab me={me} />}
      {tab === 'policies' && isAdmin && <PoliciesTab me={me} />}
      {tab === 'acc' && isAdmin && <AccountingTab period={periodArg} />}
      {tab === 'summary' && isAdmin && <SummaryTab period={periodArg} notify={(m) => console.log(m)} />}
      {tab === 'analytics' && isAdmin && <AnalyticsTab period={periodArg} notify={(m) => console.log(m)} />}
      {tab === 'myc' && isEmployee && <MyContractsTab me={me} notify={(m) => console.log(m)} />}
      {tab === 'mydash' && isEmployee && <MyDashboardTab me={me} />}
      {tab === 'mysal' && isEmployee && <MySalaryTab me={me} notify={(m) => console.log(m)} />}
      {tab === 'import' && isAdmin && <ImportTab />}
      {tab === 'settings' && isAdmin && <SettingsTab me={me} />}
      {clientId && <ClientModal id={clientId} isAdmin={isAdmin} onClose={() => setClientId(null)} />}
    </div>
  );
}

ReactDOM.createRoot(document.getElementById('root')).render(<App />);
