| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533 |
- import React, { useState, useEffect } from "react";
- import IconButton from "../../components/IconButton/IconButton";
- import deleteIcon from "../../assets/images/delete.png";
- import editIcon from "../../assets/images/edit.png";
- import { useTranslation } from "react-i18next";
- import PropTypes from "prop-types";
- import { useSelector, useDispatch } from "react-redux";
- import {
- deleteFileReq,
- getFileFiltersReq,
- getFilesReq,
- setContent,
- updateFileReq,
- } from "../../store/actions/files/fileActions";
- import { Fade, Pagination } from "@mui/material";
- import FilterButton from "../../components/Button/FilterButton";
- import searchImage from "../../assets/images/search.png";
- import DocsFilters from "../../components/Docs/DocsFilters";
- import ConfirmDialog from "../../components/MUI/ConfirmDialog";
- import FileViewer from "react-file-viewer";
- import CustomModal from "../../components/UI/CustomModal";
- import xIcon from "../../assets/images/x.png";
- import Button from "../../components/Button/Button";
- import { selectIsLoadingByActionType } from "../../store/selectors/loadingSelectors";
- import { FETCH_FILES_LOADING } from "../../store/actions/files/fileActionConstants";
- import { useParams } from "react-router";
- import { PAGE_SIZE_FILES } from "../../constants/keyCodeConstants";
-
- const FileTable = ({ trigger }) => {
- const [file, setFile] = useState(null);
- const { data } = useSelector((s) => s.files);
- const { filters } = useSelector((s) => s.fileFilters);
- const [page, setPage] = useState(1);
- const [toggleFiltersDrawer, setToggleFiltersDrawer] = useState(false);
- const [isSearchFieldVisible, setIsSearchFieldVisible] = useState(false);
- let { id } = useParams();
- const [fileForDelete, setFileForDelete] = useState({
- open: false,
- title: "",
- streamId: null,
- });
- const [openNoteModal, setOpenNoteModal] = useState({
- open: false,
- note: null,
- streamId: "",
- });
- const { t } = useTranslation();
- const dispatch = useDispatch();
-
- useEffect(() => {
- if (data !== null && data.data !== undefined) {
- if (data.data.length === 0) {
- setPage(getTotalNumberOfPages(data) - 1);
- }
- }
- }, [data]);
-
- const getTotalNumberOfPages = (data) => {
- return parseInt(data.total) <= PAGE_SIZE_FILES
- ? 1
- : Math.ceil(parseInt(data.total) / PAGE_SIZE_FILES);
- };
-
- useEffect(() => {
- setFile(null);
- }, [id]);
-
- useEffect(() => {
- if (trigger) {
- handleChangeVisibility();
- }
- }, [trigger]);
-
- const handleToggleFiltersDrawer = () => {
- setToggleFiltersDrawer((oldState) => !oldState);
- };
-
- useEffect(() => {
- handleFilters(page);
- }, [page]);
-
- useEffect(() => {
- dispatch(getFileFiltersReq());
- dispatch(
- getFilesReq({
- payload: {
- pageSize: PAGE_SIZE_FILES,
- currentPage: page,
- categoryId: id,
- extensions: [],
- tags: [],
- content: "",
- },
- })
- );
- }, [id, page]);
-
- const handleChange = (_, value) => {
- setFile(null);
- handleFilters(value);
- setPage(value);
- };
-
- const handleFilters = (value) => {
- var extFilters = [];
- filters.extensions
- ?.filter((n) => n.isChecked)
- .forEach((m) => extFilters.push(m.name));
-
- var tagFilters = [];
- filters.tags
- ?.filter((n) => n.isChecked)
- .forEach((m) => tagFilters.push(m.name));
-
- dispatch(
- getFilesReq({
- payload: {
- pageSize: PAGE_SIZE_FILES,
- currentPage: value,
- categoryId: id,
- extensions: extFilters,
- tags: tagFilters,
- content: filters.content === undefined ? "" : filters.content,
- },
- })
- );
- };
-
- const isLoading = useSelector(
- selectIsLoadingByActionType(FETCH_FILES_LOADING)
- );
-
- const handleChangeVisibility = () => {
- setIsSearchFieldVisible(false);
- };
-
- const stopPropagation = (e) => {
- e.stopPropagation();
- };
-
- const handleChangeContent = (value) => {
- dispatch(setContent(value));
- };
-
- const deleteFileHandler = (stream_id) => {
- dispatch(deleteFileReq({ id: stream_id }));
- if (file !== null && stream_id === file.streamId) {
- setFile(null);
- }
- };
-
- const handleKeyDown = (event) => {
- if (event.key === "Enter" && filters.content !== "" && filters.content !== undefined) {
- var extFilters = [];
- filters.extensions
- ?.filter((n) => n.isChecked)
- .forEach((m) => extFilters.push(m.name));
-
- var tagFilters = [];
- filters.tags
- ?.filter((n) => n.isChecked)
- .forEach((m) => tagFilters.push(m.name));
-
- dispatch(
- getFilesReq({
- payload: {
- pageSize: PAGE_SIZE_FILES,
- currentPage: page,
- categoryId: id,
- extensions: extFilters,
- tags: tagFilters,
- content: filters.content,
- },
- })
- );
- }
- };
-
- const input = (
- <div>
- <input
- placeholder="Pretrazi..."
- value={filters.content}
- onChange={(e) => handleChangeContent(e.target.value)}
- onKeyDown={handleKeyDown}
- className="candidate-search-field"
- onClick={stopPropagation}
- role="input"
- />
- </div>
- );
-
- const onError = (e) => {
- console.log(e, "error in file-viewer");
- };
-
- const displayFile = (fileStream, streamId, fileType) => {
- setFile({ fileStream, streamId, fileType });
- };
-
- const getPathForFile = (fileType, fileStream) => {
- if (fileType === "docx") {
- return `data:application/vnd.openxmlformats-officedocument.wordprocessingml.document;base64,${fileStream}`;
- } else if (fileType === "doc") {
- return `data:application/msword;base64,${fileStream}`;
- } else {
- return `data:application/pdf;base64,${fileStream}`;
- }
- };
-
- const updateFileNoteHandler = () => {
- dispatch(
- updateFileReq({
- id: openNoteModal.streamId,
- data: openNoteModal.note,
- onSuccessOpenNoteModal,
- })
- );
- };
-
- const onSuccessOpenNoteModal = () => {
- setOpenNoteModal({
- open: false,
- note: null,
- streamId: "",
- });
- };
-
- return isLoading ? (
- <div>
- <div className="l-t-rectangle"></div>
- <div className="r-b-rectangle"></div>
- <div className="loader-container h-withHeader">
- <span>Loading</span>
- </div>
- </div>
- ) : (
- <div>
- <ConfirmDialog
- open={fileForDelete.open}
- title="Brisanje dokumenta"
- subtitle={fileForDelete.title}
- imgSrc={deleteIcon}
- content="Da li ste sigurni za brisanje dokumenta?"
- onClose={() => {
- setFileForDelete({ open: false, title: "", streamId: null });
- }}
- onConfirm={() => {
- deleteFileHandler(fileForDelete.streamId);
- setFileForDelete({ open: false, title: "", streamId: null });
- }}
- />
- <CustomModal
- classes="files-custom-modal"
- open={openNoteModal.open}
- onCloseModal={() =>
- setOpenNoteModal({ open: false, note: null, streamId: "" })
- }
- >
- <div className="add-pattern-modal-header">
- <div className="add-pattern-modal-header-title">
- <div className="add-pattern-modal-header-title-image">
- <img src={editIcon} alt="plus" />
- </div>
- <div className="add-pattern-modal-header-title-title">
- <p>Edit text</p>
- </div>
- <div className="add-pattern-modal-header-title-sub">
- <sub> | Note</sub>
- </div>
- </div>
- <div
- className="add-pattern-modal-header-close"
- onClick={() =>
- setOpenNoteModal({ open: false, note: null, streamId: "" })
- }
- >
- <img src={xIcon} alt="close" />
- </div>
- </div>
- <div className="files-edit-note">
- <textarea
- className="files-note"
- value={openNoteModal.note}
- onChange={(e) =>
- setOpenNoteModal((oldState) => ({
- ...oldState,
- note: e.target.value,
- }))
- }
- />
- </div>
- <div className="files-custom-modal-buttons">
- <Button
- type="button"
- variant="contained"
- className="c-btn c-btn--primary-outlined"
- onClick={() =>
- setOpenNoteModal({ open: false, note: null, streamId: "" })
- }
- >
- Close
- </Button>
- <Button
- type="button"
- variant="contained"
- className="c-btn c-btn--primary"
- onClick={updateFileNoteHandler}
- >
- Save Changes
- </Button>
- </div>
- </CustomModal>
- <DocsFilters
- open={toggleFiltersDrawer}
- handleClose={handleToggleFiltersDrawer}
- setPage={setPage}
- setFile={setFile}
- />
-
- <div
- style={{
- display: "flex",
- justifyContent: "space-between",
- position: "relative",
- width: "calc(100% - 144px)",
- }}
- >
- <div>
- <Fade in={isSearchFieldVisible} timeout={500} className="proba">
- {input}
- </Fade>
- <Fade in={isSearchFieldVisible} timeout={500}>
- <div
- style={{
- position: "absolute",
- zIndex: 10000,
- left: 780,
- top: 20,
- marginTop: 0,
- }}
- >
- <img src={searchImage} />
- </div>
- </Fade>
- </div>
- <div style={{ display: "flex" }}>
- {isSearchFieldVisible ? (
- ""
- ) : (
- <IconButton
- className="c-btn c-btn--primary-outlined candidate-btn search-field userPageBtn ml-20px no-padding custom-filter-button"
- onClick={() => setIsSearchFieldVisible(true)}
- >
- {t("candidates.search")}
- <img
- src={searchImage}
- alt="filter"
- className="candidates-image"
- />
- </IconButton>
- )}
- <FilterButton onShowFilters={handleToggleFiltersDrawer} />
- </div>
- </div>
- <div
- style={{
- display: "flex",
- marginTop: "39px",
- flexDirection: "column",
- justifyContent: "space-between",
- minHeight: "500px",
- width: "100%",
- }}
- >
- <div className="table-cont">
- <div
- style={{
- display: "flex",
- height: "300px",
- width: "calc(100% - 144px)",
- }}
- >
- <table
- className={"usersTable-users mini"}
- style={{
- width: file === null ? "100%" : "400px",
- height: "50%",
- }}
- >
- <thead>
- <tr
- className="headingRow headingRowFiles"
- style={{ cursor: "pointer" }}
- >
- {file === null ? <th>Putanja dokumenta</th> : ""}
- <th>Naziv dokumenta</th>
- {file === null ? <th>Tip dokumenta</th> : ""}
- {file === null ? <th>Veličina dokumenta</th> : ""}
- {file === null ? <th>Obrisi dokument</th> : ""}
- {file === null ? <th>Note</th> : ""}
- <th>Preuzmi dokument</th>
- </tr>
- </thead>
- <tbody>
- {data.data?.map((n, index) => (
- <tr
- key={index}
- className="secondaryRow"
- onClick={() =>
- displayFile(n.file_stream, n.stream_id, n.file_type)
- }
- >
- {file === null ? (
- <td className="docs-name">{n.fileName}</td>
- ) : (
- ""
- )}
- <td className="docs-name">{n.title}</td>
- {file === null ? <td>{n.file_type && n.file_type}</td> : ""}
-
- {file === null ? (
- <td className="profession">{n.cached_file_size}kB</td>
- ) : (
- ""
- )}
- {file === null ? (
- <td className="profession">
- <IconButton
- className="c-btn c-btn--primary-outlined files-view-page-delete-btn"
- onClick={() =>
- setFileForDelete({
- open: true,
- title: n.title,
- streamId: n.stream_id,
- })
- }
- >
- <img
- style={{ width: "12px", height: "12px" }}
- src={deleteIcon}
- />
- </IconButton>
- </td>
- ) : (
- ""
- )}
- {file === null ? (
- <td className="profession">
- <IconButton
- className="c-btn c-btn--primary-outlined files-view-page-delete-btn"
- onClick={() => {
- setOpenNoteModal({
- open: true,
- note: n.note,
- streamId: n.stream_id,
- });
- }}
- >
- <img
- style={{ width: "12px", height: "12px" }}
- src={editIcon}
- />
- </IconButton>
- </td>
- ) : (
- ""
- )}
- <td>
- <div onClick={stopPropagation}>
- <a
- className="applicant-cv-button"
- style={{
- width: "100px",
- height: "40px",
- padding: 8,
- }}
- download={n.title}
- href={getPathForFile(n.file_type, n.file_stream)}
- >
- {t("common.download")}
- </a>
- </div>
- </td>
- </tr>
- ))}
- </tbody>
- </table>
- {file !== null ? (
- <div
- style={{
- width: "830px",
- marginLeft: "30px",
- overflowY: "visible",
- }}
- >
- <FileViewer
- fileType={file.fileType}
- filePath={getPathForFile(file.fileType, file.fileStream)}
- onError={onError}
- />
- </div>
- ) : (
- ""
- )}
- </div>
- <Pagination
- size={"small"}
- count={getTotalNumberOfPages(data)}
- color="primary"
- className="candidates-pagination"
- onChange={handleChange}
- shape="rounded"
- page={page}
- />
- </div>
- <div
- style={{
- display: "flex",
- justifyContent: "flex-end",
- marginBottom: "35px",
- }}
- ></div>
- </div>
- </div>
- );
- };
-
- FileTable.propTypes = {
- trigger: PropTypes.number,
- };
-
- export default FileTable;
|