@backstage/plugin-catalog 1.28.0-next.0 → 1.28.0-next.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (25) hide show
  1. package/CHANGELOG.md +95 -0
  2. package/dist/alpha/DefaultEntityContentLayout.esm.js +83 -0
  3. package/dist/alpha/DefaultEntityContentLayout.esm.js.map +1 -0
  4. package/dist/alpha/components/EntityHeader/EntityHeader.esm.js +187 -0
  5. package/dist/alpha/components/EntityHeader/EntityHeader.esm.js.map +1 -0
  6. package/dist/alpha/components/EntityLabels/EntityLabels.esm.js.map +1 -1
  7. package/dist/alpha/components/EntityLayout/EntityLayout.esm.js +13 -137
  8. package/dist/alpha/components/EntityLayout/EntityLayout.esm.js.map +1 -1
  9. package/dist/alpha/entityContents.esm.js +2 -2
  10. package/dist/alpha/entityContents.esm.js.map +1 -1
  11. package/dist/alpha/filter/FilterWrapper.esm.js +3 -11
  12. package/dist/alpha/filter/FilterWrapper.esm.js.map +1 -1
  13. package/dist/alpha/pages.esm.js +39 -28
  14. package/dist/alpha/pages.esm.js.map +1 -1
  15. package/dist/alpha.d.ts +240 -235
  16. package/dist/apis/EntityPresentationApi/DefaultEntityPresentationApi.esm.js +33 -2
  17. package/dist/apis/EntityPresentationApi/DefaultEntityPresentationApi.esm.js.map +1 -1
  18. package/dist/components/HasSubcomponentsCard/HasSubcomponentsCard.esm.js +3 -2
  19. package/dist/components/HasSubcomponentsCard/HasSubcomponentsCard.esm.js.map +1 -1
  20. package/dist/index.d.ts +13 -13
  21. package/package.json +14 -15
  22. package/dist/alpha/EntityOverviewPage.esm.js +0 -17
  23. package/dist/alpha/EntityOverviewPage.esm.js.map +0 -1
  24. package/dist/alpha/components/EntityLayout/EntityLayoutTitle.esm.js +0 -20
  25. package/dist/alpha/components/EntityLayout/EntityLayoutTitle.esm.js.map +0 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,100 @@
1
1
  # @backstage/plugin-catalog
2
2
 
3
+ ## 1.28.0-next.2
4
+
5
+ ### Minor Changes
6
+
7
+ - 247a40b: Now a custom entity page header can be passed as input to the default entity page.
8
+ - 93533bd: The order in which group tabs appear on the entity page has been changed.
9
+
10
+ ### Before
11
+
12
+ Previously, entity contents determined the order in which groups were rendered, so a group was rendered as soon as its first entity content was detected.
13
+
14
+ ### After
15
+
16
+ Groups are now rendered first by default based on their order in the `app-config.yaml` file:
17
+
18
+ ```diff
19
+ app:
20
+ extensions:
21
+ - page:catalog/entity:
22
+ + config:
23
+ + groups:
24
+ + # this will be the first tab of the default entity page
25
+ + - deployment:
26
+ + title: Deployment
27
+ + # this will be the second tab of the default entiy page
28
+ + - documentation:
29
+ + title: Documentation
30
+ ```
31
+
32
+ If you wish to place a normal tab before a group, you must add the tab to a group and place the group in the order you wish it to appear on the entity page (groups that contains only one tab are rendered as normal tabs).
33
+
34
+ ```diff
35
+ app:
36
+ extensions:
37
+ - page:catalog/entity:
38
+ config:
39
+ groups:
40
+ + # Example placing the overview tab first
41
+ + - overview:
42
+ + title: Overview
43
+ - deployment:
44
+ title: Deployment
45
+ # this will be the second tab of the default entiy page
46
+ - documentation:
47
+ title: Documentation
48
+ - entity-content:catalog/overview:
49
+ + config:
50
+ + group: 'overview'
51
+ ```
52
+
53
+ ### Patch Changes
54
+
55
+ - 31731b0: Internal refactor to avoid `expiry-map` dependency.
56
+ - Updated dependencies
57
+ - @backstage/frontend-plugin-api@0.10.0-next.2
58
+ - @backstage/plugin-catalog-react@1.16.0-next.2
59
+ - @backstage/core-compat-api@0.4.0-next.2
60
+ - @backstage/core-components@0.16.5-next.1
61
+ - @backstage/plugin-search-react@1.8.7-next.2
62
+ - @backstage/catalog-client@1.9.1
63
+ - @backstage/catalog-model@1.7.3
64
+ - @backstage/core-plugin-api@1.10.4
65
+ - @backstage/errors@1.2.7
66
+ - @backstage/integration-react@1.2.5-next.0
67
+ - @backstage/types@1.2.1
68
+ - @backstage/plugin-catalog-common@1.1.3
69
+ - @backstage/plugin-permission-react@0.4.31
70
+ - @backstage/plugin-scaffolder-common@1.5.10-next.0
71
+ - @backstage/plugin-search-common@1.2.17
72
+
73
+ ## 1.28.0-next.1
74
+
75
+ ### Minor Changes
76
+
77
+ - 06d1226: Allow providing `kind` parameters to replace the default `Component` kind for `SubComponents` card
78
+
79
+ ### Patch Changes
80
+
81
+ - Updated dependencies
82
+ - @backstage/core-components@0.16.5-next.0
83
+ - @backstage/plugin-scaffolder-common@1.5.10-next.0
84
+ - @backstage/plugin-catalog-react@1.16.0-next.1
85
+ - @backstage/core-compat-api@0.3.7-next.1
86
+ - @backstage/catalog-client@1.9.1
87
+ - @backstage/catalog-model@1.7.3
88
+ - @backstage/core-plugin-api@1.10.4
89
+ - @backstage/errors@1.2.7
90
+ - @backstage/frontend-plugin-api@0.9.6-next.1
91
+ - @backstage/integration-react@1.2.4
92
+ - @backstage/types@1.2.1
93
+ - @backstage/plugin-catalog-common@1.1.3
94
+ - @backstage/plugin-permission-react@0.4.31
95
+ - @backstage/plugin-search-common@1.2.17
96
+ - @backstage/plugin-search-react@1.8.7-next.1
97
+
3
98
  ## 1.28.0-next.0
4
99
 
5
100
  ### Minor Changes
