@backstage-community/plugin-apiiro 0.1.0 → 1.0.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 (88) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/README.md +91 -12
  3. package/config.d.ts +14 -0
  4. package/dist/App.esm.js +96 -4
  5. package/dist/App.esm.js.map +1 -1
  6. package/dist/api/index.esm.js +6 -0
  7. package/dist/api/index.esm.js.map +1 -1
  8. package/dist/assets/SettingIcon.esm.js +1 -0
  9. package/dist/assets/SettingIcon.esm.js.map +1 -1
  10. package/dist/components/ApiiroSidebar.esm.js.map +1 -1
  11. package/dist/components/CalendarDatePicker.esm.js.map +1 -1
  12. package/dist/components/DataGrid/DataGrid.esm.js +1 -0
  13. package/dist/components/DataGrid/DataGrid.esm.js.map +1 -1
  14. package/dist/components/MetricsGroup/TabMetricsGroup.esm.js +28 -4
  15. package/dist/components/MetricsGroup/TabMetricsGroup.esm.js.map +1 -1
  16. package/dist/components/MetricsGroup/WidgetMetricsGroup.esm.js +30 -7
  17. package/dist/components/MetricsGroup/WidgetMetricsGroup.esm.js.map +1 -1
  18. package/dist/components/RiskLevel.esm.js +1 -0
  19. package/dist/components/RiskLevel.esm.js.map +1 -1
  20. package/dist/components/charts/GaugeChart.esm.js +8 -6
  21. package/dist/components/charts/GaugeChart.esm.js.map +1 -1
  22. package/dist/components/common/StatusContainer.esm.js +123 -0
  23. package/dist/components/common/StatusContainer.esm.js.map +1 -0
  24. package/dist/components/filters/DiscoveredOnFilter.esm.js +1 -1
  25. package/dist/components/filters/DiscoveredOnFilter.esm.js.map +1 -1
  26. package/dist/components/tiles/MttrVsSLATile.esm.js +31 -14
  27. package/dist/components/tiles/MttrVsSLATile.esm.js.map +1 -1
  28. package/dist/components/tiles/RiskOverTimeTile.esm.js +15 -12
  29. package/dist/components/tiles/RiskOverTimeTile.esm.js.map +1 -1
  30. package/dist/components/tiles/SLAAdherenceTile.esm.js +15 -12
  31. package/dist/components/tiles/SLAAdherenceTile.esm.js.map +1 -1
  32. package/dist/components/tiles/StatusTile.esm.js +98 -66
  33. package/dist/components/tiles/StatusTile.esm.js.map +1 -1
  34. package/dist/components/tiles/TopLanguagesTile.esm.js +1 -0
  35. package/dist/components/tiles/TopLanguagesTile.esm.js.map +1 -1
  36. package/dist/components/tiles/TopRiskTile.esm.js +19 -16
  37. package/dist/components/tiles/TopRiskTile.esm.js.map +1 -1
  38. package/dist/index.d.ts +31 -6
  39. package/dist/index.esm.js +2 -1
  40. package/dist/index.esm.js.map +1 -1
  41. package/dist/pages/Applications/Applications.esm.js +104 -0
  42. package/dist/pages/Applications/Applications.esm.js.map +1 -0
  43. package/dist/pages/Applications/tableConfig.esm.js +147 -0
  44. package/dist/pages/Applications/tableConfig.esm.js.map +1 -0
  45. package/dist/pages/Repositories/Repositories.esm.js +26 -26
  46. package/dist/pages/Repositories/Repositories.esm.js.map +1 -1
  47. package/dist/pages/Repositories/tableConfig.esm.js +3 -2
  48. package/dist/pages/Repositories/tableConfig.esm.js.map +1 -1
  49. package/dist/pages/Risks/Risks.esm.js +9 -3
  50. package/dist/pages/Risks/Risks.esm.js.map +1 -1
  51. package/dist/pages/Risks/tableConfig.esm.js +25 -11
  52. package/dist/pages/Risks/tableConfig.esm.js.map +1 -1
  53. package/dist/pages/tab/ComponentTab.esm.js +72 -0
  54. package/dist/pages/tab/ComponentTab.esm.js.map +1 -0
  55. package/dist/pages/tab/SystemTab.esm.js +159 -0
  56. package/dist/pages/tab/SystemTab.esm.js.map +1 -0
  57. package/dist/pages/tab/TabProvider.esm.js +9 -3
  58. package/dist/pages/tab/TabProvider.esm.js.map +1 -1
  59. package/dist/pages/widget/ComponentWidget.esm.js +67 -0
  60. package/dist/pages/widget/ComponentWidget.esm.js.map +1 -0
  61. package/dist/pages/widget/SystemWidget.esm.js +81 -0
  62. package/dist/pages/widget/SystemWidget.esm.js.map +1 -0
  63. package/dist/pages/widget/WidgetProvider.esm.js +9 -3
  64. package/dist/pages/widget/WidgetProvider.esm.js.map +1 -1
  65. package/dist/plugin.esm.js.map +1 -1
  66. package/dist/queries/application.queries.esm.js +64 -0
  67. package/dist/queries/application.queries.esm.js.map +1 -0
  68. package/dist/queries/mttr-statistics.queries.esm.js +19 -12
  69. package/dist/queries/mttr-statistics.queries.esm.js.map +1 -1
  70. package/dist/queries/repository.queries.esm.js +19 -8
  71. package/dist/queries/repository.queries.esm.js.map +1 -1
  72. package/dist/queries/risk-score-over-time.queries.esm.js +19 -12
  73. package/dist/queries/risk-score-over-time.queries.esm.js.map +1 -1
  74. package/dist/queries/risks.queries.esm.js +19 -7
  75. package/dist/queries/risks.queries.esm.js.map +1 -1
  76. package/dist/queries/sla-breach.queries.esm.js +22 -11
  77. package/dist/queries/sla-breach.queries.esm.js.map +1 -1
  78. package/dist/queries/top-risks.queries.esm.js +19 -7
  79. package/dist/queries/top-risks.queries.esm.js.map +1 -1
  80. package/dist/theme/themeUtils.esm.js +5 -2
  81. package/dist/theme/themeUtils.esm.js.map +1 -1
  82. package/dist/utils/utils.esm.js +3 -2
  83. package/dist/utils/utils.esm.js.map +1 -1
  84. package/package.json +12 -12
  85. package/dist/pages/tab/Tab.esm.js +0 -147
  86. package/dist/pages/tab/Tab.esm.js.map +0 -1
  87. package/dist/pages/widget/Widget.esm.js +0 -161
  88. package/dist/pages/widget/Widget.esm.js.map +0 -1
@@ -10,9 +10,11 @@ import { apiiroApiRef } from '../../api/index.esm.js';
10
10
 
11
11
  const TabMetricsGroup = ({
12
12
  repositoryData,
13
+ applicationData,
13
14
  entity,
14
15
  repoId,
15
- entityRef
16
+ entityRef,
17
+ applicationId
16
18
  }) => {
17
19
  const apiiroApi = useApi(apiiroApiRef);
18
20
  const defaultViewChart = apiiroApi.getDefaultAllowMetricsView();
@@ -22,13 +24,35 @@ const TabMetricsGroup = ({
22
24
  StatusTile,
23
25
  {
24
26
  repository: repositoryData,
27
+ application: applicationData,
25
28
  allowViewChart
26
29
  }
27
30
  ) }),
28
31
  allowViewChart && /* @__PURE__ */ jsxs(Fragment, { children: [
29
- /* @__PURE__ */ jsx(Grid, { xs: 12, sm: 6, lg: 4, children: /* @__PURE__ */ jsx(MttrVsSLATile, { repoId, entityRef }) }),
30
- /* @__PURE__ */ jsx(Grid, { xs: 12, sm: 6, lg: 4, children: /* @__PURE__ */ jsx(RiskOverTimeTile, { repoId, entityRef }) }),
31
- /* @__PURE__ */ jsx(Grid, { xs: 12, sm: 6, lg: 4, children: /* @__PURE__ */ jsx(SLAAdherenceTile, { repoId, entityRef }) })
32
+ /* @__PURE__ */ jsx(Grid, { xs: 12, sm: 6, lg: 4, children: /* @__PURE__ */ jsx(
33
+ MttrVsSLATile,
34
+ {
35
+ repoId,
36
+ applicationId,
37
+ entityRef
38
+ }
39
+ ) }),
40
+ /* @__PURE__ */ jsx(Grid, { xs: 12, sm: 6, lg: 4, children: /* @__PURE__ */ jsx(
41
+ RiskOverTimeTile,
42
+ {
43
+ repoId,
44
+ applicationId,
45
+ entityRef
46
+ }
47
+ ) }),
48
+ /* @__PURE__ */ jsx(Grid, { xs: 12, sm: 6, lg: 4, children: /* @__PURE__ */ jsx(
49
+ SLAAdherenceTile,
50
+ {
51
+ repoId,
52
+ applicationId,
53
+ entityRef
54
+ }
55
+ ) })
32
56
  ] })
33
57
  ] }) });
34
58
  };
@@ -1 +1 @@
1
- {"version":3,"file":"TabMetricsGroup.esm.js","sources":["../../../src/components/MetricsGroup/TabMetricsGroup.tsx"],"sourcesContent":["/*\n * Copyright 2025 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport Grid from '@mui/material/Unstable_Grid2';\nimport { MttrVsSLATile } from '../tiles/MttrVsSLATile';\nimport { SLAAdherenceTile } from '../tiles/SLAAdherenceTile';\nimport { RiskOverTimeTile } from '../tiles/RiskOverTimeTile';\nimport { RepositoryType } from '../../queries';\nimport StatusTile from '../tiles/StatusTile';\nimport type { Entity } from '@backstage/catalog-model';\nimport { useApi } from '@backstage/core-plugin-api';\nimport { isApiiroMetricViewAvailable } from '../../utils';\nimport { apiiroApiRef } from '../../api';\n\nexport const TabMetricsGroup = ({\n repositoryData,\n entity,\n repoId,\n entityRef,\n}: {\n repositoryData: RepositoryType;\n entity: Entity;\n repoId: string;\n entityRef: string;\n}) => {\n const apiiroApi = useApi(apiiroApiRef);\n const defaultViewChart = apiiroApi.getDefaultAllowMetricsView();\n const allowViewChart =\n isApiiroMetricViewAvailable(entity) ?? defaultViewChart;\n return (\n <Grid container spacing={3} direction=\"column\">\n <Grid container spacing={3}>\n <Grid xs={12} sm={12}>\n <StatusTile\n repository={repositoryData}\n allowViewChart={allowViewChart}\n />\n </Grid>\n {allowViewChart && (\n <>\n <Grid xs={12} sm={6} lg={4}>\n <MttrVsSLATile repoId={repoId} entityRef={entityRef} />\n </Grid>\n <Grid xs={12} sm={6} lg={4}>\n <RiskOverTimeTile repoId={repoId} entityRef={entityRef} />\n </Grid>\n <Grid xs={12} sm={6} lg={4}>\n <SLAAdherenceTile repoId={repoId} entityRef={entityRef} />\n </Grid>\n </>\n )}\n </Grid>\n </Grid>\n );\n};\n"],"names":[],"mappings":";;;;;;;;;;AA0BO,MAAM,kBAAkB,CAAC;AAAA,EAC9B,cAAA;AAAA,EACA,MAAA;AAAA,EACA,MAAA;AAAA,EACA;AACF,CAAA,KAKM;AACJ,EAAA,MAAM,SAAA,GAAY,OAAO,YAAY,CAAA;AACrC,EAAA,MAAM,gBAAA,GAAmB,UAAU,0BAAA,EAA2B;AAC9D,EAAA,MAAM,cAAA,GACJ,2BAAA,CAA4B,MAAM,CAAA,IAAK,gBAAA;AACzC,EAAA,uBACE,GAAA,CAAC,IAAA,EAAA,EAAK,SAAA,EAAS,IAAA,EAAC,OAAA,EAAS,CAAA,EAAG,SAAA,EAAU,QAAA,EACpC,QAAA,kBAAA,IAAA,CAAC,IAAA,EAAA,EAAK,SAAA,EAAS,IAAA,EAAC,SAAS,CAAA,EACvB,QAAA,EAAA;AAAA,oBAAA,GAAA,CAAC,IAAA,EAAA,EAAK,EAAA,EAAI,EAAA,EAAI,EAAA,EAAI,EAAA,EAChB,QAAA,kBAAA,GAAA;AAAA,MAAC,UAAA;AAAA,MAAA;AAAA,QACC,UAAA,EAAY,cAAA;AAAA,QACZ;AAAA;AAAA,KACF,EACF,CAAA;AAAA,IACC,kCACC,IAAA,CAAA,QAAA,EAAA,EACE,QAAA,EAAA;AAAA,sBAAA,GAAA,CAAC,IAAA,EAAA,EAAK,EAAA,EAAI,EAAA,EAAI,EAAA,EAAI,CAAA,EAAG,EAAA,EAAI,CAAA,EACvB,QAAA,kBAAA,GAAA,CAAC,aAAA,EAAA,EAAc,MAAA,EAAgB,SAAA,EAAsB,CAAA,EACvD,CAAA;AAAA,sBACA,GAAA,CAAC,IAAA,EAAA,EAAK,EAAA,EAAI,EAAA,EAAI,EAAA,EAAI,CAAA,EAAG,EAAA,EAAI,CAAA,EACvB,QAAA,kBAAA,GAAA,CAAC,gBAAA,EAAA,EAAiB,MAAA,EAAgB,SAAA,EAAsB,CAAA,EAC1D,CAAA;AAAA,sBACA,GAAA,CAAC,IAAA,EAAA,EAAK,EAAA,EAAI,EAAA,EAAI,EAAA,EAAI,CAAA,EAAG,EAAA,EAAI,CAAA,EACvB,QAAA,kBAAA,GAAA,CAAC,gBAAA,EAAA,EAAiB,MAAA,EAAgB,SAAA,EAAsB,CAAA,EAC1D;AAAA,KAAA,EACF;AAAA,GAAA,EAEJ,CAAA,EACF,CAAA;AAEJ;;;;"}
1
+ {"version":3,"file":"TabMetricsGroup.esm.js","sources":["../../../src/components/MetricsGroup/TabMetricsGroup.tsx"],"sourcesContent":["/*\n * Copyright 2025 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport Grid from '@mui/material/Unstable_Grid2';\nimport { MttrVsSLATile } from '../tiles/MttrVsSLATile';\nimport { SLAAdherenceTile } from '../tiles/SLAAdherenceTile';\nimport { RiskOverTimeTile } from '../tiles/RiskOverTimeTile';\nimport { ApplicationType, RepositoryType } from '../../queries';\nimport StatusTile from '../tiles/StatusTile';\nimport type { Entity } from '@backstage/catalog-model';\nimport { useApi } from '@backstage/core-plugin-api';\nimport { isApiiroMetricViewAvailable } from '../../utils';\nimport { apiiroApiRef } from '../../api';\n\nexport const TabMetricsGroup = ({\n repositoryData,\n applicationData,\n entity,\n repoId,\n entityRef,\n applicationId,\n}: {\n repositoryData?: RepositoryType;\n applicationData?: ApplicationType;\n entity: Entity;\n repoId?: string;\n entityRef: string;\n applicationId?: string;\n}) => {\n const apiiroApi = useApi(apiiroApiRef);\n const defaultViewChart = apiiroApi.getDefaultAllowMetricsView();\n const allowViewChart =\n isApiiroMetricViewAvailable(entity) ?? defaultViewChart;\n return (\n <Grid container spacing={3} direction=\"column\">\n <Grid container spacing={3}>\n <Grid xs={12} sm={12}>\n <StatusTile\n repository={repositoryData}\n application={applicationData}\n allowViewChart={allowViewChart}\n />\n </Grid>\n {allowViewChart && (\n <>\n <Grid xs={12} sm={6} lg={4}>\n <MttrVsSLATile\n repoId={repoId}\n applicationId={applicationId}\n entityRef={entityRef}\n />\n </Grid>\n <Grid xs={12} sm={6} lg={4}>\n <RiskOverTimeTile\n repoId={repoId}\n applicationId={applicationId}\n entityRef={entityRef}\n />\n </Grid>\n <Grid xs={12} sm={6} lg={4}>\n <SLAAdherenceTile\n repoId={repoId}\n applicationId={applicationId}\n entityRef={entityRef}\n />\n </Grid>\n </>\n )}\n </Grid>\n </Grid>\n );\n};\n"],"names":[],"mappings":";;;;;;;;;;AA0BO,MAAM,kBAAkB,CAAC;AAAA,EAC9B,cAAA;AAAA,EACA,eAAA;AAAA,EACA,MAAA;AAAA,EACA,MAAA;AAAA,EACA,SAAA;AAAA,EACA;AACF,CAAA,KAOM;AACJ,EAAA,MAAM,SAAA,GAAY,OAAO,YAAY,CAAA;AACrC,EAAA,MAAM,gBAAA,GAAmB,UAAU,0BAAA,EAA2B;AAC9D,EAAA,MAAM,cAAA,GACJ,2BAAA,CAA4B,MAAM,CAAA,IAAK,gBAAA;AACzC,EAAA,uBACE,GAAA,CAAC,IAAA,EAAA,EAAK,SAAA,EAAS,IAAA,EAAC,OAAA,EAAS,CAAA,EAAG,SAAA,EAAU,QAAA,EACpC,QAAA,kBAAA,IAAA,CAAC,IAAA,EAAA,EAAK,SAAA,EAAS,IAAA,EAAC,SAAS,CAAA,EACvB,QAAA,EAAA;AAAA,oBAAA,GAAA,CAAC,IAAA,EAAA,EAAK,EAAA,EAAI,EAAA,EAAI,EAAA,EAAI,EAAA,EAChB,QAAA,kBAAA,GAAA;AAAA,MAAC,UAAA;AAAA,MAAA;AAAA,QACC,UAAA,EAAY,cAAA;AAAA,QACZ,WAAA,EAAa,eAAA;AAAA,QACb;AAAA;AAAA,KACF,EACF,CAAA;AAAA,IACC,kCACC,IAAA,CAAA,QAAA,EAAA,EACE,QAAA,EAAA;AAAA,sBAAA,GAAA,CAAC,QAAK,EAAA,EAAI,EAAA,EAAI,EAAA,EAAI,CAAA,EAAG,IAAI,CAAA,EACvB,QAAA,kBAAA,GAAA;AAAA,QAAC,aAAA;AAAA,QAAA;AAAA,UACC,MAAA;AAAA,UACA,aAAA;AAAA,UACA;AAAA;AAAA,OACF,EACF,CAAA;AAAA,0BACC,IAAA,EAAA,EAAK,EAAA,EAAI,IAAI,EAAA,EAAI,CAAA,EAAG,IAAI,CAAA,EACvB,QAAA,kBAAA,GAAA;AAAA,QAAC,gBAAA;AAAA,QAAA;AAAA,UACC,MAAA;AAAA,UACA,aAAA;AAAA,UACA;AAAA;AAAA,OACF,EACF,CAAA;AAAA,0BACC,IAAA,EAAA,EAAK,EAAA,EAAI,IAAI,EAAA,EAAI,CAAA,EAAG,IAAI,CAAA,EACvB,QAAA,kBAAA,GAAA;AAAA,QAAC,gBAAA;AAAA,QAAA;AAAA,UACC,MAAA;AAAA,UACA,aAAA;AAAA,UACA;AAAA;AAAA,OACF,EACF;AAAA,KAAA,EACF;AAAA,GAAA,EAEJ,CAAA,EACF,CAAA;AAEJ;;;;"}
@@ -1,4 +1,4 @@
1
- import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
1
+ import { jsx, jsxs } from 'react/jsx-runtime';
2
2
  import Grid from '@mui/material/Unstable_Grid2';
