// Users & permissions + Module manager + Settings.
// Backed by /api/users, /api/roles, /api/modules.

// ─────────────────────────────────────────────────────────────
//  UsersPermissions — internal team with roles + permission matrix
// ─────────────────────────────────────────────────────────────

function initials(name) {
  return (name || '?').split(/\s+/).filter(Boolean).map(p => p[0]).join('').slice(0, 2).toUpperCase();
}

function formatRelative(ts) {
  if (!ts) return 'nikdy';
  const s = Math.floor((Date.now() / 1000) - ts);
  if (s < 60) return 'teraz';
  if (s < 3600) return `pred ${Math.floor(s/60)} min`;
  if (s < 86400) return `pred ${Math.floor(s/3600)} h`;
  return `pred ${Math.floor(s/86400)} dňami`;
}

function Toast({ kind = 'info', children, onClose }) {
  React.useEffect(() => {
    const id = setTimeout(onClose, 6000);
    return () => clearTimeout(id);
  }, [onClose]);
  return (
    <div style={{
      position:'fixed', bottom:20, right:20, zIndex:2000,
      background: kind === 'error' ? 'var(--red-500)' : kind === 'success' ? 'var(--green-500)' : 'var(--navy-800)',
      color:'#fff', padding:'10px 14px', borderRadius:8, boxShadow:'var(--shadow-lg)',
      fontSize:13, fontWeight:500, maxWidth:420,
    }}>{children}</div>
  );
}

// Reusable modal
function Modal({ title, children, onClose, footer, width = 480 }) {
  React.useEffect(() => {
    const onKey = (e) => { if (e.key === 'Escape') onClose(); };
    document.addEventListener('keydown', onKey);
    return () => document.removeEventListener('keydown', onKey);
  }, [onClose]);
  return (
    <div className="modal-backdrop" onClick={onClose}>
      <div className="modal" style={{width}} onClick={e => e.stopPropagation()}>
        <div className="modal-head">
          <h3>{title}</h3>
          <button className="btn btn-ghost btn-xs" onClick={onClose}><Ico.x/></button>
        </div>
        <div className="modal-body">{children}</div>
        {footer && <div className="modal-foot">{footer}</div>}
      </div>
    </div>
  );
}

function InviteUserModal({ roles, onClose, onCreated }) {
  const [email, setEmail] = React.useState('');
  const [name, setName] = React.useState('');
  const [role, setRole] = React.useState('viewer');
  const [submitting, setSubmitting] = React.useState(false);
  const [err, setErr] = React.useState('');
  const [tempPwd, setTempPwd] = React.useState(null);

  async function submit(e) {
    e.preventDefault();
    setSubmitting(true); setErr('');
    const r = await API.createUser({ email: email.trim(), name: name.trim(), role });
    setSubmitting(false);
    if (r.status === 201 || r.ok) {
      setTempPwd(r.body.tempPassword || '—');
      onCreated && onCreated(r.body.user);
    } else {
      const map = {
        email_taken: 'Tento e-mail už je zaregistrovaný.',
        invalid_email: 'Neplatný e-mail.',
        invalid_name: 'Meno musí mať aspoň 2 znaky.',
        invalid_role: 'Neplatná rola.',
        forbidden: 'Nemáte oprávnenie pridávať používateľov.',
      };
      setErr(map[r.body.error] || 'Nepodarilo sa vytvoriť používateľa.');
    }
  }

  return (
    <Modal
      title="Pozvať používateľa"
      onClose={onClose}
      footer={tempPwd ? (
        <button className="btn btn-primary" onClick={onClose}>Zavrieť</button>
      ) : (
        <>
          <button className="btn" onClick={onClose} disabled={submitting}>Zrušiť</button>
          <button className="btn btn-primary" onClick={submit} disabled={submitting}>
            {submitting ? 'Vytváram…' : 'Vytvoriť'}
          </button>
        </>
      )}>
      {tempPwd ? (
        <div className="vstack" style={{gap:10}}>
          <div className="auth-alert" style={{background:'var(--green-100)', color:'#135e37', borderColor:'rgba(42,157,95,0.3)'}}>
            <Ico.check/> <span>Používateľ <b>{email}</b> bol vytvorený.</span>
          </div>
          <div className="field">
            <label>Dočasné heslo (odovzdajte ho používateľovi)</label>
            <div className="hstack">
              <input className="input mono" value={tempPwd} readOnly style={{flex:1, fontSize:13}}/>
              <button className="btn btn-sm" onClick={() => navigator.clipboard && navigator.clipboard.writeText(tempPwd)}>Kopírovať</button>
            </div>
            <div className="small muted" style={{marginTop:4}}>Heslo sa tu zobrazí naposledy — v logoch sa neuchováva. Používateľ ho musí pri prvom prihlásení zmeniť.</div>
          </div>
        </div>
      ) : (
        <form className="vstack" style={{gap:12}} onSubmit={submit}>
          {err && <div className="auth-alert" role="alert"><Ico.dots/> <span>{err}</span></div>}
          <div className="field">
            <label>Meno a priezvisko</label>
            <input className="input" value={name} onChange={e => setName(e.target.value)} placeholder="Napr. Mária Nováková" required autoFocus/>
          </div>
          <div className="field">
            <label>E-mail</label>
            <input className="input" type="email" value={email} onChange={e => setEmail(e.target.value)} placeholder="meno@ddenergy.sk" required/>
          </div>
          <div className="field">
            <label>Rola</label>
            <select className="select" value={role} onChange={e => setRole(e.target.value)}>
              {roles.map(r => <option key={r.slug} value={r.slug}>{r.label}</option>)}
            </select>
          </div>
          <div className="small muted">Vygenerujeme dočasné heslo, ktoré odovzdáte používateľovi. Pri prvom prihlásení si ho musí zmeniť.</div>
        </form>
      )}
    </Modal>
  );
}

