@backstage-community/plugin-servicenow 1.1.0 → 1.2.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 +31 -1
- package/README.md +68 -16
- package/dist/alpha/plugin.esm.js +35 -0
- package/dist/alpha/plugin.esm.js.map +1 -0
- package/dist/alpha.d.ts +57 -0
- package/dist/alpha.esm.js +2 -0
- package/dist/alpha.esm.js.map +1 -0
- package/dist/components/Servicenow/{ServicenowContent.esm.js → EntityServicenowContent.esm.js} +8 -8
- package/dist/components/Servicenow/EntityServicenowContent.esm.js.map +1 -0
- package/dist/components/Servicenow/IncidentsFilter.esm.js +1 -1
- package/dist/components/Servicenow/IncidentsFilter.esm.js.map +1 -1
- package/dist/components/Servicenow/IncidentsTableHeader.esm.js.map +1 -1
- package/dist/components/Servicenow/index.esm.js +1 -1
- package/dist/index.d.ts +12 -3
- package/dist/index.esm.js +2 -1
- package/dist/index.esm.js.map +1 -1
- package/dist/plugin.esm.js +10 -8
- package/dist/plugin.esm.js.map +1 -1
- package/package.json +36 -16
- package/dist/components/Servicenow/ServicenowContent.esm.js.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,35 @@
|
|
|
1
1
|
# @backstage-community/plugin-servicenow
|
|
2
2
|
|
|
3
|
+
## 1.2.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 0d3e031: Added support for the new frontend system.
|
|
8
|
+
|
|
9
|
+
The plugin automatically adds a "ServiceNow" tab to a software catalog entity if the entity has an `servicenow.com/entity-id` annotation.
|
|
10
|
+
|
|
11
|
+
It does not add the "My ServiceNow Tickets" tab to the user entity for now.
|
|
12
|
+
|
|
13
|
+
- 6be0265: Export the catalog entity tab content as `EntityServicenowContent` instead of `ServicenowPage` to align it with the Backstage naming conventions.
|
|
14
|
+
|
|
15
|
+
The `ServicenowPage` is still exported for now, but marked as **deprecated**. It might be removed or replaced in a future release.
|
|
16
|
+
|
|
17
|
+
### Patch Changes
|
|
18
|
+
|
|
19
|
+
- 6be0265: Create and export new catalog entity filter function isServicenowAvailable
|
|
20
|
+
- Updated dependencies [6be0265]
|
|
21
|
+
- Updated dependencies [6be0265]
|
|
22
|
+
- @backstage-community/plugin-servicenow-common@1.2.0
|
|
23
|
+
|
|
24
|
+
## 1.1.1
|
|
25
|
+
|
|
26
|
+
### Patch Changes
|
|
27
|
+
|
|
28
|
+
- 34aa972: Updated dependency `@mui/icons-material` to `5.18.0`.
|
|
29
|
+
Updated dependency `@mui/material` to `5.18.0`.
|
|
30
|
+
Updated dependency `@mui/styles` to `5.18.0`.
|
|
31
|
+
Updated dependency `@mui/lab` to `5.0.0-alpha.177`.
|
|
32
|
+
|
|
3
33
|
## 1.1.0
|
|
4
34
|
|
|
5
35
|
### Minor Changes
|
|
@@ -15,7 +45,7 @@
|
|
|
15
45
|
|
|
16
46
|
### Major Changes
|
|
17
47
|
|
|
18
|
-
- 3d708ba: First version of
|
|
48
|
+
- 3d708ba: First version of ServiceNow plugin
|
|
19
49
|
|
|
20
50
|
### Patch Changes
|
|
21
51
|
|
package/README.md
CHANGED
|
@@ -2,33 +2,85 @@
|
|
|
2
2
|
|
|
3
3
|
This is the frontend implementation of the ServiceNow plugin for Backstage.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Together with its backend this plugin can show incident data for catalog entities.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
## Screenshot
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
Software Catalog integration:
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+

