|
1 | | -import { Navigate, Route, Routes } from "react-router-dom"; |
| 1 | +import { Suspense, lazy } from "react"; |
| 2 | +import { Navigate, Route, Routes, useParams } from "react-router-dom"; |
2 | 3 | import { AuthGate } from "./lib/auth"; |
3 | 4 | import { AppShell } from "./components/AppShell"; |
4 | | -import { DashboardPage } from "./pages/DashboardPage"; |
5 | | -import { StatusPage } from "./pages/StatusPage"; |
6 | | -import { PatternsPage } from "./pages/PatternsPage"; |
7 | | -import { PatternDetailPage } from "./pages/PatternDetailPage"; |
8 | | -import { ShadowPage } from "./pages/ShadowPage"; |
9 | | -import { ShadowDetailPage } from "./pages/ShadowDetailPage"; |
10 | | -import { DetectPage } from "./pages/DetectPage"; |
11 | | -import { DetectDetailPage } from "./pages/DetectDetailPage"; |
12 | | -import { SystemPromptPage } from "./pages/SystemPromptPage"; |
13 | | -import { ServicesPage } from "./pages/ServicesPage"; |
| 5 | +import { SkCard } from "./components/Skeleton"; |
| 6 | +// Hot paths (the 3am set) stay in the main chunk: |
| 7 | +import { NowPage } from "./pages/NowPage"; |
14 | 8 | import { IncidentsPage } from "./pages/IncidentsPage"; |
15 | 9 | import { IncidentDetailPage } from "./pages/IncidentDetailPage"; |
16 | | -import { AnalysesPage } from "./pages/AnalysesPage"; |
17 | 10 | import { AnalysesListPage } from "./pages/AnalysesListPage"; |
18 | | -import { AnalysisDetailPage } from "./pages/AnalysisDetailPage"; |
19 | | -import { IncidentsConfigPage } from "./pages/IncidentsConfigPage"; |
20 | | -import { AgentConfigPage } from "./pages/AgentConfigPage"; |
21 | | -import { MembersPage } from "./pages/MembersPage"; |
22 | | -import { TeamsPage } from "./pages/TeamsPage"; |
23 | | -import { RunbooksPage } from "./pages/RunbooksPage"; |
| 11 | +import { AgentOverviewPage } from "./pages/AgentOverviewPage"; |
| 12 | +import { PatternsPage } from "./pages/PatternsPage"; |
| 13 | +import { DecisionsPage } from "./pages/DecisionsPage"; |
| 14 | +import { ServicesPage } from "./pages/ServicesPage"; |
| 15 | + |
| 16 | +// Cold paths load on demand (route-level splitting, no extra deps) — |
| 17 | +// keeps the main chunk inside the ≤120KB gzip budget. |
| 18 | +const AnalysisDetailPage = lazyPage( |
| 19 | + () => import("./pages/AnalysisDetailPage"), |
| 20 | + "AnalysisDetailPage", |
| 21 | +); |
| 22 | +const PatternDetailPage = lazyPage( |
| 23 | + () => import("./pages/PatternDetailPage"), |
| 24 | + "PatternDetailPage", |
| 25 | +); |
| 26 | +const DetectDetailPage = lazyPage( |
| 27 | + () => import("./pages/DetectDetailPage"), |
| 28 | + "DetectDetailPage", |
| 29 | +); |
| 30 | +const ShadowDetailPage = lazyPage( |
| 31 | + () => import("./pages/ShadowDetailPage"), |
| 32 | + "ShadowDetailPage", |
| 33 | +); |
| 34 | +const SystemPromptPage = lazyPage( |
| 35 | + () => import("./pages/SystemPromptPage"), |
| 36 | + "SystemPromptPage", |
| 37 | +); |
| 38 | +const RunbooksPage = lazyPage(() => import("./pages/RunbooksPage"), "RunbooksPage"); |
| 39 | +const PeoplePage = lazyPage(() => import("./pages/PeoplePage"), "PeoplePage"); |
| 40 | +const SettingsPage = lazyPage(() => import("./pages/SettingsPage"), "SettingsPage"); |
| 41 | + |
| 42 | +// lazyPage adapts our named-export pages to React.lazy and wraps them in a |
| 43 | +// Suspense fallback that mirrors a loading card (no blank flash). |
| 44 | +function lazyPage( |
| 45 | + loader: () => Promise<Record<string, unknown>>, |
| 46 | + name: string, |
| 47 | +) { |
| 48 | + const L = lazy(async () => { |
| 49 | + const m = await loader(); |
| 50 | + return { default: m[name] as React.ComponentType }; |
| 51 | + }); |
| 52 | + return function LazyRoute() { |
| 53 | + return ( |
| 54 | + <Suspense |
| 55 | + fallback={ |
| 56 | + <main className="flex-1 overflow-auto p-6"> |
| 57 | + <SkCard lines={4} /> |
| 58 | + </main> |
| 59 | + } |
| 60 | + > |
| 61 | + <L /> |
| 62 | + </Suspense> |
| 63 | + ); |
| 64 | + }; |
| 65 | +} |
24 | 66 |
|
| 67 | +// Route map per UX_REDESIGN §3.3. Three zones (Respond / Agent / Manage) + |
| 68 | +// a full set of legacy redirects so pre-redesign bookmarks and alert |
| 69 | +// deep-links keep working (back-stack-integrity / deep-linking rules). |
25 | 70 | export default function App() { |
26 | 71 | return ( |
27 | 72 | <AuthGate> |
28 | 73 | <Routes> |
29 | 74 | <Route element={<AppShell />}> |
30 | | - <Route index element={<Navigate to="/dashboard" replace />} /> |
31 | | - <Route path="/dashboard" element={<DashboardPage />} /> |
| 75 | + <Route index element={<Navigate to="/now" replace />} /> |
| 76 | + |
| 77 | + {/* Respond */} |
| 78 | + <Route path="/now" element={<NowPage />} /> |
32 | 79 | <Route path="/incidents" element={<IncidentsPage />} /> |
33 | | - <Route path="/analyses" element={<AnalysesListPage />} /> |
34 | 80 | <Route path="/incidents/:id" element={<IncidentDetailPage />} /> |
35 | | - <Route path="/incidents/:id/analyses" element={<AnalysesPage />} /> |
| 81 | + <Route path="/analyses" element={<AnalysesListPage />} /> |
36 | 82 | <Route |
37 | 83 | path="/incidents/:id/analyses/:analysisId" |
38 | 84 | element={<AnalysisDetailPage />} |
39 | 85 | /> |
40 | | - <Route path="/status" element={<StatusPage />} /> |
41 | | - <Route path="/patterns" element={<PatternsPage />} /> |
42 | | - <Route path="/patterns/:id" element={<PatternDetailPage />} /> |
43 | | - <Route path="/shadow" element={<ShadowPage />} /> |
44 | | - <Route path="/shadow/:patternId" element={<ShadowDetailPage />} /> |
45 | | - <Route path="/detect" element={<DetectPage />} /> |
46 | | - <Route path="/detect/system-prompt" element={<SystemPromptPage />} /> |
47 | | - <Route path="/detect/:id" element={<DetectDetailPage />} /> |
48 | | - <Route path="/services" element={<ServicesPage />} /> |
49 | | - <Route path="/runbooks" element={<RunbooksPage />} /> |
50 | | - <Route path="/members" element={<MembersPage />} /> |
51 | | - <Route path="/teams" element={<TeamsPage />} /> |
52 | | - <Route path="/config/incidents" element={<IncidentsConfigPage />} /> |
53 | | - <Route path="/config/agent" element={<AgentConfigPage />} /> |
54 | | - <Route path="*" element={<Navigate to="/dashboard" replace />} /> |
| 86 | + |
| 87 | + {/* Agent */} |
| 88 | + <Route path="/agent" element={<AgentOverviewPage />} /> |
| 89 | + <Route path="/agent/patterns" element={<PatternsPage />} /> |
| 90 | + <Route path="/agent/patterns/:id" element={<PatternDetailPage />} /> |
| 91 | + <Route path="/agent/decisions" element={<DecisionsPage />} /> |
| 92 | + <Route |
| 93 | + path="/agent/decisions/system-prompt" |
| 94 | + element={<SystemPromptPage />} |
| 95 | + /> |
| 96 | + <Route |
| 97 | + path="/agent/decisions/detect/:id" |
| 98 | + element={<DetectDetailPage />} |
| 99 | + /> |
| 100 | + <Route |
| 101 | + path="/agent/decisions/shadow/:patternId" |
| 102 | + element={<ShadowDetailPage />} |
| 103 | + /> |
| 104 | + <Route path="/agent/services" element={<ServicesPage />} /> |
| 105 | + <Route path="/agent/runbooks" element={<RunbooksPage />} /> |
| 106 | + |
| 107 | + {/* Manage */} |
| 108 | + <Route path="/people" element={<PeoplePage />} /> |
| 109 | + <Route path="/settings" element={<SettingsPage />} /> |
| 110 | + |
| 111 | + {/* Legacy redirects (old IA → new homes) */} |
| 112 | + <Route path="/dashboard" element={<Navigate to="/now" replace />} /> |
| 113 | + <Route path="/status" element={<Navigate to="/agent" replace />} /> |
| 114 | + <Route |
| 115 | + path="/patterns" |
| 116 | + element={<Navigate to="/agent/patterns" replace />} |
| 117 | + /> |
| 118 | + <Route path="/patterns/:id" element={<PatternIdRedirect />} /> |
| 119 | + <Route |
| 120 | + path="/detect" |
| 121 | + element={<Navigate to="/agent/decisions?tab=detect" replace />} |
| 122 | + /> |
| 123 | + <Route |
| 124 | + path="/detect/system-prompt" |
| 125 | + element={<Navigate to="/agent/decisions/system-prompt" replace />} |
| 126 | + /> |
| 127 | + <Route path="/detect/:id" element={<DetectIdRedirect />} /> |
| 128 | + <Route |
| 129 | + path="/shadow" |
| 130 | + element={<Navigate to="/agent/decisions?tab=shadow" replace />} |
| 131 | + /> |
| 132 | + <Route path="/shadow/:patternId" element={<ShadowIdRedirect />} /> |
| 133 | + <Route |
| 134 | + path="/services" |
| 135 | + element={<Navigate to="/agent/services" replace />} |
| 136 | + /> |
| 137 | + <Route |
| 138 | + path="/runbooks" |
| 139 | + element={<Navigate to="/agent/runbooks" replace />} |
| 140 | + /> |
| 141 | + <Route |
| 142 | + path="/members" |
| 143 | + element={<Navigate to="/people?tab=members" replace />} |
| 144 | + /> |
| 145 | + <Route |
| 146 | + path="/teams" |
| 147 | + element={<Navigate to="/people?tab=teams" replace />} |
| 148 | + /> |
| 149 | + <Route |
| 150 | + path="/config/incidents" |
| 151 | + element={<Navigate to="/settings" replace />} |
| 152 | + /> |
| 153 | + <Route |
| 154 | + path="/config/agent" |
| 155 | + element={<Navigate to="/settings?tab=agent" replace />} |
| 156 | + /> |
| 157 | + <Route |
| 158 | + path="/postmortems" |
| 159 | + element={<Navigate to="/analyses?tab=postmortems" replace />} |
| 160 | + /> |
| 161 | + <Route |
| 162 | + path="/incidents/:id/analyses" |
| 163 | + element={<IncidentAnalysesRedirect />} |
| 164 | + /> |
| 165 | + |
| 166 | + <Route path="*" element={<Navigate to="/now" replace />} /> |
55 | 167 | </Route> |
56 | 168 | </Routes> |
57 | 169 | </AuthGate> |
58 | 170 | ); |
59 | 171 | } |
| 172 | + |
| 173 | +function PatternIdRedirect() { |
| 174 | + const { id } = useParams(); |
| 175 | + return <Navigate to={`/agent/patterns/${id}`} replace />; |
| 176 | +} |
| 177 | + |
| 178 | +function DetectIdRedirect() { |
| 179 | + const { id } = useParams(); |
| 180 | + return <Navigate to={`/agent/decisions/detect/${id}`} replace />; |
| 181 | +} |
| 182 | + |
| 183 | +function ShadowIdRedirect() { |
| 184 | + const { patternId } = useParams(); |
| 185 | + return <Navigate to={`/agent/decisions/shadow/${patternId}`} replace />; |
| 186 | +} |
| 187 | + |
| 188 | +function IncidentAnalysesRedirect() { |
| 189 | + const { id } = useParams(); |
| 190 | + return <Navigate to={`/analyses?incident=${id}`} replace />; |
| 191 | +} |
0 commit comments