Skip to content

Commit 34fdc61

Browse files
committed
Added profile_picture and resume - Recruiter
1 parent fa70d7c commit 34fdc61

File tree

3 files changed

+299
-42
lines changed

3 files changed

+299
-42
lines changed

frontend/src/components/RecruiterApplicantsComponents.jsx

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,23 @@ import {
88
TableHead,
99
TableSortLabel,
1010
Toolbar,
11-
Tooltip,
12-
IconButton,
1311
Typography,
1412
OutlinedInput,
1513
InputAdornment,
1614
} from "@mui/material";
1715

1816
// Label Component
1917
const Label = ({ color, children }) => {
18+
const backgroundColors = {
19+
success: "success.main",
20+
error: "error.main",
21+
warning: "warning.main",
22+
info: "info.main",
23+
primary: "primary.main",
24+
secondary: "secondary.main",
25+
offerSent: "#4CAF50",
26+
};
27+
2028
return (
2129
<Box
2230
sx={{
@@ -28,14 +36,18 @@ const Label = ({ color, children }) => {
2836
minHeight: 32,
2937
px: 1,
3038
color: "white",
31-
backgroundColor: color === "success" ? "success.main" : "error.main",
39+
fontSize: "0.55rem",
40+
fontWeight:'bold',
41+
padding:'4px',
42+
backgroundColor: backgroundColors[color] || "error.main",
3243
}}
3344
>
3445
{children}
3546
</Box>
3647
);
3748
};
3849

50+
3951
Label.propTypes = {
4052
color: PropTypes.string.isRequired,
4153
children: PropTypes.node,
@@ -95,7 +107,7 @@ const UserListHead = ({
95107
return (
96108
<TableHead
97109
sx={{
98-
bgcolor: "grey.200", // Add this line to set the background color to grey
110+
bgcolor: "grey.200",
99111
}}
100112
>
101113
<TableRow>
@@ -149,7 +161,7 @@ const UserListToolbar = ({
149161
display: "flex",
150162
justifyContent: "space-between",
151163
padding: 1,
152-
backgroundColor: "background.paper", // Add this line to set the background color to white
164+
backgroundColor: "background.paper",
153165
...(numSelected > 0 && {
154166
color: "primary.main",
155167
bgcolor: "primary.lighter",

frontend/src/pages/RecruiterApplicants.jsx

Lines changed: 154 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import {
77
TableCell,
88
TableRow,
99
Typography,
10-
Link,
1110
Button,
1211
Dialog,
1312
DialogTitle,
@@ -27,6 +26,7 @@ import {
2726
UserListToolbar,
2827
} from "../components/RecruiterApplicantsComponents";
2928
import defaultProfile from "../img/defaultProfile.jpg";
29+
import GetAppIcon from "@mui/icons-material/GetApp";
3030

3131
const RecruiterApplicantsPage = () => {
3232
const [filterName, setFilterName] = useState("");
@@ -35,6 +35,11 @@ const RecruiterApplicantsPage = () => {
3535
const [dialogOpen, setDialogOpen] = useState(false);
3636
const [dialogData, setDialogData] = useState({});
3737

38+
const [resumeURL, setResumeURL] = useState("");
39+
const [open, setOpen] = useState(false);
40+
41+
const [profilePictures, setProfilePictures] = useState({});
42+
3843
useEffect(() => {
3944
fetchJobs();
4045
}, []);
@@ -58,14 +63,15 @@ const RecruiterApplicantsPage = () => {
5863
"Content-Type": "application/json",
5964
Authorization: `Bearer ${localStorage.getItem("access_token")}`,
6065
},
61-
},
66+
}
6267
);
6368

6469
if (response.ok) {
6570
const data = await response.json();
6671
const jobIds = data.map((job) => job.id);
6772
setJobIds(jobIds);
68-
} else {
73+
}
74+
else {
6975
console.error("Failed to fetch jobs for the recruiter");
7076
}
7177
} catch (error) {
@@ -88,16 +94,28 @@ const RecruiterApplicantsPage = () => {
8894
"Content-Type": "application/json",
8995
Authorization: `Bearer ${localStorage.getItem("access_token")}`,
9096
},
91-
},
97+
}
9298
);
9399

94100
if (response.status === 404) {
95101
setApplicants([]);
96102
} else {
97103
const data = await response.json();
98-
// Check if data is an array before setting the state
104+
105+
// had to check if data is an array or not for error checking
99106
if (Array.isArray(data)) {
100-
allApplicants = [...allApplicants, ...data];
107+
const applicantsWithProfilePictures = await Promise.all(
108+
data.map(async (applicant) => {
109+
const profilePictureUrl = await fetchProfilePicture(
110+
applicant.user_profile_id
111+
);
112+
return { ...applicant, profile_picture: profilePictureUrl };
113+
})
114+
);
115+
allApplicants = [
116+
...allApplicants,
117+
...applicantsWithProfilePictures,
118+
];
101119
} else {
102120
setApplicants([]);
103121
console.error("Fetched data is not an array:", data);
@@ -111,15 +129,58 @@ const RecruiterApplicantsPage = () => {
111129
}
112130
};
113131

114-
const downloadResume = (resume, filename) => {
115-
const element = document.createElement("a");
116-
element.href = URL.createObjectURL(
117-
new Blob([resume], { type: "application/pdf" }),
118-
);
119-
element.download = filename;
120-
document.body.appendChild(element);
121-
element.click();
122-
document.body.removeChild(element);
132+
const handleDownloadClick = async (userId) => {
133+
const endpoint = `https://chapi.techstartucalgary.com/users/profile/${userId}/resume`;
134+
135+
try {
136+
const response = await fetch(endpoint, {
137+
method: "GET",
138+
headers: {
139+
"Content-Type": "application/pdf",
140+
Authorization: `Bearer ${localStorage.getItem("access_token")}`,
141+
},
142+
});
143+
144+
if (!response.ok) {
145+
throw new Error("Failed to fetch resume");
146+
}
147+
148+
const blob = await response.blob();
149+
const objectURL = URL.createObjectURL(blob);
150+
setResumeURL(objectURL);
151+
setOpen(true);
152+
} catch (error) {
153+
console.error(error);
154+
}
155+
};
156+
157+
const handleClose = () => {
158+
setOpen(false);
159+
};
160+
161+
const fetchProfilePicture = async (userId) => {
162+
try {
163+
const response = await fetch(
164+
`https://chapi.techstartucalgary.com/users/profile/${userId}/profile_picture`,
165+
{
166+
method: "GET",
167+
headers: {
168+
Authorization: `Bearer ${localStorage.getItem("access_token")}`,
169+
},
170+
}
171+
);
172+
173+
if (response.ok) {
174+
const data = await response.blob();
175+
return URL.createObjectURL(data);
176+
} else {
177+
console.error("Failed to fetch profile picture");
178+
return defaultProfile;
179+
}
180+
} catch (error) {
181+
console.error(error);
182+
return defaultProfile;
183+
}
123184
};
124185

125186
const handleFilterByName = (event) => {
@@ -129,7 +190,7 @@ const RecruiterApplicantsPage = () => {
129190
const filteredApplicants = applicants.filter((applicant) =>
130191
`${applicant.applicant.first_name} ${applicant.applicant.last_name}`
131192
.toLowerCase()
132-
.includes(filterName.toLowerCase()),
193+
.includes(filterName.toLowerCase())
133194
);
134195

135196
const handleDialogOpen = (row) => {
@@ -156,7 +217,7 @@ const RecruiterApplicantsPage = () => {
156217
new_status: dialogData.application_status.status,
157218
rejection_feedback: dialogData.application_status.feedback,
158219
}),
159-
},
220+
}
160221
);
161222

162223
if (response.ok) {
@@ -170,6 +231,23 @@ const RecruiterApplicantsPage = () => {
170231
}
171232
};
172233

234+
const getStatusColor = (status) => {
235+
switch (status) {
236+
case "SUBMITTED":
237+
return "success";
238+
case "UNDER REVIEW":
239+
return "info";
240+
case "UNDERGOING FURTHER SCREENING":
241+
return "warning";
242+
case "REJECTED":
243+
return "error";
244+
case "OFFER SENT":
245+
return "offerSent";
246+
default:
247+
return "secondary";
248+
}
249+
};
250+
173251
return (
174252
<Container maxWidth="lg">
175253
<Box
@@ -208,22 +286,18 @@ const RecruiterApplicantsPage = () => {
208286
<TableRow key={row.user_profile_id} tabIndex={-1}>
209287
<TableCell component="th" scope="row" padding="none">
210288
<img
211-
src={
212-
row.applicant.profile_picture
213-
? `data:image/jpeg;base64,${row.applicant.profile_picture}`
214-
: defaultProfile
215-
}
289+
src={row.profile_picture || defaultProfile}
216290
alt={""}
217291
style={{
218292
borderRadius: "50%",
219-
width: "40px",
220-
height: "40px",
293+
width: "35px",
294+
height: "35px",
221295
marginRight: "8px",
296+
marginLeft: "8px",
222297
verticalAlign: "middle",
223298
}}
224299
/>
225300
</TableCell>
226-
227301
<TableCell component="th" scope="row" padding="none">
228302
<Typography
229303
variant="body1"
@@ -236,6 +310,7 @@ const RecruiterApplicantsPage = () => {
236310
overflow: "hidden",
237311
textOverflow: "ellipsis",
238312
whiteSpace: "nowrap",
313+
marginLeft: "8px",
239314
}}
240315
>
241316
{`${row.applicant.first_name} ${row.applicant.last_name}`}
@@ -244,30 +319,45 @@ const RecruiterApplicantsPage = () => {
244319

245320
<TableCell>{row.job.title}</TableCell>
246321
<TableCell>
247-
<Label
322+
{/* <Label
248323
color={
249324
row.application_status.status === "SUBMITTED"
250325
? "success"
251326
: "error"
252327
}
253328
>
254329
{row.application_status.status}
330+
</Label> */}
331+
<Label
332+
color={getStatusColor(row.application_status.status)}
333+
>
334+
{row.application_status.status}
255335
</Label>
256336
</TableCell>
337+
257338
<TableCell>
258-
<Link
259-
href="#"
260-
onClick={(e) => {
261-
e.preventDefault();
262-
downloadResume(
263-
row.applicant.resume,
264-
`${row.applicant.first_name}_${row.applicant.last_name}_Resume.pdf`,
265-
);
339+
<Button
340+
variant="outlined"
341+
onClick={() => handleDownloadClick(row.user_profile_id)}
342+
startIcon={<GetAppIcon />}
343+
sx={{
344+
whiteSpace: "nowrap",
345+
overflow: "hidden",
346+
textOverflow: "ellipsis",
347+
bgcolor: "primary.main",
348+
color: "white",
349+
fontSize: "14px",
350+
marginLeft: "6px",
351+
marginRight: "0px",
352+
fontWeight: "bold",
353+
"&:hover": {
354+
bgcolor: "primary.dark",
355+
},
266356
}}
267-
>
268-
{`${row.applicant.first_name}_${row.applicant.last_name}_Resume.pdf`}
269-
</Link>
357+
></Button>
270358
</TableCell>
359+
360+
{/* </TableCell> */}
271361
<TableCell>
272362
<Button
273363
variant="outlined"
@@ -276,6 +366,17 @@ const RecruiterApplicantsPage = () => {
276366
whiteSpace: "nowrap",
277367
overflow: "hidden",
278368
textOverflow: "ellipsis",
369+
border: "2px solid grey",
370+
borderRadius: "4px",
371+
color: "grey",
372+
fontWeight: "bold",
373+
padding: "6px 12px",
374+
transition: "background-color 0.3s",
375+
"&:hover": {
376+
// backgroundColor: "#4CAF50",
377+
backgroundColor: "primary.dark",
378+
color: "#fff",
379+
},
279380
}}
280381
>
281382
Update Status
@@ -306,6 +407,22 @@ const RecruiterApplicantsPage = () => {
306407
)}
307408
</Scrollbar>
308409
</Box>
410+
411+
<Dialog open={open} onClose={handleClose} maxWidth="md" fullWidth>
412+
<DialogTitle>Resume</DialogTitle>
413+
<DialogContent>
414+
<iframe
415+
src={resumeURL}
416+
title="Resume"
417+
width="100%"
418+
height="600px"
419+
></iframe>
420+
</DialogContent>
421+
<DialogActions>
422+
<Button onClick={handleClose}>Close</Button>
423+
</DialogActions>
424+
</Dialog>
425+
309426
<Dialog
310427
open={dialogOpen}
311428
onClose={handleDialogClose}

0 commit comments

Comments
 (0)