diff --git a/AppImage/components/health-status-modal.tsx b/AppImage/components/health-status-modal.tsx index 4a033bd..3496648 100644 --- a/AppImage/components/health-status-modal.tsx +++ b/AppImage/components/health-status-modal.tsx @@ -6,24 +6,19 @@ import { Badge } from "@/components/ui/badge" import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card" import { Loader2, CheckCircle2, AlertTriangle, XCircle, Activity } from "lucide-react" -interface HealthCheck { - category: string - name: string - status: "healthy" | "warning" | "critical" - value: string - message: string - details: any +interface HealthDetail { + status: string + reason?: string + [key: string]: any } interface HealthDetails { - overall: { - status: "healthy" | "warning" | "critical" - critical_count: number - warning_count: number - healthy_count: number - total_checks: number + overall: string + summary: string + details: { + [category: string]: HealthDetail | { [key: string]: HealthDetail } } - checks: HealthCheck[] + timestamp: string } interface HealthStatusModalProps { @@ -53,21 +48,92 @@ export function HealthStatusModal({ open, onOpenChange, getApiUrl }: HealthStatu throw new Error("Failed to fetch health details") } const data = await response.json() + console.log("[v0] Health data received:", data) setHealthData(data) } catch (err) { + console.error("[v0] Error fetching health data:", err) setError(err instanceof Error ? err.message : "Unknown error") } finally { setLoading(false) } } + const getHealthStats = () => { + if (!healthData?.details) { + return { total: 0, healthy: 0, warnings: 0, critical: 0 } + } + + let healthy = 0 + let warnings = 0 + let critical = 0 + let total = 0 + + const countStatus = (detail: any) => { + if (detail && typeof detail === "object" && detail.status) { + total++ + const status = detail.status.toUpperCase() + if (status === "OK") healthy++ + else if (status === "WARNING") warnings++ + else if (status === "CRITICAL") critical++ + } + } + + Object.values(healthData.details).forEach((categoryData) => { + if (categoryData && typeof categoryData === "object") { + if ("status" in categoryData) { + countStatus(categoryData) + } else { + Object.values(categoryData).forEach(countStatus) + } + } + }) + + return { total, healthy, warnings, critical } + } + + const getGroupedChecks = () => { + if (!healthData?.details) return {} + + const grouped: { [key: string]: Array<{ name: string; status: string; reason?: string; details?: any }> } = {} + + Object.entries(healthData.details).forEach(([category, categoryData]) => { + if (!categoryData || typeof categoryData !== "object") return + + const categoryName = category.charAt(0).toUpperCase() + category.slice(1) + grouped[categoryName] = [] + + if ("status" in categoryData) { + grouped[categoryName].push({ + name: categoryName, + status: categoryData.status, + reason: categoryData.reason, + details: categoryData, + }) + } else { + Object.entries(categoryData).forEach(([subKey, subData]: [string, any]) => { + if (subData && typeof subData === "object" && "status" in subData) { + grouped[categoryName].push({ + name: subKey, + status: subData.status, + reason: subData.reason, + details: subData, + }) + } + }) + } + }) + + return grouped + } + const getStatusIcon = (status: string) => { - switch (status) { - case "healthy": + const statusUpper = status?.toUpperCase() + switch (statusUpper) { + case "OK": return - case "warning": + case "WARNING": return - case "critical": + case "CRITICAL": return default: return @@ -75,31 +141,21 @@ export function HealthStatusModal({ open, onOpenChange, getApiUrl }: HealthStatu } const getStatusBadge = (status: string) => { - switch (status) { - case "healthy": + const statusUpper = status?.toUpperCase() + switch (statusUpper) { + case "OK": return Healthy - case "warning": + case "WARNING": return Warning - case "critical": + case "CRITICAL": return Critical default: return Unknown } } - const groupedChecks = - healthData?.checks && Array.isArray(healthData.checks) - ? healthData.checks.reduce( - (acc, check) => { - if (!acc[check.category]) { - acc[check.category] = [] - } - acc[check.category].push(check) - return acc - }, - {} as Record, - ) - : {} + const stats = getHealthStats() + const groupedChecks = getGroupedChecks() return ( @@ -119,7 +175,7 @@ export function HealthStatusModal({ open, onOpenChange, getApiUrl }: HealthStatu )} {error && ( -
+

Error loading health status

{error}

@@ -132,25 +188,26 @@ export function HealthStatusModal({ open, onOpenChange, getApiUrl }: HealthStatu Overall Status - {getStatusBadge(healthData.overall.status)} + {getStatusBadge(healthData.overall)} + {healthData.summary &&

{healthData.summary}

}
-
{healthData.overall.total_checks}
+
{stats.total}
Total Checks
-
{healthData.overall.healthy_count}
+
{stats.healthy}
Healthy
-
{healthData.overall.warning_count}
+
{stats.warnings}
Warnings
-
{healthData.overall.critical_count}
+
{stats.critical}
Critical
@@ -158,35 +215,52 @@ export function HealthStatusModal({ open, onOpenChange, getApiUrl }: HealthStatu {/* Grouped Health Checks */} - {groupedChecks && - Object.entries(groupedChecks).map(([category, checks]) => ( - - - {category} - - -
- {checks.map((check, index) => ( -
-
{getStatusIcon(check.status)}
-
-
-

{check.name}

- - {check.value} - -
-

{check.message}

+ {Object.entries(groupedChecks).map(([category, checks]) => ( + + + {category} + + +
+ {checks.map((check, index) => ( +
+
{getStatusIcon(check.status)}
+
+
+

{check.name}

+ + {check.status} +
+ {check.reason &&

{check.reason}

} + {check.details && ( +
+ {Object.entries(check.details).map(([key, value]) => { + if (key === "status" || key === "reason" || typeof value === "object") return null + return ( +
+ {key}: {String(value)} +
+ ) + })} +
+ )}
- ))} -
- - - ))} +
+ ))} +
+ + + ))} + + {healthData.timestamp && ( +
+ Last updated: {new Date(healthData.timestamp).toLocaleString()} +
+ )}
)}