| @@ -0,0 +1,139 @@ | |||
| import React, { useState } from "react"; | |||
| import CustomDrawer from "../UI/CustomDrawer"; | |||
| import { Checkbox, FormControlLabel, FormGroup } from "@mui/material"; | |||
| import PropTypes from "prop-types"; | |||
| import { useDispatch } from "react-redux"; | |||
| import { setFilteredPatternsReq } from "../../store/actions/patterns/patternsActions"; | |||
| const PatternFilters = ({ | |||
| openFilterDrawer, | |||
| handleClose, | |||
| selectionLevelFilter, | |||
| onChangeFilterCheckboxes, | |||
| }) => { | |||
| const [fromDate, setFromDate] = useState(""); | |||
| const [toDate, setToDate] = useState(""); | |||
| const dispatch = useDispatch(); | |||
| const submitFiltersHandler = (e) => { | |||
| e.preventDefault(); | |||
| const selectionLevels = selectionLevelFilter | |||
| .filter((level) => level.isChecked === true) | |||
| .map((level) => level.id); | |||
| dispatch( | |||
| setFilteredPatternsReq({ | |||
| fromDate: fromDate === "" ? null : fromDate, | |||
| toDate: toDate === "" ? null : toDate, | |||
| selectionLevels, | |||
| handleApiResponseSuccess, | |||
| }) | |||
| ); | |||
| }; | |||
| const handleApiResponseSuccess = () => { | |||
| handleClose(); | |||
| }; | |||
| const hasSelectedFilters = () => { | |||
| const checked = selectionLevelFilter.filter( | |||
| (level) => level.isChecked === true | |||
| ); | |||
| if (checked.length > 0) { | |||
| return true; | |||
| } | |||
| if (fromDate !== "") { | |||
| return true; | |||
| } | |||
| if (toDate !== "") { | |||
| return true; | |||
| } | |||
| return false; | |||
| }; | |||
| return ( | |||
| <CustomDrawer | |||
| title="Šabloni" | |||
| open={openFilterDrawer} | |||
| onCloseDrawer={handleClose} | |||
| > | |||
| <form onSubmit={submitFiltersHandler}> | |||
| <div className="custom-drawer"> | |||
| <div> | |||
| <div className="custom-drawer-sub-card"> | |||
| <div className="custom-drawer-sub-card-label"> | |||
| <p>Kategorija</p> | |||
| </div> | |||
| <div className="custom-drawer-sub-card-content"> | |||
| <FormGroup> | |||
| {selectionLevelFilter.map((process) => ( | |||
| <FormControlLabel | |||
| key={process.id} | |||
| control={ | |||
| <Checkbox | |||
| value={process.name} | |||
| checked={process.isChecked} | |||
| onChange={() => onChangeFilterCheckboxes(process.id)} | |||
| /> | |||
| } | |||
| label={process.name} | |||
| /> | |||
| ))} | |||
| </FormGroup> | |||
| </div> | |||
| </div> | |||
| <div className="custom-drawer-sub-card"> | |||
| <div className="custom-drawer-sub-card-label"> | |||
| <p>Datum kreiranja</p> | |||
| </div> | |||
| <div className="custom-drawer-sub-card-content"> | |||
| <FormGroup style={{ marginBottom: "9px" }}> | |||
| <label className="custom-drawer-sub-card-content-sub-label"> | |||
| Od | |||
| </label> | |||
| <input | |||
| type="date" | |||
| onChange={(e) => setFromDate(e.target.value)} | |||
| value={fromDate} | |||
| /> | |||
| </FormGroup> | |||
| <FormGroup> | |||
| <label className="custom-drawer-sub-card-content-sub-label"> | |||
| Do | |||
| </label> | |||
| <input | |||
| type="date" | |||
| onChange={(e) => setToDate(e.target.value)} | |||
| value={toDate} | |||
| /> | |||
| </FormGroup> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| <div className="custom-drawer-submit"> | |||
| <button | |||
| className="c-btn c-btn--primary" | |||
| disabled={!hasSelectedFilters()} | |||
| > | |||
| Pretrazi | |||
| </button> | |||
| </div> | |||
| </div> | |||
| </form> | |||
| </CustomDrawer> | |||
| ); | |||
| }; | |||
| PatternFilters.propTypes = { | |||
| openFilterDrawer: PropTypes.bool, | |||
| handleClose: PropTypes.func, | |||
| selectionLevelFilter: PropTypes.any, | |||
| onChangeFilterCheckboxes: PropTypes.func, | |||
| }; | |||
| export default PatternFilters; | |||
| @@ -166,9 +166,11 @@ const AdDetailsPage = () => { | |||
| <button onClick={archiveAdsArrowLeftHandler}> | |||
| <img src={arrow_left} alt="arrow-left" /> | |||
| </button> | |||
| <button onClick={archiveAdsArrowRightHandler}> | |||
| <img src={arrow_right} alt="arrow-right" /> | |||
| </button> | |||
| {ad.applicants.length > 3 && ( | |||
| <button onClick={archiveAdsArrowRightHandler}> | |||
| <img src={arrow_right} alt="arrow-right" /> | |||
| </button> | |||
| )} | |||
| </div> | |||
| </div> | |||
| )} | |||
| @@ -199,9 +201,11 @@ const AdDetailsPage = () => { | |||
| <button onClick={archiveAdsArrowLeftHandler}> | |||
| <img src={arrow_left} alt="arrow-left" /> | |||
| </button> | |||
| <button onClick={archiveAdsArrowRightHandler}> | |||
| <img src={arrow_right} alt="arrow-right" /> | |||
| </button> | |||
| {ad.applicants.length > 3 && ( | |||
| <button onClick={archiveAdsArrowRightHandler}> | |||
| <img src={arrow_right} alt="arrow-right" /> | |||
| </button> | |||
| )} | |||
| </div> | |||
| )} | |||
| </div> | |||
| @@ -2,13 +2,7 @@ import React, { useState, useEffect } from "react"; | |||
| import PropTypes from "prop-types"; | |||
| import FilterButton from "../../components/Button/FilterButton"; | |||
| import { useTheme } from "@mui/system"; | |||
| import { | |||
| Checkbox, | |||
| FormControlLabel, | |||
| FormGroup, | |||
| IconButton, | |||
| useMediaQuery, | |||
| } from "@mui/material"; | |||
| import { IconButton, useMediaQuery } from "@mui/material"; | |||
| import userPageBtnIcon from "../../assets/images/userPageBtnIcon.png"; | |||
| import PatternCard from "../../components/Patterns/PatternCard"; | |||
| import { useDispatch, useSelector } from "react-redux"; | |||
| @@ -22,7 +16,7 @@ import xIcon from "../../assets/images/x.png"; | |||
| import { setProcessesReq } from "../../store/actions/processes/processesAction"; | |||
| import { createPatternReq } from "../../store/actions/createPattern/createPatternActions"; | |||
| import { updatePatternReq } from "../../store/actions/updatePattern/updatePatternActions"; | |||
| import CustomDrawer from "../../components/UI/CustomDrawer"; | |||
| import PatternFilters from "../../components/Patterns/PatternFilters"; | |||
| const PatternsPage = ({ history }) => { | |||
| const theme = useTheme(); | |||
| @@ -35,6 +29,7 @@ const PatternsPage = ({ history }) => { | |||
| const [addPatternTitle, setAddPatternTitle] = useState(""); | |||
| const [addPatternCategory, setAddPatternCategory] = useState(1); | |||
| const [addPatternMessage, setAddPatternMessage] = useState(""); | |||
| const [selectionLevelFilter, setSelectionLevelFilter] = useState([]); | |||
| const patterns = useSelector(selectPatterns); | |||
| const processes = useSelector(selectProcesses); | |||
| const dispatch = useDispatch(); | |||
| @@ -47,6 +42,11 @@ const PatternsPage = ({ history }) => { | |||
| useEffect(() => { | |||
| if (processes.length > 0) { | |||
| setAddPatternCategory(processes[0].id); | |||
| const tmpSelectionLevelFilter = processes.map((level) => ({ | |||
| ...level, | |||
| isChecked: false, | |||
| })); | |||
| setSelectionLevelFilter([...tmpSelectionLevelFilter]); | |||
| } | |||
| }, [processes]); | |||
| @@ -107,14 +107,27 @@ const PatternsPage = ({ history }) => { | |||
| setOpenEditPatternModal(false); | |||
| }; | |||
| const handleFilterCheckboxes = (id) => { | |||
| const tmpSelectionLevelFilter = selectionLevelFilter.map((level) => | |||
| level.id === id ? { ...level, isChecked: !level.isChecked } : level | |||
| ); | |||
| setSelectionLevelFilter(tmpSelectionLevelFilter); | |||
| }; | |||
| return ( | |||
| <> | |||
| <CustomDrawer | |||
| <PatternFilters | |||
| openFilterDrawer={openFilterDrawer} | |||
| handleClose={closeFilterDrawerHandler} | |||
| selectionLevelFilter={selectionLevelFilter} | |||
| onChangeFilterCheckboxes={handleFilterCheckboxes} | |||
| /> | |||
| {/* <CustomDrawer | |||
| title="Šabloni" | |||
| open={openFilterDrawer} | |||
| onCloseDrawer={closeFilterDrawerHandler} | |||
| > | |||
| <form> | |||
| <form onSubmit={submitFiltersHandler}> | |||
| <div className="custom-drawer"> | |||
| <div> | |||
| <div className="custom-drawer-sub-card"> | |||
| @@ -123,10 +136,19 @@ const PatternsPage = ({ history }) => { | |||
| </div> | |||
| <div className="custom-drawer-sub-card-content"> | |||
| <FormGroup> | |||
| {processes.map((process) => ( | |||
| {selectionLevelFilter.map((process) => ( | |||
| <FormControlLabel | |||
| key={process.id} | |||
| control={<Checkbox value={process.name} />} | |||
| control={ | |||
| <Checkbox | |||
| value={process.name} | |||
| checked={process.isChecked} | |||
| onChange={handleFilterCheckboxes.bind( | |||
| this, | |||
| process.id | |||
| )} | |||
| /> | |||
| } | |||
| label={process.name} | |||
| /> | |||
| ))} | |||
| @@ -142,13 +164,21 @@ const PatternsPage = ({ history }) => { | |||
| <label className="custom-drawer-sub-card-content-sub-label"> | |||
| Od | |||
| </label> | |||
| <input type="date" /> | |||
| <input | |||
| type="date" | |||
| onChange={(e) => setFromDate(e.target.value)} | |||
| value={fromDate} | |||
| /> | |||
| </FormGroup> | |||
| <FormGroup> | |||
| <label className="custom-drawer-sub-card-content-sub-label"> | |||
| Do | |||
| </label> | |||
| <input type="date" /> | |||
| <input | |||
| type="date" | |||
| onChange={(e) => setToDate(e.target.value)} | |||
| value={toDate} | |||
| /> | |||
| </FormGroup> | |||
| </div> | |||
| </div> | |||
| @@ -158,7 +188,7 @@ const PatternsPage = ({ history }) => { | |||
| </div> | |||
| </div> | |||
| </form> | |||
| </CustomDrawer> | |||
| </CustomDrawer> */} | |||
| <CustomModal | |||
| open={openAddPatternModal} | |||
| onCloseModal={closeAddPatternModalHandler} | |||
| @@ -42,6 +42,7 @@ export default { | |||
| patterns: { | |||
| allPatterns: base + "/patterns", | |||
| patternById: base + "/patterns/:id", | |||
| filteredPatterns: base + "/patterns/filter", | |||
| createPattern: base + "/patterns", | |||
| updatePattern: base + "/patterns/:id", | |||
| }, | |||
| @@ -5,6 +5,20 @@ export const getAllPatterns = () => | |||
| getRequest(apiEndpoints.patterns.allPatterns); | |||
| export const getPatternById = (id) => | |||
| getRequest(apiEndpoints.patterns.patternById.replace(":id", id)); | |||
| export const getFilteredPatterns = (payload) => { | |||
| let selectionLevelsQuery = ""; | |||
| for (let i = 0; i < payload.selectionLevels.length; i++) { | |||
| selectionLevelsQuery += `selectionLevels=${payload.selectionLevels[i]}&`; | |||
| } | |||
| return getRequest( | |||
| apiEndpoints.patterns.filteredPatterns + | |||
| `?fromDate=${ | |||
| payload.fromDate === null ? "" : new Date(payload.fromDate).toISOString() | |||
| }&toDate=${ | |||
| payload.toDate === null ? "" : new Date(payload.toDate).toISOString() | |||
| }&${selectionLevelsQuery}` | |||
| ); | |||
| }; | |||
| export const createPatternRequest = (payload) => | |||
| postRequest(apiEndpoints.patterns.createPattern, payload); | |||
| export const updatePatternRequest = (payload) => | |||
| @@ -5,7 +5,18 @@ import { | |||
| } from "../actionHelpers"; | |||
| const FETCH_PATTERNS_SCOPE = "FETCH_PATTERNS"; | |||
| const FILTER_PATTERNS_SCOPE = "FILTER_PATTERNS"; | |||
| export const FETCH_PATTERNS_REQ = createFetchType(FETCH_PATTERNS_SCOPE); | |||
| export const FETCH_PATTERNS_ERR = createErrorType(FETCH_PATTERNS_SCOPE); | |||
| export const FETCH_PATTERNS_SUCCESS = createSuccessType(FETCH_PATTERNS_SCOPE); | |||
| export const FETCH_FILTERED_PATTERNS_REQ = createFetchType( | |||
| FILTER_PATTERNS_SCOPE | |||
| ); | |||
| export const FETCH_FILTERED_PATTERNS_ERR = createErrorType( | |||
| FILTER_PATTERNS_SCOPE | |||
| ); | |||
| export const FETCH_FILTERED_PATTERNS_SUCCESS = createSuccessType( | |||
| FILTER_PATTERNS_SCOPE | |||
| ); | |||
| @@ -2,6 +2,9 @@ import { | |||
| FETCH_PATTERNS_REQ, | |||
| FETCH_PATTERNS_ERR, | |||
| FETCH_PATTERNS_SUCCESS, | |||
| FETCH_FILTERED_PATTERNS_REQ, | |||
| FETCH_FILTERED_PATTERNS_SUCCESS, | |||
| FETCH_FILTERED_PATTERNS_ERR, | |||
| } from "./patternsActionConstants"; | |||
| export const setPatternsReq = () => ({ | |||
| @@ -17,3 +20,18 @@ export const setPatterns = (payload) => ({ | |||
| type: FETCH_PATTERNS_SUCCESS, | |||
| payload, | |||
| }); | |||
| export const setFilteredPatternsReq = (payload) => ({ | |||
| type: FETCH_FILTERED_PATTERNS_REQ, | |||
| payload, | |||
| }); | |||
| export const setFilteredPatternsError = (payload) => ({ | |||
| type: FETCH_FILTERED_PATTERNS_ERR, | |||
| payload, | |||
| }); | |||
| export const setFilteredPatterns = (payload) => ({ | |||
| type: FETCH_FILTERED_PATTERNS_SUCCESS, | |||
| payload, | |||
| }); | |||
| @@ -1,4 +1,6 @@ | |||
| import { | |||
| FETCH_FILTERED_PATTERNS_ERR, | |||
| FETCH_FILTERED_PATTERNS_SUCCESS, | |||
| FETCH_PATTERNS_ERR, | |||
| FETCH_PATTERNS_SUCCESS, | |||
| } from "../../actions/patterns/patternsActionConstants"; | |||
| @@ -13,6 +15,8 @@ export default createReducer( | |||
| { | |||
| [FETCH_PATTERNS_SUCCESS]: setStatePatterns, | |||
| [FETCH_PATTERNS_ERR]: setPatternsErrorMessage, | |||
| [FETCH_FILTERED_PATTERNS_SUCCESS]: setStateFilteredPatterns, | |||
| [FETCH_FILTERED_PATTERNS_ERR]: setFilteredPatternsErrorMessage, | |||
| }, | |||
| initialState | |||
| ); | |||
| @@ -30,3 +34,17 @@ function setPatternsErrorMessage(state, action) { | |||
| errorMessage: action.payload, | |||
| }; | |||
| } | |||
| function setStateFilteredPatterns(state, action) { | |||
| return { | |||
| ...state, | |||
| patterns: action.payload, | |||
| }; | |||
| } | |||
| function setFilteredPatternsErrorMessage(state, action) { | |||
| return { | |||
| ...state, | |||
| errorMessage: action.payload, | |||
| }; | |||
| } | |||
| @@ -2,10 +2,13 @@ import { all, call, put, takeLatest } from "redux-saga/effects"; | |||
| import { | |||
| createPatternRequest, | |||
| getAllPatterns, | |||
| getFilteredPatterns, | |||
| getPatternById, | |||
| updatePatternRequest, | |||
| } from "../../request/patternsRequest"; | |||
| import { | |||
| setFilteredPatterns, | |||
| setFilteredPatternsError, | |||
| setPatterns, | |||
| setPatternsError, | |||
| } from "../actions/patterns/patternsActions"; | |||
| @@ -14,7 +17,10 @@ import { | |||
| createPattern, | |||
| createPatternError, | |||
| } from "../actions/createPattern/createPatternActions"; | |||
| import { FETCH_PATTERNS_REQ } from "../actions/patterns/patternsActionConstants"; | |||
| import { | |||
| FETCH_FILTERED_PATTERNS_REQ, | |||
| FETCH_PATTERNS_REQ, | |||
| } from "../actions/patterns/patternsActionConstants"; | |||
| import { FETCH_PATTERN_REQ } from "../actions/pattern/patternActionConstants"; | |||
| import { CREATE_PATTERN_REQ } from "../actions/createPattern/createPatternActionConstants"; | |||
| import { UPDATE_PATTERN_REQ } from "../actions/updatePattern/updatePatternActionConstants"; | |||
| @@ -22,6 +28,10 @@ import { | |||
| updatePattern, | |||
| updatePatternError, | |||
| } from "../actions/updatePattern/updatePatternActions"; | |||
| import { authScopeStringGetHelper } from "../../util/helpers/authScopeHelpers"; | |||
| import { JWT_TOKEN } from "../../constants/localStorage"; | |||
| import { addHeaderToken } from "../../request"; | |||
| import { rejectErrorCodeHelper } from "../../util/helpers/rejectErrorCodeHelper"; | |||
| export function* getPatterns() { | |||
| try { | |||
| @@ -41,6 +51,21 @@ export function* getPattern({ payload }) { | |||
| } | |||
| } | |||
| export function* filterPatterns({ payload }) { | |||
| try { | |||
| const JwtToken = yield call(authScopeStringGetHelper, JWT_TOKEN); | |||
| yield call(addHeaderToken, JwtToken); | |||
| const { data } = yield call(getFilteredPatterns, payload); | |||
| yield put(setFilteredPatterns(data)); | |||
| if (payload.handleApiResponseSuccess) { | |||
| yield call(payload.handleApiResponseSuccess); | |||
| } | |||
| } catch (error) { | |||
| const errorMessage = yield call(rejectErrorCodeHelper, error); | |||
| yield put(setFilteredPatternsError(errorMessage)); | |||
| } | |||
| } | |||
| export function* createPatternSaga({ payload }) { | |||
| try { | |||
| const result = yield call(createPatternRequest, payload); | |||
| @@ -67,6 +92,7 @@ export default function* adsSaga() { | |||
| yield all([ | |||
| takeLatest(FETCH_PATTERNS_REQ, getPatterns), | |||
| takeLatest(FETCH_PATTERN_REQ, getPattern), | |||
| takeLatest(FETCH_FILTERED_PATTERNS_REQ, filterPatterns), | |||
| takeLatest(CREATE_PATTERN_REQ, createPatternSaga), | |||
| takeLatest(UPDATE_PATTERN_REQ, updatePatternSaga), | |||
| ]); | |||