/** * UPDATE 필드 매핑 패널 * UPDATE 액션용 조건 설정 및 필드 매핑 컴포넌트 */ import React, { useState, useEffect } from "react"; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { Badge } from "@/components/ui/badge"; import { Alert, AlertDescription } from "@/components/ui/alert"; import { Plus, X, Search, AlertCircle, ArrowRight } from "lucide-react"; import { Separator } from "@/components/ui/separator"; import { ColumnInfo, getColumnsFromConnection } from "@/lib/api/multiConnection"; import { useToast } from "@/hooks/use-toast"; export interface UpdateCondition { id: string; fromColumn: string; operator: "=" | "!=" | ">" | "<" | ">=" | "<=" | "LIKE" | "IN" | "NOT IN"; value: string | string[]; logicalOperator?: "AND" | "OR"; } export interface UpdateFieldMapping { id: string; fromColumn: string; toColumn: string; transformFunction?: string; defaultValue?: string; } export interface WhereCondition { id: string; toColumn: string; operator: "=" | "!=" | ">" | "<" | ">=" | "<=" | "LIKE" | "IN" | "NOT IN"; valueSource: "from_column" | "static" | "current_timestamp"; fromColumn?: string; staticValue?: string; logicalOperator?: "AND" | "OR"; } export interface UpdateFieldMappingPanelProps { action: any; actionIndex: number; settings: any; onSettingsChange: (settings: any) => void; fromConnectionId?: number; toConnectionId?: number; fromTableName?: string; toTableName?: string; disabled?: boolean; } export const UpdateFieldMappingPanel: React.FC = ({ action, actionIndex, settings, onSettingsChange, fromConnectionId, toConnectionId, fromTableName, toTableName, disabled = false, }) => { const { toast } = useToast(); // 상태 관리 const [fromTableColumns, setFromTableColumns] = useState([]); const [toTableColumns, setToTableColumns] = useState([]); const [updateConditions, setUpdateConditions] = useState([]); const [updateFields, setUpdateFields] = useState([]); const [whereConditions, setWhereConditions] = useState([]); const [loading, setLoading] = useState(false); // 검색 상태 const [fromColumnSearch, setFromColumnSearch] = useState(""); const [toColumnSearch, setToColumnSearch] = useState(""); // 컬럼 정보 로드 useEffect(() => { if (fromConnectionId !== undefined && fromTableName) { loadColumnInfo(fromConnectionId, fromTableName, setFromTableColumns, "FROM"); } }, [fromConnectionId, fromTableName]); useEffect(() => { if (toConnectionId !== undefined && toTableName) { loadColumnInfo(toConnectionId, toTableName, setToTableColumns, "TO"); } }, [toConnectionId, toTableName]); // 컬럼 정보 로드 함수 const loadColumnInfo = async ( connectionId: number, tableName: string, setColumns: React.Dispatch>, type: "FROM" | "TO", ) => { try { setLoading(true); const columns = await getColumnsFromConnection(connectionId, tableName); setColumns(columns); } catch (error) { console.error(`${type} 컬럼 정보 로드 실패:`, error); toast({ title: "컬럼 로드 실패", description: `${type} 테이블의 컬럼 정보를 불러오는데 실패했습니다.`, variant: "destructive", }); } finally { setLoading(false); } }; // 컬럼 필터링 const getFilteredColumns = (columns: ColumnInfo[], searchTerm: string) => { if (!searchTerm.trim()) return columns; const term = searchTerm.toLowerCase(); return columns.filter( (col) => col.columnName.toLowerCase().includes(term) || col.displayName.toLowerCase().includes(term), ); }; // UPDATE 조건 추가 const addUpdateCondition = () => { const newCondition: UpdateCondition = { id: `condition_${Date.now()}`, fromColumn: "", operator: "=", value: "", logicalOperator: updateConditions.length > 0 ? "AND" : undefined, }; setUpdateConditions([...updateConditions, newCondition]); }; // UPDATE 조건 제거 const removeUpdateCondition = (id: string) => { setUpdateConditions(updateConditions.filter((c) => c.id !== id)); }; // UPDATE 조건 수정 const updateCondition = (id: string, field: keyof UpdateCondition, value: any) => { setUpdateConditions(updateConditions.map((c) => (c.id === id ? { ...c, [field]: value } : c))); }; // UPDATE 필드 매핑 추가 const addUpdateFieldMapping = () => { const newMapping: UpdateFieldMapping = { id: `mapping_${Date.now()}`, fromColumn: "", toColumn: "", transformFunction: "", defaultValue: "", }; setUpdateFields([...updateFields, newMapping]); }; // UPDATE 필드 매핑 제거 const removeUpdateFieldMapping = (id: string) => { setUpdateFields(updateFields.filter((m) => m.id !== id)); }; // UPDATE 필드 매핑 수정 const updateFieldMapping = (id: string, field: keyof UpdateFieldMapping, value: any) => { setUpdateFields(updateFields.map((m) => (m.id === id ? { ...m, [field]: value } : m))); }; // WHERE 조건 추가 const addWhereCondition = () => { const newCondition: WhereCondition = { id: `where_${Date.now()}`, toColumn: "", operator: "=", valueSource: "from_column", fromColumn: "", staticValue: "", logicalOperator: whereConditions.length > 0 ? "AND" : undefined, }; setWhereConditions([...whereConditions, newCondition]); }; // WHERE 조건 제거 const removeWhereCondition = (id: string) => { setWhereConditions(whereConditions.filter((c) => c.id !== id)); }; // WHERE 조건 수정 const updateWhereCondition = (id: string, field: keyof WhereCondition, value: any) => { setWhereConditions(whereConditions.map((c) => (c.id === id ? { ...c, [field]: value } : c))); }; // 자기 자신 테이블 작업 경고 const getSelfTableWarning = () => { if (fromConnectionId === toConnectionId && fromTableName === toTableName) { return "⚠️ 자기 자신 테이블 UPDATE 작업입니다. 무한 루프 및 데이터 손상에 주의하세요."; } return null; }; const warningMessage = getSelfTableWarning(); const filteredFromColumns = getFilteredColumns(fromTableColumns, fromColumnSearch); const filteredToColumns = getFilteredColumns(toTableColumns, toColumnSearch); return (
{/* 경고 메시지 */} {warningMessage && ( {warningMessage} )} {/* UPDATE 조건 설정 */} 🔍 업데이트 조건 설정 FROM 테이블에서 어떤 조건을 만족하는 데이터가 있을 때 TO 테이블을 업데이트할지 설정하세요 {/* 검색 필드 */}
setFromColumnSearch(e.target.value)} className="pl-9" />
{/* 업데이트 조건 리스트 */}
{updateConditions.map((condition, index) => (
{index > 0 && ( )} updateCondition(condition.id, "value", e.target.value)} className="flex-1" />
))}
{/* UPDATE 필드 매핑 */} 📝 업데이트 필드 매핑 FROM 테이블의 값을 TO 테이블의 어떤 필드에 업데이트할지 설정하세요 {/* 검색 필드 */}
setFromColumnSearch(e.target.value)} className="pl-9" />
setToColumnSearch(e.target.value)} className="pl-9" />
{/* 필드 매핑 리스트 */}
{updateFields.map((mapping) => (
updateFieldMapping(mapping.id, "defaultValue", e.target.value)} className="w-32" />
))}
{/* WHERE 조건 설정 */} 🎯 업데이트 대상 조건 TO 테이블에서 어떤 레코드를 업데이트할지 WHERE 조건을 설정하세요 {/* WHERE 조건 리스트 */}
{whereConditions.map((condition, index) => (
{index > 0 && ( )} {condition.valueSource === "from_column" && ( )} {condition.valueSource === "static" && ( updateWhereCondition(condition.id, "staticValue", e.target.value)} className="w-32" /> )}
))}
{whereConditions.length === 0 && ( 안전을 위해 UPDATE 작업에는 최소 하나 이상의 WHERE 조건이 필요합니다. )}
); };