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> <div className="text-center py-6 text-muted-foreground text-xs">Loading</div>
) : snaps.length === 0 ? ( ) : snaps.length === 0 ? (
<div className="text-center py-6 text-muted-foreground text-xs">No snapshots</div> <div className="text-center py-6 text-muted-foreground text-xs">No snapshots</div>
) : ( ) : (() => {
<div className="overflow-x-auto"> // Group snapshots by dataset
<table className="w-full text-xs"> const groups = new Map<string, Snapshot[]>()
<thead className="bg-muted"> snaps.forEach((s) => {
<tr> const ds = s.dataset || s.name.split("@")[0]
<th className="px-3 py-2 text-left font-medium">Name</th> if (!groups.has(ds)) groups.set(ds, [])
<th className="px-3 py-2 text-left font-medium">Created</th> groups.get(ds)!.push(s)
<th className="px-3 py-2 text-left font-medium">Used</th> })
<th className="px-3 py-2 text-left font-medium">Referenced</th> const expandKey = `snapgroup-${pool.name}`
<th className="px-3 py-2 text-left font-medium">Clones</th> return (
<th className="px-3 py-2 text-right font-medium"></th> <div className="overflow-x-auto">
</tr> <table className="w-full text-xs">
</thead> <thead className="bg-muted">
<tbody> <tr>
{snaps.map((snap) => ( <th className="px-3 py-2 text-left font-medium">Name</th>
<tr key={snap.name} className="border-b border-border/50 hover:bg-muted/30"> <th className="px-3 py-2 text-left font-medium">Created</th>
<td className="px-3 py-2 font-mono"> <th className="px-3 py-2 text-left font-medium">Used</th>
<span className="text-muted-foreground">{snap.dataset}@</span>{snap.name.split("@")[1]} <th className="px-3 py-2 text-left font-medium">Referenced</th>
</td> <th className="px-3 py-2 text-left font-medium">Clones</th>
<td className="px-3 py-2 text-muted-foreground"> <th className="px-3 py-2 text-right font-medium"></th>
{snap.creation_datetime ? new Date(snap.creation_datetime).toLocaleString() : "—"} </tr>
</td> </thead>
<td className="px-3 py-2">{formatBytes(snap.used)}</td> <tbody>
<td className="px-3 py-2">{formatBytes(snap.referenced)}</td> {Array.from(groups.entries()).map(([ds, dsSnaps]) => {
<td className="px-3 py-2 text-muted-foreground"></td> const isGroupExpanded = expandedDatasets.has(`${expandKey}-${ds}`)
<td className="px-3 py-2 text-right"> return [
<button // Group header row
className="p-1 rounded hover:bg-muted" <tr
onClick={(e) => { key={`group-${ds}`}
e.stopPropagation() className="border-b border-border bg-muted/20 hover:bg-muted/40 cursor-pointer select-none"
setSnapContextMenu({ snap, x: e.clientX, y: e.clientY }) 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" /> <td className="px-3 py-2 font-mono font-medium" colSpan={5}>
</button> <div className="flex items-center gap-2">
</td> {isGroupExpanded ? <ChevronDown className="w-3 h-3" /> : <ChevronRight className="w-3 h-3" />}
</tr> <span>{ds}</span>
))} <span className="text-muted-foreground font-normal">{dsSnaps.length}</span>
</tbody> </div>
</table> </td>
</div> <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> </div>
) )
})()} })()}