/** * Array Card Component * Displays Unraid array status and disk information */ import { ActionIcon, Badge, Card, Group, Progress, Stack, Table, Text, ThemeIcon, Title, Tooltip, } from '@mantine/core'; import { IconDatabase, IconPlayerPlay, IconPlayerStop, IconTemperature, IconHardDrive, } from '@tabler/icons-react'; import type { UnraidArray, ArrayDisk, ArrayState } from '~/lib/unraid/types'; interface ArrayCardProps { array: UnraidArray; onStartArray?: () => void; onStopArray?: () => void; isLoading?: boolean; } function formatBytes(bytes: number): string { if (bytes === 0) return '0 B'; const k = 1024; const sizes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB']; const i = Math.floor(Math.log(bytes) / Math.log(k)); return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]; } function getStatusColor(status: string): string { switch (status) { case 'DISK_OK': return 'green'; case 'DISK_INVALID': case 'DISK_WRONG': return 'red'; case 'DISK_DSBL': case 'DISK_DSBL_NEW': return 'orange'; case 'DISK_NEW': return 'blue'; case 'DISK_NP': return 'gray'; default: return 'gray'; } } function getArrayStateColor(state: ArrayState): string { switch (state) { case 'STARTED': return 'green'; case 'STOPPED': return 'red'; default: return 'orange'; } } function DiskRow({ disk }: { disk: ArrayDisk }) { const usedPercent = disk.fsSize && disk.fsUsed ? ((disk.fsUsed / disk.fsSize) * 100).toFixed(1) : null; return ( {disk.name} {disk.status.replace('DISK_', '')} {formatBytes(disk.size)} {disk.temp !== null ? ( 50 ? 'red' : disk.temp > 40 ? 'orange' : undefined}> {disk.temp}°C ) : ( {disk.spunDown ? 'Spun down' : '-'} )} {usedPercent ? ( 90 ? 'red' : parseFloat(usedPercent) > 75 ? 'orange' : 'blue'} /> ) : ( - )} ); } export function ArrayCard({ array, onStartArray, onStopArray, isLoading }: ArrayCardProps) { const usedPercent = ((array.capacity.used / array.capacity.total) * 100).toFixed(1); const isStarted = array.state === 'STARTED'; return (
Array {formatBytes(array.capacity.used)} / {formatBytes(array.capacity.total)}
{array.state} {isStarted && onStopArray && ( )} {!isStarted && onStartArray && ( )}
{/* Array Capacity */}
Total Capacity {usedPercent}% used 90 ? 'red' : parseFloat(usedPercent) > 75 ? 'orange' : 'blue'} />
{/* Parity Check Status */} {array.parityCheckStatus?.running && (
Parity Check in Progress {array.parityCheckStatus.progress.toFixed(1)}% - {array.parityCheckStatus.errors} errors
)} {/* Parity Disks */} {array.parities.length > 0 && ( <> Parity ({array.parities.length}) {array.parities.map((disk) => ( ))}
Disk Status Size Temp Usage
)} {/* Data Disks */} {array.disks.length > 0 && ( <> Data Disks ({array.disks.length}) {array.disks.map((disk) => ( ))}
Disk Status Size Temp Usage
)} {/* Cache Pools */} {array.caches.length > 0 && ( <> Cache Pools ({array.caches.length}) {array.caches.map((cache) => { const cacheUsedPercent = ((cache.fsUsed / cache.fsSize) * 100).toFixed(1); return (
{cache.name} ({cache.fsType}) {formatBytes(cache.fsUsed)} / {formatBytes(cache.fsSize)} 90 ? 'red' : parseFloat(cacheUsedPercent) > 75 ? 'orange' : 'teal'} />
); })} )}
); } export default ArrayCard;