Skip to content

Commit 0f47b58

Browse files
feat(site): refactor health pages (#11025)
1 parent 2e4e0b2 commit 0f47b58

23 files changed

+1618
-464
lines changed

site/.storybook/preview.jsx

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,17 @@ export const decorators = [
3838
},
3939
(Story) => {
4040
return (
41-
<QueryClientProvider client={new QueryClient()}>
41+
<QueryClientProvider
42+
client={
43+
new QueryClient({
44+
defaultOptions: {
45+
queries: {
46+
staleTime: Infinity,
47+
},
48+
},
49+
})
50+
}
51+
>
4252
<Story />
4353
</QueryClientProvider>
4454
);

site/src/AppRouter.tsx

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import AuditPage from "pages/AuditPage/AuditPage";
44
import LoginPage from "pages/LoginPage/LoginPage";
55
import { SetupPage } from "pages/SetupPage/SetupPage";
66
import { TemplateLayout } from "pages/TemplatePage/TemplateLayout";
7+
import { HealthLayout } from "pages/HealthPage/HealthLayout";
78
import TemplatesPage from "pages/TemplatesPage/TemplatesPage";
89
import UsersPage from "pages/UsersPage/UsersPage";
910
import WorkspacesPage from "pages/WorkspacesPage/WorkspacesPage";
@@ -197,9 +198,16 @@ const TemplateInsightsPage = lazy(
197198
() =>
198199
import("./pages/TemplatePage/TemplateInsightsPage/TemplateInsightsPage"),
199200
);
200-
const HealthPage = lazy(() => import("./pages/HealthPage/HealthPage"));
201201
const GroupsPage = lazy(() => import("./pages/GroupsPage/GroupsPage"));
202202
const IconsPage = lazy(() => import("./pages/IconsPage/IconsPage"));
203+
const AccessURLPage = lazy(() => import("./pages/HealthPage/AccessURLPage"));
204+
const DatabasePage = lazy(() => import("./pages/HealthPage/DatabasePage"));
205+
const DERPPage = lazy(() => import("./pages/HealthPage/DERPPage"));
206+
const DERPRegionPage = lazy(() => import("./pages/HealthPage/DERPRegionPage"));
207+
const WebsocketPage = lazy(() => import("./pages/HealthPage/WebsocketPage"));
208+
const WorkspaceProxyHealthPage = lazy(
209+
() => import("./pages/HealthPage/WorkspaceProxyPage"),
210+
);
203211

204212
export const AppRouter: FC = () => {
205213
return (
@@ -214,8 +222,6 @@ export const AppRouter: FC = () => {
214222
<Route element={<DashboardLayout />}>
215223
<Route index element={<Navigate to="/workspaces" replace />} />
216224

217-
<Route path="/health" element={<HealthPage />} />
218-
219225
<Route
220226
path="/external-auth/:provider"
221227
element={<ExternalAuthPage />}
@@ -339,6 +345,21 @@ export const AppRouter: FC = () => {
339345
<Route path="schedule" element={<WorkspaceSchedulePage />} />
340346
</Route>
341347

348+
<Route path="/health" element={<HealthLayout />}>
349+
<Route index element={<Navigate to="access-url" />} />
350+
<Route path="access-url" element={<AccessURLPage />} />
351+
<Route path="database" element={<DatabasePage />} />
352+
<Route path="derp" element={<DERPPage />} />
353+
<Route
354+
path="derp/regions/:regionId"
355+
element={<DERPRegionPage />}
356+
/>
357+
<Route path="websocket" element={<WebsocketPage />} />
358+
<Route
359+
path="workspace-proxy"
360+
element={<WorkspaceProxyHealthPage />}
361+
/>
362+
</Route>
342363
{/* Using path="*"" means "match anything", so this route
343364
acts like a catch-all for URLs that we don't have explicit
344365
routes for. */}

site/src/components/Dashboard/Navbar/Navbar.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ export const Navbar: FC = () => {
1919
const canViewAllUsers = Boolean(permissions.readAllUsers);
2020
const proxyContextValue = useProxy();
2121
const dashboard = useDashboard();
22+
const canViewHealth =
23+
canViewDeployment &&
24+
dashboard.experiments.includes("deployment_health_page");
2225

2326
return (
2427
<NavbarView
@@ -30,6 +33,7 @@ export const Navbar: FC = () => {
3033
canViewAuditLog={canViewAuditLog}
3134
canViewDeployment={canViewDeployment}
3235
canViewAllUsers={canViewAllUsers}
36+
canViewHealth={canViewHealth}
3337
proxyContextValue={
3438
dashboard.experiments.includes("moons") ? proxyContextValue : undefined
3539
}

site/src/components/Dashboard/Navbar/NavbarView.test.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ describe("NavbarView", () => {
3333
canViewAuditLog
3434
canViewDeployment
3535
canViewAllUsers
36+
canViewHealth
3637
/>,
3738
);
3839
const workspacesLink = await screen.findByText(navLanguage.workspaces);
@@ -48,6 +49,7 @@ describe("NavbarView", () => {
4849
canViewAuditLog
4950
canViewDeployment
5051
canViewAllUsers
52+
canViewHealth
5153
/>,
5254
);
5355
const templatesLink = await screen.findByText(navLanguage.templates);
@@ -63,6 +65,7 @@ describe("NavbarView", () => {
6365
canViewAuditLog
6466
canViewDeployment
6567
canViewAllUsers
68+
canViewHealth
6669
/>,
6770
);
6871
const userLink = await screen.findByText(navLanguage.users);
@@ -78,6 +81,7 @@ describe("NavbarView", () => {
7881
canViewAuditLog
7982
canViewDeployment
8083
canViewAllUsers
84+
canViewHealth
8185
/>,
8286
);
8387
const auditLink = await screen.findByText(navLanguage.audit);
@@ -93,6 +97,7 @@ describe("NavbarView", () => {
9397
canViewAuditLog
9498
canViewDeployment
9599
canViewAllUsers
100+
canViewHealth
96101
/>,
97102
);
98103
const auditLink = await screen.findByText(navLanguage.deployment);

site/src/components/Dashboard/Navbar/NavbarView.tsx

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ export interface NavbarViewProps {
3333
canViewAuditLog: boolean;
3434
canViewDeployment: boolean;
3535
canViewAllUsers: boolean;
36+
canViewHealth: boolean;
3637
proxyContextValue?: ProxyContextValue;
3738
}
3839

@@ -50,13 +51,15 @@ interface NavItemsProps {
5051
canViewAuditLog: boolean;
5152
canViewDeployment: boolean;
5253
canViewAllUsers: boolean;
54+
canViewHealth: boolean;
5355
}
5456

5557
const NavItems: FC<NavItemsProps> = ({
5658
className,
5759
canViewAuditLog,
5860
canViewDeployment,
5961
canViewAllUsers,
62+
canViewHealth,
6063
}) => {
6164
const location = useLocation();
6265
const theme = useTheme();
@@ -93,6 +96,11 @@ const NavItems: FC<NavItemsProps> = ({
9396
{Language.deployment}
9497
</NavLink>
9598
)}
99+
{canViewHealth && (
100+
<NavLink css={styles.link} to="/health">
101+
Health
102+
</NavLink>
103+
)}
96104
</nav>
97105
);
98106
};
@@ -106,6 +114,7 @@ export const NavbarView: FC<NavbarViewProps> = ({
106114
canViewAuditLog,
107115
canViewDeployment,
108116
canViewAllUsers,
117+
canViewHealth,
109118
proxyContextValue,
110119
}) => {
111120
const theme = useTheme();
@@ -150,6 +159,7 @@ export const NavbarView: FC<NavbarViewProps> = ({
150159
canViewAuditLog={canViewAuditLog}
151160
canViewDeployment={canViewDeployment}
152161
canViewAllUsers={canViewAllUsers}
162+
canViewHealth={canViewHealth}
153163
/>
154164
</div>
155165
</Drawer>
@@ -167,6 +177,7 @@ export const NavbarView: FC<NavbarViewProps> = ({
167177
canViewAuditLog={canViewAuditLog}
168178
canViewDeployment={canViewDeployment}
169179
canViewAllUsers={canViewAllUsers}
180+
canViewHealth={canViewHealth}
170181
/>
171182

172183
<div css={styles.navMenus}>
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { StoryObj, Meta } from "@storybook/react";
2+
import { AccessURLPage } from "./AccessURLPage";
3+
import { HealthLayout } from "./HealthLayout";
4+
import {
5+
reactRouterOutlet,
6+
reactRouterParameters,
7+
} from "storybook-addon-react-router-v6";
8+
import { useQueryClient } from "react-query";
9+
import { MockHealth } from "testHelpers/entities";
10+
11+
const meta: Meta = {
12+
title: "pages/Health/AccessURL",
13+
render: HealthLayout,
14+
parameters: {
15+
layout: "fullscreen",
16+
reactRouter: reactRouterParameters({
17+
routing: reactRouterOutlet(
18+
{ path: "/health/access-url" },
19+
<AccessURLPage />,
20+
),
21+
}),
22+
},
23+
decorators: [
24+
(Story) => {
25+
const queryClient = useQueryClient();
26+
queryClient.setQueryData(["health"], MockHealth);
27+
return <Story />;
28+
},
29+
],
30+
};
31+
32+
export default meta;
33+
type Story = StoryObj;
34+
35+
export const Default: Story = {};
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import { useOutletContext } from "react-router-dom";
2+
import {
3+
Header,
4+
HeaderTitle,
5+
Main,
6+
GridData,
7+
GridDataLabel,
8+
GridDataValue,
9+
HealthyDot,
10+
} from "./Content";
11+
import { HealthcheckReport } from "api/typesGenerated";
12+
import { Alert } from "components/Alert/Alert";
13+
import { Helmet } from "react-helmet-async";
14+
import { pageTitle } from "utils/page";
15+
16+
export const AccessURLPage = () => {
17+
const healthStatus = useOutletContext<HealthcheckReport>();
18+
const accessUrl = healthStatus.access_url;
19+
20+
return (
21+
<>
22+
<Helmet>
23+
<title>{pageTitle("Access URL - Health")}</title>
24+
</Helmet>
25+
26+
<Header>
27+
<HeaderTitle>
28+
<HealthyDot severity={accessUrl.severity} />
29+
Access URL
30+
</HeaderTitle>
31+
</Header>
32+
33+
<Main>
34+
{accessUrl.warnings.map((warning) => {
35+
return (
36+
<Alert key={warning.code} severity="warning">
37+
{warning.message}
38+
</Alert>
39+
);
40+
})}
41+
42+
<GridData>
43+
<GridDataLabel>Severity</GridDataLabel>
44+
<GridDataValue>{accessUrl.severity}</GridDataValue>
45+
46+
<GridDataLabel>Access URL</GridDataLabel>
47+
<GridDataValue>{accessUrl.access_url}</GridDataValue>
48+
49+
<GridDataLabel>Reachable</GridDataLabel>
50+
<GridDataValue>{accessUrl.reachable ? "Yes" : "No"}</GridDataValue>
51+
52+
<GridDataLabel>Status Code</GridDataLabel>
53+
<GridDataValue>{accessUrl.status_code}</GridDataValue>
54+
</GridData>
55+
</Main>
56+
</>
57+
);
58+
};
59+
60+
export default AccessURLPage;

0 commit comments

Comments
 (0)