Просмотр исходного кода

implemented interviewer selection

pull/120/head
meris.ahmatovic 3 лет назад
Родитель
Сommit
3d9aed0540

+ 51
- 12
src/assets/styles/components/_selectionProcessPage.scss Просмотреть файл

border-radius: 18px; border-radius: 18px;
gap: 18px; gap: 18px;
margin-right: 36px; margin-right: 36px;
transition: background-color 0.35s ease;
@include media-below($bp-xl) { @include media-below($bp-xl) {
margin-right: 20px !important; margin-right: 20px !important;
padding: 18px !important; padding: 18px !important;
} }
&.over{
background-color: $mainBlueLight !important;
transition: background-color 0.35s ease;
}
} }


.sel-item:hover { .sel-item:hover {
transition: .25s;
scale: 1.05; scale: 1.05;
border-color: $mainBlue !important;
background-color: $mainBlueLight !important;
border-color:$mainBlue;
.sel-item-inner{
background-color: $mainBlueLight !important;
transition: .25s;
.status button{
background-color: $mainBlueLight !important;
transition: .25s;
}
}

} }


.bg-danger { .bg-danger {
flex-grow: 0; flex-grow: 0;
} }


.sel-item {
.sel-item{
border-radius: 18px;
border: 1px solid #e4e4e4;
overflow: hidden;
transition: .25s;
}
.sel-item-scheduler{
display: flex;
justify-content: space-between;
background: #E4E4E4;

p{
padding: 12.5px 25px 12.5px 0px !important;
font-family: 'Source Sans Pro';
font-style: italic;
font-weight: 400;
font-size: 16px;
line-height: 20px;
text-align: right;
color: #272727;
}
}
.sel-item-inner {
transition: .25s;
display: flex; display: flex;
flex-direction: row; flex-direction: row;
align-items: center; align-items: center;
cursor: pointer; cursor: pointer;
width: 458px; width: 458px;
background: #ffffff; background: #ffffff;
border: 1px solid #e4e4e4;
border-radius: 18px;
transition: 0.3s;
// transition: 0.3s;
@include media-below($bp-xl) { @include media-below($bp-xl) {
justify-content: space-between; justify-content: space-between;
padding: 18px; padding: 18px;
width: 303px; width: 303px;
} }
.status button{ transition: .25s;}
} }


.sel-item-no-data {
.sel-item-inner-no-data {
display: flex; display: flex;
flex-direction: row; flex-direction: row;
align-items: center; align-items: center;
} }
} }


.sel-item .status {
.sel-item-inner .status {
font-family: "Source Sans Pro"; font-family: "Source Sans Pro";
font-style: normal; font-style: normal;
font-weight: 400; font-weight: 400;
} }
} }


.sel-item .date-name {
.sel-item-inner .date-name {
display: flex; display: flex;
flex-direction: row; flex-direction: row;
align-items: flex-start; align-items: flex-start;
} }
} }


.sel-item .date-name .date {
.sel-item-inner .date-name .date {
font-family: "Source Sans Pro"; font-family: "Source Sans Pro";
font-style: normal; font-style: normal;
font-weight: 400; font-weight: 400;
} }


.full-name p, .full-name p,
.sel-item .date-name .date p {
.sel-item-inner .date-name .date p {
@include media-below($bp-xl) { @include media-below($bp-xl) {
font-size: 14px !important; font-size: 14px !important;
} }
} }


.sel-item .status button {
.sel-item-inner .status button {
box-sizing: border-box; box-sizing: border-box;
display: flex; display: flex;
flex-direction: row; flex-direction: row;

+ 46
- 15
src/components/MUI/InterviewDialog.js Просмотреть файл

responsive, responsive,
}) => { }) => {
const [selected, setSelected] = useState(""); const [selected, setSelected] = useState("");
const [selectedInterviewer, setSelectedInterviewer] = useState("");
const [value, setValue] = useState(null); const [value, setValue] = useState(null);


const theme = useTheme(); const theme = useTheme();
const fullScreen = useMediaQuery(theme.breakpoints.down("md")); const fullScreen = useMediaQuery(theme.breakpoints.down("md"));


const { options } = useSelector((n) => n.options); const { options } = useSelector((n) => n.options);
const { user } = useSelector((s) => s.user);
const { users } = useSelector((n) => n.users);
// const { user } = useSelector((s) => s.user);
const { isSuccess } = useSelector((s) => s.initProcess); const { isSuccess } = useSelector((s) => s.initProcess);


const dispatch = useDispatch(); const dispatch = useDispatch();
}, [dispatch, isSuccess]); }, [dispatch, isSuccess]);


