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} from "lucide-react" import * as LucideIcons from "lucide-react" import { useTheme } from "@/components/theme-provider" import { useMetrics } from "./hooks/useMetrics" import { useConfig } from "./hooks/useConfig" import { ServiceStatus } from "./types/metrics" import { ServiceCard, Section, CardStyle } from "./types/services" import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip" import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert" import { AlertCircle } from "lucide-react" const getIconUrl = (name: string | undefined, isDark: boolean = false): string => { if (!name) return '' // Convert to selfh.st reference format const ref = name.toLowerCase().replace(/[^a-z0-9]/g, '-') // Use light version for dark mode if available const suffix = isDark ? '-light' : '' return `https://cdn.jsdelivr.net/gh/selfhst/icons/png/${ref}${suffix}.png` } function ServiceIcon({ service, section, isDark }: { service: ServiceCard, section: Section, isDark: boolean }) { if (service.icon) { return
{service.icon}
} if (service.iconName) { // Handle Lucide icons if (service.iconName.startsWith('lucide-')) { const iconName = service.iconName.replace('lucide-', '') .split('-') .map(part => part.charAt(0).toUpperCase() + part.slice(1)) .join('') // Cast to any to avoid TypeScript complexity with dynamic imports const Icon = (LucideIcons as any)[iconName] || LucideIcons.Box return } // Handle selfh.st icons return ( {`${service.name} { if (isDark && e.currentTarget.src.includes('-light')) { e.currentTarget.src = getIconUrl(service.iconName, false) } }} /> ) } // Use section's default icon const Icon = (LucideIcons as any)[section.icon] || LucideIcons.Box return } function ThemeToggle() { const { theme, setTheme } = useTheme() const cycleTheme = () => { if (theme === 'light') setTheme('dark') else if (theme === 'dark') setTheme('system') else setTheme('light') } return ( ) } function StatusIndicator({ status, responseTime, certDaysRemaining }: { status: ServiceStatus, responseTime: number, certDaysRemaining: number }) { const getStatusColor = () => { switch (status) { case 'up': return 'bg-green-500' case 'down': return 'bg-red-500' case 'pending': return 'bg-yellow-500' case 'maintenance': return 'bg-blue-500' } } 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 (

{getStatusText()}

) } function ServiceSection({ section, services, metrics }: { section: Section, services: ServiceCard[], metrics: Record }) { const { theme } = useTheme() const isDark = theme === 'dark' || (theme === 'system' && window.matchMedia('(prefers-color-scheme: dark)').matches) return (

{section.title}

{services.map((service) => { const serviceMetrics = metrics[service.monitorName || service.name] || { status: 'pending', responseTime: 0, certDaysRemaining: 0 } const style: CardStyle = { background: service.cardStyle?.background || section.cardStyle?.background || "bg-gray-50 dark:bg-gray-950/30", iconColor: service.cardStyle?.iconColor || section.cardStyle?.iconColor || "text-gray-600 dark:text-gray-400" } return (
{service.name}
{service.description}
) })}
) } function App() { const { metrics } = useMetrics() const { config, error } = useConfig() const { services, sections } = config return (

Kent.pw Homepage

{error && ( Error Failed to load configuration: {error} )} {sections?.length > 0 ? ( <> {sections.map((section: Section) => { const sectionServices = services.filter((s: { category: string }) => s.category === section.id) if (sectionServices.length === 0) return null return ( ) })} ) : !error && ( Loading Loading service configuration... )}
) } export default App