import { createFileRoute } from "@tanstack/react-router"; import { useState, useEffect } from "react"; import { client, type StatusResponse, type Status } from "../lib/api"; import { Card, Flex, Text, Tooltip, Skeleton } from "@radix-ui/themes"; import { CheckCircle, XCircle, Clock, Bot, Globe, Hourglass, Activity, MessageCircle, Circle, WifiOff, } from "lucide-react"; import TimeAgo from "react-timeago"; import { ThemeToggle } from "../components/ThemeToggle"; import "../App.css"; export const Route = createFileRoute("/")({ component: App, }); // Constants const REFRESH_INTERVAL = import.meta.env.DEV ? 3000 : 30000; const REQUEST_TIMEOUT = 10000; // 10 seconds const CARD_STYLES = { padding: "24px", maxWidth: "400px", width: "100%", } as const; const BORDER_STYLES = { marginTop: "16px", paddingTop: "16px", borderTop: "1px solid #e2e8f0", } as const; // Service icon mapping const SERVICE_ICONS: Record = { bot: Bot, banner: Globe, discord: MessageCircle, }; // Types interface ResponseTiming { health: number | null; status: number | null; } interface StatusIcon { icon: typeof CheckCircle; color: string; } interface Service { name: string; status: Status; icon: typeof Bot; } type StatusState = | { mode: "loading"; } | { mode: "response"; timing: ResponseTiming; lastFetch: Date; status: StatusResponse; } | { mode: "error"; lastFetch: Date; } | { mode: "timeout"; lastFetch: Date; }; // Helper functions const getStatusIcon = (status: Status | "Unreachable"): StatusIcon => { const statusMap: Record = { Active: { icon: CheckCircle, color: "green" }, Connected: { icon: CheckCircle, color: "green" }, Healthy: { icon: CheckCircle, color: "green" }, Disabled: { icon: Circle, color: "gray" }, Error: { icon: XCircle, color: "red" }, Unreachable: { icon: WifiOff, color: "red" }, }; return statusMap[status]; }; const getOverallHealth = (state: StatusState): Status | "Unreachable" => { if (state.mode === "timeout") return "Unreachable"; if (state.mode === "error") return "Error"; if (state.mode === "response") return state.status.status; return "Error"; }; const getServices = (state: StatusState): Service[] => { if (state.mode !== "response") return []; return Object.entries(state.status.services).map( ([serviceId, serviceInfo]) => ({ name: serviceInfo.name, status: serviceInfo.status, icon: SERVICE_ICONS[serviceId] || SERVICE_ICONS.default, }) ); }; // Status Component const StatusDisplay = ({ status }: { status: Status | "Unreachable" }) => { const { icon: Icon, color } = getStatusIcon(status); return ( {status} ); }; // Service Status Component const ServiceStatus = ({ service }: { service: Service }) => { return ( {service.name} ); }; // Skeleton Service Component const SkeletonService = () => { return ( ); }; // Timing Row Component const TimingRow = ({ icon: Icon, name, children, }: { icon: React.ComponentType<{ size?: number }>; name: string; children: React.ReactNode; }) => ( {name} {children} ); function App() { const [state, setState] = useState({ mode: "loading" }); // Helper variables for state checking const isLoading = state.mode === "loading"; const hasError = state.mode === "error"; const hasTimeout = state.mode === "timeout"; const hasResponse = state.mode === "response"; const shouldShowSkeleton = isLoading || hasError; const shouldShowTiming = hasResponse && state.timing.health !== null; const shouldShowLastFetch = hasResponse || hasError || hasTimeout; useEffect(() => { let timeoutId: NodeJS.Timeout; const fetchData = async () => { try { const startTime = Date.now(); // Create a timeout promise const timeoutPromise = new Promise((_, reject) => { setTimeout( () => reject(new Error("Request timeout")), REQUEST_TIMEOUT ); }); // Race between the API call and timeout const statusData = await Promise.race([ client.getStatus(), timeoutPromise, ]); const endTime = Date.now(); const responseTime = endTime - startTime; setState({ mode: "response", status: statusData, timing: { health: responseTime, status: responseTime }, lastFetch: new Date(), }); } catch (err) { const errorMessage = err instanceof Error ? err.message : "Failed to fetch data"; // Check if it's a timeout error if (errorMessage === "Request timeout") { setState({ mode: "timeout", lastFetch: new Date(), }); } else { setState({ mode: "error", lastFetch: new Date(), }); } } // Schedule the next request after the current one completes timeoutId = setTimeout(fetchData, REFRESH_INTERVAL); }; // Start the first request immediately fetchData(); return () => { if (timeoutId) { clearTimeout(timeoutId); } }; }, []); const overallHealth = getOverallHealth(state); const { color: overallColor } = getStatusIcon(overallHealth); const services = getServices(state); return (
{/* Theme Toggle - Fixed position in top right */}
{/* Overall Status */} System Status {isLoading ? ( ) : ( )} {/* Individual Services */} {shouldShowSkeleton ? // Show skeleton for 3 services during initial loading only Array.from({ length: 3 }).map((_, index) => ( )) : services.map((service) => ( ))} {isLoading ? ( ) : shouldShowTiming ? ( {state.timing.health}ms ) : null} {shouldShowLastFetch ? ( {isLoading ? ( Loading... ) : ( )} ) : isLoading ? ( Loading... ) : null} {__APP_VERSION__ && ( v{__APP_VERSION__} )} {__APP_VERSION__ && ( ); }