From 92ddb4f13d48cbf344dc2bf63df4457b3c713608 Mon Sep 17 00:00:00 2001 From: rlaks5757 Date: Wed, 26 Mar 2025 16:51:54 +0900 Subject: feat: report batch download 기능 완료 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- components/documents/view-document-dialog.tsx | 292 ++++++++++++++------------ 1 file changed, 156 insertions(+), 136 deletions(-) (limited to 'components/documents/view-document-dialog.tsx') diff --git a/components/documents/view-document-dialog.tsx b/components/documents/view-document-dialog.tsx index 752252ee..9711c4be 100644 --- a/components/documents/view-document-dialog.tsx +++ b/components/documents/view-document-dialog.tsx @@ -1,79 +1,93 @@ -"use client" +"use client"; -import * as React from "react" +import * as React from "react"; import { WebViewerInstance } from "@pdftron/webviewer"; import { - Dialog, DialogTrigger, DialogContent, DialogHeader, - DialogTitle, DialogDescription, DialogFooter -} from "@/components/ui/dialog" -import { Building2, FileIcon, Loader2 } from "lucide-react" -import { Button } from "@/components/ui/button" -import fs from "fs" + Dialog, + DialogTrigger, + DialogContent, + DialogHeader, + DialogTitle, + DialogDescription, + DialogFooter, +} from "@/components/ui/dialog"; +import { Building2, FileIcon, Loader2 } from "lucide-react"; +import { Button } from "@/components/ui/button"; + +// 인터페이스 +interface Attachment { + id: number; + fileName: string; + filePath: string; + fileType?: string; +} interface Version { - id: number - stage: string - revision: string - uploaderType: string - uploaderName: string | null - comment: string | null - status: string | null - planDate: string | null - actualDate: string | null - approvedDate: string | null - DocumentSubmitDate: Date - attachments: Attachment[] - selected: boolean + id: number; + stage: string; + revision: string; + uploaderType: string; + uploaderName: string | null; + comment: string | null; + status: string | null; + planDate: string | null; + actualDate: string | null; + approvedDate: string | null; + DocumentSubmitDate: Date; + attachments: Attachment[]; + selected?: boolean; } type ViewDocumentDialogProps = { - versions: Version[] -} + versions: Version[]; +}; -export function ViewDocumentDialog({versions}: ViewDocumentDialogProps){ - const [open, setOpen] = React.useState(false) - +export function ViewDocumentDialog({ versions }: ViewDocumentDialogProps) { + const [open, setOpen] = React.useState(false); return ( <> - - {open && - } - + + {open && ( + + )} + ); } -function DocumentViewer({open, setOpen, versions}){ - const [instance, setInstance] = React.useState(null) - const [viwerLoading, setViewerLoading] = React.useState(true) - const [fileSetLoading, setFileSetLoading] = React.useState(true) +const DocumentViewer: React.FC<{ + open: boolean; + setOpen: React.Dispatch>; + versions: Version[]; +}> = ({ open, setOpen, versions }) => { + const [instance, setInstance] = React.useState( + null + ); + const [viwerLoading, setViewerLoading] = React.useState(true); + const [fileSetLoading, setFileSetLoading] = React.useState(true); const viewer = React.useRef(null); const initialized = React.useRef(false); const isCancelled = React.useRef(false); // 초기화 중단용 flag 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 + ";"); @@ -81,146 +95,152 @@ function DocumentViewer({open, setOpen, versions}){ htmlElement.removeAttribute("style"); // color-scheme도 없으면 style 속성 자체 삭제 } - console.log("html style 삭제") + console.log("html style 삭제"); }; React.useEffect(() => { if (open && !initialized.current) { initialized.current = true; isCancelled.current = false; // 다시 열릴 때는 false로 리셋 - + requestAnimationFrame(() => { if (viewer.current) { import("@pdftron/webviewer").then(({ default: WebViewer }) => { - console.log(isCancelled.current) + console.log(isCancelled.current); if (isCancelled.current) { console.log("📛 WebViewer 초기화 취소됨 (Dialog 닫힘)"); - + return; } - + WebViewer( { path: "/pdftronWeb", - licenseKey: "demo:1739264618684:616161d7030000000091db1c97c6f386d41d3506ab5b507381ef2ee2bd", + licenseKey: + "demo:1739264618684:616161d7030000000091db1c97c6f386d41d3506ab5b507381ef2ee2bd", fullAPI: true, - css:"/globals.css" + css: "/globals.css", }, viewer.current as HTMLDivElement ).then(async (instance: WebViewerInstance) => { - - setInstance(instance); instance.UI.enableFeatures([instance.UI.Feature.MultiTab]); - instance.UI.disableElements(["addTabButton", "multiTabsEmptyPage"]); + instance.UI.disableElements([ + "addTabButton", + "multiTabsEmptyPage", + ]); setViewerLoading(false); - }); }); } }); } - - return async () => { + + return () => { // cleanup 시에는 중단 flag 세움 - if(instance){ - await instance.UI.dispose() + if (instance) { + instance.UI.dispose(); } - await setTimeout(() => cleanupHtmlStyle(), 500) + setTimeout(() => cleanupHtmlStyle(), 500); }; }, [open]); React.useEffect(() => { - const loadDocument = async () => { - - if(instance && versions.length > 0){ - const { UI } = instance; - - const optionsArray = [] - - versions.forEach(c => { - const {attachments} = c - attachments.forEach(c2 => { - const {fileName, filePath, fileType} = c2 - - const options = { - filename: fileName, - ...(fileType.includes("xlsx") && { - officeOptions: { - formatOptions: { - applyPageBreaksToSheet: true, + const loadDocument = async () => { + if (instance && versions.length > 0) { + const { UI } = instance; + + const optionsArray: any[] = []; + + versions.forEach((c) => { + const { attachments } = c; + attachments.forEach((c2) => { + const { fileName, filePath, fileType } = c2; + + const fileTypeCur = fileType ?? ""; + + const options = { + filename: fileName, + ...(fileTypeCur.includes("xlsx") && { + officeOptions: { + formatOptions: { + applyPageBreaksToSheet: true, + }, }, - }, - }), - }; + }), + }; - optionsArray.push({ - filePath, - options - }) - }) - }) + optionsArray.push({ + filePath, + options, + }); + }); + }); - const tabIds = []; + const tabIds = []; - for (const option of optionsArray) { - const { filePath, options } = option; - const response = await fetch(filePath); - const blob = await response.blob(); + for (const option of optionsArray) { + const { filePath, options } = option; + const response = await fetch(filePath); + const blob = await response.blob(); - const tab = await UI.TabManager.addTab(blob, options); - tabIds.push(tab); // 탭 ID 저장 - } + const tab = await UI.TabManager.addTab(blob, options); + tabIds.push(tab); // 탭 ID 저장 + } - if (tabIds.length > 0) { - await UI.TabManager.setActiveTab(tabIds[0]); - } + if (tabIds.length > 0) { + await UI.TabManager.setActiveTab(tabIds[0]); + } - setFileSetLoading(false) - } - } - loadDocument(); - }, [instance, versions]) + setFileSetLoading(false); + } + }; + loadDocument(); + }, [instance, versions]); - return ( - { - console.log({val, fileSetLoading}) - if(!val && fileSetLoading){ - return; - } - + { + console.log({ val, fileSetLoading }); + if (!val && fileSetLoading) { + return; + } + if (instance) { try { await instance.UI.dispose(); - setInstance(null); // 상태도 초기화 - + setInstance(null); // 상태도 초기화 } catch (e) { console.warn("dispose error", e); } } - + // cleanupHtmlStyle() - setViewerLoading(false); - setOpen(prev => !prev) - await setTimeout(() => cleanupHtmlStyle(), 1000) - }}> - - - - 문서 미리보기 - - - 첨부파일 미리보기 - + setViewerLoading(false); + setOpen((prev) => !prev); + await setTimeout(() => cleanupHtmlStyle(), 1000); + }} + > + + + 문서 미리보기 + 첨부파일 미리보기 -
- {viwerLoading &&
- -

문서 뷰어 로딩 중...

-
} +
+ {viwerLoading && ( +
+ +

+ 문서 뷰어 로딩 중... +

+
+ )}
- +
); -} \ No newline at end of file +}; -- cgit v1.2.3