@backstage-community/plugin-ilert 0.3.0 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (116) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/dist/ILertIcon.esm.js +61 -0
  3. package/dist/ILertIcon.esm.js.map +1 -0
  4. package/dist/api/client.esm.js +395 -0
  5. package/dist/api/client.esm.js.map +1 -0
  6. package/dist/components/Alert/AlertActionsMenu.esm.js +142 -0
  7. package/dist/components/Alert/AlertActionsMenu.esm.js.map +1 -0
  8. package/dist/components/Alert/AlertAssignModal.esm.js +147 -0
  9. package/dist/components/Alert/AlertAssignModal.esm.js.map +1 -0
  10. package/dist/components/Alert/AlertLink.esm.js +15 -0
  11. package/dist/components/Alert/AlertLink.esm.js.map +1 -0
  12. package/dist/components/Alert/AlertNewModal.esm.js +192 -0
  13. package/dist/components/Alert/AlertNewModal.esm.js.map +1 -0
  14. package/dist/components/AlertSource/AlertSourceLink.esm.js +49 -0
  15. package/dist/components/AlertSource/AlertSourceLink.esm.js.map +1 -0
  16. package/dist/components/AlertsPage/AlertsPage.esm.js +67 -0
  17. package/dist/components/AlertsPage/AlertsPage.esm.js.map +1 -0
  18. package/dist/components/AlertsPage/AlertsTable.esm.js +185 -0
  19. package/dist/components/AlertsPage/AlertsTable.esm.js.map +1 -0
  20. package/dist/components/AlertsPage/StatusChip.esm.js +47 -0
  21. package/dist/components/AlertsPage/StatusChip.esm.js.map +1 -0
  22. package/dist/components/AlertsPage/TableTitle.esm.js +74 -0
  23. package/dist/components/AlertsPage/TableTitle.esm.js.map +1 -0
  24. package/dist/components/Errors/MissingAuthorizationHeaderError.esm.js +24 -0
  25. package/dist/components/Errors/MissingAuthorizationHeaderError.esm.js.map +1 -0
  26. package/dist/components/ILertCard/ILertCard.esm.js +113 -0
  27. package/dist/components/ILertCard/ILertCard.esm.js.map +1 -0
  28. package/dist/components/ILertCard/ILertCardActionsHeader.esm.js +138 -0
  29. package/dist/components/ILertCard/ILertCardActionsHeader.esm.js.map +1 -0
  30. package/dist/components/ILertCard/ILertCardEmptyState.esm.js +57 -0
  31. package/dist/components/ILertCard/ILertCardEmptyState.esm.js.map +1 -0
  32. package/dist/components/ILertCard/ILertCardHeaderStatus.esm.js +29 -0
  33. package/dist/components/ILertCard/ILertCardHeaderStatus.esm.js.map +1 -0
  34. package/dist/components/ILertCard/ILertCardMaintenanceModal.esm.js +101 -0
  35. package/dist/components/ILertCard/ILertCardMaintenanceModal.esm.js.map +1 -0
  36. package/dist/components/ILertCard/ILertCardOnCall.esm.js +78 -0
  37. package/dist/components/ILertCard/ILertCardOnCall.esm.js.map +1 -0
  38. package/dist/components/ILertCard/ILertCardOnCallEmptyState.esm.js +26 -0
  39. package/dist/components/ILertCard/ILertCardOnCallEmptyState.esm.js.map +1 -0
  40. package/dist/components/ILertCard/ILertCardOnCallItem.esm.js +109 -0
  41. package/dist/components/ILertCard/ILertCardOnCallItem.esm.js.map +1 -0
  42. package/dist/components/ILertCard/index.esm.js +2 -0
  43. package/dist/components/ILertCard/index.esm.js.map +1 -0
  44. package/dist/components/ILertPage/ILertPage.esm.js +48 -0
  45. package/dist/components/ILertPage/ILertPage.esm.js.map +1 -0
  46. package/dist/components/OnCallSchedulesPage/OnCallSchedulesGrid.esm.js +135 -0
  47. package/dist/components/OnCallSchedulesPage/OnCallSchedulesGrid.esm.js.map +1 -0
  48. package/dist/components/OnCallSchedulesPage/OnCallSchedulesPage.esm.js +27 -0
  49. package/dist/components/OnCallSchedulesPage/OnCallSchedulesPage.esm.js.map +1 -0
  50. package/dist/components/OnCallSchedulesPage/OnCallShiftItem.esm.js +63 -0
  51. package/dist/components/OnCallSchedulesPage/OnCallShiftItem.esm.js.map +1 -0
  52. package/dist/components/Service/ServiceActionsMenu.esm.js +47 -0
  53. package/dist/components/Service/ServiceActionsMenu.esm.js.map +1 -0
  54. package/dist/components/Service/ServiceLink.esm.js +15 -0
  55. package/dist/components/Service/ServiceLink.esm.js.map +1 -0
  56. package/dist/components/ServicesPage/ServicesPage.esm.js +32 -0
  57. package/dist/components/ServicesPage/ServicesPage.esm.js.map +1 -0
  58. package/dist/components/ServicesPage/ServicesTable.esm.js +99 -0
  59. package/dist/components/ServicesPage/ServicesTable.esm.js.map +1 -0
  60. package/dist/components/ServicesPage/StatusChip.esm.js +67 -0
  61. package/dist/components/ServicesPage/StatusChip.esm.js.map +1 -0
  62. package/dist/components/Shift/ShiftOverrideModal.esm.js +171 -0
  63. package/dist/components/Shift/ShiftOverrideModal.esm.js.map +1 -0
  64. package/dist/components/StatusPage/StatusPageActionsMenu.esm.js +50 -0
  65. package/dist/components/StatusPage/StatusPageActionsMenu.esm.js.map +1 -0
  66. package/dist/components/StatusPage/StatusPageLink.esm.js +17 -0
  67. package/dist/components/StatusPage/StatusPageLink.esm.js.map +1 -0
  68. package/dist/components/StatusPage/StatusPageURL.esm.js +18 -0
  69. package/dist/components/StatusPage/StatusPageURL.esm.js.map +1 -0
  70. package/dist/components/StatusPagePage/StatusChip.esm.js +67 -0
  71. package/dist/components/StatusPagePage/StatusChip.esm.js.map +1 -0
  72. package/dist/components/StatusPagePage/StatusPagesPage.esm.js +32 -0
  73. package/dist/components/StatusPagePage/StatusPagesPage.esm.js.map +1 -0
  74. package/dist/components/StatusPagePage/StatusPagesTable.esm.js +115 -0
  75. package/dist/components/StatusPagePage/StatusPagesTable.esm.js.map +1 -0
  76. package/dist/components/StatusPagePage/VisibilityChip.esm.js +37 -0
  77. package/dist/components/StatusPagePage/VisibilityChip.esm.js.map +1 -0
  78. package/dist/components/index.esm.js +3 -0
  79. package/dist/components/index.esm.js.map +1 -0
  80. package/dist/constants.esm.js +4 -0
  81. package/dist/constants.esm.js.map +1 -0
  82. package/dist/hooks/index.esm.js +13 -0
  83. package/dist/hooks/index.esm.js.map +1 -0
  84. package/dist/hooks/useAlertActions.esm.js +43 -0
  85. package/dist/hooks/useAlertActions.esm.js.map +1 -0
  86. package/dist/hooks/useAlertSource.esm.js +52 -0
  87. package/dist/hooks/useAlertSource.esm.js.map +1 -0
  88. package/dist/hooks/useAlertSourceOnCalls.esm.js +47 -0
  89. package/dist/hooks/useAlertSourceOnCalls.esm.js.map +1 -0
  90. package/dist/hooks/useAlerts.esm.js +128 -0
  91. package/dist/hooks/useAlerts.esm.js.map +1 -0
  92. package/dist/hooks/useAssignAlert.esm.js +53 -0
  93. package/dist/hooks/useAssignAlert.esm.js.map +1 -0
  94. package/dist/hooks/useNewAlert.esm.js +58 -0
  95. package/dist/hooks/useNewAlert.esm.js.map +1 -0
  96. package/dist/hooks/useOnCallSchedules.esm.js +42 -0
  97. package/dist/hooks/useOnCallSchedules.esm.js.map +1 -0
  98. package/dist/hooks/useServices.esm.js +70 -0
  99. package/dist/hooks/useServices.esm.js.map +1 -0
  100. package/dist/hooks/useShiftOverride.esm.js +60 -0
  101. package/dist/hooks/useShiftOverride.esm.js.map +1 -0
  102. package/dist/hooks/useStatusPages.esm.js +72 -0
  103. package/dist/hooks/useStatusPages.esm.js.map +1 -0
  104. package/dist/index.esm.js +7 -3346
  105. package/dist/index.esm.js.map +1 -1
  106. package/dist/plugin.esm.js +39 -0
  107. package/dist/plugin.esm.js.map +1 -0
  108. package/dist/route-refs.esm.js +8 -0
  109. package/dist/route-refs.esm.js.map +1 -0
  110. package/dist/types.esm.js +13 -0
  111. package/dist/types.esm.js.map +1 -0
  112. package/package.json +12 -8
  113. package/dist/esm/index-D2_Gjvjx.esm.js +0 -58
  114. package/dist/esm/index-D2_Gjvjx.esm.js.map +0 -1
  115. package/dist/esm/index-aory7UQN.esm.js +0 -58
  116. package/dist/esm/index-aory7UQN.esm.js.map +0 -1
