mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-05-10 12:00:17 +00:00
Split infrastructure drawer render owners
This commit is contained in:
parent
a64b252f2d
commit
6f3741dbfb
10 changed files with 1421 additions and 1460 deletions
|
|
@ -3105,6 +3105,9 @@
|
|||
"frontend-modern/src/components/Discovery/useDiscoveryTabState.ts",
|
||||
"frontend-modern/src/components/Infrastructure/infrastructureSelectors.ts",
|
||||
"frontend-modern/src/components/Infrastructure/ResourceDetailDrawer.tsx",
|
||||
"frontend-modern/src/components/Infrastructure/ResourceDetailDrawerDebugTab.tsx",
|
||||
"frontend-modern/src/components/Infrastructure/ResourceDetailDrawerOverviewTab.tsx",
|
||||
"frontend-modern/src/components/Infrastructure/ResourceDetailDrawerSupportDisclosure.tsx",
|
||||
"frontend-modern/src/components/Infrastructure/resourceDetailMappers.ts",
|
||||
"frontend-modern/src/components/Infrastructure/ResourceFacetSummary.tsx",
|
||||
"frontend-modern/src/components/Infrastructure/UnifiedResourceTable.tsx",
|
||||
|
|
@ -3179,6 +3182,9 @@
|
|||
"frontend-modern/src/components/Discovery/useDiscoveryTabState.ts",
|
||||
"frontend-modern/src/components/Infrastructure/infrastructureSelectors.ts",
|
||||
"frontend-modern/src/components/Infrastructure/ResourceDetailDrawer.tsx",
|
||||
"frontend-modern/src/components/Infrastructure/ResourceDetailDrawerDebugTab.tsx",
|
||||
"frontend-modern/src/components/Infrastructure/ResourceDetailDrawerOverviewTab.tsx",
|
||||
"frontend-modern/src/components/Infrastructure/ResourceDetailDrawerSupportDisclosure.tsx",
|
||||
"frontend-modern/src/components/Infrastructure/resourceDetailMappers.ts",
|
||||
"frontend-modern/src/components/Infrastructure/ResourceFacetSummary.tsx",
|
||||
"frontend-modern/src/components/Infrastructure/UnifiedResourceTable.tsx",
|
||||
|
|
|
|||
|
|
@ -44,13 +44,16 @@ cross-source deduplication.
|
|||
22. `internal/unifiedresources/privacy.go`
|
||||
23. `internal/unifiedresources/actions.go`
|
||||
24. `frontend-modern/src/components/Infrastructure/ResourceDetailDrawer.tsx`
|
||||
25. `frontend-modern/src/components/Infrastructure/ResourceFacetSummary.tsx`
|
||||
26. `frontend-modern/src/components/Infrastructure/useResourceDetailDrawerState.ts`
|
||||
27. `frontend-modern/src/components/Infrastructure/useUnifiedResourceTableState.ts`
|
||||
28. `frontend-modern/src/components/Discovery/DiscoveryTab.tsx`
|
||||
29. `frontend-modern/src/components/Discovery/useDiscoveryTabState.ts`
|
||||
30. `frontend-modern/src/features/infrastructure/InfrastructurePageSurface.tsx`
|
||||
31. `frontend-modern/src/features/infrastructure/useInfrastructurePageState.ts`
|
||||
25. `frontend-modern/src/components/Infrastructure/ResourceDetailDrawerOverviewTab.tsx`
|
||||
26. `frontend-modern/src/components/Infrastructure/ResourceDetailDrawerDebugTab.tsx`
|
||||
27. `frontend-modern/src/components/Infrastructure/ResourceDetailDrawerSupportDisclosure.tsx`
|
||||
28. `frontend-modern/src/components/Infrastructure/ResourceFacetSummary.tsx`
|
||||
29. `frontend-modern/src/components/Infrastructure/useResourceDetailDrawerState.ts`
|
||||
30. `frontend-modern/src/components/Infrastructure/useUnifiedResourceTableState.ts`
|
||||
31. `frontend-modern/src/components/Discovery/DiscoveryTab.tsx`
|
||||
32. `frontend-modern/src/components/Discovery/useDiscoveryTabState.ts`
|
||||
33. `frontend-modern/src/features/infrastructure/InfrastructurePageSurface.tsx`
|
||||
34. `frontend-modern/src/features/infrastructure/useInfrastructurePageState.ts`
|
||||
|
||||
## Shared Boundaries
|
||||
|
||||
|
|
@ -77,7 +80,7 @@ assembly branch.
|
|||
4. Add metrics-target normalization or synthetic metrics support through `internal/unifiedresources/metrics_targets.go` and `internal/unifiedresources/metrics.go`
|
||||
5. Add platform registry, resolution, or host-dedup behavior through `internal/unifiedresources/registry.go`, `internal/unifiedresources/resolve.go`, `internal/unifiedresources/resolved_host_set.go`, `internal/unifiedresources/snapshot_source_filter.go`, `internal/unifiedresources/store.go`, `internal/unifiedresources/kubernetes_capabilities.go`, and `internal/unifiedresources/pbs_rollups.go`
|
||||
6. Add canonical governed name-resolution or policy-aware resource lookup behavior through `internal/unifiedresources/resolve.go` and `internal/unifiedresources/resolve_context.go`
|
||||
7. Add or change resource drawer timeline/facet presentation through `frontend-modern/src/components/Infrastructure/ResourceDetailDrawer.tsx`, `frontend-modern/src/components/Infrastructure/useResourceDetailDrawerState.ts`, `frontend-modern/src/components/Infrastructure/ResourceFacetSummary.tsx`, `frontend-modern/src/components/Infrastructure/resourceDetailMappers.ts`, and the governed `internal/api/resources.go` facet/timeline contract together
|
||||
7. Add or change resource drawer timeline/facet presentation through `frontend-modern/src/components/Infrastructure/ResourceDetailDrawer.tsx`, `frontend-modern/src/components/Infrastructure/ResourceDetailDrawerOverviewTab.tsx`, `frontend-modern/src/components/Infrastructure/ResourceDetailDrawerDebugTab.tsx`, `frontend-modern/src/components/Infrastructure/useResourceDetailDrawerState.ts`, `frontend-modern/src/components/Infrastructure/ResourceFacetSummary.tsx`, `frontend-modern/src/components/Infrastructure/resourceDetailMappers.ts`, and the governed `internal/api/resources.go` facet/timeline contract together
|
||||
8. Add or change discovery-support runtime under the resource drawer through `frontend-modern/src/components/Discovery/DiscoveryTab.tsx` for shell/presentation ownership and `frontend-modern/src/components/Discovery/useDiscoveryTabState.ts` for fetch, websocket-progress, and notes-mutation ownership
|
||||
|
||||
## Forbidden Paths
|
||||
|
|
@ -131,6 +134,11 @@ hints.
|
|||
Those same relationship changes now summarize the actual edge(s) in `from` and
|
||||
`to`, so the canonical timeline keeps the relationship transition readable without
|
||||
needing the drawer to reconstruct an edge summary from raw endpoints.
|
||||
The infrastructure resource drawer now follows the explicit shell/state/render
|
||||
split used elsewhere in v6: `ResourceDetailDrawer.tsx` owns composition,
|
||||
`useResourceDetailDrawerState.ts` owns runtime state and fetch orchestration,
|
||||
and the overview/debug render-heavy surfaces live in dedicated drawer-local
|
||||
owners instead of staying inline in the shell.
|
||||
The backend AI and Patrol context renderers now derive their canonical change
|
||||
kind, source type, source adapter, actor, reason, and related-resource
|
||||
fragments from `internal/unifiedresources/change_presentation.go`, so the
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,93 @@
|
|||
import { For, Show } from 'solid-js';
|
||||
import type { Component } from 'solid-js';
|
||||
import type { Resource } from '@/types/resource';
|
||||
import { formatRelativeTime } from '@/utils/format';
|
||||
import type { UseResourceDetailDrawerStateResult } from './useResourceDetailDrawerState';
|
||||
|
||||
interface ResourceDetailDrawerDebugTabProps {
|
||||
resource: Resource;
|
||||
drawer: UseResourceDetailDrawerStateResult;
|
||||
}
|
||||
|
||||
export const ResourceDetailDrawerDebugTab: Component<ResourceDetailDrawerDebugTabProps> = (
|
||||
props,
|
||||
) => {
|
||||
const { drawer } = props;
|
||||
|
||||
return (
|
||||
<div class="space-y-3">
|
||||
<div class="flex items-center justify-between gap-3">
|
||||
<div class="text-xs text-muted">
|
||||
Debug mode is enabled via localStorage (<code>pulse_debug_mode</code>).
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
onClick={drawer.handleCopyJson}
|
||||
class="rounded-md border border-border bg-surface px-3 py-1.5 text-xs font-medium text-base-content transition-colors hover:bg-surface-hover"
|
||||
>
|
||||
{drawer.copied() ? 'Copied' : 'Copy JSON'}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="mt-3 space-y-4">
|
||||
<div>
|
||||
<div class="text-[11px] font-medium uppercase tracking-wide text-base-content mb-2">
|
||||
Unified Resource
|
||||
</div>
|
||||
<pre class="max-h-[280px] overflow-auto rounded-md bg-base p-3 text-[11px] text-base-content">
|
||||
{JSON.stringify(props.resource, null, 2)}
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="text-[11px] font-medium uppercase tracking-wide text-base-content mb-2">
|
||||
Identity Matching
|
||||
</div>
|
||||
<pre class="max-h-[220px] overflow-auto rounded-md bg-base p-3 text-[11px] text-base-content">
|
||||
{JSON.stringify(
|
||||
{
|
||||
identity: props.resource.identity,
|
||||
matchInfo: drawer.identityMatchInfo(),
|
||||
},
|
||||
null,
|
||||
2,
|
||||
)}
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="text-[11px] font-medium uppercase tracking-wide text-base-content mb-2">
|
||||
Sources
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<For each={drawer.sourceSections()}>
|
||||
{(section) => {
|
||||
const status = drawer.sourceStatus()[section.id];
|
||||
const lastSeenText = formatRelativeTime(status?.lastSeen);
|
||||
return (
|
||||
<details class="rounded-md border border-border bg-surface p-3">
|
||||
<summary class="flex cursor-pointer list-none items-center justify-between text-sm font-medium text-base-content">
|
||||
<span>{section.label}</span>
|
||||
<span class="text-[11px] text-muted">
|
||||
{status?.status ?? 'unknown'}
|
||||
{lastSeenText ? ` • ${lastSeenText}` : ''}
|
||||
</span>
|
||||
</summary>
|
||||
<Show when={status?.error}>
|
||||
<div class="mt-2 text-[11px] text-amber-600 dark:text-amber-300">
|
||||
{status?.error}
|
||||
</div>
|
||||
</Show>
|
||||
<pre class="mt-3 max-h-[220px] overflow-auto rounded-md bg-base p-3 text-[11px] text-base-content">
|
||||
{JSON.stringify(section.payload ?? {}, null, 2)}
|
||||
</pre>
|
||||
</details>
|
||||
);
|
||||
}}
|
||||
</For>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,55 @@
|
|||
import { Show } from 'solid-js';
|
||||
import type { Component, JSX } from 'solid-js';
|
||||
|
||||
interface ResourceDetailDrawerSupportDisclosureProps {
|
||||
title: string;
|
||||
summary?: string | null;
|
||||
expanded: boolean;
|
||||
onToggle: () => void;
|
||||
showLabel: string;
|
||||
hideLabel: string;
|
||||
children: JSX.Element;
|
||||
class?: string;
|
||||
buttonClass?: string;
|
||||
contentClass?: string;
|
||||
dataTestId?: string;
|
||||
}
|
||||
|
||||
export const ResourceDetailDrawerSupportDisclosure: Component<
|
||||
ResourceDetailDrawerSupportDisclosureProps
|
||||
> = (props) => {
|
||||
const summary = () => props.summary?.trim() ?? '';
|
||||
|
||||
return (
|
||||
<div
|
||||
data-testid={props.dataTestId}
|
||||
class={props.class ?? 'rounded border border-dashed border-border bg-surface-hover p-3'}
|
||||
>
|
||||
<div class="flex flex-wrap items-start justify-between gap-3">
|
||||
<div>
|
||||
<div class="text-[11px] font-medium uppercase tracking-wide text-base-content">
|
||||
{props.title}
|
||||
</div>
|
||||
<Show when={summary()}>
|
||||
<div class="mt-1 text-[10px] text-base-content">{summary()}</div>
|
||||
</Show>
|
||||
</div>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
onClick={props.onToggle}
|
||||
class={
|
||||
props.buttonClass ??
|
||||
'inline-flex items-center rounded-md border border-border bg-surface px-2.5 py-1 text-[10px] font-medium text-base-content transition-colors hover:bg-base'
|
||||
}
|
||||
>
|
||||
{props.expanded ? props.hideLabel : props.showLabel}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<Show when={props.expanded}>
|
||||
<div class={props.contentClass ?? 'mt-3'}>{props.children}</div>
|
||||
</Show>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
@ -3,7 +3,8 @@ import { fireEvent, render, screen, within } from '@solidjs/testing-library';
|
|||
|
||||
import discoveryTabSource from '@/components/Discovery/DiscoveryTab.tsx?raw';
|
||||
import discoveryTabStateSource from '@/components/Discovery/useDiscoveryTabState.ts?raw';
|
||||
import resourceDetailDrawerSource from '@/components/Infrastructure/ResourceDetailDrawer.tsx?raw';
|
||||
import resourceDetailDrawerShellSource from '@/components/Infrastructure/ResourceDetailDrawer.tsx?raw';
|
||||
import resourceDetailDrawerOverviewSource from '@/components/Infrastructure/ResourceDetailDrawerOverviewTab.tsx?raw';
|
||||
import type { Resource } from '@/types/resource';
|
||||
import { ResourceDetailDrawer } from '@/components/Infrastructure/ResourceDetailDrawer';
|
||||
|
||||
|
|
@ -109,10 +110,15 @@ describe('ResourceDetailDrawer change history section', () => {
|
|||
expect(discoveryTabSource).not.toContain('getConnectedAgents(');
|
||||
expect(discoveryTabSource).not.toContain('triggerDiscovery(');
|
||||
expect(discoveryTabSource).not.toContain('updateDiscoveryNotes(');
|
||||
expect(resourceDetailDrawerSource).toContain("from '@/components/shared/TagBadges'");
|
||||
expect(resourceDetailDrawerSource).not.toContain(
|
||||
expect(resourceDetailDrawerOverviewSource).toContain("from '@/components/shared/TagBadges'");
|
||||
expect(resourceDetailDrawerOverviewSource).not.toContain(
|
||||
"from '@/components/Dashboard/TagBadges'",
|
||||
);
|
||||
expect(resourceDetailDrawerShellSource).toContain(
|
||||
"from './ResourceDetailDrawerOverviewTab'",
|
||||
);
|
||||
expect(resourceDetailDrawerShellSource).toContain("from './ResourceDetailDrawerDebugTab'");
|
||||
expect(resourceDetailDrawerShellSource).not.toContain('Change history');
|
||||
});
|
||||
|
||||
it('keeps compact timeline summary chips in overview while showing one embedded change history section', async () => {
|
||||
|
|
|
|||
|
|
@ -181,6 +181,7 @@ export interface UseResourceDetailDrawerStateResult {
|
|||
>;
|
||||
hasRuntimeOperationalContext: Accessor<boolean>;
|
||||
sourceSections: Accessor<Array<{ id: string; label: string; payload: unknown }>>;
|
||||
sourceStatus: Accessor<NonNullable<PlatformData['sourceStatus']>>;
|
||||
identityMatchInfo: Accessor<unknown>;
|
||||
debugJson: Accessor<string>;
|
||||
tabs: Accessor<Array<{ id: DrawerTab; label: string }>>;
|
||||
|
|
@ -910,6 +911,7 @@ export const useResourceDetailDrawerState = (
|
|||
relatedLinks,
|
||||
hasRuntimeOperationalContext,
|
||||
sourceSections,
|
||||
sourceStatus,
|
||||
identityMatchInfo,
|
||||
debugJson,
|
||||
tabs,
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import monitoredSystemLimitWarningBannerSource from '@/components/shared/Monitor
|
|||
import selectionCardGroupSource from '@/components/shared/SelectionCardGroup.tsx?raw';
|
||||
import tagBadgesSource from '@/components/shared/TagBadges.tsx?raw';
|
||||
import guestRowSource from '@/components/Dashboard/GuestRow.tsx?raw';
|
||||
import resourceDetailDrawerSource from '@/components/Infrastructure/ResourceDetailDrawer.tsx?raw';
|
||||
import resourceDetailDrawerOverviewSource from '@/components/Infrastructure/ResourceDetailDrawerOverviewTab.tsx?raw';
|
||||
import aiSettingsDialogsSource from '@/components/Settings/AISettingsDialogs.tsx?raw';
|
||||
import generalSettingsPanelSource from '@/components/Settings/GeneralSettingsPanel.tsx?raw';
|
||||
import proxmoxSettingsPanelSource from '@/components/Settings/ProxmoxSettingsPanel.tsx?raw';
|
||||
|
|
@ -100,8 +100,8 @@ describe('shared primitive guardrails', () => {
|
|||
expect(tagBadgesSource).toContain("from '@/components/shared/Tooltip'");
|
||||
expect(guestRowSource).toContain("from '@/components/shared/TagBadges'");
|
||||
expect(guestRowSource).not.toContain("from './TagBadges'");
|
||||
expect(resourceDetailDrawerSource).toContain("from '@/components/shared/TagBadges'");
|
||||
expect(resourceDetailDrawerSource).not.toContain(
|
||||
expect(resourceDetailDrawerOverviewSource).toContain("from '@/components/shared/TagBadges'");
|
||||
expect(resourceDetailDrawerOverviewSource).not.toContain(
|
||||
"from '@/components/Dashboard/TagBadges'",
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -243,6 +243,8 @@ import licensePresentationSource from '@/utils/licensePresentation.ts?raw';
|
|||
import securityScorePresentationSource from '@/utils/securityScorePresentation.ts?raw';
|
||||
import securityAuthPresentationSource from '@/utils/securityAuthPresentation.ts?raw';
|
||||
import resourceDetailDrawerShellSource from '@/components/Infrastructure/ResourceDetailDrawer.tsx?raw';
|
||||
import resourceDetailDrawerOverviewSource from '@/components/Infrastructure/ResourceDetailDrawerOverviewTab.tsx?raw';
|
||||
import resourceDetailDrawerDebugSource from '@/components/Infrastructure/ResourceDetailDrawerDebugTab.tsx?raw';
|
||||
import infrastructureSummarySource from '@/components/Infrastructure/InfrastructureSummary.tsx?raw';
|
||||
import resourceDetailMappersSource from '@/components/Infrastructure/resourceDetailMappers.ts?raw';
|
||||
import resourceDetailDrawerStateSource from '@/components/Infrastructure/useResourceDetailDrawerState.ts?raw';
|
||||
|
|
@ -518,6 +520,8 @@ const aiSettingsSource = [
|
|||
|
||||
const resourceDetailDrawerSource = [
|
||||
resourceDetailDrawerShellSource,
|
||||
resourceDetailDrawerOverviewSource,
|
||||
resourceDetailDrawerDebugSource,
|
||||
resourceDetailDrawerStateSource,
|
||||
].join('\n');
|
||||
|
||||
|
|
@ -861,10 +865,14 @@ describe('frontend resource type boundaries', () => {
|
|||
expect(guestRowCellsSource).toContain('useTooltip');
|
||||
expect(guestRowSource).not.toContain('buildGuestId');
|
||||
expect(tagBadgesSource).toContain("from '@/components/shared/Tooltip'");
|
||||
expect(resourceDetailDrawerShellSource).toContain("from '@/components/shared/TagBadges'");
|
||||
expect(resourceDetailDrawerShellSource).not.toContain(
|
||||
expect(resourceDetailDrawerOverviewSource).toContain("from '@/components/shared/TagBadges'");
|
||||
expect(resourceDetailDrawerOverviewSource).not.toContain(
|
||||
"from '@/components/Dashboard/TagBadges'",
|
||||
);
|
||||
expect(resourceDetailDrawerShellSource).toContain(
|
||||
"from './ResourceDetailDrawerOverviewTab'",
|
||||
);
|
||||
expect(resourceDetailDrawerShellSource).toContain("from './ResourceDetailDrawerDebugTab'");
|
||||
expect(guestDrawerSource).toContain('useGuestDrawerState');
|
||||
expect(guestDrawerSource).toContain('GuestDrawerOverview');
|
||||
expect(guestDrawerStateSource).toContain('getCanonicalWorkloadId');
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue