Route app root through dashboard shell

This commit is contained in:
rcourtman
2026-03-27 11:20:42 +00:00
parent 05b94df130
commit ffccbef36e
4 changed files with 21 additions and 4 deletions
@@ -463,6 +463,12 @@ instead of wiring product-surface components such as
`frontend-modern/src/components/Storage/Storage.tsx` directly into the router,
so hosted bootstrap ownership stays at the app boundary rather than leaking
route concerns back into feature components.
That same authenticated route shell also owns the canonical post-auth landing
path. `frontend-modern/src/App.tsx` must send `/` through the dashboard page
shell first, so existing operators land on the overview route and first-time
operators hit the governed dashboard empty state that forwards them into
Infrastructure Install, instead of carrying a root-only redirect to the
infrastructure route that bypasses the shared landing contract.
The Pro license settings surface now follows the same rule as well. Changes to
`frontend-modern/src/components/Settings/ProLicensePanel.tsx` must carry this
contract and the dedicated Pro-license proof file instead of remaining an
@@ -192,6 +192,11 @@ work extends shared components instead of creating new local variants.
must name the canonical install workspace explicitly and expose the same
first-host next step instead of falling back to passive “nothing here yet”
wording.
11. Keep the authenticated app root aligned with that same first-session path.
`frontend-modern/src/App.tsx` must land `/` on the dashboard shell and let
the governed dashboard empty state route first-time operators into
Infrastructure Install, instead of preserving a separate root-only jump to
`/infrastructure` that drifts from the rest of the onboarding contract.
## Current State
+6 -4
View File
@@ -34,6 +34,7 @@ import { aiChatStore } from './stores/aiChat';
import { useKeyboardShortcuts } from './hooks/useKeyboardShortcuts';
import { useKioskMode } from '@/hooks/useKioskMode';
import {
DASHBOARD_PATH,
buildRecoveryPath,
buildInfrastructurePath,
buildStoragePath,
@@ -78,7 +79,8 @@ const SetupCompletionPreviewPage = lazy(() =>
default: module.SetupCompletionPreview,
})),
);
const ROOT_INFRASTRUCTURE_PATH = buildInfrastructurePath();
const ROOT_DASHBOARD_PATH = DASHBOARD_PATH;
const INFRASTRUCTURE_ROUTE_PATH = buildInfrastructurePath();
const ROOT_WORKLOADS_PATH = buildWorkloadsPath();
const STORAGE_PATH = buildStoragePath();
const RECOVERY_ROUTE_PATH = buildRecoveryPath();
@@ -378,13 +380,13 @@ function App() {
<Route path="/cloud" component={CloudPricingPage} />
<Route path="/cloud/signup" component={HostedSignupPage} />
<Route path="/preview/setup-complete" component={SetupCompletionPreviewPage} />
<Route path="/dashboard" component={DashboardPage} />
<Route path="/" component={() => <Navigate href={ROOT_INFRASTRUCTURE_PATH} />} />
<Route path={ROOT_DASHBOARD_PATH} component={DashboardPage} />
<Route path="/" component={() => <Navigate href={ROOT_DASHBOARD_PATH} />} />
<Route path={ROOT_WORKLOADS_PATH} component={WorkloadsView} />
<Route path={STORAGE_PATH} component={StoragePage} />
<Route path={RECOVERY_ROUTE_PATH} component={RecoveryRoute} />
<Route path="/ceph" component={CephPage} />
<Route path={ROOT_INFRASTRUCTURE_PATH} component={InfrastructurePage} />
<Route path={INFRASTRUCTURE_ROUTE_PATH} component={InfrastructurePage} />
<Route path="/alerts/*" component={AlertsPage} />
<Route path="/ai/*" component={AIIntelligencePage} />
@@ -5,9 +5,13 @@ import appRuntimeStateSource from '@/useAppRuntimeState.ts?raw';
describe('App architecture', () => {
it('keeps App as the entry shell that delegates runtime and chrome ownership', () => {
expect(appSource).toContain('DASHBOARD_PATH,');
expect(appSource).toContain("import { AppLayout } from '@/AppLayout';");
expect(appSource).toContain("import { useAppRuntimeState } from '@/useAppRuntimeState';");
expect(appSource).toContain('const runtime = useAppRuntimeState();');
expect(appSource).toContain('const ROOT_DASHBOARD_PATH = DASHBOARD_PATH;');
expect(appSource).toContain('<Route path={ROOT_DASHBOARD_PATH} component={DashboardPage} />');
expect(appSource).toContain('<Route path="/" component={() => <Navigate href={ROOT_DASHBOARD_PATH} />} />');
expect(appSource).toContain("const StoragePage = lazy(() => import('./pages/Storage'));");
expect(appSource).toContain("const OperationsPage = lazy(() => import('./pages/Operations'));");
expect(appSource).not.toContain(