Skip to content

Commit 6dc85dc

Browse files
author
Jaissica Hora
committed
jaissica - created actual vs planned expense bar chart
1 parent a609e35 commit 6dc85dc

3 files changed

Lines changed: 141 additions & 13 deletions

File tree

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
.chart-container {
2+
width: 100%;
3+
min-height: 250px;
4+
max-height: 400px;
5+
padding: 0.5rem;
6+
box-sizing: border-box;
7+
overflow-x: auto;
8+
}
9+
10+
.recharts-wrapper text {
11+
font-size: 11px;
12+
}
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
import React, { useEffect, useState } from 'react';
2+
import {
3+
BarChart, Bar, XAxis, YAxis, LabelList, ResponsiveContainer
4+
} from 'recharts';
5+
6+
const categories = ["Plumbing", "Electrical", "Structural", "Mechanical"];
7+
const projects = ["Project A", "Project B", "Project C"];
8+
9+
export default function ExpenseBarChart() {
10+
const [projectId, setProjectId] = useState('');
11+
const [categoryFilter, setCategoryFilter] = useState('ALL');
12+
const [selectedDate, setSelectedDate] = useState('');
13+
const [data, setData] = useState([]);
14+
15+
useEffect(() => {
16+
async function fetchData() {
17+
try {
18+
const rawData = [
19+
{ projectId: 'Project A', category: 'Plumbing', plannedCost: 1000, actualCost: 1200, date: '2025-04-01' },
20+
{ projectId: 'Project A', category: 'Electrical', plannedCost: 1500, actualCost: 1300, date: '2025-04-01' },
21+
{ projectId: 'Project B', category: 'Plumbing', plannedCost: 1100, actualCost: 1050, date: '2025-04-01' },
22+
{ projectId: 'Project B', category: 'Structural', plannedCost: 2200, actualCost: 2150, date: '2025-04-01' },
23+
{ projectId: 'Project C', category: 'Mechanical', plannedCost: 1300, actualCost: 1350, date: '2025-04-01' },
24+
{ projectId: 'Project C', category: 'Electrical', plannedCost: 1400, actualCost: 1600, date: '2025-04-01' },
25+
];
26+
27+
const filtered = rawData.filter(entry => {
28+
const dateMatch = !selectedDate || entry.date === selectedDate;
29+
const projectMatch = projectId === '' || entry.projectId === projectId;
30+
const categoryMatch = categoryFilter === 'ALL' || entry.category === categoryFilter;
31+
return dateMatch && projectMatch && categoryMatch;
32+
});
33+
34+
const aggregated = {};
35+
filtered.forEach(entry => {
36+
const key = entry.projectId;
37+
if (!aggregated[key]) {
38+
aggregated[key] = { project: key, planned: 0, actual: 0 };
39+
}
40+
aggregated[key].planned += entry.plannedCost;
41+
aggregated[key].actual += entry.actualCost;
42+
});
43+
44+
setData(Object.values(aggregated));
45+
} catch (error) {
46+
console.error("Error fetching expense comparison data:", error);
47+
}
48+
}
49+
50+
fetchData();
51+
}, [projectId, categoryFilter, selectedDate]);
52+
53+
return (
54+
<div style={{ width: '100%', padding: '0.5rem' }}>
55+
<div style={{ textAlign: 'center', marginBottom: '0.75rem' }}>
56+
<h4 style={{ margin: 0, color: '#555', fontSize: '1.2rem' }}>Planned vs Actual Cost</h4>
57+
</div>
58+
59+
<div
60+
style={{
61+
display: 'flex',
62+
flexWrap: 'wrap',
63+
justifyContent: 'center',
64+
alignItems: 'center',
65+
gap: '1rem',
66+
fontSize: '0.75rem',
67+
marginBottom: '0.5rem'
68+
}}
69+
>
70+
<label style={{ minWidth: '150px' }}>Project:
71+
<select value={projectId} onChange={(e) => setProjectId(e.target.value)} style={{ marginLeft: '0.3rem', width: '100%' }}>
72+
<option value="">All</option>
73+
{projects.map(p => <option key={p} value={p}>{p}</option>)}
74+
</select>
75+
</label>
76+
<label style={{ minWidth: '150px' }}>Category:
77+
<select value={categoryFilter} onChange={(e) => setCategoryFilter(e.target.value)} style={{ marginLeft: '0.3rem', width: '100%' }}>
78+
<option value="ALL">All</option>
79+
{categories.map(cat => <option key={cat} value={cat}>{cat}</option>)}
80+
</select>
81+
</label>
82+
<label style={{ minWidth: '150px' }}>Date:
83+
<input type="date" value={selectedDate} onChange={(e) => setSelectedDate(e.target.value)} style={{ marginLeft: '0.3rem', width: '100%' }} />
84+
</label>
85+
</div>
86+
87+
<div style={{ display: 'flex', justifyContent: 'center', gap: '1rem', fontSize: '0.75rem', marginBottom: '0.75rem', flexWrap: 'wrap' }}>
88+
<span style={{ display: 'flex', alignItems: 'center', gap: '0.25rem' }}>
89+
<span style={{ width: 10, height: 10, backgroundColor: '#4285F4', display: 'inline-block' }} /> Planned
90+
</span>
91+
<span style={{ display: 'flex', alignItems: 'center', gap: '0.25rem' }}>
92+
<span style={{ width: 10, height: 10, backgroundColor: '#EA4335', display: 'inline-block' }} /> Actual
93+
</span>
94+
</div>
95+
96+
<div style={{ width: '100%', height: '240px' }}>
97+
<ResponsiveContainer width="100%" height="100%">
98+
<BarChart
99+
data={data}
100+
margin={{ top: 10, right: 10, left: 35, bottom: 35 }}
101+
>
102+
<XAxis dataKey="project" tick={{ fontSize: 10 }} interval={0} angle={-15} textAnchor="end" label={{ value: 'Project Name', position: 'insideBottom', dy: 25, fontSize: 10 }} />
103+
<YAxis tick={{ fontSize: 10 }} axisLine={true} tickLine={true} />
104+
<Bar dataKey="planned" fill="#4285F4" name="Planned">
105+
<LabelList dataKey="planned" position="top" style={{ fontSize: 8 }} />
106+
</Bar>
107+
<Bar dataKey="actual" fill="#EA4335" name="Actual">
108+
<LabelList dataKey="actual" position="top" style={{ fontSize: 8 }} />
109+
</Bar>
110+
</BarChart>
111+
</ResponsiveContainer>
112+
</div>
113+
</div>
114+
);
115+
}
116+
117+
118+

