228 lines
9.3 KiB
TypeScript
228 lines
9.3 KiB
TypeScript
"use client";
|
|
|
|
import React from "react";
|
|
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 { Plus, Save, Trash2 } from "lucide-react";
|
|
import { TableInfo, ColumnInfo } from "@/lib/api/dataflow";
|
|
import { DataSaveSettings as DataSaveSettingsType } from "@/types/connectionTypes";
|
|
import { ActionConditionsSection } from "./ActionConditionsSection";
|
|
import { ActionFieldMappings } from "./ActionFieldMappings";
|
|
import { ActionSplitConfig } from "./ActionSplitConfig";
|
|
|
|
interface DataSaveSettingsProps {
|
|
settings: DataSaveSettingsType;
|
|
onSettingsChange: (settings: DataSaveSettingsType) => void;
|
|
availableTables: TableInfo[];
|
|
fromTableColumns: ColumnInfo[];
|
|
toTableColumns: ColumnInfo[];
|
|
fromTableName?: string;
|
|
toTableName?: string;
|
|
tableColumnsCache: { [tableName: string]: ColumnInfo[] };
|
|
}
|
|
|
|
export const DataSaveSettings: React.FC<DataSaveSettingsProps> = ({
|
|
settings,
|
|
onSettingsChange,
|
|
availableTables,
|
|
fromTableColumns,
|
|
toTableColumns,
|
|
fromTableName,
|
|
toTableName,
|
|
tableColumnsCache,
|
|
}) => {
|
|
const addAction = () => {
|
|
const newAction = {
|
|
id: `action_${settings.actions.length + 1}`,
|
|
name: `액션 ${settings.actions.length + 1}`,
|
|
actionType: "insert" as const,
|
|
// 첫 번째 액션이 아니면 기본적으로 AND 연산자 추가
|
|
...(settings.actions.length > 0 && { logicalOperator: "AND" as const }),
|
|
fieldMappings: [],
|
|
conditions: [],
|
|
splitConfig: {
|
|
sourceField: "",
|
|
delimiter: "",
|
|
targetField: "",
|
|
},
|
|
};
|
|
onSettingsChange({
|
|
...settings,
|
|
actions: [...settings.actions, newAction],
|
|
});
|
|
};
|
|
|
|
const updateAction = (actionIndex: number, field: string, value: any) => {
|
|
const newActions = [...settings.actions];
|
|
(newActions[actionIndex] as any)[field] = value;
|
|
onSettingsChange({ ...settings, actions: newActions });
|
|
};
|
|
|
|
const removeAction = (actionIndex: number) => {
|
|
const newActions = settings.actions.filter((_, i) => i !== actionIndex);
|
|
|
|
// 첫 번째 액션을 삭제했다면, 새로운 첫 번째 액션의 logicalOperator 제거
|
|
if (actionIndex === 0 && newActions.length > 0) {
|
|
delete newActions[0].logicalOperator;
|
|
}
|
|
|
|
onSettingsChange({ ...settings, actions: newActions });
|
|
};
|
|
|
|
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-4">
|
|
{/* 액션 목록 */}
|
|
<div>
|
|
<div className="mb-2 flex items-center justify-between">
|
|
<Label className="text-sm font-medium">저장 액션</Label>
|
|
<Button size="sm" variant="outline" onClick={addAction} className="h-7 text-xs">
|
|
<Plus className="mr-1 h-3 w-3" />
|
|
액션 추가
|
|
</Button>
|
|
</div>
|
|
|
|
{settings.actions.length === 0 ? (
|
|
<div className="rounded-lg border border-dashed p-3 text-center text-xs text-gray-500">
|
|
저장 액션을 추가하여 데이터를 어떻게 저장할지 설정하세요.
|
|
</div>
|
|
) : (
|
|
<div className="space-y-3">
|
|
{settings.actions.map((action, actionIndex) => (
|
|
<div key={action.id}>
|
|
{/* 첫 번째 액션이 아닌 경우 논리 연산자 표시 */}
|
|
{actionIndex > 0 && (
|
|
<div className="mb-2 flex items-center justify-center">
|
|
<div className="flex items-center gap-2 rounded-lg bg-gray-100 px-3 py-1">
|
|
<span className="text-xs text-gray-600">이전 액션과의 관계:</span>
|
|
<Select
|
|
value={action.logicalOperator || "AND"}
|
|
onValueChange={(value: "AND" | "OR") => updateAction(actionIndex, "logicalOperator", value)}
|
|
>
|
|
<SelectTrigger className="h-8 w-20 text-sm">
|
|
<SelectValue />
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
<SelectItem value="AND">AND</SelectItem>
|
|
<SelectItem value="OR">OR</SelectItem>
|
|
</SelectContent>
|
|
</Select>
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
<div className="rounded border bg-white p-3">
|
|
<div className="mb-3 flex items-center justify-between">
|
|
<Input
|
|
value={action.name}
|
|
onChange={(e) => updateAction(actionIndex, "name", e.target.value)}
|
|
className="h-7 flex-1 text-xs font-medium"
|
|
placeholder="액션 이름"
|
|
/>
|
|
<Button
|
|
size="sm"
|
|
variant="ghost"
|
|
onClick={() => removeAction(actionIndex)}
|
|
className="h-7 w-7 p-0"
|
|
>
|
|
<Trash2 className="h-3 w-3" />
|
|
</Button>
|
|
</div>
|
|
|
|
<div className="grid grid-cols-1 gap-3">
|
|
{/* 액션 타입 */}
|
|
<div>
|
|
<Label className="text-xs">액션 타입</Label>
|
|
<Select
|
|
value={action.actionType}
|
|
onValueChange={(value: "insert" | "update" | "delete" | "upsert") =>
|
|
updateAction(actionIndex, "actionType", value)
|
|
}
|
|
>
|
|
<SelectTrigger className="h-7 text-xs">
|
|
<SelectValue />
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
<SelectItem value="insert">INSERT</SelectItem>
|
|
<SelectItem value="update">UPDATE</SelectItem>
|
|
<SelectItem value="delete">DELETE</SelectItem>
|
|
<SelectItem value="upsert">UPSERT</SelectItem>
|
|
</SelectContent>
|
|
</Select>
|
|
</div>
|
|
</div>
|
|
|
|
{/* 액션별 개별 실행 조건 */}
|
|
<ActionConditionsSection
|
|
action={action}
|
|
actionIndex={actionIndex}
|
|
settings={settings}
|
|
onSettingsChange={onSettingsChange}
|
|
fromTableColumns={fromTableColumns}
|
|
toTableColumns={toTableColumns}
|
|
fromTableName={fromTableName}
|
|
toTableName={toTableName}
|
|
/>
|
|
|
|
{/* 데이터 분할 설정 - DELETE 액션은 제외 */}
|
|
{action.actionType !== "delete" && (
|
|
<ActionSplitConfig
|
|
action={action}
|
|
actionIndex={actionIndex}
|
|
settings={settings}
|
|
onSettingsChange={onSettingsChange}
|
|
fromTableColumns={fromTableColumns}
|
|
toTableColumns={toTableColumns}
|
|
/>
|
|
)}
|
|
|
|
{/* 필드 매핑 - DELETE 액션은 제외 */}
|
|
{action.actionType !== "delete" && (
|
|
<ActionFieldMappings
|
|
action={action}
|
|
actionIndex={actionIndex}
|
|
settings={settings}
|
|
onSettingsChange={onSettingsChange}
|
|
availableTables={availableTables}
|
|
tableColumnsCache={tableColumnsCache}
|
|
fromTableColumns={fromTableColumns}
|
|
toTableColumns={toTableColumns}
|
|
fromTableName={fromTableName}
|
|
toTableName={toTableName}
|
|
enableMultiConnection={true}
|
|
/>
|
|
)}
|
|
|
|
{/* DELETE 액션일 때 다중 커넥션 지원 */}
|
|
{action.actionType === "delete" && (
|
|
<ActionFieldMappings
|
|
action={action}
|
|
actionIndex={actionIndex}
|
|
settings={settings}
|
|
onSettingsChange={onSettingsChange}
|
|
availableTables={availableTables}
|
|
tableColumnsCache={tableColumnsCache}
|
|
fromTableColumns={fromTableColumns}
|
|
toTableColumns={toTableColumns}
|
|
fromTableName={fromTableName}
|
|
toTableName={toTableName}
|
|
enableMultiConnection={true}
|
|
/>
|
|
)}
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|