diff --git a/frontend/src/component/project/Project/ProjectOverview.tsx b/frontend/src/component/project/Project/ProjectOverview.tsx index 96c4abeba9..8dfa58bb57 100644 --- a/frontend/src/component/project/Project/ProjectOverview.tsx +++ b/frontend/src/component/project/Project/ProjectOverview.tsx @@ -1,13 +1,17 @@ import useProject, { useProjectNameOrId, } from 'hooks/api/getters/useProject/useProject'; -import { styled } from '@mui/material'; +import { Box, styled } from '@mui/material'; import { ProjectFeatureToggles } from './ProjectFeatureToggles/ProjectFeatureToggles'; import ProjectInfo from './ProjectInfo/ProjectInfo'; import { usePageTitle } from 'hooks/usePageTitle'; import { useRequiredPathParam } from 'hooks/useRequiredPathParam'; import { useLastViewedProject } from '../../../hooks/useLastViewedProject'; import { useEffect } from 'react'; +import { StatusBox } from './ProjectStatus/StatusBox'; +import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; +import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig'; +import { ProjectStatus } from './ProjectStatus/ProjectStatus'; const refreshInterval = 15 * 1000; @@ -23,6 +27,12 @@ const StyledProjectToggles = styled('div')(() => ({ minWidth: 0, })); +const StyledContentContainer = styled(Box)(() => ({ + display: 'flex', + flexDirection: 'column', + width: '100%', +})); + const ProjectOverview = () => { const projectId = useRequiredPathParam('projectId'); const projectName = useProjectNameOrId(projectId); @@ -30,6 +40,7 @@ const ProjectOverview = () => { const { members, features, health, description, environments } = project; usePageTitle(`Project overview – ${projectName}`); const { setLastViewed } = useLastViewedProject(); + const { uiConfig } = useUiConfig(); useEffect(() => { setLastViewed(projectId); @@ -44,13 +55,19 @@ const ProjectOverview = () => { health={health} featureCount={features?.length} /> - - + } /> - + + + + ); }; diff --git a/frontend/src/component/project/Project/ProjectStatus/ProjectStatus.tsx b/frontend/src/component/project/Project/ProjectStatus/ProjectStatus.tsx new file mode 100644 index 0000000000..71bbe27276 --- /dev/null +++ b/frontend/src/component/project/Project/ProjectStatus/ProjectStatus.tsx @@ -0,0 +1,24 @@ +import { Box, styled } from '@mui/material'; +import { StatusBox } from './StatusBox'; + +const StyledBox = styled(Box)(({ theme }) => ({ + padding: theme.spacing(0, 0, 2, 2), + display: 'flex', + justifyContent: 'space-between', + flexWrap: 'wrap', +})); + +export const ProjectStatus = () => { + return ( + + + {' '} + + + + ); +}; diff --git a/frontend/src/component/project/Project/ProjectStatus/StatusBox.tsx b/frontend/src/component/project/Project/ProjectStatus/StatusBox.tsx new file mode 100644 index 0000000000..3c9ce882f0 --- /dev/null +++ b/frontend/src/component/project/Project/ProjectStatus/StatusBox.tsx @@ -0,0 +1,85 @@ +import { ArrowOutward, SouthEast } from '@mui/icons-material'; +import { Box, Typography, styled } from '@mui/material'; +import { flexRow } from 'themes/themeStyles'; + +const StyledBox = styled(Box)(({ theme }) => ({ + padding: theme.spacing(4, 2), + backgroundColor: theme.palette.background.paper, + minWidth: '240px', + display: 'flex', + flexDirection: 'column', + alignItems: 'center', + borderRadius: `${theme.shape.borderRadiusLarge}px`, +})); + +const StyledTypographyHeader = styled(Typography)(({ theme }) => ({ + marginBottom: theme.spacing(2), +})); + +const StyledTypographyCount = styled(Typography)(({ theme }) => ({ + fontSize: theme.fontSizes.largeHeader, + fontWeight: 'bold', +})); + +const StyledBoxChangeContainer = styled(Box)(({ theme }) => ({ + ...flexRow, + flexDirection: 'column', + alignItems: 'center', + marginLeft: theme.spacing(1.5), +})); + +const StyledTypographySubtext = styled(Typography)(({ theme }) => ({ + color: theme.palette.neutral.main, + fontSize: theme.fontSizes.smallBody, +})); + +const StyledTypographyChange = styled(Typography)(({ theme }) => ({ + marginLeft: theme.spacing(1), + fontSize: theme.fontSizes.smallBody, +})); + +interface IStatusBoxProps { + title: string; + boxText: string; + change: number; +} + +const resolveIcon = (change: number) => { + if (change > 0) { + return ( + + ); + } + return ; +}; + +const resolveColor = (change: number) => { + if (change > 0) { + return 'success.main'; + } + return 'error.main'; +}; + +export const StatusBox = ({ title, boxText, change }: IStatusBoxProps) => { + return ( + + {title} + + {boxText} + + + {resolveIcon(change)} + + {change} + + + + this month + + + + + ); +}; diff --git a/frontend/src/interfaces/uiConfig.ts b/frontend/src/interfaces/uiConfig.ts index 0068415fc4..0f7c06185a 100644 --- a/frontend/src/interfaces/uiConfig.ts +++ b/frontend/src/interfaces/uiConfig.ts @@ -45,6 +45,7 @@ export interface IFlags { messageBanner?: boolean; serviceAccounts?: boolean; featuresExportImport?: boolean; + newProjectOverview?: boolean; } export interface IVersionInfo { diff --git a/frontend/src/themes/dark-theme.ts b/frontend/src/themes/dark-theme.ts index 030ca0a464..51dd591554 100644 --- a/frontend/src/themes/dark-theme.ts +++ b/frontend/src/themes/dark-theme.ts @@ -44,6 +44,7 @@ export default createTheme({ }, }, fontSizes: { + largeHeader: '2.25rem', mainHeader: '1.25rem', bodySize: '1rem', smallBody: `${14 / 16}rem`, diff --git a/frontend/src/themes/theme.ts b/frontend/src/themes/theme.ts index e929184c6d..a9296f3571 100644 --- a/frontend/src/themes/theme.ts +++ b/frontend/src/themes/theme.ts @@ -41,6 +41,7 @@ export default createTheme({ }, }, fontSizes: { + largeHeader: '2rem', mainHeader: '1.25rem', bodySize: '1rem', smallBody: `${14 / 16}rem`, diff --git a/frontend/src/themes/themeTypes.ts b/frontend/src/themes/themeTypes.ts index c4bc46c516..70bccb2909 100644 --- a/frontend/src/themes/themeTypes.ts +++ b/frontend/src/themes/themeTypes.ts @@ -4,6 +4,7 @@ declare module '@mui/material/styles' { * @deprecated */ fontSizes: { + largeHeader: string; mainHeader: string; bodySize: string; smallBody: string; diff --git a/src/lib/__snapshots__/create-config.test.ts.snap b/src/lib/__snapshots__/create-config.test.ts.snap index 1715571204..358d94852c 100644 --- a/src/lib/__snapshots__/create-config.test.ts.snap +++ b/src/lib/__snapshots__/create-config.test.ts.snap @@ -77,6 +77,7 @@ exports[`should create default config 1`] = ` "maintenanceMode": false, "messageBanner": false, "networkView": false, + "newProjectOverview": false, "proxyReturnAllToggles": false, "responseTimeWithAppName": false, "serviceAccounts": false, @@ -95,6 +96,7 @@ exports[`should create default config 1`] = ` "maintenanceMode": false, "messageBanner": false, "networkView": false, + "newProjectOverview": false, "proxyReturnAllToggles": false, "responseTimeWithAppName": false, "serviceAccounts": false, diff --git a/src/lib/types/experimental.ts b/src/lib/types/experimental.ts index ae6bedd284..c35f25180b 100644 --- a/src/lib/types/experimental.ts +++ b/src/lib/types/experimental.ts @@ -10,6 +10,10 @@ const flags = { process.env.UNLEASH_EXPERIMENTAL_EMBED_PROXY, true, ), + newProjectOverview: parseEnvVarBoolean( + process.env.NEW_PROJECT_OVERVIEW, + false, + ), embedProxyFrontend: parseEnvVarBoolean( process.env.UNLEASH_EXPERIMENTAL_EMBED_PROXY_FRONTEND, true, diff --git a/src/server-dev.ts b/src/server-dev.ts index e9539b421c..37ad823135 100644 --- a/src/server-dev.ts +++ b/src/server-dev.ts @@ -42,6 +42,7 @@ process.nextTick(async () => { variantsPerEnvironment: true, maintenance: false, featuresExportImport: true, + newProjectOverview: true, }, }, authentication: {