summaryrefslogtreecommitdiff
path: root/lib/basic-contract/template/basic-contract-template-viewer.tsx
diff options
context:
space:
mode:
authordujinkim <dujin.kim@dtsolution.co.kr>2025-07-23 09:08:03 +0000
committerdujinkim <dujin.kim@dtsolution.co.kr>2025-07-23 09:08:03 +0000
commita50bc9baea332f996e6bc3a5d70c69f6d2d0f194 (patch)
tree7493b8a4d9cc7cc3375068f1aa10b0067e85988f /lib/basic-contract/template/basic-contract-template-viewer.tsx
parent7402e759857d511add0d3eb19f1fa13cb957c1df (diff)
(대표님, 최겸) 기본계약 템플릿 및 에디터, 기술영업 벤더정보, 파일 보안다운로드, 벤더 document sync 상태 서비스, 메뉴 Config, 기술영업 미사용 제거
Diffstat (limited to 'lib/basic-contract/template/basic-contract-template-viewer.tsx')
-rw-r--r--lib/basic-contract/template/basic-contract-template-viewer.tsx234
1 files changed, 234 insertions, 0 deletions
diff --git a/lib/basic-contract/template/basic-contract-template-viewer.tsx b/lib/basic-contract/template/basic-contract-template-viewer.tsx
new file mode 100644
index 00000000..59989e46
--- /dev/null
+++ b/lib/basic-contract/template/basic-contract-template-viewer.tsx
@@ -0,0 +1,234 @@
+"use client";
+
+import React, {
+ useState,
+ useEffect,
+ useRef,
+ SetStateAction,
+ Dispatch,
+} from "react";
+import { WebViewerInstance } from "@pdftron/webviewer";
+import { Loader2 } from "lucide-react";
+import { toast } from "sonner";
+
+interface BasicContractTemplateViewerProps {
+ templateId?: number;
+ filePath?: string;
+ instance: WebViewerInstance | null;
+ setInstance: Dispatch<SetStateAction<WebViewerInstance | null>>;
+}
+
+export function BasicContractTemplateViewer({
+ templateId,
+ filePath,
+ instance,
+ setInstance,
+}: BasicContractTemplateViewerProps) {
+ const [fileLoading, setFileLoading] = useState<boolean>(true);
+ const viewer = useRef<HTMLDivElement>(null);
+ const initialized = useRef(false);
+ const isCancelled = useRef(false);
+
+ // WebViewer 초기화 (기존 SignViewer와 완전히 동일)
+ useEffect(() => {
+ if (!initialized.current && viewer.current) {
+ initialized.current = true;
+ isCancelled.current = false;
+
+ requestAnimationFrame(() => {
+ if (viewer.current) {
+ import("@pdftron/webviewer").then(({ default: WebViewer }) => {
+ if (isCancelled.current) {
+ console.log("📛 WebViewer 초기화 취소됨");
+ return;
+ }
+
+ // viewerElement이 확실히 존재함을 확인
+ const viewerElement = viewer.current;
+ if (!viewerElement) return;
+
+ WebViewer(
+ {
+ path: "/pdftronWeb",
+ licenseKey: process.env.NEXT_PUBLIC_PDFTRON_WEBVIEW_KEY,
+ fullAPI: true,
+ // 한글 입력 지원을 위한 설정
+ enableOfficeEditing: true, // Office 편집 모드에서 IME 지원 필요
+ l: "ko", // 한국어 로케일 설정
+ },
+ viewerElement
+ ).then((instance: WebViewerInstance) => {
+ setInstance(instance);
+ setFileLoading(false);
+
+ try {
+ const { disableElements, enableElements, setToolbarGroup } = instance.UI;
+
+ // 편집에 필요한 요소들 활성화
+ enableElements([
+ "toolbarGroup-Edit",
+ "toolbarGroup-Insert",
+ "textSelectButton",
+ "panToolButton"
+ ]);
+
+ // 불필요한 요소들만 비활성화
+ disableElements([
+ "toolbarGroup-Annotate", // 주석 도구
+ "toolbarGroup-Shapes", // 도형 도구
+ "toolbarGroup-Forms", // 폼 도구
+ "signatureToolButton", // 서명 도구
+ "stampToolButton", // 스탬프 도구
+ "rubberStampToolButton", // 러버스탬프 도구
+ "freeHandToolButton", // 자유 그리기
+ "stickyToolButton", // 스티키 노트
+ "calloutToolButton", // 콜아웃
+ ]);
+
+ // 편집 툴바 설정
+ setToolbarGroup("toolbarGroup-Edit");
+
+ // 한글 입력 지원을 위한 추가 설정
+ const iframeWindow = instance.UI.iframeWindow;
+ if (iframeWindow && iframeWindow.document) {
+ // IME 지원 활성화
+ const documentBody = iframeWindow.document.body;
+ if (documentBody) {
+ documentBody.style.imeMode = 'active';
+ documentBody.setAttribute('lang', 'ko-KR');
+ }
+
+ // 키보드 이벤트 리스너 추가 (한글 입력 감지)
+ iframeWindow.document.addEventListener('compositionstart', (e) => {
+ console.log('🇰🇷 한글 입력 시작');
+ });
+
+ iframeWindow.document.addEventListener('compositionend', (e) => {
+ console.log('🇰🇷 한글 입력 완료:', e.data);
+ });
+ }
+
+ console.log("📝 WebViewer 한글 지원 초기화 완료");
+ } catch (uiError) {
+ console.warn("⚠️ UI 설정 중 오류 (무시됨):", uiError);
+ }
+ }).catch((error) => {
+ console.error("❌ WebViewer 초기화 실패:", error);
+ setFileLoading(false);
+ toast.error("뷰어 초기화에 실패했습니다.");
+ });
+ });
+ }
+ });
+ }
+
+ return () => {
+ if (instance) {
+ instance.UI.dispose();
+ }
+ isCancelled.current = true;
+ setTimeout(() => cleanupHtmlStyle(), 500);
+ };
+ }, []);
+
+ // 문서 로드 (기존 SignViewer와 동일)
+ useEffect(() => {
+ if (!instance || !filePath) return;
+
+ loadDocument(instance, filePath);
+ }, [instance, filePath]);
+
+ // 한글 지원 Office 문서 로드
+ const loadDocument = async (instance: WebViewerInstance, documentPath: string) => {
+ setFileLoading(true);
+ try {
+ // 절대 URL로 변환
+ const fullPath = documentPath.startsWith('http')
+ ? documentPath
+ : `${window.location.origin}${documentPath}`;
+
+ // 파일명 추출
+ const fileName = documentPath.split('/').pop() || 'document.docx';
+
+ console.log("📄 한글 지원 Office 문서 로드 시작:", fullPath);
+ console.log("📎 파일명:", fileName);
+
+ // PDFTron 공식 방법: instance.UI.loadDocument() + 한글 지원 옵션
+ await instance.UI.loadDocument(fullPath, {
+ filename: fileName,
+ enableOfficeEditing: true,
+ // 한글 입력 지원을 위한 추가 옵션
+ officeOptions: {
+ locale: 'ko-KR',
+ enableIME: true,
+ }
+ });
+
+ // 문서 로드 후 한글 입력 환경 설정
+ setTimeout(() => {
+ try {
+ const iframeWindow = instance.UI.iframeWindow;
+ if (iframeWindow && iframeWindow.document) {
+ // Office 편집기 컨테이너 찾기
+ const officeContainer = iframeWindow.document.querySelector('[data-office-editor]') ||
+ iframeWindow.document.querySelector('.office-editor') ||
+ iframeWindow.document.body;
+
+ if (officeContainer) {
+ // 한글 입력 최적화 설정
+ officeContainer.style.imeMode = 'active';
+ officeContainer.setAttribute('lang', 'ko-KR');
+ officeContainer.setAttribute('inputmode', 'text');
+
+ console.log("🇰🇷 한글 입력 환경 설정 완료");
+ }
+ }
+ } catch (setupError) {
+ console.warn("⚠️ 한글 입력 환경 설정 실패:", setupError);
+ }
+ }, 1000);
+
+ console.log("✅ 한글 지원 Office 편집 모드로 문서 로드 완료");
+ toast.success("Office 편집 모드가 활성화되었습니다.", {
+ description: "한글 입력이 안 될 경우 외부에서 작성 후 복사-붙여넣기를 사용하세요."
+ });
+
+ } catch (err) {
+ console.error("❌ Office 문서 로딩 중 오류:", err);
+ toast.error(`Office 문서 로드 실패: ${err instanceof Error ? err.message : '알 수 없는 오류'}`);
+ } finally {
+ setFileLoading(false);
+ }
+ };
+
+ // 기존 SignViewer와 동일한 렌더링 (확대 문제 해결)
+ return (
+ <div className="relative w-full h-full overflow-hidden">
+ <div
+ ref={viewer}
+ className="w-full h-full"
+ style={{
+ position: 'relative',
+ overflow: 'hidden',
+ contain: 'layout style paint', // CSS containment로 격리
+ }}
+ >
+ {fileLoading && (
+ <div className="absolute inset-0 flex flex-col items-center justify-center bg-white bg-opacity-90 z-10">
+ <Loader2 className="h-8 w-8 text-blue-500 animate-spin mb-4" />
+ <p className="text-sm text-muted-foreground">문서 로딩 중...</p>
+ </div>
+ )}
+ </div>
+ </div>
+ );
+}
+
+// WebViewer 정리 함수 (기존과 동일)
+const cleanupHtmlStyle = () => {
+ // iframe 스타일 정리 (WebViewer가 추가한 스타일)
+ const elements = document.querySelectorAll('.Document_container');
+ elements.forEach((elem) => {
+ elem.remove();
+ });
+}; \ No newline at end of file