1
1
import type { Interpolation , Theme } from "@emotion/react" ;
2
- import AlertTitle from "@mui/material/AlertTitle" ;
3
- import Button from "@mui/material/Button" ;
4
2
import Card from "@mui/material/Card" ;
5
3
import Divider from "@mui/material/Divider" ;
6
4
import List from "@mui/material/List" ;
@@ -11,25 +9,29 @@ import ToggleButtonGroup from "@mui/material/ToggleButtonGroup";
11
9
import Tooltip from "@mui/material/Tooltip" ;
12
10
import { Fragment , type FC } from "react" ;
13
11
import { useMutation , useQueries , useQueryClient } from "react-query" ;
12
+ import { useSearchParams } from "react-router-dom" ;
14
13
import {
15
14
notificationDispatchMethods ,
16
15
selectTemplatesByGroup ,
17
16
systemNotificationTemplates ,
18
17
updateNotificationTemplateMethod ,
19
18
} from "api/queries/notifications" ;
20
- import { Alert , AlertDetail } from "components/Alert/Alert" ;
19
+ import type { NotificationsConfig } from "api/typesGenerated" ;
20
+ import { Alert } from "components/Alert/Alert" ;
21
21
import { displaySuccess } from "components/GlobalSnackbar/utils" ;
22
22
import { Loader } from "components/Loader/Loader" ;
23
23
import { Stack } from "components/Stack/Stack" ;
24
- import { useClipboard } from "hooks " ;
24
+ import { TabLink , Tabs , TabsList } from "components/Tabs/Tabs " ;
25
25
import {
26
26
castNotificationMethod ,
27
27
methodIcons ,
28
28
methodLabels ,
29
29
type NotificationMethod ,
30
30
} from "modules/notifications/utils" ;
31
31
import { Section } from "pages/UserSettingsPage/Section" ;
32
+ import { deploymentGroupHasParent } from "utils/deployOptions" ;
32
33
import { useDeploySettings } from "../DeploySettingsLayout" ;
34
+ import OptionsTable from "../OptionsTable" ;
33
35
34
36
type MethodToggleGroupProps = {
35
37
templateId :string ;
@@ -89,6 +91,7 @@ const MethodToggleGroup: FC<MethodToggleGroupProps> = ({
89
91
} ;
90
92
91
93
export const NotificationsPage :FC = ( ) => {
94
+ const [ searchParams ] = useSearchParams ( ) ;
92
95
const { deploymentValues} = useDeploySettings ( ) ;
93
96
const [ templatesByGroup , dispatchMethods ] = useQueries ( {
94
97
queries :[
@@ -99,104 +102,120 @@ export const NotificationsPage: FC = () => {
99
102
notificationDispatchMethods ( ) ,
100
103
] ,
101
104
} ) ;
102
- const ready = templatesByGroup . data && dispatchMethods . data ;
103
-
104
- const isUsingWebhook = dispatchMethods . data ?. available . includes ( "webhook" ) ;
105
- const webhookEndpoint =
106
- deploymentValues ?. config . notifications ?. webhook . endpoint ;
105
+ const ready =
106
+ templatesByGroup . data && dispatchMethods . data && deploymentValues ;
107
+ const tab = searchParams . get ( "tab" ) || "events" ;
107
108
108
109
return (
109
110
< Section
110
111
title = "Notifications"
111
112
description = "Control delivery methods for notifications. Settings applied to this deployment."
112
113
layout = "fluid"
113
114
>
114
- { ready ?(
115
- < Stack spacing = { 3 } >
116
- { isUsingWebhook &&
117
- ( webhookEndpoint ?(
118
- < WebhookInfo endpoint = { webhookEndpoint } />
119
- ) :(
120
- < Alert severity = "warning" >
121
- Webhook method is enabled, but the endpoint is not configured.
122
- </ Alert >
123
- ) ) }
124
- { Object . entries ( templatesByGroup . data ) . map ( ( [ group , templates ] ) => (
125
- < Card
126
- key = { group }
127
- variant = "outlined"
128
- css = { { background :"transparent" , width :"100%" } }
129
- >
130
- < List >
131
- < ListItem css = { styles . listHeader } >
132
- < ListItemText css = { styles . listItemText } primary = { group } />
133
- </ ListItem >
115
+ < Tabs active = { tab } >
116
+ < TabsList >
117
+ < TabLink to = "?tab=events" value = "events" >
118
+ Events
119
+ </ TabLink >
120
+ < TabLink to = "?tab=settings" value = "settings" >
121
+ Settings
122
+ </ TabLink >
123
+ </ TabsList >
124
+ </ Tabs >
134
125
135
- { templates . map ( ( tpl ) => {
136
- const value = castNotificationMethod (
137
- tpl . method || dispatchMethods . data . default ,
138
- ) ;
139
- const options = dispatchMethods . data . available . map (
140
- castNotificationMethod ,
141
- ) ;
142
-
143
- return (
144
- < Fragment key = { tpl . id } >
145
- < ListItem >
146
- < ListItemText
147
- css = { styles . listItemText }
148
- primary = { tpl . name }
149
- />
150
- < MethodToggleGroup
151
- templateId = { tpl . id }
152
- options = { options }
153
- value = { value }
154
- />
155
- </ ListItem >
156
- < Divider css = { styles . divider } />
157
- </ Fragment >
158
- ) ;
159
- } ) }
160
- </ List >
161
- </ Card >
162
- ) ) }
163
- </ Stack >
164
- ) :(
165
- < Loader />
166
- ) }
126
+ < div css = { styles . content } >
127
+ { ready ?(
128
+ tab === "events" ?(
129
+ < EventsView
130
+ defaultMethod = { castNotificationMethod (
131
+ dispatchMethods . data . default ,
132
+ ) }
133
+ availableMethods = { dispatchMethods . data . available . map (
134
+ castNotificationMethod ,
135
+ ) }
136
+ notificationsConfig = { deploymentValues . config . notifications }
137
+ templatesByGroup = { templatesByGroup . data }
138
+ />
139
+ ) :(
140
+ < OptionsTable
141
+ options = { deploymentValues ?. options . filter ( ( o ) =>
142
+ deploymentGroupHasParent ( o . group , "Notifications" ) ,
143
+ ) }
144
+ />
145
+ )
146
+ ) :(
147
+ < Loader />
148
+ ) }
149
+ </ div >
167
150
</ Section >
168
151
) ;
169
152
} ;
170
153
171
- export default NotificationsPage ;
172
-
173
- type WebhookInfoProps = {
174
- endpoint :string ;
154
+ type EventsViewProps = {
155
+ defaultMethod :NotificationMethod ;
156
+ availableMethods :NotificationMethod [ ] ;
157
+ notificationsConfig ?:NotificationsConfig ;
158
+ templatesByGroup :ReturnType < typeof selectTemplatesByGroup > ;
175
159
} ;
176
160
177
- const WebhookInfo = ( { endpoint} :WebhookInfoProps ) => {
178
- const clipboard = useClipboard ( { textToCopy :endpoint } ) ;
161
+ const EventsView :FC < EventsViewProps > = ( {
162
+ defaultMethod,
163
+ availableMethods,
164
+ notificationsConfig,
165
+ templatesByGroup,
166
+ } ) => {
167
+ const isUsingWebhook = availableMethods . includes ( "webhook" ) ;
168
+ const webhookEndpoint = notificationsConfig ?. webhook . endpoint ;
179
169
180
170
return (
181
- < Alert
182
- severity = "info"
183
- actions = {
184
- < Button
185
- variant = "text"
186
- onClick = { clipboard . copyToClipboard }
187
- disabled = { clipboard . showCopiedSuccess }
171
+ < Stack spacing = { 3 } >
172
+ { isUsingWebhook && ! webhookEndpoint && (
173
+ < Alert severity = "warning" >
174
+ Webhook method is enabled, but the endpoint is not configured.
175
+ </ Alert >
176
+ ) }
177
+ { Object . entries ( templatesByGroup ) . map ( ( [ group , templates ] ) => (
178
+ < Card
179
+ key = { group }
180
+ variant = "outlined"
181
+ css = { { background :"transparent" , width :"100%" } }
188
182
>
189
- { clipboard . showCopiedSuccess ?"Copied!" :"Copy" }
190
- </ Button >
191
- }
192
- >
193
- < AlertTitle > Webhook Endpoint</ AlertTitle >
194
- < AlertDetail > { endpoint } </ AlertDetail >
195
- </ Alert >
183
+ < List >
184
+ < ListItem css = { styles . listHeader } >
185
+ < ListItemText css = { styles . listItemText } primary = { group } />
186
+ </ ListItem >
187
+
188
+ { templates . map ( ( tpl ) => {
189
+ const value = castNotificationMethod ( tpl . method || defaultMethod ) ;
190
+
191
+ return (
192
+ < Fragment key = { tpl . id } >
193
+ < ListItem >
194
+ < ListItemText
195
+ css = { styles . listItemText }
196
+ primary = { tpl . name }
197
+ />
198
+ < MethodToggleGroup
199
+ templateId = { tpl . id }
200
+ options = { availableMethods }
201
+ value = { value }
202
+ />
203
+ </ ListItem >
204
+ < Divider css = { styles . divider } />
205
+ </ Fragment >
206
+ ) ;
207
+ } ) }
208
+ </ List >
209
+ </ Card >
210
+ ) ) }
211
+ </ Stack >
196
212
) ;
197
213
} ;
198
214
215
+ export default NotificationsPage ;
216
+
199
217
const styles = {
218
+ content :{ paddingTop :24 } ,
200
219
listHeader :( theme ) => ( {
201
220
background :theme . palette . background . paper ,
202
221
borderBottom :`1px solid${ theme . palette . divider } ` ,