@red-hat-developer-hub/backstage-plugin-scorecard 4.2.0 → 4.3.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 (73) hide show
  1. package/CHANGELOG.md +47 -0
  2. package/README.md +74 -10
  3. package/dist/alpha.d.ts +16 -0
  4. package/dist/blueprints/ScorecardLayoutBlueprint.esm.js +12 -10
  5. package/dist/blueprints/ScorecardLayoutBlueprint.esm.js.map +1 -1
  6. package/dist/components/AggregatedMetricCards/AggregatedMetricCard.esm.js +12 -5
  7. package/dist/components/AggregatedMetricCards/AggregatedMetricCard.esm.js.map +1 -1
  8. package/dist/components/AggregatedMetricCards/ScalarStatCard/ScalarStatCard.esm.js +76 -0
  9. package/dist/components/AggregatedMetricCards/ScalarStatCard/ScalarStatCard.esm.js.map +1 -0
  10. package/dist/components/AggregatedMetricCards/ScalarStatCard/ScalarStatTile.esm.js +66 -0
  11. package/dist/components/AggregatedMetricCards/ScalarStatCard/ScalarStatTile.esm.js.map +1 -0
  12. package/dist/components/AggregatedMetricCards/StatusGroupedCard/StatusGroupedCardComponent.esm.js +1 -0
  13. package/dist/components/AggregatedMetricCards/StatusGroupedCard/StatusGroupedCardComponent.esm.js.map +1 -1
  14. package/dist/components/AggregatedMetricCards/WeightedStatusScoreCard/DonutChartTooltipContent.esm.js +1 -1
  15. package/dist/components/AggregatedMetricCards/WeightedStatusScoreCard/WeightedStatusScoreCardComponent.esm.js +2 -5
  16. package/dist/components/AggregatedMetricCards/WeightedStatusScoreCard/WeightedStatusScoreCardComponent.esm.js.map +1 -1
  17. package/dist/components/Common/CardWrapper.esm.js +2 -2
  18. package/dist/components/Common/CardWrapper.esm.js.map +1 -1
  19. package/dist/components/MetricGroupCard/DataSourcesDialog.esm.js +3 -2
  20. package/dist/components/MetricGroupCard/DataSourcesDialog.esm.js.map +1 -1
  21. package/dist/components/MetricGroupCard/DataSourcesDialogColumns.esm.js +7 -1
  22. package/dist/components/MetricGroupCard/DataSourcesDialogColumns.esm.js.map +1 -1
  23. package/dist/components/MetricGroupCard/StatusIcon.esm.js +1 -1
  24. package/dist/components/MetricGroupCard/ThresholdBucketTile.esm.js +1 -1
  25. package/dist/components/MetricGroupCard/ThresholdLegend.esm.js +1 -1
  26. package/dist/components/MetricGroupCard/thresholdBucketUtils.esm.js +1 -0
  27. package/dist/components/MetricGroupCard/thresholdBucketUtils.esm.js.map +1 -1
  28. package/dist/components/Scorecard/CustomLegend.esm.js +4 -2
  29. package/dist/components/Scorecard/CustomLegend.esm.js.map +1 -1
  30. package/dist/components/Scorecard/EntityScorecardContent.esm.js +2 -1
  31. package/dist/components/Scorecard/EntityScorecardContent.esm.js.map +1 -1
  32. package/dist/components/Scorecard/Scorecard.esm.js +3 -2
  33. package/dist/components/Scorecard/Scorecard.esm.js.map +1 -1
  34. package/dist/components/Scorecard/ScorecardEntityContentGridView.esm.js +13 -4
  35. package/dist/components/Scorecard/ScorecardEntityContentGridView.esm.js.map +1 -1
  36. package/dist/components/ScorecardHomepageSection/CustomLegend.esm.js +1 -1
  37. package/dist/components/ScorecardHomepageSection/CustomTooltip.esm.js +1 -1
  38. package/dist/components/ScorecardHomepageSection/EmptyStatePanel.esm.js +1 -1
  39. package/dist/components/ScorecardPage/EntitiesTable/EntitiesRow.esm.js +1 -0
  40. package/dist/components/ScorecardPage/EntitiesTable/EntitiesRow.esm.js.map +1 -1
  41. package/dist/components/ScorecardPage/EntitiesTable/EntitiesTable.esm.js +25 -16
  42. package/dist/components/ScorecardPage/EntitiesTable/EntitiesTable.esm.js.map +1 -1
  43. package/dist/components/ScorecardPage/EntitiesTable/EntitiesTableWrapper.esm.js +8 -1
  44. package/dist/components/ScorecardPage/EntitiesTable/EntitiesTableWrapper.esm.js.map +1 -1
  45. package/dist/components/ScorecardPage/EntitiesTable/cells/MetricStatusCell.esm.js +1 -1
  46. package/dist/extensions/entityTab.esm.js +8 -11
  47. package/dist/extensions/entityTab.esm.js.map +1 -1
  48. package/dist/extensions/homePageCards.esm.js +79 -1
  49. package/dist/extensions/homePageCards.esm.js.map +1 -1
  50. package/dist/index.d.ts +60 -0
  51. package/dist/index.esm.js +8 -2
  52. package/dist/index.esm.js.map +1 -1
  53. package/dist/translations/de.esm.js +16 -0
  54. package/dist/translations/de.esm.js.map +1 -1
  55. package/dist/translations/es.esm.js +16 -0
  56. package/dist/translations/es.esm.js.map +1 -1
  57. package/dist/translations/fr.esm.js +16 -0
  58. package/dist/translations/fr.esm.js.map +1 -1
  59. package/dist/translations/it.esm.js +16 -0
  60. package/dist/translations/it.esm.js.map +1 -1
  61. package/dist/translations/ja.esm.js +16 -0
  62. package/dist/translations/ja.esm.js.map +1 -1
  63. package/dist/translations/ref.esm.js +26 -0
  64. package/dist/translations/ref.esm.js.map +1 -1
  65. package/dist/utils/formatMetricUnit.esm.js +12 -0
  66. package/dist/utils/formatMetricUnit.esm.js.map +1 -0
  67. package/dist/utils/getDefaultEntitiesTableSort.esm.js +19 -0
  68. package/dist/utils/getDefaultEntitiesTableSort.esm.js.map +1 -0
  69. package/dist/utils/isScalarAggregation.esm.js +14 -0
  70. package/dist/utils/isScalarAggregation.esm.js.map +1 -0
  71. package/dist/utils/translationUtils.esm.js +6 -1
  72. package/dist/utils/translationUtils.esm.js.map +1 -1
  73. package/package.json +26 -33
package/CHANGELOG.md CHANGED
@@ -1,5 +1,52 @@
1
1
  # @red-hat-developer-hub/backstage-plugin-scorecard
2
2
 
3
+ ## 4.3.0
4
+
5
+ ### Minor Changes
6
+
7
+ - c380e6b: Skip scalar aggregation threshold coloring when no successful samples contributed (`total` is 0). Return a null display color and keep the card grey fallback. Scalar aggregation responses now include `aggregationChartDisplayColor` (threshold-derived chart color, or `null` when `total` is 0).
8
+
9
+ **BREAKING**: Changed types in `scorecard-common` module:
10
+
11
+ - `WeightedStatusScoreAggregationResult.aggregationChartDisplayColor` widened from `string` to `string | null`.
12
+ - `ScalarAggregationResult` gained a required `aggregationChartDisplayColor: string | null` property.
13
+
14
+ These changes are intentional: the API can return `null` when no samples contribute, and scalar KPI results now expose the same display-color field as weighted status score aggregations.
15
+
16
+ - 628533c: Add optional `titleKey` and `descriptionKey` on Scorecard layout groups so group titles and descriptions from app-config can be translated, with fallback to the configured English strings.
17
+ - a1c3eb3: Updated DORA Mean Time to Restore to Median Time to Restore.
18
+ - ff6683f: Add DORA metrics and a collectors framework for composing datasource data into metrics.
19
+
20
+ - New `@red-hat-developer-hub/backstage-plugin-scorecard-backend-module-dora` with Deployment Frequency, Median Lead Time for Changes, Mean Time to Restore, and Change Failure Rate
21
+ - New data collectors used by DORA: GitHub deployments, deployment workflow runs, and deployment pull requests; Jira incidents
22
+ - Metric time-series API `/metrics/catalog/:kind/:namespace/:name/time-series`
23
+ - Adds `defaultVisualization` to Metric metadata for sparkline
24
+
25
+ - 08e6312: Render scalar aggregation KPI cards from result shape (`ScalarStatCard`) for sum, average, min, max, and count, including threshold status and an unsupported-shape fallback.
26
+ - f3f71a5: Add unit to metric and display it in threshold legend
27
+ - a7a1b4a: Backstage version bump to v1.54.6
28
+
29
+ ### Patch Changes
30
+
31
+ - 1e93f4e: Add `HomePageWidgetBlueprint` for the README filecheck aggregated card so it appears in the NFS homepage add-widget dialog alongside the existing LICENSE and CODEOWNERS widgets.
32
+ - 0754805: Scalar aggregation KPI cards now use `result.aggregationChartDisplayColor` from the backend instead of re-evaluating threshold expressions on the frontend. Min/max scalar drill-down pages now default-sort the entities table by metric value (ascending for min, descending for max).
33
+ - 47ac76d: Export public `ScalarAggregationType` for scalar aggregation KPI types (`sum`, `average`, `count`, `min`, `max`).
34
+
35
+ Add legacy app E2E coverage for scalar and statusGrouped homepage aggregation KPI cards, including reusable Playwright registration helpers and legacy homepage widget entries for scalar KPIs.
36
+
37
+ - Updated dependencies [9c1936e]
38
+ - Updated dependencies [2bb0ec5]
39
+ - Updated dependencies [485fadb]
40
+ - Updated dependencies [befccc2]
41
+ - Updated dependencies [c380e6b]
42
+ - Updated dependencies [fea86e8]
43
+ - Updated dependencies [ff6683f]
44
+ - Updated dependencies [ecb789b]
45
+ - Updated dependencies [47ac76d]
46
+ - Updated dependencies [f3f71a5]
47
+ - Updated dependencies [a7a1b4a]
48
+ - @red-hat-developer-hub/backstage-plugin-scorecard-common@4.3.0
49
+
3
50
  ## 4.2.0
4
51
 
5
52
  ### Minor Changes
