Simplify resource drawer header context

This commit is contained in:
rcourtman 2026-03-19 21:44:15 +00:00
parent efcbb9f137
commit 3cc4e952cd
5 changed files with 46 additions and 32 deletions

View file

@ -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

View file

@ -541,12 +541,14 @@ const DrawerContent: Component<ResourceDetailDrawerProps> = (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<ResourceDetailDrawerProps> = (props) => {
)}
</For>
</Show>
<For each={kubernetesCapabilityBadges()}>
{(badge) => (
<span class={badge.classes} title={badge.title}>
{badge.label}
</span>
)}
</For>
<For each={policyBadges()}>
{(badge) => (
<span class={badge.className} title={badge.title}>
{badge.label}
</span>
)}
</For>
</div>
</div>
@ -750,22 +738,6 @@ const DrawerContent: Component<ResourceDetailDrawerProps> = (props) => {
</Show>
</div>
<Show when={relatedLinks().length > 0}>
<div class="flex items-center justify-end gap-2">
<For each={relatedLinks()}>
{(link) => (
<a
href={link.href}
aria-label={link.ariaLabel}
class="inline-flex items-center rounded border border-blue-200 bg-blue-50 px-2.5 py-1 text-xs font-medium text-blue-700 transition-colors hover:bg-blue-100 dark:border-blue-700 dark:bg-blue-900 dark:text-blue-200 dark:hover:bg-blue-900"
>
{link.label}
</a>
)}
</For>
</div>
</Show>
<div class="flex items-center gap-6 border-b border-border px-1 mb-1">
<For each={tabs()}>
{(tab) => (
@ -885,6 +857,38 @@ const DrawerContent: Component<ResourceDetailDrawerProps> = (props) => {
</span>
</div>
</Show>
<Show when={kubernetesCapabilityBadges().length > 0}>
<div class="flex flex-col gap-1">
<span class="text-muted">Platform signals</span>
<div class="flex flex-wrap gap-1">
<For each={kubernetesCapabilityBadges()}>
{(badge) => (
<span class={badge.classes} title={badge.title}>
{badge.label}
</span>
)}
</For>
</div>
</div>
</Show>
<Show when={relatedLinks().length > 0}>
<div class="flex flex-col gap-1 pt-1">
<span class="text-muted">Quick links</span>
<div class="flex flex-wrap gap-2">
<For each={relatedLinks()}>
{(link) => (
<a
href={link.href}
aria-label={link.ariaLabel}
class="inline-flex items-center rounded border border-blue-200 bg-blue-50 px-2.5 py-1 text-[11px] font-medium text-blue-700 transition-colors hover:bg-blue-100 dark:border-blue-700 dark:bg-blue-900 dark:text-blue-200 dark:hover:bg-blue-900"
>
{link.compactLabel}
</a>
)}
</For>
</div>
</div>
</Show>
</div>
</div>

View file

@ -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();

View file

@ -114,13 +114,17 @@ describe('ResourceDetailDrawer runtime and identity cards', () => {
},
});
const { getByText } = render(() => <ResourceDetailDrawer resource={resource} />);
const { getByRole, getByText } = render(() => <ResourceDetailDrawer resource={resource} />);
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', () => {

View file

@ -90,6 +90,7 @@ describe('ResourceDetailDrawer kubernetes capabilities', () => {
<ResourceDetailDrawer resource={buildKubernetesResource()} />
));
expect(getByText('Platform signals')).toBeInTheDocument();
expect(getByText('K8s Node CPU/Memory')).toBeInTheDocument();
expect(getByText('Node Telemetry (Agent)')).toBeInTheDocument();
expect(getByText('Pod CPU/Memory')).toBeInTheDocument();