mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-09-25 04:33:03 +00:00
Move identity support into identity card
This commit is contained in:
@@ -432,13 +432,15 @@ while deeper machine detail remains available on demand.
|
||||
That host-details section now reads as a simple vertical stack of detail cards
|
||||
instead of a wrapped card grid, so the opened state stays linear instead of
|
||||
feeling like a second dashboard.
|
||||
Within that summary shell, current-state facts now stay in the primary summary
|
||||
while platform IDs, quick links, platform-signal badges, aliases, IPs, and
|
||||
tags render inline as supporting rows, so first read remains status-first and
|
||||
identity-first instead of mixing state with hidden supporting action surfaces.
|
||||
Those rows stay label-first and only expand when a specific value, like alias
|
||||
overflow, needs its own reveal, so the summary answers the main resource
|
||||
question before deeper metadata appears.
|
||||
Within that summary shell, current-state facts now stay operational: platform
|
||||
IDs, quick links, and platform-signal badges remain with runtime status,
|
||||
while aliases, IPs, and tags live only under the dedicated `Identity` card.
|
||||
That keeps first read status-first while still preserving canonical identity
|
||||
metadata on the same top-level summary surface instead of mixing identity
|
||||
support details into current-state chrome.
|
||||
The identity-side rows stay label-first and only expand when a specific value,
|
||||
like alias overflow, needs its own reveal, so the summary answers the main
|
||||
resource question before deeper metadata appears.
|
||||
When the identity side has no owned rows or supporting labels yet, the sparse
|
||||
fallback now stays terse (`No identity metadata yet.`) so empty state chrome
|
||||
does not read heavier than the data it is standing in for.
|
||||
|
||||
@@ -149,7 +149,7 @@ export const ResourceDetailDrawerOverviewTab: Component<ResourceDetailDrawerOver
|
||||
</span>
|
||||
</div>
|
||||
</Show>
|
||||
<Show when={drawer.hasRuntimeOperationalContext() || drawer.hasIdentitySupportContext()}>
|
||||
<Show when={drawer.hasRuntimeOperationalContext()}>
|
||||
<div class="mt-2 rounded border border-dashed border-border bg-surface-hover p-3">
|
||||
<div class="space-y-2.5">
|
||||
<Show when={drawer.hasRuntimeOperationalContext()}>
|
||||
@@ -199,74 +199,6 @@ export const ResourceDetailDrawerOverviewTab: Component<ResourceDetailDrawerOver
|
||||
</Show>
|
||||
</div>
|
||||
</Show>
|
||||
<Show when={drawer.hasIdentitySupportContext()}>
|
||||
<div class="space-y-1.5">
|
||||
<Show when={resource.identity?.ips && resource.identity.ips.length > 0}>
|
||||
<div class="flex flex-col gap-1">
|
||||
<span class="text-muted">IP Addresses</span>
|
||||
<div class="flex flex-wrap gap-1">
|
||||
<For each={resource.identity?.ips ?? []}>
|
||||
{(ip) => (
|
||||
<span
|
||||
class="inline-flex items-center rounded bg-blue-100 px-1.5 py-0.5 text-[10px] text-blue-700 dark:bg-blue-900 dark:text-blue-200"
|
||||
title={ip}
|
||||
>
|
||||
{ip}
|
||||
</span>
|
||||
)}
|
||||
</For>
|
||||
</div>
|
||||
</div>
|
||||
</Show>
|
||||
<Show when={resource.tags && resource.tags.length > 0}>
|
||||
<div class="flex items-center justify-between gap-2">
|
||||
<span class="text-muted">Tags</span>
|
||||
<TagBadges tags={resource.tags} maxVisible={6} />
|
||||
</div>
|
||||
</Show>
|
||||
<Show when={drawer.identityAliasValues().length > 0}>
|
||||
<Show
|
||||
when={drawer.hasAliasOverflow()}
|
||||
fallback={
|
||||
<div class="flex flex-col gap-1">
|
||||
<span class="text-muted">Aliases</span>
|
||||
<div class="flex flex-wrap gap-1">
|
||||
<For each={drawer.aliasPreviewValues()}>
|
||||
{(value) => (
|
||||
<span
|
||||
class="inline-flex items-center rounded bg-surface-alt px-1.5 py-0.5 text-[10px]"
|
||||
title={value}
|
||||
>
|
||||
{value}
|
||||
</span>
|
||||
)}
|
||||
</For>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<details class="rounded border border-border bg-surface px-2 py-1.5">
|
||||
<summary class="flex cursor-pointer list-none items-center justify-between text-[10px] font-medium text-muted">
|
||||
<span>Aliases</span>
|
||||
<span class="text-muted">{drawer.identityAliasValues().length}</span>
|
||||
</summary>
|
||||
<div class="mt-2 flex flex-wrap gap-1 border-t border-border pt-2">
|
||||
<For each={drawer.identityAliasValues()}>
|
||||
{(value) => (
|
||||
<span
|
||||
class="inline-flex items-center rounded bg-surface-alt px-1.5 py-0.5 text-[10px]"
|
||||
title={value}
|
||||
>
|
||||
{value}
|
||||
</span>
|
||||
)}
|
||||
</For>
|
||||
</div>
|
||||
</details>
|
||||
</Show>
|
||||
</Show>
|
||||
</div>
|
||||
</Show>
|
||||
</div>
|
||||
</div>
|
||||
</Show>
|
||||
@@ -288,6 +220,70 @@ export const ResourceDetailDrawerOverviewTab: Component<ResourceDetailDrawerOver
|
||||
</div>
|
||||
)}
|
||||
</For>
|
||||
<Show when={drawer.identityIpValues().length > 0}>
|
||||
<div class="flex flex-col gap-1">
|
||||
<span class="text-muted">IP Addresses</span>
|
||||
<div class="flex flex-wrap gap-1">
|
||||
<For each={drawer.identityIpValues()}>
|
||||
{(ip) => (
|
||||
<span
|
||||
class="inline-flex items-center rounded bg-blue-100 px-1.5 py-0.5 text-[10px] text-blue-700 dark:bg-blue-900 dark:text-blue-200"
|
||||
title={ip}
|
||||
>
|
||||
{ip}
|
||||
</span>
|
||||
)}
|
||||
</For>
|
||||
</div>
|
||||
</div>
|
||||
</Show>
|
||||
<Show when={resource.tags && resource.tags.length > 0}>
|
||||
<div class="flex items-center justify-between gap-2">
|
||||
<span class="text-muted">Tags</span>
|
||||
<TagBadges tags={resource.tags} maxVisible={6} />
|
||||
</div>
|
||||
</Show>
|
||||
<Show when={drawer.identityAliasValues().length > 0}>
|
||||
<Show
|
||||
when={drawer.hasAliasOverflow()}
|
||||
fallback={
|
||||
<div class="flex flex-col gap-1">
|
||||
<span class="text-muted">Aliases</span>
|
||||
<div class="flex flex-wrap gap-1">
|
||||
<For each={drawer.aliasPreviewValues()}>
|
||||
{(value) => (
|
||||
<span
|
||||
class="inline-flex items-center rounded bg-surface-alt px-1.5 py-0.5 text-[10px]"
|
||||
title={value}
|
||||
>
|
||||
{value}
|
||||
</span>
|
||||
)}
|
||||
</For>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<details class="rounded border border-border bg-surface px-2 py-1.5">
|
||||
<summary class="flex cursor-pointer list-none items-center justify-between text-[10px] font-medium text-muted">
|
||||
<span>Aliases</span>
|
||||
<span class="text-muted">{drawer.identityAliasValues().length}</span>
|
||||
</summary>
|
||||
<div class="mt-2 flex flex-wrap gap-1 border-t border-border pt-2">
|
||||
<For each={drawer.identityAliasValues()}>
|
||||
{(value) => (
|
||||
<span
|
||||
class="inline-flex items-center rounded bg-surface-alt px-1.5 py-0.5 text-[10px]"
|
||||
title={value}
|
||||
>
|
||||
{value}
|
||||
</span>
|
||||
)}
|
||||
</For>
|
||||
</div>
|
||||
</details>
|
||||
</Show>
|
||||
</Show>
|
||||
<Show when={!drawer.identityCardHasRichData()}>
|
||||
<div class="rounded border border-dashed bg-surface-hover px-2 py-1.5 text-[10px] ">
|
||||
No identity metadata yet.
|
||||
|
||||
+6
-2
@@ -275,6 +275,8 @@ describe('ResourceDetailDrawer change history section', () => {
|
||||
expect(
|
||||
screen.getByTestId('resource-change-history-section').querySelector('.mt-3.grid.gap-2'),
|
||||
).toBeNull();
|
||||
const currentStateSection = screen.getByTestId('resource-current-state-section');
|
||||
const identitySection = screen.getByTestId('resource-identity-section');
|
||||
expect(screen.queryByText('Host details')).toBeNull();
|
||||
expect(screen.queryByText('Service details')).toBeNull();
|
||||
expect(screen.queryByText('Supporting context')).toBeNull();
|
||||
@@ -306,7 +308,8 @@ describe('ResourceDetailDrawer change history section', () => {
|
||||
expect(screen.queryByText('Details')).toBeNull();
|
||||
expect(screen.queryByRole('button', { name: 'Show details' })).toBeNull();
|
||||
expect(screen.getByText('Platform ID')).toBeInTheDocument();
|
||||
expect(screen.getByText('Tags')).toBeInTheDocument();
|
||||
expect(within(identitySection).getByText('Tags')).toBeInTheDocument();
|
||||
expect(within(currentStateSection).queryByText('Tags')).toBeNull();
|
||||
expect(
|
||||
within(changeHistorySection).queryByText('Filterable event history for this resource.'),
|
||||
).toBeNull();
|
||||
@@ -420,7 +423,8 @@ describe('ResourceDetailDrawer change history section', () => {
|
||||
|
||||
render(() => <ResourceDetailDrawer resource={resource} />);
|
||||
|
||||
expect(screen.getByText('Aliases')).toBeInTheDocument();
|
||||
expect(within(screen.getByTestId('resource-identity-section')).getByText('Aliases')).toBeInTheDocument();
|
||||
expect(within(screen.getByTestId('resource-current-state-section')).queryByText('Aliases')).toBeNull();
|
||||
});
|
||||
|
||||
it('renders timeline history without surfacing unsupported capability or relationship facets', async () => {
|
||||
|
||||
+3
-8
@@ -355,11 +355,12 @@ export const useResourceDetailDrawerDerivedState = (
|
||||
});
|
||||
|
||||
const identityAliasValues = createMemo(() => getResourceIdentityAliases(resource));
|
||||
const identityIpValues = createMemo(() => resource.identity?.ips ?? []);
|
||||
const primaryIdentityRows = createMemo(() => getPrimaryResourceIdentityRows(resource));
|
||||
const identityCardHasRichData = createMemo(
|
||||
() =>
|
||||
primaryIdentityRows().length > 0 ||
|
||||
(resource.identity?.ips?.length || 0) > 0 ||
|
||||
identityIpValues().length > 0 ||
|
||||
(resource.tags?.length || 0) > 0 ||
|
||||
identityAliasValues().length > 0,
|
||||
);
|
||||
@@ -369,12 +370,6 @@ export const useResourceDetailDrawerDerivedState = (
|
||||
const hasAliasOverflow = createMemo(
|
||||
() => identityAliasValues().length > ALIAS_COLLAPSE_THRESHOLD,
|
||||
);
|
||||
const hasIdentitySupportContext = createMemo(
|
||||
() =>
|
||||
(resource.identity?.ips?.length ?? 0) > 0 ||
|
||||
(resource.tags?.length ?? 0) > 0 ||
|
||||
identityAliasValues().length > 0,
|
||||
);
|
||||
const hasMergedSources = createMemo(() => mergedSources().length > 1);
|
||||
const discoveryConfig = createMemo(() => toDiscoveryConfig(resource));
|
||||
const discoveryContextSummary = createMemo(() => {
|
||||
@@ -585,12 +580,12 @@ export const useResourceDetailDrawerDerivedState = (
|
||||
mergedSources,
|
||||
sourceStatus,
|
||||
sourceSummary,
|
||||
identityIpValues,
|
||||
identityAliasValues,
|
||||
primaryIdentityRows,
|
||||
identityCardHasRichData,
|
||||
aliasPreviewValues,
|
||||
hasAliasOverflow,
|
||||
hasIdentitySupportContext,
|
||||
hasMergedSources,
|
||||
discoveryConfig,
|
||||
discoveryContextSummary,
|
||||
|
||||
Reference in New Issue
Block a user