summaryrefslogtreecommitdiff
path: root/components/documents
diff options
context:
space:
mode:
Diffstat (limited to 'components/documents')
-rw-r--r--components/documents/StageList.tsx2
-rw-r--r--components/documents/view-document-dialog.tsx292
2 files changed, 157 insertions, 137 deletions
diff --git a/components/documents/StageList.tsx b/components/documents/StageList.tsx
index 81f8a5ca..6df448df 100644
--- a/components/documents/StageList.tsx
+++ b/components/documents/StageList.tsx
@@ -55,7 +55,7 @@ interface Version {
approvedDate: string | null
DocumentSubmitDate: Date
attachments: Attachment[]
- selected?: boolean
+ selected?: boolean;
}
export default function StageList({ document }: StageListProps) {
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 (
<>
- <Button
- size="sm"
- className="border-blue-200"
- variant="outline"
- onClick={() => setOpen(prev => !prev)}
- >
- 문서 보기
- </Button>
- {open && <DocumentViewer
- open={open}
- setOpen={setOpen}
- versions={versions}
- />
- }
- </>
+ <Button
+ size="sm"
+ className="border-blue-200"
+ variant="outline"
+ onClick={() => setOpen((prev) => !prev)}
+ >
+ 문서 보기
+ </Button>
+ {open && (
+ <DocumentViewer open={open} setOpen={setOpen} versions={versions} />
+ )}
+ </>
);
}
-function DocumentViewer({open, setOpen, versions}){
- const [instance, setInstance] = React.useState<null | WebViewerInstance>(null)
- const [viwerLoading, setViewerLoading] = React.useState<boolean>(true)
- const [fileSetLoading, setFileSetLoading] = React.useState<boolean>(true)
+const DocumentViewer: React.FC<{
+ open: boolean;
+ setOpen: React.Dispatch<React.SetStateAction<boolean>>;
+ versions: Version[];
+}> = ({ open, setOpen, versions }) => {
+ const [instance, setInstance] = React.useState<null | WebViewerInstance>(
+ null
+ );
+ const [viwerLoading, setViewerLoading] = React.useState<boolean>(true);
+ const [fileSetLoading, setFileSetLoading] = React.useState<boolean>(true);
const viewer = React.useRef<HTMLDivElement>(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 (
- <Dialog open={open} onOpenChange={async (val) => {
- console.log({val, fileSetLoading})
- if(!val && fileSetLoading){
- return;
- }
-
+ <Dialog
+ open={open}
+ onOpenChange={async (val) => {
+ 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)
- }}>
- <DialogContent className="w-[90vw] h-[90vh]" style={{maxWidth: "none"}}>
- <DialogHeader className="h-[38px]">
- <DialogTitle>
- 문서 미리보기
- </DialogTitle>
- <DialogDescription>
- 첨부파일 미리보기
- </DialogDescription>
+ setViewerLoading(false);
+ setOpen((prev) => !prev);
+ await setTimeout(() => cleanupHtmlStyle(), 1000);
+ }}
+ >
+ <DialogContent className="w-[90vw] h-[90vh]" style={{ maxWidth: "none" }}>
+ <DialogHeader className="h-[38px]">
+ <DialogTitle>문서 미리보기</DialogTitle>
+ <DialogDescription>첨부파일 미리보기</DialogDescription>
</DialogHeader>
- <div ref={viewer} style={{height: "calc(90vh - 20px - 38px - 1rem - 48px)"}}>
- {viwerLoading && <div className="flex flex-col items-center justify-center py-12">
- <Loader2 className="h-8 w-8 text-blue-500 animate-spin mb-4" />
- <p className="text-sm text-muted-foreground">문서 뷰어 로딩 중...</p>
- </div>}
+ <div
+ ref={viewer}
+ style={{ height: "calc(90vh - 20px - 38px - 1rem - 48px)" }}
+ >
+ {viwerLoading && (
+ <div className="flex flex-col items-center justify-center py-12">
+ <Loader2 className="h-8 w-8 text-blue-500 animate-spin mb-4" />
+ <p className="text-sm text-muted-foreground">
+ 문서 뷰어 로딩 중...
+ </p>
+ </div>
+ )}
</div>
- </DialogContent>
+ </DialogContent>
</Dialog>
);
-} \ No newline at end of file
+};