2025-10-06 22:23:56 +02:00
"use client"
import { Card } from "@/components/ui/card"
import { Badge } from "@/components/ui/badge"
import { Progress } from "@/components/ui/progress"
import { Dialog , DialogContent , DialogDescription , DialogHeader , DialogTitle } from "@/components/ui/dialog"
import {
Thermometer ,
CpuIcon ,
Zap ,
HardDrive ,
Network ,
FanIcon ,
PowerIcon ,
Battery ,
Cpu ,
MemoryStick ,
Cpu as Gpu ,
2025-10-06 22:58:54 +02:00
Info ,
2025-10-06 22:23:56 +02:00
} from "lucide-react"
import useSWR from "swr"
import { useState } from "react"
import { type HardwareData , type GPU , type PCIDevice , type StorageDevice , fetcher } from "../types/hardware"
const getDeviceTypeColor = ( type : string ) : string = > {
const lowerType = type . toLowerCase ( )
if ( lowerType . includes ( "storage" ) || lowerType . includes ( "sata" ) || lowerType . includes ( "raid" ) ) {
return "bg-orange-500/10 text-orange-500 border-orange-500/20"
}
if ( lowerType . includes ( "usb" ) ) {
return "bg-purple-500/10 text-purple-500 border-purple-500/20"
}
if ( lowerType . includes ( "network" ) || lowerType . includes ( "ethernet" ) ) {
return "bg-blue-500/10 text-blue-500 border-blue-500/20"
}
if ( lowerType . includes ( "graphics" ) || lowerType . includes ( "vga" ) || lowerType . includes ( "display" ) ) {
return "bg-green-500/10 text-green-500 border-green-500/20"
}
return "bg-gray-500/10 text-gray-500 border-gray-500/20"
}
2025-10-06 22:58:54 +02:00
const getMonitoringToolRecommendation = ( vendor : string ) : string = > {
const lowerVendor = vendor . toLowerCase ( )
if ( lowerVendor . includes ( "intel" ) ) {
return "To get extended GPU monitoring information, please install intel-gpu-tools or igt-gpu-tools package."
}
if ( lowerVendor . includes ( "nvidia" ) ) {
2025-10-06 23:40:54 +02:00
return "For NVIDIA GPUs, real-time monitoring requires the proprietary drivers (nvidia-driver package). Install them only if your GPU is used directly by the host."
2025-10-06 22:58:54 +02:00
}
2025-10-06 23:40:54 +02:00
2025-10-06 22:58:54 +02:00
if ( lowerVendor . includes ( "amd" ) || lowerVendor . includes ( "ati" ) ) {
return "To get extended GPU monitoring information, please install radeontop package."
}
return "To get extended GPU monitoring information, please install the appropriate GPU monitoring tools for your hardware."
}
2025-10-06 22:23:56 +02:00
export default function Hardware() {
const { data : hardwareData , error } = useSWR < HardwareData > ( "/api/hardware" , fetcher , {
refreshInterval : 5000 ,
} )
const [ selectedGPU , setSelectedGPU ] = useState < GPU | null > ( null )
const [ selectedPCIDevice , setSelectedPCIDevice ] = useState < PCIDevice | null > ( null )
const [ selectedDisk , setSelectedDisk ] = useState < StorageDevice | null > ( null )
const [ selectedNetwork , setSelectedNetwork ] = useState < PCIDevice | null > ( null )
2025-10-05 22:46:14 +02:00
2025-10-06 23:51:35 +02:00
const realtimeURL = selectedGPU ? ` /api/gpu/ ${ selectedGPU . slot } /realtime ` : null
console . log ( "[v0] GPU modal opened, selectedGPU:" , selectedGPU )
console . log ( "[v0] Realtime URL:" , realtimeURL )
const { data : realtimeGPUData , error : realtimeError } = useSWR < any > ( realtimeURL , fetcher , {
refreshInterval : 2000 ,
2025-10-06 23:40:54 +02:00
revalidateOnFocus : false ,
} )
2025-10-06 23:51:35 +02:00
console . log ( "[v0] Realtime GPU data:" , realtimeGPUData )
console . log ( "[v0] Realtime GPU error:" , realtimeError )
// </CHANGE>
2025-10-06 23:26:26 +02:00
const findPCIDeviceForGPU = ( gpu : GPU ) : PCIDevice | null = > {
if ( ! hardwareData ? . pci_devices || ! gpu . slot ) return null
// Try to find exact match first (e.g., "00:02.0")
let pciDevice = hardwareData . pci_devices . find ( ( d ) = > d . slot === gpu . slot )
// If not found, try to match by partial slot (e.g., "00" matches "00:02.0")
if ( ! pciDevice && gpu . slot . length <= 2 ) {
pciDevice = hardwareData . pci_devices . find (
( d ) = >
d . slot . startsWith ( gpu . slot + ":" ) &&
( d . type . toLowerCase ( ) . includes ( "vga" ) ||
d . type . toLowerCase ( ) . includes ( "graphics" ) ||
d . type . toLowerCase ( ) . includes ( "display" ) ) ,
)
}
return pciDevice || null
}
2025-10-06 23:40:54 +02:00
const hasRealtimeData = ( gpu : GPU , realtimeData? : any ) : boolean = > {
const combinedData = { . . . gpu , . . . realtimeData }
return ! ! (
( combinedData . temperature !== undefined && combinedData . temperature > 0 ) ||
combinedData . utilization_gpu !== undefined ||
combinedData . memory_total ||
combinedData . power_draw
)
}
2025-10-05 20:45:54 +02:00
return (
2025-10-06 22:23:56 +02:00
< div className = "space-y-6 p-6" >
{ /* System Information - CPU & Motherboard */ }
{ ( hardwareData ? . cpu || hardwareData ? . motherboard ) && (
< Card className = "border-border/50 bg-card/50 p-6" >
< div className = "mb-4 flex items-center gap-2" >
< Cpu className = "h-5 w-5 text-primary" / >
< h2 className = "text-lg font-semibold" > System Information < / h2 >
< / div >
< div className = "grid gap-6 md:grid-cols-2" >
{ /* CPU Info */ }
{ hardwareData ? . cpu && Object . keys ( hardwareData . cpu ) . length > 0 && (
< div >
< div className = "mb-2 flex items-center gap-2" >
< CpuIcon className = "h-4 w-4 text-muted-foreground" / >
< h3 className = "text-sm font-semibold" > CPU < / h3 >
< / div >
< div className = "space-y-2" >
{ hardwareData . cpu . model && (
< div className = "flex justify-between text-sm" >
< span className = "text-muted-foreground" > Model < / span >
< span className = "font-medium text-right" > { hardwareData . cpu . model } < / span >
< / div >
) }
{ hardwareData . cpu . cores_per_socket && hardwareData . cpu . sockets && (
< div className = "flex justify-between text-sm" >
< span className = "text-muted-foreground" > Cores < / span >
< span className = "font-medium" >
{ hardwareData . cpu . sockets } × { hardwareData . cpu . cores_per_socket } = { " " }
{ hardwareData . cpu . sockets * hardwareData . cpu . cores_per_socket } cores
< / span >
< / div >
) }
{ hardwareData . cpu . total_threads && (
< div className = "flex justify-between text-sm" >
< span className = "text-muted-foreground" > Threads < / span >
< span className = "font-medium" > { hardwareData . cpu . total_threads } < / span >
< / div >
) }
{ hardwareData . cpu . l3_cache && (
< div className = "flex justify-between text-sm" >
< span className = "text-muted-foreground" > L3 Cache < / span >
< span className = "font-medium" > { hardwareData . cpu . l3_cache } < / span >
< / div >
) }
{ hardwareData . cpu . virtualization && (
< div className = "flex justify-between text-sm" >
< span className = "text-muted-foreground" > Virtualization < / span >
< span className = "font-medium" > { hardwareData . cpu . virtualization } < / span >
< / div >
) }
< / div >
< / div >
) }
{ /* Motherboard Info */ }
{ hardwareData ? . motherboard && Object . keys ( hardwareData . motherboard ) . length > 0 && (
< div >
< div className = "mb-2 flex items-center gap-2" >
< Cpu className = "h-4 w-4 text-muted-foreground" / >
< h3 className = "text-sm font-semibold" > Motherboard < / h3 >
< / div >
< div className = "space-y-2" >
{ hardwareData . motherboard . manufacturer && (
< div className = "flex justify-between text-sm" >
< span className = "text-muted-foreground" > Manufacturer < / span >
< span className = "font-medium text-right" > { hardwareData . motherboard . manufacturer } < / span >
< / div >
) }
{ hardwareData . motherboard . model && (
< div className = "flex justify-between text-sm" >
< span className = "text-muted-foreground" > Model < / span >
< span className = "font-medium text-right" > { hardwareData . motherboard . model } < / span >
< / div >
) }
{ hardwareData . motherboard . bios ? . vendor && (
< div className = "flex justify-between text-sm" >
< span className = "text-muted-foreground" > BIOS < / span >
< span className = "font-medium text-right" > { hardwareData . motherboard . bios . vendor } < / span >
< / div >
) }
{ hardwareData . motherboard . bios ? . version && (
< div className = "flex justify-between text-sm" >
< span className = "text-muted-foreground" > Version < / span >
< span className = "font-medium" > { hardwareData . motherboard . bios . version } < / span >
< / div >
) }
{ hardwareData . motherboard . bios ? . date && (
< div className = "flex justify-between text-sm" >
< span className = "text-muted-foreground" > Date < / span >
< span className = "font-medium" > { hardwareData . motherboard . bios . date } < / span >
< / div >
) }
< / div >
< / div >
) }
< / div >
< / Card >
) }
{ /* Memory Modules */ }
{ hardwareData ? . memory_modules && hardwareData . memory_modules . length > 0 && (
< Card className = "border-border/50 bg-card/50 p-6" >
< div className = "mb-4 flex items-center gap-2" >
< MemoryStick className = "h-5 w-5 text-primary" / >
< h2 className = "text-lg font-semibold" > Memory Modules < / h2 >
< Badge variant = "outline" className = "ml-auto" >
{ hardwareData . memory_modules . length } installed
< / Badge >
< / div >
< div className = "grid gap-3 md:grid-cols-2 lg:grid-cols-3" >
{ hardwareData . memory_modules . map ( ( module , index ) = > (
< div key = { index } className = "rounded-lg border border-border/30 bg-background/50 p-4" >
< div className = "mb-2 font-medium text-sm" > { module . slot } < / div >
< div className = "space-y-1" >
{ module . size && (
< div className = "flex justify-between text-sm" >
< span className = "text-muted-foreground" > Size < / span >
< span className = "font-medium" > { module . size } < / span >
< / div >
) }
{ module . type && (
< div className = "flex justify-between text-sm" >
< span className = "text-muted-foreground" > Type < / span >
< span className = "font-medium" > { module . type } < / span >
< / div >
) }
{ module . speed && (
< div className = "flex justify-between text-sm" >
< span className = "text-muted-foreground" > Speed < / span >
< span className = "font-medium" > { module . speed } < / span >
< / div >
) }
{ module . manufacturer && (
< div className = "flex justify-between text-sm" >
< span className = "text-muted-foreground" > Manufacturer < / span >
< span className = "font-medium text-right" > { module . manufacturer } < / span >
< / div >
) }
< / div >
< / div >
) ) }
< / div >
< / Card >
) }
{ /* Thermal Monitoring */ }
{ hardwareData ? . temperatures && hardwareData . temperatures . length > 0 && (
< Card className = "border-border/50 bg-card/50 p-6" >
< div className = "mb-4 flex items-center gap-2" >
< Thermometer className = "h-5 w-5 text-primary" / >
< h2 className = "text-lg font-semibold" > Thermal Monitoring < / h2 >
< Badge variant = "outline" className = "ml-auto" >
{ hardwareData . temperatures . length } sensors
< / Badge >
< / div >
< div className = "grid gap-4 md:grid-cols-2" >
{ hardwareData . temperatures . map ( ( temp , index ) = > {
const percentage = temp . critical > 0 ? ( temp . current / temp . critical ) * 100 : ( temp . current / 100 ) * 100
const isHot = temp . current > ( temp . high || 80 )
const isCritical = temp . current > ( temp . critical || 90 )
return (
< div key = { index } className = "space-y-2" >
< div className = "flex items-center justify-between" >
< span className = "text-sm font-medium" > { temp . name } < / span >
< span
className = { ` text-sm font-semibold ${ isCritical ? "text-red-500" : isHot ? "text-orange-500" : "text-green-500" } ` }
>
{ temp . current . toFixed ( 1 ) } ° C
< / span >
< / div >
< div className = "h-2 w-full overflow-hidden rounded-full bg-secondary" >
< div
className = "h-full bg-blue-500 transition-all"
style = { { width : ` ${ Math . min ( percentage , 100 ) } % ` } }
/ >
< / div >
{ temp . adapter && < span className = "text-xs text-muted-foreground" > { temp . adapter } < / span > }
< / div >
)
} ) }
< / div >
< / Card >
) }
{ /* GPU Information - Enhanced with modal */ }
{ hardwareData ? . gpus && hardwareData . gpus . length > 0 && (
< Card className = "border-border/50 bg-card/50 p-6" >
< div className = "mb-4 flex items-center gap-2" >
< Gpu className = "h-5 w-5 text-primary" / >
< h2 className = "text-lg font-semibold" > Graphics Cards < / h2 >
< Badge variant = "outline" className = "ml-auto" >
{ hardwareData . gpus . length } GPU { hardwareData . gpus . length > 1 ? "s" : "" }
< / Badge >
< / div >
2025-10-06 22:39:37 +02:00
< div className = "grid gap-4 sm:grid-cols-2" >
2025-10-06 23:40:54 +02:00
{ hardwareData . gpus . map ( ( gpu , index ) = > {
const pciDevice = findPCIDeviceForGPU ( gpu )
const fullSlot = pciDevice ? . slot || gpu . slot
2025-10-06 22:23:56 +02:00
2025-10-06 23:40:54 +02:00
return (
< div
key = { index }
onClick = { ( ) = > setSelectedGPU ( gpu ) }
className = "cursor-pointer rounded-lg border border-border/30 bg-background/50 p-4 transition-colors hover:bg-background/80"
>
< div className = "mb-3 flex items-center justify-between" >
< span className = "font-medium text-sm" > { gpu . name } < / span >
< Badge className = { getDeviceTypeColor ( "graphics" ) } > { gpu . vendor } < / Badge >
2025-10-06 22:23:56 +02:00
< / div >
2025-10-06 23:40:54 +02:00
< div className = "space-y-2" >
2025-10-06 22:23:56 +02:00
< div className = "flex justify-between text-sm" >
2025-10-06 23:40:54 +02:00
< span className = "text-muted-foreground" > Type < / span >
< span className = "font-medium" > { gpu . type } < / span >
2025-10-06 22:39:37 +02:00
< / div >
2025-10-06 23:40:54 +02:00
{ fullSlot && (
< div className = "flex justify-between text-sm" >
< span className = "text-muted-foreground" > PCI Slot < / span >
< span className = "font-mono text-xs" > { fullSlot } < / span >
< / div >
) }
2025-10-06 22:39:37 +02:00
2025-10-06 23:40:54 +02:00
{ gpu . pci_driver && (
< div className = "flex justify-between text-sm" >
< span className = "text-muted-foreground" > Driver < / span >
< span className = "font-mono text-xs text-green-500" > { gpu . pci_driver } < / span >
< / div >
) }
2025-10-06 22:23:56 +02:00
2025-10-06 23:40:54 +02:00
{ gpu . pci_kernel_module && (
2025-10-06 22:23:56 +02:00
< div className = "flex justify-between text-sm" >
2025-10-06 23:40:54 +02:00
< span className = "text-muted-foreground" > Kernel Module < / span >
< span className = "font-mono text-xs" > { gpu . pci_kernel_module } < / span >
2025-10-06 22:23:56 +02:00
< / div >
2025-10-06 23:40:54 +02:00
) }
{ gpu . driver_version && (
< div className = "flex justify-between text-sm" >
< span className = "text-muted-foreground" > Driver Version < / span >
< span className = "font-mono text-xs" > { gpu . driver_version } < / span >
2025-10-06 22:23:56 +02:00
< / div >
2025-10-06 23:40:54 +02:00
) }
2025-10-06 22:23:56 +02:00
2025-10-06 23:40:54 +02:00
{ gpu . temperature !== undefined && gpu . temperature > 0 && (
< div className = "space-y-1" >
< div className = "flex justify-between text-sm" >
< span className = "text-muted-foreground" > Temperature < / span >
< span className = "font-semibold text-green-500" > { gpu . temperature } ° C < / span >
< / div >
< div className = "h-2 w-full overflow-hidden rounded-full bg-secondary" >
< div
className = "h-full bg-blue-500 transition-all"
style = { { width : ` ${ Math . min ( ( gpu . temperature / 100 ) * 100 , 100 ) } % ` } }
/ >
< / div >
< / div >
) }
2025-10-06 22:23:56 +02:00
2025-10-06 23:40:54 +02:00
{ gpu . memory_total && (
2025-10-06 22:23:56 +02:00
< div className = "flex justify-between text-sm" >
2025-10-06 23:40:54 +02:00
< span className = "text-muted-foreground" > Memory < / span >
< span className = "font-medium" >
{ gpu . memory_used } / { gpu . memory_total }
< / span >
2025-10-06 22:23:56 +02:00
< / div >
2025-10-06 23:40:54 +02:00
) }
{ gpu . utilization_gpu !== undefined && (
< div className = "space-y-1" >
< div className = "flex justify-between text-sm" >
< span className = "text-muted-foreground" > GPU Usage < / span >
< span className = "font-medium" > { gpu . utilization_gpu } % < / span >
< / div >
< div className = "h-2 w-full overflow-hidden rounded-full bg-secondary" >
< div
className = "h-full bg-green-500 transition-all"
style = { { width : ` ${ gpu . utilization_gpu } % ` } }
/ >
< / div >
2025-10-06 22:23:56 +02:00
< / div >
2025-10-06 23:40:54 +02:00
) }
2025-10-06 22:23:56 +02:00
2025-10-06 23:40:54 +02:00
{ gpu . power_draw && gpu . power_draw !== "N/A" && (
< div className = "flex justify-between text-sm" >
< span className = "text-muted-foreground" > Power < / span >
< span className = "font-medium" > { gpu . power_draw } < / span >
< / div >
) }
< / div >
2025-10-06 22:23:56 +02:00
< / div >
2025-10-06 23:40:54 +02:00
)
} ) }
2025-10-06 22:23:56 +02:00
< / div >
< / Card >
) }
{ /* GPU Detail Modal */ }
< Dialog open = { selectedGPU !== null } onOpenChange = { ( ) = > setSelectedGPU ( null ) } >
< DialogContent className = "max-w-2xl max-h-[80vh] overflow-y-auto" >
< DialogHeader >
< DialogTitle > { selectedGPU ? . name } < / DialogTitle >
2025-10-06 23:16:31 +02:00
< DialogDescription > PCI Device Information < / DialogDescription >
2025-10-06 22:23:56 +02:00
< / DialogHeader >
2025-10-06 23:26:26 +02:00
{ selectedGPU &&
( ( ) = > {
const pciDevice = findPCIDeviceForGPU ( selectedGPU )
2025-10-06 23:40:54 +02:00
const combinedGPU = { . . . selectedGPU , . . . realtimeGPUData }
2025-10-06 23:16:31 +02:00
2025-10-06 23:26:26 +02:00
return (
< div className = "space-y-4" >
{ /* Basic PCI Device Information - Same format as PCI Device modal */ }
< div className = "space-y-3" >
< div className = "flex justify-between border-b border-border/50 pb-2" >
< span className = "text-sm font-medium text-muted-foreground" > Device Type < / span >
< Badge className = { getDeviceTypeColor ( "graphics" ) } > Graphics Card < / Badge >
< / div >
2025-10-06 23:16:31 +02:00
2025-10-06 23:26:26 +02:00
< div className = "flex justify-between border-b border-border/50 pb-2" >
< span className = "text-sm font-medium text-muted-foreground" > PCI Slot < / span >
< span className = "font-mono text-sm" > { pciDevice ? . slot || selectedGPU . slot } < / span >
< / div >
2025-10-06 22:23:56 +02:00
< div className = "flex justify-between border-b border-border/50 pb-2" >
2025-10-06 23:26:26 +02:00
< span className = "text-sm font-medium text-muted-foreground" > Device Name < / span >
< span className = "text-sm text-right" > { pciDevice ? . device || selectedGPU . name } < / span >
2025-10-06 22:23:56 +02:00
< / div >
2025-10-06 23:26:26 +02:00
2025-10-06 22:23:56 +02:00
< div className = "flex justify-between border-b border-border/50 pb-2" >
2025-10-06 23:26:26 +02:00
< span className = "text-sm font-medium text-muted-foreground" > Vendor < / span >
< span className = "text-sm" > { pciDevice ? . vendor || selectedGPU . vendor } < / span >
2025-10-06 22:23:56 +02:00
< / div >
2025-10-06 23:26:26 +02:00
2025-10-06 22:23:56 +02:00
< div className = "flex justify-between border-b border-border/50 pb-2" >
2025-10-06 23:26:26 +02:00
< span className = "text-sm font-medium text-muted-foreground" > Class < / span >
< span className = "font-mono text-sm" > { pciDevice ? . class || selectedGPU . pci_class || "N/A" } < / span >
< / div >
2025-10-06 22:23:56 +02:00
2025-10-06 23:26:26 +02:00
{ ( pciDevice ? . driver || selectedGPU . pci_driver ) && (
2025-10-06 22:58:54 +02:00
< div className = "flex justify-between border-b border-border/50 pb-2" >
2025-10-06 23:26:26 +02:00
< span className = "text-sm font-medium text-muted-foreground" > Driver < / span >
< span className = "font-mono text-sm text-green-500" >
{ pciDevice ? . driver || selectedGPU . pci_driver }
< / span >
2025-10-06 22:58:54 +02:00
< / div >
) }
2025-10-06 23:26:26 +02:00
{ ( pciDevice ? . kernel_module || selectedGPU . pci_kernel_module ) && (
2025-10-06 22:58:54 +02:00
< div className = "flex justify-between border-b border-border/50 pb-2" >
2025-10-06 23:26:26 +02:00
< span className = "text-sm font-medium text-muted-foreground" > Kernel Module < / span >
< span className = "font-mono text-sm" >
{ pciDevice ? . kernel_module || selectedGPU . pci_kernel_module }
< / span >
2025-10-06 22:58:54 +02:00
< / div >
) }
2025-10-06 23:26:26 +02:00
< div className = "flex justify-between border-b border-border/50 pb-2" >
< span className = "text-sm font-medium text-muted-foreground" > Type < / span >
< span className = "text-sm font-medium" > { selectedGPU . type } < / span >
< / div >
2025-10-06 23:40:54 +02:00
{ combinedGPU . driver_version && (
2025-10-06 22:58:54 +02:00
< div className = "flex justify-between border-b border-border/50 pb-2" >
2025-10-06 23:26:26 +02:00
< span className = "text-sm font-medium text-muted-foreground" > Driver Version < / span >
2025-10-06 23:40:54 +02:00
< span className = "font-mono text-sm" > { combinedGPU . driver_version } < / span >
2025-10-06 22:58:54 +02:00
< / div >
) }
2025-10-06 22:23:56 +02:00
2025-10-06 23:40:54 +02:00
{ combinedGPU . pcie_gen && (
2025-10-06 22:23:56 +02:00
< div className = "flex justify-between border-b border-border/50 pb-2" >
2025-10-06 23:26:26 +02:00
< span className = "text-sm font-medium text-muted-foreground" > PCIe Generation < / span >
2025-10-06 23:40:54 +02:00
< span className = "text-sm font-medium" > Gen { combinedGPU . pcie_gen } < / span >
2025-10-06 22:23:56 +02:00
< / div >
) }
2025-10-06 23:26:26 +02:00
2025-10-06 23:40:54 +02:00
{ combinedGPU . pcie_width && (
2025-10-06 22:23:56 +02:00
< div className = "flex justify-between border-b border-border/50 pb-2" >
2025-10-06 23:26:26 +02:00
< span className = "text-sm font-medium text-muted-foreground" > PCIe Width < / span >
2025-10-06 23:40:54 +02:00
< span className = "text-sm font-medium" > { combinedGPU . pcie_width } < / span >
2025-10-06 22:23:56 +02:00
< / div >
) }
< / div >
2025-10-06 23:26:26 +02:00
{ /* Memory Info - Only show if available */ }
2025-10-06 23:40:54 +02:00
{ combinedGPU . memory_total && (
2025-10-06 23:26:26 +02:00
< div className = "space-y-2" >
< h3 className = "font-semibold text-sm" > Memory < / h3 >
< div className = "grid gap-2" >
< div className = "flex justify-between border-b border-border/50 pb-2" >
< span className = "text-sm text-muted-foreground" > Total < / span >
2025-10-06 23:40:54 +02:00
< span className = "text-sm font-medium" > { combinedGPU . memory_total } < / span >
2025-10-06 23:26:26 +02:00
< / div >
< div className = "flex justify-between border-b border-border/50 pb-2" >
< span className = "text-sm text-muted-foreground" > Used < / span >
2025-10-06 23:40:54 +02:00
< span className = "text-sm font-medium" > { combinedGPU . memory_used } < / span >
2025-10-06 22:23:56 +02:00
< / div >
2025-10-06 23:26:26 +02:00
< div className = "flex justify-between border-b border-border/50 pb-2" >
< span className = "text-sm text-muted-foreground" > Free < / span >
2025-10-06 23:40:54 +02:00
< span className = "text-sm font-medium" > { combinedGPU . memory_free } < / span >
2025-10-06 23:26:26 +02:00
< / div >
2025-10-06 23:40:54 +02:00
{ combinedGPU . utilization_memory !== undefined && (
2025-10-06 23:26:26 +02:00
< div className = "space-y-1" >
< div className = "flex justify-between" >
< span className = "text-sm text-muted-foreground" > Memory Utilization < / span >
2025-10-06 23:40:54 +02:00
< span className = "text-sm font-medium" > { combinedGPU . utilization_memory } % < / span >
2025-10-06 23:26:26 +02:00
< / div >
2025-10-06 23:40:54 +02:00
< Progress value = { combinedGPU . utilization_memory } className = "h-2" / >
2025-10-06 23:26:26 +02:00
< / div >
) }
2025-10-06 22:23:56 +02:00
< / div >
2025-10-06 23:26:26 +02:00
< / div >
) }
2025-10-06 22:23:56 +02:00
2025-10-06 23:26:26 +02:00
{ /* Performance - Only show if realtime data available */ }
2025-10-06 23:40:54 +02:00
{ hasRealtimeData ( selectedGPU , realtimeGPUData ) && (
2025-10-06 23:26:26 +02:00
< div className = "space-y-2" >
< h3 className = "font-semibold text-sm" > Performance < / h3 >
< div className = "grid gap-2" >
2025-10-06 23:40:54 +02:00
{ combinedGPU . temperature !== undefined && combinedGPU . temperature > 0 && (
2025-10-06 23:26:26 +02:00
< div className = "space-y-1" >
< div className = "flex justify-between" >
< span className = "text-sm text-muted-foreground" > Temperature < / span >
2025-10-06 23:40:54 +02:00
< span className = "text-sm font-semibold text-green-500" > { combinedGPU . temperature } ° C < / span >
2025-10-06 23:26:26 +02:00
< / div >
2025-10-06 23:40:54 +02:00
< Progress value = { ( combinedGPU . temperature / 100 ) * 100 } className = "h-2" / >
2025-10-06 23:26:26 +02:00
< / div >
) }
2025-10-06 23:40:54 +02:00
{ combinedGPU . utilization_gpu !== undefined && (
2025-10-06 23:26:26 +02:00
< div className = "space-y-1" >
< div className = "flex justify-between" >
< span className = "text-sm text-muted-foreground" > GPU Utilization < / span >
2025-10-06 23:40:54 +02:00
< span className = "text-sm font-medium" > { combinedGPU . utilization_gpu } % < / span >
2025-10-06 23:26:26 +02:00
< / div >
2025-10-06 23:40:54 +02:00
< Progress value = { combinedGPU . utilization_gpu } className = "h-2" / >
2025-10-06 23:26:26 +02:00
< / div >
) }
2025-10-06 23:40:54 +02:00
{ combinedGPU . power_draw && combinedGPU . power_draw !== "N/A" && (
2025-10-06 23:26:26 +02:00
< div className = "flex justify-between border-b border-border/50 pb-2" >
< span className = "text-sm text-muted-foreground" > Power Draw < / span >
2025-10-06 23:40:54 +02:00
< span className = "text-sm font-medium" > { combinedGPU . power_draw } < / span >
2025-10-06 23:26:26 +02:00
< / div >
) }
2025-10-06 23:40:54 +02:00
{ combinedGPU . power_limit && (
2025-10-06 23:26:26 +02:00
< div className = "flex justify-between border-b border-border/50 pb-2" >
< span className = "text-sm text-muted-foreground" > Power Limit < / span >
2025-10-06 23:40:54 +02:00
< span className = "text-sm font-medium" > { combinedGPU . power_limit } < / span >
2025-10-06 23:26:26 +02:00
< / div >
) }
2025-10-06 23:40:54 +02:00
{ combinedGPU . fan_speed && (
2025-10-06 23:26:26 +02:00
< div className = "flex justify-between border-b border-border/50 pb-2" >
< span className = "text-sm text-muted-foreground" > Fan Speed < / span >
< span className = "text-sm font-medium" >
2025-10-06 23:40:54 +02:00
{ combinedGPU . fan_speed } { combinedGPU . fan_unit }
2025-10-06 23:26:26 +02:00
< / span >
< / div >
) }
< / div >
2025-10-06 22:58:54 +02:00
< / div >
2025-10-06 23:26:26 +02:00
) }
2025-10-06 22:58:54 +02:00
2025-10-06 23:26:26 +02:00
{ /* Clock Speeds - Only show if available */ }
2025-10-06 23:40:54 +02:00
{ ( combinedGPU . clock_graphics || combinedGPU . clock_memory ) && (
2025-10-06 23:26:26 +02:00
< div className = "space-y-2" >
< h3 className = "font-semibold text-sm" > Clock Speeds < / h3 >
< div className = "grid gap-2" >
2025-10-06 23:40:54 +02:00
{ combinedGPU . clock_graphics && (
2025-10-06 23:26:26 +02:00
< div className = "flex justify-between border-b border-border/50 pb-2" >
< span className = "text-sm text-muted-foreground" > Graphics Clock < / span >
2025-10-06 23:40:54 +02:00
< span className = "text-sm font-medium" > { combinedGPU . clock_graphics } < / span >
2025-10-06 23:26:26 +02:00
< / div >
) }
2025-10-06 23:40:54 +02:00
{ combinedGPU . clock_memory && (
2025-10-06 23:26:26 +02:00
< div className = "flex justify-between border-b border-border/50 pb-2" >
< span className = "text-sm text-muted-foreground" > Memory Clock < / span >
2025-10-06 23:40:54 +02:00
< span className = "text-sm font-medium" > { combinedGPU . clock_memory } < / span >
2025-10-06 23:26:26 +02:00
< / div >
) }
< / div >
< / div >
) }
{ /* Running Processes - Only show if available */ }
2025-10-06 23:40:54 +02:00
{ combinedGPU . processes && combinedGPU . processes . length > 0 && (
2025-10-06 23:26:26 +02:00
< div className = "space-y-2" >
< h3 className = "font-semibold text-sm" > Running Processes < / h3 >
< div className = "space-y-2" >
2025-10-06 23:40:54 +02:00
{ combinedGPU . processes . map ( ( proc : any , idx : number ) = > (
2025-10-06 23:26:26 +02:00
< div key = { idx } className = "rounded-lg border border-border/30 bg-background/50 p-3" >
< div className = "flex justify-between" >
< span className = "font-mono text-xs" > PID : { proc . pid } < / span >
< span className = "text-xs font-medium" > { proc . memory } < / span >
< / div >
< p className = "mt-1 text-sm" > { proc . name } < / p >
< / div >
) ) }
< / div >
< / div >
) }
2025-10-06 23:40:54 +02:00
{ ! hasRealtimeData ( selectedGPU , realtimeGPUData ) && (
2025-10-06 23:26:26 +02:00
< div className = "rounded-lg border border-blue-500/20 bg-blue-500/10 p-4" >
< div className = "flex gap-3" >
< Info className = "h-5 w-5 text-blue-500 flex-shrink-0 mt-0.5" / >
< div className = "space-y-1" >
< p className = "text-sm font-medium text-blue-500" > Extended Monitoring Not Available < / p >
< p className = "text-xs text-muted-foreground" >
{ getMonitoringToolRecommendation ( pciDevice ? . vendor || selectedGPU . vendor ) }
< / p >
< / div >
< / div >
< / div >
) }
2025-10-06 23:40:54 +02:00
{ combinedGPU . note && (
2025-10-06 23:26:26 +02:00
< div className = "rounded-lg bg-muted p-3" >
2025-10-06 23:40:54 +02:00
< p className = "text-xs text-muted-foreground" > { combinedGPU . note } < / p >
2025-10-06 23:26:26 +02:00
< / div >
) }
2025-10-06 22:23:56 +02:00
< / div >
2025-10-06 23:26:26 +02:00
)
} ) ( ) }
2025-10-06 22:23:56 +02:00
< / DialogContent >
< / Dialog >
{ /* PCI Devices - Changed to modal */ }
{ hardwareData ? . pci_devices && hardwareData . pci_devices . length > 0 && (
< Card className = "border-border/50 bg-card/50 p-6" >
< div className = "mb-4 flex items-center gap-2" >
< CpuIcon className = "h-5 w-5 text-primary" / >
< h2 className = "text-lg font-semibold" > PCI Devices < / h2 >
< Badge variant = "outline" className = "ml-auto" >
{ hardwareData . pci_devices . length } devices
< / Badge >
< / div >
< div className = "grid gap-3 md:grid-cols-2 lg:grid-cols-3" >
{ hardwareData . pci_devices . map ( ( device , index ) = > (
< div
key = { index }
onClick = { ( ) = > setSelectedPCIDevice ( device ) }
className = "cursor-pointer rounded-lg border border-border/30 bg-background/50 p-3 transition-colors hover:bg-background/80"
>
< div className = "flex items-center justify-between mb-2" >
< Badge className = { getDeviceTypeColor ( device . type ) } > { device . type } < / Badge >
< span className = "font-mono text-xs text-muted-foreground" > { device . slot } < / span >
< / div >
< p className = "font-medium text-sm truncate" > { device . device } < / p >
< p className = "text-xs text-muted-foreground truncate" > { device . vendor } < / p >
{ device . driver && < p className = "mt-1 font-mono text-xs text-green-500" > Driver : { device . driver } < / p > }
< / div >
) ) }
< / div >
< / Card >
) }
{ /* PCI Device Detail Modal */ }
< Dialog open = { selectedPCIDevice !== null } onOpenChange = { ( ) = > setSelectedPCIDevice ( null ) } >
< DialogContent className = "max-w-2xl" >
< DialogHeader >
< DialogTitle > { selectedPCIDevice ? . device } < / DialogTitle >
< DialogDescription > PCI Device Information < / DialogDescription >
< / DialogHeader >
{ selectedPCIDevice && (
< div className = "space-y-3" >
< div className = "flex justify-between border-b border-border/50 pb-2" >
< span className = "text-sm font-medium text-muted-foreground" > Device Type < / span >
< Badge className = { getDeviceTypeColor ( selectedPCIDevice . type ) } > { selectedPCIDevice . type } < / Badge >
< / div >
< div className = "flex justify-between border-b border-border/50 pb-2" >
< span className = "text-sm font-medium text-muted-foreground" > PCI Slot < / span >
< span className = "font-mono text-sm" > { selectedPCIDevice . slot } < / span >
< / div >
< div className = "flex justify-between border-b border-border/50 pb-2" >
< span className = "text-sm font-medium text-muted-foreground" > Device Name < / span >
< span className = "text-sm text-right" > { selectedPCIDevice . device } < / span >
< / div >
< div className = "flex justify-between border-b border-border/50 pb-2" >
< span className = "text-sm font-medium text-muted-foreground" > Vendor < / span >
< span className = "text-sm" > { selectedPCIDevice . vendor } < / span >
< / div >
< div className = "flex justify-between border-b border-border/50 pb-2" >
< span className = "text-sm font-medium text-muted-foreground" > Class < / span >
< span className = "font-mono text-sm" > { selectedPCIDevice . class } < / span >
< / div >
{ selectedPCIDevice . driver && (
< div className = "flex justify-between border-b border-border/50 pb-2" >
< span className = "text-sm font-medium text-muted-foreground" > Driver < / span >
< span className = "font-mono text-sm text-green-500" > { selectedPCIDevice . driver } < / span >
< / div >
) }
{ selectedPCIDevice . kernel_module && (
< div className = "flex justify-between border-b border-border/50 pb-2" >
< span className = "text-sm font-medium text-muted-foreground" > Kernel Module < / span >
< span className = "font-mono text-sm" > { selectedPCIDevice . kernel_module } < / span >
< / div >
) }
< / div >
) }
< / DialogContent >
< / Dialog >
{ /* Power Consumption */ }
{ hardwareData ? . power_meter && (
< Card className = "border-border/50 bg-card/50 p-6" >
< div className = "mb-4 flex items-center gap-2" >
< Zap className = "h-5 w-5 text-yellow-500" / >
< h2 className = "text-lg font-semibold" > Power Consumption < / h2 >
< / div >
< div className = "space-y-4" >
< div className = "flex items-center justify-between rounded-lg border border-border/30 bg-background/50 p-4" >
< div className = "space-y-1" >
< p className = "text-sm font-medium" > { hardwareData . power_meter . name } < / p >
{ hardwareData . power_meter . adapter && (
< p className = "text-xs text-muted-foreground" > { hardwareData . power_meter . adapter } < / p >
) }
< / div >
< div className = "text-right" >
< p className = "text-2xl font-bold text-yellow-500" > { hardwareData . power_meter . watts . toFixed ( 1 ) } W < / p >
< p className = "text-xs text-muted-foreground" > Current Draw < / p >
< / div >
< / div >
< / div >
< / Card >
) }
{ /* Fans */ }
{ hardwareData ? . fans && hardwareData . fans . length > 0 && (
< Card className = "border-border/50 bg-card/50 p-6" >
< div className = "mb-4 flex items-center gap-2" >
< FanIcon className = "h-5 w-5 text-primary" / >
< h2 className = "text-lg font-semibold" > System Fans < / h2 >
< Badge variant = "outline" className = "ml-auto" >
{ hardwareData . fans . length } fans
< / Badge >
< / div >
< div className = "grid gap-4 md:grid-cols-2" >
{ hardwareData . fans . map ( ( fan , index ) = > {
const maxRPM = 5000
const percentage = Math . min ( ( fan . speed / maxRPM ) * 100 , 100 )
return (
< div key = { index } className = "space-y-2" >
< div className = "flex items-center justify-between" >
< span className = "text-sm font-medium" > { fan . name } < / span >
< span className = "text-sm font-semibold text-blue-500" >
{ fan . speed . toFixed ( 0 ) } { fan . unit }
< / span >
< / div >
< div className = "h-2 w-full overflow-hidden rounded-full bg-secondary" >
< div className = "h-full bg-blue-500 transition-all" style = { { width : ` ${ percentage } % ` } } / >
< / div >
{ fan . adapter && < span className = "text-xs text-muted-foreground" > { fan . adapter } < / span > }
< / div >
)
} ) }
< / div >
< / Card >
) }
{ /* Power Supplies */ }
{ hardwareData ? . power_supplies && hardwareData . power_supplies . length > 0 && (
< Card className = "border-border/50 bg-card/50 p-6" >
< div className = "mb-4 flex items-center gap-2" >
< PowerIcon className = "h-5 w-5 text-primary" / >
< h2 className = "text-lg font-semibold" > Power Supplies < / h2 >
< Badge variant = "outline" className = "ml-auto" >
{ hardwareData . power_supplies . length } PSUs
< / Badge >
< / div >
< div className = "grid gap-3 md:grid-cols-2" >
{ hardwareData . power_supplies . map ( ( psu , index ) = > (
< div key = { index } className = "rounded-lg border border-border/30 bg-background/50 p-4" >
< div className = "flex items-center justify-between" >
< span className = "text-sm font-medium" > { psu . name } < / span >
{ psu . status && (
< Badge variant = { psu . status . toLowerCase ( ) === "ok" ? "default" : "destructive" } > { psu . status } < / Badge >
) }
< / div >
< p className = "mt-2 text-2xl font-bold text-primary" > { psu . watts } W < / p >
< p className = "text-xs text-muted-foreground" > Current Output < / p >
< / div >
) ) }
< / div >
< / Card >
) }
{ /* UPS */ }
{ hardwareData ? . ups && Object . keys ( hardwareData . ups ) . length > 0 && hardwareData . ups . model && (
< Card className = "border-border/50 bg-card/50 p-6" >
< div className = "mb-4 flex items-center gap-2" >
< Battery className = "h-5 w-5 text-primary" / >
< h2 className = "text-lg font-semibold" > UPS Status < / h2 >
< / div >
< div className = "space-y-4" >
< div className = "rounded-lg border border-border/30 bg-background/50 p-4" >
< div className = "flex items-center justify-between mb-4" >
< span className = "text-sm font-medium" > { hardwareData . ups . model } < / span >
< Badge variant = { hardwareData . ups . status === "OL" ? "default" : "destructive" } >
{ hardwareData . ups . status }
< / Badge >
< / div >
< div className = "grid gap-3 md:grid-cols-2" >
{ hardwareData . ups . battery_charge && (
< div className = "space-y-1" >
< div className = "flex items-center justify-between" >
< span className = "text-xs text-muted-foreground" > Battery Charge < / span >
< span className = "text-sm font-semibold" > { hardwareData . ups . battery_charge } < / span >
< / div >
< Progress
value = { Number . parseInt ( hardwareData . ups . battery_charge . replace ( "%" , "" ) ) }
className = "h-2"
/ >
< / div >
) }
{ hardwareData . ups . load_percent && (
< div className = "space-y-1" >
< div className = "flex items-center justify-between" >
< span className = "text-xs text-muted-foreground" > Load < / span >
< span className = "text-sm font-semibold" > { hardwareData . ups . load_percent } < / span >
< / div >
< Progress value = { Number . parseInt ( hardwareData . ups . load_percent . replace ( "%" , "" ) ) } className = "h-2" / >
< / div >
) }
{ hardwareData . ups . time_left && (
< div >
< span className = "text-xs text-muted-foreground" > Runtime < / span >
< p className = "text-sm font-semibold" > { hardwareData . ups . time_left } < / p >
< / div >
) }
{ hardwareData . ups . line_voltage && (
< div >
< span className = "text-xs text-muted-foreground" > Input Voltage < / span >
< p className = "text-sm font-semibold" > { hardwareData . ups . line_voltage } < / p >
< / div >
) }
< / div >
< / div >
< / div >
< / Card >
) }
{ /* Network Summary - Clickable */ }
{ hardwareData ? . pci_devices &&
hardwareData . pci_devices . filter ( ( d ) = > d . type . toLowerCase ( ) . includes ( "network" ) ) . length > 0 && (
< Card className = "border-border/50 bg-card/50 p-6" >
< div className = "mb-4 flex items-center gap-2" >
< Network className = "h-5 w-5 text-primary" / >
< h2 className = "text-lg font-semibold" > Network Summary < / h2 >
< Badge variant = "outline" className = "ml-auto" >
{ hardwareData . pci_devices . filter ( ( d ) = > d . type . toLowerCase ( ) . includes ( "network" ) ) . length } interfaces
< / Badge >
< / div >
< div className = "grid gap-3 md:grid-cols-2 lg:grid-cols-3" >
{ hardwareData . pci_devices
. filter ( ( d ) = > d . type . toLowerCase ( ) . includes ( "network" ) )
. map ( ( device , index ) = > (
< div
key = { index }
onClick = { ( ) = > setSelectedNetwork ( device ) }
className = "cursor-pointer rounded-lg border border-border/30 bg-background/50 p-3 transition-colors hover:bg-background/80"
>
< div className = "flex items-center justify-between mb-1" >
< span className = "text-sm font-medium truncate" > { device . device } < / span >
< Badge className = "bg-blue-500/10 text-blue-500 border-blue-500/20 text-xs" > Ethernet < / Badge >
< / div >
< p className = "text-xs text-muted-foreground truncate" > { device . vendor } < / p >
{ device . driver && < p className = "mt-1 font-mono text-xs text-green-500" > Driver : { device . driver } < / p > }
< / div >
) ) }
< / div >
< p className = "mt-4 text-xs text-muted-foreground" > Click on an interface for detailed information < / p >
< / Card >
) }
{ /* Network Detail Modal */ }
< Dialog open = { selectedNetwork !== null } onOpenChange = { ( ) = > setSelectedNetwork ( null ) } >
< DialogContent className = "max-w-2xl" >
< DialogHeader >
< DialogTitle > { selectedNetwork ? . device } < / DialogTitle >
< DialogDescription > Network Interface Information < / DialogDescription >
< / DialogHeader >
{ selectedNetwork && (
< div className = "space-y-3" >
< div className = "flex justify-between border-b border-border/50 pb-2" >
< span className = "text-sm font-medium text-muted-foreground" > Device Type < / span >
< Badge className = { getDeviceTypeColor ( selectedNetwork . type ) } > { selectedNetwork . type } < / Badge >
< / div >
< div className = "flex justify-between border-b border-border/50 pb-2" >
< span className = "text-sm font-medium text-muted-foreground" > PCI Slot < / span >
< span className = "font-mono text-sm" > { selectedNetwork . slot } < / span >
< / div >
< div className = "flex justify-between border-b border-border/50 pb-2" >
< span className = "text-sm font-medium text-muted-foreground" > Vendor < / span >
< span className = "text-sm" > { selectedNetwork . vendor } < / span >
< / div >
< div className = "flex justify-between border-b border-border/50 pb-2" >
< span className = "text-sm font-medium text-muted-foreground" > Class < / span >
< span className = "font-mono text-sm" > { selectedNetwork . class } < / span >
< / div >
{ selectedNetwork . driver && (
< div className = "flex justify-between border-b border-border/50 pb-2" >
< span className = "text-sm font-medium text-muted-foreground" > Driver < / span >
< span className = "font-mono text-sm text-green-500" > { selectedNetwork . driver } < / span >
< / div >
) }
{ selectedNetwork . kernel_module && (
< div className = "flex justify-between border-b border-border/50 pb-2" >
< span className = "text-sm font-medium text-muted-foreground" > Kernel Module < / span >
< span className = "font-mono text-sm" > { selectedNetwork . kernel_module } < / span >
< / div >
) }
< / div >
) }
< / DialogContent >
< / Dialog >
{ /* Storage Summary - Clickable */ }
{ hardwareData ? . storage_devices && hardwareData . storage_devices . length > 0 && (
< Card className = "border-border/50 bg-card/50 p-6" >
< div className = "mb-4 flex items-center gap-2" >
< HardDrive className = "h-5 w-5 text-primary" / >
< h2 className = "text-lg font-semibold" > Storage Summary < / h2 >
< Badge variant = "outline" className = "ml-auto" >
{ hardwareData . storage_devices . length } devices
< / Badge >
< / div >
< div className = "grid gap-3 md:grid-cols-2 lg:grid-cols-3" >
{ hardwareData . storage_devices . map ( ( device , index ) = > (
< div
key = { index }
onClick = { ( ) = > setSelectedDisk ( device ) }
className = "cursor-pointer rounded-lg border border-border/30 bg-background/50 p-3 transition-colors hover:bg-background/80"
>
< div className = "flex items-center justify-between mb-2" >
< span className = "text-sm font-medium" > { device . name } < / span >
< Badge className = "bg-blue-500/10 text-blue-500 border-blue-500/20 px-2.5 py-0.5" > { device . type } < / Badge >
< / div >
{ device . size && < p className = "text-sm font-medium" > { device . size } < / p > }
{ device . model && < p className = "text-xs text-muted-foreground truncate" > { device . model } < / p > }
{ device . driver && < p className = "mt-1 font-mono text-xs text-green-500" > Driver : { device . driver } < / p > }
< / div >
) ) }
< / div >
< p className = "mt-4 text-xs text-muted-foreground" > Click on a device for detailed hardware information < / p >
< / Card >
) }
{ /* Disk Detail Modal */ }
< Dialog open = { selectedDisk !== null } onOpenChange = { ( ) = > setSelectedDisk ( null ) } >
< DialogContent className = "max-w-2xl" >
< DialogHeader >
< DialogTitle > { selectedDisk ? . name } < / DialogTitle >
< DialogDescription > Storage Device Hardware Information < / DialogDescription >
< / DialogHeader >
{ selectedDisk && (
< div className = "space-y-3" >
< div className = "flex justify-between border-b border-border/50 pb-2" >
< span className = "text-sm font-medium text-muted-foreground" > Device Name < / span >
< span className = "font-mono text-sm" > { selectedDisk . name } < / span >
< / div >
{ selectedDisk . type && (
< div className = "flex justify-between border-b border-border/50 pb-2" >
< span className = "text-sm font-medium text-muted-foreground" > Type < / span >
< Badge className = "bg-blue-500/10 text-blue-500 border-blue-500/20" > { selectedDisk . type } < / Badge >
< / div >
) }
{ selectedDisk . size && (
< div className = "flex justify-between border-b border-border/50 pb-2" >
< span className = "text-sm font-medium text-muted-foreground" > Capacity < / span >
< span className = "text-sm font-medium" > { selectedDisk . size } < / span >
< / div >
) }
{ selectedDisk . model && (
< div className = "flex justify-between border-b border-border/50 pb-2" >
< span className = "text-sm font-medium text-muted-foreground" > Model < / span >
< span className = "text-sm text-right" > { selectedDisk . model } < / span >
< / div >
) }
{ selectedDisk . family && (
< div className = "flex justify-between border-b border-border/50 pb-2" >
< span className = "text-sm font-medium text-muted-foreground" > Family < / span >
< span className = "text-sm text-right" > { selectedDisk . family } < / span >
< / div >
) }
{ selectedDisk . serial && (
< div className = "flex justify-between border-b border-border/50 pb-2" >
< span className = "text-sm font-medium text-muted-foreground" > Serial Number < / span >
< span className = "font-mono text-sm" > { selectedDisk . serial } < / span >
< / div >
) }
{ selectedDisk . firmware && (
< div className = "flex justify-between border-b border-border/50 pb-2" >
< span className = "text-sm font-medium text-muted-foreground" > Firmware < / span >
< span className = "font-mono text-sm" > { selectedDisk . firmware } < / span >
< / div >
) }
{ selectedDisk . interface && (
< div className = "flex justify-between border-b border-border/50 pb-2" >
< span className = "text-sm font-medium text-muted-foreground" > Interface < / span >
< span className = "text-sm font-medium" > { selectedDisk . interface } < / span >
< / div >
) }
{ selectedDisk . driver && (
< div className = "flex justify-between border-b border-border/50 pb-2" >
< span className = "text-sm font-medium text-muted-foreground" > Driver < / span >
< span className = "font-mono text-sm text-green-500" > { selectedDisk . driver } < / span >
< / div >
) }
{ selectedDisk . rotation_rate && (
< div className = "flex justify-between border-b border-border/50 pb-2" >
< span className = "text-sm font-medium text-muted-foreground" > Rotation Rate < / span >
< span className = "text-sm" > { selectedDisk . rotation_rate } < / span >
< / div >
) }
{ selectedDisk . form_factor && (
< div className = "flex justify-between border-b border-border/50 pb-2" >
< span className = "text-sm font-medium text-muted-foreground" > Form Factor < / span >
< span className = "text-sm" > { selectedDisk . form_factor } < / span >
< / div >
) }
{ selectedDisk . sata_version && (
< div className = "flex justify-between border-b border-border/50 pb-2" >
< span className = "text-sm font-medium text-muted-foreground" > SATA Version < / span >
< span className = "text-sm" > { selectedDisk . sata_version } < / span >
< / div >
) }
< / div >
) }
< / DialogContent >
< / Dialog >
2025-10-05 20:45:54 +02:00
< / div >
)
}