source-gamepanel/apps/web/src/pages/admin/nodes.tsx

71 lines
2.3 KiB
TypeScript

import { useQuery } from '@tanstack/react-query';
import { Network, Wifi, WifiOff } from 'lucide-react';
import { api } from '@/lib/api';
import { formatBytes } from '@/lib/utils';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { Badge } from '@/components/ui/badge';
interface NodeItem {
id: string;
name: string;
fqdn: string;
daemonPort: number;
grpcPort: number;
memoryTotal: number;
diskTotal: number;
isOnline: boolean;
organizationId: string;
}
export function AdminNodesPage() {
const { data } = useQuery({
queryKey: ['admin-nodes'],
queryFn: () => api.get<{ data: NodeItem[] }>('/admin/nodes'),
});
const nodes = data?.data ?? [];
return (
<div className="space-y-6">
<div>
<h1 className="text-2xl font-bold">All Nodes</h1>
<p className="text-muted-foreground">{nodes.length} nodes across all organizations</p>
</div>
<div className="grid gap-4 sm:grid-cols-2">
{nodes.map((node) => (
<Card key={node.id}>
<CardHeader className="flex flex-row items-center justify-between pb-2">
<div className="flex items-center gap-3">
<Network className="h-5 w-5 text-primary" />
<CardTitle className="text-base">{node.name}</CardTitle>
</div>
<Badge variant={node.isOnline ? 'default' : 'destructive'}>
{node.isOnline ? (
<><Wifi className="mr-1 h-3 w-3" /> Online</>
) : (
<><WifiOff className="mr-1 h-3 w-3" /> Offline</>
)}
</Badge>
</CardHeader>
<CardContent>
<p className="text-sm text-muted-foreground">{node.fqdn}:{node.daemonPort}</p>
<div className="mt-3 flex gap-4 text-sm">
<span>{formatBytes(node.memoryTotal)} RAM</span>
<span>{formatBytes(node.diskTotal)} Disk</span>
</div>
</CardContent>
</Card>
))}
{nodes.length === 0 && (
<Card>
<CardContent className="py-12 text-center text-muted-foreground">
No nodes registered
</CardContent>
</Card>
)}
</div>
</div>
);
}