Files
gridpilot.gg/apps/website/components/dev/sections/APIStatusSection.tsx
2026-01-18 23:24:30 +01:00

170 lines
5.0 KiB
TypeScript

'use client';
import { Button } from '@/ui/Button';
import { Icon } from '@/ui/Icon';
import { Badge, StatRow, StatusIndicator } from '@/ui/StatusIndicator';
import { Text } from '@/ui/Text';
import { Grid } from '@/ui/Grid';
import { Stack } from '@/ui/Stack';
import { Activity, RefreshCw, Terminal, Wifi } from 'lucide-react';
interface APIStatusSectionProps {
apiStatus: string;
apiHealth: {
successfulRequests: number;
totalRequests: number;
averageResponseTime: number;
consecutiveFailures: number;
lastCheck: number | Date | null;
};
circuitBreakers: Record<string, { state: string; failures: number }>;
checkingHealth: boolean;
onHealthCheck: () => void;
onResetStats: () => void;
onTestError: () => void;
}
export function APIStatusSection({
apiStatus,
apiHealth,
circuitBreakers,
checkingHealth,
onHealthCheck,
onResetStats,
onTestError
}: APIStatusSectionProps) {
const reliability = apiHealth.totalRequests === 0
? 0
: (apiHealth.successfulRequests / apiHealth.totalRequests);
const reliabilityLabel = apiHealth.totalRequests === 0 ? 'N/A' : 'Calculated';
const getReliabilityColor = () => {
if (apiHealth.totalRequests === 0) return 'text-gray-500';
if (reliability >= 0.95) return 'text-performance-green';
if (reliability >= 0.8) return 'text-warning-amber';
return 'text-red-400';
};
const getStatusVariant = () => {
if (apiStatus === 'connected') return 'success';
if (apiStatus === 'degraded') return 'warning';
return 'danger';
};
const statusLabel = apiStatus.toUpperCase();
const healthSummary = `${apiHealth.successfulRequests}/${apiHealth.totalRequests} req`;
const avgResponseLabel = `${apiHealth.averageResponseTime.toFixed(0)}ms`;
const lastCheckLabel = apiHealth.lastCheck ? 'Recently' : 'Never';
const consecutiveFailuresLabel = String(apiHealth.consecutiveFailures);
return (
<Stack>
<Stack direction="row" align="center" gap={2} mb={3}>
<Icon icon={Activity} size={4} color="rgb(156, 163, 175)" />
<Text size="xs" weight="semibold" color="text-gray-400" uppercase letterSpacing="wide">
API Status
</Text>
</Stack>
{/* Status Indicator */}
<StatusIndicator
icon={Wifi}
label={statusLabel}
subLabel={healthSummary}
variant={getStatusVariant()}
/>
<Stack mt={2}>
{/* Reliability */}
<StatRow
label="Reliability"
value={reliabilityLabel}
valueColor={getReliabilityColor()}
/>
{/* Response Time */}
<StatRow
label="Avg Response"
value={avgResponseLabel}
valueColor="text-primary-blue"
valueFont="mono"
/>
</Stack>
{/* Consecutive Failures */}
{apiHealth.consecutiveFailures > 0 && (
<Stack mt={2}>
<StatusIndicator
icon={Activity}
label="Consecutive Failures"
subLabel={consecutiveFailuresLabel}
variant="danger"
/>
</Stack>
)}
{/* Circuit Breakers */}
<Stack mt={2}>
<Text size="xs" color="text-gray-500" block mb={1}>Circuit Breakers:</Text>
{Object.keys(circuitBreakers).length === 0 ? (
<Text size="xs" color="text-gray-500" italic>None active</Text>
) : (
<Stack gap={1} maxHeight="4rem" overflow="auto">
{Object.entries(circuitBreakers).map(([endpoint, status]) => (
<Stack key={endpoint} direction="row" align="center" justify="between">
<Text size="xs" color="text-gray-400" truncate flexGrow={1}>
{endpoint}
</Text>
<Badge variant={status.state === 'CLOSED' ? 'success' : status.state === 'OPEN' ? 'danger' : 'warning'}>
{status.state}
</Badge>
{status.failures > 0 && (
<Text size="xs" color="text-red-400" ml={1}>({status.failures})</Text>
)}
</Stack>
))}
</Stack>
)}
</Stack>
{/* API Actions */}
<Grid cols={2} gap={2} mt={3}>
<Button
variant="primary"
onClick={onHealthCheck}
disabled={checkingHealth}
size="sm"
icon={<Icon icon={RefreshCw} size={3} animate={checkingHealth ? 'spin' : 'none'} />}
>
Health Check
</Button>
<Button
variant="secondary"
onClick={onResetStats}
size="sm"
>
Reset Stats
</Button>
</Grid>
<Grid cols={1} gap={2} mt={2}>
<Button
variant="danger"
onClick={onTestError}
size="sm"
icon={<Icon icon={Terminal} size={3} />}
>
Test Error Handler
</Button>
</Grid>
<Stack mt={2}>
<Text size="xs" color="text-gray-600">
Last Check: {lastCheckLabel}
</Text>
</Stack>
</Stack>
);
}