fix(web): Rules-of-Hooks-Verletzung auf der Kundenseite (React #310)

welcomeBusy's useState stand hinter dem frühen "noch am Laden"-Return,
wurde also erst ab dem zweiten Render (sobald die Kundendaten da sind)
aufgerufen - unterschiedliche Hook-Anzahl zwischen den Renders, siehe
React-Fehler #310. Bestand schon vor der heutigen Impersonation-Änderung
(seit der Begrüßungsmail-Funktion), ist aber in der Praxis bei jedem
Öffnen der Kundenseite aufgetreten ("This page couldn't load").

Hook vor den frühen Return verschoben. Rest der Datei und eine grobe
Suche über die restliche Web-App auf dasselbe Muster geprüft - keine
weiteren echten Treffer (nur falsch-positive in anderen, unabhängigen
Komponenten derselben Dateien).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Kundencenter 2026-10-01 11:16:44 +02:00
parent 2ed9697ca6
commit e2a73f4e6d

View file

@ -18,6 +18,7 @@ export default function Kunde() {
const [contracts, setContracts] = useState<CRow[] | null>(null); const [orders, setOrders] = useState<ORow[] | null>(null); const [ress, setRess] = useState<RRow[] | null>(null); const [contracts, setContracts] = useState<CRow[] | null>(null); const [orders, setOrders] = useState<ORow[] | null>(null); const [ress, setRess] = useState<RRow[] | null>(null);
const [o, setO] = useState<Org | null>(null); const [msg, setMsg] = useState<{ k: 'ok' | 'err' | 'warn'; t: string } | null>(null); const [confirm, setConfirm] = useState<string | null>(null); const [switchTo, setSwitchTo] = useState<'private' | 'business' | null>(null); const [o, setO] = useState<Org | null>(null); const [msg, setMsg] = useState<{ k: 'ok' | 'err' | 'warn'; t: string } | null>(null); const [confirm, setConfirm] = useState<string | null>(null); const [switchTo, setSwitchTo] = useState<'private' | 'business' | null>(null);
const [impersonateOpen, setImpersonateOpen] = useState(false); const [impersonateBusy, setImpersonateBusy] = useState(false); const [impersonateOpen, setImpersonateOpen] = useState(false); const [impersonateBusy, setImpersonateBusy] = useState(false);
const [welcomeBusy, setWelcomeBusy] = useState(false);
const load = useCallback(() => api<Org>('GET', `/admin/customers/${id}`).then(setO).catch((e) => setMsg({ k: 'err', t: errMsg(e) })), [id]); const load = useCallback(() => api<Org>('GET', `/admin/customers/${id}`).then(setO).catch((e) => setMsg({ k: 'err', t: errMsg(e) })), [id]);
useEffect(() => { void load(); api<CRow[]>('GET', `/contracts?org=${id}`).then(setContracts).catch(() => setContracts([])); api<ORow[]>('GET', `/orders?org=${id}`).then(setOrders).catch(() => setOrders([])); api<RRow[]>('GET', `/resources?org=${id}`).then(setRess).catch(() => setRess([])); }, [load, id]); useEffect(() => { void load(); api<CRow[]>('GET', `/contracts?org=${id}`).then(setContracts).catch(() => setContracts([])); api<ORow[]>('GET', `/orders?org=${id}`).then(setOrders).catch(() => setOrders([])); api<RRow[]>('GET', `/resources?org=${id}`).then(setRess).catch(() => setRess([])); }, [load, id]);
if (!o) return msg ? <Alert kind={msg.k}>{msg.t}</Alert> : <p className="muted" role="status">Wird geladen …</p>; if (!o) return msg ? <Alert kind={msg.k}>{msg.t}</Alert> : <p className="muted" role="status">Wird geladen …</p>;
@ -45,7 +46,6 @@ export default function Kunde() {
await reload(); r.push('/dashboard'); await reload(); r.push('/dashboard');
} catch (x) { setMsg({ k: 'err', t: errMsg(x) }); setImpersonateBusy(false); } } catch (x) { setMsg({ k: 'err', t: errMsg(x) }); setImpersonateBusy(false); }
} }
const [welcomeBusy, setWelcomeBusy] = useState(false);
async function sendWelcome() { async function sendWelcome() {
setWelcomeBusy(true); setWelcomeBusy(true);
try { const r = await api<{ status: string }>('POST', `/admin/customers/${id}/welcome-mail`); setMsg({ k: r.status === 'sent' ? 'ok' : 'warn', t: r.status === 'sent' ? 'Begrüßungsmail gesendet.' : `Nicht gesendet (Status: ${r.status}).` }); } try { const r = await api<{ status: string }>('POST', `/admin/customers/${id}/welcome-mail`); setMsg({ k: r.status === 'sent' ? 'ok' : 'warn', t: r.status === 'sent' ? 'Begrüßungsmail gesendet.' : `Nicht gesendet (Status: ${r.status}).` }); }