mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-09-11 22:12:23 +00:00
v6(settings): phase 6b — TrueNAS and VMware credential slots inside ConnectionEditor
Extracts the inner form bodies from TrueNASSettingsPanel and VMwareSettingsPanel into dedicated credential slot components mounted inline under ConnectionEditor. When the user adds a TrueNAS or VMware connection, the editor now shows a clean form instead of re-rendering the full platform panel (which listed every other saved connection before exposing the add dialog). State still lives on TrueNASSettingsPanelState and VMwareSettingsPanelState, so save, test, preview, and admission-preview behavior remain identical. The slots prime the existing create-dialog state on mount and close it on cancel; save completion is detected via the state's dialogOpen transition.
This commit is contained in:
@@ -331,11 +331,19 @@ an add-only capacity posture.
|
||||
`NodeModalAuthenticationSection`, `NodeModalMonitoringSection`, and
|
||||
`NodeModalStatusFooter` primitives inline under the editor — dropping
|
||||
the Dialog wrapper and the surrounding discovery/configured-nodes
|
||||
workspace. The add flow must not reintroduce the full Proxmox
|
||||
workspace (discovery card, configured nodes table, node-modal stack)
|
||||
into the credential slot, because that previously showed the
|
||||
ledger-of-other-systems in the middle of entering one system's
|
||||
credentials.
|
||||
workspace. For TrueNAS and VMware, the credential slots are
|
||||
`frontend-modern/src/components/Settings/ConnectionEditor/CredentialSlots/TrueNASCredentialSlot.tsx`
|
||||
and
|
||||
`frontend-modern/src/components/Settings/ConnectionEditor/CredentialSlots/VMwareCredentialSlot.tsx`;
|
||||
they extract the inner form bodies from the per-type panels and
|
||||
render them inline under the editor while still driving the existing
|
||||
`TrueNASSettingsPanelState` and `VMwareSettingsPanelState` APIs for
|
||||
save, test, preview, and admission-preview behavior. The add flow
|
||||
must not reintroduce the full per-type workspace (Proxmox discovery
|
||||
card, configured nodes table, node-modal stack; TrueNAS/VMware
|
||||
connection list with headers and row actions) into the credential
|
||||
slot, because that previously showed the ledger-of-other-systems in
|
||||
the middle of entering one system's credentials.
|
||||
|
||||
## Forbidden Paths
|
||||
|
||||
|
||||
@@ -246,9 +246,15 @@ work extends shared components instead of creating new local variants.
|
||||
`NodeModalAuthenticationSection`, `NodeModalMonitoringSection`, and
|
||||
`NodeModalStatusFooter` inline under the editor shell rather than
|
||||
embedding the full Proxmox workspace (discovery card, configured
|
||||
nodes table, delete dialog, node modal stack). Showing a ledger of
|
||||
other systems inside the credential slot is exactly the ledger-inside-
|
||||
editor drift this contract forbids.
|
||||
nodes table, delete dialog, node modal stack). For TrueNAS and VMware
|
||||
the credential slots are
|
||||
`frontend-modern/src/components/Settings/ConnectionEditor/CredentialSlots/TrueNASCredentialSlot.tsx`
|
||||
and
|
||||
`frontend-modern/src/components/Settings/ConnectionEditor/CredentialSlots/VMwareCredentialSlot.tsx`
|
||||
and they must render only the connection form body inline under the
|
||||
editor shell — no connection list, no row actions, no surrounding
|
||||
panel chrome. Showing a ledger of other systems inside the credential
|
||||
slot is exactly the ledger-inside-editor drift this contract forbids.
|
||||
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.
|
||||
7. Keep shared storage feature presenters on canonical platform truth. When reusable storage presenters under `frontend-modern/src/features/storageBackups/` classify canonical resources for the shared storage route, API-backed virtualization datastores such as VMware must stay inventory-only datastores instead of inheriting PBS-specific backup-repository or protected-target copy from older fallback branches.
|
||||
8. Keep shared source/platform vocabulary on the governed manifest boundary. `frontend-modern/src/utils/platformSupportManifest.generated.ts` must be the tracked frontend projection of `docs/release-control/v6/internal/PLATFORM_SUPPORT_MANIFEST.json`, `frontend-modern/src/utils/platformSupportManifest.ts`, `frontend-modern/src/utils/sourcePlatforms.ts`, and `frontend-modern/src/utils/sourcePlatformOptions.ts` must consume that generated projection instead of embedding divergent future-label lists, setup/onboarding path allowlists, or presentation-only guesses, and `frontend-modern/scripts/canonical-platform-audit.mjs` must fail when the generated projection drifts from the governed manifest.
|
||||
|
||||
+275
@@ -0,0 +1,275 @@
|
||||
import { Component, Show, createEffect, onMount } from 'solid-js';
|
||||
import {
|
||||
formCheckbox,
|
||||
formControl,
|
||||
formField,
|
||||
formHelpText,
|
||||
formLabel,
|
||||
formSelect,
|
||||
} from '@/components/shared/Form';
|
||||
import { MonitoredSystemAdmissionPreview } from '../../MonitoredSystemAdmissionPreview';
|
||||
import type { TrueNASSettingsPanelState } from '../../useTrueNASSettingsPanelState';
|
||||
|
||||
const buttonClass =
|
||||
'inline-flex min-h-10 sm:min-h-9 items-center justify-center rounded-md border border-border px-3 py-2 text-sm font-medium text-base-content transition-colors hover:bg-surface-hover disabled:cursor-not-allowed disabled:opacity-60';
|
||||
const primaryButtonClass =
|
||||
'inline-flex min-h-10 sm:min-h-9 items-center justify-center rounded-md bg-blue-600 px-3 py-2 text-sm font-medium text-white transition-colors hover:bg-blue-700 disabled:cursor-not-allowed disabled:opacity-60';
|
||||
|
||||
export interface TrueNASCredentialSlotProps {
|
||||
state: TrueNASSettingsPanelState;
|
||||
onCancel: () => void;
|
||||
onSaved: () => void;
|
||||
}
|
||||
|
||||
export const TrueNASCredentialSlot: Component<TrueNASCredentialSlotProps> = (props) => {
|
||||
let primed = false;
|
||||
|
||||
onMount(() => {
|
||||
if (!props.state.dialogOpen()) {
|
||||
props.state.openCreateDialog();
|
||||
}
|
||||
primed = true;
|
||||
});
|
||||
|
||||
createEffect(() => {
|
||||
const open = props.state.dialogOpen();
|
||||
if (primed && !open && !props.state.saving()) {
|
||||
props.onSaved();
|
||||
}
|
||||
});
|
||||
|
||||
const handleCancel = () => {
|
||||
props.state.closeDialog();
|
||||
props.onCancel();
|
||||
};
|
||||
|
||||
return (
|
||||
<div class="space-y-6">
|
||||
<Show when={props.state.featureDisabled()}>
|
||||
<div class="rounded-md border border-amber-300 bg-amber-50 px-4 py-3 text-sm text-amber-900 dark:border-amber-700 dark:bg-amber-950/40 dark:text-amber-200">
|
||||
{props.state.featureDisabledMessage() ||
|
||||
'TrueNAS connections are disabled for this Pulse tier.'}
|
||||
</div>
|
||||
</Show>
|
||||
|
||||
<Show when={!props.state.featureDisabled()}>
|
||||
<div class="grid gap-4 sm:grid-cols-2">
|
||||
<label class={formField}>
|
||||
<span class={formLabel}>Name</span>
|
||||
<input
|
||||
class={formControl}
|
||||
value={props.state.form().name}
|
||||
onInput={(event) => props.state.updateForm({ name: event.currentTarget.value })}
|
||||
placeholder="tower"
|
||||
/>
|
||||
</label>
|
||||
<label class={formField}>
|
||||
<span class={formLabel}>Host</span>
|
||||
<input
|
||||
class={formControl}
|
||||
value={props.state.form().host}
|
||||
onInput={(event) => props.state.updateForm({ host: event.currentTarget.value })}
|
||||
placeholder="truenas.local"
|
||||
/>
|
||||
</label>
|
||||
<label class={formField}>
|
||||
<span class={formLabel}>Port</span>
|
||||
<input
|
||||
class={formControl}
|
||||
inputMode="numeric"
|
||||
value={props.state.form().port}
|
||||
onInput={(event) => props.state.updateForm({ port: event.currentTarget.value })}
|
||||
placeholder={props.state.form().useHttps ? '443' : '80'}
|
||||
/>
|
||||
</label>
|
||||
<label class={formField}>
|
||||
<span class={formLabel}>Poll interval (seconds)</span>
|
||||
<input
|
||||
class={formControl}
|
||||
inputMode="numeric"
|
||||
value={props.state.form().pollIntervalSeconds}
|
||||
onInput={(event) =>
|
||||
props.state.updateForm({ pollIntervalSeconds: event.currentTarget.value })
|
||||
}
|
||||
placeholder="60"
|
||||
/>
|
||||
<span class={formHelpText}>
|
||||
How often Pulse should refresh this TrueNAS connection.
|
||||
</span>
|
||||
</label>
|
||||
<label class={formField}>
|
||||
<span class={formLabel}>Authentication</span>
|
||||
<select
|
||||
class={formSelect}
|
||||
value={props.state.form().authMode}
|
||||
onChange={(event) =>
|
||||
props.state.updateForm({
|
||||
authMode: event.currentTarget.value as 'apiKey' | 'userpass',
|
||||
apiKey: '',
|
||||
password: '',
|
||||
})
|
||||
}
|
||||
>
|
||||
<option value="apiKey">API key</option>
|
||||
<option value="userpass">Username and password</option>
|
||||
</select>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<Show when={props.state.form().authMode === 'apiKey'}>
|
||||
<label class={formField}>
|
||||
<span class={formLabel}>API key</span>
|
||||
<input
|
||||
class={formControl}
|
||||
type="password"
|
||||
value={props.state.form().apiKey}
|
||||
onInput={(event) => props.state.updateForm({ apiKey: event.currentTarget.value })}
|
||||
placeholder={
|
||||
props.state.form().hasStoredApiKey
|
||||
? 'Saved API key retained unless replaced'
|
||||
: ''
|
||||
}
|
||||
/>
|
||||
<Show when={props.state.form().hasStoredApiKey}>
|
||||
<span class={formHelpText}>Leave this blank to keep the saved API key.</span>
|
||||
</Show>
|
||||
</label>
|
||||
</Show>
|
||||
|
||||
<Show when={props.state.form().authMode === 'userpass'}>
|
||||
<div class="grid gap-4 sm:grid-cols-2">
|
||||
<label class={formField}>
|
||||
<span class={formLabel}>Username</span>
|
||||
<input
|
||||
class={formControl}
|
||||
value={props.state.form().username}
|
||||
onInput={(event) =>
|
||||
props.state.updateForm({ username: event.currentTarget.value })
|
||||
}
|
||||
placeholder="admin"
|
||||
/>
|
||||
</label>
|
||||
<label class={formField}>
|
||||
<span class={formLabel}>Password</span>
|
||||
<input
|
||||
class={formControl}
|
||||
type="password"
|
||||
value={props.state.form().password}
|
||||
onInput={(event) =>
|
||||
props.state.updateForm({ password: event.currentTarget.value })
|
||||
}
|
||||
placeholder={
|
||||
props.state.form().hasStoredPassword
|
||||
? 'Saved password retained unless replaced'
|
||||
: ''
|
||||
}
|
||||
/>
|
||||
<Show when={props.state.form().hasStoredPassword}>
|
||||
<span class={formHelpText}>Leave this blank to keep the saved password.</span>
|
||||
</Show>
|
||||
</label>
|
||||
</div>
|
||||
</Show>
|
||||
|
||||
<div class="grid gap-4 sm:grid-cols-2">
|
||||
<label class={formField}>
|
||||
<span class={formLabel}>TLS fingerprint</span>
|
||||
<input
|
||||
class={formControl}
|
||||
value={props.state.form().fingerprint}
|
||||
onInput={(event) =>
|
||||
props.state.updateForm({ fingerprint: event.currentTarget.value })
|
||||
}
|
||||
placeholder="Optional SHA256 fingerprint"
|
||||
/>
|
||||
<span class={formHelpText}>Optional certificate pin for HTTPS connections.</span>
|
||||
</label>
|
||||
<div class="space-y-3 rounded-md border border-border bg-surface-alt px-4 py-3">
|
||||
<label class="flex items-center gap-3">
|
||||
<input
|
||||
type="checkbox"
|
||||
class={formCheckbox}
|
||||
checked={props.state.form().useHttps}
|
||||
onChange={(event) =>
|
||||
props.state.updateForm({ useHttps: event.currentTarget.checked })
|
||||
}
|
||||
/>
|
||||
<span class="text-sm text-base-content">Use HTTPS</span>
|
||||
</label>
|
||||
<label class="flex items-center gap-3">
|
||||
<input
|
||||
type="checkbox"
|
||||
class={formCheckbox}
|
||||
checked={props.state.form().insecureSkipVerify}
|
||||
onChange={(event) =>
|
||||
props.state.updateForm({ insecureSkipVerify: event.currentTarget.checked })
|
||||
}
|
||||
/>
|
||||
<span class="text-sm text-base-content">Skip TLS verification</span>
|
||||
</label>
|
||||
<label class="flex items-center gap-3">
|
||||
<input
|
||||
type="checkbox"
|
||||
class={formCheckbox}
|
||||
checked={props.state.form().enabled}
|
||||
onChange={(event) =>
|
||||
props.state.updateForm({ enabled: event.currentTarget.checked })
|
||||
}
|
||||
/>
|
||||
<span class="text-sm text-base-content">Enable polling immediately</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<MonitoredSystemAdmissionPreview
|
||||
preview={props.state.monitoredSystemPreview()}
|
||||
loading={props.state.previewing()}
|
||||
error={props.state.monitoredSystemPreviewError()}
|
||||
errorTitle={props.state.monitoredSystemPreviewErrorTitle()}
|
||||
/>
|
||||
|
||||
<div class="flex flex-col-reverse gap-2 sm:flex-row sm:justify-end">
|
||||
<button
|
||||
type="button"
|
||||
class={buttonClass}
|
||||
onClick={handleCancel}
|
||||
disabled={props.state.saving() || props.state.testing()}
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class={buttonClass}
|
||||
onClick={() => void props.state.testCurrentForm()}
|
||||
disabled={props.state.saving() || props.state.testing()}
|
||||
>
|
||||
{props.state.testing() ? 'Testing…' : 'Test connection'}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class={buttonClass}
|
||||
onClick={() => void props.state.previewCurrentForm()}
|
||||
disabled={
|
||||
props.state.saving() || props.state.testing() || props.state.previewing()
|
||||
}
|
||||
>
|
||||
{props.state.previewing() ? 'Previewing…' : 'Preview impact'}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class={primaryButtonClass}
|
||||
onClick={() => void props.state.saveCurrentForm()}
|
||||
disabled={
|
||||
props.state.saving() ||
|
||||
props.state.testing() ||
|
||||
props.state.previewing() ||
|
||||
props.state.monitoredSystemAdmissionSaveBlocked()
|
||||
}
|
||||
>
|
||||
{props.state.saving() ? 'Adding…' : 'Add connection'}
|
||||
</button>
|
||||
</div>
|
||||
</Show>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
+213
@@ -0,0 +1,213 @@
|
||||
import { Component, Show, createEffect, onMount } from 'solid-js';
|
||||
import ShieldAlert from 'lucide-solid/icons/shield-alert';
|
||||
import { CalloutCard } from '@/components/shared/CalloutCard';
|
||||
import {
|
||||
formCheckbox,
|
||||
formControl,
|
||||
formField,
|
||||
formHelpText,
|
||||
formLabel,
|
||||
} from '@/components/shared/Form';
|
||||
import { MonitoredSystemAdmissionPreview } from '../../MonitoredSystemAdmissionPreview';
|
||||
import type { VMwareSettingsPanelState } from '../../useVMwareSettingsPanelState';
|
||||
|
||||
const buttonClass =
|
||||
'inline-flex min-h-10 sm:min-h-9 items-center justify-center rounded-md border border-border px-3 py-2 text-sm font-medium text-base-content transition-colors hover:bg-surface-hover disabled:cursor-not-allowed disabled:opacity-60';
|
||||
const primaryButtonClass =
|
||||
'inline-flex min-h-10 sm:min-h-9 items-center justify-center rounded-md bg-blue-600 px-3 py-2 text-sm font-medium text-white transition-colors hover:bg-blue-700 disabled:cursor-not-allowed disabled:opacity-60';
|
||||
|
||||
export interface VMwareCredentialSlotProps {
|
||||
state: VMwareSettingsPanelState;
|
||||
onCancel: () => void;
|
||||
onSaved: () => void;
|
||||
}
|
||||
|
||||
export const VMwareCredentialSlot: Component<VMwareCredentialSlotProps> = (props) => {
|
||||
let primed = false;
|
||||
|
||||
onMount(() => {
|
||||
if (!props.state.dialogOpen()) {
|
||||
props.state.openCreateDialog();
|
||||
}
|
||||
primed = true;
|
||||
});
|
||||
|
||||
createEffect(() => {
|
||||
const open = props.state.dialogOpen();
|
||||
if (primed && !open && !props.state.saving()) {
|
||||
props.onSaved();
|
||||
}
|
||||
});
|
||||
|
||||
const handleCancel = () => {
|
||||
props.state.closeDialog();
|
||||
props.onCancel();
|
||||
};
|
||||
|
||||
return (
|
||||
<div class="space-y-6">
|
||||
<Show when={props.state.featureDisabled()}>
|
||||
<div class="rounded-md border border-amber-300 bg-amber-50 px-4 py-3 text-sm text-amber-900 dark:border-amber-700 dark:bg-amber-950/40 dark:text-amber-200">
|
||||
{props.state.featureDisabledMessage() ||
|
||||
'VMware connections are disabled for this Pulse tier.'}
|
||||
</div>
|
||||
</Show>
|
||||
|
||||
<Show when={!props.state.featureDisabled()}>
|
||||
<Show when={props.state.connectionFailure()}>
|
||||
{(failure) => (
|
||||
<CalloutCard
|
||||
data-testid="vmware-connection-test-feedback"
|
||||
tone={failure().tone}
|
||||
title={failure().title}
|
||||
description={
|
||||
<>
|
||||
<p>{failure().message}</p>
|
||||
<Show when={failure().guidance}>
|
||||
<p class="mt-2">{failure().guidance}</p>
|
||||
</Show>
|
||||
</>
|
||||
}
|
||||
icon={<ShieldAlert class="h-5 w-5" />}
|
||||
/>
|
||||
)}
|
||||
</Show>
|
||||
|
||||
<div class="grid gap-4 sm:grid-cols-2">
|
||||
<label class={formField}>
|
||||
<span class={formLabel}>Name</span>
|
||||
<input
|
||||
class={formControl}
|
||||
value={props.state.form().name}
|
||||
onInput={(event) => props.state.updateForm({ name: event.currentTarget.value })}
|
||||
placeholder="lab-vcenter"
|
||||
/>
|
||||
</label>
|
||||
<label class={formField}>
|
||||
<span class={formLabel}>Host</span>
|
||||
<input
|
||||
class={formControl}
|
||||
value={props.state.form().host}
|
||||
onInput={(event) => props.state.updateForm({ host: event.currentTarget.value })}
|
||||
placeholder="vcsa.lab.local"
|
||||
/>
|
||||
</label>
|
||||
<label class={formField}>
|
||||
<span class={formLabel}>Port</span>
|
||||
<input
|
||||
class={formControl}
|
||||
inputMode="numeric"
|
||||
value={props.state.form().port}
|
||||
onInput={(event) => props.state.updateForm({ port: event.currentTarget.value })}
|
||||
placeholder="443"
|
||||
/>
|
||||
</label>
|
||||
<label class={formField}>
|
||||
<span class={formLabel}>Username</span>
|
||||
<input
|
||||
class={formControl}
|
||||
value={props.state.form().username}
|
||||
onInput={(event) =>
|
||||
props.state.updateForm({ username: event.currentTarget.value })
|
||||
}
|
||||
placeholder="administrator@vsphere.local"
|
||||
/>
|
||||
</label>
|
||||
<label class={`${formField} sm:col-span-2`}>
|
||||
<span class={formLabel}>Password</span>
|
||||
<input
|
||||
class={formControl}
|
||||
type="password"
|
||||
value={props.state.form().password}
|
||||
onInput={(event) =>
|
||||
props.state.updateForm({ password: event.currentTarget.value })
|
||||
}
|
||||
placeholder={
|
||||
props.state.form().hasStoredPassword
|
||||
? 'Saved password retained unless replaced'
|
||||
: ''
|
||||
}
|
||||
/>
|
||||
<Show when={props.state.form().hasStoredPassword}>
|
||||
<span class={formHelpText}>Leave this blank to keep the saved password.</span>
|
||||
</Show>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="space-y-3 rounded-md border border-border bg-surface-alt px-4 py-3">
|
||||
<label class="flex items-center gap-3">
|
||||
<input
|
||||
type="checkbox"
|
||||
class={formCheckbox}
|
||||
checked={props.state.form().insecureSkipVerify}
|
||||
onChange={(event) =>
|
||||
props.state.updateForm({ insecureSkipVerify: event.currentTarget.checked })
|
||||
}
|
||||
/>
|
||||
<span class="text-sm text-base-content">Skip TLS verification</span>
|
||||
</label>
|
||||
<label class="flex items-center gap-3">
|
||||
<input
|
||||
type="checkbox"
|
||||
class={formCheckbox}
|
||||
checked={props.state.form().enabled}
|
||||
onChange={(event) =>
|
||||
props.state.updateForm({ enabled: event.currentTarget.checked })
|
||||
}
|
||||
/>
|
||||
<span class="text-sm text-base-content">Enable this vCenter connection</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<MonitoredSystemAdmissionPreview
|
||||
preview={props.state.monitoredSystemPreview()}
|
||||
loading={props.state.previewing()}
|
||||
error={props.state.monitoredSystemPreviewError()}
|
||||
errorTitle={props.state.monitoredSystemPreviewErrorTitle()}
|
||||
/>
|
||||
|
||||
<div class="flex flex-col-reverse gap-2 sm:flex-row sm:justify-end">
|
||||
<button
|
||||
type="button"
|
||||
class={buttonClass}
|
||||
onClick={handleCancel}
|
||||
disabled={props.state.saving() || props.state.testing()}
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class={buttonClass}
|
||||
onClick={() => void props.state.testCurrentForm()}
|
||||
disabled={props.state.saving() || props.state.testing()}
|
||||
>
|
||||
{props.state.testing() ? 'Testing…' : 'Test connection'}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class={buttonClass}
|
||||
onClick={() => void props.state.previewCurrentForm()}
|
||||
disabled={
|
||||
props.state.saving() || props.state.testing() || props.state.previewing()
|
||||
}
|
||||
>
|
||||
{props.state.previewing() ? 'Previewing…' : 'Preview impact'}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class={primaryButtonClass}
|
||||
onClick={() => void props.state.saveCurrentForm()}
|
||||
disabled={
|
||||
props.state.saving() ||
|
||||
props.state.testing() ||
|
||||
props.state.previewing() ||
|
||||
props.state.monitoredSystemAdmissionSaveBlocked()
|
||||
}
|
||||
>
|
||||
{props.state.saving() ? 'Adding…' : 'Add connection'}
|
||||
</button>
|
||||
</div>
|
||||
</Show>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -11,13 +11,13 @@ import {
|
||||
} from './connectionsTableModel';
|
||||
import { ConnectionEditor } from './ConnectionEditor/ConnectionEditor';
|
||||
import { NodeCredentialSlot } from './ConnectionEditor/CredentialSlots/NodeCredentialSlot';
|
||||
import { TrueNASCredentialSlot } from './ConnectionEditor/CredentialSlots/TrueNASCredentialSlot';
|
||||
import { VMwareCredentialSlot } from './ConnectionEditor/CredentialSlots/VMwareCredentialSlot';
|
||||
import type { ConnectionType } from '@/api/connections';
|
||||
import { InfrastructureActiveRowDetails } from './InfrastructureActiveRowDetails';
|
||||
import { InfrastructureInstallerSection } from './InfrastructureInstallerSection';
|
||||
import { InfrastructureIgnoredRowDetails } from './InfrastructureIgnoredRowDetails';
|
||||
import { InfrastructureStopMonitoringDialog } from './InfrastructureStopMonitoringDialog';
|
||||
import { TrueNASSettingsPanel } from './TrueNASSettingsPanel';
|
||||
import { VMwareSettingsPanel } from './VMwareSettingsPanel';
|
||||
import {
|
||||
buildInfrastructureWorkspacePath,
|
||||
deriveAddStepFromLegacyPath,
|
||||
@@ -178,9 +178,21 @@ const InfrastructureWorkspaceContent: Component<InfrastructureWorkspaceProps> =
|
||||
case 'pmg':
|
||||
return renderNodeSlot(type);
|
||||
case 'truenas':
|
||||
return <TrueNASSettingsPanel state={props.trueNASSettings} />;
|
||||
return (
|
||||
<TrueNASCredentialSlot
|
||||
state={props.trueNASSettings}
|
||||
onCancel={exitAddMode}
|
||||
onSaved={exitAddMode}
|
||||
/>
|
||||
);
|
||||
case 'vmware':
|
||||
return <VMwareSettingsPanel state={props.vmwareSettings} />;
|
||||
return (
|
||||
<VMwareCredentialSlot
|
||||
state={props.vmwareSettings}
|
||||
onCancel={exitAddMode}
|
||||
onSaved={exitAddMode}
|
||||
/>
|
||||
);
|
||||
case 'agent':
|
||||
return (
|
||||
<div class="space-y-4">
|
||||
|
||||
@@ -62,12 +62,12 @@ vi.mock('../ConnectionEditor/CredentialSlots/NodeCredentialSlot', () => ({
|
||||
),
|
||||
}));
|
||||
|
||||
vi.mock('../TrueNASSettingsPanel', () => ({
|
||||
TrueNASSettingsPanel: () => <div data-testid="truenas-section">truenas</div>,
|
||||
vi.mock('../ConnectionEditor/CredentialSlots/TrueNASCredentialSlot', () => ({
|
||||
TrueNASCredentialSlot: () => <div data-testid="truenas-section">truenas</div>,
|
||||
}));
|
||||
|
||||
vi.mock('../VMwareSettingsPanel', () => ({
|
||||
VMwareSettingsPanel: () => <div data-testid="vmware-section">vmware</div>,
|
||||
vi.mock('../ConnectionEditor/CredentialSlots/VMwareCredentialSlot', () => ({
|
||||
VMwareCredentialSlot: () => <div data-testid="vmware-section">vmware</div>,
|
||||
}));
|
||||
|
||||
vi.mock('../InfrastructureActiveRowDetails', () => ({
|
||||
|
||||
Reference in New Issue
Block a user