import { useEffect, useState } from 'react' import { api } from '../api/client' import { Layout } from '../components/Layout' import { CredDialog } from './ResellerCompaniesPage' interface Tenant { id: string; name: string; slug: string; plan: string; is_active: boolean country: string; reseller_id: string | null; reseller_name: string | null user_count: number; active_user_count: number; admin_email: string | null } interface Reseller { id: string; email: string; first_name: string; last_name: string is_active: boolean; company_count: number } interface Me { first_name: string; last_name: string; role: string } const empty = { name: '', country: 'DE', plan: 'trial', admin_email: '', admin_first_name: '', admin_last_name: '' } const emptyReseller = { email: '', first_name: '', last_name: '' } export function TenantsPage() { const [me, setMe] = useState(null) const [tab, setTab] = useState<'tenants' | 'resellers'>('tenants') const [tenants, setTenants] = useState([]) const [resellers, setResellers] = useState([]) const [error, setError] = useState(null) const [showNew, setShowNew] = useState(false) const [form, setForm] = useState({ ...empty, reseller_id: '' }) const [showNewReseller, setShowNewReseller] = useState(false) const [rForm, setRForm] = useState({ ...emptyReseller }) const [busy, setBusy] = useState(false) const [cred, setCred] = useState<{ title: string; email: string; password: string } | null>(null) async function load() { try { const [t, r] = await Promise.all([ api.get('/admin/tenants'), api.get('/admin/resellers'), ]) setTenants(t); setResellers(r) } catch (e: unknown) { setError(e instanceof Error ? e.message : 'Fehler beim Laden') } } useEffect(() => { api.get('/auth/me').then(setMe).catch(() => {}) load() }, []) async function createTenant() { setBusy(true); setError(null) try { const q = form.reseller_id ? `?reseller_id=${form.reseller_id}` : '' const { reseller_id, ...rest } = form void reseller_id const body = { ...rest, admin_email: rest.admin_email.trim() || undefined } const res = await api.post<{ initial_password?: string | null; admin_email?: string | null }>( `/admin/tenants${q}`, body, ) setShowNew(false); setForm({ ...empty, reseller_id: '' }) if (res.initial_password) setCred({ title: 'Zugangsdaten Firmen-Administrator', email: res.admin_email ?? '', password: res.initial_password }) await load() } catch (e: unknown) { setError(e instanceof Error ? e.message : 'Fehler beim Anlegen') } finally { setBusy(false) } } async function toggleTenant(t: Tenant) { try { await api.patch(`/admin/tenants/${t.id}`, { is_active: !t.is_active }); await load() } catch (e: unknown) { setError(e instanceof Error ? e.message : 'Fehler') } } async function assignReseller(t: Tenant, reseller_id: string) { try { await api.patch(`/admin/tenants/${t.id}/reseller`, { reseller_id: reseller_id || null }); await load() } catch (e: unknown) { setError(e instanceof Error ? e.message : 'Fehler') } } async function createReseller() { setBusy(true); setError(null) try { const body = { ...rForm, email: rForm.email.trim() || undefined } const res = await api.post<{ initial_password?: string | null; email?: string | null }>('/admin/resellers', body) setShowNewReseller(false); setRForm({ ...emptyReseller }) if (res.initial_password) setCred({ title: 'Zugangsdaten Reseller', email: res.email ?? '', password: res.initial_password }) await load() } catch (e: unknown) { setError(e instanceof Error ? e.message : 'Fehler beim Anlegen') } finally { setBusy(false) } } async function toggleReseller(r: Reseller) { try { await api.patch(`/admin/resellers/${r.id}?is_active=${!r.is_active}`, {}); await load() } catch (e: unknown) { setError(e instanceof Error ? e.message : 'Fehler') } } return (

Mandanten

{tab === 'tenants' ? ( ) : ( )}
{(['tenants', 'resellers'] as const).map(t => ( ))}
{error &&
{error}
} {tab === 'tenants' && (
{tenants.map(t => ( ))} {tenants.length === 0 && }
Firma Plan Nutzer Admin Reseller Status
{t.name}
{t.slug}
{t.plan} {t.active_user_count}/{t.user_count} {t.admin_email ?? '—'}
Keine Firmen.
)} {tab === 'resellers' && (
{resellers.map(r => ( ))} {resellers.length === 0 && }
Name E-Mail Firmen Status
{r.first_name} {r.last_name} {r.email} {r.company_count}
Keine Reseller.
)} {showNew && ( setShowNew(false)} onSubmit={createTenant} busy={busy}> setForm({ ...form, name: e.target.value })} />
setForm({ ...form, country: e.target.value })} /> setForm({ ...form, plan: e.target.value })} />

Erster Administrator. Mit E-Mail → Einladung; ohne E-Mail → einmaliges Temp-Passwort.

setForm({ ...form, admin_email: e.target.value })} />
setForm({ ...form, admin_first_name: e.target.value })} /> setForm({ ...form, admin_last_name: e.target.value })} />
)} {showNewReseller && ( setShowNewReseller(false)} onSubmit={createReseller} busy={busy}>

Mit E-Mail erhält der Reseller eine Einladung – ohne E-Mail ein einmaliges Temp-Passwort. Danach kann er eigene Firmen anlegen und verwalten.

setRForm({ ...rForm, email: e.target.value })} />
setRForm({ ...rForm, first_name: e.target.value })} /> setRForm({ ...rForm, last_name: e.target.value })} />
)} {cred && setCred(null)} />}
) } const inp = 'w-full px-3 py-2 border border-gray-300 rounded-lg text-sm' function Field({ label, children }: { label: string; children: React.ReactNode }) { return } function Modal({ title, children, onClose, onSubmit, busy }: { title: string; children: React.ReactNode; onClose: () => void; onSubmit: () => void; busy: boolean }) { return (
e.stopPropagation()}>

{title}

{children}
) }