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" ;
2928import defaultProfile from "../img/defaultProfile.jpg" ;
29+ import GetAppIcon from "@mui/icons-material/GetApp" ;
3030
3131const 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