fix(B10): add explicit not found route
This commit is contained in:
parent
36544d10d4
commit
ee7404ac61
64
web/src/components/layout/AppShell.dom.test.tsx
Normal file
64
web/src/components/layout/AppShell.dom.test.tsx
Normal file
@ -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: () => <aside>Sidebar</aside>,
|
||||
}));
|
||||
|
||||
vi.mock('./Header', () => ({
|
||||
Header: () => <header>Header</header>,
|
||||
}));
|
||||
|
||||
vi.mock('./RightPanel', () => ({
|
||||
RightPanel: () => <aside>Right panel</aside>,
|
||||
}));
|
||||
|
||||
vi.mock('../../views/HomeView', () => ({
|
||||
HomeView: () => <div>Home view</div>,
|
||||
}));
|
||||
|
||||
vi.mock('../../views/PortfolioView', () => ({
|
||||
PortfolioView: () => <div>Portfolio view</div>,
|
||||
}));
|
||||
|
||||
vi.mock('../../views/ResearchView', () => ({
|
||||
ResearchView: () => <div>Research view</div>,
|
||||
}));
|
||||
|
||||
vi.mock('../../views/MarketsView', () => ({
|
||||
MarketsView: () => <div>Markets view</div>,
|
||||
}));
|
||||
|
||||
vi.mock('../../views/ScreenerView', () => ({
|
||||
ScreenerView: () => <div>Screener view</div>,
|
||||
}));
|
||||
|
||||
vi.mock('../../views/WatchlistView', () => ({
|
||||
WatchlistView: () => <div>Watchlist view</div>,
|
||||
}));
|
||||
|
||||
vi.mock('../../views/AlertsView', () => ({
|
||||
AlertsView: () => <div>Alerts view</div>,
|
||||
}));
|
||||
|
||||
vi.mock('../../views/SettingsView', () => ({
|
||||
SettingsView: () => <div>Settings view</div>,
|
||||
}));
|
||||
|
||||
describe('AppShell routing fallback', () => {
|
||||
it('shows a useful 404 state for unknown routes', () => {
|
||||
render(
|
||||
<MemoryRouter initialEntries={['/missing/workspace']}>
|
||||
<AppShell />
|
||||
</MemoryRouter>,
|
||||
);
|
||||
|
||||
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();
|
||||
});
|
||||
});
|
||||
@ -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 (
|
||||
<section
|
||||
aria-labelledby="not-found-title"
|
||||
style={{
|
||||
minHeight: 420,
|
||||
borderRadius: 24,
|
||||
border: '1px solid #E5E7EB',
|
||||
background: 'linear-gradient(135deg, #FFF7ED 0%, #EFF6FF 100%)',
|
||||
padding: '56px 32px',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
textAlign: 'center',
|
||||
color: '#111827',
|
||||
}}
|
||||
>
|
||||
<div style={{ fontSize: 12, fontWeight: 900, letterSpacing: '0.16em', color: '#2563EB', textTransform: 'uppercase' }}>
|
||||
404
|
||||
</div>
|
||||
<h1 id="not-found-title" style={{ margin: '10px 0 8px', fontSize: 34, fontWeight: 900 }}>
|
||||
Route not found
|
||||
</h1>
|
||||
<p style={{ margin: 0, maxWidth: 520, color: '#4B5563', fontSize: 14, lineHeight: 1.6 }}>
|
||||
No trading workspace exists at <code style={{ fontWeight: 800 }}>{location.pathname}</code>. The app is still running normally.
|
||||
</p>
|
||||
<Link
|
||||
to="/"
|
||||
style={{
|
||||
marginTop: 24,
|
||||
borderRadius: 999,
|
||||
background: '#111827',
|
||||
color: '#fff',
|
||||
padding: '10px 18px',
|
||||
fontSize: 13,
|
||||
fontWeight: 800,
|
||||
textDecoration: 'none',
|
||||
}}
|
||||
>
|
||||
Return home
|
||||
</Link>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
export function AppShell() {
|
||||
return (
|
||||
<div className="dashboard-shell">
|
||||
@ -35,8 +83,7 @@ export function AppShell() {
|
||||
<Route path="/watchlist" element={<WatchlistView />} />
|
||||
<Route path="/alerts" element={<AlertsView />} />
|
||||
<Route path="/settings" element={<SettingsView />} />
|
||||
{/* Fallback */}
|
||||
<Route path="*" element={<HomeView />} />
|
||||
<Route path="*" element={<NotFoundView />} />
|
||||
</Routes>
|
||||
</main>
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user