mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-05-19 07:54:10 +00:00
Remove infrastructure setup modals
This commit is contained in:
parent
688bdd4246
commit
264c9377a2
5 changed files with 159 additions and 232 deletions
|
|
@ -858,18 +858,18 @@ ledger. Inline detail drawers may surface reporting-item and ignored-item
|
|||
controls, but installer setup, platform-specific configuration, and profile
|
||||
management must remain secondary flows rather than being dumped underneath the
|
||||
default ledger.
|
||||
Those secondary infrastructure views must open through route-backed modal work
|
||||
surfaces rather than replacing the ledger body or stacking beneath it. When the
|
||||
Those secondary infrastructure views must open through route-backed workspaces
|
||||
rather than modal overlays or stacked inline bodies. When the
|
||||
operator opens platform connection management, install tooling, or the legacy
|
||||
operations workspace route, `InfrastructureWorkspace.tsx` must keep the same
|
||||
top-level systems ledger anchored in place and layer the secondary surface in a
|
||||
centered modal that can close back to `/settings/infrastructure` without
|
||||
trapping the user or relegating primary setup work to an off-canvas detail
|
||||
pattern.
|
||||
single `Infrastructure` destination and provide explicit back navigation to
|
||||
`/settings/infrastructure` without trapping the user behind overlays or
|
||||
relegating primary setup work to a detail pattern.
|
||||
That infrastructure surface must now stay single-purpose per route-backed
|
||||
modal: the default systems view owns the top-level monitored-system ledger,
|
||||
the connections modal owns API-backed platform management, and the install
|
||||
modal owns Linux/Windows/macOS/FreeBSD command generation. The shared
|
||||
workspace: the default systems view owns the top-level monitored-system ledger,
|
||||
the connections workspace owns API-backed platform management, and the install
|
||||
workspace owns Linux/Windows/macOS/FreeBSD command generation plus the add-flow
|
||||
handoff. The shared
|
||||
Settings sidebar owns only the top-level `Infrastructure` destination; movement
|
||||
between those three jobs belongs to explicit ledger actions inside
|
||||
`InfrastructureWorkspace.tsx`, not extra sidebar entries or body-replacing
|
||||
|
|
|
|||
|
|
@ -220,17 +220,14 @@ work extends shared components instead of creating new local variants.
|
|||
connections must stay in their own management workspace instead of showing up
|
||||
as peer rows in the default table, while installer tooling, provider setup
|
||||
workspaces, and profile management remain secondary flows opened by explicit
|
||||
deep links or modal work surfaces instead of being dumped inline underneath the default
|
||||
deep links or dedicated workspace routes instead of being dumped inline underneath the default
|
||||
table.
|
||||
Those deep-linked secondary views must keep the systems ledger mounted and
|
||||
open inside route-backed modal surfaces instead of replacing the page body or
|
||||
stacking another inline workspace underneath it, so the operator always
|
||||
stays anchored to the same canonical ledger while managing setup flows
|
||||
without relegating primary setup work to off-canvas side drawers.
|
||||
Those deep-linked secondary views must stay under the same single
|
||||
`Infrastructure` sidebar destination, but they should render as normal page
|
||||
content with explicit back navigation instead of modal or drawer overlays.
|
||||
That same shared shell boundary now owns one canonical infrastructure
|
||||
destination in the Settings sidebar. `InfrastructureWorkspace.tsx` owns the
|
||||
one default ledger plus route-backed `Connections` and `Install` modal work
|
||||
surfaces
|
||||
default ledger plus route-backed `Connections` and `Install` workspaces
|
||||
inside that destination, while each secondary flow still stays
|
||||
single-purpose instead of stacking multiple workspace surfaces at once.
|
||||
6. Keep Proxmox deep-link route selection on the shared settings-navigation boundary. `frontend-modern/src/components/Settings/settingsNavigationModel.ts` and `frontend-modern/src/components/Settings/useSettingsNavigation.ts` must treat the canonical PBS and PMG Proxmox deep links as agent-selection authority even though those URLs resolve to the shared `infrastructure-operations` tab. Reloading or remounting on a PBS or PMG deep link must not silently fall back to the PVE selector state.
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
import { Component, Show, createEffect, createMemo, createSignal } from 'solid-js';
|
||||
import { Component, For, Show, createEffect, createMemo, createSignal } from 'solid-js';
|
||||
import { useLocation, useNavigate } from '@solidjs/router';
|
||||
import { Dialog } from '@/components/shared/Dialog';
|
||||
import { presentationPolicyIsReadOnly } from '@/stores/sessionPresentationPolicy';
|
||||
import { AgentProfilesPanel } from './AgentProfilesPanel';
|
||||
import { AddSystemPicker, type AddSystemChoice } from './AddSystemPicker';
|
||||
import { ADD_SYSTEM_CHOICES, type AddSystemChoice } from './AddSystemPicker';
|
||||
import { ConnectionsTable, type ConnectionsTableHeaderAction } from './ConnectionsTable';
|
||||
import {
|
||||
buildInfrastructureSystemRows,
|
||||
|
|
@ -37,8 +37,7 @@ const InfrastructureWorkspaceContent: Component<InfrastructureWorkspaceProps> =
|
|||
|
||||
const activeView = createMemo(() => getInfrastructureWorkspaceViewFromPath(location.pathname));
|
||||
const readOnlyWorkspace = createMemo(() => presentationPolicyIsReadOnly());
|
||||
const [pickerOpen, setPickerOpen] = createSignal(false);
|
||||
const [profilesOpen, setProfilesOpen] = createSignal(false);
|
||||
const [showInstallProfiles, setShowInstallProfiles] = createSignal(false);
|
||||
|
||||
const rows = createMemo<InfrastructureSystemRow[]>(() =>
|
||||
buildInfrastructureSystemRows({
|
||||
|
|
@ -52,7 +51,7 @@ const InfrastructureWorkspaceContent: Component<InfrastructureWorkspaceProps> =
|
|||
: [
|
||||
{
|
||||
label: 'Add infrastructure',
|
||||
onSelect: () => setPickerOpen(true),
|
||||
onSelect: () => navigate(buildInfrastructureWorkspacePath('install'), { scroll: false }),
|
||||
tone: 'primary' as const,
|
||||
},
|
||||
],
|
||||
|
|
@ -68,28 +67,12 @@ const InfrastructureWorkspaceContent: Component<InfrastructureWorkspaceProps> =
|
|||
navigate(buildInfrastructureWorkspacePath('inventory'), { scroll: false });
|
||||
};
|
||||
const closeInstallWorkspace = () => {
|
||||
setShowInstallProfiles(false);
|
||||
navigate(buildInfrastructureWorkspacePath('inventory'), { scroll: false });
|
||||
};
|
||||
|
||||
const openProxmoxNode = (nodeKind: 'pve' | 'pbs' | 'pmg', nodeId: string) => {
|
||||
const nodes =
|
||||
nodeKind === 'pve'
|
||||
? props.pveNodes()
|
||||
: nodeKind === 'pbs'
|
||||
? props.pbsNodes()
|
||||
: props.pmgNodes();
|
||||
const node = nodes.find((candidate) => candidate.id === nodeId) ?? null;
|
||||
|
||||
props.onSelectAgent(nodeKind);
|
||||
props.setCurrentNodeType(nodeKind);
|
||||
props.setEditingNode(node);
|
||||
props.setModalResetKey((value) => value + 1);
|
||||
props.setShowNodeModal(true);
|
||||
navigate(proxmoxRouteForKind(nodeKind), { scroll: false });
|
||||
};
|
||||
|
||||
const handleAddSystem = (choice: AddSystemChoice) => {
|
||||
setPickerOpen(false);
|
||||
setShowInstallProfiles(false);
|
||||
|
||||
if (choice.kind === 'agent') {
|
||||
navigate(buildInfrastructureWorkspacePath('install'), { scroll: false });
|
||||
|
|
@ -97,18 +80,16 @@ const InfrastructureWorkspaceContent: Component<InfrastructureWorkspaceProps> =
|
|||
}
|
||||
|
||||
if (choice.kind === 'truenas') {
|
||||
props.trueNASSettings.openCreateDialog();
|
||||
navigate('/settings/infrastructure/platforms/truenas', { scroll: false });
|
||||
return;
|
||||
}
|
||||
|
||||
if (choice.kind === 'vmware') {
|
||||
props.vmwareSettings.openCreateDialog();
|
||||
navigate('/settings/infrastructure/platforms/vmware', { scroll: false });
|
||||
return;
|
||||
}
|
||||
|
||||
openProxmoxNode(choice.kind, '');
|
||||
navigate(proxmoxRouteForKind(choice.kind), { scroll: false });
|
||||
};
|
||||
|
||||
const handleManageAction = (action: SystemManageAction) => {
|
||||
|
|
@ -136,152 +117,126 @@ const InfrastructureWorkspaceContent: Component<InfrastructureWorkspaceProps> =
|
|||
if (activeView() === 'inventory') {
|
||||
return;
|
||||
}
|
||||
setPickerOpen(false);
|
||||
setProfilesOpen(false);
|
||||
if (activeView() !== 'install') {
|
||||
setShowInstallProfiles(false);
|
||||
}
|
||||
state.setExpandedRowKey(null);
|
||||
state.setSelectedIgnoredRowKey(null);
|
||||
});
|
||||
|
||||
return (
|
||||
<div class="space-y-8">
|
||||
<ConnectionsTable
|
||||
rows={rows}
|
||||
headerActions={headerActions()}
|
||||
onManageRow={(row) => handleManageAction(row.manage)}
|
||||
/>
|
||||
<Show when={activeView() === 'inventory'}>
|
||||
<ConnectionsTable
|
||||
rows={rows}
|
||||
headerActions={headerActions()}
|
||||
onManageRow={(row) => handleManageAction(row.manage)}
|
||||
/>
|
||||
</Show>
|
||||
|
||||
<AddSystemPicker
|
||||
isOpen={pickerOpen()}
|
||||
onClose={() => setPickerOpen(false)}
|
||||
onSelect={handleAddSystem}
|
||||
onManageProfiles={() => {
|
||||
setPickerOpen(false);
|
||||
setProfilesOpen(true);
|
||||
}}
|
||||
/>
|
||||
|
||||
<InfrastructureStopMonitoringDialog />
|
||||
|
||||
<Dialog
|
||||
isOpen={profilesOpen()}
|
||||
onClose={() => setProfilesOpen(false)}
|
||||
panelClass="h-[calc(100dvh-2rem)] w-full max-w-[1100px]"
|
||||
ariaLabel="Agent profiles"
|
||||
>
|
||||
<div class="flex h-full flex-col bg-surface">
|
||||
<div class="border-b border-border bg-surface-alt px-4 py-4 sm:px-6">
|
||||
<div class="flex items-start justify-between gap-4">
|
||||
<Show when={!readOnlyWorkspace() && activeView() === 'install'}>
|
||||
<div class="space-y-6">
|
||||
<div class="space-y-3">
|
||||
<button
|
||||
type="button"
|
||||
onClick={closeInstallWorkspace}
|
||||
class="inline-flex items-center gap-2 rounded-md border border-border px-3 py-1.5 text-sm font-medium text-base-content transition-colors hover:bg-surface-hover"
|
||||
>
|
||||
Back to monitored systems
|
||||
</button>
|
||||
<div class="flex flex-col gap-3 lg:flex-row lg:items-start lg:justify-between">
|
||||
<div class="space-y-1">
|
||||
<div class="text-[11px] font-semibold uppercase tracking-[0.18em] text-muted">
|
||||
Add infrastructure
|
||||
</div>
|
||||
<div class="text-xl font-semibold text-base-content">Choose what to connect</div>
|
||||
<div class="text-sm text-muted">
|
||||
Start with a host install or jump straight to an API-backed platform connection.
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setShowInstallProfiles((value) => !value)}
|
||||
class="inline-flex items-center rounded-md border border-border px-3 py-1.5 text-sm font-medium text-base-content transition-colors hover:bg-surface-hover"
|
||||
>
|
||||
{showInstallProfiles() ? 'Hide agent profiles' : 'Manage agent profiles'}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid gap-3 md:grid-cols-2 xl:grid-cols-3">
|
||||
<For each={ADD_SYSTEM_CHOICES}>
|
||||
{(choice) => (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => handleAddSystem(choice)}
|
||||
class={`flex h-full flex-col items-start justify-between gap-4 rounded-xl border px-4 py-4 text-left transition-colors ${
|
||||
choice.kind === 'agent'
|
||||
? 'border-blue-300 bg-blue-50/60 text-base-content dark:border-blue-700 dark:bg-blue-950/20'
|
||||
: 'border-border bg-surface hover:bg-surface-hover'
|
||||
}`}
|
||||
>
|
||||
<div class="space-y-1.5">
|
||||
<div class="text-sm font-semibold text-base-content">{choice.title}</div>
|
||||
<div class="text-xs text-muted">{choice.description}</div>
|
||||
</div>
|
||||
<span
|
||||
class={`inline-flex items-center rounded-full px-2 py-0.5 text-xs font-medium ${
|
||||
choice.method === 'api'
|
||||
? 'bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-100'
|
||||
: 'bg-emerald-100 text-emerald-800 dark:bg-emerald-900 dark:text-emerald-100'
|
||||
}`}
|
||||
>
|
||||
{choice.methodLabel}
|
||||
</span>
|
||||
</button>
|
||||
)}
|
||||
</For>
|
||||
</div>
|
||||
|
||||
<div class="rounded-xl border border-border bg-surface p-4 shadow-sm sm:p-6">
|
||||
<InfrastructureInstallerSection />
|
||||
</div>
|
||||
|
||||
<Show when={showInstallProfiles()}>
|
||||
<div class="rounded-xl border border-border bg-surface p-4 shadow-sm sm:p-6">
|
||||
<div class="mb-4 space-y-1">
|
||||
<div class="text-lg font-semibold text-base-content">Agent profiles</div>
|
||||
<div class="text-sm text-muted">
|
||||
Manage reusable install defaults for agent-based systems.
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setProfilesOpen(false)}
|
||||
class="rounded-md p-1 hover:bg-surface-hover hover:text-base-content"
|
||||
aria-label="Close"
|
||||
>
|
||||
<svg class="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M6 18L18 6M6 6l12 12"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
<AgentProfilesPanel />
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-1 overflow-y-auto p-4 sm:p-6">
|
||||
<AgentProfilesPanel />
|
||||
</div>
|
||||
</Show>
|
||||
</div>
|
||||
</Dialog>
|
||||
</Show>
|
||||
|
||||
<Dialog
|
||||
isOpen={!readOnlyWorkspace() && activeView() === 'platforms'}
|
||||
onClose={closeConnectionsWorkspace}
|
||||
panelClass="h-[calc(100dvh-2rem)] w-full max-w-[1280px]"
|
||||
ariaLabel="Platform connections"
|
||||
>
|
||||
<div class="flex h-full flex-col bg-surface">
|
||||
<div class="border-b border-border bg-surface-alt px-4 py-4 sm:px-6">
|
||||
<div class="flex items-start justify-between gap-4">
|
||||
<div class="space-y-1">
|
||||
<div class="text-[11px] font-semibold uppercase tracking-[0.18em] text-muted">
|
||||
Connections
|
||||
</div>
|
||||
<div class="text-lg font-semibold text-base-content">Platform connections</div>
|
||||
<div class="text-sm text-muted">
|
||||
Configure API-backed providers without leaving the infrastructure ledger.
|
||||
</div>
|
||||
<Show when={!readOnlyWorkspace() && activeView() === 'platforms'}>
|
||||
<div class="space-y-6">
|
||||
<div class="space-y-3">
|
||||
<button
|
||||
type="button"
|
||||
onClick={closeConnectionsWorkspace}
|
||||
class="inline-flex items-center gap-2 rounded-md border border-border px-3 py-1.5 text-sm font-medium text-base-content transition-colors hover:bg-surface-hover"
|
||||
>
|
||||
Back to monitored systems
|
||||
</button>
|
||||
<div class="space-y-1">
|
||||
<div class="text-[11px] font-semibold uppercase tracking-[0.18em] text-muted">
|
||||
Connections
|
||||
</div>
|
||||
<div class="text-xl font-semibold text-base-content">Platform connections</div>
|
||||
<div class="text-sm text-muted">
|
||||
Configure API-backed providers in a full workspace instead of a modal overlay.
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
onClick={closeConnectionsWorkspace}
|
||||
class="rounded-md p-1 hover:bg-surface-hover hover:text-base-content"
|
||||
aria-label="Close"
|
||||
>
|
||||
<svg class="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M6 18L18 6M6 6l12 12"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-1 overflow-y-auto p-4 sm:p-6">
|
||||
<PlatformConnectionsWorkspace {...props} />
|
||||
</div>
|
||||
<PlatformConnectionsWorkspace {...props} />
|
||||
</div>
|
||||
</Dialog>
|
||||
</Show>
|
||||
|
||||
<Dialog
|
||||
isOpen={!readOnlyWorkspace() && activeView() === 'install'}
|
||||
onClose={closeInstallWorkspace}
|
||||
panelClass="h-[calc(100dvh-2rem)] w-full max-w-[1280px]"
|
||||
ariaLabel="Install Pulse agent"
|
||||
>
|
||||
<div class="flex h-full flex-col bg-surface">
|
||||
<div class="border-b border-border bg-surface-alt px-4 py-4 sm:px-6">
|
||||
<div class="flex items-start justify-between gap-4">
|
||||
<div class="space-y-1">
|
||||
<div class="text-[11px] font-semibold uppercase tracking-[0.18em] text-muted">
|
||||
Install
|
||||
</div>
|
||||
<div class="text-lg font-semibold text-base-content">Install Pulse agent</div>
|
||||
<div class="text-sm text-muted">
|
||||
Generate Linux, macOS, FreeBSD, and Windows install commands from the same
|
||||
workspace.
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
onClick={closeInstallWorkspace}
|
||||
class="rounded-md p-1 hover:bg-surface-hover hover:text-base-content"
|
||||
aria-label="Close"
|
||||
>
|
||||
<svg class="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M6 18L18 6M6 6l12 12"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-1 overflow-y-auto p-4 sm:p-6">
|
||||
<InfrastructureInstallerSection />
|
||||
</div>
|
||||
</div>
|
||||
</Dialog>
|
||||
<InfrastructureStopMonitoringDialog />
|
||||
|
||||
<Dialog
|
||||
isOpen={Boolean(state.selectedActiveRow())}
|
||||
|
|
|
|||
|
|
@ -99,15 +99,11 @@ const reportingRow = (overrides: Partial<UnifiedAgentRow> = {}): UnifiedAgentRow
|
|||
...overrides,
|
||||
}) as UnifiedAgentRow;
|
||||
|
||||
const trueNASOpenCreateDialogSpy = vi.fn();
|
||||
const vmwareOpenCreateDialogSpy = vi.fn();
|
||||
const setShowNodeModalSpy = vi.fn();
|
||||
const setEditingNodeSpy = vi.fn();
|
||||
const setCurrentNodeTypeSpy = vi.fn();
|
||||
const setModalResetKeySpy = vi.fn();
|
||||
|
||||
const onSelectAgentSpy = vi.fn();
|
||||
|
||||
const baseProps = () =>
|
||||
({
|
||||
pveNodes: () => [{ id: 'pve-1', name: 'zeus', host: '10.0.0.1', type: 'pve', status: 'connected' }],
|
||||
|
|
@ -116,20 +112,20 @@ const baseProps = () =>
|
|||
agentStateResources: () => [],
|
||||
trueNASSettings: {
|
||||
connections: () => [{ id: 'tn-1', name: 'Tower NAS', host: '10.0.0.20', enabled: true }],
|
||||
openCreateDialog: trueNASOpenCreateDialogSpy,
|
||||
openCreateDialog: vi.fn(),
|
||||
closeDialog: vi.fn(),
|
||||
closeDeleteDialog: vi.fn(),
|
||||
openEditDialog: vi.fn(),
|
||||
},
|
||||
vmwareSettings: {
|
||||
connections: () => [{ id: 'vm-1', name: 'lab-vcenter', host: '10.0.0.30', enabled: true }],
|
||||
openCreateDialog: vmwareOpenCreateDialogSpy,
|
||||
openCreateDialog: vi.fn(),
|
||||
closeDialog: vi.fn(),
|
||||
closeDeleteDialog: vi.fn(),
|
||||
openEditDialog: vi.fn(),
|
||||
},
|
||||
selectedAgent: () => 'pve',
|
||||
onSelectAgent: onSelectAgentSpy,
|
||||
onSelectAgent: vi.fn(),
|
||||
setShowNodeModal: setShowNodeModalSpy,
|
||||
setEditingNode: setEditingNodeSpy,
|
||||
setCurrentNodeType: setCurrentNodeTypeSpy,
|
||||
|
|
@ -143,13 +139,10 @@ describe('InfrastructureWorkspace', () => {
|
|||
presentationPolicyIsReadOnlyMock.mockReturnValue(false);
|
||||
setExpandedRowKeySpy.mockReset();
|
||||
setSelectedIgnoredRowKeySpy.mockReset();
|
||||
trueNASOpenCreateDialogSpy.mockReset();
|
||||
vmwareOpenCreateDialogSpy.mockReset();
|
||||
setShowNodeModalSpy.mockReset();
|
||||
setEditingNodeSpy.mockReset();
|
||||
setCurrentNodeTypeSpy.mockReset();
|
||||
setModalResetKeySpy.mockReset();
|
||||
onSelectAgentSpy.mockReset();
|
||||
mockPathname = '/settings/infrastructure';
|
||||
mockActiveRows = [reportingRow()];
|
||||
mockIgnoredRows = [];
|
||||
|
|
@ -185,59 +178,16 @@ describe('InfrastructureWorkspace', () => {
|
|||
expect(screen.queryByText('lab-vcenter')).toBeNull();
|
||||
});
|
||||
|
||||
it('opens the add-system picker when the add button is clicked', () => {
|
||||
it('routes the add-infrastructure action to the install workspace', () => {
|
||||
renderWorkspace();
|
||||
|
||||
fireEvent.click(screen.getByRole('button', { name: /Add infrastructure/i }));
|
||||
|
||||
expect(screen.getByText('Install on a host')).toBeInTheDocument();
|
||||
expect(screen.getByText('Proxmox VE')).toBeInTheDocument();
|
||||
expect(screen.getByText('TrueNAS SCALE')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('routes the agent-host choice to the install section deep link', () => {
|
||||
renderWorkspace();
|
||||
fireEvent.click(screen.getByRole('button', { name: /Add infrastructure/i }));
|
||||
fireEvent.click(screen.getByText('Install on a host'));
|
||||
|
||||
expect(navigateSpy).toHaveBeenCalledWith('/settings/infrastructure/install', {
|
||||
scroll: false,
|
||||
});
|
||||
});
|
||||
|
||||
it('opens provider creation flows directly from the add-system picker', () => {
|
||||
renderWorkspace();
|
||||
fireEvent.click(screen.getByRole('button', { name: /Add infrastructure/i }));
|
||||
fireEvent.click(screen.getByText('TrueNAS SCALE'));
|
||||
|
||||
expect(trueNASOpenCreateDialogSpy).toHaveBeenCalledTimes(1);
|
||||
expect(navigateSpy).toHaveBeenCalledWith('/settings/infrastructure/platforms/truenas', {
|
||||
scroll: false,
|
||||
});
|
||||
|
||||
fireEvent.click(screen.getByRole('button', { name: /Add infrastructure/i }));
|
||||
fireEvent.click(screen.getByText('VMware vSphere or ESXi'));
|
||||
|
||||
expect(vmwareOpenCreateDialogSpy).toHaveBeenCalledTimes(1);
|
||||
expect(navigateSpy).toHaveBeenCalledWith('/settings/infrastructure/platforms/vmware', {
|
||||
scroll: false,
|
||||
});
|
||||
});
|
||||
|
||||
it('opens the proxmox node modal directly from the add-system picker', () => {
|
||||
renderWorkspace();
|
||||
fireEvent.click(screen.getByRole('button', { name: /Add infrastructure/i }));
|
||||
fireEvent.click(screen.getByText('Proxmox VE'));
|
||||
|
||||
expect(onSelectAgentSpy).toHaveBeenCalledWith('pve');
|
||||
expect(setCurrentNodeTypeSpy).toHaveBeenCalledWith('pve');
|
||||
expect(setEditingNodeSpy).toHaveBeenCalledWith(null);
|
||||
expect(setShowNodeModalSpy).toHaveBeenCalledWith(true);
|
||||
expect(navigateSpy).toHaveBeenCalledWith('/settings/infrastructure/platforms/proxmox/pve', {
|
||||
scroll: false,
|
||||
});
|
||||
});
|
||||
|
||||
it('opens reporting details from the top ledger in a drawer', () => {
|
||||
renderWorkspace();
|
||||
|
||||
|
|
@ -247,46 +197,71 @@ describe('InfrastructureWorkspace', () => {
|
|||
expect(screen.getByTestId('active-details')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('keeps the ledger visible and opens platform setup in a drawer on platform deep links', () => {
|
||||
it('renders platform connections as a full-page workspace on platform deep links', () => {
|
||||
mockPathname = '/settings/infrastructure/platforms/truenas';
|
||||
renderWorkspace();
|
||||
|
||||
expect(screen.getByRole('heading', { name: 'Monitored systems' })).toBeInTheDocument();
|
||||
expect(screen.queryByRole('heading', { name: 'Monitored systems' })).toBeNull();
|
||||
expect(screen.getByText('Platform connections')).toBeInTheDocument();
|
||||
expect(screen.getByTestId('platform-section')).toBeInTheDocument();
|
||||
expect(screen.queryByTestId('install-section')).toBeNull();
|
||||
});
|
||||
|
||||
it('keeps the ledger visible and opens install tools in a drawer on install deep links', () => {
|
||||
it('renders install tools as a full-page workspace on install deep links', () => {
|
||||
mockPathname = '/settings/infrastructure/install';
|
||||
renderWorkspace();
|
||||
|
||||
expect(screen.getByRole('heading', { name: 'Monitored systems' })).toBeInTheDocument();
|
||||
expect(screen.getByText('Install Pulse agent')).toBeInTheDocument();
|
||||
expect(screen.getByText('Choose what to connect')).toBeInTheDocument();
|
||||
expect(screen.getByText('Install on a host')).toBeInTheDocument();
|
||||
expect(screen.getByTestId('install-section')).toBeInTheDocument();
|
||||
expect(screen.queryByTestId('platform-section')).toBeNull();
|
||||
});
|
||||
|
||||
it('opens agent profiles from the add-infrastructure chooser secondary action', () => {
|
||||
it('opens agent profiles inline inside the install workspace', () => {
|
||||
mockPathname = '/settings/infrastructure/install';
|
||||
renderWorkspace();
|
||||
|
||||
fireEvent.click(screen.getByRole('button', { name: 'Add infrastructure' }));
|
||||
fireEvent.click(screen.getByRole('button', { name: 'Manage agent profiles' }));
|
||||
|
||||
expect(screen.getByTestId('agent-profiles')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('closes the platform drawer back to the ledger route', () => {
|
||||
it('returns from platform connections to the monitored systems route', () => {
|
||||
mockPathname = '/settings/infrastructure/platforms/truenas';
|
||||
renderWorkspace();
|
||||
|
||||
fireEvent.click(screen.getAllByRole('button', { name: 'Close' })[0]);
|
||||
fireEvent.click(screen.getByRole('button', { name: 'Back to monitored systems' }));
|
||||
|
||||
expect(navigateSpy).toHaveBeenCalledWith('/settings/infrastructure', {
|
||||
scroll: false,
|
||||
});
|
||||
});
|
||||
|
||||
it('routes infrastructure choices through full-page setup routes instead of auto-opening dialogs', () => {
|
||||
mockPathname = '/settings/infrastructure/install';
|
||||
renderWorkspace();
|
||||
|
||||
fireEvent.click(screen.getByRole('button', { name: /TrueNAS SCALE/i }));
|
||||
expect(navigateSpy).toHaveBeenCalledWith('/settings/infrastructure/platforms/truenas', {
|
||||
scroll: false,
|
||||
});
|
||||
|
||||
fireEvent.click(screen.getByRole('button', { name: /VMware vSphere or ESXi/i }));
|
||||
expect(navigateSpy).toHaveBeenCalledWith('/settings/infrastructure/platforms/vmware', {
|
||||
scroll: false,
|
||||
});
|
||||
|
||||
fireEvent.click(screen.getByRole('button', { name: /Proxmox VE/i }));
|
||||
expect(navigateSpy).toHaveBeenCalledWith('/settings/infrastructure/platforms/proxmox/pve', {
|
||||
scroll: false,
|
||||
});
|
||||
|
||||
expect(setShowNodeModalSpy).not.toHaveBeenCalled();
|
||||
expect(setEditingNodeSpy).not.toHaveBeenCalled();
|
||||
expect(setCurrentNodeTypeSpy).not.toHaveBeenCalled();
|
||||
expect(setModalResetKeySpy).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('collapses read-only sessions back to inventory and hides setup sections', () => {
|
||||
presentationPolicyIsReadOnlyMock.mockReturnValue(true);
|
||||
mockPathname = '/settings/infrastructure/install';
|
||||
|
|
|
|||
|
|
@ -903,7 +903,7 @@ describe('Settings architecture guardrails', () => {
|
|||
);
|
||||
expect(infrastructureWorkspaceSource).not.toContain('<Subtabs');
|
||||
expect(infrastructureWorkspaceSource).toContain('Platform connections');
|
||||
expect(infrastructureWorkspaceSource).toContain('Install Pulse agent');
|
||||
expect(infrastructureWorkspaceSource).toContain('Choose what to connect');
|
||||
expect(infrastructureWorkspaceSource).toContain('activeView() === \'platforms\'');
|
||||
expect(infrastructureWorkspaceSource).toContain('activeView() === \'install\'');
|
||||
expect(infrastructureWorkspaceSource).not.toContain("activeView() === 'operations'");
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue