@red-hat-developer-hub/backstage-plugin-scorecard 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +30 -0
- package/README.md +164 -0
- package/dist/alpha.d.ts +36 -0
- package/dist/alpha.esm.js +3 -0
- package/dist/alpha.esm.js.map +1 -0
- package/dist/api/index.esm.js +63 -0
- package/dist/api/index.esm.js.map +1 -0
- package/dist/components/Common/NoScorecardsState.esm.js +83 -0
- package/dist/components/Common/NoScorecardsState.esm.js.map +1 -0
- package/dist/components/Common/PermissionRequiredState.esm.js +94 -0
- package/dist/components/Common/PermissionRequiredState.esm.js.map +1 -0
- package/dist/components/Scorecard/EntityScorecardContent.esm.js +85 -0
- package/dist/components/Scorecard/EntityScorecardContent.esm.js.map +1 -0
- package/dist/components/Scorecard/Scorecard.esm.js +249 -0
- package/dist/components/Scorecard/Scorecard.esm.js.map +1 -0
- package/dist/components/Scorecard/index.esm.js +2 -0
- package/dist/components/Scorecard/index.esm.js.map +1 -0
- package/dist/hooks/useScorecardMetricsReadPermission.esm.js +16 -0
- package/dist/hooks/useScorecardMetricsReadPermission.esm.js.map +1 -0
- package/dist/hooks/useScorecards.esm.js +45 -0
- package/dist/hooks/useScorecards.esm.js.map +1 -0
- package/dist/hooks/useTranslation.esm.js +8 -0
- package/dist/hooks/useTranslation.esm.js.map +1 -0
- package/dist/images/no-scorecards.svg +9 -0
- package/dist/images/permission-required.svg +48 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.esm.js +2 -0
- package/dist/index.esm.js.map +1 -0
- package/dist/plugin.esm.js +36 -0
- package/dist/plugin.esm.js.map +1 -0
- package/dist/routes.esm.js +8 -0
- package/dist/routes.esm.js.map +1 -0
- package/dist/translations/de.esm.js +37 -0
- package/dist/translations/de.esm.js.map +1 -0
- package/dist/translations/es.esm.js +37 -0
- package/dist/translations/es.esm.js.map +1 -0
- package/dist/translations/fr.esm.js +37 -0
- package/dist/translations/fr.esm.js.map +1 -0
- package/dist/translations/index.esm.js +14 -0
- package/dist/translations/index.esm.js.map +1 -0
- package/dist/translations/ref.esm.js +50 -0
- package/dist/translations/ref.esm.js.map +1 -0
- package/dist/utils/utils.esm.js +24 -0
- package/dist/utils/utils.esm.js.map +1 -0
- package/package.json +88 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# @red-hat-developer-hub/backstage-plugin-scorecard
|
|
2
|
+
|
|
3
|
+
## 1.0.0
|
|
4
|
+
|
|
5
|
+
### Major Changes
|
|
6
|
+
|
|
7
|
+
- 6709132: Release the major version of Scorecard plugin
|
|
8
|
+
|
|
9
|
+
### Patch Changes
|
|
10
|
+
|
|
11
|
+
- Updated dependencies [6709132]
|
|
12
|
+
- @red-hat-developer-hub/backstage-plugin-scorecard-common@1.0.0
|
|
13
|
+
|
|
14
|
+
## 0.2.0
|
|
15
|
+
|
|
16
|
+
### Minor Changes
|
|
17
|
+
|
|
18
|
+
- 0478b79: Unify provider IDs
|
|
19
|
+
|
|
20
|
+
## 0.1.0
|
|
21
|
+
|
|
22
|
+
### Minor Changes
|
|
23
|
+
|
|
24
|
+
- fe2fa2d: Add internationalization (i18n) support with German, French and Spanish translations for the Scorecard plugin
|
|
25
|
+
- b5ec15c: First version of Scorecard plugin
|
|
26
|
+
|
|
27
|
+
### Patch Changes
|
|
28
|
+
|
|
29
|
+
- Updated dependencies [b5ec15c]
|
|
30
|
+
- @red-hat-developer-hub/backstage-plugin-scorecard-common@0.1.0
|
package/README.md
ADDED
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
# 📊 Scorecard Plugin for Backstage
|
|
2
|
+
|
|
3
|
+
The Scorecard plugin provides a configurable framework to visualize Key Performance Indicators (KPIs) in Backstage. This frontend plugin integrates with the Scorecard backend to deliver Scorecards.
|
|
4
|
+
|
|
5
|
+
## Getting started
|
|
6
|
+
|
|
7
|
+
Your plugin has been added to the example app in this repository, meaning you'll be able to access it by running `yarn start` in the root directory, and then navigating to [/scorecard](http://localhost:3000/scorecard).
|
|
8
|
+
|
|
9
|
+
You can also serve the plugin in isolation by running `yarn start` in the plugin directory.
|
|
10
|
+
This method of serving the plugin provides quicker iteration speed and a faster startup and hot reloads.
|
|
11
|
+
It is only meant for local development, and the setup for it can be found inside the [/dev](./dev) directory.
|
|
12
|
+
|
|
13
|
+
## For Administrators
|
|
14
|
+
|
|
15
|
+
### Prerequisites
|
|
16
|
+
|
|
17
|
+
Before installing the frontend plugin, ensure that the Scorecard backend is integrated into your Backstage instance. Follow the [Scorecard backend plugin README](https://github.com/redhat-developer/rhdh-plugins/blob/main/workspaces/scorecard/plugins/scorecard-backend/README.md) for setup instructions.
|
|
18
|
+
|
|
19
|
+
### Installation
|
|
20
|
+
|
|
21
|
+
To install the Scorecard plugin, run the following command:
|
|
22
|
+
|
|
23
|
+
```sh
|
|
24
|
+
yarn workspace app add @red-hat-developer-hub/backstage-plugin-scorecard
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
**Note**
|
|
28
|
+
|
|
29
|
+
### Permission Framework Support
|
|
30
|
+
|
|
31
|
+
The Scorecard plugin has support for the permission framework.
|
|
32
|
+
|
|
33
|
+
- When [RBAC permission](https://github.com/backstage/community-plugins/tree/main/workspaces/rbac/plugins/rbac-backend#installation) framework is enabled, for non-admin users to access scorecard UI, the role associated with your user should have the following permission policies associated with it. Add the following in your permission policies configuration file named `rbac-policy.csv`:
|
|
34
|
+
|
|
35
|
+
```CSV
|
|
36
|
+
p, role:default/team_a, scorecard.metric.read, read, allow
|
|
37
|
+
|
|
38
|
+
g, user:default/<your-user-name>, role:default/team_a
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
You can specify the path to this configuration file in your application configuration:
|
|
42
|
+
|
|
43
|
+
```yaml
|
|
44
|
+
permission:
|
|
45
|
+
enabled: true
|
|
46
|
+
rbac:
|
|
47
|
+
policies-csv-file: /some/path/rbac-policy.csv
|
|
48
|
+
policyFileReload: true
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Configuration
|
|
52
|
+
|
|
53
|
+
1. Add the Scorecard page to you Entity overview page by modifying `packages/app/src/components/catalog/EntityPage.tsx`:
|
|
54
|
+
|
|
55
|
+
```tsx
|
|
56
|
+
import { ScorecardPage } from '@red-hat-developer-hub/backstage-plugin-scorecard';
|
|
57
|
+
|
|
58
|
+
const scorecardRoute = (
|
|
59
|
+
<EntityLayout.Route path="/scorecard" title="Scorecard">
|
|
60
|
+
<ScorecardPage />
|
|
61
|
+
</EntityLayout.Route>
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
const serviceEntityPage = (
|
|
65
|
+
<EntityLayout>
|
|
66
|
+
...
|
|
67
|
+
{scorecardRoute}
|
|
68
|
+
</EntityLayout>
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
const websiteEntityPage = (
|
|
72
|
+
<EntityLayout>
|
|
73
|
+
...
|
|
74
|
+
{scorecardRoute}
|
|
75
|
+
</EntityLayout>
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
const componentPage = (
|
|
79
|
+
<EntitySwitch>
|
|
80
|
+
<EntitySwitch.Case if={isComponentType('service')}>
|
|
81
|
+
{serviceEntityPage}
|
|
82
|
+
</EntitySwitch.Case>
|
|
83
|
+
|
|
84
|
+
<EntitySwitch.Case if={isComponentType('website')}>
|
|
85
|
+
{websiteEntityPage}
|
|
86
|
+
</EntitySwitch.Case>
|
|
87
|
+
</EntitySwitch>
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
export const entityPage = (
|
|
91
|
+
<EntitySwitch>
|
|
92
|
+
...
|
|
93
|
+
<EntitySwitch.Case if={isKind('component')} children={componentPage} />
|
|
94
|
+
...
|
|
95
|
+
</EntitySwitch>
|
|
96
|
+
);
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### Accessing the Plugin
|
|
100
|
+
|
|
101
|
+
1. Open your Backstage application.
|
|
102
|
+
2. Navigate to the Entity overview page from catalog.
|
|
103
|
+
3. Explore and analyze scorecard metrics using the scorecards tab.
|
|
104
|
+
|
|
105
|
+
## Adding Translations
|
|
106
|
+
|
|
107
|
+
The Scorecard plugin supports internationalization (i18n) for metric titles and descriptions. To add translations for new metrics:
|
|
108
|
+
|
|
109
|
+
### 1. Add Translations to Translation Files
|
|
110
|
+
|
|
111
|
+
Add your translations to the appropriate language files in `src/translations/`:
|
|
112
|
+
|
|
113
|
+
**English (ref.ts):**
|
|
114
|
+
|
|
115
|
+
```typescript
|
|
116
|
+
export const scorecardMessages = {
|
|
117
|
+
// ... existing translations
|
|
118
|
+
metric: {
|
|
119
|
+
'your-metric-id': {
|
|
120
|
+
title: 'Your Translated Title',
|
|
121
|
+
description: 'Your translated description',
|
|
122
|
+
},
|
|
123
|
+
},
|
|
124
|
+
};
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
**Other languages (de.ts, es.ts, fr.ts):**
|
|
128
|
+
|
|
129
|
+
```typescript
|
|
130
|
+
const scorecardTranslationDe = createTranslationMessages({
|
|
131
|
+
ref: scorecardTranslationRef,
|
|
132
|
+
messages: {
|
|
133
|
+
// ... existing translations
|
|
134
|
+
'metric.your-metric-id.title': 'Ihr übersetzter Titel', // German
|
|
135
|
+
'metric.your-metric-id.description': 'Ihre übersetzte Beschreibung',
|
|
136
|
+
},
|
|
137
|
+
});
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### 3. Translation Key Format
|
|
141
|
+
|
|
142
|
+
Translation keys follow this pattern:
|
|
143
|
+
|
|
144
|
+
- **Metric titles**: `metric.{metric-id}.title`
|
|
145
|
+
- **Metric descriptions**: `metric.{metric-id}.description`
|
|
146
|
+
|
|
147
|
+
### 4. Fallback Behavior
|
|
148
|
+
|
|
149
|
+
If a translation key is not found, the plugin will automatically fall back to:
|
|
150
|
+
|
|
151
|
+
- The original `title` and `description` values from the metric definition
|
|
152
|
+
- For thresholds: capitalized versions of the threshold keys
|
|
153
|
+
|
|
154
|
+
### Example: Adding GitHub Open PRs Translation
|
|
155
|
+
|
|
156
|
+
```typescript
|
|
157
|
+
// In ref.ts
|
|
158
|
+
metric: {
|
|
159
|
+
'github.open_prs': {
|
|
160
|
+
title: 'GitHub open PRs',
|
|
161
|
+
description: 'Current count of open Pull Requests for a given GitHub repository',
|
|
162
|
+
},
|
|
163
|
+
}
|
|
164
|
+
```
|
package/dist/alpha.d.ts
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import * as _backstage_core_plugin_api_alpha from '@backstage/core-plugin-api/alpha';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Translation reference for scorecard plugin
|
|
5
|
+
* @public
|
|
6
|
+
*/
|
|
7
|
+
declare const scorecardTranslationRef: _backstage_core_plugin_api_alpha.TranslationRef<"plugin.scorecard", {
|
|
8
|
+
readonly "emptyState.button": string;
|
|
9
|
+
readonly "emptyState.title": string;
|
|
10
|
+
readonly "emptyState.description": string;
|
|
11
|
+
readonly "emptyState.altText": string;
|
|
12
|
+
readonly "permissionRequired.button": string;
|
|
13
|
+
readonly "permissionRequired.title": string;
|
|
14
|
+
readonly "permissionRequired.description": string;
|
|
15
|
+
readonly "permissionRequired.altText": string;
|
|
16
|
+
readonly "errors.entityMissingProperties": string;
|
|
17
|
+
readonly "errors.invalidApiResponse": string;
|
|
18
|
+
readonly "errors.fetchError": string;
|
|
19
|
+
readonly "errors.metricDataUnavailable": string;
|
|
20
|
+
readonly "errors.invalidThresholds": string;
|
|
21
|
+
readonly "metric.github.open_prs.title": string;
|
|
22
|
+
readonly "metric.github.open_prs.description": string;
|
|
23
|
+
readonly "metric.jira.open_issues.title": string;
|
|
24
|
+
readonly "metric.jira.open_issues.description": string;
|
|
25
|
+
readonly "thresholds.success": string;
|
|
26
|
+
readonly "thresholds.error": string;
|
|
27
|
+
readonly "thresholds.warning": string;
|
|
28
|
+
}>;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Translation Resource for scorecard
|
|
32
|
+
* @public
|
|
33
|
+
*/
|
|
34
|
+
declare const scorecardTranslations: _backstage_core_plugin_api_alpha.TranslationResource<"plugin.scorecard">;
|
|
35
|
+
|
|
36
|
+
export { scorecardTranslationRef, scorecardTranslations };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"alpha.esm.js","sources":[],"sourcesContent":[],"names":[],"mappings":";"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { createApiRef } from '@backstage/core-plugin-api';
|
|
2
|
+
|
|
3
|
+
const scorecardApiRef = createApiRef({
|
|
4
|
+
id: "plugin.scorecard.service"
|
|
5
|
+
});
|
|
6
|
+
class ScorecardApiClient {
|
|
7
|
+
fetchApi;
|
|
8
|
+
discoveryApi;
|
|
9
|
+
constructor(options) {
|
|
10
|
+
this.fetchApi = options.fetchApi;
|
|
11
|
+
this.discoveryApi = options.discoveryApi;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Gets the base URL for the scorecard backend API.
|
|
15
|
+
* @returns Promise resolving to the base URL
|
|
16
|
+
*/
|
|
17
|
+
async getBaseUrl() {
|
|
18
|
+
return await this.discoveryApi.getBaseUrl("scorecard");
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Retrieves scorecard metrics for a specific entity.
|
|
22
|
+
* @param entity - The Backstage entity to get metrics for
|
|
23
|
+
* @param metricIds - Optional array of specific metric IDs to retrieve
|
|
24
|
+
* @returns Promise resolving to an array of metric results
|
|
25
|
+
* @throws Error if the request fails or returns invalid data
|
|
26
|
+
*/
|
|
27
|
+
async getScorecards(entity, metricIds) {
|
|
28
|
+
if (!entity?.kind || !entity?.metadata?.namespace || !entity?.metadata?.name) {
|
|
29
|
+
throw new Error(
|
|
30
|
+
"Entity missing required properties for scorecard lookup"
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
const baseUrl = await this.getBaseUrl();
|
|
34
|
+
const url = new URL(
|
|
35
|
+
`${baseUrl}/metrics/catalog/${entity.kind}/${entity.metadata.namespace}/${entity.metadata.name}`
|
|
36
|
+
);
|
|
37
|
+
if (metricIds) {
|
|
38
|
+
url.searchParams.set("metricIds", metricIds.join(","));
|
|
39
|
+
}
|
|
40
|
+
try {
|
|
41
|
+
const response = await this.fetchApi.fetch(url.toString());
|
|
42
|
+
if (!response.ok) {
|
|
43
|
+
const errorText = await response.text();
|
|
44
|
+
throw new Error(
|
|
45
|
+
`Failed to fetch scorecards: ${response.status} ${response.statusText}. ${errorText}`
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
const data = await response.json();
|
|
49
|
+
if (!Array.isArray(data)) {
|
|
50
|
+
throw new Error("Invalid response format from scorecard API");
|
|
51
|
+
}
|
|
52
|
+
return data;
|
|
53
|
+
} catch (error) {
|
|
54
|
+
if (error instanceof Error) {
|
|
55
|
+
throw error;
|
|
56
|
+
}
|
|
57
|
+
throw new Error(`Unexpected error fetching scorecards: ${String(error)}`);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export { ScorecardApiClient, scorecardApiRef };
|
|
63
|
+
//# sourceMappingURL=index.esm.js.map
|
|
@@ -0,0 +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 { Entity } from '@backstage/catalog-model';\nimport type { MetricResult } from '@red-hat-developer-hub/backstage-plugin-scorecard-common';\n\nexport interface ScorecardApi {\n /**\n * Retrieves scorecard metrics for a specific entity.\n * @param entity - The Backstage entity to get metrics for\n * @param metricIds - Optional array of specific metric IDs to retrieve\n * @returns Promise resolving to an array of metric results\n */\n getScorecards(entity: Entity, metricIds?: string[]): Promise<MetricResult[]>;\n}\n\nexport const scorecardApiRef = createApiRef<ScorecardApi>({\n id: 'plugin.scorecard.service',\n});\n\nexport type ScorecardApiClientOptions = {\n fetchApi: FetchApi;\n discoveryApi: DiscoveryApi;\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 /**\n * Gets the base URL for the scorecard backend API.\n * @returns Promise resolving to the base URL\n */\n async getBaseUrl(): Promise<string> {\n return await this.discoveryApi.getBaseUrl('scorecard');\n }\n\n /**\n * Retrieves scorecard metrics for a specific entity.\n * @param entity - The Backstage entity to get metrics for\n * @param metricIds - Optional array of specific metric IDs to retrieve\n * @returns Promise resolving to an array of metric results\n * @throws Error if the request fails or returns invalid data\n */\n async getScorecards(\n entity: Entity,\n metricIds?: string[],\n ): 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"],"names":[],"mappings":";;AAkCO,MAAM,kBAAkB,YAA2B,CAAA;AAAA,EACxD,EAAI,EAAA;AACN,CAAC;AAWM,MAAM,kBAA2C,CAAA;AAAA,EACrC,QAAA;AAAA,EACA,YAAA;AAAA,EAEjB,YAAY,OAAoC,EAAA;AAC9C,IAAA,IAAA,CAAK,WAAW,OAAQ,CAAA,QAAA;AACxB,IAAA,IAAA,CAAK,eAAe,OAAQ,CAAA,YAAA;AAAA;AAC9B;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,UAA8B,GAAA;AAClC,IAAA,OAAO,MAAM,IAAA,CAAK,YAAa,CAAA,UAAA,CAAW,WAAW,CAAA;AAAA;AACvD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAM,aACJ,CAAA,MAAA,EACA,SACyB,EAAA;AACzB,IACE,IAAA,CAAC,MAAQ,EAAA,IAAA,IACT,CAAC,MAAA,EAAQ,UAAU,SACnB,IAAA,CAAC,MAAQ,EAAA,QAAA,EAAU,IACnB,EAAA;AACA,MAAA,MAAM,IAAI,KAAA;AAAA,QACR;AAAA,OACF;AAAA;AAGF,IAAM,MAAA,OAAA,GAAU,MAAM,IAAA,CAAK,UAAW,EAAA;AACtC,IAAA,MAAM,MAAM,IAAI,GAAA;AAAA,MACd,CAAG,EAAA,OAAO,CAAoB,iBAAA,EAAA,MAAA,CAAO,IAAI,CAAA,CAAA,EAAI,MAAO,CAAA,QAAA,CAAS,SAAS,CAAA,CAAA,EAAI,MAAO,CAAA,QAAA,CAAS,IAAI,CAAA;AAAA,KAChG;AAEA,IAAA,IAAI,SAAW,EAAA;AACb,MAAA,GAAA,CAAI,aAAa,GAAI,CAAA,WAAA,EAAa,SAAU,CAAA,IAAA,CAAK,GAAG,CAAC,CAAA;AAAA;AAGvD,IAAI,IAAA;AACF,MAAA,MAAM,WAAW,MAAM,IAAA,CAAK,SAAS,KAAM,CAAA,GAAA,CAAI,UAAU,CAAA;AAEzD,MAAI,IAAA,CAAC,SAAS,EAAI,EAAA;AAChB,QAAM,MAAA,SAAA,GAAY,MAAM,QAAA,CAAS,IAAK,EAAA;AACtC,QAAA,MAAM,IAAI,KAAA;AAAA,UACR,+BAA+B,QAAS,CAAA,MAAM,IAAI,QAAS,CAAA,UAAU,KAAK,SAAS,CAAA;AAAA,SACrF;AAAA;AAGF,MAAM,MAAA,IAAA,GAAO,MAAM,QAAA,CAAS,IAAK,EAAA;AAEjC,MAAA,IAAI,CAAC,KAAA,CAAM,OAAQ,CAAA,IAAI,CAAG,EAAA;AACxB,QAAM,MAAA,IAAI,MAAM,4CAA4C,CAAA;AAAA;AAG9D,MAAO,OAAA,IAAA;AAAA,aACA,KAAO,EAAA;AACd,MAAA,IAAI,iBAAiB,KAAO,EAAA;AAC1B,QAAM,MAAA,KAAA;AAAA;AAER,MAAA,MAAM,IAAI,KAAM,CAAA,CAAA,sCAAA,EAAyC,MAAO,CAAA,KAAK,CAAC,CAAE,CAAA,CAAA;AAAA;AAC1E;AAEJ;;;;"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
2
|
+
import { useApi, configApiRef } from '@backstage/core-plugin-api';
|
|
3
|
+
import Box from '@mui/material/Box';
|
|
4
|
+
import Typography from '@mui/material/Typography';
|
|
5
|
+
import Button from '@mui/material/Button';
|
|
6
|
+
import Grid from '@mui/material/Grid';
|
|
7
|
+
import OpenInNewOutlinedIcon from '@mui/icons-material/OpenInNewOutlined';
|
|
8
|
+
import noScorecardsSvg from '../../images/no-scorecards.svg';
|
|
9
|
+
import { useTranslation } from '../../hooks/useTranslation.esm.js';
|
|
10
|
+
|
|
11
|
+
const NoScorecardsState = () => {
|
|
12
|
+
const { t } = useTranslation();
|
|
13
|
+
const configApi = useApi(configApiRef);
|
|
14
|
+
const supportUrl = configApi.getOptionalString("app.support.url") ?? "https://access.redhat.com/documentation/red_hat_developer_hub";
|
|
15
|
+
return /* @__PURE__ */ jsx(Box, { sx: { p: 4, height: "100%", maxWidth: "1592px", margin: "auto" }, children: /* @__PURE__ */ jsxs(
|
|
16
|
+
Grid,
|
|
17
|
+
{
|
|
18
|
+
container: true,
|
|
19
|
+
spacing: 4,
|
|
20
|
+
alignItems: "center",
|
|
21
|
+
justifyContent: "center",
|
|
22
|
+
height: "100%",
|
|
23
|
+
children: [
|
|
24
|
+
/* @__PURE__ */ jsxs(Grid, { item: true, xs: 12, md: 6, sx: { textAlign: "left" }, children: [
|
|
25
|
+
/* @__PURE__ */ jsx(
|
|
26
|
+
Typography,
|
|
27
|
+
{
|
|
28
|
+
sx: (theme) => ({
|
|
29
|
+
fontSize: "2.5rem",
|
|
30
|
+
fontWeight: 300,
|
|
31
|
+
color: theme.palette.text.primary,
|
|
32
|
+
mb: 2
|
|
33
|
+
}),
|
|
34
|
+
children: t("emptyState.title")
|
|
35
|
+
}
|
|
36
|
+
),
|
|
37
|
+
/* @__PURE__ */ jsx(
|
|
38
|
+
Typography,
|
|
39
|
+
{
|
|
40
|
+
sx: (theme) => ({
|
|
41
|
+
fontSize: "1rem",
|
|
42
|
+
color: theme.palette.text.secondary,
|
|
43
|
+
mb: 3,
|
|
44
|
+
lineHeight: 1.5
|
|
45
|
+
}),
|
|
46
|
+
children: t("emptyState.description")
|
|
47
|
+
}
|
|
48
|
+
),
|
|
49
|
+
/* @__PURE__ */ jsx(
|
|
50
|
+
Button,
|
|
51
|
+
{
|
|
52
|
+
color: "primary",
|
|
53
|
+
variant: "contained",
|
|
54
|
+
size: "large",
|
|
55
|
+
component: "a",
|
|
56
|
+
href: supportUrl,
|
|
57
|
+
target: "_blank",
|
|
58
|
+
rel: "noopener noreferrer",
|
|
59
|
+
endIcon: /* @__PURE__ */ jsx(OpenInNewOutlinedIcon, { sx: { width: 16, height: 16, ml: 1 } }),
|
|
60
|
+
children: t("emptyState.button")
|
|
61
|
+
}
|
|
62
|
+
)
|
|
63
|
+
] }),
|
|
64
|
+
/* @__PURE__ */ jsx(Grid, { item: true, xs: 12, md: 6, sx: { textAlign: "right" }, children: /* @__PURE__ */ jsx(
|
|
65
|
+
Box,
|
|
66
|
+
{
|
|
67
|
+
component: "img",
|
|
68
|
+
src: noScorecardsSvg,
|
|
69
|
+
alt: t("emptyState.altText"),
|
|
70
|
+
sx: {
|
|
71
|
+
width: "100%",
|
|
72
|
+
maxWidth: "600px",
|
|
73
|
+
height: "auto"
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
) })
|
|
77
|
+
]
|
|
78
|
+
}
|
|
79
|
+
) });
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
export { NoScorecardsState as default };
|
|
83
|
+
//# sourceMappingURL=NoScorecardsState.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"NoScorecardsState.esm.js","sources":["../../../src/components/Common/NoScorecardsState.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 { useApi, configApiRef } from '@backstage/core-plugin-api';\n\nimport Box from '@mui/material/Box';\nimport Typography from '@mui/material/Typography';\nimport Button from '@mui/material/Button';\nimport Grid from '@mui/material/Grid';\nimport OpenInNewOutlinedIcon from '@mui/icons-material/OpenInNewOutlined';\n\nimport noScorecardsSvg from '../../images/no-scorecards.svg';\nimport { useTranslation } from '../../hooks/useTranslation';\n\nconst NoScorecardsState: React.FC = () => {\n const { t } = useTranslation();\n\n const configApi = useApi(configApiRef);\n const supportUrl =\n configApi.getOptionalString('app.support.url') ??\n 'https://access.redhat.com/documentation/red_hat_developer_hub';\n\n return (\n <Box sx={{ p: 4, height: '100%', maxWidth: '1592px', margin: 'auto' }}>\n <Grid\n container\n spacing={4}\n alignItems=\"center\"\n justifyContent=\"center\"\n height=\"100%\"\n >\n <Grid item xs={12} md={6} sx={{ textAlign: 'left' }}>\n <Typography\n sx={theme => ({\n fontSize: '2.5rem',\n fontWeight: 300,\n color: theme.palette.text.primary,\n mb: 2,\n })}\n >\n {t('emptyState.title')}\n </Typography>\n\n <Typography\n sx={theme => ({\n fontSize: '1rem',\n color: theme.palette.text.secondary,\n mb: 3,\n lineHeight: 1.5,\n })}\n >\n {t('emptyState.description')}\n </Typography>\n\n <Button\n color=\"primary\"\n variant=\"contained\"\n size=\"large\"\n component=\"a\"\n href={supportUrl}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n endIcon={\n <OpenInNewOutlinedIcon sx={{ width: 16, height: 16, ml: 1 }} />\n }\n >\n {t('emptyState.button')}\n </Button>\n </Grid>\n\n <Grid item xs={12} md={6} sx={{ textAlign: 'right' }}>\n <Box\n component=\"img\"\n src={noScorecardsSvg}\n alt={t('emptyState.altText')}\n sx={{\n width: '100%',\n maxWidth: '600px',\n height: 'auto',\n }}\n />\n </Grid>\n </Grid>\n </Box>\n );\n};\n\nexport default NoScorecardsState;\n"],"names":[],"mappings":";;;;;;;;;;AA2BA,MAAM,oBAA8B,MAAM;AACxC,EAAM,MAAA,EAAE,CAAE,EAAA,GAAI,cAAe,EAAA;AAE7B,EAAM,MAAA,SAAA,GAAY,OAAO,YAAY,CAAA;AACrC,EAAA,MAAM,UACJ,GAAA,SAAA,CAAU,iBAAkB,CAAA,iBAAiB,CAC7C,IAAA,+DAAA;AAEF,EAAA,uBACG,GAAA,CAAA,GAAA,EAAA,EAAI,EAAI,EAAA,EAAE,CAAG,EAAA,CAAA,EAAG,MAAQ,EAAA,MAAA,EAAQ,QAAU,EAAA,QAAA,EAAU,MAAQ,EAAA,MAAA,EAC3D,EAAA,QAAA,kBAAA,IAAA;AAAA,IAAC,IAAA;AAAA,IAAA;AAAA,MACC,SAAS,EAAA,IAAA;AAAA,MACT,OAAS,EAAA,CAAA;AAAA,MACT,UAAW,EAAA,QAAA;AAAA,MACX,cAAe,EAAA,QAAA;AAAA,MACf,MAAO,EAAA,MAAA;AAAA,MAEP,QAAA,EAAA;AAAA,wBAAC,IAAA,CAAA,IAAA,EAAA,EAAK,IAAI,EAAA,IAAA,EAAC,EAAI,EAAA,EAAA,EAAI,EAAI,EAAA,CAAA,EAAG,EAAI,EAAA,EAAE,SAAW,EAAA,MAAA,EACzC,EAAA,QAAA,EAAA;AAAA,0BAAA,GAAA;AAAA,YAAC,UAAA;AAAA,YAAA;AAAA,cACC,IAAI,CAAU,KAAA,MAAA;AAAA,gBACZ,QAAU,EAAA,QAAA;AAAA,gBACV,UAAY,EAAA,GAAA;AAAA,gBACZ,KAAA,EAAO,KAAM,CAAA,OAAA,CAAQ,IAAK,CAAA,OAAA;AAAA,gBAC1B,EAAI,EAAA;AAAA,eACN,CAAA;AAAA,cAEC,YAAE,kBAAkB;AAAA;AAAA,WACvB;AAAA,0BAEA,GAAA;AAAA,YAAC,UAAA;AAAA,YAAA;AAAA,cACC,IAAI,CAAU,KAAA,MAAA;AAAA,gBACZ,QAAU,EAAA,MAAA;AAAA,gBACV,KAAA,EAAO,KAAM,CAAA,OAAA,CAAQ,IAAK,CAAA,SAAA;AAAA,gBAC1B,EAAI,EAAA,CAAA;AAAA,gBACJ,UAAY,EAAA;AAAA,eACd,CAAA;AAAA,cAEC,YAAE,wBAAwB;AAAA;AAAA,WAC7B;AAAA,0BAEA,GAAA;AAAA,YAAC,MAAA;AAAA,YAAA;AAAA,cACC,KAAM,EAAA,SAAA;AAAA,cACN,OAAQ,EAAA,WAAA;AAAA,cACR,IAAK,EAAA,OAAA;AAAA,cACL,SAAU,EAAA,GAAA;AAAA,cACV,IAAM,EAAA,UAAA;AAAA,cACN,MAAO,EAAA,QAAA;AAAA,cACP,GAAI,EAAA,qBAAA;AAAA,cACJ,OAAA,kBACG,GAAA,CAAA,qBAAA,EAAA,EAAsB,EAAI,EAAA,EAAE,KAAO,EAAA,EAAA,EAAI,MAAQ,EAAA,EAAA,EAAI,EAAI,EAAA,CAAA,EAAK,EAAA,CAAA;AAAA,cAG9D,YAAE,mBAAmB;AAAA;AAAA;AACxB,SACF,EAAA,CAAA;AAAA,wBAEC,GAAA,CAAA,IAAA,EAAA,EAAK,IAAI,EAAA,IAAA,EAAC,EAAI,EAAA,EAAA,EAAI,EAAI,EAAA,CAAA,EAAG,EAAI,EAAA,EAAE,SAAW,EAAA,OAAA,EACzC,EAAA,QAAA,kBAAA,GAAA;AAAA,UAAC,GAAA;AAAA,UAAA;AAAA,YACC,SAAU,EAAA,KAAA;AAAA,YACV,GAAK,EAAA,eAAA;AAAA,YACL,GAAA,EAAK,EAAE,oBAAoB,CAAA;AAAA,YAC3B,EAAI,EAAA;AAAA,cACF,KAAO,EAAA,MAAA;AAAA,cACP,QAAU,EAAA,OAAA;AAAA,cACV,MAAQ,EAAA;AAAA;AACV;AAAA,SAEJ,EAAA;AAAA;AAAA;AAAA,GAEJ,EAAA,CAAA;AAEJ;;;;"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
2
|
+
import Box from '@mui/material/Box';
|
|
3
|
+
import Grid from '@mui/material/Grid';
|
|
4
|
+
import Button from '@mui/material/Button';
|
|
5
|
+
import Typography from '@mui/material/Typography';
|
|
6
|
+
import OpenInNewIcon from '@mui/icons-material/OpenInNew';
|
|
7
|
+
import { useTranslation } from '../../hooks/useTranslation.esm.js';
|
|
8
|
+
import permissionRequiredSvg from '../../images/permission-required.svg';
|
|
9
|
+
|
|
10
|
+
const PermissionRequiredState = () => {
|
|
11
|
+
const { t } = useTranslation();
|
|
12
|
+
return /* @__PURE__ */ jsx(Box, { sx: { p: 4, height: "100%", maxWidth: "1592px", margin: "auto" }, children: /* @__PURE__ */ jsxs(
|
|
13
|
+
Grid,
|
|
14
|
+
{
|
|
15
|
+
container: true,
|
|
16
|
+
spacing: 4,
|
|
17
|
+
alignItems: "center",
|
|
18
|
+
justifyContent: "center",
|
|
19
|
+
height: "100%",
|
|
20
|
+
children: [
|
|
21
|
+
/* @__PURE__ */ jsxs(Grid, { item: true, xs: 12, md: 6, sx: { textAlign: "left" }, children: [
|
|
22
|
+
/* @__PURE__ */ jsx(
|
|
23
|
+
Typography,
|
|
24
|
+
{
|
|
25
|
+
sx: (theme) => ({
|
|
26
|
+
fontSize: "2.5rem",
|
|
27
|
+
fontWeight: 400,
|
|
28
|
+
color: theme.palette.text.primary,
|
|
29
|
+
mb: 2
|
|
30
|
+
}),
|
|
31
|
+
children: t("permissionRequired.title")
|
|
32
|
+
}
|
|
33
|
+
),
|
|
34
|
+
/* @__PURE__ */ jsx(
|
|
35
|
+
Typography,
|
|
36
|
+
{
|
|
37
|
+
sx: (theme) => ({
|
|
38
|
+
fontSize: "1rem",
|
|
39
|
+
color: theme.palette.text.secondary,
|
|
40
|
+
mb: 3,
|
|
41
|
+
lineHeight: 1.5
|
|
42
|
+
}),
|
|
43
|
+
children: t("permissionRequired.description", {
|
|
44
|
+
permission: /* @__PURE__ */ jsx(
|
|
45
|
+
Typography,
|
|
46
|
+
{
|
|
47
|
+
component: "span",
|
|
48
|
+
sx: (theme) => ({
|
|
49
|
+
fontWeight: "bold",
|
|
50
|
+
color: theme.palette.text.primary
|
|
51
|
+
}),
|
|
52
|
+
children: "scorecard.metric.read"
|
|
53
|
+
}
|
|
54
|
+
)
|
|
55
|
+
})
|
|
56
|
+
}
|
|
57
|
+
),
|
|
58
|
+
/* @__PURE__ */ jsxs(
|
|
59
|
+
Button,
|
|
60
|
+
{
|
|
61
|
+
variant: "outlined",
|
|
62
|
+
target: "_blank",
|
|
63
|
+
href: "https://github.com/redhat-developer/rhdh-plugins/blob/main/workspaces/scorecard/plugins/scorecard/README.md#permission-framework-support",
|
|
64
|
+
sx: (theme) => ({
|
|
65
|
+
color: theme.palette.primary.main
|
|
66
|
+
}),
|
|
67
|
+
children: [
|
|
68
|
+
t("permissionRequired.button"),
|
|
69
|
+
" \xA0 ",
|
|
70
|
+
/* @__PURE__ */ jsx(OpenInNewIcon, {})
|
|
71
|
+
]
|
|
72
|
+
}
|
|
73
|
+
)
|
|
74
|
+
] }),
|
|
75
|
+
/* @__PURE__ */ jsx(Grid, { item: true, xs: 12, md: 6, sx: { textAlign: "right" }, children: /* @__PURE__ */ jsx(
|
|
76
|
+
Box,
|
|
77
|
+
{
|
|
78
|
+
component: "img",
|
|
79
|
+
src: permissionRequiredSvg,
|
|
80
|
+
alt: t("permissionRequired.altText"),
|
|
81
|
+
sx: {
|
|
82
|
+
width: "100%",
|
|
83
|
+
maxWidth: "600px",
|
|
84
|
+
height: "auto"
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
) })
|
|
88
|
+
]
|
|
89
|
+
}
|
|
90
|
+
) });
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
export { PermissionRequiredState as default };
|
|
94
|
+
//# sourceMappingURL=PermissionRequiredState.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PermissionRequiredState.esm.js","sources":["../../../src/components/Common/PermissionRequiredState.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 Grid from '@mui/material/Grid';\nimport Button from '@mui/material/Button';\nimport Typography from '@mui/material/Typography';\nimport OpenInNewIcon from '@mui/icons-material/OpenInNew';\n\nimport { useTranslation } from '../../hooks/useTranslation';\nimport permissionRequiredSvg from '../../images/permission-required.svg';\n\nconst PermissionRequiredState = () => {\n const { t } = useTranslation();\n\n return (\n <Box sx={{ p: 4, height: '100%', maxWidth: '1592px', margin: 'auto' }}>\n <Grid\n container\n spacing={4}\n alignItems=\"center\"\n justifyContent=\"center\"\n height=\"100%\"\n >\n <Grid item xs={12} md={6} sx={{ textAlign: 'left' }}>\n <Typography\n sx={theme => ({\n fontSize: '2.5rem',\n fontWeight: 400,\n color: theme.palette.text.primary,\n mb: 2,\n })}\n >\n {t('permissionRequired.title')}\n </Typography>\n\n <Typography\n sx={theme => ({\n fontSize: '1rem',\n color: theme.palette.text.secondary,\n mb: 3,\n lineHeight: 1.5,\n })}\n >\n {t('permissionRequired.description' as any, {\n permission: (\n <Typography\n component=\"span\"\n sx={theme => ({\n fontWeight: 'bold',\n color: theme.palette.text.primary,\n })}\n >\n scorecard.metric.read\n </Typography>\n ),\n })}\n </Typography>\n\n <Button\n variant=\"outlined\"\n target=\"_blank\"\n href=\"https://github.com/redhat-developer/rhdh-plugins/blob/main/workspaces/scorecard/plugins/scorecard/README.md#permission-framework-support\"\n sx={theme => ({\n color: theme.palette.primary.main,\n })}\n >\n {t('permissionRequired.button')} <OpenInNewIcon />\n </Button>\n </Grid>\n\n <Grid item xs={12} md={6} sx={{ textAlign: 'right' }}>\n <Box\n component=\"img\"\n src={permissionRequiredSvg}\n alt={t('permissionRequired.altText')}\n sx={{\n width: '100%',\n maxWidth: '600px',\n height: 'auto',\n }}\n />\n </Grid>\n </Grid>\n </Box>\n );\n};\n\nexport default PermissionRequiredState;\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { jsx } from 'react/jsx-runtime';
|
|
2
|
+
import { ResponseErrorPanel } from '@backstage/core-components';
|
|
3
|
+
import Box from '@mui/material/Box';
|
|
4
|
+
import CircularProgress from '@mui/material/CircularProgress';
|
|
5
|
+
import NoScorecardsState from '../Common/NoScorecardsState.esm.js';
|
|
6
|
+
import Scorecard from './Scorecard.esm.js';
|
|
7
|
+
import { useScorecards } from '../../hooks/useScorecards.esm.js';
|
|
8
|
+
import { getStatusConfig } from '../../utils/utils.esm.js';
|
|
9
|
+
import { useScorecardMetricsReadPermission } from '../../hooks/useScorecardMetricsReadPermission.esm.js';
|
|
10
|
+
import PermissionRequiredState from '../Common/PermissionRequiredState.esm.js';
|
|
11
|
+
import { useTranslation } from '../../hooks/useTranslation.esm.js';
|
|
12
|
+
|
|
13
|
+
const EntityScorecardContent = () => {
|
|
14
|
+
const { scorecards, loadingData, error } = useScorecards();
|
|
15
|
+
const { t } = useTranslation();
|
|
16
|
+
const {
|
|
17
|
+
allowed: hasReadScorecardMetricsPermission,
|
|
18
|
+
loading: loadingPermission
|
|
19
|
+
} = useScorecardMetricsReadPermission();
|
|
20
|
+
if (loadingData || loadingPermission) {
|
|
21
|
+
return /* @__PURE__ */ jsx(
|
|
22
|
+
Box,
|
|
23
|
+
{
|
|
24
|
+
display: "flex",
|
|
25
|
+
justifyContent: "center",
|
|
26
|
+
alignItems: "center",
|
|
27
|
+
minHeight: "200px",
|
|
28
|
+
children: /* @__PURE__ */ jsx(CircularProgress, {})
|
|
29
|
+
}
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
if (!hasReadScorecardMetricsPermission) {
|
|
33
|
+
return /* @__PURE__ */ jsx(PermissionRequiredState, {});
|
|
34
|
+
}
|
|
35
|
+
if (error) {
|
|
36
|
+
return /* @__PURE__ */ jsx(ResponseErrorPanel, { error });
|
|
37
|
+
}
|
|
38
|
+
if (!loadingData && scorecards?.length === 0) {
|
|
39
|
+
return /* @__PURE__ */ jsx(NoScorecardsState, {});
|
|
40
|
+
}
|
|
41
|
+
return /* @__PURE__ */ jsx(
|
|
42
|
+
Box,
|
|
43
|
+
{
|
|
44
|
+
display: "flex",
|
|
45
|
+
flexWrap: "wrap",
|
|
46
|
+
gap: 2,
|
|
47
|
+
sx: { alignItems: "flex-start" },
|
|
48
|
+
children: scorecards?.map((metric) => {
|
|
49
|
+
const isMetricDataError = metric.status === "error" || metric.result?.value === void 0;
|
|
50
|
+
const isThresholdError = metric.result?.thresholdResult?.status === "error";
|
|
51
|
+
const statusConfig = getStatusConfig({
|
|
52
|
+
evaluation: metric.result?.thresholdResult?.evaluation,
|
|
53
|
+
thresholdStatus: metric.result?.thresholdResult?.status,
|
|
54
|
+
metricStatus: metric.status
|
|
55
|
+
});
|
|
56
|
+
const titleKey = `metric.${metric.id}.title`;
|
|
57
|
+
const descriptionKey = `metric.${metric.id}.description`;
|
|
58
|
+
const title = t(titleKey, {});
|
|
59
|
+
const description = t(descriptionKey, {});
|
|
60
|
+
const finalTitle = title === titleKey ? metric.metadata.title : title;
|
|
61
|
+
const finalDescription = description === descriptionKey ? metric.metadata.description : description;
|
|
62
|
+
return /* @__PURE__ */ jsx(
|
|
63
|
+
Scorecard,
|
|
64
|
+
{
|
|
65
|
+
cardTitle: finalTitle,
|
|
66
|
+
description: finalDescription,
|
|
67
|
+
loading: false,
|
|
68
|
+
statusColor: statusConfig.color,
|
|
69
|
+
StatusIcon: statusConfig.icon ?? (() => null),
|
|
70
|
+
value: metric.result?.value,
|
|
71
|
+
thresholds: metric.result?.thresholdResult,
|
|
72
|
+
isMetricDataError,
|
|
73
|
+
metricDataError: metric?.error,
|
|
74
|
+
isThresholdError,
|
|
75
|
+
thresholdError: metric.result?.thresholdResult?.error
|
|
76
|
+
},
|
|
77
|
+
metric.id
|
|
78
|
+
);
|
|
79
|
+
})
|
|
80
|
+
}
|
|
81
|
+
);
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
export { EntityScorecardContent };
|
|
85
|
+
//# sourceMappingURL=EntityScorecardContent.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EntityScorecardContent.esm.js","sources":["../../../src/components/Scorecard/EntityScorecardContent.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 { MetricResult } from '@red-hat-developer-hub/backstage-plugin-scorecard-common';\nimport { ResponseErrorPanel } from '@backstage/core-components';\n\nimport Box from '@mui/material/Box';\nimport CircularProgress from '@mui/material/CircularProgress';\n\nimport NoScorecardsState from '../Common/NoScorecardsState';\nimport Scorecard from './Scorecard';\nimport { useScorecards } from '../../hooks/useScorecards';\nimport { getStatusConfig } from '../../utils/utils';\nimport { useScorecardMetricsReadPermission } from '../../hooks/useScorecardMetricsReadPermission';\nimport PermissionRequiredState from '../Common/PermissionRequiredState';\nimport { useTranslation } from '../../hooks/useTranslation';\n\nexport const EntityScorecardContent = () => {\n const { scorecards, loadingData, error } = useScorecards();\n const { t } = useTranslation();\n\n const {\n allowed: hasReadScorecardMetricsPermission,\n loading: loadingPermission,\n } = useScorecardMetricsReadPermission();\n\n if (loadingData || loadingPermission) {\n return (\n <Box\n display=\"flex\"\n justifyContent=\"center\"\n alignItems=\"center\"\n minHeight=\"200px\"\n >\n <CircularProgress />\n </Box>\n );\n }\n\n if (!hasReadScorecardMetricsPermission) {\n return <PermissionRequiredState />;\n }\n\n if (error) {\n return <ResponseErrorPanel error={error} />;\n }\n\n if (!loadingData && scorecards?.length === 0) {\n return <NoScorecardsState />;\n }\n\n return (\n <Box\n display=\"flex\"\n flexWrap=\"wrap\"\n gap={2}\n sx={{ alignItems: 'flex-start' }}\n >\n {scorecards?.map((metric: MetricResult) => {\n // Check if metric data unavailable\n const isMetricDataError =\n metric.status === 'error' || metric.result?.value === undefined;\n\n // Check if threshold has an error\n const isThresholdError =\n metric.result?.thresholdResult?.status === 'error';\n\n const statusConfig = getStatusConfig({\n evaluation: metric.result?.thresholdResult?.evaluation,\n thresholdStatus: metric.result?.thresholdResult?.status,\n metricStatus: metric.status,\n });\n\n // Use metric ID to construct translation keys, fallback to original title/description\n const titleKey = `metric.${metric.id}.title`;\n const descriptionKey = `metric.${metric.id}.description`;\n\n const title = t(titleKey as any, {});\n const description = t(descriptionKey as any, {});\n\n // If translation returns the key itself, fallback to original title/description\n const finalTitle = title === titleKey ? metric.metadata.title : title;\n const finalDescription =\n description === descriptionKey\n ? metric.metadata.description\n : description;\n\n return (\n <Scorecard\n key={metric.id}\n cardTitle={finalTitle}\n description={finalDescription}\n loading={false}\n statusColor={statusConfig.color}\n StatusIcon={statusConfig.icon ?? (() => null)}\n value={metric.result?.value}\n thresholds={metric.result?.thresholdResult}\n isMetricDataError={isMetricDataError}\n metricDataError={metric?.error}\n isThresholdError={isThresholdError}\n thresholdError={metric.result?.thresholdResult?.error}\n />\n );\n })}\n </Box>\n );\n};\n"],"names":[],"mappings":"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"}
|