| @@ -1,126 +0,0 @@ | |||
| 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(); | |||
| }); | |||
| }); | |||
| @@ -1,73 +0,0 @@ | |||
| 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()); | |||
| }); | |||
| }); | |||
| @@ -7,6 +7,7 @@ import linkedin from "../../assets/images/linkedin.png"; | |||
| import facebook from "../../assets/images/facebook.png"; | |||
| import instagram from "../../assets/images/instagram.png"; | |||
| import { selectLogo } from "../../util/helpers/technologiesLogos"; | |||
| import { useTranslation } from "react-i18next"; | |||
| const Ad = ({ | |||
| title, | |||
| @@ -18,6 +19,7 @@ const Ad = ({ | |||
| }) => { | |||
| const theme = useTheme(); | |||
| const matches = useMediaQuery(theme.breakpoints.down("sm")); | |||
| const { t } = useTranslation(); | |||
| return ( | |||
| <div className={`ad-card ${className}`} onClick={onShowAdDetails}> | |||
| @@ -37,7 +39,9 @@ const Ad = ({ | |||
| </div> | |||
| <div className="ad-card-experience"> | |||
| <p>{minimumExperience}+ years of experience</p> | |||
| <p> | |||
| {minimumExperience}+ {t("common.experience")} | |||
| </p> | |||
| </div> | |||
| {!matches && ( | |||
| @@ -2,6 +2,7 @@ import React from "react"; | |||
| import PropTypes from "prop-types"; | |||
| import { CANDIDATES_DETAILS_PAGE } from "../../constants/pages"; | |||
| import { useHistory } from "react-router-dom"; | |||
| import { useTranslation } from "react-i18next"; | |||
| const AdDetailsCandidateCard = ({ | |||
| className, | |||
| @@ -12,21 +13,32 @@ const AdDetailsCandidateCard = ({ | |||
| cv, | |||
| }) => { | |||
| const history = useHistory(); | |||
| const { t } = useTranslation(); | |||
| 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"> | |||
| <p>{new Date().toLocaleDateString()}</p> | |||
| </div> | |||
| <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} | |||
| </h3> | |||
| </div> | |||
| <div className="ad-details-candidate-experience"> | |||
| {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 className="ad-details-candidate-buttons"> | |||
| @@ -12,6 +12,7 @@ import { changeIsCheckedValue } from "../../store/actions/technologies/technolog | |||
| import { useDispatch } from "react-redux"; | |||
| import { setFilteredAdsReq } from "../../store/actions/ads/adsAction"; | |||
| import { useHistory } from "react-router-dom"; | |||
| import { useTranslation } from "react-i18next"; | |||
| const AdFilters = ({ open, handleClose, technologies }) => { | |||
| const [sliderValue, setSliderValue] = useState([0, 10]); | |||
| @@ -19,6 +20,7 @@ const AdFilters = ({ open, handleClose, technologies }) => { | |||
| const [workHour, setWorkHour] = useState("FullTime"); | |||
| const dispatch = useDispatch(); | |||
| const history = useHistory(); | |||
| const { t } = useTranslation(); | |||
| const handleSliderChange = (_, newValue) => { | |||
| setSliderValue(newValue); | |||
| @@ -82,9 +84,9 @@ const AdFilters = ({ open, handleClose, technologies }) => { | |||
| <div className="ad-filters-header-container"> | |||
| <div className="ad-filters-header"> | |||
| <img src={filterIcon} alt="filter_icon" /> | |||
| <h3>Filteri</h3> | |||
| <h3>{t("filters.filters")}</h3> | |||
| <p> | |||
| <sub>| Oglasi</sub> | |||
| <sub>| {t("ads.ads")}</sub> | |||
| </p> | |||
| </div> | |||
| <div className="ad-filters-header-close" onClick={handleClose}> | |||
| @@ -93,7 +95,7 @@ const AdFilters = ({ open, handleClose, technologies }) => { | |||
| </div> | |||
| <div className="ad-filters-experience"> | |||
| <div className="ad-filters-sub-title"> | |||
| <p>Godine iskustva</p> | |||
| <p>{t("filters.experience")}</p> | |||
| </div> | |||
| <div className="ad-filters-experience-slider"> | |||
| <Slider | |||
| @@ -107,7 +109,7 @@ const AdFilters = ({ open, handleClose, technologies }) => { | |||
| </div> | |||
| <div className="ad-filters-technologies"> | |||
| <div className="ad-filters-sub-title"> | |||
| <p>Tehnologije</p> | |||
| <p>{t("filters.tecnologies")}</p> | |||
| </div> | |||
| <div className="ad-filters-technologies-checkboxes"> | |||
| <FormGroup> | |||
| @@ -130,7 +132,7 @@ const AdFilters = ({ open, handleClose, technologies }) => { | |||
| </div> | |||
| <div className="ad-filters-technologies"> | |||
| <div className="ad-filters-sub-title"> | |||
| <p>Tip zaposlenja</p> | |||
| <p>{t("filters.employmentType")}</p> | |||
| </div> | |||
| <div className="ad-filters-employment-type"> | |||
| <button | |||
| @@ -141,7 +143,7 @@ const AdFilters = ({ open, handleClose, technologies }) => { | |||
| }`} | |||
| onClick={employmentTypeHandler.bind(this, "Intership")} | |||
| > | |||
| Intership | |||
| {t("filters.internship")} | |||
| </button> | |||
| <button | |||
| className={`c-btn ${ | |||
| @@ -151,13 +153,13 @@ const AdFilters = ({ open, handleClose, technologies }) => { | |||
| }`} | |||
| onClick={employmentTypeHandler.bind(this, "Work")} | |||
| > | |||
| Posao | |||
| {t("filters.work")} | |||
| </button> | |||
| </div> | |||
| </div> | |||
| <div className="ad-filters-technologies"> | |||
| <div className="ad-filters-sub-title"> | |||
| <p>Radno vreme</p> | |||
| <p>{t("filters.workHour")}</p> | |||
| </div> | |||
| <div className="ad-filters-employment-type"> | |||
| <button | |||
| @@ -168,7 +170,7 @@ const AdFilters = ({ open, handleClose, technologies }) => { | |||
| }`} | |||
| onClick={workHourHandler.bind(this, "PartTime")} | |||
| > | |||
| Part-time | |||
| {t("filters.partTime")} | |||
| </button> | |||
| <button | |||
| className={`c-btn ${ | |||
| @@ -178,13 +180,17 @@ const AdFilters = ({ open, handleClose, technologies }) => { | |||
| }`} | |||
| onClick={workHourHandler.bind(this, "FullTime")} | |||
| > | |||
| Full-time | |||
| {t("filters.fullTime")} | |||
| </button> | |||
| </div> | |||
| </div> | |||
| <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> | |||
| </div> | |||
| </div> | |||
| @@ -1,163 +0,0 @@ | |||
| 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; | |||
| @@ -1,151 +0,0 @@ | |||
| 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; | |||
| @@ -1,143 +0,0 @@ | |||
| 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; | |||
| @@ -1,77 +0,0 @@ | |||
| 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; | |||
| @@ -14,6 +14,7 @@ import ApplyForAdFourthStage from "./ApplyForAdFourthStage"; | |||
| import { applyForAdReq } from "../../store/actions/applyForAd/applyForAdActions"; | |||
| import { useHistory } from "react-router-dom"; | |||
| import { ADS_PAGE } from "../../constants/pages"; | |||
| import { useTranslation } from "react-i18next"; | |||
| const ApplyForAd = ({ open, title, adId, onCloseModal }) => { | |||
| const [stage, setStage] = useState(1); | |||
| @@ -24,7 +25,8 @@ const ApplyForAd = ({ open, title, adId, onCloseModal }) => { | |||
| const [phoneNumber, setPhoneNumber] = useState(""); | |||
| const [mappedTechnologies, setMappedTechnologies] = useState([]); | |||
| const [experience, setExperience] = useState(0); | |||
| const [professionalQualification, setProfessionalQualification] = useState(""); | |||
| const [professionalQualification, setProfessionalQualification] = | |||
| useState(""); | |||
| const [linkedinLink, setLinkedinLink] = useState(""); | |||
| const [githubLink, setGithubLink] = useState(""); | |||
| const [email, setEmail] = useState(""); | |||
| @@ -35,6 +37,7 @@ const ApplyForAd = ({ open, title, adId, onCloseModal }) => { | |||
| const technologies = useSelector(selectTechnologies); | |||
| const dispatch = useDispatch(); | |||
| const history = useHistory(); | |||
| const { t } = useTranslation(); | |||
| useEffect(() => { | |||
| dispatch(setTechnologiesReq()); | |||
| @@ -95,7 +98,7 @@ const ApplyForAd = ({ open, title, adId, onCloseModal }) => { | |||
| <img src={briefcaseIcon} alt="plus" /> | |||
| </div> | |||
| <div className="apply-for-ad-header-left-image-title"> | |||
| <p>Prijavi se</p> | |||
| <p>{t("ads.signUp")}</p> | |||
| </div> | |||
| <div className="apply-for-ad-header-left-image-title-sub"> | |||
| <sub> | {title}</sub> | |||
| @@ -1,5 +1,6 @@ | |||
| import React from "react"; | |||
| import PropTypes from "prop-types"; | |||
| import { useTranslation } from "react-i18next"; | |||
| const ApplyForAdFirstStage = ({ | |||
| firstName, | |||
| @@ -21,10 +22,12 @@ const ApplyForAdFirstStage = ({ | |||
| phoneNumber.length === 0 || | |||
| gender.length === 0; | |||
| const { t } = useTranslation(); | |||
| return ( | |||
| <div> | |||
| <div className="apply-for-ad-modal-form-control"> | |||
| <label>Ime</label> | |||
| <label>{t("common.name")}</label> | |||
| <input | |||
| type="text" | |||
| placeholder="ex. Petar" | |||
| @@ -34,7 +37,7 @@ const ApplyForAdFirstStage = ({ | |||
| /> | |||
| </div> | |||
| <div className="apply-for-ad-modal-form-control"> | |||
| <label>Prezime</label> | |||
| <label>{t("common.lastName")}</label> | |||
| <input | |||
| type="text" | |||
| placeholder="ex. Petrovic" | |||
| @@ -44,7 +47,7 @@ const ApplyForAdFirstStage = ({ | |||
| /> | |||
| </div> | |||
| <div className="apply-for-ad-modal-form-control"> | |||
| <label>Pol</label> | |||
| <label>{t("common.gender")}</label> | |||
| <div style={{ display: "flex" }}> | |||
| <div style={{ display: "flex" }}> | |||
| <input | |||
| @@ -55,7 +58,7 @@ const ApplyForAdFirstStage = ({ | |||
| checked={gender === "Muski"} | |||
| onChange={(e) => setGender(e.target.value)} | |||
| /> | |||
| <p style={{ marginLeft: "5px" }}>Muski</p> | |||
| <p style={{ marginLeft: "5px" }}>{t("common.male")}</p> | |||
| </div> | |||
| <div style={{ display: "flex", marginLeft: "50px" }}> | |||
| <input | |||
| @@ -66,12 +69,12 @@ const ApplyForAdFirstStage = ({ | |||
| checked={gender === "Zenski"} | |||
| onChange={(e) => setGender(e.target.value)} | |||
| /> | |||
| <p style={{ marginLeft: "5px" }}>Zenski</p> | |||
| <p style={{ marginLeft: "5px" }}>{t("common.female")}</p> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| <div className="apply-for-ad-modal-form-control"> | |||
| <label>Datum Rodjenja</label> | |||
| <label>{t("common.dateOfBirth")}</label> | |||
| <input | |||
| type="date" | |||
| placeholder="ex. Datum rodjenja" | |||
| @@ -81,7 +84,7 @@ const ApplyForAdFirstStage = ({ | |||
| /> | |||
| </div> | |||
| <div className="apply-for-ad-modal-form-control"> | |||
| <label>Broj telefona</label> | |||
| <label>{t("common.phoneNumber")}</label> | |||
| <input | |||
| type="text" | |||
| placeholder="ex. +381/60/000/0000" | |||
| @@ -91,10 +94,13 @@ const ApplyForAdFirstStage = ({ | |||
| /> | |||
| </div> | |||
| <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> | |||
| </div> | |||
| </div> | |||
| @@ -1,6 +1,7 @@ | |||
| import React, { useState } from "react"; | |||
| import PropTypes from "prop-types"; | |||
| import uploadIcon from "../../assets/images/upload.png"; | |||
| import { useTranslation } from "react-i18next"; | |||
| const ApplyForAdFourthStage = ({ | |||
| coverLetter, | |||
| @@ -11,6 +12,7 @@ const ApplyForAdFourthStage = ({ | |||
| onFinishedFourStages, | |||
| }) => { | |||
| const [dropzoneActive, setDropzoneActive] = useState(false); | |||
| const {t} = useTranslation() | |||
| const disabled = pdfFile === null || coverLetter === ""; | |||
| @@ -46,7 +48,7 @@ const ApplyForAdFourthStage = ({ | |||
| ) : ( | |||
| <> | |||
| <p> | |||
| Prevuci .pdf dokument u ovom delu ekrana ili | |||
| {t("ads.dragPdf1")} | |||
| <label | |||
| htmlFor="upload-file" | |||
| style={{ | |||
| @@ -55,9 +57,9 @@ const ApplyForAdFourthStage = ({ | |||
| color: "#1E92D0", | |||
| }} | |||
| > | |||
| Pretrazi | |||
| {t("common.search")} | |||
| </label> | |||
| na racunaru | |||
| {t("ads.dragPdf2")} | |||
| </p> | |||
| <input | |||
| type="file" | |||
| @@ -78,7 +80,7 @@ const ApplyForAdFourthStage = ({ | |||
| </div> | |||
| </div> | |||
| <div className="apply-for-ad-modal-form-control"> | |||
| <label>Propratno pismo (Opciono)</label> | |||
| <label>{t("ads.coverLetter")}</label> | |||
| <textarea | |||
| value={coverLetter} | |||
| onChange={(e) => setCoverLetter(e.target.value)} | |||
| @@ -92,14 +94,14 @@ const ApplyForAdFourthStage = ({ | |||
| onClick={onDecreaseStage} | |||
| data-testid="apply-for-ad-modal-fourth-stage-go-back-button" | |||
| > | |||
| NAZAD | |||
| {t("common.back")} | |||
| </button> | |||
| <button | |||
| disabled={disabled} | |||
| data-testid="apply-for-ad-modal-fourth-stage-go-forward-button" | |||
| onClick={onFinishedFourStages} | |||
| > | |||
| PRIJAVI SE | |||
| {t("ads.signUp")} | |||
| </button> | |||
| </div> | |||
| </div> | |||
| @@ -1,6 +1,7 @@ | |||
| import React from "react"; | |||
| import PropTypes from "prop-types"; | |||
| import { Checkbox, FormControlLabel } from "@mui/material"; | |||
| import { useTranslation } from "react-i18next"; | |||
| const ApplyForAdSecondStage = ({ | |||
| professionalQualification, | |||
| @@ -12,6 +13,7 @@ const ApplyForAdSecondStage = ({ | |||
| onIncreaseStage, | |||
| onDecreaseStage, | |||
| }) => { | |||
| const {t} = useTranslation() | |||
| let disabled = true; | |||
| let isTechnologySelected = false; | |||
| if (technologies.length > 0) { | |||
| @@ -35,7 +37,7 @@ const ApplyForAdSecondStage = ({ | |||
| return ( | |||
| <div data-testid="apply-for-ad-second-stage"> | |||
| <div className="apply-for-ad-header-title"> | |||
| <p>Strucna sprema</p> | |||
| <p>{t("ads.professionalQualification")}</p> | |||
| </div> | |||
| <div className="apply-for-ad-modal-form-control"> | |||
| <input | |||
| @@ -47,7 +49,7 @@ const ApplyForAdSecondStage = ({ | |||
| /> | |||
| </div> | |||
| <div className="apply-for-ad-header-title"> | |||
| <p>Tehnologije koje znaš</p> | |||
| <p>{t("ads.technologies")}</p> | |||
| </div> | |||
| <div className="apply-for-ad-header-sub"> | |||
| <div className="apply-for-ad-header-sub-group"> | |||
| @@ -93,7 +95,7 @@ const ApplyForAdSecondStage = ({ | |||
| </div> | |||
| </div> | |||
| <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"> | |||
| {technologies | |||
| .filter((x) => x.technologyType === "Other") | |||
| @@ -115,7 +117,7 @@ const ApplyForAdSecondStage = ({ | |||
| </div> | |||
| </div> | |||
| <div className="apply-for-ad-modal-form-control"> | |||
| <label>Godine iskustva</label> | |||
| <label>{t("filters.experience")}</label> | |||
| <input | |||
| type="number" | |||
| placeholder="ex. 3 godine iskustva" | |||
| @@ -125,9 +127,9 @@ const ApplyForAdSecondStage = ({ | |||
| /> | |||
| </div> | |||
| <div className="apply-for-ad-buttons"> | |||
| <button onClick={onDecreaseStage}>NAZAD</button> | |||
| <button onClick={onDecreaseStage}>{t("common.back")}</button> | |||
| <button onClick={onIncreaseStage} disabled={disabled}> | |||
| NASTAVI | |||
| {t("common.continue")} | |||
| </button> | |||
| </div> | |||
| </div> | |||
| @@ -1,5 +1,6 @@ | |||
| import React from "react"; | |||
| import PropTypes from "prop-types"; | |||
| import { useTranslation } from "react-i18next"; | |||
| const ApplyForAdThirdStage = ({ | |||
| onIncreaseStage, | |||
| @@ -18,10 +19,12 @@ const ApplyForAdThirdStage = ({ | |||
| githubLink.length === 0 || | |||
| bitBucketLink.length === 0; | |||
| const {t} = useTranslation() | |||
| return ( | |||
| <div> | |||
| <div className="apply-for-ad-header-title"> | |||
| <p>Društvene mreže</p> | |||
| <p>{t("common.socialNetwork")}</p> | |||
| </div> | |||
| <div className="apply-for-ad-modal-form-control"> | |||
| <label>LinkedIn</label> | |||
| @@ -37,7 +40,8 @@ const ApplyForAdThirdStage = ({ | |||
| <label>GitHub</label> | |||
| <input | |||
| 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)} | |||
| placeholder="ex. https://www.github.com/petarpetrovic" | |||
| /> | |||
| @@ -46,7 +50,8 @@ const ApplyForAdThirdStage = ({ | |||
| <label>BitBucket</label> | |||
| <input | |||
| 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)} | |||
| placeholder="ex. https://developer.atlassian.com/user/petarapetrovic" | |||
| /> | |||
| @@ -55,15 +60,25 @@ const ApplyForAdThirdStage = ({ | |||
| <label>Email</label> | |||
| <input | |||
| 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)} | |||
| placeholder="ex. petar.petrovic@dilig.net" | |||
| /> | |||
| </div> | |||
| <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> | |||
| </div> | |||
| </div> | |||
| @@ -1,6 +1,7 @@ | |||
| import React from "react"; | |||
| import PropTypes from "prop-types"; | |||
| import { selectLogo } from "../../util/helpers/technologiesLogos"; | |||
| import { useTranslation } from "react-i18next"; | |||
| const ArchiveAd = ({ | |||
| className, | |||
| @@ -10,6 +11,7 @@ const ArchiveAd = ({ | |||
| expiredAt, | |||
| onShowAdDetails, | |||
| }) => { | |||
| const { t } = useTranslation(); | |||
| return ( | |||
| <div className={`archive-ad ${className}`} onClick={onShowAdDetails}> | |||
| <div className="archive-ad-date"> | |||
| @@ -26,9 +28,11 @@ const ArchiveAd = ({ | |||
| </div> | |||
| <div className="archive-ad-experience"> | |||
| {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> | |||
| @@ -1,6 +1,7 @@ | |||
| import React from "react"; | |||
| import PropTypes from "prop-types"; | |||
| import { selectLogo } from "../../util/helpers/technologiesLogos"; | |||
| import { useTranslation } from "react-i18next"; | |||
| const StatsAd = ({ | |||
| className, | |||
| @@ -11,10 +12,11 @@ const StatsAd = ({ | |||
| expiredAt, | |||
| onShowAdDetails, | |||
| }) => { | |||
| const {t} = useTranslation() | |||
| return ( | |||
| <div className={`archive-ad stats-ad ${className}`} onClick={onShowAdDetails}> | |||
| <div className="ad-count"> | |||
| {count} prijavljenih | |||
| {count} {t("ads.registered")} | |||
| </div> | |||
| <div className="archive-ad-date"> | |||
| <p> | |||
| @@ -30,9 +32,9 @@ const StatsAd = ({ | |||
| </div> | |||
| <div className="archive-ad-experience"> | |||
| {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> | |||
| @@ -4,17 +4,19 @@ import React from "react"; | |||
| import userPageBtnIcon from "../../assets/images/userPageBtnIcon.png"; | |||
| import { useTheme } from "@mui/system"; | |||
| import { useMediaQuery } from "@mui/material"; | |||
| import { useTranslation } from "react-i18next"; | |||
| const EditButton = ({ onEnableEdit }) => { | |||
| const theme = useTheme(); | |||
| const matches = useMediaQuery(theme.breakpoints.down("sm")); | |||
| const {t} = useTranslation() | |||
| return ( | |||
| <IconButton | |||
| className={"c-btn--primary-outlined edit-btn-class c-btn userPageBtn ml-20px no-padding"} | |||
| onClick={onEnableEdit} | |||
| > | |||
| {!matches && "Režim uređivanja"} | |||
| {!matches && t("patterns.editing2")} | |||
| <img | |||
| style={{ | |||
| position: "relative", | |||
| @@ -4,17 +4,21 @@ import React from "react"; | |||
| import filters from "../../assets/images/filters.png"; | |||
| import { useTheme } from "@mui/system"; | |||
| import { useMediaQuery } from "@mui/material"; | |||
| import { useTranslation } from "react-i18next"; | |||
| const FilterButton = ({ onShowFilters }) => { | |||
| const theme = useTheme(); | |||
| const matches = useMediaQuery(theme.breakpoints.down("sm")); | |||
| const { t } = useTranslation(); | |||
| return ( | |||
| <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} | |||
| > | |||
| {!matches && "Filteri"} | |||
| {!matches && t("filters.filters")} | |||
| <img | |||
| style={{ | |||
| position: "relative", | |||
| @@ -2,8 +2,10 @@ import React from "react"; | |||
| import PropTypes from "prop-types"; | |||
| import { formatDate } from "../../util/helpers/dateHelpers"; | |||
| import { CANDIDATES_PAGE } from "../../constants/pages"; | |||
| import { useTranslation } from "react-i18next"; | |||
| const CandidateCard = ({ candidate, className, history }) => { | |||
| const { t } = useTranslation(); | |||
| const navigateToDetailsPage = () => { | |||
| history.push({ | |||
| pathname: CANDIDATES_PAGE + "/" + candidate.applicantId, | |||
| @@ -27,8 +29,8 @@ const CandidateCard = ({ candidate, className, history }) => { | |||
| </p> | |||
| <p className="candidate-card-years"> | |||
| {candidate.experience === 0 | |||
| ? "No experience" | |||
| : candidate.experience + "+ years of experience"} | |||
| ? t("common.noExperience") | |||
| : candidate.experience + " " + t("common.experience")} | |||
| </p> | |||
| <div className="candidate-card-tecnologies-container"> | |||
| {candidate.technologyApplicants.map((technology, index) => ( | |||
| @@ -18,6 +18,7 @@ import { | |||
| changeIsCheckedValue, | |||
| resetIsCheckedValue, | |||
| } from "../../store/actions/technologies/technologiesActions"; | |||
| import { useTranslation } from "react-i18next"; | |||
| const CandidateFilters = ({ | |||
| open, | |||
| @@ -37,9 +38,9 @@ const CandidateFilters = ({ | |||
| }) => { | |||
| const dispatch = useDispatch(); | |||
| const [isInitial, setIsInitial] = useState(true); | |||
| const { t } = useTranslation(); | |||
| useEffect(() => { | |||
| }, [isTableView]); | |||
| useEffect(() => {}, [isTableView]); | |||
| const handleSliderChange = (_, newValue) => { | |||
| setSliderValue(newValue); | |||
| @@ -184,9 +185,9 @@ const CandidateFilters = ({ | |||
| <div className="ad-filters-header-container"> | |||
| <div className="ad-filters-header"> | |||
| <img src={filterIcon} alt="filter_icon" /> | |||
| <h3>Filteri</h3> | |||
| <h3>{t("filters.filters")}</h3> | |||
| <p> | |||
| <sub>| Kandidati</sub> | |||
| <sub>| {t("candidates.candidates")}</sub> | |||
| </p> | |||
| </div> | |||
| <div className="ad-filters-header-close" onClick={handleClose}> | |||
| @@ -195,7 +196,7 @@ const CandidateFilters = ({ | |||
| </div> | |||
| <div className="ad-filters-experience"> | |||
| <div className="ad-filters-sub-title"> | |||
| <p>Godine iskustva</p> | |||
| <p>{t("filters.experience")}</p> | |||
| </div> | |||
| <div className="ad-filters-experience-slider"> | |||
| <Slider | |||
| @@ -209,7 +210,7 @@ const CandidateFilters = ({ | |||
| </div> | |||
| <div className="ad-filters-technologies"> | |||
| <div className="ad-filters-sub-title"> | |||
| <p>Tehnologije</p> | |||
| <p>{t("filters.technologies")}</p> | |||
| </div> | |||
| <div className="ad-filters-technologies-checkboxes"> | |||
| <FormGroup> | |||
| @@ -232,7 +233,7 @@ const CandidateFilters = ({ | |||
| </div> | |||
| <div className="ad-filters-technologies"> | |||
| <div className="ad-filters-sub-title"> | |||
| <p>Tip zaposlenja</p> | |||
| <p>{t("filters.employmentType")}</p> | |||
| </div> | |||
| <div className="ad-filters-employment-type"> | |||
| {typesOfEmployments.map((type, index) => ( | |||
| @@ -252,9 +253,9 @@ const CandidateFilters = ({ | |||
| </div> | |||
| <div className="ad-filters-technologies" style={{ marginTop: "35px" }}> | |||
| <div className="ad-filters-sub-title"> | |||
| <p>Datum prijave</p> | |||
| <p>{t("filters.dateOfApplication")}</p> | |||
| <div className="filter-date-container"> | |||
| <p>Od</p> | |||
| <p>{t("common.from")}</p> | |||
| <input | |||
| type="date" | |||
| id="start" | |||
| @@ -264,7 +265,7 @@ const CandidateFilters = ({ | |||
| /> | |||
| </div> | |||
| <div className="filter-date-container"> | |||
| <p>Do</p> | |||
| <p>{t("common.to")}</p> | |||
| <input | |||
| type="date" | |||
| id="start" | |||
| @@ -282,7 +283,7 @@ const CandidateFilters = ({ | |||
| onClick={fiterItems} | |||
| disabled={!isThereSelectedFilter()} | |||
| > | |||
| Pretrazi | |||
| {t("common.search")} | |||
| </button> | |||
| </div> | |||
| </div> | |||
| @@ -20,6 +20,7 @@ import { useDispatch, useSelector } from "react-redux"; | |||
| import { useEffect } from "react"; | |||
| import { SelectionContext } from "../../context/SelectionContext"; | |||
| import { setUpdateStatusReq } from "../../store/actions/processes/processAction"; | |||
| import { useTranslation } from "react-i18next"; | |||
| // import { setUpdateInterviewerReq } from "../../store/actions/processes/processAction"; | |||
| // import { setUpdateStatusReq } from "../../store/actions/processes/processAction"; | |||
| @@ -34,6 +35,7 @@ const CommentProcessDialog = ({ | |||
| responsive, | |||
| }) => { | |||
| const { activeProcessUnsuccess, setActiveProcessUnsuccess } = useContext(SelectionContext); | |||
| const {t} = useTranslation() | |||
| const theme = useTheme(); | |||
| const fullScreen = useMediaQuery(theme.breakpoints.down("md")); | |||
| @@ -151,7 +153,7 @@ const CommentProcessDialog = ({ | |||
| className={`c-btn--primary-outlined interview-btn c-btn dialog-btn`} | |||
| onClick={onClose} | |||
| > | |||
| Otkaži | |||
| {t("common.cancel")} | |||
| </IconButton> | |||
| ) : ( | |||
| "" | |||
| @@ -161,7 +163,7 @@ const CommentProcessDialog = ({ | |||
| className={`c-btn--primary-outlined sm-full interview-btn c-btn dialog-btn`} | |||
| onClick={submitHandler} | |||
| > | |||
| Potvrdi | |||
| {t("common.confirm")} | |||
| </IconButton> | |||
| </DialogActions> | |||
| </div> | |||
| @@ -10,6 +10,7 @@ import { | |||
| DialogContent, | |||
| } from "@mui/material"; | |||
| import IconButton from "../IconButton/IconButton"; | |||
| import { useTranslation } from "react-i18next"; | |||
| const ConfirmDialog = ({ | |||
| title, | |||
| @@ -25,6 +26,7 @@ const ConfirmDialog = ({ | |||
| }) => { | |||
| const theme = useTheme(); | |||
| const fullScreen = useMediaQuery(theme.breakpoints.down("md")); | |||
| const { t } = useTranslation(); | |||
| const handleClose = () => { | |||
| onClose(); | |||
| @@ -41,11 +43,15 @@ const ConfirmDialog = ({ | |||
| padding: "36px", | |||
| }} | |||
| > | |||
| <div style={{ padding: "36px" }} data-testid='alert-container'> | |||
| <div style={{ padding: "36px" }} data-testid="alert-container"> | |||
| <DialogTitle style={{ padding: 0 }}> | |||
| {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 | |||
| style={{ | |||
| position: "relative", | |||
| @@ -55,10 +61,8 @@ const ConfirmDialog = ({ | |||
| src={imgSrc} | |||
| /> | |||
| <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 | |||
| style={{ | |||
| position: "relative", | |||
| @@ -99,7 +103,9 @@ const ConfirmDialog = ({ | |||
| )} | |||
| </DialogTitle> | |||
| <DialogContent> | |||
| <div className="modal-content" data-testid="modal-content">{content}</div> | |||
| <div className="modal-content" data-testid="modal-content"> | |||
| {content} | |||
| </div> | |||
| </DialogContent> | |||
| <DialogActions style={{ padding: 0 }}> | |||
| {!fullScreen ? ( | |||
| @@ -107,7 +113,7 @@ const ConfirmDialog = ({ | |||
| className={`c-btn--primary-outlined c-btn dialog-btn not-full-screen-btn`} | |||
| onClick={onClose} | |||
| > | |||
| Otkaži | |||
| {t("common.cancel")} | |||
| </IconButton> | |||
| ) : ( | |||
| "" | |||
| @@ -116,7 +122,7 @@ const ConfirmDialog = ({ | |||
| className={`c-btn--primary-outlined sm-full c-btn dialog-btn`} | |||
| onClick={onConfirm} | |||
| > | |||
| Potvrdi | |||
| {t("common.confirm")} | |||
| </IconButton> | |||
| </DialogActions> | |||
| </div> | |||
| @@ -21,6 +21,7 @@ import { useDispatch, useSelector } from "react-redux"; | |||
| import { format, isValid } from "date-fns"; | |||
| import { fetchInitProcess } from "../../store/actions/candidates/candidatesActions"; | |||
| import { useEffect } from "react"; | |||
| import { useTranslation } from "react-i18next"; | |||
| const InterviewDialog = ({ | |||
| title, | |||
| @@ -35,6 +36,7 @@ const InterviewDialog = ({ | |||
| const [selected, setSelected] = useState(""); | |||
| const [selectedInterviewer, setSelectedInterviewer] = useState(null); | |||
| const [value, setValue] = useState(null); | |||
| const { t } = useTranslation(); | |||
| const theme = useTheme(); | |||
| const fullScreen = useMediaQuery(theme.breakpoints.down("md")); | |||
| @@ -148,7 +150,7 @@ const InterviewDialog = ({ | |||
| <form className="modal-content interviewDialog"> | |||
| <FormControl fullWidth> | |||
| <InputLabel id="demo-simple-select-label"> | |||
| Ime kandidata | |||
| {t("dialogs.candidateName")} | |||
| </InputLabel> | |||
| <Select | |||
| labelId="demo-simple-select-label" | |||
| @@ -176,7 +178,7 @@ const InterviewDialog = ({ | |||
| </FormControl> | |||
| <FormControl fullWidth> | |||
| <InputLabel id="demo-simple-select-label"> | |||
| Ime intervjuera (opciono) | |||
| {t("dialogs.interviewerName")} | |||
| </InputLabel> | |||
| <Select | |||
| labelId="demo-simple-select-label" | |||
| @@ -215,7 +217,7 @@ const InterviewDialog = ({ | |||
| className={`c-btn--primary-outlined interview-btn c-btn dialog-btn`} | |||
| onClick={onClose} | |||
| > | |||
| Otkaži | |||
| {t("common.cancel")} | |||
| </IconButton> | |||
| ) : ( | |||
| "" | |||
| @@ -225,7 +227,7 @@ const InterviewDialog = ({ | |||
| className={`c-btn--primary-outlined sm-full interview-btn c-btn dialog-btn`} | |||
| onClick={submitHandler} | |||
| > | |||
| Potvrdi | |||
| {t("common.confirm")} | |||
| </IconButton> | |||
| </DialogActions> | |||
| </div> | |||
| @@ -23,6 +23,7 @@ import { useDispatch, useSelector } from "react-redux"; | |||
| import { useEffect } from "react"; | |||
| import { SelectionContext } from "../../context/SelectionContext"; | |||
| import { setUpdateInterviewerReq } from "../../store/actions/processes/processAction"; | |||
| import { useTranslation } from "react-i18next"; | |||
| // import { setUpdateStatusReq } from "../../store/actions/processes/processAction"; | |||
| const InterviewerDialog = ({ | |||
| @@ -35,11 +36,9 @@ const InterviewerDialog = ({ | |||
| fullWidth, | |||
| responsive, | |||
| }) => { | |||
| const { | |||
| activeInterview, | |||
| setActiveInterview | |||
| } = useContext(SelectionContext); | |||
| const { activeInterview, setActiveInterview } = useContext(SelectionContext); | |||
| const [selected, setSelected] = useState(""); | |||
| const {t} = useTranslation() | |||
| const theme = useTheme(); | |||
| const fullScreen = useMediaQuery(theme.breakpoints.down("md")); | |||
| @@ -74,7 +73,7 @@ const InterviewerDialog = ({ | |||
| }; | |||
| const apiSuccess = () => { | |||
| setActiveInterview(null) | |||
| setActiveInterview(null); | |||
| // console.log("ok"); | |||
| }; | |||
| @@ -148,7 +147,7 @@ const InterviewerDialog = ({ | |||
| <form className="modal-content interviewDialog"> | |||
| <FormControl fullWidth> | |||
| <InputLabel id="demo-simple-select-label"> | |||
| Ime intervjuera | |||
| {t("dialogs.interviewerName2")} | |||
| </InputLabel> | |||
| <Select | |||
| labelId="demo-simple-select-label" | |||
| @@ -188,7 +187,7 @@ const InterviewerDialog = ({ | |||
| className={`c-btn--primary-outlined interview-btn c-btn dialog-btn`} | |||
| onClick={onClose} | |||
| > | |||
| Otkaži | |||
| {t("common.cancel")} | |||
| </IconButton> | |||
| ) : ( | |||
| "" | |||
| @@ -198,7 +197,7 @@ const InterviewerDialog = ({ | |||
| className={`c-btn--primary-outlined sm-full interview-btn c-btn dialog-btn`} | |||
| onClick={submitHandler} | |||
| > | |||
| Potvrdi | |||
| {t("common.confirm")} | |||
| </IconButton> | |||
| </DialogActions> | |||
| </div> | |||
| @@ -106,7 +106,7 @@ const InviteDialog = ({ | |||
| inputProps={{ "data-testid": "invite-input-text" }} | |||
| name="firstName" | |||
| // label={t("users.receiver")} | |||
| label={"Ime"} | |||
| label={t("common.firstName")} | |||
| margin="normal" | |||
| value={formik.values.firstName} | |||
| onChange={formik.handleChange} | |||
| @@ -120,7 +120,7 @@ const InviteDialog = ({ | |||
| inputProps={{ "data-testid": "invite-input-text" }} | |||
| name="lastName" | |||
| // label={t("users.receiver")} | |||
| label={"Prezime"} | |||
| label={t("common.lastName")} | |||
| margin="normal" | |||
| value={formik.values.lastName} | |||
| onChange={formik.handleChange} | |||
| @@ -135,7 +135,7 @@ const InviteDialog = ({ | |||
| inputProps={{ "data-testid": "invite-input-text" }} | |||
| name="email" | |||
| // label={t("users.receiver")} | |||
| label={"E-mail adresa"} | |||
| label={"E-mail " + t("common.address")} | |||
| margin="normal" | |||
| value={formik.values.email} | |||
| onChange={formik.handleChange} | |||
| @@ -160,7 +160,7 @@ const InviteDialog = ({ | |||
| }} | |||
| src={planeVector} | |||
| />{" "} | |||
| Registracioni link | |||
| {t("registration.link")} | |||
| </IconButton> | |||
| </form> | |||
| </DialogContent> | |||
| @@ -97,7 +97,7 @@ const NavbarComponent = () => { | |||
| <Typography | |||
| sx={{ fontWeight: 600, fontSize: "18px", letterSpacing: "1.5px" }} | |||
| > | |||
| Navigacija | |||
| {t("nav.navigation")} | |||
| </Typography> | |||
| {/* <img src={x}/> */} | |||
| <IconButton onClick={handleToggleDrawer}> | |||
| @@ -118,7 +118,7 @@ const NavbarComponent = () => { | |||
| sx={{ fontSize: "14px", marginTop: "8px" }} | |||
| className="text-grey9d" | |||
| > | |||
| HR Specialist | |||
| HR {t("common.specialist")} | |||
| </Typography> | |||
| <div className="hr" style={{ width: "90%", marginTop: "18px" }}></div> | |||
| </div> | |||
| @@ -26,6 +26,7 @@ import { | |||
| createScreeningTest, | |||
| fetchScreeningTests, | |||
| } from "../../store/actions/screeningTests/screeningTestActions"; | |||
| import { useTranslation } from "react-i18next"; | |||
| const StatusDialog = ({ | |||
| title, | |||
| @@ -49,6 +50,7 @@ const StatusDialog = ({ | |||
| const { users } = useSelector((s) => s.users); | |||
| const { isSuccess } = useSelector((s) => s.initProcess); | |||
| const { screeningTests } = useSelector((s) => s.screeningTests); | |||
| const { t } = useTranslation(); | |||
| const dispatch = useDispatch(); | |||
| @@ -197,13 +199,13 @@ const StatusDialog = ({ | |||
| <form className="modal-content interviewDialog"> | |||
| <FormControl fullWidth> | |||
| <InputLabel id="demo-simple-select-label"> | |||
| Ime intervjuera | |||
| {t("dialogs.interviewerName2")} | |||
| </InputLabel> | |||
| <Select | |||
| labelId="demo-simple-select-label" | |||
| id="demo-simple-select" | |||
| value={selected} | |||
| label="Ime intervjuera" | |||
| label={t("dialogs.interviewerName2")} | |||
| onChange={(e) => { | |||
| setSelected(e.target.value); | |||
| }} | |||
| @@ -255,7 +257,7 @@ const StatusDialog = ({ | |||
| activeProcess.process.selectionLevelId === 2 && ( | |||
| <TextField | |||
| name="duration" | |||
| label={"Duration"} | |||
| label={t("dialogs.duration")} | |||
| value={duration} | |||
| onChange={(e) => setDuration(e.target.value)} | |||
| fullWidth | |||
| @@ -264,7 +266,7 @@ const StatusDialog = ({ | |||
| {/* {activeProcess.process && activeProcess.process.date ? <p>Proces ima zakazan termin</p> : ''} */} | |||
| <DateTimePicker | |||
| label="Termin" | |||
| label={t("dialogs.appointment")} | |||
| value={value} | |||
| onChange={handleChange} | |||
| renderInput={(params) => <TextField {...params} />} | |||
| @@ -278,7 +280,7 @@ const StatusDialog = ({ | |||
| className={`c-btn--primary-outlined interview-btn c-btn dialog-btn`} | |||
| onClick={onClose} | |||
| > | |||
| Cancel | |||
| {t("common.cancel")} | |||
| </IconButton> | |||
| ) : ( | |||
| "" | |||
| @@ -288,7 +290,7 @@ const StatusDialog = ({ | |||
| className={`c-btn--primary-outlined sm-full interview-btn c-btn dialog-btn`} | |||
| onClick={submitHandler} | |||
| > | |||
| Confirm | |||
| {t("common.confirm")} | |||
| </IconButton> | |||
| </DialogActions> | |||
| </div> | |||
| @@ -4,6 +4,7 @@ import { Checkbox, FormControlLabel, FormGroup } from "@mui/material"; | |||
| import PropTypes from "prop-types"; | |||
| import { useDispatch } from "react-redux"; | |||
| import { setFilteredPatternsReq } from "../../store/actions/patterns/patternsActions"; | |||
| import { useTranslation } from "react-i18next"; | |||
| const PatternFilters = ({ | |||
| openFilterDrawer, | |||
| @@ -14,8 +15,9 @@ const PatternFilters = ({ | |||
| const [fromDate, setFromDate] = useState(""); | |||
| const [toDate, setToDate] = useState(""); | |||
| const dispatch = useDispatch(); | |||
| const { t } = useTranslation(); | |||
| console.log("OVDE",selectionLevelFilter) | |||
| console.log("OVDE", selectionLevelFilter); | |||
| const submitFiltersHandler = (e) => { | |||
| e.preventDefault(); | |||
| @@ -69,7 +71,7 @@ const PatternFilters = ({ | |||
| <div> | |||
| <div className="custom-drawer-sub-card"> | |||
| <div className="custom-drawer-sub-card-label"> | |||
| <p>Kategorija</p> | |||
| <p>{t("filters.category")}</p> | |||
| </div> | |||
| <div className="custom-drawer-sub-card-content"> | |||
| <FormGroup> | |||
| @@ -92,13 +94,13 @@ const PatternFilters = ({ | |||
| </div> | |||
| <div className="custom-drawer-sub-card"> | |||
| <div className="custom-drawer-sub-card-label"> | |||
| <p>Datum kreiranja</p> | |||
| <p>{t("filters.creationDate")}</p> | |||
| </div> | |||
| <div className="custom-drawer-sub-card-content"> | |||
| <FormGroup style={{ marginBottom: "9px" }}> | |||
| <div className="custom-drawer-sub-card-content-sub"> | |||
| <label className="custom-drawer-sub-card-content-sub-label"> | |||
| Od | |||
| {t("common.from")} | |||
| </label> | |||
| <input | |||
| type="date" | |||
| @@ -111,7 +113,7 @@ const PatternFilters = ({ | |||
| <FormGroup> | |||
| <div className="custom-drawer-sub-card-content-sub"> | |||
| <label className="custom-drawer-sub-card-content-sub-label"> | |||
| Do | |||
| {t("common.to")} | |||
| </label> | |||
| <input | |||
| type="date" | |||
| @@ -130,7 +132,7 @@ const PatternFilters = ({ | |||
| data-testid="custom-drawer-submit-search" | |||
| disabled={!hasSelectedFilters()} | |||
| > | |||
| Pretrazi | |||
| {t("common.search")} | |||
| </button> | |||
| </div> | |||
| </div> | |||
| @@ -39,7 +39,7 @@ const UserProfile = ({ show, innerRef }) => { | |||
| sx={{ fontSize: "14px", marginTop: "8px" }} | |||
| className="text-grey9d" | |||
| > | |||
| HR Specialist | |||
| HR {t("common.specialist")} | |||
| </Typography> | |||
| {/* <div className="hr" style={{ width: "90%", marginTop: "18px" }}></div> */} | |||
| @@ -55,22 +55,16 @@ function FirstStepForm() { | |||
| .matches( | |||
| // 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 | |||
| .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, | |||
| validateOnBlur: true, | |||
| @@ -127,7 +121,7 @@ function FirstStepForm() { | |||
| <TextField | |||
| className="rounded-input" | |||
| name="confirm" | |||
| label={"Potvrda šifre"} | |||
| label={t("login.passwordConfirmation")} | |||
| margin="normal" | |||
| type={showPassword ? "text" : "password"} | |||
| fullWidth | |||
| @@ -156,7 +150,7 @@ function FirstStepForm() { | |||
| className="c-btn c-btn--primary w-289 f" | |||
| type="submit" | |||
| > | |||
| Nastavi | |||
| {t("common.continue")} | |||
| </Button> | |||
| {/* <div className="flex-center"> | |||
| <div className="hr hr-s"></div> | |||
| @@ -103,7 +103,7 @@ function SecondStepForm() { | |||
| className="c-btn c-btn--primary w-289 f" | |||
| type="submit" | |||
| > | |||
| Registruj se | |||
| {t("registration.register")} | |||
| </Button> | |||
| {/* <div className="flex-center"> | |||
| <div className="hr hr-s"></div> | |||
| @@ -130,4 +130,4 @@ function SecondStepForm() { | |||
| ); | |||
| } | |||
| export default SecondStepForm; | |||
| export default SecondStepForm; | |||
| @@ -1,31 +1,34 @@ | |||
| import React from "react"; | |||
| import PropTypes from "prop-types"; | |||
| import { formatTimeSrb } from "../../util/helpers/dateHelpers"; | |||
| import { useTranslation } from "react-i18next"; | |||
| const DayComponent = ({ numberOfDay, nameOfDay, interviews, onClick }) => { | |||
| const { t } = useTranslation(); | |||
| return ( | |||
| <div className="day-component-container" onClick={onClick}> | |||
| <div className="day-component-day-informations-container"> | |||
| <p className="day-component-day-number">{numberOfDay}</p> | |||
| <p className="day-component-day-name">{nameOfDay}</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.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 && ( | |||
| <span className="day-component-more"> | |||
| +{interviews.length - 2} stavke | |||
| +{interviews.length - 2} {t("schedule.items")} | |||
| </span> | |||
| )} | |||
| </div> | |||
| @@ -11,6 +11,7 @@ import { formatTimeSrb } from "../../util/helpers/dateHelpers"; | |||
| import { CANDIDATES_PAGE } from "../../constants/pages"; | |||
| import { useTheme } from "@emotion/react"; | |||
| import { useMediaQuery } from "@mui/material"; | |||
| import { useTranslation } from "react-i18next"; | |||
| const DayDetailsComponent = ({ | |||
| selectedDate, | |||
| @@ -27,6 +28,7 @@ const DayDetailsComponent = ({ | |||
| const matches = useMediaQuery(theme.breakpoints.down("361")); | |||
| const [isLeftArrowDisabled, setIsLeftArrowDisabled] = useState(false); | |||
| const [isRightArrowDisabled, setIsRightArrowDisabled] = useState(false); | |||
| const { t } = useTranslation(); | |||
| const handleClose = () => { | |||
| onClose(); | |||
| }; | |||
| @@ -85,7 +87,7 @@ const DayDetailsComponent = ({ | |||
| > | |||
| <DialogTitle className="day-datails-title-container"> | |||
| <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> | |||
| <img | |||
| src={x} | |||
| @@ -153,9 +155,7 @@ const DayDetailsComponent = ({ | |||
| }} | |||
| > | |||
| {isLeftArrowDisabled === true ? ( | |||
| <div | |||
| className="day-details-arrow-container" | |||
| > | |||
| <div className="day-details-arrow-container"> | |||
| <img src={arrowLeftDisabled} /> | |||
| </div> | |||
| ) : ( | |||
| @@ -2,76 +2,90 @@ import React from "react"; | |||
| import PropTypes from "prop-types"; | |||
| import { formatDate } from "../../util/helpers/dateHelpers"; | |||
| 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 = ({ | |||
| 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> | |||
| ); | |||
| }; | |||
| 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; | |||
| @@ -12,6 +12,7 @@ import Backdrop from "../../components/MUI/BackdropComponent"; | |||
| import { IconButton } from "@mui/material"; | |||
| import plus from "../../assets/images/plus.png"; | |||
| import SelectionCard from "./SelectionCard"; | |||
| import { useTranslation } from "react-i18next"; | |||
| const Selection = (props) => { | |||
| const [over, setOver] = useState(false); | |||
| @@ -19,13 +20,17 @@ const Selection = (props) => { | |||
| const errorMessage = useSelector(selectDoneProcessError); | |||
| const dispatch = useDispatch(); | |||
| const user = useSelector(selectAuthUser); | |||
| const { t } = useTranslation(); | |||
| const dropItem = (e, selId) => { | |||
| setOver(false); | |||
| var data = e.dataTransfer.getData("text/plain"); | |||
| const selectionProcess = JSON.parse(data); | |||
| if (selectionProcess.selectionLevelId < selId && selectionProcess.status !== "Neuspešno") { | |||
| if ( | |||
| selectionProcess.selectionLevelId < selId && | |||
| selectionProcess.status !== "Neuspešno" | |||
| ) { | |||
| dispatch( | |||
| setDoneProcessReq({ | |||
| id: selectionProcess.id, | |||
| @@ -58,8 +63,10 @@ const Selection = (props) => { | |||
| 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) => { | |||
| return ( | |||
| <SelectionCard | |||
| @@ -82,10 +89,10 @@ const Selection = (props) => { | |||
| onDrop={(e) => dropItem(e, props.selection.id)} | |||
| > | |||
| <div className="selection-card-title"> | |||
| <h3 style={{marginRight:'50px'}}>{props.selection.name}</h3> | |||
| <h3 style={{ marginRight: "50px" }}>{props.selection.name}</h3> | |||
| {props.order === 0 ? ( | |||
| <IconButton | |||
| sx={{marginRight:'35px'}} | |||
| sx={{ marginRight: "35px" }} | |||
| className={`c-btn--primary-outlined c-btn td-btn`} | |||
| onClick={props.modalEvent} | |||
| > | |||
| @@ -94,7 +101,7 @@ const Selection = (props) => { | |||
| position: "relative", | |||
| }} | |||
| src={plus} | |||
| data-testid='interview-image' | |||
| data-testid="interview-image" | |||
| /> | |||
| </IconButton> | |||
| ) : ( | |||
| @@ -109,7 +116,9 @@ const Selection = (props) => { | |||
| {applicants && applicants !== null && applicants?.length === 0 && ( | |||
| <div className="sel-item-no-data"> | |||
| <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> | |||
| )} | |||
| @@ -14,6 +14,7 @@ import { | |||
| setDoneProcessReq, | |||
| setUpdateStatusReq, | |||
| } from "../../store/actions/processes/processAction"; | |||
| import { useTranslation } from "react-i18next"; | |||
| // import Button from "../Button/Button"; | |||
| const options = ["Zakazan", "Odrađen", "Čeka na zakazivanje", "Neuspešno"]; | |||
| @@ -21,6 +22,7 @@ const options = ["Zakazan", "Odrađen", "Čeka na zakazivanje", "Neuspešno"]; | |||
| const SelectionCard = (props) => { | |||
| const [showForm, setShowForm] = useState(false); | |||
| const [selected, setSelected] = useState(props.item.status); | |||
| const { t } = useTranslation(); | |||
| const { success } = useSelector((s) => s.statusUpdate); | |||
| const { | |||
| @@ -96,7 +98,7 @@ const SelectionCard = (props) => { | |||
| className="sel-item" | |||
| onDragStart={props.dragStart} | |||
| onClick={clickHandler} | |||
| data-testid='sel-card' | |||
| data-testid="sel-card" | |||
| > | |||
| {" "} | |||
| <div | |||
| @@ -170,11 +172,11 @@ const SelectionCard = (props) => { | |||
| className="interbtn" | |||
| onClick={(e) => changeInterviewerHandler(e)} | |||
| > | |||
| Promeni | |||
| {t("common.change")} | |||
| </Button> | |||
| </div> | |||
| <p> | |||
| Intervjuer: {props.item.scheduler.firstName}{" "} | |||
| {t("selection.interviewer")}: {props.item.scheduler.firstName}{" "} | |||
| {props.item.scheduler.lastName} | |||
| </p> | |||
| </div> | |||
| @@ -56,7 +56,7 @@ const SelectionFilter = ({ open, handleClose, statuses }) => { | |||
| <div className="ad-filters-header-container"> | |||
| <div className="ad-filters-header"> | |||
| <img src={filterIcon} alt="filter_icon" /> | |||
| <h3>{t("filter.title")}</h3> | |||
| <h3>{t("filters.filters")}</h3> | |||
| <p> | |||
| <sub>| {t("selection.title")}</sub> | |||
| </p> | |||
| @@ -67,7 +67,7 @@ const SelectionFilter = ({ open, handleClose, statuses }) => { | |||
| </div> | |||
| <div className="ad-filters-technologies"> | |||
| <div className="ad-filters-sub-title"> | |||
| <p>{t("selection.filterStatus")}</p> | |||
| <p>Status</p> | |||
| </div> | |||
| <div className="ad-filters-technologies-checkboxes"> | |||
| <FormGroup> | |||
| @@ -101,7 +101,7 @@ const SelectionFilter = ({ open, handleClose, statuses }) => { | |||
| <p>{t("selection.filterDate")}</p> | |||
| </div> | |||
| <div className="add-ad-modal-stage-sub-card"> | |||
| <label>{t("selection.filterFrom")}</label> | |||
| <label>{t("common.from")}</label> | |||
| <input | |||
| type="date" | |||
| placeholder="ex" | |||
| @@ -110,7 +110,7 @@ const SelectionFilter = ({ open, handleClose, statuses }) => { | |||
| /> | |||
| </div> | |||
| <div className="add-ad-modal-stage-sub-card"> | |||
| <label>{t("selection.filterTo")}</label> | |||
| <label>{t("common.to")}</label> | |||
| <input | |||
| type="date" | |||
| placeholder="ex" | |||
| @@ -121,7 +121,7 @@ const SelectionFilter = ({ open, handleClose, statuses }) => { | |||
| </div> | |||
| <div className="ad-filters-search"> | |||
| <button onClick={onSubmitFilters} className="c-btn c-btn--primary"> | |||
| {t("selection.filterSubmit")} | |||
| {t("common.search")} | |||
| </button> | |||
| </div> | |||
| </div> | |||
| @@ -4,8 +4,10 @@ import Box from "@mui/material/Box"; | |||
| import Drawer from "@mui/material/Drawer"; | |||
| import filterIcon from "../../assets/images/filters.png"; | |||
| import x from "../../assets/images/x.png"; | |||
| import { useTranslation } from "react-i18next"; | |||
| const CustomDrawer = ({ title, open, onCloseDrawer, children }) => { | |||
| const { t } = useTranslation(); | |||
| const list = () => ( | |||
| <Box | |||
| sx={{ | |||
| @@ -21,7 +23,7 @@ const CustomDrawer = ({ title, open, onCloseDrawer, children }) => { | |||
| <div className="custom-filter-drawer-header-container"> | |||
| <div className="custom-filter-drawer-header"> | |||
| <img src={filterIcon} alt="filter_icon" /> | |||
| <h3>Filteri</h3> | |||
| <h3>{t("filters.filters")}</h3> | |||
| <p> | |||
| <sub>| {title}</sub> | |||
| </p> | |||
| @@ -1,21 +1,21 @@ | |||
| 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 */ | |||
| i18n.use(initReactI18next).init({ | |||
| lng: 'rs', | |||
| fallbackLng: 'en', | |||
| lng: "en", | |||
| fallbackLng: "en", | |||
| debug: false, | |||
| supportedLngs: ['en','rs'], | |||
| supportedLngs: ["en", "rs"], | |||
| resources: { | |||
| en: { | |||
| translation: enTranslations, | |||
| }, | |||
| rs:{ | |||
| rs: { | |||
| translation: rsTranslations, | |||
| }, | |||
| }, | |||
| @@ -11,20 +11,23 @@ export default { | |||
| trademark: "TM", | |||
| search: "Search", | |||
| error: "Error", | |||
| continue: "Continue", | |||
| continue: "CONTINUE", | |||
| labelUsername: "Username", | |||
| labelPassword: "Password", | |||
| or: "or", | |||
| and:"and", | |||
| next: "Next", | |||
| nextPage: "Next page", | |||
| previousPage: "Previous page", | |||
| back: "Back", | |||
| back: "BACK", | |||
| goBack: "Go Back", | |||
| ok: "Ok", | |||
| done: "Done", | |||
| confirm: "Confirm", | |||
| printDownload: "Print/Download", | |||
| cancel: "Cancel", | |||
| delete: "Delete", | |||
| change: "Change", | |||
| remove: "Remove", | |||
| invite: "Invite", | |||
| save: "Save", | |||
| @@ -32,12 +35,26 @@ export default { | |||
| download: "Download", | |||
| yes: "Yes", | |||
| no: "No", | |||
| to: "to", | |||
| to: "To", | |||
| from: "From", | |||
| select: "Select...", | |||
| none: "None", | |||
| date: { | |||
| 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: { | |||
| welcome: "Welcome!", | |||
| @@ -61,6 +78,15 @@ export default { | |||
| useDifferentEmail: "Use different email address or username", | |||
| signInWithGoogle: "Continue with google", | |||
| 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: { | |||
| weak: "weak", | |||
| @@ -94,6 +120,7 @@ export default { | |||
| UsernameDoesNotExist: "Username does not exist", | |||
| }, | |||
| nav: { | |||
| navigation: "Navigation", | |||
| ads: "Ads", | |||
| selectionFlow: "Selection flow", | |||
| candidates: "Candidates", | |||
| @@ -102,6 +129,7 @@ export default { | |||
| statistics: "Statistics", | |||
| users: "Users", | |||
| signOut: "Sign Out", | |||
| schedule:"Schedule" | |||
| }, | |||
| ads: { | |||
| activeAds: "Active Ads", | |||
| @@ -114,6 +142,28 @@ export default { | |||
| adDetailsRequirements: "Requirements", | |||
| adDetailsOffer: "What We Offer", | |||
| 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: { | |||
| management: "User management", | |||
| @@ -127,6 +177,19 @@ export default { | |||
| contact: "Contact", | |||
| phone: "Phone", | |||
| 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: { | |||
| done: { | |||
| @@ -136,9 +199,90 @@ export default { | |||
| 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" | |||
| } | |||
| }; | |||
| @@ -10,36 +10,53 @@ export default { | |||
| common: { | |||
| // close: 'Close', | |||
| // trademark: 'TM', | |||
| // search: 'Search', | |||
| search: "Pretrazi", | |||
| // error: 'Error', | |||
| // continue: 'Continue', | |||
| labelUsername: "Korisničko ime", | |||
| labelPassword: "Šifra", | |||
| labelConfirmPassword: "Ponovljena šifra", | |||
| or: "ili", | |||
| and: "i", | |||
| // next: 'Next', | |||
| // nextPage: 'Next page', | |||
| // previousPage: 'Previous page', | |||
| // back: 'Back', | |||
| back: "NAZAD", | |||
| // goBack: 'Go Back', | |||
| // ok: 'Ok', | |||
| // done: 'Done', | |||
| // confirm: 'Confirm', | |||
| confirm: "Potvrdi", | |||
| // printDownload: 'Print/Download', | |||
| // cancel: 'Cancel', | |||
| cancel: "Otkazi", | |||
| delete: "Obrisi", | |||
| change: "Promeni", | |||
| // remove: 'Remove', | |||
| // invite: 'Invite', | |||
| // save: 'Save', | |||
| // complete: 'Complete', | |||
| // download: 'Download', | |||
| download: "Preuzmi", | |||
| // yes: 'Yes', | |||
| // no: 'No', | |||
| // to: 'to', | |||
| to: "Do", | |||
| from: "Od", | |||
| // select: 'Select...', | |||
| // none: 'None', | |||
| // date: { | |||
| // 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: { | |||
| welcome: "Dobrodošli!", | |||
| @@ -58,17 +75,17 @@ export default { | |||
| emailRequired: "Potrebno je uneti email adresu.", | |||
| // signUp: 'Sign Up', | |||
| 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: | |||
| "Samo unesi e-mail adresu svog HR Center profila.", | |||
| forgotYourPasswordButton: "POŠALJI", | |||
| forgotYourPasswordBackLink: "Nazad na Login", | |||
| 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', | |||
| // get useDifferentEmail() { | |||
| // return this._useDifferentEmail; | |||
| @@ -77,7 +94,16 @@ export default { | |||
| // this._useDifferentEmail = value; | |||
| // }, | |||
| 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: { | |||
| // weak: 'weak', | |||
| @@ -113,14 +139,15 @@ export default { | |||
| // }, | |||
| 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", | |||
| users: 'Korisnici', | |||
| signOut: 'Izloguj se' | |||
| users: "Korisnici", | |||
| signOut: "Izloguj se", | |||
| }, | |||
| ads: { | |||
| activeAds: "Aktivni Oglasi", | |||
| @@ -133,33 +160,73 @@ export default { | |||
| adDetailsRequirements: "Zahtevi", | |||
| adDetailsOffer: "Šta Nudimo", | |||
| 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: { | |||
| 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: { | |||
| title: "Filteri" | |||
| title: "Filteri", | |||
| }, | |||
| selectionLevels: { | |||
| done: { | |||
| @@ -169,9 +236,90 @@ export default { | |||
| 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", | |||
| }, | |||
| }; | |||
| @@ -133,7 +133,9 @@ const AdDetailsPage = () => { | |||
| <h1>{ad.title}</h1> | |||
| </div> | |||
| <div className="ad-details-tech-logo-title-sub"> | |||
| <sub>| {ad.totalApplicants} prijavljenih</sub> | |||
| <sub> | |||
| | {ad.totalApplicants} {t("ads.registered")} | |||
| </sub> | |||
| </div> | |||
| </div> | |||
| {!(new Date(ad.expiredAt) < new Date()) && ( | |||
| @@ -156,10 +158,10 @@ const AdDetailsPage = () => { | |||
| </div> | |||
| <ConfirmDialog | |||
| open={showArchiveAdDialog} | |||
| title={"Arhiviranje oglasa"} | |||
| title={t("ads.archivingAd")} | |||
| subtitle={ad.title} | |||
| imgSrc={archiveIcon} | |||
| content="Da li ste sigurni da želite da arhivirate oglas?" | |||
| content={t("ads.archivingAdQuestion")} | |||
| onClose={() => { | |||
| setShowArchiveAdDialog(false); | |||
| }} | |||
| @@ -172,8 +174,8 @@ const AdDetailsPage = () => { | |||
| </p> | |||
| </div> | |||
| <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 className="ad-details-content-content"> | |||
| <div className="ad-details-content-conten-description"> | |||
| @@ -266,7 +268,7 @@ const AdDetailsPage = () => { | |||
| className="ad-details-buttons-link" | |||
| onClick={() => history.push({ pathname: ADS_PAGE })} | |||
| > | |||
| Nazad na sve oglase | |||
| {t("ads.backToAds")} | |||
| </button> | |||
| {!(new Date(ad.expiredAt) < new Date()) && ( | |||
| @@ -274,7 +276,7 @@ const AdDetailsPage = () => { | |||
| className="c-btn c-btn--primary add-ad-btn apply-for-ad-button" | |||
| onClick={() => setApplyForAdOpenModal(true)} | |||
| > | |||
| PRIJAVI SE | |||
| {t("ads.signUp").toUpperCase()} | |||
| </IconButton> | |||
| )} | |||
| </div> | |||
| @@ -280,7 +280,7 @@ const AdsPage = ({ history }) => { | |||
| } | |||
| onClick={() => handleChangeVisibility(true)} | |||
| > | |||
| {!matches && "Pretraga"} | |||
| {!matches && t("common.search")} | |||
| <img | |||
| style={{ | |||
| position: "relative", | |||
| @@ -356,14 +356,14 @@ const AdsPage = ({ history }) => { | |||
| <FilterButton onShowFilters={handleToggleFiltersDrawer} /> | |||
| </div> | |||
| <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"> | |||
| <IconButton | |||
| className="c-btn ads-page-btn c-btn--primary add-ad-btn" | |||
| onClick={() => history.push(CREATE_AD_PAGE)} | |||
| > | |||
| Dodaj Oglas | |||
| {t("ads.adNewAd")} | |||
| </IconButton> | |||
| </div> | |||
| </div> | |||
| @@ -441,7 +441,7 @@ const AdsPage = ({ history }) => { | |||
| className="c-btn ads-page-btn c-btn--primary add-ad-btn" | |||
| onClick={createAd} | |||
| > | |||
| + Oglas | |||
| + {t("ads.ad")} | |||
| </IconButton> | |||
| </div> | |||
| )} | |||
| @@ -1,5 +1,6 @@ | |||
| import React from "react"; | |||
| import PropTypes from "prop-types"; | |||
| import { useTranslation } from "react-i18next"; | |||
| const CreateAdFirstStep = ({ | |||
| title, | |||
| @@ -11,7 +12,7 @@ const CreateAdFirstStep = ({ | |||
| expiredAt, | |||
| setExpiredAt, | |||
| }) => { | |||
| const { t } = useTranslation(); | |||
| const employmentTypeHandler = (type) => { | |||
| setEmploymentType(type); | |||
| }; | |||
| @@ -23,7 +24,7 @@ const CreateAdFirstStep = ({ | |||
| return ( | |||
| <div data-testid="create-ad-first-step-form"> | |||
| <div className="create-ad-form-control"> | |||
| <label>Naslov</label> | |||
| <label>{t("common.title")}</label> | |||
| <input | |||
| type="text" | |||
| className="create-ad-form-control-first-step-input" | |||
| @@ -33,7 +34,7 @@ const CreateAdFirstStep = ({ | |||
| /> | |||
| </div> | |||
| <div className="create-ad-form-control"> | |||
| <label>Tip zaposlenja</label> | |||
| <label>{t("filters.employmentType")}</label> | |||
| <div className="create-ad-form-control-buttons"> | |||
| <button | |||
| className={`c-btn ${ | |||
| @@ -43,7 +44,7 @@ const CreateAdFirstStep = ({ | |||
| }`} | |||
| onClick={employmentTypeHandler.bind(this, "Work")} | |||
| > | |||
| Posao | |||
| {t("filters.work")} | |||
| </button> | |||
| <button | |||
| className={`c-btn ${ | |||
| @@ -53,12 +54,12 @@ const CreateAdFirstStep = ({ | |||
| }`} | |||
| onClick={employmentTypeHandler.bind(this, "Intership")} | |||
| > | |||
| Intership | |||
| {t("filters.internship")} | |||
| </button> | |||
| </div> | |||
| </div> | |||
| <div className="create-ad-form-control"> | |||
| <label>Radno vreme</label> | |||
| <label>{t("filters.workHour")}</label> | |||
| <div className="create-ad-form-control-buttons"> | |||
| <button | |||
| className={`c-btn ${ | |||
| @@ -68,7 +69,7 @@ const CreateAdFirstStep = ({ | |||
| }`} | |||
| onClick={workHourHandler.bind(this, "PartTime")} | |||
| > | |||
| Part-time | |||
| {t("filters.partTime")} | |||
| </button> | |||
| <button | |||
| className={`c-btn ${ | |||
| @@ -78,12 +79,12 @@ const CreateAdFirstStep = ({ | |||
| }`} | |||
| onClick={workHourHandler.bind(this, "FullTime")} | |||
| > | |||
| Full-time | |||
| {t("filters.fullTime")} | |||
| </button> | |||
| </div> | |||
| </div> | |||
| <div className="create-ad-form-control"> | |||
| <label>Datum isteka oglasa</label> | |||
| <label>{t("ads.expirationDate")}</label> | |||
| <input | |||
| type="date" | |||
| className="create-ad-form-control-first-step-input" | |||
| @@ -13,6 +13,7 @@ import CreateAdSecondStep from "./CreateAdSecondStep"; | |||
| import CreateAdThirdStep from "./CreateAdThirdStep"; | |||
| import { useHistory } from "react-router-dom"; | |||
| import { ADS_PAGE } from "../../constants/pages"; | |||
| import { useTranslation } from "react-i18next"; | |||
| const CreateAdPage = () => { | |||
| const [stage, setStage] = useState(1); | |||
| @@ -23,6 +24,7 @@ const CreateAdPage = () => { | |||
| const [experience, setExperience] = useState(0); | |||
| const childRef = useRef(); | |||
| const history = useHistory(); | |||
| const { t } = useTranslation(); | |||
| const technologies = useSelector( | |||
| (state) => state.addAdTechnologies.technologies | |||
| @@ -115,9 +117,9 @@ const CreateAdPage = () => { | |||
| alt="plus" | |||
| style={{ width: "18px", height: "18px" }} | |||
| /> | |||
| <h2>Dodavanje</h2> | |||
| <h2>{t("ads.adding")}</h2> | |||
| <h2> | |||
| <sub> | Oglas</sub> | |||
| <sub> | {t("ads.ad")}</sub> | |||
| </h2> | |||
| </div> | |||
| <div className="create-ad-steps"> | |||
| @@ -148,19 +150,19 @@ const CreateAdPage = () => { | |||
| disabled={stage === 1} | |||
| onClick={backClickHandler} | |||
| > | |||
| NAZAD | |||
| {t("common.back")} | |||
| </button> | |||
| <button | |||
| className="create-ad-buttons-forward" | |||
| onClick={forwardClickHandler} | |||
| > | |||
| NASTAVI | |||
| {t("common.continue")} | |||
| </button> | |||
| </div> | |||
| </div> | |||
| <div className="create-ad-go-back"> | |||
| <button onClick={() => history.push(ADS_PAGE)}> | |||
| Nazad na sve oglase | |||
| {t("ads.backToAds")} | |||
| </button> | |||
| </div> | |||
| </div> | |||
| @@ -3,8 +3,10 @@ import PropTypes from "prop-types"; | |||
| import { Checkbox, FormControlLabel } from "@mui/material"; | |||
| import { useDispatch } from "react-redux"; | |||
| import { changeIsCheckedAddAdValue } from "../../store/actions/addAdTechnologies/addAdTechnologiesActions"; | |||
| import { useTranslation } from "react-i18next"; | |||
| const CreateAdSecondStep = ({ technologies, experience, setExperience }) => { | |||
| const { t } = useTranslation; | |||
| const dispatch = useDispatch(); | |||
| const handleCheckboxes = (technologyId) => { | |||
| @@ -14,7 +16,7 @@ const CreateAdSecondStep = ({ technologies, experience, setExperience }) => { | |||
| return ( | |||
| <div data-testid="create-ad-second-step-form"> | |||
| <div className="create-ad-form-control"> | |||
| <label>Napredne tehnologije</label> | |||
| <label>{t("filters.advancedTechnologies")}</label> | |||
| </div> | |||
| <div className="add-ad-modal-stage-sub-card"> | |||
| <div className="add-ad-modal-stage-sub-card-checkboxes-group"> | |||
| @@ -62,7 +64,7 @@ const CreateAdSecondStep = ({ technologies, experience, setExperience }) => { | |||
| </div> | |||
| <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"> | |||
| {technologies | |||
| .filter((x) => x.technologyType === "Other") | |||
| @@ -84,7 +86,7 @@ const CreateAdSecondStep = ({ technologies, experience, setExperience }) => { | |||
| </div> | |||
| </div> | |||
| <div className="create-ad-form-control"> | |||
| <label>Godine iskustva</label> | |||
| <label>{t("filters.experience")}</label> | |||
| <input | |||
| type="number" | |||
| min={0} | |||
| @@ -3,8 +3,10 @@ import PropTypes from "prop-types"; | |||
| import { Editor } from "@tinymce/tinymce-react"; | |||
| import { useRef } from "react"; | |||
| import { useEffect } from "react"; | |||
| import { useTranslation } from "react-i18next"; | |||
| const CreateAdThirdStep = ({ childRef }) => { | |||
| const { t } = useTranslation(); | |||
| const editorKeyResponsibilitiesRef = useRef(); | |||
| const editorRequirementsRef = useRef(); | |||
| const editorOfferRef = useRef(); | |||
| @@ -24,7 +26,7 @@ const CreateAdThirdStep = ({ childRef }) => { | |||
| return ( | |||
| <div data-testid="create-ad-third-step-form"> | |||
| <div className="create-ad-form-control"> | |||
| <label>Glavna zaduženja</label> | |||
| <label>{t("ads.duties")}</label> | |||
| <Editor | |||
| onInit={(evt, editor) => | |||
| (editorKeyResponsibilitiesRef.current = editor) | |||
| @@ -33,14 +35,14 @@ const CreateAdThirdStep = ({ childRef }) => { | |||
| /> | |||
| </div> | |||
| <div className="create-ad-form-control"> | |||
| <label>Uslovi</label> | |||
| <label>{t("ads.conditions")}</label> | |||
| <Editor | |||
| onInit={(evt, editor) => (editorRequirementsRef.current = editor)} | |||
| style={{ height: "1rem !important" }} | |||
| /> | |||
| </div> | |||
| <div className="create-ad-form-control"> | |||
| <label>Šta nudimo</label> | |||
| <label>{t("ads.offer")}</label> | |||
| <Editor | |||
| onInit={(evt, editor) => (editorOfferRef.current = editor)} | |||
| style={{ height: "1rem !important" }} | |||
| @@ -11,6 +11,7 @@ import PropTypes from "prop-types"; | |||
| import { useTheme } from "@mui/system"; | |||
| import { useMediaQuery } from "@mui/material"; | |||
| import { selectAdsCandidates } from "../../store/selectors/candidatesSelectors"; | |||
| import { useTranslation } from "react-i18next"; | |||
| const AdsCandidatesPage = ({ history, search }) => { | |||
| const dispatch = useDispatch(); | |||
| @@ -18,6 +19,7 @@ const AdsCandidatesPage = ({ history, search }) => { | |||
| const [getRef, setRef] = useDynamicRefs(); | |||
| const theme = useTheme(); | |||
| const matches = useMediaQuery(theme.breakpoints.down("361")); | |||
| const {t} = useTranslation() | |||
| useEffect(() => { | |||
| dispatch( | |||
| @@ -108,7 +110,7 @@ const AdsCandidatesPage = ({ history, search }) => { | |||
| <img src={adImage} className="ads-candidates-image" /> | |||
| <p className="ads-candidates-title">{adCandidates.title}</p> | |||
| <p className="ads-candidates-numberOfApplicants"> | |||
| | {adCandidates.nubmerOfApplicants} prijavljenih | |||
| | {adCandidates.nubmerOfApplicants} {t("ads.registered")} | |||
| </p> | |||
| </div> | |||
| <div className="ads-candidates-slider"> | |||
| @@ -24,6 +24,7 @@ import deleteIcon from "../../assets/images/delete.png"; | |||
| import { selectCandidate } from "../../store/selectors/candidateSelectors"; | |||
| import { selectAuthUser } from "../../store/selectors/userSelectors"; | |||
| import { selectUsers } from "../../store/selectors/usersSelector"; | |||
| import { useTranslation } from "react-i18next"; | |||
| const CandidateDetailsPage = ({ history }) => { | |||
| const dispatch = useDispatch(); | |||
| @@ -38,6 +39,7 @@ const CandidateDetailsPage = ({ history }) => { | |||
| const adsSliderRef = useRef(); | |||
| const [showDelete, setDelete] = useState(false); | |||
| const [usersToNotify, setUsersToNotify] = useState([]); //emails of users which are taged in comment | |||
| const { t } = useTranslation(); | |||
| useEffect(() => { | |||
| dispatch(fetchCandidate({ id })); | |||
| @@ -217,10 +219,10 @@ const CandidateDetailsPage = ({ history }) => { | |||
| <div className="main-candidate-container"> | |||
| <ConfirmDialog | |||
| open={showDelete} | |||
| title={"Brisanje kandidata"} | |||
| title={t("candidates.deleteCandidate")} | |||
| subtitle={candidate.firstName + " " + candidate.lastName} | |||
| imgSrc={deleteIcon} | |||
| content="Da li ste sigurni da zelite da obrisete kandidata?" | |||
| content={t("candidates.deleteCandidateQuestion")} | |||
| onClose={() => { | |||
| setDelete(false); | |||
| }} | |||
| @@ -230,7 +232,7 @@ const CandidateDetailsPage = ({ history }) => { | |||
| <div className="r-b-rectangle"></div> | |||
| <div className="top-candidate-container"> | |||
| <div> | |||
| <p className="candidate-header">Kandidat</p> | |||
| <p className="candidate-header">{t("candidates.candidate")}</p> | |||
| <span className="separation-line">|</span> | |||
| <p className="candidate-lower-header"> | |||
| {candidate.firstName} {candidate.lastName} | |||
| @@ -241,7 +243,7 @@ const CandidateDetailsPage = ({ history }) => { | |||
| className="c-btn c-btn--primary-outlined candidate-btn" | |||
| onClick={() => setDelete(true)} | |||
| > | |||
| Obrisi | |||
| {t("common.delete")} | |||
| <img src={deleteImage} alt="delete" className="candidates-image" /> | |||
| </IconButton> | |||
| </div> | |||
| @@ -250,8 +252,8 @@ const CandidateDetailsPage = ({ history }) => { | |||
| <div className="details-candidate-container"> | |||
| <p style={{ margin: 0 }} data-testid="candidate-experience"> | |||
| {candidate.experience === 0 | |||
| ? "No experience" | |||
| : "Experience:" + candidate.experience} | |||
| ? t("common.noExperience") | |||
| : t("candidates.experience") + ":" + candidate.experience} | |||
| </p> | |||
| <div className="technologies-candidate-container"> | |||
| {candidate.technologyApplicants.map((obj, index) => ( | |||
| @@ -263,16 +265,25 @@ const CandidateDetailsPage = ({ history }) => { | |||
| <div className="candidate-informations-container"> | |||
| <div className="candidate-informations-sub-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 | |||
| style={{ alignSelf: "flex-end", marginLeft: 42 }} | |||
| 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 className="candidate-property-value"> | |||
| {candidate.professionalQualification === "" | |||
| @@ -283,9 +294,11 @@ const CandidateDetailsPage = ({ history }) => { | |||
| </div> | |||
| <div className="candidate-informations-sub-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">Telefon:</p> | |||
| <p className="candidate-property">{t("users.phone")}:</p> | |||
| </div> | |||
| <div | |||
| style={{ alignSelf: "flex-end", marginLeft: 42 }} | |||
| @@ -299,7 +312,9 @@ const CandidateDetailsPage = ({ history }) => { | |||
| </div> | |||
| <div className="candidate-informations-sub-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">GitHub</p> | |||
| <p className="candidate-property">BitBucket</p> | |||
| @@ -357,7 +372,7 @@ const CandidateDetailsPage = ({ history }) => { | |||
| </div> | |||
| <div className="comment-separation-line"></div> | |||
| <div className="send-comment-container"> | |||
| <p>Komentar</p> | |||
| <p>{t("candidates.comment")}</p> | |||
| <div className="send-comment-sub-container"> | |||
| <MentionsInput | |||
| value={inputValue} | |||
| @@ -411,7 +426,7 @@ const CandidateDetailsPage = ({ history }) => { | |||
| alt="plane" | |||
| className="candidates-image" | |||
| /> | |||
| <p>Komentar</p> | |||
| <p>{t("candidates.comment")}</p> | |||
| </div> | |||
| ) : ( | |||
| <div | |||
| @@ -430,7 +445,9 @@ const CandidateDetailsPage = ({ history }) => { | |||
| </div> | |||
| </div> | |||
| <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 | |||
| style={{ | |||
| @@ -440,7 +457,10 @@ const CandidateDetailsPage = ({ history }) => { | |||
| {(matches | |||
| ? candidate.ads.length > 1 | |||
| : 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}> | |||
| <img src={arrow_left} alt="arrow-left" /> | |||
| </button> | |||
| @@ -468,7 +488,7 @@ const CandidateDetailsPage = ({ history }) => { | |||
| className="applicant-ads-back-button" | |||
| onClick={goToPageWithAllCandidates} | |||
| > | |||
| Nazad na sve kandidate | |||
| {t("candidates.backToCandidates")} | |||
| </p> | |||
| <a | |||
| className="applicant-cv-button" | |||
| @@ -476,7 +496,7 @@ const CandidateDetailsPage = ({ history }) => { | |||
| href={`data:application/pdf;base64,${candidate.cv}`} | |||
| title="Download pdf document" | |||
| > | |||
| Preuzmi cv | |||
| {t("common.download")} cv | |||
| </a> | |||
| </div> | |||
| </div> | |||
| @@ -14,6 +14,7 @@ import { setTechnologiesReq } from "../../store/actions/technologies/technologie | |||
| import { selectTechnologies } from "../../store/selectors/technologiesSelectors"; | |||
| import { useDispatch, useSelector } from "react-redux"; | |||
| import Fade from "@mui/material/Fade"; | |||
| import { useTranslation } from "react-i18next"; | |||
| const CandidatesPage = ({ history }) => { | |||
| const dispatch = useDispatch(); | |||
| @@ -33,6 +34,7 @@ const CandidatesPage = ({ history }) => { | |||
| { name: "Posao", isChecked: false }, | |||
| { name: "Intership", isChecked: false }, | |||
| ]); | |||
| const { t } = useTranslation(); | |||
| useEffect(() => { | |||
| dispatch(setTechnologiesReq()); | |||
| @@ -95,7 +97,7 @@ const CandidatesPage = ({ history }) => { | |||
| <div className="top-candidates-container"> | |||
| {!matches ? ( | |||
| <p className="candidates-header" data-testid="candidates-header1"> | |||
| Kandidati | |||
| {t("candidates.candidates")} | |||
| </p> | |||
| ) : ( | |||
| <p | |||
| @@ -103,7 +105,7 @@ const CandidatesPage = ({ history }) => { | |||
| data-testid="candidates-header2" | |||
| style={{ fontSize: "22px" }} | |||
| > | |||
| Kandidati | |||
| {t("candidates.candidates")} | |||
| </p> | |||
| )} | |||
| <div style={{ postion: "relative" }}> | |||
| @@ -129,7 +131,7 @@ const CandidatesPage = ({ history }) => { | |||
| className="c-btn c-btn--primary-outlined candidate-btn all-white-btn" | |||
| onClick={changeView} | |||
| > | |||
| Tablicni prikaz | |||
| {t("candidates.tableView")} | |||
| <img | |||
| src={tableImage} | |||
| alt="table" | |||
| @@ -141,7 +143,7 @@ const CandidatesPage = ({ history }) => { | |||
| className="c-btn c-btn--primary-outlined candidate-btn candidate-btn-view-2" | |||
| onClick={changeView} | |||
| > | |||
| Tablicni prikaz | |||
| {t("candidates.tableView")} | |||
| <img | |||
| src={tableImage} | |||
| alt="table" | |||
| @@ -166,7 +168,7 @@ const CandidatesPage = ({ history }) => { | |||
| className="c-btn c-btn--primary-outlined candidate-btn" | |||
| onClick={handleChangeVisibility} | |||
| > | |||
| Pretraga | |||
| {t("candidates.search")} | |||
| <img | |||
| src={searchImage} | |||
| alt="filter" | |||
| @@ -179,7 +181,7 @@ const CandidatesPage = ({ history }) => { | |||
| className="c-btn c-btn--primary-outlined candidate-btn candidate-btn-filters1" | |||
| onClick={handleToggleFiltersDrawer} | |||
| > | |||
| Filteri | |||
| {t("filters.filters")} | |||
| <img | |||
| src={filterImage} | |||
| alt="filter" | |||
| @@ -14,6 +14,8 @@ import { | |||
| } from "../../store/selectors/candidatesSelectors"; | |||
| import { getCV } from "../../request/candidatesRequest"; | |||
| import Fade from "@mui/material/Fade"; | |||
| import { useTranslation } from "react-i18next"; | |||
| const TableViewPage = ({ | |||
| history, | |||
| setPage, | |||
| @@ -32,6 +34,7 @@ const TableViewPage = ({ | |||
| const matches = useMediaQuery(theme.breakpoints.down("361")); | |||
| const [linkToCV, setLinkToCV] = useState(""); | |||
| const [isCVDisplayed, setIsCVDisplayed] = useState(false); | |||
| const { t } = useTranslation(); | |||
| useEffect(() => { | |||
| dispatch( | |||
| @@ -140,10 +143,12 @@ const TableViewPage = ({ | |||
| > | |||
| <thead> | |||
| <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> | |||
| </tr> | |||
| </thead> | |||
| @@ -16,6 +16,7 @@ import { selectPatternApplicants } from "../../store/selectors/patternApplicants | |||
| import { PATTERNS_PAGE } from "../../constants/pages"; | |||
| import { useHistory } from "react-router-dom"; | |||
| import parse from "html-react-parser"; | |||
| import { useTranslation } from "react-i18next"; | |||
| const PatternDetailsPage = () => { | |||
| const [emails, setEmails] = useState([]); | |||
| @@ -29,6 +30,7 @@ const PatternDetailsPage = () => { | |||
| const { id } = useParams(); | |||
| const dispatch = useDispatch(); | |||
| const history = useHistory(); | |||
| const { t } = useTranslation(); | |||
| const navigateToPatternsPage = () => { | |||
| history.push(PATTERNS_PAGE); | |||
| @@ -102,17 +104,17 @@ const PatternDetailsPage = () => { | |||
| <div className="pattern-details" data-testid="pattern-details"> | |||
| <div className="pattern-details-header"> | |||
| <p> | |||
| <span>Napravljen:</span>{" "} | |||
| <span>{t("patterns.made")}:</span>{" "} | |||
| {new Date(pattern.createdAt).toLocaleDateString()} | |||
| </p> | |||
| </div> | |||
| <div className="pattern-details-card"> | |||
| <div className="pattern-details-card-title"> | |||
| <div className="pattern-details-card-title-title"> | |||
| <h1>Šablon</h1> | |||
| <h1>{t("pattern.pattern")}</h1> | |||
| </div> | |||
| <div className="pattern-details-card-title-sub"> | |||
| <sub> | Zakazivanje termina</sub> | |||
| <sub> | {t("patterns.scheduling")}</sub> | |||
| </div> | |||
| </div> | |||
| <div className="pattern-details-card-sub-card"> | |||
| @@ -165,7 +167,7 @@ const PatternDetailsPage = () => { | |||
| <div className="pattern-details-card-sub-card"> | |||
| <div className="pattern-details-card-sub-card-title"> | |||
| <p>Teskt poruke</p> | |||
| <p>{t("patterns.messageText")}</p> | |||
| </div> | |||
| <div className="pattern-details-card-sub-card-message-pattern"> | |||
| {/* <textarea disabled value={pattern.message}></textarea> */} | |||
| @@ -179,7 +181,7 @@ const PatternDetailsPage = () => { | |||
| data-testid="ad-details-buttons-link" | |||
| onClick={() => history.push(PATTERNS_PAGE)} | |||
| > | |||
| Nazad na sve oglase | |||
| {t("ads.backToAds")} | |||
| </button> | |||
| <IconButton | |||
| disabled={emails && emails.length === 0} | |||
| @@ -195,7 +197,7 @@ const PatternDetailsPage = () => { | |||
| }} | |||
| src={sendMessage} | |||
| /> | |||
| PORUKU | |||
| {t("patterns.message")} | |||
| </IconButton> | |||
| </div> | |||
| </div> | |||
| @@ -19,6 +19,7 @@ import { updatePatternReq } from "../../store/actions/updatePattern/updatePatter | |||
| import PatternFilters from "../../components/Patterns/PatternFilters"; | |||
| import IconButton from "../../components/IconButton/IconButton"; | |||
| import { Editor } from "@tinymce/tinymce-react"; | |||
| import { useTranslation } from "react-i18next"; | |||
| const PatternsPage = ({ history }) => { | |||
| const theme = useTheme(); | |||
| @@ -36,6 +37,7 @@ const PatternsPage = ({ history }) => { | |||
| const dispatch = useDispatch(); | |||
| const addPatternEditor = useRef(); | |||
| const editPatternEditor = useRef(); | |||
| const { t } = useTranslation(); | |||
| useEffect(() => { | |||
| dispatch(setPatternsReq()); | |||
| @@ -149,10 +151,10 @@ const PatternsPage = ({ history }) => { | |||
| <img src={plusIcon} alt="plus" /> | |||
| </div> | |||
| <div className="add-pattern-modal-header-title-title"> | |||
| <p>Dodavanje</p> | |||
| <p>{t("ads.adding")}</p> | |||
| </div> | |||
| <div className="add-pattern-modal-header-title-sub"> | |||
| <sub> | Šablon</sub> | |||
| <sub> | {t("patterns.pattern")}</sub> | |||
| </div> | |||
| </div> | |||
| <div | |||
| @@ -164,7 +166,7 @@ const PatternsPage = ({ history }) => { | |||
| </div> | |||
| <form onSubmit={submitAddPatternHandler}> | |||
| <div className="add-pattern-modal-form-control"> | |||
| <label>Naslov</label> | |||
| <label>{t("common.title")}</label> | |||
| <input | |||
| type="text" | |||
| placeholder="ex. Datum HR intervjua" | |||
| @@ -173,7 +175,7 @@ const PatternsPage = ({ history }) => { | |||
| /> | |||
| </div> | |||
| <div className="add-pattern-modal-form-control"> | |||
| <label>Kategorija</label> | |||
| <label>{t("filters.category")}</label> | |||
| <select | |||
| name="add-pattern-category" | |||
| value={addPatternCategory} | |||
| @@ -188,7 +190,7 @@ const PatternsPage = ({ history }) => { | |||
| </select> | |||
| </div> | |||
| <div className="add-pattern-modal-form-control"> | |||
| <label>Tekst poruke</label> | |||
| <label>{t("patterns.messageText")}</label> | |||
| <Editor | |||
| onInit={(evt, editor) => (addPatternEditor.current = editor)} | |||
| style={{ height: "100px !important" }} | |||
| @@ -210,10 +212,10 @@ const PatternsPage = ({ history }) => { | |||
| <img src={userPageBtnIcon} alt="plus" /> | |||
| </div> | |||
| <div className="edit-pattern-modal-header-title-title"> | |||
| <p>Uređivanje</p> | |||
| <p>{t("patterns.editing")}</p> | |||
| </div> | |||
| <div className="edit-pattern-modal-header-title-sub"> | |||
| <sub> | Šablon</sub> | |||
| <sub> | {t("patterns.pattern")}</sub> | |||
| </div> | |||
| </div> | |||
| <div | |||
| @@ -225,7 +227,7 @@ const PatternsPage = ({ history }) => { | |||
| </div> | |||
| <form onSubmit={submitEditPatternHandler}> | |||
| <div className="edit-pattern-modal-form-control"> | |||
| <label>Naslov</label> | |||
| <label>{t("common.title")}</label> | |||
| <input | |||
| type="text" | |||
| placeholder="ex. Datum HR intervjua" | |||
| @@ -239,7 +241,7 @@ const PatternsPage = ({ history }) => { | |||
| /> | |||
| </div> | |||
| <div className="edit-pattern-modal-form-control"> | |||
| <label>Kategorija</label> | |||
| <label>{t("filters.category")}</label> | |||
| <select | |||
| name="edit-pattern-category" | |||
| value={editPattern ? editPattern.selectionLevelId : 1} | |||
| @@ -259,7 +261,7 @@ const PatternsPage = ({ history }) => { | |||
| </select> | |||
| </div> | |||
| <div className="edit-pattern-modal-form-control"> | |||
| <label>Tekst poruke</label> | |||
| <label>{t("patterns.messageText")}</label> | |||
| <Editor | |||
| initialValue={editPattern ? editPattern.message : ""} | |||
| onInit={(evt, editor) => (editPatternEditor.current = editor)} | |||
| @@ -274,7 +276,7 @@ const PatternsPage = ({ history }) => { | |||
| <div className="patterns"> | |||
| <div className="patterns-header"> | |||
| <div> | |||
| <h1>Napravljeni Šabloni</h1> | |||
| <h1>{t("patterns.patternsMade")}</h1> | |||
| </div> | |||
| <div style={{ display: "flex" }}> | |||
| <IconButton | |||
| @@ -283,7 +285,7 @@ const PatternsPage = ({ history }) => { | |||
| isShownEdit && "pattern-header-active-button" | |||
| }`} | |||
| > | |||
| {!matches && "Režim uređivanja"} | |||
| {!matches && t("patterns.editing2")} | |||
| <img | |||
| style={{ | |||
| position: "relative", | |||
| @@ -304,7 +306,7 @@ const PatternsPage = ({ history }) => { | |||
| )} | |||
| {patterns && patterns.length === 0 && ( | |||
| <div> | |||
| <p>Trenutno nema dodatih sablona</p> | |||
| <p>{t("patterns.noPatterns")}</p> | |||
| </div> | |||
| )} | |||
| {patterns && | |||
| @@ -331,7 +333,7 @@ const PatternsPage = ({ history }) => { | |||
| className="c-btn c-btn--primary add-ad-btn add-pattern-btn" | |||
| onClick={() => setOpenAddPatternModal(true)} | |||
| > | |||
| Dodaj Šablon | |||
| {t("patterns.addPattern")} | |||
| </IconButton> | |||
| </div> | |||
| </div> | |||
| @@ -119,7 +119,7 @@ const RegisterPage = ({ history }) => { | |||
| {t("login.welcome")} | |||
| </Typography> | |||
| <Typography variant="p" sx={{ mb: 2 }}> | |||
| Dva koraka do HR Centra. | |||
| {t("registration.twoSteps")} | |||
| </Typography> | |||
| <div className="steps-cont"> | |||
| <div | |||
| @@ -145,7 +145,7 @@ const RegisterPage = ({ history }) => { | |||
| <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> | |||
| </Container> | |||
| @@ -12,6 +12,7 @@ import { | |||
| getFormatedDayOrMonth, | |||
| } from "../../util/helpers/dateHelpers"; | |||
| import PropTypes from "prop-types"; | |||
| import { useTranslation } from "react-i18next"; | |||
| const SchedulePage = ({ history }) => { | |||
| const dispatch = useDispatch(); | |||
| @@ -24,6 +25,7 @@ const SchedulePage = ({ history }) => { | |||
| ); | |||
| const [currentlySelectedDate, setCurrentlySelectedDate] = useState(""); | |||
| const [currentlySelectedDay, setCurrentlySelectedDay] = useState(0); | |||
| const { t } = useTranslation(); | |||
| useEffect(() => { | |||
| dispatch(fetchSchedule({ month: currentMonth + 1, year: currentYear })); | |||
| @@ -130,7 +132,7 @@ const SchedulePage = ({ history }) => { | |||
| history={history} | |||
| /> | |||
| <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"> | |||
| | {getMonthString(currentMonth)} {currentYear} | |||
| </p> | |||
| @@ -105,7 +105,7 @@ const SelectionProcessOfApplicantPage = () => { | |||
| <div className="l-t-rectangle"></div> | |||
| <div className="r-b-rectangle"></div> | |||
| {/* <AdFilters /> */} | |||
| <div data-testid='appl-sel' className="ads"> | |||
| <div data-testid="appl-sel" className="ads"> | |||
| {processes && processes.length > 0 && ( | |||
| <div className="active-ads"> | |||
| <div className="active-ads-header"> | |||
| @@ -147,7 +147,7 @@ const SelectionProcessOfApplicantPage = () => { | |||
| schedguler={ | |||
| process?.scheduler | |||
| ? `${process?.scheduler?.firstName} ${process?.scheduler?.lastName}` | |||
| : "Proces nema intervjuera." | |||
| : t("selection.noInterviewer") | |||
| } | |||
| link={process.link} | |||
| date={new Date(process.date)} | |||
| @@ -181,7 +181,7 @@ const SelectionProcessOfApplicantPage = () => { | |||
| {applicant?.selectionProcesses?.some( | |||
| (n) => n.status === "Neuspešno" | |||
| ) | |||
| ? "Komentar:" | |||
| ? t("candidates.comment") + ":" | |||
| : t("selection.tipHeader")} | |||
| </h3> | |||
| <p> | |||
| @@ -201,7 +201,7 @@ const SelectionProcessOfApplicantPage = () => { | |||
| </div> | |||
| <div className="add-ad"> | |||
| <Link className="ad-details-buttons-link" to="/selectionFlow"> | |||
| Nazad na sve kandidate | |||
| {t("candidates.backToCandidates")} | |||
| </Link> | |||
| </div> | |||
| </> | |||
| @@ -3,7 +3,6 @@ import { useSelector } from "react-redux"; | |||
| import Selection from "../../components/Selection/Selection"; | |||
| import FilterButton from "../../components/Button/FilterButton"; | |||
| import { useTranslation } from "react-i18next"; | |||
| import AddAdModal from "../../components/Ads/AddAdModal"; | |||
| import { useDispatch } from "react-redux"; | |||
| import { setDoneProcess } from "../../store/actions/processes/processAction"; | |||
| import { setProcessesReq } from "../../store/actions/processes/processesAction"; | |||
| @@ -27,7 +26,6 @@ import CommentProcessDialog from "../../components/MUI/CommentProcessDialog"; | |||
| const SelectionProcessPage = ({ history }) => { | |||
| const [toggleFiltersDrawer, setToggleFiltersDrawer] = useState(false); | |||
| const [toggleModal, setToggleModal] = useState(false); | |||
| const [toggleInitModal, setToggleInitModal] = useState(false); | |||
| // const errorMessage = useSelector(selectProcessesError); | |||
| const process = useSelector(selectDoneProcess); | |||
| @@ -73,10 +71,6 @@ const SelectionProcessPage = ({ history }) => { | |||
| setToggleFiltersDrawer((oldState) => !oldState); | |||
| }; | |||
| const handleToggleModal = () => { | |||
| setToggleModal((oldState) => !oldState); | |||
| }; | |||
| const renderList = processes?.map((item, index) => { | |||
| return ( | |||
| <Selection | |||
| @@ -104,8 +98,8 @@ const SelectionProcessPage = ({ history }) => { | |||
| /> | |||
| <StatusDialog | |||
| open={activeProcess !== null} | |||
| title={"Dodavanje intervjuera"} | |||
| subtitle={"Selekcija"} | |||
| title={t("selection.addInterviewer")} | |||
| subtitle={t("selection.selection")} | |||
| imgSrc={plus} | |||
| onClose={() => { | |||
| setActiveProcess(null); | |||
| @@ -113,8 +107,8 @@ const SelectionProcessPage = ({ history }) => { | |||
| /> | |||
| <CommentProcessDialog | |||
| open={activeProcessUnsuccess !== null} | |||
| title={"Komentar"} | |||
| subtitle={"Selekcija"} | |||
| title={t("candidates.comment")} | |||
| subtitle={t("selection.selection")} | |||
| imgSrc={forbiden} | |||
| onClose={() => { | |||
| setActiveProcessUnsuccess(null); | |||
| @@ -122,8 +116,8 @@ const SelectionProcessPage = ({ history }) => { | |||
| /> | |||
| <InterviewerDialog | |||
| open={activeInterview !== null} | |||
| title={"Promena intervjuera"} | |||
| subtitle={"Selekcija"} | |||
| title={t("selection.changeInterviewer")} | |||
| subtitle={t("selection.selection")} | |||
| imgSrc={plus} | |||
| onClose={() => { | |||
| setActiveInterview(null); | |||
| @@ -131,14 +125,13 @@ const SelectionProcessPage = ({ history }) => { | |||
| /> | |||
| <InterviewDialog | |||
| open={toggleInitModal} | |||
| title={"Dodavanje kandidata"} | |||
| subtitle={"HR intervju"} | |||
| title={t("selection.addCandidate")} | |||
| subtitle={t("selection.interview")} | |||
| imgSrc={plus} | |||
| onClose={() => { | |||
| setToggleInitModal(false); | |||
| }} | |||
| /> | |||
| <AddAdModal open={toggleModal} handleClose={handleToggleModal} /> | |||
| <div className="selections"> | |||
| <div className="level-header"> | |||
| <h1> | |||
| @@ -17,7 +17,7 @@ import { returni18nLevel } from "../../util/helpers/stringHelpers"; | |||
| // import ArchiveAd from "../../components/Ads/ArchiveAd"; | |||
| // import { AD_DETAILS_PAGE } from "../../constants/pages"; | |||
| const StatsPage = ({history}) => { | |||
| const StatsPage = ({ history }) => { | |||
| const dispatch = useDispatch(); | |||
| const { stats } = useSelector((s) => s.stats); | |||
| @@ -80,18 +80,24 @@ const StatsPage = ({history}) => { | |||
| style={{ letterSpacing: "1px" }} | |||
| className="page-heading px36-heading" | |||
| > | |||
| Statistika | |||
| {t("stats.statistic")} | |||
| </h1> | |||
| <div className="stats-section"> | |||
| <h2 className="section-header">Tok Selekcije</h2> | |||
| <h2 className="section-header">{t("stats.selectionFlow")}</h2> | |||
| <div className="stats-items"> | |||
| {stats.levels && | |||
| 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"> | |||
| <h3>{n.countDone}</h3> | |||
| <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> | |||
| </div> | |||
| <div className="bottom-static"></div> | |||
| @@ -100,11 +106,15 @@ const StatsPage = ({history}) => { | |||
| </div> | |||
| </div> | |||
| <div className="stats-section"> | |||
| <h2 className="section-header">Odnosi</h2> | |||
| <h2 className="section-header">{t("stats.relations")}</h2> | |||
| <div className="stats-items-dynamic"> | |||
| {stats.levels && | |||
| 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"> | |||
| <h3> | |||
| {n.countDone} | |||
| @@ -112,13 +122,17 @@ const StatsPage = ({history}) => { | |||
| {n.countAll} | |||
| </h3> | |||
| <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> | |||
| </div> | |||
| <div className="bottom-dynamic"> | |||
| <div | |||
| style={{ width: `${(n.countDone * 1.0 / n.countAll) * 100}%` }} | |||
| style={{ | |||
| width: `${((n.countDone * 1.0) / n.countAll) * 100}%`, | |||
| }} | |||
| className="bottom-loader-indicator" | |||
| ></div> | |||
| </div> | |||
| @@ -127,10 +141,10 @@ const StatsPage = ({history}) => { | |||
| </div> | |||
| </div> | |||
| <div className="stats-section"> | |||
| <h2 className="section-header">Prijavljeni po pozicijama</h2> | |||
| <h2 className="section-header">{t("stats.registered")}</h2> | |||
| </div> | |||
| <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-header"> | |||
| <h2>{t("ads.archiveAds")}</h2> | |||
| @@ -143,7 +157,10 @@ const StatsPage = ({history}) => { | |||
| <img src={arrow_left} alt="arrow-left" /> | |||
| </button> | |||
| {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" /> | |||
| </button> | |||
| )} | |||
| @@ -160,7 +177,7 @@ const StatsPage = ({history}) => { | |||
| > | |||
| {stats.ads.map((ad, index) => ( | |||
| <StatsAd | |||
| count={ad.count} | |||
| count={ad.count} | |||
| key={index} | |||
| title={ad.title} | |||
| minimumExperience={ad.minimumExperience} | |||
| @@ -182,7 +199,7 @@ const StatsPage = ({history}) => { | |||
| <img src={arrow_left} alt="arrow-left" /> | |||
| </button> | |||
| {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" /> | |||
| </button> | |||
| )} | |||
| @@ -18,6 +18,7 @@ import { useEffect } from "react"; | |||
| import { useTheme } from "@emotion/react"; | |||
| import { useMediaQuery } from "@mui/material"; | |||
| import ConfirmDialog from "../../components/MUI/ConfirmDialog"; | |||
| import { useTranslation } from "react-i18next"; | |||
| const UserDetails = ({ history }) => { | |||
| const theme = useTheme(); | |||
| @@ -27,8 +28,9 @@ const UserDetails = ({ history }) => { | |||
| const [showConfirm, setConfirm] = useState(false); | |||
| const [showReset, setReset] = 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) => { | |||
| dispatch( | |||
| @@ -84,10 +86,10 @@ const UserDetails = ({ history }) => { | |||
| <div className="r-b-rectangle"></div> | |||
| <ConfirmDialog | |||
| open={showReset} | |||
| title={"Reset password"} | |||
| title={t("users.resetPassword")} | |||
| subtitle={user?.firstName + " " + user?.lastName} | |||
| imgSrc={lock} | |||
| content="Are you sure you want to send password reset link?" | |||
| content={t("users.resetLink")} | |||
| onClose={() => { | |||
| setReset(false); | |||
| }} | |||
| @@ -98,10 +100,10 @@ const UserDetails = ({ history }) => { | |||
| /> | |||
| <ConfirmDialog | |||
| open={showConfirm} | |||
| title={"Disable user"} | |||
| title={t("users.disableUser")} | |||
| subtitle={user?.firstName + " " + user?.lastName} | |||
| imgSrc={forbiden} | |||
| content="Are you sure you want to disable user?" | |||
| content={t("users.questionDisableUser")} | |||
| onClose={() => { | |||
| setConfirm(false); | |||
| }} | |||
| @@ -112,10 +114,10 @@ const UserDetails = ({ history }) => { | |||
| /> | |||
| <ConfirmDialog | |||
| open={showDelete} | |||
| title={"Delete user"} | |||
| title={t("users.deleteUser")} | |||
| subtitle={user?.firstName + " " + user?.lastName} | |||
| imgSrc={filters} | |||
| content="Are you sure you want to delete user?" | |||
| content={t("users.questionDeleteUser")} | |||
| onClose={() => { | |||
| setDelete(false); | |||
| }} | |||
| @@ -125,156 +127,173 @@ const UserDetails = ({ history }) => { | |||
| }} | |||
| /> | |||
| <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={{ | |||
| 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={{ | |||
| letterSpacing: "0.75px", | |||
| 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 | |||
| 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> | |||
| ); | |||
| @@ -180,10 +180,10 @@ const UsersPage = (props) => { | |||
| </div> | |||
| <ConfirmDialog | |||
| open={showConfirm} | |||
| title={"Disable user"} | |||
| title={t("users.disableUser")} | |||
| subtitle={chosen?.firstName + " " + chosen?.lastName} | |||
| imgSrc={forbiden} | |||
| content="Are you sure you want to disable user?" | |||
| content={t("users.questionDisableUser")} | |||
| onClose={() => { | |||
| setConfirm(false); | |||
| }} | |||
| @@ -193,10 +193,10 @@ const UsersPage = (props) => { | |||
| /> | |||
| <ConfirmDialog | |||
| open={showReset} | |||
| title={"Reset password"} | |||
| title={t("users.resetPassword")} | |||
| subtitle={chosen?.firstName + " " + chosen?.lastName} | |||
| imgSrc={lock} | |||
| content="Are you sure you want to send password reset link?" | |||
| content={t("users.resetLink")} | |||
| onClose={() => { | |||
| setReset(false); | |||
| }} | |||
| @@ -266,7 +266,7 @@ const UsersPage = (props) => { | |||
| } | |||
| onClick={handleChangeVisibility} | |||
| > | |||
| {!matches && "Pretraga"} | |||
| {!matches && t("candidates.search")} | |||
| <img | |||
| style={{ | |||