/ web / src / components / PlatformsCard.tsx
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  }