@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,6 +10,7 @@ import 'react';
10
10
  import 'react-dom';
11
11
  import { NotFound } from '../common/NotFound.esm.js';
12
12
  import { SomethingWentWrong } from '../common/SomethingWentWrong.esm.js';
13
+ import '../common/StatusContainer.esm.js';
13
14
  import { LogoSpinner } from '../common/logoSpinner.esm.js';
14
15
 
15
16
  const GaugesGrid = styled(Box)(() => ({
@@ -17,10 +18,20 @@ const GaugesGrid = styled(Box)(() => ({
17
18
  gridTemplateColumns: "1fr 1fr",
18
19
  justifyItems: "center",
19
20
  alignItems: "center",
20
- gap: "8px",
21
+ gap: "20px",
21
22
  width: "fit-content",
22
23
  maxWidth: "100%",
23
- margin: "0 auto",
24
+ maxHeight: "280px",
25
+ overflowY: "auto",
26
+ overflowX: "hidden",
27
+ scrollbarWidth: "none",
28
+ // Firefox
29
+ msOverflowStyle: "none",
30
+ // IE and Edge
31
+ "&::-webkit-scrollbar": {
32
+ display: "none"
33
+ // Chrome, Safari, and Opera
34
+ },
24
35
  "@media (max-width: 320px)": {
25
36
  gridTemplateColumns: "1fr",
26
37
  gap: "12px"
@@ -60,7 +71,7 @@ const transformMttrStatisticsToGauges = (statistics) => {
60
71
  unit: "Hours"
61
72
  };
62
73
  };
63
- const riskLevelOrder = ["Critical", "High", "Medium", "Low"];
74
+ const riskLevelOrder = ["Critical", "High", "Medium", "Low", "Informational"];
64
75
  const sortedStatistics = statistics.sort((a, b) => {
65
76
  const aIndex = riskLevelOrder.indexOf(a.riskLevel);
66
77
  const bIndex = riskLevelOrder.indexOf(b.riskLevel);
@@ -70,23 +81,26 @@ const transformMttrStatisticsToGauges = (statistics) => {
70
81
  });
71
82
  return sortedStatistics.map((stat) => {
72
83
  const meanTimeDisplay = convertHoursForDisplay(stat.meanTimeInHours);
73
- const slaDisplay = convertHoursForDisplay(stat.slaInHours);
84
+ const slaDisplay = stat.slaInHours !== null ? convertHoursForDisplay(stat.slaInHours) : null;
74
85
  const unit = meanTimeDisplay.unit;
75
- const maxHours = Math.max(stat.meanTimeInHours, stat.slaInHours) + 24;
86
+ const maxHours = Math.max(stat.meanTimeInHours, stat.slaInHours || 0) + 24;
76
87
  const maxDisplay = convertHoursForDisplay(maxHours);
88
+ let tickValue = null;
89
+ if (slaDisplay !== null && stat.slaInHours !== null) {
90
+ tickValue = unit === "Days" ? slaDisplay.displayValue * 24 : Math.ceil(stat.slaInHours);
91
+ }
77
92
  return {
78
93
  value: unit === "Days" ? meanTimeDisplay.displayValue * 24 : Math.ceil(stat.meanTimeInHours),
79
94
  // Original hours for arc scaling
80
- tickValue: unit === "Days" ? slaDisplay.displayValue * 24 : Math.ceil(stat.slaInHours),
81
- // Original hours for arc scaling
95
+ tickValue,
82
96
  minValue: 0,
83
97
  maxValue: unit === "Days" ? maxDisplay.displayValue * 24 : Math.ceil(maxHours),
84
98
  categoryLabel: stat.riskLevel,
85
- tooltip: `View ${stat.riskLevel} risks out of SLA`,
99
+ tooltip: stat?.slaInHours ? `View ${stat.riskLevel} risks out of SLA` : "SLA is not defined",
86
100
  unit,
87
101
  displayValue: meanTimeDisplay.displayValue,
88
102
  // Converted value for center display
89
- displayTickValue: slaDisplay.displayValue,
103
+ displayTickValue: slaDisplay?.displayValue ?? null,
90
104
  // Converted value for tick display
91
105
  displayMinValue: 0,
92
106
  // Min is always 0
@@ -102,7 +116,8 @@ const MttrVsSLATile = ({
102
116
  footerText = "*SLA (days)",
103
117
  width = "100%",
104
118
  repoId,
105
- entityRef
119
+ entityRef,
120
+ applicationId
106
121
  }) => {
107
122
  const connectBackendApi = useApi(apiiroApiRef);
108
123
  const { fetch } = useApi(fetchApiRef);
@@ -113,10 +128,11 @@ const MttrVsSLATile = ({
113
128
  } = useMttrStatisticsData({
114
129
  connectApi: connectBackendApi,
115
130
  fetchApi: fetch,
116
- repositoryKey: repoId,
117
- entityRef
131
+ repositoryId: repoId,
132
+ entityRef,
133
+ applicationId
118
134
  });
119
- const shouldUseApiData = !!repoId;
135
+ const shouldUseApiData = !!repoId || !!applicationId;
120
136
  const apiGauges = shouldUseApiData && mttrStatisticsData ? transformMttrStatisticsToGauges(mttrStatisticsData) : null;
121
137
  const finalGauges = apiGauges || gauges;
122
138
  const footer = footerText && !mttrStatisticsDataLoading ? /* @__PURE__ */ jsx(FooterText, { children: footerText }) : void 0;
@@ -136,7 +152,7 @@ const MttrVsSLATile = ({
136
152
  return /* @__PURE__ */ jsx(ChartBox, { title, tooltip, width, children: /* @__PURE__ */ jsx(SomethingWentWrong, {}) });
137
153
  }
138
154
  if (finalGauges.length === 0) {
139
- return /* @__PURE__ */ jsx(ChartBox, { title, tooltip, width, children: repoId ? /* @__PURE__ */ jsx(NotFound, {}) : /* @__PURE__ */ jsx(NotFound, { message: "Please provide the repository details to access the data." }) });
155
+ return /* @__PURE__ */ jsx(ChartBox, { title, tooltip, width, children: repoId ? /* @__PURE__ */ jsx(NotFound, {}) : /* @__PURE__ */ jsx(NotFound, { message: "Please configure the apiiro annotation to access the data." }) });
140
156
  }
141
157
  return /* @__PURE__ */ jsx(ChartBox, { title, tooltip, footer, width, children: /* @__PURE__ */ jsx(GaugesGrid, { children: finalGauges.map((gauge, index) => /* @__PURE__ */ jsxs(GaugeContainer, { children: [
142
158
  /* @__PURE__ */ jsx(
@@ -160,6 +176,7 @@ const MttrVsSLATile = ({
160
176
  minValue: gauge.displayMinValue ?? gauge.minValue,
161
177
  maxValue: gauge.displayMaxValue ?? gauge.maxValue,
162
178
  categoryLabel: gauge.categoryLabel,
179
+ tickValue: gauge.tickValue,
163
180
  width: "130px"
164
181
  }
165
182
  )
@@ -1 +1 @@
1
- {"version":3,"file":"MttrVsSLATile.esm.js","sources":["../../../src/components/tiles/MttrVsSLATile.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 Box from '@mui/material/Box';\nimport { styled } from '@mui/material/styles';\nimport { fetchApiRef, useApi } from '@backstage/core-plugin-api';\nimport { ChartBox } from '../common/ChartBox';\nimport GaugeChart, { GaugeBottomLabels } from '../charts/GaugeChart';\nimport { useMttrStatisticsData } from '../../queries/mttr-statistics.queries';\nimport { apiiroApiRef } from '../../api';\nimport { MttrStatistic } from '../../queries/queries.type';\nimport { NotFound, SomethingWentWrong } from '../common';\nimport { LogoSpinner } from '../common/logoSpinner';\n\ninterface GaugeData {\n value: number;\n tickValue: number;\n minValue: number;\n maxValue: number;\n categoryLabel: string;\n tooltip?: string;\n unit?: string;\n displayValue?: number;\n displayTickValue?: number;\n displayMinValue?: number;\n displayMaxValue?: number;\n}\n\ninterface MttrVsSLATileProps {\n title?: string;\n tooltip?: string;\n gauges?: GaugeData[];\n footerText?: string;\n width?: string | number;\n height?: string | number;\n repoId?: string;\n entityRef?: string;\n}\n\nconst GaugesGrid = styled(Box)(() => ({\n display: 'grid',\n gridTemplateColumns: '1fr 1fr',\n justifyItems: 'center',\n alignItems: 'center',\n gap: '8px',\n width: 'fit-content',\n maxWidth: '100%',\n margin: '0 auto',\n '@media (max-width: 320px)': {\n gridTemplateColumns: '1fr',\n gap: '12px',\n },\n}));\n\nconst GaugeContainer = styled(Box)(() => ({\n display: 'flex',\n flexDirection: 'column',\n alignItems: 'center',\n minWidth: 0,\n position: 'relative',\n '&:hover': {\n zIndex: 10,\n },\n}));\n\nconst FooterText = styled('p')(({ theme }) => ({\n fontSize: '11px',\n color: theme.palette.text.secondary,\n fontWeight: 400,\n lineHeight: 1.2,\n margin: 0,\n whiteSpace: 'nowrap',\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n textAlign: 'center',\n}));\n\nconst transformMttrStatisticsToGauges = (\n statistics: MttrStatistic[],\n): GaugeData[] => {\n const convertHoursForDisplay = (\n hours: number,\n ): { displayValue: number; unit: string } => {\n if (hours > 23) {\n return {\n displayValue: Math.ceil(hours / 24),\n unit: 'Days',\n };\n }\n return {\n displayValue: Math.ceil(hours),\n unit: 'Hours',\n };\n };\n\n // Define the desired order: Medium, Low, High, Critical\n const riskLevelOrder = ['Critical', 'High', 'Medium', 'Low'];\n\n // Sort statistics according to the desired order\n const sortedStatistics = statistics.sort((a, b) => {\n const aIndex = riskLevelOrder.indexOf(a.riskLevel);\n const bIndex = riskLevelOrder.indexOf(b.riskLevel);\n\n // If risk level not found in order array, put it at the end\n const aOrder = aIndex === -1 ? riskLevelOrder.length : aIndex;\n const bOrder = bIndex === -1 ? riskLevelOrder.length : bIndex;\n\n return aOrder - bOrder;\n });\n\n return sortedStatistics.map(stat => {\n const meanTimeDisplay = convertHoursForDisplay(stat.meanTimeInHours);\n const slaDisplay = convertHoursForDisplay(stat.slaInHours);\n\n // Use the unit from the mean time (primary value)\n const unit = meanTimeDisplay.unit;\n\n // Use original hours for arc scaling, but display converted values for labels\n const maxHours = Math.max(stat.meanTimeInHours, stat.slaInHours) + 24;\n const maxDisplay = convertHoursForDisplay(maxHours);\n\n return {\n value:\n unit === 'Days'\n ? meanTimeDisplay.displayValue * 24\n : Math.ceil(stat.meanTimeInHours), // Original hours for arc scaling\n tickValue:\n unit === 'Days'\n ? slaDisplay.displayValue * 24\n : Math.ceil(stat.slaInHours), // Original hours for arc scaling\n minValue: 0,\n maxValue:\n unit === 'Days' ? maxDisplay.displayValue * 24 : Math.ceil(maxHours),\n categoryLabel: stat.riskLevel,\n tooltip: `View ${stat.riskLevel} risks out of SLA`,\n unit: unit,\n displayValue: meanTimeDisplay.displayValue, // Converted value for center display\n displayTickValue: slaDisplay.displayValue, // Converted value for tick display\n displayMinValue: 0, // Min is always 0\n displayMaxValue: maxDisplay.displayValue, // Converted max value for bottom labels\n };\n });\n};\n\nexport const MttrVsSLATile = ({\n title = 'MTTR vs. SLA',\n tooltip = 'The scale is set dynamically according to the SLA definition',\n gauges = [],\n footerText = '*SLA (days)',\n width = '100%',\n repoId,\n entityRef,\n}: MttrVsSLATileProps) => {\n // Use API hooks internally\n const connectBackendApi = useApi(apiiroApiRef);\n const { fetch } = useApi(fetchApiRef);\n\n // Always call the hook, but conditionally use the result\n const {\n mttrStatisticsData,\n mttrStatisticsDataError,\n mttrStatisticsDataLoading,\n } = useMttrStatisticsData({\n connectApi: connectBackendApi,\n fetchApi: fetch,\n repositoryKey: repoId,\n entityRef: entityRef,\n });\n // Only use API data if repositoryKey is provided\n const shouldUseApiData = !!repoId;\n\n // Transform API data to gauge data if available and should be used\n const apiGauges =\n shouldUseApiData && mttrStatisticsData\n ? transformMttrStatisticsToGauges(mttrStatisticsData)\n : null;\n\n // Use API data if available, otherwise fall back to provided gauges\n const finalGauges = apiGauges || gauges;\n\n const footer =\n footerText && !mttrStatisticsDataLoading ? (\n <FooterText>{footerText}</FooterText>\n ) : undefined;\n\n // Show loading state if API is being used and data is loading\n if (shouldUseApiData && mttrStatisticsDataLoading) {\n return (\n <ChartBox title={title} tooltip={tooltip} footer={footer} width={width}>\n <Box\n display=\"flex\"\n justifyContent=\"center\"\n alignItems=\"center\"\n minHeight=\"250px\"\n >\n <LogoSpinner />\n </Box>\n </ChartBox>\n );\n }\n\n // Show error state if API is being used and there's an error\n if (shouldUseApiData && mttrStatisticsDataError) {\n return (\n <ChartBox title={title} tooltip={tooltip} width={width}>\n <SomethingWentWrong />\n </ChartBox>\n );\n }\n\n // Show message when no data is available\n if (finalGauges.length === 0) {\n return (\n <ChartBox title={title} tooltip={tooltip} width={width}>\n {repoId ? (\n <NotFound />\n ) : (\n <NotFound message=\"Please provide the repository details to access the data.\" />\n )}\n </ChartBox>\n );\n }\n\n return (\n <ChartBox title={title} tooltip={tooltip} footer={footer} width={width}>\n <GaugesGrid>\n {finalGauges.map((gauge, index) => (\n <GaugeContainer key={index}>\n <GaugeChart\n width={200}\n height={180}\n value={gauge.value}\n tickValue={gauge.tickValue}\n minValue={gauge.minValue}\n maxValue={gauge.maxValue}\n tooltip={gauge.tooltip}\n unit={gauge.unit}\n displayValue={gauge.displayValue}\n displayTickValue={gauge.displayTickValue}\n />\n <GaugeBottomLabels\n minValue={gauge.displayMinValue ?? gauge.minValue}\n maxValue={gauge.displayMaxValue ?? gauge.maxValue}\n categoryLabel={gauge.categoryLabel}\n width=\"130px\"\n />\n </GaugeContainer>\n ))}\n </GaugesGrid>\n </ChartBox>\n );\n};\n\nexport default MttrVsSLATile;\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"MttrVsSLATile.esm.js","sources":["../../../src/components/tiles/MttrVsSLATile.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 Box from '@mui/material/Box';\nimport { styled } from '@mui/material/styles';\nimport { fetchApiRef, useApi } from '@backstage/core-plugin-api';\nimport { ChartBox } from '../common/ChartBox';\nimport GaugeChart, { GaugeBottomLabels } from '../charts/GaugeChart';\nimport { useMttrStatisticsData } from '../../queries/mttr-statistics.queries';\nimport { apiiroApiRef } from '../../api';\nimport { MttrStatistic } from '../../queries/queries.type';\nimport { NotFound, SomethingWentWrong } from '../common';\nimport { LogoSpinner } from '../common/logoSpinner';\n\ninterface GaugeData {\n value: number;\n tickValue: number | null;\n minValue: number;\n maxValue: number;\n categoryLabel: string;\n tooltip?: string;\n unit?: string;\n displayValue?: number;\n displayTickValue?: number | null;\n displayMinValue?: number;\n displayMaxValue?: number;\n}\n\ninterface MttrVsSLATileProps {\n title?: string;\n tooltip?: string;\n gauges?: GaugeData[];\n footerText?: string;\n width?: string | number;\n height?: string | number;\n repoId?: string;\n entityRef?: string;\n applicationId?: string;\n}\n\nconst GaugesGrid = styled(Box)(() => ({\n display: 'grid',\n gridTemplateColumns: '1fr 1fr',\n justifyItems: 'center',\n alignItems: 'center',\n gap: '20px',\n width: 'fit-content',\n maxWidth: '100%',\n maxHeight: '280px',\n overflowY: 'auto',\n overflowX: 'hidden',\n scrollbarWidth: 'none', // Firefox\n msOverflowStyle: 'none', // IE and Edge\n '&::-webkit-scrollbar': {\n display: 'none', // Chrome, Safari, and Opera\n },\n '@media (max-width: 320px)': {\n gridTemplateColumns: '1fr',\n gap: '12px',\n },\n}));\n\nconst GaugeContainer = styled(Box)(() => ({\n display: 'flex',\n flexDirection: 'column',\n alignItems: 'center',\n minWidth: 0,\n position: 'relative',\n '&:hover': {\n zIndex: 10,\n },\n}));\n\nconst FooterText = styled('p')(({ theme }) => ({\n fontSize: '11px',\n color: theme.palette.text.secondary,\n fontWeight: 400,\n lineHeight: 1.2,\n margin: 0,\n whiteSpace: 'nowrap',\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n textAlign: 'center',\n}));\n\nconst transformMttrStatisticsToGauges = (\n statistics: MttrStatistic[],\n): GaugeData[] => {\n const convertHoursForDisplay = (\n hours: number,\n ): { displayValue: number; unit: string } => {\n if (hours > 23) {\n return {\n displayValue: Math.ceil(hours / 24),\n unit: 'Days',\n };\n }\n return {\n displayValue: Math.ceil(hours),\n unit: 'Hours',\n };\n };\n\n // Define the desired order: Medium, Low, High, Critical\n const riskLevelOrder = ['Critical', 'High', 'Medium', 'Low', 'Informational'];\n\n // Sort statistics according to the desired order\n const sortedStatistics = statistics.sort((a, b) => {\n const aIndex = riskLevelOrder.indexOf(a.riskLevel);\n const bIndex = riskLevelOrder.indexOf(b.riskLevel);\n\n // If risk level not found in order array, put it at the end\n const aOrder = aIndex === -1 ? riskLevelOrder.length : aIndex;\n const bOrder = bIndex === -1 ? riskLevelOrder.length : bIndex;\n\n return aOrder - bOrder;\n });\n\n return sortedStatistics.map(stat => {\n const meanTimeDisplay = convertHoursForDisplay(stat.meanTimeInHours);\n const slaDisplay =\n stat.slaInHours !== null ? convertHoursForDisplay(stat.slaInHours) : null;\n\n // Use the unit from the mean time (primary value)\n const unit = meanTimeDisplay.unit;\n\n // Use original hours for arc scaling, but display converted values for labels\n const maxHours = Math.max(stat.meanTimeInHours, stat.slaInHours || 0) + 24;\n const maxDisplay = convertHoursForDisplay(maxHours);\n\n let tickValue: number | null = null;\n if (slaDisplay !== null && stat.slaInHours !== null) {\n tickValue =\n unit === 'Days'\n ? slaDisplay.displayValue * 24\n : Math.ceil(stat.slaInHours);\n }\n\n return {\n value:\n unit === 'Days'\n ? meanTimeDisplay.displayValue * 24\n : Math.ceil(stat.meanTimeInHours), // Original hours for arc scaling\n tickValue,\n minValue: 0,\n maxValue:\n unit === 'Days' ? maxDisplay.displayValue * 24 : Math.ceil(maxHours),\n categoryLabel: stat.riskLevel,\n tooltip: stat?.slaInHours\n ? `View ${stat.riskLevel} risks out of SLA`\n : 'SLA is not defined',\n unit: unit,\n displayValue: meanTimeDisplay.displayValue, // Converted value for center display\n displayTickValue: slaDisplay?.displayValue ?? null, // Converted value for tick display\n displayMinValue: 0, // Min is always 0\n displayMaxValue: maxDisplay.displayValue, // Converted max value for bottom labels\n };\n });\n};\n\nexport const MttrVsSLATile = ({\n title = 'MTTR vs. SLA',\n tooltip = 'The scale is set dynamically according to the SLA definition',\n gauges = [],\n footerText = '*SLA (days)',\n width = '100%',\n repoId,\n entityRef,\n applicationId,\n}: MttrVsSLATileProps) => {\n // Use API hooks internally\n const connectBackendApi = useApi(apiiroApiRef);\n const { fetch } = useApi(fetchApiRef);\n\n // Always call the hook, but conditionally use the result\n const {\n mttrStatisticsData,\n mttrStatisticsDataError,\n mttrStatisticsDataLoading,\n } = useMttrStatisticsData({\n connectApi: connectBackendApi,\n fetchApi: fetch,\n repositoryId: repoId,\n entityRef: entityRef,\n applicationId: applicationId,\n });\n // Only use API data if repositoryId or applicationId is provided\n const shouldUseApiData = !!repoId || !!applicationId;\n\n // Transform API data to gauge data if available and should be used\n const apiGauges =\n shouldUseApiData && mttrStatisticsData\n ? transformMttrStatisticsToGauges(mttrStatisticsData)\n : null;\n\n // Use API data if available, otherwise fall back to provided gauges\n const finalGauges = apiGauges || gauges;\n\n const footer =\n footerText && !mttrStatisticsDataLoading ? (\n <FooterText>{footerText}</FooterText>\n ) : undefined;\n\n // Show loading state if API is being used and data is loading\n if (shouldUseApiData && mttrStatisticsDataLoading) {\n return (\n <ChartBox title={title} tooltip={tooltip} footer={footer} width={width}>\n <Box\n display=\"flex\"\n justifyContent=\"center\"\n alignItems=\"center\"\n minHeight=\"250px\"\n >\n <LogoSpinner />\n </Box>\n </ChartBox>\n );\n }\n\n // Show error state if API is being used and there's an error\n if (shouldUseApiData && mttrStatisticsDataError) {\n return (\n <ChartBox title={title} tooltip={tooltip} width={width}>\n <SomethingWentWrong />\n </ChartBox>\n );\n }\n\n // Show message when no data is available\n if (finalGauges.length === 0) {\n return (\n <ChartBox title={title} tooltip={tooltip} width={width}>\n {repoId ? (\n <NotFound />\n ) : (\n <NotFound message=\"Please configure the apiiro annotation to access the data.\" />\n )}\n </ChartBox>\n );\n }\n\n return (\n <ChartBox title={title} tooltip={tooltip} footer={footer} width={width}>\n <GaugesGrid>\n {finalGauges.map((gauge, index) => (\n <GaugeContainer key={index}>\n <GaugeChart\n width={200}\n height={180}\n value={gauge.value}\n tickValue={gauge.tickValue}\n minValue={gauge.minValue}\n maxValue={gauge.maxValue}\n tooltip={gauge.tooltip}\n unit={gauge.unit}\n displayValue={gauge.displayValue}\n displayTickValue={gauge.displayTickValue}\n />\n <GaugeBottomLabels\n minValue={gauge.displayMinValue ?? gauge.minValue}\n maxValue={gauge.displayMaxValue ?? gauge.maxValue}\n categoryLabel={gauge.categoryLabel}\n tickValue={gauge.tickValue}\n width=\"130px\"\n />\n </GaugeContainer>\n ))}\n </GaugesGrid>\n </ChartBox>\n );\n};\n\nexport default MttrVsSLATile;\n"],"names":[],"mappings":"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"}
@@ -15,6 +15,7 @@ import 'react';
15
15
  import 'react-dom';
16
16
  import { NotFound } from '../common/NotFound.esm.js';
17
17
  import { SomethingWentWrong } from '../common/SomethingWentWrong.esm.js';
18
+ import '../common/StatusContainer.esm.js';
18
19
  import { LogoSpinner } from '../common/logoSpinner.esm.js';
19
20
  import { formatNumberWithSuffix } from '../../utils/numberFormatter.esm.js';
20
21
 
@@ -87,7 +88,8 @@ const RiskOverTimeTile = ({
87
88
  tooltip = "Track risk score changes over time",
88
89
  width = "100%",
89
90
  repoId,
90
- entityRef
91
+ entityRef,
92
+ applicationId
91
93
  }) => {
92
94
  const theme = useTheme();
93
95
  const trendColors = getTrendColors(theme);
@@ -100,36 +102,37 @@ const RiskOverTimeTile = ({
100
102
  } = useRiskScoreOverTimeData({
101
103
  connectApi: connectBackendApi,
102
104
  fetchApi: fetch,
103
- repositoryKey: repoId,
104
- entityRef
105
+ repositoryId: repoId,
106
+ entityRef,
107
+ applicationId
105
108
  });
106
- if (riskScoreOverTimeDataLoading) {
109
+ if (!repoId && !applicationId) {
107
110
  return /* @__PURE__ */ jsx(ChartBox, { title, tooltip, width, children: /* @__PURE__ */ jsx(
108
111
  Box,
109
112
  {
110
113
  display: "flex",
111
114
  justifyContent: "center",
112
115
  alignItems: "center",
113
- minHeight: "250px",
114
- children: /* @__PURE__ */ jsx(LogoSpinner, {})
116
+ minHeight: "300px",
117
+ children: /* @__PURE__ */ jsx(NotFound, { message: "Please configure the apiiro annotation to access the data." })
115
118
  }
116
119
  ) });
117
120
  }
118
- if (riskScoreOverTimeDataError) {
119
- return /* @__PURE__ */ jsx(ChartBox, { title, tooltip, width, children: /* @__PURE__ */ jsx(SomethingWentWrong, {}) });
120
- }
121
- if (!repoId) {
121
+ if (riskScoreOverTimeDataLoading) {
122
122
  return /* @__PURE__ */ jsx(ChartBox, { title, tooltip, width, children: /* @__PURE__ */ jsx(
123
123
  Box,
124
124
  {
125
125
  display: "flex",
126
126
  justifyContent: "center",
127
127
  alignItems: "center",
128
- minHeight: "300px",
129
- children: /* @__PURE__ */ jsx(NotFound, { message: "Please provide the repository details to access the data." })
128
+ minHeight: "250px",
129
+ children: /* @__PURE__ */ jsx(LogoSpinner, {})
130
130
  }
131
131
  ) });
132
132
  }
133
+ if (riskScoreOverTimeDataError) {
134
+ return /* @__PURE__ */ jsx(ChartBox, { title, tooltip, width, children: /* @__PURE__ */ jsx(SomethingWentWrong, {}) });
135
+ }
133
136
  const finalData = riskScoreOverTimeData ? riskScoreOverTimeData.map((point) => ({
134
137
  date: point.date,
135
138
  riskScore: point.count
@@ -1 +1 @@
1
- {"version":3,"file":"RiskOverTimeTile.esm.js","sources":["../../../src/components/tiles/RiskOverTimeTile.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 Box from '@mui/material/Box';\nimport Typography from '@mui/material/Typography';\nimport { styled, useTheme } from '@mui/material/styles';\nimport { getTrendColors } from '../../theme/themeUtils';\nimport { fetchApiRef, useApi } from '@backstage/core-plugin-api';\nimport { ChartBox } from '../common/ChartBox';\nimport { LineChart, LineChartSeries } from '../charts/LineChart';\nimport TrendingUpIcon from '@mui/icons-material/TrendingUp';\nimport TrendingDownIcon from '@mui/icons-material/TrendingDown';\nimport RemoveIcon from '@mui/icons-material/Remove';\nimport { useRiskScoreOverTimeData } from '../../queries/risk-score-over-time.queries';\nimport { apiiroApiRef } from '../../api';\nimport { RiskScoreOverTimeDataPoint } from '../../queries/queries.type';\nimport { NotFound, SomethingWentWrong } from '../common';\nimport { LogoSpinner } from '../common/logoSpinner';\nimport { formatNumberWithSuffix } from '../../utils/numberFormatter';\n\ninterface RiskDataPoint {\n date: string;\n riskScore: number;\n}\n\ninterface RiskOverTimeTileProps {\n title?: string;\n tooltip?: string;\n width?: string | number;\n height?: string | number;\n repoId?: string;\n entityRef?: string;\n}\n\nconst HeaderContainer = styled(Box)(() => ({\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n marginBottom: '16px',\n width: '100%',\n}));\n\nconst TitleContainer = styled(Box)(() => ({\n display: 'flex',\n alignItems: 'center',\n gap: '8px',\n}));\n\nconst StyledTitle = styled(Typography)(({ theme }) => ({\n fontWeight: 400,\n fontSize: '16px',\n lineHeight: '24px',\n color: theme.palette.text.primary,\n}));\n\nconst PercentageContainer = styled(Box)<{\n backgroundColor: string;\n textColor: string;\n}>(({ backgroundColor, textColor }) => ({\n display: 'flex',\n alignItems: 'center',\n gap: '4px',\n backgroundColor: backgroundColor,\n padding: '4px 8px',\n borderRadius: '12px',\n color: textColor,\n fontSize: '14px',\n fontWeight: 500,\n}));\n\n// Custom date sorting function for YYYY-MM-DD format\nconst sortDateStrings = (dates: string[]): string[] => {\n return dates.sort((a, b) => {\n const dateA = new Date(a).getTime();\n const dateB = new Date(b).getTime();\n // Handle invalid dates by putting them at the end\n if (isNaN(dateA) && isNaN(dateB)) return 0;\n if (isNaN(dateA)) return 1;\n if (isNaN(dateB)) return -1;\n return dateA - dateB;\n });\n};\n\nconst formatRiskScore = (value: number): string => {\n return formatNumberWithSuffix(value, 1);\n};\n\n// Format X-axis ticks to show only MM/DD\nconst formatXAxisDate = (dateStr: string | number): string => {\n if (typeof dateStr !== 'string') return String(dateStr);\n const date = new Date(dateStr);\n // Handle invalid dates\n if (isNaN(date.getTime())) {\n return String(dateStr);\n }\n const month = (date.getMonth() + 1).toString().padStart(2, '0');\n const day = date.getDate().toString().padStart(2, '0');\n return `${month}/${day}`;\n};\n\n// Format tooltip header as \"D MMM YYYY\" (e.g., 7 Oct 2025) with day first and no comma\nconst formatTooltipDate = (dateStr: string | number): string => {\n if (typeof dateStr !== 'string') return String(dateStr);\n const date = new Date(dateStr);\n // Handle invalid dates\n if (isNaN(date.getTime())) {\n return String(dateStr);\n }\n // Use en-GB to ensure day-first order without comma\n return date.toLocaleDateString('en-GB', {\n day: 'numeric',\n month: 'short',\n year: 'numeric',\n });\n};\n\nexport const RiskOverTimeTile = ({\n title = 'Risk score over time',\n tooltip = 'Track risk score changes over time',\n width = '100%',\n repoId,\n entityRef,\n}: RiskOverTimeTileProps) => {\n // Use API hooks internally\n const theme = useTheme();\n const trendColors = getTrendColors(theme);\n const connectBackendApi = useApi(apiiroApiRef);\n const { fetch } = useApi(fetchApiRef);\n\n // Always call the hook, but conditionally use the result\n const {\n riskScoreOverTimeData,\n riskScoreOverTimeDataError,\n riskScoreOverTimeDataLoading,\n } = useRiskScoreOverTimeData({\n connectApi: connectBackendApi,\n fetchApi: fetch,\n repositoryKey: repoId,\n entityRef: entityRef,\n });\n\n // Show loading state while data is loading (Query check - loading)\n if (riskScoreOverTimeDataLoading) {\n return (\n <ChartBox title={title} tooltip={tooltip} width={width}>\n <Box\n display=\"flex\"\n justifyContent=\"center\"\n alignItems=\"center\"\n minHeight=\"250px\"\n >\n <LogoSpinner />\n </Box>\n </ChartBox>\n );\n }\n\n // Show error state if there's an error (Query check - error)\n if (riskScoreOverTimeDataError) {\n return (\n <ChartBox title={title} tooltip={tooltip} width={width}>\n <SomethingWentWrong />\n </ChartBox>\n );\n }\n\n // Show message when no repository key is provided\n if (!repoId) {\n return (\n <ChartBox title={title} tooltip={tooltip} width={width}>\n <Box\n display=\"flex\"\n justifyContent=\"center\"\n alignItems=\"center\"\n minHeight=\"300px\"\n >\n <NotFound message=\"Please provide the repository details to access the data.\" />\n </Box>\n </ChartBox>\n );\n }\n\n // Transform API data to component data format\n const finalData: RiskDataPoint[] = riskScoreOverTimeData\n ? riskScoreOverTimeData.map((point: RiskScoreOverTimeDataPoint) => ({\n date: point.date,\n riskScore: point.count,\n }))\n : [];\n\n // Total data length check\n if (finalData.length === 0) {\n return (\n <ChartBox title={title} tooltip={tooltip} width={width}>\n <NotFound />\n </ChartBox>\n );\n }\n\n // Check if all data points have zero count\n const hasAllZeroValues = riskScoreOverTimeData!.every(\n (point: RiskScoreOverTimeDataPoint) => point.count === 0,\n );\n\n // If all zero, send NotFound\n if (hasAllZeroValues) {\n return (\n <ChartBox title={title} tooltip={tooltip} width={width}>\n <NotFound />\n </ChartBox>\n );\n }\n\n // Calculate percentage change from API data (only when we have valid data)\n const calculatePercentageChange = (data: RiskDataPoint[]): number => {\n if (data.length <= 1) return 0;\n\n const firstValue = data[0].riskScore ?? 0;\n const lastValue = data[data.length - 1].riskScore ?? 0;\n const difference = lastValue - firstValue;\n\n // Avoid division by zero\n if (firstValue === 0) {\n return lastValue > 0 ? 100 : 0;\n }\n\n // Handle negative firstValue edge case\n if (firstValue < 0) {\n // If first value is negative and last is positive, return positive change\n if (lastValue > 0) return 100;\n // If both are negative, calculate relative change\n return (difference / Math.abs(firstValue)) * 100;\n }\n\n return (difference / firstValue) * 100;\n };\n\n const calculatedPercentageChange = calculatePercentageChange(finalData);\n\n // Determine colors based on percentage change\n const isNeutralChange = calculatedPercentageChange === 0;\n const isPositiveChange = calculatedPercentageChange > 0;\n\n // Set colors based on change type using theme-aware colors\n let lineColor: string;\n let indicatorColor: string;\n let indicatorTextColor: string;\n let TrendIcon: typeof TrendingUpIcon;\n\n if (isNeutralChange) {\n lineColor = trendColors.neutral.line;\n indicatorColor = trendColors.neutral.background;\n indicatorTextColor = trendColors.neutral.text;\n TrendIcon = RemoveIcon; // Minus/horizontal icon for no trend\n } else if (isPositiveChange) {\n lineColor = trendColors.positive.line;\n indicatorColor = trendColors.positive.background;\n indicatorTextColor = trendColors.positive.text;\n TrendIcon = TrendingUpIcon; // Up icon for positive\n } else {\n lineColor = trendColors.negative.line;\n indicatorColor = trendColors.negative.background;\n indicatorTextColor = trendColors.negative.text;\n TrendIcon = TrendingDownIcon; // Down icon for negative\n }\n\n // Calculate y-axis range with exactly 6 ticks; top tick equals data maximum\n const calculateYAxisRange = (data: RiskDataPoint[]) => {\n // Safety check: ensure we have data\n if (!data || data.length === 0) {\n return { yMin: 0, yMax: 100 };\n }\n\n const values = data\n .map(d => d.riskScore)\n .filter(v => !isNaN(v) && isFinite(v));\n\n // Safety check: ensure we have valid numeric values\n if (values.length === 0) {\n return { yMin: 0, yMax: 100 };\n }\n\n const minValue = Math.min(...values);\n const maxValue = Math.max(...values);\n\n // Edge case: both values are zero\n if (minValue === 0 && maxValue === 0) {\n return { yMin: 0, yMax: 10 };\n }\n\n // Edge case: maxValue is zero but minValue is not (shouldn't happen, but handle it)\n if (maxValue === 0 && minValue < 0) {\n return { yMin: minValue * 1.2, yMax: 10 };\n }\n\n // Calculate yMin: subtract 15% from minValue, but ensure it doesn't go below 0 for non-negative data\n // If minValue is negative, allow yMin to be negative\n let yMin: number;\n if (minValue >= 0) {\n yMin = Math.max(0, minValue - minValue * 0.15);\n } else {\n yMin = minValue - minValue * 0.15;\n }\n\n // Calculate yMax: add 1% to maxValue\n // Handle edge case where maxValue is very small (near zero)\n let yMax: number;\n if (maxValue === 0) {\n yMax = 10;\n } else {\n yMax = maxValue + maxValue * 0.01;\n }\n\n // Edge case: ensure yMin < yMax (shouldn't happen with current logic, but safety check)\n if (yMin >= yMax) {\n if (maxValue === 0) {\n return { yMin: 0, yMax: 10 };\n }\n // If they're equal or reversed, create a small range around maxValue\n yMin = Math.max(0, maxValue * 0.9);\n yMax = maxValue * 1.1;\n }\n\n return { yMin, yMax };\n };\n\n const { yMin, yMax } = calculateYAxisRange(finalData);\n\n // Build exactly 6 ticks between yMin and yMax (inclusive)\n const yTicks: number[] = (() => {\n const count = 6;\n const ticks: number[] = [];\n\n // Edge case: yMin and yMax are equal (or very close due to floating point)\n if (Math.abs(yMax - yMin) < Number.EPSILON) {\n for (let i = 0; i < count; i++) ticks.push(yMin);\n return ticks;\n }\n\n // Edge case: yMin > yMax (shouldn't happen, but handle it)\n if (yMin > yMax) {\n // Reverse the range\n const step = (yMin - yMax) / (count - 1);\n for (let i = 0; i < count; i++) {\n ticks.push(yMax + i * step);\n }\n return ticks.reverse();\n }\n\n // Normal case: calculate evenly spaced ticks\n const step = (yMax - yMin) / (count - 1);\n for (let i = 0; i < count; i++) {\n // Use toFixed and parseFloat to handle floating point precision issues\n const tickValue = yMin + i * step;\n // Round to reasonable precision (10 decimal places max)\n ticks.push(parseFloat(tickValue.toFixed(10)));\n }\n\n // Ensure the last tick is exactly yMax to avoid floating point drift\n ticks[count - 1] = yMax;\n\n return ticks;\n })();\n\n const series: LineChartSeries[] = [\n {\n id: 'riskScore',\n label: 'Risk score',\n data: finalData.map(point => ({\n x: point.date,\n y: point.riskScore,\n })),\n color: lineColor, // Dynamic color based on percentage change\n },\n ];\n\n const customHeader = (\n <HeaderContainer>\n <TitleContainer>\n <StyledTitle>{title}</StyledTitle>\n </TitleContainer>\n <PercentageContainer\n backgroundColor={indicatorColor}\n textColor={indicatorTextColor}\n >\n <TrendIcon sx={{ fontSize: '16px', color: indicatorTextColor }} />\n <Typography\n variant=\"body2\"\n sx={{ fontWeight: 500, color: indicatorTextColor }}\n >\n {calculatedPercentageChange > 0 ? '+' : ''}\n {calculatedPercentageChange.toFixed(1)}%\n </Typography>\n </PercentageContainer>\n </HeaderContainer>\n );\n\n return (\n <ChartBox\n title={title}\n tooltip={tooltip}\n width={width}\n customHeader={customHeader}\n >\n <LineChart\n series={series}\n width=\"100%\"\n height={300}\n showLegend\n legendPosition=\"bottom\"\n showGrid\n formatYValue={formatRiskScore}\n formatXValue={formatXAxisDate}\n formatTooltipXValue={formatTooltipDate}\n showDataPoints={false}\n customXSort={values => sortDateStrings(values as string[])}\n yAxisMin={yMin}\n yAxisMax={yMax}\n yAxisTicks={yTicks}\n />\n </ChartBox>\n );\n};\n\nexport default RiskOverTimeTile;\n"],"names":["yMin","yMax","step"],"mappings":"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"}
1
+ {"version":3,"file":"RiskOverTimeTile.esm.js","sources":["../../../src/components/tiles/RiskOverTimeTile.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 Box from '@mui/material/Box';\nimport Typography from '@mui/material/Typography';\nimport { styled, useTheme } from '@mui/material/styles';\nimport { getTrendColors } from '../../theme/themeUtils';\nimport { fetchApiRef, useApi } from '@backstage/core-plugin-api';\nimport { ChartBox } from '../common/ChartBox';\nimport { LineChart, LineChartSeries } from '../charts/LineChart';\nimport TrendingUpIcon from '@mui/icons-material/TrendingUp';\nimport TrendingDownIcon from '@mui/icons-material/TrendingDown';\nimport RemoveIcon from '@mui/icons-material/Remove';\nimport { useRiskScoreOverTimeData } from '../../queries/risk-score-over-time.queries';\nimport { apiiroApiRef } from '../../api';\nimport { RiskScoreOverTimeDataPoint } from '../../queries/queries.type';\nimport { NotFound, SomethingWentWrong } from '../common';\nimport { LogoSpinner } from '../common/logoSpinner';\nimport { formatNumberWithSuffix } from '../../utils/numberFormatter';\n\ninterface RiskDataPoint {\n date: string;\n riskScore: number;\n}\n\ninterface RiskOverTimeTileProps {\n title?: string;\n tooltip?: string;\n width?: string | number;\n height?: string | number;\n repoId?: string;\n entityRef?: string;\n applicationId?: string;\n}\n\nconst HeaderContainer = styled(Box)(() => ({\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n marginBottom: '16px',\n width: '100%',\n}));\n\nconst TitleContainer = styled(Box)(() => ({\n display: 'flex',\n alignItems: 'center',\n gap: '8px',\n}));\n\nconst StyledTitle = styled(Typography)(({ theme }) => ({\n fontWeight: 400,\n fontSize: '16px',\n lineHeight: '24px',\n color: theme.palette.text.primary,\n}));\n\nconst PercentageContainer = styled(Box)<{\n backgroundColor: string;\n textColor: string;\n}>(({ backgroundColor, textColor }) => ({\n display: 'flex',\n alignItems: 'center',\n gap: '4px',\n backgroundColor: backgroundColor,\n padding: '4px 8px',\n borderRadius: '12px',\n color: textColor,\n fontSize: '14px',\n fontWeight: 500,\n}));\n\n// Custom date sorting function for YYYY-MM-DD format\nconst sortDateStrings = (dates: string[]): string[] => {\n return dates.sort((a, b) => {\n const dateA = new Date(a).getTime();\n const dateB = new Date(b).getTime();\n // Handle invalid dates by putting them at the end\n if (isNaN(dateA) && isNaN(dateB)) return 0;\n if (isNaN(dateA)) return 1;\n if (isNaN(dateB)) return -1;\n return dateA - dateB;\n });\n};\n\nconst formatRiskScore = (value: number): string => {\n return formatNumberWithSuffix(value, 1);\n};\n\n// Format X-axis ticks to show only MM/DD\nconst formatXAxisDate = (dateStr: string | number): string => {\n if (typeof dateStr !== 'string') return String(dateStr);\n const date = new Date(dateStr);\n // Handle invalid dates\n if (isNaN(date.getTime())) {\n return String(dateStr);\n }\n const month = (date.getMonth() + 1).toString().padStart(2, '0');\n const day = date.getDate().toString().padStart(2, '0');\n return `${month}/${day}`;\n};\n\n// Format tooltip header as \"D MMM YYYY\" (e.g., 7 Oct 2025) with day first and no comma\nconst formatTooltipDate = (dateStr: string | number): string => {\n if (typeof dateStr !== 'string') return String(dateStr);\n const date = new Date(dateStr);\n // Handle invalid dates\n if (isNaN(date.getTime())) {\n return String(dateStr);\n }\n // Use en-GB to ensure day-first order without comma\n return date.toLocaleDateString('en-GB', {\n day: 'numeric',\n month: 'short',\n year: 'numeric',\n });\n};\n\nexport const RiskOverTimeTile = ({\n title = 'Risk score over time',\n tooltip = 'Track risk score changes over time',\n width = '100%',\n repoId,\n entityRef,\n applicationId,\n}: RiskOverTimeTileProps) => {\n // Use API hooks internally\n const theme = useTheme();\n const trendColors = getTrendColors(theme);\n const connectBackendApi = useApi(apiiroApiRef);\n const { fetch } = useApi(fetchApiRef);\n\n const {\n riskScoreOverTimeData,\n riskScoreOverTimeDataError,\n riskScoreOverTimeDataLoading,\n } = useRiskScoreOverTimeData({\n connectApi: connectBackendApi,\n fetchApi: fetch,\n repositoryId: repoId,\n entityRef: entityRef,\n applicationId: applicationId,\n });\n\n // Show message when repository key or application key is not provided\n if (!repoId && !applicationId) {\n return (\n <ChartBox title={title} tooltip={tooltip} width={width}>\n <Box\n display=\"flex\"\n justifyContent=\"center\"\n alignItems=\"center\"\n minHeight=\"300px\"\n >\n <NotFound message=\"Please configure the apiiro annotation to access the data.\" />\n </Box>\n </ChartBox>\n );\n }\n\n // Show loading state while data is loading (Query check - loading)\n if (riskScoreOverTimeDataLoading) {\n return (\n <ChartBox title={title} tooltip={tooltip} width={width}>\n <Box\n display=\"flex\"\n justifyContent=\"center\"\n alignItems=\"center\"\n minHeight=\"250px\"\n >\n <LogoSpinner />\n </Box>\n </ChartBox>\n );\n }\n\n // Show error state if there's an error (Query check - error)\n if (riskScoreOverTimeDataError) {\n return (\n <ChartBox title={title} tooltip={tooltip} width={width}>\n <SomethingWentWrong />\n </ChartBox>\n );\n }\n\n // Transform API data to component data format\n const finalData: RiskDataPoint[] = riskScoreOverTimeData\n ? riskScoreOverTimeData.map((point: RiskScoreOverTimeDataPoint) => ({\n date: point.date,\n riskScore: point.count,\n }))\n : [];\n\n // Total data length check\n if (finalData.length === 0) {\n return (\n <ChartBox title={title} tooltip={tooltip} width={width}>\n <NotFound />\n </ChartBox>\n );\n }\n\n // Check if all data points have zero count\n const hasAllZeroValues = riskScoreOverTimeData!.every(\n (point: RiskScoreOverTimeDataPoint) => point.count === 0,\n );\n\n // If all zero, send NotFound\n if (hasAllZeroValues) {\n return (\n <ChartBox title={title} tooltip={tooltip} width={width}>\n <NotFound />\n </ChartBox>\n );\n }\n\n // Calculate percentage change from API data (only when we have valid data)\n const calculatePercentageChange = (data: RiskDataPoint[]): number => {\n if (data.length <= 1) return 0;\n\n const firstValue = data[0].riskScore ?? 0;\n const lastValue = data[data.length - 1].riskScore ?? 0;\n const difference = lastValue - firstValue;\n\n // Avoid division by zero\n if (firstValue === 0) {\n return lastValue > 0 ? 100 : 0;\n }\n\n // Handle negative firstValue edge case\n if (firstValue < 0) {\n // If first value is negative and last is positive, return positive change\n if (lastValue > 0) return 100;\n // If both are negative, calculate relative change\n return (difference / Math.abs(firstValue)) * 100;\n }\n\n return (difference / firstValue) * 100;\n };\n\n const calculatedPercentageChange = calculatePercentageChange(finalData);\n\n // Determine colors based on percentage change\n const isNeutralChange = calculatedPercentageChange === 0;\n const isPositiveChange = calculatedPercentageChange > 0;\n\n // Set colors based on change type using theme-aware colors\n let lineColor: string;\n let indicatorColor: string;\n let indicatorTextColor: string;\n let TrendIcon: typeof TrendingUpIcon;\n\n if (isNeutralChange) {\n lineColor = trendColors.neutral.line;\n indicatorColor = trendColors.neutral.background;\n indicatorTextColor = trendColors.neutral.text;\n TrendIcon = RemoveIcon; // Minus/horizontal icon for no trend\n } else if (isPositiveChange) {\n lineColor = trendColors.positive.line;\n indicatorColor = trendColors.positive.background;\n indicatorTextColor = trendColors.positive.text;\n TrendIcon = TrendingUpIcon; // Up icon for positive\n } else {\n lineColor = trendColors.negative.line;\n indicatorColor = trendColors.negative.background;\n indicatorTextColor = trendColors.negative.text;\n TrendIcon = TrendingDownIcon; // Down icon for negative\n }\n\n // Calculate y-axis range with exactly 6 ticks; top tick equals data maximum\n const calculateYAxisRange = (data: RiskDataPoint[]) => {\n // Safety check: ensure we have data\n if (!data || data.length === 0) {\n return { yMin: 0, yMax: 100 };\n }\n\n const values = data\n .map(d => d.riskScore)\n .filter(v => !isNaN(v) && isFinite(v));\n\n // Safety check: ensure we have valid numeric values\n if (values.length === 0) {\n return { yMin: 0, yMax: 100 };\n }\n\n const minValue = Math.min(...values);\n const maxValue = Math.max(...values);\n\n // Edge case: both values are zero\n if (minValue === 0 && maxValue === 0) {\n return { yMin: 0, yMax: 10 };\n }\n\n // Edge case: maxValue is zero but minValue is not (shouldn't happen, but handle it)\n if (maxValue === 0 && minValue < 0) {\n return { yMin: minValue * 1.2, yMax: 10 };\n }\n\n // Calculate yMin: subtract 15% from minValue, but ensure it doesn't go below 0 for non-negative data\n // If minValue is negative, allow yMin to be negative\n let yMin: number;\n if (minValue >= 0) {\n yMin = Math.max(0, minValue - minValue * 0.15);\n } else {\n yMin = minValue - minValue * 0.15;\n }\n\n // Calculate yMax: add 1% to maxValue\n // Handle edge case where maxValue is very small (near zero)\n let yMax: number;\n if (maxValue === 0) {\n yMax = 10;\n } else {\n yMax = maxValue + maxValue * 0.01;\n }\n\n // Edge case: ensure yMin < yMax (shouldn't happen with current logic, but safety check)\n if (yMin >= yMax) {\n if (maxValue === 0) {\n return { yMin: 0, yMax: 10 };\n }\n // If they're equal or reversed, create a small range around maxValue\n yMin = Math.max(0, maxValue * 0.9);\n yMax = maxValue * 1.1;\n }\n\n return { yMin, yMax };\n };\n\n const { yMin, yMax } = calculateYAxisRange(finalData);\n\n // Build exactly 6 ticks between yMin and yMax (inclusive)\n const yTicks: number[] = (() => {\n const count = 6;\n const ticks: number[] = [];\n\n // Edge case: yMin and yMax are equal (or very close due to floating point)\n if (Math.abs(yMax - yMin) < Number.EPSILON) {\n for (let i = 0; i < count; i++) ticks.push(yMin);\n return ticks;\n }\n\n // Edge case: yMin > yMax (shouldn't happen, but handle it)\n if (yMin > yMax) {\n // Reverse the range\n const step = (yMin - yMax) / (count - 1);\n for (let i = 0; i < count; i++) {\n ticks.push(yMax + i * step);\n }\n return ticks.reverse();\n }\n\n // Normal case: calculate evenly spaced ticks\n const step = (yMax - yMin) / (count - 1);\n for (let i = 0; i < count; i++) {\n // Use toFixed and parseFloat to handle floating point precision issues\n const tickValue = yMin + i * step;\n // Round to reasonable precision (10 decimal places max)\n ticks.push(parseFloat(tickValue.toFixed(10)));\n }\n\n // Ensure the last tick is exactly yMax to avoid floating point drift\n ticks[count - 1] = yMax;\n\n return ticks;\n })();\n\n const series: LineChartSeries[] = [\n {\n id: 'riskScore',\n label: 'Risk score',\n data: finalData.map(point => ({\n x: point.date,\n y: point.riskScore,\n })),\n color: lineColor, // Dynamic color based on percentage change\n },\n ];\n\n const customHeader = (\n <HeaderContainer>\n <TitleContainer>\n <StyledTitle>{title}</StyledTitle>\n </TitleContainer>\n <PercentageContainer\n backgroundColor={indicatorColor}\n textColor={indicatorTextColor}\n >\n <TrendIcon sx={{ fontSize: '16px', color: indicatorTextColor }} />\n <Typography\n variant=\"body2\"\n sx={{ fontWeight: 500, color: indicatorTextColor }}\n >\n {calculatedPercentageChange > 0 ? '+' : ''}\n {calculatedPercentageChange.toFixed(1)}%\n </Typography>\n </PercentageContainer>\n </HeaderContainer>\n );\n\n return (\n <ChartBox\n title={title}\n tooltip={tooltip}\n width={width}\n customHeader={customHeader}\n >\n <LineChart\n series={series}\n width=\"100%\"\n height={300}\n showLegend\n legendPosition=\"bottom\"\n showGrid\n formatYValue={formatRiskScore}\n formatXValue={formatXAxisDate}\n formatTooltipXValue={formatTooltipDate}\n showDataPoints={false}\n customXSort={values => sortDateStrings(values as string[])}\n yAxisMin={yMin}\n yAxisMax={yMax}\n yAxisTicks={yTicks}\n />\n </ChartBox>\n );\n};\n\nexport default RiskOverTimeTile;\n"],"names":["yMin","yMax","step"],"mappings":"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"}
@@ -11,6 +11,7 @@ import 'react';
11
11
  import 'react-dom';
12
12
  import { NotFound } from '../common/NotFound.esm.js';
13
13
  import { SomethingWentWrong } from '../common/SomethingWentWrong.esm.js';
14
+ import '../common/StatusContainer.esm.js';
14
15
  import { LogoSpinner } from '../common/logoSpinner.esm.js';
15
16
 
16
17
  const SLAAdherenceTile = ({
@@ -19,7 +20,8 @@ const SLAAdherenceTile = ({
19
20
  width = "100%",
20
21
  height = "366px",
21
22
  repoId,
22
- entityRef
23
+ entityRef,
24
+ applicationId
23
25
  }) => {
24
26
  const theme = useTheme();
25
27
  const slaColors = getSlaColors(theme);
@@ -28,36 +30,37 @@ const SLAAdherenceTile = ({
28
30
  const { slaBreachData, slaBreachDataError, slaBreachDataLoading } = useSlaBreachData({
29
31
  connectApi: connectBackendApi,
30
32
  fetchApi: fetch,
31
- repositoryKey: repoId,
32
- entityRef
33
+ repositoryId: repoId,
34
+ entityRef,
35
+ applicationId
33
36
  });
34
- if (slaBreachDataLoading) {
37
+ if (!repoId && !applicationId) {
35
38
  return /* @__PURE__ */ jsx(ChartBox, { title, width, height, children: /* @__PURE__ */ jsx(
36
39
  Box,
37
40
  {
38
41
  display: "flex",
39
42
  justifyContent: "center",
40
43
  alignItems: "center",
41
- minHeight: "250px",
42
- children: /* @__PURE__ */ jsx(LogoSpinner, {})
44
+ minHeight: "300px",
45
+ children: /* @__PURE__ */ jsx(NotFound, { message: "Please configure the apiiro annotation to access the data." })
43
46
  }
44
47
  ) });
45
48
  }
46
- if (slaBreachDataError) {
47
- return /* @__PURE__ */ jsx(ChartBox, { title, width, children: /* @__PURE__ */ jsx(SomethingWentWrong, {}) });
48
- }
49
- if (!repoId) {
49
+ if (slaBreachDataLoading) {
50
50
  return /* @__PURE__ */ jsx(ChartBox, { title, width, height, children: /* @__PURE__ */ jsx(
51
51
  Box,
52
52
  {
53
53
  display: "flex",
54
54
  justifyContent: "center",
55
55
  alignItems: "center",
56
- minHeight: "300px",
57
- children: /* @__PURE__ */ jsx(NotFound, { message: "Please provide the repository details to access the data." })
56
+ minHeight: "250px",
57
+ children: /* @__PURE__ */ jsx(LogoSpinner, {})
58
58
  }
59
59
  ) });
60
60
  }
61
+ if (slaBreachDataError) {
62
+ return /* @__PURE__ */ jsx(ChartBox, { title, width, children: /* @__PURE__ */ jsx(SomethingWentWrong, {}) });
63
+ }
61
64
  const finalData = slaBreachData ? slaBreachData.map((point) => ({
62
65
  category: point.riskLevel,
63
66
  slaBreaches: point.slaBreach,
@@ -1 +1 @@
1
- {"version":3,"file":"SLAAdherenceTile.esm.js","sources":["../../../src/components/tiles/SLAAdherenceTile.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 Box from '@mui/material/Box';\nimport { useTheme } from '@mui/material/styles';\nimport { fetchApiRef, useApi } from '@backstage/core-plugin-api';\nimport { getSlaColors } from '../../theme/themeUtils';\nimport { ChartBox } from '../common/ChartBox';\nimport ColumnChart, { ColumnData } from '../charts/ColumnChart';\nimport { useSlaBreachData } from '../../queries/sla-breach.queries';\nimport { apiiroApiRef } from '../../api';\nimport { SlaBreachDataPoint } from '../../queries/queries.type';\nimport { NotFound, SomethingWentWrong } from '../common';\nimport { LogoSpinner } from '../common/logoSpinner';\n\nexport interface SLAAdherenceData {\n category: string;\n slaBreaches: number;\n slaAdherence: number;\n dueDateNotSet: number;\n}\n\nexport interface SLAAdherenceTileProps {\n title?: string;\n tooltip?: string;\n data?: SLAAdherenceData[];\n width?: string | number;\n height?: string | number;\n repoId?: string;\n entityRef?: string;\n}\n\nexport const SLAAdherenceTile = ({\n title = 'SLA adherence',\n data = [],\n width = '100%',\n height = '366px',\n repoId,\n entityRef,\n}: SLAAdherenceTileProps) => {\n // Use API hooks internally\n const theme = useTheme();\n const slaColors = getSlaColors(theme);\n const connectBackendApi = useApi(apiiroApiRef);\n const { fetch } = useApi(fetchApiRef);\n\n // Always call the hook, but conditionally use the result\n const { slaBreachData, slaBreachDataError, slaBreachDataLoading } =\n useSlaBreachData({\n connectApi: connectBackendApi,\n fetchApi: fetch,\n repositoryKey: repoId,\n entityRef: entityRef,\n });\n\n // Show loading state while data is loading (Query check - loading)\n if (slaBreachDataLoading) {\n return (\n <ChartBox title={title} width={width} height={height}>\n <Box\n display=\"flex\"\n justifyContent=\"center\"\n alignItems=\"center\"\n minHeight=\"250px\"\n >\n <LogoSpinner />\n </Box>\n </ChartBox>\n );\n }\n\n // Show error state if there's an error (Query check - error)\n if (slaBreachDataError) {\n return (\n <ChartBox title={title} width={width}>\n <SomethingWentWrong />\n </ChartBox>\n );\n }\n\n // Show message when no repository key is provided\n if (!repoId) {\n return (\n <ChartBox title={title} width={width} height={height}>\n <Box\n display=\"flex\"\n justifyContent=\"center\"\n alignItems=\"center\"\n minHeight=\"300px\"\n >\n <NotFound message=\"Please provide the repository details to access the data.\" />\n </Box>\n </ChartBox>\n );\n }\n\n // Transform API data to component data format\n const finalData: SLAAdherenceData[] = slaBreachData\n ? slaBreachData.map((point: SlaBreachDataPoint) => ({\n category: point.riskLevel,\n slaBreaches: point.slaBreach,\n slaAdherence: point.slaAdherence,\n dueDateNotSet: point.unsetDueDate,\n }))\n : data;\n\n // Total data length check\n if (finalData.length === 0) {\n return (\n <ChartBox title={title} width={width} height={height}>\n <NotFound />\n </ChartBox>\n );\n }\n\n // Check if all values are zero (slaBreach, slaAdherence, and unsetDueDate)\n const hasAllZeroValues = slaBreachData!.every(\n (point: SlaBreachDataPoint) =>\n point.slaBreach === 0 &&\n point.slaAdherence === 0 &&\n point.unsetDueDate === 0,\n );\n\n // If all zero, send NotFound\n if (hasAllZeroValues) {\n return (\n <ChartBox title={title} width={width} height={height}>\n <NotFound />\n </ChartBox>\n );\n }\n\n // Calculate chart width based on container\n const getChartWidth = () => {\n if (typeof width === 'string' && width.includes('%')) {\n // For percentage widths, assume a reasonable container width\n return 400; // This will be responsive within the container\n }\n return typeof width === 'number' ? width - 40 : 400; // Leave some padding\n };\n\n // Transform data for ColumnChart\n const columnData: ColumnData[] = finalData.map(item => ({\n category: item.category,\n values: [\n {\n label: 'SLA breaches',\n value: item.slaBreaches,\n color: slaColors.breach,\n },\n {\n label: 'SLA adherence',\n value: item.slaAdherence,\n color: slaColors.adherence,\n },\n {\n label: 'Due date not set',\n value: item.dueDateNotSet,\n color: slaColors.notSet,\n },\n ],\n }));\n\n return (\n <ChartBox title={title} width={width} height={height}>\n <ColumnChart\n data={columnData}\n width={getChartWidth()}\n height={300}\n showLegend\n legendPosition=\"bottom\"\n />\n </ChartBox>\n );\n};\n\nexport default SLAAdherenceTile;\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"SLAAdherenceTile.esm.js","sources":["../../../src/components/tiles/SLAAdherenceTile.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 Box from '@mui/material/Box';\nimport { useTheme } from '@mui/material/styles';\nimport { fetchApiRef, useApi } from '@backstage/core-plugin-api';\nimport { getSlaColors } from '../../theme/themeUtils';\nimport { ChartBox } from '../common/ChartBox';\nimport ColumnChart, { ColumnData } from '../charts/ColumnChart';\nimport { useSlaBreachData } from '../../queries/sla-breach.queries';\nimport { apiiroApiRef } from '../../api';\nimport { SlaBreachDataPoint } from '../../queries/queries.type';\nimport { NotFound, SomethingWentWrong } from '../common';\nimport { LogoSpinner } from '../common/logoSpinner';\n\nexport interface SLAAdherenceData {\n category: string;\n slaBreaches: number;\n slaAdherence: number;\n dueDateNotSet: number;\n}\n\nexport interface SLAAdherenceTileProps {\n title?: string;\n tooltip?: string;\n data?: SLAAdherenceData[];\n width?: string | number;\n height?: string | number;\n repoId?: string;\n entityRef?: string;\n applicationId?: string;\n}\n\nexport const SLAAdherenceTile = ({\n title = 'SLA adherence',\n data = [],\n width = '100%',\n height = '366px',\n repoId,\n entityRef,\n applicationId,\n}: SLAAdherenceTileProps) => {\n // Use API hooks internally\n const theme = useTheme();\n const slaColors = getSlaColors(theme);\n const connectBackendApi = useApi(apiiroApiRef);\n const { fetch } = useApi(fetchApiRef);\n\n const { slaBreachData, slaBreachDataError, slaBreachDataLoading } =\n useSlaBreachData({\n connectApi: connectBackendApi,\n fetchApi: fetch,\n repositoryId: repoId,\n entityRef: entityRef,\n applicationId: applicationId,\n });\n\n // Show message when repository key or application key is not provided\n if (!repoId && !applicationId) {\n return (\n <ChartBox title={title} width={width} height={height}>\n <Box\n display=\"flex\"\n justifyContent=\"center\"\n alignItems=\"center\"\n minHeight=\"300px\"\n >\n <NotFound message=\"Please configure the apiiro annotation to access the data.\" />\n </Box>\n </ChartBox>\n );\n }\n\n // Show loading state while data is loading (Query check - loading)\n if (slaBreachDataLoading) {\n return (\n <ChartBox title={title} width={width} height={height}>\n <Box\n display=\"flex\"\n justifyContent=\"center\"\n alignItems=\"center\"\n minHeight=\"250px\"\n >\n <LogoSpinner />\n </Box>\n </ChartBox>\n );\n }\n\n // Show error state if there's an error (Query check - error)\n if (slaBreachDataError) {\n return (\n <ChartBox title={title} width={width}>\n <SomethingWentWrong />\n </ChartBox>\n );\n }\n\n // Transform API data to component data format\n const finalData: SLAAdherenceData[] = slaBreachData\n ? slaBreachData.map((point: SlaBreachDataPoint) => ({\n category: point.riskLevel,\n slaBreaches: point.slaBreach,\n slaAdherence: point.slaAdherence,\n dueDateNotSet: point.unsetDueDate,\n }))\n : data;\n\n // Total data length check\n if (finalData.length === 0) {\n return (\n <ChartBox title={title} width={width} height={height}>\n <NotFound />\n </ChartBox>\n );\n }\n\n // Check if all values are zero (slaBreach, slaAdherence, and unsetDueDate)\n const hasAllZeroValues = slaBreachData!.every(\n (point: SlaBreachDataPoint) =>\n point.slaBreach === 0 &&\n point.slaAdherence === 0 &&\n point.unsetDueDate === 0,\n );\n\n // If all zero, send NotFound\n if (hasAllZeroValues) {\n return (\n <ChartBox title={title} width={width} height={height}>\n <NotFound />\n </ChartBox>\n );\n }\n\n // Calculate chart width based on container\n const getChartWidth = () => {\n if (typeof width === 'string' && width.includes('%')) {\n // For percentage widths, assume a reasonable container width\n return 400; // This will be responsive within the container\n }\n return typeof width === 'number' ? width - 40 : 400; // Leave some padding\n };\n\n // Transform data for ColumnChart\n const columnData: ColumnData[] = finalData.map(item => ({\n category: item.category,\n values: [\n {\n label: 'SLA breaches',\n value: item.slaBreaches,\n color: slaColors.breach,\n },\n {\n label: 'SLA adherence',\n value: item.slaAdherence,\n color: slaColors.adherence,\n },\n {\n label: 'Due date not set',\n value: item.dueDateNotSet,\n color: slaColors.notSet,\n },\n ],\n }));\n\n return (\n <ChartBox title={title} width={width} height={height}>\n <ColumnChart\n data={columnData}\n width={getChartWidth()}\n height={300}\n showLegend\n legendPosition=\"bottom\"\n />\n </ChartBox>\n );\n};\n\nexport default SLAAdherenceTile;\n"],"names":[],"mappings":"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"}