@backstage-community/plugin-servicenow 1.0.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.
Files changed (56) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +34 -0
  3. package/dist/api/ServiceNowBackendClient.esm.js +45 -0
  4. package/dist/api/ServiceNowBackendClient.esm.js.map +1 -0
  5. package/dist/components/Servicenow/ClosedIcon.esm.js +14 -0
  6. package/dist/components/Servicenow/ClosedIcon.esm.js.map +1 -0
  7. package/dist/components/Servicenow/CustomSvgIcon.esm.js +13 -0
  8. package/dist/components/Servicenow/CustomSvgIcon.esm.js.map +1 -0
  9. package/dist/components/Servicenow/InProgressIcon.esm.js +14 -0
  10. package/dist/components/Servicenow/InProgressIcon.esm.js.map +1 -0
  11. package/dist/components/Servicenow/IncidentsFilter.esm.js +69 -0
  12. package/dist/components/Servicenow/IncidentsFilter.esm.js.map +1 -0
  13. package/dist/components/Servicenow/IncidentsListColumns.esm.js +43 -0
  14. package/dist/components/Servicenow/IncidentsListColumns.esm.js.map +1 -0
  15. package/dist/components/Servicenow/IncidentsTableBody.esm.js +28 -0
  16. package/dist/components/Servicenow/IncidentsTableBody.esm.js.map +1 -0
  17. package/dist/components/Servicenow/IncidentsTableHeader.esm.js +44 -0
  18. package/dist/components/Servicenow/IncidentsTableHeader.esm.js.map +1 -0
  19. package/dist/components/Servicenow/IncidentsTableRow.esm.js +54 -0
  20. package/dist/components/Servicenow/IncidentsTableRow.esm.js.map +1 -0
  21. package/dist/components/Servicenow/ModerateIcon.esm.js +14 -0
  22. package/dist/components/Servicenow/ModerateIcon.esm.js.map +1 -0
  23. package/dist/components/Servicenow/ServicenowContent.esm.js +196 -0
  24. package/dist/components/Servicenow/ServicenowContent.esm.js.map +1 -0
  25. package/dist/components/Servicenow/index.esm.js +2 -0
  26. package/dist/components/Servicenow/index.esm.js.map +1 -0
  27. package/dist/components/shared-components/IncidentEnumFilter.esm.js +79 -0
  28. package/dist/components/shared-components/IncidentEnumFilter.esm.js.map +1 -0
  29. package/dist/hooks/useDebouncedValue.esm.js +13 -0
  30. package/dist/hooks/useDebouncedValue.esm.js.map +1 -0
  31. package/dist/hooks/useQueryArrayFilter.esm.js +38 -0
  32. package/dist/hooks/useQueryArrayFilter.esm.js.map +1 -0
  33. package/dist/hooks/useQueryArrayState.esm.js +44 -0
  34. package/dist/hooks/useQueryArrayState.esm.js.map +1 -0
  35. package/dist/hooks/useQueryHelpers.esm.js +24 -0
  36. package/dist/hooks/useQueryHelpers.esm.js.map +1 -0
  37. package/dist/hooks/useQueryState.esm.js +50 -0
  38. package/dist/hooks/useQueryState.esm.js.map +1 -0
  39. package/dist/hooks/useUserEmail.esm.js +22 -0
  40. package/dist/hooks/useUserEmail.esm.js.map +1 -0
  41. package/dist/index.d.ts +23 -0
  42. package/dist/index.esm.js +2 -0
  43. package/dist/index.esm.js.map +1 -0
  44. package/dist/plugin.esm.js +43 -0
  45. package/dist/plugin.esm.js.map +1 -0
  46. package/dist/routes.esm.js +8 -0
  47. package/dist/routes.esm.js.map +1 -0
  48. package/dist/types.esm.js +13 -0
  49. package/dist/types.esm.js.map +1 -0
  50. package/dist/utils/incidentUtils.esm.js +58 -0
  51. package/dist/utils/incidentUtils.esm.js.map +1 -0
  52. package/dist/utils/queryParamsUtils.esm.js +37 -0
  53. package/dist/utils/queryParamsUtils.esm.js.map +1 -0
  54. package/dist/utils/stringUtils.esm.js +13 -0
  55. package/dist/utils/stringUtils.esm.js.map +1 -0
  56. package/package.json +88 -0