const handleChange = (newValue) => { const handleChange = (newValue) => {
if (isValid(newValue)) { // throws an error if invalid value is set
if (isValid(newValue)) {
// throws an error if invalid value is set
var date = format(newValue, "yyyy-MM-dd'T'HH:mm:ss.SSSxxx"); var date = format(newValue, "yyyy-MM-dd'T'HH:mm:ss.SSSxxx");
setValue(date); setValue(date);
} }
}; };


useEffect(()=>{
setSelected('')
setValue(null)
}, [onClose])
useEffect(() => {
setSelected("");
setSelectedInterviewer("");
setValue(null);
}, [onClose]);


const handleClose = () => { const handleClose = () => {
onClose(); onClose();
}; };


console.log(selectedInterviewer)
const submitHandler = () => { const submitHandler = () => {
dispatch(
fetchInitProcess({
model: {
schedulerId: user.id,
appointment: value,
applicantId: selected,
},
})
);
dispatch(
fetchInitProcess({
model: {
schedulerId: selectedInterviewer,
appointment: value,
applicantId: selected,
},
})
);
}; };


return ( return (
: ""} : ""}
</Select> </Select>
</FormControl> </FormControl>
<FormControl fullWidth>
<InputLabel id="demo-simple-select-label">
Ime intervjuera (opciono)
</InputLabel>
<Select
labelId="demo-simple-select-label"
id="demo-simple-select"
value={selectedInterviewer}
label="Ime intervjuera (opciono)"
onChange={(e) => {
setSelectedInterviewer(e.target.value);
}}
>
{users
? users.map(({ id, firstName, lastName }, index) => (
<MenuItem
key={index}
sx={{ textAlign: "left" }}
value={id}
>
{firstName} {lastName}
</MenuItem>
))
: ""}
</Select>
</FormControl>
<DateTimePicker <DateTimePicker
label="Termin (opciono)" label="Termin (opciono)"
value={value} value={value}

+ 228
- 0
src/components/MUI/StatusDialog.js Просмотреть файл

import React, { useContext, useState } from "react";
import PropTypes from "prop-types";
import x from "../../assets/images/x.png";
import {
Dialog,
DialogTitle,
DialogActions,
useMediaQuery,
useTheme,
DialogContent,
FormControl,
InputLabel,
Select,
MenuItem,
TextField,
// TextField,
} from "@mui/material";
import IconButton from "../IconButton/IconButton";
import { DateTimePicker } from "@mui/x-date-pickers/DateTimePicker";
import { useDispatch, useSelector } from "react-redux";
import { format, isValid } from "date-fns";
// import { fetchInitProcess } from "../../store/actions/candidates/candidatesActions";
import { useEffect } from "react";
import { SelectionContext } from "../../context/SelectionContext";
import { setUpdateStatusReq } from "../../store/actions/processes/processAction";

