280 lines
9.6 KiB
TypeScript
280 lines
9.6 KiB
TypeScript
"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 { 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";
|
|
|
|
// 연결 정보 타입
|
|
interface ConnectionInfo {
|
|
fromNode: {
|
|
id: string;
|
|
tableName: string;
|
|
displayName: string;
|
|
};
|
|
toNode: {
|
|
id: string;
|
|
tableName: string;
|
|
displayName: string;
|
|
};
|
|
fromColumn?: string;
|
|
toColumn?: string;
|
|
selectedColumnsData?: {
|
|
[tableName: string]: {
|
|
displayName: string;
|
|
columns: string[];
|
|
};
|
|
};
|
|
}
|
|
|
|
// 연결 설정 타입
|
|
interface ConnectionConfig {
|
|
relationshipName: string;
|
|
relationshipType: "one-to-one" | "one-to-many" | "many-to-one" | "many-to-many";
|
|
connectionType: "simple-key" | "data-save" | "external-call";
|
|
fromColumnName: string;
|
|
toColumnName: string;
|
|
settings?: Record<string, any>;
|
|
description?: string;
|
|
}
|
|
|
|
interface ConnectionSetupModalProps {
|
|
isOpen: boolean;
|
|
connection: ConnectionInfo | null;
|
|
onConfirm: (config: ConnectionConfig) => void;
|
|
onCancel: () => void;
|
|
}
|
|
|
|
export const ConnectionSetupModal: React.FC<ConnectionSetupModalProps> = ({
|
|
isOpen,
|
|
connection,
|
|
onConfirm,
|
|
onCancel,
|
|
}) => {
|
|
const [config, setConfig] = useState<ConnectionConfig>({
|
|
relationshipName: "",
|
|
relationshipType: "one-to-one",
|
|
connectionType: "simple-key",
|
|
fromColumnName: "",
|
|
toColumnName: "",
|
|
description: "",
|
|
});
|
|
|
|
// 모달이 열릴 때 기본값 설정
|
|
useEffect(() => {
|
|
if (isOpen && connection) {
|
|
const fromTableName = connection.fromNode.displayName;
|
|
const toTableName = connection.toNode.displayName;
|
|
|
|
setConfig({
|
|
relationshipName: `${fromTableName} → ${toTableName}`,
|
|
relationshipType: "one-to-one",
|
|
connectionType: "simple-key",
|
|
fromColumnName: "",
|
|
toColumnName: "",
|
|
description: `${fromTableName}과 ${toTableName} 간의 데이터 관계`,
|
|
});
|
|
}
|
|
}, [isOpen, connection]);
|
|
|
|
const handleConfirm = () => {
|
|
if (config.relationshipName && config.fromColumnName && config.toColumnName) {
|
|
onConfirm(config);
|
|
handleCancel(); // 모달 닫기
|
|
}
|
|
};
|
|
|
|
const handleCancel = () => {
|
|
setConfig({
|
|
relationshipName: "",
|
|
relationshipType: "one-to-one",
|
|
connectionType: "simple-key",
|
|
fromColumnName: "",
|
|
toColumnName: "",
|
|
description: "",
|
|
});
|
|
onCancel();
|
|
};
|
|
|
|
if (!connection) return null;
|
|
|
|
// 선택된 컬럼 데이터 가져오기
|
|
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];
|
|
|
|
return (
|
|
<Dialog open={isOpen} onOpenChange={handleCancel}>
|
|
<DialogContent className="max-h-[90vh] max-w-4xl overflow-y-auto">
|
|
<DialogHeader>
|
|
<DialogTitle className="flex items-center gap-2 text-xl">
|
|
<Link className="h-5 w-5" />
|
|
테이블 간 컬럼 연결 설정
|
|
</DialogTitle>
|
|
</DialogHeader>
|
|
|
|
<div className="space-y-6">
|
|
{/* 연결 정보 표시 */}
|
|
<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>
|
|
|
|
{/* 대상 테이블 */}
|
|
<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>
|
|
|
|
<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>
|
|
|
|
<div>
|
|
<Label htmlFor="toColumnName">대상 컬럼</Label>
|
|
<Input
|
|
id="toColumnName"
|
|
value={config.toColumnName}
|
|
onChange={(e) => setConfig({ ...config, toColumnName: e.target.value })}
|
|
placeholder="대상 컬럼명을 입력하세요"
|
|
/>
|
|
</div>
|
|
|
|
<div>
|
|
<Label htmlFor="description">설명</Label>
|
|
<Textarea
|
|
id="description"
|
|
value={config.description}
|
|
onChange={(e) => setConfig({ ...config, description: e.target.value })}
|
|
placeholder="연결에 대한 설명을 입력하세요"
|
|
rows={3}
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<DialogFooter>
|
|
<Button variant="outline" onClick={handleCancel}>
|
|
취소
|
|
</Button>
|
|
<Button
|
|
onClick={handleConfirm}
|
|
disabled={!config.relationshipName || !config.fromColumnName || !config.toColumnName}
|
|
>
|
|
연결 생성
|
|
</Button>
|
|
</DialogFooter>
|
|
</DialogContent>
|
|
</Dialog>
|
|
);
|
|
};
|