summaryrefslogtreecommitdiff
path: root/hooks
diff options
context:
space:
mode:
authordujinkim <dujin.kim@dtsolution.co.kr>2025-10-01 10:31:23 +0000
committerdujinkim <dujin.kim@dtsolution.co.kr>2025-10-01 10:31:23 +0000
commit74843fe598702a9a55f914f2d2d291368a5abb13 (patch)
treea88abdaf039f51dd843e0416321f08877b17ea75 /hooks
parent33e8452331c301430191b3506825ebaf3edac93a (diff)
(대표님) dolce 수정, spreadjs 수정 등
Diffstat (limited to 'hooks')
-rw-r--r--hooks/use-sync-status.ts156
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