@@ -0,0 +1,196 @@
1
+ import { jsx, jsxs } from 'react/jsx-runtime';
2
+ import { useState, useEffect } from 'react';
3
+ import { useApi, identityApiRef } from '@backstage/core-plugin-api';
4
+ import { useEntity, CatalogFilterLayout } from '@backstage/plugin-catalog-react';
5
+ import { Table } from '@backstage/core-components';
6
+ import Box from '@mui/material/Box';
7
+ import TablePagination from '@mui/material/TablePagination';
8
+ import { IncidentsFilter } from './IncidentsFilter.esm.js';
9
+ import { IncidentsListColumns } from './IncidentsListColumns.esm.js';
10
+ import { IncidentsTableBody } from './IncidentsTableBody.esm.js';
11
+ import { IncidentsTableHeader } from './IncidentsTableHeader.esm.js';
12
+ import { IncidentTableFieldEnum } from '../../types.esm.js';
13
+ import { SortingOrderEnum, ServiceAnnotationFieldName } from '@backstage-community/plugin-servicenow-common';
14
+ import { buildIncidentQueryParams } from '../../utils/queryParamsUtils.esm.js';
15
+ import { useDebouncedValue } from '../../hooks/useDebouncedValue.esm.js';
16
+ import { useQueryState } from '../../hooks/useQueryState.esm.js';
17
+ import { useQueryArrayState } from '../../hooks/useQueryArrayState.esm.js';
18
+ import { serviceNowApiRef } from '../../api/ServiceNowBackendClient.esm.js';
19
+ import { useUserEmail } from '../../hooks/useUserEmail.esm.js';
20
+ import CircularProgress from '@mui/material/CircularProgress';
21
+ import TableBody from '@mui/material/TableBody';
22
+ import TableRow from '@mui/material/TableRow';
23
+ import TableCell from '@mui/material/TableCell';
24
+ import { useUpdateQueryParams } from '../../hooks/useQueryHelpers.esm.js';
25
+
26
+ const ServicenowContent = () => {
27
+ const { entity } = useEntity();
28
+ const serviceNowApi = useApi(serviceNowApiRef);
29
+ const [search, setSearch] = useQueryState("search", "");
30
+ const [input, setInput] = useState(search);
31
+ const debouncedInput = useDebouncedValue(input, 300);
32
+ const [order, setOrder] = useQueryState("order", SortingOrderEnum.Asc);
33
+ const [orderBy, setOrderBy] = useQueryState(
34
+ "orderBy",
35
+ IncidentTableFieldEnum.Number
36
+ );
37
+ const [rowsPerPage] = useQueryState("limit", 5);
38
+ const [offset, setOffset] = useQueryState("offset", 0);
39
+ const [state] = useQueryArrayState("state", []);
40
+ const [priority] = useQueryArrayState("priority", []);
41
+ const pageNumber = Math.floor(offset / rowsPerPage);
42
+ const [count, setCount] = useState(0);
43
+ const [incidentsData, setIncidentsData] = useState([]);
44
+ const [loading, setLoading] = useState(false);
45
+ const [error, setError] = useState(null);
46
+ const updateQueryParams = useUpdateQueryParams();
47
+ const entityId = entity.metadata.annotations?.[ServiceAnnotationFieldName];
48
+ const kind = entity.kind.toLocaleLowerCase("en-US");
49
+ const userEmail = useUserEmail(kind);
50
+ const identityApi = useApi(identityApiRef);
51
+ useEffect(() => {
52
+ identityApi.getBackstageIdentity().then((identity) => {
53
+ if (identity.userEntityRef) {
54
+ localStorage.setItem("userEntityRef", identity.userEntityRef);
55
+ }
56
+ });
57
+ }, [identityApi]);
58
+ useEffect(() => {
59
+ if (debouncedInput !== search) {
60
+ setSearch(debouncedInput);
61
+ setOffset(0);
62
+ }
63
+ }, [debouncedInput, search, setSearch, setOffset]);
64
+ useEffect(() => {
65
+ async function fetchIncidents() {
66
+ setLoading(true);
67
+ setError(null);
68
+ if (!userEmail && !entityId) {
69
+ setIncidentsData([]);
70
+ setLoading(false);
71
+ return;
72
+ }
73
+ try {
74
+ const queryParams = buildIncidentQueryParams({
75
+ limit: rowsPerPage,
76
+ offset,
77
+ order,
78
+ orderBy,
79
+ search,
80
+ priority: priority ?? void 0,
81
+ state: state ?? void 0,
82
+ userEmail,
83
+ entityId
84
+ });
85
+ const { incidents, totalCount } = await serviceNowApi.getIncidents(
86
+ queryParams
87
+ );
88
+ setIncidentsData(incidents);
89
+ setCount(totalCount);
90
+ } catch (e) {
91
+ setError(e.message);
92
+ setIncidentsData([]);
93
+ } finally {
94
+ setLoading(false);
95
+ }
96
+ }
97
+ fetchIncidents();
98
+ }, [
99
+ rowsPerPage,
100
+ offset,
101
+ order,
102
+ orderBy,
103
+ search,
104
+ serviceNowApi,
105
+ entityId,
106
+ priority,
107
+ state,
108
+ userEmail
109
+ ]);
110
+ const handleRowsPerPageChange = (event) => {
111
+ const newLimit = parseInt(event.target.value, 10);
112
+ updateQueryParams({
113
+ limit: String(newLimit),
114
+ offset: "0"
115
+ });
116
+ };
117
+ const handlePageChange = (_event, page) => {
118
+ setOffset(page * rowsPerPage);
119
+ };
120
+ const handleRequestSort = (_event, property) => {
121
+ const isAsc = orderBy === property && order === SortingOrderEnum.Asc;
122
+ setOrder(isAsc ? SortingOrderEnum.Desc : SortingOrderEnum.Asc);
123
+ setOrderBy(property);
124
+ setOffset(0);
125
+ };
126
+ const handleSearch = (str) => {
127
+ setInput(str);
128
+ };
129
+ const IncidentsTableHeaderComponent = () => /* @__PURE__ */ jsx(
130
+ IncidentsTableHeader,
131
+ {
132
+ order,
133
+ orderBy,
134
+ onRequestSort: handleRequestSort
135
+ }
136
+ );
137
+ const IncidentsTableBodyComponent = () => loading ? /* @__PURE__ */ jsx(TableBody, { children: /* @__PURE__ */ jsx(TableRow, { children: /* @__PURE__ */ jsx(TableCell, { colSpan: IncidentsListColumns.length, align: "center", children: /* @__PURE__ */ jsx(Box, { sx: { py: 3 }, children: /* @__PURE__ */ jsx(CircularProgress, {}) }) }) }) }) : /* @__PURE__ */ jsx(IncidentsTableBody, { rows: incidentsData });
138
+ const TablePaginationComponent = () => {
139
+ return /* @__PURE__ */ jsx(
140
+ TablePagination,
141
+ {
142
+ rowsPerPageOptions: [5, 10, 20, 50, 100].map((n) => ({
143
+ value: n,
144
+ label: `${n} rows`
145
+ })),
146
+ component: "div",
147
+ sx: { mr: 1 },
148
+ count: count ?? 0,
149
+ rowsPerPage,
150
+ page: pageNumber,
151
+ onPageChange: handlePageChange,
152
+ onRowsPerPageChange: handleRowsPerPageChange,
153
+ labelRowsPerPage: null,
154
+ showFirstButton: true,
155
+ showLastButton: true
156
+ }
157
+ );
158
+ };
159
+ return /* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsxs(CatalogFilterLayout, { children: [
160
+ /* @__PURE__ */ jsx(CatalogFilterLayout.Filters, { children: /* @__PURE__ */ jsx(IncidentsFilter, {}) }),
161
+ /* @__PURE__ */ jsx(CatalogFilterLayout.Content, { children: error ? /* @__PURE__ */ jsxs(Box, { sx: { padding: 2, color: "error.main" }, children: [
162
+ "Error loading incidents: ",
163
+ error
164
+ ] }) : /* @__PURE__ */ jsx(
165
+ Table,
166
+ {
167
+ data: loading ? [] : incidentsData,
168
+ columns: IncidentsListColumns,
169
+ onSearchChange: handleSearch,
170
+ title: count === 0 ? "ServiceNow tickets" : `ServiceNow tickets (${count})`,
171
+ localization: { toolbar: { searchPlaceholder: "Search" } },
172
+ components: {
173
+ Header: IncidentsTableHeaderComponent,
174
+ Body: IncidentsTableBodyComponent,
175
+ Pagination: TablePaginationComponent
176
+ },
177
+ emptyContent: loading ? null : /* @__PURE__ */ jsx(
178
+ Box,
179
+ {
180
+ "data-testid": "no-incidents-found",
181
+ sx: {
182
+ padding: 2,
183
+ display: "flex",
184
+ justifyContent: "center"
185
+ },
186
+ children: "No records found"
187
+ }
188
+ ),
189
+ isLoading: loading
190
+ }
191
+ ) })
192
+ ] }) });
193
+ };
194
+
195
+ export { ServicenowContent };
196
+ //# sourceMappingURL=ServicenowContent.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ServicenowContent.esm.js","sources":["../../../src/components/Servicenow/ServicenowContent.tsx"],"sourcesContent":["/*\n * Copyright 2025 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 */\n\nimport { useEffect, useState, MouseEvent, ChangeEvent } from 'react';\nimport { identityApiRef, useApi } from '@backstage/core-plugin-api';\n\nimport {\n CatalogFilterLayout,\n useEntity,\n} from '@backstage/plugin-catalog-react';\nimport { Table } from '@backstage/core-components';\nimport Box from '@mui/material/Box';\nimport TablePagination from '@mui/material/TablePagination';\n\nimport { IncidentsFilter } from './IncidentsFilter';\nimport { IncidentsListColumns } from './IncidentsListColumns';\nimport { IncidentsTableBody } from './IncidentsTableBody';\nimport { IncidentsTableHeader } from './IncidentsTableHeader';\nimport {\n IncidentTableFieldEnum,\n type IncidentsData,\n type IncidentTableField,\n} from '../../types';\nimport {\n Order,\n SortingOrderEnum,\n ServiceAnnotationFieldName,\n} from '@backstage-community/plugin-servicenow-common';\nimport { buildIncidentQueryParams } from '../../utils/queryParamsUtils';\nimport { useDebouncedValue } from '../../hooks/useDebouncedValue';\nimport { useQueryState } from '../../hooks/useQueryState';\nimport { useQueryArrayState } from '../../hooks/useQueryArrayState';\nimport { serviceNowApiRef } from '../../api/ServiceNowBackendClient';\nimport useUserEmail from '../../hooks/useUserEmail';\nimport CircularProgress from '@mui/material/CircularProgress';\nimport TableBody from '@mui/material/TableBody';\nimport TableRow from '@mui/material/TableRow';\nimport TableCell from '@mui/material/TableCell';\nimport { useUpdateQueryParams } from '../../hooks/useQueryHelpers';\n\nexport const ServicenowContent = () => {\n const { entity } = useEntity();\n const serviceNowApi = useApi(serviceNowApiRef);\n\n const [search, setSearch] = useQueryState<string>('search', '');\n const [input, setInput] = useState(search);\n const debouncedInput = useDebouncedValue(input, 300);\n\n const [order, setOrder] = useQueryState<Order>('order', SortingOrderEnum.Asc);\n const [orderBy, setOrderBy] = useQueryState<IncidentTableField>(\n 'orderBy',\n IncidentTableFieldEnum.Number,\n );\n\n const [rowsPerPage] = useQueryState<number>('limit', 5);\n const [offset, setOffset] = useQueryState<number>('offset', 0);\n const [state] = useQueryArrayState('state', []);\n const [priority] = useQueryArrayState('priority', []);\n\n const pageNumber = Math.floor(offset / rowsPerPage);\n\n const [count, setCount] = useState<number>(0);\n const [incidentsData, setIncidentsData] = useState<IncidentsData[]>([]);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const updateQueryParams = useUpdateQueryParams();\n\n const entityId = entity.metadata.annotations?.[ServiceAnnotationFieldName];\n\n const kind = entity.kind.toLocaleLowerCase('en-US');\n const userEmail = useUserEmail(kind);\n\n const identityApi = useApi(identityApiRef);\n\n useEffect(() => {\n identityApi.getBackstageIdentity().then(identity => {\n if (identity.userEntityRef) {\n localStorage.setItem('userEntityRef', identity.userEntityRef);\n }\n });\n }, [identityApi]);\n\n useEffect(() => {\n if (debouncedInput !== search) {\n setSearch(debouncedInput);\n setOffset(0);\n }\n }, [debouncedInput, search, setSearch, setOffset]);\n\n useEffect(() => {\n async function fetchIncidents() {\n setLoading(true);\n setError(null);\n\n if (!userEmail && !entityId) {\n setIncidentsData([]);\n setLoading(false);\n return;\n }\n\n try {\n const queryParams = buildIncidentQueryParams({\n limit: rowsPerPage,\n offset,\n order,\n orderBy,\n search: search,\n priority: priority ?? undefined,\n state: state ?? undefined,\n userEmail,\n entityId,\n });\n\n const { incidents, totalCount } = await serviceNowApi.getIncidents(\n queryParams,\n );\n setIncidentsData(incidents);\n setCount(totalCount);\n } catch (e) {\n setError((e as Error).message);\n setIncidentsData([]);\n } finally {\n setLoading(false);\n }\n }\n\n fetchIncidents();\n }, [\n rowsPerPage,\n offset,\n order,\n orderBy,\n search,\n serviceNowApi,\n entityId,\n priority,\n state,\n userEmail,\n ]);\n\n const handleRowsPerPageChange = (event: ChangeEvent<HTMLInputElement>) => {\n const newLimit = parseInt(event.target.value, 10);\n updateQueryParams({\n limit: String(newLimit),\n offset: '0',\n });\n };\n\n const handlePageChange = (_event: unknown, page: number) => {\n setOffset(page * rowsPerPage);\n };\n\n const handleRequestSort = (\n _event: MouseEvent<unknown>,\n property: IncidentTableField,\n ) => {\n const isAsc = orderBy === property && order === SortingOrderEnum.Asc;\n setOrder(isAsc ? SortingOrderEnum.Desc : SortingOrderEnum.Asc);\n setOrderBy(property);\n setOffset(0);\n };\n\n const handleSearch = (str: string) => {\n setInput(str);\n };\n\n const IncidentsTableHeaderComponent = () => (\n <IncidentsTableHeader\n order={order}\n orderBy={orderBy}\n onRequestSort={handleRequestSort}\n />\n );\n\n const IncidentsTableBodyComponent = () =>\n loading ? (\n <TableBody>\n <TableRow>\n <TableCell colSpan={IncidentsListColumns.length} align=\"center\">\n <Box sx={{ py: 3 }}>\n <CircularProgress />\n </Box>\n </TableCell>\n </TableRow>\n </TableBody>\n ) : (\n <IncidentsTableBody rows={incidentsData} />\n );\n\n const TablePaginationComponent = () => {\n return (\n <TablePagination\n rowsPerPageOptions={[5, 10, 20, 50, 100].map(n => ({\n value: n,\n label: `${n} rows`,\n }))}\n component=\"div\"\n sx={{ mr: 1 }}\n count={count ?? 0}\n rowsPerPage={rowsPerPage}\n page={pageNumber}\n onPageChange={handlePageChange}\n onRowsPerPageChange={handleRowsPerPageChange}\n labelRowsPerPage={null}\n showFirstButton\n showLastButton\n />\n );\n };\n\n return (\n <Box>\n <CatalogFilterLayout>\n <CatalogFilterLayout.Filters>\n <IncidentsFilter />\n </CatalogFilterLayout.Filters>\n <CatalogFilterLayout.Content>\n {error ? (\n <Box sx={{ padding: 2, color: 'error.main' }}>\n Error loading incidents: {error}\n </Box>\n ) : (\n <Table\n data={loading ? [] : incidentsData}\n columns={IncidentsListColumns}\n onSearchChange={handleSearch}\n title={\n count === 0\n ? 'ServiceNow tickets'\n : `ServiceNow tickets (${count})`\n }\n localization={{ toolbar: { searchPlaceholder: 'Search' } }}\n components={{\n Header: IncidentsTableHeaderComponent,\n Body: IncidentsTableBodyComponent,\n Pagination: TablePaginationComponent,\n }}\n emptyContent={\n loading ? null : (\n <Box\n data-testid=\"no-incidents-found\"\n sx={{\n padding: 2,\n display: 'flex',\n justifyContent: 'center',\n }}\n >\n No records found\n </Box>\n )\n }\n isLoading={loading}\n />\n )}\n </CatalogFilterLayout.Content>\n </CatalogFilterLayout>\n </Box>\n );\n};\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export { ServicenowContent } from './ServicenowContent.esm.js';
2
+ //# sourceMappingURL=index.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.esm.js","sources":[],"sourcesContent":[],"names":[],"mappings":""}
@@ -0,0 +1,79 @@
1
+ import { jsxs, jsx } from 'react/jsx-runtime';
2
+ import Autocomplete from '@mui/material/Autocomplete';
3
+ import Box from '@mui/material/Box';
4
+ import Checkbox from '@mui/material/Checkbox';
5
+ import CheckBoxOutlineBlankIcon from '@mui/icons-material/CheckBoxOutlineBlank';
6
+ import CheckBoxIcon from '@mui/icons-material/CheckBox';
7
+ import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
8
+ import InputLabel from '@mui/material/InputLabel';
9
+ import TextField from '@mui/material/TextField';
10
+ import { PRIORITY_MAP, INCIDENT_STATE_MAP, renderStatusLabel } from '../../utils/incidentUtils.esm.js';
11
+
12
+ const IncidentEnumFilter = ({
13
+ label,
14
+ filterKey,
15
+ dataMap,
16
+ value,
17
+ onChange
18
+ }) => {
19
+ const items = Object.entries(dataMap).map(
20
+ ([key, itemValue]) => ({
21
+ value: key,
22
+ label: itemValue.label
23
+ })
24
+ );
25
+ return /* @__PURE__ */ jsxs(Box, { sx: { display: "flex", flexDirection: "column" }, children: [
26
+ /* @__PURE__ */ jsx(
27
+ InputLabel,
28
+ {
29
+ sx: {
30
+ transform: "initial",
31
+ fontWeight: "bold",
32
+ fontSize: (theme) => theme.typography.body2.fontSize,
33
+ fontFamily: (theme) => theme.typography.fontFamily,
34
+ color: (theme) => theme.palette.text.primary
35
+ },
36
+ children: label
37
+ }
38
+ ),
39
+ /* @__PURE__ */ jsx(
40
+ Autocomplete,
41
+ {
42
+ multiple: true,
43
+ disableCloseOnSelect: true,
44
+ "aria-label": label,
45
+ options: items,
46
+ isOptionEqualToValue: (option, val) => option.value === val.value,
47
+ getOptionLabel: (option) => option.label,
48
+ onChange,
49
+ renderOption: (renderProps, option, { selected }) => {
50
+ const { key, ...optionProps } = renderProps;
51
+ const statusData = filterKey === "priority" ? PRIORITY_MAP[Number(option.value)] : INCIDENT_STATE_MAP[Number(option.value)];
52
+ return /* @__PURE__ */ jsxs("li", { ...optionProps, children: [
53
+ /* @__PURE__ */ jsx(
54
+ Checkbox,
55
+ {
56
+ icon: /* @__PURE__ */ jsx(CheckBoxOutlineBlankIcon, { fontSize: "small" }),
57
+ checkedIcon: /* @__PURE__ */ jsx(CheckBoxIcon, { fontSize: "small" }),
58
+ checked: selected
59
+ }
60
+ ),
61
+ renderStatusLabel(statusData)
62
+ ] }, key);
63
+ },
64
+ size: "small",
65
+ value,
66
+ popupIcon: /* @__PURE__ */ jsx(
67
+ ExpandMoreIcon,
68
+ {
69
+ "data-testid": `select-${label.toLowerCase().replace(/\s/g, "-")}`
70
+ }
71
+ ),
72
+ renderInput: (params) => /* @__PURE__ */ jsx(TextField, { ...params, variant: "outlined" })
73
+ }
74
+ )
75
+ ] });
76
+ };
77
+
78
+ export { IncidentEnumFilter };
79
+ //# sourceMappingURL=IncidentEnumFilter.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"IncidentEnumFilter.esm.js","sources":["../../../src/components/shared-components/IncidentEnumFilter.tsx"],"sourcesContent":["/*\n * Copyright 2024 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 */\n\nimport { SelectItem } from '@backstage/core-components';\nimport Autocomplete from '@mui/material/Autocomplete';\nimport Box from '@mui/material/Box';\nimport Checkbox from '@mui/material/Checkbox';\nimport CheckBoxOutlineBlankIcon from '@mui/icons-material/CheckBoxOutlineBlank';\nimport CheckBoxIcon from '@mui/icons-material/CheckBox';\nimport ExpandMoreIcon from '@mui/icons-material/ExpandMore';\nimport InputLabel from '@mui/material/InputLabel';\nimport TextField from '@mui/material/TextField';\nimport {\n INCIDENT_STATE_MAP,\n PRIORITY_MAP,\n renderStatusLabel,\n} from '../../utils/incidentUtils';\n\ntype SourceMap = Record<number, { label: string }>;\n\nexport interface IncidentEnumFilterProps {\n label: string;\n filterKey: string;\n dataMap: SourceMap;\n value: SelectItem[];\n onChange: (event: any, value: SelectItem[]) => void;\n}\n\nexport const IncidentEnumFilter = ({\n label,\n filterKey,\n dataMap,\n value,\n onChange,\n}: IncidentEnumFilterProps) => {\n const items: SelectItem[] = Object.entries(dataMap).map(\n ([key, itemValue]) => ({\n value: key,\n label: itemValue.label,\n }),\n );\n\n return (\n <Box sx={{ display: 'flex', flexDirection: 'column' }}>\n <InputLabel\n sx={{\n transform: 'initial',\n fontWeight: 'bold',\n fontSize: theme => theme.typography.body2.fontSize,\n fontFamily: theme => theme.typography.fontFamily,\n color: theme => theme.palette.text.primary,\n }}\n >\n {label}\n </InputLabel>\n <Autocomplete\n multiple\n disableCloseOnSelect\n aria-label={label}\n options={items}\n isOptionEqualToValue={(option, val) => option.value === val.value}\n getOptionLabel={option => option.label}\n onChange={onChange}\n renderOption={(renderProps, option, { selected }) => {\n const { key, ...optionProps } = renderProps;\n const statusData =\n filterKey === 'priority'\n ? PRIORITY_MAP[Number(option.value)]\n : INCIDENT_STATE_MAP[Number(option.value)];\n\n return (\n <li key={key} {...optionProps}>\n <Checkbox\n icon={<CheckBoxOutlineBlankIcon fontSize=\"small\" />}\n checkedIcon={<CheckBoxIcon fontSize=\"small\" />}\n checked={selected}\n />\n {renderStatusLabel(statusData)}\n </li>\n );\n }}\n size=\"small\"\n value={value}\n popupIcon={\n <ExpandMoreIcon\n data-testid={`select-${label.toLowerCase().replace(/\\s/g, '-')}`}\n />\n }\n renderInput={params => <TextField {...params} variant=\"outlined\" />}\n />\n </Box>\n );\n};\n"],"names":[],"mappings":";;;;;;;;;;;AAyCO,MAAM,qBAAqB,CAAC;AAAA,EACjC,KAAA;AAAA,EACA,SAAA;AAAA,EACA,OAAA;AAAA,EACA,KAAA;AAAA,EACA;AACF,CAA+B,KAAA;AAC7B,EAAA,MAAM,KAAsB,GAAA,MAAA,CAAO,OAAQ,CAAA,OAAO,CAAE,CAAA,GAAA;AAAA,IAClD,CAAC,CAAC,GAAK,EAAA,SAAS,CAAO,MAAA;AAAA,MACrB,KAAO,EAAA,GAAA;AAAA,MACP,OAAO,SAAU,CAAA;AAAA,KACnB;AAAA,GACF;AAEA,EACE,uBAAA,IAAA,CAAC,OAAI,EAAI,EAAA,EAAE,SAAS,MAAQ,EAAA,aAAA,EAAe,UACzC,EAAA,QAAA,EAAA;AAAA,oBAAA,GAAA;AAAA,MAAC,UAAA;AAAA,MAAA;AAAA,QACC,EAAI,EAAA;AAAA,UACF,SAAW,EAAA,SAAA;AAAA,UACX,UAAY,EAAA,MAAA;AAAA,UACZ,QAAU,EAAA,CAAA,KAAA,KAAS,KAAM,CAAA,UAAA,CAAW,KAAM,CAAA,QAAA;AAAA,UAC1C,UAAA,EAAY,CAAS,KAAA,KAAA,KAAA,CAAM,UAAW,CAAA,UAAA;AAAA,UACtC,KAAO,EAAA,CAAA,KAAA,KAAS,KAAM,CAAA,OAAA,CAAQ,IAAK,CAAA;AAAA,SACrC;AAAA,QAEC,QAAA,EAAA;AAAA;AAAA,KACH;AAAA,oBACA,GAAA;AAAA,MAAC,YAAA;AAAA,MAAA;AAAA,QACC,QAAQ,EAAA,IAAA;AAAA,QACR,oBAAoB,EAAA,IAAA;AAAA,QACpB,YAAY,EAAA,KAAA;AAAA,QACZ,OAAS,EAAA,KAAA;AAAA,QACT,sBAAsB,CAAC,MAAA,EAAQ,GAAQ,KAAA,MAAA,CAAO,UAAU,GAAI,CAAA,KAAA;AAAA,QAC5D,cAAA,EAAgB,YAAU,MAAO,CAAA,KAAA;AAAA,QACjC,QAAA;AAAA,QACA,cAAc,CAAC,WAAA,EAAa,MAAQ,EAAA,EAAE,UAAe,KAAA;AACnD,UAAA,MAAM,EAAE,GAAA,EAAK,GAAG,WAAA,EAAgB,GAAA,WAAA;AAChC,UAAA,MAAM,UACJ,GAAA,SAAA,KAAc,UACV,GAAA,YAAA,CAAa,MAAO,CAAA,MAAA,CAAO,KAAK,CAAC,CACjC,GAAA,kBAAA,CAAmB,MAAO,CAAA,MAAA,CAAO,KAAK,CAAC,CAAA;AAE7C,UACE,uBAAA,IAAA,CAAC,IAAc,EAAA,EAAA,GAAG,WAChB,EAAA,QAAA,EAAA;AAAA,4BAAA,GAAA;AAAA,cAAC,QAAA;AAAA,cAAA;AAAA,gBACC,IAAM,kBAAA,GAAA,CAAC,wBAAyB,EAAA,EAAA,QAAA,EAAS,OAAQ,EAAA,CAAA;AAAA,gBACjD,WAAa,kBAAA,GAAA,CAAC,YAAa,EAAA,EAAA,QAAA,EAAS,OAAQ,EAAA,CAAA;AAAA,gBAC5C,OAAS,EAAA;AAAA;AAAA,aACX;AAAA,YACC,kBAAkB,UAAU;AAAA,WAAA,EAAA,EANtB,GAOT,CAAA;AAAA,SAEJ;AAAA,QACA,IAAK,EAAA,OAAA;AAAA,QACL,KAAA;AAAA,QACA,SACE,kBAAA,GAAA;AAAA,UAAC,cAAA;AAAA,UAAA;AAAA,YACC,aAAA,EAAa,UAAU,KAAM,CAAA,WAAA,GAAc,OAAQ,CAAA,KAAA,EAAO,GAAG,CAAC,CAAA;AAAA;AAAA,SAChE;AAAA,QAEF,aAAa,CAAU,MAAA,qBAAA,GAAA,CAAC,aAAW,GAAG,MAAA,EAAQ,SAAQ,UAAW,EAAA;AAAA;AAAA;AACnE,GACF,EAAA,CAAA;AAEJ;;;;"}
@@ -0,0 +1,13 @@
1
+ import { useState, useEffect } from 'react';
2
+
3
+ function useDebouncedValue(value, delay) {
4
+ const [debounced, setDebounced] = useState(value);
5
+ useEffect(() => {
6
+ const timer = setTimeout(() => setDebounced(value), delay);
7
+ return () => clearTimeout(timer);
8
+ }, [value, delay]);
9
+ return debounced;
10
+ }
11
+
12
+ export { useDebouncedValue };
13
+ //# sourceMappingURL=useDebouncedValue.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useDebouncedValue.esm.js","sources":["../../src/hooks/useDebouncedValue.ts"],"sourcesContent":["/*\n * Copyright 2025 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 */\n\nimport { useState, useEffect } from 'react';\n\nexport function useDebouncedValue<T>(value: T, delay: number): T {\n const [debounced, setDebounced] = useState(value);\n\n useEffect(() => {\n const timer = setTimeout(() => setDebounced(value), delay);\n return () => clearTimeout(timer);\n }, [value, delay]);\n\n return debounced;\n}\n"],"names":[],"mappings":";;AAkBgB,SAAA,iBAAA,CAAqB,OAAU,KAAkB,EAAA;AAC/D,EAAA,MAAM,CAAC,SAAA,EAAW,YAAY,CAAA,GAAI,SAAS,KAAK,CAAA;AAEhD,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,MAAM,QAAQ,UAAW,CAAA,MAAM,YAAa,CAAA,KAAK,GAAG,KAAK,CAAA;AACzD,IAAO,OAAA,MAAM,aAAa,KAAK,CAAA;AAAA,GAC9B,EAAA,CAAC,KAAO,EAAA,KAAK,CAAC,CAAA;AAEjB,EAAO,OAAA,SAAA;AACT;;;;"}
@@ -0,0 +1,38 @@
1
+ import { useMemo, useCallback } from 'react';
2
+ import { useQueryState } from './useQueryState.esm.js';
3
+ import { INCIDENT_STATE_MAP, PRIORITY_MAP } from '../utils/incidentUtils.esm.js';
4
+
5
+ const useQueryArrayFilter = (filterName) => {
6
+ const [raw, setRaw] = useQueryState(filterName, "");
7
+ const [, setOffset] = useQueryState("offset", 0);
8
+ const map = filterName === "state" ? INCIDENT_STATE_MAP : PRIORITY_MAP;
9
+ const current = useMemo(() => {
10
+ return raw.split(",").filter((v) => v !== "").map((value) => ({
11
+ value,
12
+ label: map[Number(value)]?.label
13
+ })).filter((item) => item.label);
14
+ }, [raw, map]);
15
+ const set = useCallback(
16
+ (newValues) => {
17
+ const joined = newValues.join(",");
18
+ setRaw(joined);
19
+ setOffset(0);
20
+ },
21
+ [setRaw, setOffset]
22
+ );
23
+ const clear = useCallback(() => {
24
+ setRaw("");
25
+ setOffset(0);
26
+ }, [setRaw, setOffset]);
27
+ return useMemo(
28
+ () => ({
29
+ current,
30
+ set,
31
+ clear
32
+ }),
33
+ [current, set, clear]
34
+ );
35
+ };
36
+
37
+ export { useQueryArrayFilter };
38
+ //# sourceMappingURL=useQueryArrayFilter.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useQueryArrayFilter.esm.js","sources":["../../src/hooks/useQueryArrayFilter.ts"],"sourcesContent":["/*\n * Copyright 2024 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 */\n\nimport { useMemo, useCallback } from 'react';\nimport { useQueryState } from './useQueryState';\nimport { INCIDENT_STATE_MAP, PRIORITY_MAP } from '../utils/incidentUtils';\nimport { SelectItem } from '@backstage/core-components';\n\nexport const useQueryArrayFilter = (filterName: 'state' | 'priority') => {\n const [raw, setRaw] = useQueryState<string>(filterName, '');\n const [, setOffset] = useQueryState<number>('offset', 0);\n\n const map = filterName === 'state' ? INCIDENT_STATE_MAP : PRIORITY_MAP;\n\n const current: SelectItem[] = useMemo(() => {\n return raw\n .split(',')\n .filter(v => v !== '')\n .map(value => ({\n value,\n label: map[Number(value)]?.label,\n }))\n .filter(item => item.label);\n }, [raw, map]);\n\n const set = useCallback(\n (newValues: (string | number)[]) => {\n const joined = newValues.join(',');\n setRaw(joined);\n setOffset(0);\n },\n [setRaw, setOffset],\n );\n\n const clear = useCallback(() => {\n setRaw('');\n setOffset(0);\n }, [setRaw, setOffset]);\n\n return useMemo(\n () =>\n ({\n current,\n set,\n clear,\n } as const),\n [current, set, clear],\n );\n};\n"],"names":[],"mappings":";;;;AAqBa,MAAA,mBAAA,GAAsB,CAAC,UAAqC,KAAA;AACvE,EAAA,MAAM,CAAC,GAAK,EAAA,MAAM,CAAI,GAAA,aAAA,CAAsB,YAAY,EAAE,CAAA;AAC1D,EAAA,MAAM,GAAG,SAAS,CAAI,GAAA,aAAA,CAAsB,UAAU,CAAC,CAAA;AAEvD,EAAM,MAAA,GAAA,GAAM,UAAe,KAAA,OAAA,GAAU,kBAAqB,GAAA,YAAA;AAE1D,EAAM,MAAA,OAAA,GAAwB,QAAQ,MAAM;AAC1C,IAAO,OAAA,GAAA,CACJ,KAAM,CAAA,GAAG,CACT,CAAA,MAAA,CAAO,OAAK,CAAM,KAAA,EAAE,CACpB,CAAA,GAAA,CAAI,CAAU,KAAA,MAAA;AAAA,MACb,KAAA;AAAA,MACA,KAAO,EAAA,GAAA,CAAI,MAAO,CAAA,KAAK,CAAC,CAAG,EAAA;AAAA,KAC3B,CAAA,CAAA,CACD,MAAO,CAAA,CAAA,IAAA,KAAQ,KAAK,KAAK,CAAA;AAAA,GAC3B,EAAA,CAAC,GAAK,EAAA,GAAG,CAAC,CAAA;AAEb,EAAA,MAAM,GAAM,GAAA,WAAA;AAAA,IACV,CAAC,SAAmC,KAAA;AAClC,MAAM,MAAA,MAAA,GAAS,SAAU,CAAA,IAAA,CAAK,GAAG,CAAA;AACjC,MAAA,MAAA,CAAO,MAAM,CAAA;AACb,MAAA,SAAA,CAAU,CAAC,CAAA;AAAA,KACb;AAAA,IACA,CAAC,QAAQ,SAAS;AAAA,GACpB;AAEA,EAAM,MAAA,KAAA,GAAQ,YAAY,MAAM;AAC9B,IAAA,MAAA,CAAO,EAAE,CAAA;AACT,IAAA,SAAA,CAAU,CAAC,CAAA;AAAA,GACV,EAAA,CAAC,MAAQ,EAAA,SAAS,CAAC,CAAA;AAEtB,EAAO,OAAA,OAAA;AAAA,IACL,OACG;AAAA,MACC,OAAA;AAAA,MACA,GAAA;AAAA,MACA;AAAA,KACF,CAAA;AAAA,IACF,CAAC,OAAS,EAAA,GAAA,EAAK,KAAK;AAAA,GACtB;AACF;;;;"}
@@ -0,0 +1,44 @@
1
+ import { useCallback, useState, useEffect } from 'react';
2
+ import { useSearchParams } from 'react-router-dom';
3
+
4
+ function useQueryArrayState(key, defaultValue = []) {
5
+ const [searchParams, setSearchParams] = useSearchParams();
6
+ const getArrayFromParam = useCallback(() => {
7
+ const raw = searchParams.get(key);
8
+ return raw ? raw.split(",").filter(Boolean) : defaultValue;
9
+ }, [key, searchParams, defaultValue]);
10
+ const [state, setState] = useState(() => getArrayFromParam());
11
+ useEffect(() => {
12
+ const value = getArrayFromParam();
13
+ setState((prev) => {
14
+ const prevRaw = prev.join(",");
15
+ const nextRaw = value.join(",");
16
+ return prevRaw === nextRaw ? prev : value;
17
+ });
18
+ }, [getArrayFromParam]);
19
+ const setValue = useCallback(
20
+ (newValue) => {
21
+ const newRaw = newValue.join(",");
22
+ const currentRaw = searchParams.get(key);
23
+ if (currentRaw === newRaw || !currentRaw && newRaw === "") return;
24
+ setSearchParams(
25
+ (prev) => {
26
+ const params = new URLSearchParams(prev);
27
+ if (newRaw === "") {
28
+ params.delete(key);
29
+ } else {
30
+ params.set(key, newRaw);
31
+ }
32
+ return params;
33
+ },
34
+ { replace: true }
35
+ );
36
+ setState(newValue);
37
+ },
38
+ [key, searchParams, setSearchParams]
39
+ );
40
+ return [state, setValue];
41
+ }
42
+
43
+ export { useQueryArrayState };
44
+ //# sourceMappingURL=useQueryArrayState.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useQueryArrayState.esm.js","sources":["../../src/hooks/useQueryArrayState.ts"],"sourcesContent":["/*\n * Copyright 2025 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 { useCallback, useEffect, useState } from 'react';\nimport { useSearchParams } from 'react-router-dom';\n\nexport function useQueryArrayState(\n key: string,\n defaultValue: string[] = [],\n): [string[], (value: string[]) => void] {\n const [searchParams, setSearchParams] = useSearchParams();\n\n const getArrayFromParam = useCallback((): string[] => {\n const raw = searchParams.get(key);\n return raw ? raw.split(',').filter(Boolean) : defaultValue;\n }, [key, searchParams, defaultValue]);\n\n const [state, setState] = useState<string[]>(() => getArrayFromParam());\n\n useEffect(() => {\n const value = getArrayFromParam();\n setState(prev => {\n const prevRaw = prev.join(',');\n const nextRaw = value.join(',');\n return prevRaw === nextRaw ? prev : value;\n });\n }, [getArrayFromParam]);\n\n const setValue = useCallback(\n (newValue: string[]) => {\n const newRaw = newValue.join(',');\n const currentRaw = searchParams.get(key);\n\n if (currentRaw === newRaw || (!currentRaw && newRaw === '')) return;\n\n setSearchParams(\n prev => {\n const params = new URLSearchParams(prev);\n if (newRaw === '') {\n params.delete(key);\n } else {\n params.set(key, newRaw);\n }\n return params;\n },\n { replace: true },\n );\n\n setState(newValue);\n },\n [key, searchParams, setSearchParams],\n );\n\n return [state, setValue];\n}\n"],"names":[],"mappings":";;;AAkBO,SAAS,kBACd,CAAA,GAAA,EACA,YAAyB,GAAA,EACc,EAAA;AACvC,EAAA,MAAM,CAAC,YAAA,EAAc,eAAe,CAAA,GAAI,eAAgB,EAAA;AAExD,EAAM,MAAA,iBAAA,GAAoB,YAAY,MAAgB;AACpD,IAAM,MAAA,GAAA,GAAM,YAAa,CAAA,GAAA,CAAI,GAAG,CAAA;AAChC,IAAA,OAAO,MAAM,GAAI,CAAA,KAAA,CAAM,GAAG,CAAE,CAAA,MAAA,CAAO,OAAO,CAAI,GAAA,YAAA;AAAA,GAC7C,EAAA,CAAC,GAAK,EAAA,YAAA,EAAc,YAAY,CAAC,CAAA;AAEpC,EAAA,MAAM,CAAC,KAAO,EAAA,QAAQ,IAAI,QAAmB,CAAA,MAAM,mBAAmB,CAAA;AAEtE,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,MAAM,QAAQ,iBAAkB,EAAA;AAChC,IAAA,QAAA,CAAS,CAAQ,IAAA,KAAA;AACf,MAAM,MAAA,OAAA,GAAU,IAAK,CAAA,IAAA,CAAK,GAAG,CAAA;AAC7B,MAAM,MAAA,OAAA,GAAU,KAAM,CAAA,IAAA,CAAK,GAAG,CAAA;AAC9B,MAAO,OAAA,OAAA,KAAY,UAAU,IAAO,GAAA,KAAA;AAAA,KACrC,CAAA;AAAA,GACH,EAAG,CAAC,iBAAiB,CAAC,CAAA;AAEtB,EAAA,MAAM,QAAW,GAAA,WAAA;AAAA,IACf,CAAC,QAAuB,KAAA;AACtB,MAAM,MAAA,MAAA,GAAS,QAAS,CAAA,IAAA,CAAK,GAAG,CAAA;AAChC,MAAM,MAAA,UAAA,GAAa,YAAa,CAAA,GAAA,CAAI,GAAG,CAAA;AAEvC,MAAA,IAAI,UAAe,KAAA,MAAA,IAAW,CAAC,UAAA,IAAc,WAAW,EAAK,EAAA;AAE7D,MAAA,eAAA;AAAA,QACE,CAAQ,IAAA,KAAA;AACN,UAAM,MAAA,MAAA,GAAS,IAAI,eAAA,CAAgB,IAAI,CAAA;AACvC,UAAA,IAAI,WAAW,EAAI,EAAA;AACjB,YAAA,MAAA,CAAO,OAAO,GAAG,CAAA;AAAA,WACZ,MAAA;AACL,YAAO,MAAA,CAAA,GAAA,CAAI,KAAK,MAAM,CAAA;AAAA;AAExB,UAAO,OAAA,MAAA;AAAA,SACT;AAAA,QACA,EAAE,SAAS,IAAK;AAAA,OAClB;AAEA,MAAA,QAAA,CAAS,QAAQ,CAAA;AAAA,KACnB;AAAA,IACA,CAAC,GAAK,EAAA,YAAA,EAAc,eAAe;AAAA,GACrC;AAEA,EAAO,OAAA,CAAC,OAAO,QAAQ,CAAA;AACzB;;;;"}
@@ -0,0 +1,24 @@
1
+ import { useSearchParams } from 'react-router-dom';
2
+
3
+ function useUpdateQueryParams() {
4
+ const [, setSearchParams] = useSearchParams();
5
+ return (updates) => {
6
+ setSearchParams(
7
+ (prev) => {
8
+ const params = new URLSearchParams(prev);
9
+ for (const key in updates) {
10
+ if (updates[key] === "") {
11
+ params.delete(key);
12
+ } else {
13
+ params.set(key, updates[key]);
14
+ }
15
+ }
16
+ return params;
17
+ },
18
+ { replace: true }
19
+ );
20
+ };
21
+ }
22
+
23
+ export { useUpdateQueryParams };
24
+ //# sourceMappingURL=useQueryHelpers.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useQueryHelpers.esm.js","sources":["../../src/hooks/useQueryHelpers.ts"],"sourcesContent":["/*\n * Copyright 2025 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 */\n\nimport { useSearchParams } from 'react-router-dom';\n\nexport function useUpdateQueryParams() {\n const [, setSearchParams] = useSearchParams();\n\n return (updates: Record<string, string>) => {\n setSearchParams(\n prev => {\n const params = new URLSearchParams(prev);\n for (const key in updates) {\n if (updates[key] === '') {\n params.delete(key);\n } else {\n params.set(key, updates[key]);\n }\n }\n return params;\n },\n { replace: true },\n );\n };\n}\n"],"names":[],"mappings":";;AAkBO,SAAS,oBAAuB,GAAA;AACrC,EAAA,MAAM,GAAG,eAAe,CAAA,GAAI,eAAgB,EAAA;AAE5C,EAAA,OAAO,CAAC,OAAoC,KAAA;AAC1C,IAAA,eAAA;AAAA,MACE,CAAQ,IAAA,KAAA;AACN,QAAM,MAAA,MAAA,GAAS,IAAI,eAAA,CAAgB,IAAI,CAAA;AACvC,QAAA,KAAA,MAAW,OAAO,OAAS,EAAA;AACzB,UAAI,IAAA,OAAA,CAAQ,GAAG,CAAA,KAAM,EAAI,EAAA;AACvB,YAAA,MAAA,CAAO,OAAO,GAAG,CAAA;AAAA,WACZ,MAAA;AACL,YAAA,MAAA,CAAO,GAAI,CAAA,GAAA,EAAK,OAAQ,CAAA,GAAG,CAAC,CAAA;AAAA;AAC9B;AAEF,QAAO,OAAA,MAAA;AAAA,OACT;AAAA,MACA,EAAE,SAAS,IAAK;AAAA,KAClB;AAAA,GACF;AACF;;;;"}
@@ -0,0 +1,50 @@
1
+ import { useCallback, useState, useEffect } from 'react';
2
+ import { useSearchParams } from 'react-router-dom';
3
+
4
+ function useQueryState(key, defaultValue) {
5
+ const [searchParams, setSearchParams] = useSearchParams();
6
+ const getValueFromParams = useCallback(
7
+ (raw) => {
8
+ if (typeof defaultValue === "number") {
9
+ return raw !== null ? Number(raw) : defaultValue;
10
+ }
11
+ if (typeof defaultValue === "boolean") {
12
+ return raw === "true";
13
+ }
14
+ return raw ?? defaultValue;
15
+ },
16
+ [defaultValue]
17
+ );
18
+ const [state, setState] = useState(
19
+ () => getValueFromParams(searchParams.get(key))
20
+ );
21
+ useEffect(() => {
22
+ const paramValue = getValueFromParams(searchParams.get(key));
23
+ setState(paramValue);
24
+ }, [getValueFromParams, searchParams, key]);
25
+ const setValue = useCallback(
26
+ (newValue) => {
27
+ const currentRaw = searchParams.get(key);
28
+ const newRaw = String(newValue);
29
+ if (currentRaw === newRaw || !currentRaw && newRaw === "") return;
30
+ setSearchParams(
31
+ (prev) => {
32
+ const params = new URLSearchParams(prev);
33
+ if (newRaw === "") {
34
+ params.delete(key);
35
+ } else {
36
+ params.set(key, newRaw);
37
+ }
38
+ return params;
39
+ },
40
+ { replace: true }
41
+ );
42
+ setState(newValue);
43
+ },
44
+ [key, searchParams, setSearchParams]
45
+ );
46
+ return [state, setValue];
47
+ }
48
+
49
+ export { useQueryState };
50
+ //# sourceMappingURL=useQueryState.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useQueryState.esm.js","sources":["../../src/hooks/useQueryState.ts"],"sourcesContent":["/*\n * Copyright 2025 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 { useCallback, useEffect, useState } from 'react';\nimport { useSearchParams } from 'react-router-dom';\n\nexport function useQueryState<T>(\n key: string,\n defaultValue: T,\n): [T, (value: T) => void] {\n const [searchParams, setSearchParams] = useSearchParams();\n\n const getValueFromParams = useCallback(\n (raw: string | null): T => {\n if (typeof defaultValue === 'number') {\n return (raw !== null ? Number(raw) : defaultValue) as T;\n }\n if (typeof defaultValue === 'boolean') {\n return (raw === 'true') as T;\n }\n return (raw ?? defaultValue) as T;\n },\n [defaultValue],\n );\n\n const [state, setState] = useState<T>(() =>\n getValueFromParams(searchParams.get(key)),\n );\n\n useEffect(() => {\n const paramValue = getValueFromParams(searchParams.get(key));\n setState(paramValue);\n }, [getValueFromParams, searchParams, key]);\n\n const setValue = useCallback(\n (newValue: T) => {\n const currentRaw = searchParams.get(key);\n const newRaw = String(newValue);\n\n if (currentRaw === newRaw || (!currentRaw && newRaw === '')) return;\n\n setSearchParams(\n prev => {\n const params = new URLSearchParams(prev);\n\n if (newRaw === '') {\n params.delete(key);\n } else {\n params.set(key, newRaw);\n }\n\n return params;\n },\n { replace: true },\n );\n\n setState(newValue);\n },\n [key, searchParams, setSearchParams],\n );\n\n return [state, setValue];\n}\n"],"names":[],"mappings":";;;AAkBgB,SAAA,aAAA,CACd,KACA,YACyB,EAAA;AACzB,EAAA,MAAM,CAAC,YAAA,EAAc,eAAe,CAAA,GAAI,eAAgB,EAAA;AAExD,EAAA,MAAM,kBAAqB,GAAA,WAAA;AAAA,IACzB,CAAC,GAA0B,KAAA;AACzB,MAAI,IAAA,OAAO,iBAAiB,QAAU,EAAA;AACpC,QAAA,OAAQ,GAAQ,KAAA,IAAA,GAAO,MAAO,CAAA,GAAG,CAAI,GAAA,YAAA;AAAA;AAEvC,MAAI,IAAA,OAAO,iBAAiB,SAAW,EAAA;AACrC,QAAA,OAAQ,GAAQ,KAAA,MAAA;AAAA;AAElB,MAAA,OAAQ,GAAO,IAAA,YAAA;AAAA,KACjB;AAAA,IACA,CAAC,YAAY;AAAA,GACf;AAEA,EAAM,MAAA,CAAC,KAAO,EAAA,QAAQ,CAAI,GAAA,QAAA;AAAA,IAAY,MACpC,kBAAA,CAAmB,YAAa,CAAA,GAAA,CAAI,GAAG,CAAC;AAAA,GAC1C;AAEA,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,MAAM,UAAa,GAAA,kBAAA,CAAmB,YAAa,CAAA,GAAA,CAAI,GAAG,CAAC,CAAA;AAC3D,IAAA,QAAA,CAAS,UAAU,CAAA;AAAA,GAClB,EAAA,CAAC,kBAAoB,EAAA,YAAA,EAAc,GAAG,CAAC,CAAA;AAE1C,EAAA,MAAM,QAAW,GAAA,WAAA;AAAA,IACf,CAAC,QAAgB,KAAA;AACf,MAAM,MAAA,UAAA,GAAa,YAAa,CAAA,GAAA,CAAI,GAAG,CAAA;AACvC,MAAM,MAAA,MAAA,GAAS,OAAO,QAAQ,CAAA;AAE9B,MAAA,IAAI,UAAe,KAAA,MAAA,IAAW,CAAC,UAAA,IAAc,WAAW,EAAK,EAAA;AAE7D,MAAA,eAAA;AAAA,QACE,CAAQ,IAAA,KAAA;AACN,UAAM,MAAA,MAAA,GAAS,IAAI,eAAA,CAAgB,IAAI,CAAA;AAEvC,UAAA,IAAI,WAAW,EAAI,EAAA;AACjB,YAAA,MAAA,CAAO,OAAO,GAAG,CAAA;AAAA,WACZ,MAAA;AACL,YAAO,MAAA,CAAA,GAAA,CAAI,KAAK,MAAM,CAAA;AAAA;AAGxB,UAAO,OAAA,MAAA;AAAA,SACT;AAAA,QACA,EAAE,SAAS,IAAK;AAAA,OAClB;AAEA,MAAA,QAAA,CAAS,QAAQ,CAAA;AAAA,KACnB;AAAA,IACA,CAAC,GAAK,EAAA,YAAA,EAAc,eAAe;AAAA,GACrC;AAEA,EAAO,OAAA,CAAC,OAAO,QAAQ,CAAA;AACzB;;;;"}
@@ -0,0 +1,22 @@
1
+ import { useApi, identityApiRef } from '@backstage/core-plugin-api';
2
+ import { catalogApiRef } from '@backstage/plugin-catalog-react';
3
+ import { useAsync } from 'react-use';
4
+
5
+ function useUserEmail(kind) {
6
+ const identityApi = useApi(identityApiRef);
7
+ const catalogApi = useApi(catalogApiRef);
8
+ const state = useAsync(async () => {
9
+ if (kind !== "user") return void 0;
10
+ const profile = await identityApi.getProfileInfo();
11
+ if (profile?.email) return profile.email;
12
+ const identity = await identityApi.getBackstageIdentity();
13
+ const userEntity = await catalogApi.getEntityByRef(
14
+ identity.userEntityRef
15
+ );
16
+ return userEntity?.spec?.profile?.email;
17
+ }, [identityApi, catalogApi, kind]);
18
+ return state.value;
19
+ }
20
+
21
+ export { useUserEmail as default, useUserEmail };
22
+ //# sourceMappingURL=useUserEmail.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useUserEmail.esm.js","sources":["../../src/hooks/useUserEmail.ts"],"sourcesContent":["/*\n * Copyright 2025 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 */\n\nimport { identityApiRef, useApi } from '@backstage/core-plugin-api';\nimport { catalogApiRef } from '@backstage/plugin-catalog-react';\nimport { UserEntity } from '@backstage/catalog-model';\nimport { useAsync } from 'react-use';\n\nexport function useUserEmail(kind: string): string | undefined {\n const identityApi = useApi(identityApiRef);\n const catalogApi = useApi(catalogApiRef);\n\n const state = useAsync(async () => {\n if (kind !== 'user') return undefined;\n\n const profile = await identityApi.getProfileInfo();\n if (profile?.email) return profile.email;\n\n const identity = await identityApi.getBackstageIdentity();\n const userEntity = (await catalogApi.getEntityByRef(\n identity.userEntityRef,\n )) as UserEntity | undefined;\n return userEntity?.spec?.profile?.email;\n }, [identityApi, catalogApi, kind]);\n\n return state.value;\n}\n\nexport default useUserEmail;\n"],"names":[],"mappings":";;;;AAqBO,SAAS,aAAa,IAAkC,EAAA;AAC7D,EAAM,MAAA,WAAA,GAAc,OAAO,cAAc,CAAA;AACzC,EAAM,MAAA,UAAA,GAAa,OAAO,aAAa,CAAA;AAEvC,EAAM,MAAA,KAAA,GAAQ,SAAS,YAAY;AACjC,IAAI,IAAA,IAAA,KAAS,QAAe,OAAA,MAAA;AAE5B,IAAM,MAAA,OAAA,GAAU,MAAM,WAAA,CAAY,cAAe,EAAA;AACjD,IAAI,IAAA,OAAA,EAAS,KAAO,EAAA,OAAO,OAAQ,CAAA,KAAA;AAEnC,IAAM,MAAA,QAAA,GAAW,MAAM,WAAA,CAAY,oBAAqB,EAAA;AACxD,IAAM,MAAA,UAAA,GAAc,MAAM,UAAW,CAAA,cAAA;AAAA,MACnC,QAAS,CAAA;AAAA,KACX;AACA,IAAO,OAAA,UAAA,EAAY,MAAM,OAAS,EAAA,KAAA;AAAA,GACjC,EAAA,CAAC,WAAa,EAAA,UAAA,EAAY,IAAI,CAAC,CAAA;AAElC,EAAA,OAAO,KAAM,CAAA,KAAA;AACf;;;;"}
@@ -0,0 +1,23 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import * as _backstage_core_plugin_api from '@backstage/core-plugin-api';
3
+ import { Entity } from '@backstage/catalog-model';
4
+
5
+ /**
6
+ * Servicenow Plugin
7
+ * @public
8
+ */
9
+ declare const servicenowPlugin: _backstage_core_plugin_api.BackstagePlugin<{
10
+ root: _backstage_core_plugin_api.RouteRef<undefined>;
11
+ }, {}, {}>;
12
+ /**
13
+ * Servicenow Page
14
+ * @public
15
+ */
16
+ declare const ServicenowPage: () => react_jsx_runtime.JSX.Element;
17
+ /**
18
+ * @public
19
+ * Check if the current entity is the logged-in user
20
+ */
21
+ declare const isMyProfile: (entity: Entity) => boolean;
22
+
23
+ export { ServicenowPage, isMyProfile, servicenowPlugin };
@@ -0,0 +1,2 @@
1
+ export { ServicenowPage, isMyProfile, servicenowPlugin } from './plugin.esm.js';
2
+ //# sourceMappingURL=index.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.esm.js","sources":[],"sourcesContent":[],"names":[],"mappings":""}