| 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; |
| 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} |
| 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; |
| 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 /> |
| 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> | ||||
| )} | )} |
| 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; |
| 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, | |||||
| }; |
| 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"} |
| 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: { |
| 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 = ""; |
| 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, | |||||
| }); | |||||
| 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'; |
| 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 | |||||
| }); | }); |
| 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 }; | |||||
| } |
| 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)]); |