proxmox(overview): render the guest totals the page model already computes

v5's dashboard closed with a 'N running | N degraded | N stopped' strip
(Dashboard.tsx); the v6 rebuild kept computing those counts in
ProxmoxPageSummary — tests and all — but nothing ever rendered them,
the same flows-but-never-renders failure mode as #1471's wearout field.
Add a degradedGuestCount to the summary and close the overview with a
compact totals strip using the status-chip vocabulary (running /
attention / stopped). Flagged by the Proxmox overview parity audit.
This commit is contained in:
rcourtman
2026-06-11 19:08:17 +01:00
parent 91f601317f
commit 5dfaf96cae
4 changed files with 67 additions and 0 deletions
@@ -1,5 +1,6 @@
import { useLocation } from '@solidjs/router';
import { Show, createMemo, createResource, type Accessor } from 'solid-js';
import { StatusDot } from '@/components/shared/StatusDot';
import StorageSurface from '@/components/Storage/Storage';
import { WorkloadsFilter } from '@/components/Workloads/WorkloadsFilter';
import { WorkloadsSurface } from '@/components/Workloads/WorkloadsSurface';
@@ -346,6 +347,31 @@ function ProxmoxOverview(props: ProxmoxOverviewProps) {
emptyStateTitle="No Proxmox workloads"
emptyStateDescription="Proxmox VMs and LXCs appear here when inventory is available."
/>
{/* v5 dashboard closed with a running/stopped totals strip; the counts
are platform-wide, matching the status-chip vocabulary above. */}
<Show when={props.model().summary.guestCount > 0}>
<div
class="flex items-center gap-2 rounded border border-border bg-surface-alt px-2 py-1 text-xs text-muted"
data-testid="proxmox-guest-totals"
>
<span class="flex items-center gap-1.5">
<StatusDot size="xs" variant="success" ariaHidden />
{props.model().summary.runningGuestCount} running
</span>
<Show when={props.model().summary.degradedGuestCount > 0}>
<span aria-hidden="true">|</span>
<span class="flex items-center gap-1.5">
<StatusDot size="xs" variant="warning" ariaHidden />
{props.model().summary.degradedGuestCount} attention
</span>
</Show>
<span aria-hidden="true">|</span>
<span class="flex items-center gap-1.5">
<StatusDot size="xs" variant="danger" ariaHidden />
{props.model().summary.stoppedGuestCount} stopped
</span>
</div>
</Show>
</div>
);
}
@@ -154,6 +154,41 @@ describe('ProxmoxPageSurface contract', () => {
);
});
it('renders the v5-style guest totals strip from the page summary', () => {
setResources([
makeResource({
id: 'agent:pve-1',
type: 'agent',
proxmox: { nodeName: 'pve-1', clusterName: 'homelab' },
}),
makeResource({
id: 'vm-100',
type: 'vm',
status: 'running',
proxmox: { nodeName: 'pve-1', vmid: 100 },
}),
makeResource({
id: 'vm-101',
type: 'vm',
status: 'degraded',
proxmox: { nodeName: 'pve-1', vmid: 101 },
}),
makeResource({
id: 'vm-102',
type: 'vm',
status: 'stopped',
proxmox: { nodeName: 'pve-1', vmid: 102 },
}),
]);
render(() => <ProxmoxPageSurface />);
const totals = screen.getByTestId('proxmox-guest-totals');
expect(totals).toHaveTextContent('1 running');
expect(totals).toHaveTextContent('1 attention');
expect(totals).toHaveTextContent('1 stopped');
});
it('does not surface stale-agent notices for development builds without an agent target', () => {
mockVersionInfo.mockReturnValue({
version: '6.0.0-rc.6+git.172.g2c360f779.dirty',
@@ -126,6 +126,8 @@ describe('proxmoxPageModel', () => {
nodeCount: 1,
guestCount: 1,
runningGuestCount: 1,
degradedGuestCount: 0,
stoppedGuestCount: 0,
storageCount: 1,
pbsCount: 1,
pmgCount: 1,
@@ -25,6 +25,7 @@ export type ProxmoxPageSummary = {
nodeCount: number;
guestCount: number;
runningGuestCount: number;
degradedGuestCount: number;
stoppedGuestCount: number;
storageCount: number;
pbsCount: number;
@@ -342,6 +343,9 @@ export function buildProxmoxPageModel(resources: Resource[]): ProxmoxPageModel {
runningGuestCount: guests.filter(
(resource) => resource.status === 'online' || resource.status === 'running',
).length,
degradedGuestCount: guests.filter(
(resource) => resource.status === 'degraded' || resource.status === 'warning',
).length,
stoppedGuestCount: guests.filter(
(resource) => resource.status === 'offline' || resource.status === 'stopped',
).length,