mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-09-11 14:00:29 +00:00
Split dashboard workload URL sync owners
This commit is contained in:
@@ -71,32 +71,34 @@ regression protection.
|
||||
49. `frontend-modern/src/components/Dashboard/workloadSelectors.ts`
|
||||
50. `frontend-modern/src/components/Dashboard/workloadTopology.ts`
|
||||
51. `frontend-modern/src/components/Dashboard/dashboardWorkloadRouteModel.ts`
|
||||
52. `frontend-modern/src/components/Dashboard/useDashboardWorkloadFilterOptions.ts`
|
||||
53. `frontend-modern/src/components/Dashboard/__tests__/dashboardWorkloadRouteModel.test.ts`
|
||||
54. `frontend-modern/src/components/Dashboard/__tests__/workloadTopology.test.ts`
|
||||
55. `frontend-modern/src/components/Infrastructure/UnifiedResourceTable.tsx`
|
||||
56. `frontend-modern/src/components/Infrastructure/useUnifiedResourceTableState.ts`
|
||||
57. `frontend-modern/src/components/Infrastructure/infrastructureSelectors.ts`
|
||||
58. `frontend-modern/src/components/Infrastructure/resourceDetailMappers.ts`
|
||||
59. `frontend-modern/src/components/Dashboard/__tests__/Dashboard.performance.contract.test.tsx`
|
||||
60. `frontend-modern/src/components/Dashboard/__tests__/DashboardFilter.test.tsx`
|
||||
61. `frontend-modern/src/components/Dashboard/__tests__/useDashboardFilterState.test.ts`
|
||||
62. `frontend-modern/src/components/Dashboard/__tests__/useDashboardSelectionState.test.ts`
|
||||
63. `frontend-modern/src/components/Dashboard/MetricBar.test.tsx`
|
||||
64. `frontend-modern/src/components/Dashboard/__tests__/useMetricBarState.test.tsx`
|
||||
65. `frontend-modern/src/components/Dashboard/__tests__/EnhancedCPUBar.test.tsx`
|
||||
66. `frontend-modern/src/components/Dashboard/__tests__/useEnhancedCPUBarState.test.tsx`
|
||||
67. `frontend-modern/src/components/Dashboard/ThresholdSlider.test.tsx`
|
||||
68. `frontend-modern/src/components/Dashboard/__tests__/useThresholdSliderState.test.ts`
|
||||
69. `frontend-modern/src/components/Dashboard/__tests__/StackedDiskBar.test.tsx`
|
||||
70. `frontend-modern/src/components/Dashboard/__tests__/useStackedDiskBarState.test.tsx`
|
||||
71. `frontend-modern/src/components/Dashboard/StackedMemoryBar.test.tsx`
|
||||
72. `frontend-modern/src/components/Dashboard/__tests__/useStackedMemoryBarState.test.tsx`
|
||||
73. `frontend-modern/src/components/Dashboard/__tests__/DiskList.test.tsx`
|
||||
74. `frontend-modern/src/components/Dashboard/__tests__/GuestRow.test.tsx`
|
||||
75. `frontend-modern/src/components/Dashboard/GuestDrawer.test.tsx`
|
||||
76. `frontend-modern/src/components/Dashboard/__tests__/useGroupedTableWindowing.test.ts`
|
||||
77. `frontend-modern/src/components/Infrastructure/__tests__/UnifiedResourceTable.performance.contract.test.tsx`
|
||||
52. `frontend-modern/src/components/Dashboard/dashboardWorkloadUrlSyncModel.ts`
|
||||
53. `frontend-modern/src/components/Dashboard/useDashboardWorkloadFilterOptions.ts`
|
||||
54. `frontend-modern/src/components/Dashboard/__tests__/dashboardWorkloadRouteModel.test.ts`
|
||||
55. `frontend-modern/src/components/Dashboard/__tests__/dashboardWorkloadUrlSyncModel.test.ts`
|
||||
56. `frontend-modern/src/components/Dashboard/__tests__/workloadTopology.test.ts`
|
||||
57. `frontend-modern/src/components/Infrastructure/UnifiedResourceTable.tsx`
|
||||
58. `frontend-modern/src/components/Infrastructure/useUnifiedResourceTableState.ts`
|
||||
59. `frontend-modern/src/components/Infrastructure/infrastructureSelectors.ts`
|
||||
60. `frontend-modern/src/components/Infrastructure/resourceDetailMappers.ts`
|
||||
61. `frontend-modern/src/components/Dashboard/__tests__/Dashboard.performance.contract.test.tsx`
|
||||
62. `frontend-modern/src/components/Dashboard/__tests__/DashboardFilter.test.tsx`
|
||||
63. `frontend-modern/src/components/Dashboard/__tests__/useDashboardFilterState.test.ts`
|
||||
64. `frontend-modern/src/components/Dashboard/__tests__/useDashboardSelectionState.test.ts`
|
||||
65. `frontend-modern/src/components/Dashboard/MetricBar.test.tsx`
|
||||
66. `frontend-modern/src/components/Dashboard/__tests__/useMetricBarState.test.tsx`
|
||||
67. `frontend-modern/src/components/Dashboard/__tests__/EnhancedCPUBar.test.tsx`
|
||||
68. `frontend-modern/src/components/Dashboard/__tests__/useEnhancedCPUBarState.test.tsx`
|
||||
69. `frontend-modern/src/components/Dashboard/ThresholdSlider.test.tsx`
|
||||
70. `frontend-modern/src/components/Dashboard/__tests__/useThresholdSliderState.test.ts`
|
||||
71. `frontend-modern/src/components/Dashboard/__tests__/StackedDiskBar.test.tsx`
|
||||
72. `frontend-modern/src/components/Dashboard/__tests__/useStackedDiskBarState.test.tsx`
|
||||
73. `frontend-modern/src/components/Dashboard/StackedMemoryBar.test.tsx`
|
||||
74. `frontend-modern/src/components/Dashboard/__tests__/useStackedMemoryBarState.test.tsx`
|
||||
75. `frontend-modern/src/components/Dashboard/__tests__/DiskList.test.tsx`
|
||||
76. `frontend-modern/src/components/Dashboard/__tests__/GuestRow.test.tsx`
|
||||
77. `frontend-modern/src/components/Dashboard/GuestDrawer.test.tsx`
|
||||
78. `frontend-modern/src/components/Dashboard/__tests__/useGroupedTableWindowing.test.ts`
|
||||
79. `frontend-modern/src/components/Infrastructure/__tests__/UnifiedResourceTable.performance.contract.test.tsx`
|
||||
|
||||
## Shared Boundaries
|
||||
|
||||
@@ -121,7 +123,7 @@ regression protection.
|
||||
11. Extend dashboard disk-list derivations and fallback runtime wiring through `frontend-modern/src/components/Dashboard/diskListModel.ts` and `frontend-modern/src/components/Dashboard/useDiskListState.ts` rather than rebuilding usage math, progress-state mapping, or tooltip fallback logic inside `frontend-modern/src/components/Dashboard/DiskList.tsx`
|
||||
12. Extend dashboard guest metadata cache persistence, metadata refresh, org-scope switching, and optimistic custom-URL updates through `frontend-modern/src/components/Dashboard/useDashboardGuestMetadataState.ts` rather than rebuilding dashboard-local storage caches, event listeners, or guest metadata API wiring inside `frontend-modern/src/components/Dashboard/useDashboardState.ts`
|
||||
13. Extend dashboard deep-link selection, hovered-row continuity, and table scroll preservation through `frontend-modern/src/components/Dashboard/useDashboardSelectionState.ts` rather than rebuilding resource-query parsing, selected-row scroll pinning, or hovered-row invalidation inside `frontend-modern/src/components/Dashboard/useDashboardState.ts`
|
||||
14. Extend dashboard workload route ownership, route-driven option catalogs, and toolbar filter config through `frontend-modern/src/components/Dashboard/useDashboardWorkloadRouteState.ts`, `frontend-modern/src/components/Dashboard/useDashboardWorkloadFilterOptions.ts`, and `frontend-modern/src/components/Dashboard/dashboardWorkloadRouteModel.ts`, and extend query-param synchronization through `frontend-modern/src/components/Dashboard/useDashboardWorkloadUrlSync.ts`, rather than rebuilding route sync, alias parsing, option derivation, or managed workload URLs inside `frontend-modern/src/components/Dashboard/useDashboardState.ts`
|
||||
14. Extend dashboard workload route ownership, route-driven option catalogs, and toolbar filter config through `frontend-modern/src/components/Dashboard/useDashboardWorkloadRouteState.ts`, `frontend-modern/src/components/Dashboard/useDashboardWorkloadFilterOptions.ts`, and `frontend-modern/src/components/Dashboard/dashboardWorkloadRouteModel.ts`, and extend query-param synchronization plus managed workload URL semantics through `frontend-modern/src/components/Dashboard/useDashboardWorkloadUrlSync.ts` and `frontend-modern/src/components/Dashboard/dashboardWorkloadUrlSyncModel.ts`, rather than rebuilding route sync, alias parsing, option derivation, param precedence, or managed workload URLs inside `frontend-modern/src/components/Dashboard/useDashboardState.ts`
|
||||
15. Extend grouped dashboard workload derivation, summary fallbacks, and grouped/windowed table presentation through `frontend-modern/src/components/Dashboard/useDashboardWorkloadDerivedState.ts`, and extend node parent mapping through `frontend-modern/src/components/Dashboard/workloadTopology.ts`, rather than rebuilding grouped selectors, summary snapshot math, or topology lookups inside `frontend-modern/src/components/Dashboard/useDashboardState.ts`
|
||||
16. Extend dashboard control defaults, persistent view preferences, keyboard reset behavior, column-visibility ownership, and tag-search flow through `frontend-modern/src/components/Dashboard/useDashboardControlsState.ts` and `frontend-modern/src/components/Dashboard/dashboardFilterModel.ts` rather than rebuilding sort/search/grouping state, reset drift, or column-toggle plumbing inside `frontend-modern/src/components/Dashboard/useDashboardState.ts`
|
||||
17. Extend dashboard filter active-count, reset semantics, and mobile toolbar state through `frontend-modern/src/components/Dashboard/dashboardFilterModel.ts` and `frontend-modern/src/components/Dashboard/useDashboardFilterState.ts`, rather than rebuilding filter-local state inside `frontend-modern/src/components/Dashboard/DashboardFilter.tsx`
|
||||
|
||||
@@ -2560,6 +2560,7 @@
|
||||
"frontend-modern/src/components/Dashboard/DashboardStatsStrip.tsx",
|
||||
"frontend-modern/src/components/Dashboard/dashboardWorkloadRouteModel.ts",
|
||||
"frontend-modern/src/components/Dashboard/DashboardWorkloadTable.tsx",
|
||||
"frontend-modern/src/components/Dashboard/dashboardWorkloadUrlSyncModel.ts",
|
||||
"frontend-modern/src/components/Dashboard/DiskList.tsx",
|
||||
"frontend-modern/src/components/Dashboard/diskListModel.ts",
|
||||
"frontend-modern/src/components/Dashboard/EnhancedCPUBar.tsx",
|
||||
@@ -2615,6 +2616,7 @@
|
||||
"frontend-modern/src/components/Dashboard/__tests__/Dashboard.performance.contract.test.tsx",
|
||||
"frontend-modern/src/components/Dashboard/__tests__/DashboardFilter.test.tsx",
|
||||
"frontend-modern/src/components/Dashboard/__tests__/dashboardWorkloadRouteModel.test.ts",
|
||||
"frontend-modern/src/components/Dashboard/__tests__/dashboardWorkloadUrlSyncModel.test.ts",
|
||||
"frontend-modern/src/components/Dashboard/__tests__/DiskList.test.tsx",
|
||||
"frontend-modern/src/components/Dashboard/__tests__/EnhancedCPUBar.test.tsx",
|
||||
"frontend-modern/src/components/Dashboard/__tests__/StackedDiskBar.test.tsx",
|
||||
@@ -2665,6 +2667,7 @@
|
||||
"frontend-modern/src/components/Dashboard/DashboardStatsStrip.tsx",
|
||||
"frontend-modern/src/components/Dashboard/dashboardWorkloadRouteModel.ts",
|
||||
"frontend-modern/src/components/Dashboard/DashboardWorkloadTable.tsx",
|
||||
"frontend-modern/src/components/Dashboard/dashboardWorkloadUrlSyncModel.ts",
|
||||
"frontend-modern/src/components/Dashboard/DiskList.tsx",
|
||||
"frontend-modern/src/components/Dashboard/diskListModel.ts",
|
||||
"frontend-modern/src/components/Dashboard/EnhancedCPUBar.tsx",
|
||||
@@ -2716,6 +2719,7 @@
|
||||
"frontend-modern/src/components/Dashboard/__tests__/Dashboard.performance.contract.test.tsx",
|
||||
"frontend-modern/src/components/Dashboard/__tests__/DashboardFilter.test.tsx",
|
||||
"frontend-modern/src/components/Dashboard/__tests__/dashboardWorkloadRouteModel.test.ts",
|
||||
"frontend-modern/src/components/Dashboard/__tests__/dashboardWorkloadUrlSyncModel.test.ts",
|
||||
"frontend-modern/src/components/Dashboard/__tests__/DiskList.test.tsx",
|
||||
"frontend-modern/src/components/Dashboard/__tests__/EnhancedCPUBar.test.tsx",
|
||||
"frontend-modern/src/components/Dashboard/__tests__/GuestRow.test.tsx",
|
||||
|
||||
+22
-3
@@ -17,6 +17,7 @@ import dashboardSelectionStateSource from '../useDashboardSelectionState.ts?raw'
|
||||
import dashboardWorkloadDerivedStateSource from '../useDashboardWorkloadDerivedState.ts?raw';
|
||||
import dashboardWorkloadFilterOptionsSource from '../useDashboardWorkloadFilterOptions.ts?raw';
|
||||
import dashboardWorkloadRouteModelSource from '../dashboardWorkloadRouteModel.ts?raw';
|
||||
import dashboardWorkloadUrlSyncModelSource from '../dashboardWorkloadUrlSyncModel.ts?raw';
|
||||
import dashboardWorkloadRouteStateSource from '../useDashboardWorkloadRouteState.ts?raw';
|
||||
import dashboardWorkloadUrlSyncSource from '../useDashboardWorkloadUrlSync.ts?raw';
|
||||
import dashboardStateSource from '../useDashboardState.ts?raw';
|
||||
@@ -558,15 +559,33 @@ describe('Dashboard performance contract', () => {
|
||||
expect(dashboardWorkloadRouteModelSource).toContain(
|
||||
"normalizeWorkloadViewModeParam(raw) ?? 'all'",
|
||||
);
|
||||
expect(dashboardWorkloadUrlSyncSource).toContain('buildWorkloadsPath({');
|
||||
expect(dashboardWorkloadUrlSyncSource).toContain('normalizeWorkloadViewModeParam');
|
||||
expect(dashboardWorkloadUrlSyncSource).not.toContain('buildWorkloadsPath({');
|
||||
expect(dashboardWorkloadUrlSyncSource).not.toContain('normalizeWorkloadViewModeParam');
|
||||
expect(dashboardWorkloadUrlSyncSource).not.toContain('parseWorkloadsLinkSearch');
|
||||
expect(dashboardWorkloadUrlSyncSource).toContain(
|
||||
"from './dashboardWorkloadRouteModel'",
|
||||
);
|
||||
expect(dashboardWorkloadUrlSyncSource).toContain(
|
||||
"from './dashboardWorkloadUrlSyncModel'",
|
||||
);
|
||||
expect(dashboardWorkloadUrlSyncSource).toContain(
|
||||
'const [handledTypeParam, setHandledTypeParam]',
|
||||
);
|
||||
expect(dashboardWorkloadUrlSyncSource).toContain('parseWorkloadsLinkSearch');
|
||||
expect(dashboardWorkloadUrlSyncSource).toContain('parseDashboardWorkloadUrlParams');
|
||||
expect(dashboardWorkloadUrlSyncSource).toContain(
|
||||
'resolveDashboardManagedWorkloadsNavigateTarget({',
|
||||
);
|
||||
expect(dashboardWorkloadUrlSyncModelSource).toContain('parseWorkloadsLinkSearch(search)');
|
||||
expect(dashboardWorkloadUrlSyncModelSource).toContain('buildWorkloadsPath({');
|
||||
expect(dashboardWorkloadUrlSyncModelSource).toContain(
|
||||
'resolveDashboardManagedWorkloadsNavigateTarget',
|
||||
);
|
||||
expect(dashboardWorkloadUrlSyncModelSource).toContain(
|
||||
'resolveDashboardWorkloadRuntimeParam',
|
||||
);
|
||||
expect(dashboardWorkloadUrlSyncModelSource).toContain(
|
||||
'normalizeWorkloadViewModeParam(params.type)',
|
||||
);
|
||||
expect(dashboardControlsStateSource).toContain('useBreakpoint');
|
||||
expect(dashboardControlsStateSource).toContain('useColumnVisibility');
|
||||
expect(dashboardControlsStateSource).toContain('usePersistentSignal');
|
||||
|
||||
+106
@@ -0,0 +1,106 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
import {
|
||||
parseDashboardWorkloadUrlParams,
|
||||
resolveDashboardManagedWorkloadsNavigateTarget,
|
||||
resolveDashboardWorkloadRuntimeParam,
|
||||
resolveDashboardWorkloadTypeParam,
|
||||
} from '../dashboardWorkloadUrlSyncModel';
|
||||
|
||||
describe('dashboardWorkloadUrlSyncModel', () => {
|
||||
it('parses canonical workload route params and keeps resource deep links intact', () => {
|
||||
expect(
|
||||
parseDashboardWorkloadUrlParams(
|
||||
'?type=docker&runtime=containerd&context=prod&namespace=default&agent=node-a&resource=guest-1',
|
||||
),
|
||||
).toEqual({
|
||||
type: 'docker',
|
||||
runtime: 'containerd',
|
||||
context: 'prod',
|
||||
namespace: 'default',
|
||||
agent: 'node-a',
|
||||
resource: 'guest-1',
|
||||
});
|
||||
});
|
||||
|
||||
it('resolves workload type params through the canonical alias rules and k8s precedence', () => {
|
||||
expect(
|
||||
resolveDashboardWorkloadTypeParam({
|
||||
type: 'docker',
|
||||
runtime: '',
|
||||
context: '',
|
||||
namespace: '',
|
||||
agent: '',
|
||||
resource: '',
|
||||
}),
|
||||
).toBe('app-container');
|
||||
expect(
|
||||
resolveDashboardWorkloadTypeParam({
|
||||
type: 'vm',
|
||||
runtime: '',
|
||||
context: 'prod',
|
||||
namespace: '',
|
||||
agent: '',
|
||||
resource: '',
|
||||
}),
|
||||
).toBeNull();
|
||||
});
|
||||
|
||||
it('only applies runtime params when the url semantics still resolve to app-container scope', () => {
|
||||
expect(
|
||||
resolveDashboardWorkloadRuntimeParam({
|
||||
type: 'docker',
|
||||
runtime: 'containerd',
|
||||
context: '',
|
||||
namespace: '',
|
||||
agent: '',
|
||||
resource: '',
|
||||
}),
|
||||
).toEqual({
|
||||
forceViewMode: 'app-container',
|
||||
runtime: 'containerd',
|
||||
shouldApply: true,
|
||||
});
|
||||
|
||||
expect(
|
||||
resolveDashboardWorkloadRuntimeParam({
|
||||
type: 'vm',
|
||||
runtime: 'containerd',
|
||||
context: 'prod',
|
||||
namespace: '',
|
||||
agent: '',
|
||||
resource: '',
|
||||
}),
|
||||
).toEqual({
|
||||
forceViewMode: null,
|
||||
runtime: 'containerd',
|
||||
shouldApply: false,
|
||||
});
|
||||
});
|
||||
|
||||
it('builds managed workload navigate targets without dropping unrelated resource params', () => {
|
||||
expect(
|
||||
resolveDashboardManagedWorkloadsNavigateTarget({
|
||||
currentSearch: '?resource=guest-1&type=vm&agent=node-a',
|
||||
viewMode: 'pod',
|
||||
containerRuntime: 'docker',
|
||||
selectedKubernetesContext: 'prod',
|
||||
selectedKubernetesNamespace: 'default',
|
||||
selectedNode: 'cluster-a-node-a',
|
||||
selectedHostHint: null,
|
||||
}),
|
||||
).toBe('/workloads?resource=guest-1&type=pod&context=prod&namespace=default');
|
||||
|
||||
expect(
|
||||
resolveDashboardManagedWorkloadsNavigateTarget({
|
||||
currentSearch: '?type=pod&context=prod&namespace=default',
|
||||
viewMode: 'pod',
|
||||
containerRuntime: '',
|
||||
selectedKubernetesContext: 'prod',
|
||||
selectedKubernetesNamespace: 'default',
|
||||
selectedNode: null,
|
||||
selectedHostHint: null,
|
||||
}),
|
||||
).toBeNull();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,122 @@
|
||||
import type { ViewMode } from '@/types/workloads';
|
||||
import {
|
||||
buildWorkloadsPath,
|
||||
parseWorkloadsLinkSearch,
|
||||
WORKLOADS_PATH,
|
||||
WORKLOADS_QUERY_PARAMS,
|
||||
} from '@/routing/resourceLinks';
|
||||
import { areSearchParamsEquivalent } from '@/utils/searchParams';
|
||||
import { normalizeWorkloadViewModeParam } from '@/utils/workloads';
|
||||
|
||||
export interface DashboardWorkloadUrlParams {
|
||||
type: string;
|
||||
runtime: string;
|
||||
context: string;
|
||||
namespace: string;
|
||||
agent: string;
|
||||
resource: string;
|
||||
}
|
||||
|
||||
export interface DashboardWorkloadRuntimeParamResolution {
|
||||
forceViewMode: ViewMode | null;
|
||||
runtime: string;
|
||||
shouldApply: boolean;
|
||||
}
|
||||
|
||||
interface DashboardManagedWorkloadsNavigateTargetOptions {
|
||||
containerRuntime: string;
|
||||
currentSearch: string;
|
||||
selectedHostHint: string | null;
|
||||
selectedKubernetesContext: string | null;
|
||||
selectedKubernetesNamespace: string | null;
|
||||
selectedNode: string | null;
|
||||
viewMode: ViewMode;
|
||||
}
|
||||
|
||||
export const parseDashboardWorkloadUrlParams = (search: string): DashboardWorkloadUrlParams =>
|
||||
parseWorkloadsLinkSearch(search);
|
||||
|
||||
const hasDashboardWorkloadKubernetesScope = (params: DashboardWorkloadUrlParams): boolean =>
|
||||
Boolean(params.context.trim()) || Boolean(params.namespace.trim());
|
||||
|
||||
export const resolveDashboardWorkloadTypeParam = (
|
||||
params: DashboardWorkloadUrlParams,
|
||||
): ViewMode | null => {
|
||||
const nextMode = normalizeWorkloadViewModeParam(params.type);
|
||||
if (!nextMode) return null;
|
||||
if (hasDashboardWorkloadKubernetesScope(params) && nextMode !== 'pod') return null;
|
||||
return nextMode;
|
||||
};
|
||||
|
||||
export const resolveDashboardWorkloadRuntimeParam = (
|
||||
params: DashboardWorkloadUrlParams,
|
||||
): DashboardWorkloadRuntimeParamResolution => {
|
||||
const nextMode = resolveDashboardWorkloadTypeParam(params);
|
||||
const runtimeRelevant =
|
||||
!hasDashboardWorkloadKubernetesScope(params) &&
|
||||
(nextMode === 'app-container' || !params.type.trim());
|
||||
|
||||
if (!runtimeRelevant) {
|
||||
return {
|
||||
forceViewMode: null,
|
||||
runtime: params.runtime,
|
||||
shouldApply: false,
|
||||
};
|
||||
}
|
||||
|
||||
if (!params.runtime.trim()) {
|
||||
return {
|
||||
forceViewMode: null,
|
||||
runtime: '',
|
||||
shouldApply: true,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
forceViewMode: 'app-container',
|
||||
runtime: params.runtime,
|
||||
shouldApply: true,
|
||||
};
|
||||
};
|
||||
|
||||
export const resolveDashboardManagedWorkloadsNavigateTarget = ({
|
||||
containerRuntime,
|
||||
currentSearch,
|
||||
selectedHostHint,
|
||||
selectedKubernetesContext,
|
||||
selectedKubernetesNamespace,
|
||||
selectedNode,
|
||||
viewMode,
|
||||
}: DashboardManagedWorkloadsNavigateTargetOptions): string | null => {
|
||||
const currentParams = new URLSearchParams(currentSearch);
|
||||
const nextParams = new URLSearchParams(currentSearch);
|
||||
const nextType = viewMode === 'all' ? '' : viewMode;
|
||||
const nextRuntime = viewMode === 'app-container' ? containerRuntime.trim() : '';
|
||||
const nextContext = viewMode === 'pod' ? (selectedKubernetesContext ?? '') : '';
|
||||
const nextNamespace = viewMode === 'pod' ? (selectedKubernetesNamespace ?? '') : '';
|
||||
const nextAgent = viewMode === 'pod' ? '' : (selectedNode ?? selectedHostHint ?? '');
|
||||
|
||||
const managedPath = buildWorkloadsPath({
|
||||
type: nextType || null,
|
||||
runtime: nextRuntime || null,
|
||||
context: nextContext || null,
|
||||
namespace: nextNamespace || null,
|
||||
agent: nextAgent || null,
|
||||
});
|
||||
const managedUrl = new URL(managedPath, 'http://pulse.local');
|
||||
nextParams.delete(WORKLOADS_QUERY_PARAMS.type);
|
||||
nextParams.delete(WORKLOADS_QUERY_PARAMS.runtime);
|
||||
nextParams.delete(WORKLOADS_QUERY_PARAMS.context);
|
||||
nextParams.delete(WORKLOADS_QUERY_PARAMS.namespace);
|
||||
nextParams.delete(WORKLOADS_QUERY_PARAMS.agent);
|
||||
managedUrl.searchParams.forEach((value, key) => {
|
||||
nextParams.set(key, value);
|
||||
});
|
||||
|
||||
if (areSearchParamsEquivalent(currentParams, nextParams)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const nextSearch = nextParams.toString();
|
||||
return nextSearch ? `${WORKLOADS_PATH}?${nextSearch}` : WORKLOADS_PATH;
|
||||
};
|
||||
@@ -1,4 +1,5 @@
|
||||
import {
|
||||
createMemo,
|
||||
createEffect,
|
||||
createSignal,
|
||||
onCleanup,
|
||||
@@ -9,14 +10,15 @@ import {
|
||||
import { useLocation, useNavigate } from '@solidjs/router';
|
||||
import type { ViewMode } from '@/types/workloads';
|
||||
import {
|
||||
buildWorkloadsPath,
|
||||
parseWorkloadsLinkSearch,
|
||||
WORKLOADS_PATH,
|
||||
WORKLOADS_QUERY_PARAMS,
|
||||
} from '@/routing/resourceLinks';
|
||||
import { areSearchParamsEquivalent } from '@/utils/searchParams';
|
||||
import { normalizeWorkloadViewModeParam } from '@/utils/workloads';
|
||||
import type { DashboardWorkloadNodeOption } from './dashboardWorkloadRouteModel';
|
||||
import {
|
||||
parseDashboardWorkloadUrlParams,
|
||||
resolveDashboardManagedWorkloadsNavigateTarget,
|
||||
resolveDashboardWorkloadRuntimeParam,
|
||||
resolveDashboardWorkloadTypeParam,
|
||||
} from './dashboardWorkloadUrlSyncModel';
|
||||
|
||||
export interface DashboardWorkloadUrlSyncOptions {
|
||||
containerRuntime: Accessor<string>;
|
||||
@@ -42,6 +44,7 @@ export function useDashboardWorkloadUrlSync(options: DashboardWorkloadUrlSyncOpt
|
||||
const navigate = useNavigate();
|
||||
const location = useLocation();
|
||||
const isWorkloadsRoute = () => location.pathname === WORKLOADS_PATH;
|
||||
const workloadUrlParams = createMemo(() => parseDashboardWorkloadUrlParams(location.search));
|
||||
|
||||
const [handledTypeParam, setHandledTypeParam] = createSignal('');
|
||||
const [handledRuntimeParam, setHandledRuntimeParam] = createSignal('');
|
||||
@@ -143,9 +146,8 @@ export function useDashboardWorkloadUrlSync(options: DashboardWorkloadUrlSyncOpt
|
||||
});
|
||||
|
||||
createEffect(() => {
|
||||
const parsed = parseWorkloadsLinkSearch(location.search);
|
||||
const typeParam = parsed.type;
|
||||
const normalizedType = typeParam ?? '';
|
||||
const parsed = workloadUrlParams();
|
||||
const normalizedType = parsed.type;
|
||||
if (normalizedType === handledTypeParam()) return;
|
||||
|
||||
if (!normalizedType) {
|
||||
@@ -153,25 +155,18 @@ export function useDashboardWorkloadUrlSync(options: DashboardWorkloadUrlSyncOpt
|
||||
return;
|
||||
}
|
||||
|
||||
const hasK8sScope =
|
||||
Boolean((parsed.context ?? '').trim()) || Boolean((parsed.namespace ?? '').trim());
|
||||
const nextMode = normalizeWorkloadViewModeParam(normalizedType);
|
||||
const nextMode = resolveDashboardWorkloadTypeParam(parsed);
|
||||
if (!nextMode) {
|
||||
setHandledTypeParam(normalizedType);
|
||||
return;
|
||||
}
|
||||
if (hasK8sScope && nextMode !== 'pod') {
|
||||
setHandledTypeParam(normalizedType);
|
||||
return;
|
||||
}
|
||||
|
||||
options.setViewMode(nextMode);
|
||||
setHandledTypeParam(normalizedType);
|
||||
});
|
||||
|
||||
createEffect(() => {
|
||||
const { context: contextParam } = parseWorkloadsLinkSearch(location.search);
|
||||
const normalized = contextParam ?? '';
|
||||
const normalized = workloadUrlParams().context;
|
||||
if (normalized === handledContextParam()) return;
|
||||
|
||||
if (normalized) {
|
||||
@@ -191,8 +186,7 @@ export function useDashboardWorkloadUrlSync(options: DashboardWorkloadUrlSyncOpt
|
||||
});
|
||||
|
||||
createEffect(() => {
|
||||
const { namespace: namespaceParam } = parseWorkloadsLinkSearch(location.search);
|
||||
const normalized = namespaceParam ?? '';
|
||||
const normalized = workloadUrlParams().namespace;
|
||||
if (normalized === handledNamespaceParam()) return;
|
||||
|
||||
if (normalized) {
|
||||
@@ -212,8 +206,7 @@ export function useDashboardWorkloadUrlSync(options: DashboardWorkloadUrlSyncOpt
|
||||
});
|
||||
|
||||
createEffect(() => {
|
||||
const { agent: agentParam } = parseWorkloadsLinkSearch(location.search);
|
||||
const normalized = agentParam ?? '';
|
||||
const normalized = workloadUrlParams().agent;
|
||||
if (normalized === handledAgentParam()) return;
|
||||
|
||||
if (normalized) {
|
||||
@@ -233,34 +226,21 @@ export function useDashboardWorkloadUrlSync(options: DashboardWorkloadUrlSyncOpt
|
||||
});
|
||||
|
||||
createEffect(() => {
|
||||
const parsed = parseWorkloadsLinkSearch(location.search);
|
||||
const urlRuntime = parsed.runtime ?? '';
|
||||
const parsed = workloadUrlParams();
|
||||
const urlRuntime = parsed.runtime;
|
||||
if (urlRuntime === handledRuntimeParam()) return;
|
||||
|
||||
const urlContext = parsed.context ?? '';
|
||||
const hasContext = Boolean(urlContext.trim());
|
||||
const hasNamespace = Boolean((parsed.namespace ?? '').trim());
|
||||
const urlType = parsed.type ?? '';
|
||||
const nextMode = normalizeWorkloadViewModeParam(urlType);
|
||||
const runtimeRelevant =
|
||||
!hasContext && !hasNamespace && (nextMode === 'app-container' || !urlType.trim());
|
||||
|
||||
if (!runtimeRelevant) {
|
||||
const resolution = resolveDashboardWorkloadRuntimeParam(parsed);
|
||||
if (!resolution.shouldApply) {
|
||||
setHandledRuntimeParam(urlRuntime);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!urlRuntime.trim()) {
|
||||
options.setContainerRuntime('');
|
||||
setHandledRuntimeParam('');
|
||||
return;
|
||||
if (resolution.forceViewMode && options.viewMode() !== resolution.forceViewMode) {
|
||||
options.setViewMode(resolution.forceViewMode);
|
||||
}
|
||||
|
||||
if (options.viewMode() !== 'app-container') {
|
||||
options.setViewMode('app-container');
|
||||
}
|
||||
options.setContainerRuntime(urlRuntime);
|
||||
if (!options.showFilters()) {
|
||||
options.setContainerRuntime(resolution.runtime);
|
||||
if (resolution.runtime && !options.showFilters()) {
|
||||
options.setShowFilters(true);
|
||||
}
|
||||
setHandledRuntimeParam(urlRuntime);
|
||||
@@ -269,13 +249,13 @@ export function useDashboardWorkloadUrlSync(options: DashboardWorkloadUrlSyncOpt
|
||||
createEffect(() => {
|
||||
if (!isWorkloadsRoute()) return;
|
||||
|
||||
const parsed = parseWorkloadsLinkSearch(location.search);
|
||||
const urlType = parsed.type ?? '';
|
||||
const urlRuntime = parsed.runtime ?? '';
|
||||
const urlContext = parsed.context ?? '';
|
||||
const urlNamespace = parsed.namespace ?? '';
|
||||
const urlAgent = parsed.agent ?? '';
|
||||
const urlResource = parsed.resource ?? '';
|
||||
const parsed = workloadUrlParams();
|
||||
const urlType = parsed.type;
|
||||
const urlRuntime = parsed.runtime;
|
||||
const urlContext = parsed.context;
|
||||
const urlNamespace = parsed.namespace;
|
||||
const urlAgent = parsed.agent;
|
||||
const urlResource = parsed.resource;
|
||||
|
||||
if (handledTypeParam() !== urlType) return;
|
||||
if (handledRuntimeParam() !== urlRuntime) return;
|
||||
@@ -284,40 +264,16 @@ export function useDashboardWorkloadUrlSync(options: DashboardWorkloadUrlSyncOpt
|
||||
if (handledAgentParam() !== urlAgent) return;
|
||||
if (urlResource) return;
|
||||
|
||||
const currentParams = new URLSearchParams(location.search);
|
||||
const nextParams = new URLSearchParams(location.search);
|
||||
const nextType = options.viewMode() === 'all' ? '' : options.viewMode();
|
||||
const nextRuntime =
|
||||
options.viewMode() === 'app-container' ? options.containerRuntime().trim() : '';
|
||||
const nextContext =
|
||||
options.viewMode() === 'pod' ? (options.selectedKubernetesContext() ?? '') : '';
|
||||
const nextNamespace =
|
||||
options.viewMode() === 'pod' ? (options.selectedKubernetesNamespace() ?? '') : '';
|
||||
const nextAgent =
|
||||
options.viewMode() === 'pod'
|
||||
? ''
|
||||
: (options.selectedNode() ?? options.selectedHostHint() ?? '');
|
||||
|
||||
const managedPath = buildWorkloadsPath({
|
||||
type: nextType || null,
|
||||
runtime: nextRuntime || null,
|
||||
context: nextContext || null,
|
||||
namespace: nextNamespace || null,
|
||||
agent: nextAgent || null,
|
||||
const nextPath = resolveDashboardManagedWorkloadsNavigateTarget({
|
||||
currentSearch: location.search,
|
||||
viewMode: options.viewMode(),
|
||||
containerRuntime: options.containerRuntime(),
|
||||
selectedKubernetesContext: options.selectedKubernetesContext(),
|
||||
selectedKubernetesNamespace: options.selectedKubernetesNamespace(),
|
||||
selectedNode: options.selectedNode(),
|
||||
selectedHostHint: options.selectedHostHint(),
|
||||
});
|
||||
const managedUrl = new URL(managedPath, 'http://pulse.local');
|
||||
nextParams.delete(WORKLOADS_QUERY_PARAMS.type);
|
||||
nextParams.delete(WORKLOADS_QUERY_PARAMS.runtime);
|
||||
nextParams.delete(WORKLOADS_QUERY_PARAMS.context);
|
||||
nextParams.delete(WORKLOADS_QUERY_PARAMS.namespace);
|
||||
nextParams.delete(WORKLOADS_QUERY_PARAMS.agent);
|
||||
managedUrl.searchParams.forEach((value, key) => {
|
||||
nextParams.set(key, value);
|
||||
});
|
||||
|
||||
if (!areSearchParamsEquivalent(currentParams, nextParams)) {
|
||||
const nextSearch = nextParams.toString();
|
||||
const nextPath = nextSearch ? `${WORKLOADS_PATH}?${nextSearch}` : WORKLOADS_PATH;
|
||||
if (nextPath) {
|
||||
scheduleUrlSyncNavigate(nextPath);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -93,6 +93,7 @@ import dashboardSelectionStateSource from '@/components/Dashboard/useDashboardSe
|
||||
import dashboardWorkloadDerivedStateSource from '@/components/Dashboard/useDashboardWorkloadDerivedState.ts?raw';
|
||||
import dashboardWorkloadFilterOptionsSource from '@/components/Dashboard/useDashboardWorkloadFilterOptions.ts?raw';
|
||||
import dashboardWorkloadRouteModelSource from '@/components/Dashboard/dashboardWorkloadRouteModel.ts?raw';
|
||||
import dashboardWorkloadUrlSyncModelSource from '@/components/Dashboard/dashboardWorkloadUrlSyncModel.ts?raw';
|
||||
import dashboardWorkloadRouteStateSource from '@/components/Dashboard/useDashboardWorkloadRouteState.ts?raw';
|
||||
import dashboardWorkloadUrlSyncSource from '@/components/Dashboard/useDashboardWorkloadUrlSync.ts?raw';
|
||||
import dashboardStateSource from '@/components/Dashboard/useDashboardState.ts?raw';
|
||||
@@ -566,13 +567,29 @@ describe('frontend resource type boundaries', () => {
|
||||
'const containerRuntimeFilterConfig = createMemo',
|
||||
);
|
||||
expect(dashboardWorkloadRouteStateSource).not.toContain('const [handledTypeParam, setHandledTypeParam]');
|
||||
expect(dashboardWorkloadUrlSyncSource).toContain('buildWorkloadsPath({');
|
||||
expect(dashboardWorkloadUrlSyncSource).toContain('normalizeWorkloadViewModeParam');
|
||||
expect(dashboardWorkloadUrlSyncSource).not.toContain('buildWorkloadsPath({');
|
||||
expect(dashboardWorkloadUrlSyncSource).not.toContain('normalizeWorkloadViewModeParam');
|
||||
expect(dashboardWorkloadUrlSyncSource).not.toContain('parseWorkloadsLinkSearch');
|
||||
expect(dashboardWorkloadUrlSyncSource).toContain("from './dashboardWorkloadRouteModel'");
|
||||
expect(dashboardWorkloadUrlSyncSource).toContain("from './dashboardWorkloadUrlSyncModel'");
|
||||
expect(dashboardWorkloadUrlSyncSource).toContain(
|
||||
'const [handledTypeParam, setHandledTypeParam]',
|
||||
);
|
||||
expect(dashboardWorkloadUrlSyncSource).toContain('parseWorkloadsLinkSearch');
|
||||
expect(dashboardWorkloadUrlSyncSource).toContain('parseDashboardWorkloadUrlParams');
|
||||
expect(dashboardWorkloadUrlSyncSource).toContain(
|
||||
'resolveDashboardManagedWorkloadsNavigateTarget({',
|
||||
);
|
||||
expect(dashboardWorkloadUrlSyncModelSource).toContain('parseWorkloadsLinkSearch(search)');
|
||||
expect(dashboardWorkloadUrlSyncModelSource).toContain('buildWorkloadsPath({');
|
||||
expect(dashboardWorkloadUrlSyncModelSource).toContain(
|
||||
'resolveDashboardManagedWorkloadsNavigateTarget',
|
||||
);
|
||||
expect(dashboardWorkloadUrlSyncModelSource).toContain(
|
||||
'resolveDashboardWorkloadRuntimeParam',
|
||||
);
|
||||
expect(dashboardWorkloadUrlSyncModelSource).toContain(
|
||||
'normalizeWorkloadViewModeParam(params.type)',
|
||||
);
|
||||
expect(dashboardControlsStateSource).toContain('useBreakpoint');
|
||||
expect(dashboardControlsStateSource).toContain('useColumnVisibility');
|
||||
expect(dashboardControlsStateSource).toContain('usePersistentSignal');
|
||||
|
||||
@@ -1941,8 +1941,10 @@ class SubsystemLookupTest(unittest.TestCase):
|
||||
result = lookup_paths(
|
||||
[
|
||||
"frontend-modern/src/components/Dashboard/dashboardWorkloadRouteModel.ts",
|
||||
"frontend-modern/src/components/Dashboard/dashboardWorkloadUrlSyncModel.ts",
|
||||
"frontend-modern/src/components/Dashboard/useDashboardWorkloadFilterOptions.ts",
|
||||
"frontend-modern/src/components/Dashboard/useDashboardWorkloadRouteState.ts",
|
||||
"frontend-modern/src/components/Dashboard/useDashboardWorkloadUrlSync.ts",
|
||||
]
|
||||
)
|
||||
self.assertEqual(result["unowned_runtime_files"], [])
|
||||
|
||||
Reference in New Issue
Block a user