function UsersPermissions() {
  const [users, setUsers] = React.useState([]);
  const [roles, setRoles] = React.useState([]);
  const [modules, setModules] = React.useState([]);
  const [loading, setLoading] = React.useState(true);
  const [selectedRole, setSelectedRole] = React.useState(null);
  const [showInvite, setShowInvite] = React.useState(false);
  const [toast, setToast] = React.useState(null);
  const [busyId, setBusyId] = React.useState(null);
  const canEdit = window.hasPerm ? window.hasPerm('users.edit') : true;
  const canCreate = window.hasPerm ? window.hasPerm('users.create') : true;
  const canDelete = window.hasPerm ? window.hasPerm('users.delete') : true;

  async function reload() {
    setLoading(true);
    const [u, r, m] = await Promise.all([API.listUsers(), API.listRoles(), API.listModules()]);
    setUsers(u); setRoles(r); setModules(m);
    if (!selectedRole && r.length) setSelectedRole(r.find(x => x.slug === 'manager') || r[0]);
    setLoading(false);
  }
  React.useEffect(() => { reload(); }, []);

  async function changeRole(user, newRole) {
    setBusyId(user.id);
    const r = await API.updateUser(user.id, { role: newRole });
    setBusyId(null);
    if (r.ok) { setToast({ kind: 'success', text: `Rola aktualizovaná: ${newRole}` }); reload(); }
    else setToast({ kind: 'error', text: r.body.error === 'last_admin' ? 'Nedá sa zmeniť — zostal by len jeden admin.' : 'Nepodarilo sa uložiť.' });
  }
  async function toggleActive(user) {
    setBusyId(user.id);
    const r = await API.updateUser(user.id, { is_active: !user.is_active });
    setBusyId(null);
    if (r.ok) { setToast({ kind: 'success', text: user.is_active ? 'Používateľ deaktivovaný.' : 'Používateľ aktivovaný.' }); reload(); }
    else setToast({ kind: 'error', text: r.body.error === 'cannot_disable_self' ? 'Nemôžete deaktivovať seba.' : r.body.error === 'last_admin' ? 'Zostal by len jeden admin.' : 'Chyba.' });
  }
  async function resetPassword(user) {
    if (!confirm(`Vygenerovať nové dočasné heslo pre ${user.email}? Všetky jeho relácie sa odhlásia.`)) return;
    const r = await API.resetPassword(user.id);
    if (r.ok) alert(`Nové heslo pre ${user.email}:\n\n${r.body.tempPassword}\n\nOdovzdajte ho používateľovi. V logu sa neukladá.`);
    else setToast({ kind: 'error', text: 'Nepodarilo sa resetnúť heslo.' });
  }
  async function removeUser(user) {
    if (!confirm(`Naozaj chcete odstrániť ${user.name} (${user.email})?`)) return;
    setBusyId(user.id);
    const r = await API.deleteUser(user.id);
    setBusyId(null);
    if (r.ok) { setToast({ kind: 'success', text: 'Používateľ odstránený.' }); reload(); }
    else setToast({ kind: 'error', text: r.body.error === 'last_admin' ? 'Zostal by len jeden admin.' : r.body.error === 'cannot_delete_self' ? 'Nemôžete odstrániť seba.' : 'Chyba.' });
  }
  async function togglePerm(roleSlug, perm) {
    if (!canEdit) return;
    const role = roles.find(r => r.slug === roleSlug);
    if (!role) return;
    const next = { ...role.permissions, [perm]: !role.permissions[perm] };
    const r = await API.updateRole(roleSlug, { permissions: next });
    if (r.ok) { reload(); }
    else setToast({ kind: 'error', text: r.body.error === 'admin_requires_wildcard' ? 'Admin musí mať všetky práva.' : 'Nepodarilo sa uložiť.' });
  }

  const actions = ['view', 'edit', 'create', 'delete'];
  const visibleModules = modules.filter(m => m.slug !== 'settings');

  function renderCell(role, module, action) {
    const perm = `${module.slug}.${action}`;
    const val = role.permissions['*'] === true ? true : role.permissions[perm] === true;
    const isWild = role.permissions['*'] === true;
    const disabled = isWild || !canEdit || role.slug === 'admin';
    return (
      <td key={action} style={{textAlign:'center'}}>
        <button
          onClick={() => !disabled && togglePerm(role.slug, perm)}
          disabled={disabled}
          className={`dot-perm ${val ? 'y' : 'n'}`}
          style={{border:'none', cursor: disabled ? 'default' : 'pointer', opacity: disabled && !val ? 0.4 : 1}}
          title={disabled ? (isWild ? 'Admin má všetky práva' : '') : (val ? 'Klikni na odobratie' : 'Klikni na povolenie')}
        >{val ? <Ico.check/> : <Ico.x/>}</button>
      </td>
    );
  }

  if (loading) {
    return <AppShell active="users" crumbs={['Administrácia', 'Používatelia a práva']}><div className="page-head"><div><h1>Načítavam…</h1></div></div></AppShell>;
  }

  const activeCount = users.filter(u => u.is_active).length;

  return (
    <AppShell active="users" crumbs={['Administrácia', 'Používatelia a práva']}>
      {toast && <Toast kind={toast.kind} onClose={() => setToast(null)}>{toast.text}</Toast>}
      {showInvite && (
        <InviteUserModal
          roles={roles}
          onClose={() => setShowInvite(false)}
          onCreated={() => reload()}
        />
      )}

      <div className="page-head">
        <div>
          <h1>Používatelia a práva</h1>
          <p>{activeCount} aktívnych · {roles.length} rolí · granular permissions s override na používateľa</p>
        </div>
        <div className="page-head-actions">
          <button className="btn" onClick={reload}><Ico.upload/>Obnoviť</button>
          {canCreate && <button className="btn btn-primary" onClick={() => setShowInvite(true)}><Ico.plus/>Pozvať používateľa</button>}
        </div>
      </div>

      <div style={{display:'grid', gridTemplateColumns:'1.4fr 1fr', gap:14}}>
        <div className="card" style={{overflow:'hidden'}}>
          <div className="card-head">
            <h3>Používatelia ({users.length})</h3>
          </div>
          <div style={{overflow:'auto'}}>
            <table className="tbl">
              <thead><tr><th>Meno / e-mail</th><th>Rola</th><th>Posledné prihl.</th><th>2FA</th><th>Stav</th><th></th></tr></thead>
              <tbody>
                {users.map(u => (
                  <tr key={u.id} style={busyId === u.id ? { opacity: 0.5 } : {}}>
                    <td>
                      <div className="hstack">
                        <div className="sb-avatar" style={{width:28, height:28, fontSize:11}}>{initials(u.name)}</div>
                        <div>
                          <div style={{fontWeight:500}}>{u.name}</div>
                          <div className="small muted">{u.email}</div>
                        </div>
                      </div>
                    </td>
                    <td>
                      {canEdit ? (
                        <select
                          className="select"
                          value={u.role}
                          style={{height:26, fontSize:11.5, padding:'0 6px'}}
                          onChange={e => changeRole(u, e.target.value)}
                          disabled={busyId === u.id}
                        >
                          {roles.map(r => <option key={r.slug} value={r.slug}>{r.label}</option>)}
                        </select>
                      ) : <span className="chip">{roles.find(r => r.slug === u.role)?.label || u.role}</span>}
                    </td>
                    <td className="small muted">{formatRelative(u.last_login_at)}</td>
                    <td>{u.has_2fa ? <Ico.check style={{color:'var(--green-500)'}}/> : <span className="chip amber">vyžiadať</span>}</td>
                    <td>{u.is_active ? <span className="chip green dot">Aktívny</span> : <span className="chip gray dot">Deaktivovaný</span>}</td>
                    <td style={{textAlign:'right', whiteSpace:'nowrap'}}>
                      {canEdit && (
                        <button className="btn btn-xs" onClick={() => resetPassword(u)} title="Resetovať heslo"><Ico.lock/></button>
                      )}
                      {canEdit && (
                        <button className="btn btn-xs" style={{marginLeft:4}} onClick={() => toggleActive(u)} disabled={busyId === u.id}>
                          {u.is_active ? 'Deaktivovať' : 'Aktivovať'}
                        </button>
                      )}
                      {canDelete && (
                        <button className="btn btn-xs btn-danger" style={{marginLeft:4}} onClick={() => removeUser(u)} disabled={busyId === u.id}><Ico.x/></button>
                      )}
                    </td>
                  </tr>
                ))}
                {users.length === 0 && (
                  <tr><td colSpan={6} style={{textAlign:'center', padding:30, color:'var(--ink-500)'}}>Zatiaľ žiadni používatelia.</td></tr>
                )}
              </tbody>
            </table>
          </div>
        </div>

        <div className="card">
          <div className="card-head">
            <h3>Matica práv</h3>
            <select
              className="select"
              value={selectedRole ? selectedRole.slug : ''}
              onChange={e => setSelectedRole(roles.find(r => r.slug === e.target.value))}
              style={{height:26, fontSize:11.5}}
            >
              {roles.map(r => <option key={r.slug} value={r.slug}>{r.label}</option>)}
            </select>
          </div>
          {selectedRole && (
            <div style={{padding:'10px 14px'}}>
              <div className="small muted" style={{marginBottom:8}}>
                <b style={{color:'var(--ink-900)'}}>{selectedRole.label}</b> — {selectedRole.description || '—'}
                {selectedRole.is_system && <span className="chip navy" style={{marginLeft:6, padding:'1px 6px', fontSize:10}}>system</span>}
                {selectedRole.permissions['*'] === true && <span className="chip amber" style={{marginLeft:6, padding:'1px 6px', fontSize:10}}>wildcard</span>}
                <div style={{marginTop:4}}>Používatelia s touto rolou: <b>{selectedRole.users_count}</b></div>
              </div>
              <div style={{overflow:'auto'}}>
                <table className="perm-matrix">
                  <thead><tr><th className="lbl">Modul</th><th>Zobr.</th><th>Upr.</th><th>Vytv.</th><th>Zmaz.</th></tr></thead>
                  <tbody>
                    {visibleModules.map(m => (
                      <tr key={m.slug}>
                        <td className="lbl" style={{fontWeight:500, opacity: m.is_enabled ? 1 : 0.4}}>
                          {m.label}
                          {!m.is_enabled && <span className="small muted"> (vypnutý)</span>}
                        </td>
                        {actions.map(a => renderCell(selectedRole, m, a))}
                      </tr>
                    ))}
                  </tbody>
                </table>
              </div>
              <div className="divider"/>
              <div className="hstack small muted" style={{gap:14, flexWrap:'wrap'}}>
                <span><span className="dot-perm y" style={{display:'inline-flex', width:12, height:12, verticalAlign:'middle', marginRight:4}}/>povolené</span>
                <span><span className="dot-perm n" style={{display:'inline-flex', width:12, height:12, verticalAlign:'middle', marginRight:4}}/>nepovolené</span>
                <span style={{marginLeft:'auto'}}>Klikni na bunku pre zmenu</span>
              </div>
            </div>
          )}
        </div>
      </div>
    </AppShell>
  );
}

// ─────────────────────────────────────────────────────────────
//  ModuleManager — on/off + description + config
// ─────────────────────────────────────────────────────────────
function ModuleManager() {
  const [modules, setModules] = React.useState([]);
  const [loading, setLoading] = React.useState(true);
  const [toast, setToast] = React.useState(null);
  const [busy, setBusy] = React.useState(null);
  const canEdit = window.hasPerm ? window.hasPerm('modules.edit') : true;

  const iconFor = {
    dashboard: <Ico.dashboard/>, orders: <Ico.orders/>, products: <Ico.products/>,
    pricing: <Ico.pricing/>, b2b: <Ico.b2b/>, datasheets: <Ico.datasheet/>,
    gallery: <Ico.gallery/>, ai: <Ico.ai/>, calendar: <Ico.construction/>,
    monitoring: <Ico.construction/>, users_customers: <Ico.users/>,
    users: <Ico.users/>, modules: <Ico.modules/>, settings: <Ico.settings/>,
  };

  async function reload() {
    setLoading(true);
    setModules(await API.listModules());
    setLoading(false);
  }
  React.useEffect(() => { reload(); }, []);

  async function toggle(m) {
    if (!canEdit) return setToast({ kind:'error', text:'Nemáte oprávnenie.' });
    if (m.is_core && m.is_enabled) return setToast({ kind:'error', text:'Jadrový modul nemožno vypnúť.' });
    setBusy(m.slug);
    const r = await API.toggleModule(m.slug, !m.is_enabled);
    setBusy(null);
    if (r.ok) {
      setToast({ kind:'success', text: `${m.label}: ${m.is_enabled ? 'vypnutý' : 'zapnutý'}` });
      // Reload the modules list for this screen + notify App to refresh the sidebar.
      await reload();
      // Push fresh modules into global session so Sidebar hides/shows immediately.
      window.dispatchEvent(new CustomEvent('dd-modules-changed'));
    }
    else setToast({ kind:'error', text: r.body.error === 'core_module' ? 'Jadrový modul.' : 'Chyba.' });
  }

  const enabled = modules.filter(m => m.is_enabled).length;

  return (
    <AppShell active="modules" crumbs={['Administrácia', 'Moduly']}>
      {toast && <Toast kind={toast.kind} onClose={() => setToast(null)}>{toast.text}</Toast>}

      <div className="page-head">
        <div>
          <h1>Moduly</h1>
          <p>Aktivujte alebo deaktivujte moduly pre celú firmu · {enabled} aktívnych z {modules.length}</p>
        </div>
      </div>

      {loading ? <div>Načítavam…</div> : (
        <div style={{display:'grid', gridTemplateColumns:'repeat(auto-fill, minmax(260px, 1fr))', gap:12}}>
          {modules.map(m => (
            <div key={m.slug} className={`mod-tile ${m.is_enabled ? '' : 'off'}`}>
              <div className="hstack">
                <div className="mod-ico">{iconFor[m.slug] || <Ico.modules/>}</div>
                <div style={{flex:1, minWidth:0}}>
                  <h4>{m.label}</h4>
                  <div className="small muted" style={{marginTop:2}}>
                    {m.is_core && <span className="chip navy" style={{marginRight:4, padding:'0 6px', fontSize:10}}>core</span>}
                    {m.is_enabled ? 'Aktívny modul' : 'Neaktívny'}
                  </div>
                </div>
                <div
                  className={`toggle ${m.is_enabled ? 'on' : ''}`}
                  onClick={() => !busy && toggle(m)}
                  style={{cursor: (canEdit && !m.is_core) || !m.is_enabled ? 'pointer' : 'not-allowed', opacity: busy === m.slug ? 0.5 : 1}}
                  title={m.is_core ? 'Jadrový modul — nemožno vypnúť' : ''}
                />
              </div>
              <p>{m.description}</p>
            </div>
          ))}
        </div>
      )}
    </AppShell>
  );
}

// ─────────────────────────────────────────────────────────────
//  Settings (profile + change password + 2FA placeholder)
// ─────────────────────────────────────────────────────────────
function Settings2FA() {
  const [current, setCurrent] = React.useState('');
  const [next, setNext] = React.useState('');
  const [confirm, setConfirm] = React.useState('');
  const [submitting, setSubmitting] = React.useState(false);
  const [msg, setMsg] = React.useState(null);
  const s = window.currentSession || {};
  const user = s.user || {};

  async function changePwd(e) {
    e.preventDefault();
    setMsg(null);
    if (next.length < 12) return setMsg({ kind:'error', text:'Nové heslo musí mať aspoň 12 znakov.' });
    if (next !== confirm) return setMsg({ kind:'error', text:'Potvrdenie sa nezhoduje.' });
    setSubmitting(true);
    const r = await API.changePassword(current, next);
    setSubmitting(false);
    if (r.ok) { setMsg({ kind:'success', text:'Heslo zmenené. Ostatné relácie boli odhlásené.' }); setCurrent(''); setNext(''); setConfirm(''); }
    else setMsg({ kind:'error', text: r.body.error === 'wrong_password' ? 'Aktuálne heslo je nesprávne.' : r.body.message || 'Chyba.' });
  }

  return (
    <AppShell active="settings" crumbs={['Nastavenia', 'Bezpečnosť']}>
      <div className="page-head">
        <div><h1>Nastavenia účtu</h1><p>Profil, bezpečnosť, prepojenia</p></div>
      </div>

      <div style={{display:'grid', gridTemplateColumns:'200px 1fr', gap:14}}>
        <div className="card" style={{padding:'8px 6px', height:'fit-content'}}>
          <div className="sb-nav" style={{padding:0}}>
            {['Profil', 'Bezpečnosť', 'Notifikácie', 'Integrácie'].map((s,i) => (
              <a key={s} className={i===1?'active':''} style={{color: i===1?'var(--navy-900)':'var(--ink-700)', background: i===1?'var(--blue-100)':'transparent', boxShadow: i===1?'inset 2px 0 0 var(--amber-500)':'none', borderColor:'transparent'}}>
                <span>{s}</span>
              </a>
            ))}
          </div>
        </div>

        <div className="vstack" style={{gap:14}}>
          <div className="card">
            <div className="card-head"><h3>Môj účet</h3></div>
            <div className="card-body">
              <div className="hstack" style={{gap:12}}>
                <div className="sb-avatar" style={{width:48, height:48, fontSize:18}}>{initials(user.name)}</div>
                <div>
                  <div style={{fontSize:15, fontWeight:600}}>{user.name || '—'}</div>
                  <div className="small muted">{user.email || '—'} · {user.role || '—'}</div>
                </div>
              </div>
            </div>
          </div>

          <div className="card">
            <div className="card-head"><h3>Zmena hesla</h3></div>
            <form className="card-body" onSubmit={changePwd}>
              {msg && (
                <div className="auth-alert" style={msg.kind === 'success' ? {background:'var(--green-100)', color:'#135e37', borderColor:'rgba(42,157,95,0.3)'} : {}} role="alert">
                  {msg.kind === 'success' ? <Ico.check/> : <Ico.dots/>} <span>{msg.text}</span>
                </div>
              )}
              <div style={{display:'grid', gridTemplateColumns:'repeat(3, 1fr)', gap:10, alignItems:'end'}}>
                <div className="field"><label>Aktuálne heslo</label><input className="input" type="password" value={current} onChange={e => setCurrent(e.target.value)} autoComplete="current-password" required/></div>
                <div className="field"><label>Nové heslo</label><input className="input" type="password" value={next} onChange={e => setNext(e.target.value)} autoComplete="new-password" minLength={12} required/></div>
                <div className="field"><label>Potvrdiť nové</label><input className="input" type="password" value={confirm} onChange={e => setConfirm(e.target.value)} autoComplete="new-password" required/></div>
                <div style={{gridColumn:'span 3'}} className="hstack">
                  <div className="small muted">Politika: min. 12 znakov. Po zmene sa odhlásia všetky ostatné relácie.</div>
                  <button className="btn btn-primary btn-sm" style={{marginLeft:'auto'}} type="submit" disabled={submitting}>
                    {submitting ? 'Ukladám…' : 'Zmeniť heslo'}
                  </button>
                </div>
              </div>
            </form>
          </div>

          <div className="card">
            <div className="card-head"><h3>Dvojfaktorové overenie</h3></div>
            <div className="card-body">
              <p className="muted" style={{margin:0}}>Nastavenie 2FA (TOTP + záložné kódy) — doplníme v ďalšej iterácii. Kostra je pripravená (otplib + <span className="mono">users.totp_secret</span>).</p>
            </div>
          </div>
        </div>
      </div>
    </AppShell>
  );
}

// Export shared UI primitives so integrations.jsx (loaded later) can reuse them.
Object.assign(window, { UsersPermissions, ModuleManager, Settings2FA, Modal, Toast, initials });
