Spaces:
Sleeping
Sleeping
| "use client"; | |
| import { useEffect, useState } from "react"; | |
| import { Repository, RepoStatus, AggregatedMetrics, fetchRepoStatus, fetchRepoMetrics } from "@/lib/api"; | |
| import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; | |
| import { StatusBadge } from "./StatusBadge"; | |
| import { MetricChart } from "./MetricChart"; | |
| import { Button } from "@/components/ui/button"; | |
| import { Trash2, ExternalLink, Box, RefreshCw } from "lucide-react"; | |
| import { | |
| AlertDialog, | |
| AlertDialogAction, | |
| AlertDialogCancel, | |
| AlertDialogContent, | |
| AlertDialogDescription, | |
| AlertDialogFooter, | |
| AlertDialogHeader, | |
| AlertDialogTitle, | |
| AlertDialogTrigger, | |
| } from "@/components/ui/alert-dialog"; | |
| interface RepositoryCardProps { | |
| repo: Repository; | |
| onRemove: (id: string) => void; | |
| timeRange: string; | |
| } | |
| export function RepositoryCard({ repo, onRemove, timeRange }: RepositoryCardProps) { | |
| const [status, setStatus] = useState<RepoStatus | null>(null); | |
| const [metrics, setMetrics] = useState<AggregatedMetrics | null>(null); | |
| const [loading, setLoading] = useState(true); | |
| const pollData = async () => { | |
| try { | |
| const [newStatus, newMetrics] = await Promise.all([ | |
| fetchRepoStatus(repo.id), | |
| fetchRepoMetrics(repo.id, timeRange) | |
| ]); | |
| setStatus(newStatus); | |
| setMetrics(newMetrics); | |
| } catch (err) { | |
| console.error("Failed to poll card data", err); | |
| } finally { | |
| setLoading(false); | |
| } | |
| }; | |
| useEffect(() => { | |
| pollData(); | |
| const interval = setInterval(pollData, 10000); // Poll every 10s | |
| return () => clearInterval(interval); | |
| }, [repo.id, timeRange]); | |
| return ( | |
| <Card className="group border-border/50 overflow-hidden bg-card/40 transition-all hover:bg-card/60"> | |
| <CardHeader className="flex flex-row items-start justify-between space-y-0 p-6"> | |
| <div className="flex gap-4"> | |
| <div className="mt-1 flex h-10 w-10 items-center justify-center rounded-lg bg-primary/10 text-primary"> | |
| <Box className="h-6 w-6" /> | |
| </div> | |
| <div className="space-y-1"> | |
| <CardTitle className="text-xl font-semibold flex items-center gap-2"> | |
| {repo.repo} | |
| <a | |
| href={`https://huggingface.co/spaces/${repo.namespace}/${repo.repo}`} | |
| target="_blank" | |
| rel="noreferrer" | |
| className="text-muted-foreground hover:text-primary transition-colors" | |
| > | |
| <ExternalLink className="h-4 w-4" /> | |
| </a> | |
| </CardTitle> | |
| <p className="text-sm text-muted-foreground">{repo.namespace}</p> | |
| </div> | |
| </div> | |
| <div className="flex items-center gap-2"> | |
| {status ? ( | |
| <> | |
| <StatusBadge type="status" value={status.state} /> | |
| <StatusBadge type="stage" value={status.stage} /> | |
| </> | |
| ) : ( | |
| <div className="h-6 w-32 bg-secondary animate-pulse rounded-full" /> | |
| )} | |
| <AlertDialog> | |
| <AlertDialogTrigger asChild> | |
| <Button variant="ghost" size="icon" className="h-8 w-8 text-muted-foreground hover:text-destructive"> | |
| <Trash2 className="h-4 w-4" /> | |
| </Button> | |
| </AlertDialogTrigger> | |
| <AlertDialogContent> | |
| <AlertDialogHeader> | |
| <AlertDialogTitle>Delete Monitoring?</AlertDialogTitle> | |
| <AlertDialogDescription> | |
| This will stop monitoring the repository <strong>{repo.namespace}/{repo.repo}</strong>. This action cannot be undone. | |
| </AlertDialogDescription> | |
| </AlertDialogHeader> | |
| <AlertDialogFooter> | |
| <AlertDialogCancel>Cancel</AlertDialogCancel> | |
| <AlertDialogAction onClick={() => onRemove(repo.id)} className="bg-destructive text-destructive-foreground hover:bg-destructive/90"> | |
| Delete | |
| </AlertDialogAction> | |
| </AlertDialogFooter> | |
| </AlertDialogContent> | |
| </AlertDialog> | |
| </div> | |
| </CardHeader> | |
| <CardContent className="p-6 pt-0"> | |
| <div className="grid grid-cols-1 md:grid-cols-2 gap-4"> | |
| <MetricChart | |
| title="CPU Usage" | |
| metrics={metrics?.cpu} | |
| timestamps={metrics?.timestamps} | |
| unit="%" | |
| colorVar="hsl(var(--chart-1))" | |
| gradientId={`cpu-gradient-${repo.id}`} | |
| loading={loading} | |
| /> | |
| <MetricChart | |
| title="Memory Usage" | |
| metrics={metrics?.memory} | |
| timestamps={metrics?.timestamps} | |
| unit="%" | |
| colorVar="hsl(var(--chart-2))" | |
| gradientId={`mem-gradient-${repo.id}`} | |
| loading={loading} | |
| /> | |
| </div> | |
| </CardContent> | |
| </Card> | |
| ); | |
| } | |