@kyverno/backstage-plugin-policy-reporter 2.8.0 → 2.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/dist/components/EntityCustomPoliciesContent/EntityCustomPoliciesContent.esm.js +6 -5
  3. package/dist/components/EntityCustomPoliciesContent/EntityCustomPoliciesContent.esm.js.map +1 -1
  4. package/dist/components/EntityKyvernoPoliciesContent/EntityKyvernoPoliciesContent.esm.js +6 -5
  5. package/dist/components/EntityKyvernoPoliciesContent/EntityKyvernoPoliciesContent.esm.js.map +1 -1
  6. package/dist/components/FilterLayout/FilterLayout.esm.js +44 -0
  7. package/dist/components/FilterLayout/FilterLayout.esm.js.map +1 -0
  8. package/dist/components/PolicyReportsPage/PolicyReportsPage.esm.js +25 -12
  9. package/dist/components/PolicyReportsPage/PolicyReportsPage.esm.js.map +1 -1
  10. package/dist/components/PolicyReportsTable/PolicyReportsTable.esm.js +10 -1
  11. package/dist/components/PolicyReportsTable/PolicyReportsTable.esm.js.map +1 -1
  12. package/dist/components/SelectCategory/SelectCategory.esm.js +83 -0
  13. package/dist/components/SelectCategory/SelectCategory.esm.js.map +1 -0
  14. package/dist/components/SelectKind/SelectKind.esm.js +83 -0
  15. package/dist/components/SelectKind/SelectKind.esm.js.map +1 -0
  16. package/dist/components/SelectNamespace/SelectNamespace.esm.js +53 -10
  17. package/dist/components/SelectNamespace/SelectNamespace.esm.js.map +1 -1
  18. package/dist/components/SelectPolicy/SelectPolicy.esm.js +83 -0
  19. package/dist/components/SelectPolicy/SelectPolicy.esm.js.map +1 -0
  20. package/dist/components/SelectSeverity/SelectSeverity.esm.js +1 -2
  21. package/dist/components/SelectSeverity/SelectSeverity.esm.js.map +1 -1
  22. package/dist/components/SelectSource/SelectSource.esm.js +83 -0
  23. package/dist/components/SelectSource/SelectSource.esm.js.map +1 -0
  24. package/dist/components/SelectStatus/SelectStatus.esm.js +1 -2
  25. package/dist/components/SelectStatus/SelectStatus.esm.js.map +1 -1
  26. package/dist/hooks/usePolicyReportsFilters.esm.js +6 -4
  27. package/dist/hooks/usePolicyReportsFilters.esm.js.map +1 -1
  28. package/dist/index.d.ts +1 -1
  29. package/dist/package.json.esm.js +4 -2
  30. package/dist/package.json.esm.js.map +1 -1
  31. package/package.json +18 -15
  32. package/dist/hooks/useNamespaces.esm.js +0 -28
  33. package/dist/hooks/useNamespaces.esm.js.map +0 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,35 @@
1
1
  # @kyverno/backstage-plugin-policy-reporter
2
2
 
3
+ ## 2.10.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 256fb9a: Added source filtering to the PolicyReportsPage. Users can now filter the list of policy reports by selecting one or more sources from the selected cluster.
8
+ - 2f4ac45: Added policy filtering to the PolicyReportsPage. Users can now filter the list of policy reports by selecting one or more policies from the selected cluster.
9
+ - d598ade: Added category filtering to the PolicyReportsPage. Users can now filter the list of policy reports by selecting one or more categories from the selected cluster.
10
+ - a664d0c: Added kind filtering to the PolicyReportsPage. Users can now filter the list of policy reports by selecting one or more kinds from the selected cluster.
11
+ - a1d0f51: Updated components that make use of the Policy Reporter Backend API to create toast whenever there are any requests errors.
12
+ - 880eecc: Backstage version bump to v1.51.1
13
+ - 44c5238: Backstage version bump to v1.52.0
14
+
15
+ ### Patch Changes
16
+
17
+ - d803dd4: Fixed namespace, kind, sources and category filters being cleared when the responsive filter layout remounts. Cleanup now skips while option lists are loading or temporarily empty.
18
+ - dbc7557: Fixed a bug where switching environments would retain the previously selected namespace, even if it didn't exist in the new environment.
19
+ - Updated dependencies [256fb9a]
20
+ - Updated dependencies [2f4ac45]
21
+ - Updated dependencies [d598ade]
22
+ - Updated dependencies [a664d0c]
23
+ - Updated dependencies [880eecc]
24
+ - Updated dependencies [44c5238]
25
+ - @kyverno/backstage-plugin-policy-reporter-common@2.5.0
26
+
27
+ ## 2.9.0
28
+
29
+ ### Minor Changes
30
+
31
+ - 836f939: Add `FilterLayout` component with responsive sidebar filters. On large screens filters render as a sidebar, on smaller screens they collapse into a dialog triggered by a filter icon button. Updated `EntityCustomPoliciesContent`, `EntityKyvernoPoliciesContent`, and `PolicyReportsPage` to use the new layout.
32
+
3
33
  ## 2.8.0
4
34
 
5
35
  ### Minor Changes
@@ -1,7 +1,7 @@
1
1
  import { jsx, jsxs } from 'react/jsx-runtime';
2
2
  import { Content, Progress } from '@backstage/core-components';
3
3
  import { useEntity, MissingAnnotationEmptyState } from '@backstage/plugin-catalog-react';
4
- import { Container, Header, Grid, Flex } from '@backstage/ui';
4
+ import { Container, Header } from '@backstage/ui';
5
5
  import { PolicyReportsTable } from '../PolicyReportsTable/PolicyReportsTable.esm.js';
6
6
  import { SelectEnvironment } from '../SelectEnvironment/SelectEnvironment.esm.js';
7
7
  import { isPolicyReporterAvailable, getResourceName, getKinds, getNamespaces } from '../../utils/annotations.esm.js';
@@ -11,6 +11,7 @@ import { PolicyReportsFiltersProvider } from '../../hooks/usePolicyReportsFilter
11
11
  import { KYVERNO_RESOURCE_NAME_ANNOTATION } from '@kyverno/backstage-plugin-policy-reporter-common';
12
12
  import { SelectStatus } from '../SelectStatus/SelectStatus.esm.js';
13
13
  import { SelectSeverity } from '../SelectSeverity/SelectSeverity.esm.js';
14
+ import { FilterLayout } from '../FilterLayout/FilterLayout.esm.js';
14
15
 
