diff --git a/docs/release-control/v6/internal/subsystems/unified-resources.md b/docs/release-control/v6/internal/subsystems/unified-resources.md index 7801175b4..f07cdbf95 100644 --- a/docs/release-control/v6/internal/subsystems/unified-resources.md +++ b/docs/release-control/v6/internal/subsystems/unified-resources.md @@ -459,14 +459,15 @@ That access-side analysis surface still follows the same shell/runtime split as the rest of the drawer: `DiscoveryTab.tsx` owns presentation and disclosures, while `useDiscoveryTabState.ts` owns API fetches, websocket progress, and note/discovery mutations. -The overview keeps access, host, service, and investigation detail as -collapsed sibling disclosures under the primary card pair, so the drawer keeps +The overview keeps access, investigation detail, service detail, and host +detail as collapsed sibling disclosures under the primary card pair, so the +drawer keeps the top-level shape to current-state/identity plus `Change history` before any secondary operational context appears. That secondary hierarchy now renders in two layers: a full-width `Change -history` surface first, followed by a separate support-disclosure grid for -access, host, service, and context, so timeline inspection stays visually -primary instead of competing with the support cards for equal width. +history` surface first, followed by a separate support-disclosure grid ordered +as `Access`, `Context`, `Service`, and `Host`, so the operator sees links and +investigation context before deeper technical drill-down cards. Inside `Change history`, the event list now renders directly in the parent section instead of inside a second bordered `Event log` card, so the timeline reads like one inspection surface rather than a card nested under its own diff --git a/frontend-modern/src/components/Infrastructure/ResourceDetailDrawerOverviewTab.tsx b/frontend-modern/src/components/Infrastructure/ResourceDetailDrawerOverviewTab.tsx index 729da05a4..544041ab7 100644 --- a/frontend-modern/src/components/Infrastructure/ResourceDetailDrawerOverviewTab.tsx +++ b/frontend-modern/src/components/Infrastructure/ResourceDetailDrawerOverviewTab.tsx @@ -520,6 +520,224 @@ export const ResourceDetailDrawerOverviewTab: Component + + drawer.setShowAccessContext((value) => !value)} + showLabel="Show access" + hideLabel="Hide access" + class="h-full" + contentClass="mt-3 space-y-3" + dataTestId="resource-access-section" + > + 0}> +
+
+ Links +
+
+ + {(link) => ( + + {link.compactLabel} + + )} + +
+
+
+ + + {(config) => ( +
+ + +
+
+
+
+ Analysis +
+ +
+ {drawer.discoveryContextSummary()} +
+
+
+ +
+ + + +
+ {getDiscoveryLoadingState().text} +
+ } + > + +
+
+
+
+ )} +
+
+
+ + + drawer.setShowInvestigationContext((value) => !value)} + showLabel="Show context" + hideLabel="Hide context" + class="h-full" + contentClass="mt-3 space-y-3" + dataTestId="resource-investigation-context" + > + + {(intel) => ( +
+
+ AI +
+
+ Health + + {intel().health.grade} · {Math.round(intel().health.score)}/100 + +
+
+ Trend + + {intel().health.trend} + +
+
+ Notes + {intel().note_count} +
+ + +
+
+ + Correlations + + +
+ + +
+ +
+
+
+
+
+ )} +
+ + +
+
+ Governance +
+ +
+ Sensitivity + + {getResourceSensitivityLabel(resource.policy?.sensitivity)} + +
+
+ Routing + + {getResourceRoutingScopeLabel(resource.policy?.routing.scope)} + +
+
+ 0 || drawer.governanceSummary()}> +
+ Redactions + + {drawer.policyRedactions().length} + +
+
+ 0}> +
+ Redaction labels +
+ + {(label) => ( + + {label} + + )} + +
+
+
+ +
+ AI-Safe Summary +
+ {drawer.governanceSummary()} +
+
+
+
+
+
+
+ - - drawer.setShowAccessContext((value) => !value)} - showLabel="Show access" - hideLabel="Hide access" - class="h-full" - contentClass="mt-3 space-y-3" - dataTestId="resource-access-section" - > - 0}> -
-
- Links -
-
- - {(link) => ( - - {link.compactLabel} - - )} - -
-
-
- - - {(config) => ( -
- - -
-
-
-
- Analysis -
- -
- {drawer.discoveryContextSummary()} -
-
-
- -
- - - -
- {getDiscoveryLoadingState().text} -
- } - > - -
-
-
-
- )} -
-
-
- - - drawer.setShowInvestigationContext((value) => !value)} - showLabel="Show context" - hideLabel="Hide context" - class="h-full" - contentClass="mt-3 space-y-3" - dataTestId="resource-investigation-context" - > - - {(intel) => ( -
-
- AI -
-
- Health - - {intel().health.grade} · {Math.round(intel().health.score)}/100 - -
-
- Trend - - {intel().health.trend} - -
-
- Notes - {intel().note_count} -
- - -
-
- - Correlations - - -
- - -
- -
-
-
-
-
- )} -
- - -
-
- Governance -
- -
- Sensitivity - - {getResourceSensitivityLabel(resource.policy?.sensitivity)} - -
-
- Routing - - {getResourceRoutingScopeLabel(resource.policy?.routing.scope)} - -
-
- 0 || drawer.governanceSummary()}> -
- Redactions - - {drawer.policyRedactions().length} - -
-
- 0}> -
- Redaction labels -
- - {(label) => ( - - {label} - - )} - -
-
-
- -
- AI-Safe Summary -
- {drawer.governanceSummary()} -
-
-
-
-
-
-
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 115494eb6..c1cf2fef5 100644 --- a/frontend-modern/src/components/Infrastructure/__tests__/ResourceDetailDrawer.history.test.tsx +++ b/frontend-modern/src/components/Infrastructure/__tests__/ResourceDetailDrawer.history.test.tsx @@ -381,6 +381,14 @@ describe('ResourceDetailDrawer change history section', () => { expect(within(changeHistorySection).queryByText('Recent activity')).toBeNull(); expect(screen.queryByText('Events')).toBeNull(); expect(screen.getAllByText('Timeline 3')).toHaveLength(1); + expect( + Array.from(screen.getByTestId('resource-support-sections').children).map((node) => + node.getAttribute('data-testid'), + ), + ).toEqual([ + 'resource-access-section', + 'resource-investigation-context', + ]); expect(screen.getAllByText('Restart 2')).toHaveLength(1); expect(screen.getAllByText('Anomaly 1')).toHaveLength(1); expect(screen.getAllByText('Platform event 1')).toHaveLength(1); @@ -635,6 +643,14 @@ describe('ResourceDetailDrawer change history section', () => { render(() => ); expect(screen.getByText('Service')).toBeInTheDocument(); + expect( + Array.from(screen.getByTestId('resource-support-sections').children).map((node) => + node.getAttribute('data-testid'), + ), + ).toEqual([ + 'resource-access-section', + 'resource-service-details-section', + ]); fireEvent.click(screen.getByRole('button', { name: 'Show service' })); const serviceDetails = within(screen.getByTestId('resource-service-details-section')); expect( 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 0715bdca8..e76ee6769 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 @@ -235,6 +235,14 @@ describe('ResourceDetailDrawer runtime and identity cards', () => { expect( getByTestId('resource-support-sections').classList.contains('flex-wrap'), ).toBe(true); + expect( + Array.from(getByTestId('resource-support-sections').children).map((node) => + node.getAttribute('data-testid'), + ), + ).toEqual([ + 'resource-access-section', + 'resource-host-details-section', + ]); expect(getByTestId('resource-host-details-section').querySelector('.mt-3.flex.flex-wrap')).toBeTruthy(); expect( getByTestId('resource-host-details-section').querySelector('.mt-3.flex.flex-wrap')?.classList.contains('[&>*]:min-w-[220px]'),