"use client"; import React, { FC, Dispatch, SetStateAction, useState, useEffect, useRef, } from "react"; import { WebViewerInstance } from "@pdftron/webviewer"; import { Loader2 } from "lucide-react"; import { saveAs } from "file-saver"; import { toast } from "sonner"; import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogFooter, } from "@/components/ui/dialog"; import { Label } from "@/components/ui/label"; import { Select, SelectContent, 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, formCode, }) => { 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", // }, }); saveAs(new Blob([fileData]), fileName); toast.success("Report 다운로드 완료!"); } }; return ( 0} onOpenChange={onClose}> Report 사용하시고자 하는 Report Template를 선택하여 주시기 바랍니다.
); }; interface ReportWebViewerProps { columnsJSON: DataTableColumnJSON[]; reportTempPath: string; reportDatas: ReportData[]; instance: null | WebViewerInstance; setInstance: Dispatch>; setFileLoading: Dispatch>; formCode: string; } const ReportWebViewer: FC = ({ columnsJSON, reportTempPath, reportDatas, instance, setInstance, setFileLoading, formCode, }) => { 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); setViewerLoading(false); }); }); } }); } return () => { // cleanup 시에는 중단 flag 세움 if (instance) { instance.UI.dispose(); } setTimeout(() => cleanupHtmlStyle(), 500); }; }, []); useEffect(() => { importReportData( columnsJSON, instance, reportDatas, reportTempPath, setFileLoading, formCode ); }, [reportTempPath, reportDatas, instance, columnsJSON, formCode]); 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>, formCode: string ) => void; const importReportData: ImportReportData = async ( columnsJSON, instance, reportDatas, reportTempPath, setFileLoading, formCode ) => { 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, { filename: `${formCode}_report.docx`, extension: "docx", }); await doc.applyTemplateValues(reportValueMapping); documentViewer.loadDocument(doc, { extension: "docx", enableOfficeEditing: true, officeOptions: { formatOptions: { applyPageBreaksToSheet: true, }, }, }); } } 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 }; }) ); };