Skip to content

Commit e087d06

Browse files
fix(scorecard): add UI with mock data for Scorecard (#1279)
* fix(scorecard): add UI with mock data for Scorecard * fix: type error
1 parent cf9f6d0 commit e087d06

7 files changed

Lines changed: 745 additions & 90 deletions

File tree

workspaces/scorecard/plugins/scorecard/src/components/ScorecardPage/Scorecard.tsx

Lines changed: 88 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -13,45 +13,53 @@
1313
* See the License for the specific language governing permissions and
1414
* limitations under the License.
1515
*/
16-
import { ChartDonut, ChartThemeColor } from '@patternfly/react-charts/victory';
17-
import WarningAmberIcon from '@mui/icons-material/WarningAmber';
16+
17+
import { ChartDonut } from '@patternfly/react-charts/victory';
1818
import Card from '@mui/material/Card';
1919
import CardContent from '@mui/material/CardContent';
2020
import Typography from '@mui/material/Typography';
2121
import Box from '@mui/material/Box';
2222
import Divider from '@mui/material/Divider';
23+
import CircularProgress from '@mui/material/CircularProgress';
2324

24-
export const Scorecard = () => {
25-
const openPRs = 22;
26-
27-
// Determine status
28-
let statusColor = ChartThemeColor.gray;
29-
30-
if (openPRs >= 10 && openPRs <= 50) {
31-
statusColor = '#F0AB00';
32-
} else if (openPRs > 50) {
33-
statusColor = '#C9190B';
34-
}
25+
interface ScorecardProps {
26+
cardTitle: string;
27+
description: string;
28+
loading: boolean;
29+
statusColor: string;
30+
StatusIcon: React.ElementType;
31+
value: number;
32+
thresholds: { key: string; expression: string }[];
33+
}
3534

35+
const Scorecard = ({
36+
cardTitle,
37+
description,
38+
loading,
39+
statusColor,
40+
StatusIcon,
41+
value,
42+
thresholds,
43+
}: ScorecardProps) => {
3644
return (
3745
<Card
3846
style={{
3947
width: '364px',
4048
borderRadius: 8,
41-
border: '2px solid #ffffff1f', // #0000001f
49+
border: '2px solid #0000001f',
4250
boxShadow: 'none',
4351
}}
4452
>
4553
<CardContent style={{ padding: 0 }}>
4654
<Box sx={{ p: 2 }}>
4755
<Typography variant="h6" style={{ fontWeight: 500 }}>
48-
Github open PRs
56+
{cardTitle}
4957
</Typography>
5058
</Box>
5159
<Divider />
5260
<Box sx={{ p: 2 }}>
5361
<Typography variant="body2" color="textSecondary">
54-
Current count of open Pull Requests for a given GitHub repository.
62+
{description}
5563
</Typography>
5664
</Box>
5765

@@ -62,77 +70,77 @@ export const Scorecard = () => {
6270
sx={{ p: 2 }}
6371
>
6472
<Box position="relative" width={200} height={200}>
65-
{/* ChartDonut */}
66-
<ChartDonut
67-
ariaDesc="Open PRs donut chart"
68-
ariaTitle="Open PRs"
69-
constrainToVisibleArea
70-
data={[{ x: 'Open PRs', y: openPRs }]}
71-
height={200}
72-
width={200}
73-
colorScale={[statusColor]}
74-
/>
75-
76-
{/* Overlay Content */}
77-
<Box
78-
position="absolute"
79-
top="50%"
80-
left="50%"
81-
sx={{
82-
transform: 'translate(-50%, -50%)',
83-
display: 'flex',
84-
flexDirection: 'column',
85-
alignItems: 'center',
86-
}}
87-
>
88-
<WarningAmberIcon sx={{ color: statusColor, fontSize: 28 }} />
89-
<Typography
90-
variant="h6"
91-
style={{ color: statusColor, fontWeight: 600 }}
73+
{loading ? (
74+
<Box
75+
sx={{
76+
position: 'absolute',
77+
top: '50%',
78+
left: '50%',
79+
transform: 'translate(-50%, -50%)',
80+
}}
9281
>
93-
{openPRs}
94-
</Typography>
95-
</Box>
82+
<CircularProgress />
83+
</Box>
84+
) : (
85+
<>
86+
<ChartDonut
87+
constrainToVisibleArea
88+
data={[{ x: cardTitle, y: value === 0 ? 1 : value }]}
89+
height={200}
90+
width={200}
91+
colorScale={[statusColor]}
92+
/>
93+
<Box
94+
position="absolute"
95+
top="50%"
96+
left="50%"
97+
sx={{
98+
transform: 'translate(-50%, -50%)',
99+
display: 'flex',
100+
flexDirection: 'column',
101+
alignItems: 'center',
102+
}}
103+
>
104+
<StatusIcon sx={{ color: statusColor, fontSize: 24 }} />
105+
<Typography
106+
variant="h6"
107+
style={{ color: statusColor, fontWeight: 600 }}
108+
>
109+
{value}
110+
</Typography>
111+
</Box>
112+
</>
113+
)}
96114
</Box>
97115

98116
<Box sx={{ p: 0 }}>
99-
<div
100-
style={{ display: 'flex', alignItems: 'center', marginBottom: 8 }}
101-
>
102-
<div
103-
style={{
104-
width: 10,
105-
height: 10,
106-
backgroundColor: '#3E8635',
107-
marginRight: 8,
108-
}}
109-
/>
110-
<Typography variant="body2">Ideal &lt; 10</Typography>
111-
</div>
112-
<div
113-
style={{ display: 'flex', alignItems: 'center', marginBottom: 8 }}
114-
>
115-
<div
116-
style={{
117-
width: 10,
118-
height: 10,
119-
backgroundColor: '#F0AB00',
120-
marginRight: 8,
121-
}}
122-
/>
123-
<Typography variant="body2">Warning 10-50</Typography>
124-
</div>
125-
<div style={{ display: 'flex', alignItems: 'center' }}>
117+
{thresholds.map(({ key, expression }) => (
126118
<div
119+
key={key}
127120
style={{
128-
width: 10,
129-
height: 10,
130-
backgroundColor: '#C9190B',
131-
marginRight: 8,
121+
display: 'flex',
122+
alignItems: 'center',
123+
marginBottom: 8,
132124
}}
133-
/>
134-
<Typography variant="body2">Critical &gt; 50</Typography>
135-
</div>
125+
>
126+
<div
127+
style={{
128+
width: 10,
129+
height: 10,
130+
backgroundColor:
131+
{
132+
error: 'red',
133+
warning: 'orange',
134+
success: 'green',
135+
}[key] || 'green',
136+
marginRight: 8,
137+
}}
138+
/>
139+
<Typography variant="body2">
140+
{key} {expression && `${expression}`}
141+
</Typography>
142+
</div>
143+
))}
136144
</Box>
137145
</Box>
138146
</CardContent>

workspaces/scorecard/plugins/scorecard/src/components/ScorecardPage/ScorecardEmptyState.tsx

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,18 +19,19 @@ import Box from '@mui/material/Box';
1919
import Typography from '@mui/material/Typography';
2020
import Button from '@mui/material/Button';
2121
import Grid from '@mui/material/Grid';
22+
import OpenInNewOutlinedIcon from '@mui/icons-material/OpenInNewOutlined';
2223

2324
import noScorecardSvg from '../../images/no-scorecard.svg';
2425

2526
const ScorecardEmptyState: React.FC = () => {
2627
return (
27-
<Box sx={{ minHeight: '500px', p: 4 }}>
28+
<Box sx={{ p: 4 }}>
2829
<Grid container spacing={4} alignItems="center" justifyContent="center">
2930
<Grid item xs={12} md={6}>
3031
<Typography
3132
sx={theme => ({
32-
fontSize: '2rem',
33-
fontWeight: 600,
33+
fontSize: '2.5rem',
34+
fontWeight: 300,
3435
color: theme.palette.text.primary,
3536
mb: 2,
3637
})}
@@ -40,7 +41,7 @@ const ScorecardEmptyState: React.FC = () => {
4041

4142
<Typography
4243
sx={theme => ({
43-
fontSize: '1.125rem',
44+
fontSize: '1rem',
4445
color: theme.palette.text.secondary,
4546
mb: 3,
4647
lineHeight: 1.5,
@@ -58,15 +59,16 @@ const ScorecardEmptyState: React.FC = () => {
5859
px: 3,
5960
py: 1.5,
6061
fontSize: '1rem',
61-
fontWeight: 500,
62+
fontWeight: 400,
6263
textTransform: 'none',
63-
borderRadius: '4px',
64+
borderRadius: '28px',
6465
'&:hover': {
6566
backgroundColor: '#1565c0',
6667
},
6768
}}
6869
>
69-
View documentation
70+
View documentation{' '}
71+
<OpenInNewOutlinedIcon sx={{ width: 16, height: 16, ml: 1 }} />
7072
</Button>
7173
</Link>
7274
</Grid>

workspaces/scorecard/plugins/scorecard/src/components/ScorecardPage/ScorecardPage.tsx

Lines changed: 58 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,67 @@
1414
* limitations under the License.
1515
*/
1616

17+
import { useState, useEffect } from 'react';
18+
19+
import Box from '@mui/material/Box';
20+
import WarningAmberIcon from '@mui/icons-material/WarningAmber';
21+
import CheckCircleOutlineIcon from '@mui/icons-material/CheckCircleOutline';
22+
import DangerousOutlinedIcon from '@mui/icons-material/DangerousOutlined';
23+
24+
import ScorecardEmptyState from './ScorecardEmptyState';
25+
import { fetchMockData } from './mockData';
1726
import Scorecard from './Scorecard';
1827

1928
export const ScorecardPage = () => {
29+
const [data, setData] = useState<any>(null);
30+
const [loading, setLoading] = useState(true);
31+
const [error, setError] = useState<string | null>(null);
32+
33+
useEffect(() => {
34+
fetchMockData()
35+
.then(metrics => {
36+
setData(metrics);
37+
setLoading(false);
38+
})
39+
.catch(err => {
40+
setError(err.message);
41+
setLoading(false);
42+
});
43+
}, []);
44+
45+
if ((!loading && data?.metrics?.length === 0) || error) {
46+
return <ScorecardEmptyState />;
47+
}
48+
2049
return (
21-
<div>
22-
<Scorecard />
23-
</div>
50+
<Box
51+
display="flex"
52+
flexWrap="wrap"
53+
gap={2}
54+
sx={{ alignItems: 'flex-start' }}
55+
>
56+
{data?.metrics?.map((metric: any) => (
57+
<Scorecard
58+
key={metric.id}
59+
cardTitle={metric.metadata.title}
60+
description={metric.metadata.description}
61+
loading={false}
62+
statusColor={(() => {
63+
const label = metric.result.thresholdResult.evaluation;
64+
if (label === 'error') return 'red';
65+
if (label === 'warning') return 'orange';
66+
return 'green';
67+
})()}
68+
StatusIcon={(() => {
69+
const label = metric.result.thresholdResult.evaluation;
70+
if (label === 'error') return DangerousOutlinedIcon;
71+
if (label === 'warning') return WarningAmberIcon;
72+
return CheckCircleOutlineIcon;
73+
})()}
74+
value={metric.result.value}
75+
thresholds={metric.result.thresholdResult.definition.rules}
76+
/>
77+
))}
78+
</Box>
2479
);
2580
};

0 commit comments

Comments
 (0)