Skip to content

Commit a646f11

Browse files
committed
progress
1 parent 590b752 commit a646f11

2 files changed

Lines changed: 129 additions & 0 deletions

File tree

src/frontend/src/pages/ProjectDetailPage/ProjectViewContainer/ProjectDetails.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import LoadingIndicator from '../../../components/LoadingIndicator';
2020
import PieChart from '../../FinancePage/FinanceComponents/PieChart';
2121
import WarningBanner from '../../../components/WarningBanner';
2222
import { Box } from '@mui/system';
23+
import ProjectSpendingHistory from '../../ProjectPage/ProjectSpendingHistory';
2324

2425
export const getProjectTeamsName = (project: ProjectPreview): string => {
2526
return project.teams.map((team) => team.teamName).join(', ');
@@ -136,6 +137,7 @@ const ProjectDetails: React.FC<ProjectDetailsProps> = ({ project }) => {
136137
reimbursed={rrData.reimbursed}
137138
available={rrData.available}
138139
/>
140+
<ProjectSpendingHistory wbsNum={project.wbsNum} />
139141
</Grid>
140142
)}
141143
<Grid item {...summaryGridSize} sx={{ mb: emptyRRData ? 2 : 7 }}>
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
import React from 'react';
2+
import {
3+
Box,
4+
Typography,
5+
Table,
6+
TableBody,
7+
TableCell,
8+
TableContainer,
9+
TableHead,
10+
TableRow,
11+
Paper,
12+
Chip,
13+
Collapse,
14+
IconButton
15+
} from '@mui/material';
16+
import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown';
17+
import KeyboardArrowUpIcon from '@mui/icons-material/KeyboardArrowUp';
18+
import { useGetMaterialsForWbsElement } from '../../hooks/bom.hooks';
19+
import { Material, WbsNumber } from 'shared';
20+
21+
interface ProjectSpendingHistoryProps {
22+
wbsNum: WbsNumber;
23+
}
24+
25+
const ProjectSpendingHistory: React.FC<ProjectSpendingHistoryProps> = ({ wbsNum }) => {
26+
const { data: materials, isLoading, isError } = useGetMaterialsForWbsElement(wbsNum);
27+
const [openRows, setOpenRows] = React.useState<Record<string, boolean>>({});
28+
29+
// Group materials by reimbursementRequestId
30+
const grouped = React.useMemo(() => {
31+
if (!materials) return [];
32+
const map: Record<string, { request: any; materials: Material[] }> = {};
33+
materials.forEach((mat) => {
34+
const rr = mat.reimbursementRequest;
35+
if (rr) {
36+
if (!map[rr.reimbursementRequestId]) {
37+
map[rr.reimbursementRequestId] = { request: rr, materials: [] };
38+
}
39+
map[rr.reimbursementRequestId].materials.push(mat);
40+
}
41+
});
42+
return Object.values(map);
43+
}, [materials]);
44+
45+
if (isLoading) return <Typography>Loading spending history...</Typography>;
46+
if (isError) return <Typography color="error">Failed to load spending history.</Typography>;
47+
if (!grouped.length) return <Typography>No spending history for this project.</Typography>;
48+
49+
const handleToggleRow = (id: string) => {
50+
setOpenRows((prev) => ({ ...prev, [id]: !prev[id] }));
51+
};
52+
53+
return (
54+
<Box sx={{ mt: 4 }}>
55+
<Typography variant="h5" sx={{ mb: 2, color: 'primary.main', fontWeight: 'bold' }}>
56+
Spending History
57+
</Typography>
58+
<TableContainer component={Paper} sx={{ background: '#232323', borderRadius: 2 }}>
59+
<Table size="small">
60+
<TableHead>
61+
<TableRow>
62+
<TableCell />
63+
<TableCell>Submitter</TableCell>
64+
<TableCell>Date</TableCell>
65+
<TableCell>Status</TableCell>
66+
<TableCell align="right">Total Amount</TableCell>
67+
</TableRow>
68+
</TableHead>
69+
<TableBody>
70+
{grouped.map(({ request, materials }) => (
71+
<React.Fragment key={request.reimbursementRequestId}>
72+
<TableRow hover>
73+
<TableCell>
74+
<IconButton size="small" onClick={() => handleToggleRow(request.reimbursementRequestId)}>
75+
{openRows[request.reimbursementRequestId] ? <KeyboardArrowUpIcon /> : <KeyboardArrowDownIcon />}
76+
</IconButton>
77+
</TableCell>
78+
<TableCell>{request.recipient?.name || request.recipient?.email || 'N/A'}</TableCell>
79+
<TableCell>{new Date(request.dateCreated).toLocaleDateString()}</TableCell>
80+
<TableCell>
81+
<Chip
82+
label={request.reimbursementStatuses?.[0]?.status || 'N/A'}
83+
color={request.reimbursementStatuses?.[0]?.status === 'REIMBURSED' ? 'success' : 'warning'}
84+
size="small"
85+
/>
86+
</TableCell>
87+
<TableCell align="right">${request.totalCost?.toFixed(2) || '0.00'}</TableCell>
88+
</TableRow>
89+
<TableRow>
90+
<TableCell style={{ paddingBottom: 0, paddingTop: 0 }} colSpan={5}>
91+
<Collapse in={openRows[request.reimbursementRequestId]} timeout="auto" unmountOnExit>
92+
<Box sx={{ margin: 1, background: '#181818', borderRadius: 1, p: 2 }}>
93+
<Typography variant="subtitle1" sx={{ color: 'secondary.main', mb: 1 }}>
94+
Line Items
95+
</Typography>
96+
<Table size="small">
97+
<TableHead>
98+
<TableRow>
99+
<TableCell>Name</TableCell>
100+
<TableCell>Notes</TableCell>
101+
<TableCell align="right">Amount</TableCell>
102+
</TableRow>
103+
</TableHead>
104+
<TableBody>
105+
{materials.map((mat) => (
106+
<TableRow key={mat.materialId}>
107+
<TableCell>{mat.name}</TableCell>
108+
<TableCell>{mat.notes || '-'}</TableCell>
109+
<TableCell align="right">${mat.price?.toFixed(2) || '0.00'}</TableCell>
110+
</TableRow>
111+
))}
112+
</TableBody>
113+
</Table>
114+
</Box>
115+
</Collapse>
116+
</TableCell>
117+
</TableRow>
118+
</React.Fragment>
119+
))}
120+
</TableBody>
121+
</Table>
122+
</TableContainer>
123+
</Box>
124+
);
125+
};
126+
127+
export default ProjectSpendingHistory;

0 commit comments

Comments
 (0)