관계 수정 , 삭제 구현
This commit is contained in:
@@ -9,7 +9,7 @@ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { Textarea } from "@/components/ui/textarea";
|
||||
import { ArrowRight, Link, Key, Save, Globe, Plus } from "lucide-react";
|
||||
import { DataFlowAPI, TableRelationship } from "@/lib/api/dataflow";
|
||||
import { DataFlowAPI, TableRelationship, TableInfo, ColumnInfo } from "@/lib/api/dataflow";
|
||||
import toast from "react-hot-toast";
|
||||
|
||||
// 연결 정보 타입
|
||||
@@ -112,25 +112,57 @@ export const ConnectionSetupModal: React.FC<ConnectionSetupModalProps> = ({
|
||||
bodyTemplate: "{}",
|
||||
});
|
||||
|
||||
// 테이블 및 컬럼 선택을 위한 새로운 상태들
|
||||
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]);
|
||||
|
||||
// 모달이 열릴 때 기본값 설정
|
||||
useEffect(() => {
|
||||
if (isOpen && connection) {
|
||||
const fromTableName = connection.fromNode.displayName;
|
||||
const toTableName = connection.toNode.displayName;
|
||||
const fromTableName = connection.fromNode.tableName;
|
||||
const toTableName = connection.toNode.tableName;
|
||||
const fromDisplayName = connection.fromNode.displayName;
|
||||
const toDisplayName = connection.toNode.displayName;
|
||||
|
||||
// 테이블 선택 설정
|
||||
setSelectedFromTable(fromTableName);
|
||||
setSelectedToTable(toTableName);
|
||||
|
||||
setConfig({
|
||||
relationshipName: `${fromTableName} → ${toTableName}`,
|
||||
relationshipName: `${fromDisplayName} → ${toDisplayName}`,
|
||||
relationshipType: "one-to-one",
|
||||
connectionType: "simple-key",
|
||||
fromColumnName: "",
|
||||
toColumnName: "",
|
||||
description: `${fromTableName}과 ${toTableName} 간의 데이터 관계`,
|
||||
description: `${fromDisplayName}과 ${toDisplayName} 간의 데이터 관계`,
|
||||
settings: {},
|
||||
});
|
||||
|
||||
// 단순 키값 연결 기본값 설정
|
||||
setSimpleKeySettings({
|
||||
notes: `${fromTableName}과 ${toTableName} 간의 키값 연결`,
|
||||
notes: `${fromDisplayName}과 ${toDisplayName} 간의 키값 연결`,
|
||||
});
|
||||
|
||||
// 데이터 저장 기본값 설정
|
||||
@@ -148,9 +180,67 @@ export const ConnectionSetupModal: React.FC<ConnectionSetupModalProps> = ({
|
||||
headers: "{}",
|
||||
bodyTemplate: "{}",
|
||||
});
|
||||
|
||||
// 선택된 컬럼 정보가 있다면 설정
|
||||
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(", "),
|
||||
}));
|
||||
}
|
||||
}
|
||||
}, [isOpen, connection]);
|
||||
|
||||
// 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 = () => {
|
||||
if (!config.relationshipName || !connection) {
|
||||
toast.error("필수 정보를 모두 입력해주세요.");
|
||||
@@ -172,27 +262,23 @@ export const ConnectionSetupModal: React.FC<ConnectionSetupModalProps> = ({
|
||||
break;
|
||||
}
|
||||
|
||||
// 선택된 컬럼들 추출
|
||||
const selectedColumnsData = connection.selectedColumnsData || {};
|
||||
const tableNames = Object.keys(selectedColumnsData);
|
||||
const fromTable = tableNames[0];
|
||||
const toTable = tableNames[1];
|
||||
|
||||
const fromColumns = selectedColumnsData[fromTable]?.columns || [];
|
||||
const toColumns = selectedColumnsData[toTable]?.columns || [];
|
||||
|
||||
if (fromColumns.length === 0 || toColumns.length === 0) {
|
||||
toast.error("선택된 컬럼이 없습니다.");
|
||||
// 선택된 컬럼들 검증
|
||||
if (selectedFromColumns.length === 0 || selectedToColumns.length === 0) {
|
||||
toast.error("선택된 컬럼이 없습니다. From과 To 테이블에서 각각 최소 1개 이상의 컬럼을 선택해주세요.");
|
||||
return;
|
||||
}
|
||||
|
||||
// 선택된 테이블과 컬럼 정보 사용
|
||||
const fromTableName = selectedFromTable || connection.fromNode.tableName;
|
||||
const toTableName = selectedToTable || connection.toNode.tableName;
|
||||
|
||||
// 메모리 기반 시스템: 관계 데이터만 생성하여 부모로 전달
|
||||
const relationshipData: TableRelationship = {
|
||||
relationship_name: config.relationshipName,
|
||||
from_table_name: connection.fromNode.tableName,
|
||||
to_table_name: connection.toNode.tableName,
|
||||
from_column_name: fromColumns.join(","), // 여러 컬럼을 콤마로 구분
|
||||
to_column_name: toColumns.join(","), // 여러 컬럼을 콤마로 구분
|
||||
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,
|
||||
@@ -200,15 +286,15 @@ export const ConnectionSetupModal: React.FC<ConnectionSetupModalProps> = ({
|
||||
...settings,
|
||||
description: config.description,
|
||||
multiColumnMapping: {
|
||||
fromColumns: fromColumns,
|
||||
toColumns: toColumns,
|
||||
fromTable: selectedColumnsData[fromTable]?.displayName || fromTable,
|
||||
toTable: selectedColumnsData[toTable]?.displayName || toTable,
|
||||
fromColumns: selectedFromColumns,
|
||||
toColumns: selectedToColumns,
|
||||
fromTable: fromTableName,
|
||||
toTable: toTableName,
|
||||
},
|
||||
isMultiColumn: fromColumns.length > 1 || toColumns.length > 1,
|
||||
isMultiColumn: selectedFromColumns.length > 1 || selectedToColumns.length > 1,
|
||||
columnCount: {
|
||||
from: fromColumns.length,
|
||||
to: toColumns.length,
|
||||
from: selectedFromColumns.length,
|
||||
to: selectedToColumns.length,
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -445,28 +531,128 @@ export const ConnectionSetupModal: React.FC<ConnectionSetupModalProps> = ({
|
||||
</DialogHeader>
|
||||
|
||||
<div className="space-y-4">
|
||||
{/* 연결 정보 표시 */}
|
||||
<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 className="rounded-lg border bg-gray-50 p-4">
|
||||
<div className="mb-4 text-sm font-medium">테이블 및 컬럼 선택</div>
|
||||
|
||||
{/* 테이블 선택 */}
|
||||
<div className="mb-4 grid grid-cols-2 gap-4">
|
||||
<div>
|
||||
<Label className="text-xs font-medium text-gray-600">From 테이블</Label>
|
||||
<Select value={selectedFromTable} onValueChange={setSelectedFromTable}>
|
||||
<SelectTrigger className="mt-1">
|
||||
<SelectValue placeholder="테이블 선택" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{availableTables.map((table) => (
|
||||
<SelectItem key={table.tableName} value={table.tableName}>
|
||||
{table.displayName} ({table.tableName})
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Label className="text-xs font-medium text-gray-600">To 테이블</Label>
|
||||
<Select value={selectedToTable} onValueChange={setSelectedToTable}>
|
||||
<SelectTrigger className="mt-1">
|
||||
<SelectValue placeholder="테이블 선택" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{availableTables.map((table) => (
|
||||
<SelectItem key={table.tableName} value={table.tableName}>
|
||||
{table.displayName} ({table.tableName})
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
</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 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>
|
||||
</div>
|
||||
|
||||
{/* 선택된 컬럼 미리보기 */}
|
||||
{(selectedFromColumns.length > 0 || selectedToColumns.length > 0) && (
|
||||
<div className="mt-4 flex items-center gap-2 text-sm">
|
||||
<span className="font-medium">{selectedFromTable}</span>
|
||||
<div className="flex flex-wrap gap-1">
|
||||
{selectedFromColumns.map((column) => (
|
||||
<Badge key={column} variant="outline" className="text-xs">
|
||||
{column}
|
||||
</Badge>
|
||||
))}
|
||||
</div>
|
||||
<ArrowRight className="h-4 w-4 text-gray-400" />
|
||||
<span className="font-medium">{selectedToTable}</span>
|
||||
<div className="flex flex-wrap gap-1">
|
||||
{selectedToColumns.map((column) => (
|
||||
<Badge key={column} variant="secondary" className="text-xs">
|
||||
{column}
|
||||
</Badge>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* 기본 연결 설정 */}
|
||||
|
||||
Reference in New Issue
Block a user