@nospt/backstage-plugin-apigee 0.1.0 → 0.1.1
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/dist/components/ApiCard.esm.js +0 -0
- package/dist/components/ApiCard.esm.js.map +1 -1
- package/dist/components/ApiCardGrid.esm.js +0 -0
- package/dist/components/ApiCardGrid.esm.js.map +1 -1
- package/dist/components/ApigeeApiCatalogPage.esm.js +0 -0
- package/dist/components/ApigeeApiCatalogPage.esm.js.map +1 -1
- package/dist/components/FilterToolbar.esm.js +0 -0
- package/dist/components/FilterToolbar.esm.js.map +1 -1
- package/dist/components/SearchBar.esm.js +0 -0
- package/dist/components/SearchBar.esm.js.map +1 -1
- package/dist/components/apiCardViewModel.esm.js +0 -0
- package/dist/components/apiCardViewModel.esm.js.map +1 -1
- package/dist/components/apigeeFacetFilters.esm.js +0 -0
- package/dist/components/apigeeFacetFilters.esm.js.map +1 -1
- package/dist/components/apigeeOrgFilter.esm.js +0 -0
- package/dist/components/apigeeOrgFilter.esm.js.map +1 -1
- package/dist/components/apigeeSearchParams.esm.js +0 -0
- package/dist/components/apigeeSearchParams.esm.js.map +1 -1
- package/dist/components/categoryIcons.esm.js +0 -0
- package/dist/components/categoryIcons.esm.js.map +1 -1
- package/dist/components/facetModel.esm.js +0 -0
- package/dist/components/facetModel.esm.js.map +1 -1
- package/dist/components/grouping.esm.js +0 -0
- package/dist/components/grouping.esm.js.map +1 -1
- package/dist/components/states.esm.js +0 -0
- package/dist/components/states.esm.js.map +1 -1
- package/dist/components/useApigeeFilters.esm.js +0 -0
- package/dist/components/useApigeeFilters.esm.js.map +1 -1
- package/dist/components/useDebouncedValue.esm.js +0 -0
- package/dist/components/useDebouncedValue.esm.js.map +1 -1
- package/dist/constants.esm.js +0 -0
- package/dist/constants.esm.js.map +1 -1
- package/dist/index.d.ts +0 -0
- package/dist/index.esm.js +0 -0
- package/dist/index.esm.js.map +0 -0
- package/dist/plugin.esm.js +0 -0
- package/dist/plugin.esm.js.map +1 -1
- package/dist/routes.esm.js +0 -0
- package/dist/routes.esm.js.map +1 -1
- package/package.json +3 -3
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
|
+
```
|
|
File without changes
|
|
@@ -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 {\n Link,\n StatusError,\n StatusOK,\n StatusWarning,\n} from '@backstage/core-components';\nimport { useRouteRef } from '@backstage/frontend-plugin-api';\nimport { entityRouteRef } from '@backstage/plugin-catalog-react';\nimport {\n Card,\n CardActionArea,\n CardContent,\n Tooltip,\n Typography,\n makeStyles,\n} from '@material-ui/core';\nimport DescriptionOutlined from '@material-ui/icons/DescriptionOutlined';\nimport GroupOutlined from '@material-ui/icons/GroupOutlined';\nimport type { SvgIconComponent } from '@material-ui/icons';\nimport type { ApiCardViewModel, LifecycleBadgeKind } from './apiCardViewModel';\n\nexport interface ApiCardProps {\n vm: ApiCardViewModel;\n /** Category icon resolved by the parent grid (05-05); decorative. */\n icon?: SvgIconComponent;\n}\n\nconst useStyles = makeStyles(theme => ({\n card: {\n height: '100%',\n },\n action: {\n height: '100%',\n minHeight: 172,\n alignItems: 'stretch',\n transition: theme.transitions.create(['box-shadow']),\n '&:focus-visible': {\n outline: `2px solid ${theme.palette.primary.main}`,\n outlineOffset: 2,\n },\n '&:hover': {\n boxShadow: theme.shadows[4],\n },\n '@media (prefers-reduced-motion: reduce)': {\n transition: 'none',\n },\n },\n content: {\n display: 'flex',\n flexDirection: 'column',\n gap: theme.spacing(1),\n height: '100%',\n },\n header: {\n display: 'flex',\n alignItems: 'flex-start',\n gap: theme.spacing(1),\n },\n categoryIcon: {\n color: theme.palette.text.secondary,\n marginTop: 2,\n },\n name: {\n flex: 1,\n display: '-webkit-box',\n WebkitLineClamp: 2,\n WebkitBoxOrient: 'vertical',\n overflow: 'hidden',\n wordBreak: 'break-word',\n },\n meta: {\n display: 'flex',\n alignItems: 'center',\n gap: theme.spacing(0.5),\n color: theme.palette.text.secondary,\n },\n metaIcon: {\n fontSize: 16,\n },\n version: {\n marginLeft: 'auto',\n },\n description: {\n display: '-webkit-box',\n WebkitLineClamp: 2,\n WebkitBoxOrient: 'vertical',\n overflow: 'hidden',\n color: theme.palette.text.secondary,\n },\n footer: {\n marginTop: 'auto',\n display: 'flex',\n alignItems: 'center',\n gap: theme.spacing(1),\n },\n specIcon: {\n display: 'inline-flex',\n color: theme.palette.text.secondary,\n },\n}));\n\nfunction LifecycleBadge({\n kind,\n label,\n}: {\n kind: LifecycleBadgeKind;\n label: string;\n}) {\n if (kind === 'ok') {\n return <StatusOK>{label}</StatusOK>;\n }\n if (kind === 'error') {\n return <StatusError>{label}</StatusError>;\n }\n return <StatusWarning>{label}</StatusWarning>;\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 classes = useStyles();\n const entityRoute = useRouteRef(entityRouteRef);\n const { kind, namespace, name } = vm.targetRef;\n const href = entityRoute ? entityRoute({ kind: kind.toLowerCase(), namespace, name }) : undefined;\n const ariaLabel = `Open ${vm.name} API details`;\n\n const content = (\n <CardContent className={classes.content}>\n <div className={classes.header}>\n {Icon ? (\n <Icon aria-hidden className={classes.categoryIcon} fontSize=\"small\" />\n ) : null}\n <Typography variant=\"subtitle1\" component=\"h3\" className={classes.name}>\n {vm.name}\n </Typography>\n <LifecycleBadge kind={vm.badge.kind} label={vm.badge.label} />\n </div>\n\n <div className={classes.meta}>\n <GroupOutlined aria-hidden className={classes.metaIcon} />\n <Typography variant=\"body2\" component=\"span\">\n {vm.ownerLabel}\n </Typography>\n {vm.version ? (\n <Typography\n variant=\"body2\"\n component=\"span\"\n className={classes.version}\n >\n v{vm.version}\n </Typography>\n ) : null}\n </div>\n\n {vm.description ? (\n <Typography variant=\"body2\" className={classes.description}>\n {vm.description}\n </Typography>\n ) : null}\n\n {vm.hasSpec ? (\n <div className={classes.footer}>\n <Tooltip title=\"OpenAPI spec available\">\n <span\n role=\"img\"\n aria-label=\"OpenAPI spec available\"\n className={classes.specIcon}\n >\n <DescriptionOutlined aria-hidden fontSize=\"small\" />\n </span>\n </Tooltip>\n </div>\n ) : null}\n </CardContent>\n );\n\n return (\n <Card className={classes.card} variant=\"outlined\">\n {href ? (\n <CardActionArea\n component={Link}\n to={href}\n className={classes.action}\n aria-label={ariaLabel}\n >\n {content}\n </CardActionArea>\n ) : (\n <CardActionArea className={classes.action} aria-label={ariaLabel}>\n {content}\n </CardActionArea>\n )}\n </Card>\n );\n}\n"],"names":[],"mappings":"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"}
|
|
File without changes
|
|
@@ -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 { Chip, Typography, makeStyles } from '@material-ui/core';\nimport { ApiCard } from './ApiCard';\nimport type { ApiCardViewModel } from './apiCardViewModel';\nimport { resolveCategoryIcon } from './categoryIcons';\nimport { groupByCategory } from './grouping';\n\nexport type ViewMode = 'flat' | 'grouped';\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\nconst useStyles = makeStyles(theme => ({\n section: {\n marginBottom: theme.spacing(4),\n },\n sectionHeader: {\n display: 'flex',\n alignItems: 'center',\n gap: theme.spacing(1),\n marginBottom: theme.spacing(2),\n },\n sectionIcon: {\n color: theme.palette.text.secondary,\n },\n}));\n\n/**\n * Presentational grid of `ApiCard` tiles. In `flat` mode a single responsive grid; in\n * `grouped` mode one section per category (icon + label + count chip) with the \"Other\"\n * bucket last (ordering enforced by `groupByCategory`). No data fetching. (UI-01, UI-11)\n */\nexport function ApiCardGrid({ vms, viewMode, overrides }: ApiCardGridProps) {\n const classes = useStyles();\n\n if (viewMode === 'flat') {\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\n const groups = groupByCategory(vms);\n return (\n <>\n {groups.map(group => {\n const SectionIcon = resolveCategoryIcon(group.key, overrides);\n return (\n <section key={group.key} className={classes.section}>\n <div className={classes.sectionHeader}>\n <SectionIcon aria-hidden className={classes.sectionIcon} />\n <Typography variant=\"h5\" component=\"h2\">\n {group.label}\n </Typography>\n <Chip size=\"small\" label={group.items.length} />\n </div>\n <ItemCardGrid>\n {group.items.map(vm => (\n <ApiCard\n key={vm.targetRef.name}\n vm={vm}\n icon={resolveCategoryIcon(vm.category, overrides)}\n />\n ))}\n </ItemCardGrid>\n </section>\n );\n })}\n </>\n );\n}\n"],"names":[],"mappings":";;;;;;;AAiBA,MAAM,SAAA,GAAY,WAAW,CAAA,KAAA,MAAU;AAAA,EACrC,OAAA,EAAS;AAAA,IACP,YAAA,EAAc,KAAA,CAAM,OAAA,CAAQ,CAAC;AAAA,GAC/B;AAAA,EACA,aAAA,EAAe;AAAA,IACb,OAAA,EAAS,MAAA;AAAA,IACT,UAAA,EAAY,QAAA;AAAA,IACZ,GAAA,EAAK,KAAA,CAAM,OAAA,CAAQ,CAAC,CAAA;AAAA,IACpB,YAAA,EAAc,KAAA,CAAM,OAAA,CAAQ,CAAC;AAAA,GAC/B;AAAA,EACA,WAAA,EAAa;AAAA,IACX,KAAA,EAAO,KAAA,CAAM,OAAA,CAAQ,IAAA,CAAK;AAAA;AAE9B,CAAA,CAAE,CAAA;AAOK,SAAS,WAAA,CAAY,EAAE,GAAA,EAAK,QAAA,EAAU,WAAU,EAAqB;AAC1E,EAAA,MAAM,UAAU,SAAA,EAAU;AAE1B,EAAA,IAAI,aAAa,MAAA,EAAQ;AACvB,IAAA,uBACE,GAAA,CAAC,YAAA,EAAA,EACE,QAAA,EAAA,GAAA,CAAI,GAAA,CAAI,CAAA,EAAA,qBACP,GAAA;AAAA,MAAC,OAAA;AAAA,MAAA;AAAA,QAEC,EAAA;AAAA,QACA,IAAA,EAAM,mBAAA,CAAoB,EAAA,CAAG,QAAA,EAAU,SAAS;AAAA,OAAA;AAAA,MAF3C,GAAG,SAAA,CAAU;AAAA,KAIrB,CAAA,EACH,CAAA;AAAA,EAEJ;AAEA,EAAA,MAAM,MAAA,GAAS,gBAAgB,GAAG,CAAA;AAClC,EAAA,uBACE,GAAA,CAAA,QAAA,EAAA,EACG,QAAA,EAAA,MAAA,CAAO,GAAA,CAAI,CAAA,KAAA,KAAS;AACnB,IAAA,MAAM,WAAA,GAAc,mBAAA,CAAoB,KAAA,CAAM,GAAA,EAAK,SAAS,CAAA;AAC5D,IAAA,uBACE,IAAA,CAAC,SAAA,EAAA,EAAwB,SAAA,EAAW,OAAA,CAAQ,OAAA,EAC1C,QAAA,EAAA;AAAA,sBAAA,IAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAW,OAAA,CAAQ,aAAA,EACtB,QAAA,EAAA;AAAA,wBAAA,GAAA,CAAC,WAAA,EAAA,EAAY,aAAA,EAAW,IAAA,EAAC,SAAA,EAAW,QAAQ,WAAA,EAAa,CAAA;AAAA,4BACxD,UAAA,EAAA,EAAW,OAAA,EAAQ,MAAK,SAAA,EAAU,IAAA,EAChC,gBAAM,KAAA,EACT,CAAA;AAAA,4BACC,IAAA,EAAA,EAAK,IAAA,EAAK,SAAQ,KAAA,EAAO,KAAA,CAAM,MAAM,MAAA,EAAQ;AAAA,OAAA,EAChD,CAAA;AAAA,sBACA,GAAA,CAAC,YAAA,EAAA,EACE,QAAA,EAAA,KAAA,CAAM,KAAA,CAAM,IAAI,CAAA,EAAA,qBACf,GAAA;AAAA,QAAC,OAAA;AAAA,QAAA;AAAA,UAEC,EAAA;AAAA,UACA,IAAA,EAAM,mBAAA,CAAoB,EAAA,CAAG,QAAA,EAAU,SAAS;AAAA,SAAA;AAAA,QAF3C,GAAG,SAAA,CAAU;AAAA,OAIrB,CAAA,EACH;AAAA,KAAA,EAAA,EAhBY,MAAM,GAiBpB,CAAA;AAAA,EAEJ,CAAC,CAAA,EACH,CAAA;AAEJ;;;;"}
|
|
File without changes
|
|
@@ -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`/`apigeeSearch`) are driven from\r\n * 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 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 vms = useMemo(() => entities.map(toApiCardViewModel), [entities]);\r\n const facets = useMemo(\r\n () => deriveFacets(backendEntities.filter(isApigeeEntity), overrides),\r\n [backendEntities, overrides],\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":"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"}
|
|
1
|
+
{"version":3,"file":"ApigeeApiCatalogPage.esm.js","sources":["../../src/components/ApigeeApiCatalogPage.tsx"],"sourcesContent":["// SPDX-License-Identifier: Apache-2.0\nimport { Content, ContentHeader, Header, 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 { Button, ButtonGroup, makeStyles } from '@material-ui/core';\nimport ViewAgenda from '@material-ui/icons/ViewAgenda';\nimport ViewModule from '@material-ui/icons/ViewModule';\nimport { useEffect, useMemo, useState } from 'react';\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 { 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`/`apigeeSearch`) are driven from\n * 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 apigeeSearch?: ApiSearchFilter;\n};\n\nconst useStyles = makeStyles({\n toggleGroup: {\n marginLeft: 'auto',\n },\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/Grouped 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 classes = useStyles();\n const { entities, backendEntities, loading, error } =\n useEntityList<ApigeeFilters>();\n const { state, inputValue, setInputValue, toggleFacet, clearFacet, 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 vms = useMemo(() => entities.map(toApiCardViewModel), [entities]);\n const facets = useMemo(\n () => deriveFacets(backendEntities.filter(isApigeeEntity), overrides),\n [backendEntities, overrides],\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 return (\n <Page themeId=\"apis\">\n <Header\n title=\"API Catalog\"\n subtitle=\"Browse APIs discovered from Apigee & API Hub\"\n />\n <Content>\n <ContentHeader title={`${vms.length} APIs`}>\n <div\n role=\"group\"\n aria-label=\"API view mode\"\n className={classes.toggleGroup}\n >\n <ButtonGroup size=\"small\">\n <Button\n startIcon={<ViewModule />}\n aria-label=\"Flat grid view\"\n aria-pressed={viewMode === 'flat'}\n color={viewMode === 'flat' ? 'primary' : 'default'}\n onClick={() => setViewMode('flat')}\n >\n Flat\n </Button>\n <Button\n startIcon={<ViewAgenda />}\n aria-label=\"Group by category\"\n aria-pressed={viewMode === 'grouped'}\n color={viewMode === 'grouped' ? 'primary' : 'default'}\n onClick={() => setViewMode('grouped')}\n >\n Grouped\n </Button>\n </ButtonGroup>\n </div>\n </ContentHeader>\n {!loading &&\n !error &&\n backendEntities.filter(isApigeeEntity).length > 0 ? (\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 />\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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"}
|
|
File without changes
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FilterToolbar.esm.js","sources":["../../src/components/FilterToolbar.tsx"],"sourcesContent":["// SPDX-License-Identifier: Apache-2.0\r\nimport { useState, type ReactNode } from 'react';\r\nimport {\r\n Box,\r\n Button,\r\n Checkbox,\r\n Chip,\r\n FormControlLabel,\r\n FormGroup,\r\n Menu,\r\n makeStyles,\r\n} from '@material-ui/core';\r\nimport ArrowDropDown from '@material-ui/icons/ArrowDropDown';\r\nimport Clear from '@material-ui/icons/Clear';\r\nimport type { DerivedFacet, FacetKey } from './facetModel';\r\n\r\nexport interface FilterToolbarProps {\r\n /** Derived facets from `deriveFacets()`; ALL render (revised D-05) — one with no values is disabled. */\r\n facets: DerivedFacet[];\r\n /** Current selections per facet (hydrated from the URL state in 06-04). */\r\n selected: Record<FacetKey, string[]>;\r\n onToggleFacet: (key: FacetKey, value: string) => void;\r\n /** Per-facet menu-footer \"Clear\" (clears just that facet). */\r\n onClearFacet: (key: FacetKey) => void;\r\n /** Toolbar \"Clear all\" (resets every facet + search). */\r\n onClearAll: () => void;\r\n /** `isActive(state)` — gates the \"Clear all\" affordance. */\r\n active: boolean;\r\n /** Filtered result count for the polite live region (UI-07). */\r\n resultCount: number;\r\n /** `SearchBar` mounted inline in the toolbar row. */\r\n searchBar: ReactNode;\r\n}\r\n\r\nconst useStyles = makeStyles(theme => ({\r\n toolbar: {\r\n display: 'flex',\r\n flexWrap: 'wrap',\r\n alignItems: 'center',\r\n gap: theme.spacing(1),\r\n marginBottom: theme.spacing(2),\r\n },\r\n control: {\r\n '&:focus-visible': {\r\n outline: `2px solid ${theme.palette.primary.main}`,\r\n outlineOffset: 2,\r\n },\r\n },\r\n clearAll: {\r\n marginLeft: 'auto',\r\n },\r\n countChip: {\r\n marginLeft: theme.spacing(0.5),\r\n },\r\n formGroup: {\r\n padding: theme.spacing(0, 2),\r\n },\r\n menuFooter: {\r\n display: 'flex',\r\n justifyContent: 'flex-end',\r\n padding: theme.spacing(0.5, 1),\r\n },\r\n optionLabel: {\r\n display: 'flex',\r\n alignItems: 'center',\r\n gap: theme.spacing(0.5),\r\n },\r\n optionIcon: {\r\n fontSize: 16,\r\n },\r\n srOnly: {\r\n position: 'absolute',\r\n width: 1,\r\n height: 1,\r\n padding: 0,\r\n margin: -1,\r\n overflow: 'hidden',\r\n clip: 'rect(0 0 0 0)',\r\n whiteSpace: 'nowrap',\r\n border: 0,\r\n },\r\n}));\r\n\r\n/**\r\n * A single multi-select facet as an outlined menu-button (verified MUI v4 pattern —\r\n * 06-RESEARCH Pattern 7). The `Menu` sets `getContentAnchorEl={null}` so it bottom-anchors,\r\n * and its options are `FormControlLabel`+`Checkbox` inside a `FormGroup` (not clickable menu\r\n * rows) so toggling a checkbox keeps the menu OPEN for multi-select. All dynamic text (`facet.label`,\r\n * `option.label`) renders as React children only — never raw HTML (T-06-03-01).\r\n */\r\nfunction FacetDropdown({\r\n facet,\r\n selected,\r\n onToggle,\r\n onClear,\r\n}: {\r\n facet: DerivedFacet;\r\n selected: string[];\r\n onToggle: (value: string) => void;\r\n onClear: () => void;\r\n}) {\r\n const classes = useStyles();\r\n const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);\r\n const open = Boolean(anchorEl);\r\n const menuId = `facet-menu-${facet.key}`;\r\n const count = selected.length;\r\n\r\n return (\r\n <>\r\n <Button\r\n className={classes.control}\r\n variant=\"outlined\"\r\n size=\"small\"\r\n disabled={!facet.enabled}\r\n endIcon={<ArrowDropDown />}\r\n color={count > 0 ? 'primary' : 'default'}\r\n aria-haspopup=\"true\"\r\n aria-expanded={open}\r\n aria-controls={open ? menuId : undefined}\r\n aria-label={\r\n count > 0\r\n ? `${facet.label} filter, ${count} selected`\r\n : `${facet.label} filter`\r\n }\r\n onClick={event => setAnchorEl(event.currentTarget)}\r\n >\r\n {facet.label}\r\n {count > 0 ? (\r\n <Chip\r\n className={classes.countChip}\r\n size=\"small\"\r\n color=\"primary\"\r\n label={count}\r\n />\r\n ) : null}\r\n </Button>\r\n <Menu\r\n id={menuId}\r\n anchorEl={anchorEl}\r\n open={open}\r\n onClose={() => setAnchorEl(null)}\r\n getContentAnchorEl={null}\r\n anchorOrigin={{ vertical: 'bottom', horizontal: 'left' }}\r\n transformOrigin={{ vertical: 'top', horizontal: 'left' }}\r\n >\r\n <FormGroup\r\n className={classes.formGroup}\r\n role=\"group\"\r\n aria-label={`${facet.label} options`}\r\n >\r\n {facet.options.map(option => {\r\n const OptionIcon = facet.icons?.[option.value];\r\n return (\r\n <FormControlLabel\r\n key={option.value}\r\n control={\r\n <Checkbox\r\n color=\"primary\"\r\n checked={selected.includes(option.value)}\r\n onChange={() => onToggle(option.value)}\r\n />\r\n }\r\n label={\r\n OptionIcon ? (\r\n <span className={classes.optionLabel}>\r\n <OptionIcon\r\n className={classes.optionIcon}\r\n aria-hidden\r\n />\r\n {option.label}\r\n </span>\r\n ) : (\r\n option.label\r\n )\r\n }\r\n />\r\n );\r\n })}\r\n </FormGroup>\r\n {count > 0 ? (\r\n <div className={classes.menuFooter}>\r\n <Button\r\n className={classes.control}\r\n variant=\"text\"\r\n size=\"small\"\r\n onClick={onClear}\r\n >\r\n Clear\r\n </Button>\r\n </div>\r\n ) : null}\r\n </Menu>\r\n </>\r\n );\r\n}\r\n\r\n/**\r\n * Presentational Phase 6 toolbar (D-01 top toolbar, D-02 multi-select dropdowns). Renders EVERY\r\n * facet in `FACET_ORDER` (revised D-05 — a facet with no values renders disabled), the inline\r\n * `searchBar` slot, a conditional right-aligned \"Clear all\", and a polite `aria-live` result-count\r\n * region. Purely props-driven — no data fetching and no URL/provider access (that wiring is 06-04).\r\n * (UI-06, UI-07)\r\n */\r\nexport function FilterToolbar({\r\n facets,\r\n selected,\r\n onToggleFacet,\r\n onClearFacet,\r\n onClearAll,\r\n active,\r\n resultCount,\r\n searchBar,\r\n}: FilterToolbarProps) {\r\n const classes = useStyles();\r\n\r\n return (\r\n <div role=\"search\" aria-label=\"Filter and search APIs\">\r\n <Box className={classes.toolbar}>\r\n {facets.map(facet => (\r\n <FacetDropdown\r\n key={facet.key}\r\n facet={facet}\r\n selected={selected[facet.key]}\r\n onToggle={value => onToggleFacet(facet.key, value)}\r\n onClear={() => onClearFacet(facet.key)}\r\n />\r\n ))}\r\n {searchBar}\r\n {active ? (\r\n <Button\r\n className={`${classes.control} ${classes.clearAll}`}\r\n variant=\"text\"\r\n size=\"small\"\r\n startIcon={<Clear />}\r\n onClick={onClearAll}\r\n >\r\n Clear all\r\n </Button>\r\n ) : null}\r\n </Box>\r\n <span className={classes.srOnly} aria-live=\"polite\">\r\n {`${resultCount} APIs match your filters`}\r\n </span>\r\n </div>\r\n );\r\n}\r\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"FilterToolbar.esm.js","sources":["../../src/components/FilterToolbar.tsx"],"sourcesContent":["// SPDX-License-Identifier: Apache-2.0\nimport { useState, type ReactNode } from 'react';\nimport {\n Box,\n Button,\n Checkbox,\n Chip,\n FormControlLabel,\n FormGroup,\n Menu,\n makeStyles,\n} from '@material-ui/core';\nimport ArrowDropDown from '@material-ui/icons/ArrowDropDown';\nimport Clear from '@material-ui/icons/Clear';\nimport type { DerivedFacet, FacetKey } from './facetModel';\n\nexport interface FilterToolbarProps {\n /** Derived facets from `deriveFacets()`; ALL render (revised D-05) — one with no values is disabled. */\n facets: DerivedFacet[];\n /** Current selections per facet (hydrated from the URL state in 06-04). */\n selected: Record<FacetKey, string[]>;\n onToggleFacet: (key: FacetKey, value: string) => void;\n /** Per-facet menu-footer \"Clear\" (clears just that facet). */\n onClearFacet: (key: FacetKey) => void;\n /** Toolbar \"Clear all\" (resets every facet + search). */\n onClearAll: () => void;\n /** `isActive(state)` — gates the \"Clear all\" affordance. */\n active: boolean;\n /** Filtered result count for the polite live region (UI-07). */\n resultCount: number;\n /** `SearchBar` mounted inline in the toolbar row. */\n searchBar: ReactNode;\n}\n\nconst useStyles = makeStyles(theme => ({\n toolbar: {\n display: 'flex',\n flexWrap: 'wrap',\n alignItems: 'center',\n gap: theme.spacing(1),\n marginBottom: theme.spacing(2),\n },\n control: {\n '&:focus-visible': {\n outline: `2px solid ${theme.palette.primary.main}`,\n outlineOffset: 2,\n },\n },\n clearAll: {\n marginLeft: 'auto',\n },\n countChip: {\n marginLeft: theme.spacing(0.5),\n },\n formGroup: {\n padding: theme.spacing(0, 2),\n },\n menuFooter: {\n display: 'flex',\n justifyContent: 'flex-end',\n padding: theme.spacing(0.5, 1),\n },\n optionLabel: {\n display: 'flex',\n alignItems: 'center',\n gap: theme.spacing(0.5),\n },\n optionIcon: {\n fontSize: 16,\n },\n srOnly: {\n position: 'absolute',\n width: 1,\n height: 1,\n padding: 0,\n margin: -1,\n overflow: 'hidden',\n clip: 'rect(0 0 0 0)',\n whiteSpace: 'nowrap',\n border: 0,\n },\n}));\n\n/**\n * A single multi-select facet as an outlined menu-button (verified MUI v4 pattern —\n * 06-RESEARCH Pattern 7). The `Menu` sets `getContentAnchorEl={null}` so it bottom-anchors,\n * and its options are `FormControlLabel`+`Checkbox` inside a `FormGroup` (not clickable menu\n * rows) so toggling a checkbox keeps the menu OPEN for multi-select. All dynamic text (`facet.label`,\n * `option.label`) renders as React children only — never raw HTML (T-06-03-01).\n */\nfunction FacetDropdown({\n facet,\n selected,\n onToggle,\n onClear,\n}: {\n facet: DerivedFacet;\n selected: string[];\n onToggle: (value: string) => void;\n onClear: () => void;\n}) {\n const classes = useStyles();\n const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);\n const open = Boolean(anchorEl);\n const menuId = `facet-menu-${facet.key}`;\n const count = selected.length;\n\n return (\n <>\n <Button\n className={classes.control}\n variant=\"outlined\"\n size=\"small\"\n disabled={!facet.enabled}\n endIcon={<ArrowDropDown />}\n color={count > 0 ? 'primary' : 'default'}\n aria-haspopup=\"true\"\n aria-expanded={open}\n aria-controls={open ? menuId : undefined}\n aria-label={\n count > 0\n ? `${facet.label} filter, ${count} selected`\n : `${facet.label} filter`\n }\n onClick={event => setAnchorEl(event.currentTarget)}\n >\n {facet.label}\n {count > 0 ? (\n <Chip\n className={classes.countChip}\n size=\"small\"\n color=\"primary\"\n label={count}\n />\n ) : null}\n </Button>\n <Menu\n id={menuId}\n anchorEl={anchorEl}\n open={open}\n onClose={() => setAnchorEl(null)}\n getContentAnchorEl={null}\n anchorOrigin={{ vertical: 'bottom', horizontal: 'left' }}\n transformOrigin={{ vertical: 'top', horizontal: 'left' }}\n >\n <FormGroup\n className={classes.formGroup}\n role=\"group\"\n aria-label={`${facet.label} options`}\n >\n {facet.options.map(option => {\n const OptionIcon = facet.icons?.[option.value];\n return (\n <FormControlLabel\n key={option.value}\n control={\n <Checkbox\n color=\"primary\"\n checked={selected.includes(option.value)}\n onChange={() => onToggle(option.value)}\n />\n }\n label={\n OptionIcon ? (\n <span className={classes.optionLabel}>\n <OptionIcon\n className={classes.optionIcon}\n aria-hidden\n />\n {option.label}\n </span>\n ) : (\n option.label\n )\n }\n />\n );\n })}\n </FormGroup>\n {count > 0 ? (\n <div className={classes.menuFooter}>\n <Button\n className={classes.control}\n variant=\"text\"\n size=\"small\"\n onClick={onClear}\n >\n Clear\n </Button>\n </div>\n ) : null}\n </Menu>\n </>\n );\n}\n\n/**\n * Presentational Phase 6 toolbar (D-01 top toolbar, D-02 multi-select dropdowns). Renders EVERY\n * facet in `FACET_ORDER` (revised D-05 — a facet with no values renders disabled), the inline\n * `searchBar` slot, a conditional right-aligned \"Clear all\", and a polite `aria-live` result-count\n * region. Purely props-driven — no data fetching and no URL/provider access (that wiring is 06-04).\n * (UI-06, UI-07)\n */\nexport function FilterToolbar({\n facets,\n selected,\n onToggleFacet,\n onClearFacet,\n onClearAll,\n active,\n resultCount,\n searchBar,\n}: FilterToolbarProps) {\n const classes = useStyles();\n\n return (\n <div role=\"search\" aria-label=\"Filter and search APIs\">\n <Box className={classes.toolbar}>\n {facets.map(facet => (\n <FacetDropdown\n key={facet.key}\n facet={facet}\n selected={selected[facet.key]}\n onToggle={value => onToggleFacet(facet.key, value)}\n onClear={() => onClearFacet(facet.key)}\n />\n ))}\n {searchBar}\n {active ? (\n <Button\n className={`${classes.control} ${classes.clearAll}`}\n variant=\"text\"\n size=\"small\"\n startIcon={<Clear />}\n onClick={onClearAll}\n >\n Clear all\n </Button>\n ) : null}\n </Box>\n <span className={classes.srOnly} aria-live=\"polite\">\n {`${resultCount} APIs match your filters`}\n </span>\n </div>\n );\n}\n"],"names":[],"mappings":"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"}
|
|
File without changes
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SearchBar.esm.js","sources":["../../src/components/SearchBar.tsx"],"sourcesContent":["// SPDX-License-Identifier: Apache-2.0\
|
|
1
|
+
{"version":3,"file":"SearchBar.esm.js","sources":["../../src/components/SearchBar.tsx"],"sourcesContent":["// SPDX-License-Identifier: Apache-2.0\nimport {\n IconButton,\n InputAdornment,\n TextField,\n makeStyles,\n} from '@material-ui/core';\nimport Clear from '@material-ui/icons/Clear';\nimport Search from '@material-ui/icons/Search';\n\nexport interface SearchBarProps {\n /** Controlled input value — echoes keystrokes instantly (no debounce here). */\n value: string;\n /** Reports every change; the 200 ms trailing debounce lives in `useApigeeFilters` (06-04). */\n onChange: (next: string) => void;\n}\n\nconst useStyles = makeStyles(theme => ({\n field: {\n minWidth: 240,\n flex: '1 1 auto',\n '& input:focus-visible': {\n outline: `2px solid ${theme.palette.primary.main}`,\n outlineOffset: 2,\n },\n },\n searchIcon: {\n color: theme.palette.text.secondary,\n },\n}));\n\n/**\n * Stateless, controlled outlined search field (UI-SPEC §Component Inventory #2). Holds NO state\n * and NO debounce — it purely echoes `value` and reports changes; the 200 ms trailing debounce\n * lives in the `useApigeeFilters` hook (06-04). Leading search icon, and a Clear\n * (✕) button shown only when `value` is non-empty. No spinner (UI-SPEC Assumption 9). The `value`\n * renders only as a controlled prop (React auto-escapes) — never raw HTML (T-06-03-01).\n */\nexport function SearchBar({ value, onChange }: SearchBarProps) {\n const classes = useStyles();\n\n return (\n <TextField\n className={classes.field}\n variant=\"outlined\"\n size=\"small\"\n placeholder=\"Search APIs…\"\n value={value}\n onChange={event => onChange(event.target.value)}\n inputProps={{ 'aria-label': 'Search APIs' }}\n InputProps={{\n startAdornment: (\n <InputAdornment position=\"start\">\n <Search className={classes.searchIcon} />\n </InputAdornment>\n ),\n endAdornment: value ? (\n <InputAdornment position=\"end\">\n <IconButton\n size=\"small\"\n aria-label=\"Clear search\"\n onClick={() => onChange('')}\n >\n <Clear />\n </IconButton>\n </InputAdornment>\n ) : undefined,\n }}\n />\n );\n}\n"],"names":[],"mappings":";;;;;AAiBA,MAAM,SAAA,GAAY,WAAW,CAAA,KAAA,MAAU;AAAA,EACrC,KAAA,EAAO;AAAA,IACL,QAAA,EAAU,GAAA;AAAA,IACV,IAAA,EAAM,UAAA;AAAA,IACN,uBAAA,EAAyB;AAAA,MACvB,OAAA,EAAS,CAAA,UAAA,EAAa,KAAA,CAAM,OAAA,CAAQ,QAAQ,IAAI,CAAA,CAAA;AAAA,MAChD,aAAA,EAAe;AAAA;AACjB,GACF;AAAA,EACA,UAAA,EAAY;AAAA,IACV,KAAA,EAAO,KAAA,CAAM,OAAA,CAAQ,IAAA,CAAK;AAAA;AAE9B,CAAA,CAAE,CAAA;AASK,SAAS,SAAA,CAAU,EAAE,KAAA,EAAO,QAAA,EAAS,EAAmB;AAC7D,EAAA,MAAM,UAAU,SAAA,EAAU;AAE1B,EAAA,uBACE,GAAA;AAAA,IAAC,SAAA;AAAA,IAAA;AAAA,MACC,WAAW,OAAA,CAAQ,KAAA;AAAA,MACnB,OAAA,EAAQ,UAAA;AAAA,MACR,IAAA,EAAK,OAAA;AAAA,MACL,WAAA,EAAY,mBAAA;AAAA,MACZ,KAAA;AAAA,MACA,QAAA,EAAU,CAAA,KAAA,KAAS,QAAA,CAAS,KAAA,CAAM,OAAO,KAAK,CAAA;AAAA,MAC9C,UAAA,EAAY,EAAE,YAAA,EAAc,aAAA,EAAc;AAAA,MAC1C,UAAA,EAAY;AAAA,QACV,cAAA,kBACE,GAAA,CAAC,cAAA,EAAA,EAAe,QAAA,EAAS,OAAA,EACvB,8BAAC,MAAA,EAAA,EAAO,SAAA,EAAW,OAAA,CAAQ,UAAA,EAAY,CAAA,EACzC,CAAA;AAAA,QAEF,YAAA,EAAc,KAAA,mBACZ,GAAA,CAAC,cAAA,EAAA,EAAe,UAAS,KAAA,EACvB,QAAA,kBAAA,GAAA;AAAA,UAAC,UAAA;AAAA,UAAA;AAAA,YACC,IAAA,EAAK,OAAA;AAAA,YACL,YAAA,EAAW,cAAA;AAAA,YACX,OAAA,EAAS,MAAM,QAAA,CAAS,EAAE,CAAA;AAAA,YAE1B,8BAAC,KAAA,EAAA,EAAM;AAAA;AAAA,WAEX,CAAA,GACE;AAAA;AACN;AAAA,GACF;AAEJ;;;;"}
|
|
File without changes
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"apiCardViewModel.esm.js","sources":["../../src/components/apiCardViewModel.ts"],"sourcesContent":["// SPDX-License-Identifier: Apache-2.0\
|
|
1
|
+
{"version":3,"file":"apiCardViewModel.esm.js","sources":["../../src/components/apiCardViewModel.ts"],"sourcesContent":["// SPDX-License-Identifier: Apache-2.0\nimport { type Entity, parseEntityRef } from '@backstage/catalog-model';\nimport { humanizeEntityRef } from '@backstage/plugin-catalog-react';\nimport {\n ANNOTATION_HAS_SPEC,\n ANNOTATION_CATEGORY,\n ANNOTATION_API_HUB_VERSION,\n} from '../constants';\n\nexport type LifecycleBadgeKind = 'ok' | 'warning' | 'error';\n\nexport interface ApiCardViewModel {\n /** metadata.title ?? metadata.name */\n name: string;\n /** humanizeEntityRef(spec.owner, { defaultKind: 'group' }) — 'unknown' when absent */\n ownerLabel: string;\n badge: { label: string; kind: LifecycleBadgeKind };\n /** annotations['apigee.com/has-spec'] === 'true' */\n hasSpec: boolean;\n /** annotations['apigee.com/api-hub-version'] — omitted when absent (D-05) */\n version?: string;\n /** first comma-separated slug of apigee.com/category — undefined => 'Other' bucket */\n category?: string;\n /** metadata.description — omitted when empty */\n description?: string;\n /** for entityRouteRef navigation (05-04) */\n targetRef: { kind: 'API'; namespace: string; name: string };\n}\n\n/** D-04 lifecycle -> badge mapping. Unknown/undeployed/missing default to warning. */\nfunction toBadge(lifecycle: string | undefined): {\n label: string;\n kind: LifecycleBadgeKind;\n} {\n switch (lifecycle) {\n case 'production':\n return { label: 'Production', kind: 'ok' };\n case 'deprecated':\n return { label: 'Deprecated', kind: 'error' };\n case 'experimental':\n return { label: 'Experimental', kind: 'warning' };\n default:\n return { label: 'Unknown', kind: 'warning' };\n }\n}\n\n/** PURE mapper: an Apigee `kind: API` entity -> the view-model each tile renders. */\nexport function toApiCardViewModel(entity: Entity): ApiCardViewModel {\n const annotations = entity.metadata.annotations ?? {};\n const owner = entity.spec?.owner as string | undefined;\n const description = entity.metadata.description?.trim()\n ? entity.metadata.description\n : undefined;\n\n return {\n name: entity.metadata.title ?? entity.metadata.name,\n ownerLabel: owner\n ? humanizeEntityRef(\n parseEntityRef(owner, { defaultKind: 'group', defaultNamespace: 'default' }),\n { defaultKind: 'group' },\n )\n : 'unknown',\n badge: toBadge(entity.spec?.lifecycle as string | undefined),\n hasSpec: annotations[ANNOTATION_HAS_SPEC] === 'true',\n version: annotations[ANNOTATION_API_HUB_VERSION] || undefined,\n category: annotations[ANNOTATION_CATEGORY]?.split(',')[0]?.trim() || undefined,\n description,\n targetRef: {\n kind: 'API',\n namespace: entity.metadata.namespace ?? 'default',\n name: entity.metadata.name,\n },\n };\n}\n"],"names":[],"mappings":";;;;AA8BA,SAAS,QAAQ,SAAA,EAGf;AACA,EAAA,QAAQ,SAAA;AAAW,IACjB,KAAK,YAAA;AACH,MAAA,OAAO,EAAE,KAAA,EAAO,YAAA,EAAc,IAAA,EAAM,IAAA,EAAK;AAAA,IAC3C,KAAK,YAAA;AACH,MAAA,OAAO,EAAE,KAAA,EAAO,YAAA,EAAc,IAAA,EAAM,OAAA,EAAQ;AAAA,IAC9C,KAAK,cAAA;AACH,MAAA,OAAO,EAAE,KAAA,EAAO,cAAA,EAAgB,IAAA,EAAM,SAAA,EAAU;AAAA,IAClD;AACE,MAAA,OAAO,EAAE,KAAA,EAAO,SAAA,EAAW,IAAA,EAAM,SAAA,EAAU;AAAA;AAEjD;AAGO,SAAS,mBAAmB,MAAA,EAAkC;AACnE,EAAA,MAAM,WAAA,GAAc,MAAA,CAAO,QAAA,CAAS,WAAA,IAAe,EAAC;AACpD,EAAA,MAAM,KAAA,GAAQ,OAAO,IAAA,EAAM,KAAA;AAC3B,EAAA,MAAM,WAAA,GAAc,OAAO,QAAA,CAAS,WAAA,EAAa,MAAK,GAClD,MAAA,CAAO,SAAS,WAAA,GAChB,MAAA;AAEJ,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,MAAA,CAAO,QAAA,CAAS,KAAA,IAAS,OAAO,QAAA,CAAS,IAAA;AAAA,IAC/C,YAAY,KAAA,GACR,iBAAA;AAAA,MACE,eAAe,KAAA,EAAO,EAAE,aAAa,OAAA,EAAS,gBAAA,EAAkB,WAAW,CAAA;AAAA,MAC3E,EAAE,aAAa,OAAA;AAAQ,KACzB,GACA,SAAA;AAAA,IACJ,KAAA,EAAO,OAAA,CAAQ,MAAA,CAAO,IAAA,EAAM,SAA+B,CAAA;AAAA,IAC3D,OAAA,EAAS,WAAA,CAAY,mBAAmB,CAAA,KAAM,MAAA;AAAA,IAC9C,OAAA,EAAS,WAAA,CAAY,0BAA0B,CAAA,IAAK,MAAA;AAAA,IACpD,QAAA,EAAU,WAAA,CAAY,mBAAmB,CAAA,EAAG,KAAA,CAAM,GAAG,CAAA,CAAE,CAAC,CAAA,EAAG,IAAA,EAAK,IAAK,MAAA;AAAA,IACrE,WAAA;AAAA,IACA,SAAA,EAAW;AAAA,MACT,IAAA,EAAM,KAAA;AAAA,MACN,SAAA,EAAW,MAAA,CAAO,QAAA,CAAS,SAAA,IAAa,SAAA;AAAA,MACxC,IAAA,EAAM,OAAO,QAAA,CAAS;AAAA;AACxB,GACF;AACF;;;;"}
|
|
File without changes
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"apigeeFacetFilters.esm.js","sources":["../../src/components/apigeeFacetFilters.ts"],"sourcesContent":["// SPDX-License-Identifier: Apache-2.0\
|
|
1
|
+
{"version":3,"file":"apigeeFacetFilters.esm.js","sources":["../../src/components/apigeeFacetFilters.ts"],"sourcesContent":["// SPDX-License-Identifier: Apache-2.0\nimport { type Entity, parseEntityRef } from '@backstage/catalog-model';\nimport type { EntityFilter } from '@backstage/plugin-catalog-react';\nimport { humanizeEntityRef } from '@backstage/plugin-catalog-react';\nimport {\n ANNOTATION_APIGEE_ORGS,\n ANNOTATION_APIGEE_ORG,\n ANNOTATION_CATEGORY,\n} from '../constants';\n\n/** Dual-key is-Apigee gate (same rule as the existing ApigeeOrgFilter). */\nexport function isApigeeEntity(e: Entity): boolean {\n const a = e.metadata.annotations ?? {};\n return ANNOTATION_APIGEE_ORGS in a || ANNOTATION_APIGEE_ORG in a;\n}\n\n// --- Pure field extractors: the ONE place each facet's value is defined. Reused by\n// deriveFacets(), the facet predicates, and the search haystack. Unit-testable. ---\nexport const entityLifecycle = (e: Entity): string | undefined =>\n (e.spec?.lifecycle as string | undefined)?.trim() || undefined;\n\nexport const entityOwnerLabel = (e: Entity): string | undefined => {\n const owner = e.spec?.owner as string | undefined;\n return owner\n ? humanizeEntityRef(\n parseEntityRef(owner, { defaultKind: 'group', defaultNamespace: 'default' }),\n { defaultKind: 'group' },\n ) // 'group:default/payments-team' -> 'payments-team'\n : undefined;\n};\n\nexport const entityOrg = (e: Entity): string | undefined => {\n const a = e.metadata.annotations ?? {};\n return (\n (a[ANNOTATION_APIGEE_ORGS] ?? a[ANNOTATION_APIGEE_ORG])?.split(',')[0]?.trim() ||\n undefined\n );\n};\n\nexport const entityCategory = (e: Entity): string | undefined =>\n (e.metadata.annotations ?? {})[ANNOTATION_CATEGORY]?.split(',')[0]?.trim() ||\n undefined;\n\n/**\n * Facet-only category value: maps an uncategorised entity to the 'other' bucket\n * (LOCKED UI-SPEC Facet Data). Search uses `entityCategory` (the REAL slug) instead,\n * so a search for \"other\" does not match uncategorised APIs.\n */\nexport const entityCategoryFacet = (e: Entity): string =>\n entityCategory(e) ?? 'other';\n\n/** Generic single-field multi-value facet. Within-facet OR. No URL seam / getCatalogFilters. */\nexport class FieldFacetFilter implements EntityFilter {\n constructor(\n private readonly extract: (e: Entity) => string | undefined,\n public readonly values: string[],\n ) {}\n filterEntity(entity: Entity): boolean {\n const v = this.extract(entity);\n return v !== undefined && this.values.includes(v); // OR across selected values\n }\n}\n\n/** Broad \"find anything\" search: name + description + owner + org + category (D-09). */\nexport function apiSearchHaystack(e: Entity): string {\n return [\n e.metadata.title ?? e.metadata.name,\n e.metadata.description ?? '',\n entityOwnerLabel(e) ?? '',\n entityOrg(e) ?? '',\n entityCategory(e) ?? '',\n ]\n .join(' ')\n .toLowerCase();\n}\n\nexport class ApiSearchFilter implements EntityFilter {\n private readonly needle: string;\n constructor(query: string) {\n this.needle = query.trim().toLowerCase();\n }\n filterEntity(entity: Entity): boolean {\n // String.includes ONLY — never build a RegExp from user input (ReDoS guard).\n return this.needle === '' || apiSearchHaystack(entity).includes(this.needle);\n }\n}\n\n/**\n * Replaces EntityKindFilter('api','API'): keeps the backend kind=api filter but exposes no\n * URL seam, so the provider does not serialize `filters[kind]=api` into the URL (D-06).\n */\nexport class ApiKindFilter implements EntityFilter {\n readonly value = 'api';\n getCatalogFilters(): Record<string, string> {\n return { kind: this.value };\n }\n}\n"],"names":[],"mappings":";;;;AAWO,SAAS,eAAe,CAAA,EAAoB;AACjD,EAAA,MAAM,CAAA,GAAI,CAAA,CAAE,QAAA,CAAS,WAAA,IAAe,EAAC;AACrC,EAAA,OAAO,sBAAA,IAA0B,KAAK,qBAAA,IAAyB,CAAA;AACjE;AAIO,MAAM,kBAAkB,CAAC,CAAA,KAC7B,EAAE,IAAA,EAAM,SAAA,EAAkC,MAAK,IAAK;AAEhD,MAAM,gBAAA,GAAmB,CAAC,CAAA,KAAkC;AACjE,EAAA,MAAM,KAAA,GAAQ,EAAE,IAAA,EAAM,KAAA;AACtB,EAAA,OAAO,KAAA,GACH,iBAAA;AAAA,IACE,eAAe,KAAA,EAAO,EAAE,aAAa,OAAA,EAAS,gBAAA,EAAkB,WAAW,CAAA;AAAA,IAC3E,EAAE,aAAa,OAAA;AAAQ,GACzB,GACA,MAAA;AACN;AAEO,MAAM,SAAA,GAAY,CAAC,CAAA,KAAkC;AAC1D,EAAA,MAAM,CAAA,GAAI,CAAA,CAAE,QAAA,CAAS,WAAA,IAAe,EAAC;AACrC,EAAA,OAAA,CACG,CAAA,CAAE,sBAAsB,CAAA,IAAK,CAAA,CAAE,qBAAqB,CAAA,GAAI,KAAA,CAAM,GAAG,CAAA,CAAE,CAAC,CAAA,EAAG,IAAA,EAAK,IAC7E,MAAA;AAEJ;AAEO,MAAM,iBAAiB,CAAC,CAAA,KAAA,CAC5B,CAAA,CAAE,QAAA,CAAS,eAAe,EAAC,EAAG,mBAAmB,CAAA,EAAG,MAAM,GAAG,CAAA,CAAE,CAAC,CAAA,EAAG,MAAK,IACzE;AAOK,MAAM,mBAAA,GAAsB,CAAC,CAAA,KAClC,cAAA,CAAe,CAAC,CAAA,IAAK;AAGhB,MAAM,gBAAA,CAAyC;AAAA,EACpD,WAAA,CACmB,SACD,MAAA,EAChB;AAFiB,IAAA,IAAA,CAAA,OAAA,GAAA,OAAA;AACD,IAAA,IAAA,CAAA,MAAA,GAAA,MAAA;AAAA,EACf;AAAA,EAFgB,OAAA;AAAA,EACD,MAAA;AAAA,EAElB,aAAa,MAAA,EAAyB;AACpC,IAAA,MAAM,CAAA,GAAI,IAAA,CAAK,OAAA,CAAQ,MAAM,CAAA;AAC7B,IAAA,OAAO,CAAA,KAAM,MAAA,IAAa,IAAA,CAAK,MAAA,CAAO,SAAS,CAAC,CAAA;AAAA,EAClD;AACF;AAGO,SAAS,kBAAkB,CAAA,EAAmB;AACnD,EAAA,OAAO;AAAA,IACL,CAAA,CAAE,QAAA,CAAS,KAAA,IAAS,CAAA,CAAE,QAAA,CAAS,IAAA;AAAA,IAC/B,CAAA,CAAE,SAAS,WAAA,IAAe,EAAA;AAAA,IAC1B,gBAAA,CAAiB,CAAC,CAAA,IAAK,EAAA;AAAA,IACvB,SAAA,CAAU,CAAC,CAAA,IAAK,EAAA;AAAA,IAChB,cAAA,CAAe,CAAC,CAAA,IAAK;AAAA,GACvB,CACG,IAAA,CAAK,GAAG,CAAA,CACR,WAAA,EAAY;AACjB;AAEO,MAAM,eAAA,CAAwC;AAAA,EAClC,MAAA;AAAA,EACjB,YAAY,KAAA,EAAe;AACzB,IAAA,IAAA,CAAK,MAAA,GAAS,KAAA,CAAM,IAAA,EAAK,CAAE,WAAA,EAAY;AAAA,EACzC;AAAA,EACA,aAAa,MAAA,EAAyB;AAEpC,IAAA,OAAO,IAAA,CAAK,WAAW,EAAA,IAAM,iBAAA,CAAkB,MAAM,CAAA,CAAE,QAAA,CAAS,KAAK,MAAM,CAAA;AAAA,EAC7E;AACF;AAMO,MAAM,aAAA,CAAsC;AAAA,EACxC,KAAA,GAAQ,KAAA;AAAA,EACjB,iBAAA,GAA4C;AAC1C,IAAA,OAAO,EAAE,IAAA,EAAM,IAAA,CAAK,KAAA,EAAM;AAAA,EAC5B;AACF;;;;"}
|
|
File without changes
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"apigeeOrgFilter.esm.js","sources":["../../src/components/apigeeOrgFilter.ts"],"sourcesContent":["// SPDX-License-Identifier: Apache-2.0\
|
|
1
|
+
{"version":3,"file":"apigeeOrgFilter.esm.js","sources":["../../src/components/apigeeOrgFilter.ts"],"sourcesContent":["// SPDX-License-Identifier: Apache-2.0\nimport type { Entity } from '@backstage/catalog-model';\nimport type { EntityFilter } from '@backstage/plugin-catalog-react';\nimport { ANNOTATION_APIGEE_ORGS, ANNOTATION_APIGEE_ORG } from '../constants';\n\n/**\n * Frontend predicate that keeps an entity when it carries EITHER Apigee org key.\n * MUST match both: proxy-derived APIs use the plural key, hub-only APIs the singular\n * (RESEARCH Pitfall 1 / CAT-05). Uses filterEntity (not getCatalogFilters) because a\n * single backend filter record cannot OR two different annotation keys.\n */\nexport class ApigeeOrgFilter implements EntityFilter {\n filterEntity(entity: Entity): boolean {\n const a = entity.metadata.annotations ?? {};\n return ANNOTATION_APIGEE_ORGS in a || ANNOTATION_APIGEE_ORG in a;\n }\n}\n"],"names":[],"mappings":";;AAWO,MAAM,eAAA,CAAwC;AAAA,EACnD,aAAa,MAAA,EAAyB;AACpC,IAAA,MAAM,CAAA,GAAI,MAAA,CAAO,QAAA,CAAS,WAAA,IAAe,EAAC;AAC1C,IAAA,OAAO,sBAAA,IAA0B,KAAK,qBAAA,IAAyB,CAAA;AAAA,EACjE;AACF;;;;"}
|
|
File without changes
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"apigeeSearchParams.esm.js","sources":["../../src/components/apigeeSearchParams.ts"],"sourcesContent":["// SPDX-License-Identifier: Apache-2.0\
|
|
1
|
+
{"version":3,"file":"apigeeSearchParams.esm.js","sources":["../../src/components/apigeeSearchParams.ts"],"sourcesContent":["// SPDX-License-Identifier: Apache-2.0\nimport { FACET_ORDER, type FacetKey } from './facetModel';\n\nexport interface ApigeeFilterState {\n selected: Record<FacetKey, string[]>;\n q: string;\n}\n\n/** Clean URL keys (D-06): one query key per facet, no native `filters[...]` wrapper. */\nconst QKEY: Record<FacetKey, string> = {\n lifecycle: 'lifecycle',\n org: 'org',\n owner: 'owner',\n category: 'category',\n};\n\n/**\n * URL → state. Repeated keys become arrays (D-07) via `getAll`; unknown/extra keys are\n * ignored. Values are treated as opaque strings — no eval, RegExp, or HTML (T-06-02-01);\n * an unknown facet value simply matches nothing downstream (derived allow-list).\n */\nexport function parseSearchParams(sp: URLSearchParams): ApigeeFilterState {\n const selected = Object.fromEntries(\n FACET_ORDER.map(k => [k, sp.getAll(QKEY[k])]),\n ) as Record<FacetKey, string[]>;\n return { selected, q: sp.get('q') ?? '' };\n}\n\n/**\n * state → URL. Deterministic order (FACET_ORDER, then `q` last) reproduces the exact SC #3\n * shape `?lifecycle=deprecated&q=payment`. Only the known clean keys are appended and empties\n * are omitted (T-06-02-02) — no injection surface. Multi-value facets serialize as repeated\n * keys (D-07) via `append`.\n */\nexport function buildSearchParams(state: ApigeeFilterState): URLSearchParams {\n const sp = new URLSearchParams();\n for (const k of FACET_ORDER) {\n for (const v of state.selected[k]) {\n sp.append(QKEY[k], v);\n }\n }\n if (state.q.trim()) {\n sp.set('q', state.q.trim());\n }\n return sp;\n}\n\n/** True when any facet is selected or the search box holds a non-blank query. */\nexport function isActive(state: ApigeeFilterState): boolean {\n return (\n state.q.trim() !== '' || FACET_ORDER.some(k => state.selected[k].length > 0)\n );\n}\n"],"names":[],"mappings":";;AASA,MAAM,IAAA,GAAiC;AAAA,EACrC,SAAA,EAAW,WAAA;AAAA,EACX,GAAA,EAAK,KAAA;AAAA,EACL,KAAA,EAAO,OAAA;AAAA,EACP,QAAA,EAAU;AACZ,CAAA;AAOO,SAAS,kBAAkB,EAAA,EAAwC;AACxE,EAAA,MAAM,WAAW,MAAA,CAAO,WAAA;AAAA,IACtB,WAAA,CAAY,GAAA,CAAI,CAAA,CAAA,KAAK,CAAC,CAAA,EAAG,EAAA,CAAG,MAAA,CAAO,IAAA,CAAK,CAAC,CAAC,CAAC,CAAC;AAAA,GAC9C;AACA,EAAA,OAAO,EAAE,QAAA,EAAU,CAAA,EAAG,GAAG,GAAA,CAAI,GAAG,KAAK,EAAA,EAAG;AAC1C;AAsBO,SAAS,SAAS,KAAA,EAAmC;AAC1D,EAAA,OACE,KAAA,CAAM,CAAA,CAAE,IAAA,EAAK,KAAM,EAAA,IAAM,WAAA,CAAY,IAAA,CAAK,CAAA,CAAA,KAAK,KAAA,CAAM,QAAA,CAAS,CAAC,CAAA,CAAE,SAAS,CAAC,CAAA;AAE/E;;;;"}
|
|
File without changes
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"categoryIcons.esm.js","sources":["../../src/components/categoryIcons.tsx"],"sourcesContent":["// SPDX-License-Identifier: Apache-2.0\
|
|
1
|
+
{"version":3,"file":"categoryIcons.esm.js","sources":["../../src/components/categoryIcons.tsx"],"sourcesContent":["// SPDX-License-Identifier: Apache-2.0\nimport type { SvgIconComponent } from '@material-ui/icons';\nimport Category from '@material-ui/icons/Category';\nimport Security from '@material-ui/icons/Security';\nimport Payment from '@material-ui/icons/Payment';\nimport People from '@material-ui/icons/People';\nimport ShoppingCart from '@material-ui/icons/ShoppingCart';\nimport Notifications from '@material-ui/icons/Notifications';\nimport Assessment from '@material-ui/icons/Assessment';\nimport SettingsEthernet from '@material-ui/icons/SettingsEthernet';\nimport Devices from '@material-ui/icons/Devices';\nimport Tv from '@material-ui/icons/Tv';\nimport Place from '@material-ui/icons/Place';\n\nexport type IconKey =\n | 'Category'\n | 'Security'\n | 'Payment'\n | 'People'\n | 'ShoppingCart'\n | 'Notifications'\n | 'Assessment'\n | 'SettingsEthernet'\n | 'Devices'\n | 'Tv'\n | 'Place';\n\n/**\n * Fixed allow-list registry. A config override may ONLY select one of these keys —\n * a config value is never a module path or an arbitrary component (T-05-01).\n */\nexport const ICONS: Record<IconKey, SvgIconComponent> = {\n Category,\n Security,\n Payment,\n People,\n ShoppingCart,\n Notifications,\n Assessment,\n SettingsEthernet,\n Devices,\n Tv,\n Place,\n};\n\n/** Default category-slug -> IconKey substring map (D-08, case-insensitive). */\nconst DEFAULT_MAP: ReadonlyArray<{ match: RegExp; key: IconKey }> = [\n { match: /security|auth|identity|oauth/i, key: 'Security' },\n { match: /payment|billing|finance|fino/i, key: 'Payment' },\n { match: /customer|crm|people|partner/i, key: 'People' },\n { match: /order|commerce|catalog|product/i, key: 'ShoppingCart' },\n { match: /communication|messaging|notification/i, key: 'Notifications' },\n { match: /data|analytics|reporting/i, key: 'Assessment' },\n { match: /network|integration|common|infra/i, key: 'SettingsEthernet' },\n { match: /device|iot/i, key: 'Devices' },\n { match: /media|tv|content/i, key: 'Tv' },\n { match: /location|geo|gis/i, key: 'Place' },\n];\n\n/** Type guard: is `v` an OWN key of the static ICONS allow-list? */\nfunction isIconKey(v: string): v is IconKey {\n return Object.prototype.hasOwnProperty.call(ICONS, v);\n}\n\n/**\n * Resolve a category slug to a Material icon component.\n *\n * A config `overrides` value may only select an allow-listed IconKey; any other value\n * (unknown key, module path, `constructor`, etc.) falls back to `Category` and warns\n * once. This never `import()`s a config path or renders an arbitrary component — it is\n * the primary mitigation for the config-driven component-injection threat (T-05-01).\n */\nexport function resolveCategoryIcon(\n slug: string | undefined,\n overrides?: Record<string, string>,\n): SvgIconComponent {\n if (slug && overrides && Object.prototype.hasOwnProperty.call(overrides, slug)) {\n const value = overrides[slug];\n if (isIconKey(value)) {\n return ICONS[value];\n }\n // eslint-disable-next-line no-console\n console.warn(\n '[apigee] Ignoring unknown categoryIcons value \"%s\" for \"%s\"; using Category',\n value,\n slug,\n );\n }\n\n if (slug) {\n const match = DEFAULT_MAP.find(entry => entry.match.test(slug));\n if (match) {\n return ICONS[match.key];\n }\n }\n\n return ICONS.Category;\n}\n"],"names":[],"mappings":";;;;;;;;;;;;AA+BO,MAAM,KAAA,GAA2C;AAAA,EACtD,QAAA;AAAA,EACA,QAAA;AAAA,EACA,OAAA;AAAA,EACA,MAAA;AAAA,EACA,YAAA;AAAA,EACA,aAAA;AAAA,EACA,UAAA;AAAA,EACA,gBAAA;AAAA,EACA,OAAA;AAAA,EACA,EAAA;AAAA,EACA;AACF;AAGA,MAAM,WAAA,GAA8D;AAAA,EAClE,EAAE,KAAA,EAAO,+BAAA,EAAiC,GAAA,EAAK,UAAA,EAAW;AAAA,EAC1D,EAAE,KAAA,EAAO,+BAAA,EAAiC,GAAA,EAAK,SAAA,EAAU;AAAA,EACzD,EAAE,KAAA,EAAO,8BAAA,EAAgC,GAAA,EAAK,QAAA,EAAS;AAAA,EACvD,EAAE,KAAA,EAAO,iCAAA,EAAmC,GAAA,EAAK,cAAA,EAAe;AAAA,EAChE,EAAE,KAAA,EAAO,uCAAA,EAAyC,GAAA,EAAK,eAAA,EAAgB;AAAA,EACvE,EAAE,KAAA,EAAO,2BAAA,EAA6B,GAAA,EAAK,YAAA,EAAa;AAAA,EACxD,EAAE,KAAA,EAAO,mCAAA,EAAqC,GAAA,EAAK,kBAAA,EAAmB;AAAA,EACtE,EAAE,KAAA,EAAO,aAAA,EAAe,GAAA,EAAK,SAAA,EAAU;AAAA,EACvC,EAAE,KAAA,EAAO,mBAAA,EAAqB,GAAA,EAAK,IAAA,EAAK;AAAA,EACxC,EAAE,KAAA,EAAO,mBAAA,EAAqB,GAAA,EAAK,OAAA;AACrC,CAAA;AAGA,SAAS,UAAU,CAAA,EAAyB;AAC1C,EAAA,OAAO,MAAA,CAAO,SAAA,CAAU,cAAA,CAAe,IAAA,CAAK,OAAO,CAAC,CAAA;AACtD;AAUO,SAAS,mBAAA,CACd,MACA,SAAA,EACkB;AAClB,EAAA,IAAI,IAAA,IAAQ,aAAa,MAAA,CAAO,SAAA,CAAU,eAAe,IAAA,CAAK,SAAA,EAAW,IAAI,CAAA,EAAG;AAC9E,IAAA,MAAM,KAAA,GAAQ,UAAU,IAAI,CAAA;AAC5B,IAAA,IAAI,SAAA,CAAU,KAAK,CAAA,EAAG;AACpB,MAAA,OAAO,MAAM,KAAK,CAAA;AAAA,IACpB;AAEA,IAAA,OAAA,CAAQ,IAAA;AAAA,MACN,6EAAA;AAAA,MACA,KAAA;AAAA,MACA;AAAA,KACF;AAAA,EACF;AAEA,EAAA,IAAI,IAAA,EAAM;AACR,IAAA,MAAM,KAAA,GAAQ,YAAY,IAAA,CAAK,CAAA,KAAA,KAAS,MAAM,KAAA,CAAM,IAAA,CAAK,IAAI,CAAC,CAAA;AAC9D,IAAA,IAAI,KAAA,EAAO;AACT,MAAA,OAAO,KAAA,CAAM,MAAM,GAAG,CAAA;AAAA,IACxB;AAAA,EACF;AAEA,EAAA,OAAO,KAAA,CAAM,QAAA;AACf;;;;"}
|
|
File without changes
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"facetModel.esm.js","sources":["../../src/components/facetModel.ts"],"sourcesContent":["// SPDX-License-Identifier: Apache-2.0\
|
|
1
|
+
{"version":3,"file":"facetModel.esm.js","sources":["../../src/components/facetModel.ts"],"sourcesContent":["// SPDX-License-Identifier: Apache-2.0\nimport type { Entity } from '@backstage/catalog-model';\nimport type { SvgIconComponent } from '@material-ui/icons';\nimport { resolveCategoryIcon } from './categoryIcons';\nimport { humanise } from './grouping';\nimport {\n entityCategoryFacet,\n entityLifecycle,\n entityOrg,\n entityOwnerLabel,\n} from './apigeeFacetFilters';\n\nexport type FacetKey = 'lifecycle' | 'org' | 'owner' | 'category';\n\nexport interface FacetOption {\n value: string;\n label: string;\n}\n\nexport interface DerivedFacet {\n key: FacetKey;\n label: string;\n options: FacetOption[];\n /**\n * True when the facet has at least one selectable value. The toolbar ALWAYS renders every\n * facet (revised D-05); an `enabled: false` facet (no values in the loaded data) renders as a\n * disabled dropdown so all four filters stay visible.\n */\n enabled: boolean;\n /**\n * Per-option icon map — present ONLY on the `category` facet (UI-10).\n * Keys are option values (e.g. `'payments'`, `'other'`); values are allow-listed\n * MUI icon components resolved via `resolveCategoryIcon`.\n * Not present on lifecycle / org / owner facets.\n */\n icons?: Record<string, SvgIconComponent>;\n}\n\n/** Locked facet order (D-02): Lifecycle → Org → Owner → Category. */\nexport const FACET_ORDER: FacetKey[] = ['lifecycle', 'org', 'owner', 'category'];\n\n/**\n * Lifecycle options are ordered by severity (most common / least risky first), NOT\n * alphabetically (UI-SPEC Assumption 5). Unknown values fall to the end via `indexOf` = -1\n * being handled below.\n */\nconst LIFECYCLE_SEVERITY = ['production', 'experimental', 'deprecated', 'unknown'];\n\n/** Fixed facet trigger labels (UI-SPEC §Component Inventory — order-locked). */\nconst FACET_TRIGGER_LABEL: Record<FacetKey, string> = {\n lifecycle: 'Lifecycle',\n org: 'Org',\n owner: 'Owner',\n category: 'Category',\n};\n\n/**\n * The single value extractor per facet slot. Category uses `entityCategoryFacet`\n * (undefined → 'other') so uncategorised APIs are selectable; the others reuse the\n * 06-01 extractors verbatim.\n */\nconst EXTRACT: Record<FacetKey, (e: Entity) => string | undefined> = {\n lifecycle: entityLifecycle,\n org: entityOrg,\n owner: entityOwnerLabel,\n category: entityCategoryFacet,\n};\n\n/**\n * Per-facet option label (LOCKED — UI-SPEC §Facet Data + §Assumption 5):\n * - lifecycle → capitalise first char ('production' → 'Production')\n * - owner → value AS-IS (already humanised by `entityOwnerLabel`; do NOT humanise again)\n * - category → humanise(value), except 'other' → 'Other'\n * - org → humanise(value) ('apigee-x-demo' → 'Apigee X Demo')\n */\nfunction labelFor(key: FacetKey, value: string): string {\n switch (key) {\n case 'lifecycle':\n return value.charAt(0).toUpperCase() + value.slice(1);\n case 'owner':\n return value;\n case 'category':\n return value === 'other' ? 'Other' : humanise(value);\n case 'org':\n default:\n return humanise(value);\n }\n}\n\n/**\n * Sort distinct facet values:\n * - lifecycle by fixed severity order\n * - category alphabetically with 'other' forced LAST (mirrors the grouped view)\n * - org / owner alphabetically (localeCompare)\n */\nfunction sortValues(key: FacetKey, values: string[]): string[] {\n if (key === 'lifecycle') {\n // Unrecognized lifecycles sort to the END, not the front (indexOf === -1 would\n // otherwise rank them before 'production'). Matches the documented intent.\n const rank = (v: string) => {\n const i = LIFECYCLE_SEVERITY.indexOf(v);\n return i === -1 ? LIFECYCLE_SEVERITY.length : i;\n };\n return [...values].sort((a, b) => rank(a) - rank(b));\n }\n if (key === 'category') {\n return [...values].sort((a, b) => {\n if (a === 'other') return 1;\n if (b === 'other') return -1;\n return a.localeCompare(b);\n });\n }\n return [...values].sort((a, b) => a.localeCompare(b));\n}\n\n/**\n * Derive stable, labelled, sorted facets from the FULL loaded entity set. Every facet in\n * FACET_ORDER is ALWAYS returned (revised D-05): `enabled` is true when the facet has >= 1\n * distinct value and false when the data has none — the toolbar renders a disabled dropdown for\n * the latter so all four filters stay visible even with uniform data. The caller passes the\n * pre-facet universe (`backendEntities.filter(isApigeeEntity)`) so options do not vanish as the\n * grid narrows.\n */\nexport function deriveFacets(\n entities: Entity[],\n overrides?: Record<string, string>,\n): DerivedFacet[] {\n return FACET_ORDER.map(key => {\n const distinct = Array.from(\n new Set(entities.map(EXTRACT[key]).filter((v): v is string => !!v)),\n );\n const options = sortValues(key, distinct).map(value => ({\n value,\n label: labelFor(key, value),\n }));\n const icons: Record<string, SvgIconComponent> | undefined =\n key === 'category'\n ? Object.fromEntries(\n options.map(opt => [\n opt.value,\n resolveCategoryIcon(\n opt.value === 'other' ? undefined : opt.value,\n overrides,\n ),\n ]),\n )\n : undefined;\n return {\n key,\n label: FACET_TRIGGER_LABEL[key],\n options,\n enabled: options.length > 0,\n ...(icons !== undefined ? { icons } : {}),\n };\n });\n}\n"],"names":[],"mappings":"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"}
|
|
File without changes
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"grouping.esm.js","sources":["../../src/components/grouping.ts"],"sourcesContent":["// SPDX-License-Identifier: Apache-2.0\
|
|
1
|
+
{"version":3,"file":"grouping.esm.js","sources":["../../src/components/grouping.ts"],"sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\nexport interface CategoryGroup<T> {\n key: string;\n label: string;\n items: T[];\n}\n\n/** Humanise a slug: split on `-`/`_`, drop empties, capitalise each word. */\nexport function humanise(slug: string): string {\n return slug\n .split(/[-_]/)\n .filter(Boolean)\n .map(word => word.charAt(0).toUpperCase() + word.slice(1))\n .join(' ');\n}\n\n/**\n * Bucket items by their optional `category`, humanise the label, sort groups\n * alphabetically, and always place the 'Other' bucket (uncategorised) last.\n * Generic over any `{ category?: string }` — no view-model coupling (UI-11 / D-06).\n */\nexport function groupByCategory<T extends { category?: string }>(\n items: T[],\n): CategoryGroup<T>[] {\n const buckets = new Map<string, T[]>();\n for (const item of items) {\n const key = item.category?.trim() || 'other';\n const existing = buckets.get(key);\n if (existing) {\n existing.push(item);\n } else {\n buckets.set(key, [item]);\n }\n }\n\n const groups: CategoryGroup<T>[] = Array.from(buckets.entries()).map(\n ([key, groupItems]) => ({\n key,\n label: key === 'other' ? 'Other' : humanise(key),\n items: groupItems,\n }),\n );\n\n groups.sort((a, b) => {\n if (a.key === 'other') return 1;\n if (b.key === 'other') return -1;\n return a.label.localeCompare(b.label);\n });\n\n return groups;\n}\n"],"names":[],"mappings":"AASO,SAAS,SAAS,IAAA,EAAsB;AAC7C,EAAA,OAAO,IAAA,CACJ,MAAM,MAAM,CAAA,CACZ,OAAO,OAAO,CAAA,CACd,IAAI,CAAA,IAAA,KAAQ,IAAA,CAAK,OAAO,CAAC,CAAA,CAAE,aAAY,GAAI,IAAA,CAAK,MAAM,CAAC,CAAC,CAAA,CACxD,IAAA,CAAK,GAAG,CAAA;AACb;AAOO,SAAS,gBACd,KAAA,EACoB;AACpB,EAAA,MAAM,OAAA,uBAAc,GAAA,EAAiB;AACrC,EAAA,KAAA,MAAW,QAAQ,KAAA,EAAO;AACxB,IAAA,MAAM,GAAA,GAAM,IAAA,CAAK,QAAA,EAAU,IAAA,EAAK,IAAK,OAAA;AACrC,IAAA,MAAM,QAAA,GAAW,OAAA,CAAQ,GAAA,CAAI,GAAG,CAAA;AAChC,IAAA,IAAI,QAAA,EAAU;AACZ,MAAA,QAAA,CAAS,KAAK,IAAI,CAAA;AAAA,IACpB,CAAA,MAAO;AACL,MAAA,OAAA,CAAQ,GAAA,CAAI,GAAA,EAAK,CAAC,IAAI,CAAC,CAAA;AAAA,IACzB;AAAA,EACF;AAEA,EAAA,MAAM,SAA6B,KAAA,CAAM,IAAA,CAAK,OAAA,CAAQ,OAAA,EAAS,CAAA,CAAE,GAAA;AAAA,IAC/D,CAAC,CAAC,GAAA,EAAK,UAAU,CAAA,MAAO;AAAA,MACtB,GAAA;AAAA,MACA,KAAA,EAAO,GAAA,KAAQ,OAAA,GAAU,OAAA,GAAU,SAAS,GAAG,CAAA;AAAA,MAC/C,KAAA,EAAO;AAAA,KACT;AAAA,GACF;AAEA,EAAA,MAAA,CAAO,IAAA,CAAK,CAAC,CAAA,EAAG,CAAA,KAAM;AACpB,IAAA,IAAI,CAAA,CAAE,GAAA,KAAQ,OAAA,EAAS,OAAO,CAAA;AAC9B,IAAA,IAAI,CAAA,CAAE,GAAA,KAAQ,OAAA,EAAS,OAAO,EAAA;AAC9B,IAAA,OAAO,CAAA,CAAE,KAAA,CAAM,aAAA,CAAc,CAAA,CAAE,KAAK,CAAA;AAAA,EACtC,CAAC,CAAA;AAED,EAAA,OAAO,MAAA;AACT;;;;"}
|
|
File without changes
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"states.esm.js","sources":["../../src/components/states.tsx"],"sourcesContent":["// SPDX-License-Identifier: Apache-2.0\
|
|
1
|
+
{"version":3,"file":"states.esm.js","sources":["../../src/components/states.tsx"],"sourcesContent":["// SPDX-License-Identifier: Apache-2.0\nimport {\n EmptyState,\n ItemCardGrid,\n LinkButton,\n ResponseErrorPanel,\n} from '@backstage/core-components';\nimport { Box, Button, makeStyles } from '@material-ui/core';\n\nconst useStyles = makeStyles(theme => ({\n srOnly: {\n position: 'absolute',\n width: 1,\n height: 1,\n padding: 0,\n margin: -1,\n overflow: 'hidden',\n clip: 'rect(0 0 0 0)',\n whiteSpace: 'nowrap',\n border: 0,\n },\n card: {\n minHeight: 172,\n padding: theme.spacing(2),\n borderRadius: theme.shape.borderRadius,\n border: `1px solid ${theme.palette.divider}`,\n backgroundColor: theme.palette.background.paper,\n },\n bar: {\n backgroundColor: theme.palette.action.hover,\n borderRadius: theme.shape.borderRadius,\n animation: '$pulse 1.5s ease-in-out infinite',\n '@media (prefers-reduced-motion: reduce)': {\n animation: 'none',\n },\n },\n titleBar: { height: 20, width: '70%', marginBottom: theme.spacing(2) },\n metaBar: { height: 12, width: '50%', marginBottom: theme.spacing(1) },\n badgeBar: { height: 16, width: '30%', marginTop: theme.spacing(2) },\n '@keyframes pulse': {\n '0%, 100%': { opacity: 1 },\n '50%': { opacity: 0.4 },\n },\n}));\n\n/**\n * Loading placeholder: `count` skeleton tiles inside the same responsive grid, using\n * plain Box blocks (no MUI lab dependency — D-07). The region is marked `aria-busy`\n * with a visually-hidden polite live region so assistive tech announces the load. (UI-05)\n */\nexport function SkeletonGrid({ count = 8 }: { count?: number } = {}) {\n const classes = useStyles();\n return (\n <div aria-busy=\"true\">\n <span className={classes.srOnly} aria-live=\"polite\">\n Loading Apigee APIs…\n </span>\n <ItemCardGrid>\n {Array.from({ length: count }).map((_, index) => (\n <Box\n key={`apigee-skeleton-${index}`}\n className={classes.card}\n aria-hidden=\"true\"\n >\n <Box className={`${classes.bar} ${classes.titleBar}`} />\n <Box className={`${classes.bar} ${classes.metaBar}`} />\n <Box className={`${classes.bar} ${classes.metaBar}`} />\n <Box className={`${classes.bar} ${classes.badgeBar}`} />\n </Box>\n ))}\n </ItemCardGrid>\n </div>\n );\n}\n\n/** Friendly empty state shown when no Apigee APIs are in the catalog (UI-05). */\nexport function ApiCatalogEmptyState() {\n return (\n <EmptyState\n missing=\"content\"\n title=\"No Apigee APIs found\"\n description=\"No API entities with an Apigee org annotation are in the catalog yet. If you expect to see APIs here, check that the apigee-backend sync has run.\"\n action={\n <LinkButton\n to=\"/catalog?filters[kind]=API\"\n variant=\"contained\"\n color=\"primary\"\n >\n Browse the full catalog\n </LinkButton>\n }\n />\n );\n}\n\n/**\n * Distinct \"filtered to zero\" state (D-10) — shown when Apigee APIs exist but the active\n * filters/search exclude them all. Unlike `ApiCatalogEmptyState` (which links out to the full\n * catalog), this offers a non-destructive reset CTA (the button below) which is fully reversible,\n * so it uses primary/neutral styling (no error color, no confirmation dialog). (UI-08)\n */\nexport function NoMatchState({ onClearAll }: { onClearAll: () => void }) {\n return (\n <EmptyState\n missing=\"content\"\n title=\"No APIs match your filters\"\n description=\"Try removing a filter or clearing your search to see more APIs.\"\n action={\n <Button variant=\"contained\" color=\"primary\" onClick={onClearAll}>\n Clear all filters\n </Button>\n }\n />\n );\n}\n\n/** Error state shown when the catalog fetch fails (UI-05). */\nexport function ApiCatalogError({ error }: { error: Error }) {\n return <ResponseErrorPanel title=\"Couldn't load Apigee APIs\" error={error} />;\n}\n"],"names":[],"mappings":"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"}
|
|
File without changes
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useApigeeFilters.esm.js","sources":["../../src/components/useApigeeFilters.ts"],"sourcesContent":["// SPDX-License-Identifier: Apache-2.0\
|
|
1
|
+
{"version":3,"file":"useApigeeFilters.esm.js","sources":["../../src/components/useApigeeFilters.ts"],"sourcesContent":["// SPDX-License-Identifier: Apache-2.0\nimport { useCallback, useEffect, useMemo, useState } from 'react';\nimport { useSearchParams } from 'react-router-dom';\nimport { useEntityList } from '@backstage/plugin-catalog-react';\nimport type { Entity } from '@backstage/catalog-model';\nimport {\n ApiSearchFilter,\n FieldFacetFilter,\n entityCategoryFacet,\n entityLifecycle,\n entityOrg,\n entityOwnerLabel,\n} from './apigeeFacetFilters';\nimport { parseSearchParams } from './apigeeSearchParams';\nimport { useDebouncedValue } from './useDebouncedValue';\nimport type { ApigeeFilters } from './ApigeeApiCatalogPage';\nimport type { FacetKey } from './facetModel';\n\n/**\n * Value extractor per facet slot used to build the FILTER. Category uses the FACET extractor\n * (undefined → 'other'), so selecting the 'other' bucket matches uncategorised APIs — search uses\n * the real slug instead (06-01).\n */\nconst FACET_EXTRACT: Record<FacetKey, (e: Entity) => string | undefined> = {\n lifecycle: entityLifecycle,\n org: entityOrg,\n owner: entityOwnerLabel,\n category: entityCategoryFacet,\n};\n\n/** Facet slot → the `ApigeeFilters` key its `FieldFacetFilter` registers under. */\ntype FacetFilterKey =\n | 'apigeeLifecycle'\n | 'apigeeOrgFacet'\n | 'apigeeOwner'\n | 'apigeeCategory';\nconst FILTER_KEY: Record<FacetKey, FacetFilterKey> = {\n lifecycle: 'apigeeLifecycle',\n org: 'apigeeOrgFacet',\n owner: 'apigeeOwner',\n category: 'apigeeCategory',\n};\n\n/**\n * The `useSearchParams` ⇄ `updateFilters` bridge: the URL is the single source of truth. It parses\n * the clean facet + `q` keys into state, drives the provider's in-memory filters one-directionally\n * (URL → `updateFilters`, no re-fetch), and writes every change back with `{ replace: true }` (D-08)\n * so the address bar stays clean, shareable and reload-safe (UI-09). The search box keeps one local\n * `inputValue` (instant echo) with a single 200 ms trailing debounce (D-09) → URL; the URL never\n * writes back into the input, so there is no feedback loop (UI-06, UI-07).\n */\nexport function useApigeeFilters() {\n const [searchParams, setSearchParams] = useSearchParams();\n const { updateFilters } = useEntityList<ApigeeFilters>();\n\n // Depend on the stable STRING, not the per-render URLSearchParams object (Pitfall 4).\n const spString = searchParams.toString();\n const state = useMemo(() => parseSearchParams(searchParams), [spString]); // eslint-disable-line\n\n // Search text is the only local state; seeded once from the URL.\n const [inputValue, setInputValue] = useState(() => state.q);\n const debouncedQ = useDebouncedValue(inputValue, 200); // D-09\n\n // E2: debounced q → URL (replace), merged with the current facet params.\n useEffect(() => {\n setSearchParams(\n prev => {\n const next = new URLSearchParams(prev);\n if (debouncedQ.trim()) {\n next.set('q', debouncedQ.trim());\n } else {\n next.delete('q');\n }\n return next;\n },\n { replace: true },\n );\n }, [debouncedQ, setSearchParams]);\n\n // E1: URL → provider filters (one-directional). Runs on mount (seeds shared links) + on change.\n useEffect(() => {\n const patch: Partial<ApigeeFilters> = {};\n (Object.keys(FILTER_KEY) as FacetKey[]).forEach(k => {\n patch[FILTER_KEY[k]] = state.selected[k].length\n ? new FieldFacetFilter(FACET_EXTRACT[k], state.selected[k])\n : undefined;\n });\n patch.apigeeSearch = state.q.trim()\n ? new ApiSearchFilter(state.q)\n : undefined;\n updateFilters(patch);\n }, [spString, updateFilters]); // eslint-disable-line\n\n const toggleFacet = useCallback(\n (key: FacetKey, value: string) => {\n setSearchParams(\n prev => {\n const next = new URLSearchParams(prev);\n const cur = next.getAll(key);\n next.delete(key);\n const after = cur.includes(value)\n ? cur.filter(v => v !== value)\n : [...cur, value];\n after.forEach(v => next.append(key, v));\n return next;\n },\n { replace: true },\n );\n },\n [setSearchParams],\n );\n\n const clearFacet = useCallback(\n (key: FacetKey) => {\n setSearchParams(\n prev => {\n const next = new URLSearchParams(prev);\n next.delete(key);\n return next;\n },\n { replace: true },\n );\n },\n [setSearchParams],\n );\n\n const clearAll = useCallback(() => {\n setInputValue('');\n setSearchParams(new URLSearchParams(), { replace: true });\n }, [setSearchParams]);\n\n return { state, inputValue, setInputValue, toggleFacet, clearFacet, clearAll };\n}\n"],"names":[],"mappings":"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"}
|
|
File without changes
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useDebouncedValue.esm.js","sources":["../../src/components/useDebouncedValue.ts"],"sourcesContent":["// SPDX-License-Identifier: Apache-2.0\
|
|
1
|
+
{"version":3,"file":"useDebouncedValue.esm.js","sources":["../../src/components/useDebouncedValue.ts"],"sourcesContent":["// SPDX-License-Identifier: Apache-2.0\nimport { useEffect, useState } from 'react';\n\n/**\n * Trailing-edge debounce: returns `value` immediately, then re-emits the latest `value`\n * after `delayMs` of no change. Rapid successive changes cancel the prior timer via\n * `clearTimeout`, so only the trailing value is emitted. No external dependency (D-09).\n */\nexport function useDebouncedValue<T>(value: T, delayMs: number): T {\n const [debounced, setDebounced] = useState(value);\n useEffect(() => {\n const id = setTimeout(() => setDebounced(value), delayMs);\n return () => clearTimeout(id);\n }, [value, delayMs]);\n return debounced;\n}\n"],"names":[],"mappings":";;AAQO,SAAS,iBAAA,CAAqB,OAAU,OAAA,EAAoB;AACjE,EAAA,MAAM,CAAC,SAAA,EAAW,YAAY,CAAA,GAAI,SAAS,KAAK,CAAA;AAChD,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,MAAM,KAAK,UAAA,CAAW,MAAM,YAAA,CAAa,KAAK,GAAG,OAAO,CAAA;AACxD,IAAA,OAAO,MAAM,aAAa,EAAE,CAAA;AAAA,EAC9B,CAAA,EAAG,CAAC,KAAA,EAAO,OAAO,CAAC,CAAA;AACnB,EAAA,OAAO,SAAA;AACT;;;;"}
|
package/dist/constants.esm.js
CHANGED
|
File without changes
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constants.esm.js","sources":["../src/constants.ts"],"sourcesContent":["// SPDX-License-Identifier: Apache-2.0\
|
|
1
|
+
{"version":3,"file":"constants.esm.js","sources":["../src/constants.ts"],"sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n// Apigee catalog annotation keys. Authoritative source:\n// plugins/apigee-backend/src/lib/entity-builder.ts (verified 2026-07-09).\n\n/** Org annotation written by proxyToEntities (PLURAL, proxy-derived API entities). */\nexport const ANNOTATION_APIGEE_ORGS = 'apigee.com/orgs';\n/** Org annotation written by hubOnlyToEntity (SINGULAR, hub-only API entities). */\nexport const ANNOTATION_APIGEE_ORG = 'apigee.com/org';\n/** 'true' | 'false' — set by applyHubMetadata when a spec is embedded or linked. */\nexport const ANNOTATION_HAS_SPEC = 'apigee.com/has-spec';\n/** Comma-joined slugified categories (absent in the current seed dump). */\nexport const ANNOTATION_CATEGORY = 'apigee.com/category';\n/** Full API Hub resource name — present only on hub-only entities. */\nexport const ANNOTATION_API_HUB_ID = 'apigee.com/api-hub-id';\n/**\n * Best-effort API Hub version key (A3 — not yet emitted by the backend, absent in the seed).\n * The view-model HIDES the version line when this is absent (D-05).\n */\nexport const ANNOTATION_API_HUB_VERSION = 'apigee.com/api-hub-version';\n"],"names":[],"mappings":"AAKO,MAAM,sBAAA,GAAyB;AAE/B,MAAM,qBAAA,GAAwB;AAE9B,MAAM,mBAAA,GAAsB;AAE5B,MAAM,mBAAA,GAAsB;AAO5B,MAAM,0BAAA,GAA6B;;;;"}
|
package/dist/index.d.ts
CHANGED
|
File without changes
|
package/dist/index.esm.js
CHANGED
|
File without changes
|
package/dist/index.esm.js.map
CHANGED
|
File without changes
|
package/dist/plugin.esm.js
CHANGED
|
File without changes
|
package/dist/plugin.esm.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.esm.js","sources":["../src/plugin.tsx"],"sourcesContent":["// SPDX-License-Identifier: Apache-2.0\
|
|
1
|
+
{"version":3,"file":"plugin.esm.js","sources":["../src/plugin.tsx"],"sourcesContent":["// SPDX-License-Identifier: Apache-2.0\nimport {\n createFrontendPlugin,\n NavItemBlueprint,\n PageBlueprint,\n} from '@backstage/frontend-plugin-api';\nimport LayersIcon from '@material-ui/icons/Layers';\nimport { rootRouteRef } from './routes';\n\nexport const apigeePage = PageBlueprint.make({\n params: {\n title: 'API Catalog',\n routeRef: rootRouteRef,\n path: '/apigee',\n loader: () =>\n import('./components/ApigeeApiCatalogPage').then(\n ({ ApigeeApiCatalogPage }) => <ApigeeApiCatalogPage />,\n ),\n },\n});\n\nexport const apigeeNavItem = NavItemBlueprint.make({\n params: {\n title: 'API Catalog',\n icon: LayersIcon,\n routeRef: rootRouteRef,\n },\n});\n\nexport const apigeePlugin = createFrontendPlugin({\n pluginId: 'apigee',\n title: 'API Catalog',\n extensions: [apigeePage, apigeeNavItem],\n routes: {\n root: rootRouteRef,\n },\n});\n"],"names":[],"mappings":";;;;;AASO,MAAM,UAAA,GAAa,cAAc,IAAA,CAAK;AAAA,EAC3C,MAAA,EAAQ;AAAA,IACN,KAAA,EAAO,aAAA;AAAA,IACP,QAAA,EAAU,YAAA;AAAA,IACV,IAAA,EAAM,SAAA;AAAA,IACN,MAAA,EAAQ,MACN,OAAO,0CAAmC,CAAA,CAAE,IAAA;AAAA,MAC1C,CAAC,EAAE,oBAAA,EAAqB,yBAAO,oBAAA,EAAA,EAAqB;AAAA;AACtD;AAEN,CAAC;AAEM,MAAM,aAAA,GAAgB,iBAAiB,IAAA,CAAK;AAAA,EACjD,MAAA,EAAQ;AAAA,IACN,KAAA,EAAO,aAAA;AAAA,IACP,IAAA,EAAM,UAAA;AAAA,IACN,QAAA,EAAU;AAAA;AAEd,CAAC;AAEM,MAAM,eAAe,oBAAA,CAAqB;AAAA,EAC/C,QAAA,EAAU,QAAA;AAAA,EACV,KAAA,EAAO,aAAA;AAAA,EACP,UAAA,EAAY,CAAC,UAAA,EAAY,aAAa,CAAA;AAAA,EACtC,MAAA,EAAQ;AAAA,IACN,IAAA,EAAM;AAAA;AAEV,CAAC;;;;"}
|
package/dist/routes.esm.js
CHANGED
|
File without changes
|
package/dist/routes.esm.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"routes.esm.js","sources":["../src/routes.ts"],"sourcesContent":["// SPDX-License-Identifier: Apache-2.0\
|
|
1
|
+
{"version":3,"file":"routes.esm.js","sources":["../src/routes.ts"],"sourcesContent":["// SPDX-License-Identifier: Apache-2.0\nimport { createRouteRef } from '@backstage/frontend-plugin-api';\n\nexport const rootRouteRef = createRouteRef();\n"],"names":[],"mappings":";;AAGO,MAAM,eAAe,cAAA;;;;"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nospt/backstage-plugin-apigee",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"description": "Backstage frontend plugin bootstrap for Apigee integration",
|
|
6
6
|
"author": "NOS SGPS, S.A.",
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
"@backstage/plugin-catalog-react": "^2.1.1",
|
|
54
54
|
"@material-ui/core": "^4.9.13",
|
|
55
55
|
"@material-ui/icons": "^4.9.1",
|
|
56
|
-
"@nospt/backstage-plugin-apigee-common": "
|
|
56
|
+
"@nospt/backstage-plugin-apigee-common": "^0.1.1"
|
|
57
57
|
},
|
|
58
58
|
"peerDependencies": {
|
|
59
59
|
"react": "^16.13.1 || ^17.0.0 || ^18.0.0",
|
|
@@ -86,4 +86,4 @@
|
|
|
86
86
|
}
|
|
87
87
|
},
|
|
88
88
|
"module": "./dist/index.esm.js"
|
|
89
|
-
}
|
|
89
|
+
}
|