src/components/BMDashboard/WeeklyProjectSummary/WeeklyProjectSummary.jsx

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { v4 as uuidv4 } from 'uuid';
55
import WeeklyProjectSummaryHeader from './WeeklyProjectSummaryHeader';
66
import { fetchAllMaterials } from '../../../actions/bmdashboard/materialsActions';
77
import QuantityOfMaterialsUsed from './QuantityOfMaterialsUsed/QuantityOfMaterialsUsed';
8+
import ExpenseBarChart from './Financials/ExpenseBarChart';
89

910
const projectStatusButtons = [
1011
{
@@ -220,20 +221,17 @@ export default function WeeklyProjectSummary() {
220221
key: 'Financials',
221222
className: 'large',
222223
content: (
223-
<>
224-
{Array.from({ length: 4 }).map(() => {
225-
const uniqueId = uuidv4();
226-
return (
227-
<div key={uniqueId} className="weekly-project-summary-card financial-small">
228-
📊 Card
229-
</div>
230-
);
231-
})}
232-
224+
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(2, 1fr)', gap: '10px' }}>
225+
<div className="weekly-project-summary-card financial-small">📊 Card</div>
226+
<div className="weekly-project-summary-card financial-chart">
227+
<ExpenseBarChart />
228+
</div>
229+
<div className="weekly-project-summary-card financial-small">📊 Card</div>
230+
<div className="weekly-project-summary-card financial-small">📊 Card</div>
233231
<div className="weekly-project-summary-card financial-big">📊 Big Card</div>
234-
</>
232+
</div>
235233
),
236-
},
234+
},
237235
{
238236
title: 'Loss Tracking',
239237
key: 'Loss Tracking',
@@ -267,7 +265,7 @@ export default function WeeklyProjectSummary() {
267265
],
268266
[quantityOfMaterialsUsedData],
269267
);
270-
268+
271269
return (
272270
<div className={`weekly-project-summary-container ${darkMode ? 'dark-mode' : ''}`}>
273271
<WeeklyProjectSummaryHeader />

0 commit comments

Comments
 (0)