diff --git a/web/src/components/layout/AppShell.dom.test.tsx b/web/src/components/layout/AppShell.dom.test.tsx
new file mode 100644
index 0000000..0256aa6
--- /dev/null
+++ b/web/src/components/layout/AppShell.dom.test.tsx
@@ -0,0 +1,64 @@
+// @vitest-environment jsdom
+import { render, screen } from '@testing-library/react';
+import { MemoryRouter } from 'react-router-dom';
+import { describe, expect, it, vi } from 'vitest';
+import { AppShell } from './AppShell';
+
+vi.mock('./Sidebar', () => ({
+ Sidebar: () => ,
+}));
+
+vi.mock('./Header', () => ({
+ Header: () => ,
+}));
+
+vi.mock('./RightPanel', () => ({
+ RightPanel: () => ,
+}));
+
+vi.mock('../../views/HomeView', () => ({
+ HomeView: () =>
Home view
,
+}));
+
+vi.mock('../../views/PortfolioView', () => ({
+ PortfolioView: () => Portfolio view
,
+}));
+
+vi.mock('../../views/ResearchView', () => ({
+ ResearchView: () => Research view
,
+}));
+
+vi.mock('../../views/MarketsView', () => ({
+ MarketsView: () => Markets view
,
+}));
+
+vi.mock('../../views/ScreenerView', () => ({
+ ScreenerView: () => Screener view
,
+}));
+
+vi.mock('../../views/WatchlistView', () => ({
+ WatchlistView: () => Watchlist view
,
+}));
+
+vi.mock('../../views/AlertsView', () => ({
+ AlertsView: () => Alerts view
,
+}));
+
+vi.mock('../../views/SettingsView', () => ({
+ SettingsView: () => Settings view
,
+}));
+
+describe('AppShell routing fallback', () => {
+ it('shows a useful 404 state for unknown routes', () => {
+ render(
+
+
+ ,
+ );
+
+ expect(screen.getByRole('heading', { name: 'Route not found' })).toBeInTheDocument();
+ expect(screen.getByText('/missing/workspace')).toBeInTheDocument();
+ expect(screen.getByRole('link', { name: 'Return home' })).toHaveAttribute('href', '/');
+ expect(screen.queryByText('Home view')).not.toBeInTheDocument();
+ });
+});
diff --git a/web/src/components/layout/AppShell.tsx b/web/src/components/layout/AppShell.tsx
index b216f6d..ccfe5cc 100644
--- a/web/src/components/layout/AppShell.tsx
+++ b/web/src/components/layout/AppShell.tsx
@@ -1,4 +1,4 @@
-import { Routes, Route } from 'react-router-dom';
+import { Link, Routes, Route, useLocation } from 'react-router-dom';
import { Sidebar } from './Sidebar';
import { Header } from './Header';
import { RightPanel } from './RightPanel';
@@ -11,6 +11,54 @@ import { WatchlistView } from '../../views/WatchlistView';
import { AlertsView } from '../../views/AlertsView';
import { SettingsView } from '../../views/SettingsView';
+function NotFoundView() {
+ const location = useLocation();
+
+ return (
+
+
+ 404
+
+
+ Route not found
+
+
+ No trading workspace exists at {location.pathname}. The app is still running normally.
+
+
+ Return home
+
+
+ );
+}
+
export function AppShell() {
return (
@@ -35,8 +83,7 @@ export function AppShell() {
} />
} />
} />
- {/* Fallback */}
- } />
+ } />