1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-02-09 00:18:00 +01:00

feat: Add stale flags counter (#8741)

This PR adds the stale flag component to the health grid. In doing so,
it also reworks the layout of the health row (now a grid) and updates
the health component.

In addition to removing the text from the component, I have adjust the
SVG a bit to make it not shrink on smaller screens and have adjusted
it's spacing, so that it's not full of dead space at the bottom. This
makes it easier to style because it doesn't add 15px of invisible
content.

This PR also touches up a few other visual issues I found, such as
header level and sidebar width.

Wide:

![image](https://github.com/user-attachments/assets/acb57b17-eb7f-4b69-9bfa-1113bb748467)

Medium:

![image](https://github.com/user-attachments/assets/a57331b0-825f-4b20-9b05-3ecd81804f5d)

Narrow:

![image](https://github.com/user-attachments/assets/65c6e8d1-1783-4354-b71b-2867eabcc9ec)
This commit is contained in:
Thomas Heartman 2024-11-14 09:55:21 +01:00 committed by GitHub
parent 3e424ec20d
commit 9440b52464
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 155 additions and 67 deletions

View File

@ -1,5 +1,4 @@
import { useTheme, Typography } from '@mui/material';
import { styled } from '@mui/system';
import { styled, useTheme, Typography } from '@mui/material';
import { Link } from 'react-router-dom';
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
import { useProjectStatus } from 'hooks/api/getters/useProjectStatus/useProjectStatus';
@ -10,21 +9,34 @@ const HealthContainer = styled('div')(({ theme }) => ({
padding: theme.spacing(3),
borderRadius: theme.shape.borderRadiusExtraLarge,
minWidth: '300px',
gridArea: 'health',
}));
const TextContainer = styled('div')(({ theme }) => ({
display: 'flex',
flexDirection: 'column',
gap: theme.spacing(1),
}));
const ChartRow = styled('div')(({ theme }) => ({
display: 'flex',
alignItems: 'center',
gap: theme.spacing(2),
}));
const SVGWrapper = styled('div')(({ theme }) => ({
flex: 'none',
height: 85,
width: 100,
position: 'relative',
}));
const StyledSVG = styled('svg')({
width: 200,
height: 100,
position: 'absolute',
});
const DescriptionText = styled(Typography)(({ theme }) => ({
color: theme.palette.text.secondary,
marginBottom: theme.spacing(2),
const StyledLink = styled(Link)(({ theme }) => ({
fontSize: theme.typography.body2.fontSize,
}));
export const ProjectHealth = () => {
@ -53,6 +65,7 @@ export const ProjectHealth = () => {
return (
<HealthContainer>
<ChartRow>
<SVGWrapper>
<StyledSVG viewBox='0 0 100 100'>
<circle
cx='50'
@ -85,16 +98,19 @@ export const ProjectHealth = () => {
{averageHealth}%
</text>
</StyledSVG>
</SVGWrapper>
<TextContainer>
<Typography variant='body2'>
On average, your project health has remained at{' '}
{averageHealth}% the last 4 weeks
</Typography>
{!isOss() && (
<StyledLink to='/insights'>
View health over time
</StyledLink>
)}
</TextContainer>
</ChartRow>
<DescriptionText variant='body2'>
Remember to archive your stale feature flags to keep the project
health growing
</DescriptionText>
{!isOss() && <Link to='/insights'>View health over time</Link>}
</HealthContainer>
);
};

View File

@ -14,6 +14,7 @@ const Wrapper = styled('article')(({ theme }) => ({
padding: theme.spacing(3),
borderRadius: theme.shape.borderRadiusExtraLarge,
minWidth: '300px',
gridArea: 'resources',
}));
const ProjectResourcesInner = styled('div')(({ theme }) => ({
@ -33,9 +34,9 @@ const ItemContent = styled('span')(({ theme }) => ({
}));
const onNarrowWidget = (css: object) => ({
'@container (max-width: 400px)': css,
'@container (max-width: 385px)': css,
'@supports not (container-type: inline-size)': {
'@media (max-width: 400px)': css,
'@media (max-width: 385px)': css,
},
});
@ -116,8 +117,8 @@ export const ProjectResources = () => {
return (
<Wrapper ref={loadingRef}>
<ProjectResourcesInner>
<Typography variant='h3' sx={{ margin: 0 }}>
Project Resources
<Typography variant='h4' sx={{ margin: 0 }}>
Project resources
</Typography>
<ResourceList>
<ListItem

View File

@ -4,9 +4,12 @@ import { ProjectResources } from './ProjectResources';
import { ProjectActivity } from './ProjectActivity';
import { ProjectHealth } from './ProjectHealth';
import { ProjectLifecycleSummary } from './ProjectLifecycleSummary';
import { StaleFlags } from './StaleFlags';
const ModalContentContainer = styled('div')(({ theme }) => ({
const ModalContentContainer = styled('section')(({ theme }) => ({
minHeight: '100vh',
maxWidth: 1100,
width: '95vw',
backgroundColor: theme.palette.background.default,
padding: theme.spacing(4),
display: 'flex',
@ -19,27 +22,43 @@ type Props = {
close: () => void;
};
const HealthRow = styled('div')(({ theme }) => ({
display: 'flex',
flexFlow: 'row wrap',
padding: theme.spacing(2),
gap: theme.spacing(2),
'&>*': {
// todo: reconsider this value when the health widget is
// implemented. It may not be right, but it works for the
// placeholder
flex: '30%',
const onNarrowGrid = (css: object) => ({
'@container (max-width: 650px)': css,
'@supports not (container-type: inline-size)': {
'@media (max-width: 712px)': css,
},
});
const HealthContainer = styled('div')({
containerType: 'inline-size',
});
const HealthGrid = styled('div')(({ theme }) => ({
display: 'grid',
gridTemplateAreas: `
"health resources"
"stale resources"
`,
gridTemplateColumns: '1fr 1fr',
gap: theme.spacing(1, 2),
...onNarrowGrid({
display: 'flex',
flexDirection: 'column',
gap: theme.spacing(1),
}),
}));
export const ProjectStatusModal = ({ open, close }: Props) => {
return (
<DynamicSidebarModal open={open} onClose={close} label='Project status'>
<ModalContentContainer>
<HealthRow>
<HealthContainer>
<HealthGrid>
<ProjectHealth />
<StaleFlags />
<ProjectResources />
</HealthRow>
</HealthGrid>
</HealthContainer>
<ProjectActivity />

View File

@ -0,0 +1,52 @@
import { Typography } from '@mui/material';
import { styled } from '@mui/material';
import { PrettifyLargeNumber } from 'component/common/PrettifyLargeNumber/PrettifyLargeNumber';
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
import type { FC } from 'react';
import { Link } from 'react-router-dom';
const Wrapper = styled('article')(({ theme }) => ({
backgroundColor: theme.palette.envAccordion.expanded,
padding: theme.spacing(3),
borderRadius: theme.shape.borderRadiusExtraLarge,
minWidth: '300px',
gridArea: 'stale',
display: 'flex',
flexDirection: 'column',
gap: theme.spacing(1),
}));
const BigText = styled('span')(({ theme }) => ({
fontSize: `calc(2 * ${theme.typography.body1.fontSize})`,
lineHeight: 0,
}));
const BigNumber: FC<{ value?: number }> = ({ value }) => {
return (
<BigText>
<PrettifyLargeNumber
value={value ?? 0}
threshold={1000}
precision={1}
/>
</BigText>
);
};
export const StaleFlags = () => {
const projectId = useRequiredPathParam('projectId');
return (
<Wrapper>
<Typography component='h4'>
<BigNumber value={6} />{' '}
<Link to={`/projects/${projectId}?state=IS%3Astale`}>
stale flags
</Link>
</Typography>
<Typography variant='body2'>
Remember to archive your stale feature flags to keep the project
healthy
</Typography>
</Wrapper>
);
};