@nospt/backstage-plugin-apigee 1.1.0-rc2 → 1.1.0-rc4
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/README.md +19 -19
- package/config.d.ts +25 -25
- package/dist/components/ApiCard.esm.js +67 -125
- package/dist/components/ApiCard.esm.js.map +1 -1
- package/dist/components/ApiCard.module.css.esm.js +8 -0
- package/dist/components/ApiCard.module.css.esm.js.map +1 -0
- package/dist/components/ApiCardGrid.esm.js +12 -46
- package/dist/components/ApiCardGrid.esm.js.map +1 -1
- package/dist/components/ApiTable.esm.js +162 -0
- package/dist/components/ApiTable.esm.js.map +1 -0
- package/dist/components/ApiTable.module.css.esm.js +8 -0
- package/dist/components/ApiTable.module.css.esm.js.map +1 -0
- package/dist/components/ApigeeApiCatalogPage.esm.js +80 -60
- package/dist/components/ApigeeApiCatalogPage.esm.js.map +1 -1
- package/dist/components/ApigeeNavContent.esm.js +2 -2
- package/dist/components/ApigeeNavContent.esm.js.map +1 -1
- package/dist/components/CountCards.esm.js +122 -0
- package/dist/components/CountCards.esm.js.map +1 -0
- package/dist/components/CountCards.module.css.esm.js +8 -0
- package/dist/components/CountCards.module.css.esm.js.map +1 -0
- package/dist/components/FilterToolbar.esm.js +75 -142
- package/dist/components/FilterToolbar.esm.js.map +1 -1
- package/dist/components/FilterToolbar.module.css.esm.js +8 -0
- package/dist/components/FilterToolbar.module.css.esm.js.map +1 -0
- package/dist/components/SearchBar.esm.js +7 -35
- package/dist/components/SearchBar.esm.js.map +1 -1
- package/dist/components/apiCardViewModel.esm.js +16 -2
- package/dist/components/apiCardViewModel.esm.js.map +1 -1
- package/dist/components/apigeeFacetFilters.esm.js.map +1 -1
- package/dist/components/apigeeOrgFilter.esm.js.map +1 -1
- package/dist/components/apigeeSearchParams.esm.js +14 -1
- package/dist/components/apigeeSearchParams.esm.js.map +1 -1
- package/dist/components/categoryIcons.esm.js +12 -22
- package/dist/components/categoryIcons.esm.js.map +1 -1
- package/dist/components/facetModel.esm.js +30 -10
- package/dist/components/facetModel.esm.js.map +1 -1
- package/dist/components/grouping.esm.js +1 -26
- package/dist/components/grouping.esm.js.map +1 -1
- package/dist/components/states.esm.js +19 -49
- package/dist/components/states.esm.js.map +1 -1
- package/dist/components/useApigeeFilters.esm.js +18 -2
- package/dist/components/useApigeeFilters.esm.js.map +1 -1
- package/dist/components/useDebouncedValue.esm.js.map +1 -1
- package/dist/constants.esm.js +3 -2
- package/dist/constants.esm.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/node_modules_dist/style-inject/dist/style-inject.es.esm.js +29 -0
- package/dist/node_modules_dist/style-inject/dist/style-inject.es.esm.js.map +1 -0
- package/dist/plugin.esm.js +2 -2
- package/dist/plugin.esm.js.map +1 -1
- package/dist/routes.esm.js.map +1 -1
- package/package.json +17 -15
package/README.md
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
|
-
# @nospt/backstage-plugin-apigee
|
|
2
|
-
|
|
3
|
-
Frontend package for the bootstrap stage of the Backstage Apigee plugin.
|
|
4
|
-
|
|
5
|
-
## Included
|
|
6
|
-
|
|
7
|
-
- New Frontend System plugin registration
|
|
8
|
-
- Placeholder page under `/apigee/bootstrap`
|
|
9
|
-
- Shared permission exports from the common package
|
|
10
|
-
|
|
11
|
-
## Development
|
|
12
|
-
|
|
13
|
-
```bash
|
|
14
|
-
cd plugins/apigee
|
|
15
|
-
yarn start
|
|
16
|
-
yarn test
|
|
17
|
-
yarn lint
|
|
18
|
-
yarn build
|
|
19
|
-
```
|
|
1
|
+
# @nospt/backstage-plugin-apigee
|
|
2
|
+
|
|
3
|
+
Frontend package for the bootstrap stage of the Backstage Apigee plugin.
|
|
4
|
+
|
|
5
|
+
## Included
|
|
6
|
+
|
|
7
|
+
- New Frontend System plugin registration
|
|
8
|
+
- Placeholder page under `/apigee/bootstrap`
|
|
9
|
+
- Shared permission exports from the common package
|
|
10
|
+
|
|
11
|
+
## Development
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
cd plugins/apigee
|
|
15
|
+
yarn start
|
|
16
|
+
yarn test
|
|
17
|
+
yarn lint
|
|
18
|
+
yarn build
|
|
19
|
+
```
|
package/config.d.ts
CHANGED
|
@@ -1,25 +1,25 @@
|
|
|
1
|
-
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
-
export interface Config {
|
|
3
|
-
apigee?: {
|
|
4
|
-
catalog?: {
|
|
5
|
-
/**
|
|
6
|
-
* Optional override of the default category -> icon map (UI-10 / D-08).
|
|
7
|
-
* Keys are category slugs (matching the apigee.com/category annotation);
|
|
8
|
-
* values are icon KEYS from the fixed allow-list resolved by
|
|
9
|
-
* src/components/categoryIcons.tsx. Unknown/disallowed values fall back to
|
|
10
|
-
* the `Category` icon (a value is NEVER a module path or component — see
|
|
11
|
-
* Registry Safety in 05-UI-SPEC.md).
|
|
12
|
-
* @visibility frontend
|
|
13
|
-
*/
|
|
14
|
-
categoryIcons?: { [categorySlug: string]: string };
|
|
15
|
-
/**
|
|
16
|
-
* Optional org-id -> display-label map for the Org facet.
|
|
17
|
-
* Keys are raw annotation values (for example `apigee-x-private-p-495866`);
|
|
18
|
-
* values are user-facing labels (for example `Private`).
|
|
19
|
-
* Unmapped org IDs fall back to the default humanised label.
|
|
20
|
-
* @visibility frontend
|
|
21
|
-
*/
|
|
22
|
-
orgLabels?: { [orgId: string]: string };
|
|
23
|
-
};
|
|
24
|
-
};
|
|
25
|
-
}
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
export interface Config {
|
|
3
|
+
apigee?: {
|
|
4
|
+
catalog?: {
|
|
5
|
+
/**
|
|
6
|
+
* Optional override of the default category -> icon map (UI-10 / D-08).
|
|
7
|
+
* Keys are category slugs (matching the apigee.com/category annotation);
|
|
8
|
+
* values are icon KEYS from the fixed allow-list resolved by
|
|
9
|
+
* src/components/categoryIcons.tsx. Unknown/disallowed values fall back to
|
|
10
|
+
* the `Category` icon (a value is NEVER a module path or component — see
|
|
11
|
+
* Registry Safety in 05-UI-SPEC.md).
|
|
12
|
+
* @visibility frontend
|
|
13
|
+
*/
|
|
14
|
+
categoryIcons?: { [categorySlug: string]: string };
|
|
15
|
+
/**
|
|
16
|
+
* Optional org-id -> display-label map for the Org facet.
|
|
17
|
+
* Keys are raw annotation values (for example `apigee-x-private-p-495866`);
|
|
18
|
+
* values are user-facing labels (for example `Private`).
|
|
19
|
+
* Unmapped org IDs fall back to the default humanised label.
|
|
20
|
+
* @visibility frontend
|
|
21
|
+
*/
|
|
22
|
+
orgLabels?: { [orgId: string]: string };
|
|
23
|
+
};
|
|
24
|
+
};
|
|
25
|
+
}
|
|
@@ -1,146 +1,88 @@
|
|
|
1
|
-
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
2
|
-
import { Link
|
|
1
|
+
import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
|
|
2
|
+
import { Link } from '@backstage/core-components';
|
|
3
3
|
import { useRouteRef } from '@backstage/frontend-plugin-api';
|
|
4
4
|
import { entityRouteRef } from '@backstage/plugin-catalog-react';
|
|
5
|
-
import {
|
|
6
|
-
import
|
|
7
|
-
import
|
|
5
|
+
import { Text } from '@backstage/ui';
|
|
6
|
+
import { RiGlobalLine, RiLockLine, RiTeamLine, RiBuildingLine, RiFileTextLine } from '@remixicon/react';
|
|
7
|
+
import styles from './ApiCard.module.css.esm.js';
|
|
8
8
|
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
"&:focus-visible": {
|
|
19
|
-
outline: `2px solid ${theme.palette.primary.main}`,
|
|
20
|
-
outlineOffset: 2
|
|
21
|
-
},
|
|
22
|
-
"&:hover": {
|
|
23
|
-
boxShadow: theme.shadows[4]
|
|
24
|
-
},
|
|
25
|
-
"@media (prefers-reduced-motion: reduce)": {
|
|
26
|
-
transition: "none"
|
|
27
|
-
}
|
|
28
|
-
},
|
|
29
|
-
content: {
|
|
30
|
-
display: "flex",
|
|
31
|
-
flexDirection: "column",
|
|
32
|
-
gap: theme.spacing(1),
|
|
33
|
-
height: "100%"
|
|
34
|
-
},
|
|
35
|
-
header: {
|
|
36
|
-
display: "flex",
|
|
37
|
-
alignItems: "flex-start",
|
|
38
|
-
gap: theme.spacing(1)
|
|
39
|
-
},
|
|
40
|
-
categoryIcon: {
|
|
41
|
-
color: theme.palette.text.secondary,
|
|
42
|
-
marginTop: 2
|
|
43
|
-
},
|
|
44
|
-
name: {
|
|
45
|
-
flex: 1,
|
|
46
|
-
display: "-webkit-box",
|
|
47
|
-
WebkitLineClamp: 2,
|
|
48
|
-
WebkitBoxOrient: "vertical",
|
|
49
|
-
overflow: "hidden",
|
|
50
|
-
wordBreak: "break-word"
|
|
51
|
-
},
|
|
52
|
-
meta: {
|
|
53
|
-
display: "flex",
|
|
54
|
-
alignItems: "center",
|
|
55
|
-
gap: theme.spacing(0.5),
|
|
56
|
-
color: theme.palette.text.secondary
|
|
57
|
-
},
|
|
58
|
-
metaIcon: {
|
|
59
|
-
fontSize: 16
|
|
60
|
-
},
|
|
61
|
-
version: {
|
|
62
|
-
marginLeft: "auto"
|
|
63
|
-
},
|
|
64
|
-
description: {
|
|
65
|
-
display: "-webkit-box",
|
|
66
|
-
WebkitLineClamp: 2,
|
|
67
|
-
WebkitBoxOrient: "vertical",
|
|
68
|
-
overflow: "hidden",
|
|
69
|
-
color: theme.palette.text.secondary
|
|
70
|
-
},
|
|
71
|
-
footer: {
|
|
72
|
-
marginTop: "auto",
|
|
73
|
-
display: "flex",
|
|
74
|
-
alignItems: "center",
|
|
75
|
-
gap: theme.spacing(1)
|
|
76
|
-
},
|
|
77
|
-
specIcon: {
|
|
78
|
-
display: "inline-flex",
|
|
79
|
-
color: theme.palette.text.secondary
|
|
80
|
-
}
|
|
81
|
-
}));
|
|
9
|
+
const BADGE_KIND_CLASS = {
|
|
10
|
+
ok: styles.badgeOk,
|
|
11
|
+
warning: styles.badgeWarning,
|
|
12
|
+
error: styles.badgeError
|
|
13
|
+
};
|
|
14
|
+
const VISIBILITY_ICON = {
|
|
15
|
+
private: RiLockLine,
|
|
16
|
+
public: RiGlobalLine
|
|
17
|
+
};
|
|
82
18
|
function LifecycleBadge({
|
|
83
19
|
kind,
|
|
84
20
|
label
|
|
85
21
|
}) {
|
|
86
|
-
|
|
87
|
-
return /* @__PURE__ */ jsx(StatusOK, { children: label });
|
|
88
|
-
}
|
|
89
|
-
if (kind === "error") {
|
|
90
|
-
return /* @__PURE__ */ jsx(StatusError, { children: label });
|
|
91
|
-
}
|
|
92
|
-
return /* @__PURE__ */ jsx(StatusWarning, { children: label });
|
|
22
|
+
return /* @__PURE__ */ jsx("span", { className: `${styles.badge} ${BADGE_KIND_CLASS[kind]}`, children: label });
|
|
93
23
|
}
|
|
94
24
|
function ApiCard({ vm, icon: Icon }) {
|
|
95
|
-
const classes = useStyles();
|
|
96
25
|
const entityRoute = useRouteRef(entityRouteRef);
|
|
97
26
|
const { kind, namespace, name } = vm.targetRef;
|
|
98
27
|
const href = entityRoute ? entityRoute({ kind: kind.toLowerCase(), namespace, name }) : void 0;
|
|
99
28
|
const ariaLabel = `Open ${vm.name} API details`;
|
|
100
|
-
const content = /* @__PURE__ */ jsxs(
|
|
101
|
-
/* @__PURE__ */ jsxs("div", { className:
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
29
|
+
const content = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
30
|
+
/* @__PURE__ */ jsxs("div", { className: styles.header, children: [
|
|
31
|
+
/* @__PURE__ */ jsxs("div", { className: styles.titleRow, children: [
|
|
32
|
+
Icon ? /* @__PURE__ */ jsx(Icon, { "aria-hidden": true, className: styles.categoryIcon, size: 20 }) : null,
|
|
33
|
+
/* @__PURE__ */ jsx(
|
|
34
|
+
Text,
|
|
35
|
+
{
|
|
36
|
+
variant: "body-medium",
|
|
37
|
+
weight: "bold",
|
|
38
|
+
as: "h3",
|
|
39
|
+
className: styles.name,
|
|
40
|
+
children: vm.name
|
|
41
|
+
}
|
|
42
|
+
)
|
|
43
|
+
] }),
|
|
44
|
+
/* @__PURE__ */ jsxs("div", { className: styles.badges, children: [
|
|
45
|
+
/* @__PURE__ */ jsx(LifecycleBadge, { kind: vm.badge.kind, label: vm.badge.label }),
|
|
46
|
+
vm.version ? /* @__PURE__ */ jsx("span", { className: `${styles.badge} ${styles.badgeVersion}`, children: vm.version }) : null
|
|
47
|
+
] }),
|
|
48
|
+
vm.description ? /* @__PURE__ */ jsx(
|
|
49
|
+
Text,
|
|
111
50
|
{
|
|
112
|
-
variant: "
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
vm.version
|
|
118
|
-
]
|
|
51
|
+
variant: "body-small",
|
|
52
|
+
color: "secondary",
|
|
53
|
+
as: "p",
|
|
54
|
+
className: styles.description,
|
|
55
|
+
children: vm.description
|
|
119
56
|
}
|
|
120
57
|
) : null
|
|
121
58
|
] }),
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
className:
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
59
|
+
/* @__PURE__ */ jsxs("div", { className: styles.body, children: [
|
|
60
|
+
/* @__PURE__ */ jsxs("div", { className: styles.leftCol, children: [
|
|
61
|
+
/* @__PURE__ */ jsxs("div", { className: styles.metaItem, children: [
|
|
62
|
+
/* @__PURE__ */ jsx(RiTeamLine, { "aria-hidden": true, size: 16 }),
|
|
63
|
+
/* @__PURE__ */ jsx(Text, { variant: "body-small", color: "secondary", as: "span", children: vm.ownerLabel })
|
|
64
|
+
] }),
|
|
65
|
+
vm.department ? /* @__PURE__ */ jsxs("div", { className: styles.metaItem, children: [
|
|
66
|
+
/* @__PURE__ */ jsx(RiBuildingLine, { "aria-hidden": true, size: 16 }),
|
|
67
|
+
/* @__PURE__ */ jsx(Text, { variant: "body-small", color: "secondary", as: "span", children: vm.department })
|
|
68
|
+
] }) : null,
|
|
69
|
+
vm.visibilities.map((visibility) => {
|
|
70
|
+
const VisibilityIcon = VISIBILITY_ICON[visibility];
|
|
71
|
+
return /* @__PURE__ */ jsxs("div", { className: styles.metaItem, children: [
|
|
72
|
+
/* @__PURE__ */ jsx(VisibilityIcon, { "aria-hidden": true, size: 16 }),
|
|
73
|
+
/* @__PURE__ */ jsx(Text, { variant: "body-small", color: "secondary", as: "span", children: visibility })
|
|
74
|
+
] }, visibility);
|
|
75
|
+
}),
|
|
76
|
+
vm.hasSpec ? /* @__PURE__ */ jsxs("div", { className: styles.metaItem, children: [
|
|
77
|
+
/* @__PURE__ */ jsx(RiFileTextLine, { "aria-hidden": true, size: 16 }),
|
|
78
|
+
/* @__PURE__ */ jsx(Text, { variant: "body-small", color: "secondary", as: "span", children: "OpenAPI spec" })
|
|
79
|
+
] }) : null
|
|
80
|
+
] }),
|
|
81
|
+
/* @__PURE__ */ jsx("div", { className: styles.rightCol })
|
|
82
|
+
] })
|
|
132
83
|
] });
|
|
133
|
-
return /* @__PURE__ */ jsx(
|
|
134
|
-
CardActionArea,
|
|
135
|
-
{
|
|
136
|
-
component: Link,
|
|
137
|
-
to: href,
|
|
138
|
-
className: classes.action,
|
|
139
|
-
"aria-label": ariaLabel,
|
|
140
|
-
children: content
|
|
141
|
-
}
|
|
142
|
-
) : /* @__PURE__ */ jsx(CardActionArea, { className: classes.action, "aria-label": ariaLabel, children: content }) });
|
|
84
|
+
return /* @__PURE__ */ jsx("div", { className: styles.card, children: href ? /* @__PURE__ */ jsx(Link, { to: href, className: styles.action, "aria-label": ariaLabel, children: content }) : /* @__PURE__ */ jsx("div", { className: styles.action, children: content }) });
|
|
143
85
|
}
|
|
144
86
|
|
|
145
|
-
export { ApiCard };
|
|
87
|
+
export { ApiCard, LifecycleBadge, VISIBILITY_ICON };
|
|
146
88
|
//# sourceMappingURL=ApiCard.esm.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ApiCard.esm.js","sources":["../../src/components/ApiCard.tsx"],"sourcesContent":["// SPDX-License-Identifier: Apache-2.0\r\nimport {\r\n Link,\r\n StatusError,\r\n StatusOK,\r\n StatusWarning,\r\n} from '@backstage/core-components';\r\nimport { useRouteRef } from '@backstage/frontend-plugin-api';\r\nimport { entityRouteRef } from '@backstage/plugin-catalog-react';\r\nimport {\r\n Card,\r\n CardActionArea,\r\n CardContent,\r\n Tooltip,\r\n Typography,\r\n makeStyles,\r\n} from '@material-ui/core';\r\nimport DescriptionOutlined from '@material-ui/icons/DescriptionOutlined';\r\nimport GroupOutlined from '@material-ui/icons/GroupOutlined';\r\nimport type { SvgIconComponent } from '@material-ui/icons';\r\nimport type { ApiCardViewModel, LifecycleBadgeKind } from './apiCardViewModel';\r\n\r\nexport interface ApiCardProps {\r\n vm: ApiCardViewModel;\r\n /** Category icon resolved by the parent grid (05-05); decorative. */\r\n icon?: SvgIconComponent;\r\n}\r\n\r\nconst useStyles = makeStyles(theme => ({\r\n card: {\r\n height: '100%',\r\n },\r\n action: {\r\n height: '100%',\r\n minHeight: 172,\r\n alignItems: 'stretch',\r\n transition: theme.transitions.create(['box-shadow']),\r\n '&:focus-visible': {\r\n outline: `2px solid ${theme.palette.primary.main}`,\r\n outlineOffset: 2,\r\n },\r\n '&:hover': {\r\n boxShadow: theme.shadows[4],\r\n },\r\n '@media (prefers-reduced-motion: reduce)': {\r\n transition: 'none',\r\n },\r\n },\r\n content: {\r\n display: 'flex',\r\n flexDirection: 'column',\r\n gap: theme.spacing(1),\r\n height: '100%',\r\n },\r\n header: {\r\n display: 'flex',\r\n alignItems: 'flex-start',\r\n gap: theme.spacing(1),\r\n },\r\n categoryIcon: {\r\n color: theme.palette.text.secondary,\r\n marginTop: 2,\r\n },\r\n name: {\r\n flex: 1,\r\n display: '-webkit-box',\r\n WebkitLineClamp: 2,\r\n WebkitBoxOrient: 'vertical',\r\n overflow: 'hidden',\r\n wordBreak: 'break-word',\r\n },\r\n meta: {\r\n display: 'flex',\r\n alignItems: 'center',\r\n gap: theme.spacing(0.5),\r\n color: theme.palette.text.secondary,\r\n },\r\n metaIcon: {\r\n fontSize: 16,\r\n },\r\n version: {\r\n marginLeft: 'auto',\r\n },\r\n description: {\r\n display: '-webkit-box',\r\n WebkitLineClamp: 2,\r\n WebkitBoxOrient: 'vertical',\r\n overflow: 'hidden',\r\n color: theme.palette.text.secondary,\r\n },\r\n footer: {\r\n marginTop: 'auto',\r\n display: 'flex',\r\n alignItems: 'center',\r\n gap: theme.spacing(1),\r\n },\r\n specIcon: {\r\n display: 'inline-flex',\r\n color: theme.palette.text.secondary,\r\n },\r\n}));\r\n\r\nfunction LifecycleBadge({\r\n kind,\r\n label,\r\n}: {\r\n kind: LifecycleBadgeKind;\r\n label: string;\r\n}) {\r\n if (kind === 'ok') {\r\n return <StatusOK>{label}</StatusOK>;\r\n }\r\n if (kind === 'error') {\r\n return <StatusError>{label}</StatusError>;\r\n }\r\n return <StatusWarning>{label}</StatusWarning>;\r\n}\r\n\r\n/**\r\n * A single Apigee API tile. The whole card is one anchor-based control whose href is\r\n * resolved via `entityRouteRef` (never hardcoded — TR04); it degrades to a non-link\r\n * button when the route ref is unbound (dev harness). Entity text renders as React\r\n * children only — never via raw HTML injection (T-05-03). (UI-02, UI-03)\r\n */\r\nexport function ApiCard({ vm, icon: Icon }: ApiCardProps) {\r\n const classes = useStyles();\r\n const entityRoute = useRouteRef(entityRouteRef);\r\n const { kind, namespace, name } = vm.targetRef;\r\n const href = entityRoute ? entityRoute({ kind: kind.toLowerCase(), namespace, name }) : undefined;\r\n const ariaLabel = `Open ${vm.name} API details`;\r\n\r\n const content = (\r\n <CardContent className={classes.content}>\r\n <div className={classes.header}>\r\n {Icon ? (\r\n <Icon aria-hidden className={classes.categoryIcon} fontSize=\"small\" />\r\n ) : null}\r\n <Typography variant=\"subtitle1\" component=\"h3\" className={classes.name}>\r\n {vm.name}\r\n </Typography>\r\n <LifecycleBadge kind={vm.badge.kind} label={vm.badge.label} />\r\n </div>\r\n\r\n <div className={classes.meta}>\r\n <GroupOutlined aria-hidden className={classes.metaIcon} />\r\n <Typography variant=\"body2\" component=\"span\">\r\n {vm.ownerLabel}\r\n </Typography>\r\n {vm.version ? (\r\n <Typography\r\n variant=\"body2\"\r\n component=\"span\"\r\n className={classes.version}\r\n >\r\n v{vm.version}\r\n </Typography>\r\n ) : null}\r\n </div>\r\n\r\n {vm.description ? (\r\n <Typography variant=\"body2\" className={classes.description}>\r\n {vm.description}\r\n </Typography>\r\n ) : null}\r\n\r\n {vm.hasSpec ? (\r\n <div className={classes.footer}>\r\n <Tooltip title=\"OpenAPI spec available\">\r\n <span\r\n role=\"img\"\r\n aria-label=\"OpenAPI spec available\"\r\n className={classes.specIcon}\r\n >\r\n <DescriptionOutlined aria-hidden fontSize=\"small\" />\r\n </span>\r\n </Tooltip>\r\n </div>\r\n ) : null}\r\n </CardContent>\r\n );\r\n\r\n return (\r\n <Card className={classes.card} variant=\"outlined\">\r\n {href ? (\r\n <CardActionArea\r\n component={Link}\r\n to={href}\r\n className={classes.action}\r\n aria-label={ariaLabel}\r\n >\r\n {content}\r\n </CardActionArea>\r\n ) : (\r\n <CardActionArea className={classes.action} aria-label={ariaLabel}>\r\n {content}\r\n </CardActionArea>\r\n )}\r\n </Card>\r\n );\r\n}\r\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ApiCard.esm.js","sources":["../../src/components/ApiCard.tsx"],"sourcesContent":["// SPDX-License-Identifier: Apache-2.0\nimport { Link } from '@backstage/core-components';\nimport { useRouteRef } from '@backstage/frontend-plugin-api';\nimport { entityRouteRef } from '@backstage/plugin-catalog-react';\nimport { Text } from '@backstage/ui';\nimport {\n RiBuildingLine,\n RiFileTextLine,\n RiGlobalLine,\n RiLockLine,\n RiTeamLine,\n type RemixiconComponentType,\n} from '@remixicon/react';\nimport type {\n ApiCardViewModel,\n ApiVisibility,\n LifecycleBadgeKind,\n} from './apiCardViewModel';\nimport styles from './ApiCard.module.css';\n\nexport interface ApiCardProps {\n vm: ApiCardViewModel;\n /** Category icon resolved by the parent grid (05-05); decorative. */\n icon?: RemixiconComponentType;\n}\n\nconst BADGE_KIND_CLASS: Record<LifecycleBadgeKind, string> = {\n ok: styles.badgeOk,\n warning: styles.badgeWarning,\n error: styles.badgeError,\n};\n\nexport const VISIBILITY_ICON: Record<ApiVisibility, RemixiconComponentType> = {\n private: RiLockLine,\n public: RiGlobalLine,\n};\n\n/** Solid colored status pill (D-04). Color conveys state; the text label is the\n * accessible source of truth so it never relies on color alone. */\nexport function LifecycleBadge({\n kind,\n label,\n}: {\n kind: LifecycleBadgeKind;\n label: string;\n}) {\n return (\n <span className={`${styles.badge} ${BADGE_KIND_CLASS[kind]}`}>{label}</span>\n );\n}\n\n/**\n * A single Apigee API tile. The whole card is one anchor-based control whose href is\n * resolved via `entityRouteRef` (never hardcoded — TR04); it degrades to a non-link\n * button when the route ref is unbound (dev harness). Entity text renders as React\n * children only — never via raw HTML injection (T-05-03). (UI-02, UI-03)\n */\nexport function ApiCard({ vm, icon: Icon }: ApiCardProps) {\n const entityRoute = useRouteRef(entityRouteRef);\n const { kind, namespace, name } = vm.targetRef;\n const href = entityRoute\n ? entityRoute({ kind: kind.toLowerCase(), namespace, name })\n : undefined;\n const ariaLabel = `Open ${vm.name} API details`;\n\n const content = (\n <>\n <div className={styles.header}>\n <div className={styles.titleRow}>\n {Icon ? (\n <Icon aria-hidden className={styles.categoryIcon} size={20} />\n ) : null}\n <Text\n variant=\"body-medium\"\n weight=\"bold\"\n as=\"h3\"\n className={styles.name}\n >\n {vm.name}\n </Text>\n </div>\n <div className={styles.badges}>\n <LifecycleBadge kind={vm.badge.kind} label={vm.badge.label} />\n {vm.version ? (\n <span className={`${styles.badge} ${styles.badgeVersion}`}>\n {vm.version}\n </span>\n ) : null}\n </div>\n {vm.description ? (\n <Text\n variant=\"body-small\"\n color=\"secondary\"\n as=\"p\"\n className={styles.description}\n >\n {vm.description}\n </Text>\n ) : null}\n </div>\n\n <div className={styles.body}>\n <div className={styles.leftCol}>\n <div className={styles.metaItem}>\n <RiTeamLine aria-hidden size={16} />\n <Text variant=\"body-small\" color=\"secondary\" as=\"span\">\n {vm.ownerLabel}\n </Text>\n </div>\n {vm.department ? (\n <div className={styles.metaItem}>\n <RiBuildingLine aria-hidden size={16} />\n <Text variant=\"body-small\" color=\"secondary\" as=\"span\">\n {vm.department}\n </Text>\n </div>\n ) : null}\n {vm.visibilities.map(visibility => {\n const VisibilityIcon = VISIBILITY_ICON[visibility];\n return (\n <div key={visibility} className={styles.metaItem}>\n <VisibilityIcon aria-hidden size={16} />\n <Text variant=\"body-small\" color=\"secondary\" as=\"span\">\n {visibility}\n </Text>\n </div>\n );\n })}\n {vm.hasSpec ? (\n <div className={styles.metaItem}>\n <RiFileTextLine aria-hidden size={16} />\n <Text variant=\"body-small\" color=\"secondary\" as=\"span\">\n OpenAPI spec\n </Text>\n </div>\n ) : null}\n </div>\n\n <div className={styles.rightCol}>\n {/* Quality score + information coverage list — to be implemented later */}\n </div>\n </div>\n </>\n );\n\n return (\n <div className={styles.card}>\n {href ? (\n <Link to={href} className={styles.action} aria-label={ariaLabel}>\n {content}\n </Link>\n ) : (\n <div className={styles.action}>{content}</div>\n )}\n </div>\n );\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import styleInject from '../node_modules_dist/style-inject/dist/style-inject.es.esm.js';
|
|
2
|
+
|
|
3
|
+
var css_248z = ".ApiCard_card__3afe92d8c5 {\n height: 100%;\n border: 1px solid var(--bui-border-1);\n border-radius: var(--bui-radius-3);\n background-color: var(--bui-bg-neutral-1);\n transition: box-shadow 200ms ease;\n}\n\n.ApiCard_card__3afe92d8c5:hover {\n box-shadow: var(--bui-shadow);\n}\n\n@media (prefers-reduced-motion: reduce) {\n .ApiCard_card__3afe92d8c5 {\n transition: none;\n }\n}\n\n.ApiCard_action__3afe92d8c5 {\n display: flex;\n flex-direction: column;\n gap: var(--bui-space-2);\n height: 100%;\n min-height: 172px;\n padding: var(--bui-space-4);\n color: inherit;\n text-decoration: none;\n border-radius: var(--bui-radius-3);\n}\n\n.ApiCard_action__3afe92d8c5:focus-visible {\n outline: 2px solid var(--bui-ring);\n outline-offset: 2px;\n}\n\n.ApiCard_header__3afe92d8c5 {\n display: flex;\n flex-direction: column;\n gap: var(--bui-space-2);\n}\n\n.ApiCard_titleRow__3afe92d8c5 {\n display: flex;\n align-items: flex-start;\n gap: var(--bui-space-2);\n}\n\n.ApiCard_categoryIcon__3afe92d8c5 {\n margin-top: 2px;\n color: var(--bui-fg-secondary);\n}\n\n.ApiCard_name__3afe92d8c5 {\n flex: 1;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n word-break: break-word;\n}\n\n.ApiCard_badges__3afe92d8c5 {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--bui-space-1);\n}\n\n.ApiCard_badge__3afe92d8c5 {\n flex-shrink: 0;\n display: inline-flex;\n align-items: center;\n padding: 2px var(--bui-space-2);\n border-radius: var(--bui-radius-full);\n font-size: var(--bui-font-size-1);\n font-weight: var(--bui-font-weight-bold);\n line-height: 1.5;\n text-transform: uppercase;\n letter-spacing: 0.04em;\n white-space: nowrap;\n}\n\n.ApiCard_badgeOk__3afe92d8c5 {\n background-color: var(--bui-bg-success);\n color: var(--bui-fg-success-on-bg);\n}\n\n.ApiCard_badgeWarning__3afe92d8c5 {\n background-color: var(--bui-bg-warning);\n color: var(--bui-fg-warning-on-bg);\n}\n\n.ApiCard_badgeError__3afe92d8c5 {\n background-color: var(--bui-bg-danger);\n color: var(--bui-fg-danger-on-bg);\n}\n\n.ApiCard_badgeVersion__3afe92d8c5 {\n background-color: var(--bui-bg-info);\n color: var(--bui-fg-info-on-bg);\n text-transform: none;\n}\n\n.ApiCard_body__3afe92d8c5 {\n display: flex;\n align-items: flex-start;\n gap: var(--bui-space-4);\n}\n\n.ApiCard_leftCol__3afe92d8c5,\n.ApiCard_rightCol__3afe92d8c5 {\n flex: 1;\n min-width: 0;\n display: flex;\n flex-direction: column;\n gap: var(--bui-space-1);\n}\n\n.ApiCard_leftCol__3afe92d8c5 {\n color: var(--bui-fg-secondary);\n}\n\n.ApiCard_metaItem__3afe92d8c5 {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--bui-space-1);\n}\n\n.ApiCard_description__3afe92d8c5 {\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n}\n";
|
|
4
|
+
var styles = {"card":"ApiCard_card__3afe92d8c5","action":"ApiCard_action__3afe92d8c5","header":"ApiCard_header__3afe92d8c5","titleRow":"ApiCard_titleRow__3afe92d8c5","categoryIcon":"ApiCard_categoryIcon__3afe92d8c5","name":"ApiCard_name__3afe92d8c5","badges":"ApiCard_badges__3afe92d8c5","badge":"ApiCard_badge__3afe92d8c5","badgeOk":"ApiCard_badgeOk__3afe92d8c5","badgeWarning":"ApiCard_badgeWarning__3afe92d8c5","badgeError":"ApiCard_badgeError__3afe92d8c5","badgeVersion":"ApiCard_badgeVersion__3afe92d8c5","body":"ApiCard_body__3afe92d8c5","leftCol":"ApiCard_leftCol__3afe92d8c5","rightCol":"ApiCard_rightCol__3afe92d8c5","metaItem":"ApiCard_metaItem__3afe92d8c5","description":"ApiCard_description__3afe92d8c5"};
|
|
5
|
+
styleInject(css_248z);
|
|
6
|
+
|
|
7
|
+
export { styles as default };
|
|
8
|
+
//# sourceMappingURL=ApiCard.module.css.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ApiCard.module.css.esm.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;"}
|
|
@@ -1,55 +1,21 @@
|
|
|
1
|
-
import { jsx
|
|
1
|
+
import { jsx } from 'react/jsx-runtime';
|
|
2
2
|
import { ItemCardGrid } from '@backstage/core-components';
|
|
3
|
-
import { Typography, Chip, makeStyles } from '@material-ui/core';
|
|
4
3
|
import { ApiCard } from './ApiCard.esm.js';
|
|
4
|
+
import { ApiTable } from './ApiTable.esm.js';
|
|
5
5
|
import { resolveCategoryIcon } from './categoryIcons.esm.js';
|
|
6
|
-
import { groupByCategory } from './grouping.esm.js';
|
|
7
6
|
|
|
8
|
-
const useStyles = makeStyles((theme) => ({
|
|
9
|
-
section: {
|
|
10
|
-
marginBottom: theme.spacing(4)
|
|
11
|
-
},
|
|
12
|
-
sectionHeader: {
|
|
13
|
-
display: "flex",
|
|
14
|
-
alignItems: "center",
|
|
15
|
-
gap: theme.spacing(1),
|
|
16
|
-
marginBottom: theme.spacing(2)
|
|
17
|
-
},
|
|
18
|
-
sectionIcon: {
|
|
19
|
-
color: theme.palette.text.secondary
|
|
20
|
-
}
|
|
21
|
-
}));
|
|
22
7
|
function ApiCardGrid({ vms, viewMode, overrides }) {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
return /* @__PURE__ */ jsx(ItemCardGrid, { children: vms.map((vm) => /* @__PURE__ */ jsx(
|
|
26
|
-
ApiCard,
|
|
27
|
-
{
|
|
28
|
-
vm,
|
|
29
|
-
icon: resolveCategoryIcon(vm.category, overrides)
|
|
30
|
-
},
|
|
31
|
-
vm.targetRef.name
|
|
32
|
-
)) });
|
|
8
|
+
if (viewMode === "list") {
|
|
9
|
+
return /* @__PURE__ */ jsx(ApiTable, { vms });
|
|
33
10
|
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
] }),
|
|
43
|
-
/* @__PURE__ */ jsx(ItemCardGrid, { children: group.items.map((vm) => /* @__PURE__ */ jsx(
|
|
44
|
-
ApiCard,
|
|
45
|
-
{
|
|
46
|
-
vm,
|
|
47
|
-
icon: resolveCategoryIcon(vm.category, overrides)
|
|
48
|
-
},
|
|
49
|
-
vm.targetRef.name
|
|
50
|
-
)) })
|
|
51
|
-
] }, group.key);
|
|
52
|
-
}) });
|
|
11
|
+
return /* @__PURE__ */ jsx(ItemCardGrid, { children: vms.map((vm) => /* @__PURE__ */ jsx(
|
|
12
|
+
ApiCard,
|
|
13
|
+
{
|
|
14
|
+
vm,
|
|
15
|
+
icon: resolveCategoryIcon(vm.category, overrides)
|
|
16
|
+
},
|
|
17
|
+
vm.targetRef.name
|
|
18
|
+
)) });
|
|
53
19
|
}
|
|
54
20
|
|
|
55
21
|
export { ApiCardGrid };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ApiCardGrid.esm.js","sources":["../../src/components/ApiCardGrid.tsx"],"sourcesContent":["// SPDX-License-Identifier: Apache-2.0\
|
|
1
|
+
{"version":3,"file":"ApiCardGrid.esm.js","sources":["../../src/components/ApiCardGrid.tsx"],"sourcesContent":["// SPDX-License-Identifier: Apache-2.0\nimport { ItemCardGrid } from '@backstage/core-components';\nimport { ApiCard } from './ApiCard';\nimport { ApiTable } from './ApiTable';\nimport type { ApiCardViewModel } from './apiCardViewModel';\nimport { resolveCategoryIcon } from './categoryIcons';\n\nexport type ViewMode = 'flat' | 'list';\n\nexport interface ApiCardGridProps {\n vms: ApiCardViewModel[];\n viewMode: ViewMode;\n /** Raw `apigee.catalog.categoryIcons` override map (slug -> IconKey string). */\n overrides?: Record<string, string>;\n}\n\n/**\n * Presentational grid of `ApiCard` tiles. In `flat` mode a single responsive grid; in\n * `list` mode a table. No data fetching. (UI-01, UI-11)\n */\nexport function ApiCardGrid({ vms, viewMode, overrides }: ApiCardGridProps) {\n if (viewMode === 'list') {\n return <ApiTable vms={vms} />;\n }\n\n return (\n <ItemCardGrid>\n {vms.map(vm => (\n <ApiCard\n key={vm.targetRef.name}\n vm={vm}\n icon={resolveCategoryIcon(vm.category, overrides)}\n />\n ))}\n </ItemCardGrid>\n );\n}\n"],"names":[],"mappings":";;;;;;AAoBO,SAAS,WAAA,CAAY,EAAE,GAAA,EAAK,QAAA,EAAU,WAAU,EAAqB;AAC1E,EAAA,IAAI,aAAa,MAAA,EAAQ;AACvB,IAAA,uBAAO,GAAA,CAAC,YAAS,GAAA,EAAU,CAAA;AAAA,EAC7B;AAEA,EAAA,uBACE,GAAA,CAAC,YAAA,EAAA,EACE,QAAA,EAAA,GAAA,CAAI,GAAA,CAAI,CAAA,EAAA,qBACP,GAAA;AAAA,IAAC,OAAA;AAAA,IAAA;AAAA,MAEC,EAAA;AAAA,MACA,IAAA,EAAM,mBAAA,CAAoB,EAAA,CAAG,QAAA,EAAU,SAAS;AAAA,KAAA;AAAA,IAF3C,GAAG,SAAA,CAAU;AAAA,GAIrB,CAAA,EACH,CAAA;AAEJ;;;;"}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
2
|
+
import { Link } from '@backstage/core-components';
|
|
3
|
+
import { useRouteRef } from '@backstage/frontend-plugin-api';
|
|
4
|
+
import { entityRouteRef } from '@backstage/plugin-catalog-react';
|
|
5
|
+
import { RiFileTextLine, RiArrowUpSLine, RiArrowDownSLine, RiArrowUpDownLine } from '@remixicon/react';
|
|
6
|
+
import { useState, useMemo } from 'react';
|
|
7
|
+
import { LifecycleBadge, VISIBILITY_ICON } from './ApiCard.esm.js';
|
|
8
|
+
import styles from './ApiTable.module.css.esm.js';
|
|
9
|
+
|
|
10
|
+
function sortValue(vm, key) {
|
|
11
|
+
switch (key) {
|
|
12
|
+
case "name":
|
|
13
|
+
return vm.name;
|
|
14
|
+
case "lifecycle":
|
|
15
|
+
return vm.badge.label;
|
|
16
|
+
case "version":
|
|
17
|
+
return vm.version ?? "";
|
|
18
|
+
case "owner":
|
|
19
|
+
return vm.ownerLabel;
|
|
20
|
+
case "department":
|
|
21
|
+
return vm.department ?? "";
|
|
22
|
+
case "orgs":
|
|
23
|
+
return vm.visibilities.join(", ");
|
|
24
|
+
default:
|
|
25
|
+
return "";
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
function SortableHeader({
|
|
29
|
+
label,
|
|
30
|
+
sortKey,
|
|
31
|
+
sort,
|
|
32
|
+
onSort
|
|
33
|
+
}) {
|
|
34
|
+
const activeSort = sort && sort.key === sortKey ? sort : null;
|
|
35
|
+
const ariaSort = activeSort ? activeSort.dir === "asc" ? "ascending" : "descending" : "none";
|
|
36
|
+
const Icon = activeSort ? activeSort.dir === "asc" ? RiArrowUpSLine : RiArrowDownSLine : RiArrowUpDownLine;
|
|
37
|
+
return /* @__PURE__ */ jsx("th", { scope: "col", "aria-sort": ariaSort, children: /* @__PURE__ */ jsxs(
|
|
38
|
+
"button",
|
|
39
|
+
{
|
|
40
|
+
type: "button",
|
|
41
|
+
className: styles.sortButton,
|
|
42
|
+
onClick: () => onSort(sortKey),
|
|
43
|
+
children: [
|
|
44
|
+
label,
|
|
45
|
+
/* @__PURE__ */ jsx(
|
|
46
|
+
Icon,
|
|
47
|
+
{
|
|
48
|
+
"aria-hidden": true,
|
|
49
|
+
size: 14,
|
|
50
|
+
className: activeSort ? styles.sortIcon : styles.sortIconIdle
|
|
51
|
+
}
|
|
52
|
+
)
|
|
53
|
+
]
|
|
54
|
+
}
|
|
55
|
+
) });
|
|
56
|
+
}
|
|
57
|
+
function ApiTable({ vms }) {
|
|
58
|
+
const entityRoute = useRouteRef(entityRouteRef);
|
|
59
|
+
const [sort, setSort] = useState(null);
|
|
60
|
+
const handleSort = (key) => setSort(
|
|
61
|
+
(prev) => prev?.key === key ? { key, dir: prev.dir === "asc" ? "desc" : "asc" } : { key, dir: "asc" }
|
|
62
|
+
);
|
|
63
|
+
const sortedVms = useMemo(() => {
|
|
64
|
+
if (!sort) {
|
|
65
|
+
return vms;
|
|
66
|
+
}
|
|
67
|
+
const dir = sort.dir === "asc" ? 1 : -1;
|
|
68
|
+
return [...vms].sort(
|
|
69
|
+
(a, b) => dir * sortValue(a, sort.key).localeCompare(sortValue(b, sort.key))
|
|
70
|
+
);
|
|
71
|
+
}, [vms, sort]);
|
|
72
|
+
return /* @__PURE__ */ jsx("div", { className: styles.tableWrap, children: /* @__PURE__ */ jsxs("table", { className: styles.table, children: [
|
|
73
|
+
/* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { children: [
|
|
74
|
+
/* @__PURE__ */ jsx(
|
|
75
|
+
SortableHeader,
|
|
76
|
+
{
|
|
77
|
+
label: "Name",
|
|
78
|
+
sortKey: "name",
|
|
79
|
+
sort,
|
|
80
|
+
onSort: handleSort
|
|
81
|
+
}
|
|
82
|
+
),
|
|
83
|
+
/* @__PURE__ */ jsx(
|
|
84
|
+
SortableHeader,
|
|
85
|
+
{
|
|
86
|
+
label: "Lifecycle",
|
|
87
|
+
sortKey: "lifecycle",
|
|
88
|
+
sort,
|
|
89
|
+
onSort: handleSort
|
|
90
|
+
}
|
|
91
|
+
),
|
|
92
|
+
/* @__PURE__ */ jsx(
|
|
93
|
+
SortableHeader,
|
|
94
|
+
{
|
|
95
|
+
label: "Version",
|
|
96
|
+
sortKey: "version",
|
|
97
|
+
sort,
|
|
98
|
+
onSort: handleSort
|
|
99
|
+
}
|
|
100
|
+
),
|
|
101
|
+
/* @__PURE__ */ jsx(
|
|
102
|
+
SortableHeader,
|
|
103
|
+
{
|
|
104
|
+
label: "Owner",
|
|
105
|
+
sortKey: "owner",
|
|
106
|
+
sort,
|
|
107
|
+
onSort: handleSort
|
|
108
|
+
}
|
|
109
|
+
),
|
|
110
|
+
/* @__PURE__ */ jsx(
|
|
111
|
+
SortableHeader,
|
|
112
|
+
{
|
|
113
|
+
label: "Department",
|
|
114
|
+
sortKey: "department",
|
|
115
|
+
sort,
|
|
116
|
+
onSort: handleSort
|
|
117
|
+
}
|
|
118
|
+
),
|
|
119
|
+
/* @__PURE__ */ jsx(
|
|
120
|
+
SortableHeader,
|
|
121
|
+
{
|
|
122
|
+
label: "Orgs",
|
|
123
|
+
sortKey: "orgs",
|
|
124
|
+
sort,
|
|
125
|
+
onSort: handleSort
|
|
126
|
+
}
|
|
127
|
+
),
|
|
128
|
+
/* @__PURE__ */ jsx("th", { scope: "col", children: "Spec" })
|
|
129
|
+
] }) }),
|
|
130
|
+
/* @__PURE__ */ jsx("tbody", { children: sortedVms.map((vm) => {
|
|
131
|
+
const { kind, namespace, name } = vm.targetRef;
|
|
132
|
+
const href = entityRoute ? entityRoute({ kind: kind.toLowerCase(), namespace, name }) : void 0;
|
|
133
|
+
return /* @__PURE__ */ jsxs("tr", { children: [
|
|
134
|
+
/* @__PURE__ */ jsx("td", { className: styles.nameCell, children: href ? /* @__PURE__ */ jsx(Link, { to: href, className: styles.nameLink, children: vm.name }) : vm.name }),
|
|
135
|
+
/* @__PURE__ */ jsx("td", { children: /* @__PURE__ */ jsx(LifecycleBadge, { kind: vm.badge.kind, label: vm.badge.label }) }),
|
|
136
|
+
/* @__PURE__ */ jsx("td", { children: vm.version ?? "\u2014" }),
|
|
137
|
+
/* @__PURE__ */ jsx("td", { children: vm.ownerLabel }),
|
|
138
|
+
/* @__PURE__ */ jsx("td", { children: vm.department ?? "\u2014" }),
|
|
139
|
+
/* @__PURE__ */ jsx("td", { children: vm.visibilities.length > 0 ? /* @__PURE__ */ jsx("span", { className: styles.orgs, children: vm.visibilities.map((visibility) => {
|
|
140
|
+
const VisibilityIcon = VISIBILITY_ICON[visibility];
|
|
141
|
+
return /* @__PURE__ */ jsxs("span", { className: styles.org, children: [
|
|
142
|
+
/* @__PURE__ */ jsx(VisibilityIcon, { "aria-hidden": true, size: 14 }),
|
|
143
|
+
visibility
|
|
144
|
+
] }, visibility);
|
|
145
|
+
}) }) : "\u2014" }),
|
|
146
|
+
/* @__PURE__ */ jsx("td", { children: vm.hasSpec ? /* @__PURE__ */ jsx(
|
|
147
|
+
"span",
|
|
148
|
+
{
|
|
149
|
+
role: "img",
|
|
150
|
+
"aria-label": "OpenAPI spec available",
|
|
151
|
+
title: "OpenAPI spec available",
|
|
152
|
+
className: styles.specIcon,
|
|
153
|
+
children: /* @__PURE__ */ jsx(RiFileTextLine, { "aria-hidden": true, size: 16 })
|
|
154
|
+
}
|
|
155
|
+
) : "\u2014" })
|
|
156
|
+
] }, vm.targetRef.name);
|
|
157
|
+
}) })
|
|
158
|
+
] }) });
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export { ApiTable };
|
|
162
|
+
//# sourceMappingURL=ApiTable.esm.js.map
|