import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card" import { Separator } from "@/components/ui/separator" import { Button } from "@/components/ui/button" import { Moon, Sun, Monitor, CheckCircle2, XCircle, AlertCircle, Clock } from "lucide-react" import { useTheme } from "@/components/theme-provider" import { useMetrics } from "./hooks/useMetrics" import { ServiceStatus } from "./types/metrics" import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip" import { Server, Box, Play, Activity, Shield, Database, Download, Home, Tv, Globe, Gauge, HardDrive, Search, Settings, LayoutDashboard, Package, Smartphone } from "lucide-react" interface ServiceCard { name: string description: string url: string icon: React.ReactNode category: 'system' | 'media' | 'monitoring' | 'tools' | 'acot' | 'home' monitorName?: string // Optional monitor name that matches Uptime Kuma } const services: ServiceCard[] = [ { name: "Portainer", description: "Container Management", url: "https://portainer.kent.pw", icon: , category: "system" }, { name: "Gitea", description: "Git Server", url: "https://gitea.kent.pw", icon: , category: "system" }, { name: "Cockpit", description: "Server Management", url: "https://cockpit.kent.pw", icon: , category: "system" }, { name: "DiskStation", description: "Synology NAS", url: "https://diskstation.kent.pw", icon: , category: "system", monitorName: "Synology Diskstation" }, { name: "Plex", description: "Media Server", url: "https://plex.kent.pw", icon: , category: "media" }, { name: "Sonarr", description: "TV Show Management", url: "https://sonarr.kent.pw", icon: , category: "media" }, { name: "Jackett", description: "Torrent Indexer", url: "https://jackett.kent.pw", icon: , category: "media" }, { name: "Deluge", description: "Torrent Client", url: "https://deluge.kent.pw", icon: , category: "media" }, { name: "Uptime", description: "Service Monitoring", url: "https://uptime.kent.pw", icon: , category: "monitoring" }, { name: "AdGuard", description: "Network Ad Blocking", url: "https://adguard.kent.pw", icon: , category: "system", monitorName: "Adguard Home" }, { name: "NocoDB", description: "Database Platform", url: "https://noco.kent.pw", icon: , category: "tools", monitorName: "NocoDB" }, { name: "IT Tools", description: "Developer Utilities", url: "https://ittools.kent.pw", icon: , category: "tools", monitorName: "IT Tools" }, { name: "Firefox", description: "Browser Instance", url: "https://firefox.kent.pw", icon: , category: "tools" }, { name: "Speedtest", description: "Network Speed Monitor", url: "https://speedtest.kent.pw", icon: , category: "monitoring", monitorName: "Speedtest Tracker" }, { name: "Drive", description: "File Storage", url: "https://drive.kent.pw", icon: , category: "tools", monitorName: "Synology Drive" }, { name: "Dashboard", description: "ACOT Dashboard", url: "https://dashboard.kent.pw", icon: , category: "acot" }, { name: "Inventory", description: "ACOT Inventory", url: "https://inventory.kent.pw", icon: , category: "acot" }, { name: "Homebridge", description: "HomeKit Bridge", url: "https://homebridge.kent.pw", icon: , category: "home" }, { name: "Scrypted", description: "Smart Home Integration", url: "https://scrypted.kent.pw", icon: , category: "home" } ] function ThemeToggle() { const { theme, setTheme } = useTheme() const cycleTheme = () => { if (theme === 'light') setTheme('dark') else if (theme === 'dark') setTheme('system') else setTheme('light') } return ( Toggle theme ) } function StatusIndicator({ status, responseTime, certDaysRemaining }: { status: ServiceStatus, responseTime: number, certDaysRemaining: number }) { const getStatusIcon = () => { switch (status) { case 'up': return case 'down': return case 'pending': return case 'maintenance': return } } const getStatusText = () => { const statusText = status.charAt(0).toUpperCase() + status.slice(1) const responseTimeText = `Response time: ${responseTime}ms` const certText = certDaysRemaining > 0 ? `SSL cert expires in ${certDaysRemaining} days` : 'SSL cert expired' return `${statusText} • ${responseTimeText} • ${certText}` } return ( {getStatusIcon()} {getStatusText()} ) } function ServiceSection({ title, services, metrics }: { title: string, services: ServiceCard[], metrics: Record }) { return ( {title} {services.map((service) => { const serviceMetrics = metrics[service.monitorName || service.name] || { status: 'pending', responseTime: 0, certDaysRemaining: 0 } // Get background color based on service const bgColor = (() => { switch (service.name) { case "Portainer": return "bg-blue-50 dark:bg-blue-950/30" // Portainer blue case "Gitea": return "bg-green-50 dark:bg-green-950/30" // Gitea green case "Plex": return "bg-orange-50 dark:bg-orange-950/30" // Plex orange case "Sonarr": return "bg-blue-50 dark:bg-blue-950/30" // Sonarr blue case "AdGuard": return "bg-emerald-50 dark:bg-emerald-950/30" // AdGuard green case "Homebridge": return "bg-purple-50 dark:bg-purple-950/30" // Homebridge purple case "Scrypted": return "bg-indigo-50 dark:bg-indigo-950/30" // Scrypted blue/purple case "Dashboard": case "Inventory": return "bg-sky-50 dark:bg-sky-950/30" // ACOT blue case "DiskStation": return "bg-slate-50 dark:bg-slate-950/30" // Synology gray case "Deluge": return "bg-green-50 dark:bg-green-950/30" // Deluge green case "Firefox": return "bg-orange-50 dark:bg-orange-950/30" // Firefox orange case "Uptime": return "bg-emerald-50 dark:bg-emerald-950/30" // Uptime green (success color) case "Speedtest": return "bg-blue-50 dark:bg-blue-950/30" // Speedtest blue default: return "bg-gray-50 dark:bg-gray-950/30" // Default subtle background } })() // Get icon color based on service const iconColor = (() => { switch (service.name) { case "Portainer": return "text-blue-600 dark:text-blue-400" case "Gitea": return "text-green-600 dark:text-green-400" case "Plex": return "text-orange-600 dark:text-orange-400" case "Sonarr": return "text-blue-600 dark:text-blue-400" case "AdGuard": return "text-emerald-600 dark:text-emerald-400" case "Homebridge": return "text-purple-600 dark:text-purple-400" case "Scrypted": return "text-indigo-600 dark:text-indigo-400" case "Dashboard": case "Inventory": return "text-sky-600 dark:text-sky-400" case "DiskStation": return "text-slate-600 dark:text-slate-400" case "Deluge": return "text-green-600 dark:text-green-400" case "Firefox": return "text-orange-600 dark:text-orange-400" case "Uptime": return "text-emerald-600 dark:text-emerald-400" case "Speedtest": return "text-blue-600 dark:text-blue-400" default: return "text-gray-600 dark:text-gray-400" } })() return ( {service.icon} {service.name} {service.description} ) })} ) } function App() { const { metrics, loading, error } = useMetrics(import.meta.env.VITE_UPTIME_API_KEY) const systemServices = services.filter(s => s.category === 'system') const mediaServices = services.filter(s => s.category === 'media') const monitoringServices = services.filter(s => s.category === 'monitoring') const toolServices = services.filter(s => s.category === 'tools') const acotServices = services.filter(s => s.category === 'acot') const homeServices = services.filter(s => s.category === 'home') return ( Kent.pw Homepage ) } export default App
{getStatusText()}