| import { fireEvent, render, screen, waitFor } from "@testing-library/react"; | |||||
| import * as redux from "react-redux"; | |||||
| import store from "../../store"; | |||||
| import { Router } from "react-router-dom"; | |||||
| import history from "../../store/utils/history"; | |||||
| import { mockState } from "../../mockState"; | |||||
| import ColorModeProvider from "../../context/ColorModeContext"; | |||||
| import AddAdModalFirstStage from "../../components/Ads/AddAdModalFirstStage"; | |||||
| describe("Add ad modals ui tests", () => { | |||||
| const props = { | |||||
| onIncrementStage: jest.fn(), | |||||
| onDecrementStage: jest.fn(), | |||||
| onCompleteFirstStage: jest.fn(), | |||||
| title: "Title", | |||||
| employmentType: "Work", | |||||
| workHour: "FullTime", | |||||
| setTitle: jest.fn(), | |||||
| setEmploymentType: jest.fn(), | |||||
| setWorkHour: jest.fn(), | |||||
| expiredAt: new Date(), | |||||
| setExpiredAt: jest.fn(), | |||||
| }; | |||||
| const cont = ( | |||||
| <redux.Provider store={store}> | |||||
| <Router history={history}> | |||||
| <ColorModeProvider> | |||||
| <AddAdModalFirstStage {...props} /> | |||||
| </ColorModeProvider> | |||||
| </Router> | |||||
| </redux.Provider> | |||||
| ); | |||||
| let spyOnUseSelector; | |||||
| beforeEach(() => { | |||||
| spyOnUseSelector = jest.spyOn(redux, "useSelector"); | |||||
| spyOnUseSelector.mockReturnValue(mockState.ads.ads); | |||||
| }); | |||||
| afterEach(() => { | |||||
| jest.restoreAllMocks(); | |||||
| }); | |||||
| it("Should render add ad modal stage", () => { | |||||
| const { container } = render(cont); | |||||
| const modal = container.getElementsByClassName("add-ad-modal-stage"); | |||||
| expect(modal).toBeDefined(); | |||||
| }); | |||||
| it("Should render work button", () => { | |||||
| render(cont); | |||||
| expect(screen.getByTestId("add-ad-modal-work-btn")).toBeDefined(); | |||||
| }); | |||||
| it("Should render intership button", () => { | |||||
| render(cont); | |||||
| expect(screen.getByTestId("add-ad-modal-intership-btn")).toBeDefined(); | |||||
| }); | |||||
| it("Should render parttime button", () => { | |||||
| render(cont); | |||||
| expect(screen.getByTestId("add-ad-modal-parttime-btn")).toBeDefined(); | |||||
| }); | |||||
| it("Should render fulltime button", () => { | |||||
| render(cont); | |||||
| expect(screen.getByTestId("add-ad-modal-fulltime-btn")).toBeDefined(); | |||||
| }); | |||||
| it("Should render add ad modal first stage actions", () => { | |||||
| const { container } = render(cont); | |||||
| expect( | |||||
| container.getElementsByClassName("add-ad-modal-action") | |||||
| ).toBeDefined(); | |||||
| }); | |||||
| it("Should change employment type to intership", () => { | |||||
| render(cont); | |||||
| fireEvent.click(screen.getByTestId("add-ad-modal-intership-btn")); | |||||
| expect(props.setEmploymentType).toHaveBeenCalled(); | |||||
| }); | |||||
| it("Should change employment type to work", () => { | |||||
| render(cont); | |||||
| fireEvent.click(screen.getByTestId("add-ad-modal-work-btn")); | |||||
| expect(props.setEmploymentType).toHaveBeenCalled(); | |||||
| }); | |||||
| it("Should change work hour to parttime", () => { | |||||
| render(cont); | |||||
| fireEvent.click(screen.getByTestId("add-ad-modal-parttime-btn")); | |||||
| expect(props.setWorkHour).toHaveBeenCalled(); | |||||
| }); | |||||
| it("Should change work hour to fulltime", () => { | |||||
| render(cont); | |||||
| fireEvent.click(screen.getByTestId("add-ad-modal-fulltime-btn")); | |||||
| expect(props.setWorkHour).toHaveBeenCalled(); | |||||
| }); | |||||
| it("Should call function on click go back button", async () => { | |||||
| render(cont); | |||||
| fireEvent.click(screen.getByTestId("add-ad-modal-go-back")); | |||||
| waitFor(() => expect(props.onDecrementStage).toHaveBeenCalled()); | |||||
| }); | |||||
| it("Should call function on click go forward button", () => { | |||||
| render(cont); | |||||
| fireEvent.click(screen.getByTestId("add-ad-modal-go-forward")); | |||||
| expect(props.onIncrementStage).toHaveBeenCalled(); | |||||
| }); | |||||
| it("Should change title text", () => { | |||||
| render(cont); | |||||
| fireEvent.change(screen.getByTestId("add-ad-modal-title"), { target: { value: ".NET DEVELOPER" } }) | |||||
| expect(props.setTitle).toBeCalled(); | |||||
| }); | |||||
| it("Should change expired at text", () => { | |||||
| render(cont); | |||||
| fireEvent.change(screen.getByTestId("add-ad-modal-expired-at"), { target: { value: "2020-05-24" } }) | |||||
| expect(props.setExpiredAt).toBeCalled(); | |||||
| }); | |||||
| }); |
| import { render, screen, waitFor, fireEvent } from "@testing-library/react"; | |||||
| import * as redux from "react-redux"; | |||||
| import store from "../../store"; | |||||
| import { Router } from "react-router-dom"; | |||||
| import history from "../../store/utils/history"; | |||||
| import { mockState } from "../../mockState"; | |||||
| import ColorModeProvider from "../../context/ColorModeContext"; | |||||
| import AddAdModalSecondStage from "../../components/Ads/AddAdModalSecondStage"; | |||||
| describe("Add ad modals ui tests", () => { | |||||
| const props = { | |||||
| onIncrementStage: jest.fn(), | |||||
| onDecrementStage: jest.fn(), | |||||
| technologies: [ | |||||
| { | |||||
| value: ".NET", | |||||
| isChecked: false, | |||||
| technologyId: 1, | |||||
| technologyType: "Backend", | |||||
| }, | |||||
| ], | |||||
| experience: 1, | |||||
| setExperience: jest.fn(), | |||||
| }; | |||||
| const cont = ( | |||||
| <redux.Provider store={store}> | |||||
| <Router history={history}> | |||||
| <ColorModeProvider> | |||||
| <AddAdModalSecondStage {...props} /> | |||||
| </ColorModeProvider> | |||||
| </Router> | |||||
| </redux.Provider> | |||||
| ); | |||||
| let spyOnUseSelector; | |||||
| beforeEach(() => { | |||||
| spyOnUseSelector = jest.spyOn(redux, "useSelector"); | |||||
| spyOnUseSelector.mockReturnValue(mockState.ads.ads); | |||||
| }); | |||||
| afterEach(() => { | |||||
| jest.restoreAllMocks(); | |||||
| }); | |||||
| it("Should render add ad modal second stage", () => { | |||||
| const { container } = render(cont); | |||||
| expect( | |||||
| container.getElementsByClassName("add-ad-modal-stages")[0] | |||||
| ).toBeDefined(); | |||||
| }); | |||||
| it("Should change experience input", async () => { | |||||
| render(cont); | |||||
| fireEvent.change(screen.getByTestId("add-ad-modal-experience"), { | |||||
| target: { value: 1 }, | |||||
| }); | |||||
| waitFor(() => expect(props.setExperience).toBeCalled()); | |||||
| }); | |||||
| it("Should call go back", async () => { | |||||
| render(cont); | |||||
| fireEvent.click(screen.getByTestId("add-ad-modal-second-go-back")); | |||||
| waitFor(() => expect(props.onDecrementStage).toBeCalled()); | |||||
| }); | |||||
| it("Should call go forward", async () => { | |||||
| render(cont); | |||||
| fireEvent.click(screen.getByTestId("add-ad-modal-second-go-forward")); | |||||
| waitFor(() => expect(props.onIncrementStage).toBeCalled()); | |||||
| }); | |||||
| }); |
| import facebook from "../../assets/images/facebook.png"; | import facebook from "../../assets/images/facebook.png"; | ||||
| import instagram from "../../assets/images/instagram.png"; | import instagram from "../../assets/images/instagram.png"; | ||||
| import { selectLogo } from "../../util/helpers/technologiesLogos"; | import { selectLogo } from "../../util/helpers/technologiesLogos"; | ||||
| import { useTranslation } from "react-i18next"; | |||||
| const Ad = ({ | const Ad = ({ | ||||
| title, | title, | ||||
| }) => { | }) => { | ||||
| const theme = useTheme(); | const theme = useTheme(); | ||||
| const matches = useMediaQuery(theme.breakpoints.down("sm")); | const matches = useMediaQuery(theme.breakpoints.down("sm")); | ||||
| const { t } = useTranslation(); | |||||
| return ( | return ( | ||||
| <div className={`ad-card ${className}`} onClick={onShowAdDetails}> | <div className={`ad-card ${className}`} onClick={onShowAdDetails}> | ||||
| </div> | </div> | ||||
| <div className="ad-card-experience"> | <div className="ad-card-experience"> | ||||
| <p>{minimumExperience}+ years of experience</p> | |||||
| <p> | |||||
| {minimumExperience}+ {t("common.experience")} | |||||
| </p> | |||||
| </div> | </div> | ||||
| {!matches && ( | {!matches && ( |
| import PropTypes from "prop-types"; | import PropTypes from "prop-types"; | ||||
| import { CANDIDATES_DETAILS_PAGE } from "../../constants/pages"; | import { CANDIDATES_DETAILS_PAGE } from "../../constants/pages"; | ||||
| import { useHistory } from "react-router-dom"; | import { useHistory } from "react-router-dom"; | ||||
| import { useTranslation } from "react-i18next"; | |||||
| const AdDetailsCandidateCard = ({ | const AdDetailsCandidateCard = ({ | ||||
| className, | className, | ||||
| cv, | cv, | ||||
| }) => { | }) => { | ||||
| const history = useHistory(); | const history = useHistory(); | ||||
| const { t } = useTranslation(); | |||||
| return ( | return ( | ||||
| <div data-testid="ad-details-candidate" className={`ad-details-candidate ${className}`}> | |||||
| <div | |||||
| data-testid="ad-details-candidate" | |||||
| className={`ad-details-candidate ${className}`} | |||||
| > | |||||
| <div className="ad-details-candidate-date"> | <div className="ad-details-candidate-date"> | ||||
| <p>{new Date().toLocaleDateString()}</p> | <p>{new Date().toLocaleDateString()}</p> | ||||
| </div> | </div> | ||||
| <div className="ad-details-candidate-title"> | <div className="ad-details-candidate-title"> | ||||
| <h3 data-testid="ad-details-candidate-title-link" onClick={() => history.push(CANDIDATES_DETAILS_PAGE.replace(":id", id))}> | |||||
| <h3 | |||||
| data-testid="ad-details-candidate-title-link" | |||||
| onClick={() => | |||||
| history.push(CANDIDATES_DETAILS_PAGE.replace(":id", id)) | |||||
| } | |||||
| > | |||||
| {firstName} {lastName} | {firstName} {lastName} | ||||
| </h3> | </h3> | ||||
| </div> | </div> | ||||
| <div className="ad-details-candidate-experience"> | <div className="ad-details-candidate-experience"> | ||||
| {experience > 0 ? ( | {experience > 0 ? ( | ||||
| <p>{experience}+ years of experience</p> | |||||
| <p> | |||||
| {experience}+ {t("common.experience")} | |||||
| </p> | |||||
| ) : ( | ) : ( | ||||
| <p>No experience</p> | |||||
| <p>{t("common.noExperience")}</p> | |||||
| )} | )} | ||||
| </div> | </div> | ||||
| <div className="ad-details-candidate-buttons"> | <div className="ad-details-candidate-buttons"> |
| import { useDispatch } from "react-redux"; | import { useDispatch } from "react-redux"; | ||||
| import { setFilteredAdsReq } from "../../store/actions/ads/adsAction"; | import { setFilteredAdsReq } from "../../store/actions/ads/adsAction"; | ||||
| import { useHistory } from "react-router-dom"; | import { useHistory } from "react-router-dom"; | ||||
| import { useTranslation } from "react-i18next"; | |||||
| const AdFilters = ({ open, handleClose, technologies }) => { | const AdFilters = ({ open, handleClose, technologies }) => { | ||||
| const [sliderValue, setSliderValue] = useState([0, 10]); | const [sliderValue, setSliderValue] = useState([0, 10]); | ||||
| const [workHour, setWorkHour] = useState("FullTime"); | const [workHour, setWorkHour] = useState("FullTime"); | ||||
| const dispatch = useDispatch(); | const dispatch = useDispatch(); | ||||
| const history = useHistory(); | const history = useHistory(); | ||||
| const { t } = useTranslation(); | |||||
| const handleSliderChange = (_, newValue) => { | const handleSliderChange = (_, newValue) => { | ||||
| setSliderValue(newValue); | setSliderValue(newValue); | ||||
| <div className="ad-filters-header-container"> | <div className="ad-filters-header-container"> | ||||
| <div className="ad-filters-header"> | <div className="ad-filters-header"> | ||||
| <img src={filterIcon} alt="filter_icon" /> | <img src={filterIcon} alt="filter_icon" /> | ||||
| <h3>Filteri</h3> | |||||
| <h3>{t("filters.filters")}</h3> | |||||
| <p> | <p> | ||||
| <sub>| Oglasi</sub> | |||||
| <sub>| {t("ads.ads")}</sub> | |||||
| </p> | </p> | ||||
| </div> | </div> | ||||
| <div className="ad-filters-header-close" onClick={handleClose}> | <div className="ad-filters-header-close" onClick={handleClose}> | ||||
| </div> | </div> | ||||
| <div className="ad-filters-experience"> | <div className="ad-filters-experience"> | ||||
| <div className="ad-filters-sub-title"> | <div className="ad-filters-sub-title"> | ||||
| <p>Godine iskustva</p> | |||||
| <p>{t("filters.experience")}</p> | |||||
| </div> | </div> | ||||
| <div className="ad-filters-experience-slider"> | <div className="ad-filters-experience-slider"> | ||||
| <Slider | <Slider | ||||
| </div> | </div> | ||||
| <div className="ad-filters-technologies"> | <div className="ad-filters-technologies"> | ||||
| <div className="ad-filters-sub-title"> | <div className="ad-filters-sub-title"> | ||||
| <p>Tehnologije</p> | |||||
| <p>{t("filters.tecnologies")}</p> | |||||
| </div> | </div> | ||||
| <div className="ad-filters-technologies-checkboxes"> | <div className="ad-filters-technologies-checkboxes"> | ||||
| <FormGroup> | <FormGroup> | ||||
| </div> | </div> | ||||
| <div className="ad-filters-technologies"> | <div className="ad-filters-technologies"> | ||||
| <div className="ad-filters-sub-title"> | <div className="ad-filters-sub-title"> | ||||
| <p>Tip zaposlenja</p> | |||||
| <p>{t("filters.employmentType")}</p> | |||||
| </div> | </div> | ||||
| <div className="ad-filters-employment-type"> | <div className="ad-filters-employment-type"> | ||||
| <button | <button | ||||
| }`} | }`} | ||||
| onClick={employmentTypeHandler.bind(this, "Intership")} | onClick={employmentTypeHandler.bind(this, "Intership")} | ||||
| > | > | ||||
| Intership | |||||
| {t("filters.internship")} | |||||
| </button> | </button> | ||||
| <button | <button | ||||
| className={`c-btn ${ | className={`c-btn ${ | ||||
| }`} | }`} | ||||
| onClick={employmentTypeHandler.bind(this, "Work")} | onClick={employmentTypeHandler.bind(this, "Work")} | ||||
| > | > | ||||
| Posao | |||||
| {t("filters.work")} | |||||
| </button> | </button> | ||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| <div className="ad-filters-technologies"> | <div className="ad-filters-technologies"> | ||||
| <div className="ad-filters-sub-title"> | <div className="ad-filters-sub-title"> | ||||
| <p>Radno vreme</p> | |||||
| <p>{t("filters.workHour")}</p> | |||||
| </div> | </div> | ||||
| <div className="ad-filters-employment-type"> | <div className="ad-filters-employment-type"> | ||||
| <button | <button | ||||
| }`} | }`} | ||||
| onClick={workHourHandler.bind(this, "PartTime")} | onClick={workHourHandler.bind(this, "PartTime")} | ||||
| > | > | ||||
| Part-time | |||||
| {t("filters.partTime")} | |||||
| </button> | </button> | ||||
| <button | <button | ||||
| className={`c-btn ${ | className={`c-btn ${ | ||||
| }`} | }`} | ||||
| onClick={workHourHandler.bind(this, "FullTime")} | onClick={workHourHandler.bind(this, "FullTime")} | ||||
| > | > | ||||
| Full-time | |||||
| {t("filters.fullTime")} | |||||
| </button> | </button> | ||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| <div className="ad-filters-search"> | <div className="ad-filters-search"> | ||||
| <button onClick={onSubmitFilters} className="c-btn c-btn--primary" data-testid="ad-filters-submit"> | |||||
| Pretrazi | |||||
| <button | |||||
| onClick={onSubmitFilters} | |||||
| className="c-btn c-btn--primary" | |||||
| data-testid="ad-filters-submit" | |||||
| > | |||||
| {t("filters.search")} | |||||
| </button> | </button> | ||||
| </div> | </div> | ||||
| </div> | </div> |
| 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 { | |||||
| resetIsCheckedAddAdValue, | |||||
| setTechnologiesAddAdReq, | |||||
| } from "../../store/actions/addAdTechnologies/addAdTechnologiesActions"; | |||||
| import { setCreateAdReq } from "../../store/actions/createAd/createAdActions"; | |||||
| const style = { | |||||
| position: "absolute", | |||||
| top: "50%", | |||||
| left: "50%", | |||||
| transform: "translate(-50%, -50%)", | |||||
| width: 400, | |||||
| bgcolor: "background.paper", | |||||
| border: "2px solid #000", | |||||
| boxShadow: 24, | |||||
| }; | |||||
| /* istanbul ignore file */ | |||||
| 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 technologies = useSelector( | |||||
| (state) => state.addAdTechnologies.technologies | |||||
| ); | |||||
| const dispatch = useDispatch(); | |||||
| useEffect(() => { | |||||
| dispatch(setTechnologiesAddAdReq()); | |||||
| }, []); | |||||
| const onSuccessAddAd = () => { | |||||
| setStage(1); | |||||
| setTitle(""); | |||||
| setEmploymentType("Work"); | |||||
| setWorkHour("FullTime"); | |||||
| setExpiredAt(""); | |||||
| setExperience(0); | |||||
| dispatch(resetIsCheckedAddAdValue()); | |||||
| }; | |||||
| const addAdHandler = ( | |||||
| htmlKeyResponsibilities, | |||||
| htmlRequirements, | |||||
| htmlOffer | |||||
| ) => { | |||||
| const technologiesIds = technologies | |||||
| .filter((x) => x.isChecked === true) | |||||
| .map((x) => x.technologyId); | |||||
| dispatch( | |||||
| setCreateAdReq({ | |||||
| title, | |||||
| minimumExperience: experience, | |||||
| createdAt: new Date(), | |||||
| expiredAt, | |||||
| keyResponsibilities: htmlKeyResponsibilities, | |||||
| requirements: htmlRequirements, | |||||
| offer: htmlOffer, | |||||
| workHour, | |||||
| employmentType, | |||||
| technologiesIds, | |||||
| onSuccessAddAd, | |||||
| }) | |||||
| ); | |||||
| 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} | |||||
| onClose={handleClose} | |||||
| aria-labelledby="parent-modal-title" | |||||
| aria-describedby="parent-modal-description" | |||||
| > | |||||
| <Box | |||||
| sx={{ ...style, width: 512 }} | |||||
| className="add-ad-modal" | |||||
| data-testid="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} | |||||
| onAddAd={addAdHandler} | |||||
| /> | |||||
| )} | |||||
| </div> | |||||
| </Box> | |||||
| </Modal> | |||||
| ); | |||||
| }; | |||||
| AddAdModal.propTypes = { | |||||
| open: PropType.any, | |||||
| handleClose: PropType.func, | |||||
| }; | |||||
| export default AddAdModal; |
| import React from "react"; | |||||
| import PropTypes from "prop-types"; | |||||
| /* istanbul ignore file */ | |||||
| 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 | |||||
| data-testid="add-ad-modal-title" | |||||
| 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 | |||||
| data-testid="add-ad-modal-work-btn" | |||||
| className={`c-btn ${ | |||||
| employmentType === "Work" | |||||
| ? "c-btn c-btn--primary" | |||||
| : "c-btn--primary-outlined" | |||||
| }`} | |||||
| onClick={employmentTypeHandler.bind(this, "Work")} | |||||
| > | |||||
| Posao | |||||
| </button> | |||||
| <button | |||||
| data-testid="add-ad-modal-intership-btn" | |||||
| 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 | |||||
| data-testid="add-ad-modal-parttime-btn" | |||||
| className={`c-btn ${ | |||||
| workHour === "PartTime" | |||||
| ? "c-btn c-btn--primary" | |||||
| : "c-btn--primary-outlined" | |||||
| }`} | |||||
| onClick={workHourHandler.bind(this, "PartTime")} | |||||
| > | |||||
| Part-time | |||||
| </button> | |||||
| <button | |||||
| data-testid="add-ad-modal-fulltime-btn" | |||||
| 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 | |||||
| data-testid="add-ad-modal-expired-at" | |||||
| type="date" | |||||
| placeholder="ex" | |||||
| value={expiredAt} | |||||
| onChange={(e) => setExpiredAt(e.target.value)} | |||||
| /> | |||||
| </div> | |||||
| </div> | |||||
| <div className="add-ad-modal-actions"> | |||||
| <button | |||||
| data-testid="add-ad-modal-go-back" | |||||
| className="c-btn c-btn--primary-outlined" | |||||
| disabled | |||||
| onClick={onDecrementStage} | |||||
| > | |||||
| NAZAD | |||||
| </button> | |||||
| <button | |||||
| className="c-btn c-btn--primary" | |||||
| onClick={completeStageHandler} | |||||
| data-testid="add-ad-modal-go-forward" | |||||
| > | |||||
| 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; |
| 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} | |||||
| className="add-ad-modal-technology" | |||||
| 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} | |||||
| data-testid="add-ad-modal-experience" | |||||
| onChange={(e) => setExperience(e.target.value)} | |||||
| /> | |||||
| </div> | |||||
| </div> | |||||
| <div className="add-ad-modal-actions"> | |||||
| <button | |||||
| className="c-btn c-btn--primary-outlined" | |||||
| data-testid="add-ad-modal-second-go-back" | |||||
| onClick={onDecrementStage} | |||||
| > | |||||
| NAZAD | |||||
| </button> | |||||
| <button | |||||
| className="c-btn c-btn--primary" | |||||
| data-testid="add-ad-modal-second-go-forward" | |||||
| 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; |
| import React, { useRef } from "react"; | |||||
| import PropTypes from "prop-types"; | |||||
| import { Editor } from "@tinymce/tinymce-react"; | |||||
| /* istanbul ignore file */ | |||||
| const AddAdModalThirdStage = ({ onDecrementStage, onAddAd }) => { | |||||
| const editorKeyResponsibilitiesRef = useRef(); | |||||
| const editorRequirementsRef = useRef(); | |||||
| const editorOfferRef = useRef(); | |||||
| const completeStageHandler = () => { | |||||
| if ( | |||||
| editorKeyResponsibilitiesRef.current.getContent() === "" || | |||||
| editorRequirementsRef.current.getContent() === "" || | |||||
| editorOfferRef.current.getContent() === "" | |||||
| ) { | |||||
| return; | |||||
| } | |||||
| onAddAd( | |||||
| editorKeyResponsibilitiesRef.current.getContent(), | |||||
| editorRequirementsRef.current.getContent(), | |||||
| editorOfferRef.current.getContent() | |||||
| ); | |||||
| }; | |||||
| return ( | |||||
| <div className="add-ad-modal-stage"> | |||||
| <div> | |||||
| <div className="add-ad-modal-stage-sub-card-third"> | |||||
| <label>Glavna zaduzenja</label> | |||||
| <Editor | |||||
| onInit={(evt, editor) => | |||||
| (editorKeyResponsibilitiesRef.current = editor) | |||||
| } | |||||
| style={{ height: "1rem !important" }} | |||||
| /> | |||||
| </div> | |||||
| <div className="add-ad-modal-stage-sub-card-third"> | |||||
| <label>Uslovi</label> | |||||
| <Editor | |||||
| onInit={(evt, editor) => (editorRequirementsRef.current = editor)} | |||||
| style={{ height: "1rem !important" }} | |||||
| /> | |||||
| </div> | |||||
| <div className="add-ad-modal-stage-sub-card-third"> | |||||
| <label>Sta nudimo</label> | |||||
| <Editor | |||||
| onInit={(evt, editor) => (editorOfferRef.current = editor)} | |||||
| style={{ height: "1rem !important" }} | |||||
| /> | |||||
| </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, | |||||
| onAddAd: PropTypes.func, | |||||
| }; | |||||
| export default AddAdModalThirdStage; |
| import { applyForAdReq } from "../../store/actions/applyForAd/applyForAdActions"; | import { applyForAdReq } from "../../store/actions/applyForAd/applyForAdActions"; | ||||
| import { useHistory } from "react-router-dom"; | import { useHistory } from "react-router-dom"; | ||||
| import { ADS_PAGE } from "../../constants/pages"; | import { ADS_PAGE } from "../../constants/pages"; | ||||
| import { useTranslation } from "react-i18next"; | |||||
| const ApplyForAd = ({ open, title, adId, onCloseModal }) => { | const ApplyForAd = ({ open, title, adId, onCloseModal }) => { | ||||
| const [stage, setStage] = useState(1); | const [stage, setStage] = useState(1); | ||||
| const [phoneNumber, setPhoneNumber] = useState(""); | const [phoneNumber, setPhoneNumber] = useState(""); | ||||
| const [mappedTechnologies, setMappedTechnologies] = useState([]); | const [mappedTechnologies, setMappedTechnologies] = useState([]); | ||||
| const [experience, setExperience] = useState(0); | const [experience, setExperience] = useState(0); | ||||
| const [professionalQualification, setProfessionalQualification] = useState(""); | |||||
| const [professionalQualification, setProfessionalQualification] = | |||||
| useState(""); | |||||
| const [linkedinLink, setLinkedinLink] = useState(""); | const [linkedinLink, setLinkedinLink] = useState(""); | ||||
| const [githubLink, setGithubLink] = useState(""); | const [githubLink, setGithubLink] = useState(""); | ||||
| const [email, setEmail] = useState(""); | const [email, setEmail] = useState(""); | ||||
| const technologies = useSelector(selectTechnologies); | const technologies = useSelector(selectTechnologies); | ||||
| const dispatch = useDispatch(); | const dispatch = useDispatch(); | ||||
| const history = useHistory(); | const history = useHistory(); | ||||
| const { t } = useTranslation(); | |||||
| useEffect(() => { | useEffect(() => { | ||||
| dispatch(setTechnologiesReq()); | dispatch(setTechnologiesReq()); | ||||
| <img src={briefcaseIcon} alt="plus" /> | <img src={briefcaseIcon} alt="plus" /> | ||||
| </div> | </div> | ||||
| <div className="apply-for-ad-header-left-image-title"> | <div className="apply-for-ad-header-left-image-title"> | ||||
| <p>Prijavi se</p> | |||||
| <p>{t("ads.signUp")}</p> | |||||
| </div> | </div> | ||||
| <div className="apply-for-ad-header-left-image-title-sub"> | <div className="apply-for-ad-header-left-image-title-sub"> | ||||
| <sub> | {title}</sub> | <sub> | {title}</sub> |
| import React from "react"; | import React from "react"; | ||||
| import PropTypes from "prop-types"; | import PropTypes from "prop-types"; | ||||
| import { useTranslation } from "react-i18next"; | |||||
| const ApplyForAdFirstStage = ({ | const ApplyForAdFirstStage = ({ | ||||
| firstName, | firstName, | ||||
| phoneNumber.length === 0 || | phoneNumber.length === 0 || | ||||
| gender.length === 0; | gender.length === 0; | ||||
| const { t } = useTranslation(); | |||||
| return ( | return ( | ||||
| <div> | <div> | ||||
| <div className="apply-for-ad-modal-form-control"> | <div className="apply-for-ad-modal-form-control"> | ||||
| <label>Ime</label> | |||||
| <label>{t("common.name")}</label> | |||||
| <input | <input | ||||
| type="text" | type="text" | ||||
| placeholder="ex. Petar" | placeholder="ex. Petar" | ||||
| /> | /> | ||||
| </div> | </div> | ||||
| <div className="apply-for-ad-modal-form-control"> | <div className="apply-for-ad-modal-form-control"> | ||||
| <label>Prezime</label> | |||||
| <label>{t("common.lastName")}</label> | |||||
| <input | <input | ||||
| type="text" | type="text" | ||||
| placeholder="ex. Petrovic" | placeholder="ex. Petrovic" | ||||
| /> | /> | ||||
| </div> | </div> | ||||
| <div className="apply-for-ad-modal-form-control"> | <div className="apply-for-ad-modal-form-control"> | ||||
| <label>Pol</label> | |||||
| <label>{t("common.gender")}</label> | |||||
| <div style={{ display: "flex" }}> | <div style={{ display: "flex" }}> | ||||
| <div style={{ display: "flex" }}> | <div style={{ display: "flex" }}> | ||||
| <input | <input | ||||
| checked={gender === "Muski"} | checked={gender === "Muski"} | ||||
| onChange={(e) => setGender(e.target.value)} | onChange={(e) => setGender(e.target.value)} | ||||
| /> | /> | ||||
| <p style={{ marginLeft: "5px" }}>Muski</p> | |||||
| <p style={{ marginLeft: "5px" }}>{t("common.male")}</p> | |||||
| </div> | </div> | ||||
| <div style={{ display: "flex", marginLeft: "50px" }}> | <div style={{ display: "flex", marginLeft: "50px" }}> | ||||
| <input | <input | ||||
| checked={gender === "Zenski"} | checked={gender === "Zenski"} | ||||
| onChange={(e) => setGender(e.target.value)} | onChange={(e) => setGender(e.target.value)} | ||||
| /> | /> | ||||
| <p style={{ marginLeft: "5px" }}>Zenski</p> | |||||
| <p style={{ marginLeft: "5px" }}>{t("common.female")}</p> | |||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| <div className="apply-for-ad-modal-form-control"> | <div className="apply-for-ad-modal-form-control"> | ||||
| <label>Datum Rodjenja</label> | |||||
| <label>{t("common.dateOfBirth")}</label> | |||||
| <input | <input | ||||
| type="date" | type="date" | ||||
| placeholder="ex. Datum rodjenja" | placeholder="ex. Datum rodjenja" | ||||
| /> | /> | ||||
| </div> | </div> | ||||
| <div className="apply-for-ad-modal-form-control"> | <div className="apply-for-ad-modal-form-control"> | ||||
| <label>Broj telefona</label> | |||||
| <label>{t("common.phoneNumber")}</label> | |||||
| <input | <input | ||||
| type="text" | type="text" | ||||
| placeholder="ex. +381/60/000/0000" | placeholder="ex. +381/60/000/0000" | ||||
| /> | /> | ||||
| </div> | </div> | ||||
| <div className="apply-for-ad-buttons"> | <div className="apply-for-ad-buttons"> | ||||
| <button disabled>NAZAD</button> | |||||
| <button disabled={disabled} | |||||
| data-testid="apply-for-ad-modal-go-forward-button" onClick={() => onIncreaseStage()}> | |||||
| NASTAVI | |||||
| <button disabled>{t("common.back")}</button> | |||||
| <button | |||||
| disabled={disabled} | |||||
| data-testid="apply-for-ad-modal-go-forward-button" | |||||
| onClick={() => onIncreaseStage()} | |||||
| > | |||||
| {t("common.continue")} | |||||
| </button> | </button> | ||||
| </div> | </div> | ||||
| </div> | </div> |
| import React, { useState } from "react"; | import React, { useState } from "react"; | ||||
| import PropTypes from "prop-types"; | import PropTypes from "prop-types"; | ||||
| import uploadIcon from "../../assets/images/upload.png"; | import uploadIcon from "../../assets/images/upload.png"; | ||||
| import { useTranslation } from "react-i18next"; | |||||
| const ApplyForAdFourthStage = ({ | const ApplyForAdFourthStage = ({ | ||||
| coverLetter, | coverLetter, | ||||
| onFinishedFourStages, | onFinishedFourStages, | ||||
| }) => { | }) => { | ||||
| const [dropzoneActive, setDropzoneActive] = useState(false); | const [dropzoneActive, setDropzoneActive] = useState(false); | ||||
| const {t} = useTranslation() | |||||
| const disabled = pdfFile === null || coverLetter === ""; | const disabled = pdfFile === null || coverLetter === ""; | ||||
| ) : ( | ) : ( | ||||
| <> | <> | ||||
| <p> | <p> | ||||
| Prevuci .pdf dokument u ovom delu ekrana ili | |||||
| {t("ads.dragPdf1")} | |||||
| <label | <label | ||||
| htmlFor="upload-file" | htmlFor="upload-file" | ||||
| style={{ | style={{ | ||||
| color: "#1E92D0", | color: "#1E92D0", | ||||
| }} | }} | ||||
| > | > | ||||
| Pretrazi | |||||
| {t("common.search")} | |||||
| </label> | </label> | ||||
| na racunaru | |||||
| {t("ads.dragPdf2")} | |||||
| </p> | </p> | ||||
| <input | <input | ||||
| type="file" | type="file" | ||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| <div className="apply-for-ad-modal-form-control"> | <div className="apply-for-ad-modal-form-control"> | ||||
| <label>Propratno pismo (Opciono)</label> | |||||
| <label>{t("ads.coverLetter")}</label> | |||||
| <textarea | <textarea | ||||
| value={coverLetter} | value={coverLetter} | ||||
| onChange={(e) => setCoverLetter(e.target.value)} | onChange={(e) => setCoverLetter(e.target.value)} | ||||
| onClick={onDecreaseStage} | onClick={onDecreaseStage} | ||||
| data-testid="apply-for-ad-modal-fourth-stage-go-back-button" | data-testid="apply-for-ad-modal-fourth-stage-go-back-button" | ||||
| > | > | ||||
| NAZAD | |||||
| {t("common.back")} | |||||
| </button> | </button> | ||||
| <button | <button | ||||
| disabled={disabled} | disabled={disabled} | ||||
| data-testid="apply-for-ad-modal-fourth-stage-go-forward-button" | data-testid="apply-for-ad-modal-fourth-stage-go-forward-button" | ||||
| onClick={onFinishedFourStages} | onClick={onFinishedFourStages} | ||||
| > | > | ||||
| PRIJAVI SE | |||||
| {t("ads.signUp")} | |||||
| </button> | </button> | ||||
| </div> | </div> | ||||
| </div> | </div> |
| import React from "react"; | import React from "react"; | ||||
| import PropTypes from "prop-types"; | import PropTypes from "prop-types"; | ||||
| import { Checkbox, FormControlLabel } from "@mui/material"; | import { Checkbox, FormControlLabel } from "@mui/material"; | ||||
| import { useTranslation } from "react-i18next"; | |||||
| const ApplyForAdSecondStage = ({ | const ApplyForAdSecondStage = ({ | ||||
| professionalQualification, | professionalQualification, | ||||
| onIncreaseStage, | onIncreaseStage, | ||||
| onDecreaseStage, | onDecreaseStage, | ||||
| }) => { | }) => { | ||||
| const {t} = useTranslation() | |||||
| let disabled = true; | let disabled = true; | ||||
| let isTechnologySelected = false; | let isTechnologySelected = false; | ||||
| if (technologies.length > 0) { | if (technologies.length > 0) { | ||||
| return ( | return ( | ||||
| <div data-testid="apply-for-ad-second-stage"> | <div data-testid="apply-for-ad-second-stage"> | ||||
| <div className="apply-for-ad-header-title"> | <div className="apply-for-ad-header-title"> | ||||
| <p>Strucna sprema</p> | |||||
| <p>{t("ads.professionalQualification")}</p> | |||||
| </div> | </div> | ||||
| <div className="apply-for-ad-modal-form-control"> | <div className="apply-for-ad-modal-form-control"> | ||||
| <input | <input | ||||
| /> | /> | ||||
| </div> | </div> | ||||
| <div className="apply-for-ad-header-title"> | <div className="apply-for-ad-header-title"> | ||||
| <p>Tehnologije koje znaš</p> | |||||
| <p>{t("ads.technologies")}</p> | |||||
| </div> | </div> | ||||
| <div className="apply-for-ad-header-sub"> | <div className="apply-for-ad-header-sub"> | ||||
| <div className="apply-for-ad-header-sub-group"> | <div className="apply-for-ad-header-sub-group"> | ||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| <div className="apply-for-ad-header-sub-group"> | <div className="apply-for-ad-header-sub-group"> | ||||
| <label>Others</label> | |||||
| <label>{t("ads.others")}</label> | |||||
| <div className="apply-for-ad-header-sub-group-checkboxes"> | <div className="apply-for-ad-header-sub-group-checkboxes"> | ||||
| {technologies | {technologies | ||||
| .filter((x) => x.technologyType === "Other") | .filter((x) => x.technologyType === "Other") | ||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| <div className="apply-for-ad-modal-form-control"> | <div className="apply-for-ad-modal-form-control"> | ||||
| <label>Godine iskustva</label> | |||||
| <label>{t("filters.experience")}</label> | |||||
| <input | <input | ||||
| type="number" | type="number" | ||||
| placeholder="ex. 3 godine iskustva" | placeholder="ex. 3 godine iskustva" | ||||
| /> | /> | ||||
| </div> | </div> | ||||
| <div className="apply-for-ad-buttons"> | <div className="apply-for-ad-buttons"> | ||||
| <button onClick={onDecreaseStage}>NAZAD</button> | |||||
| <button onClick={onDecreaseStage}>{t("common.back")}</button> | |||||
| <button onClick={onIncreaseStage} disabled={disabled}> | <button onClick={onIncreaseStage} disabled={disabled}> | ||||
| NASTAVI | |||||
| {t("common.continue")} | |||||
| </button> | </button> | ||||
| </div> | </div> | ||||
| </div> | </div> |
| import React from "react"; | import React from "react"; | ||||
| import PropTypes from "prop-types"; | import PropTypes from "prop-types"; | ||||
| import { useTranslation } from "react-i18next"; | |||||
| const ApplyForAdThirdStage = ({ | const ApplyForAdThirdStage = ({ | ||||
| onIncreaseStage, | onIncreaseStage, | ||||
| githubLink.length === 0 || | githubLink.length === 0 || | ||||
| bitBucketLink.length === 0; | bitBucketLink.length === 0; | ||||
| const {t} = useTranslation() | |||||
| return ( | return ( | ||||
| <div> | <div> | ||||
| <div className="apply-for-ad-header-title"> | <div className="apply-for-ad-header-title"> | ||||
| <p>Društvene mreže</p> | |||||
| <p>{t("common.socialNetwork")}</p> | |||||
| </div> | </div> | ||||
| <div className="apply-for-ad-modal-form-control"> | <div className="apply-for-ad-modal-form-control"> | ||||
| <label>LinkedIn</label> | <label>LinkedIn</label> | ||||
| <label>GitHub</label> | <label>GitHub</label> | ||||
| <input | <input | ||||
| type="text" | type="text" | ||||
| value={githubLink}data-testid="apply-for-ad-modal-third-stage-github-input" | |||||
| value={githubLink} | |||||
| data-testid="apply-for-ad-modal-third-stage-github-input" | |||||
| onChange={(e) => setGithubLink(e.target.value)} | onChange={(e) => setGithubLink(e.target.value)} | ||||
| placeholder="ex. https://www.github.com/petarpetrovic" | placeholder="ex. https://www.github.com/petarpetrovic" | ||||
| /> | /> | ||||
| <label>BitBucket</label> | <label>BitBucket</label> | ||||
| <input | <input | ||||
| type="text" | type="text" | ||||
| value={bitBucketLink}data-testid="apply-for-ad-modal-third-stage-bitbucket-input" | |||||
| value={bitBucketLink} | |||||
| data-testid="apply-for-ad-modal-third-stage-bitbucket-input" | |||||
| onChange={(e) => setBitBucketLink(e.target.value)} | onChange={(e) => setBitBucketLink(e.target.value)} | ||||
| placeholder="ex. https://developer.atlassian.com/user/petarapetrovic" | placeholder="ex. https://developer.atlassian.com/user/petarapetrovic" | ||||
| /> | /> | ||||
| <label>Email</label> | <label>Email</label> | ||||
| <input | <input | ||||
| type="email" | type="email" | ||||
| value={email}data-testid="apply-for-ad-modal-third-stage-email-input" | |||||
| value={email} | |||||
| data-testid="apply-for-ad-modal-third-stage-email-input" | |||||
| onChange={(e) => setEmail(e.target.value)} | onChange={(e) => setEmail(e.target.value)} | ||||
| placeholder="ex. petar.petrovic@dilig.net" | placeholder="ex. petar.petrovic@dilig.net" | ||||
| /> | /> | ||||
| </div> | </div> | ||||
| <div className="apply-for-ad-buttons"> | <div className="apply-for-ad-buttons"> | ||||
| <button onClick={onDecreaseStage}data-testid="apply-for-ad-modal-third-stage-go-back-button">NAZAD</button> | |||||
| <button onClick={onIncreaseStage} disabled={disabled}data-testid="apply-for-ad-modal-third-stage-go-forward-button"> | |||||
| NASTAVI | |||||
| <button | |||||
| onClick={onDecreaseStage} | |||||
| data-testid="apply-for-ad-modal-third-stage-go-back-button" | |||||
| > | |||||
| {t("common.back")} | |||||
| </button> | |||||
| <button | |||||
| onClick={onIncreaseStage} | |||||
| disabled={disabled} | |||||
| data-testid="apply-for-ad-modal-third-stage-go-forward-button" | |||||
| > | |||||
| {t("common.continue")} | |||||
| </button> | </button> | ||||
| </div> | </div> | ||||
| </div> | </div> |
| import React from "react"; | import React from "react"; | ||||
| import PropTypes from "prop-types"; | import PropTypes from "prop-types"; | ||||
| import { selectLogo } from "../../util/helpers/technologiesLogos"; | import { selectLogo } from "../../util/helpers/technologiesLogos"; | ||||
| import { useTranslation } from "react-i18next"; | |||||
| const ArchiveAd = ({ | const ArchiveAd = ({ | ||||
| className, | className, | ||||
| expiredAt, | expiredAt, | ||||
| onShowAdDetails, | onShowAdDetails, | ||||
| }) => { | }) => { | ||||
| const { t } = useTranslation(); | |||||
| return ( | return ( | ||||
| <div className={`archive-ad ${className}`} onClick={onShowAdDetails}> | <div className={`archive-ad ${className}`} onClick={onShowAdDetails}> | ||||
| <div className="archive-ad-date"> | <div className="archive-ad-date"> | ||||
| </div> | </div> | ||||
| <div className="archive-ad-experience"> | <div className="archive-ad-experience"> | ||||
| {minimumExperience > 0 ? ( | {minimumExperience > 0 ? ( | ||||
| <p>{minimumExperience}+ years of experience</p> | |||||
| <p> | |||||
| {minimumExperience}+ {t("common.experience")} | |||||
| </p> | |||||
| ) : ( | ) : ( | ||||
| <p>No experience needed</p> | |||||
| <p>{t("ads.noExperience")}</p> | |||||
| )} | )} | ||||
| </div> | </div> | ||||
| </div> | </div> |
| import React from "react"; | import React from "react"; | ||||
| import PropTypes from "prop-types"; | import PropTypes from "prop-types"; | ||||
| import { selectLogo } from "../../util/helpers/technologiesLogos"; | import { selectLogo } from "../../util/helpers/technologiesLogos"; | ||||
| import { useTranslation } from "react-i18next"; | |||||
| const StatsAd = ({ | const StatsAd = ({ | ||||
| className, | className, | ||||
| expiredAt, | expiredAt, | ||||
| onShowAdDetails, | onShowAdDetails, | ||||
| }) => { | }) => { | ||||
| const {t} = useTranslation() | |||||
| return ( | return ( | ||||
| <div className={`archive-ad stats-ad ${className}`} onClick={onShowAdDetails}> | <div className={`archive-ad stats-ad ${className}`} onClick={onShowAdDetails}> | ||||
| <div className="ad-count"> | <div className="ad-count"> | ||||
| {count} prijavljenih | |||||
| {count} {t("ads.registered")} | |||||
| </div> | </div> | ||||
| <div className="archive-ad-date"> | <div className="archive-ad-date"> | ||||
| <p> | <p> | ||||
| </div> | </div> | ||||
| <div className="archive-ad-experience"> | <div className="archive-ad-experience"> | ||||
| {minimumExperience > 0 ? ( | {minimumExperience > 0 ? ( | ||||
| <p>{minimumExperience}+ years of experience</p> | |||||
| <p>{minimumExperience}+ {t("common.experience")}</p> | |||||
| ) : ( | ) : ( | ||||
| <p>No experience needed</p> | |||||
| <p>{t("ads.noExperience2")}</p> | |||||
| )} | )} | ||||
| </div> | </div> | ||||
| </div> | </div> |
| import userPageBtnIcon from "../../assets/images/userPageBtnIcon.png"; | import userPageBtnIcon from "../../assets/images/userPageBtnIcon.png"; | ||||
| import { useTheme } from "@mui/system"; | import { useTheme } from "@mui/system"; | ||||
| import { useMediaQuery } from "@mui/material"; | import { useMediaQuery } from "@mui/material"; | ||||
| import { useTranslation } from "react-i18next"; | |||||
| const EditButton = ({ onEnableEdit }) => { | const EditButton = ({ onEnableEdit }) => { | ||||
| const theme = useTheme(); | const theme = useTheme(); | ||||
| const matches = useMediaQuery(theme.breakpoints.down("sm")); | const matches = useMediaQuery(theme.breakpoints.down("sm")); | ||||
| const {t} = useTranslation() | |||||
| return ( | return ( | ||||
| <IconButton | <IconButton | ||||
| className={"c-btn--primary-outlined edit-btn-class c-btn userPageBtn ml-20px no-padding"} | className={"c-btn--primary-outlined edit-btn-class c-btn userPageBtn ml-20px no-padding"} | ||||
| onClick={onEnableEdit} | onClick={onEnableEdit} | ||||
| > | > | ||||
| {!matches && "Režim uređivanja"} | |||||
| {!matches && t("patterns.editing2")} | |||||
| <img | <img | ||||
| style={{ | style={{ | ||||
| position: "relative", | position: "relative", |
| import filters from "../../assets/images/filters.png"; | import filters from "../../assets/images/filters.png"; | ||||
| import { useTheme } from "@mui/system"; | import { useTheme } from "@mui/system"; | ||||
| import { useMediaQuery } from "@mui/material"; | import { useMediaQuery } from "@mui/material"; | ||||
| import { useTranslation } from "react-i18next"; | |||||
| const FilterButton = ({ onShowFilters }) => { | const FilterButton = ({ onShowFilters }) => { | ||||
| const theme = useTheme(); | const theme = useTheme(); | ||||
| const matches = useMediaQuery(theme.breakpoints.down("sm")); | const matches = useMediaQuery(theme.breakpoints.down("sm")); | ||||
| const { t } = useTranslation(); | |||||
| return ( | return ( | ||||
| <IconButton | <IconButton | ||||
| className={"c-btn--primary-outlined ads-page-btn fltr-btn c-btn userPageBtn ml-20px no-padding custom-filter-button"} | |||||
| className={ | |||||
| "c-btn--primary-outlined ads-page-btn fltr-btn c-btn userPageBtn ml-20px no-padding custom-filter-button" | |||||
| } | |||||
| onClick={onShowFilters} | onClick={onShowFilters} | ||||
| > | > | ||||
| {!matches && "Filteri"} | |||||
| {!matches && t("filters.filters")} | |||||
| <img | <img | ||||
| style={{ | style={{ | ||||
| position: "relative", | position: "relative", |
| import PropTypes from "prop-types"; | import PropTypes from "prop-types"; | ||||
| import { formatDate } from "../../util/helpers/dateHelpers"; | import { formatDate } from "../../util/helpers/dateHelpers"; | ||||
| import { CANDIDATES_PAGE } from "../../constants/pages"; | import { CANDIDATES_PAGE } from "../../constants/pages"; | ||||
| import { useTranslation } from "react-i18next"; | |||||
| const CandidateCard = ({ candidate, className, history }) => { | const CandidateCard = ({ candidate, className, history }) => { | ||||
| const { t } = useTranslation(); | |||||
| const navigateToDetailsPage = () => { | const navigateToDetailsPage = () => { | ||||
| history.push({ | history.push({ | ||||
| pathname: CANDIDATES_PAGE + "/" + candidate.applicantId, | pathname: CANDIDATES_PAGE + "/" + candidate.applicantId, | ||||
| </p> | </p> | ||||
| <p className="candidate-card-years"> | <p className="candidate-card-years"> | ||||
| {candidate.experience === 0 | {candidate.experience === 0 | ||||
| ? "No experience" | |||||
| : candidate.experience + "+ years of experience"} | |||||
| ? t("common.noExperience") | |||||
| : candidate.experience + " " + t("common.experience")} | |||||
| </p> | </p> | ||||
| <div className="candidate-card-tecnologies-container"> | <div className="candidate-card-tecnologies-container"> | ||||
| {candidate.technologyApplicants.map((technology, index) => ( | {candidate.technologyApplicants.map((technology, index) => ( |
| changeIsCheckedValue, | changeIsCheckedValue, | ||||
| resetIsCheckedValue, | resetIsCheckedValue, | ||||
| } from "../../store/actions/technologies/technologiesActions"; | } from "../../store/actions/technologies/technologiesActions"; | ||||
| import { useTranslation } from "react-i18next"; | |||||
| const CandidateFilters = ({ | const CandidateFilters = ({ | ||||
| open, | open, | ||||
| }) => { | }) => { | ||||
| const dispatch = useDispatch(); | const dispatch = useDispatch(); | ||||
| const [isInitial, setIsInitial] = useState(true); | const [isInitial, setIsInitial] = useState(true); | ||||
| const { t } = useTranslation(); | |||||
| useEffect(() => { | |||||
| }, [isTableView]); | |||||
| useEffect(() => {}, [isTableView]); | |||||
| const handleSliderChange = (_, newValue) => { | const handleSliderChange = (_, newValue) => { | ||||
| setSliderValue(newValue); | setSliderValue(newValue); | ||||
| <div className="ad-filters-header-container"> | <div className="ad-filters-header-container"> | ||||
| <div className="ad-filters-header"> | <div className="ad-filters-header"> | ||||
| <img src={filterIcon} alt="filter_icon" /> | <img src={filterIcon} alt="filter_icon" /> | ||||
| <h3>Filteri</h3> | |||||
| <h3>{t("filters.filters")}</h3> | |||||
| <p> | <p> | ||||
| <sub>| Kandidati</sub> | |||||
| <sub>| {t("candidates.candidates")}</sub> | |||||
| </p> | </p> | ||||
| </div> | </div> | ||||
| <div className="ad-filters-header-close" onClick={handleClose}> | <div className="ad-filters-header-close" onClick={handleClose}> | ||||
| </div> | </div> | ||||
| <div className="ad-filters-experience"> | <div className="ad-filters-experience"> | ||||
| <div className="ad-filters-sub-title"> | <div className="ad-filters-sub-title"> | ||||
| <p>Godine iskustva</p> | |||||
| <p>{t("filters.experience")}</p> | |||||
| </div> | </div> | ||||
| <div className="ad-filters-experience-slider"> | <div className="ad-filters-experience-slider"> | ||||
| <Slider | <Slider | ||||
| </div> | </div> | ||||
| <div className="ad-filters-technologies"> | <div className="ad-filters-technologies"> | ||||
| <div className="ad-filters-sub-title"> | <div className="ad-filters-sub-title"> | ||||
| <p>Tehnologije</p> | |||||
| <p>{t("filters.technologies")}</p> | |||||
| </div> | </div> | ||||
| <div className="ad-filters-technologies-checkboxes"> | <div className="ad-filters-technologies-checkboxes"> | ||||
| <FormGroup> | <FormGroup> | ||||
| </div> | </div> | ||||
| <div className="ad-filters-technologies"> | <div className="ad-filters-technologies"> | ||||
| <div className="ad-filters-sub-title"> | <div className="ad-filters-sub-title"> | ||||
| <p>Tip zaposlenja</p> | |||||
| <p>{t("filters.employmentType")}</p> | |||||
| </div> | </div> | ||||
| <div className="ad-filters-employment-type"> | <div className="ad-filters-employment-type"> | ||||
| {typesOfEmployments.map((type, index) => ( | {typesOfEmployments.map((type, index) => ( | ||||
| </div> | </div> | ||||
| <div className="ad-filters-technologies" style={{ marginTop: "35px" }}> | <div className="ad-filters-technologies" style={{ marginTop: "35px" }}> | ||||
| <div className="ad-filters-sub-title"> | <div className="ad-filters-sub-title"> | ||||
| <p>Datum prijave</p> | |||||
| <p>{t("filters.dateOfApplication")}</p> | |||||
| <div className="filter-date-container"> | <div className="filter-date-container"> | ||||
| <p>Od</p> | |||||
| <p>{t("common.from")}</p> | |||||
| <input | <input | ||||
| type="date" | type="date" | ||||
| id="start" | id="start" | ||||
| /> | /> | ||||
| </div> | </div> | ||||
| <div className="filter-date-container"> | <div className="filter-date-container"> | ||||
| <p>Do</p> | |||||
| <p>{t("common.to")}</p> | |||||
| <input | <input | ||||
| type="date" | type="date" | ||||
| id="start" | id="start" | ||||
| onClick={fiterItems} | onClick={fiterItems} | ||||
| disabled={!isThereSelectedFilter()} | disabled={!isThereSelectedFilter()} | ||||
| > | > | ||||
| Pretrazi | |||||
| {t("common.search")} | |||||
| </button> | </button> | ||||
| </div> | </div> | ||||
| </div> | </div> |
| import { useEffect } from "react"; | import { useEffect } from "react"; | ||||
| import { SelectionContext } from "../../context/SelectionContext"; | import { SelectionContext } from "../../context/SelectionContext"; | ||||
| import { setUpdateStatusReq } from "../../store/actions/processes/processAction"; | import { setUpdateStatusReq } from "../../store/actions/processes/processAction"; | ||||
| import { useTranslation } from "react-i18next"; | |||||
| // import { setUpdateInterviewerReq } from "../../store/actions/processes/processAction"; | // import { setUpdateInterviewerReq } from "../../store/actions/processes/processAction"; | ||||
| // import { setUpdateStatusReq } from "../../store/actions/processes/processAction"; | // import { setUpdateStatusReq } from "../../store/actions/processes/processAction"; | ||||
| responsive, | responsive, | ||||
| }) => { | }) => { | ||||
| const { activeProcessUnsuccess, setActiveProcessUnsuccess } = useContext(SelectionContext); | const { activeProcessUnsuccess, setActiveProcessUnsuccess } = useContext(SelectionContext); | ||||
| const {t} = useTranslation() | |||||
| const theme = useTheme(); | const theme = useTheme(); | ||||
| const fullScreen = useMediaQuery(theme.breakpoints.down("md")); | const fullScreen = useMediaQuery(theme.breakpoints.down("md")); | ||||
| className={`c-btn--primary-outlined interview-btn c-btn dialog-btn`} | className={`c-btn--primary-outlined interview-btn c-btn dialog-btn`} | ||||
| onClick={onClose} | onClick={onClose} | ||||
| > | > | ||||
| Otkaži | |||||
| {t("common.cancel")} | |||||
| </IconButton> | </IconButton> | ||||
| ) : ( | ) : ( | ||||
| "" | "" | ||||
| className={`c-btn--primary-outlined sm-full interview-btn c-btn dialog-btn`} | className={`c-btn--primary-outlined sm-full interview-btn c-btn dialog-btn`} | ||||
| onClick={submitHandler} | onClick={submitHandler} | ||||
| > | > | ||||
| Potvrdi | |||||
| {t("common.confirm")} | |||||
| </IconButton> | </IconButton> | ||||
| </DialogActions> | </DialogActions> | ||||
| </div> | </div> |
| DialogContent, | DialogContent, | ||||
| } from "@mui/material"; | } from "@mui/material"; | ||||
| import IconButton from "../IconButton/IconButton"; | import IconButton from "../IconButton/IconButton"; | ||||
| import { useTranslation } from "react-i18next"; | |||||
| const ConfirmDialog = ({ | const ConfirmDialog = ({ | ||||
| title, | title, | ||||
| }) => { | }) => { | ||||
| const theme = useTheme(); | const theme = useTheme(); | ||||
| const fullScreen = useMediaQuery(theme.breakpoints.down("md")); | const fullScreen = useMediaQuery(theme.breakpoints.down("md")); | ||||
| const { t } = useTranslation(); | |||||
| const handleClose = () => { | const handleClose = () => { | ||||
| onClose(); | onClose(); | ||||
| padding: "36px", | padding: "36px", | ||||
| }} | }} | ||||
| > | > | ||||
| <div style={{ padding: "36px" }} data-testid='alert-container'> | |||||
| <div style={{ padding: "36px" }} data-testid="alert-container"> | |||||
| <DialogTitle style={{ padding: 0 }}> | <DialogTitle style={{ padding: 0 }}> | ||||
| {fullScreen ? ( | {fullScreen ? ( | ||||
| <> | <> | ||||
| <div className="flex-center" style={{ justifyContent: "start" }} data-testid="full-screen-elem"> | |||||
| <div | |||||
| className="flex-center" | |||||
| style={{ justifyContent: "start" }} | |||||
| data-testid="full-screen-elem" | |||||
| > | |||||
| <img | <img | ||||
| style={{ | style={{ | ||||
| position: "relative", | position: "relative", | ||||
| src={imgSrc} | src={imgSrc} | ||||
| /> | /> | ||||
| <h5 style={{ textAlign: "start" }}>{title}</h5> | <h5 style={{ textAlign: "start" }}>{title}</h5> | ||||
| <div style={{ justifySelf: "stretch", flex:'1' }}></div> | |||||
| <IconButton | |||||
| onClick={onClose} | |||||
| > | |||||
| <div style={{ justifySelf: "stretch", flex: "1" }}></div> | |||||
| <IconButton onClick={onClose}> | |||||
| <img | <img | ||||
| style={{ | style={{ | ||||
| position: "relative", | position: "relative", | ||||
| )} | )} | ||||
| </DialogTitle> | </DialogTitle> | ||||
| <DialogContent> | <DialogContent> | ||||
| <div className="modal-content" data-testid="modal-content">{content}</div> | |||||
| <div className="modal-content" data-testid="modal-content"> | |||||
| {content} | |||||
| </div> | |||||
| </DialogContent> | </DialogContent> | ||||
| <DialogActions style={{ padding: 0 }}> | <DialogActions style={{ padding: 0 }}> | ||||
| {!fullScreen ? ( | {!fullScreen ? ( | ||||
| className={`c-btn--primary-outlined c-btn dialog-btn not-full-screen-btn`} | className={`c-btn--primary-outlined c-btn dialog-btn not-full-screen-btn`} | ||||
| onClick={onClose} | onClick={onClose} | ||||
| > | > | ||||
| Otkaži | |||||
| {t("common.cancel")} | |||||
| </IconButton> | </IconButton> | ||||
| ) : ( | ) : ( | ||||
| "" | "" | ||||
| className={`c-btn--primary-outlined sm-full c-btn dialog-btn`} | className={`c-btn--primary-outlined sm-full c-btn dialog-btn`} | ||||
| onClick={onConfirm} | onClick={onConfirm} | ||||
| > | > | ||||
| Potvrdi | |||||
| {t("common.confirm")} | |||||
| </IconButton> | </IconButton> | ||||
| </DialogActions> | </DialogActions> | ||||
| </div> | </div> |
| import { format, isValid } from "date-fns"; | import { format, isValid } from "date-fns"; | ||||
| import { fetchInitProcess } from "../../store/actions/candidates/candidatesActions"; | import { fetchInitProcess } from "../../store/actions/candidates/candidatesActions"; | ||||
| import { useEffect } from "react"; | import { useEffect } from "react"; | ||||
| import { useTranslation } from "react-i18next"; | |||||
| const InterviewDialog = ({ | const InterviewDialog = ({ | ||||
| title, | title, | ||||
| const [selected, setSelected] = useState(""); | const [selected, setSelected] = useState(""); | ||||
| const [selectedInterviewer, setSelectedInterviewer] = useState(null); | const [selectedInterviewer, setSelectedInterviewer] = useState(null); | ||||
| const [value, setValue] = useState(null); | const [value, setValue] = useState(null); | ||||
| const { t } = useTranslation(); | |||||
| const theme = useTheme(); | const theme = useTheme(); | ||||
| const fullScreen = useMediaQuery(theme.breakpoints.down("md")); | const fullScreen = useMediaQuery(theme.breakpoints.down("md")); | ||||
| <form className="modal-content interviewDialog"> | <form className="modal-content interviewDialog"> | ||||
| <FormControl fullWidth> | <FormControl fullWidth> | ||||
| <InputLabel id="demo-simple-select-label"> | <InputLabel id="demo-simple-select-label"> | ||||
| Ime kandidata | |||||
| {t("dialogs.candidateName")} | |||||
| </InputLabel> | </InputLabel> | ||||
| <Select | <Select | ||||
| labelId="demo-simple-select-label" | labelId="demo-simple-select-label" | ||||
| </FormControl> | </FormControl> | ||||
| <FormControl fullWidth> | <FormControl fullWidth> | ||||
| <InputLabel id="demo-simple-select-label"> | <InputLabel id="demo-simple-select-label"> | ||||
| Ime intervjuera (opciono) | |||||
| {t("dialogs.interviewerName")} | |||||
| </InputLabel> | </InputLabel> | ||||
| <Select | <Select | ||||
| labelId="demo-simple-select-label" | labelId="demo-simple-select-label" | ||||
| className={`c-btn--primary-outlined interview-btn c-btn dialog-btn`} | className={`c-btn--primary-outlined interview-btn c-btn dialog-btn`} | ||||
| onClick={onClose} | onClick={onClose} | ||||
| > | > | ||||
| Otkaži | |||||
| {t("common.cancel")} | |||||
| </IconButton> | </IconButton> | ||||
| ) : ( | ) : ( | ||||
| "" | "" | ||||
| className={`c-btn--primary-outlined sm-full interview-btn c-btn dialog-btn`} | className={`c-btn--primary-outlined sm-full interview-btn c-btn dialog-btn`} | ||||
| onClick={submitHandler} | onClick={submitHandler} | ||||
| > | > | ||||
| Potvrdi | |||||
| {t("common.confirm")} | |||||
| </IconButton> | </IconButton> | ||||
| </DialogActions> | </DialogActions> | ||||
| </div> | </div> |
| import { useEffect } from "react"; | import { useEffect } from "react"; | ||||
| import { SelectionContext } from "../../context/SelectionContext"; | import { SelectionContext } from "../../context/SelectionContext"; | ||||
| import { setUpdateInterviewerReq } from "../../store/actions/processes/processAction"; | import { setUpdateInterviewerReq } from "../../store/actions/processes/processAction"; | ||||
| import { useTranslation } from "react-i18next"; | |||||
| // import { setUpdateStatusReq } from "../../store/actions/processes/processAction"; | // import { setUpdateStatusReq } from "../../store/actions/processes/processAction"; | ||||
| const InterviewerDialog = ({ | const InterviewerDialog = ({ | ||||
| fullWidth, | fullWidth, | ||||
| responsive, | responsive, | ||||
| }) => { | }) => { | ||||
| const { | |||||
| activeInterview, | |||||
| setActiveInterview | |||||
| } = useContext(SelectionContext); | |||||
| const { activeInterview, setActiveInterview } = useContext(SelectionContext); | |||||
| const [selected, setSelected] = useState(""); | const [selected, setSelected] = useState(""); | ||||
| const {t} = useTranslation() | |||||
| const theme = useTheme(); | const theme = useTheme(); | ||||
| const fullScreen = useMediaQuery(theme.breakpoints.down("md")); | const fullScreen = useMediaQuery(theme.breakpoints.down("md")); | ||||
| }; | }; | ||||
| const apiSuccess = () => { | const apiSuccess = () => { | ||||
| setActiveInterview(null) | |||||
| setActiveInterview(null); | |||||
| // console.log("ok"); | // console.log("ok"); | ||||
| }; | }; | ||||
| <form className="modal-content interviewDialog"> | <form className="modal-content interviewDialog"> | ||||
| <FormControl fullWidth> | <FormControl fullWidth> | ||||
| <InputLabel id="demo-simple-select-label"> | <InputLabel id="demo-simple-select-label"> | ||||
| Ime intervjuera | |||||
| {t("dialogs.interviewerName2")} | |||||
| </InputLabel> | </InputLabel> | ||||
| <Select | <Select | ||||
| labelId="demo-simple-select-label" | labelId="demo-simple-select-label" | ||||
| className={`c-btn--primary-outlined interview-btn c-btn dialog-btn`} | className={`c-btn--primary-outlined interview-btn c-btn dialog-btn`} | ||||
| onClick={onClose} | onClick={onClose} | ||||
| > | > | ||||
| Otkaži | |||||
| {t("common.cancel")} | |||||
| </IconButton> | </IconButton> | ||||
| ) : ( | ) : ( | ||||
| "" | "" | ||||
| className={`c-btn--primary-outlined sm-full interview-btn c-btn dialog-btn`} | className={`c-btn--primary-outlined sm-full interview-btn c-btn dialog-btn`} | ||||
| onClick={submitHandler} | onClick={submitHandler} | ||||
| > | > | ||||
| Potvrdi | |||||
| {t("common.confirm")} | |||||
| </IconButton> | </IconButton> | ||||
| </DialogActions> | </DialogActions> | ||||
| </div> | </div> |
| inputProps={{ "data-testid": "invite-input-text" }} | inputProps={{ "data-testid": "invite-input-text" }} | ||||
| name="firstName" | name="firstName" | ||||
| // label={t("users.receiver")} | // label={t("users.receiver")} | ||||
| label={"Ime"} | |||||
| label={t("common.firstName")} | |||||
| margin="normal" | margin="normal" | ||||
| value={formik.values.firstName} | value={formik.values.firstName} | ||||
| onChange={formik.handleChange} | onChange={formik.handleChange} | ||||
| inputProps={{ "data-testid": "invite-input-text" }} | inputProps={{ "data-testid": "invite-input-text" }} | ||||
| name="lastName" | name="lastName" | ||||
| // label={t("users.receiver")} | // label={t("users.receiver")} | ||||
| label={"Prezime"} | |||||
| label={t("common.lastName")} | |||||
| margin="normal" | margin="normal" | ||||
| value={formik.values.lastName} | value={formik.values.lastName} | ||||
| onChange={formik.handleChange} | onChange={formik.handleChange} | ||||
| inputProps={{ "data-testid": "invite-input-text" }} | inputProps={{ "data-testid": "invite-input-text" }} | ||||
| name="email" | name="email" | ||||
| // label={t("users.receiver")} | // label={t("users.receiver")} | ||||
| label={"E-mail adresa"} | |||||
| label={"E-mail " + t("common.address")} | |||||
| margin="normal" | margin="normal" | ||||
| value={formik.values.email} | value={formik.values.email} | ||||
| onChange={formik.handleChange} | onChange={formik.handleChange} | ||||
| }} | }} | ||||
| src={planeVector} | src={planeVector} | ||||
| />{" "} | />{" "} | ||||
| Registracioni link | |||||
| {t("registration.link")} | |||||
| </IconButton> | </IconButton> | ||||
| </form> | </form> | ||||
| </DialogContent> | </DialogContent> |
| <Typography | <Typography | ||||
| sx={{ fontWeight: 600, fontSize: "18px", letterSpacing: "1.5px" }} | sx={{ fontWeight: 600, fontSize: "18px", letterSpacing: "1.5px" }} | ||||
| > | > | ||||
| Navigacija | |||||
| {t("nav.navigation")} | |||||
| </Typography> | </Typography> | ||||
| {/* <img src={x}/> */} | {/* <img src={x}/> */} | ||||
| <IconButton onClick={handleToggleDrawer}> | <IconButton onClick={handleToggleDrawer}> | ||||
| sx={{ fontSize: "14px", marginTop: "8px" }} | sx={{ fontSize: "14px", marginTop: "8px" }} | ||||
| className="text-grey9d" | className="text-grey9d" | ||||
| > | > | ||||
| HR Specialist | |||||
| HR {t("common.specialist")} | |||||
| </Typography> | </Typography> | ||||
| <div className="hr" style={{ width: "90%", marginTop: "18px" }}></div> | <div className="hr" style={{ width: "90%", marginTop: "18px" }}></div> | ||||
| </div> | </div> |
| createScreeningTest, | createScreeningTest, | ||||
| fetchScreeningTests, | fetchScreeningTests, | ||||
| } from "../../store/actions/screeningTests/screeningTestActions"; | } from "../../store/actions/screeningTests/screeningTestActions"; | ||||
| import { useTranslation } from "react-i18next"; | |||||
| const StatusDialog = ({ | const StatusDialog = ({ | ||||
| title, | title, | ||||
| const { users } = useSelector((s) => s.users); | const { users } = useSelector((s) => s.users); | ||||
| const { isSuccess } = useSelector((s) => s.initProcess); | const { isSuccess } = useSelector((s) => s.initProcess); | ||||
| const { screeningTests } = useSelector((s) => s.screeningTests); | const { screeningTests } = useSelector((s) => s.screeningTests); | ||||
| const { t } = useTranslation(); | |||||
| const dispatch = useDispatch(); | const dispatch = useDispatch(); | ||||
| <form className="modal-content interviewDialog"> | <form className="modal-content interviewDialog"> | ||||
| <FormControl fullWidth> | <FormControl fullWidth> | ||||
| <InputLabel id="demo-simple-select-label"> | <InputLabel id="demo-simple-select-label"> | ||||
| Ime intervjuera | |||||
| {t("dialogs.interviewerName2")} | |||||
| </InputLabel> | </InputLabel> | ||||
| <Select | <Select | ||||
| labelId="demo-simple-select-label" | labelId="demo-simple-select-label" | ||||
| id="demo-simple-select" | id="demo-simple-select" | ||||
| value={selected} | value={selected} | ||||
| label="Ime intervjuera" | |||||
| label={t("dialogs.interviewerName2")} | |||||
| onChange={(e) => { | onChange={(e) => { | ||||
| setSelected(e.target.value); | setSelected(e.target.value); | ||||
| }} | }} | ||||
| activeProcess.process.selectionLevelId === 2 && ( | activeProcess.process.selectionLevelId === 2 && ( | ||||
| <TextField | <TextField | ||||
| name="duration" | name="duration" | ||||
| label={"Duration"} | |||||
| label={t("dialogs.duration")} | |||||
| value={duration} | value={duration} | ||||
| onChange={(e) => setDuration(e.target.value)} | onChange={(e) => setDuration(e.target.value)} | ||||
| fullWidth | fullWidth | ||||
| {/* {activeProcess.process && activeProcess.process.date ? <p>Proces ima zakazan termin</p> : ''} */} | {/* {activeProcess.process && activeProcess.process.date ? <p>Proces ima zakazan termin</p> : ''} */} | ||||
| <DateTimePicker | <DateTimePicker | ||||
| label="Termin" | |||||
| label={t("dialogs.appointment")} | |||||
| value={value} | value={value} | ||||
| onChange={handleChange} | onChange={handleChange} | ||||
| renderInput={(params) => <TextField {...params} />} | renderInput={(params) => <TextField {...params} />} | ||||
| className={`c-btn--primary-outlined interview-btn c-btn dialog-btn`} | className={`c-btn--primary-outlined interview-btn c-btn dialog-btn`} | ||||
| onClick={onClose} | onClick={onClose} | ||||
| > | > | ||||
| Cancel | |||||
| {t("common.cancel")} | |||||
| </IconButton> | </IconButton> | ||||
| ) : ( | ) : ( | ||||
| "" | "" | ||||
| className={`c-btn--primary-outlined sm-full interview-btn c-btn dialog-btn`} | className={`c-btn--primary-outlined sm-full interview-btn c-btn dialog-btn`} | ||||
| onClick={submitHandler} | onClick={submitHandler} | ||||
| > | > | ||||
| Confirm | |||||
| {t("common.confirm")} | |||||
| </IconButton> | </IconButton> | ||||
| </DialogActions> | </DialogActions> | ||||
| </div> | </div> |
| import PropTypes from "prop-types"; | import PropTypes from "prop-types"; | ||||
| import { useDispatch } from "react-redux"; | import { useDispatch } from "react-redux"; | ||||
| import { setFilteredPatternsReq } from "../../store/actions/patterns/patternsActions"; | import { setFilteredPatternsReq } from "../../store/actions/patterns/patternsActions"; | ||||
| import { useTranslation } from "react-i18next"; | |||||
| const PatternFilters = ({ | const PatternFilters = ({ | ||||
| openFilterDrawer, | openFilterDrawer, | ||||
| const [fromDate, setFromDate] = useState(""); | const [fromDate, setFromDate] = useState(""); | ||||
| const [toDate, setToDate] = useState(""); | const [toDate, setToDate] = useState(""); | ||||
| const dispatch = useDispatch(); | const dispatch = useDispatch(); | ||||
| const { t } = useTranslation(); | |||||
| console.log("OVDE",selectionLevelFilter) | |||||
| console.log("OVDE", selectionLevelFilter); | |||||
| const submitFiltersHandler = (e) => { | const submitFiltersHandler = (e) => { | ||||
| e.preventDefault(); | e.preventDefault(); | ||||
| <div> | <div> | ||||
| <div className="custom-drawer-sub-card"> | <div className="custom-drawer-sub-card"> | ||||
| <div className="custom-drawer-sub-card-label"> | <div className="custom-drawer-sub-card-label"> | ||||
| <p>Kategorija</p> | |||||
| <p>{t("filters.category")}</p> | |||||
| </div> | </div> | ||||
| <div className="custom-drawer-sub-card-content"> | <div className="custom-drawer-sub-card-content"> | ||||
| <FormGroup> | <FormGroup> | ||||
| </div> | </div> | ||||
| <div className="custom-drawer-sub-card"> | <div className="custom-drawer-sub-card"> | ||||
| <div className="custom-drawer-sub-card-label"> | <div className="custom-drawer-sub-card-label"> | ||||
| <p>Datum kreiranja</p> | |||||
| <p>{t("filters.creationDate")}</p> | |||||
| </div> | </div> | ||||
| <div className="custom-drawer-sub-card-content"> | <div className="custom-drawer-sub-card-content"> | ||||
| <FormGroup style={{ marginBottom: "9px" }}> | <FormGroup style={{ marginBottom: "9px" }}> | ||||
| <div className="custom-drawer-sub-card-content-sub"> | <div className="custom-drawer-sub-card-content-sub"> | ||||
| <label className="custom-drawer-sub-card-content-sub-label"> | <label className="custom-drawer-sub-card-content-sub-label"> | ||||
| Od | |||||
| {t("common.from")} | |||||
| </label> | </label> | ||||
| <input | <input | ||||
| type="date" | type="date" | ||||
| <FormGroup> | <FormGroup> | ||||
| <div className="custom-drawer-sub-card-content-sub"> | <div className="custom-drawer-sub-card-content-sub"> | ||||
| <label className="custom-drawer-sub-card-content-sub-label"> | <label className="custom-drawer-sub-card-content-sub-label"> | ||||
| Do | |||||
| {t("common.to")} | |||||
| </label> | </label> | ||||
| <input | <input | ||||
| type="date" | type="date" | ||||
| data-testid="custom-drawer-submit-search" | data-testid="custom-drawer-submit-search" | ||||
| disabled={!hasSelectedFilters()} | disabled={!hasSelectedFilters()} | ||||
| > | > | ||||
| Pretrazi | |||||
| {t("common.search")} | |||||
| </button> | </button> | ||||
| </div> | </div> | ||||
| </div> | </div> |
| sx={{ fontSize: "14px", marginTop: "8px" }} | sx={{ fontSize: "14px", marginTop: "8px" }} | ||||
| className="text-grey9d" | className="text-grey9d" | ||||
| > | > | ||||
| HR Specialist | |||||
| HR {t("common.specialist")} | |||||
| </Typography> | </Typography> | ||||
| {/* <div className="hr" style={{ width: "90%", marginTop: "18px" }}></div> */} | {/* <div className="hr" style={{ width: "90%", marginTop: "18px" }}></div> */} | ||||
| .matches( | .matches( | ||||
| // eslint-disable-next-line | // eslint-disable-next-line | ||||
| /[!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?]+/, | /[!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?]+/, | ||||
| "Paswword must contain atleast one special character" | |||||
| t("login.specialCharacterRequired") | |||||
| ) | ) | ||||
| .matches(/[0-9]/g, "Paswword must contain atleast one number") | |||||
| .matches( | |||||
| /[a-z]/g, | |||||
| "Paswword must contain at least one lowercase character" | |||||
| ) | |||||
| .matches( | |||||
| /[A-Z]/g, | |||||
| "Paswword must contain at least one uppercase character" | |||||
| ) | |||||
| .min(8, "Password must contain at least 8 characters"), | |||||
| .matches(/[0-9]/g, t("login.numberRequired")) | |||||
| .matches(/[a-z]/g, t("login.lowercaseRequired")) | |||||
| .matches(/[A-Z]/g, t("login.uppercaseRequired")) | |||||
| .min(8, t("login.numberOfCharactersRequired")), | |||||
| confirm: yup | confirm: yup | ||||
| .string() | .string() | ||||
| .required("Please retype your password.") | |||||
| .oneOf([yup.ref("password")], "Your passwords do not match."), | |||||
| .required(t("login.retypePassword")) | |||||
| .oneOf([yup.ref("password")], t("login.matchPasswords")), | |||||
| }), | }), | ||||
| onSubmit: submitHandler, | onSubmit: submitHandler, | ||||
| validateOnBlur: true, | validateOnBlur: true, | ||||
| <TextField | <TextField | ||||
| className="rounded-input" | className="rounded-input" | ||||
| name="confirm" | name="confirm" | ||||
| label={"Potvrda šifre"} | |||||
| label={t("login.passwordConfirmation")} | |||||
| margin="normal" | margin="normal" | ||||
| type={showPassword ? "text" : "password"} | type={showPassword ? "text" : "password"} | ||||
| fullWidth | fullWidth | ||||
| className="c-btn c-btn--primary w-289 f" | className="c-btn c-btn--primary w-289 f" | ||||
| type="submit" | type="submit" | ||||
| > | > | ||||
| Nastavi | |||||
| {t("common.continue")} | |||||
| </Button> | </Button> | ||||
| {/* <div className="flex-center"> | {/* <div className="flex-center"> | ||||
| <div className="hr hr-s"></div> | <div className="hr hr-s"></div> |
| className="c-btn c-btn--primary w-289 f" | className="c-btn c-btn--primary w-289 f" | ||||
| type="submit" | type="submit" | ||||
| > | > | ||||
| Registruj se | |||||
| {t("registration.register")} | |||||
| </Button> | </Button> | ||||
| {/* <div className="flex-center"> | {/* <div className="flex-center"> | ||||
| <div className="hr hr-s"></div> | <div className="hr hr-s"></div> | ||||
| ); | ); | ||||
| } | } | ||||
| export default SecondStepForm; | |||||
| export default SecondStepForm; |
| import React from "react"; | import React from "react"; | ||||
| import PropTypes from "prop-types"; | import PropTypes from "prop-types"; | ||||
| import { formatTimeSrb } from "../../util/helpers/dateHelpers"; | import { formatTimeSrb } from "../../util/helpers/dateHelpers"; | ||||
| import { useTranslation } from "react-i18next"; | |||||
| const DayComponent = ({ numberOfDay, nameOfDay, interviews, onClick }) => { | const DayComponent = ({ numberOfDay, nameOfDay, interviews, onClick }) => { | ||||
| const { t } = useTranslation(); | |||||
| return ( | return ( | ||||
| <div className="day-component-container" onClick={onClick}> | <div className="day-component-container" onClick={onClick}> | ||||
| <div className="day-component-day-informations-container"> | <div className="day-component-day-informations-container"> | ||||
| <p className="day-component-day-number">{numberOfDay}</p> | <p className="day-component-day-number">{numberOfDay}</p> | ||||
| <p className="day-component-day-name">{nameOfDay}</p> | <p className="day-component-day-name">{nameOfDay}</p> | ||||
| </div> | </div> | ||||
| {interviews && interviews.slice(0, 2).map((interview, index) => ( | |||||
| <div | |||||
| key={index} | |||||
| className="day-component-interviews-container" | |||||
| style={{ marginTop: "4px" }} | |||||
| > | |||||
| <p className="day-component-interviews-time"> | |||||
| {formatTimeSrb(interview.date)} | |||||
| </p> | |||||
| <p className="day-component-interviews-name"> | |||||
| {interview.selectionLevel.name} | |||||
| </p> | |||||
| </div> | |||||
| ))} | |||||
| {interviews && | |||||
| interviews.slice(0, 2).map((interview, index) => ( | |||||
| <div | |||||
| key={index} | |||||
| className="day-component-interviews-container" | |||||
| style={{ marginTop: "4px" }} | |||||
| > | |||||
| <p className="day-component-interviews-time"> | |||||
| {formatTimeSrb(interview.date)} | |||||
| </p> | |||||
| <p className="day-component-interviews-name"> | |||||
| {interview.selectionLevel.name} | |||||
| </p> | |||||
| </div> | |||||
| ))} | |||||
| {interviews && interviews.length > 2 && ( | {interviews && interviews.length > 2 && ( | ||||
| <span className="day-component-more"> | <span className="day-component-more"> | ||||
| +{interviews.length - 2} stavke | |||||
| +{interviews.length - 2} {t("schedule.items")} | |||||
| </span> | </span> | ||||
| )} | )} | ||||
| </div> | </div> |
| import { CANDIDATES_PAGE } from "../../constants/pages"; | import { CANDIDATES_PAGE } from "../../constants/pages"; | ||||
| import { useTheme } from "@emotion/react"; | import { useTheme } from "@emotion/react"; | ||||
| import { useMediaQuery } from "@mui/material"; | import { useMediaQuery } from "@mui/material"; | ||||
| import { useTranslation } from "react-i18next"; | |||||
| const DayDetailsComponent = ({ | const DayDetailsComponent = ({ | ||||
| selectedDate, | selectedDate, | ||||
| const matches = useMediaQuery(theme.breakpoints.down("361")); | const matches = useMediaQuery(theme.breakpoints.down("361")); | ||||
| const [isLeftArrowDisabled, setIsLeftArrowDisabled] = useState(false); | const [isLeftArrowDisabled, setIsLeftArrowDisabled] = useState(false); | ||||
| const [isRightArrowDisabled, setIsRightArrowDisabled] = useState(false); | const [isRightArrowDisabled, setIsRightArrowDisabled] = useState(false); | ||||
| const { t } = useTranslation(); | |||||
| const handleClose = () => { | const handleClose = () => { | ||||
| onClose(); | onClose(); | ||||
| }; | }; | ||||
| > | > | ||||
| <DialogTitle className="day-datails-title-container"> | <DialogTitle className="day-datails-title-container"> | ||||
| <img src={calendar} className="day-details-calendar-image" /> | <img src={calendar} className="day-details-calendar-image" /> | ||||
| <p className="day-details-main-header">Planer aktivnosti</p> | |||||
| <p className="day-details-main-header">{t("schedule.planner")}</p> | |||||
| <p className="day-details-header">| {selectedDate}.</p> | <p className="day-details-header">| {selectedDate}.</p> | ||||
| <img | <img | ||||
| src={x} | src={x} | ||||
| }} | }} | ||||
| > | > | ||||
| {isLeftArrowDisabled === true ? ( | {isLeftArrowDisabled === true ? ( | ||||
| <div | |||||
| className="day-details-arrow-container" | |||||
| > | |||||
| <div className="day-details-arrow-container"> | |||||
| <img src={arrowLeftDisabled} /> | <img src={arrowLeftDisabled} /> | ||||
| </div> | </div> | ||||
| ) : ( | ) : ( |
| import PropTypes from "prop-types"; | import PropTypes from "prop-types"; | ||||
| import { formatDate } from "../../util/helpers/dateHelpers"; | import { formatDate } from "../../util/helpers/dateHelpers"; | ||||
| import { Link } from "react-router-dom"; | import { Link } from "react-router-dom"; | ||||
| import success from "../../assets/images/svg/success.svg" | |||||
| import unsucc from "../../assets/images/unsucc.png" | |||||
| import success from "../../assets/images/svg/success.svg"; | |||||
| import unsucc from "../../assets/images/unsucc.png"; | |||||
| import { useTranslation } from "react-i18next"; | |||||
| const ApplicantSelection = ({ | const ApplicantSelection = ({ | ||||
| levelNumber, | |||||
| levelName, | |||||
| schedguler, | |||||
| link, | |||||
| date, | |||||
| status, | |||||
| id, | |||||
| onShowAdDetails, | |||||
| className, | |||||
| levelNumber, | |||||
| levelName, | |||||
| schedguler, | |||||
| link, | |||||
| date, | |||||
| status, | |||||
| id, | |||||
| onShowAdDetails, | |||||
| className, | |||||
| }) => { | }) => { | ||||
| return ( | |||||
| <div data-testid='appSelection' className={`active-process-card ${className}`} onClick={onShowAdDetails}> | |||||
| <div className="active-process-card-header"> | |||||
| <div className="active-process-card-number"> | |||||
| <p> | |||||
| {levelNumber} | |||||
| </p> | |||||
| </div> | |||||
| {(status === "Odrađen") && <div className="active-process-card-logo"> | |||||
| <img src={success} alt="success-image" /> | |||||
| </div>} | |||||
| {(status === "Neuspešno") && <div className="active-process-card-logo"> | |||||
| <img src={unsucc} alt="success-image" /> | |||||
| </div>} | |||||
| </div> | |||||
| const { t } = useTranslation(); | |||||
| return ( | |||||
| <div | |||||
| data-testid="appSelection" | |||||
| className={`active-process-card ${className}`} | |||||
| onClick={onShowAdDetails} | |||||
| > | |||||
| <div className="active-process-card-header"> | |||||
| <div className="active-process-card-number"> | |||||
| <p>{levelNumber}</p> | |||||
| </div> | |||||
| {status === "Odrađen" && ( | |||||
| <div className="active-process-card-logo"> | |||||
| <img src={success} alt="success-image" /> | |||||
| </div> | |||||
| )} | |||||
| {status === "Neuspešno" && ( | |||||
| <div className="active-process-card-logo"> | |||||
| <img src={unsucc} alt="success-image" /> | |||||
| </div> | |||||
| )} | |||||
| </div> | |||||
| <div className="active-process-card-body"> | |||||
| <div className="active-process-card-date"> | |||||
| <p> | |||||
| {date && date !== "" && formatDate(new Date(date))} | |||||
| </p> | |||||
| </div> | |||||
| <div className="active-process-card-body"> | |||||
| <div className="active-process-card-date"> | |||||
| <p>{date && date !== "" && formatDate(new Date(date))}</p> | |||||
| </div> | |||||
| <div className="ad-card-title"> | |||||
| <h3>{levelName}</h3> | |||||
| </div> | |||||
| <div className="ad-card-title"> | |||||
| <h3>{levelName}</h3> | |||||
| </div> | |||||
| <div className="active-process-card-date"> | |||||
| <p> | |||||
| {schedguler} | |||||
| </p> | |||||
| </div> | |||||
| <div className="active-process-card-date"> | |||||
| <p>{schedguler}</p> | |||||
| </div> | |||||
| <div className="active-process-card-buttons"> | |||||
| <button className={status === "Neuspešno" && 'unsucc' }>{status}</button> | |||||
| </div> | |||||
| <div className="active-process-card-buttons"> | |||||
| <button className={status === "Neuspešno" && "unsucc"}> | |||||
| {status} | |||||
| </button> | |||||
| </div> | |||||
| <div className="active-process-card-link"> | |||||
| {(status === "Odrađen") && <Link className="ad-details-buttons-link" to={"/candidates/" + id}>Detaljni izveštaja</Link>} | |||||
| {(link && status !== "Odrađen") && <a href={link} className="ad-details-buttons-link">Link do Google Meet-a</a>} | |||||
| </div> | |||||
| </div> | |||||
| <div className="active-process-card-link"> | |||||
| {status === "Odrađen" && ( | |||||
| <Link className="ad-details-buttons-link" to={"/candidates/" + id}> | |||||
| {t("selection.report")} | |||||
| </Link> | |||||
| )} | |||||
| {link && status !== "Odrađen" && ( | |||||
| <a href={link} className="ad-details-buttons-link"> | |||||
| {t("selection.link")} | |||||
| </a> | |||||
| )} | |||||
| </div> | </div> | ||||
| ); | |||||
| </div> | |||||
| </div> | |||||
| ); | |||||
| }; | }; | ||||
| ApplicantSelection.propTypes = { | ApplicantSelection.propTypes = { | ||||
| id: PropTypes.string, | |||||
| levelNumber: PropTypes.number, | |||||
| levelName: PropTypes.string, | |||||
| schedguler: PropTypes.string, | |||||
| link: PropTypes.string, | |||||
| date: PropTypes.any, | |||||
| status: PropTypes.string, | |||||
| onShowAdDetails: PropTypes.func, | |||||
| className: PropTypes.any, | |||||
| id: PropTypes.string, | |||||
| levelNumber: PropTypes.number, | |||||
| levelName: PropTypes.string, | |||||
| schedguler: PropTypes.string, | |||||
| link: PropTypes.string, | |||||
| date: PropTypes.any, | |||||
| status: PropTypes.string, | |||||
| onShowAdDetails: PropTypes.func, | |||||
| className: PropTypes.any, | |||||
| }; | }; | ||||
| export default ApplicantSelection; | export default ApplicantSelection; |
| import { IconButton } from "@mui/material"; | import { IconButton } from "@mui/material"; | ||||
| import plus from "../../assets/images/plus.png"; | import plus from "../../assets/images/plus.png"; | ||||
| import SelectionCard from "./SelectionCard"; | import SelectionCard from "./SelectionCard"; | ||||
| import { useTranslation } from "react-i18next"; | |||||
| const Selection = (props) => { | const Selection = (props) => { | ||||
| const [over, setOver] = useState(false); | const [over, setOver] = useState(false); | ||||
| const errorMessage = useSelector(selectDoneProcessError); | const errorMessage = useSelector(selectDoneProcessError); | ||||
| const dispatch = useDispatch(); | const dispatch = useDispatch(); | ||||
| const user = useSelector(selectAuthUser); | const user = useSelector(selectAuthUser); | ||||
| const { t } = useTranslation(); | |||||
| const dropItem = (e, selId) => { | const dropItem = (e, selId) => { | ||||
| setOver(false); | setOver(false); | ||||
| var data = e.dataTransfer.getData("text/plain"); | var data = e.dataTransfer.getData("text/plain"); | ||||
| const selectionProcess = JSON.parse(data); | const selectionProcess = JSON.parse(data); | ||||
| if (selectionProcess.selectionLevelId < selId && selectionProcess.status !== "Neuspešno") { | |||||
| if ( | |||||
| selectionProcess.selectionLevelId < selId && | |||||
| selectionProcess.status !== "Neuspešno" | |||||
| ) { | |||||
| dispatch( | dispatch( | ||||
| setDoneProcessReq({ | setDoneProcessReq({ | ||||
| id: selectionProcess.id, | id: selectionProcess.id, | ||||
| props.history.push(SELECTION_PROCESS_OF_APPLICANT_PAGE.replace(":id", id)); | props.history.push(SELECTION_PROCESS_OF_APPLICANT_PAGE.replace(":id", id)); | ||||
| }; | }; | ||||
| const applicants = allApplicants?.filter((a) => a.status !== "Odrađen" || a.selectionLevelId === 4); | |||||
| const applicants = allApplicants?.filter( | |||||
| (a) => a.status !== "Odrađen" || a.selectionLevelId === 4 | |||||
| ); | |||||
| const renderList = applicants?.map((item, index) => { | const renderList = applicants?.map((item, index) => { | ||||
| return ( | return ( | ||||
| <SelectionCard | <SelectionCard | ||||
| onDrop={(e) => dropItem(e, props.selection.id)} | onDrop={(e) => dropItem(e, props.selection.id)} | ||||
| > | > | ||||
| <div className="selection-card-title"> | <div className="selection-card-title"> | ||||
| <h3 style={{marginRight:'50px'}}>{props.selection.name}</h3> | |||||
| <h3 style={{ marginRight: "50px" }}>{props.selection.name}</h3> | |||||
| {props.order === 0 ? ( | {props.order === 0 ? ( | ||||
| <IconButton | <IconButton | ||||
| sx={{marginRight:'35px'}} | |||||
| sx={{ marginRight: "35px" }} | |||||
| className={`c-btn--primary-outlined c-btn td-btn`} | className={`c-btn--primary-outlined c-btn td-btn`} | ||||
| onClick={props.modalEvent} | onClick={props.modalEvent} | ||||
| > | > | ||||
| position: "relative", | position: "relative", | ||||
| }} | }} | ||||
| src={plus} | src={plus} | ||||
| data-testid='interview-image' | |||||
| data-testid="interview-image" | |||||
| /> | /> | ||||
| </IconButton> | </IconButton> | ||||
| ) : ( | ) : ( | ||||
| {applicants && applicants !== null && applicants?.length === 0 && ( | {applicants && applicants !== null && applicants?.length === 0 && ( | ||||
| <div className="sel-item-no-data"> | <div className="sel-item-no-data"> | ||||
| <div className="date"> | <div className="date"> | ||||
| <p data-testid='empty-selection' style={{width:'240px'}}>Nema kandidata u selekciji</p> | |||||
| <p data-testid="empty-selection" style={{ width: "240px" }}> | |||||
| {t("selection.noCandidates")} | |||||
| </p> | |||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| )} | )} |
| setDoneProcessReq, | setDoneProcessReq, | ||||
| setUpdateStatusReq, | setUpdateStatusReq, | ||||
| } from "../../store/actions/processes/processAction"; | } from "../../store/actions/processes/processAction"; | ||||
| import { useTranslation } from "react-i18next"; | |||||
| // import Button from "../Button/Button"; | // import Button from "../Button/Button"; | ||||
| const options = ["Zakazan", "Odrađen", "Čeka na zakazivanje", "Neuspešno"]; | const options = ["Zakazan", "Odrađen", "Čeka na zakazivanje", "Neuspešno"]; | ||||
| const SelectionCard = (props) => { | const SelectionCard = (props) => { | ||||
| const [showForm, setShowForm] = useState(false); | const [showForm, setShowForm] = useState(false); | ||||
| const [selected, setSelected] = useState(props.item.status); | const [selected, setSelected] = useState(props.item.status); | ||||
| const { t } = useTranslation(); | |||||
| const { success } = useSelector((s) => s.statusUpdate); | const { success } = useSelector((s) => s.statusUpdate); | ||||
| const { | const { | ||||
| className="sel-item" | className="sel-item" | ||||
| onDragStart={props.dragStart} | onDragStart={props.dragStart} | ||||
| onClick={clickHandler} | onClick={clickHandler} | ||||
| data-testid='sel-card' | |||||
| data-testid="sel-card" | |||||
| > | > | ||||
| {" "} | {" "} | ||||
| <div | <div | ||||
| className="interbtn" | className="interbtn" | ||||
| onClick={(e) => changeInterviewerHandler(e)} | onClick={(e) => changeInterviewerHandler(e)} | ||||
| > | > | ||||
| Promeni | |||||
| {t("common.change")} | |||||
| </Button> | </Button> | ||||
| </div> | </div> | ||||
| <p> | <p> | ||||
| Intervjuer: {props.item.scheduler.firstName}{" "} | |||||
| {t("selection.interviewer")}: {props.item.scheduler.firstName}{" "} | |||||
| {props.item.scheduler.lastName} | {props.item.scheduler.lastName} | ||||
| </p> | </p> | ||||
| </div> | </div> |
| <div className="ad-filters-header-container"> | <div className="ad-filters-header-container"> | ||||
| <div className="ad-filters-header"> | <div className="ad-filters-header"> | ||||
| <img src={filterIcon} alt="filter_icon" /> | <img src={filterIcon} alt="filter_icon" /> | ||||
| <h3>{t("filter.title")}</h3> | |||||
| <h3>{t("filters.filters")}</h3> | |||||
| <p> | <p> | ||||
| <sub>| {t("selection.title")}</sub> | <sub>| {t("selection.title")}</sub> | ||||
| </p> | </p> | ||||
| </div> | </div> | ||||
| <div className="ad-filters-technologies"> | <div className="ad-filters-technologies"> | ||||
| <div className="ad-filters-sub-title"> | <div className="ad-filters-sub-title"> | ||||
| <p>{t("selection.filterStatus")}</p> | |||||
| <p>Status</p> | |||||
| </div> | </div> | ||||
| <div className="ad-filters-technologies-checkboxes"> | <div className="ad-filters-technologies-checkboxes"> | ||||
| <FormGroup> | <FormGroup> | ||||
| <p>{t("selection.filterDate")}</p> | <p>{t("selection.filterDate")}</p> | ||||
| </div> | </div> | ||||
| <div className="add-ad-modal-stage-sub-card"> | <div className="add-ad-modal-stage-sub-card"> | ||||
| <label>{t("selection.filterFrom")}</label> | |||||
| <label>{t("common.from")}</label> | |||||
| <input | <input | ||||
| type="date" | type="date" | ||||
| placeholder="ex" | placeholder="ex" | ||||
| /> | /> | ||||
| </div> | </div> | ||||
| <div className="add-ad-modal-stage-sub-card"> | <div className="add-ad-modal-stage-sub-card"> | ||||
| <label>{t("selection.filterTo")}</label> | |||||
| <label>{t("common.to")}</label> | |||||
| <input | <input | ||||
| type="date" | type="date" | ||||
| placeholder="ex" | placeholder="ex" | ||||
| </div> | </div> | ||||
| <div className="ad-filters-search"> | <div className="ad-filters-search"> | ||||
| <button onClick={onSubmitFilters} className="c-btn c-btn--primary"> | <button onClick={onSubmitFilters} className="c-btn c-btn--primary"> | ||||
| {t("selection.filterSubmit")} | |||||
| {t("common.search")} | |||||
| </button> | </button> | ||||
| </div> | </div> | ||||
| </div> | </div> |
| import Drawer from "@mui/material/Drawer"; | import Drawer from "@mui/material/Drawer"; | ||||
| import filterIcon from "../../assets/images/filters.png"; | import filterIcon from "../../assets/images/filters.png"; | ||||
| import x from "../../assets/images/x.png"; | import x from "../../assets/images/x.png"; | ||||
| import { useTranslation } from "react-i18next"; | |||||
| const CustomDrawer = ({ title, open, onCloseDrawer, children }) => { | const CustomDrawer = ({ title, open, onCloseDrawer, children }) => { | ||||
| const { t } = useTranslation(); | |||||
| const list = () => ( | const list = () => ( | ||||
| <Box | <Box | ||||
| sx={{ | sx={{ | ||||
| <div className="custom-filter-drawer-header-container"> | <div className="custom-filter-drawer-header-container"> | ||||
| <div className="custom-filter-drawer-header"> | <div className="custom-filter-drawer-header"> | ||||
| <img src={filterIcon} alt="filter_icon" /> | <img src={filterIcon} alt="filter_icon" /> | ||||
| <h3>Filteri</h3> | |||||
| <h3>{t("filters.filters")}</h3> | |||||
| <p> | <p> | ||||
| <sub>| {title}</sub> | <sub>| {title}</sub> | ||||
| </p> | </p> |
| import { format as formatDate } from 'date-fns'; | |||||
| import i18n from 'i18next'; | |||||
| import { initReactI18next } from 'react-i18next'; | |||||
| import { format as formatDate } from "date-fns"; | |||||
| import i18n from "i18next"; | |||||
| import { initReactI18next } from "react-i18next"; | |||||
| import enTranslations from './resources/en'; | |||||
| import rsTranslations from './resources/rs'; | |||||
| import enTranslations from "./resources/en"; | |||||
| import rsTranslations from "./resources/rs"; | |||||
| /* istanbul ignore file */ | /* istanbul ignore file */ | ||||
| i18n.use(initReactI18next).init({ | i18n.use(initReactI18next).init({ | ||||
| lng: 'rs', | |||||
| fallbackLng: 'en', | |||||
| lng: "en", | |||||
| fallbackLng: "en", | |||||
| debug: false, | debug: false, | ||||
| supportedLngs: ['en','rs'], | |||||
| supportedLngs: ["en", "rs"], | |||||
| resources: { | resources: { | ||||
| en: { | en: { | ||||
| translation: enTranslations, | translation: enTranslations, | ||||
| }, | }, | ||||
| rs:{ | |||||
| rs: { | |||||
| translation: rsTranslations, | translation: rsTranslations, | ||||
| }, | }, | ||||
| }, | }, |
| trademark: "TM", | trademark: "TM", | ||||
| search: "Search", | search: "Search", | ||||
| error: "Error", | error: "Error", | ||||
| continue: "Continue", | |||||
| continue: "CONTINUE", | |||||
| labelUsername: "Username", | labelUsername: "Username", | ||||
| labelPassword: "Password", | labelPassword: "Password", | ||||
| or: "or", | or: "or", | ||||
| and:"and", | |||||
| next: "Next", | next: "Next", | ||||
| nextPage: "Next page", | nextPage: "Next page", | ||||
| previousPage: "Previous page", | previousPage: "Previous page", | ||||
| back: "Back", | |||||
| back: "BACK", | |||||
| goBack: "Go Back", | goBack: "Go Back", | ||||
| ok: "Ok", | ok: "Ok", | ||||
| done: "Done", | done: "Done", | ||||
| confirm: "Confirm", | confirm: "Confirm", | ||||
| printDownload: "Print/Download", | printDownload: "Print/Download", | ||||
| cancel: "Cancel", | cancel: "Cancel", | ||||
| delete: "Delete", | |||||
| change: "Change", | |||||
| remove: "Remove", | remove: "Remove", | ||||
| invite: "Invite", | invite: "Invite", | ||||
| save: "Save", | save: "Save", | ||||
| download: "Download", | download: "Download", | ||||
| yes: "Yes", | yes: "Yes", | ||||
| no: "No", | no: "No", | ||||
| to: "to", | |||||
| to: "To", | |||||
| from: "From", | |||||
| select: "Select...", | select: "Select...", | ||||
| none: "None", | none: "None", | ||||
| date: { | date: { | ||||
| range: "{{start}} to {{end}}", | range: "{{start}} to {{end}}", | ||||
| }, | }, | ||||
| experience: "years of experience", | |||||
| noExperience: "No experience", | |||||
| firstName: "First name", | |||||
| lastName: "Last name", | |||||
| gender: "Gender", | |||||
| male: "Male", | |||||
| female: "Female", | |||||
| dateOfBirth: "Date of birth", | |||||
| phoneNumber: "Phone number", | |||||
| socialNetwork: "The social network", | |||||
| address: "address", | |||||
| specialist: "Specialist", | |||||
| title: "Title", | |||||
| }, | }, | ||||
| login: { | login: { | ||||
| welcome: "Welcome!", | welcome: "Welcome!", | ||||
| useDifferentEmail: "Use different email address or username", | useDifferentEmail: "Use different email address or username", | ||||
| signInWithGoogle: "Continue with google", | signInWithGoogle: "Continue with google", | ||||
| invalidEmail: "Invalid email format", | invalidEmail: "Invalid email format", | ||||
| specialCharacterRequired: | |||||
| "Paswword must contain at least one special character", | |||||
| numberRequired: "Paswword must contain at least one number", | |||||
| lowercaseRequired: "Paswword must contain at least one lowercase character", | |||||
| uppercaseRequired: "Paswword must contain at least one uppercase character", | |||||
| numberOfCharactersRequired: "Password must contain at least 8 characters", | |||||
| retypePassword: "Please retype your password.", | |||||
| matchPasswords: "Your passwords do not match.", | |||||
| passwordConfirmation: "Password confirmation", | |||||
| }, | }, | ||||
| password: { | password: { | ||||
| weak: "weak", | weak: "weak", | ||||
| UsernameDoesNotExist: "Username does not exist", | UsernameDoesNotExist: "Username does not exist", | ||||
| }, | }, | ||||
| nav: { | nav: { | ||||
| navigation: "Navigation", | |||||
| ads: "Ads", | ads: "Ads", | ||||
| selectionFlow: "Selection flow", | selectionFlow: "Selection flow", | ||||
| candidates: "Candidates", | candidates: "Candidates", | ||||
| statistics: "Statistics", | statistics: "Statistics", | ||||
| users: "Users", | users: "Users", | ||||
| signOut: "Sign Out", | signOut: "Sign Out", | ||||
| schedule:"Schedule" | |||||
| }, | }, | ||||
| ads: { | ads: { | ||||
| activeAds: "Active Ads", | activeAds: "Active Ads", | ||||
| adDetailsRequirements: "Requirements", | adDetailsRequirements: "Requirements", | ||||
| adDetailsOffer: "What We Offer", | adDetailsOffer: "What We Offer", | ||||
| archiveAdsCandidates: "Registered candidates", | archiveAdsCandidates: "Registered candidates", | ||||
| adding: "Adding", | |||||
| ad: "Ad", | |||||
| ads: "Ads", | |||||
| signUp: "Sign up", | |||||
| dragPdf1: "Drag the .pdf document in this part of the screen or", | |||||
| dragPdf2: "on the computer", | |||||
| coverLetter: "Cover letter (Optional)", | |||||
| professionalQualification: "Professional qualification", | |||||
| technologies: "Technologies you know", | |||||
| others: "Others", | |||||
| noExperience2: "No experience needed", | |||||
| registered: "registered", | |||||
| backToAds: "Back to all ads", | |||||
| thereIsNoAds: "Unfortunately, there are currently no active ads", | |||||
| addAd: "You can always add a new one in just a few simple steps", | |||||
| adNewAd: "Add an ad", | |||||
| expirationDate: "Ad Expiration Date", | |||||
| duties: "Main duties", | |||||
| conditions: "Conditions", | |||||
| offer: "What we offer", | |||||
| archivingAd:"Archiving ad", | |||||
| archivingAdQuestion:"Are you sure you want to archive the ad?", | |||||
| }, | }, | ||||
| users: { | users: { | ||||
| management: "User management", | management: "User management", | ||||
| contact: "Contact", | contact: "Contact", | ||||
| phone: "Phone", | phone: "Phone", | ||||
| socials: "Social Media", | socials: "Social Media", | ||||
| resetPassword:"Reset password", | |||||
| resetLink:"Are you sure you want to send password reset link?", | |||||
| disableUser:"Disable user", | |||||
| questionDisableUser:"Are you sure you want to disable user?", | |||||
| deleteUser:"Delete user", | |||||
| questionDeleteUser:"Are you sure you want to delete user?", | |||||
| block:"Block", | |||||
| unblock:"Unblock", | |||||
| profile:"Edit profile", | |||||
| positionNotDeclared:"Position has not been declared yet.", | |||||
| backToUsers: "Back to list of users", | |||||
| noSocialMedia:"User takes not part in any social media.", | |||||
| noPhoneNumber:"User has no phone number saved." | |||||
| }, | }, | ||||
| selectionLevels: { | selectionLevels: { | ||||
| done: { | done: { | ||||
| FD: "Candidates accepted", | FD: "Candidates accepted", | ||||
| }, | }, | ||||
| }, | }, | ||||
| registration:{ | |||||
| phone: 'Phone', | |||||
| linkedinProfile: 'LinkedIn profile', | |||||
| position: 'Position', | |||||
| registration: { | |||||
| phone: "Phone", | |||||
| linkedinProfile: "LinkedIn profile", | |||||
| position: "Position", | |||||
| link: "Registration link", | |||||
| register: "Register", | |||||
| twoSteps:"Two steps to the HR Center.", | |||||
| mistake:"There was a mistake..." | |||||
| }, | |||||
| filters: { | |||||
| filters: "Filters", | |||||
| experience: "Years of experience", | |||||
| technologies: "Technologies", | |||||
| employmentType: "Type of employment", | |||||
| internship: "Internship", | |||||
| work: "Work", | |||||
| workHour: "Work hour", | |||||
| partTime: "Part-time", | |||||
| fullTime: "Full-time", | |||||
| search: "Search", | |||||
| dateOfApplication: "Date of application", | |||||
| category: "Category", | |||||
| creationDate: "Creation date", | |||||
| advancedTechnologies: "Advanced technologies", | |||||
| }, | |||||
| candidates: { | |||||
| candidates: "Candidates", | |||||
| candidate: "Candidate", | |||||
| experience: "Experience", | |||||
| informations: "Informations", | |||||
| contact: "Contact", | |||||
| comment: "Comment", | |||||
| allApplications: "All applications", | |||||
| backToCandidates: "Back to all candidates", | |||||
| tableView: "Table view", | |||||
| search: "Search", | |||||
| position: "Position", | |||||
| deleteCandidate:"Deleting candidates", | |||||
| deleteCandidateQuestion:"Are you sure you want to delete the candidate?" | |||||
| }, | |||||
| dialogs: { | |||||
| candidateName: "Candidate's name", | |||||
| interviewerName: "Interviewer's name (optional)", | |||||
| interviewerName2: "Interviewer's name", | |||||
| duration: "Duration", | |||||
| appointment: "Appointment", | |||||
| }, | |||||
| schedule: { | |||||
| items: "items", | |||||
| planner: "Activity planner", | |||||
| }, | |||||
| selection: { | |||||
| report: "Detailed report", | |||||
| link: "Link to Google Meet", | |||||
| noCandidates: "There are no candidates in the selection", | |||||
| interviewer: "Interviewer", | |||||
| noInterviewer:"The process has no interviewer.", | |||||
| title:"Selection flow", | |||||
| subtitle:"All candidates", | |||||
| addInterviewer:"Adding an interviewer", | |||||
| selection:"Selection", | |||||
| changeInterviewer:"Change of interviewer", | |||||
| addCandidate:"Adding candidate", | |||||
| interview:"HR interview", | |||||
| filterDate:"Date" | |||||
| }, | |||||
| patterns:{ | |||||
| made:"Made", | |||||
| pattern:"Pattern", | |||||
| scheduling:"Scheduling an appointment", | |||||
| messageText:"Message text", | |||||
| message:"MESSAGE", | |||||
| editing:"Editing", | |||||
| patternsMade:"Patterns made", | |||||
| noPatterns:"There are currently no patterns added", | |||||
| addPattern:"Add pattern", | |||||
| editing2:"Editin mode" | |||||
| }, | |||||
| stats:{ | |||||
| statistic:"Statistic", | |||||
| selectionFlow:"Selection flow", | |||||
| relations:"Relations", | |||||
| number:"Number", | |||||
| contacted:"Number of contacted:", | |||||
| registered:"Registered by positions" | |||||
| } | } | ||||
| }; | }; |
| common: { | common: { | ||||
| // close: 'Close', | // close: 'Close', | ||||
| // trademark: 'TM', | // trademark: 'TM', | ||||
| // search: 'Search', | |||||
| search: "Pretrazi", | |||||
| // error: 'Error', | // error: 'Error', | ||||
| // continue: 'Continue', | // continue: 'Continue', | ||||
| labelUsername: "Korisničko ime", | labelUsername: "Korisničko ime", | ||||
| labelPassword: "Šifra", | labelPassword: "Šifra", | ||||
| labelConfirmPassword: "Ponovljena šifra", | labelConfirmPassword: "Ponovljena šifra", | ||||
| or: "ili", | or: "ili", | ||||
| and: "i", | |||||
| // next: 'Next', | // next: 'Next', | ||||
| // nextPage: 'Next page', | // nextPage: 'Next page', | ||||
| // previousPage: 'Previous page', | // previousPage: 'Previous page', | ||||
| // back: 'Back', | |||||
| back: "NAZAD", | |||||
| // goBack: 'Go Back', | // goBack: 'Go Back', | ||||
| // ok: 'Ok', | // ok: 'Ok', | ||||
| // done: 'Done', | // done: 'Done', | ||||
| // confirm: 'Confirm', | |||||
| confirm: "Potvrdi", | |||||
| // printDownload: 'Print/Download', | // printDownload: 'Print/Download', | ||||
| // cancel: 'Cancel', | |||||
| cancel: "Otkazi", | |||||
| delete: "Obrisi", | |||||
| change: "Promeni", | |||||
| // remove: 'Remove', | // remove: 'Remove', | ||||
| // invite: 'Invite', | // invite: 'Invite', | ||||
| // save: 'Save', | // save: 'Save', | ||||
| // complete: 'Complete', | // complete: 'Complete', | ||||
| // download: 'Download', | |||||
| download: "Preuzmi", | |||||
| // yes: 'Yes', | // yes: 'Yes', | ||||
| // no: 'No', | // no: 'No', | ||||
| // to: 'to', | |||||
| to: "Do", | |||||
| from: "Od", | |||||
| // select: 'Select...', | // select: 'Select...', | ||||
| // none: 'None', | // none: 'None', | ||||
| // date: { | // date: { | ||||
| // range: '{{start}} to {{end}}', | // range: '{{start}} to {{end}}', | ||||
| // }, | // }, | ||||
| continue: "NASTAVI", | |||||
| firstName: "Ime", | |||||
| lastName: "Prezime", | |||||
| gender: "Pol", | |||||
| male: "Muski", | |||||
| female: "Zenski", | |||||
| dateOfBirth: "Datum rodjenja", | |||||
| phoneNumber: "Broj telefona", | |||||
| socialNetwork: "Drustvene mreze", | |||||
| address: "adresa", | |||||
| specialist: "Specijalista", | |||||
| title: "Naslov", | |||||
| experience: "godina iskustva", | |||||
| }, | }, | ||||
| login: { | login: { | ||||
| welcome: "Dobrodošli!", | welcome: "Dobrodošli!", | ||||
| emailRequired: "Potrebno je uneti email adresu.", | emailRequired: "Potrebno je uneti email adresu.", | ||||
| // signUp: 'Sign Up', | // signUp: 'Sign Up', | ||||
| usernameRequired: "Potrebno je uneti korisničko ime.", | usernameRequired: "Potrebno je uneti korisničko ime.", | ||||
| passwordRequired: "Potrebno je uneti šifru.", | |||||
| forgotYourPassword: "Zaboravio/la si šifru?", | |||||
| resetYourPassword: "Nova šifra", | |||||
| resetYourPasswordHelpText: "Unesi novu šifru.", | |||||
| passwordRequired: "Potrebno je uneti lozinku.", | |||||
| forgotYourPassword: "Zaboravio/la si lozinku?", | |||||
| resetYourPassword: "Nova lozinka", | |||||
| resetYourPasswordHelpText: "Unesi novu lozinku.", | |||||
| forgotYourPasswordHelpText: | forgotYourPasswordHelpText: | ||||
| "Samo unesi e-mail adresu svog HR Center profila.", | "Samo unesi e-mail adresu svog HR Center profila.", | ||||
| forgotYourPasswordButton: "POŠALJI", | forgotYourPasswordButton: "POŠALJI", | ||||
| forgotYourPasswordBackLink: "Nazad na Login", | forgotYourPasswordBackLink: "Nazad na Login", | ||||
| forgotYourPasswordConfimation: | forgotYourPasswordConfimation: | ||||
| "Proveri email adresu da bi resetovao šifru.", | |||||
| passwordDontMatch: "Šifre se ne poklapaju.", | |||||
| "Proveri email adresu da bi resetovao lozinku.", | |||||
| passwordDontMatch: "Lozinke se ne poklapaju.", | |||||
| // _useDifferentEmail: 'Use different email address or username', | // _useDifferentEmail: 'Use different email address or username', | ||||
| // get useDifferentEmail() { | // get useDifferentEmail() { | ||||
| // return this._useDifferentEmail; | // return this._useDifferentEmail; | ||||
| // this._useDifferentEmail = value; | // this._useDifferentEmail = value; | ||||
| // }, | // }, | ||||
| signInWithGoogle: "Prijava putem Google-a", | signInWithGoogle: "Prijava putem Google-a", | ||||
| invalidEmail:"Format adrese nije validan" | |||||
| invalidEmail: "Format adrese nije validan", | |||||
| specialCharacterRequired: | |||||
| "Lozinka mora da sadrzi najmanje jedan poseban znak", | |||||
| numberRequired: "Lozinka mora da sadrzi najmanje jedan broj", | |||||
| lowercaseRequired: "Lozinka mora da sadrzi najmanje jedno malo slovo", | |||||
| uppercaseRequired: "Lozinka mora da sadrzi najmanje jedno veliko slovo", | |||||
| numberOfCharactersRequired: "Lozinka mora da sadrzi najmanje 8 karaktera", | |||||
| retypePassword: "Molimo ponovo unesite lozinku.", | |||||
| matchPasswords: "Lozinke se ne poklapaju", | |||||
| passwordConfirmation: "Potvrda lozinke", | |||||
| }, | }, | ||||
| // password: { | // password: { | ||||
| // weak: 'weak', | // weak: 'weak', | ||||
| // }, | // }, | ||||
| nav: { | nav: { | ||||
| ads: 'Oglasi', | |||||
| selectionFlow: 'Tok Selekcije', | |||||
| candidates: 'Kandidati', | |||||
| schedule: 'Planer', | |||||
| patterns: 'Šabloni', | |||||
| navigation: "Navigacija", | |||||
| ads: "Oglasi", | |||||
| selectionFlow: "Tok Selekcije", | |||||
| candidates: "Kandidati", | |||||
| schedule: "Planer", | |||||
| patterns: "Šabloni", | |||||
| statistics: "Statistika", | statistics: "Statistika", | ||||
| users: 'Korisnici', | |||||
| signOut: 'Izloguj se' | |||||
| users: "Korisnici", | |||||
| signOut: "Izloguj se", | |||||
| }, | }, | ||||
| ads: { | ads: { | ||||
| activeAds: "Aktivni Oglasi", | activeAds: "Aktivni Oglasi", | ||||
| adDetailsRequirements: "Zahtevi", | adDetailsRequirements: "Zahtevi", | ||||
| adDetailsOffer: "Šta Nudimo", | adDetailsOffer: "Šta Nudimo", | ||||
| archiveAdsCandidates: "Prijavljeni kandidati", | archiveAdsCandidates: "Prijavljeni kandidati", | ||||
| experience: "godina iskustva", | |||||
| noExperience: "Nema iskustva", | |||||
| adding: "Dodavanje", | |||||
| ad: "Oglas", | |||||
| ads: "Oglasi", | |||||
| signUp: "Prijavi se", | |||||
| dragPdf1: "Prevuci .pdf dokument u ovom delu ekrana ili", | |||||
| dragPdf2: "na racunaru", | |||||
| coverLetter: "Propratno pismo (Opciono)", | |||||
| professionalQualification: "Strucna sprema", | |||||
| technologies: "Tehnologije koje znas", | |||||
| others: "Druge", | |||||
| noExperience2: "Nije potrebno iskustvo", | |||||
| registered: "prijavljenih", | |||||
| backToAds: "Nazad na sve oglase", | |||||
| thereIsNoAds: "Nažalost, trenutno nema aktivnih oglasa", | |||||
| addAd: "Uvek možete dodati novi u samo par jednostavnih koraka", | |||||
| adNewAd: "Dodaj oglas", | |||||
| expirationDate: "Datum isteka oglasa", | |||||
| duties: "Glavna zaduženja", | |||||
| conditions: "Uslovi", | |||||
| offer: "Šta nudimo", | |||||
| archivingAds:"Arhiviranje oglasa", | |||||
| archivingAdQuestion:"Da li ste sigurni da želite da arhivirate oglas?", | |||||
| }, | }, | ||||
| selection: { | |||||
| title: "Tok Selekcije", | |||||
| subtitle: "Svi kandidati", | |||||
| tipHeader: "Savet za uspešan Screening test", | |||||
| tipBody: "Zapamtite da odeljenje za ljudske resurse u sebi sadrži reč „ljudski“. HR treba da vas vidi kakvi ste i da bi stekli osećaj za vašu stvarnu ličnost i postarali se da se dobro uklopite u kompaniju. Zato je bolje da se ponašate prirodno i opušteno. Važno je i pokazati da posedujete snažne međuljudske veštine i da se ponašate profesionalno. Na dan intervjua budite tačni, predstavite se pristojno i obucite se na odgovarajući način. To znači da razmislite o slici kompanije, ali i da se odevate na način koji vam je ugodan tokom dana.", | |||||
| filterStatus: "Status", | |||||
| filterDate: "Datum", | |||||
| filterFrom: "Od", | |||||
| filterTo: "Do", | |||||
| filterSubmit: "Pretraži" | |||||
| }, | |||||
| // selection: { | |||||
| // title: "Tok Selekcije", | |||||
| // subtitle: "Svi kandidati", | |||||
| // tipHeader: "Savet za uspešan Screening test", | |||||
| // tipBody: | |||||
| // "Zapamtite da odeljenje za ljudske resurse u sebi sadrži reč „ljudski“. HR treba da vas vidi kakvi ste i da bi stekli osećaj za vašu stvarnu ličnost i postarali se da se dobro uklopite u kompaniju. Zato je bolje da se ponašate prirodno i opušteno. Važno je i pokazati da posedujete snažne međuljudske veštine i da se ponašate profesionalno. Na dan intervjua budite tačni, predstavite se pristojno i obucite se na odgovarajući način. To znači da razmislite o slici kompanije, ali i da se odevate na način koji vam je ugodan tokom dana.", | |||||
| // filterStatus: "Status", | |||||
| // filterDate: "Datum", | |||||
| // filterFrom: "Od", | |||||
| // filterTo: "Do", | |||||
| // filterSubmit: "Pretraži", | |||||
| // }, | |||||
| users: { | users: { | ||||
| management: "Upravljanje korisnicima", | management: "Upravljanje korisnicima", | ||||
| fullName: 'Ime i prezime', | |||||
| position: 'Pozicija', | |||||
| invite: 'Pozovi', | |||||
| inviteUser: 'Pozovi korisnika', | |||||
| regLink: 'Registracioni link', | |||||
| receiver: 'Primalac', | |||||
| user: 'Korisnik', | |||||
| contact: 'Kontakt', | |||||
| phone: 'Telefon', | |||||
| socials: 'Društvene mreže', | |||||
| fullName: "Ime i prezime", | |||||
| position: "Pozicija", | |||||
| invite: "Pozovi", | |||||
| inviteUser: "Pozovi korisnika", | |||||
| regLink: "Registracioni link", | |||||
| receiver: "Primalac", | |||||
| user: "Korisnik", | |||||
| contact: "Kontakt", | |||||
| phone: "Telefon", | |||||
| socials: "Društvene mreže", | |||||
| resetPassword: "Resetuj lozinku", | |||||
| resetLink: | |||||
| "Da li ste sigurni da zelite da posaljete link za resetovanje lozinke?", | |||||
| disableUser: "Onemoguci korisnika", | |||||
| questionDisableUser: | |||||
| "Da li ste sigurni da zelite da onemogucite korisnika?", | |||||
| deleteUser: "Obrisite korisnika", | |||||
| questionDeleteUser: "Da li ste sigurni da zelite ukloniti korisnika?", | |||||
| block: "Blokiraj", | |||||
| unblock: "Odblokiraj", | |||||
| profile: "Uredi profil", | |||||
| positionNotDeclared: "Pozicija nije jos uvek odredjena.", | |||||
| backToUsers: "Nazad na listu korisnika", | |||||
| noSocialMedia: "Korisnik nije deo nijedne drustvene mreze.", | |||||
| noPhoneNumber: "Korisnik nema sacuvan broj telefona.", | |||||
| }, | }, | ||||
| filter: { | filter: { | ||||
| title: "Filteri" | |||||
| title: "Filteri", | |||||
| }, | }, | ||||
| selectionLevels: { | selectionLevels: { | ||||
| done: { | done: { | ||||
| FD: "Primljenih kandidata", | FD: "Primljenih kandidata", | ||||
| }, | }, | ||||
| }, | }, | ||||
| registration:{ | |||||
| phone: 'Broj Telefona', | |||||
| linkedinProfile: 'LinkedIn profil', | |||||
| position: 'Pozicija', | |||||
| } | |||||
| registration: { | |||||
| phone: "Broj Telefona", | |||||
| linkedinProfile: "LinkedIn profil", | |||||
| position: "Pozicija", | |||||
| link: "Registracioni link", | |||||
| register: "Registruj se", | |||||
| twoSteps: "Dva koraka do HR Centra.", | |||||
| mistake: "Doslo je do greske...", | |||||
| }, | |||||
| filters: { | |||||
| filters: "Filteri", | |||||
| experience: "Godine iskustva", | |||||
| technologies: "Tehnologije", | |||||
| employmentType: "Tip zaposlenja", | |||||
| internship: "Strucna praksa", | |||||
| work: "Posao", | |||||
| workHour: "Radno vreme", | |||||
| partTime: "Skraceno vreme", | |||||
| fullTime: "Puno vreme", | |||||
| search: "Pretrazi", | |||||
| dateOfApplication: "Datum prijave", | |||||
| category: "Kategorija", | |||||
| creationDate: "Datum kreiranja", | |||||
| advancedTechnologies: "Napredne tehnologije", | |||||
| }, | |||||
| candidates: { | |||||
| candidates: "Kandidati", | |||||
| candidate: "Kandidat", | |||||
| experience: "Iskustvo", | |||||
| informations: "Informacije", | |||||
| contact: "Kontakt", | |||||
| comment: "Komentar", | |||||
| allApplications: "Sve prijave", | |||||
| backToCandidates: "Nazad na sve kandidate", | |||||
| tableView: "Tablicni prikaz", | |||||
| search: "Pretraga", | |||||
| position: "Pozicija", | |||||
| deleteCandidate:"Brisanje kandidata", | |||||
| deleteCandidateQuestion:"Da li ste sigurni da zelite da obrisete kandidata?" | |||||
| }, | |||||
| dialogs: { | |||||
| candidateName: "Ime kandidata", | |||||
| interviewerName: "Ime intervjuera (opciono)", | |||||
| interviewerName2: "Ime intervjuera", | |||||
| duration: "Trajanje", | |||||
| appointment: "Termin", | |||||
| }, | |||||
| schedule: { | |||||
| items: "stavke", | |||||
| planner: "Planer aktivnosti", | |||||
| }, | |||||
| selection: { | |||||
| report: "Detaljni izvestaj", | |||||
| link: "Link do Google Meet-a", | |||||
| noCandidates: "Nema kandidata u selekciji", | |||||
| interviewer: "Intervjuer", | |||||
| noInterviewer: "Proces nema intervjuera.", | |||||
| title: "Tok selekcije", | |||||
| subtitle: "Svi kandidati", | |||||
| addInterviewer:"Dodavanje intervjuera", | |||||
| selection:"Selekcija", | |||||
| changeInterviewer:"Promena intervjuera", | |||||
| addCandidate:"Dodavanje kandidata", | |||||
| interview:"HR intervju", | |||||
| filterDate:"Datum" | |||||
| }, | |||||
| patterns: { | |||||
| made: "Napravljen", | |||||
| pattern: "Šablon", | |||||
| scheduling: "Zakazivanje termina", | |||||
| messageText: "Tekst poruke", | |||||
| message: "PORUKU", | |||||
| editing: "Uređivanje", | |||||
| patternsMade: "Napravljeni Šabloni", | |||||
| noPatterns: "Trenutno nema dodatih sablona", | |||||
| addPattern: "Dodaj Šablon", | |||||
| editing2:"Režim uređivanja" | |||||
| }, | |||||
| stats: { | |||||
| statistic: "Statistika", | |||||
| selectionFlow: "Tok selekcije", | |||||
| relations: "Odnosi", | |||||
| number: "Broj", | |||||
| contacted: "Broj kontaktiranih:", | |||||
| registered: "Prijavljeni po pozicijama", | |||||
| }, | |||||
| }; | }; |
| <h1>{ad.title}</h1> | <h1>{ad.title}</h1> | ||||
| </div> | </div> | ||||
| <div className="ad-details-tech-logo-title-sub"> | <div className="ad-details-tech-logo-title-sub"> | ||||
| <sub>| {ad.totalApplicants} prijavljenih</sub> | |||||
| <sub> | |||||
| | {ad.totalApplicants} {t("ads.registered")} | |||||
| </sub> | |||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| {!(new Date(ad.expiredAt) < new Date()) && ( | {!(new Date(ad.expiredAt) < new Date()) && ( | ||||
| </div> | </div> | ||||
| <ConfirmDialog | <ConfirmDialog | ||||
| open={showArchiveAdDialog} | open={showArchiveAdDialog} | ||||
| title={"Arhiviranje oglasa"} | |||||
| title={t("ads.archivingAd")} | |||||
| subtitle={ad.title} | subtitle={ad.title} | ||||
| imgSrc={archiveIcon} | imgSrc={archiveIcon} | ||||
| content="Da li ste sigurni da želite da arhivirate oglas?" | |||||
| content={t("ads.archivingAdQuestion")} | |||||
| onClose={() => { | onClose={() => { | ||||
| setShowArchiveAdDialog(false); | setShowArchiveAdDialog(false); | ||||
| }} | }} | ||||
| </p> | </p> | ||||
| </div> | </div> | ||||
| <div className="ad-details-content-work-time"> | <div className="ad-details-content-work-time"> | ||||
| <button>Posao</button> | |||||
| <button>Full-time</button> | |||||
| <button>{t("filters.work")}</button> | |||||
| <button>{t("filters.fullTime")}</button> | |||||
| </div> | </div> | ||||
| <div className="ad-details-content-content"> | <div className="ad-details-content-content"> | ||||
| <div className="ad-details-content-conten-description"> | <div className="ad-details-content-conten-description"> | ||||
| className="ad-details-buttons-link" | className="ad-details-buttons-link" | ||||
| onClick={() => history.push({ pathname: ADS_PAGE })} | onClick={() => history.push({ pathname: ADS_PAGE })} | ||||
| > | > | ||||
| Nazad na sve oglase | |||||
| {t("ads.backToAds")} | |||||
| </button> | </button> | ||||
| {!(new Date(ad.expiredAt) < new Date()) && ( | {!(new Date(ad.expiredAt) < new Date()) && ( | ||||
| className="c-btn c-btn--primary add-ad-btn apply-for-ad-button" | className="c-btn c-btn--primary add-ad-btn apply-for-ad-button" | ||||
| onClick={() => setApplyForAdOpenModal(true)} | onClick={() => setApplyForAdOpenModal(true)} | ||||
| > | > | ||||
| PRIJAVI SE | |||||
| {t("ads.signUp").toUpperCase()} | |||||
| </IconButton> | </IconButton> | ||||
| )} | )} | ||||
| </div> | </div> |
| } | } | ||||
| onClick={() => handleChangeVisibility(true)} | onClick={() => handleChangeVisibility(true)} | ||||
| > | > | ||||
| {!matches && "Pretraga"} | |||||
| {!matches && t("common.search")} | |||||
| <img | <img | ||||
| style={{ | style={{ | ||||
| position: "relative", | position: "relative", | ||||
| <FilterButton onShowFilters={handleToggleFiltersDrawer} /> | <FilterButton onShowFilters={handleToggleFiltersDrawer} /> | ||||
| </div> | </div> | ||||
| <img src={noActiveAds} alt="noActiveAds" /> | <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> | |||||
| <h1>{t("ads.thereIsNoAds")}</h1> | |||||
| <p>{t("ads.addAd")}</p> | |||||
| <div className="add-ad add-ad-no-ads"> | <div className="add-ad add-ad-no-ads"> | ||||
| <IconButton | <IconButton | ||||
| className="c-btn ads-page-btn c-btn--primary add-ad-btn" | className="c-btn ads-page-btn c-btn--primary add-ad-btn" | ||||
| onClick={() => history.push(CREATE_AD_PAGE)} | onClick={() => history.push(CREATE_AD_PAGE)} | ||||
| > | > | ||||
| Dodaj Oglas | |||||
| {t("ads.adNewAd")} | |||||
| </IconButton> | </IconButton> | ||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| className="c-btn ads-page-btn c-btn--primary add-ad-btn" | className="c-btn ads-page-btn c-btn--primary add-ad-btn" | ||||
| onClick={createAd} | onClick={createAd} | ||||
| > | > | ||||
| + Oglas | |||||
| + {t("ads.ad")} | |||||
| </IconButton> | </IconButton> | ||||
| </div> | </div> | ||||
| )} | )} |
| import React from "react"; | import React from "react"; | ||||
| import PropTypes from "prop-types"; | import PropTypes from "prop-types"; | ||||
| import { useTranslation } from "react-i18next"; | |||||
| const CreateAdFirstStep = ({ | const CreateAdFirstStep = ({ | ||||
| title, | title, | ||||
| expiredAt, | expiredAt, | ||||
| setExpiredAt, | setExpiredAt, | ||||
| }) => { | }) => { | ||||
| const { t } = useTranslation(); | |||||
| const employmentTypeHandler = (type) => { | const employmentTypeHandler = (type) => { | ||||
| setEmploymentType(type); | setEmploymentType(type); | ||||
| }; | }; | ||||
| return ( | return ( | ||||
| <div data-testid="create-ad-first-step-form"> | <div data-testid="create-ad-first-step-form"> | ||||
| <div className="create-ad-form-control"> | <div className="create-ad-form-control"> | ||||
| <label>Naslov</label> | |||||
| <label>{t("common.title")}</label> | |||||
| <input | <input | ||||
| type="text" | type="text" | ||||
| className="create-ad-form-control-first-step-input" | className="create-ad-form-control-first-step-input" | ||||
| /> | /> | ||||
| </div> | </div> | ||||
| <div className="create-ad-form-control"> | <div className="create-ad-form-control"> | ||||
| <label>Tip zaposlenja</label> | |||||
| <label>{t("filters.employmentType")}</label> | |||||
| <div className="create-ad-form-control-buttons"> | <div className="create-ad-form-control-buttons"> | ||||
| <button | <button | ||||
| className={`c-btn ${ | className={`c-btn ${ | ||||
| }`} | }`} | ||||
| onClick={employmentTypeHandler.bind(this, "Work")} | onClick={employmentTypeHandler.bind(this, "Work")} | ||||
| > | > | ||||
| Posao | |||||
| {t("filters.work")} | |||||
| </button> | </button> | ||||
| <button | <button | ||||
| className={`c-btn ${ | className={`c-btn ${ | ||||
| }`} | }`} | ||||
| onClick={employmentTypeHandler.bind(this, "Intership")} | onClick={employmentTypeHandler.bind(this, "Intership")} | ||||
| > | > | ||||
| Intership | |||||
| {t("filters.internship")} | |||||
| </button> | </button> | ||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| <div className="create-ad-form-control"> | <div className="create-ad-form-control"> | ||||
| <label>Radno vreme</label> | |||||
| <label>{t("filters.workHour")}</label> | |||||
| <div className="create-ad-form-control-buttons"> | <div className="create-ad-form-control-buttons"> | ||||
| <button | <button | ||||
| className={`c-btn ${ | className={`c-btn ${ | ||||
| }`} | }`} | ||||
| onClick={workHourHandler.bind(this, "PartTime")} | onClick={workHourHandler.bind(this, "PartTime")} | ||||
| > | > | ||||
| Part-time | |||||
| {t("filters.partTime")} | |||||
| </button> | </button> | ||||
| <button | <button | ||||
| className={`c-btn ${ | className={`c-btn ${ | ||||
| }`} | }`} | ||||
| onClick={workHourHandler.bind(this, "FullTime")} | onClick={workHourHandler.bind(this, "FullTime")} | ||||
| > | > | ||||
| Full-time | |||||
| {t("filters.fullTime")} | |||||
| </button> | </button> | ||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| <div className="create-ad-form-control"> | <div className="create-ad-form-control"> | ||||
| <label>Datum isteka oglasa</label> | |||||
| <label>{t("ads.expirationDate")}</label> | |||||
| <input | <input | ||||
| type="date" | type="date" | ||||
| className="create-ad-form-control-first-step-input" | className="create-ad-form-control-first-step-input" |
| import CreateAdThirdStep from "./CreateAdThirdStep"; | import CreateAdThirdStep from "./CreateAdThirdStep"; | ||||
| import { useHistory } from "react-router-dom"; | import { useHistory } from "react-router-dom"; | ||||
| import { ADS_PAGE } from "../../constants/pages"; | import { ADS_PAGE } from "../../constants/pages"; | ||||
| import { useTranslation } from "react-i18next"; | |||||
| const CreateAdPage = () => { | const CreateAdPage = () => { | ||||
| const [stage, setStage] = useState(1); | const [stage, setStage] = useState(1); | ||||
| const [experience, setExperience] = useState(0); | const [experience, setExperience] = useState(0); | ||||
| const childRef = useRef(); | const childRef = useRef(); | ||||
| const history = useHistory(); | const history = useHistory(); | ||||
| const { t } = useTranslation(); | |||||
| const technologies = useSelector( | const technologies = useSelector( | ||||
| (state) => state.addAdTechnologies.technologies | (state) => state.addAdTechnologies.technologies | ||||
| alt="plus" | alt="plus" | ||||
| style={{ width: "18px", height: "18px" }} | style={{ width: "18px", height: "18px" }} | ||||
| /> | /> | ||||
| <h2>Dodavanje</h2> | |||||
| <h2>{t("ads.adding")}</h2> | |||||
| <h2> | <h2> | ||||
| <sub> | Oglas</sub> | |||||
| <sub> | {t("ads.ad")}</sub> | |||||
| </h2> | </h2> | ||||
| </div> | </div> | ||||
| <div className="create-ad-steps"> | <div className="create-ad-steps"> | ||||
| disabled={stage === 1} | disabled={stage === 1} | ||||
| onClick={backClickHandler} | onClick={backClickHandler} | ||||
| > | > | ||||
| NAZAD | |||||
| {t("common.back")} | |||||
| </button> | </button> | ||||
| <button | <button | ||||
| className="create-ad-buttons-forward" | className="create-ad-buttons-forward" | ||||
| onClick={forwardClickHandler} | onClick={forwardClickHandler} | ||||
| > | > | ||||
| NASTAVI | |||||
| {t("common.continue")} | |||||
| </button> | </button> | ||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| <div className="create-ad-go-back"> | <div className="create-ad-go-back"> | ||||
| <button onClick={() => history.push(ADS_PAGE)}> | <button onClick={() => history.push(ADS_PAGE)}> | ||||
| Nazad na sve oglase | |||||
| {t("ads.backToAds")} | |||||
| </button> | </button> | ||||
| </div> | </div> | ||||
| </div> | </div> |
| import { Checkbox, FormControlLabel } from "@mui/material"; | import { Checkbox, FormControlLabel } from "@mui/material"; | ||||
| import { useDispatch } from "react-redux"; | import { useDispatch } from "react-redux"; | ||||
| import { changeIsCheckedAddAdValue } from "../../store/actions/addAdTechnologies/addAdTechnologiesActions"; | import { changeIsCheckedAddAdValue } from "../../store/actions/addAdTechnologies/addAdTechnologiesActions"; | ||||
| import { useTranslation } from "react-i18next"; | |||||
| const CreateAdSecondStep = ({ technologies, experience, setExperience }) => { | const CreateAdSecondStep = ({ technologies, experience, setExperience }) => { | ||||
| const { t } = useTranslation; | |||||
| const dispatch = useDispatch(); | const dispatch = useDispatch(); | ||||
| const handleCheckboxes = (technologyId) => { | const handleCheckboxes = (technologyId) => { | ||||
| return ( | return ( | ||||
| <div data-testid="create-ad-second-step-form"> | <div data-testid="create-ad-second-step-form"> | ||||
| <div className="create-ad-form-control"> | <div className="create-ad-form-control"> | ||||
| <label>Napredne tehnologije</label> | |||||
| <label>{t("filters.advancedTechnologies")}</label> | |||||
| </div> | </div> | ||||
| <div className="add-ad-modal-stage-sub-card"> | <div className="add-ad-modal-stage-sub-card"> | ||||
| <div className="add-ad-modal-stage-sub-card-checkboxes-group"> | <div className="add-ad-modal-stage-sub-card-checkboxes-group"> | ||||
| </div> | </div> | ||||
| <div className="add-ad-modal-stage-sub-card-checkboxes-group"> | <div className="add-ad-modal-stage-sub-card-checkboxes-group"> | ||||
| <label>Other</label> | |||||
| <label>{t("ads.others")}</label> | |||||
| <div className="add-ad-modal-stage-sub-card-checkboxes"> | <div className="add-ad-modal-stage-sub-card-checkboxes"> | ||||
| {technologies | {technologies | ||||
| .filter((x) => x.technologyType === "Other") | .filter((x) => x.technologyType === "Other") | ||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| <div className="create-ad-form-control"> | <div className="create-ad-form-control"> | ||||
| <label>Godine iskustva</label> | |||||
| <label>{t("filters.experience")}</label> | |||||
| <input | <input | ||||
| type="number" | type="number" | ||||
| min={0} | min={0} |
| import { Editor } from "@tinymce/tinymce-react"; | import { Editor } from "@tinymce/tinymce-react"; | ||||
| import { useRef } from "react"; | import { useRef } from "react"; | ||||
| import { useEffect } from "react"; | import { useEffect } from "react"; | ||||
| import { useTranslation } from "react-i18next"; | |||||
| const CreateAdThirdStep = ({ childRef }) => { | const CreateAdThirdStep = ({ childRef }) => { | ||||
| const { t } = useTranslation(); | |||||
| const editorKeyResponsibilitiesRef = useRef(); | const editorKeyResponsibilitiesRef = useRef(); | ||||
| const editorRequirementsRef = useRef(); | const editorRequirementsRef = useRef(); | ||||
| const editorOfferRef = useRef(); | const editorOfferRef = useRef(); | ||||
| return ( | return ( | ||||
| <div data-testid="create-ad-third-step-form"> | <div data-testid="create-ad-third-step-form"> | ||||
| <div className="create-ad-form-control"> | <div className="create-ad-form-control"> | ||||
| <label>Glavna zaduženja</label> | |||||
| <label>{t("ads.duties")}</label> | |||||
| <Editor | <Editor | ||||
| onInit={(evt, editor) => | onInit={(evt, editor) => | ||||
| (editorKeyResponsibilitiesRef.current = editor) | (editorKeyResponsibilitiesRef.current = editor) | ||||
| /> | /> | ||||
| </div> | </div> | ||||
| <div className="create-ad-form-control"> | <div className="create-ad-form-control"> | ||||
| <label>Uslovi</label> | |||||
| <label>{t("ads.conditions")}</label> | |||||
| <Editor | <Editor | ||||
| onInit={(evt, editor) => (editorRequirementsRef.current = editor)} | onInit={(evt, editor) => (editorRequirementsRef.current = editor)} | ||||
| style={{ height: "1rem !important" }} | style={{ height: "1rem !important" }} | ||||
| /> | /> | ||||
| </div> | </div> | ||||
| <div className="create-ad-form-control"> | <div className="create-ad-form-control"> | ||||
| <label>Šta nudimo</label> | |||||
| <label>{t("ads.offer")}</label> | |||||
| <Editor | <Editor | ||||
| onInit={(evt, editor) => (editorOfferRef.current = editor)} | onInit={(evt, editor) => (editorOfferRef.current = editor)} | ||||
| style={{ height: "1rem !important" }} | style={{ height: "1rem !important" }} |
| import { useTheme } from "@mui/system"; | import { useTheme } from "@mui/system"; | ||||
| import { useMediaQuery } from "@mui/material"; | import { useMediaQuery } from "@mui/material"; | ||||
| import { selectAdsCandidates } from "../../store/selectors/candidatesSelectors"; | import { selectAdsCandidates } from "../../store/selectors/candidatesSelectors"; | ||||
| import { useTranslation } from "react-i18next"; | |||||
| const AdsCandidatesPage = ({ history, search }) => { | const AdsCandidatesPage = ({ history, search }) => { | ||||
| const dispatch = useDispatch(); | const dispatch = useDispatch(); | ||||
| const [getRef, setRef] = useDynamicRefs(); | const [getRef, setRef] = useDynamicRefs(); | ||||
| const theme = useTheme(); | const theme = useTheme(); | ||||
| const matches = useMediaQuery(theme.breakpoints.down("361")); | const matches = useMediaQuery(theme.breakpoints.down("361")); | ||||
| const {t} = useTranslation() | |||||
| useEffect(() => { | useEffect(() => { | ||||
| dispatch( | dispatch( | ||||
| <img src={adImage} className="ads-candidates-image" /> | <img src={adImage} className="ads-candidates-image" /> | ||||
| <p className="ads-candidates-title">{adCandidates.title}</p> | <p className="ads-candidates-title">{adCandidates.title}</p> | ||||
| <p className="ads-candidates-numberOfApplicants"> | <p className="ads-candidates-numberOfApplicants"> | ||||
| | {adCandidates.nubmerOfApplicants} prijavljenih | |||||
| | {adCandidates.nubmerOfApplicants} {t("ads.registered")} | |||||
| </p> | </p> | ||||
| </div> | </div> | ||||
| <div className="ads-candidates-slider"> | <div className="ads-candidates-slider"> |
| import { selectCandidate } from "../../store/selectors/candidateSelectors"; | import { selectCandidate } from "../../store/selectors/candidateSelectors"; | ||||
| import { selectAuthUser } from "../../store/selectors/userSelectors"; | import { selectAuthUser } from "../../store/selectors/userSelectors"; | ||||
| import { selectUsers } from "../../store/selectors/usersSelector"; | import { selectUsers } from "../../store/selectors/usersSelector"; | ||||
| import { useTranslation } from "react-i18next"; | |||||
| const CandidateDetailsPage = ({ history }) => { | const CandidateDetailsPage = ({ history }) => { | ||||
| const dispatch = useDispatch(); | const dispatch = useDispatch(); | ||||
| const adsSliderRef = useRef(); | const adsSliderRef = useRef(); | ||||
| const [showDelete, setDelete] = useState(false); | const [showDelete, setDelete] = useState(false); | ||||
| const [usersToNotify, setUsersToNotify] = useState([]); //emails of users which are taged in comment | const [usersToNotify, setUsersToNotify] = useState([]); //emails of users which are taged in comment | ||||
| const { t } = useTranslation(); | |||||
| useEffect(() => { | useEffect(() => { | ||||
| dispatch(fetchCandidate({ id })); | dispatch(fetchCandidate({ id })); | ||||
| <div className="main-candidate-container"> | <div className="main-candidate-container"> | ||||
| <ConfirmDialog | <ConfirmDialog | ||||
| open={showDelete} | open={showDelete} | ||||
| title={"Brisanje kandidata"} | |||||
| title={t("candidates.deleteCandidate")} | |||||
| subtitle={candidate.firstName + " " + candidate.lastName} | subtitle={candidate.firstName + " " + candidate.lastName} | ||||
| imgSrc={deleteIcon} | imgSrc={deleteIcon} | ||||
| content="Da li ste sigurni da zelite da obrisete kandidata?" | |||||
| content={t("candidates.deleteCandidateQuestion")} | |||||
| onClose={() => { | onClose={() => { | ||||
| setDelete(false); | setDelete(false); | ||||
| }} | }} | ||||
| <div className="r-b-rectangle"></div> | <div className="r-b-rectangle"></div> | ||||
| <div className="top-candidate-container"> | <div className="top-candidate-container"> | ||||
| <div> | <div> | ||||
| <p className="candidate-header">Kandidat</p> | |||||
| <p className="candidate-header">{t("candidates.candidate")}</p> | |||||
| <span className="separation-line">|</span> | <span className="separation-line">|</span> | ||||
| <p className="candidate-lower-header"> | <p className="candidate-lower-header"> | ||||
| {candidate.firstName} {candidate.lastName} | {candidate.firstName} {candidate.lastName} | ||||
| className="c-btn c-btn--primary-outlined candidate-btn" | className="c-btn c-btn--primary-outlined candidate-btn" | ||||
| onClick={() => setDelete(true)} | onClick={() => setDelete(true)} | ||||
| > | > | ||||
| Obrisi | |||||
| {t("common.delete")} | |||||
| <img src={deleteImage} alt="delete" className="candidates-image" /> | <img src={deleteImage} alt="delete" className="candidates-image" /> | ||||
| </IconButton> | </IconButton> | ||||
| </div> | </div> | ||||
| <div className="details-candidate-container"> | <div className="details-candidate-container"> | ||||
| <p style={{ margin: 0 }} data-testid="candidate-experience"> | <p style={{ margin: 0 }} data-testid="candidate-experience"> | ||||
| {candidate.experience === 0 | {candidate.experience === 0 | ||||
| ? "No experience" | |||||
| : "Experience:" + candidate.experience} | |||||
| ? t("common.noExperience") | |||||
| : t("candidates.experience") + ":" + candidate.experience} | |||||
| </p> | </p> | ||||
| <div className="technologies-candidate-container"> | <div className="technologies-candidate-container"> | ||||
| {candidate.technologyApplicants.map((obj, index) => ( | {candidate.technologyApplicants.map((obj, index) => ( | ||||
| <div className="candidate-informations-container"> | <div className="candidate-informations-container"> | ||||
| <div className="candidate-informations-sub-container"> | <div className="candidate-informations-sub-container"> | ||||
| <div className="candidate-property-container"> | <div className="candidate-property-container"> | ||||
| <p className="informations-candidate-header">Informacije</p> | |||||
| <p className="candidate-property">Pol:</p> | |||||
| <p className="candidate-property">Strucna sprema:</p> | |||||
| <p className="informations-candidate-header"> | |||||
| {t("candidates.informations")} | |||||
| </p> | |||||
| <p className="candidate-property">{t("common.gender")}:</p> | |||||
| <p className="candidate-property"> | |||||
| {t("ads.professionalQualification")}: | |||||
| </p> | |||||
| </div> | </div> | ||||
| <div | <div | ||||
| style={{ alignSelf: "flex-end", marginLeft: 42 }} | style={{ alignSelf: "flex-end", marginLeft: 42 }} | ||||
| className="candidate-property-container" | className="candidate-property-container" | ||||
| > | > | ||||
| <p className="candidate-property-value" data-testId="candidate-gender"> | |||||
| {candidate.gender === "M" ? "Muski" : "Zenski"} | |||||
| <p | |||||
| className="candidate-property-value" | |||||
| data-testId="candidate-gender" | |||||
| > | |||||
| {candidate.gender === "M" | |||||
| ? t("common.male") | |||||
| : t("common.female")} | |||||
| </p> | </p> | ||||
| <p className="candidate-property-value"> | <p className="candidate-property-value"> | ||||
| {candidate.professionalQualification === "" | {candidate.professionalQualification === "" | ||||
| </div> | </div> | ||||
| <div className="candidate-informations-sub-container"> | <div className="candidate-informations-sub-container"> | ||||
| <div className="candidate-property-container"> | <div className="candidate-property-container"> | ||||
| <p className="informations-candidate-header">Kontakt</p> | |||||
| <p className="informations-candidate-header"> | |||||
| {t("candidates.contact")} | |||||
| </p> | |||||
| <p className="candidate-property">Email:</p> | <p className="candidate-property">Email:</p> | ||||
| <p className="candidate-property">Telefon:</p> | |||||
| <p className="candidate-property">{t("users.phone")}:</p> | |||||
| </div> | </div> | ||||
| <div | <div | ||||
| style={{ alignSelf: "flex-end", marginLeft: 42 }} | style={{ alignSelf: "flex-end", marginLeft: 42 }} | ||||
| </div> | </div> | ||||
| <div className="candidate-informations-sub-container"> | <div className="candidate-informations-sub-container"> | ||||
| <div className="candidate-property-container"> | <div className="candidate-property-container"> | ||||
| <p className="informations-candidate-header">Drustvene mreze</p> | |||||
| <p className="informations-candidate-header"> | |||||
| {t("users.socials")} | |||||
| </p> | |||||
| <p className="candidate-property">Linkedln</p> | <p className="candidate-property">Linkedln</p> | ||||
| <p className="candidate-property">GitHub</p> | <p className="candidate-property">GitHub</p> | ||||
| <p className="candidate-property">BitBucket</p> | <p className="candidate-property">BitBucket</p> | ||||
| </div> | </div> | ||||
| <div className="comment-separation-line"></div> | <div className="comment-separation-line"></div> | ||||
| <div className="send-comment-container"> | <div className="send-comment-container"> | ||||
| <p>Komentar</p> | |||||
| <p>{t("candidates.comment")}</p> | |||||
| <div className="send-comment-sub-container"> | <div className="send-comment-sub-container"> | ||||
| <MentionsInput | <MentionsInput | ||||
| value={inputValue} | value={inputValue} | ||||
| alt="plane" | alt="plane" | ||||
| className="candidates-image" | className="candidates-image" | ||||
| /> | /> | ||||
| <p>Komentar</p> | |||||
| <p>{t("candidates.comment")}</p> | |||||
| </div> | </div> | ||||
| ) : ( | ) : ( | ||||
| <div | <div | ||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| <div className="applicant-ads-container"> | <div className="applicant-ads-container"> | ||||
| <p style={{ marginLeft: matches ? 36 : 144 }}>Sve prijave</p> | |||||
| <p style={{ marginLeft: matches ? 36 : 144 }}> | |||||
| {t("candidates.allApplications")} | |||||
| </p> | |||||
| <div className="applicant-ads-container-2"> | <div className="applicant-ads-container-2"> | ||||
| <div | <div | ||||
| style={{ | style={{ | ||||
| {(matches | {(matches | ||||
| ? candidate.ads.length > 1 | ? candidate.ads.length > 1 | ||||
| : candidate.ads.length > 5) && ( | : candidate.ads.length > 5) && ( | ||||
| <div className="active-ads-ads-arrows" data-testid="candidate-ad-responsive-arrows"> | |||||
| <div | |||||
| className="active-ads-ads-arrows" | |||||
| data-testid="candidate-ad-responsive-arrows" | |||||
| > | |||||
| <button onClick={activeAdsArrowLeftHandler}> | <button onClick={activeAdsArrowLeftHandler}> | ||||
| <img src={arrow_left} alt="arrow-left" /> | <img src={arrow_left} alt="arrow-left" /> | ||||
| </button> | </button> | ||||
| className="applicant-ads-back-button" | className="applicant-ads-back-button" | ||||
| onClick={goToPageWithAllCandidates} | onClick={goToPageWithAllCandidates} | ||||
| > | > | ||||
| Nazad na sve kandidate | |||||
| {t("candidates.backToCandidates")} | |||||
| </p> | </p> | ||||
| <a | <a | ||||
| className="applicant-cv-button" | className="applicant-cv-button" | ||||
| href={`data:application/pdf;base64,${candidate.cv}`} | href={`data:application/pdf;base64,${candidate.cv}`} | ||||
| title="Download pdf document" | title="Download pdf document" | ||||
| > | > | ||||
| Preuzmi cv | |||||
| {t("common.download")} cv | |||||
| </a> | </a> | ||||
| </div> | </div> | ||||
| </div> | </div> |
| import { selectTechnologies } from "../../store/selectors/technologiesSelectors"; | import { selectTechnologies } from "../../store/selectors/technologiesSelectors"; | ||||
| import { useDispatch, useSelector } from "react-redux"; | import { useDispatch, useSelector } from "react-redux"; | ||||
| import Fade from "@mui/material/Fade"; | import Fade from "@mui/material/Fade"; | ||||
| import { useTranslation } from "react-i18next"; | |||||
| const CandidatesPage = ({ history }) => { | const CandidatesPage = ({ history }) => { | ||||
| const dispatch = useDispatch(); | const dispatch = useDispatch(); | ||||
| { name: "Posao", isChecked: false }, | { name: "Posao", isChecked: false }, | ||||
| { name: "Intership", isChecked: false }, | { name: "Intership", isChecked: false }, | ||||
| ]); | ]); | ||||
| const { t } = useTranslation(); | |||||
| useEffect(() => { | useEffect(() => { | ||||
| dispatch(setTechnologiesReq()); | dispatch(setTechnologiesReq()); | ||||
| <div className="top-candidates-container"> | <div className="top-candidates-container"> | ||||
| {!matches ? ( | {!matches ? ( | ||||
| <p className="candidates-header" data-testid="candidates-header1"> | <p className="candidates-header" data-testid="candidates-header1"> | ||||
| Kandidati | |||||
| {t("candidates.candidates")} | |||||
| </p> | </p> | ||||
| ) : ( | ) : ( | ||||
| <p | <p | ||||
| data-testid="candidates-header2" | data-testid="candidates-header2" | ||||
| style={{ fontSize: "22px" }} | style={{ fontSize: "22px" }} | ||||
| > | > | ||||
| Kandidati | |||||
| {t("candidates.candidates")} | |||||
| </p> | </p> | ||||
| )} | )} | ||||
| <div style={{ postion: "relative" }}> | <div style={{ postion: "relative" }}> | ||||
| className="c-btn c-btn--primary-outlined candidate-btn all-white-btn" | className="c-btn c-btn--primary-outlined candidate-btn all-white-btn" | ||||
| onClick={changeView} | onClick={changeView} | ||||
| > | > | ||||
| Tablicni prikaz | |||||
| {t("candidates.tableView")} | |||||
| <img | <img | ||||
| src={tableImage} | src={tableImage} | ||||
| alt="table" | alt="table" | ||||
| className="c-btn c-btn--primary-outlined candidate-btn candidate-btn-view-2" | className="c-btn c-btn--primary-outlined candidate-btn candidate-btn-view-2" | ||||
| onClick={changeView} | onClick={changeView} | ||||
| > | > | ||||
| Tablicni prikaz | |||||
| {t("candidates.tableView")} | |||||
| <img | <img | ||||
| src={tableImage} | src={tableImage} | ||||
| alt="table" | alt="table" | ||||
| className="c-btn c-btn--primary-outlined candidate-btn" | className="c-btn c-btn--primary-outlined candidate-btn" | ||||
| onClick={handleChangeVisibility} | onClick={handleChangeVisibility} | ||||
| > | > | ||||
| Pretraga | |||||
| {t("candidates.search")} | |||||
| <img | <img | ||||
| src={searchImage} | src={searchImage} | ||||
| alt="filter" | alt="filter" | ||||
| className="c-btn c-btn--primary-outlined candidate-btn candidate-btn-filters1" | className="c-btn c-btn--primary-outlined candidate-btn candidate-btn-filters1" | ||||
| onClick={handleToggleFiltersDrawer} | onClick={handleToggleFiltersDrawer} | ||||
| > | > | ||||
| Filteri | |||||
| {t("filters.filters")} | |||||
| <img | <img | ||||
| src={filterImage} | src={filterImage} | ||||
| alt="filter" | alt="filter" |
| } from "../../store/selectors/candidatesSelectors"; | } from "../../store/selectors/candidatesSelectors"; | ||||
| import { getCV } from "../../request/candidatesRequest"; | import { getCV } from "../../request/candidatesRequest"; | ||||
| import Fade from "@mui/material/Fade"; | import Fade from "@mui/material/Fade"; | ||||
| import { useTranslation } from "react-i18next"; | |||||
| const TableViewPage = ({ | const TableViewPage = ({ | ||||
| history, | history, | ||||
| setPage, | setPage, | ||||
| const matches = useMediaQuery(theme.breakpoints.down("361")); | const matches = useMediaQuery(theme.breakpoints.down("361")); | ||||
| const [linkToCV, setLinkToCV] = useState(""); | const [linkToCV, setLinkToCV] = useState(""); | ||||
| const [isCVDisplayed, setIsCVDisplayed] = useState(false); | const [isCVDisplayed, setIsCVDisplayed] = useState(false); | ||||
| const { t } = useTranslation(); | |||||
| useEffect(() => { | useEffect(() => { | ||||
| dispatch( | dispatch( | ||||
| > | > | ||||
| <thead> | <thead> | ||||
| <tr className="headingRow"> | <tr className="headingRow"> | ||||
| <th>Ime i prezime</th> | |||||
| {!isCVDisplayed && <th>Iskustvo</th>} | |||||
| <th>Datum prijave</th> | |||||
| <th>Pozicija</th> | |||||
| <th> | |||||
| {t("common.firstName")} {t("common.and")} {t("common.lastName")} | |||||
| </th> | |||||
| {!isCVDisplayed && <th>{t("candidates.experience")}</th>} | |||||
| <th>{t("filters.dateOfApplication")}</th> | |||||
| <th>{t("candidates.position")}</th> | |||||
| <th>CV link</th> | <th>CV link</th> | ||||
| </tr> | </tr> | ||||
| </thead> | </thead> |
| import { PATTERNS_PAGE } from "../../constants/pages"; | import { PATTERNS_PAGE } from "../../constants/pages"; | ||||
| import { useHistory } from "react-router-dom"; | import { useHistory } from "react-router-dom"; | ||||
| import parse from "html-react-parser"; | import parse from "html-react-parser"; | ||||
| import { useTranslation } from "react-i18next"; | |||||
| const PatternDetailsPage = () => { | const PatternDetailsPage = () => { | ||||
| const [emails, setEmails] = useState([]); | const [emails, setEmails] = useState([]); | ||||
| const { id } = useParams(); | const { id } = useParams(); | ||||
| const dispatch = useDispatch(); | const dispatch = useDispatch(); | ||||
| const history = useHistory(); | const history = useHistory(); | ||||
| const { t } = useTranslation(); | |||||
| const navigateToPatternsPage = () => { | const navigateToPatternsPage = () => { | ||||
| history.push(PATTERNS_PAGE); | history.push(PATTERNS_PAGE); | ||||
| <div className="pattern-details" data-testid="pattern-details"> | <div className="pattern-details" data-testid="pattern-details"> | ||||
| <div className="pattern-details-header"> | <div className="pattern-details-header"> | ||||
| <p> | <p> | ||||
| <span>Napravljen:</span>{" "} | |||||
| <span>{t("patterns.made")}:</span>{" "} | |||||
| {new Date(pattern.createdAt).toLocaleDateString()} | {new Date(pattern.createdAt).toLocaleDateString()} | ||||
| </p> | </p> | ||||
| </div> | </div> | ||||
| <div className="pattern-details-card"> | <div className="pattern-details-card"> | ||||
| <div className="pattern-details-card-title"> | <div className="pattern-details-card-title"> | ||||
| <div className="pattern-details-card-title-title"> | <div className="pattern-details-card-title-title"> | ||||
| <h1>Šablon</h1> | |||||
| <h1>{t("pattern.pattern")}</h1> | |||||
| </div> | </div> | ||||
| <div className="pattern-details-card-title-sub"> | <div className="pattern-details-card-title-sub"> | ||||
| <sub> | Zakazivanje termina</sub> | |||||
| <sub> | {t("patterns.scheduling")}</sub> | |||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| <div className="pattern-details-card-sub-card"> | <div className="pattern-details-card-sub-card"> | ||||
| <div className="pattern-details-card-sub-card"> | <div className="pattern-details-card-sub-card"> | ||||
| <div className="pattern-details-card-sub-card-title"> | <div className="pattern-details-card-sub-card-title"> | ||||
| <p>Teskt poruke</p> | |||||
| <p>{t("patterns.messageText")}</p> | |||||
| </div> | </div> | ||||
| <div className="pattern-details-card-sub-card-message-pattern"> | <div className="pattern-details-card-sub-card-message-pattern"> | ||||
| {/* <textarea disabled value={pattern.message}></textarea> */} | {/* <textarea disabled value={pattern.message}></textarea> */} | ||||
| data-testid="ad-details-buttons-link" | data-testid="ad-details-buttons-link" | ||||
| onClick={() => history.push(PATTERNS_PAGE)} | onClick={() => history.push(PATTERNS_PAGE)} | ||||
| > | > | ||||
| Nazad na sve oglase | |||||
| {t("ads.backToAds")} | |||||
| </button> | </button> | ||||
| <IconButton | <IconButton | ||||
| disabled={emails && emails.length === 0} | disabled={emails && emails.length === 0} | ||||
| }} | }} | ||||
| src={sendMessage} | src={sendMessage} | ||||
| /> | /> | ||||
| PORUKU | |||||
| {t("patterns.message")} | |||||
| </IconButton> | </IconButton> | ||||
| </div> | </div> | ||||
| </div> | </div> |
| import PatternFilters from "../../components/Patterns/PatternFilters"; | import PatternFilters from "../../components/Patterns/PatternFilters"; | ||||
| import IconButton from "../../components/IconButton/IconButton"; | import IconButton from "../../components/IconButton/IconButton"; | ||||
| import { Editor } from "@tinymce/tinymce-react"; | import { Editor } from "@tinymce/tinymce-react"; | ||||
| import { useTranslation } from "react-i18next"; | |||||
| const PatternsPage = ({ history }) => { | const PatternsPage = ({ history }) => { | ||||
| const theme = useTheme(); | const theme = useTheme(); | ||||
| const dispatch = useDispatch(); | const dispatch = useDispatch(); | ||||
| const addPatternEditor = useRef(); | const addPatternEditor = useRef(); | ||||
| const editPatternEditor = useRef(); | const editPatternEditor = useRef(); | ||||
| const { t } = useTranslation(); | |||||
| useEffect(() => { | useEffect(() => { | ||||
| dispatch(setPatternsReq()); | dispatch(setPatternsReq()); | ||||
| <img src={plusIcon} alt="plus" /> | <img src={plusIcon} alt="plus" /> | ||||
| </div> | </div> | ||||
| <div className="add-pattern-modal-header-title-title"> | <div className="add-pattern-modal-header-title-title"> | ||||
| <p>Dodavanje</p> | |||||
| <p>{t("ads.adding")}</p> | |||||
| </div> | </div> | ||||
| <div className="add-pattern-modal-header-title-sub"> | <div className="add-pattern-modal-header-title-sub"> | ||||
| <sub> | Šablon</sub> | |||||
| <sub> | {t("patterns.pattern")}</sub> | |||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| <div | <div | ||||
| </div> | </div> | ||||
| <form onSubmit={submitAddPatternHandler}> | <form onSubmit={submitAddPatternHandler}> | ||||
| <div className="add-pattern-modal-form-control"> | <div className="add-pattern-modal-form-control"> | ||||
| <label>Naslov</label> | |||||
| <label>{t("common.title")}</label> | |||||
| <input | <input | ||||
| type="text" | type="text" | ||||
| placeholder="ex. Datum HR intervjua" | placeholder="ex. Datum HR intervjua" | ||||
| /> | /> | ||||
| </div> | </div> | ||||
| <div className="add-pattern-modal-form-control"> | <div className="add-pattern-modal-form-control"> | ||||
| <label>Kategorija</label> | |||||
| <label>{t("filters.category")}</label> | |||||
| <select | <select | ||||
| name="add-pattern-category" | name="add-pattern-category" | ||||
| value={addPatternCategory} | value={addPatternCategory} | ||||
| </select> | </select> | ||||
| </div> | </div> | ||||
| <div className="add-pattern-modal-form-control"> | <div className="add-pattern-modal-form-control"> | ||||
| <label>Tekst poruke</label> | |||||
| <label>{t("patterns.messageText")}</label> | |||||
| <Editor | <Editor | ||||
| onInit={(evt, editor) => (addPatternEditor.current = editor)} | onInit={(evt, editor) => (addPatternEditor.current = editor)} | ||||
| style={{ height: "100px !important" }} | style={{ height: "100px !important" }} | ||||
| <img src={userPageBtnIcon} alt="plus" /> | <img src={userPageBtnIcon} alt="plus" /> | ||||
| </div> | </div> | ||||
| <div className="edit-pattern-modal-header-title-title"> | <div className="edit-pattern-modal-header-title-title"> | ||||
| <p>Uređivanje</p> | |||||
| <p>{t("patterns.editing")}</p> | |||||
| </div> | </div> | ||||
| <div className="edit-pattern-modal-header-title-sub"> | <div className="edit-pattern-modal-header-title-sub"> | ||||
| <sub> | Šablon</sub> | |||||
| <sub> | {t("patterns.pattern")}</sub> | |||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| <div | <div | ||||
| </div> | </div> | ||||
| <form onSubmit={submitEditPatternHandler}> | <form onSubmit={submitEditPatternHandler}> | ||||
| <div className="edit-pattern-modal-form-control"> | <div className="edit-pattern-modal-form-control"> | ||||
| <label>Naslov</label> | |||||
| <label>{t("common.title")}</label> | |||||
| <input | <input | ||||
| type="text" | type="text" | ||||
| placeholder="ex. Datum HR intervjua" | placeholder="ex. Datum HR intervjua" | ||||
| /> | /> | ||||
| </div> | </div> | ||||
| <div className="edit-pattern-modal-form-control"> | <div className="edit-pattern-modal-form-control"> | ||||
| <label>Kategorija</label> | |||||
| <label>{t("filters.category")}</label> | |||||
| <select | <select | ||||
| name="edit-pattern-category" | name="edit-pattern-category" | ||||
| value={editPattern ? editPattern.selectionLevelId : 1} | value={editPattern ? editPattern.selectionLevelId : 1} | ||||
| </select> | </select> | ||||
| </div> | </div> | ||||
| <div className="edit-pattern-modal-form-control"> | <div className="edit-pattern-modal-form-control"> | ||||
| <label>Tekst poruke</label> | |||||
| <label>{t("patterns.messageText")}</label> | |||||
| <Editor | <Editor | ||||
| initialValue={editPattern ? editPattern.message : ""} | initialValue={editPattern ? editPattern.message : ""} | ||||
| onInit={(evt, editor) => (editPatternEditor.current = editor)} | onInit={(evt, editor) => (editPatternEditor.current = editor)} | ||||
| <div className="patterns"> | <div className="patterns"> | ||||
| <div className="patterns-header"> | <div className="patterns-header"> | ||||
| <div> | <div> | ||||
| <h1>Napravljeni Šabloni</h1> | |||||
| <h1>{t("patterns.patternsMade")}</h1> | |||||
| </div> | </div> | ||||
| <div style={{ display: "flex" }}> | <div style={{ display: "flex" }}> | ||||
| <IconButton | <IconButton | ||||
| isShownEdit && "pattern-header-active-button" | isShownEdit && "pattern-header-active-button" | ||||
| }`} | }`} | ||||
| > | > | ||||
| {!matches && "Režim uređivanja"} | |||||
| {!matches && t("patterns.editing2")} | |||||
| <img | <img | ||||
| style={{ | style={{ | ||||
| position: "relative", | position: "relative", | ||||
| )} | )} | ||||
| {patterns && patterns.length === 0 && ( | {patterns && patterns.length === 0 && ( | ||||
| <div> | <div> | ||||
| <p>Trenutno nema dodatih sablona</p> | |||||
| <p>{t("patterns.noPatterns")}</p> | |||||
| </div> | </div> | ||||
| )} | )} | ||||
| {patterns && | {patterns && | ||||
| className="c-btn c-btn--primary add-ad-btn add-pattern-btn" | className="c-btn c-btn--primary add-ad-btn add-pattern-btn" | ||||
| onClick={() => setOpenAddPatternModal(true)} | onClick={() => setOpenAddPatternModal(true)} | ||||
| > | > | ||||
| Dodaj Šablon | |||||
| {t("patterns.addPattern")} | |||||
| </IconButton> | </IconButton> | ||||
| </div> | </div> | ||||
| </div> | </div> |
| {t("login.welcome")} | {t("login.welcome")} | ||||
| </Typography> | </Typography> | ||||
| <Typography variant="p" sx={{ mb: 2 }}> | <Typography variant="p" sx={{ mb: 2 }}> | ||||
| Dva koraka do HR Centra. | |||||
| {t("registration.twoSteps")} | |||||
| </Typography> | </Typography> | ||||
| <div className="steps-cont"> | <div className="steps-cont"> | ||||
| <div | <div | ||||
| <Step /> | <Step /> | ||||
| </> | </> | ||||
| ) : ( | ) : ( | ||||
| <h3 data-testid='error-h' style={{ margin: "50px 0px" }}>There was a mistake...</h3> | |||||
| <h3 data-testid='error-h' style={{ margin: "50px 0px" }}>{t("registration.mistake")}</h3> | |||||
| )} | )} | ||||
| </Box> | </Box> | ||||
| </Container> | </Container> |
| getFormatedDayOrMonth, | getFormatedDayOrMonth, | ||||
| } from "../../util/helpers/dateHelpers"; | } from "../../util/helpers/dateHelpers"; | ||||
| import PropTypes from "prop-types"; | import PropTypes from "prop-types"; | ||||
| import { useTranslation } from "react-i18next"; | |||||
| const SchedulePage = ({ history }) => { | const SchedulePage = ({ history }) => { | ||||
| const dispatch = useDispatch(); | const dispatch = useDispatch(); | ||||
| ); | ); | ||||
| const [currentlySelectedDate, setCurrentlySelectedDate] = useState(""); | const [currentlySelectedDate, setCurrentlySelectedDate] = useState(""); | ||||
| const [currentlySelectedDay, setCurrentlySelectedDay] = useState(0); | const [currentlySelectedDay, setCurrentlySelectedDay] = useState(0); | ||||
| const { t } = useTranslation(); | |||||
| useEffect(() => { | useEffect(() => { | ||||
| dispatch(fetchSchedule({ month: currentMonth + 1, year: currentYear })); | dispatch(fetchSchedule({ month: currentMonth + 1, year: currentYear })); | ||||
| history={history} | history={history} | ||||
| /> | /> | ||||
| <div> | <div> | ||||
| <p className="schedule-page-main-header">Planer aktivnosti</p> | |||||
| <p className="schedule-page-main-header">{t("schedule.planner")}</p> | |||||
| <p className="schedule-page-header"> | <p className="schedule-page-header"> | ||||
| | {getMonthString(currentMonth)} {currentYear} | | {getMonthString(currentMonth)} {currentYear} | ||||
| </p> | </p> |
| <div className="l-t-rectangle"></div> | <div className="l-t-rectangle"></div> | ||||
| <div className="r-b-rectangle"></div> | <div className="r-b-rectangle"></div> | ||||
| {/* <AdFilters /> */} | {/* <AdFilters /> */} | ||||
| <div data-testid='appl-sel' className="ads"> | |||||
| <div data-testid="appl-sel" className="ads"> | |||||
| {processes && processes.length > 0 && ( | {processes && processes.length > 0 && ( | ||||
| <div className="active-ads"> | <div className="active-ads"> | ||||
| <div className="active-ads-header"> | <div className="active-ads-header"> | ||||
| schedguler={ | schedguler={ | ||||
| process?.scheduler | process?.scheduler | ||||
| ? `${process?.scheduler?.firstName} ${process?.scheduler?.lastName}` | ? `${process?.scheduler?.firstName} ${process?.scheduler?.lastName}` | ||||
| : "Proces nema intervjuera." | |||||
| : t("selection.noInterviewer") | |||||
| } | } | ||||
| link={process.link} | link={process.link} | ||||
| date={new Date(process.date)} | date={new Date(process.date)} | ||||
| {applicant?.selectionProcesses?.some( | {applicant?.selectionProcesses?.some( | ||||
| (n) => n.status === "Neuspešno" | (n) => n.status === "Neuspešno" | ||||
| ) | ) | ||||
| ? "Komentar:" | |||||
| ? t("candidates.comment") + ":" | |||||
| : t("selection.tipHeader")} | : t("selection.tipHeader")} | ||||
| </h3> | </h3> | ||||
| <p> | <p> | ||||
| </div> | </div> | ||||
| <div className="add-ad"> | <div className="add-ad"> | ||||
| <Link className="ad-details-buttons-link" to="/selectionFlow"> | <Link className="ad-details-buttons-link" to="/selectionFlow"> | ||||
| Nazad na sve kandidate | |||||
| {t("candidates.backToCandidates")} | |||||
| </Link> | </Link> | ||||
| </div> | </div> | ||||
| </> | </> |
| import Selection from "../../components/Selection/Selection"; | import Selection from "../../components/Selection/Selection"; | ||||
| import FilterButton from "../../components/Button/FilterButton"; | import FilterButton from "../../components/Button/FilterButton"; | ||||
| import { useTranslation } from "react-i18next"; | import { useTranslation } from "react-i18next"; | ||||
| import AddAdModal from "../../components/Ads/AddAdModal"; | |||||
| import { useDispatch } from "react-redux"; | import { useDispatch } from "react-redux"; | ||||
| import { setDoneProcess } from "../../store/actions/processes/processAction"; | import { setDoneProcess } from "../../store/actions/processes/processAction"; | ||||
| import { setProcessesReq } from "../../store/actions/processes/processesAction"; | import { setProcessesReq } from "../../store/actions/processes/processesAction"; | ||||
| const SelectionProcessPage = ({ history }) => { | const SelectionProcessPage = ({ history }) => { | ||||
| const [toggleFiltersDrawer, setToggleFiltersDrawer] = useState(false); | const [toggleFiltersDrawer, setToggleFiltersDrawer] = useState(false); | ||||
| const [toggleModal, setToggleModal] = useState(false); | |||||
| const [toggleInitModal, setToggleInitModal] = useState(false); | const [toggleInitModal, setToggleInitModal] = useState(false); | ||||
| // const errorMessage = useSelector(selectProcessesError); | // const errorMessage = useSelector(selectProcessesError); | ||||
| const process = useSelector(selectDoneProcess); | const process = useSelector(selectDoneProcess); | ||||
| setToggleFiltersDrawer((oldState) => !oldState); | setToggleFiltersDrawer((oldState) => !oldState); | ||||
| }; | }; | ||||
| const handleToggleModal = () => { | |||||
| setToggleModal((oldState) => !oldState); | |||||
| }; | |||||
| const renderList = processes?.map((item, index) => { | const renderList = processes?.map((item, index) => { | ||||
| return ( | return ( | ||||
| <Selection | <Selection | ||||
| /> | /> | ||||
| <StatusDialog | <StatusDialog | ||||
| open={activeProcess !== null} | open={activeProcess !== null} | ||||
| title={"Dodavanje intervjuera"} | |||||
| subtitle={"Selekcija"} | |||||
| title={t("selection.addInterviewer")} | |||||
| subtitle={t("selection.selection")} | |||||
| imgSrc={plus} | imgSrc={plus} | ||||
| onClose={() => { | onClose={() => { | ||||
| setActiveProcess(null); | setActiveProcess(null); | ||||
| /> | /> | ||||
| <CommentProcessDialog | <CommentProcessDialog | ||||
| open={activeProcessUnsuccess !== null} | open={activeProcessUnsuccess !== null} | ||||
| title={"Komentar"} | |||||
| subtitle={"Selekcija"} | |||||
| title={t("candidates.comment")} | |||||
| subtitle={t("selection.selection")} | |||||
| imgSrc={forbiden} | imgSrc={forbiden} | ||||
| onClose={() => { | onClose={() => { | ||||
| setActiveProcessUnsuccess(null); | setActiveProcessUnsuccess(null); | ||||
| /> | /> | ||||
| <InterviewerDialog | <InterviewerDialog | ||||
| open={activeInterview !== null} | open={activeInterview !== null} | ||||
| title={"Promena intervjuera"} | |||||
| subtitle={"Selekcija"} | |||||
| title={t("selection.changeInterviewer")} | |||||
| subtitle={t("selection.selection")} | |||||
| imgSrc={plus} | imgSrc={plus} | ||||
| onClose={() => { | onClose={() => { | ||||
| setActiveInterview(null); | setActiveInterview(null); | ||||
| /> | /> | ||||
| <InterviewDialog | <InterviewDialog | ||||
| open={toggleInitModal} | open={toggleInitModal} | ||||
| title={"Dodavanje kandidata"} | |||||
| subtitle={"HR intervju"} | |||||
| title={t("selection.addCandidate")} | |||||
| subtitle={t("selection.interview")} | |||||
| imgSrc={plus} | imgSrc={plus} | ||||
| onClose={() => { | onClose={() => { | ||||
| setToggleInitModal(false); | setToggleInitModal(false); | ||||
| }} | }} | ||||
| /> | /> | ||||
| <AddAdModal open={toggleModal} handleClose={handleToggleModal} /> | |||||
| <div className="selections"> | <div className="selections"> | ||||
| <div className="level-header"> | <div className="level-header"> | ||||
| <h1> | <h1> |
| // import ArchiveAd from "../../components/Ads/ArchiveAd"; | // import ArchiveAd from "../../components/Ads/ArchiveAd"; | ||||
| // import { AD_DETAILS_PAGE } from "../../constants/pages"; | // import { AD_DETAILS_PAGE } from "../../constants/pages"; | ||||
| const StatsPage = ({history}) => { | |||||
| const StatsPage = ({ history }) => { | |||||
| const dispatch = useDispatch(); | const dispatch = useDispatch(); | ||||
| const { stats } = useSelector((s) => s.stats); | const { stats } = useSelector((s) => s.stats); | ||||
| style={{ letterSpacing: "1px" }} | style={{ letterSpacing: "1px" }} | ||||
| className="page-heading px36-heading" | className="page-heading px36-heading" | ||||
| > | > | ||||
| Statistika | |||||
| {t("stats.statistic")} | |||||
| </h1> | </h1> | ||||
| <div className="stats-section"> | <div className="stats-section"> | ||||
| <h2 className="section-header">Tok Selekcije</h2> | |||||
| <h2 className="section-header">{t("stats.selectionFlow")}</h2> | |||||
| <div className="stats-items"> | <div className="stats-items"> | ||||
| {stats.levels && | {stats.levels && | ||||
| stats.levels.map((n) => ( | stats.levels.map((n) => ( | ||||
| <div key={n.level} className="stats-item" data-testid="stats-item"> | |||||
| <div | |||||
| key={n.level} | |||||
| className="stats-item" | |||||
| data-testid="stats-item" | |||||
| > | |||||
| <div className="stats-item-content"> | <div className="stats-item-content"> | ||||
| <h3>{n.countDone}</h3> | <h3>{n.countDone}</h3> | ||||
| <p> | <p> | ||||
| {returni18nLevel(n.level) != 'FD' && 'Obavljenih'}<br></br>{t('selectionLevels.done.'+returni18nLevel(n.level))} | |||||
| {returni18nLevel(n.level) != "FD" && t("common.done")} | |||||
| <br></br> | |||||
| {t("selectionLevels.done." + returni18nLevel(n.level))} | |||||
| </p> | </p> | ||||
| </div> | </div> | ||||
| <div className="bottom-static"></div> | <div className="bottom-static"></div> | ||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| <div className="stats-section"> | <div className="stats-section"> | ||||
| <h2 className="section-header">Odnosi</h2> | |||||
| <h2 className="section-header">{t("stats.relations")}</h2> | |||||
| <div className="stats-items-dynamic"> | <div className="stats-items-dynamic"> | ||||
| {stats.levels && | {stats.levels && | ||||
| stats.levels.map((n) => ( | stats.levels.map((n) => ( | ||||
| <div key={n.level} className="stats-item" data-testid="stats-item2"> | |||||
| <div | |||||
| key={n.level} | |||||
| className="stats-item" | |||||
| data-testid="stats-item2" | |||||
| > | |||||
| <div className="stats-item-content"> | <div className="stats-item-content"> | ||||
| <h3> | <h3> | ||||
| {n.countDone} | {n.countDone} | ||||
| {n.countAll} | {n.countAll} | ||||
| </h3> | </h3> | ||||
| <p> | <p> | ||||
| Broj {t('selectionLevels.done.'+returni18nLevel(n.level))}:<br></br> | |||||
| Broj kontaktiranih: | |||||
| {t("stats.number")}{" "} | |||||
| {t("selectionLevels.done." + returni18nLevel(n.level))}: | |||||
| <br></br> | |||||
| {t("stats.contacted")} | |||||
| </p> | </p> | ||||
| </div> | </div> | ||||
| <div className="bottom-dynamic"> | <div className="bottom-dynamic"> | ||||
| <div | <div | ||||
| style={{ width: `${(n.countDone * 1.0 / n.countAll) * 100}%` }} | |||||
| style={{ | |||||
| width: `${((n.countDone * 1.0) / n.countAll) * 100}%`, | |||||
| }} | |||||
| className="bottom-loader-indicator" | className="bottom-loader-indicator" | ||||
| ></div> | ></div> | ||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| <div className="stats-section"> | <div className="stats-section"> | ||||
| <h2 className="section-header">Prijavljeni po pozicijama</h2> | |||||
| <h2 className="section-header">{t("stats.registered")}</h2> | |||||
| </div> | </div> | ||||
| <div className="ads stat-ads"> | <div className="ads stat-ads"> | ||||
| {stats.ads && stats.ads.length > 0 && ( | |||||
| {stats.ads && stats.ads.length > 0 && ( | |||||
| <div className="archived-ads"> | <div className="archived-ads"> | ||||
| {/* <div className="archived-ads-header"> | {/* <div className="archived-ads-header"> | ||||
| <h2>{t("ads.archiveAds")}</h2> | <h2>{t("ads.archiveAds")}</h2> | ||||
| <img src={arrow_left} alt="arrow-left" /> | <img src={arrow_left} alt="arrow-left" /> | ||||
| </button> | </button> | ||||
| {stats.ads.length > 3 && ( | {stats.ads.length > 3 && ( | ||||
| <button onClick={arrowRightHandler} data-testid="right-arrow"> | |||||
| <button | |||||
| onClick={arrowRightHandler} | |||||
| data-testid="right-arrow" | |||||
| > | |||||
| <img src={arrow_right} alt="arrow-right" /> | <img src={arrow_right} alt="arrow-right" /> | ||||
| </button> | </button> | ||||
| )} | )} | ||||
| > | > | ||||
| {stats.ads.map((ad, index) => ( | {stats.ads.map((ad, index) => ( | ||||
| <StatsAd | <StatsAd | ||||
| count={ad.count} | |||||
| count={ad.count} | |||||
| key={index} | key={index} | ||||
| title={ad.title} | title={ad.title} | ||||
| minimumExperience={ad.minimumExperience} | minimumExperience={ad.minimumExperience} | ||||
| <img src={arrow_left} alt="arrow-left" /> | <img src={arrow_left} alt="arrow-left" /> | ||||
| </button> | </button> | ||||
| {stats.ads.length > 2 && ( | {stats.ads.length > 2 && ( | ||||
| <button onClick={arrowRightHandler} data-testid="right-arrow"> | |||||
| <button onClick={arrowRightHandler} data-testid="right-arrow"> | |||||
| <img src={arrow_right} alt="arrow-right" /> | <img src={arrow_right} alt="arrow-right" /> | ||||
| </button> | </button> | ||||
| )} | )} |
| import { useTheme } from "@emotion/react"; | import { useTheme } from "@emotion/react"; | ||||
| import { useMediaQuery } from "@mui/material"; | import { useMediaQuery } from "@mui/material"; | ||||
| import ConfirmDialog from "../../components/MUI/ConfirmDialog"; | import ConfirmDialog from "../../components/MUI/ConfirmDialog"; | ||||
| import { useTranslation } from "react-i18next"; | |||||
| const UserDetails = ({ history }) => { | const UserDetails = ({ history }) => { | ||||
| const theme = useTheme(); | const theme = useTheme(); | ||||
| const [showConfirm, setConfirm] = useState(false); | const [showConfirm, setConfirm] = useState(false); | ||||
| const [showReset, setReset] = useState(false); | const [showReset, setReset] = useState(false); | ||||
| const [showDelete, setDelete] = useState(false); | const [showDelete, setDelete] = useState(false); | ||||
| const { t } = useTranslation(); | |||||
| const { user,errorMessage } = useSelector((s) => s.userDetails); | |||||
| const { user, errorMessage } = useSelector((s) => s.userDetails); | |||||
| const handleReset = (email) => { | const handleReset = (email) => { | ||||
| dispatch( | dispatch( | ||||
| <div className="r-b-rectangle"></div> | <div className="r-b-rectangle"></div> | ||||
| <ConfirmDialog | <ConfirmDialog | ||||
| open={showReset} | open={showReset} | ||||
| title={"Reset password"} | |||||
| title={t("users.resetPassword")} | |||||
| subtitle={user?.firstName + " " + user?.lastName} | subtitle={user?.firstName + " " + user?.lastName} | ||||
| imgSrc={lock} | imgSrc={lock} | ||||
| content="Are you sure you want to send password reset link?" | |||||
| content={t("users.resetLink")} | |||||
| onClose={() => { | onClose={() => { | ||||
| setReset(false); | setReset(false); | ||||
| }} | }} | ||||
| /> | /> | ||||
| <ConfirmDialog | <ConfirmDialog | ||||
| open={showConfirm} | open={showConfirm} | ||||
| title={"Disable user"} | |||||
| title={t("users.disableUser")} | |||||
| subtitle={user?.firstName + " " + user?.lastName} | subtitle={user?.firstName + " " + user?.lastName} | ||||
| imgSrc={forbiden} | imgSrc={forbiden} | ||||
| content="Are you sure you want to disable user?" | |||||
| content={t("users.questionDisableUser")} | |||||
| onClose={() => { | onClose={() => { | ||||
| setConfirm(false); | setConfirm(false); | ||||
| }} | }} | ||||
| /> | /> | ||||
| <ConfirmDialog | <ConfirmDialog | ||||
| open={showDelete} | open={showDelete} | ||||
| title={"Delete user"} | |||||
| title={t("users.deleteUser")} | |||||
| subtitle={user?.firstName + " " + user?.lastName} | subtitle={user?.firstName + " " + user?.lastName} | ||||
| imgSrc={filters} | imgSrc={filters} | ||||
| content="Are you sure you want to delete user?" | |||||
| content={t("users.questionDeleteUser")} | |||||
| onClose={() => { | onClose={() => { | ||||
| setDelete(false); | setDelete(false); | ||||
| }} | }} | ||||
| }} | }} | ||||
| /> | /> | ||||
| <div className="pl-144 pt-36px"> | <div className="pl-144 pt-36px"> | ||||
| {errorMessage ? errorMessage : <> | |||||
| <div className="divider"> | |||||
| <div className="flex-center"> | |||||
| <h1 style={{ letterSpacing: "1px" }}>Korisnik</h1> | |||||
| <div | |||||
| className="vr" | |||||
| style={{ | |||||
| margin: "0px 10px 0px 15px", | |||||
| top: "6px", | |||||
| backgroundColor: "#252525", | |||||
| }} | |||||
| ></div> | |||||
| <h3 | |||||
| style={{ | |||||
| letterSpacing: "0.75px", | |||||
| position: "relative", | |||||
| top: "4px", | |||||
| }} | |||||
| className="text-blue" | |||||
| > | |||||
| {user && user.firstName} {user && user.lastName} | |||||
| </h3> | |||||
| </div> | |||||
| <div className="flex-center"> | |||||
| <IconButton | |||||
| className={ | |||||
| "c-btn--primary-outlined c-btn userPageBtn ml-20px no-padding" | |||||
| } | |||||
| onClick={() => setReset(true)} | |||||
| > | |||||
| {!matches && "Resetuj password"} | |||||
| <img | |||||
| style={{ | |||||
| position: "relative", | |||||
| top: -0.25, | |||||
| paddingLeft: !matches && "10px", | |||||
| }} | |||||
| src={lock} | |||||
| /> | |||||
| </IconButton> | |||||
| {!matches && ( | |||||
| <IconButton | |||||
| className={`c-btn--primary-outlined c-btn userPageBtn ml-20px no-padding ${ | |||||
| user?.isEnabled ? "activeEnable" : "deactiveEnable" | |||||
| }`} | |||||
| onClick={() => setConfirm(true)} | |||||
| > | |||||
| {user?.isEnabled ? "Blokiraj" : "Odblokiraj"} | |||||
| <img | |||||
| {errorMessage ? ( | |||||
| errorMessage | |||||
| ) : ( | |||||
| <> | |||||
| <div className="divider"> | |||||
| <div className="flex-center"> | |||||
| <h1 style={{ letterSpacing: "1px" }}>{t("users.user")}</h1> | |||||
| <div | |||||
| className="vr" | |||||
| style={{ | style={{ | ||||
| position: "relative", | |||||
| top: -0.25, | |||||
| paddingLeft: "10px", | |||||
| margin: "0px 10px 0px 15px", | |||||
| top: "6px", | |||||
| backgroundColor: "#252525", | |||||
| }} | }} | ||||
| src={forbiden} | |||||
| /> | |||||
| </IconButton> | |||||
| )} | |||||
| {!matches && ( | |||||
| <IconButton | |||||
| className={ | |||||
| "c-btn--primary-outlined c-btn userPageBtn ml-20px no-padding" | |||||
| } | |||||
| onClick={() => setDelete(true)} | |||||
| > | |||||
| Obrisi | |||||
| <img | |||||
| ></div> | |||||
| <h3 | |||||
| style={{ | style={{ | ||||
| letterSpacing: "0.75px", | |||||
| position: "relative", | position: "relative", | ||||
| top: -0.25, | |||||
| paddingLeft: "10px", | |||||
| top: "4px", | |||||
| }} | }} | ||||
| src={filters} | |||||
| /> | |||||
| </IconButton> | |||||
| )} | |||||
| <IconButton | |||||
| className={ | |||||
| "c-btn--primary-outlined c-btn userPageBtn ml-20px no-padding" | |||||
| } | |||||
| className="text-blue" | |||||
| > | |||||
| {user && user.firstName} {user && user.lastName} | |||||
| </h3> | |||||
| </div> | |||||
| <div className="flex-center"> | |||||
| <IconButton | |||||
| className={ | |||||
| "c-btn--primary-outlined c-btn userPageBtn ml-20px no-padding" | |||||
| } | |||||
| onClick={() => setReset(true)} | |||||
| > | |||||
| {!matches && t("users.resetPassword")} | |||||
| <img | |||||
| style={{ | |||||
| position: "relative", | |||||
| top: -0.25, | |||||
| paddingLeft: !matches && "10px", | |||||
| }} | |||||
| src={lock} | |||||
| /> | |||||
| </IconButton> | |||||
| {!matches && ( | |||||
| <IconButton | |||||
| className={`c-btn--primary-outlined c-btn userPageBtn ml-20px no-padding ${ | |||||
| user?.isEnabled ? "activeEnable" : "deactiveEnable" | |||||
| }`} | |||||
| onClick={() => setConfirm(true)} | |||||
| > | |||||
| {user?.isEnabled ? t("users.block") : t("users.unblock")} | |||||
| <img | |||||
| style={{ | |||||
| position: "relative", | |||||
| top: -0.25, | |||||
| paddingLeft: "10px", | |||||
| }} | |||||
| src={forbiden} | |||||
| /> | |||||
| </IconButton> | |||||
| )} | |||||
| {!matches && ( | |||||
| <IconButton | |||||
| className={ | |||||
| "c-btn--primary-outlined c-btn userPageBtn ml-20px no-padding" | |||||
| } | |||||
| onClick={() => setDelete(true)} | |||||
| > | |||||
| {t("common.delete")} | |||||
| <img | |||||
| style={{ | |||||
| position: "relative", | |||||
| top: -0.25, | |||||
| paddingLeft: "10px", | |||||
| }} | |||||
| src={filters} | |||||
| /> | |||||
| </IconButton> | |||||
| )} | |||||
| <IconButton | |||||
| className={ | |||||
| "c-btn--primary-outlined c-btn userPageBtn ml-20px no-padding" | |||||
| } | |||||
| > | |||||
| {!matches && t("users.profile")} | |||||
| <img | |||||
| style={{ | |||||
| position: "relative", | |||||
| top: -0.25, | |||||
| paddingLeft: !matches && "10px", | |||||
| }} | |||||
| src={filters} | |||||
| /> | |||||
| </IconButton> | |||||
| </div> | |||||
| </div> | |||||
| <div | |||||
| className="flex-center" | |||||
| style={{ justifyContent: "flex-start" }} | |||||
| > | > | ||||
| {!matches && "Uredi profil"} | |||||
| <img | <img | ||||
| style={{ | |||||
| position: "relative", | |||||
| top: -0.25, | |||||
| paddingLeft: !matches && "10px", | |||||
| }} | |||||
| src={filters} | |||||
| src={avatar} | |||||
| height="108px" | |||||
| width="108px" | |||||
| style={{ margin: "18px 15px 36px 0px" }} | |||||
| /> | /> | ||||
| </IconButton> | |||||
| </div> | |||||
| </div> | |||||
| <div className="flex-center" style={{ justifyContent: "flex-start" }}> | |||||
| <img | |||||
| src={avatar} | |||||
| height="108px" | |||||
| width="108px" | |||||
| style={{ margin: "18px 15px 36px 0px" }} | |||||
| /> | |||||
| <p> | |||||
| {user?.position | |||||
| ? user.position | |||||
| : "Position has not been declared yet."} | |||||
| </p> | |||||
| </div> | |||||
| <div style={{ display: "flex", flexDirection: "column", gap: "18px" }}> | |||||
| <p style={{ fontWeight: "600" }}>Kontakt</p> | |||||
| <div className="flex-center" style={{ justifyContent: "flex-start" }}> | |||||
| <p style={{ width: "85px" }}>Email:</p> | |||||
| <p className="text-blue">{user && user.email}</p> | |||||
| </div> | |||||
| <div className="flex-center" style={{ justifyContent: "flex-start" }}> | |||||
| <p style={{ width: "85px" }}>Telefon:</p> | |||||
| <p className="text-blue"> | |||||
| {user?.phoneNumber | |||||
| ? user.phoneNumber | |||||
| : "User has no phone number saved."} | |||||
| </p> | |||||
| </div> | |||||
| </div> | |||||
| <div | |||||
| style={{ | |||||
| display: "flex", | |||||
| flexDirection: "column", | |||||
| gap: "18px", | |||||
| paddingTop: "36px", | |||||
| }} | |||||
| > | |||||
| <p style={{ fontWeight: "600" }}>Drustvene mreze</p> | |||||
| <div className="flex-center" style={{ justifyContent: "flex-start" }}> | |||||
| <p style={{ width: "85px" }}>LinkedIn:</p> | |||||
| <p className="text-blue"> | |||||
| {user?.linkedIn | |||||
| ? user.linkedIn | |||||
| : "User takes not part in any social media."} | |||||
| </p> | |||||
| </div> | |||||
| </div> | |||||
| <div | |||||
| style={{ | |||||
| display: "flex", | |||||
| justifyContent: "flex-end", | |||||
| marginTop: "150px", | |||||
| }} | |||||
| > | |||||
| <Link to={"/users"} className="text-blue"> | |||||
| Nazad na listu korisnika | |||||
| </Link> | |||||
| </div></>} | |||||
| <p> | |||||
| {user?.position | |||||
| ? user.position | |||||
| : t("users.positionNotDeclared")} | |||||
| </p> | |||||
| </div> | |||||
| <div | |||||
| style={{ display: "flex", flexDirection: "column", gap: "18px" }} | |||||
| > | |||||
| <p style={{ fontWeight: "600" }}>{t("users.contact")}</p> | |||||
| <div | |||||
| className="flex-center" | |||||
| style={{ justifyContent: "flex-start" }} | |||||
| > | |||||
| <p style={{ width: "85px" }}>Email:</p> | |||||
| <p className="text-blue">{user && user.email}</p> | |||||
| </div> | |||||
| <div | |||||
| className="flex-center" | |||||
| style={{ justifyContent: "flex-start" }} | |||||
| > | |||||
| <p style={{ width: "85px" }}>{t("users.phone")}:</p> | |||||
| <p className="text-blue"> | |||||
| {user?.phoneNumber | |||||
| ? user.phoneNumber | |||||
| : t("users.noPhoneNumber")} | |||||
| </p> | |||||
| </div> | |||||
| </div> | |||||
| <div | |||||
| style={{ | |||||
| display: "flex", | |||||
| flexDirection: "column", | |||||
| gap: "18px", | |||||
| paddingTop: "36px", | |||||
| }} | |||||
| > | |||||
| <p style={{ fontWeight: "600" }}>{t("users.socials")}</p> | |||||
| <div | |||||
| className="flex-center" | |||||
| style={{ justifyContent: "flex-start" }} | |||||
| > | |||||
| <p style={{ width: "85px" }}>LinkedIn:</p> | |||||
| <p className="text-blue"> | |||||
| {user?.linkedIn ? user.linkedIn : t("users.noSocialMedia")} | |||||
| </p> | |||||
| </div> | |||||
| </div> | |||||
| <div | |||||
| style={{ | |||||
| display: "flex", | |||||
| justifyContent: "flex-end", | |||||
| marginTop: "150px", | |||||
| }} | |||||
| > | |||||
| <Link to={"/users"} className="text-blue"> | |||||
| {t("users.backToUsers")} | |||||
| </Link> | |||||
| </div> | |||||
| </> | |||||
| )} | |||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| ); | ); |
| </div> | </div> | ||||
| <ConfirmDialog | <ConfirmDialog | ||||
| open={showConfirm} | open={showConfirm} | ||||
| title={"Disable user"} | |||||
| title={t("users.disableUser")} | |||||
| subtitle={chosen?.firstName + " " + chosen?.lastName} | subtitle={chosen?.firstName + " " + chosen?.lastName} | ||||
| imgSrc={forbiden} | imgSrc={forbiden} | ||||
| content="Are you sure you want to disable user?" | |||||
| content={t("users.questionDisableUser")} | |||||
| onClose={() => { | onClose={() => { | ||||
| setConfirm(false); | setConfirm(false); | ||||
| }} | }} | ||||
| /> | /> | ||||
| <ConfirmDialog | <ConfirmDialog | ||||
| open={showReset} | open={showReset} | ||||
| title={"Reset password"} | |||||
| title={t("users.resetPassword")} | |||||
| subtitle={chosen?.firstName + " " + chosen?.lastName} | subtitle={chosen?.firstName + " " + chosen?.lastName} | ||||
| imgSrc={lock} | imgSrc={lock} | ||||
| content="Are you sure you want to send password reset link?" | |||||
| content={t("users.resetLink")} | |||||
| onClose={() => { | onClose={() => { | ||||
| setReset(false); | setReset(false); | ||||
| }} | }} | ||||
| } | } | ||||
| onClick={handleChangeVisibility} | onClick={handleChangeVisibility} | ||||
| > | > | ||||
| {!matches && "Pretraga"} | |||||
| {!matches && t("candidates.search")} | |||||
| <img | <img | ||||
| style={{ | style={{ |