Split Proxmox node card into system info (left) / storage (right)

Was a single stacked column (uptime/CPU/memory/swap, then the storage
table below); now a two-column layout within the card body — system
stats on the left, storage table (or the missing-permission hint) on
the right — so the card reads more like a dashboard tile at a glance.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
bobbanandClaude Sonnet 5 committed 2026-09-17 20:50:16 +02:00
1 parent 846953194c
commit 7b39b7be4d
1 file changed
+58 -56
+58 -56
View File
@@ -186,68 +186,70 @@ export default function Proxmox({ user }: { user: CurrentUser }) {
<div className="card-body"> <div className="card-body">
{n.error && <div className="alert alert-danger">{n.error}</div>} {n.error && <div className="alert alert-danger">{n.error}</div>}
<div className="row g-3"> <div className="row g-3">
<div className="col-sm-6"> <div className="col-md-6">
<div className="text-secondary small">Uptime</div> <div className="mb-3">
<div>{formatUptime(n.uptime)}</div> <div className="text-secondary small">Uptime</div>
</div> <div>{formatUptime(n.uptime)}</div>
<div className="col-sm-6">
<div className="text-secondary small">CPU</div>
<div>
{n.cpuUsagePercent !== null ? `${n.cpuUsagePercent.toFixed(1)}%` : "—"}
{n.cpuCores ? <span className="text-secondary"> ({n.cpuCores} cores)</span> : null}
</div> </div>
{n.loadAverage && ( <div className="mb-3">
<div className="text-secondary small">Load: {n.loadAverage.map((l) => l.toFixed(2)).join(", ")}</div> <div className="text-secondary small">CPU</div>
<div>
{n.cpuUsagePercent !== null ? `${n.cpuUsagePercent.toFixed(1)}%` : "—"}
{n.cpuCores ? <span className="text-secondary"> ({n.cpuCores} cores)</span> : null}
</div>
{n.loadAverage && (
<div className="text-secondary small">Load: {n.loadAverage.map((l) => l.toFixed(2)).join(", ")}</div>
)}
</div>
<div className={n.swapTotalBytes ? "mb-3" : ""}>
<div className="text-secondary small">Memory</div>
<div>
{formatBytes(n.memUsedBytes)} / {formatBytes(n.memTotalBytes)}
</div>
{usageBar(n.memUsedBytes, n.memTotalBytes)}
</div>
{!!n.swapTotalBytes && (
<div>
<div className="text-secondary small">Swap</div>
<div>
{formatBytes(n.swapUsedBytes)} / {formatBytes(n.swapTotalBytes)}
</div>
{usageBar(n.swapUsedBytes, n.swapTotalBytes)}
</div>
)} )}
</div> </div>
<div className="col-sm-6">
<div className="text-secondary small">Memory</div>
<div>
{formatBytes(n.memUsedBytes)} / {formatBytes(n.memTotalBytes)}
</div>
{usageBar(n.memUsedBytes, n.memTotalBytes)}
</div>
{!!n.swapTotalBytes && (
<div className="col-sm-6">
<div className="text-secondary small">Swap</div>
<div>
{formatBytes(n.swapUsedBytes)} / {formatBytes(n.swapTotalBytes)}
</div>
{usageBar(n.swapUsedBytes, n.swapTotalBytes)}
</div>
)}
</div>
{n.storages.length > 0 ? ( <div className="col-md-6">
<div className="mt-3">
<div className="text-secondary small mb-1">Storage</div> <div className="text-secondary small mb-1">Storage</div>
<table className="table table-sm table-vcenter mb-0"> {n.storages.length > 0 ? (
<tbody> <table className="table table-sm table-vcenter mb-0">
{n.storages.map((s) => ( <tbody>
<tr key={s.id}> {n.storages.map((s) => (
<td> <tr key={s.id}>
{s.id} <td>
{!s.active && <span className="badge bg-secondary-lt text-secondary ms-1">Inactive</span>} {s.id}
</td> {!s.active && <span className="badge bg-secondary-lt text-secondary ms-1">Inactive</span>}
<td className="text-secondary">{s.type}</td> </td>
<td> <td className="text-secondary">{s.type}</td>
{formatBytes(s.usedBytes)} / {formatBytes(s.totalBytes)} <td>
{usageBar(s.usedBytes, s.totalBytes)} {formatBytes(s.usedBytes)} / {formatBytes(s.totalBytes)}
</td> {usageBar(s.usedBytes, s.totalBytes)}
</tr> </td>
))} </tr>
</tbody> ))}
</table> </tbody>
</table>
) : (
!n.error && (
<div className="text-secondary small">
No storage visible to this API token. Proxmox filters the storage list
per-token — grant it <code>Datastore.Audit</code> (e.g. on{" "}
<code>/storage</code>) to see disk usage here.
</div>
)
)}
</div> </div>
) : ( </div>
!n.error && (
<div className="text-secondary small mt-3">
No storage visible to this API token. Proxmox filters the storage list per-token —
grant it <code>Datastore.Audit</code> (e.g. on <code>/storage</code>) to see disk
usage here.
</div>
)
)}
</div> </div>
</div> </div>
</div> </div>