mirror of
https://github.com/MacRimi/ProxMenux.git
synced 2025-10-11 04:16:17 +00:00
Update virtual-machines.tsx
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
"use client"
|
||||
|
||||
import { useState, useMemo } from "react"
|
||||
import { useState, useMemo, useEffect } from "react"
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "./ui/card"
|
||||
import { Badge } from "./ui/badge"
|
||||
import { Progress } from "./ui/progress"
|
||||
@@ -36,6 +36,7 @@ interface VMData {
|
||||
netout?: number
|
||||
diskread?: number
|
||||
diskwrite?: number
|
||||
ip?: string
|
||||
}
|
||||
|
||||
interface VMConfig {
|
||||
@@ -109,8 +110,8 @@ const extractIPFromConfig = (config?: VMConfig): string => {
|
||||
const netConfig = config[netKey]
|
||||
|
||||
if (netConfig && typeof netConfig === "string") {
|
||||
// Look for ip=x.x.x.x/xx pattern
|
||||
const ipMatch = netConfig.match(/ip=([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})(\/\d+)?/)
|
||||
// Look for ip=x.x.x.x/xx or ip=x.x.x.x pattern
|
||||
const ipMatch = netConfig.match(/ip=([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})/)
|
||||
if (ipMatch) {
|
||||
return ipMatch[1] // Return just the IP without CIDR
|
||||
}
|
||||
@@ -141,6 +142,36 @@ export function VirtualMachines() {
|
||||
const [vmDetails, setVMDetails] = useState<VMDetails | null>(null)
|
||||
const [controlLoading, setControlLoading] = useState(false)
|
||||
const [detailsLoading, setDetailsLoading] = useState(false)
|
||||
const [vmConfigs, setVmConfigs] = useState<Record<number, string>>({})
|
||||
|
||||
useEffect(() => {
|
||||
const fetchLXCIPs = async () => {
|
||||
if (!vmData) return
|
||||
|
||||
const lxcs = vmData.filter((vm) => vm.type === "lxc")
|
||||
const configs: Record<number, string> = {}
|
||||
|
||||
await Promise.all(
|
||||
lxcs.map(async (lxc) => {
|
||||
try {
|
||||
const response = await fetch(`/api/vms/${lxc.vmid}`)
|
||||
if (response.ok) {
|
||||
const details = await response.json()
|
||||
if (details.config) {
|
||||
configs[lxc.vmid] = extractIPFromConfig(details.config)
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`Error fetching config for LXC ${lxc.vmid}:`, error)
|
||||
}
|
||||
}),
|
||||
)
|
||||
|
||||
setVmConfigs(configs)
|
||||
}
|
||||
|
||||
fetchLXCIPs()
|
||||
}, [vmData])
|
||||
|
||||
const handleVMClick = async (vm: VMData) => {
|
||||
setSelectedVM(vm)
|
||||
@@ -249,7 +280,6 @@ export function VirtualMachines() {
|
||||
return { color: "bg-purple-500/10 text-purple-500 border-purple-500/20", label: "VM" }
|
||||
}
|
||||
|
||||
// Safe data handling with default empty array
|
||||
const safeVMData = vmData || []
|
||||
|
||||
const totalAllocatedMemoryGB = useMemo(() => {
|
||||
@@ -293,7 +323,6 @@ export function VirtualMachines() {
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
{/* VM Overview Cards */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
|
||||
<Card className="bg-card border-border">
|
||||
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
|
||||
@@ -350,6 +379,20 @@ export function VirtualMachines() {
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card className="bg-card border-border">
|
||||
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
|
||||
<CardTitle className="text-sm font-medium text-muted-foreground">Total Disk</CardTitle>
|
||||
<HardDrive className="h-4 w-4 text-muted-foreground" />
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="text-2xl font-bold text-foreground">
|
||||
{(safeVMData.reduce((sum, vm) => sum + (vm.maxdisk || 0), 0) / 1024 ** 3).toFixed(1)} GB
|
||||
</div>
|
||||
<p className="text-xs text-muted-foreground mt-2">Allocated disk space</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<Card className="bg-card border-border">
|
||||
<CardHeader>
|
||||
<CardTitle className="text-foreground flex items-center">
|
||||
@@ -368,7 +411,7 @@ export function VirtualMachines() {
|
||||
const memGB = (vm.mem / 1024 ** 3).toFixed(1)
|
||||
const maxMemGB = (vm.maxmem / 1024 ** 3).toFixed(1)
|
||||
const typeBadge = getTypeBadge(vm.type)
|
||||
const vmIP = extractIPFromConfig(vm.config)
|
||||
const lxcIP = vm.type === "lxc" ? vmConfigs[vm.vmid] : null
|
||||
|
||||
return (
|
||||
<div
|
||||
@@ -379,15 +422,29 @@ export function VirtualMachines() {
|
||||
<div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-3 mb-4">
|
||||
<div className="flex items-center space-x-4">
|
||||
<Server className="h-6 w-6 text-muted-foreground flex-shrink-0" />
|
||||
<div className="min-w-0">
|
||||
<div className="min-w-0 flex-1">
|
||||
<div className="font-semibold text-foreground text-lg flex items-center flex-wrap gap-2">
|
||||
<span className="truncate">{vm.name}</span>
|
||||
<Badge variant="outline" className={`text-xs flex-shrink-0 ${typeBadge.color}`}>
|
||||
{typeBadge.label}
|
||||
</Badge>
|
||||
</div>
|
||||
<div className="flex items-center gap-2 flex-wrap">
|
||||
<div className="text-sm text-muted-foreground">ID: {vm.vmid}</div>
|
||||
<div className="text-sm text-muted-foreground">IP: {vmIP}</div>
|
||||
{lxcIP && (
|
||||
<>
|
||||
<span className="text-muted-foreground">•</span>
|
||||
<div className="flex items-center gap-1 text-sm">
|
||||
<Network
|
||||
className={`h-3 w-3 ${lxcIP === "DHCP" ? "text-yellow-500" : "text-green-500"}`}
|
||||
/>
|
||||
<span className={lxcIP === "DHCP" ? "text-yellow-500" : "text-green-500"}>
|
||||
IP: {lxcIP}
|
||||
</span>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -456,7 +513,6 @@ export function VirtualMachines() {
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* VM Details Modal */}
|
||||
<Dialog
|
||||
open={!!selectedVM}
|
||||
onOpenChange={() => {
|
||||
@@ -563,7 +619,6 @@ export function VirtualMachines() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Resources Configuration */}
|
||||
{detailsLoading ? (
|
||||
<div className="text-center py-8 text-muted-foreground">Loading configuration...</div>
|
||||
) : vmDetails?.config ? (
|
||||
@@ -620,7 +675,6 @@ export function VirtualMachines() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Network Configuration */}
|
||||
<div>
|
||||
<h3 className="text-sm font-semibold text-muted-foreground mb-3 uppercase tracking-wide">
|
||||
Network
|
||||
@@ -659,7 +713,6 @@ export function VirtualMachines() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Options */}
|
||||
<div>
|
||||
<h3 className="text-sm font-semibold text-muted-foreground mb-3 uppercase tracking-wide">
|
||||
Options
|
||||
@@ -724,7 +777,6 @@ export function VirtualMachines() {
|
||||
</>
|
||||
) : null}
|
||||
|
||||
{/* Control Actions */}
|
||||
<div>
|
||||
<h3 className="text-sm font-semibold text-muted-foreground mb-3 uppercase tracking-wide">
|
||||
Control Actions
|
||||
@@ -774,6 +826,5 @@ export function VirtualMachines() {
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
Reference in New Issue
Block a user