summaryrefslogtreecommitdiff
path: root/lib/vendor-document-list/ship/import-from-dolce-button.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'lib/vendor-document-list/ship/import-from-dolce-button.tsx')
-rw-r--r--lib/vendor-document-list/ship/import-from-dolce-button.tsx258
1 files changed, 185 insertions, 73 deletions
diff --git a/lib/vendor-document-list/ship/import-from-dolce-button.tsx b/lib/vendor-document-list/ship/import-from-dolce-button.tsx
index 519d40cb..23d80981 100644
--- a/lib/vendor-document-list/ship/import-from-dolce-button.tsx
+++ b/lib/vendor-document-list/ship/import-from-dolce-button.tsx
@@ -20,52 +20,87 @@ import {
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"
interface ImportFromDOLCEButtonProps {
- contractId: number
+ allDocuments: SimplifiedDocumentsView[] // contractId 대신 문서 배열
onImportComplete?: () => void
}
-interface ImportStatus {
- lastImportAt?: string
- availableDocuments: number
- newDocuments: number
- updatedDocuments: number
- importEnabled: boolean
-}
-
export function ImportFromDOLCEButton({
- contractId,
+ allDocuments,
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<ImportStatus | null>(null)
+ const [importStatusMap, setImportStatusMap] = React.useState<Map<number, ImportStatus>>(new Map())
const [statusLoading, setStatusLoading] = React.useState(false)
- // DOLCE 상태 조회
- const fetchImportStatus = async () => {
+ // 문서들에서 contractId들 추출
+ const contractIds = React.useMemo(() => {
+ const uniqueIds = [...new Set(allDocuments.map(doc => doc.contractId).filter(Boolean))]
+ return uniqueIds.sort()
+ }, [allDocuments])
+
+ console.log(contractIds, "contractIds")
+
+ // 주요 contractId (가장 많이 나타나는 것)
+ const primaryContractId = React.useMemo(() => {
+ if (contractIds.length === 1) return contractIds[0]
+
+ const counts = allDocuments.reduce((acc, doc) => {
+ const id = doc.contractId || 0
+ acc[id] = (acc[id] || 0) + 1
+ return acc
+ }, {} as Record<number, number>)
+
+ return Number(Object.entries(counts)
+ .sort(([,a], [,b]) => b - a)[0]?.[0] || contractIds[0] || 0)
+ }, [contractIds, allDocuments])
+
+ // 모든 contractId에 대한 상태 조회
+ const fetchAllImportStatus = async () => {
setStatusLoading(true)
+ const statusMap = new Map<number, ImportStatus>()
+
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')
- }
+ // 각 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)
+ }
+ })
- const status = await response.json()
- setImportStatus(status)
+ setImportStatusMap(statusMap)
- // 프로젝트 코드가 없는 경우 에러 처리
- if (status.error) {
- toast.error(`상태 확인 실패: ${status.error}`)
- setImportStatus(null)
- }
} catch (error) {
- console.error('Failed to fetch import status:', error)
- toast.error('DOLCE 상태를 확인할 수 없습니다. 프로젝트 설정을 확인해주세요.')
- setImportStatus(null)
+ console.error('Failed to fetch import statuses:', error)
+ toast.error('상태를 확인할 수 없습니다. 프로젝트 설정을 확인해주세요.')
} finally {
setStatusLoading(false)
}
@@ -73,11 +108,32 @@ export function ImportFromDOLCEButton({
// 컴포넌트 마운트 시 상태 조회
React.useEffect(() => {
- fetchImportStatus()
- }, [contractId])
+ 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 (!contractId) return
+ if (contractIds.length === 0) return
setImportProgress(0)
setIsImporting(true)
@@ -85,51 +141,68 @@ export function ImportFromDOLCEButton({
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'
+ 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(errorData.message || 'Import failed')
- }
+ if (!response.ok) {
+ const errorData = await response.json()
+ throw new Error(`Contract ${contractId}: ${errorData.message || 'Import failed'}`)
+ }
+
+ return response.json()
+ })
- const result = await 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 (result?.success) {
- const { newCount = 0, updatedCount = 0, skippedCount = 0 } = result
+ if (totalResult.success) {
toast.success(
`DOLCE 가져오기 완료`,
{
- description: `신규 ${newCount}건, 업데이트 ${updatedCount}건, 건너뜀 ${skippedCount}건 (B3/B4/B5 포함)`
+ description: `신규 ${totalResult.newCount}건, 업데이트 ${totalResult.updatedCount}건, 건너뜀 ${totalResult.skippedCount}건 (${contractIds.length}개 계약)`
}
)
} else {
toast.error(
`DOLCE 가져오기 부분 실패`,
{
- description: result?.message || '일부 DrawingKind에서 가져오기에 실패했습니다.'
+ description: '일부 계약에서 가져오기에 실패했습니다.'
}
)
}
- fetchImportStatus() // 상태 갱신
+ fetchAllImportStatus() // 상태 갱신
onImportComplete?.()
}, 500)
@@ -148,19 +221,19 @@ export function ImportFromDOLCEButton({
return <Badge variant="secondary">DOLCE 연결 확인 중...</Badge>
}
- if (!importStatus) {
+ if (importStatusMap.size === 0) {
return <Badge variant="destructive">DOLCE 연결 오류</Badge>
}
- if (!importStatus.importEnabled) {
+ if (!totalStats.importEnabled) {
return <Badge variant="secondary">DOLCE 가져오기 비활성화</Badge>
}
- if (importStatus.newDocuments > 0 || importStatus.updatedDocuments > 0) {
+ if (totalStats.newDocuments > 0 || totalStats.updatedDocuments > 0) {
return (
<Badge variant="default" className="gap-1 bg-blue-500 hover:bg-blue-600">
<AlertTriangle className="w-3 h-3" />
- 업데이트 가능 (B3/B4/B5)
+ 업데이트 가능 ({contractIds.length}개 계약)
</Badge>
)
}
@@ -173,8 +246,12 @@ export function ImportFromDOLCEButton({
)
}
- const canImport = importStatus?.importEnabled &&
- (importStatus?.newDocuments > 0 || importStatus?.updatedDocuments > 0)
+ const canImport = totalStats.importEnabled &&
+ (totalStats.newDocuments > 0 || totalStats.updatedDocuments > 0)
+
+ if (contractIds.length === 0) {
+ return null // 계약이 없으면 버튼을 표시하지 않음
+ }
return (
<>
@@ -193,19 +270,19 @@ export function ImportFromDOLCEButton({
<Download className="w-4 h-4" />
)}
<span className="hidden sm:inline">DOLCE에서 가져오기</span>
- {importStatus && (importStatus.newDocuments > 0 || importStatus.updatedDocuments > 0) && (
+ {totalStats.newDocuments + totalStats.updatedDocuments > 0 && (
<Badge
variant="default"
className="h-5 w-5 p-0 text-xs flex items-center justify-center bg-blue-500"
>
- {importStatus.newDocuments + importStatus.updatedDocuments}
+ {totalStats.newDocuments + totalStats.updatedDocuments}
</Badge>
)}
</Button>
</div>
</PopoverTrigger>
- <PopoverContent className="w-80">
+ <PopoverContent className="w-96">
<div className="space-y-4">
<div className="space-y-2">
<h4 className="font-medium">DOLCE 가져오기 상태</h4>
@@ -215,33 +292,61 @@ export function ImportFromDOLCEButton({
</div>
</div>
- {importStatus && (
+ {/* 다중 계약 정보 표시 */}
+ {contractIds.length > 1 && (
+ <div className="text-sm">
+ <div className="text-muted-foreground">대상 계약</div>
+ <div className="font-medium">{contractIds.length}개 계약</div>
+ <div className="text-xs text-muted-foreground">
+ Contract IDs: {contractIds.join(', ')}
+ </div>
+ </div>
+ )}
+
+ {totalStats && (
<div className="space-y-3">
<Separator />
<div className="grid grid-cols-2 gap-4 text-sm">
<div>
<div className="text-muted-foreground">신규 문서</div>
- <div className="font-medium">{importStatus.newDocuments || 0}건</div>
+ <div className="font-medium">{totalStats.newDocuments || 0}건</div>
</div>
<div>
<div className="text-muted-foreground">업데이트</div>
- <div className="font-medium">{importStatus.updatedDocuments || 0}건</div>
+ <div className="font-medium">{totalStats.updatedDocuments || 0}건</div>
</div>
</div>
<div className="text-sm">
<div className="text-muted-foreground">DOLCE 전체 문서 (B3/B4/B5)</div>
- <div className="font-medium">{importStatus.availableDocuments || 0}건</div>
+ <div className="font-medium">{totalStats.availableDocuments || 0}건</div>
</div>
- {importStatus.lastImportAt && (
- <div className="text-sm">
- <div className="text-muted-foreground">마지막 가져오기</div>
- <div className="font-medium">
- {new Date(importStatus.lastImportAt).toLocaleString()}
+ {/* 각 계약별 세부 정보 (펼치기/접기 가능) */}
+ {contractIds.length > 1 && (
+ <details className="text-sm">
+ <summary className="cursor-pointer text-muted-foreground hover:text-foreground">
+ 계약별 세부 정보
+ </summary>
+ <div className="mt-2 space-y-2 pl-2 border-l-2 border-muted">
+ {contractIds.map(contractId => {
+ const status = importStatusMap.get(contractId)
+ return (
+ <div key={contractId} className="text-xs">
+ <div className="font-medium">Contract {contractId}</div>
+ {status ? (
+ <div className="text-muted-foreground">
+ 신규 {status.newDocuments}건, 업데이트 {status.updatedDocuments}건
+ </div>
+ ) : (
+ <div className="text-destructive">상태 확인 실패</div>
+ )}
+ </div>
+ )
+ })}
</div>
- </div>
+ </details>
)}
</div>
)}
@@ -271,7 +376,7 @@ export function ImportFromDOLCEButton({
<Button
variant="outline"
size="sm"
- onClick={fetchImportStatus}
+ onClick={fetchAllImportStatus}
disabled={statusLoading}
>
{statusLoading ? (
@@ -292,16 +397,17 @@ export function ImportFromDOLCEButton({
<DialogTitle>DOLCE에서 문서 목록 가져오기</DialogTitle>
<DialogDescription>
삼성중공업 DOLCE 시스템에서 최신 문서 목록을 가져옵니다.
+ {contractIds.length > 1 && ` (${contractIds.length}개 계약 대상)`}
</DialogDescription>
</DialogHeader>
<div className="space-y-4">
- {importStatus && (
+ {totalStats && (
<div className="rounded-lg border p-4 space-y-3">
<div className="flex items-center justify-between text-sm">
<span>가져올 항목</span>
<span className="font-medium">
- {(importStatus.newDocuments || 0) + (importStatus.updatedDocuments || 0)}건
+ {totalStats.newDocuments + totalStats.updatedDocuments}건
</span>
</div>
@@ -309,6 +415,12 @@ export function ImportFromDOLCEButton({
신규 문서와 업데이트된 문서가 포함됩니다. (B3, B4, B5)
<br />
B4 문서의 경우 GTTPreDwg, GTTWorkingDwg 이슈 스테이지가 자동 생성됩니다.
+ {contractIds.length > 1 && (
+ <>
+ <br />
+ {contractIds.length}개 계약에서 순차적으로 가져옵니다.
+ </>
+ )}
</div>
{isImporting && (