aethex-forge/client/pages/Status.tsx
Builder.io 319e68ae56 Add API_BASE and fix URLs in Status.tsx
cgen-b0ba8ff0fb1a4188bc26e6e10d23a0e6
2025-11-13 02:59:05 +00:00

359 lines
12 KiB
TypeScript

import { useState, useEffect } from "react";
const API_BASE = import.meta.env.VITE_API_BASE || "";
import Layout from "@/components/Layout";
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "@/components/ui/card";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import {
CheckCircle,
XCircle,
AlertTriangle,
RefreshCw,
Server,
Database,
Globe,
Activity,
Clock,
Wifi,
Shield,
Zap,
} from "lucide-react";
interface ServiceStatus {
name: string;
status: "operational" | "degraded" | "outage";
responseTime: number;
uptime: string;
lastCheck: string;
description: string;
}
interface SystemMetric {
name: string;
value: string;
unit: string;
status: "good" | "warning" | "critical";
icon: any;
}
function iconFor(name: string) {
switch (name) {
case "Activity":
return Activity;
case "Zap":
return Zap;
case "Globe":
return Globe;
case "Shield":
return Shield;
default:
return Activity;
}
}
export default function Status() {
const [services, setServices] = useState<ServiceStatus[]>([]);
const [metrics, setMetrics] = useState<SystemMetric[]>([]);
const [lastUpdated, setLastUpdated] = useState(new Date());
const [isRefreshing, setIsRefreshing] = useState(false);
const fetchStatus = async () => {
try {
const resp = await fetch(`${API_BASE}/api/status`);
if (!resp.ok) throw new Error("Status API failed");
const data = await resp.json();
const mappedMetrics: SystemMetric[] = (
Array.isArray(data.metrics) ? data.metrics : []
).map((it: any) => ({
name: String(it.name),
value: String(it.value ?? "--"),
unit: String(it.unit ?? ""),
status: (it.status ?? "good") as any,
icon: iconFor(String(it.icon || "Activity")),
}));
setMetrics(mappedMetrics);
const mappedServices: ServiceStatus[] = (
Array.isArray(data.services) ? data.services : []
).map((it: any) => ({
name: String(it.name),
status: (it.status ?? "operational") as any,
responseTime: Number(it.responseTime) || 0,
uptime: String(it.uptime ?? "--"),
lastCheck: new Date(
it.lastCheck || data.updatedAt || Date.now(),
).toLocaleTimeString(),
description: String(it.description || ""),
}));
setServices(mappedServices);
setLastUpdated(new Date(data.updatedAt || Date.now()));
} catch (e) {
setMetrics([
{
name: "Global Uptime",
value: "--",
unit: "%",
status: "warning",
icon: Activity,
},
]);
}
};
const getStatusIcon = (status: string) => {
switch (status) {
case "operational":
return <CheckCircle className="h-5 w-5 text-green-500" />;
case "degraded":
return <AlertTriangle className="h-5 w-5 text-yellow-500" />;
case "outage":
return <XCircle className="h-5 w-5 text-red-500" />;
default:
return <CheckCircle className="h-5 w-5 text-gray-400" />;
}
};
const getStatusColor = (status: string) => {
switch (status) {
case "operational":
return "bg-green-500";
case "degraded":
return "bg-yellow-500";
case "outage":
return "bg-red-500";
default:
return "bg-gray-400";
}
};
const getMetricColor = (status: string) => {
switch (status) {
case "good":
return "text-green-400";
case "warning":
return "text-yellow-400";
case "critical":
return "text-red-400";
default:
return "text-gray-400";
}
};
const getOverallStatus = () => {
const hasOutage = services.some((s) => s.status === "outage");
const hasDegraded = services.some((s) => s.status === "degraded");
if (hasOutage) return { status: "outage", message: "Service Disruption" };
if (hasDegraded) return { status: "degraded", message: "Partial Outage" };
return { status: "operational", message: "All Systems Operational" };
};
const refreshStatus = async () => {
setIsRefreshing(true);
await fetchStatus();
setIsRefreshing(false);
};
useEffect(() => {
fetchStatus();
const t = setInterval(fetchStatus, 60000);
return () => clearInterval(t);
}, []);
const overall = getOverallStatus();
return (
<Layout>
<div className="min-h-screen bg-gradient-to-br from-slate-900 via-purple-900 to-slate-900">
<div className="container mx-auto px-4 py-8">
{/* Header */}
<div className="mb-8">
<div className="flex items-center justify-between mb-4">
<div>
<h1 className="text-3xl font-bold text-white mb-2">
AeThex System Status
</h1>
<p className="text-gray-300">
Real-time status and performance monitoring
</p>
</div>
<Button
onClick={refreshStatus}
disabled={isRefreshing}
className="bg-purple-600 hover:bg-purple-700"
>
<RefreshCw
className={`h-4 w-4 mr-2 ${isRefreshing ? "animate-spin" : ""}`}
/>
Refresh
</Button>
</div>
{/* Overall Status */}
<Card className="bg-slate-800/50 border-slate-700">
<CardContent className="p-6">
<div className="flex items-center justify-between">
<div className="flex items-center space-x-3">
{getStatusIcon(overall.status)}
<div>
<h2 className="text-xl font-semibold text-white">
{overall.message}
</h2>
<p className="text-gray-400">
Last updated: {lastUpdated.toLocaleTimeString()}
</p>
</div>
</div>
<Badge
className={`${getStatusColor(overall.status)} text-white border-0 px-3 py-1`}
>
{overall.status.toUpperCase()}
</Badge>
</div>
</CardContent>
</Card>
</div>
{/* System Metrics */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4 mb-8">
{metrics.map((metric, index) => {
const Icon = metric.icon;
return (
<Card key={index} className="bg-slate-800/50 border-slate-700">
<CardContent className="p-4">
<div className="flex items-center justify-between">
<div>
<p className="text-sm text-gray-400">{metric.name}</p>
<p
className={`text-2xl font-bold ${getMetricColor(metric.status)}`}
>
{metric.value}
{metric.unit}
</p>
</div>
<Icon
className={`h-8 w-8 ${getMetricColor(metric.status)}`}
/>
</div>
</CardContent>
</Card>
);
})}
</div>
{/* Service Status */}
<Card className="bg-slate-800/50 border-slate-700">
<CardHeader>
<CardTitle className="text-white flex items-center">
<Server className="h-5 w-5 mr-2" />
Service Status
</CardTitle>
<CardDescription className="text-gray-300">
Current operational status of all AeThex services
</CardDescription>
</CardHeader>
<CardContent>
<div className="space-y-4">
{services.map((service, index) => (
<div
key={index}
className="flex items-center justify-between p-4 bg-slate-900/50 rounded-lg border border-slate-600"
>
<div className="flex items-center space-x-4">
{getStatusIcon(service.status)}
<div>
<h4 className="text-white font-medium">
{service.name}
</h4>
<p className="text-sm text-gray-400">
{service.description}
</p>
</div>
</div>
<div className="text-right">
<div className="flex items-center space-x-4">
<div>
<p className="text-sm text-gray-400">Response Time</p>
<p className="text-white font-medium">
{service.responseTime}ms
</p>
</div>
<div>
<p className="text-sm text-gray-400">Uptime</p>
<p className="text-white font-medium">
{service.uptime}
</p>
</div>
<Badge
className={`${getStatusColor(service.status)} text-white border-0`}
>
{service.status}
</Badge>
</div>
<p className="text-xs text-gray-500 mt-1">
Last check: {service.lastCheck}
</p>
</div>
</div>
))}
</div>
</CardContent>
</Card>
{/* Recent Incidents */}
<Card className="bg-slate-800/50 border-slate-700 mt-8">
<CardHeader>
<CardTitle className="text-white flex items-center">
<Clock className="h-5 w-5 mr-2" />
Recent Incidents
</CardTitle>
</CardHeader>
<CardContent>
<div className="space-y-3">
<div className="flex items-start space-x-3 p-3 bg-slate-900/50 rounded-lg border border-slate-600">
<CheckCircle className="h-5 w-5 text-green-500 mt-0.5" />
<div>
<p className="text-white font-medium">
Project Management Performance Restored
</p>
<p className="text-sm text-gray-400">
Response times have returned to normal after brief
degradation
</p>
<p className="text-xs text-gray-500 mt-1">
2 hours ago Resolved
</p>
</div>
</div>
<div className="flex items-start space-x-3 p-3 bg-slate-900/50 rounded-lg border border-slate-600">
<CheckCircle className="h-5 w-5 text-green-500 mt-0.5" />
<div>
<p className="text-white font-medium">
Scheduled Maintenance Completed
</p>
<p className="text-sm text-gray-400">
Database optimization and security updates applied
successfully
</p>
<p className="text-xs text-gray-500 mt-1">
1 day ago Resolved
</p>
</div>
</div>
</div>
</CardContent>
</Card>
</div>
</div>
</Layout>
);
}