@backstage-community/plugin-ilert 0.3.0 → 0.3.2
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 +12 -0
- package/dist/ILertIcon.esm.js +61 -0
- package/dist/ILertIcon.esm.js.map +1 -0
- package/dist/api/client.esm.js +395 -0
- package/dist/api/client.esm.js.map +1 -0
- package/dist/components/Alert/AlertActionsMenu.esm.js +142 -0
- package/dist/components/Alert/AlertActionsMenu.esm.js.map +1 -0
- package/dist/components/Alert/AlertAssignModal.esm.js +147 -0
- package/dist/components/Alert/AlertAssignModal.esm.js.map +1 -0
- package/dist/components/Alert/AlertLink.esm.js +15 -0
- package/dist/components/Alert/AlertLink.esm.js.map +1 -0
- package/dist/components/Alert/AlertNewModal.esm.js +192 -0
- package/dist/components/Alert/AlertNewModal.esm.js.map +1 -0
- package/dist/components/AlertSource/AlertSourceLink.esm.js +49 -0
- package/dist/components/AlertSource/AlertSourceLink.esm.js.map +1 -0
- package/dist/components/AlertsPage/AlertsPage.esm.js +67 -0
- package/dist/components/AlertsPage/AlertsPage.esm.js.map +1 -0
- package/dist/components/AlertsPage/AlertsTable.esm.js +185 -0
- package/dist/components/AlertsPage/AlertsTable.esm.js.map +1 -0
- package/dist/components/AlertsPage/StatusChip.esm.js +47 -0
- package/dist/components/AlertsPage/StatusChip.esm.js.map +1 -0
- package/dist/components/AlertsPage/TableTitle.esm.js +74 -0
- package/dist/components/AlertsPage/TableTitle.esm.js.map +1 -0
- package/dist/components/Errors/MissingAuthorizationHeaderError.esm.js +24 -0
- package/dist/components/Errors/MissingAuthorizationHeaderError.esm.js.map +1 -0
- package/dist/components/ILertCard/ILertCard.esm.js +113 -0
- package/dist/components/ILertCard/ILertCard.esm.js.map +1 -0
- package/dist/components/ILertCard/ILertCardActionsHeader.esm.js +138 -0
- package/dist/components/ILertCard/ILertCardActionsHeader.esm.js.map +1 -0
- package/dist/components/ILertCard/ILertCardEmptyState.esm.js +57 -0
- package/dist/components/ILertCard/ILertCardEmptyState.esm.js.map +1 -0
- package/dist/components/ILertCard/ILertCardHeaderStatus.esm.js +29 -0
- package/dist/components/ILertCard/ILertCardHeaderStatus.esm.js.map +1 -0
- package/dist/components/ILertCard/ILertCardMaintenanceModal.esm.js +101 -0
- package/dist/components/ILertCard/ILertCardMaintenanceModal.esm.js.map +1 -0
- package/dist/components/ILertCard/ILertCardOnCall.esm.js +78 -0
- package/dist/components/ILertCard/ILertCardOnCall.esm.js.map +1 -0
- package/dist/components/ILertCard/ILertCardOnCallEmptyState.esm.js +26 -0
- package/dist/components/ILertCard/ILertCardOnCallEmptyState.esm.js.map +1 -0
- package/dist/components/ILertCard/ILertCardOnCallItem.esm.js +109 -0
- package/dist/components/ILertCard/ILertCardOnCallItem.esm.js.map +1 -0
- package/dist/components/ILertCard/index.esm.js +2 -0
- package/dist/components/ILertCard/index.esm.js.map +1 -0
- package/dist/components/ILertPage/ILertPage.esm.js +48 -0
- package/dist/components/ILertPage/ILertPage.esm.js.map +1 -0
- package/dist/components/OnCallSchedulesPage/OnCallSchedulesGrid.esm.js +135 -0
- package/dist/components/OnCallSchedulesPage/OnCallSchedulesGrid.esm.js.map +1 -0
- package/dist/components/OnCallSchedulesPage/OnCallSchedulesPage.esm.js +27 -0
- package/dist/components/OnCallSchedulesPage/OnCallSchedulesPage.esm.js.map +1 -0
- package/dist/components/OnCallSchedulesPage/OnCallShiftItem.esm.js +63 -0
- package/dist/components/OnCallSchedulesPage/OnCallShiftItem.esm.js.map +1 -0
- package/dist/components/Service/ServiceActionsMenu.esm.js +47 -0
- package/dist/components/Service/ServiceActionsMenu.esm.js.map +1 -0
- package/dist/components/Service/ServiceLink.esm.js +15 -0
- package/dist/components/Service/ServiceLink.esm.js.map +1 -0
- package/dist/components/ServicesPage/ServicesPage.esm.js +32 -0
- package/dist/components/ServicesPage/ServicesPage.esm.js.map +1 -0
- package/dist/components/ServicesPage/ServicesTable.esm.js +99 -0
- package/dist/components/ServicesPage/ServicesTable.esm.js.map +1 -0
- package/dist/components/ServicesPage/StatusChip.esm.js +67 -0
- package/dist/components/ServicesPage/StatusChip.esm.js.map +1 -0
- package/dist/components/Shift/ShiftOverrideModal.esm.js +171 -0
- package/dist/components/Shift/ShiftOverrideModal.esm.js.map +1 -0
- package/dist/components/StatusPage/StatusPageActionsMenu.esm.js +50 -0
- package/dist/components/StatusPage/StatusPageActionsMenu.esm.js.map +1 -0
- package/dist/components/StatusPage/StatusPageLink.esm.js +17 -0
- package/dist/components/StatusPage/StatusPageLink.esm.js.map +1 -0
- package/dist/components/StatusPage/StatusPageURL.esm.js +18 -0
- package/dist/components/StatusPage/StatusPageURL.esm.js.map +1 -0
- package/dist/components/StatusPagePage/StatusChip.esm.js +67 -0
- package/dist/components/StatusPagePage/StatusChip.esm.js.map +1 -0
- package/dist/components/StatusPagePage/StatusPagesPage.esm.js +32 -0
- package/dist/components/StatusPagePage/StatusPagesPage.esm.js.map +1 -0
- package/dist/components/StatusPagePage/StatusPagesTable.esm.js +115 -0
- package/dist/components/StatusPagePage/StatusPagesTable.esm.js.map +1 -0
- package/dist/components/StatusPagePage/VisibilityChip.esm.js +37 -0
- package/dist/components/StatusPagePage/VisibilityChip.esm.js.map +1 -0
- package/dist/components/index.esm.js +3 -0
- package/dist/components/index.esm.js.map +1 -0
- package/dist/constants.esm.js +4 -0
- package/dist/constants.esm.js.map +1 -0
- package/dist/hooks/index.esm.js +13 -0
- package/dist/hooks/index.esm.js.map +1 -0
- package/dist/hooks/useAlertActions.esm.js +43 -0
- package/dist/hooks/useAlertActions.esm.js.map +1 -0
- package/dist/hooks/useAlertSource.esm.js +52 -0
- package/dist/hooks/useAlertSource.esm.js.map +1 -0
- package/dist/hooks/useAlertSourceOnCalls.esm.js +47 -0
- package/dist/hooks/useAlertSourceOnCalls.esm.js.map +1 -0
- package/dist/hooks/useAlerts.esm.js +128 -0
- package/dist/hooks/useAlerts.esm.js.map +1 -0
- package/dist/hooks/useAssignAlert.esm.js +53 -0
- package/dist/hooks/useAssignAlert.esm.js.map +1 -0
- package/dist/hooks/useNewAlert.esm.js +58 -0
- package/dist/hooks/useNewAlert.esm.js.map +1 -0
- package/dist/hooks/useOnCallSchedules.esm.js +42 -0
- package/dist/hooks/useOnCallSchedules.esm.js.map +1 -0
- package/dist/hooks/useServices.esm.js +70 -0
- package/dist/hooks/useServices.esm.js.map +1 -0
- package/dist/hooks/useShiftOverride.esm.js +60 -0
- package/dist/hooks/useShiftOverride.esm.js.map +1 -0
- package/dist/hooks/useStatusPages.esm.js +72 -0
- package/dist/hooks/useStatusPages.esm.js.map +1 -0
- package/dist/index.esm.js +7 -3346
- package/dist/index.esm.js.map +1 -1
- package/dist/plugin.esm.js +39 -0
- package/dist/plugin.esm.js.map +1 -0
- package/dist/route-refs.esm.js +8 -0
- package/dist/route-refs.esm.js.map +1 -0
- package/dist/types.esm.js +13 -0
- package/dist/types.esm.js.map +1 -0
- package/package.json +12 -8
- package/dist/esm/index-D2_Gjvjx.esm.js +0 -58
- package/dist/esm/index-D2_Gjvjx.esm.js.map +0 -1
- package/dist/esm/index-aory7UQN.esm.js +0 -58
- package/dist/esm/index-aory7UQN.esm.js.map +0 -1
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import IconButton from '@material-ui/core/IconButton';
|
|
2
|
+
import Menu from '@material-ui/core/Menu';
|
|
3
|
+
import MenuItem from '@material-ui/core/MenuItem';
|
|
4
|
+
import Typography from '@material-ui/core/Typography';
|
|
5
|
+
import MoreVertIcon from '@material-ui/icons/MoreVert';
|
|
6
|
+
import React from 'react';
|
|
7
|
+
import { ilertApiRef } from '../../api/client.esm.js';
|
|
8
|
+
import { useAlertActions } from '../../hooks/useAlertActions.esm.js';
|
|
9
|
+
import { AlertAssignModal } from './AlertAssignModal.esm.js';
|
|
10
|
+
import { parseEntityRef, DEFAULT_NAMESPACE } from '@backstage/catalog-model';
|
|
11
|
+
import { Progress, Link } from '@backstage/core-components';
|
|
12
|
+
import { useApi, alertApiRef, identityApiRef } from '@backstage/core-plugin-api';
|
|
13
|
+
|
|
14
|
+
const AlertActionsMenu = ({
|
|
15
|
+
alert,
|
|
16
|
+
onAlertChanged,
|
|
17
|
+
setIsLoading
|
|
18
|
+
}) => {
|
|
19
|
+
const ilertApi = useApi(ilertApiRef);
|
|
20
|
+
const alertApi = useApi(alertApiRef);
|
|
21
|
+
const identityApi = useApi(identityApiRef);
|
|
22
|
+
const [anchorEl, setAnchorEl] = React.useState(null);
|
|
23
|
+
const callback = onAlertChanged || ((_) => {
|
|
24
|
+
});
|
|
25
|
+
const setProcessing = setIsLoading || ((_) => {
|
|
26
|
+
});
|
|
27
|
+
const [isAssignAlertModalOpened, setIsAssignAlertModalOpened] = React.useState(false);
|
|
28
|
+
const [{ alertActions, isLoading }] = useAlertActions(
|
|
29
|
+
alert,
|
|
30
|
+
Boolean(anchorEl)
|
|
31
|
+
);
|
|
32
|
+
const handleClick = (event) => {
|
|
33
|
+
setAnchorEl(event.currentTarget);
|
|
34
|
+
};
|
|
35
|
+
const handleCloseMenu = () => {
|
|
36
|
+
setAnchorEl(null);
|
|
37
|
+
};
|
|
38
|
+
const handleAccept = async () => {
|
|
39
|
+
try {
|
|
40
|
+
handleCloseMenu();
|
|
41
|
+
setProcessing(true);
|
|
42
|
+
const { userEntityRef } = await identityApi.getBackstageIdentity();
|
|
43
|
+
const { name: userName } = parseEntityRef(userEntityRef, {
|
|
44
|
+
defaultKind: "User",
|
|
45
|
+
defaultNamespace: DEFAULT_NAMESPACE
|
|
46
|
+
});
|
|
47
|
+
const newAlert = await ilertApi.acceptAlert(alert, userName);
|
|
48
|
+
alertApi.post({ message: "Alert accepted." });
|
|
49
|
+
callback(newAlert);
|
|
50
|
+
setProcessing(false);
|
|
51
|
+
} catch (err) {
|
|
52
|
+
setProcessing(false);
|
|
53
|
+
alertApi.post({ message: err, severity: "error" });
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
const handleResolve = async () => {
|
|
57
|
+
try {
|
|
58
|
+
handleCloseMenu();
|
|
59
|
+
setProcessing(true);
|
|
60
|
+
const { userEntityRef } = await identityApi.getBackstageIdentity();
|
|
61
|
+
const { name: userName } = parseEntityRef(userEntityRef, {
|
|
62
|
+
defaultKind: "User",
|
|
63
|
+
defaultNamespace: DEFAULT_NAMESPACE
|
|
64
|
+
});
|
|
65
|
+
const newAlert = await ilertApi.resolveAlert(alert, userName);
|
|
66
|
+
alertApi.post({ message: "Alert resolved." });
|
|
67
|
+
callback(newAlert);
|
|
68
|
+
setProcessing(false);
|
|
69
|
+
} catch (err) {
|
|
70
|
+
setProcessing(false);
|
|
71
|
+
alertApi.post({ message: err, severity: "error" });
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
const handleAssign = () => {
|
|
75
|
+
handleCloseMenu();
|
|
76
|
+
setIsAssignAlertModalOpened(true);
|
|
77
|
+
};
|
|
78
|
+
const handleTriggerAction = (action) => async () => {
|
|
79
|
+
try {
|
|
80
|
+
handleCloseMenu();
|
|
81
|
+
setProcessing(true);
|
|
82
|
+
await ilertApi.triggerAlertAction(alert, action);
|
|
83
|
+
alertApi.post({ message: "Alert action triggered." });
|
|
84
|
+
setProcessing(false);
|
|
85
|
+
} catch (err) {
|
|
86
|
+
setProcessing(false);
|
|
87
|
+
alertApi.post({ message: err, severity: "error" });
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
const actions = alertActions.map((a) => {
|
|
91
|
+
const successTrigger = a.history ? a.history.find((h) => h.success) : void 0;
|
|
92
|
+
const triggeredBy = successTrigger && successTrigger.actor ? `${successTrigger.actor.firstName} ${successTrigger.actor.lastName}` : "";
|
|
93
|
+
return /* @__PURE__ */ React.createElement(
|
|
94
|
+
MenuItem,
|
|
95
|
+
{
|
|
96
|
+
key: a.webhookId,
|
|
97
|
+
onClick: handleTriggerAction(a),
|
|
98
|
+
disabled: !!successTrigger
|
|
99
|
+
},
|
|
100
|
+
/* @__PURE__ */ React.createElement(Typography, { variant: "inherit", noWrap: true }, triggeredBy ? `${a.name} (by ${triggeredBy})` : a.name)
|
|
101
|
+
);
|
|
102
|
+
});
|
|
103
|
+
return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(
|
|
104
|
+
IconButton,
|
|
105
|
+
{
|
|
106
|
+
"aria-label": "more",
|
|
107
|
+
"aria-controls": "long-menu",
|
|
108
|
+
"aria-haspopup": "true",
|
|
109
|
+
onClick: handleClick,
|
|
110
|
+
size: "small"
|
|
111
|
+
},
|
|
112
|
+
/* @__PURE__ */ React.createElement(MoreVertIcon, null)
|
|
113
|
+
), /* @__PURE__ */ React.createElement(
|
|
114
|
+
Menu,
|
|
115
|
+
{
|
|
116
|
+
id: `alert-actions-menu-${alert.id}`,
|
|
117
|
+
anchorEl,
|
|
118
|
+
keepMounted: true,
|
|
119
|
+
open: Boolean(anchorEl),
|
|
120
|
+
onClose: handleCloseMenu,
|
|
121
|
+
PaperProps: {
|
|
122
|
+
style: { maxHeight: 48 * 5.5 }
|
|
123
|
+
}
|
|
124
|
+
},
|
|
125
|
+
alert.status === "PENDING" ? /* @__PURE__ */ React.createElement(MenuItem, { key: "ack", onClick: handleAccept }, /* @__PURE__ */ React.createElement(Typography, { variant: "inherit", noWrap: true }, "Accept")) : null,
|
|
126
|
+
alert.status !== "RESOLVED" ? /* @__PURE__ */ React.createElement(MenuItem, { key: "close", onClick: handleResolve }, /* @__PURE__ */ React.createElement(Typography, { variant: "inherit", noWrap: true }, "Resolve")) : null,
|
|
127
|
+
alert.status !== "RESOLVED" ? /* @__PURE__ */ React.createElement(MenuItem, { key: "assign", onClick: handleAssign }, /* @__PURE__ */ React.createElement(Typography, { variant: "inherit", noWrap: true }, "Assign")) : null,
|
|
128
|
+
isLoading ? /* @__PURE__ */ React.createElement(MenuItem, { key: "loading" }, /* @__PURE__ */ React.createElement(Progress, { style: { width: "100%" } })) : actions,
|
|
129
|
+
/* @__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")))
|
|
130
|
+
), /* @__PURE__ */ React.createElement(
|
|
131
|
+
AlertAssignModal,
|
|
132
|
+
{
|
|
133
|
+
alert,
|
|
134
|
+
setIsModalOpened: setIsAssignAlertModalOpened,
|
|
135
|
+
isModalOpened: isAssignAlertModalOpened,
|
|
136
|
+
onAlertChanged
|
|
137
|
+
}
|
|
138
|
+
));
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
export { AlertActionsMenu };
|
|
142
|
+
//# sourceMappingURL=AlertActionsMenu.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AlertActionsMenu.esm.js","sources":["../../../src/components/Alert/AlertActionsMenu.tsx"],"sourcesContent":["/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport IconButton from '@material-ui/core/IconButton';\nimport Menu from '@material-ui/core/Menu';\nimport MenuItem from '@material-ui/core/MenuItem';\nimport Typography from '@material-ui/core/Typography';\nimport MoreVertIcon from '@material-ui/icons/MoreVert';\nimport React from 'react';\nimport { ilertApiRef } from '../../api';\nimport { useAlertActions } from '../../hooks/useAlertActions';\nimport { Alert, AlertAction } from '../../types';\nimport { AlertAssignModal } from './AlertAssignModal';\n\nimport { DEFAULT_NAMESPACE, parseEntityRef } from '@backstage/catalog-model';\nimport { Link, Progress } from '@backstage/core-components';\nimport {\n alertApiRef,\n identityApiRef,\n useApi,\n} from '@backstage/core-plugin-api';\n\nexport const AlertActionsMenu = ({\n alert,\n onAlertChanged,\n setIsLoading,\n}: {\n alert: Alert;\n onAlertChanged?: (alert: Alert) => void;\n setIsLoading?: (isLoading: boolean) => void;\n}) => {\n const ilertApi = useApi(ilertApiRef);\n const alertApi = useApi(alertApiRef);\n const identityApi = useApi(identityApiRef);\n const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);\n const callback = onAlertChanged || ((_: Alert): void => {});\n const setProcessing = setIsLoading || ((_: boolean): void => {});\n const [isAssignAlertModalOpened, setIsAssignAlertModalOpened] =\n React.useState(false);\n\n const [{ alertActions, isLoading }] = useAlertActions(\n alert,\n Boolean(anchorEl),\n );\n\n const handleClick = (event: React.MouseEvent<HTMLElement>) => {\n setAnchorEl(event.currentTarget);\n };\n\n const handleCloseMenu = () => {\n setAnchorEl(null);\n };\n\n const handleAccept = async (): Promise<void> => {\n try {\n handleCloseMenu();\n setProcessing(true);\n\n const { userEntityRef } = await identityApi.getBackstageIdentity();\n const { name: userName } = parseEntityRef(userEntityRef, {\n defaultKind: 'User',\n defaultNamespace: DEFAULT_NAMESPACE,\n });\n const newAlert = await ilertApi.acceptAlert(alert, userName);\n alertApi.post({ message: 'Alert accepted.' });\n\n callback(newAlert);\n setProcessing(false);\n } catch (err) {\n setProcessing(false);\n alertApi.post({ message: err, severity: 'error' });\n }\n };\n\n const handleResolve = async (): Promise<void> => {\n try {\n handleCloseMenu();\n setProcessing(true);\n const { userEntityRef } = await identityApi.getBackstageIdentity();\n const { name: userName } = parseEntityRef(userEntityRef, {\n defaultKind: 'User',\n defaultNamespace: DEFAULT_NAMESPACE,\n });\n const newAlert = await ilertApi.resolveAlert(alert, userName);\n alertApi.post({ message: 'Alert resolved.' });\n\n callback(newAlert);\n setProcessing(false);\n } catch (err) {\n setProcessing(false);\n alertApi.post({ message: err, severity: 'error' });\n }\n };\n\n const handleAssign = () => {\n handleCloseMenu();\n setIsAssignAlertModalOpened(true);\n };\n\n const handleTriggerAction = (action: AlertAction) => async () => {\n try {\n handleCloseMenu();\n setProcessing(true);\n await ilertApi.triggerAlertAction(alert, action);\n alertApi.post({ message: 'Alert action triggered.' });\n setProcessing(false);\n } catch (err) {\n setProcessing(false);\n alertApi.post({ message: err, severity: 'error' });\n }\n };\n\n const actions: React.ReactNode[] = alertActions.map(a => {\n const successTrigger = a.history\n ? a.history.find(h => h.success)\n : undefined;\n const triggeredBy =\n successTrigger && successTrigger.actor\n ? `${successTrigger.actor.firstName} ${successTrigger.actor.lastName}`\n : '';\n return (\n <MenuItem\n key={a.webhookId}\n onClick={handleTriggerAction(a)}\n disabled={!!successTrigger}\n >\n <Typography variant=\"inherit\" noWrap>\n {triggeredBy ? `${a.name} (by ${triggeredBy})` : a.name}\n </Typography>\n </MenuItem>\n );\n });\n\n return (\n <>\n <IconButton\n aria-label=\"more\"\n aria-controls=\"long-menu\"\n aria-haspopup=\"true\"\n onClick={handleClick}\n size=\"small\"\n >\n <MoreVertIcon />\n </IconButton>\n <Menu\n id={`alert-actions-menu-${alert.id}`}\n anchorEl={anchorEl}\n keepMounted\n open={Boolean(anchorEl)}\n onClose={handleCloseMenu}\n PaperProps={{\n style: { maxHeight: 48 * 5.5 },\n }}\n >\n {alert.status === 'PENDING' ? (\n <MenuItem key=\"ack\" onClick={handleAccept}>\n <Typography variant=\"inherit\" noWrap>\n Accept\n </Typography>\n </MenuItem>\n ) : null}\n\n {alert.status !== 'RESOLVED' ? (\n <MenuItem key=\"close\" onClick={handleResolve}>\n <Typography variant=\"inherit\" noWrap>\n Resolve\n </Typography>\n </MenuItem>\n ) : null}\n\n {alert.status !== 'RESOLVED' ? (\n <MenuItem key=\"assign\" onClick={handleAssign}>\n <Typography variant=\"inherit\" noWrap>\n Assign\n </Typography>\n </MenuItem>\n ) : null}\n\n {isLoading ? (\n <MenuItem key=\"loading\">\n <Progress style={{ width: '100%' }} />\n </MenuItem>\n ) : (\n actions\n )}\n\n <MenuItem key=\"details\" onClick={handleCloseMenu}>\n <Typography variant=\"inherit\" noWrap>\n <Link to={ilertApi.getAlertDetailsURL(alert)}>View in iLert</Link>\n </Typography>\n </MenuItem>\n </Menu>\n <AlertAssignModal\n alert={alert}\n setIsModalOpened={setIsAssignAlertModalOpened}\n isModalOpened={isAssignAlertModalOpened}\n onAlertChanged={onAlertChanged}\n />\n </>\n );\n};\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { useApi, alertApiRef } from '@backstage/core-plugin-api';
|
|
2
|
+
import Typography from '@material-ui/core/Typography';
|
|
3
|
+
import Button from '@material-ui/core/Button';
|
|
4
|
+
import Dialog from '@material-ui/core/Dialog';
|
|
5
|
+
import DialogActions from '@material-ui/core/DialogActions';
|
|
6
|
+
import DialogContent from '@material-ui/core/DialogContent';
|
|
7
|
+
import DialogTitle from '@material-ui/core/DialogTitle';
|
|
8
|
+
import { makeStyles } from '@material-ui/core/styles';
|
|
9
|
+
import TextField from '@material-ui/core/TextField';
|
|
10
|
+
import Alert from '@material-ui/lab/Alert';
|
|
11
|
+
import Autocomplete from '@material-ui/lab/Autocomplete';
|
|
12
|
+
import React from 'react';
|
|
13
|
+
import { ilertApiRef } from '../../api/client.esm.js';
|
|
14
|
+
import { useAssignAlert } from '../../hooks/useAssignAlert.esm.js';
|
|
15
|
+
|
|
16
|
+
const useStyles = makeStyles(() => ({
|
|
17
|
+
container: {
|
|
18
|
+
display: "flex",
|
|
19
|
+
flexWrap: "wrap"
|
|
20
|
+
},
|
|
21
|
+
formControl: {
|
|
22
|
+
minWidth: 120,
|
|
23
|
+
width: "100%"
|
|
24
|
+
},
|
|
25
|
+
option: {
|
|
26
|
+
fontSize: 15,
|
|
27
|
+
"& > span": {
|
|
28
|
+
marginRight: 10,
|
|
29
|
+
fontSize: 18
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
optionWrapper: {
|
|
33
|
+
display: "flex",
|
|
34
|
+
width: "100%"
|
|
35
|
+
},
|
|
36
|
+
sourceImage: {
|
|
37
|
+
height: 22,
|
|
38
|
+
paddingRight: 4
|
|
39
|
+
}
|
|
40
|
+
}));
|
|
41
|
+
const AlertAssignModal = ({
|
|
42
|
+
alert,
|
|
43
|
+
isModalOpened,
|
|
44
|
+
setIsModalOpened,
|
|
45
|
+
onAlertChanged
|
|
46
|
+
}) => {
|
|
47
|
+
const [
|
|
48
|
+
{ alertRespondersList, alertResponder, isLoading },
|
|
49
|
+
{ setIsLoading, setAlertResponder, setAlertRespondersList }
|
|
50
|
+
] = useAssignAlert(alert, isModalOpened);
|
|
51
|
+
const callback = onAlertChanged || ((_) => {
|
|
52
|
+
});
|
|
53
|
+
const ilertApi = useApi(ilertApiRef);
|
|
54
|
+
const alertApi = useApi(alertApiRef);
|
|
55
|
+
const classes = useStyles();
|
|
56
|
+
const handleClose = () => {
|
|
57
|
+
setAlertRespondersList([]);
|
|
58
|
+
setIsModalOpened(false);
|
|
59
|
+
};
|
|
60
|
+
const handleAssign = () => {
|
|
61
|
+
if (!alert || !alertResponder) {
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
setIsLoading(true);
|
|
65
|
+
setAlertRespondersList([]);
|
|
66
|
+
setTimeout(async () => {
|
|
67
|
+
try {
|
|
68
|
+
const newAlert = await ilertApi.assignAlert(alert, alertResponder);
|
|
69
|
+
callback(newAlert);
|
|
70
|
+
alertApi.post({ message: "Alert assigned." });
|
|
71
|
+
} catch (err) {
|
|
72
|
+
alertApi.post({ message: err, severity: "error" });
|
|
73
|
+
}
|
|
74
|
+
setIsLoading(false);
|
|
75
|
+
setIsModalOpened(false);
|
|
76
|
+
}, 250);
|
|
77
|
+
};
|
|
78
|
+
const canAssign = !!alertResponder;
|
|
79
|
+
return /* @__PURE__ */ React.createElement(
|
|
80
|
+
Dialog,
|
|
81
|
+
{
|
|
82
|
+
open: isModalOpened,
|
|
83
|
+
onClose: handleClose,
|
|
84
|
+
"aria-labelledby": "assign-alert-form-title"
|
|
85
|
+
},
|
|
86
|
+
/* @__PURE__ */ React.createElement(DialogTitle, { id: "assign-alert-form-title" }, "Select responder to assign"),
|
|
87
|
+
/* @__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(
|
|
88
|
+
Autocomplete,
|
|
89
|
+
{
|
|
90
|
+
disabled: isLoading,
|
|
91
|
+
options: alertRespondersList,
|
|
92
|
+
value: alertResponder,
|
|
93
|
+
classes: {
|
|
94
|
+
root: classes.formControl,
|
|
95
|
+
option: classes.option
|
|
96
|
+
},
|
|
97
|
+
onChange: (_event, newValue) => {
|
|
98
|
+
setAlertResponder(newValue);
|
|
99
|
+
},
|
|
100
|
+
autoHighlight: true,
|
|
101
|
+
groupBy: (option) => {
|
|
102
|
+
switch (option.group) {
|
|
103
|
+
case "SUGGESTED":
|
|
104
|
+
return "Suggested responders";
|
|
105
|
+
case "USER":
|
|
106
|
+
return "Users";
|
|
107
|
+
case "ESCALATION_POLICY":
|
|
108
|
+
return "Escalation policies";
|
|
109
|
+
case "ON_CALL_SCHEDULE":
|
|
110
|
+
return "Schedules";
|
|
111
|
+
default:
|
|
112
|
+
return "";
|
|
113
|
+
}
|
|
114
|
+
},
|
|
115
|
+
getOptionLabel: (a) => a.name,
|
|
116
|
+
renderOption: (a) => /* @__PURE__ */ React.createElement("div", { className: classes.optionWrapper }, /* @__PURE__ */ React.createElement(Typography, { noWrap: true }, a.name)),
|
|
117
|
+
renderInput: (params) => /* @__PURE__ */ React.createElement(
|
|
118
|
+
TextField,
|
|
119
|
+
{
|
|
120
|
+
...params,
|
|
121
|
+
label: "Responder",
|
|
122
|
+
variant: "outlined",
|
|
123
|
+
margin: "normal",
|
|
124
|
+
inputProps: {
|
|
125
|
+
...params.inputProps,
|
|
126
|
+
autoComplete: "new-password"
|
|
127
|
+
// disable autocomplete and autofill
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
)
|
|
131
|
+
}
|
|
132
|
+
)),
|
|
133
|
+
/* @__PURE__ */ React.createElement(DialogActions, null, /* @__PURE__ */ React.createElement(
|
|
134
|
+
Button,
|
|
135
|
+
{
|
|
136
|
+
disabled: !canAssign,
|
|
137
|
+
onClick: handleAssign,
|
|
138
|
+
color: "primary",
|
|
139
|
+
variant: "contained"
|
|
140
|
+
},
|
|
141
|
+
"Assign"
|
|
142
|
+
), /* @__PURE__ */ React.createElement(Button, { onClick: handleClose, color: "primary" }, "Cancel"))
|
|
143
|
+
);
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
export { AlertAssignModal };
|
|
147
|
+
//# sourceMappingURL=AlertAssignModal.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AlertAssignModal.esm.js","sources":["../../../src/components/Alert/AlertAssignModal.tsx"],"sourcesContent":["/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { alertApiRef, useApi } from '@backstage/core-plugin-api';\nimport Typography from '@material-ui/core/Typography';\nimport Button from '@material-ui/core/Button';\nimport Dialog from '@material-ui/core/Dialog';\nimport DialogActions from '@material-ui/core/DialogActions';\nimport DialogContent from '@material-ui/core/DialogContent';\nimport DialogTitle from '@material-ui/core/DialogTitle';\nimport { makeStyles } from '@material-ui/core/styles';\nimport TextField from '@material-ui/core/TextField';\nimport MUIAlert from '@material-ui/lab/Alert';\nimport Autocomplete from '@material-ui/lab/Autocomplete';\nimport React from 'react';\nimport { ilertApiRef } from '../../api';\nimport { useAssignAlert } from '../../hooks/useAssignAlert';\nimport { Alert } from '../../types';\n\nconst useStyles = makeStyles(() => ({\n container: {\n display: 'flex',\n flexWrap: 'wrap',\n },\n formControl: {\n minWidth: 120,\n width: '100%',\n },\n option: {\n fontSize: 15,\n '& > span': {\n marginRight: 10,\n fontSize: 18,\n },\n },\n optionWrapper: {\n display: 'flex',\n width: '100%',\n },\n sourceImage: {\n height: 22,\n paddingRight: 4,\n },\n}));\n\nexport const AlertAssignModal = ({\n alert,\n isModalOpened,\n setIsModalOpened,\n onAlertChanged,\n}: {\n alert: Alert | null;\n isModalOpened: boolean;\n setIsModalOpened: (open: boolean) => void;\n onAlertChanged?: (alert: Alert) => void;\n}) => {\n const [\n { alertRespondersList, alertResponder, isLoading },\n { setIsLoading, setAlertResponder, setAlertRespondersList },\n ] = useAssignAlert(alert, isModalOpened);\n const callback = onAlertChanged || ((_: Alert): void => {});\n const ilertApi = useApi(ilertApiRef);\n const alertApi = useApi(alertApiRef);\n const classes = useStyles();\n\n const handleClose = () => {\n setAlertRespondersList([]);\n setIsModalOpened(false);\n };\n\n const handleAssign = () => {\n if (!alert || !alertResponder) {\n return;\n }\n setIsLoading(true);\n setAlertRespondersList([]);\n setTimeout(async () => {\n try {\n const newAlert = await ilertApi.assignAlert(alert, alertResponder);\n callback(newAlert);\n alertApi.post({ message: 'Alert assigned.' });\n } catch (err) {\n alertApi.post({ message: err, severity: 'error' });\n }\n setIsLoading(false);\n setIsModalOpened(false);\n }, 250);\n };\n\n const canAssign = !!alertResponder;\n\n return (\n <Dialog\n open={isModalOpened}\n onClose={handleClose}\n aria-labelledby=\"assign-alert-form-title\"\n >\n <DialogTitle id=\"assign-alert-form-title\">\n Select responder to assign\n </DialogTitle>\n <DialogContent>\n <MUIAlert severity=\"info\">\n <Typography variant=\"body1\" gutterBottom align=\"justify\">\n This action will assign the alert to the selected responder.\n </Typography>\n </MUIAlert>\n <Autocomplete\n disabled={isLoading}\n options={alertRespondersList}\n value={alertResponder}\n classes={{\n root: classes.formControl,\n option: classes.option,\n }}\n onChange={(_event: any, newValue: any) => {\n setAlertResponder(newValue);\n }}\n autoHighlight\n groupBy={option => {\n switch (option.group) {\n case 'SUGGESTED':\n return 'Suggested responders';\n case 'USER':\n return 'Users';\n case 'ESCALATION_POLICY':\n return 'Escalation policies';\n case 'ON_CALL_SCHEDULE':\n return 'Schedules';\n default:\n return '';\n }\n }}\n getOptionLabel={a => a.name}\n renderOption={a => (\n <div className={classes.optionWrapper}>\n <Typography noWrap>{a.name}</Typography>\n </div>\n )}\n renderInput={params => (\n <TextField\n {...params}\n label=\"Responder\"\n variant=\"outlined\"\n margin=\"normal\"\n inputProps={{\n ...params.inputProps,\n autoComplete: 'new-password', // disable autocomplete and autofill\n }}\n />\n )}\n />\n </DialogContent>\n <DialogActions>\n <Button\n disabled={!canAssign}\n onClick={handleAssign}\n color=\"primary\"\n variant=\"contained\"\n >\n Assign\n </Button>\n <Button onClick={handleClose} color=\"primary\">\n Cancel\n </Button>\n </DialogActions>\n </Dialog>\n );\n};\n"],"names":["MUIAlert"],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { ilertApiRef } from '../../api/client.esm.js';
|
|
3
|
+
import { Link } from '@backstage/core-components';
|
|
4
|
+
import { useApi } from '@backstage/core-plugin-api';
|
|
5
|
+
|
|
6
|
+
const AlertLink = ({ alert }) => {
|
|
7
|
+
const ilertApi = useApi(ilertApiRef);
|
|
8
|
+
if (!alert) {
|
|
9
|
+
return null;
|
|
10
|
+
}
|
|
11
|
+
return /* @__PURE__ */ React.createElement(Link, { to: ilertApi.getAlertDetailsURL(alert) }, "#", alert.id);
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export { AlertLink };
|
|
15
|
+
//# sourceMappingURL=AlertLink.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AlertLink.esm.js","sources":["../../../src/components/Alert/AlertLink.tsx"],"sourcesContent":["/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport React from 'react';\nimport { ilertApiRef } from '../../api';\nimport { Alert } from '../../types';\n\nimport { Link } from '@backstage/core-components';\nimport { useApi } from '@backstage/core-plugin-api';\n\nexport const AlertLink = ({ alert }: { alert: Alert | null }) => {\n const ilertApi = useApi(ilertApiRef);\n\n if (!alert) {\n return null;\n }\n\n return <Link to={ilertApi.getAlertDetailsURL(alert)}>#{alert.id}</Link>;\n};\n"],"names":[],"mappings":";;;;;AAsBO,MAAM,SAAY,GAAA,CAAC,EAAE,KAAA,EAAqC,KAAA;AAC/D,EAAM,MAAA,QAAA,GAAW,OAAO,WAAW,CAAA,CAAA;AAEnC,EAAA,IAAI,CAAC,KAAO,EAAA;AACV,IAAO,OAAA,IAAA,CAAA;AAAA,GACT;AAEA,EAAO,uBAAA,KAAA,CAAA,aAAA,CAAC,QAAK,EAAI,EAAA,QAAA,CAAS,mBAAmB,KAAK,CAAA,EAAA,EAAG,GAAE,EAAA,KAAA,CAAM,EAAG,CAAA,CAAA;AAClE;;;;"}
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import { parseEntityRef, DEFAULT_NAMESPACE } from '@backstage/catalog-model';
|
|
2
|
+
import { useApi, alertApiRef, identityApiRef } from '@backstage/core-plugin-api';
|
|
3
|
+
import Typography from '@material-ui/core/Typography';
|
|
4
|
+
import Button from '@material-ui/core/Button';
|
|
5
|
+
import Dialog from '@material-ui/core/Dialog';
|
|
6
|
+
import DialogActions from '@material-ui/core/DialogActions';
|
|
7
|
+
import DialogContent from '@material-ui/core/DialogContent';
|
|
8
|
+
import DialogTitle from '@material-ui/core/DialogTitle';
|
|
9
|
+
import { makeStyles } from '@material-ui/core/styles';
|
|
10
|
+
import TextField from '@material-ui/core/TextField';
|
|
11
|
+
import useMediaQuery from '@material-ui/core/useMediaQuery';
|
|
12
|
+
import Alert from '@material-ui/lab/Alert';
|
|
13
|
+
import Autocomplete from '@material-ui/lab/Autocomplete';
|
|
14
|
+
import React from 'react';
|
|
15
|
+
import { ilertApiRef } from '../../api/client.esm.js';
|
|
16
|
+
import { useNewAlert } from '../../hooks/useNewAlert.esm.js';
|
|
17
|
+
|
|
18
|
+
const useStyles = makeStyles(() => ({
|
|
19
|
+
container: {
|
|
20
|
+
display: "flex",
|
|
21
|
+
flexWrap: "wrap"
|
|
22
|
+
},
|
|
23
|
+
formControl: {
|
|
24
|
+
minWidth: 120,
|
|
25
|
+
width: "100%"
|
|
26
|
+
},
|
|
27
|
+
option: {
|
|
28
|
+
fontSize: 15,
|
|
29
|
+
"& > span": {
|
|
30
|
+
marginRight: 10,
|
|
31
|
+
fontSize: 18
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
optionWrapper: {
|
|
35
|
+
display: "flex",
|
|
36
|
+
width: "100%"
|
|
37
|
+
},
|
|
38
|
+
sourceImage: {
|
|
39
|
+
height: 22,
|
|
40
|
+
paddingRight: 4
|
|
41
|
+
}
|
|
42
|
+
}));
|
|
43
|
+
const AlertNewModal = ({
|
|
44
|
+
isModalOpened,
|
|
45
|
+
setIsModalOpened,
|
|
46
|
+
refetchAlerts,
|
|
47
|
+
initialAlertSource,
|
|
48
|
+
entityName
|
|
49
|
+
}) => {
|
|
50
|
+
const [
|
|
51
|
+
{ alertSources, alertSource, summary, details, isLoading },
|
|
52
|
+
{ setAlertSource, setSummary, setDetails, setIsLoading }
|
|
53
|
+
] = useNewAlert(isModalOpened, initialAlertSource);
|
|
54
|
+
const ilertApi = useApi(ilertApiRef);
|
|
55
|
+
const alertApi = useApi(alertApiRef);
|
|
56
|
+
const identityApi = useApi(identityApiRef);
|
|
57
|
+
const source = window.location.toString();
|
|
58
|
+
const classes = useStyles();
|
|
59
|
+
const prefersDarkMode = useMediaQuery("(prefers-color-scheme: dark)");
|
|
60
|
+
const handleClose = () => {
|
|
61
|
+
setIsModalOpened(false);
|
|
62
|
+
};
|
|
63
|
+
let integrationKey = "";
|
|
64
|
+
if (initialAlertSource && initialAlertSource.integrationKey) {
|
|
65
|
+
integrationKey = initialAlertSource.integrationKey;
|
|
66
|
+
} else if (alertSource && alertSource.integrationKey) {
|
|
67
|
+
integrationKey = alertSource.integrationKey;
|
|
68
|
+
}
|
|
69
|
+
const handleCreate = () => {
|
|
70
|
+
if (!integrationKey) {
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
setIsLoading(true);
|
|
74
|
+
setTimeout(async () => {
|
|
75
|
+
try {
|
|
76
|
+
const { userEntityRef } = await identityApi.getBackstageIdentity();
|
|
77
|
+
const { name: userName } = parseEntityRef(userEntityRef, {
|
|
78
|
+
defaultKind: "User",
|
|
79
|
+
defaultNamespace: DEFAULT_NAMESPACE
|
|
80
|
+
});
|
|
81
|
+
await ilertApi.createAlert({
|
|
82
|
+
integrationKey,
|
|
83
|
+
summary,
|
|
84
|
+
details,
|
|
85
|
+
userName,
|
|
86
|
+
source
|
|
87
|
+
});
|
|
88
|
+
alertApi.post({ message: "Alert created." });
|
|
89
|
+
refetchAlerts();
|
|
90
|
+
} catch (err) {
|
|
91
|
+
alertApi.post({ message: err, severity: "error" });
|
|
92
|
+
}
|
|
93
|
+
setIsModalOpened(false);
|
|
94
|
+
}, 250);
|
|
95
|
+
};
|
|
96
|
+
const canCreate = !!integrationKey && !!summary;
|
|
97
|
+
return /* @__PURE__ */ React.createElement(
|
|
98
|
+
Dialog,
|
|
99
|
+
{
|
|
100
|
+
open: isModalOpened,
|
|
101
|
+
onClose: handleClose,
|
|
102
|
+
"aria-labelledby": "create-alert-form-title"
|
|
103
|
+
},
|
|
104
|
+
/* @__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"),
|
|
105
|
+
/* @__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(
|
|
106
|
+
Autocomplete,
|
|
107
|
+
{
|
|
108
|
+
disabled: isLoading,
|
|
109
|
+
options: alertSources,
|
|
110
|
+
value: alertSource,
|
|
111
|
+
classes: {
|
|
112
|
+
root: classes.formControl,
|
|
113
|
+
option: classes.option
|
|
114
|
+
},
|
|
115
|
+
onChange: (_event, newValue) => {
|
|
116
|
+
setAlertSource(newValue);
|
|
117
|
+
},
|
|
118
|
+
autoHighlight: true,
|
|
119
|
+
getOptionLabel: (a) => a.name,
|
|
120
|
+
renderOption: (a) => /* @__PURE__ */ React.createElement("div", { className: classes.optionWrapper }, /* @__PURE__ */ React.createElement(
|
|
121
|
+
"img",
|
|
122
|
+
{
|
|
123
|
+
src: prefersDarkMode ? a.lightIconUrl : a.iconUrl,
|
|
124
|
+
alt: a.name,
|
|
125
|
+
className: classes.sourceImage
|
|
126
|
+
}
|
|
127
|
+
), /* @__PURE__ */ React.createElement(Typography, { noWrap: true }, a.name)),
|
|
128
|
+
renderInput: (params) => /* @__PURE__ */ React.createElement(
|
|
129
|
+
TextField,
|
|
130
|
+
{
|
|
131
|
+
...params,
|
|
132
|
+
label: "Alert Source",
|
|
133
|
+
variant: "outlined",
|
|
134
|
+
margin: "normal",
|
|
135
|
+
inputProps: {
|
|
136
|
+
...params.inputProps,
|
|
137
|
+
autoComplete: "new-password"
|
|
138
|
+
// disable autocomplete and autofill
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
)
|
|
142
|
+
}
|
|
143
|
+
) : null, /* @__PURE__ */ React.createElement(
|
|
144
|
+
TextField,
|
|
145
|
+
{
|
|
146
|
+
disabled: isLoading,
|
|
147
|
+
label: "Summary",
|
|
148
|
+
fullWidth: true,
|
|
149
|
+
margin: "normal",
|
|
150
|
+
variant: "outlined",
|
|
151
|
+
classes: {
|
|
152
|
+
root: classes.formControl
|
|
153
|
+
},
|
|
154
|
+
value: summary,
|
|
155
|
+
onChange: (event) => {
|
|
156
|
+
setSummary(event.target.value);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
), /* @__PURE__ */ React.createElement(
|
|
160
|
+
TextField,
|
|
161
|
+
{
|
|
162
|
+
disabled: isLoading,
|
|
163
|
+
label: "Details",
|
|
164
|
+
fullWidth: true,
|
|
165
|
+
multiline: true,
|
|
166
|
+
rows: 4,
|
|
167
|
+
margin: "normal",
|
|
168
|
+
variant: "outlined",
|
|
169
|
+
classes: {
|
|
170
|
+
root: classes.formControl
|
|
171
|
+
},
|
|
172
|
+
value: details,
|
|
173
|
+
onChange: (event) => {
|
|
174
|
+
setDetails(event.target.value);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
)),
|
|
178
|
+
/* @__PURE__ */ React.createElement(DialogActions, null, /* @__PURE__ */ React.createElement(
|
|
179
|
+
Button,
|
|
180
|
+
{
|
|
181
|
+
disabled: !canCreate,
|
|
182
|
+
onClick: handleCreate,
|
|
183
|
+
color: "secondary",
|
|
184
|
+
variant: "contained"
|
|
185
|
+
},
|
|
186
|
+
"Create"
|
|
187
|
+
), /* @__PURE__ */ React.createElement(Button, { onClick: handleClose, color: "primary" }, "Cancel"))
|
|
188
|
+
);
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
export { AlertNewModal };
|
|
192
|
+
//# sourceMappingURL=AlertNewModal.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AlertNewModal.esm.js","sources":["../../../src/components/Alert/AlertNewModal.tsx"],"sourcesContent":["/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { DEFAULT_NAMESPACE, parseEntityRef } from '@backstage/catalog-model';\nimport {\n alertApiRef,\n identityApiRef,\n useApi,\n} from '@backstage/core-plugin-api';\nimport Typography from '@material-ui/core/Typography';\nimport Button from '@material-ui/core/Button';\nimport Dialog from '@material-ui/core/Dialog';\nimport DialogActions from '@material-ui/core/DialogActions';\nimport DialogContent from '@material-ui/core/DialogContent';\nimport DialogTitle from '@material-ui/core/DialogTitle';\nimport { makeStyles } from '@material-ui/core/styles';\nimport TextField from '@material-ui/core/TextField';\nimport useMediaQuery from '@material-ui/core/useMediaQuery';\nimport Alert from '@material-ui/lab/Alert';\nimport Autocomplete from '@material-ui/lab/Autocomplete';\nimport React from 'react';\nimport { ilertApiRef } from '../../api';\nimport { useNewAlert } from '../../hooks/useNewAlert';\nimport { AlertSource } from '../../types';\n\nconst useStyles = makeStyles(() => ({\n container: {\n display: 'flex',\n flexWrap: 'wrap',\n },\n formControl: {\n minWidth: 120,\n width: '100%',\n },\n option: {\n fontSize: 15,\n '& > span': {\n marginRight: 10,\n fontSize: 18,\n },\n },\n optionWrapper: {\n display: 'flex',\n width: '100%',\n },\n sourceImage: {\n height: 22,\n paddingRight: 4,\n },\n}));\n\nexport const AlertNewModal = ({\n isModalOpened,\n setIsModalOpened,\n refetchAlerts,\n initialAlertSource,\n entityName,\n}: {\n isModalOpened: boolean;\n setIsModalOpened: (open: boolean) => void;\n refetchAlerts: () => void;\n initialAlertSource?: AlertSource | null;\n entityName?: string;\n}) => {\n const [\n { alertSources, alertSource, summary, details, isLoading },\n { setAlertSource, setSummary, setDetails, setIsLoading },\n ] = useNewAlert(isModalOpened, initialAlertSource);\n const ilertApi = useApi(ilertApiRef);\n const alertApi = useApi(alertApiRef);\n const identityApi = useApi(identityApiRef);\n const source = window.location.toString();\n const classes = useStyles();\n const prefersDarkMode = useMediaQuery('(prefers-color-scheme: dark)');\n\n const handleClose = () => {\n setIsModalOpened(false);\n };\n\n let integrationKey = '';\n if (initialAlertSource && initialAlertSource.integrationKey) {\n integrationKey = initialAlertSource.integrationKey;\n } else if (alertSource && alertSource.integrationKey) {\n integrationKey = alertSource.integrationKey;\n }\n const handleCreate = () => {\n if (!integrationKey) {\n return;\n }\n setIsLoading(true);\n setTimeout(async () => {\n try {\n const { userEntityRef } = await identityApi.getBackstageIdentity();\n const { name: userName } = parseEntityRef(userEntityRef, {\n defaultKind: 'User',\n defaultNamespace: DEFAULT_NAMESPACE,\n });\n await ilertApi.createAlert({\n integrationKey,\n summary,\n details,\n userName,\n source,\n });\n alertApi.post({ message: 'Alert created.' });\n refetchAlerts();\n } catch (err) {\n alertApi.post({ message: err, severity: 'error' });\n }\n setIsModalOpened(false);\n }, 250);\n };\n\n const canCreate = !!integrationKey && !!summary;\n\n return (\n <Dialog\n open={isModalOpened}\n onClose={handleClose}\n aria-labelledby=\"create-alert-form-title\"\n >\n <DialogTitle id=\"create-alert-form-title\">\n {entityName ? (\n <div>\n This action will trigger an alert for{' '}\n <strong>\"{entityName}\"</strong>.\n </div>\n ) : (\n 'New alert'\n )}\n </DialogTitle>\n <DialogContent>\n <Alert severity=\"info\">\n <Typography variant=\"body1\" gutterBottom align=\"justify\">\n Please describe the problem you want to report. Be as descriptive as\n possible. Your signed in user and a reference to the current page\n will automatically be amended to the alarm so that the receiver can\n reach out to you if necessary.\n </Typography>\n </Alert>\n {!initialAlertSource ? (\n <Autocomplete\n disabled={isLoading}\n options={alertSources}\n value={alertSource}\n classes={{\n root: classes.formControl,\n option: classes.option,\n }}\n onChange={(_event: any, newValue: any) => {\n setAlertSource(newValue);\n }}\n autoHighlight\n getOptionLabel={a => a.name}\n renderOption={a => (\n <div className={classes.optionWrapper}>\n <img\n src={prefersDarkMode ? a.lightIconUrl : a.iconUrl}\n alt={a.name}\n className={classes.sourceImage}\n />\n <Typography noWrap>{a.name}</Typography>\n </div>\n )}\n renderInput={params => (\n <TextField\n {...params}\n label=\"Alert Source\"\n variant=\"outlined\"\n margin=\"normal\"\n inputProps={{\n ...params.inputProps,\n autoComplete: 'new-password', // disable autocomplete and autofill\n }}\n />\n )}\n />\n ) : null}\n <TextField\n disabled={isLoading}\n label=\"Summary\"\n fullWidth\n margin=\"normal\"\n variant=\"outlined\"\n classes={{\n root: classes.formControl,\n }}\n value={summary}\n onChange={event => {\n setSummary(event.target.value);\n }}\n />\n <TextField\n disabled={isLoading}\n label=\"Details\"\n fullWidth\n multiline\n rows={4}\n margin=\"normal\"\n variant=\"outlined\"\n classes={{\n root: classes.formControl,\n }}\n value={details}\n onChange={event => {\n setDetails(event.target.value);\n }}\n />\n </DialogContent>\n <DialogActions>\n <Button\n disabled={!canCreate}\n onClick={handleCreate}\n color=\"secondary\"\n variant=\"contained\"\n >\n Create\n </Button>\n <Button onClick={handleClose} color=\"primary\">\n Cancel\n </Button>\n </DialogActions>\n </Dialog>\n );\n};\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import Grid from '@material-ui/core/Grid';
|
|
3
|
+
import { ilertApiRef } from '../../api/client.esm.js';
|
|
4
|
+
import { makeStyles } from '@material-ui/core/styles';
|
|
5
|
+
import useMediaQuery from '@material-ui/core/useMediaQuery';
|
|
6
|
+
import { useApi } from '@backstage/core-plugin-api';
|
|
7
|
+
import { Link } from '@backstage/core-components';
|
|
8
|
+
|
|
9
|
+
const useStyles = makeStyles({
|
|
10
|
+
root: {
|
|
11
|
+
display: "flex",
|
|
12
|
+
maxWidth: "100%"
|
|
13
|
+
},
|
|
14
|
+
image: {
|
|
15
|
+
height: 22,
|
|
16
|
+
paddingRight: 4
|
|
17
|
+
},
|
|
18
|
+
link: {
|
|
19
|
+
lineHeight: "22px"
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
const AlertSourceLink = ({
|
|
23
|
+
alertSource
|
|
24
|
+
}) => {
|
|
25
|
+
const ilertApi = useApi(ilertApiRef);
|
|
26
|
+
const classes = useStyles();
|
|
27
|
+
const prefersDarkMode = useMediaQuery("(prefers-color-scheme: dark)");
|
|
28
|
+
if (!alertSource) {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
return /* @__PURE__ */ React.createElement(Grid, { container: true, spacing: 0 }, /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 2 }, /* @__PURE__ */ React.createElement(
|
|
32
|
+
"img",
|
|
33
|
+
{
|
|
34
|
+
src: prefersDarkMode ? alertSource.lightIconUrl : alertSource.iconUrl,
|
|
35
|
+
alt: alertSource.name,
|
|
36
|
+
className: classes.image
|
|
37
|
+
}
|
|
38
|
+
)), /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 10 }, /* @__PURE__ */ React.createElement(
|
|
39
|
+
Link,
|
|
40
|
+
{
|
|
41
|
+
className: classes.link,
|
|
42
|
+
to: ilertApi.getAlertSourceDetailsURL(alertSource)
|
|
43
|
+
},
|
|
44
|
+
alertSource.name
|
|
45
|
+
)));
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
export { AlertSourceLink };
|
|
49
|
+
//# sourceMappingURL=AlertSourceLink.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AlertSourceLink.esm.js","sources":["../../../src/components/AlertSource/AlertSourceLink.tsx"],"sourcesContent":["/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport React from 'react';\nimport Grid from '@material-ui/core/Grid';\nimport { AlertSource } from '../../types';\nimport { ilertApiRef } from '../../api';\nimport { makeStyles } from '@material-ui/core/styles';\nimport useMediaQuery from '@material-ui/core/useMediaQuery';\n\nimport { useApi } from '@backstage/core-plugin-api';\nimport { Link } from '@backstage/core-components';\n\nconst useStyles = makeStyles({\n root: {\n display: 'flex',\n maxWidth: '100%',\n },\n image: {\n height: 22,\n paddingRight: 4,\n },\n link: {\n lineHeight: '22px',\n },\n});\n\nexport const AlertSourceLink = ({\n alertSource,\n}: {\n alertSource: AlertSource | null;\n}) => {\n const ilertApi = useApi(ilertApiRef);\n const classes = useStyles();\n const prefersDarkMode = useMediaQuery('(prefers-color-scheme: dark)');\n\n if (!alertSource) {\n return null;\n }\n\n return (\n <Grid container spacing={0}>\n <Grid item xs={2}>\n <img\n src={prefersDarkMode ? alertSource.lightIconUrl : alertSource.iconUrl}\n alt={alertSource.name}\n className={classes.image}\n />\n </Grid>\n <Grid item xs={10}>\n <Link\n className={classes.link}\n to={ilertApi.getAlertSourceDetailsURL(alertSource)}\n >\n {alertSource.name}\n </Link>\n </Grid>\n </Grid>\n );\n};\n"],"names":[],"mappings":";;;;;;;;AAyBA,MAAM,YAAY,UAAW,CAAA;AAAA,EAC3B,IAAM,EAAA;AAAA,IACJ,OAAS,EAAA,MAAA;AAAA,IACT,QAAU,EAAA,MAAA;AAAA,GACZ;AAAA,EACA,KAAO,EAAA;AAAA,IACL,MAAQ,EAAA,EAAA;AAAA,IACR,YAAc,EAAA,CAAA;AAAA,GAChB;AAAA,EACA,IAAM,EAAA;AAAA,IACJ,UAAY,EAAA,MAAA;AAAA,GACd;AACF,CAAC,CAAA,CAAA;AAEM,MAAM,kBAAkB,CAAC;AAAA,EAC9B,WAAA;AACF,CAEM,KAAA;AACJ,EAAM,MAAA,QAAA,GAAW,OAAO,WAAW,CAAA,CAAA;AACnC,EAAA,MAAM,UAAU,SAAU,EAAA,CAAA;AAC1B,EAAM,MAAA,eAAA,GAAkB,cAAc,8BAA8B,CAAA,CAAA;AAEpE,EAAA,IAAI,CAAC,WAAa,EAAA;AAChB,IAAO,OAAA,IAAA,CAAA;AAAA,GACT;AAEA,EACE,uBAAA,KAAA,CAAA,aAAA,CAAC,IAAK,EAAA,EAAA,SAAA,EAAS,IAAC,EAAA,OAAA,EAAS,CACvB,EAAA,kBAAA,KAAA,CAAA,aAAA,CAAC,IAAK,EAAA,EAAA,IAAA,EAAI,IAAC,EAAA,EAAA,EAAI,CACb,EAAA,kBAAA,KAAA,CAAA,aAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MACC,GAAK,EAAA,eAAA,GAAkB,WAAY,CAAA,YAAA,GAAe,WAAY,CAAA,OAAA;AAAA,MAC9D,KAAK,WAAY,CAAA,IAAA;AAAA,MACjB,WAAW,OAAQ,CAAA,KAAA;AAAA,KAAA;AAAA,GAEvB,CACA,kBAAA,KAAA,CAAA,aAAA,CAAC,QAAK,IAAI,EAAA,IAAA,EAAC,IAAI,EACb,EAAA,kBAAA,KAAA,CAAA,aAAA;AAAA,IAAC,IAAA;AAAA,IAAA;AAAA,MACC,WAAW,OAAQ,CAAA,IAAA;AAAA,MACnB,EAAA,EAAI,QAAS,CAAA,wBAAA,CAAyB,WAAW,CAAA;AAAA,KAAA;AAAA,IAEhD,WAAY,CAAA,IAAA;AAAA,GAEjB,CACF,CAAA,CAAA;AAEJ;;;;"}
|