diff --git a/src/components/BMDashboard/WeeklyProjectSummary/ActualVsPlannedCost/ActualVsPlannedCost.jsx b/src/components/BMDashboard/WeeklyProjectSummary/ActualVsPlannedCost/ActualVsPlannedCost.jsx
index b2e139f3f0..54493fbcfe 100644
--- a/src/components/BMDashboard/WeeklyProjectSummary/ActualVsPlannedCost/ActualVsPlannedCost.jsx
+++ b/src/components/BMDashboard/WeeklyProjectSummary/ActualVsPlannedCost/ActualVsPlannedCost.jsx
@@ -1,4 +1,5 @@
import { useEffect, useState, useMemo } from 'react';
+import PropTypes from 'prop-types';
import axios from 'axios';
import { useDispatch, useSelector } from 'react-redux';
import {
@@ -17,6 +18,161 @@ import { fetchBMProjects } from '../../../../actions/bmdashboard/projectActions'
import { ENDPOINTS } from '../../../../utils/URL';
import styles from './ActualVsPlannedCost.module.css';
+function getBudgetStatus(variance) {
+ if (variance > 0) return 'Over Budget';
+ if (variance < 0) return 'Under Budget';
+ return 'On Budget';
+}
+
+function getVarianceCardClass(variance, cardStyles) {
+ if (variance > 0) return cardStyles.varianceOverrun;
+ if (variance < 0) return cardStyles.varianceUnder;
+ return cardStyles.varianceNeutral;
+}
+
+function VarianceCard({ item, cardStyles }) {
+ const isOverrun = item.variance > 0;
+ const cardClass = getVarianceCardClass(item.variance, cardStyles);
+ return (
+
+
Actual vs Planned Costs
@@ -234,6 +338,26 @@ function ActualVsPlannedCost() {
{chartContent}
+
+ {!loading && !isFiltering && hasData && (
+
+
+
Variance and Budget Indicators
+
+ Total Variance: {isTotalOverrun ? '+' : ''}
+ {totalVariance.toLocaleString()}
+ {totalVariancePct !== null &&
+ ` (${isTotalOverrun ? '+' : ''}${totalVariancePct.toFixed(1)}%)`}
+
+
+
+
+ {chartDataWithVariance.map(item => (
+
+ ))}
+
+
+ )}
);
}
diff --git a/src/components/BMDashboard/WeeklyProjectSummary/ActualVsPlannedCost/ActualVsPlannedCost.module.css b/src/components/BMDashboard/WeeklyProjectSummary/ActualVsPlannedCost/ActualVsPlannedCost.module.css
index 6d1152ac71..053248bb46 100644
--- a/src/components/BMDashboard/WeeklyProjectSummary/ActualVsPlannedCost/ActualVsPlannedCost.module.css
+++ b/src/components/BMDashboard/WeeklyProjectSummary/ActualVsPlannedCost/ActualVsPlannedCost.module.css
@@ -43,6 +43,134 @@
color: var(--text-color);
}
+.varianceSummaryContainer {
+ margin-top: 12px;
+ border-top: 1px solid var(--button-hover);
+ padding-top: 10px;
+}
+
+.varianceSummaryHeader {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 10px;
+ margin-bottom: 10px;
+ flex-wrap: wrap;
+}
+
+.varianceSummaryTitle {
+ font-size: 0.9rem;
+ margin: 0;
+ color: var(--text-color);
+}
+
+.totalOverrunBadge,
+.totalOnTrackBadge {
+ font-size: 0.75rem;
+ font-weight: 700;
+ padding: 4px 8px;
+ border-radius: 999px;
+ border: 1px solid transparent;
+}
+
+.totalOverrunBadge {
+ color: #b63d30;
+ background: rgba(231, 74, 59, 0.14);
+ border-color: rgba(231, 74, 59, 0.35);
+}
+
+.totalOnTrackBadge {
+ color: #0f7f5b;
+ background: rgba(28, 200, 138, 0.14);
+ border-color: rgba(28, 200, 138, 0.35);
+}
+
+.varianceCardsRow {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
+ gap: 10px;
+}
+
+.varianceCard {
+ border-radius: 8px;
+ border: 1px solid transparent;
+ padding: 10px;
+}
+
+.varianceOverrun {
+ background: rgba(231, 74, 59, 0.1);
+ border-color: rgba(231, 74, 59, 0.35);
+}
+
+.varianceUnder {
+ background: rgba(28, 200, 138, 0.1);
+ border-color: rgba(28, 200, 138, 0.35);
+}
+
+.varianceNeutral {
+ background: rgba(128, 128, 128, 0.08);
+ border-color: rgba(128, 128, 128, 0.2);
+}
+
+.varianceCardCategory {
+ font-size: 0.82rem;
+ font-weight: 700;
+ color: var(--text-color);
+ margin-bottom: 8px;
+}
+
+.varianceCardRow {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ color: var(--text-color);
+ font-size: 0.8rem;
+ margin-bottom: 3px;
+}
+
+.varianceCardPct {
+ margin-top: 4px;
+ font-size: 0.78rem;
+ font-weight: 700;
+ color: var(--text-color);
+}
+
+.varianceCardStatus {
+ margin-top: 6px;
+ font-size: 0.72rem;
+ font-weight: 700;
+ text-transform: uppercase;
+ letter-spacing: 0.02em;
+ color: var(--text-color);
+}
+
+.darkMode .totalOverrunBadge {
+ color: #ffb7af;
+ background: rgba(231, 74, 59, 0.2);
+ border-color: rgba(255, 183, 175, 0.45);
+}
+
+.darkMode .totalOnTrackBadge {
+ color: #9ce7cb;
+ background: rgba(28, 200, 138, 0.2);
+ border-color: rgba(156, 231, 203, 0.45);
+}
+
+.darkMode .varianceOverrun {
+ background: rgba(231, 74, 59, 0.18);
+ border-color: rgba(255, 183, 175, 0.3);
+}
+
+.darkMode .varianceUnder {
+ background: rgba(28, 200, 138, 0.18);
+ border-color: rgba(156, 231, 203, 0.3);
+}
+
+.darkMode .varianceNeutral {
+ background: rgba(160, 170, 190, 0.12);
+ border-color: rgba(160, 170, 190, 0.28);
+}
+
@media (width <= 768px) {
.selectorsContainer {
flex-direction: column;
diff --git a/src/components/BMDashboard/WeeklyProjectSummary/WeeklyProjectSummary.jsx b/src/components/BMDashboard/WeeklyProjectSummary/WeeklyProjectSummary.jsx
index 68108ad932..781b59c2eb 100644
--- a/src/components/BMDashboard/WeeklyProjectSummary/WeeklyProjectSummary.jsx
+++ b/src/components/BMDashboard/WeeklyProjectSummary/WeeklyProjectSummary.jsx
@@ -28,6 +28,8 @@ import SupplierPerformanceGraph from './SupplierPerformanceGraph.jsx';
import MostFrequentKeywords from './MostFrequentKeywords/MostFrequentKeywords';
import LessonsLearntChart from '../LessonsLearnt/LessonsLearntChart';
import DistributionLaborHours from './DistributionLaborHours/DistributionLaborHours';
+import FinancialsTrackingCard from './ExpenditureChart/FinancialsTrackingCard';
+import FinancialStatButtons from './Financials/FinancialStatButtons';
import ToolsStoppageHorizontalBarChart from './Tools/ToolsStoppageHorizontalBarChart/ToolsStoppageHorizontalBarChart';
import IssueCharts from '../Issues/openIssueCharts';
import ToolStatusDonutChart from './ToolStatusDonutChart/ToolStatusDonutChart';
@@ -413,9 +415,11 @@ function WeeklyProjectSummary() {
className={`${styles.weeklyProjectSummaryCard} ${styles.normalCard}`}
>
{(() => {
+ if (index === 0) return
;
+ if (index === 1) return
;
if (index === 2) return
;
if (index === 3) return
;
- return '📊 Card';
+ return null;
})()}
);
diff --git a/src/services/projectCostService.js b/src/services/projectCostService.js
index 66f0f1dfcb..bbde84c02f 100644
--- a/src/services/projectCostService.js
+++ b/src/services/projectCostService.js
@@ -4,11 +4,11 @@ import { ApiEndpoint } from '../utils/URL';
const ApiUri = `${ApiEndpoint}/`;
const getProjectCosts = projectId => {
- return httpService.get(`${ApiUri}/project/${projectId}/costs`);
+ return httpService.get(`${ApiUri}project/${projectId}/costs`);
};
const getProjectPredictions = projectId => {
- return httpService.get(`${ApiUri}/project/${projectId}/predictions`);
+ return httpService.get(`${ApiUri}project/${projectId}/predictions`);
};
export default {