From 610d3bccf1cb640e2a21df28d8d2a954c2bf337e Mon Sep 17 00:00:00 2001 From: dujinkim Date: Thu, 5 Jun 2025 01:53:35 +0000 Subject: (대표님) 변경사항 0604 - OCR 관련 및 drizzle generated sqls MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../table/import-from-dolce-button.tsx | 356 +++++++++++++++++++++ 1 file changed, 356 insertions(+) create mode 100644 lib/vendor-document-list/table/import-from-dolce-button.tsx (limited to 'lib/vendor-document-list/table/import-from-dolce-button.tsx') diff --git a/lib/vendor-document-list/table/import-from-dolce-button.tsx b/lib/vendor-document-list/table/import-from-dolce-button.tsx new file mode 100644 index 00000000..519d40cb --- /dev/null +++ b/lib/vendor-document-list/table/import-from-dolce-button.tsx @@ -0,0 +1,356 @@ +"use client" + +import * as React from "react" +import { RefreshCw, Download, Loader2, CheckCircle, AlertTriangle } from "lucide-react" +import { toast } from "sonner" + +import { Button } from "@/components/ui/button" +import { + Dialog, + DialogContent, + DialogDescription, + DialogHeader, + DialogTitle, +} from "@/components/ui/dialog" +import { + Popover, + PopoverContent, + PopoverTrigger, +} from "@/components/ui/popover" +import { Badge } from "@/components/ui/badge" +import { Progress } from "@/components/ui/progress" +import { Separator } from "@/components/ui/separator" + +interface ImportFromDOLCEButtonProps { + contractId: number + onImportComplete?: () => void +} + +interface ImportStatus { + lastImportAt?: string + availableDocuments: number + newDocuments: number + updatedDocuments: number + importEnabled: boolean +} + +export function ImportFromDOLCEButton({ + contractId, + onImportComplete +}: ImportFromDOLCEButtonProps) { + const [isDialogOpen, setIsDialogOpen] = React.useState(false) + const [importProgress, setImportProgress] = React.useState(0) + const [isImporting, setIsImporting] = React.useState(false) + const [importStatus, setImportStatus] = React.useState(null) + const [statusLoading, setStatusLoading] = React.useState(false) + + // DOLCE 상태 조회 + const fetchImportStatus = async () => { + setStatusLoading(true) + try { + const response = await fetch(`/api/sync/import/status?contractId=${contractId}&sourceSystem=DOLCE`) + if (!response.ok) { + const errorData = await response.json().catch(() => ({})) + throw new Error(errorData.message || 'Failed to fetch import status') + } + + const status = await response.json() + setImportStatus(status) + + // 프로젝트 코드가 없는 경우 에러 처리 + if (status.error) { + toast.error(`상태 확인 실패: ${status.error}`) + setImportStatus(null) + } + } catch (error) { + console.error('Failed to fetch import status:', error) + toast.error('DOLCE 상태를 확인할 수 없습니다. 프로젝트 설정을 확인해주세요.') + setImportStatus(null) + } finally { + setStatusLoading(false) + } + } + + // 컴포넌트 마운트 시 상태 조회 + React.useEffect(() => { + fetchImportStatus() + }, [contractId]) + + const handleImport = async () => { + if (!contractId) return + + setImportProgress(0) + setIsImporting(true) + + try { + // 진행률 시뮬레이션 + const progressInterval = setInterval(() => { + setImportProgress(prev => Math.min(prev + 15, 90)) + }, 300) + + const response = await fetch('/api/sync/import', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + contractId, + sourceSystem: 'DOLCE' + }) + }) + + if (!response.ok) { + const errorData = await response.json() + throw new Error(errorData.message || 'Import failed') + } + + const result = await response.json() + + clearInterval(progressInterval) + setImportProgress(100) + + setTimeout(() => { + setImportProgress(0) + setIsDialogOpen(false) + setIsImporting(false) + + if (result?.success) { + const { newCount = 0, updatedCount = 0, skippedCount = 0 } = result + toast.success( + `DOLCE 가져오기 완료`, + { + description: `신규 ${newCount}건, 업데이트 ${updatedCount}건, 건너뜀 ${skippedCount}건 (B3/B4/B5 포함)` + } + ) + } else { + toast.error( + `DOLCE 가져오기 부분 실패`, + { + description: result?.message || '일부 DrawingKind에서 가져오기에 실패했습니다.' + } + ) + } + + fetchImportStatus() // 상태 갱신 + onImportComplete?.() + }, 500) + + } catch (error) { + setImportProgress(0) + setIsImporting(false) + + toast.error('DOLCE 가져오기 실패', { + description: error instanceof Error ? error.message : '알 수 없는 오류가 발생했습니다.' + }) + } + } + + const getStatusBadge = () => { + if (statusLoading) { + return DOLCE 연결 확인 중... + } + + if (!importStatus) { + return DOLCE 연결 오류 + } + + if (!importStatus.importEnabled) { + return DOLCE 가져오기 비활성화 + } + + if (importStatus.newDocuments > 0 || importStatus.updatedDocuments > 0) { + return ( + + + 업데이트 가능 (B3/B4/B5) + + ) + } + + return ( + + + DOLCE와 동기화됨 + + ) + } + + const canImport = importStatus?.importEnabled && + (importStatus?.newDocuments > 0 || importStatus?.updatedDocuments > 0) + + return ( + <> + + +
+ +
+
+ + +
+
+

DOLCE 가져오기 상태

+
+ 현재 상태 + {getStatusBadge()} +
+
+ + {importStatus && ( +
+ + +
+
+
신규 문서
+
{importStatus.newDocuments || 0}건
+
+
+
업데이트
+
{importStatus.updatedDocuments || 0}건
+
+
+ +
+
DOLCE 전체 문서 (B3/B4/B5)
+
{importStatus.availableDocuments || 0}건
+
+ + {importStatus.lastImportAt && ( +
+
마지막 가져오기
+
+ {new Date(importStatus.lastImportAt).toLocaleString()} +
+
+ )} +
+ )} + + + +
+ + + +
+
+
+
+ + {/* 가져오기 진행 다이얼로그 */} + + + + DOLCE에서 문서 목록 가져오기 + + 삼성중공업 DOLCE 시스템에서 최신 문서 목록을 가져옵니다. + + + +
+ {importStatus && ( +
+
+ 가져올 항목 + + {(importStatus.newDocuments || 0) + (importStatus.updatedDocuments || 0)}건 + +
+ +
+ 신규 문서와 업데이트된 문서가 포함됩니다. (B3, B4, B5) +
+ B4 문서의 경우 GTTPreDwg, GTTWorkingDwg 이슈 스테이지가 자동 생성됩니다. +
+ + {isImporting && ( +
+
+ 진행률 + {importProgress}% +
+ +
+ )} +
+ )} + +
+ + +
+
+
+
+ + ) +} \ No newline at end of file -- cgit v1.2.3