@red-hat-developer-hub/backstage-plugin-scorecard 2.7.0 → 2.7.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/dist/alpha.d.ts +1 -1
  3. package/dist/api/index.esm.js +45 -1
  4. package/dist/api/index.esm.js.map +1 -1
  5. package/dist/components/AggregatedMetricCards/AverageCard/AverageCardComponent.esm.js +3 -1
  6. package/dist/components/AggregatedMetricCards/AverageCard/AverageCardComponent.esm.js.map +1 -1
  7. package/dist/components/AggregatedMetricCards/StatusGroupedCard/StatusGroupedCardComponent.esm.js +3 -1
  8. package/dist/components/AggregatedMetricCards/StatusGroupedCard/StatusGroupedCardComponent.esm.js.map +1 -1
  9. package/dist/components/AggregatedMetricCards/components/CardSubheader.esm.js +26 -12
  10. package/dist/components/AggregatedMetricCards/components/CardSubheader.esm.js.map +1 -1
  11. package/dist/components/Common/CardWrapper.esm.js +2 -1
  12. package/dist/components/Common/CardWrapper.esm.js.map +1 -1
  13. package/dist/components/ScorecardHomepageSection/ScorecardHomepageCard.esm.js +12 -1
  14. package/dist/components/ScorecardHomepageSection/ScorecardHomepageCard.esm.js.map +1 -1
  15. package/dist/components/ScorecardPage/EntitiesTable/EntitiesTable.esm.js +72 -64
  16. package/dist/components/ScorecardPage/EntitiesTable/EntitiesTable.esm.js.map +1 -1
  17. package/dist/components/ScorecardPage/EntitiesTable/EntitiesTableWrapper.esm.js +2 -2
  18. package/dist/components/ScorecardPage/EntitiesTable/EntitiesTableWrapper.esm.js.map +1 -1
  19. package/dist/index.d.ts +1 -1
  20. package/dist/translations/de.esm.js +28 -0
  21. package/dist/translations/de.esm.js.map +1 -1
  22. package/dist/translations/es.esm.js +28 -0
  23. package/dist/translations/es.esm.js.map +1 -1
  24. package/dist/translations/fr.esm.js +28 -0
  25. package/dist/translations/fr.esm.js.map +1 -1
  26. package/dist/translations/it.esm.js +28 -0
  27. package/dist/translations/it.esm.js.map +1 -1
  28. package/dist/translations/ja.esm.js +28 -0
  29. package/dist/translations/ja.esm.js.map +1 -1
  30. package/dist/translations/ref.esm.js +52 -1
  31. package/dist/translations/ref.esm.js.map +1 -1
  32. package/dist/types/{index.d-Uz1xsqdo.d.ts → index.d-Bzst_d5c.d.ts} +27 -0
  33. package/package.json +2 -2
package/CHANGELOG.md CHANGED
@@ -1,5 +1,21 @@
1
1
  # @red-hat-developer-hub/backstage-plugin-scorecard
2
2
 
3
+ ## 2.7.2
4
+
5
+ ### Patch Changes
6
+
7
+ - 04e95fe: Add translations for new sonarqube module.
8
+ - 04e95fe: Add tooltip to Scorecard header to show titles that are longer then one line
9
+ - @red-hat-developer-hub/backstage-plugin-scorecard-common@2.7.2
10
+
11
+ ## 2.7.1
12
+
13
+ ### Patch Changes
14
+
15
+ - 91e724f: Expose scorecard entity calculation health on drill-down and aggregation APIs, and align the drill-down warning plus homepage subheader with those counts.
16
+ - Updated dependencies [91e724f]
17
+ - @red-hat-developer-hub/backstage-plugin-scorecard-common@2.7.1
18
+
3
19
  ## 2.7.0
4
20
 
5
21
  ### Minor Changes
package/dist/alpha.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import * as react from 'react';
2
2
  import * as _backstage_frontend_plugin_api from '@backstage/frontend-plugin-api';
3
3
  import * as _backstage_core_plugin_api from '@backstage/core-plugin-api';
4
- export { a as scorecardTranslationRef, s as scorecardTranslations } from './types/index.d-Uz1xsqdo.js';
4
+ export { a as scorecardTranslationRef, s as scorecardTranslations } from './types/index.d-Bzst_d5c.js';
5
5
 
