diff --git a/src/components/BMDashboard/WeeklyProjectSummary/CostPredictionChart.jsx b/src/components/BMDashboard/WeeklyProjectSummary/CostPredictionChart.jsx index e66b940f30..de7189b90d 100644 --- a/src/components/BMDashboard/WeeklyProjectSummary/CostPredictionChart.jsx +++ b/src/components/BMDashboard/WeeklyProjectSummary/CostPredictionChart.jsx @@ -12,6 +12,7 @@ import { ReferenceLine, } from 'recharts'; import styles from './CostPredictionChart.module.css'; +import { fetchBMProjects } from '../../../actions/bmdashboard/projectActions'; import projectCostService from '../../../services/projectCostService'; // Custom dot renderer (unchanged) @@ -63,18 +64,50 @@ function renderDotTopOrBottom(lineKey, color) { return null; }; } +function CustomTooltip({ active, payload, label }) { + if (!active || !payload || payload.length === 0) return null; + return ( +
+
{label}
+ {payload.map(item => ( +
+ {item.name}: $ + {Number(item.value).toLocaleString()} +
+ ))} +
+ ); +} function CostPredictionChart({ projectId }) { const dispatch = useDispatch(); const [chartData, setChartData] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); + // Date filters + const [startDate, setStartDate] = useState(''); + const [endDate, setEndDate] = useState(''); const darkMode = useSelector(state => state.theme.darkMode); + //Project list for dropdown + const projects = useSelector(state => state.bmProjects) || []; + const [selectedProject, setSelectedProject] = useState(''); const legendItems = [ { label: 'Planned Cost', color: '#7acba6', type: 'circle' }, { label: 'Actual Cost', color: '#9aa6ff', type: 'circle' }, { label: 'Predicted Cost', color: '#ff8c2a', type: 'dash' }, ]; + // Reset all the filters + const resetFilters = () => { + setSelectedProject(''); + setStartDate(''); + setEndDate(''); + setError(''); + }; + //Project dropdown + useEffect(() => { + dispatch(fetchBMProjects()); + }, [dispatch]); + useEffect(() => { const fetchData = async () => { try { @@ -106,8 +139,9 @@ function CostPredictionChart({ projectId }) { } }; - if (projectId) fetchData(); - }, [projectId]); + const effectiveProjectId = selectedProject || projectId; //fetches data when either the prop projectId or the dropdown selection changes + if (effectiveProjectId) fetchData(); + }, [projectId, selectedProject]); if (loading) return
Loading chart data...
; if (error) return
Error: {error}
; @@ -117,16 +151,74 @@ function CostPredictionChart({ projectId }) { year: 'numeric', }); + // Filter data by date range + const filteredData = chartData.filter(item => { + const itemDate = new Date(item.month); + const start = startDate ? new Date(startDate) : null; + const end = endDate ? new Date(endDate) : null; + return (!start || itemDate >= start) && (!end || itemDate <= end); + }); + // THEME COLORS — only for grid, axis ticks/lines, legend const gridColor = darkMode ? '#e5e7eb' : '#9ca3af'; // grid lines const tickColor = darkMode ? '#e5e7eb' : '#9ca3af'; // tick text const axisLineCol = darkMode ? '#e5e7eb' : '#9ca3af'; // axis baseline & tick marks const legendColor = darkMode ? '#e5e7eb' : '#9ca3af'; // legend text + return (

Planned Vs Actual costs tracking

+ {/* Project Filter Dropdown */} +
+
+ + +
+
+ + {/* Date Filters */} +
+
+ + setStartDate(e.target.value)} /> +
+
+ + setEndDate(e.target.value)} /> +
+
+ {/* Reset Filters button */} +
+ +
- + {/* Grid */} ( - - {payload.value} - - )} - tickMargin={0} // apply margin at XAxis level, not inside + tick={({ x, y, payload }) => { + // Converts "February 2024" to "Feb 24" + const shortLabel = payload.value.replace( + /(\w{3})\w*\s(\d{4})/, + (_, m, y) => `${m} ${y.slice(-2)}`, + ); + + return ( + + {shortLabel} + + ); + }} + tickMargin={10} /> + ( @@ -159,7 +261,11 @@ function CostPredictionChart({ projectId }) { )} /> {/* Tooltip & Legend */} - + } + labelFormatter={label => ` ${label}`} + formatter={(value, name) => [`$${Number(value).toLocaleString()}`, name]} + />