From e224417116c1add05e5950fe705703b230ea7fea Mon Sep 17 00:00:00 2001 From: Thomas Heartman <thomas@getunleash.io> Date: Thu, 7 Nov 2024 10:44:13 +0100 Subject: [PATCH] 1-3059: add initial visuals for lifecycle summary (#8679) Add rough implementation of the lifecycle summary components. This PR adds components for all the different lifecycle stages. We don't have any data yet, so they're all hardcoded for now, just to get the visuals right. I'm expecting the lines of code to drop and to refactor/extract some structures as development continues. For now, this is what they look like: ![image](https://github.com/user-attachments/assets/d7deacaa-83e1-46c2-bc28-8264416c1dd9) Things to note: - The lifecycle stage icon colors don't match up with the sketches, but they match up with what we currently have in the app. If we change them, we should change them together. - This implementation does not contain the "Flag lifecycle" header or the "view graphs" link. --- .../ProjectStatus/ProjectLifecycleSummary.tsx | 177 ++++++++++++++++++ .../ProjectStatus/ProjectStatusModal.tsx | 3 + 2 files changed, 180 insertions(+) create mode 100644 frontend/src/component/project/Project/ProjectStatus/ProjectLifecycleSummary.tsx diff --git a/frontend/src/component/project/Project/ProjectStatus/ProjectLifecycleSummary.tsx b/frontend/src/component/project/Project/ProjectStatus/ProjectLifecycleSummary.tsx new file mode 100644 index 000000000000..972269351186 --- /dev/null +++ b/frontend/src/component/project/Project/ProjectStatus/ProjectLifecycleSummary.tsx @@ -0,0 +1,177 @@ +import { styled } from '@mui/material'; +import { FeatureLifecycleStageIcon } from 'component/feature/FeatureView/FeatureOverview/FeatureLifecycle/FeatureLifecycleStageIcon'; +import { useRequiredPathParam } from 'hooks/useRequiredPathParam'; +import { Link } from 'react-router-dom'; + +const LifecycleBox = styled('li')(({ theme }) => ({ + padding: theme.spacing(2), + borderRadius: theme.shape.borderRadiusExtraLarge, + border: `2px solid ${theme.palette.divider}`, + width: '180px', + height: '175px', + display: 'flex', + flexFlow: 'column', + justifyContent: 'space-between', +})); + +const Wrapper = styled('ul')(({ theme }) => ({ + display: 'grid', + listStyle: 'none', + gridTemplateColumns: 'repeat(auto-fit, 180px)', + gap: theme.spacing(1), + justifyContent: 'center', +})); + +const Counter = styled('span')({ + display: 'flex', + alignItems: 'center', + justifyContent: 'space-between', +}); + +const BigNumber = styled('span')(({ theme }) => ({ + fontSize: `calc(2 * ${theme.typography.body1.fontSize})`, +})); + +const Stats = styled('dl')(({ theme }) => ({ + margin: 0, + fontSize: theme.typography.body2.fontSize, + '& dd': { + margin: 0, + fontWeight: 'bold', + }, +})); + +const NegativeStat = styled('span')(({ theme }) => ({ + color: theme.palette.warning.contrastText, +})); + +const NoData = styled('span')({ + fontWeight: 'normal', +}); + +const LinkNoUnderline = styled(Link)({ + textDecoration: 'none', +}); + +export const ProjectLifecycleSummary = () => { + const projectId = useRequiredPathParam('projectId'); + return ( + <Wrapper> + <LifecycleBox> + <p> + <Counter> + <BigNumber>15</BigNumber> + + <FeatureLifecycleStageIcon + aria-hidden='true' + stage={{ + name: 'initial', + enteredStageAt: '', + }} + /> + </Counter> + <span>flags in initial</span> + </p> + <Stats> + <dt>Avg. time in stage</dt> + <dd> + <NegativeStat>21 days</NegativeStat> + </dd> + </Stats> + </LifecycleBox> + <LifecycleBox> + <p> + <Counter> + <BigNumber>3</BigNumber> + + <FeatureLifecycleStageIcon + aria-hidden='true' + stage={{ + name: 'pre-live', + enteredStageAt: '', + environments: [], + }} + /> + </Counter> + <span>flags in pre-live</span> + </p> + <Stats> + <dt>Avg. time in stage</dt> + <dd>18 days</dd> + </Stats> + </LifecycleBox> + <LifecycleBox> + <p> + <Counter> + <BigNumber>2</BigNumber> + + <FeatureLifecycleStageIcon + aria-hidden='true' + stage={{ + name: 'live', + enteredStageAt: '', + environments: [], + }} + /> + </Counter> + <span>flags in live</span> + </p> + <Stats> + <dt>Avg. time in stage</dt> + <dd>10 days</dd> + </Stats> + </LifecycleBox> + <LifecycleBox> + <p> + <Counter> + <BigNumber>6</BigNumber> + + <FeatureLifecycleStageIcon + aria-hidden='true' + stage={{ + name: 'completed', + enteredStageAt: '', + environments: [], + status: 'kept', + }} + /> + </Counter> + <span> + <LinkNoUnderline + to={`/projects/${projectId}/placeholder`} + > + flags + </LinkNoUnderline>{' '} + in cleanup + </span> + </p> + <Stats> + <dt>Avg. time in stage</dt> + <dd> + <NoData>No data</NoData> + </dd> + </Stats> + </LifecycleBox> + <LifecycleBox> + <p> + <Counter> + <BigNumber>15</BigNumber> + + <FeatureLifecycleStageIcon + aria-hidden='true' + stage={{ + name: 'archived', + enteredStageAt: '', + }} + /> + </Counter> + <span>flags in archived</span> + </p> + <Stats> + <dt>This month</dt> + <dd>3 flags archived</dd> + </Stats> + </LifecycleBox> + </Wrapper> + ); +}; diff --git a/frontend/src/component/project/Project/ProjectStatus/ProjectStatusModal.tsx b/frontend/src/component/project/Project/ProjectStatus/ProjectStatusModal.tsx index aa35be2dbcb6..838255fc7a76 100644 --- a/frontend/src/component/project/Project/ProjectStatus/ProjectStatusModal.tsx +++ b/frontend/src/component/project/Project/ProjectStatus/ProjectStatusModal.tsx @@ -2,6 +2,7 @@ import { styled } from '@mui/material'; import { SidebarModal } from 'component/common/SidebarModal/SidebarModal'; import { ProjectResources } from './ProjectResources'; import { ProjectActivity } from './ProjectActivity'; +import { ProjectLifecycleSummary } from './ProjectLifecycleSummary'; const ModalContentContainer = styled('div')(({ theme }) => ({ minHeight: '100vh', @@ -40,6 +41,8 @@ export const ProjectStatusModal = ({ open, close }: Props) => { </HealthRow> <ProjectActivity /> + + <ProjectLifecycleSummary /> </ModalContentContainer> </SidebarModal> );