"use client" import { useState, useEffect } from "react" import { Card, CardContent, CardHeader, CardTitle } from "./ui/card" import { Progress } from "./ui/progress" import { Badge } from "./ui/badge" import { Button } from "./ui/button" import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogTrigger } from "./ui/dialog" import { HardDrive, Database, Archive, AlertTriangle, CheckCircle, Activity, AlertCircle, Info, Thermometer, } from "lucide-react" interface StorageData { total: number used: number available: number disks: DiskInfo[] } interface DiskInfo { name: string mountpoint: string fstype: string total: number used: number available: number usage_percent: number health: string temperature: number disk_type?: string percentage_used?: number ssd_life_left?: number wear_leveling_count?: number media_wearout_indicator?: number } interface DiskGroup { type: string disks: DiskInfo[] avgTemp: number status: "safe" | "warning" | "critical" } const fetchStorageData = async (): Promise => { try { const response = await fetch("/api/storage", { method: "GET", headers: { "Content-Type": "application/json", }, signal: AbortSignal.timeout(5000), }) if (!response.ok) { throw new Error(`Flask server responded with status: ${response.status}`) } const data = await response.json() return data } catch (error) { console.error("[v0] Failed to fetch storage data from Flask server:", error) return null } } const getTempStatus = (temp: number, diskType: string): "safe" | "warning" | "critical" => { if (diskType === "HDD") { if (temp > 55) return "critical" if (temp > 45) return "warning" return "safe" } else if (diskType === "SSD") { if (temp > 65) return "critical" if (temp > 55) return "warning" return "safe" } else if (diskType === "NVMe") { if (temp > 70) return "critical" if (temp > 60) return "warning" return "safe" } // Umbral genérico if (temp > 70) return "critical" if (temp > 60) return "warning" return "safe" } const groupDisksByType = (disks: DiskInfo[]): DiskGroup[] => { const groups: { [key: string]: DiskInfo[] } = {} disks.forEach((disk) => { const type = disk.disk_type || "Unknown" if (!groups[type]) { groups[type] = [] } groups[type].push(disk) }) return Object.entries(groups).map(([type, disks]) => { const temps = disks.map((d) => d.temperature).filter((t) => t > 0) const avgTemp = temps.length > 0 ? Math.round(temps.reduce((a, b) => a + b, 0) / temps.length) : 0 // Determinar el estado más crítico del grupo let status: "safe" | "warning" | "critical" = "safe" disks.forEach((disk) => { const diskStatus = getTempStatus(disk.temperature, type) if (diskStatus === "critical") status = "critical" else if (diskStatus === "warning" && status !== "critical") status = "warning" }) return { type, disks, avgTemp, status } }) } function TemperatureThresholdsModal() { return ( Umbrales de temperatura por tipo de disco Rangos de temperatura recomendados para cada tipo de dispositivo de almacenamiento
Tipo de disco Temperatura de operación Zona segura Zona de advertencia Zona crítica
HDD 0°C – 60°C (común: 5–55°C) ≤ 45°C 46 – 55°C > 55°C
SSD 0°C – 70°C ≤ 55°C 56 – 65°C > 65°C
NVMe 0°C – 70°C ≤ 60°C 61 – 70°C > 70°C
) } export function StorageMetrics() { const [storageData, setStorageData] = useState(null) const [loading, setLoading] = useState(true) const [error, setError] = useState(null) useEffect(() => { const fetchData = async () => { setLoading(true) setError(null) const result = await fetchStorageData() if (!result) { setError("Flask server not available. Please ensure the server is running.") } else { setStorageData(result) } setLoading(false) } fetchData() const interval = setInterval(fetchData, 60000) return () => clearInterval(interval) }, []) if (loading) { return (
Loading storage data...
) } if (error || !storageData) { return (
Flask Server Not Available
{error || "Unable to connect to the Flask server. Please ensure the server is running and try again."}
) } const usagePercent = storageData.total > 0 ? (storageData.used / storageData.total) * 100 : 0 const diskGroups = groupDisksByType(storageData.disks) return (
{/* Storage Overview Cards */}
Total Storage
{storageData.total.toFixed(1)} GB

{storageData.used.toFixed(1)} GB used • {storageData.available.toFixed(1)} GB available

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

{usagePercent.toFixed(1)}% of total space

Available
{storageData.available.toFixed(1)} GB
{((storageData.available / storageData.total) * 100).toFixed(1)}% Free

Available space

Disks
{storageData.disks.length}
{storageData.disks.filter((d) => d.health === "healthy").length} Healthy

Storage devices

{diskGroups.length > 0 && (

Temperatura por tipo de disco

{diskGroups.map((group) => (
{group.type} Temperature
{group.status === "safe" ? "Seguro" : group.status === "warning" ? "Advertencia" : "Crítico"}
{group.avgTemp}°C

Promedio de {group.disks.length} disco{group.disks.length > 1 ? "s" : ""}

{group.disks.map((disk, idx) => (
{disk.name} {disk.temperature}°C
))}
))}
)} {/* Disk Details */} Storage Devices
{storageData.disks.map((disk, index) => (
{disk.name} {disk.disk_type && ( {disk.disk_type} )}
{disk.fstype} • {disk.mountpoint}
{disk.used.toFixed(1)} GB / {disk.total.toFixed(1)} GB
Temp
{disk.temperature}°C
{(disk.disk_type === "SSD" || disk.disk_type === "NVMe") && disk.ssd_life_left !== undefined && (
Vida útil
= 80 ? "text-green-500" : disk.ssd_life_left >= 50 ? "text-yellow-500" : "text-red-500" }`} > {disk.ssd_life_left}%
)} {disk.health === "healthy" ? ( ) : ( )} {disk.health}
))}
) }