spb/maclustr-console
Public
TypeScript 96.1%
CSS 3.1%
JavaScript 0.8%
1"use client";23import type { NASMetrics } from "@/lib/nas";45function fmt(bytes: number): string {6 const gb = bytes / (1024 * 1024 * 1024);7 if (gb >= 1024) return `${(gb / 1024).toFixed(2)} TB`;8 if (gb >= 1) return `${gb.toFixed(0)} GB`;9 return `${(bytes / (1024 * 1024)).toFixed(0)} MB`;10}1112export default function NASCard({ nas }: { nas: NASMetrics }) {13 const pct = Math.min(Math.max(nas.usedPercent ?? 0, 0), 100);14 const online = nas.online ?? false;15 const color = pct >= 90 ? "#ef4444" : pct >= 75 ? "#f59e0b" : "#22c55e";1617 const r = 42, c = 2 * Math.PI * r;18 const dash = (pct / 100) * c;1920 return (21 <div className="glass glass-hover rounded-2xl p-5 fade-in">22 <div className="flex items-center justify-between mb-4">23 <div className="flex items-center gap-3">24 <div className="text-orange-500"><DriveIcon /></div>25 <div>26 <div className="flex items-center gap-2">27 <h3 className="text-sm font-bold text-zinc-900">{nas.name}</h3>28 <span className={`h-2 w-2 rounded-full ${online ? "bg-green-500" : "bg-red-500"}`}29 style={online ? { boxShadow: "0 0 8px #22c55e88" } : {}} />30 </div>31 <div className="text-xs text-zinc-500">{nas.model} · {nas.host}</div>32 </div>33 </div>34 <a href={nas.webUrl} target="_blank" rel="noreferrer"35 className="text-xs font-medium bg-white/70 border border-zinc-200 text-zinc-700 px-3 py-1.5 rounded-lg hover:bg-white hover:border-violet-300 transition">36 Open UGOS37 </a>38 </div>3940 <div className="flex items-center gap-6">41 <div className="relative shrink-0" style={{ width: 110, height: 110 }}>42 <svg width="110" height="110" viewBox="0 0 110 110">43 <circle cx="55" cy="55" r={r} fill="none" stroke={color} strokeOpacity="0.15" strokeWidth="10" />44 <circle cx="55" cy="55" r={r} fill="none" stroke={color} strokeWidth="10" strokeLinecap="round"45 strokeDasharray={`${dash} ${c}`} transform="rotate(-90 55 55)"46 style={{ transition: "stroke-dasharray .5s ease" }} />47 </svg>48 <div className="absolute inset-0 flex flex-col items-center justify-center">49 <span className="text-2xl font-bold tabular-nums text-zinc-900">{Math.round(pct)}%</span>50 <span className="text-[10px] text-zinc-500">used</span>51 </div>52 </div>5354 <div className="grid grid-cols-2 gap-x-6 gap-y-3 flex-1">55 <Stat label="Used" value={fmt(nas.usedBytes)} accent={color} />56 <Stat label="Free" value={fmt(nas.availBytes)} accent="#16a34a" />57 <Stat label="Capacity" value={fmt(nas.totalBytes)} accent="#2563eb" />58 <Stat label="Mounted on" value={`${nas.mountedNodes}/${nas.totalNodes}`}59 accent={nas.mountedNodes === nas.totalNodes && online ? "#16a34a" : "#d97706"} />60 </div>61 </div>6263 <div className="mt-4 pt-3 border-t border-zinc-200/70 flex flex-wrap gap-x-6 gap-y-1 text-xs text-zinc-500">64 <span>Share: <span className="text-zinc-700 font-medium">{nas.share}</span></span>65 <span>OS: <span className="text-zinc-700 font-medium">UGOS Pro</span></span>66 </div>67 </div>68 );69}7071function Stat({ label, value, accent }: { label: string; value: string; accent: string }) {72 return (73 <div>74 <div className="text-xs text-zinc-500">{label}</div>75 <div className="text-lg font-bold tabular-nums text-zinc-900" style={{ color: accent }}>{value}</div>76 </div>77 );78}7980const DriveIcon = () => (81 <svg width="26" height="26" viewBox="0 0 24 24" fill="none" stroke="currentColor"82 strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round">83 <rect x="3" y="4" width="18" height="16" rx="2" />84 <path d="M7 8h10M7 12h10" />85 <circle cx="8" cy="16" r="1" />86 </svg>87);88