package/README.md CHANGED
@@ -9,7 +9,7 @@ For NFS, register the default `scorecardPlugin` plus `scorecardTranslationsModul
9
9
  **Features:**
10
10
 
11
11
  - **Entity scorecard tab** — View scorecard metrics on catalog entity pages (components, websites, etc.).
12
- - **Scorecard homepage card** — Show aggregated KPIs on the home page (e.g. GitHub open PRs, Jira open issues). Supports **`statusGrouped`** (multi-slice pie) and **`weightedStatusScore`** (weighted health donut) KPI types configured under **`scorecard.aggregationKPIs`**. Scalar types (`sum`, `average`, `max`, `min`, `count`) are supported by the backend API but not yet rendered by the bundled homepage card (see [Homepage scorecard cards](#homepage-scorecard-cards)).
12
+ - **Scorecard homepage card** — Show aggregated KPIs on the home page (e.g. GitHub open PRs, Jira open issues). Supports **`statusGrouped`** (multi-slice pie), **`weightedStatusScore`** (weighted health donut), and scalar types (`sum`, `average`, `max`, `min`, `count`) configured under **`scorecard.aggregationKPIs`**.
13
13
  - **Scorecard Entities page** — Drill down from an aggregated metric to see the list of entities contributing to that metric, with entity-level values and status, so you can identify services impacting the KPI and investigate issues.
14
14
  - **Metric group cards (grid layout)** — Group related metrics into cards with threshold bucket tiles, a filterable/sortable data sources dialog, and a Masonry grid layout. Enabled via app-config.yaml.
15
15
 
@@ -133,7 +133,9 @@ To align with the legacy EntityPage (Scorecard on component pages and default en
133
133
  groups:
134
134
  codeQuality:
135
135
  title: 'Code Quality'
136
+ titleKey: groups.codeQuality.title
136
137
  description: 'SonarQube code quality metrics'
138
+ descriptionKey: groups.codeQuality.description
137
139
  metrics:
138
140
  - sonarqube.reliabilityIssues
139
141
  - sonarqube.codeCoverage
@@ -147,11 +149,13 @@ To align with the legacy EntityPage (Scorecard on component pages and default en
147
149
 
148
150
  **Groups config schema:**
149
151
 
150
- | Field | Type | Required | Description |
151
- | ------------- | ---------- | -------- | ---------------------------------------------------- |
152
- | `title` | `string` | Yes | Display title for the group card. |
153
- | `description` | `string` | No | Optional description shown below the title. |
154
- | `metrics` | `string[]` | Yes | Ordered list of metric IDs to include in this group. |
152
+ | Field | Type | Required | Description |
153
+ | ---------------- | ---------- | -------- | ------------------------------------------------------------------------------------------------ |
154
+ | `title` | `string` | Yes | Display title for the group card. Used when `titleKey` is omitted or the translation is missing. |
155
+ | `titleKey` | `string` | No | Translation key under `plugin.scorecard` (for example `groups.codeQuality.title`). |
156
+ | `description` | `string` | No | Optional description shown below the title. Used when `descriptionKey` is omitted or missing. |
157
+ | `descriptionKey` | `string` | No | Translation key under `plugin.scorecard` (for example `groups.codeQuality.description`). |
158
+ | `metrics` | `string[]` | Yes | Ordered list of metric IDs to include in this group. |
155
159
 
156
160
  **Behavior:**
157
161
 
@@ -239,9 +243,54 @@ To align with the legacy EntityPage (Scorecard on component pages and default en
239
243
  sm: { w: 4, h: 6, x: 8 }
240
244
  xs: { w: 4, h: 6, x: 8 }
241
245
  xxs: { w: 4, h: 6, x: 8 }
246
+ AggregatedCardWithMaxOpenPrs:
247
+ priority: 480
248
+ breakpoints:
249
+ xl: { w: 4, h: 6 }
250
+ lg: { w: 4, h: 6 }
251
+ md: { w: 4, h: 6 }
252
+ sm: { w: 4, h: 6 }
253
+ xs: { w: 4, h: 6 }
254
+ xxs: { w: 4, h: 6 }
255
+ AggregatedCardWithMinOpenPrs:
256
+ priority: 490
257
+ breakpoints:
258
+ xl: { w: 4, h: 6, x: 4 }
259
+ lg: { w: 4, h: 6, x: 4 }
260
+ md: { w: 4, h: 6, x: 4 }
261
+ sm: { w: 4, h: 6, x: 4 }
262
+ xs: { w: 4, h: 6, x: 4 }
263
+ xxs: { w: 4, h: 6, x: 4 }
264
+ AggregatedCardWithTotalOpenBugs:
265
+ priority: 500
266
+ breakpoints:
267
+ xl: { w: 4, h: 6, x: 8 }
268
+ lg: { w: 4, h: 6, x: 8 }
269
+ md: { w: 4, h: 6, x: 8 }
270
+ sm: { w: 4, h: 6, x: 8 }
271
+ xs: { w: 4, h: 6, x: 8 }
272
+ xxs: { w: 4, h: 6, x: 8 }
273
+ AggregatedCardWithEntitiesWithOpenPrs:
274
+ priority: 510
275
+ breakpoints:
276
+ xl: { w: 4, h: 6 }
277
+ lg: { w: 4, h: 6 }
278
+ md: { w: 4, h: 6 }
279
+ sm: { w: 4, h: 6 }
280
+ xs: { w: 4, h: 6 }
281
+ xxs: { w: 4, h: 6 }
282
+ AggregatedCardWithAvgOpenPrs:
283
+ priority: 520
284
+ breakpoints:
285
+ xl: { w: 4, h: 6, x: 4 }
286
+ lg: { w: 4, h: 6, x: 4 }
287
+ md: { w: 4, h: 6, x: 4 }
288
+ sm: { w: 4, h: 6, x: 4 }
289
+ xs: { w: 4, h: 6, x: 4 }
290
+ xxs: { w: 4, h: 6, x: 4 }
242
291
  ```
243
292
 
244
- The home module contributes seven widgets:
293
+ The home module contributes twelve widgets:
245
294
 
246
295
  - `AggregatedCardWithDeprecatedMetricId` (title: **Scorecard: With deprecated metricId property (Jira)**)
247
296
  - `AggregatedCardWithDefaultAggregation` (title: **Scorecard: With default aggregation config (GitHub)**)