3
3
  import { TopLanguagesTile } from '../tiles/TopLanguagesTile.esm.js';
4
4
  import { TopRiskTile } from '../tiles/TopRiskTile.esm.js';
@@ -9,26 +9,49 @@ import { apiiroApiRef } from '../../api/index.esm.js';
9
9
 
10
10
  const WidgetMetricsGroup = ({
11
11
  repositoryData,
12
+ applicationData,
12
13
  repoId,
13
14
  entityRef,
14
- entity
15
+ entity,
16
+ applicationId
15
17
  }) => {
16
18
  const apiiroApi = useApi(apiiroApiRef);
17
19
  const defaultViewChart = apiiroApi.getDefaultAllowMetricsView();
18
20
  const allowViewChart = isApiiroMetricViewAvailable(entity) ?? defaultViewChart;
21
+ const detailViewUrl = repoId ? repositoryData?.entityUrl : applicationData?.entityUrl;
22
+ const transformApplicationLanguages = (languages) => {
23
+ if (!languages) return {};
24
+ const totalPercentage = languages.reduce((sum, item) => {
25
+ return sum + (item.percentage || 0);
26
+ }, 0);
27
+ if (totalPercentage === 0) return {};
28
+ return languages.reduce((acc, item) => {
29
+ if (item.language && item.percentage !== void 0) {
30
+ acc[item.language] = item.percentage / totalPercentage * 100;
31
+ }
32
+ return acc;
33
+ }, {});
34
+ };
35
+ const languagePercentages = repositoryData?.languagePercentages || transformApplicationLanguages(applicationData?.languagePercentages) || {};
19
36
  return /* @__PURE__ */ jsx(Grid, { container: true, spacing: 3, direction: "column", children: /* @__PURE__ */ jsxs(Grid, { container: true, spacing: 3, children: [
20
37
  /* @__PURE__ */ jsx(Grid, { xs: 12, sm: 12, children: /* @__PURE__ */ jsx(
21
38
  StatusTile,
22
39
  {
23
40
  repository: repositoryData,
24
- detailViewLink: `${repositoryData.entityUrl}/apiiro`,
41
+ application: applicationData,
42
+ detailViewLink: `${detailViewUrl}/apiiro`,
25
43
  allowViewChart
26
44
  }
27
45
  ) }),
28
- allowViewChart && /* @__PURE__ */ jsxs(Fragment, { children: [
29
- /* @__PURE__ */ jsx(Grid, { xs: 12, sm: 6, lg: 6, children: /* @__PURE__ */ jsx(TopLanguagesTile, { data: repositoryData?.languagePercentages }) }),
30
- /* @__PURE__ */ jsx(Grid, { xs: 12, sm: 6, lg: 6, children: /* @__PURE__ */ jsx(TopRiskTile, { repoId, entityRef }) })
31
- ] })
46
+ allowViewChart && repositoryData && /* @__PURE__ */ jsx(Grid, { xs: 12, sm: 6, lg: 6, children: /* @__PURE__ */ jsx(TopLanguagesTile, { data: languagePercentages }) }),
47
+ allowViewChart && /* @__PURE__ */ jsx(Grid, { xs: 12, sm: 6, lg: applicationData ? 12 : 6, children: /* @__PURE__ */ jsx(
48
+ TopRiskTile,
49
+ {
50
+ repoId,
51
+ applicationId,
52
+ entityRef
53
+ }
54
+ ) })
32
55
  ] }) });
33
56
  };
34
57
 
@@ -1 +1 @@
1
- {"version":3,"file":"WidgetMetricsGroup.esm.js","sources":["../../../src/components/MetricsGroup/WidgetMetricsGroup.tsx"],"sourcesContent":["/*\n * Copyright 2025 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport Grid from '@mui/material/Unstable_Grid2';\nimport { TopLanguagesTile } from '../tiles/TopLanguagesTile';\nimport { TopRiskTile } from '../tiles/TopRiskTile';\nimport StatusTile from '../tiles/StatusTile';\nimport { RepositoryType } from '../../queries';\nimport { useApi } from '@backstage/core-plugin-api';\nimport { isApiiroMetricViewAvailable } from '../../utils';\nimport type { Entity } from '@backstage/catalog-model';\nimport { apiiroApiRef } from '../../api';\n\nexport const WidgetMetricsGroup = ({\n repositoryData,\n repoId,\n entityRef,\n entity,\n}: {\n repositoryData: RepositoryType;\n repoId: string;\n entityRef: string;\n entity: Entity;\n}) => {\n const apiiroApi = useApi(apiiroApiRef);\n const defaultViewChart = apiiroApi.getDefaultAllowMetricsView();\n const allowViewChart =\n isApiiroMetricViewAvailable(entity) ?? defaultViewChart;\n return (\n <Grid container spacing={3} direction=\"column\">\n <Grid container spacing={3}>\n <Grid xs={12} sm={12}>\n <StatusTile\n repository={repositoryData}\n detailViewLink={`${repositoryData.entityUrl}/apiiro`}\n allowViewChart={allowViewChart}\n />\n </Grid>\n {allowViewChart && (\n <>\n <Grid xs={12} sm={6} lg={6}>\n <TopLanguagesTile data={repositoryData?.languagePercentages} />\n </Grid>\n <Grid xs={12} sm={6} lg={6}>\n <TopRiskTile repoId={repoId} entityRef={entityRef} />\n </Grid>\n </>\n )}\n </Grid>\n </Grid>\n );\n};\n"],"names":[],"mappings":";;;;;;;;;AAyBO,MAAM,qBAAqB,CAAC;AAAA,EACjC,cAAA;AAAA,EACA,MAAA;AAAA,EACA,SAAA;AAAA,EACA;AACF,CAAA,KAKM;AACJ,EAAA,MAAM,SAAA,GAAY,OAAO,YAAY,CAAA;AACrC,EAAA,MAAM,gBAAA,GAAmB,UAAU,0BAAA,EAA2B;AAC9D,EAAA,MAAM,cAAA,GACJ,2BAAA,CAA4B,MAAM,CAAA,IAAK,gBAAA;AACzC,EAAA,uBACE,GAAA,CAAC,IAAA,EAAA,EAAK,SAAA,EAAS,IAAA,EAAC,OAAA,EAAS,CAAA,EAAG,SAAA,EAAU,QAAA,EACpC,QAAA,kBAAA,IAAA,CAAC,IAAA,EAAA,EAAK,SAAA,EAAS,IAAA,EAAC,SAAS,CAAA,EACvB,QAAA,EAAA;AAAA,oBAAA,GAAA,CAAC,IAAA,EAAA,EAAK,EAAA,EAAI,EAAA,EAAI,EAAA,EAAI,EAAA,EAChB,QAAA,kBAAA,GAAA;AAAA,MAAC,UAAA;AAAA,MAAA;AAAA,QACC,UAAA,EAAY,cAAA;AAAA,QACZ,cAAA,EAAgB,CAAA,EAAG,cAAA,CAAe,SAAS,CAAA,OAAA,CAAA;AAAA,QAC3C;AAAA;AAAA,KACF,EACF,CAAA;AAAA,IACC,kCACC,IAAA,CAAA,QAAA,EAAA,EACE,QAAA,EAAA;AAAA,sBAAA,GAAA,CAAC,IAAA,EAAA,EAAK,EAAA,EAAI,EAAA,EAAI,EAAA,EAAI,CAAA,EAAG,EAAA,EAAI,CAAA,EACvB,QAAA,kBAAA,GAAA,CAAC,gBAAA,EAAA,EAAiB,IAAA,EAAM,cAAA,EAAgB,mBAAA,EAAqB,CAAA,EAC/D,CAAA;AAAA,sBACA,GAAA,CAAC,IAAA,EAAA,EAAK,EAAA,EAAI,EAAA,EAAI,EAAA,EAAI,CAAA,EAAG,EAAA,EAAI,CAAA,EACvB,QAAA,kBAAA,GAAA,CAAC,WAAA,EAAA,EAAY,MAAA,EAAgB,SAAA,EAAsB,CAAA,EACrD;AAAA,KAAA,EACF;AAAA,GAAA,EAEJ,CAAA,EACF,CAAA;AAEJ;;;;"}
1
+ {"version":3,"file":"WidgetMetricsGroup.esm.js","sources":["../../../src/components/MetricsGroup/WidgetMetricsGroup.tsx"],"sourcesContent":["/*\n * Copyright 2025 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport Grid from '@mui/material/Unstable_Grid2';\nimport { TopLanguagesTile } from '../tiles/TopLanguagesTile';\nimport { TopRiskTile } from '../tiles/TopRiskTile';\nimport StatusTile from '../tiles/StatusTile';\nimport { ApplicationType, RepositoryType } from '../../queries';\nimport { useApi } from '@backstage/core-plugin-api';\nimport { isApiiroMetricViewAvailable } from '../../utils';\nimport type { Entity } from '@backstage/catalog-model';\nimport { apiiroApiRef } from '../../api';\n\nexport const WidgetMetricsGroup = ({\n repositoryData,\n applicationData,\n repoId,\n entityRef,\n entity,\n applicationId,\n}: {\n repositoryData?: RepositoryType;\n applicationData?: ApplicationType;\n repoId?: string;\n entityRef: string;\n entity: Entity;\n applicationId?: string;\n}) => {\n const apiiroApi = useApi(apiiroApiRef);\n const defaultViewChart = apiiroApi.getDefaultAllowMetricsView();\n const allowViewChart =\n isApiiroMetricViewAvailable(entity) ?? defaultViewChart;\n const detailViewUrl = repoId\n ? repositoryData?.entityUrl\n : applicationData?.entityUrl;\n\n const transformApplicationLanguages = (\n languages?: { language?: string; percentage?: number }[],\n ): Record<string, number> => {\n if (!languages) return {};\n\n const totalPercentage = languages.reduce((sum, item) => {\n return sum + (item.percentage || 0);\n }, 0);\n\n if (totalPercentage === 0) return {};\n\n return languages.reduce((acc, item) => {\n if (item.language && item.percentage !== undefined) {\n acc[item.language] = (item.percentage / totalPercentage) * 100;\n }\n return acc;\n }, {} as Record<string, number>);\n };\n\n const languagePercentages =\n repositoryData?.languagePercentages ||\n transformApplicationLanguages(applicationData?.languagePercentages) ||\n {};\n\n return (\n <Grid container spacing={3} direction=\"column\">\n <Grid container spacing={3}>\n <Grid xs={12} sm={12}>\n <StatusTile\n repository={repositoryData}\n application={applicationData}\n detailViewLink={`${detailViewUrl}/apiiro`}\n allowViewChart={allowViewChart}\n />\n </Grid>\n {allowViewChart && repositoryData && (\n <Grid xs={12} sm={6} lg={6}>\n <TopLanguagesTile data={languagePercentages} />\n </Grid>\n )}\n {allowViewChart && (\n <Grid xs={12} sm={6} lg={applicationData ? 12 : 6}>\n <TopRiskTile\n repoId={repoId}\n applicationId={applicationId}\n entityRef={entityRef}\n />\n </Grid>\n )}\n </Grid>\n </Grid>\n );\n};\n"],"names":[],"mappings":";;;;;;;;;AAyBO,MAAM,qBAAqB,CAAC;AAAA,EACjC,cAAA;AAAA,EACA,eAAA;AAAA,EACA,MAAA;AAAA,EACA,SAAA;AAAA,EACA,MAAA;AAAA,EACA;AACF,CAAA,KAOM;AACJ,EAAA,MAAM,SAAA,GAAY,OAAO,YAAY,CAAA;AACrC,EAAA,MAAM,gBAAA,GAAmB,UAAU,0BAAA,EAA2B;AAC9D,EAAA,MAAM,cAAA,GACJ,2BAAA,CAA4B,MAAM,CAAA,IAAK,gBAAA;AACzC,EAAA,MAAM,aAAA,GAAgB,MAAA,GAClB,cAAA,EAAgB,SAAA,GAChB,eAAA,EAAiB,SAAA;AAErB,EAAA,MAAM,6BAAA,GAAgC,CACpC,SAAA,KAC2B;AAC3B,IAAA,IAAI,CAAC,SAAA,EAAW,OAAO,EAAC;AAExB,IAAA,MAAM,eAAA,GAAkB,SAAA,CAAU,MAAA,CAAO,CAAC,KAAK,IAAA,KAAS;AACtD,MAAA,OAAO,GAAA,IAAO,KAAK,UAAA,IAAc,CAAA,CAAA;AAAA,IACnC,GAAG,CAAC,CAAA;AAEJ,IAAA,IAAI,eAAA,KAAoB,CAAA,EAAG,OAAO,EAAC;AAEnC,IAAA,OAAO,SAAA,CAAU,MAAA,CAAO,CAAC,GAAA,EAAK,IAAA,KAAS;AACrC,MAAA,IAAI,IAAA,CAAK,QAAA,IAAY,IAAA,CAAK,UAAA,KAAe,MAAA,EAAW;AAClD,QAAA,GAAA,CAAI,IAAA,CAAK,QAAQ,CAAA,GAAK,IAAA,CAAK,aAAa,eAAA,GAAmB,GAAA;AAAA,MAC7D;AACA,MAAA,OAAO,GAAA;AAAA,IACT,CAAA,EAAG,EAA4B,CAAA;AAAA,EACjC,CAAA;AAEA,EAAA,MAAM,sBACJ,cAAA,EAAgB,mBAAA,IAChB,8BAA8B,eAAA,EAAiB,mBAAmB,KAClE,EAAC;AAEH,EAAA,uBACE,GAAA,CAAC,IAAA,EAAA,EAAK,SAAA,EAAS,IAAA,EAAC,OAAA,EAAS,CAAA,EAAG,SAAA,EAAU,QAAA,EACpC,QAAA,kBAAA,IAAA,CAAC,IAAA,EAAA,EAAK,SAAA,EAAS,IAAA,EAAC,SAAS,CAAA,EACvB,QAAA,EAAA;AAAA,oBAAA,GAAA,CAAC,IAAA,EAAA,EAAK,EAAA,EAAI,EAAA,EAAI,EAAA,EAAI,EAAA,EAChB,QAAA,kBAAA,GAAA;AAAA,MAAC,UAAA;AAAA,MAAA;AAAA,QACC,UAAA,EAAY,cAAA;AAAA,QACZ,WAAA,EAAa,eAAA;AAAA,QACb,cAAA,EAAgB,GAAG,aAAa,CAAA,OAAA,CAAA;AAAA,QAChC;AAAA;AAAA,KACF,EACF,CAAA;AAAA,IACC,cAAA,IAAkB,cAAA,oBACjB,GAAA,CAAC,IAAA,EAAA,EAAK,IAAI,EAAA,EAAI,EAAA,EAAI,CAAA,EAAG,EAAA,EAAI,CAAA,EACvB,QAAA,kBAAA,GAAA,CAAC,gBAAA,EAAA,EAAiB,IAAA,EAAM,qBAAqB,CAAA,EAC/C,CAAA;AAAA,IAED,cAAA,oBACC,GAAA,CAAC,IAAA,EAAA,EAAK,EAAA,EAAI,EAAA,EAAI,IAAI,CAAA,EAAG,EAAA,EAAI,eAAA,GAAkB,EAAA,GAAK,CAAA,EAC9C,QAAA,kBAAA,GAAA;AAAA,MAAC,WAAA;AAAA,MAAA;AAAA,QACC,MAAA;AAAA,QACA,aAAA;AAAA,QACA;AAAA;AAAA,KACF,EACF;AAAA,GAAA,EAEJ,CAAA,EACF,CAAA;AAEJ;;;;"}
@@ -13,6 +13,7 @@ const createRiskColorMappings = (theme) => {
13
13
  High: riskColors.high,
14
14
  Medium: riskColors.medium,
15
15
  Low: riskColors.low,
16
+ Informational: riskColors.informational,
16
17
  AutoIgnored: riskColors.autoIgnored
17
18
  };
