@backstage-community/plugin-apiiro 0.1.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 (223) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +232 -0
  3. package/config.d.ts +30 -0
  4. package/dist/App.esm.js +12 -0
  5. package/dist/App.esm.js.map +1 -0
  6. package/dist/api/index.esm.js +71 -0
  7. package/dist/api/index.esm.js.map +1 -0
  8. package/dist/assets/BulleyeIcon.esm.js +454 -0
  9. package/dist/assets/BulleyeIcon.esm.js.map +1 -0
  10. package/dist/assets/NoResultIcon.esm.js +146 -0
  11. package/dist/assets/NoResultIcon.esm.js.map +1 -0
  12. package/dist/assets/RiskIcon.esm.js +27 -0
  13. package/dist/assets/RiskIcon.esm.js.map +1 -0
  14. package/dist/assets/SettingIcon.esm.js +49 -0
  15. package/dist/assets/SettingIcon.esm.js.map +1 -0
  16. package/dist/assets/apiiroLogo/apiiroLogo.esm.js +21 -0
  17. package/dist/assets/apiiroLogo/apiiroLogo.esm.js.map +1 -0
  18. package/dist/assets/apiiroLogo/apiiroSidebar.esm.js +23 -0
  19. package/dist/assets/apiiroLogo/apiiroSidebar.esm.js.map +1 -0
  20. package/dist/assets/apiiroLogo/apiiroSmall.esm.js +19 -0
  21. package/dist/assets/apiiroLogo/apiiroSmall.esm.js.map +1 -0
  22. package/dist/assets/languageIcons/C.esm.js +7 -0
  23. package/dist/assets/languageIcons/C.esm.js.map +1 -0
  24. package/dist/assets/languageIcons/Cicd.esm.js +7 -0
  25. package/dist/assets/languageIcons/Cicd.esm.js.map +1 -0
  26. package/dist/assets/languageIcons/Clojure.esm.js +7 -0
  27. package/dist/assets/languageIcons/Clojure.esm.js.map +1 -0
  28. package/dist/assets/languageIcons/Cpp.esm.js +7 -0
  29. package/dist/assets/languageIcons/Cpp.esm.js.map +1 -0
  30. package/dist/assets/languageIcons/Csharp.esm.js +7 -0
  31. package/dist/assets/languageIcons/Csharp.esm.js.map +1 -0
  32. package/dist/assets/languageIcons/Dart.esm.js +7 -0
  33. package/dist/assets/languageIcons/Dart.esm.js.map +1 -0
  34. package/dist/assets/languageIcons/Go.esm.js +7 -0
  35. package/dist/assets/languageIcons/Go.esm.js.map +1 -0
  36. package/dist/assets/languageIcons/Groovy.esm.js +7 -0
  37. package/dist/assets/languageIcons/Groovy.esm.js.map +1 -0
  38. package/dist/assets/languageIcons/HTML.esm.js +7 -0
  39. package/dist/assets/languageIcons/HTML.esm.js.map +1 -0
  40. package/dist/assets/languageIcons/HclLanguage.esm.js +7 -0
  41. package/dist/assets/languageIcons/HclLanguage.esm.js.map +1 -0
  42. package/dist/assets/languageIcons/Java.esm.js +7 -0
  43. package/dist/assets/languageIcons/Java.esm.js.map +1 -0
  44. package/dist/assets/languageIcons/Javascript.esm.js +7 -0
  45. package/dist/assets/languageIcons/Javascript.esm.js.map +1 -0
  46. package/dist/assets/languageIcons/Kotlin.esm.js +7 -0
  47. package/dist/assets/languageIcons/Kotlin.esm.js.map +1 -0
  48. package/dist/assets/languageIcons/ObjectiveC.esm.js +7 -0
  49. package/dist/assets/languageIcons/ObjectiveC.esm.js.map +1 -0
  50. package/dist/assets/languageIcons/PHP.esm.js +7 -0
  51. package/dist/assets/languageIcons/PHP.esm.js.map +1 -0
  52. package/dist/assets/languageIcons/Perl.esm.js +13 -0
  53. package/dist/assets/languageIcons/Perl.esm.js.map +1 -0
  54. package/dist/assets/languageIcons/Python.esm.js +7 -0
  55. package/dist/assets/languageIcons/Python.esm.js.map +1 -0
  56. package/dist/assets/languageIcons/Ruby.esm.js +7 -0
  57. package/dist/assets/languageIcons/Ruby.esm.js.map +1 -0
  58. package/dist/assets/languageIcons/Rust.esm.js +7 -0
  59. package/dist/assets/languageIcons/Rust.esm.js.map +1 -0
  60. package/dist/assets/languageIcons/Scala.esm.js +7 -0
  61. package/dist/assets/languageIcons/Scala.esm.js.map +1 -0
  62. package/dist/assets/languageIcons/Swift.esm.js +7 -0
  63. package/dist/assets/languageIcons/Swift.esm.js.map +1 -0
  64. package/dist/assets/languageIcons/Terraform.esm.js +7 -0
  65. package/dist/assets/languageIcons/Terraform.esm.js.map +1 -0
  66. package/dist/assets/languageIcons/Typescript.esm.js +7 -0
  67. package/dist/assets/languageIcons/Typescript.esm.js.map +1 -0
  68. package/dist/assets/languageIcons/Unknown.esm.js +7 -0
  69. package/dist/assets/languageIcons/Unknown.esm.js.map +1 -0
  70. package/dist/assets/languageIcons/VB.esm.js +10 -0
  71. package/dist/assets/languageIcons/VB.esm.js.map +1 -0
  72. package/dist/assets/languageIcons/YAML.esm.js +7 -0
  73. package/dist/assets/languageIcons/YAML.esm.js.map +1 -0
  74. package/dist/assets/providerIcons/Azure.esm.js +7 -0
  75. package/dist/assets/providerIcons/Azure.esm.js.map +1 -0
  76. package/dist/assets/providerIcons/Bitbucket.esm.js +7 -0
  77. package/dist/assets/providerIcons/Bitbucket.esm.js.map +1 -0
  78. package/dist/assets/providerIcons/Gitlab.esm.js +7 -0
  79. package/dist/assets/providerIcons/Gitlab.esm.js.map +1 -0
  80. package/dist/components/ApiiroSidebar.esm.js +10 -0
  81. package/dist/components/ApiiroSidebar.esm.js.map +1 -0
  82. package/dist/components/ApplicationsList/ApplicationsList.esm.js +196 -0
  83. package/dist/components/ApplicationsList/ApplicationsList.esm.js.map +1 -0
  84. package/dist/components/CalendarDatePicker.esm.js +154 -0
  85. package/dist/components/CalendarDatePicker.esm.js.map +1 -0
  86. package/dist/components/CalendarDatePicker.styles.esm.js +198 -0
  87. package/dist/components/CalendarDatePicker.styles.esm.js.map +1 -0
  88. package/dist/components/Chip.esm.js +60 -0
  89. package/dist/components/Chip.esm.js.map +1 -0
  90. package/dist/components/ChipsList.esm.js +207 -0
  91. package/dist/components/ChipsList.esm.js.map +1 -0
  92. package/dist/components/ComponentDisplay.esm.js +42 -0
  93. package/dist/components/ComponentDisplay.esm.js.map +1 -0
  94. package/dist/components/DataGrid/CustomColumnMenu.esm.js +29 -0
  95. package/dist/components/DataGrid/CustomColumnMenu.esm.js.map +1 -0
  96. package/dist/components/DataGrid/CustomPagination.esm.js +113 -0
  97. package/dist/components/DataGrid/CustomPagination.esm.js.map +1 -0
  98. package/dist/components/DataGrid/CustomSearchToolbar.esm.js +117 -0
  99. package/dist/components/DataGrid/CustomSearchToolbar.esm.js.map +1 -0
  100. package/dist/components/DataGrid/DataGrid.esm.js +336 -0
  101. package/dist/components/DataGrid/DataGrid.esm.js.map +1 -0
  102. package/dist/components/DataGrid/PinColumnMenuItem.esm.js +24 -0
  103. package/dist/components/DataGrid/PinColumnMenuItem.esm.js.map +1 -0
  104. package/dist/components/DueDate.esm.js +34 -0
  105. package/dist/components/DueDate.esm.js.map +1 -0
  106. package/dist/components/Header.esm.js +27 -0
  107. package/dist/components/Header.esm.js.map +1 -0
  108. package/dist/components/MainContributors/MainContributors.esm.js +62 -0
  109. package/dist/components/MainContributors/MainContributors.esm.js.map +1 -0
  110. package/dist/components/MetricsGroup/TabMetricsGroup.esm.js +37 -0
  111. package/dist/components/MetricsGroup/TabMetricsGroup.esm.js.map +1 -0
  112. package/dist/components/MetricsGroup/WidgetMetricsGroup.esm.js +36 -0
  113. package/dist/components/MetricsGroup/WidgetMetricsGroup.esm.js.map +1 -0
  114. package/dist/components/RepositoryDisplay/RepositoryDisplay.esm.js +121 -0
  115. package/dist/components/RepositoryDisplay/RepositoryDisplay.esm.js.map +1 -0
  116. package/dist/components/RiskLevel.esm.js +88 -0
  117. package/dist/components/RiskLevel.esm.js.map +1 -0
  118. package/dist/components/RiskStatus.esm.js +58 -0
  119. package/dist/components/RiskStatus.esm.js.map +1 -0
  120. package/dist/components/SimpleTooltip.esm.js +24 -0
  121. package/dist/components/SimpleTooltip.esm.js.map +1 -0
  122. package/dist/components/SourcesDisplay.esm.js +47 -0
  123. package/dist/components/SourcesDisplay.esm.js.map +1 -0
  124. package/dist/components/TagsList/TagsList.esm.js +38 -0
  125. package/dist/components/TagsList/TagsList.esm.js.map +1 -0
  126. package/dist/components/TeamsDisplay.esm.js +47 -0
  127. package/dist/components/TeamsDisplay.esm.js.map +1 -0
  128. package/dist/components/charts/ColumnChart.esm.js +402 -0
  129. package/dist/components/charts/ColumnChart.esm.js.map +1 -0
  130. package/dist/components/charts/GaugeChart.esm.js +249 -0
  131. package/dist/components/charts/GaugeChart.esm.js.map +1 -0
  132. package/dist/components/charts/LineChart.esm.js +328 -0
  133. package/dist/components/charts/LineChart.esm.js.map +1 -0
  134. package/dist/components/charts/PieChart.esm.js +233 -0
  135. package/dist/components/charts/PieChart.esm.js.map +1 -0
  136. package/dist/components/common/ChartBox.esm.js +88 -0
  137. package/dist/components/common/ChartBox.esm.js.map +1 -0
  138. package/dist/components/common/CustomTooltip.esm.js +255 -0
  139. package/dist/components/common/CustomTooltip.esm.js.map +1 -0
  140. package/dist/components/common/ErrorSnackbar.esm.js +39 -0
  141. package/dist/components/common/ErrorSnackbar.esm.js.map +1 -0
  142. package/dist/components/common/NotFound.esm.js +30 -0
  143. package/dist/components/common/NotFound.esm.js.map +1 -0
  144. package/dist/components/common/SomethingWentWrong.esm.js +35 -0
  145. package/dist/components/common/SomethingWentWrong.esm.js.map +1 -0
  146. package/dist/components/common/languageIcons.esm.js +61 -0
  147. package/dist/components/common/languageIcons.esm.js.map +1 -0
  148. package/dist/components/common/logoSpinner.esm.js +28 -0
  149. package/dist/components/common/logoSpinner.esm.js.map +1 -0
  150. package/dist/components/common/scmProviders.esm.js +41 -0
  151. package/dist/components/common/scmProviders.esm.js.map +1 -0
  152. package/dist/components/filters/DiscoveredOnFilter.esm.js +284 -0
  153. package/dist/components/filters/DiscoveredOnFilter.esm.js.map +1 -0
  154. package/dist/components/filters/FilterDropdown.esm.js +325 -0
  155. package/dist/components/filters/FilterDropdown.esm.js.map +1 -0
  156. package/dist/components/filters/FilterDropdownClear.esm.js +45 -0
  157. package/dist/components/filters/FilterDropdownClear.esm.js.map +1 -0
  158. package/dist/components/filters/FilterDropdownList.esm.js +102 -0
  159. package/dist/components/filters/FilterDropdownList.esm.js.map +1 -0
  160. package/dist/components/filters/FilterDropdownSearch.esm.js +65 -0
  161. package/dist/components/filters/FilterDropdownSearch.esm.js.map +1 -0
  162. package/dist/components/filters/RiskInsightFilter.esm.js +579 -0
  163. package/dist/components/filters/RiskInsightFilter.esm.js.map +1 -0
  164. package/dist/components/tiles/MttrVsSLATile.esm.js +170 -0
  165. package/dist/components/tiles/MttrVsSLATile.esm.js.map +1 -0
  166. package/dist/components/tiles/RiskOverTimeTile.esm.js +311 -0
  167. package/dist/components/tiles/RiskOverTimeTile.esm.js.map +1 -0
  168. package/dist/components/tiles/SLAAdherenceTile.esm.js +115 -0
  169. package/dist/components/tiles/SLAAdherenceTile.esm.js.map +1 -0
  170. package/dist/components/tiles/StatusTile.esm.js +235 -0
  171. package/dist/components/tiles/StatusTile.esm.js.map +1 -0
  172. package/dist/components/tiles/TopLanguagesTile.esm.js +234 -0
  173. package/dist/components/tiles/TopLanguagesTile.esm.js.map +1 -0
  174. package/dist/components/tiles/TopRiskTile.esm.js +208 -0
  175. package/dist/components/tiles/TopRiskTile.esm.js.map +1 -0
  176. package/dist/hooks/useUrlFilters.esm.js +102 -0
  177. package/dist/hooks/useUrlFilters.esm.js.map +1 -0
  178. package/dist/index.d.ts +28 -0
  179. package/dist/index.esm.js +42 -0
  180. package/dist/index.esm.js.map +1 -0
  181. package/dist/pages/Repositories/Repositories.esm.js +102 -0
  182. package/dist/pages/Repositories/Repositories.esm.js.map +1 -0
  183. package/dist/pages/Repositories/tableConfig.esm.js +294 -0
  184. package/dist/pages/Repositories/tableConfig.esm.js.map +1 -0
  185. package/dist/pages/Risks/Risks.esm.js +258 -0
  186. package/dist/pages/Risks/Risks.esm.js.map +1 -0
  187. package/dist/pages/Risks/tableConfig.esm.js +305 -0
  188. package/dist/pages/Risks/tableConfig.esm.js.map +1 -0
  189. package/dist/pages/tab/Tab.esm.js +147 -0
  190. package/dist/pages/tab/Tab.esm.js.map +1 -0
  191. package/dist/pages/tab/TabProvider.esm.js +11 -0
  192. package/dist/pages/tab/TabProvider.esm.js.map +1 -0
  193. package/dist/pages/widget/Widget.esm.js +161 -0
  194. package/dist/pages/widget/Widget.esm.js.map +1 -0
  195. package/dist/pages/widget/WidgetProvider.esm.js +12 -0
  196. package/dist/pages/widget/WidgetProvider.esm.js.map +1 -0
  197. package/dist/plugin.esm.js +30 -0
  198. package/dist/plugin.esm.js.map +1 -0
  199. package/dist/queries/filterOptions.queries.esm.js +46 -0
  200. package/dist/queries/filterOptions.queries.esm.js.map +1 -0
  201. package/dist/queries/mttr-statistics.queries.esm.js +61 -0
  202. package/dist/queries/mttr-statistics.queries.esm.js.map +1 -0
  203. package/dist/queries/repository.queries.esm.js +60 -0
  204. package/dist/queries/repository.queries.esm.js.map +1 -0
  205. package/dist/queries/risk-score-over-time.queries.esm.js +61 -0
  206. package/dist/queries/risk-score-over-time.queries.esm.js.map +1 -0
  207. package/dist/queries/risks.queries.esm.js +65 -0
  208. package/dist/queries/risks.queries.esm.js.map +1 -0
  209. package/dist/queries/sla-breach.queries.esm.js +57 -0
  210. package/dist/queries/sla-breach.queries.esm.js.map +1 -0
  211. package/dist/queries/top-risks.queries.esm.js +47 -0
  212. package/dist/queries/top-risks.queries.esm.js.map +1 -0
  213. package/dist/routes.esm.js +8 -0
  214. package/dist/routes.esm.js.map +1 -0
  215. package/dist/theme/themeUtils.esm.js +290 -0
  216. package/dist/theme/themeUtils.esm.js.map +1 -0
  217. package/dist/utils/dateFormatter.esm.js +67 -0
  218. package/dist/utils/dateFormatter.esm.js.map +1 -0
  219. package/dist/utils/numberFormatter.esm.js +21 -0
  220. package/dist/utils/numberFormatter.esm.js.map +1 -0
  221. package/dist/utils/utils.esm.js +27 -0
  222. package/dist/utils/utils.esm.js.map +1 -0
  223. package/package.json +90 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RiskInsightFilter.esm.js","sources":["../../../src/components/filters/RiskInsightFilter.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, useState } from 'react';\nimport Box from '@mui/material/Box';\nimport ButtonBase from '@mui/material/ButtonBase';\nimport Popper from '@mui/material/Popper';\nimport Paper from '@mui/material/Paper';\nimport ClickAwayListener from '@mui/material/ClickAwayListener';\nimport Typography from '@mui/material/Typography';\nimport Checkbox from '@mui/material/Checkbox';\nimport Chip from '@mui/material/Chip';\nimport IconButton from '@mui/material/IconButton';\nimport Collapse from '@mui/material/Collapse';\nimport Divider from '@mui/material/Divider';\nimport Link from '@mui/material/Link';\nimport List from '@mui/material/List';\nimport ListItemButton from '@mui/material/ListItemButton';\nimport ListItemIcon from '@mui/material/ListItemIcon';\nimport ListItemText from '@mui/material/ListItemText';\nimport Skeleton from '@mui/material/Skeleton';\nimport { SimpleTooltip } from '../SimpleTooltip';\nimport KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown';\nimport KeyboardArrowRightIcon from '@mui/icons-material/KeyboardArrowRight';\nimport InfoOutlinedIcon from '@mui/icons-material/InfoOutlined';\nimport { alpha, styled, Theme, useTheme } from '@mui/material/styles';\nimport type {\n RiskInsightFilterProps,\n RiskInsightOption,\n} from './RiskInsightFilter.types';\nimport { FilterDropdownSearch } from './FilterDropdownSearch';\nimport { FilterDropdownClear } from './FilterDropdownClear';\nimport { getCountBadgeColors } from '../../theme/themeUtils';\n\nconst GROUP_DISPLAY_LIMIT = 5;\nconst GROUP_OPTION_LIMIT = 4;\n\nconst DropdownButton = styled(ButtonBase, {\n shouldForwardProp: prop => prop !== 'open' && prop !== 'hasSelection',\n})<{ open: boolean; hasSelection: boolean }>(\n ({ theme, open, hasSelection }) => {\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 buttonBackground = theme.palette.background.paper;\n if (open) {\n buttonBackground = hoverBackground;\n } else if (hasSelection) {\n buttonBackground = selectedBackground;\n }\n\n let borderColor = closedBorder;\n if (open) {\n borderColor = primaryMain;\n } else if (hasSelection) {\n borderColor = selectedBorder;\n }\n\n let boxShadow = 'none';\n if (open) {\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 let hoverColor = hoverBackground;\n if (hasSelection) {\n hoverColor = alpha(primaryMain, isDark ? 0.24 : 0.1);\n }\n\n return {\n display: 'flex',\n alignItems: 'center',\n gap: theme.spacing(1),\n padding: theme.spacing(0.75, 1.5),\n borderRadius: 999,\n border: `1px solid ${borderColor}`,\n backgroundColor: buttonBackground,\n transition: 'all 0.2s ease',\n boxShadow,\n '&:hover': {\n backgroundColor: hoverColor,\n },\n '& .MuiSvgIcon-root': {\n fontSize: 18,\n color: hasSelection\n ? theme.palette.primary.main\n : theme.palette.text.secondary,\n },\n };\n },\n);\n\nconst sentimentPalette = (theme: Theme, sentiment: string) => {\n const normalized = (sentiment || '').toLowerCase();\n if (normalized === 'positive') {\n return {\n color: theme.palette.success.main,\n background: alpha(theme.palette.success.main, 0.12),\n };\n }\n if (normalized === 'negative') {\n return {\n color: theme.palette.error.main,\n background: alpha(theme.palette.error.main, 0.12),\n };\n }\n if (normalized === 'neutral') {\n return {\n color: theme.palette.info.main,\n background: alpha(theme.palette.info.main, 0.12),\n };\n }\n return {\n color: theme.palette.text.secondary,\n background: alpha(theme.palette.text.secondary, 0.15),\n };\n};\n\nconst sortByPriority = (options: RiskInsightOption[]) => {\n return [...options].sort((a, b) => {\n const aOrder = a.sortOrder ?? Number.MAX_SAFE_INTEGER;\n const bOrder = b.sortOrder ?? Number.MAX_SAFE_INTEGER;\n if (aOrder !== bOrder) {\n return aOrder - bOrder;\n }\n return a.displayName.localeCompare(b.displayName);\n });\n};\n\nconst groupOptions = (options: RiskInsightOption[]) => {\n const groups = new Map<string, RiskInsightOption[]>();\n options.forEach(option => {\n const list = groups.get(option.group) ?? [];\n list.push(option);\n groups.set(option.group, list);\n });\n\n return [...groups.entries()]\n .map(([groupName, groupItems]) => ({\n groupName,\n groupOrder: groupItems[0]?.groupOrder ?? Number.MAX_SAFE_INTEGER,\n options: sortByPriority(groupItems),\n }))\n .sort((a, b) => {\n if (a.groupOrder !== b.groupOrder) {\n return (\n (a.groupOrder ?? Number.MAX_SAFE_INTEGER) -\n (b.groupOrder ?? Number.MAX_SAFE_INTEGER)\n );\n }\n return a.groupName.localeCompare(b.groupName);\n });\n};\n\nexport const RiskInsightFilter = ({\n label,\n options,\n selectedValues,\n onChange,\n loading = false,\n clearLabel = 'Clear selection',\n}: RiskInsightFilterProps) => {\n const theme = useTheme();\n const [anchorEl, setAnchorEl] = useState<HTMLElement | null>(null);\n const [search, setSearch] = useState('');\n const [showAll, setShowAll] = useState(false);\n const [expandedGroups, setExpandedGroups] = useState<Record<string, boolean>>(\n {},\n );\n const [expandedOptions, setExpandedOptions] = useState<\n Record<string, boolean>\n >({});\n\n const isOpen = Boolean(anchorEl);\n const hasSelection = selectedValues.length > 0;\n\n const groupedOptions = useMemo(() => groupOptions(options), [options]);\n\n const filteredGroups = useMemo(() => {\n if (!search.trim()) {\n return groupedOptions;\n }\n const lower = search.trim().toLowerCase();\n return groupedOptions\n .map(group => ({\n ...group,\n options: group.options.filter(\n option =>\n option.displayName.toLowerCase().includes(lower) ||\n option.group.toLowerCase().includes(lower),\n ),\n }))\n .filter(group => group.options.length > 0);\n }, [groupedOptions, search]);\n\n const hasMoreGroups = filteredGroups.length > GROUP_DISPLAY_LIMIT;\n const visibleGroups =\n showAll || search\n ? filteredGroups\n : filteredGroups.slice(0, GROUP_DISPLAY_LIMIT);\n\n const toggleGroup = (groupName: string) => {\n if (search) return; // keep groups expanded during search\n setExpandedGroups(prev => ({\n ...prev,\n [groupName]: !prev[groupName],\n }));\n };\n\n const toggleOptions = (groupName: string) => {\n setExpandedOptions(prev => ({\n ...prev,\n [groupName]: !prev[groupName],\n }));\n };\n\n const toggleOption = (name: string) => {\n const set = new Set(selectedValues);\n if (set.has(name)) {\n set.delete(name);\n } else {\n set.add(name);\n }\n onChange([...set]);\n };\n\n const handleToggle = (event: MouseEvent<HTMLElement>) => {\n if (anchorEl) {\n setAnchorEl(null);\n return;\n }\n setAnchorEl(event.currentTarget);\n };\n\n const handleClose = () => {\n setAnchorEl(null);\n };\n\n const handleClear = () => {\n if (!selectedValues.length) {\n return;\n }\n setSearch('');\n onChange([]);\n };\n\n const selectedChipLabel = (() => {\n if (!hasSelection) {\n return label;\n }\n const firstSelectedName = selectedValues[0];\n const firstSelected = options.find(\n option => option.name === firstSelectedName,\n );\n const firstLabel = firstSelected?.displayName ?? firstSelectedName;\n const remainingCount = selectedValues.length - 1;\n if (remainingCount > 0) {\n return `${label}: ${firstLabel} (+${remainingCount})`;\n }\n return `${label}: ${firstLabel}`;\n })();\n\n const additionalCount =\n selectedValues.length > 1 ? selectedValues.length - 1 : 0;\n\n const remainingItemsTooltip = useMemo(() => {\n if (selectedValues.length <= 1) {\n return '';\n }\n const remainingItems = selectedValues.slice(1).map(value => {\n const option = options.find(opt => opt.name === value);\n return option?.displayName ?? value;\n });\n return remainingItems.join(', ');\n }, [selectedValues, options]);\n\n if (loading) {\n return (\n <Skeleton\n variant=\"rounded\"\n width={120}\n height={36}\n sx={{\n borderRadius: 999,\n }}\n />\n );\n }\n\n return (\n <>\n <DropdownButton\n open={isOpen}\n hasSelection={hasSelection}\n onClick={handleToggle}\n >\n <Typography\n variant=\"body2\"\n sx={{\n color: theme.palette.text.primary,\n whiteSpace: 'nowrap',\n }}\n >\n {selectedChipLabel.replace(/ \\(\\+\\d+\\)$/, '')}\n </Typography>\n {additionalCount > 0 && (\n <SimpleTooltip\n title={remainingItemsTooltip}\n placement=\"top\"\n tooltipProps={{\n componentsProps: {\n tooltip: {\n sx: {\n whiteSpace: 'normal',\n maxWidth: '400px',\n },\n },\n },\n }}\n >\n <Box\n sx={{\n display: 'inline-flex',\n alignItems: 'center',\n justifyContent: 'center',\n minWidth: 26,\n px: 1,\n py: 0.25,\n borderRadius: 999,\n backgroundColor: getCountBadgeColors(theme).background,\n color: getCountBadgeColors(theme).text,\n fontSize: 12,\n fontWeight: 600,\n }}\n >\n {`+${additionalCount}`}\n </Box>\n </SimpleTooltip>\n )}\n <KeyboardArrowDownIcon\n fontSize=\"small\"\n sx={{\n transform: isOpen ? 'rotate(180deg)' : 'none',\n transition: 'transform 0.2s ease',\n color: theme.palette.text.secondary,\n }}\n />\n </DropdownButton>\n\n <Popper\n open={isOpen}\n anchorEl={anchorEl}\n placement=\"bottom-start\"\n modifiers={[{ name: 'offset', options: { offset: [0, 8] } }]}\n >\n <ClickAwayListener onClickAway={handleClose}>\n <Paper\n sx={{\n borderRadius: 3,\n boxShadow: '0 16px 32px rgba(31, 45, 98, 0.18)',\n width: 320,\n display: 'flex',\n flexDirection: 'column',\n overflow: 'hidden',\n backgroundColor: theme.palette.background.paper,\n border: `1px solid ${alpha(theme.palette.common.black, 0.08)}`,\n }}\n >\n <Box\n sx={{\n p: 2,\n pb: 1,\n }}\n >\n <FilterDropdownSearch\n value={search}\n onChange={setSearch}\n placeholder=\"Search...\"\n />\n </Box>\n\n <Box sx={{ px: 1, pt: 1, pb: 1.5 }}>\n {filteredGroups.length === 0 ? (\n <Typography\n variant=\"body2\"\n sx={{ color: theme.palette.text.secondary, py: 1 }}\n >\n No insights found\n </Typography>\n ) : (\n <>\n <Box sx={{ maxHeight: 280, overflowY: 'auto' }}>\n {visibleGroups.map(group => {\n const isExpanded = search\n ? true\n : expandedGroups[group.groupName] ?? false;\n const isOptionExpanded =\n expandedOptions[group.groupName] ?? false;\n const optionsToRender =\n isOptionExpanded || search\n ? group.options\n : group.options.slice(0, GROUP_OPTION_LIMIT);\n const hasMoreOptions =\n group.options.length > GROUP_OPTION_LIMIT;\n return (\n <Box key={group.groupName}>\n <ListItemButton\n disableRipple\n onClick={() => toggleGroup(group.groupName)}\n sx={{\n display: 'flex',\n justifyContent: 'space-between',\n alignItems: 'center',\n '&:hover': {\n backgroundColor: alpha(\n theme.palette.primary.main,\n 0.04,\n ),\n },\n }}\n >\n <ListItemText\n primaryTypographyProps={{\n fontSize: 12,\n letterSpacing: 0.6,\n color: alpha(theme.palette.text.primary, 0.85),\n textTransform: 'uppercase',\n }}\n primary={`${group.groupName} (${group.options.length})`}\n />\n <KeyboardArrowRightIcon\n sx={{\n transition: 'transform 0.2s ease',\n transform: isExpanded\n ? 'rotate(90deg)'\n : 'none',\n color: alpha(theme.palette.text.primary, 0.6),\n }}\n />\n </ListItemButton>\n <Collapse\n in={isExpanded}\n timeout=\"auto\"\n unmountOnExit\n >\n <List disablePadding sx={{ px: 2, py: 1 }}>\n {optionsToRender.map(option => {\n const palette = sentimentPalette(\n theme,\n option.sentiment,\n );\n return (\n <ListItemButton\n key={option.name}\n onClick={() => toggleOption(option.name)}\n sx={{\n display: 'flex',\n py: 0,\n borderRadius: 2,\n alignItems: 'center',\n justifyContent: 'space-between',\n width: '100%',\n }}\n dense\n >\n <Box\n sx={{\n display: 'flex',\n alignItems: 'center',\n flex: 1,\n minWidth: 0,\n }}\n >\n <ListItemIcon sx={{ minWidth: 32 }}>\n <Checkbox\n edge=\"start\"\n tabIndex={-1}\n disableRipple\n checked={selectedValues.includes(\n option.name,\n )}\n />\n </ListItemIcon>\n <Chip\n label={option.displayName}\n size=\"small\"\n sx={{\n mb: 0,\n fontWeight: 600,\n color: palette.color,\n backgroundColor: palette.background,\n borderRadius: 2,\n maxWidth: '100%',\n '& .MuiChip-label': {\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n whiteSpace: 'nowrap',\n },\n }}\n />\n </Box>\n <SimpleTooltip\n title={\n option.description || 'No description'\n }\n placement=\"top\"\n tooltipProps={{\n componentsProps: {\n tooltip: {\n sx: {\n whiteSpace: 'pre-wrap',\n maxWidth: '300px',\n wordBreak: 'break-word',\n },\n },\n },\n }}\n >\n <IconButton\n size=\"small\"\n sx={{ ml: 0.5, flexShrink: 0 }}\n >\n <InfoOutlinedIcon fontSize=\"small\" />\n </IconButton>\n </SimpleTooltip>\n </ListItemButton>\n );\n })}\n </List>\n {hasMoreOptions && !search && (\n <Link\n component=\"button\"\n type=\"button\"\n underline=\"hover\"\n onClick={() => toggleOptions(group.groupName)}\n sx={{\n mx: 2,\n mb: 1,\n display: 'inline-flex',\n fontSize: 13,\n fontWeight: 500,\n color: theme.palette.primary.main,\n textUnderlineOffset: '2px',\n }}\n >\n {isOptionExpanded\n ? 'Show less'\n : 'Show full list'}\n </Link>\n )}\n </Collapse>\n <Divider\n sx={{\n borderColor: alpha(theme.palette.divider, 0.1),\n }}\n />\n </Box>\n );\n })}\n </Box>\n\n {hasMoreGroups && !search && (\n <Link\n alignSelf=\"center\"\n component=\"button\"\n type=\"button\"\n underline=\"hover\"\n onClick={() => setShowAll(prev => !prev)}\n sx={{\n mt: 1,\n fontSize: 13,\n fontWeight: 500,\n color: theme.palette.primary.main,\n textUnderlineOffset: '2px',\n }}\n >\n {showAll ? 'Hide full list' : 'Show full list'}\n </Link>\n )}\n </>\n )}\n </Box>\n\n <FilterDropdownClear\n disabled={!hasSelection}\n label={clearLabel}\n onClear={handleClear}\n />\n </Paper>\n </ClickAwayListener>\n </Popper>\n </>\n );\n};\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,170 @@