@@ -250,6 +299,11 @@ To align with the legacy EntityPage (Scorecard on component pages and default en
250
299
  - `AggregatedCardWithGithubFilecheckLicense` (title: **Scorecard: LICENSE file exists**)
251
300
  - `AggregatedCardWithGithubFilecheckCodeowners` (title: **Scorecard: CODEOWNERS file exists**)
252
301
  - `AggregatedCardWithGithubOpenPrsWeighted` (title: **Scorecard: GitHub open PRs (weighted health)**)
302
+ - `AggregatedCardWithMaxOpenPrs` (title: **Scorecard: Maximum open PRs**)
303
+ - `AggregatedCardWithMinOpenPrs` (title: **Scorecard: Minimum open PRs**)
304
+ - `AggregatedCardWithTotalOpenBugs` (title: **Scorecard: Total open bugs**)
305
+ - `AggregatedCardWithEntitiesWithOpenPrs` (title: **Scorecard: Entities with open PRs**)
306
+ - `AggregatedCardWithAvgOpenPrs` (title: **Scorecard: Average open PRs**)
253
307
 
254
308
  These widgets render the `ScorecardHomepageCard` component used in legacy apps, preconfigured with different aggregation/metric configurations.
255
309
 
@@ -280,6 +334,11 @@ The default `scorecardPlugin` registers the Scorecard page, API, entity tab, lay
280
334
  | `home-page-widget:scorecard/scorecard-github-filecheck-license` | Homepage widget for file check "License". |
281
335
  | `home-page-widget:scorecard/scorecard-github-filecheck-codeowners` | Homepage widget for file check "Codeowners". |
282
336
  | `home-page-widget:scorecard/scorecard-github-open-prs-weighted` | Homepage widget for weighted GitHub open PRs health. |
337
+ | `home-page-widget:scorecard/scorecard-max-open-prs` | Homepage widget for scalar max open PRs. |
338
+ | `home-page-widget:scorecard/scorecard-min-open-prs` | Homepage widget for scalar min open PRs. |
339
+ | `home-page-widget:scorecard/scorecard-total-open-bugs` | Homepage widget for scalar sum of open bugs. |
340
+ | `home-page-widget:scorecard/scorecard-entities-with-open-prs` | Homepage widget for scalar count of entities with open PRs. |
341
+ | `home-page-widget:scorecard/scorecard-avg-open-prs` | Homepage widget for scalar average open PRs. |
283
342
 
284
343
  ##### Migration notes (NFS graduation)
285
344
 
@@ -458,11 +517,11 @@ Supported scorecard aggregation types (see [Entity Aggregation — Aggregation t
458
517
  - [`weightedStatusScore`](../scorecard-backend/docs/aggregation.md#weighted-status-score-type) — weighted portfolio health percentage (donut); requires `options.statusScores`.
459
518
  - Scalar types ([`sum`](../scorecard-backend/docs/aggregation.md#sum-type), [`average`](../scorecard-backend/docs/aggregation.md#average-type), [`max`](../scorecard-backend/docs/aggregation.md#max-type), [`min`](../scorecard-backend/docs/aggregation.md#min-type), [`count`](../scorecard-backend/docs/aggregation.md#count-type)) — roll up latest numeric metric values; number metrics only. Optional [`filter.status`](../scorecard-backend/docs/aggregation.md#status-filter-scalar-types) limits the rollup to a threshold status.
460
519
 
461
- **Scalar KPI types** (`sum`, `average`, `max`, `min`, `count`) are fully supported by **`GET /aggregations/:aggregationId`**, including optional **`filter.status`** and **`options.thresholds`**. The bundled **`ScorecardHomepageCard`** / **`AggregatedMetricCard`** only renders **`statusGrouped`** and **`weightedStatusScore`**; scalar types show an unsupported-type error panel. Build a custom card to display **`result.value`** and classify it with **`result.thresholds`**.
520
+ **Scalar KPI types** (`sum`, `average`, `max`, `min`, `count`) are fully supported by **`GET /aggregations/:aggregationId`**, including optional **`filter.status`** and **`options.thresholds`**. **`AggregatedMetricCard`** chooses the UI from the **result shape**: a numeric `value` renders **`ScalarStatCard`** (large number, optional description, aggregation-type label, tile color from **`result.aggregationChartDisplayColor`**); a `values` array renders the existing pie/donut cards; anything else shows **`UnsupportedAggregationType`**. Unknown scalar type names still render **`ScalarStatCard`** when the payload is scalar-shaped.
462
521
 
463
522
  **Terminology:** Scalar **`average`** (mean of numeric metric values) is distinct from the former weighted KPI type also named `average`, which was renamed to **`weightedStatusScore`**. Update app-config **`type: average`** entries for weighted health scoring to **`type: weightedStatusScore`** and migrate API consumers from **`result.averageScore`** to **`result.weightedStatusScore`**.
464
523
 
465
- The NFS example widget **`AggregatedCardWithGithubOpenPrs`** uses **`aggregationId: maxOpenPrs`** (scalar **`max`** KPI). Uncomment and configure **`maxOpenPrs`** under **`scorecard.aggregationKPIs`** in app-config to exercise the API; expect the unsupported-type panel until scalar homepage UI is added.
524
+ Configure a scalar KPI such as **`maxOpenPrs`** under **`scorecard.aggregationKPIs`** and pass that id as **`aggregationId`** to **`ScorecardHomepageCard`** to render the aggregated value.
466
525
 
467
526
  #### Card props
468
527
 
@@ -550,14 +609,19 @@ Translation keys follow this pattern:
550
609
 
551
610
  - **Metric titles**: `metric.{metric-id}.title`
552
611
  - **Metric descriptions**: `metric.{metric-id}.description`
612
+ - **Layout group titles**: set `titleKey` on the group in app-config (for example `groups.codeQuality.title`)
613
+ - **Layout group descriptions**: set `descriptionKey` on the group (for example `groups.codeQuality.description`)
614
+
615
+ Use the same `metric.{id}.{field}` pattern with a **KPI id** as `{metric-id}` when localizing **`aggregationKPIs`** titles (for example **`metric.openIssuesKpi.title`**).
553
616
 
554
- Use the same pattern with a **KPI id** as `{metric-id}` when localizing **`aggregationKPIs`** titles (for example **`metric.openIssuesKpi.title`**).
617
+ Group titles and descriptions do **not** use the `metric.{id}.{field}` convention. They are customer-defined in `scorecard-layout` config, so you pass explicit `titleKey` / `descriptionKey` values. Provide translations in plugin locale files or via RHDH `i18n.overrides` under `plugin.scorecard`. If a key is omitted or not found, the UI shows the group's `title` / `description` strings and never the raw key.
555
618
 
556
619
  ### 3. Fallback Behavior
557
620
 
558
621
  If a translation key is not found, the plugin will automatically fall back to:
559
622
 
560
623
  - The original `title` and `description` values from the metric definition
624
+ - For layout groups: the `title` and `description` values from app-config
561
625
  - For thresholds: capitalized versions of the threshold keys
562
626
 
563
627
  ### Example: Adding GitHub Open PRs Translation
package/dist/alpha.d.ts CHANGED
@@ -34,6 +34,14 @@ declare const scorecardTranslationRef: _backstage_frontend_plugin_api.Translatio
34
34
  readonly "errors.noDataFoundMessage": string;
35
35
  readonly "errors.unsupportedAggregationType": string;
36
36
  readonly "errors.authenticationErrorMessage": string;
37
+ readonly "metric.dora.deploymentFrequency.title": string;
38
+ readonly "metric.dora.deploymentFrequency.description": string;
39
+ readonly "metric.dora.medianLeadTimeForChanges.title": string;
40
+ readonly "metric.dora.medianLeadTimeForChanges.description": string;
41
+ readonly "metric.dora.changeFailureRate.title": string;
42
+ readonly "metric.dora.changeFailureRate.description": string;
43
+ readonly "metric.dora.medianTimeToRestore.title": string;
44
+ readonly "metric.dora.medianTimeToRestore.description": string;
37
45
  readonly "metric.github.openPRs.title": string;
38
46
  readonly "metric.github.openPRs.description": string;
39
47
  readonly "metric.jira.openIssues.title": string;
@@ -77,9 +85,17 @@ declare const scorecardTranslationRef: _backstage_frontend_plugin_api.Translatio
77
85
  readonly "metric.drillDownCalculationFailures": string;
78
86
  readonly "metric.homepageEntityHealthRatio": string;
79
87
  readonly "metric.homepageEntityCalculationHealth": string;
88
+ readonly "aggregation.min": string;
89
+ readonly "aggregation.max": string;
90
+ readonly "aggregation.sum": string;
91
+ readonly "aggregation.count": string;
92
+ readonly "aggregation.average": string;
80
93
  readonly "thresholds.success": string;
81
94
  readonly "thresholds.warning": string;
82
95
  readonly "thresholds.error": string;
96
+ readonly "thresholds.elite": string;
97
+ readonly "thresholds.medium": string;
98
+ readonly "thresholds.low": string;
83
99
  readonly "thresholds.exist": string;
84
100
  readonly "thresholds.missing": string;
85
101
  readonly "thresholds.noEntities": string;
@@ -1,5 +1,6 @@
1
1
  import { createElement } from 'react';
2
2
  import { createExtensionDataRef, createExtensionBlueprint, coreExtensionData, ExtensionBoundary } from '@backstage/frontend-plugin-api';
3
+ import { z } from 'zod';
3
4
 
4
5
  const scorecardLayoutTitleDataRef = createExtensionDataRef().with({ id: "scorecard.layout-title" });
5
6
  const ScorecardEntityContentLayoutBlueprint = createExtensionBlueprint({
@@ -12,16 +13,17 @@ const ScorecardEntityContentLayoutBlueprint = createExtensionBlueprint({
12
13
  dataRefs: {
13
14
  title: scorecardLayoutTitleDataRef
14
15
  },
15
- config: {
16
- schema: {
17
- groups: (schema) => schema.record(
18
- schema.object({
19
- title: schema.string(),
20
- description: schema.string().optional(),
21
- metrics: schema.array(schema.string())
22
- })
23
- ).optional().default({})
24
- }
16
+ configSchema: {
17
+ groups: z.record(
18
+ z.string(),
19
+ z.object({
20
+ title: z.string(),
21
+ titleKey: z.string().optional(),
22
+ description: z.string().optional(),
23
+ descriptionKey: z.string().optional(),
24
+ metrics: z.array(z.string())
25
+ })
26
+ ).optional().default({})
25
27
  },
26
28
  *factory(params, { config, node }) {
27
29
  yield scorecardLayoutTitleDataRef(params.title);
@@ -1 +1 @@
1
- {"version":3,"file":"ScorecardLayoutBlueprint.esm.js","sources":["../../src/blueprints/ScorecardLayoutBlueprint.ts"],"sourcesContent":["/*\n * Copyright Red Hat, Inc.\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 */\n\nimport { ComponentType, createElement } from 'react';\nimport {\n coreExtensionData,\n createExtensionBlueprint,\n createExtensionDataRef,\n ExtensionBoundary,\n} from '@backstage/frontend-plugin-api';\n\n/**\n * Props for custom Scorecard entity-tab layout components.\n *\n * @public\n */\nexport interface ScorecardLayoutProps {\n /**\n * Metric groups to render, keyed by group id.\n */\n groups: Record<\n string,\n { title: string; description?: string; metrics: string[] }\n >;\n}\n\n/**\n * Extension data ref carrying the human-readable layout title\n * (e.g. \"Grid\", \"List\") so the entity tab can build a toggle.\n *\n * @public\n */\nexport const scorecardLayoutTitleDataRef =\n createExtensionDataRef<string>().with({ id: 'scorecard.layout-title' });\n\n/**\n * Blueprint for creating scorecard layout extensions.\n *\n * Each layout is a separate extension that attaches to the Scorecard entity tab.\n * Platform engineers enable/disable individual layouts via app-config.yaml;\n * when multiple layouts are enabled the tab renders a toggle to switch between them.\n *\n * @public\n */\nexport const ScorecardEntityContentLayoutBlueprint = createExtensionBlueprint({\n kind: 'scorecard-layout',\n attachTo: {\n id: 'entity-content:scorecard/entity-content-scorecard',\n input: 'layouts',\n },\n output: [coreExtensionData.reactElement, scorecardLayoutTitleDataRef],\n dataRefs: {\n title: scorecardLayoutTitleDataRef,\n },\n config: {\n schema: {\n groups: schema =>\n schema\n .record(\n schema.object({\n title: schema.string(),\n description: schema.string().optional(),\n metrics: schema.array(schema.string()),\n }),\n )\n .optional()\n .default({}),\n },\n },\n *factory(\n params: {\n title: string;\n loader: () => Promise<ComponentType<ScorecardLayoutProps>>;\n },\n { config, node },\n ) {\n yield scorecardLayoutTitleDataRef(params.title);\n yield coreExtensionData.reactElement(\n ExtensionBoundary.lazy(node, async () => {\n const Component = await params.loader();\n return createElement(Component, {\n groups: config.groups,\n });\n }),\n );\n },\n});\n"],"names":[],"mappings":";;;AA6CO,MAAM,8BACX,sBAAA,EAA+B,CAAE,KAAK,EAAE,EAAA,EAAI,0BAA0B;AAWjE,MAAM,wCAAwC,wBAAA,CAAyB;AAAA,EAC5E,IAAA,EAAM,kBAAA;AAAA,EACN,QAAA,EAAU;AAAA,IACR,EAAA,EAAI,mDAAA;AAAA,IACJ,KAAA,EAAO;AAAA,GACT;AAAA,EACA,MAAA,EAAQ,CAAC,iBAAA,CAAkB,YAAA,EAAc,2BAA2B,CAAA;AAAA,EACpE,QAAA,EAAU;AAAA,IACR,KAAA,EAAO;AAAA,GACT;AAAA,EACA,MAAA,EAAQ;AAAA,IACN,MAAA,EAAQ;AAAA,MACN,MAAA,EAAQ,YACN,MAAA,CACG,MAAA;AAAA,QACC,OAAO,MAAA,CAAO;AAAA,UACZ,KAAA,EAAO,OAAO,MAAA,EAAO;AAAA,UACrB,WAAA,EAAa,MAAA,CAAO,MAAA,EAAO,CAAE,QAAA,EAAS;AAAA,UACtC,OAAA,EAAS,MAAA,CAAO,KAAA,CAAM,MAAA,CAAO,QAAQ;AAAA,SACtC;AAAA,OACH,CACC,QAAA,EAAS,CACT,OAAA,CAAQ,EAAE;AAAA;AACjB,GACF;AAAA,EACA,CAAC,OAAA,CACC,MAAA,EAIA,EAAE,MAAA,EAAQ,MAAK,EACf;AACA,IAAA,MAAM,2BAAA,CAA4B,OAAO,KAAK,CAAA;AAC9C,IAAA,MAAM,iBAAA,CAAkB,YAAA;AAAA,MACtB,iBAAA,CAAkB,IAAA,CAAK,IAAA,EAAM,YAAY;AACvC,QAAA,MAAM,SAAA,GAAY,MAAM,MAAA,CAAO,MAAA,EAAO;AACtC,QAAA,OAAO,cAAc,SAAA,EAAW;AAAA,UAC9B,QAAQ,MAAA,CAAO;AAAA,SAChB,CAAA;AAAA,MACH,CAAC;AAAA,KACH;AAAA,EACF;AACF,CAAC;;;;"}
1
+ {"version":3,"file":"ScorecardLayoutBlueprint.esm.js","sources":["../../src/blueprints/ScorecardLayoutBlueprint.ts"],"sourcesContent":["/*\n * Copyright Red Hat, Inc.\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 */\n\nimport { ComponentType, createElement } from 'react';\nimport {\n coreExtensionData,\n createExtensionBlueprint,\n createExtensionDataRef,\n ExtensionBoundary,\n} from '@backstage/frontend-plugin-api';\nimport { z } from 'zod';\n\n/**\n * Props for custom Scorecard entity-tab layout components.\n *\n * @public\n */\nexport interface ScorecardLayoutProps {\n /**\n * Metric groups to render, keyed by group id.\n */\n groups: Record<\n string,\n {\n title: string;\n titleKey?: string;\n description?: string;\n descriptionKey?: string;\n metrics: string[];\n }\n >;\n}\n\n/**\n * Extension data ref carrying the human-readable layout title\n * (e.g. \"Grid\", \"List\") so the entity tab can build a toggle.\n *\n * @public\n */\nexport const scorecardLayoutTitleDataRef =\n createExtensionDataRef<string>().with({ id: 'scorecard.layout-title' });\n\n/**\n * Blueprint for creating scorecard layout extensions.\n *\n * Each layout is a separate extension that attaches to the Scorecard entity tab.\n * Platform engineers enable/disable individual layouts via app-config.yaml;\n * when multiple layouts are enabled the tab renders a toggle to switch between them.\n *\n * @public\n */\nexport const ScorecardEntityContentLayoutBlueprint = createExtensionBlueprint({\n kind: 'scorecard-layout',\n attachTo: {\n id: 'entity-content:scorecard/entity-content-scorecard',\n input: 'layouts',\n },\n output: [coreExtensionData.reactElement, scorecardLayoutTitleDataRef],\n dataRefs: {\n title: scorecardLayoutTitleDataRef,\n },\n configSchema: {\n groups: z\n .record(\n z.string(),\n z.object({\n title: z.string(),\n titleKey: z.string().optional(),\n description: z.string().optional(),\n descriptionKey: z.string().optional(),\n metrics: z.array(z.string()),\n }),\n )\n .optional()\n .default({}),\n },\n *factory(\n params: {\n title: string;\n loader: () => Promise<ComponentType<ScorecardLayoutProps>>;\n },\n { config, node },\n ) {\n yield scorecardLayoutTitleDataRef(params.title);\n yield coreExtensionData.reactElement(\n ExtensionBoundary.lazy(node, async () => {\n const Component = await params.loader();\n return createElement(Component, {\n groups: config.groups,\n });\n }),\n );\n },\n});\n"],"names":[],"mappings":";;;;AAoDO,MAAM,8BACX,sBAAA,EAA+B,CAAE,KAAK,EAAE,EAAA,EAAI,0BAA0B;AAWjE,MAAM,wCAAwC,wBAAA,CAAyB;AAAA,EAC5E,IAAA,EAAM,kBAAA;AAAA,EACN,QAAA,EAAU;AAAA,IACR,EAAA,EAAI,mDAAA;AAAA,IACJ,KAAA,EAAO;AAAA,GACT;AAAA,EACA,MAAA,EAAQ,CAAC,iBAAA,CAAkB,YAAA,EAAc,2BAA2B,CAAA;AAAA,EACpE,QAAA,EAAU;AAAA,IACR,KAAA,EAAO;AAAA,GACT;AAAA,EACA,YAAA,EAAc;AAAA,IACZ,QAAQ,CAAA,CACL,MAAA;AAAA,MACC,EAAE,MAAA,EAAO;AAAA,MACT,EAAE,MAAA,CAAO;AAAA,QACP,KAAA,EAAO,EAAE,MAAA,EAAO;AAAA,QAChB,QAAA,EAAU,CAAA,CAAE,MAAA,EAAO,CAAE,QAAA,EAAS;AAAA,QAC9B,WAAA,EAAa,CAAA,CAAE,MAAA,EAAO,CAAE,QAAA,EAAS;AAAA,QACjC,cAAA,EAAgB,CAAA,CAAE,MAAA,EAAO,CAAE,QAAA,EAAS;AAAA,QACpC,OAAA,EAAS,CAAA,CAAE,KAAA,CAAM,CAAA,CAAE,QAAQ;AAAA,OAC5B;AAAA,KACH,CACC,QAAA,EAAS,CACT,OAAA,CAAQ,EAAE;AAAA,GACf;AAAA,EACA,CAAC,OAAA,CACC,MAAA,EAIA,EAAE,MAAA,EAAQ,MAAK,EACf;AACA,IAAA,MAAM,2BAAA,CAA4B,OAAO,KAAK,CAAA;AAC9C,IAAA,MAAM,iBAAA,CAAkB,YAAA;AAAA,MACtB,iBAAA,CAAkB,IAAA,CAAK,IAAA,EAAM,YAAY;AACvC,QAAA,MAAM,SAAA,GAAY,MAAM,MAAA,CAAO,MAAA,EAAO;AACtC,QAAA,OAAO,cAAc,SAAA,EAAW;AAAA,UAC9B,QAAQ,MAAA,CAAO;AAAA,SAChB,CAAA;AAAA,MACH,CAAC;AAAA,KACH;AAAA,EACF;AACF,CAAC;;;;"}
@@ -1,19 +1,26 @@
1
1
  import { jsx } from 'react/jsx-runtime';
2
- import { aggregationTypes } from '@red-hat-developer-hub/backstage-plugin-scorecard-common';
3
2
  import { StatusGroupedCardComponent } from './StatusGroupedCard/StatusGroupedCardComponent.esm.js';
4
3
  import { WeightedStatusScoreCardComponent } from './WeightedStatusScoreCard/WeightedStatusScoreCardComponent.esm.js';
4
+ import { ScalarStatCard } from './ScalarStatCard/ScalarStatCard.esm.js';
5
5
  import { UnsupportedAggregationType } from './UnsupportedAggregationType.esm.js';
6
+ import 'date-fns';
7
+ import { isScalarAggregationResult, isWeightedStatusScoreResult, isDistributionAggregationResult } from '../../utils/isScalarAggregation.esm.js';
8
+ import '@red-hat-developer-hub/backstage-plugin-scorecard-common';
6
9
 
7
- const isStatusGroupedCardProps = (props) => props.scorecard.metadata.aggregationType === aggregationTypes.statusGrouped;
8
- const isWeightedStatusScoreCardProps = (props) => props.scorecard.metadata.aggregationType === aggregationTypes.weightedStatusScore;
10
+ const isScalarStatCardProps = (props) => isScalarAggregationResult(props.scorecard.result);
11
+ const isWeightedStatusScoreCardProps = (props) => isWeightedStatusScoreResult(props.scorecard.result);
12
+ const isStatusGroupedCardProps = (props) => isDistributionAggregationResult(props.scorecard.result);
9
13
  const AggregatedMetricCard = (props) => {
10
14
  const { cardTitle, description, dataTestId, scorecard } = props;
11
- if (isStatusGroupedCardProps(props)) {
12
- return /* @__PURE__ */ jsx(StatusGroupedCardComponent, { ...props });
15
+ if (isScalarStatCardProps(props)) {
16
+ return /* @__PURE__ */ jsx(ScalarStatCard, { ...props });
13
17
  }
14
18
  if (isWeightedStatusScoreCardProps(props)) {
15
19
  return /* @__PURE__ */ jsx(WeightedStatusScoreCardComponent, { ...props });
16
20
  }
21
+ if (isStatusGroupedCardProps(props)) {
22
+ return /* @__PURE__ */ jsx(StatusGroupedCardComponent, { ...props });
23
+ }
17
24
  return /* @__PURE__ */ jsx(
18
25
  UnsupportedAggregationType,
19
26
  {
@@ -1 +1 @@
1
- {"version":3,"file":"AggregatedMetricCard.esm.js","sources":["../../../src/components/AggregatedMetricCards/AggregatedMetricCard.tsx"],"sourcesContent":["/*\n * Copyright Red Hat, Inc.\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 */\n\nimport {\n aggregationTypes,\n type AggregatedMetricResult,\n} from '@red-hat-developer-hub/backstage-plugin-scorecard-common';\nimport { StatusGroupedCardComponent } from './StatusGroupedCard/StatusGroupedCardComponent';\nimport { WeightedStatusScoreCardComponent } from './WeightedStatusScoreCard/WeightedStatusScoreCardComponent';\nimport { UnsupportedAggregationType } from './UnsupportedAggregationType';\n\nimport type { WeightedStatusScoreCardComponentProps } from './WeightedStatusScoreCard/types';\nimport type { StatusGroupedCardComponentProps } from './StatusGroupedCard/types';\nimport type { AggregatedMetricCardBaseProps } from './types';\n\ntype AggregatedMetricCardProps = AggregatedMetricCardBaseProps & {\n scorecard: AggregatedMetricResult;\n};\n\nconst isStatusGroupedCardProps = (\n props: AggregatedMetricCardProps,\n): props is StatusGroupedCardComponentProps =>\n props.scorecard.metadata.aggregationType === aggregationTypes.statusGrouped;\n\nconst isWeightedStatusScoreCardProps = (\n props: AggregatedMetricCardProps,\n): props is WeightedStatusScoreCardComponentProps =>\n props.scorecard.metadata.aggregationType ===\n aggregationTypes.weightedStatusScore;\n\nexport const AggregatedMetricCard = (props: AggregatedMetricCardProps) => {\n const { cardTitle, description, dataTestId, scorecard } = props;\n\n if (isStatusGroupedCardProps(props)) {\n return <StatusGroupedCardComponent {...props} />;\n }\n if (isWeightedStatusScoreCardProps(props)) {\n return <WeightedStatusScoreCardComponent {...props} />;\n }\n return (\n <UnsupportedAggregationType\n cardTitle={cardTitle}\n description={description}\n dataTestId={dataTestId}\n aggregationType={String(scorecard.metadata.aggregationType)}\n />\n );\n};\n"],"names":[],"mappings":";;;;;;AAgCA,MAAM,2BAA2B,CAC/B,KAAA,KAEA,MAAM,SAAA,CAAU,QAAA,CAAS,oBAAoB,gBAAA,CAAiB,aAAA;AAEhE,MAAM,iCAAiC,CACrC,KAAA,KAEA,MAAM,SAAA,CAAU,QAAA,CAAS,oBACzB,gBAAA,CAAiB,mBAAA;AAEZ,MAAM,oBAAA,GAAuB,CAAC,KAAA,KAAqC;AACxE,EAAA,MAAM,EAAE,SAAA,EAAW,WAAA,EAAa,UAAA,EAAY,WAAU,GAAI,KAAA;AAE1D,EAAA,IAAI,wBAAA,CAAyB,KAAK,CAAA,EAAG;AACnC,IAAA,uBAAO,GAAA,CAAC,0BAAA,EAAA,EAA4B,GAAG,KAAA,EAAO,CAAA;AAAA,EAChD;AACA,EAAA,IAAI,8BAAA,CAA+B,KAAK,CAAA,EAAG;AACzC,IAAA,uBAAO,GAAA,CAAC,gCAAA,EAAA,EAAkC,GAAG,KAAA,EAAO,CAAA;AAAA,EACtD;AACA,EAAA,uBACE,GAAA;AAAA,IAAC,0BAAA;AAAA,IAAA;AAAA,MACC,SAAA;AAAA,MACA,WAAA;AAAA,MACA,UAAA;AAAA,MACA,eAAA,EAAiB,MAAA,CAAO,SAAA,CAAU,QAAA,CAAS,eAAe;AAAA;AAAA,GAC5D;AAEJ;;;;"}
1
+ {"version":3,"file":"AggregatedMetricCard.esm.js","sources":["../../../src/components/AggregatedMetricCards/AggregatedMetricCard.tsx"],"sourcesContent":["/*\n * Copyright Red Hat, Inc.\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 */\n\nimport type { AggregatedMetricResult } from '@red-hat-developer-hub/backstage-plugin-scorecard-common';\nimport { StatusGroupedCardComponent } from './StatusGroupedCard/StatusGroupedCardComponent';\nimport { WeightedStatusScoreCardComponent } from './WeightedStatusScoreCard/WeightedStatusScoreCardComponent';\nimport { ScalarStatCard } from './ScalarStatCard/ScalarStatCard';\nimport { UnsupportedAggregationType } from './UnsupportedAggregationType';\nimport {\n isDistributionAggregationResult,\n isScalarAggregationResult,\n isWeightedStatusScoreResult,\n} from '../../utils';\n\nimport type { WeightedStatusScoreCardComponentProps } from './WeightedStatusScoreCard/types';\nimport type { StatusGroupedCardComponentProps } from './StatusGroupedCard/types';\nimport type { ScalarStatCardProps } from './ScalarStatCard/types';\nimport type { AggregatedMetricCardBaseProps } from './types';\n\ntype AggregatedMetricCardProps = AggregatedMetricCardBaseProps & {\n scorecard: AggregatedMetricResult;\n};\n\nconst isScalarStatCardProps = (\n props: AggregatedMetricCardProps,\n): props is ScalarStatCardProps =>\n isScalarAggregationResult(props.scorecard.result);\n\nconst isWeightedStatusScoreCardProps = (\n props: AggregatedMetricCardProps,\n): props is WeightedStatusScoreCardComponentProps =>\n isWeightedStatusScoreResult(props.scorecard.result);\n\nconst isStatusGroupedCardProps = (\n props: AggregatedMetricCardProps,\n): props is StatusGroupedCardComponentProps =>\n isDistributionAggregationResult(props.scorecard.result);\n\nexport const AggregatedMetricCard = (props: AggregatedMetricCardProps) => {\n const { cardTitle, description, dataTestId, scorecard } = props;\n\n if (isScalarStatCardProps(props)) {\n return <ScalarStatCard {...props} />;\n }\n if (isWeightedStatusScoreCardProps(props)) {\n return <WeightedStatusScoreCardComponent {...props} />;\n }\n if (isStatusGroupedCardProps(props)) {\n return <StatusGroupedCardComponent {...props} />;\n }\n return (\n <UnsupportedAggregationType\n cardTitle={cardTitle}\n description={description}\n dataTestId={dataTestId}\n aggregationType={String(scorecard.metadata.aggregationType)}\n />\n );\n};\n"],"names":[],"mappings":";;;;;;;;;AAoCA,MAAM,wBAAwB,CAC5B,KAAA,KAEA,yBAAA,CAA0B,KAAA,CAAM,UAAU,MAAM,CAAA;AAElD,MAAM,iCAAiC,CACrC,KAAA,KAEA,2BAAA,CAA4B,KAAA,CAAM,UAAU,MAAM,CAAA;AAEpD,MAAM,2BAA2B,CAC/B,KAAA,KAEA,+BAAA,CAAgC,KAAA,CAAM,UAAU,MAAM,CAAA;AAEjD,MAAM,oBAAA,GAAuB,CAAC,KAAA,KAAqC;AACxE,EAAA,MAAM,EAAE,SAAA,EAAW,WAAA,EAAa,UAAA,EAAY,WAAU,GAAI,KAAA;AAE1D,EAAA,IAAI,qBAAA,CAAsB,KAAK,CAAA,EAAG;AAChC,IAAA,uBAAO,GAAA,CAAC,cAAA,EAAA,EAAgB,GAAG,KAAA,EAAO,CAAA;AAAA,EACpC;AACA,EAAA,IAAI,8BAAA,CAA+B,KAAK,CAAA,EAAG;AACzC,IAAA,uBAAO,GAAA,CAAC,gCAAA,EAAA,EAAkC,GAAG,KAAA,EAAO,CAAA;AAAA,EACtD;AACA,EAAA,IAAI,wBAAA,CAAyB,KAAK,CAAA,EAAG;AACnC,IAAA,uBAAO,GAAA,CAAC,0BAAA,EAAA,EAA4B,GAAG,KAAA,EAAO,CAAA;AAAA,EAChD;AACA,EAAA,uBACE,GAAA;AAAA,IAAC,0BAAA;AAAA,IAAA;AAAA,MACC,SAAA;AAAA,MACA,WAAA;AAAA,MACA,UAAA;AAAA,MACA,eAAA,EAAiB,MAAA,CAAO,SAAA,CAAU,QAAA,CAAS,eAAe;AAAA;AAAA,GAC5D;AAEJ;;;;"}
@@ -0,0 +1,76 @@
1
+ import { jsx } from 'react/jsx-runtime';
2
+ import { useTheme } from '@mui/material/styles';
3
+ import { CardWrapper } from '../../Common/CardWrapper.esm.js';
4
+ import 'date-fns';
5
+ import { formatWithMetricUnit } from '../../../utils/formatMetricUnit.esm.js';
6
+ import '@red-hat-developer-hub/backstage-plugin-scorecard-common';
7
+ import { resolveStatusColor } from '../../../utils/statusUtils.esm.js';
8
+ import { useTranslation } from '../../../hooks/useTranslation.esm.js';
9
+ import { CardInfoButton } from '../components/CardInfoButton.esm.js';
10
+ import { CardSubheader } from '../components/CardSubheader.esm.js';
11
+ import { CardChartContainer } from '../components/CardChartContainer.esm.js';
12
+ import { formatAggregationScoreDetail } from '../WeightedStatusScoreCard/TooltipContent.esm.js';
13
+ import { ScalarStatTile } from './ScalarStatTile.esm.js';
14
+
15
+ function getAggregationTypeLabel(aggregationType, t) {
16
+ if (!aggregationType) {
17
+ return "";
18
+ }
19
+ const key = `aggregation.${aggregationType}`;
20
+ const translated = t(key, {});
21
+ if (translated !== key) {
22
+ return translated;
23
+ }
24
+ return aggregationType.charAt(0).toUpperCase() + aggregationType.slice(1);
25
+ }
26
+ const ScalarStatCard = ({
27
+ scorecard,
28
+ cardTitle,
29
+ description,
30
+ aggregationId,
31
+ showSubheader = true,
32
+ showInfo = true,
33
+ dataTestId
34
+ }) => {
35
+ const theme = useTheme();
36
+ const { t } = useTranslation();
37
+ const { result, metadata, id: scorecardId } = scorecard;
38
+ const resolvedColor = result.aggregationChartDisplayColor ? resolveStatusColor(theme, result.aggregationChartDisplayColor) : theme.palette.grey[300];
39
+ const displayValue = formatWithMetricUnit(
40
+ formatAggregationScoreDetail(result.value),
41
+ metadata.unit
42
+ );
43
+ const aggregationLabel = getAggregationTypeLabel(metadata.aggregationType, t);
44
+ const subheader = showSubheader ? /* @__PURE__ */ jsx(
45
+ CardSubheader,
46
+ {
47
+ aggregationId,
48
+ scorecardId,
49
+ entitiesCount: result.total,
50
+ entitiesConsidered: result.entitiesConsidered,
51
+ calculationErrorCount: result.calculationErrorCount
52
+ }
53
+ ) : null;
54
+ const info = showInfo ? /* @__PURE__ */ jsx(CardInfoButton, { timestamp: result.timestamp }) : null;
55
+ return /* @__PURE__ */ jsx(
56
+ CardWrapper,
57
+ {
58
+ title: cardTitle,
59
+ dataTestId,
60
+ subheader,
61
+ description,
62
+ info,
63
+ children: /* @__PURE__ */ jsx(CardChartContainer, { children: /* @__PURE__ */ jsx(
64
+ ScalarStatTile,
65
+ {
66
+ displayValue,
67
+ label: aggregationLabel,
68
+ resolvedColor
69
+ }
70
+ ) })
71
+ }
72
+ );
73
+ };
74
+
75
+ export { ScalarStatCard };
76
+ //# sourceMappingURL=ScalarStatCard.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ScalarStatCard.esm.js","sources":["../../../../src/components/AggregatedMetricCards/ScalarStatCard/ScalarStatCard.tsx"],"sourcesContent":["/*\n * Copyright Red Hat, Inc.\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 */\n\nimport { useTheme } from '@mui/material/styles';\nimport type { TranslationFunction } from '@backstage/core-plugin-api/alpha';\n\nimport { CardWrapper } from '../../Common/CardWrapper';\nimport { formatWithMetricUnit, resolveStatusColor } from '../../../utils';\nimport { useTranslation } from '../../../hooks/useTranslation';\nimport { scorecardTranslationRef } from '../../../translations';\nimport { CardInfoButton } from '../components/CardInfoButton';\nimport { CardSubheader } from '../components/CardSubheader';\nimport { CardChartContainer } from '../components/CardChartContainer';\nimport { formatAggregationScoreDetail } from '../WeightedStatusScoreCard/TooltipContent';\nimport { ScalarStatTile } from './ScalarStatTile';\nimport type { ScalarStatCardProps } from './types';\n\nfunction getAggregationTypeLabel(\n aggregationType: string,\n t: TranslationFunction<typeof scorecardTranslationRef.T>,\n): string {\n if (!aggregationType) {\n return '';\n }\n\n const key = `aggregation.${aggregationType}`;\n const translated = t(key as any, {});\n if (translated !== key) {\n return translated;\n }\n\n return aggregationType.charAt(0).toUpperCase() + aggregationType.slice(1);\n}\n\nexport const ScalarStatCard = ({\n scorecard,\n cardTitle,\n description,\n aggregationId,\n showSubheader = true,\n showInfo = true,\n dataTestId,\n}: ScalarStatCardProps) => {\n const theme = useTheme();\n const { t } = useTranslation();\n const { result, metadata, id: scorecardId } = scorecard;\n\n const resolvedColor = result.aggregationChartDisplayColor\n ? resolveStatusColor(theme, result.aggregationChartDisplayColor)\n : theme.palette.grey[300];\n const displayValue = formatWithMetricUnit(\n formatAggregationScoreDetail(result.value),\n metadata.unit,\n );\n const aggregationLabel = getAggregationTypeLabel(metadata.aggregationType, t);\n\n const subheader = showSubheader ? (\n <CardSubheader\n aggregationId={aggregationId}\n scorecardId={scorecardId}\n entitiesCount={result.total}\n entitiesConsidered={result.entitiesConsidered}\n calculationErrorCount={result.calculationErrorCount}\n />\n ) : null;\n\n const info = showInfo ? (\n <CardInfoButton timestamp={result.timestamp} />\n ) : null;\n\n return (\n <CardWrapper\n title={cardTitle}\n dataTestId={dataTestId}\n subheader={subheader}\n description={description}\n info={info}\n >\n <CardChartContainer>\n <ScalarStatTile\n displayValue={displayValue}\n label={aggregationLabel}\n resolvedColor={resolvedColor}\n />\n </CardChartContainer>\n </CardWrapper>\n );\n};\n"],"names":[],"mappings":";;;;;;;;;;;;;;AA8BA,SAAS,uBAAA,CACP,iBACA,CAAA,EACQ;AACR,EAAA,IAAI,CAAC,eAAA,EAAiB;AACpB,IAAA,OAAO,EAAA;AAAA,EACT;AAEA,EAAA,MAAM,GAAA,GAAM,eAAe,eAAe,CAAA,CAAA;AAC1C,EAAA,MAAM,UAAA,GAAa,CAAA,CAAE,GAAA,EAAY,EAAE,CAAA;AACnC,EAAA,IAAI,eAAe,GAAA,EAAK;AACtB,IAAA,OAAO,UAAA;AAAA,EACT;AAEA,EAAA,OAAO,eAAA,CAAgB,OAAO,CAAC,CAAA,CAAE,aAAY,GAAI,eAAA,CAAgB,MAAM,CAAC,CAAA;AAC1E;AAEO,MAAM,iBAAiB,CAAC;AAAA,EAC7B,SAAA;AAAA,EACA,SAAA;AAAA,EACA,WAAA;AAAA,EACA,aAAA;AAAA,EACA,aAAA,GAAgB,IAAA;AAAA,EAChB,QAAA,GAAW,IAAA;AAAA,EACX;AACF,CAAA,KAA2B;AACzB,EAAA,MAAM,QAAQ,QAAA,EAAS;AACvB,EAAA,MAAM,EAAE,CAAA,EAAE,GAAI,cAAA,EAAe;AAC7B,EAAA,MAAM,EAAE,MAAA,EAAQ,QAAA,EAAU,EAAA,EAAI,aAAY,GAAI,SAAA;AAE9C,EAAA,MAAM,aAAA,GAAgB,MAAA,CAAO,4BAAA,GACzB,kBAAA,CAAmB,KAAA,EAAO,MAAA,CAAO,4BAA4B,CAAA,GAC7D,KAAA,CAAM,OAAA,CAAQ,IAAA,CAAK,GAAG,CAAA;AAC1B,EAAA,MAAM,YAAA,GAAe,oBAAA;AAAA,IACnB,4BAAA,CAA6B,OAAO,KAAK,CAAA;AAAA,IACzC,QAAA,CAAS;AAAA,GACX;AACA,EAAA,MAAM,gBAAA,GAAmB,uBAAA,CAAwB,QAAA,CAAS,eAAA,EAAiB,CAAC,CAAA;AAE5E,EAAA,MAAM,YAAY,aAAA,mBAChB,GAAA;AAAA,IAAC,aAAA;AAAA,IAAA;AAAA,MACC,aAAA;AAAA,MACA,WAAA;AAAA,MACA,eAAe,MAAA,CAAO,KAAA;AAAA,MACtB,oBAAoB,MAAA,CAAO,kBAAA;AAAA,MAC3B,uBAAuB,MAAA,CAAO;AAAA;AAAA,GAChC,GACE,IAAA;AAEJ,EAAA,MAAM,OAAO,QAAA,mBACX,GAAA,CAAC,kBAAe,SAAA,EAAW,MAAA,CAAO,WAAW,CAAA,GAC3C,IAAA;AAEJ,EAAA,uBACE,GAAA;AAAA,IAAC,WAAA;AAAA,IAAA;AAAA,MACC,KAAA,EAAO,SAAA;AAAA,MACP,UAAA;AAAA,MACA,SAAA;AAAA,MACA,WAAA;AAAA,MACA,IAAA;AAAA,MAEA,8BAAC,kBAAA,EAAA,EACC,QAAA,kBAAA,GAAA;AAAA,QAAC,cAAA;AAAA,QAAA;AAAA,UACC,YAAA;AAAA,UACA,KAAA,EAAO,gBAAA;AAAA,UACP;AAAA;AAAA,OACF,EACF;AAAA;AAAA,GACF;AAEJ;;;;"}
@@ -0,0 +1,66 @@
1
+ import { jsxs, jsx } from 'react/jsx-runtime';
2
+ import Box from '@mui/material/Box';
3
+ import Typography from '@mui/material/Typography';
4
+ import { darken, alpha } from '@mui/material/styles';
5
+
6
+ const ScalarStatTile = ({
7
+ displayValue,
8
+ label,
9
+ resolvedColor
10
+ }) => {
11
+ const textColor = darken(resolvedColor, 0.3);
12
+ return /* @__PURE__ */ jsxs(
13
+ Box,
14
+ {
15
+ "data-testid": "scalar-stat-tile",
16
+ "aria-label": `${label} ${displayValue}`,
17
+ sx: {
18
+ width: "100%",
19
+ minHeight: 140,
20
+ py: 4,
21
+ px: 2,
22
+ borderRadius: 2,
23
+ backgroundColor: alpha(resolvedColor, 0.16),
24
+ textAlign: "center",
25
+ display: "flex",
26
+ flexDirection: "column",
27
+ alignItems: "center",
28
+ justifyContent: "center"
29
+ },
30
+ children: [
31
+ /* @__PURE__ */ jsx(
32
+ Typography,
33
+ {
34
+ "data-testid": "scalar-stat-value",
35
+ variant: "h3",
36
+ sx: {
37
+ fontSize: "2.5rem",
38
+ fontWeight: 700,
39
+ color: textColor,
40
+ lineHeight: 1.2
41
+ },
42
+ children: displayValue
43
+ }
44
+ ),
45
+ /* @__PURE__ */ jsx(
46
+ Typography,
47
+ {
48
+ "data-testid": "scalar-stat-label",
49
+ variant: "body1",
50
+ sx: {
51
+ fontSize: "1rem",
52
+ fontWeight: 500,
53
+ color: textColor,
54
+ lineHeight: 1.3,
55
+ mt: 0.5
56
+ },
57
+ children: label
58
+ }
59
+ )
60
+ ]
61
+ }
62
+ );
63
+ };
64
+
65
+ export { ScalarStatTile };
66
+ //# sourceMappingURL=ScalarStatTile.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ScalarStatTile.esm.js","sources":["../../../../src/components/AggregatedMetricCards/ScalarStatCard/ScalarStatTile.tsx"],"sourcesContent":["/*\n * Copyright Red Hat, Inc.\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 */\n\nimport Box from '@mui/material/Box';\nimport Typography from '@mui/material/Typography';\nimport { alpha, darken } from '@mui/material/styles';\n\ntype ScalarStatTileProps = {\n displayValue: string;\n label: string;\n resolvedColor: string;\n};\n\nexport const ScalarStatTile = ({\n displayValue,\n label,\n resolvedColor,\n}: ScalarStatTileProps) => {\n const textColor = darken(resolvedColor, 0.3);\n\n return (\n <Box\n data-testid=\"scalar-stat-tile\"\n aria-label={`${label} ${displayValue}`}\n sx={{\n width: '100%',\n minHeight: 140,\n py: 4,\n px: 2,\n borderRadius: 2,\n backgroundColor: alpha(resolvedColor, 0.16),\n textAlign: 'center',\n display: 'flex',\n flexDirection: 'column',\n alignItems: 'center',\n justifyContent: 'center',\n }}\n >\n <Typography\n data-testid=\"scalar-stat-value\"\n variant=\"h3\"\n sx={{\n fontSize: '2.5rem',\n fontWeight: 700,\n color: textColor,\n lineHeight: 1.2,\n }}\n >\n {displayValue}\n </Typography>\n <Typography\n data-testid=\"scalar-stat-label\"\n variant=\"body1\"\n sx={{\n fontSize: '1rem',\n fontWeight: 500,\n color: textColor,\n lineHeight: 1.3,\n mt: 0.5,\n }}\n >\n {label}\n </Typography>\n </Box>\n );\n};\n"],"names":[],"mappings":";;;;;AA0BO,MAAM,iBAAiB,CAAC;AAAA,EAC7B,YAAA;AAAA,EACA,KAAA;AAAA,EACA;AACF,CAAA,KAA2B;AACzB,EAAA,MAAM,SAAA,GAAY,MAAA,CAAO,aAAA,EAAe,GAAG,CAAA;AAE3C,EAAA,uBACE,IAAA;AAAA,IAAC,GAAA;AAAA,IAAA;AAAA,MACC,aAAA,EAAY,kBAAA;AAAA,MACZ,YAAA,EAAY,CAAA,EAAG,KAAK,CAAA,CAAA,EAAI,YAAY,CAAA,CAAA;AAAA,MACpC,EAAA,EAAI;AAAA,QACF,KAAA,EAAO,MAAA;AAAA,QACP,SAAA,EAAW,GAAA;AAAA,QACX,EAAA,EAAI,CAAA;AAAA,QACJ,EAAA,EAAI,CAAA;AAAA,QACJ,YAAA,EAAc,CAAA;AAAA,QACd,eAAA,EAAiB,KAAA,CAAM,aAAA,EAAe,IAAI,CAAA;AAAA,QAC1C,SAAA,EAAW,QAAA;AAAA,QACX,OAAA,EAAS,MAAA;AAAA,QACT,aAAA,EAAe,QAAA;AAAA,QACf,UAAA,EAAY,QAAA;AAAA,QACZ,cAAA,EAAgB;AAAA,OAClB;AAAA,MAEA,QAAA,EAAA;AAAA,wBAAA,GAAA;AAAA,UAAC,UAAA;AAAA,UAAA;AAAA,YACC,aAAA,EAAY,mBAAA;AAAA,YACZ,OAAA,EAAQ,IAAA;AAAA,YACR,EAAA,EAAI;AAAA,cACF,QAAA,EAAU,QAAA;AAAA,cACV,UAAA,EAAY,GAAA;AAAA,cACZ,KAAA,EAAO,SAAA;AAAA,cACP,UAAA,EAAY;AAAA,aACd;AAAA,YAEC,QAAA,EAAA;AAAA;AAAA,SACH;AAAA,wBACA,GAAA;AAAA,UAAC,UAAA;AAAA,UAAA;AAAA,YACC,aAAA,EAAY,mBAAA;AAAA,YACZ,OAAA,EAAQ,OAAA;AAAA,YACR,EAAA,EAAI;AAAA,cACF,QAAA,EAAU,MAAA;AAAA,cACV,UAAA,EAAY,GAAA;AAAA,cACZ,KAAA,EAAO,SAAA;AAAA,cACP,UAAA,EAAY,GAAA;AAAA,cACZ,EAAA,EAAI;AAAA,aACN;AAAA,YAEC,QAAA,EAAA;AAAA;AAAA;AACH;AAAA;AAAA,GACF;AAEJ;;;;"}
@@ -3,6 +3,7 @@ import { useTheme } from '@mui/material/styles';
3
3
  import { useState } from 'react';
4
4
  import { SCORECARD_ERROR_STATE_COLOR } from '../../../utils/constants.esm.js';
5
5
  import 'date-fns';
6
+ import '@red-hat-developer-hub/backstage-plugin-scorecard-common';
6
7
  import { resolveStatusColor } from '../../../utils/statusUtils.esm.js';
7
8
  import { getThresholdRuleColor } from '../../../utils/thresholdUtils.esm.js';
8
9
  import { CardWrapper } from '../../Common/CardWrapper.esm.js';
@@ -1 +1 @@
1
- {"version":3,"file":"StatusGroupedCardComponent.esm.js","sources":["../../../../src/components/AggregatedMetricCards/StatusGroupedCard/StatusGroupedCardComponent.tsx"],"sourcesContent":["/*\n * Copyright Red Hat, Inc.\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 */\n\nimport { useTheme } from '@mui/material/styles';\nimport { useState } from 'react';\nimport { PieData } from '../../types';\nimport type { TooltipPosition } from '../types';\nimport {\n getThresholdRuleColor,\n SCORECARD_ERROR_STATE_COLOR,\n resolveStatusColor,\n} from '../../../utils';\nimport { CardWrapper } from '../../Common/CardWrapper';\nimport { CardInfoButton } from '../components/CardInfoButton';\nimport { ResponsivePieChart } from '../../ScorecardHomepageSection/ResponsivePieChart';\nimport { CardLegendContent } from '../components/CardLegendContent';\nimport { CardPieTooltipContent } from '../components/CardPieTooltipContent';\nimport { CardChartContainer } from '../components/CardChartContainer';\nimport { CardSubheader } from '../components/CardSubheader';\nimport { CardTooltip } from '../components/CardTooltip';\nimport { StatusGroupedCardComponentProps } from './types';\n\nexport const StatusGroupedCardComponent = ({\n scorecard,\n cardTitle,\n description,\n aggregationId,\n showSubheader = true,\n showInfo = true,\n dataTestId,\n}: StatusGroupedCardComponentProps) => {\n const theme = useTheme();\n\n const [activeIndex, setActiveIndex] = useState<number | null>(null);\n const [tooltipPosition, setTooltipPosition] =\n useState<TooltipPosition | null>(null);\n\n const { id: scorecardId, result } = scorecard;\n\n const pieData: PieData[] =\n result.values?.map(value => ({\n name: value.name,\n value: value.count,\n color: resolveStatusColor(\n theme,\n getThresholdRuleColor(result.thresholds.rules, value.name) ??\n SCORECARD_ERROR_STATE_COLOR,\n ),\n })) ?? [];\n\n const subheader = showSubheader ? (\n <CardSubheader\n aggregationId={aggregationId}\n scorecardId={scorecardId}\n entitiesCount={result.total}\n entitiesConsidered={result.entitiesConsidered}\n calculationErrorCount={result.calculationErrorCount}\n />\n ) : null;\n\n const info = showInfo ? (\n <CardInfoButton timestamp={result.timestamp} />\n ) : null;\n\n return (\n <CardWrapper\n title={cardTitle}\n dataTestId={dataTestId}\n subheader={subheader}\n description={description}\n info={info}\n >\n <CardChartContainer>\n <ResponsivePieChart\n pieData={pieData}\n legendContent={props => (\n <CardLegendContent\n {...props}\n activeIndex={activeIndex}\n setActiveIndex={setActiveIndex}\n setTooltipPosition={setTooltipPosition}\n pieData={pieData}\n />\n )}\n tooltipContent={({ active, payload }) => (\n <CardPieTooltipContent\n active={active}\n payload={payload}\n pieData={pieData}\n />\n )}\n />\n\n {activeIndex !== null && tooltipPosition && (\n <CardTooltip\n tooltipPosition={tooltipPosition}\n pieData={pieData}\n payload={[\n {\n name: pieData[activeIndex].name,\n value: pieData[activeIndex].value,\n payload: pieData[activeIndex],\n },\n ]}\n />\n )}\n </CardChartContainer>\n </CardWrapper>\n );\n};\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAmCO,MAAM,6BAA6B,CAAC;AAAA,EACzC,SAAA;AAAA,EACA,SAAA;AAAA,EACA,WAAA;AAAA,EACA,aAAA;AAAA,EACA,aAAA,GAAgB,IAAA;AAAA,EAChB,QAAA,GAAW,IAAA;AAAA,EACX;AACF,CAAA,KAAuC;AACrC,EAAA,MAAM,QAAQ,QAAA,EAAS;AAEvB,EAAA,MAAM,CAAC,WAAA,EAAa,cAAc,CAAA,GAAI,SAAwB,IAAI,CAAA;AAClE,EAAA,MAAM,CAAC,eAAA,EAAiB,kBAAkB,CAAA,GACxC,SAAiC,IAAI,CAAA;AAEvC,EAAA,MAAM,EAAE,EAAA,EAAI,WAAA,EAAa,MAAA,EAAO,GAAI,SAAA;AAEpC,EAAA,MAAM,OAAA,GACJ,MAAA,CAAO,MAAA,EAAQ,GAAA,CAAI,CAAA,KAAA,MAAU;AAAA,IAC3B,MAAM,KAAA,CAAM,IAAA;AAAA,IACZ,OAAO,KAAA,CAAM,KAAA;AAAA,IACb,KAAA,EAAO,kBAAA;AAAA,MACL,KAAA;AAAA,MACA,sBAAsB,MAAA,CAAO,UAAA,CAAW,KAAA,EAAO,KAAA,CAAM,IAAI,CAAA,IACvD;AAAA;AACJ,GACF,CAAE,KAAK,EAAC;AAEV,EAAA,MAAM,YAAY,aAAA,mBAChB,GAAA;AAAA,IAAC,aAAA;AAAA,IAAA;AAAA,MACC,aAAA;AAAA,MACA,WAAA;AAAA,MACA,eAAe,MAAA,CAAO,KAAA;AAAA,MACtB,oBAAoB,MAAA,CAAO,kBAAA;AAAA,MAC3B,uBAAuB,MAAA,CAAO;AAAA;AAAA,GAChC,GACE,IAAA;AAEJ,EAAA,MAAM,OAAO,QAAA,mBACX,GAAA,CAAC,kBAAe,SAAA,EAAW,MAAA,CAAO,WAAW,CAAA,GAC3C,IAAA;AAEJ,EAAA,uBACE,GAAA;AAAA,IAAC,WAAA;AAAA,IAAA;AAAA,MACC,KAAA,EAAO,SAAA;AAAA,MACP,UAAA;AAAA,MACA,SAAA;AAAA,MACA,WAAA;AAAA,MACA,IAAA;AAAA,MAEA,+BAAC,kBAAA,EAAA,EACC,QAAA,EAAA;AAAA,wBAAA,GAAA;AAAA,UAAC,kBAAA;AAAA,UAAA;AAAA,YACC,OAAA;AAAA,YACA,eAAe,CAAA,KAAA,qBACb,GAAA;AAAA,cAAC,iBAAA;AAAA,cAAA;AAAA,gBACE,GAAG,KAAA;AAAA,gBACJ,WAAA;AAAA,gBACA,cAAA;AAAA,gBACA,kBAAA;AAAA,gBACA;AAAA;AAAA,aACF;AAAA,YAEF,cAAA,EAAgB,CAAC,EAAE,MAAA,EAAQ,SAAQ,qBACjC,GAAA;AAAA,cAAC,qBAAA;AAAA,cAAA;AAAA,gBACC,MAAA;AAAA,gBACA,OAAA;AAAA,gBACA;AAAA;AAAA;AACF;AAAA,SAEJ;AAAA,QAEC,WAAA,KAAgB,QAAQ,eAAA,oBACvB,GAAA;AAAA,UAAC,WAAA;AAAA,UAAA;AAAA,YACC,eAAA;AAAA,YACA,OAAA;AAAA,YACA,OAAA,EAAS;AAAA,cACP;AAAA,gBACE,IAAA,EAAM,OAAA,CAAQ,WAAW,CAAA,CAAE,IAAA;AAAA,gBAC3B,KAAA,EAAO,OAAA,CAAQ,WAAW,CAAA,CAAE,KAAA;AAAA,gBAC5B,OAAA,EAAS,QAAQ,WAAW;AAAA;AAC9B;AACF;AAAA;AACF,OAAA,EAEJ;AAAA;AAAA,GACF;AAEJ;;;;"}
1
+ {"version":3,"file":"StatusGroupedCardComponent.esm.js","sources":["../../../../src/components/AggregatedMetricCards/StatusGroupedCard/StatusGroupedCardComponent.tsx"],"sourcesContent":["/*\n * Copyright Red Hat, Inc.\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 */\n\nimport { useTheme } from '@mui/material/styles';\nimport { useState } from 'react';\nimport { PieData } from '../../types';\nimport type { TooltipPosition } from '../types';\nimport {\n getThresholdRuleColor,\n SCORECARD_ERROR_STATE_COLOR,\n resolveStatusColor,\n} from '../../../utils';\nimport { CardWrapper } from '../../Common/CardWrapper';\nimport { CardInfoButton } from '../components/CardInfoButton';\nimport { ResponsivePieChart } from '../../ScorecardHomepageSection/ResponsivePieChart';\nimport { CardLegendContent } from '../components/CardLegendContent';\nimport { CardPieTooltipContent } from '../components/CardPieTooltipContent';\nimport { CardChartContainer } from '../components/CardChartContainer';\nimport { CardSubheader } from '../components/CardSubheader';\nimport { CardTooltip } from '../components/CardTooltip';\nimport { StatusGroupedCardComponentProps } from './types';\n\nexport const StatusGroupedCardComponent = ({\n scorecard,\n cardTitle,\n description,\n aggregationId,\n showSubheader = true,\n showInfo = true,\n dataTestId,\n}: StatusGroupedCardComponentProps) => {\n const theme = useTheme();\n\n const [activeIndex, setActiveIndex] = useState<number | null>(null);\n const [tooltipPosition, setTooltipPosition] =\n useState<TooltipPosition | null>(null);\n\n const { id: scorecardId, result } = scorecard;\n\n const pieData: PieData[] =\n result.values?.map(value => ({\n name: value.name,\n value: value.count,\n color: resolveStatusColor(\n theme,\n getThresholdRuleColor(result.thresholds.rules, value.name) ??\n SCORECARD_ERROR_STATE_COLOR,\n ),\n })) ?? [];\n\n const subheader = showSubheader ? (\n <CardSubheader\n aggregationId={aggregationId}\n scorecardId={scorecardId}\n entitiesCount={result.total}\n entitiesConsidered={result.entitiesConsidered}\n calculationErrorCount={result.calculationErrorCount}\n />\n ) : null;\n\n const info = showInfo ? (\n <CardInfoButton timestamp={result.timestamp} />\n ) : null;\n\n return (\n <CardWrapper\n title={cardTitle}\n dataTestId={dataTestId}\n subheader={subheader}\n description={description}\n info={info}\n >\n <CardChartContainer>\n <ResponsivePieChart\n pieData={pieData}\n legendContent={props => (\n <CardLegendContent\n {...props}\n activeIndex={activeIndex}\n setActiveIndex={setActiveIndex}\n setTooltipPosition={setTooltipPosition}\n pieData={pieData}\n />\n )}\n tooltipContent={({ active, payload }) => (\n <CardPieTooltipContent\n active={active}\n payload={payload}\n pieData={pieData}\n />\n )}\n />\n\n {activeIndex !== null && tooltipPosition && (\n <CardTooltip\n tooltipPosition={tooltipPosition}\n pieData={pieData}\n payload={[\n {\n name: pieData[activeIndex].name,\n value: pieData[activeIndex].value,\n payload: pieData[activeIndex],\n },\n ]}\n />\n )}\n </CardChartContainer>\n </CardWrapper>\n );\n};\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;AAmCO,MAAM,6BAA6B,CAAC;AAAA,EACzC,SAAA;AAAA,EACA,SAAA;AAAA,EACA,WAAA;AAAA,EACA,aAAA;AAAA,EACA,aAAA,GAAgB,IAAA;AAAA,EAChB,QAAA,GAAW,IAAA;AAAA,EACX;AACF,CAAA,KAAuC;AACrC,EAAA,MAAM,QAAQ,QAAA,EAAS;AAEvB,EAAA,MAAM,CAAC,WAAA,EAAa,cAAc,CAAA,GAAI,SAAwB,IAAI,CAAA;AAClE,EAAA,MAAM,CAAC,eAAA,EAAiB,kBAAkB,CAAA,GACxC,SAAiC,IAAI,CAAA;AAEvC,EAAA,MAAM,EAAE,EAAA,EAAI,WAAA,EAAa,MAAA,EAAO,GAAI,SAAA;AAEpC,EAAA,MAAM,OAAA,GACJ,MAAA,CAAO,MAAA,EAAQ,GAAA,CAAI,CAAA,KAAA,MAAU;AAAA,IAC3B,MAAM,KAAA,CAAM,IAAA;AAAA,IACZ,OAAO,KAAA,CAAM,KAAA;AAAA,IACb,KAAA,EAAO,kBAAA;AAAA,MACL,KAAA;AAAA,MACA,sBAAsB,MAAA,CAAO,UAAA,CAAW,KAAA,EAAO,KAAA,CAAM,IAAI,CAAA,IACvD;AAAA;AACJ,GACF,CAAE,KAAK,EAAC;AAEV,EAAA,MAAM,YAAY,aAAA,mBAChB,GAAA;AAAA,IAAC,aAAA;AAAA,IAAA;AAAA,MACC,aAAA;AAAA,MACA,WAAA;AAAA,MACA,eAAe,MAAA,CAAO,KAAA;AAAA,MACtB,oBAAoB,MAAA,CAAO,kBAAA;AAAA,MAC3B,uBAAuB,MAAA,CAAO;AAAA;AAAA,GAChC,GACE,IAAA;AAEJ,EAAA,MAAM,OAAO,QAAA,mBACX,GAAA,CAAC,kBAAe,SAAA,EAAW,MAAA,CAAO,WAAW,CAAA,GAC3C,IAAA;AAEJ,EAAA,uBACE,GAAA;AAAA,IAAC,WAAA;AAAA,IAAA;AAAA,MACC,KAAA,EAAO,SAAA;AAAA,MACP,UAAA;AAAA,MACA,SAAA;AAAA,MACA,WAAA;AAAA,MACA,IAAA;AAAA,MAEA,+BAAC,kBAAA,EAAA,EACC,QAAA,EAAA;AAAA,wBAAA,GAAA;AAAA,UAAC,kBAAA;AAAA,UAAA;AAAA,YACC,OAAA;AAAA,YACA,eAAe,CAAA,KAAA,qBACb,GAAA;AAAA,cAAC,iBAAA;AAAA,cAAA;AAAA,gBACE,GAAG,KAAA;AAAA,gBACJ,WAAA;AAAA,gBACA,cAAA;AAAA,gBACA,kBAAA;AAAA,gBACA;AAAA;AAAA,aACF;AAAA,YAEF,cAAA,EAAgB,CAAC,EAAE,MAAA,EAAQ,SAAQ,qBACjC,GAAA;AAAA,cAAC,qBAAA;AAAA,cAAA;AAAA,gBACC,MAAA;AAAA,gBACA,OAAA;AAAA,gBACA;AAAA;AAAA;AACF;AAAA,SAEJ;AAAA,QAEC,WAAA,KAAgB,QAAQ,eAAA,oBACvB,GAAA;AAAA,UAAC,WAAA;AAAA,UAAA;AAAA,YACC,eAAA;AAAA,YACA,OAAA;AAAA,YACA,OAAA,EAAS;AAAA,cACP;AAAA,gBACE,IAAA,EAAM,OAAA,CAAQ,WAAW,CAAA,CAAE,IAAA;AAAA,gBAC3B,KAAA,EAAO,OAAA,CAAQ,WAAW,CAAA,CAAE,KAAA;AAAA,gBAC5B,OAAA,EAAS,QAAQ,WAAW;AAAA;AAC9B;AACF;AAAA;AACF,OAAA,EAEJ;AAAA;AAAA,GACF;AAEJ;;;;"}
@@ -5,8 +5,8 @@ import Typography from '@mui/material/Typography';
5
5
  import { TooltipContent, formatAggregationScoreDetail } from './TooltipContent.esm.js';
6
6
  import { useTranslation } from '../../../hooks/useTranslation.esm.js';
7
7
  import 'date-fns';
8
- import { getTranslatedStatus } from '../../../utils/statusUtils.esm.js';
9
8
  import '@red-hat-developer-hub/backstage-plugin-scorecard-common';
9
+ import { getTranslatedStatus } from '../../../utils/statusUtils.esm.js';
10
10
 
11
11
  const DonutChartTooltipContent = ({
12
12
  weightedSum,
@@ -3,8 +3,8 @@ import { useState } from 'react';
3
3
  import { useTheme } from '@mui/material/styles';
4
4
  import { CardWrapper } from '../../Common/CardWrapper.esm.js';
5
5
  import 'date-fns';
6
- import { resolveStatusColor } from '../../../utils/statusUtils.esm.js';
7
6
  import '@red-hat-developer-hub/backstage-plugin-scorecard-common';
7
+ import { resolveStatusColor } from '../../../utils/statusUtils.esm.js';
8
8
  import { ResponsivePieChart } from '../../ScorecardHomepageSection/ResponsivePieChart.esm.js';
9
9
  import { CardInfoButton } from '../components/CardInfoButton.esm.js';
10
10
  import { CardSubheader } from '../components/CardSubheader.esm.js';
@@ -41,10 +41,7 @@ const WeightedStatusScoreCardComponent = ({
41
41
  const weightedStatusScorePercent = scorecard.result.weightedStatusScore;
42
42
  const { fill: chartFillPercent, remainder: chartRemainderPercent } = clampPercentForDonut(weightedStatusScorePercent);
43
43
  const centerPercentLabel = `${formatPercentage(weightedStatusScorePercent)}%`;
44
- const arcResolvedColor = resolveStatusColor(
45
- theme,
46
- scorecard.result.aggregationChartDisplayColor
47
- );
44
+ const arcResolvedColor = scorecard.result.aggregationChartDisplayColor ? resolveStatusColor(theme, scorecard.result.aggregationChartDisplayColor) : theme.palette.grey[300];
48
45
  const weightedStatusScorePieData = [
49
46
  {
50
47
  name: WEIGHTED_STATUS_SCORE_SLICE,