@pagerduty/backstage-plugin 0.20.0 → 0.21.0
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 +11 -0
- package/dist/alpha/pages.esm.js +41 -4
- package/dist/alpha/pages.esm.js.map +1 -1
- package/dist/alpha/plugin.esm.js +4 -3
- package/dist/alpha/plugin.esm.js.map +1 -1
- package/dist/alpha.d.ts +105 -26
- package/dist/api/client.esm.js +136 -0
- package/dist/api/client.esm.js.map +1 -1
- package/dist/components/PagerDutyPage/ConfigurationPage.esm.js +119 -0
- package/dist/components/PagerDutyPage/ConfigurationPage.esm.js.map +1 -0
- package/dist/components/PagerDutyPage/CustomFieldModal.esm.js +152 -0
- package/dist/components/PagerDutyPage/CustomFieldModal.esm.js.map +1 -0
- package/dist/components/PagerDutyPage/CustomFields/ButtonTabs.esm.js +51 -0
- package/dist/components/PagerDutyPage/CustomFields/ButtonTabs.esm.js.map +1 -0
- package/dist/components/PagerDutyPage/CustomFields/CustomFieldsEmptyState.esm.js +46 -0
- package/dist/components/PagerDutyPage/CustomFields/CustomFieldsEmptyState.esm.js.map +1 -0
- package/dist/components/PagerDutyPage/CustomFields/CustomFieldsTabPanel.esm.js +154 -0
- package/dist/components/PagerDutyPage/CustomFields/CustomFieldsTabPanel.esm.js.map +1 -0
- package/dist/components/PagerDutyPage/CustomFields/DeleteCustomFieldDialog.esm.js +63 -0
- package/dist/components/PagerDutyPage/CustomFields/DeleteCustomFieldDialog.esm.js.map +1 -0
- package/dist/components/PagerDutyPage/CustomFields/SyncLogsFilters.esm.js +126 -0
- package/dist/components/PagerDutyPage/CustomFields/SyncLogsFilters.esm.js.map +1 -0
- package/dist/components/PagerDutyPage/CustomFields/SyncLogsTab.esm.js +285 -0
- package/dist/components/PagerDutyPage/CustomFields/SyncLogsTab.esm.js.map +1 -0
- package/dist/components/PagerDutyPage/CustomFields/customFieldErrors.esm.js +13 -0
- package/dist/components/PagerDutyPage/CustomFields/customFieldErrors.esm.js.map +1 -0
- package/dist/components/PagerDutyPage/CustomFields.esm.js +192 -0
- package/dist/components/PagerDutyPage/CustomFields.esm.js.map +1 -0
- package/dist/components/PagerDutyPage/MappingsTable/MappingsTable.esm.js +1 -1
- package/dist/components/PagerDutyPage/MappingsTable/MappingsTable.esm.js.map +1 -1
- package/dist/components/PagerDutyPage/MappingsTable/MappingsTableContent.esm.js +15 -3
- package/dist/components/PagerDutyPage/MappingsTable/MappingsTableContent.esm.js.map +1 -1
- package/dist/components/PagerDutyPage/MappingsTable/NameCell.esm.js +35 -0
- package/dist/components/PagerDutyPage/MappingsTable/NameCell.esm.js.map +1 -0
- package/dist/components/PagerDutyPage/MappingsTable/TableSkeleton.esm.js +26 -29
- package/dist/components/PagerDutyPage/MappingsTable/TableSkeleton.esm.js.map +1 -1
- package/dist/components/PagerDutyPage/ServiceMappingPage.esm.js +22 -0
- package/dist/components/PagerDutyPage/ServiceMappingPage.esm.js.map +1 -0
- package/dist/components/PagerDutyPage/index.esm.js +12 -127
- package/dist/components/PagerDutyPage/index.esm.js.map +1 -1
- package/dist/index.d.ts +50 -3
- package/dist/package.json.esm.js +2 -2
- package/package.json +17 -17
- package/dist/alpha/nav-items.esm.js +0 -21
- package/dist/alpha/nav-items.esm.js.map +0 -1
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
2
|
+
import { useState, useEffect } from 'react';
|
|
3
|
+
import { makeStyles, Typography, createStyles } from '@material-ui/core';
|
|
4
|
+
import { Box, Alert, Card, RadioGroup, Radio } from '@backstage/ui';
|
|
5
|
+
import { useApi, alertApiRef } from '@backstage/core-plugin-api';
|
|
6
|
+
import { NotFoundError } from '@backstage/errors';
|
|
7
|
+
import { pagerDutyApiRef } from '../../api/client.esm.js';
|
|
8
|
+
|
|
9
|
+
const SERVICE_DEPENDENCY_SYNC_STRATEGY = "settings::service-dependency-sync-strategy";
|
|
10
|
+
const useStyles = makeStyles(
|
|
11
|
+
() => createStyles({
|
|
12
|
+
cardStyles: {
|
|
13
|
+
padding: "15px",
|
|
14
|
+
marginTop: "8px",
|
|
15
|
+
paddingBottom: "20px"
|
|
16
|
+
},
|
|
17
|
+
textContainerStyles: {
|
|
18
|
+
marginTop: "16px"
|
|
19
|
+
},
|
|
20
|
+
linkStyles: {
|
|
21
|
+
color: "cadetblue"
|
|
22
|
+
}
|
|
23
|
+
})
|
|
24
|
+
);
|
|
25
|
+
const ConfigurationPage = () => {
|
|
26
|
+
const { cardStyles, textContainerStyles, linkStyles } = useStyles();
|
|
27
|
+
const pagerDutyApi = useApi(pagerDutyApiRef);
|
|
28
|
+
const alertApi = useApi(alertApiRef);
|
|
29
|
+
const [
|
|
30
|
+
selectedServiceDependencyStrategy,
|
|
31
|
+
setSelectedServiceDependencyStrategy
|
|
32
|
+
] = useState("disabled");
|
|
33
|
+
useEffect(() => {
|
|
34
|
+
function fetchSetting() {
|
|
35
|
+
pagerDutyApi.getSetting(SERVICE_DEPENDENCY_SYNC_STRATEGY).then((result) => {
|
|
36
|
+
if (result !== void 0) {
|
|
37
|
+
setSelectedServiceDependencyStrategy(result.value);
|
|
38
|
+
}
|
|
39
|
+
}).catch((error) => {
|
|
40
|
+
if (error instanceof NotFoundError) {
|
|
41
|
+
setSelectedServiceDependencyStrategy("disabled");
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
fetchSetting();
|
|
46
|
+
}, [pagerDutyApi]);
|
|
47
|
+
const handleChange = async (value) => {
|
|
48
|
+
const previousValue = selectedServiceDependencyStrategy;
|
|
49
|
+
setSelectedServiceDependencyStrategy(value);
|
|
50
|
+
try {
|
|
51
|
+
await pagerDutyApi.storeSettings([
|
|
52
|
+
{
|
|
53
|
+
id: SERVICE_DEPENDENCY_SYNC_STRATEGY,
|
|
54
|
+
value
|
|
55
|
+
}
|
|
56
|
+
]);
|
|
57
|
+
} catch (error) {
|
|
58
|
+
setSelectedServiceDependencyStrategy(previousValue);
|
|
59
|
+
alertApi.post({
|
|
60
|
+
message: `Failed to save service dependency synchronization strategy. ${error instanceof Error ? error.message : error}`,
|
|
61
|
+
severity: "error"
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
return /* @__PURE__ */ jsxs(Box, { pl: "20px", pr: "20px", children: [
|
|
66
|
+
/* @__PURE__ */ jsx(
|
|
67
|
+
Alert,
|
|
68
|
+
{
|
|
69
|
+
className: textContainerStyles,
|
|
70
|
+
status: "warning",
|
|
71
|
+
icon: true,
|
|
72
|
+
title: "Changing this setting will affect how your service dependencies are synchronized and may cause data loss.",
|
|
73
|
+
description: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
74
|
+
"Check the",
|
|
75
|
+
" ",
|
|
76
|
+
/* @__PURE__ */ jsx(
|
|
77
|
+
"a",
|
|
78
|
+
{
|
|
79
|
+
className: linkStyles,
|
|
80
|
+
href: "https://pagerduty.github.io/backstage-plugin-docs/index.html",
|
|
81
|
+
target: "_blank",
|
|
82
|
+
rel: "noopener noreferrer",
|
|
83
|
+
children: "documentation"
|
|
84
|
+
}
|
|
85
|
+
),
|
|
86
|
+
" ",
|
|
87
|
+
"for more information."
|
|
88
|
+
] })
|
|
89
|
+
}
|
|
90
|
+
),
|
|
91
|
+
/* @__PURE__ */ jsxs(
|
|
92
|
+
Card,
|
|
93
|
+
{
|
|
94
|
+
title: "Service dependency synchronization preferences",
|
|
95
|
+
className: cardStyles,
|
|
96
|
+
children: [
|
|
97
|
+
/* @__PURE__ */ jsx(Typography, { variant: "h6", children: "Service dependency synchronization strategy" }),
|
|
98
|
+
/* @__PURE__ */ jsxs(
|
|
99
|
+
RadioGroup,
|
|
100
|
+
{
|
|
101
|
+
label: "Select the main source of truth for your service dependencies",
|
|
102
|
+
value: selectedServiceDependencyStrategy,
|
|
103
|
+
onChange: (value) => void handleChange(value),
|
|
104
|
+
children: [
|
|
105
|
+
/* @__PURE__ */ jsx(Radio, { value: "backstage" /* backstage */, children: "Backstage" }),
|
|
106
|
+
/* @__PURE__ */ jsx(Radio, { value: "pagerduty" /* pagerduty */, children: "PagerDuty" }),
|
|
107
|
+
/* @__PURE__ */ jsx(Radio, { value: "both" /* both */, children: "Both" }),
|
|
108
|
+
/* @__PURE__ */ jsx(Radio, { value: "disabled" /* disabled */, children: "Disabled" })
|
|
109
|
+
]
|
|
110
|
+
}
|
|
111
|
+
)
|
|
112
|
+
]
|
|
113
|
+
}
|
|
114
|
+
)
|
|
115
|
+
] });
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
export { ConfigurationPage };
|
|
119
|
+
//# sourceMappingURL=ConfigurationPage.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ConfigurationPage.esm.js","sources":["../../../src/components/PagerDutyPage/ConfigurationPage.tsx"],"sourcesContent":["import { useEffect, useState } from 'react';\nimport { createStyles, makeStyles, Typography } from '@material-ui/core';\nimport { Card, RadioGroup, Radio, Box, Alert } from '@backstage/ui';\nimport { useApi, alertApiRef } from '@backstage/core-plugin-api';\nimport { NotFoundError } from '@backstage/errors';\nimport { pagerDutyApiRef } from '../../api';\n\nenum StoreSettings {\n backstage = 'backstage',\n pagerduty = 'pagerduty',\n both = 'both',\n disabled = 'disabled',\n}\n\nconst SERVICE_DEPENDENCY_SYNC_STRATEGY =\n 'settings::service-dependency-sync-strategy';\n\nconst useStyles = makeStyles(() =>\n createStyles({\n cardStyles: {\n padding: '15px',\n marginTop: '8px',\n paddingBottom: \"20px\"\n },\n textContainerStyles: {\n marginTop: '16px',\n },\n linkStyles: {\n color: 'cadetblue',\n },\n }),\n);\n\n/** @public */\nexport const ConfigurationPage = () => {\n const { cardStyles, textContainerStyles, linkStyles } = useStyles();\n const pagerDutyApi = useApi(pagerDutyApiRef);\n const alertApi = useApi(alertApiRef);\n const [\n selectedServiceDependencyStrategy,\n setSelectedServiceDependencyStrategy,\n ] = useState('disabled');\n\n useEffect(() => {\n function fetchSetting() {\n pagerDutyApi\n .getSetting(SERVICE_DEPENDENCY_SYNC_STRATEGY)\n .then(result => {\n if (result !== undefined) {\n setSelectedServiceDependencyStrategy(result.value);\n }\n })\n .catch(error => {\n if (error instanceof NotFoundError) {\n // If the setting is not found, set the default value to \"disabled\"\n setSelectedServiceDependencyStrategy('disabled');\n }\n });\n }\n\n fetchSetting();\n }, [pagerDutyApi]);\n\n const handleChange = async (value: StoreSettings) => {\n // Optimistically reflect the selection, remembering the previous value so we\n // can roll back if the save fails — otherwise the radio would show a setting\n // that was never persisted and silently revert on the next page load.\n const previousValue = selectedServiceDependencyStrategy;\n setSelectedServiceDependencyStrategy(value);\n\n try {\n await pagerDutyApi.storeSettings([\n {\n id: SERVICE_DEPENDENCY_SYNC_STRATEGY,\n value,\n },\n ]);\n } catch (error) {\n setSelectedServiceDependencyStrategy(previousValue);\n alertApi.post({\n message: `Failed to save service dependency synchronization strategy. ${\n error instanceof Error ? error.message : error\n }`,\n severity: 'error',\n });\n }\n };\n\n return (\n <Box pl=\"20px\" pr=\"20px\">\n <Alert\n className={textContainerStyles}\n status=\"warning\"\n icon\n title=\"Changing this setting will affect how your service dependencies are synchronized and may cause data loss.\"\n description={\n <>\n Check the{' '}\n <a\n className={linkStyles}\n href=\"https://pagerduty.github.io/backstage-plugin-docs/index.html\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n >\n documentation\n </a>{' '}\n for more information.\n </>\n }\n />\n <Card\n title=\"Service dependency synchronization preferences\"\n className={cardStyles}\n >\n <Typography variant=\"h6\">\n Service dependency synchronization strategy\n </Typography>\n <RadioGroup\n label=\"Select the main source of truth for your service dependencies\"\n value={selectedServiceDependencyStrategy}\n onChange={value => void handleChange(value as StoreSettings)}\n >\n <Radio value={StoreSettings.backstage}>Backstage</Radio>\n <Radio value={StoreSettings.pagerduty}>PagerDuty</Radio>\n <Radio value={StoreSettings.both}>Both</Radio>\n <Radio value={StoreSettings.disabled}>Disabled</Radio>\n </RadioGroup>\n </Card>\n </Box>\n );\n};\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
2
|
+
import { useState, useEffect } from 'react';
|
|
3
|
+
import { Dialog, DialogHeader, DialogBody, Box, Text, Flex, TextField, DialogFooter, Button } from '@backstage/ui';
|
|
4
|
+
import { makeStyles, createStyles } from '@material-ui/core/styles';
|
|
5
|
+
|
|
6
|
+
const useStyles = makeStyles(
|
|
7
|
+
(theme) => createStyles({
|
|
8
|
+
generalErrorBox: {
|
|
9
|
+
backgroundColor: "rgba(211, 47, 47, 0.08)",
|
|
10
|
+
borderRadius: theme.shape.borderRadius,
|
|
11
|
+
padding: `${theme.spacing(1)}px ${theme.spacing(1.5)}px`,
|
|
12
|
+
marginBottom: theme.spacing(2)
|
|
13
|
+
},
|
|
14
|
+
fieldErrorText: {
|
|
15
|
+
color: theme.palette.error.main,
|
|
16
|
+
fontSize: "0.75rem",
|
|
17
|
+
margin: `${theme.spacing(0.5)}px 0 0 0`
|
|
18
|
+
},
|
|
19
|
+
descriptionLabel: {
|
|
20
|
+
display: "block",
|
|
21
|
+
fontSize: "0.875rem",
|
|
22
|
+
marginBottom: theme.spacing(0.75)
|
|
23
|
+
},
|
|
24
|
+
textarea: {
|
|
25
|
+
width: "100%",
|
|
26
|
+
boxSizing: "border-box",
|
|
27
|
+
padding: `${theme.spacing(1)}px ${theme.spacing(1.5)}px`,
|
|
28
|
+
fontSize: "0.875rem",
|
|
29
|
+
border: `1px solid ${theme.palette.divider}`,
|
|
30
|
+
borderRadius: theme.shape.borderRadius,
|
|
31
|
+
resize: "vertical",
|
|
32
|
+
fontFamily: "inherit",
|
|
33
|
+
color: "inherit",
|
|
34
|
+
backgroundColor: "transparent",
|
|
35
|
+
"&:disabled": {
|
|
36
|
+
backgroundColor: theme.palette.action.disabledBackground
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
})
|
|
40
|
+
);
|
|
41
|
+
const CustomFieldModal = ({
|
|
42
|
+
open,
|
|
43
|
+
saving,
|
|
44
|
+
error,
|
|
45
|
+
onClose,
|
|
46
|
+
onSave,
|
|
47
|
+
mode = "add",
|
|
48
|
+
initialValues
|
|
49
|
+
}) => {
|
|
50
|
+
const classes = useStyles();
|
|
51
|
+
const [formData, setFormData] = useState(
|
|
52
|
+
initialValues ?? { name: "", entityPath: "", description: "" }
|
|
53
|
+
);
|
|
54
|
+
const [localErrors, setLocalErrors] = useState({});
|
|
55
|
+
useEffect(() => {
|
|
56
|
+
if (open) {
|
|
57
|
+
setFormData(initialValues ?? { name: "", entityPath: "", description: "" });
|
|
58
|
+
setLocalErrors({});
|
|
59
|
+
}
|
|
60
|
+
}, [open]);
|
|
61
|
+
useEffect(() => {
|
|
62
|
+
setLocalErrors(error ?? {});
|
|
63
|
+
}, [error]);
|
|
64
|
+
const handleFormChange = (field) => (value) => {
|
|
65
|
+
setFormData((prev) => ({ ...prev, [field]: value }));
|
|
66
|
+
if (field in localErrors) {
|
|
67
|
+
setLocalErrors((prev) => ({ ...prev, [field]: void 0 }));
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
const handleClose = () => {
|
|
71
|
+
setFormData({ name: "", entityPath: "", description: "" });
|
|
72
|
+
setLocalErrors({});
|
|
73
|
+
onClose();
|
|
74
|
+
};
|
|
75
|
+
const handleSave = async () => {
|
|
76
|
+
await onSave(formData);
|
|
77
|
+
};
|
|
78
|
+
const getSaveLabel = () => {
|
|
79
|
+
return mode === "edit" ? "Save" : "Add";
|
|
80
|
+
};
|
|
81
|
+
const saveLabel = getSaveLabel();
|
|
82
|
+
return /* @__PURE__ */ jsxs(
|
|
83
|
+
Dialog,
|
|
84
|
+
{
|
|
85
|
+
isOpen: open,
|
|
86
|
+
onOpenChange: (isOpen) => !isOpen && handleClose(),
|
|
87
|
+
width: 500,
|
|
88
|
+
children: [
|
|
89
|
+
/* @__PURE__ */ jsx(DialogHeader, { children: mode === "edit" ? "Edit Custom Field" : "Add New Custom Field" }),
|
|
90
|
+
/* @__PURE__ */ jsxs(DialogBody, { children: [
|
|
91
|
+
localErrors.general && /* @__PURE__ */ jsx(Box, { className: classes.generalErrorBox, children: /* @__PURE__ */ jsx(Text, { variant: "body-small", color: "danger", children: localErrors.general }) }),
|
|
92
|
+
/* @__PURE__ */ jsxs(Flex, { direction: "column", gap: "4", children: [
|
|
93
|
+
/* @__PURE__ */ jsxs(Box, { children: [
|
|
94
|
+
/* @__PURE__ */ jsx(
|
|
95
|
+
TextField,
|
|
96
|
+
{
|
|
97
|
+
label: "Name",
|
|
98
|
+
value: formData.name,
|
|
99
|
+
onChange: handleFormChange("name"),
|
|
100
|
+
isDisabled: saving,
|
|
101
|
+
isInvalid: !!localErrors.name
|
|
102
|
+
}
|
|
103
|
+
),
|
|
104
|
+
localErrors.name && /* @__PURE__ */ jsx(Text, { as: "p", className: classes.fieldErrorText, children: localErrors.name })
|
|
105
|
+
] }),
|
|
106
|
+
/* @__PURE__ */ jsxs(Box, { children: [
|
|
107
|
+
/* @__PURE__ */ jsx(
|
|
108
|
+
TextField,
|
|
109
|
+
{
|
|
110
|
+
label: "Entity Path",
|
|
111
|
+
value: formData.entityPath,
|
|
112
|
+
onChange: handleFormChange("entityPath"),
|
|
113
|
+
isDisabled: saving,
|
|
114
|
+
isInvalid: !!localErrors.entityPath
|
|
115
|
+
}
|
|
116
|
+
),
|
|
117
|
+
localErrors.entityPath && /* @__PURE__ */ jsx(Text, { as: "p", className: classes.fieldErrorText, children: localErrors.entityPath })
|
|
118
|
+
] }),
|
|
119
|
+
/* @__PURE__ */ jsxs(Box, { children: [
|
|
120
|
+
/* @__PURE__ */ jsx(Text, { as: "label", className: classes.descriptionLabel, children: "Description" }),
|
|
121
|
+
/* @__PURE__ */ jsx(
|
|
122
|
+
"textarea",
|
|
123
|
+
{
|
|
124
|
+
value: formData.description,
|
|
125
|
+
onChange: (e) => handleFormChange("description")(e.target.value),
|
|
126
|
+
disabled: saving,
|
|
127
|
+
rows: 5,
|
|
128
|
+
className: classes.textarea
|
|
129
|
+
}
|
|
130
|
+
)
|
|
131
|
+
] })
|
|
132
|
+
] })
|
|
133
|
+
] }),
|
|
134
|
+
/* @__PURE__ */ jsx(DialogFooter, { children: /* @__PURE__ */ jsxs(Flex, { gap: "2", justify: "end", children: [
|
|
135
|
+
/* @__PURE__ */ jsx(Button, { variant: "secondary", isDisabled: saving, onClick: handleClose, children: "Cancel" }),
|
|
136
|
+
/* @__PURE__ */ jsx(
|
|
137
|
+
Button,
|
|
138
|
+
{
|
|
139
|
+
variant: "primary",
|
|
140
|
+
onClick: handleSave,
|
|
141
|
+
isDisabled: !formData.name || !formData.entityPath || saving,
|
|
142
|
+
children: saveLabel
|
|
143
|
+
}
|
|
144
|
+
)
|
|
145
|
+
] }) })
|
|
146
|
+
]
|
|
147
|
+
}
|
|
148
|
+
);
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
export { CustomFieldModal };
|
|
152
|
+
//# sourceMappingURL=CustomFieldModal.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CustomFieldModal.esm.js","sources":["../../../src/components/PagerDutyPage/CustomFieldModal.tsx"],"sourcesContent":["import { useState, useEffect } from 'react';\nimport {\n Box,\n Button,\n Dialog,\n DialogBody,\n DialogFooter,\n DialogHeader,\n Flex,\n Text,\n TextField,\n} from '@backstage/ui';\nimport { makeStyles, createStyles } from '@material-ui/core/styles';\nimport { BackstageTheme } from '@backstage/theme';\n\nconst useStyles = makeStyles<BackstageTheme>(theme =>\n createStyles({\n generalErrorBox: {\n backgroundColor: 'rgba(211, 47, 47, 0.08)',\n borderRadius: theme.shape.borderRadius,\n padding: `${theme.spacing(1)}px ${theme.spacing(1.5)}px`,\n marginBottom: theme.spacing(2),\n },\n fieldErrorText: {\n color: theme.palette.error.main,\n fontSize: '0.75rem',\n margin: `${theme.spacing(0.5)}px 0 0 0`,\n },\n descriptionLabel: {\n display: 'block',\n fontSize: '0.875rem',\n marginBottom: theme.spacing(0.75),\n },\n textarea: {\n width: '100%',\n boxSizing: 'border-box' as const,\n padding: `${theme.spacing(1)}px ${theme.spacing(1.5)}px`,\n fontSize: '0.875rem',\n border: `1px solid ${theme.palette.divider}`,\n borderRadius: theme.shape.borderRadius,\n resize: 'vertical' as const,\n fontFamily: 'inherit',\n color: 'inherit',\n backgroundColor: 'transparent',\n '&:disabled': {\n backgroundColor: theme.palette.action.disabledBackground,\n },\n },\n }),\n);\n\ninterface FormData {\n name: string;\n entityPath: string;\n description: string;\n}\n\n/** @public */\nexport interface FieldErrors {\n name?: string;\n entityPath?: string;\n general?: string;\n}\n\ninterface CustomFieldModalProps {\n open: boolean;\n saving: boolean;\n error: FieldErrors | null;\n onClose: () => void;\n onSave: (data: FormData) => Promise<void>;\n mode?: 'add' | 'edit';\n initialValues?: FormData;\n}\n\n/** @public */\nexport const CustomFieldModal = ({\n open,\n saving,\n error,\n onClose,\n onSave,\n mode = 'add',\n initialValues,\n}: CustomFieldModalProps) => {\n const classes = useStyles();\n const [formData, setFormData] = useState<FormData>(\n initialValues ?? { name: '', entityPath: '', description: '' },\n );\n const [localErrors, setLocalErrors] = useState<FieldErrors>({});\n\n useEffect(() => {\n if (open) {\n setFormData(initialValues ?? { name: '', entityPath: '', description: '' });\n setLocalErrors({});\n }\n // initialValues is intentionally excluded: the form must only reset when the\n // modal opens, not on every re-render of the parent that creates a new object\n // reference while the modal is already open (e.g. while saving).\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [open]);\n\n useEffect(() => {\n setLocalErrors(error ?? {});\n }, [error]);\n\n const handleFormChange = (field: keyof FormData) => (value: string) => {\n setFormData(prev => ({ ...prev, [field]: value }));\n if (field in localErrors) {\n setLocalErrors(prev => ({ ...prev, [field]: undefined }));\n }\n };\n\n const handleClose = () => {\n setFormData({ name: '', entityPath: '', description: '' });\n setLocalErrors({});\n onClose();\n };\n\n const handleSave = async () => {\n await onSave(formData);\n };\n\n const getSaveLabel = () => {\n return mode === 'edit' ? 'Save' : 'Add';\n };\n const saveLabel = getSaveLabel();\n\n return (\n <Dialog\n isOpen={open}\n onOpenChange={isOpen => !isOpen && handleClose()}\n width={500}\n >\n <DialogHeader>\n {mode === 'edit' ? 'Edit Custom Field' : 'Add New Custom Field'}\n </DialogHeader>\n <DialogBody>\n {localErrors.general && (\n <Box className={classes.generalErrorBox}>\n <Text variant=\"body-small\" color=\"danger\">\n {localErrors.general}\n </Text>\n </Box>\n )}\n <Flex direction=\"column\" gap=\"4\">\n <Box>\n <TextField\n label=\"Name\"\n value={formData.name}\n onChange={handleFormChange('name')}\n isDisabled={saving}\n isInvalid={!!localErrors.name}\n />\n {localErrors.name && (\n <Text as=\"p\" className={classes.fieldErrorText}>\n {localErrors.name}\n </Text>\n )}\n </Box>\n <Box>\n <TextField\n label=\"Entity Path\"\n value={formData.entityPath}\n onChange={handleFormChange('entityPath')}\n isDisabled={saving}\n isInvalid={!!localErrors.entityPath}\n />\n {localErrors.entityPath && (\n <Text as=\"p\" className={classes.fieldErrorText}>\n {localErrors.entityPath}\n </Text>\n )}\n </Box>\n <Box>\n <Text as=\"label\" className={classes.descriptionLabel}>\n Description\n </Text>\n <textarea\n value={formData.description}\n onChange={e => handleFormChange('description')(e.target.value)}\n disabled={saving}\n rows={5}\n className={classes.textarea}\n />\n </Box>\n </Flex>\n </DialogBody>\n <DialogFooter>\n <Flex gap=\"2\" justify=\"end\">\n <Button variant=\"secondary\" isDisabled={saving} onClick={handleClose}>\n Cancel\n </Button>\n <Button\n variant=\"primary\"\n onClick={handleSave}\n isDisabled={!formData.name || !formData.entityPath || saving}\n >\n {saveLabel}\n </Button>\n </Flex>\n </DialogFooter>\n </Dialog>\n );\n};\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { jsx } from 'react/jsx-runtime';
|
|
2
|
+
import { Flex, Button } from '@backstage/ui';
|
|
3
|
+
import { makeStyles } from '@material-ui/core';
|
|
4
|
+
|
|
5
|
+
const useStyles = makeStyles(() => ({
|
|
6
|
+
buttonFirst: {
|
|
7
|
+
borderTopRightRadius: "0px !important",
|
|
8
|
+
borderBottomRightRadius: "0px !important"
|
|
9
|
+
},
|
|
10
|
+
buttonMiddle: {
|
|
11
|
+
borderRadius: "0px !important"
|
|
12
|
+
},
|
|
13
|
+
buttonLast: {
|
|
14
|
+
borderTopLeftRadius: "0px !important",
|
|
15
|
+
borderBottomLeftRadius: "0px !important"
|
|
16
|
+
}
|
|
17
|
+
}));
|
|
18
|
+
function ButtonTabs({
|
|
19
|
+
items,
|
|
20
|
+
value,
|
|
21
|
+
onChange
|
|
22
|
+
}) {
|
|
23
|
+
const classes = useStyles();
|
|
24
|
+
return /* @__PURE__ */ jsx(Flex, { gap: "0", align: "center", children: items.map((item, index) => {
|
|
25
|
+
const isActive = item.key === value;
|
|
26
|
+
const isFirst = index === 0;
|
|
27
|
+
const isLast = index === items.length - 1;
|
|
28
|
+
const isSingle = isFirst && isLast;
|
|
29
|
+
let className;
|
|
30
|
+
if (!isSingle) {
|
|
31
|
+
if (isFirst) className = classes.buttonFirst;
|
|
32
|
+
else if (isLast) className = classes.buttonLast;
|
|
33
|
+
else className = classes.buttonMiddle;
|
|
34
|
+
}
|
|
35
|
+
return /* @__PURE__ */ jsx(
|
|
36
|
+
Button,
|
|
37
|
+
{
|
|
38
|
+
variant: isActive ? "primary" : "secondary",
|
|
39
|
+
size: "small",
|
|
40
|
+
onClick: () => onChange(item.key),
|
|
41
|
+
"aria-pressed": isActive,
|
|
42
|
+
className,
|
|
43
|
+
children: item.label
|
|
44
|
+
},
|
|
45
|
+
item.key
|
|
46
|
+
);
|
|
47
|
+
}) });
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export { ButtonTabs };
|
|
51
|
+
//# sourceMappingURL=ButtonTabs.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ButtonTabs.esm.js","sources":["../../../../src/components/PagerDutyPage/CustomFields/ButtonTabs.tsx"],"sourcesContent":["import { ReactNode } from 'react';\nimport { Button, Flex } from '@backstage/ui';\nimport { makeStyles } from '@material-ui/core';\n\n/** @public */\nexport type ButtonTabItem<TKey extends string = string> = {\n key: TKey;\n label: ReactNode;\n};\n\ninterface ButtonTabsProps<TKey extends string = string> {\n items: ButtonTabItem<TKey>[];\n value: TKey;\n onChange: (key: TKey) => void;\n}\n\nconst useStyles = makeStyles(() => ({\n buttonFirst: {\n borderTopRightRadius: '0px !important',\n borderBottomRightRadius: '0px !important',\n },\n buttonMiddle: {\n borderRadius: '0px !important',\n },\n buttonLast: {\n borderTopLeftRadius: '0px !important',\n borderBottomLeftRadius: '0px !important',\n },\n}));\n\n/** @public */\nexport function ButtonTabs<TKey extends string = string>({\n items,\n value,\n onChange,\n}: ButtonTabsProps<TKey>) {\n const classes = useStyles();\n\n return (\n <Flex gap=\"0\" align=\"center\">\n {items.map((item, index) => {\n const isActive = item.key === value;\n const isFirst = index === 0;\n const isLast = index === items.length - 1;\n const isSingle = isFirst && isLast;\n\n let className: string | undefined;\n if (!isSingle) {\n if (isFirst) className = classes.buttonFirst;\n else if (isLast) className = classes.buttonLast;\n else className = classes.buttonMiddle;\n }\n\n return (\n <Button\n key={item.key}\n variant={isActive ? 'primary' : 'secondary'}\n size=\"small\"\n onClick={() => onChange(item.key)}\n aria-pressed={isActive}\n className={className}\n >\n {item.label}\n </Button>\n );\n })}\n </Flex>\n );\n}\n"],"names":[],"mappings":";;;;AAgBA,MAAM,SAAA,GAAY,WAAW,OAAO;AAAA,EAClC,WAAA,EAAa;AAAA,IACX,oBAAA,EAAsB,gBAAA;AAAA,IACtB,uBAAA,EAAyB;AAAA,GAC3B;AAAA,EACA,YAAA,EAAc;AAAA,IACZ,YAAA,EAAc;AAAA,GAChB;AAAA,EACA,UAAA,EAAY;AAAA,IACV,mBAAA,EAAqB,gBAAA;AAAA,IACrB,sBAAA,EAAwB;AAAA;AAE5B,CAAA,CAAE,CAAA;AAGK,SAAS,UAAA,CAAyC;AAAA,EACvD,KAAA;AAAA,EACA,KAAA;AAAA,EACA;AACF,CAAA,EAA0B;AACxB,EAAA,MAAM,UAAU,SAAA,EAAU;AAE1B,EAAA,uBACE,GAAA,CAAC,IAAA,EAAA,EAAK,GAAA,EAAI,GAAA,EAAI,KAAA,EAAM,UACjB,QAAA,EAAA,KAAA,CAAM,GAAA,CAAI,CAAC,IAAA,EAAM,KAAA,KAAU;AAC1B,IAAA,MAAM,QAAA,GAAW,KAAK,GAAA,KAAQ,KAAA;AAC9B,IAAA,MAAM,UAAU,KAAA,KAAU,CAAA;AAC1B,IAAA,MAAM,MAAA,GAAS,KAAA,KAAU,KAAA,CAAM,MAAA,GAAS,CAAA;AACxC,IAAA,MAAM,WAAW,OAAA,IAAW,MAAA;AAE5B,IAAA,IAAI,SAAA;AACJ,IAAA,IAAI,CAAC,QAAA,EAAU;AACb,MAAA,IAAI,OAAA,cAAqB,OAAA,CAAQ,WAAA;AAAA,WAAA,IACxB,MAAA,cAAoB,OAAA,CAAQ,UAAA;AAAA,uBACpB,OAAA,CAAQ,YAAA;AAAA,IAC3B;AAEA,IAAA,uBACE,GAAA;AAAA,MAAC,MAAA;AAAA,MAAA;AAAA,QAEC,OAAA,EAAS,WAAW,SAAA,GAAY,WAAA;AAAA,QAChC,IAAA,EAAK,OAAA;AAAA,QACL,OAAA,EAAS,MAAM,QAAA,CAAS,IAAA,CAAK,GAAG,CAAA;AAAA,QAChC,cAAA,EAAc,QAAA;AAAA,QACd,SAAA;AAAA,QAEC,QAAA,EAAA,IAAA,CAAK;AAAA,OAAA;AAAA,MAPD,IAAA,CAAK;AAAA,KAQZ;AAAA,EAEJ,CAAC,CAAA,EACH,CAAA;AAEJ;;;;"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
2
|
+
import { Flex, TextField, Text } from '@backstage/ui';
|
|
3
|
+
import { ArrowForward } from '@mui/icons-material';
|
|
4
|
+
import { makeStyles, createStyles } from '@material-ui/core/styles';
|
|
5
|
+
|
|
6
|
+
const useStyles = makeStyles(
|
|
7
|
+
(theme) => createStyles({
|
|
8
|
+
container: {
|
|
9
|
+
padding: theme.spacing(6),
|
|
10
|
+
width: "100%",
|
|
11
|
+
textAlign: "center"
|
|
12
|
+
},
|
|
13
|
+
inputWrapper: {
|
|
14
|
+
width: "8rem",
|
|
15
|
+
filter: "brightness(0.88)"
|
|
16
|
+
},
|
|
17
|
+
inputWrapperDark: {
|
|
18
|
+
width: "8rem",
|
|
19
|
+
filter: "brightness(0.5)"
|
|
20
|
+
}
|
|
21
|
+
})
|
|
22
|
+
);
|
|
23
|
+
function CustomFieldsEmptyState({ message }) {
|
|
24
|
+
const classes = useStyles();
|
|
25
|
+
return /* @__PURE__ */ jsxs(
|
|
26
|
+
Flex,
|
|
27
|
+
{
|
|
28
|
+
direction: "column",
|
|
29
|
+
align: "center",
|
|
30
|
+
justify: "center",
|
|
31
|
+
gap: "3",
|
|
32
|
+
className: classes.container,
|
|
33
|
+
children: [
|
|
34
|
+
/* @__PURE__ */ jsxs(Flex, { align: "center", gap: "3", "aria-hidden": true, children: [
|
|
35
|
+
/* @__PURE__ */ jsx("div", { className: classes.inputWrapper, children: /* @__PURE__ */ jsx(TextField, { value: "", isDisabled: true, "aria-label": "" }) }),
|
|
36
|
+
/* @__PURE__ */ jsx(ArrowForward, { fontSize: "small" }),
|
|
37
|
+
/* @__PURE__ */ jsx("div", { className: classes.inputWrapperDark, children: /* @__PURE__ */ jsx(TextField, { value: "", isDisabled: true, "aria-label": "" }) })
|
|
38
|
+
] }),
|
|
39
|
+
/* @__PURE__ */ jsx(Text, { variant: "body-medium", color: "secondary", children: message })
|
|
40
|
+
]
|
|
41
|
+
}
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export { CustomFieldsEmptyState };
|
|
46
|
+
//# sourceMappingURL=CustomFieldsEmptyState.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CustomFieldsEmptyState.esm.js","sources":["../../../../src/components/PagerDutyPage/CustomFields/CustomFieldsEmptyState.tsx"],"sourcesContent":["import { Flex, Text, TextField } from '@backstage/ui';\nimport { ArrowForward } from '@mui/icons-material';\nimport { makeStyles, createStyles } from '@material-ui/core/styles';\nimport { BackstageTheme } from '@backstage/theme';\n\nconst useStyles = makeStyles<BackstageTheme>(theme =>\n createStyles({\n container: {\n padding: theme.spacing(6),\n width: '100%',\n textAlign: 'center',\n },\n inputWrapper: {\n width: '8rem',\n filter: 'brightness(0.88)'\n },\n inputWrapperDark: {\n width: '8rem',\n filter: 'brightness(0.5)',\n },\n }),\n);\n\ninterface CustomFieldsEmptyStateProps {\n message: string;\n}\n\nexport function CustomFieldsEmptyState({ message }: CustomFieldsEmptyStateProps) {\n const classes = useStyles();\n return (\n <Flex\n direction=\"column\"\n align=\"center\"\n justify=\"center\"\n gap=\"3\"\n className={classes.container}\n >\n <Flex align=\"center\" gap=\"3\" aria-hidden>\n <div className={classes.inputWrapper}>\n <TextField value=\"\" isDisabled aria-label=\"\" />\n </div>\n <ArrowForward fontSize=\"small\" />\n <div className={classes.inputWrapperDark}>\n <TextField value=\"\" isDisabled aria-label=\"\" />\n </div>\n </Flex>\n <Text variant=\"body-medium\" color=\"secondary\">\n {message}\n </Text>\n </Flex>\n );\n}\n"],"names":[],"mappings":";;;;;AAKA,MAAM,SAAA,GAAY,UAAA;AAAA,EAA2B,WAC3C,YAAA,CAAa;AAAA,IACX,SAAA,EAAW;AAAA,MACT,OAAA,EAAS,KAAA,CAAM,OAAA,CAAQ,CAAC,CAAA;AAAA,MACxB,KAAA,EAAO,MAAA;AAAA,MACP,SAAA,EAAW;AAAA,KACb;AAAA,IACA,YAAA,EAAc;AAAA,MACZ,KAAA,EAAO,MAAA;AAAA,MACP,MAAA,EAAQ;AAAA,KACV;AAAA,IACA,gBAAA,EAAkB;AAAA,MAChB,KAAA,EAAO,MAAA;AAAA,MACP,MAAA,EAAQ;AAAA;AACV,GACD;AACH,CAAA;AAMO,SAAS,sBAAA,CAAuB,EAAE,OAAA,EAAQ,EAAgC;AAC/E,EAAA,MAAM,UAAU,SAAA,EAAU;AAC1B,EAAA,uBACE,IAAA;AAAA,IAAC,IAAA;AAAA,IAAA;AAAA,MACC,SAAA,EAAU,QAAA;AAAA,MACV,KAAA,EAAM,QAAA;AAAA,MACN,OAAA,EAAQ,QAAA;AAAA,MACR,GAAA,EAAI,GAAA;AAAA,MACJ,WAAW,OAAA,CAAQ,SAAA;AAAA,MAEnB,QAAA,EAAA;AAAA,wBAAA,IAAA,CAAC,QAAK,KAAA,EAAM,QAAA,EAAS,GAAA,EAAI,GAAA,EAAI,eAAW,IAAA,EACtC,QAAA,EAAA;AAAA,0BAAA,GAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAW,OAAA,CAAQ,YAAA,EACtB,QAAA,kBAAA,GAAA,CAAC,SAAA,EAAA,EAAU,KAAA,EAAM,EAAA,EAAG,UAAA,EAAU,IAAA,EAAC,YAAA,EAAW,EAAA,EAAG,CAAA,EAC/C,CAAA;AAAA,0BACA,GAAA,CAAC,YAAA,EAAA,EAAa,QAAA,EAAS,OAAA,EAAQ,CAAA;AAAA,0BAC/B,GAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAW,OAAA,CAAQ,gBAAA,EACtB,QAAA,kBAAA,GAAA,CAAC,SAAA,EAAA,EAAU,KAAA,EAAM,EAAA,EAAG,UAAA,EAAU,IAAA,EAAC,YAAA,EAAW,IAAG,CAAA,EAC/C;AAAA,SAAA,EACF,CAAA;AAAA,4BACC,IAAA,EAAA,EAAK,OAAA,EAAQ,aAAA,EAAc,KAAA,EAAM,aAC/B,QAAA,EAAA,OAAA,EACH;AAAA;AAAA;AAAA,GACF;AAEJ;;;;"}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
2
|
+
import { useState, useCallback, useMemo } from 'react';
|
|
3
|
+
import { useQueryClient, useQuery } from '@tanstack/react-query';
|
|
4
|
+
import { CellText, MenuTrigger, ButtonIcon, Menu, MenuItem, Table } from '@backstage/ui';
|
|
5
|
+
import { MoreVert, Edit, ToggleOn, ToggleOff, Delete } from '@mui/icons-material';
|
|
6
|
+
import Snackbar from '@mui/material/Snackbar';
|
|
7
|
+
import { Alert } from '@material-ui/lab';
|
|
8
|
+
import { useApi } from '@backstage/core-plugin-api';
|
|
9
|
+
import { pagerDutyApiRef } from '../../../api/client.esm.js';
|
|
10
|
+
import { CustomFieldModal } from '../CustomFieldModal.esm.js';
|
|
11
|
+
import { TableSkeleton } from '../MappingsTable/TableSkeleton.esm.js';
|
|
12
|
+
import { useAccountContext } from '../AccountContext.esm.js';
|
|
13
|
+
import { toFieldErrors } from './customFieldErrors.esm.js';
|
|
14
|
+
import { CustomFieldsEmptyState } from './CustomFieldsEmptyState.esm.js';
|
|
15
|
+
import { DeleteCustomFieldDialog } from './DeleteCustomFieldDialog.esm.js';
|
|
16
|
+
|
|
17
|
+
const CustomFieldsTabPanel = () => {
|
|
18
|
+
const pagerDutyApi = useApi(pagerDutyApiRef);
|
|
19
|
+
const queryClient = useQueryClient();
|
|
20
|
+
const { selectedAccount } = useAccountContext();
|
|
21
|
+
const account = selectedAccount || void 0;
|
|
22
|
+
const [selectedCustomField, setSelectedCustomField] = useState(null);
|
|
23
|
+
const [isEditModalOpen, setIsEditModalOpen] = useState(false);
|
|
24
|
+
const [editSaving, setEditSaving] = useState(false);
|
|
25
|
+
const [editError, setEditError] = useState(null);
|
|
26
|
+
const [toggleError, setToggleError] = useState(null);
|
|
27
|
+
const [fieldToDelete, setFieldToDelete] = useState(null);
|
|
28
|
+
const { data: customFieldsData, isLoading } = useQuery({
|
|
29
|
+
queryKey: ["pagerduty", "customFields", account ?? ""],
|
|
30
|
+
queryFn: () => pagerDutyApi.getCustomFields(account)
|
|
31
|
+
});
|
|
32
|
+
const customFields = customFieldsData?.customFields ?? [];
|
|
33
|
+
const handleOpenEditModal = useCallback((field) => {
|
|
34
|
+
setSelectedCustomField(field);
|
|
35
|
+
setEditError(null);
|
|
36
|
+
setIsEditModalOpen(true);
|
|
37
|
+
}, []);
|
|
38
|
+
const handleCloseEditModal = () => {
|
|
39
|
+
setIsEditModalOpen(false);
|
|
40
|
+
setSelectedCustomField(null);
|
|
41
|
+
setEditError(null);
|
|
42
|
+
};
|
|
43
|
+
const handleUpdateCustomField = async (formData) => {
|
|
44
|
+
if (!selectedCustomField) return;
|
|
45
|
+
const fieldId = selectedCustomField.id;
|
|
46
|
+
setEditSaving(true);
|
|
47
|
+
setEditError(null);
|
|
48
|
+
const result = await pagerDutyApi.updateCustomField(fieldId, formData, account);
|
|
49
|
+
if (result.status === "ok") {
|
|
50
|
+
queryClient.invalidateQueries({ queryKey: ["pagerduty", "customFields", account ?? ""] });
|
|
51
|
+
setIsEditModalOpen(false);
|
|
52
|
+
setSelectedCustomField(null);
|
|
53
|
+
} else {
|
|
54
|
+
setEditError(toFieldErrors(result.error));
|
|
55
|
+
}
|
|
56
|
+
setEditSaving(false);
|
|
57
|
+
};
|
|
58
|
+
const handleToggleEnabled = useCallback(async (field) => {
|
|
59
|
+
const nextEnabled = !field.pagerdutyCustomFieldEnabled;
|
|
60
|
+
const result = await pagerDutyApi.setCustomFieldEnabled(field.id, nextEnabled, account);
|
|
61
|
+
if (result.status === "ok") {
|
|
62
|
+
queryClient.invalidateQueries({ queryKey: ["pagerduty", "customFields", account ?? ""] });
|
|
63
|
+
} else {
|
|
64
|
+
setToggleError(result.error);
|
|
65
|
+
}
|
|
66
|
+
}, [pagerDutyApi, account, queryClient]);
|
|
67
|
+
const columnConfig = useMemo(() => [
|
|
68
|
+
{ id: "name", label: "Custom Field", isRowHeader: true, isSortable: false, cell: (item) => /* @__PURE__ */ jsx(CellText, { title: item.pagerdutyCustomFieldDisplayName }) },
|
|
69
|
+
{ id: "entityPath", label: "Entity Path", isRowHeader: false, isSortable: false, cell: (item) => /* @__PURE__ */ jsx(CellText, { title: item.backstageEntityMappingPath }) },
|
|
70
|
+
{ id: "description", label: "Description", isRowHeader: false, isSortable: false, cell: (item) => /* @__PURE__ */ jsx(CellText, { title: item.description ?? "" }) },
|
|
71
|
+
{
|
|
72
|
+
id: "actions",
|
|
73
|
+
label: "",
|
|
74
|
+
isRowHeader: false,
|
|
75
|
+
isSortable: false,
|
|
76
|
+
cell: (item) => /* @__PURE__ */ jsx(CellText, { title: "", leadingIcon: /* @__PURE__ */ jsxs(MenuTrigger, { children: [
|
|
77
|
+
/* @__PURE__ */ jsx(ButtonIcon, { icon: /* @__PURE__ */ jsx(MoreVert, { fontSize: "small" }), "aria-label": "actions", variant: "tertiary", size: "small" }),
|
|
78
|
+
/* @__PURE__ */ jsxs(Menu, { children: [
|
|
79
|
+
/* @__PURE__ */ jsx(MenuItem, { iconStart: /* @__PURE__ */ jsx(Edit, { fontSize: "small" }), onAction: () => handleOpenEditModal(item), children: "Edit" }),
|
|
80
|
+
/* @__PURE__ */ jsx(
|
|
81
|
+
MenuItem,
|
|
82
|
+
{
|
|
83
|
+
iconStart: item.pagerdutyCustomFieldEnabled ? /* @__PURE__ */ jsx(ToggleOn, { fontSize: "small" }) : /* @__PURE__ */ jsx(ToggleOff, { fontSize: "small" }),
|
|
84
|
+
onAction: () => handleToggleEnabled(item),
|
|
85
|
+
children: item.pagerdutyCustomFieldEnabled ? "Disable" : "Enable"
|
|
86
|
+
}
|
|
87
|
+
),
|
|
88
|
+
/* @__PURE__ */ jsx(MenuItem, { iconStart: /* @__PURE__ */ jsx(Delete, { fontSize: "small" }), onAction: () => setFieldToDelete(item), children: "Delete" })
|
|
89
|
+
] })
|
|
90
|
+
] }) })
|
|
91
|
+
}
|
|
92
|
+
], [handleOpenEditModal, handleToggleEnabled]);
|
|
93
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
94
|
+
isLoading ? /* @__PURE__ */ jsx(
|
|
95
|
+
TableSkeleton,
|
|
96
|
+
{
|
|
97
|
+
columns: [
|
|
98
|
+
{ label: "Custom Field", width: "30%" },
|
|
99
|
+
{ label: "Entity Path", width: "25%" },
|
|
100
|
+
{ label: "Description", width: "35%" },
|
|
101
|
+
{ label: "", width: "10%" }
|
|
102
|
+
]
|
|
103
|
+
}
|
|
104
|
+
) : /* @__PURE__ */ jsx(
|
|
105
|
+
Table,
|
|
106
|
+
{
|
|
107
|
+
columnConfig,
|
|
108
|
+
data: customFields,
|
|
109
|
+
pagination: { type: "none" },
|
|
110
|
+
rowConfig: {
|
|
111
|
+
getIsDisabled: (item) => !item.pagerdutyCustomFieldEnabled
|
|
112
|
+
},
|
|
113
|
+
emptyState: /* @__PURE__ */ jsx(CustomFieldsEmptyState, { message: "No custom fields have been added" })
|
|
114
|
+
}
|
|
115
|
+
),
|
|
116
|
+
/* @__PURE__ */ jsx(
|
|
117
|
+
CustomFieldModal,
|
|
118
|
+
{
|
|
119
|
+
open: isEditModalOpen,
|
|
120
|
+
saving: editSaving,
|
|
121
|
+
error: editError,
|
|
122
|
+
onClose: handleCloseEditModal,
|
|
123
|
+
onSave: handleUpdateCustomField,
|
|
124
|
+
mode: "edit",
|
|
125
|
+
initialValues: selectedCustomField ? {
|
|
126
|
+
name: selectedCustomField.pagerdutyCustomFieldDisplayName,
|
|
127
|
+
entityPath: selectedCustomField.backstageEntityMappingPath,
|
|
128
|
+
description: selectedCustomField.description ?? ""
|
|
129
|
+
} : void 0
|
|
130
|
+
}
|
|
131
|
+
),
|
|
132
|
+
/* @__PURE__ */ jsx(
|
|
133
|
+
DeleteCustomFieldDialog,
|
|
134
|
+
{
|
|
135
|
+
field: fieldToDelete,
|
|
136
|
+
onClose: () => setFieldToDelete(null),
|
|
137
|
+
onSuccess: () => setFieldToDelete(null)
|
|
138
|
+
}
|
|
139
|
+
),
|
|
140
|
+
/* @__PURE__ */ jsx(
|
|
141
|
+
Snackbar,
|
|
142
|
+
{
|
|
143
|
+
open: toggleError !== null,
|
|
144
|
+
autoHideDuration: 5e3,
|
|
145
|
+
onClose: () => setToggleError(null),
|
|
146
|
+
anchorOrigin: { vertical: "bottom", horizontal: "right" },
|
|
147
|
+
children: /* @__PURE__ */ jsx(Alert, { severity: "error", onClose: () => setToggleError(null), children: toggleError })
|
|
148
|
+
}
|
|
149
|
+
)
|
|
150
|
+
] });
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
export { CustomFieldsTabPanel };
|
|
154
|
+
//# sourceMappingURL=CustomFieldsTabPanel.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CustomFieldsTabPanel.esm.js","sources":["../../../../src/components/PagerDutyPage/CustomFields/CustomFieldsTabPanel.tsx"],"sourcesContent":["import { useState, useMemo, useCallback } from 'react';\nimport { useQuery, useQueryClient } from '@tanstack/react-query';\nimport {\n ButtonIcon,\n CellText,\n Menu,\n MenuItem,\n MenuTrigger,\n Table,\n type ColumnConfig,\n} from '@backstage/ui';\nimport { Delete, Edit, MoreVert, ToggleOff, ToggleOn } from '@mui/icons-material';\nimport Snackbar from '@mui/material/Snackbar';\nimport { Alert } from '@material-ui/lab';\nimport { useApi } from '@backstage/core-plugin-api';\nimport { pagerDutyApiRef } from '../../../api';\nimport { BackstageCustomField } from '@pagerduty/backstage-plugin-common';\nimport { CustomFieldModal, FieldErrors } from '../CustomFieldModal';\nimport { TableSkeleton } from '../MappingsTable/TableSkeleton';\nimport { useAccountContext } from '../AccountContext';\nimport { toFieldErrors } from './customFieldErrors';\nimport { CustomFieldsEmptyState } from './CustomFieldsEmptyState';\nimport { DeleteCustomFieldDialog } from './DeleteCustomFieldDialog';\n\nexport const CustomFieldsTabPanel = () => {\n const pagerDutyApi = useApi(pagerDutyApiRef);\n const queryClient = useQueryClient();\n const { selectedAccount } = useAccountContext();\n const account = selectedAccount || undefined;\n const [selectedCustomField, setSelectedCustomField] = useState<BackstageCustomField | null>(null);\n const [isEditModalOpen, setIsEditModalOpen] = useState(false);\n const [editSaving, setEditSaving] = useState(false);\n const [editError, setEditError] = useState<FieldErrors | null>(null);\n const [toggleError, setToggleError] = useState<string | null>(null);\n const [fieldToDelete, setFieldToDelete] = useState<BackstageCustomField | null>(null);\n\n const { data: customFieldsData, isLoading } = useQuery({\n queryKey: ['pagerduty', 'customFields', account ?? ''],\n queryFn: () => pagerDutyApi.getCustomFields(account),\n });\n const customFields = customFieldsData?.customFields ?? [];\n\n const handleOpenEditModal = useCallback((field: BackstageCustomField) => {\n setSelectedCustomField(field);\n setEditError(null);\n setIsEditModalOpen(true);\n }, []);\n\n const handleCloseEditModal = () => {\n setIsEditModalOpen(false);\n setSelectedCustomField(null);\n setEditError(null);\n };\n\n const handleUpdateCustomField = async (formData: { name: string; entityPath: string; description: string }) => {\n if (!selectedCustomField) return;\n const fieldId = selectedCustomField.id;\n setEditSaving(true);\n setEditError(null);\n const result = await pagerDutyApi.updateCustomField(fieldId, formData, account);\n if (result.status === 'ok') {\n queryClient.invalidateQueries({ queryKey: ['pagerduty', 'customFields', account ?? ''] });\n setIsEditModalOpen(false);\n setSelectedCustomField(null);\n } else {\n setEditError(toFieldErrors(result.error));\n }\n setEditSaving(false);\n };\n\n const handleToggleEnabled = useCallback(async (field: BackstageCustomField) => {\n const nextEnabled = !field.pagerdutyCustomFieldEnabled;\n const result = await pagerDutyApi.setCustomFieldEnabled(field.id, nextEnabled, account);\n if (result.status === 'ok') {\n queryClient.invalidateQueries({ queryKey: ['pagerduty', 'customFields', account ?? ''] });\n } else {\n setToggleError(result.error);\n }\n }, [pagerDutyApi, account, queryClient]);\n\n const columnConfig: ColumnConfig<BackstageCustomField>[] = useMemo(() => [\n { id: 'name', label: 'Custom Field', isRowHeader: true, isSortable: false, cell: item => <CellText title={item.pagerdutyCustomFieldDisplayName} /> },\n { id: 'entityPath', label: 'Entity Path', isRowHeader: false, isSortable: false, cell: item => <CellText title={item.backstageEntityMappingPath} /> },\n { id: 'description', label: 'Description', isRowHeader: false, isSortable: false, cell: item => <CellText title={item.description ?? ''} /> },\n {\n id: 'actions', label: '', isRowHeader: false, isSortable: false,\n cell: item => (\n <CellText title=\"\" leadingIcon={\n <MenuTrigger>\n <ButtonIcon icon={<MoreVert fontSize=\"small\" />} aria-label=\"actions\" variant=\"tertiary\" size=\"small\" />\n <Menu>\n <MenuItem iconStart={<Edit fontSize=\"small\" />} onAction={() => handleOpenEditModal(item)}>Edit</MenuItem>\n <MenuItem\n iconStart={\n item.pagerdutyCustomFieldEnabled ? (\n <ToggleOn fontSize=\"small\" />\n ) : (\n <ToggleOff fontSize=\"small\" />\n )\n }\n onAction={() => handleToggleEnabled(item)}\n >\n {item.pagerdutyCustomFieldEnabled ? 'Disable' : 'Enable'}\n </MenuItem>\n <MenuItem iconStart={<Delete fontSize=\"small\" />} onAction={() => setFieldToDelete(item)}>Delete</MenuItem>\n </Menu>\n </MenuTrigger>\n } />\n ),\n },\n ], [handleOpenEditModal, handleToggleEnabled]);\n\n return (\n <>\n {isLoading ? (\n <TableSkeleton\n columns={[\n { label: 'Custom Field', width: '30%' },\n { label: 'Entity Path', width: '25%' },\n { label: 'Description', width: '35%' },\n { label: '', width: '10%' },\n ]}\n />\n ) : (\n <Table\n columnConfig={columnConfig}\n data={customFields}\n pagination={{ type: 'none' }}\n rowConfig={{\n getIsDisabled: item => !item.pagerdutyCustomFieldEnabled,\n }}\n emptyState={\n <CustomFieldsEmptyState message='No custom fields have been added' />\n }\n />\n )}\n\n <CustomFieldModal\n open={isEditModalOpen}\n saving={editSaving}\n error={editError}\n onClose={handleCloseEditModal}\n onSave={handleUpdateCustomField}\n mode=\"edit\"\n initialValues={selectedCustomField ? {\n name: selectedCustomField.pagerdutyCustomFieldDisplayName,\n entityPath: selectedCustomField.backstageEntityMappingPath,\n description: selectedCustomField.description ?? '',\n } : undefined}\n />\n\n <DeleteCustomFieldDialog\n field={fieldToDelete}\n onClose={() => setFieldToDelete(null)}\n onSuccess={() => setFieldToDelete(null)}\n />\n\n <Snackbar\n open={toggleError !== null}\n autoHideDuration={5000}\n onClose={() => setToggleError(null)}\n anchorOrigin={{ vertical: 'bottom', horizontal: 'right' }}\n >\n <Alert severity=\"error\" onClose={() => setToggleError(null)}>\n {toggleError}\n </Alert>\n </Snackbar>\n </>\n );\n};\n"],"names":[],"mappings":"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"}
|