1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
|
"use client"
import * as React from "react"
import { type Table } from "@tanstack/react-table"
import { Download, RefreshCcw } from "lucide-react"
import { toast } from "sonner"
import { exportTableToExcel } from "@/lib/export"
import { Button } from "@/components/ui/button"
import { ExtendedFormMappings } from "../validation"
interface ItemsTableToolbarActionsProps {
table: Table<ExtendedFormMappings>
}
export function FormListsTableToolbarActions({ table }: ItemsTableToolbarActionsProps) {
const [isLoading, setIsLoading] = React.useState(false)
const [syncId, setSyncId] = React.useState<string | null>(null)
const pollingRef = React.useRef<NodeJS.Timeout | null>(null)
// Clean up polling on unmount
React.useEffect(() => {
return () => {
if (pollingRef.current) {
clearInterval(pollingRef.current)
}
}
}, [])
const startFormSync = async () => {
try {
setIsLoading(true)
// API 엔드포인트 호출 - 작업 시작만 요청
const response = await fetch('/api/cron/forms/start', {
method: 'POST'
})
if (!response.ok) {
const errorData = await response.json()
throw new Error(errorData.error || 'Failed to start form sync')
}
const data = await response.json()
// 작업 ID 저장
if (data.syncId) {
setSyncId(data.syncId)
toast.info('Form sync started. This may take a while...')
// 상태 확인을 위한 폴링 시작
startPolling(data.syncId)
} else {
throw new Error('No sync ID returned from server')
}
} catch (error) {
console.error('Error starting form sync:', error)
toast.error(
error instanceof Error
? error.message
: 'An error occurred while starting form sync'
)
setIsLoading(false)
}
}
const startPolling = (id: string) => {
// 이전 폴링이 있다면 제거
if (pollingRef.current) {
clearInterval(pollingRef.current)
}
// 5초마다 상태 확인
pollingRef.current = setInterval(async () => {
try {
const response = await fetch(`/api/cron/forms/status?id=${id}`)
if (!response.ok) {
throw new Error('Failed to get sync status')
}
const data = await response.json()
if (data.status === 'completed') {
// 폴링 중지
if (pollingRef.current) {
clearInterval(pollingRef.current)
pollingRef.current = null
}
// 상태 초기화
setIsLoading(false)
setSyncId(null)
// 성공 메시지 표시
toast.success(
`Forms synced successfully! ${data.result?.items || 0} items processed.`
)
// 테이블 데이터 업데이트 - 전체 페이지 새로고침 대신 데이터만 갱신
// table.resetRowSelection()
// 여기서 테이블 데이터를 다시 불러오는 함수를 호출
// 예: refetchTableData()
// 또는 SWR/React Query 등을 사용하고 있다면 mutate() 호출
// 리로드가 꼭 필요하다면 아래 주석 해제
window.location.reload()
} else if (data.status === 'failed') {
// 에러 처리
if (pollingRef.current) {
clearInterval(pollingRef.current)
pollingRef.current = null
}
setIsLoading(false)
setSyncId(null)
toast.error(data.error || 'Sync failed')
} else if (data.status === 'processing') {
// 진행 상태 업데이트 (선택적)
if (data.progress) {
toast.info(`Sync in progress: ${data.progress}%`, {
id: `sync-progress-${id}`,
})
}
}
} catch (error) {
console.error('Error checking sync status:', error)
}
}, 5000) // 5초마다 체크
}
return (
<div className="flex items-center gap-2">
{/** Sync Forms 버튼 */}
<Button
variant="samsung"
size="sm"
className="gap-2"
onClick={startFormSync}
disabled={isLoading}
>
<RefreshCcw className={`size-4 ${isLoading ? 'animate-spin' : ''}`} aria-hidden="true" />
<span className="hidden sm:inline">
{isLoading ? 'Syncing...' : 'Get Forms'}
</span>
</Button>
{/** Export 버튼 */}
<Button
variant="outline"
size="sm"
onClick={() =>
exportTableToExcel(table, {
filename: "Forms",
excludeColumns: ["select", "actions"],
})
}
className="gap-2"
>
<Download className="size-4" aria-hidden="true" />
<span className="hidden sm:inline">Export</span>
</Button>
</div>
)
}
|