diff --git a/docs/release-control/v6/internal/subsystems/unified-resources.md b/docs/release-control/v6/internal/subsystems/unified-resources.md index bbef547c0..fa70da6d9 100644 --- a/docs/release-control/v6/internal/subsystems/unified-resources.md +++ b/docs/release-control/v6/internal/subsystems/unified-resources.md @@ -245,6 +245,10 @@ That same overview now keeps AI intelligence and data-governance details inside a collapsed `Investigation context` disclosure, so runtime status and identity stay primary while secondary AI and policy signals remain available without competing with the first-screen monitoring story. +The drawer header now stays focused on canonical identity and source/type +badges only, while workload/service drill-down links and Kubernetes platform +signals live with the runtime card, so the top strip does not compete with +the resource name, status, or primary identity line. The same facet bundle now also returns grouped recent-change counts by canonical change kind, so the detail drawer can surface the distribution of state transitions, restarts, config updates, and anomalies without diff --git a/frontend-modern/src/components/Infrastructure/ResourceDetailDrawer.tsx b/frontend-modern/src/components/Infrastructure/ResourceDetailDrawer.tsx index 3a37b2f40..276412434 100644 --- a/frontend-modern/src/components/Infrastructure/ResourceDetailDrawer.tsx +++ b/frontend-modern/src/components/Infrastructure/ResourceDetailDrawer.tsx @@ -541,12 +541,14 @@ const DrawerContent: Component = (props) => { const workloadsHref = createMemo(() => buildWorkloadsHref(props.resource)); const headerIdentity = createMemo(() => getPrimaryResourceIdentity(props.resource)); const relatedLinks = createMemo(() => { - const links: Array<{ href: string; label: string; ariaLabel: string }> = []; + const links: Array<{ href: string; label: string; compactLabel: string; ariaLabel: string }> = + []; const workloads = workloadsHref(); if (workloads) { links.push({ href: workloads, label: 'Open in Workloads', + compactLabel: 'Workloads', ariaLabel: `Open related workloads for ${displayName()}`, }); } @@ -714,20 +716,6 @@ const DrawerContent: Component = (props) => { )} - - {(badge) => ( - - {badge.label} - - )} - - - {(badge) => ( - - {badge.label} - - )} - @@ -750,22 +738,6 @@ const DrawerContent: Component = (props) => { - 0}> -
- - {(link) => ( - - {link.label} - - )} - -
-
-
{(tab) => ( @@ -885,6 +857,38 @@ const DrawerContent: Component = (props) => {
+ 0}> +
+ Platform signals +
+ + {(badge) => ( + + {badge.label} + + )} + +
+
+
+ 0}> +
+ Quick links +
+ + {(link) => ( + + {link.compactLabel} + + )} + +
+
+
diff --git a/frontend-modern/src/components/Infrastructure/__tests__/ResourceDetailDrawer.history.test.tsx b/frontend-modern/src/components/Infrastructure/__tests__/ResourceDetailDrawer.history.test.tsx index b87c2bf7c..8683a38a9 100644 --- a/frontend-modern/src/components/Infrastructure/__tests__/ResourceDetailDrawer.history.test.tsx +++ b/frontend-modern/src/components/Infrastructure/__tests__/ResourceDetailDrawer.history.test.tsx @@ -194,6 +194,7 @@ describe('ResourceDetailDrawer change history section', () => { expect(screen.getAllByText('Pulse diff 2')).toHaveLength(1); expect(screen.getAllByText('Docker adapter 2')).toHaveLength(1); expect(screen.getAllByText('Proxmox adapter 1')).toHaveLength(1); + expect(screen.queryByText('Quick links')).toBeNull(); expect(screen.getByText('Investigation context')).toBeInTheDocument(); expect(screen.queryByText('Storage 1 alias')).toBeNull(); expect(screen.queryByText('VM Child')).toBeNull(); diff --git a/frontend-modern/src/components/Infrastructure/__tests__/ResourceDetailDrawer.identity-runtime.test.tsx b/frontend-modern/src/components/Infrastructure/__tests__/ResourceDetailDrawer.identity-runtime.test.tsx index 8c2618025..a5fbb1723 100644 --- a/frontend-modern/src/components/Infrastructure/__tests__/ResourceDetailDrawer.identity-runtime.test.tsx +++ b/frontend-modern/src/components/Infrastructure/__tests__/ResourceDetailDrawer.identity-runtime.test.tsx @@ -114,13 +114,17 @@ describe('ResourceDetailDrawer runtime and identity cards', () => { }, }); - const { getByText } = render(() => ); + const { getByRole, getByText } = render(() => ); expect(getByText('Runtime')).toBeInTheDocument(); expect(getByText('Sources')).toBeInTheDocument(); expect(getByText('2/2 healthy')).toBeInTheDocument(); expect(getByText('Mode')).toBeInTheDocument(); expect(getByText('Hybrid')).toBeInTheDocument(); + expect(getByText('Quick links')).toBeInTheDocument(); + expect(getByRole('link', { name: 'Open related workloads for host-1' })).toHaveTextContent( + 'Workloads', + ); }); it('falls back to source list summary when per-source health is unavailable', () => { diff --git a/frontend-modern/src/components/Infrastructure/__tests__/ResourceDetailDrawer.k8s-capabilities.test.tsx b/frontend-modern/src/components/Infrastructure/__tests__/ResourceDetailDrawer.k8s-capabilities.test.tsx index a45c3dc9f..6739be3cc 100644 --- a/frontend-modern/src/components/Infrastructure/__tests__/ResourceDetailDrawer.k8s-capabilities.test.tsx +++ b/frontend-modern/src/components/Infrastructure/__tests__/ResourceDetailDrawer.k8s-capabilities.test.tsx @@ -90,6 +90,7 @@ describe('ResourceDetailDrawer kubernetes capabilities', () => { )); + expect(getByText('Platform signals')).toBeInTheDocument(); expect(getByText('K8s Node CPU/Memory')).toBeInTheDocument(); expect(getByText('Node Telemetry (Agent)')).toBeInTheDocument(); expect(getByText('Pod CPU/Memory')).toBeInTheDocument();