| @@ -63,6 +63,27 @@ h3 { | |||
| border-radius: 9px; | |||
| cursor: pointer; | |||
| } | |||
| .active-ads-ads-no-ads { | |||
| height: 75vh; | |||
| display: flex; | |||
| flex-direction: column; | |||
| align-items: center; | |||
| justify-content: center; | |||
| text-align: center; | |||
| width: 466px; | |||
| margin: 0 auto; | |||
| @include media-below($bp-xl) { | |||
| width: 300px; | |||
| } | |||
| } | |||
| .active-ads-ads-no-ads h1 { | |||
| margin-bottom: 18px !important; | |||
| } | |||
| .active-ads-ads-no-ads p { | |||
| margin-bottom: 36px !important; | |||
| } | |||
| .active-ads-ads-ad { | |||
| padding-left: 81px; | |||
| @@ -132,6 +153,10 @@ h3 { | |||
| } | |||
| } | |||
| .archive-ads-no-active-ads { | |||
| padding-bottom: 136px !important; | |||
| } | |||
| .archive-ad { | |||
| box-sizing: border-box; | |||
| display: flex; | |||
| @@ -281,7 +306,7 @@ h3 { | |||
| background-color: $mainBlueLight !important; | |||
| } | |||
| .ad-card-buttons-button{ | |||
| .ad-card-buttons-button { | |||
| display: flex; | |||
| flex-direction: row; | |||
| justify-content: center; | |||
| @@ -435,6 +460,11 @@ h3 { | |||
| } | |||
| } | |||
| .add-ad-no-ads { | |||
| margin: 0 !important; | |||
| padding: 0 !important; | |||
| } | |||
| .ad-filters-header-container { | |||
| display: flex; | |||
| justify-content: space-between; | |||
| @@ -631,4 +661,128 @@ h3 { | |||
| .hiddenAd { | |||
| visibility: hidden !important; | |||
| } | |||
| .add-ad-modal { | |||
| padding: 36px !important; | |||
| border: none !important; | |||
| border-radius: 18px; | |||
| min-height: 591px !important; | |||
| width: 512px !important; | |||
| } | |||
| .add-ad-modal-header { | |||
| display: flex; | |||
| align-items: center; | |||
| justify-content: space-between; | |||
| margin-bottom: 18px !important; | |||
| } | |||
| .add-ad-modal-header img { | |||
| width: 18px; | |||
| height: 18px; | |||
| } | |||
| .add-ad-modal-header-title { | |||
| display: flex; | |||
| align-items: flex-end; | |||
| } | |||
| .add-ad-modal-header-title > * { | |||
| margin-right: 6px; | |||
| } | |||
| .add-ad-modal-header-title-span { | |||
| color: $mainBlue; | |||
| } | |||
| .add-ad-modal-header-icon button { | |||
| background-color: transparent; | |||
| border: none; | |||
| cursor: pointer; | |||
| } | |||
| .add-ad-modal-header-icon img { | |||
| width: 9px !important; | |||
| height: 12px !important; | |||
| } | |||
| .add-ad-modal-stages { | |||
| display: flex; | |||
| flex-direction: column; | |||
| justify-content: space-between; | |||
| min-height: calc(591px - 36px - 36px - 20.8px - 18px) !important; | |||
| } | |||
| .add-ad-modal-stage { | |||
| display: flex; | |||
| flex-direction: column; | |||
| justify-content: space-between; | |||
| min-height: calc(591px - 36px - 36px - 20.8px - 18px) !important; | |||
| } | |||
| .add-ad-modal-stage-sub-card { | |||
| display: flex; | |||
| flex-direction: column; | |||
| margin-bottom: 9px; | |||
| } | |||
| .add-ad-modal-stage-sub-card-third { | |||
| display: flex; | |||
| flex-direction: column; | |||
| margin-bottom: 9px; | |||
| } | |||
| .add-ad-modal-stage-sub-card label, | |||
| .add-ad-modal-stage-sub-card-third label { | |||
| margin-bottom: 4.5px !important; | |||
| } | |||
| .add-ad-modal-stage-sub-card-title { | |||
| margin-bottom: 9px; | |||
| font-weight: bold; | |||
| } | |||
| .add-ad-modal-stage-sub-card input, | |||
| .add-ad-modal-stage-sub-card-third textarea { | |||
| padding: 9px; | |||
| border-radius: 7px; | |||
| border: 1px solid #e4e4e4; | |||
| outline: none; | |||
| } | |||
| .add-ad-modal-stage-sub-card-third textarea { | |||
| resize: none; | |||
| } | |||
| .add-ad-modal-stage-sub-card-checkboxes { | |||
| padding: 0 9px !important; | |||
| } | |||
| .add-ad-modal-stage-sub-card-checkboxes { | |||
| display: flex; | |||
| flex-wrap: wrap; | |||
| justify-content: flex-start; | |||
| } | |||
| .add-ad-modal-stage-sub-card-checkboxes .MuiFormControlLabel-root { | |||
| width: calc(100% / 3) !important; | |||
| margin: 0 !important; | |||
| } | |||
| .add-ad-modal-stage-sub-card-buttons { | |||
| display: flex; | |||
| } | |||
| .add-ad-modal-stage-sub-card-buttons button { | |||
| margin-right: 9px; | |||
| } | |||
| .add-ad-modal-actions { | |||
| display: flex; | |||
| justify-content: space-between; | |||
| } | |||
| .add-ad-modal-actions button { | |||
| padding: 18px 72px !important; | |||
| } | |||
| @@ -8,12 +8,54 @@ import Checkbox from "@mui/material/Checkbox"; | |||
| import Slider from "@mui/material/Slider"; | |||
| import filterIcon from "../../assets/images/filter_vector.png"; | |||
| import x from "../../assets/images/x.png"; | |||
| import { changeIsCheckedValue } from "../../store/actions/technologies/technologiesActions"; | |||
| import { useDispatch } from "react-redux"; | |||
| import { setFilteredAdsReq } from "../../store/actions/ads/adsAction"; | |||
| const AdFilters = ({ open, handleClose }) => { | |||
| const [value, setValue] = useState([0, 10]); | |||
| const AdFilters = ({ open, handleClose, technologies }) => { | |||
| const [sliderValue, setSliderValue] = useState([0, 10]); | |||
| const [employmentType, setEmploymentType] = useState("Work"); | |||
| const [workHour, setWorkHour] = useState("FullTime"); | |||
| const dispatch = useDispatch(); | |||
| const handleSliderChange = (_, newValue) => { | |||
| setValue(newValue); | |||
| setSliderValue(newValue); | |||
| }; | |||
| const onSubmitFilters = () => { | |||
| const tech = technologies | |||
| .filter((tech) => tech.isChecked === true) | |||
| .map((tech) => tech.name); | |||
| if(tech.length === 0) { | |||
| return; | |||
| } | |||
| dispatch( | |||
| setFilteredAdsReq({ | |||
| minExperience: sliderValue[0], | |||
| maxExperience: sliderValue[1], | |||
| technologies: tech, | |||
| workHour, | |||
| employmentType, | |||
| }) | |||
| ); | |||
| handleClose(); | |||
| }; | |||
| const handleCheckboxes = (e) => { | |||
| const { value } = e.target; | |||
| dispatch(changeIsCheckedValue(value)); | |||
| }; | |||
| const employmentTypeHandler = (type) => { | |||
| setEmploymentType(type); | |||
| }; | |||
| const workHourHandler = (type) => { | |||
| setWorkHour(type); | |||
| }; | |||
| const list = () => ( | |||
| @@ -48,7 +90,7 @@ const AdFilters = ({ open, handleClose }) => { | |||
| <div className="ad-filters-experience-slider"> | |||
| <Slider | |||
| getAriaLabel={() => "Temperature range"} | |||
| value={value} | |||
| value={sliderValue} | |||
| onChange={handleSliderChange} | |||
| valueLabelDisplay="auto" | |||
| max={20} | |||
| @@ -61,14 +103,19 @@ const AdFilters = ({ open, handleClose }) => { | |||
| </div> | |||
| <div className="ad-filters-technologies-checkboxes"> | |||
| <FormGroup> | |||
| <FormControlLabel control={<Checkbox />} label="HTML/CSS" /> | |||
| <FormControlLabel control={<Checkbox />} label="Vanilla JS" /> | |||
| <FormControlLabel control={<Checkbox />} label="React" /> | |||
| <FormControlLabel control={<Checkbox />} label="Angular" /> | |||
| <FormControlLabel control={<Checkbox />} label="SQL" /> | |||
| <FormControlLabel control={<Checkbox />} label="Node.js" /> | |||
| <FormControlLabel control={<Checkbox />} label="PHP" /> | |||
| <FormControlLabel control={<Checkbox />} label="Git" /> | |||
| {technologies.map((technology, index) => ( | |||
| <FormControlLabel | |||
| key={index} | |||
| control={ | |||
| <Checkbox | |||
| onChange={handleCheckboxes} | |||
| value={technology.name} | |||
| checked={technology.isChecked} | |||
| /> | |||
| } | |||
| label={technology.name} | |||
| /> | |||
| ))} | |||
| </FormGroup> | |||
| </div> | |||
| </div> | |||
| @@ -77,8 +124,26 @@ const AdFilters = ({ open, handleClose }) => { | |||
| <p>Tip zaposlenja</p> | |||
| </div> | |||
| <div className="ad-filters-employment-type"> | |||
| <button className="c-btn c-btn--primary-outlined">Intership</button> | |||
| <button className="c-btn c-btn--primary">Posao</button> | |||
| <button | |||
| className={`c-btn ${ | |||
| employmentType === "Intership" | |||
| ? "c-btn c-btn--primary" | |||
| : "c-btn--primary-outlined" | |||
| }`} | |||
| onClick={employmentTypeHandler.bind(this, "Intership")} | |||
| > | |||
| Intership | |||
| </button> | |||
| <button | |||
| className={`c-btn ${ | |||
| employmentType === "Work" | |||
| ? "c-btn c-btn--primary" | |||
| : "c-btn--primary-outlined" | |||
| }`} | |||
| onClick={employmentTypeHandler.bind(this, "Work")} | |||
| > | |||
| Posao | |||
| </button> | |||
| </div> | |||
| </div> | |||
| <div className="ad-filters-technologies"> | |||
| @@ -86,12 +151,32 @@ const AdFilters = ({ open, handleClose }) => { | |||
| <p>Radno vreme</p> | |||
| </div> | |||
| <div className="ad-filters-employment-type"> | |||
| <button className="c-btn c-btn--primary-outlined">Part-time</button> | |||
| <button className="c-btn c-btn--primary-outlined">Full-time</button> | |||
| <button | |||
| className={`c-btn ${ | |||
| workHour === "PartTime" | |||
| ? "c-btn c-btn--primary" | |||
| : "c-btn--primary-outlined" | |||
| }`} | |||
| onClick={workHourHandler.bind(this, "PartTime")} | |||
| > | |||
| Part-time | |||
| </button> | |||
| <button | |||
| className={`c-btn ${ | |||
| workHour === "FullTime" | |||
| ? "c-btn c-btn--primary" | |||
| : "c-btn--primary-outlined" | |||
| }`} | |||
| onClick={workHourHandler.bind(this, "FullTime")} | |||
| > | |||
| Full-time | |||
| </button> | |||
| </div> | |||
| </div> | |||
| <div className="ad-filters-search"> | |||
| <button className="c-btn c-btn--primary">Pretrazi</button> | |||
| <button onClick={onSubmitFilters} className="c-btn c-btn--primary"> | |||
| Pretrazi | |||
| </button> | |||
| </div> | |||
| </div> | |||
| </Box> | |||
| @@ -109,6 +194,7 @@ const AdFilters = ({ open, handleClose }) => { | |||
| AdFilters.propTypes = { | |||
| open: PropType.any, | |||
| handleClose: PropType.func, | |||
| technologies: PropType.any, | |||
| }; | |||
| export default AdFilters; | |||
| @@ -1,7 +1,15 @@ | |||
| import React from "react"; | |||
| import React, { useEffect, useState } from "react"; | |||
| import PropType from "prop-types"; | |||
| import Box from "@mui/material/Box"; | |||
| import Modal from "@mui/material/Modal"; | |||
| import plus from "../../assets/images/plus.png"; | |||
| import xIcon from "../../assets/images/x.png"; | |||
| import AddAdModalFirstStage from "./AddAdModalFirstStage"; | |||
| import AddAdModalSecondStage from "./AddAdModalSecondStage"; | |||
| import AddAdModalThirdStage from "./AddAdModalThirdStage"; | |||
| import { useSelector, useDispatch } from "react-redux"; | |||
| import { setTechnologiesAddAdReq } from "../../store/actions/addAdTechnologies/addAdTechnologiesActions"; | |||
| import { setCreateAdReq } from "../../store/actions/createAd/createAdActions"; | |||
| const style = { | |||
| position: "absolute", | |||
| @@ -12,12 +20,61 @@ const style = { | |||
| bgcolor: "background.paper", | |||
| border: "2px solid #000", | |||
| boxShadow: 24, | |||
| pt: 2, | |||
| px: 4, | |||
| pb: 3, | |||
| }; | |||
| const AddAdModal = ({ open, handleClose }) => { | |||
| const [stage, setStage] = useState(1); | |||
| const [title, setTitle] = useState(""); | |||
| const [employmentType, setEmploymentType] = useState("Work"); | |||
| const [workHour, setWorkHour] = useState("FullTime"); | |||
| const [expiredAt, setExpiredAt] = useState(new Date()); | |||
| const [experience, setExperience] = useState(0); | |||
| const [keyResponsibilities, setKeyResponsibilities] = useState(""); | |||
| const [requirements, setRequirements] = useState(""); | |||
| const [whatWeOffer, setWhatWeOffer] = useState(""); | |||
| const technologies = useSelector( | |||
| (state) => state.addAdTechnologies.technologies | |||
| ); | |||
| const dispatch = useDispatch(); | |||
| useEffect(() => { | |||
| dispatch(setTechnologiesAddAdReq()); | |||
| }, []); | |||
| const addAdHandler = () => { | |||
| const technologiesIds = technologies | |||
| .filter((x) => x.isChecked === true) | |||
| .map((x) => x.technologyId); | |||
| dispatch( | |||
| setCreateAdReq({ | |||
| title, | |||
| minimumExperience: experience, | |||
| createdAt: new Date(), | |||
| expiredAt, | |||
| keyResponsibilities, | |||
| requirements, | |||
| offer: whatWeOffer, | |||
| workHour, | |||
| employmentType, | |||
| technologiesIds, | |||
| }) | |||
| ); | |||
| handleClose(); | |||
| }; | |||
| const completeFirstStage = () => { | |||
| console.log(title, employmentType, workHour, expiredAt); | |||
| }; | |||
| const incrementStageHandler = () => { | |||
| setStage((oldState) => oldState + 1); | |||
| }; | |||
| const decrementStageHandler = () => { | |||
| setStage((oldState) => oldState - 1); | |||
| }; | |||
| return ( | |||
| <Modal | |||
| open={open} | |||
| @@ -25,11 +82,61 @@ const AddAdModal = ({ open, handleClose }) => { | |||
| aria-labelledby="parent-modal-title" | |||
| aria-describedby="parent-modal-description" | |||
| > | |||
| <Box sx={{ ...style, width: 400 }}> | |||
| <h2 id="parent-modal-title">Text in a modal</h2> | |||
| <p id="parent-modal-description"> | |||
| Duis mollis, est non commodo luctus, nisi erat porttitor ligula. | |||
| </p> | |||
| <Box sx={{ ...style, width: 512 }} className="add-ad-modal"> | |||
| <div className="add-ad-modal-header"> | |||
| <div className="add-ad-modal-header-title"> | |||
| <img src={plus} alt="plus" /> | |||
| <h3>Dodavanje</h3> | |||
| <h4> | |||
| <span className="add-ad-modal-header-title-span">| Oglas</span> | |||
| </h4> | |||
| <h4></h4> | |||
| </div> | |||
| <div className="add-ad-modal-header-icon"> | |||
| <button onClick={handleClose}> | |||
| <img src={xIcon} alt="xIcon" /> | |||
| </button> | |||
| </div> | |||
| </div> | |||
| <div className="add-ad-modal-stages"> | |||
| {stage === 1 && ( | |||
| <AddAdModalFirstStage | |||
| title={title} | |||
| setTitle={setTitle} | |||
| employmentType={employmentType} | |||
| setEmploymentType={setEmploymentType} | |||
| workHour={workHour} | |||
| setWorkHour={setWorkHour} | |||
| expiredAt={expiredAt} | |||
| setExpiredAt={setExpiredAt} | |||
| onCompleteFirstStage={completeFirstStage} | |||
| onIncrementStage={incrementStageHandler} | |||
| onDecrementStage={decrementStageHandler} | |||
| /> | |||
| )} | |||
| {stage === 2 && ( | |||
| <AddAdModalSecondStage | |||
| onIncrementStage={incrementStageHandler} | |||
| onDecrementStage={decrementStageHandler} | |||
| technologies={technologies} | |||
| experience={experience} | |||
| setExperience={setExperience} | |||
| /> | |||
| )} | |||
| {stage === 3 && ( | |||
| <AddAdModalThirdStage | |||
| onDecrementStage={decrementStageHandler} | |||
| keyResponsibilities={keyResponsibilities} | |||
| setKeyResponsibilities={setKeyResponsibilities} | |||
| requirements={requirements} | |||
| setRequirements={setRequirements} | |||
| whatWeOffer={whatWeOffer} | |||
| setWhatWeOffer={setWhatWeOffer} | |||
| onAddAd={addAdHandler} | |||
| /> | |||
| )} | |||
| </div> | |||
| </Box> | |||
| </Modal> | |||
| ); | |||
| @@ -37,7 +144,7 @@ const AddAdModal = ({ open, handleClose }) => { | |||
| AddAdModal.propTypes = { | |||
| open: PropType.any, | |||
| handleClose: PropType.func | |||
| handleClose: PropType.func, | |||
| }; | |||
| export default AddAdModal; | |||
| @@ -0,0 +1,140 @@ | |||
| import React from "react"; | |||
| import PropTypes from "prop-types"; | |||
| const AddAdModalFirstStage = ({ | |||
| onIncrementStage, | |||
| onDecrementStage, | |||
| onCompleteFirstStage, | |||
| title, | |||
| employmentType, | |||
| workHour, | |||
| setTitle, | |||
| setEmploymentType, | |||
| setWorkHour, | |||
| expiredAt, | |||
| setExpiredAt, | |||
| }) => { | |||
| const completeStageHandler = () => { | |||
| if(title.length === 0) { | |||
| return; | |||
| } | |||
| onIncrementStage(); | |||
| onCompleteFirstStage(); | |||
| }; | |||
| const employmentTypeHandler = (type) => { | |||
| setEmploymentType(type); | |||
| }; | |||
| const workHourHandler = (type) => { | |||
| setWorkHour(type); | |||
| }; | |||
| return ( | |||
| <div className="add-ad-modal-stage"> | |||
| <div> | |||
| <div className="add-ad-modal-stage-sub-card"> | |||
| <label>Naslov</label> | |||
| <input | |||
| type="text" | |||
| value={title} | |||
| placeholder="ex. Medior React Developer" | |||
| onChange={(e) => setTitle(e.target.value)} | |||
| /> | |||
| </div> | |||
| <div className="add-ad-modal-stage-sub-card"> | |||
| <label>Tip zaposlenja</label> | |||
| <div className="add-ad-modal-stage-sub-card-buttons"> | |||
| <button | |||
| className={`c-btn ${ | |||
| employmentType === "Work" | |||
| ? "c-btn c-btn--primary" | |||
| : "c-btn--primary-outlined" | |||
| }`} | |||
| onClick={employmentTypeHandler.bind(this, "Work")} | |||
| > | |||
| Posao | |||
| </button> | |||
| <button | |||
| className={`c-btn ${ | |||
| employmentType === "Intership" | |||
| ? "c-btn c-btn--primary" | |||
| : "c-btn--primary-outlined" | |||
| }`} | |||
| onClick={employmentTypeHandler.bind(this, "Intership")} | |||
| > | |||
| Intership | |||
| </button> | |||
| </div> | |||
| </div> | |||
| <div className="add-ad-modal-stage-sub-card"> | |||
| <label>Radno vreme</label> | |||
| <div className="add-ad-modal-stage-sub-card-buttons"> | |||
| <button | |||
| className={`c-btn ${ | |||
| workHour === "PartTime" | |||
| ? "c-btn c-btn--primary" | |||
| : "c-btn--primary-outlined" | |||
| }`} | |||
| onClick={workHourHandler.bind(this, "PartTime")} | |||
| > | |||
| Part-time | |||
| </button> | |||
| <button | |||
| className={`c-btn ${ | |||
| workHour === "FullTime" | |||
| ? "c-btn c-btn--primary" | |||
| : "c-btn--primary-outlined" | |||
| }`} | |||
| onClick={workHourHandler.bind(this, "FullTime")} | |||
| > | |||
| Full-time | |||
| </button> | |||
| </div> | |||
| </div> | |||
| <div className="add-ad-modal-stage-sub-card"> | |||
| <label>Datum isteka oglasa</label> | |||
| <input | |||
| type="date" | |||
| placeholder="ex" | |||
| value={expiredAt} | |||
| onChange={(e) => setExpiredAt(e.target.value)} | |||
| /> | |||
| </div> | |||
| </div> | |||
| <div className="add-ad-modal-actions"> | |||
| <button | |||
| className="c-btn c-btn--primary-outlined" | |||
| disabled | |||
| onClick={onDecrementStage} | |||
| > | |||
| NAZAD | |||
| </button> | |||
| <button className="c-btn c-btn--primary" onClick={completeStageHandler}> | |||
| NASTAVI | |||
| </button> | |||
| </div> | |||
| </div> | |||
| ); | |||
| }; | |||
| AddAdModalFirstStage.propTypes = { | |||
| onCompleteFirstStage: PropTypes.func, | |||
| onIncrementStage: PropTypes.func, | |||
| onDecrementStage: PropTypes.func, | |||
| title: PropTypes.string, | |||
| employmentType: PropTypes.string, | |||
| workHour: PropTypes.string, | |||
| setTitle: PropTypes.func, | |||
| setEmploymentType: PropTypes.func, | |||
| setWorkHour: PropTypes.func, | |||
| expiredAt: PropTypes.any, | |||
| setExpiredAt: PropTypes.func, | |||
| }; | |||
| export default AddAdModalFirstStage; | |||
| @@ -0,0 +1,136 @@ | |||
| import React from "react"; | |||
| import PropTypes from "prop-types"; | |||
| import { Checkbox, FormControlLabel } from "@mui/material"; | |||
| import { useDispatch } from "react-redux"; | |||
| import { changeIsCheckedAddAdValue } from "../../store/actions/addAdTechnologies/addAdTechnologiesActions"; | |||
| const AddAdModalSecondStage = ({ | |||
| onIncrementStage, | |||
| onDecrementStage, | |||
| technologies, | |||
| experience, | |||
| setExperience, | |||
| }) => { | |||
| const dispatch = useDispatch(); | |||
| const completeStageHandler = () => { | |||
| const checkedTechnologies = technologies.filter( | |||
| (x) => x.isChecked === true | |||
| ); | |||
| if (checkedTechnologies.length === 0) { | |||
| return; | |||
| } | |||
| onIncrementStage(); | |||
| }; | |||
| const handleCheckboxes = (technologyId) => { | |||
| dispatch(changeIsCheckedAddAdValue(technologyId)); | |||
| }; | |||
| return ( | |||
| <div className="add-ad-modal-stages"> | |||
| <div> | |||
| <div className="add-ad-modal-stage-sub-card-title"> | |||
| <label>Neophodne tehnologije</label> | |||
| </div> | |||
| <div className="add-ad-modal-stage-sub-card"> | |||
| <div className="add-ad-modal-stage-sub-card-checkboxes-group"> | |||
| <label>Front-End</label> | |||
| <div className="add-ad-modal-stage-sub-card-checkboxes"> | |||
| {technologies | |||
| .filter((x) => x.technologyType === "Frontend") | |||
| .map((x) => ( | |||
| <FormControlLabel | |||
| key={x.technologyId} | |||
| control={ | |||
| <Checkbox | |||
| value={x.name} | |||
| checked={x.isChecked} | |||
| onChange={handleCheckboxes.bind(this, x.technologyId)} | |||
| /> | |||
| } | |||
| label={x.name} | |||
| /> | |||
| ))} | |||
| </div> | |||
| </div> | |||
| <div className="add-ad-modal-stage-sub-card-checkboxes-group"> | |||
| <label>Back-End</label> | |||
| <div className="add-ad-modal-stage-sub-card-checkboxes"> | |||
| {technologies | |||
| .filter((x) => x.technologyType === "Backend") | |||
| .map((x) => ( | |||
| <FormControlLabel | |||
| key={x.technologyId} | |||
| control={ | |||
| <Checkbox | |||
| value={x.name} | |||
| checked={x.isChecked} | |||
| onChange={handleCheckboxes.bind(this, x.technologyId)} | |||
| /> | |||
| } | |||
| label={x.name} | |||
| /> | |||
| ))} | |||
| </div> | |||
| </div> | |||
| <div className="add-ad-modal-stage-sub-card-checkboxes-group"> | |||
| <label>Other</label> | |||
| <div className="add-ad-modal-stage-sub-card-checkboxes"> | |||
| {technologies | |||
| .filter((x) => x.technologyType === "Other") | |||
| .map((x) => ( | |||
| <FormControlLabel | |||
| key={x.technologyId} | |||
| control={ | |||
| <Checkbox | |||
| value={x.name} | |||
| checked={x.isChecked} | |||
| onChange={handleCheckboxes.bind(this, x.technologyId)} | |||
| /> | |||
| } | |||
| label={x.name} | |||
| /> | |||
| ))} | |||
| </div> | |||
| </div> | |||
| </div> | |||
| <div className="add-ad-modal-stage-sub-card"> | |||
| <label>Godine iskustva</label> | |||
| <input | |||
| type="number" | |||
| placeholder="ex. 3 godine iskustva" | |||
| value={experience} | |||
| onChange={(e) => setExperience(e.target.value)} | |||
| /> | |||
| </div> | |||
| </div> | |||
| <div className="add-ad-modal-actions"> | |||
| <button | |||
| className="c-btn c-btn--primary-outlined" | |||
| onClick={onDecrementStage} | |||
| > | |||
| NAZAD | |||
| </button> | |||
| <button className="c-btn c-btn--primary" onClick={completeStageHandler}> | |||
| NASTAVI | |||
| </button> | |||
| </div> | |||
| </div> | |||
| ); | |||
| }; | |||
| AddAdModalSecondStage.propTypes = { | |||
| onIncrementStage: PropTypes.func, | |||
| onDecrementStage: PropTypes.func, | |||
| technologies: PropTypes.any, | |||
| experience: PropTypes.any, | |||
| setExperience: PropTypes.func, | |||
| }; | |||
| export default AddAdModalSecondStage; | |||
| @@ -0,0 +1,89 @@ | |||
| import React from "react"; | |||
| import PropTypes from "prop-types"; | |||
| const AddAdModalThirdStage = ({ | |||
| onDecrementStage, | |||
| keyResponsibilities, | |||
| setKeyResponsibilities, | |||
| requirements, | |||
| setRequirements, | |||
| whatWeOffer, | |||
| setWhatWeOffer, | |||
| onAddAd, | |||
| }) => { | |||
| const completeStageHandler = () => { | |||
| if ( | |||
| keyResponsibilities.length === 0 || | |||
| requirements.length === 0 || | |||
| whatWeOffer.length === 0 | |||
| ) { | |||
| return; | |||
| } | |||
| onAddAd(); | |||
| }; | |||
| return ( | |||
| <div className="add-ad-modal-stage"> | |||
| <div> | |||
| <div className="add-ad-modal-stage-sub-card-third"> | |||
| <label>Glavna zaduzenja</label> | |||
| <textarea | |||
| type="text" | |||
| placeholder="ex. Working as a React developer on various projects... (Separate every responsibility with | sign)" | |||
| value={keyResponsibilities} | |||
| onChange={(e) => setKeyResponsibilities(e.target.value)} | |||
| rows={5} | |||
| ></textarea> | |||
| </div> | |||
| <div className="add-ad-modal-stage-sub-card-third"> | |||
| <label>Uslovi</label> | |||
| <textarea | |||
| type="text" | |||
| placeholder="ex. Good software development fundamentals... (Separate every condition with | sign)" | |||
| value={requirements} | |||
| onChange={(e) => setRequirements(e.target.value)} | |||
| rows={5} | |||
| ></textarea> | |||
| </div> | |||
| <div className="add-ad-modal-stage-sub-card-third"> | |||
| <label>Sta nudimo</label> | |||
| <textarea | |||
| type="text" | |||
| placeholder="ex. Full Remote position... (Separate every offer with | sign)" | |||
| value={whatWeOffer} | |||
| onChange={(e) => setWhatWeOffer(e.target.value)} | |||
| rows={5} | |||
| ></textarea> | |||
| </div> | |||
| </div> | |||
| <div className="add-ad-modal-actions"> | |||
| <button | |||
| className="c-btn c-btn--primary-outlined" | |||
| onClick={onDecrementStage} | |||
| > | |||
| NAZAD | |||
| </button> | |||
| <button className="c-btn c-btn--primary" onClick={completeStageHandler}> | |||
| DODAJ OGLAS | |||
| </button> | |||
| </div> | |||
| </div> | |||
| ); | |||
| }; | |||
| AddAdModalThirdStage.propTypes = { | |||
| onDecrementStage: PropTypes.func, | |||
| keyResponsibilities: PropTypes.any, | |||
| setKeyResponsibilities: PropTypes.func, | |||
| requirements: PropTypes.any, | |||
| setRequirements: PropTypes.func, | |||
| whatWeOffer: PropTypes.any, | |||
| setWhatWeOffer: PropTypes.func, | |||
| onAddAd: PropTypes.func, | |||
| }; | |||
| export default AddAdModalThirdStage; | |||
| @@ -21,6 +21,9 @@ import { useTheme } from "@emotion/react"; | |||
| import { useMediaQuery } from "@mui/material"; | |||
| import { selectArchiveAds } from "../../store/selectors/archiveAdsSelectors"; | |||
| import { setArchiveAdsReq } from "../../store/actions/archiveAds/archiveAdsActions"; | |||
| import noActiveAds from "../../assets/images/no_active_ads.png"; | |||
| import { setTechnologiesReq } from "../../store/actions/technologies/technologiesActions"; | |||
| import { selectTechnologies } from "../../store/selectors/technologiesSelectors"; | |||
| const AdsPage = ({ history }) => { | |||
| const theme = useTheme(); | |||
| @@ -28,6 +31,7 @@ const AdsPage = ({ history }) => { | |||
| const [toggleFiltersDrawer, setToggleFiltersDrawer] = useState(false); | |||
| const [toggleModal, setToggleModal] = useState(false); | |||
| const ads = useSelector(selectAds); | |||
| const technologies = useSelector(selectTechnologies); | |||
| const archiveAds = useSelector(selectArchiveAds); | |||
| const activeAdsSliderRef = useRef(); | |||
| const archiveAdsSliderRef = useRef(); | |||
| @@ -35,6 +39,7 @@ const AdsPage = ({ history }) => { | |||
| const dispatch = useDispatch(); | |||
| useEffect(() => { | |||
| dispatch(setTechnologiesReq()); | |||
| dispatch(setAdsReq()); | |||
| dispatch(setArchiveAdsReq()); | |||
| }, []); | |||
| @@ -58,7 +63,7 @@ const AdsPage = ({ history }) => { | |||
| settings: { | |||
| slidesToShow: 3, | |||
| slidesToScroll: 3, | |||
| infinite: true, | |||
| infinite: false, | |||
| dots: false, | |||
| }, | |||
| }, | |||
| @@ -124,6 +129,7 @@ const AdsPage = ({ history }) => { | |||
| <AdFilters | |||
| open={toggleFiltersDrawer} | |||
| handleClose={handleToggleFiltersDrawer} | |||
| technologies={technologies} | |||
| /> | |||
| <AddAdModal open={toggleModal} handleClose={handleToggleModal} /> | |||
| <div className="ads"> | |||
| @@ -140,9 +146,11 @@ const AdsPage = ({ history }) => { | |||
| <button onClick={activeAdsArrowLeftHandler}> | |||
| <img src={arrow_left} alt="arrow-left" /> | |||
| </button> | |||
| <button onClick={activeAdsArrowRightHandler}> | |||
| <img src={arrow_right} alt="arrow-right" /> | |||
| </button> | |||
| {ads.length > 3 && ( | |||
| <button onClick={activeAdsArrowRightHandler}> | |||
| <img src={arrow_right} alt="arrow-right" /> | |||
| </button> | |||
| )} | |||
| </div> | |||
| )} | |||
| </div> | |||
| @@ -172,14 +180,31 @@ const AdsPage = ({ history }) => { | |||
| </div> | |||
| </div> | |||
| )} | |||
| {matches && ( | |||
| {ads && ads.length > 0 && matches && ( | |||
| <div className="active-ads-ads-arrows"> | |||
| <button onClick={activeAdsArrowLeftHandler}> | |||
| <img src={arrow_left} alt="arrow-left" /> | |||
| </button> | |||
| <button onClick={activeAdsArrowRightHandler}> | |||
| <img src={arrow_right} alt="arrow-right" /> | |||
| </button> | |||
| {ads.length > 3 && ( | |||
| <button onClick={activeAdsArrowRightHandler}> | |||
| <img src={arrow_right} alt="arrow-right" /> | |||
| </button> | |||
| )} | |||
| </div> | |||
| )} | |||
| {(!ads || ads.length === 0) && ( | |||
| <div className="active-ads-ads-no-ads"> | |||
| <img src={noActiveAds} alt="noActiveAds" /> | |||
| <h1>Nažalost, trenutno nema aktivnih oglasa</h1> | |||
| <p>Uvek možete dodati novi u samo par jednostavnih koraka</p> | |||
| <div className="add-ad add-ad-no-ads"> | |||
| <IconButton | |||
| className="c-btn c-btn--primary add-ad-btn" | |||
| onClick={handleToggleModal} | |||
| > | |||
| Dodaj Oglas | |||
| </IconButton> | |||
| </div> | |||
| </div> | |||
| )} | |||
| {archiveAds && archiveAds.length > 0 && ( | |||
| @@ -194,9 +219,11 @@ const AdsPage = ({ history }) => { | |||
| <button onClick={archiveAdsArrowLeftHandler}> | |||
| <img src={arrow_left} alt="arrow-left" /> | |||
| </button> | |||
| <button onClick={archiveAdsArrowRightHandler}> | |||
| <img src={arrow_right} alt="arrow-right" /> | |||
| </button> | |||
| {archiveAds.length > 5 && ( | |||
| <button onClick={archiveAdsArrowRightHandler}> | |||
| <img src={arrow_right} alt="arrow-right" /> | |||
| </button> | |||
| )} | |||
| </div> | |||
| </div> | |||
| )} | |||
| @@ -230,23 +257,29 @@ const AdsPage = ({ history }) => { | |||
| <button onClick={archiveAdsArrowLeftHandler}> | |||
| <img src={arrow_left} alt="arrow-left" /> | |||
| </button> | |||
| <button onClick={archiveAdsArrowRightHandler}> | |||
| <img src={arrow_right} alt="arrow-right" /> | |||
| </button> | |||
| {archiveAds.length > 5 && ( | |||
| <button onClick={archiveAdsArrowRightHandler}> | |||
| <img src={arrow_right} alt="arrow-right" /> | |||
| </button> | |||
| )} | |||
| </div> | |||
| )} | |||
| <div className="archive-ads-no-active-ads"></div> | |||
| </div> | |||
| )} | |||
| </div> | |||
| <div className="add-ad"> | |||
| <IconButton | |||
| className="c-btn c-btn--primary add-ad-btn" | |||
| onClick={handleToggleModal} | |||
| > | |||
| + Oglas | |||
| </IconButton> | |||
| </div> | |||
| {ads && ads.length > 0 && ( | |||
| <div className="add-ad"> | |||
| <IconButton | |||
| className="c-btn c-btn--primary add-ad-btn" | |||
| onClick={handleToggleModal} | |||
| > | |||
| + Oglas | |||
| </IconButton> | |||
| </div> | |||
| )} | |||
| </> | |||
| ); | |||
| }; | |||
| @@ -1,7 +1,19 @@ | |||
| import { getRequest } from "."; | |||
| import { getRequest, postRequest } from "."; | |||
| import apiEndpoints from "./apiEndpoints"; | |||
| export const getAllAds = () => getRequest(apiEndpoints.ads.allAds); | |||
| export const getAllFilteredAds = (payload) => { | |||
| let technologiesQuery = ""; | |||
| for (let i = 0; i < payload.technologies.length; i++) { | |||
| technologiesQuery += `technologies=${payload.technologies[i]}&`; | |||
| } | |||
| return getRequest( | |||
| apiEndpoints.ads.allFilteredAds + | |||
| `?minExperience=${payload.minExperience}&maxExperience=${payload.maxExperience}&workHour=${payload.workHour}&employmentType=${payload.employmentType}&${technologiesQuery}` | |||
| ); | |||
| }; | |||
| export const getAllArchiveAds = () => | |||
| getRequest(apiEndpoints.ads.allArchiveAds); | |||
| export const getAdDetailsById = (id) => getRequest(`${apiEndpoints.ads.adDetails}/${id}`); | |||
| export const getAdDetailsById = (id) => | |||
| getRequest(`${apiEndpoints.ads.adDetails}/${id}`); | |||
| export const createNewAd = (ad) => postRequest(apiEndpoints.ads.createAd, ad); | |||
| @@ -20,9 +20,14 @@ export default { | |||
| }, | |||
| ads: { | |||
| allAds: base + "/ads", | |||
| createAd: base + "/ads", | |||
| allFilteredAds: base + "/ads/filtered", | |||
| allArchiveAds: base + "/ads/archive", | |||
| adDetails: base + "/ads/details", | |||
| }, | |||
| technologies: { | |||
| allTechnologies: base + "/technologies", | |||
| }, | |||
| comments:{ | |||
| addComment:base + '/comments' | |||
| }, | |||
| @@ -0,0 +1,5 @@ | |||
| import { getRequest } from "."; | |||
| import apiEndpoints from "./apiEndpoints"; | |||
| export const getAllTechnologies = () => | |||
| getRequest(apiEndpoints.technologies.allTechnologies); | |||
| @@ -0,0 +1,5 @@ | |||
| export const FETCH_ADD_AD_TECHNOLOGIES_REQ = "FETCH_ADD_AD_TECHNOLOGIES_REQ"; | |||
| export const FETCH_ADD_AD_TECHNOLOGIES_ERR = "FETCH_ADD_AD_TECHNOLOGIES_ERR"; | |||
| export const FETCH_ADD_AD_TECHNOLOGIES_SUCCESS = "FETCH_ADD_AD_TECHNOLOGIES_SUCCESS"; | |||
| export const CHANGE_ISCHECKED_VALUE_ADD_AD = "CHANGE_ISCHECKED_VALUE"; | |||
| @@ -0,0 +1,26 @@ | |||
| import { | |||
| FETCH_ADD_AD_TECHNOLOGIES_REQ, | |||
| FETCH_ADD_AD_TECHNOLOGIES_ERR, | |||
| FETCH_ADD_AD_TECHNOLOGIES_SUCCESS, | |||
| CHANGE_ISCHECKED_VALUE_ADD_AD, | |||
| } from "./addAdTechnologiesActionConstants"; | |||
| export const setTechnologiesAddAdReq = () => ({ | |||
| type: FETCH_ADD_AD_TECHNOLOGIES_REQ, | |||
| }); | |||
| export const setTechnologiesAddAdError = (payload) => ({ | |||
| type: FETCH_ADD_AD_TECHNOLOGIES_ERR, | |||
| payload, | |||
| }); | |||
| export const setTechnologiesAddAd = (payload) => ({ | |||
| type: FETCH_ADD_AD_TECHNOLOGIES_SUCCESS, | |||
| payload, | |||
| }); | |||
| export const changeIsCheckedAddAdValue = (payload) => ({ | |||
| type: CHANGE_ISCHECKED_VALUE_ADD_AD, | |||
| payload, | |||
| }); | |||
| @@ -2,6 +2,9 @@ import { | |||
| FETCH_ADS_REQ, | |||
| FETCH_ADS_ERR, | |||
| FETCH_ADS_SUCCESS, | |||
| FETCH_FILTERED_ADS_REQ, | |||
| FETCH_FILTERED_ADS_ERR, | |||
| FETCH_FILTERED_ADS_SUCCESS, | |||
| } from "./adsActionConstants"; | |||
| export const setAdsReq = () => ({ | |||
| @@ -17,3 +20,18 @@ export const setAds = (payload) => ({ | |||
| type: FETCH_ADS_SUCCESS, | |||
| payload, | |||
| }); | |||
| export const setFilteredAdsReq = (payload) => ({ | |||
| type: FETCH_FILTERED_ADS_REQ, | |||
| payload, | |||
| }); | |||
| export const setFilteredAdsError = (payload) => ({ | |||
| type: FETCH_FILTERED_ADS_ERR, | |||
| payload, | |||
| }); | |||
| export const setFilteredAds = (payload) => ({ | |||
| type: FETCH_FILTERED_ADS_SUCCESS, | |||
| payload, | |||
| }); | |||
| @@ -1,3 +1,7 @@ | |||
| export const FETCH_ADS_REQ = 'FETCH_ADS_REQ'; | |||
| export const FETCH_ADS_ERR = 'FETCH_ADS_ERR'; | |||
| export const FETCH_ADS_SUCCESS = 'FETCH_ADS_SUCCESS'; | |||
| export const FETCH_ADS_SUCCESS = 'FETCH_ADS_SUCCESS'; | |||
| export const FETCH_FILTERED_ADS_REQ = 'FETCH_FILTERED_ADS_REQ'; | |||
| export const FETCH_FILTERED_ADS_ERR = 'FETCH_FILTERED_ADS_ERR'; | |||
| export const FETCH_FILTERED_ADS_SUCCESS = 'FETCH_FILTERED_ADS_SUCCESS'; | |||
| @@ -0,0 +1,14 @@ | |||
| import { | |||
| createErrorType, | |||
| createFetchType, | |||
| createLoadingType, | |||
| createSuccessType, | |||
| } from "../actionHelpers"; | |||
| const CREATE_AD_SCOPE = "CREATE_AD"; | |||
| export const CREATE_AD_REQ = createFetchType(CREATE_AD_SCOPE); | |||
| export const CREATE_AD_ERR = createErrorType(CREATE_AD_SCOPE); | |||
| export const CREATE_AD_SUCCESS = createSuccessType(CREATE_AD_SCOPE); | |||
| export const CREATE_AD_LOADING = createLoadingType(CREATE_AD_SCOPE); | |||
| @@ -0,0 +1,21 @@ | |||
| import { | |||
| CREATE_AD_ERR, | |||
| CREATE_AD_REQ, | |||
| CREATE_AD_SUCCESS, | |||
| } from "./createAdActionConstants"; | |||
| export const setCreateAdReq = (payload) => ({ | |||
| type: CREATE_AD_REQ, | |||
| payload, | |||
| }); | |||
| export const setCreateAdError = (payload) => ({ | |||
| type: CREATE_AD_ERR, | |||
| payload, | |||
| }); | |||
| export const setCreateAd = (payload) => ({ | |||
| type: CREATE_AD_SUCCESS, | |||
| payload, | |||
| }); | |||
| @@ -0,0 +1,5 @@ | |||
| export const FETCH_TECHNOLOGIES_REQ = "FETCH_TECHNOLOGIES_REQ"; | |||
| export const FETCH_TECHNOLOGIES_ERR = "FETCH_TECHNOLOGIES_ERR"; | |||
| export const FETCH_TECHNOLOGIES_SUCCESS = "FETCH_TECHNOLOGIES_SUCCESS"; | |||
| export const CHANGE_ISCHECKED_VALUE = "CHANGE_ISCHECKED_VALUE"; | |||
| @@ -0,0 +1,25 @@ | |||
| import { | |||
| FETCH_TECHNOLOGIES_ERR, | |||
| FETCH_TECHNOLOGIES_SUCCESS, | |||
| FETCH_TECHNOLOGIES_REQ, | |||
| CHANGE_ISCHECKED_VALUE | |||
| } from "./technologiesActionConstants"; | |||
| export const setTechnologiesReq = () => ({ | |||
| type: FETCH_TECHNOLOGIES_REQ, | |||
| }); | |||
| export const setTechnologiesError = (payload) => ({ | |||
| type: FETCH_TECHNOLOGIES_ERR, | |||
| payload, | |||
| }); | |||
| export const setTechnologies = (payload) => ({ | |||
| type: FETCH_TECHNOLOGIES_SUCCESS, | |||
| payload, | |||
| }); | |||
| export const changeIsCheckedValue = (payload) => ({ | |||
| type: CHANGE_ISCHECKED_VALUE, | |||
| payload, | |||
| }); | |||
| @@ -1,6 +1,8 @@ | |||
| import { | |||
| FETCH_ADS_ERR, | |||
| FETCH_ADS_SUCCESS, | |||
| FETCH_FILTERED_ADS_ERR, | |||
| FETCH_FILTERED_ADS_SUCCESS, | |||
| } from "../../actions/ads/adsActionConstants"; | |||
| import createReducer from "../../utils/createReducer"; | |||
| @@ -13,6 +15,8 @@ export default createReducer( | |||
| { | |||
| [FETCH_ADS_SUCCESS]: setStateAds, | |||
| [FETCH_ADS_ERR]: setAdsErrorMessage, | |||
| [FETCH_FILTERED_ADS_SUCCESS]: setStateFilteredAds, | |||
| [FETCH_FILTERED_ADS_ERR]: setFilteredAdsErrorMessage, | |||
| }, | |||
| initialState | |||
| ); | |||
| @@ -30,3 +34,17 @@ function setAdsErrorMessage(state, action) { | |||
| errorMessage: action.payload, | |||
| }; | |||
| } | |||
| function setStateFilteredAds(state, action) { | |||
| return { | |||
| ...state, | |||
| ads: action.payload, | |||
| }; | |||
| } | |||
| function setFilteredAdsErrorMessage(state, action) { | |||
| return { | |||
| ...state, | |||
| errorMessage: action.payload, | |||
| }; | |||
| } | |||
| @@ -0,0 +1,26 @@ | |||
| import { | |||
| CREATE_AD_SUCCESS, | |||
| CREATE_AD_ERR, | |||
| } from "../../actions/createAd/createAdActionConstants"; | |||
| import createReducer from "../../utils/createReducer"; | |||
| const initialState = { | |||
| ad: null, | |||
| errorMessage: "", | |||
| }; | |||
| export default createReducer( | |||
| { | |||
| [CREATE_AD_SUCCESS]: setStateCreateAd, | |||
| [CREATE_AD_ERR]: setStateErrorMessage, | |||
| }, | |||
| initialState | |||
| ); | |||
| function setStateCreateAd(state, action) { | |||
| return { ...state, ad: action.payload }; | |||
| } | |||
| function setStateErrorMessage(state, action) { | |||
| return { ...state, errorMessage: action.payload }; | |||
| } | |||
| @@ -1,19 +1,22 @@ | |||
| import { combineReducers } from 'redux'; | |||
| import loginReducer from './login/loginReducer'; | |||
| import loadingReducer from './loading/loadingReducer'; | |||
| import userReducer from './user/userReducer'; | |||
| import randomDataReducer from './randomData/randomDataReducer'; | |||
| import usersReducer from './user/usersReducer'; | |||
| import candidateReducer from './candidate/candidateReducer'; | |||
| import { combineReducers } from "redux"; | |||
| import loginReducer from "./login/loginReducer"; | |||
| import loadingReducer from "./loading/loadingReducer"; | |||
| import userReducer from "./user/userReducer"; | |||
| import randomDataReducer from "./randomData/randomDataReducer"; | |||
| import usersReducer from "./user/usersReducer"; | |||
| import candidateReducer from "./candidate/candidateReducer"; | |||
| import adsReducer from "./ad/adsReducer"; | |||
| import adReducer from "./ad/adReducer"; | |||
| import archiveAdsReducer from "./ad/archiveAdsReducer"; | |||
| import candidatesReducer from "./candidates/candidatesReducer"; | |||
| import processesReducer from './processes/processesReducer'; | |||
| import technologiesReducer from "./technology/technologiesReducer"; | |||
| import addAddTechnologiesReducer from "./technology/addAddTechnologiesReducer"; | |||
| import createAdReducer from "./ad/createAdReducer"; | |||
| import processesReducer from "./processes/processesReducer"; | |||
| import processReducer from "./processes/processReducer"; | |||
| import applicantWithProcessesReducer from "./processes/applicantWithProcessesReducer"; | |||
| import userDetailsReducer from './user/userDetailsReducer'; | |||
| import inviteUserReducer from './user/inviteUserReducer'; | |||
| import userDetailsReducer from "./user/userDetailsReducer"; | |||
| import inviteUserReducer from "./user/inviteUserReducer"; | |||
| export default combineReducers({ | |||
| login: loginReducer, | |||
| @@ -21,16 +24,17 @@ export default combineReducers({ | |||
| loading: loadingReducer, | |||
| randomData: randomDataReducer, | |||
| users: usersReducer, | |||
| candidate:candidateReducer, | |||
| candidate: candidateReducer, | |||
| ads: adsReducer, | |||
| ad: adReducer, | |||
| archiveAds: archiveAdsReducer, | |||
| technologies: technologiesReducer, | |||
| addAdTechnologies: addAddTechnologiesReducer, | |||
| createAd: createAdReducer, | |||
| candidates: candidatesReducer, | |||
| processes: processesReducer, | |||
| process: processReducer, | |||
| applicant: applicantWithProcessesReducer, | |||
| userDetails: userDetailsReducer, | |||
| invite: inviteUserReducer | |||
| invite: inviteUserReducer, | |||
| }); | |||
| @@ -0,0 +1,39 @@ | |||
| import createReducer from "../../utils/createReducer"; | |||
| import { | |||
| FETCH_ADD_AD_TECHNOLOGIES_ERR, | |||
| FETCH_ADD_AD_TECHNOLOGIES_SUCCESS, | |||
| CHANGE_ISCHECKED_VALUE_ADD_AD, | |||
| } from "../../actions/addAdTechnologies/addAdTechnologiesActionConstants"; | |||
| const initialState = { | |||
| technologies: [], | |||
| errorMessage: "", | |||
| }; | |||
| export default createReducer( | |||
| { | |||
| [FETCH_ADD_AD_TECHNOLOGIES_SUCCESS]: setStateTechnologiesAddAd, | |||
| [FETCH_ADD_AD_TECHNOLOGIES_ERR]: setStateAddAdErrorMessage, | |||
| [CHANGE_ISCHECKED_VALUE_ADD_AD]: setIsCheckedTechnologyAddAd, | |||
| }, | |||
| initialState | |||
| ); | |||
| function setStateTechnologiesAddAd(state, action) { | |||
| return { ...state, technologies: action.payload }; | |||
| } | |||
| function setStateAddAdErrorMessage(state, action) { | |||
| return { ...state, errorMessage: action.payload }; | |||
| } | |||
| function setIsCheckedTechnologyAddAd(state, action) { | |||
| return { | |||
| ...state, | |||
| technologies: state.technologies.map((tech) => | |||
| tech.technologyId === action.payload | |||
| ? { ...tech, isChecked: !tech.isChecked } | |||
| : tech | |||
| ), | |||
| }; | |||
| } | |||
| @@ -0,0 +1,45 @@ | |||
| import createReducer from "../../utils/createReducer"; | |||
| import { | |||
| FETCH_TECHNOLOGIES_SUCCESS, | |||
| FETCH_TECHNOLOGIES_ERR, | |||
| CHANGE_ISCHECKED_VALUE, | |||
| } from "../../actions/technologies/technologiesActionConstants"; | |||
| const initialState = { | |||
| technologies: [], | |||
| errorMessage: "", | |||
| }; | |||
| export default createReducer( | |||
| { | |||
| [FETCH_TECHNOLOGIES_SUCCESS]: setStateTechnologies, | |||
| [FETCH_TECHNOLOGIES_ERR]: setStateErrorMessage, | |||
| [CHANGE_ISCHECKED_VALUE]: setIsCheckedTechnology, | |||
| }, | |||
| initialState | |||
| ); | |||
| function setStateTechnologies(state, action) { | |||
| return { ...state, technologies: action.payload }; | |||
| } | |||
| function setStateErrorMessage(state, action) { | |||
| return { ...state, errorMessage: action.payload }; | |||
| } | |||
| function setIsCheckedTechnology(state, action) { | |||
| const tmpIndex = state.technologies.findIndex( | |||
| (x) => x.name === action.payload | |||
| ); | |||
| if (tmpIndex === -1) { | |||
| return state; | |||
| } | |||
| return { | |||
| ...state, | |||
| technologies: state.technologies.map((tech, index) => | |||
| tmpIndex === index ? { ...tech, isChecked: !tech.isChecked } : tech | |||
| ), | |||
| }; | |||
| } | |||
| @@ -3,16 +3,29 @@ import { | |||
| getAllAds, | |||
| getAdDetailsById, | |||
| getAllArchiveAds, | |||
| getAllFilteredAds, | |||
| createNewAd, | |||
| } from "../../request/adsRequest"; | |||
| import { setAds, setAdsError } from "../actions/ads/adsAction"; | |||
| import { | |||
| setAds, | |||
| setAdsError, | |||
| setFilteredAds, | |||
| setFilteredAdsError, | |||
| } from "../actions/ads/adsAction"; | |||
| import { setAd, setAdError } from "../actions/ad/adActions"; | |||
| import { | |||
| setArchiveAds, | |||
| setArchiveAdsError, | |||
| } from "../actions/archiveAds/archiveAdsActions"; | |||
| import { FETCH_ADS_REQ } from "../actions/ads/adsActionConstants"; | |||
| import { FETCH_FILTERED_ADS_REQ } from "../actions/ads/adsActionConstants"; | |||
| import { FETCH_AD_REQ } from "../actions/ad/adActionConstants"; | |||
| import { FETCH_ARCHIVE_ADS_REQ } from "../actions/archiveAds/archiveAdsActionConstants"; | |||
| import { CREATE_AD_REQ } from "../actions/createAd/createAdActionConstants"; | |||
| import { | |||
| setCreateAd, | |||
| setCreateAdError, | |||
| } from "../actions/createAd/createAdActions"; | |||
| export function* getAds() { | |||
| try { | |||
| @@ -23,6 +36,15 @@ export function* getAds() { | |||
| } | |||
| } | |||
| export function* getFilteredAds({ payload }) { | |||
| try { | |||
| const result = yield call(getAllFilteredAds, payload); | |||
| yield put(setFilteredAds(result.data)); | |||
| } catch (error) { | |||
| yield put(setFilteredAdsError(error)); | |||
| } | |||
| } | |||
| export function* getAd({ payload }) { | |||
| try { | |||
| const result = yield call(getAdDetailsById, payload.id); | |||
| @@ -41,10 +63,22 @@ export function* getArchiveAds() { | |||
| } | |||
| } | |||
| export function* createAd({ payload }) { | |||
| try { | |||
| const result = yield call(createNewAd, payload); | |||
| const ad = result.data; | |||
| yield put(setCreateAd(ad)); | |||
| } catch (error) { | |||
| yield put(setCreateAdError(error)); | |||
| } | |||
| } | |||
| export default function* adsSaga() { | |||
| yield all([ | |||
| takeLatest(FETCH_ADS_REQ, getAds), | |||
| takeLatest(FETCH_FILTERED_ADS_REQ, getFilteredAds), | |||
| takeLatest(FETCH_AD_REQ, getAd), | |||
| takeLatest(FETCH_ARCHIVE_ADS_REQ, getArchiveAds), | |||
| takeLatest(CREATE_AD_REQ, createAd), | |||
| ]); | |||
| } | |||
| @@ -2,6 +2,7 @@ import { all } from "redux-saga/effects"; | |||
| import adsSaga from "./adsSaga"; | |||
| import candidatesSaga from './candidatesSaga'; | |||
| import loginSaga from "./loginSaga"; | |||
| import technologiesSaga from "./technologiesSaga"; | |||
| import usersSaga from "./usersSaga"; | |||
| import processesSaga from "./processSaga"; | |||
| @@ -10,6 +11,7 @@ export default function* rootSaga() { | |||
| loginSaga(), | |||
| usersSaga(), | |||
| adsSaga(), | |||
| technologiesSaga(), | |||
| candidatesSaga(), | |||
| processesSaga(), | |||
| ]); | |||
| @@ -0,0 +1,36 @@ | |||
| import { all, call, put, takeLatest } from "redux-saga/effects"; | |||
| import { getAllTechnologies } from "../../request/technologiesRequest"; | |||
| import { FETCH_ADD_AD_TECHNOLOGIES_REQ } from "../actions/addAdTechnologies/addAdTechnologiesActionConstants"; | |||
| import { setTechnologiesAddAd } from "../actions/addAdTechnologies/addAdTechnologiesActions"; | |||
| import { FETCH_TECHNOLOGIES_REQ } from "../actions/technologies/technologiesActionConstants"; | |||
| import { | |||
| setTechnologies, | |||
| setTechnologiesError, | |||
| } from "../actions/technologies/technologiesActions"; | |||
| export function* getTechnologies() { | |||
| try { | |||
| const result = yield call(getAllTechnologies); | |||
| const resultData = result.data.map((res) => ({ ...res, isChecked: false })); | |||
| yield put(setTechnologies(resultData)); | |||
| } catch (error) { | |||
| yield put(setTechnologiesError(error)); | |||
| } | |||
| } | |||
| export function* getTechnologiesAddAd() { | |||
| try { | |||
| const result = yield call(getAllTechnologies); | |||
| const resultData = result.data.map((res) => ({ ...res, isChecked: false })); | |||
| yield put(setTechnologiesAddAd(resultData)); | |||
| } catch (error) { | |||
| yield put(setTechnologiesError(error)); | |||
| } | |||
| } | |||
| export default function* technologiesSaga() { | |||
| yield all([ | |||
| takeLatest(FETCH_TECHNOLOGIES_REQ, getTechnologies), | |||
| takeLatest(FETCH_ADD_AD_TECHNOLOGIES_REQ, getTechnologiesAddAd), | |||
| ]); | |||
| } | |||
| @@ -0,0 +1,13 @@ | |||
| import { createSelector } from "@reduxjs/toolkit"; | |||
| export const technologiesSelector = (state) => state.technologies; | |||
| export const selectTechnologies = createSelector( | |||
| technologiesSelector, | |||
| (state) => state.technologies | |||
| ); | |||
| export const selectTechnologiesError = createSelector( | |||
| technologiesSelector, | |||
| (state) => state.errorMessage | |||
| ); | |||