18
19
  };
@@ -1 +1 @@
1
- {"version":3,"file":"RiskLevel.esm.js","sources":["../../src/components/RiskLevel.tsx"],"sourcesContent":["/*\n * Copyright 2025 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { RiskLevel as RiskIcon } from '../assets/RiskIcon';\nimport Box from '@mui/material/Box';\nimport Typography from '@mui/material/Typography';\nimport VisibilityOffIcon from '@mui/icons-material/VisibilityOff';\nimport { SxProps, Theme, useTheme } from '@mui/material/styles';\nimport { getRiskColors } from '../theme/themeUtils';\n\nexport type RiskLevelValue =\n | 'High'\n | 'Medium'\n | 'Low'\n | 'Critical'\n | 'AutoIgnored';\n\nexport interface ColorMapping {\n [key: string]: string;\n}\n\nexport interface RiskLevelProps {\n /**\n * The risk level value to display\n */\n level: string;\n\n /**\n * Default color to use if level is not found in colorMapping\n */\n defaultColor?: string;\n\n /**\n * Size of the icon\n */\n iconSize?: 'small' | 'medium' | 'large';\n\n /**\n * Whether to show the text label alongside the icon\n */\n showLabel?: boolean;\n\n /**\n * Custom styles for the container\n */\n sx?: SxProps<Theme>;\n\n /**\n * Custom styles for the icon\n */\n iconSx?: SxProps<Theme>;\n\n /**\n * Custom styles for the text\n */\n textSx?: SxProps<Theme>;\n}\n\n/**\n * RiskLevel component that displays a custom RiskIcon with customizable colors\n *\n * @example\n * // Basic usage with color mapping\n * <RiskLevel\n * level=\"High\"\n * />\n *\n * @example\n * // With label and custom styling\n * <RiskLevel\n * level=\"Critical\"\n * showLabel={true}\n * iconSize=\"large\"\n * />\n */\n\n/**\n * Creates theme-aware risk color mappings.\n * Use this function with useTheme() to get colors that work in both light and dark modes.\n */\nexport const createRiskColorMappings = (theme: Theme): ColorMapping => {\n const riskColors = getRiskColors(theme);\n\n return {\n Critical: riskColors.critical,\n High: riskColors.high,\n Medium: riskColors.medium,\n Low: riskColors.low,\n AutoIgnored: riskColors.autoIgnored,\n };\n};\n\nexport const RiskLevel = ({\n level,\n defaultColor,\n iconSize = 'medium',\n showLabel = false,\n sx,\n iconSx,\n textSx,\n}: RiskLevelProps) => {\n const theme = useTheme();\n const themeDefaultColor = defaultColor ?? theme.palette.grey[500];\n const colorMapping: ColorMapping = createRiskColorMappings(theme);\n // Determine the color based on mapping\n const getRiskColor = (): string => {\n if (!colorMapping) {\n return themeDefaultColor;\n }\n\n const mappedColor = colorMapping[level];\n return mappedColor || themeDefaultColor;\n };\n\n const riskColor = getRiskColor();\n\n // Icon size mapping for custom SVG\n const iconSizeMap = {\n small: { width: 16, height: 16 },\n medium: { width: 20, height: 20 },\n large: { width: 24, height: 24 },\n };\n\n return (\n <Box\n sx={{\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n gap: showLabel ? 1 : 0,\n ...sx,\n }}\n >\n <Box\n sx={{\n color: riskColor,\n borderRadius: '50%',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n ...iconSizeMap[iconSize],\n ...iconSx,\n }}\n >\n {level === 'AutoIgnored' ? (\n <VisibilityOffIcon sx={{ fontSize: iconSizeMap[iconSize].width }} />\n ) : (\n <RiskIcon />\n )}\n </Box>\n {showLabel && (\n <Typography\n variant=\"body2\"\n sx={{\n color: riskColor,\n fontWeight: 500,\n ...textSx,\n }}\n >\n {level}\n </Typography>\n )}\n </Box>\n );\n};\n\nexport default RiskLevel;\n"],"names":["RiskIcon"],"mappings":";;;;;;;;AA4FO,MAAM,uBAAA,GAA0B,CAAC,KAAA,KAA+B;AACrE,EAAA,MAAM,UAAA,GAAa,cAAc,KAAK,CAAA;AAEtC,EAAA,OAAO;AAAA,IACL,UAAU,UAAA,CAAW,QAAA;AAAA,IACrB,MAAM,UAAA,CAAW,IAAA;AAAA,IACjB,QAAQ,UAAA,CAAW,MAAA;AAAA,IACnB,KAAK,UAAA,CAAW,GAAA;AAAA,IAChB,aAAa,UAAA,CAAW;AAAA,GAC1B;AACF;AAEO,MAAM,YAAY,CAAC;AAAA,EACxB,KAAA;AAAA,EACA,YAAA;AAAA,EACA,QAAA,GAAW,QAAA;AAAA,EACX,SAAA,GAAY,KAAA;AAAA,EACZ,EAAA;AAAA,EACA,MAAA;AAAA,EACA;AACF,CAAA,KAAsB;AACpB,EAAA,MAAM,QAAQ,QAAA,EAAS;AACvB,EAAA,MAAM,iBAAA,GAAoB,YAAA,IAAgB,KAAA,CAAM,OAAA,CAAQ,KAAK,GAAG,CAAA;AAChE,EAAA,MAAM,YAAA,GAA6B,wBAAwB,KAAK,CAAA;AAEhE,EAAA,MAAM,eAAe,MAAc;AACjC,IAAA,IAAI,CAAC,YAAA,EAAc;AACjB,MAAA,OAAO,iBAAA;AAAA,IACT;AAEA,IAAA,MAAM,WAAA,GAAc,aAAa,KAAK,CAAA;AACtC,IAAA,OAAO,WAAA,IAAe,iBAAA;AAAA,EACxB,CAAA;AAEA,EAAA,MAAM,YAAY,YAAA,EAAa;AAG/B,EAAA,MAAM,WAAA,GAAc;AAAA,IAClB,KAAA,EAAO,EAAE,KAAA,EAAO,EAAA,EAAI,QAAQ,EAAA,EAAG;AAAA,IAC/B,MAAA,EAAQ,EAAE,KAAA,EAAO,EAAA,EAAI,QAAQ,EAAA,EAAG;AAAA,IAChC,KAAA,EAAO,EAAE,KAAA,EAAO,EAAA,EAAI,QAAQ,EAAA;AAAG,GACjC;AAEA,EAAA,uBACE,IAAA;AAAA,IAAC,GAAA;AAAA,IAAA;AAAA,MACC,EAAA,EAAI;AAAA,QACF,OAAA,EAAS,MAAA;AAAA,QACT,UAAA,EAAY,QAAA;AAAA,QACZ,cAAA,EAAgB,QAAA;AAAA,QAChB,GAAA,EAAK,YAAY,CAAA,GAAI,CAAA;AAAA,QACrB,GAAG;AAAA,OACL;AAAA,MAEA,QAAA,EAAA;AAAA,wBAAA,GAAA;AAAA,UAAC,GAAA;AAAA,UAAA;AAAA,YACC,EAAA,EAAI;AAAA,cACF,KAAA,EAAO,SAAA;AAAA,cACP,YAAA,EAAc,KAAA;AAAA,cACd,OAAA,EAAS,MAAA;AAAA,cACT,UAAA,EAAY,QAAA;AAAA,cACZ,cAAA,EAAgB,QAAA;AAAA,cAChB,GAAG,YAAY,QAAQ,CAAA;AAAA,cACvB,GAAG;AAAA,aACL;AAAA,YAEC,QAAA,EAAA,KAAA,KAAU,aAAA,mBACT,GAAA,CAAC,iBAAA,EAAA,EAAkB,IAAI,EAAE,QAAA,EAAU,WAAA,CAAY,QAAQ,CAAA,CAAE,KAAA,EAAM,EAAG,CAAA,uBAEjEA,WAAA,EAAA,EAAS;AAAA;AAAA,SAEd;AAAA,QACC,SAAA,oBACC,GAAA;AAAA,UAAC,UAAA;AAAA,UAAA;AAAA,YACC,OAAA,EAAQ,OAAA;AAAA,YACR,EAAA,EAAI;AAAA,cACF,KAAA,EAAO,SAAA;AAAA,cACP,UAAA,EAAY,GAAA;AAAA,cACZ,GAAG;AAAA,aACL;AAAA,YAEC,QAAA,EAAA;AAAA;AAAA;AACH;AAAA;AAAA,GAEJ;AAEJ;;;;"}
1
+ {"version":3,"file":"RiskLevel.esm.js","sources":["../../src/components/RiskLevel.tsx"],"sourcesContent":["/*\n * Copyright 2025 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { RiskLevel as RiskIcon } from '../assets/RiskIcon';\nimport Box from '@mui/material/Box';\nimport Typography from '@mui/material/Typography';\nimport VisibilityOffIcon from '@mui/icons-material/VisibilityOff';\nimport { SxProps, Theme, useTheme } from '@mui/material/styles';\nimport { getRiskColors } from '../theme/themeUtils';\n\nexport type RiskLevelValue =\n | 'High'\n | 'Medium'\n | 'Low'\n | 'Critical'\n | 'Informational'\n | 'AutoIgnored';\n\nexport interface ColorMapping {\n [key: string]: string;\n}\n\nexport interface RiskLevelProps {\n /**\n * The risk level value to display\n */\n level: string;\n\n /**\n * Default color to use if level is not found in colorMapping\n */\n defaultColor?: string;\n\n /**\n * Size of the icon\n */\n iconSize?: 'small' | 'medium' | 'large';\n\n /**\n * Whether to show the text label alongside the icon\n */\n showLabel?: boolean;\n\n /**\n * Custom styles for the container\n */\n sx?: SxProps<Theme>;\n\n /**\n * Custom styles for the icon\n */\n iconSx?: SxProps<Theme>;\n\n /**\n * Custom styles for the text\n */\n textSx?: SxProps<Theme>;\n}\n\n/**\n * RiskLevel component that displays a custom RiskIcon with customizable colors\n *\n * @example\n * // Basic usage with color mapping\n * <RiskLevel\n * level=\"High\"\n * />\n *\n * @example\n * // With label and custom styling\n * <RiskLevel\n * level=\"Critical\"\n * showLabel={true}\n * iconSize=\"large\"\n * />\n */\n\n/**\n * Creates theme-aware risk color mappings.\n * Use this function with useTheme() to get colors that work in both light and dark modes.\n */\nexport const createRiskColorMappings = (theme: Theme): ColorMapping => {\n const riskColors = getRiskColors(theme);\n\n return {\n Critical: riskColors.critical,\n High: riskColors.high,\n Medium: riskColors.medium,\n Low: riskColors.low,\n Informational: riskColors.informational,\n AutoIgnored: riskColors.autoIgnored,\n };\n};\n\nexport const RiskLevel = ({\n level,\n defaultColor,\n iconSize = 'medium',\n showLabel = false,\n sx,\n iconSx,\n textSx,\n}: RiskLevelProps) => {\n const theme = useTheme();\n const themeDefaultColor = defaultColor ?? theme.palette.grey[500];\n const colorMapping: ColorMapping = createRiskColorMappings(theme);\n // Determine the color based on mapping\n const getRiskColor = (): string => {\n if (!colorMapping) {\n return themeDefaultColor;\n }\n\n const mappedColor = colorMapping[level];\n return mappedColor || themeDefaultColor;\n };\n\n const riskColor = getRiskColor();\n\n // Icon size mapping for custom SVG\n const iconSizeMap = {\n small: { width: 16, height: 16 },\n medium: { width: 20, height: 20 },\n large: { width: 24, height: 24 },\n };\n\n return (\n <Box\n sx={{\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n gap: showLabel ? 1 : 0,\n ...sx,\n }}\n >\n <Box\n sx={{\n color: riskColor,\n borderRadius: '50%',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n ...iconSizeMap[iconSize],\n ...iconSx,\n }}\n >\n {level === 'AutoIgnored' ? (\n <VisibilityOffIcon sx={{ fontSize: iconSizeMap[iconSize].width }} />\n ) : (\n <RiskIcon />\n )}\n </Box>\n {showLabel && (\n <Typography\n variant=\"body2\"\n sx={{\n color: riskColor,\n fontWeight: 500,\n ...textSx,\n }}\n >\n {level}\n </Typography>\n )}\n </Box>\n );\n};\n\nexport default RiskLevel;\n"],"names":["RiskIcon"],"mappings":";;;;;;;;AA6FO,MAAM,uBAAA,GAA0B,CAAC,KAAA,KAA+B;AACrE,EAAA,MAAM,UAAA,GAAa,cAAc,KAAK,CAAA;AAEtC,EAAA,OAAO;AAAA,IACL,UAAU,UAAA,CAAW,QAAA;AAAA,IACrB,MAAM,UAAA,CAAW,IAAA;AAAA,IACjB,QAAQ,UAAA,CAAW,MAAA;AAAA,IACnB,KAAK,UAAA,CAAW,GAAA;AAAA,IAChB,eAAe,UAAA,CAAW,aAAA;AAAA,IAC1B,aAAa,UAAA,CAAW;AAAA,GAC1B;AACF;AAEO,MAAM,YAAY,CAAC;AAAA,EACxB,KAAA;AAAA,EACA,YAAA;AAAA,EACA,QAAA,GAAW,QAAA;AAAA,EACX,SAAA,GAAY,KAAA;AAAA,EACZ,EAAA;AAAA,EACA,MAAA;AAAA,EACA;AACF,CAAA,KAAsB;AACpB,EAAA,MAAM,QAAQ,QAAA,EAAS;AACvB,EAAA,MAAM,iBAAA,GAAoB,YAAA,IAAgB,KAAA,CAAM,OAAA,CAAQ,KAAK,GAAG,CAAA;AAChE,EAAA,MAAM,YAAA,GAA6B,wBAAwB,KAAK,CAAA;AAEhE,EAAA,MAAM,eAAe,MAAc;AACjC,IAAA,IAAI,CAAC,YAAA,EAAc;AACjB,MAAA,OAAO,iBAAA;AAAA,IACT;AAEA,IAAA,MAAM,WAAA,GAAc,aAAa,KAAK,CAAA;AACtC,IAAA,OAAO,WAAA,IAAe,iBAAA;AAAA,EACxB,CAAA;AAEA,EAAA,MAAM,YAAY,YAAA,EAAa;AAG/B,EAAA,MAAM,WAAA,GAAc;AAAA,IAClB,KAAA,EAAO,EAAE,KAAA,EAAO,EAAA,EAAI,QAAQ,EAAA,EAAG;AAAA,IAC/B,MAAA,EAAQ,EAAE,KAAA,EAAO,EAAA,EAAI,QAAQ,EAAA,EAAG;AAAA,IAChC,KAAA,EAAO,EAAE,KAAA,EAAO,EAAA,EAAI,QAAQ,EAAA;AAAG,GACjC;AAEA,EAAA,uBACE,IAAA;AAAA,IAAC,GAAA;AAAA,IAAA;AAAA,MACC,EAAA,EAAI;AAAA,QACF,OAAA,EAAS,MAAA;AAAA,QACT,UAAA,EAAY,QAAA;AAAA,QACZ,cAAA,EAAgB,QAAA;AAAA,QAChB,GAAA,EAAK,YAAY,CAAA,GAAI,CAAA;AAAA,QACrB,GAAG;AAAA,OACL;AAAA,MAEA,QAAA,EAAA;AAAA,wBAAA,GAAA;AAAA,UAAC,GAAA;AAAA,UAAA;AAAA,YACC,EAAA,EAAI;AAAA,cACF,KAAA,EAAO,SAAA;AAAA,cACP,YAAA,EAAc,KAAA;AAAA,cACd,OAAA,EAAS,MAAA;AAAA,cACT,UAAA,EAAY,QAAA;AAAA,cACZ,cAAA,EAAgB,QAAA;AAAA,cAChB,GAAG,YAAY,QAAQ,CAAA;AAAA,cACvB,GAAG;AAAA,aACL;AAAA,YAEC,QAAA,EAAA,KAAA,KAAU,aAAA,mBACT,GAAA,CAAC,iBAAA,EAAA,EAAkB,IAAI,EAAE,QAAA,EAAU,WAAA,CAAY,QAAQ,CAAA,CAAE,KAAA,EAAM,EAAG,CAAA,uBAEjEA,WAAA,EAAA,EAAS;AAAA;AAAA,SAEd;AAAA,QACC,SAAA,oBACC,GAAA;AAAA,UAAC,UAAA;AAAA,UAAA;AAAA,YACC,OAAA,EAAQ,OAAA;AAAA,YACR,EAAA,EAAI;AAAA,cACF,KAAA,EAAO,SAAA;AAAA,cACP,UAAA,EAAY,GAAA;AAAA,cACZ,GAAG;AAAA,aACL;AAAA,YAEC,QAAA,EAAA;AAAA;AAAA;AACH;AAAA;AAAA,GAEJ;AAEJ;;;;"}
@@ -7,7 +7,7 @@ import { CustomTooltip } from '../common/CustomTooltip.esm.js';
7
7
  import Typography from '@mui/material/Typography';
