"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" import { SimplifiedDocumentsView } from "@/db/schema" import { ImportStatus } from "../import-service" import { useSession } from "next-auth/react" import { getContractIdsByVendor } from "../service" // 서버 액션 import interface ImportFromDOLCEButtonProps { allDocuments: SimplifiedDocumentsView[] // contractId 대신 문서 배열 onImportComplete?: () => void } export function ImportFromDOLCEButton({ allDocuments, onImportComplete }: ImportFromDOLCEButtonProps) { const [isDialogOpen, setIsDialogOpen] = React.useState(false) const [importProgress, setImportProgress] = React.useState(0) const [isImporting, setIsImporting] = React.useState(false) const [importStatusMap, setImportStatusMap] = React.useState>(new Map()) const [statusLoading, setStatusLoading] = React.useState(false) const [vendorContractIds, setVendorContractIds] = React.useState([]) // 서버에서 가져온 contractIds const [loadingVendorContracts, setLoadingVendorContracts] = React.useState(false) const { data: session } = useSession() const vendorId = session?.user.companyId; // allDocuments에서 추출한 contractIds const documentsContractIds = React.useMemo(() => { const uniqueIds = [...new Set(allDocuments.map(doc => doc.contractId).filter(Boolean))] return uniqueIds.sort() }, [allDocuments]) // 최종 사용할 contractIds (allDocuments가 있으면 문서에서, 없으면 vendor의 모든 contracts) const contractIds = React.useMemo(() => { if (documentsContractIds.length > 0) { return documentsContractIds } return vendorContractIds }, [documentsContractIds, vendorContractIds]) console.log(contractIds, "contractIds") // vendorId로 contracts 가져오기 React.useEffect(() => { const fetchVendorContracts = async () => { // allDocuments가 비어있고 vendorId가 있을 때만 실행 if (allDocuments.length === 0 && vendorId) { setLoadingVendorContracts(true) try { const contractIds = await getContractIdsByVendor(vendorId) setVendorContractIds(contractIds) } catch (error) { console.error('Failed to fetch vendor contracts:', error) toast.error('Failed to fetch contract information.') } finally { setLoadingVendorContracts(false) } } } fetchVendorContracts() }, [allDocuments.length, vendorId]) // 주요 contractId (가장 많이 나타나는 것) const primaryContractId = React.useMemo(() => { if (contractIds.length === 1) return contractIds[0] if (allDocuments.length > 0) { const counts = allDocuments.reduce((acc, doc) => { const id = doc.contractId || 0 acc[id] = (acc[id] || 0) + 1 return acc }, {} as Record) return Number(Object.entries(counts) .sort(([,a], [,b]) => b - a)[0]?.[0] || contractIds[0] || 0) } return contractIds[0] || 0 }, [contractIds, allDocuments]) // 모든 contractId에 대한 상태 조회 const fetchAllImportStatus = async () => { if (contractIds.length === 0) return setStatusLoading(true) const statusMap = new Map() try { // 각 contractId별로 상태 조회 const statusPromises = contractIds.map(async (contractId) => { 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() if (status.error) { console.warn(`Status error for contract ${contractId}:`, status.error) return { contractId, status: null } } return { contractId, status } } catch (error) { console.error(`Failed to fetch status for contract ${contractId}:`, error) return { contractId, status: null } } }) const results = await Promise.all(statusPromises) results.forEach(({ contractId, status }) => { if (status) { statusMap.set(contractId, status) } }) setImportStatusMap(statusMap) } catch (error) { console.error('Failed to fetch import statuses:', error) toast.error('Unable to check status. Please verify project settings.') } finally { setStatusLoading(false) } } // 컴포넌트 마운트 시 상태 조회 React.useEffect(() => { if (contractIds.length > 0) { fetchAllImportStatus() } }, [contractIds]) // 주요 contractId의 상태 const primaryImportStatus = importStatusMap.get(primaryContractId) // 전체 통계 계산 const totalStats = React.useMemo(() => { const statuses = Array.from(importStatusMap.values()) return statuses.reduce((acc, status) => ({ availableDocuments: acc.availableDocuments + (status.availableDocuments || 0), newDocuments: acc.newDocuments + (status.newDocuments || 0), updatedDocuments: acc.updatedDocuments + (status.updatedDocuments || 0), importEnabled: acc.importEnabled || status.importEnabled }), { availableDocuments: 0, newDocuments: 0, updatedDocuments: 0, importEnabled: false }) }, [importStatusMap]) const handleImport = async () => { if (contractIds.length === 0) return setImportProgress(0) setIsImporting(true) try { // 진행률 시뮬레이션 const progressInterval = setInterval(() => { setImportProgress(prev => Math.min(prev + 10, 85)) }, 500) // 여러 contractId에 대해 순차적으로 가져오기 실행 const importPromises = contractIds.map(async (contractId) => { 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(`Contract ${contractId}: ${errorData.message || 'Import failed'}`) } return response.json() }) const results = await Promise.all(importPromises) clearInterval(progressInterval) setImportProgress(100) // 결과 집계 const totalResult = results.reduce((acc, result) => ({ newCount: acc.newCount + (result.newCount || 0), updatedCount: acc.updatedCount + (result.updatedCount || 0), skippedCount: acc.skippedCount + (result.skippedCount || 0), success: acc.success && result.success }), { newCount: 0, updatedCount: 0, skippedCount: 0, success: true }) setTimeout(() => { setImportProgress(0) setIsDialogOpen(false) setIsImporting(false) if (totalResult.success) { toast.success( `DOLCE import completed`, { description: `New ${totalResult.newCount}, Updated ${totalResult.updatedCount}, Skipped ${totalResult.skippedCount} (${contractIds.length} contracts)` } ) } else { toast.error( `DOLCE import partially failed`, { description: 'Some contracts failed to import.' } ) } fetchAllImportStatus() // 상태 갱신 onImportComplete?.() }, 500) } catch (error) { setImportProgress(0) setIsImporting(false) toast.error('DOLCE import failed', { description: error instanceof Error ? error.message : 'An unknown error occurred.' }) } } const getStatusBadge = () => { if (loadingVendorContracts) { return Loading contract information... } if (statusLoading) { return Checking DOLCE connection... } if (importStatusMap.size === 0) { return DOLCE Connection Error } if (!totalStats.importEnabled) { return DOLCE Import Disabled } if (totalStats.newDocuments > 0 || totalStats.updatedDocuments > 0) { return ( Updates Available ({contractIds.length} contracts) ) } return ( Synchronized with DOLCE ) } const canImport = totalStats.importEnabled && (totalStats.newDocuments > 0 || totalStats.updatedDocuments > 0) // 로딩 중이거나 contractIds가 없으면 버튼을 표시하지 않음 if (loadingVendorContracts || contractIds.length === 0) { return null } return ( <>

