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: {