6
6
  /**
7
7
  * The primary Scorecard frontend plugin.
@@ -72,7 +72,51 @@ class ScorecardApiClient {
72
72
  "Invalid response format from aggregated scorecard API"
73
73
  );
74
74
  }
75
- return data;
75
+ const resultRaw = data.result;
76
+ if (resultRaw === null || typeof resultRaw !== "object" || Array.isArray(resultRaw)) {
77
+ throw new TypeError(
78
+ "Invalid response format from aggregated scorecard API: result must be a non-null object"
79
+ );
80
+ }
81
+ const requireFiniteNumber = (value, fieldName) => {
82
+ const n = Number(value ?? 0);
83
+ if (!Number.isFinite(n)) {
84
+ throw new TypeError(
85
+ `Invalid aggregated scorecard API response: ${fieldName} must be a finite number`
86
+ );
87
+ }
88
+ return n;
89
+ };
90
+ const resultRecord = resultRaw;
91
+ const timestampValue = resultRecord.timestamp;
92
+ let timestamp;
93
+ if (typeof timestampValue === "string") {
94
+ timestamp = timestampValue;
95
+ } else if (typeof timestampValue === "number" && Number.isFinite(timestampValue)) {
96
+ timestamp = String(timestampValue);
97
+ } else if (typeof timestampValue === "bigint") {
98
+ timestamp = timestampValue.toString();
99
+ } else if (timestampValue instanceof Date) {
100
+ timestamp = timestampValue.toISOString();
101
+ } else {
102
+ timestamp = "";
103
+ }
104
+ return {
105
+ ...data,
106
+ result: {
107
+ ...resultRecord,
108
+ total: requireFiniteNumber(resultRecord.total, "total"),
109
+ entitiesConsidered: requireFiniteNumber(
110
+ resultRecord.entitiesConsidered,
111
+ "entitiesConsidered"
112
+ ),
113
+ calculationErrorCount: requireFiniteNumber(
114
+ resultRecord.calculationErrorCount,
115
+ "calculationErrorCount"
116
+ ),
117
+ timestamp
118
+ }
119
+ };
76
120
  } catch (error) {
77
121
  if (error instanceof Error) {
78
122
  throw error;
@@ -1 +1 @@
1
- {"version":3,"file":"index.esm.js","sources":["../../src/api/index.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 {\n createApiRef,\n FetchApi,\n DiscoveryApi,\n} from '@backstage/core-plugin-api';\nimport type {\n MetricResult,\n AggregatedMetricResult,\n AggregationMetadata,\n Metric,\n EntityMetricDetailResponse,\n} from '@red-hat-developer-hub/backstage-plugin-scorecard-common';\n\nimport type { GetAggregatedScorecardEntitiesOptions } from '../components/types';\n\nexport { ScorecardQueryProvider } from './ScorecardQueryProvider';\n\nimport type {\n ScorecardApi,\n ScorecardApiClientOptions,\n ScorecardOptions,\n} from './types';\n\nexport const scorecardApiRef = createApiRef<ScorecardApi>({\n id: 'plugin.scorecard.service',\n});\n\n/**\n * Client implementation for the Scorecard API.\n * @public\n */\nexport class ScorecardApiClient implements ScorecardApi {\n private readonly fetchApi: FetchApi;\n private readonly discoveryApi: DiscoveryApi;\n\n constructor(options: ScorecardApiClientOptions) {\n this.fetchApi = options.fetchApi;\n this.discoveryApi = options.discoveryApi;\n }\n\n async getBaseUrl(): Promise<string> {\n return await this.discoveryApi.getBaseUrl('scorecard');\n }\n\n async getScorecards({\n entity,\n metricIds,\n }: ScorecardOptions): Promise<MetricResult[]> {\n if (\n !entity?.kind ||\n !entity?.metadata?.namespace ||\n !entity?.metadata?.name\n ) {\n throw new Error(\n 'Entity missing required properties for scorecard lookup',\n );\n }\n\n const baseUrl = await this.getBaseUrl();\n const url = new URL(\n `${baseUrl}/metrics/catalog/${entity.kind}/${entity.metadata.namespace}/${entity.metadata.name}`,\n );\n\n if (metricIds) {\n url.searchParams.set('metricIds', metricIds.join(','));\n }\n\n try {\n const response = await this.fetchApi.fetch(url.toString());\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(\n `Failed to fetch scorecards: ${response.status} ${response.statusText}. ${errorText}`,\n );\n }\n\n const data = await response.json();\n\n if (!Array.isArray(data)) {\n throw new Error('Invalid response format from scorecard API');\n }\n\n return data;\n } catch (error) {\n if (error instanceof Error) {\n throw error;\n }\n throw new Error(`Unexpected error fetching scorecards: ${String(error)}`);\n }\n }\n\n async getAggregatedScorecard(\n aggregationId: string,\n ): Promise<AggregatedMetricResult> {\n if (!aggregationId || aggregationId.trim() === '') {\n throw new Error('Aggregation ID is required for aggregated scorecards');\n }\n\n const baseUrl = await this.getBaseUrl();\n const url = new URL(`${baseUrl}/aggregations/${aggregationId}`);\n\n try {\n const response = await this.fetchApi.fetch(url.toString());\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(\n `Failed to fetch aggregated scorecards: ${response.status} ${response.statusText}. ${errorText}`,\n );\n }\n\n const data = await response.json();\n\n if (\n !data ||\n Array.isArray(data) ||\n typeof data !== 'object' ||\n !('result' in data) ||\n !('metadata' in data) ||\n !('id' in data) ||\n !('status' in data)\n ) {\n throw new TypeError(\n 'Invalid response format from aggregated scorecard API',\n );\n }\n\n return data;\n } catch (error) {\n if (error instanceof Error) {\n throw error;\n }\n throw new Error(\n `Unexpected error fetching aggregated scorecards: ${String(error)}`,\n );\n }\n }\n\n async getMetrics(options?: {\n metricIds?: string[];\n }): Promise<{ metrics: Metric[] }> {\n const { metricIds } = options || {};\n\n const isMetricIds =\n metricIds && Array.isArray(metricIds) && metricIds.length > 0;\n\n const baseUrl = await this.getBaseUrl();\n const url = new URL(`${baseUrl}/metrics`);\n\n if (isMetricIds) {\n url.searchParams.set('metricIds', metricIds.join(','));\n }\n\n try {\n const response = await this.fetchApi.fetch(url.toString());\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(\n `Failed to fetch metric: ${response.status} ${response.statusText}. ${errorText}`,\n );\n }\n\n const data = await response.json();\n\n if (\n !data ||\n Array.isArray(data) ||\n typeof data !== 'object' ||\n !('metrics' in data) ||\n !Array.isArray(data.metrics)\n ) {\n throw new TypeError('Invalid response format from metrics API');\n }\n\n return data;\n } catch (error) {\n if (error instanceof Error) {\n throw error;\n }\n throw new Error(`Unexpected error fetching metric: ${String(error)}`);\n }\n }\n\n async getAggregatedScorecardEntities(\n options: GetAggregatedScorecardEntitiesOptions,\n ): Promise<EntityMetricDetailResponse> {\n const {\n metricId,\n page,\n pageSize,\n ownershipEntityRefs = [],\n orderBy = null,\n order = 'asc',\n } = options;\n\n if (!metricId) {\n throw new Error('Metric ID is required for aggregated scorecards');\n }\n\n const baseUrl = await this.getBaseUrl();\n const url = new URL(\n `${baseUrl}/metrics/${metricId}/catalog/aggregations/entities`,\n );\n if (page) {\n url.searchParams.append('page', page.toString());\n }\n if (pageSize) {\n url.searchParams.append('pageSize', pageSize.toString());\n }\n if (ownershipEntityRefs.length > 0) {\n for (const ownershipEntityRef of ownershipEntityRefs) {\n url.searchParams.append('owner', ownershipEntityRef);\n }\n }\n if (orderBy) {\n url.searchParams.append('sortBy', orderBy);\n url.searchParams.append('sortOrder', order);\n }\n\n try {\n const response = await this.fetchApi.fetch(url.toString());\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(\n `Failed to fetch aggregated scorecards: ${response.status} ${response.statusText}. ${errorText}`,\n );\n }\n\n const data = await response.json();\n\n if (!data || Array.isArray(data) || typeof data !== 'object') {\n throw new TypeError(\n 'Invalid response format from aggregated scorecard API',\n );\n }\n\n return data;\n } catch (error) {\n if (error instanceof Error) {\n throw error;\n }\n throw new Error(\n `Unexpected error fetching aggregated scorecards: ${String(error)}`,\n );\n }\n }\n\n async getAggregationMetadata(\n aggregationId: string,\n ): Promise<AggregationMetadata> {\n if (!aggregationId || aggregationId.trim() === '') {\n throw new Error('Aggregation ID is required for aggregation metadata');\n }\n\n const baseUrl = await this.getBaseUrl();\n const url = new URL(`${baseUrl}/aggregations/${aggregationId}/metadata`);\n\n try {\n const response = await this.fetchApi.fetch(url.toString());\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(\n `Failed to fetch aggregation metadata: ${response.status} ${response.statusText}. ${errorText}`,\n );\n }\n\n const data = await response.json();\n\n if (\n !data ||\n Array.isArray(data) ||\n typeof data !== 'object' ||\n !('title' in data) ||\n !('description' in data) ||\n !('type' in data) ||\n !('aggregationType' in data)\n ) {\n throw new TypeError(\n 'Invalid response format from aggregation metadata API',\n );\n }\n\n return data as AggregationMetadata;\n } catch (error) {\n if (error instanceof Error) {\n throw error;\n }\n throw new Error(\n `Unexpected error fetching aggregation metadata: ${String(error)}`,\n );\n }\n }\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.esm.js","sources":["../../src/api/index.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 {\n createApiRef,\n FetchApi,\n DiscoveryApi,\n} from '@backstage/core-plugin-api';\nimport type {\n MetricResult,\n AggregatedMetricResult,\n AggregationMetadata,\n Metric,\n EntityMetricDetailResponse,\n} from '@red-hat-developer-hub/backstage-plugin-scorecard-common';\n\nimport type { GetAggregatedScorecardEntitiesOptions } from '../components/types';\n\nexport { ScorecardQueryProvider } from './ScorecardQueryProvider';\n\nimport type {\n ScorecardApi,\n ScorecardApiClientOptions,\n ScorecardOptions,\n} from './types';\n\nexport const scorecardApiRef = createApiRef<ScorecardApi>({\n id: 'plugin.scorecard.service',\n});\n\n/**\n * Client implementation for the Scorecard API.\n * @public\n */\nexport class ScorecardApiClient implements ScorecardApi {\n private readonly fetchApi: FetchApi;\n private readonly discoveryApi: DiscoveryApi;\n\n constructor(options: ScorecardApiClientOptions) {\n this.fetchApi = options.fetchApi;\n this.discoveryApi = options.discoveryApi;\n }\n\n async getBaseUrl(): Promise<string> {\n return await this.discoveryApi.getBaseUrl('scorecard');\n }\n\n async getScorecards({\n entity,\n metricIds,\n }: ScorecardOptions): Promise<MetricResult[]> {\n if (\n !entity?.kind ||\n !entity?.metadata?.namespace ||\n !entity?.metadata?.name\n ) {\n throw new Error(\n 'Entity missing required properties for scorecard lookup',\n );\n }\n\n const baseUrl = await this.getBaseUrl();\n const url = new URL(\n `${baseUrl}/metrics/catalog/${entity.kind}/${entity.metadata.namespace}/${entity.metadata.name}`,\n );\n\n if (metricIds) {\n url.searchParams.set('metricIds', metricIds.join(','));\n }\n\n try {\n const response = await this.fetchApi.fetch(url.toString());\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(\n `Failed to fetch scorecards: ${response.status} ${response.statusText}. ${errorText}`,\n );\n }\n\n const data = await response.json();\n\n if (!Array.isArray(data)) {\n throw new Error('Invalid response format from scorecard API');\n }\n\n return data;\n } catch (error) {\n if (error instanceof Error) {\n throw error;\n }\n throw new Error(`Unexpected error fetching scorecards: ${String(error)}`);\n }\n }\n\n async getAggregatedScorecard(\n aggregationId: string,\n ): Promise<AggregatedMetricResult> {\n if (!aggregationId || aggregationId.trim() === '') {\n throw new Error('Aggregation ID is required for aggregated scorecards');\n }\n\n const baseUrl = await this.getBaseUrl();\n const url = new URL(`${baseUrl}/aggregations/${aggregationId}`);\n\n try {\n const response = await this.fetchApi.fetch(url.toString());\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(\n `Failed to fetch aggregated scorecards: ${response.status} ${response.statusText}. ${errorText}`,\n );\n }\n\n const data = await response.json();\n\n if (\n !data ||\n Array.isArray(data) ||\n typeof data !== 'object' ||\n !('result' in data) ||\n !('metadata' in data) ||\n !('id' in data) ||\n !('status' in data)\n ) {\n throw new TypeError(\n 'Invalid response format from aggregated scorecard API',\n );\n }\n\n const resultRaw = data.result;\n if (\n resultRaw === null ||\n typeof resultRaw !== 'object' ||\n Array.isArray(resultRaw)\n ) {\n throw new TypeError(\n 'Invalid response format from aggregated scorecard API: result must be a non-null object',\n );\n }\n\n const requireFiniteNumber = (value: unknown, fieldName: string) => {\n const n = Number(value ?? 0);\n if (!Number.isFinite(n)) {\n throw new TypeError(\n `Invalid aggregated scorecard API response: ${fieldName} must be a finite number`,\n );\n }\n return n;\n };\n\n const resultRecord = resultRaw as Record<string, unknown>;\n const timestampValue = resultRecord.timestamp;\n let timestamp: string;\n if (typeof timestampValue === 'string') {\n timestamp = timestampValue;\n } else if (\n typeof timestampValue === 'number' &&\n Number.isFinite(timestampValue)\n ) {\n timestamp = String(timestampValue);\n } else if (typeof timestampValue === 'bigint') {\n timestamp = timestampValue.toString();\n } else if (timestampValue instanceof Date) {\n timestamp = timestampValue.toISOString();\n } else {\n timestamp = '';\n }\n\n return {\n ...data,\n result: {\n ...resultRecord,\n total: requireFiniteNumber(resultRecord.total, 'total'),\n entitiesConsidered: requireFiniteNumber(\n resultRecord.entitiesConsidered,\n 'entitiesConsidered',\n ),\n calculationErrorCount: requireFiniteNumber(\n resultRecord.calculationErrorCount,\n 'calculationErrorCount',\n ),\n timestamp,\n },\n } as AggregatedMetricResult;\n } catch (error) {\n if (error instanceof Error) {\n throw error;\n }\n throw new Error(\n `Unexpected error fetching aggregated scorecards: ${String(error)}`,\n );\n }\n }\n\n async getMetrics(options?: {\n metricIds?: string[];\n }): Promise<{ metrics: Metric[] }> {\n const { metricIds } = options || {};\n\n const isMetricIds =\n metricIds && Array.isArray(metricIds) && metricIds.length > 0;\n\n const baseUrl = await this.getBaseUrl();\n const url = new URL(`${baseUrl}/metrics`);\n\n if (isMetricIds) {\n url.searchParams.set('metricIds', metricIds.join(','));\n }\n\n try {\n const response = await this.fetchApi.fetch(url.toString());\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(\n `Failed to fetch metric: ${response.status} ${response.statusText}. ${errorText}`,\n );\n }\n\n const data = await response.json();\n\n if (\n !data ||\n Array.isArray(data) ||\n typeof data !== 'object' ||\n !('metrics' in data) ||\n !Array.isArray(data.metrics)\n ) {\n throw new TypeError('Invalid response format from metrics API');\n }\n\n return data;\n } catch (error) {\n if (error instanceof Error) {\n throw error;\n }\n throw new Error(`Unexpected error fetching metric: ${String(error)}`);\n }\n }\n\n async getAggregatedScorecardEntities(\n options: GetAggregatedScorecardEntitiesOptions,\n ): Promise<EntityMetricDetailResponse> {\n const {\n metricId,\n page,\n pageSize,\n ownershipEntityRefs = [],\n orderBy = null,\n order = 'asc',\n } = options;\n\n if (!metricId) {\n throw new Error('Metric ID is required for aggregated scorecards');\n }\n\n const baseUrl = await this.getBaseUrl();\n const url = new URL(\n `${baseUrl}/metrics/${metricId}/catalog/aggregations/entities`,\n );\n if (page) {\n url.searchParams.append('page', page.toString());\n }\n if (pageSize) {\n url.searchParams.append('pageSize', pageSize.toString());\n }\n if (ownershipEntityRefs.length > 0) {\n for (const ownershipEntityRef of ownershipEntityRefs) {\n url.searchParams.append('owner', ownershipEntityRef);\n }\n }\n if (orderBy) {\n url.searchParams.append('sortBy', orderBy);\n url.searchParams.append('sortOrder', order);\n }\n\n try {\n const response = await this.fetchApi.fetch(url.toString());\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(\n `Failed to fetch aggregated scorecards: ${response.status} ${response.statusText}. ${errorText}`,\n );\n }\n\n const data = await response.json();\n\n if (!data || Array.isArray(data) || typeof data !== 'object') {\n throw new TypeError(\n 'Invalid response format from aggregated scorecard API',\n );\n }\n\n return data;\n } catch (error) {\n if (error instanceof Error) {\n throw error;\n }\n throw new Error(\n `Unexpected error fetching aggregated scorecards: ${String(error)}`,\n );\n }\n }\n\n async getAggregationMetadata(\n aggregationId: string,\n ): Promise<AggregationMetadata> {\n if (!aggregationId || aggregationId.trim() === '') {\n throw new Error('Aggregation ID is required for aggregation metadata');\n }\n\n const baseUrl = await this.getBaseUrl();\n const url = new URL(`${baseUrl}/aggregations/${aggregationId}/metadata`);\n\n try {\n const response = await this.fetchApi.fetch(url.toString());\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(\n `Failed to fetch aggregation metadata: ${response.status} ${response.statusText}. ${errorText}`,\n );\n }\n\n const data = await response.json();\n\n if (\n !data ||\n Array.isArray(data) ||\n typeof data !== 'object' ||\n !('title' in data) ||\n !('description' in data) ||\n !('type' in data) ||\n !('aggregationType' in data)\n ) {\n throw new TypeError(\n 'Invalid response format from aggregation metadata API',\n );\n }\n\n return data as AggregationMetadata;\n } catch (error) {\n if (error instanceof Error) {\n throw error;\n }\n throw new Error(\n `Unexpected error fetching aggregation metadata: ${String(error)}`,\n );\n }\n }\n}\n"],"names":[],"mappings":"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"}
@@ -76,7 +76,9 @@ const AverageCardComponent = ({
76
76
  {
77
77
  aggregationId,
78
78
  scorecardId: scorecard.id,
79
- entitiesCount: scorecard.result.total
79
+ entitiesCount: scorecard.result.total,
80
+ entitiesConsidered: scorecard.result.entitiesConsidered,
81
+ calculationErrorCount: scorecard.result.calculationErrorCount
80
82
  }
81
83
  ) : null;
82
84
  const info = showInfo ? /* @__PURE__ */ jsx(CardInfoButton, { timestamp: scorecard.result.timestamp }) : null;
@@ -1 +1 @@
1
- {"version":3,"file":"AverageCardComponent.esm.js","sources":["../../../../src/components/AggregatedMetricCards/AverageCard/AverageCardComponent.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 { useState } from 'react';\nimport type { MouseEvent } from 'react';\n\nimport { useTheme } from '@mui/material/styles';\n\nimport { CardWrapper } from '../../Common/CardWrapper';\nimport type { PieData } from '../../types';\nimport {\n getThresholdRuleColor,\n resolveStatusColor,\n SCORECARD_ERROR_STATE_COLOR,\n} from '../../../utils';\nimport { ResponsivePieChart } from '../../ScorecardHomepageSection/ResponsivePieChart';\nimport { CardInfoButton } from '../components/CardInfoButton';\nimport { CardSubheader } from '../components/CardSubheader';\nimport { CardChartContainer } from '../components/CardChartContainer';\nimport { CardTooltip } from '../components/CardTooltip';\nimport { LegendTooltipContent } from './LegendTooltipContent';\nimport { DonutChartTooltipContent } from './DonutChartTooltipContent';\nimport type { AverageCardComponentProps, TooltipPosition } from './types';\nimport { CardLegendContent } from '../components/CardLegendContent';\nimport { AverageCardPieCenterLabel } from './AverageCardPieCenterLabel';\nimport { formatPercentage } from '../../../utils/formatPercentage';\n\nconst AVERAGE_SCORE_SLICE = 'averageScoreFill';\nconst AVERAGE_REMAINDER_SLICE = 'averageScoreRemainder';\n\nfunction clampPercentForDonut(rawPercent: number): {\n fill: number;\n remainder: number;\n} {\n const fill = Math.min(100, Math.max(0, rawPercent));\n return { fill, remainder: 100 - fill };\n}\n\nexport const AverageCardComponent = ({\n scorecard,\n cardTitle,\n description,\n aggregationId,\n showSubheader = true,\n showInfo = true,\n dataTestId,\n}: AverageCardComponentProps) => {\n const theme = useTheme();\n\n const [activeIndex, setActiveIndex] = useState<number | null>(null);\n const [tooltipPosition, setTooltipPosition] =\n useState<TooltipPosition | null>(null);\n const [centerTooltipPosition, setCenterTooltipPosition] =\n useState<TooltipPosition | null>(null);\n\n const updateCenterTooltipPosition = (e: MouseEvent<SVGCircleElement>) => {\n const rect = e.currentTarget.getBoundingClientRect();\n setCenterTooltipPosition({\n left: rect.left + rect.width / 2,\n top: rect.top,\n });\n };\n\n const rawPercent = scorecard.result.averageScore * 100;\n const { fill: chartFillPercent, remainder: chartRemainderPercent } =\n clampPercentForDonut(rawPercent);\n\n const centerPercentLabel = `${formatPercentage(rawPercent)}%`;\n\n const arcResolvedColor = resolveStatusColor(\n theme,\n scorecard.result.aggregationChartDisplayColor,\n );\n\n const averagePieData: PieData[] = [\n {\n name: AVERAGE_SCORE_SLICE,\n value: chartFillPercent,\n color: arcResolvedColor,\n },\n {\n name: AVERAGE_REMAINDER_SLICE,\n value: chartRemainderPercent,\n color: theme.palette.grey[300],\n },\n ];\n\n const statusPieData: PieData[] =\n scorecard.result.values?.map(value => ({\n name: value.name,\n value: value.count,\n score: value.score,\n color: resolveStatusColor(\n theme,\n getThresholdRuleColor(scorecard.result.thresholds.rules, value.name) ??\n SCORECARD_ERROR_STATE_COLOR,\n ),\n })) ?? [];\n\n const subheader = showSubheader ? (\n <CardSubheader\n aggregationId={aggregationId}\n scorecardId={scorecard.id}\n entitiesCount={scorecard.result.total}\n />\n ) : null;\n\n const info = showInfo ? (\n <CardInfoButton timestamp={scorecard.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={averagePieData}\n LabelContent={props => (\n <AverageCardPieCenterLabel\n {...props}\n centerPercentLabel={centerPercentLabel}\n arcResolvedColor={arcResolvedColor}\n setActiveIndex={setActiveIndex}\n setTooltipPosition={setTooltipPosition}\n updateCenterTooltipPosition={updateCenterTooltipPosition}\n setCenterTooltipPosition={setCenterTooltipPosition}\n />\n )}\n legendContent={props => (\n <CardLegendContent\n {...props}\n activeIndex={activeIndex}\n setActiveIndex={setActiveIndex}\n setTooltipPosition={setTooltipPosition}\n pieData={statusPieData}\n />\n )}\n />\n\n {centerTooltipPosition && (\n <CardTooltip\n tooltipPosition={centerTooltipPosition}\n pieData={averagePieData}\n payload={[\n {\n name: AVERAGE_SCORE_SLICE,\n value: 1,\n payload: averagePieData[0],\n },\n ]}\n customContent={\n <DonutChartTooltipContent\n weightedSum={scorecard.result.averageWeightedSum}\n maxPossible={scorecard.result.averageMaxPossible}\n />\n }\n />\n )}\n\n {activeIndex !== null &&\n tooltipPosition &&\n statusPieData[activeIndex] && (\n <CardTooltip\n tooltipPosition={tooltipPosition}\n pieData={statusPieData}\n payload={[\n {\n name: statusPieData[activeIndex].name,\n value: statusPieData[activeIndex].value || 1,\n payload: statusPieData[activeIndex],\n },\n ]}\n customContent={\n <LegendTooltipContent\n row={statusPieData[activeIndex]}\n maxPossible={scorecard.result.averageMaxPossible}\n />\n }\n />\n )}\n </CardChartContainer>\n </CardWrapper>\n );\n};\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"AverageCardComponent.esm.js","sources":["../../../../src/components/AggregatedMetricCards/AverageCard/AverageCardComponent.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 { useState } from 'react';\nimport type { MouseEvent } from 'react';\n\nimport { useTheme } from '@mui/material/styles';\n\nimport { CardWrapper } from '../../Common/CardWrapper';\nimport type { PieData } from '../../types';\nimport {\n getThresholdRuleColor,\n resolveStatusColor,\n SCORECARD_ERROR_STATE_COLOR,\n} from '../../../utils';\nimport { ResponsivePieChart } from '../../ScorecardHomepageSection/ResponsivePieChart';\nimport { CardInfoButton } from '../components/CardInfoButton';\nimport { CardSubheader } from '../components/CardSubheader';\nimport { CardChartContainer } from '../components/CardChartContainer';\nimport { CardTooltip } from '../components/CardTooltip';\nimport { LegendTooltipContent } from './LegendTooltipContent';\nimport { DonutChartTooltipContent } from './DonutChartTooltipContent';\nimport type { AverageCardComponentProps, TooltipPosition } from './types';\nimport { CardLegendContent } from '../components/CardLegendContent';\nimport { AverageCardPieCenterLabel } from './AverageCardPieCenterLabel';\nimport { formatPercentage } from '../../../utils/formatPercentage';\n\nconst AVERAGE_SCORE_SLICE = 'averageScoreFill';\nconst AVERAGE_REMAINDER_SLICE = 'averageScoreRemainder';\n\nfunction clampPercentForDonut(rawPercent: number): {\n fill: number;\n remainder: number;\n} {\n const fill = Math.min(100, Math.max(0, rawPercent));\n return { fill, remainder: 100 - fill };\n}\n\nexport const AverageCardComponent = ({\n scorecard,\n cardTitle,\n description,\n aggregationId,\n showSubheader = true,\n showInfo = true,\n dataTestId,\n}: AverageCardComponentProps) => {\n const theme = useTheme();\n\n const [activeIndex, setActiveIndex] = useState<number | null>(null);\n const [tooltipPosition, setTooltipPosition] =\n useState<TooltipPosition | null>(null);\n const [centerTooltipPosition, setCenterTooltipPosition] =\n useState<TooltipPosition | null>(null);\n\n const updateCenterTooltipPosition = (e: MouseEvent<SVGCircleElement>) => {\n const rect = e.currentTarget.getBoundingClientRect();\n setCenterTooltipPosition({\n left: rect.left + rect.width / 2,\n top: rect.top,\n });\n };\n\n const rawPercent = scorecard.result.averageScore * 100;\n const { fill: chartFillPercent, remainder: chartRemainderPercent } =\n clampPercentForDonut(rawPercent);\n\n const centerPercentLabel = `${formatPercentage(rawPercent)}%`;\n\n const arcResolvedColor = resolveStatusColor(\n theme,\n scorecard.result.aggregationChartDisplayColor,\n );\n\n const averagePieData: PieData[] = [\n {\n name: AVERAGE_SCORE_SLICE,\n value: chartFillPercent,\n color: arcResolvedColor,\n },\n {\n name: AVERAGE_REMAINDER_SLICE,\n value: chartRemainderPercent,\n color: theme.palette.grey[300],\n },\n ];\n\n const statusPieData: PieData[] =\n scorecard.result.values?.map(value => ({\n name: value.name,\n value: value.count,\n score: value.score,\n color: resolveStatusColor(\n theme,\n getThresholdRuleColor(scorecard.result.thresholds.rules, value.name) ??\n SCORECARD_ERROR_STATE_COLOR,\n ),\n })) ?? [];\n\n const subheader = showSubheader ? (\n <CardSubheader\n aggregationId={aggregationId}\n scorecardId={scorecard.id}\n entitiesCount={scorecard.result.total}\n entitiesConsidered={scorecard.result.entitiesConsidered}\n calculationErrorCount={scorecard.result.calculationErrorCount}\n />\n ) : null;\n\n const info = showInfo ? (\n <CardInfoButton timestamp={scorecard.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={averagePieData}\n LabelContent={props => (\n <AverageCardPieCenterLabel\n {...props}\n centerPercentLabel={centerPercentLabel}\n arcResolvedColor={arcResolvedColor}\n setActiveIndex={setActiveIndex}\n setTooltipPosition={setTooltipPosition}\n updateCenterTooltipPosition={updateCenterTooltipPosition}\n setCenterTooltipPosition={setCenterTooltipPosition}\n />\n )}\n legendContent={props => (\n <CardLegendContent\n {...props}\n activeIndex={activeIndex}\n setActiveIndex={setActiveIndex}\n setTooltipPosition={setTooltipPosition}\n pieData={statusPieData}\n />\n )}\n />\n\n {centerTooltipPosition && (\n <CardTooltip\n tooltipPosition={centerTooltipPosition}\n pieData={averagePieData}\n payload={[\n {\n name: AVERAGE_SCORE_SLICE,\n value: 1,\n payload: averagePieData[0],\n },\n ]}\n customContent={\n <DonutChartTooltipContent\n weightedSum={scorecard.result.averageWeightedSum}\n maxPossible={scorecard.result.averageMaxPossible}\n />\n }\n />\n )}\n\n {activeIndex !== null &&\n tooltipPosition &&\n statusPieData[activeIndex] && (\n <CardTooltip\n tooltipPosition={tooltipPosition}\n pieData={statusPieData}\n payload={[\n {\n name: statusPieData[activeIndex].name,\n value: statusPieData[activeIndex].value || 1,\n payload: statusPieData[activeIndex],\n },\n ]}\n customContent={\n <LegendTooltipContent\n row={statusPieData[activeIndex]}\n maxPossible={scorecard.result.averageMaxPossible}\n />\n }\n />\n )}\n </CardChartContainer>\n </CardWrapper>\n );\n};\n"],"names":[],"mappings":"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"}
@@ -40,7 +40,9 @@ const StatusGroupedCardComponent = ({
40
40
  {
41
41
  aggregationId,
42
42
  scorecardId,
43
- entitiesCount: result.total
43
+ entitiesCount: result.total,
44
+ entitiesConsidered: result.entitiesConsidered,
45
+ calculationErrorCount: result.calculationErrorCount
44
46
  }
45
47
  ) : null;
46
48
  const info = showInfo ? /* @__PURE__ */ jsx(CardInfoButton, { timestamp: result.timestamp }) : null;
@@ -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 {\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] = useState<{\n left: number;\n top: number;\n } | 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 />\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":";;;;;;;;;;;;;;;;AAkCO,MAAM,6BAA6B,CAAC;AAAA,EACzC,SAAA;AAAA,EACA,SAAA;AAAA,EACA,WAAA;AAAA,EACA,aAAA;AAAA,EACA,aAAgB,GAAA,IAAA;AAAA,EAChB,QAAW,GAAA,IAAA;AAAA,EACX;AACF,CAAuC,KAAA;AACrC,EAAA,MAAM,QAAQ,QAAS,EAAA;AAEvB,EAAA,MAAM,CAAC,WAAA,EAAa,cAAc,CAAA,GAAI,SAAwB,IAAI,CAAA;AAClE,EAAA,MAAM,CAAC,eAAA,EAAiB,kBAAkB,CAAA,GAAI,SAGpC,IAAI,CAAA;AAEd,EAAA,MAAM,EAAE,EAAA,EAAI,WAAa,EAAA,MAAA,EAAW,GAAA,SAAA;AAEpC,EAAA,MAAM,OACJ,GAAA,MAAA,CAAO,MAAQ,EAAA,GAAA,CAAI,CAAU,KAAA,MAAA;AAAA,IAC3B,MAAM,KAAM,CAAA,IAAA;AAAA,IACZ,OAAO,KAAM,CAAA,KAAA;AAAA,IACb,KAAO,EAAA,kBAAA;AAAA,MACL,KAAA;AAAA,MACA,sBAAsB,MAAO,CAAA,UAAA,CAAW,KAAO,EAAA,KAAA,CAAM,IAAI,CACvD,IAAA;AAAA;AACJ,GACF,CAAE,KAAK,EAAC;AAEV,EAAA,MAAM,YAAY,aAChB,mBAAA,GAAA;AAAA,IAAC,aAAA;AAAA,IAAA;AAAA,MACC,aAAA;AAAA,MACA,WAAA;AAAA,MACA,eAAe,MAAO,CAAA;AAAA;AAAA,GAEtB,GAAA,IAAA;AAEJ,EAAA,MAAM,OAAO,QACX,mBAAA,GAAA,CAAC,kBAAe,SAAW,EAAA,MAAA,CAAO,WAAW,CAC3C,GAAA,IAAA;AAEJ,EACE,uBAAA,GAAA;AAAA,IAAC,WAAA;AAAA,IAAA;AAAA,MACC,KAAO,EAAA,SAAA;AAAA,MACP,UAAA;AAAA,MACA,SAAA;AAAA,MACA,WAAA;AAAA,MACA,IAAA;AAAA,MAEA,+BAAC,kBACC,EAAA,EAAA,QAAA,EAAA;AAAA,wBAAA,GAAA;AAAA,UAAC,kBAAA;AAAA,UAAA;AAAA,YACC,OAAA;AAAA,YACA,eAAe,CACb,KAAA,qBAAA,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,cAAgB,EAAA,CAAC,EAAE,MAAA,EAAQ,SACzB,qBAAA,GAAA;AAAA,cAAC,qBAAA;AAAA,cAAA;AAAA,gBACC,MAAA;AAAA,gBACA,OAAA;AAAA,gBACA;AAAA;AAAA;AACF;AAAA,SAEJ;AAAA,QAEC,WAAA,KAAgB,QAAQ,eACvB,oBAAA,GAAA;AAAA,UAAC,WAAA;AAAA,UAAA;AAAA,YACC,eAAA;AAAA,YACA,OAAA;AAAA,YACA,OAAS,EAAA;AAAA,cACP;AAAA,gBACE,IAAA,EAAM,OAAQ,CAAA,WAAW,CAAE,CAAA,IAAA;AAAA,gBAC3B,KAAA,EAAO,OAAQ,CAAA,WAAW,CAAE,CAAA,KAAA;AAAA,gBAC5B,OAAA,EAAS,QAAQ,WAAW;AAAA;AAC9B;AACF;AAAA;AACF,OAEJ,EAAA;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 {\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] = useState<{\n left: number;\n top: number;\n } | 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":";;;;;;;;;;;;;;;;AAkCO,MAAM,6BAA6B,CAAC;AAAA,EACzC,SAAA;AAAA,EACA,SAAA;AAAA,EACA,WAAA;AAAA,EACA,aAAA;AAAA,EACA,aAAgB,GAAA,IAAA;AAAA,EAChB,QAAW,GAAA,IAAA;AAAA,EACX;AACF,CAAuC,KAAA;AACrC,EAAA,MAAM,QAAQ,QAAS,EAAA;AAEvB,EAAA,MAAM,CAAC,WAAA,EAAa,cAAc,CAAA,GAAI,SAAwB,IAAI,CAAA;AAClE,EAAA,MAAM,CAAC,eAAA,EAAiB,kBAAkB,CAAA,GAAI,SAGpC,IAAI,CAAA;AAEd,EAAA,MAAM,EAAE,EAAA,EAAI,WAAa,EAAA,MAAA,EAAW,GAAA,SAAA;AAEpC,EAAA,MAAM,OACJ,GAAA,MAAA,CAAO,MAAQ,EAAA,GAAA,CAAI,CAAU,KAAA,MAAA;AAAA,IAC3B,MAAM,KAAM,CAAA,IAAA;AAAA,IACZ,OAAO,KAAM,CAAA,KAAA;AAAA,IACb,KAAO,EAAA,kBAAA;AAAA,MACL,KAAA;AAAA,MACA,sBAAsB,MAAO,CAAA,UAAA,CAAW,KAAO,EAAA,KAAA,CAAM,IAAI,CACvD,IAAA;AAAA;AACJ,GACF,CAAE,KAAK,EAAC;AAEV,EAAA,MAAM,YAAY,aAChB,mBAAA,GAAA;AAAA,IAAC,aAAA;AAAA,IAAA;AAAA,MACC,aAAA;AAAA,MACA,WAAA;AAAA,MACA,eAAe,MAAO,CAAA,KAAA;AAAA,MACtB,oBAAoB,MAAO,CAAA,kBAAA;AAAA,MAC3B,uBAAuB,MAAO,CAAA;AAAA;AAAA,GAE9B,GAAA,IAAA;AAEJ,EAAA,MAAM,OAAO,QACX,mBAAA,GAAA,CAAC,kBAAe,SAAW,EAAA,MAAA,CAAO,WAAW,CAC3C,GAAA,IAAA;AAEJ,EACE,uBAAA,GAAA;AAAA,IAAC,WAAA;AAAA,IAAA;AAAA,MACC,KAAO,EAAA,SAAA;AAAA,MACP,UAAA;AAAA,MACA,SAAA;AAAA,MACA,WAAA;AAAA,MACA,IAAA;AAAA,MAEA,+BAAC,kBACC,EAAA,EAAA,QAAA,EAAA;AAAA,wBAAA,GAAA;AAAA,UAAC,kBAAA;AAAA,UAAA;AAAA,YACC,OAAA;AAAA,YACA,eAAe,CACb,KAAA,qBAAA,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,cAAgB,EAAA,CAAC,EAAE,MAAA,EAAQ,SACzB,qBAAA,GAAA;AAAA,cAAC,qBAAA;AAAA,cAAA;AAAA,gBACC,MAAA;AAAA,gBACA,OAAA;AAAA,gBACA;AAAA;AAAA;AACF;AAAA,SAEJ;AAAA,QAEC,WAAA,KAAgB,QAAQ,eACvB,oBAAA,GAAA;AAAA,UAAC,WAAA;AAAA,UAAA;AAAA,YACC,eAAA;AAAA,YACA,OAAA;AAAA,YACA,OAAS,EAAA;AAAA,cACP;AAAA,gBACE,IAAA,EAAM,OAAQ,CAAA,WAAW,CAAE,CAAA,IAAA;AAAA,gBAC3B,KAAA,EAAO,OAAQ,CAAA,WAAW,CAAE,CAAA,KAAA;AAAA,gBAC5B,OAAA,EAAS,QAAQ,WAAW;AAAA;AAC9B;AACF;AAAA;AACF,OAEJ,EAAA;AAAA;AAAA,GACF;AAEJ;;;;"}
@@ -1,4 +1,5 @@
1
1
  import { jsx } from 'react/jsx-runtime';
2
+ import Box from '@mui/material/Box';
2
3
  import Tooltip from '@mui/material/Tooltip';
3
4
  import { Link } from '@backstage/core-components';
4
5
  import { useTranslation } from '../../../hooks/useTranslation.esm.js';
@@ -6,27 +7,40 @@ import { useTranslation } from '../../../hooks/useTranslation.esm.js';
6
7
  const CardSubheader = ({
7
8
  aggregationId,
8
9
  scorecardId,
9
- entitiesCount
10
+ entitiesCount,
11
+ entitiesConsidered = entitiesCount,
12
+ calculationErrorCount = 0
10
13
  }) => {
11
14
  const { t } = useTranslation();
12
- return /* @__PURE__ */ jsx(
15
+ const totalEntities = Math.max(0, entitiesConsidered);
16
+ const inferredErrorCount = Math.max(0, totalEntities - entitiesCount);
17
+ const effectiveErrorCount = Math.max(
18
+ calculationErrorCount,
19
+ inferredErrorCount
20
+ );
21
+ const hasCalculationErrors = effectiveErrorCount > 0;
22
+ const healthyEntitiesCount = Math.max(0, totalEntities - effectiveErrorCount);
23
+ const ratioTemplate = t("metric.homepageEntityHealthRatio");
24
+ const entitiesLabel = hasCalculationErrors ? ratioTemplate.replace("{{healthy}}", String(healthyEntitiesCount)).replace("{{total}}", String(totalEntities)) : t("thresholds.entities", { count: entitiesCount });
25
+ const linkNode = /* @__PURE__ */ jsx(
26
+ Link,
27
+ {
28
+ to: `/scorecard/aggregations/${encodeURIComponent(
29
+ aggregationId
30
+ )}/metrics/${encodeURIComponent(scorecardId)}`,
31
+ children: entitiesLabel
32
+ }
33
+ );
34
+ return /* @__PURE__ */ jsx(Box, { sx: { display: "inline-flex", alignItems: "center" }, children: hasCalculationErrors ? /* @__PURE__ */ jsx(
13
35
  Tooltip,
14
36
  {
15
37
  enterDelay: 1500,
16
38
  title: t("metric.someEntitiesNotReportingValues"),
17
39
  arrow: true,
18
40
  placement: "right",
19
- children: /* @__PURE__ */ jsx(
20
- Link,
21
- {
22
- to: `/scorecard/aggregations/${encodeURIComponent(
23
- aggregationId
24
- )}/metrics/${encodeURIComponent(scorecardId)}`,
25
- children: t("thresholds.entities", { count: entitiesCount })
26
- }
27
- )
41
+ children: linkNode
28
42
  }
29
- );
43
+ ) : linkNode });
30
44
  };
31
45
 
32
46
  export { CardSubheader };
@@ -1 +1 @@
1
- {"version":3,"file":"CardSubheader.esm.js","sources":["../../../../src/components/AggregatedMetricCards/components/CardSubheader.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 Tooltip from '@mui/material/Tooltip';\nimport { Link } from '@backstage/core-components';\nimport { useTranslation } from '../../../hooks/useTranslation';\n\ntype CardSubheaderProps = {\n aggregationId: string;\n scorecardId: string;\n entitiesCount: number;\n};\n\nexport const CardSubheader = ({\n aggregationId,\n scorecardId,\n entitiesCount,\n}: CardSubheaderProps) => {\n const { t } = useTranslation();\n\n return (\n <Tooltip\n enterDelay={1500}\n title={t('metric.someEntitiesNotReportingValues')}\n arrow\n placement=\"right\"\n >\n <Link\n to={`/scorecard/aggregations/${encodeURIComponent(\n aggregationId,\n )}/metrics/${encodeURIComponent(scorecardId)}`}\n >\n {t('thresholds.entities', { count: entitiesCount })}\n </Link>\n </Tooltip>\n );\n};\n"],"names":[],"mappings":";;;;;AA0BO,MAAM,gBAAgB,CAAC;AAAA,EAC5B,aAAA;AAAA,EACA,WAAA;AAAA,EACA;AACF,CAA0B,KAAA;AACxB,EAAM,MAAA,EAAE,CAAE,EAAA,GAAI,cAAe,EAAA;AAE7B,EACE,uBAAA,GAAA;AAAA,IAAC,OAAA;AAAA,IAAA;AAAA,MACC,UAAY,EAAA,IAAA;AAAA,MACZ,KAAA,EAAO,EAAE,uCAAuC,CAAA;AAAA,MAChD,KAAK,EAAA,IAAA;AAAA,MACL,SAAU,EAAA,OAAA;AAAA,MAEV,QAAA,kBAAA,GAAA;AAAA,QAAC,IAAA;AAAA,QAAA;AAAA,UACC,IAAI,CAA2B,wBAAA,EAAA,kBAAA;AAAA,YAC7B;AAAA,WACD,CAAA,SAAA,EAAY,kBAAmB,CAAA,WAAW,CAAC,CAAA,CAAA;AAAA,UAE3C,QAAE,EAAA,CAAA,CAAA,qBAAA,EAAuB,EAAE,KAAA,EAAO,eAAe;AAAA;AAAA;AACpD;AAAA,GACF;AAEJ;;;;"}
1
+ {"version":3,"file":"CardSubheader.esm.js","sources":["../../../../src/components/AggregatedMetricCards/components/CardSubheader.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 Tooltip from '@mui/material/Tooltip';\nimport { Link } from '@backstage/core-components';\nimport { useTranslation } from '../../../hooks/useTranslation';\n\ntype CardSubheaderProps = {\n aggregationId: string;\n scorecardId: string;\n entitiesCount: number;\n entitiesConsidered?: number;\n calculationErrorCount?: number;\n};\n\nexport const CardSubheader = ({\n aggregationId,\n scorecardId,\n entitiesCount,\n entitiesConsidered = entitiesCount,\n calculationErrorCount = 0,\n}: CardSubheaderProps) => {\n const { t } = useTranslation();\n const totalEntities = Math.max(0, entitiesConsidered);\n const inferredErrorCount = Math.max(0, totalEntities - entitiesCount);\n const effectiveErrorCount = Math.max(\n calculationErrorCount,\n inferredErrorCount,\n );\n const hasCalculationErrors = effectiveErrorCount > 0;\n const healthyEntitiesCount = Math.max(0, totalEntities - effectiveErrorCount);\n const ratioTemplate = t('metric.homepageEntityHealthRatio');\n const entitiesLabel = hasCalculationErrors\n ? ratioTemplate\n .replace('{{healthy}}', String(healthyEntitiesCount))\n .replace('{{total}}', String(totalEntities))\n : t('thresholds.entities', { count: entitiesCount });\n\n const linkNode = (\n <Link\n to={`/scorecard/aggregations/${encodeURIComponent(\n aggregationId,\n )}/metrics/${encodeURIComponent(scorecardId)}`}\n >\n {entitiesLabel}\n </Link>\n );\n\n return (\n <Box sx={{ display: 'inline-flex', alignItems: 'center' }}>\n {hasCalculationErrors ? (\n <Tooltip\n enterDelay={1500}\n title={t('metric.someEntitiesNotReportingValues')}\n arrow\n placement=\"right\"\n >\n {linkNode}\n </Tooltip>\n ) : (\n linkNode\n )}\n </Box>\n );\n};\n"],"names":[],"mappings":";;;;;;AA6BO,MAAM,gBAAgB,CAAC;AAAA,EAC5B,aAAA;AAAA,EACA,WAAA;AAAA,EACA,aAAA;AAAA,EACA,kBAAqB,GAAA,aAAA;AAAA,EACrB,qBAAwB,GAAA;AAC1B,CAA0B,KAAA;AACxB,EAAM,MAAA,EAAE,CAAE,EAAA,GAAI,cAAe,EAAA;AAC7B,EAAA,MAAM,aAAgB,GAAA,IAAA,CAAK,GAAI,CAAA,CAAA,EAAG,kBAAkB,CAAA;AACpD,EAAA,MAAM,kBAAqB,GAAA,IAAA,CAAK,GAAI,CAAA,CAAA,EAAG,gBAAgB,aAAa,CAAA;AACpE,EAAA,MAAM,sBAAsB,IAAK,CAAA,GAAA;AAAA,IAC/B,qBAAA;AAAA,IACA;AAAA,GACF;AACA,EAAA,MAAM,uBAAuB,mBAAsB,GAAA,CAAA;AACnD,EAAA,MAAM,oBAAuB,GAAA,IAAA,CAAK,GAAI,CAAA,CAAA,EAAG,gBAAgB,mBAAmB,CAAA;AAC5E,EAAM,MAAA,aAAA,GAAgB,EAAE,kCAAkC,CAAA;AAC1D,EAAM,MAAA,aAAA,GAAgB,uBAClB,aACG,CAAA,OAAA,CAAQ,eAAe,MAAO,CAAA,oBAAoB,CAAC,CACnD,CAAA,OAAA,CAAQ,aAAa,MAAO,CAAA,aAAa,CAAC,CAC7C,GAAA,CAAA,CAAE,uBAAuB,EAAE,KAAA,EAAO,eAAe,CAAA;AAErD,EAAA,MAAM,QACJ,mBAAA,GAAA;AAAA,IAAC,IAAA;AAAA,IAAA;AAAA,MACC,IAAI,CAA2B,wBAAA,EAAA,kBAAA;AAAA,QAC7B;AAAA,OACD,CAAA,SAAA,EAAY,kBAAmB,CAAA,WAAW,CAAC,CAAA,CAAA;AAAA,MAE3C,QAAA,EAAA;AAAA;AAAA,GACH;AAGF,EACE,uBAAA,GAAA,CAAC,OAAI,EAAI,EAAA,EAAE,SAAS,aAAe,EAAA,UAAA,EAAY,QAAS,EAAA,EACrD,QACC,EAAA,oBAAA,mBAAA,GAAA;AAAA,IAAC,OAAA;AAAA,IAAA;AAAA,MACC,UAAY,EAAA,IAAA;AAAA,MACZ,KAAA,EAAO,EAAE,uCAAuC,CAAA;AAAA,MAChD,KAAK,EAAA,IAAA;AAAA,MACL,SAAU,EAAA,OAAA;AAAA,MAET,QAAA,EAAA;AAAA;AAAA,MAGH,QAEJ,EAAA,CAAA;AAEJ;;;;"}
@@ -5,6 +5,7 @@ import CardContent from '@mui/material/CardContent';
5
5
  import Divider from '@mui/material/Divider';
6
6
  import Box from '@mui/material/Box';
7
7
  import Typography from '@mui/material/Typography';
8
+ import Tooltip from '@mui/material/Tooltip';
8
9
 
9
10
  const CardWrapper = ({
10
11
  children,
@@ -35,7 +36,7 @@ const CardWrapper = ({
35
36
  /* @__PURE__ */ jsx(
36
37
  CardHeader,
37
38
  {
38
- title,
39
+ title: /* @__PURE__ */ jsx(Tooltip, { title, children: /* @__PURE__ */ jsx("div", { children: title }) }),
39
40
  subheader: subheader ?? void 0,
40
41
  sx: {
41
42
  "& .v5-MuiCardHeader-title, & .v5-MuiCardHeader-subheader": {
@@ -1 +1 @@
1
- {"version":3,"file":"CardWrapper.esm.js","sources":["../../../src/components/Common/CardWrapper.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 { ReactNode, HTMLProps } from 'react';\n\nimport Card from '@mui/material/Card';\nimport CardHeader from '@mui/material/CardHeader';\nimport CardContent from '@mui/material/CardContent';\nimport Divider from '@mui/material/Divider';\nimport Box from '@mui/material/Box';\nimport Typography from '@mui/material/Typography';\n\ninterface CardWrapperProps extends HTMLProps<HTMLDivElement> {\n children: ReactNode;\n title: string;\n subheader?: ReactNode;\n description?: string;\n width?: string;\n childrenWidth?: string | number;\n childrenHeight?: string | number;\n role?: string;\n info?: ReactNode;\n dataTestId?: string;\n}\n\nexport const CardWrapper = ({\n children,\n title,\n subheader,\n description,\n width,\n childrenWidth = '100%',\n childrenHeight = '100%',\n role = 'article',\n info,\n dataTestId,\n}: CardWrapperProps) => {\n return (\n <Card\n elevation={0}\n sx={{\n width: width ?? '100%',\n border: muiTheme => `1px solid ${muiTheme.palette.grey[300]}`,\n overflow: 'auto',\n height: '100%',\n }}\n role={role}\n data-testid={dataTestId}\n >\n <Box sx={{ display: 'flex', justifyContent: 'space-between' }}>\n <CardHeader\n title={title}\n subheader={subheader ?? undefined}\n sx={{\n '& .v5-MuiCardHeader-title, & .v5-MuiCardHeader-subheader': {\n fontSize: '1.25rem',\n fontWeight: 500,\n display: '-webkit-box',\n WebkitBoxOrient: 'vertical',\n WebkitLineClamp: 1,\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n },\n }}\n />\n {info && (\n <Box sx={{ display: 'flex', alignItems: 'center' }}>{info}</Box>\n )}\n </Box>\n <Divider />\n <CardContent\n sx={{\n flex: 1,\n display: 'flex',\n flexDirection: 'column',\n overflow: 'hidden',\n }}\n >\n {description && (\n <Box sx={{ pb: 2, width: '100%', minWidth: 316 }}>\n <Typography\n variant=\"body2\"\n color=\"textSecondary\"\n sx={{\n fontSize: '1rem',\n fontWeight: 400,\n display: '-webkit-box',\n WebkitBoxOrient: 'vertical',\n WebkitLineClamp: 2,\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n }}\n >\n {description}\n </Typography>\n </Box>\n )}\n\n <Box\n sx={{\n flex: 1,\n minHeight: 0,\n display: 'flex',\n justifyContent: 'center',\n alignItems: 'center',\n }}\n >\n <Box\n sx={{\n width: childrenWidth,\n height: childrenHeight,\n flexShrink: 0,\n }}\n >\n {children}\n </Box>\n </Box>\n </CardContent>\n </Card>\n );\n};\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"CardWrapper.esm.js","sources":["../../../src/components/Common/CardWrapper.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 { ReactNode, HTMLProps } from 'react';\n\nimport Card from '@mui/material/Card';\nimport CardHeader from '@mui/material/CardHeader';\nimport CardContent from '@mui/material/CardContent';\nimport Divider from '@mui/material/Divider';\nimport Box from '@mui/material/Box';\nimport Typography from '@mui/material/Typography';\nimport Tooltip from '@mui/material/Tooltip';\n\ninterface CardWrapperProps extends HTMLProps<HTMLDivElement> {\n children: ReactNode;\n title: string;\n subheader?: ReactNode;\n description?: string;\n width?: string;\n childrenWidth?: string | number;\n childrenHeight?: string | number;\n role?: string;\n info?: ReactNode;\n dataTestId?: string;\n}\n\nexport const CardWrapper = ({\n children,\n title,\n subheader,\n description,\n width,\n childrenWidth = '100%',\n childrenHeight = '100%',\n role = 'article',\n info,\n dataTestId,\n}: CardWrapperProps) => {\n return (\n <Card\n elevation={0}\n sx={{\n width: width ?? '100%',\n border: muiTheme => `1px solid ${muiTheme.palette.grey[300]}`,\n overflow: 'auto',\n height: '100%',\n }}\n role={role}\n data-testid={dataTestId}\n >\n <Box sx={{ display: 'flex', justifyContent: 'space-between' }}>\n <CardHeader\n title={\n <Tooltip title={title}>\n <div>{title}</div>\n </Tooltip>\n }\n subheader={subheader ?? undefined}\n sx={{\n '& .v5-MuiCardHeader-title, & .v5-MuiCardHeader-subheader': {\n fontSize: '1.25rem',\n fontWeight: 500,\n display: '-webkit-box',\n WebkitBoxOrient: 'vertical',\n WebkitLineClamp: 1,\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n },\n }}\n />\n {info && (\n <Box sx={{ display: 'flex', alignItems: 'center' }}>{info}</Box>\n )}\n </Box>\n <Divider />\n <CardContent\n sx={{\n flex: 1,\n display: 'flex',\n flexDirection: 'column',\n overflow: 'hidden',\n }}\n >\n {description && (\n <Box sx={{ pb: 2, width: '100%', minWidth: 316 }}>\n <Typography\n variant=\"body2\"\n color=\"textSecondary\"\n sx={{\n fontSize: '1rem',\n fontWeight: 400,\n display: '-webkit-box',\n WebkitBoxOrient: 'vertical',\n WebkitLineClamp: 2,\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n }}\n >\n {description}\n </Typography>\n </Box>\n )}\n\n <Box\n sx={{\n flex: 1,\n minHeight: 0,\n display: 'flex',\n justifyContent: 'center',\n alignItems: 'center',\n }}\n >\n <Box\n sx={{\n width: childrenWidth,\n height: childrenHeight,\n flexShrink: 0,\n }}\n >\n {children}\n </Box>\n </Box>\n </CardContent>\n </Card>\n );\n};\n"],"names":[],"mappings":"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"}
@@ -9,6 +9,10 @@ import { useMetricDisplayLabels } from '../../hooks/useMetricDisplayLabels.esm.j
9
9
  import { CardLoading } from '../Common/CardLoading.esm.js';
10
10
  import { ScorecardQueryProvider } from '../../api/ScorecardQueryProvider.esm.js';
11
11
 
12
+ function toSafeFiniteNumber(value) {
13
+ const n = Number(value ?? 0);
14
+ return Number.isFinite(n) ? n : 0;
15
+ }
12
16
  const ScorecardHomepageCard = ({
13
17
  metricId,
14
18
  aggregationId,
@@ -46,7 +50,14 @@ const ScorecardHomepageCard = ({
46
50
  if (!data) {
47
51
  return null;
48
52
  }
49
- if (data.result?.total === 0) {
53
+ const result = data.result;
54
+ const total = toSafeFiniteNumber(result.total);
55
+ const calculationErrorCount = toSafeFiniteNumber(
56
+ result.calculationErrorCount
57
+ );
58
+ const entitiesConsidered = toSafeFiniteNumber(result.entitiesConsidered);
59
+ const hasNoRenderableAggregation = total === 0 && calculationErrorCount === 0 && entitiesConsidered === 0;
60
+ if (hasNoRenderableAggregation) {
50
61
  return /* @__PURE__ */ jsx(
51
62
  EmptyStatePanel,
52
63
  {
@@ -1 +1 @@
1
- {"version":3,"file":"ScorecardHomepageCard.esm.js","sources":["../../../src/components/ScorecardHomepageSection/ScorecardHomepageCard.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 { ScorecardQueryProvider } from '../../api';\nimport { AggregatedMetricCard } from '../AggregatedMetricCards/AggregatedMetricCard';\nimport { useAggregatedScorecard } from '../../hooks/useAggregatedScorecard';\nimport { useTranslation } from '../../hooks/useTranslation';\nimport { ErrorStatePanel } from './ErrorStatePanel';\nimport { EmptyStatePanel } from './EmptyStatePanel';\nimport { Metric } from '@red-hat-developer-hub/backstage-plugin-scorecard-common';\nimport { useMetricDisplayLabels } from '../../hooks/useMetricDisplayLabels';\nimport { CardLoading } from '../Common/CardLoading';\n\nexport const ScorecardHomepageCard = ({\n metricId,\n aggregationId,\n showSubheader = true,\n showInfo = true,\n}: {\n metricId?: string;\n aggregationId?: string;\n showSubheader?: boolean;\n showInfo?: boolean;\n}) => {\n const { t } = useTranslation();\n\n // Deprecated logic to support both metricId and aggregationId. Only aggregationId will be used in the future.\n const resolvedScorecardId = aggregationId || metricId || '';\n\n const { data, isLoading, error } = useAggregatedScorecard({\n aggregationId: resolvedScorecardId,\n });\n\n const aggregatedMetricDetails = data\n ? ({\n id: resolvedScorecardId,\n title: data.metadata.title,\n description: data.metadata.description,\n } as Pick<Metric, 'id' | 'title' | 'description'>)\n : undefined;\n\n const { title, description } = useMetricDisplayLabels(\n aggregatedMetricDetails,\n );\n\n const cardDataTestId = `scorecard-homepage-card-${resolvedScorecardId}`;\n\n if (isLoading) {\n return <CardLoading dataTestId={cardDataTestId} />;\n }\n\n if (error) {\n return (\n <ErrorStatePanel\n error={error}\n showSubheader={showSubheader}\n aggregationId={resolvedScorecardId}\n cardDataTestId={cardDataTestId}\n />\n );\n }\n\n if (!data) {\n return null;\n }\n\n if (data.result?.total === 0) {\n return (\n <EmptyStatePanel\n showSubheader={showSubheader}\n cardTitle={title}\n cardDescription={description}\n label={t('errors.noDataFound')}\n tooltipContent={t('errors.noDataFoundMessage')}\n dataTestId={cardDataTestId}\n />\n );\n }\n\n return (\n <AggregatedMetricCard\n key={data.id}\n showSubheader={showSubheader}\n showInfo={showInfo}\n cardTitle={title}\n description={description}\n scorecard={data}\n aggregationId={resolvedScorecardId}\n dataTestId={cardDataTestId}\n />\n );\n};\n\n/**\n * ScorecardHomepageCard wrapped with QueryClientProvider so it works\n * when rendered outside a tree that already has a provider (e.g. on the homepage).\n */\nexport const ScorecardHomepageCardWithProvider = (props: {\n metricId?: string;\n aggregationId?: string;\n showSubheader?: boolean;\n showInfo?: boolean;\n}) => (\n <ScorecardQueryProvider>\n <ScorecardHomepageCard {...props} />\n </ScorecardQueryProvider>\n);\n"],"names":[],"mappings":";;;;;;;;;;;AA0BO,MAAM,wBAAwB,CAAC;AAAA,EACpC,QAAA;AAAA,EACA,aAAA;AAAA,EACA,aAAgB,GAAA,IAAA;AAAA,EAChB,QAAW,GAAA;AACb,CAKM,KAAA;AACJ,EAAM,MAAA,EAAE,CAAE,EAAA,GAAI,cAAe,EAAA;AAG7B,EAAM,MAAA,mBAAA,GAAsB,iBAAiB,QAAY,IAAA,EAAA;AAEzD,EAAA,MAAM,EAAE,IAAA,EAAM,SAAW,EAAA,KAAA,KAAU,sBAAuB,CAAA;AAAA,IACxD,aAAe,EAAA;AAAA,GAChB,CAAA;AAED,EAAA,MAAM,0BAA0B,IAC3B,GAAA;AAAA,IACC,EAAI,EAAA,mBAAA;AAAA,IACJ,KAAA,EAAO,KAAK,QAAS,CAAA,KAAA;AAAA,IACrB,WAAA,EAAa,KAAK,QAAS,CAAA;AAAA,GAE7B,GAAA,MAAA;AAEJ,EAAM,MAAA,EAAE,KAAO,EAAA,WAAA,EAAgB,GAAA,sBAAA;AAAA,IAC7B;AAAA,GACF;AAEA,EAAM,MAAA,cAAA,GAAiB,2BAA2B,mBAAmB,CAAA,CAAA;AAErE,EAAA,IAAI,SAAW,EAAA;AACb,IAAO,uBAAA,GAAA,CAAC,WAAY,EAAA,EAAA,UAAA,EAAY,cAAgB,EAAA,CAAA;AAAA;AAGlD,EAAA,IAAI,KAAO,EAAA;AACT,IACE,uBAAA,GAAA;AAAA,MAAC,eAAA;AAAA,MAAA;AAAA,QACC,KAAA;AAAA,QACA,aAAA;AAAA,QACA,aAAe,EAAA,mBAAA;AAAA,QACf;AAAA;AAAA,KACF;AAAA;AAIJ,EAAA,IAAI,CAAC,IAAM,EAAA;AACT,IAAO,OAAA,IAAA;AAAA;AAGT,EAAI,IAAA,IAAA,CAAK,MAAQ,EAAA,KAAA,KAAU,CAAG,EAAA;AAC5B,IACE,uBAAA,GAAA;AAAA,MAAC,eAAA;AAAA,MAAA;AAAA,QACC,aAAA;AAAA,QACA,SAAW,EAAA,KAAA;AAAA,QACX,eAAiB,EAAA,WAAA;AAAA,QACjB,KAAA,EAAO,EAAE,oBAAoB,CAAA;AAAA,QAC7B,cAAA,EAAgB,EAAE,2BAA2B,CAAA;AAAA,QAC7C,UAAY,EAAA;AAAA;AAAA,KACd;AAAA;AAIJ,EACE,uBAAA,GAAA;AAAA,IAAC,oBAAA;AAAA,IAAA;AAAA,MAEC,aAAA;AAAA,MACA,QAAA;AAAA,MACA,SAAW,EAAA,KAAA;AAAA,MACX,WAAA;AAAA,MACA,SAAW,EAAA,IAAA;AAAA,MACX,aAAe,EAAA,mBAAA;AAAA,MACf,UAAY,EAAA;AAAA,KAAA;AAAA,IAPP,IAAK,CAAA;AAAA,GAQZ;AAEJ;AAMa,MAAA,iCAAA,GAAoC,CAAC,KAMhD,qBAAA,GAAA,CAAC,0BACC,QAAC,kBAAA,GAAA,CAAA,qBAAA,EAAA,EAAuB,GAAG,KAAA,EAAO,CACpC,EAAA;;;;"}
1
+ {"version":3,"file":"ScorecardHomepageCard.esm.js","sources":["../../../src/components/ScorecardHomepageSection/ScorecardHomepageCard.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 { ScorecardQueryProvider } from '../../api';\nimport { AggregatedMetricCard } from '../AggregatedMetricCards/AggregatedMetricCard';\nimport { useAggregatedScorecard } from '../../hooks/useAggregatedScorecard';\nimport { useTranslation } from '../../hooks/useTranslation';\nimport { ErrorStatePanel } from './ErrorStatePanel';\nimport { EmptyStatePanel } from './EmptyStatePanel';\nimport { Metric } from '@red-hat-developer-hub/backstage-plugin-scorecard-common';\nimport { useMetricDisplayLabels } from '../../hooks/useMetricDisplayLabels';\nimport { CardLoading } from '../Common/CardLoading';\n\n/** Coerces unknown/missing values to a finite number for safe UI math (NaN → 0). */\nfunction toSafeFiniteNumber(value: unknown): number {\n const n = Number(value ?? 0);\n return Number.isFinite(n) ? n : 0;\n}\n\nexport const ScorecardHomepageCard = ({\n metricId,\n aggregationId,\n showSubheader = true,\n showInfo = true,\n}: {\n metricId?: string;\n aggregationId?: string;\n showSubheader?: boolean;\n showInfo?: boolean;\n}) => {\n const { t } = useTranslation();\n\n // Deprecated logic to support both metricId and aggregationId. Only aggregationId will be used in the future.\n const resolvedScorecardId = aggregationId || metricId || '';\n\n const { data, isLoading, error } = useAggregatedScorecard({\n aggregationId: resolvedScorecardId,\n });\n\n const aggregatedMetricDetails = data\n ? ({\n id: resolvedScorecardId,\n title: data.metadata.title,\n description: data.metadata.description,\n } as Pick<Metric, 'id' | 'title' | 'description'>)\n : undefined;\n\n const { title, description } = useMetricDisplayLabels(\n aggregatedMetricDetails,\n );\n\n const cardDataTestId = `scorecard-homepage-card-${resolvedScorecardId}`;\n\n if (isLoading) {\n return <CardLoading dataTestId={cardDataTestId} />;\n }\n\n if (error) {\n return (\n <ErrorStatePanel\n error={error}\n showSubheader={showSubheader}\n aggregationId={resolvedScorecardId}\n cardDataTestId={cardDataTestId}\n />\n );\n }\n\n if (!data) {\n return null;\n }\n\n const result = data.result;\n const total = toSafeFiniteNumber(result.total);\n const calculationErrorCount = toSafeFiniteNumber(\n result.calculationErrorCount,\n );\n const entitiesConsidered = toSafeFiniteNumber(result.entitiesConsidered);\n const hasNoRenderableAggregation =\n total === 0 && calculationErrorCount === 0 && entitiesConsidered === 0;\n\n if (hasNoRenderableAggregation) {\n return (\n <EmptyStatePanel\n showSubheader={showSubheader}\n cardTitle={title}\n cardDescription={description}\n label={t('errors.noDataFound')}\n tooltipContent={t('errors.noDataFoundMessage')}\n dataTestId={cardDataTestId}\n />\n );\n }\n\n return (\n <AggregatedMetricCard\n key={data.id}\n showSubheader={showSubheader}\n showInfo={showInfo}\n cardTitle={title}\n description={description}\n scorecard={data}\n aggregationId={resolvedScorecardId}\n dataTestId={cardDataTestId}\n />\n );\n};\n\n/**\n * ScorecardHomepageCard wrapped with QueryClientProvider so it works\n * when rendered outside a tree that already has a provider (e.g. on the homepage).\n */\nexport const ScorecardHomepageCardWithProvider = (props: {\n metricId?: string;\n aggregationId?: string;\n showSubheader?: boolean;\n showInfo?: boolean;\n}) => (\n <ScorecardQueryProvider>\n <ScorecardHomepageCard {...props} />\n </ScorecardQueryProvider>\n);\n"],"names":[],"mappings":";;;;;;;;;;;AA2BA,SAAS,mBAAmB,KAAwB,EAAA;AAClD,EAAM,MAAA,CAAA,GAAI,MAAO,CAAA,KAAA,IAAS,CAAC,CAAA;AAC3B,EAAA,OAAO,MAAO,CAAA,QAAA,CAAS,CAAC,CAAA,GAAI,CAAI,GAAA,CAAA;AAClC;AAEO,MAAM,wBAAwB,CAAC;AAAA,EACpC,QAAA;AAAA,EACA,aAAA;AAAA,EACA,aAAgB,GAAA,IAAA;AAAA,EAChB,QAAW,GAAA;AACb,CAKM,KAAA;AACJ,EAAM,MAAA,EAAE,CAAE,EAAA,GAAI,cAAe,EAAA;AAG7B,EAAM,MAAA,mBAAA,GAAsB,iBAAiB,QAAY,IAAA,EAAA;AAEzD,EAAA,MAAM,EAAE,IAAA,EAAM,SAAW,EAAA,KAAA,KAAU,sBAAuB,CAAA;AAAA,IACxD,aAAe,EAAA;AAAA,GAChB,CAAA;AAED,EAAA,MAAM,0BAA0B,IAC3B,GAAA;AAAA,IACC,EAAI,EAAA,mBAAA;AAAA,IACJ,KAAA,EAAO,KAAK,QAAS,CAAA,KAAA;AAAA,IACrB,WAAA,EAAa,KAAK,QAAS,CAAA;AAAA,GAE7B,GAAA,MAAA;AAEJ,EAAM,MAAA,EAAE,KAAO,EAAA,WAAA,EAAgB,GAAA,sBAAA;AAAA,IAC7B;AAAA,GACF;AAEA,EAAM,MAAA,cAAA,GAAiB,2BAA2B,mBAAmB,CAAA,CAAA;AAErE,EAAA,IAAI,SAAW,EAAA;AACb,IAAO,uBAAA,GAAA,CAAC,WAAY,EAAA,EAAA,UAAA,EAAY,cAAgB,EAAA,CAAA;AAAA;AAGlD,EAAA,IAAI,KAAO,EAAA;AACT,IACE,uBAAA,GAAA;AAAA,MAAC,eAAA;AAAA,MAAA;AAAA,QACC,KAAA;AAAA,QACA,aAAA;AAAA,QACA,aAAe,EAAA,mBAAA;AAAA,QACf;AAAA;AAAA,KACF;AAAA;AAIJ,EAAA,IAAI,CAAC,IAAM,EAAA;AACT,IAAO,OAAA,IAAA;AAAA;AAGT,EAAA,MAAM,SAAS,IAAK,CAAA,MAAA;AACpB,EAAM,MAAA,KAAA,GAAQ,kBAAmB,CAAA,MAAA,CAAO,KAAK,CAAA;AAC7C,EAAA,MAAM,qBAAwB,GAAA,kBAAA;AAAA,IAC5B,MAAO,CAAA;AAAA,GACT;AACA,EAAM,MAAA,kBAAA,GAAqB,kBAAmB,CAAA,MAAA,CAAO,kBAAkB,CAAA;AACvE,EAAA,MAAM,0BACJ,GAAA,KAAA,KAAU,CAAK,IAAA,qBAAA,KAA0B,KAAK,kBAAuB,KAAA,CAAA;AAEvE,EAAA,IAAI,0BAA4B,EAAA;AAC9B,IACE,uBAAA,GAAA;AAAA,MAAC,eAAA;AAAA,MAAA;AAAA,QACC,aAAA;AAAA,QACA,SAAW,EAAA,KAAA;AAAA,QACX,eAAiB,EAAA,WAAA;AAAA,QACjB,KAAA,EAAO,EAAE,oBAAoB,CAAA;AAAA,QAC7B,cAAA,EAAgB,EAAE,2BAA2B,CAAA;AAAA,QAC7C,UAAY,EAAA;AAAA;AAAA,KACd;AAAA;AAIJ,EACE,uBAAA,GAAA;AAAA,IAAC,oBAAA;AAAA,IAAA;AAAA,MAEC,aAAA;AAAA,MACA,QAAA;AAAA,MACA,SAAW,EAAA,KAAA;AAAA,MACX,WAAA;AAAA,MACA,SAAW,EAAA,IAAA;AAAA,MACX,aAAe,EAAA,mBAAA;AAAA,MACf,UAAY,EAAA;AAAA,KAAA;AAAA,IAPP,IAAK,CAAA;AAAA,GAQZ;AAEJ;AAMa,MAAA,iCAAA,GAAoC,CAAC,KAMhD,qBAAA,GAAA,CAAC,0BACC,QAAC,kBAAA,GAAA,CAAA,qBAAA,EAAA,EAAuB,GAAG,KAAA,EAAO,CACpC,EAAA;;;;"}
@@ -82,73 +82,81 @@ const EntitiesTable = ({
82
82
  const { entityMetadataMap } = useEntityMetadataMap(entityRefs);
83
83
  const entities = aggregatedScorecardEntities?.entities ?? [];
84
84
  const total = aggregatedScorecardEntities?.pagination?.total ?? 0;
85
+ const calculationErrorCount = aggregatedScorecardEntities?.entityHealth?.calculationErrorCount ?? 0;
85
86
  const entitiesTableTitle = total > 0 ? t("entitiesPage.entitiesTable.titleWithCount", { count: total }) : t("entitiesPage.entitiesTable.title");
86
- return /* @__PURE__ */ jsx(EntitiesTableWrapper, { title: entitiesTableTitle, isError: !!entitiesError, children: /* @__PURE__ */ jsxs(Table, { sx: { width: "100%", tableLayout: "fixed" }, children: [
87
- /* @__PURE__ */ jsx(
88
- EntitiesTableHeader,
89
- {
90
- orderBy,
91
- order,
92
- onSortRequest: handleSortRequest
93
- }
94
- ),
95
- /* @__PURE__ */ jsxs(TableBody, { children: [
96
- (ownershipLoading || loadingDataEntities) && /* @__PURE__ */ jsx(TableRow, { children: /* @__PURE__ */ jsx(
97
- TableCell,
98
- {
99
- colSpan: SCORECARD_ENTITIES_TABLE_HEADERS.length,
100
- align: "center",
101
- children: /* @__PURE__ */ jsx(CircularProgress, { "aria-label": t("common.loading") })
102
- }
103
- ) }, "entities-table-loading-row"),
104
- !ownershipLoading && !loadingDataEntities && entitiesError && /* @__PURE__ */ jsx(
105
- EntitiesTableStateRow,
106
- {
107
- colSpan: SCORECARD_ENTITIES_TABLE_HEADERS.length,
108
- error: entitiesError,
109
- metricId,
110
- setMetricTitle
111
- }
112
- ),
113
- !ownershipLoading && !loadingDataEntities && !entitiesError && entities.length === 0 && /* @__PURE__ */ jsx(
114
- EntitiesTableStateRow,
115
- {
116
- colSpan: SCORECARD_ENTITIES_TABLE_HEADERS.length,
117
- metricId,
118
- setMetricTitle,
119
- noEntities: entities.length === 0
120
- }
121
- ),
122
- !ownershipLoading && !loadingDataEntities && entities.length > 0 && entities.map((entity) => /* @__PURE__ */ jsx(
123
- EntitiesRow,
124
- {
125
- entity,
126
- entityMetadataMap,
127
- thresholdRules
128
- },
129
- entity.entityRef
130
- ))
131
- ] }),
132
- /* @__PURE__ */ jsx(TableFooter, { children: /* @__PURE__ */ jsx(TableRow, { children: /* @__PURE__ */ jsx(
133
- TableCell,
134
- {
135
- colSpan: SCORECARD_ENTITIES_TABLE_HEADERS.length,
136
- sx: {
137
- padding: 0
138
- },
139
- children: /* @__PURE__ */ jsx(
140
- EntitiesTableFooter,
87
+ return /* @__PURE__ */ jsx(
88
+ EntitiesTableWrapper,
89
+ {
90
+ title: entitiesTableTitle,
91
+ showCalculationWarning: calculationErrorCount > 0,
92
+ children: /* @__PURE__ */ jsxs(Table, { sx: { width: "100%", tableLayout: "fixed" }, children: [
93
+ /* @__PURE__ */ jsx(
94
+ EntitiesTableHeader,
141
95
  {
142
- count: aggregatedScorecardEntities?.pagination?.total ?? 0,
143
- page,
144
- rowsPerPage,
145
- handleChangePage: (_event, newPage) => setPage(newPage),
146
- handleChangeRowsPerPage
96
+ orderBy,
97
+ order,
98
+ onSortRequest: handleSortRequest
147
99
  }
148
- )
149
- }
150
- ) }) })
151
- ] }) });
100
+ ),
101
+ /* @__PURE__ */ jsxs(TableBody, { children: [
102
+ (ownershipLoading || loadingDataEntities) && /* @__PURE__ */ jsx(TableRow, { children: /* @__PURE__ */ jsx(
103
+ TableCell,
104
+ {
105
+ colSpan: SCORECARD_ENTITIES_TABLE_HEADERS.length,
106
+ align: "center",
107
+ children: /* @__PURE__ */ jsx(CircularProgress, { "aria-label": t("common.loading") })
108
+ }
109
+ ) }, "entities-table-loading-row"),
110
+ !ownershipLoading && !loadingDataEntities && entitiesError && /* @__PURE__ */ jsx(
111
+ EntitiesTableStateRow,
112
+ {
113
+ colSpan: SCORECARD_ENTITIES_TABLE_HEADERS.length,
114
+ error: entitiesError,
115
+ metricId,
116
+ setMetricTitle
117
+ }
118
+ ),
119
+ !ownershipLoading && !loadingDataEntities && !entitiesError && entities.length === 0 && /* @__PURE__ */ jsx(
120
+ EntitiesTableStateRow,
121
+ {
122
+ colSpan: SCORECARD_ENTITIES_TABLE_HEADERS.length,
123
+ metricId,
124
+ setMetricTitle,
125
+ noEntities: entities.length === 0
126
+ }
127
+ ),
128
+ !ownershipLoading && !loadingDataEntities && entities.length > 0 && entities.map((entity) => /* @__PURE__ */ jsx(
129
+ EntitiesRow,
130
+ {
131
+ entity,
132
+ entityMetadataMap,
133
+ thresholdRules
134
+ },
135
+ entity.entityRef
136
+ ))
137
+ ] }),
138
+ /* @__PURE__ */ jsx(TableFooter, { children: /* @__PURE__ */ jsx(TableRow, { children: /* @__PURE__ */ jsx(
139
+ TableCell,
140
+ {
141
+ colSpan: SCORECARD_ENTITIES_TABLE_HEADERS.length,
142
+ sx: {
143
+ padding: 0
144
+ },
145
+ children: /* @__PURE__ */ jsx(
146
+ EntitiesTableFooter,
147
+ {
148
+ count: aggregatedScorecardEntities?.pagination?.total ?? 0,
149
+ page,
150
+ rowsPerPage,
151
+ handleChangePage: (_event, newPage) => setPage(newPage),
152
+ handleChangeRowsPerPage
153
+ }
154
+ )
155
+ }
156
+ ) }) })
157
+ ] })
158
+ }
159
+ );
152
160
  };
153
161
 
154
162
  export { EntitiesTable };