summaryrefslogtreecommitdiff
path: root/lib/basic-contract
diff options
context:
space:
mode:
Diffstat (limited to 'lib/basic-contract')
-rw-r--r--lib/basic-contract/template/basic-contract-template-viewer.tsx31
-rw-r--r--lib/basic-contract/template/template-editor-wrapper.tsx78
2 files changed, 90 insertions, 19 deletions
diff --git a/lib/basic-contract/template/basic-contract-template-viewer.tsx b/lib/basic-contract/template/basic-contract-template-viewer.tsx
index 38438d33..59273dca 100644
--- a/lib/basic-contract/template/basic-contract-template-viewer.tsx
+++ b/lib/basic-contract/template/basic-contract-template-viewer.tsx
@@ -17,6 +17,7 @@ interface BasicContractTemplateViewerProps {
instance: WebViewerInstance | null;
setInstance: Dispatch<SetStateAction<WebViewerInstance | null>>;
onSignatureFieldFound?: (searchText: string) => void; // 서명란 발견 시 콜백
+ onLoadError?: (error: Error) => void; // 파일 로드 실패 시 콜백
}
// 서명란 텍스트 찾기 함수 (변수 추가를 위해 사용) - 모든 서명란 텍스트를 찾아서 반환
@@ -77,8 +78,10 @@ export function BasicContractTemplateViewer({
instance,
setInstance,
onSignatureFieldFound,
+ onLoadError,
}: BasicContractTemplateViewerProps) {
const [fileLoading, setFileLoading] = useState<boolean>(true);
+ const [hasError, setHasError] = useState<boolean>(false);
const viewer = useRef<HTMLDivElement>(null);
const initialized = useRef(false);
const isCancelled = useRef(false);
@@ -234,6 +237,8 @@ export function BasicContractTemplateViewer({
// 한글 지원 Office 문서 로드
const loadDocument = async (instance: WebViewerInstance, documentPath: string) => {
setFileLoading(true);
+ setHasError(false);
+
try {
// 절대 URL로 변환
const fullPath = documentPath.startsWith('http')
@@ -246,6 +251,21 @@ export function BasicContractTemplateViewer({
console.log("📄 한글 지원 Office 문서 로드 시작:", fullPath);
console.log("📎 파일명:", fileName);
+ // 파일 존재 여부 먼저 확인
+ try {
+ const response = await fetch(fullPath, { method: 'HEAD' });
+ if (!response.ok) {
+ throw new Error(`파일을 찾을 수 없습니다 (${response.status})`);
+ }
+ } catch (fetchError) {
+ const error = new Error('파일이 존재하지 않거나 접근할 수 없습니다.');
+ setHasError(true);
+ if (onLoadError) {
+ onLoadError(error);
+ }
+ throw error;
+ }
+
// PDFTron 공식 방법: instance.UI.loadDocument() + 한글 지원 옵션
await instance.UI.loadDocument(fullPath, {
filename: fileName,
@@ -283,7 +303,14 @@ export function BasicContractTemplateViewer({
} catch (err) {
console.error("❌ Office 문서 로딩 중 오류:", err);
- toast.error(`Office 문서 로드 실패: ${err instanceof Error ? err.message : '알 수 없는 오류'}`);
+ const errorMessage = err instanceof Error ? err.message : '알 수 없는 오류';
+
+ setHasError(true);
+ if (onLoadError) {
+ onLoadError(err instanceof Error ? err : new Error(errorMessage));
+ }
+
+ toast.error(`Office 문서 로드 실패: ${errorMessage}`);
} finally {
setFileLoading(false);
}
@@ -301,7 +328,7 @@ export function BasicContractTemplateViewer({
contain: 'layout style paint', // CSS containment로 격리
}}
>
- {fileLoading && (
+ {fileLoading && !hasError && (
<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>
diff --git a/lib/basic-contract/template/template-editor-wrapper.tsx b/lib/basic-contract/template/template-editor-wrapper.tsx
index 8d2ce97f..be5e6856 100644
--- a/lib/basic-contract/template/template-editor-wrapper.tsx
+++ b/lib/basic-contract/template/template-editor-wrapper.tsx
@@ -102,6 +102,7 @@ export function TemplateEditorWrapper({
const [templateName, setTemplateName] = React.useState<string>("");
const [predefinedVariables, setPredefinedVariables] = React.useState<string[]>([]);
const [signatureVariables, setSignatureVariables] = React.useState<string[]>([]); // 서명란 변수
+ const [fileLoadError, setFileLoadError] = React.useState<Error | null>(null); // 파일 로드 오류
// 템플릿 이름 로드 및 변수 설정
React.useEffect(() => {
@@ -503,23 +504,66 @@ export function TemplateEditorWrapper({
{/* 뷰어 영역 (확대 문제 해결을 위한 컨테이너 격리) */}
<div className="flex-1 relative overflow-hidden">
- <div className="absolute inset-0">
- <BasicContractTemplateViewer
- templateId={templateId}
- filePath={filePath}
- instance={instance}
- setInstance={setInstance}
- onSignatureFieldFound={(searchText) => {
- // 서명란 발견 시 원본 텍스트를 그대로 추가 (자동 서명 기능을 위해)
- setSignatureVariables(prev => {
- if (!prev.includes(searchText)) {
- return [...prev, searchText];
- }
- return prev;
- });
- }}
- />
- </div>
+ {fileLoadError ? (
+ <div className="absolute inset-0 flex flex-col items-center justify-center bg-gray-50 p-8">
+ <div className="max-w-md text-center space-y-4">
+ <AlertCircle className="h-16 w-16 text-red-500 mx-auto" />
+ <div>
+ <h3 className="text-lg font-semibold text-gray-900">파일을 불러올 수 없습니다</h3>
+ <p className="text-sm text-gray-600 mt-2">
+ {fileLoadError.message}
+ </p>
+ </div>
+ <div className="bg-yellow-50 border border-yellow-200 rounded-lg p-4 text-left">
+ <p className="text-sm text-yellow-800">
+ <strong>가능한 원인:</strong>
+ </p>
+ <ul className="text-sm text-yellow-700 mt-2 space-y-1 list-disc list-inside">
+ <li>템플릿이 폐기되어 파일이 삭제되었습니다</li>
+ <li>템플릿이 DB에서 삭제되었습니다</li>
+ <li>파일 경로가 변경되었거나 손상되었습니다</li>
+ </ul>
+ </div>
+ <div className="flex gap-2 justify-center">
+ <Button
+ variant="outline"
+ onClick={() => window.history.back()}
+ >
+ 목록으로 돌아가기
+ </Button>
+ <Button
+ onClick={() => {
+ setFileLoadError(null);
+ window.location.reload();
+ }}
+ >
+ 새로고침
+ </Button>
+ </div>
+ </div>
+ </div>
+ ) : (
+ <div className="absolute inset-0">
+ <BasicContractTemplateViewer
+ templateId={templateId}
+ filePath={filePath}
+ instance={instance}
+ setInstance={setInstance}
+ onSignatureFieldFound={(searchText) => {
+ // 서명란 발견 시 원본 텍스트를 그대로 추가 (자동 서명 기능을 위해)
+ setSignatureVariables(prev => {
+ if (!prev.includes(searchText)) {
+ return [...prev, searchText];
+ }
+ return prev;
+ });
+ }}
+ onLoadError={(error) => {
+ setFileLoadError(error);
+ }}
+ />
+ </div>
+ )}
</div>
{/* 하단 안내 (한글 입력 팁 포함) */}