Przeglądaj źródła

Merge branch 'feature/implement_filtering' of Neca/HRCenter into FE_dev

feature/1522_UI_when_table_view_is_disabled-fe
safet.purkovic 3 lat temu
rodzic
commit
0dcdbf7d95

+ 18
- 0
src/assets/styles/components/_selectionProcessPage.scss Wyświetl plik

@@ -217,6 +217,24 @@ h3 {
}
}

.sel-item-no-data {
display: flex;
flex-direction: row;
align-items: center;
padding: 18px 36px;
gap: 18px;
width: 458px;
background: #FFFFFF;
border: 1px solid #E4E4E4;
border-radius: 18px;
transition: 0.3s;
@include media-below($bp-xl) {
justify-content: space-between;
padding: 18px;
width: 303px;
}
}

.sel-item .status {
font-family: 'Source Sans Pro';
font-style: normal;

+ 1
- 1
src/components/Ads/AdFilters.js Wyświetl plik

@@ -103,7 +103,7 @@ const AdFilters = ({ open, handleClose, technologies }) => {
</div>
<div className="ad-filters-technologies-checkboxes">
<FormGroup>
{technologies.map((technology, index) => (
{technologies?.map((technology, index) => (
<FormControlLabel
key={index}
control={

+ 3
- 3
src/components/Selection/Selection.js Wyświetl plik

@@ -20,7 +20,7 @@ const dragOver = (e) => {
}

const Selection = (props) => {
const applicants = props.selection.selectionProcesses;
const allApplicants = props.selection.selectionProcesses;
const errorMessage = useSelector(selectDoneProcessError);
const dispatch = useDispatch();
const user = useSelector(selectAuthUser);
@@ -49,7 +49,7 @@ const Selection = (props) => {
const handleOpenDetails = (id) => {
props.history.push(SELECTION_PROCESS_OF_APPLICANT_PAGE.replace(":id", id))
}
const applicants = allApplicants?.filter(a => a.status !== "Odrađen");
const renderList = applicants?.map((item, index) => {
return <div draggable key={index} className="sel-item" onDragStart={e => dragStart(e, item)}
onClick={() => handleOpenDetails(item.applicant.applicantId)}>
@@ -79,7 +79,7 @@ const Selection = (props) => {
<Backdrop position="absolute" isLoading={isLoading} />

{applicants.length > 0 && renderList}
{applicants.length === 0 && <div className="sel-item">
{applicants.length === 0 && <div className="sel-item-no-data">
<div className="date">
<p>Nema kandidata u selekciji</p>
</div>

+ 150
- 0
src/components/Selection/SelectionFilter.js Wyświetl plik

@@ -0,0 +1,150 @@
import React, { useState } from "react";
import PropType from "prop-types";
import Box from "@mui/material/Box";
import Drawer from "@mui/material/Drawer";
import FormGroup from "@mui/material/FormGroup";
import FormControlLabel from "@mui/material/FormControlLabel";
import Checkbox from "@mui/material/Checkbox";
import filterIcon from "../../assets/images/filter_vector.png";
import x from "../../assets/images/x.png";
import { changeStatusIsCheckedValue } from "../../store/actions/processes/statusAction";
import { useDispatch } from "react-redux";
import { setFilteredProcessesReq } from "../../store/actions/processes/processesAction";
import { useTranslation } from "react-i18next";

const SelectionFilter = ({ open, handleClose, statuses }) => {
const [startAt, setStartAt] = useState("");
const [endAt, setEndAt] = useState("");
const { t } = useTranslation();

const dispatch = useDispatch();

const onSubmitFilters = () => {
const status = statuses
.filter((status) => status.isChecked === true)
.map((status) => status.name);

if (status.length === 0) {
return;
}

dispatch(
setFilteredProcessesReq({
statuses: status,
startAt,
endAt
})
);

handleClose();
};

const handleCheckboxes = (e) => {
const { value } = e.target;
dispatch(changeStatusIsCheckedValue(value));
};

const list = () => (
<Box
sx={{
width: 360,
height: "100%",
borderRadius: "18px 0 0 18px",
padding: "36px",
}}
role="presentation"
// onClick={handleClose}
onKeyDown={handleClose}
>
<div>
<div className="ad-filters-header-container">
<div className="ad-filters-header">
<img src={filterIcon} alt="filter_icon" />
<h3>{t("filter.title")}</h3>
<p>
<sub>| {t("selection.title")}</sub>
</p>
</div>
<div className="ad-filters-header-close" onClick={handleClose}>
<img src={x} alt="x" />
</div>
</div>
<div className="ad-filters-technologies">
<div className="ad-filters-sub-title">
<p>{t("selection.filterStatus")}</p>
</div>
<div className="ad-filters-technologies-checkboxes">
<FormGroup>
{statuses?.map((technology, index) => (
<FormControlLabel
key={index}
control={
<Checkbox
onChange={handleCheckboxes}
value={technology.name}
checked={technology.isChecked}
/>
}
label={technology.name}
/>
))}
</FormGroup>
</div>
</div>
<div className="ad-filters-technologies">
{/* <div className="ad-filters-sub-title">
<p>Datum isteka oglasa</p>
</div>
<input
type="date"
placeholder="ex"
value={expiredAt}
onChange={(e) => setExpiredAt(e.target.value)}
/> */}
<div className="ad-filters-sub-title">
<p>{t("selection.filterDate")}</p>
</div>
<div className="add-ad-modal-stage-sub-card">
<label>{t("selection.filterFrom")}</label>
<input
type="date"
placeholder="ex"
value={startAt}
onChange={(e) => setStartAt(e.target.value)}
/>
</div>
<div className="add-ad-modal-stage-sub-card">
<label>{t("selection.filterTo")}</label>
<input
type="date"
placeholder="ex"
value={endAt}
onChange={(e) => setEndAt(e.target.value)}
/>
</div>
</div>
<div className="ad-filters-search">
<button onClick={onSubmitFilters} className="c-btn c-btn--primary">
{t("selection.filterSubmit")}
</button>
</div>
</div>
</Box >
);

return (
<div>
<Drawer anchor="right" open={open} onClose={handleClose}>
{list()}
</Drawer>
</div>
);
};

SelectionFilter.propTypes = {
open: PropType.any,
handleClose: PropType.func,
statuses: PropType.any,
};

export default SelectionFilter;

+ 22
- 14
src/i18n/resources/rs.js Wyświetl plik

@@ -137,19 +137,27 @@ export default {
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."
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',
}
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',
},
filter: {
title: "Filteri"
}
};

+ 2
- 1
src/pages/SelectionProcessPage/SelectionProcessOfApplicantPage.js Wyświetl plik

@@ -78,6 +78,7 @@ const SelectionProcessOfApplicantPage = () => {

const concatLevels = () => {
const applicantSelections = [];

for (var i = processes.length; i < levels.length; i++) {
applicantSelections.push(<ApplicantSelection
levelNumber={levels[i].id}
@@ -145,7 +146,7 @@ const SelectionProcessOfApplicantPage = () => {
className={index === processes.length - 1 ? "active-process" : ""}
/>
})}
{processes.length < levels.length && concatLevels()}
{processes.length <= levels.length && concatLevels()}
</Slider>
</div>
</div>

+ 20
- 7
src/pages/SelectionProcessPage/SelectionProcessPage.js Wyświetl plik

@@ -1,17 +1,18 @@
import React, { useState, useEffect } from "react";
import { useSelector} from 'react-redux';
import Selection from "../../components/Selection/Selection";
import IconButton from "../../components/IconButton/IconButton";
import filterVector from "../../assets/images/filter_vector.png";
import FilterButton from "../../components/Button/FilterButton";
import { useTranslation } from "react-i18next";
import AddAdModal from "../../components/Ads/AddAdModal";
import { useDispatch } from "react-redux";
import AdFilters from "../../components/Ads/AdFilters";
import { setDoneProcess } from "../../store/actions/processes/processAction";
import { setProcessesReq } from "../../store/actions/processes/processesAction";
import { setStatuses } from "../../store/actions/processes/statusAction";
import { selectDoneProcess } from "../../store/selectors/processSelectors";
import { selectProcesses } from "../../store/selectors/processesSelectors"
import { selecStatuses } from "../../store/selectors/statusSelectors"
import PropTypes from "prop-types";
import SelectionFilter from "../../components/Selection/SelectionFilter"

const SelectionProcessPage = ({history}) => {
const [toggleFiltersDrawer, setToggleFiltersDrawer] = useState(false);
@@ -22,9 +23,19 @@ const SelectionProcessPage = ({history}) => {
// const doneErrorMessage = useSelector(selectDoneProcessError);
const { t } = useTranslation();
const dispatch = useDispatch();
const statuses = useSelector(selecStatuses);

useEffect(() => {
dispatch(setProcessesReq());
const s = [
{"isChecked":false,"name":"Zakazan"},
{"isChecked":false,"name":"Odrađen"},
{"isChecked":false,"name":"Čeka na zakazivanje"},
{"isChecked":false,"name":"Čeka se odgovor"}]
dispatch(setStatuses(s));
}, []);


@@ -51,10 +62,11 @@ const SelectionProcessPage = ({history}) => {
<>
<div className="l-t-rectangle"></div>
<div className="r-b-rectangle"></div>
<AdFilters />
<AdFilters
<SelectionFilter />
<SelectionFilter
open={toggleFiltersDrawer}
handleClose={handleToggleFiltersDrawer}
statuses={statuses}
/>
<AddAdModal open={toggleModal} handleClose={handleToggleModal} />
<div className="selections">
@@ -68,16 +80,17 @@ const SelectionProcessPage = ({history}) => {
{t("selection.subtitle")}
</span>
</h1>
<FilterButton onShowFilters={handleToggleFiltersDrawer} />
</div>
<div>
<IconButton
{/* <IconButton
sx={{ marginLeft: "15px" }}
className="c-btn c-btn--primary-outlined fixed-right"
onClick={handleToggleFiltersDrawer}
>
Filteri{" "}
<img src={filterVector} alt="filter" className="filter-vector" />
</IconButton>
</IconButton> */}
<div className="selection-levels-processes">
<div className="selection-levels-processes-process">
{renderList}

+ 1
- 0
src/request/apiEndpoints.js Wyświetl plik

@@ -33,6 +33,7 @@ export default {
},
processes: {
allLevels: base + "/selectionlevels",
filteredLevels: base + "/selectionlevels/filtered",
doneProcess: base + "/selectionprocesses",
getApplicantProcesses: base + "/applicants/processes",
// allProcesses: base + "/selectionprocesses",

+ 20
- 0
src/request/processesReguest.js Wyświetl plik

@@ -4,3 +4,23 @@ import apiEndpoints from "./apiEndpoints";
export const getAllLevels = () => getRequest(apiEndpoints.processes.allLevels);
export const doneProcess = (data) => postRequest(apiEndpoints.processes.doneProcess,data);
export const getProcessesOfApplicant = (id) => getRequest(`${apiEndpoints.processes.getApplicantProcesses}/${id}`);
export const getAllFilteredProcessesReq = (payload) => {
let statusesQuery = "";
for (let i = 0; i < payload.statuses.length; i++) {
statusesQuery += `statuses=${payload.statuses[i]}&`;
}

if(payload.startAt && payload.startAt !== null)
statusesQuery += `DateStart=${payload.startAt}`;

if(payload.endAt && payload.endAt !== null)
statusesQuery += `&DateEnd=${payload.endAt}`;


return getRequest(
apiEndpoints.processes.filteredLevels +
"?" +
`${statusesQuery}`
);
};

+ 6
- 0
src/store/actions/processes/processesAction.js Wyświetl plik

@@ -2,12 +2,18 @@ import {
FETCH_PROCESSES_REQ,
FETCH_PROCESSES_ERR,
FETCH_PROCESSES_SUCCESS,
FETCH_FILTERED_PROCESSES_REQ
} from "./processesActionConstants";

export const setProcessesReq = () => ({
type: FETCH_PROCESSES_REQ,
});

export const setFilteredProcessesReq = (payload) => ({
type: FETCH_FILTERED_PROCESSES_REQ,
payload,
});

export const setProcessesError = (payload) => ({
type: FETCH_PROCESSES_ERR,
payload,

+ 7
- 1
src/store/actions/processes/processesActionConstants.js Wyświetl plik

@@ -9,6 +9,7 @@ import {
export const FETCH_PROCESSES_REQ = 'FETCH_PROCESSES_REQ';
export const FETCH_PROCESSES_ERR = 'FETCH_PROCESSES_ERR';
export const FETCH_PROCESSES_SUCCESS = 'FETCH_PROCESSES_SUCCESS';
export const FETCH_FILTERED_PROCESSES_REQ = 'FETCH_FILTERED_PROCESSES_REQ';

const PUT_PROCESS_SCOPE = 'PUT_PROCESS';
export const PUT_PROCESS_REQ = createFetchType(PUT_PROCESS_SCOPE);
@@ -21,4 +22,9 @@ export const PUT_PROCESS_LOADING = createLoadingType(PUT_PROCESS_SCOPE);

export const FETCH_APPLICANT_PROCESSES_REQ = 'FETCH_APPLICANT_PROCESSES_REQ';
export const FETCH_APPLICANT_PROCESSES_ERR = 'FETCH_APPLICANT_PROCESSES_ERR';
export const FETCH_APPLICANT_PROCESSES_SUCCESS = 'FETCH_APPLICANT_PROCESSES_SUCCESS';
export const FETCH_APPLICANT_PROCESSES_SUCCESS = 'FETCH_APPLICANT_PROCESSES_SUCCESS';

export const FETCH_STATUSES_REQ = 'FETCH_STATUSES_REQ';
export const FETCH_STATUSES_ERR = 'FETCH_STATUSES_ERR';
export const FETCH_STATUSES_SUCCESS = 'FETCH_STATUSES_SUCCESS';
export const CHANGE_STATUS_ISCHECKED_VALUE = 'CHANGE_STATUS_ISCHECKED_VALUE';

+ 25
- 0
src/store/actions/processes/statusAction.js Wyświetl plik

@@ -0,0 +1,25 @@
import {
FETCH_STATUSES_ERR,
FETCH_STATUSES_SUCCESS,
FETCH_STATUSES_REQ,
CHANGE_STATUS_ISCHECKED_VALUE
} from "./processesActionConstants";
export const setStatusesReq = () => ({
type: FETCH_STATUSES_REQ,
});
export const setStatusesError = (payload) => ({
type: FETCH_STATUSES_ERR,
payload,
});
export const setStatuses = (payload) => ({
type: FETCH_STATUSES_SUCCESS,
payload,
});
export const changeStatusIsCheckedValue = (payload) => ({
type: CHANGE_STATUS_ISCHECKED_VALUE,
payload,
});

+ 2
- 0
src/store/reducers/index.js Wyświetl plik

@@ -17,6 +17,7 @@ import processReducer from "./processes/processReducer";
import applicantWithProcessesReducer from "./processes/applicantWithProcessesReducer";
import userDetailsReducer from "./user/userDetailsReducer";
import inviteUserReducer from "./user/inviteUserReducer";
import statusReducer from "./processes/statusReducer";

export default combineReducers({
login: loginReducer,
@@ -37,4 +38,5 @@ export default combineReducers({
applicant: applicantWithProcessesReducer,
userDetails: userDetailsReducer,
invite: inviteUserReducer,
statuses: statusReducer,
});

+ 45
- 0
src/store/reducers/processes/statusReducer.js Wyświetl plik

@@ -0,0 +1,45 @@
import createReducer from "../../utils/createReducer";
import {
FETCH_STATUSES_SUCCESS,
FETCH_STATUSES_ERR,
CHANGE_STATUS_ISCHECKED_VALUE,
} from "../../actions/processes/processesActionConstants";

const initialState = {
statuses: [],
errorMessage: "",
};

export default createReducer(
{
[FETCH_STATUSES_SUCCESS]: setStateStatuses,
[FETCH_STATUSES_ERR]: setStateErrorMessage,
[CHANGE_STATUS_ISCHECKED_VALUE]: setIsCheckedStatus,
},
initialState
);

function setStateStatuses(state, action) {
return { ...state, statuses: action.payload };
}

function setStateErrorMessage(state, action) {
return { ...state, errorMessage: action.payload };
}

function setIsCheckedStatus(state, action) {
const tmpIndex = state.statuses.findIndex(
(x) => x.name === action.payload
);

if (tmpIndex === -1) {
return state;
}

return {
...state,
statuses: state.statuses.map((status, index) =>
tmpIndex === index ? { ...status, isChecked: !status.isChecked } : status
),
};
}

+ 15
- 2
src/store/saga/processSaga.js Wyświetl plik

@@ -1,12 +1,12 @@
import { all, call, put, takeLatest } from "redux-saga/effects";
import { getAllLevels, doneProcess, getProcessesOfApplicant } from "../../request/processesReguest";
import { getAllLevels, doneProcess, getProcessesOfApplicant, getAllFilteredProcessesReq } from "../../request/processesReguest";
import { setProcesses, setProcessesError } from "../actions/processes/processesAction";
import { addHeaderToken } from "../../request";
import { authScopeStringGetHelper } from "../../util/helpers/authScopeHelpers";
import { JWT_TOKEN } from "../../constants/localStorage";
import { setDoneProcess, setDoneProcessError } from "../actions/processes/processAction";
import { setApplicant, setApplicantError } from "../actions/processes/applicantAction";
import { FETCH_PROCESSES_REQ, PUT_PROCESS_REQ, FETCH_APPLICANT_PROCESSES_REQ } from "../actions/processes/processesActionConstants";
import { FETCH_PROCESSES_REQ, FETCH_FILTERED_PROCESSES_REQ ,PUT_PROCESS_REQ, FETCH_APPLICANT_PROCESSES_REQ } from "../actions/processes/processesActionConstants";

export function* getProcesses() {
try {
@@ -19,6 +19,18 @@ export function* getProcesses() {
}
}

export function* getFilteredProcesses(payload) {
try {
// const JwtToken = yield call(authScopeStringGetHelper, JWT_TOKEN);
// yield call(addHeaderToken, JwtToken);
console.log(payload.payload)
const result = yield call(getAllFilteredProcessesReq, payload.payload);
yield put(setProcesses(result.data));
} catch (error) {
yield put(setProcessesError(error));
}
}

export function* finishProcess(payload) {
try {
const JwtToken = yield call(authScopeStringGetHelper, JWT_TOKEN);
@@ -45,6 +57,7 @@ export function* getApplicantProcesses(payload) {

export default function* processesSaga() {
yield all([takeLatest(FETCH_PROCESSES_REQ, getProcesses)]);
yield all([takeLatest(FETCH_FILTERED_PROCESSES_REQ, getFilteredProcesses)]);
yield all([takeLatest(PUT_PROCESS_REQ, finishProcess)]);
yield all([takeLatest(FETCH_APPLICANT_PROCESSES_REQ, getApplicantProcesses)]);
}

+ 13
- 0
src/store/selectors/statusSelectors.js Wyświetl plik

@@ -0,0 +1,13 @@
import { createSelector } from "@reduxjs/toolkit";

export const statusesSelector = (state) => state.statuses;

export const selecStatuses = createSelector(
statusesSelector,
(state) => state.statuses
);

export const selecStatusesError = createSelector(
statusesSelector,
(state) => state.errorMessage
);

Ładowanie…
Anuluj
Zapisz