8
8
 
9
9
  const getArcColor = (value, tickValue, gaugeColors) => {
10
- if (tickValue === 0) {
10
+ if (tickValue === null || tickValue === 0) {
11
11
  return gaugeColors.background;
12
12
  }
13
13
  return value > tickValue ? gaugeColors.warning : gaugeColors.success;
@@ -52,21 +52,23 @@ function GaugeBottomLabels({
52
52
  minValue,
53
53
  maxValue,
54
54
  categoryLabel,
55
+ tickValue,
55
56
  width = "135px"
56
57
  }) {
57
58
  const theme = useTheme();
59
+ const shouldShowLabels = tickValue !== null;
58
60
  return /* @__PURE__ */ jsxs(
59
61
  "div",
60
62
  {
61
63
  style: {
62
64
  display: "flex",
63
- justifyContent: "space-between",
65
+ justifyContent: shouldShowLabels ? "space-between" : "center",
64
66
  alignItems: "center",
65
67
  width,
66
68
  marginTop: 7
67
69
  },
68
70
  children: [
69
- /* @__PURE__ */ jsx(
71
+ shouldShowLabels && /* @__PURE__ */ jsx(
70
72
  Typography,
71
73
  {
72
74
  style: { fontSize: "14px", color: theme.palette.text.secondary },
@@ -84,7 +86,7 @@ function GaugeBottomLabels({
84
86
  children: categoryLabel
85
87
  }
86
88
  ),
87
- /* @__PURE__ */ jsx(
89
+ shouldShowLabels && /* @__PURE__ */ jsx(
88
90
  Typography,
89
91
  {
90
92
  style: { fontSize: "14px", color: theme.palette.text.secondary },
@@ -191,7 +193,7 @@ function GaugeChart({
191
193
  }
192
194
  };
193
195
  }, [value]);
194
- const margin = { top: 20, right: 20, bottom: 20, left: 20 };
196
+ const margin = { top: 20, right: 35, bottom: 20, left: 35 };
195
197
  const innerWidth = width - margin.left - margin.right;
196
198
  const innerHeight = height - margin.top - margin.bottom;
197
199
  const outerRadius = Math.min(innerWidth, innerHeight) / 2.2;
@@ -227,7 +229,7 @@ function GaugeChart({
227
229
  markerValue: tickValue,
228
230
  min: minValue,
229
231
  max: maxValue,
230
- displayMarkerValue: displayTickValue
232
+ displayMarkerValue: displayTickValue || void 0
231
233
  }
232
234
  ),
233
235
  /* @__PURE__ */ jsx(
@@ -1 +1 @@
1
- {"version":3,"file":"GaugeChart.esm.js","sources":["../../../src/components/charts/GaugeChart.tsx"],"sourcesContent":["/*\n * Copyright 2025 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { useGaugeState, Gauge, gaugeClasses } from '@mui/x-charts/Gauge';\nimport { useState, useEffect, useRef } from 'react';\nimport { useTheme } from '@mui/material/styles';\nimport { getGaugeColors } from '../../theme/themeUtils';\nimport CustomTooltip from '../common/CustomTooltip';\nimport Typography from '@mui/material/Typography';\n\nconst getArcColor = (\n value: number,\n tickValue: number,\n gaugeColors: ReturnType<typeof getGaugeColors>,\n) => {\n if (tickValue === 0) {\n return gaugeColors.background;\n }\n return value > tickValue ? gaugeColors.warning : gaugeColors.success;\n};\n\nfunction GaugeCenterLabel({\n value,\n width,\n height,\n unit = 'Hours',\n}: {\n value: number;\n width: number;\n height: number;\n unit?: string;\n}) {\n const theme = useTheme();\n return (\n <g>\n <text\n x={width / 2}\n y={height / 2 / 2 + 20}\n fontSize=\"24px\"\n fontWeight=\"bold\"\n fill={theme.palette.text.primary}\n textAnchor=\"middle\"\n dominantBaseline=\"middle\"\n >\n {value ? Math.round(value) : '-'}\n </text>\n <text\n x={width / 2}\n y={height / 2 / 2 + 40}\n fontSize=\"12px\"\n fontWeight=\"100\"\n fill={theme.palette.text.secondary}\n textAnchor=\"middle\"\n dominantBaseline=\"middle\"\n >\n {value ? unit : null}\n </text>\n </g>\n );\n}\n\nfunction GaugeBottomLabels({\n minValue,\n maxValue,\n categoryLabel,\n width = '135px',\n}: {\n minValue: number;\n maxValue: number;\n categoryLabel: string;\n width?: string;\n}) {\n const theme = useTheme();\n return (\n <div\n style={{\n display: 'flex',\n justifyContent: 'space-between',\n alignItems: 'center',\n width,\n marginTop: 7,\n }}\n >\n <Typography\n style={{ fontSize: '14px', color: theme.palette.text.secondary }}\n >\n {minValue}\n </Typography>\n <Typography\n style={{\n fontSize: '14px',\n fontWeight: 400,\n color: theme.palette.text.primary,\n }}\n >\n {categoryLabel}\n </Typography>\n <Typography\n style={{ fontSize: '14px', color: theme.palette.text.secondary }}\n >\n {maxValue}\n </Typography>\n </div>\n );\n}\n\nfunction GaugePointer({\n markerValue,\n min = 0,\n max = 100,\n displayMarkerValue,\n}: {\n markerValue: number | null | undefined;\n min?: number;\n max?: number;\n displayMarkerValue?: number;\n}) {\n const theme = useTheme();\n const gaugeColors = getGaugeColors(theme);\n const { startAngle, endAngle, innerRadius, outerRadius, cx, cy } =\n useGaugeState();\n\n // Handle null or undefined values - show tick in middle with \"_\" label\n if (markerValue === null || markerValue === undefined) {\n return null;\n }\n\n // Clamp markerValue between min and max\n const clampedValue = Math.min(Math.max(markerValue, min), max);\n\n // Map markerValue to angle (radians)\n const valueRatio = (clampedValue - min) / (max - min);\n const valueAngle = startAngle + valueRatio * (endAngle - startAngle);\n\n // Pointer line endpoints\n const x1 = cx + innerRadius * Math.sin(valueAngle);\n const y1 = cy - innerRadius * Math.cos(valueAngle);\n\n const x2 = cx + outerRadius * Math.sin(valueAngle);\n const y2 = cy - outerRadius * Math.cos(valueAngle);\n\n // Label position outside outer radius\n const labelOffset = 10; // pixels outside outerRadius\n const labelRadius = outerRadius + labelOffset;\n\n const labelX = cx + labelRadius * Math.sin(valueAngle);\n const labelY = cy - labelRadius * Math.cos(valueAngle);\n\n return (\n <g>\n {/* Pointer line */}\n <line\n x1={x1}\n y1={y1}\n x2={x2}\n y2={y2}\n stroke={gaugeColors.pointer}\n strokeWidth={2}\n strokeLinecap=\"round\"\n />\n\n {/* Marker value label positioned outside outerRadius */}\n <text\n x={labelX}\n y={labelY}\n fontSize=\"12px\"\n textAnchor=\"middle\"\n dominantBaseline=\"middle\"\n fill={theme.palette.text.primary}\n >\n {displayMarkerValue || markerValue}*\n </text>\n </g>\n );\n}\n\nexport { GaugeBottomLabels };\n\nexport default function GaugeChart({\n width,\n height,\n value,\n tickValue,\n minValue,\n maxValue,\n tooltip,\n unit = 'Hours',\n displayValue,\n displayTickValue,\n}: {\n width: number;\n height: number;\n value: number;\n tickValue: number;\n minValue: number;\n maxValue: number;\n tooltip?: string;\n unit?: string;\n displayValue?: number;\n displayTickValue?: number;\n}) {\n const theme = useTheme();\n const gaugeColors = getGaugeColors(theme);\n const [animatedValue, setAnimatedValue] = useState(0);\n const animationRef = useRef<number>();\n const currentValueRef = useRef(0);\n\n // Custom animation function\n const animateValue = (\n start: number,\n end: number,\n duration: number = 1000,\n ) => {\n if (animationRef.current) {\n cancelAnimationFrame(animationRef.current);\n }\n\n const startTime = Date.now();\n const animate = () => {\n const elapsed = Date.now() - startTime;\n const progress = Math.min(elapsed / duration, 1);\n\n // Easing function (ease-out)\n const easeOut = 1 - Math.pow(1 - progress, 3);\n const currentValue = start + (end - start) * easeOut;\n\n setAnimatedValue(currentValue);\n currentValueRef.current = currentValue;\n\n if (progress < 1) {\n animationRef.current = requestAnimationFrame(animate);\n }\n };\n\n animate();\n };\n\n // Animate when value changes\n useEffect(() => {\n animateValue(currentValueRef.current, value);\n\n return () => {\n if (animationRef.current) {\n cancelAnimationFrame(animationRef.current);\n }\n };\n }, [value]);\n\n // Define margins (adjust as needed)\n const margin = { top: 20, right: 20, bottom: 20, left: 20 };\n\n const innerWidth = width - margin.left - margin.right; // 220 - 20 - 20 = 180\n const innerHeight = height - margin.top - margin.bottom; // 200 - 20 - 20 = 160 // fixed centerY (you can adjust this)\n\n const outerRadius = Math.min(innerWidth, innerHeight) / 2.2;\n // Math.min(180, 160) / 2.2 ≈ 72.73\n\n const innerRadius = outerRadius * 0.72;\n\n const arcColor = getArcColor(value, tickValue, gaugeColors);\n\n // 72.73 * 0.67 ≈ 48.72\n\n const gaugeElement = (\n <Gauge\n width={width}\n height={height / 2}\n startAngle={-90}\n endAngle={90}\n innerRadius={innerRadius}\n outerRadius={outerRadius}\n value={animatedValue}\n valueMin={minValue}\n valueMax={maxValue}\n text={() => null}\n sx={() => ({\n [`& .${gaugeClasses.valueArc}`]: {\n fill: arcColor,\n transition: 'none', // Disable default transitions to let react-spring handle it\n },\n [`& .${gaugeClasses.referenceArc}`]: {\n fill: gaugeColors.background,\n },\n })}\n >\n <GaugePointer\n markerValue={tickValue}\n min={minValue}\n max={maxValue}\n displayMarkerValue={displayTickValue}\n />\n <GaugeCenterLabel\n value={displayValue || animatedValue}\n width={width}\n height={height}\n unit={unit}\n />\n </Gauge>\n );\n\n // If tooltip is provided, wrap with CustomTooltip, otherwise return gauge directly\n return tooltip ? (\n <div style={{ display: 'inline-block', cursor: 'pointer' }}>\n <CustomTooltip title={tooltip} placement=\"top\">\n <div style={{ width: '100%', height: '100%' }}>{gaugeElement}</div>\n </CustomTooltip>\n </div>\n ) : (\n gaugeElement\n );\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"GaugeChart.esm.js","sources":["../../../src/components/charts/GaugeChart.tsx"],"sourcesContent":["/*\n * Copyright 2025 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { useGaugeState, Gauge, gaugeClasses } from '@mui/x-charts/Gauge';\nimport { useState, useEffect, useRef } from 'react';\nimport { useTheme } from '@mui/material/styles';\nimport { getGaugeColors } from '../../theme/themeUtils';\nimport CustomTooltip from '../common/CustomTooltip';\nimport Typography from '@mui/material/Typography';\n\nconst getArcColor = (\n value: number,\n tickValue: number | null,\n gaugeColors: ReturnType<typeof getGaugeColors>,\n) => {\n if (tickValue === null || tickValue === 0) {\n return gaugeColors.background;\n }\n return value > tickValue ? gaugeColors.warning : gaugeColors.success;\n};\n\nfunction GaugeCenterLabel({\n value,\n width,\n height,\n unit = 'Hours',\n}: {\n value: number;\n width: number;\n height: number;\n unit?: string;\n}) {\n const theme = useTheme();\n return (\n <g>\n <text\n x={width / 2}\n y={height / 2 / 2 + 20}\n fontSize=\"24px\"\n fontWeight=\"bold\"\n fill={theme.palette.text.primary}\n textAnchor=\"middle\"\n dominantBaseline=\"middle\"\n >\n {value ? Math.round(value) : '-'}\n </text>\n <text\n x={width / 2}\n y={height / 2 / 2 + 40}\n fontSize=\"12px\"\n fontWeight=\"100\"\n fill={theme.palette.text.secondary}\n textAnchor=\"middle\"\n dominantBaseline=\"middle\"\n >\n {value ? unit : null}\n </text>\n </g>\n );\n}\n\nfunction GaugeBottomLabels({\n minValue,\n maxValue,\n categoryLabel,\n tickValue,\n width = '135px',\n}: {\n minValue: number;\n maxValue: number;\n categoryLabel: string;\n tickValue: number | null;\n width?: string;\n}) {\n const theme = useTheme();\n const shouldShowLabels = tickValue !== null;\n\n return (\n <div\n style={{\n display: 'flex',\n justifyContent: shouldShowLabels ? 'space-between' : 'center',\n alignItems: 'center',\n width,\n marginTop: 7,\n }}\n >\n {shouldShowLabels && (\n <Typography\n style={{ fontSize: '14px', color: theme.palette.text.secondary }}\n >\n {minValue}\n </Typography>\n )}\n <Typography\n style={{\n fontSize: '14px',\n fontWeight: 400,\n color: theme.palette.text.primary,\n }}\n >\n {categoryLabel}\n </Typography>\n {shouldShowLabels && (\n <Typography\n style={{ fontSize: '14px', color: theme.palette.text.secondary }}\n >\n {maxValue}\n </Typography>\n )}\n </div>\n );\n}\n\nfunction GaugePointer({\n markerValue,\n min = 0,\n max = 100,\n displayMarkerValue,\n}: {\n markerValue: number | null | undefined;\n min?: number;\n max?: number;\n displayMarkerValue?: number;\n}) {\n const theme = useTheme();\n const gaugeColors = getGaugeColors(theme);\n const { startAngle, endAngle, innerRadius, outerRadius, cx, cy } =\n useGaugeState();\n\n // Handle null or undefined values - show tick in middle with \"_\" label\n if (markerValue === null || markerValue === undefined) {\n return null;\n }\n\n // Clamp markerValue between min and max\n const clampedValue = Math.min(Math.max(markerValue, min), max);\n\n // Map markerValue to angle (radians)\n const valueRatio = (clampedValue - min) / (max - min);\n const valueAngle = startAngle + valueRatio * (endAngle - startAngle);\n\n // Pointer line endpoints\n const x1 = cx + innerRadius * Math.sin(valueAngle);\n const y1 = cy - innerRadius * Math.cos(valueAngle);\n\n const x2 = cx + outerRadius * Math.sin(valueAngle);\n const y2 = cy - outerRadius * Math.cos(valueAngle);\n\n // Label position outside outer radius\n const labelOffset = 10; // pixels outside outerRadius\n const labelRadius = outerRadius + labelOffset;\n\n const labelX = cx + labelRadius * Math.sin(valueAngle);\n const labelY = cy - labelRadius * Math.cos(valueAngle);\n\n return (\n <g>\n {/* Pointer line */}\n <line\n x1={x1}\n y1={y1}\n x2={x2}\n y2={y2}\n stroke={gaugeColors.pointer}\n strokeWidth={2}\n strokeLinecap=\"round\"\n />\n\n {/* Marker value label positioned outside outerRadius */}\n <text\n x={labelX}\n y={labelY}\n fontSize=\"12px\"\n textAnchor=\"middle\"\n dominantBaseline=\"middle\"\n fill={theme.palette.text.primary}\n >\n {displayMarkerValue || markerValue}*\n </text>\n </g>\n );\n}\n\nexport { GaugeBottomLabels };\n\nexport default function GaugeChart({\n width,\n height,\n value,\n tickValue,\n minValue,\n maxValue,\n tooltip,\n unit = 'Hours',\n displayValue,\n displayTickValue,\n}: {\n width: number;\n height: number;\n value: number;\n tickValue: number | null;\n minValue: number;\n maxValue: number;\n tooltip?: string;\n unit?: string;\n displayValue?: number;\n displayTickValue?: number | null;\n}) {\n const theme = useTheme();\n const gaugeColors = getGaugeColors(theme);\n const [animatedValue, setAnimatedValue] = useState(0);\n const animationRef = useRef<number>();\n const currentValueRef = useRef(0);\n\n // Custom animation function\n const animateValue = (\n start: number,\n end: number,\n duration: number = 1000,\n ) => {\n if (animationRef.current) {\n cancelAnimationFrame(animationRef.current);\n }\n\n const startTime = Date.now();\n const animate = () => {\n const elapsed = Date.now() - startTime;\n const progress = Math.min(elapsed / duration, 1);\n\n // Easing function (ease-out)\n const easeOut = 1 - Math.pow(1 - progress, 3);\n const currentValue = start + (end - start) * easeOut;\n\n setAnimatedValue(currentValue);\n currentValueRef.current = currentValue;\n\n if (progress < 1) {\n animationRef.current = requestAnimationFrame(animate);\n }\n };\n\n animate();\n };\n\n // Animate when value changes\n useEffect(() => {\n animateValue(currentValueRef.current, value);\n\n return () => {\n if (animationRef.current) {\n cancelAnimationFrame(animationRef.current);\n }\n };\n }, [value]);\n\n // Define margins (adjust as needed)\n const margin = { top: 20, right: 35, bottom: 20, left: 35 };\n\n const innerWidth = width - margin.left - margin.right;\n const innerHeight = height - margin.top - margin.bottom;\n\n const outerRadius = Math.min(innerWidth, innerHeight) / 2.2;\n\n const innerRadius = outerRadius * 0.72;\n\n const arcColor = getArcColor(value, tickValue, gaugeColors);\n\n const gaugeElement = (\n <Gauge\n width={width}\n height={height / 2}\n startAngle={-90}\n endAngle={90}\n innerRadius={innerRadius}\n outerRadius={outerRadius}\n value={animatedValue}\n valueMin={minValue}\n valueMax={maxValue}\n text={() => null}\n sx={() => ({\n [`& .${gaugeClasses.valueArc}`]: {\n fill: arcColor,\n transition: 'none', // Disable default transitions to let react-spring handle it\n },\n [`& .${gaugeClasses.referenceArc}`]: {\n fill: gaugeColors.background,\n },\n })}\n >\n <GaugePointer\n markerValue={tickValue}\n min={minValue}\n max={maxValue}\n displayMarkerValue={displayTickValue || undefined}\n />\n <GaugeCenterLabel\n value={displayValue || animatedValue}\n width={width}\n height={height}\n unit={unit}\n />\n </Gauge>\n );\n\n // If tooltip is provided, wrap with CustomTooltip, otherwise return gauge directly\n return tooltip ? (\n <div style={{ display: 'inline-block', cursor: 'pointer' }}>\n <CustomTooltip title={tooltip} placement=\"top\">\n <div style={{ width: '100%', height: '100%' }}>{gaugeElement}</div>\n </CustomTooltip>\n </div>\n ) : (\n gaugeElement\n );\n}\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,123 @@
1
+ import { jsx, jsxs, Fragment as Fragment$1 } from 'react/jsx-runtime';
2
+ import { Fragment } from 'react';
3
+ import Box from '@mui/material/Box';
4
+ import { useTheme, styled } from '@mui/material/styles';
5
+ import { ApiiroLogo } from '../../assets/apiiroLogo/apiiroLogo.esm.js';
6
+ import '@mui/material/SvgIcon';
7
+ import { getLogoContainerColors } from '../../theme/themeUtils.esm.js';
8
+ import { NotFound } from './NotFound.esm.js';
9
+ import { SomethingWentWrong } from './SomethingWentWrong.esm.js';
10
+ import { LogoSpinner } from './logoSpinner.esm.js';
11
+
12
+ const LogoContainer = styled(Box)(({ theme }) => {
13
+ const logoColors = getLogoContainerColors(theme);
14
+ return {
15
+ display: "flex",
16
+ flexDirection: "row",
17
+ justifyContent: "center",
18
+ alignItems: "center",
19
+ padding: "12px 15px",
20
+ gap: "10px",
21
+ width: "109px",
22
+ height: "40px",
23
+ background: logoColors.background,
24
+ borderRadius: "10px",
25
+ "& svg": {
26
+ width: "79px",
27
+ height: "22px",
28
+ "& path": {
29
+ fill: logoColors.logoFill
30
+ }
31
+ }
32
+ };
33
+ });
34
+ const StatusContainer = ({
35
+ isLoading,
36
+ error,
37
+ isEmpty,
38
+ notFoundMessage = "No results found.",
39
+ wrapper: Wrapper = Fragment,
40
+ children,
41
+ showLogo = true
42
+ }) => {
43
+ const theme = useTheme();
44
+ const containerSx = {
45
+ border: `1px solid ${theme.palette.divider}`,
46
+ borderRadius: "12px",
47
+ backgroundColor: theme.palette.background.paper,
48
+ boxShadow: theme.shadows[1]
49
+ };
50
+ if (isLoading) {
51
+ return /* @__PURE__ */ jsx(Wrapper, { children: /* @__PURE__ */ jsxs(
52
+ Box,
53
+ {
54
+ display: "flex",
55
+ justifyContent: showLogo ? "flex-start" : "center",
56
+ alignItems: "center",
57
+ flexDirection: "column",
58
+ minHeight: "300px",
59
+ sx: containerSx,
60
+ children: [
61
+ showLogo && /* @__PURE__ */ jsx(LogoContainer, { sx: { alignSelf: "flex-end" }, children: /* @__PURE__ */ jsx(ApiiroLogo, {}) }),
62
+ /* @__PURE__ */ jsx(
63
+ Box,
64
+ {
65
+ display: "flex",
66
+ justifyContent: "center",
67
+ alignItems: "center",
68
+ minHeight: showLogo ? "250px" : "300px",
69
+ children: /* @__PURE__ */ jsx(LogoSpinner, {})
70
+ }
71
+ )
72
+ ]
73
+ }
74
+ ) });
75
+ }
76
+ if (error) {
77
+ return /* @__PURE__ */ jsx(Wrapper, { children: /* @__PURE__ */ jsxs(
78
+ Box,
79
+ {
80
+ display: "flex",
81
+ justifyContent: "center",
82
+ alignItems: "center",
83
+ flexDirection: "column",
84
+ minHeight: "300px",
85
+ sx: containerSx,
86
+ children: [
87
+ showLogo && /* @__PURE__ */ jsx(LogoContainer, { sx: { alignSelf: "flex-end" }, children: /* @__PURE__ */ jsx(ApiiroLogo, {}) }),
88
+ /* @__PURE__ */ jsx(SomethingWentWrong, { statusCode: error?.details?.status })
89
+ ]
90
+ }
91
+ ) });
92
+ }
93
+ if (isEmpty) {
94
+ return /* @__PURE__ */ jsx(Wrapper, { children: /* @__PURE__ */ jsxs(
95
+ Box,
96
+ {
97
+ display: "flex",
98
+ justifyContent: showLogo ? "flex-start" : "center",
99
+ alignItems: "center",
100
+ flexDirection: "column",
101
+ minHeight: "300px",
102
+ sx: containerSx,
103
+ children: [
104
+ showLogo && /* @__PURE__ */ jsx(LogoContainer, { sx: { alignSelf: "flex-end" }, children: /* @__PURE__ */ jsx(ApiiroLogo, {}) }),
105
+ /* @__PURE__ */ jsx(
106
+ Box,
107
+ {
108
+ display: "flex",
109
+ justifyContent: "center",
110
+ alignItems: "center",
111
+ minHeight: showLogo ? "250px" : "300px",
112
+ children: /* @__PURE__ */ jsx(NotFound, { message: notFoundMessage })
113
+ }
114
+ )
115
+ ]
116
+ }
117
+ ) });
118
+ }
119
+ return /* @__PURE__ */ jsx(Fragment$1, { children });
120
+ };
121
+
122
+ export { StatusContainer };
123
+ //# sourceMappingURL=StatusContainer.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"StatusContainer.esm.js","sources":["../../../src/components/common/StatusContainer.tsx"],"sourcesContent":["/*\n * Copyright 2025 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { ComponentType, Fragment, ReactNode } from 'react';\nimport Box from '@mui/material/Box';\nimport { styled, useTheme } from '@mui/material/styles';\nimport { ApiiroLogo } from '../../assets/apiiroLogo';\nimport { getLogoContainerColors } from '../../theme/themeUtils';\nimport { NotFound } from './NotFound';\nimport { SomethingWentWrong } from './SomethingWentWrong';\nimport { LogoSpinner } from './logoSpinner';\n\nconst LogoContainer = styled(Box)(({ theme }) => {\n const logoColors = getLogoContainerColors(theme);\n return {\n display: 'flex',\n flexDirection: 'row',\n justifyContent: 'center',\n alignItems: 'center',\n padding: '12px 15px',\n gap: '10px',\n width: '109px',\n height: '40px',\n background: logoColors.background,\n borderRadius: '10px',\n '& svg': {\n width: '79px',\n height: '22px',\n '& path': {\n fill: logoColors.logoFill,\n },\n },\n };\n});\n\ntype StatusContainerProps = {\n isLoading: boolean;\n error?: { details?: { status?: number } } | null;\n isEmpty: boolean;\n notFoundMessage?: string;\n wrapper?: ComponentType<{ children: ReactNode }>;\n children: ReactNode;\n showLogo?: boolean;\n};\n\nexport const StatusContainer = ({\n isLoading,\n error,\n isEmpty,\n notFoundMessage = 'No results found.',\n wrapper: Wrapper = Fragment,\n children,\n showLogo = true,\n}: StatusContainerProps) => {\n const theme = useTheme();\n\n const containerSx = {\n border: `1px solid ${theme.palette.divider}`,\n borderRadius: '12px',\n backgroundColor: theme.palette.background.paper,\n boxShadow: theme.shadows[1],\n };\n\n if (isLoading) {\n return (\n <Wrapper>\n <Box\n display=\"flex\"\n justifyContent={showLogo ? 'flex-start' : 'center'}\n alignItems=\"center\"\n flexDirection=\"column\"\n minHeight=\"300px\"\n sx={containerSx}\n >\n {showLogo && (\n <LogoContainer sx={{ alignSelf: 'flex-end' }}>\n <ApiiroLogo />\n </LogoContainer>\n )}\n <Box\n display=\"flex\"\n justifyContent=\"center\"\n alignItems=\"center\"\n minHeight={showLogo ? '250px' : '300px'}\n >\n <LogoSpinner />\n </Box>\n </Box>\n </Wrapper>\n );\n }\n\n if (error) {\n return (\n <Wrapper>\n <Box\n display=\"flex\"\n justifyContent=\"center\"\n alignItems=\"center\"\n flexDirection=\"column\"\n minHeight=\"300px\"\n sx={containerSx}\n >\n {showLogo && (\n <LogoContainer sx={{ alignSelf: 'flex-end' }}>\n <ApiiroLogo />\n </LogoContainer>\n )}\n <SomethingWentWrong statusCode={error?.details?.status} />\n </Box>\n </Wrapper>\n );\n }\n\n if (isEmpty) {\n return (\n <Wrapper>\n <Box\n display=\"flex\"\n justifyContent={showLogo ? 'flex-start' : 'center'}\n alignItems=\"center\"\n flexDirection=\"column\"\n minHeight=\"300px\"\n sx={containerSx}\n >\n {showLogo && (\n <LogoContainer sx={{ alignSelf: 'flex-end' }}>\n <ApiiroLogo />\n </LogoContainer>\n )}\n <Box\n display=\"flex\"\n justifyContent=\"center\"\n alignItems=\"center\"\n minHeight={showLogo ? '250px' : '300px'}\n >\n <NotFound message={notFoundMessage} />\n </Box>\n </Box>\n </Wrapper>\n );\n }\n\n return <>{children}</>;\n};\n"],"names":["Fragment"],"mappings":"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"}
@@ -89,7 +89,7 @@ const DiscoveredOnFilter = ({
89
89
  if (yearsDiff > MAX_RANGE_YEARS) {
90
90
  const spanText = formatDateRangeSpan(startDate, endDate);
91
91
  setValidationError(
92
- `Date range cannot exceed ${MAX_RANGE_YEARS} years. Current selection spans ${spanText}.`
92
+ `Date range can not exceed ${MAX_RANGE_YEARS} years. Current selection spans ${spanText}.`
93
93
  );
94
94
  return;
95
95
  }
@@ -1 +1 @@
1
- {"version":3,"file":"DiscoveredOnFilter.esm.js","sources":["../../../src/components/filters/DiscoveredOnFilter.tsx"],"sourcesContent":["/*\n * Copyright 2025 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { MouseEvent, useMemo, useRef, useState } from 'react';\nimport Box from '@mui/material/Box';\nimport ButtonBase from '@mui/material/ButtonBase';\nimport Typography from '@mui/material/Typography';\nimport Popper from '@mui/material/Popper';\nimport ClickAwayListener from '@mui/material/ClickAwayListener';\nimport Tooltip from '@mui/material/Tooltip';\nimport IconButton from '@mui/material/IconButton';\nimport Skeleton from '@mui/material/Skeleton';\nimport KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown';\nimport CloseIcon from '@mui/icons-material/Close';\nimport { alpha } from '@mui/material/styles';\n\nimport {\n CalendarDatePicker,\n CalendarDateValue,\n CalendarQuickRange,\n} from '../CalendarDatePicker';\n\ntype DiscoveredOnFilterProps = {\n label?: string;\n value: CalendarDateValue;\n quickRanges: CalendarQuickRange[];\n selectedQuickRange: string;\n onChange: (value: CalendarDateValue) => void;\n onQuickRangeSelect: (range: CalendarQuickRange) => void;\n loading?: boolean;\n};\n\nconst hasCompleteRange = (value: CalendarDateValue): value is [Date, Date] => {\n if (!Array.isArray(value)) {\n return false;\n }\n const [start, end] = value;\n return start instanceof Date && end instanceof Date;\n};\n\n// Helper function to calculate the difference between two dates in years\nconst getYearsDifference = (startDate: Date, endDate: Date): number => {\n const start = new Date(startDate);\n const end = new Date(endDate);\n\n // Calculate the difference in milliseconds\n const diffTime = Math.abs(end.getTime() - start.getTime());\n\n // Convert to years (365.25 days per year to account for leap years)\n const diffYears = diffTime / (1000 * 60 * 60 * 24 * 365.25);\n\n return diffYears;\n};\n\n// Helper function to format the date range span with years and days\nconst formatDateRangeSpan = (startDate: Date, endDate: Date): string => {\n const start = new Date(startDate);\n const end = new Date(endDate);\n\n // Calculate the difference in milliseconds\n const diffTime = Math.abs(end.getTime() - start.getTime());\n\n // Convert to total days\n const totalDays = diffTime / (1000 * 60 * 60 * 24);\n\n // Calculate years and remaining days\n const years = Math.floor(totalDays / 365.25);\n const remainingDays = Math.floor(totalDays % 365.25);\n\n if (years > 0) {\n return `${years} year${years !== 1 ? 's' : ''} and ${remainingDays} day${\n remainingDays !== 1 ? 's' : ''\n }`;\n }\n\n return `${Math.floor(totalDays)} day${\n Math.floor(totalDays) !== 1 ? 's' : ''\n }`;\n};\n\n// Maximum allowed range in years\nconst MAX_RANGE_YEARS = 10;\n\nexport const DiscoveredOnFilter = ({\n label = 'Discovered on',\n value,\n quickRanges,\n selectedQuickRange,\n onChange,\n onQuickRangeSelect,\n loading = false,\n}: DiscoveredOnFilterProps) => {\n const [anchorEl, setAnchorEl] = useState<HTMLElement | null>(null);\n const [validationError, setValidationError] = useState<string | null>(null);\n const skipCloseRef = useRef(false);\n\n const isOpen = Boolean(anchorEl);\n\n const displayLabel = useMemo(() => {\n if (selectedQuickRange !== 'custom') {\n const preset = quickRanges.find(\n range => range.value === selectedQuickRange,\n );\n if (preset) {\n return `${label}: ${preset.label}`;\n }\n }\n\n if (hasCompleteRange(value)) {\n const [from, to] = value;\n const formatDate = (date: Date) =>\n date.toLocaleDateString(undefined, {\n year: 'numeric',\n month: 'short',\n day: 'numeric',\n });\n return `${label}: ${formatDate(from)} – ${formatDate(to)}`;\n }\n\n return label;\n }, [label, quickRanges, selectedQuickRange, value]);\n\n const hasSelection = displayLabel !== label;\n\n const handleToggle = (event: MouseEvent<HTMLElement>) => {\n setAnchorEl(prev => (prev ? null : event.currentTarget));\n };\n\n const handleClose = () => {\n setAnchorEl(null);\n setValidationError(null); // Clear validation error when dropdown closes\n };\n\n const handleCalendarChange = (nextValue: CalendarDateValue) => {\n // Clear any previous validation errors\n setValidationError(null);\n\n // Validate the date range if it's complete\n if (hasCompleteRange(nextValue)) {\n const [startDate, endDate] = nextValue;\n const yearsDiff = getYearsDifference(startDate, endDate);\n\n if (yearsDiff > MAX_RANGE_YEARS) {\n const spanText = formatDateRangeSpan(startDate, endDate);\n setValidationError(\n `Date range cannot exceed ${MAX_RANGE_YEARS} years. Current selection spans ${spanText}.`,\n );\n return; // Don't proceed with the change\n }\n }\n\n onChange(nextValue);\n\n const shouldSkipClose = skipCloseRef.current;\n skipCloseRef.current = false;\n\n if (shouldSkipClose) {\n return;\n }\n\n if (hasCompleteRange(nextValue)) {\n handleClose();\n }\n };\n\n const handleQuickRangeClick = (range: CalendarQuickRange) => {\n // Clear any previous validation errors\n setValidationError(null);\n\n // Validate the quick range if it's not custom\n if (range.value !== 'custom') {\n const rangeValue = range.getRange();\n if (rangeValue && hasCompleteRange(rangeValue)) {\n const [startDate, endDate] = rangeValue;\n const yearsDiff = getYearsDifference(startDate, endDate);\n\n if (yearsDiff > MAX_RANGE_YEARS) {\n const spanText = formatDateRangeSpan(startDate, endDate);\n setValidationError(\n `\"${range.label}\" preset spans ${spanText}, which exceeds ${MAX_RANGE_YEARS} years limit. Please use a custom range.`,\n );\n return; // Don't proceed with the selection\n }\n }\n }\n\n skipCloseRef.current = range.value !== 'custom';\n onQuickRangeSelect(range);\n };\n\n const handleClear = (event: MouseEvent<HTMLElement>) => {\n event.stopPropagation();\n skipCloseRef.current = false;\n setValidationError(null); // Clear any validation errors\n onChange([]);\n handleClose();\n };\n\n if (loading) {\n return (\n <Skeleton\n variant=\"rounded\"\n width={140}\n height={36}\n sx={{\n borderRadius: 999,\n }}\n />\n );\n }\n\n return (\n <Box data-testid=\"discovered-on-filter\" sx={{ position: 'relative' }}>\n <ButtonBase\n className=\"discovered-on-button\"\n onClick={handleToggle}\n sx={theme => {\n const primaryMain = theme.palette.primary.main;\n const closedBorder = theme.palette.divider;\n const isDark = theme.palette.mode === 'dark';\n const hoverBackground = alpha(primaryMain, isDark ? 0.28 : 0.12);\n const selectedBorder = isDark\n ? theme.palette.primary.light\n : theme.palette.primary.dark;\n const selectedBackground = alpha(primaryMain, isDark ? 0.2 : 0.06);\n\n let backgroundColor = theme.palette.background.paper;\n if (isOpen) {\n backgroundColor = hoverBackground;\n } else if (hasSelection) {\n backgroundColor = selectedBackground;\n }\n\n let borderColor = closedBorder;\n if (isOpen) {\n borderColor = primaryMain;\n } else if (hasSelection) {\n borderColor = selectedBorder;\n }\n\n let boxShadow = 'none';\n if (isOpen) {\n boxShadow = '0 4px 12px rgba(38, 54, 140, 0.18)';\n } else if (hasSelection) {\n boxShadow = '0 2px 8px rgba(38, 54, 140, 0.12)';\n }\n\n return {\n borderRadius: 999,\n padding: theme.spacing(0.75, 1.75),\n border: `1px solid ${borderColor}`,\n display: 'flex',\n alignItems: 'center',\n gap: 0.5,\n backgroundColor,\n boxShadow,\n transition: 'all 0.2s ease',\n minHeight: 38,\n };\n }}\n >\n <Typography\n variant=\"body2\"\n sx={theme => ({\n whiteSpace: 'nowrap',\n color: theme.palette.text.primary,\n })}\n >\n {displayLabel}\n </Typography>\n {hasSelection ? (\n <Tooltip title=\"Clear and remove filter\">\n <IconButton\n size=\"small\"\n onClick={handleClear}\n sx={theme => ({\n color: theme.palette.text.secondary,\n '&:hover': {\n color: theme.palette.primary.main,\n },\n })}\n >\n <CloseIcon fontSize=\"inherit\" />\n </IconButton>\n </Tooltip>\n ) : null}\n <KeyboardArrowDownIcon\n fontSize=\"small\"\n sx={theme => ({\n transform: isOpen ? 'rotate(180deg)' : 'none',\n transition: 'transform 0.2s ease',\n color: hasSelection\n ? theme.palette.primary.main\n : theme.palette.text.secondary,\n })}\n />\n </ButtonBase>\n <Popper\n open={isOpen}\n anchorEl={anchorEl}\n placement=\"bottom-start\"\n modifiers={[\n {\n name: 'offset',\n options: { offset: [0, 8] },\n },\n ]}\n >\n <ClickAwayListener onClickAway={handleClose}>\n <Box sx={{ mt: 1 }}>\n <CalendarDatePicker\n value={value}\n onChange={handleCalendarChange}\n selectedQuickRange={selectedQuickRange}\n onQuickRangeSelect={handleQuickRangeClick}\n quickRanges={quickRanges}\n />\n {validationError && (\n <Box\n sx={{\n mt: 1,\n p: 1,\n backgroundColor: 'error.light',\n borderRadius: 1,\n border: '1px solid',\n borderColor: 'error.main',\n }}\n >\n <Typography\n variant=\"caption\"\n sx={{\n color: 'error.contrastText',\n fontWeight: 500,\n }}\n >\n {validationError}\n </Typography>\n </Box>\n )}\n </Box>\n </ClickAwayListener>\n </Popper>\n </Box>\n );\n};\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"DiscoveredOnFilter.esm.js","sources":["../../../src/components/filters/DiscoveredOnFilter.tsx"],"sourcesContent":["/*\n * Copyright 2025 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { MouseEvent, useMemo, useRef, useState } from 'react';\nimport Box from '@mui/material/Box';\nimport ButtonBase from '@mui/material/ButtonBase';\nimport Typography from '@mui/material/Typography';\nimport Popper from '@mui/material/Popper';\nimport ClickAwayListener from '@mui/material/ClickAwayListener';\nimport Tooltip from '@mui/material/Tooltip';\nimport IconButton from '@mui/material/IconButton';\nimport Skeleton from '@mui/material/Skeleton';\nimport KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown';\nimport CloseIcon from '@mui/icons-material/Close';\nimport { alpha } from '@mui/material/styles';\n\nimport {\n CalendarDatePicker,\n CalendarDateValue,\n CalendarQuickRange,\n} from '../CalendarDatePicker';\n\ntype DiscoveredOnFilterProps = {\n label?: string;\n value: CalendarDateValue;\n quickRanges: CalendarQuickRange[];\n selectedQuickRange: string;\n onChange: (value: CalendarDateValue) => void;\n onQuickRangeSelect: (range: CalendarQuickRange) => void;\n loading?: boolean;\n};\n\nconst hasCompleteRange = (value: CalendarDateValue): value is [Date, Date] => {\n if (!Array.isArray(value)) {\n return false;\n }\n const [start, end] = value;\n return start instanceof Date && end instanceof Date;\n};\n\n// Helper function to calculate the difference between two dates in years\nconst getYearsDifference = (startDate: Date, endDate: Date): number => {\n const start = new Date(startDate);\n const end = new Date(endDate);\n\n // Calculate the difference in milliseconds\n const diffTime = Math.abs(end.getTime() - start.getTime());\n\n // Convert to years (365.25 days per year to account for leap years)\n const diffYears = diffTime / (1000 * 60 * 60 * 24 * 365.25);\n\n return diffYears;\n};\n\n// Helper function to format the date range span with years and days\nconst formatDateRangeSpan = (startDate: Date, endDate: Date): string => {\n const start = new Date(startDate);\n const end = new Date(endDate);\n\n // Calculate the difference in milliseconds\n const diffTime = Math.abs(end.getTime() - start.getTime());\n\n // Convert to total days\n const totalDays = diffTime / (1000 * 60 * 60 * 24);\n\n // Calculate years and remaining days\n const years = Math.floor(totalDays / 365.25);\n const remainingDays = Math.floor(totalDays % 365.25);\n\n if (years > 0) {\n return `${years} year${years !== 1 ? 's' : ''} and ${remainingDays} day${\n remainingDays !== 1 ? 's' : ''\n }`;\n }\n\n return `${Math.floor(totalDays)} day${\n Math.floor(totalDays) !== 1 ? 's' : ''\n }`;\n};\n\n// Maximum allowed range in years\nconst MAX_RANGE_YEARS = 10;\n\nexport const DiscoveredOnFilter = ({\n label = 'Discovered on',\n value,\n quickRanges,\n selectedQuickRange,\n onChange,\n onQuickRangeSelect,\n loading = false,\n}: DiscoveredOnFilterProps) => {\n const [anchorEl, setAnchorEl] = useState<HTMLElement | null>(null);\n const [validationError, setValidationError] = useState<string | null>(null);\n const skipCloseRef = useRef(false);\n\n const isOpen = Boolean(anchorEl);\n\n const displayLabel = useMemo(() => {\n if (selectedQuickRange !== 'custom') {\n const preset = quickRanges.find(\n range => range.value === selectedQuickRange,\n );\n if (preset) {\n return `${label}: ${preset.label}`;\n }\n }\n\n if (hasCompleteRange(value)) {\n const [from, to] = value;\n const formatDate = (date: Date) =>\n date.toLocaleDateString(undefined, {\n year: 'numeric',\n month: 'short',\n day: 'numeric',\n });\n return `${label}: ${formatDate(from)} – ${formatDate(to)}`;\n }\n\n return label;\n }, [label, quickRanges, selectedQuickRange, value]);\n\n const hasSelection = displayLabel !== label;\n\n const handleToggle = (event: MouseEvent<HTMLElement>) => {\n setAnchorEl(prev => (prev ? null : event.currentTarget));\n };\n\n const handleClose = () => {\n setAnchorEl(null);\n setValidationError(null); // Clear validation error when dropdown closes\n };\n\n const handleCalendarChange = (nextValue: CalendarDateValue) => {\n // Clear any previous validation errors\n setValidationError(null);\n\n // Validate the date range if it's complete\n if (hasCompleteRange(nextValue)) {\n const [startDate, endDate] = nextValue;\n const yearsDiff = getYearsDifference(startDate, endDate);\n\n if (yearsDiff > MAX_RANGE_YEARS) {\n const spanText = formatDateRangeSpan(startDate, endDate);\n setValidationError(\n `Date range can not exceed ${MAX_RANGE_YEARS} years. Current selection spans ${spanText}.`,\n );\n return; // Don't proceed with the change\n }\n }\n\n onChange(nextValue);\n\n const shouldSkipClose = skipCloseRef.current;\n skipCloseRef.current = false;\n\n if (shouldSkipClose) {\n return;\n }\n\n if (hasCompleteRange(nextValue)) {\n handleClose();\n }\n };\n\n const handleQuickRangeClick = (range: CalendarQuickRange) => {\n // Clear any previous validation errors\n setValidationError(null);\n\n // Validate the quick range if it's not custom\n if (range.value !== 'custom') {\n const rangeValue = range.getRange();\n if (rangeValue && hasCompleteRange(rangeValue)) {\n const [startDate, endDate] = rangeValue;\n const yearsDiff = getYearsDifference(startDate, endDate);\n\n if (yearsDiff > MAX_RANGE_YEARS) {\n const spanText = formatDateRangeSpan(startDate, endDate);\n setValidationError(\n `\"${range.label}\" preset spans ${spanText}, which exceeds ${MAX_RANGE_YEARS} years limit. Please use a custom range.`,\n );\n return; // Don't proceed with the selection\n }\n }\n }\n\n skipCloseRef.current = range.value !== 'custom';\n onQuickRangeSelect(range);\n };\n\n const handleClear = (event: MouseEvent<HTMLElement>) => {\n event.stopPropagation();\n skipCloseRef.current = false;\n setValidationError(null); // Clear any validation errors\n onChange([]);\n handleClose();\n };\n\n if (loading) {\n return (\n <Skeleton\n variant=\"rounded\"\n width={140}\n height={36}\n sx={{\n borderRadius: 999,\n }}\n />\n );\n }\n\n return (\n <Box data-testid=\"discovered-on-filter\" sx={{ position: 'relative' }}>\n <ButtonBase\n className=\"discovered-on-button\"\n onClick={handleToggle}\n sx={theme => {\n const primaryMain = theme.palette.primary.main;\n const closedBorder = theme.palette.divider;\n const isDark = theme.palette.mode === 'dark';\n const hoverBackground = alpha(primaryMain, isDark ? 0.28 : 0.12);\n const selectedBorder = isDark\n ? theme.palette.primary.light\n : theme.palette.primary.dark;\n const selectedBackground = alpha(primaryMain, isDark ? 0.2 : 0.06);\n\n let backgroundColor = theme.palette.background.paper;\n if (isOpen) {\n backgroundColor = hoverBackground;\n } else if (hasSelection) {\n backgroundColor = selectedBackground;\n }\n\n let borderColor = closedBorder;\n if (isOpen) {\n borderColor = primaryMain;\n } else if (hasSelection) {\n borderColor = selectedBorder;\n }\n\n let boxShadow = 'none';\n if (isOpen) {\n boxShadow = '0 4px 12px rgba(38, 54, 140, 0.18)';\n } else if (hasSelection) {\n boxShadow = '0 2px 8px rgba(38, 54, 140, 0.12)';\n }\n\n return {\n borderRadius: 999,\n padding: theme.spacing(0.75, 1.75),\n border: `1px solid ${borderColor}`,\n display: 'flex',\n alignItems: 'center',\n gap: 0.5,\n backgroundColor,\n boxShadow,\n transition: 'all 0.2s ease',\n minHeight: 38,\n };\n }}\n >\n <Typography\n variant=\"body2\"\n sx={theme => ({\n whiteSpace: 'nowrap',\n color: theme.palette.text.primary,\n })}\n >\n {displayLabel}\n </Typography>\n {hasSelection ? (\n <Tooltip title=\"Clear and remove filter\">\n <IconButton\n size=\"small\"\n onClick={handleClear}\n sx={theme => ({\n color: theme.palette.text.secondary,\n '&:hover': {\n color: theme.palette.primary.main,\n },\n })}\n >\n <CloseIcon fontSize=\"inherit\" />\n </IconButton>\n </Tooltip>\n ) : null}\n <KeyboardArrowDownIcon\n fontSize=\"small\"\n sx={theme => ({\n transform: isOpen ? 'rotate(180deg)' : 'none',\n transition: 'transform 0.2s ease',\n color: hasSelection\n ? theme.palette.primary.main\n : theme.palette.text.secondary,\n })}\n />\n </ButtonBase>\n <Popper\n open={isOpen}\n anchorEl={anchorEl}\n placement=\"bottom-start\"\n modifiers={[\n {\n name: 'offset',\n options: { offset: [0, 8] },\n },\n ]}\n >\n <ClickAwayListener onClickAway={handleClose}>\n <Box sx={{ mt: 1 }}>\n <CalendarDatePicker\n value={value}\n onChange={handleCalendarChange}\n selectedQuickRange={selectedQuickRange}\n onQuickRangeSelect={handleQuickRangeClick}\n quickRanges={quickRanges}\n />\n {validationError && (\n <Box\n sx={{\n mt: 1,\n p: 1,\n backgroundColor: 'error.light',\n borderRadius: 1,\n border: '1px solid',\n borderColor: 'error.main',\n }}\n >\n <Typography\n variant=\"caption\"\n sx={{\n color: 'error.contrastText',\n fontWeight: 500,\n }}\n >\n {validationError}\n </Typography>\n </Box>\n )}\n </Box>\n </ClickAwayListener>\n </Popper>\n </Box>\n );\n};\n"],"names":[],"mappings":";;;;;;;;;;;;;;;AA4CA,MAAM,gBAAA,GAAmB,CAAC,KAAA,KAAoD;AAC5E,EAAA,IAAI,CAAC,KAAA,CAAM,OAAA,CAAQ,KAAK,CAAA,EAAG;AACzB,IAAA,OAAO,KAAA;AAAA,EACT;AACA,EAAA,MAAM,CAAC,KAAA,EAAO,GAAG,CAAA,GAAI,KAAA;AACrB,EAAA,OAAO,KAAA,YAAiB,QAAQ,GAAA,YAAe,IAAA;AACjD,CAAA;AAGA,MAAM,kBAAA,GAAqB,CAAC,SAAA,EAAiB,OAAA,KAA0B;AACrE,EAAA,MAAM,KAAA,GAAQ,IAAI,IAAA,CAAK,SAAS,CAAA;AAChC,EAAA,MAAM,GAAA,GAAM,IAAI,IAAA,CAAK,OAAO,CAAA;AAG5B,EAAA,MAAM,QAAA,GAAW,KAAK,GAAA,CAAI,GAAA,CAAI,SAAQ,GAAI,KAAA,CAAM,SAAS,CAAA;AAGzD,EAAA,MAAM,SAAA,GAAY,QAAA,IAAY,GAAA,GAAO,EAAA,GAAK,KAAK,EAAA,GAAK,MAAA,CAAA;AAEpD,EAAA,OAAO,SAAA;AACT,CAAA;AAGA,MAAM,mBAAA,GAAsB,CAAC,SAAA,EAAiB,OAAA,KAA0B;AACtE,EAAA,MAAM,KAAA,GAAQ,IAAI,IAAA,CAAK,SAAS,CAAA;AAChC,EAAA,MAAM,GAAA,GAAM,IAAI,IAAA,CAAK,OAAO,CAAA;AAG5B,EAAA,MAAM,QAAA,GAAW,KAAK,GAAA,CAAI,GAAA,CAAI,SAAQ,GAAI,KAAA,CAAM,SAAS,CAAA;AAGzD,EAAA,MAAM,SAAA,GAAY,QAAA,IAAY,GAAA,GAAO,EAAA,GAAK,EAAA,GAAK,EAAA,CAAA;AAG/C,EAAA,MAAM,KAAA,GAAQ,IAAA,CAAK,KAAA,CAAM,SAAA,GAAY,MAAM,CAAA;AAC3C,EAAA,MAAM,aAAA,GAAgB,IAAA,CAAK,KAAA,CAAM,SAAA,GAAY,MAAM,CAAA;AAEnD,EAAA,IAAI,QAAQ,CAAA,EAAG;AACb,IAAA,OAAO,CAAA,EAAG,KAAK,CAAA,KAAA,EAAQ,KAAA,KAAU,CAAA,GAAI,GAAA,GAAM,EAAE,CAAA,KAAA,EAAQ,aAAa,CAAA,IAAA,EAChE,aAAA,KAAkB,CAAA,GAAI,MAAM,EAC9B,CAAA,CAAA;AAAA,EACF;AAEA,EAAA,OAAO,CAAA,EAAG,IAAA,CAAK,KAAA,CAAM,SAAS,CAAC,CAAA,IAAA,EAC7B,IAAA,CAAK,KAAA,CAAM,SAAS,CAAA,KAAM,CAAA,GAAI,GAAA,GAAM,EACtC,CAAA,CAAA;AACF,CAAA;AAGA,MAAM,eAAA,GAAkB,EAAA;AAEjB,MAAM,qBAAqB,CAAC;AAAA,EACjC,KAAA,GAAQ,eAAA;AAAA,EACR,KAAA;AAAA,EACA,WAAA;AAAA,EACA,kBAAA;AAAA,EACA,QAAA;AAAA,EACA,kBAAA;AAAA,EACA,OAAA,GAAU;AACZ,CAAA,KAA+B;AAC7B,EAAA,MAAM,CAAC,QAAA,EAAU,WAAW,CAAA,GAAI,SAA6B,IAAI,CAAA;AACjE,EAAA,MAAM,CAAC,eAAA,EAAiB,kBAAkB,CAAA,GAAI,SAAwB,IAAI,CAAA;AAC1E,EAAA,MAAM,YAAA,GAAe,OAAO,KAAK,CAAA;AAEjC,EAAA,MAAM,MAAA,GAAS,QAAQ,QAAQ,CAAA;AAE/B,EAAA,MAAM,YAAA,GAAe,QAAQ,MAAM;AACjC,IAAA,IAAI,uBAAuB,QAAA,EAAU;AACnC,MAAA,MAAM,SAAS,WAAA,CAAY,IAAA;AAAA,QACzB,CAAA,KAAA,KAAS,MAAM,KAAA,KAAU;AAAA,OAC3B;AACA,MAAA,IAAI,MAAA,EAAQ;AACV,QAAA,OAAO,CAAA,EAAG,KAAK,CAAA,EAAA,EAAK,MAAA,CAAO,KAAK,CAAA,CAAA;AAAA,MAClC;AAAA,IACF;AAEA,IAAA,IAAI,gBAAA,CAAiB,KAAK,CAAA,EAAG;AAC3B,MAAA,MAAM,CAAC,IAAA,EAAM,EAAE,CAAA,GAAI,KAAA;AACnB,MAAA,MAAM,UAAA,GAAa,CAAC,IAAA,KAClB,IAAA,CAAK,mBAAmB,MAAA,EAAW;AAAA,QACjC,IAAA,EAAM,SAAA;AAAA,QACN,KAAA,EAAO,OAAA;AAAA,QACP,GAAA,EAAK;AAAA,OACN,CAAA;AACH,MAAA,OAAO,CAAA,EAAG,KAAK,CAAA,EAAA,EAAK,UAAA,CAAW,IAAI,CAAC,CAAA,QAAA,EAAM,UAAA,CAAW,EAAE,CAAC,CAAA,CAAA;AAAA,IAC1D;AAEA,IAAA,OAAO,KAAA;AAAA,EACT,GAAG,CAAC,KAAA,EAAO,WAAA,EAAa,kBAAA,EAAoB,KAAK,CAAC,CAAA;AAElD,EAAA,MAAM,eAAe,YAAA,KAAiB,KAAA;AAEtC,EAAA,MAAM,YAAA,GAAe,CAAC,KAAA,KAAmC;AACvD,IAAA,WAAA,CAAY,CAAA,IAAA,KAAS,IAAA,GAAO,IAAA,GAAO,KAAA,CAAM,aAAc,CAAA;AAAA,EACzD,CAAA;AAEA,EAAA,MAAM,cAAc,MAAM;AACxB,IAAA,WAAA,CAAY,IAAI,CAAA;AAChB,IAAA,kBAAA,CAAmB,IAAI,CAAA;AAAA,EACzB,CAAA;AAEA,EAAA,MAAM,oBAAA,GAAuB,CAAC,SAAA,KAAiC;AAE7D,IAAA,kBAAA,CAAmB,IAAI,CAAA;AAGvB,IAAA,IAAI,gBAAA,CAAiB,SAAS,CAAA,EAAG;AAC/B,MAAA,MAAM,CAAC,SAAA,EAAW,OAAO,CAAA,GAAI,SAAA;AAC7B,MAAA,MAAM,SAAA,GAAY,kBAAA,CAAmB,SAAA,EAAW,OAAO,CAAA;AAEvD,MAAA,IAAI,YAAY,eAAA,EAAiB;AAC/B,QAAA,MAAM,QAAA,GAAW,mBAAA,CAAoB,SAAA,EAAW,OAAO,CAAA;AACvD,QAAA,kBAAA;AAAA,UACE,CAAA,0BAAA,EAA6B,eAAe,CAAA,gCAAA,EAAmC,QAAQ,CAAA,CAAA;AAAA,SACzF;AACA,QAAA;AAAA,MACF;AAAA,IACF;AAEA,IAAA,QAAA,CAAS,SAAS,CAAA;AAElB,IAAA,MAAM,kBAAkB,YAAA,CAAa,OAAA;AACrC,IAAA,YAAA,CAAa,OAAA,GAAU,KAAA;AAEvB,IAAA,IAAI,eAAA,EAAiB;AACnB,MAAA;AAAA,IACF;AAEA,IAAA,IAAI,gBAAA,CAAiB,SAAS,CAAA,EAAG;AAC/B,MAAA,WAAA,EAAY;AAAA,IACd;AAAA,EACF,CAAA;AAEA,EAAA,MAAM,qBAAA,GAAwB,CAAC,KAAA,KAA8B;AAE3D,IAAA,kBAAA,CAAmB,IAAI,CAAA;AAGvB,IAAA,IAAI,KAAA,CAAM,UAAU,QAAA,EAAU;AAC5B,MAAA,MAAM,UAAA,GAAa,MAAM,QAAA,EAAS;AAClC,MAAA,IAAI,UAAA,IAAc,gBAAA,CAAiB,UAAU,CAAA,EAAG;AAC9C,QAAA,MAAM,CAAC,SAAA,EAAW,OAAO,CAAA,GAAI,UAAA;AAC7B,QAAA,MAAM,SAAA,GAAY,kBAAA,CAAmB,SAAA,EAAW,OAAO,CAAA;AAEvD,QAAA,IAAI,YAAY,eAAA,EAAiB;AAC/B,UAAA,MAAM,QAAA,GAAW,mBAAA,CAAoB,SAAA,EAAW,OAAO,CAAA;AACvD,UAAA,kBAAA;AAAA,YACE,IAAI,KAAA,CAAM,KAAK,CAAA,eAAA,EAAkB,QAAQ,mBAAmB,eAAe,CAAA,wCAAA;AAAA,WAC7E;AACA,UAAA;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAEA,IAAA,YAAA,CAAa,OAAA,GAAU,MAAM,KAAA,KAAU,QAAA;AACvC,IAAA,kBAAA,CAAmB,KAAK,CAAA;AAAA,EAC1B,CAAA;AAEA,EAAA,MAAM,WAAA,GAAc,CAAC,KAAA,KAAmC;AACtD,IAAA,KAAA,CAAM,eAAA,EAAgB;AACtB,IAAA,YAAA,CAAa,OAAA,GAAU,KAAA;AACvB,IAAA,kBAAA,CAAmB,IAAI,CAAA;AACvB,IAAA,QAAA,CAAS,EAAE,CAAA;AACX,IAAA,WAAA,EAAY;AAAA,EACd,CAAA;AAEA,EAAA,IAAI,OAAA,EAAS;AACX,IAAA,uBACE,GAAA;AAAA,MAAC,QAAA;AAAA,MAAA;AAAA,QACC,OAAA,EAAQ,SAAA;AAAA,QACR,KAAA,EAAO,GAAA;AAAA,QACP,MAAA,EAAQ,EAAA;AAAA,QACR,EAAA,EAAI;AAAA,UACF,YAAA,EAAc;AAAA;AAChB;AAAA,KACF;AAAA,EAEJ;AAEA,EAAA,uBACE,IAAA,CAAC,OAAI,aAAA,EAAY,sBAAA,EAAuB,IAAI,EAAE,QAAA,EAAU,YAAW,EACjE,QAAA,EAAA;AAAA,oBAAA,IAAA;AAAA,MAAC,UAAA;AAAA,MAAA;AAAA,QACC,SAAA,EAAU,sBAAA;AAAA,QACV,OAAA,EAAS,YAAA;AAAA,QACT,IAAI,CAAA,KAAA,KAAS;AACX,UAAA,MAAM,WAAA,GAAc,KAAA,CAAM,OAAA,CAAQ,OAAA,CAAQ,IAAA;AAC1C,UAAA,MAAM,YAAA,GAAe,MAAM,OAAA,CAAQ,OAAA;AACnC,UAAA,MAAM,MAAA,GAAS,KAAA,CAAM,OAAA,CAAQ,IAAA,KAAS,MAAA;AACtC,UAAA,MAAM,eAAA,GAAkB,KAAA,CAAM,WAAA,EAAa,MAAA,GAAS,OAAO,IAAI,CAAA;AAC/D,UAAA,MAAM,cAAA,GAAiB,SACnB,KAAA,CAAM,OAAA,CAAQ,QAAQ,KAAA,GACtB,KAAA,CAAM,QAAQ,OAAA,CAAQ,IAAA;AAC1B,UAAA,MAAM,kBAAA,GAAqB,KAAA,CAAM,WAAA,EAAa,MAAA,GAAS,MAAM,IAAI,CAAA;AAEjE,UAAA,IAAI,eAAA,GAAkB,KAAA,CAAM,OAAA,CAAQ,UAAA,CAAW,KAAA;AAC/C,UAAA,IAAI,MAAA,EAAQ;AACV,YAAA,eAAA,GAAkB,eAAA;AAAA,UACpB,WAAW,YAAA,EAAc;AACvB,YAAA,eAAA,GAAkB,kBAAA;AAAA,UACpB;AAEA,UAAA,IAAI,WAAA,GAAc,YAAA;AAClB,UAAA,IAAI,MAAA,EAAQ;AACV,YAAA,WAAA,GAAc,WAAA;AAAA,UAChB,WAAW,YAAA,EAAc;AACvB,YAAA,WAAA,GAAc,cAAA;AAAA,UAChB;AAEA,UAAA,IAAI,SAAA,GAAY,MAAA;AAChB,UAAA,IAAI,MAAA,EAAQ;AACV,YAAA,SAAA,GAAY,oCAAA;AAAA,UACd,WAAW,YAAA,EAAc;AACvB,YAAA,SAAA,GAAY,mCAAA;AAAA,UACd;AAEA,UAAA,OAAO;AAAA,YACL,YAAA,EAAc,GAAA;AAAA,YACd,OAAA,EAAS,KAAA,CAAM,OAAA,CAAQ,IAAA,EAAM,IAAI,CAAA;AAAA,YACjC,MAAA,EAAQ,aAAa,WAAW,CAAA,CAAA;AAAA,YAChC,OAAA,EAAS,MAAA;AAAA,YACT,UAAA,EAAY,QAAA;AAAA,YACZ,GAAA,EAAK,GAAA;AAAA,YACL,eAAA;AAAA,YACA,SAAA;AAAA,YACA,UAAA,EAAY,eAAA;AAAA,YACZ,SAAA,EAAW;AAAA,WACb;AAAA,QACF,CAAA;AAAA,QAEA,QAAA,EAAA;AAAA,0BAAA,GAAA;AAAA,YAAC,UAAA;AAAA,YAAA;AAAA,cACC,OAAA,EAAQ,OAAA;AAAA,cACR,IAAI,CAAA,KAAA,MAAU;AAAA,gBACZ,UAAA,EAAY,QAAA;AAAA,gBACZ,KAAA,EAAO,KAAA,CAAM,OAAA,CAAQ,IAAA,CAAK;AAAA,eAC5B,CAAA;AAAA,cAEC,QAAA,EAAA;AAAA;AAAA,WACH;AAAA,UACC,YAAA,mBACC,GAAA,CAAC,OAAA,EAAA,EAAQ,KAAA,EAAM,yBAAA,EACb,QAAA,kBAAA,GAAA;AAAA,YAAC,UAAA;AAAA,YAAA;AAAA,cACC,IAAA,EAAK,OAAA;AAAA,cACL,OAAA,EAAS,WAAA;AAAA,cACT,IAAI,CAAA,KAAA,MAAU;AAAA,gBACZ,KAAA,EAAO,KAAA,CAAM,OAAA,CAAQ,IAAA,CAAK,SAAA;AAAA,gBAC1B,SAAA,EAAW;AAAA,kBACT,KAAA,EAAO,KAAA,CAAM,OAAA,CAAQ,OAAA,CAAQ;AAAA;AAC/B,eACF,CAAA;AAAA,cAEA,QAAA,kBAAA,GAAA,CAAC,SAAA,EAAA,EAAU,QAAA,EAAS,SAAA,EAAU;AAAA;AAAA,aAElC,CAAA,GACE,IAAA;AAAA,0BACJ,GAAA;AAAA,YAAC,qBAAA;AAAA,YAAA;AAAA,cACC,QAAA,EAAS,OAAA;AAAA,cACT,IAAI,CAAA,KAAA,MAAU;AAAA,gBACZ,SAAA,EAAW,SAAS,gBAAA,GAAmB,MAAA;AAAA,gBACvC,UAAA,EAAY,qBAAA;AAAA,gBACZ,KAAA,EAAO,eACH,KAAA,CAAM,OAAA,CAAQ,QAAQ,IAAA,GACtB,KAAA,CAAM,QAAQ,IAAA,CAAK;AAAA,eACzB;AAAA;AAAA;AACF;AAAA;AAAA,KACF;AAAA,oBACA,GAAA;AAAA,MAAC,MAAA;AAAA,MAAA;AAAA,QACC,IAAA,EAAM,MAAA;AAAA,QACN,QAAA;AAAA,QACA,SAAA,EAAU,cAAA;AAAA,QACV,SAAA,EAAW;AAAA,UACT;AAAA,YACE,IAAA,EAAM,QAAA;AAAA,YACN,SAAS,EAAE,MAAA,EAAQ,CAAC,CAAA,EAAG,CAAC,CAAA;AAAE;AAC5B,SACF;AAAA,QAEA,QAAA,kBAAA,GAAA,CAAC,iBAAA,EAAA,EAAkB,WAAA,EAAa,WAAA,EAC9B,QAAA,kBAAA,IAAA,CAAC,OAAI,EAAA,EAAI,EAAE,EAAA,EAAI,CAAA,EAAE,EACf,QAAA,EAAA;AAAA,0BAAA,GAAA;AAAA,YAAC,kBAAA;AAAA,YAAA;AAAA,cACC,KAAA;AAAA,cACA,QAAA,EAAU,oBAAA;AAAA,cACV,kBAAA;AAAA,cACA,kBAAA,EAAoB,qBAAA;AAAA,cACpB;AAAA;AAAA,WACF;AAAA,UACC,eAAA,oBACC,GAAA;AAAA,YAAC,GAAA;AAAA,YAAA;AAAA,cACC,EAAA,EAAI;AAAA,gBACF,EAAA,EAAI,CAAA;AAAA,gBACJ,CAAA,EAAG,CAAA;AAAA,gBACH,eAAA,EAAiB,aAAA;AAAA,gBACjB,YAAA,EAAc,CAAA;AAAA,gBACd,MAAA,EAAQ,WAAA;AAAA,gBACR,WAAA,EAAa;AAAA,eACf;AAAA,cAEA,QAAA,kBAAA,GAAA;AAAA,gBAAC,UAAA;AAAA,gBAAA;AAAA,kBACC,OAAA,EAAQ,SAAA;AAAA,kBACR,EAAA,EAAI;AAAA,oBACF,KAAA,EAAO,oBAAA;AAAA,oBACP,UAAA,EAAY;AAAA,mBACd;AAAA,kBAEC,QAAA,EAAA;AAAA;AAAA;AACH;AAAA;AACF,SAAA,EAEJ,CAAA,EACF;AAAA;AAAA;AACF,GAAA,EACF,CAAA;AAEJ;;;;"}