@@ -0,0 +1,83 @@
1
+ import React from 'react';
2
+ import Grid from '@material-ui/core/Grid';
3
+ import { makeStyles } from '@material-ui/core/styles';
4
+ import { EntitySwitch } from '../components/EntitySwitch/EntitySwitch.esm.js';
5
+ import { isOrphan, EntityOrphanWarning } from '../components/EntityOrphanWarning/EntityOrphanWarning.esm.js';
6
+ import { hasRelationWarnings, EntityRelationWarning } from '../components/EntityRelationWarning/EntityRelationWarning.esm.js';
7
+ import { hasCatalogProcessingErrors, EntityProcessingErrorsPanel } from '../components/EntityProcessingErrorsPanel/EntityProcessingErrorsPanel.esm.js';
8
+ import { HorizontalScrollGrid } from '@backstage/core-components';
9
+
10
+ const useStyles = makeStyles((theme) => ({
11
+ root: {
12
+ display: "flex",
13
+ flexFlow: "column nowrap",
14
+ gap: theme.spacing(3)
15
+ },
16
+ contentArea: {
17
+ display: "flex",
18
+ flexFlow: "column",
19
+ gap: theme.spacing(3),
20
+ alignItems: "stretch",
21
+ minWidth: 0
22
+ },
23
+ infoArea: {
24
+ display: "flex",
25
+ flexFlow: "column nowrap",
26
+ alignItems: "stretch",
27
+ gap: theme.spacing(3),
28
+ minWidth: 0
29
+ },
30
+ summaryArea: {
31
+ margin: theme.spacing(1.5)
32
+ // To counteract MUI negative grid margin
33
+ },
34
+ summaryCard: {
35
+ flex: "0 0 auto",
36
+ "& + &": {
37
+ marginLeft: theme.spacing(3)
38
+ }
39
+ },
40
+ [theme.breakpoints.up("md")]: {
41
+ root: {
42
+ display: "grid",
43
+ gap: 0,
44
+ gridTemplateAreas: ({ summaryCards }) => `
45
+ "${summaryCards ? "summary" : "content"} info"
46
+ "content info"
47
+ `,
48
+ gridTemplateColumns: ({ infoCards }) => infoCards ? "2fr 1fr" : "1fr",
49
+ alignItems: "start"
50
+ },
51
+ infoArea: {
52
+ gridArea: "info",
53
+ position: "sticky",
54
+ top: theme.spacing(3),
55
+ marginLeft: theme.spacing(3)
56
+ },
57
+ contentArea: {
58
+ gridArea: "content"
59
+ },
60
+ summaryArea: {
61
+ gridArea: "summary",
62
+ marginBottom: theme.spacing(3)
63
+ }
64
+ }
65
+ }));
66
+ const entityWarningContent = /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(EntitySwitch, null, /* @__PURE__ */ React.createElement(EntitySwitch.Case, { if: isOrphan }, /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React.createElement(EntityOrphanWarning, null)))), /* @__PURE__ */ React.createElement(EntitySwitch, null, /* @__PURE__ */ React.createElement(EntitySwitch.Case, { if: hasRelationWarnings }, /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React.createElement(EntityRelationWarning, null)))), /* @__PURE__ */ React.createElement(EntitySwitch, null, /* @__PURE__ */ React.createElement(EntitySwitch.Case, { if: hasCatalogProcessingErrors }, /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React.createElement(EntityProcessingErrorsPanel, null)))));
67
+ function DefaultEntityContentLayout(props) {
68
+ const { cards } = props;
69
+ const infoCards = cards.filter((card) => card.type === "info");
70
+ const summaryCards = cards.filter((card) => card.type === "summary");
71
+ const contentCards = cards.filter(
72
+ (card) => !card.type || card.type === "content"
73
+ );
74
+ const classes = useStyles({
75
+ infoCards: !!infoCards.length,
76
+ summaryCards: !!summaryCards.length,
77
+ contentCards: !!contentCards.length
78
+ });
79
+ return /* @__PURE__ */ React.createElement(React.Fragment, null, entityWarningContent, /* @__PURE__ */ React.createElement("div", { className: classes.root }, infoCards.length > 0 ? /* @__PURE__ */ React.createElement("div", { className: classes.infoArea }, infoCards.map((card) => card.element)) : null, summaryCards.length > 0 ? /* @__PURE__ */ React.createElement("div", { className: classes.summaryArea }, /* @__PURE__ */ React.createElement(HorizontalScrollGrid, null, summaryCards.map((card) => /* @__PURE__ */ React.createElement("div", { className: classes.summaryCard }, card.element)))) : null, contentCards.length > 0 ? /* @__PURE__ */ React.createElement("div", { className: classes.contentArea }, contentCards.map((card) => card.element)) : null));
80
+ }
81
+
82
+ export { DefaultEntityContentLayout };
83
+ //# sourceMappingURL=DefaultEntityContentLayout.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DefaultEntityContentLayout.esm.js","sources":["../../src/alpha/DefaultEntityContentLayout.tsx"],"sourcesContent":["/*\n * Copyright 2023 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 React from 'react';\nimport Grid from '@material-ui/core/Grid';\nimport { makeStyles, Theme } from '@material-ui/core/styles';\nimport { EntityContentLayoutProps } from '@backstage/plugin-catalog-react/alpha';\nimport { EntitySwitch } from '../components/EntitySwitch';\nimport {\n EntityOrphanWarning,\n isOrphan,\n} from '../components/EntityOrphanWarning';\nimport {\n EntityRelationWarning,\n hasRelationWarnings,\n} from '../components/EntityRelationWarning';\nimport {\n EntityProcessingErrorsPanel,\n hasCatalogProcessingErrors,\n} from '../components/EntityProcessingErrorsPanel';\nimport { HorizontalScrollGrid } from '@backstage/core-components';\n\nconst useStyles = makeStyles<\n Theme,\n { infoCards: boolean; summaryCards: boolean; contentCards: boolean }\n>(theme => ({\n root: {\n display: 'flex',\n flexFlow: 'column nowrap',\n gap: theme.spacing(3),\n },\n contentArea: {\n display: 'flex',\n flexFlow: 'column',\n gap: theme.spacing(3),\n alignItems: 'stretch',\n minWidth: 0,\n },\n infoArea: {\n display: 'flex',\n flexFlow: 'column nowrap',\n alignItems: 'stretch',\n gap: theme.spacing(3),\n minWidth: 0,\n },\n summaryArea: {\n margin: theme.spacing(1.5), // To counteract MUI negative grid margin\n },\n summaryCard: {\n flex: '0 0 auto',\n '& + &': {\n marginLeft: theme.spacing(3),\n },\n },\n [theme.breakpoints.up('md')]: {\n root: {\n display: 'grid',\n gap: 0,\n gridTemplateAreas: ({ summaryCards }) => `\n \"${summaryCards ? 'summary' : 'content'} info\"\n \"content info\"\n `,\n gridTemplateColumns: ({ infoCards }) => (infoCards ? '2fr 1fr' : '1fr'),\n alignItems: 'start',\n },\n infoArea: {\n gridArea: 'info',\n position: 'sticky',\n top: theme.spacing(3),\n marginLeft: theme.spacing(3),\n },\n contentArea: {\n gridArea: 'content',\n },\n summaryArea: {\n gridArea: 'summary',\n marginBottom: theme.spacing(3),\n },\n },\n}));\n\nconst entityWarningContent = (\n <>\n <EntitySwitch>\n <EntitySwitch.Case if={isOrphan}>\n <Grid item xs={12}>\n <EntityOrphanWarning />\n </Grid>\n </EntitySwitch.Case>\n </EntitySwitch>\n\n <EntitySwitch>\n <EntitySwitch.Case if={hasRelationWarnings}>\n <Grid item xs={12}>\n <EntityRelationWarning />\n </Grid>\n </EntitySwitch.Case>\n </EntitySwitch>\n\n <EntitySwitch>\n <EntitySwitch.Case if={hasCatalogProcessingErrors}>\n <Grid item xs={12}>\n <EntityProcessingErrorsPanel />\n </Grid>\n </EntitySwitch.Case>\n </EntitySwitch>\n </>\n);\n\nexport function DefaultEntityContentLayout(props: EntityContentLayoutProps) {\n const { cards } = props;\n\n const infoCards = cards.filter(card => card.type === 'info');\n const summaryCards = cards.filter(card => card.type === 'summary');\n const contentCards = cards.filter(\n card => !card.type || card.type === 'content',\n );\n\n const classes = useStyles({\n infoCards: !!infoCards.length,\n summaryCards: !!summaryCards.length,\n contentCards: !!contentCards.length,\n });\n\n return (\n <>\n {entityWarningContent}\n <div className={classes.root}>\n {infoCards.length > 0 ? (\n <div className={classes.infoArea}>\n {infoCards.map(card => card.element)}\n </div>\n ) : null}\n {summaryCards.length > 0 ? (\n <div className={classes.summaryArea}>\n <HorizontalScrollGrid>\n {summaryCards.map(card => (\n <div className={classes.summaryCard}>{card.element}</div>\n ))}\n </HorizontalScrollGrid>\n </div>\n ) : null}\n {contentCards.length > 0 ? (\n <div className={classes.contentArea}>\n {contentCards.map(card => card.element)}\n </div>\n ) : null}\n </div>\n </>\n );\n}\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,187 @@
1
+ import React, { useState, useCallback, useEffect } from 'react';
2
+ import { useLocation, useNavigate, useSearchParams } from 'react-router-dom';
3
+ import useAsync from 'react-use/esm/useAsync';
4
+ import { makeStyles } from '@material-ui/core/styles';
5
+ import Box from '@material-ui/core/Box';
6
+ import { Header, Breadcrumbs } from '@backstage/core-components';
7
+ import { useRouteRefParams, useRouteRef, useApi } from '@backstage/core-plugin-api';
8
+ import { DEFAULT_NAMESPACE } from '@backstage/catalog-model';
9
+ import { useAsyncEntity, entityRouteRef, InspectEntityDialog, UnregisterEntityDialog, EntityDisplayName, FavoriteEntity, catalogApiRef, EntityRefLink } from '@backstage/plugin-catalog-react';
10
+ import { EntityLabels } from '../EntityLabels/EntityLabels.esm.js';
11
+ import { EntityContextMenu } from '../../../components/EntityContextMenu/EntityContextMenu.esm.js';
12
+ import { rootRouteRef, unregisterRedirectRouteRef } from '../../../routes.esm.js';
13
+
14
+ function headerProps(paramKind, paramNamespace, paramName, entity) {
15
+ const kind = paramKind ?? entity?.kind ?? "";
16
+ const namespace = paramNamespace ?? entity?.metadata.namespace ?? "";
17
+ const name = entity?.metadata.title ?? paramName ?? entity?.metadata.name ?? "";
18
+ return {
19
+ headerTitle: `${name}${namespace && namespace !== DEFAULT_NAMESPACE ? ` in ${namespace}` : ""}`,
20
+ headerType: (() => {
21
+ let t = kind.toLocaleLowerCase("en-US");
22
+ if (entity && entity.spec && "type" in entity.spec) {
23
+ t += " \u2014 ";
24
+ t += entity.spec.type.toLocaleLowerCase("en-US");
25
+ }
26
+ return t;
27
+ })()
28
+ };
29
+ }
30
+ function findParentRelation(entityRelations = [], relationTypes = []) {
31
+ for (const type of relationTypes) {
32
+ const foundRelation = entityRelations.find(
33
+ (relation) => relation.type === type
34
+ );
35
+ if (foundRelation) {
36
+ return foundRelation;
37
+ }
38
+ }
39
+ return null;
40
+ }
41
+ const useStyles = makeStyles((theme) => ({
42
+ breadcrumbs: {
43
+ color: theme.page.fontColor,
44
+ fontSize: theme.typography.caption.fontSize,
45
+ textTransform: "uppercase",
46
+ marginTop: theme.spacing(1),
47
+ opacity: 0.8,
48
+ "& span ": {
49
+ color: theme.page.fontColor,
50
+ textDecoration: "underline",
51
+ textUnderlineOffset: "3px"
52
+ }
53
+ }
54
+ }));
55
+ function EntityHeaderTitle() {
56
+ const { entity } = useAsyncEntity();
57
+ const { kind, namespace, name } = useRouteRefParams(entityRouteRef);
58
+ const { headerTitle: title } = headerProps(kind, namespace, name, entity);
59
+ return /* @__PURE__ */ React.createElement(Box, { display: "inline-flex", alignItems: "center", height: "1em", maxWidth: "100%" }, /* @__PURE__ */ React.createElement(
60
+ Box,
61
+ {
62
+ component: "span",
63
+ textOverflow: "ellipsis",
64
+ whiteSpace: "nowrap",
65
+ overflow: "hidden"
66
+ },
67
+ entity ? /* @__PURE__ */ React.createElement(EntityDisplayName, { entityRef: entity, hideIcon: true }) : title
68
+ ), entity && /* @__PURE__ */ React.createElement(FavoriteEntity, { entity }));
69
+ }
70
+ function EntityHeaderSubtitle(props) {
71
+ const { parentEntityRelations } = props;
72
+ const classes = useStyles();
73
+ const { entity } = useAsyncEntity();
74
+ const { name } = useRouteRefParams(entityRouteRef);
75
+ const parentEntity = findParentRelation(
76
+ entity?.relations ?? [],
77
+ parentEntityRelations ?? []
78
+ );
79
+ const catalogApi = useApi(catalogApiRef);
80
+ const { value: ancestorEntity } = useAsync(async () => {
81
+ if (parentEntity) {
82
+ return findParentRelation(
83
+ (await catalogApi.getEntityByRef(parentEntity?.targetRef))?.relations,
84
+ parentEntityRelations
85
+ );
86
+ }
87
+ return null;
88
+ }, [parentEntity, catalogApi]);
89
+ return parentEntity ? /* @__PURE__ */ React.createElement(Breadcrumbs, { separator: ">", className: classes.breadcrumbs }, ancestorEntity && /* @__PURE__ */ React.createElement(EntityRefLink, { entityRef: ancestorEntity.targetRef, disableTooltip: true }), /* @__PURE__ */ React.createElement(EntityRefLink, { entityRef: parentEntity.targetRef, disableTooltip: true }), name) : null;
90
+ }
91
+ function EntityHeader(props) {
92
+ const {
93
+ UNSTABLE_extraContextMenuItems,
94
+ UNSTABLE_contextMenuOptions,
95
+ parentEntityRelations,
96
+ title,
97
+ subtitle
98
+ } = props;
99
+ const { entity } = useAsyncEntity();
100
+ const { kind, namespace, name } = useRouteRefParams(entityRouteRef);
101
+ const { headerTitle: entityFallbackText, headerType: type } = headerProps(
102
+ kind,
103
+ namespace,
104
+ name,
105
+ entity
106
+ );
107
+ const location = useLocation();
108
+ const navigate = useNavigate();
109
+ const catalogRoute = useRouteRef(rootRouteRef);
110
+ const unregisterRedirectRoute = useRouteRef(unregisterRedirectRouteRef);
111
+ const [confirmationDialogOpen, setConfirmationDialogOpen] = useState(false);
112
+ const openUnregisterEntityDialog = useCallback(
113
+ () => setConfirmationDialogOpen(true),
114
+ [setConfirmationDialogOpen]
115
+ );
116
+ const closeUnregisterEntityDialog = useCallback(
117
+ () => setConfirmationDialogOpen(false),
118
+ [setConfirmationDialogOpen]
119
+ );
120
+ const cleanUpAfterUnregisterConfirmation = useCallback(async () => {
121
+ setConfirmationDialogOpen(false);
122
+ navigate(
123
+ unregisterRedirectRoute ? unregisterRedirectRoute() : catalogRoute()
124
+ );
125
+ }, [
126
+ navigate,
127
+ catalogRoute,
128
+ unregisterRedirectRoute,
129
+ setConfirmationDialogOpen
130
+ ]);
131
+ useEffect(() => {
132
+ setConfirmationDialogOpen(false);
133
+ }, [location.pathname]);
134
+ const [searchParams, setSearchParams] = useSearchParams();
135
+ const selectedInspectEntityDialogTab = searchParams.get("inspect");
136
+ const setInspectEntityDialogTab = useCallback(
137
+ (newTab) => setSearchParams(`inspect=${newTab}`),
138
+ [setSearchParams]
139
+ );
140
+ const openInspectEntityDialog = useCallback(
141
+ () => setSearchParams("inspect"),
142
+ [setSearchParams]
143
+ );
144
+ const closeInspectEntityDialog = useCallback(
145
+ () => setSearchParams(),
146
+ [setSearchParams]
147
+ );
148
+ const inspectDialogOpen = typeof selectedInspectEntityDialogTab === "string";
149
+ return /* @__PURE__ */ React.createElement(
150
+ Header,
151
+ {
152
+ pageTitleOverride: entityFallbackText,
153
+ type,
154
+ title: title ?? /* @__PURE__ */ React.createElement(EntityHeaderTitle, null),
155
+ subtitle: subtitle ?? /* @__PURE__ */ React.createElement(EntityHeaderSubtitle, { parentEntityRelations })
156
+ },
157
+ entity && /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(EntityLabels, { entity }), /* @__PURE__ */ React.createElement(
158
+ EntityContextMenu,
159
+ {
160
+ UNSTABLE_extraContextMenuItems,
161
+ UNSTABLE_contextMenuOptions,
162
+ onInspectEntity: openInspectEntityDialog,
163
+ onUnregisterEntity: openUnregisterEntityDialog
164
+ }
165
+ ), /* @__PURE__ */ React.createElement(
166
+ InspectEntityDialog,
167
+ {
168
+ entity,
169
+ initialTab: selectedInspectEntityDialogTab || void 0,
170
+ open: inspectDialogOpen,
171
+ onClose: closeInspectEntityDialog,
172
+ onSelect: setInspectEntityDialogTab
173
+ }
174
+ ), /* @__PURE__ */ React.createElement(
175
+ UnregisterEntityDialog,
176
+ {
177
+ entity,
178
+ open: confirmationDialogOpen,
179
+ onClose: closeUnregisterEntityDialog,
180
+ onConfirm: cleanUpAfterUnregisterConfirmation
181
+ }
182
+ ))
183
+ );
184
+ }
185
+
186
+ export { EntityHeader };
187
+ //# sourceMappingURL=EntityHeader.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EntityHeader.esm.js","sources":["../../../../src/alpha/components/EntityHeader/EntityHeader.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 React, {\n useState,\n useCallback,\n useEffect,\n ComponentProps,\n ReactNode,\n} from 'react';\nimport { useNavigate, useLocation, useSearchParams } from 'react-router-dom';\nimport useAsync from 'react-use/esm/useAsync';\n\nimport { makeStyles } from '@material-ui/core/styles';\nimport Box from '@material-ui/core/Box';\n\nimport { Header, Breadcrumbs } from '@backstage/core-components';\nimport {\n useApi,\n useRouteRef,\n useRouteRefParams,\n} from '@backstage/core-plugin-api';\nimport { IconComponent } from '@backstage/frontend-plugin-api';\n\nimport {\n Entity,\n EntityRelation,\n DEFAULT_NAMESPACE,\n} from '@backstage/catalog-model';\n\nimport {\n useAsyncEntity,\n entityRouteRef,\n catalogApiRef,\n EntityRefLink,\n InspectEntityDialog,\n UnregisterEntityDialog,\n EntityDisplayName,\n FavoriteEntity,\n} from '@backstage/plugin-catalog-react';\n\nimport { EntityLabels } from '../EntityLabels';\nimport { EntityContextMenu } from '../../../components/EntityContextMenu';\nimport { rootRouteRef, unregisterRedirectRouteRef } from '../../../routes';\n\nfunction headerProps(\n paramKind: string | undefined,\n paramNamespace: string | undefined,\n paramName: string | undefined,\n entity: Entity | undefined,\n): { headerTitle: string; headerType: string } {\n const kind = paramKind ?? entity?.kind ?? '';\n const namespace = paramNamespace ?? entity?.metadata.namespace ?? '';\n const name =\n entity?.metadata.title ?? paramName ?? entity?.metadata.name ?? '';\n\n return {\n headerTitle: `${name}${\n namespace && namespace !== DEFAULT_NAMESPACE ? ` in ${namespace}` : ''\n }`,\n headerType: (() => {\n let t = kind.toLocaleLowerCase('en-US');\n if (entity && entity.spec && 'type' in entity.spec) {\n t += ' — ';\n t += (entity.spec as { type: string }).type.toLocaleLowerCase('en-US');\n }\n return t;\n })(),\n };\n}\n\nfunction findParentRelation(\n entityRelations: EntityRelation[] = [],\n relationTypes: string[] = [],\n) {\n for (const type of relationTypes) {\n const foundRelation = entityRelations.find(\n relation => relation.type === type,\n );\n if (foundRelation) {\n return foundRelation; // Return the first found relation and stop\n }\n }\n return null;\n}\n\nconst useStyles = makeStyles(theme => ({\n breadcrumbs: {\n color: theme.page.fontColor,\n fontSize: theme.typography.caption.fontSize,\n textTransform: 'uppercase',\n marginTop: theme.spacing(1),\n opacity: 0.8,\n '& span ': {\n color: theme.page.fontColor,\n textDecoration: 'underline',\n textUnderlineOffset: '3px',\n },\n },\n}));\n\nfunction EntityHeaderTitle() {\n const { entity } = useAsyncEntity();\n const { kind, namespace, name } = useRouteRefParams(entityRouteRef);\n const { headerTitle: title } = headerProps(kind, namespace, name, entity);\n return (\n <Box display=\"inline-flex\" alignItems=\"center\" height=\"1em\" maxWidth=\"100%\">\n <Box\n component=\"span\"\n textOverflow=\"ellipsis\"\n whiteSpace=\"nowrap\"\n overflow=\"hidden\"\n >\n {entity ? <EntityDisplayName entityRef={entity} hideIcon /> : title}\n </Box>\n {entity && <FavoriteEntity entity={entity} />}\n </Box>\n );\n}\n\nfunction EntityHeaderSubtitle(props: { parentEntityRelations?: string[] }) {\n const { parentEntityRelations } = props;\n const classes = useStyles();\n const { entity } = useAsyncEntity();\n const { name } = useRouteRefParams(entityRouteRef);\n const parentEntity = findParentRelation(\n entity?.relations ?? [],\n parentEntityRelations ?? [],\n );\n\n const catalogApi = useApi(catalogApiRef);\n\n const { value: ancestorEntity } = useAsync(async () => {\n if (parentEntity) {\n return findParentRelation(\n (await catalogApi.getEntityByRef(parentEntity?.targetRef))?.relations,\n parentEntityRelations,\n );\n }\n return null;\n }, [parentEntity, catalogApi]);\n\n return parentEntity ? (\n <Breadcrumbs separator=\">\" className={classes.breadcrumbs}>\n {ancestorEntity && (\n <EntityRefLink entityRef={ancestorEntity.targetRef} disableTooltip />\n )}\n <EntityRefLink entityRef={parentEntity.targetRef} disableTooltip />\n {name}\n </Breadcrumbs>\n ) : null;\n}\n\n/** @alpha */\nexport function EntityHeader(props: {\n // NOTE(freben): Intentionally not exported at this point, since it's part of\n // the unstable extra context menu items concept below\n UNSTABLE_extraContextMenuItems?: {\n title: string;\n Icon: IconComponent;\n onClick: () => void;\n }[];\n // NOTE(blam): Intentionally not exported at this point, since it's part of\n // unstable context menu option, eg: disable the unregister entity menu\n UNSTABLE_contextMenuOptions?: {\n disableUnregister: boolean | 'visible' | 'hidden' | 'disable';\n };\n /**\n * An array of relation types used to determine the parent entities in the hierarchy.\n * These relations are prioritized in the order provided, allowing for flexible\n * navigation through entity relationships.\n *\n * For example, use relation types like `[\"partOf\", \"memberOf\", \"ownedBy\"]` to define how the entity is related to\n * its parents in the Entity Catalog.\n *\n * It adds breadcrumbs in the Entity page to enhance user navigation and context awareness.\n */\n parentEntityRelations?: string[];\n title?: ReactNode;\n subtitle?: ReactNode;\n}) {\n const {\n UNSTABLE_extraContextMenuItems,\n UNSTABLE_contextMenuOptions,\n parentEntityRelations,\n title,\n subtitle,\n } = props;\n const { entity } = useAsyncEntity();\n const { kind, namespace, name } = useRouteRefParams(entityRouteRef);\n const { headerTitle: entityFallbackText, headerType: type } = headerProps(\n kind,\n namespace,\n name,\n entity,\n );\n\n const location = useLocation();\n const navigate = useNavigate();\n const catalogRoute = useRouteRef(rootRouteRef);\n const unregisterRedirectRoute = useRouteRef(unregisterRedirectRouteRef);\n\n const [confirmationDialogOpen, setConfirmationDialogOpen] = useState(false);\n\n const openUnregisterEntityDialog = useCallback(\n () => setConfirmationDialogOpen(true),\n [setConfirmationDialogOpen],\n );\n\n const closeUnregisterEntityDialog = useCallback(\n () => setConfirmationDialogOpen(false),\n [setConfirmationDialogOpen],\n );\n\n const cleanUpAfterUnregisterConfirmation = useCallback(async () => {\n setConfirmationDialogOpen(false);\n navigate(\n unregisterRedirectRoute ? unregisterRedirectRoute() : catalogRoute(),\n );\n }, [\n navigate,\n catalogRoute,\n unregisterRedirectRoute,\n setConfirmationDialogOpen,\n ]);\n\n // Make sure to close the dialog if the user clicks links in it that navigate\n // to another entity.\n useEffect(() => {\n setConfirmationDialogOpen(false);\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [location.pathname]);\n\n const [searchParams, setSearchParams] = useSearchParams();\n const selectedInspectEntityDialogTab = searchParams.get('inspect');\n\n const setInspectEntityDialogTab = useCallback(\n (newTab: string) => setSearchParams(`inspect=${newTab}`),\n [setSearchParams],\n );\n\n const openInspectEntityDialog = useCallback(\n () => setSearchParams('inspect'),\n [setSearchParams],\n );\n\n const closeInspectEntityDialog = useCallback(\n () => setSearchParams(),\n [setSearchParams],\n );\n\n const inspectDialogOpen = typeof selectedInspectEntityDialogTab === 'string';\n\n return (\n <Header\n pageTitleOverride={entityFallbackText}\n type={type}\n title={title ?? <EntityHeaderTitle />}\n subtitle={\n subtitle ?? (\n <EntityHeaderSubtitle parentEntityRelations={parentEntityRelations} />\n )\n }\n >\n {entity && (\n <>\n <EntityLabels entity={entity} />\n <EntityContextMenu\n UNSTABLE_extraContextMenuItems={UNSTABLE_extraContextMenuItems}\n UNSTABLE_contextMenuOptions={UNSTABLE_contextMenuOptions}\n onInspectEntity={openInspectEntityDialog}\n onUnregisterEntity={openUnregisterEntityDialog}\n />\n <InspectEntityDialog\n entity={entity!}\n initialTab={\n (selectedInspectEntityDialogTab as ComponentProps<\n typeof InspectEntityDialog\n >['initialTab']) || undefined\n }\n open={inspectDialogOpen}\n onClose={closeInspectEntityDialog}\n onSelect={setInspectEntityDialogTab}\n />\n <UnregisterEntityDialog\n entity={entity!}\n open={confirmationDialogOpen}\n onClose={closeUnregisterEntityDialog}\n onConfirm={cleanUpAfterUnregisterConfirmation}\n />\n </>\n )}\n </Header>\n );\n}\n"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"EntityLabels.esm.js","sources":["../../../../src/alpha/components/EntityLabels/EntityLabels.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 React from 'react';\nimport { HeaderLabel } from '@backstage/core-components';\nimport { Entity, RELATION_OWNED_BY } from '@backstage/catalog-model';\nimport {\n EntityRefLinks,\n getEntityRelations,\n} from '@backstage/plugin-catalog-react';\nimport { useTranslationRef } from '@backstage/core-plugin-api/alpha';\nimport { catalogTranslationRef } from '../../../alpha/translation';\n\ntype EntityLabelsProps = {\n entity: Entity;\n};\n\nexport function EntityLabels(props: EntityLabelsProps) {\n const { entity } = props;\n const ownedByRelations = getEntityRelations(entity, RELATION_OWNED_BY);\n const { t } = useTranslationRef(catalogTranslationRef);\n return (\n <>\n {ownedByRelations.length > 0 && (\n <HeaderLabel\n label={t('entityLabels.ownerLabel')}\n contentTypograpyRootComponent=\"p\"\n value={\n <EntityRefLinks\n entityRefs={ownedByRelations}\n defaultKind=\"Group\"\n color=\"inherit\"\n />\n }\n />\n )}\n {entity.spec?.lifecycle && (\n <HeaderLabel\n label={t('entityLabels.lifecycleLabel')}\n value={entity.spec.lifecycle?.toString()}\n />\n )}\n </>\n );\n}\n"],"names":[],"mappings":";;;;;;;AA8BO,SAAS,aAAa,KAA0B,EAAA;AACrD,EAAM,MAAA,EAAE,QAAW,GAAA,KAAA;AACnB,EAAM,MAAA,gBAAA,GAAmB,kBAAmB,CAAA,MAAA,EAAQ,iBAAiB,CAAA;AACrE,EAAA,MAAM,EAAE,CAAA,EAAM,GAAA,iBAAA,CAAkB,qBAAqB,CAAA;AACrD,EACE,uBAAA,KAAA,CAAA,aAAA,CAAA,KAAA,CAAA,QAAA,EAAA,IAAA,EACG,gBAAiB,CAAA,MAAA,GAAS,CACzB,oBAAA,KAAA,CAAA,aAAA;AAAA,IAAC,WAAA;AAAA,IAAA;AAAA,MACC,KAAA,EAAO,EAAE,yBAAyB,CAAA;AAAA,MAClC,6BAA8B,EAAA,GAAA;AAAA,MAC9B,KACE,kBAAA,KAAA,CAAA,aAAA;AAAA,QAAC,cAAA;AAAA,QAAA;AAAA,UACC,UAAY,EAAA,gBAAA;AAAA,UACZ,WAAY,EAAA,OAAA;AAAA,UACZ,KAAM,EAAA;AAAA;AAAA;AACR;AAAA,GAEJ,EAED,MAAO,CAAA,IAAA,EAAM,SACZ,oBAAA,KAAA,CAAA,aAAA;AAAA,IAAC,WAAA;AAAA,IAAA;AAAA,MACC,KAAA,EAAO,EAAE,6BAA6B,CAAA;AAAA,MACtC,KAAO,EAAA,MAAA,CAAO,IAAK,CAAA,SAAA,EAAW,QAAS;AAAA;AAAA,GAG7C,CAAA;AAEJ;;;;"}
1
+ {"version":3,"file":"EntityLabels.esm.js","sources":["../../../../src/alpha/components/EntityLabels/EntityLabels.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 React from 'react';\nimport { HeaderLabel } from '@backstage/core-components';\nimport { Entity, RELATION_OWNED_BY } from '@backstage/catalog-model';\nimport {\n EntityRefLinks,\n getEntityRelations,\n} from '@backstage/plugin-catalog-react';\nimport { useTranslationRef } from '@backstage/core-plugin-api/alpha';\nimport { catalogTranslationRef } from '../../translation';\n\ntype EntityLabelsProps = {\n entity: Entity;\n};\n\nexport function EntityLabels(props: EntityLabelsProps) {\n const { entity } = props;\n const ownedByRelations = getEntityRelations(entity, RELATION_OWNED_BY);\n const { t } = useTranslationRef(catalogTranslationRef);\n return (\n <>\n {ownedByRelations.length > 0 && (\n <HeaderLabel\n label={t('entityLabels.ownerLabel')}\n contentTypograpyRootComponent=\"p\"\n value={\n <EntityRefLinks\n entityRefs={ownedByRelations}\n defaultKind=\"Group\"\n color=\"inherit\"\n />\n }\n />\n )}\n {entity.spec?.lifecycle && (\n <HeaderLabel\n label={t('entityLabels.lifecycleLabel')}\n value={entity.spec.lifecycle?.toString()}\n />\n )}\n </>\n );\n}\n"],"names":[],"mappings":";;;;;;;AA8BO,SAAS,aAAa,KAA0B,EAAA;AACrD,EAAM,MAAA,EAAE,QAAW,GAAA,KAAA;AACnB,EAAM,MAAA,gBAAA,GAAmB,kBAAmB,CAAA,MAAA,EAAQ,iBAAiB,CAAA;AACrE,EAAA,MAAM,EAAE,CAAA,EAAM,GAAA,iBAAA,CAAkB,qBAAqB,CAAA;AACrD,EACE,uBAAA,KAAA,CAAA,aAAA,CAAA,KAAA,CAAA,QAAA,EAAA,IAAA,EACG,gBAAiB,CAAA,MAAA,GAAS,CACzB,oBAAA,KAAA,CAAA,aAAA;AAAA,IAAC,WAAA;AAAA,IAAA;AAAA,MACC,KAAA,EAAO,EAAE,yBAAyB,CAAA;AAAA,MAClC,6BAA8B,EAAA,GAAA;AAAA,MAC9B,KACE,kBAAA,KAAA,CAAA,aAAA;AAAA,QAAC,cAAA;AAAA,QAAA;AAAA,UACC,UAAY,EAAA,gBAAA;AAAA,UACZ,WAAY,EAAA,OAAA;AAAA,UACZ,KAAM,EAAA;AAAA;AAAA;AACR;AAAA,GAEJ,EAED,MAAO,CAAA,IAAA,EAAM,SACZ,oBAAA,KAAA,CAAA,aAAA;AAAA,IAAC,WAAA;AAAA,IAAA;AAAA,MACC,KAAA,EAAO,EAAE,6BAA6B,CAAA;AAAA,MACtC,KAAO,EAAA,MAAA,CAAO,IAAK,CAAA,SAAA,EAAW,QAAS;AAAA;AAAA,GAG7C,CAAA;AAEJ;;;;"}
@@ -1,77 +1,28 @@
1
- import React, { useState, useEffect } from 'react';
2
- import { useLocation, useNavigate, useSearchParams } from 'react-router-dom';
3
- import useAsync from 'react-use/esm/useAsync';
4
- import { makeStyles } from '@material-ui/core/styles';
1
+ import React from 'react';
5
2
  import Alert from '@material-ui/lab/Alert';
