"use client"; import React, { FC, Dispatch, SetStateAction, useState, useEffect, useRef, } from "react"; import { WebViewerInstance, Core } from "@pdftron/webviewer"; import { useToast } from "@/hooks/use-toast"; import prettyBytes from "pretty-bytes"; import { X, Loader2 } from "lucide-react"; import { Badge } from "@/components/ui/badge"; import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogFooter, } from "@/components/ui/dialog"; import { Label } from "@/components/ui/label"; import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select"; import { Button } from "@/components/ui/button"; import { getReportTempList } from "@/lib/forms/services"; import { DataTableColumnJSON } from "./form-data-table-columns"; type ReportData = { [key: string]: any; }; interface tempFile { fileName: string; filePath: string; } interface FormDataReportDialogProps { columnsJSON: DataTableColumnJSON[]; reportData: ReportData[]; setReportData: Dispatch>; packageId: number; formId: number; formCode: string; } export const FormDataReportDialog: FC = ({ columnsJSON, reportData, setReportData, packageId, formId, }) => { const [tempList, setTempList] = useState([]); const [selectTemp, setSelectTemp] = useState(""); const [instance, setInstance] = useState(null); const [fileLoading, setFileLoading] = useState(true); useEffect(() => { updateReportTempList(packageId, formId, setTempList); }, [packageId, formId]); const onClose = async (value: boolean) => { if (fileLoading) { return; } if (!value) { setTimeout(() => cleanupHtmlStyle(), 1000); setReportData([]); } }; const downloadFileData = async () => { if (instance) { const { UI, Core } = instance; const { documentViewer } = Core; const doc = documentViewer.getDocument(); const fileName = doc.getFilename(); const fileData = await doc.getFileData({ includeAnnotations: true, // 사용자가 추가한 폼 필드 및 입력 포함 // officeOptions: { // outputFormat: "docx", // }, }); const blob = new Blob([fileData], { type: "application/vnd.openxmlformats-officedocument.wordprocessingml.document", }); const link = document.createElement("a"); link.href = URL.createObjectURL(blob); link.download = fileName; document.body.appendChild(link); link.click(); document.body.removeChild(link); // const allTabs = UI.TabManager.getAllTabs() as { // id: number; // src: Core.Document; // }[]; // for (const tab of allTabs) { // // await UI.TabManager.setActiveTab(tab.id); // await activateTabAndWaitForLoad(instance, tab.id); // const tabDoc = tab.src; // const fileName = tabDoc.getFilename(); // const fileData = await tabDoc.getFileData({ // includeAnnotations: true, // }); // console.log({ fileData }); // const blob = new Blob([fileData], { // type: "application/vnd.openxmlformats-officedocument.wordprocessingml.document", // }); // // 다운로드 // // const link = document.createElement("a"); // // link.href = URL.createObjectURL(blob); // // link.download = fileName; // // document.body.appendChild(link); // // link.click(); // // document.body.removeChild(link); // } } }; return ( 0} onOpenChange={onClose}> Report 사용하시고자 하는 Report Template를 선택하여 주시기 바랍니다.
); }; interface ReportWebViewerProps { columnsJSON: DataTableColumnJSON[]; reportTempPath: string; reportDatas: ReportData[]; instance: null | WebViewerInstance; setInstance: Dispatch>; setFileLoading: Dispatch>; } const ReportWebViewer: FC = ({ columnsJSON, reportTempPath, reportDatas, instance, setInstance, setFileLoading, }) => { const [viwerLoading, setViewerLoading] = useState(true); const viewer = useRef(null); const initialized = React.useRef(false); const isCancelled = React.useRef(false); // 초기화 중단용 flag useEffect(() => { if (!initialized.current) { initialized.current = true; isCancelled.current = false; // 다시 열릴 때는 false로 리셋 requestAnimationFrame(() => { if (viewer.current) { import("@pdftron/webviewer").then(({ default: WebViewer }) => { console.log(isCancelled.current); if (isCancelled.current) { console.log("📛 WebViewer 초기화 취소됨 (Dialog 닫힘)"); return; } WebViewer( { path: "/pdftronWeb", licenseKey: process.env.NEXT_PUBLIC_PDFTRON_WEBVIEW_KEY, fullAPI: true, }, viewer.current as HTMLDivElement ).then(async (instance: WebViewerInstance) => { setInstance(instance); // //Tab 메뉴 사용 필요시 활성화 // instance.UI.enableFeatures([instance.UI.Feature.MultiTab]); // instance.UI.disableElements([ // "addTabButton", // "multiTabsEmptyPage", // ]); setViewerLoading(false); }); }); } }); } return () => { // cleanup 시에는 중단 flag 세움 if (instance) { instance.UI.dispose(); } setTimeout(() => cleanupHtmlStyle(), 500); }; }, []); useEffect(() => { importReportData( columnsJSON, instance, reportDatas, reportTempPath, setFileLoading ); }, [reportTempPath, reportDatas, instance, columnsJSON]); return (
{viwerLoading && (

문서 뷰어 로딩 중...

)}
); }; const cleanupHtmlStyle = () => { const htmlElement = document.documentElement; // 기존 style 속성 가져오기 const originalStyle = htmlElement.getAttribute("style") || ""; // "color-scheme: light" 또는 "color-scheme: dark" 찾기 const colorSchemeStyle = originalStyle .split(";") .map((s) => s.trim()) .find((s) => s.startsWith("color-scheme:")); // 새로운 스타일 적용 (color-scheme만 유지) if (colorSchemeStyle) { htmlElement.setAttribute("style", colorSchemeStyle + ";"); } else { htmlElement.removeAttribute("style"); // color-scheme도 없으면 style 속성 자체 삭제 } console.log("html style 삭제"); }; const stringifyAllValues = (obj: any): any => { if (Array.isArray(obj)) { return obj.map((item) => stringifyAllValues(item)); } else if (typeof obj === "object" && obj !== null) { const result: any = {}; for (const key in obj) { result[key] = stringifyAllValues(obj[key]); } return result; } else { return obj !== null && obj !== undefined ? String(obj) : ""; } }; type ImportReportData = ( columnsJSON: DataTableColumnJSON[], instance: null | WebViewerInstance, reportDatas: ReportData[], reportTempPath: string, setFileLoading: Dispatch> ) => void; const importReportData: ImportReportData = async ( columnsJSON, instance, reportDatas, reportTempPath, setFileLoading ) => { setFileLoading(true); try { if (instance && reportDatas.length > 0 && reportTempPath.length > 0) { const { UI, Core } = instance; const { documentViewer, createDocument } = Core; const getFileData = await fetch(reportTempPath); const reportFileBlob = await getFileData.blob(); const reportData = reportDatas[0]; const reportValue = stringifyAllValues(reportData); const reportValueMapping: { [key: string]: any } = {}; columnsJSON.forEach((c) => { const { key, label } = c; const objKey = label.split(" ").join("_"); reportValueMapping[objKey] = reportValue?.[key] ?? ""; }); const doc = await createDocument(reportFileBlob, { extension: "docx", }); await doc.applyTemplateValues(reportValueMapping); documentViewer.loadDocument(doc, { extension: "docx", enableOfficeEditing: true, officeOptions: { formatOptions: { applyPageBreaksToSheet: true, }, }, }); } } catch (err) { } finally { setFileLoading(false); } }; const importReportDataTab: ImportReportData = async ( columnJSON, instance, reportDatas, reportTempPath, setFileLoading ) => { setFileLoading(true); try { if (instance && reportDatas.length > 0 && reportTempPath.length > 0) { const { UI, Core } = instance; const { createDocument } = Core; const getFileData = await fetch(reportTempPath); const reportFileBlob = await getFileData.blob(); const prevTab = UI.TabManager.getAllTabs(); (prevTab as object[] as { id: number }[]).forEach((c) => { const { id } = c; UI.TabManager.deleteTab(id); }); const fileOptions = reportDatas.map((c) => { const { tagNumber } = c; const options = { filename: `${tagNumber}_report.docx`, }; return { options, reportData: c }; }); const tabIds = []; for (const fileOption of fileOptions) { let doc = await createDocument(reportFileBlob, { ...fileOption.options, extension: "docx", }); await doc.applyTemplateValues( stringifyAllValues(fileOption.reportData) ); const tab = await UI.TabManager.addTab(doc, { ...fileOption.options, }); tabIds.push(tab); // 탭 ID 저장 } if (tabIds.length > 0) { await UI.TabManager.setActiveTab(tabIds[0]); } } } catch (err) { } finally { setFileLoading(false); } }; type UpdateReportTempList = ( packageId: number, formId: number, setPrevReportTemp: Dispatch> ) => void; const updateReportTempList: UpdateReportTempList = async ( packageId, formId, setTempList ) => { const tempList = await getReportTempList(packageId, formId); setTempList( tempList.map((c) => { const { fileName, filePath } = c; return { fileName, filePath }; }) ); };