feat(web-ui): reorganize network and security views into tabbed subviews with route-aware navigation

This commit is contained in:
2026-04-08 07:45:26 +00:00
parent 1b37944aab
commit 2b76e05a40
22 changed files with 973 additions and 610 deletions

View File

@@ -30,6 +30,7 @@ export interface IConfigState {
export interface IUiState {
activeView: string;
activeSubview: string | null;
sidebarCollapsed: boolean;
autoRefresh: boolean;
refreshInterval: number; // milliseconds
@@ -116,16 +117,24 @@ export const configStatePart = await appState.getStatePart<IConfigState>(
// Determine initial view from URL path
const getInitialView = (): string => {
const path = typeof window !== 'undefined' ? window.location.pathname : '/';
const validViews = ['overview', 'network', 'emails', 'logs', 'routes', 'apitokens', 'configuration', 'security', 'certificates', 'remoteingress', 'sourceprofiles', 'networktargets', 'targetprofiles'];
const validViews = ['overview', 'network', 'emails', 'logs', 'apitokens', 'configuration', 'security', 'certificates', 'remoteingress', 'vpn'];
const segments = path.split('/').filter(Boolean);
const view = segments[0];
return validViews.includes(view) ? view : 'overview';
};
// Determine initial subview (second URL segment) from the path
const getInitialSubview = (): string | null => {
const path = typeof window !== 'undefined' ? window.location.pathname : '/';
const segments = path.split('/').filter(Boolean);
return segments[1] ?? null;
};
export const uiStatePart = await appState.getStatePart<IUiState>(
'ui',
{
activeView: getInitialView(),
activeSubview: getInitialSubview(),
sidebarCollapsed: false,
autoRefresh: true,
refreshInterval: 1000, // 1 second
@@ -435,15 +444,6 @@ export const setActiveViewAction = uiStatePart.createAction<string>(async (state
}, 100);
}
// If switching to routes view, ensure we fetch route data
if (viewName === 'routes' && currentState.activeView !== 'routes') {
setTimeout(() => {
routeManagementStatePart.dispatchAction(fetchMergedRoutesAction, null);
// Also fetch profiles/targets for the Create Route dropdowns
profilesTargetsStatePart.dispatchAction(fetchProfilesAndTargetsAction, null);
}, 100);
}
// If switching to apitokens view, ensure we fetch token data
if (viewName === 'apitokens' && currentState.activeView !== 'apitokens') {
setTimeout(() => {
@@ -458,20 +458,6 @@ export const setActiveViewAction = uiStatePart.createAction<string>(async (state
}, 100);
}
// If switching to security profiles or network targets views, fetch profiles/targets data
if ((viewName === 'sourceprofiles' || viewName === 'networktargets') && currentState.activeView !== viewName) {
setTimeout(() => {
profilesTargetsStatePart.dispatchAction(fetchProfilesAndTargetsAction, null);
}, 100);
}
// If switching to target profiles view, fetch target profiles data
if (viewName === 'targetprofiles' && currentState.activeView !== viewName) {
setTimeout(() => {
targetProfilesStatePart.dispatchAction(fetchTargetProfilesAction, null);
}, 100);
}
return {
...currentState,
activeView: viewName,