diff --git a/frontend/src/component/personalDashboard/PersonalDashboard.test.tsx b/frontend/src/component/personalDashboard/PersonalDashboard.test.tsx
index 2983ff29e3..6af93d7585 100644
--- a/frontend/src/component/personalDashboard/PersonalDashboard.test.tsx
+++ b/frontend/src/component/personalDashboard/PersonalDashboard.test.tsx
@@ -36,6 +36,11 @@ const setupLongRunningProject = () => {
insights: {
avgHealthCurrentWindow: 80,
avgHealthPastWindow: 70,
+ totalFlags: 39,
+ potentiallyStaleFlags: 14,
+ staleFlags: 13,
+ activeFlags: 12,
+ health: 81,
},
latestEvents: [{ summary: 'someone created a flag', id: 0 }],
roles: [{ name: 'Member' }],
@@ -135,7 +140,10 @@ test('Render personal dashboard for a long running project', async () => {
await screen.findByText('70%'); // avg health past window
await screen.findByText('someone created a flag');
await screen.findByText('Member');
-
+ await screen.findByText('81%'); // current health score
+ await screen.findByText('12 feature flags'); // active flags
+ await screen.findByText('13 feature flags'); // stale flags
+ await screen.findByText('14 feature flags'); // potentially stale flags
await screen.findByText('myFlag');
await screen.findByText('No feature flag metrics data');
await screen.findByText('production');
diff --git a/frontend/src/component/personalDashboard/ProjectSetupComplete.tsx b/frontend/src/component/personalDashboard/ProjectSetupComplete.tsx
index 1a3dcc8522..b46cc9cfcd 100644
--- a/frontend/src/component/personalDashboard/ProjectSetupComplete.tsx
+++ b/frontend/src/component/personalDashboard/ProjectSetupComplete.tsx
@@ -3,6 +3,8 @@ import type { FC } from 'react';
import { Link } from 'react-router-dom';
import Lightbulb from '@mui/icons-material/LightbulbOutlined';
import type { PersonalDashboardProjectDetailsSchemaInsights } from '../../openapi';
+import { ProjectHealthChart } from 'component/project/Project/ProjectInsights/ProjectHealth/ProjectHealthChart';
+import { FlagCounts } from '../project/Project/ProjectInsights/ProjectHealth/FlagCounts';
const TitleContainer = styled('div')(({ theme }) => ({
display: 'flex',
@@ -12,8 +14,16 @@ const TitleContainer = styled('div')(({ theme }) => ({
justifyContent: 'center',
}));
+const Health = styled('div')(({ theme }) => ({
+ display: 'flex',
+ flexDirection: 'row',
+ alignItems: 'center',
+ justifyContent: 'center',
+ gap: theme.spacing(3),
+}));
+
const ActionBox = styled('article')(({ theme }) => ({
- padding: theme.spacing(4, 2),
+ padding: theme.spacing(0, 2),
display: 'flex',
gap: theme.spacing(3),
flexDirection: 'column',
@@ -128,6 +138,22 @@ export const ProjectSetupComplete: FC<{
Project Insight
+
+
+
+
+
prop !== 'color',
+})<{ color?: string }>(({ theme, color }) => ({
+ height: '15px',
+ width: '15px',
+ borderRadius: '50%',
+ display: 'inline-block',
+ backgroundColor: color,
+}));
+
+const FlagCountsWrapper = styled(Box)(({ theme }) => ({
+ display: 'flex',
+ flexDirection: 'column',
+ gap: theme.spacing(2),
+}));
+
+const FlagsCount = styled('p')(({ theme }) => ({
+ color: theme.palette.text.secondary,
+ marginLeft: theme.spacing(3),
+}));
+
+const StatusWithDot = styled(Box)(({ theme }) => ({
+ display: 'flex',
+ alignItems: 'center',
+ gap: theme.spacing(1),
+}));
+
+export const FlagCounts: FC<{
+ projectId: string;
+ activeCount: number;
+ potentiallyStaleCount: number;
+ staleCount: number;
+ hideLinks?: boolean;
+}> = ({
+ projectId,
+ activeCount,
+ potentiallyStaleCount,
+ staleCount,
+ hideLinks = false,
+}) => {
+ const theme = useTheme();
+
+ return (
+
+
+
+
+ Active
+
+ {activeCount} feature flags
+
+
+
+
+ Potentially stale
+ {hideLinks ? null : (
+ (configure)
+ )}
+
+ {potentiallyStaleCount} feature flags
+
+
+
+
+ Stale
+ {hideLinks ? null : (
+
+ (view flags)
+
+ )}
+
+ {staleCount} feature flags
+
+
+ );
+};
diff --git a/frontend/src/component/project/Project/ProjectInsights/ProjectHealth/ProjectHealth.tsx b/frontend/src/component/project/Project/ProjectInsights/ProjectHealth/ProjectHealth.tsx
index 6b15b4ea96..a026a4b229 100644
--- a/frontend/src/component/project/Project/ProjectInsights/ProjectHealth/ProjectHealth.tsx
+++ b/frontend/src/component/project/Project/ProjectInsights/ProjectHealth/ProjectHealth.tsx
@@ -1,31 +1,10 @@
import { ProjectHealthChart } from './ProjectHealthChart';
-import { Alert, Box, styled, Typography, useTheme } from '@mui/material';
-import { Link } from 'react-router-dom';
+import { Alert, Box, styled, Typography } from '@mui/material';
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
import type { ProjectInsightsSchemaHealth } from '../../../../../openapi';
import type { FC } from 'react';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
-
-const Dot = styled('span', {
- shouldForwardProp: (prop) => prop !== 'color',
-})<{ color?: string }>(({ theme, color }) => ({
- height: '15px',
- width: '15px',
- borderRadius: '50%',
- display: 'inline-block',
- backgroundColor: color,
-}));
-
-const FlagsCount = styled('p')(({ theme }) => ({
- color: theme.palette.text.secondary,
- marginLeft: theme.spacing(3),
-}));
-
-const FlagCounts = styled(Box)(({ theme }) => ({
- display: 'flex',
- flexDirection: 'column',
- gap: theme.spacing(2),
-}));
+import { FlagCounts } from './FlagCounts';
const Container = styled(Box)(({ theme }) => ({
display: 'flex',
@@ -33,16 +12,9 @@ const Container = styled(Box)(({ theme }) => ({
gap: theme.spacing(2),
}));
-const StatusWithDot = styled(Box)(({ theme }) => ({
- display: 'flex',
- alignItems: 'center',
- gap: theme.spacing(1),
-}));
-
export const ProjectHealth: FC<{ health: ProjectInsightsSchemaHealth }> = ({
health,
}) => {
- const theme = useTheme();
const projectId = useRequiredPathParam('projectId');
const { staleCount, potentiallyStaleCount, activeCount, rating } = health;
@@ -73,39 +45,13 @@ export const ProjectHealth: FC<{ health: ProjectInsightsSchemaHealth }> = ({
potentiallyStale={potentiallyStaleCount}
health={rating}
/>
-
-
-
-
- Active
-
- {activeCount} feature flags
-
-
-
-
-
- Potentially stale
-
- (configure)
-
-
- {potentiallyStaleCount} feature flags
-
-
-
-
-
- Stale
-
- (view flags)
-
-
- {staleCount} feature flags
-
-
+
+
);
diff --git a/frontend/src/openapi/models/personalDashboardProjectDetailsSchemaInsights.ts b/frontend/src/openapi/models/personalDashboardProjectDetailsSchemaInsights.ts
index d081685ecf..284f9bb620 100644
--- a/frontend/src/openapi/models/personalDashboardProjectDetailsSchemaInsights.ts
+++ b/frontend/src/openapi/models/personalDashboardProjectDetailsSchemaInsights.ts
@@ -18,4 +18,9 @@ export type PersonalDashboardProjectDetailsSchemaInsights = {
* @nullable
*/
avgHealthPastWindow: number | null;
+ totalFlags: number;
+ activeFlags: number;
+ staleFlags: number;
+ potentiallyStaleFlags: number;
+ health: number;
};
diff --git a/src/lib/features/personal-dashboard/personal-dashboard-controller.e2e.test.ts b/src/lib/features/personal-dashboard/personal-dashboard-controller.e2e.test.ts
index a89579031c..f4267d7db1 100644
--- a/src/lib/features/personal-dashboard/personal-dashboard-controller.e2e.test.ts
+++ b/src/lib/features/personal-dashboard/personal-dashboard-controller.e2e.test.ts
@@ -326,6 +326,11 @@ test('should return personal dashboard project details', async () => {
insights: {
avgHealthPastWindow: 80,
avgHealthCurrentWindow: 91,
+ totalFlags: 3,
+ potentiallyStaleFlags: 0,
+ staleFlags: 0,
+ activeFlags: 3,
+ health: 100,
},
});
});
diff --git a/src/lib/features/personal-dashboard/personal-dashboard-service.ts b/src/lib/features/personal-dashboard/personal-dashboard-service.ts
index 3ea0b672e7..316d070364 100644
--- a/src/lib/features/personal-dashboard/personal-dashboard-service.ts
+++ b/src/lib/features/personal-dashboard/personal-dashboard-service.ts
@@ -165,6 +165,16 @@ export class PersonalDashboardService {
);
}
+ const [projectInsights] =
+ await this.projectReadModel.getProjectsForInsights({
+ id: projectId,
+ });
+ const totalFlags = projectInsights?.featureCount || 0;
+ const potentiallyStaleFlags =
+ projectInsights?.potentiallyStaleFeatureCount || 0;
+ const staleFlags = projectInsights?.staleFeatureCount || 0;
+ const currentHealth = projectInsights?.health || 0;
+
return {
latestEvents,
onboardingStatus,
@@ -173,6 +183,11 @@ export class PersonalDashboardService {
insights: {
avgHealthCurrentWindow,
avgHealthPastWindow,
+ totalFlags,
+ potentiallyStaleFlags,
+ staleFlags,
+ activeFlags: totalFlags - staleFlags - potentiallyStaleFlags,
+ health: currentHealth,
},
};
}
diff --git a/src/lib/openapi/spec/personal-dashboard-project-details-schema.ts b/src/lib/openapi/spec/personal-dashboard-project-details-schema.ts
index b2cba57d5d..74703fbc1c 100644
--- a/src/lib/openapi/spec/personal-dashboard-project-details-schema.ts
+++ b/src/lib/openapi/spec/personal-dashboard-project-details-schema.ts
@@ -19,7 +19,15 @@ export const personalDashboardProjectDetailsSchema = {
type: 'object',
description: 'Insights for the project',
additionalProperties: false,
- required: ['avgHealthCurrentWindow', 'avgHealthPastWindow'],
+ required: [
+ 'avgHealthCurrentWindow',
+ 'avgHealthPastWindow',
+ 'totalFlags',
+ 'activeFlags',
+ 'staleFlags',
+ 'potentiallyStaleFlags',
+ 'health',
+ ],
properties: {
avgHealthCurrentWindow: {
type: 'number',
@@ -35,6 +43,33 @@ export const personalDashboardProjectDetailsSchema = {
example: 70,
nullable: true,
},
+ totalFlags: {
+ type: 'number',
+ example: 100,
+ description: 'The current number of all flags',
+ },
+ activeFlags: {
+ type: 'number',
+ example: 98,
+ description: 'The current number of active flags',
+ },
+ staleFlags: {
+ type: 'number',
+ example: 0,
+ description:
+ 'The current number of user marked stale flags',
+ },
+ potentiallyStaleFlags: {
+ type: 'number',
+ example: 2,
+ description:
+ 'The current number of time calculated potentially stale flags',
+ },
+ health: {
+ type: 'number',
+ description: 'The current health score of the project',
+ example: 80,
+ },
},
},
onboardingStatus: projectOverviewSchema.properties.onboardingStatus,