mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-10-19 10:33:29 +00:00
v6(settings): phase 6a — PVE/PBS/PMG credential slot inside ConnectionEditor
Replaces the ProxmoxSettingsPanel stopgap that rendered an entire Proxmox workspace (discovery card, configured nodes table, node modal stack) inside the add-connection credential slot. The add flow now mounts NodeCredentialSlot, which drops the Dialog chrome and reuses the NodeModalBasicInfoSection / AuthenticationSection / MonitoringSection / StatusFooter primitives directly so the user sees a fresh credentials form — not the configured-nodes table they just came from. Save dispatch still routes through the existing per-type saveNode plumbing; the unified form is additive and does not change backend contracts.
This commit is contained in:
parent
a1f8dbe8f9
commit
4a587ba544
5 changed files with 109 additions and 23 deletions
|
|
@ -321,11 +321,21 @@ an add-only capacity posture.
|
|||
infrastructure workspace in place, headed by a back-to-systems
|
||||
affordance. The editor's probe step calls the aggregator probe
|
||||
endpoint and dispatches the detected or manually-selected type into a
|
||||
credential slot that renders the existing per-type panel; it must not
|
||||
bypass the probe endpoint or fabricate probe candidates, and the
|
||||
agent credential slot must continue to reach
|
||||
credential slot; it must not bypass the probe endpoint or fabricate
|
||||
probe candidates, and the agent credential slot must continue to reach
|
||||
`InfrastructureInstallerSection.tsx` so install handoffs remain on the
|
||||
canonical unified-agent install path.
|
||||
canonical unified-agent install path. For PVE, PBS, and PMG, the
|
||||
credential slot is
|
||||
`frontend-modern/src/components/Settings/ConnectionEditor/CredentialSlots/NodeCredentialSlot.tsx`,
|
||||
which reuses the existing `NodeModalBasicInfoSection`,
|
||||
`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.
|
||||
|
||||
## Forbidden Paths
|
||||
|
||||
|
|
|
|||
|
|
@ -234,13 +234,21 @@ work extends shared components instead of creating new local variants.
|
|||
`frontend-modern/src/components/Settings/ConnectionEditor/ConnectionEditor.tsx`,
|
||||
not through a per-type picker or per-type step tree. The editor's probe
|
||||
step is the canonical entry point; credential slots are dispatched by the
|
||||
detected or manually-selected type and must reach the existing per-type
|
||||
settings panel rather than diverging into a parallel add surface. The
|
||||
add flow must render inline in place of the inventory table, not as a
|
||||
right-side drawer, centered modal, or other overlay chrome — placing
|
||||
isolated per-type forms behind a common drawer is re-wrapping, and the
|
||||
unified add surface is specifically not allowed to reintroduce that
|
||||
pattern.
|
||||
detected or manually-selected type and must reach an inline credential
|
||||
form rather than diverging into a parallel add surface. The add flow
|
||||
must render inline in place of the inventory table, not as a right-side
|
||||
drawer, centered modal, or other overlay chrome — placing isolated
|
||||
per-type forms behind a common drawer is re-wrapping, and the unified
|
||||
add surface is specifically not allowed to reintroduce that pattern.
|
||||
For PVE, PBS, and PMG, the credential slot is
|
||||
`frontend-modern/src/components/Settings/ConnectionEditor/CredentialSlots/NodeCredentialSlot.tsx`
|
||||
and it must compose `NodeModalBasicInfoSection`,
|
||||
`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.
|
||||
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.
|
||||
|
|
|
|||
|
|
@ -0,0 +1,66 @@
|
|||
import { Component } from 'solid-js';
|
||||
import type { NodeConfig, NodeConfigWithStatus } from '@/types/nodes';
|
||||
import { NodeModalAuthenticationSection } from '@/components/Settings/NodeModalAuthenticationSection';
|
||||
import { NodeModalBasicInfoSection } from '@/components/Settings/NodeModalBasicInfoSection';
|
||||
import { NodeModalMonitoringSection } from '@/components/Settings/NodeModalMonitoringSection';
|
||||
import { NodeModalStatusFooter } from '@/components/Settings/NodeModalStatusFooter';
|
||||
import { useNodeModalState } from '@/components/Settings/useNodeModalState';
|
||||
import type { NodeModalProps } from '@/components/Settings/nodeModalModel';
|
||||
import type { InfrastructurePlatformSettingsProps } from '@/components/Settings/proxmoxSettingsModel';
|
||||
|
||||
export type NodeSlotType = 'pve' | 'pbs' | 'pmg';
|
||||
|
||||
export interface NodeCredentialSlotProps {
|
||||
nodeType: NodeSlotType;
|
||||
settings: InfrastructurePlatformSettingsProps;
|
||||
editingNode?: NodeConfigWithStatus | null;
|
||||
prefillNode?: Partial<NodeConfig>;
|
||||
initialAddress?: string;
|
||||
onCancel: () => void;
|
||||
onSaved: () => void;
|
||||
}
|
||||
|
||||
export const NodeCredentialSlot: Component<NodeCredentialSlotProps> = (props) => {
|
||||
const prefill: Partial<NodeConfig> | undefined = props.prefillNode ?? (
|
||||
props.initialAddress ? { host: props.initialAddress } : undefined
|
||||
);
|
||||
|
||||
const handleSave = async (nodeData: Partial<NodeConfig>) => {
|
||||
await props.settings.saveNode(nodeData);
|
||||
props.onSaved();
|
||||
};
|
||||
|
||||
const modalProps: NodeModalProps = {
|
||||
isOpen: true,
|
||||
nodeType: props.nodeType,
|
||||
editingNode: props.editingNode ?? undefined,
|
||||
prefillNode: prefill,
|
||||
onClose: props.onCancel,
|
||||
onSave: handleSave,
|
||||
securityStatus: props.settings.securityStatus() ?? undefined,
|
||||
temperatureMonitoringEnabled: props.settings.resolveTemperatureMonitoringEnabled(
|
||||
props.editingNode ?? null,
|
||||
),
|
||||
temperatureMonitoringLocked: props.settings.temperatureMonitoringLocked(),
|
||||
savingTemperatureSetting: props.settings.savingTemperatureSetting(),
|
||||
onToggleTemperatureMonitoring: async (enabled) => {
|
||||
const target = props.editingNode;
|
||||
if (target?.id) {
|
||||
await props.settings.handleNodeTemperatureMonitoringChange(target.id, enabled);
|
||||
} else {
|
||||
await props.settings.handleTemperatureMonitoringChange(enabled);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
const state = useNodeModalState(modalProps);
|
||||
|
||||
return (
|
||||
<form onSubmit={state.handleSubmit} class="space-y-6">
|
||||
<NodeModalBasicInfoSection modalProps={modalProps} state={state} />
|
||||
<NodeModalAuthenticationSection modalProps={modalProps} state={state} />
|
||||
<NodeModalMonitoringSection modalProps={modalProps} state={state} />
|
||||
<NodeModalStatusFooter modalProps={modalProps} state={state} />
|
||||
</form>
|
||||
);
|
||||
};
|
||||
|
|
@ -10,12 +10,12 @@ import {
|
|||
type SystemManageAction,
|
||||
} from './connectionsTableModel';
|
||||
import { ConnectionEditor } from './ConnectionEditor/ConnectionEditor';
|
||||
import { NodeCredentialSlot } from './ConnectionEditor/CredentialSlots/NodeCredentialSlot';
|
||||
import type { ConnectionType } from '@/api/connections';
|
||||
import { InfrastructureActiveRowDetails } from './InfrastructureActiveRowDetails';
|
||||
import { InfrastructureInstallerSection } from './InfrastructureInstallerSection';
|
||||
import { InfrastructureIgnoredRowDetails } from './InfrastructureIgnoredRowDetails';
|
||||
import { InfrastructureStopMonitoringDialog } from './InfrastructureStopMonitoringDialog';
|
||||
import { ProxmoxSettingsPanel } from './ProxmoxSettingsPanel';
|
||||
import { TrueNASSettingsPanel } from './TrueNASSettingsPanel';
|
||||
import { VMwareSettingsPanel } from './VMwareSettingsPanel';
|
||||
import {
|
||||
|
|
@ -24,7 +24,6 @@ import {
|
|||
type InfrastructureAddStep,
|
||||
} from './infrastructureWorkspaceModel';
|
||||
import type { InfrastructurePlatformSettingsProps } from './proxmoxSettingsModel';
|
||||
import type { NodeType } from './infrastructureSettingsModel';
|
||||
import {
|
||||
InfrastructureOperationsStateProvider,
|
||||
useInfrastructureOperationsContext,
|
||||
|
|
@ -127,14 +126,13 @@ const InfrastructureWorkspaceContent: Component<InfrastructureWorkspaceProps> =
|
|||
setShowAgentProfiles(false);
|
||||
};
|
||||
|
||||
const renderProxmoxSlot = (type: NodeType) => {
|
||||
const selectedAgent = () => type;
|
||||
const renderNodeSlot = (type: 'pve' | 'pbs' | 'pmg') => {
|
||||
return (
|
||||
<ProxmoxSettingsPanel
|
||||
{...props}
|
||||
selectedAgent={selectedAgent}
|
||||
onSelectAgent={() => {}}
|
||||
embedded
|
||||
<NodeCredentialSlot
|
||||
nodeType={type}
|
||||
settings={props}
|
||||
onCancel={exitAddMode}
|
||||
onSaved={exitAddMode}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
|
@ -178,7 +176,7 @@ const InfrastructureWorkspaceContent: Component<InfrastructureWorkspaceProps> =
|
|||
case 'pve':
|
||||
case 'pbs':
|
||||
case 'pmg':
|
||||
return renderProxmoxSlot(type);
|
||||
return renderNodeSlot(type);
|
||||
case 'truenas':
|
||||
return <TrueNASSettingsPanel state={props.trueNASSettings} />;
|
||||
case 'vmware':
|
||||
|
|
|
|||
|
|
@ -54,8 +54,12 @@ vi.mock('../InfrastructureInstallerSection', () => ({
|
|||
InfrastructureInstallerSection: () => <div data-testid="install-section">install</div>,
|
||||
}));
|
||||
|
||||
vi.mock('../ProxmoxSettingsPanel', () => ({
|
||||
ProxmoxSettingsPanel: () => <div data-testid="proxmox-section">proxmox</div>,
|
||||
vi.mock('../ConnectionEditor/CredentialSlots/NodeCredentialSlot', () => ({
|
||||
NodeCredentialSlot: (props: { nodeType: string }) => (
|
||||
<div data-testid="proxmox-section" data-node-type={props.nodeType}>
|
||||
proxmox
|
||||
</div>
|
||||
),
|
||||
}));
|
||||
|
||||
vi.mock('../TrueNASSettingsPanel', () => ({
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue