// components/sync/send-to-shi-button.tsx (최종 완성 버전) "use client" import * as React from "react" import { Send, Loader2, CheckCircle, AlertTriangle, Settings, RefreshCw } 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 { ScrollArea } from "@/components/ui/scroll-area" import { Alert, AlertDescription } from "@/components/ui/alert" // ✅ 업데이트된 Hook import import { useClientSyncStatus, useTriggerSync, syncUtils } from "@/hooks/use-sync-status" import type { EnhancedDocument } from "@/types/enhanced-documents" interface SendToSHIButtonProps { documents?: EnhancedDocument[] onSyncComplete?: () => void projectType: "ship" | "plant" } export function SendToSHIButton({ documents = [], onSyncComplete, projectType }: SendToSHIButtonProps) { const [isDialogOpen, setIsDialogOpen] = React.useState(false) const [syncProgress, setSyncProgress] = React.useState(0) const [currentSyncingContract, setCurrentSyncingContract] = React.useState(null) const targetSystem = projectType === 'ship' ? "DOLCE" : "SWP" // 문서에서 유효한 계약 ID 목록 추출 const documentsContractIds = React.useMemo(() => { const validIds = documents .map(doc => doc.contractId) .filter((id): id is number => typeof id === 'number' && id > 0) const uniqueIds = [...new Set(validIds)] return uniqueIds.sort() }, [documents]) // ✅ 클라이언트 전용 Hook 사용 (서버 사이드 렌더링 호환) const { contractStatuses, totalStats, refetchAll } = useClientSyncStatus( documentsContractIds, targetSystem ) const { triggerSync, isLoading: isSyncing, error: syncError } = useTriggerSync() // 개발 환경에서 디버깅 정보 React.useEffect(() => { if (process.env.NODE_ENV === 'development') { console.log('SendToSHIButton Debug Info:', { documentsContractIds, totalStats, contractStatuses: contractStatuses.map(({ contractId, syncStatus, error }) => ({ contractId, pendingChanges: syncStatus?.pendingChanges, hasError: !!error })) }) } }, [documentsContractIds, totalStats, contractStatuses]) // 동기화 실행 함수 const handleSync = async () => { if (documentsContractIds.length === 0) { toast.info('동기화할 계약이 없습니다.') return } setSyncProgress(0) let successfulSyncs = 0 let failedSyncs = 0 let totalSuccessCount = 0 let totalFailureCount = 0 const errors: string[] = [] try { // 동기화 가능한 계약들만 필터링 const contractsToSync = contractStatuses.filter(({ syncStatus, error }) => { if (error) { console.warn(`Contract ${contractStatuses.find(c => c.error === error)?.contractId} has error:`, error) return false } if (!syncStatus) return false if (!syncStatus.syncEnabled) return false if (syncStatus.pendingChanges <= 0) return false return true }) if (contractsToSync.length === 0) { toast.info('동기화할 변경사항이 없습니다.') setIsDialogOpen(false) return } console.log(`Starting sync for ${contractsToSync.length} contracts`) // 각 contract별로 순차 동기화 for (let i = 0; i < contractsToSync.length; i++) { const { contractId } = contractsToSync[i] setCurrentSyncingContract(contractId) try { console.log(`Syncing contract ${contractId}...`) const result = await triggerSync({ contractId, targetSystem }) if (result?.success) { successfulSyncs++ totalSuccessCount += result.successCount || 0 console.log(`Contract ${contractId} sync successful:`, result) } else { failedSyncs++ totalFailureCount += result?.failureCount || 0 const errorMsg = result?.errors?.[0] || result?.message || 'Unknown sync error' errors.push(`Contract ${contractId}: ${errorMsg}`) console.error(`Contract ${contractId} sync failed:`, result) } } catch (error) { failedSyncs++ const errorMessage = error instanceof Error ? error.message : '알 수 없는 오류' errors.push(`Contract ${contractId}: ${errorMessage}`) console.error(`Contract ${contractId} sync exception:`, error) } // 진행률 업데이트 setSyncProgress(((i + 1) / contractsToSync.length) * 100) } setCurrentSyncingContract(null) // 결과 처리 및 토스트 표시 setTimeout(() => { setSyncProgress(0) setIsDialogOpen(false) if (failedSyncs === 0) { toast.success( `모든 계약 동기화 완료: ${totalSuccessCount}건 성공`, { description: `${successfulSyncs}개 계약에서 ${totalSuccessCount}개 항목이 SHI 시스템으로 전송되었습니다.` } ) } else if (successfulSyncs > 0) { toast.warning( `부분 동기화 완료: ${successfulSyncs}개 성공, ${failedSyncs}개 실패`, { description: errors.slice(0, 3).join(', ') + (errors.length > 3 ? ' 외 더보기...' : '') } ) } else { toast.error( `동기화 실패: ${failedSyncs}개 계약 모두 실패`, { description: errors[0] || '모든 계약 동기화에 실패했습니다.' } ) } // 모든 contract 상태 갱신 refetchAll() onSyncComplete?.() }, 500) } catch (error) { setSyncProgress(0) setCurrentSyncingContract(null) const errorMessage = syncUtils.formatError(error as any) toast.error('동기화 실패', { description: errorMessage }) console.error('Sync process failed:', error) } } // 동기화 상태에 따른 뱃지 생성 const getSyncStatusBadge = () => { if (totalStats.isLoading) { return ( 확인 중... ) } if (totalStats.hasError) { return ( 연결 오류 ) } if (documentsContractIds.length === 0) { return 계약 없음 } if (totalStats.totalPending > 0) { return ( {totalStats.totalPending}건 대기 ) } if (totalStats.totalSynced > 0) { return ( 동기화됨 ) } return 변경사항 없음 } return ( <>

SHI 동기화 상태

전체 상태 {getSyncStatusBadge()}
{documentsContractIds.length}개 계약 대상 • {targetSystem} 시스템
{/* 에러 상태 표시 */} {totalStats.hasError && ( 일부 계약의 동기화 상태를 확인할 수 없습니다. 네트워크 연결을 확인해주세요. {process.env.NODE_ENV === 'development' && (
Debug: {contractStatuses.filter(({ error }) => error).length}개 계약에서 오류
)}
)} {/* 정상 상태일 때 통계 표시 */} {!totalStats.hasError && documentsContractIds.length > 0 && (
대기 중
{totalStats.totalPending}건
동기화됨
{totalStats.totalSynced}건
실패
{totalStats.totalFailed}건
{/* 계약별 상세 상태 */} {contractStatuses.length > 1 && (
계약별 상태
{contractStatuses.map(({ contractId, syncStatus, isLoading, error }) => (
Contract {contractId} {isLoading ? ( 로딩... ) : error ? ( 오류 ) : syncStatus && syncStatus.pendingChanges > 0 ? ( {syncStatus.pendingChanges}건 대기 ) : ( 최신 )}
))}
)}
)} {/* 계약 정보가 없는 경우 */} {documentsContractIds.length === 0 && ( 동기화할 문서가 없습니다. 문서를 선택해주세요. )} {/* 액션 버튼들 */}
{/* 동기화 진행 다이얼로그 */} SHI 시스템으로 동기화 {documentsContractIds.length}개 계약의 변경된 문서 데이터를 {targetSystem} 시스템으로 전송합니다.
{!totalStats.hasError && documentsContractIds.length > 0 && (
전송 대상 {totalStats.totalPending}건
대상 계약 {documentsContractIds.length}개
문서, 리비전, 첨부파일의 변경사항이 포함됩니다.
{isSyncing && (
진행률 {Math.round(syncProgress)}%
{currentSyncingContract && (
현재 처리 중: Contract {currentSyncingContract}
)}
)}
)} {totalStats.hasError && ( 일부 계약의 동기화 상태를 확인할 수 없습니다. 네트워크 연결을 확인하고 다시 시도해주세요. )} {documentsContractIds.length === 0 && ( 동기화할 계약이 없습니다. 문서를 선택해주세요. )}
) }