package/dist/index.esm.js CHANGED
@@ -1,3347 +1,8 @@
1
- import { createApiRef, createRouteRef, createPlugin, createApiFactory, discoveryApiRef, configApiRef, fetchApiRef, createRoutableExtension, createComponentExtension, useApi, errorApiRef, alertApiRef, identityApiRef } from '@backstage/core-plugin-api';
2
- import { AuthenticationError, ResponseError } from '@backstage/errors';
3
- import { DateTime, Interval } from 'luxon';
4
- import { EmptyState, Progress, Link, Table, Content, ResponseErrorPanel, ContentHeader, SupportButton, ItemCardGrid, Page, Header, HeaderLabel, HeaderTabs, HeaderIconLinkRow, CodeSnippet } from '@backstage/core-components';
5
- import React from 'react';
6
- import Button from '@material-ui/core/Button';
7
- import AddIcon from '@material-ui/icons/Add';
8
- import useAsyncRetry from 'react-use/esm/useAsyncRetry';
9
- import { parseEntityRef, DEFAULT_NAMESPACE } from '@backstage/catalog-model';
10
- import Typography from '@material-ui/core/Typography';
11
- import Dialog from '@material-ui/core/Dialog';
12
- import DialogActions from '@material-ui/core/DialogActions';
13
- import DialogContent from '@material-ui/core/DialogContent';
14
- import DialogTitle from '@material-ui/core/DialogTitle';
15
- import { makeStyles, withStyles } from '@material-ui/core/styles';
16
- import TextField from '@material-ui/core/TextField';
17
- import useMediaQuery from '@material-ui/core/useMediaQuery';
18
- import Alert from '@material-ui/lab/Alert';
19
- import Autocomplete from '@material-ui/lab/Autocomplete';
20
- import humanizeDuration from 'humanize-duration';
21
- import IconButton from '@material-ui/core/IconButton';
22
- import Menu from '@material-ui/core/Menu';
23
- import MenuItem from '@material-ui/core/MenuItem';
24
- import MoreVertIcon from '@material-ui/icons/MoreVert';
25
- import Grid from '@material-ui/core/Grid';
26
- import Chip from '@material-ui/core/Chip';
27
- import Checkbox from '@material-ui/core/Checkbox';
28
- import FormControl from '@material-ui/core/FormControl';
29
- import ListItemText from '@material-ui/core/ListItemText';
30
- import Select from '@material-ui/core/Select';
31
- import Card from '@material-ui/core/Card';
32
- import CardContent from '@material-ui/core/CardContent';
33
- import CardHeader from '@material-ui/core/CardHeader';
34
- import RepeatIcon from '@material-ui/icons/Repeat';
35
- import { DateTimePicker } from '@material-ui/pickers/DateTimePicker';
36
- import MuiPickersUtilsProvider from '@material-ui/pickers/MuiPickersUtilsProvider';
37
- import LuxonUtils from '@date-io/luxon';
38
- import Divider from '@material-ui/core/Divider';
39
- import { useEntity } from '@backstage/plugin-catalog-react';
40
- import AlarmAddIcon from '@material-ui/icons/AlarmAdd';
41
- import BuildIcon from '@material-ui/icons/Build';
42
- import PauseIcon from '@material-ui/icons/Pause';
43
- import PlayArrowIcon from '@material-ui/icons/PlayArrow';
44
- import WebIcon from '@material-ui/icons/Web';
45
- import DialogContentText from '@material-ui/core/DialogContentText';
46
- import List from '@material-ui/core/List';
47
- import ListItem from '@material-ui/core/ListItem';
48
- import ListItemIcon from '@material-ui/core/ListItemIcon';
49
- import ListSubheader from '@material-ui/core/ListSubheader';
50
- import ReplayIcon from '@material-ui/icons/Replay';
51
- import Avatar from '@material-ui/core/Avatar';
52
- import ListItemSecondaryAction from '@material-ui/core/ListItemSecondaryAction';
53
- import Tooltip from '@material-ui/core/Tooltip';
54
- import EmailIcon from '@material-ui/icons/Email';
55
- import PhoneIcon from '@material-ui/icons/Phone';
56
- import SvgIcon from '@material-ui/core/SvgIcon';
57
-
58
- var __defProp = Object.defineProperty;
59
- var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
60
- var __publicField = (obj, key, value) => {
61
- __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
62
- return value;
63
- };
64
- const ilertApiRef = createApiRef({
65
- id: "plugin.ilert.service"
66
- });
67
- const DEFAULT_PROXY_PATH = "/ilert";
68
- const JSON_HEADERS = {
69
- "Content-Type": "application/json",
70
- Accept: "application/json"
71
- };
72
- class ILertClient {
73
- constructor(opts) {
74
- __publicField(this, "discoveryApi");
75
- __publicField(this, "fetchApi");
76
- __publicField(this, "proxyPath");
77
- __publicField(this, "baseUrl");
78
- this.discoveryApi = opts.discoveryApi;
79
- this.fetchApi = opts.fetchApi;
80
- this.baseUrl = opts.baseUrl;
81
- this.proxyPath = opts.proxyPath;
82
- }
83
- static fromConfig(configApi, discoveryApi, fetchApi) {
84
- var _a, _b;
85
- const baseUrl = (_a = configApi.getOptionalString("ilert.baseUrl")) != null ? _a : "https://app.ilert.com";
86
- return new ILertClient({
87
- discoveryApi,
88
- fetchApi,
89
- baseUrl,
90
- proxyPath: (_b = configApi.getOptionalString("ilert.proxyPath")) != null ? _b : DEFAULT_PROXY_PATH
91
- });
92
- }
93
- async fetch(input, init) {
94
- const apiUrl = await this.apiUrl();
95
- const response = await this.fetchApi.fetch(`${apiUrl}${input}`, init);
96
- if (response.status === 401) {
97
- throw new AuthenticationError(
98
- "This request requires HTTP authentication."
99
- );
100
- }
101
- if (!response.ok || response.status >= 400) {
102
- throw await ResponseError.fromResponse(response);
103
- }
104
- return await response.json();
105
- }
106
- async fetchAlerts(opts) {
107
- const init = {
108
- headers: JSON_HEADERS
109
- };
110
- const query = new URLSearchParams();
111
- if ((opts == null ? void 0 : opts.maxResults) !== void 0) {
112
- query.append("max-results", String(opts.maxResults));
113
- }
114
- if ((opts == null ? void 0 : opts.startIndex) !== void 0) {
115
- query.append("start-index", String(opts.startIndex));
116
- }
117
- if ((opts == null ? void 0 : opts.alertSources) !== void 0 && Array.isArray(opts.alertSources)) {
118
- opts.alertSources.forEach((a) => {
119
- if (a) {
120
- query.append("alert-source", String(a));
121
- }
122
- });
123
- }
124
- if ((opts == null ? void 0 : opts.states) !== void 0 && Array.isArray(opts.states)) {
125
- opts.states.forEach((state) => {
126
- query.append("state", state);
127
- });
128
- }
129
- const response = await this.fetch(`/api/alerts?${query.toString()}`, init);
130
- return response;
131
- }
132
- async fetchAlertsCount(opts) {
133
- const init = {
134
- headers: JSON_HEADERS
135
- };
136
- const query = new URLSearchParams();
137
- if (opts && opts.states && Array.isArray(opts.states)) {
138
- opts.states.forEach((state) => {
139
- query.append("state", state);
140
- });
141
- }
142
- const response = await this.fetch(
143
- `/api/alerts/count?${query.toString()}`,
144
- init
145
- );
146
- return response && response.count ? response.count : 0;
147
- }
148
- async fetchAlert(id) {
149
- const init = {
150
- headers: JSON_HEADERS
151
- };
152
- const response = await this.fetch(
153
- `/api/alerts/${encodeURIComponent(id)}`,
154
- init
155
- );
156
- return response;
157
- }
158
- async fetchAlertResponders(alert) {
159
- const init = {
160
- headers: JSON_HEADERS
161
- };
162
- const response = await this.fetch(
163
- `/api/alerts/${encodeURIComponent(alert.id)}/responders`,
164
- init
165
- );
166
- return response;
167
- }
168
- async fetchAlertActions(alert) {
169
- const init = {
170
- headers: JSON_HEADERS
171
- };
172
- const response = await this.fetch(
173
- `/api/alerts/${encodeURIComponent(alert.id)}/actions`,
174
- init
175
- );
176
- return response;
177
- }
178
- async acceptAlert(alert, userName) {
179
- var _a;
180
- const init = {
181
- method: "POST",
182
- headers: JSON_HEADERS,
183
- body: JSON.stringify({
184
- apiKey: ((_a = alert.alertSource) == null ? void 0 : _a.integrationKey) || "",
185
- alertKey: alert.alertKey,
186
- summary: `from ${userName} via Backstage plugin`,
187
- eventType: "ACCEPT"
188
- })
189
- };
190
- await this.fetch("/api/events", init);
191
- return this.fetchAlert(alert.id);
192
- }
193
- async resolveAlert(alert, userName) {
194
- var _a;
195
- const init = {
196
- method: "POST",
197
- headers: JSON_HEADERS,
198
- body: JSON.stringify({
199
- apiKey: ((_a = alert.alertSource) == null ? void 0 : _a.integrationKey) || "",
200
- alertKey: alert.alertKey,
201
- summary: `from ${userName} via Backstage plugin`,
202
- eventType: "RESOLVE"
203
- })
204
- };
205
- await this.fetch("/api/events", init);
206
- return this.fetchAlert(alert.id);
207
- }
208
- async assignAlert(alert, responder) {
209
- const init = {
210
- method: "PUT",
211
- headers: JSON_HEADERS
212
- };
213
- const query = new URLSearchParams();
214
- switch (responder.group) {
215
- case "ESCALATION_POLICY":
216
- query.append("policy-id", String(responder.id));
217
- break;
218
- case "ON_CALL_SCHEDULE":
219
- query.append("schedule-id", String(responder.id));
220
- break;
221
- default:
222
- query.append("user-id", String(responder.id));
223
- break;
224
- }
225
- const response = await this.fetch(
226
- `/api/alerts/${encodeURIComponent(alert.id)}/assign?${query.toString()}`,
227
- init
228
- );
229
- return response;
230
- }
231
- async triggerAlertAction(alert, action) {
232
- const init = {
233
- method: "POST",
234
- headers: JSON_HEADERS,
235
- body: JSON.stringify({
236
- webhookId: action.webhookId,
237
- extensionId: action.extensionId,
238
- type: action.type,
239
- name: action.name
240
- })
241
- };
242
- await this.fetch(
243
- `/api/alerts/${encodeURIComponent(alert.id)}/actions`,
244
- init
245
- );
246
- }
247
- async createAlert(eventRequest) {
248
- const init = {
249
- method: "POST",
250
- headers: JSON_HEADERS,
251
- body: JSON.stringify({
252
- apiKey: eventRequest.integrationKey,
253
- summary: eventRequest.summary,
254
- details: eventRequest.details,
255
- eventType: "ALERT",
256
- links: [
257
- {
258
- href: eventRequest.source,
259
- text: "Backstage Url"
260
- }
261
- ],
262
- customDetails: {
263
- userName: eventRequest.userName
264
- }
265
- })
266
- };
267
- const response = await this.fetch("/api/events", init);
268
- return response;
269
- }
270
- async fetchAlertSources() {
271
- const init = {
272
- headers: JSON_HEADERS
273
- };
274
- const response = await this.fetch("/api/alert-sources", init);
275
- return response;
276
- }
277
- async fetchAlertSource(idOrIntegrationKey) {
278
- const init = {
279
- headers: JSON_HEADERS
280
- };
281
- const response = await this.fetch(
282
- `/api/alert-sources/${encodeURIComponent(idOrIntegrationKey)}`,
283
- init
284
- );
285
- return response;
286
- }
287
- async fetchAlertSourceOnCalls(alertSource) {
288
- const init = {
289
- headers: JSON_HEADERS
290
- };
291
- const response = await this.fetch(
292
- `/api/on-calls?policies=${encodeURIComponent(
293
- alertSource.escalationPolicy.id
294
- )}&expand=user&expand=escalationPolicy&timezone=${DateTime.local().zoneName}`,
295
- init
296
- );
297
- return response;
298
- }
299
- async enableAlertSource(alertSource) {
300
- const init = {
301
- method: "PUT",
302
- headers: JSON_HEADERS,
303
- body: JSON.stringify({ ...alertSource, active: true })
304
- };
305
- const response = await this.fetch(
306
- `/api/alert-sources/${encodeURIComponent(alertSource.id)}`,
307
- init
308
- );
309
- return response;
310
- }
311
- async disableAlertSource(alertSource) {
312
- const init = {
313
- method: "PUT",
314
- headers: JSON_HEADERS,
315
- body: JSON.stringify({ ...alertSource, active: false })
316
- };
317
- const response = await this.fetch(
318
- `/api/alert-sources/${encodeURIComponent(alertSource.id)}`,
319
- init
320
- );
321
- return response;
322
- }
323
- async addImmediateMaintenance(alertSourceId, minutes) {
324
- const init = {
325
- method: "POST",
326
- headers: JSON_HEADERS,
327
- body: JSON.stringify({
328
- start: DateTime.utc().toISO(),
329
- end: DateTime.utc().plus({ minutes }).toISO(),
330
- description: `Immediate maintenance window for ${minutes} minutes. Backstage \u2014 iLert plugin.`,
331
- createdBy: "Backstage",
332
- timezone: DateTime.local().zoneName,
333
- alertSources: [{ id: alertSourceId }]
334
- })
335
- };
336
- const response = await this.fetch("/api/maintenance-windows", init);
337
- return response;
338
- }
339
- async fetchOnCallSchedules() {
340
- const init = {
341
- headers: JSON_HEADERS
342
- };
343
- const response = await this.fetch(
344
- "/api/schedules?include=currentShift&include=nextShift",
345
- init
346
- );
347
- return response;
348
- }
349
- async fetchUsers() {
350
- const init = {
351
- headers: JSON_HEADERS
352
- };
353
- const response = await this.fetch("/api/users", init);
354
- return response;
355
- }
356
- async overrideShift(scheduleId, userId, start, end) {
357
- const init = {
358
- method: "PUT",
359
- headers: JSON_HEADERS,
360
- body: JSON.stringify({ user: { id: userId }, start, end })
361
- };
362
- const response = await this.fetch(
363
- `/api/schedules/${encodeURIComponent(scheduleId)}/overrides`,
364
- init
365
- );
366
- return response;
367
- }
368
- async fetchServices(opts) {
369
- const init = {
370
- headers: JSON_HEADERS
371
- };
372
- const query = new URLSearchParams();
373
- if ((opts == null ? void 0 : opts.maxResults) !== void 0) {
374
- query.append("max-results", String(opts.maxResults));
375
- }
376
- if ((opts == null ? void 0 : opts.startIndex) !== void 0) {
377
- query.append("start-index", String(opts.startIndex));
378
- }
379
- query.append("include", "uptime");
380
- const response = await this.fetch(
381
- `/api/services?${query.toString()}`,
382
- init
383
- );
384
- return response;
385
- }
386
- async fetchStatusPages(opts) {
387
- const init = {
388
- headers: JSON_HEADERS
389
- };
390
- const query = new URLSearchParams();
391
- if ((opts == null ? void 0 : opts.maxResults) !== void 0) {
392
- query.append("max-results", String(opts.maxResults));
393
- }
394
- if ((opts == null ? void 0 : opts.startIndex) !== void 0) {
395
- query.append("start-index", String(opts.startIndex));
396
- }
397
- query.append("include", "subscribed");
398
- const response = await this.fetch(
399
- `/api/status-pages?${query.toString()}`,
400
- init
401
- );
402
- return response;
403
- }
404
- getAlertDetailsURL(alert) {
405
- return `${this.baseUrl}/alert/view.jsf?id=${encodeURIComponent(alert.id)}`;
406
- }
407
- getAlertSourceDetailsURL(alertSource) {
408
- if (!alertSource) {
409
- return "";
410
- }
411
- return `${this.baseUrl}/source/view.jsf?id=${encodeURIComponent(
412
- alertSource.id
413
- )}`;
414
- }
415
- getEscalationPolicyDetailsURL(escalationPolicy) {
416
- return `${this.baseUrl}/policy/view.jsf?id=${encodeURIComponent(
417
- escalationPolicy.id
418
- )}`;
419
- }
420
- getScheduleDetailsURL(schedule) {
421
- return `${this.baseUrl}/schedule/view.jsf?id=${encodeURIComponent(
422
- schedule.id
423
- )}`;
424
- }
425
- getServiceDetailsURL(service) {
426
- return `${this.baseUrl}/service/view.jsf?id=${encodeURIComponent(
427
- service.id
428
- )}`;
429
- }
430
- getStatusPageDetailsURL(statusPage) {
431
- return `${this.baseUrl}/status-page/view.jsf?id=${encodeURIComponent(
432
- statusPage.id
433
- )}`;
434
- }
435
- getStatusPageURL(statusPage) {
436
- return statusPage.domain ? statusPage.domain : statusPage.subdomain;
437
- }
438
- getUserPhoneNumber(user) {
439
- var _a, _b;
440
- return ((_a = user == null ? void 0 : user.mobile) == null ? void 0 : _a.number) || ((_b = user == null ? void 0 : user.landline) == null ? void 0 : _b.number) || "";
441
- }
442
- getUserInitials(user) {
443
- if (!user) {
444
- return "";
445
- }
446
- if (!user.firstName && !user.lastName) {
447
- return user.username;
448
- }
449
- return `${user.firstName} ${user.lastName}`;
450
- }
451
- async apiUrl() {
452
- const proxyUrl = await this.discoveryApi.getBaseUrl("proxy");
453
- return proxyUrl + this.proxyPath;
454
- }
455
- }
456
-
457
- const iLertRouteRef = createRouteRef({
458
- id: "ilert"
459
- });
460
-
461
- const ilertPlugin = createPlugin({
462
- id: "ilert",
463
- apis: [
464
- createApiFactory({
465
- api: ilertApiRef,
466
- deps: {
467
- discoveryApi: discoveryApiRef,
468
- configApi: configApiRef,
469
- fetchApi: fetchApiRef
470
- },
471
- factory: ({ discoveryApi, configApi, fetchApi }) => ILertClient.fromConfig(configApi, discoveryApi, fetchApi)
472
- })
473
- ],
474
- routes: {
475
- root: iLertRouteRef
476
- }
477
- });
478
- const ILertPage$1 = ilertPlugin.provide(
479
- createRoutableExtension({
480
- name: "ILertPage",
481
- component: () => import('./esm/index-D2_Gjvjx.esm.js').then((m) => m.ILertPage),
482
- mountPoint: iLertRouteRef
483
- })
484
- );
485
- const EntityILertCard = ilertPlugin.provide(
486
- createComponentExtension({
487
- name: "EntityILertCard",
488
- component: {
489
- lazy: () => import('./esm/index-aory7UQN.esm.js').then((m) => m.ILertCard)
490
- }
491
- })
492
- );
493
-
494
- const PENDING = "PENDING";
495
- const ACCEPTED = "ACCEPTED";
496
- const RESOLVED = "RESOLVED";
497
- const OPERATIONAL = "OPERATIONAL";
498
- const UNDER_MAINTENANCE = "UNDER_MAINTENANCE";
499
- const DEGRADED = "DEGRADED";
500
- const PARTIAL_OUTAGE = "PARTIAL_OUTAGE";
501
- const MAJOR_OUTAGE = "MAJOR_OUTAGE";
502
- const PRIVATE = "PRIVATE";
503
- const PUBLIC = "PUBLIC";
504
-
505
- const useAlerts = (paging, singleSource, alertSource) => {
506
- const ilertApi = useApi(ilertApiRef);
507
- const errorApi = useApi(errorApiRef);
508
- const [tableState, setTableState] = React.useState({
509
- page: 0,
510
- pageSize: 10
511
- });
512
- const [states, setStates] = React.useState([
513
- ACCEPTED,
514
- PENDING
515
- ]);
516
- const [alertsList, setAlertsList] = React.useState([]);
517
- const [alertsCount, setAlertsCount] = React.useState(0);
518
- const [isLoading, setIsLoading] = React.useState(false);
519
- const fetchAlertsCall = async () => {
520
- try {
521
- if (singleSource && !alertSource) {
522
- return;
523
- }
524
- setIsLoading(true);
525
- const opts = {
526
- states,
527
- alertSources: alertSource ? [alertSource.id] : []
528
- };
529
- if (paging) {
530
- opts.maxResults = tableState.pageSize;
531
- opts.startIndex = tableState.page * tableState.pageSize;
532
- }
533
- const data = await ilertApi.fetchAlerts(opts);
534
- setAlertsList(data || []);
535
- setIsLoading(false);
536
- } catch (e) {
537
- if (!(e instanceof AuthenticationError)) {
538
- errorApi.post(e);
539
- }
540
- setIsLoading(false);
541
- throw e;
542
- }
543
- };
544
- const fetchAlertsCountCall = async () => {
545
- try {
546
- const count = await ilertApi.fetchAlertsCount({ states });
547
- setAlertsCount(count || 0);
548
- } catch (e) {
549
- if (!(e instanceof AuthenticationError)) {
550
- errorApi.post(e);
551
- }
552
- throw e;
553
- }
554
- };
555
- const fetchAlerts = useAsyncRetry(fetchAlertsCall, [
556
- tableState,
557
- states,
558
- singleSource,
559
- alertSource
560
- ]);
561
- const refetchAlerts = () => {
562
- setTableState({ ...tableState, page: 0 });
563
- Promise.all([fetchAlertsCall(), fetchAlertsCountCall()]);
564
- };
565
- const fetchAlertsCount = useAsyncRetry(fetchAlertsCountCall, [states]);
566
- const error = fetchAlerts.error || fetchAlertsCount.error;
567
- const retry = () => {
568
- fetchAlerts.retry();
569
- fetchAlertsCount.retry();
570
- };
571
- const onAlertChanged = (newAlert) => {
572
- let shouldRefetchAlerts = false;
573
- setAlertsList(
574
- alertsList.reduce((acc, alert) => {
575
- if (newAlert.id === alert.id) {
576
- if (states.includes(newAlert.status)) {
577
- acc.push(newAlert);
578
- } else {
579
- shouldRefetchAlerts = true;
580
- }
581
- return acc;
582
- }
583
- acc.push(alert);
584
- return acc;
585
- }, [])
586
- );
587
- if (shouldRefetchAlerts) {
588
- refetchAlerts();
589
- }
590
- };
591
- const onChangePage = (page) => {
592
- setTableState({ ...tableState, page });
593
- };
594
- const onChangeRowsPerPage = (p) => {
595
- setTableState({ ...tableState, pageSize: p });
596
- };
597
- const onAlertStatesChange = (s) => {
598
- setStates(s);
599
- };
600
- return [
601
- {
602
- tableState,
603
- states,
604
- alerts: alertsList,
605
- alertsCount,
606
- error,
607
- isLoading
608
- },
609
- {
610
- setTableState,
611
- setStates,
612
- setAlertsList,
613
- setIsLoading,
614
- retry,
615
- onAlertChanged,
616
- refetchAlerts,
617
- onChangePage,
618
- onChangeRowsPerPage,
619
- onAlertStatesChange
620
- }
621
- ];
622
- };
623
-
624
- const useNewAlert = (open, initialAlertSource) => {
625
- const ilertApi = useApi(ilertApiRef);
626
- const errorApi = useApi(errorApiRef);
627
- const [alertSourcesList, setAlertSourcesList] = React.useState(
628
- []
629
- );
630
- const [alertSource, setAlertSource] = React.useState(
631
- null
632
- );
633
- const [summary, setSummary] = React.useState("");
634
- const [details, setDetails] = React.useState("");
635
- const [isLoading, setIsLoading] = React.useState(false);
636
- const fetchAlertSources = useAsyncRetry(async () => {
637
- try {
638
- if (!open || initialAlertSource) {
639
- return;
640
- }
641
- const count = await ilertApi.fetchAlertSources();
642
- setAlertSourcesList(count || 0);
643
- } catch (e) {
644
- if (!(e instanceof AuthenticationError)) {
645
- errorApi.post(e);
646
- }
647
- throw e;
648
- }
649
- }, [open]);
650
- const error = fetchAlertSources.error;
651
- const retry = () => {
652
- fetchAlertSources.retry();
653
- };
654
- return [
655
- {
656
- alertSources: alertSourcesList,
657
- alertSource: initialAlertSource ? initialAlertSource : alertSource,
658
- summary,
659
- details,
660
- error,
661
- isLoading
662
- },
663
- {
664
- setAlertSourcesList,
665
- setAlertSource,
666
- setSummary,
667
- setDetails,
668
- setIsLoading,
669
- retry
670
- }
671
- ];
672
- };
673
-
674
- const useStyles$e = makeStyles(() => ({
675
- container: {
676
- display: "flex",
677
- flexWrap: "wrap"
678
- },
679
- formControl: {
680
- minWidth: 120,
681
- width: "100%"
682
- },
683
- option: {
684
- fontSize: 15,
685
- "& > span": {
686
- marginRight: 10,
687
- fontSize: 18
688
- }
689
- },
690
- optionWrapper: {
691
- display: "flex",
692
- width: "100%"
693
- },
694
- sourceImage: {
695
- height: 22,
696
- paddingRight: 4
697
- }
698
- }));
699
- const AlertNewModal = ({
700
- isModalOpened,
701
- setIsModalOpened,
702
- refetchAlerts,
703
- initialAlertSource,
704
- entityName
705
- }) => {
706
- const [
707
- { alertSources, alertSource, summary, details, isLoading },
708
- { setAlertSource, setSummary, setDetails, setIsLoading }
709
- ] = useNewAlert(isModalOpened, initialAlertSource);
710
- const ilertApi = useApi(ilertApiRef);
711
- const alertApi = useApi(alertApiRef);
712
- const identityApi = useApi(identityApiRef);
713
- const source = window.location.toString();
714
- const classes = useStyles$e();
715
- const prefersDarkMode = useMediaQuery("(prefers-color-scheme: dark)");
716
- const handleClose = () => {
717
- setIsModalOpened(false);
718
- };
719
- let integrationKey = "";
720
- if (initialAlertSource && initialAlertSource.integrationKey) {
721
- integrationKey = initialAlertSource.integrationKey;
722
- } else if (alertSource && alertSource.integrationKey) {
723
- integrationKey = alertSource.integrationKey;
724
- }
725
- const handleCreate = () => {
726
- if (!integrationKey) {
727
- return;
728
- }
729
- setIsLoading(true);
730
- setTimeout(async () => {
731
- try {
732
- const { userEntityRef } = await identityApi.getBackstageIdentity();
733
- const { name: userName } = parseEntityRef(userEntityRef, {
734
- defaultKind: "User",
735
- defaultNamespace: DEFAULT_NAMESPACE
736
- });
737
- await ilertApi.createAlert({
738
- integrationKey,
739
- summary,
740
- details,
741
- userName,
742
- source
743
- });
744
- alertApi.post({ message: "Alert created." });
745
- refetchAlerts();
746
- } catch (err) {
747
- alertApi.post({ message: err, severity: "error" });
748
- }
749
- setIsModalOpened(false);
750
- }, 250);
751
- };
752
- const canCreate = !!integrationKey && !!summary;
753
- return /* @__PURE__ */ React.createElement(
754
- Dialog,
755
- {
756
- open: isModalOpened,
757
- onClose: handleClose,
758
- "aria-labelledby": "create-alert-form-title"
759
- },
760
- /* @__PURE__ */ React.createElement(DialogTitle, { id: "create-alert-form-title" }, entityName ? /* @__PURE__ */ React.createElement("div", null, "This action will trigger an alert for", " ", /* @__PURE__ */ React.createElement("strong", null, '"', entityName, '"'), ".") : "New alert"),
761
- /* @__PURE__ */ React.createElement(DialogContent, null, /* @__PURE__ */ React.createElement(Alert, { severity: "info" }, /* @__PURE__ */ React.createElement(Typography, { variant: "body1", gutterBottom: true, align: "justify" }, "Please describe the problem you want to report. Be as descriptive as possible. Your signed in user and a reference to the current page will automatically be amended to the alarm so that the receiver can reach out to you if necessary.")), !initialAlertSource ? /* @__PURE__ */ React.createElement(
762
- Autocomplete,
763
- {
764
- disabled: isLoading,
765
- options: alertSources,
766
- value: alertSource,
767
- classes: {
768
- root: classes.formControl,
769
- option: classes.option
770
- },
771
- onChange: (_event, newValue) => {
772
- setAlertSource(newValue);
773
- },
774
- autoHighlight: true,
775
- getOptionLabel: (a) => a.name,
776
- renderOption: (a) => /* @__PURE__ */ React.createElement("div", { className: classes.optionWrapper }, /* @__PURE__ */ React.createElement(
777
- "img",
778
- {
779
- src: prefersDarkMode ? a.lightIconUrl : a.iconUrl,
780
- alt: a.name,
781
- className: classes.sourceImage
782
- }
783
- ), /* @__PURE__ */ React.createElement(Typography, { noWrap: true }, a.name)),
784
- renderInput: (params) => /* @__PURE__ */ React.createElement(
785
- TextField,
786
- {
787
- ...params,
788
- label: "Alert Source",
789
- variant: "outlined",
790
- margin: "normal",
791
- inputProps: {
792
- ...params.inputProps,
793
- autoComplete: "new-password"
794
- // disable autocomplete and autofill
795
- }
796
- }
797
- )
798
- }
799
- ) : null, /* @__PURE__ */ React.createElement(
800
- TextField,
801
- {
802
- disabled: isLoading,
803
- label: "Summary",
804
- fullWidth: true,
805
- margin: "normal",
806
- variant: "outlined",
807
- classes: {
808
- root: classes.formControl
809
- },
810
- value: summary,
811
- onChange: (event) => {
812
- setSummary(event.target.value);
813
- }
814
- }
815
- ), /* @__PURE__ */ React.createElement(
816
- TextField,
817
- {
818
- disabled: isLoading,
819
- label: "Details",
820
- fullWidth: true,
821
- multiline: true,
822
- rows: 4,
823
- margin: "normal",
824
- variant: "outlined",
825
- classes: {
826
- root: classes.formControl
827
- },
828
- value: details,
829
- onChange: (event) => {
830
- setDetails(event.target.value);
831
- }
832
- }
833
- )),
834
- /* @__PURE__ */ React.createElement(DialogActions, null, /* @__PURE__ */ React.createElement(
835
- Button,
836
- {
837
- disabled: !canCreate,
838
- onClick: handleCreate,
839
- color: "secondary",
840
- variant: "contained"
841
- },
842
- "Create"
843
- ), /* @__PURE__ */ React.createElement(Button, { onClick: handleClose, color: "primary" }, "Cancel"))
844
- );
845
- };
846
-
847
- const MissingAuthorizationHeaderError = () => /* @__PURE__ */ React.createElement(
848
- EmptyState,
849
- {
850
- missing: "info",
851
- title: "Missing or invalid iLert authorization header",
852
- description: "The request to fetch data needs a valid authorization header. See README for more details.",
853
- action: /* @__PURE__ */ React.createElement(
854
- Button,
855
- {
856
- color: "primary",
857
- variant: "contained",
858
- href: "https://github.com/backstage/backstage/blob/master/plugins/ilert/README.md"
859
- },
860
- "Read More"
861
- )
862
- }
863
- );
864
-
865
- const useAlertActions = (alert, open) => {
866
- const ilertApi = useApi(ilertApiRef);
867
- const errorApi = useApi(errorApiRef);
868
- const [alertActionsList, setAlertActionsList] = React.useState(
869
- []
870
- );
871
- const [isLoading, setIsLoading] = React.useState(false);
872
- const { error, retry } = useAsyncRetry(async () => {
873
- try {
874
- if (!alert || !open) {
875
- return;
876
- }
877
- const data = await ilertApi.fetchAlertActions(alert);
878
- setAlertActionsList(data);
879
- } catch (e) {
880
- if (!(e instanceof AuthenticationError)) {
881
- errorApi.post(e);
882
- }
883
- throw e;
884
- }
885
- }, [alert, open]);
886
- return [
887
- {
888
- alertActions: alertActionsList,
889
- error,
890
- isLoading
891
- },
892
- {
893
- setAlertActionsList,
894
- setIsLoading,
895
- retry
896
- }
897
- ];
898
- };
899
-
900
- const useAssignAlert = (alert, open) => {
901
- const ilertApi = useApi(ilertApiRef);
902
- const errorApi = useApi(errorApiRef);
903
- const [alertRespondersList, setAlertRespondersList] = React.useState([]);
904
- const [alertResponder, setAlertResponder] = React.useState(null);
905
- const [isLoading, setIsLoading] = React.useState(false);
906
- const { error, retry } = useAsyncRetry(async () => {
907
- try {
908
- if (!alert || !open) {
909
- return;
910
- }
911
- const data = await ilertApi.fetchAlertResponders(alert);
912
- if (data && Array.isArray(data)) {
913
- const groups = [
914
- "SUGGESTED",
915
- "USER",
916
- "ESCALATION_POLICY",
917
- "ON_CALL_SCHEDULE"
918
- ];
919
- data.sort((a, b) => groups.indexOf(a.group) - groups.indexOf(b.group));
920
- setAlertRespondersList(data);
921
- }
922
- } catch (e) {
923
- if (!(e instanceof AuthenticationError)) {
924
- errorApi.post(e);
925
- }
926
- throw e;
927
- }
928
- }, [alert, open]);
929
- return [
930
- {
931
- alertRespondersList,
932
- alertResponder,
933
- error,
934
- isLoading
935
- },
936
- {
937
- setAlertRespondersList,
938
- setAlertResponder,
939
- setIsLoading,
940
- retry
941
- }
942
- ];
943
- };
944
-
945
- const useStyles$d = makeStyles(() => ({
946
- container: {
947
- display: "flex",
948
- flexWrap: "wrap"
949
- },
950
- formControl: {
951
- minWidth: 120,
952
- width: "100%"
953
- },
954
- option: {
955
- fontSize: 15,
956
- "& > span": {
957
- marginRight: 10,
958
- fontSize: 18
959
- }
960
- },
961
- optionWrapper: {
962
- display: "flex",
963
- width: "100%"
964
- },
965
- sourceImage: {
966
- height: 22,
967
- paddingRight: 4
968
- }
969
- }));
970
- const AlertAssignModal = ({
971
- alert,
972
- isModalOpened,
973
- setIsModalOpened,
974
- onAlertChanged
975
- }) => {
976
- const [
977
- { alertRespondersList, alertResponder, isLoading },
978
- { setIsLoading, setAlertResponder, setAlertRespondersList }
979
- ] = useAssignAlert(alert, isModalOpened);
980
- const callback = onAlertChanged || ((_) => {
981
- });
982
- const ilertApi = useApi(ilertApiRef);
983
- const alertApi = useApi(alertApiRef);
984
- const classes = useStyles$d();
985
- const handleClose = () => {
986
- setAlertRespondersList([]);
987
- setIsModalOpened(false);
988
- };
989
- const handleAssign = () => {
990
- if (!alert || !alertResponder) {
991
- return;
992
- }
993
- setIsLoading(true);
994
- setAlertRespondersList([]);
995
- setTimeout(async () => {
996
- try {
997
- const newAlert = await ilertApi.assignAlert(alert, alertResponder);
998
- callback(newAlert);
999
- alertApi.post({ message: "Alert assigned." });
1000
- } catch (err) {
1001
- alertApi.post({ message: err, severity: "error" });
1002
- }
1003
- setIsLoading(false);
1004
- setIsModalOpened(false);
1005
- }, 250);
1006
- };
1007
- const canAssign = !!alertResponder;
1008
- return /* @__PURE__ */ React.createElement(
1009
- Dialog,
1010
- {
1011
- open: isModalOpened,
1012
- onClose: handleClose,
1013
- "aria-labelledby": "assign-alert-form-title"
1014
- },
1015
- /* @__PURE__ */ React.createElement(DialogTitle, { id: "assign-alert-form-title" }, "Select responder to assign"),
1016
- /* @__PURE__ */ React.createElement(DialogContent, null, /* @__PURE__ */ React.createElement(Alert, { severity: "info" }, /* @__PURE__ */ React.createElement(Typography, { variant: "body1", gutterBottom: true, align: "justify" }, "This action will assign the alert to the selected responder.")), /* @__PURE__ */ React.createElement(
1017
- Autocomplete,
1018
- {
1019
- disabled: isLoading,
1020
- options: alertRespondersList,
1021
- value: alertResponder,
1022
- classes: {
1023
- root: classes.formControl,
1024
- option: classes.option
1025
- },
1026
- onChange: (_event, newValue) => {
1027
- setAlertResponder(newValue);
1028
- },
1029
- autoHighlight: true,
1030
- groupBy: (option) => {
1031
- switch (option.group) {
1032
- case "SUGGESTED":
1033
- return "Suggested responders";
1034
- case "USER":
1035
- return "Users";
1036
- case "ESCALATION_POLICY":
1037
- return "Escalation policies";
1038
- case "ON_CALL_SCHEDULE":
1039
- return "Schedules";
1040
- default:
1041
- return "";
1042
- }
1043
- },
1044
- getOptionLabel: (a) => a.name,
1045
- renderOption: (a) => /* @__PURE__ */ React.createElement("div", { className: classes.optionWrapper }, /* @__PURE__ */ React.createElement(Typography, { noWrap: true }, a.name)),
1046
- renderInput: (params) => /* @__PURE__ */ React.createElement(
1047
- TextField,
1048
- {
1049
- ...params,
1050
- label: "Responder",
1051
- variant: "outlined",
1052
- margin: "normal",
1053
- inputProps: {
1054
- ...params.inputProps,
1055
- autoComplete: "new-password"
1056
- // disable autocomplete and autofill
1057
- }
1058
- }
1059
- )
1060
- }
1061
- )),
1062
- /* @__PURE__ */ React.createElement(DialogActions, null, /* @__PURE__ */ React.createElement(
1063
- Button,
1064
- {
1065
- disabled: !canAssign,
1066
- onClick: handleAssign,
1067
- color: "primary",
1068
- variant: "contained"
1069
- },
1070
- "Assign"
1071
- ), /* @__PURE__ */ React.createElement(Button, { onClick: handleClose, color: "primary" }, "Cancel"))
1072
- );
1073
- };
1074
-
1075
- const AlertActionsMenu = ({
1076
- alert,
1077
- onAlertChanged,
1078
- setIsLoading
1079
- }) => {
1080
- const ilertApi = useApi(ilertApiRef);
1081
- const alertApi = useApi(alertApiRef);
1082
- const identityApi = useApi(identityApiRef);
1083
- const [anchorEl, setAnchorEl] = React.useState(null);
1084
- const callback = onAlertChanged || ((_) => {
1085
- });
1086
- const setProcessing = setIsLoading || ((_) => {
1087
- });
1088
- const [isAssignAlertModalOpened, setIsAssignAlertModalOpened] = React.useState(false);
1089
- const [{ alertActions, isLoading }] = useAlertActions(
1090
- alert,
1091
- Boolean(anchorEl)
1092
- );
1093
- const handleClick = (event) => {
1094
- setAnchorEl(event.currentTarget);
1095
- };
1096
- const handleCloseMenu = () => {
1097
- setAnchorEl(null);
1098
- };
1099
- const handleAccept = async () => {
1100
- try {
1101
- handleCloseMenu();
1102
- setProcessing(true);
1103
- const { userEntityRef } = await identityApi.getBackstageIdentity();
1104
- const { name: userName } = parseEntityRef(userEntityRef, {
1105
- defaultKind: "User",
1106
- defaultNamespace: DEFAULT_NAMESPACE
1107
- });
1108
- const newAlert = await ilertApi.acceptAlert(alert, userName);
1109
- alertApi.post({ message: "Alert accepted." });
1110
- callback(newAlert);
1111
- setProcessing(false);
1112
- } catch (err) {
1113
- setProcessing(false);
1114
- alertApi.post({ message: err, severity: "error" });
1115
- }
1116
- };
1117
- const handleResolve = async () => {
1118
- try {
1119
- handleCloseMenu();
1120
- setProcessing(true);
1121
- const { userEntityRef } = await identityApi.getBackstageIdentity();
1122
- const { name: userName } = parseEntityRef(userEntityRef, {
1123
- defaultKind: "User",
1124
- defaultNamespace: DEFAULT_NAMESPACE
1125
- });
1126
- const newAlert = await ilertApi.resolveAlert(alert, userName);
1127
- alertApi.post({ message: "Alert resolved." });
1128
- callback(newAlert);
1129
- setProcessing(false);
1130
- } catch (err) {
1131
- setProcessing(false);
1132
- alertApi.post({ message: err, severity: "error" });
1133
- }
1134
- };
1135
- const handleAssign = () => {
1136
- handleCloseMenu();
1137
- setIsAssignAlertModalOpened(true);
1138
- };
1139
- const handleTriggerAction = (action) => async () => {
1140
- try {
1141
- handleCloseMenu();
1142
- setProcessing(true);
1143
- await ilertApi.triggerAlertAction(alert, action);
1144
- alertApi.post({ message: "Alert action triggered." });
1145
- setProcessing(false);
1146
- } catch (err) {
1147
- setProcessing(false);
1148
- alertApi.post({ message: err, severity: "error" });
1149
- }
1150
- };
1151
- const actions = alertActions.map((a) => {
1152
- const successTrigger = a.history ? a.history.find((h) => h.success) : void 0;
1153
- const triggeredBy = successTrigger && successTrigger.actor ? `${successTrigger.actor.firstName} ${successTrigger.actor.lastName}` : "";
1154
- return /* @__PURE__ */ React.createElement(
1155
- MenuItem,
1156
- {
1157
- key: a.webhookId,
1158
- onClick: handleTriggerAction(a),
1159
- disabled: !!successTrigger
1160
- },
1161
- /* @__PURE__ */ React.createElement(Typography, { variant: "inherit", noWrap: true }, triggeredBy ? `${a.name} (by ${triggeredBy})` : a.name)
1162
- );
1163
- });
1164
- return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(
1165
- IconButton,
1166
- {
1167
- "aria-label": "more",
1168
- "aria-controls": "long-menu",
1169
- "aria-haspopup": "true",
1170
- onClick: handleClick,
1171
- size: "small"
1172
- },
1173
- /* @__PURE__ */ React.createElement(MoreVertIcon, null)
1174
- ), /* @__PURE__ */ React.createElement(
1175
- Menu,
1176
- {
1177
- id: `alert-actions-menu-${alert.id}`,
1178
- anchorEl,
1179
- keepMounted: true,
1180
- open: Boolean(anchorEl),
1181
- onClose: handleCloseMenu,
1182
- PaperProps: {
1183
- style: { maxHeight: 48 * 5.5 }
1184
- }
1185
- },
1186
- alert.status === "PENDING" ? /* @__PURE__ */ React.createElement(MenuItem, { key: "ack", onClick: handleAccept }, /* @__PURE__ */ React.createElement(Typography, { variant: "inherit", noWrap: true }, "Accept")) : null,
1187
- alert.status !== "RESOLVED" ? /* @__PURE__ */ React.createElement(MenuItem, { key: "close", onClick: handleResolve }, /* @__PURE__ */ React.createElement(Typography, { variant: "inherit", noWrap: true }, "Resolve")) : null,
1188
- alert.status !== "RESOLVED" ? /* @__PURE__ */ React.createElement(MenuItem, { key: "assign", onClick: handleAssign }, /* @__PURE__ */ React.createElement(Typography, { variant: "inherit", noWrap: true }, "Assign")) : null,
1189
- isLoading ? /* @__PURE__ */ React.createElement(MenuItem, { key: "loading" }, /* @__PURE__ */ React.createElement(Progress, { style: { width: "100%" } })) : actions,
1190
- /* @__PURE__ */ React.createElement(MenuItem, { key: "details", onClick: handleCloseMenu }, /* @__PURE__ */ React.createElement(Typography, { variant: "inherit", noWrap: true }, /* @__PURE__ */ React.createElement(Link, { to: ilertApi.getAlertDetailsURL(alert) }, "View in iLert")))
1191
- ), /* @__PURE__ */ React.createElement(
1192
- AlertAssignModal,
1193
- {
1194
- alert,
1195
- setIsModalOpened: setIsAssignAlertModalOpened,
1196
- isModalOpened: isAssignAlertModalOpened,
1197
- onAlertChanged
1198
- }
1199
- ));
1200
- };
1201
-
1202
- const AlertLink = ({ alert }) => {
1203
- const ilertApi = useApi(ilertApiRef);
1204
- if (!alert) {
1205
- return null;
1206
- }
1207
- return /* @__PURE__ */ React.createElement(Link, { to: ilertApi.getAlertDetailsURL(alert) }, "#", alert.id);
1208
- };
1209
-
1210
- const useStyles$c = makeStyles({
1211
- root: {
1212
- display: "flex",
1213
- maxWidth: "100%"
1214
- },
1215
- image: {
1216
- height: 22,
1217
- paddingRight: 4
1218
- },
1219
- link: {
1220
- lineHeight: "22px"
1221
- }
1222
- });
1223
- const AlertSourceLink = ({
1224
- alertSource
1225
- }) => {
1226
- const ilertApi = useApi(ilertApiRef);
1227
- const classes = useStyles$c();
1228
- const prefersDarkMode = useMediaQuery("(prefers-color-scheme: dark)");
1229
- if (!alertSource) {
1230
- return null;
1231
- }
1232
- return /* @__PURE__ */ React.createElement(Grid, { container: true, spacing: 0 }, /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 2 }, /* @__PURE__ */ React.createElement(
1233
- "img",
1234
- {
1235
- src: prefersDarkMode ? alertSource.lightIconUrl : alertSource.iconUrl,
1236
- alt: alertSource.name,
1237
- className: classes.image
1238
- }
1239
- )), /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 10 }, /* @__PURE__ */ React.createElement(
1240
- Link,
1241
- {
1242
- className: classes.link,
1243
- to: ilertApi.getAlertSourceDetailsURL(alertSource)
1244
- },
1245
- alertSource.name
1246
- )));
1247
- };
1248
-
1249
- const ResolvedChip = withStyles((theme) => ({
1250
- root: {
1251
- backgroundColor: "#4caf50",
1252
- color: theme.palette.common.white,
1253
- margin: 0
1254
- }
1255
- }))(Chip);
1256
- const AcceptedChip = withStyles((theme) => ({
1257
- root: {
1258
- backgroundColor: "#ffb74d",
1259
- color: theme.palette.common.white,
1260
- margin: 0
1261
- }
1262
- }))(Chip);
1263
- const PendingChip = withStyles((theme) => ({
1264
- root: {
1265
- backgroundColor: "#d32f2f",
1266
- color: theme.palette.common.white,
1267
- margin: 0
1268
- }
1269
- }))(Chip);
1270
- const alertStatusLabels = {
1271
- [RESOLVED]: "Resolved",
1272
- [ACCEPTED]: "Accepted",
1273
- [PENDING]: "Pending"
1274
- };
1275
- const StatusChip$2 = ({ alert }) => {
1276
- const label = `${alertStatusLabels[alert.status]}`;
1277
- switch (alert.status) {
1278
- case RESOLVED:
1279
- return /* @__PURE__ */ React.createElement(ResolvedChip, { label, size: "small" });
1280
- case ACCEPTED:
1281
- return /* @__PURE__ */ React.createElement(AcceptedChip, { label, size: "small" });
1282
- case PENDING:
1283
- return /* @__PURE__ */ React.createElement(PendingChip, { label, size: "small" });
1284
- default:
1285
- return /* @__PURE__ */ React.createElement(Chip, { label, size: "small" });
1286
- }
1287
- };
1288
-
1289
- const ITEM_HEIGHT = 48;
1290
- const ITEM_PADDING_TOP = 8;
1291
- const MenuProps = {
1292
- PaperProps: {
1293
- style: {
1294
- maxHeight: ITEM_HEIGHT * 4.5 + ITEM_PADDING_TOP,
1295
- width: 250
1296
- }
1297
- }
1298
- };
1299
- const useStyles$b = makeStyles({
1300
- root: {
1301
- display: "flex"
1302
- },
1303
- label: {
1304
- marginTop: 8,
1305
- marginRight: 4
1306
- },
1307
- formControl: {
1308
- minWidth: 120,
1309
- maxWidth: 300
1310
- },
1311
- grow: {
1312
- flexGrow: 1
1313
- }
1314
- });
1315
- const TableTitle = ({
1316
- alertStates,
1317
- onAlertStatesChange
1318
- }) => {
1319
- const classes = useStyles$b();
1320
- const handleAlertStatusSelectChange = (event) => {
1321
- onAlertStatesChange(event.target.value);
1322
- };
1323
- return /* @__PURE__ */ React.createElement("div", { className: classes.root }, /* @__PURE__ */ React.createElement(Typography, { noWrap: true, className: classes.label }, "Status:"), /* @__PURE__ */ React.createElement(
1324
- FormControl,
1325
- {
1326
- className: classes.formControl,
1327
- variant: "outlined",
1328
- size: "small"
1329
- },
1330
- /* @__PURE__ */ React.createElement(
1331
- Select,
1332
- {
1333
- id: "alerts-status-select",
1334
- multiple: true,
1335
- value: alertStates,
1336
- onChange: handleAlertStatusSelectChange,
1337
- renderValue: (selected) => selected.join(", "),
1338
- MenuProps
1339
- },
1340
- [PENDING, ACCEPTED, RESOLVED].map((state) => /* @__PURE__ */ React.createElement(MenuItem, { key: state, value: state }, /* @__PURE__ */ React.createElement(
1341
- Checkbox,
1342
- {
1343
- checked: alertStates.indexOf(state) > -1
1344
- }
1345
- ), /* @__PURE__ */ React.createElement(ListItemText, { primary: alertStatusLabels[state] })))
1346
- )
1347
- ));
1348
- };
1349
-
1350
- const useStyles$a = makeStyles((theme) => ({
1351
- empty: {
1352
- padding: theme.spacing(2),
1353
- display: "flex",
1354
- justifyContent: "center"
1355
- }
1356
- }));
1357
- const AlertsTable = ({
1358
- alerts,
1359
- alertsCount,
1360
- tableState,
1361
- states,
1362
- isLoading,
1363
- onAlertChanged,
1364
- setIsLoading,
1365
- onAlertStatesChange,
1366
- onChangePage,
1367
- onChangeRowsPerPage,
1368
- compact
1369
- }) => {
1370
- const ilertApi = useApi(ilertApiRef);
1371
- const classes = useStyles$a();
1372
- const xsColumnStyle = {
1373
- width: "5%",
1374
- maxWidth: "5%"
1375
- };
1376
- const smColumnStyle = {
1377
- width: "10%",
1378
- maxWidth: "10%"
1379
- };
1380
- const mdColumnStyle = {
1381
- width: "15%",
1382
- maxWidth: "15%"
1383
- };
1384
- const lgColumnStyle = {
1385
- width: "20%",
1386
- maxWidth: "20%"
1387
- };
1388
- const xlColumnStyle = {
1389
- width: "30%",
1390
- maxWidth: "30%"
1391
- };
1392
- const idColumn = {
1393
- title: "ID",
1394
- field: "id",
1395
- highlight: true,
1396
- cellStyle: smColumnStyle,
1397
- headerStyle: smColumnStyle,
1398
- render: (rowData) => /* @__PURE__ */ React.createElement(AlertLink, { alert: rowData })
1399
- };
1400
- const summaryColumn = {
1401
- title: "Summary",
1402
- field: "summary",
1403
- cellStyle: !compact ? xlColumnStyle : void 0,
1404
- headerStyle: !compact ? xlColumnStyle : void 0,
1405
- render: (rowData) => /* @__PURE__ */ React.createElement(Typography, null, rowData.summary)
1406
- };
1407
- const sourceColumn = {
1408
- title: "Source",
1409
- field: "source",
1410
- cellStyle: mdColumnStyle,
1411
- headerStyle: mdColumnStyle,
1412
- render: (rowData) => /* @__PURE__ */ React.createElement(AlertSourceLink, { alertSource: rowData.alertSource })
1413
- };
1414
- const durationColumn = {
1415
- title: "Duration",
1416
- field: "reportTime",
1417
- type: "datetime",
1418
- cellStyle: smColumnStyle,
1419
- headerStyle: smColumnStyle,
1420
- render: (rowData) => /* @__PURE__ */ React.createElement(Typography, { noWrap: true }, rowData.status !== "RESOLVED" ? humanizeDuration(
1421
- Interval.fromDateTimes(DateTime.fromISO(rowData.reportTime), DateTime.now()).toDuration().valueOf(),
1422
- { units: ["h", "m", "s"], largest: 2, round: true }
1423
- ) : humanizeDuration(
1424
- Interval.fromDateTimes(
1425
- DateTime.fromISO(rowData.reportTime),
1426
- DateTime.fromISO(rowData.resolvedOn)
1427
- ).toDuration().valueOf(),
1428
- { units: ["h", "m", "s"], largest: 2, round: true }
1429
- ))
1430
- };
1431
- const respondersColumn = {
1432
- title: "Responders",
1433
- field: "responders",
1434
- cellStyle: !compact ? mdColumnStyle : lgColumnStyle,
1435
- headerStyle: !compact ? mdColumnStyle : lgColumnStyle,
1436
- render: (rowData) => /* @__PURE__ */ React.createElement(Typography, null, rowData.responders.map((value, i, arr) => {
1437
- return ilertApi.getUserInitials(value.user) + (arr.length - 1 !== i ? ", " : "");
1438
- }))
1439
- };
1440
- const priorityColumn = {
1441
- title: "Priority",
1442
- field: "priority",
1443
- cellStyle: smColumnStyle,
1444
- headerStyle: smColumnStyle,
1445
- render: (rowData) => /* @__PURE__ */ React.createElement(Typography, { noWrap: true }, rowData.priority === "HIGH" ? "High" : "Low")
1446
- };
1447
- const statusColumn = {
1448
- title: "Status",
1449
- field: "status",
1450
- cellStyle: xsColumnStyle,
1451
- headerStyle: xsColumnStyle,
1452
- render: (rowData) => /* @__PURE__ */ React.createElement(StatusChip$2, { alert: rowData })
1453
- };
1454
- const actionsColumn = {
1455
- title: "",
1456
- field: "",
1457
- cellStyle: xsColumnStyle,
1458
- headerStyle: xsColumnStyle,
1459
- render: (rowData) => /* @__PURE__ */ React.createElement(
1460
- AlertActionsMenu,
1461
- {
1462
- alert: rowData,
1463
- onAlertChanged,
1464
- setIsLoading
1465
- }
1466
- )
1467
- };
1468
- const columns = compact ? [
1469
- summaryColumn,
1470
- durationColumn,
1471
- respondersColumn,
1472
- statusColumn,
1473
- actionsColumn
1474
- ] : [
1475
- idColumn,
1476
- summaryColumn,
1477
- sourceColumn,
1478
- durationColumn,
1479
- respondersColumn,
1480
- priorityColumn,
1481
- statusColumn,
1482
- actionsColumn
1483
- ];
1484
- return /* @__PURE__ */ React.createElement(
1485
- Table,
1486
- {
1487
- options: {
1488
- sorting: false,
1489
- search: !compact,
1490
- paging: !compact,
1491
- actionsColumnIndex: -1,
1492
- pageSize: tableState.pageSize,
1493
- pageSizeOptions: !compact ? [10, 20, 50, 100] : [3, 10, 20, 50, 100],
1494
- padding: "dense",
1495
- loadingType: "overlay",
1496
- showEmptyDataSourceMessage: !isLoading,
1497
- showTitle: true,
1498
- toolbar: true
1499
- },
1500
- emptyContent: /* @__PURE__ */ React.createElement(Typography, { color: "textSecondary", className: classes.empty }, "No alerts right now"),
1501
- title: !compact ? /* @__PURE__ */ React.createElement(
1502
- TableTitle,
1503
- {
1504
- alertStates: states,
1505
- onAlertStatesChange
1506
- }
1507
- ) : /* @__PURE__ */ React.createElement(Typography, { variant: "button", color: "textSecondary" }, "ALERTS"),
1508
- page: tableState.page,
1509
- totalCount: alertsCount,
1510
- onPageChange: onChangePage,
1511
- onRowsPerPageChange: onChangeRowsPerPage,
1512
- columns,
1513
- data: alerts,
1514
- isLoading
1515
- }
1516
- );
1517
- };
1518
-
1519
- const AlertsPage = () => {
1520
- const [
1521
- { tableState, states, alerts, alertsCount, isLoading, error },
1522
- {
1523
- onAlertStatesChange,
1524
- onChangePage,
1525
- onChangeRowsPerPage,
1526
- onAlertChanged,
1527
- refetchAlerts,
1528
- setIsLoading
1529
- }
1530
- ] = useAlerts(true);
1531
- const [isModalOpened, setIsModalOpened] = React.useState(false);
1532
- const handleCreateNewAlertClick = () => {
1533
- setIsModalOpened(true);
1534
- };
1535
- if (error) {
1536
- if (error.name === "AuthenticationError") {
1537
- return /* @__PURE__ */ React.createElement(Content, null, /* @__PURE__ */ React.createElement(MissingAuthorizationHeaderError, null));
1538
- }
1539
- return /* @__PURE__ */ React.createElement(Content, null, /* @__PURE__ */ React.createElement(ResponseErrorPanel, { error }));
1540
- }
1541
- return /* @__PURE__ */ React.createElement(Content, null, /* @__PURE__ */ React.createElement(ContentHeader, { title: "Alerts" }, /* @__PURE__ */ React.createElement(
1542
- Button,
1543
- {
1544
- variant: "contained",
1545
- color: "primary",
1546
- size: "small",
1547
- startIcon: /* @__PURE__ */ React.createElement(AddIcon, null),
1548
- onClick: handleCreateNewAlertClick
1549
- },
1550
- "Create Alert"
1551
- ), /* @__PURE__ */ React.createElement(
1552
- AlertNewModal,
1553
- {
1554
- isModalOpened,
1555
- setIsModalOpened,
1556
- refetchAlerts
1557
- }
1558
- ), /* @__PURE__ */ React.createElement(SupportButton, null, "This helps you to bring iLert into your developer portal.")), /* @__PURE__ */ React.createElement(
1559
- AlertsTable,
1560
- {
1561
- alerts,
1562
- alertsCount,
1563
- tableState,
1564
- states,
1565
- onAlertChanged,
1566
- onAlertStatesChange,
1567
- onChangePage,
1568
- onChangeRowsPerPage,
1569
- isLoading,
1570
- setIsLoading
1571
- }
1572
- ));
1573
- };
1574
-
1575
- const useShiftOverride = (s, isModalOpened) => {
1576
- const ilertApi = useApi(ilertApiRef);
1577
- const errorApi = useApi(errorApiRef);
1578
- const [shift, setShift] = React.useState(s);
1579
- const [usersList, setUsersList] = React.useState([]);
1580
- const [isLoading, setIsLoading] = React.useState(false);
1581
- const { error, retry } = useAsyncRetry(async () => {
1582
- try {
1583
- if (!isModalOpened) {
1584
- return;
1585
- }
1586
- setIsLoading(true);
1587
- const data = await ilertApi.fetchUsers();
1588
- setUsersList(data || []);
1589
- setIsLoading(false);
1590
- } catch (e) {
1591
- setIsLoading(false);
1592
- if (!(e instanceof AuthenticationError)) {
1593
- errorApi.post(e);
1594
- }
1595
- throw e;
1596
- }
1597
- }, [isModalOpened]);
1598
- const setUser = (user) => {
1599
- setShift({ ...shift, user });
1600
- };
1601
- const setStart = (start) => {
1602
- setShift({ ...shift, start });
1603
- };
1604
- const setEnd = (end) => {
1605
- setShift({ ...shift, end });
1606
- };
1607
- return [
1608
- {
1609
- shift,
1610
- users: usersList,
1611
- user: shift.user,
1612
- start: shift.start,
1613
- end: shift.end,
1614
- error,
1615
- isLoading
1616
- },
1617
- {
1618
- retry,
1619
- setIsLoading,
1620
- setUser,
1621
- setStart,
1622
- setEnd
1623
- }
1624
- ];
1625
- };
1626
-
1627
- const useStyles$9 = makeStyles(() => ({
1628
- container: {
1629
- display: "flex",
1630
- flexWrap: "wrap"
1631
- },
1632
- formControl: {
1633
- minWidth: 120,
1634
- width: "100%"
1635
- },
1636
- option: {
1637
- fontSize: 15,
1638
- "& > span": {
1639
- marginRight: 10,
1640
- fontSize: 18
1641
- }
1642
- },
1643
- optionWrapper: {
1644
- display: "flex",
1645
- width: "100%"
1646
- },
1647
- sourceImage: {
1648
- height: 22,
1649
- paddingRight: 4
1650
- },
1651
- grow: {
1652
- flexGrow: 1
1653
- }
1654
- }));
1655
- const ShiftOverrideModal = ({
1656
- scheduleId,
1657
- shift,
1658
- refetchOnCallSchedules,
1659
- isModalOpened,
1660
- setIsModalOpened
1661
- }) => {
1662
- const [
1663
- { isLoading, users, user, start, end },
1664
- { setUser, setStart, setEnd, setIsLoading }
1665
- ] = useShiftOverride(shift, isModalOpened);
1666
- const ilertApi = useApi(ilertApiRef);
1667
- const alertApi = useApi(alertApiRef);
1668
- const classes = useStyles$9();
1669
- const handleClose = () => {
1670
- setIsModalOpened(false);
1671
- };
1672
- const handleOverride = () => {
1673
- if (!shift || !shift.user) {
1674
- return;
1675
- }
1676
- setIsLoading(true);
1677
- setTimeout(async () => {
1678
- try {
1679
- const success = await ilertApi.overrideShift(
1680
- scheduleId,
1681
- user.id,
1682
- start,
1683
- end
1684
- );
1685
- if (success) {
1686
- alertApi.post({ message: "Shift overridden." });
1687
- refetchOnCallSchedules();
1688
- }
1689
- } catch (err) {
1690
- alertApi.post({ message: err, severity: "error" });
1691
- }
1692
- setIsModalOpened(false);
1693
- }, 250);
1694
- };
1695
- if (!shift) {
1696
- return null;
1697
- }
1698
- return /* @__PURE__ */ React.createElement(
1699
- Dialog,
1700
- {
1701
- open: isModalOpened,
1702
- onClose: handleClose,
1703
- "aria-labelledby": "override-shift-form-title"
1704
- },
1705
- /* @__PURE__ */ React.createElement(DialogTitle, { id: "override-shift-form-title" }, "Shift override"),
1706
- /* @__PURE__ */ React.createElement(DialogContent, null, /* @__PURE__ */ React.createElement(MuiPickersUtilsProvider, { utils: LuxonUtils }, /* @__PURE__ */ React.createElement(
1707
- Autocomplete,
1708
- {
1709
- disabled: isLoading,
1710
- options: users,
1711
- value: user,
1712
- classes: {
1713
- root: classes.formControl,
1714
- option: classes.option
1715
- },
1716
- onChange: (_event, newValue) => {
1717
- setUser(newValue);
1718
- },
1719
- autoHighlight: true,
1720
- getOptionLabel: (a) => ilertApi.getUserInitials(a),
1721
- renderOption: (a) => /* @__PURE__ */ React.createElement("div", { className: classes.optionWrapper }, /* @__PURE__ */ React.createElement(Typography, { noWrap: true }, ilertApi.getUserInitials(a))),
1722
- renderInput: (params) => /* @__PURE__ */ React.createElement(
1723
- TextField,
1724
- {
1725
- ...params,
1726
- label: "User",
1727
- variant: "outlined",
1728
- fullWidth: true,
1729
- inputProps: {
1730
- ...params.inputProps,
1731
- autoComplete: "new-password"
1732
- // disable autocomplete and autofill
1733
- }
1734
- }
1735
- )
1736
- }
1737
- ), /* @__PURE__ */ React.createElement(
1738
- DateTimePicker,
1739
- {
1740
- label: "Start",
1741
- inputVariant: "outlined",
1742
- fullWidth: true,
1743
- margin: "normal",
1744
- ampm: false,
1745
- value: start,
1746
- className: classes.formControl,
1747
- onChange: (date) => {
1748
- var _a;
1749
- setStart((_a = date == null ? void 0 : date.toISO()) != null ? _a : "");
1750
- }
1751
- }
1752
- ), /* @__PURE__ */ React.createElement(
1753
- DateTimePicker,
1754
- {
1755
- label: "End",
1756
- inputVariant: "outlined",
1757
- fullWidth: true,
1758
- margin: "normal",
1759
- ampm: false,
1760
- value: end,
1761
- className: classes.formControl,
1762
- onChange: (date) => {
1763
- var _a;
1764
- setEnd((_a = date == null ? void 0 : date.toISO()) != null ? _a : "");
1765
- }
1766
- }
1767
- ))),
1768
- /* @__PURE__ */ React.createElement(DialogActions, null, /* @__PURE__ */ React.createElement(
1769
- Button,
1770
- {
1771
- disabled: isLoading,
1772
- onClick: handleOverride,
1773
- color: "primary",
1774
- variant: "contained"
1775
- },
1776
- "Override"
1777
- ), /* @__PURE__ */ React.createElement(Button, { disabled: isLoading, onClick: handleClose, color: "primary" }, "Cancel"))
1778
- );
1779
- };
1780
-
1781
- const useStyles$8 = makeStyles({
1782
- button: {
1783
- marginTop: 4,
1784
- padding: 0,
1785
- lineHeight: 1.8,
1786
- "& span": {
1787
- lineHeight: 1.8,
1788
- fontSize: "0.65rem"
1789
- },
1790
- "& svg": {
1791
- fontSize: "0.85rem !important"
1792
- }
1793
- }
1794
- });
1795
- const OnCallShiftItem = ({
1796
- scheduleId,
1797
- shift,
1798
- refetchOnCallSchedules
1799
- }) => {
1800
- const classes = useStyles$8();
1801
- const ilertApi = useApi(ilertApiRef);
1802
- const [isModalOpened, setIsModalOpened] = React.useState(false);
1803
- const handleOverride = () => {
1804
- setIsModalOpened(true);
1805
- };
1806
- if (!shift || !shift.start) {
1807
- return /* @__PURE__ */ React.createElement(Grid, { container: true, spacing: 0 }, /* @__PURE__ */ React.createElement(Grid, { item: true, sm: 12 }, /* @__PURE__ */ React.createElement(Typography, { variant: "subtitle1", color: "textSecondary" }, "Nobody")));
1808
- }
1809
- return /* @__PURE__ */ React.createElement(Grid, { container: true, spacing: 0 }, shift && shift.user ? /* @__PURE__ */ React.createElement(Grid, { item: true, sm: 12 }, /* @__PURE__ */ React.createElement(Typography, { variant: "subtitle1", noWrap: true }, ilertApi.getUserInitials(shift.user))) : null, /* @__PURE__ */ React.createElement(Grid, { item: true, sm: 12 }, /* @__PURE__ */ React.createElement(Typography, { variant: "subtitle2", color: "textSecondary" }, `${DateTime.fromISO(shift.start).toFormat("D MMM, HH:mm")} - ${DateTime.fromISO(shift.end).toFormat("D MMM, HH:mm")}`)), /* @__PURE__ */ React.createElement(Grid, { item: true, sm: 12 }, /* @__PURE__ */ React.createElement(
1810
- Button,
1811
- {
1812
- color: "primary",
1813
- size: "small",
1814
- className: classes.button,
1815
- startIcon: /* @__PURE__ */ React.createElement(RepeatIcon, null),
1816
- onClick: handleOverride
1817
- },
1818
- /* @__PURE__ */ React.createElement(Typography, { variant: "overline" }, "Override shift")
1819
- ), /* @__PURE__ */ React.createElement(
1820
- ShiftOverrideModal,
1821
- {
1822
- scheduleId,
1823
- shift,
1824
- refetchOnCallSchedules,
1825
- isModalOpened,
1826
- setIsModalOpened
1827
- }
1828
- )));
1829
- };
1830
-
1831
- const useStyles$7 = makeStyles(() => ({
1832
- card: {
1833
- margin: 16,
1834
- width: "calc(100% - 32px)"
1835
- },
1836
- cardHeader: {
1837
- maxWidth: "100%"
1838
- },
1839
- cardContent: {
1840
- marginLeft: 80,
1841
- borderLeft: "1px #808289 solid",
1842
- position: "relative"
1843
- },
1844
- indicatorNext: {
1845
- position: "absolute",
1846
- top: "calc(40% - 10px)",
1847
- left: -6,
1848
- width: 12,
1849
- height: 12,
1850
- background: "#92949c !important",
1851
- borderRadius: "50%"
1852
- },
1853
- indicatorCurrent: {
1854
- position: "absolute",
1855
- top: "calc(40% - 10px)",
1856
- left: -6,
1857
- width: 12,
1858
- height: 12,
1859
- background: "#ffb74d !important",
1860
- color: "#ffb74d !important",
1861
- borderRadius: "50%",
1862
- "&::after": {
1863
- position: "absolute",
1864
- top: 0,
1865
- left: 0,
1866
- width: "100%",
1867
- height: "100%",
1868
- borderRadius: "50%",
1869
- animation: "$ripple 1.2s infinite ease-in-out",
1870
- border: "1px solid currentColor",
1871
- content: '""'
1872
- }
1873
- },
1874
- "@keyframes ripple": {
1875
- "0%": {
1876
- transform: "scale(.8)",
1877
- opacity: 1
1878
- },
1879
- "100%": {
1880
- transform: "scale(2.4)",
1881
- opacity: 0
1882
- }
1883
- },
1884
- beforeText: {
1885
- position: "absolute",
1886
- top: "calc(31% - 10px)",
1887
- left: -78,
1888
- width: 65,
1889
- height: 20,
1890
- textAlign: "center",
1891
- color: "#808289"
1892
- },
1893
- marginBottom: {
1894
- marginBottom: 16
1895
- },
1896
- link: {
1897
- fontSize: "1.5rem",
1898
- fontWeight: 700,
1899
- overflow: "hidden",
1900
- whiteSpace: "nowrap",
1901
- textOverflow: "ellipsis",
1902
- display: "block"
1903
- }
1904
- }));
1905
- const OnCallSchedulesGrid = ({
1906
- onCallSchedules,
1907
- isLoading,
1908
- refetchOnCallSchedules
1909
- }) => {
1910
- const ilertApi = useApi(ilertApiRef);
1911
- const classes = useStyles$7();
1912
- if (isLoading) {
1913
- return /* @__PURE__ */ React.createElement(Progress, null);
1914
- }
1915
- return /* @__PURE__ */ React.createElement(ItemCardGrid, { "data-testid": "docs-explore" }, !(onCallSchedules == null ? void 0 : onCallSchedules.length) ? null : onCallSchedules.map((schedule, index) => /* @__PURE__ */ React.createElement(Card, { key: index }, /* @__PURE__ */ React.createElement(
1916
- CardHeader,
1917
- {
1918
- classes: { content: classes.cardHeader },
1919
- title: /* @__PURE__ */ React.createElement(
1920
- Link,
1921
- {
1922
- to: ilertApi.getScheduleDetailsURL(schedule),
1923
- className: classes.link
1924
- },
1925
- schedule.name
1926
- )
1927
- }
1928
- ), /* @__PURE__ */ React.createElement(CardContent, { className: classes.cardContent }, /* @__PURE__ */ React.createElement("div", { className: classes.indicatorCurrent }), /* @__PURE__ */ React.createElement(
1929
- OnCallShiftItem,
1930
- {
1931
- shift: schedule.currentShift,
1932
- scheduleId: schedule.id,
1933
- refetchOnCallSchedules
1934
- }
1935
- ), /* @__PURE__ */ React.createElement(Typography, { className: classes.beforeText, variant: "body2" }, "On call now")), /* @__PURE__ */ React.createElement(
1936
- CardContent,
1937
- {
1938
- className: `${classes.cardContent} ${classes.marginBottom}`
1939
- },
1940
- /* @__PURE__ */ React.createElement("div", { className: classes.indicatorNext }),
1941
- /* @__PURE__ */ React.createElement(
1942
- OnCallShiftItem,
1943
- {
1944
- shift: schedule.nextShift,
1945
- scheduleId: schedule.id,
1946
- refetchOnCallSchedules
1947
- }
1948
- ),
1949
- /* @__PURE__ */ React.createElement(Typography, { className: classes.beforeText, variant: "body2" }, "Next on call")
1950
- ))));
1951
- };
1952
-
1953
- const useOnCallSchedules = () => {
1954
- const ilertApi = useApi(ilertApiRef);
1955
- const errorApi = useApi(errorApiRef);
1956
- const [onCallSchedulesList, setOnCallSchedulesList] = React.useState([]);
1957
- const [isLoading, setIsLoading] = React.useState(false);
1958
- const fetchOnCallSchedulesCall = async () => {
1959
- try {
1960
- setIsLoading(true);
1961
- const data = await ilertApi.fetchOnCallSchedules();
1962
- setOnCallSchedulesList(data || []);
1963
- setIsLoading(false);
1964
- } catch (e) {
1965
- setIsLoading(false);
1966
- if (!(e instanceof AuthenticationError)) {
1967
- errorApi.post(e);
1968
- }
1969
- throw e;
1970
- }
1971
- };
1972
- const { error, retry } = useAsyncRetry(fetchOnCallSchedulesCall, []);
1973
- return [
1974
- {
1975
- onCallSchedules: onCallSchedulesList,
1976
- error,
1977
- isLoading
1978
- },
1979
- {
1980
- retry,
1981
- setIsLoading,
1982
- refetchOnCallSchedules: fetchOnCallSchedulesCall
1983
- }
1984
- ];
1985
- };
1986
-
1987
- const OnCallSchedulesPage = () => {
1988
- const [{ onCallSchedules, isLoading, error }, { refetchOnCallSchedules }] = useOnCallSchedules();
1989
- if (error) {
1990
- if (error instanceof AuthenticationError) {
1991
- return /* @__PURE__ */ React.createElement(Content, null, /* @__PURE__ */ React.createElement(MissingAuthorizationHeaderError, null));
1992
- }
1993
- return /* @__PURE__ */ React.createElement(Content, null, /* @__PURE__ */ React.createElement(ResponseErrorPanel, { error }));
1994
- }
1995
- return /* @__PURE__ */ React.createElement(Content, null, /* @__PURE__ */ React.createElement(ContentHeader, { title: "Who is on call?" }, /* @__PURE__ */ React.createElement(SupportButton, null, "This helps you to bring iLert into your developer portal.")), /* @__PURE__ */ React.createElement(
1996
- OnCallSchedulesGrid,
1997
- {
1998
- onCallSchedules,
1999
- isLoading,
2000
- refetchOnCallSchedules
2001
- }
2002
- ));
2003
- };
2004
-
2005
- const useServices = (paging) => {
2006
- const ilertApi = useApi(ilertApiRef);
2007
- const errorApi = useApi(errorApiRef);
2008
- const [tableState, setTableState] = React.useState({
2009
- page: 0,
2010
- pageSize: 10
2011
- });
2012
- const [servicesList, setServicesList] = React.useState([]);
2013
- const [isLoading, setIsLoading] = React.useState(false);
2014
- const fetchServicesCall = async () => {
2015
- try {
2016
- setIsLoading(true);
2017
- const opts = {};
2018
- if (paging) {
2019
- opts.maxResults = tableState.pageSize;
2020
- opts.startIndex = tableState.page * tableState.pageSize;
2021
- }
2022
- const data = await ilertApi.fetchServices(opts);
2023
- setServicesList(data || []);
2024
- setIsLoading(false);
2025
- } catch (e) {
2026
- if (!(e instanceof AuthenticationError)) {
2027
- errorApi.post(e);
2028
- }
2029
- setIsLoading(false);
2030
- throw e;
2031
- }
2032
- };
2033
- const fetchServices = useAsyncRetry(fetchServicesCall, [tableState]);
2034
- const refetchServices = () => {
2035
- setTableState({ ...tableState, page: 0 });
2036
- Promise.all([fetchServicesCall()]);
2037
- };
2038
- const error = fetchServices.error;
2039
- const retry = () => {
2040
- fetchServices.retry();
2041
- };
2042
- const onChangePage = (page) => {
2043
- setTableState({ ...tableState, page });
2044
- };
2045
- const onChangeRowsPerPage = (p) => {
2046
- setTableState({ ...tableState, pageSize: p });
2047
- };
2048
- return [
2049
- {
2050
- tableState,
2051
- services: servicesList,
2052
- isLoading,
2053
- error
2054
- },
2055
- {
2056
- setTableState,
2057
- setServicesList,
2058
- setIsLoading,
2059
- retry,
2060
- refetchServices,
2061
- onChangePage,
2062
- onChangeRowsPerPage
2063
- }
2064
- ];
2065
- };
2066
-
2067
- const OperationalChip$1 = withStyles((theme) => ({
2068
- root: {
2069
- backgroundColor: "#388E3D",
2070
- color: theme.palette.common.white,
2071
- margin: 0
2072
- }
2073
- }))(Chip);
2074
- const UnderMaintenanceChip$1 = withStyles((theme) => ({
2075
- root: {
2076
- backgroundColor: "#616161",
2077
- color: theme.palette.common.white,
2078
- margin: 0
2079
- }
2080
- }))(Chip);
2081
- const DegradedChip$1 = withStyles((theme) => ({
2082
- root: {
2083
- backgroundColor: "#FBC02D",
2084
- color: theme.palette.common.white,
2085
- margin: 0
2086
- }
2087
- }))(Chip);
2088
- const PartialOutageChip$1 = withStyles((theme) => ({
2089
- root: {
2090
- backgroundColor: "#F57C02",
2091
- color: theme.palette.common.white,
2092
- margin: 0
2093
- }
2094
- }))(Chip);
2095
- const MajorOutageChip$1 = withStyles((theme) => ({
2096
- root: {
2097
- backgroundColor: "#D22F2E",
2098
- color: theme.palette.common.white,
2099
- margin: 0
2100
- }
2101
- }))(Chip);
2102
- const serviceStatusLabels = {
2103
- [OPERATIONAL]: "Operational",
2104
- [UNDER_MAINTENANCE]: "Under maintenance",
2105
- [DEGRADED]: "Degraded",
2106
- [PARTIAL_OUTAGE]: "Partial outage",
2107
- [MAJOR_OUTAGE]: "Major outage"
2108
- };
2109
- const StatusChip$1 = ({ service }) => {
2110
- const label = `${serviceStatusLabels[service.status]}`;
2111
- switch (service.status) {
2112
- case OPERATIONAL:
2113
- return /* @__PURE__ */ React.createElement(OperationalChip$1, { label, size: "small" });
2114
- case UNDER_MAINTENANCE:
2115
- return /* @__PURE__ */ React.createElement(UnderMaintenanceChip$1, { label, size: "small" });
2116
- case DEGRADED:
2117
- return /* @__PURE__ */ React.createElement(DegradedChip$1, { label, size: "small" });
2118
- case PARTIAL_OUTAGE:
2119
- return /* @__PURE__ */ React.createElement(PartialOutageChip$1, { label, size: "small" });
2120
- case MAJOR_OUTAGE:
2121
- return /* @__PURE__ */ React.createElement(MajorOutageChip$1, { label, size: "small" });
2122
- default:
2123
- return /* @__PURE__ */ React.createElement(Chip, { label, size: "small" });
2124
- }
2125
- };
2126
-
2127
- const ServiceActionsMenu = ({ service }) => {
2128
- const ilertApi = useApi(ilertApiRef);
2129
- const [anchorEl, setAnchorEl] = React.useState(null);
2130
- const handleClick = (event) => {
2131
- setAnchorEl(event.currentTarget);
2132
- };
2133
- const handleCloseMenu = () => {
2134
- setAnchorEl(null);
2135
- };
2136
- return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(
2137
- IconButton,
2138
- {
2139
- "aria-label": "more",
2140
- "aria-controls": "long-menu",
2141
- "aria-haspopup": "true",
2142
- onClick: handleClick,
2143
- size: "small"
2144
- },
2145
- /* @__PURE__ */ React.createElement(MoreVertIcon, null)
2146
- ), /* @__PURE__ */ React.createElement(
2147
- Menu,
2148
- {
2149
- id: `service-actions-menu-${service.id}`,
2150
- anchorEl,
2151
- keepMounted: true,
2152
- open: Boolean(anchorEl),
2153
- onClose: handleCloseMenu,
2154
- PaperProps: {
2155
- style: { maxHeight: 48 * 5.5 }
2156
- }
2157
- },
2158
- /* @__PURE__ */ React.createElement(MenuItem, { key: "details", onClick: handleCloseMenu }, /* @__PURE__ */ React.createElement(Typography, { variant: "inherit", noWrap: true }, /* @__PURE__ */ React.createElement(Link, { to: ilertApi.getServiceDetailsURL(service) }, "View in iLert")))
2159
- ));
2160
- };
2161
-
2162
- const ServiceLink = ({ service }) => {
2163
- const ilertApi = useApi(ilertApiRef);
2164
- if (!service) {
2165
- return null;
2166
- }
2167
- return /* @__PURE__ */ React.createElement(Link, { to: ilertApi.getServiceDetailsURL(service) }, "#", service.id);
2168
- };
2169
-
2170
- const useStyles$6 = makeStyles((theme) => ({
2171
- empty: {
2172
- padding: theme.spacing(2),
2173
- display: "flex",
2174
- justifyContent: "center"
2175
- }
2176
- }));
2177
- const ServicesTable = ({
2178
- services,
2179
- tableState,
2180
- isLoading,
2181
- onChangePage,
2182
- onChangeRowsPerPage,
2183
- compact
2184
- }) => {
2185
- const classes = useStyles$6();
2186
- const smColumnStyle = {
2187
- width: "10%",
2188
- maxWidth: "10%"
2189
- };
2190
- const xlColumnStyle = {
2191
- width: "30%",
2192
- maxWidth: "30%"
2193
- };
2194
- const idColumn = {
2195
- title: "ID",
2196
- field: "id",
2197
- highlight: true,
2198
- cellStyle: smColumnStyle,
2199
- headerStyle: smColumnStyle,
2200
- render: (rowData) => /* @__PURE__ */ React.createElement(ServiceLink, { service: rowData })
2201
- };
2202
- const nameColumn = {
2203
- title: "Name",
2204
- field: "name",
2205
- cellStyle: !compact ? xlColumnStyle : void 0,
2206
- headerStyle: !compact ? xlColumnStyle : void 0,
2207
- render: (rowData) => /* @__PURE__ */ React.createElement(Typography, null, rowData.name)
2208
- };
2209
- const statusColumn = {
2210
- title: "Status",
2211
- field: "status",
2212
- cellStyle: smColumnStyle,
2213
- headerStyle: smColumnStyle,
2214
- render: (rowData) => /* @__PURE__ */ React.createElement(StatusChip$1, { service: rowData })
2215
- };
2216
- const uptimeColumn = {
2217
- title: "Uptime in the last 90 days",
2218
- field: "uptimePercentage",
2219
- cellStyle: smColumnStyle,
2220
- headerStyle: smColumnStyle,
2221
- render: (rowData) => {
2222
- var _a, _b;
2223
- return /* @__PURE__ */ React.createElement(Typography, null, ((_b = (_a = rowData.uptime) == null ? void 0 : _a.uptimePercentage) == null ? void 0 : _b.p90) || "");
2224
- }
2225
- };
2226
- const actionsColumn = {
2227
- title: "",
2228
- field: "",
2229
- cellStyle: smColumnStyle,
2230
- headerStyle: smColumnStyle,
2231
- render: (rowData) => /* @__PURE__ */ React.createElement(ServiceActionsMenu, { service: rowData })
2232
- };
2233
- const columns = compact ? [nameColumn, statusColumn, uptimeColumn, actionsColumn] : [idColumn, nameColumn, statusColumn, uptimeColumn, actionsColumn];
2234
- return /* @__PURE__ */ React.createElement(
2235
- Table,
2236
- {
2237
- options: {
2238
- sorting: false,
2239
- search: !compact,
2240
- paging: !compact,
2241
- actionsColumnIndex: -1,
2242
- pageSize: tableState.pageSize,
2243
- pageSizeOptions: !compact ? [10, 20, 50, 100] : [3, 10, 20, 50, 100],
2244
- padding: "dense",
2245
- loadingType: "overlay",
2246
- showEmptyDataSourceMessage: !isLoading,
2247
- showTitle: true,
2248
- toolbar: true
2249
- },
2250
- emptyContent: /* @__PURE__ */ React.createElement(Typography, { color: "textSecondary", className: classes.empty }, "No services"),
2251
- title: /* @__PURE__ */ React.createElement(Typography, { variant: "button", color: "textSecondary" }, "SERVICES"),
2252
- page: tableState.page,
2253
- onPageChange: onChangePage,
2254
- onRowsPerPageChange: onChangeRowsPerPage,
2255
- columns,
2256
- data: services,
2257
- isLoading
2258
- }
2259
- );
2260
- };
2261
-
2262
- const ServicesPage = () => {
2263
- const [
2264
- { tableState, services, isLoading, error },
2265
- { onChangePage, onChangeRowsPerPage, setIsLoading }
2266
- ] = useServices(true);
2267
- if (error) {
2268
- if (error.name === "AuthenticationError") {
2269
- return /* @__PURE__ */ React.createElement(Content, null, /* @__PURE__ */ React.createElement(MissingAuthorizationHeaderError, null));
2270
- }
2271
- return /* @__PURE__ */ React.createElement(Content, null, /* @__PURE__ */ React.createElement(ResponseErrorPanel, { error }));
2272
- }
2273
- return /* @__PURE__ */ React.createElement(Content, null, /* @__PURE__ */ React.createElement(ContentHeader, { title: "Services" }, /* @__PURE__ */ React.createElement(SupportButton, null, "This helps you to bring iLert into your developer portal.")), /* @__PURE__ */ React.createElement(
2274
- ServicesTable,
2275
- {
2276
- services,
2277
- tableState,
2278
- onChangePage,
2279
- onChangeRowsPerPage,
2280
- isLoading,
2281
- setIsLoading
2282
- }
2283
- ));
2284
- };
2285
-
2286
- const useStatusPages = (paging) => {
2287
- const ilertApi = useApi(ilertApiRef);
2288
- const errorApi = useApi(errorApiRef);
2289
- const [tableState, setTableState] = React.useState({
2290
- page: 0,
2291
- pageSize: 10
2292
- });
2293
- const [statusPagesList, setStatusPagesList] = React.useState(
2294
- []
2295
- );
2296
- const [isLoading, setIsLoading] = React.useState(false);
2297
- const fetchStatusPagesCall = async () => {
2298
- try {
2299
- setIsLoading(true);
2300
- const opts = {};
2301
- if (paging) {
2302
- opts.maxResults = tableState.pageSize;
2303
- opts.startIndex = tableState.page * tableState.pageSize;
2304
- }
2305
- const data = await ilertApi.fetchStatusPages(opts);
2306
- setStatusPagesList(data || []);
2307
- setIsLoading(false);
2308
- } catch (e) {
2309
- if (!(e instanceof AuthenticationError)) {
2310
- errorApi.post(e);
2311
- }
2312
- setIsLoading(false);
2313
- throw e;
2314
- }
2315
- };
2316
- const fetchStatusPages = useAsyncRetry(fetchStatusPagesCall, [tableState]);
2317
- const refetchStatusPages = () => {
2318
- setTableState({ ...tableState, page: 0 });
2319
- Promise.all([fetchStatusPagesCall()]);
2320
- };
2321
- const error = fetchStatusPages.error;
2322
- const retry = () => {
2323
- fetchStatusPages.retry();
2324
- };
2325
- const onChangePage = (page) => {
2326
- setTableState({ ...tableState, page });
2327
- };
2328
- const onChangeRowsPerPage = (p) => {
2329
- setTableState({ ...tableState, pageSize: p });
2330
- };
2331
- return [
2332
- {
2333
- tableState,
2334
- statusPages: statusPagesList,
2335
- isLoading,
2336
- error
2337
- },
2338
- {
2339
- setTableState,
2340
- setStatusPagesList,
2341
- setIsLoading,
2342
- retry,
2343
- refetchStatusPages,
2344
- onChangePage,
2345
- onChangeRowsPerPage
2346
- }
2347
- ];
2348
- };
2349
-
2350
- const PrivateChip = withStyles((theme) => ({
2351
- root: {
2352
- backgroundColor: "#4caf50",
2353
- color: theme.palette.common.white,
2354
- margin: 0
2355
- }
2356
- }))(Chip);
2357
- const PublicChip = withStyles((theme) => ({
2358
- root: {
2359
- backgroundColor: "#ffb74d",
2360
- color: theme.palette.common.white,
2361
- margin: 0
2362
- }
2363
- }))(Chip);
2364
- const statusPageVisibilityLabels = {
2365
- [PUBLIC]: "Public",
2366
- [PRIVATE]: "Private"
2367
- };
2368
- const VisibilityChip = ({ statusPage }) => {
2369
- const label = `${statusPageVisibilityLabels[statusPage.visibility]}`;
2370
- switch (statusPage.visibility) {
2371
- case PRIVATE:
2372
- return /* @__PURE__ */ React.createElement(PrivateChip, { label, size: "small" });
2373
- case PUBLIC:
2374
- return /* @__PURE__ */ React.createElement(PublicChip, { label, size: "small" });
2375
- default:
2376
- return /* @__PURE__ */ React.createElement(Chip, { label, size: "small" });
2377
- }
2378
- };
2379
-
2380
- const StatusPageActionsMenu = ({
2381
- statusPage
2382
- }) => {
2383
- const ilertApi = useApi(ilertApiRef);
2384
- const [anchorEl, setAnchorEl] = React.useState(null);
2385
- const handleClick = (event) => {
2386
- setAnchorEl(event.currentTarget);
2387
- };
2388
- const handleCloseMenu = () => {
2389
- setAnchorEl(null);
2390
- };
2391
- return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(
2392
- IconButton,
2393
- {
2394
- "aria-label": "more",
2395
- "aria-controls": "long-menu",
2396
- "aria-haspopup": "true",
2397
- onClick: handleClick,
2398
- size: "small"
2399
- },
2400
- /* @__PURE__ */ React.createElement(MoreVertIcon, null)
2401
- ), /* @__PURE__ */ React.createElement(
2402
- Menu,
2403
- {
2404
- id: `statusPage-actions-menu-${statusPage.id}`,
2405
- anchorEl,
2406
- keepMounted: true,
2407
- open: Boolean(anchorEl),
2408
- onClose: handleCloseMenu,
2409
- PaperProps: {
2410
- style: { maxHeight: 48 * 5.5 }
2411
- }
2412
- },
2413
- /* @__PURE__ */ React.createElement(MenuItem, { key: "details", onClick: handleCloseMenu }, /* @__PURE__ */ React.createElement(Typography, { variant: "inherit", noWrap: true }, /* @__PURE__ */ React.createElement(Link, { to: ilertApi.getStatusPageDetailsURL(statusPage) }, "View in iLert"))),
2414
- /* @__PURE__ */ React.createElement(MenuItem, null, /* @__PURE__ */ React.createElement(Typography, { variant: "inherit", noWrap: true }, /* @__PURE__ */ React.createElement(Link, { to: `https://${ilertApi.getStatusPageURL(statusPage)}` }, "View status page")))
2415
- ));
2416
- };
2417
-
2418
- const StatusPageLink = ({
2419
- statusPage
2420
- }) => {
2421
- const ilertApi = useApi(ilertApiRef);
2422
- if (!statusPage) {
2423
- return null;
2424
- }
2425
- return /* @__PURE__ */ React.createElement(Link, { to: ilertApi.getStatusPageDetailsURL(statusPage) }, "#", statusPage.id);
2426
- };
2427
-
2428
- const StatusPageURL = ({
2429
- statusPage
2430
- }) => {
2431
- const ilertApi = useApi(ilertApiRef);
2432
- if (!statusPage) {
2433
- return null;
2434
- }
2435
- const url = ilertApi.getStatusPageURL(statusPage);
2436
- return /* @__PURE__ */ React.createElement(Link, { to: `https://${url}` }, url);
2437
- };
2438
-
2439
- const OperationalChip = withStyles((theme) => ({
2440
- root: {
2441
- backgroundColor: "#388E3D",
2442
- color: theme.palette.common.white,
2443
- margin: 0
2444
- }
2445
- }))(Chip);
2446
- const UnderMaintenanceChip = withStyles((theme) => ({
2447
- root: {
2448
- backgroundColor: "#616161",
2449
- color: theme.palette.common.white,
2450
- margin: 0
2451
- }
2452
- }))(Chip);
2453
- const DegradedChip = withStyles((theme) => ({
2454
- root: {
2455
- backgroundColor: "#FBC02D",
2456
- color: theme.palette.common.white,
2457
- margin: 0
2458
- }
2459
- }))(Chip);
2460
- const PartialOutageChip = withStyles((theme) => ({
2461
- root: {
2462
- backgroundColor: "#F57C02",
2463
- color: theme.palette.common.white,
2464
- margin: 0
2465
- }
2466
- }))(Chip);
2467
- const MajorOutageChip = withStyles((theme) => ({
2468
- root: {
2469
- backgroundColor: "#D22F2E",
2470
- color: theme.palette.common.white,
2471
- margin: 0
2472
- }
2473
- }))(Chip);
2474
- const statusPageStatusLabels = {
2475
- [OPERATIONAL]: "Operational",
2476
- [UNDER_MAINTENANCE]: "Under maintenance",
2477
- [DEGRADED]: "Degraded",
2478
- [PARTIAL_OUTAGE]: "Partial outage",
2479
- [MAJOR_OUTAGE]: "Major outage"
2480
- };
2481
- const StatusChip = ({ statusPage }) => {
2482
- const label = `${statusPageStatusLabels[statusPage.status]}`;
2483
- switch (statusPage.status) {
2484
- case OPERATIONAL:
2485
- return /* @__PURE__ */ React.createElement(OperationalChip, { label, size: "small" });
2486
- case UNDER_MAINTENANCE:
2487
- return /* @__PURE__ */ React.createElement(UnderMaintenanceChip, { label, size: "small" });
2488
- case DEGRADED:
2489
- return /* @__PURE__ */ React.createElement(DegradedChip, { label, size: "small" });
2490
- case PARTIAL_OUTAGE:
2491
- return /* @__PURE__ */ React.createElement(PartialOutageChip, { label, size: "small" });
2492
- case MAJOR_OUTAGE:
2493
- return /* @__PURE__ */ React.createElement(MajorOutageChip, { label, size: "small" });
2494
- default:
2495
- return /* @__PURE__ */ React.createElement(Chip, { label, size: "small" });
2496
- }
2497
- };
2498
-
2499
- const useStyles$5 = makeStyles((theme) => ({
2500
- empty: {
2501
- padding: theme.spacing(2),
2502
- display: "flex",
2503
- justifyContent: "center"
2504
- }
2505
- }));
2506
- const StatusPagesTable = ({
2507
- statusPages,
2508
- tableState,
2509
- isLoading,
2510
- onChangePage,
2511
- onChangeRowsPerPage,
2512
- compact
2513
- }) => {
2514
- const classes = useStyles$5();
2515
- const smColumnStyle = {
2516
- width: "10%",
2517
- maxWidth: "10%"
2518
- };
2519
- const xlColumnStyle = {
2520
- width: "30%",
2521
- maxWidth: "30%"
2522
- };
2523
- const idColumn = {
2524
- title: "ID",
2525
- field: "id",
2526
- highlight: true,
2527
- cellStyle: smColumnStyle,
2528
- headerStyle: smColumnStyle,
2529
- render: (rowData) => /* @__PURE__ */ React.createElement(StatusPageLink, { statusPage: rowData })
2530
- };
2531
- const nameColumn = {
2532
- title: "Name",
2533
- field: "name",
2534
- cellStyle: !compact ? xlColumnStyle : void 0,
2535
- headerStyle: !compact ? xlColumnStyle : void 0,
2536
- render: (rowData) => /* @__PURE__ */ React.createElement(Typography, null, rowData.name)
2537
- };
2538
- const urlColumn = {
2539
- title: "URL",
2540
- field: "url",
2541
- cellStyle: smColumnStyle,
2542
- headerStyle: smColumnStyle,
2543
- render: (rowData) => /* @__PURE__ */ React.createElement(StatusPageURL, { statusPage: rowData })
2544
- };
2545
- const visibilityColumn = {
2546
- title: "Visibility",
2547
- field: "visibility",
2548
- cellStyle: smColumnStyle,
2549
- headerStyle: smColumnStyle,
2550
- render: (rowData) => /* @__PURE__ */ React.createElement(VisibilityChip, { statusPage: rowData })
2551
- };
2552
- const statusColumn = {
2553
- title: "Status",
2554
- field: "status",
2555
- cellStyle: smColumnStyle,
2556
- headerStyle: smColumnStyle,
2557
- render: (rowData) => /* @__PURE__ */ React.createElement(StatusChip, { statusPage: rowData })
2558
- };
2559
- const actionsColumn = {
2560
- title: "",
2561
- field: "",
2562
- cellStyle: smColumnStyle,
2563
- headerStyle: smColumnStyle,
2564
- render: (rowData) => /* @__PURE__ */ React.createElement(StatusPageActionsMenu, { statusPage: rowData })
2565
- };
2566
- const columns = compact ? [nameColumn, statusColumn, urlColumn, actionsColumn] : [
2567
- idColumn,
2568
- nameColumn,
2569
- statusColumn,
2570
- urlColumn,
2571
- visibilityColumn,
2572
- actionsColumn
2573
- ];
2574
- return /* @__PURE__ */ React.createElement(
2575
- Table,
2576
- {
2577
- options: {
2578
- sorting: false,
2579
- search: !compact,
2580
- paging: !compact,
2581
- actionsColumnIndex: -1,
2582
- pageSize: tableState.pageSize,
2583
- pageSizeOptions: !compact ? [10, 20, 50, 100] : [3, 10, 20, 50, 100],
2584
- padding: "dense",
2585
- loadingType: "overlay",
2586
- showEmptyDataSourceMessage: !isLoading,
2587
- showTitle: true,
2588
- toolbar: true
2589
- },
2590
- emptyContent: /* @__PURE__ */ React.createElement(Typography, { color: "textSecondary", className: classes.empty }, "No status pages"),
2591
- title: /* @__PURE__ */ React.createElement(Typography, { variant: "button", color: "textSecondary" }, "STATUS PAGES"),
2592
- page: tableState.page,
2593
- onPageChange: onChangePage,
2594
- onRowsPerPageChange: onChangeRowsPerPage,
2595
- columns,
2596
- data: statusPages,
2597
- isLoading
2598
- }
2599
- );
2600
- };
2601
-
2602
- const StatusPagesPage = () => {
2603
- const [
2604
- { tableState, statusPages, isLoading, error },
2605
- { onChangePage, onChangeRowsPerPage, setIsLoading }
2606
- ] = useStatusPages(true);
2607
- if (error) {
2608
- if (error.name === "AuthenticationError") {
2609
- return /* @__PURE__ */ React.createElement(Content, null, /* @__PURE__ */ React.createElement(MissingAuthorizationHeaderError, null));
2610
- }
2611
- return /* @__PURE__ */ React.createElement(Content, null, /* @__PURE__ */ React.createElement(ResponseErrorPanel, { error }));
2612
- }
2613
- return /* @__PURE__ */ React.createElement(Content, null, /* @__PURE__ */ React.createElement(ContentHeader, { title: "Status pages" }, /* @__PURE__ */ React.createElement(SupportButton, null, "This helps you to bring iLert into your developer portal.")), /* @__PURE__ */ React.createElement(
2614
- StatusPagesTable,
2615
- {
2616
- statusPages,
2617
- tableState,
2618
- onChangePage,
2619
- onChangeRowsPerPage,
2620
- isLoading,
2621
- setIsLoading
2622
- }
2623
- ));
2624
- };
2625
-
2626
- const ILertPage = () => {
2627
- const [selectedTab, setSelectedTab] = React.useState(0);
2628
- const tabs = [
2629
- { label: "Who is on call?" },
2630
- { label: "Alerts" },
2631
- { label: "Services" },
2632
- { label: "Status pages" }
2633
- ];
2634
- const renderTab = () => {
2635
- switch (selectedTab) {
2636
- case 0:
2637
- return /* @__PURE__ */ React.createElement(OnCallSchedulesPage, null);
2638
- case 1:
2639
- return /* @__PURE__ */ React.createElement(AlertsPage, null);
2640
- case 2:
2641
- return /* @__PURE__ */ React.createElement(ServicesPage, null);
2642
- case 3:
2643
- return /* @__PURE__ */ React.createElement(StatusPagesPage, null);
2644
- default:
2645
- return null;
2646
- }
2647
- };
2648
- return /* @__PURE__ */ React.createElement(Page, { themeId: "website" }, /* @__PURE__ */ React.createElement(Header, { title: "iLert", type: "tool" }, /* @__PURE__ */ React.createElement(HeaderLabel, { label: "Owner", value: "iLert" }), /* @__PURE__ */ React.createElement(HeaderLabel, { label: "Lifecycle", value: "Alpha" })), /* @__PURE__ */ React.createElement(
2649
- HeaderTabs,
2650
- {
2651
- selectedIndex: selectedTab,
2652
- onChange: (index) => setSelectedTab(index),
2653
- tabs: tabs.map(({ label }, index) => ({
2654
- id: index.toString(),
2655
- label
2656
- }))
2657
- }
2658
- ), /* @__PURE__ */ React.createElement(Content, { noPadding: true }, renderTab()));
2659
- };
2660
-
2661
- const ILERT_INTEGRATION_KEY_ANNOTATION = "ilert.com/integration-key";
2662
-
2663
- function useILertEntity() {
2664
- var _a;
2665
- const { entity } = useEntity();
2666
- const integrationKey = ((_a = entity.metadata.annotations) == null ? void 0 : _a[ILERT_INTEGRATION_KEY_ANNOTATION]) || "";
2667
- const name = entity.metadata.name;
2668
- const identifier = `${entity.kind}:${entity.metadata.namespace || "default"}/${entity.metadata.name}`;
2669
- return { integrationKey, name, identifier };
2670
- }
2671
-
2672
- const useAlertSource = (integrationKey) => {
2673
- const ilertApi = useApi(ilertApiRef);
2674
- const errorApi = useApi(errorApiRef);
2675
- const [alertSource, setAlertSource] = React.useState(
2676
- null
2677
- );
2678
- const [isAlertSourceLoading, setIsAlertSourceLoading] = React.useState(false);
2679
- const fetchAlertSourceCall = async () => {
2680
- try {
2681
- if (!integrationKey) {
2682
- return;
2683
- }
2684
- setIsAlertSourceLoading(true);
2685
- const data = await ilertApi.fetchAlertSource(integrationKey);
2686
- setAlertSource(data || null);
2687
- setIsAlertSourceLoading(false);
2688
- } catch (e) {
2689
- setIsAlertSourceLoading(false);
2690
- if (!(e instanceof AuthenticationError)) {
2691
- errorApi.post(e);
2692
- }
2693
- throw e;
2694
- }
2695
- };
2696
- const { error: alertSourceError, retry: alertSourceRetry } = useAsyncRetry(
2697
- fetchAlertSourceCall,
2698
- [integrationKey]
2699
- );
2700
- const retry = () => alertSourceRetry();
2701
- return [
2702
- {
2703
- alertSource,
2704
- error: alertSourceError,
2705
- isLoading: isAlertSourceLoading
2706
- },
2707
- {
2708
- retry,
2709
- setIsLoading: setIsAlertSourceLoading,
2710
- refetchAlertSource: fetchAlertSourceCall,
2711
- setAlertSource
2712
- }
2713
- ];
2714
- };
2715
-
2716
- const ILertCardActionsHeader = ({
2717
- alertSource,
2718
- setAlertSource,
2719
- setIsNewAlertModalOpened,
2720
- setIsMaintenanceModalOpened
2721
- }) => {
2722
- const ilertApi = useApi(ilertApiRef);
2723
- const alertApi = useApi(alertApiRef);
2724
- const [isLoading, setIsLoading] = React.useState(false);
2725
- const [isDisableModalOpened, setIsDisableModalOpened] = React.useState(false);
2726
- const handleCreateNewAlert = () => {
2727
- setIsNewAlertModalOpened(true);
2728
- };
2729
- const handleEnableAlertSource = async () => {
2730
- try {
2731
- if (!alertSource) {
2732
- return;
2733
- }
2734
- setIsLoading(true);
2735
- const newAlertSource = await ilertApi.enableAlertSource(alertSource);
2736
- alertApi.post({ message: "Alert source enabled." });
2737
- setIsLoading(false);
2738
- setAlertSource(newAlertSource);
2739
- } catch (err) {
2740
- setIsLoading(false);
2741
- alertApi.post({ message: err, severity: "error" });
2742
- }
2743
- };
2744
- const handleDisableAlertSource = async () => {
2745
- try {
2746
- if (!alertSource) {
2747
- return;
2748
- }
2749
- setIsDisableModalOpened(false);
2750
- setIsLoading(true);
2751
- const newAlertSource = await ilertApi.disableAlertSource(alertSource);
2752
- alertApi.post({ message: "Alert source disabled." });
2753
- setIsLoading(false);
2754
- setAlertSource(newAlertSource);
2755
- } catch (err) {
2756
- setIsLoading(false);
2757
- alertApi.post({ message: err, severity: "error" });
2758
- }
2759
- };
2760
- const handleDisableAlertSourceWarningOpen = () => {
2761
- setIsDisableModalOpened(true);
2762
- };
2763
- const handleDisableAlertSourceWarningClose = () => {
2764
- setIsDisableModalOpened(false);
2765
- };
2766
- const handleMaintenanceAlertSource = () => {
2767
- setIsMaintenanceModalOpened(true);
2768
- };
2769
- const alertSourceLink = {
2770
- label: "Alert Source",
2771
- href: ilertApi.getAlertSourceDetailsURL(alertSource),
2772
- icon: /* @__PURE__ */ React.createElement(WebIcon, null)
2773
- };
2774
- const createAlertLink = {
2775
- label: "Create Alert",
2776
- onClick: handleCreateNewAlert,
2777
- icon: /* @__PURE__ */ React.createElement(AlarmAddIcon, null),
2778
- color: "secondary",
2779
- disabled: !alertSource || alertSource.status === "DISABLED" || alertSource.status === "IN_MAINTENANCE"
2780
- };
2781
- const enableAlertSourceLink = {
2782
- label: "Enable",
2783
- onClick: handleEnableAlertSource,
2784
- icon: /* @__PURE__ */ React.createElement(PlayArrowIcon, null),
2785
- disabled: !alertSource || isLoading
2786
- };
2787
- const disableAlertSourceLink = {
2788
- label: "Disable",
2789
- onClick: handleDisableAlertSourceWarningOpen,
2790
- icon: /* @__PURE__ */ React.createElement(PauseIcon, null),
2791
- disabled: !alertSource || isLoading
2792
- };
2793
- const maintenanceAlertSourceLink = {
2794
- label: "Immediate maintenance",
2795
- onClick: handleMaintenanceAlertSource,
2796
- icon: /* @__PURE__ */ React.createElement(BuildIcon, null),
2797
- disabled: !alertSource || isLoading
2798
- };
2799
- const links = [
2800
- alertSourceLink,
2801
- createAlertLink,
2802
- alertSource && alertSource.active ? disableAlertSourceLink : enableAlertSourceLink
2803
- ];
2804
- if (alertSource && alertSource.status !== "IN_MAINTENANCE") {
2805
- links.push(maintenanceAlertSourceLink);
2806
- }
2807
- return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(HeaderIconLinkRow, { links }), /* @__PURE__ */ React.createElement(
2808
- Dialog,
2809
- {
2810
- open: isDisableModalOpened,
2811
- onClose: handleDisableAlertSourceWarningClose,
2812
- "aria-labelledby": "alert-source-disable-form-title"
2813
- },
2814
- /* @__PURE__ */ React.createElement(DialogTitle, { id: "alert-source-disable-form-title" }, "Disable alert source"),
2815
- /* @__PURE__ */ React.createElement(DialogContent, null, /* @__PURE__ */ React.createElement(Alert, { severity: "info" }, /* @__PURE__ */ React.createElement(Typography, { variant: "body1", align: "justify" }, "Do you really want to disable this alert source? A disabled alert source cannot create new alerts."))),
2816
- /* @__PURE__ */ React.createElement(DialogActions, null, /* @__PURE__ */ React.createElement(
2817
- Button,
2818
- {
2819
- onClick: handleDisableAlertSource,
2820
- color: "secondary",
2821
- variant: "contained"
2822
- },
2823
- "Disable"
2824
- ), /* @__PURE__ */ React.createElement(
2825
- Button,
2826
- {
2827
- onClick: handleDisableAlertSourceWarningClose,
2828
- color: "primary"
2829
- },
2830
- "Cancel"
2831
- ))
2832
- ));
2833
- };
2834
-
2835
- const ENTITY_YAML = `apiVersion: backstage.io/v1alpha1
2836
- kind: Component
2837
- metadata:
2838
- name: example
2839
- description: example.com
2840
- annotations:
2841
- ilert.com/integration-key: [INTEGRATION_KEY]
2842
- spec:
2843
- type: website
2844
- lifecycle: production
2845
- owner: guest`;
2846
- const useStyles$4 = makeStyles((theme) => ({
2847
- code: {
2848
- borderRadius: 6,
2849
- margin: theme.spacing(2, 0),
2850
- background: theme.palette.type === "dark" ? "#444" : theme.palette.common.white
2851
- },
2852
- header: {
2853
- display: "inline-block",
2854
- padding: theme.spacing(2, 2, 2, 2.5)
2855
- }
2856
- }));
2857
- const ILertCardEmptyState = () => {
2858
- const classes = useStyles$4();
2859
- return /* @__PURE__ */ React.createElement(Card, { "data-testid": "ilert-empty-card" }, /* @__PURE__ */ React.createElement(CardHeader, { title: "iLert", className: classes.header }), /* @__PURE__ */ React.createElement(Divider, null), /* @__PURE__ */ React.createElement(CardContent, null, /* @__PURE__ */ React.createElement(Typography, { variant: "body1" }, "No integration key defined for this entity. You can add integration key to your entity YAML as shown in the highlighted example below:"), /* @__PURE__ */ React.createElement("div", { className: classes.code }, /* @__PURE__ */ React.createElement(
2860
- CodeSnippet,
2861
- {
2862
- text: ENTITY_YAML,
2863
- language: "yaml",
2864
- showLineNumbers: true,
2865
- highlightedNumbers: [6, 7],
2866
- customStyle: { background: "inherit", fontSize: "115%" }
2867
- }
2868
- )), /* @__PURE__ */ React.createElement(
2869
- Button,
2870
- {
2871
- variant: "contained",
2872
- color: "primary",
2873
- target: "_blank",
2874
- href: "https://github.com/backstage/backstage/blob/master/plugins/ilert/README.md"
2875
- },
2876
- "Read more"
2877
- )));
2878
- };
2879
-
2880
- const MaintenanceChip = withStyles((theme) => ({
2881
- root: {
2882
- backgroundColor: "#92949c",
2883
- color: theme.palette.common.white,
2884
- marginTop: 8
2885
- }
2886
- }))(Chip);
2887
- const ILertCardHeaderStatus = ({
2888
- alertSource
2889
- }) => {
2890
- if (!alertSource) {
2891
- return null;
2892
- }
2893
- switch (alertSource.status) {
2894
- case "IN_MAINTENANCE":
2895
- return /* @__PURE__ */ React.createElement(MaintenanceChip, { label: "MAINTENANCE", size: "small" });
2896
- case "DISABLED":
2897
- return /* @__PURE__ */ React.createElement(MaintenanceChip, { label: "INACTIVE", size: "small" });
2898
- default:
2899
- return null;
2900
- }
2901
- };
2902
-
2903
- const ILertCardMaintenanceModal = ({
2904
- alertSource,
2905
- refetchAlertSource,
2906
- isModalOpened,
2907
- setIsModalOpened
2908
- }) => {
2909
- const ilertApi = useApi(ilertApiRef);
2910
- const alertApi = useApi(alertApiRef);
2911
- const [minutes, setMinutes] = React.useState(5);
2912
- const handleClose = () => {
2913
- setIsModalOpened(false);
2914
- };
2915
- const handleImmediateMaintenance = () => {
2916
- if (!alertSource) {
2917
- return;
2918
- }
2919
- setIsModalOpened(false);
2920
- setTimeout(async () => {
2921
- try {
2922
- await ilertApi.addImmediateMaintenance(alertSource.id, minutes);
2923
- alertApi.post({ message: "Maintenance started." });
2924
- refetchAlertSource();
2925
- } catch (err) {
2926
- alertApi.post({ message: err, severity: "error" });
2927
- }
2928
- }, 250);
2929
- };
2930
- const handleMinutesChange = (event) => {
2931
- setMinutes(event.target.value);
2932
- };
2933
- const minuteOptions = [
2934
- {
2935
- value: 5,
2936
- label: "5 minutes"
2937
- },
2938
- {
2939
- value: 10,
2940
- label: "10 minutes"
2941
- },
2942
- {
2943
- value: 15,
2944
- label: "15 minutes"
2945
- },
2946
- {
2947
- value: 30,
2948
- label: "30 minutes"
2949
- },
2950
- {
2951
- value: 60,
2952
- label: "60 minutes"
2953
- }
2954
- ];
2955
- if (!alertSource) {
2956
- return null;
2957
- }
2958
- return /* @__PURE__ */ React.createElement(
2959
- Dialog,
2960
- {
2961
- open: isModalOpened,
2962
- onClose: handleClose,
2963
- "aria-labelledby": "maintenance-form-title"
2964
- },
2965
- /* @__PURE__ */ React.createElement(DialogTitle, { id: "maintenance-form-title" }, "New maintenance window"),
2966
- /* @__PURE__ */ React.createElement(DialogContent, null, /* @__PURE__ */ React.createElement(DialogContentText, null, "Keep your alert sources quiet, when your systems are under maintenance."), /* @__PURE__ */ React.createElement(
2967
- TextField,
2968
- {
2969
- select: true,
2970
- label: "Duration",
2971
- value: minutes,
2972
- onChange: handleMinutesChange,
2973
- variant: "outlined",
2974
- fullWidth: true
2975
- },
2976
- minuteOptions.map((option) => /* @__PURE__ */ React.createElement(MenuItem, { key: option.value, value: option.value }, option.label))
2977
- )),
2978
- /* @__PURE__ */ React.createElement(DialogActions, null, /* @__PURE__ */ React.createElement(
2979
- Button,
2980
- {
2981
- onClick: handleImmediateMaintenance,
2982
- color: "primary",
2983
- variant: "contained"
2984
- },
2985
- "Create"
2986
- ), /* @__PURE__ */ React.createElement(Button, { onClick: handleClose, color: "primary" }, "Cancel"))
2987
- );
2988
- };
2989
-
2990
- const useAlertSourceOnCalls = (alertSource) => {
2991
- const ilertApi = useApi(ilertApiRef);
2992
- const errorApi = useApi(errorApiRef);
2993
- const [onCallsList, setOnCallsList] = React.useState([]);
2994
- const [isLoading, setIsLoading] = React.useState(false);
2995
- const fetchAlertSourceOnCallsCall = async () => {
2996
- try {
2997
- if (!alertSource) {
2998
- return;
2999
- }
3000
- setIsLoading(true);
3001
- const data = await ilertApi.fetchAlertSourceOnCalls(alertSource);
3002
- setOnCallsList(data || []);
3003
- setIsLoading(false);
3004
- } catch (e) {
3005
- setIsLoading(false);
3006
- if (!(e instanceof AuthenticationError)) {
3007
- errorApi.post(e);
3008
- }
3009
- throw e;
3010
- }
3011
- };
3012
- const { error, retry } = useAsyncRetry(fetchAlertSourceOnCallsCall, [
3013
- alertSource
3014
- ]);
3015
- return [
3016
- {
3017
- onCalls: onCallsList,
3018
- error,
3019
- isLoading
3020
- },
3021
- {
3022
- retry,
3023
- setIsLoading,
3024
- refetchAlertSourceOnCalls: fetchAlertSourceOnCallsCall
3025
- }
3026
- ];
3027
- };
3028
-
3029
- const useStyles$3 = makeStyles({
3030
- text: {
3031
- fontStyle: "italic"
3032
- }
3033
- });
3034
- const ILertCardOnCallEmptyState = () => {
3035
- const classes = useStyles$3();
3036
- return /* @__PURE__ */ React.createElement(ListItem, null, /* @__PURE__ */ React.createElement(ListItemText, null, /* @__PURE__ */ React.createElement(
3037
- Typography,
3038
- {
3039
- variant: "subtitle1",
3040
- color: "textSecondary",
3041
- className: classes.text
3042
- },
3043
- "Nobody"
3044
- )));
3045
- };
3046
-
3047
- const useStyles$2 = makeStyles({
3048
- listItemPrimary: {
3049
- fontWeight: "bold"
3050
- },
3051
- fistItemLine: {
3052
- position: "absolute",
3053
- bottom: 0,
3054
- height: "50%",
3055
- left: 36
3056
- },
3057
- lastItemLine: {
3058
- position: "absolute",
3059
- top: 0,
3060
- height: "50%",
3061
- left: 36
3062
- },
3063
- itemLine: {
3064
- position: "absolute",
3065
- top: 0,
3066
- bottom: 0,
3067
- height: "100%",
3068
- left: 36
3069
- }
3070
- });
3071
- const ILertCardOnCallItem = ({
3072
- onCall,
3073
- fistItem = false,
3074
- lastItem = false
3075
- }) => {
3076
- const ilertApi = useApi(ilertApiRef);
3077
- const classes = useStyles$2();
3078
- if (!onCall || !onCall.user) {
3079
- return null;
3080
- }
3081
- const phoneNumber = ilertApi.getUserPhoneNumber(onCall.user);
3082
- const escalationRepeating = onCall.escalationPolicy.repeating;
3083
- const escalationSeconds = onCall.escalationPolicy.escalationRules[onCall.escalationLevel - 1].escalationTimeout;
3084
- const escalationHoursOnly = Math.floor(escalationSeconds / 60);
3085
- const escalationMinutesOnly = escalationSeconds % 60;
3086
- let escalationText = "";
3087
- if (!lastItem || lastItem && escalationRepeating) {
3088
- escalationText = "escalate";
3089
- if (escalationSeconds === 0) {
3090
- escalationText += " immediately";
3091
- } else {
3092
- escalationText += " after";
3093
- if (escalationHoursOnly > 0) {
3094
- escalationText += ` ${escalationHoursOnly} ${escalationHoursOnly === 1 ? "hour" : "hours"}`;
3095
- }
3096
- if (escalationMinutesOnly > 0 || escalationSeconds === 0) {
3097
- escalationText += ` ${escalationMinutesOnly} ${escalationMinutesOnly === 1 ? "minute" : "minutes"}`;
3098
- }
3099
- }
3100
- }
3101
- return /* @__PURE__ */ React.createElement(ListItem, null, fistItem ? /* @__PURE__ */ React.createElement(Divider, { orientation: "vertical", className: classes.fistItemLine }) : null, lastItem ? /* @__PURE__ */ React.createElement(Divider, { orientation: "vertical", className: classes.lastItemLine }) : null, !fistItem && !lastItem ? /* @__PURE__ */ React.createElement(Divider, { orientation: "vertical", className: classes.itemLine }) : null, /* @__PURE__ */ React.createElement(ListItemIcon, null, /* @__PURE__ */ React.createElement(
3102
- Tooltip,
3103
- {
3104
- title: `Escalation level #${onCall.escalationLevel}`,
3105
- placement: "top"
3106
- },
3107
- /* @__PURE__ */ React.createElement(Avatar, null, onCall.escalationLevel)
3108
- )), onCall.schedule ? /* @__PURE__ */ React.createElement(
3109
- Tooltip,
3110
- {
3111
- title: `On call shift ${DateTime.fromISO(onCall.start).toFormat("D MMM, HH:mm")} - ${DateTime.fromISO(onCall.end).toFormat("D MMM, HH:mm")}`,
3112
- placement: "top-start"
3113
- },
3114
- /* @__PURE__ */ React.createElement(
3115
- ListItemText,
3116
- {
3117
- primary: /* @__PURE__ */ React.createElement(Typography, { className: classes.listItemPrimary }, ilertApi.getUserInitials(onCall.user)),
3118
- secondary: escalationText
3119
- }
3120
- )
3121
- ) : /* @__PURE__ */ React.createElement(
3122
- ListItemText,
3123
- {
3124
- primary: /* @__PURE__ */ React.createElement(Typography, { className: classes.listItemPrimary }, ilertApi.getUserInitials(onCall.user)),
3125
- secondary: escalationText
3126
- }
3127
- ), /* @__PURE__ */ React.createElement(ListItemSecondaryAction, null, phoneNumber ? /* @__PURE__ */ React.createElement(Tooltip, { title: "Call to user", placement: "top" }, /* @__PURE__ */ React.createElement(IconButton, { href: `tel:${phoneNumber}` }, /* @__PURE__ */ React.createElement(PhoneIcon, { color: "secondary" }))) : null, /* @__PURE__ */ React.createElement(
3128
- Tooltip,
3129
- {
3130
- title: `Send e-mail to user ${onCall.user.email}`,
3131
- placement: "top"
3132
- },
3133
- /* @__PURE__ */ React.createElement(IconButton, { href: `mailto:${onCall.user.email}` }, /* @__PURE__ */ React.createElement(EmailIcon, { color: "primary" }))
3134
- )));
3135
- };
3136
-
3137
- const useStyles$1 = makeStyles((theme) => ({
3138
- repeatText: {
3139
- fontStyle: "italic"
3140
- },
3141
- repeatIcon: {
3142
- marginLeft: theme.spacing(1)
3143
- }
3144
- }));
3145
- const ILertCardOnCall = ({
3146
- alertSource
3147
- }) => {
3148
- const classes = useStyles$1();
3149
- const [{ onCalls, isLoading }, {}] = useAlertSourceOnCalls(alertSource);
3150
- if (isLoading) {
3151
- return /* @__PURE__ */ React.createElement(Progress, null);
3152
- }
3153
- if (!alertSource || !onCalls) {
3154
- return null;
3155
- }
3156
- const repeatInfo = () => {
3157
- if (!alertSource || !alertSource.escalationPolicy || !alertSource.escalationPolicy.repeating || !alertSource.escalationPolicy.frequency) {
3158
- return null;
3159
- }
3160
- return /* @__PURE__ */ React.createElement(ListItem, { key: "repeat" }, /* @__PURE__ */ React.createElement(ListItemIcon, null, /* @__PURE__ */ React.createElement(ReplayIcon, { className: classes.repeatIcon, fontSize: "small" })), /* @__PURE__ */ React.createElement(
3161
- ListItemText,
3162
- {
3163
- primary: /* @__PURE__ */ React.createElement(
3164
- Typography,
3165
- {
3166
- variant: "body2",
3167
- color: "textSecondary",
3168
- className: classes.repeatText
3169
- },
3170
- `Repeat ${alertSource.escalationPolicy.frequency} times`
3171
- )
3172
- }
3173
- ));
3174
- };
3175
- if (!onCalls.length) {
3176
- return /* @__PURE__ */ React.createElement(List, { dense: true, subheader: /* @__PURE__ */ React.createElement(ListSubheader, null, "ON CALL") }, /* @__PURE__ */ React.createElement(ILertCardOnCallEmptyState, null));
3177
- }
3178
- if (onCalls.length === 1) {
3179
- /* @__PURE__ */ React.createElement(List, { dense: true, subheader: /* @__PURE__ */ React.createElement(ListSubheader, null, "ON CALL") }, /* @__PURE__ */ React.createElement(
3180
- ILertCardOnCallItem,
3181
- {
3182
- onCall: onCalls[0],
3183
- fistItem: false,
3184
- lastItem: false
3185
- }
3186
- ));
3187
- }
3188
- return /* @__PURE__ */ React.createElement(List, { dense: true, subheader: /* @__PURE__ */ React.createElement(ListSubheader, null, "ON CALL") }, onCalls.map((onCall, index) => /* @__PURE__ */ React.createElement(
3189
- ILertCardOnCallItem,
3190
- {
3191
- key: index,
3192
- onCall,
3193
- fistItem: index === 0,
3194
- lastItem: index === onCalls.length - 1
3195
- }
3196
- )), repeatInfo());
3197
- };
3198
-
3199
- const isPluginApplicableToEntity = (entity) => {
3200
- var _a;
3201
- return Boolean((_a = entity.metadata.annotations) == null ? void 0 : _a[ILERT_INTEGRATION_KEY_ANNOTATION]);
3202
- };
3203
- const useStyles = makeStyles({
3204
- content: {
3205
- paddingLeft: "0 !important",
3206
- paddingRight: "0 !important",
3207
- paddingBottom: "0 !important",
3208
- paddingTop: "0 !important",
3209
- "& div div": {
3210
- boxShadow: "none !important"
3211
- }
3212
- }
3213
- });
3214
- const ILertCard = () => {
3215
- const classes = useStyles();
3216
- const { integrationKey, name } = useILertEntity();
3217
- const [{ alertSource }, { setAlertSource, refetchAlertSource }] = useAlertSource(integrationKey);
3218
- const [
3219
- { tableState, states, alerts, alertsCount, isLoading, error },
3220
- {
3221
- onAlertStatesChange,
3222
- onChangePage,
3223
- onChangeRowsPerPage,
3224
- onAlertChanged,
3225
- refetchAlerts,
3226
- setIsLoading
3227
- }
3228
- ] = useAlerts(false, true, alertSource);
3229
- const [isNewAlertModalOpened, setIsNewAlertModalOpened] = React.useState(false);
3230
- const [isMaintenanceModalOpened, setIsMaintenanceModalOpened] = React.useState(false);
3231
- if (error) {
3232
- if (error instanceof AuthenticationError) {
3233
- return /* @__PURE__ */ React.createElement(MissingAuthorizationHeaderError, null);
3234
- }
3235
- return /* @__PURE__ */ React.createElement(ResponseErrorPanel, { error });
3236
- }
3237
- if (!integrationKey) {
3238
- return /* @__PURE__ */ React.createElement(ILertCardEmptyState, null);
3239
- }
3240
- return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(Card, { "data-testid": "ilert-card" }, /* @__PURE__ */ React.createElement(
3241
- CardHeader,
3242
- {
3243
- title: "iLert",
3244
- subheader: /* @__PURE__ */ React.createElement(
3245
- ILertCardActionsHeader,
3246
- {
3247
- alertSource,
3248
- setAlertSource,
3249
- setIsNewAlertModalOpened,
3250
- setIsMaintenanceModalOpened
3251
- }
3252
- ),
3253
- action: /* @__PURE__ */ React.createElement(ILertCardHeaderStatus, { alertSource })
3254
- }
3255
- ), /* @__PURE__ */ React.createElement(Divider, null), /* @__PURE__ */ React.createElement(CardContent, { className: classes.content }, /* @__PURE__ */ React.createElement(ILertCardOnCall, { alertSource }), /* @__PURE__ */ React.createElement(
3256
- AlertsTable,
3257
- {
3258
- alerts,
3259
- alertsCount,
3260
- tableState,
3261
- states,
3262
- onAlertChanged,
3263
- onAlertStatesChange,
3264
- onChangePage,
3265
- onChangeRowsPerPage,
3266
- isLoading,
3267
- setIsLoading,
3268
- compact: true
3269
- }
3270
- ))), /* @__PURE__ */ React.createElement(
3271
- AlertNewModal,
3272
- {
3273
- isModalOpened: isNewAlertModalOpened,
3274
- setIsModalOpened: setIsNewAlertModalOpened,
3275
- refetchAlerts,
3276
- initialAlertSource: alertSource,
3277
- entityName: name
3278
- }
3279
- ), /* @__PURE__ */ React.createElement(
3280
- ILertCardMaintenanceModal,
3281
- {
3282
- alertSource,
3283
- refetchAlertSource,
3284
- isModalOpened: isMaintenanceModalOpened,
3285
- setIsModalOpened: setIsMaintenanceModalOpened
3286
- }
3287
- ));
3288
- };
3289
-
3290
- const ILertIcon = () => /* @__PURE__ */ React.createElement(SvgIcon, null, /* @__PURE__ */ React.createElement("g", null, /* @__PURE__ */ React.createElement(
3291
- "path",
3292
- {
3293
- stroke: "none",
3294
- fillRule: "nonzero",
3295
- fill: "#fff",
3296
- fillOpacity: "1",
3297
- d: "M 0 9.277344 L 0 18.574219 C 0 20.832031 1.847656 22.679688 4.105469 22.679688 L 12.582031 22.679688 C 6.21875 21.007812 1.277344 15.792969 0 9.277344 Z M 0 9.277344"
3298
- }
3299
- ), /* @__PURE__ */ React.createElement(
3300
- "path",
3301
- {
3302
- stroke: "none",
3303
- fillRule: "nonzero",
3304
- fill: "#fff",
3305
- fillOpacity: "1",
3306
- d: "M 19.527344 22.5625 C 21.328125 22.128906 22.679688 20.503906 22.679688 18.574219 L 22.679688 16.273438 C 21.691406 16.820312 20.632812 17.222656 19.527344 17.46875 Z M 19.527344 22.5625"
3307
- }
3308
- ), /* @__PURE__ */ React.createElement(
3309
- "path",
3310
- {
3311
- stroke: "none",
3312
- fillRule: "nonzero",
3313
- fill: "#fff",
3314
- fillOpacity: "1",
3315
- d: "M 14.417969 17.46875 C 9.136719 16.296875 5.171875 11.578125 5.171875 5.945312 C 5.167969 3.855469 5.726562 1.804688 6.785156 0 L 4.105469 0 C 1.847656 0 0 1.847656 0 4.105469 L 0 6.882812 C 0.433594 14.816406 6.335938 21.316406 13.992188 22.679688 L 14.421875 22.679688 Z M 14.417969 17.46875"
3316
- }
3317
- ), /* @__PURE__ */ React.createElement(
3318
- "path",
3319
- {
3320
- stroke: "none",
3321
- fillRule: "nonzero",
3322
- fill: "#fff",
3323
- fillOpacity: "1",
3324
- d: "M 19.527344 12.375 L 19.527344 17.160156 C 20.632812 16.910156 21.695312 16.496094 22.679688 15.929688 L 22.679688 9.855469 C 21.902344 10.988281 20.804688 11.863281 19.527344 12.375 Z M 19.527344 12.375"
3325
- }
3326
- ), /* @__PURE__ */ React.createElement(
3327
- "path",
3328
- {
3329
- stroke: "none",
3330
- fillRule: "nonzero",
3331
- fill: "#fff",
3332
- fillOpacity: "1",
3333
- d: "M 14.417969 17.160156 L 14.417969 12.375 C 11.863281 11.355469 10.054688 8.859375 10.054688 5.945312 C 10.054688 3.503906 11.34375 1.246094 13.441406 0 L 7.128906 0 C 6.039062 1.792969 5.464844 3.847656 5.46875 5.945312 C 5.46875 11.410156 9.300781 15.996094 14.417969 17.160156 Z M 14.417969 17.160156"
3334
- }
3335
- ), /* @__PURE__ */ React.createElement(
3336
- "path",
3337
- {
3338
- stroke: "none",
3339
- fillRule: "nonzero",
3340
- fill: "#fff",
3341
- fillOpacity: "1",
3342
- d: "M 10.355469 5.945312 C 10.355469 8.613281 11.957031 11.019531 14.417969 12.050781 L 14.417969 9.917969 C 15.972656 10.925781 17.972656 10.925781 19.527344 9.917969 L 19.527344 12.050781 C 20.847656 11.496094 21.953125 10.53125 22.679688 9.300781 L 22.679688 4.105469 C 22.679688 1.847656 20.832031 0 18.574219 0 L 14.066406 0 C 11.796875 1.113281 10.355469 3.417969 10.355469 5.945312 Z M 16.972656 2.5625 C 18.84375 2.5625 20.355469 4.078125 20.355469 5.945312 C 20.355469 7.8125 18.84375 9.328125 16.972656 9.328125 C 15.105469 9.328125 13.589844 7.8125 13.589844 5.945312 C 13.59375 4.078125 15.105469 2.566406 16.972656 2.5625 Z M 16.972656 2.5625"
3343
- }
3344
- )));
3345
-
3346
- export { ACCEPTED, DEGRADED, EntityILertCard, ILertCard, ILertClient, ILertIcon, ILertPage$1 as ILertPage, MAJOR_OUTAGE, OPERATIONAL, PARTIAL_OUTAGE, PENDING, PRIVATE, PUBLIC, RESOLVED, ILertPage as Router, UNDER_MAINTENANCE, iLertRouteRef, ilertApiRef, ilertPlugin, isPluginApplicableToEntity as isILertAvailable, isPluginApplicableToEntity, ilertPlugin as plugin };
1
+ export { EntityILertCard, ILertPage, ilertPlugin, ilertPlugin as plugin } from './plugin.esm.js';
2
+ export { ILertPage as Router } from './components/ILertPage/ILertPage.esm.js';
3
+ export { ILertCard, isPluginApplicableToEntity as isILertAvailable, isPluginApplicableToEntity } from './components/ILertCard/ILertCard.esm.js';
4
+ export { ILertIcon } from './ILertIcon.esm.js';
5
+ export { ILertClient, ilertApiRef } from './api/client.esm.js';
6
+ export { iLertRouteRef } from './route-refs.esm.js';
7
+ export { ACCEPTED, DEGRADED, MAJOR_OUTAGE, OPERATIONAL, PARTIAL_OUTAGE, PENDING, PRIVATE, PUBLIC, RESOLVED, UNDER_MAINTENANCE } from './types.esm.js';
3347
8
  //# sourceMappingURL=index.esm.js.map