Skip to content

Commit 599c0c8

Browse files
hotfixed filter options
1 parent 2a27bb1 commit 599c0c8

1 file changed

Lines changed: 67 additions & 84 deletions

File tree

Lines changed: 67 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
import { useEffect, useState } from 'react';
22
import HoursWorkedPieChart from '../HoursWorkedPieChart/HoursWorkedPieChart';
33

4-
// components
4+
// Components
55
import Loading from '../../common/Loading';
66

77
const COLORS = ['#00AFF4', '#FFA500', '#00B030', '#EC52CB', '#F8FF00'];
88

9+
// --- Helper Functions ---
10+
911
function parseRangeStart(rangeStr) {
1012
if (!rangeStr) return 0;
1113
const [first] = String(rangeStr).split(/[-+]/);
@@ -16,27 +18,26 @@ function parseRangeStart(rangeStr) {
1618
function normalizeBucketId(rangeStr) {
1719
if (!rangeStr) return '';
1820
const trimmed = String(rangeStr).trim();
21+
1922
if (trimmed.includes('+')) {
2023
const start = parseRangeStart(trimmed);
21-
return start === 40 ? '50+' : `${start}+`;
22-
}
23-
if (trimmed.includes('-')) {
24-
const start = parseRangeStart(trimmed);
25-
if (start === 40) return '40';
26-
return String(start);
24+
return `${start}+`;
2725
}
26+
2827
return String(parseRangeStart(trimmed));
2928
}
3029

3130
function mergeHoursBuckets(hoursData) {
3231
const safeHoursData = Array.isArray(hoursData) ? hoursData : [];
3332
const merged = new Map();
33+
3434
safeHoursData.forEach(item => {
3535
const normalizedId = normalizeBucketId(item?._id);
3636
if (!normalizedId) return;
3737
const existing = merged.get(normalizedId) || 0;
3838
merged.set(normalizedId, existing + (Number(item?.count) || 0));
3939
});
40+
4041
return [...merged.entries()]
4142
.map(([id, count]) => ({ _id: id, count }))
4243
.sort((a, b) => parseRangeStart(a._id) - parseRangeStart(b._id));
@@ -76,54 +77,58 @@ function allocateRoundedHoursByCount(normalizedHoursData, totalHoursWorked) {
7677
.sort((a, b) => parseRangeStart(a._id) - parseRangeStart(b._id));
7778
}
7879

79-
// convert backend range string (e.g. "10", "40+", "20-29")
80-
// into a user-facing label with units.
8180
export function formatRangeLabel(rangeStr) {
8281
if (!rangeStr) return '';
8382
const normalizedRange = normalizeBucketId(rangeStr);
84-
let displayName = '';
83+
8584
if (normalizedRange.includes('+')) {
8685
const num = parseFloat(normalizedRange.replace('+', ''));
87-
if (num === 40) {
88-
displayName = '50+ hrs';
89-
} else {
90-
displayName = `${num}+ hrs`;
91-
}
86+
return `${num}+ hrs`;
9287
} else {
9388
const num = parseFloat(normalizedRange);
94-
const next = (num + 9.99).toFixed(2);
95-
displayName = `${num}-${next} hrs`;
89+
return `${num}-${num + 9} hrs`;
9690
}
97-
return displayName;
9891
}
9992

93+
function buildChartData(hoursData, totalHoursData) {
94+
const normalizedHoursData = mergeHoursBuckets(hoursData);
95+
const totalVolunteers = normalizedHoursData.reduce((total, cur) => total + (cur.count || 0), 0);
96+
const totalHoursWorked = Number(totalHoursData?.current ?? totalHoursData?.count ?? 0);
97+
98+
const hoursByBucket = allocateRoundedHoursByCount(normalizedHoursData, totalHoursWorked);
99+
const totalAllocatedHours = hoursByBucket.reduce(
100+
(sum, bucket) => sum + (bucket.allocatedHours || 0),
101+
0,
102+
);
103+
104+
const userData = hoursByBucket.map(range => {
105+
// FALLBACK: If total hours is 0, visualizes chart layout by volunteer count
106+
// so that the chart displays nicely instead of loading with empty 0 slices.
107+
const value = totalHoursWorked > 0 ? range.allocatedHours || 0 : range.count || 0;
108+
const denominator = totalHoursWorked > 0 ? totalAllocatedHours : totalVolunteers;
109+
110+
return {
111+
name: formatRangeLabel(range._id),
112+
value,
113+
percentage: denominator ? Math.round((value / denominator) * 100) : 0,
114+
};
115+
});
116+
117+
return { normalizedHoursData, userData, totalVolunteers, totalHoursWorked };
118+
}
119+
120+
// --- Sub-Components ---
121+
100122
function HoursWorkList({ data, darkMode }) {
101123
if (!data) return <div />;
102124

103125
const ranges = data.map((elem, index) => {
104-
const rangeStr = elem._id;
105-
const entry = {
106-
name: rangeStr,
126+
return {
127+
name: elem._id,
107128
count: elem.count,
129+
displayName: formatRangeLabel(elem._id),
130+
color: COLORS[index % COLORS.length],
108131
};
109-
110-
// derive human-readable label for the bucket
111-
const displayName = formatRangeLabel(rangeStr);
112-
113-
entry.displayName = displayName;
114-
entry.color = COLORS[index];
115-
116-
const rangeArr = rangeStr.split('-');
117-
if (rangeArr.length > 1) {
118-
const [min, max] = rangeArr;
119-
entry.min = Number(min);
120-
entry.max = Number(max);
121-
} else {
122-
const min = rangeStr.split('+');
123-
entry.min = Number(min);
124-
entry.max = Infinity;
125-
}
126-
return entry;
127132
});
128133

129134
return (
@@ -132,17 +137,16 @@ function HoursWorkList({ data, darkMode }) {
132137
<div>
133138
<ul className="list-unstyled">
134139
{ranges.map(item => (
135-
<li key={item.name} className="text-secondary d-flex align-items-center">
140+
<li key={item.name} className="text-secondary d-flex align-items-center mb-1">
136141
<div
137142
className="me-2"
138143
style={{
139144
width: '15px',
140145
height: '15px',
141-
marginRight: '5px',
142146
backgroundColor: item.color,
143147
}}
144148
/>
145-
<span className="ms-2">{item.name}</span>
149+
<span className="ms-2">{item.displayName}</span>
146150
</li>
147151
))}
148152
</ul>
@@ -151,29 +155,7 @@ function HoursWorkList({ data, darkMode }) {
151155
);
152156
}
153157

154-
// export HoursWorkList separately for testing
155-
export { HoursWorkList };
156-
157-
// shared helper: derives normalizedHoursData, userData, and totals from raw API data
158-
function buildChartData(hoursData, totalHoursData) {
159-
const normalizedHoursData = mergeHoursBuckets(hoursData);
160-
const totalVolunteers = normalizedHoursData.reduce((total, cur) => total + (cur.count || 0), 0);
161-
const totalHoursWorked = Number(totalHoursData?.current ?? totalHoursData?.count ?? 0);
162-
const hoursByBucket = allocateRoundedHoursByCount(normalizedHoursData, totalHoursWorked);
163-
const totalAllocatedHours = hoursByBucket.reduce(
164-
(sum, bucket) => sum + (bucket.allocatedHours || 0),
165-
0,
166-
);
167-
const userData = hoursByBucket.map(range => {
168-
const value = range.allocatedHours || 0;
169-
return {
170-
name: range._id,
171-
value,
172-
percentage: totalAllocatedHours ? Math.round((value / totalAllocatedHours) * 100) : 0,
173-
};
174-
});
175-
return { normalizedHoursData, userData, totalVolunteers, totalHoursWorked };
176-
}
158+
// --- Main Exported Component ---
177159

178160
export default function VolunteerHoursDistribution({
179161
isLoading,
@@ -182,30 +164,31 @@ export default function VolunteerHoursDistribution({
182164
totalHoursData,
183165
}) {
184166
const [windowSize, setWindowSize] = useState({
185-
width: window.innerWidth,
186-
height: window.innerHeight,
167+
width: typeof window !== 'undefined' ? window.innerWidth : 1200,
168+
height: typeof window !== 'undefined' ? window.innerHeight : 800,
187169
});
188170

189-
const updateWindowSize = () => {
190-
setWindowSize({
191-
width: window.innerWidth,
192-
height: window.innerHeight,
193-
});
194-
};
195-
196171
useEffect(() => {
197-
window.addEventListener('resize', updateWindowSize);
198-
return () => {
199-
window.removeEventListener('resize', updateWindowSize);
172+
if (typeof window === 'undefined') return;
173+
174+
const updateWindowSize = () => {
175+
setWindowSize({
176+
width: window.innerWidth,
177+
height: window.innerHeight,
178+
});
200179
};
180+
181+
window.addEventListener('resize', updateWindowSize);
182+
return () => window.removeEventListener('resize', updateWindowSize);
201183
}, []);
202184

203185
if (isLoading) {
204186
return (
205-
<div className="d-flex justify-content-center align-items-center">
206-
<div className="w-100vh">
207-
<Loading />
208-
</div>
187+
<div
188+
className="d-flex justify-content-center align-items-center"
189+
style={{ minHeight: '200px' }}
190+
>
191+
<Loading />
209192
</div>
210193
);
211194
}
@@ -232,10 +215,10 @@ export default function VolunteerHoursDistribution({
232215
);
233216
}
234217

235-
// computeDistribution: pure helper to derive the chart payload from API data
218+
// Extra named exports for automated testing
219+
export { HoursWorkList, mergeHoursBuckets };
220+
236221
export function computeDistribution(hoursData, totalHoursData) {
237222
const { userData, totalVolunteers, totalHoursWorked } = buildChartData(hoursData, totalHoursData);
238223
return { userData, totalVolunteers, totalHoursWorked };
239224
}
240-
241-
export { mergeHoursBuckets };

0 commit comments

Comments
 (0)