1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-07-12 13:48:35 +02:00

refactor: fix stats layout and unify components (#6671)

This commit is contained in:
Jaanus Sellin 2024-03-22 10:07:44 +02:00 committed by GitHub
parent e6150def36
commit f5a7cc9125
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 135 additions and 137 deletions

View File

@ -57,7 +57,7 @@ export const ProjectInsights = () => {
<NarrowContainer> <NarrowContainer>
<FlagTypesUsed featureTypeCounts={data.featureTypeCounts} /> <FlagTypesUsed featureTypeCounts={data.featureTypeCounts} />
</NarrowContainer> </NarrowContainer>
<NarrowContainer> <NarrowContainer sx={{ padding: 0 }}>
<ProjectMembers projectId={projectId} members={data.members} /> <ProjectMembers projectId={projectId} members={data.members} />
</NarrowContainer> </NarrowContainer>
<WideContainer> <WideContainer>

View File

@ -34,13 +34,7 @@ export const HelpPopper: FC<IHelpPopperProps> = ({ children, id }) => {
const open = Boolean(anchor); const open = Boolean(anchor);
return ( return (
<Box <Box>
sx={{
position: 'absolute',
top: (theme) => theme.spacing(0.5),
right: (theme) => theme.spacing(0.5),
}}
>
<IconButton onClick={onOpen} aria-describedby={id} size='small'> <IconButton onClick={onOpen} aria-describedby={id} size='small'>
<HelpOutline <HelpOutline
sx={{ sx={{

View File

@ -2,6 +2,9 @@ import { Box, styled, Typography } from '@mui/material';
import type { ProjectStatsSchema } from 'openapi/models'; import type { ProjectStatsSchema } from 'openapi/models';
import { HelpPopper } from './HelpPopper'; import { HelpPopper } from './HelpPopper';
import { StatusBox } from './StatusBox'; import { StatusBox } from './StatusBox';
import KeyboardArrowRight from '@mui/icons-material/KeyboardArrowRight';
import { Link } from 'react-router-dom';
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
const StyledBox = styled(Box)(({ theme }) => ({ const StyledBox = styled(Box)(({ theme }) => ({
display: 'grid', display: 'grid',
@ -16,27 +19,20 @@ const StyledBox = styled(Box)(({ theme }) => ({
}, },
})); }));
const StyledWidget = styled(Box)(({ theme }) => ({
position: 'relative',
padding: theme.spacing(3),
backgroundColor: theme.palette.background.paper,
flex: 1,
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
borderRadius: `${theme.shape.borderRadiusLarge}px`,
[theme.breakpoints.down('lg')]: {
padding: theme.spacing(2),
},
}));
const StyledTimeToProductionDescription = styled(Typography)(({ theme }) => ({ const StyledTimeToProductionDescription = styled(Typography)(({ theme }) => ({
color: theme.palette.text.secondary, color: theme.palette.text.secondary,
fontSize: theme.typography.body2.fontSize, fontSize: theme.typography.body2.fontSize,
lineHeight: theme.typography.body2.lineHeight, lineHeight: theme.typography.body2.lineHeight,
})); }));
const NavigationBar = styled(Link)(({ theme }) => ({
marginLeft: 'auto',
display: 'flex',
justifyContent: 'space-between',
textDecoration: 'none',
color: theme.palette.text.primary,
}));
interface IProjectStatsProps { interface IProjectStatsProps {
stats: ProjectStatsSchema; stats: ProjectStatsSchema;
} }
@ -45,6 +41,7 @@ export const ProjectInsightsStats = ({ stats }: IProjectStatsProps) => {
if (Object.keys(stats).length === 0) { if (Object.keys(stats).length === 0) {
return null; return null;
} }
const projectId = useRequiredPathParam('projectId');
const { const {
avgTimeToProdCurrentWindow, avgTimeToProdCurrentWindow,
@ -58,79 +55,77 @@ export const ProjectInsightsStats = ({ stats }: IProjectStatsProps) => {
return ( return (
<StyledBox> <StyledBox>
<StyledWidget> <StatusBox
<StatusBox title='Total changes'
title='Total changes' boxText={String(projectActivityCurrentWindow)}
boxText={String(projectActivityCurrentWindow)} change={
change={ projectActivityCurrentWindow - projectActivityPastWindow
projectActivityCurrentWindow - projectActivityPastWindow }
} >
> <HelpPopper id='total-changes'>
<HelpPopper id='total-changes'> Sum of all configuration and state modifications in the
Sum of all configuration and state modifications in the project.
project. </HelpPopper>
</HelpPopper> </StatusBox>
</StatusBox> <StatusBox
</StyledWidget> title='Avg. time to production'
<StyledWidget> boxText={
<StatusBox <Box
title='Avg. time to production' sx={{
boxText={ display: 'flex',
<Box alignItems: 'center',
sx={{ gap: (theme) => theme.spacing(1),
display: 'flex', }}
alignItems: 'center', >
gap: (theme) => theme.spacing(1), {avgTimeToProdCurrentWindow}{' '}
}} <Typography component='span'>days</Typography>
> </Box>
{avgTimeToProdCurrentWindow}{' '} }
<Typography component='span'>days</Typography> customChangeElement={
</Box> <StyledTimeToProductionDescription>
} In project life
customChangeElement={ </StyledTimeToProductionDescription>
<StyledTimeToProductionDescription> }
In project life percentage
</StyledTimeToProductionDescription> >
} <HelpPopper id='avg-time-to-prod'>
percentage How long did it take on average from a feature toggle was
> created until it was enabled in an environment of type
<HelpPopper id='avg-time-to-prod'> production. This is calculated only from feature toggles
How long did it take on average from a feature toggle with the type of "release".
was created until it was enabled in an environment of </HelpPopper>
type production. This is calculated only from feature </StatusBox>
toggles with the type of "release". <StatusBox
</HelpPopper> title='Features created'
</StatusBox> boxText={String(createdCurrentWindow)}
</StyledWidget> change={createdCurrentWindow - createdPastWindow}
<StyledWidget> >
<StatusBox <NavigationBar to={`/projects/${projectId}`}>
title='Features created' <KeyboardArrowRight />
boxText={String(createdCurrentWindow)} </NavigationBar>
change={createdCurrentWindow - createdPastWindow} </StatusBox>
/>
</StyledWidget>
<StyledWidget> <StatusBox
<StatusBox title='Stale flags'
title='Stale toggles' boxText={String(projectActivityCurrentWindow)}
boxText={String(projectActivityCurrentWindow)} change={
change={ projectActivityCurrentWindow - projectActivityPastWindow
projectActivityCurrentWindow - projectActivityPastWindow }
} >
> <NavigationBar to={`/projects/${projectId}/health`}>
<HelpPopper id='stale-toggles'> <KeyboardArrowRight />
Sum of all stale toggles in the project </NavigationBar>
</HelpPopper> </StatusBox>
</StatusBox>
</StyledWidget>
<StyledWidget> <StatusBox
<StatusBox title='Features archived'
title='Features archived' boxText={String(archivedCurrentWindow)}
boxText={String(archivedCurrentWindow)} change={archivedCurrentWindow - archivedPastWindow}
change={archivedCurrentWindow - archivedPastWindow} >
/> <NavigationBar to={`/projects/${projectId}/archive`}>
</StyledWidget> <KeyboardArrowRight />
</NavigationBar>
</StatusBox>
</StyledBox> </StyledBox>
); );
}; };

View File

@ -5,10 +5,6 @@ import { Box, Typography, styled } from '@mui/material';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
import { flexRow } from 'themes/themeStyles'; import { flexRow } from 'themes/themeStyles';
const StyledTypographyHeader = styled(Typography)(({ theme }) => ({
marginBottom: theme.spacing(2.5),
}));
const StyledTypographyCount = styled(Box)(({ theme }) => ({ const StyledTypographyCount = styled(Box)(({ theme }) => ({
fontSize: theme.fontSizes.largeHeader, fontSize: theme.fontSizes.largeHeader,
})); }));
@ -31,8 +27,25 @@ const StyledTypographyChange = styled(Typography)(({ theme }) => ({
fontWeight: theme.typography.fontWeightBold, fontWeight: theme.typography.fontWeightBold,
})); }));
const RowContainer = styled(Box)(({ theme }) => ({
...flexRow,
}));
const StyledWidget = styled(Box)(({ theme }) => ({
padding: theme.spacing(3),
backgroundColor: theme.palette.background.paper,
flex: 1,
display: 'flex',
flexDirection: 'column',
gap: theme.spacing(2.5),
borderRadius: `${theme.shape.borderRadiusLarge}px`,
[theme.breakpoints.down('lg')]: {
padding: theme.spacing(2),
},
}));
interface IStatusBoxProps { interface IStatusBoxProps {
title?: string; title: string;
boxText: ReactNode; boxText: ReactNode;
change?: number; change?: number;
percentage?: boolean; percentage?: boolean;
@ -42,10 +55,24 @@ interface IStatusBoxProps {
const resolveIcon = (change: number) => { const resolveIcon = (change: number) => {
if (change > 0) { if (change > 0) {
return ( return (
<CallMade sx={{ color: 'success.dark', height: 20, width: 20 }} /> <CallMade
sx={{
color: 'success.dark',
height: 20,
width: 20,
}}
/>
); );
} }
return <SouthEast sx={{ color: 'warning.dark', height: 20, width: 20 }} />; return (
<SouthEast
sx={{
color: 'warning.dark',
height: 20,
width: 20,
}}
/>
);
}; };
const resolveColor = (change: number) => { const resolveColor = (change: number) => {
@ -63,23 +90,14 @@ export const StatusBox: FC<IStatusBoxProps> = ({
children, children,
customChangeElement, customChangeElement,
}) => ( }) => (
<> <StyledWidget>
<ConditionallyRender <RowContainer>
condition={Boolean(title)} <Typography variant='h3' data-loading>
show={ {title}
<StyledTypographyHeader data-loading> </Typography>
{title} {children}
</StyledTypographyHeader> </RowContainer>
} <RowContainer>
/>
{children}
<Box
sx={{
...flexRow,
justifyContent: 'center',
width: 'auto',
}}
>
<StyledTypographyCount data-loading> <StyledTypographyCount data-loading>
{boxText} {boxText}
</StyledTypographyCount> </StyledTypographyCount>
@ -124,6 +142,6 @@ export const StatusBox: FC<IStatusBoxProps> = ({
/> />
} }
/> />
</Box> </RowContainer>
</> </StyledWidget>
); );

View File

@ -1,5 +1,5 @@
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig'; import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
import { Box, styled, Typography } from '@mui/material'; import { styled } from '@mui/material';
import { StatusBox } from '../ProjectInsightsStats/StatusBox'; import { StatusBox } from '../ProjectInsightsStats/StatusBox';
import KeyboardArrowRight from '@mui/icons-material/KeyboardArrowRight'; import KeyboardArrowRight from '@mui/icons-material/KeyboardArrowRight';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
@ -11,18 +11,13 @@ interface IProjectMembersProps {
} }
const NavigationBar = styled(Link)(({ theme }) => ({ const NavigationBar = styled(Link)(({ theme }) => ({
marginLeft: 'auto',
display: 'flex', display: 'flex',
justifyContent: 'space-between', justifyContent: 'space-between',
textDecoration: 'none', textDecoration: 'none',
color: theme.palette.text.primary, color: theme.palette.text.primary,
})); }));
export const StyledProjectInfoWidgetContainer = styled('div')(({ theme }) => ({
display: 'flex',
flexDirection: 'column',
gap: theme.spacing(2.5),
}));
export const ProjectMembers = ({ export const ProjectMembers = ({
members, members,
projectId, projectId,
@ -35,18 +30,14 @@ export const ProjectMembers = ({
const { currentMembers, change } = members; const { currentMembers, change } = members;
return ( return (
<StyledProjectInfoWidgetContainer> <StatusBox
title={'Project members'}
boxText={`${currentMembers}`}
change={change}
>
<NavigationBar to={link}> <NavigationBar to={link}>
<Typography variant='h3'>Project members</Typography>
<KeyboardArrowRight /> <KeyboardArrowRight />
</NavigationBar> </NavigationBar>
<Box </StatusBox>
sx={{
display: 'flex',
}}
>
<StatusBox boxText={`${currentMembers}`} change={change} />
</Box>
</StyledProjectInfoWidgetContainer>
); );
}; };