@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.
Potentially problematic release.
This version of @nospt/backstage-plugin-apigee might be problematic. Click here for more details.
- 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
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ApiTable.esm.js","sources":["../../src/components/ApiTable.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 {\n RiArrowDownSLine,\n RiArrowUpDownLine,\n RiArrowUpSLine,\n RiFileTextLine,\n} from '@remixicon/react';\nimport { useMemo, useState } from 'react';\nimport { LifecycleBadge, VISIBILITY_ICON } from './ApiCard';\nimport type { ApiCardViewModel } from './apiCardViewModel';\nimport styles from './ApiTable.module.css';\n\nexport interface ApiTableProps {\n vms: ApiCardViewModel[];\n}\n\ntype SortKey = 'name' | 'lifecycle' | 'version' | 'owner' | 'department' | 'orgs';\ntype SortState = { key: SortKey; dir: 'asc' | 'desc' };\n\n/** Alphabetical sort key per column; missing values collapse to '' (sort first asc). */\nfunction sortValue(vm: ApiCardViewModel, key: SortKey): string {\n switch (key) {\n case 'name':\n return vm.name;\n case 'lifecycle':\n return vm.badge.label;\n case 'version':\n return vm.version ?? '';\n case 'owner':\n return vm.ownerLabel;\n case 'department':\n return vm.department ?? '';\n case 'orgs':\n return vm.visibilities.join(', ');\n default:\n return '';\n }\n}\n\n/** Clickable column header that toggles asc/desc alphabetical sort for its column. */\nfunction SortableHeader({\n label,\n sortKey,\n sort,\n onSort,\n}: {\n label: string;\n sortKey: SortKey;\n sort: SortState | null;\n onSort: (key: SortKey) => void;\n}) {\n const activeSort = sort && sort.key === sortKey ? sort : null;\n const ariaSort = activeSort\n ? activeSort.dir === 'asc'\n ? 'ascending'\n : 'descending'\n : 'none';\n const Icon = activeSort\n ? activeSort.dir === 'asc'\n ? RiArrowUpSLine\n : RiArrowDownSLine\n : RiArrowUpDownLine;\n return (\n <th scope=\"col\" aria-sort={ariaSort}>\n <button\n type=\"button\"\n className={styles.sortButton}\n onClick={() => onSort(sortKey)}\n >\n {label}\n <Icon\n aria-hidden\n size={14}\n className={activeSort ? styles.sortIcon : styles.sortIconIdle}\n />\n </button>\n </th>\n );\n}\n\n/**\n * Tabular (\"list\") presentation of the same view-models rendered as tiles by `ApiCard`.\n * Each row's name is the entity link (resolved via `entityRouteRef`, degrading to plain\n * text when the route ref is unbound); one anchor per row keeps the table keyboard\n * accessible. Reuses `ApiCard`'s lifecycle badge and org icons. (UI-01)\n */\nexport function ApiTable({ vms }: ApiTableProps) {\n const entityRoute = useRouteRef(entityRouteRef);\n const [sort, setSort] = useState<SortState | null>(null);\n\n const handleSort = (key: SortKey) =>\n setSort(prev =>\n prev?.key === key\n ? { key, dir: prev.dir === 'asc' ? 'desc' : 'asc' }\n : { key, dir: 'asc' },\n );\n\n const sortedVms = useMemo(() => {\n if (!sort) {\n return vms;\n }\n const dir = sort.dir === 'asc' ? 1 : -1;\n return [...vms].sort(\n (a, b) => dir * sortValue(a, sort.key).localeCompare(sortValue(b, sort.key)),\n );\n }, [vms, sort]);\n\n return (\n <div className={styles.tableWrap}>\n <table className={styles.table}>\n <thead>\n <tr>\n <SortableHeader\n label=\"Name\"\n sortKey=\"name\"\n sort={sort}\n onSort={handleSort}\n />\n <SortableHeader\n label=\"Lifecycle\"\n sortKey=\"lifecycle\"\n sort={sort}\n onSort={handleSort}\n />\n <SortableHeader\n label=\"Version\"\n sortKey=\"version\"\n sort={sort}\n onSort={handleSort}\n />\n <SortableHeader\n label=\"Owner\"\n sortKey=\"owner\"\n sort={sort}\n onSort={handleSort}\n />\n <SortableHeader\n label=\"Department\"\n sortKey=\"department\"\n sort={sort}\n onSort={handleSort}\n />\n <SortableHeader\n label=\"Orgs\"\n sortKey=\"orgs\"\n sort={sort}\n onSort={handleSort}\n />\n <th scope=\"col\">Spec</th>\n </tr>\n </thead>\n <tbody>\n {sortedVms.map(vm => {\n const { kind, namespace, name } = vm.targetRef;\n const href = entityRoute\n ? entityRoute({ kind: kind.toLowerCase(), namespace, name })\n : undefined;\n return (\n <tr key={vm.targetRef.name}>\n <td className={styles.nameCell}>\n {href ? (\n <Link to={href} className={styles.nameLink}>\n {vm.name}\n </Link>\n ) : (\n vm.name\n )}\n </td>\n <td>\n <LifecycleBadge kind={vm.badge.kind} label={vm.badge.label} />\n </td>\n <td>{vm.version ?? '—'}</td>\n <td>{vm.ownerLabel}</td>\n <td>{vm.department ?? '—'}</td>\n <td>\n {vm.visibilities.length > 0 ? (\n <span className={styles.orgs}>\n {vm.visibilities.map(visibility => {\n const VisibilityIcon = VISIBILITY_ICON[visibility];\n return (\n <span key={visibility} className={styles.org}>\n <VisibilityIcon aria-hidden size={14} />\n {visibility}\n </span>\n );\n })}\n </span>\n ) : (\n '—'\n )}\n </td>\n <td>\n {vm.hasSpec ? (\n <span\n role=\"img\"\n aria-label=\"OpenAPI spec available\"\n title=\"OpenAPI spec available\"\n className={styles.specIcon}\n >\n <RiFileTextLine aria-hidden size={16} />\n </span>\n ) : (\n '—'\n )}\n </td>\n </tr>\n );\n })}\n </tbody>\n </table>\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 = ".ApiTable_tableWrap__13d7c249ed {\n width: 100%;\n overflow-x: auto;\n}\n\n.ApiTable_table__13d7c249ed {\n width: 100%;\n border-collapse: collapse;\n font-size: var(--bui-font-size-2);\n}\n\n.ApiTable_table__13d7c249ed th,\n.ApiTable_table__13d7c249ed td {\n padding: var(--bui-space-2) var(--bui-space-3);\n text-align: left;\n vertical-align: middle;\n white-space: nowrap;\n border-bottom: 1px solid var(--bui-border-1);\n}\n\n.ApiTable_table__13d7c249ed th {\n color: var(--bui-fg-secondary);\n font-weight: var(--bui-font-weight-bold);\n}\n\n.ApiTable_table__13d7c249ed td {\n color: var(--bui-fg-secondary);\n}\n\n.ApiTable_table__13d7c249ed tbody tr:hover {\n background-color: var(--bui-bg-neutral-1);\n}\n\n.ApiTable_sortButton__13d7c249ed {\n display: inline-flex;\n align-items: center;\n gap: var(--bui-space-1);\n margin: 0;\n padding: 0;\n border: none;\n background: none;\n color: inherit;\n font: inherit;\n font-weight: var(--bui-font-weight-bold);\n cursor: pointer;\n}\n\n.ApiTable_sortButton__13d7c249ed:hover {\n color: var(--bui-fg-primary);\n}\n\n.ApiTable_sortButton__13d7c249ed:focus-visible {\n outline: 2px solid var(--bui-ring);\n outline-offset: 2px;\n border-radius: 2px;\n}\n\n.ApiTable_sortIcon__13d7c249ed {\n color: var(--bui-fg-primary);\n}\n\n.ApiTable_sortIconIdle__13d7c249ed {\n opacity: 0.4;\n}\n\n.ApiTable_nameCell__13d7c249ed {\n color: var(--bui-fg-primary);\n font-weight: var(--bui-font-weight-bold);\n}\n\n.ApiTable_nameLink__13d7c249ed {\n color: inherit;\n text-decoration: none;\n}\n\n.ApiTable_nameLink__13d7c249ed:hover {\n text-decoration: underline;\n}\n\n.ApiTable_orgs__13d7c249ed {\n display: inline-flex;\n align-items: center;\n gap: var(--bui-space-2);\n}\n\n.ApiTable_org__13d7c249ed {\n display: inline-flex;\n align-items: center;\n gap: var(--bui-space-1);\n}\n\n.ApiTable_specIcon__13d7c249ed {\n display: inline-flex;\n color: var(--bui-fg-secondary);\n}\n";
|
|
4
|
+
var styles = {"tableWrap":"ApiTable_tableWrap__13d7c249ed","table":"ApiTable_table__13d7c249ed","sortButton":"ApiTable_sortButton__13d7c249ed","sortIcon":"ApiTable_sortIcon__13d7c249ed","sortIconIdle":"ApiTable_sortIconIdle__13d7c249ed","nameCell":"ApiTable_nameCell__13d7c249ed","nameLink":"ApiTable_nameLink__13d7c249ed","orgs":"ApiTable_orgs__13d7c249ed","org":"ApiTable_org__13d7c249ed","specIcon":"ApiTable_specIcon__13d7c249ed"};
|
|
5
|
+
styleInject(css_248z);
|
|
6
|
+
|
|
7
|
+
export { styles as default };
|
|
8
|
+
//# sourceMappingURL=ApiTable.module.css.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ApiTable.module.css.esm.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;"}
|
|
@@ -1,27 +1,23 @@
|
|
|
1
|
-
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
2
|
-
import { Page,
|
|
1
|
+
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
2
|
+
import { Page, Content, ContentHeader } from '@backstage/core-components';
|
|
3
3
|
import { useApi, configApiRef } from '@backstage/core-plugin-api';
|
|
4
4
|
import { EntityListProvider, useEntityList } from '@backstage/plugin-catalog-react';
|
|
5
|
-
import {
|
|
6
|
-
import
|
|
7
|
-
import ViewModule from '@material-ui/icons/ViewModule';
|
|
5
|
+
import { ToggleButton } from '@backstage/ui';
|
|
6
|
+
import { RiLayoutGridLine, RiListUnordered } from '@remixicon/react';
|
|
8
7
|
import { useEffect, useMemo, useState } from 'react';
|
|
8
|
+
import { ANNOTATION_HAS_SPEC } from '../constants.esm.js';
|
|
9
9
|
import { toApiCardViewModel } from './apiCardViewModel.esm.js';
|
|
10
10
|
import { ApiCardGrid } from './ApiCardGrid.esm.js';
|
|
11
11
|
import { ApiKindFilter, isApigeeEntity } from './apigeeFacetFilters.esm.js';
|
|
12
12
|
import { ApigeeOrgFilter } from './apigeeOrgFilter.esm.js';
|
|
13
13
|
import { isActive } from './apigeeSearchParams.esm.js';
|
|
14
|
-
import {
|
|
14
|
+
import { CountCards } from './CountCards.esm.js';
|
|
15
|
+
import { deriveFacets, deriveCounts } from './facetModel.esm.js';
|
|
15
16
|
import { FilterToolbar } from './FilterToolbar.esm.js';
|
|
16
17
|
import { SearchBar } from './SearchBar.esm.js';
|
|
17
18
|
import { ApiCatalogError, SkeletonGrid, NoMatchState, ApiCatalogEmptyState } from './states.esm.js';
|
|
18
19
|
import { useApigeeFilters } from './useApigeeFilters.esm.js';
|
|
19
20
|
|
|
20
|
-
const useStyles = makeStyles({
|
|
21
|
-
toggleGroup: {
|
|
22
|
-
marginLeft: "auto"
|
|
23
|
-
}
|
|
24
|
-
});
|
|
25
21
|
function ApplyApigeeFilters() {
|
|
26
22
|
const { updateFilters } = useEntityList();
|
|
27
23
|
useEffect(() => {
|
|
@@ -33,9 +29,16 @@ function ApplyApigeeFilters() {
|
|
|
33
29
|
return null;
|
|
34
30
|
}
|
|
35
31
|
function ApigeeCatalogContent() {
|
|
36
|
-
const classes = useStyles();
|
|
37
32
|
const { entities, backendEntities, loading, error } = useEntityList();
|
|
38
|
-
const {
|
|
33
|
+
const {
|
|
34
|
+
state,
|
|
35
|
+
inputValue,
|
|
36
|
+
setInputValue,
|
|
37
|
+
toggleFacet,
|
|
38
|
+
selectOnlyFacet,
|
|
39
|
+
clearFacet,
|
|
40
|
+
clearAll
|
|
41
|
+
} = useApigeeFilters();
|
|
39
42
|
const configApi = useApi(configApiRef);
|
|
40
43
|
const overrides = useMemo(() => {
|
|
41
44
|
const config = configApi.getOptionalConfig("apigee.catalog.categoryIcons");
|
|
@@ -65,10 +68,24 @@ function ApigeeCatalogContent() {
|
|
|
65
68
|
}
|
|
66
69
|
return Object.keys(out).length > 0 ? out : void 0;
|
|
67
70
|
}, [configApi]);
|
|
71
|
+
const apigeeEntities = useMemo(
|
|
72
|
+
() => backendEntities.filter(isApigeeEntity),
|
|
73
|
+
[backendEntities]
|
|
74
|
+
);
|
|
68
75
|
const vms = useMemo(() => entities.map(toApiCardViewModel), [entities]);
|
|
69
76
|
const facets = useMemo(
|
|
70
|
-
() => deriveFacets(
|
|
71
|
-
[
|
|
77
|
+
() => deriveFacets(apigeeEntities, overrides, orgLabels),
|
|
78
|
+
[apigeeEntities, overrides, orgLabels]
|
|
79
|
+
);
|
|
80
|
+
const counts = useMemo(
|
|
81
|
+
() => deriveCounts(apigeeEntities, ["org", "lifecycle", "department"], orgLabels),
|
|
82
|
+
[apigeeEntities, orgLabels]
|
|
83
|
+
);
|
|
84
|
+
const specCount = useMemo(
|
|
85
|
+
() => apigeeEntities.filter(
|
|
86
|
+
(entity) => entity.metadata.annotations?.[ANNOTATION_HAS_SPEC] === "true"
|
|
87
|
+
).length,
|
|
88
|
+
[apigeeEntities]
|
|
72
89
|
);
|
|
73
90
|
const active = isActive(state);
|
|
74
91
|
const [viewMode, setViewMode] = useState("flat");
|
|
@@ -84,48 +101,50 @@ function ApigeeCatalogContent() {
|
|
|
84
101
|
}
|
|
85
102
|
return /* @__PURE__ */ jsx(ApiCardGrid, { vms, viewMode, overrides });
|
|
86
103
|
};
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
104
|
+
const viewToggle = /* @__PURE__ */ jsxs(
|
|
105
|
+
"div",
|
|
106
|
+
{
|
|
107
|
+
role: "group",
|
|
108
|
+
"aria-label": "API view mode",
|
|
109
|
+
style: { display: "inline-flex", gap: "var(--bui-space-1)" },
|
|
110
|
+
children: [
|
|
111
|
+
/* @__PURE__ */ jsx(
|
|
112
|
+
ToggleButton,
|
|
113
|
+
{
|
|
114
|
+
"aria-label": "Flat grid view",
|
|
115
|
+
isSelected: viewMode === "flat",
|
|
116
|
+
onChange: () => setViewMode("flat"),
|
|
117
|
+
iconStart: /* @__PURE__ */ jsx(RiLayoutGridLine, {}),
|
|
118
|
+
children: "Flat"
|
|
119
|
+
}
|
|
120
|
+
),
|
|
121
|
+
/* @__PURE__ */ jsx(
|
|
122
|
+
ToggleButton,
|
|
123
|
+
{
|
|
124
|
+
"aria-label": "List view",
|
|
125
|
+
isSelected: viewMode === "list",
|
|
126
|
+
onChange: () => setViewMode("list"),
|
|
127
|
+
iconStart: /* @__PURE__ */ jsx(RiListUnordered, {}),
|
|
128
|
+
children: "List"
|
|
129
|
+
}
|
|
130
|
+
)
|
|
131
|
+
]
|
|
132
|
+
}
|
|
133
|
+
);
|
|
134
|
+
return /* @__PURE__ */ jsx(Page, { themeId: "apis", children: /* @__PURE__ */ jsxs(Content, { children: [
|
|
135
|
+
/* @__PURE__ */ jsx(ContentHeader, { title: "APIs" }),
|
|
136
|
+
!loading && !error && apigeeEntities.length > 0 ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
137
|
+
/* @__PURE__ */ jsx(
|
|
138
|
+
CountCards,
|
|
98
139
|
{
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
Button,
|
|
105
|
-
{
|
|
106
|
-
startIcon: /* @__PURE__ */ jsx(ViewModule, {}),
|
|
107
|
-
"aria-label": "Flat grid view",
|
|
108
|
-
"aria-pressed": viewMode === "flat",
|
|
109
|
-
color: viewMode === "flat" ? "primary" : "default",
|
|
110
|
-
onClick: () => setViewMode("flat"),
|
|
111
|
-
children: "Flat"
|
|
112
|
-
}
|
|
113
|
-
),
|
|
114
|
-
/* @__PURE__ */ jsx(
|
|
115
|
-
Button,
|
|
116
|
-
{
|
|
117
|
-
startIcon: /* @__PURE__ */ jsx(ViewAgenda, {}),
|
|
118
|
-
"aria-label": "Group by category",
|
|
119
|
-
"aria-pressed": viewMode === "grouped",
|
|
120
|
-
color: viewMode === "grouped" ? "primary" : "default",
|
|
121
|
-
onClick: () => setViewMode("grouped"),
|
|
122
|
-
children: "Grouped"
|
|
123
|
-
}
|
|
124
|
-
)
|
|
125
|
-
] })
|
|
140
|
+
total: apigeeEntities.length,
|
|
141
|
+
specCount,
|
|
142
|
+
counts,
|
|
143
|
+
selected: state.selected,
|
|
144
|
+
onSelectFacet: selectOnlyFacet
|
|
126
145
|
}
|
|
127
|
-
)
|
|
128
|
-
|
|
146
|
+
),
|
|
147
|
+
/* @__PURE__ */ jsx(
|
|
129
148
|
FilterToolbar,
|
|
130
149
|
{
|
|
131
150
|
facets,
|
|
@@ -135,12 +154,13 @@ function ApigeeCatalogContent() {
|
|
|
135
154
|
onClearAll: clearAll,
|
|
136
155
|
active,
|
|
137
156
|
resultCount: vms.length,
|
|
138
|
-
searchBar: /* @__PURE__ */ jsx(SearchBar, { value: inputValue, onChange: setInputValue })
|
|
157
|
+
searchBar: /* @__PURE__ */ jsx(SearchBar, { value: inputValue, onChange: setInputValue }),
|
|
158
|
+
viewToggle
|
|
139
159
|
}
|
|
140
|
-
)
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
] });
|
|
160
|
+
)
|
|
161
|
+
] }) : null,
|
|
162
|
+
renderBody()
|
|
163
|
+
] }) });
|
|
144
164
|
}
|
|
145
165
|
function ApigeeApiCatalogPage() {
|
|
146
166
|
return /* @__PURE__ */ jsxs(EntityListProvider, { children: [
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ApigeeApiCatalogPage.esm.js","sources":["../../src/components/ApigeeApiCatalogPage.tsx"],"sourcesContent":["// SPDX-License-Identifier: Apache-2.0\r\nimport { Content, ContentHeader, Header, Page } from '@backstage/core-components';\r\nimport { configApiRef, useApi } from '@backstage/core-plugin-api';\r\nimport {\r\n EntityListProvider,\r\n useEntityList,\r\n type DefaultEntityFilters,\r\n} from '@backstage/plugin-catalog-react';\r\nimport { Button, ButtonGroup, makeStyles } from '@material-ui/core';\r\nimport ViewAgenda from '@material-ui/icons/ViewAgenda';\r\nimport ViewModule from '@material-ui/icons/ViewModule';\r\nimport { useEffect, useMemo, useState } from 'react';\r\nimport { toApiCardViewModel } from './apiCardViewModel';\r\nimport { ApiCardGrid, type ViewMode } from './ApiCardGrid';\r\nimport {\r\n ApiKindFilter,\r\n isApigeeEntity,\r\n type ApiSearchFilter,\r\n type FieldFacetFilter,\r\n} from './apigeeFacetFilters';\r\nimport { ApigeeOrgFilter } from './apigeeOrgFilter';\r\nimport { isActive } from './apigeeSearchParams';\r\nimport { deriveFacets } from './facetModel';\r\nimport { FilterToolbar } from './FilterToolbar';\r\nimport { SearchBar } from './SearchBar';\r\nimport {\r\n ApiCatalogEmptyState,\r\n ApiCatalogError,\r\n NoMatchState,\r\n SkeletonGrid,\r\n} from './states';\r\nimport { useApigeeFilters } from './useApigeeFilters';\r\n\r\n/**\r\n * Entity-list filters registered on the Apigee catalog page. `apigeeKind` (the seam-free\r\n * `ApiKindFilter`) and `apigeeOrg` (the dual-key is-Apigee gate) are the backend filters applied\r\n * once by {@link ApplyApigeeFilters}; the facet + search filters\r\n * (`apigeeLifecycle`/`apigeeOrgFacet`/`apigeeOwner`/`apigeeCategory`/`apigeeDepartment`/`apigeeSearch`)\r\n * are driven from the URL by `useApigeeFilters`. Exported so the hook can import the filter contract.\r\n */\r\nexport type ApigeeFilters = DefaultEntityFilters & {\r\n apigeeKind?: ApiKindFilter;\r\n apigeeOrg?: ApigeeOrgFilter;\r\n apigeeLifecycle?: FieldFacetFilter;\r\n apigeeOrgFacet?: FieldFacetFilter;\r\n apigeeOwner?: FieldFacetFilter;\r\n apigeeCategory?: FieldFacetFilter;\r\n apigeeDepartment?: FieldFacetFilter;\r\n apigeeSearch?: ApiSearchFilter;\r\n};\r\n\r\nconst useStyles = makeStyles({\r\n toggleGroup: {\r\n marginLeft: 'auto',\r\n },\r\n});\r\n\r\n/**\r\n * Registers the Apigee backend filters on the shared EntityListProvider ONCE: the seam-free\r\n * `ApiKindFilter` (kind=api with NO `toQueryValue`, so the provider's URL writer stays a no-op and\r\n * `useSearchParams` owns a clean URL) plus the dual-key `ApigeeOrgFilter` is-Apigee gate. The facet\r\n * + search filters are applied separately, URL-driven, by `useApigeeFilters`. Rendered inside the\r\n * provider; renders nothing itself.\r\n */\r\nfunction ApplyApigeeFilters() {\r\n const { updateFilters } = useEntityList<ApigeeFilters>();\r\n useEffect(() => {\r\n updateFilters({\r\n apigeeKind: new ApiKindFilter(),\r\n apigeeOrg: new ApigeeOrgFilter(),\r\n });\r\n }, [updateFilters]);\r\n return null;\r\n}\r\n\r\n/**\r\n * The page body: reads the filtered entity list, maps to view-models, and branches on\r\n * loading/empty/error. Exposes a Flat/Grouped view toggle (component state only, no view\r\n * URL param — that is a Phase 6 seam). (UI-01, UI-05, UI-11)\r\n */\r\nexport function ApigeeCatalogContent() {\r\n const classes = useStyles();\r\n const { entities, backendEntities, loading, error } =\r\n useEntityList<ApigeeFilters>();\r\n const { state, inputValue, setInputValue, toggleFacet, clearFacet, clearAll } =\r\n useApigeeFilters();\r\n const configApi = useApi(configApiRef);\r\n\r\n const overrides = useMemo<Record<string, string> | undefined>(() => {\r\n const config = configApi.getOptionalConfig('apigee.catalog.categoryIcons');\r\n if (!config) {\r\n return undefined;\r\n }\r\n const out: Record<string, string> = {};\r\n for (const key of config.keys()) {\r\n const value = config.getOptionalString(key);\r\n if (value) {\r\n out[key] = value;\r\n }\r\n }\r\n return Object.keys(out).length > 0 ? out : undefined;\r\n }, [configApi]);\r\n\r\n const orgLabels = useMemo<Record<string, string> | undefined>(() => {\r\n const config = configApi.getOptionalConfig('apigee.catalog.orgLabels');\r\n if (!config) {\r\n return undefined;\r\n }\r\n const out: Record<string, string> = {};\r\n for (const key of config.keys()) {\r\n const value = config.getOptionalString(key);\r\n if (value) {\r\n out[key] = value;\r\n }\r\n }\r\n return Object.keys(out).length > 0 ? out : undefined;\r\n }, [configApi]);\r\n\r\n const vms = useMemo(() => entities.map(toApiCardViewModel), [entities]);\r\n const facets = useMemo(\r\n () => deriveFacets(backendEntities.filter(isApigeeEntity), overrides, orgLabels),\r\n [backendEntities, overrides, orgLabels],\r\n );\r\n const active = isActive(state);\r\n const [viewMode, setViewMode] = useState<ViewMode>('flat');\r\n\r\n const renderBody = () => {\r\n if (error) {\r\n return <ApiCatalogError error={error} />;\r\n }\r\n if (loading) {\r\n return <SkeletonGrid />;\r\n }\r\n if (vms.length === 0) {\r\n return active ? (\r\n <NoMatchState onClearAll={clearAll} />\r\n ) : (\r\n <ApiCatalogEmptyState />\r\n );\r\n }\r\n return <ApiCardGrid vms={vms} viewMode={viewMode} overrides={overrides} />;\r\n };\r\n\r\n return (\r\n <Page themeId=\"apis\">\r\n <Header\r\n title=\"API Catalog\"\r\n subtitle=\"Browse APIs discovered from Apigee & API Hub\"\r\n />\r\n <Content>\r\n <ContentHeader title={`${vms.length} APIs`}>\r\n <div\r\n role=\"group\"\r\n aria-label=\"API view mode\"\r\n className={classes.toggleGroup}\r\n >\r\n <ButtonGroup size=\"small\">\r\n <Button\r\n startIcon={<ViewModule />}\r\n aria-label=\"Flat grid view\"\r\n aria-pressed={viewMode === 'flat'}\r\n color={viewMode === 'flat' ? 'primary' : 'default'}\r\n onClick={() => setViewMode('flat')}\r\n >\r\n Flat\r\n </Button>\r\n <Button\r\n startIcon={<ViewAgenda />}\r\n aria-label=\"Group by category\"\r\n aria-pressed={viewMode === 'grouped'}\r\n color={viewMode === 'grouped' ? 'primary' : 'default'}\r\n onClick={() => setViewMode('grouped')}\r\n >\r\n Grouped\r\n </Button>\r\n </ButtonGroup>\r\n </div>\r\n </ContentHeader>\r\n {!loading &&\r\n !error &&\r\n backendEntities.filter(isApigeeEntity).length > 0 ? (\r\n <FilterToolbar\r\n facets={facets}\r\n selected={state.selected}\r\n onToggleFacet={toggleFacet}\r\n onClearFacet={clearFacet}\r\n onClearAll={clearAll}\r\n active={active}\r\n resultCount={vms.length}\r\n searchBar={\r\n <SearchBar value={inputValue} onChange={setInputValue} />\r\n }\r\n />\r\n ) : null}\r\n {renderBody()}\r\n </Content>\r\n </Page>\r\n );\r\n}\r\n\r\n/**\r\n * Route target for `/apigee` (05-06). Wraps the content in an `EntityListProvider` and\r\n * registers the Apigee filters — the shared filter context Phase 6's facets + search reuse.\r\n */\r\nexport function ApigeeApiCatalogPage() {\r\n return (\r\n <EntityListProvider>\r\n <ApplyApigeeFilters />\r\n <ApigeeCatalogContent />\r\n </EntityListProvider>\r\n );\r\n}\r\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;AAmDA,MAAM,YAAY,UAAA,CAAW;AAAA,EAC3B,WAAA,EAAa;AAAA,IACX,UAAA,EAAY;AAAA;AAEhB,CAAC,CAAA;AASD,SAAS,kBAAA,GAAqB;AAC5B,EAAA,MAAM,EAAE,aAAA,EAAc,GAAI,aAAA,EAA6B;AACvD,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,aAAA,CAAc;AAAA,MACZ,UAAA,EAAY,IAAI,aAAA,EAAc;AAAA,MAC9B,SAAA,EAAW,IAAI,eAAA;AAAgB,KAChC,CAAA;AAAA,EACH,CAAA,EAAG,CAAC,aAAa,CAAC,CAAA;AAClB,EAAA,OAAO,IAAA;AACT;AAOO,SAAS,oBAAA,GAAuB;AACrC,EAAA,MAAM,UAAU,SAAA,EAAU;AAC1B,EAAA,MAAM,EAAE,QAAA,EAAU,eAAA,EAAiB,OAAA,EAAS,KAAA,KAC1C,aAAA,EAA6B;AAC/B,EAAA,MAAM,EAAE,OAAO,UAAA,EAAY,aAAA,EAAe,aAAa,UAAA,EAAY,QAAA,KACjE,gBAAA,EAAiB;AACnB,EAAA,MAAM,SAAA,GAAY,OAAO,YAAY,CAAA;AAErC,EAAA,MAAM,SAAA,GAAY,QAA4C,MAAM;AAClE,IAAA,MAAM,MAAA,GAAS,SAAA,CAAU,iBAAA,CAAkB,8BAA8B,CAAA;AACzE,IAAA,IAAI,CAAC,MAAA,EAAQ;AACX,MAAA,OAAO,MAAA;AAAA,IACT;AACA,IAAA,MAAM,MAA8B,EAAC;AACrC,IAAA,KAAA,MAAW,GAAA,IAAO,MAAA,CAAO,IAAA,EAAK,EAAG;AAC/B,MAAA,MAAM,KAAA,GAAQ,MAAA,CAAO,iBAAA,CAAkB,GAAG,CAAA;AAC1C,MAAA,IAAI,KAAA,EAAO;AACT,QAAA,GAAA,CAAI,GAAG,CAAA,GAAI,KAAA;AAAA,MACb;AAAA,IACF;AACA,IAAA,OAAO,OAAO,IAAA,CAAK,GAAG,CAAA,CAAE,MAAA,GAAS,IAAI,GAAA,GAAM,MAAA;AAAA,EAC7C,CAAA,EAAG,CAAC,SAAS,CAAC,CAAA;AAEd,EAAA,MAAM,SAAA,GAAY,QAA4C,MAAM;AAClE,IAAA,MAAM,MAAA,GAAS,SAAA,CAAU,iBAAA,CAAkB,0BAA0B,CAAA;AACrE,IAAA,IAAI,CAAC,MAAA,EAAQ;AACX,MAAA,OAAO,MAAA;AAAA,IACT;AACA,IAAA,MAAM,MAA8B,EAAC;AACrC,IAAA,KAAA,MAAW,GAAA,IAAO,MAAA,CAAO,IAAA,EAAK,EAAG;AAC/B,MAAA,MAAM,KAAA,GAAQ,MAAA,CAAO,iBAAA,CAAkB,GAAG,CAAA;AAC1C,MAAA,IAAI,KAAA,EAAO;AACT,QAAA,GAAA,CAAI,GAAG,CAAA,GAAI,KAAA;AAAA,MACb;AAAA,IACF;AACA,IAAA,OAAO,OAAO,IAAA,CAAK,GAAG,CAAA,CAAE,MAAA,GAAS,IAAI,GAAA,GAAM,MAAA;AAAA,EAC7C,CAAA,EAAG,CAAC,SAAS,CAAC,CAAA;AAEd,EAAA,MAAM,GAAA,GAAM,QAAQ,MAAM,QAAA,CAAS,IAAI,kBAAkB,CAAA,EAAG,CAAC,QAAQ,CAAC,CAAA;AACtE,EAAA,MAAM,MAAA,GAAS,OAAA;AAAA,IACb,MAAM,YAAA,CAAa,eAAA,CAAgB,OAAO,cAAc,CAAA,EAAG,WAAW,SAAS,CAAA;AAAA,IAC/E,CAAC,eAAA,EAAiB,SAAA,EAAW,SAAS;AAAA,GACxC;AACA,EAAA,MAAM,MAAA,GAAS,SAAS,KAAK,CAAA;AAC7B,EAAA,MAAM,CAAC,QAAA,EAAU,WAAW,CAAA,GAAI,SAAmB,MAAM,CAAA;AAEzD,EAAA,MAAM,aAAa,MAAM;AACvB,IAAA,IAAI,KAAA,EAAO;AACT,MAAA,uBAAO,GAAA,CAAC,mBAAgB,KAAA,EAAc,CAAA;AAAA,IACxC;AACA,IAAA,IAAI,OAAA,EAAS;AACX,MAAA,2BAAQ,YAAA,EAAA,EAAa,CAAA;AAAA,IACvB;AACA,IAAA,IAAI,GAAA,CAAI,WAAW,CAAA,EAAG;AACpB,MAAA,OAAO,yBACL,GAAA,CAAC,YAAA,EAAA,EAAa,YAAY,QAAA,EAAU,CAAA,uBAEnC,oBAAA,EAAA,EAAqB,CAAA;AAAA,IAE1B;AACA,IAAA,uBAAO,GAAA,CAAC,WAAA,EAAA,EAAY,GAAA,EAAU,QAAA,EAAoB,SAAA,EAAsB,CAAA;AAAA,EAC1E,CAAA;AAEA,EAAA,uBACE,IAAA,CAAC,IAAA,EAAA,EAAK,OAAA,EAAQ,MAAA,EACZ,QAAA,EAAA;AAAA,oBAAA,GAAA;AAAA,MAAC,MAAA;AAAA,MAAA;AAAA,QACC,KAAA,EAAM,aAAA;AAAA,QACN,QAAA,EAAS;AAAA;AAAA,KACX;AAAA,yBACC,OAAA,EAAA,EACC,QAAA,EAAA;AAAA,sBAAA,GAAA,CAAC,aAAA,EAAA,EAAc,KAAA,EAAO,CAAA,EAAG,GAAA,CAAI,MAAM,CAAA,KAAA,CAAA,EACjC,QAAA,kBAAA,GAAA;AAAA,QAAC,KAAA;AAAA,QAAA;AAAA,UACC,IAAA,EAAK,OAAA;AAAA,UACL,YAAA,EAAW,eAAA;AAAA,UACX,WAAW,OAAA,CAAQ,WAAA;AAAA,UAEnB,QAAA,kBAAA,IAAA,CAAC,WAAA,EAAA,EAAY,IAAA,EAAK,OAAA,EAChB,QAAA,EAAA;AAAA,4BAAA,GAAA;AAAA,cAAC,MAAA;AAAA,cAAA;AAAA,gBACC,SAAA,sBAAY,UAAA,EAAA,EAAW,CAAA;AAAA,gBACvB,YAAA,EAAW,gBAAA;AAAA,gBACX,gBAAc,QAAA,KAAa,MAAA;AAAA,gBAC3B,KAAA,EAAO,QAAA,KAAa,MAAA,GAAS,SAAA,GAAY,SAAA;AAAA,gBACzC,OAAA,EAAS,MAAM,WAAA,CAAY,MAAM,CAAA;AAAA,gBAClC,QAAA,EAAA;AAAA;AAAA,aAED;AAAA,4BACA,GAAA;AAAA,cAAC,MAAA;AAAA,cAAA;AAAA,gBACC,SAAA,sBAAY,UAAA,EAAA,EAAW,CAAA;AAAA,gBACvB,YAAA,EAAW,mBAAA;AAAA,gBACX,gBAAc,QAAA,KAAa,SAAA;AAAA,gBAC3B,KAAA,EAAO,QAAA,KAAa,SAAA,GAAY,SAAA,GAAY,SAAA;AAAA,gBAC5C,OAAA,EAAS,MAAM,WAAA,CAAY,SAAS,CAAA;AAAA,gBACrC,QAAA,EAAA;AAAA;AAAA;AAED,WAAA,EACF;AAAA;AAAA,OACF,EACF,CAAA;AAAA,MACC,CAAC,WACF,CAAC,KAAA,IACD,gBAAgB,MAAA,CAAO,cAAc,CAAA,CAAE,MAAA,GAAS,CAAA,mBAC9C,GAAA;AAAA,QAAC,aAAA;AAAA,QAAA;AAAA,UACC,MAAA;AAAA,UACA,UAAU,KAAA,CAAM,QAAA;AAAA,UAChB,aAAA,EAAe,WAAA;AAAA,UACf,YAAA,EAAc,UAAA;AAAA,UACd,UAAA,EAAY,QAAA;AAAA,UACZ,MAAA;AAAA,UACA,aAAa,GAAA,CAAI,MAAA;AAAA,UACjB,2BACE,GAAA,CAAC,SAAA,EAAA,EAAU,KAAA,EAAO,UAAA,EAAY,UAAU,aAAA,EAAe;AAAA;AAAA,OAE3D,GACE,IAAA;AAAA,MACH,UAAA;AAAW,KAAA,EACd;AAAA,GAAA,EACF,CAAA;AAEJ;AAMO,SAAS,oBAAA,GAAuB;AACrC,EAAA,4BACG,kBAAA,EAAA,EACC,QAAA,EAAA;AAAA,oBAAA,GAAA,CAAC,kBAAA,EAAA,EAAmB,CAAA;AAAA,wBACnB,oBAAA,EAAA,EAAqB;AAAA,GAAA,EACxB,CAAA;AAEJ;;;;"}
|
|
1
|
+
{"version":3,"file":"ApigeeApiCatalogPage.esm.js","sources":["../../src/components/ApigeeApiCatalogPage.tsx"],"sourcesContent":["// SPDX-License-Identifier: Apache-2.0\nimport { Content, ContentHeader, Page } from '@backstage/core-components';\nimport { configApiRef, useApi } from '@backstage/core-plugin-api';\nimport {\n EntityListProvider,\n useEntityList,\n type DefaultEntityFilters,\n} from '@backstage/plugin-catalog-react';\nimport { ToggleButton } from '@backstage/ui';\nimport { RiLayoutGridLine, RiListUnordered } from '@remixicon/react';\nimport { useEffect, useMemo, useState } from 'react';\nimport { ANNOTATION_HAS_SPEC } from '../constants';\nimport { toApiCardViewModel } from './apiCardViewModel';\nimport { ApiCardGrid, type ViewMode } from './ApiCardGrid';\nimport {\n ApiKindFilter,\n isApigeeEntity,\n type ApiSearchFilter,\n type FieldFacetFilter,\n} from './apigeeFacetFilters';\nimport { ApigeeOrgFilter } from './apigeeOrgFilter';\nimport { isActive } from './apigeeSearchParams';\nimport { CountCards } from './CountCards';\nimport { deriveCounts, deriveFacets } from './facetModel';\nimport { FilterToolbar } from './FilterToolbar';\nimport { SearchBar } from './SearchBar';\nimport {\n ApiCatalogEmptyState,\n ApiCatalogError,\n NoMatchState,\n SkeletonGrid,\n} from './states';\nimport { useApigeeFilters } from './useApigeeFilters';\n\n/**\n * Entity-list filters registered on the Apigee catalog page. `apigeeKind` (the seam-free\n * `ApiKindFilter`) and `apigeeOrg` (the dual-key is-Apigee gate) are the backend filters applied\n * once by {@link ApplyApigeeFilters}; the facet + search filters\n * (`apigeeLifecycle`/`apigeeOrgFacet`/`apigeeOwner`/`apigeeCategory`/`apigeeDepartment`/`apigeeSearch`)\n * are driven from the URL by `useApigeeFilters`. Exported so the hook can import the filter contract.\n */\nexport type ApigeeFilters = DefaultEntityFilters & {\n apigeeKind?: ApiKindFilter;\n apigeeOrg?: ApigeeOrgFilter;\n apigeeLifecycle?: FieldFacetFilter;\n apigeeOrgFacet?: FieldFacetFilter;\n apigeeOwner?: FieldFacetFilter;\n apigeeCategory?: FieldFacetFilter;\n apigeeDepartment?: FieldFacetFilter;\n apigeeSearch?: ApiSearchFilter;\n};\n\n/**\n * Registers the Apigee backend filters on the shared EntityListProvider ONCE: the seam-free\n * `ApiKindFilter` (kind=api with NO `toQueryValue`, so the provider's URL writer stays a no-op and\n * `useSearchParams` owns a clean URL) plus the dual-key `ApigeeOrgFilter` is-Apigee gate. The facet\n * + search filters are applied separately, URL-driven, by `useApigeeFilters`. Rendered inside the\n * provider; renders nothing itself.\n */\nfunction ApplyApigeeFilters() {\n const { updateFilters } = useEntityList<ApigeeFilters>();\n useEffect(() => {\n updateFilters({\n apigeeKind: new ApiKindFilter(),\n apigeeOrg: new ApigeeOrgFilter(),\n });\n }, [updateFilters]);\n return null;\n}\n\n/**\n * The page body: reads the filtered entity list, maps to view-models, and branches on\n * loading/empty/error. Exposes a Flat/List view toggle (component state only, no view\n * URL param — that is a Phase 6 seam). (UI-01, UI-05, UI-11)\n */\nexport function ApigeeCatalogContent() {\n const { entities, backendEntities, loading, error } =\n useEntityList<ApigeeFilters>();\n const {\n state,\n inputValue,\n setInputValue,\n toggleFacet,\n selectOnlyFacet,\n clearFacet,\n clearAll,\n } = useApigeeFilters();\n const configApi = useApi(configApiRef);\n\n const overrides = useMemo<Record<string, string> | undefined>(() => {\n const config = configApi.getOptionalConfig('apigee.catalog.categoryIcons');\n if (!config) {\n return undefined;\n }\n const out: Record<string, string> = {};\n for (const key of config.keys()) {\n const value = config.getOptionalString(key);\n if (value) {\n out[key] = value;\n }\n }\n return Object.keys(out).length > 0 ? out : undefined;\n }, [configApi]);\n\n const orgLabels = useMemo<Record<string, string> | undefined>(() => {\n const config = configApi.getOptionalConfig('apigee.catalog.orgLabels');\n if (!config) {\n return undefined;\n }\n const out: Record<string, string> = {};\n for (const key of config.keys()) {\n const value = config.getOptionalString(key);\n if (value) {\n out[key] = value;\n }\n }\n return Object.keys(out).length > 0 ? out : undefined;\n }, [configApi]);\n\n const apigeeEntities = useMemo(\n () => backendEntities.filter(isApigeeEntity),\n [backendEntities],\n );\n const vms = useMemo(() => entities.map(toApiCardViewModel), [entities]);\n const facets = useMemo(\n () => deriveFacets(apigeeEntities, overrides, orgLabels),\n [apigeeEntities, overrides, orgLabels],\n );\n // Static full-catalog counts: the cards stay fixed as filters apply; only the grid narrows.\n const counts = useMemo(\n () => deriveCounts(apigeeEntities, ['org', 'lifecycle', 'department'], orgLabels),\n [apigeeEntities, orgLabels],\n );\n const specCount = useMemo(\n () =>\n apigeeEntities.filter(\n entity => entity.metadata.annotations?.[ANNOTATION_HAS_SPEC] === 'true',\n ).length,\n [apigeeEntities],\n );\n const active = isActive(state);\n const [viewMode, setViewMode] = useState<ViewMode>('flat');\n\n const renderBody = () => {\n if (error) {\n return <ApiCatalogError error={error} />;\n }\n if (loading) {\n return <SkeletonGrid />;\n }\n if (vms.length === 0) {\n return active ? (\n <NoMatchState onClearAll={clearAll} />\n ) : (\n <ApiCatalogEmptyState />\n );\n }\n return <ApiCardGrid vms={vms} viewMode={viewMode} overrides={overrides} />;\n };\n\n const viewToggle = (\n <div\n role=\"group\"\n aria-label=\"API view mode\"\n style={{ display: 'inline-flex', gap: 'var(--bui-space-1)' }}\n >\n <ToggleButton\n aria-label=\"Flat grid view\"\n isSelected={viewMode === 'flat'}\n onChange={() => setViewMode('flat')}\n iconStart={<RiLayoutGridLine />}\n >\n Flat\n </ToggleButton>\n <ToggleButton\n aria-label=\"List view\"\n isSelected={viewMode === 'list'}\n onChange={() => setViewMode('list')}\n iconStart={<RiListUnordered />}\n >\n List\n </ToggleButton>\n </div>\n );\n\n return (\n <Page themeId=\"apis\">\n <Content>\n <ContentHeader title=\"APIs\" />\n {!loading && !error && apigeeEntities.length > 0 ? (\n <>\n <CountCards\n total={apigeeEntities.length}\n specCount={specCount}\n counts={counts}\n selected={state.selected}\n onSelectFacet={selectOnlyFacet}\n />\n <FilterToolbar\n facets={facets}\n selected={state.selected}\n onToggleFacet={toggleFacet}\n onClearFacet={clearFacet}\n onClearAll={clearAll}\n active={active}\n resultCount={vms.length}\n searchBar={\n <SearchBar value={inputValue} onChange={setInputValue} />\n }\n viewToggle={viewToggle}\n />\n </>\n ) : null}\n {renderBody()}\n </Content>\n </Page>\n );\n}\n\n/**\n * Route target for `/apigee` (05-06). Wraps the content in an `EntityListProvider` and\n * registers the Apigee filters — the shared filter context Phase 6's facets + search reuse.\n */\nexport function ApigeeApiCatalogPage() {\n return (\n <EntityListProvider>\n <ApplyApigeeFilters />\n <ApigeeCatalogContent />\n </EntityListProvider>\n );\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
2
2
|
import { Sidebar, SidebarItem, SidebarSubmenu, SidebarSubmenuItem } from '@backstage/core-components';
|
|
3
|
-
import
|
|
3
|
+
import { RiStackLine } from '@remixicon/react';
|
|
4
4
|
|
|
5
5
|
const ApigeeNavContent = ({ navItems }) => {
|
|
6
6
|
const allItems = navItems.rest();
|
|
@@ -20,7 +20,7 @@ const ApigeeNavContent = ({ navItems }) => {
|
|
|
20
20
|
},
|
|
21
21
|
item.node.spec.id
|
|
22
22
|
)),
|
|
23
|
-
apigeeItems.length > 0 && /* @__PURE__ */ jsx(SidebarItem, { icon:
|
|
23
|
+
apigeeItems.length > 0 && /* @__PURE__ */ jsx(SidebarItem, { icon: () => /* @__PURE__ */ jsx(RiStackLine, {}), text: "API Hub", children: /* @__PURE__ */ jsx(SidebarSubmenu, { title: "API Hub", children: apigeeItems.map((item) => /* @__PURE__ */ jsx(
|
|
24
24
|
SidebarSubmenuItem,
|
|
25
25
|
{
|
|
26
26
|
title: item.title,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ApigeeNavContent.esm.js","sources":["../../src/components/ApigeeNavContent.tsx"],"sourcesContent":["// SPDX-License-Identifier: Apache-2.0\
|
|
1
|
+
{"version":3,"file":"ApigeeNavContent.esm.js","sources":["../../src/components/ApigeeNavContent.tsx"],"sourcesContent":["// SPDX-License-Identifier: Apache-2.0\nimport {\n Sidebar,\n SidebarItem,\n SidebarSubmenu,\n SidebarSubmenuItem,\n} from '@backstage/core-components';\nimport type {\n NavContentComponent,\n NavContentNavItem,\n} from '@backstage/plugin-app-react';\nimport { RiStackLine } from '@remixicon/react';\nimport React from 'react';\n\n/**\n * Renders the app nav bar with an expandable \"API Hub\" group for all apigee\n * plugin pages, while rendering any other plugins' pages as flat sidebar items.\n *\n * To activate, add `apigeeNavModule` to your app's `createApp({ features })`.\n */\nexport const ApigeeNavContent: NavContentComponent = ({ navItems }) => {\n const allItems = navItems.rest();\n const apigeeItems = allItems.filter(\n (item: NavContentNavItem) =>\n item.node.spec.plugin.pluginId === 'apigee',\n );\n const otherItems = allItems.filter(\n (item: NavContentNavItem) =>\n item.node.spec.plugin.pluginId !== 'apigee',\n );\n\n return (\n <Sidebar>\n {otherItems.map((item: NavContentNavItem) => (\n <SidebarItem\n key={item.node.spec.id}\n to={item.href}\n // item.icon is IconElement (JSX); SidebarItem.icon is IconComponent.\n // Wrapping in a render-prop is the documented pattern from AppNav.\n icon={(_props: object) => item.icon as React.ReactElement}\n text={item.title}\n />\n ))}\n {apigeeItems.length > 0 && (\n <SidebarItem icon={() => <RiStackLine />} text=\"API Hub\">\n <SidebarSubmenu title=\"API Hub\">\n {apigeeItems.map((item: NavContentNavItem) => (\n <SidebarSubmenuItem\n key={item.node.spec.id}\n title={item.title}\n to={item.href}\n />\n ))}\n </SidebarSubmenu>\n </SidebarItem>\n )}\n </Sidebar>\n );\n};\n"],"names":[],"mappings":";;;;AAoBO,MAAM,gBAAA,GAAwC,CAAC,EAAE,QAAA,EAAS,KAAM;AACrE,EAAA,MAAM,QAAA,GAAW,SAAS,IAAA,EAAK;AAC/B,EAAA,MAAM,cAAc,QAAA,CAAS,MAAA;AAAA,IAC3B,CAAC,IAAA,KACC,IAAA,CAAK,IAAA,CAAK,IAAA,CAAK,OAAO,QAAA,KAAa;AAAA,GACvC;AACA,EAAA,MAAM,aAAa,QAAA,CAAS,MAAA;AAAA,IAC1B,CAAC,IAAA,KACC,IAAA,CAAK,IAAA,CAAK,IAAA,CAAK,OAAO,QAAA,KAAa;AAAA,GACvC;AAEA,EAAA,4BACG,OAAA,EAAA,EACE,QAAA,EAAA;AAAA,IAAA,UAAA,CAAW,GAAA,CAAI,CAAC,IAAA,qBACf,GAAA;AAAA,MAAC,WAAA;AAAA,MAAA;AAAA,QAEC,IAAI,IAAA,CAAK,IAAA;AAAA,QAGT,IAAA,EAAM,CAAC,MAAA,KAAmB,IAAA,CAAK,IAAA;AAAA,QAC/B,MAAM,IAAA,CAAK;AAAA,OAAA;AAAA,MALN,IAAA,CAAK,KAAK,IAAA,CAAK;AAAA,KAOvB,CAAA;AAAA,IACA,YAAY,MAAA,GAAS,CAAA,wBACnB,WAAA,EAAA,EAAY,IAAA,EAAM,sBAAM,GAAA,CAAC,WAAA,EAAA,EAAY,GAAI,IAAA,EAAK,SAAA,EAC7C,8BAAC,cAAA,EAAA,EAAe,KAAA,EAAM,WACnB,QAAA,EAAA,WAAA,CAAY,GAAA,CAAI,CAAC,IAAA,qBAChB,GAAA;AAAA,MAAC,kBAAA;AAAA,MAAA;AAAA,QAEC,OAAO,IAAA,CAAK,KAAA;AAAA,QACZ,IAAI,IAAA,CAAK;AAAA,OAAA;AAAA,MAFJ,IAAA,CAAK,KAAK,IAAA,CAAK;AAAA,KAIvB,GACH,CAAA,EACF;AAAA,GAAA,EAEJ,CAAA;AAEJ;;;;"}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
2
|
+
import { Text } from '@backstage/ui';
|
|
3
|
+
import styles from './CountCards.module.css.esm.js';
|
|
4
|
+
|
|
5
|
+
function CountCards({
|
|
6
|
+
total,
|
|
7
|
+
specCount,
|
|
8
|
+
counts,
|
|
9
|
+
selected,
|
|
10
|
+
onSelectFacet
|
|
11
|
+
}) {
|
|
12
|
+
return /* @__PURE__ */ jsxs("div", { className: styles.row, children: [
|
|
13
|
+
/* @__PURE__ */ jsxs("div", { role: "group", "aria-label": "APIs", className: styles.card, children: [
|
|
14
|
+
/* @__PURE__ */ jsx(
|
|
15
|
+
Text,
|
|
16
|
+
{
|
|
17
|
+
as: "p",
|
|
18
|
+
variant: "title-large",
|
|
19
|
+
weight: "bold",
|
|
20
|
+
className: styles.total,
|
|
21
|
+
children: total
|
|
22
|
+
}
|
|
23
|
+
),
|
|
24
|
+
/* @__PURE__ */ jsx(
|
|
25
|
+
Text,
|
|
26
|
+
{
|
|
27
|
+
as: "h3",
|
|
28
|
+
variant: "body-x-small",
|
|
29
|
+
color: "secondary",
|
|
30
|
+
className: styles.cardTitle,
|
|
31
|
+
children: "APIs"
|
|
32
|
+
}
|
|
33
|
+
)
|
|
34
|
+
] }),
|
|
35
|
+
/* @__PURE__ */ jsxs("div", { role: "group", "aria-label": "With specs", className: styles.card, children: [
|
|
36
|
+
/* @__PURE__ */ jsx(
|
|
37
|
+
Text,
|
|
38
|
+
{
|
|
39
|
+
as: "p",
|
|
40
|
+
variant: "title-large",
|
|
41
|
+
weight: "bold",
|
|
42
|
+
className: styles.total,
|
|
43
|
+
children: specCount
|
|
44
|
+
}
|
|
45
|
+
),
|
|
46
|
+
/* @__PURE__ */ jsx(
|
|
47
|
+
Text,
|
|
48
|
+
{
|
|
49
|
+
as: "h3",
|
|
50
|
+
variant: "body-x-small",
|
|
51
|
+
color: "secondary",
|
|
52
|
+
className: styles.cardTitle,
|
|
53
|
+
children: "With specs"
|
|
54
|
+
}
|
|
55
|
+
)
|
|
56
|
+
] }),
|
|
57
|
+
counts.map((count) => {
|
|
58
|
+
const heading = `By ${count.label.toLowerCase()}`;
|
|
59
|
+
return /* @__PURE__ */ jsxs(
|
|
60
|
+
"div",
|
|
61
|
+
{
|
|
62
|
+
role: "group",
|
|
63
|
+
"aria-label": heading,
|
|
64
|
+
className: styles.card,
|
|
65
|
+
children: [
|
|
66
|
+
/* @__PURE__ */ jsx(
|
|
67
|
+
Text,
|
|
68
|
+
{
|
|
69
|
+
as: "h3",
|
|
70
|
+
variant: "body-x-small",
|
|
71
|
+
color: "secondary",
|
|
72
|
+
className: styles.cardTitle,
|
|
73
|
+
children: heading
|
|
74
|
+
}
|
|
75
|
+
),
|
|
76
|
+
count.buckets.length === 0 ? /* @__PURE__ */ jsx(Text, { variant: "body-small", color: "secondary", children: "No data" }) : /* @__PURE__ */ jsx("div", { className: styles.list, children: count.buckets.map((bucket) => {
|
|
77
|
+
const isSelected = selected[count.key].includes(bucket.value);
|
|
78
|
+
return /* @__PURE__ */ jsxs(
|
|
79
|
+
"button",
|
|
80
|
+
{
|
|
81
|
+
type: "button",
|
|
82
|
+
className: `${styles.bucket} ${isSelected ? styles.bucketSelected : ""}`,
|
|
83
|
+
"aria-pressed": isSelected,
|
|
84
|
+
"aria-label": `${bucket.label}, ${bucket.count} ${bucket.count === 1 ? "API" : "APIs"}`,
|
|
85
|
+
onClick: () => onSelectFacet(count.key, bucket.value),
|
|
86
|
+
children: [
|
|
87
|
+
/* @__PURE__ */ jsx(
|
|
88
|
+
Text,
|
|
89
|
+
{
|
|
90
|
+
as: "span",
|
|
91
|
+
variant: "body-small",
|
|
92
|
+
truncate: true,
|
|
93
|
+
title: bucket.label,
|
|
94
|
+
className: styles.bucketLabel,
|
|
95
|
+
children: bucket.label
|
|
96
|
+
}
|
|
97
|
+
),
|
|
98
|
+
/* @__PURE__ */ jsx(
|
|
99
|
+
Text,
|
|
100
|
+
{
|
|
101
|
+
as: "span",
|
|
102
|
+
variant: "body-small",
|
|
103
|
+
weight: "bold",
|
|
104
|
+
className: styles.bucketCount,
|
|
105
|
+
children: bucket.count
|
|
106
|
+
}
|
|
107
|
+
)
|
|
108
|
+
]
|
|
109
|
+
},
|
|
110
|
+
bucket.value
|
|
111
|
+
);
|
|
112
|
+
}) })
|
|
113
|
+
]
|
|
114
|
+
},
|
|
115
|
+
count.key
|
|
116
|
+
);
|
|
117
|
+
})
|
|
118
|
+
] });
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export { CountCards };
|
|
122
|
+
//# sourceMappingURL=CountCards.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CountCards.esm.js","sources":["../../src/components/CountCards.tsx"],"sourcesContent":["// SPDX-License-Identifier: Apache-2.0\nimport { Text } from '@backstage/ui';\nimport type { DerivedCount, FacetKey } from './facetModel';\nimport styles from './CountCards.module.css';\n\nexport interface CountCardsProps {\n /** Total APIs in the Apigee catalog (static snapshot — not the filtered count). */\n total: number;\n /** APIs in the catalog that have an OpenAPI spec (static snapshot). */\n specCount: number;\n /** Per-facet breakdowns (org / lifecycle / department) from `deriveCounts`. */\n counts: DerivedCount[];\n /** Currently selected values per facet (from the URL filter state). */\n selected: Record<FacetKey, string[]>;\n /** Select a single facet value — clicking filters the catalog to exactly that value, replacing any other card selection. */\n onSelectFacet: (key: FacetKey, value: string) => void;\n}\n\n/**\n * Summary stat row shown above the filter toolbar: the prominent total-APIs card, a with-specs\n * stat card, and one breakdown card per facet (org / lifecycle / department). Counts are a\n * fixed snapshot of the full Apigee catalog — they do NOT change as filters apply. Each bucket is\n * a single-select link: clicking filters the grid to exactly that value (replacing any other card\n * selection), and the active value is highlighted. Purely presentational.\n */\nexport function CountCards({\n total,\n specCount,\n counts,\n selected,\n onSelectFacet,\n}: CountCardsProps) {\n return (\n <div className={styles.row}>\n <div role=\"group\" aria-label=\"APIs\" className={styles.card}>\n <Text\n as=\"p\"\n variant=\"title-large\"\n weight=\"bold\"\n className={styles.total}\n >\n {total}\n </Text>\n <Text\n as=\"h3\"\n variant=\"body-x-small\"\n color=\"secondary\"\n className={styles.cardTitle}\n >\n APIs\n </Text>\n </div>\n <div role=\"group\" aria-label=\"With specs\" className={styles.card}>\n <Text\n as=\"p\"\n variant=\"title-large\"\n weight=\"bold\"\n className={styles.total}\n >\n {specCount}\n </Text>\n <Text\n as=\"h3\"\n variant=\"body-x-small\"\n color=\"secondary\"\n className={styles.cardTitle}\n >\n With specs\n </Text>\n </div>\n {counts.map(count => {\n const heading = `By ${count.label.toLowerCase()}`;\n return (\n <div\n key={count.key}\n role=\"group\"\n aria-label={heading}\n className={styles.card}\n >\n <Text\n as=\"h3\"\n variant=\"body-x-small\"\n color=\"secondary\"\n className={styles.cardTitle}\n >\n {heading}\n </Text>\n {count.buckets.length === 0 ? (\n <Text variant=\"body-small\" color=\"secondary\">\n No data\n </Text>\n ) : (\n <div className={styles.list}>\n {count.buckets.map(bucket => {\n const isSelected = selected[count.key].includes(bucket.value);\n return (\n <button\n key={bucket.value}\n type=\"button\"\n className={`${styles.bucket} ${\n isSelected ? styles.bucketSelected : ''\n }`}\n aria-pressed={isSelected}\n aria-label={`${bucket.label}, ${bucket.count} ${\n bucket.count === 1 ? 'API' : 'APIs'\n }`}\n onClick={() => onSelectFacet(count.key, bucket.value)}\n >\n <Text\n as=\"span\"\n variant=\"body-small\"\n truncate\n title={bucket.label}\n className={styles.bucketLabel}\n >\n {bucket.label}\n </Text>\n <Text\n as=\"span\"\n variant=\"body-small\"\n weight=\"bold\"\n className={styles.bucketCount}\n >\n {bucket.count}\n </Text>\n </button>\n );\n })}\n </div>\n )}\n </div>\n );\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 = ".CountCards_row__c9b5cbf79d {\n display: flex;\n flex-wrap: wrap;\n gap: var(--bui-space-4);\n margin-bottom: var(--bui-space-4);\n}\n\n.CountCards_card__c9b5cbf79d {\n flex: 1 1 200px;\n min-width: 200px;\n padding: var(--bui-space-4);\n border: 1px solid var(--bui-border-1);\n border-radius: var(--bui-radius-3);\n background-color: var(--bui-bg-neutral-1);\n}\n\n.CountCards_total__c9b5cbf79d {\n line-height: 1.1;\n}\n\n.CountCards_cardTitle__c9b5cbf79d {\n margin-bottom: var(--bui-space-2);\n text-transform: uppercase;\n letter-spacing: 0.5px;\n}\n\n.CountCards_list__c9b5cbf79d {\n max-height: 160px;\n overflow-y: auto;\n}\n\n.CountCards_bucket__c9b5cbf79d {\n display: flex;\n gap: var(--bui-space-2);\n align-items: baseline;\n justify-content: space-between;\n width: 100%;\n padding: var(--bui-space-1) var(--bui-space-2);\n border: none;\n border-radius: var(--bui-radius-2);\n background: none;\n color: inherit;\n font: inherit;\n text-align: left;\n cursor: pointer;\n}\n\n.CountCards_bucket__c9b5cbf79d:hover {\n background-color: var(--bui-bg-neutral-1-hover);\n}\n\n.CountCards_bucket__c9b5cbf79d:focus-visible {\n outline: 2px solid var(--bui-ring);\n outline-offset: -2px;\n}\n\n.CountCards_bucketSelected__c9b5cbf79d {\n background-color: var(--bui-bg-neutral-1-pressed);\n}\n\n.CountCards_bucketLabel__c9b5cbf79d {\n flex: 1;\n min-width: 0;\n}\n\n.CountCards_bucketCount__c9b5cbf79d {\n font-variant-numeric: tabular-nums;\n}\n";
|
|
4
|
+
var styles = {"row":"CountCards_row__c9b5cbf79d","card":"CountCards_card__c9b5cbf79d","total":"CountCards_total__c9b5cbf79d","cardTitle":"CountCards_cardTitle__c9b5cbf79d","list":"CountCards_list__c9b5cbf79d","bucket":"CountCards_bucket__c9b5cbf79d","bucketSelected":"CountCards_bucketSelected__c9b5cbf79d","bucketLabel":"CountCards_bucketLabel__c9b5cbf79d","bucketCount":"CountCards_bucketCount__c9b5cbf79d"};
|
|
5
|
+
styleInject(css_248z);
|
|
6
|
+
|
|
7
|
+
export { styles as default };
|
|
8
|
+
//# sourceMappingURL=CountCards.module.css.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CountCards.module.css.esm.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;"}
|