6
- import { attachComponentData, useRouteRefParams, useElementFilter, useRouteRef, useApi } from '@backstage/core-plugin-api';
3
+ import { attachComponentData, useRouteRefParams, useElementFilter } from '@backstage/core-plugin-api';
7
4
  import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
8
- import { Page, Header, Breadcrumbs, Progress, Content, WarningPanel, Link } from '@backstage/core-components';
9
- import { DEFAULT_NAMESPACE } from '@backstage/catalog-model';
10
- import { entityRouteRef, useAsyncEntity, catalogApiRef, EntityRefLink, InspectEntityDialog, UnregisterEntityDialog } from '@backstage/plugin-catalog-react';
5
+ import { Page, Progress, Content, WarningPanel, Link } from '@backstage/core-components';
6
+ import { entityRouteRef, useAsyncEntity } from '@backstage/plugin-catalog-react';
11
7
  import { catalogTranslationRef } from '../../translation.esm.js';
12
- import { rootRouteRef, unregisterRedirectRouteRef } from '../../../routes.esm.js';
13
- import { EntityContextMenu } from '../../../components/EntityContextMenu/EntityContextMenu.esm.js';
8
+ import { EntityHeader } from '../EntityHeader/EntityHeader.esm.js';
14
9
  import { EntityTabs } from '../EntityTabs/EntityTabs.esm.js';