const StatusDialog = ({
title,
subtitle,
imgSrc,
onClose,
open,
maxWidth,
fullWidth,
responsive,
}) => {
const [selected, setSelected] = useState("");
const [value, setValue] = useState(null);

const { activeProcess, setActiveProcess } = useContext(SelectionContext)

const theme = useTheme();
const fullScreen = useMediaQuery(theme.breakpoints.down("md"));

const { users } = useSelector((s) => s.users);
const { isSuccess } = useSelector((s) => s.initProcess);

const dispatch = useDispatch();

useEffect(() => {
handleClose();
}, [dispatch, isSuccess]);

const handleChange = (newValue) => {
if (isValid(newValue)) { // throws an error if invalid value is set
var date = format(newValue, "yyyy-MM-dd'T'HH:mm:ss.SSSxxx");
setValue(date);
}
};

useEffect(()=>{
setSelected('')
setValue(null)
}, [onClose])

const handleClose = () => {
onClose();
};

const submitHandler = () => {
dispatch(
setUpdateStatusReq({
data: {
schedulerId: selected,
appointment: value,
newStatus: activeProcess.status,
processId: activeProcess.process.id,
},
responseHandler: apiSuccess
})
);
};

const apiSuccess = () =>{
setActiveProcess(null)
}

return (
<Dialog
maxWidth={maxWidth}
keepMounted={false}
fullWidth={fullWidth}
fullScreen={responsive && fullScreen}
onClose={handleClose}
open={open}
style={{
padding: "36px",
}}
>
<div style={{ padding: "36px" }}>
<DialogTitle style={{ padding: 0 }}>
{fullScreen ? (
<>
<div className="flex-center" style={{ justifyContent: "start" }}>
<img
style={{
position: "relative",
top: -0.25,
paddingRight: "10px",
}}
src={imgSrc}
/>
<h5 style={{ textAlign: "start" }}>{title}</h5>
<div style={{ justifySelf: "stretch", flex: "1" }}></div>
<IconButton onClick={onClose}>
<img
style={{
position: "relative",
top: -0.25,
}}
src={x}
/>
</IconButton>
</div>
<p
className="dialog-subtitle"
style={{ paddingLeft: "23px", marginTop: "-10px" }}
>
| {subtitle}
</p>
</>
) : (
<div
className="flex-center"
style={{ justifyContent: "space-between" }}
>
<div className="flex-center" style={{ justifyContent: "start" }}>
<img
style={{
position: "relative",
top: -0.25,
paddingRight: "10px",
}}
src={imgSrc}
/>
<h5>{title}</h5>
<div className="vr"></div>
<p className="dialog-subtitle">{subtitle}</p>
</div>
</div>
)}
</DialogTitle>
<DialogContent>
<form className="modal-content interviewDialog">
<FormControl fullWidth>
<InputLabel id="demo-simple-select-label">
Ime intervjuera
</InputLabel>
<Select
labelId="demo-simple-select-label"
id="demo-simple-select"
value={selected}
label="Ime intervjuera"
onChange={(e) => {
setSelected(e.target.value);
}}
>
{users
? users.map(
({ id, firstName, lastName }, index) => (
<MenuItem
key={index}
sx={{ textAlign: "left" }}
value={id}
>
{firstName} {lastName}
</MenuItem>
)
)
: ""}
</Select>
</FormControl>
{/* {activeProcess.process && activeProcess.process.date ? <p>Proces ima zakazan termin</p> : ''} */}
<DateTimePicker
label="Termin"
value={value}
onChange={handleChange}
renderInput={(params) => <TextField {...params} />}
/>
</form>
</DialogContent>
<DialogActions style={{ padding: 0, justifyContent: "space-between" }}>
{!fullScreen ? (
<IconButton
data-testid="editbtn"
className={`c-btn--primary-outlined interview-btn c-btn dialog-btn`}
onClick={onClose}
>
Cancel
</IconButton>
) : (
""
)}
<IconButton
data-testid="editbtn"
className={`c-btn--primary-outlined sm-full interview-btn c-btn dialog-btn`}
onClick={submitHandler}
>
Confirm
</IconButton>
</DialogActions>
</div>
</Dialog>
);
};

StatusDialog.propTypes = {
title: PropTypes.any,
subtitle: PropTypes.any,
imgSrc: PropTypes.any,
open: PropTypes.bool.isRequired,
onClose: PropTypes.func.isRequired,
maxWidth: PropTypes.oneOf(["xs", "sm", "md", "lg", "xl"]),
fullWidth: PropTypes.bool,
responsive: PropTypes.bool,
};

export default StatusDialog;

+ 8
- 0
src/components/Section/MainContainer.js Просмотреть файл

import { useLocation } from "react-router-dom"; import { useLocation } from "react-router-dom";
import Navbar from "../../components/MUI/NavbarComponent"; import Navbar from "../../components/MUI/NavbarComponent";
import { FormProvider } from "../../context/FormContext"; import { FormProvider } from "../../context/FormContext";
import { SelectionProvider } from "../../context/SelectionContext";
// import AppRoutes from "../../AppRoutes"; // import AppRoutes from "../../AppRoutes";


