25개 이상의 토픽을 선택하실 수 없습니다. Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

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