diff --git a/frontend/src/component/project/Project/ProjectInsights/ProjectHealthChart.test.tsx b/frontend/src/component/project/Project/ProjectInsights/ProjectHealthChart.test.tsx
new file mode 100644
index 0000000000..8bb05b261e
--- /dev/null
+++ b/frontend/src/component/project/Project/ProjectInsights/ProjectHealthChart.test.tsx
@@ -0,0 +1,113 @@
+import React from 'react';
+import '@testing-library/jest-dom';
+import { ProjectHealthChart } from './ProjectHealthChart';
+import { render } from 'utils/testRenderer';
+import { screen } from '@testing-library/react';
+
+describe('ProjectHealthChart', () => {
+ test('renders correctly with no flags', () => {
+ const { container } = render(
+ ,
+ );
+
+ const activeCircle = container.querySelector(
+ 'circle[data-testid="active-circle"]',
+ );
+ const staleCircle = container.querySelector(
+ 'circle[data-testid="stale-circle"]',
+ );
+ const potentiallyStaleCircle = container.querySelector(
+ 'circle[data-testid="potentially-stale-circle"]',
+ );
+
+ expect(activeCircle).toBeInTheDocument();
+ expect(staleCircle).not.toBeInTheDocument();
+ expect(potentiallyStaleCircle).not.toBeInTheDocument();
+ });
+
+ test('renders correctly with 1 active and 0 stale', () => {
+ const { container } = render(
+ ,
+ );
+
+ const activeCircle = container.querySelector(
+ 'circle[data-testid="active-circle"]',
+ );
+ const staleCircle = container.querySelector(
+ 'circle[data-testid="stale-circle"]',
+ );
+ const potentiallyStaleCircle = container.querySelector(
+ 'circle[data-testid="potentially-stale-circle"]',
+ );
+
+ expect(activeCircle).toBeInTheDocument();
+ expect(staleCircle).not.toBeInTheDocument();
+ expect(potentiallyStaleCircle).not.toBeInTheDocument();
+ });
+
+ test('renders correctly with 0 active and 1 stale', () => {
+ const { container } = render(
+ ,
+ );
+
+ const staleCircle = container.querySelector(
+ 'circle[data-testid="stale-circle"]',
+ );
+
+ expect(staleCircle).toBeInTheDocument();
+ });
+
+ test('renders correctly with active, stale and potentially stale', () => {
+ const { container } = render(
+ ,
+ );
+
+ const activeCircle = container.querySelector(
+ 'circle[data-testid="active-circle"]',
+ );
+ const staleCircle = container.querySelector(
+ 'circle[data-testid="stale-circle"]',
+ );
+ const potentiallyStaleCircle = container.querySelector(
+ 'circle[data-testid="potentially-stale-circle"]',
+ );
+
+ expect(activeCircle).toBeInTheDocument();
+ expect(staleCircle).toBeInTheDocument();
+ expect(potentiallyStaleCircle).toBeInTheDocument();
+ });
+
+ test('renders flags count and health', () => {
+ const { container } = render(
+ ,
+ );
+
+ expect(screen.queryByText('3 flags')).toBeInTheDocument();
+ expect(screen.queryByText('50%')).toBeInTheDocument();
+ });
+});
diff --git a/frontend/src/component/project/Project/ProjectInsights/ProjectHealthChart.tsx b/frontend/src/component/project/Project/ProjectInsights/ProjectHealthChart.tsx
new file mode 100644
index 0000000000..70d407f1ce
--- /dev/null
+++ b/frontend/src/component/project/Project/ProjectInsights/ProjectHealthChart.tsx
@@ -0,0 +1,128 @@
+import type React from 'react';
+import { useTheme } from '@mui/material';
+import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
+
+interface ProgressComponentProps {
+ active: number;
+ stale: number;
+ potentiallyStale: number;
+ health: number;
+}
+
+export const ProjectHealthChart: React.FC = ({
+ active,
+ stale,
+ potentiallyStale,
+ health,
+}) => {
+ const theme = useTheme();
+ const gap = active === 0 || stale === 0 ? 0 : 10;
+ const strokeWidth = 6;
+ const radius = 50 - strokeWidth / 2;
+ const circumference = 2 * Math.PI * radius;
+ const gapAngle = (gap / circumference) * 360;
+
+ const totalCount = active + stale;
+ const activePercentage =
+ totalCount === 0 ? 100 : (active / totalCount) * 100;
+ const stalePercentage = totalCount === 0 ? 0 : (stale / totalCount) * 100;
+ const potentiallyStalePercentage =
+ active === 0 ? 0 : (potentiallyStale / active) * 100;
+
+ const activeLength = (activePercentage / 100) * circumference - gap;
+ const staleLength = (stalePercentage / 100) * circumference - gap;
+ const potentiallyStaleLength =
+ (potentiallyStalePercentage / 100) * activeLength;
+
+ const activeRotation = -90 + gapAngle / 2;
+ const potentiallyStaleRotation =
+ activeRotation +
+ ((activeLength - potentiallyStaleLength) / circumference) * 360;
+ const staleRotation =
+ activeRotation + (activeLength / circumference) * 360 + gapAngle;
+
+ const innerRadius = radius / 1.2;
+
+ return (
+
+ );
+};