테이블 관계 저장 구현
This commit is contained in:
@@ -6,10 +6,11 @@ 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 { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { Textarea } from "@/components/ui/textarea";
|
||||
import { ArrowRight, Database, Link } from "lucide-react";
|
||||
import { ArrowRight, Link, Key, Save, Globe, Plus } from "lucide-react";
|
||||
import { DataFlowAPI, TableRelationship } from "@/lib/api/dataflow";
|
||||
import toast from "react-hot-toast";
|
||||
|
||||
// 연결 정보 타입
|
||||
interface ConnectionInfo {
|
||||
@@ -40,20 +41,44 @@ interface ConnectionConfig {
|
||||
connectionType: "simple-key" | "data-save" | "external-call";
|
||||
fromColumnName: string;
|
||||
toColumnName: string;
|
||||
settings?: Record<string, any>;
|
||||
settings?: Record<string, unknown>;
|
||||
description?: string;
|
||||
}
|
||||
|
||||
// 단순 키값 연결 설정
|
||||
interface SimpleKeySettings {
|
||||
syncDirection: "unidirectional" | "bidirectional";
|
||||
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;
|
||||
}
|
||||
|
||||
interface ConnectionSetupModalProps {
|
||||
isOpen: boolean;
|
||||
connection: ConnectionInfo | null;
|
||||
onConfirm: (config: ConnectionConfig) => void;
|
||||
companyCode: string;
|
||||
onConfirm: (relationship: TableRelationship) => void;
|
||||
onCancel: () => void;
|
||||
}
|
||||
|
||||
export const ConnectionSetupModal: React.FC<ConnectionSetupModalProps> = ({
|
||||
isOpen,
|
||||
connection,
|
||||
companyCode,
|
||||
onConfirm,
|
||||
onCancel,
|
||||
}) => {
|
||||
@@ -64,6 +89,27 @@ export const ConnectionSetupModal: React.FC<ConnectionSetupModalProps> = ({
|
||||
fromColumnName: "",
|
||||
toColumnName: "",
|
||||
description: "",
|
||||
settings: {},
|
||||
});
|
||||
|
||||
// 연결 종류별 설정 상태
|
||||
const [simpleKeySettings, setSimpleKeySettings] = useState<SimpleKeySettings>({
|
||||
syncDirection: "bidirectional",
|
||||
notes: "",
|
||||
});
|
||||
|
||||
const [dataSaveSettings, setDataSaveSettings] = useState<DataSaveSettings>({
|
||||
sourceField: "",
|
||||
targetField: "",
|
||||
saveConditions: "",
|
||||
});
|
||||
|
||||
const [externalCallSettings, setExternalCallSettings] = useState<ExternalCallSettings>({
|
||||
callType: "rest-api",
|
||||
apiUrl: "",
|
||||
httpMethod: "POST",
|
||||
headers: "{}",
|
||||
bodyTemplate: "{}",
|
||||
});
|
||||
|
||||
// 모달이 열릴 때 기본값 설정
|
||||
@@ -79,14 +125,82 @@ export const ConnectionSetupModal: React.FC<ConnectionSetupModalProps> = ({
|
||||
fromColumnName: "",
|
||||
toColumnName: "",
|
||||
description: `${fromTableName}과 ${toTableName} 간의 데이터 관계`,
|
||||
settings: {},
|
||||
});
|
||||
|
||||
// 단순 키값 연결 기본값 설정
|
||||
setSimpleKeySettings({
|
||||
syncDirection: "bidirectional",
|
||||
notes: `${fromTableName}과 ${toTableName} 간의 키값 연결`,
|
||||
});
|
||||
|
||||
// 데이터 저장 기본값 설정
|
||||
setDataSaveSettings({
|
||||
sourceField: "",
|
||||
targetField: "",
|
||||
saveConditions: "데이터 저장 조건을 입력하세요",
|
||||
});
|
||||
|
||||
// 외부 호출 기본값 설정
|
||||
setExternalCallSettings({
|
||||
callType: "rest-api",
|
||||
apiUrl: "https://api.example.com/webhook",
|
||||
httpMethod: "POST",
|
||||
headers: "{}",
|
||||
bodyTemplate: "{}",
|
||||
});
|
||||
}
|
||||
}, [isOpen, connection]);
|
||||
|
||||
const handleConfirm = () => {
|
||||
if (config.relationshipName && config.fromColumnName && config.toColumnName) {
|
||||
onConfirm(config);
|
||||
const handleConfirm = async () => {
|
||||
if (!config.relationshipName || !connection) {
|
||||
toast.error("필수 정보를 모두 입력해주세요.");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// 연결 종류별 설정을 준비
|
||||
let settings = {};
|
||||
|
||||
switch (config.connectionType) {
|
||||
case "simple-key":
|
||||
settings = simpleKeySettings;
|
||||
break;
|
||||
case "data-save":
|
||||
settings = dataSaveSettings;
|
||||
break;
|
||||
case "external-call":
|
||||
settings = externalCallSettings;
|
||||
break;
|
||||
}
|
||||
|
||||
// API 호출을 위한 관계 데이터 준비
|
||||
const relationshipData: Omit<TableRelationship, "relationshipId"> = {
|
||||
relationshipName: config.relationshipName,
|
||||
fromTableName: connection.fromNode.tableName,
|
||||
fromColumnName: connection.fromColumn || "",
|
||||
toTableName: connection.toNode.tableName,
|
||||
toColumnName: connection.toColumn || "",
|
||||
relationshipType: config.relationshipType,
|
||||
connectionType: config.connectionType,
|
||||
companyCode: companyCode,
|
||||
settings: settings,
|
||||
isActive: "Y",
|
||||
};
|
||||
|
||||
toast.loading("관계를 생성하고 있습니다...", { id: "create-relationship" });
|
||||
|
||||
// API 호출
|
||||
const createdRelationship = await DataFlowAPI.createRelationship(relationshipData);
|
||||
|
||||
toast.success("관계가 성공적으로 생성되었습니다!", { id: "create-relationship" });
|
||||
|
||||
// 성공 콜백 호출
|
||||
onConfirm(createdRelationship);
|
||||
handleCancel(); // 모달 닫기
|
||||
} catch (error) {
|
||||
console.error("관계 생성 오류:", error);
|
||||
toast.error("관계 생성에 실패했습니다. 다시 시도해주세요.", { id: "create-relationship" });
|
||||
}
|
||||
};
|
||||
|
||||
@@ -113,163 +227,344 @@ export const ConnectionSetupModal: React.FC<ConnectionSetupModalProps> = ({
|
||||
const fromTableData = selectedColumnsData[fromTable];
|
||||
const toTableData = selectedColumnsData[toTable];
|
||||
|
||||
// 연결 종류별 설정 패널 렌더링
|
||||
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>
|
||||
</div>
|
||||
<div className="space-y-3">
|
||||
<div>
|
||||
<Label htmlFor="syncDirection" className="text-sm">
|
||||
동기화 방향
|
||||
</Label>
|
||||
<Select
|
||||
value={simpleKeySettings.syncDirection}
|
||||
onValueChange={(value: "unidirectional" | "bidirectional") =>
|
||||
setSimpleKeySettings({ ...simpleKeySettings, syncDirection: value })
|
||||
}
|
||||
>
|
||||
<SelectTrigger className="text-sm">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="unidirectional">단방향 (소스 → 타겟)</SelectItem>
|
||||
<SelectItem value="bidirectional">양방향 (소스 ↔ 타겟)</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
<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 className="rounded-lg bg-blue-50 p-3 text-xs text-blue-700">
|
||||
<div className="mb-1 font-medium">🔄 통합 중계 테이블 사용</div>
|
||||
<div>
|
||||
모든 데이터 연결은 <code>data_relationship_bridge</code> 테이블에서 통합 관리됩니다.
|
||||
</div>
|
||||
</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>
|
||||
);
|
||||
|
||||
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">
|
||||
<div>
|
||||
<Label htmlFor="callType" className="text-sm">
|
||||
호출 유형
|
||||
</Label>
|
||||
<Select
|
||||
value={externalCallSettings.callType}
|
||||
onValueChange={(value: "rest-api" | "email" | "webhook" | "ftp" | "queue") =>
|
||||
setExternalCallSettings({ ...externalCallSettings, callType: value })
|
||||
}
|
||||
>
|
||||
<SelectTrigger className="text-sm">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="rest-api">REST API 호출</SelectItem>
|
||||
<SelectItem value="email">이메일 전송</SelectItem>
|
||||
<SelectItem value="webhook">웹훅</SelectItem>
|
||||
<SelectItem value="ftp">FTP 업로드</SelectItem>
|
||||
<SelectItem value="queue">메시지 큐</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
{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>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Dialog open={isOpen} onOpenChange={handleCancel}>
|
||||
<DialogContent className="max-h-[90vh] max-w-4xl overflow-y-auto">
|
||||
<DialogContent className="max-h-[80vh] max-w-3xl overflow-y-auto">
|
||||
<DialogHeader>
|
||||
<DialogTitle className="flex items-center gap-2 text-xl">
|
||||
<Link className="h-5 w-5" />
|
||||
테이블 간 컬럼 연결 설정
|
||||
<DialogTitle className="flex items-center gap-2 text-lg">
|
||||
<Link className="h-4 w-4" />
|
||||
필드 연결 설정
|
||||
</DialogTitle>
|
||||
</DialogHeader>
|
||||
|
||||
<div className="space-y-6">
|
||||
<div className="space-y-4">
|
||||
{/* 연결 정보 표시 */}
|
||||
<div className="grid grid-cols-1 gap-4 md:grid-cols-2">
|
||||
{/* 시작 테이블 */}
|
||||
<Card>
|
||||
<CardHeader className="pb-3">
|
||||
<CardTitle className="flex items-center gap-2 text-sm">
|
||||
<Database className="h-4 w-4" />
|
||||
시작 테이블
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="space-y-2">
|
||||
<div className="font-medium">{fromTableData?.displayName || fromTable}</div>
|
||||
<div className="text-xs text-gray-500">{fromTable}</div>
|
||||
<div className="flex flex-wrap gap-1">
|
||||
{fromTableData?.columns.map((column, index) => (
|
||||
<Badge key={`${fromTable}-${column}-${index}`} variant="outline" className="text-xs">
|
||||
{column}
|
||||
</Badge>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* 화살표 */}
|
||||
<div className="flex items-center justify-center md:hidden">
|
||||
<ArrowRight className="h-6 w-6 text-gray-400" />
|
||||
<div className="rounded-lg border bg-gray-50 p-3">
|
||||
<div className="mb-2 text-sm font-medium">연결 정보</div>
|
||||
<div className="flex items-center gap-2 text-sm">
|
||||
<span className="font-medium">{fromTableData?.displayName || fromTable}</span>
|
||||
<span className="text-xs text-gray-500">({fromTable})</span>
|
||||
<ArrowRight className="h-4 w-4 text-gray-400" />
|
||||
<span className="font-medium">{toTableData?.displayName || toTable}</span>
|
||||
<span className="text-xs text-gray-500">({toTable})</span>
|
||||
</div>
|
||||
<div className="mt-2 flex flex-wrap gap-1">
|
||||
{fromTableData?.columns.map((column, index) => (
|
||||
<Badge key={`${fromTable}-${column}-${index}`} variant="outline" className="text-xs">
|
||||
{column}
|
||||
</Badge>
|
||||
))}
|
||||
{toTableData?.columns.map((column, index) => (
|
||||
<Badge key={`${toTable}-${column}-${index}`} variant="secondary" className="text-xs">
|
||||
{column}
|
||||
</Badge>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* 대상 테이블 */}
|
||||
<Card>
|
||||
<CardHeader className="pb-3">
|
||||
<CardTitle className="flex items-center gap-2 text-sm">
|
||||
<Database className="h-4 w-4" />
|
||||
대상 테이블
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="space-y-2">
|
||||
<div className="font-medium">{toTableData?.displayName || toTable}</div>
|
||||
<div className="text-xs text-gray-500">{toTable}</div>
|
||||
<div className="flex flex-wrap gap-1">
|
||||
{toTableData?.columns.map((column, index) => (
|
||||
<Badge key={`${toTable}-${column}-${index}`} variant="outline" className="text-xs">
|
||||
{column}
|
||||
</Badge>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
{/* 연결 설정 폼 */}
|
||||
<div className="grid grid-cols-1 gap-4 md:grid-cols-2">
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<Label htmlFor="relationshipName">관계명</Label>
|
||||
<Input
|
||||
id="relationshipName"
|
||||
value={config.relationshipName}
|
||||
onChange={(e) => setConfig({ ...config, relationshipName: e.target.value })}
|
||||
placeholder="관계명을 입력하세요"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Label htmlFor="relationshipType">관계 유형</Label>
|
||||
<Select
|
||||
value={config.relationshipType}
|
||||
onValueChange={(value: any) => setConfig({ ...config, relationshipType: value })}
|
||||
>
|
||||
<SelectTrigger>
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="one-to-one">1:1 (One-to-One)</SelectItem>
|
||||
<SelectItem value="one-to-many">1:N (One-to-Many)</SelectItem>
|
||||
<SelectItem value="many-to-one">N:1 (Many-to-One)</SelectItem>
|
||||
<SelectItem value="many-to-many">N:N (Many-to-Many)</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Label htmlFor="connectionType">연결 방식</Label>
|
||||
<Select
|
||||
value={config.connectionType}
|
||||
onValueChange={(value: any) => setConfig({ ...config, connectionType: value })}
|
||||
>
|
||||
<SelectTrigger>
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="simple-key">단순 키 연결</SelectItem>
|
||||
<SelectItem value="data-save">데이터 저장</SelectItem>
|
||||
<SelectItem value="external-call">외부 호출</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</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>
|
||||
<Label htmlFor="relationshipType">연결 방식</Label>
|
||||
<Select
|
||||
value={config.relationshipType}
|
||||
onValueChange={(value: "one-to-one" | "one-to-many" | "many-to-one" | "many-to-many") =>
|
||||
setConfig({ ...config, relationshipType: value })
|
||||
}
|
||||
>
|
||||
<SelectTrigger className="text-sm">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="one-to-one">1:1 (One to One)</SelectItem>
|
||||
<SelectItem value="one-to-many">1:N (One to Many)</SelectItem>
|
||||
<SelectItem value="many-to-one">N:1 (Many to One)</SelectItem>
|
||||
<SelectItem value="many-to-many">N:N (Many to Many)</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<Label htmlFor="fromColumnName">시작 컬럼</Label>
|
||||
<Input
|
||||
id="fromColumnName"
|
||||
value={config.fromColumnName}
|
||||
onChange={(e) => setConfig({ ...config, fromColumnName: e.target.value })}
|
||||
placeholder="시작 컬럼명을 입력하세요"
|
||||
/>
|
||||
{/* 연결 종류 선택 */}
|
||||
<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>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Label htmlFor="toColumnName">대상 컬럼</Label>
|
||||
<Input
|
||||
id="toColumnName"
|
||||
value={config.toColumnName}
|
||||
onChange={(e) => setConfig({ ...config, toColumnName: e.target.value })}
|
||||
placeholder="대상 컬럼명을 입력하세요"
|
||||
/>
|
||||
<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>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Label htmlFor="description">설명</Label>
|
||||
<Textarea
|
||||
id="description"
|
||||
value={config.description}
|
||||
onChange={(e) => setConfig({ ...config, description: e.target.value })}
|
||||
placeholder="연결에 대한 설명을 입력하세요"
|
||||
rows={3}
|
||||
/>
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 연결 종류별 상세 설정 */}
|
||||
{renderConnectionTypeSettings()}
|
||||
</div>
|
||||
|
||||
<DialogFooter>
|
||||
<Button variant="outline" onClick={handleCancel}>
|
||||
취소
|
||||
</Button>
|
||||
<Button
|
||||
onClick={handleConfirm}
|
||||
disabled={!config.relationshipName || !config.fromColumnName || !config.toColumnName}
|
||||
>
|
||||
<Button onClick={handleConfirm} disabled={!config.relationshipName}>
|
||||
연결 생성
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
|
||||
Reference in New Issue
Block a user