60 lines
2.0 KiB
TypeScript
60 lines
2.0 KiB
TypeScript
import { useState } from 'react';
|
|
import { AnimatePresence } from 'framer-motion';
|
|
import { Settings2 } from 'lucide-react';
|
|
import { ControlFleetHost, ControlPerfSample, ControlConnection } from '@/hooks/useControlStream';
|
|
import { HostCard } from './HostCard';
|
|
import { HostConfigEditor } from './HostConfigEditor';
|
|
|
|
export interface GpuData {
|
|
vram_used: number;
|
|
vram_total: number;
|
|
temperature: number;
|
|
power: number;
|
|
}
|
|
|
|
interface FleetTabProps {
|
|
hosts: ControlFleetHost[];
|
|
gpuMap: Map<string, GpuData>;
|
|
perfSamples?: ControlPerfSample[];
|
|
connection?: ControlConnection;
|
|
}
|
|
|
|
export function FleetTab({ hosts, gpuMap, perfSamples = [], connection = 'connecting' }: FleetTabProps) {
|
|
const [editing, setEditing] = useState<string | null>(null);
|
|
|
|
if (hosts.length === 0) {
|
|
// B3: distinguish "not connected" from a genuinely empty fleet.
|
|
const msg = connection === 'live'
|
|
? 'No hosts connected'
|
|
: connection === 'down'
|
|
? 'Control service unreachable — retrying…'
|
|
: 'Connecting to control service…';
|
|
return (
|
|
<div className="flex items-center justify-center h-full">
|
|
<p className="text-sm text-muted-foreground">{msg}</p>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div className="flex-1 overflow-y-auto p-4 space-y-4">
|
|
<AnimatePresence mode="popLayout">
|
|
{hosts.map((host) => (
|
|
<div key={host.providerId} className="relative">
|
|
<button
|
|
type="button"
|
|
onClick={() => setEditing(host.providerId)}
|
|
title="SSH config editor"
|
|
className="absolute top-2 right-2 z-10 p-1.5 rounded-md text-muted-foreground hover:text-foreground hover:bg-muted/40"
|
|
>
|
|
<Settings2 className="size-4" />
|
|
</button>
|
|
<HostCard host={host} gpuData={gpuMap.get(host.providerId) ?? null} perfSamples={perfSamples} />
|
|
</div>
|
|
))}
|
|
</AnimatePresence>
|
|
{editing && <HostConfigEditor providerId={editing} onClose={() => setEditing(null)} />}
|
|
</div>
|
|
);
|
|
}
|