"use client"; import React, { useState, useCallback } 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 { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { Textarea } from "@/components/ui/textarea"; import { Checkbox } from "@/components/ui/checkbox"; import { Badge } from "@/components/ui/badge"; import { Plus, Trash2, ArrowRight, Settings, Eye, EyeOff, RefreshCw, Database, Globe } from "lucide-react"; import { FieldMapping, TableInfo, FieldInfo, DataDirection, DATA_TYPE_OPTIONS, TRANSFORM_TYPE_OPTIONS, } from "@/types/external-call/DataMappingTypes"; interface FieldMappingEditorProps { mappings: FieldMapping[]; onMappingsChange: (mappings: FieldMapping[]) => void; direction: "inbound" | "outbound"; sourceTable?: TableInfo; // outbound용 targetTable?: TableInfo; // inbound용 readonly?: boolean; } export const FieldMappingEditor: React.FC = ({ mappings, onMappingsChange, direction, sourceTable, targetTable, readonly = false, }) => { const [showAdvanced, setShowAdvanced] = useState(false); const [sampleApiData, setSampleApiData] = useState(""); // 새 매핑 추가 const addMapping = useCallback(() => { const newMapping: FieldMapping = { id: `mapping-${Date.now()}`, sourceField: "", targetField: "", dataType: "string", required: false, }; onMappingsChange([...mappings, newMapping]); }, [mappings, onMappingsChange]); // 매핑 삭제 const removeMapping = useCallback( (id: string) => { onMappingsChange(mappings.filter((m) => m.id !== id)); }, [mappings, onMappingsChange], ); // 매핑 업데이트 const updateMapping = useCallback( (id: string, updates: Partial) => { onMappingsChange(mappings.map((m) => (m.id === id ? { ...m, ...updates } : m))); }, [mappings, onMappingsChange], ); // 자동 매핑 (이름 기반) const autoMapFields = useCallback(() => { const currentTable = direction === "inbound" ? targetTable : sourceTable; if (!currentTable) return; const newMappings: FieldMapping[] = []; currentTable.fields.forEach((field) => { // 이미 매핑된 필드는 건너뛰기 const existingMapping = mappings.find((m) => direction === "inbound" ? m.targetField === field.name : m.sourceField === field.name, ); if (existingMapping) return; const mapping: FieldMapping = { id: `auto-${field.name}-${Date.now()}`, sourceField: direction === "inbound" ? field.name : field.name, targetField: direction === "inbound" ? field.name : field.name, dataType: field.dataType, required: !field.nullable, }; newMappings.push(mapping); }); onMappingsChange([...mappings, ...newMappings]); }, [direction, targetTable, sourceTable, mappings, onMappingsChange]); // 샘플 데이터에서 필드 추출 const extractFieldsFromSample = useCallback(() => { if (!sampleApiData.trim()) return; try { const parsed = JSON.parse(sampleApiData); const fields = Object.keys(parsed); const newMappings: FieldMapping[] = []; fields.forEach((fieldName) => { // 이미 매핑된 필드는 건너뛰기 const existingMapping = mappings.find((m) => direction === "inbound" ? m.sourceField === fieldName : m.targetField === fieldName, ); if (existingMapping) return; // 데이터 타입 추론 const value = parsed[fieldName]; let dataType: any = "string"; if (typeof value === "number") dataType = "number"; else if (typeof value === "boolean") dataType = "boolean"; else if (value instanceof Date || /^\d{4}-\d{2}-\d{2}/.test(value)) dataType = "date"; const mapping: FieldMapping = { id: `sample-${fieldName}-${Date.now()}`, sourceField: direction === "inbound" ? fieldName : "", targetField: direction === "inbound" ? "" : fieldName, dataType, required: false, }; newMappings.push(mapping); }); onMappingsChange([...mappings, ...newMappings]); setSampleApiData(""); } catch (error) { console.error("샘플 데이터 파싱 실패:", error); } }, [sampleApiData, direction, mappings, onMappingsChange]); const currentTable = direction === "inbound" ? targetTable : sourceTable; return (
필드 매핑 {mappings.length}개 매핑
{!readonly && ( )}
{/* 샘플 데이터 입력 (고급 모드) */} {showAdvanced && !readonly && (