ERP-node/frontend/components/dataflow/ConnectionSetupModal.tsx

743 lines
28 KiB
TypeScript
Raw Normal View History

2025-09-05 16:19:31 +09:00
"use client";
import React, { useState, useEffect } from "react";
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";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import { Badge } from "@/components/ui/badge";
import { Textarea } from "@/components/ui/textarea";
2025-09-08 16:46:53 +09:00
import { ArrowRight, Link, Key, Save, Globe, Plus } from "lucide-react";
2025-09-10 17:25:41 +09:00
import { DataFlowAPI, TableRelationship, TableInfo, ColumnInfo } from "@/lib/api/dataflow";
2025-09-08 16:46:53 +09:00
import toast from "react-hot-toast";
2025-09-05 16:19:31 +09:00
// 연결 정보 타입
interface ConnectionInfo {
fromNode: {
id: string;
tableName: string;
2025-09-05 18:00:18 +09:00
displayName: string;
2025-09-05 16:19:31 +09:00
};
toNode: {
id: string;
tableName: string;
2025-09-05 18:00:18 +09:00
displayName: string;
2025-09-05 16:19:31 +09:00
};
2025-09-05 18:00:18 +09:00
fromColumn?: string;
toColumn?: string;
selectedColumnsData?: {
[tableName: string]: {
displayName: string;
columns: string[];
2025-09-05 16:19:31 +09:00
};
};
2025-09-11 10:45:16 +09:00
existingRelationship?: {
relationshipName: string;
relationshipType: string;
connectionType: string;
settings?: any;
};
2025-09-05 16:19:31 +09:00
}
// 연결 설정 타입
interface ConnectionConfig {
relationshipName: string;
relationshipType: "one-to-one" | "one-to-many" | "many-to-one" | "many-to-many";
connectionType: "simple-key" | "data-save" | "external-call";
2025-09-05 18:00:18 +09:00
fromColumnName: string;
toColumnName: string;
2025-09-08 16:46:53 +09:00
settings?: Record<string, unknown>;
2025-09-05 16:19:31 +09:00
description?: string;
}
2025-09-08 16:46:53 +09:00
// 단순 키값 연결 설정
interface SimpleKeySettings {
notes: string;
}
// 데이터 저장 설정
interface DataSaveSettings {
sourceField: string;
targetField: string;
saveConditions: string;
}
// 외부 호출 설정
interface ExternalCallSettings {
callType: "rest-api" | "email" | "webhook" | "ftp" | "queue";
apiUrl?: string;
httpMethod?: "GET" | "POST" | "PUT" | "DELETE";
headers?: string;
bodyTemplate?: string;
}
2025-09-05 16:19:31 +09:00
interface ConnectionSetupModalProps {
isOpen: boolean;
connection: ConnectionInfo | null;
2025-09-08 16:46:53 +09:00
companyCode: string;
diagramId?: number;
2025-09-08 16:46:53 +09:00
onConfirm: (relationship: TableRelationship) => void;
2025-09-05 16:19:31 +09:00
onCancel: () => void;
}
export const ConnectionSetupModal: React.FC<ConnectionSetupModalProps> = ({
isOpen,
connection,
2025-09-08 16:46:53 +09:00
companyCode,
diagramId,
2025-09-05 16:19:31 +09:00
onConfirm,
onCancel,
}) => {
2025-09-05 18:00:18 +09:00
const [config, setConfig] = useState<ConnectionConfig>({
relationshipName: "",
relationshipType: "one-to-one",
connectionType: "simple-key",
fromColumnName: "",
toColumnName: "",
description: "",
2025-09-08 16:46:53 +09:00
settings: {},
});
// 연결 종류별 설정 상태
const [simpleKeySettings, setSimpleKeySettings] = useState<SimpleKeySettings>({
notes: "",
});
const [dataSaveSettings, setDataSaveSettings] = useState<DataSaveSettings>({
sourceField: "",
targetField: "",
saveConditions: "",
});
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-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[]>([]);
// 테이블 목록 로드
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-05 18:00:18 +09:00
setConfig({
2025-09-11 10:45:16 +09:00
relationshipName: existingRel?.relationshipName || `${fromDisplayName}${toDisplayName}`,
relationshipType:
(existingRel?.relationshipType as "one-to-one" | "one-to-many" | "many-to-one" | "many-to-many") ||
"one-to-one",
connectionType: (existingRel?.connectionType as "simple-key" | "data-save" | "external-call") || "simple-key",
2025-09-05 18:00:18 +09:00
fromColumnName: "",
toColumnName: "",
2025-09-11 10:45:16 +09:00
description: existingRel?.settings?.description || `${fromDisplayName}${toDisplayName} 간의 데이터 관계`,
settings: existingRel?.settings || {},
2025-09-08 16:46:53 +09:00
});
// 단순 키값 연결 기본값 설정
setSimpleKeySettings({
2025-09-10 17:25:41 +09:00
notes: `${fromDisplayName}${toDisplayName} 간의 키값 연결`,
2025-09-08 16:46:53 +09:00
});
// 데이터 저장 기본값 설정
setDataSaveSettings({
sourceField: "",
targetField: "",
saveConditions: "데이터 저장 조건을 입력하세요",
});
// 외부 호출 기본값 설정
setExternalCallSettings({
callType: "rest-api",
apiUrl: "https://api.example.com/webhook",
httpMethod: "POST",
headers: "{}",
bodyTemplate: "{}",
2025-09-05 18:00:18 +09:00
});
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
}
}, [isOpen, connection]);
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]);
const handleConfirm = () => {
2025-09-08 16:46:53 +09:00
if (!config.relationshipName || !connection) {
toast.error("필수 정보를 모두 입력해주세요.");
return;
}
// 연결 종류별 설정을 준비
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-10 17:25:41 +09:00
// 선택된 컬럼들 검증
if (selectedFromColumns.length === 0 || selectedToColumns.length === 0) {
toast.error("선택된 컬럼이 없습니다. From과 To 테이블에서 각각 최소 1개 이상의 컬럼을 선택해주세요.");
return;
2025-09-05 16:19:31 +09:00
}
2025-09-10 17:25:41 +09:00
// 선택된 테이블과 컬럼 정보 사용
const fromTableName = selectedFromTable || connection.fromNode.tableName;
const toTableName = selectedToTable || connection.toNode.tableName;
// 메모리 기반 시스템: 관계 데이터만 생성하여 부모로 전달
const relationshipData: TableRelationship = {
relationship_name: config.relationshipName,
2025-09-10 17:25:41 +09:00
from_table_name: fromTableName,
to_table_name: toTableName,
from_column_name: selectedFromColumns.join(","), // 여러 컬럼을 콤마로 구분
to_column_name: selectedToColumns.join(","), // 여러 컬럼을 콤마로 구분
relationship_type: config.relationshipType as any,
connection_type: config.connectionType as any,
company_code: companyCode,
settings: {
...settings,
description: config.description,
multiColumnMapping: {
2025-09-10 17:25:41 +09:00
fromColumns: selectedFromColumns,
toColumns: selectedToColumns,
fromTable: fromTableName,
toTable: toTableName,
},
2025-09-10 17:25:41 +09:00
isMultiColumn: selectedFromColumns.length > 1 || selectedToColumns.length > 1,
columnCount: {
2025-09-10 17:25:41 +09:00
from: selectedFromColumns.length,
to: selectedToColumns.length,
},
},
};
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: "",
relationshipType: "one-to-one",
connectionType: "simple-key",
fromColumnName: "",
toColumnName: "",
description: "",
});
onCancel();
2025-09-05 16:19:31 +09:00
};
if (!connection) return null;
2025-09-05 18:00:18 +09:00
// 선택된 컬럼 데이터 가져오기
const selectedColumnsData = connection.selectedColumnsData || {};
const tableNames = Object.keys(selectedColumnsData);
const fromTable = tableNames[0];
const toTable = tableNames[1];
const fromTableData = selectedColumnsData[fromTable];
const toTableData = selectedColumnsData[toTable];
2025-09-08 16:46:53 +09:00
// 연결 종류별 설정 패널 렌더링
const renderConnectionTypeSettings = () => {
switch (config.connectionType) {
case "simple-key":
return (
<div className="rounded-lg border border-l-4 border-l-blue-500 bg-blue-50/30 p-4">
<div className="mb-3 flex items-center gap-2">
<Key className="h-4 w-4 text-blue-500" />
<span className="text-sm font-medium"> </span>
2025-09-05 18:00:18 +09:00
</div>
2025-09-08 16:46:53 +09:00
<div className="space-y-3">
<div>
<Label htmlFor="notes" className="text-sm">
</Label>
<Textarea
id="notes"
value={simpleKeySettings.notes}
onChange={(e) => setSimpleKeySettings({ ...simpleKeySettings, notes: e.target.value })}
placeholder="데이터 연결에 대한 설명을 입력하세요"
rows={2}
className="text-sm"
/>
</div>
</div>
</div>
);
case "data-save":
return (
<div className="rounded-lg border border-l-4 border-l-green-500 bg-green-50/30 p-4">
<div className="mb-3 flex items-center gap-2">
<Save className="h-4 w-4 text-green-500" />
<span className="text-sm font-medium"> </span>
</div>
<div className="space-y-3">
<div className="grid grid-cols-2 gap-3">
<div>
<Label htmlFor="sourceField" className="text-sm">
</Label>
<Input
id="sourceField"
value={dataSaveSettings.sourceField}
onChange={(e) => setDataSaveSettings({ ...dataSaveSettings, sourceField: e.target.value })}
placeholder="소스 필드"
className="text-sm"
/>
</div>
<div>
<Label htmlFor="targetField" className="text-sm">
</Label>
<div className="flex items-center gap-2">
<Input
id="targetField"
value={dataSaveSettings.targetField}
onChange={(e) => setDataSaveSettings({ ...dataSaveSettings, targetField: e.target.value })}
placeholder="대상 필드"
className="text-sm"
/>
<Button size="sm" variant="outline">
<Plus className="h-3 w-3" />
</Button>
</div>
</div>
</div>
<div>
<Label htmlFor="saveConditions" className="text-sm">
</Label>
<Textarea
id="saveConditions"
value={dataSaveSettings.saveConditions}
onChange={(e) => setDataSaveSettings({ ...dataSaveSettings, saveConditions: e.target.value })}
placeholder="데이터 저장 조건을 입력하세요"
rows={2}
className="text-sm"
/>
</div>
</div>
</div>
);
2025-09-05 16:19:31 +09:00
2025-09-08 16:46:53 +09:00
case "external-call":
return (
<div className="rounded-lg border border-l-4 border-l-orange-500 bg-orange-50/30 p-4">
<div className="mb-3 flex items-center gap-2">
<Globe className="h-4 w-4 text-orange-500" />
<span className="text-sm font-medium"> </span>
</div>
<div className="space-y-3">
2025-09-05 18:00:18 +09:00
<div>
2025-09-08 16:46:53 +09:00
<Label htmlFor="callType" className="text-sm">
</Label>
2025-09-05 18:00:18 +09:00
<Select
2025-09-08 16:46:53 +09:00
value={externalCallSettings.callType}
onValueChange={(value: "rest-api" | "email" | "webhook" | "ftp" | "queue") =>
setExternalCallSettings({ ...externalCallSettings, callType: value })
}
2025-09-05 18:00:18 +09:00
>
2025-09-08 16:46:53 +09:00
<SelectTrigger className="text-sm">
2025-09-05 18:00:18 +09:00
<SelectValue />
2025-09-05 16:19:31 +09:00
</SelectTrigger>
<SelectContent>
2025-09-08 16:46:53 +09:00
<SelectItem value="rest-api">REST API </SelectItem>
<SelectItem value="email"> </SelectItem>
<SelectItem value="webhook"></SelectItem>
<SelectItem value="ftp">FTP </SelectItem>
<SelectItem value="queue"> </SelectItem>
2025-09-05 16:19:31 +09:00
</SelectContent>
</Select>
2025-09-05 18:00:18 +09:00
</div>
2025-09-08 16:46:53 +09:00
{externalCallSettings.callType === "rest-api" && (
<>
<div>
<Label htmlFor="apiUrl" className="text-sm">
API URL
</Label>
<Input
id="apiUrl"
value={externalCallSettings.apiUrl}
onChange={(e) => setExternalCallSettings({ ...externalCallSettings, apiUrl: e.target.value })}
placeholder="https://api.example.com/webhook"
className="text-sm"
/>
</div>
<div className="grid grid-cols-2 gap-3">
<div>
<Label htmlFor="httpMethod" className="text-sm">
HTTP Method
</Label>
<Select
value={externalCallSettings.httpMethod}
onValueChange={(value: "GET" | "POST" | "PUT" | "DELETE") =>
setExternalCallSettings({ ...externalCallSettings, httpMethod: value })
}
>
<SelectTrigger className="text-sm">
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="GET">GET</SelectItem>
<SelectItem value="POST">POST</SelectItem>
<SelectItem value="PUT">PUT</SelectItem>
<SelectItem value="DELETE">DELETE</SelectItem>
</SelectContent>
</Select>
</div>
<div>
<Label htmlFor="headers" className="text-sm">
Headers
</Label>
<Textarea
id="headers"
value={externalCallSettings.headers}
onChange={(e) => setExternalCallSettings({ ...externalCallSettings, headers: e.target.value })}
placeholder="{}"
rows={1}
className="text-sm"
/>
</div>
</div>
<div>
<Label htmlFor="bodyTemplate" className="text-sm">
Body Template
</Label>
<Textarea
id="bodyTemplate"
value={externalCallSettings.bodyTemplate}
onChange={(e) =>
setExternalCallSettings({ ...externalCallSettings, bodyTemplate: e.target.value })
}
placeholder="{}"
rows={2}
className="text-sm"
/>
</div>
</>
)}
2025-09-05 18:00:18 +09:00
</div>
2025-09-08 16:46:53 +09:00
</div>
);
2025-09-05 18:00:18 +09:00
2025-09-08 16:46:53 +09:00
default:
return null;
}
};
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">
2025-09-10 17:25:41 +09:00
{/* 테이블 및 컬럼 선택 */}
<div className="rounded-lg border bg-gray-50 p-4">
<div className="mb-4 text-sm font-medium"> </div>
2025-09-10 17:58:23 +09:00
{/* 현재 선택된 테이블 표시 */}
2025-09-10 17:25:41 +09:00
<div className="mb-4 grid grid-cols-2 gap-4">
<div>
<Label className="text-xs font-medium text-gray-600">From </Label>
2025-09-10 17:58:23 +09:00
<div className="mt-1">
<span className="text-sm font-medium text-gray-800">
{availableTables.find((t) => t.tableName === selectedFromTable)?.displayName || selectedFromTable}
</span>
<span className="ml-2 text-xs text-gray-500">({selectedFromTable})</span>
</div>
2025-09-10 17:25:41 +09:00
</div>
<div>
<Label className="text-xs font-medium text-gray-600">To </Label>
2025-09-10 17:58:23 +09:00
<div className="mt-1">
<span className="text-sm font-medium text-gray-800">
{availableTables.find((t) => t.tableName === selectedToTable)?.displayName || selectedToTable}
</span>
<span className="ml-2 text-xs text-gray-500">({selectedToTable})</span>
</div>
2025-09-10 17:25:41 +09:00
</div>
2025-09-08 16:46:53 +09:00
</div>
2025-09-10 17:25:41 +09:00
{/* 컬럼 선택 */}
<div className="grid grid-cols-2 gap-4">
<div>
<Label className="text-xs font-medium text-gray-600">From </Label>
<div className="mt-2 max-h-32 overflow-y-auto rounded border bg-white p-2">
{fromTableColumns.map((column) => (
<label key={column.columnName} className="flex items-center gap-2 py-1 text-sm">
<input
type="checkbox"
checked={selectedFromColumns.includes(column.columnName)}
onChange={(e) => {
if (e.target.checked) {
setSelectedFromColumns((prev) => [...prev, column.columnName]);
} else {
setSelectedFromColumns((prev) => prev.filter((col) => col !== column.columnName));
}
}}
className="rounded"
/>
<span>{column.columnName}</span>
<span className="text-xs text-gray-500">({column.dataType})</span>
</label>
))}
{fromTableColumns.length === 0 && (
<div className="py-2 text-xs text-gray-500">
{selectedFromTable ? "컬럼을 불러오는 중..." : "테이블을 먼저 선택해주세요"}
</div>
)}
</div>
</div>
<div>
<Label className="text-xs font-medium text-gray-600">To </Label>
<div className="mt-2 max-h-32 overflow-y-auto rounded border bg-white p-2">
{toTableColumns.map((column) => (
<label key={column.columnName} className="flex items-center gap-2 py-1 text-sm">
<input
type="checkbox"
checked={selectedToColumns.includes(column.columnName)}
onChange={(e) => {
if (e.target.checked) {
setSelectedToColumns((prev) => [...prev, column.columnName]);
} else {
setSelectedToColumns((prev) => prev.filter((col) => col !== column.columnName));
}
}}
className="rounded"
/>
<span>{column.columnName}</span>
<span className="text-xs text-gray-500">({column.dataType})</span>
</label>
))}
{toTableColumns.length === 0 && (
<div className="py-2 text-xs text-gray-500">
{selectedToTable ? "컬럼을 불러오는 중..." : "테이블을 먼저 선택해주세요"}
</div>
)}
</div>
</div>
2025-09-08 16:46:53 +09:00
</div>
2025-09-10 17:25:41 +09:00
{/* 선택된 컬럼 미리보기 */}
{(selectedFromColumns.length > 0 || selectedToColumns.length > 0) && (
2025-09-10 17:58:23 +09:00
<div className="mt-4 grid grid-cols-2 gap-4">
<div>
<Label className="text-xs font-medium text-gray-600"> From </Label>
<div className="mt-1 flex flex-wrap gap-1">
{selectedFromColumns.length > 0 ? (
selectedFromColumns.map((column) => (
<Badge key={column} variant="outline" className="text-xs">
{column}
</Badge>
))
) : (
<span className="text-xs text-gray-400"> </span>
)}
</div>
2025-09-10 17:25:41 +09:00
</div>
2025-09-10 17:58:23 +09:00
<div>
<Label className="text-xs font-medium text-gray-600"> To </Label>
<div className="mt-1 flex flex-wrap gap-1">
{selectedToColumns.length > 0 ? (
selectedToColumns.map((column) => (
<Badge key={column} variant="secondary" className="text-xs">
{column}
</Badge>
))
) : (
<span className="text-xs text-gray-400"> </span>
)}
</div>
2025-09-10 17:25:41 +09:00
</div>
</div>
)}
2025-09-08 16:46:53 +09:00
</div>
{/* 기본 연결 설정 */}
<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>
{/* 연결 종류 선택 */}
<div>
<Label className="text-sm font-medium"> </Label>
<div className="mt-2 grid grid-cols-3 gap-2">
<div
className={`cursor-pointer rounded-lg border-2 p-3 text-center transition-colors ${
config.connectionType === "simple-key"
? "border-blue-500 bg-blue-50"
: "border-gray-200 hover:border-gray-300"
}`}
onClick={() => setConfig({ ...config, connectionType: "simple-key" })}
>
<Key className="mx-auto h-6 w-6 text-blue-500" />
<div className="mt-1 text-xs font-medium"> </div>
<div className="text-xs text-gray-600"> </div>
2025-09-05 16:19:31 +09:00
</div>
2025-09-08 16:46:53 +09:00
<div
className={`cursor-pointer rounded-lg border-2 p-3 text-center transition-colors ${
config.connectionType === "data-save"
? "border-green-500 bg-green-50"
: "border-gray-200 hover:border-gray-300"
}`}
onClick={() => setConfig({ ...config, connectionType: "data-save" })}
>
<Save className="mx-auto h-6 w-6 text-green-500" />
<div className="mt-1 text-xs font-medium"> </div>
<div className="text-xs text-gray-600"> </div>
2025-09-05 18:00:18 +09:00
</div>
2025-09-08 16:46:53 +09:00
<div
className={`cursor-pointer rounded-lg border-2 p-3 text-center transition-colors ${
config.connectionType === "external-call"
? "border-orange-500 bg-orange-50"
: "border-gray-200 hover:border-gray-300"
}`}
onClick={() => setConfig({ ...config, connectionType: "external-call" })}
>
<Globe className="mx-auto h-6 w-6 text-orange-500" />
<div className="mt-1 text-xs font-medium"> </div>
<div className="text-xs text-gray-600">API/ </div>
2025-09-05 18:00:18 +09:00
</div>
2025-09-05 16:19:31 +09:00
</div>
</div>
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-08 16:46:53 +09:00
<Button onClick={handleConfirm} disabled={!config.relationshipName}>
2025-09-05 18:00:18 +09:00
</Button>
2025-09-05 16:19:31 +09:00
</DialogFooter>
</DialogContent>
</Dialog>
);
};