Skip to content

Commit 2b97432

Browse files
committed
progress
1 parent 22f2487 commit 2b97432

1 file changed

Lines changed: 126 additions & 27 deletions

File tree

src/frontend/src/pages/ProjectPage/ProjectSpendingHistory.tsx

Lines changed: 126 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,10 @@ import {
1919
InputLabel,
2020
Select,
2121
Button,
22-
Link
22+
Link,
23+
LinearProgress,
24+
Card,
25+
CardContent
2326
} from '@mui/material';
2427
import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown';
2528
import KeyboardArrowUpIcon from '@mui/icons-material/KeyboardArrowUp';
@@ -101,14 +104,18 @@ const ProjectSpendingHistory: React.FC<ProjectSpendingHistoryProps> = ({ wbsNum
101104
return false;
102105
}
103106
}
104-
const requestDate = new Date(request.dateCreated);
105-
if (dateFromFilter) {
107+
const requestDate =
108+
request.reimbursementStatuses && request.reimbursementStatuses.length > 0
109+
? new Date(Math.min(...request.reimbursementStatuses.map((status) => new Date(status.dateCreated).getTime())))
110+
: null;
111+
112+
if (dateFromFilter && requestDate) {
106113
const fromDate = new Date(dateFromFilter);
107114
if (requestDate < fromDate) {
108115
return false;
109116
}
110117
}
111-
if (dateToFilter) {
118+
if (dateToFilter && requestDate) {
112119
const toDate = new Date(dateToFilter);
113120
toDate.setHours(23, 59, 59, 999);
114121
if (requestDate > toDate) {
@@ -161,6 +168,22 @@ const ProjectSpendingHistory: React.FC<ProjectSpendingHistoryProps> = ({ wbsNum
161168
setAmountMaxFilter('');
162169
};
163170

171+
const budgetInfo = useMemo(() => {
172+
if (!project || !grouped.length) return null;
173+
174+
const totalBudget = project.budget; // Budget is in cents
175+
const totalSpent = grouped.reduce((sum, { request }) => sum + (request.totalCost || 0), 0); // Total cost is in cents
176+
const budgetRemaining = totalBudget - totalSpent;
177+
const budgetUsedPercentage = totalBudget > 0 ? (totalSpent / totalBudget) * 100 : 0;
178+
179+
return {
180+
totalBudget: totalBudget / 100, // Convert to dollars
181+
totalSpent: totalSpent / 100, // Convert to dollars
182+
budgetRemaining: budgetRemaining / 100, // Convert to dollars
183+
budgetUsedPercentage: Math.min(budgetUsedPercentage, 100) // Cap at 100%
184+
};
185+
}, [project, grouped]);
186+
164187
const hasActiveFilters =
165188
submitterFilter || statusFilter || dateFromFilter || dateToFilter || amountMinFilter || amountMaxFilter;
166189

@@ -212,6 +235,67 @@ const ProjectSpendingHistory: React.FC<ProjectSpendingHistoryProps> = ({ wbsNum
212235
</Box>
213236
</Box>
214237

238+
{budgetInfo && (
239+
<Card sx={{ mb: 3, backgroundColor: '#2a2a2a', border: '1px solid #444' }}>
240+
<CardContent>
241+
<Grid container spacing={3} alignItems="center">
242+
<Grid item xs={12} md={8}>
243+
<Typography variant="h6" sx={{ mb: 1 }}>
244+
Budget Overview
245+
</Typography>
246+
<Box sx={{ mb: 2 }}>
247+
<Box sx={{ display: 'flex', justifyContent: 'space-between', mb: 1 }}>
248+
<Typography variant="body2" color="textSecondary">
249+
Spent: ${budgetInfo.totalSpent.toFixed(2)}
250+
</Typography>
251+
<Typography variant="body2" color="textSecondary">
252+
Total Budget: ${budgetInfo.totalBudget.toFixed(2)}
253+
</Typography>
254+
</Box>
255+
<LinearProgress
256+
variant="determinate"
257+
value={budgetInfo.budgetUsedPercentage}
258+
sx={{
259+
height: 8,
260+
borderRadius: 5,
261+
backgroundColor: '#444',
262+
'& .MuiLinearProgress-bar': {
263+
borderRadius: 5,
264+
backgroundColor:
265+
budgetInfo.budgetUsedPercentage > 90
266+
? '#f44336'
267+
: budgetInfo.budgetUsedPercentage > 75
268+
? '#ff9800'
269+
: '#4caf50'
270+
}
271+
}}
272+
/>
273+
</Box>
274+
</Grid>
275+
<Grid item xs={12} md={4}>
276+
<Box sx={{ textAlign: { xs: 'left', md: 'right' } }}>
277+
<Typography variant="body2" color="textSecondary" sx={{ mb: 0.5 }}>
278+
Budget Remaining
279+
</Typography>
280+
<Typography
281+
variant="h6"
282+
sx={{
283+
color: budgetInfo.budgetRemaining >= 0 ? '#4caf50' : '#f44336',
284+
fontWeight: 'bold'
285+
}}
286+
>
287+
${budgetInfo.budgetRemaining.toFixed(2)}
288+
</Typography>
289+
<Typography variant="caption" color="textSecondary">
290+
({budgetInfo.budgetUsedPercentage.toFixed(1)}% used)
291+
</Typography>
292+
</Box>
293+
</Grid>
294+
</Grid>
295+
</CardContent>
296+
</Card>
297+
)}
298+
215299
{showFilters && (
216300
<Box sx={{ mb: 3, p: 2, border: '1px solid #444', borderRadius: 1, backgroundColor: '#1a1a1a' }}>
217301
<Typography variant="h6" sx={{ mb: 2 }}>
@@ -306,20 +390,24 @@ const ProjectSpendingHistory: React.FC<ProjectSpendingHistoryProps> = ({ wbsNum
306390
<TableRow>
307391
<TableCell />
308392
<TableCell>Submitter / RR Link</TableCell>
309-
<TableCell>Date</TableCell>
393+
<TableCell>Description</TableCell>
394+
<TableCell>Date Submitted</TableCell>
310395
<TableCell>Status</TableCell>
311396
<TableCell align="right">Total Amount</TableCell>
312397
</TableRow>
313398
</TableHead>
314399
<TableBody>
315400
{filteredData.map(({ request, materials }) => {
401+
const hasMaterials = materials.length > 0;
316402
return (
317403
<React.Fragment key={request.reimbursementRequestId}>
318404
<TableRow hover>
319405
<TableCell>
320-
<IconButton size="small" onClick={() => handleToggleRow(request.reimbursementRequestId)}>
321-
{openRows[request.reimbursementRequestId] ? <KeyboardArrowUpIcon /> : <KeyboardArrowDownIcon />}
322-
</IconButton>
406+
{hasMaterials ? (
407+
<IconButton size="small" onClick={() => handleToggleRow(request.reimbursementRequestId)}>
408+
{openRows[request.reimbursementRequestId] ? <KeyboardArrowUpIcon /> : <KeyboardArrowDownIcon />}
409+
</IconButton>
410+
) : null}
323411
</TableCell>
324412
<TableCell>
325413
<Link
@@ -332,7 +420,23 @@ const ProjectSpendingHistory: React.FC<ProjectSpendingHistoryProps> = ({ wbsNum
332420
'N/A'}
333421
</Link>
334422
</TableCell>
335-
<TableCell>{new Date(request.dateCreated).toLocaleDateString()}</TableCell>
423+
<TableCell>
424+
<Typography variant="body2" sx={{ maxWidth: 300, overflow: 'hidden', textOverflow: 'ellipsis' }}>
425+
{request.accountCode?.name ||
426+
request.reimbursementProducts?.map((p) => p.name).join(', ') ||
427+
request.vendor?.name ||
428+
'No description available'}
429+
</Typography>
430+
</TableCell>
431+
<TableCell>
432+
{request.reimbursementStatuses && request.reimbursementStatuses.length > 0
433+
? new Date(
434+
Math.min(
435+
...request.reimbursementStatuses.map((status) => new Date(status.dateCreated).getTime())
436+
)
437+
).toLocaleDateString()
438+
: ''}
439+
</TableCell>
336440
<TableCell>
337441
<Chip
338442
label={request.reimbursementStatuses?.[0]?.type?.replace(/_/g, ' ') || 'N/A'}
@@ -342,14 +446,14 @@ const ProjectSpendingHistory: React.FC<ProjectSpendingHistoryProps> = ({ wbsNum
342446
</TableCell>
343447
<TableCell align="right">${(request.totalCost / 100)?.toFixed(2) || '0.00'}</TableCell>
344448
</TableRow>
345-
<TableRow>
346-
<TableCell style={{ paddingBottom: 0, paddingTop: 0 }} colSpan={5}>
347-
<Collapse in={openRows[request.reimbursementRequestId]} timeout="auto" unmountOnExit>
348-
<Box sx={{ margin: 1, background: '#181818', borderRadius: 1, p: 2 }}>
349-
<Typography variant="subtitle1" sx={{ color: 'secondary.main', mb: 1 }}>
350-
Line Items
351-
</Typography>
352-
{materials.length > 0 ? (
449+
{hasMaterials && (
450+
<TableRow>
451+
<TableCell style={{ paddingBottom: 0, paddingTop: 0 }} colSpan={6}>
452+
<Collapse in={openRows[request.reimbursementRequestId]} timeout="auto" unmountOnExit>
453+
<Box sx={{ margin: 1, background: '#181818', borderRadius: 1, p: 2 }}>
454+
<Typography variant="subtitle1" sx={{ color: 'secondary.main', mb: 1 }}>
455+
Line Items
456+
</Typography>
353457
<Table size="small">
354458
<TableHead>
355459
<TableRow>
@@ -368,16 +472,11 @@ const ProjectSpendingHistory: React.FC<ProjectSpendingHistoryProps> = ({ wbsNum
368472
))}
369473
</TableBody>
370474
</Table>
371-
) : (
372-
<Typography variant="body2" color="textSecondary">
373-
This reimbursement request has no associated BOM line items. It may have been created
374-
independently or with non-BOM products.
375-
</Typography>
376-
)}
377-
</Box>
378-
</Collapse>
379-
</TableCell>
380-
</TableRow>
475+
</Box>
476+
</Collapse>
477+
</TableCell>
478+
</TableRow>
479+
)}
381480
</React.Fragment>
382481
);
383482
})}

0 commit comments

Comments
 (0)