Skip to content

Commit e8a7b7e

Browse files
authored
feat: add notifications troubleshooting tab (coder#16650)
1 parent fcc9b05 commit e8a7b7e

File tree

4 files changed

+137
-39
lines changed

4 files changed

+137
-39
lines changed

site/src/api/api.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2297,6 +2297,10 @@ class ApiMethods {
22972297
return res.data;
22982298
};
22992299

2300+
postTestNotification = async () => {
2301+
await this.axios.post<void>("/api/v2/notifications/test");
2302+
};
2303+
23002304
requestOneTimePassword = async (
23012305
req: TypesGen.RequestOneTimePasscodeRequest,
23022306
) => {

site/src/pages/DeploymentSettingsPage/NotificationsPage/NotificationsPage.tsx

Lines changed: 57 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@ import {
44
selectTemplatesByGroup,
55
systemNotificationTemplates,
66
} from "api/queries/notifications";
7+
import { FeatureStageBadge } from "components/FeatureStageBadge/FeatureStageBadge";
78
import { Loader } from "components/Loader/Loader";
9+
import { SettingsHeader } from "components/SettingsHeader/SettingsHeader";
810
import { TabLink, Tabs, TabsList } from "components/Tabs/Tabs";
911
import { useSearchParamsKey } from "hooks/useSearchParamsKey";
1012
import { useDeploymentSettings } from "modules/management/DeploymentSettingsProvider";
@@ -14,9 +16,11 @@ import type { FC } from "react";
1416
import { Helmet } from "react-helmet-async";
1517
import { useQueries } from "react-query";
1618
import { deploymentGroupHasParent } from "utils/deployOptions";
19+
import { docs } from "utils/docs";
1720
import { pageTitle } from "utils/page";
1821
import OptionsTable from "../OptionsTable";
1922
import { NotificationEvents } from "./NotificationEvents";
23+
import { Troubleshooting } from "./Troubleshooting";
2024

2125
export const NotificationsPage: FC = () => {
2226
const { deploymentConfig } = useDeploymentSettings();
@@ -40,48 +44,62 @@ export const NotificationsPage: FC = () => {
4044
<Helmet>
4145
<title>{pageTitle("Notifications Settings")}</title>
4246
</Helmet>
43-
<Section
44-
title="Notifications"
47+
<SettingsHeader
48+
title={
49+
<>
50+
Notifications
51+
<span css={{ position: "relative", top: "-6px" }}>
52+
<FeatureStageBadge
53+
contentType={"beta"}
54+
size="lg"
55+
css={{ marginBottom: "5px", fontSize: "0.75rem" }}
56+
/>
57+
</span>
58+
</>
59+
}
4560
description="Control delivery methods for notifications on this deployment."
46-
layout="fluid"
47-
featureStage={"beta"}
48-
>
49-
<Tabs active={tabState.value}>
50-
<TabsList>
51-
<TabLink to="?tab=events" value="events">
52-
Events
53-
</TabLink>
54-
<TabLink to="?tab=settings" value="settings">
55-
Settings
56-
</TabLink>
57-
</TabsList>
58-
</Tabs>
61+
docsHref={docs("/admin/monitoring/notifications")}
62+
/>
63+
<Tabs active={tabState.value}>
64+
<TabsList>
65+
<TabLink to="?tab=events" value="events">
66+
Events
67+
</TabLink>
68+
<TabLink to="?tab=settings" value="settings">
69+
Settings
70+
</TabLink>
71+
<TabLink to="?tab=troubleshooting" value="troubleshooting">
72+
Troubleshooting
73+
</TabLink>
74+
</TabsList>
75+
</Tabs>
5976

60-
<div css={styles.content}>
61-
{ready ? (
62-
tabState.value === "events" ? (
63-
<NotificationEvents
64-
templatesByGroup={templatesByGroup.data}
65-
deploymentConfig={deploymentConfig.config}
66-
defaultMethod={castNotificationMethod(
67-
dispatchMethods.data.default,
68-
)}
69-
availableMethods={dispatchMethods.data.available.map(
70-
castNotificationMethod,
71-
)}
72-
/>
73-
) : (
74-
<OptionsTable
75-
options={deploymentConfig.options.filter((o) =>
76-
deploymentGroupHasParent(o.group, "Notifications"),
77-
)}
78-
/>
79-
)
77+
<div css={styles.content}>
78+
{ready ? (
79+
tabState.value === "events" ? (
80+
<NotificationEvents
81+
templatesByGroup={templatesByGroup.data}
82+
deploymentConfig={deploymentConfig.config}
83+
defaultMethod={castNotificationMethod(
84+
dispatchMethods.data.default,
85+
)}
86+
availableMethods={dispatchMethods.data.available.map(
87+
castNotificationMethod,
88+
)}
89+
/>
90+
) : tabState.value === "troubleshooting" ? (
91+
<Troubleshooting />
8092
) : (
81-
<Loader />
82-
)}
83-
</div>
84-
</Section>
93+
<OptionsTable
94+
options={deploymentConfig.options.filter((o) =>
95+
deploymentGroupHasParent(o.group, "Notifications"),
96+
)}
97+
/>
98+
)
99+
) : (
100+
<Loader />
101+
)}
102+
</div>
85103
</>
86104
);
87105
};
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import type { Meta, StoryObj } from "@storybook/react";
2+
import { spyOn, userEvent, within } from "@storybook/test";
3+
import { API } from "api/api";
4+
import { Troubleshooting } from "./Troubleshooting";
5+
import { baseMeta } from "./storybookUtils";
6+
7+
const meta: Meta<typeof Troubleshooting> = {
8+
title: "pages/DeploymentSettingsPage/NotificationsPage/Troubleshooting",
9+
component: Troubleshooting,
10+
...baseMeta,
11+
};
12+
13+
export default meta;
14+
15+
type Story = StoryObj<typeof Troubleshooting>;
16+
17+
export const TestNotification: Story = {
18+
play: async ({ canvasElement }) => {
19+
spyOn(API, "postTestNotification").mockResolvedValue();
20+
const user = userEvent.setup();
21+
const canvas = within(canvasElement);
22+
23+
const sendButton = canvas.getByRole("button", {
24+
name: "Send notification",
25+
});
26+
await user.click(sendButton);
27+
await within(document.body).findByText("Test notification sent");
28+
},
29+
};
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import { useTheme } from "@emotion/react";
2+
import LoadingButton from "@mui/lab/LoadingButton";
3+
import { API } from "api/api";
4+
import { displayError, displaySuccess } from "components/GlobalSnackbar/utils";
5+
import type { FC } from "react";
6+
import { useMutation } from "react-query";
7+
8+
export const Troubleshooting: FC = () => {
9+
const { mutate: sendTestNotificationApi, isLoading } = useMutation(
10+
API.postTestNotification,
11+
{
12+
onSuccess: () => displaySuccess("Test notification sent"),
13+
onError: () => displayError("Failed to send test notification"),
14+
},
15+
);
16+
17+
const theme = useTheme();
18+
return (
19+
<>
20+
<div
21+
css={{
22+
fontSize: 14,
23+
color: theme.palette.text.secondary,
24+
lineHeight: "160%",
25+
marginBottom: 16,
26+
}}
27+
>
28+
Send a test notification to troubleshoot your notification settings.
29+
</div>
30+
<div>
31+
<span>
32+
<LoadingButton
33+
variant="outlined"
34+
loading={isLoading}
35+
size="small"
36+
css={{ minWidth: "auto", aspectRatio: "1" }}
37+
onClick={() => {
38+
sendTestNotificationApi();
39+
}}
40+
>
41+
Send notification
42+
</LoadingButton>
43+
</span>
44+
</div>
45+
</>
46+
);
47+
};

0 commit comments

Comments
 (0)