summaryrefslogtreecommitdiff
path: root/components/material-groups/sync-button.tsx
diff options
context:
space:
mode:
authorjoonhoekim <26rote@gmail.com>2025-09-09 10:40:11 +0000
committerjoonhoekim <26rote@gmail.com>2025-09-09 10:40:11 +0000
commitcaa01b321311de3983fb8bcf65bb20a6c047cf57 (patch)
treea01fb9078aaa044f57d8cdf59a06bf18a6e3e7df /components/material-groups/sync-button.tsx
parent86b1fd1cc801f45642f84d24c0b5c84368454ff0 (diff)
(김준회) 자재그룹코드 및 자재그룹명에 대해 별도 테이블 생성, 동기화 로직 작성(일회성 사용이며 수신시점에는 자동저장하므로 추후 사용 불필요), 자재그룹 선택기를 변경사항에 맞춰 업데이트, 자재그룹명은 MAKTX 로 김학의 프로 답변에 따라 변경
Diffstat (limited to 'components/material-groups/sync-button.tsx')
-rw-r--r--components/material-groups/sync-button.tsx240
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>
+ );
+}