88 lines
3.2 KiB
TypeScript
88 lines
3.2 KiB
TypeScript
import { type ReactNode } from 'react';
|
|
import { useQuery } from '@tanstack/react-query';
|
|
import { apiRequest } from '../../lib/api-client';
|
|
import { healthSchema, type Health } from '../../lib/schemas';
|
|
import { Card, CardBody, CardHeader } from '../../components/ui/card';
|
|
import { Badge } from '../../components/ui/badge';
|
|
import { ErrorState, Spinner } from '../../components/ui/states';
|
|
|
|
/** Admin-Seite: detaillierter Systemstatus der Plattform. */
|
|
export function SystemStatusPage(): ReactNode {
|
|
const healthQuery = useQuery({
|
|
queryKey: ['health'],
|
|
queryFn: async (): Promise<Health> =>
|
|
healthSchema.parse(await apiRequest('/api/v1/health')),
|
|
refetchInterval: 30_000,
|
|
});
|
|
|
|
return (
|
|
<div className="mx-auto max-w-4xl space-y-6">
|
|
<div>
|
|
<h1 className="text-2xl font-bold text-slate-900">Systemstatus</h1>
|
|
<p className="mt-1 text-sm text-slate-500">
|
|
Zustand aller Plattform-Komponenten (aktualisiert alle 30 Sekunden).
|
|
</p>
|
|
</div>
|
|
|
|
<Card>
|
|
<CardHeader title="Komponenten" />
|
|
<CardBody>
|
|
{healthQuery.isLoading && <Spinner />}
|
|
{healthQuery.isError && (
|
|
<ErrorState message="Der Health-Endpoint ist nicht erreichbar." />
|
|
)}
|
|
{healthQuery.data && (
|
|
<div className="space-y-3">
|
|
<StatusRow
|
|
label="Management-Backend"
|
|
variant={healthQuery.data.components.backend === 'healthy' ? 'success' : 'danger'}
|
|
status={healthQuery.data.components.backend === 'healthy' ? 'Healthy' : 'Unhealthy'}
|
|
/>
|
|
<StatusRow
|
|
label="PostgreSQL"
|
|
variant={
|
|
healthQuery.data.components.database.status === 'healthy'
|
|
? 'success'
|
|
: 'danger'
|
|
}
|
|
status={
|
|
healthQuery.data.components.database.status === 'healthy'
|
|
? `Healthy · ${healthQuery.data.components.database.latencyMs} ms`
|
|
: 'Unhealthy'
|
|
}
|
|
/>
|
|
<div className="flex items-center justify-between rounded-lg border border-slate-200 px-4 py-3">
|
|
<span className="text-sm font-medium text-slate-700">Plattform-Version</span>
|
|
<span className="text-sm text-slate-500">{healthQuery.data.version}</span>
|
|
</div>
|
|
<div className="flex items-center justify-between rounded-lg border border-slate-200 px-4 py-3">
|
|
<span className="text-sm font-medium text-slate-700">Uptime</span>
|
|
<span className="text-sm text-slate-500">
|
|
{Math.floor(healthQuery.data.uptimeSeconds / 60)} Minuten
|
|
</span>
|
|
</div>
|
|
</div>
|
|
)}
|
|
</CardBody>
|
|
</Card>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
/** Zeile im Systemstatus. */
|
|
function StatusRow({
|
|
label,
|
|
status,
|
|
variant,
|
|
}: {
|
|
label: string;
|
|
status: string;
|
|
variant: 'success' | 'danger';
|
|
}): ReactNode {
|
|
return (
|
|
<div className="flex items-center justify-between rounded-lg border border-slate-200 px-4 py-3">
|
|
<span className="text-sm font-medium text-slate-700">{label}</span>
|
|
<Badge variant={variant}>{status}</Badge>
|
|
</div>
|
|
);
|
|
} |