15
- import { EntityLabels } from '../EntityLabels/EntityLabels.esm.js';
16
- import { EntityLayoutTitle } from './EntityLayoutTitle.esm.js';
17
10
 
18
11
  const dataKey = "plugin.catalog.entityLayoutRoute";
19
12
  const Route = () => null;
20
13
  attachComponentData(Route, dataKey, true);
21
14
  attachComponentData(Route, "core.gatherMountPoints", true);
22
- function headerProps(paramKind, paramNamespace, paramName, entity) {
23
- const kind = paramKind ?? entity?.kind ?? "";
24
- const namespace = paramNamespace ?? entity?.metadata.namespace ?? "";
25
- const name = entity?.metadata.title ?? paramName ?? entity?.metadata.name ?? "";
26
- return {
27
- headerTitle: `${name}${namespace && namespace !== DEFAULT_NAMESPACE ? ` in ${namespace}` : ""}`,
28
- headerType: (() => {
29
- let t = kind.toLocaleLowerCase("en-US");
30
- if (entity && entity.spec && "type" in entity.spec) {
31
- t += " \u2014 ";
32
- t += entity.spec.type.toLocaleLowerCase("en-US");
33
- }
34
- return t;
35
- })()
36
- };
37
- }
38
- function findParentRelation(entityRelations = [], relationTypes = []) {
39
- for (const type of relationTypes) {
40
- const foundRelation = entityRelations.find(
41
- (relation) => relation.type === type
42
- );
43
- if (foundRelation) {
44
- return foundRelation;
45
- }
46
- }
47
- return null;
48
- }
49
- const useStyles = makeStyles((theme) => ({
50
- breadcrumbs: {
51
- color: theme.page.fontColor,
52
- fontSize: theme.typography.caption.fontSize,
53
- textTransform: "uppercase",
54
- marginTop: theme.spacing(1),
55
- opacity: 0.8,
56
- "& span ": {
57
- color: theme.page.fontColor,
58
- textDecoration: "underline",
59
- textUnderlineOffset: "3px"
60
- }
61
- }
62
- }));
63
15
  const EntityLayout = (props) => {
64
16
  const {
65
17
  UNSTABLE_extraContextMenuItems,
66
18
  UNSTABLE_contextMenuOptions,
67
19
  children,
20
+ header,
68
21
  NotFoundComponent,
69
22
  parentEntityRelations
70
23
  } = props;
71
- const classes = useStyles();
72
- const { kind, namespace, name } = useRouteRefParams(entityRouteRef);
24
+ const { kind } = useRouteRefParams(entityRouteRef);
73
25
  const { entity, loading, error } = useAsyncEntity();
74
- const location = useLocation();
75
26
  const routes = useElementFilter(
76
27
  children,
77
28
  (elements) => elements.selectByComponentData({
@@ -95,90 +46,15 @@ const EntityLayout = (props) => {
95
46
  }),
96
47
  [entity]
97
48
  );
98
- const { headerTitle, headerType } = headerProps(
99
- kind,
100
- namespace,
101
- name,
102
- entity
103
- );
104
- const [confirmationDialogOpen, setConfirmationDialogOpen] = useState(false);
105
- const navigate = useNavigate();
106
- const [searchParams, setSearchParams] = useSearchParams();
107
- const catalogRoute = useRouteRef(rootRouteRef);
108
- const unregisterRedirectRoute = useRouteRef(unregisterRedirectRouteRef);
109
49
  const { t } = useTranslationRef(catalogTranslationRef);
110
- const cleanUpAfterRemoval = async () => {
111
- setConfirmationDialogOpen(false);
112
- navigate(
113
- unregisterRedirectRoute ? unregisterRedirectRoute() : catalogRoute()
114
- );
115
- };
116
- const parentEntity = findParentRelation(
117
- entity?.relations ?? [],
118
- parentEntityRelations ?? []
119
- );
120
- const catalogApi = useApi(catalogApiRef);
121
- const { value: ancestorEntity } = useAsync(async () => {
122
- if (parentEntity) {
123
- return findParentRelation(
124
- (await catalogApi.getEntityByRef(parentEntity?.targetRef))?.relations,
125
- parentEntityRelations
126
- );
127
- }
128
- return null;
129
- }, [parentEntity]);
130
- useEffect(() => {
131
- setConfirmationDialogOpen(false);
132
- }, [location.pathname]);
133
- const selectedInspectTab = searchParams.get("inspect");
134
- const showInspectTab = typeof selectedInspectTab === "string";
135
- return /* @__PURE__ */ React.createElement(Page, { themeId: entity?.spec?.type?.toString() ?? "home" }, /* @__PURE__ */ React.createElement(
136
- Header,
137
- {
138
- title: /* @__PURE__ */ React.createElement(EntityLayoutTitle, { title: headerTitle, entity }),
139
- pageTitleOverride: headerTitle,
140
- type: headerType,
141
- subtitle: parentEntity && /* @__PURE__ */ React.createElement(Breadcrumbs, { separator: ">", className: classes.breadcrumbs }, ancestorEntity && /* @__PURE__ */ React.createElement(
142
- EntityRefLink,
143
- {
144
- entityRef: ancestorEntity.targetRef,
145
- disableTooltip: true
146
- }
147
- ), /* @__PURE__ */ React.createElement(
148
- EntityRefLink,
149
- {
150
- entityRef: parentEntity.targetRef,
151
- disableTooltip: true
152
- }
153
- ), name)
154
- },
155
- entity && /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(EntityLabels, { entity }), /* @__PURE__ */ React.createElement(
156
- EntityContextMenu,
157
- {
158
- UNSTABLE_extraContextMenuItems,
159
- UNSTABLE_contextMenuOptions,
160
- onUnregisterEntity: () => setConfirmationDialogOpen(true),
161
- onInspectEntity: () => setSearchParams("inspect")
162
- }
163
- ))
164
- ), loading && /* @__PURE__ */ React.createElement(Progress, null), entity && /* @__PURE__ */ React.createElement(EntityTabs, { routes }), error && /* @__PURE__ */ React.createElement(Content, null, /* @__PURE__ */ React.createElement(Alert, { severity: "error" }, error.toString())), !loading && !error && !entity && /* @__PURE__ */ React.createElement(Content, null, NotFoundComponent ? NotFoundComponent : /* @__PURE__ */ React.createElement(WarningPanel, { title: t("entityLabels.warningPanelTitle") }, "There is no ", kind, " with the requested", " ", /* @__PURE__ */ React.createElement(Link, { to: "https://backstage.io/docs/features/software-catalog/references" }, "kind, namespace, and name"), ".")), showInspectTab && /* @__PURE__ */ React.createElement(
165
- InspectEntityDialog,
166
- {
167
- entity,
168
- initialTab: selectedInspectTab || void 0,
169
- onSelect: (newTab) => setSearchParams(`inspect=${newTab}`),
170
- open: true,
171
- onClose: () => setSearchParams()
172
- }
173
- ), /* @__PURE__ */ React.createElement(
174
- UnregisterEntityDialog,
50
+ return /* @__PURE__ */ React.createElement(Page, { themeId: entity?.spec?.type?.toString() ?? "home" }, header ?? /* @__PURE__ */ React.createElement(
51
+ EntityHeader,
175
52
  {
176
- open: confirmationDialogOpen,
177
- entity,
178
- onConfirm: cleanUpAfterRemoval,
179
- onClose: () => setConfirmationDialogOpen(false)
53
+ parentEntityRelations,
54
+ UNSTABLE_contextMenuOptions,
55
+ UNSTABLE_extraContextMenuItems
180
56
  }
181
- ));
57
+ ), loading && /* @__PURE__ */ React.createElement(Progress, null), entity && /* @__PURE__ */ React.createElement(EntityTabs, { routes }), error && /* @__PURE__ */ React.createElement(Content, null, /* @__PURE__ */ React.createElement(Alert, { severity: "error" }, error.toString())), !loading && !error && !entity && /* @__PURE__ */ React.createElement(Content, null, NotFoundComponent ? NotFoundComponent : /* @__PURE__ */ React.createElement(WarningPanel, { title: t("entityLabels.warningPanelTitle") }, "There is no ", kind, " with the requested", " ", /* @__PURE__ */ React.createElement(Link, { to: "https://backstage.io/docs/features/software-catalog/references" }, "kind, namespace, and name"), ".")));
182
58
  };
183
59
  EntityLayout.Route = Route;
184
60