diff --git a/frontend/src/assets/icons/projectStatus.svg b/frontend/src/assets/icons/projectStatus.svg index 713e29eb62d3..8872e15086db 100644 --- a/frontend/src/assets/icons/projectStatus.svg +++ b/frontend/src/assets/icons/projectStatus.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/frontend/src/component/project/Project/ProjectStatus/ProjectActivity.tsx b/frontend/src/component/project/Project/ProjectStatus/ProjectActivity.tsx index 79145f662d52..4b8bb253b4a0 100644 --- a/frontend/src/component/project/Project/ProjectStatus/ProjectActivity.tsx +++ b/frontend/src/component/project/Project/ProjectStatus/ProjectActivity.tsx @@ -7,16 +7,9 @@ import { styled, Tooltip } from '@mui/material'; const StyledContainer = styled('div')(({ theme }) => ({ display: 'flex', flexDirection: 'column', - justifyContent: 'center', alignItems: 'center', - gap: theme.spacing(2), })); -const TitleContainer = styled('h4')({ - margin: 0, - width: '100%', -}); - type Output = { date: string; count: number; level: number }; const ensureFullYearData = (data: Output[]): Output[] => { @@ -93,7 +86,6 @@ export const ProjectActivity = () => { <> {data.activityCountByDate.length > 0 ? ( - Activity in project ({ - backgroundColor: theme.palette.envAccordion.expanded, - padding: theme.spacing(3), - borderRadius: theme.shape.borderRadiusExtraLarge, - minWidth: '300px', - gridArea: 'health', -})); +import { HealthGridTile } from './ProjectHealthGrid.styles'; const TextContainer = styled('div')(({ theme }) => ({ display: 'flex', @@ -63,7 +56,7 @@ export const ProjectHealth = () => { : theme.palette.success.border; return ( - + @@ -111,6 +104,6 @@ export const ProjectHealth = () => { )} - + ); }; diff --git a/frontend/src/component/project/Project/ProjectStatus/ProjectHealthGrid.styles.tsx b/frontend/src/component/project/Project/ProjectStatus/ProjectHealthGrid.styles.tsx new file mode 100644 index 000000000000..2102a9ebb522 --- /dev/null +++ b/frontend/src/component/project/Project/ProjectStatus/ProjectHealthGrid.styles.tsx @@ -0,0 +1,7 @@ +import { styled } from '@mui/material'; + +export const HealthGridTile = styled('article')(({ theme }) => ({ + backgroundColor: theme.palette.envAccordion.expanded, + padding: theme.spacing(3), + borderRadius: theme.shape.borderRadiusExtraLarge, +})); diff --git a/frontend/src/component/project/Project/ProjectStatus/ProjectHealthGrid.tsx b/frontend/src/component/project/Project/ProjectStatus/ProjectHealthGrid.tsx new file mode 100644 index 000000000000..bc6517a306d7 --- /dev/null +++ b/frontend/src/component/project/Project/ProjectStatus/ProjectHealthGrid.tsx @@ -0,0 +1,57 @@ +import { ProjectHealth } from './ProjectHealth'; +import { styled } from '@mui/material'; +import { StaleFlags } from './StaleFlags'; +import { ProjectResources } from './ProjectResources'; + +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: 'repeat(2, minmax(300px, 1fr))', + gap: theme.spacing(1, 2), + ...onNarrowGrid({ + display: 'flex', + flexDirection: 'column', + gap: theme.spacing(1), + }), +})); + +const Tile = styled('div', { + shouldForwardProp: (prop) => prop !== 'gridArea', +})<{ gridArea: string }>(({ theme, gridArea }) => ({ + gridArea, + '&>*': { + height: '100%', + }, +})); + +export const ProjectHealthGrid = () => { + return ( + + + + + + + + + + + + + + ); +}; diff --git a/frontend/src/component/project/Project/ProjectStatus/ProjectLifecycleSummary.tsx b/frontend/src/component/project/Project/ProjectStatus/ProjectLifecycleSummary.tsx index b59a48111d32..ba90284837e5 100644 --- a/frontend/src/component/project/Project/ProjectStatus/ProjectLifecycleSummary.tsx +++ b/frontend/src/component/project/Project/ProjectStatus/ProjectLifecycleSummary.tsx @@ -6,26 +6,10 @@ import { useRequiredPathParam } from 'hooks/useRequiredPathParam'; import type { FC } from 'react'; import { PrettifyLargeNumber } from 'component/common/PrettifyLargeNumber/PrettifyLargeNumber'; import type { ProjectStatusSchemaLifecycleSummary } from 'openapi'; -import { HelpIcon } from 'component/common/HelpIcon/HelpIcon'; import { HtmlTooltip } from 'component/common/HtmlTooltip/HtmlTooltip'; import { lifecycleMessages } from './LifecycleMessages'; import InfoIcon from '@mui/icons-material/Info'; -const LifecycleRow = styled('div')(({ theme }) => ({ - display: 'flex', - flexFlow: 'column', - gap: theme.spacing(1), -})); - -const HeaderRow = styled('div')(({ theme }) => ({ - display: 'flex', - flex: 'auto', - '& > *': { - marginBlock: 0, - fontWeight: 'normal', - }, -})); - const LifecycleBoxContent = styled('div')(({ theme }) => ({ padding: theme.spacing(2), gap: theme.spacing(4), @@ -87,6 +71,7 @@ const LifecycleList = styled('ul')(({ theme }) => ({ justifyContent: 'center', padding: 0, flex: 'auto', + margin: 0, })); const Counter = styled('span')({ @@ -146,44 +131,6 @@ const BigNumber: FC<{ value?: number }> = ({ value }) => { ); }; - -const TooltipContent = styled('div')(({ theme }) => ({ - padding: theme.spacing(0.5), -})); - -const TooltipText = styled('p')(({ theme }) => ({ - fontSize: theme.typography.body1.fontSize, - '& + p': { - marginTop: theme.spacing(1), - }, -})); - -const LifecycleTooltip: FC = () => { - return ( - - - Based on usage metrics and interactions with Unleash, - feature flags can go through five distinct lifecycle - stages. These stages mirror the typical software - development process and allow you to identify - bottlenecks at any stage of the lifecycle. - - - - - Read more in our documentation - - - - } - /> - ); -}; - export const ProjectLifecycleSummary = () => { const projectId = useRequiredPathParam('projectId'); const { data, loading } = useProjectStatus(projectId); @@ -200,119 +147,103 @@ export const ProjectLifecycleSummary = () => { } }; return ( - - -

Flag lifecycle

- -
- - - -

- - - - - {flagWord('initial')} in initial -

- -
- -

- - - - - {flagWord('preLive')} in pre-live -

- -
- -

- - - - - {flagWord('live')} in live -

- -
- -

- - - - - {flagWord('completed')} in cleanup -

- -
- -

- - - - - {flagWord('archived')} in archived -

- -
This month
-
- {data?.lifecycleSummary.archived.currentFlags ?? 0}{' '} - flags archived -
-
-
-
-
+ + +

+ + + + + {flagWord('initial')} in initial +

+ +
+ +

+ + + + + {flagWord('preLive')} in pre-live +

+ +
+ +

+ + + + + {flagWord('live')} in live +

+ +
+ +

+ + + + + {flagWord('completed')} in cleanup +

+ +
+ +

+ + + + + {flagWord('archived')} in archived +

+ +
This month
+
+ {data?.lifecycleSummary.archived.currentFlags ?? 0}{' '} + flags archived +
+
+
+
); }; diff --git a/frontend/src/component/project/Project/ProjectStatus/ProjectResources.tsx b/frontend/src/component/project/Project/ProjectStatus/ProjectResources.tsx index 9130458a0715..d8b7bfdd176b 100644 --- a/frontend/src/component/project/Project/ProjectStatus/ProjectResources.tsx +++ b/frontend/src/component/project/Project/ProjectStatus/ProjectResources.tsx @@ -8,14 +8,7 @@ import SegmentsIcon from '@mui/icons-material/DonutLarge'; import ConnectedIcon from '@mui/icons-material/Cable'; import { useProjectStatus } from 'hooks/api/getters/useProjectStatus/useProjectStatus'; import useLoading from 'hooks/useLoading'; - -const Wrapper = styled('article')(({ theme }) => ({ - backgroundColor: theme.palette.envAccordion.expanded, - padding: theme.spacing(3), - borderRadius: theme.shape.borderRadiusExtraLarge, - minWidth: '300px', - gridArea: 'resources', -})); +import { HealthGridTile } from './ProjectHealthGrid.styles'; const ProjectResourcesInner = styled('div')(({ theme }) => ({ display: 'flex', @@ -115,7 +108,7 @@ export const ProjectResources = () => { const loadingRef = useLoading(loading, '[data-loading-resources=true]'); return ( - + Project resources @@ -155,6 +148,6 @@ export const ProjectResources = () => { - + ); }; diff --git a/frontend/src/component/project/Project/ProjectStatus/ProjectStatusModal.tsx b/frontend/src/component/project/Project/ProjectStatus/ProjectStatusModal.tsx index 1d199c3c9fdd..b6e79c12b57e 100644 --- a/frontend/src/component/project/Project/ProjectStatus/ProjectStatusModal.tsx +++ b/frontend/src/component/project/Project/ProjectStatus/ProjectStatusModal.tsx @@ -1,20 +1,28 @@ -import { styled } from '@mui/material'; +import { Button, styled } from '@mui/material'; import { DynamicSidebarModal } from 'component/common/SidebarModal/SidebarModal'; -import { ProjectResources } from './ProjectResources'; +import { ReactComponent as ProjectStatusSvg } from 'assets/icons/projectStatus.svg'; import { ProjectActivity } from './ProjectActivity'; -import { ProjectHealth } from './ProjectHealth'; import { ProjectLifecycleSummary } from './ProjectLifecycleSummary'; -import { StaleFlags } from './StaleFlags'; +import type { FC } from 'react'; +import { HelpIcon } from 'component/common/HelpIcon/HelpIcon'; +import { ProjectHealthGrid } from './ProjectHealthGrid'; const ModalContentContainer = styled('section')(({ theme }) => ({ minHeight: '100vh', maxWidth: 1100, width: '95vw', backgroundColor: theme.palette.background.default, - padding: theme.spacing(4), display: 'flex', flexFlow: 'column', gap: theme.spacing(4), + paddingInline: theme.spacing(4), + paddingBlock: theme.spacing(10), +})); + +const WidgetContainer = styled('div')(({ theme }) => ({ + display: 'flex', + flexDirection: 'column', + gap: theme.spacing(9), })); type Props = { @@ -22,47 +30,112 @@ type Props = { close: () => void; }; -const onNarrowGrid = (css: object) => ({ - '@container (max-width: 650px)': css, - '@supports not (container-type: inline-size)': { - '@media (max-width: 712px)': css, +const LifecycleHeaderRow = styled('div')(({ theme }) => ({ + display: 'flex', + alignItems: 'end', +})); + +const HeaderRow = styled('div')(({ theme }) => ({ + display: 'flex', + alignItems: 'center', + gap: theme.spacing(1.5), +})); + +const StyledProjectStatusSvg = styled(ProjectStatusSvg)(({ theme }) => ({ + fill: theme.palette.primary.main, +})); + +const ModalHeader = styled('h3')(({ theme }) => ({ + fontSize: theme.typography.h2.fontSize, + margin: 0, +})); + +const RowHeader = styled('h4')(({ theme }) => ({ + margin: 0, + fontWeight: 'normal', +})); + +const Row = styled('div')(({ theme }) => ({ + display: 'flex', + flexDirection: 'column', + gap: theme.spacing(2), +})); + +const TooltipContent = styled('div')(({ theme }) => ({ + padding: theme.spacing(0.5), +})); + +const TooltipText = styled('p')(({ theme }) => ({ + fontSize: theme.typography.body1.fontSize, + '& + p': { + marginTop: theme.spacing(1), }, -}); - -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), - }), +})); + +const LifecycleTooltip: FC = () => { + return ( + + + Based on usage metrics and interactions with Unleash, + feature flags can go through five distinct lifecycle + stages. These stages mirror the typical software + development process and allow you to identify + bottlenecks at any stage of the lifecycle. + + + + + Read more in our documentation + + + + } + /> + ); +}; + +const CloseRow = styled('div')(({ theme }) => ({ + display: 'flex', + justifyContent: 'flex-end', + marginBlockStart: 'auto', })); export const ProjectStatusModal = ({ open, close }: Props) => { return ( - - - - - - - + + + + + Health + + - + + Activity in project + + - + + + Flag lifecycle + + + + + + + + ); diff --git a/frontend/src/component/project/Project/ProjectStatus/StaleFlags.tsx b/frontend/src/component/project/Project/ProjectStatus/StaleFlags.tsx index ab91009567ac..a0e7c0ec3070 100644 --- a/frontend/src/component/project/Project/ProjectStatus/StaleFlags.tsx +++ b/frontend/src/component/project/Project/ProjectStatus/StaleFlags.tsx @@ -4,13 +4,9 @@ import { PrettifyLargeNumber } from 'component/common/PrettifyLargeNumber/Pretti import { useRequiredPathParam } from 'hooks/useRequiredPathParam'; import type { FC } from 'react'; import { Link } from 'react-router-dom'; +import { HealthGridTile } from './ProjectHealthGrid.styles'; -const Wrapper = styled('article')(({ theme }) => ({ - backgroundColor: theme.palette.envAccordion.expanded, - padding: theme.spacing(3), - borderRadius: theme.shape.borderRadiusExtraLarge, - minWidth: '300px', - gridArea: 'stale', +const Wrapper = styled(HealthGridTile)(({ theme }) => ({ display: 'flex', flexDirection: 'column', gap: theme.spacing(1),