diff --git a/src/components/BMDashboard/BMDashboard.jsx b/src/components/BMDashboard/BMDashboard.jsx
index 56087ba420..919bc8008f 100644
--- a/src/components/BMDashboard/BMDashboard.jsx
+++ b/src/components/BMDashboard/BMDashboard.jsx
@@ -4,6 +4,7 @@ import { Container } from 'reactstrap';
import { fetchBMProjects } from '../../actions/bmdashboard/projectActions';
import ProjectsList from './Projects/ProjectsList';
import ProjectSelectForm from './Projects/ProjectSelectForm';
+import ProjectStatusDonutChart from './ProjectStatus/ProjectStatusDonutChart';
import BMError from './shared/BMError';
import './BMDashboard.css';
@@ -36,6 +37,7 @@ export function BMDashboard() {
) : (
<>
+
>
)}
diff --git a/src/components/BMDashboard/ProjectStatus/ProjectStatusDonutChart.jsx b/src/components/BMDashboard/ProjectStatus/ProjectStatusDonutChart.jsx
new file mode 100644
index 0000000000..661255dab9
--- /dev/null
+++ b/src/components/BMDashboard/ProjectStatus/ProjectStatusDonutChart.jsx
@@ -0,0 +1,166 @@
+import React, { useEffect, useState } from 'react';
+import { PieChart, Pie, Cell, Tooltip, Legend, ResponsiveContainer, Label } from 'recharts';
+import axios from 'axios';
+import styles from './ProjectStatusDonutChart.module.css';
+
+const COLORS = ['#B39DDB', '#80DEEA', '#FFABAB']; // Active, Completed, Delayed
+
+export default function ProjectStatusDonutChart() {
+ const [loading, setLoading] = useState(true);
+ const [error, setError] = useState(null);
+ const [statusData, setStatusData] = useState(null);
+
+ const [startDate, setStartDate] = useState('');
+ const [endDate, setEndDate] = useState('');
+
+ const fetchStatus = async () => {
+ try {
+ setLoading(true);
+ setError(null);
+
+ // Build query string
+ const query = [];
+ if (startDate) query.push(`startDate=${startDate}`);
+ if (endDate) query.push(`endDate=${endDate}`);
+ const queryString = query.length ? `?${query.join('&')}` : '';
+
+ // Get token from localStorage (Dev Admin session)
+ const token = localStorage.getItem('token');
+
+ const res = await axios.get(`http://localhost:4500/api/projects/status${queryString}`, {
+ headers: { Authorization: token },
+ });
+
+ // TEMPORARY MOCK DATA - for testing purposes
+ /*setStatusData({
+ totalProjects: 50,
+ activeProjects: 20,
+ completedProjects: 20,
+ delayedProjects: 10,
+ });
+ return;*/
+
+ setStatusData(res.data);
+ } catch (err) {
+ // console.error(err);
+ setError('Unable to load project status.');
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ useEffect(() => {
+ fetchStatus();
+ }, []);
+
+ if (loading) return
Loading project status...
;
+ if (error) return {error}
;
+ if (!statusData) return No data available.
;
+
+ const pieData = [
+ { name: 'Active Projects', value: statusData.activeProjects },
+ { name: 'Completed Projects', value: statusData.completedProjects },
+ { name: 'Delayed Projects', value: statusData.delayedProjects },
+ ];
+
+ // SHOW MESSAGE WHEN THERE IS NO DATA
+ if (pieData.every(item => item.value === 0)) {
+ return (
+
+
PROJECT STATUS
+
No project status data available.
+
+ );
+ }
+
+ const today = new Date().toLocaleDateString('en-US', {
+ weekday: 'short',
+ month: 'short',
+ day: 'numeric',
+ year: 'numeric',
+ });
+
+ const allZero =
+ !statusData.activeProjects && !statusData.completedProjects && !statusData.delayedProjects;
+
+ return (
+
+
PROJECT STATUS
+
+
+ setStartDate(e.target.value)}
+ />
+
+ setEndDate(e.target.value)}
+ />
+
+
+
+
+
+ {/* Only draw the ring if at least one status has data */}
+ {!allZero && (
+
+
+
+ {pieData.map((entry, index) => (
+ |
+ ))}
+
+
+
+
+
+
+ )}
+
+
+
{today}
+
+
ACTIVE PROJECTS
+
{statusData.activeProjects}
+
+
COMPLETED PROJECTS
+
{statusData.completedProjects}
+
+
DELAYED PROJECTS
+
{statusData.delayedProjects}
+
+
+
+ );
+}
diff --git a/src/components/BMDashboard/ProjectStatus/ProjectStatusDonutChart.module.css b/src/components/BMDashboard/ProjectStatus/ProjectStatusDonutChart.module.css
new file mode 100644
index 0000000000..0d9e347cf3
--- /dev/null
+++ b/src/components/BMDashboard/ProjectStatus/ProjectStatusDonutChart.module.css
@@ -0,0 +1,97 @@
+.container {
+ width: 100%;
+ margin-top: 20px;
+ background: #ffffff;
+ color: #111827;
+ padding: 20px;
+ border-radius: 12px;
+ position: relative;
+ border: 1px solid rgba(0, 0, 0, 0.12);
+}
+
+.title {
+ text-align: center;
+ margin-bottom: 20px;
+ font-size: 26px;
+ font-weight: 600;
+}
+
+.filterRow {
+ display: flex;
+ justify-content: center;
+ gap: 10px;
+ margin-bottom: 20px;
+}
+
+.applyBtn {
+ background: #4285f4;
+ color: white;
+ padding: 8px 16px;
+ border-radius: 6px;
+ border: none;
+ cursor: pointer;
+}
+
+.chartWrapper {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ gap: 80px;
+ width: 100%;
+}
+
+.summaryBox {
+ flex: 0 0 280px;
+ min-width: 260px;
+}
+
+.label {
+ font-size: 14px;
+ font-weight: 700;
+ color: #6b7280;
+}
+
+.value {
+ font-size: 22px;
+ color: #d9534f;
+ margin-bottom: 10px;
+}
+
+@media (max-width: 768px) {
+ .chartWrapper {
+ flex-direction: column;
+ gap: 28px;
+ align-items: center;
+ }
+
+ .chartContainer {
+ flex: none;
+ width: 92vw;
+ max-width: 420px;
+ height: 92vw;
+ max-height: 420px;
+ }
+
+ .summaryBox {
+ width: 100%;
+ align-items: center;
+ text-align: center;
+ }
+}
+.centerLabel {
+ font-size: 16px;
+ font-weight: 600;
+ fill: currentColor;
+}
+
+.centerValue {
+ font-size: 18px;
+ font-weight: 700;
+ fill: currentColor;
+}
+
+.noDataMessage {
+ text-align: center;
+ margin-top: 16px;
+ font-weight: 500;
+}
\ No newline at end of file