Changeset befb988 for jobvista-frontend/src/views/applications
- Timestamp:
- 06/17/24 21:59:14 (5 months ago)
- Branches:
- main
- Children:
- 08f82ec
- Parents:
- b248810
- Location:
- jobvista-frontend/src/views/applications
- Files:
-
- 4 edited
- 1 moved
Legend:
- Unmodified
- Added
- Removed
-
jobvista-frontend/src/views/applications/ApplicationDetailsModal.js
rb248810 rbefb988 1 1 import React, {useEffect, useState} from "react"; 2 import "../ job_advertisements/Form.css";2 import "../shared_css/Modal.css"; 3 3 4 4 import 'react-responsive-modal/styles.css'; … … 19 19 20 20 21 export const ViewApplicationDetails= (props) => {21 export const ApplicationDetailsModal = (props) => { 22 22 const {application} = props 23 23 const [modal, setModal] = useState(false); … … 54 54 <div className="col-md-6"> 55 55 <label className="label">Why are you interested in joining our company?</label> 56 <textarea disabled type="text" defaultValue={application.questionAnswers[0]} disabled placeholder="Write your answer here..." className="applic taion-textarea"/>56 <textarea disabled type="text" defaultValue={application.questionAnswers[0]} disabled placeholder="Write your answer here..." className="application-textarea"/> 57 57 58 58 59 59 <label className="label">What makes you a good fit for this position?</label> 60 <textarea disabled type="text" defaultValue={application.questionAnswers[1]} placeholder="Write your answer here..." className="applic taion-textarea"/>60 <textarea disabled type="text" defaultValue={application.questionAnswers[1]} placeholder="Write your answer here..." className="application-textarea"/> 61 61 62 62 63 63 <label className="label">What do you hope to achieve in your first 6 months in this role?</label> 64 <textarea disabled type="text" defaultValue={application.questionAnswers[2]} placeholder="Write your answer here..." className="applic taion-textarea"/>64 <textarea disabled type="text" defaultValue={application.questionAnswers[2]} placeholder="Write your answer here..." className="application-textarea"/> 65 65 66 66 </div> … … 73 73 <br/> 74 74 <label className="label">Message to the recruiter</label> 75 <textarea disabled type="text" defaultValue={application.message} placeholder="Optional..." className="applic taion-textarea"/>75 <textarea disabled type="text" defaultValue={application.message} placeholder="Optional..." className="application-textarea"/> 76 76 77 77 </div> -
jobvista-frontend/src/views/applications/Applications.css
rb248810 rbefb988 1 . applications-container {1 .custom-container { 2 2 width: 65% !important; 3 3 max-width: 1500px !important; … … 6 6 7 7 .application-title { 8 font-family: Ubuntu;9 text-transform: uppercase;10 margin : 25px auto;8 font-family: "Segoe UI"; 9 margin-top: 40px; 10 margin-bottom: 20px; 11 11 display: flex; 12 justify-content: center; 12 justify-content: start; 13 border-bottom: 1px solid #CFCFCF; 13 14 } 14 .application-title h 1{15 font-weight: bold;15 .application-title h3 { 16 font-weight: 500; 16 17 } 17 18 … … 28 29 transition: all 0.3s ease; 29 30 box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); 30 transform: translate(0, 0);31 31 height: auto; 32 32 padding: 20px 20px; 33 33 display: flex; 34 34 justify-content: center; 35 gap: 20px; 35 36 margin: 15px 0; 37 /*z-index: -1000;*/ 38 } 39 .application-card .app-job-seeker-pic { 40 border: 1px solid gray; 41 border-radius: 50% 42 } 43 .application-card .app-job-seeker-pic img { 44 border-radius: 50%; 36 45 } 37 46 38 .application-card .left-box { 39 width: 55%; 40 display: inline-block; 47 .application-card .app-info { 48 width: 60%; 49 display: inline-flex; 50 flex-direction: column; 51 align-items: flex-start; 52 justify-content: center; 53 gap: 10px; 41 54 } 42 55 43 .application-card . left-box.jobAd-title {56 .application-card .app-info .jobAd-title { 44 57 font-weight: 600; 45 58 /*text-transform: uppercase;*/ 46 59 font-family: 'Segoe UI', sans-serif; 47 font-size: 22px;60 /*font-size: 22px;*/ 48 61 } 62 .application-card .app-info .jobAd-title 49 63 50 .application-card . left-box.contact-info {64 .application-card .app-info .contact-info { 51 65 display: inline-flex; 52 gap: 10px;66 gap: 30px; 53 67 } 54 .application-card . left-box.contact-info .contact-item {68 .application-card .app-info .contact-info .contact-item { 55 69 display: inline-flex; 56 70 align-items: center; 57 71 gap: 5px; 72 margin-right: 10px 58 73 } 59 74 60 .application-card .right-box { 61 width: 45%; 75 .application-card .app-info .contact-info .contact-item span { 76 color: darkgray; 77 } 78 79 .application-card .app-status { 80 width: 40%; 62 81 display: inline-flex; 63 82 justify-content: end; … … 66 85 } 67 86 68 .application-card . right-box.select {69 width: 35%;87 .application-card .app-status .select { 88 width: 45%; 70 89 display: inline-block; 71 90 } 91 92 .status { 93 color: white; 94 padding: 5px 10px; 95 border-radius: 5px; 96 width: 150px; 97 text-align: center; 98 } 99 72 100 73 101 .application-button { … … 75 103 border-radius: 8px; 76 104 width: auto; 77 background-color: rgba(207, 235, 255, 1); 78 color: black; 79 font-weight: bold; 80 padding: 5px 10px; 81 transition: 0.2s; 105 background-image: linear-gradient(to bottom, #dddddd, #f0f0f0); 106 background-color: #dddddd; 107 color: #333333; 108 109 font-weight: 700; 110 padding: 8px 13px; 82 111 } 112 83 113 .application-button:hover { 84 background-color: rgb(187, 215, 235); 114 background: linear-gradient(to bottom, #bbbbbb, #dddddd); 115 color: #333333; 85 116 } 86 117 -
jobvista-frontend/src/views/applications/ApplicationsByJobAd.js
rb248810 rbefb988 1 import {useDispatch } from "react-redux";1 import {useDispatch, useSelector} from "react-redux"; 2 2 import {useEffect, useState} from "react"; 3 3 import {useParams} from "react-router"; 4 4 import {ApplicationActions} from "../../redux/actions/applicationActions"; 5 import { ViewApplicationDetails} from "./ViewApplicationDetails";5 import {ApplicationDetailsModal} from "./ApplicationDetailsModal"; 6 6 import "./Applications.css" 7 7 import Select from "react-select"; 8 import {sortElementsBy} from "../../utils/utils"; 9 import {JobSeekerActions} from "../../redux/actions/JobSeekerActions"; 10 import {notifyAppStatusUpdate} from "../../utils/toastUtils"; 8 11 9 12 export const ApplicationsByJobAd = () => { … … 11 14 const dispatch = useDispatch(); 12 15 let {advertisement_id} = useParams(); 16 13 17 const [applicationsByJobAd, setApplicationsByJobAd] = useState([]); 18 let applicationsByJobAdState = useSelector(state => state.appl.applicationsByJobAdId) 19 const [dispatched, setDispatched] = useState(false); 20 21 const [profilePics, setProfilePics] = useState({}); 22 let profilePicsState = useSelector(state => state.images.profilePictures) 23 const [profilePicsDispatched, setProfilePicsDispatched] = useState(false); 24 14 25 const [jobAdTitle, setJobAdTitle] = useState(""); 15 26 16 27 useEffect(() => { 17 dispatch(ApplicationActions.fetchApplicationsByJobAdId(advertisement_id, (success, reponse) => { 18 if (success && reponse.data.length > 0) { 19 setApplicationsByJobAd(reponse.data) 20 setJobAdTitle(reponse.data[0].jobAdTitle) 28 if(!dispatched && (applicationsByJobAdState.length === 0 || applicationsByJobAdState.length === 1)) { 29 dispatch(ApplicationActions.fetchApplicationsByJobAdId(advertisement_id, (success, reponse) => { 30 if (success && reponse.data.length > 0) { 31 setApplicationsByJobAd(sortElementsBy(reponse.data, "submittedOn")) 32 setJobAdTitle(reponse.data[0].jobAdTitle) 33 } 34 setDispatched(true) 35 console.log("Fetch applications by job ad GET") 36 })) 37 } else { 38 setApplicationsByJobAd(sortElementsBy(applicationsByJobAdState, "submittedOn")); 39 if(applicationsByJobAdState.length > 0) { 40 setJobAdTitle(applicationsByJobAdState[0].jobAdTitle) 41 } 42 43 } 44 45 }, [applicationsByJobAdState]) 46 47 useEffect(() => { 48 if(dispatched && !profilePicsDispatched) { 49 applicationsByJobAd.forEach(app => { 50 if(app.jobSeekerId && !profilePics[app.jobSeekerId]) { 51 fetchProfilePic(app.jobSeekerId) 52 } 53 }) 54 setProfilePicsDispatched(true); 55 console.log("Fetch all profile pics GET") 56 } else if(profilePicsDispatched) { 57 setProfilePics(profilePicsState) 58 console.log("Fetch all profile pics STATE") 59 } 60 }, [dispatched]) 61 62 const fetchProfilePic = (jobSeekerId) => { 63 dispatch(JobSeekerActions.downloadProfilePic(jobSeekerId, (success, response) => { 64 if(success) { 65 setProfilePics(prevState => ({...prevState, [jobSeekerId]: response})) 21 66 } 22 67 })) 23 }, []) 68 } 69 24 70 25 71 const options = [ … … 37 83 dispatch(ApplicationActions.updateApplicationStatus(id, selectedOption.value, (success, response) => { 38 84 if(success) { 39 console.log("Status updated.") 85 // console.log("Status updated.") 86 notifyAppStatusUpdate() 40 87 } 41 88 })) … … 43 90 44 91 45 return (<div className=" applications-container">92 return (<div className="custom-container"> 46 93 <div className="application-title"> 47 94 {jobAdTitle ? 48 <h 1>Applications for <span>{jobAdTitle}</span></h1> :95 <h3>Applications for <b>{jobAdTitle}</b></h3> : 49 96 <h1></h1> 50 97 } 51 52 98 </div> 53 99 54 100 {applicationsByJobAd && applicationsByJobAd.map((application, index) => ( 55 101 <div className="application-card"> 56 <div className="left-box"> 102 <div className="app-job-seeker-pic"> 103 <img 104 // loading gif 105 src={profilePicsState[application.jobSeekerId]} 106 alt="" 107 width={75} height={75} 108 /> 109 </div> 110 <div className="app-info"> 57 111 <span>Submitted on <b>{new Date(application.submittedOn).toLocaleString('default', { 58 112 day: 'numeric', … … 60 114 year: 'numeric' 61 115 })}</b></span> 62 <br/><br/>63 116 <div className="contact-info"> 64 117 <div className="contact-item"> 65 118 <i className="fa-solid fa-user"></i> <span>{application.jobSeekerName}</span> 66 </div> 67 <div className="contact-item"> 119 </div> <div className="contact-item"> 68 120 <i className="fa-solid fa-envelope"></i> <span>{application.jobSeekerEmail}</span> 69 </div> 70 <div className="contact-item"> 121 </div> <div className="contact-item"> 71 122 <i className="fa-solid fa-phone"></i> <span>{application.jobSeekerPhoneNumber}</span> 72 123 </div> … … 74 125 </div> 75 126 76 <div className=" right-box">77 < ViewApplicationDetailsapplication={application}/>127 <div className="app-status"> 128 <ApplicationDetailsModal application={application}/> 78 129 <div className="select"> 79 130 <Select options={options} onChange={(selectedOption) => handleChangeStatus(selectedOption, application.id)} defaultValue={handleDefaultStatus(application.status)}/> -
jobvista-frontend/src/views/applications/ApplicationsByJobSeeker.js
rb248810 rbefb988 2 2 import {useEffect, useState} from "react"; 3 3 import {ApplicationActions} from "../../redux/actions/applicationActions"; 4 import { ViewApplicationDetails} from "./ViewApplicationDetails";4 import {ApplicationDetailsModal} from "./ApplicationDetailsModal"; 5 5 import Select from "react-select"; 6 7 import {RecruiterActions} from "../../redux/actions/recruiterActions"; 8 import {sortElementsBy} from "../../utils/utils"; 6 9 7 10 export const ApplicationsByJobSeeker = () => { 8 11 const dispatch = useDispatch(); 12 const auth = useSelector(state => state.auth.currentUser); 13 9 14 const [applicationsByJobSeeker, setApplicationsByJobSeeker] = useState([]); 10 15 let applicationsByJobSeekerState = useSelector(state => state.appl.applicationsByJobSeeker); 11 16 const [dispatched, setDispatched] = useState(false); 12 17 18 const [logos, setLogos] = useState({}); 19 let logosState = useSelector(state => state.images.logos) 20 const [logoDispatched, setLogoDispatched] = useState(false); 21 22 23 13 24 useEffect(() => { 14 if(!dispatched && applicationsByJobSeekerState.length === 0) {15 dispatch(ApplicationActions.fetchApplicationsByJobSeeker( (success, response) => {25 if(!dispatched && (applicationsByJobSeekerState.length === 0 || applicationsByJobSeekerState.length === 1) ) { 26 dispatch(ApplicationActions.fetchApplicationsByJobSeeker(auth.id, (success, response) => { 16 27 if(success && response.data.length > 0) { 17 setApplicationsByJobSeeker( response.data);28 setApplicationsByJobSeeker(sortElementsBy(response.data, "submittedOn")); 18 29 } 30 setDispatched(true) 19 31 console.log("Fetch applications by job seeker GET") 20 32 })) 21 setDispatched(true) 33 22 34 } else { 23 setApplicationsByJobSeeker( applicationsByJobSeekerState);35 setApplicationsByJobSeeker(sortElementsBy(applicationsByJobSeekerState, "submittedOn")); 24 36 console.log("Fetch applications by job seeker STATE") 25 37 } 26 38 }, [applicationsByJobSeekerState]) 27 39 40 useEffect(() => { 41 42 if(dispatched && !logoDispatched) { 43 applicationsByJobSeeker.forEach(jobAd => { 44 if(jobAd.recruiterId && !logos[jobAd.recruiterId]) { 45 fetchLogo(jobAd.recruiterId); 46 } 47 }) 48 setLogoDispatched(true) 49 console.log("Fetch all logos GET") 50 } else if (logoDispatched){ 51 setLogos(logosState) 52 console.log("Fetch all logos STATE") 53 54 } 55 56 }, [dispatched, logosState]) 57 58 59 60 const fetchLogo = (recruiterId) => { 61 dispatch(RecruiterActions.downloadLogo(recruiterId, (success, response) => { 62 if(success) { 63 setLogos(prevLogos => ({...prevLogos, [recruiterId]: response})) 64 } 65 })); 66 }; 67 28 68 const options = [ 29 {value: 'PROPOSED', label: <span ><i className="fa-solid fa-paper-plane"></i> Proposed</span>},30 {value: 'UNDER_REVIEW', label: <span ><i className="fa-solid fa-file-pen"></i> Under Review</span>},31 {value: 'ACCEPTED', label: <span ><i className="fa-solid fa-user-check"></i> Accepted</span>},32 {value: 'DENIED', label: <span ><i className="fa-solid fa-user-slash"></i> Denied</span>}69 {value: 'PROPOSED', label: <span className="status" style={{backgroundColor: '#4A90E2'}}><i className="fa-solid fa-paper-plane"></i> Proposed</span>}, 70 {value: 'UNDER_REVIEW', label: <span className="status" style={{backgroundColor: '#F5A623'}}><i className="fa-solid fa-file-pen"></i> Under Review</span>}, 71 {value: 'ACCEPTED', label: <span className="status" style={{backgroundColor: '#7ED321'}}><i className="fa-solid fa-user-check"></i> Accepted</span>}, 72 {value: 'DENIED', label: <span className="status" style={{backgroundColor: '#D0021B'}}><i className="fa-solid fa-user-slash"></i> Denied</span>} 33 73 ]; 34 74 … … 40 80 41 81 return ( 42 <div className="applications-container"> 82 <div className="custom-container"> 83 43 84 <div className="application-title"> 44 85 <h3>Application history</h3> 45 86 </div> 46 87 {applicationsByJobSeeker && applicationsByJobSeeker.map((application, index) => ( 47 88 <div key={index} className="application-card"> 48 <div className="left-box"> 49 <span className="jobAd-title">{application.jobAdTitle}</span> 50 <br/> 51 <br/> 89 <div className="app-company-logo"> 90 <img 91 // loading gif 92 src={logosState[application.recruiterId]} 93 alt="" 94 width={75} height={75} 95 /> 96 </div> 97 98 <div className="app-info"> 99 <div className="jobAd-title">{application.jobAdTitle}</div> 52 100 <div className="contact-info"> 53 101 <div className="contact-item"> … … 68 116 </div> 69 117 70 <div className="right-box"> 71 <ViewApplicationDetails application={application}/> 72 <div className="select"> 73 <Select isDisabled={true} options={options} defaultValue={handleDefaultValue(application.status)}/> 74 </div> 118 <div className="app-status"> 119 <ApplicationDetailsModal application={application}/> 120 <> {handleDefaultValue(application.status).label}</> 121 {/*<div className="select">*/} 122 {/* <Select isDisabled={true} options={options} />*/} 123 {/*</div>*/} 75 124 76 125 </div> -
jobvista-frontend/src/views/applications/ApplyToJobAdModal.js
rb248810 rbefb988 1 1 import React, {useState} from "react"; 2 import "../ job_advertisements/Form.css";2 import "../shared_css/Modal.css"; 3 3 4 4 import 'react-responsive-modal/styles.css'; 5 5 import {Modal} from 'react-responsive-modal'; 6 import Select from "react-select";7 6 8 7 //Validation … … 12 11 13 12 14 import {employmentStatusOptions, industryOptions, jobTypeOptions} from "../selectOptions";15 13 import {useDispatch, useSelector} from "react-redux"; 16 import {JobAdvertisementActions} from "../../redux/actions/jobAdvertisementActions";17 14 import Roles from "../../enumerations/Roles"; 18 15 import {ApplicationActions} from "../../redux/actions/applicationActions"; 16 import {notifyJobAdApply} from "../../utils/toastUtils"; 19 17 20 18 … … 55 53 formData,(success, response) => { 56 54 if(success) { 57 console.log("Job Advertisement added")58 55 toggleModal() 56 notifyJobAdApply() 59 57 } 60 58 } … … 70 68 {role===Roles.JOBSEEKER && 71 69 <> 72 {jobAd.active && <button onClick={toggleModal} className=" card-button apply">Apply now</button> }70 {jobAd.active && <button onClick={toggleModal} className="apply-button apply">Apply now</button> } 73 71 {!jobAd.active && <button className="card-button apply disabled">Apply now</button> } 74 72 </> … … 86 84 <label className="label">Why are you interested in joining our company?</label> 87 85 <textarea type="text" placeholder="Write your answer here..." 88 {...register("answerOne")} className="applic taion-textarea"/>86 {...register("answerOne")} className="application-textarea"/> 89 87 <p style={{color: "red"}}>{errors.answerOne?.message}</p> 90 88 91 89 <label className="label">What makes you a good fit for this position?</label> 92 90 <textarea type="text" placeholder="Write your answer here..." 93 {...register("answerTwo")} className="applic taion-textarea"/>91 {...register("answerTwo")} className="application-textarea"/> 94 92 <p style={{color: "red"}}>{errors.answerTwo?.message}</p> 95 93 96 94 <label className="label">What do you hope to achieve in your first 6 months in this role?</label> 97 95 <textarea type="text" placeholder="Write your answer here..." 98 {...register("answerThree")} className="applic taion-textarea"/>96 {...register("answerThree")} className="application-textarea"/> 99 97 <p style={{color: "red"}}>{errors.answerThree?.message}</p> 100 98 … … 103 101 <label htmlFor="start">Curriculum vitae (CV)</label> 104 102 <br/> 105 <input {...register("file")} onChange={(e) => setResumeFile(e.target.files[0])} required type="file" id="fileUpload" accept=".pdf"/>103 <input {...register("file")} className="resume-link" onChange={(e) => setResumeFile(e.target.files[0])} required type="file" id="fileUpload" accept=".pdf"/> 106 104 107 105 <br/> 108 106 <label className="label">Message to the recruiter</label> 109 107 <textarea type="text" placeholder="Optional..." 110 {...register("messageToRecruiter")} className="applic taion-textarea"/>108 {...register("messageToRecruiter")} className="application-textarea"/> 111 109 112 110 <br/><br/> … … 115 113 </div> 116 114 117 <div className="aligned"> 115 <div className="modal-buttons"> 116 <div className="cancel-btn" onClick={toggleModal}> Cancel</div> 118 117 <button className="submit-btn"> Submit</button> 119 118 </div>
Note:
See TracChangeset
for help on using the changeset viewer.