diff --git a/frontend-modern/src/features/proxmox/ProxmoxPageSurface.tsx b/frontend-modern/src/features/proxmox/ProxmoxPageSurface.tsx index 255f82c67a..11d25bc99b 100644 --- a/frontend-modern/src/features/proxmox/ProxmoxPageSurface.tsx +++ b/frontend-modern/src/features/proxmox/ProxmoxPageSurface.tsx @@ -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. */} + 0}> +
+ + + {props.model().summary.runningGuestCount} running + + 0}> + + + + {props.model().summary.degradedGuestCount} attention + + + + + + {props.model().summary.stoppedGuestCount} stopped + +
+
); } diff --git a/frontend-modern/src/features/proxmox/__tests__/ProxmoxPageSurface.contract.test.tsx b/frontend-modern/src/features/proxmox/__tests__/ProxmoxPageSurface.contract.test.tsx index d8d7f578af..abe29b247b 100644 --- a/frontend-modern/src/features/proxmox/__tests__/ProxmoxPageSurface.contract.test.tsx +++ b/frontend-modern/src/features/proxmox/__tests__/ProxmoxPageSurface.contract.test.tsx @@ -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(() => ); + + 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', diff --git a/frontend-modern/src/features/proxmox/__tests__/proxmoxPageModel.test.ts b/frontend-modern/src/features/proxmox/__tests__/proxmoxPageModel.test.ts index 86f76ec6f9..3795a117aa 100644 --- a/frontend-modern/src/features/proxmox/__tests__/proxmoxPageModel.test.ts +++ b/frontend-modern/src/features/proxmox/__tests__/proxmoxPageModel.test.ts @@ -126,6 +126,8 @@ describe('proxmoxPageModel', () => { nodeCount: 1, guestCount: 1, runningGuestCount: 1, + degradedGuestCount: 0, + stoppedGuestCount: 0, storageCount: 1, pbsCount: 1, pmgCount: 1, diff --git a/frontend-modern/src/features/proxmox/proxmoxPageModel.ts b/frontend-modern/src/features/proxmox/proxmoxPageModel.ts index 855d23f705..f18ad260cb 100644 --- a/frontend-modern/src/features/proxmox/proxmoxPageModel.ts +++ b/frontend-modern/src/features/proxmox/proxmoxPageModel.ts @@ -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,