DOLCE Import Status

Current Status {getStatusBadge()}
{/* 계약 소스 표시 */} {allDocuments.length === 0 && vendorContractIds.length > 0 && (
No documents found, importing from all contracts.
)} {/* 다중 계약 정보 표시 */} {contractIds.length > 1 && (
Target Contracts
{contractIds.length} contracts
Contract IDs: {contractIds.join(', ')}
)} {totalStats && (
New Documents
{totalStats.newDocuments || 0}
Updates
{totalStats.updatedDocuments || 0}
Total DOLCE Documents (B3/B4/B5)
{totalStats.availableDocuments || 0}
{/* 각 계약별 세부 정보 (펼치기/접기 가능) */} {contractIds.length > 1 && (
Details by Contract
{contractIds.map(contractId => { const status = importStatusMap.get(contractId) return (
Contract {contractId}
{status ? (
New {status.newDocuments}, Updates {status.updatedDocuments}
) : (
Status check failed
)}
) })}
)}
)}
{/* 가져오기 진행 다이얼로그 */} Import Document List from DOLCE Import the latest document list from Samsung Heavy Industries DOLCE system. {contractIds.length > 1 && ` (${contractIds.length} contracts targeted)`}
{totalStats && (
Items to Import {totalStats.newDocuments + totalStats.updatedDocuments}
Includes new and updated documents (B3, B4, B5).
For B4 documents, GTTPreDwg and GTTWorkingDwg issue stages will be auto-generated. {contractIds.length > 1 && ( <>
Will import sequentially from {contractIds.length} contracts. )}
{isImporting && (
Progress {importProgress}%
)}
)}
) }