mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-05-08 09:53:25 +00:00
Split settings panel registry context owner
This commit is contained in:
parent
5c9149122b
commit
88bdac466f
7 changed files with 148 additions and 124 deletions
|
|
@ -84,8 +84,9 @@ work extends shared components instead of creating new local variants.
|
|||
62. `frontend-modern/src/components/Settings/networkSettingsModel.ts`
|
||||
63. `frontend-modern/src/components/Settings/useDiscoverySettingsState.ts`
|
||||
64. `frontend-modern/src/components/Settings/useSettingsInfrastructurePanelProps.ts`
|
||||
65. `frontend-modern/src/components/Settings/useSettingsPanelRegistry.tsx`
|
||||
66. `frontend-modern/src/components/Settings/useSettingsSystemPanels.tsx`
|
||||
65. `frontend-modern/src/components/Settings/settingsPanelRegistryContext.tsx`
|
||||
66. `frontend-modern/src/components/Settings/useSettingsPanelRegistry.tsx`
|
||||
67. `frontend-modern/src/components/Settings/useSettingsSystemPanels.tsx`
|
||||
|
||||
## Shared Boundaries
|
||||
|
||||
|
|
@ -272,7 +273,7 @@ through `frontend-modern/src/utils/alertIncidentPresentation.ts` instead of
|
|||
maintaining page-local incident panel styling inside
|
||||
`frontend-modern/src/pages/Alerts.tsx`.
|
||||
|
||||
The settings shell now also has an explicit four-way ownership split.
|
||||
The settings shell now also has an explicit five-way ownership split.
|
||||
`frontend-modern/src/components/Settings/useDiscoverySettingsState.ts` owns the
|
||||
shared discovery draft and subnet-validation state,
|
||||
`frontend-modern/src/components/Settings/useSettingsInfrastructurePanelProps.ts`
|
||||
|
|
@ -280,11 +281,13 @@ owns infrastructure workspace prop assembly and resource-derived infrastructure
|
|||
read-model shaping for the shell,
|
||||
`frontend-modern/src/components/Settings/useSettingsSystemPanels.tsx` owns
|
||||
system panel prop assembly for general, network, updates, and recovery, and
|
||||
`frontend-modern/src/components/Settings/useSettingsPanelRegistry.tsx` owns
|
||||
registry composition only. `frontend-modern/src/components/Settings/Settings.tsx`
|
||||
`frontend-modern/src/components/Settings/settingsPanelRegistryContext.tsx` owns
|
||||
registry context assembly for dispatchable settings tabs while
|
||||
`frontend-modern/src/components/Settings/useSettingsPanelRegistry.tsx` owns the
|
||||
final memoized registry composition only. `frontend-modern/src/components/Settings/Settings.tsx`
|
||||
must stay a shell that wires those owners together instead of re-accumulating
|
||||
infrastructure workspace props, system panel prop maps, or discovery draft
|
||||
state inline.
|
||||
infrastructure workspace props, registry context maps, system panel prop maps,
|
||||
or discovery draft state inline.
|
||||
|
||||
The resource incident panel's collapsed activity summary is now part of that
|
||||
same shared primitive boundary. Event-type count chips, visible-event copy,
|
||||
|
|
|
|||
|
|
@ -1866,6 +1866,7 @@
|
|||
"frontend-modern/src/components/Settings/settingsHeaderMeta.ts",
|
||||
"frontend-modern/src/components/Settings/SettingsPageShell.tsx",
|
||||
"frontend-modern/src/components/Settings/settingsPanelRegistry.ts",
|
||||
"frontend-modern/src/components/Settings/settingsPanelRegistryContext.tsx",
|
||||
"frontend-modern/src/components/Settings/ssoProvidersModel.ts",
|
||||
"frontend-modern/src/components/Settings/SSOProvidersPanel.tsx",
|
||||
"frontend-modern/src/components/Settings/SystemLogsPanel.tsx",
|
||||
|
|
@ -1951,6 +1952,7 @@
|
|||
"frontend-modern/src/components/Settings/settingsHeaderMeta.ts",
|
||||
"frontend-modern/src/components/Settings/SettingsPageShell.tsx",
|
||||
"frontend-modern/src/components/Settings/settingsPanelRegistry.ts",
|
||||
"frontend-modern/src/components/Settings/settingsPanelRegistryContext.tsx",
|
||||
"frontend-modern/src/components/Settings/ssoProvidersModel.ts",
|
||||
"frontend-modern/src/components/Settings/SSOProvidersPanel.tsx",
|
||||
"frontend-modern/src/components/Settings/UpdateInstallGuide.tsx",
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ import infrastructureConfiguredNodesStateSource from '../useInfrastructureConfig
|
|||
import infrastructureDiscoveryRuntimeStateSource from '../useInfrastructureDiscoveryRuntimeState.ts?raw';
|
||||
import settingsInfrastructurePanelPropsSource from '../useSettingsInfrastructurePanelProps.ts?raw';
|
||||
import nodeModalStateSource from '../useNodeModalState.ts?raw';
|
||||
import settingsPanelRegistryContextSource from '../settingsPanelRegistryContext.tsx?raw';
|
||||
import settingsPanelRegistryHookSource from '../useSettingsPanelRegistry.tsx?raw';
|
||||
import settingsSystemPanelsSource from '../useSettingsSystemPanels.tsx?raw';
|
||||
import apiAccessPanelSource from '../APIAccessPanel.tsx?raw';
|
||||
|
|
@ -138,6 +139,7 @@ const extractedModules = [
|
|||
'../useInfrastructureConfiguredNodesState.ts',
|
||||
'../useInfrastructureDiscoveryRuntimeState.ts',
|
||||
'../useSettingsInfrastructurePanelProps.ts',
|
||||
'../settingsPanelRegistryContext.tsx',
|
||||
'../apiTokenManagerModel.ts',
|
||||
'../useAPITokenManagerState.ts',
|
||||
'../useAuditLogPanelState.ts',
|
||||
|
|
@ -380,6 +382,7 @@ describe('Settings architecture guardrails', () => {
|
|||
expect(accessHookSource).toContain('shouldHideSettingsNavItem');
|
||||
expect(accessHookSource).toContain('tabFeatureRequirements');
|
||||
expect(panelRegistryHookSource).toContain('createSettingsPanelRegistry');
|
||||
expect(panelRegistryHookSource).toContain('buildSettingsPanelRegistryContext');
|
||||
expect(shellHookSource).toContain('SETTINGS_HEADER_META');
|
||||
expect(settingsSource).toContain('useSettingsPanelRegistry');
|
||||
expect(settingsSource).toContain('useSettingsAccess');
|
||||
|
|
@ -411,19 +414,22 @@ describe('Settings architecture guardrails', () => {
|
|||
expect(settingsSystemPanelsSource).toContain('allowedOrigins:');
|
||||
expect(settingsSystemPanelsSource).toContain('backupPollingEnabled:');
|
||||
expect(settingsSystemPanelsSource).toContain('handleDiscoveryEnabledChange:');
|
||||
expect(settingsPanelRegistryHookSource).toContain('systemPanels: SettingsSystemPanels');
|
||||
expect(settingsPanelRegistryHookSource).toContain(
|
||||
expect(settingsPanelRegistryHookSource).toContain('buildSettingsPanelRegistryContext');
|
||||
expect(settingsPanelRegistryContextSource).toContain('systemPanels: SettingsSystemPanels');
|
||||
expect(settingsPanelRegistryContextSource).toContain(
|
||||
'systemGeneralPanel: params.systemPanels.systemGeneralPanel',
|
||||
);
|
||||
expect(settingsPanelRegistryHookSource).toContain(
|
||||
expect(settingsPanelRegistryContextSource).toContain(
|
||||
'getNetworkPanelProps: params.systemPanels.getNetworkPanelProps',
|
||||
);
|
||||
expect(settingsPanelRegistryHookSource).toContain(
|
||||
expect(settingsPanelRegistryContextSource).toContain(
|
||||
'getUpdatesPanelProps: params.systemPanels.getUpdatesPanelProps',
|
||||
);
|
||||
expect(settingsPanelRegistryHookSource).toContain(
|
||||
expect(settingsPanelRegistryContextSource).toContain(
|
||||
'getRecoveryPanelProps: params.systemPanels.getRecoveryPanelProps',
|
||||
);
|
||||
expect(settingsPanelRegistryContextSource).toContain('const systemAiPanel: Component');
|
||||
expect(settingsPanelRegistryContextSource).toContain('const securitySsoPanel: Component');
|
||||
expect(settingsPanelRegistryHookSource).not.toContain('pvePollingInterval: params.');
|
||||
expect(settingsPanelRegistryHookSource).not.toContain('allowedOrigins: params.');
|
||||
expect(settingsPanelRegistryHookSource).not.toContain('backupPollingEnabled: params.');
|
||||
|
|
|
|||
|
|
@ -0,0 +1,112 @@
|
|||
import type { Accessor, Component, Setter } from 'solid-js';
|
||||
import type { VersionInfo } from '@/api/updates';
|
||||
import type { SecurityStatus as SecurityStatusInfo } from '@/types/config';
|
||||
import { AICostDashboard } from '@/components/AI/AICostDashboard';
|
||||
import { AISettings } from './AISettings';
|
||||
import { ProLicensePanel } from './ProLicensePanel';
|
||||
import { SSOProvidersPanel } from './SSOProvidersPanel';
|
||||
import type { ProxmoxSettingsPanelProps } from './proxmoxSettingsModel';
|
||||
import type { SettingsPanelRegistryContext } from './settingsPanelRegistry';
|
||||
import type { SettingsSystemPanels } from './useSettingsSystemPanels';
|
||||
|
||||
export interface UseSettingsPanelRegistryParams {
|
||||
securityStatus: Accessor<SecurityStatusInfo | null>;
|
||||
securityStatusLoading: Accessor<boolean>;
|
||||
organizationMonitoredSystemUsage: Accessor<number>;
|
||||
organizationGuestUsage: Accessor<number>;
|
||||
loadSecurityStatus: () => Promise<void>;
|
||||
showQuickSecuritySetup: Accessor<boolean>;
|
||||
setShowQuickSecuritySetup: Setter<boolean>;
|
||||
showQuickSecurityWizard: Accessor<boolean>;
|
||||
setShowQuickSecurityWizard: Setter<boolean>;
|
||||
showPasswordModal: Accessor<boolean>;
|
||||
setShowPasswordModal: Setter<boolean>;
|
||||
hideLocalLogin: Accessor<boolean>;
|
||||
hideLocalLoginLocked: Accessor<boolean>;
|
||||
savingHideLocalLogin: Accessor<boolean>;
|
||||
handleHideLocalLoginChange: (enabled: boolean) => Promise<void>;
|
||||
versionInfo: Accessor<VersionInfo | null>;
|
||||
getInfrastructurePanelProps: () => ProxmoxSettingsPanelProps;
|
||||
systemPanels: SettingsSystemPanels;
|
||||
}
|
||||
|
||||
export function buildSettingsPanelRegistryContext(
|
||||
params: UseSettingsPanelRegistryParams,
|
||||
): SettingsPanelRegistryContext {
|
||||
const settingsCapabilities = () => params.securityStatus()?.settingsCapabilities ?? null;
|
||||
|
||||
const systemAiPanel: Component = () => (
|
||||
<div class="space-y-6">
|
||||
<AISettings />
|
||||
<AICostDashboard />
|
||||
</div>
|
||||
);
|
||||
|
||||
const systemBillingPanel: Component = () => (
|
||||
<div class="space-y-6">
|
||||
<ProLicensePanel />
|
||||
</div>
|
||||
);
|
||||
|
||||
const securitySsoPanel: Component = () => (
|
||||
<div class="space-y-6">
|
||||
<SSOProvidersPanel
|
||||
onConfigUpdated={params.loadSecurityStatus}
|
||||
canManage={settingsCapabilities()?.singleSignOnWrite === true}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
return {
|
||||
getInfrastructurePanelProps: params.getInfrastructurePanelProps,
|
||||
systemGeneralPanel: params.systemPanels.systemGeneralPanel,
|
||||
systemAiPanel,
|
||||
systemBillingPanel,
|
||||
securitySsoPanel,
|
||||
getNetworkPanelProps: params.systemPanels.getNetworkPanelProps,
|
||||
getUpdatesPanelProps: params.systemPanels.getUpdatesPanelProps,
|
||||
getRecoveryPanelProps: params.systemPanels.getRecoveryPanelProps,
|
||||
getOrganizationOverviewPanelProps: () => ({}),
|
||||
getOrganizationAccessPanelProps: () => ({}),
|
||||
getOrganizationSharingPanelProps: () => ({}),
|
||||
getOrganizationBillingPanelProps: () => ({
|
||||
nodeUsage: params.organizationMonitoredSystemUsage(),
|
||||
guestUsage: params.organizationGuestUsage(),
|
||||
}),
|
||||
getApiAccessPanelProps: () => ({
|
||||
currentTokenHint: params.securityStatus()?.apiTokenHint,
|
||||
onTokensChanged: () => {
|
||||
void params.loadSecurityStatus();
|
||||
},
|
||||
refreshing: params.securityStatusLoading(),
|
||||
canManage: settingsCapabilities()?.apiAccessWrite === true,
|
||||
}),
|
||||
getSecurityOverviewPanelProps: () => ({
|
||||
securityStatus: params.securityStatus,
|
||||
securityStatusLoading: params.securityStatusLoading,
|
||||
}),
|
||||
getSecurityAuthPanelProps: () => ({
|
||||
securityStatus: params.securityStatus,
|
||||
securityStatusLoading: params.securityStatusLoading,
|
||||
versionInfo: params.versionInfo,
|
||||
showQuickSecuritySetup: params.showQuickSecuritySetup,
|
||||
setShowQuickSecuritySetup: params.setShowQuickSecuritySetup,
|
||||
showQuickSecurityWizard: params.showQuickSecurityWizard,
|
||||
setShowQuickSecurityWizard: params.setShowQuickSecurityWizard,
|
||||
showPasswordModal: params.showPasswordModal,
|
||||
setShowPasswordModal: params.setShowPasswordModal,
|
||||
hideLocalLogin: params.hideLocalLogin,
|
||||
hideLocalLoginLocked: params.hideLocalLoginLocked,
|
||||
savingHideLocalLogin: params.savingHideLocalLogin,
|
||||
handleHideLocalLoginChange: params.handleHideLocalLoginChange,
|
||||
loadSecurityStatus: params.loadSecurityStatus,
|
||||
canManage: settingsCapabilities()?.authenticationWrite === true,
|
||||
}),
|
||||
getRelayPanelProps: () => ({
|
||||
canManage: settingsCapabilities()?.relayWrite === true,
|
||||
}),
|
||||
getAuditWebhookPanelProps: () => ({
|
||||
canManage: settingsCapabilities()?.auditWebhooksWrite === true,
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
|
@ -1,114 +1,10 @@
|
|||
import { Accessor, Component, Setter, createMemo } from 'solid-js';
|
||||
import type { VersionInfo } from '@/api/updates';
|
||||
import type { SecurityStatus as SecurityStatusInfo } from '@/types/config';
|
||||
import { AISettings } from './AISettings';
|
||||
import { AICostDashboard } from '@/components/AI/AICostDashboard';
|
||||
import { ProLicensePanel } from './ProLicensePanel';
|
||||
import { SSOProvidersPanel } from './SSOProvidersPanel';
|
||||
import { createMemo } from 'solid-js';
|
||||
import { createSettingsPanelRegistry } from './settingsPanelRegistry';
|
||||
import type { ProxmoxSettingsPanelProps } from './proxmoxSettingsModel';
|
||||
import type { SettingsSystemPanels } from './useSettingsSystemPanels';
|
||||
|
||||
interface UseSettingsPanelRegistryParams {
|
||||
securityStatus: Accessor<SecurityStatusInfo | null>;
|
||||
securityStatusLoading: Accessor<boolean>;
|
||||
organizationMonitoredSystemUsage: Accessor<number>;
|
||||
organizationGuestUsage: Accessor<number>;
|
||||
loadSecurityStatus: () => Promise<void>;
|
||||
showQuickSecuritySetup: Accessor<boolean>;
|
||||
setShowQuickSecuritySetup: Setter<boolean>;
|
||||
showQuickSecurityWizard: Accessor<boolean>;
|
||||
setShowQuickSecurityWizard: Setter<boolean>;
|
||||
showPasswordModal: Accessor<boolean>;
|
||||
setShowPasswordModal: Setter<boolean>;
|
||||
hideLocalLogin: Accessor<boolean>;
|
||||
hideLocalLoginLocked: Accessor<boolean>;
|
||||
savingHideLocalLogin: Accessor<boolean>;
|
||||
handleHideLocalLoginChange: (enabled: boolean) => Promise<void>;
|
||||
versionInfo: Accessor<VersionInfo | null>;
|
||||
getInfrastructurePanelProps: () => ProxmoxSettingsPanelProps;
|
||||
systemPanels: SettingsSystemPanels;
|
||||
}
|
||||
import {
|
||||
buildSettingsPanelRegistryContext,
|
||||
type UseSettingsPanelRegistryParams,
|
||||
} from './settingsPanelRegistryContext';
|
||||
|
||||
export function useSettingsPanelRegistry(params: UseSettingsPanelRegistryParams) {
|
||||
const settingsCapabilities = createMemo(
|
||||
() => params.securityStatus()?.settingsCapabilities ?? null,
|
||||
);
|
||||
|
||||
const systemAiPanel: Component = () => (
|
||||
<div class="space-y-6">
|
||||
<AISettings />
|
||||
<AICostDashboard />
|
||||
</div>
|
||||
);
|
||||
|
||||
const systemBillingPanel: Component = () => (
|
||||
<div class="space-y-6">
|
||||
<ProLicensePanel />
|
||||
</div>
|
||||
);
|
||||
|
||||
const securitySsoPanel: Component = () => (
|
||||
<div class="space-y-6">
|
||||
<SSOProvidersPanel
|
||||
onConfigUpdated={params.loadSecurityStatus}
|
||||
canManage={settingsCapabilities()?.singleSignOnWrite === true}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
return createMemo(() =>
|
||||
createSettingsPanelRegistry({
|
||||
getInfrastructurePanelProps: params.getInfrastructurePanelProps,
|
||||
systemGeneralPanel: params.systemPanels.systemGeneralPanel,
|
||||
systemAiPanel,
|
||||
systemBillingPanel,
|
||||
securitySsoPanel,
|
||||
getNetworkPanelProps: params.systemPanels.getNetworkPanelProps,
|
||||
getUpdatesPanelProps: params.systemPanels.getUpdatesPanelProps,
|
||||
getRecoveryPanelProps: params.systemPanels.getRecoveryPanelProps,
|
||||
getOrganizationOverviewPanelProps: () => ({}),
|
||||
getOrganizationAccessPanelProps: () => ({}),
|
||||
getOrganizationSharingPanelProps: () => ({}),
|
||||
getOrganizationBillingPanelProps: () => ({
|
||||
nodeUsage: params.organizationMonitoredSystemUsage(),
|
||||
guestUsage: params.organizationGuestUsage(),
|
||||
}),
|
||||
getApiAccessPanelProps: () => ({
|
||||
currentTokenHint: params.securityStatus()?.apiTokenHint,
|
||||
onTokensChanged: () => {
|
||||
void params.loadSecurityStatus();
|
||||
},
|
||||
refreshing: params.securityStatusLoading(),
|
||||
canManage: settingsCapabilities()?.apiAccessWrite === true,
|
||||
}),
|
||||
getSecurityOverviewPanelProps: () => ({
|
||||
securityStatus: params.securityStatus,
|
||||
securityStatusLoading: params.securityStatusLoading,
|
||||
}),
|
||||
getSecurityAuthPanelProps: () => ({
|
||||
securityStatus: params.securityStatus,
|
||||
securityStatusLoading: params.securityStatusLoading,
|
||||
versionInfo: params.versionInfo,
|
||||
showQuickSecuritySetup: params.showQuickSecuritySetup,
|
||||
setShowQuickSecuritySetup: params.setShowQuickSecuritySetup,
|
||||
showQuickSecurityWizard: params.showQuickSecurityWizard,
|
||||
setShowQuickSecurityWizard: params.setShowQuickSecurityWizard,
|
||||
showPasswordModal: params.showPasswordModal,
|
||||
setShowPasswordModal: params.setShowPasswordModal,
|
||||
hideLocalLogin: params.hideLocalLogin,
|
||||
hideLocalLoginLocked: params.hideLocalLoginLocked,
|
||||
savingHideLocalLogin: params.savingHideLocalLogin,
|
||||
handleHideLocalLoginChange: params.handleHideLocalLoginChange,
|
||||
loadSecurityStatus: params.loadSecurityStatus,
|
||||
canManage: settingsCapabilities()?.authenticationWrite === true,
|
||||
}),
|
||||
getRelayPanelProps: () => ({
|
||||
canManage: settingsCapabilities()?.relayWrite === true,
|
||||
}),
|
||||
getAuditWebhookPanelProps: () => ({
|
||||
canManage: settingsCapabilities()?.auditWebhooksWrite === true,
|
||||
}),
|
||||
}),
|
||||
);
|
||||
return createMemo(() => createSettingsPanelRegistry(buildSettingsPanelRegistryContext(params)));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ import diagnosticsResultsPanelSource from '@/components/Settings/DiagnosticsResu
|
|||
import diagnosticsStateSource from '@/components/Settings/useDiagnosticsPanelState.ts?raw';
|
||||
import settingsShellSource from '@/components/Settings/Settings.tsx?raw';
|
||||
import settingsPanelRegistrySource from '@/components/Settings/useSettingsPanelRegistry.tsx?raw';
|
||||
import settingsPanelRegistryContextSource from '@/components/Settings/settingsPanelRegistryContext.tsx?raw';
|
||||
import settingsSystemPanelsSource from '@/components/Settings/useSettingsSystemPanels.tsx?raw';
|
||||
import settingsInfrastructurePanelPropsSource from '@/components/Settings/useSettingsInfrastructurePanelProps.ts?raw';
|
||||
import discoverySettingsStateSource from '@/components/Settings/useDiscoverySettingsState.ts?raw';
|
||||
|
|
@ -858,11 +859,14 @@ describe('frontend resource type boundaries', () => {
|
|||
expect(settingsShellSource).toContain(
|
||||
'const settingsPanelRegistry = useSettingsPanelRegistry({',
|
||||
);
|
||||
expect(settingsPanelRegistrySource).toContain('buildSettingsPanelRegistryContext');
|
||||
expect(settingsShellSource).not.toContain('getInfrastructurePanelProps: () => ({');
|
||||
expect(settingsPanelRegistrySource).toContain('systemPanels: SettingsSystemPanels');
|
||||
expect(settingsPanelRegistrySource).toContain(
|
||||
expect(settingsPanelRegistryContextSource).toContain('systemPanels: SettingsSystemPanels');
|
||||
expect(settingsPanelRegistryContextSource).toContain(
|
||||
'getNetworkPanelProps: params.systemPanels.getNetworkPanelProps',
|
||||
);
|
||||
expect(settingsPanelRegistryContextSource).toContain('const systemBillingPanel: Component');
|
||||
expect(settingsPanelRegistryContextSource).toContain('getSecurityAuthPanelProps');
|
||||
expect(settingsPanelRegistrySource).not.toContain('allowedOrigins: params.');
|
||||
expect(settingsPanelRegistrySource).not.toContain('backupPollingEnabled: params.');
|
||||
expect(settingsSystemPanelsSource).toContain('GeneralSettingsPanel');
|
||||
|
|
|
|||
|
|
@ -1910,6 +1910,7 @@ class SubsystemLookupTest(unittest.TestCase):
|
|||
"frontend-modern/src/components/Settings/SecurityOverviewPanel.tsx",
|
||||
"frontend-modern/src/components/Settings/SSOProvidersPanel.tsx",
|
||||
"frontend-modern/src/components/Settings/UpdatesSettingsPanel.tsx",
|
||||
"frontend-modern/src/components/Settings/settingsPanelRegistryContext.tsx",
|
||||
"frontend-modern/src/components/Settings/useDiscoverySettingsState.ts",
|
||||
"frontend-modern/src/components/Settings/useSettingsInfrastructurePanelProps.ts",
|
||||
"frontend-modern/src/components/Settings/useSettingsPanelRegistry.tsx",
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue