| @@ -6426,13 +6426,19 @@ | |||
| } | |||
| }, | |||
| "node_modules/caniuse-lite": { | |||
| "version": "1.0.30001234", | |||
| "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001234.tgz", | |||
| "integrity": "sha512-a3gjUVKkmwLdNysa1xkUAwN2VfJUJyVW47rsi3aCbkRCtbHAfo+rOsCqVw29G6coQ8gzAPb5XBXwiGHwme3isA==", | |||
| "funding": { | |||
| "type": "opencollective", | |||
| "url": "https://opencollective.com/browserslist" | |||
| } | |||
| "version": "1.0.30001439", | |||
| "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001439.tgz", | |||
| "integrity": "sha512-1MgUzEkoMO6gKfXflStpYgZDlFM7M/ck/bgfVCACO5vnAf0fXoNVHdWtqGU+MYca+4bL9Z5bpOVmR33cWW9G2A==", | |||
| "funding": [ | |||
| { | |||
| "type": "opencollective", | |||
| "url": "https://opencollective.com/browserslist" | |||
| }, | |||
| { | |||
| "type": "tidelift", | |||
| "url": "https://tidelift.com/funding/github/npm/caniuse-lite" | |||
| } | |||
| ] | |||
| }, | |||
| "node_modules/capture-exit": { | |||
| "version": "2.0.0", | |||
| @@ -31741,9 +31747,9 @@ | |||
| } | |||
| }, | |||
| "caniuse-lite": { | |||
| "version": "1.0.30001234", | |||
| "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001234.tgz", | |||
| "integrity": "sha512-a3gjUVKkmwLdNysa1xkUAwN2VfJUJyVW47rsi3aCbkRCtbHAfo+rOsCqVw29G6coQ8gzAPb5XBXwiGHwme3isA==" | |||
| "version": "1.0.30001439", | |||
| "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001439.tgz", | |||
| "integrity": "sha512-1MgUzEkoMO6gKfXflStpYgZDlFM7M/ck/bgfVCACO5vnAf0fXoNVHdWtqGU+MYca+4bL9Z5bpOVmR33cWW9G2A==" | |||
| }, | |||
| "capture-exit": { | |||
| "version": "2.0.0", | |||
| @@ -9,10 +9,11 @@ import { runSaga } from "redux-saga"; | |||
| import { ADS_CANDIDATES_FETCH } from "../../store/actions/candidates/candidatesActionConstants"; | |||
| import { getAdsCandidates } from "../../store/saga/candidatesSaga"; | |||
| import { | |||
| fetchAdsCandidates, | |||
| fetchAdsCandidatesError, | |||
| fetchAdsCandidatesSuccess, | |||
| } from "../../store/actions/candidates/candidatesActions"; | |||
| import AdsCandidatesPage from "../../pages/CandidatesPage/AdsCandidatesPage"; | |||
| import * as helper from "../../util/helpers/rejectErrorCodeHelper"; | |||
| describe("AdsCandidatesPage render tests", () => { | |||
| const cont = ( | |||
| @@ -75,31 +76,33 @@ describe("AdsCandidatesPage render tests", () => { | |||
| await runSaga(fakeStore, getAdsCandidates, {}).done; | |||
| expect(api.getFilteredAdsCandidates.mock.calls.length).toBe(1); | |||
| expect(dispatchedActions[0].payload).toBe( | |||
| fetchAdsCandidates(mockedCall.data).payload | |||
| expect(dispatchedActions).toContainEqual( | |||
| fetchAdsCandidatesSuccess(mockedCall.data) | |||
| ); | |||
| }); | |||
| // it("should handle adsCandidates load errors in case of failure", async () => { | |||
| // const dispatchedActions = []; | |||
| it("should handle adsCandidates load errors in case of failure", async () => { | |||
| const dispatchedActions = []; | |||
| helper.rejectErrorCodeHelper = jest.fn(() => mockState.technologies.fetchTecnologiesErrorMessage); | |||
| // const error = { | |||
| // response: { | |||
| // data: { message: mockState.candidates.fetchCandidatesErrorMessage }, | |||
| // }, | |||
| // }; | |||
| // api.getFilteredAdsCandidates = jest.fn(() => Promise.reject(error)); | |||
| const error = { | |||
| response: { | |||
| data: { message: mockState.technologies.fetchTecnologiesErrorMessage }, | |||
| }, | |||
| }; | |||
| api.getFilteredAdsCandidates = jest.fn(() => Promise.reject(error)); | |||
| // const fakeStore = { | |||
| // getState: () => mockState.candidates.adsCandidates, | |||
| // dispatch: (action) => dispatchedActions.push(action), | |||
| // }; | |||
| const fakeStore = { | |||
| getState: () => mockState.candidates.adsCandidates, | |||
| dispatch: (action) => dispatchedActions.push(action), | |||
| }; | |||
| // await runSaga(fakeStore, getAdsCandidates,{}).done; | |||
| await runSaga(fakeStore, getAdsCandidates, {}).done; | |||
| // expect(api.getFilteredAdsCandidates.mock.calls.length).toBe(1); | |||
| // expect(dispatchedActions).toContainEqual( | |||
| // fetchAdsCandidates(error.response.data.message) | |||
| // ); | |||
| // }); | |||
| expect(api.getFilteredAdsCandidates.mock.calls.length).toBe(1); | |||
| expect(dispatchedActions).toContainEqual( | |||
| fetchAdsCandidatesError(error.response.data.message) | |||
| ); | |||
| }); | |||
| }); | |||
| @@ -13,6 +13,7 @@ import { | |||
| setTechnologies, | |||
| setTechnologiesError, | |||
| } from "../../store/actions/technologies/technologiesActions"; | |||
| import * as helper from "../../util/helpers/rejectErrorCodeHelper"; | |||
| describe("CandidatesPage render tests", () => { | |||
| const cont = ( | |||
| @@ -53,6 +54,8 @@ describe("CandidatesPage render tests", () => { | |||
| it("should load and handle techonologies in case of success", async () => { | |||
| const dispatchedActions = []; | |||
| helper.rejectErrorCodeHelper = jest.fn(()=> 'Server error') | |||
| const mockedCall = { data: mockState.technologies.technologies }; | |||
| api.getAllTechnologies = jest.fn(() => Promise.resolve(mockedCall)); | |||
| @@ -66,26 +69,28 @@ describe("CandidatesPage render tests", () => { | |||
| expect(dispatchedActions).toContainEqual(setTechnologies(mockedCall.data)); | |||
| }); | |||
| // it("should handle technologies load errors in case of failure", async () => { | |||
| // const dispatchedActions = []; | |||
| it("should handle technologies load errors in case of failure", async () => { | |||
| const dispatchedActions = []; | |||
| // const error = { | |||
| // response: { | |||
| // data: { message: mockState.technologies.fetchTecnologiesErrorMessage }, | |||
| // }, | |||
| // }; | |||
| // api.getAllTechnologies = jest.fn(() => Promise.reject(error)); | |||
| helper.rejectErrorCodeHelper = jest.fn(() => mockState.candidates.fetchCandidatesErrorMessage); | |||
| // const fakeStore = { | |||
| // getState: () => mockState.technologies.technologies, | |||
| // dispatch: (action) => dispatchedActions.push(action), | |||
| // }; | |||
| const error = { | |||
| response: { | |||
| data: { message: mockState.candidates.fetchCandidatesErrorMessage }, | |||
| }, | |||
| }; | |||
| api.getAllTechnologies = jest.fn(() => Promise.reject(error)); | |||
| // await runSaga(fakeStore, getTechnologies).done; | |||
| const fakeStore = { | |||
| getState: () => mockState.technologies.technologies, | |||
| dispatch: (action) => dispatchedActions.push(action), | |||
| }; | |||
| await runSaga(fakeStore, getTechnologies).done; | |||
| // expect(api.getAllTechnologies.mock.calls.length).toBe(1); | |||
| // expect(dispatchedActions).toContainEqual( | |||
| // setTechnologiesError(error.response.data.message) | |||
| // ); | |||
| // }); | |||
| expect(api.getAllTechnologies.mock.calls.length).toBe(1); | |||
| expect(dispatchedActions).toContainEqual( | |||
| setTechnologiesError(error.response.data.message) | |||
| ); | |||
| }); | |||
| }); | |||
| @@ -0,0 +1,111 @@ | |||
| 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 { render } from "@testing-library/react"; | |||
| import * as api from "../../request/scheduleRequest"; | |||
| import { runSaga } from "redux-saga"; | |||
| import { SCHEDULE_FETCH } from "../../store/actions/schedule/scheduleActionConstants"; | |||
| import { getSchedule } from "../../store/saga/scheduleSaga"; | |||
| import { | |||
| fetchScheduleSuccess, | |||
| fetchScheduleError, | |||
| } from "../../store/actions/schedule/scheduleActions"; | |||
| import SchedulePage from "../../pages/SchedulePage/SchedulePage"; | |||
| import ColorModeProvider from "../../context/ColorModeContext"; | |||
| import * as helper from "../../util/helpers/rejectErrorCodeHelper"; | |||
| describe("SchedulePage render tests", () => { | |||
| const cont = ( | |||
| <ColorModeProvider> | |||
| <redux.Provider store={store}> | |||
| <Router history={history}> | |||
| <SchedulePage /> | |||
| </Router> | |||
| </redux.Provider> | |||
| </ColorModeProvider> | |||
| ); | |||
| let spyOnUseSelector; | |||
| let spyOnUseDispatch; | |||
| let mockDispatch; | |||
| beforeEach(() => { | |||
| spyOnUseSelector = jest.spyOn(redux, "useSelector"); | |||
| spyOnUseSelector.mockReturnValueOnce(mockState.schedule.schedule); | |||
| spyOnUseDispatch = jest.spyOn(redux, "useDispatch"); | |||
| mockDispatch = jest.fn(); | |||
| spyOnUseDispatch.mockReturnValue(mockDispatch); | |||
| }); | |||
| afterEach(() => { | |||
| jest.restoreAllMocks(); | |||
| }); | |||
| it("Should dispatch get schedule request when rendered", () => { | |||
| render(cont); | |||
| expect(mockDispatch).toHaveBeenCalledWith({ | |||
| type: SCHEDULE_FETCH, | |||
| payload: { | |||
| month: 12, | |||
| year: 2022, | |||
| }, | |||
| }); | |||
| }); | |||
| it("should load and handle schedule in case of success", async () => { | |||
| const dispatchedActions = []; | |||
| const mockedCall = { data: mockState.schedule.schedule }; | |||
| api.getSpecificSchedule = jest.fn(() => Promise.resolve(mockedCall)); | |||
| const fakeStore = { | |||
| getState: () => mockState.schedule.schedule, | |||
| dispatch: (action) => dispatchedActions.push(action), | |||
| }; | |||
| await runSaga(fakeStore, getSchedule, { | |||
| payload: { | |||
| month: 12, | |||
| year: 2022, | |||
| }, | |||
| }).done; | |||
| expect(api.getSpecificSchedule.mock.calls.length).toBe(1); | |||
| expect(dispatchedActions).toContainEqual(fetchScheduleSuccess(mockedCall.data)); | |||
| }); | |||
| it("should handle candidate load errors in case of failure", async () => { | |||
| const dispatchedActions = []; | |||
| helper.rejectErrorCodeHelper = jest.fn( | |||
| () => mockState.schedule.fetchScheduleErrorMessage | |||
| ); | |||
| const error = { | |||
| response: { | |||
| data: { message: mockState.schedule.fetchScheduleErrorMessage }, | |||
| }, | |||
| }; | |||
| api.getSpecificSchedule = jest.fn(() => Promise.reject(error)); | |||
| const fakeStore = { | |||
| getState: () => mockState.schedule.schedule, | |||
| dispatch: (action) => dispatchedActions.push(action), | |||
| }; | |||
| await runSaga(fakeStore, getSchedule, { | |||
| payload: { | |||
| month: 12, | |||
| year: 2022, | |||
| }, | |||
| }).done; | |||
| expect(api.getSpecificSchedule.mock.calls.length).toBe(1); | |||
| expect(dispatchedActions).toContainEqual( | |||
| fetchScheduleError(error.response.data.message) | |||
| ); | |||
| }); | |||
| }); | |||
| @@ -19,7 +19,7 @@ describe("CandidatesPage render tests", () => { | |||
| beforeEach(() => { | |||
| spyOnUseSelector = jest.spyOn(redux, "useSelector"); | |||
| spyOnUseSelector.mockReturnValueOnce(mockState.technologies.technologies); | |||
| spyOnUseSelector.mockReturnValueOnce(mockState.technologies.technologies) | |||
| }); | |||
| afterEach(() => { | |||
| @@ -53,11 +53,13 @@ describe("CandidatesPage render tests", () => { | |||
| ); | |||
| }); | |||
| // it("input for searching by name should be shown after clicking button for first time",() => { | |||
| // it("input for searching by name should be shown after clicking button for first time", () => { | |||
| // const { container } = render(cont); | |||
| // fireEvent.click(container.getElementsByClassName("candidate-btn")[1]) | |||
| // expect(container.getElementsByClassName("proba")[0].style.visibility).toBe('vissible'); | |||
| // }) | |||
| // fireEvent.click(container.getElementsByClassName("candidate-btn")[1]); | |||
| // expect(container.getElementsByClassName("proba")[0].style.visibility).toBe( | |||
| // "vissible" | |||
| // ); | |||
| // }); | |||
| it("Should render TableViewPage component when page is initialy rendered", () => { | |||
| const { container } = render(cont); | |||
| @@ -66,9 +68,11 @@ describe("CandidatesPage render tests", () => { | |||
| ).toBeDefined(); | |||
| }); | |||
| // it("should render AdsCandidatesPage component when button for switching to another view is clicked for the first time",() => { | |||
| // const { container } = render(cont); | |||
| // fireEvent.click(container.getElementsByClassName("candidate-btn")[0]) | |||
| // expect(container.getElementsByClassName("ads-candidates-container")[0]).toBeDefined(); | |||
| // }) | |||
| // it("should render AdsCandidatesPage component when button for switching to another view is clicked for the first time", () => { | |||
| // const { container } = render(cont); | |||
| // fireEvent.click(container.getElementsByClassName("candidate-btn")[0]); | |||
| // expect( | |||
| // container.getElementsByClassName("ads-candidates-container")[0] | |||
| // ).toBeDefined(); | |||
| // }); | |||
| }); | |||
| @@ -0,0 +1,46 @@ | |||
| import * as redux from "react-redux"; | |||
| import store from "../../store"; | |||
| import { Router } from "react-router-dom"; | |||
| import { mockState } from "../../mockState"; | |||
| import { render } from "@testing-library/react"; | |||
| import DayComponent from "../../components/Schedules/DayComponent"; | |||
| import history from "../../store/utils/history"; | |||
| const props = { | |||
| numberOfDay: 1, | |||
| nameOfDay: "sre", | |||
| interviews: mockState.schedule.schedule, | |||
| onClick: jest.fn(), | |||
| }; | |||
| describe("DayComponent render tests", () => { | |||
| const cont = ( | |||
| <redux.Provider store={store}> | |||
| <Router history={history}> | |||
| <DayComponent {...props} /> | |||
| </Router> | |||
| </redux.Provider> | |||
| ); | |||
| afterEach(() => { | |||
| jest.restoreAllMocks(); | |||
| }); | |||
| it("Should render", () => { | |||
| const { container } = render(cont); | |||
| expect( | |||
| container.getElementsByClassName("day-component-container")[0] | |||
| ).toBeDefined(); | |||
| }); | |||
| it("Should show only two interviews even if there is more interviews and span element which will tell us how many interviews is there", () => { | |||
| const { container } = render(cont); | |||
| expect( | |||
| container.getElementsByClassName("day-component-more")[0] | |||
| ).toBeDefined(); | |||
| expect( | |||
| container.getElementsByClassName("day-component-interviews-container") | |||
| .length | |||
| ).toBe(2); | |||
| }); | |||
| }); | |||
| @@ -0,0 +1,47 @@ | |||
| import * as redux from "react-redux"; | |||
| import store from "../../store"; | |||
| import { Router } from "react-router-dom"; | |||
| import { mockState } from "../../mockState"; | |||
| import { render, screen } from "@testing-library/react"; | |||
| import history from "../../store/utils/history"; | |||
| import DayDetailsComponent from "../../components/Schedules/DayDetailsComponent"; | |||
| import ColorModeProvider from "../../context/ColorModeContext"; | |||
| const props = { | |||
| selectedDate: "20.12.2023", | |||
| selectionProcesses: mockState.schedule.schedule, | |||
| open: jest.fn(), | |||
| onClose: jest.fn(), | |||
| setCurrentlySelected: jest.fn(), | |||
| setCurrentlySelectedDay: jest.fn(), | |||
| currentlySelectedDay: 1, | |||
| numberOfDaysInMonth: 31, | |||
| }; | |||
| describe("DayDetailsComponent render tests", () => { | |||
| const cont = ( | |||
| <ColorModeProvider> | |||
| <redux.Provider store={store}> | |||
| <Router history={history}> | |||
| <DayDetailsComponent {...props} /> | |||
| </Router> | |||
| </redux.Provider> | |||
| </ColorModeProvider> | |||
| ); | |||
| afterEach(() => { | |||
| jest.restoreAllMocks(); | |||
| }); | |||
| it("Should render", () => { | |||
| render(cont); | |||
| expect(screen.getByTestId("day-component-dialog")).toBeDefined(); | |||
| }); | |||
| it("Should show all interviews which we pass to component", () => { | |||
| render(cont); | |||
| expect(screen.getAllByTestId("day-details-component-process").length).toBe( | |||
| mockState.schedule.schedule.length | |||
| ); | |||
| }); | |||
| }); | |||
| @@ -0,0 +1,109 @@ | |||
| import { render, fireEvent, screen } from "@testing-library/react"; | |||
| import * as redux from "react-redux"; | |||
| import store from "../../store"; | |||
| import { mockState } from "../../mockState"; | |||
| import { Router } from "react-router-dom"; | |||
| import history from "../../store/utils/history"; | |||
| import SchedulePage from "../../pages/SchedulePage/SchedulePage"; | |||
| import ColorModeProvider from "../../context/ColorModeContext"; | |||
| describe("SchedulePage render tests", () => { | |||
| const cont = ( | |||
| <ColorModeProvider> | |||
| <redux.Provider store={store}> | |||
| <Router history={history}> | |||
| <SchedulePage search="" /> | |||
| </Router> | |||
| </redux.Provider> | |||
| </ColorModeProvider> | |||
| ); | |||
| let spyOnUseSelector; | |||
| beforeEach(() => { | |||
| spyOnUseSelector = jest.spyOn(redux, "useSelector"); | |||
| spyOnUseSelector | |||
| .mockReturnValueOnce(mockState.schedule.schedule) | |||
| .mockReturnValueOnce(mockState.schedule.schedule); | |||
| }); | |||
| afterEach(() => { | |||
| jest.restoreAllMocks(); | |||
| }); | |||
| it("Should render", () => { | |||
| const { container } = render(cont); | |||
| expect( | |||
| container.getElementsByClassName("schedule-page-container")[0] | |||
| ).toBeDefined(); | |||
| }); | |||
| it("Number of div's should be equal to the number of days in current month", () => { | |||
| const { container } = render(cont); | |||
| const date = new Date(); | |||
| const numberOfDaysInCurrentMonth = new Date( | |||
| date.getFullYear(), | |||
| date.getMonth() + 1, | |||
| 0 | |||
| ).getDate(); | |||
| expect( | |||
| container.getElementsByClassName("day-component-container").length | |||
| ).toBe(numberOfDaysInCurrentMonth); | |||
| }); | |||
| it("should render two arrows", () => { | |||
| const { container } = render(cont); | |||
| expect( | |||
| container.getElementsByClassName("schedule-page-arrow-container").length | |||
| ).toBe(2); | |||
| }); | |||
| it("after clicking arrow for going back one month number of div's should be equal to number of days in new month", () => { | |||
| const { container } = render(cont); | |||
| fireEvent.click( | |||
| container.getElementsByClassName("schedule-page-arrow-container")[0] | |||
| ); | |||
| const date = new Date(); | |||
| const currentMonth = date.getMonth(); | |||
| const currentYear = date.getFullYear(); | |||
| const numberOfDaysInCurrentMonth = new Date( | |||
| currentMonth - 1 === -1 ? currentYear - 1 : currentYear, | |||
| currentMonth - 1 === -1 ? 12 : currentMonth, | |||
| 0 | |||
| ).getDate(); | |||
| expect( | |||
| container.getElementsByClassName("day-component-container").length | |||
| ).toBe(numberOfDaysInCurrentMonth); | |||
| }); | |||
| it("after clicking arrow for going forward one month number of div's should be equal to number of days in new month", () => { | |||
| const { container } = render(cont); | |||
| fireEvent.click( | |||
| container.getElementsByClassName("schedule-page-arrow-container")[1] | |||
| ); | |||
| const date = new Date(); | |||
| const currentMonth = date.getMonth(); | |||
| const currentYear = date.getFullYear(); | |||
| const numberOfDaysInCurrentMonth = new Date( | |||
| currentMonth + 1 === 12 ? currentYear + 1 : currentYear, | |||
| currentMonth + 1 === 12 ? 1 : currentMonth + 2, | |||
| 0 | |||
| ).getDate(); | |||
| expect( | |||
| container.getElementsByClassName("day-component-container").length | |||
| ).toBe(numberOfDaysInCurrentMonth); | |||
| }); | |||
| it("When page is initialy rendered DayDetailsComponent should not be rendered", () => { | |||
| render(cont); | |||
| expect(screen.queryByTestId("day-component-dialog")).toBeNull(); | |||
| }); | |||
| it("After clicking on some day DayDetailsComponent should be rendered", () => { | |||
| const { container } = render(cont); | |||
| fireEvent.click( | |||
| container.getElementsByClassName("day-component-container")[0] | |||
| ); | |||
| expect(screen.getByTestId("day-component-dialog")).toBeDefined(); | |||
| }); | |||
| }); | |||
| @@ -92,11 +92,11 @@ const CandidateFilters = ({ | |||
| const isThereSelectedFilter = () => { | |||
| let tech = technologies.filter(t => t.isChecked === true); | |||
| if(tech.length > 0){ | |||
| if(tech && tech.length > 0){ | |||
| return true; | |||
| } | |||
| let k = typesOfEmployments.filter(te => te.isChecked === true) | |||
| if(k.length > 0){ | |||
| if(k && k.length > 0){ | |||
| return true | |||
| } | |||
| @@ -125,7 +125,7 @@ const CandidateFilters = ({ | |||
| currentPage, | |||
| minExperience: sliderValue[0], | |||
| maxExperience: sliderValue[1], | |||
| employmentType:selectedEmploymentType.length === 0 ? "" : selectedEmploymentType[0].name, | |||
| employmentType:selectedEmploymentType && selectedEmploymentType.length === 0 ? "" : selectedEmploymentType[0].name, | |||
| minDateOfApplication: startingDate, | |||
| maxDateOfApplication: endingDate, | |||
| technologies: tech, | |||
| @@ -139,7 +139,7 @@ const CandidateFilters = ({ | |||
| currentPage, | |||
| minExperience: sliderValue[0], | |||
| maxExperience: sliderValue[1], | |||
| employmentType:selectedEmploymentType.length === 0 ? "" : selectedEmploymentType[0].name, | |||
| employmentType:selectedEmploymentType && selectedEmploymentType.length === 0 ? "" : selectedEmploymentType[0].name, | |||
| minDateOfApplication: startingDate, | |||
| maxDateOfApplication: endingDate, | |||
| technologies: tech, | |||
| @@ -2,25 +2,28 @@ import React from "react"; | |||
| import PropTypes from "prop-types"; | |||
| import { formatTimeSrb } from "../../util/helpers/dateHelpers"; | |||
| const DayComponent = ({ numberOfDay, nameOfDay, interviews,onClick }) => { | |||
| const interviewsToShow = interviews.slice(0, 2); | |||
| const DayComponent = ({ numberOfDay, nameOfDay, interviews, onClick }) => { | |||
| 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> | |||
| {interviewsToShow.map((interview, index) => ( | |||
| {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> | |||
| <p className="day-component-interviews-time"> | |||
| {formatTimeSrb(interview.date)} | |||
| </p> | |||
| <p className="day-component-interviews-name"> | |||
| {interview.selectionLevel.name} | |||
| </p> | |||
| </div> | |||
| ))} | |||
| {interviews.length > 2 && ( | |||
| {interviews && interviews.length > 2 && ( | |||
| <span className="day-component-more"> | |||
| +{interviews.length - 2} stavke | |||
| </span> | |||
| @@ -33,7 +36,7 @@ DayComponent.propTypes = { | |||
| numberOfDay: PropTypes.number, | |||
| nameOfDay: PropTypes.string, | |||
| interviews: PropTypes.array, | |||
| onClick:PropTypes.func | |||
| onClick: PropTypes.func, | |||
| }; | |||
| export default DayComponent; | |||
| @@ -73,7 +73,7 @@ const DayDetailsComponent = ({ | |||
| open={open} | |||
| maxWidth={!matches ? "549" : "339"} | |||
| > | |||
| <div className="day-details-sub-container"> | |||
| <div className="day-details-sub-container" data-testid="day-component-dialog"> | |||
| <DialogTitle className="day-datails-title-container"> | |||
| <img src={calendar} className="day-details-calendar-image" /> | |||
| <p className="day-details-main-header">Planer aktivnosti</p> | |||
| @@ -92,8 +92,8 @@ const DayDetailsComponent = ({ | |||
| }} | |||
| > | |||
| <div className="day-details-content-sub-container"> | |||
| {selectionProcesses.map((selectionProcess, index) => ( | |||
| <div key={index}> | |||
| {selectionProcesses && selectionProcesses.map((selectionProcess, index) => ( | |||
| <div key={index} data-testid="day-details-component-process"> | |||
| <div style={{ display: "flex", alignItems: "center" }}> | |||
| <p className="day-details-time"> | |||
| {formatTimeSrb(selectionProcess.date)}h | |||
| @@ -1,4 +1,61 @@ | |||
| export const mockState = { | |||
| schedule: { | |||
| schedule: [ | |||
| { | |||
| date: "2022-12-01T11:00:00", | |||
| link: "some link", | |||
| selectionLevel: { | |||
| id: 1, | |||
| name: "HR intervju", | |||
| }, | |||
| applicant: { | |||
| applicantId: 1, | |||
| firstName: "Dzenis", | |||
| lastName: "Hadzifejzovic", | |||
| }, | |||
| }, | |||
| { | |||
| date: "2022-12-13T01:10:00", | |||
| link: null, | |||
| selectionLevel: { | |||
| id: 1, | |||
| name: "HR intervju", | |||
| }, | |||
| applicant: { | |||
| applicantId: 25, | |||
| firstName: "Meris", | |||
| lastName: "Ahmatovic", | |||
| }, | |||
| }, | |||
| { | |||
| date: "2022-12-28T03:21:00", | |||
| link: null, | |||
| selectionLevel: { | |||
| id: 1, | |||
| name: "HR intervju", | |||
| }, | |||
| applicant: { | |||
| applicantId: 1, | |||
| firstName: "Dzenis", | |||
| lastName: "Hadzifejzovic", | |||
| }, | |||
| }, | |||
| { | |||
| date: "2022-12-28T03:10:00", | |||
| link: null, | |||
| selectionLevel: { | |||
| id: 1, | |||
| name: "HR intervju", | |||
| }, | |||
| applicant: { | |||
| applicantId: 25, | |||
| firstName: "Meris", | |||
| lastName: "Ahmatovic", | |||
| }, | |||
| }, | |||
| ], | |||
| fetchScheduleErrorMessage: "Server error", | |||
| }, | |||
| candidate: { | |||
| candidate: { | |||
| applicantId: 1, | |||
| @@ -8,12 +8,15 @@ import { filterCandidates } from "../../store/actions/candidates/candidatesActio | |||
| import { PAGE_SIZE_CANDIDATES } from "../../constants/keyCodeConstants"; | |||
| import { useTheme } from "@mui/system"; | |||
| import { useMediaQuery } from "@mui/material"; | |||
| import { selectCandidates,selectPagination } from "../../store/selectors/candidatesSelectors"; | |||
| import { | |||
| selectCandidates, | |||
| selectPagination, | |||
| } from "../../store/selectors/candidatesSelectors"; | |||
| const TableViewPage = ({ history, setPage, page, search }) => { | |||
| const dispatch = useDispatch(); | |||
| const candidates = useSelector(selectCandidates); | |||
| const pagination = useSelector(selectPagination); // pagination is total number of candidates on backend | |||
| const pagination = useSelector(selectPagination); // pagination is total number of candidates on backend | |||
| const theme = useTheme(); | |||
| const matches = useMediaQuery(theme.breakpoints.down("361")); | |||
| @@ -92,49 +95,52 @@ const TableViewPage = ({ history, setPage, page, search }) => { | |||
| </tr> | |||
| </thead> | |||
| <tbody> | |||
| {candidates | |||
| .filter((n) => | |||
| (n.firstName + " " + n.lastName) | |||
| .toLowerCase() | |||
| .includes(search.toLowerCase()) | |||
| ) | |||
| .map((candidate, index) => ( | |||
| <tr | |||
| key={index} | |||
| className="secondaryRow cadidate-row" | |||
| style={{ | |||
| width: "800px", | |||
| height: "40px", | |||
| borderRadius: "12px", | |||
| cursor: "pointer", | |||
| }} | |||
| onClick={() => navigate(candidate.applicantId)} | |||
| > | |||
| <td> | |||
| {(candidate.firstName + " " + candidate.lastName).includes( | |||
| search | |||
| ) ? ( | |||
| formatLabel( | |||
| candidate.firstName + " " + candidate.lastName, | |||
| search | |||
| ) | |||
| ) : ( | |||
| <span> | |||
| {candidate.firstName + " " + candidate.lastName} | |||
| </span> | |||
| )} | |||
| </td> | |||
| <td>{candidate.experience}</td> | |||
| <td>{formatDate(candidate.dateOfApplication)}</td> | |||
| <td>{candidate.position}</td> | |||
| <td> | |||
| <a href={candidate.CV} className="cvLink"> | |||
| {candidate.firstName} | |||
| {candidate.lastName}.pdf | |||
| </a> | |||
| </td> | |||
| </tr> | |||
| ))} | |||
| {candidates && | |||
| candidates | |||
| .filter((n) => | |||
| (n.firstName + " " + n.lastName) | |||
| .toLowerCase() | |||
| .includes(search.toLowerCase()) | |||
| ) | |||
| .map((candidate, index) => ( | |||
| <tr | |||
| key={index} | |||
| className="secondaryRow cadidate-row" | |||
| style={{ | |||
| width: "800px", | |||
| height: "40px", | |||
| borderRadius: "12px", | |||
| cursor: "pointer", | |||
| }} | |||
| onClick={() => navigate(candidate.applicantId)} | |||
| > | |||
| <td> | |||
| {( | |||
| candidate.firstName + | |||
| " " + | |||
| candidate.lastName | |||
| ).includes(search) ? ( | |||
| formatLabel( | |||
| candidate.firstName + " " + candidate.lastName, | |||
| search | |||
| ) | |||
| ) : ( | |||
| <span> | |||
| {candidate.firstName + " " + candidate.lastName} | |||
| </span> | |||
| )} | |||
| </td> | |||
| <td>{candidate.experience}</td> | |||
| <td>{formatDate(candidate.dateOfApplication)}</td> | |||
| <td>{candidate.position}</td> | |||
| <td> | |||
| <a href={candidate.CV} className="cvLink"> | |||
| {candidate.firstName} | |||
| {candidate.lastName}.pdf | |||
| </a> | |||
| </td> | |||
| </tr> | |||
| ))} | |||
| </tbody> | |||
| </table> | |||
| </div> | |||
| @@ -56,11 +56,14 @@ const SchedulePage = () => { | |||
| }; | |||
| const getSelectionProcessesForSpecificDay = (day) => { | |||
| return selectionProcesses.filter( | |||
| (k) => | |||
| new Date(k.date).getDate() === day && | |||
| new Date(k.date).getFullYear() === currentYear && | |||
| new Date(k.date).getMonth() === currentMonth | |||
| return ( | |||
| selectionProcesses && | |||
| selectionProcesses.filter( | |||
| (k) => | |||
| new Date(k.date).getDate() === day && | |||
| new Date(k.date).getFullYear() === currentYear && | |||
| new Date(k.date).getMonth() === currentMonth | |||
| ) | |||
| ); | |||
| }; | |||
| @@ -8,7 +8,7 @@ import { | |||
| deleteCandidate, | |||
| getFilteredCandidates, | |||
| getCandidateOptions, | |||
| initializeProcessRequest | |||
| initializeProcessRequest, | |||
| } from "../../request/candidatesRequest"; | |||
| import { authScopeStringGetHelper } from "../../util/helpers/authScopeHelpers"; | |||
| import { | |||
| @@ -25,7 +25,7 @@ import { | |||
| fetchCandidateOptionsSuccess, | |||
| fetchCandidateOptionsError, | |||
| fetchInitProcessSuccess, | |||
| fetchInitProcessError | |||
| fetchInitProcessError, | |||
| } from "../actions/candidates/candidatesActions"; | |||
| import { rejectErrorCodeHelper } from "../../util/helpers/rejectErrorCodeHelper"; | |||
| import { | |||
| @@ -42,19 +42,20 @@ import { | |||
| DELETE_CANDIDATE, | |||
| } from "../actions/candidate/candidateActionConstants"; | |||
| export function* filterCandidates({ payload }) { | |||
| try { | |||
| const JwtToken = yield call(authScopeStringGetHelper, JWT_TOKEN); | |||
| yield call(addHeaderToken, JwtToken); | |||
| const {data} = yield call(getFilteredCandidates,payload); | |||
| const { data } = yield call(getFilteredCandidates, payload); | |||
| yield put(filterCandidatesSuccess(data)); | |||
| if(payload.handleApiResponseSuccess){ | |||
| yield call(payload.handleApiResponseSuccess) | |||
| if (payload.handleApiResponseSuccess) { | |||
| yield call(payload.handleApiResponseSuccess); | |||
| } | |||
| } catch (error) { | |||
| const errorMessage = yield call(rejectErrorCodeHelper, error); | |||
| yield put(filterCandidatesError(errorMessage)); | |||
| if (error.response && error.response.data) { | |||
| const errorMessage = yield call(rejectErrorCodeHelper, error); | |||
| yield put(filterCandidatesError(errorMessage)); | |||
| } | |||
| } | |||
| } | |||
| @@ -65,8 +66,10 @@ export function* getSingleCandidate({ payload }) { | |||
| const { data } = yield call(getCandidate, payload.id); | |||
| yield put(fetchCandidateSuccess(data)); | |||
| } catch (error) { | |||
| const errorMessage = yield call(rejectErrorCodeHelper, error); | |||
| yield put(fetchCandidateError(errorMessage)); | |||
| if (error.response && error.response.data) { | |||
| const errorMessage = yield call(rejectErrorCodeHelper, error); | |||
| yield put(fetchCandidateError(errorMessage)); | |||
| } | |||
| } | |||
| } | |||
| @@ -78,23 +81,27 @@ export function* addComment(data) { | |||
| yield call(createComment, myObj); | |||
| yield put(createCandidateCommentSuccess({ user, myObj })); | |||
| } catch (error) { | |||
| const errorMessage = yield call(rejectErrorCodeHelper, error); | |||
| yield put(createCandidateCommentError(errorMessage)); | |||
| if (error.response && error.response.data) { | |||
| const errorMessage = yield call(rejectErrorCodeHelper, error); | |||
| yield put(createCandidateCommentError(errorMessage)); | |||
| } | |||
| } | |||
| } | |||
| export function* getAdsCandidates({payload}) { | |||
| export function* getAdsCandidates({ payload }) { | |||
| try { | |||
| const JwtToken = yield call(authScopeStringGetHelper, JWT_TOKEN); | |||
| yield call(addHeaderToken, JwtToken); | |||
| const { data } = yield call(getFilteredAdsCandidates,payload); | |||
| const { data } = yield call(getFilteredAdsCandidates, payload); | |||
| yield put(fetchAdsCandidatesSuccess(data)); | |||
| if(payload.handleApiResponseSuccess){ | |||
| yield call(payload.handleApiResponseSuccess) | |||
| if (payload.handleApiResponseSuccess) { | |||
| yield call(payload.handleApiResponseSuccess); | |||
| } | |||
| } catch (error) { | |||
| const errorMessage = yield call(rejectErrorCodeHelper, error); | |||
| yield put(fetchAdsCandidatesError(errorMessage)); | |||
| if (error.response && error.response.data) { | |||
| const errorMessage = yield call(rejectErrorCodeHelper, error); | |||
| yield put(fetchAdsCandidatesError(errorMessage)); | |||
| } | |||
| } | |||
| } | |||
| @@ -102,33 +109,39 @@ export function* deleteSingleCandidate({ payload }) { | |||
| // console.log(payload) | |||
| try { | |||
| yield call(deleteCandidate, payload.id); | |||
| if(payload.handleApiResponseSuccess){ | |||
| yield call(payload.handleApiResponseSuccess) | |||
| if (payload.handleApiResponseSuccess) { | |||
| yield call(payload.handleApiResponseSuccess); | |||
| } | |||
| yield put(deleteCandidateSuccess()); | |||
| } catch (error) { | |||
| const errorMessage = yield call(rejectErrorCodeHelper, error); | |||
| yield put(deleteCandidateError(errorMessage)); | |||
| if (error.response && error.response.data) { | |||
| const errorMessage = yield call(rejectErrorCodeHelper, error); | |||
| yield put(deleteCandidateError(errorMessage)); | |||
| } | |||
| } | |||
| } | |||
| export function* getOptions() { | |||
| try { | |||
| const { data } = yield call(getCandidateOptions); | |||
| const { data } = yield call(getCandidateOptions); | |||
| yield put(fetchCandidateOptionsSuccess(data)); | |||
| } catch (error) { | |||
| const errorMessage = yield call(rejectErrorCodeHelper, error); | |||
| yield put(fetchCandidateOptionsError(errorMessage)); | |||
| if (error.response && error.response.data) { | |||
| const errorMessage = yield call(rejectErrorCodeHelper, error); | |||
| yield put(fetchCandidateOptionsError(errorMessage)); | |||
| } | |||
| } | |||
| } | |||
| export function* initializeProcess({payload}) { | |||
| export function* initializeProcess({ payload }) { | |||
| try { | |||
| yield call(initializeProcessRequest, payload.model); | |||
| yield put(fetchInitProcessSuccess()); | |||
| } catch (error) { | |||
| const errorMessage = yield call(rejectErrorCodeHelper, error); | |||
| yield put(fetchInitProcessError(errorMessage)); | |||
| if (error.response && error.response.data) { | |||
| const errorMessage = yield call(rejectErrorCodeHelper, error); | |||
| yield put(fetchInitProcessError(errorMessage)); | |||
| } | |||
| } | |||
| } | |||
| @@ -3701,9 +3701,9 @@ | |||
| "lodash.uniq" "^4.5.0" | |||
| "caniuse-lite@^1.0.0", "caniuse-lite@^1.0.30000981", "caniuse-lite@^1.0.30001109", "caniuse-lite@^1.0.30001125", "caniuse-lite@^1.0.30001219": | |||
| "integrity" "sha512-a3gjUVKkmwLdNysa1xkUAwN2VfJUJyVW47rsi3aCbkRCtbHAfo+rOsCqVw29G6coQ8gzAPb5XBXwiGHwme3isA==" | |||
| "resolved" "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001234.tgz" | |||
| "version" "1.0.30001234" | |||
| "integrity" "sha512-1MgUzEkoMO6gKfXflStpYgZDlFM7M/ck/bgfVCACO5vnAf0fXoNVHdWtqGU+MYca+4bL9Z5bpOVmR33cWW9G2A==" | |||
| "resolved" "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001439.tgz" | |||
| "version" "1.0.30001439" | |||
| "capture-exit@^2.0.0": | |||
| "integrity" "sha512-PiT/hQmTonHhl/HFGN+Lx3JJUznrVYJ3+AQsnthneZbvW7x+f08Tk7yLJTLEOUvBTbduLeeBkxEaYXUOUrRq6g==" | |||