You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

FilesViewPage.js 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411
  1. import { Pagination } from "@mui/material";
  2. import React from "react";
  3. import { useEffect } from "react";
  4. import { useState } from "react";
  5. import { useSelector } from "react-redux";
  6. import { useDispatch } from "react-redux";
  7. import FilterButton from "../../components/Button/FilterButton";
  8. import DocsFilters from "../../components/Docs/DocsFilters";
  9. import { FETCH_FILES_LOADING } from "../../store/actions/files/fileActionConstants";
  10. import {
  11. deleteFileReq,
  12. getFileFiltersReq,
  13. getFilesReq,
  14. } from "../../store/actions/files/fileActions";
  15. import { setContent } from "../../store/actions/files/fileActions";
  16. import { selectIsLoadingByActionType } from "../../store/selectors/loadingSelectors";
  17. import Fade from "@mui/material/Fade";
  18. import { useTranslation } from "react-i18next";
  19. import searchImage from "../../assets/images/search.png";
  20. import IconButton from "../../components/IconButton/IconButton";
  21. import { ADD_FILE, FILES_PAGE } from "../../constants/pages";
  22. import PropTypes from "prop-types";
  23. import FileViewer from "react-file-viewer";
  24. import { useLocation } from "react-router";
  25. import deleteIcon from "../../assets/images/delete.png";
  26. import ConfirmDialog from "../../components/MUI/ConfirmDialog";
  27. const FilesViewPage = ({ history }) => {
  28. const [toggleFiltersDrawer, setToggleFiltersDrawer] = useState(false);
  29. const [page, setPage] = useState(1);
  30. const { filters } = useSelector((s) => s.fileFilters);
  31. const { data } = useSelector((s) => s.files);
  32. const { t } = useTranslation();
  33. const [isSearchFieldVisible, setIsSearchFieldVisible] = useState(false);
  34. const [file, setFile] = useState(null);
  35. const [fileForDelete, setFileForDelete] = useState({open: false, title: '', streamId: null});
  36. const { state } = useLocation();
  37. const dispatch = useDispatch();
  38. if (state === undefined) {
  39. history.replace(FILES_PAGE);
  40. }
  41. const handleToggleFiltersDrawer = () => {
  42. setToggleFiltersDrawer((oldState) => !oldState);
  43. setFile(null);
  44. };
  45. useEffect(() => {
  46. handleFilters(page);
  47. }, [file]);
  48. useEffect(() => {
  49. dispatch(getFileFiltersReq());
  50. dispatch(
  51. getFilesReq({
  52. payload: {
  53. pageSize: 6,
  54. currentPage: page,
  55. categories: [state && state.category],
  56. extensions: [],
  57. tags: [],
  58. content: "",
  59. },
  60. })
  61. );
  62. }, []);
  63. const handleChange = (_, value) => {
  64. handleFilters(value);
  65. setPage(value);
  66. };
  67. const handleFilters = (value) => {
  68. var catFilters = [state && state.category];
  69. var extFilters = [];
  70. filters.extensions
  71. ?.filter((n) => n.isChecked)
  72. .forEach((m) => extFilters.push(m.name));
  73. var tagFilters = [];
  74. filters.tags
  75. ?.filter((n) => n.isChecked)
  76. .forEach((m) => tagFilters.push(m.name));
  77. dispatch(
  78. getFilesReq({
  79. payload: {
  80. pageSize: 6,
  81. currentPage: value,
  82. categories: catFilters,
  83. extensions: extFilters,
  84. tags: tagFilters,
  85. content: filters.content === null ? "" : filters.content,
  86. },
  87. })
  88. );
  89. };
  90. const isLoading = useSelector(
  91. selectIsLoadingByActionType(FETCH_FILES_LOADING)
  92. );
  93. const handleChangeVisibility = () => {
  94. setIsSearchFieldVisible(!isSearchFieldVisible);
  95. };
  96. const stopPropagation = (e) => {
  97. e.stopPropagation();
  98. };
  99. const handleChangeContent = (value) => {
  100. dispatch(setContent(value));
  101. };
  102. const deleteFileHandler = (stream_id) => {
  103. dispatch(deleteFileReq({ id: stream_id }));
  104. if (file !== null && stream_id === file.streamId) {
  105. setFile(null);
  106. }
  107. };
  108. const handleKeyDown = (event) => {
  109. if (event.key === "Enter" && filters.content !== "") {
  110. var catFilters = [state && state.category];
  111. var extFilters = [];
  112. filters.extensions
  113. ?.filter((n) => n.isChecked)
  114. .forEach((m) => extFilters.push(m.name));
  115. var tagFilters = [];
  116. filters.tags
  117. ?.filter((n) => n.isChecked)
  118. .forEach((m) => tagFilters.push(m.name));
  119. dispatch(
  120. getFilesReq({
  121. payload: {
  122. pageSize: 6,
  123. currentPage: page,
  124. categories: catFilters,
  125. extensions: extFilters,
  126. tags: tagFilters,
  127. content: filters.content,
  128. },
  129. })
  130. );
  131. }
  132. };
  133. const input = (
  134. <div>
  135. <input
  136. placeholder="Pretrazi..."
  137. value={filters.content === undefined ? "" : filters.content}
  138. onChange={(e) => handleChangeContent(e.target.value)}
  139. onKeyDown={handleKeyDown}
  140. className="candidate-search-field"
  141. onClick={stopPropagation}
  142. role="input"
  143. />
  144. </div>
  145. );
  146. const onError = (e) => {
  147. console.log(e, "error in file-viewer");
  148. };
  149. const displayFile = (fileStream, streamId, fileType) => {
  150. setFile({ fileStream, streamId, fileType });
  151. };
  152. const getHrefForDownload = (fileType, fileStream) => {
  153. if (fileType === "docx") {
  154. return `data:application/vnd.openxmlformats-officedocument.wordprocessingml.document;base64,${fileStream}`;
  155. } else if (fileType === "doc") {
  156. return `data:application/msword;base64,${fileStream}`;
  157. } else {
  158. return `data:application/pdf;base64,${fileStream}`;
  159. }
  160. };
  161. return isLoading ? (
  162. <div>
  163. <div className="l-t-rectangle"></div>
  164. <div className="r-b-rectangle"></div>
  165. <div className="loader-container h-withHeader">
  166. <span>Loading</span>
  167. </div>
  168. </div>
  169. ) : (
  170. <>
  171. <ConfirmDialog
  172. open={fileForDelete.open}
  173. title="Brisanje dokumenta"
  174. subtitle={fileForDelete.title}
  175. imgSrc={deleteIcon}
  176. content="Da li ste sigurni za brisanje dokumenta?"
  177. onClose={() => {
  178. setFileForDelete({open: false, title: '', streamId: null});
  179. }}
  180. onConfirm={() => {
  181. deleteFileHandler(fileForDelete.streamId)
  182. setFileForDelete({open: false, title: '', streamId: null});
  183. }}
  184. />
  185. <div onClick={() => setIsSearchFieldVisible(false)}>
  186. <div className="l-t-rectangle"></div>
  187. <div className="r-b-rectangle"></div>
  188. <DocsFilters
  189. open={toggleFiltersDrawer}
  190. handleClose={handleToggleFiltersDrawer}
  191. setPage={setPage}
  192. category={state && state.category}
  193. />
  194. <div
  195. className="pl-144 flex-center"
  196. style={{
  197. paddingTop: "36px",
  198. justifyContent: "space-between",
  199. postion: "relative",
  200. }}
  201. >
  202. <h1 className="page-heading">Pregled dokumenata</h1>
  203. <div style={{ postion: "relative" }}>
  204. <Fade in={isSearchFieldVisible} timeout={500} className="proba">
  205. {input}
  206. </Fade>
  207. <Fade in={isSearchFieldVisible} timeout={500}>
  208. <div
  209. style={{
  210. position: "absolute",
  211. zIndex: 10000,
  212. right: 95,
  213. marginTop: 0,
  214. }}
  215. >
  216. <img src={searchImage} />
  217. </div>
  218. </Fade>
  219. </div>
  220. <div className="flex-center">
  221. <IconButton
  222. className="c-btn c-btn--primary-outlined candidate-btn search-field userPageBtn ml-20px no-padding custom-filter-button"
  223. onClick={handleChangeVisibility}
  224. >
  225. {t("candidates.search")}
  226. <img
  227. src={searchImage}
  228. alt="filter"
  229. className="candidates-image"
  230. />
  231. </IconButton>
  232. <FilterButton onShowFilters={handleToggleFiltersDrawer} />
  233. </div>
  234. </div>
  235. <div
  236. className="pl-144"
  237. style={{
  238. display: "flex",
  239. marginTop: "39px",
  240. flexDirection: "column",
  241. justifyContent: "space-between",
  242. minHeight: "500px",
  243. }}
  244. >
  245. <div className="table-cont">
  246. <div style={{ display: "flex", height: "300px" }}>
  247. <table
  248. className={"usersTable-users mini"}
  249. style={{
  250. width: file === null ? "100%" : "800px",
  251. height: "50%",
  252. }}
  253. >
  254. <thead>
  255. <tr
  256. className="headingRow headingRowFiles"
  257. style={{ cursor: "pointer" }}
  258. >
  259. <th>Putanja dokumenta</th>
  260. <th>Naziv dokumenta</th>
  261. <th>Tip dokumenta</th>
  262. {file === null ? <th>Veličina dokumenta</th> : ""}
  263. <th>Obrisi dokument</th>
  264. <th>Preuzmi dokument</th>
  265. </tr>
  266. </thead>
  267. <tbody>
  268. {data &&
  269. data.data?.length > 0 &&
  270. data.data?.map((n, index) => (
  271. <tr
  272. key={index}
  273. className="secondaryRow"
  274. onClick={() => displayFile(n.file_stream, n.stream_id, n.file_type)}
  275. >
  276. <td className="docs-name">{n.fileName}</td>
  277. <td className="docs-name">{n.title}</td>
  278. <td>{n.file_type && n.file_type}</td>
  279. {file === null ? (
  280. <td className="profession">{n.cached_file_size}kB</td>
  281. ) : (
  282. ""
  283. )}
  284. <td className="profession">
  285. <IconButton
  286. className="c-btn c-btn--primary-outlined files-view-page-delete-btn"
  287. onClick={() => /* deleteFileHandler(n.stream_id) */setFileForDelete({open: true, title: n.title, streamId: n.stream_id})}
  288. >
  289. <img
  290. style={{ width: "12px", height: "12px" }}
  291. src={deleteIcon}
  292. />
  293. </IconButton>
  294. </td>
  295. <td>
  296. <div onClick={stopPropagation}>
  297. <a
  298. className="applicant-cv-button"
  299. style={{
  300. width: "100px",
  301. height: "40px",
  302. padding: 8,
  303. }}
  304. download={n.title}
  305. href={getHrefForDownload(
  306. n.file_type,
  307. n.file_stream
  308. )}
  309. >
  310. {t("common.download")}
  311. </a>
  312. </div>
  313. </td>
  314. </tr>
  315. ))}
  316. </tbody>
  317. </table>
  318. {file && (
  319. <div
  320. style={{
  321. width: "500px",
  322. marginLeft: "30px",
  323. overflowY: "visible",
  324. }}
  325. >
  326. <FileViewer
  327. fileType={file.fileType}
  328. filePath={`data:application/vnd.openxmlformats-officedocument.wordprocessingml.document;base64,${file.fileStream}`}
  329. onError={onError}
  330. />
  331. </div>
  332. )}
  333. </div>
  334. {/* <a
  335. className="applicant-cv-button"
  336. download={"Proba2.docx"}
  337. href={`data:application/vnd.openxmlformats-officedocument.wordprocessingml.document;base64,${file2}`}
  338. title="Download pdf document"
  339. >
  340. {t("common.download")} file
  341. </a> */}
  342. <Pagination
  343. // size={matches ? "small" : "medium"}
  344. size={"small"}
  345. count={
  346. parseInt(data.total) <= 6
  347. ? 1
  348. : Math.ceil(parseInt(data.total) / 6)
  349. }
  350. color="primary"
  351. className="candidates-pagination"
  352. onChange={handleChange}
  353. shape="rounded"
  354. page={page}
  355. />
  356. <IconButton
  357. className="c-btn ads-page-btn c-btn--primary add-ad-btn filesPage"
  358. onClick={() => history.push(ADD_FILE)}
  359. >
  360. + Fajl
  361. </IconButton>
  362. </div>
  363. <div
  364. style={{
  365. display: "flex",
  366. justifyContent: "flex-end",
  367. marginBottom: "35px",
  368. }}
  369. ></div>
  370. </div>
  371. </div>
  372. </>
  373. );
  374. };
  375. FilesViewPage.propTypes = {
  376. history: PropTypes.shape({
  377. replace: PropTypes.func,
  378. push: PropTypes.func,
  379. location: PropTypes.shape({
  380. pathname: PropTypes.string,
  381. }),
  382. }),
  383. };
  384. export default FilesViewPage;