const urls = [ const urls = [
<FormProvider> <FormProvider>
<div className="">{children}</div> <div className="">{children}</div>
</FormProvider> </FormProvider>
) : pathname === "/selectionFlow" ? (
<SelectionProvider>
<div className="">
<Navbar />
<div className="h-withHeader">{children}</div>
</div>
</SelectionProvider>
) : ( ) : (
<div className=""> <div className="">
<Navbar /> <Navbar />

+ 29
- 38
src/components/Selection/Selection.js Просмотреть файл

import React from "react";
import React, { useState } from "react";
import PropTypes from "prop-types"; import PropTypes from "prop-types";
import { selectDoneProcessError } from "../../store/selectors/processSelectors"; import { selectDoneProcessError } from "../../store/selectors/processSelectors";
import { selectAuthUser } from "../../store/selectors/userSelectors"; import { selectAuthUser } from "../../store/selectors/userSelectors";
import { setDoneProcessReq } from "../../store/actions/processes/processAction"; import { setDoneProcessReq } from "../../store/actions/processes/processAction";
import { useDispatch, useSelector } from "react-redux"; import { useDispatch, useSelector } from "react-redux";
import { formatDateSrb, formatTimeSrb } from "../../util/helpers/dateHelpers";
// import { formatDateSrb, formatTimeSrb } from "../../util/helpers/dateHelpers";
import { SELECTION_PROCESS_OF_APPLICANT_PAGE } from "../../constants/pages"; import { SELECTION_PROCESS_OF_APPLICANT_PAGE } from "../../constants/pages";
import { PUT_PROCESS_LOADING } from "../../store/actions/processes/processesActionConstants"; import { PUT_PROCESS_LOADING } from "../../store/actions/processes/processesActionConstants";
import { selectIsLoadingByActionType } from "../../store/selectors/loadingSelectors"; import { selectIsLoadingByActionType } from "../../store/selectors/loadingSelectors";
import Backdrop from "../../components/MUI/BackdropComponent"; import Backdrop from "../../components/MUI/BackdropComponent";
import { IconButton } from "@mui/material"; import { IconButton } from "@mui/material";
import plus from "../../assets/images/plus.png"; import plus from "../../assets/images/plus.png";

const dragStart = (e, applicant) => {
e.dataTransfer.setData("text/plain", JSON.stringify(applicant));
};

const dragOver = (e) => {
e.preventDefault();
};
import SelectionCard from "./SelectionCard";


const Selection = (props) => { const Selection = (props) => {
const [over, setOver] = useState(false);
const allApplicants = props.selection.selectionProcesses; const allApplicants = props.selection.selectionProcesses;
const errorMessage = useSelector(selectDoneProcessError); const errorMessage = useSelector(selectDoneProcessError);
const dispatch = useDispatch(); const dispatch = useDispatch();
const user = useSelector(selectAuthUser); const user = useSelector(selectAuthUser);


const dropItem = (e, selId) => { const dropItem = (e, selId) => {
setOver(false);
var data = e.dataTransfer.getData("text/plain"); var data = e.dataTransfer.getData("text/plain");
const selectionProcess = JSON.parse(data); const selectionProcess = JSON.parse(data);

if (selectionProcess.selectionLevelId < selId) { if (selectionProcess.selectionLevelId < selId) {
dispatch( dispatch(
setDoneProcessReq({ setDoneProcessReq({
}) })
); );
} }

if (errorMessage) { if (errorMessage) {
console.log(errorMessage); console.log(errorMessage);
} }
}; };


const dragStart = (e, applicant) => {
e.dataTransfer.setData("text/plain", JSON.stringify(applicant));
};

const dragOver = (e) => {
e.preventDefault();
setOver(true);
};

const isLoading = useSelector( const isLoading = useSelector(
selectIsLoadingByActionType(PUT_PROCESS_LOADING) selectIsLoadingByActionType(PUT_PROCESS_LOADING)
); );
const handleOpenDetails = (id) => { const handleOpenDetails = (id) => {
props.history.push(SELECTION_PROCESS_OF_APPLICANT_PAGE.replace(":id", id)); props.history.push(SELECTION_PROCESS_OF_APPLICANT_PAGE.replace(":id", id));
}; };
const applicants = allApplicants?.filter((a) => a.status !== "Odrađen");

const applicants = allApplicants?.filter((a) => a.status !== "Odrađen" || a.selectionLevelId === 4);
const renderList = applicants?.map((item, index) => { const renderList = applicants?.map((item, index) => {
return ( return (
<div
draggable
<SelectionCard
key={index} key={index}
className="sel-item"
onDragStart={(e) => dragStart(e, item)}
onClick={() => handleOpenDetails(item.applicant.applicantId)}
>
<div className="status">
<button>{item.status}</button>
</div>
<div className="date-name">
<div className="date">
{item.date !== null && item.date !== "" && (
<p>
{formatDateSrb(item.date)} <span className="grey">|</span>{" "}
{formatTimeSrb(item.date)}
</p>
)}
</div>
<div className="full-name">
<p>{item.applicant.firstName + " " + item.applicant.lastName}</p>
</div>
</div>
</div>
item={item}
dragStart={(e) => dragStart(e, item)}
click={() => handleOpenDetails(item.applicant.applicantId)}
/>
); );
}); });


data-testid="selection-level" data-testid="selection-level"
dropppable="true" dropppable="true"
id={props.selection.id} id={props.selection.id}
className="selection-card"
className={`selection-card ${over ? "over" : ""}`}
onDragOver={(e) => dragOver(e)} onDragOver={(e) => dragOver(e)}
onDragLeave={() => setOver(false)}
onDrop={(e) => dropItem(e, props.selection.id)} onDrop={(e) => dropItem(e, props.selection.id)}
> >
<div className="selection-card-title"> <div className="selection-card-title">
<h3>{props.selection.name}</h3>
<h3 style={{marginRight:'50px'}}>{props.selection.name}</h3>
{props.order === 0 ? ( {props.order === 0 ? (
<IconButton <IconButton
sx={{marginRight:'35px'}}
className={`c-btn--primary-outlined c-btn td-btn`} className={`c-btn--primary-outlined c-btn td-btn`}
onClick={props.modalEvent} onClick={props.modalEvent}
> >
)} )}
</div> </div>
<Backdrop position="absolute" isLoading={isLoading} /> <Backdrop position="absolute" isLoading={isLoading} />

{applicants && {applicants &&
applicants !== null && applicants !== null &&
applicants?.length > 0 && applicants?.length > 0 &&
{applicants && applicants !== null && applicants?.length === 0 && ( {applicants && applicants !== null && applicants?.length === 0 && (
<div className="sel-item-no-data"> <div className="sel-item-no-data">
<div className="date"> <div className="date">
<p>Nema kandidata u selekciji</p>
<p style={{width:'240px'}}>Nema kandidata u selekciji</p>
</div> </div>
</div> </div>
)} )}

+ 147
- 0
src/components/Selection/SelectionCard.js Просмотреть файл

import React, { useContext, useState } from "react";
import PropTypes from "prop-types";
import { formatDateSrb, formatTimeSrb } from "../../util/helpers/dateHelpers";
import { FormControl, InputLabel, MenuItem, Select } from "@mui/material";
import { SelectionContext } from "../../context/SelectionContext";
import { useDispatch} from "react-redux";
import { setDoneProcessReq, setUpdateStatusReq } from "../../store/actions/processes/processAction";

const options = ["Zakazan", "Odrađen", "Čeka na zakazivanje"];

const SelectionCard = (props) => {
const [showForm, setShowForm] = useState(false);
const [selected, setSelected] = useState(props.item.status);

const { setActiveProcess } = useContext(SelectionContext);
const dispatch = useDispatch();

// const { success } = useSelector(s => s.statusUpdate)

const statusChange = (e) => {
if(props.item.status !== "Odrađen"){
e.stopPropagation();
setShowForm(true);
}
};

const select = (e) => {
e.stopPropagation();
if (e.target.value === "Zakazan") {
setActiveProcess({process:props.item, status:'Zakazan'});
} else if (e.target.value === "Odrađen") {
if (props.item.selectionLevelId !== 4)
dispatch(
setDoneProcessReq({
id: props.item.id,
name: "Some random name",
applicantId: props.item.applicant.applicantId
})
);
else {
// pozvati nasu custom metodu za promenu statusa bez prebacivanja u veci nivo
// promeni status u odradjeno
dispatch(
setUpdateStatusReq({
data: {
// schedulerId: selected,
// appointment: value,
newStatus: "Odrađen",
processId: props.item.id,
}
}))
}
}
setSelected(e.target.value);
};

const clickHandler = () => {
if (showForm) {
setSelected(props.item.status);
setShowForm(false);
} else props.click();
};

// useEffect(()=>{
// setShowForm(false)
// },[success])

return (
<div
draggable
className="sel-item"
onDragStart={props.dragStart}
onClick={clickHandler}
>
{" "}
<div
className={`sel-item-inner ${props.item.scheduler && "withScheduler"}`}
>
{showForm ? (
<form>
<FormControl>
<InputLabel id="demo-simple-select-label">Status</InputLabel>
<Select
label="Status"
onChange={(e) => {
select(e);
}}
onClick={(e) => e.stopPropagation()}
value={selected}
sx={{
height: "40px",
fontSize: "14px",
paddingRight: "5px",
}}
>
{options.map((n, index) => (
<MenuItem key={index} sx={{ textAlign: "left" }} value={n}>
{n}
</MenuItem>
))}
</Select>
</FormControl>
</form>
) : (
<div className="status" onClick={(e) => statusChange(e)}>
<button>{props.item.status}</button>
</div>
)}
<div className="date-name">
<div className="date">
{props.item.date !== null && props.item.date !== "" && (
<p>
{formatDateSrb(props.item.date)} <span className="grey">|</span>{" "}
{formatTimeSrb(props.item.date)}
</p>
)}
</div>
<div className="full-name">
<p>
{props.item.applicant.firstName +
" " +
props.item.applicant.lastName}
</p>
</div>
</div>
</div>
{props.item.scheduler ? (
<div className="sel-item-scheduler">
<div></div>
<p>
Intervjuer: {props.item.scheduler.firstName}{" "}
{props.item.scheduler.lastName}
</p>
</div>
) : (
""
)}
</div>
);
};

SelectionCard.propTypes = {
item: PropTypes.any,
click: PropTypes.func,
dragStart: PropTypes.func,
};
export default SelectionCard;

+ 20
- 0
src/context/SelectionContext.js Просмотреть файл

import React, { createContext, useState } from "react";
import PropTypes from "prop-types";

export const SelectionContext = createContext();

export const SelectionProvider = ({children}) => {
const [activeProcess, setActiveProcess] = useState(null);

return (
<SelectionContext.Provider
value={{ activeProcess, setActiveProcess }}
>
{children}
</SelectionContext.Provider>
);
};

SelectionProvider.propTypes = {
children: PropTypes.node,
};

+ 20
- 3
src/pages/SelectionProcessPage/SelectionProcessPage.js Просмотреть файл

import React, { useState, useEffect } from "react";
import React, { useState, useEffect, useContext } from "react";
import { useSelector } from "react-redux"; import { useSelector } from "react-redux";
import Selection from "../../components/Selection/Selection"; import Selection from "../../components/Selection/Selection";
import FilterButton from "../../components/Button/FilterButton"; import FilterButton from "../../components/Button/FilterButton";
import { AdapterDateFns } from '@mui/x-date-pickers/AdapterDateFns'; import { AdapterDateFns } from '@mui/x-date-pickers/AdapterDateFns';
import { LocalizationProvider } from '@mui/x-date-pickers'; import { LocalizationProvider } from '@mui/x-date-pickers';
import { fetchCandidateOptions } from "../../store/actions/candidates/candidatesActions"; import { fetchCandidateOptions } from "../../store/actions/candidates/candidatesActions";
import { SelectionContext } from "../../context/SelectionContext";
import { setUsersReq } from "../../store/actions/users/usersActions";
import StatusDialog from "../../components/MUI/StatusDialog";


const SelectionProcessPage = ({ history }) => { const SelectionProcessPage = ({ history }) => {
const [toggleFiltersDrawer, setToggleFiltersDrawer] = useState(false); const [toggleFiltersDrawer, setToggleFiltersDrawer] = useState(false);
const dispatch = useDispatch(); const dispatch = useDispatch();
const statuses = useSelector(selecStatuses); const statuses = useSelector(selecStatuses);
const {isSuccess} = useSelector(s => s.initProcess); const {isSuccess} = useSelector(s => s.initProcess);
const {success} = useSelector(s => s.statusUpdate);

const {activeProcess,setActiveProcess} = useContext(SelectionContext)


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


useEffect(() => { useEffect(() => {
dispatch(setUsersReq());
dispatch(setProcessesReq()); dispatch(setProcessesReq());
dispatch(setDoneProcess(false)); dispatch(setDoneProcess(false));
}, [process.process.doneProcess]);
}, [process.process.doneProcess, success]);


const handleToggleFiltersDrawer = () => { const handleToggleFiltersDrawer = () => {
setToggleFiltersDrawer((oldState) => !oldState); setToggleFiltersDrawer((oldState) => !oldState);
handleClose={handleToggleFiltersDrawer} handleClose={handleToggleFiltersDrawer}
statuses={statuses} statuses={statuses}
/> />
<StatusDialog
open={activeProcess !== null}
title={"Dodavanje intervjuera"}
subtitle={'Selekcija'}
imgSrc={plus}
onClose={() => {
setActiveProcess(null);
}}
/>
<InterviewDialog <InterviewDialog
open={toggleInitModal} open={toggleInitModal}
title={"Dodavanje kandidata"} title={"Dodavanje kandidata"}

+ 1
- 0
src/request/apiEndpoints.js Просмотреть файл

filteredLevels: base + "/selectionlevels/filtered", filteredLevels: base + "/selectionlevels/filtered",
doneProcess: base + "/selectionprocesses", doneProcess: base + "/selectionprocesses",
getApplicantProcesses: base + "/applicants/processes", getApplicantProcesses: base + "/applicants/processes",
changeStatus: base + "/selectionprocesses/status-update"
// allProcesses: base + "/selectionprocesses", // allProcesses: base + "/selectionprocesses",
}, },
patterns: { patterns: {

+ 1
- 0
src/request/processesReguest.js Просмотреть файл



export const getAllLevels = () => getRequest(apiEndpoints.processes.allLevels); export const getAllLevels = () => getRequest(apiEndpoints.processes.allLevels);
export const doneProcess = (data) => postRequest(apiEndpoints.processes.doneProcess,data); export const doneProcess = (data) => postRequest(apiEndpoints.processes.doneProcess,data);
export const updateStatus = (data) => postRequest(apiEndpoints.processes.changeStatus,data);
export const getProcessesOfApplicant = (id) => getRequest(`${apiEndpoints.processes.getApplicantProcesses}/${id}`); export const getProcessesOfApplicant = (id) => getRequest(`${apiEndpoints.processes.getApplicantProcesses}/${id}`);
export const getAllFilteredProcessesReq = (payload) => { export const getAllFilteredProcessesReq = (payload) => {
let statusesQuery = ""; let statusesQuery = "";

+ 18
- 0
src/store/actions/processes/processAction.js Просмотреть файл

import { import {
FETCH_STATUS_CHANGE_ERR,
FETCH_STATUS_CHANGE_REQ,
FETCH_STATUS_CHANGE_SUCCESS,
PUT_PROCESS_ERR, PUT_PROCESS_ERR,
PUT_PROCESS_REQ, PUT_PROCESS_REQ,
PUT_PROCESS_SUCCESS PUT_PROCESS_SUCCESS
type: PUT_PROCESS_SUCCESS, type: PUT_PROCESS_SUCCESS,
payload payload
}); });
//
export const setUpdateStatusReq = (payload) => ({
type: FETCH_STATUS_CHANGE_REQ,
payload
});
export const setUpdateStatusErr = (payload) => ({
type: FETCH_STATUS_CHANGE_ERR,
payload,
});
export const setUpdateStatusSucc = () => ({
type: FETCH_STATUS_CHANGE_SUCCESS,
});

+ 5
- 1
src/store/actions/processes/processesActionConstants.js Просмотреть файл

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

export const FETCH_STATUS_CHANGE_REQ = 'FETCH_STATUS_CHANGE_REQ';
export const FETCH_STATUS_CHANGE_ERR = 'FETCH_STATUS_CHANGE_ERR';
export const FETCH_STATUS_CHANGE_SUCCESS = 'FETCH_STATUS_CHANGE_SUCCESS';

+ 2
- 0
src/store/reducers/index.js Просмотреть файл

import candidateOptionsReducer from "./candidates/candidateOptionsReducer"; import candidateOptionsReducer from "./candidates/candidateOptionsReducer";
import initProcessReducer from "./candidates/initProcessReducer"; import initProcessReducer from "./candidates/initProcessReducer";
import applyForAdReducer from "./applicants/applyForAdReducer"; import applyForAdReducer from "./applicants/applyForAdReducer";
import statusUpdateReducer from "./processes/statusUpdateReducer";


export default combineReducers({ export default combineReducers({
login: loginReducer, login: loginReducer,
scheduleAppointment: scheduleAppointmentReducer, scheduleAppointment: scheduleAppointmentReducer,
patternApplicants: patternApplicantsReducer, patternApplicants: patternApplicantsReducer,
applyForAd: applyForAdReducer, applyForAd: applyForAdReducer,
statusUpdate: statusUpdateReducer
}); });

+ 32
- 0
src/store/reducers/processes/statusUpdateReducer.js Просмотреть файл

import createReducer from "../../utils/createReducer";
import {
FETCH_STATUS_CHANGE_ERR,
FETCH_STATUS_CHANGE_REQ,
FETCH_STATUS_CHANGE_SUCCESS,
} from "../../actions/processes/processesActionConstants";

const initialState = {
success: false,
errorMessage: "",
};

export default createReducer(
{
[FETCH_STATUS_CHANGE_REQ]: setStateErrorReq,
[FETCH_STATUS_CHANGE_ERR]: setStateErrorMessage,
[FETCH_STATUS_CHANGE_SUCCESS]: statusUpdateSuccess,
},
initialState
);

function statusUpdateSuccess(state) {
return { ...state, success: true };
}

function setStateErrorReq(state) {
return { ...state, success: false };
}

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

+ 20
- 3
src/store/saga/processSaga.js Просмотреть файл

import { all, call, put, takeLatest } from "redux-saga/effects"; import { all, call, put, takeLatest } from "redux-saga/effects";
import { getAllLevels, doneProcess, getProcessesOfApplicant, getAllFilteredProcessesReq } from "../../request/processesReguest";
import { getAllLevels, doneProcess, getProcessesOfApplicant, getAllFilteredProcessesReq, updateStatus } from "../../request/processesReguest";
import { setProcesses, setProcessesError } from "../actions/processes/processesAction"; import { setProcesses, setProcessesError } from "../actions/processes/processesAction";
import { addHeaderToken } from "../../request"; import { addHeaderToken } from "../../request";
import { authScopeStringGetHelper } from "../../util/helpers/authScopeHelpers"; import { authScopeStringGetHelper } from "../../util/helpers/authScopeHelpers";
import { JWT_TOKEN } from "../../constants/localStorage"; import { JWT_TOKEN } from "../../constants/localStorage";
import { setDoneProcess, setDoneProcessError } from "../actions/processes/processAction";
import { setDoneProcess, setDoneProcessError, setUpdateStatusErr, setUpdateStatusSucc } from "../actions/processes/processAction";
import { setApplicant, setApplicantError } from "../actions/processes/applicantAction"; import { setApplicant, setApplicantError } from "../actions/processes/applicantAction";
import { FETCH_PROCESSES_REQ, FETCH_FILTERED_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, FETCH_STATUS_CHANGE_REQ } from "../actions/processes/processesActionConstants";
import { rejectErrorCodeHelper } from "../../util/helpers/rejectErrorCodeHelper"; import { rejectErrorCodeHelper } from "../../util/helpers/rejectErrorCodeHelper";


export function* getProcesses() { export function* getProcesses() {
} }
} }
} }
export function* changeStatus({payload}) {
try {
const JwtToken = yield call(authScopeStringGetHelper, JWT_TOKEN);
yield call(addHeaderToken, JwtToken);
yield call(updateStatus,payload.data);
yield put(setUpdateStatusSucc());
if(payload.responseHandler){
yield call(payload.responseHandler)
}
} catch (error) {
if (error.response && error.response.data) {
const errorMessage = yield call(rejectErrorCodeHelper, error);
yield put(setUpdateStatusErr(errorMessage));
}
}
}


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

Загрузка…
Отмена
Сохранить