mirror of
https://github.com/Unleash/unleash.git
synced 2025-01-25 00:07:47 +01:00
refactor: fix stats layout and unify components (#6671)
This commit is contained in:
parent
e6150def36
commit
f5a7cc9125
@ -57,7 +57,7 @@ export const ProjectInsights = () => {
|
||||
<NarrowContainer>
|
||||
<FlagTypesUsed featureTypeCounts={data.featureTypeCounts} />
|
||||
</NarrowContainer>
|
||||
<NarrowContainer>
|
||||
<NarrowContainer sx={{ padding: 0 }}>
|
||||
<ProjectMembers projectId={projectId} members={data.members} />
|
||||
</NarrowContainer>
|
||||
<WideContainer>
|
||||
|
@ -34,13 +34,7 @@ export const HelpPopper: FC<IHelpPopperProps> = ({ children, id }) => {
|
||||
const open = Boolean(anchor);
|
||||
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
position: 'absolute',
|
||||
top: (theme) => theme.spacing(0.5),
|
||||
right: (theme) => theme.spacing(0.5),
|
||||
}}
|
||||
>
|
||||
<Box>
|
||||
<IconButton onClick={onOpen} aria-describedby={id} size='small'>
|
||||
<HelpOutline
|
||||
sx={{
|
||||
|
@ -2,6 +2,9 @@ import { Box, styled, Typography } from '@mui/material';
|
||||
import type { ProjectStatsSchema } from 'openapi/models';
|
||||
import { HelpPopper } from './HelpPopper';
|
||||
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 }) => ({
|
||||
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 }) => ({
|
||||
color: theme.palette.text.secondary,
|
||||
fontSize: theme.typography.body2.fontSize,
|
||||
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 {
|
||||
stats: ProjectStatsSchema;
|
||||
}
|
||||
@ -45,6 +41,7 @@ export const ProjectInsightsStats = ({ stats }: IProjectStatsProps) => {
|
||||
if (Object.keys(stats).length === 0) {
|
||||
return null;
|
||||
}
|
||||
const projectId = useRequiredPathParam('projectId');
|
||||
|
||||
const {
|
||||
avgTimeToProdCurrentWindow,
|
||||
@ -58,79 +55,77 @@ export const ProjectInsightsStats = ({ stats }: IProjectStatsProps) => {
|
||||
|
||||
return (
|
||||
<StyledBox>
|
||||
<StyledWidget>
|
||||
<StatusBox
|
||||
title='Total changes'
|
||||
boxText={String(projectActivityCurrentWindow)}
|
||||
change={
|
||||
projectActivityCurrentWindow - projectActivityPastWindow
|
||||
}
|
||||
>
|
||||
<HelpPopper id='total-changes'>
|
||||
Sum of all configuration and state modifications in the
|
||||
project.
|
||||
</HelpPopper>
|
||||
</StatusBox>
|
||||
</StyledWidget>
|
||||
<StyledWidget>
|
||||
<StatusBox
|
||||
title='Avg. time to production'
|
||||
boxText={
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: (theme) => theme.spacing(1),
|
||||
}}
|
||||
>
|
||||
{avgTimeToProdCurrentWindow}{' '}
|
||||
<Typography component='span'>days</Typography>
|
||||
</Box>
|
||||
}
|
||||
customChangeElement={
|
||||
<StyledTimeToProductionDescription>
|
||||
In project life
|
||||
</StyledTimeToProductionDescription>
|
||||
}
|
||||
percentage
|
||||
>
|
||||
<HelpPopper id='avg-time-to-prod'>
|
||||
How long did it take on average from a feature toggle
|
||||
was created until it was enabled in an environment of
|
||||
type production. This is calculated only from feature
|
||||
toggles with the type of "release".
|
||||
</HelpPopper>
|
||||
</StatusBox>
|
||||
</StyledWidget>
|
||||
<StyledWidget>
|
||||
<StatusBox
|
||||
title='Features created'
|
||||
boxText={String(createdCurrentWindow)}
|
||||
change={createdCurrentWindow - createdPastWindow}
|
||||
/>
|
||||
</StyledWidget>
|
||||
<StatusBox
|
||||
title='Total changes'
|
||||
boxText={String(projectActivityCurrentWindow)}
|
||||
change={
|
||||
projectActivityCurrentWindow - projectActivityPastWindow
|
||||
}
|
||||
>
|
||||
<HelpPopper id='total-changes'>
|
||||
Sum of all configuration and state modifications in the
|
||||
project.
|
||||
</HelpPopper>
|
||||
</StatusBox>
|
||||
<StatusBox
|
||||
title='Avg. time to production'
|
||||
boxText={
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: (theme) => theme.spacing(1),
|
||||
}}
|
||||
>
|
||||
{avgTimeToProdCurrentWindow}{' '}
|
||||
<Typography component='span'>days</Typography>
|
||||
</Box>
|
||||
}
|
||||
customChangeElement={
|
||||
<StyledTimeToProductionDescription>
|
||||
In project life
|
||||
</StyledTimeToProductionDescription>
|
||||
}
|
||||
percentage
|
||||
>
|
||||
<HelpPopper id='avg-time-to-prod'>
|
||||
How long did it take on average from a feature toggle was
|
||||
created until it was enabled in an environment of type
|
||||
production. This is calculated only from feature toggles
|
||||
with the type of "release".
|
||||
</HelpPopper>
|
||||
</StatusBox>
|
||||
<StatusBox
|
||||
title='Features created'
|
||||
boxText={String(createdCurrentWindow)}
|
||||
change={createdCurrentWindow - createdPastWindow}
|
||||
>
|
||||
<NavigationBar to={`/projects/${projectId}`}>
|
||||
<KeyboardArrowRight />
|
||||
</NavigationBar>
|
||||
</StatusBox>
|
||||
|
||||
<StyledWidget>
|
||||
<StatusBox
|
||||
title='Stale toggles'
|
||||
boxText={String(projectActivityCurrentWindow)}
|
||||
change={
|
||||
projectActivityCurrentWindow - projectActivityPastWindow
|
||||
}
|
||||
>
|
||||
<HelpPopper id='stale-toggles'>
|
||||
Sum of all stale toggles in the project
|
||||
</HelpPopper>
|
||||
</StatusBox>
|
||||
</StyledWidget>
|
||||
<StatusBox
|
||||
title='Stale flags'
|
||||
boxText={String(projectActivityCurrentWindow)}
|
||||
change={
|
||||
projectActivityCurrentWindow - projectActivityPastWindow
|
||||
}
|
||||
>
|
||||
<NavigationBar to={`/projects/${projectId}/health`}>
|
||||
<KeyboardArrowRight />
|
||||
</NavigationBar>
|
||||
</StatusBox>
|
||||
|
||||
<StyledWidget>
|
||||
<StatusBox
|
||||
title='Features archived'
|
||||
boxText={String(archivedCurrentWindow)}
|
||||
change={archivedCurrentWindow - archivedPastWindow}
|
||||
/>
|
||||
</StyledWidget>
|
||||
<StatusBox
|
||||
title='Features archived'
|
||||
boxText={String(archivedCurrentWindow)}
|
||||
change={archivedCurrentWindow - archivedPastWindow}
|
||||
>
|
||||
<NavigationBar to={`/projects/${projectId}/archive`}>
|
||||
<KeyboardArrowRight />
|
||||
</NavigationBar>
|
||||
</StatusBox>
|
||||
</StyledBox>
|
||||
);
|
||||
};
|
||||
|
@ -5,10 +5,6 @@ import { Box, Typography, styled } from '@mui/material';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import { flexRow } from 'themes/themeStyles';
|
||||
|
||||
const StyledTypographyHeader = styled(Typography)(({ theme }) => ({
|
||||
marginBottom: theme.spacing(2.5),
|
||||
}));
|
||||
|
||||
const StyledTypographyCount = styled(Box)(({ theme }) => ({
|
||||
fontSize: theme.fontSizes.largeHeader,
|
||||
}));
|
||||
@ -31,8 +27,25 @@ const StyledTypographyChange = styled(Typography)(({ theme }) => ({
|
||||
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 {
|
||||
title?: string;
|
||||
title: string;
|
||||
boxText: ReactNode;
|
||||
change?: number;
|
||||
percentage?: boolean;
|
||||
@ -42,10 +55,24 @@ interface IStatusBoxProps {
|
||||
const resolveIcon = (change: number) => {
|
||||
if (change > 0) {
|
||||
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) => {
|
||||
@ -63,23 +90,14 @@ export const StatusBox: FC<IStatusBoxProps> = ({
|
||||
children,
|
||||
customChangeElement,
|
||||
}) => (
|
||||
<>
|
||||
<ConditionallyRender
|
||||
condition={Boolean(title)}
|
||||
show={
|
||||
<StyledTypographyHeader data-loading>
|
||||
{title}
|
||||
</StyledTypographyHeader>
|
||||
}
|
||||
/>
|
||||
{children}
|
||||
<Box
|
||||
sx={{
|
||||
...flexRow,
|
||||
justifyContent: 'center',
|
||||
width: 'auto',
|
||||
}}
|
||||
>
|
||||
<StyledWidget>
|
||||
<RowContainer>
|
||||
<Typography variant='h3' data-loading>
|
||||
{title}
|
||||
</Typography>
|
||||
{children}
|
||||
</RowContainer>
|
||||
<RowContainer>
|
||||
<StyledTypographyCount data-loading>
|
||||
{boxText}
|
||||
</StyledTypographyCount>
|
||||
@ -124,6 +142,6 @@ export const StatusBox: FC<IStatusBoxProps> = ({
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</Box>
|
||||
</>
|
||||
</RowContainer>
|
||||
</StyledWidget>
|
||||
);
|
||||
|
@ -1,5 +1,5 @@
|
||||
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 KeyboardArrowRight from '@mui/icons-material/KeyboardArrowRight';
|
||||
import { Link } from 'react-router-dom';
|
||||
@ -11,18 +11,13 @@ interface IProjectMembersProps {
|
||||
}
|
||||
|
||||
const NavigationBar = styled(Link)(({ theme }) => ({
|
||||
marginLeft: 'auto',
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
textDecoration: 'none',
|
||||
color: theme.palette.text.primary,
|
||||
}));
|
||||
|
||||
export const StyledProjectInfoWidgetContainer = styled('div')(({ theme }) => ({
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
gap: theme.spacing(2.5),
|
||||
}));
|
||||
|
||||
export const ProjectMembers = ({
|
||||
members,
|
||||
projectId,
|
||||
@ -35,18 +30,14 @@ export const ProjectMembers = ({
|
||||
|
||||
const { currentMembers, change } = members;
|
||||
return (
|
||||
<StyledProjectInfoWidgetContainer>
|
||||
<StatusBox
|
||||
title={'Project members'}
|
||||
boxText={`${currentMembers}`}
|
||||
change={change}
|
||||
>
|
||||
<NavigationBar to={link}>
|
||||
<Typography variant='h3'>Project members</Typography>
|
||||
<KeyboardArrowRight />
|
||||
</NavigationBar>
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
}}
|
||||
>
|
||||
<StatusBox boxText={`${currentMembers}`} change={change} />
|
||||
</Box>
|
||||
</StyledProjectInfoWidgetContainer>
|
||||
</StatusBox>
|
||||
);
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user