diff options
| author | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-08-11 09:02:00 +0000 |
|---|---|---|
| committer | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-08-11 09:02:00 +0000 |
| commit | cbb4c7fe0b94459162ad5e998bc05cd293e0ff96 (patch) | |
| tree | 0a26712f7685e4f6511e637b9a81269d90a47c8f /lib/vendor-document-list/ship/import-from-dolce-button.tsx | |
| parent | eb654f88214095f71be142b989e620fd28db3f69 (diff) | |
(대표님) 입찰, EDP 변경사항 대응, spreadJS 오류 수정, 벤더실사 수정
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.tsx | 152 |
1 files changed, 78 insertions, 74 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 90796d8e..1ffe466d 100644 --- a/lib/vendor-document-list/ship/import-from-dolce-button.tsx +++ b/lib/vendor-document-list/ship/import-from-dolce-button.tsx @@ -25,6 +25,8 @@ import { SimplifiedDocumentsView } from "@/db/schema" import { ImportStatus } from "../import-service" import { useSession } from "next-auth/react" import { getProjectIdsByVendor } from "../service" +import { useParams } from "next/navigation" +import { useTranslation } from "@/i18n/client" // 🔥 API 응답 캐시 (컴포넌트 외부에 선언하여 인스턴스 간 공유) const statusCache = new Map<string, { data: ImportStatus; timestamp: number }>() @@ -69,6 +71,10 @@ export function ImportFromDOLCEButton({ const { data: session } = useSession() const vendorId = session?.user.companyId + const params = useParams() + const lng = (params?.lng as string) || "ko" + const { t } = useTranslation(lng, "engineering") + // 🔥 allDocuments에서 projectIds 추출 (props로 전달받은 경우 사용) const documentsProjectIds = React.useMemo(() => { if (propProjectIds) return propProjectIds // props로 받은 경우 그대로 사용 @@ -182,44 +188,34 @@ export function ImportFromDOLCEButton({ } catch (error) { console.error('Failed to fetch import statuses:', error) - toast.error('Unable to check status. Please verify project settings.') + toast.error(t('dolceImport.messages.statusCheckError')) } finally { setStatusLoading(false) } - }, [debouncedProjectIds, fetchImportStatusCached]) + }, [debouncedProjectIds, fetchImportStatusCached, t]) // 🔥 vendorId로 projects 가져오기 (최적화) React.useEffect(() => { - let isCancelled = false - - const fetchVendorProjects = async () => { - if (allDocuments.length === 0 && vendorId && !loadingVendorProjects) { - setLoadingVendorProjects(true) - try { - const projectIds = await getProjectIdsByVendor(vendorId) - if (!isCancelled) { - setVendorProjectIds(projectIds) - } - } catch (error) { - console.error('Failed to fetch vendor projects:', error) - if (!isCancelled) { - toast.error('Failed to fetch project information.') - } - } finally { - if (!isCancelled) { - setLoadingVendorProjects(false) - } - } - } - } - - fetchVendorProjects() - - return () => { - isCancelled = true - } - }, [allDocuments.length, vendorId, loadingVendorProjects]) - + let isCancelled = false; + + if (allDocuments.length !== 0 || !vendorId) return; + + setLoadingVendorProjects(true); + + getProjectIdsByVendor(vendorId) + .then((projectIds) => { + if (!isCancelled) setVendorProjectIds(projectIds); + }) + .catch((error) => { + if (!isCancelled) toast.error(t('dolceImport.messages.projectFetchError')); + }) + .finally(() => { + if (!isCancelled) setLoadingVendorProjects(false); + }); + + return () => { isCancelled = true; }; + }, [allDocuments, vendorId, t]); + // 🔥 컴포넌트 마운트 시 상태 조회 (디바운싱 적용) React.useEffect(() => { if (debouncedProjectIds.length > 0) { @@ -314,16 +310,21 @@ export function ImportFromDOLCEButton({ if (totalResult.success) { toast.success( - `DOLCE import completed`, + t('dolceImport.messages.importSuccess'), { - description: `New ${totalResult.newCount}, Updated ${totalResult.updatedCount}, Skipped ${totalResult.skippedCount} (${projectIds.length} projects)` + description: t('dolceImport.messages.importSuccessDescription', { + newCount: totalResult.newCount, + updatedCount: totalResult.updatedCount, + skippedCount: totalResult.skippedCount, + projectCount: projectIds.length + }) } ) } else { toast.error( - `DOLCE import partially failed`, + t('dolceImport.messages.importPartiallyFailed'), { - description: 'Some projects failed to import.' + description: t('dolceImport.messages.importPartiallyFailedDescription') } ) } @@ -338,35 +339,35 @@ export function ImportFromDOLCEButton({ setImportProgress(0) setIsImporting(false) - toast.error('DOLCE import failed', { - description: error instanceof Error ? error.message : 'An unknown error occurred.' + toast.error(t('dolceImport.messages.importFailed'), { + description: error instanceof Error ? error.message : t('dolceImport.messages.unknownError') }) } - }, [projectIds, fetchAllImportStatus, onImportComplete]) + }, [projectIds, fetchAllImportStatus, onImportComplete, t]) // 🔥 상태 뱃지 메모이제이션 const statusBadge = React.useMemo(() => { if (loadingVendorProjects) { - return <Badge variant="secondary">Loading project information...</Badge> + return <Badge variant="secondary">{t('dolceImport.status.loadingProjectInfo')}</Badge> } if (statusLoading) { - return <Badge variant="secondary">Checking DOLCE connection...</Badge> + return <Badge variant="secondary">{t('dolceImport.status.checkingConnection')}</Badge> } if (importStatusMap.size === 0) { - return <Badge variant="destructive">DOLCE Connection Error</Badge> + return <Badge variant="destructive">{t('dolceImport.status.connectionError')}</Badge> } if (!totalStats.importEnabled) { - return <Badge variant="secondary">DOLCE Import Disabled</Badge> + return <Badge variant="secondary">{t('dolceImport.status.importDisabled')}</Badge> } if (totalStats.newDocuments > 0 || totalStats.updatedDocuments > 0) { return ( <Badge variant="samsung" className="gap-1"> <AlertTriangle className="w-3 h-3" /> - Updates Available ({projectIds.length} projects) + {t('dolceImport.status.updatesAvailable', { projectCount: projectIds.length })} </Badge> ) } @@ -374,10 +375,10 @@ export function ImportFromDOLCEButton({ return ( <Badge variant="default" className="gap-1 bg-green-500 hover:bg-green-600"> <CheckCircle className="w-3 h-3" /> - Synchronized with DOLCE + {t('dolceImport.status.synchronized')} </Badge> ) - }, [loadingVendorProjects, statusLoading, importStatusMap.size, totalStats, projectIds.length]) + }, [loadingVendorProjects, statusLoading, importStatusMap.size, totalStats, projectIds.length, t]) const canImport = totalStats.importEnabled && (totalStats.newDocuments > 0 || totalStats.updatedDocuments > 0) @@ -389,7 +390,7 @@ export function ImportFromDOLCEButton({ }, [fetchAllImportStatus]) // 로딩 중이거나 projectIds가 없으면 버튼을 표시하지 않음 - if (loadingVendorProjects || projectIds.length === 0) { + if (projectIds.length === 0) { return null } @@ -409,7 +410,7 @@ export function ImportFromDOLCEButton({ ) : ( <Download className="w-4 h-4" /> )} - <span className="hidden sm:inline">Get List</span> + <span className="hidden sm:inline">{t('dolceImport.buttons.getList')}</span> {totalStats.newDocuments + totalStats.updatedDocuments > 0 && ( <Badge variant="samsung" @@ -425,9 +426,9 @@ export function ImportFromDOLCEButton({ <PopoverContent className="w-96"> <div className="space-y-4"> <div className="space-y-2"> - <h4 className="font-medium">DOLCE Import Status</h4> + <h4 className="font-medium">{t('dolceImport.labels.importStatus')}</h4> <div className="flex items-center justify-between"> - <span className="text-sm text-muted-foreground">Current Status</span> + <span className="text-sm text-muted-foreground">{t('dolceImport.labels.currentStatus')}</span> {statusBadge} </div> </div> @@ -435,17 +436,17 @@ export function ImportFromDOLCEButton({ {/* 프로젝트 소스 표시 */} {allDocuments.length === 0 && vendorProjectIds.length > 0 && ( <div className="text-xs text-blue-600 bg-blue-50 p-2 rounded"> - No documents found, importing from all projects. + {t('dolceImport.descriptions.noDocumentsImportAll')} </div> )} {/* 다중 프로젝트 정보 표시 */} {projectIds.length > 1 && ( <div className="text-sm"> - <div className="text-muted-foreground">Target Projects</div> - <div className="font-medium">{projectIds.length} projects</div> + <div className="text-muted-foreground">{t('dolceImport.labels.targetProjects')}</div> + <div className="font-medium">{t('dolceImport.labels.projectCount', { count: projectIds.length })}</div> <div className="text-xs text-muted-foreground"> - Project IDs: {projectIds.join(', ')} + {t('dolceImport.labels.projectIds')}: {projectIds.join(', ')} </div> </div> )} @@ -456,17 +457,17 @@ export function ImportFromDOLCEButton({ <div className="grid grid-cols-2 gap-4 text-sm"> <div> - <div className="text-muted-foreground">New Documents</div> + <div className="text-muted-foreground">{t('dolceImport.labels.newDocuments')}</div> <div className="font-medium">{totalStats.newDocuments || 0}</div> </div> <div> - <div className="text-muted-foreground">Updates</div> + <div className="text-muted-foreground">{t('dolceImport.labels.updates')}</div> <div className="font-medium">{totalStats.updatedDocuments || 0}</div> </div> </div> <div className="text-sm"> - <div className="text-muted-foreground">Total Documents (B3/B4/B5)</div> + <div className="text-muted-foreground">{t('dolceImport.labels.totalDocuments')}</div> <div className="font-medium">{totalStats.availableDocuments || 0}</div> </div> @@ -474,20 +475,23 @@ export function ImportFromDOLCEButton({ {projectIds.length > 1 && ( <details className="text-sm"> <summary className="cursor-pointer text-muted-foreground hover:text-foreground"> - Details by Project + {t('dolceImport.labels.detailsByProject')} </summary> <div className="mt-2 space-y-2 pl-2 border-l-2 border-muted"> {projectIds.map(projectId => { const status = importStatusMap.get(projectId) return ( <div key={projectId} className="text-xs"> - <div className="font-medium">Project {projectId}</div> + <div className="font-medium">{t('dolceImport.labels.projectLabel', { projectId })}</div> {status ? ( <div className="text-muted-foreground"> - New {status.newDocuments}, Updates {status.updatedDocuments} + {t('dolceImport.descriptions.projectDetails', { + newDocuments: status.newDocuments, + updatedDocuments: status.updatedDocuments + })} </div> ) : ( - <div className="text-destructive">Status check failed</div> + <div className="text-destructive">{t('dolceImport.status.statusCheckFailed')}</div> )} </div> ) @@ -510,12 +514,12 @@ export function ImportFromDOLCEButton({ {isImporting ? ( <> <Loader2 className="w-4 h-4 mr-2 animate-spin" /> - Importing... + {t('dolceImport.buttons.importing')} </> ) : ( <> <Download className="w-4 h-4 mr-2" /> - Import Now + {t('dolceImport.buttons.importNow')} </> )} </Button> @@ -541,10 +545,10 @@ export function ImportFromDOLCEButton({ <Dialog open={isDialogOpen} onOpenChange={setIsDialogOpen}> <DialogContent className="sm:max-w-md"> <DialogHeader> - <DialogTitle>Import Document List from DOLCE</DialogTitle> + <DialogTitle>{t('dolceImport.dialog.title')}</DialogTitle> <DialogDescription> - Import the latest document list from Samsung Heavy Industries DOLCE system. - {projectIds.length > 1 && ` (${projectIds.length} projects targeted)`} + {t('dolceImport.dialog.description')} + {projectIds.length > 1 && ` (${t('dolceImport.dialog.multipleProjects', { count: projectIds.length })})`} </DialogDescription> </DialogHeader> @@ -552,20 +556,20 @@ export function ImportFromDOLCEButton({ {totalStats && ( <div className="rounded-lg border p-4 space-y-3"> <div className="flex items-center justify-between text-sm"> - <span>Items to Import</span> + <span>{t('dolceImport.labels.itemsToImport')}</span> <span className="font-medium"> {totalStats.newDocuments + totalStats.updatedDocuments} </span> </div> <div className="text-xs text-muted-foreground"> - Includes new and updated documents (B3, B4, B5). + {t('dolceImport.descriptions.includesNewAndUpdated')} <br /> - For B4 documents, GTTPreDwg and GTTWorkingDwg issue stages will be auto-generated. + {t('dolceImport.descriptions.b4DocumentsNote')} {projectIds.length > 1 && ( <> <br /> - Will import sequentially from {projectIds.length} projects. + {t('dolceImport.descriptions.sequentialImport', { count: projectIds.length })} </> )} </div> @@ -573,7 +577,7 @@ export function ImportFromDOLCEButton({ {isImporting && ( <div className="space-y-2"> <div className="flex items-center justify-between text-sm"> - <span>Progress</span> + <span>{t('dolceImport.labels.progress')}</span> <span>{importProgress}%</span> </div> <Progress value={importProgress} className="h-2" /> @@ -588,7 +592,7 @@ export function ImportFromDOLCEButton({ onClick={() => setIsDialogOpen(false)} disabled={isImporting} > - Cancel + {t('buttons.cancel')} </Button> <Button onClick={handleImport} @@ -597,12 +601,12 @@ export function ImportFromDOLCEButton({ {isImporting ? ( <> <Loader2 className="w-4 h-4 mr-2 animate-spin" /> - Importing... + {t('dolceImport.buttons.importing')} </> ) : ( <> <Download className="w-4 h-4 mr-2" /> - Start Import + {t('dolceImport.buttons.startImport')} </> )} </Button> |
