| @@ -6,8 +6,8 @@ | |||
| } | |||
| .files-page-card { | |||
| margin-bottom: 1.5rem; | |||
| } | |||
| margin-bottom: 1.5rem; | |||
| } | |||
| .files-page-card-title { | |||
| margin-bottom: 1rem; | |||
| @@ -32,9 +32,9 @@ | |||
| } | |||
| .files-page-card-add-button { | |||
| padding-bottom: 36px; | |||
| display: flex; | |||
| justify-content: space-between; | |||
| padding-bottom: 36px; | |||
| display: flex; | |||
| justify-content: space-between; | |||
| } | |||
| .files-page-drag-and-drop { | |||
| @@ -45,11 +45,56 @@ | |||
| padding-right: 35px !important; | |||
| } | |||
| .search-field{ | |||
| .search-field { | |||
| margin-right: 30px; | |||
| margin-left: 0px; | |||
| } | |||
| .filesPage{ | |||
| .filesPage { | |||
| align-self: flex-end; | |||
| } | |||
| .files-page-categories { | |||
| display: flex; | |||
| flex-wrap: wrap; | |||
| } | |||
| .files-page-categories-category { | |||
| width: calc(100% / 4) !important; | |||
| margin: 0 !important; | |||
| padding: 16px; | |||
| } | |||
| .files-page-category-button { | |||
| display: flex; | |||
| flex-direction: row; | |||
| justify-content: center; | |||
| align-items: center; | |||
| padding: 18px 72px; | |||
| gap: 10px; | |||
| width: 100%; | |||
| height: 100%; | |||
| background: #226cb0; | |||
| border-radius: 9px; | |||
| @include media-below($bp-xl) { | |||
| width: 147px; | |||
| } | |||
| } | |||
| .add-file-message { | |||
| display: flex; | |||
| align-items: center !important; | |||
| justify-content: center !important; | |||
| margin: 16px 0; | |||
| } | |||
| .add-file-message p { | |||
| color: green; | |||
| font-weight: bold; | |||
| font-size: 1.25rem; | |||
| letter-spacing: 2px; | |||
| } | |||
| .files-view-page-delete-btn { | |||
| margin: 0 auto !important; | |||
| } | |||
| @@ -12,12 +12,12 @@ import x from "../../assets/images/x.png"; | |||
| import { useDispatch, useSelector } from "react-redux"; | |||
| import { | |||
| getFilesReq, | |||
| updateFileFilterCat, | |||
| // updateFileFilterCat, | |||
| updateFileFilterExt, | |||
| updateFileFilterTag, | |||
| } from "../../store/actions/files/fileActions"; | |||
| const DocsFilters = ({ open, handleClose, setPage }) => { | |||
| const DocsFilters = ({ open, handleClose, setPage, category }) => { | |||
| const dispatch = useDispatch(); | |||
| const { filters } = useSelector((s) => s.fileFilters); | |||
| @@ -25,16 +25,16 @@ const DocsFilters = ({ open, handleClose, setPage }) => { | |||
| dispatch(updateFileFilterExt(e.target.value)); | |||
| const handleCheckboxesTags = (e) => | |||
| dispatch(updateFileFilterTag(e.target.value)); | |||
| const handleCheckboxesCat = (e) => | |||
| dispatch(updateFileFilterCat(e.target.value)); | |||
| // const handleCheckboxesCat = (e) => | |||
| // dispatch(updateFileFilterCat(e.target.value)); | |||
| const submitFiltersHandler = (e) => { | |||
| e.preventDefault(); | |||
| var catFilters = []; | |||
| filters.categories | |||
| ?.filter((n) => n.isChecked) | |||
| .forEach((m) => catFilters.push(m.name)); | |||
| var catFilters = [category && category]; | |||
| // filters.categories | |||
| // ?.filter((n) => n.isChecked) | |||
| // .forEach((m) => catFilters.push(m.name)); | |||
| var extFilters = []; | |||
| filters.extensions | |||
| @@ -138,7 +138,7 @@ const DocsFilters = ({ open, handleClose, setPage }) => { | |||
| ))} | |||
| </FormGroup> | |||
| </div> | |||
| <div | |||
| {/* <div | |||
| style={{ paddingTop: "10px" }} | |||
| className="custom-drawer-sub-card-label" | |||
| > | |||
| @@ -161,7 +161,7 @@ const DocsFilters = ({ open, handleClose, setPage }) => { | |||
| /> | |||
| ))} | |||
| </FormGroup> | |||
| </div> | |||
| </div> */} | |||
| </div> | |||
| <div className="custom-drawer-submit"> | |||
| <button | |||
| @@ -195,6 +195,7 @@ DocsFilters.propTypes = { | |||
| open: PropType.any, | |||
| handleClose: PropType.func, | |||
| setPage: PropType.func, | |||
| category: PropType.string | |||
| }; | |||
| export default DocsFilters; | |||
| @@ -17,10 +17,11 @@ import { uploadFileReq } from "../../store/actions/uploadFile/uploadFileActions" | |||
| import { FILES_VIEW_PAGE } from "../../constants/pages"; | |||
| import PropTypes from "prop-types"; | |||
| const AddFile = ({history}) => { | |||
| const AddFile = ({ history }) => { | |||
| const [dropzoneActive, setDropzoneActive] = useState(false); | |||
| const [pdfFile, setPdfFile] = useState(null); | |||
| const [title, setTitle] = useState(""); | |||
| const [showMessage, setShowMessage] = useState(false); | |||
| const dispatch = useDispatch(); | |||
| const categories = useSelector(selectCategories); | |||
| const tags = useSelector(selectTags); | |||
| @@ -35,9 +36,18 @@ const AddFile = ({history}) => { | |||
| const onSuccessUploadFile = () => { | |||
| dispatch(resetIsCheckedTagsValue()); | |||
| setPdfFile(null); | |||
| setTitle("") | |||
| setTitle(""); | |||
| setShowMessage(true); | |||
| }; | |||
| useEffect(() => { | |||
| if (showMessage === true) { | |||
| setTimeout(() => { | |||
| setShowMessage(false); | |||
| }, 1000); | |||
| } | |||
| }, [showMessage]); | |||
| const handleDrop = (e) => { | |||
| e.preventDefault(); | |||
| const selectedFile = e.dataTransfer.files[0]; | |||
| @@ -75,6 +85,8 @@ const AddFile = ({history}) => { | |||
| return ( | |||
| <div className="files-page"> | |||
| <div className="l-t-rectangle"></div> | |||
| <div className="r-b-rectangle"></div> | |||
| <div className="files-page-card"> | |||
| <div className="files-page-card-title"> | |||
| <h1>Title</h1> | |||
| @@ -200,8 +212,18 @@ const AddFile = ({history}) => { | |||
| </div> | |||
| </div> | |||
| <div | |||
| className="add-file-message" | |||
| style={showMessage === false ? { display: "none" } : { display: "flex" }} | |||
| > | |||
| <p>Uspesno dodat fajl</p> | |||
| </div> | |||
| <div className="files-page-card"> | |||
| <div className="files-page-card-add-button" style={{alignItems:'center'}}> | |||
| <div | |||
| className="files-page-card-add-button" | |||
| style={{ alignItems: "center" }} | |||
| > | |||
| <p | |||
| className="applicant-ads-back-button" | |||
| onClick={() => history.push(FILES_VIEW_PAGE)} | |||
| @@ -1,21 +1,65 @@ | |||
| import React, { useEffect } from 'react' | |||
| import React, { useEffect } from "react"; | |||
| import PropTypes from "prop-types"; | |||
| import { useSelector, useDispatch } from "react-redux"; | |||
| import IconButton from "../../components/IconButton/IconButton"; | |||
| import { setCategoriesReq } from "../../store/actions/categories/categoriesAction"; | |||
| import { selectCategories } from "../../store/selectors/categoriesSelector"; | |||
| import { useDispatch, useSelector } from 'react-redux'; | |||
| import table from "../../assets/images/table.png"; | |||
| import { FILES_VIEW_PAGE } from "../../constants/pages"; | |||
| const FilesPage = () => { | |||
| const dispatch = useDispatch() | |||
| const FilesPage = ({ history }) => { | |||
| const categories = useSelector(selectCategories); | |||
| const dispatch = useDispatch(); | |||
| useEffect(() => { | |||
| dispatch(setCategoriesReq()); | |||
| }) | |||
| }, []); | |||
| return ( | |||
| <div> | |||
| {categories.map((category,index) => ( | |||
| <p key={index}>{category.name}</p> | |||
| )) } | |||
| </div> | |||
| ) | |||
| } | |||
| <> | |||
| <div className="l-t-rectangle"></div> | |||
| <div className="r-b-rectangle"></div> | |||
| <div className="pl-144" style={{ paddingTop: "36px" }}> | |||
| <div style={{ marginBottom: "39px" }}> | |||
| <h1 className="page-heading">Kategorije</h1> | |||
| </div> | |||
| <div className="files-page-categories"> | |||
| {categories && | |||
| categories.map((category) => ( | |||
| <div className="files-page-categories-category" key={category.id}> | |||
| <IconButton | |||
| className="c-btn c-btn--primary-outlined files-page-category-button" | |||
| data-testid="pattern-details-send-email" | |||
| onClick={() => | |||
| history.push(FILES_VIEW_PAGE, { category: category.name }) | |||
| } | |||
| > | |||
| <img | |||
| style={{ | |||
| marginRight: "5px", | |||
| width: "12px", | |||
| height: "12px", | |||
| }} | |||
| src={table} | |||
| /> | |||
| {category.name} | |||
| </IconButton> | |||
| </div> | |||
| ))} | |||
| </div> | |||
| </div> | |||
| </> | |||
| ); | |||
| }; | |||
| FilesPage.propTypes = { | |||
| history: PropTypes.shape({ | |||
| replace: PropTypes.func, | |||
| push: PropTypes.func, | |||
| location: PropTypes.shape({ | |||
| pathname: PropTypes.string, | |||
| }), | |||
| }), | |||
| }; | |||
| export default FilesPage | |||
| export default FilesPage; | |||
| @@ -8,6 +8,7 @@ import FilterButton from "../../components/Button/FilterButton"; | |||
| import DocsFilters from "../../components/Docs/DocsFilters"; | |||
| import { FETCH_FILES_LOADING } from "../../store/actions/files/fileActionConstants"; | |||
| import { | |||
| deleteFileReq, | |||
| getFileFiltersReq, | |||
| getFilesReq, | |||
| } from "../../store/actions/files/fileActions"; | |||
| @@ -17,20 +18,27 @@ import Fade from "@mui/material/Fade"; | |||
| import { useTranslation } from "react-i18next"; | |||
| import searchImage from "../../assets/images/search.png"; | |||
| import IconButton from "../../components/IconButton/IconButton"; | |||
| import { ADD_FILE } from "../../constants/pages"; | |||
| import { ADD_FILE, FILES_PAGE } from "../../constants/pages"; | |||
| import PropTypes from "prop-types"; | |||
| import { useLocation } from "react-router"; | |||
| import deleteIcon from "../../assets/images/delete.png"; | |||
| const FilesViewPage = ({history}) => { | |||
| const FilesViewPage = ({ history }) => { | |||
| const [toggleFiltersDrawer, setToggleFiltersDrawer] = useState(false); | |||
| const [page, setPage] = useState(1); | |||
| const { filters } = useSelector((s) => s.fileFilters); | |||
| const { data } = useSelector((s) => s.files); | |||
| const { t } = useTranslation(); | |||
| const [isSearchFieldVisible, setIsSearchFieldVisible] = useState(false); | |||
| const { state } = useLocation(); | |||
| // const [timer, setTimer] = useState(null); | |||
| const dispatch = useDispatch(); | |||
| if (state === undefined) { | |||
| history.replace(FILES_PAGE); | |||
| } | |||
| const handleToggleFiltersDrawer = () => { | |||
| setToggleFiltersDrawer((oldState) => !oldState); | |||
| }; | |||
| @@ -42,7 +50,7 @@ const FilesViewPage = ({history}) => { | |||
| payload: { | |||
| pageSize: 6, | |||
| currentPage: page, | |||
| categories: [], | |||
| categories: [state && state.category], | |||
| extensions: [], | |||
| tags: [], | |||
| content: "", | |||
| @@ -52,10 +60,10 @@ const FilesViewPage = ({history}) => { | |||
| }, []); | |||
| const handleChange = (_, value) => { | |||
| var catFilters = []; | |||
| filters.categories | |||
| ?.filter((n) => n.isChecked) | |||
| .forEach((m) => catFilters.push(m.name)); | |||
| var catFilters = [state && state.category]; | |||
| // filters.categories | |||
| // ?.filter((n) => n.isChecked) | |||
| // .forEach((m) => catFilters.push(m.name)); | |||
| var extFilters = []; | |||
| filters.extensions | |||
| @@ -98,13 +106,13 @@ const FilesViewPage = ({history}) => { | |||
| dispatch(setContent(value)); | |||
| }; | |||
| const deleteFileHandler = (stream_id) => { | |||
| dispatch(deleteFileReq({ id: stream_id })); | |||
| }; | |||
| const handleKeyDown = (event) => { | |||
| console.log(filters.content); | |||
| if (event.key === "Enter" && filters.content !== "") { | |||
| var catFilters = []; | |||
| filters.categories | |||
| ?.filter((n) => n.isChecked) | |||
| .forEach((m) => catFilters.push(m.name)); | |||
| var catFilters = [state && state.category]; | |||
| var extFilters = []; | |||
| filters.extensions | |||
| @@ -161,9 +169,9 @@ const FilesViewPage = ({history}) => { | |||
| open={toggleFiltersDrawer} | |||
| handleClose={handleToggleFiltersDrawer} | |||
| setPage={setPage} | |||
| category={state && state.category} | |||
| /> | |||
| <div | |||
| // onClick={() => setIsSearchFieldVisible(false)} | |||
| className="pl-144 flex-center" | |||
| style={{ | |||
| paddingTop: "36px", | |||
| @@ -193,7 +201,6 @@ const FilesViewPage = ({history}) => { | |||
| </Fade> | |||
| </div> | |||
| <div className="flex-center"> | |||
| {/* <button></button> */} | |||
| <IconButton | |||
| className="c-btn c-btn--primary-outlined candidate-btn search-field userPageBtn ml-20px no-padding custom-filter-button" | |||
| onClick={handleChangeVisibility} | |||
| @@ -206,7 +213,6 @@ const FilesViewPage = ({history}) => { | |||
| </div> | |||
| <div | |||
| className="pl-144" | |||
| // onClick={() => setIsSearchFieldVisible(false)} | |||
| style={{ | |||
| display: "flex", | |||
| marginTop: "39px", | |||
| @@ -243,6 +249,11 @@ const FilesViewPage = ({history}) => { | |||
| {/* Document size (MB) */} | |||
| Veličina dokumenta | |||
| </th> | |||
| <th> | |||
| {/* {t("users.position")} */} | |||
| {/* Document size (MB) */} | |||
| Obriši dokument | |||
| </th> | |||
| {/* <th> */} | |||
| {/* {t("users.position")} */} | |||
| {/* Document size (MB) */} | |||
| @@ -261,6 +272,20 @@ const FilesViewPage = ({history}) => { | |||
| <td className="docs-name">{n.title}</td> | |||
| <td>{n.file_type && n.file_type}</td> | |||
| <td className="profession">{n.cached_file_size}kB</td> | |||
| <td className="profession"> | |||
| <IconButton | |||
| className="c-btn c-btn--primary-outlined files-view-page-delete-btn" | |||
| onClick={() => deleteFileHandler(n.stream_id)} | |||
| > | |||
| <img | |||
| style={{ | |||
| width: "12px", | |||
| height: "12px", | |||
| }} | |||
| src={deleteIcon} | |||
| /> | |||
| </IconButton> | |||
| </td> | |||
| </tr> | |||
| ))} | |||
| </tbody> | |||
| @@ -83,5 +83,6 @@ export default { | |||
| files: { | |||
| uploadFile: base + "/files", | |||
| all: base + "/files/filtered", | |||
| deleteFile: base + "/files/delete-file", | |||
| }, | |||
| }; | |||
| @@ -1,5 +1,7 @@ | |||
| import { postRequest } from "."; | |||
| import { deleteRequest, postRequest } from "."; | |||
| import apiEndpoints from "./apiEndpoints"; | |||
| export const uploadFileRequest = (payload) => | |||
| postRequest(apiEndpoints.files.uploadFile, payload); | |||
| export const deleteFileRequest = (id) => | |||
| deleteRequest(apiEndpoints.files.deleteFile + "/" + id); | |||
| @@ -20,3 +20,8 @@ export const FETCH_FILES_REQ = createFetchType(FETCH_FILES_SCOPE); | |||
| export const FETCH_FILES_ERR = createErrorType(FETCH_FILES_SCOPE); | |||
| export const FETCH_FILES_SUCCESS = createSuccessType(FETCH_FILES_SCOPE); | |||
| export const FETCH_FILES_LOADING = createLoadingType(FETCH_FILES_SCOPE); | |||
| const DELETE_FILE_SCOPE = "DELETE_FILE" | |||
| export const DELETE_FILE_REQ = createFetchType(DELETE_FILE_SCOPE) | |||
| export const DELETE_FILE_ERR = createErrorType(DELETE_FILE_SCOPE) | |||
| export const DELETE_FILE_SUCCESS = createSuccessType(DELETE_FILE_SCOPE) | |||
| @@ -8,7 +8,10 @@ import { | |||
| UPDATE_FILTERS_CATEGORY, | |||
| UPDATE_FILTERS_EXTENSION, | |||
| UPDATE_FILTERS_TAG, | |||
| SET_CONTENT | |||
| SET_CONTENT, | |||
| DELETE_FILE_REQ, | |||
| DELETE_FILE_ERR, | |||
| DELETE_FILE_SUCCESS | |||
| } from "./fileActionConstants"; | |||
| export const getFileFiltersReq = () => ({ | |||
| @@ -56,4 +59,19 @@ export const getFileSuccess = (payload) => ({ | |||
| export const setContent = (payload) => ({ | |||
| type:SET_CONTENT, | |||
| payload | |||
| }) | |||
| }) | |||
| export const deleteFileReq = (payload) => ({ | |||
| type: DELETE_FILE_REQ, | |||
| payload | |||
| }); | |||
| export const deleteFileError = (payload) => ({ | |||
| type: DELETE_FILE_ERR, | |||
| payload, | |||
| }); | |||
| export const deleteFileAction = (payload) => ({ | |||
| type: DELETE_FILE_SUCCESS, | |||
| payload | |||
| }); | |||
| @@ -3,6 +3,10 @@ import { | |||
| FETCH_FILES_ERR, | |||
| FETCH_FILES_SUCCESS, | |||
| } from "../../actions/files/fileActionConstants"; | |||
| import { | |||
| DELETE_FILE_ERR, | |||
| DELETE_FILE_SUCCESS, | |||
| } from "../../actions/files/fileActionConstants"; | |||
| const initialState = { | |||
| data: {}, | |||
| @@ -13,6 +17,8 @@ export default createReducer( | |||
| { | |||
| [FETCH_FILES_SUCCESS]: setFiles, | |||
| [FETCH_FILES_ERR]: setFilesErrorMessage, | |||
| [DELETE_FILE_SUCCESS]: deleteFileReducer, | |||
| [DELETE_FILE_ERR]: deleteFileReducerError, | |||
| }, | |||
| initialState | |||
| ); | |||
| @@ -30,3 +36,15 @@ function setFilesErrorMessage(state, action) { | |||
| fetchFilesErrorMessage: action.payload, | |||
| }; | |||
| } | |||
| function deleteFileReducer(state, action) { | |||
| const newArr = state.data.data.filter(x => x.stream_id !== action.payload) | |||
| return { ...state, data: {...state.data, data: newArr} }; | |||
| } | |||
| function deleteFileReducerError(state, action) { | |||
| return { | |||
| ...state, | |||
| fetchFilesErrorMessage: action.payload, | |||
| }; | |||
| } | |||
| @@ -78,5 +78,5 @@ export default combineReducers({ | |||
| categories: categoriesReducer, | |||
| tags: tagsReducer, | |||
| fileFilters: fileFiltersReducer, | |||
| files: getFilesReducer | |||
| files: getFilesReducer, | |||
| }); | |||
| @@ -8,10 +8,10 @@ import { JWT_TOKEN } from "../../constants/localStorage"; | |||
| import { addHeaderToken } from "../../request"; | |||
| import { rejectErrorCodeHelper } from "../../util/helpers/rejectErrorCodeHelper"; | |||
| import { UPLOAD_FILE_REQ } from "../actions/uploadFile/uploadFileActionConstants"; | |||
| import { uploadFileRequest } from "../../request/filesRequest"; | |||
| import { deleteFileRequest, uploadFileRequest } from "../../request/filesRequest"; | |||
| import { getAllFilesReq } from "../../request/fileRequests"; | |||
| import { FETCH_FILES_REQ } from "../actions/files/fileActionConstants"; | |||
| import { getFileError, getFileSuccess } from "../actions/files/fileActions"; | |||
| import { DELETE_FILE_REQ, FETCH_FILES_REQ } from "../actions/files/fileActionConstants"; | |||
| import { deleteFileAction, deleteFileError, getFileError, getFileSuccess } from "../actions/files/fileActions"; | |||
| export function* uploadFileSaga({ payload }) { | |||
| try { | |||
| @@ -46,7 +46,23 @@ export function* getAll({ payload }) { | |||
| } | |||
| } | |||
| } | |||
| export function* deleteFileSaga({ payload }) { | |||
| try { | |||
| const JwtToken = yield call(authScopeStringGetHelper, JWT_TOKEN); | |||
| yield call(addHeaderToken, JwtToken); | |||
| yield call(deleteFileRequest, payload.id); | |||
| yield put(deleteFileAction(payload.id)) | |||
| } catch (error) { | |||
| if (error.response && error.response.data) { | |||
| const errorMessage = yield call(rejectErrorCodeHelper, error); | |||
| yield put(deleteFileError(errorMessage)); | |||
| } | |||
| } | |||
| } | |||
| export default function* filesSaga() { | |||
| yield all([takeLatest(UPLOAD_FILE_REQ, uploadFileSaga)]); | |||
| yield all([takeEvery(FETCH_FILES_REQ, getAll)]); | |||
| yield all([takeLatest(DELETE_FILE_REQ, deleteFileSaga)]); | |||
| } | |||