Changeset 28b3398 for jobvista-frontend
- Timestamp:
- 06/09/24 14:24:03 (5 months ago)
- Branches:
- main
- Children:
- b248810
- Parents:
- 19398ad
- Location:
- jobvista-frontend
- Files:
-
- 7 added
- 20 edited
Legend:
- Unmodified
- Added
- Removed
-
jobvista-frontend/package-lock.json
r19398ad r28b3398 19 19 "axios": "^1.6.8", 20 20 "formik": "^2.4.6", 21 "primereact": "^10.6.6", 22 "quill": "^2.0.2", 21 23 "react": "^18.3.1", 22 24 "react-dom": "^18.3.1", … … 8930 8932 "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" 8931 8933 }, 8934 "node_modules/fast-diff": { 8935 "version": "1.3.0", 8936 "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", 8937 "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==" 8938 }, 8932 8939 "node_modules/fast-glob": { 8933 8940 "version": "3.3.2", … … 13088 13095 "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==" 13089 13096 }, 13097 "node_modules/lodash.clonedeep": { 13098 "version": "4.5.0", 13099 "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", 13100 "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==" 13101 }, 13090 13102 "node_modules/lodash.debounce": { 13091 13103 "version": "4.0.8", 13092 13104 "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", 13093 13105 "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==" 13106 }, 13107 "node_modules/lodash.isequal": { 13108 "version": "4.5.0", 13109 "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", 13110 "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==" 13094 13111 }, 13095 13112 "node_modules/lodash.memoize": { … … 13848 13865 "tslib": "^2.0.3" 13849 13866 } 13867 }, 13868 "node_modules/parchment": { 13869 "version": "3.0.0", 13870 "resolved": "https://registry.npmjs.org/parchment/-/parchment-3.0.0.tgz", 13871 "integrity": "sha512-HUrJFQ/StvgmXRcQ1ftY6VEZUq3jA2t9ncFN4F84J/vN0/FPpQF+8FKXb3l6fLces6q0uOHj6NJn+2xvZnxO6A==" 13850 13872 }, 13851 13873 "node_modules/parent-module": { … … 15324 15346 } 15325 15347 }, 15348 "node_modules/primereact": { 15349 "version": "10.6.6", 15350 "resolved": "https://registry.npmjs.org/primereact/-/primereact-10.6.6.tgz", 15351 "integrity": "sha512-+C0Bt6vS/jh09DQVS4UXpVctbvqJDUC3t3mVdGmhmIINYD8kdfL3fvc3bUGniGxkKKzwkSYdAQXhZlcgj8LUgw==", 15352 "dependencies": { 15353 "@types/react-transition-group": "^4.4.1", 15354 "react-transition-group": "^4.4.1" 15355 }, 15356 "engines": { 15357 "node": ">=14.0.0" 15358 }, 15359 "peerDependencies": { 15360 "@types/react": "^17.0.0 || ^18.0.0", 15361 "react": "^17.0.0 || ^18.0.0", 15362 "react-dom": "^17.0.0 || ^18.0.0" 15363 }, 15364 "peerDependenciesMeta": { 15365 "@types/react": { 15366 "optional": true 15367 } 15368 } 15369 }, 15326 15370 "node_modules/process-nextick-args": { 15327 15371 "version": "2.0.1", … … 15453 15497 } 15454 15498 ] 15499 }, 15500 "node_modules/quill": { 15501 "version": "2.0.2", 15502 "resolved": "https://registry.npmjs.org/quill/-/quill-2.0.2.tgz", 15503 "integrity": "sha512-QfazNrhMakEdRG57IoYFwffUIr04LWJxbS/ZkidRFXYCQt63c1gK6Z7IHUXMx/Vh25WgPBU42oBaNzQ0K1R/xw==", 15504 "dependencies": { 15505 "eventemitter3": "^5.0.1", 15506 "lodash-es": "^4.17.21", 15507 "parchment": "^3.0.0", 15508 "quill-delta": "^5.1.0" 15509 }, 15510 "engines": { 15511 "npm": ">=8.2.3" 15512 } 15513 }, 15514 "node_modules/quill-delta": { 15515 "version": "5.1.0", 15516 "resolved": "https://registry.npmjs.org/quill-delta/-/quill-delta-5.1.0.tgz", 15517 "integrity": "sha512-X74oCeRI4/p0ucjb5Ma8adTXd9Scumz367kkMK5V/IatcX6A0vlgLgKbzXWy5nZmCGeNJm2oQX0d2Eqj+ZIlCA==", 15518 "dependencies": { 15519 "fast-diff": "^1.3.0", 15520 "lodash.clonedeep": "^4.5.0", 15521 "lodash.isequal": "^4.5.0" 15522 }, 15523 "engines": { 15524 "node": ">= 12.0.0" 15525 } 15526 }, 15527 "node_modules/quill/node_modules/eventemitter3": { 15528 "version": "5.0.1", 15529 "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", 15530 "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==" 15455 15531 }, 15456 15532 "node_modules/raf": { -
jobvista-frontend/package.json
r19398ad r28b3398 14 14 "axios": "^1.6.8", 15 15 "formik": "^2.4.6", 16 "primereact": "^10.6.6", 17 "quill": "^2.0.2", 16 18 "react": "^18.3.1", 17 19 "react-dom": "^18.3.1", -
jobvista-frontend/src/App.css
r19398ad r28b3398 4 4 .App { 5 5 background-color: rgb(243, 242, 241); 6 //background-color: #EBF2FC; 6 7 height: 100vh; 7 8 overflow-y: auto; … … 40 41 41 42 .react-responsive-modal-overlay { 42 backdrop-filter: blur( 2px);43 backdrop-filter: blur(3px); 43 44 } 44 45 … … 47 48 .col { 48 49 height: 280px !important; 50 } 51 52 .add-new-card { 53 border-radius: 8px; 54 background-color: transparent; 55 border: 3px dashed grey; 56 transition: all 0.3s ease; 57 box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); 58 transform: translate(0, 0); 59 height: 260px; 60 width: 100%; 61 color: grey; 62 font-family: Ubuntu; 63 text-transform: uppercase; 64 } 65 .add-new-card h3 { 66 font-size: 25px; 67 } 68 .add-new-card:hover { 69 /*transform: translate(0, 8px);*/ 70 /*box-shadow: 0 10px 20px rgba(0, 0, 0, 0.2);*/ 71 color: black; 72 border: 3px dashed black; 49 73 } 50 74 -
jobvista-frontend/src/auth/RoutesConfig.js
r19398ad r28b3398 7 7 import {JobAdvertisements} from "../views/job_advertisements/JobAdvertisements"; 8 8 import {JobAdDetails} from "../views/job_advertisements/JobAdDetails"; 9 import {ApplicationsByJobAd} from "../views/applications/ApplicationsByJobAd"; 10 import {ApplicationsByJobSeeker} from "../views/applications/ApplicationsByJobSeeker"; 9 11 export const RoutesConfig = () => { 10 12 … … 16 18 <Route path="/signup/job-seeker" element={<SignUpJobSeekerForm/>}></Route> 17 19 <Route path="/my-job-advertisements" element={<JobAdvertisements/>}></Route> 18 <Route path="/job-advertisements/view/:id" element={<JobAdDetails/>}></Route> 19 <Route path="/my-job-advertisements/view/:id" element={<JobAdDetails/>}></Route> 20 <Route path="/my-applications" element={<ApplicationsByJobSeeker/>}></Route> 21 <Route path="/job-advertisements/:id" element={<JobAdDetails/>}></Route> 22 <Route path="/my-job-advertisements/:advertisement_id/applications" element={<ApplicationsByJobAd/>}></Route> 20 23 </Routes> 21 24 ) -
jobvista-frontend/src/redux/actionTypes.js
r19398ad r28b3398 9 9 export const DELETE_JOB_ADVERTISEMENT = "DELETE_JOB_ADVERTISEMENT" 10 10 export const FETCH_JOB_ADVERTISEMENTS = "FETCH_JOB_ADVERTISEMENTS" 11 export const FILTER_JOB_ADVERTISEMENTS = "FILTER_JOB_ADVERTISEMENTS" 11 12 export const FETCH_JOB_ADVERTISEMENTS_BY_RECRUITER = "FETCH_JOB_ADVERTISEMENTS_BY_RECRUITER" 13 export const FILTER_JOB_ADVERTISEMENTS_BY_RECRUITER = "FILTER_JOB_ADVERTISEMENTS_BY_RECRUITER" 14 export const SUBMIT_APPLICATION = "SUBMIT_APPLICATION" 15 export const UPDATE_APPLICATION_STATUS = "UPDATE_APPLICATION_STATUS" 16 export const FETCH_APPLICATIONS_BY_JOB_ID = "FETCH_APPLICATIONS_BY_JOB_ID" 17 export const FETCH_APPLICATIONS_BY_JOB_SEEKER_ID = "FETCH_APPLICATIONS_BY_JOB_SEEKER_ID" 18 export const DOWNLOAD_RESUME = "DOWNLOAD_RESUME" 19 20 -
jobvista-frontend/src/redux/actions/jobAdvertisementActions.js
r19398ad r28b3398 4 4 CURRENT_USER, DELETE_JOB_ADVERTISEMENT, EDIT_JOB_ADVERTISEMENT, 5 5 FETCH_JOB_ADVERTISEMENTS, 6 FETCH_JOB_ADVERTISEMENTS_BY_RECRUITER 6 FETCH_JOB_ADVERTISEMENTS_BY_RECRUITER, FILTER_JOB_ADVERTISEMENTS 7 7 } from "../actionTypes"; 8 8 … … 10 10 addJobAdvertisement: (jobAdvertisement, callback) => { 11 11 return dispatch => { 12 axios.post("/job-advertisements/add", jobAdvertisement) 12 axios.post("/job-advertisements/add", jobAdvertisement, { 13 headers: { 14 'Content-Type': 'application/json' 15 }, 16 }) 13 17 .then(response => { 14 18 dispatch({ … … 52 56 }, 53 57 fetchJobAdvertisementById: (id, callback) => { 54 axios.get("/job-advertisements/ view/" + id)58 axios.get("/job-advertisements/" + id) 55 59 .then(response => { 56 60 callback(true, response) 57 61 }).catch(error => { 58 59 62 callback(false, error) 63 }) 60 64 }, 61 65 … … 74 78 } 75 79 }, 80 filterJobAdvertisements: (filter, callback) => { 81 axios.post("/job-advertisements/filtered", filter) 82 .then(response => { 83 callback(true, response) 84 }).catch((error) => { 85 callback(false, error) }) 76 86 77 fetchJobAdvertisementsByRecruiter: (id, callback) => { 87 }, 88 89 fetchJobAdvertisementsByRecruiter: (callback) => { 78 90 return dispatch => { 79 91 let currentUser = JSON.parse(localStorage.getItem(CURRENT_USER)); … … 86 98 callback(true, response) 87 99 }).catch((error) => { 88 console.log("ERROR")89 100 callback(false, error) 90 101 }) 91 102 } 103 }, 104 105 filterJobAdvertisementsByRecruiter: (filter, callback) => { 106 107 let currentUser = JSON.parse(localStorage.getItem(CURRENT_USER)); 108 axios.post("/job-advertisements/recruiter/" + currentUser.id + "/filtered", filter) 109 .then(response => { 110 callback(true, response) 111 }).catch((error) => { 112 callback(false, error) 113 }) 114 92 115 }, 93 116 -
jobvista-frontend/src/redux/reducers/jobAdvertisementReducer.js
r19398ad r28b3398 3 3 CURRENT_USER, DELETE_JOB_ADVERTISEMENT, EDIT_JOB_ADVERTISEMENT, 4 4 FETCH_JOB_ADVERTISEMENTS, 5 FETCH_JOB_ADVERTISEMENTS_BY_RECRUITER 5 FETCH_JOB_ADVERTISEMENTS_BY_RECRUITER, FILTER_JOB_ADVERTISEMENTS, FILTER_JOB_ADVERTISEMENTS_BY_RECRUITER 6 6 } from "../actionTypes"; 7 7 import {sortElementsByDateCreated} from "../../utils/utils"; … … 48 48 } 49 49 50 50 51 case FETCH_JOB_ADVERTISEMENTS_BY_RECRUITER: 51 52 return { -
jobvista-frontend/src/redux/store.js
r19398ad r28b3398 3 3 import authReducer from "./reducers/authReducer"; 4 4 import jobAdReducer from "./reducers/jobAdvertisementReducer"; 5 import applicationReducer from "./reducers/applicationReducer" 5 6 6 7 // const rootReducer = combineReducers({ … … 16 17 reducer: { 17 18 auth: authReducer, 18 jobAd: jobAdReducer 19 jobAd: jobAdReducer, 20 appl: applicationReducer 19 21 }, 20 22 }); -
jobvista-frontend/src/utils/utils.js
r19398ad r28b3398 3 3 export const sortElementsByDateCreated = (array) => { 4 4 return array.slice().sort((a, b) => { 5 return new Date(b.postedAt).getTime() - new Date(a.postedAt).getTime() 5 return new Date(b.postedOn).getTime() - new Date(a.postedOn).getTime() 6 }); 7 } 8 9 export const sortElementsBySubmissionDate = (array) => { 10 return array.slice().sort((a, b) => { 11 return new Date(b.postedOn).getTime() - new Date(a.postedOn).getTime() 6 12 }); 7 13 } -
jobvista-frontend/src/views/dashboard/Dashboard.css
r19398ad r28b3398 11 11 } 12 12 13 14 .head-dashboard-box .head-component { 15 display: inline; 13 .head-dashboard-box .filter-container { 14 display: inline-flex; 15 justify-content: center; 16 gap: 10px; 16 17 } 17 18 … … 19 20 .head-dashboard-box .search-container { 20 21 position: relative; 21 float: left; 22 /*float: left;*/ 23 display: inline-block; 22 24 } 23 25 .head-dashboard-box .search-container .search-input { … … 26 28 padding: 5px 30px; 27 29 border-radius: 10px; 28 margin-right: 15px;30 /*margin-right: 15px;*/ 29 31 } 30 32 31 . search-container .search-input:focus {33 .head-dashboard-box .search-container .search-input:focus { 32 34 outline-color: #6367ef; 33 35 } 34 36 35 . search-container i {37 .head-dashboard-box .search-container i { 36 38 left: 10px; 37 39 position: absolute; … … 39 41 } 40 42 41 . item {42 width: 35%;43 .head-dashboard-box .item { 44 width: 20%; 43 45 display: inline-block; 44 margin-left: 10px;45 float: right;46 /*margin-left: 10px;*/ 47 /*float: left !important;*/ 46 48 } -
jobvista-frontend/src/views/dashboard/Dashboard.js
r19398ad r28b3398 20 20 21 21 const [role, setRole] = useState(""); 22 const [s ortOrder, setSortOrder] = useState("newest");23 const [selected DateRange, setSelectedDateRange] = useState("all");22 const [selectedSortOrder, setSelectedSortOrder] = useState("date_newest"); 23 const [selectedIndustry, setSelectedIndustry] = useState("all"); 24 24 const [searchTerm, setSearchTerm] = useState(""); 25 25 const [dispatched, setDispatched] = useState(false) … … 32 32 33 33 useEffect(() => { 34 if (!dispatched && jobAdvertisementsState.length == 0) {34 if (!dispatched && jobAdvertisementsState.length == 0) { 35 35 dispatch(JobAdvertisementActions.fetchJobAdvertisements((success, response) => { 36 36 if (success && response.data.length > 0) { … … 39 39 setDispatched(true) 40 40 console.log("Fetch all job advertisements GET") 41 console.log(response.data)42 41 })) 43 42 … … 46 45 console.log("Fetch all job advertisements STATE") 47 46 } 48 }, [ ])47 }, [jobAdvertisementsState]) 49 48 49 let filterJobAdvertisements = () => { 50 JobAdvertisementActions.filterJobAdvertisements( 51 { 52 searchTerm: searchTerm, 53 industry: selectedIndustry, 54 sortOrder: selectedSortOrder 55 }, (success, response) => { 56 if (success) { 57 setJobAdvertisements(response.data); 58 } 59 } 60 ) 61 } 50 62 51 63 return ( … … 53 65 <div className="head-dashboard-box"> 54 66 <div className="row"> 55 <div className="col-md- 3">56 <div className="search-container head-component">57 <i className="fa-solid fa-magnifying-glass blue-colored"></i>67 <div className="col-md-12 filter-container"> 68 <div className="search-container"> 69 <i className="fa-solid fa-magnifying-glass search-icon"></i> 58 70 <input 59 71 className="search-input" 60 72 type="text" 61 73 placeholder="Search job advertisement by title..." 62 //value={searchTerm}63 //onChange={event => setSearchTerm(event.target.value)}74 value={searchTerm} 75 onChange={event => setSearchTerm(event.target.value)} 64 76 /> 65 77 </div> 66 </div>67 <div className="col-md-9">68 78 <div className="sort-section item"> 69 79 <Select 70 80 defaultValue={{value: "all", label: "All industries"}} 71 //value={sortOrder.value}72 //onChange ={option => setSortOrder(option.value)}81 value={selectedIndustry.value} 82 onChange={option => setSelectedIndustry(option.value)} 73 83 options={industryOptionsFilter} 74 84 className="sort-range sort" … … 78 88 <Select 79 89 defaultValue={{value: "newest", label: "Date (Newest First)"}} 80 //value={sortOrder.value}81 //onChange ={option => setSortOrder(option.value)}82 options ={sortOptions}90 value={selectedSortOrder.value} 91 onChange={option => setSelectedSortOrder(option.value)} 92 options={sortOptions} 83 93 className="sort-range sort" 84 94 /> 85 95 </div> 86 <div className="date-range-section item"> 87 <Select 88 defaultValue={{value: "all", label: "Lifetime"}} 89 //value={selectedDateRange.value} 90 //onChange={option => setSelectedDateRange(option.value)} 91 options={dataRangeOptions} 92 className="date-range sort" 93 /> 94 </div> 96 <button onClick={filterJobAdvertisements} className="btn-open-modal">Find jobs</button> 95 97 </div> 98 {/*<div className="col-md-3">*/} 99 100 {/*<div className="date-range-section item">*/} 101 {/* <Select*/} 102 {/* defaultValue={{value: "all", label: "Lifetime"}}*/} 103 {/* //value={selectedDateRange.value}*/} 104 {/* //onChange={option => setSelectedDateRange(option.value)}*/} 105 {/* options={dataRangeOptions}*/} 106 {/* className="date-range sort"*/} 107 {/* />*/} 108 {/*</div>*/} 109 {/*</div>*/} 96 110 </div> 97 111 </div> … … 104 118 <div className="card-head"> 105 119 <span className="hourly-salary"><b>${jobAd.startingSalary}/hr</b></span> 106 <span className="job-type"> {jobAd.jobType===JobType.JOB ? "Job" : "Internship"}</span> 120 <span 121 className="job-type"> {jobAd.jobType === JobType.JOB ? "Job" : "Internship"}</span> 107 122 {!jobAd.active && <span className="expired">Expired</span>} 108 123 </div> 109 124 <div className="card-body"> 110 125 <h5 className="card-title">{jobAd.title}</h5> 111 <span>{jobAd.industry} • <span style={{color: "black", fontWeight: "bold"}}>{formatRelativeTime(jobAd.postedOn)}</span></span> 126 <span>{jobAd.industry} • <span style={{ 127 color: "black", 128 fontWeight: "bold" 129 }}>{formatRelativeTime(jobAd.postedOn)}</span></span> 112 130 <div className="card-info"> 113 <span><i className="fa-solid fa-building" style={{color: "#000000"}}></i> Company: <span style={{color: "black", fontWeight: "bold"}}>{jobAd.recruiterName}</span></span> <br/> 131 <span><i className="fa-solid fa-building" 132 style={{color: "#000000"}}></i> Company: <span style={{ 133 color: "black", 134 fontWeight: "bold" 135 }}>{jobAd.recruiterName}</span></span> <br/> 114 136 </div> 115 137 116 138 <div className="aligned"> 117 <Link to={`/job-advertisements/view/${jobAd.id}`} className="card-button">Read more</Link> 118 {role===Roles.JOBSEEKER && 119 <> 120 {jobAd.active && <button className="card-button">Apply now</button> } 121 {!jobAd.active && <button className="card-button disabled">Apply now</button> } 122 </> 123 } 139 <Link to={`/job-advertisements/${jobAd.id}`} className="card-button">Read 140 more</Link> 124 141 </div> 125 142 -
jobvista-frontend/src/views/job_advertisements/AddJobAdModal.js
r19398ad r28b3398 1 1 import React, {useState} from "react"; 2 2 import "./Form.css"; 3 4 import { Editor } from 'primereact/editor'; 3 5 4 6 import 'react-responsive-modal/styles.css'; … … 19 21 export const AddJobAdModal = () => { 20 22 const [modal, setModal] = useState(false); 23 const [text, setText] = useState(""); 21 24 const dispatch = useDispatch(); 22 25 const auth = useSelector(state => state.auth.currentUser) … … 52 55 employmentStatus: values.employmentStatus.value, 53 56 }, (success, response) => { 54 if (success) {57 if (success) { 55 58 console.log("Job Advertisement added") 56 59 toggleModal() … … 66 69 67 70 return (<div className="modal-wrap"> 68 <button onClick={toggleModal} className="btn-open-modal">POST ADVERTISEMENT</button> 69 <Modal open={modal} onClose={toggleModal} center classNames="job-advertisement-modal"> 70 <div className="head-modal"> 71 <h3>Post Job Advertisement</h3> 72 <i className="fa-solid fa-x btn-close-modal" onClick={toggleModal}></i> 73 </div> 71 <div className="col"> 72 <button onClick={toggleModal} className="add-new-card"> 73 <h3>+ Post Advertisement</h3> 74 </button> 75 </div> 76 {/*<button onClick={toggleModal} className="btn-open-modal">POST ADVERTISEMENT</button>*/} 77 <Modal open={modal} onClose={toggleModal} center classNames="job-advertisement-modal"> 78 <div className="head-modal"> 79 <h3>Post Job Advertisement</h3> 80 <i className="fa-solid fa-x btn-close-modal" onClick={toggleModal}></i> 81 </div> 74 82 75 76 77 78 83 <div className="modal-content"> 84 <form onSubmit={handleSubmit(addJobAdvertisement)}> 85 <div className="row"> 86 <div className="col-md-7"> 79 87 80 81 82 88 <label className="label">Job title:</label> 89 <input type="text" {...register("title")}/> 90 <p style={{color: "red"}}>{errors.title?.message}</p> 83 91 84 <label className="label">Job description:</label> 85 <textarea type="text" placeholder="Describe the job position and all the requirements" 86 className="description-textarea" {...register("description")}/> 87 <p style={{color: "red"}}>{errors.description?.message}</p> 88 </div> 89 90 <div className="col-md-5"> 91 <label className="label">Hourly rate:</label> 92 <input {...register("startingSalary")}/> 93 <p style={{color: "red"}}>{errors.startingSalary?.message}</p> 94 95 <label className="label">Industry:</label> 96 <Controller 97 name="industry" 98 control={control} 99 render={({ field }) => (<Select 100 {...field} 101 options={industryOptions} 102 />)} 103 /> 104 <p style={{color: "red"}}>{errors.industry?.message}</p> 105 106 <label className="label">Job type:</label> 107 <Controller 108 name="jobType" 109 control={control} 110 render={({ field }) => (<Select 111 {...field} 112 options={jobTypeOptions} 113 />)} 114 /> 115 <p style={{color: "red"}}>{errors.jobType?.message}</p> 116 117 <label className="label">Employment status</label> 118 <Controller 119 name="employmentStatus" 120 control={control} 121 render={({ field }) => (<Select 122 {...field} 123 options={employmentStatusOptions} 124 />)} 125 /> 126 <p style={{color: "red"}}>{errors.employmentStatus?.message}</p> 127 128 <label htmlFor="start">Active until:</label> 129 <input type="date" defaultValue={minimumDate.toLocaleDateString('en-CA')} 130 min={minimumDate.toLocaleDateString('en-CA')} onChange={(event) => console.log(event.target.value)} 131 {...register("date")}/> 92 <label className="label">Job description:</label> 93 {/*<textarea type="text" placeholder="Describe the job position and all the requirements"*/} 94 {/* className="description-textarea" {...register("description")}/>*/} 132 95 133 96 134 </div> 97 <Controller 98 name="description" 99 control={control} 100 render={({ field }) => ( 101 <Editor 102 value={field.value} 103 onTextChange={(e) => field.onChange(e.htmlValue)} 104 style={{ height: '300px', fontSize: "16px", fontFamily: "Segoe UI" }} 105 /> 106 )} 107 /> 108 <p style={{color: "red"}}>{errors.description?.message}</p> 135 109 </div> 136 110 137 <div className="aligned"> 138 <button className="submit-btn"> Submit</button> 111 <div className="col-md-5"> 112 <label className="label">Hourly rate:</label> 113 <input {...register("startingSalary")}/> 114 <p style={{color: "red"}}>{errors.startingSalary?.message}</p> 115 116 <label className="label">Industry:</label> 117 <Controller 118 name="industry" 119 control={control} 120 render={({field}) => (<Select 121 {...field} 122 options={industryOptions} 123 />)} 124 /> 125 <p style={{color: "red"}}>{errors.industry?.message}</p> 126 127 <label className="label">Job type:</label> 128 <Controller 129 name="jobType" 130 control={control} 131 render={({field}) => (<Select 132 {...field} 133 options={jobTypeOptions} 134 />)} 135 /> 136 <p style={{color: "red"}}>{errors.jobType?.message}</p> 137 138 <label className="label">Employment status</label> 139 <Controller 140 name="employmentStatus" 141 control={control} 142 render={({field}) => (<Select 143 {...field} 144 options={employmentStatusOptions} 145 />)} 146 /> 147 <p style={{color: "red"}}>{errors.employmentStatus?.message}</p> 148 149 <label htmlFor="start">Active until:</label> 150 <input type="date" defaultValue={minimumDate.toLocaleDateString('en-CA')} 151 min={minimumDate.toLocaleDateString('en-CA')} 152 onChange={(event) => console.log(event.target.value)} 153 {...register("date")}/> 154 155 139 156 </div> 157 </div> 140 158 141 </form> 142 </div> 143 </Modal> 144 </div>) 159 <div className="aligned"> 160 <button className="submit-btn"> Submit</button> 161 </div> 162 163 </form> 164 </div> 165 </Modal> 166 </div>) 145 167 } -
jobvista-frontend/src/views/job_advertisements/EditJobAdModal.js
r19398ad r28b3398 15 15 import {useDispatch, useSelector} from "react-redux"; 16 16 import {JobAdvertisementActions} from "../../redux/actions/jobAdvertisementActions"; 17 import {Editor} from "primereact/editor"; 17 18 18 19 … … 85 86 86 87 <label className="label">Job description:</label> 87 <textarea type="text" defaultValue={jobAd.props.description} placeholder="Describe the job position and all the requirements" 88 className="description-textarea" {...register("description")}/> 88 {/*<textarea type="text" defaultValue={jobAd.props.description} placeholder="Describe the job position and all the requirements"*/} 89 {/* className="description-textarea" {...register("description")}/>*/} 90 91 <Controller 92 name="description" 93 control={control} 94 render={({ field }) => ( 95 <Editor 96 defaultValue={jobAd.props.description} 97 value={jobAd.props.description} 98 onTextChange={(e) => field.onChange(e.htmlValue)} 99 style={{ height: '300px', fontSize: "16px", fontFamily: "Segoe UI" }} 100 /> 101 )} 102 /> 103 89 104 <p style={{color: "red"}}>{errors.description?.message}</p> 90 105 </div> -
jobvista-frontend/src/views/job_advertisements/Form.css
r19398ad r28b3398 12 12 font-weight: 600; 13 13 float: right; 14 margin-left: 10px;14 /*margin-left: 10px;*/ 15 15 } 16 16 … … 87 87 .description-textarea { 88 88 height: 285px; 89 } 89 90 91 .applictaion-textarea { 92 height: 100px; 93 } 94 95 .resume-link { 96 display: block; 97 padding: 5px 10px; 98 width: 100%; 99 border-radius: 3px; 100 border: 1px solid lightgrey; 101 } 102 .custom { 103 margin-top: 10px; 90 104 } 91 105 … … 161 175 } 162 176 177 .ql-snow .ql-picker.ql-font { 178 display: none; 179 } 180 .ql-toolbar.ql-snow .ql-picker-options { 181 background-color: white; 182 } 183 .ql-snow .ql-tooltip { 184 transform: translateX(100px) !important; 185 } 163 186 164 187 165 188 166 189 190 -
jobvista-frontend/src/views/job_advertisements/JobAdDetails.js
r19398ad r28b3398 9 9 import {formatRelativeTime} from "../../utils/utils"; 10 10 import {AddJobAdModal} from "./AddJobAdModal"; 11 import {ApplyToJobAdModal} from "../applications/ApplyToJobAdModal"; 11 12 12 13 … … 52 53 53 54 <p><i className="fa-solid fa-money-check-dollar"></i> <span>Hourly rate: ${jobAd.startingSalary}</span></p> 54 <p><i className="fa-solid fa-briefcase"></i> Employment status: {jobAd.employmentStatus=== EmploymentStatus.FULL_TIME? "Full-time" : "Part-time"}</p>55 <p><i className="fa-solid fa-briefcase"></i> Employment status: {jobAd.employmentStatus==="FULL_TIME" ? "Full-time" : "Part-time"}</p> 55 56 <p><i className="fa-solid fa-calendar-days"></i> Active until: {new Date(jobAd.activeUntil).toLocaleString('default', { day: 'numeric', month: 'long', year: 'numeric' })}</p> 56 57 … … 59 60 <p dangerouslySetInnerHTML={{ __html: jobAd.description.replace(/\n/g, "<br>") }}></p> 60 61 )} 61 62 63 {role===Roles.JOBSEEKER && 64 <> 65 {jobAd.active && <button className="card-button apply">Apply now</button> } 66 {!jobAd.active && <button className="card-button apply disabled">Apply now</button> } 67 </> 68 } 62 <ApplyToJobAdModal jobAd={jobAd} role={role}/> 69 63 70 64 </div> … … 76 70 {/*TO DO - AFTER IMPLEMENTING FORM FOR UPDATING PERSONAL INFO*/} 77 71 <h4>About the company</h4> 78 <p>As a pioneering Swiss software company, we provide innovative IT products and tailored digital solutions. We bring decades of experience in designing, developing, and implementing highly scalable, secure, and user-centric software. 72 <p> 73 For over two decades, we have been harnessing technology to drive meaningful change. Working side by side with leading brands, we build strategies, products and solutions tailored to unique needs –regardless of industry, region or scale. By combining world-class engineering, industry expertise and a people-centric mindset, we consult and partner with our customers to create technological solutions that drive innovation and transform businesses. 79 74 <br/><br/> 80 Working across the banking, payment, mobility, health, and publishing industries, we are experts at delivering seamless and secure user journeys within these privacy-driven environments. Our business-critical applications are designed to overcome complexity and drive growth. 81 <br/><br/> 82 We are based in Zurich, Switzerland, and have offices elsewhere in Europe, Asia, and the Middle East. Founded in 1996, we are a business of 800 experts, enabling our clients to create value with trusted software.. 75 From ideation to production, we support our customers with bespoke solutions across various industries, including payments, insurance, finance and banking, technology, media and entertainment, telecommunications, retail and consumer goods, supply chain and logistics, healthcare and life sciences, energy and resources, government, automotive and travel. 76 83 77 </p> 84 78 <p><span><i className="fa-solid fa-envelope"></i> {recruiterDetails.email}</span> • <span> -
jobvista-frontend/src/views/job_advertisements/JobAdvertisements.css
r19398ad r28b3398 1 .head-job-advertisements-box {2 height: 10%;3 width: 100%;4 background-color: #fff;5 border-radius: 12px;6 padding: 15px 20px;7 margin-bottom: 20px;8 margin-top: 30px;9 height: auto;10 }11 12 13 .head-job-advertisements-box .head-component {14 display: inline;15 }16 17 18 .head-job-advertisements-box .search-container {19 position: relative;20 float: left;21 }22 .head-job-advertisements-box .search-container .search-input {23 width: 400px;24 display: inline;25 padding: 5px 30px;26 border-radius: 10px;27 margin-right: 15px;28 }29 30 .search-container .search-input:focus {31 outline-color: #6367ef;32 }33 34 .search-container i {35 left: 10px;36 position: absolute;37 top: 10px;38 }39 40 .item {41 width: 25%;42 display: inline-block;43 margin-left: 10px;44 float: right;45 } -
jobvista-frontend/src/views/job_advertisements/JobAdvertisements.js
r19398ad r28b3398 6 6 import {JobAdvertisementActions} from "../../redux/actions/jobAdvertisementActions"; 7 7 import {formatRelativeTime, sortElementsByDateCreated} from "../../utils/utils"; 8 import {dataRangeOptions, industryOptions, sortOptions} from "../selectOptions";8 import {dataRangeOptions, industryOptions, industryOptionsFilter, sortOptions} from "../selectOptions"; 9 9 import Select from "react-select"; 10 10 import {DeleteJobAdModal} from "./DeleteJobAdModal"; … … 13 13 import JobType from "../../enumerations/JobType"; 14 14 15 export const JobAdvertisements = ( ) => {15 export const JobAdvertisements = (props) => { 16 16 17 17 const dispatch = useDispatch(); … … 21 21 22 22 const [role, setRole] = useState(""); 23 const [s ortOrder, setSortOrder] = useState("newest");24 const [selected DateRange, setSelectedDateRange] = useState("all");23 const [selectedSortOrder, setSelectedSortOrder] = useState("date_newest"); 24 const [selectedIndustry, setSelectedIndustry] = useState("all"); 25 25 const [searchTerm, setSearchTerm] = useState(""); 26 26 const [dispatched, setDispatched] = useState(false) … … 35 35 useEffect(() => { 36 36 if (!dispatched && jobAdvertisementsByRecruiterState.length === 0) { 37 dispatch(JobAdvertisementActions.fetchJobAdvertisementsByRecruiter( "deleteThis",(success, response) => {37 dispatch(JobAdvertisementActions.fetchJobAdvertisementsByRecruiter((success, response) => { 38 38 if (success && response.data.length > 0) { 39 39 setJobAdvertisementsByRecruiter(sortElementsByDateCreated(response.data)) … … 49 49 }, [jobAdvertisementsByRecruiterState]) 50 50 51 let filterJobAdvertisements = () => { 52 JobAdvertisementActions.filterJobAdvertisementsByRecruiter( 53 { 54 searchTerm: searchTerm, 55 industry: selectedIndustry, 56 sortOrder: selectedSortOrder 57 }, (success, response) => { 58 if (success) { 59 setJobAdvertisementsByRecruiter(response.data); 60 } 61 } 62 ) 63 } 64 51 65 return ( 52 66 <div className="container"> 53 <div className="head- job-advertisements-box">67 <div className="head-dashboard-box"> 54 68 <div className="row"> 55 <div className="col-md- 3">56 <div className="search-container head-component">57 <i className="fa-solid fa-magnifying-glass blue-colored"></i>69 <div className="col-md-12 filter-container"> 70 <div className="search-container"> 71 <i className="fa-solid fa-magnifying-glass search-icon"></i> 58 72 <input 59 73 className="search-input" 60 74 type="text" 61 75 placeholder="Search job advertisement by title..." 62 //value={searchTerm}63 //onChange={event => setSearchTerm(event.target.value)}76 value={searchTerm} 77 onChange={event => setSearchTerm(event.target.value)} 64 78 /> 65 79 </div> 66 </div> 67 <div className="col-md-9"> 68 69 <AddJobAdModal/> 80 <div className="sort-section item"> 81 <Select 82 defaultValue={{value: "all", label: "All industries"}} 83 value={selectedIndustry.value} 84 onChange={option => setSelectedIndustry(option.value)} 85 options={industryOptionsFilter} 86 className="sort-range sort" 87 /> 88 </div> 70 89 <div className="sort-section item"> 71 90 <Select 72 91 defaultValue={{value: "newest", label: "Date (Newest First)"}} 73 //value={sortOrder.value}74 //onChange ={option => setSortOrder(option.value)}92 value={selectedSortOrder.value} 93 onChange={option => setSelectedSortOrder(option.value)} 75 94 options={sortOptions} 76 95 className="sort-range sort" 77 96 /> 78 97 </div> 79 <div className="date-range-section item"> 80 <Select 81 defaultValue={{value: "all", label: "Lifetime"}} 82 //value={selectedDateRange.value} 83 //onChange={option => setSelectedDateRange(option.value)} 84 options={dataRangeOptions} 85 className="date-range sort" 86 /> 87 </div> 98 <button onClick={filterJobAdvertisements} className="btn-open-modal">Find jobs</button> 88 99 </div> 89 100 </div> 90 101 </div> 91 102 <div className="row row-cols-1 row-cols-md-4 g-4"> 103 <AddJobAdModal/> 92 104 93 105 {jobAdvertisementsByRecruiter && … … 97 109 <div className="card-head"> 98 110 <span className="hourly-salary"><b>${jobAd.startingSalary}/hr</b></span> 99 <span className="job-type"> {jobAd.jobType===JobType.JOB ? "Job" : "Internship"}</span> 111 <span 112 className="job-type"> {jobAd.jobType === JobType.JOB ? "Job" : "Internship"}</span> 100 113 {!jobAd.active && <span className="expired">Expired</span>} 101 114 <div className="card-management-btns"> … … 106 119 <div className="card-body"> 107 120 <h5 className="card-title">{jobAd.title}</h5> 108 <span>{jobAd.industry} • <span style={{color: "black", fontWeight: "bold"}}>{formatRelativeTime(jobAd.postedOn)}</span></span> 121 <span>{jobAd.industry} • <span style={{ 122 color: "black", 123 fontWeight: "bold" 124 }}>{formatRelativeTime(jobAd.postedOn)}</span></span> 109 125 <div className="card-info"> 110 <span><i className="fa-solid fa-building" style={{color: "#000000"}}></i> Company: <span style={{color: "black", fontWeight: "bold"}}>{jobAd.recruiterName}</span></span> <br/> 126 <span><i className="fa-solid fa-building" 127 style={{color: "#000000"}}></i> Company: <span style={{ 128 color: "black", 129 fontWeight: "bold" 130 }}>{jobAd.recruiterName}</span></span> <br/> 111 131 </div> 112 132 113 133 <div className="aligned"> 114 <Link to={`/my-job-advertisements/view/${jobAd.id}`} className="card-button solo">View applications</Link> 134 <Link to={`/my-job-advertisements/${jobAd.id}/applications`} 135 className="card-button solo">View applications</Link> 115 136 </div> 116 137 -
jobvista-frontend/src/views/selectOptions.js
r19398ad r28b3398 58 58 59 59 export const sortOptions = [ 60 { value: " newest", label: "Date (Newest First)" },61 { value: " oldest", label: "Date (Oldest First)" },62 { value: " highest", label: "Salary (High to Low)" },63 { value: " lowest", label: "Salary (Low to High)" },60 { value: "date_newest", label: "Date (Newest First)" }, 61 { value: "date_oldest", label: "Date (Oldest First)" }, 62 { value: "salary_highest", label: "Salary (High to Low)" }, 63 { value: "salary_lowest", label: "Salary (Low to High)" }, 64 64 ] -
jobvista-frontend/src/views/static/Header.css
r19398ad r28b3398 20 20 height: 80px; 21 21 background-color: #535C91; 22 font-family: Poppins, sans-serif; 22 23 } 23 24 -
jobvista-frontend/src/views/static/Header.js
r19398ad r28b3398 38 38 {role==Roles.JOBSEEKER && 39 39 <> 40 <NavLink to="/ applications" className="nav-item nav-link" >Applications</NavLink>41 <NavLink to="/favoritejobs" className="nav-item nav-link" >Saved</NavLink>40 <NavLink to="/my-applications" className="nav-item nav-link" >My Applications</NavLink> 41 {/*<NavLink to="/favoritejobs" className="nav-item nav-link" >Saved</NavLink>*/} 42 42 </> 43 43 … … 45 45 {role==Roles.RECRUITER && 46 46 <> 47 <NavLink to="/my-job-advertisements" className="nav-item nav-link" > JobAdvertisements</NavLink>48 <NavLink to="/favoritejobs" className="nav-item nav-link" >Saved</NavLink>47 <NavLink to="/my-job-advertisements" className="nav-item nav-link" >My Advertisements</NavLink> 48 {/*<NavLink to="/favoritejobs" className="nav-item nav-link" >Saved</NavLink>*/} 49 49 </> 50 50 } 51 51 <NavLink to="/about" className="nav-item nav-link">About</NavLink> 52 <NavLink to="/contact" className="nav-item nav-link"> Contact</NavLink>52 <NavLink to="/contact" className="nav-item nav-link">Support</NavLink> 53 53 </ul> 54 54
Note:
See TracChangeset
for help on using the changeset viewer.