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