PlatformsCard.tsx
1 import { AlertTriangle, Radio, Wifi, WifiOff } from "lucide-react"; 2 import type { PlatformStatus } from "@/lib/api"; 3 import { isoTimeAgo } from "@/lib/utils"; 4 import { Badge } from "@nous-research/ui/ui/components/badge"; 5 import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; 6 import { useI18n } from "@/i18n"; 7 8 export function PlatformsCard({ platforms }: PlatformsCardProps) { 9 const { t } = useI18n(); 10 const platformStateBadge: Record< 11 string, 12 { tone: "success" | "warning" | "destructive"; label: string } 13 > = { 14 connected: { tone: "success", label: t.status.connected }, 15 disconnected: { tone: "warning", label: t.status.disconnected }, 16 fatal: { tone: "destructive", label: t.status.error }, 17 }; 18 19 return ( 20 <Card> 21 <CardHeader> 22 <div className="flex items-center gap-2"> 23 <Radio className="h-5 w-5 text-muted-foreground" /> 24 <CardTitle className="text-base"> 25 {t.status.connectedPlatforms} 26 </CardTitle> 27 </div> 28 </CardHeader> 29 30 <CardContent className="grid gap-3"> 31 {platforms.map(([name, info]) => { 32 const display = platformStateBadge[info.state] ?? { 33 tone: "outline" as const, 34 label: info.state, 35 }; 36 const IconComponent = 37 info.state === "connected" 38 ? Wifi 39 : info.state === "fatal" 40 ? AlertTriangle 41 : WifiOff; 42 43 return ( 44 <div 45 key={name} 46 className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-2 border border-border p-3 w-full" 47 > 48 <div className="flex items-center gap-3 min-w-0 w-full"> 49 <IconComponent 50 className={`h-4 w-4 shrink-0 ${ 51 info.state === "connected" 52 ? "text-success" 53 : info.state === "fatal" 54 ? "text-destructive" 55 : "text-warning" 56 }`} 57 /> 58 59 <div className="flex flex-col gap-0.5 min-w-0"> 60 <span className="text-sm font-medium capitalize truncate"> 61 {name} 62 </span> 63 64 {info.error_message && ( 65 <span className="text-xs text-destructive"> 66 {info.error_message} 67 </span> 68 )} 69 70 {info.updated_at && ( 71 <span className="text-xs text-muted-foreground"> 72 {t.status.lastUpdate}: {isoTimeAgo(info.updated_at)} 73 </span> 74 )} 75 </div> 76 </div> 77 78 <Badge 79 tone={display.tone} 80 className="shrink-0 self-start sm:self-center" 81 > 82 {display.tone === "success" && ( 83 <span className="mr-1 inline-block h-1.5 w-1.5 animate-pulse rounded-full bg-current" /> 84 )} 85 {display.label} 86 </Badge> 87 </div> 88 ); 89 })} 90 </CardContent> 91 </Card> 92 ); 93 } 94 95 interface PlatformsCardProps { 96 platforms: [string, PlatformStatus][]; 97 }