"use client"; import React, { useState, useEffect } from "react"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Textarea } from "@/components/ui/textarea"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { Badge } from "@/components/ui/badge"; import { ArrowLeft, Save, RefreshCw, ArrowRight, Trash2 } from "lucide-react"; import { toast } from "sonner"; import { useRouter } from "next/navigation"; import { BatchAPI, BatchMapping, ConnectionInfo, ColumnInfo, BatchMappingRequest, } from "@/lib/api/batch"; export default function BatchCreatePage() { const router = useRouter(); // 기본 정보 const [batchName, setBatchName] = useState(""); const [cronSchedule, setCronSchedule] = useState("0 12 * * *"); const [description, setDescription] = useState(""); // 커넥션 및 데이터 const [connections, setConnections] = useState([]); const [fromConnection, setFromConnection] = useState(null); const [toConnection, setToConnection] = useState(null); const [fromTables, setFromTables] = useState([]); const [toTables, setToTables] = useState([]); const [fromTable, setFromTable] = useState(""); const [toTable, setToTable] = useState(""); const [fromColumns, setFromColumns] = useState([]); const [toColumns, setToColumns] = useState([]); // 매핑 상태 const [selectedFromColumn, setSelectedFromColumn] = useState(null); const [mappings, setMappings] = useState([]); // 로딩 상태 const [loading, setLoading] = useState(false); const [loadingConnections, setLoadingConnections] = useState(false); // 커넥션 목록 로드 useEffect(() => { loadConnections(); }, []); const loadConnections = async () => { setLoadingConnections(true); try { const data = await BatchAPI.getConnections(); setConnections(Array.isArray(data) ? data : []); } catch (error) { console.error("커넥션 로드 실패:", error); toast.error("커넥션 목록을 불러오는데 실패했습니다."); setConnections([]); } finally { setLoadingConnections(false); } }; // FROM 커넥션 변경 const handleFromConnectionChange = async (connectionId: string) => { if (connectionId === 'unknown') return; const connection = connections.find(conn => { if (conn.type === 'internal') { return connectionId === 'internal'; } return conn.id ? conn.id.toString() === connectionId : false; }); if (!connection) return; setFromConnection(connection); setFromTable(""); setFromTables([]); setFromColumns([]); setSelectedFromColumn(null); try { const tables = await BatchAPI.getTablesFromConnection(connection); setFromTables(Array.isArray(tables) ? tables : []); } catch (error) { console.error("FROM 테이블 목록 로드 실패:", error); toast.error("테이블 목록을 불러오는데 실패했습니다."); } }; // TO 커넥션 변경 const handleToConnectionChange = async (connectionId: string) => { if (connectionId === 'unknown') return; const connection = connections.find(conn => { if (conn.type === 'internal') { return connectionId === 'internal'; } return conn.id ? conn.id.toString() === connectionId : false; }); if (!connection) return; setToConnection(connection); setToTable(""); setToTables([]); setToColumns([]); try { const tables = await BatchAPI.getTablesFromConnection(connection); setToTables(Array.isArray(tables) ? tables : []); } catch (error) { console.error("TO 테이블 목록 로드 실패:", error); toast.error("테이블 목록을 불러오는데 실패했습니다."); } }; // FROM 테이블 변경 const handleFromTableChange = async (tableName: string) => { setFromTable(tableName); setFromColumns([]); setSelectedFromColumn(null); if (!fromConnection || !tableName) return; try { const columns = await BatchAPI.getTableColumns(fromConnection, tableName); setFromColumns(Array.isArray(columns) ? columns : []); } catch (error) { console.error("FROM 컬럼 목록 로드 실패:", error); toast.error("컬럼 목록을 불러오는데 실패했습니다."); } }; // TO 테이블 변경 const handleToTableChange = async (tableName: string) => { setToTable(tableName); setToColumns([]); if (!toConnection || !tableName) return; try { const columns = await BatchAPI.getTableColumns(toConnection, tableName); setToColumns(Array.isArray(columns) ? columns : []); } catch (error) { console.error("TO 컬럼 목록 로드 실패:", error); toast.error("컬럼 목록을 불러오는데 실패했습니다."); } }; // FROM 컬럼 선택 const handleFromColumnClick = (column: ColumnInfo) => { setSelectedFromColumn(column); toast.info(`FROM 컬럼 선택됨: ${column.column_name}`); }; // TO 컬럼 선택 (매핑 생성) const handleToColumnClick = (toColumn: ColumnInfo) => { if (!selectedFromColumn || !fromConnection || !toConnection) { toast.error("먼저 FROM 컬럼을 선택해주세요."); return; } // n:1 매핑 검사 const toKey = `${toConnection.type}:${toConnection.id || 'internal'}:${toTable}:${toColumn.column_name}`; const existingMapping = mappings.find(mapping => { const existingToKey = `${mapping.to_connection_type}:${mapping.to_connection_id || 'internal'}:${mapping.to_table_name}:${mapping.to_column_name}`; return existingToKey === toKey; }); if (existingMapping) { toast.error("동일한 TO 컬럼에 중복 매핑할 수 없습니다. (n:1 매핑 방지)"); return; } const newMapping: BatchMapping = { from_connection_type: fromConnection.type, from_connection_id: fromConnection.id || null, from_table_name: fromTable, from_column_name: selectedFromColumn.column_name, from_column_type: selectedFromColumn.data_type || '', to_connection_type: toConnection.type, to_connection_id: toConnection.id || null, to_table_name: toTable, to_column_name: toColumn.column_name, to_column_type: toColumn.data_type || '', mapping_order: mappings.length + 1, }; setMappings([...mappings, newMapping]); setSelectedFromColumn(null); toast.success(`매핑 생성: ${selectedFromColumn.column_name} → ${toColumn.column_name}`); }; // 매핑 삭제 const removeMapping = (index: number) => { const newMappings = mappings.filter((_, i) => i !== index); const reorderedMappings = newMappings.map((mapping, i) => ({ ...mapping, mapping_order: i + 1 })); setMappings(reorderedMappings); toast.success("매핑이 삭제되었습니다."); }; // 배치 설정 저장 const saveBatchConfig = async () => { if (!batchName.trim()) { toast.error("배치명을 입력해주세요."); return; } if (!cronSchedule.trim()) { toast.error("실행 스케줄을 입력해주세요."); return; } if (mappings.length === 0) { toast.error("최소 하나 이상의 매핑을 추가해주세요."); return; } setLoading(true); try { const request = { batchName: batchName, description: description || undefined, cronSchedule: cronSchedule, mappings: mappings, isActive: true }; await BatchAPI.createBatchConfig(request); toast.success("배치 설정이 성공적으로 저장되었습니다!"); // 목록 페이지로 이동 router.push("/admin/batchmng"); } catch (error) { console.error("배치 설정 저장 실패:", error); toast.error("배치 설정 저장에 실패했습니다."); } finally { setLoading(false); } }; return (
{/* 헤더 */}

배치관리 매핑 시스템

새로운 배치 매핑을 생성합니다.

{/* 기본 정보 */} 기본 정보
setBatchName(e.target.value)} placeholder="배치명을 입력하세요" />
setCronSchedule(e.target.value)} placeholder="0 12 * * * (매일 12시)" />