diff --git a/frontend-modern/src/features/kubernetes/KubernetesClustersTable.tsx b/frontend-modern/src/features/kubernetes/KubernetesClustersTable.tsx
index 0fdcfa5e2e..7140bcf441 100644
--- a/frontend-modern/src/features/kubernetes/KubernetesClustersTable.tsx
+++ b/frontend-modern/src/features/kubernetes/KubernetesClustersTable.tsx
@@ -1,7 +1,9 @@
import { For, Show, createMemo, type Component, type JSX } from 'solid-js';
import { StatusDot } from '@/components/shared/StatusDot';
+import { ResponsiveMetricCell } from '@/components/shared/responsive';
import { TableCard } from '@/components/shared/TableCard';
import { TableCardHeader } from '@/components/shared/TableCardHeader';
+import { StackedMemoryBar } from '@/components/Workloads/StackedMemoryBar';
import {
Table,
TableBody,
@@ -10,6 +12,7 @@ import {
TableHeader,
TableRow,
} from '@/components/shared/Table';
+import { buildMetricKeyForUnifiedResource } from '@/utils/metricsKeys';
import { getSimpleStatusIndicator } from '@/utils/status';
import { asTrimmedString } from '@/utils/stringUtils';
import {
@@ -43,11 +46,16 @@ import type { Resource } from '@/types/resource';
// CPU/Memory utilisation. It reuses the same shared primitives every
// other platform-page table uses.
-const formatPercent = (percent?: number): JSX.Element => {
- if (typeof percent !== 'number' || Number.isNaN(percent))
- return —;
- return {percent.toFixed(1)}%;
-};
+const finiteMetric = (value: number | undefined): number | undefined =>
+ typeof value === 'number' && Number.isFinite(value) ? value : undefined;
+
+const metricFallback = () => (
+
+
+ —
+
+
+);
export const KubernetesClustersTable: Component<{
clusters: Resource[];
@@ -182,6 +190,14 @@ export const KubernetesClustersTable: Component<{
cluster.id;
const context = () => asTrimmedString(cluster.kubernetes?.context) || '—';
const version = () => asTrimmedString(cluster.kubernetes?.version) || '—';
+ const metricsKey = () => buildMetricKeyForUnifiedResource(cluster);
+ const cpuPercent = () => finiteMetric(cluster.cpu?.current);
+ const memoryTotal = () => finiteMetric(cluster.memory?.total) ?? 0;
+ const memoryUsed = () => finiteMetric(cluster.memory?.used) ?? 0;
+ const memoryPercentOnly = () =>
+ memoryTotal() > 0 ? undefined : finiteMetric(cluster.memory?.current);
+ const hasMemoryMetric = () =>
+ memoryTotal() > 0 || memoryPercentOnly() !== undefined;
const counts = () =>
countsByCluster().get(cluster.id) ?? {
nodes: 0,
@@ -189,6 +205,7 @@ export const KubernetesClustersTable: Component<{
deployments: 0,
};
const indicator = () => getSimpleStatusIndicator(cluster.status);
+ const canRenderMetrics = () => indicator().variant !== 'muted';
const detailRowId = () => drawer.detailRowId(cluster);
const isExpanded = () => drawer.isExpanded(cluster);
return (
@@ -210,10 +227,7 @@ export const KubernetesClustersTable: Component<{
title={cluster.status || 'unknown'}
ariaHidden
/>
-
+
{name()}
@@ -226,7 +240,9 @@ export const KubernetesClustersTable: Component<{
- {version()}
+
+ {version()}
+
- {formatPercent(cluster.cpu?.current)}
+
- {formatPercent(cluster.memory?.current)}
+
+
+
({
+ ResponsiveMetricCell: () => ,
+}));
+
+vi.mock('@/components/Workloads/StackedMemoryBar', () => ({
+ StackedMemoryBar: () => ,
+}));
+
+const makeClusterResource = (overrides: Partial = {}): Resource => ({
+ id: 'cluster:prod-west',
+ name: 'prod-west',
+ displayName: 'prod-west',
+ platformId: 'homelab',
+ platformType: 'kubernetes',
+ sourceType: 'hybrid',
+ status: 'online',
+ type: 'k8s-cluster',
+ lastSeen: 1_700_000_000_000,
+ cpu: { current: 42 },
+ memory: { total: 32_000, used: 20_000, free: 12_000, current: 62.5 },
+ kubernetes: {
+ clusterId: 'prod-west',
+ clusterName: 'prod-west',
+ context: 'prod-west-admin',
+ version: 'v1.31.3',
+ server: 'https://prod-west.example.invalid',
+ },
+ ...overrides,
+});
+
+afterEach(() => {
+ cleanup();
+ vi.clearAllMocks();
+});
+
+describe('KubernetesClustersTable', () => {
+ it('renders canonical CPU and memory metric primitives for cluster rows', () => {
+ const cluster = makeClusterResource();
+
+ render(() => (
+ }
+ emptyTitle="No clusters"
+ emptyDescription="No clusters"
+ showToolbar={false}
+ />
+ ));
+
+ expect(screen.getByText('prod-west')).toBeInTheDocument();
+ expect(screen.getByTestId('responsive-metric-cell')).toBeInTheDocument();
+ expect(screen.getByTestId('stacked-memory-bar')).toBeInTheDocument();
+ });
+});