Feature: Snapshot-Tab gruppiert nach Dataset wie Cockpit-Plugin

Snapshots werden nach Dataset gruppiert angezeigt (tank: 94, tank/share: 94)
mit aufklappbaren Zeilen statt flacher Liste.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-04 22:56:37 +02:00
parent d079d76151
commit 9cc9844f0b
+71 -40
View File
@@ -501,48 +501,79 @@ export default function DatasetsPage() {
<div className="text-center py-6 text-muted-foreground text-xs">Loading</div>
) : snaps.length === 0 ? (
<div className="text-center py-6 text-muted-foreground text-xs">No snapshots</div>
) : (
<div className="overflow-x-auto">
<table className="w-full text-xs">
<thead className="bg-muted">
<tr>
<th className="px-3 py-2 text-left font-medium">Name</th>
<th className="px-3 py-2 text-left font-medium">Created</th>
<th className="px-3 py-2 text-left font-medium">Used</th>
<th className="px-3 py-2 text-left font-medium">Referenced</th>
<th className="px-3 py-2 text-left font-medium">Clones</th>
<th className="px-3 py-2 text-right font-medium"></th>
</tr>
</thead>
<tbody>
{snaps.map((snap) => (
<tr key={snap.name} className="border-b border-border/50 hover:bg-muted/30">
<td className="px-3 py-2 font-mono">
<span className="text-muted-foreground">{snap.dataset}@</span>{snap.name.split("@")[1]}
</td>
<td className="px-3 py-2 text-muted-foreground">
{snap.creation_datetime ? new Date(snap.creation_datetime).toLocaleString() : "—"}
</td>
<td className="px-3 py-2">{formatBytes(snap.used)}</td>
<td className="px-3 py-2">{formatBytes(snap.referenced)}</td>
<td className="px-3 py-2 text-muted-foreground"></td>
<td className="px-3 py-2 text-right">
<button
className="p-1 rounded hover:bg-muted"
onClick={(e) => {
e.stopPropagation()
setSnapContextMenu({ snap, x: e.clientX, y: e.clientY })
) : (() => {
// Group snapshots by dataset
const groups = new Map<string, Snapshot[]>()
snaps.forEach((s) => {
const ds = s.dataset || s.name.split("@")[0]
if (!groups.has(ds)) groups.set(ds, [])
groups.get(ds)!.push(s)
})
const expandKey = `snapgroup-${pool.name}`
return (
<div className="overflow-x-auto">
<table className="w-full text-xs">
<thead className="bg-muted">
<tr>
<th className="px-3 py-2 text-left font-medium">Name</th>
<th className="px-3 py-2 text-left font-medium">Created</th>
<th className="px-3 py-2 text-left font-medium">Used</th>
<th className="px-3 py-2 text-left font-medium">Referenced</th>
<th className="px-3 py-2 text-left font-medium">Clones</th>
<th className="px-3 py-2 text-right font-medium"></th>
</tr>
</thead>
<tbody>
{Array.from(groups.entries()).map(([ds, dsSnaps]) => {
const isGroupExpanded = expandedDatasets.has(`${expandKey}-${ds}`)
return [
// Group header row
<tr
key={`group-${ds}`}
className="border-b border-border bg-muted/20 hover:bg-muted/40 cursor-pointer select-none"
onClick={() => {
const next = new Set(expandedDatasets)
if (isGroupExpanded) next.delete(`${expandKey}-${ds}`)
else next.add(`${expandKey}-${ds}`)
setExpandedDatasets(next)
}}
>
<MoreVertical className="w-3 h-3" />
</button>
</td>
</tr>
))}
</tbody>
</table>
</div>
)}
<td className="px-3 py-2 font-mono font-medium" colSpan={5}>
<div className="flex items-center gap-2">
{isGroupExpanded ? <ChevronDown className="w-3 h-3" /> : <ChevronRight className="w-3 h-3" />}
<span>{ds}</span>
<span className="text-muted-foreground font-normal">{dsSnaps.length}</span>
</div>
</td>
<td />
</tr>,
// Snapshot rows (expanded)
...(isGroupExpanded ? dsSnaps.map((snap) => (
<tr key={snap.name} className="border-b border-border/40 hover:bg-muted/30">
<td className="px-3 py-1.5 font-mono pl-8">{snap.name.split("@")[1]}</td>
<td className="px-3 py-1.5 text-muted-foreground">
{snap.creation_datetime ? new Date(snap.creation_datetime).toLocaleString() : "—"}
</td>
<td className="px-3 py-1.5">{formatBytes(snap.used)}</td>
<td className="px-3 py-1.5">{formatBytes(snap.referenced)}</td>
<td className="px-3 py-1.5 text-muted-foreground"></td>
<td className="px-3 py-1.5 text-right">
<button
className="p-1 rounded hover:bg-muted"
onClick={(e) => { e.stopPropagation(); setSnapContextMenu({ snap, x: e.clientX, y: e.clientY }) }}
>
<MoreVertical className="w-3 h-3" />
</button>
</td>
</tr>
)) : [])
]
})}
</tbody>
</table>
</div>
)
})()}
</div>
)
})()}