Skip to content

Commit 1223461

Browse files
authored
feat(ui): ops-first dashboard redesign with dark-first themed UI
feat(ui): ops-first dashboard redesign with dark-first themed UI
2 parents cddf32f + c4e99c0 commit 1223461

55 files changed

Lines changed: 8160 additions & 3875 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

ui/index.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
77
<title>Versus Incident — Admin</title>
88
</head>
9-
<body class="h-full bg-ink-50 text-ink-900 antialiased">
9+
<body class="h-full antialiased">
1010
<div id="root" class="h-full"></div>
1111
<script type="module" src="/src/main.tsx"></script>
1212
</body>

ui/src/App.tsx

Lines changed: 169 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,59 +1,191 @@
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";
23
import { AuthGate } from "./lib/auth";
34
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";
148
import { IncidentsPage } from "./pages/IncidentsPage";
159
import { IncidentDetailPage } from "./pages/IncidentDetailPage";
16-
import { AnalysesPage } from "./pages/AnalysesPage";
1710
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+
}
2466

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).
2570
export default function App() {
2671
return (
2772
<AuthGate>
2873
<Routes>
2974
<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 />} />
3279
<Route path="/incidents" element={<IncidentsPage />} />
33-
<Route path="/analyses" element={<AnalysesListPage />} />
3480
<Route path="/incidents/:id" element={<IncidentDetailPage />} />
35-
<Route path="/incidents/:id/analyses" element={<AnalysesPage />} />
81+
<Route path="/analyses" element={<AnalysesListPage />} />
3682
<Route
3783
path="/incidents/:id/analyses/:analysisId"
3884
element={<AnalysisDetailPage />}
3985
/>
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 />} />
55167
</Route>
56168
</Routes>
57169
</AuthGate>
58170
);
59171
}
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

Comments
 (0)