|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
1. **Install the frontend dependency**:
|
|
12
16
|
|
|
13
17
|
```bash
|
|
14
|
-
|
|
18
|
+
# From your Backstage root directory
|
|
19
|
+
yarn --cwd packages/app add @backstage-community/plugin-servicenow
|
|
15
20
|
```
|
|
16
21
|
|
|
17
|
-
2.
|
|
22
|
+
2. **Add components to your app** (classic frontend system):
|
|
18
23
|
|
|
19
|
-
|
|
24
|
+
Open `packages/app/src/components/catalog/EntityPage.tsx` and add this imports:
|
|
25
|
+
|
|
26
|
+
```tsx
|
|
27
|
+
import {
|
|
28
|
+
EntityServicenowContent,
|
|
29
|
+
isServicenowAvailable,
|
|
30
|
+
isMyProfile,
|
|
31
|
+
} from '@backstage-community/plugin-servicenow';
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Then add the following **route** to the `serviceEntityPage`, `websiteEntityPage`,
|
|
35
|
+
and `defaultEntityPage` to show a ServiceNow tab if the annotation is configured:
|
|
36
|
+
|
|
37
|
+
```tsx
|
|
38
|
+
<EntityLayout.Route
|
|
39
|
+
path="/servicenow"
|
|
40
|
+
title="ServiceNow"
|
|
41
|
+
if={isServicenowAvailable}
|
|
42
|
+
>
|
|
43
|
+
<EntityServicenowContent />
|
|
44
|
+
</EntityLayout.Route>
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
You can also a "My ServiceNow Tickets" tab to the `userEntity`.
|
|
48
|
+
The `isMyProfile` condition shows this tab then only the entity of the current user:
|
|
20
49
|
|
|
21
|
-
|
|
50
|
+
```tsx
|
|
51
|
+
<EntityLayout.Route path="/servicenow" title="ServiceNow" if={isMyProfile}>
|
|
52
|
+
<EntityServicenowContent />
|
|
53
|
+
</EntityLayout.Route>
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
3. Or, if you're using the **new frontend system**:
|
|
57
|
+
|
|
58
|
+
In your `packages/app/src/App.tsx` add the plugin to your `createApp` `features` list:
|
|
22
59
|
|
|
23
|
-
|
|
24
|
-
|
|
60
|
+
```tsx
|
|
61
|
+
import servicenowPlugin from '@backstage-community/plugin-servicenow/alpha';
|
|
62
|
+
|
|
63
|
+
// ...
|
|
64
|
+
export default createApp({
|
|
65
|
+
features: [..., servicenowPlugin],
|
|
66
|
+
});
|
|
67
|
+
```
|
|
25
68
|
|
|
26
|
-
|
|
27
|
-
- Configuring tabs
|
|
28
|
-
- Using the `isMyProfile` condition on mount point configuration
|
|
69
|
+
## Configuration
|
|
29
70
|
|
|
30
|
-
|
|
71
|
+
To enable ServiceNow integration for an entity, add the following annotation to its metadata:
|
|
31
72
|
|
|
32
|
-
|
|
73
|
+
```yaml
|
|
74
|
+
metadata:
|
|
75
|
+
annotations:
|
|
76
|
+
servicenow.com/entity-id: my-servicenow-entity-id # has to match the value defined in the incident ticket `u_backstage_entity_id` field
|
|
77
|
+
```
|
|
33
78
|
|
|
34
|
-
|
|
79
|
+
For more information how to setup the plugin, please refer to the [General](../../docs/index.md) and [Configuration.md](../../docs/Configuration.md) documentation.
|
|
80
|
+
|
|
81
|
+
## Development
|
|
82
|
+
|
|
83
|
+
1. Install dependencies with `yarn install`
|
|
84
|
+
2. Start the Backstage dev app with `yarn start`
|
|
85
|
+
|
|
86
|
+
Your plugin has been added to the example app in this `plugins/servicenow` directory, meaning you'll be able to access it by running `yarn start` in the current directory, and then navigating to [/servicenow](http://localhost:3000/servicenow).
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { jsx } from 'react/jsx-runtime';
|
|
2
|
+
import { ApiBlueprint, createApiFactory, identityApiRef, fetchApiRef, discoveryApiRef, createFrontendPlugin } from '@backstage/frontend-plugin-api';
|
|
3
|
+
import { EntityContentBlueprint } from '@backstage/plugin-catalog-react/alpha';
|
|
4
|
+
import { ServiceNowBackendClient, serviceNowApiRef } from '../api/ServiceNowBackendClient.esm.js';
|
|
5
|
+
import { isServicenowAvailable } from '@backstage-community/plugin-servicenow-common';
|
|
6
|
+
|
|
7
|
+
const servicenowApi = ApiBlueprint.make({
|
|
8
|
+
params: {
|
|
9
|
+
factory: createApiFactory({
|
|
10
|
+
api: serviceNowApiRef,
|
|
11
|
+
deps: {
|
|
12
|
+
discoveryApi: discoveryApiRef,
|
|
13
|
+
fetchApi: fetchApiRef,
|
|
14
|
+
identityApi: identityApiRef
|
|
15
|
+
},
|
|
16
|
+
factory: ({ discoveryApi, fetchApi, identityApi }) => new ServiceNowBackendClient(discoveryApi, fetchApi, identityApi)
|
|
17
|
+
})
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
const entityServicenowContent = EntityContentBlueprint.make({
|
|
21
|
+
name: "EntityServicenowContent",
|
|
22
|
+
params: {
|
|
23
|
+
defaultPath: "servicenow",
|
|
24
|
+
defaultTitle: "ServiceNow",
|
|
25
|
+
filter: isServicenowAvailable,
|
|
26
|
+
loader: () => import('../components/Servicenow/index.esm.js').then((m) => /* @__PURE__ */ jsx(m.EntityServicenowContent, {}))
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
var plugin = createFrontendPlugin({
|
|
30
|
+
pluginId: "servicenow",
|
|
31
|
+
extensions: [servicenowApi, entityServicenowContent]
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
export { plugin as default, entityServicenowContent, servicenowApi };
|
|
35
|
+
//# sourceMappingURL=plugin.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin.esm.js","sources":["../../src/alpha/plugin.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 {\n ApiBlueprint,\n createApiFactory,\n createFrontendPlugin,\n discoveryApiRef,\n fetchApiRef,\n identityApiRef,\n} from '@backstage/frontend-plugin-api';\nimport { EntityContentBlueprint } from '@backstage/plugin-catalog-react/alpha';\nimport {\n serviceNowApiRef,\n ServiceNowBackendClient,\n} from '../api/ServiceNowBackendClient';\n\nimport { isServicenowAvailable } from '@backstage-community/plugin-servicenow-common';\n\n/** @alpha */\nexport const servicenowApi = ApiBlueprint.make({\n params: {\n factory: createApiFactory({\n api: serviceNowApiRef,\n deps: {\n discoveryApi: discoveryApiRef,\n fetchApi: fetchApiRef,\n identityApi: identityApiRef,\n },\n factory: ({ discoveryApi, fetchApi, identityApi }) =>\n new ServiceNowBackendClient(discoveryApi, fetchApi, identityApi),\n }),\n },\n});\n\n/**\n * Servicenow entity content that shows an incident table with a\n * filter similar to the catalog table\n * @alpha\n */\nexport const entityServicenowContent = EntityContentBlueprint.make({\n name: 'EntityServicenowContent',\n params: {\n defaultPath: 'servicenow',\n defaultTitle: 'ServiceNow',\n filter: isServicenowAvailable,\n loader: () =>\n import('../components/Servicenow').then(m => (\n <m.EntityServicenowContent />\n )),\n },\n});\n\n/** @alpha */\nexport default createFrontendPlugin({\n pluginId: 'servicenow',\n extensions: [servicenowApi, entityServicenowContent],\n});\n"],"names":[],"mappings":";;;;;;AAiCa,MAAA,aAAA,GAAgB,aAAa,IAAK,CAAA;AAAA,EAC7C,MAAQ,EAAA;AAAA,IACN,SAAS,gBAAiB,CAAA;AAAA,MACxB,GAAK,EAAA,gBAAA;AAAA,MACL,IAAM,EAAA;AAAA,QACJ,YAAc,EAAA,eAAA;AAAA,QACd,QAAU,EAAA,WAAA;AAAA,QACV,WAAa,EAAA;AAAA,OACf;AAAA,MACA,OAAA,EAAS,CAAC,EAAE,YAAc,EAAA,QAAA,EAAU,WAAY,EAAA,KAC9C,IAAI,uBAAA,CAAwB,YAAc,EAAA,QAAA,EAAU,WAAW;AAAA,KAClE;AAAA;AAEL,CAAC;AAOY,MAAA,uBAAA,GAA0B,uBAAuB,IAAK,CAAA;AAAA,EACjE,IAAM,EAAA,yBAAA;AAAA,EACN,MAAQ,EAAA;AAAA,IACN,WAAa,EAAA,YAAA;AAAA,IACb,YAAc,EAAA,YAAA;AAAA,IACd,MAAQ,EAAA,qBAAA;AAAA,IACR,MAAA,EAAQ,MACN,OAAO,uCAA0B,CAAA,CAAE,IAAK,CAAA,CAAA,CAAA,qBACrC,GAAA,CAAA,CAAA,CAAE,uBAAF,EAAA,EAA0B,CAC5B;AAAA;AAEP,CAAC;AAGD,aAAe,oBAAqB,CAAA;AAAA,EAClC,QAAU,EAAA,YAAA;AAAA,EACV,UAAA,EAAY,CAAC,aAAA,EAAe,uBAAuB;AACrD,CAAC,CAAA;;;;"}
|
package/dist/alpha.d.ts
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/// <reference types="react" />
|
|
2
|
+
import * as _backstage_catalog_model from '@backstage/catalog-model';
|
|
3
|
+
import * as react from 'react';
|
|
4
|
+
import * as _backstage_plugin_catalog_react_alpha from '@backstage/plugin-catalog-react/alpha';
|
|
5
|
+
import * as _backstage_frontend_plugin_api from '@backstage/frontend-plugin-api';
|
|
6
|
+
import * as _backstage_core_plugin_api from '@backstage/core-plugin-api';
|
|
7
|
+
|
|
8
|
+
/** @alpha */
|
|
9
|
+
declare const _default: _backstage_frontend_plugin_api.FrontendPlugin<{}, {}, {
|
|
10
|
+
"api:servicenow": _backstage_frontend_plugin_api.ExtensionDefinition<{
|
|
11
|
+
kind: "api";
|
|
12
|
+
name: undefined;
|
|
13
|
+
config: {};
|
|
14
|
+
configInput: {};
|
|
15
|
+
output: _backstage_frontend_plugin_api.ConfigurableExtensionDataRef<_backstage_core_plugin_api.AnyApiFactory, "core.api.factory", {}>;
|
|
16
|
+
inputs: {};
|
|
17
|
+
params: {
|
|
18
|
+
factory: _backstage_core_plugin_api.AnyApiFactory;
|
|
19
|
+
};
|
|
20
|
+
}>;
|
|
21
|
+
"entity-content:servicenow/EntityServicenowContent": _backstage_frontend_plugin_api.ExtensionDefinition<{
|
|
22
|
+
kind: "entity-content";
|
|
23
|
+
name: "EntityServicenowContent";
|
|
24
|
+
config: {
|
|
25
|
+
path: string | undefined;
|
|
26
|
+
title: string | undefined;
|
|
27
|
+
filter: _backstage_plugin_catalog_react_alpha.EntityPredicate | undefined;
|
|
28
|
+
group: string | false | undefined;
|
|
29
|
+
};
|
|
30
|
+
configInput: {
|
|
31
|
+
filter?: _backstage_plugin_catalog_react_alpha.EntityPredicate | undefined;
|
|
32
|
+
title?: string | undefined;
|
|
33
|
+
path?: string | undefined;
|
|
34
|
+
group?: string | false | undefined;
|
|
35
|
+
};
|
|
36
|
+
output: _backstage_frontend_plugin_api.ConfigurableExtensionDataRef<react.JSX.Element, "core.reactElement", {}> | _backstage_frontend_plugin_api.ConfigurableExtensionDataRef<string, "core.routing.path", {}> | _backstage_frontend_plugin_api.ConfigurableExtensionDataRef<_backstage_frontend_plugin_api.RouteRef<_backstage_frontend_plugin_api.AnyRouteRefParams>, "core.routing.ref", {
|
|
37
|
+
optional: true;
|
|
38
|
+
}> | _backstage_frontend_plugin_api.ConfigurableExtensionDataRef<string, "catalog.entity-content-title", {}> | _backstage_frontend_plugin_api.ConfigurableExtensionDataRef<(entity: _backstage_catalog_model.Entity) => boolean, "catalog.entity-filter-function", {
|
|
39
|
+
optional: true;
|
|
40
|
+
}> | _backstage_frontend_plugin_api.ConfigurableExtensionDataRef<string, "catalog.entity-filter-expression", {
|
|
41
|
+
optional: true;
|
|
42
|
+
}> | _backstage_frontend_plugin_api.ConfigurableExtensionDataRef<string, "catalog.entity-content-group", {
|
|
43
|
+
optional: true;
|
|
44
|
+
}>;
|
|
45
|
+
inputs: {};
|
|
46
|
+
params: {
|
|
47
|
+
loader: () => Promise<JSX.Element>;
|
|
48
|
+
defaultPath: string;
|
|
49
|
+
defaultTitle: string;
|
|
50
|
+
defaultGroup?: (string & {}) | "overview" | "documentation" | "development" | "deployment" | "operation" | "observability" | undefined;
|
|
51
|
+
routeRef?: _backstage_frontend_plugin_api.RouteRef<_backstage_frontend_plugin_api.AnyRouteRefParams> | undefined;
|
|
52
|
+
filter?: _backstage_plugin_catalog_react_alpha.EntityPredicate | ((entity: _backstage_catalog_model.Entity) => boolean) | undefined;
|
|
53
|
+
};
|
|
54
|
+
}>;
|
|
55
|
+
}>;
|
|
56
|
+
|
|
57
|
+
export { _default as default };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"alpha.esm.js","sources":[],"sourcesContent":[],"names":[],"mappings":""}
|
package/dist/components/Servicenow/{ServicenowContent.esm.js → EntityServicenowContent.esm.js}
RENAMED
|
@@ -4,26 +4,26 @@ import { useApi, identityApiRef } from '@backstage/core-plugin-api';
|
|
|
4
4
|
import { useEntity, CatalogFilterLayout } from '@backstage/plugin-catalog-react';
|
|
5
5
|
import { Table } from '@backstage/core-components';
|
|
6
6
|
import Box from '@mui/material/Box';
|
|
7
|
+
import CircularProgress from '@mui/material/CircularProgress';
|
|
8
|
+
import TableBody from '@mui/material/TableBody';
|
|
9
|
+
import TableRow from '@mui/material/TableRow';
|
|
10
|
+
import TableCell from '@mui/material/TableCell';
|
|
7
11
|
import TablePagination from '@mui/material/TablePagination';
|
|
12
|
+
import { SortingOrderEnum, ServiceAnnotationFieldName } from '@backstage-community/plugin-servicenow-common';
|
|
8
13
|
import { IncidentsFilter } from './IncidentsFilter.esm.js';
|
|
9
14
|
import { IncidentsListColumns } from './IncidentsListColumns.esm.js';
|
|
10
15
|
import { IncidentsTableBody } from './IncidentsTableBody.esm.js';
|
|
11
16
|
import { IncidentsTableHeader } from './IncidentsTableHeader.esm.js';
|
|
12
17
|
import { IncidentTableFieldEnum } from '../../types.esm.js';
|
|
13
|
-
import { SortingOrderEnum, ServiceAnnotationFieldName } from '@backstage-community/plugin-servicenow-common';
|
|
14
18
|
import { buildIncidentQueryParams } from '../../utils/queryParamsUtils.esm.js';
|
|
15
19
|
import { useDebouncedValue } from '../../hooks/useDebouncedValue.esm.js';
|
|
16
20
|
import { useQueryState } from '../../hooks/useQueryState.esm.js';
|
|
17
21
|
import { useQueryArrayState } from '../../hooks/useQueryArrayState.esm.js';
|
|
18
22
|
import { serviceNowApiRef } from '../../api/ServiceNowBackendClient.esm.js';
|
|
19
23
|
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
24
|
import { useUpdateQueryParams } from '../../hooks/useQueryHelpers.esm.js';
|
|
25
25
|
|
|
26
|
-
const
|
|
26
|
+
const EntityServicenowContent = () => {
|
|
27
27
|
const { entity } = useEntity();
|
|
28
28
|
const serviceNowApi = useApi(serviceNowApiRef);
|
|
29
29
|
const [search, setSearch] = useQueryState("search", "");
|
|
@@ -192,5 +192,5 @@ const ServicenowContent = () => {
|
|
|
192
192
|
] }) });
|
|
193
193
|
};
|
|
194
194
|
|
|
195
|
-
export {
|
|
196
|
-
//# sourceMappingURL=
|
|
195
|
+
export { EntityServicenowContent };
|
|
196
|
+
//# sourceMappingURL=EntityServicenowContent.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EntityServicenowContent.esm.js","sources":["../../../src/components/Servicenow/EntityServicenowContent.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';\n\nimport Box from '@mui/material/Box';\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 TablePagination from '@mui/material/TablePagination';\n\nimport {\n Order,\n SortingOrderEnum,\n ServiceAnnotationFieldName,\n} from '@backstage-community/plugin-servicenow-common';\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 { 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 { useUpdateQueryParams } from '../../hooks/useQueryHelpers';\n\nexport const EntityServicenowContent = () => {\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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"}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
2
|
+
import { useMemo, useCallback } from 'react';
|
|
2
3
|
import Box from '@mui/material/Box';
|
|
3
4
|
import { IncidentEnumFilter } from '../shared-components/IncidentEnumFilter.esm.js';
|
|
4
5
|
import { INCIDENT_STATE_MAP, PRIORITY_MAP } from '../../utils/incidentUtils.esm.js';
|
|
5
6
|
import { useQueryArrayFilter } from '../../hooks/useQueryArrayFilter.esm.js';
|
|
6
|
-
import { useMemo, useCallback } from 'react';
|
|
7
7
|
import { useUpdateQueryParams } from '../../hooks/useQueryHelpers.esm.js';
|
|
8
8
|
|
|
9
9
|
const IncidentsFilter = () => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"IncidentsFilter.esm.js","sources":["../../../src/components/Servicenow/IncidentsFilter.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 Box from '@mui/material/Box';\nimport { IncidentEnumFilter } from '../shared-components/IncidentEnumFilter';\nimport { INCIDENT_STATE_MAP, PRIORITY_MAP } from '../../utils/incidentUtils';\nimport { useQueryArrayFilter } from '../../hooks/useQueryArrayFilter';\nimport {
|
|
1
|
+
{"version":3,"file":"IncidentsFilter.esm.js","sources":["../../../src/components/Servicenow/IncidentsFilter.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 { useMemo, useCallback } from 'react';\n\nimport { SelectItem } from '@backstage/core-components';\n\nimport Box from '@mui/material/Box';\n\nimport { IncidentEnumFilter } from '../shared-components/IncidentEnumFilter';\nimport { INCIDENT_STATE_MAP, PRIORITY_MAP } from '../../utils/incidentUtils';\nimport { useQueryArrayFilter } from '../../hooks/useQueryArrayFilter';\nimport { useUpdateQueryParams } from '../../hooks/useQueryHelpers';\n\nexport const IncidentsFilter = () => {\n const stateFilter = useQueryArrayFilter('state');\n const priorityFilter = useQueryArrayFilter('priority');\n const updateQueryParams = useUpdateQueryParams();\n\n const stateValue = useMemo(() => stateFilter.current, [stateFilter]);\n const priorityValue = useMemo(() => priorityFilter.current, [priorityFilter]);\n\n const handleStateChange = useCallback(\n (_e: any, value: SelectItem[]) => {\n updateQueryParams({\n state: value.map(v => v.value).join(','),\n offset: '0',\n });\n },\n [updateQueryParams],\n );\n\n const handlePriorityChange = useCallback(\n (_e: any, value: SelectItem[]) => {\n updateQueryParams({\n priority: value.map(v => v.value).join(','),\n offset: '0',\n });\n },\n [updateQueryParams],\n );\n\n return (\n <Box\n sx={{\n display: 'flex',\n flexDirection: 'column',\n gap: 2,\n minWidth: { xs: 200, lg: 'auto' },\n }}\n >\n <IncidentEnumFilter\n label=\"State\"\n filterKey=\"state\"\n dataMap={INCIDENT_STATE_MAP}\n value={stateValue}\n onChange={handleStateChange}\n />\n <IncidentEnumFilter\n label=\"Priority\"\n filterKey=\"priority\"\n dataMap={PRIORITY_MAP}\n value={priorityValue}\n onChange={handlePriorityChange}\n />\n </Box>\n );\n};\n"],"names":[],"mappings":";;;;;;;;AA2BO,MAAM,kBAAkB,MAAM;AACnC,EAAM,MAAA,WAAA,GAAc,oBAAoB,OAAO,CAAA;AAC/C,EAAM,MAAA,cAAA,GAAiB,oBAAoB,UAAU,CAAA;AACrD,EAAA,MAAM,oBAAoB,oBAAqB,EAAA;AAE/C,EAAA,MAAM,aAAa,OAAQ,CAAA,MAAM,YAAY,OAAS,EAAA,CAAC,WAAW,CAAC,CAAA;AACnE,EAAA,MAAM,gBAAgB,OAAQ,CAAA,MAAM,eAAe,OAAS,EAAA,CAAC,cAAc,CAAC,CAAA;AAE5E,EAAA,MAAM,iBAAoB,GAAA,WAAA;AAAA,IACxB,CAAC,IAAS,KAAwB,KAAA;AAChC,MAAkB,iBAAA,CAAA;AAAA,QAChB,KAAA,EAAO,MAAM,GAAI,CAAA,CAAA,CAAA,KAAK,EAAE,KAAK,CAAA,CAAE,KAAK,GAAG,CAAA;AAAA,QACvC,MAAQ,EAAA;AAAA,OACT,CAAA;AAAA,KACH;AAAA,IACA,CAAC,iBAAiB;AAAA,GACpB;AAEA,EAAA,MAAM,oBAAuB,GAAA,WAAA;AAAA,IAC3B,CAAC,IAAS,KAAwB,KAAA;AAChC,MAAkB,iBAAA,CAAA;AAAA,QAChB,QAAA,EAAU,MAAM,GAAI,CAAA,CAAA,CAAA,KAAK,EAAE,KAAK,CAAA,CAAE,KAAK,GAAG,CAAA;AAAA,QAC1C,MAAQ,EAAA;AAAA,OACT,CAAA;AAAA,KACH;AAAA,IACA,CAAC,iBAAiB;AAAA,GACpB;AAEA,EACE,uBAAA,IAAA;AAAA,IAAC,GAAA;AAAA,IAAA;AAAA,MACC,EAAI,EAAA;AAAA,QACF,OAAS,EAAA,MAAA;AAAA,QACT,aAAe,EAAA,QAAA;AAAA,QACf,GAAK,EAAA,CAAA;AAAA,QACL,QAAU,EAAA,EAAE,EAAI,EAAA,GAAA,EAAK,IAAI,MAAO;AAAA,OAClC;AAAA,MAEA,QAAA,EAAA;AAAA,wBAAA,GAAA;AAAA,UAAC,kBAAA;AAAA,UAAA;AAAA,YACC,KAAM,EAAA,OAAA;AAAA,YACN,SAAU,EAAA,OAAA;AAAA,YACV,OAAS,EAAA,kBAAA;AAAA,YACT,KAAO,EAAA,UAAA;AAAA,YACP,QAAU,EAAA;AAAA;AAAA,SACZ;AAAA,wBACA,GAAA;AAAA,UAAC,kBAAA;AAAA,UAAA;AAAA,YACC,KAAM,EAAA,UAAA;AAAA,YACN,SAAU,EAAA,UAAA;AAAA,YACV,OAAS,EAAA,YAAA;AAAA,YACT,KAAO,EAAA,aAAA;AAAA,YACP,QAAU,EAAA;AAAA;AAAA;AACZ;AAAA;AAAA,GACF;AAEJ;;;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"IncidentsTableHeader.esm.js","sources":["../../../src/components/Servicenow/IncidentsTableHeader.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 { MouseEvent } from 'react';\nimport TableCell from '@mui/material/TableCell';\nimport TableHead from '@mui/material/TableHead';\nimport TableRow from '@mui/material/TableRow';\nimport TableSortLabel from '@mui/material/TableSortLabel';\n\nimport
|
|
1
|
+
{"version":3,"file":"IncidentsTableHeader.esm.js","sources":["../../../src/components/Servicenow/IncidentsTableHeader.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 { MouseEvent } from 'react';\nimport TableCell from '@mui/material/TableCell';\nimport TableHead from '@mui/material/TableHead';\nimport TableRow from '@mui/material/TableRow';\nimport TableSortLabel from '@mui/material/TableSortLabel';\n\nimport type { Order } from '@backstage-community/plugin-servicenow-common';\n\nimport { IncidentsListColumns } from './IncidentsListColumns';\nimport { IncidentTableField } from '../../types';\n\ntype IncidentsTableHeaderProps = {\n order: Order;\n orderBy: IncidentTableField | undefined;\n onRequestSort: (\n event: MouseEvent<unknown>,\n property: IncidentTableField,\n ) => void;\n};\n\nexport const IncidentsTableHeader = ({\n order,\n orderBy,\n onRequestSort,\n}: IncidentsTableHeaderProps) => {\n const createSortHandler =\n (property: IncidentTableField) => (event: MouseEvent<unknown>) => {\n onRequestSort(event, property);\n };\n\n return (\n <TableHead>\n <TableRow sx={{ marginLeft: '6px', borderBottom: '1px solid #e0e0e0' }}>\n {IncidentsListColumns.map(column => (\n <TableCell\n key={column.id as string}\n align=\"left\"\n padding=\"normal\"\n style={{\n lineHeight: '1.5rem',\n fontSize: '0.875rem',\n padding: '24px 16px 24px 20px',\n fontWeight: '700',\n }}\n sortDirection={orderBy === column.field ? order : 'asc'}\n >\n <TableSortLabel\n active={orderBy === column.field}\n direction={orderBy === column.field ? order : 'asc'}\n onClick={\n column.sorting === false\n ? undefined\n : createSortHandler(column.field as IncidentTableField)\n }\n disabled={column.sorting === false}\n >\n {column.title}\n </TableSortLabel>\n </TableCell>\n ))}\n </TableRow>\n </TableHead>\n );\n};\n"],"names":[],"mappings":";;;;;;;AAoCO,MAAM,uBAAuB,CAAC;AAAA,EACnC,KAAA;AAAA,EACA,OAAA;AAAA,EACA;AACF,CAAiC,KAAA;AAC/B,EAAA,MAAM,iBACJ,GAAA,CAAC,QAAiC,KAAA,CAAC,KAA+B,KAAA;AAChE,IAAA,aAAA,CAAc,OAAO,QAAQ,CAAA;AAAA,GAC/B;AAEF,EAAA,uBACG,GAAA,CAAA,SAAA,EAAA,EACC,QAAC,kBAAA,GAAA,CAAA,QAAA,EAAA,EAAS,EAAI,EAAA,EAAE,UAAY,EAAA,KAAA,EAAO,YAAc,EAAA,mBAAA,EAC9C,EAAA,QAAA,EAAA,oBAAA,CAAqB,IAAI,CACxB,MAAA,qBAAA,GAAA;AAAA,IAAC,SAAA;AAAA,IAAA;AAAA,MAEC,KAAM,EAAA,MAAA;AAAA,MACN,OAAQ,EAAA,QAAA;AAAA,MACR,KAAO,EAAA;AAAA,QACL,UAAY,EAAA,QAAA;AAAA,QACZ,QAAU,EAAA,UAAA;AAAA,QACV,OAAS,EAAA,qBAAA;AAAA,QACT,UAAY,EAAA;AAAA,OACd;AAAA,MACA,aAAe,EAAA,OAAA,KAAY,MAAO,CAAA,KAAA,GAAQ,KAAQ,GAAA,KAAA;AAAA,MAElD,QAAA,kBAAA,GAAA;AAAA,QAAC,cAAA;AAAA,QAAA;AAAA,UACC,MAAA,EAAQ,YAAY,MAAO,CAAA,KAAA;AAAA,UAC3B,SAAW,EAAA,OAAA,KAAY,MAAO,CAAA,KAAA,GAAQ,KAAQ,GAAA,KAAA;AAAA,UAC9C,SACE,MAAO,CAAA,OAAA,KAAY,QACf,MACA,GAAA,iBAAA,CAAkB,OAAO,KAA2B,CAAA;AAAA,UAE1D,QAAA,EAAU,OAAO,OAAY,KAAA,KAAA;AAAA,UAE5B,QAAO,EAAA,MAAA,CAAA;AAAA;AAAA;AACV,KAAA;AAAA,IAtBK,MAAO,CAAA;AAAA,GAwBf,GACH,CACF,EAAA,CAAA;AAEJ;;;;"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { EntityServicenowContent } from './EntityServicenowContent.esm.js';
|
|
2
2
|
//# sourceMappingURL=index.esm.js.map
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
export { isServicenowAvailable } from '@backstage-community/plugin-servicenow-common';
|
|
1
2
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
3
|
import * as _backstage_core_plugin_api from '@backstage/core-plugin-api';
|
|
3
4
|
import { Entity } from '@backstage/catalog-model';
|
|
@@ -10,14 +11,22 @@ declare const servicenowPlugin: _backstage_core_plugin_api.BackstagePlugin<{
|
|
|
10
11
|
root: _backstage_core_plugin_api.RouteRef<undefined>;
|
|
11
12
|
}, {}, {}>;
|
|
12
13
|
/**
|
|
13
|
-
* Servicenow
|
|
14
|
+
* Servicenow entity content that shows an incident table with a
|
|
15
|
+
* filter similar to the catalog table
|
|
14
16
|
* @public
|
|
15
17
|
*/
|
|
16
|
-
declare const
|
|
18
|
+
declare const EntityServicenowContent: () => react_jsx_runtime.JSX.Element;
|
|
17
19
|
/**
|
|
20
|
+
* @deprecated please use `EntityServicenowCard` instead, this page
|
|
21
|
+
* might be removed in future releases or replaced with a page
|
|
22
|
+
* that lists the incidents assigned to the current user.
|
|
18
23
|
* @public
|
|
24
|
+
*/
|
|
25
|
+
declare const ServicenowPage: () => react_jsx_runtime.JSX.Element;
|
|
26
|
+
/**
|
|
19
27
|
* Check if the current entity is the logged-in user
|
|
28
|
+
* @public
|
|
20
29
|
*/
|
|
21
30
|
declare const isMyProfile: (entity: Entity) => boolean;
|
|
22
31
|
|
|
23
|
-
export { ServicenowPage, isMyProfile, servicenowPlugin };
|
|
32
|
+
export { EntityServicenowContent, ServicenowPage, isMyProfile, servicenowPlugin };
|
package/dist/index.esm.js
CHANGED
|
@@ -1,2 +1,3 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { isServicenowAvailable } from '@backstage-community/plugin-servicenow-common';
|
|
2
|
+
export { EntityServicenowContent, ServicenowPage, isMyProfile, servicenowPlugin } from './plugin.esm.js';
|
|
2
3
|
//# sourceMappingURL=index.esm.js.map
|
package/dist/index.esm.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.esm.js","sources":[],"sourcesContent":[],"names":[],"mappings":""}
|
|
1
|
+
{"version":3,"file":"index.esm.js","sources":[],"sourcesContent":[],"names":[],"mappings":";"}
|
package/dist/plugin.esm.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { createPlugin, createApiFactory, identityApiRef, fetchApiRef, discoveryApiRef,
|
|
1
|
+
import { createPlugin, createApiFactory, identityApiRef, fetchApiRef, discoveryApiRef, createComponentExtension } from '@backstage/core-plugin-api';
|
|
2
|
+
import { parseEntityRef, stringifyEntityRef } from '@backstage/catalog-model';
|
|
2
3
|
import { rootRouteRef } from './routes.esm.js';
|
|
3
4
|
import { ServiceNowBackendClient, serviceNowApiRef } from './api/ServiceNowBackendClient.esm.js';
|
|
4
|
-
import { parseEntityRef, stringifyEntityRef } from '@backstage/catalog-model';
|
|
5
5
|
|
|
6
6
|
const servicenowPlugin = createPlugin({
|
|
7
7
|
id: "servicenow",
|
|
@@ -20,13 +20,15 @@ const servicenowPlugin = createPlugin({
|
|
|
20
20
|
root: rootRouteRef
|
|
21
21
|
}
|
|
22
22
|
});
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
name: "
|
|
26
|
-
component:
|
|
27
|
-
|
|
23
|
+
const EntityServicenowContent = servicenowPlugin.provide(
|
|
24
|
+
createComponentExtension({
|
|
25
|
+
name: "EntityServicenowContent",
|
|
26
|
+
component: {
|
|
27
|
+
lazy: () => import('./components/Servicenow/index.esm.js').then((m) => m.EntityServicenowContent)
|
|
28
|
+
}
|
|
28
29
|
})
|
|
29
30
|
);
|
|
31
|
+
const ServicenowPage = EntityServicenowContent;
|
|
30
32
|
const isMyProfile = (entity) => {
|
|
31
33
|
const currentUserRef = localStorage.getItem("userEntityRef");
|
|
32
34
|
if (!currentUserRef) return false;
|
|
@@ -39,5 +41,5 @@ const isMyProfile = (entity) => {
|
|
|
39
41
|
}
|
|
40
42
|
};
|
|
41
43
|
|
|
42
|
-
export { ServicenowPage, isMyProfile, servicenowPlugin };
|
|
44
|
+
export { EntityServicenowContent, ServicenowPage, isMyProfile, servicenowPlugin };
|
|
43
45
|
//# sourceMappingURL=plugin.esm.js.map
|
package/dist/plugin.esm.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.esm.js","sources":["../src/plugin.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 {\n createApiFactory,\n
|
|
1
|
+
{"version":3,"file":"plugin.esm.js","sources":["../src/plugin.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 {\n createApiFactory,\n createComponentExtension,\n createPlugin,\n discoveryApiRef,\n fetchApiRef,\n identityApiRef,\n} from '@backstage/core-plugin-api';\nimport {\n Entity,\n parseEntityRef,\n stringifyEntityRef,\n} from '@backstage/catalog-model';\n\nimport { rootRouteRef } from './routes';\nimport {\n serviceNowApiRef,\n ServiceNowBackendClient,\n} from './api/ServiceNowBackendClient';\n\n/**\n * Servicenow Plugin\n * @public\n */\nexport const servicenowPlugin = createPlugin({\n id: 'servicenow',\n apis: [\n createApiFactory({\n api: serviceNowApiRef,\n deps: {\n discoveryApi: discoveryApiRef,\n fetchApi: fetchApiRef,\n identityApi: identityApiRef,\n },\n factory: ({ discoveryApi, fetchApi, identityApi }) =>\n new ServiceNowBackendClient(discoveryApi, fetchApi, identityApi),\n }),\n ],\n routes: {\n root: rootRouteRef,\n },\n});\n\n/**\n * Servicenow entity content that shows an incident table with a\n * filter similar to the catalog table\n * @public\n */\nexport const EntityServicenowContent = servicenowPlugin.provide(\n createComponentExtension({\n name: 'EntityServicenowContent',\n component: {\n lazy: () =>\n import('./components/Servicenow').then(m => m.EntityServicenowContent),\n },\n }),\n);\n\n/**\n * @deprecated please use `EntityServicenowCard` instead, this page\n * might be removed in future releases or replaced with a page\n * that lists the incidents assigned to the current user.\n * @public\n */\nexport const ServicenowPage = EntityServicenowContent;\n\n/**\n * Check if the current entity is the logged-in user\n * @public\n */\nexport const isMyProfile = (entity: Entity): boolean => {\n const currentUserRef = localStorage.getItem('userEntityRef');\n if (!currentUserRef) return false;\n\n try {\n const a = parseEntityRef(currentUserRef);\n const b = parseEntityRef(stringifyEntityRef(entity));\n return (\n a.kind.toLowerCase() === b.kind.toLowerCase() &&\n a.namespace.toLowerCase() === b.namespace.toLowerCase() &&\n a.name.toLowerCase() === b.name.toLowerCase()\n );\n } catch {\n return false;\n }\n};\n"],"names":[],"mappings":";;;;;AAuCO,MAAM,mBAAmB,YAAa,CAAA;AAAA,EAC3C,EAAI,EAAA,YAAA;AAAA,EACJ,IAAM,EAAA;AAAA,IACJ,gBAAiB,CAAA;AAAA,MACf,GAAK,EAAA,gBAAA;AAAA,MACL,IAAM,EAAA;AAAA,QACJ,YAAc,EAAA,eAAA;AAAA,QACd,QAAU,EAAA,WAAA;AAAA,QACV,WAAa,EAAA;AAAA,OACf;AAAA,MACA,OAAA,EAAS,CAAC,EAAE,YAAc,EAAA,QAAA,EAAU,WAAY,EAAA,KAC9C,IAAI,uBAAA,CAAwB,YAAc,EAAA,QAAA,EAAU,WAAW;AAAA,KAClE;AAAA,GACH;AAAA,EACA,MAAQ,EAAA;AAAA,IACN,IAAM,EAAA;AAAA;AAEV,CAAC;AAOM,MAAM,0BAA0B,gBAAiB,CAAA,OAAA;AAAA,EACtD,wBAAyB,CAAA;AAAA,IACvB,IAAM,EAAA,yBAAA;AAAA,IACN,SAAW,EAAA;AAAA,MACT,IAAA,EAAM,MACJ,OAAO,sCAAyB,EAAE,IAAK,CAAA,CAAA,CAAA,KAAK,EAAE,uBAAuB;AAAA;AACzE,GACD;AACH;AAQO,MAAM,cAAiB,GAAA;AAMjB,MAAA,WAAA,GAAc,CAAC,MAA4B,KAAA;AACtD,EAAM,MAAA,cAAA,GAAiB,YAAa,CAAA,OAAA,CAAQ,eAAe,CAAA;AAC3D,EAAI,IAAA,CAAC,gBAAuB,OAAA,KAAA;AAE5B,EAAI,IAAA;AACF,IAAM,MAAA,CAAA,GAAI,eAAe,cAAc,CAAA;AACvC,IAAA,MAAM,CAAI,GAAA,cAAA,CAAe,kBAAmB,CAAA,MAAM,CAAC,CAAA;AACnD,IACE,OAAA,CAAA,CAAE,KAAK,WAAY,EAAA,KAAM,EAAE,IAAK,CAAA,WAAA,EAChC,IAAA,CAAA,CAAE,SAAU,CAAA,WAAA,OAAkB,CAAE,CAAA,SAAA,CAAU,aAC1C,IAAA,CAAA,CAAE,KAAK,WAAY,EAAA,KAAM,CAAE,CAAA,IAAA,CAAK,WAAY,EAAA;AAAA,GAExC,CAAA,MAAA;AACN,IAAO,OAAA,KAAA;AAAA;AAEX;;;;"}
|
package/package.json
CHANGED
|
@@ -1,19 +1,41 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@backstage-community/plugin-servicenow",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
|
-
"main": "dist/index.esm.js",
|
|
6
|
-
"types": "dist/index.d.ts",
|
|
5
|
+
"main": "./dist/index.esm.js",
|
|
6
|
+
"types": "./dist/index.d.ts",
|
|
7
7
|
"publishConfig": {
|
|
8
|
-
"access": "public"
|
|
9
|
-
"main": "dist/index.esm.js",
|
|
10
|
-
"types": "dist/index.d.ts"
|
|
8
|
+
"access": "public"
|
|
11
9
|
},
|
|
12
10
|
"repository": {
|
|
13
11
|
"type": "git",
|
|
14
12
|
"url": "https://github.com/backstage/community-plugins",
|
|
15
13
|
"directory": "workspaces/servicenow/plugins/servicenow"
|
|
16
14
|
},
|
|
15
|
+
"exports": {
|
|
16
|
+
".": {
|
|
17
|
+
"import": "./dist/index.esm.js",
|
|
18
|
+
"types": "./dist/index.d.ts",
|
|
19
|
+
"default": "./dist/index.esm.js"
|
|
20
|
+
},
|
|
21
|
+
"./alpha": {
|
|
22
|
+
"backstage": "@backstage/FrontendPlugin",
|
|
23
|
+
"import": "./dist/alpha.esm.js",
|
|
24
|
+
"types": "./dist/alpha.d.ts",
|
|
25
|
+
"default": "./dist/alpha.esm.js"
|
|
26
|
+
},
|
|
27
|
+
"./package.json": "./package.json"
|
|
28
|
+
},
|
|
29
|
+
"typesVersions": {
|
|
30
|
+
"*": {
|
|
31
|
+
"alpha": [
|
|
32
|
+
"dist/alpha.d.ts"
|
|
33
|
+
],
|
|
34
|
+
"package.json": [
|
|
35
|
+
"package.json"
|
|
36
|
+
]
|
|
37
|
+
}
|
|
38
|
+
},
|
|
17
39
|
"backstage": {
|
|
18
40
|
"role": "frontend-plugin",
|
|
19
41
|
"pluginId": "servicenow",
|
|
@@ -21,7 +43,10 @@
|
|
|
21
43
|
"@backstage-community/plugin-servicenow",
|
|
22
44
|
"@backstage-community/plugin-servicenow-backend",
|
|
23
45
|
"@backstage-community/plugin-servicenow-common"
|
|
24
|
-
]
|
|
46
|
+
],
|
|
47
|
+
"features": {
|
|
48
|
+
"./alpha": "@backstage/FrontendPlugin"
|
|
49
|
+
}
|
|
25
50
|
},
|
|
26
51
|
"sideEffects": false,
|
|
27
52
|
"scripts": {
|
|
@@ -34,16 +59,18 @@
|
|
|
34
59
|
"postpack": "backstage-cli package postpack"
|
|
35
60
|
},
|
|
36
61
|
"dependencies": {
|
|
37
|
-
"@backstage-community/plugin-servicenow-common": "^1.
|
|
62
|
+
"@backstage-community/plugin-servicenow-common": "^1.2.0",
|
|
38
63
|
"@backstage/catalog-model": "^1.7.5",
|
|
64
|
+
"@backstage/core-compat-api": "^0.4.4",
|
|
39
65
|
"@backstage/core-components": "^0.17.4",
|
|
40
66
|
"@backstage/core-plugin-api": "^1.10.9",
|
|
67
|
+
"@backstage/frontend-plugin-api": "^0.10.4",
|
|
41
68
|
"@backstage/plugin-catalog-react": "^1.19.1",
|
|
42
69
|
"@backstage/theme": "^0.6.7",
|
|
43
70
|
"@mui/icons-material": "^5.15.17",
|
|
44
71
|
"@mui/lab": "^5.0.0-alpha.153",
|
|
45
72
|
"@mui/material": "^5.17.1",
|
|
46
|
-
"@mui/styles": "5.
|
|
73
|
+
"@mui/styles": "5.18.0",
|
|
47
74
|
"react-dom": "^16.13.1 || ^17.0.0 || ^18.0.0",
|
|
48
75
|
"react-router-dom": "^6.0.0",
|
|
49
76
|
"react-use": "^17.2.4"
|
|
@@ -77,12 +104,5 @@
|
|
|
77
104
|
"@christoph-jerolimov"
|
|
78
105
|
],
|
|
79
106
|
"author": "Red Hat",
|
|
80
|
-
"typesVersions": {
|
|
81
|
-
"*": {
|
|
82
|
-
"package.json": [
|
|
83
|
-
"package.json"
|
|
84
|
-
]
|
|
85
|
-
}
|
|
86
|
-
},
|
|
87
107
|
"module": "./dist/index.esm.js"
|
|
88
108
|
}
|
|
@@ -1 +0,0 @@
|
|
|
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":";;;;;;;;;;;;;;;;;;;;;;;;;AAqDO,MAAM,oBAAoB,MAAM;AACrC,EAAM,MAAA,EAAE,MAAO,EAAA,GAAI,SAAU,EAAA;AAC7B,EAAM,MAAA,aAAA,GAAgB,OAAO,gBAAgB,CAAA;AAE7C,EAAA,MAAM,CAAC,MAAQ,EAAA,SAAS,CAAI,GAAA,aAAA,CAAsB,UAAU,EAAE,CAAA;AAC9D,EAAA,MAAM,CAAC,KAAA,EAAO,QAAQ,CAAA,GAAI,SAAS,MAAM,CAAA;AACzC,EAAM,MAAA,cAAA,GAAiB,iBAAkB,CAAA,KAAA,EAAO,GAAG,CAAA;AAEnD,EAAA,MAAM,CAAC,KAAO,EAAA,QAAQ,IAAI,aAAqB,CAAA,OAAA,EAAS,iBAAiB,GAAG,CAAA;AAC5E,EAAM,MAAA,CAAC,OAAS,EAAA,UAAU,CAAI,GAAA,aAAA;AAAA,IAC5B,SAAA;AAAA,IACA,sBAAuB,CAAA;AAAA,GACzB;AAEA,EAAA,MAAM,CAAC,WAAW,CAAI,GAAA,aAAA,CAAsB,SAAS,CAAC,CAAA;AACtD,EAAA,MAAM,CAAC,MAAQ,EAAA,SAAS,CAAI,GAAA,aAAA,CAAsB,UAAU,CAAC,CAAA;AAC7D,EAAA,MAAM,CAAC,KAAK,CAAA,GAAI,kBAAmB,CAAA,OAAA,EAAS,EAAE,CAAA;AAC9C,EAAA,MAAM,CAAC,QAAQ,CAAA,GAAI,kBAAmB,CAAA,UAAA,EAAY,EAAE,CAAA;AAEpD,EAAA,MAAM,UAAa,GAAA,IAAA,CAAK,KAAM,CAAA,MAAA,GAAS,WAAW,CAAA;AAElD,EAAA,MAAM,CAAC,KAAA,EAAO,QAAQ,CAAA,GAAI,SAAiB,CAAC,CAAA;AAC5C,EAAA,MAAM,CAAC,aAAe,EAAA,gBAAgB,CAAI,GAAA,QAAA,CAA0B,EAAE,CAAA;AACtE,EAAA,MAAM,CAAC,OAAA,EAAS,UAAU,CAAA,GAAI,SAAS,KAAK,CAAA;AAC5C,EAAA,MAAM,CAAC,KAAA,EAAO,QAAQ,CAAA,GAAI,SAAwB,IAAI,CAAA;AACtD,EAAA,MAAM,oBAAoB,oBAAqB,EAAA;AAE/C,EAAA,MAAM,QAAW,GAAA,MAAA,CAAO,QAAS,CAAA,WAAA,GAAc,0BAA0B,CAAA;AAEzE,EAAA,MAAM,IAAO,GAAA,MAAA,CAAO,IAAK,CAAA,iBAAA,CAAkB,OAAO,CAAA;AAClD,EAAM,MAAA,SAAA,GAAY,aAAa,IAAI,CAAA;AAEnC,EAAM,MAAA,WAAA,GAAc,OAAO,cAAc,CAAA;AAEzC,EAAA,SAAA,CAAU,MAAM;AACd,IAAY,WAAA,CAAA,oBAAA,EAAuB,CAAA,IAAA,CAAK,CAAY,QAAA,KAAA;AAClD,MAAA,IAAI,SAAS,aAAe,EAAA;AAC1B,QAAa,YAAA,CAAA,OAAA,CAAQ,eAAiB,EAAA,QAAA,CAAS,aAAa,CAAA;AAAA;AAC9D,KACD,CAAA;AAAA,GACH,EAAG,CAAC,WAAW,CAAC,CAAA;AAEhB,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,IAAI,mBAAmB,MAAQ,EAAA;AAC7B,MAAA,SAAA,CAAU,cAAc,CAAA;AACxB,MAAA,SAAA,CAAU,CAAC,CAAA;AAAA;AACb,KACC,CAAC,cAAA,EAAgB,MAAQ,EAAA,SAAA,EAAW,SAAS,CAAC,CAAA;AAEjD,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,eAAe,cAAiB,GAAA;AAC9B,MAAA,UAAA,CAAW,IAAI,CAAA;AACf,MAAA,QAAA,CAAS,IAAI,CAAA;AAEb,MAAI,IAAA,CAAC,SAAa,IAAA,CAAC,QAAU,EAAA;AAC3B,QAAA,gBAAA,CAAiB,EAAE,CAAA;AACnB,QAAA,UAAA,CAAW,KAAK,CAAA;AAChB,QAAA;AAAA;AAGF,MAAI,IAAA;AACF,QAAA,MAAM,cAAc,wBAAyB,CAAA;AAAA,UAC3C,KAAO,EAAA,WAAA;AAAA,UACP,MAAA;AAAA,UACA,KAAA;AAAA,UACA,OAAA;AAAA,UACA,MAAA;AAAA,UACA,UAAU,QAAY,IAAA,KAAA,CAAA;AAAA,UACtB,OAAO,KAAS,IAAA,KAAA,CAAA;AAAA,UAChB,SAAA;AAAA,UACA;AAAA,SACD,CAAA;AAED,QAAA,MAAM,EAAE,SAAA,EAAW,UAAW,EAAA,GAAI,MAAM,aAAc,CAAA,YAAA;AAAA,UACpD;AAAA,SACF;AACA,QAAA,gBAAA,CAAiB,SAAS,CAAA;AAC1B,QAAA,QAAA,CAAS,UAAU,CAAA;AAAA,eACZ,CAAG,EAAA;AACV,QAAA,QAAA,CAAU,EAAY,OAAO,CAAA;AAC7B,QAAA,gBAAA,CAAiB,EAAE,CAAA;AAAA,OACnB,SAAA;AACA,QAAA,UAAA,CAAW,KAAK,CAAA;AAAA;AAClB;AAGF,IAAe,cAAA,EAAA;AAAA,GACd,EAAA;AAAA,IACD,WAAA;AAAA,IACA,MAAA;AAAA,IACA,KAAA;AAAA,IACA,OAAA;AAAA,IACA,MAAA;AAAA,IACA,aAAA;AAAA,IACA,QAAA;AAAA,IACA,QAAA;AAAA,IACA,KAAA;AAAA,IACA;AAAA,GACD,CAAA;AAED,EAAM,MAAA,uBAAA,GAA0B,CAAC,KAAyC,KAAA;AACxE,IAAA,MAAM,QAAW,GAAA,QAAA,CAAS,KAAM,CAAA,MAAA,CAAO,OAAO,EAAE,CAAA;AAChD,IAAkB,iBAAA,CAAA;AAAA,MAChB,KAAA,EAAO,OAAO,QAAQ,CAAA;AAAA,MACtB,MAAQ,EAAA;AAAA,KACT,CAAA;AAAA,GACH;AAEA,EAAM,MAAA,gBAAA,GAAmB,CAAC,MAAA,EAAiB,IAAiB,KAAA;AAC1D,IAAA,SAAA,CAAU,OAAO,WAAW,CAAA;AAAA,GAC9B;AAEA,EAAM,MAAA,iBAAA,GAAoB,CACxB,MAAA,EACA,QACG,KAAA;AACH,IAAA,MAAM,KAAQ,GAAA,OAAA,KAAY,QAAY,IAAA,KAAA,KAAU,gBAAiB,CAAA,GAAA;AACjE,IAAA,QAAA,CAAS,KAAQ,GAAA,gBAAA,CAAiB,IAAO,GAAA,gBAAA,CAAiB,GAAG,CAAA;AAC7D,IAAA,UAAA,CAAW,QAAQ,CAAA;AACnB,IAAA,SAAA,CAAU,CAAC,CAAA;AAAA,GACb;AAEA,EAAM,MAAA,YAAA,GAAe,CAAC,GAAgB,KAAA;AACpC,IAAA,QAAA,CAAS,GAAG,CAAA;AAAA,GACd;AAEA,EAAA,MAAM,gCAAgC,sBACpC,GAAA;AAAA,IAAC,oBAAA;AAAA,IAAA;AAAA,MACC,KAAA;AAAA,MACA,OAAA;AAAA,MACA,aAAe,EAAA;AAAA;AAAA,GACjB;AAGF,EAAA,MAAM,2BAA8B,GAAA,MAClC,OACE,mBAAA,GAAA,CAAC,SACC,EAAA,EAAA,QAAA,kBAAA,GAAA,CAAC,QACC,EAAA,EAAA,QAAA,kBAAA,GAAA,CAAC,SAAU,EAAA,EAAA,OAAA,EAAS,oBAAqB,CAAA,MAAA,EAAQ,OAAM,QACrD,EAAA,QAAA,kBAAA,GAAA,CAAC,GAAI,EAAA,EAAA,EAAA,EAAI,EAAE,EAAA,EAAI,CAAE,EAAA,EACf,8BAAC,gBAAiB,EAAA,EAAA,CAAA,EACpB,CACF,EAAA,CAAA,EACF,CACF,EAAA,CAAA,mBAEC,GAAA,CAAA,kBAAA,EAAA,EAAmB,MAAM,aAAe,EAAA,CAAA;AAG7C,EAAA,MAAM,2BAA2B,MAAM;AACrC,IACE,uBAAA,GAAA;AAAA,MAAC,eAAA;AAAA,MAAA;AAAA,QACC,kBAAA,EAAoB,CAAC,CAAG,EAAA,EAAA,EAAI,IAAI,EAAI,EAAA,GAAG,CAAE,CAAA,GAAA,CAAI,CAAM,CAAA,MAAA;AAAA,UACjD,KAAO,EAAA,CAAA;AAAA,UACP,KAAA,EAAO,GAAG,CAAC,CAAA,KAAA;AAAA,SACX,CAAA,CAAA;AAAA,QACF,SAAU,EAAA,KAAA;AAAA,QACV,EAAA,EAAI,EAAE,EAAA,EAAI,CAAE,EAAA;AAAA,QACZ,OAAO,KAAS,IAAA,CAAA;AAAA,QAChB,WAAA;AAAA,QACA,IAAM,EAAA,UAAA;AAAA,QACN,YAAc,EAAA,gBAAA;AAAA,QACd,mBAAqB,EAAA,uBAAA;AAAA,QACrB,gBAAkB,EAAA,IAAA;AAAA,QAClB,eAAe,EAAA,IAAA;AAAA,QACf,cAAc,EAAA;AAAA;AAAA,KAChB;AAAA,GAEJ;AAEA,EACE,uBAAA,GAAA,CAAC,GACC,EAAA,EAAA,QAAA,kBAAA,IAAA,CAAC,mBACC,EAAA,EAAA,QAAA,EAAA;AAAA,oBAAA,GAAA,CAAC,mBAAoB,CAAA,OAAA,EAApB,EACC,QAAA,kBAAA,GAAA,CAAC,mBAAgB,CACnB,EAAA,CAAA;AAAA,oBACC,GAAA,CAAA,mBAAA,CAAoB,OAApB,EAAA,EACE,QACC,EAAA,KAAA,mBAAA,IAAA,CAAC,GAAI,EAAA,EAAA,EAAA,EAAI,EAAE,OAAA,EAAS,CAAG,EAAA,KAAA,EAAO,cAAgB,EAAA,QAAA,EAAA;AAAA,MAAA,2BAAA;AAAA,MAClB;AAAA,KAAA,EAC5B,CAEA,mBAAA,GAAA;AAAA,MAAC,KAAA;AAAA,MAAA;AAAA,QACC,IAAA,EAAM,OAAU,GAAA,EAAK,GAAA,aAAA;AAAA,QACrB,OAAS,EAAA,oBAAA;AAAA,QACT,cAAgB,EAAA,YAAA;AAAA,QAChB,KACE,EAAA,KAAA,KAAU,CACN,GAAA,oBAAA,GACA,uBAAuB,KAAK,CAAA,CAAA,CAAA;AAAA,QAElC,cAAc,EAAE,OAAA,EAAS,EAAE,iBAAA,EAAmB,UAAW,EAAA;AAAA,QACzD,UAAY,EAAA;AAAA,UACV,MAAQ,EAAA,6BAAA;AAAA,UACR,IAAM,EAAA,2BAAA;AAAA,UACN,UAAY,EAAA;AAAA,SACd;AAAA,QACA,YAAA,EACE,UAAU,IACR,mBAAA,GAAA;AAAA,UAAC,GAAA;AAAA,UAAA;AAAA,YACC,aAAY,EAAA,oBAAA;AAAA,YACZ,EAAI,EAAA;AAAA,cACF,OAAS,EAAA,CAAA;AAAA,cACT,OAAS,EAAA,MAAA;AAAA,cACT,cAAgB,EAAA;AAAA,aAClB;AAAA,YACD,QAAA,EAAA;AAAA;AAAA,SAED;AAAA,QAGJ,SAAW,EAAA;AAAA;AAAA,KAGjB,EAAA;AAAA,GAAA,EACF,CACF,EAAA,CAAA;AAEJ;;;;"}
|