/** * Hook zum Laden des Systemzustands. * * Der Hook zeigt niemals Platzhalter- oder Demo-Werte: solange kein echtes * Ergebnis vorliegt, bleibt der Zustand ausdrücklich "lädt" oder "Fehler" * (PROMPT.md §138/§139). */ import { useCallback, useEffect, useState } from 'react'; import { ApiError, requestApi } from '../../api/client'; import type { SystemHealth } from '../../types/api'; /** Ladezustand der Systemzustandsabfrage. */ export type HealthLoadState = 'loading' | 'loaded' | 'failed'; /** Rückgabewert des Hooks. */ export interface UseSystemHealthResult { /** Aktueller Ladezustand. */ loadState: HealthLoadState; /** Geladener Systemzustand; null, solange keiner vorliegt. */ systemHealth: SystemHealth | null; /** Aufgetretener Fehler; null, wenn kein Fehler vorliegt. */ loadError: ApiError | null; /** Lädt den Systemzustand erneut. */ reload: () => void; } /** * Lädt den Systemzustand von GET /api/v1/health. * * @param refreshIntervalMs Abstand automatischer Aktualisierungen in Millisekunden. * Ein Wert von 0 deaktiviert die automatische Aktualisierung. */ export function useSystemHealth(refreshIntervalMs = 0): UseSystemHealthResult { const [loadState, setLoadState] = useState('loading'); const [systemHealth, setSystemHealth] = useState(null); const [loadError, setLoadError] = useState(null); // reloadCounter erzwingt einen erneuten Lauf des Effekts bei manuellem Neuladen. const [reloadCounter, setReloadCounter] = useState(0); const reload = useCallback(() => { setReloadCounter((previousCounter) => previousCounter + 1); }, []); useEffect(() => { // Der Controller bricht laufende Anfragen ab, wenn die Komponente verschwindet. const abortController = new AbortController(); async function loadSystemHealth(): Promise { try { const loadedHealth = await requestApi('/health', { signal: abortController.signal, }); setSystemHealth(loadedHealth); setLoadError(null); setLoadState('loaded'); } catch (caughtError) { // Ein Abbruch ist kein Fehler, sondern Folge des Aufräumens. if (caughtError instanceof DOMException && caughtError.name === 'AbortError') { return; } // Der zuletzt bekannte Zustand wird verworfen: eine veraltete Anzeige // als aktuellen Zustand auszugeben wäre irreführend. setSystemHealth(null); setLoadError( caughtError instanceof ApiError ? caughtError : new ApiError({ code: 'UNEXPECTED_ERROR', message: 'Der Systemzustand konnte nicht ermittelt werden.', statusCode: 0, requestId: '', }), ); setLoadState('failed'); } } void loadSystemHealth(); if (refreshIntervalMs <= 0) { return () => abortController.abort(); } const refreshTimer = window.setInterval(() => { void loadSystemHealth(); }, refreshIntervalMs); return () => { window.clearInterval(refreshTimer); abortController.abort(); }; }, [refreshIntervalMs, reloadCounter]); return { loadState, systemHealth, loadError, reload }; }