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.
 
 
 

334 line
13 KiB

  1. import React, { useEffect, useRef, useState } from 'react';
  2. import { Button, Grid, InputLabel, TextField } from '@mui/material';
  3. import { GeneralConfirmWindow, notifySaveSuccess } from "../../../utils/CommonFunction";
  4. import axios from 'axios';
  5. import {apiPath, adobeAPIKey} from "../../../auth/utils";
  6. import {
  7. GET_PDF_TEMPLATE_PATH,
  8. GET_PDF_PATH,
  9. POST_PDF_PATH
  10. } from "../../../utils/ApiPathConst";
  11. import {LIONER_BUTTON_THEME} from "../../../themes/colorConst";
  12. import {ThemeProvider} from "@emotion/react";
  13. import {useNavigate} from "react-router-dom";
  14. import {useForm} from "react-hook-form";
  15. import {useLocation, useParams} from "react-router-dom";
  16. import { base64ToBinary } from "../../../utils/CommonFunction";
  17. import { CollectionsBookmarkRounded } from '../../../../node_modules/@mui/icons-material/index';
  18. import LoadingComponent from "../../extra-pages/LoadingComponent";
  19. import { fill } from 'lodash';
  20. function PDF() {
  21. const viewerRef = useRef(null); // Ref for the DOM element where PDF will render
  22. const [pdfLoaded, setPdfLoaded] = useState(false);
  23. const [viewerLoaded, setViewerLoaded] = useState(false);
  24. const [isSaving, setIsSaving] = useState(false);
  25. const [viewInstance,setViewInstance] = useState();
  26. const [adobeDCView,setAdobeDCView] = useState();
  27. const [pdfUrl, setPdfUrl] = useState();
  28. const [record, setRecord] = useState();
  29. const [formName, setFormName] = useState();
  30. const navigate = useNavigate()
  31. const params = useParams();
  32. const location = useLocation();
  33. const queryParams = new URLSearchParams(location.search);
  34. const refId = queryParams.get("refId");
  35. const loadPdfForm = async (id, templateId = 0, clientId = 0) => {
  36. if (!pdfLoaded) {
  37. if (id > 0) {
  38. // axios.get(`${apiPath}${GET_PDF_TEMPLATE_PATH}`, {
  39. axios.get(`${apiPath}${GET_PDF_PATH}/${id}`, {
  40. // responseType: 'arraybuffer', // Essential for binary data
  41. })
  42. .then((response) => {
  43. if (response.status === 200) {
  44. const res = response.data;
  45. setRecord({ // WIP - allow to update all record
  46. id : res.id,
  47. clientId: res.clientId,
  48. templateId: res.templateId,
  49. })
  50. setFormName(res.filename);
  51. //Convert Base64 to binary data
  52. handlePdfUrl(base64ToBinary(res.blobValue));
  53. setPdfLoaded(true);
  54. }
  55. })
  56. .catch(error => {
  57. console.log(error);
  58. return false;
  59. });
  60. } else {
  61. axios.get(`${apiPath}${GET_PDF_TEMPLATE_PATH}`, {
  62. // responseType: 'arraybuffer', // Essential for binary data
  63. params: {
  64. templateId: templateId,
  65. clientId: clientId,
  66. },
  67. })
  68. .then((response) => {
  69. if (response.status === 200) {
  70. const res = response.data;
  71. setFormName(res.filename);
  72. //Convert Base64 to binary data
  73. handlePdfUrl(base64ToBinary(res.blobValue));
  74. setPdfLoaded(true);
  75. }
  76. })
  77. .catch(error => {
  78. console.log(error);
  79. return false;
  80. });
  81. }
  82. }
  83. };
  84. const handlePdfUrl = (pdfBytes) => {
  85. const pdfBlob = new Blob([pdfBytes], { type: 'application/pdf' });
  86. // const pdfFile = new File([pdfBlob], 'document.pdf', { type: 'application/pdf' });
  87. console.log(pdfBlob);
  88. const pdfUrl = URL.createObjectURL(pdfBlob);
  89. setPdfUrl(pdfUrl);
  90. }
  91. const loadPdfViewer = async () => {
  92. if (pdfLoaded && viewerRef.current && !viewerLoaded && !adobeDCView) {
  93. try {
  94. loadAdobeSDK();
  95. } catch (error) {
  96. console.error('Error loading PDF:', error);
  97. }
  98. }
  99. };
  100. const loadAdobeSDK = async() => {
  101. // const token = localStorage.getItem('accessToken');
  102. if (window.AdobeDC) {
  103. const DCViewer = (new window.AdobeDC.View({
  104. clientId: `${adobeAPIKey}`,
  105. divId: 'adobe-dc-view',
  106. }));
  107. setAdobeDCView(DCViewer);
  108. await DCViewer.previewFile(
  109. {
  110. content: {
  111. location: {
  112. url: pdfUrl,
  113. // headers: [{ key: 'Authorization', value: `Bearer ${token}` }],
  114. },
  115. },
  116. metaData: { fileName: formName },
  117. },
  118. {
  119. embedMode: 'FULL_WINDOW',
  120. showAnnotationTools: true,
  121. enableFormFilling: true,
  122. }
  123. ).catch(error => {
  124. console.error('Preview error:', error);
  125. setError('Failed to load PDF: ' + error.message);
  126. }).then(instance => {
  127. URL.revokeObjectURL(pdfUrl);
  128. setViewInstance(instance);
  129. console.log('Instance: ', instance);
  130. setViewerLoaded(true);
  131. });
  132. DCViewer.registerCallback(
  133. window.AdobeDC.View.Enum.CallbackType.SAVE_API,
  134. handleSavePdf,
  135. { autoSaveFrequency: 0, enableFormFilling: true }
  136. );
  137. console.log("viewer: ", DCViewer);
  138. } else {
  139. console.error('AdobeDC not available');
  140. setError('Adobe SDK not loaded');
  141. }
  142. };
  143. useEffect(() => {
  144. if (params.id !== null) {
  145. const pdfData = (params.id).split("T");
  146. if (pdfData[0] > 0) { // Existing Record
  147. loadPdfForm(pdfData[0]);
  148. } else { // New Record
  149. const clientId = pdfData[0] * -1;
  150. const templateId = pdfData[1] * 1;
  151. setRecord({
  152. id: -1,
  153. clientId: clientId, //If PDF ID is negative, convert it to client ID
  154. templateId: templateId});
  155. loadPdfForm(-1, templateId, clientId); // Load new Template
  156. }
  157. }
  158. }, [params.id]);
  159. useEffect(() => { // Update Save function callback after record is updated
  160. console.log("Record Updated: ",record);
  161. if (record && adobeDCView) {
  162. adobeDCView.registerCallback(
  163. window.AdobeDC.View.Enum.CallbackType.SAVE_API,
  164. handleSavePdf,
  165. { autoSaveFrequency: 0, enableFormFilling: true }
  166. );
  167. }
  168. }, [record]);
  169. useEffect(() => {
  170. loadPdfViewer();
  171. }, [viewerRef.current, pdfLoaded]);
  172. const handleSavePdf = async (metaData, content, options) => {
  173. try {
  174. const filledPdfBlob = new Blob([content], { type: 'application/pdf' });
  175. // Create FormData to send the file to Spring Boot
  176. const formData = new FormData();
  177. formData.append('file', filledPdfBlob, 'filled_form.pdf');
  178. formData.append('record', JSON.stringify(record));
  179. setIsSaving(true);
  180. // Send the filled PDF to your Spring Boot backend's save endpoint
  181. await axios.post(`${apiPath}${POST_PDF_PATH}`, formData, {
  182. headers: {
  183. 'Content-Type': 'multipart/form-data' // Important for file uploads
  184. },
  185. })
  186. .then(response => {
  187. setIsSaving(false);
  188. console.log('PDF saved on server:', response.data);
  189. setRecord({
  190. id: response.data.data.id,
  191. clientId: record.clientId,
  192. templateId: record.templateId
  193. });
  194. notifySaveSuccess()});
  195. return {
  196. code: window.AdobeDC.View.Enum.ApiResponseCode.SUCCESS,
  197. data: { metaData } // Return metaData to prevent t.data undefined
  198. };
  199. } catch (error) {
  200. console.error('Error saving PDF:', error);
  201. alert('Failed to save PDF.');
  202. return { code: window.AdobeDC.View.Enum.ApiResponseCode.FAIL };
  203. }
  204. };
  205. const handlePrintPdf = () => {
  206. if (viewInstance.current) {
  207. // Trigger the SDK's built-in print functionality
  208. viewInstance.current.print();
  209. }
  210. };
  211. // Confirmation Window //
  212. const [isWindowOpen, setIsWindowOpen] = useState(false);
  213. const handleClose = () => {
  214. setIsWindowOpen(false);
  215. };
  216. const handleBackClick = () => {
  217. setIsWindowOpen(true);
  218. };
  219. const handleBack = async () => {
  220. navigate(`/pdf/${record.clientId}`);
  221. };
  222. return (
  223. <ThemeProvider theme={LIONER_BUTTON_THEME}>
  224. <div className="pdf-form-page"> {/* This is your 'pdfForm' page */}
  225. <header className="page-header">
  226. <Grid item>
  227. <Grid container>
  228. <Grid item sx={{ml:3, mr:1.5, mb:2}}>
  229. {/* <Button
  230. variant="contained"
  231. type="submit"
  232. color="save"
  233. // disabled={!viewerLoaded}
  234. onClick={handleTest}
  235. // onClick={handleSavePdf}
  236. >
  237. Save
  238. </Button> */}
  239. </Grid>
  240. <Grid item sx={{ml:{xs:1.5, md:1.5, lg:1.5}, mr:1.5, mb:2}}>
  241. <Button
  242. variant="contained"
  243. color="cancel"
  244. onClick={viewerLoaded ? handleBackClick : handleBack}
  245. disabled={isSaving}
  246. >
  247. Back
  248. </Button>
  249. <GeneralConfirmWindow
  250. isWindowOpen={isWindowOpen}
  251. title={"Back to previous page"}
  252. //content={`Confirm to delete Event "${clientDetail.name}" ?`}
  253. content={`Are you sure to leave this page?\nAny unsaved progress will be lost.`}
  254. onNormalClose={handleClose}
  255. onConfirmClose={handleBack}
  256. />
  257. </Grid>
  258. {/* <Grid item xs={9} s={6} md={5} lg={3} sx={{ml:3, mr:3, mb:0.5}}>
  259. <InputLabel htmlFor="remarks">Remarks</InputLabel>
  260. </Grid>
  261. <Grid item xs={9} s={6} md={5} lg={3} sx={{ml:3, mr:3, mb:0.5}}>
  262. <TextField
  263. fullWidth
  264. inputProps={{maxLength: 500}}
  265. id="remarks"
  266. autoComplete="off"
  267. />
  268. </Grid> */}
  269. </Grid>
  270. </Grid>
  271. {/* {pdfLoaded ? (
  272. <div className="action-buttons">
  273. <button onClick={handleSavePdf} className="action-button save-button">
  274. Save Filled PDF
  275. </button>
  276. <button onClick={handlePrintPdf} className="action-button print-button">
  277. Print PDF
  278. </button>
  279. </div>
  280. ) : (
  281. <p>Loading PDF viewer...</p>
  282. )} */}
  283. </header>
  284. {!pdfLoaded && (<LoadingComponent/>)}
  285. <div id="adobe-dc-view" ref={viewerRef} style={{
  286. width: '100%',
  287. height: 'calc(100vh - 180px)', // Adjust height based on header/footer
  288. border: '1px solid #ccc',
  289. }}
  290. hidden={!pdfLoaded}
  291. />
  292. {/* <div
  293. // id={viewerRef}
  294. ref={viewerRef}
  295. // className="pdf-viewer-container"
  296. style={{
  297. width: '100%',
  298. height: 'calc(100vh - 180px)', // Adjust height based on header/footer
  299. border: '1px solid #ccc',
  300. }}
  301. >
  302. </div> */}
  303. </div>
  304. </ThemeProvider>
  305. );
  306. }
  307. export default PDF;