mirror of
https://github.com/coder/coder.git
synced 2026-09-21 20:51:01 +08:00
feat(site): add custom notification settings (#19938)
## Description Add Custom Notification settings to `/deployment/notifications` page and `/settings/notifications` user's page. <img width="2936" height="3008" alt="Screenshot 2025-09-24 at 12 53 52" src="https://github.com/user-attachments/assets/c11ccaba-7bdd-4b0d-98b3-faa48a9796ba" /> <img width="2920" height="2976" alt="Screenshot 2025-09-24 at 12 54 06" src="https://github.com/user-attachments/assets/21aa3057-a14e-4ba6-8138-98a4dc3952dc" /> Follow-up from: https://github.com/coder/coder/pull/19751
This commit is contained in:
@@ -2519,6 +2519,13 @@ class ApiMethods {
|
||||
return res.data;
|
||||
};
|
||||
|
||||
getCustomNotificationTemplates = async () => {
|
||||
const res = await this.axios.get<TypesGen.NotificationTemplate[]>(
|
||||
"/api/v2/notifications/templates/custom",
|
||||
);
|
||||
return res.data;
|
||||
};
|
||||
|
||||
getNotificationDispatchMethods = async () => {
|
||||
const res = await this.axios.get<TypesGen.NotificationMethodsResponse>(
|
||||
"/api/v2/notifications/dispatch-methods",
|
||||
|
||||
@@ -62,6 +62,19 @@ export const systemNotificationTemplates = () => {
|
||||
};
|
||||
};
|
||||
|
||||
export const customNotificationTemplatesKey = [
|
||||
"notifications",
|
||||
"templates",
|
||||
"custom",
|
||||
];
|
||||
|
||||
export const customNotificationTemplates = () => {
|
||||
return {
|
||||
queryKey: customNotificationTemplatesKey,
|
||||
queryFn: () => API.getCustomNotificationTemplates(),
|
||||
};
|
||||
};
|
||||
|
||||
export function selectTemplatesByGroup(
|
||||
data: NotificationTemplate[],
|
||||
): Record<string, NotificationTemplate[]> {
|
||||
@@ -106,23 +119,24 @@ export const updateNotificationTemplateMethod = (
|
||||
mutationFn: (req: UpdateNotificationTemplateMethod) =>
|
||||
API.updateNotificationTemplateMethod(templateId, req),
|
||||
onMutate: (data) => {
|
||||
const prevData = queryClient.getQueryData<NotificationTemplate[]>(
|
||||
const keys = [
|
||||
systemNotificationTemplatesKey,
|
||||
);
|
||||
if (!prevData) {
|
||||
return;
|
||||
customNotificationTemplatesKey,
|
||||
];
|
||||
|
||||
for (const key of keys) {
|
||||
const prev = queryClient.getQueryData<NotificationTemplate[]>(key);
|
||||
if (!prev) {
|
||||
continue;
|
||||
}
|
||||
|
||||
queryClient.setQueryData(
|
||||
key,
|
||||
prev.map((tpl) =>
|
||||
tpl.id === templateId ? { ...tpl, method: data.method } : tpl,
|
||||
),
|
||||
);
|
||||
}
|
||||
queryClient.setQueryData(
|
||||
systemNotificationTemplatesKey,
|
||||
prevData.map((tpl) =>
|
||||
tpl.id === templateId
|
||||
? {
|
||||
...tpl,
|
||||
method: data.method,
|
||||
}
|
||||
: tpl,
|
||||
),
|
||||
);
|
||||
},
|
||||
} satisfies UseMutationOptions<
|
||||
void,
|
||||
|
||||
+4
-4
@@ -1,4 +1,4 @@
|
||||
import { MockNotificationTemplates } from "testHelpers/entities";
|
||||
import { MockSystemNotificationTemplates } from "testHelpers/entities";
|
||||
import type { Meta, StoryObj } from "@storybook/react-vite";
|
||||
import { API } from "api/api";
|
||||
import { selectTemplatesByGroup } from "api/queries/notifications";
|
||||
@@ -13,7 +13,7 @@ const meta: Meta<typeof NotificationEvents> = {
|
||||
args: {
|
||||
defaultMethod: "smtp",
|
||||
availableMethods: ["smtp", "webhook"],
|
||||
templatesByGroup: selectTemplatesByGroup(MockNotificationTemplates),
|
||||
templatesByGroup: selectTemplatesByGroup(MockSystemNotificationTemplates),
|
||||
deploymentConfig: baseMeta.parameters.deploymentValues,
|
||||
},
|
||||
...baseMeta,
|
||||
@@ -60,7 +60,7 @@ export const Toggle: Story = {
|
||||
spyOn(API, "updateNotificationTemplateMethod").mockResolvedValue();
|
||||
const user = userEvent.setup();
|
||||
const canvas = within(canvasElement);
|
||||
const tmpl = MockNotificationTemplates[4];
|
||||
const tmpl = MockSystemNotificationTemplates[4];
|
||||
const option = await canvas.findByText(tmpl.name);
|
||||
const li = option.closest("li");
|
||||
if (!li) {
|
||||
@@ -79,7 +79,7 @@ export const ToggleError: Story = {
|
||||
spyOn(API, "updateNotificationTemplateMethod").mockRejectedValue({});
|
||||
const user = userEvent.setup();
|
||||
const canvas = within(canvasElement);
|
||||
const tmpl = MockNotificationTemplates[4];
|
||||
const tmpl = MockSystemNotificationTemplates[4];
|
||||
const option = await canvas.findByText(tmpl.name);
|
||||
const li = option.closest("li");
|
||||
if (!li) {
|
||||
|
||||
+29
-3
@@ -1,9 +1,11 @@
|
||||
import {
|
||||
MockCustomNotificationTemplates,
|
||||
MockNotificationMethodsResponse,
|
||||
MockNotificationTemplates,
|
||||
MockSystemNotificationTemplates,
|
||||
} from "testHelpers/entities";
|
||||
import type { Meta, StoryObj } from "@storybook/react-vite";
|
||||
import {
|
||||
customNotificationTemplatesKey,
|
||||
notificationDispatchMethodsKey,
|
||||
systemNotificationTemplatesKey,
|
||||
} from "api/queries/notifications";
|
||||
@@ -28,6 +30,10 @@ export const LoadingTemplates: Story = {
|
||||
key: systemNotificationTemplatesKey,
|
||||
data: undefined,
|
||||
},
|
||||
{
|
||||
key: customNotificationTemplatesKey,
|
||||
data: undefined,
|
||||
},
|
||||
{
|
||||
key: notificationDispatchMethodsKey,
|
||||
data: MockNotificationMethodsResponse,
|
||||
@@ -39,7 +45,14 @@ export const LoadingTemplates: Story = {
|
||||
export const LoadingDispatchMethods: Story = {
|
||||
parameters: {
|
||||
queries: [
|
||||
{ key: systemNotificationTemplatesKey, data: MockNotificationTemplates },
|
||||
{
|
||||
key: systemNotificationTemplatesKey,
|
||||
data: MockSystemNotificationTemplates,
|
||||
},
|
||||
{
|
||||
key: customNotificationTemplatesKey,
|
||||
data: MockCustomNotificationTemplates,
|
||||
},
|
||||
{
|
||||
key: notificationDispatchMethodsKey,
|
||||
data: undefined,
|
||||
@@ -48,7 +61,20 @@ export const LoadingDispatchMethods: Story = {
|
||||
},
|
||||
};
|
||||
|
||||
export const Events: Story = {};
|
||||
export const Events: Story = {
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
|
||||
// System notification templates
|
||||
await canvas.findByText("Template Events");
|
||||
await canvas.findByText("User Events");
|
||||
await canvas.findByText("Workspace Events");
|
||||
|
||||
// Custom notification template
|
||||
await canvas.findByText("Custom Events");
|
||||
await canvas.findByText("Custom Notification");
|
||||
},
|
||||
};
|
||||
|
||||
export const Settings: Story = {
|
||||
play: async ({ canvasElement }) => {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import type { Interpolation, Theme } from "@emotion/react";
|
||||
import {
|
||||
customNotificationTemplates,
|
||||
notificationDispatchMethods,
|
||||
selectTemplatesByGroup,
|
||||
systemNotificationTemplates,
|
||||
@@ -27,21 +28,35 @@ import { Troubleshooting } from "./Troubleshooting";
|
||||
|
||||
const NotificationsPage: FC = () => {
|
||||
const { deploymentConfig } = useDeploymentConfig();
|
||||
const [templatesByGroup, dispatchMethods] = useQueries({
|
||||
queries: [
|
||||
{
|
||||
...systemNotificationTemplates(),
|
||||
select: selectTemplatesByGroup,
|
||||
},
|
||||
notificationDispatchMethods(),
|
||||
],
|
||||
});
|
||||
const [systemTemplatesByGroup, customTemplatesByGroup, dispatchMethods] =
|
||||
useQueries({
|
||||
queries: [
|
||||
{
|
||||
...systemNotificationTemplates(),
|
||||
select: selectTemplatesByGroup,
|
||||
},
|
||||
{
|
||||
...customNotificationTemplates(),
|
||||
select: selectTemplatesByGroup,
|
||||
},
|
||||
notificationDispatchMethods(),
|
||||
],
|
||||
});
|
||||
const tabState = useSearchParamsKey({
|
||||
key: "tab",
|
||||
defaultValue: "events",
|
||||
});
|
||||
|
||||
const ready = !!(templatesByGroup.data && dispatchMethods.data);
|
||||
const ready = !!(
|
||||
systemTemplatesByGroup.data &&
|
||||
customTemplatesByGroup.data &&
|
||||
dispatchMethods.data
|
||||
);
|
||||
// Combine system and custom notification templates
|
||||
const allTemplatesByGroup = {
|
||||
...systemTemplatesByGroup.data,
|
||||
...customTemplatesByGroup.data,
|
||||
};
|
||||
return (
|
||||
<>
|
||||
<Helmet>
|
||||
@@ -79,7 +94,7 @@ const NotificationsPage: FC = () => {
|
||||
{ready ? (
|
||||
tabState.value === "events" ? (
|
||||
<NotificationEvents
|
||||
templatesByGroup={templatesByGroup.data}
|
||||
templatesByGroup={allTemplatesByGroup}
|
||||
deploymentConfig={deploymentConfig.config}
|
||||
defaultMethod={castNotificationMethod(
|
||||
dispatchMethods.data.default,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import {
|
||||
MockCustomNotificationTemplates,
|
||||
MockNotificationMethodsResponse,
|
||||
MockNotificationTemplates,
|
||||
MockSystemNotificationTemplates,
|
||||
MockUserOwner,
|
||||
} from "testHelpers/entities";
|
||||
import {
|
||||
@@ -11,6 +12,7 @@ import {
|
||||
} from "testHelpers/storybook";
|
||||
import type { Meta } from "@storybook/react-vite";
|
||||
import {
|
||||
customNotificationTemplatesKey,
|
||||
notificationDispatchMethodsKey,
|
||||
systemNotificationTemplatesKey,
|
||||
} from "api/queries/notifications";
|
||||
@@ -187,7 +189,14 @@ export const baseMeta = {
|
||||
parameters: {
|
||||
experiments: ["notifications"],
|
||||
queries: [
|
||||
{ key: systemNotificationTemplatesKey, data: MockNotificationTemplates },
|
||||
{
|
||||
key: systemNotificationTemplatesKey,
|
||||
data: MockSystemNotificationTemplates,
|
||||
},
|
||||
{
|
||||
key: customNotificationTemplatesKey,
|
||||
data: MockCustomNotificationTemplates,
|
||||
},
|
||||
{
|
||||
key: notificationDispatchMethodsKey,
|
||||
data: MockNotificationMethodsResponse,
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import {
|
||||
MockCustomNotificationTemplates,
|
||||
MockNotificationMethodsResponse,
|
||||
MockNotificationPreferences,
|
||||
MockNotificationTemplates,
|
||||
MockSystemNotificationTemplates,
|
||||
MockUserOwner,
|
||||
} from "testHelpers/entities";
|
||||
import {
|
||||
@@ -12,6 +13,7 @@ import {
|
||||
import type { Meta, StoryObj } from "@storybook/react-vite";
|
||||
import { API } from "api/api";
|
||||
import {
|
||||
customNotificationTemplatesKey,
|
||||
notificationDispatchMethodsKey,
|
||||
systemNotificationTemplatesKey,
|
||||
userNotificationPreferencesKey,
|
||||
@@ -32,7 +34,11 @@ const meta = {
|
||||
},
|
||||
{
|
||||
key: systemNotificationTemplatesKey,
|
||||
data: MockNotificationTemplates,
|
||||
data: MockSystemNotificationTemplates,
|
||||
},
|
||||
{
|
||||
key: customNotificationTemplatesKey,
|
||||
data: MockCustomNotificationTemplates,
|
||||
},
|
||||
{
|
||||
key: notificationDispatchMethodsKey,
|
||||
@@ -100,7 +106,7 @@ if (!enabledPreference) {
|
||||
"No enabled notification preference available to test the disabling action.",
|
||||
);
|
||||
}
|
||||
const templateToDisable = MockNotificationTemplates.find(
|
||||
const templateToDisable = MockSystemNotificationTemplates.find(
|
||||
(tpl) => tpl.id === enabledPreference.id,
|
||||
);
|
||||
if (!templateToDisable) {
|
||||
|
||||
@@ -8,6 +8,7 @@ import ListItemText, { listItemTextClasses } from "@mui/material/ListItemText";
|
||||
import Switch from "@mui/material/Switch";
|
||||
import Tooltip from "@mui/material/Tooltip";
|
||||
import {
|
||||
customNotificationTemplates,
|
||||
disableNotification,
|
||||
notificationDispatchMethods,
|
||||
selectTemplatesByGroup,
|
||||
@@ -38,7 +39,12 @@ import { Section } from "../Section";
|
||||
|
||||
const NotificationsPage: FC = () => {
|
||||
const { user, permissions } = useAuthenticated();
|
||||
const [disabledPreferences, templatesByGroup, dispatchMethods] = useQueries({
|
||||
const [
|
||||
disabledPreferences,
|
||||
systemTemplatesByGroup,
|
||||
customTemplatesByGroup,
|
||||
dispatchMethods,
|
||||
] = useQueries({
|
||||
queries: [
|
||||
{
|
||||
...userNotificationPreferences(user.id),
|
||||
@@ -48,6 +54,10 @@ const NotificationsPage: FC = () => {
|
||||
...systemNotificationTemplates(),
|
||||
select: (data: NotificationTemplate[]) => selectTemplatesByGroup(data),
|
||||
},
|
||||
{
|
||||
...customNotificationTemplates(),
|
||||
select: (data: NotificationTemplate[]) => selectTemplatesByGroup(data),
|
||||
},
|
||||
notificationDispatchMethods(),
|
||||
],
|
||||
});
|
||||
@@ -80,7 +90,15 @@ const NotificationsPage: FC = () => {
|
||||
}, [searchParams.delete, disabledId, disableMutation]);
|
||||
|
||||
const ready =
|
||||
disabledPreferences.data && templatesByGroup.data && dispatchMethods.data;
|
||||
disabledPreferences.data &&
|
||||
systemTemplatesByGroup.data &&
|
||||
customTemplatesByGroup.data &&
|
||||
dispatchMethods.data;
|
||||
// Combine system and custom notification templates
|
||||
const allTemplatesByGroup = {
|
||||
...systemTemplatesByGroup.data,
|
||||
...customTemplatesByGroup.data,
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -94,7 +112,7 @@ const NotificationsPage: FC = () => {
|
||||
>
|
||||
{ready ? (
|
||||
<Stack spacing={4}>
|
||||
{Object.entries(templatesByGroup.data).map(([group, templates]) => {
|
||||
{Object.entries(allTemplatesByGroup).map(([group, templates]) => {
|
||||
if (!canSeeNotificationGroup(group, permissions)) {
|
||||
return null;
|
||||
}
|
||||
@@ -218,6 +236,8 @@ function canSeeNotificationGroup(
|
||||
return permissions.createTemplates;
|
||||
case "User Events":
|
||||
return permissions.createUser;
|
||||
case "Custom Events":
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
+135
-119
@@ -4537,125 +4537,141 @@ export const MockNotificationPreferences: TypesGen.NotificationPreference[] = [
|
||||
},
|
||||
];
|
||||
|
||||
export const MockNotificationTemplates: TypesGen.NotificationTemplate[] = [
|
||||
{
|
||||
id: "381df2a9-c0c0-4749-420f-80a9280c66f9",
|
||||
name: "Workspace Autobuild Failed",
|
||||
title_template: 'Workspace "{{.Labels.name}}" autobuild failed',
|
||||
body_template:
|
||||
'Hi {{.UserName}}\nAutomatic build of your workspace **{{.Labels.name}}** failed.\nThe specified reason was "**{{.Labels.reason}}**".',
|
||||
actions:
|
||||
'[{"url": "{{ base_url }}/@{{.UserUsername}}/{{.Labels.name}}", "label": "View workspace"}]',
|
||||
group: "Workspace Events",
|
||||
method: "webhook",
|
||||
kind: "system",
|
||||
enabled_by_default: true,
|
||||
},
|
||||
{
|
||||
id: "f517da0b-cdc9-410f-ab89-a86107c420ed",
|
||||
name: "Workspace Deleted",
|
||||
title_template: 'Workspace "{{.Labels.name}}" deleted',
|
||||
body_template:
|
||||
'Hi {{.UserName}}\n\nYour workspace **{{.Labels.name}}** was deleted.\nThe specified reason was "**{{.Labels.reason}}{{ if .Labels.initiator }} ({{ .Labels.initiator }}){{end}}**".',
|
||||
actions:
|
||||
'[{"url": "{{ base_url }}/workspaces", "label": "View workspaces"}, {"url": "{{ base_url }}/templates", "label": "View templates"}]',
|
||||
group: "Workspace Events",
|
||||
method: "smtp",
|
||||
kind: "system",
|
||||
enabled_by_default: true,
|
||||
},
|
||||
{
|
||||
id: "f44d9314-ad03-4bc8-95d0-5cad491da6b6",
|
||||
name: "User account deleted",
|
||||
title_template: 'User account "{{.Labels.deleted_account_name}}" deleted',
|
||||
body_template:
|
||||
"Hi {{.UserName}},\n\nUser account **{{.Labels.deleted_account_name}}** has been deleted.",
|
||||
actions:
|
||||
'[{"url": "{{ base_url }}/deployment/users?filter=status%3Aactive", "label": "View accounts"}]',
|
||||
group: "User Events",
|
||||
method: "",
|
||||
kind: "system",
|
||||
enabled_by_default: true,
|
||||
},
|
||||
{
|
||||
id: "4e19c0ac-94e1-4532-9515-d1801aa283b2",
|
||||
name: "User account created",
|
||||
title_template: 'User account "{{.Labels.created_account_name}}" created',
|
||||
body_template:
|
||||
"Hi {{.UserName}},\n\nNew user account **{{.Labels.created_account_name}}** has been created.",
|
||||
actions:
|
||||
'[{"url": "{{ base_url }}/deployment/users?filter=status%3Aactive", "label": "View accounts"}]',
|
||||
group: "User Events",
|
||||
method: "",
|
||||
kind: "system",
|
||||
enabled_by_default: true,
|
||||
},
|
||||
{
|
||||
id: "0ea69165-ec14-4314-91f1-69566ac3c5a0",
|
||||
name: "Workspace Marked as Dormant",
|
||||
title_template: 'Workspace "{{.Labels.name}}" marked as dormant',
|
||||
body_template:
|
||||
"Hi {{.UserName}}\n\nYour workspace **{{.Labels.name}}** has been marked as [**dormant**](https://coder.com/docs/templates/schedule#dormancy-threshold-enterprise) because of {{.Labels.reason}}.\nDormant workspaces are [automatically deleted](https://coder.com/docs/templates/schedule#dormancy-auto-deletion-enterprise) after {{.Labels.timeTilDormant}} of inactivity.\nTo prevent deletion, use your workspace with the link below.",
|
||||
actions:
|
||||
'[{"url": "{{ base_url }}/@{{.UserUsername}}/{{.Labels.name}}", "label": "View workspace"}]',
|
||||
group: "Workspace Events",
|
||||
method: "smtp",
|
||||
kind: "system",
|
||||
enabled_by_default: true,
|
||||
},
|
||||
{
|
||||
id: "c34a0c09-0704-4cac-bd1c-0c0146811c2b",
|
||||
name: "Workspace updated automatically",
|
||||
title_template: 'Workspace "{{.Labels.name}}" updated automatically',
|
||||
body_template:
|
||||
"Hi {{.UserName}}\nYour workspace **{{.Labels.name}}** has been updated automatically to the latest template version ({{.Labels.template_version_name}}).",
|
||||
actions:
|
||||
'[{"url": "{{ base_url }}/@{{.UserUsername}}/{{.Labels.name}}", "label": "View workspace"}]',
|
||||
group: "Workspace Events",
|
||||
method: "smtp",
|
||||
kind: "system",
|
||||
enabled_by_default: true,
|
||||
},
|
||||
{
|
||||
id: "51ce2fdf-c9ca-4be1-8d70-628674f9bc42",
|
||||
name: "Workspace Marked for Deletion",
|
||||
title_template: 'Workspace "{{.Labels.name}}" marked for deletion',
|
||||
body_template:
|
||||
"Hi {{.UserName}}\n\nYour workspace **{{.Labels.name}}** has been marked for **deletion** after {{.Labels.timeTilDormant}} of [dormancy](https://coder.com/docs/templates/schedule#dormancy-auto-deletion-enterprise) because of {{.Labels.reason}}.\nTo prevent deletion, use your workspace with the link below.",
|
||||
actions:
|
||||
'[{"url": "{{ base_url }}/@{{.UserUsername}}/{{.Labels.name}}", "label": "View workspace"}]',
|
||||
group: "Workspace Events",
|
||||
method: "webhook",
|
||||
kind: "system",
|
||||
enabled_by_default: true,
|
||||
},
|
||||
{
|
||||
id: "template-event-1",
|
||||
name: "Template Version Created",
|
||||
title_template: 'Template version "{{.Labels.version_name}}" created',
|
||||
body_template:
|
||||
'Hi {{.UserName}}\nA new version of template "{{.Labels.template_name}}" has been created.',
|
||||
actions:
|
||||
'[{"url": "{{ base_url }}/templates/{{.Labels.template_name}}", "label": "View template"}]',
|
||||
group: "Template Events",
|
||||
method: "smtp",
|
||||
kind: "system",
|
||||
enabled_by_default: true,
|
||||
},
|
||||
{
|
||||
id: "template-event-2",
|
||||
name: "Template Updated",
|
||||
title_template: 'Template "{{.Labels.template_name}}" updated',
|
||||
body_template:
|
||||
'Hi {{.UserName}}\nTemplate "{{.Labels.template_name}}" has been updated.',
|
||||
actions:
|
||||
'[{"url": "{{ base_url }}/templates/{{.Labels.template_name}}", "label": "View template"}]',
|
||||
group: "Template Events",
|
||||
method: "webhook",
|
||||
kind: "system",
|
||||
enabled_by_default: true,
|
||||
},
|
||||
];
|
||||
export const MockSystemNotificationTemplates: TypesGen.NotificationTemplate[] =
|
||||
[
|
||||
{
|
||||
id: "381df2a9-c0c0-4749-420f-80a9280c66f9",
|
||||
name: "Workspace Autobuild Failed",
|
||||
title_template: 'Workspace "{{.Labels.name}}" autobuild failed',
|
||||
body_template:
|
||||
'Hi {{.UserName}}\nAutomatic build of your workspace **{{.Labels.name}}** failed.\nThe specified reason was "**{{.Labels.reason}}**".',
|
||||
actions:
|
||||
'[{"url": "{{ base_url }}/@{{.UserUsername}}/{{.Labels.name}}", "label": "View workspace"}]',
|
||||
group: "Workspace Events",
|
||||
method: "webhook",
|
||||
kind: "system",
|
||||
enabled_by_default: true,
|
||||
},
|
||||
{
|
||||
id: "f517da0b-cdc9-410f-ab89-a86107c420ed",
|
||||
name: "Workspace Deleted",
|
||||
title_template: 'Workspace "{{.Labels.name}}" deleted',
|
||||
body_template:
|
||||
'Hi {{.UserName}}\n\nYour workspace **{{.Labels.name}}** was deleted.\nThe specified reason was "**{{.Labels.reason}}{{ if .Labels.initiator }} ({{ .Labels.initiator }}){{end}}**".',
|
||||
actions:
|
||||
'[{"url": "{{ base_url }}/workspaces", "label": "View workspaces"}, {"url": "{{ base_url }}/templates", "label": "View templates"}]',
|
||||
group: "Workspace Events",
|
||||
method: "smtp",
|
||||
kind: "system",
|
||||
enabled_by_default: true,
|
||||
},
|
||||
{
|
||||
id: "f44d9314-ad03-4bc8-95d0-5cad491da6b6",
|
||||
name: "User account deleted",
|
||||
title_template: 'User account "{{.Labels.deleted_account_name}}" deleted',
|
||||
body_template:
|
||||
"Hi {{.UserName}},\n\nUser account **{{.Labels.deleted_account_name}}** has been deleted.",
|
||||
actions:
|
||||
'[{"url": "{{ base_url }}/deployment/users?filter=status%3Aactive", "label": "View accounts"}]',
|
||||
group: "User Events",
|
||||
method: "",
|
||||
kind: "system",
|
||||
enabled_by_default: true,
|
||||
},
|
||||
{
|
||||
id: "4e19c0ac-94e1-4532-9515-d1801aa283b2",
|
||||
name: "User account created",
|
||||
title_template: 'User account "{{.Labels.created_account_name}}" created',
|
||||
body_template:
|
||||
"Hi {{.UserName}},\n\nNew user account **{{.Labels.created_account_name}}** has been created.",
|
||||
actions:
|
||||
'[{"url": "{{ base_url }}/deployment/users?filter=status%3Aactive", "label": "View accounts"}]',
|
||||
group: "User Events",
|
||||
method: "",
|
||||
kind: "system",
|
||||
enabled_by_default: true,
|
||||
},
|
||||
{
|
||||
id: "0ea69165-ec14-4314-91f1-69566ac3c5a0",
|
||||
name: "Workspace Marked as Dormant",
|
||||
title_template: 'Workspace "{{.Labels.name}}" marked as dormant',
|
||||
body_template:
|
||||
"Hi {{.UserName}}\n\nYour workspace **{{.Labels.name}}** has been marked as [**dormant**](https://coder.com/docs/templates/schedule#dormancy-threshold-enterprise) because of {{.Labels.reason}}.\nDormant workspaces are [automatically deleted](https://coder.com/docs/templates/schedule#dormancy-auto-deletion-enterprise) after {{.Labels.timeTilDormant}} of inactivity.\nTo prevent deletion, use your workspace with the link below.",
|
||||
actions:
|
||||
'[{"url": "{{ base_url }}/@{{.UserUsername}}/{{.Labels.name}}", "label": "View workspace"}]',
|
||||
group: "Workspace Events",
|
||||
method: "smtp",
|
||||
kind: "system",
|
||||
enabled_by_default: true,
|
||||
},
|
||||
{
|
||||
id: "c34a0c09-0704-4cac-bd1c-0c0146811c2b",
|
||||
name: "Workspace updated automatically",
|
||||
title_template: 'Workspace "{{.Labels.name}}" updated automatically',
|
||||
body_template:
|
||||
"Hi {{.UserName}}\nYour workspace **{{.Labels.name}}** has been updated automatically to the latest template version ({{.Labels.template_version_name}}).",
|
||||
actions:
|
||||
'[{"url": "{{ base_url }}/@{{.UserUsername}}/{{.Labels.name}}", "label": "View workspace"}]',
|
||||
group: "Workspace Events",
|
||||
method: "smtp",
|
||||
kind: "system",
|
||||
enabled_by_default: true,
|
||||
},
|
||||
{
|
||||
id: "51ce2fdf-c9ca-4be1-8d70-628674f9bc42",
|
||||
name: "Workspace Marked for Deletion",
|
||||
title_template: 'Workspace "{{.Labels.name}}" marked for deletion',
|
||||
body_template:
|
||||
"Hi {{.UserName}}\n\nYour workspace **{{.Labels.name}}** has been marked for **deletion** after {{.Labels.timeTilDormant}} of [dormancy](https://coder.com/docs/templates/schedule#dormancy-auto-deletion-enterprise) because of {{.Labels.reason}}.\nTo prevent deletion, use your workspace with the link below.",
|
||||
actions:
|
||||
'[{"url": "{{ base_url }}/@{{.UserUsername}}/{{.Labels.name}}", "label": "View workspace"}]',
|
||||
group: "Workspace Events",
|
||||
method: "webhook",
|
||||
kind: "system",
|
||||
enabled_by_default: true,
|
||||
},
|
||||
{
|
||||
id: "template-event-1",
|
||||
name: "Template Version Created",
|
||||
title_template: 'Template version "{{.Labels.version_name}}" created',
|
||||
body_template:
|
||||
'Hi {{.UserName}}\nA new version of template "{{.Labels.template_name}}" has been created.',
|
||||
actions:
|
||||
'[{"url": "{{ base_url }}/templates/{{.Labels.template_name}}", "label": "View template"}]',
|
||||
group: "Template Events",
|
||||
method: "smtp",
|
||||
kind: "system",
|
||||
enabled_by_default: true,
|
||||
},
|
||||
{
|
||||
id: "template-event-2",
|
||||
name: "Template Updated",
|
||||
title_template: 'Template "{{.Labels.template_name}}" updated',
|
||||
body_template:
|
||||
'Hi {{.UserName}}\nTemplate "{{.Labels.template_name}}" has been updated.',
|
||||
actions:
|
||||
'[{"url": "{{ base_url }}/templates/{{.Labels.template_name}}", "label": "View template"}]',
|
||||
group: "Template Events",
|
||||
method: "webhook",
|
||||
kind: "system",
|
||||
enabled_by_default: true,
|
||||
},
|
||||
];
|
||||
|
||||
export const MockCustomNotificationTemplates: TypesGen.NotificationTemplate[] =
|
||||
[
|
||||
{
|
||||
id: "39b1e189-c857-4b0c-877a-511144c18516",
|
||||
name: "Custom Notification",
|
||||
title_template: "{{.Labels.custom_title}}",
|
||||
body_template: "{{.Labels.custom_message}}",
|
||||
actions: "[]",
|
||||
group: "Custom Events",
|
||||
method: "",
|
||||
kind: "custom",
|
||||
enabled_by_default: true,
|
||||
},
|
||||
];
|
||||
|
||||
export const MockNotificationMethodsResponse: TypesGen.NotificationMethodsResponse =
|
||||
{ available: ["smtp", "webhook"], default: "smtp" };
|
||||
|
||||
Reference in New Issue
Block a user