15
16
  const EntityCustomPoliciesContent = ({
16
17
  annotationsDocumentationUrl,
@@ -54,18 +55,18 @@ const EntityCustomPoliciesContent = ({
54
55
  customActions: /* @__PURE__ */ jsx(SelectEnvironment, { environments })
55
56
  }
56
57
  ),
57
- /* @__PURE__ */ jsx(Content, { children: /* @__PURE__ */ jsxs(Grid.Root, { columns: "1", gap: "4", children: [
58
- /* @__PURE__ */ jsxs(Flex, { align: "end", gap: "4", children: [
58
+ /* @__PURE__ */ jsx(Content, { children: /* @__PURE__ */ jsxs(FilterLayout, { children: [
59
+ /* @__PURE__ */ jsxs(FilterLayout.Filters, { children: [
59
60
  /* @__PURE__ */ jsx(SelectStatus, {}),
60
61
  /* @__PURE__ */ jsx(SelectSeverity, {})
61
62
  ] }),
62
- /* @__PURE__ */ jsx(
63
+ /* @__PURE__ */ jsx(FilterLayout.Content, { children: /* @__PURE__ */ jsx(
63
64
  PolicyReportsTable,
64
65
  {
65
66
  emptyContentText: "No policies",
66
67
  policyDocumentationUrl
67
68
  }
68
- )
69
+ ) })
69
70
  ] }) })
70
71
  ] })
71
72
  }
@@ -1 +1 @@
1
- {"version":3,"file":"EntityCustomPoliciesContent.esm.js","sources":["../../../src/components/EntityCustomPoliciesContent/EntityCustomPoliciesContent.tsx"],"sourcesContent":["import { Content, Progress } from '@backstage/core-components';\nimport {\n MissingAnnotationEmptyState,\n useEntity,\n} from '@backstage/plugin-catalog-react';\nimport { Container, Flex, Grid, Header } from '@backstage/ui';\nimport { PolicyReportsTable } from '../PolicyReportsTable';\nimport { SelectEnvironment } from '../SelectEnvironment';\nimport {\n getKinds,\n getNamespaces,\n getResourceName,\n isPolicyReporterAvailable,\n} from '../../utils/annotations';\nimport { MissingEnvironmentsEmptyState } from '../MissingEnvironmentsEmptyState';\nimport { useEntityEnvironment } from '../../hooks/useEntityEnvironment';\nimport { PolicyReportsFiltersProvider } from '../../hooks/usePolicyReportsFilters';\nimport { KYVERNO_RESOURCE_NAME_ANNOTATION } from '@kyverno/backstage-plugin-policy-reporter-common';\nimport { SelectStatus } from '../SelectStatus';\nimport { SelectSeverity } from '../SelectSeverity';\n\ntype EntityCustomPoliciesContentProps = {\n annotationsDocumentationUrl?: string;\n policyDocumentationUrl?: string;\n sources: string[];\n title: string;\n};\n\nexport const EntityCustomPoliciesContent = ({\n annotationsDocumentationUrl,\n policyDocumentationUrl,\n sources,\n title,\n}: EntityCustomPoliciesContentProps) => {\n const { entity } = useEntity();\n const annotations = entity.metadata.annotations;\n\n const annotationsState = isPolicyReporterAvailable(entity);\n\n const { environments, environmentsLoading } = useEntityEnvironment(\n entity,\n annotationsState,\n );\n\n // Annotations missing\n if (!annotationsState)\n return (\n <Container>\n <Content>\n <MissingAnnotationEmptyState\n readMoreUrl={annotationsDocumentationUrl}\n annotation={KYVERNO_RESOURCE_NAME_ANNOTATION}\n />\n </Content>\n </Container>\n );\n\n // Fetching environments\n if (environmentsLoading) return <Progress />;\n\n // Environments missing\n if (!environments?.length)\n return (\n <Container>\n <Content>\n <MissingEnvironmentsEmptyState entity={entity} />\n </Content>\n </Container>\n );\n\n return (\n <PolicyReportsFiltersProvider\n defaultFilters={{\n namespaces: getNamespaces(annotations),\n kinds: getKinds(annotations),\n sources: sources,\n search: getResourceName(annotations),\n }}\n defaultEnvironment={environments[0].entityRef}\n >\n <Container>\n <Header\n title={title}\n customActions={<SelectEnvironment environments={environments} />}\n />\n <Content>\n <Grid.Root columns=\"1\" gap=\"4\">\n <Flex align=\"end\" gap=\"4\">\n <SelectStatus />\n <SelectSeverity />\n </Flex>\n <PolicyReportsTable\n emptyContentText=\"No policies\"\n policyDocumentationUrl={policyDocumentationUrl}\n />\n </Grid.Root>\n </Content>\n </Container>\n </PolicyReportsFiltersProvider>\n );\n};\n"],"names":[],"mappings":";;;;;;;;;;;;;;AA4BO,MAAM,8BAA8B,CAAC;AAAA,EAC1C,2BAAA;AAAA,EACA,sBAAA;AAAA,EACA,OAAA;AAAA,EACA;AACF,CAAA,KAAwC;AACtC,EAAA,MAAM,EAAE,MAAA,EAAO,GAAI,SAAA,EAAU;AAC7B,EAAA,MAAM,WAAA,GAAc,OAAO,QAAA,CAAS,WAAA;AAEpC,EAAA,MAAM,gBAAA,GAAmB,0BAA0B,MAAM,CAAA;AAEzD,EAAA,MAAM,EAAE,YAAA,EAAc,mBAAA,EAAoB,GAAI,oBAAA;AAAA,IAC5C,MAAA;AAAA,IACA;AAAA,GACF;AAGA,EAAA,IAAI,CAAC,gBAAA;AACH,IAAA,uBACE,GAAA,CAAC,SAAA,EAAA,EACC,QAAA,kBAAA,GAAA,CAAC,OAAA,EAAA,EACC,QAAA,kBAAA,GAAA;AAAA,MAAC,2BAAA;AAAA,MAAA;AAAA,QACC,WAAA,EAAa,2BAAA;AAAA,QACb,UAAA,EAAY;AAAA;AAAA,OAEhB,CAAA,EACF,CAAA;AAIJ,EAAA,IAAI,mBAAA,EAAqB,uBAAO,GAAA,CAAC,QAAA,EAAA,EAAS,CAAA;AAG1C,EAAA,IAAI,CAAC,YAAA,EAAc,MAAA;AACjB,IAAA,uBACE,GAAA,CAAC,aACC,QAAA,kBAAA,GAAA,CAAC,OAAA,EAAA,EACC,8BAAC,6BAAA,EAAA,EAA8B,MAAA,EAAgB,GACjD,CAAA,EACF,CAAA;AAGJ,EAAA,uBACE,GAAA;AAAA,IAAC,4BAAA;AAAA,IAAA;AAAA,MACC,cAAA,EAAgB;AAAA,QACd,UAAA,EAAY,cAAc,WAAW,CAAA;AAAA,QACrC,KAAA,EAAO,SAAS,WAAW,CAAA;AAAA,QAC3B,OAAA;AAAA,QACA,MAAA,EAAQ,gBAAgB,WAAW;AAAA,OACrC;AAAA,MACA,kBAAA,EAAoB,YAAA,CAAa,CAAC,CAAA,CAAE,SAAA;AAAA,MAEpC,+BAAC,SAAA,EAAA,EACC,QAAA,EAAA;AAAA,wBAAA,GAAA;AAAA,UAAC,MAAA;AAAA,UAAA;AAAA,YACC,KAAA;AAAA,YACA,aAAA,kBAAe,GAAA,CAAC,iBAAA,EAAA,EAAkB,YAAA,EAA4B;AAAA;AAAA,SAChE;AAAA,wBACA,GAAA,CAAC,WACC,QAAA,kBAAA,IAAA,CAAC,IAAA,CAAK,MAAL,EAAU,OAAA,EAAQ,GAAA,EAAI,GAAA,EAAI,GAAA,EACzB,QAAA,EAAA;AAAA,0BAAA,IAAA,CAAC,IAAA,EAAA,EAAK,KAAA,EAAM,KAAA,EAAM,GAAA,EAAI,GAAA,EACpB,QAAA,EAAA;AAAA,4BAAA,GAAA,CAAC,YAAA,EAAA,EAAa,CAAA;AAAA,gCACb,cAAA,EAAA,EAAe;AAAA,WAAA,EAClB,CAAA;AAAA,0BACA,GAAA;AAAA,YAAC,kBAAA;AAAA,YAAA;AAAA,cACC,gBAAA,EAAiB,aAAA;AAAA,cACjB;AAAA;AAAA;AACF,SAAA,EACF,CAAA,EACF;AAAA,OAAA,EACF;AAAA;AAAA,GACF;AAEJ;;;;"}
1
+ {"version":3,"file":"EntityCustomPoliciesContent.esm.js","sources":["../../../src/components/EntityCustomPoliciesContent/EntityCustomPoliciesContent.tsx"],"sourcesContent":["import { Content, Progress } from '@backstage/core-components';\nimport {\n MissingAnnotationEmptyState,\n useEntity,\n} from '@backstage/plugin-catalog-react';\nimport { Container, Header } from '@backstage/ui';\nimport { PolicyReportsTable } from '../PolicyReportsTable';\nimport { SelectEnvironment } from '../SelectEnvironment';\nimport {\n getKinds,\n getNamespaces,\n getResourceName,\n isPolicyReporterAvailable,\n} from '../../utils/annotations';\nimport { MissingEnvironmentsEmptyState } from '../MissingEnvironmentsEmptyState';\nimport { useEntityEnvironment } from '../../hooks/useEntityEnvironment';\nimport { PolicyReportsFiltersProvider } from '../../hooks/usePolicyReportsFilters';\nimport { KYVERNO_RESOURCE_NAME_ANNOTATION } from '@kyverno/backstage-plugin-policy-reporter-common';\nimport { SelectStatus } from '../SelectStatus';\nimport { SelectSeverity } from '../SelectSeverity';\nimport { FilterLayout } from '../FilterLayout';\n\ntype EntityCustomPoliciesContentProps = {\n annotationsDocumentationUrl?: string;\n policyDocumentationUrl?: string;\n sources: string[];\n title: string;\n};\n\nexport const EntityCustomPoliciesContent = ({\n annotationsDocumentationUrl,\n policyDocumentationUrl,\n sources,\n title,\n}: EntityCustomPoliciesContentProps) => {\n const { entity } = useEntity();\n const annotations = entity.metadata.annotations;\n\n const annotationsState = isPolicyReporterAvailable(entity);\n\n const { environments, environmentsLoading } = useEntityEnvironment(\n entity,\n annotationsState,\n );\n\n // Annotations missing\n if (!annotationsState)\n return (\n <Container>\n <Content>\n <MissingAnnotationEmptyState\n readMoreUrl={annotationsDocumentationUrl}\n annotation={KYVERNO_RESOURCE_NAME_ANNOTATION}\n />\n </Content>\n </Container>\n );\n\n // Fetching environments\n if (environmentsLoading) return <Progress />;\n\n // Environments missing\n if (!environments?.length)\n return (\n <Container>\n <Content>\n <MissingEnvironmentsEmptyState entity={entity} />\n </Content>\n </Container>\n );\n\n return (\n <PolicyReportsFiltersProvider\n defaultFilters={{\n namespaces: getNamespaces(annotations),\n kinds: getKinds(annotations),\n sources: sources,\n search: getResourceName(annotations),\n }}\n defaultEnvironment={environments[0].entityRef}\n >\n <Container>\n <Header\n title={title}\n customActions={<SelectEnvironment environments={environments} />}\n />\n <Content>\n <FilterLayout>\n <FilterLayout.Filters>\n <SelectStatus />\n <SelectSeverity />\n </FilterLayout.Filters>\n <FilterLayout.Content>\n <PolicyReportsTable\n emptyContentText=\"No policies\"\n policyDocumentationUrl={policyDocumentationUrl}\n />\n </FilterLayout.Content>\n </FilterLayout>\n </Content>\n </Container>\n </PolicyReportsFiltersProvider>\n );\n};\n"],"names":[],"mappings":";;;;;;;;;;;;;;;AA6BO,MAAM,8BAA8B,CAAC;AAAA,EAC1C,2BAAA;AAAA,EACA,sBAAA;AAAA,EACA,OAAA;AAAA,EACA;AACF,CAAA,KAAwC;AACtC,EAAA,MAAM,EAAE,MAAA,EAAO,GAAI,SAAA,EAAU;AAC7B,EAAA,MAAM,WAAA,GAAc,OAAO,QAAA,CAAS,WAAA;AAEpC,EAAA,MAAM,gBAAA,GAAmB,0BAA0B,MAAM,CAAA;AAEzD,EAAA,MAAM,EAAE,YAAA,EAAc,mBAAA,EAAoB,GAAI,oBAAA;AAAA,IAC5C,MAAA;AAAA,IACA;AAAA,GACF;AAGA,EAAA,IAAI,CAAC,gBAAA;AACH,IAAA,uBACE,GAAA,CAAC,SAAA,EAAA,EACC,QAAA,kBAAA,GAAA,CAAC,OAAA,EAAA,EACC,QAAA,kBAAA,GAAA;AAAA,MAAC,2BAAA;AAAA,MAAA;AAAA,QACC,WAAA,EAAa,2BAAA;AAAA,QACb,UAAA,EAAY;AAAA;AAAA,OAEhB,CAAA,EACF,CAAA;AAIJ,EAAA,IAAI,mBAAA,EAAqB,uBAAO,GAAA,CAAC,QAAA,EAAA,EAAS,CAAA;AAG1C,EAAA,IAAI,CAAC,YAAA,EAAc,MAAA;AACjB,IAAA,uBACE,GAAA,CAAC,aACC,QAAA,kBAAA,GAAA,CAAC,OAAA,EAAA,EACC,8BAAC,6BAAA,EAAA,EAA8B,MAAA,EAAgB,GACjD,CAAA,EACF,CAAA;AAGJ,EAAA,uBACE,GAAA;AAAA,IAAC,4BAAA;AAAA,IAAA;AAAA,MACC,cAAA,EAAgB;AAAA,QACd,UAAA,EAAY,cAAc,WAAW,CAAA;AAAA,QACrC,KAAA,EAAO,SAAS,WAAW,CAAA;AAAA,QAC3B,OAAA;AAAA,QACA,MAAA,EAAQ,gBAAgB,WAAW;AAAA,OACrC;AAAA,MACA,kBAAA,EAAoB,YAAA,CAAa,CAAC,CAAA,CAAE,SAAA;AAAA,MAEpC,+BAAC,SAAA,EAAA,EACC,QAAA,EAAA;AAAA,wBAAA,GAAA;AAAA,UAAC,MAAA;AAAA,UAAA;AAAA,YACC,KAAA;AAAA,YACA,aAAA,kBAAe,GAAA,CAAC,iBAAA,EAAA,EAAkB,YAAA,EAA4B;AAAA;AAAA,SAChE;AAAA,wBACA,GAAA,CAAC,OAAA,EAAA,EACC,QAAA,kBAAA,IAAA,CAAC,YAAA,EAAA,EACC,QAAA,EAAA;AAAA,0BAAA,IAAA,CAAC,YAAA,CAAa,SAAb,EACC,QAAA,EAAA;AAAA,4BAAA,GAAA,CAAC,YAAA,EAAA,EAAa,CAAA;AAAA,gCACb,cAAA,EAAA,EAAe;AAAA,WAAA,EAClB,CAAA;AAAA,0BACA,GAAA,CAAC,YAAA,CAAa,OAAA,EAAb,EACC,QAAA,kBAAA,GAAA;AAAA,YAAC,kBAAA;AAAA,YAAA;AAAA,cACC,gBAAA,EAAiB,aAAA;AAAA,cACjB;AAAA;AAAA,WACF,EACF;AAAA,SAAA,EACF,CAAA,EACF;AAAA,OAAA,EACF;AAAA;AAAA,GACF;AAEJ;;;;"}
@@ -1,7 +1,7 @@
1
1
  import { jsx, jsxs } from 'react/jsx-runtime';
2
2
  import { Content, Progress } from '@backstage/core-components';
3
3
  import { useEntity, MissingAnnotationEmptyState } from '@backstage/plugin-catalog-react';
4
- import { Container, Header, Grid, Flex } from '@backstage/ui';
4
+ import { Container, Header } from '@backstage/ui';
5
5
  import { PolicyReportsTable } from '../PolicyReportsTable/PolicyReportsTable.esm.js';
6
6
  import { SelectEnvironment } from '../SelectEnvironment/SelectEnvironment.esm.js';
7
7
  import { isPolicyReporterAvailable, getResourceName, getKinds, getNamespaces } from '../../utils/annotations.esm.js';
@@ -11,6 +11,7 @@ import { PolicyReportsFiltersProvider } from '../../hooks/usePolicyReportsFilter
11
11
  import { KYVERNO_RESOURCE_NAME_ANNOTATION } from '@kyverno/backstage-plugin-policy-reporter-common';
12
12
  import { SelectSeverity } from '../SelectSeverity/SelectSeverity.esm.js';
13
13
  import { SelectStatus } from '../SelectStatus/SelectStatus.esm.js';
14
+ import { FilterLayout } from '../FilterLayout/FilterLayout.esm.js';
14
15
 
15
16
  const EntityKyvernoPoliciesContent = ({
16
17
  annotationsDocumentationUrl,
@@ -52,18 +53,18 @@ const EntityKyvernoPoliciesContent = ({
52
53
  customActions: /* @__PURE__ */ jsx(SelectEnvironment, { environments })
53
54
  }
54
55
  ),
55
- /* @__PURE__ */ jsx(Content, { children: /* @__PURE__ */ jsxs(Grid.Root, { columns: "1", gap: "4", children: [
56
- /* @__PURE__ */ jsxs(Flex, { align: "end", gap: "4", children: [
56
+ /* @__PURE__ */ jsx(Content, { children: /* @__PURE__ */ jsxs(FilterLayout, { children: [
57
+ /* @__PURE__ */ jsxs(FilterLayout.Filters, { children: [
57
58
  /* @__PURE__ */ jsx(SelectStatus, {}),
58
59
  /* @__PURE__ */ jsx(SelectSeverity, {})
59
60
  ] }),
60
- /* @__PURE__ */ jsx(
61
+ /* @__PURE__ */ jsx(FilterLayout.Content, { children: /* @__PURE__ */ jsx(
61
62
  PolicyReportsTable,
62
63
  {
63
64
  emptyContentText: "No policies found",
64
65
  policyDocumentationUrl
65
66
  }
66
- )
67
+ ) })
67
68
  ] }) })
68
69
  ] })
69
70
  }
@@ -1 +1 @@
1
- {"version":3,"file":"EntityKyvernoPoliciesContent.esm.js","sources":["../../../src/components/EntityKyvernoPoliciesContent/EntityKyvernoPoliciesContent.tsx"],"sourcesContent":["import { Content, Progress } from '@backstage/core-components';\nimport {\n MissingAnnotationEmptyState,\n useEntity,\n} from '@backstage/plugin-catalog-react';\nimport { Container, Flex, Grid, Header } from '@backstage/ui';\nimport { PolicyReportsTable } from '../PolicyReportsTable';\nimport { SelectEnvironment } from '../SelectEnvironment';\nimport {\n getKinds,\n getNamespaces,\n getResourceName,\n isPolicyReporterAvailable,\n} from '../../utils/annotations';\nimport { MissingEnvironmentsEmptyState } from '../MissingEnvironmentsEmptyState';\nimport { useEntityEnvironment } from '../../hooks/useEntityEnvironment';\nimport { PolicyReportsFiltersProvider } from '../../hooks/usePolicyReportsFilters';\nimport { KYVERNO_RESOURCE_NAME_ANNOTATION } from '@kyverno/backstage-plugin-policy-reporter-common';\nimport { SelectSeverity } from '../SelectSeverity';\nimport { SelectStatus } from '../SelectStatus';\n\ntype KyvernoPoliciesContentProps = {\n annotationsDocumentationUrl?: string;\n policyDocumentationUrl?: string;\n};\n\nexport const EntityKyvernoPoliciesContent = ({\n annotationsDocumentationUrl,\n policyDocumentationUrl,\n}: KyvernoPoliciesContentProps) => {\n const { entity } = useEntity();\n const annotations = entity.metadata.annotations;\n\n const annotationsState = isPolicyReporterAvailable(entity);\n\n const { environments, environmentsLoading } = useEntityEnvironment(\n entity,\n annotationsState,\n );\n\n // Annotations missing\n if (!annotationsState)\n return (\n <Container>\n <Content>\n <MissingAnnotationEmptyState\n readMoreUrl={annotationsDocumentationUrl}\n annotation={KYVERNO_RESOURCE_NAME_ANNOTATION}\n />\n </Content>\n </Container>\n );\n\n // Fetching environments\n if (environmentsLoading) return <Progress />;\n\n // Environments missing\n if (!environments?.length)\n return (\n <Container>\n <Content>\n <MissingEnvironmentsEmptyState entity={entity} />\n </Content>\n </Container>\n );\n\n return (\n <PolicyReportsFiltersProvider\n defaultFilters={{\n namespaces: getNamespaces(annotations),\n kinds: getKinds(annotations),\n sources: ['kyverno'],\n search: getResourceName(annotations),\n }}\n defaultEnvironment={environments[0].entityRef}\n >\n <Container>\n <Header\n title=\"Kyverno Policy Reports\"\n customActions={<SelectEnvironment environments={environments} />}\n />\n <Content>\n <Grid.Root columns=\"1\" gap=\"4\">\n <Flex align=\"end\" gap=\"4\">\n <SelectStatus />\n <SelectSeverity />\n </Flex>\n <PolicyReportsTable\n emptyContentText=\"No policies found\"\n policyDocumentationUrl={policyDocumentationUrl}\n />\n </Grid.Root>\n </Content>\n </Container>\n </PolicyReportsFiltersProvider>\n );\n};\n"],"names":[],"mappings":";;;;;;;;;;;;;;AA0BO,MAAM,+BAA+B,CAAC;AAAA,EAC3C,2BAAA;AAAA,EACA;AACF,CAAA,KAAmC;AACjC,EAAA,MAAM,EAAE,MAAA,EAAO,GAAI,SAAA,EAAU;AAC7B,EAAA,MAAM,WAAA,GAAc,OAAO,QAAA,CAAS,WAAA;AAEpC,EAAA,MAAM,gBAAA,GAAmB,0BAA0B,MAAM,CAAA;AAEzD,EAAA,MAAM,EAAE,YAAA,EAAc,mBAAA,EAAoB,GAAI,oBAAA;AAAA,IAC5C,MAAA;AAAA,IACA;AAAA,GACF;AAGA,EAAA,IAAI,CAAC,gBAAA;AACH,IAAA,uBACE,GAAA,CAAC,SAAA,EAAA,EACC,QAAA,kBAAA,GAAA,CAAC,OAAA,EAAA,EACC,QAAA,kBAAA,GAAA;AAAA,MAAC,2BAAA;AAAA,MAAA;AAAA,QACC,WAAA,EAAa,2BAAA;AAAA,QACb,UAAA,EAAY;AAAA;AAAA,OAEhB,CAAA,EACF,CAAA;AAIJ,EAAA,IAAI,mBAAA,EAAqB,uBAAO,GAAA,CAAC,QAAA,EAAA,EAAS,CAAA;AAG1C,EAAA,IAAI,CAAC,YAAA,EAAc,MAAA;AACjB,IAAA,uBACE,GAAA,CAAC,aACC,QAAA,kBAAA,GAAA,CAAC,OAAA,EAAA,EACC,8BAAC,6BAAA,EAAA,EAA8B,MAAA,EAAgB,GACjD,CAAA,EACF,CAAA;AAGJ,EAAA,uBACE,GAAA;AAAA,IAAC,4BAAA;AAAA,IAAA;AAAA,MACC,cAAA,EAAgB;AAAA,QACd,UAAA,EAAY,cAAc,WAAW,CAAA;AAAA,QACrC,KAAA,EAAO,SAAS,WAAW,CAAA;AAAA,QAC3B,OAAA,EAAS,CAAC,SAAS,CAAA;AAAA,QACnB,MAAA,EAAQ,gBAAgB,WAAW;AAAA,OACrC;AAAA,MACA,kBAAA,EAAoB,YAAA,CAAa,CAAC,CAAA,CAAE,SAAA;AAAA,MAEpC,+BAAC,SAAA,EAAA,EACC,QAAA,EAAA;AAAA,wBAAA,GAAA;AAAA,UAAC,MAAA;AAAA,UAAA;AAAA,YACC,KAAA,EAAM,wBAAA;AAAA,YACN,aAAA,kBAAe,GAAA,CAAC,iBAAA,EAAA,EAAkB,YAAA,EAA4B;AAAA;AAAA,SAChE;AAAA,wBACA,GAAA,CAAC,WACC,QAAA,kBAAA,IAAA,CAAC,IAAA,CAAK,MAAL,EAAU,OAAA,EAAQ,GAAA,EAAI,GAAA,EAAI,GAAA,EACzB,QAAA,EAAA;AAAA,0BAAA,IAAA,CAAC,IAAA,EAAA,EAAK,KAAA,EAAM,KAAA,EAAM,GAAA,EAAI,GAAA,EACpB,QAAA,EAAA;AAAA,4BAAA,GAAA,CAAC,YAAA,EAAA,EAAa,CAAA;AAAA,gCACb,cAAA,EAAA,EAAe;AAAA,WAAA,EAClB,CAAA;AAAA,0BACA,GAAA;AAAA,YAAC,kBAAA;AAAA,YAAA;AAAA,cACC,gBAAA,EAAiB,mBAAA;AAAA,cACjB;AAAA;AAAA;AACF,SAAA,EACF,CAAA,EACF;AAAA,OAAA,EACF;AAAA;AAAA,GACF;AAEJ;;;;"}
1
+ {"version":3,"file":"EntityKyvernoPoliciesContent.esm.js","sources":["../../../src/components/EntityKyvernoPoliciesContent/EntityKyvernoPoliciesContent.tsx"],"sourcesContent":["import { Content, Progress } from '@backstage/core-components';\nimport {\n MissingAnnotationEmptyState,\n useEntity,\n} from '@backstage/plugin-catalog-react';\nimport { Container, Header } from '@backstage/ui';\nimport { PolicyReportsTable } from '../PolicyReportsTable';\nimport { SelectEnvironment } from '../SelectEnvironment';\nimport {\n getKinds,\n getNamespaces,\n getResourceName,\n isPolicyReporterAvailable,\n} from '../../utils/annotations';\nimport { MissingEnvironmentsEmptyState } from '../MissingEnvironmentsEmptyState';\nimport { useEntityEnvironment } from '../../hooks/useEntityEnvironment';\nimport { PolicyReportsFiltersProvider } from '../../hooks/usePolicyReportsFilters';\nimport { KYVERNO_RESOURCE_NAME_ANNOTATION } from '@kyverno/backstage-plugin-policy-reporter-common';\nimport { SelectSeverity } from '../SelectSeverity';\nimport { SelectStatus } from '../SelectStatus';\nimport { FilterLayout } from '../FilterLayout';\n\ntype KyvernoPoliciesContentProps = {\n annotationsDocumentationUrl?: string;\n policyDocumentationUrl?: string;\n};\n\nexport const EntityKyvernoPoliciesContent = ({\n annotationsDocumentationUrl,\n policyDocumentationUrl,\n}: KyvernoPoliciesContentProps) => {\n const { entity } = useEntity();\n const annotations = entity.metadata.annotations;\n\n const annotationsState = isPolicyReporterAvailable(entity);\n\n const { environments, environmentsLoading } = useEntityEnvironment(\n entity,\n annotationsState,\n );\n\n // Annotations missing\n if (!annotationsState)\n return (\n <Container>\n <Content>\n <MissingAnnotationEmptyState\n readMoreUrl={annotationsDocumentationUrl}\n annotation={KYVERNO_RESOURCE_NAME_ANNOTATION}\n />\n </Content>\n </Container>\n );\n\n // Fetching environments\n if (environmentsLoading) return <Progress />;\n\n // Environments missing\n if (!environments?.length)\n return (\n <Container>\n <Content>\n <MissingEnvironmentsEmptyState entity={entity} />\n </Content>\n </Container>\n );\n\n return (\n <PolicyReportsFiltersProvider\n defaultFilters={{\n namespaces: getNamespaces(annotations),\n kinds: getKinds(annotations),\n sources: ['kyverno'],\n search: getResourceName(annotations),\n }}\n defaultEnvironment={environments[0].entityRef}\n >\n <Container>\n <Header\n title=\"Kyverno Policy Reports\"\n customActions={<SelectEnvironment environments={environments} />}\n />\n <Content>\n <FilterLayout>\n <FilterLayout.Filters>\n <SelectStatus />\n <SelectSeverity />\n </FilterLayout.Filters>\n <FilterLayout.Content>\n <PolicyReportsTable\n emptyContentText=\"No policies found\"\n policyDocumentationUrl={policyDocumentationUrl}\n />\n </FilterLayout.Content>\n </FilterLayout>\n </Content>\n </Container>\n </PolicyReportsFiltersProvider>\n );\n};\n"],"names":[],"mappings":";;;;;;;;;;;;;;;AA2BO,MAAM,+BAA+B,CAAC;AAAA,EAC3C,2BAAA;AAAA,EACA;AACF,CAAA,KAAmC;AACjC,EAAA,MAAM,EAAE,MAAA,EAAO,GAAI,SAAA,EAAU;AAC7B,EAAA,MAAM,WAAA,GAAc,OAAO,QAAA,CAAS,WAAA;AAEpC,EAAA,MAAM,gBAAA,GAAmB,0BAA0B,MAAM,CAAA;AAEzD,EAAA,MAAM,EAAE,YAAA,EAAc,mBAAA,EAAoB,GAAI,oBAAA;AAAA,IAC5C,MAAA;AAAA,IACA;AAAA,GACF;AAGA,EAAA,IAAI,CAAC,gBAAA;AACH,IAAA,uBACE,GAAA,CAAC,SAAA,EAAA,EACC,QAAA,kBAAA,GAAA,CAAC,OAAA,EAAA,EACC,QAAA,kBAAA,GAAA;AAAA,MAAC,2BAAA;AAAA,MAAA;AAAA,QACC,WAAA,EAAa,2BAAA;AAAA,QACb,UAAA,EAAY;AAAA;AAAA,OAEhB,CAAA,EACF,CAAA;AAIJ,EAAA,IAAI,mBAAA,EAAqB,uBAAO,GAAA,CAAC,QAAA,EAAA,EAAS,CAAA;AAG1C,EAAA,IAAI,CAAC,YAAA,EAAc,MAAA;AACjB,IAAA,uBACE,GAAA,CAAC,aACC,QAAA,kBAAA,GAAA,CAAC,OAAA,EAAA,EACC,8BAAC,6BAAA,EAAA,EAA8B,MAAA,EAAgB,GACjD,CAAA,EACF,CAAA;AAGJ,EAAA,uBACE,GAAA;AAAA,IAAC,4BAAA;AAAA,IAAA;AAAA,MACC,cAAA,EAAgB;AAAA,QACd,UAAA,EAAY,cAAc,WAAW,CAAA;AAAA,QACrC,KAAA,EAAO,SAAS,WAAW,CAAA;AAAA,QAC3B,OAAA,EAAS,CAAC,SAAS,CAAA;AAAA,QACnB,MAAA,EAAQ,gBAAgB,WAAW;AAAA,OACrC;AAAA,MACA,kBAAA,EAAoB,YAAA,CAAa,CAAC,CAAA,CAAE,SAAA;AAAA,MAEpC,+BAAC,SAAA,EAAA,EACC,QAAA,EAAA;AAAA,wBAAA,GAAA;AAAA,UAAC,MAAA;AAAA,UAAA;AAAA,YACC,KAAA,EAAM,wBAAA;AAAA,YACN,aAAA,kBAAe,GAAA,CAAC,iBAAA,EAAA,EAAkB,YAAA,EAA4B;AAAA;AAAA,SAChE;AAAA,wBACA,GAAA,CAAC,OAAA,EAAA,EACC,QAAA,kBAAA,IAAA,CAAC,YAAA,EAAA,EACC,QAAA,EAAA;AAAA,0BAAA,IAAA,CAAC,YAAA,CAAa,SAAb,EACC,QAAA,EAAA;AAAA,4BAAA,GAAA,CAAC,YAAA,EAAA,EAAa,CAAA;AAAA,gCACb,cAAA,EAAA,EAAe;AAAA,WAAA,EAClB,CAAA;AAAA,0BACA,GAAA,CAAC,YAAA,CAAa,OAAA,EAAb,EACC,QAAA,kBAAA,GAAA;AAAA,YAAC,kBAAA;AAAA,YAAA;AAAA,cACC,gBAAA,EAAiB,mBAAA;AAAA,cACjB;AAAA;AAAA,WACF,EACF;AAAA,SAAA,EACF,CAAA,EACF;AAAA,OAAA,EACF;AAAA;AAAA,GACF;AAEJ;;;;"}
@@ -0,0 +1,44 @@
1
+ import { jsx, jsxs } from 'react/jsx-runtime';
2
+ import { useState } from 'react';
3
+ import { Grid, DialogTrigger, ButtonIcon, Dialog, DialogHeader, DialogBody, Flex, Text } from '@backstage/ui';
4
+ import useMediaQuery from '@material-ui/core/useMediaQuery';
5
+ import { RiFilter3Fill } from '@remixicon/react';
6
+
7
+ const Filters = ({ children, title = "Filters" }) => {
8
+ const isSmallScreen = useMediaQuery(
9
+ (theme) => theme.breakpoints.down("md"),
10
+ { noSsr: true }
11
+ );
12
+ const [open, setOpen] = useState(false);
13
+ if (isSmallScreen) {
14
+ return /* @__PURE__ */ jsx(Grid.Item, { colSpan: "12", children: /* @__PURE__ */ jsxs(DialogTrigger, { isOpen: open, onOpenChange: setOpen, children: [
15
+ /* @__PURE__ */ jsx(
16
+ ButtonIcon,
17
+ {
18
+ variant: "secondary",
19
+ "aria-label": title,
20
+ icon: /* @__PURE__ */ jsx(RiFilter3Fill, {})
21
+ }
22
+ ),
23
+ /* @__PURE__ */ jsxs(Dialog, { children: [
24
+ /* @__PURE__ */ jsx(DialogHeader, { children: title }),
25
+ /* @__PURE__ */ jsx(DialogBody, { children: /* @__PURE__ */ jsx(Flex, { direction: "column", gap: "4", children }) })
26
+ ] })
27
+ ] }) });
28
+ }
29
+ return /* @__PURE__ */ jsx(Grid.Item, { colSpan: { initial: "12", lg: "2" }, children: /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: "4", children: [
30
+ /* @__PURE__ */ jsx(Text, { variant: "title-x-small", children: title }),
31
+ children
32
+ ] }) });
33
+ };
34
+ const Content = ({ children }) => {
35
+ return /* @__PURE__ */ jsx(Grid.Item, { colSpan: { initial: "12", lg: "10" }, children });
36
+ };
37
+ const FilterLayout = ({ children }) => {
38
+ return /* @__PURE__ */ jsx(Grid.Root, { columns: "12", gap: "6", children });
39
+ };
40
+ FilterLayout.Filters = Filters;
41
+ FilterLayout.Content = Content;
42
+
43
+ export { FilterLayout };
44
+ //# sourceMappingURL=FilterLayout.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FilterLayout.esm.js","sources":["../../../src/components/FilterLayout/FilterLayout.tsx"],"sourcesContent":["import { type ReactNode, useState } from 'react';\nimport {\n ButtonIcon,\n Dialog,\n DialogBody,\n DialogHeader,\n DialogTrigger,\n Flex,\n Grid,\n Text,\n} from '@backstage/ui';\nimport useMediaQuery from '@material-ui/core/useMediaQuery';\nimport { Theme } from '@material-ui/core/styles';\nimport { RiFilter3Fill } from '@remixicon/react';\n\n/** Props for the Filters sub-component */\ninterface FiltersProps {\n children: ReactNode;\n /** Label shown in the dialog header on small screens */\n title?: string;\n}\n\n/** Renders filter children as a sidebar on large screens, or inside a Dialog on smaller screens */\nconst Filters = ({ children, title = 'Filters' }: FiltersProps) => {\n // TODO: Migrate to BUI useBreakpoint once the performance warning is resolved\n const isSmallScreen = useMediaQuery(\n (theme: Theme) => theme.breakpoints.down('md'),\n { noSsr: true },\n );\n const [open, setOpen] = useState(false);\n\n if (isSmallScreen) {\n return (\n <Grid.Item colSpan=\"12\">\n <DialogTrigger isOpen={open} onOpenChange={setOpen}>\n <ButtonIcon\n variant=\"secondary\"\n aria-label={title}\n icon={<RiFilter3Fill />}\n />\n <Dialog>\n <DialogHeader>{title}</DialogHeader>\n <DialogBody>\n <Flex direction=\"column\" gap=\"4\">\n {children}\n </Flex>\n </DialogBody>\n </Dialog>\n </DialogTrigger>\n </Grid.Item>\n );\n }\n\n return (\n <Grid.Item colSpan={{ initial: '12', lg: '2' }}>\n <Flex direction=\"column\" gap=\"4\">\n <Text variant=\"title-x-small\">{title}</Text>\n {children}\n </Flex>\n </Grid.Item>\n );\n};\n\n/** Props for the Content sub-component */\ninterface ContentProps {\n children: ReactNode;\n}\n\n/** Main content area that fills remaining grid space */\nconst Content = ({ children }: ContentProps) => {\n return (\n <Grid.Item colSpan={{ initial: '12', lg: '10' }}>{children}</Grid.Item>\n );\n};\n\n/** Props for the FilterLayout root */\ninterface FilterLayoutProps {\n children: ReactNode;\n}\n\n/** A responsive layout with a filter sidebar that collapses into a dialog on smaller screens */\nexport const FilterLayout = ({ children }: FilterLayoutProps) => {\n return (\n <Grid.Root columns=\"12\" gap=\"6\">\n {children}\n </Grid.Root>\n );\n};\n\nFilterLayout.Filters = Filters;\nFilterLayout.Content = Content;\n"],"names":[],"mappings":";;;;;;AAuBA,MAAM,UAAU,CAAC,EAAE,QAAA,EAAU,KAAA,GAAQ,WAAU,KAAoB;AAEjE,EAAA,MAAM,aAAA,GAAgB,aAAA;AAAA,IACpB,CAAC,KAAA,KAAiB,KAAA,CAAM,WAAA,CAAY,KAAK,IAAI,CAAA;AAAA,IAC7C,EAAE,OAAO,IAAA;AAAK,GAChB;AACA,EAAA,MAAM,CAAC,IAAA,EAAM,OAAO,CAAA,GAAI,SAAS,KAAK,CAAA;AAEtC,EAAA,IAAI,aAAA,EAAe;AACjB,IAAA,uBACE,GAAA,CAAC,IAAA,CAAK,IAAA,EAAL,EAAU,OAAA,EAAQ,IAAA,EACjB,QAAA,kBAAA,IAAA,CAAC,aAAA,EAAA,EAAc,MAAA,EAAQ,IAAA,EAAM,YAAA,EAAc,OAAA,EACzC,QAAA,EAAA;AAAA,sBAAA,GAAA;AAAA,QAAC,UAAA;AAAA,QAAA;AAAA,UACC,OAAA,EAAQ,WAAA;AAAA,UACR,YAAA,EAAY,KAAA;AAAA,UACZ,IAAA,sBAAO,aAAA,EAAA,EAAc;AAAA;AAAA,OACvB;AAAA,2BACC,MAAA,EAAA,EACC,QAAA,EAAA;AAAA,wBAAA,GAAA,CAAC,gBAAc,QAAA,EAAA,KAAA,EAAM,CAAA;AAAA,wBACrB,GAAA,CAAC,cACC,QAAA,kBAAA,GAAA,CAAC,IAAA,EAAA,EAAK,WAAU,QAAA,EAAS,GAAA,EAAI,GAAA,EAC1B,QAAA,EACH,CAAA,EACF;AAAA,OAAA,EACF;AAAA,KAAA,EACF,CAAA,EACF,CAAA;AAAA,EAEJ;AAEA,EAAA,2BACG,IAAA,CAAK,IAAA,EAAL,EAAU,OAAA,EAAS,EAAE,OAAA,EAAS,IAAA,EAAM,EAAA,EAAI,GAAA,IACvC,QAAA,kBAAA,IAAA,CAAC,IAAA,EAAA,EAAK,SAAA,EAAU,QAAA,EAAS,KAAI,GAAA,EAC3B,QAAA,EAAA;AAAA,oBAAA,GAAA,CAAC,IAAA,EAAA,EAAK,OAAA,EAAQ,eAAA,EAAiB,QAAA,EAAA,KAAA,EAAM,CAAA;AAAA,IACpC;AAAA,GAAA,EACH,CAAA,EACF,CAAA;AAEJ,CAAA;AAQA,MAAM,OAAA,GAAU,CAAC,EAAE,QAAA,EAAS,KAAoB;AAC9C,EAAA,uBACE,GAAA,CAAC,IAAA,CAAK,IAAA,EAAL,EAAU,OAAA,EAAS,EAAE,OAAA,EAAS,IAAA,EAAM,EAAA,EAAI,IAAA,EAAK,EAAI,QAAA,EAAS,CAAA;AAE/D,CAAA;AAQO,MAAM,YAAA,GAAe,CAAC,EAAE,QAAA,EAAS,KAAyB;AAC/D,EAAA,uBACE,GAAA,CAAC,KAAK,IAAA,EAAL,EAAU,SAAQ,IAAA,EAAK,GAAA,EAAI,KACzB,QAAA,EACH,CAAA;AAEJ;AAEA,YAAA,CAAa,OAAA,GAAU,OAAA;AACvB,YAAA,CAAa,OAAA,GAAU,OAAA;;;;"}
@@ -1,6 +1,6 @@
1
1
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
2
2
  import { Progress, Content, EmptyState } from '@backstage/core-components';
3
- import { Container, Header, ButtonLink, Grid, Flex, Box } from '@backstage/ui';
3
+ import { Container, Header, ButtonLink, Flex } from '@backstage/ui';
4
4
  import { useEnvironments } from '../../hooks/useEnvironments.esm.js';
5
5
  import { SelectEnvironment } from '../SelectEnvironment/SelectEnvironment.esm.js';
6
6
  import { PolicyReportsTable } from '../PolicyReportsTable/PolicyReportsTable.esm.js';
@@ -9,10 +9,16 @@ import { SelectSeverity } from '../SelectSeverity/SelectSeverity.esm.js';
9
9
  import { SelectNamespace } from '../SelectNamespace/SelectNamespace.esm.js';
10
10
  import { SearchField } from '../SearchField/SearchField.esm.js';
11
11
  import { PolicyReportsFiltersProvider } from '../../hooks/usePolicyReportsFilters.esm.js';
12
+ import { FilterLayout } from '../FilterLayout/FilterLayout.esm.js';
13
+ import { SelectSource } from '../SelectSource/SelectSource.esm.js';
14
+ import { SelectKind } from '../SelectKind/SelectKind.esm.js';
15
+ import { SelectCategory } from '../SelectCategory/SelectCategory.esm.js';
16
+ import { SelectPolicy } from '../SelectPolicy/SelectPolicy.esm.js';
12
17
 
13
18
  const PolicyReportsPage = ({
14
19
  title = "Policy Reports",
15
- policyDocumentationUrl
20
+ policyDocumentationUrl,
21
+ subtitle
16
22
  }) => {
17
23
  const { environments, environmentsLoading } = useEnvironments();
18
24
  const defaultFilter = {
@@ -53,23 +59,30 @@ const PolicyReportsPage = ({
53
59
  Header,
54
60
  {
55
61
  title,
62
+ description: subtitle,
56
63
  customActions: /* @__PURE__ */ jsx(SelectEnvironment, { environments })
57
64
  }
58
65
  ),
59
- /* @__PURE__ */ jsx(Content, { children: /* @__PURE__ */ jsxs(Grid.Root, { columns: "1", gap: "4", children: [
60
- /* @__PURE__ */ jsxs(Flex, { align: "end", gap: "4", children: [
66
+ /* @__PURE__ */ jsx(Content, { children: /* @__PURE__ */ jsxs(FilterLayout, { children: [
67
+ /* @__PURE__ */ jsxs(FilterLayout.Filters, { children: [
61
68
  /* @__PURE__ */ jsx(SelectStatus, {}),
62
69
  /* @__PURE__ */ jsx(SelectSeverity, {}),
63
70
  /* @__PURE__ */ jsx(SelectNamespace, {}),
64
- /* @__PURE__ */ jsx(Box, { width: "300px", style: { flexShrink: 0 }, children: /* @__PURE__ */ jsx(SearchField, {}) })
71
+ /* @__PURE__ */ jsx(SelectSource, {}),
72
+ /* @__PURE__ */ jsx(SelectKind, {}),
73
+ /* @__PURE__ */ jsx(SelectCategory, {}),
74
+ /* @__PURE__ */ jsx(SelectPolicy, {})
65
75
  ] }),
66
- /* @__PURE__ */ jsx(
67
- PolicyReportsTable,
68
- {
69
- emptyContentText: "No policies found",
70
- policyDocumentationUrl
71
- }
72
- )
76
+ /* @__PURE__ */ jsx(FilterLayout.Content, { children: /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: "4", children: [
77
+ /* @__PURE__ */ jsx(SearchField, {}),
78
+ /* @__PURE__ */ jsx(
79
+ PolicyReportsTable,
80
+ {
81
+ emptyContentText: "No policies found",
82
+ policyDocumentationUrl
83
+ }
84
+ )
85
+ ] }) })
73
86
  ] }) })
74
87
  ] })
75
88
  }
@@ -1 +1 @@
1
- {"version":3,"file":"PolicyReportsPage.esm.js","sources":["../../../src/components/PolicyReportsPage/PolicyReportsPage.tsx"],"sourcesContent":["import { Content, EmptyState, Progress } from '@backstage/core-components';\nimport { Box, Container, Flex, Grid, Header, ButtonLink } from '@backstage/ui';\nimport { useEnvironments } from '../../hooks/useEnvironments';\nimport { SelectEnvironment } from '../SelectEnvironment';\nimport { PolicyReportsTable } from '../PolicyReportsTable';\nimport { SelectStatus } from '../SelectStatus';\nimport { SelectSeverity } from '../SelectSeverity';\nimport { SelectNamespace } from '../SelectNamespace';\nimport { SearchField } from '../SearchField';\nimport { PolicyReportsFiltersProvider } from '../../hooks/usePolicyReportsFilters';\nimport { Filter } from '@kyverno/backstage-plugin-policy-reporter-common';\n\nexport interface PolicyReportsPageProps {\n title?: string;\n policyDocumentationUrl?: string;\n subtitle?: string;\n}\n\nexport const PolicyReportsPage = ({\n title = 'Policy Reports',\n policyDocumentationUrl,\n}: PolicyReportsPageProps) => {\n const { environments, environmentsLoading } = useEnvironments();\n\n const defaultFilter: Filter = {\n status: ['fail'],\n };\n\n // Loading environments\n if (environmentsLoading) return <Progress />;\n\n // Environments missing\n if (!environments?.length)\n return (\n <Container>\n <Header title={title} />\n <Content>\n <EmptyState\n missing=\"content\"\n title=\"No kubernetes-cluster Resources found\"\n description={\n <>\n You need to define a Resource with the kubernetes-cluster type\n and a<code> kyverno.io/endpoint </code> annotation for this\n plugin to work.\n </>\n }\n action={\n <>\n <ButtonLink\n target=\"_blank\"\n href=\"https://github.com/kyverno/backstage-policy-reporter-plugin\"\n >\n Read More\n </ButtonLink>\n </>\n }\n />\n </Content>\n </Container>\n );\n\n return (\n <PolicyReportsFiltersProvider\n defaultFilters={defaultFilter}\n defaultEnvironment={environments[0].entityRef}\n >\n <Container>\n <Header\n title={title}\n customActions={<SelectEnvironment environments={environments} />}\n />\n <Content>\n <Grid.Root columns=\"1\" gap=\"4\">\n <Flex align=\"end\" gap=\"4\">\n <SelectStatus />\n <SelectSeverity />\n <SelectNamespace />\n <Box width=\"300px\" style={{ flexShrink: 0 }}>\n <SearchField />\n </Box>\n </Flex>\n <PolicyReportsTable\n emptyContentText=\"No policies found\"\n policyDocumentationUrl={policyDocumentationUrl}\n />\n </Grid.Root>\n </Content>\n </Container>\n </PolicyReportsFiltersProvider>\n );\n};\n"],"names":[],"mappings":";;;;;;;;;;;;AAkBO,MAAM,oBAAoB,CAAC;AAAA,EAChC,KAAA,GAAQ,gBAAA;AAAA,EACR;AACF,CAAA,KAA8B;AAC5B,EAAA,MAAM,EAAE,YAAA,EAAc,mBAAA,EAAoB,GAAI,eAAA,EAAgB;AAE9D,EAAA,MAAM,aAAA,GAAwB;AAAA,IAC5B,MAAA,EAAQ,CAAC,MAAM;AAAA,GACjB;AAGA,EAAA,IAAI,mBAAA,EAAqB,uBAAO,GAAA,CAAC,QAAA,EAAA,EAAS,CAAA;AAG1C,EAAA,IAAI,CAAC,YAAA,EAAc,MAAA;AACjB,IAAA,4BACG,SAAA,EAAA,EACC,QAAA,EAAA;AAAA,sBAAA,GAAA,CAAC,UAAO,KAAA,EAAc,CAAA;AAAA,0BACrB,OAAA,EAAA,EACC,QAAA,kBAAA,GAAA;AAAA,QAAC,UAAA;AAAA,QAAA;AAAA,UACC,OAAA,EAAQ,SAAA;AAAA,UACR,KAAA,EAAM,uCAAA;AAAA,UACN,6BACE,IAAA,CAAA,QAAA,EAAA,EAAE,QAAA,EAAA;AAAA,YAAA,sEAAA;AAAA,4BAEK,GAAA,CAAC,UAAK,QAAA,EAAA,uBAAA,EAAqB,CAAA;AAAA,YAAO;AAAA,WAAA,EAEzC,CAAA;AAAA,UAEF,wBACE,GAAA,CAAA,QAAA,EAAA,EACE,QAAA,kBAAA,GAAA;AAAA,YAAC,UAAA;AAAA,YAAA;AAAA,cACC,MAAA,EAAO,QAAA;AAAA,cACP,IAAA,EAAK,6DAAA;AAAA,cACN,QAAA,EAAA;AAAA;AAAA,WAED,EACF;AAAA;AAAA,OAEJ,EACF;AAAA,KAAA,EACF,CAAA;AAGJ,EAAA,uBACE,GAAA;AAAA,IAAC,4BAAA;AAAA,IAAA;AAAA,MACC,cAAA,EAAgB,aAAA;AAAA,MAChB,kBAAA,EAAoB,YAAA,CAAa,CAAC,CAAA,CAAE,SAAA;AAAA,MAEpC,+BAAC,SAAA,EAAA,EACC,QAAA,EAAA;AAAA,wBAAA,GAAA;AAAA,UAAC,MAAA;AAAA,UAAA;AAAA,YACC,KAAA;AAAA,YACA,aAAA,kBAAe,GAAA,CAAC,iBAAA,EAAA,EAAkB,YAAA,EAA4B;AAAA;AAAA,SAChE;AAAA,wBACA,GAAA,CAAC,WACC,QAAA,kBAAA,IAAA,CAAC,IAAA,CAAK,MAAL,EAAU,OAAA,EAAQ,GAAA,EAAI,GAAA,EAAI,GAAA,EACzB,QAAA,EAAA;AAAA,0BAAA,IAAA,CAAC,IAAA,EAAA,EAAK,KAAA,EAAM,KAAA,EAAM,GAAA,EAAI,GAAA,EACpB,QAAA,EAAA;AAAA,4BAAA,GAAA,CAAC,YAAA,EAAA,EAAa,CAAA;AAAA,gCACb,cAAA,EAAA,EAAe,CAAA;AAAA,gCACf,eAAA,EAAA,EAAgB,CAAA;AAAA,4BACjB,GAAA,CAAC,GAAA,EAAA,EAAI,KAAA,EAAM,OAAA,EAAQ,KAAA,EAAO,EAAE,UAAA,EAAY,CAAA,EAAE,EACxC,QAAA,kBAAA,GAAA,CAAC,WAAA,EAAA,EAAY,CAAA,EACf;AAAA,WAAA,EACF,CAAA;AAAA,0BACA,GAAA;AAAA,YAAC,kBAAA;AAAA,YAAA;AAAA,cACC,gBAAA,EAAiB,mBAAA;AAAA,cACjB;AAAA;AAAA;AACF,SAAA,EACF,CAAA,EACF;AAAA,OAAA,EACF;AAAA;AAAA,GACF;AAEJ;;;;"}
1
+ {"version":3,"file":"PolicyReportsPage.esm.js","sources":["../../../src/components/PolicyReportsPage/PolicyReportsPage.tsx"],"sourcesContent":["import { Content, EmptyState, Progress } from '@backstage/core-components';\nimport { Container, Flex, Header, ButtonLink } from '@backstage/ui';\nimport { useEnvironments } from '../../hooks/useEnvironments';\nimport { SelectEnvironment } from '../SelectEnvironment';\nimport { PolicyReportsTable } from '../PolicyReportsTable';\nimport { SelectStatus } from '../SelectStatus';\nimport { SelectSeverity } from '../SelectSeverity';\nimport { SelectNamespace } from '../SelectNamespace';\nimport { SearchField } from '../SearchField';\nimport { PolicyReportsFiltersProvider } from '../../hooks/usePolicyReportsFilters';\nimport { Filter } from '@kyverno/backstage-plugin-policy-reporter-common';\nimport { FilterLayout } from '../FilterLayout';\nimport { SelectSource } from '../SelectSource';\nimport { SelectKind } from '../SelectKind';\nimport { SelectCategory } from '../SelectCategory';\nimport { SelectPolicy } from '../SelectPolicy';\n\nexport interface PolicyReportsPageProps {\n title?: string;\n policyDocumentationUrl?: string;\n subtitle?: string;\n}\n\nexport const PolicyReportsPage = ({\n title = 'Policy Reports',\n policyDocumentationUrl,\n subtitle,\n}: PolicyReportsPageProps) => {\n const { environments, environmentsLoading } = useEnvironments();\n\n const defaultFilter: Filter = {\n status: ['fail'],\n };\n\n // Loading environments\n if (environmentsLoading) return <Progress />;\n\n // Environments missing\n if (!environments?.length)\n return (\n <Container>\n <Header title={title} />\n <Content>\n <EmptyState\n missing=\"content\"\n title=\"No kubernetes-cluster Resources found\"\n description={\n <>\n You need to define a Resource with the kubernetes-cluster type\n and a<code> kyverno.io/endpoint </code> annotation for this\n plugin to work.\n </>\n }\n action={\n <>\n <ButtonLink\n target=\"_blank\"\n href=\"https://github.com/kyverno/backstage-policy-reporter-plugin\"\n >\n Read More\n </ButtonLink>\n </>\n }\n />\n </Content>\n </Container>\n );\n\n return (\n <PolicyReportsFiltersProvider\n defaultFilters={defaultFilter}\n defaultEnvironment={environments[0].entityRef}\n >\n <Container>\n <Header\n title={title}\n description={subtitle}\n customActions={<SelectEnvironment environments={environments} />}\n />\n <Content>\n <FilterLayout>\n <FilterLayout.Filters>\n <SelectStatus />\n <SelectSeverity />\n <SelectNamespace />\n <SelectSource />\n <SelectKind />\n <SelectCategory />\n <SelectPolicy />\n </FilterLayout.Filters>\n <FilterLayout.Content>\n <Flex direction=\"column\" gap=\"4\">\n <SearchField />\n <PolicyReportsTable\n emptyContentText=\"No policies found\"\n policyDocumentationUrl={policyDocumentationUrl}\n />\n </Flex>\n </FilterLayout.Content>\n </FilterLayout>\n </Content>\n </Container>\n </PolicyReportsFiltersProvider>\n );\n};\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;AAuBO,MAAM,oBAAoB,CAAC;AAAA,EAChC,KAAA,GAAQ,gBAAA;AAAA,EACR,sBAAA;AAAA,EACA;AACF,CAAA,KAA8B;AAC5B,EAAA,MAAM,EAAE,YAAA,EAAc,mBAAA,EAAoB,GAAI,eAAA,EAAgB;AAE9D,EAAA,MAAM,aAAA,GAAwB;AAAA,IAC5B,MAAA,EAAQ,CAAC,MAAM;AAAA,GACjB;AAGA,EAAA,IAAI,mBAAA,EAAqB,uBAAO,GAAA,CAAC,QAAA,EAAA,EAAS,CAAA;AAG1C,EAAA,IAAI,CAAC,YAAA,EAAc,MAAA;AACjB,IAAA,4BACG,SAAA,EAAA,EACC,QAAA,EAAA;AAAA,sBAAA,GAAA,CAAC,UAAO,KAAA,EAAc,CAAA;AAAA,0BACrB,OAAA,EAAA,EACC,QAAA,kBAAA,GAAA;AAAA,QAAC,UAAA;AAAA,QAAA;AAAA,UACC,OAAA,EAAQ,SAAA;AAAA,UACR,KAAA,EAAM,uCAAA;AAAA,UACN,6BACE,IAAA,CAAA,QAAA,EAAA,EAAE,QAAA,EAAA;AAAA,YAAA,sEAAA;AAAA,4BAEK,GAAA,CAAC,UAAK,QAAA,EAAA,uBAAA,EAAqB,CAAA;AAAA,YAAO;AAAA,WAAA,EAEzC,CAAA;AAAA,UAEF,wBACE,GAAA,CAAA,QAAA,EAAA,EACE,QAAA,kBAAA,GAAA;AAAA,YAAC,UAAA;AAAA,YAAA;AAAA,cACC,MAAA,EAAO,QAAA;AAAA,cACP,IAAA,EAAK,6DAAA;AAAA,cACN,QAAA,EAAA;AAAA;AAAA,WAED,EACF;AAAA;AAAA,OAEJ,EACF;AAAA,KAAA,EACF,CAAA;AAGJ,EAAA,uBACE,GAAA;AAAA,IAAC,4BAAA;AAAA,IAAA;AAAA,MACC,cAAA,EAAgB,aAAA;AAAA,MAChB,kBAAA,EAAoB,YAAA,CAAa,CAAC,CAAA,CAAE,SAAA;AAAA,MAEpC,+BAAC,SAAA,EAAA,EACC,QAAA,EAAA;AAAA,wBAAA,GAAA;AAAA,UAAC,MAAA;AAAA,UAAA;AAAA,YACC,KAAA;AAAA,YACA,WAAA,EAAa,QAAA;AAAA,YACb,aAAA,kBAAe,GAAA,CAAC,iBAAA,EAAA,EAAkB,YAAA,EAA4B;AAAA;AAAA,SAChE;AAAA,wBACA,GAAA,CAAC,OAAA,EAAA,EACC,QAAA,kBAAA,IAAA,CAAC,YAAA,EAAA,EACC,QAAA,EAAA;AAAA,0BAAA,IAAA,CAAC,YAAA,CAAa,SAAb,EACC,QAAA,EAAA;AAAA,4BAAA,GAAA,CAAC,YAAA,EAAA,EAAa,CAAA;AAAA,gCACb,cAAA,EAAA,EAAe,CAAA;AAAA,gCACf,eAAA,EAAA,EAAgB,CAAA;AAAA,gCAChB,YAAA,EAAA,EAAa,CAAA;AAAA,gCACb,UAAA,EAAA,EAAW,CAAA;AAAA,gCACX,cAAA,EAAA,EAAe,CAAA;AAAA,gCACf,YAAA,EAAA,EAAa;AAAA,WAAA,EAChB,CAAA;AAAA,0BACA,GAAA,CAAC,aAAa,OAAA,EAAb,EACC,+BAAC,IAAA,EAAA,EAAK,SAAA,EAAU,QAAA,EAAS,GAAA,EAAI,GAAA,EAC3B,QAAA,EAAA;AAAA,4BAAA,GAAA,CAAC,WAAA,EAAA,EAAY,CAAA;AAAA,4BACb,GAAA;AAAA,cAAC,kBAAA;AAAA,cAAA;AAAA,gBACC,gBAAA,EAAiB,mBAAA;AAAA,gBACjB;AAAA;AAAA;AACF,WAAA,EACF,CAAA,EACF;AAAA,SAAA,EACF,CAAA,EACF;AAAA,OAAA,EACF;AAAA;AAAA,GACF;AAEJ;;;;"}
@@ -5,7 +5,7 @@ import { StatusComponent } from '../StatusComponent/StatusComponent.esm.js';
5
5
  import { SeverityComponent } from '../SeverityComponent/SeverityComponent.esm.js';
6
6
  import { PolicyReportsDrawerComponent } from '../PolicyReportsDrawerComponent/PolicyReportsDrawerComponent.esm.js';
7
7
  import { useTable, Table, Text, CellText, Cell, Link } from '@backstage/ui';
8
- import { useApi } from '@backstage/frontend-plugin-api';
8
+ import { useApi, toastApiRef } from '@backstage/frontend-plugin-api';
9
9
  import { policyReporterApiRef } from '../../api/PolicyReporterClient.esm.js';
10
10
  import { usePolicyReportsFilters } from '../../hooks/usePolicyReportsFilters.esm.js';
11
11
 
@@ -16,6 +16,7 @@ const PolicyReportsTable = ({
16
16
  const policyReporterApi = useApi(policyReporterApiRef);
17
17
  const { filter, environment } = usePolicyReportsFilters();
18
18
  const search = filter.search;
19
+ const toast = useApi(toastApiRef);
19
20
  const [drawerContent, setDrawerContent] = useState(
20
21
  void 0
21
22
  );
@@ -40,6 +41,14 @@ const PolicyReportsTable = ({
40
41
  }
41
42
  });
42
43
  const result = await response.json();
44
+ if (!response.ok) {
45
+ toast.post({
46
+ title: "Failed to fetch policies",
47
+ description: result.error || response.statusText || `Request failed with status ${response.status}`,
48
+ status: "danger"
49
+ });
50
+ return { data: [], totalCount: 0 };
51
+ }
43
52
  return {
44
53
  data: result.items,
45
54
  totalCount: result.count
@@ -1 +1 @@
1
- {"version":3,"file":"PolicyReportsTable.esm.js","sources":["../../../src/components/PolicyReportsTable/PolicyReportsTable.tsx"],"sourcesContent":["import { useState } from 'react';\nimport { ListResult } from '@kyverno/backstage-plugin-policy-reporter-common';\nimport { Drawer } from '@material-ui/core';\nimport { StatusComponent } from '../StatusComponent';\nimport { SeverityComponent } from '../SeverityComponent';\nimport { PolicyReportsDrawerComponent } from '../PolicyReportsDrawerComponent';\nimport {\n CellText,\n ColumnConfig,\n Text,\n useTable,\n Table,\n Cell,\n Link,\n} from '@backstage/ui';\nimport { useApi } from '@backstage/frontend-plugin-api';\nimport { policyReporterApiRef } from '../../api';\nimport { usePolicyReportsFilters } from '../../hooks/usePolicyReportsFilters';\n\ninterface PolicyReportsTableProps {\n emptyContentText: string;\n policyDocumentationUrl?: string;\n}\n\nexport const PolicyReportsTable = ({\n emptyContentText,\n policyDocumentationUrl,\n}: PolicyReportsTableProps) => {\n const policyReporterApi = useApi(policyReporterApiRef);\n const { filter, environment } = usePolicyReportsFilters();\n const search = filter.search;\n\n const [drawerContent, setDrawerContent] = useState<ListResult | undefined>(\n undefined,\n );\n\n const { tableProps } = useTable({\n filter,\n mode: 'offset',\n search: search,\n getData: async ({\n offset,\n pageSize,\n filter: fetchFilter,\n search: searchParam,\n }) => {\n // useTable provides:\n // - offset: absolute record position (0, 20, 40, 60...)\n // - pageSize: number of records per page (20)\n //\n // Policy Reporter API expects:\n // - page: page number starting from 1 (1, 2, 3, 4...)\n // - offset: number of results per page (this is actually pageSize)\n //\n // Example calculations:\n // useTable offset=0, pageSize=20 → page=1 (first 20 records)\n // useTable offset=20, pageSize=20 → page=2 (records 21-40)\n // useTable offset=40, pageSize=20 → page=3 (records 41-60)\n\n // Convert useTable's absolute offset to 1-indexed page number\n // Math.floor(0/20) + 1 = 1, Math.floor(20/20) + 1 = 2, etc.\n const page = Math.floor(offset / pageSize) + 1;\n\n const response = await policyReporterApi.getNamespacedResults({\n query: {\n environment: encodeURI(environment),\n page: page,\n offset: pageSize,\n ...fetchFilter,\n search: searchParam === '' ? undefined : searchParam,\n },\n });\n\n const result = await response.json();\n\n return {\n data: result.items,\n totalCount: result.count,\n };\n },\n });\n\n const columns: ColumnConfig<ListResult>[] = [\n {\n id: 'name',\n label: 'Name',\n isRowHeader: true,\n cell: item => <CellText title={item.name} />,\n },\n {\n id: 'namespace',\n label: 'Namespace',\n isRowHeader: true,\n cell: item => <CellText title={item.namespace} />,\n },\n {\n id: 'kind',\n label: 'Kind',\n isRowHeader: true,\n cell: item => <CellText title={item.kind} />,\n },\n {\n id: 'policy',\n label: 'Policy',\n isRowHeader: true,\n cell: item => {\n if (policyDocumentationUrl) {\n return (\n <Cell>\n <Link\n target=\"_blank\"\n href={`${policyDocumentationUrl}#${item.policy}`}\n >\n {item.policy}\n </Link>\n </Cell>\n );\n }\n return <CellText title={item.policy} />;\n },\n },\n {\n id: 'rule',\n label: 'Rule',\n isRowHeader: true,\n cell: item => <CellText title={item.rule} />,\n },\n {\n id: 'status',\n label: 'Status',\n isRowHeader: true,\n cell: item => (\n <Cell>\n <StatusComponent status={item.status} />\n </Cell>\n ),\n },\n {\n id: 'severity',\n label: 'Severity',\n isRowHeader: true,\n cell: item => (\n <Cell>\n <SeverityComponent severity={item.severity} />\n </Cell>\n ),\n },\n ];\n\n return (\n <>\n <Drawer\n anchor=\"right\"\n open={Boolean(drawerContent)}\n onClose={() => setDrawerContent(undefined)}\n >\n <PolicyReportsDrawerComponent content={drawerContent} />\n </Drawer>\n <Table\n rowConfig={{\n onClick: item => setDrawerContent(item),\n }}\n emptyState={\n search ? (\n <Text>No results match \"{search}\"</Text>\n ) : (\n <Text>{emptyContentText}</Text>\n )\n }\n columnConfig={columns}\n {...tableProps}\n />\n </>\n );\n};\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"PolicyReportsTable.esm.js","sources":["../../../src/components/PolicyReportsTable/PolicyReportsTable.tsx"],"sourcesContent":["import { useState } from 'react';\nimport {\n ListResult,\n RequestError,\n} from '@kyverno/backstage-plugin-policy-reporter-common';\nimport { Drawer } from '@material-ui/core';\nimport { StatusComponent } from '../StatusComponent';\nimport { SeverityComponent } from '../SeverityComponent';\nimport { PolicyReportsDrawerComponent } from '../PolicyReportsDrawerComponent';\nimport {\n CellText,\n ColumnConfig,\n Text,\n useTable,\n Table,\n Cell,\n Link,\n} from '@backstage/ui';\nimport { toastApiRef, useApi } from '@backstage/frontend-plugin-api';\nimport { policyReporterApiRef } from '../../api';\nimport { usePolicyReportsFilters } from '../../hooks/usePolicyReportsFilters';\n\ninterface PolicyReportsTableProps {\n emptyContentText: string;\n policyDocumentationUrl?: string;\n}\n\nexport const PolicyReportsTable = ({\n emptyContentText,\n policyDocumentationUrl,\n}: PolicyReportsTableProps) => {\n const policyReporterApi = useApi(policyReporterApiRef);\n const { filter, environment } = usePolicyReportsFilters();\n const search = filter.search;\n const toast = useApi(toastApiRef);\n\n const [drawerContent, setDrawerContent] = useState<ListResult | undefined>(\n undefined,\n );\n\n const { tableProps } = useTable({\n filter,\n mode: 'offset',\n search: search,\n getData: async ({\n offset,\n pageSize,\n filter: fetchFilter,\n search: searchParam,\n }) => {\n // useTable provides:\n // - offset: absolute record position (0, 20, 40, 60...)\n // - pageSize: number of records per page (20)\n //\n // Policy Reporter API expects:\n // - page: page number starting from 1 (1, 2, 3, 4...)\n // - offset: number of results per page (this is actually pageSize)\n //\n // Example calculations:\n // useTable offset=0, pageSize=20 → page=1 (first 20 records)\n // useTable offset=20, pageSize=20 → page=2 (records 21-40)\n // useTable offset=40, pageSize=20 → page=3 (records 41-60)\n\n // Convert useTable's absolute offset to 1-indexed page number\n // Math.floor(0/20) + 1 = 1, Math.floor(20/20) + 1 = 2, etc.\n const page = Math.floor(offset / pageSize) + 1;\n\n const response = await policyReporterApi.getNamespacedResults({\n query: {\n environment: encodeURI(environment),\n page: page,\n offset: pageSize,\n ...fetchFilter,\n search: searchParam === '' ? undefined : searchParam,\n },\n });\n\n const result = await response.json();\n\n if (!response.ok) {\n toast.post({\n title: 'Failed to fetch policies',\n description:\n (result as unknown as RequestError).error ||\n response.statusText ||\n `Request failed with status ${response.status}`,\n status: 'danger',\n });\n return { data: [], totalCount: 0 };\n }\n\n return {\n data: result.items,\n totalCount: result.count,\n };\n },\n });\n\n const columns: ColumnConfig<ListResult>[] = [\n {\n id: 'name',\n label: 'Name',\n isRowHeader: true,\n cell: item => <CellText title={item.name} />,\n },\n {\n id: 'namespace',\n label: 'Namespace',\n isRowHeader: true,\n cell: item => <CellText title={item.namespace} />,\n },\n {\n id: 'kind',\n label: 'Kind',\n isRowHeader: true,\n cell: item => <CellText title={item.kind} />,\n },\n {\n id: 'policy',\n label: 'Policy',\n isRowHeader: true,\n cell: item => {\n if (policyDocumentationUrl) {\n return (\n <Cell>\n <Link\n target=\"_blank\"\n href={`${policyDocumentationUrl}#${item.policy}`}\n >\n {item.policy}\n </Link>\n </Cell>\n );\n }\n return <CellText title={item.policy} />;\n },\n },\n {\n id: 'rule',\n label: 'Rule',\n isRowHeader: true,\n cell: item => <CellText title={item.rule} />,\n },\n {\n id: 'status',\n label: 'Status',\n isRowHeader: true,\n cell: item => (\n <Cell>\n <StatusComponent status={item.status} />\n </Cell>\n ),\n },\n {\n id: 'severity',\n label: 'Severity',\n isRowHeader: true,\n cell: item => (\n <Cell>\n <SeverityComponent severity={item.severity} />\n </Cell>\n ),\n },\n ];\n\n return (\n <>\n <Drawer\n anchor=\"right\"\n open={Boolean(drawerContent)}\n onClose={() => setDrawerContent(undefined)}\n >\n <PolicyReportsDrawerComponent content={drawerContent} />\n </Drawer>\n <Table\n rowConfig={{\n onClick: item => setDrawerContent(item),\n }}\n emptyState={\n search ? (\n <Text>No results match \"{search}\"</Text>\n ) : (\n <Text>{emptyContentText}</Text>\n )\n }\n columnConfig={columns}\n {...tableProps}\n />\n </>\n );\n};\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,83 @@
1
+ import { jsx } from 'react/jsx-runtime';
2
+ import { useAsyncList, Select } from '@backstage/ui';
3
+ import { useMemo, useRef, useEffect } from 'react';
4
+ import { usePolicyReportsFilters } from '../../hooks/usePolicyReportsFilters.esm.js';
5
+ import { policyReporterApiRef } from '../../api/PolicyReporterClient.esm.js';
6
+ import { useApi, toastApiRef } from '@backstage/frontend-plugin-api';
7
+
8
+ const SelectCategory = () => {
9
+ const { filter, updateFilter, environment } = usePolicyReportsFilters();
10
+ const api = useApi(policyReporterApiRef);
11
+ const toast = useApi(toastApiRef);
12
+ const categoryOptions = useAsyncList({
13
+ async load({}) {
14
+ if (!environment) return { items: [] };
15
+ const response = await api.getCategories({ query: { environment } });
16
+ const result = await response.json();
17
+ if (!response.ok) {
18
+ toast.post({
19
+ title: "Failed to fetch categories",
20
+ description: result.error || response.statusText || `Request failed with status ${response.status}`,
21
+ status: "danger"
22
+ });
23
+ return { items: [] };
24
+ }
25
+ return {
26
+ items: result.map((c) => ({ id: c, label: c }))
27
+ };
28
+ }
29
+ });
30
+ const categories = useMemo(() => {
31
+ if (categoryOptions.isLoading || categoryOptions.items.length === 0) {
32
+ return void 0;
33
+ }
34
+ return new Set(categoryOptions.items.map((c) => c.label));
35
+ }, [categoryOptions.isLoading, categoryOptions.items]);
36
+ const categoryRef = useRef(categoryOptions);
37
+ categoryRef.current = categoryOptions;
38
+ useEffect(() => {
39
+ categoryRef.current.reload();
40
+ }, [environment]);
41
+ const selectedCategories = filter.categories ?? [];
42
+ const selectedCategoriesRef = useRef(selectedCategories);
43
+ selectedCategoriesRef.current = selectedCategories;
44
+ useEffect(() => {
45
+ if (!categories) return;
46
+ const selected = selectedCategoriesRef.current;
47
+ if (selected.every((category) => categories.has(category))) return;
48
+ updateFilter({
49
+ categories: selected.filter((category) => categories.has(category))
50
+ });
51
+ }, [categories, updateFilter]);
52
+ const handleChange = (key) => {
53
+ if (key === null) {
54
+ updateFilter({ categories: [] });
55
+ return;
56
+ }
57
+ if (!categories) return;
58
+ if (!Array.isArray(key)) {
59
+ updateFilter({
60
+ categories: categories.has(key) ? [key] : []
61
+ });
62
+ return;
63
+ }
64
+ const filtered = key.filter(
65
+ (item) => categories.has(item)
66
+ );
67
+ updateFilter({ categories: filtered });
68
+ };
69
+ return /* @__PURE__ */ jsx(
70
+ Select,
71
+ {
72
+ label: "Category",
73
+ selectionMode: "multiple",
74
+ options: categoryOptions,
75
+ value: selectedCategories,
76
+ onChange: handleChange,
77
+ placeholder: "All"
78
+ }
79
+ );
80
+ };
81
+
82
+ export { SelectCategory };
83
+ //# sourceMappingURL=SelectCategory.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SelectCategory.esm.js","sources":["../../../src/components/SelectCategory/SelectCategory.tsx"],"sourcesContent":["import { IdentifiedOption, Select, useAsyncList } from '@backstage/ui';\nimport { Key, useEffect, useMemo, useRef } from 'react';\nimport { usePolicyReportsFilters } from '../../hooks/usePolicyReportsFilters';\nimport { policyReporterApiRef } from '../../api';\nimport { toastApiRef, useApi } from '@backstage/frontend-plugin-api';\nimport { RequestError } from '@kyverno/backstage-plugin-policy-reporter-common';\n\nexport const SelectCategory = () => {\n const { filter, updateFilter, environment } = usePolicyReportsFilters();\n const api = useApi(policyReporterApiRef);\n const toast = useApi(toastApiRef);\n\n const categoryOptions = useAsyncList<IdentifiedOption>({\n async load({}) {\n if (!environment) return { items: [] };\n const response = await api.getCategories({ query: { environment } });\n const result = await response.json();\n\n if (!response.ok) {\n toast.post({\n title: 'Failed to fetch categories',\n description:\n (result as unknown as RequestError).error ||\n response.statusText ||\n `Request failed with status ${response.status}`,\n status: 'danger',\n });\n return { items: [] };\n }\n\n return {\n items: result.map(c => ({ id: c, label: c })),\n };\n },\n });\n\n const categories = useMemo(() => {\n if (categoryOptions.isLoading || categoryOptions.items.length === 0) {\n return undefined;\n }\n\n return new Set(categoryOptions.items.map(c => c.label));\n }, [categoryOptions.isLoading, categoryOptions.items]);\n\n // useRef to avoid dependency on the useAsyncList result\n const categoryRef = useRef(categoryOptions);\n categoryRef.current = categoryOptions;\n\n // Fetch sources again after changes to the environment\n useEffect(() => {\n categoryRef.current.reload();\n }, [environment]);\n\n const selectedCategories = filter.categories ?? [];\n\n // Keep a ref so the effect can read the latest selection without making it\n // a dependency — the effect should only trigger when available sources change.\n const selectedCategoriesRef = useRef(selectedCategories);\n selectedCategoriesRef.current = selectedCategories;\n\n useEffect(() => {\n if (!categories) return;\n\n const selected = selectedCategoriesRef.current;\n\n if (selected.every(category => categories.has(category))) return;\n\n updateFilter({\n categories: selected.filter(category => categories.has(category)),\n });\n }, [categories, updateFilter]);\n\n const handleChange = (key: Key | Key[] | null) => {\n if (key === null) {\n updateFilter({ categories: [] });\n return;\n }\n\n if (!categories) return;\n\n if (!Array.isArray(key)) {\n updateFilter({\n categories: categories.has(key as string) ? [key as string] : [],\n });\n return;\n }\n\n const filtered = key.filter((item): item is string =>\n categories.has(item as string),\n );\n updateFilter({ categories: filtered });\n };\n\n return (\n <Select\n label=\"Category\"\n selectionMode=\"multiple\"\n options={categoryOptions}\n value={selectedCategories}\n onChange={handleChange}\n placeholder=\"All\"\n />\n );\n};\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,83 @@
1
+ import { jsx } from 'react/jsx-runtime';
2
+ import { useAsyncList, Select } from '@backstage/ui';
3
+ import { useMemo, useRef, useEffect } from 'react';
4
+ import { usePolicyReportsFilters } from '../../hooks/usePolicyReportsFilters.esm.js';
5
+ import { policyReporterApiRef } from '../../api/PolicyReporterClient.esm.js';
6
+ import { useApi, toastApiRef } from '@backstage/frontend-plugin-api';
7
+
8
+ const SelectKind = () => {
9
+ const { filter, updateFilter, environment } = usePolicyReportsFilters();
10
+ const api = useApi(policyReporterApiRef);
11
+ const toast = useApi(toastApiRef);
12
+ const kindOptions = useAsyncList({
13
+ async load({}) {
14
+ if (!environment) return { items: [] };
15
+ const response = await api.getKinds({ query: { environment } });
16
+ const result = await response.json();
17
+ if (!response.ok) {
18
+ toast.post({
19
+ title: "Failed to fetch kinds",
20
+ description: result.error || response.statusText || `Request failed with status ${response.status}`,
21
+ status: "danger"
22
+ });
23
+ return { items: [] };
24
+ }
25
+ return {
26
+ items: result.map((k) => ({ id: k, label: k }))
27
+ };
28
+ }
29
+ });
30
+ const kinds = useMemo(() => {
31
+ if (kindOptions.isLoading || kindOptions.items.length === 0) {
32
+ return void 0;
33
+ }
34
+ return new Set(kindOptions.items.map((k) => k.label));
35
+ }, [kindOptions.isLoading, kindOptions.items]);
36
+ const kindRef = useRef(kindOptions);
37
+ kindRef.current = kindOptions;
38
+ useEffect(() => {
39
+ kindRef.current.reload();
40
+ }, [environment]);
41
+ const selectedKinds = filter.kinds ?? [];
42
+ const selectedKindsRef = useRef(selectedKinds);
43
+ selectedKindsRef.current = selectedKinds;
44
+ useEffect(() => {
45
+ if (!kinds) return;
46
+ const selected = selectedKindsRef.current;
47
+ if (selected.every((kind) => kinds.has(kind))) return;
48
+ updateFilter({
49
+ kinds: selected.filter((kind) => kinds.has(kind))
50
+ });
51
+ }, [kinds, updateFilter]);
52
+ const handleChange = (key) => {
53
+ if (key === null) {
54
+ updateFilter({ kinds: [] });
55
+ return;
56
+ }
57
+ if (!kinds) return;
58
+ if (!Array.isArray(key)) {
59
+ updateFilter({
60
+ kinds: kinds.has(key) ? [key] : []
61
+ });
62
+ return;
63
+ }
64
+ const filtered = key.filter(
65
+ (item) => kinds.has(item)
66
+ );
67
+ updateFilter({ kinds: filtered });
68
+ };
69
+ return /* @__PURE__ */ jsx(
70
+ Select,
71
+ {
72
+ label: "Kind",
73
+ selectionMode: "multiple",
74
+ options: kindOptions,
75
+ value: selectedKinds,
76
+ onChange: handleChange,
77
+ placeholder: "All"
78
+ }
79
+ );
80
+ };
81
+
82
+ export { SelectKind };
83
+ //# sourceMappingURL=SelectKind.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SelectKind.esm.js","sources":["../../../src/components/SelectKind/SelectKind.tsx"],"sourcesContent":["import { IdentifiedOption, Select, useAsyncList } from '@backstage/ui';\nimport { Key, useEffect, useMemo, useRef } from 'react';\nimport { usePolicyReportsFilters } from '../../hooks/usePolicyReportsFilters';\nimport { policyReporterApiRef } from '../../api';\nimport { toastApiRef, useApi } from '@backstage/frontend-plugin-api';\nimport { RequestError } from '@kyverno/backstage-plugin-policy-reporter-common';\n\nexport const SelectKind = () => {\n const { filter, updateFilter, environment } = usePolicyReportsFilters();\n const api = useApi(policyReporterApiRef);\n const toast = useApi(toastApiRef);\n\n const kindOptions = useAsyncList<IdentifiedOption>({\n async load({}) {\n if (!environment) return { items: [] };\n const response = await api.getKinds({ query: { environment } });\n const result = await response.json();\n\n if (!response.ok) {\n toast.post({\n title: 'Failed to fetch kinds',\n description:\n (result as unknown as RequestError).error ||\n response.statusText ||\n `Request failed with status ${response.status}`,\n status: 'danger',\n });\n return { items: [] };\n }\n\n return {\n items: result.map((k: string) => ({ id: k, label: k })),\n };\n },\n });\n\n const kinds = useMemo(() => {\n if (kindOptions.isLoading || kindOptions.items.length === 0) {\n return undefined;\n }\n\n return new Set(kindOptions.items.map(k => k.label));\n }, [kindOptions.isLoading, kindOptions.items]);\n\n const kindRef = useRef(kindOptions);\n kindRef.current = kindOptions;\n\n useEffect(() => {\n kindRef.current.reload();\n }, [environment]);\n\n const selectedKinds = filter.kinds ?? [];\n\n const selectedKindsRef = useRef(selectedKinds);\n selectedKindsRef.current = selectedKinds;\n\n useEffect(() => {\n if (!kinds) return;\n\n const selected = selectedKindsRef.current;\n\n if (selected.every(kind => kinds.has(kind))) return;\n\n updateFilter({\n kinds: selected.filter(kind => kinds.has(kind)),\n });\n }, [kinds, updateFilter]);\n\n const handleChange = (key: Key | Key[] | null) => {\n if (key === null) {\n updateFilter({ kinds: [] });\n return;\n }\n\n if (!kinds) return;\n\n if (!Array.isArray(key)) {\n updateFilter({\n kinds: kinds.has(key as string) ? [key as string] : [],\n });\n return;\n }\n\n const filtered = key.filter((item): item is string =>\n kinds.has(item as string),\n );\n updateFilter({ kinds: filtered });\n };\n\n return (\n <Select\n label=\"Kind\"\n selectionMode=\"multiple\"\n options={kindOptions}\n value={selectedKinds}\n onChange={handleChange}\n placeholder=\"All\"\n />\n );\n};\n"],"names":[],"mappings":"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"}
@@ -1,25 +1,69 @@
1
1
  import { jsx } from 'react/jsx-runtime';
2
- import { Select } from '@backstage/ui';
2
+ import { useAsyncList, Select } from '@backstage/ui';
3
+ import { useMemo, useRef, useEffect } from 'react';
3
4
  import { usePolicyReportsFilters } from '../../hooks/usePolicyReportsFilters.esm.js';
4
- import { useNamespaces } from '../../hooks/useNamespaces.esm.js';
5
+ import { useApi, toastApiRef } from '@backstage/frontend-plugin-api';
6
+ import { policyReporterApiRef } from '../../api/PolicyReporterClient.esm.js';
5
7
 
6
8
  const SelectNamespace = () => {
7
9
  const { filter, updateFilter, environment } = usePolicyReportsFilters();
8
- const { namespaces: availableNamespaces } = useNamespaces(environment);
9
- const options = availableNamespaces.map((ns) => ({ value: ns, label: ns }));
10
+ const api = useApi(policyReporterApiRef);
11
+ const toast = useApi(toastApiRef);
12
+ const namespaceOptions = useAsyncList({
13
+ async load({}) {
14
+ if (!environment) return { items: [] };
15
+ const response = await api.getNamespaces({ query: { environment } });
16
+ const result = await response.json();
17
+ if (!response.ok) {
18
+ toast.post({
19
+ title: "Failed to fetch namespaces",
20
+ description: result.error || response.statusText || `Request failed with status ${response.status}`,
21
+ status: "danger"
22
+ });
23
+ return { items: [] };
24
+ }
25
+ const namespaces2 = Array.isArray(result) ? result : [];
26
+ return {
27
+ items: namespaces2.map((ns) => ({ id: ns, label: ns }))
28
+ };
29
+ }
30
+ });
31
+ const namespaces = useMemo(() => {
32
+ if (namespaceOptions.isLoading || namespaceOptions.items.length === 0) {
33
+ return void 0;
34
+ }
35
+ return new Set(namespaceOptions.items.map((ns) => ns.label));
36
+ }, [namespaceOptions.isLoading, namespaceOptions.items]);
37
+ const namespacesRef = useRef(namespaceOptions);
38
+ namespacesRef.current = namespaceOptions;
39
+ useEffect(() => {
40
+ namespacesRef.current.reload();
41
+ }, [environment]);
42
+ const selectedNamespaces = filter.namespaces ?? [];
43
+ const selectedNamespacesRef = useRef(selectedNamespaces);
44
+ selectedNamespacesRef.current = selectedNamespaces;
45
+ useEffect(() => {
46
+ if (!namespaces) return;
47
+ const selected = selectedNamespacesRef.current;
48
+ if (selected.every((ns) => namespaces.has(ns))) return;
49
+ updateFilter({
50
+ namespaces: selected.filter((ns) => namespaces.has(ns))
51
+ });
52
+ }, [namespaces, updateFilter]);
10
53
  const handleChange = (key) => {
11
54
  if (key === null) {
12
55
  updateFilter({ namespaces: [] });
13
56
  return;
14
57
  }
58
+ if (!namespaces) return;
15
59
  if (!Array.isArray(key)) {
16
60
  updateFilter({
17
- namespaces: availableNamespaces.includes(key) ? [key] : []
61
+ namespaces: namespaces.has(key) ? [key] : []
18
62
  });
19
63
  return;
20
64
  }
21
65
  const filtered = key.filter(
22
- (item) => availableNamespaces.includes(item)
66
+ (item) => namespaces.has(item)
23
67
  );
24
68
  updateFilter({ namespaces: filtered });
25
69
  };
@@ -28,11 +72,10 @@ const SelectNamespace = () => {
28
72
  {
29
73
  label: "Namespace",
30
74
  selectionMode: "multiple",
31
- options,
32
- value: filter.namespaces ?? [],
75
+ options: namespaceOptions,
76
+ value: selectedNamespaces,
33
77
  onChange: handleChange,
34
- placeholder: "All",
35
- style: { width: 200 }
78
+ placeholder: "All"
36
79
  }
37
80
  );
38
81
  };
@@ -1 +1 @@
1
- {"version":3,"file":"SelectNamespace.esm.js","sources":["../../../src/components/SelectNamespace/SelectNamespace.tsx"],"sourcesContent":["import { Select } from '@backstage/ui';\nimport { Key } from 'react';\nimport { usePolicyReportsFilters } from '../../hooks/usePolicyReportsFilters';\nimport { useNamespaces } from '../../hooks/useNamespaces';\n\nexport const SelectNamespace = () => {\n const { filter, updateFilter, environment } = usePolicyReportsFilters();\n const { namespaces: availableNamespaces } = useNamespaces(environment);\n\n const options = availableNamespaces.map(ns => ({ value: ns, label: ns }));\n\n const handleChange = (key: Key | Key[] | null) => {\n if (key === null) {\n updateFilter({ namespaces: [] });\n return;\n }\n\n if (!Array.isArray(key)) {\n updateFilter({\n namespaces: availableNamespaces.includes(key as string)\n ? [key as string]\n : [],\n });\n return;\n }\n\n const filtered = key.filter((item): item is string =>\n availableNamespaces.includes(item as string),\n );\n updateFilter({ namespaces: filtered });\n };\n\n return (\n <Select\n label=\"Namespace\"\n selectionMode=\"multiple\"\n options={options}\n value={filter.namespaces ?? []}\n onChange={handleChange}\n placeholder=\"All\"\n style={{ width: 200 }}\n />\n );\n};\n"],"names":[],"mappings":";;;;;AAKO,MAAM,kBAAkB,MAAM;AACnC,EAAA,MAAM,EAAE,MAAA,EAAQ,YAAA,EAAc,WAAA,KAAgB,uBAAA,EAAwB;AACtE,EAAA,MAAM,EAAE,UAAA,EAAY,mBAAA,EAAoB,GAAI,cAAc,WAAW,CAAA;AAErE,EAAA,MAAM,OAAA,GAAU,oBAAoB,GAAA,CAAI,CAAA,EAAA,MAAO,EAAE,KAAA,EAAO,EAAA,EAAI,KAAA,EAAO,EAAA,EAAG,CAAE,CAAA;AAExE,EAAA,MAAM,YAAA,GAAe,CAAC,GAAA,KAA4B;AAChD,IAAA,IAAI,QAAQ,IAAA,EAAM;AAChB,MAAA,YAAA,CAAa,EAAE,UAAA,EAAY,EAAC,EAAG,CAAA;AAC/B,MAAA;AAAA,IACF;AAEA,IAAA,IAAI,CAAC,KAAA,CAAM,OAAA,CAAQ,GAAG,CAAA,EAAG;AACvB,MAAA,YAAA,CAAa;AAAA,QACX,UAAA,EAAY,oBAAoB,QAAA,CAAS,GAAa,IAClD,CAAC,GAAa,IACd;AAAC,OACN,CAAA;AACD,MAAA;AAAA,IACF;AAEA,IAAA,MAAM,WAAW,GAAA,CAAI,MAAA;AAAA,MAAO,CAAC,IAAA,KAC3B,mBAAA,CAAoB,QAAA,CAAS,IAAc;AAAA,KAC7C;AACA,IAAA,YAAA,CAAa,EAAE,UAAA,EAAY,QAAA,EAAU,CAAA;AAAA,EACvC,CAAA;AAEA,EAAA,uBACE,GAAA;AAAA,IAAC,MAAA;AAAA,IAAA;AAAA,MACC,KAAA,EAAM,WAAA;AAAA,MACN,aAAA,EAAc,UAAA;AAAA,MACd,OAAA;AAAA,MACA,KAAA,EAAO,MAAA,CAAO,UAAA,IAAc,EAAC;AAAA,MAC7B,QAAA,EAAU,YAAA;AAAA,MACV,WAAA,EAAY,KAAA;AAAA,MACZ,KAAA,EAAO,EAAE,KAAA,EAAO,GAAA;AAAI;AAAA,GACtB;AAEJ;;;;"}
1
+ {"version":3,"file":"SelectNamespace.esm.js","sources":["../../../src/components/SelectNamespace/SelectNamespace.tsx"],"sourcesContent":["import { IdentifiedOption, Select, useAsyncList } from '@backstage/ui';\nimport { Key, useEffect, useMemo, useRef } from 'react';\nimport { usePolicyReportsFilters } from '../../hooks/usePolicyReportsFilters';\nimport { toastApiRef, useApi } from '@backstage/frontend-plugin-api';\nimport { policyReporterApiRef } from '../../api';\nimport { RequestError } from '@kyverno/backstage-plugin-policy-reporter-common';\n\nexport const SelectNamespace = () => {\n const { filter, updateFilter, environment } = usePolicyReportsFilters();\n const api = useApi(policyReporterApiRef);\n const toast = useApi(toastApiRef);\n\n const namespaceOptions = useAsyncList<IdentifiedOption>({\n async load({}) {\n if (!environment) return { items: [] };\n const response = await api.getNamespaces({ query: { environment } });\n const result = await response.json();\n\n if (!response.ok) {\n toast.post({\n title: 'Failed to fetch namespaces',\n description:\n (result as unknown as RequestError).error ||\n response.statusText ||\n `Request failed with status ${response.status}`,\n status: 'danger',\n });\n return { items: [] };\n }\n\n const namespaces = Array.isArray(result) ? result : [];\n\n return {\n items: namespaces.map(ns => ({ id: ns, label: ns })),\n };\n },\n });\n\n const namespaces = useMemo(() => {\n if (namespaceOptions.isLoading || namespaceOptions.items.length === 0) {\n return undefined;\n }\n\n return new Set(namespaceOptions.items.map(ns => ns.label));\n }, [namespaceOptions.isLoading, namespaceOptions.items]);\n\n // useRef to avoid dependency on the useAsyncList result\n const namespacesRef = useRef(namespaceOptions);\n namespacesRef.current = namespaceOptions;\n\n // Fetch namespaces again after changes to the environment\n useEffect(() => {\n namespacesRef.current.reload();\n }, [environment]);\n\n const selectedNamespaces = filter.namespaces ?? [];\n\n // Keep a ref so the effect can read the latest selection without making it\n // a dependency — the effect should only trigger when available namespaces change.\n const selectedNamespacesRef = useRef(selectedNamespaces);\n selectedNamespacesRef.current = selectedNamespaces;\n\n useEffect(() => {\n if (!namespaces) return;\n\n const selected = selectedNamespacesRef.current;\n\n if (selected.every(ns => namespaces.has(ns))) return;\n\n updateFilter({\n namespaces: selected.filter(ns => namespaces.has(ns)),\n });\n }, [namespaces, updateFilter]);\n\n const handleChange = (key: Key | Key[] | null) => {\n if (key === null) {\n updateFilter({ namespaces: [] });\n return;\n }\n\n if (!namespaces) return;\n\n if (!Array.isArray(key)) {\n updateFilter({\n namespaces: namespaces.has(key as string) ? [key as string] : [],\n });\n return;\n }\n\n const filtered = key.filter((item): item is string =>\n namespaces.has(item as string),\n );\n updateFilter({ namespaces: filtered });\n };\n\n return (\n <Select\n label=\"Namespace\"\n selectionMode=\"multiple\"\n options={namespaceOptions}\n value={selectedNamespaces}\n onChange={handleChange}\n placeholder=\"All\"\n />\n );\n};\n"],"names":["namespaces"],"mappings":"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"}
@@ -0,0 +1,83 @@
1
+ import { jsx } from 'react/jsx-runtime';
2
+ import { useAsyncList, Select } from '@backstage/ui';
3
+ import { useMemo, useRef, useEffect } from 'react';
4
+ import { usePolicyReportsFilters } from '../../hooks/usePolicyReportsFilters.esm.js';
5
+ import { policyReporterApiRef } from '../../api/PolicyReporterClient.esm.js';
6
+ import { useApi, toastApiRef } from '@backstage/frontend-plugin-api';
7
+
8
+ const SelectPolicy = () => {
9
+ const { filter, updateFilter, environment } = usePolicyReportsFilters();
10
+ const api = useApi(policyReporterApiRef);
11
+ const toast = useApi(toastApiRef);
12
+ const policyOptions = useAsyncList({
13
+ async load({}) {
14
+ if (!environment) return { items: [] };
15
+ const response = await api.getPolicies({ query: { environment } });
16
+ const result = await response.json();
17
+ if (!response.ok) {
18
+ toast.post({
19
+ title: "Failed to fetch policies",
20
+ description: result.error || response.statusText || `Request failed with status ${response.status}`,
21
+ status: "danger"
22
+ });
23
+ return { items: [] };
24
+ }
25
+ return {
26
+ items: result.map((s) => ({ id: s, label: s }))
27
+ };
28
+ }
29
+ });
30
+ const policies = useMemo(() => {
31
+ if (policyOptions.isLoading || policyOptions.items.length === 0) {
32
+ return void 0;
33
+ }
34
+ return new Set(policyOptions.items.map((s) => s.label));
35
+ }, [policyOptions.items, policyOptions.isLoading]);
36
+ const policyRef = useRef(policyOptions);
37
+ policyRef.current = policyOptions;
38
+ useEffect(() => {
39
+ policyRef.current.reload();
40
+ }, [environment]);
41
+ const selectedPolicies = filter.policies ?? [];
42
+ const selectedPoliciesRef = useRef(selectedPolicies);
43
+ selectedPoliciesRef.current = selectedPolicies;
44
+ useEffect(() => {
45
+ if (!policies) return;
46
+ const selected = selectedPoliciesRef.current;
47
+ if (selected.every((policy) => policies.has(policy))) return;
48
+ updateFilter({
49
+ policies: selected.filter((policy) => policies.has(policy))
50
+ });
51
+ }, [policies, updateFilter]);
52
+ const handleChange = (key) => {
53
+ if (!policies) return;
54
+ if (key === null) {
55
+ updateFilter({ policies: [] });
56
+ return;
57
+ }
58
+ if (!Array.isArray(key)) {
59
+ updateFilter({
60
+ policies: policies.has(key) ? [key] : []
61
+ });
62
+ return;
63
+ }
64
+ const filtered = key.filter(
65
+ (item) => policies.has(item)
66
+ );
67
+ updateFilter({ policies: filtered });
68
+ };
69
+ return /* @__PURE__ */ jsx(
70
+ Select,
71
+ {
72
+ label: "Policy",
73
+ selectionMode: "multiple",
74
+ options: policyOptions,
75
+ value: selectedPolicies,
76
+ onChange: handleChange,
77
+ placeholder: "All"
78
+ }
79
+ );
80
+ };
81
+
82
+ export { SelectPolicy };
83
+ //# sourceMappingURL=SelectPolicy.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SelectPolicy.esm.js","sources":["../../../src/components/SelectPolicy/SelectPolicy.tsx"],"sourcesContent":["import { IdentifiedOption, Select, useAsyncList } from '@backstage/ui';\nimport { Key, useEffect, useMemo, useRef } from 'react';\nimport { usePolicyReportsFilters } from '../../hooks/usePolicyReportsFilters';\nimport { policyReporterApiRef } from '../../api';\nimport { toastApiRef, useApi } from '@backstage/frontend-plugin-api';\nimport { RequestError } from '@kyverno/backstage-plugin-policy-reporter-common';\n\nexport const SelectPolicy = () => {\n const { filter, updateFilter, environment } = usePolicyReportsFilters();\n const api = useApi(policyReporterApiRef);\n const toast = useApi(toastApiRef);\n\n const policyOptions = useAsyncList<IdentifiedOption>({\n async load({}) {\n if (!environment) return { items: [] };\n const response = await api.getPolicies({ query: { environment } });\n const result = await response.json();\n\n if (!response.ok) {\n toast.post({\n title: 'Failed to fetch policies',\n description:\n (result as unknown as RequestError).error ||\n response.statusText ||\n `Request failed with status ${response.status}`,\n status: 'danger',\n });\n return { items: [] };\n }\n\n return {\n items: result.map(s => ({ id: s, label: s })),\n };\n },\n });\n\n const policies = useMemo(() => {\n if (policyOptions.isLoading || policyOptions.items.length === 0) {\n return undefined;\n }\n\n return new Set(policyOptions.items.map(s => s.label));\n }, [policyOptions.items, policyOptions.isLoading]);\n\n // useRef to avoid dependency on the useAsyncList result\n const policyRef = useRef(policyOptions);\n policyRef.current = policyOptions;\n\n // Fetch policies again after changes to the environment\n useEffect(() => {\n policyRef.current.reload();\n }, [environment]);\n\n const selectedPolicies = filter.policies ?? [];\n\n // Keep a ref so the effect can read the latest selection without making it\n // a dependency — the effect should only trigger when available policies change.\n const selectedPoliciesRef = useRef(selectedPolicies);\n selectedPoliciesRef.current = selectedPolicies;\n\n useEffect(() => {\n if (!policies) return;\n const selected = selectedPoliciesRef.current;\n\n if (selected.every(policy => policies.has(policy))) return;\n\n updateFilter({\n policies: selected.filter(policy => policies.has(policy)),\n });\n }, [policies, updateFilter]);\n\n const handleChange = (key: Key | Key[] | null) => {\n if (!policies) return;\n\n if (key === null) {\n updateFilter({ policies: [] });\n return;\n }\n\n if (!Array.isArray(key)) {\n updateFilter({\n policies: policies.has(key as string) ? [key as string] : [],\n });\n return;\n }\n\n const filtered = key.filter((item): item is string =>\n policies.has(item as string),\n );\n updateFilter({ policies: filtered });\n };\n\n return (\n <Select\n label=\"Policy\"\n selectionMode=\"multiple\"\n options={policyOptions}\n value={selectedPolicies}\n onChange={handleChange}\n placeholder=\"All\"\n />\n );\n};\n"],"names":[],"mappings":"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"}
@@ -37,8 +37,7 @@ const SelectSeverity = () => {
37
37
  options: SEVERITY_OPTIONS,
38
38
  value: filter.severities ?? [],
39
39
  onChange: handleChange,
40
- placeholder: "All",
41
- style: { width: 200 }
40
+ placeholder: "All"
42
41
  }
43
42
  );
44
43
  };
@@ -1 +1 @@
1
- {"version":3,"file":"SelectSeverity.esm.js","sources":["../../../src/components/SelectSeverity/SelectSeverity.tsx"],"sourcesContent":["import { Select } from '@backstage/ui';\nimport { Severity } from '@kyverno/backstage-plugin-policy-reporter-common';\nimport { Key } from 'react';\nimport { usePolicyReportsFilters } from '../../hooks/usePolicyReportsFilters';\n\nconst SEVERITY_OPTIONS: { value: Severity; label: string }[] = [\n { value: 'unknown', label: 'Unknown' },\n { value: 'low', label: 'Low' },\n { value: 'medium', label: 'Medium' },\n { value: 'high', label: 'High' },\n { value: 'critical', label: 'Critical' },\n { value: 'info', label: 'Info' },\n];\n\nconst validSeverities = SEVERITY_OPTIONS.map(option => option.value);\n\nexport const SelectSeverity = () => {\n const { filter, updateFilter } = usePolicyReportsFilters();\n\n const handleChange = (key: Key | Key[] | null) => {\n if (key === null) {\n updateFilter({ severities: [] });\n return;\n }\n\n if (!Array.isArray(key)) {\n updateFilter({\n severities: validSeverities.includes(key as Severity)\n ? [key as Severity]\n : [],\n });\n return;\n }\n\n const filteredSeverities = key.filter((item): item is Severity =>\n validSeverities.includes(item as Severity),\n );\n updateFilter({ severities: filteredSeverities });\n };\n\n return (\n <Select\n label=\"Severity\"\n selectionMode=\"multiple\"\n options={SEVERITY_OPTIONS}\n value={filter.severities ?? []}\n onChange={handleChange}\n placeholder=\"All\"\n style={{ width: 200 }}\n />\n );\n};\n"],"names":[],"mappings":";;;;AAKA,MAAM,gBAAA,GAAyD;AAAA,EAC7D,EAAE,KAAA,EAAO,SAAA,EAAW,KAAA,EAAO,SAAA,EAAU;AAAA,EACrC,EAAE,KAAA,EAAO,KAAA,EAAO,KAAA,EAAO,KAAA,EAAM;AAAA,EAC7B,EAAE,KAAA,EAAO,QAAA,EAAU,KAAA,EAAO,QAAA,EAAS;AAAA,EACnC,EAAE,KAAA,EAAO,MAAA,EAAQ,KAAA,EAAO,MAAA,EAAO;AAAA,EAC/B,EAAE,KAAA,EAAO,UAAA,EAAY,KAAA,EAAO,UAAA,EAAW;AAAA,EACvC,EAAE,KAAA,EAAO,MAAA,EAAQ,KAAA,EAAO,MAAA;AAC1B,CAAA;AAEA,MAAM,eAAA,GAAkB,gBAAA,CAAiB,GAAA,CAAI,CAAA,MAAA,KAAU,OAAO,KAAK,CAAA;AAE5D,MAAM,iBAAiB,MAAM;AAClC,EAAA,MAAM,EAAE,MAAA,EAAQ,YAAA,EAAa,GAAI,uBAAA,EAAwB;AAEzD,EAAA,MAAM,YAAA,GAAe,CAAC,GAAA,KAA4B;AAChD,IAAA,IAAI,QAAQ,IAAA,EAAM;AAChB,MAAA,YAAA,CAAa,EAAE,UAAA,EAAY,EAAC,EAAG,CAAA;AAC/B,MAAA;AAAA,IACF;AAEA,IAAA,IAAI,CAAC,KAAA,CAAM,OAAA,CAAQ,GAAG,CAAA,EAAG;AACvB,MAAA,YAAA,CAAa;AAAA,QACX,UAAA,EAAY,gBAAgB,QAAA,CAAS,GAAe,IAChD,CAAC,GAAe,IAChB;AAAC,OACN,CAAA;AACD,MAAA;AAAA,IACF;AAEA,IAAA,MAAM,qBAAqB,GAAA,CAAI,MAAA;AAAA,MAAO,CAAC,IAAA,KACrC,eAAA,CAAgB,QAAA,CAAS,IAAgB;AAAA,KAC3C;AACA,IAAA,YAAA,CAAa,EAAE,UAAA,EAAY,kBAAA,EAAoB,CAAA;AAAA,EACjD,CAAA;AAEA,EAAA,uBACE,GAAA;AAAA,IAAC,MAAA;AAAA,IAAA;AAAA,MACC,KAAA,EAAM,UAAA;AAAA,MACN,aAAA,EAAc,UAAA;AAAA,MACd,OAAA,EAAS,gBAAA;AAAA,MACT,KAAA,EAAO,MAAA,CAAO,UAAA,IAAc,EAAC;AAAA,MAC7B,QAAA,EAAU,YAAA;AAAA,MACV,WAAA,EAAY,KAAA;AAAA,MACZ,KAAA,EAAO,EAAE,KAAA,EAAO,GAAA;AAAI;AAAA,GACtB;AAEJ;;;;"}
1
+ {"version":3,"file":"SelectSeverity.esm.js","sources":["../../../src/components/SelectSeverity/SelectSeverity.tsx"],"sourcesContent":["import { Select } from '@backstage/ui';\nimport { Severity } from '@kyverno/backstage-plugin-policy-reporter-common';\nimport { Key } from 'react';\nimport { usePolicyReportsFilters } from '../../hooks/usePolicyReportsFilters';\n\nconst SEVERITY_OPTIONS: { value: Severity; label: string }[] = [\n { value: 'unknown', label: 'Unknown' },\n { value: 'low', label: 'Low' },\n { value: 'medium', label: 'Medium' },\n { value: 'high', label: 'High' },\n { value: 'critical', label: 'Critical' },\n { value: 'info', label: 'Info' },\n];\n\nconst validSeverities = SEVERITY_OPTIONS.map(option => option.value);\n\nexport const SelectSeverity = () => {\n const { filter, updateFilter } = usePolicyReportsFilters();\n\n const handleChange = (key: Key | Key[] | null) => {\n if (key === null) {\n updateFilter({ severities: [] });\n return;\n }\n\n if (!Array.isArray(key)) {\n updateFilter({\n severities: validSeverities.includes(key as Severity)\n ? [key as Severity]\n : [],\n });\n return;\n }\n\n const filteredSeverities = key.filter((item): item is Severity =>\n validSeverities.includes(item as Severity),\n );\n updateFilter({ severities: filteredSeverities });\n };\n\n return (\n <Select\n label=\"Severity\"\n selectionMode=\"multiple\"\n options={SEVERITY_OPTIONS}\n value={filter.severities ?? []}\n onChange={handleChange}\n placeholder=\"All\"\n />\n );\n};\n"],"names":[],"mappings":";;;;AAKA,MAAM,gBAAA,GAAyD;AAAA,EAC7D,EAAE,KAAA,EAAO,SAAA,EAAW,KAAA,EAAO,SAAA,EAAU;AAAA,EACrC,EAAE,KAAA,EAAO,KAAA,EAAO,KAAA,EAAO,KAAA,EAAM;AAAA,EAC7B,EAAE,KAAA,EAAO,QAAA,EAAU,KAAA,EAAO,QAAA,EAAS;AAAA,EACnC,EAAE,KAAA,EAAO,MAAA,EAAQ,KAAA,EAAO,MAAA,EAAO;AAAA,EAC/B,EAAE,KAAA,EAAO,UAAA,EAAY,KAAA,EAAO,UAAA,EAAW;AAAA,EACvC,EAAE,KAAA,EAAO,MAAA,EAAQ,KAAA,EAAO,MAAA;AAC1B,CAAA;AAEA,MAAM,eAAA,GAAkB,gBAAA,CAAiB,GAAA,CAAI,CAAA,MAAA,KAAU,OAAO,KAAK,CAAA;AAE5D,MAAM,iBAAiB,MAAM;AAClC,EAAA,MAAM,EAAE,MAAA,EAAQ,YAAA,EAAa,GAAI,uBAAA,EAAwB;AAEzD,EAAA,MAAM,YAAA,GAAe,CAAC,GAAA,KAA4B;AAChD,IAAA,IAAI,QAAQ,IAAA,EAAM;AAChB,MAAA,YAAA,CAAa,EAAE,UAAA,EAAY,EAAC,EAAG,CAAA;AAC/B,MAAA;AAAA,IACF;AAEA,IAAA,IAAI,CAAC,KAAA,CAAM,OAAA,CAAQ,GAAG,CAAA,EAAG;AACvB,MAAA,YAAA,CAAa;AAAA,QACX,UAAA,EAAY,gBAAgB,QAAA,CAAS,GAAe,IAChD,CAAC,GAAe,IAChB;AAAC,OACN,CAAA;AACD,MAAA;AAAA,IACF;AAEA,IAAA,MAAM,qBAAqB,GAAA,CAAI,MAAA;AAAA,MAAO,CAAC,IAAA,KACrC,eAAA,CAAgB,QAAA,CAAS,IAAgB;AAAA,KAC3C;AACA,IAAA,YAAA,CAAa,EAAE,UAAA,EAAY,kBAAA,EAAoB,CAAA;AAAA,EACjD,CAAA;AAEA,EAAA,uBACE,GAAA;AAAA,IAAC,MAAA;AAAA,IAAA;AAAA,MACC,KAAA,EAAM,UAAA;AAAA,MACN,aAAA,EAAc,UAAA;AAAA,MACd,OAAA,EAAS,gBAAA;AAAA,MACT,KAAA,EAAO,MAAA,CAAO,UAAA,IAAc,EAAC;AAAA,MAC7B,QAAA,EAAU,YAAA;AAAA,MACV,WAAA,EAAY;AAAA;AAAA,GACd;AAEJ;;;;"}
@@ -0,0 +1,83 @@
1
+ import { jsx } from 'react/jsx-runtime';
2
+ import { useAsyncList, Select } from '@backstage/ui';
3
+ import { useMemo, useRef, useEffect } from 'react';
4
+ import { usePolicyReportsFilters } from '../../hooks/usePolicyReportsFilters.esm.js';
5
+ import { policyReporterApiRef } from '../../api/PolicyReporterClient.esm.js';
6
+ import { useApi, toastApiRef } from '@backstage/frontend-plugin-api';
7
+
8
+ const SelectSource = () => {
9
+ const { filter, updateFilter, environment } = usePolicyReportsFilters();
10
+ const api = useApi(policyReporterApiRef);
11
+ const toast = useApi(toastApiRef);
12
+ const sourceOptions = useAsyncList({
13
+ async load({}) {
14
+ if (!environment) return { items: [] };
15
+ const response = await api.getSources({ query: { environment } });
16
+ const result = await response.json();
17
+ if (!response.ok) {
18
+ toast.post({
19
+ title: "Failed to fetch sources",
20
+ description: result.error || response.statusText || `Request failed with status ${response.status}`,
21
+ status: "danger"
22
+ });
23
+ return { items: [] };
24
+ }
25
+ return {
26
+ items: result.map((s) => ({ id: s, label: s }))
27
+ };
28
+ }
29
+ });
30
+ const sources = useMemo(() => {
31
+ if (sourceOptions.isLoading || sourceOptions.items.length === 0) {
32
+ return void 0;
33
+ }
34
+ return new Set(sourceOptions.items.map((s) => s.label));
35
+ }, [sourceOptions.items, sourceOptions.isLoading]);
36
+ const sourceRef = useRef(sourceOptions);
37
+ sourceRef.current = sourceOptions;
38
+ useEffect(() => {
39
+ sourceRef.current.reload();
40
+ }, [environment]);
41
+ const selectedSources = filter.sources ?? [];
42
+ const selectedSourcesRef = useRef(selectedSources);
43
+ selectedSourcesRef.current = selectedSources;
44
+ useEffect(() => {
45
+ if (!sources) return;
46
+ const selected = selectedSourcesRef.current;
47
+ if (selected.every((source) => sources.has(source))) return;
48
+ updateFilter({
49
+ sources: selected.filter((source) => sources.has(source))
50
+ });
51
+ }, [sources, updateFilter]);
52
+ const handleChange = (key) => {
53
+ if (!sources) return;
54
+ if (key === null) {
55
+ updateFilter({ sources: [] });
56
+ return;
57
+ }
58
+ if (!Array.isArray(key)) {
59
+ updateFilter({
60
+ sources: sources.has(key) ? [key] : []
61
+ });
62
+ return;
63
+ }
64
+ const filtered = key.filter(
65
+ (item) => sources.has(item)
66
+ );
67
+ updateFilter({ sources: filtered });
68
+ };
69
+ return /* @__PURE__ */ jsx(
70
+ Select,
71
+ {
72
+ label: "Source",
73
+ selectionMode: "multiple",
74
+ options: sourceOptions,
75
+ value: selectedSources,
76
+ onChange: handleChange,
77
+ placeholder: "All"
78
+ }
79
+ );
80
+ };
81
+
82
+ export { SelectSource };
83
+ //# sourceMappingURL=SelectSource.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SelectSource.esm.js","sources":["../../../src/components/SelectSource/SelectSource.tsx"],"sourcesContent":["import { IdentifiedOption, Select, useAsyncList } from '@backstage/ui';\nimport { Key, useEffect, useMemo, useRef } from 'react';\nimport { usePolicyReportsFilters } from '../../hooks/usePolicyReportsFilters';\nimport { policyReporterApiRef } from '../../api';\nimport { toastApiRef, useApi } from '@backstage/frontend-plugin-api';\nimport { RequestError } from '@kyverno/backstage-plugin-policy-reporter-common';\n\nexport const SelectSource = () => {\n const { filter, updateFilter, environment } = usePolicyReportsFilters();\n const api = useApi(policyReporterApiRef);\n const toast = useApi(toastApiRef);\n\n const sourceOptions = useAsyncList<IdentifiedOption>({\n async load({}) {\n if (!environment) return { items: [] };\n const response = await api.getSources({ query: { environment } });\n const result = await response.json();\n\n if (!response.ok) {\n toast.post({\n title: 'Failed to fetch sources',\n description:\n (result as unknown as RequestError).error ||\n response.statusText ||\n `Request failed with status ${response.status}`,\n status: 'danger',\n });\n return { items: [] };\n }\n\n return {\n items: result.map(s => ({ id: s, label: s })),\n };\n },\n });\n\n const sources = useMemo(() => {\n if (sourceOptions.isLoading || sourceOptions.items.length === 0) {\n return undefined;\n }\n return new Set(sourceOptions.items.map(s => s.label));\n }, [sourceOptions.items, sourceOptions.isLoading]);\n\n // useRef to avoid dependency on the useAsyncList result\n const sourceRef = useRef(sourceOptions);\n sourceRef.current = sourceOptions;\n\n // Fetch sources again after changes to the environment\n useEffect(() => {\n sourceRef.current.reload();\n }, [environment]);\n\n const selectedSources = filter.sources ?? [];\n\n // Keep a ref so the effect can read the latest selection without making it\n // a dependency — the effect should only trigger when available sources change.\n const selectedSourcesRef = useRef(selectedSources);\n selectedSourcesRef.current = selectedSources;\n\n useEffect(() => {\n if (!sources) return;\n const selected = selectedSourcesRef.current;\n\n if (selected.every(source => sources.has(source))) return;\n\n updateFilter({\n sources: selected.filter(source => sources.has(source)),\n });\n }, [sources, updateFilter]);\n\n const handleChange = (key: Key | Key[] | null) => {\n if (!sources) return;\n\n if (key === null) {\n updateFilter({ sources: [] });\n return;\n }\n\n if (!Array.isArray(key)) {\n updateFilter({\n sources: sources.has(key as string) ? [key as string] : [],\n });\n return;\n }\n\n const filtered = key.filter((item): item is string =>\n sources.has(item as string),\n );\n updateFilter({ sources: filtered });\n };\n\n return (\n <Select\n label=\"Source\"\n selectionMode=\"multiple\"\n options={sourceOptions}\n value={selectedSources}\n onChange={handleChange}\n placeholder=\"All\"\n />\n );\n};\n"],"names":[],"mappings":";;;;;;;AAOO,MAAM,eAAe,MAAM;AAChC,EAAA,MAAM,EAAE,MAAA,EAAQ,YAAA,EAAc,WAAA,KAAgB,uBAAA,EAAwB;AACtE,EAAA,MAAM,GAAA,GAAM,OAAO,oBAAoB,CAAA;AACvC,EAAA,MAAM,KAAA,GAAQ,OAAO,WAAW,CAAA;AAEhC,EAAA,MAAM,gBAAgB,YAAA,CAA+B;AAAA,IACnD,MAAM,IAAA,CAAK,EAAC,EAAG;AACb,MAAA,IAAI,CAAC,WAAA,EAAa,OAAO,EAAE,KAAA,EAAO,EAAC,EAAE;AACrC,MAAA,MAAM,QAAA,GAAW,MAAM,GAAA,CAAI,UAAA,CAAW,EAAE,KAAA,EAAO,EAAE,WAAA,EAAY,EAAG,CAAA;AAChE,MAAA,MAAM,MAAA,GAAS,MAAM,QAAA,CAAS,IAAA,EAAK;AAEnC,MAAA,IAAI,CAAC,SAAS,EAAA,EAAI;AAChB,QAAA,KAAA,CAAM,IAAA,CAAK;AAAA,UACT,KAAA,EAAO,yBAAA;AAAA,UACP,aACG,MAAA,CAAmC,KAAA,IACpC,SAAS,UAAA,IACT,CAAA,2BAAA,EAA8B,SAAS,MAAM,CAAA,CAAA;AAAA,UAC/C,MAAA,EAAQ;AAAA,SACT,CAAA;AACD,QAAA,OAAO,EAAE,KAAA,EAAO,EAAC,EAAE;AAAA,MACrB;AAEA,MAAA,OAAO;AAAA,QACL,KAAA,EAAO,OAAO,GAAA,CAAI,CAAA,CAAA,MAAM,EAAE,EAAA,EAAI,CAAA,EAAG,KAAA,EAAO,CAAA,EAAE,CAAE;AAAA,OAC9C;AAAA,IACF;AAAA,GACD,CAAA;AAED,EAAA,MAAM,OAAA,GAAU,QAAQ,MAAM;AAC5B,IAAA,IAAI,aAAA,CAAc,SAAA,IAAa,aAAA,CAAc,KAAA,CAAM,WAAW,CAAA,EAAG;AAC/D,MAAA,OAAO,MAAA;AAAA,IACT;AACA,IAAA,OAAO,IAAI,IAAI,aAAA,CAAc,KAAA,CAAM,IAAI,CAAA,CAAA,KAAK,CAAA,CAAE,KAAK,CAAC,CAAA;AAAA,EACtD,GAAG,CAAC,aAAA,CAAc,KAAA,EAAO,aAAA,CAAc,SAAS,CAAC,CAAA;AAGjD,EAAA,MAAM,SAAA,GAAY,OAAO,aAAa,CAAA;AACtC,EAAA,SAAA,CAAU,OAAA,GAAU,aAAA;AAGpB,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,SAAA,CAAU,QAAQ,MAAA,EAAO;AAAA,EAC3B,CAAA,EAAG,CAAC,WAAW,CAAC,CAAA;AAEhB,EAAA,MAAM,eAAA,GAAkB,MAAA,CAAO,OAAA,IAAW,EAAC;AAI3C,EAAA,MAAM,kBAAA,GAAqB,OAAO,eAAe,CAAA;AACjD,EAAA,kBAAA,CAAmB,OAAA,GAAU,eAAA;AAE7B,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,IAAI,CAAC,OAAA,EAAS;AACd,IAAA,MAAM,WAAW,kBAAA,CAAmB,OAAA;AAEpC,IAAA,IAAI,SAAS,KAAA,CAAM,CAAA,MAAA,KAAU,QAAQ,GAAA,CAAI,MAAM,CAAC,CAAA,EAAG;AAEnD,IAAA,YAAA,CAAa;AAAA,MACX,SAAS,QAAA,CAAS,MAAA,CAAO,YAAU,OAAA,CAAQ,GAAA,CAAI,MAAM,CAAC;AAAA,KACvD,CAAA;AAAA,EACH,CAAA,EAAG,CAAC,OAAA,EAAS,YAAY,CAAC,CAAA;AAE1B,EAAA,MAAM,YAAA,GAAe,CAAC,GAAA,KAA4B;AAChD,IAAA,IAAI,CAAC,OAAA,EAAS;AAEd,IAAA,IAAI,QAAQ,IAAA,EAAM;AAChB,MAAA,YAAA,CAAa,EAAE,OAAA,EAAS,EAAC,EAAG,CAAA;AAC5B,MAAA;AAAA,IACF;AAEA,IAAA,IAAI,CAAC,KAAA,CAAM,OAAA,CAAQ,GAAG,CAAA,EAAG;AACvB,MAAA,YAAA,CAAa;AAAA,QACX,OAAA,EAAS,QAAQ,GAAA,CAAI,GAAa,IAAI,CAAC,GAAa,IAAI;AAAC,OAC1D,CAAA;AACD,MAAA;AAAA,IACF;AAEA,IAAA,MAAM,WAAW,GAAA,CAAI,MAAA;AAAA,MAAO,CAAC,IAAA,KAC3B,OAAA,CAAQ,GAAA,CAAI,IAAc;AAAA,KAC5B;AACA,IAAA,YAAA,CAAa,EAAE,OAAA,EAAS,QAAA,EAAU,CAAA;AAAA,EACpC,CAAA;AAEA,EAAA,uBACE,GAAA;AAAA,IAAC,MAAA;AAAA,IAAA;AAAA,MACC,KAAA,EAAM,QAAA;AAAA,MACN,aAAA,EAAc,UAAA;AAAA,MACd,OAAA,EAAS,aAAA;AAAA,MACT,KAAA,EAAO,eAAA;AAAA,MACP,QAAA,EAAU,YAAA;AAAA,MACV,WAAA,EAAY;AAAA;AAAA,GACd;AAEJ;;;;"}
@@ -37,8 +37,7 @@ const SelectStatus = () => {
37
37
  options: STATUS_OPTIONS,
38
38
  value: filter.status ?? [],
39
39
  onChange: handleChange,
40
- placeholder: "All",
41
- style: { width: 200 }
40
+ placeholder: "All"
42
41
  }
43
42
  );
44
43
  };
@@ -1 +1 @@
1
- {"version":3,"file":"SelectStatus.esm.js","sources":["../../../src/components/SelectStatus/SelectStatus.tsx"],"sourcesContent":["import { Status } from '@kyverno/backstage-plugin-policy-reporter-common';\nimport { Select } from '@backstage/ui';\nimport { Key } from 'react';\nimport { usePolicyReportsFilters } from '../../hooks/usePolicyReportsFilters';\n\nconst STATUS_OPTIONS: { value: Status; label: string }[] = [\n { value: 'fail', label: 'Fail' },\n { value: 'skip', label: 'Skip' },\n { value: 'pass', label: 'Pass' },\n { value: 'warn', label: 'Warn' },\n { value: 'error', label: 'Error' },\n { value: 'summary', label: 'Summary' },\n];\n\nconst validStatuses = STATUS_OPTIONS.map(option => option.value);\n\nexport const SelectStatus = () => {\n const { filter, updateFilter } = usePolicyReportsFilters();\n\n const handleChange = (key: Key | Key[] | null) => {\n if (key === null) {\n updateFilter({ status: [] });\n return;\n }\n\n if (!Array.isArray(key)) {\n updateFilter({\n status: validStatuses.includes(key as Status) ? [key as Status] : [],\n });\n return;\n }\n\n const filteredStatuses = key.filter((item): item is Status =>\n validStatuses.includes(item as Status),\n );\n updateFilter({ status: filteredStatuses });\n };\n\n return (\n <Select\n label=\"Status\"\n selectionMode=\"multiple\"\n options={STATUS_OPTIONS}\n value={filter.status ?? []}\n onChange={handleChange}\n placeholder=\"All\"\n style={{ width: 200 }}\n />\n );\n};\n"],"names":[],"mappings":";;;;AAKA,MAAM,cAAA,GAAqD;AAAA,EACzD,EAAE,KAAA,EAAO,MAAA,EAAQ,KAAA,EAAO,MAAA,EAAO;AAAA,EAC/B,EAAE,KAAA,EAAO,MAAA,EAAQ,KAAA,EAAO,MAAA,EAAO;AAAA,EAC/B,EAAE,KAAA,EAAO,MAAA,EAAQ,KAAA,EAAO,MAAA,EAAO;AAAA,EAC/B,EAAE,KAAA,EAAO,MAAA,EAAQ,KAAA,EAAO,MAAA,EAAO;AAAA,EAC/B,EAAE,KAAA,EAAO,OAAA,EAAS,KAAA,EAAO,OAAA,EAAQ;AAAA,EACjC,EAAE,KAAA,EAAO,SAAA,EAAW,KAAA,EAAO,SAAA;AAC7B,CAAA;AAEA,MAAM,aAAA,GAAgB,cAAA,CAAe,GAAA,CAAI,CAAA,MAAA,KAAU,OAAO,KAAK,CAAA;AAExD,MAAM,eAAe,MAAM;AAChC,EAAA,MAAM,EAAE,MAAA,EAAQ,YAAA,EAAa,GAAI,uBAAA,EAAwB;AAEzD,EAAA,MAAM,YAAA,GAAe,CAAC,GAAA,KAA4B;AAChD,IAAA,IAAI,QAAQ,IAAA,EAAM;AAChB,MAAA,YAAA,CAAa,EAAE,MAAA,EAAQ,EAAC,EAAG,CAAA;AAC3B,MAAA;AAAA,IACF;AAEA,IAAA,IAAI,CAAC,KAAA,CAAM,OAAA,CAAQ,GAAG,CAAA,EAAG;AACvB,MAAA,YAAA,CAAa;AAAA,QACX,MAAA,EAAQ,cAAc,QAAA,CAAS,GAAa,IAAI,CAAC,GAAa,IAAI;AAAC,OACpE,CAAA;AACD,MAAA;AAAA,IACF;AAEA,IAAA,MAAM,mBAAmB,GAAA,CAAI,MAAA;AAAA,MAAO,CAAC,IAAA,KACnC,aAAA,CAAc,QAAA,CAAS,IAAc;AAAA,KACvC;AACA,IAAA,YAAA,CAAa,EAAE,MAAA,EAAQ,gBAAA,EAAkB,CAAA;AAAA,EAC3C,CAAA;AAEA,EAAA,uBACE,GAAA;AAAA,IAAC,MAAA;AAAA,IAAA;AAAA,MACC,KAAA,EAAM,QAAA;AAAA,MACN,aAAA,EAAc,UAAA;AAAA,MACd,OAAA,EAAS,cAAA;AAAA,MACT,KAAA,EAAO,MAAA,CAAO,MAAA,IAAU,EAAC;AAAA,MACzB,QAAA,EAAU,YAAA;AAAA,MACV,WAAA,EAAY,KAAA;AAAA,MACZ,KAAA,EAAO,EAAE,KAAA,EAAO,GAAA;AAAI;AAAA,GACtB;AAEJ;;;;"}
1
+ {"version":3,"file":"SelectStatus.esm.js","sources":["../../../src/components/SelectStatus/SelectStatus.tsx"],"sourcesContent":["import { Status } from '@kyverno/backstage-plugin-policy-reporter-common';\nimport { Select } from '@backstage/ui';\nimport { Key } from 'react';\nimport { usePolicyReportsFilters } from '../../hooks/usePolicyReportsFilters';\n\nconst STATUS_OPTIONS: { value: Status; label: string }[] = [\n { value: 'fail', label: 'Fail' },\n { value: 'skip', label: 'Skip' },\n { value: 'pass', label: 'Pass' },\n { value: 'warn', label: 'Warn' },\n { value: 'error', label: 'Error' },\n { value: 'summary', label: 'Summary' },\n];\n\nconst validStatuses = STATUS_OPTIONS.map(option => option.value);\n\nexport const SelectStatus = () => {\n const { filter, updateFilter } = usePolicyReportsFilters();\n\n const handleChange = (key: Key | Key[] | null) => {\n if (key === null) {\n updateFilter({ status: [] });\n return;\n }\n\n if (!Array.isArray(key)) {\n updateFilter({\n status: validStatuses.includes(key as Status) ? [key as Status] : [],\n });\n return;\n }\n\n const filteredStatuses = key.filter((item): item is Status =>\n validStatuses.includes(item as Status),\n );\n updateFilter({ status: filteredStatuses });\n };\n\n return (\n <Select\n label=\"Status\"\n selectionMode=\"multiple\"\n options={STATUS_OPTIONS}\n value={filter.status ?? []}\n onChange={handleChange}\n placeholder=\"All\"\n />\n );\n};\n"],"names":[],"mappings":";;;;AAKA,MAAM,cAAA,GAAqD;AAAA,EACzD,EAAE,KAAA,EAAO,MAAA,EAAQ,KAAA,EAAO,MAAA,EAAO;AAAA,EAC/B,EAAE,KAAA,EAAO,MAAA,EAAQ,KAAA,EAAO,MAAA,EAAO;AAAA,EAC/B,EAAE,KAAA,EAAO,MAAA,EAAQ,KAAA,EAAO,MAAA,EAAO;AAAA,EAC/B,EAAE,KAAA,EAAO,MAAA,EAAQ,KAAA,EAAO,MAAA,EAAO;AAAA,EAC/B,EAAE,KAAA,EAAO,OAAA,EAAS,KAAA,EAAO,OAAA,EAAQ;AAAA,EACjC,EAAE,KAAA,EAAO,SAAA,EAAW,KAAA,EAAO,SAAA;AAC7B,CAAA;AAEA,MAAM,aAAA,GAAgB,cAAA,CAAe,GAAA,CAAI,CAAA,MAAA,KAAU,OAAO,KAAK,CAAA;AAExD,MAAM,eAAe,MAAM;AAChC,EAAA,MAAM,EAAE,MAAA,EAAQ,YAAA,EAAa,GAAI,uBAAA,EAAwB;AAEzD,EAAA,MAAM,YAAA,GAAe,CAAC,GAAA,KAA4B;AAChD,IAAA,IAAI,QAAQ,IAAA,EAAM;AAChB,MAAA,YAAA,CAAa,EAAE,MAAA,EAAQ,EAAC,EAAG,CAAA;AAC3B,MAAA;AAAA,IACF;AAEA,IAAA,IAAI,CAAC,KAAA,CAAM,OAAA,CAAQ,GAAG,CAAA,EAAG;AACvB,MAAA,YAAA,CAAa;AAAA,QACX,MAAA,EAAQ,cAAc,QAAA,CAAS,GAAa,IAAI,CAAC,GAAa,IAAI;AAAC,OACpE,CAAA;AACD,MAAA;AAAA,IACF;AAEA,IAAA,MAAM,mBAAmB,GAAA,CAAI,MAAA;AAAA,MAAO,CAAC,IAAA,KACnC,aAAA,CAAc,QAAA,CAAS,IAAc;AAAA,KACvC;AACA,IAAA,YAAA,CAAa,EAAE,MAAA,EAAQ,gBAAA,EAAkB,CAAA;AAAA,EAC3C,CAAA;AAEA,EAAA,uBACE,GAAA;AAAA,IAAC,MAAA;AAAA,IAAA;AAAA,MACC,KAAA,EAAM,QAAA;AAAA,MACN,aAAA,EAAc,UAAA;AAAA,MACd,OAAA,EAAS,cAAA;AAAA,MACT,KAAA,EAAO,MAAA,CAAO,MAAA,IAAU,EAAC;AAAA,MACzB,QAAA,EAAU,YAAA;AAAA,MACV,WAAA,EAAY;AAAA;AAAA,GACd;AAEJ;;;;"}
@@ -1,6 +1,6 @@
1
1
  import { jsx } from 'react/jsx-runtime';
2
2
  import qs from 'qs';
3
- import { useMemo, useCallback, useLayoutEffect, createContext, useContext } from 'react';
3
+ import { useMemo, useRef, useCallback, useLayoutEffect, createContext, useContext } from 'react';
4
4
  import { useSearchParams } from 'react-router-dom';
5
5
 
6
6
  const PolicyReportsFiltersContext = createContext(void 0);
@@ -26,9 +26,11 @@ const PolicyReportsFiltersProvider = ({
26
26
  () => urlEnvironment ?? defaultEnvironment,
27
27
  [urlEnvironment, defaultEnvironment]
28
28
  );
29
+ const setSearchParamsRef = useRef(setSearchParams);
30
+ setSearchParamsRef.current = setSearchParams;
29
31
  const updateParams = useCallback(
30
32
  (update) => {
31
- setSearchParams(
33
+ setSearchParamsRef.current(
32
34
  (prev) => {
33
35
  const prevParsed = qs.parse(prev.toString());
34
36
  const next = typeof update === "function" ? update(prevParsed) : update;
@@ -40,7 +42,7 @@ const PolicyReportsFiltersProvider = ({
40
42
  { replace: true }
41
43
  );
42
44
  },
43
- [setSearchParams]
45
+ []
44
46
  );
45
47
  useLayoutEffect(() => {
46
48
  if (!urlEnvironment) {
@@ -49,7 +51,7 @@ const PolicyReportsFiltersProvider = ({
49
51
  environment: defaultEnvironment
50
52
  });
51
53
  }
52
- }, [defaultEnvironment, urlEnvironment, defaultFilters]);
54
+ }, [defaultEnvironment, urlEnvironment, defaultFilters, updateParams]);
53
55
  const updateFilter = useCallback(
54
56
  (update) => {
55
57
  updateParams((prev) => ({
@@ -1 +1 @@
1
- {"version":3,"file":"usePolicyReportsFilters.esm.js","sources":["../../src/hooks/usePolicyReportsFilters.tsx"],"sourcesContent":["import qs from 'qs';\nimport {\n createContext,\n PropsWithChildren,\n useCallback,\n useContext,\n useLayoutEffect,\n useMemo,\n} from 'react';\nimport { useSearchParams } from 'react-router-dom';\nimport type { Filter } from '@kyverno/backstage-plugin-policy-reporter-common';\n\nexport type PolicyReportsFiltersContextValue = {\n filter: Filter;\n updateFilter: (update: Partial<Filter>) => void;\n environment: string;\n setEnvironment: (entityRef: string) => void;\n};\n\nconst PolicyReportsFiltersContext = createContext<\n PolicyReportsFiltersContextValue | undefined\n>(undefined);\n\nexport type PolicyReportsFiltersProviderProps = PropsWithChildren<{\n /**\n * Default filter values written to the URL\n */\n defaultFilters?: Partial<Filter>;\n /**\n * The entityRef of the environment to use\n * when no ?environment= param is present in the URL.\n */\n defaultEnvironment: string;\n}>;\n\n/**\n * Provides shared filter state and selected environment for all policy report\n * filter components.\n */\nexport const PolicyReportsFiltersProvider = ({\n children,\n defaultFilters,\n defaultEnvironment,\n}: PolicyReportsFiltersProviderProps) => {\n const [searchParams, setSearchParams] = useSearchParams();\n\n const parsed = useMemo(\n () => qs.parse(searchParams.toString()),\n [searchParams],\n );\n\n const urlEnvironment = useMemo(\n () => parsed.environment?.toString(),\n [parsed],\n );\n\n // Use defaultFilters until the URL has been initialised (urlEnvironment set).\n // After that, always read from the URL so an explicit empty filter is valid.\n const filter = useMemo(\n () =>\n urlEnvironment ? ((parsed.filter ?? {}) as Filter) : defaultFilters ?? {},\n [parsed, urlEnvironment, defaultFilters],\n );\n\n const environment = useMemo(\n () => urlEnvironment ?? defaultEnvironment,\n [urlEnvironment, defaultEnvironment],\n );\n\n const updateParams = useCallback(\n (\n update:\n | Record<string, unknown>\n | ((prev: Record<string, unknown>) => Record<string, unknown>),\n ) => {\n setSearchParams(\n prev => {\n const prevParsed = qs.parse(prev.toString());\n const next =\n typeof update === 'function' ? update(prevParsed) : update;\n return qs.stringify(next, {\n arrayFormat: 'indices',\n skipNulls: true,\n });\n },\n { replace: true },\n );\n },\n [setSearchParams],\n );\n\n useLayoutEffect(() => {\n if (!urlEnvironment) {\n updateParams({\n filter: defaultFilters,\n environment: defaultEnvironment,\n });\n }\n // setSearchParams isn't stable, hence disabling the exhaustive-deps check\n // for more info please visit https://github.com/remix-run/react-router/issues/9991\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [defaultEnvironment, urlEnvironment, defaultFilters]);\n\n const updateFilter = useCallback(\n (update: Partial<Filter>) => {\n updateParams(prev => ({\n ...prev,\n filter: { ...(prev.filter as object), ...update },\n }));\n },\n [updateParams],\n );\n\n const setEnvironment = useCallback(\n (entityRef: string) => {\n updateParams(prev => ({ ...prev, environment: entityRef }));\n },\n [updateParams],\n );\n\n const value = useMemo(\n () => ({\n filter,\n updateFilter,\n environment,\n setEnvironment,\n }),\n [filter, updateFilter, environment, setEnvironment],\n );\n\n return (\n <PolicyReportsFiltersContext.Provider value={value}>\n {children}\n </PolicyReportsFiltersContext.Provider>\n );\n};\n\n/**\n * Hook for reading and updating policy report filters and the selected\n * environment. Must be used within a PolicyReportsFiltersProvider.\n */\nexport const usePolicyReportsFilters = (): PolicyReportsFiltersContextValue => {\n const ctx = useContext(PolicyReportsFiltersContext);\n if (!ctx) {\n throw new Error(\n 'usePolicyReportsFilters must be used within PolicyReportsFiltersProvider',\n );\n }\n return ctx;\n};\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"usePolicyReportsFilters.esm.js","sources":["../../src/hooks/usePolicyReportsFilters.tsx"],"sourcesContent":["import qs from 'qs';\nimport {\n createContext,\n PropsWithChildren,\n useCallback,\n useContext,\n useLayoutEffect,\n useMemo,\n useRef,\n} from 'react';\nimport { useSearchParams } from 'react-router-dom';\nimport type { Filter } from '@kyverno/backstage-plugin-policy-reporter-common';\n\nexport type PolicyReportsFiltersContextValue = {\n filter: Filter;\n updateFilter: (update: Partial<Filter>) => void;\n environment: string;\n setEnvironment: (entityRef: string) => void;\n};\n\nconst PolicyReportsFiltersContext = createContext<\n PolicyReportsFiltersContextValue | undefined\n>(undefined);\n\nexport type PolicyReportsFiltersProviderProps = PropsWithChildren<{\n /**\n * Default filter values written to the URL\n */\n defaultFilters?: Partial<Filter>;\n /**\n * The entityRef of the environment to use\n * when no ?environment= param is present in the URL.\n */\n defaultEnvironment: string;\n}>;\n\n/**\n * Provides shared filter state and selected environment for all policy report\n * filter components.\n */\nexport const PolicyReportsFiltersProvider = ({\n children,\n defaultFilters,\n defaultEnvironment,\n}: PolicyReportsFiltersProviderProps) => {\n const [searchParams, setSearchParams] = useSearchParams();\n\n const parsed = useMemo(\n () => qs.parse(searchParams.toString()),\n [searchParams],\n );\n\n const urlEnvironment = useMemo(\n () => parsed.environment?.toString(),\n [parsed],\n );\n\n // Use defaultFilters until the URL has been initialised (urlEnvironment set).\n // After that, always read from the URL so an explicit empty filter is valid.\n const filter = useMemo(\n () =>\n urlEnvironment ? ((parsed.filter ?? {}) as Filter) : defaultFilters ?? {},\n [parsed, urlEnvironment, defaultFilters],\n );\n\n const environment = useMemo(\n () => urlEnvironment ?? defaultEnvironment,\n [urlEnvironment, defaultEnvironment],\n );\n\n // setSearchParams is not stable (https://github.com/remix-run/react-router/issues/9991),\n // so we hold it in a ref to keep updateParams/updateFilter stable across renders.\n const setSearchParamsRef = useRef(setSearchParams);\n setSearchParamsRef.current = setSearchParams;\n\n const updateParams = useCallback(\n (\n update:\n | Record<string, unknown>\n | ((prev: Record<string, unknown>) => Record<string, unknown>),\n ) => {\n setSearchParamsRef.current(\n prev => {\n const prevParsed = qs.parse(prev.toString());\n const next =\n typeof update === 'function' ? update(prevParsed) : update;\n return qs.stringify(next, {\n arrayFormat: 'indices',\n skipNulls: true,\n });\n },\n { replace: true },\n );\n },\n [],\n );\n\n useLayoutEffect(() => {\n if (!urlEnvironment) {\n updateParams({\n filter: defaultFilters,\n environment: defaultEnvironment,\n });\n }\n }, [defaultEnvironment, urlEnvironment, defaultFilters, updateParams]);\n\n const updateFilter = useCallback(\n (update: Partial<Filter>) => {\n updateParams(prev => ({\n ...prev,\n filter: { ...(prev.filter as object), ...update },\n }));\n },\n [updateParams],\n );\n\n const setEnvironment = useCallback(\n (entityRef: string) => {\n updateParams(prev => ({ ...prev, environment: entityRef }));\n },\n [updateParams],\n );\n\n const value = useMemo(\n () => ({\n filter,\n updateFilter,\n environment,\n setEnvironment,\n }),\n [filter, updateFilter, environment, setEnvironment],\n );\n\n return (\n <PolicyReportsFiltersContext.Provider value={value}>\n {children}\n </PolicyReportsFiltersContext.Provider>\n );\n};\n\n/**\n * Hook for reading and updating policy report filters and the selected\n * environment. Must be used within a PolicyReportsFiltersProvider.\n */\nexport const usePolicyReportsFilters = (): PolicyReportsFiltersContextValue => {\n const ctx = useContext(PolicyReportsFiltersContext);\n if (!ctx) {\n throw new Error(\n 'usePolicyReportsFilters must be used within PolicyReportsFiltersProvider',\n );\n }\n return ctx;\n};\n"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -18,6 +18,6 @@ declare const EntityCustomPoliciesContent: ({ annotationsDocumentationUrl, polic
18
18
  sources: string[];
19
19
  title: string;
20
20
  }) => react_jsx_runtime.JSX.Element;
21
- declare const PolicyReportsPage: ({ title, policyDocumentationUrl, }: PolicyReportsPageProps) => react_jsx_runtime.JSX.Element;
21
+ declare const PolicyReportsPage: ({ title, policyDocumentationUrl, subtitle, }: PolicyReportsPageProps) => react_jsx_runtime.JSX.Element;
22
22
 
23
23
  export { EntityCustomPoliciesContent, EntityKyvernoPoliciesContent, PolicyReportsPage, policyReporterPlugin };
@@ -1,5 +1,5 @@
1
1
  var name = "@kyverno/backstage-plugin-policy-reporter";
2
- var version = "2.8.0";
2
+ var version = "2.10.0";
3
3
  var main = "src/index.ts";
4
4
  var types = "src/index.ts";
5
5
  var license = "Apache-2.0";
@@ -20,7 +20,8 @@ var backstage = {
20
20
  var publishConfig = {
21
21
  access: "public",
22
22
  main: "dist/index.esm.js",
23
- types: "dist/index.d.ts"
23
+ types: "dist/index.d.ts",
24
+ provenance: true
24
25
  };
25
26
  var exports$1 = {
26
27
  ".": "./src/index.ts",
@@ -65,6 +66,7 @@ var dependencies = {
65
66
  "@material-ui/core": "^4.12.4",
66
67
  "@material-ui/icons": "^4.11.3",
67
68
  "@material-ui/lab": "4.0.0-alpha.61",
69
+ "@remixicon/react": ">=4.6.0 <4.9.0",
68
70
  qs: "^6.15.0",
69
71
  "react-use": "^17.6.0"
70
72
  };
@@ -1 +1 @@
1
- {"version":3,"file":"package.json.esm.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"package.json.esm.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kyverno/backstage-plugin-policy-reporter",
3
- "version": "2.8.0",
3
+ "version": "2.10.0",
4
4
  "main": "dist/index.esm.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "license": "Apache-2.0",
@@ -24,7 +24,8 @@
24
24
  "publishConfig": {
25
25
  "access": "public",
26
26
  "main": "dist/index.esm.js",
27
- "types": "dist/index.d.ts"
27
+ "types": "dist/index.d.ts",
28
+ "provenance": true
28
29
  },
29
30
  "exports": {
30
31
  ".": {
@@ -66,18 +67,19 @@
66
67
  "directory": "plugins/policy-reporter"
67
68
  },
68
69
  "dependencies": {
69
- "@backstage/catalog-client": "^1.15.0",
70
- "@backstage/catalog-model": "^1.8.0",
71
- "@backstage/core-components": "^0.18.9",
72
- "@backstage/core-plugin-api": "^1.12.5",
73
- "@backstage/frontend-plugin-api": "^0.16.2",
74
- "@backstage/plugin-catalog-react": "^2.1.4",
70
+ "@backstage/catalog-client": "^1.16.0",
71
+ "@backstage/catalog-model": "^1.9.0",
72
+ "@backstage/core-components": "^0.18.11",
73
+ "@backstage/core-plugin-api": "^1.12.7",
74
+ "@backstage/frontend-plugin-api": "^0.17.2",
75
+ "@backstage/plugin-catalog-react": "^3.1.0",
75
76
  "@backstage/theme": "^0.7.3",
76
- "@backstage/ui": "^0.14.3",
77
- "@kyverno/backstage-plugin-policy-reporter-common": "^2.4.0",
77
+ "@backstage/ui": "^0.16.0",
78
+ "@kyverno/backstage-plugin-policy-reporter-common": "^2.5.0",
78
79
  "@material-ui/core": "^4.12.4",
79
80
  "@material-ui/icons": "^4.11.3",
80
81
  "@material-ui/lab": "4.0.0-alpha.61",
82
+ "@remixicon/react": ">=4.6.0 <4.9.0",
81
83
  "qs": "^6.15.0",
82
84
  "react-use": "^17.6.0"
83
85
  },
@@ -86,10 +88,10 @@
86
88
  "react-router-dom": "^6.0.0"
87
89
  },
88
90
  "devDependencies": {
89
- "@backstage/cli": "^0.36.1",
90
- "@backstage/core-app-api": "^1.20.0",
91
- "@backstage/dev-utils": "^1.1.22",
92
- "@backstage/test-utils": "^1.7.17",
91
+ "@backstage/cli": "^0.36.3",
92
+ "@backstage/core-app-api": "^1.20.2",
93
+ "@backstage/dev-utils": "^1.1.24",
94
+ "@backstage/test-utils": "^1.7.19",
93
95
  "@testing-library/jest-dom": "^6.0.0",
94
96
  "@testing-library/react": "^14.0.0",
95
97
  "@testing-library/user-event": "^14.0.0",
@@ -99,5 +101,6 @@
99
101
  "files": [
100
102
  "dist"
101
103
  ],
102
- "module": "./dist/index.esm.js"
104
+ "module": "./dist/index.esm.js",
105
+ "provenance": true
103
106
  }
@@ -1,28 +0,0 @@
1
- import { useAsync } from 'react-use';
2
- import { useApi } from '@backstage/core-plugin-api';
3
- import { policyReporterApiRef } from '../api/PolicyReporterClient.esm.js';
4
-
5
- const useNamespaces = (entityRef) => {
6
- const api = useApi(policyReporterApiRef);
7
- const {
8
- value: namespaces,
9
- loading,
10
- error
11
- } = useAsync(async () => {
12
- if (!entityRef) {
13
- return [];
14
- }
15
- const response = await api.getNamespaces({
16
- query: { environment: entityRef }
17
- });
18
- return response.json();
19
- }, [api, entityRef]);
20
- return {
21
- namespaces: namespaces ?? [],
22
- loading,
23
- error
24
- };
25
- };
26
-
27
- export { useNamespaces };
28
- //# sourceMappingURL=useNamespaces.esm.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"useNamespaces.esm.js","sources":["../../src/hooks/useNamespaces.ts"],"sourcesContent":["import { useAsync } from 'react-use';\nimport { useApi } from '@backstage/core-plugin-api';\nimport { policyReporterApiRef } from '../api';\n\nexport const useNamespaces = (entityRef?: string) => {\n const api = useApi(policyReporterApiRef);\n\n const {\n value: namespaces,\n loading,\n error,\n } = useAsync(async () => {\n if (!entityRef) {\n return [];\n }\n\n const response = await api.getNamespaces({\n query: { environment: entityRef },\n });\n return response.json();\n }, [api, entityRef]);\n\n return {\n namespaces: namespaces ?? [],\n loading,\n error,\n };\n};\n"],"names":[],"mappings":";;;;AAIO,MAAM,aAAA,GAAgB,CAAC,SAAA,KAAuB;AACnD,EAAA,MAAM,GAAA,GAAM,OAAO,oBAAoB,CAAA;AAEvC,EAAA,MAAM;AAAA,IACJ,KAAA,EAAO,UAAA;AAAA,IACP,OAAA;AAAA,IACA;AAAA,GACF,GAAI,SAAS,YAAY;AACvB,IAAA,IAAI,CAAC,SAAA,EAAW;AACd,MAAA,OAAO,EAAC;AAAA,IACV;AAEA,IAAA,MAAM,QAAA,GAAW,MAAM,GAAA,CAAI,aAAA,CAAc;AAAA,MACvC,KAAA,EAAO,EAAE,WAAA,EAAa,SAAA;AAAU,KACjC,CAAA;AACD,IAAA,OAAO,SAAS,IAAA,EAAK;AAAA,EACvB,CAAA,EAAG,CAAC,GAAA,EAAK,SAAS,CAAC,CAAA;AAEnB,EAAA,OAAO;AAAA,IACL,UAAA,EAAY,cAAc,EAAC;AAAA,IAC3B,OAAA;AAAA,IACA;AAAA,GACF;AACF;;;;"}