diff options
| author | joonhoekim <26rote@gmail.com> | 2025-09-09 10:40:11 +0000 |
|---|---|---|
| committer | joonhoekim <26rote@gmail.com> | 2025-09-09 10:40:11 +0000 |
| commit | caa01b321311de3983fb8bcf65bb20a6c047cf57 (patch) | |
| tree | a01fb9078aaa044f57d8cdf59a06bf18a6e3e7df /components/material-groups/sync-button.tsx | |
| parent | 86b1fd1cc801f45642f84d24c0b5c84368454ff0 (diff) | |
(김준회) 자재그룹코드 및 자재그룹명에 대해 별도 테이블 생성, 동기화 로직 작성(일회성 사용이며 수신시점에는 자동저장하므로 추후 사용 불필요), 자재그룹 선택기를 변경사항에 맞춰 업데이트, 자재그룹명은 MAKTX 로 김학의 프로 답변에 따라 변경
Diffstat (limited to 'components/material-groups/sync-button.tsx')
| -rw-r--r-- | components/material-groups/sync-button.tsx | 240 |
1 files changed, 240 insertions, 0 deletions
diff --git a/components/material-groups/sync-button.tsx b/components/material-groups/sync-button.tsx new file mode 100644 index 00000000..7dc7da8f --- /dev/null +++ b/components/material-groups/sync-button.tsx @@ -0,0 +1,240 @@ +"use client"; + +import * as React from "react"; +import { Button } from "@/components/ui/button"; +import { Badge } from "@/components/ui/badge"; +import { + Dialog, + DialogContent, + DialogDescription, + DialogHeader, + DialogTitle, + DialogTrigger +} from "@/components/ui/dialog"; +import { + RefreshCw, + Database, + CheckCircle, + AlertCircle, + Clock, + TrendingUp +} from "lucide-react"; +import { useToast } from "@/hooks/use-toast"; +import { + syncMaterialGroupMaster, + getMaterialGroupSyncStatus, + type SyncResult +} from "@/lib/material-groups/sync-service"; + +export function MaterialGroupSyncButton() { + const [isOpen, setIsOpen] = React.useState(false); + const [isLoading, setIsLoading] = React.useState(false); + const [syncStatus, setSyncStatus] = React.useState<{ + totalMDGRecords: number; + totalMasterRecords: number; + lastSyncDate?: string; + } | null>(null); + const [lastSyncResult, setLastSyncResult] = React.useState<SyncResult | null>(null); + const { toast } = useToast(); + + // 다이얼로그 열릴 때 상태 정보 로드 + React.useEffect(() => { + if (isOpen) { + loadSyncStatus(); + } + }, [isOpen]); + + const loadSyncStatus = async () => { + try { + const status = await getMaterialGroupSyncStatus(); + if (status.success && status.data) { + setSyncStatus(status.data); + } + } catch (error) { + console.error("동기화 상태 로드 실패:", error); + } + }; + + const handleSync = async () => { + setIsLoading(true); + try { + const result = await syncMaterialGroupMaster(); + setLastSyncResult(result); + + if (result.success) { + toast({ + title: "동기화 완료", + description: result.message, + variant: "default", + }); + // 상태 다시 로드 + await loadSyncStatus(); + } else { + toast({ + title: "동기화 실패", + description: result.message, + variant: "destructive", + }); + } + } catch (error) { + toast({ + title: "동기화 오류", + description: error instanceof Error ? error.message : "알 수 없는 오류가 발생했습니다.", + variant: "destructive", + }); + } finally { + setIsLoading(false); + } + }; + + const formatDate = (dateString: string) => { + return new Date(dateString).toLocaleString('ko-KR', { + year: 'numeric', + month: '2-digit', + day: '2-digit', + hour: '2-digit', + minute: '2-digit' + }); + }; + + const getSyncBadgeVariant = () => { + if (!syncStatus) return "secondary"; + if (syncStatus.totalMDGRecords === syncStatus.totalMasterRecords) { + return "default"; // 동기화됨 + } + return "destructive"; // 동기화 필요 + }; + + return ( + <Dialog open={isOpen} onOpenChange={setIsOpen}> + <DialogTrigger asChild> + <Button variant="outline" size="sm" className="gap-2"> + <Database className="h-4 w-4" /> + 동기화 + {syncStatus && ( + <Badge variant={getSyncBadgeVariant()} className="ml-1"> + {syncStatus.totalMasterRecords} + </Badge> + )} + </Button> + </DialogTrigger> + + <DialogContent className="sm:max-w-[500px]"> + <DialogHeader> + <DialogTitle className="flex items-center gap-2"> + <Database className="h-5 w-5" /> + 자재그룹 마스터 동기화 + </DialogTitle> + <DialogDescription> + MDG 테이블로부터 자재그룹 마스터 데이터를 동기화합니다. + </DialogDescription> + </DialogHeader> + + <div className="space-y-4"> + {/* 현재 상태 */} + {syncStatus && ( + <div className="rounded-lg border p-4 space-y-3"> + <h4 className="font-medium flex items-center gap-2"> + <TrendingUp className="h-4 w-4" /> + 현재 상태 + </h4> + + <div className="grid grid-cols-2 gap-4 text-sm"> + <div> + <span className="text-muted-foreground">MDG 자재그룹:</span> + <div className="font-medium">{syncStatus.totalMDGRecords.toLocaleString()}개</div> + </div> + <div> + <span className="text-muted-foreground">마스터 테이블:</span> + <div className="font-medium">{syncStatus.totalMasterRecords.toLocaleString()}개</div> + </div> + </div> + + {syncStatus.lastSyncDate && ( + <div className="text-sm"> + <span className="text-muted-foreground flex items-center gap-1"> + <Clock className="h-3 w-3" /> + 최근 동기화: + </span> + <div className="font-medium">{formatDate(syncStatus.lastSyncDate)}</div> + </div> + )} + + <div className="flex items-center gap-2"> + <Badge variant={getSyncBadgeVariant()}> + {syncStatus.totalMDGRecords === syncStatus.totalMasterRecords + ? "동기화됨" + : "동기화 필요"} + </Badge> + </div> + </div> + )} + + {/* 마지막 동기화 결과 */} + {lastSyncResult && ( + <div className="rounded-lg border p-4 space-y-3"> + <h4 className="font-medium flex items-center gap-2"> + {lastSyncResult.success ? ( + <CheckCircle className="h-4 w-4 text-green-600" /> + ) : ( + <AlertCircle className="h-4 w-4 text-red-600" /> + )} + 마지막 동기화 결과 + </h4> + + <p className="text-sm text-muted-foreground"> + {lastSyncResult.message} + </p> + + {lastSyncResult.data && ( + <div className="grid grid-cols-3 gap-2 text-sm"> + <div className="text-center"> + <div className="font-medium text-green-600"> + {lastSyncResult.data.newRecords} + </div> + <div className="text-muted-foreground">신규</div> + </div> + <div className="text-center"> + <div className="font-medium text-blue-600"> + {lastSyncResult.data.updatedRecords} + </div> + <div className="text-muted-foreground">업데이트</div> + </div> + <div className="text-center"> + <div className="font-medium text-gray-600"> + {lastSyncResult.data.skippedRecords} + </div> + <div className="text-muted-foreground">건너뜀</div> + </div> + </div> + )} + </div> + )} + + {/* 동기화 실행 버튼 */} + <Button + onClick={handleSync} + disabled={isLoading} + className="w-full" + > + {isLoading ? ( + <> + <RefreshCw className="mr-2 h-4 w-4 animate-spin" /> + 동기화 중... + </> + ) : ( + <> + <RefreshCw className="mr-2 h-4 w-4" /> + 동기화 실행 + </> + )} + </Button> + + <p className="text-xs text-muted-foreground text-center"> + * 동기화는 기존 데이터를 업데이트하며 새로운 자재그룹을 추가합니다. + </p> + </div> + </DialogContent> + </Dialog> + ); +} |
