"use client"; import { useState, useMemo } from "react"; import { usePoll } from "@/lib/use-poll"; import type { Container, OverviewStats, KumaStats, DiskUsageEntry } from "@/lib/types"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Button } from "@/components/ui/button"; import { Badge } from "@/components/ui/badge"; import { DataTable, Column } from "@/components/data-table"; import { ContainerLogsModal } from "@/components/container-logs-modal"; import { StatusBadge } from "@/components/status-badge"; import { postAPI } from "@/lib/api"; import { CardSkeleton } from "@/components/skeleton"; interface OlaresPod { name: string; namespace: string; status: string; restarts: number; age: string; } const endpointColors: Record = { atlantis: "text-blue-400", calypso: "text-violet-400", olares: "text-emerald-400", nuc: "text-amber-400", rpi5: "text-cyan-400", homelab: "text-green-400", }; function getContainerStateColor(state: string): string { const lower = state.toLowerCase(); if (lower === "running") return "text-green-400"; if (lower === "exited" || lower === "dead") return "text-red-400"; if (lower === "created" || lower === "restarting" || lower === "paused") return "text-amber-400"; return "text-foreground"; } const hostColors: Record = { atlantis: "text-blue-400", calypso: "text-violet-400", olares: "text-emerald-400", nuc: "text-amber-400", rpi5: "text-cyan-400", homelab: "text-green-400", guava: "text-orange-400", seattle: "text-teal-400", jellyfish: "text-indigo-400", "matrix-ubuntu": "text-pink-400", }; export default function InfrastructurePage() { const { data: containers } = usePoll( "/api/containers", 30000 ); const { data: overview } = usePoll( "/api/stats/overview", 60000 ); const { data: pods } = usePoll("/api/olares/pods", 30000); const { data: kuma } = usePoll("/api/kuma/monitors", 60000); const { data: disks } = usePoll("/api/disk-usage", 300000); const [logsTarget, setLogsTarget] = useState<{ id: string; name: string; endpoint: string; } | null>(null); const [hoveredMonitor, setHoveredMonitor] = useState(null); const endpoints = useMemo(() => { if (!containers) return []; return [...new Set(containers.map((c) => c.endpoint))]; }, [containers]); const containerColumns: Column[] = [ { key: "name", label: "Name", render: (row) => ( {row.name} ), }, { key: "state", label: "State", render: (row) => ( ), }, { key: "status", label: "Status" }, { key: "endpoint", label: "Endpoint", render: (row) => ( {row.endpoint} ), }, { key: "image", label: "Image", render: (row) => ( {row.image} ), }, ]; const podColumns: Column[] = [ { key: "name", label: "Pod" }, { key: "namespace", label: "Namespace" }, { key: "status", label: "Status", render: (row) => ( ), }, { key: "restarts", label: "Restarts" }, { key: "age", label: "Age" }, ]; const gpu = overview?.gpu; return (

Infrastructure

{/* Kuma Monitors */} Uptime Kuma {kuma && (
{kuma.up} up {kuma.down > 0 && ( {kuma.down} down )} {kuma.total} total
)}
{!kuma ? ( ) : (
{kuma.monitors.map((m) => (
setHoveredMonitor(m.id)} onMouseLeave={() => setHoveredMonitor(null)} > {hoveredMonitor === m.id && (

{m.name}

{m.url &&

{m.url}

}

{!m.active ? "Inactive" : m.status ? "Up" : "Down"}

)}
))}
)}
{/* Container Table */} Containers data={containers ?? []} columns={containerColumns} searchKey="name" filterKey="endpoint" filterOptions={endpoints} actions={(row) => (
)} />
{/* Row 2: Olares Pods + GPU */}
Olares Pods data={pods ?? []} columns={podColumns} searchKey="name" /> GPU Status {!gpu ? ( ) : gpu.available ? ( <>

{gpu.name}

{gpu.vram_used_mb != null && gpu.vram_total_mb != null && (
VRAM {(gpu.vram_used_mb / 1024).toFixed(1)} /{" "} {(gpu.vram_total_mb / 1024).toFixed(1)} GB
)}

Temperature

{gpu.temp_c ?? "--"}°C

Power

{gpu.power_w ?? "--"}W

Utilization

{gpu.utilization_pct ?? "--"}%

) : (

GPU not available

)}
{/* Disk Usage - Full */} Disk Usage {!disks ? ( ) : disks.length === 0 ? (

No disk data

) : (
{[...disks] .sort((a, b) => b.used_pct - a.used_pct) .map((d, i) => { const color = d.used_pct >= 85 ? "from-red-500 to-red-400" : d.used_pct >= 70 ? "from-amber-500 to-amber-400" : "from-green-500 to-emerald-400"; const hostCls = hostColors[d.host.toLowerCase()] ?? "text-foreground"; return (
{d.host} {d.mount}
{d.total_gb >= 1000 ? `${(d.total_gb / 1000).toFixed(1)} TB` : `${Math.round(d.total_gb)} GB`} {d.used_pct}%
); })}
)} {/* Logs Modal */} setLogsTarget(null)} />
); }