diff options
| author | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-10-01 10:31:23 +0000 |
|---|---|---|
| committer | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-10-01 10:31:23 +0000 |
| commit | 74843fe598702a9a55f914f2d2d291368a5abb13 (patch) | |
| tree | a88abdaf039f51dd843e0416321f08877b17ea75 /hooks | |
| parent | 33e8452331c301430191b3506825ebaf3edac93a (diff) | |
(대표님) dolce 수정, spreadjs 수정 등
Diffstat (limited to 'hooks')
| -rw-r--r-- | hooks/use-sync-status.ts | 156 |
1 files changed, 123 insertions, 33 deletions
diff --git a/hooks/use-sync-status.ts b/hooks/use-sync-status.ts index 52a67343..10ead6a6 100644 --- a/hooks/use-sync-status.ts +++ b/hooks/use-sync-status.ts @@ -1,19 +1,38 @@ -// hooks/use-sync-status.ts (완전히 개선된 버전) +// hooks/use-sync-status.ts (업데이트된 버전) import useSWR, { mutate as globalMutate } from 'swr' import useSWRMutation from 'swr/mutation' import * as React from 'react' +import { fabClasses } from '@mui/material' + +// 🔧 타입 정의 강화 - entityTypeDetails 추가 +interface EntityTypeDetail { + pending: number + synced: number + failed: number + total: number +} -// 🔧 타입 정의 강화 interface SyncStatus { syncEnabled: boolean pendingChanges: number syncedChanges: number failedChanges: number - lastSyncAt?: string | null + totalChanges: number error?: string | null projectId?: number + vendorId?: number targetSystem?: string lastUpdated?: string + requiresSync: boolean + // 새로 추가된 필드들 + entityTypeDetails?: { + document: EntityTypeDetail + revision: EntityTypeDetail + attachment: EntityTypeDetail + } + hasPendingChanges?: boolean + hasFailedChanges?: boolean + syncHealthy?: boolean } interface ApiError extends Error { @@ -76,20 +95,29 @@ const fetcher = async (url: string): Promise<any> => { } } -// 🔧 안전한 기본값 생성 +// 🔧 안전한 기본값 생성 - entityTypeDetails 추가 const createDefaultSyncStatus = (error?: string, projectId?: number): SyncStatus => ({ syncEnabled: false, + requiresSync:false, pendingChanges: 0, syncedChanges: 0, failedChanges: 0, - lastSyncAt: null, + totalChanges: 0, error, projectId, - lastUpdated: new Date().toISOString() + lastUpdated: new Date().toISOString(), + entityTypeDetails: { + document: { pending: 0, synced: 0, failed: 0, total: 0 }, + revision: { pending: 0, synced: 0, failed: 0, total: 0 }, + attachment: { pending: 0, synced: 0, failed: 0, total: 0 } + }, + hasPendingChanges: false, + hasFailedChanges: false, + syncHealthy: true }) -// ✅ 단일 계약 동기화 상태 조회 -export function useSyncStatus(projectId: number | null, targetSystem: string = 'SHI') { +// ✅ 단일 계약 동기화 상태 조회 - DOLCE를 기본값으로 변경 +export function useSyncStatus(projectId: number | null, targetSystem: string = 'DOLCE') { const key = projectId ? `/api/sync/status?projectId=${projectId}&targetSystem=${targetSystem}` : null @@ -120,7 +148,7 @@ export function useSyncStatus(projectId: number | null, targetSystem: string = ' } }, [key, localMutate]) - // 항상 안전한 데이터 반환 + // 항상 안전한 데이터 반환 - 새 필드들 포함 const safeData: SyncStatus = React.useMemo(() => { if (data && typeof data === 'object') { return { @@ -128,11 +156,20 @@ export function useSyncStatus(projectId: number | null, targetSystem: string = ' pendingChanges: Number(data.pendingChanges) || 0, syncedChanges: Number(data.syncedChanges) || 0, failedChanges: Number(data.failedChanges) || 0, - lastSyncAt: data.lastSyncAt || null, + totalChanges: Number(data.totalChanges) || 0, error: data.error || (error ? (error as ApiError).message : null), projectId: projectId || data.projectId, + vendorId: data.vendorId, targetSystem: targetSystem, - lastUpdated: new Date().toISOString() + lastUpdated: new Date().toISOString(), + entityTypeDetails: data.entityTypeDetails || { + document: { pending: 0, synced: 0, failed: 0, total: 0 }, + revision: { pending: 0, synced: 0, failed: 0, total: 0 }, + attachment: { pending: 0, synced: 0, failed: 0, total: 0 } + }, + hasPendingChanges: Boolean(data.hasPendingChanges), + hasFailedChanges: Boolean(data.hasFailedChanges), + syncHealthy: Boolean(data.syncHealthy) } } @@ -150,11 +187,8 @@ export function useSyncStatus(projectId: number | null, targetSystem: string = ' } } -// ❌ useMultipleSyncStatus 제거 (Hook 규칙 위반 때문에) -// 대신 useDynamicSyncStatus 사용 권장 - -// ✅ 다중 계약 동기화 상태 조회 (Hook 규칙 준수) -export function useDynamicSyncStatus(projectIds: number[], targetSystem: string = 'SHI') { +// ✅ 다중 계약 동기화 상태 조회 (Hook 규칙 준수) - DOLCE를 기본값으로 +export function useDynamicSyncStatus(projectIds: number[], targetSystem: string = 'DOLCE') { // Hook 규칙 준수: 고정된 수의 Hook 호출 const paddedContractIds = React.useMemo(() => { // 입력 검증 및 경고 @@ -172,6 +206,7 @@ export function useDynamicSyncStatus(projectIds: number[], targetSystem: string return padded }, [projectIds]) + // 각 contractId에 대해 고정된 수의 Hook 호출 const allResults = paddedContractIds.map((projectId) => { const result = useSyncStatus(projectId, targetSystem) @@ -189,13 +224,21 @@ export function useDynamicSyncStatus(projectIds: number[], targetSystem: string } => result !== null) }, [allResults]) - // 전체 통계 계산 + // 전체 통계 계산 - entityTypeDetails 포함 const totalStats = React.useMemo(() => { let totalPending = 0 let totalSynced = 0 let totalFailed = 0 + let totalChanges = 0 let hasError = false let isLoading = false + + // entityType별 합계 초기화 + const entityTypeDetailsTotals = { + document: { pending: 0, synced: 0, failed: 0, total: 0 }, + revision: { pending: 0, synced: 0, failed: 0, total: 0 }, + attachment: { pending: 0, synced: 0, failed: 0, total: 0 } + } validResults.forEach(({ syncStatus, error, isLoading: loading }) => { if (error) hasError = true @@ -205,6 +248,20 @@ export function useDynamicSyncStatus(projectIds: number[], targetSystem: string totalPending += Number(syncStatus.pendingChanges) || 0 totalSynced += Number(syncStatus.syncedChanges) || 0 totalFailed += Number(syncStatus.failedChanges) || 0 + totalChanges += Number(syncStatus.totalChanges) || 0 + + // entityTypeDetails 합계 계산 + if (syncStatus.entityTypeDetails) { + Object.keys(entityTypeDetailsTotals).forEach((entityType) => { + const key = entityType as keyof typeof entityTypeDetailsTotals + if (syncStatus.entityTypeDetails?.[key]) { + entityTypeDetailsTotals[key].pending += syncStatus.entityTypeDetails[key].pending || 0 + entityTypeDetailsTotals[key].synced += syncStatus.entityTypeDetails[key].synced || 0 + entityTypeDetailsTotals[key].failed += syncStatus.entityTypeDetails[key].failed || 0 + entityTypeDetailsTotals[key].total += syncStatus.entityTypeDetails[key].total || 0 + } + }) + } } }) @@ -212,9 +269,14 @@ export function useDynamicSyncStatus(projectIds: number[], targetSystem: string totalPending, totalSynced, totalFailed, + totalChanges, hasError, isLoading, - canSync: totalPending > 0 && !hasError && projectIds.length > 0 + canSync: totalPending > 0 && !hasError && projectIds.length > 0, + entityTypeDetailsTotals, + hasPendingChanges: totalPending > 0, + hasFailedChanges: totalFailed > 0, + syncHealthy: totalFailed === 0 && totalPending < 100 } }, [validResults, projectIds.length]) @@ -235,8 +297,8 @@ export function useDynamicSyncStatus(projectIds: number[], targetSystem: string } } -// ✅ 클라이언트 전용 동기화 상태 조회 (서버 사이드 렌더링 호환) -export function useClientSyncStatus(projectIds: number[], targetSystem: string = 'SHI',) { +// ✅ 클라이언트 전용 동기화 상태 조회 (서버 사이드 렌더링 호환) - DOLCE 기본값 +export function useClientSyncStatus(projectIds: number[], targetSystem: string = 'DOLCE') { const [isClient, setIsClient] = React.useState(false) React.useEffect(() => { @@ -256,9 +318,18 @@ export function useClientSyncStatus(projectIds: number[], targetSystem: string = totalPending: 0, totalSynced: 0, totalFailed: 0, + totalChanges: 0, hasError: false, isLoading: true, - canSync: false + canSync: false, + entityTypeDetailsTotals: { + document: { pending: 0, synced: 0, failed: 0, total: 0 }, + revision: { pending: 0, synced: 0, failed: 0, total: 0 }, + attachment: { pending: 0, synced: 0, failed: 0, total: 0 } + }, + hasPendingChanges: false, + hasFailedChanges: false, + syncHealthy: true }, refetchAll: () => {} } @@ -267,8 +338,8 @@ export function useClientSyncStatus(projectIds: number[], targetSystem: string = return syncResult } -// ✅ 동기화 배치 목록 조회 -export function useSyncBatches(projectId: number | null, targetSystem: string = 'SHI') { +// ✅ 동기화 배치 목록 조회 - DOLCE 기본값 +export function useSyncBatches(projectId: number | null, targetSystem: string = 'DOLCE') { const key = projectId ? `/api/sync/batches?projectId=${projectId}&targetSystem=${targetSystem}` : null @@ -290,8 +361,8 @@ export function useSyncBatches(projectId: number | null, targetSystem: string = } } -// ✅ 동기화 설정 조회 -export function useSyncConfig(projectId: number | null, targetSystem: string = 'SHI') { +// ✅ 동기화 설정 조회 - DOLCE 기본값 +export function useSyncConfig(projectId: number | null, targetSystem: string = 'DOLCE') { const key = projectId ? `/api/sync/config?projectId=${projectId}&targetSystem=${targetSystem}` : null @@ -319,7 +390,7 @@ export function useSyncConfig(projectId: number | null, targetSystem: string = ' } } -// ✅ 동기화 트리거 (뮤테이션) +// ✅ 동기화 트리거 (뮤테이션) - DOLCE 기본값 export function useTriggerSync() { const { trigger, isMutating, error } = useSWRMutation( '/api/sync/trigger', @@ -327,7 +398,7 @@ export function useTriggerSync() { const response = await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify(arg) + body: JSON.stringify({ ...arg, targetSystem: arg.targetSystem || 'DOLCE' }) }) if (!response.ok) { @@ -347,7 +418,7 @@ export function useTriggerSync() { const result = await trigger(arg) // 성공 후 관련 캐시 무효화 - const targetSystem = arg.targetSystem || 'SHI' + const targetSystem = arg.targetSystem || 'DOLCE' const statusKey = `/api/sync/status?projectId=${arg.projectId}&targetSystem=${targetSystem}` const batchesKey = `/api/sync/batches?projectId=${arg.projectId}&targetSystem=${targetSystem}` @@ -389,7 +460,6 @@ export function useUpdateSyncConfig() { return response.json() } - // ✅ onSuccess 콜백 제거 ) // ✅ 수동 캐시 무효화를 포함한 래핑 함수 @@ -415,8 +485,9 @@ export function useUpdateSyncConfig() { error: error as ApiError | null } } -// ✅ 실시간 동기화 상태 훅 (높은 갱신 빈도) -export function useRealtimeSyncStatus(projectId: number | null, targetSystem: string = 'SHI') { + +// ✅ 실시간 동기화 상태 훅 (높은 갱신 빈도) - DOLCE 기본값 +export function useRealtimeSyncStatus(projectId: number | null, targetSystem: string = 'DOLCE') { const key = projectId ? `/api/sync/status?projectId=${projectId}&targetSystem=${targetSystem}&realtime=true` : null @@ -449,8 +520,8 @@ export function useRealtimeSyncStatus(projectId: number | null, targetSystem: st // 🔧 유틸리티 함수들 export const syncUtils = { - // 캐시 수동 무효화 - invalidateCache: (projectId: number, targetSystem: string = 'SHI') => { + // 캐시 수동 무효화 - DOLCE 기본값 + invalidateCache: (projectId: number, targetSystem: string = 'DOLCE') => { const statusKey = `/api/sync/status?projectId=${projectId}&targetSystem=${targetSystem}` const batchesKey = `/api/sync/batches?projectId=${projectId}&targetSystem=${targetSystem}` const configKey = `/api/sync/config?projectId=${projectId}&targetSystem=${targetSystem}` @@ -476,5 +547,24 @@ export const syncUtils = { if (error.status >= 500) return '서버 오류가 발생했습니다' return error.message || '알 수 없는 오류가 발생했습니다' + }, + + // entityType별 통계 포맷터 + formatEntityTypeStats: (details?: SyncStatus['entityTypeDetails']): string => { + if (!details) return '통계 없음' + + const parts: string[] = [] + + if (details.document.total > 0) { + parts.push(`document: ${details.document.pending}/${details.document.total}`) + } + if (details.revision.total > 0) { + parts.push(`revision: ${details.revision.pending}/${details.revision.total}`) + } + if (details.attachment.total > 0) { + parts.push(`attachment: ${details.attachment.pending}/${details.attachment.total}`) + } + + return parts.length > 0 ? parts.join(', ') : '변경사항 없음' } }
\ No newline at end of file |
