"use client" import { useEffect, useState } from "react" import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card" import { HardDrive, Database, AlertTriangle, CheckCircle2, XCircle, Thermometer, Info } from "lucide-react" import { Badge } from "@/components/ui/badge" import { Progress } from "@/components/ui/progress" import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from "@/components/ui/dialog" interface DiskInfo { name: string size?: string temperature: number health: string power_on_hours?: number smart_status?: string model?: string serial?: string mountpoint?: string fstype?: string total?: number used?: number available?: number usage_percent?: number reallocated_sectors?: number pending_sectors?: number crc_errors?: number } interface ZFSPool { name: string size: string allocated: string free: string health: string } interface StorageData { total: number used: number available: number disks: DiskInfo[] zfs_pools: ZFSPool[] disk_count: number healthy_disks: number warning_disks: number critical_disks: number error?: string } export function StorageOverview() { const [storageData, setStorageData] = useState(null) const [loading, setLoading] = useState(true) const [selectedDisk, setSelectedDisk] = useState(null) const [detailsOpen, setDetailsOpen] = useState(false) const fetchStorageData = async () => { try { const baseUrl = typeof window !== "undefined" ? `${window.location.protocol}//${window.location.hostname}:8008` : "" const response = await fetch(`${baseUrl}/api/storage`) const data = await response.json() console.log("[v0] Storage data received:", data) setStorageData(data) } catch (error) { console.error("Error fetching storage data:", error) } finally { setLoading(false) } } useEffect(() => { fetchStorageData() const interval = setInterval(fetchStorageData, 30000) // Update every 30 seconds return () => clearInterval(interval) }, []) const getHealthIcon = (health: string) => { switch (health.toLowerCase()) { case "healthy": case "passed": case "online": return case "warning": return case "critical": case "failed": case "degraded": return default: return } } const getHealthBadge = (health: string) => { switch (health.toLowerCase()) { case "healthy": case "passed": case "online": return Healthy case "warning": return Warning case "critical": case "failed": case "degraded": return Critical default: return Unknown } } const getTempColor = (temp: number) => { if (temp === 0) return "text-gray-500" if (temp < 45) return "text-green-500" if (temp < 60) return "text-yellow-500" return "text-red-500" } const formatHours = (hours: number) => { if (hours === 0) return "N/A" const years = Math.floor(hours / 8760) const days = Math.floor((hours % 8760) / 24) if (years > 0) { return `${years}y ${days}d` } return `${days}d` } const handleDiskClick = (disk: DiskInfo) => { setSelectedDisk(disk) setDetailsOpen(true) } if (loading) { return (
Loading storage information...
) } if (!storageData || storageData.error) { return (
Error loading storage data: {storageData?.error || "Unknown error"}
) } const disksWithTemp = storageData.disks.filter((disk) => disk.temperature > 0) const avgTemp = disksWithTemp.length > 0 ? Math.round(disksWithTemp.reduce((sum, disk) => sum + disk.temperature, 0) / disksWithTemp.length) : 0 const usagePercent = storageData.total > 0 ? ((storageData.used / (storageData.total * 1024)) * 100).toFixed(2) : "0.00" return (
{/* Storage Summary */}
Total Storage
{storageData.total.toFixed(1)} TB

{storageData.disk_count} physical disks

Used Storage
{storageData.used.toFixed(1)} GB

{usagePercent}% used

Disk Health
{storageData.healthy_disks}

{storageData.warning_disks > 0 && ( {storageData.warning_disks} warning )} {storageData.critical_disks > 0 && ( {storageData.critical_disks} critical )} {storageData.warning_disks === 0 && storageData.critical_disks === 0 && "All disks healthy"}

Avg Temperature
{avgTemp > 0 ? `${avgTemp}°C` : "N/A"}

Across all disks

{/* ZFS Pools */} {storageData.zfs_pools && storageData.zfs_pools.length > 0 && ( ZFS Pools
{storageData.zfs_pools.map((pool) => (

{pool.name}

{getHealthBadge(pool.health)}
{getHealthIcon(pool.health)}

Size

{pool.size}

Allocated

{pool.allocated}

Free

{pool.free}

))}
)} {/* Physical Disks */} Physical Disks & SMART Status
{storageData.disks.map((disk) => (
handleDiskClick(disk)} >

/dev/{disk.name}

{disk.model && disk.model !== "Unknown" && (

{disk.model}

)}
{disk.temperature > 0 && (
{disk.temperature}°C
)} {getHealthBadge(disk.health)}
{disk.size && (

Size

{disk.size}

)} {disk.smart_status && disk.smart_status !== "unknown" && (

SMART Status

{disk.smart_status}

)} {disk.power_on_hours !== undefined && disk.power_on_hours > 0 && (

Power On Time

{formatHours(disk.power_on_hours)}

)} {disk.serial && disk.serial !== "Unknown" && (

Serial

{disk.serial}

)}
{disk.mountpoint && (
Mounted at: {disk.mountpoint} {disk.fstype && ({disk.fstype})}
{disk.usage_percent !== undefined && ( {disk.usage_percent}% )}
{disk.usage_percent !== undefined && (
{disk.used} GB used {disk.available} GB free of {disk.total} GB
)}
)}
))}
{/* Disk Details Dialog */} Disk Details: /dev/{selectedDisk?.name} Complete SMART information and health status {selectedDisk && (

Model

{selectedDisk.model}

Serial Number

{selectedDisk.serial}

Capacity

{selectedDisk.size}

Health Status

{getHealthBadge(selectedDisk.health)}

SMART Attributes

Temperature

{selectedDisk.temperature > 0 ? `${selectedDisk.temperature}°C` : "N/A"}

Power On Hours

{selectedDisk.power_on_hours && selectedDisk.power_on_hours > 0 ? `${selectedDisk.power_on_hours.toLocaleString()}h (${formatHours(selectedDisk.power_on_hours)})` : "N/A"}

SMART Status

{selectedDisk.smart_status}

Reallocated Sectors

0 ? "text-yellow-500" : ""}`} > {selectedDisk.reallocated_sectors ?? 0}

Pending Sectors

0 ? "text-yellow-500" : ""}`} > {selectedDisk.pending_sectors ?? 0}

CRC Errors

0 ? "text-yellow-500" : ""}`} > {selectedDisk.crc_errors ?? 0}

{selectedDisk.mountpoint && (

Mount Information

Mount Point: {selectedDisk.mountpoint}
Filesystem: {selectedDisk.fstype}
{selectedDisk.total && ( <>
Total: {selectedDisk.total} GB
Used: {selectedDisk.used} GB
Available: {selectedDisk.available} GB
{selectedDisk.usage_percent !== undefined && (

{selectedDisk.usage_percent}% used

)} )}
)}
)}
) }