import { useEffect, useMemo, useState } from 'react'
import { StatusBadge } from '@/components/ui/status-badge'
import { EmptyState } from '@/components/ui/empty-state'
import { Skeleton } from '@/components/ui/skeleton'
import { Server, Cpu, MemoryStick, HardDrive } from 'lucide-react'
import { useNodes } from '@/hooks/useProxmox'
import { NodeDetail } from '@/components/nodes/NodeDetail'
import { formatBytes, formatUptime } from '@/lib/format'
import { cn } from '@/lib/utils'
interface NodesPageProps {
connectionId: string
initialNodeName?: string
onNavigate?: (view: string) => void
}
function nodePercent(used: number, total: number): number {
return total > 0 ? Math.round((used / total) * 100) : 0
}
function MiniStat({
label,
icon: Icon,
value,
}: {
label: string
icon: React.ComponentType<{ className?: string }>
value: string
}) {
return (
{label}
{value}
)
}
export function NodesPage({ connectionId, initialNodeName }: NodesPageProps) {
const { data: nodes, isLoading, error } = useNodes(connectionId)
const [selectedNodeName, setSelectedNodeName] = useState(null)
// Auto-select the node requested via the sidebar (node-detail view) when it
// is present; otherwise default-select the first node. Keep the selection
// valid as the node list refreshes by falling back to the first node when
// the selection disappears.
useEffect(() => {
if (nodes && nodes.length > 0) {
setSelectedNodeName((current) => {
if (initialNodeName && nodes.some((n) => n.node === initialNodeName)) {
return initialNodeName
}
return nodes.some((n) => n.node === current) ? current : nodes[0].node
})
}
}, [nodes, initialNodeName])
const selectedNode = useMemo(() => {
if (!nodes || nodes.length === 0) return undefined
return nodes.find((n) => n.node === selectedNodeName) ?? nodes[0]
}, [nodes, selectedNodeName])
if (isLoading) {
return (
{Array.from({ length: 3 }, (_, i) => (
))}
)
}
if (error) {
return (
)
}
if (!nodes || nodes.length === 0) {
return (
)
}
return (
{/* Node list */}
Nodes
{nodes.length} node{nodes.length !== 1 ? 's' : ''} in the cluster
{nodes.map((node) => (
))}
{/* Selected node detail */}
{selectedNode && (
)}
)
}