2025-09-05 16:19:31 +09:00
|
|
|
"use client";
|
|
|
|
|
|
2025-09-16 15:43:18 +09:00
|
|
|
import React, { useState, useEffect, useCallback } from "react";
|
2025-09-05 16:19:31 +09:00
|
|
|
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from "@/components/ui/dialog";
|
|
|
|
|
import { Button } from "@/components/ui/button";
|
|
|
|
|
import { Input } from "@/components/ui/input";
|
|
|
|
|
import { Label } from "@/components/ui/label";
|
2025-09-16 15:43:18 +09:00
|
|
|
import { Link } from "lucide-react";
|
2025-09-12 09:58:49 +09:00
|
|
|
import { DataFlowAPI, TableRelationship, TableInfo, ColumnInfo, ConditionNode } from "@/lib/api/dataflow";
|
2025-09-16 15:43:18 +09:00
|
|
|
import {
|
|
|
|
|
ConnectionConfig,
|
|
|
|
|
SimpleKeySettings,
|
|
|
|
|
DataSaveSettings,
|
|
|
|
|
ExternalCallSettings,
|
|
|
|
|
ConnectionSetupModalProps,
|
|
|
|
|
} from "@/types/connectionTypes";
|
|
|
|
|
import { isConditionalConnection } from "@/utils/connectionUtils";
|
|
|
|
|
import { useConditionManager } from "@/hooks/useConditionManager";
|
|
|
|
|
import { ConditionalSettings } from "./condition/ConditionalSettings";
|
|
|
|
|
import { ConnectionTypeSelector } from "./connection/ConnectionTypeSelector";
|
|
|
|
|
import { SimpleKeySettings as SimpleKeySettingsComponent } from "./connection/SimpleKeySettings";
|
|
|
|
|
import { DataSaveSettings as DataSaveSettingsComponent } from "./connection/DataSaveSettings";
|
|
|
|
|
import { ExternalCallSettings as ExternalCallSettingsComponent } from "./connection/ExternalCallSettings";
|
2025-09-08 16:46:53 +09:00
|
|
|
import toast from "react-hot-toast";
|
2025-09-05 16:19:31 +09:00
|
|
|
|
|
|
|
|
export const ConnectionSetupModal: React.FC<ConnectionSetupModalProps> = ({
|
|
|
|
|
isOpen,
|
|
|
|
|
connection,
|
2025-09-08 16:46:53 +09:00
|
|
|
companyCode,
|
2025-09-05 16:19:31 +09:00
|
|
|
onConfirm,
|
|
|
|
|
onCancel,
|
|
|
|
|
}) => {
|
2025-09-05 18:00:18 +09:00
|
|
|
const [config, setConfig] = useState<ConnectionConfig>({
|
|
|
|
|
relationshipName: "",
|
|
|
|
|
connectionType: "simple-key",
|
|
|
|
|
fromColumnName: "",
|
|
|
|
|
toColumnName: "",
|
2025-09-08 16:46:53 +09:00
|
|
|
settings: {},
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 연결 종류별 설정 상태
|
|
|
|
|
const [simpleKeySettings, setSimpleKeySettings] = useState<SimpleKeySettings>({
|
|
|
|
|
notes: "",
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const [dataSaveSettings, setDataSaveSettings] = useState<DataSaveSettings>({
|
2025-09-12 16:15:36 +09:00
|
|
|
actions: [],
|
2025-09-08 16:46:53 +09:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const [externalCallSettings, setExternalCallSettings] = useState<ExternalCallSettings>({
|
|
|
|
|
callType: "rest-api",
|
|
|
|
|
apiUrl: "",
|
|
|
|
|
httpMethod: "POST",
|
|
|
|
|
headers: "{}",
|
|
|
|
|
bodyTemplate: "{}",
|
2025-09-05 18:00:18 +09:00
|
|
|
});
|
2025-09-05 16:19:31 +09:00
|
|
|
|
2025-09-16 15:43:18 +09:00
|
|
|
// 테이블 및 컬럼 선택을 위한 상태들
|
2025-09-10 17:25:41 +09:00
|
|
|
const [availableTables, setAvailableTables] = useState<TableInfo[]>([]);
|
|
|
|
|
const [selectedFromTable, setSelectedFromTable] = useState<string>("");
|
|
|
|
|
const [selectedToTable, setSelectedToTable] = useState<string>("");
|
|
|
|
|
const [fromTableColumns, setFromTableColumns] = useState<ColumnInfo[]>([]);
|
|
|
|
|
const [toTableColumns, setToTableColumns] = useState<ColumnInfo[]>([]);
|
|
|
|
|
const [selectedFromColumns, setSelectedFromColumns] = useState<string[]>([]);
|
|
|
|
|
const [selectedToColumns, setSelectedToColumns] = useState<string[]>([]);
|
2025-09-12 16:15:36 +09:00
|
|
|
const [tableColumnsCache, setTableColumnsCache] = useState<{ [tableName: string]: ColumnInfo[] }>({});
|
2025-09-10 17:25:41 +09:00
|
|
|
|
2025-09-16 15:43:18 +09:00
|
|
|
// 조건 관리 훅 사용
|
|
|
|
|
const {
|
|
|
|
|
conditions,
|
|
|
|
|
setConditions,
|
|
|
|
|
addCondition,
|
|
|
|
|
addGroupStart,
|
|
|
|
|
addGroupEnd,
|
|
|
|
|
updateCondition,
|
|
|
|
|
removeCondition,
|
|
|
|
|
getCurrentGroupLevel,
|
|
|
|
|
} = useConditionManager();
|
|
|
|
|
|
|
|
|
|
// 기존 설정 로드 함수
|
|
|
|
|
const loadExistingSettings = useCallback(
|
|
|
|
|
(settings: Record<string, unknown>, connectionType: string) => {
|
|
|
|
|
if (connectionType === "simple-key" && settings.notes) {
|
|
|
|
|
setSimpleKeySettings({
|
|
|
|
|
notes: settings.notes as string,
|
|
|
|
|
});
|
|
|
|
|
} else if (connectionType === "data-save" && settings.actions) {
|
|
|
|
|
// data-save 설정 로드 - 안전하게 처리
|
|
|
|
|
const actionsData = Array.isArray(settings.actions) ? settings.actions : [];
|
|
|
|
|
setDataSaveSettings({
|
|
|
|
|
actions: actionsData.map((action: Record<string, unknown>) => ({
|
|
|
|
|
id: (action.id as string) || `action-${Date.now()}`,
|
|
|
|
|
name: (action.name as string) || "새 액션",
|
|
|
|
|
actionType: (action.actionType as "insert" | "update" | "delete" | "upsert") || "insert",
|
|
|
|
|
conditions: Array.isArray(action.conditions)
|
|
|
|
|
? (action.conditions as ConditionNode[]).map((condition) => ({
|
|
|
|
|
...condition,
|
|
|
|
|
operator: condition.operator || "=", // 기본값 보장
|
|
|
|
|
}))
|
|
|
|
|
: [],
|
|
|
|
|
fieldMappings: Array.isArray(action.fieldMappings)
|
|
|
|
|
? action.fieldMappings.map((mapping: Record<string, unknown>) => ({
|
|
|
|
|
sourceTable: (mapping.sourceTable as string) || "",
|
|
|
|
|
sourceField: (mapping.sourceField as string) || "",
|
|
|
|
|
targetTable: (mapping.targetTable as string) || "",
|
|
|
|
|
targetField: (mapping.targetField as string) || "",
|
|
|
|
|
defaultValue: (mapping.defaultValue as string) || "",
|
|
|
|
|
transformFunction: (mapping.transformFunction as string) || "",
|
|
|
|
|
}))
|
|
|
|
|
: [],
|
|
|
|
|
splitConfig: action.splitConfig
|
|
|
|
|
? {
|
|
|
|
|
sourceField: ((action.splitConfig as Record<string, unknown>).sourceField as string) || "",
|
|
|
|
|
delimiter: ((action.splitConfig as Record<string, unknown>).delimiter as string) || ",",
|
|
|
|
|
targetField: ((action.splitConfig as Record<string, unknown>).targetField as string) || "",
|
|
|
|
|
}
|
|
|
|
|
: undefined,
|
|
|
|
|
})),
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 전체 실행 조건 로드
|
|
|
|
|
if (settings.control) {
|
|
|
|
|
const controlSettings = settings.control as { conditionTree?: ConditionNode[] };
|
|
|
|
|
if (Array.isArray(controlSettings.conditionTree)) {
|
|
|
|
|
setConditions(controlSettings.conditionTree || []);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else if (connectionType === "external-call") {
|
|
|
|
|
setExternalCallSettings({
|
|
|
|
|
callType: (settings.callType as "rest-api" | "webhook") || "rest-api",
|
|
|
|
|
apiUrl: (settings.apiUrl as string) || "",
|
|
|
|
|
httpMethod: (settings.httpMethod as "GET" | "POST" | "PUT" | "DELETE") || "POST",
|
|
|
|
|
headers: (settings.headers as string) || "{}",
|
|
|
|
|
bodyTemplate: (settings.bodyTemplate as string) || "{}",
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
[setConditions, setSimpleKeySettings, setDataSaveSettings, setExternalCallSettings],
|
|
|
|
|
);
|
2025-09-12 09:58:49 +09:00
|
|
|
|
2025-09-10 17:25:41 +09:00
|
|
|
// 테이블 목록 로드
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
const loadTables = async () => {
|
|
|
|
|
try {
|
|
|
|
|
const tables = await DataFlowAPI.getTables();
|
|
|
|
|
setAvailableTables(tables);
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error("테이블 목록 로드 실패:", error);
|
|
|
|
|
toast.error("테이블 목록을 불러오는데 실패했습니다.");
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if (isOpen) {
|
|
|
|
|
loadTables();
|
|
|
|
|
}
|
|
|
|
|
}, [isOpen]);
|
|
|
|
|
|
2025-09-05 18:00:18 +09:00
|
|
|
// 모달이 열릴 때 기본값 설정
|
2025-09-05 16:19:31 +09:00
|
|
|
useEffect(() => {
|
|
|
|
|
if (isOpen && connection) {
|
2025-09-10 17:25:41 +09:00
|
|
|
const fromTableName = connection.fromNode.tableName;
|
|
|
|
|
const toTableName = connection.toNode.tableName;
|
|
|
|
|
const fromDisplayName = connection.fromNode.displayName;
|
|
|
|
|
const toDisplayName = connection.toNode.displayName;
|
|
|
|
|
|
|
|
|
|
// 테이블 선택 설정
|
|
|
|
|
setSelectedFromTable(fromTableName);
|
|
|
|
|
setSelectedToTable(toTableName);
|
2025-09-05 18:00:18 +09:00
|
|
|
|
2025-09-11 10:45:16 +09:00
|
|
|
// 기존 관계 정보가 있으면 사용, 없으면 기본값 설정
|
|
|
|
|
const existingRel = connection.existingRelationship;
|
2025-09-16 12:37:57 +09:00
|
|
|
const connectionType =
|
|
|
|
|
(existingRel?.connectionType as "simple-key" | "data-save" | "external-call") || "simple-key";
|
|
|
|
|
|
2025-09-05 18:00:18 +09:00
|
|
|
setConfig({
|
2025-09-11 10:45:16 +09:00
|
|
|
relationshipName: existingRel?.relationshipName || `${fromDisplayName} → ${toDisplayName}`,
|
2025-09-16 12:37:57 +09:00
|
|
|
connectionType,
|
2025-09-05 18:00:18 +09:00
|
|
|
fromColumnName: "",
|
|
|
|
|
toColumnName: "",
|
2025-09-11 10:45:16 +09:00
|
|
|
settings: existingRel?.settings || {},
|
2025-09-08 16:46:53 +09:00
|
|
|
});
|
|
|
|
|
|
2025-09-16 15:43:18 +09:00
|
|
|
// 기존 설정 데이터 로드
|
2025-09-16 12:37:57 +09:00
|
|
|
if (existingRel?.settings) {
|
2025-09-16 15:43:18 +09:00
|
|
|
loadExistingSettings(existingRel.settings, connectionType);
|
2025-09-16 12:37:57 +09:00
|
|
|
} else {
|
|
|
|
|
// 기본값 설정
|
|
|
|
|
setSimpleKeySettings({
|
|
|
|
|
notes: `${fromDisplayName}과 ${toDisplayName} 간의 키값 연결`,
|
|
|
|
|
});
|
2025-09-16 15:43:18 +09:00
|
|
|
setDataSaveSettings({ actions: [] });
|
2025-09-16 12:37:57 +09:00
|
|
|
}
|
2025-09-08 16:46:53 +09:00
|
|
|
|
2025-09-16 15:43:18 +09:00
|
|
|
// 필드 선택 상태 초기화
|
2025-09-15 20:07:28 +09:00
|
|
|
setSelectedFromColumns([]);
|
|
|
|
|
setSelectedToColumns([]);
|
|
|
|
|
|
2025-09-10 17:25:41 +09:00
|
|
|
// 선택된 컬럼 정보가 있다면 설정
|
|
|
|
|
if (connection.selectedColumnsData) {
|
|
|
|
|
const fromColumns = connection.selectedColumnsData[fromTableName]?.columns || [];
|
|
|
|
|
const toColumns = connection.selectedColumnsData[toTableName]?.columns || [];
|
|
|
|
|
|
|
|
|
|
setSelectedFromColumns(fromColumns);
|
|
|
|
|
setSelectedToColumns(toColumns);
|
|
|
|
|
|
|
|
|
|
setConfig((prev) => ({
|
|
|
|
|
...prev,
|
|
|
|
|
fromColumnName: fromColumns.join(", "),
|
|
|
|
|
toColumnName: toColumns.join(", "),
|
|
|
|
|
}));
|
|
|
|
|
}
|
2025-09-05 16:19:31 +09:00
|
|
|
}
|
2025-09-16 15:43:18 +09:00
|
|
|
}, [isOpen, connection, setConditions, loadExistingSettings]);
|
2025-09-05 16:19:31 +09:00
|
|
|
|
2025-09-10 17:25:41 +09:00
|
|
|
// From 테이블 선택 시 컬럼 로드
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
const loadFromColumns = async () => {
|
|
|
|
|
if (selectedFromTable) {
|
|
|
|
|
try {
|
|
|
|
|
const columns = await DataFlowAPI.getTableColumns(selectedFromTable);
|
|
|
|
|
setFromTableColumns(columns);
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error("From 테이블 컬럼 로드 실패:", error);
|
|
|
|
|
toast.error("From 테이블 컬럼을 불러오는데 실패했습니다.");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
loadFromColumns();
|
|
|
|
|
}, [selectedFromTable]);
|
|
|
|
|
|
|
|
|
|
// To 테이블 선택 시 컬럼 로드
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
const loadToColumns = async () => {
|
|
|
|
|
if (selectedToTable) {
|
|
|
|
|
try {
|
|
|
|
|
const columns = await DataFlowAPI.getTableColumns(selectedToTable);
|
|
|
|
|
setToTableColumns(columns);
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error("To 테이블 컬럼 로드 실패:", error);
|
|
|
|
|
toast.error("To 테이블 컬럼을 불러오는데 실패했습니다.");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
loadToColumns();
|
|
|
|
|
}, [selectedToTable]);
|
|
|
|
|
|
|
|
|
|
// 선택된 컬럼들이 변경될 때 config 업데이트
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
setConfig((prev) => ({
|
|
|
|
|
...prev,
|
|
|
|
|
fromColumnName: selectedFromColumns.join(", "),
|
|
|
|
|
toColumnName: selectedToColumns.join(", "),
|
|
|
|
|
}));
|
|
|
|
|
}, [selectedFromColumns, selectedToColumns]);
|
|
|
|
|
|
2025-09-12 16:15:36 +09:00
|
|
|
// 테이블 컬럼 로드 함수 (캐시 활용)
|
|
|
|
|
const loadTableColumns = async (tableName: string): Promise<ColumnInfo[]> => {
|
|
|
|
|
if (tableColumnsCache[tableName]) {
|
|
|
|
|
return tableColumnsCache[tableName];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
const columns = await DataFlowAPI.getTableColumns(tableName);
|
|
|
|
|
setTableColumnsCache((prev) => ({
|
|
|
|
|
...prev,
|
|
|
|
|
[tableName]: columns,
|
|
|
|
|
}));
|
|
|
|
|
return columns;
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error(`${tableName} 컬럼 로드 실패:`, error);
|
|
|
|
|
return [];
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 테이블 선택 시 컬럼 로드
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
const loadColumns = async () => {
|
|
|
|
|
const tablesToLoad = new Set<string>();
|
|
|
|
|
|
|
|
|
|
// 필드 매핑에서 사용되는 모든 테이블 수집
|
2025-09-16 15:43:18 +09:00
|
|
|
dataSaveSettings.actions?.forEach((action) => {
|
|
|
|
|
action.fieldMappings?.forEach((mapping) => {
|
2025-09-12 16:15:36 +09:00
|
|
|
if (mapping.sourceTable && !tableColumnsCache[mapping.sourceTable]) {
|
|
|
|
|
tablesToLoad.add(mapping.sourceTable);
|
|
|
|
|
}
|
|
|
|
|
if (mapping.targetTable && !tableColumnsCache[mapping.targetTable]) {
|
|
|
|
|
tablesToLoad.add(mapping.targetTable);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 필요한 테이블들의 컬럼만 로드
|
|
|
|
|
for (const tableName of tablesToLoad) {
|
|
|
|
|
await loadTableColumns(tableName);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
loadColumns();
|
|
|
|
|
}, [dataSaveSettings.actions, tableColumnsCache]); // eslint-disable-line react-hooks/exhaustive-deps
|
|
|
|
|
|
2025-09-10 15:30:14 +09:00
|
|
|
const handleConfirm = () => {
|
2025-09-08 16:46:53 +09:00
|
|
|
if (!config.relationshipName || !connection) {
|
|
|
|
|
toast.error("필수 정보를 모두 입력해주세요.");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-10 15:30:14 +09:00
|
|
|
// 연결 종류별 설정을 준비
|
|
|
|
|
let settings = {};
|
|
|
|
|
|
|
|
|
|
switch (config.connectionType) {
|
|
|
|
|
case "simple-key":
|
|
|
|
|
settings = simpleKeySettings;
|
|
|
|
|
break;
|
|
|
|
|
case "data-save":
|
|
|
|
|
settings = dataSaveSettings;
|
|
|
|
|
break;
|
|
|
|
|
case "external-call":
|
|
|
|
|
settings = externalCallSettings;
|
|
|
|
|
break;
|
|
|
|
|
}
|
2025-09-08 16:46:53 +09:00
|
|
|
|
2025-09-15 15:12:02 +09:00
|
|
|
// 단순 키값 연결일 때만 컬럼 선택 검증
|
|
|
|
|
if (config.connectionType === "simple-key") {
|
2025-09-16 12:37:57 +09:00
|
|
|
if (selectedFromColumns.length === 0 || selectedToColumns.length === 0) {
|
|
|
|
|
toast.error("선택된 컬럼이 없습니다. From과 To 테이블에서 각각 최소 1개 이상의 컬럼을 선택해주세요.");
|
|
|
|
|
return;
|
2025-09-15 15:12:02 +09:00
|
|
|
}
|
2025-09-05 16:19:31 +09:00
|
|
|
}
|
2025-09-10 15:30:14 +09:00
|
|
|
|
2025-09-10 17:25:41 +09:00
|
|
|
// 선택된 테이블과 컬럼 정보 사용
|
|
|
|
|
const fromTableName = selectedFromTable || connection.fromNode.tableName;
|
|
|
|
|
const toTableName = selectedToTable || connection.toNode.tableName;
|
|
|
|
|
|
2025-09-12 09:58:49 +09:00
|
|
|
// 조건부 연결 설정 데이터 준비
|
2025-09-16 15:43:18 +09:00
|
|
|
const conditionalSettings = isConditionalConnection(config.connectionType)
|
2025-09-12 09:58:49 +09:00
|
|
|
? {
|
|
|
|
|
control: {
|
2025-09-12 11:33:54 +09:00
|
|
|
triggerType: "insert",
|
2025-09-15 11:17:46 +09:00
|
|
|
conditionTree: conditions.length > 0 ? conditions : null,
|
2025-09-12 09:58:49 +09:00
|
|
|
},
|
|
|
|
|
category: {
|
|
|
|
|
type: config.connectionType,
|
|
|
|
|
},
|
|
|
|
|
plan: {
|
|
|
|
|
sourceTable: fromTableName,
|
2025-09-12 16:15:36 +09:00
|
|
|
targetActions:
|
|
|
|
|
config.connectionType === "data-save"
|
|
|
|
|
? dataSaveSettings.actions.map((action) => ({
|
|
|
|
|
id: action.id,
|
|
|
|
|
actionType: action.actionType,
|
|
|
|
|
enabled: true,
|
2025-09-16 14:44:41 +09:00
|
|
|
conditions:
|
|
|
|
|
action.conditions?.map((condition) => {
|
|
|
|
|
// 모든 조건 타입에 대해 operator 필드 보장
|
|
|
|
|
const baseCondition = { ...condition };
|
|
|
|
|
if (condition.type === "condition") {
|
|
|
|
|
baseCondition.operator = condition.operator || "=";
|
|
|
|
|
}
|
|
|
|
|
return baseCondition;
|
|
|
|
|
}) || [],
|
2025-09-12 16:15:36 +09:00
|
|
|
fieldMappings: action.fieldMappings.map((mapping) => ({
|
|
|
|
|
sourceTable: mapping.sourceTable,
|
|
|
|
|
sourceField: mapping.sourceField,
|
|
|
|
|
targetTable: mapping.targetTable,
|
|
|
|
|
targetField: mapping.targetField,
|
|
|
|
|
defaultValue: mapping.defaultValue,
|
|
|
|
|
transformFunction: mapping.transformFunction,
|
|
|
|
|
})),
|
|
|
|
|
splitConfig: action.splitConfig,
|
|
|
|
|
}))
|
|
|
|
|
: [],
|
2025-09-12 09:58:49 +09:00
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
: {};
|
|
|
|
|
|
2025-09-15 15:12:02 +09:00
|
|
|
// 컬럼 정보는 단순 키값 연결일 때만 사용
|
|
|
|
|
const finalFromColumns = config.connectionType === "simple-key" ? selectedFromColumns : [];
|
|
|
|
|
const finalToColumns = config.connectionType === "simple-key" ? selectedToColumns : [];
|
|
|
|
|
|
2025-09-10 15:30:14 +09:00
|
|
|
// 메모리 기반 시스템: 관계 데이터만 생성하여 부모로 전달
|
|
|
|
|
const relationshipData: TableRelationship = {
|
|
|
|
|
relationship_name: config.relationshipName,
|
2025-09-10 17:25:41 +09:00
|
|
|
from_table_name: fromTableName,
|
|
|
|
|
to_table_name: toTableName,
|
2025-09-15 15:12:02 +09:00
|
|
|
from_column_name: finalFromColumns.join(","), // 여러 컬럼을 콤마로 구분
|
|
|
|
|
to_column_name: finalToColumns.join(","), // 여러 컬럼을 콤마로 구분
|
2025-09-12 09:58:49 +09:00
|
|
|
connection_type: config.connectionType,
|
2025-09-10 15:30:14 +09:00
|
|
|
company_code: companyCode,
|
|
|
|
|
settings: {
|
|
|
|
|
...settings,
|
2025-09-12 09:58:49 +09:00
|
|
|
...conditionalSettings, // 조건부 연결 설정 추가
|
2025-09-10 15:30:14 +09:00
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
toast.success("관계가 생성되었습니다!");
|
|
|
|
|
|
|
|
|
|
// 부모 컴포넌트로 관계 데이터 전달 (DB 저장 없이)
|
|
|
|
|
onConfirm(relationshipData);
|
|
|
|
|
handleCancel(); // 모달 닫기
|
2025-09-05 16:19:31 +09:00
|
|
|
};
|
|
|
|
|
|
2025-09-05 18:00:18 +09:00
|
|
|
const handleCancel = () => {
|
|
|
|
|
setConfig({
|
|
|
|
|
relationshipName: "",
|
|
|
|
|
connectionType: "simple-key",
|
|
|
|
|
fromColumnName: "",
|
|
|
|
|
toColumnName: "",
|
|
|
|
|
});
|
|
|
|
|
onCancel();
|
2025-09-05 16:19:31 +09:00
|
|
|
};
|
|
|
|
|
|
2025-09-08 16:46:53 +09:00
|
|
|
// 연결 종류별 설정 패널 렌더링
|
|
|
|
|
const renderConnectionTypeSettings = () => {
|
|
|
|
|
switch (config.connectionType) {
|
|
|
|
|
case "simple-key":
|
2025-09-16 12:37:57 +09:00
|
|
|
return (
|
2025-09-16 15:43:18 +09:00
|
|
|
<SimpleKeySettingsComponent
|
|
|
|
|
settings={simpleKeySettings}
|
|
|
|
|
onSettingsChange={setSimpleKeySettings}
|
|
|
|
|
availableTables={availableTables}
|
|
|
|
|
selectedFromTable={selectedFromTable}
|
|
|
|
|
selectedToTable={selectedToTable}
|
|
|
|
|
fromTableColumns={fromTableColumns}
|
|
|
|
|
toTableColumns={toTableColumns}
|
|
|
|
|
selectedFromColumns={selectedFromColumns}
|
|
|
|
|
selectedToColumns={selectedToColumns}
|
|
|
|
|
onFromColumnsChange={setSelectedFromColumns}
|
|
|
|
|
onToColumnsChange={setSelectedToColumns}
|
|
|
|
|
/>
|
2025-09-08 16:46:53 +09:00
|
|
|
);
|
|
|
|
|
|
|
|
|
|
case "data-save":
|
|
|
|
|
return (
|
2025-09-16 15:43:18 +09:00
|
|
|
<DataSaveSettingsComponent
|
|
|
|
|
settings={dataSaveSettings}
|
|
|
|
|
onSettingsChange={setDataSaveSettings}
|
|
|
|
|
availableTables={availableTables}
|
|
|
|
|
fromTableColumns={fromTableColumns}
|
|
|
|
|
toTableColumns={toTableColumns}
|
|
|
|
|
tableColumnsCache={tableColumnsCache}
|
|
|
|
|
/>
|
2025-09-08 16:46:53 +09:00
|
|
|
);
|
2025-09-05 16:19:31 +09:00
|
|
|
|
2025-09-08 16:46:53 +09:00
|
|
|
case "external-call":
|
|
|
|
|
return (
|
2025-09-16 15:43:18 +09:00
|
|
|
<ExternalCallSettingsComponent settings={externalCallSettings} onSettingsChange={setExternalCallSettings} />
|
2025-09-08 16:46:53 +09:00
|
|
|
);
|
2025-09-05 18:00:18 +09:00
|
|
|
|
2025-09-08 16:46:53 +09:00
|
|
|
default:
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2025-09-16 15:43:18 +09:00
|
|
|
const isButtonDisabled = () => {
|
|
|
|
|
const hasRelationshipName = !!config.relationshipName;
|
|
|
|
|
const isDataSave = config.connectionType === "data-save";
|
|
|
|
|
const hasActions = dataSaveSettings.actions.length > 0;
|
|
|
|
|
const allActionsHaveMappings = dataSaveSettings.actions.every((action) => action.fieldMappings.length > 0);
|
|
|
|
|
const allMappingsComplete = dataSaveSettings.actions.every((action) =>
|
|
|
|
|
action.fieldMappings.every((mapping) => {
|
|
|
|
|
// 타겟은 항상 필요
|
|
|
|
|
if (!mapping.targetTable || !mapping.targetField) return false;
|
|
|
|
|
|
|
|
|
|
// 소스와 기본값 중 하나는 있어야 함
|
|
|
|
|
const hasSource = mapping.sourceTable && mapping.sourceField;
|
|
|
|
|
const hasDefault = mapping.defaultValue && mapping.defaultValue.trim();
|
|
|
|
|
|
|
|
|
|
// FROM 테이블이 비어있으면 기본값이 필요
|
|
|
|
|
if (!mapping.sourceTable) {
|
|
|
|
|
return !!hasDefault;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// FROM 테이블이 있으면 소스 매핑 완성 또는 기본값 필요
|
|
|
|
|
return hasSource || hasDefault;
|
|
|
|
|
}),
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
return !hasRelationshipName || (isDataSave && (!hasActions || !allActionsHaveMappings || !allMappingsComplete));
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if (!connection) return null;
|
|
|
|
|
|
2025-09-08 16:46:53 +09:00
|
|
|
return (
|
|
|
|
|
<Dialog open={isOpen} onOpenChange={handleCancel}>
|
|
|
|
|
<DialogContent className="max-h-[80vh] max-w-3xl overflow-y-auto">
|
|
|
|
|
<DialogHeader>
|
|
|
|
|
<DialogTitle className="flex items-center gap-2 text-lg">
|
|
|
|
|
<Link className="h-4 w-4" />
|
|
|
|
|
필드 연결 설정
|
|
|
|
|
</DialogTitle>
|
|
|
|
|
</DialogHeader>
|
|
|
|
|
|
|
|
|
|
<div className="space-y-4">
|
|
|
|
|
{/* 기본 연결 설정 */}
|
|
|
|
|
<div className="grid grid-cols-2 gap-4">
|
|
|
|
|
<div>
|
|
|
|
|
<Label htmlFor="relationshipName">연결 이름</Label>
|
|
|
|
|
<Input
|
|
|
|
|
id="relationshipName"
|
|
|
|
|
value={config.relationshipName}
|
|
|
|
|
onChange={(e) => setConfig({ ...config, relationshipName: e.target.value })}
|
|
|
|
|
placeholder="employee_id_department_id_연결"
|
|
|
|
|
className="text-sm"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{/* 연결 종류 선택 */}
|
2025-09-16 15:43:18 +09:00
|
|
|
<ConnectionTypeSelector config={config} onConfigChange={setConfig} />
|
2025-09-08 16:46:53 +09:00
|
|
|
|
2025-09-12 09:58:49 +09:00
|
|
|
{/* 조건부 연결을 위한 조건 설정 */}
|
2025-09-16 15:43:18 +09:00
|
|
|
{isConditionalConnection(config.connectionType) && (
|
|
|
|
|
<ConditionalSettings
|
|
|
|
|
conditions={conditions}
|
|
|
|
|
fromTableColumns={fromTableColumns}
|
|
|
|
|
onAddCondition={addCondition}
|
|
|
|
|
onAddGroupStart={addGroupStart}
|
|
|
|
|
onAddGroupEnd={addGroupEnd}
|
|
|
|
|
onUpdateCondition={updateCondition}
|
|
|
|
|
onRemoveCondition={removeCondition}
|
|
|
|
|
getCurrentGroupLevel={getCurrentGroupLevel}
|
|
|
|
|
/>
|
|
|
|
|
)}
|
2025-09-12 09:58:49 +09:00
|
|
|
|
2025-09-08 16:46:53 +09:00
|
|
|
{/* 연결 종류별 상세 설정 */}
|
|
|
|
|
{renderConnectionTypeSettings()}
|
2025-09-05 16:19:31 +09:00
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<DialogFooter>
|
2025-09-05 18:00:18 +09:00
|
|
|
<Button variant="outline" onClick={handleCancel}>
|
2025-09-05 16:19:31 +09:00
|
|
|
취소
|
|
|
|
|
</Button>
|
2025-09-16 15:43:18 +09:00
|
|
|
<Button onClick={handleConfirm} disabled={isButtonDisabled()}>
|
2025-09-05 18:00:18 +09:00
|
|
|
연결 생성
|
|
|
|
|
</Button>
|
2025-09-05 16:19:31 +09:00
|
|
|
</DialogFooter>
|
|
|
|
|
</DialogContent>
|
|
|
|
|
</Dialog>
|
|
|
|
|
);
|
|
|
|
|
};
|