From e23db577b76bf69008e4c5fb91b5538c2767cc39 Mon Sep 17 00:00:00 2001 From: Danielle Maywood Date: Wed, 30 Jul 2025 08:42:07 +0000 Subject: [PATCH 1/2] fix(site): allow template admins to see template notification settings Template admins have been unable to change their notification preferences via the website as we have historically hidden the settings for them. This PR ensures the frontend shows template events settings when a user has the `createTemplates` permission. --- .../NotificationsPage.stories.tsx | 6 +++++ .../NotificationsPage/NotificationsPage.tsx | 19 +++++++++----- site/src/testHelpers/entities.ts | 26 +++++++++++++++++++ 3 files changed, 45 insertions(+), 6 deletions(-) diff --git a/site/src/pages/UserSettingsPage/NotificationsPage/NotificationsPage.stories.tsx b/site/src/pages/UserSettingsPage/NotificationsPage/NotificationsPage.stories.tsx index e2ac02e773d2d..af057d3f29834 100644 --- a/site/src/pages/UserSettingsPage/NotificationsPage/NotificationsPage.stories.tsx +++ b/site/src/pages/UserSettingsPage/NotificationsPage/NotificationsPage.stories.tsx @@ -78,6 +78,12 @@ export const NonAdmin: Story = { }, }; +export const TemplateCreator: Story = { + parameters: { + permissions: { viewDeploymentConfig: false, createTemplates: true }, + }, +}; + // Ensure the selected notification template is enabled before attempting to // disable it. const enabledPreference = MockNotificationPreferences.find( diff --git a/site/src/pages/UserSettingsPage/NotificationsPage/NotificationsPage.tsx b/site/src/pages/UserSettingsPage/NotificationsPage/NotificationsPage.tsx index 1a89c2240c8d1..703c201aed52d 100644 --- a/site/src/pages/UserSettingsPage/NotificationsPage/NotificationsPage.tsx +++ b/site/src/pages/UserSettingsPage/NotificationsPage/NotificationsPage.tsx @@ -48,12 +48,19 @@ const NotificationsPage: FC = () => { ...systemNotificationTemplates(), select: (data: NotificationTemplate[]) => { const groups = selectTemplatesByGroup(data); - return permissions.viewDeploymentConfig - ? groups - : { - // Members only have access to the "Workspace Notifications" group - "Workspace Events": groups["Workspace Events"], - }; + + let displayedGroups: Record = { + // Members only have access to the "Workspace Notifications" group. + "Workspace Events": groups["Workspace Events"], + }; + + if (permissions.viewDeploymentConfig) { + displayedGroups = groups; + } else if (permissions.createTemplates) { + displayedGroups["Template Events"] = groups["Template Events"]; + } + + return displayedGroups; }, }, notificationDispatchMethods(), diff --git a/site/src/testHelpers/entities.ts b/site/src/testHelpers/entities.ts index 44e729e7f4d4f..f174699616bd4 100644 --- a/site/src/testHelpers/entities.ts +++ b/site/src/testHelpers/entities.ts @@ -4402,6 +4402,32 @@ export const MockNotificationTemplates: TypesGen.NotificationTemplate[] = [ 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 MockNotificationMethodsResponse: TypesGen.NotificationMethodsResponse = From d15973ad0a8751917cd38a229caa90cfcc2ced8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=82=B1=E3=82=A4=E3=83=A9?= Date: Thu, 31 Jul 2025 14:12:46 -0600 Subject: [PATCH 2/2] filter groups at the render level, rather than the data fetching level (#19115) --- .../NotificationsPage.stories.tsx | 14 +++++-- .../NotificationsPage/NotificationsPage.tsx | 38 +++++++++++-------- 2 files changed, 32 insertions(+), 20 deletions(-) diff --git a/site/src/pages/UserSettingsPage/NotificationsPage/NotificationsPage.stories.tsx b/site/src/pages/UserSettingsPage/NotificationsPage/NotificationsPage.stories.tsx index af057d3f29834..72f26f791e960 100644 --- a/site/src/pages/UserSettingsPage/NotificationsPage/NotificationsPage.stories.tsx +++ b/site/src/pages/UserSettingsPage/NotificationsPage/NotificationsPage.stories.tsx @@ -40,7 +40,7 @@ const meta = { }, ], user: MockUserOwner, - permissions: { viewDeploymentConfig: true }, + permissions: { createTemplates: true, createUser: true }, }, decorators: [withGlobalSnackbar, withAuthProvider, withDashboardProvider], } satisfies Meta; @@ -74,13 +74,19 @@ export const ToggleNotification: Story = { export const NonAdmin: Story = { parameters: { - permissions: { viewDeploymentConfig: false }, + permissions: { createTemplates: false, createUser: false }, }, }; -export const TemplateCreator: Story = { +export const TemplateAdmin: Story = { parameters: { - permissions: { viewDeploymentConfig: false, createTemplates: true }, + permissions: { createTemplates: true, createUser: false }, + }, +}; + +export const UserAdmin: Story = { + parameters: { + permissions: { createTemplates: false, createUser: true }, }, }; diff --git a/site/src/pages/UserSettingsPage/NotificationsPage/NotificationsPage.tsx b/site/src/pages/UserSettingsPage/NotificationsPage/NotificationsPage.tsx index 703c201aed52d..4e4b1e6bc61bd 100644 --- a/site/src/pages/UserSettingsPage/NotificationsPage/NotificationsPage.tsx +++ b/site/src/pages/UserSettingsPage/NotificationsPage/NotificationsPage.tsx @@ -28,6 +28,7 @@ import { methodIcons, methodLabels, } from "modules/notifications/utils"; +import type { Permissions } from "modules/permissions"; import { type FC, Fragment } from "react"; import { useEffect } from "react"; import { Helmet } from "react-helmet-async"; @@ -46,22 +47,7 @@ const NotificationsPage: FC = () => { }, { ...systemNotificationTemplates(), - select: (data: NotificationTemplate[]) => { - const groups = selectTemplatesByGroup(data); - - let displayedGroups: Record = { - // Members only have access to the "Workspace Notifications" group. - "Workspace Events": groups["Workspace Events"], - }; - - if (permissions.viewDeploymentConfig) { - displayedGroups = groups; - } else if (permissions.createTemplates) { - displayedGroups["Template Events"] = groups["Template Events"]; - } - - return displayedGroups; - }, + select: (data: NotificationTemplate[]) => selectTemplatesByGroup(data), }, notificationDispatchMethods(), ], @@ -110,6 +96,10 @@ const NotificationsPage: FC = () => { {ready ? ( {Object.entries(templatesByGroup.data).map(([group, templates]) => { + if (!canSeeNotificationGroup(group, permissions)) { + return null; + } + const allDisabled = templates.some((tpl) => { return notificationIsDisabled(disabledPreferences.data, tpl); }); @@ -218,6 +208,22 @@ const NotificationsPage: FC = () => { export default NotificationsPage; +function canSeeNotificationGroup( + group: string, + permissions: Permissions, +): boolean { + switch (group) { + case "Workspace Events": + return true; + case "Template Events": + return permissions.createTemplates; + case "User Events": + return permissions.createUser; + default: + return false; + } +} + function notificationIsDisabled( disabledPreferences: Record, tmpl: NotificationTemplate,