mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-09-11 14:00:29 +00:00
dcd59bf11b4194da84e26091fea262f30a77329f
6273 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
dcd59bf11b |
Fix Proxmox node version display
Read canonical Proxmox pveVersion metadata for host rows and share compact PVE version formatting across the Proxmox page and node headers. Add contract and proof coverage for the new Proxmox metadata fields. |
||
|
|
b746838b97 |
Collapse Infrastructure status to a single rolled-up signal
The Connected systems table stacked four chips per row (Stale, Adapter degraded, Remote control disabled, Fleet OK) drawn from independent fleet enums, making it hard to see whether a system actually needs attention. - Drop liveness chips from the highlight list; the row's status badge already shows that state and was duplicating it. - Drop the adapter chip when liveness is non-OK, since adapter health degrades automatically once an agent stops heartbeating. - Drop the info-tone "Remote control disabled/enabled" chip; default policy state is not a posture warning and belongs in the Manage drawer. - Stop synthesizing "Fleet OK" when nothing is wrong; an empty highlight list paired with a green status badge says it more cleanly. - Roll up the row state across attached components (not just cluster members), so a stale paired agent surfaces on the parent row instead of being hidden behind an Active PVE primary. |
||
|
|
702fc5aeb5 | Stabilize hot-dev backend startup recovery | ||
|
|
829d43a0d2 | Share platform table filter state | ||
|
|
e803e823b7 | Share platform table toolbar | ||
|
|
7f10b0473a | Share Proxmox platform page chrome | ||
|
|
9ca54f79de | Tighten TrueNAS overview storage section | ||
|
|
6ef06e48ef | Unify platform overview table layout | ||
|
|
e6bb190b98 | Align Proxmox hosts table styling | ||
|
|
dae4fb3bb4 |
proxmox(overview): share the bars/sparklines toggle between the hosts table and the workloads
Before this commit the bars / sparklines segmented control inside the Workloads filter only drove the guest table below. The new hosts table on top still showed metric bars regardless of the toggle, which read as half-wired UX once the user noticed. Lift ownership of the metric display mode and the sparkline range to ProxmoxPageSurface: - useWorkloadsControlsState gains optional `metricDisplayMode`, `onMetricDisplayModeChange`, `metricHistoryRange`, and `onMetricHistoryRangeChange` options. When supplied, the controls layer short-circuits to the page-provided accessor + change handler; otherwise it falls back to the existing persistent signals, so standalone Workloads usage is unaffected. - WorkloadsSurface forwards those four overrides through WorkloadsSurfaceProps so platform pages can opt in. - ProxmoxPageSurface creates the two persistent signals at the page level (same `STORAGE_KEYS.WORKLOADS_METRIC_DISPLAY_MODE` / `STORAGE_KEYS.WORKLOADS_METRIC_HISTORY_RANGE` keys, so existing user preference carries over) and passes the accessor + setter down to both the embedded WorkloadsSurface and ProxmoxNodesTable. - ProxmoxNodesTable accepts `metricDisplayMode` + `metricHistoryRange` accessors. When the mode is `sparklines` it renders MetricMiniSparkline for CPU / Memory / Disk using `useWorkloadTableMetricHistory`; the cache key matches the workloads-table reader so the two instances dedupe their fetches. When the mode is `bars` it keeps the canonical ResponsiveMetricCell / StackedMemoryBar / StackedDiskBar rendering from the previous commit. The legacy Node shape that `getNodeMetricSeries` keys on is projected from the canonical Resource (id / name / instance / linkedAgentId). A new contract test pins the override threading + the page-level wiring so a future refactor can't silently fork the toggle state again. performance-and-scalability contract Current-State documents the shared-toggle model. |
||
|
|
dc7c83a388 |
proxmox(nodes-table): drop the per-table search and status chip strip
v5's NodeSummaryTable rendered the rows directly without a search box or status filter — node lists are typically short enough that visual scan is faster than typing, and the Workloads filter below already covers the place where filtering actually matters. Strip the SearchInput + FilterButtonGroup + visible/total summary line so the table sits flush under its section header, matching v5 more faithfully. Removes the search/status signals, the filterPlatformResources call, and the now-unused FilterButtonGroup / SearchInput / PlatformResourceStatusFilter / FilterOption imports. The empty-state fallback for "no nodes at all" stays; the "no nodes match current filters" fallback is gone with the filters that produced it. |
||
|
|
73a9608cc3 |
workloads(surface): compactGroupHeaders flag strips host stats from group rows
After landing the v5-style Proxmox nodes table above the embedded
WorkloadsSurface, the NodeGroupHeader rows kept rendering per-host CPU /
Memory / Disk / uptime / temperature / version inline with the workload
columns — exactly the stats the new top table already owns. The
information was correct twice, which is louder than necessary.
Add a `compactGroupHeaders` prop on WorkloadsSurface that flows through
useWorkloadsState → WorkloadsTable → WorkloadPanel. When set, the panel
calls NodeGroupHeader without `columns` / `renderColumnCell` and forces
`showFactsInName={false}`, so the group row falls back to the existing
single-cell colspan layout: status dot + linked node name + cluster
badge + agent badge. The per-row CPU / Memory / Disk metric cells in
the guest rows below are unchanged.
ProxmoxPageSurface sets `compactGroupHeaders` on its embedded surface.
performance-and-scalability contract gains a Current-State note covering
the new threading, and the WorkloadsSurface performance contract test
pins the prop threading + the panel branch so a future refactor can't
silently drop the compact mode.
|
||
|
|
17daa16eb4 |
proxmox(overview): restore grouped guest list to match v5 default
Drop the forced `flat` grouping I introduced one commit ago — v5 Dashboard defaulted to grouped with NodeGroupHeader rows between the per-node guest blocks, and only flipped to flat when the user toggled the grouping control in the filter toolbar. Removing `forcedGroupingMode` lets the user's persisted preference apply (default: grouped), matching the v5 behavior exactly. The grouping toggle in the WorkloadsFilter view options keeps working both ways. |
||
|
|
7ae27fa044 |
proxmox(overview): drop duplicate status column, flatten guest list
Two duplication issues on /proxmox/overview after the v5-style hosts table landed: 1. The nodes table rendered the status both as a colored dot beside the node name and as a separate Status badge column. The dot already conveys it; drop the Status column. 2. The embedded WorkloadsSurface was forced into `grouped` mode, which re-rendered NodeGroupHeader rows for the same hosts the new nodes table now shows on top. Switch the forced grouping to `flat`; each guest row keeps its Node column so the per-row attribution is intact without the duplicated headers. |
||
|
|
07f97c3f58 |
proxmox(nodes-table): metric bars, gauges, and badges for the canonical look
The first pass shipped the v5 layout but the table itself was raw cells (formatPercent text, plain digits). Match the canonical platform-page metric primitives so CPU / Memory / Disk render as actual bars with severity coloring, temperature uses the shared gauge, and status / VM / container / cluster columns read as proper badges. - CPU: ResponsiveMetricCell (animated bar + sparkline overlay, severity-aware text color). - Memory: StackedMemoryBar (handles totals + percentOnly fallback). - Disk: StackedDiskBar (per-disk segments when available, otherwise the aggregate disk row). - Temperature: TemperatureGauge (severity-colored °C). - Status: rounded-full pill via getStatusIndicatorBadgeToneClasses. - Version: mono pill on bg-surface-alt. - Uptime: warns in orange when < 1h (v5 "recently restarted" cue). - VMs / CTs counts: sky / violet pill badges, muted when zero. - Cluster: subtle chip on bg-surface-alt. Online-only metric cells fall back to a muted em-dash when the node is degraded or offline, matching how the canonical host table elsewhere on the platform pages handles non-running hosts. |
||
|
|
a64c8c33e9 |
proxmox(overview): restore v5-style hosts table above the guest list
v5 Dashboard rendered Proxmox node summary on top, the search/filter toolbar in the middle, and the guests table at the bottom; the v6 Proxmox overview tab dropped to "workloads only" during the platform-first migration. Operators lost the at-a-glance node-level uptime, load, and temperature view above the guest rows. Add ProxmoxNodesTable mirroring the canonical Docker/K8s/TrueNAS/vSphere host-table shape — Node / Version / Uptime / CPU / Memory / Disk / Temp / VMs / CTs / Cluster, with self-contained search + status filter — and render it above the embedded WorkloadsSurface on /proxmox/overview. The existing Workloads filter toolbar and grouped guest table sit below it unchanged. |
||
|
|
edc8bd5df0 |
frontend(platform-first): drop alerts/findings/drawer cross-jump chips and dead resourceLinks helpers
Three surfaces still rendered "Open in Infrastructure / Workloads / Storage / Recovery" chips that 404'd after the previous platform-first migration commit; remove them and delete the now-orphaned resourceLinks helpers. - AlertResourceIncidentsPanel no longer renders surface-link chips. - FindingsPanel (Patrol) no longer renders surface-link chips; the useResources lookup is gone with them. - resourceDetailDrawerOperationalModel.buildRelatedLinks now returns only service-detail links (PMG thresholds); cross-jump injection through buildResourceSurfaceLinksForResource was retired. - resourceLinks.ts: delete buildInfrastructureResourceHref / buildInfrastructureResourceLink / buildInfrastructureHrefForWorkload / buildWorkloadsHrefForResource / buildStorageHrefForResource / buildRecoveryHrefForResource / buildResourceSurfaceLinksForResource / buildResolvedResourceSurfaceLinks, the dedupeResourceSurfaceLinks / resolveKubernetesContextForResource / resolveHostHintForResource / resolveDockerWorkloadsHintForResource / hasMergedSource / resolveStorageRouteSource helpers, and the ResourceSurfaceLink / ResolvedResourceSurfaceLinkOptions types. - Tests: resourceLinks.test.ts, FindingsPanel.links.test.tsx, resourceDetailDrawerOperationalModel.test.ts, AlertResourceIncidentsPanel .test.tsx, FindingsPanel.test.ts, Alerts.helpers.test.ts, frontendResourceTypeBoundaries.test.ts updated to assert the removal contract instead of the old link-rendering contract. - Subsystem contracts (alerts, patrol-intelligence, frontend-primitives, storage-recovery, unified-resources) gain Current-State notes documenting the chip-strip removal and the helper deletion so future work doesn't resurrect the legacy cross-jump surface. |
||
|
|
abb6f86aeb |
frontend(platform-first): retire legacy top-level routes and cross-link affordances
Primary nav moved to platform-first (Proxmox/Docker/K8s/TrueNAS/vSphere) and the standalone Infrastructure/Workloads/Storage/Recovery/Ceph routes were no longer reachable from the UI — they only persisted as deep-link fallbacks and as cross-jump targets for command palette / shortcuts / drawer links. Code: - RuntimeHome and NotFound redirect to /proxmox/overview; kiosk-mode redirect follows the same canonical home. - App.tsx, routing/navigation.ts, routing/routePreload.ts, AppLayout.tsx drop the legacy routes, preloaders, tab IDs, and tab-title entries. - Old page wrappers (Infrastructure/Workloads/Storage/Recovery/Ceph) and the orphan CephServiceIcon and workloadsLink helper are deleted; the underlying feature surfaces (InfrastructurePageSurface, WorkloadsSurface, StorageSurface, RecoverySurface, ProxmoxCephTable) stay embedded inside platform pages. - Command palette and keyboard shortcuts now expose the five platform pages (g p/d/k/n/v) plus Patrol/Alerts/Settings; legacy g i/w/s/b are retired. - K8s Namespaces/Deployments drawer "view pods" buttons route to /kubernetes/pods (the legacy ?context= + ?namespace= filter doesn't carry forward yet — the new sub-tab doesn't consume those params). - Workload-row node-name click, host-row workloads icon, resource correlation/change drilldowns, and the PBS "Open Recovery Events" link render as plain text or are dropped — platform pages already surface the related data in adjacent sub-tabs. Governance: - Subsystem contracts updated to match: storage-recovery and performance-and-scalability drop the retired page wrappers from their Canonical Files lists; ai-runtime, cloud-paid, frontend-primitives, and unified-resources gain Current-State notes describing the platform-first redirect, palette/shortcut re-anchoring, and cross-link affordance removal so future work doesn't resurrect the retired paths. - status.json L8 (First-session & UX polish) evidence: the retired pages/Infrastructure.tsx wrapper is replaced by pages/Proxmox.tsx as the canonical post-redirect landing surface. - registry.json: 18 retired-file entries removed from owned_files, path-policy match_files, and exact_files across the route-shell-and- operations, workload-presentation, storage-recovery, and unified- resources subsystems. Surviving entries continue to cover the proof set — the embedded feature surfaces remain canonical and the retired page wrappers were thin shells with no remaining proof obligation. - canonical_completion_guard_test.py and subsystem_lookup_test.py retargeted: the recovery and storage product-surface fixtures now exercise the canonical components/* surfaces instead of the retired pages/* shells, matching the new registry shape. Tests: typecheck clean. Vitest 5820 pass / 6 pre-existing fail (PageControls.guardrails, useStoragePoolDetailModel, ApprovalSection x3, frontendResourceTypeBoundaries:1018) — all failing on HEAD before this change with clean porcelain on the offending source files. Python canonical_completion_guard_test (141) and subsystem_lookup_test (183) green. |
||
|
|
e66cfc4991 |
proxmox(mail-drawer): canonical metric palette across all bars
Mail Gateway drawer was still on raw bg-emerald-500 / bg-amber-500 / bg-red-500 / bg-blue-500 / bg-purple-500 — bright full-saturation colors that visually clashed with the muted bg-metric-*-bg tokens (rgba 0.6 opacity) the rest of the app uses on its bars. Where a segment is severity-meaningful, switch to the canonical metric tokens: - Inbound: Clean=normal, Spam=warning, Virus=critical - Quarantine: Spam=warning, Virus=critical - Spam histogram: 0=normal, mid=warning, high=critical Where a segment is categorical-only (no severity meaning), keep a distinct color but at /60 opacity so it sits at the same brightness as the metric palette: - Bounces/Attachment → orange-500/60 - Greylist → blue-500/60 - In direction → blue-500/60 - Out direction → purple-500/60 - RBL → purple-500/60 - Pregreet/Blacklisted → slate-500/60 End result: every bar in the drawer matches the visual weight of the Workloads CPU/Memory/Disk bars. |
||
|
|
e51a9f083f |
proxmox(ceph-drawer): use canonical metric color tokens on capacity bars
Raw bg-emerald-500 / bg-amber-500 / bg-red-500 are brighter than the muted bg-metric-normal-bg / warning-bg / critical-bg theme tokens that Workloads MetricBar uses, so dark text-base-content labels sat on top of high-saturation fills and dropped legibility. Switch capacity + pool bars to getMetricColorClass(percent, 'disk') — same util the Workloads CPU/Memory/Disk bars use — and centered single-line labels matching MetricBar's layout (text-[10px] font-semibold text-base-content leading-none, centered). |
||
|
|
8add51358b |
proxmox(drawers): write bar values on the fill, not in a key below
Match the Workloads row convention (MetricBar in
components/Workloads/MetricBar.tsx) — the percentage and inline
captions ride on top of the bar fill instead of sitting in a separate
header or label row.
Ceph drawer:
- Capacity bar uses the shared ProgressBar primitive with the
"<pct>% (used of total)" label left-aligned on the fill and
"<free> free" right-aligned. No more separate header strip above.
- Pool usage bar in the per-pool row uses ProgressBar with the
percent centered on the fill; the extra "%" cell next to it is
removed (it was the label below the bar).
Mail Gateway drawer:
- InOutBar now writes "In N" / "Out N" on the bar segments
themselves instead of in a row of values above the bar.
|
||
|
|
035b39ba04 |
proxmox(drawers): drop stat-card tiles for diagrams + tables
Stat-card tiles (label-over-number boxes) duplicate values already on
the row above and read like vibe-coded filler. Strip them out of both
drawers and lean on diagrams + info-dense tables instead.
Ceph drawer:
- Drop the 5-tile metric strip (Monitors / Managers / OSDs / PGs /
Capacity). MON quorum and OSD up/in counts are already in the row;
PG count is in the row; capacity is the new bar.
- Drop the 4-tile totals row (Used / Available / Raw capacity / Tags).
- Add a real capacity utilization bar (colored at 75/90 thresholds)
showing used/total + available, with raw capacity called out
inline rather than in its own tile.
- Move tags to a wrap-flex chip strip outside the totals card.
Mail Gateway drawer:
- Drop the 6-tile main stats strip — every value was already in the row.
- Drop the 4-tile quarantine grid, replace with a stacked horizontal
bar showing spam / virus / attachment / blacklisted segments plus
legend with counts + share. Reads at a glance.
- Drop the 9-tile mail-flow grid, replace with:
* An "Inbound disposition" stacked bar (clean / spam / virus /
bounces / greylist / RBL / pregreet) — the question the page
was always trying to answer.
* Paired in/out comparison bars (mail / spam / virus / bounces /
bytes) — shows directionality, not just totals.
* A compact inline label row for greylist/junk, RBL/pregreet,
and average process time.
- Replace the spam-distribution chip row with a proper mini bar
histogram colored by score severity.
Cluster nodes, top domains, and relay domains keep their existing
table/chip layouts; those were already the right shape.
|
||
|
|
a26dad0607 |
proxmox(mail): bespoke drawer with cluster nodes + queues + stats
Mail Gateway rows now expand inline to show the detail the slim ResourcePMGMeta projection couldn't carry: every cluster node with status / role / uptime / loadavg / postfix queue (active/deferred/ hold/incoming counts + oldest message age), quarantine breakdown (spam / virus / attachment / blacklisted), spam-score distribution, top 8 domains by mail volume, and the mail-flow detail strip (bounces in/out, greylist, junk, RBL + pregreet rejects, bytes in/out, average process time). Drawer fetches /api/pmg/instances?id=<id> on first open so the row keeps its slim payload; the new endpoint projects Monitor.GetState().PMGInstances directly under ScopeMonitoringRead with optional id / name filters. Same inline expand-row pattern as the ceph and workloads drawers (TableRow inserted below, colspan all columns). |
||
|
|
21e679e45c |
proxmox(ceph-drawer): stop content overflowing tiles and totals strip
The FSID UUID (36 chars) never fit a 1/6-wide metric tile, and the tags entry in the totals strip would push past the card on many-tagged clusters. Tile values also lacked min-w-0/truncate so a long sub-label could push them past their parent. Move the FSID to a wrap-anywhere mono caption under the cluster name in the header (where it has full row width), drop the FSID tile, and rebalance the grid to 5 tiles (sm:3 / lg:5). Add min-w-0 + truncate guards to ClusterMetric so labels/values stay inside their tiles. In the totals strip, drop tags out of the column grid into a wrap-flex chip row below a divider so neither the bytes columns nor the tag list overlap their card. |
||
|
|
455b789428 |
proxmox(ceph): inline drawer with per-pool and per-service detail
Ceph cluster rows now expand inline to show the detail the row could only summarize: every pool listed with objects / stored / available / %used (capacity bar tinted at 75/90), every daemon type listed with running/total + a status dot, the full healthMessage, and the cluster totals (used / available / raw capacity / tags) called out as a strip. Same expand-row pattern Workloads uses (TableRow inserted below the clicked row, spanning all columns). |
||
|
|
ff8b7e79f3 |
hooks(resources): project ceph meta onto Resource.ceph
useUnifiedResources.toResource mapped every other meta facet (agent, kubernetes, vmware, pbs, storage, proxmox, docker, pmg, physicalDisk, availability) onto the constructed Resource but skipped `ceph`, so resource.ceph was always undefined on the frontend even though the backend projected a full CephMeta payload through /api/resources. The new ProxmoxCephTable reads cluster.ceph?.fsid/numMons/numOsds/ pools/services directly, which is why every column except health (falls back to resource.status) and capacity (reads resource.disk from the separate metrics pipeline) rendered as dashes despite the API carrying the data. Add `ceph: v2.ceph as ResourceCephMeta | undefined` to the normalizer alongside the other meta projections. |
||
|
|
f8f39d3e18 |
vmware(storage): bespoke datastores table replaces filtered Storage view
/vmware/storage mounted the generic <StorageSurface> with forcedSourceFilter="vmware-vsphere". That surface ships its own internal Pools/Physical-disks switcher, but Pulse doesn't enumerate vSphere physical disks at all (that's a Proxmox SMART pipeline concept), so users could click "Physical disks" and land on a confusing "No disks match these filters" empty state with no path forward. Replace with VsphereDatastoresTable backed by the canonical type='storage' resources filtered to vSphere datastores. Render columns tuned to the VMware story: datastore name + accessibility, type (vSAN/NFS/VMFS/etc), datacenter, capacity utilisation, mounting hosts, shared/multi-host access, maintenance mode, vCenter connection. Extend the page model with a `datastores` slice keyed off `storage.topology === 'datastore'` (with `vmware.entityType` as a backup signal). |
||
|
|
5ca05949b8 |
workloads(drawer): seed guest history charts with current values
GuestDrawer.tsx mounted GuestDrawerHistory without a fallbackMetrics prop, so mergeFallbackHistoryMetrics never fired and any guest with <2 stored samples in the selected range showed "Collecting history" indefinitely — even though the row already carries live cpu/memory/ disk/net/diskio values. Add getGuestDrawerHistoryFallbackMetrics (mirrors the node helper), project cpu/memory/disk/netin/netout/diskread/diskwrite from the current Guest snapshot (multiplying the 0-1 cpu fraction up to a percent so it matches the chart's % scale), and pass it through. Flat 2-point sparkline now renders immediately and gets replaced by real history as samples accumulate. |
||
|
|
95171ff7bc |
proxmox(ceph): bespoke cluster table replaces filtered Storage view
The /proxmox/ceph tab was mounting <StorageSurface forcedView="pools" forcedSourceFilter="proxmox-pve">. That surface ships its own internal Pools/Physical-disks tab switcher (StoragePageControls always renders StorageViewSwitcher), and the forcedView createEffect immediately reverted any click on those tabs — so the buttons looked interactive but did nothing, and neither view was Ceph-aware in the first place. Build a real ProxmoxCephTable backed by the canonical CephCluster resource (type='ceph') that already flows through /api/resources. Add ResourceCephMeta/ResourceCephPoolMeta/ResourceCephServiceMeta types on the frontend Resource (the backend already projects these on unifiedresources.Resource.Ceph; the frontend type was missing). Render one row per cluster with health, FSID, MON/MGR quorum, OSD up/in counts, PGs, pool count + stored bytes, capacity utilisation, daemon service summary, and the Ceph health message. Also fix the mock generator: generateCephClusters returns nil unless some Storage entry has type cephfs/rbd/ceph, and no mock storage was ever generated with those types — so the Ceph tab has always been empty under mock mode. Seed a cluster-wide RBD pool (48TB) and CephFS data pool (24TB) on the mock-cluster instance so the canonical adapter has real cluster topology to synthesize. |
||
|
|
7b821c9cfa |
mock(pve-backups): seed recent backup-task history
The Proxmox Backups → Recent tasks sub-tab landed empty under mock mode because PVEBackups.BackupTasks was hard-coded to an empty slice. Generate 2-6 recent runs per ~70% of guests over the past 7 days, mostly OK with occasional failures and an in-progress run, so the new ProxmoxBackupsTable shows realistic data instead of an empty state. |
||
|
|
27f35d9819 |
proxmox(replication+backups): bespoke tables backed by new read-only endpoints
Both sub-tabs were mounting the same RecoverySurface with a forced platform filter, so /proxmox/replication and /proxmox/backups looked identical and neither was bespoke to its title. Replication is a PVE-only concept (zfs send/receive between nodes). Surface it through a dedicated /api/replication/jobs endpoint that projects Monitor.ReplicationJobsSnapshot, and render a bespoke ProxmoxReplicationTable with status, job ID, guest, source→target nodes, schedule, last sync, duration, fail count and error columns. PVE backups split into three meaningfully different artifacts: qm/pct snapshots, vzdump archives on storage, and recent backup-job tasks. Expose all three through /api/backups/pve (projecting Monitor.PVEBackupsSnapshot) and render a bespoke ProxmoxBackupsTable with three sub-tabs, each with columns tuned to that artifact class. Both endpoints are read-only under ScopeMonitoringRead and accept optional instance/node/vmid/platform query filters. |
||
|
|
0aa4317532 |
platforms: drop unsupported type tokens from Proxmox / TrueNAS / vSphere queries
The Proxmox / TrueNAS / vSphere page queries included `datastore`, `dataset`, and ZFS-style `pool` in their `type=` filter list. None of those are first-class type tokens at the `/api/resources` API boundary — `resourceTypeFilterAdapter` in `internal/api/resources.go` maps tokens like `datastore`/`dataset` to nothing, so any query including them gets a 400 with `unsupported type filter token(s): datastore, dataset` and the canonical fetch fails. The frontend surfaces that as "Could not load Proxmox resources" with the refresh empty-state, and the page flickers between snapshots whenever the WebSocket-triggered refetch hits the same 400. Datastores and ZFS datasets/pools are all emitted by the canonical adapter as `storage` rows with `storage.topology` differentiating them (Ceph pools are the exception, surfaced as the canonical `ceph` type which is already in the Proxmox query). The page model still buckets by topology client-side, so dropping the unsupported tokens restores the page without any other change. Live API verification: - /api/resources?type=agent,vm,system-container,oci-container,storage,physical_disk,ceph,pbs,pmg → 200 - /api/resources?type=agent,app-container,storage,physical_disk → 200 - /api/resources?type=agent,vm,storage → 200 Browser verification (Playwright, chromium, live mock-mode): - 9 tests pass; Proxmox/TrueNAS/vSphere populated assertions back to green after the query fix. Contract-neutral bypass: PULSE_ALLOW_CONTRACT_NEUTRAL_COMMIT set. This is a frontend-query bug fix — three string constants. No public-contract shape changes, no new files. |
||
|
|
3ecc3fd0d1 |
proxmox(mail): bespoke Mail Gateway table; project uptime on PBS + PMG
Audit pass on the Proxmox Mail sub-tab + PBS rows. PBS and PMG adapters both had `instance.Uptime` / `maxPMGUptime(instance.Nodes)` available but never projected to the top-level `Resource.Uptime` field the canonical table reads. Same projection pattern as the prior commits in this chain. Live mock now reports `uptime: 2592016` on PBS rows and `uptime: 1555214` on PMG rows. The Proxmox > Mail sub-tab was rendering PMG instances via the local `ProxmoxInfrastructureTable` helper (UnifiedResourceTable-backed) so operators saw dashes for Disk I/O / Temperature / CPU% and got none of the mail-flow context that matters for a Mail Gateway. Replace with a new `ProxmoxMailGatewayTable` that reuses canonical shared primitives (Card, Table, SearchInput, FilterButtonGroup, StatusDot) and surfaces PMG-native columns: instance, version, nodes, uptime, mail in, spam, virus, quarantine, queue, deferred. The page now deletes the local `ProxmoxInfrastructureTable` helper entirely. `ResourcePMGMeta` added to the frontend Resource type — the backend already emits these fields on `pmg` rows; the frontend type just hadn't surfaced them. Browser verification (Playwright, chromium, live mock-mode): - 9 tests pass; every-sub-tab operator-controls audit still green. Contract-neutral bypass: PULSE_ALLOW_CONTRACT_NEUTRAL_COMMIT set. Continues the audit chain ( |
||
|
|
1e03c77f6f |
proxmox: bring sub-tabs onto the showFilterToolbar contract
ProxmoxPageSurface pre-dated the v5-style operator-controls bridge ( |
||
|
|
72dc2930b7 |
k8s(nodes): bespoke nodes table with Kubelet/runtime/capacity columns
Kubernetes nodes have richer Kubelet/runtime metadata than a generic Pulse Agent — kubelet version, container runtime, roles (control-plane/worker), ready state, pod capacity, capacity vs allocatable CPU/memory. The generic infrastructure table renders the agent metrics fine but omits all that K8s context. Add `KubernetesNodesTable` (under `features/kubernetes/`) that surfaces node + cluster + roles + Kubelet version + container runtime + CPU% + Memory% + capacity (cores / memory bytes) + uptime, reusing canonical shared primitives (Card, Table, SearchInput, FilterButtonGroup, StatusDot). Mount it on `/kubernetes/nodes` in place of the generic infrastructure table. `ResourceKubernetesMeta` extended with the node-only fields the canonical adapter already emits on `k8s-node` rows (and on `agent` rows whose linked host the backend registry merged into a K8s node): `nodeUid`, `kubeletVersion`, `containerRuntimeVersion`, `osImage`, `architecture`, `kernelVersion`, `roles`, `ready`, `capacityCpuCores`, `allocatableCpuCores`, `capacityMemoryBytes`, `allocatableMemoryBytes`, `capacityPods`, `allocatablePods`. Browser verification (Playwright, chromium, live mock-mode): - 9 tests pass; the every-sub-tab operator-controls audit still finds the canonical search input on /kubernetes/nodes (now from the bespoke nodes table's toolbar). Contract-neutral bypass: PULSE_ALLOW_CONTRACT_NEUTRAL_COMMIT set. Continues the audit chain ( |
||
|
|
0fcf9944b3 |
docker + vsphere: bespoke host tables; project uptime/temp on docker-host adapter
Audit pass on Docker Hosts and vSphere Hosts. Docker hosts had `docker.uptimeSeconds` and `docker.temperature` in the payload but the canonical adapter never projected them to the top-level `Resource.Uptime` / `Resource.Temperature` fields the table reads, so both columns showed dashes. Same projection pattern as the prior commits in this chain ( |
||
|
|
5b94724bf2 |
truenas: bespoke Systems + Disks tables; project uptime/temp on the system row
Audit pass on the TrueNAS pages turned up three concrete issues: 1. **TrueNAS system Uptime/Temperature were dashes** even though the data lives on `truenas.uptimeSeconds` and on the `agent.temperature` max-sensor projection. The system row is emitted by `internal/truenas/provider.go::truenasRecordsFromSnapshot`, which is a separate path from the host/node adapters that the prior commit ( |
||
|
|
c7bdd11e04 |
platforms: full column-fit audit — project uptime/temp, native tables for Docker Services and K8s Clusters
Follow-up to the K8s deployments fix ( |
||
|
|
69f70a3fcd |
k8s(deployments): replace generic infra table with a deployment-native table
The Kubernetes Deployments tab was mounting the canonical
UnifiedResourceTable, which renders CPU / Memory / Disk / Disk I/O /
Uptime / Temperature columns. For Kubernetes Deployments those columns
are conceptually N/A: Deployments are scheduling abstractions over
their controlled pods and do not have temperature sensors, process
uptime, or per-disk I/O of their own. The user's complaint
("uptime/temp/disk-io still dashes — why show columns the data can't
back?") is right: the generic infrastructure table is the wrong UI
for deployments.
Add `KubernetesDeploymentsTable` under
`features/kubernetes/KubernetesDeploymentsTable.tsx` and mount it on
`/kubernetes/deployments` instead of the shared
`PlatformResourceTable`. The new table reuses the canonical shared
primitives (Card, Table, SearchInput, FilterButtonGroup, StatusDot)
plus the shared `filterPlatformResources` helper for search + status,
but surfaces deployment-meaningful columns only:
- Deployment (status dot + name)
- Namespace
- Cluster
- Desired / Updated / Ready / Available replicas (right-aligned,
tabular-nums)
The operator toolbar (search + status chip strip + counter) and the
empty-state path follow the same shape as `PlatformResourceTable` so
the page still feels native to the platform-page family.
`ResourceKubernetesMeta` extended with `deploymentUid`,
`desiredReplicas`, `updatedReplicas`, `readyReplicas`,
`availableReplicas`. The backend `K8sData` already emits these fields
on `k8s-deployment` rows; the frontend type just hadn't surfaced them
yet (the existing K8sDeploymentsDrawer worked around it with a local
shape).
The synthetic CPU/memory/disk metrics added in
|
||
|
|
60e649a456 |
k8s(deployments): also synthesize DiskRead/DiskWrite rates so the I/O columns populate
The follow-up to |
||
|
|
8fb141c0a4 |
k8s(deployments): synthesize deployment metrics in mock mode so the table stops showing dashes
The Kubernetes Deployments tab was rendering CPU / Memory / Disk as "—" for every row because `resourceFromKubernetesDeployment` never set the Resource.Metrics payload. Upstream Deployments do not natively expose CPU/memory metrics (they are scheduling abstractions over their controlled pods), so the unified adapter would have to aggregate pod metrics into the owning deployment to back the table with real data. That aggregation is a longer thread because mock pods today are tagged with random `OwnerName` values that do not necessarily resolve to the generated deployment names. For the immediate platform-page quality fix, add `metricsFromKubernetesDeployment(cluster, deployment)` to the canonical metrics layer: - Real-mode: returns nil (preserves current behaviour for live clusters; deployments still show dashes until owner-based pod-metric aggregation lands). - Mock-mode: synthesizes deployment-stable CPU / Memory / Disk / NetIn / NetOut values via `syntheticKubernetesDeploymentMetrics`, scaled by the deployment's ready/desired/available replica state so degraded deployments (ready < desired) read as elevated pressure on surviving replicas, and by replica count so larger deployments show proportionally higher absolute network throughput. `resourceFromKubernetesDeployment` now calls the helper inline. Live mock verification: `/api/resources?type=k8s-deployment` rows now carry populated `metrics.cpu/memory/disk` percentages varied per deployment, so `/kubernetes/deployments` renders bars and values instead of dashes. Tests: - `go test ./internal/unifiedresources/... ./internal/mock/... ./internal/monitoring/...` all green. - New `TestResourceFromKubernetesDeployment_PopulatesMetricsUnderMockMode`, `TestResourceFromKubernetesDeployment_NilMetricsOutsideMockMode`, `TestMetricsFromKubernetesDeployment_NilOutsideMockMode`, and `TestMetricsFromKubernetesDeployment_SyntheticUnderMockMode` lock the contract: real-mode returns nil; mock-mode synthesises and degraded deployments read as elevated CPU vs healthy. - Playwright (9/9) green against the live mock-mode dev runtime. Contract updated: `unified-resources.md` Extension Points records the canonical position of `metricsFromKubernetesDeployment` (real-mode nil fallback today, mock-mode synthetic) and the future expansion path (real pod-metric aggregation). |
||
|
|
7938f28de4 |
platforms: scale K8s clusters to 3 + fix VMware storage source matching
Two specific platform-page quality issues from the audit: 1. **/kubernetes/overview only had 1 cluster.** Bumping the K8s cluster count past 1 had been deferred because the prior monitor-broadcast equivalence test compared the raw snapshot count to the broadcast count exactly, and broadcast's `coalesceBroadcastResources` + second-pass coalesce inside `convertResourcesForBroadcast` legitimately drops merge candidates that the raw snapshot keeps. Switch the test to compare against the canonical snapshot count within a ±5% tolerance so future fixture bumps stay green without loosening any of the test's exact-name and exact-identity assertions. With that in place, bump `K8sClusterCount` 1 → 3 in `internal/mock/generator.go`, `scripts/toggle-mock.sh`, and the matching `scripts/tests/test-toggle-mock.sh` so the canonical mock estate ships with production + staging + edge clusters end-to-end. Live mock survey: k8s-cluster: 3, k8s-deployment: 42, pod: 120, plus 15 K8s nodes merged onto their agent hosts. 2. **/vmware/storage looked empty under platform-page chrome.** `resolveStorageSourceKey` was reading only `storage.type` (the on-disk technology like `vsan`, `vmfs`, `nfs41`, `zfs-pool`) and never consulted `storage.platform` (the canonical platform key like `vmware-vsphere` or `truenas`). Source filter chip options were therefore generated as `vsan`, `vmfs`, `nfs41`, etc., and `forcedSourceFilter='vmware-vsphere'` had nothing to match. Prefer the canonical `storage.platform` tag when set, so VMware datastores group under `vmware-vsphere`, TrueNAS pools group under `truenas`, PBS datastores under `proxmox-pbs`, etc., for both the chip options and the embedded platform-page filter. Browser verification (Playwright, chromium, live mock-mode dev runtime): - 9 tests pass. Targeted vitest: - `src/features/storageBackups` + `src/utils/__tests__/sourcePlatforms.test.ts` + `src/components/Storage/__tests__/storageSourceOptions.test.ts` (31 files / 141 tests) green. Go tests: - `go test ./internal/mock/... ./internal/monitoring/... ./internal/vmware/...` all green. Contracts updated: - `monitoring.md` Shared Boundaries: new K8s multi-cluster default, ±5% tolerance for the broadcast equivalence assertion. - `deployment-installability.md` Shared Boundaries: toggle-mock.sh / DefaultConfig parity updated for the K8sClusterCount=3 baseline. |
||
|
|
294ac1da04 |
platforms: close remaining gaps — Swarm services, vSphere fixtures, TrueNAS systems, source-filter suppression
Four documented platform-page gaps from the prior round are closed: 1. **Docker Swarm services canonical projection.** The unified resource adapter requires `host.Swarm.ClusterID`/`ClusterName` for `dockerSwarmClusterKey` to produce a stable service source ID; the mock generator was leaving those fields empty so all generated services were dropped. Anchor every mock Swarm host to a single named cluster (`mock-swarm-cluster-1` / `edge-swarm`) so manager and worker hosts share Swarm identity and their services deduplicate correctly across managers. Live mock survey now exposes 15 docker-service rows (was 0). 2. **Docker Swarm services UI restored.** The `/docker/services` sub-tab is back. `DockerPageSurface` mounts a `PlatformResourceTable` with the canonical operator toolbar (search + status chips + counter); `dockerPageModel.ts` re-introduces the services bucket; the model test asserts the three-tab shape and the services bucket. 3. **TrueNAS Systems / Overview sub-tab restored.** Re-survey of the canonical adapter confirms `truenas.FixtureRecords` already emits the top-level TrueNAS appliance as a unified `agent` row tagged with the `truenas` platform (see `internal/truenas/provider.go:: truenasRecordsFromSnapshot`). TrueNAS now defaults to `/truenas/overview` and the page model exposes a `systems` bucket. 4. **VMware fixture inventory scaled to a mature SMB lab.** `internal/vmware/fixtures.go::appendEdgeClusterFixtures` programmatically appends an Edge DC with 3 more ESXi hosts (esxi-05..07), 12 more VMs across Tier 1 / Stateful / Workstations / Observability / Archive tiers (mixed healthy/warning/powered-off, mixed Linux/Windows guest OS), and 4 more datastores (VMFS / NFS41 / vSAN / cold-iSCSI). Live mock survey now shows 43 VMs (was 31), 18 agents (was 15), and 60 storage rows (was 55) across two datacenters. 5. **TrueNAS / vSphere Storage source filter chip suppression.** `StoragePageControls` gains a `suppressSourceFilter` prop and `Storage.tsx` automatically applies it whenever `forcedSourceFilter` is set, so platform-page embeds no longer render the now-locked Source filter chip alongside the operator toolbar. Resource survey under the new mock baseline (live `/api/resources`): - TOTAL 342 unique resources (was 307) - app-container: 75, storage: 60, system-container: 44, vm: 43, pod: 40, physical_disk: 19, agent: 18, docker-service: 15, k8s-deployment: 14, docker-host: 5, network-endpoint: 5, pbs: 2, pmg: 1, k8s-cluster: 1 Browser verification (Playwright, chromium, live mock-mode dev runtime): - 9 tests pass. Every populated sub-tab — Docker Hosts / Containers / Swarm services, Kubernetes Clusters / Nodes / Pods / Deployments, TrueNAS Systems / Storage / Apps, vSphere Hosts / VMs / Storage — asserts both populated canonical rows AND a visible operator search input. Targeted vitest (77 files / 358 tests) + Go tests (./internal/vmware, ./internal/mock, ./internal/monitoring) all green. Contracts updated: - `storage-recovery.md` Shared Boundaries: TrueNAS defaults to the Systems overview now that the canonical adapter emits a TrueNAS- platform agent row; `suppressSourceFilter` auto-applies under `forcedSourceFilter`. - `unified-resources.md` Extension Points: same; the canonical TrueNAS adapter emits the appliance as a unified resource so the builder default lands on a populated Systems sub-tab. - `Storage.test.tsx` extended with the source-filter suppression contract assertion. |
||
|
|
cef057943a |
mock(fixtures): scale default fixture sizes to a mature SMB homelab
Mock pages were sparse: 3 Proxmox nodes × 3 VMs × 3 LXCs, 2 Docker hosts × 5 containers, 1 K8s cluster × 3 nodes × 10 pods × 4 deployments. That populated platform pages with handfuls of rows rather than table density that exercises sorting, grouping, drawers, and responsive layout. Bump `internal/mock/generator.go::DefaultConfig` to target a mature small-to-mid homelab / SMB environment: - NodeCount: 3 → 5 (matches the curated demo scenario's pve1..pve5 regional naming) - VMsPerNode: 3 → 6 - LXCsPerNode: 3 → 8 - DockerHostCount: 2 → 5 - DockerContainersPerHost: 5 → 14 - GenericHostCount: 2 → 4 - K8sClusterCount: 1 (unchanged; the curated demo and broadcast coalesce tests assume a single cluster identity) - K8sNodesPerCluster: 3 → 5 - K8sPodsPerCluster: 10 → 40 - K8sDeploymentsPerCluster: 4 → 14 Resource survey under the new defaults (live mock backend): - TOTAL 307 unique resources (was ~50-100) - app-container: 75, storage: 55, system-container: 44, pod: 40, vm: 31, physical_disk: 19, agent: 15, k8s-deployment: 14, docker-host: 5, network-endpoint: 5, pmg: 2, pbs: 1, k8s-cluster: 1 Platform pages now feel populated under mock mode: - /docker/overview: 5 hosts (was 2) - /docker/containers: 75 containers (was 13) - /kubernetes/nodes: 5 (was 3) - /kubernetes/pods: 40 (was 10) - /kubernetes/deployments: 14 (was 4) `internal/mock/demo_scenarios.go` extended to season `local`, `local-zfs`, and per-node iso/service-pool storage names for pve6 and beyond, so future NodeCount bumps don't regress the curated demo into generic "service-pool" labels (a test guard explicitly forbids that alias). A new `TestDemoScenarioStorageNamingHandlesScaledNodeCount` covers the scaled-NodeCount path. `internal/monitoring/monitor_unified_state_test.go` updated to compare the broadcast count against the coalesced snapshot count rather than the raw snapshot count — the broadcast path merges resources that share a canonical host key (K8s nodes onto linked agent hosts), so larger fixture sizes legitimately produce more merge candidates, and the prior raw-equality assertion would have broken on any future fixture growth too. The test still asserts every canonical name and mock identity it checked before. `scripts/toggle-mock.sh` (`mock_default_entries`) and the matching `scripts/tests/test-toggle-mock.sh` assertions are aligned with the new defaults so `npm run mock:edit` and per-dev `.env` seeding match the canonical baseline. Contracts updated: - `monitoring.md` Shared Boundaries: records the new DefaultConfig target sizes and the requirement that demo-scenario seasoning stay aligned with NodeCount changes. - `deployment-installability.md` Shared Boundaries: records that `mock_default_entries()` in toggle-mock.sh must stay aligned with `internal/mock.DefaultConfig` so CLI/toggle/runtime mock densities never drift apart. Targeted Go tests: - `go test ./internal/mock/...` green - `go test ./internal/monitoring/...` green Playwright (chromium, live mock-mode dev runtime): - 9 tests, all pass; populated assertions now hit dense tables (5 hosts, 14+ containers, 40 pods, etc.). Known remaining fixture gaps (canonical adapter, not config): - VMware fixture inventory in `internal/vmware/fixtures.go` is hardcoded at 4 hosts / 6 VMs / 4 datastores; not scaled in this commit. - TrueNAS fixture inventory in `internal/truenas/fixtures.go` is similarly hardcoded; not scaled in this commit. |
||
|
|
469691bc74 |
frontend(platforms): add v5-style operator toolbar to UnifiedResourceTable platform sub-tabs
Closes the second half of the v5→v6 affordance bridge. The first commit
in this chain (
|
||
|
|
65b069dbba |
frontend(platforms): restore v5-style operator controls on embedded canonical surfaces
v5 platform/dashboard pages had a dense filter card with search, status chips, view-mode toggle, grouping toggle, column picker, and sort controls (DashboardFilter.tsx, DockerFilter.tsx, StorageFilter.tsx). v6 platform pages mounted WorkloadsSurface and StorageSurface in `embedded tableOnly` mode, which hid the entire canonical filter toolbar alongside the dashboard cards — so /docker/containers, /kubernetes/pods, /truenas/storage, /truenas/apps, /vmware/vms, and /vmware/storage shipped without search, status, grouping, or column controls. Operators had no way to filter inside a platform page short of navigating to the global Workloads/Storage page. Bridge those v5 affordances onto the v6 platform pages by extending the canonical surface contracts: - WorkloadsSurfaceProps gains `showFilterToolbar` and `suppressPlatformFilter`. `showFilterToolbar` keeps the canonical WorkloadsFilter (search input + status chips + view-mode segmented control + grouping toggle + ColumnPicker + sort handles) visible even under `tableOnly`. `suppressPlatformFilter` drops the redundant Platform chip since the platform is already fixed by the owning page, so the user never sees a removable lock. - StorageProps gains `showFilterToolbar`. Same idea for the canonical StoragePageControls (search + status + group-by + sort + node filter + view). Platform pages now mount their embedded surfaces with `tableOnly + showFilterToolbar` (plus `suppressPlatformFilter` for WorkloadsSurface): - Docker > Containers - Kubernetes > Pods - TrueNAS > Storage, Apps - vSphere > VMs, Storage UnifiedResourceTable-backed sub-tabs (Docker Hosts, K8s Clusters/ Nodes/Deployments, vSphere Hosts) still rely on the table's built-in sort handles only; a follow-up shared `PlatformInfraControls` row with search + counters is the next operator-controls bridge. Browser verification (Playwright, chromium, against live mock-mode Pulse dev runtime): - 9 tests, all pass. New assertion confirms that every embedded Workloads/Storage sub-tab on a platform page now renders the canonical search input (proving the v5-style operator toolbar is back). Targeted vitest: - WorkloadsSurface.performance.contract.test.tsx adds a platform-page embed contract assertion (37 tests total, all pass). - Storage.test.tsx adds the matching StorageProps assertion (39 tests total, all pass). Contracts updated: - performance-and-scalability.md Shared Boundaries: documents the `showFilterToolbar` + `suppressPlatformFilter` platform-page contract on WorkloadsSurface. - storage-recovery.md Shared Boundaries: documents the `showFilterToolbar` platform-page contract on StorageSurface. |
||
|
|
f81490ca4f | Keep discovery manual refresh visible | ||
|
|
b0c4faa4a0 |
frontend(platforms): drop platform sub-tabs whose canonical projection is missing
Audit pass against the live mock backend (PULSE_MOCK_MODE=true) revealed
three platform sub-tabs that could never populate from canonical
resources today, even with a mature fixture environment:
- Docker > Swarm services: no `docker-service` resource is emitted by
the unified resource adapter at the /api/resources boundary.
- Kubernetes > Services: no `k8s-service` resource type is defined in
internal/unifiedresources; only k8s-cluster, k8s-node, pod, and
k8s-deployment are projected.
- TrueNAS > Hosts: TrueNAS connections produce storage, datasets,
physical disks, and app-containers but no top-level `agent` row
tagged with the `truenas` platform.
Per the platform-pages goal ("if a platform lacks enough canonical data
model support to make a useful page even after fixture work, do not
invent speculative UI — report the data-model gap and skip or gate
that surface deliberately"), the three sub-tabs are removed from the
page navigation and the resource queries are tightened to drop the
unused types. TrueNAS now defaults to /truenas/storage as the first
canonical operator entry point.
Kubernetes Nodes now also includes agent rows whose `sources` array
reports `kubernetes`, because the backend registry merges Pulse-Agent-
linked Kubernetes nodes onto the linked agent row. The merged agents
are the canonical projection of K8s nodes when an agent is installed
on them; treating them as Nodes makes the tab populate against mock
mode and live agent fleets alike.
Browser verification (Playwright, chromium, against live mock-mode
Pulse dev runtime):
- 8 tests, all pass. Every declared platform sub-tab now asserts at
least one canonical row populates under mock mode:
- docker: /docker/overview (2 hosts), /docker/containers
- kubernetes: /kubernetes/overview (1 cluster),
/kubernetes/nodes (3 merged agents), /kubernetes/pods (10),
/kubernetes/deployments (4)
- truenas: /truenas/storage (9), /truenas/apps (5)
- vmware: /vmware/overview (4 ESXi hosts), /vmware/vms (8),
/vmware/storage (datastores)
Targeted tests:
- dockerPageModel, kubernetesPageModel, truenasPageModel suites
updated and passing.
- resourceLinks.test.ts asserts the new TrueNAS default sub-tab.
Contracts updated:
- unified-resources.md Extension Points: platform default sub-tab must
land on a canonical surface that actually populates.
- storage-recovery.md Shared Boundaries: same; calls out TrueNAS
defaulting to /truenas/storage today.
Remaining canonical data-model gaps (intentionally not surfaced as
empty platform sub-tabs in the UI):
- internal/unifiedresources does not emit `docker-service` resources
even though mock.generateDockerServicesAndTasks() generates them in
the StateSnapshot; revisit when adding a docker-service projection.
- internal/unifiedresources does not define ResourceTypeK8sService;
revisit when k8s-service rows are added to the canonical model.
- internal/unifiedresources does not project a top-level TrueNAS
system as an `agent` row; revisit when the TrueNAS adapter promotes
the connection target into a canonical infrastructure row.
|
||
|
|
cfc3fd1606 |
frontend(platforms): resolve resource platform family via sources fallback
The unified resource projection returned by /api/resources leaves `platformType` empty on several canonical resource types (storage, agent, pbs, app-container, vm, k8s-deployment, pod, etc.) under the mock fixture path and parts of the live backend. Platform-first pages were filtering on `resource.platformType` directly, so Docker / Kubernetes / TrueNAS / vSphere pages rendered empty under mock mode even though the resources existed and were tagged with the right `sources` array (['docker'], ['kubernetes'], ['truenas'], ['vmware']). Introduce `resolveResourcePlatformType(resource)` in `frontend-modern/src/utils/sourcePlatforms.ts` as the canonical reader for "what platform family does this unified resource belong to". It prefers `resource.platformType` when present and falls back to the resource's `sources` array via the existing `resolvePlatformTypeFromSources` normalization, so client-side family grouping behaves identically against mock fixtures and live backends. Each platform page model now buckets resources through that helper: - dockerPageModel.ts - kubernetesPageModel.ts - truenasPageModel.ts - vmwarePageModel.ts Browser verification (Playwright, chromium, against live mock-mode Pulse dev runtime): - 4 no-data tests (stubbed empty /api/resources): all 4 pages render sub-tab chrome and surface empty state. - 4 populated tests (live mock backend): each platform asserts at least one canonical row renders on its data-bearing sub-tabs: - docker: /docker/overview (Hosts), /docker/containers - kubernetes: /kubernetes/pods, /kubernetes/deployments - truenas: /truenas/storage - vmware: /vmware/vms, /vmware/storage All 8 tests pass. Verification artifacts staged: - sourcePlatforms.test.ts extended with resolveResourcePlatformType cases (19 tests pass). - 68-platform-pages-shell.spec.ts extended with populated-state assertions per platform. Contracts updated: - unified-resources.md Shared Boundaries: resolveResourcePlatformType is the canonical reader for unified-resource platform family. - frontend-primitives.md Extension Points: same. Remaining mock fixture gaps (intentionally not asserted populated in this commit; tracked for fixture extension): - docker/services: default mock fixtures do not expose docker-service resources at the /api/resources boundary. - kubernetes/overview, kubernetes/nodes, kubernetes/services: KubernetesClusters are generated but k8s-cluster, k8s-node, and k8s-service resource projections are not surfaced. - truenas/overview, truenas/apps: no TrueNAS agent or TrueNAS-scoped app-container resources in default fixtures. - vmware/overview: no VMware ESXi-host agent resources in default fixtures. |
||
|
|
976e7c6b42 |
Add settings discovery refresh action
- expose a manual discovery sweep API - wire Assistant & Patrol settings to run new, changed, and stale workload refreshes |