1
+ import { jsx, jsxs } from 'react/jsx-runtime';
2
+ import Box from '@mui/material/Box';
3
+ import { styled } from '@mui/material/styles';
4
+ import { useApi, fetchApiRef } from '@backstage/core-plugin-api';
5
+ import { ChartBox } from '../common/ChartBox.esm.js';
6
+ import GaugeChart, { GaugeBottomLabels } from '../charts/GaugeChart.esm.js';
7
+ import { useMttrStatisticsData } from '../../queries/mttr-statistics.queries.esm.js';
8
+ import { apiiroApiRef } from '../../api/index.esm.js';
9
+ import 'react';
10
+ import 'react-dom';
11
+ import { NotFound } from '../common/NotFound.esm.js';
12
+ import { SomethingWentWrong } from '../common/SomethingWentWrong.esm.js';
13
+ import { LogoSpinner } from '../common/logoSpinner.esm.js';
14
+
15
+ const GaugesGrid = styled(Box)(() => ({
16
+ display: "grid",
17
+ gridTemplateColumns: "1fr 1fr",
18
+ justifyItems: "center",
19
+ alignItems: "center",
20
+ gap: "8px",
21
+ width: "fit-content",
22
+ maxWidth: "100%",
23
+ margin: "0 auto",
24
+ "@media (max-width: 320px)": {
25
+ gridTemplateColumns: "1fr",
26
+ gap: "12px"
27
+ }
28
+ }));
29
+ const GaugeContainer = styled(Box)(() => ({
30
+ display: "flex",
31
+ flexDirection: "column",
32
+ alignItems: "center",
33
+ minWidth: 0,
34
+ position: "relative",
35
+ "&:hover": {
36
+ zIndex: 10
37
+ }
38
+ }));
39
+ const FooterText = styled("p")(({ theme }) => ({
40
+ fontSize: "11px",
41
+ color: theme.palette.text.secondary,
42
+ fontWeight: 400,
43
+ lineHeight: 1.2,
44
+ margin: 0,
45
+ whiteSpace: "nowrap",
46
+ overflow: "hidden",
47
+ textOverflow: "ellipsis",
48
+ textAlign: "center"
49
+ }));
50
+ const transformMttrStatisticsToGauges = (statistics) => {
51
+ const convertHoursForDisplay = (hours) => {
52
+ if (hours > 23) {
53
+ return {
54
+ displayValue: Math.ceil(hours / 24),
55
+ unit: "Days"
56
+ };
57
+ }
58
+ return {
59
+ displayValue: Math.ceil(hours),
60
+ unit: "Hours"
61
+ };
62
+ };
63
+ const riskLevelOrder = ["Critical", "High", "Medium", "Low"];
64
+ const sortedStatistics = statistics.sort((a, b) => {
65
+ const aIndex = riskLevelOrder.indexOf(a.riskLevel);
66
+ const bIndex = riskLevelOrder.indexOf(b.riskLevel);
67
+ const aOrder = aIndex === -1 ? riskLevelOrder.length : aIndex;
68
+ const bOrder = bIndex === -1 ? riskLevelOrder.length : bIndex;
69
+ return aOrder - bOrder;
70
+ });
71
+ return sortedStatistics.map((stat) => {
72
+ const meanTimeDisplay = convertHoursForDisplay(stat.meanTimeInHours);
73
+ const slaDisplay = convertHoursForDisplay(stat.slaInHours);
74
+ const unit = meanTimeDisplay.unit;
75
+ const maxHours = Math.max(stat.meanTimeInHours, stat.slaInHours) + 24;
76
+ const maxDisplay = convertHoursForDisplay(maxHours);
77
+ return {
78
+ value: unit === "Days" ? meanTimeDisplay.displayValue * 24 : Math.ceil(stat.meanTimeInHours),
79
+ // Original hours for arc scaling
80
+ tickValue: unit === "Days" ? slaDisplay.displayValue * 24 : Math.ceil(stat.slaInHours),
81
+ // Original hours for arc scaling
82
+ minValue: 0,
83
+ maxValue: unit === "Days" ? maxDisplay.displayValue * 24 : Math.ceil(maxHours),
84
+ categoryLabel: stat.riskLevel,
85
+ tooltip: `View ${stat.riskLevel} risks out of SLA`,
86
+ unit,
87
+ displayValue: meanTimeDisplay.displayValue,
88
+ // Converted value for center display
89
+ displayTickValue: slaDisplay.displayValue,
90
+ // Converted value for tick display
91
+ displayMinValue: 0,
92
+ // Min is always 0
93
+ displayMaxValue: maxDisplay.displayValue
94
+ // Converted max value for bottom labels
95
+ };
96
+ });
97
+ };
98
+ const MttrVsSLATile = ({
99
+ title = "MTTR vs. SLA",
100
+ tooltip = "The scale is set dynamically according to the SLA definition",
101
+ gauges = [],
102
+ footerText = "*SLA (days)",
103
+ width = "100%",
104
+ repoId,
105
+ entityRef
106
+ }) => {
107
+ const connectBackendApi = useApi(apiiroApiRef);
108
+ const { fetch } = useApi(fetchApiRef);
109
+ const {
110
+ mttrStatisticsData,
111
+ mttrStatisticsDataError,
112
+ mttrStatisticsDataLoading
113
+ } = useMttrStatisticsData({
114
+ connectApi: connectBackendApi,
115
+ fetchApi: fetch,
116
+ repositoryKey: repoId,
117
+ entityRef
118
+ });
119
+ const shouldUseApiData = !!repoId;
120
+ const apiGauges = shouldUseApiData && mttrStatisticsData ? transformMttrStatisticsToGauges(mttrStatisticsData) : null;
121
+ const finalGauges = apiGauges || gauges;
122
+ const footer = footerText && !mttrStatisticsDataLoading ? /* @__PURE__ */ jsx(FooterText, { children: footerText }) : void 0;
123
+ if (shouldUseApiData && mttrStatisticsDataLoading) {
124
+ return /* @__PURE__ */ jsx(ChartBox, { title, tooltip, footer, width, children: /* @__PURE__ */ jsx(
125
+ Box,
126
+ {
127
+ display: "flex",
128
+ justifyContent: "center",
129
+ alignItems: "center",
130
+ minHeight: "250px",
131
+ children: /* @__PURE__ */ jsx(LogoSpinner, {})
132
+ }
133
+ ) });
134
+ }
135
+ if (shouldUseApiData && mttrStatisticsDataError) {
136
+ return /* @__PURE__ */ jsx(ChartBox, { title, tooltip, width, children: /* @__PURE__ */ jsx(SomethingWentWrong, {}) });
137
+ }
138
+ 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." }) });
140
+ }
141
+ return /* @__PURE__ */ jsx(ChartBox, { title, tooltip, footer, width, children: /* @__PURE__ */ jsx(GaugesGrid, { children: finalGauges.map((gauge, index) => /* @__PURE__ */ jsxs(GaugeContainer, { children: [
142
+ /* @__PURE__ */ jsx(
143
+ GaugeChart,
144
+ {
145
+ width: 200,
146
+ height: 180,
147
+ value: gauge.value,
148
+ tickValue: gauge.tickValue,
149
+ minValue: gauge.minValue,
150
+ maxValue: gauge.maxValue,
151
+ tooltip: gauge.tooltip,
152
+ unit: gauge.unit,
153
+ displayValue: gauge.displayValue,
154
+ displayTickValue: gauge.displayTickValue
155
+ }
156
+ ),
157
+ /* @__PURE__ */ jsx(
158
+ GaugeBottomLabels,
159
+ {
160
+ minValue: gauge.displayMinValue ?? gauge.minValue,
161
+ maxValue: gauge.displayMaxValue ?? gauge.maxValue,
162
+ categoryLabel: gauge.categoryLabel,
163
+ width: "130px"
164
+ }
165
+ )
166
+ ] }, index)) }) });
167
+ };
168
+
169
+ export { MttrVsSLATile, MttrVsSLATile as default };
170
+ //# sourceMappingURL=MttrVsSLATile.esm.js.map
@@ -0,0 +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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"}
@@ -0,0 +1,311 @@
1
+ import { jsx, jsxs } from 'react/jsx-runtime';
2
+ import Box from '@mui/material/Box';
3
+ import Typography from '@mui/material/Typography';
4
+ import { styled, useTheme } from '@mui/material/styles';
5
+ import { getTrendColors } from '../../theme/themeUtils.esm.js';
6
+ import { useApi, fetchApiRef } from '@backstage/core-plugin-api';
7
+ import { ChartBox } from '../common/ChartBox.esm.js';
8
+ import { LineChart } from '../charts/LineChart.esm.js';
9
+ import TrendingUpIcon from '@mui/icons-material/TrendingUp';
10
+ import TrendingDownIcon from '@mui/icons-material/TrendingDown';
11
+ import RemoveIcon from '@mui/icons-material/Remove';
12
+ import { useRiskScoreOverTimeData } from '../../queries/risk-score-over-time.queries.esm.js';
13
+ import { apiiroApiRef } from '../../api/index.esm.js';
14
+ import 'react';
15
+ import 'react-dom';
16
+ import { NotFound } from '../common/NotFound.esm.js';
17
+ import { SomethingWentWrong } from '../common/SomethingWentWrong.esm.js';
18
+ import { LogoSpinner } from '../common/logoSpinner.esm.js';
19
+ import { formatNumberWithSuffix } from '../../utils/numberFormatter.esm.js';
20
+
21
+ const HeaderContainer = styled(Box)(() => ({
22
+ display: "flex",
23
+ alignItems: "center",
24
+ justifyContent: "space-between",
25
+ marginBottom: "16px",
26
+ width: "100%"
27
+ }));
28
+ const TitleContainer = styled(Box)(() => ({
29
+ display: "flex",
30
+ alignItems: "center",
31
+ gap: "8px"
32
+ }));
33
+ const StyledTitle = styled(Typography)(({ theme }) => ({
34
+ fontWeight: 400,
35
+ fontSize: "16px",
36
+ lineHeight: "24px",
37
+ color: theme.palette.text.primary
38
+ }));
39
+ const PercentageContainer = styled(Box)(({ backgroundColor, textColor }) => ({
40
+ display: "flex",
41
+ alignItems: "center",
42
+ gap: "4px",
43
+ backgroundColor,
44
+ padding: "4px 8px",
45
+ borderRadius: "12px",
46
+ color: textColor,
47
+ fontSize: "14px",
48
+ fontWeight: 500
49
+ }));
50
+ const sortDateStrings = (dates) => {
51
+ return dates.sort((a, b) => {
52
+ const dateA = new Date(a).getTime();
53
+ const dateB = new Date(b).getTime();
54
+ if (isNaN(dateA) && isNaN(dateB)) return 0;
55
+ if (isNaN(dateA)) return 1;
56
+ if (isNaN(dateB)) return -1;
57
+ return dateA - dateB;
58
+ });
59
+ };
60
+ const formatRiskScore = (value) => {
61
+ return formatNumberWithSuffix(value, 1);
62
+ };
63
+ const formatXAxisDate = (dateStr) => {
64
+ if (typeof dateStr !== "string") return String(dateStr);
65
+ const date = new Date(dateStr);
66
+ if (isNaN(date.getTime())) {
67
+ return String(dateStr);
68
+ }
69
+ const month = (date.getMonth() + 1).toString().padStart(2, "0");
70
+ const day = date.getDate().toString().padStart(2, "0");
71
+ return `${month}/${day}`;
72
+ };
73
+ const formatTooltipDate = (dateStr) => {
74
+ if (typeof dateStr !== "string") return String(dateStr);
75
+ const date = new Date(dateStr);
76
+ if (isNaN(date.getTime())) {
77
+ return String(dateStr);
78
+ }
79
+ return date.toLocaleDateString("en-GB", {
80
+ day: "numeric",
81
+ month: "short",
82
+ year: "numeric"
83
+ });
84
+ };
85
+ const RiskOverTimeTile = ({
86
+ title = "Risk score over time",
87
+ tooltip = "Track risk score changes over time",
88
+ width = "100%",
89
+ repoId,
90
+ entityRef
91
+ }) => {
92
+ const theme = useTheme();
93
+ const trendColors = getTrendColors(theme);
94
+ const connectBackendApi = useApi(apiiroApiRef);
95
+ const { fetch } = useApi(fetchApiRef);
96
+ const {
97
+ riskScoreOverTimeData,
98
+ riskScoreOverTimeDataError,
99
+ riskScoreOverTimeDataLoading
100
+ } = useRiskScoreOverTimeData({
101
+ connectApi: connectBackendApi,
102
+ fetchApi: fetch,
103
+ repositoryKey: repoId,
104
+ entityRef
105
+ });
106
+ if (riskScoreOverTimeDataLoading) {
107
+ return /* @__PURE__ */ jsx(ChartBox, { title, tooltip, width, children: /* @__PURE__ */ jsx(
108
+ Box,
109
+ {
110
+ display: "flex",
111
+ justifyContent: "center",
112
+ alignItems: "center",
113
+ minHeight: "250px",
114
+ children: /* @__PURE__ */ jsx(LogoSpinner, {})
115
+ }
116
+ ) });
117
+ }
118
+ if (riskScoreOverTimeDataError) {
119
+ return /* @__PURE__ */ jsx(ChartBox, { title, tooltip, width, children: /* @__PURE__ */ jsx(SomethingWentWrong, {}) });
120
+ }
121
+ if (!repoId) {
122
+ return /* @__PURE__ */ jsx(ChartBox, { title, tooltip, width, children: /* @__PURE__ */ jsx(
123
+ Box,
124
+ {
125
+ display: "flex",
126
+ justifyContent: "center",
127
+ alignItems: "center",
128
+ minHeight: "300px",
129
+ children: /* @__PURE__ */ jsx(NotFound, { message: "Please provide the repository details to access the data." })
130
+ }
131
+ ) });
132
+ }
133
+ const finalData = riskScoreOverTimeData ? riskScoreOverTimeData.map((point) => ({
134
+ date: point.date,
135
+ riskScore: point.count
136
+ })) : [];
137
+ if (finalData.length === 0) {
138
+ return /* @__PURE__ */ jsx(ChartBox, { title, tooltip, width, children: /* @__PURE__ */ jsx(NotFound, {}) });
139
+ }
140
+ const hasAllZeroValues = riskScoreOverTimeData.every(
141
+ (point) => point.count === 0
142
+ );
143
+ if (hasAllZeroValues) {
144
+ return /* @__PURE__ */ jsx(ChartBox, { title, tooltip, width, children: /* @__PURE__ */ jsx(NotFound, {}) });
145
+ }
146
+ const calculatePercentageChange = (data) => {
147
+ if (data.length <= 1) return 0;
148
+ const firstValue = data[0].riskScore ?? 0;
149
+ const lastValue = data[data.length - 1].riskScore ?? 0;
150
+ const difference = lastValue - firstValue;
151
+ if (firstValue === 0) {
152
+ return lastValue > 0 ? 100 : 0;
153
+ }
154
+ if (firstValue < 0) {
155
+ if (lastValue > 0) return 100;
156
+ return difference / Math.abs(firstValue) * 100;
157
+ }
158
+ return difference / firstValue * 100;
159
+ };
160
+ const calculatedPercentageChange = calculatePercentageChange(finalData);
161
+ const isNeutralChange = calculatedPercentageChange === 0;
162
+ const isPositiveChange = calculatedPercentageChange > 0;
163
+ let lineColor;
164
+ let indicatorColor;
165
+ let indicatorTextColor;
166
+ let TrendIcon;
167
+ if (isNeutralChange) {
168
+ lineColor = trendColors.neutral.line;
169
+ indicatorColor = trendColors.neutral.background;
170
+ indicatorTextColor = trendColors.neutral.text;
171
+ TrendIcon = RemoveIcon;
172
+ } else if (isPositiveChange) {
173
+ lineColor = trendColors.positive.line;
174
+ indicatorColor = trendColors.positive.background;
175
+ indicatorTextColor = trendColors.positive.text;
176
+ TrendIcon = TrendingUpIcon;
177
+ } else {
178
+ lineColor = trendColors.negative.line;
179
+ indicatorColor = trendColors.negative.background;
180
+ indicatorTextColor = trendColors.negative.text;
181
+ TrendIcon = TrendingDownIcon;
182
+ }
183
+ const calculateYAxisRange = (data) => {
184
+ if (!data || data.length === 0) {
185
+ return { yMin: 0, yMax: 100 };
186
+ }
187
+ const values = data.map((d) => d.riskScore).filter((v) => !isNaN(v) && isFinite(v));
188
+ if (values.length === 0) {
189
+ return { yMin: 0, yMax: 100 };
190
+ }
191
+ const minValue = Math.min(...values);
192
+ const maxValue = Math.max(...values);
193
+ if (minValue === 0 && maxValue === 0) {
194
+ return { yMin: 0, yMax: 10 };
195
+ }
196
+ if (maxValue === 0 && minValue < 0) {
197
+ return { yMin: minValue * 1.2, yMax: 10 };
198
+ }
199
+ let yMin2;
200
+ if (minValue >= 0) {
201
+ yMin2 = Math.max(0, minValue - minValue * 0.15);
202
+ } else {
203
+ yMin2 = minValue - minValue * 0.15;
204
+ }
205
+ let yMax2;
206
+ if (maxValue === 0) {
207
+ yMax2 = 10;
208
+ } else {
209
+ yMax2 = maxValue + maxValue * 0.01;
210
+ }
211
+ if (yMin2 >= yMax2) {
212
+ if (maxValue === 0) {
213
+ return { yMin: 0, yMax: 10 };
214
+ }
215
+ yMin2 = Math.max(0, maxValue * 0.9);
216
+ yMax2 = maxValue * 1.1;
217
+ }
218
+ return { yMin: yMin2, yMax: yMax2 };
219
+ };
220
+ const { yMin, yMax } = calculateYAxisRange(finalData);
221
+ const yTicks = (() => {
222
+ const count = 6;
223
+ const ticks = [];
224
+ if (Math.abs(yMax - yMin) < Number.EPSILON) {
225
+ for (let i = 0; i < count; i++) ticks.push(yMin);
226
+ return ticks;
227
+ }
228
+ if (yMin > yMax) {
229
+ const step2 = (yMin - yMax) / (count - 1);
230
+ for (let i = 0; i < count; i++) {
231
+ ticks.push(yMax + i * step2);
232
+ }
233
+ return ticks.reverse();
234
+ }
235
+ const step = (yMax - yMin) / (count - 1);
236
+ for (let i = 0; i < count; i++) {
237
+ const tickValue = yMin + i * step;
238
+ ticks.push(parseFloat(tickValue.toFixed(10)));
239
+ }
240
+ ticks[count - 1] = yMax;
241
+ return ticks;
242
+ })();
243
+ const series = [
244
+ {
245
+ id: "riskScore",
246
+ label: "Risk score",
247
+ data: finalData.map((point) => ({
248
+ x: point.date,
249
+ y: point.riskScore
250
+ })),
251
+ color: lineColor
252
+ // Dynamic color based on percentage change
253
+ }
254
+ ];
255
+ const customHeader = /* @__PURE__ */ jsxs(HeaderContainer, { children: [
256
+ /* @__PURE__ */ jsx(TitleContainer, { children: /* @__PURE__ */ jsx(StyledTitle, { children: title }) }),
257
+ /* @__PURE__ */ jsxs(
258
+ PercentageContainer,
259
+ {
260
+ backgroundColor: indicatorColor,
261
+ textColor: indicatorTextColor,
262
+ children: [
263
+ /* @__PURE__ */ jsx(TrendIcon, { sx: { fontSize: "16px", color: indicatorTextColor } }),
264
+ /* @__PURE__ */ jsxs(
265
+ Typography,
266
+ {
267
+ variant: "body2",
268
+ sx: { fontWeight: 500, color: indicatorTextColor },
269
+ children: [
270
+ calculatedPercentageChange > 0 ? "+" : "",
271
+ calculatedPercentageChange.toFixed(1),
272
+ "%"
273
+ ]
274
+ }
275
+ )
276
+ ]
277
+ }
278
+ )
279
+ ] });
280
+ return /* @__PURE__ */ jsx(
281
+ ChartBox,
282
+ {
283
+ title,
284
+ tooltip,
285
+ width,
286
+ customHeader,
287
+ children: /* @__PURE__ */ jsx(
288
+ LineChart,
289
+ {
290
+ series,
291
+ width: "100%",
292
+ height: 300,
293
+ showLegend: true,
294
+ legendPosition: "bottom",
295
+ showGrid: true,
296
+ formatYValue: formatRiskScore,
297
+ formatXValue: formatXAxisDate,
298
+ formatTooltipXValue: formatTooltipDate,
299
+ showDataPoints: false,
300
+ customXSort: (values) => sortDateStrings(values),
301
+ yAxisMin: yMin,
302
+ yAxisMax: yMax,
303
+ yAxisTicks: yTicks
304
+ }
305
+ )
306
+ }
307
+ );
308
+ };
309
+
310
+ export { RiskOverTimeTile, RiskOverTimeTile as default };
311
+ //# sourceMappingURL=RiskOverTimeTile.esm.js.map
@@ -0,0 +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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"}