전체 조건 설정 로직 수정
This commit is contained in:
parent
b1814e6ab8
commit
d18e78e8a0
|
|
@ -6,7 +6,6 @@ import { ArrowLeft } from "lucide-react";
|
|||
import { Button } from "@/components/ui/button";
|
||||
import { DataFlowDesigner } from "@/components/dataflow/DataFlowDesigner";
|
||||
import { DataFlowAPI } from "@/lib/api/dataflow";
|
||||
import { toast } from "sonner";
|
||||
|
||||
export default function DataFlowEditPage() {
|
||||
const params = useParams();
|
||||
|
|
@ -80,6 +79,7 @@ export default function DataFlowEditPage() {
|
|||
{/* 데이터플로우 디자이너 */}
|
||||
<div className="rounded-lg border border-gray-200 bg-white">
|
||||
<DataFlowDesigner
|
||||
key={diagramId}
|
||||
selectedDiagram={diagramName}
|
||||
diagramId={diagramId}
|
||||
onBackToList={handleBackToList}
|
||||
|
|
|
|||
|
|
@ -170,23 +170,92 @@ export const ConnectionSetupModal: React.FC<ConnectionSetupModalProps> = ({
|
|||
|
||||
// 기존 관계 정보가 있으면 사용, 없으면 기본값 설정
|
||||
const existingRel = connection.existingRelationship;
|
||||
const connectionType =
|
||||
(existingRel?.connectionType as "simple-key" | "data-save" | "external-call") || "simple-key";
|
||||
|
||||
setConfig({
|
||||
relationshipName: existingRel?.relationshipName || `${fromDisplayName} → ${toDisplayName}`,
|
||||
connectionType: (existingRel?.connectionType as "simple-key" | "data-save" | "external-call") || "simple-key",
|
||||
connectionType,
|
||||
fromColumnName: "",
|
||||
toColumnName: "",
|
||||
settings: existingRel?.settings || {},
|
||||
});
|
||||
|
||||
// 단순 키값 연결 기본값 설정
|
||||
// 🔥 기존 설정 데이터 로드
|
||||
if (existingRel?.settings) {
|
||||
const settings = existingRel.settings;
|
||||
|
||||
if (connectionType === "simple-key" && settings.notes) {
|
||||
setSimpleKeySettings({
|
||||
notes: settings.notes as string,
|
||||
});
|
||||
} else if (connectionType === "data-save" && settings.actions) {
|
||||
// data-save 설정 로드 - 안전하게 처리
|
||||
const actionsData = Array.isArray(settings.actions) ? settings.actions : [];
|
||||
setDataSaveSettings({
|
||||
actions: actionsData.map((action: any) => ({
|
||||
id: action.id || `action-${Date.now()}`,
|
||||
name: action.name || "새 액션",
|
||||
actionType: action.actionType || "insert",
|
||||
conditions: Array.isArray(action.conditions) ? action.conditions : [],
|
||||
fieldMappings: Array.isArray(action.fieldMappings)
|
||||
? action.fieldMappings.map((mapping: any) => ({
|
||||
sourceTable: mapping.sourceTable || "",
|
||||
sourceField: mapping.sourceField || "",
|
||||
targetTable: mapping.targetTable || "",
|
||||
targetField: mapping.targetField || "",
|
||||
defaultValue: mapping.defaultValue || "",
|
||||
transformFunction: mapping.transformFunction || "",
|
||||
}))
|
||||
: [],
|
||||
splitConfig: action.splitConfig
|
||||
? {
|
||||
sourceField: action.splitConfig.sourceField || "",
|
||||
delimiter: action.splitConfig.delimiter || ",",
|
||||
targetField: action.splitConfig.targetField || "",
|
||||
}
|
||||
: undefined,
|
||||
})),
|
||||
});
|
||||
|
||||
// 전체 실행 조건 로드 - 안전하게 처리
|
||||
if (settings.control) {
|
||||
const controlSettings = settings.control as { conditionTree?: ConditionNode[] };
|
||||
if (Array.isArray(controlSettings.conditionTree)) {
|
||||
// 기존 조건이 없을 때만 로드 (사용자가 추가한 조건 보존)
|
||||
setConditions((prevConditions) => {
|
||||
if (prevConditions.length === 0) {
|
||||
return controlSettings.conditionTree || [];
|
||||
}
|
||||
return prevConditions;
|
||||
});
|
||||
} else {
|
||||
// 기존 조건이 없을 때만 초기화
|
||||
setConditions((prevConditions) => (prevConditions.length === 0 ? [] : prevConditions));
|
||||
}
|
||||
} else {
|
||||
// 기존 조건이 없을 때만 초기화
|
||||
setConditions((prevConditions) => (prevConditions.length === 0 ? [] : prevConditions));
|
||||
}
|
||||
} else if (connectionType === "external-call") {
|
||||
setExternalCallSettings({
|
||||
callType: (settings.callType as "rest-api" | "webhook") || "rest-api",
|
||||
apiUrl: (settings.apiUrl as string) || "",
|
||||
httpMethod: (settings.httpMethod as "GET" | "POST" | "PUT" | "DELETE") || "POST",
|
||||
headers: (settings.headers as string) || "{}",
|
||||
bodyTemplate: (settings.bodyTemplate as string) || "{}",
|
||||
});
|
||||
}
|
||||
} else {
|
||||
// 기본값 설정
|
||||
setSimpleKeySettings({
|
||||
notes: `${fromDisplayName}과 ${toDisplayName} 간의 키값 연결`,
|
||||
});
|
||||
|
||||
// 데이터 저장 기본값 설정 (빈 배열로 시작)
|
||||
setDataSaveSettings({
|
||||
actions: [],
|
||||
});
|
||||
}
|
||||
|
||||
// 🔥 필드 선택 상태 초기화
|
||||
setSelectedFromColumns([]);
|
||||
|
|
@ -286,8 +355,8 @@ export const ConnectionSetupModal: React.FC<ConnectionSetupModalProps> = ({
|
|||
const tablesToLoad = new Set<string>();
|
||||
|
||||
// 필드 매핑에서 사용되는 모든 테이블 수집
|
||||
dataSaveSettings.actions.forEach((action) => {
|
||||
action.fieldMappings.forEach((mapping) => {
|
||||
(dataSaveSettings.actions || []).forEach((action) => {
|
||||
(action.fieldMappings || []).forEach((mapping) => {
|
||||
if (mapping.sourceTable && !tableColumnsCache[mapping.sourceTable]) {
|
||||
tablesToLoad.add(mapping.sourceTable);
|
||||
}
|
||||
|
|
@ -433,8 +502,11 @@ export const ConnectionSetupModal: React.FC<ConnectionSetupModalProps> = ({
|
|||
operator_type: "=",
|
||||
value: "",
|
||||
dataType: "string",
|
||||
logicalOperator: "AND", // 기본값으로 AND 설정
|
||||
// 첫 번째 조건이 아니고, 바로 앞이 group-start가 아니면 logicalOperator 추가
|
||||
...(conditions.length > 0 &&
|
||||
conditions[conditions.length - 1]?.type !== "group-start" && { logicalOperator: "AND" }),
|
||||
};
|
||||
|
||||
setConditions([...conditions, newCondition]);
|
||||
};
|
||||
|
||||
|
|
@ -448,7 +520,8 @@ export const ConnectionSetupModal: React.FC<ConnectionSetupModalProps> = ({
|
|||
type: "group-start",
|
||||
groupId,
|
||||
groupLevel,
|
||||
logicalOperator: conditions.length > 0 ? "AND" : undefined,
|
||||
// 첫 번째 그룹이 아니면 logicalOperator 추가
|
||||
...(conditions.length > 0 && { logicalOperator: "AND" }),
|
||||
};
|
||||
|
||||
setConditions([...conditions, groupStart]);
|
||||
|
|
@ -922,16 +995,17 @@ export const ConnectionSetupModal: React.FC<ConnectionSetupModalProps> = ({
|
|||
조건이 없으면 항상 실행됩니다.
|
||||
</div>
|
||||
) : (
|
||||
conditions.map((condition, index) => {
|
||||
<React.Fragment key="conditions-list">
|
||||
{conditions.map((condition, index) => {
|
||||
// 그룹 시작 렌더링
|
||||
if (condition.type === "group-start") {
|
||||
return (
|
||||
<div key={condition.id} className="flex items-center gap-2">
|
||||
{/* 그룹 시작 앞의 논리 연산자 */}
|
||||
{index > 0 && (
|
||||
{/* 그룹 시작 앞의 논리 연산자 - 이전 요소가 group-end가 아닌 경우에만 표시 */}
|
||||
{index > 0 && conditions[index - 1]?.type !== "group-end" && (
|
||||
<Select
|
||||
value={conditions[index - 1]?.logicalOperator || "AND"}
|
||||
onValueChange={(value: "AND" | "OR") => updateCondition(index - 1, "logicalOperator", value)}
|
||||
value={condition.logicalOperator || "AND"}
|
||||
onValueChange={(value: "AND" | "OR") => updateCondition(index, "logicalOperator", value)}
|
||||
>
|
||||
<SelectTrigger className="h-8 w-24 border-blue-200 bg-blue-50 text-xs">
|
||||
<SelectValue />
|
||||
|
|
@ -981,6 +1055,24 @@ export const ConnectionSetupModal: React.FC<ConnectionSetupModalProps> = ({
|
|||
<Trash2 className="h-3 w-3" />
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{/* 그룹 끝 다음에 다른 조건이나 그룹이 있으면 논리 연산자 표시 */}
|
||||
{index < conditions.length - 1 && (
|
||||
<Select
|
||||
value={conditions[index + 1]?.logicalOperator || "AND"}
|
||||
onValueChange={(value: "AND" | "OR") =>
|
||||
updateCondition(index + 1, "logicalOperator", value)
|
||||
}
|
||||
>
|
||||
<SelectTrigger className="h-8 w-24 border-blue-200 bg-blue-50 text-xs">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="AND">AND</SelectItem>
|
||||
<SelectItem value="OR">OR</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -988,11 +1080,13 @@ export const ConnectionSetupModal: React.FC<ConnectionSetupModalProps> = ({
|
|||
// 일반 조건 렌더링
|
||||
return (
|
||||
<div key={condition.id} className="flex items-center gap-2">
|
||||
{/* 그룹 내 첫 번째 조건이 아닐 때만 논리 연산자 표시 */}
|
||||
{index > 0 && conditions[index - 1]?.type !== "group-start" && (
|
||||
{/* 일반 조건 앞의 논리 연산자 - 이전 요소가 group-end가 아닌 경우에만 표시 */}
|
||||
{index > 0 &&
|
||||
conditions[index - 1]?.type !== "group-start" &&
|
||||
conditions[index - 1]?.type !== "group-end" && (
|
||||
<Select
|
||||
value={conditions[index - 1]?.logicalOperator || "AND"}
|
||||
onValueChange={(value: "AND" | "OR") => updateCondition(index - 1, "logicalOperator", value)}
|
||||
value={condition.logicalOperator || "AND"}
|
||||
onValueChange={(value: "AND" | "OR") => updateCondition(index, "logicalOperator", value)}
|
||||
>
|
||||
<SelectTrigger className="h-8 w-24 border-blue-200 bg-blue-50 text-xs">
|
||||
<SelectValue />
|
||||
|
|
@ -1125,13 +1219,19 @@ export const ConnectionSetupModal: React.FC<ConnectionSetupModalProps> = ({
|
|||
})()}
|
||||
|
||||
{/* 삭제 버튼 */}
|
||||
<Button size="sm" variant="ghost" onClick={() => removeCondition(index)} className="h-8 w-8 p-0">
|
||||
<Button
|
||||
size="sm"
|
||||
variant="ghost"
|
||||
onClick={() => removeCondition(index)}
|
||||
className="h-8 w-8 p-0"
|
||||
>
|
||||
<Trash2 className="h-3 w-3" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})
|
||||
})}
|
||||
</React.Fragment>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -1318,7 +1418,7 @@ export const ConnectionSetupModal: React.FC<ConnectionSetupModalProps> = ({
|
|||
conditions: [],
|
||||
splitConfig: {
|
||||
sourceField: "",
|
||||
delimiter: ",",
|
||||
delimiter: "",
|
||||
targetField: "",
|
||||
},
|
||||
};
|
||||
|
|
@ -1334,13 +1434,13 @@ export const ConnectionSetupModal: React.FC<ConnectionSetupModalProps> = ({
|
|||
</Button>
|
||||
</div>
|
||||
|
||||
{dataSaveSettings.actions.length === 0 ? (
|
||||
{(dataSaveSettings.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">
|
||||
{dataSaveSettings.actions.map((action, actionIndex) => (
|
||||
{(dataSaveSettings.actions || []).map((action, actionIndex) => (
|
||||
<div key={action.id} className="rounded border bg-white p-3">
|
||||
<div className="mb-3 flex items-center justify-between">
|
||||
<Input
|
||||
|
|
@ -1471,9 +1571,11 @@ export const ConnectionSetupModal: React.FC<ConnectionSetupModalProps> = ({
|
|||
</div>
|
||||
{action.conditions && action.conditions.length > 0 && (
|
||||
<div className="space-y-2">
|
||||
{action.conditions.map((condition, condIndex) =>
|
||||
renderActionCondition(condition, condIndex, actionIndex),
|
||||
)}
|
||||
{action.conditions.map((condition, condIndex) => (
|
||||
<div key={`action-${actionIndex}-condition-${condition.id}`}>
|
||||
{renderActionCondition(condition, condIndex, actionIndex)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
|
@ -1549,13 +1651,13 @@ export const ConnectionSetupModal: React.FC<ConnectionSetupModalProps> = ({
|
|||
<div>
|
||||
<Label className="text-xs text-gray-500">구분자</Label>
|
||||
<Input
|
||||
value={action.splitConfig?.delimiter || ","}
|
||||
value={action.splitConfig?.delimiter || ""}
|
||||
onChange={(e) => {
|
||||
const newActions = [...dataSaveSettings.actions];
|
||||
if (!newActions[actionIndex].splitConfig) {
|
||||
newActions[actionIndex].splitConfig = {
|
||||
sourceField: "",
|
||||
delimiter: ",",
|
||||
delimiter: "",
|
||||
targetField: "",
|
||||
};
|
||||
}
|
||||
|
|
@ -1627,7 +1729,10 @@ export const ConnectionSetupModal: React.FC<ConnectionSetupModalProps> = ({
|
|||
</div>
|
||||
<div className="space-y-3">
|
||||
{action.fieldMappings.map((mapping, mappingIndex) => (
|
||||
<div key={mappingIndex} className="rounded border bg-white p-2">
|
||||
<div
|
||||
key={`${action.id}-mapping-${mappingIndex}-${mapping.sourceField || "empty"}-${mapping.targetField || "empty"}`}
|
||||
className="rounded border bg-white p-2"
|
||||
>
|
||||
{/* 컴팩트한 매핑 표시 */}
|
||||
<div className="flex items-center gap-2 text-xs">
|
||||
{/* 소스 */}
|
||||
|
|
|
|||
|
|
@ -74,6 +74,56 @@ interface DataFlowDesignerProps {
|
|||
// 내부에서 사용할 확장된 JsonRelationship 타입 (connectionType 포함)
|
||||
interface ExtendedJsonRelationship extends JsonRelationship {
|
||||
connectionType: "simple-key" | "data-save" | "external-call";
|
||||
settings?: {
|
||||
control?: {
|
||||
triggerType?: "insert" | "update" | "delete";
|
||||
conditionTree?: Array<{
|
||||
id: string;
|
||||
type: string;
|
||||
field?: string;
|
||||
operator_type?: string;
|
||||
value?: unknown;
|
||||
logicalOperator?: string;
|
||||
groupId?: string;
|
||||
groupLevel?: number;
|
||||
}>;
|
||||
};
|
||||
actions?: Array<{
|
||||
id: string;
|
||||
name: string;
|
||||
actionType: "insert" | "update" | "delete" | "upsert";
|
||||
conditions?: Array<{
|
||||
id: string;
|
||||
type: string;
|
||||
field?: string;
|
||||
operator_type?: string;
|
||||
value?: unknown;
|
||||
logicalOperator?: string;
|
||||
groupId?: string;
|
||||
groupLevel?: number;
|
||||
}>;
|
||||
fieldMappings: Array<{
|
||||
sourceTable?: string;
|
||||
sourceField: string;
|
||||
targetTable?: string;
|
||||
targetField: string;
|
||||
defaultValue?: string;
|
||||
}>;
|
||||
splitConfig?: {
|
||||
sourceField: string;
|
||||
delimiter: string;
|
||||
targetField: string;
|
||||
};
|
||||
}>;
|
||||
notes?: string;
|
||||
apiCall?: {
|
||||
url: string;
|
||||
method: string;
|
||||
headers: Array<{ key: string; value: string }>;
|
||||
body: string;
|
||||
successCriteria: string;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export const DataFlowDesigner: React.FC<DataFlowDesignerProps> = ({
|
||||
|
|
@ -249,7 +299,7 @@ export const DataFlowDesigner: React.FC<DataFlowDesignerProps> = ({
|
|||
console.log("🔥 정규화된 관계들:", normalizedRelationships);
|
||||
setTempRelationships(normalizedRelationships);
|
||||
setCurrentDiagramId(currentDiagramId);
|
||||
setCurrentDiagramCategory(jsonDiagram.category || "simple-key"); // 관계도의 연결 종류 설정
|
||||
setCurrentDiagramCategory("simple-key"); // 관계도의 연결 종류 설정 (기본값)
|
||||
|
||||
// 테이블 노드 생성을 위한 테이블 정보 로드
|
||||
|
||||
|
|
@ -755,7 +805,7 @@ export const DataFlowDesigner: React.FC<DataFlowDesignerProps> = ({
|
|||
console.log("🔥 연결 타입:", newRelationship.connectionType);
|
||||
|
||||
// 첫 번째 관계가 추가되면 관계도의 category를 해당 connectionType으로 설정
|
||||
if (tempRelationships.length === 0) {
|
||||
if ((tempRelationships || []).length === 0) {
|
||||
setCurrentDiagramCategory(relationship.connection_type);
|
||||
}
|
||||
|
||||
|
|
@ -796,7 +846,7 @@ export const DataFlowDesigner: React.FC<DataFlowDesignerProps> = ({
|
|||
console.log("메모리에 관계 생성 완료:", newRelationship);
|
||||
toast.success("관계가 생성되었습니다. 저장 버튼을 눌러 관계도를 저장하세요.");
|
||||
},
|
||||
[pendingConnection, setEdges, editingRelationshipId, tempRelationships.length],
|
||||
[pendingConnection, setEdges, editingRelationshipId, tempRelationships],
|
||||
);
|
||||
|
||||
// 연결 설정 취소
|
||||
|
|
@ -841,12 +891,12 @@ export const DataFlowDesigner: React.FC<DataFlowDesignerProps> = ({
|
|||
console.log("🔍 저장할 노드 위치 정보:", nodePositions);
|
||||
console.log("📊 현재 노드 개수:", nodes.length);
|
||||
console.log("📋 연결된 테이블 목록:", connectedTables);
|
||||
console.log("🔗 관계 개수:", tempRelationships.length);
|
||||
console.log("🔗 관계 개수:", (tempRelationships || []).length);
|
||||
|
||||
// 🔥 주요 연결 타입 변수 제거 (더 이상 사용하지 않음)
|
||||
|
||||
// 🔥 수정: relationships는 핵심 관계 정보만 포함, settings 전체 제거
|
||||
const cleanRelationships = tempRelationships.map((rel) => {
|
||||
const cleanRelationships = (tempRelationships || []).map((rel) => {
|
||||
// 🔥 settings 전체를 제거하고 핵심 정보만 유지
|
||||
const cleanRel: JsonRelationship = {
|
||||
id: rel.id,
|
||||
|
|
@ -871,12 +921,12 @@ export const DataFlowDesigner: React.FC<DataFlowDesignerProps> = ({
|
|||
},
|
||||
node_positions: nodePositions,
|
||||
// 🔥 수정: 각 관계별 category 정보를 배열로 저장
|
||||
category: tempRelationships.map((rel) => ({
|
||||
category: (tempRelationships || []).map((rel) => ({
|
||||
id: rel.id,
|
||||
category: rel.connectionType,
|
||||
})),
|
||||
// 🔥 각 관계별 control 정보를 배열로 저장 (전체 실행 조건)
|
||||
control: tempRelationships
|
||||
control: (tempRelationships || [])
|
||||
.filter((rel) => rel.connectionType === "data-save")
|
||||
.map((rel) => {
|
||||
console.log("🔍 Control 데이터 추출 중:", {
|
||||
|
|
@ -889,37 +939,72 @@ export const DataFlowDesigner: React.FC<DataFlowDesignerProps> = ({
|
|||
const controlData = rel.settings?.control as {
|
||||
triggerType?: "insert" | "update" | "delete";
|
||||
conditionTree?: Array<{
|
||||
field: string;
|
||||
operator_type: "=" | "!=" | ">" | "<" | ">=" | "<=" | "LIKE";
|
||||
value: unknown;
|
||||
id: string;
|
||||
type: string;
|
||||
field?: string;
|
||||
operator_type?: "=" | "!=" | ">" | "<" | ">=" | "<=" | "LIKE";
|
||||
value?: unknown;
|
||||
logicalOperator?: "AND" | "OR";
|
||||
groupId?: string;
|
||||
groupLevel?: number;
|
||||
}>;
|
||||
};
|
||||
|
||||
console.log("🔍 추출된 controlData:", controlData);
|
||||
console.log("🔍 conditionTree:", controlData?.conditionTree);
|
||||
|
||||
// 🔥 조건 필터링: 괄호 포함 모든 유효한 조건 유지
|
||||
const validConditions = (controlData?.conditionTree || [])
|
||||
.filter((cond) => {
|
||||
// 괄호 조건은 항상 유지
|
||||
if (cond.type === "group-start" || cond.type === "group-end") {
|
||||
return true;
|
||||
}
|
||||
// 실제 조건은 field와 value가 있어야 함
|
||||
return cond.type === "condition" && cond.field && cond.value !== undefined && cond.value !== "";
|
||||
})
|
||||
.map((cond, index) => {
|
||||
// 괄호 조건 처리
|
||||
if (cond.type === "group-start" || cond.type === "group-end") {
|
||||
return {
|
||||
id: rel.id, // relationships의 id와 동일
|
||||
triggerType: (controlData?.triggerType as "insert" | "update" | "delete") || "insert",
|
||||
// 🔥 실제 저장된 conditionTree에서 조건 추출
|
||||
conditions: (controlData?.conditionTree || []).map((cond) => ({
|
||||
id: cond.id,
|
||||
type: cond.type,
|
||||
...(cond.groupId && { groupId: cond.groupId }),
|
||||
...(cond.groupLevel !== undefined && { groupLevel: cond.groupLevel }),
|
||||
// 첫 번째가 아닐 때만 logicalOperator 포함
|
||||
...(index > 0 && cond.logicalOperator && { logicalOperator: cond.logicalOperator }),
|
||||
};
|
||||
}
|
||||
|
||||
// 일반 조건 처리
|
||||
return {
|
||||
id: cond.id,
|
||||
type: cond.type,
|
||||
field: cond.field,
|
||||
operator: cond.operator_type,
|
||||
value: cond.value,
|
||||
logicalOperator: cond.logicalOperator || "AND",
|
||||
})),
|
||||
dataType: "string", // 기본값
|
||||
// 첫 번째 조건이 아닐 때만 logicalOperator 포함
|
||||
...(index > 0 && cond.logicalOperator && { logicalOperator: cond.logicalOperator }),
|
||||
};
|
||||
});
|
||||
|
||||
return {
|
||||
id: rel.id, // relationships의 id와 동일
|
||||
triggerType: (controlData?.triggerType as "insert" | "update" | "delete") || "insert",
|
||||
conditions: validConditions,
|
||||
};
|
||||
}),
|
||||
// 🔥 각 관계별 plan 정보를 배열로 저장 (저장 액션)
|
||||
plan: tempRelationships
|
||||
plan: (tempRelationships || [])
|
||||
.filter((rel) => rel.connectionType === "data-save")
|
||||
.map((rel) => ({
|
||||
id: rel.id, // relationships의 id와 동일
|
||||
sourceTable: rel.fromTable,
|
||||
// 🔥 실제 사용자가 설정한 액션들 사용
|
||||
actions:
|
||||
(rel.settings?.actions as Array<{
|
||||
(
|
||||
rel.settings?.actions as Array<{
|
||||
id: string;
|
||||
name: string;
|
||||
actionType: "insert" | "update" | "delete" | "upsert";
|
||||
|
|
@ -931,15 +1016,82 @@ export const DataFlowDesigner: React.FC<DataFlowDesignerProps> = ({
|
|||
defaultValue?: string;
|
||||
transformFunction?: string;
|
||||
}>;
|
||||
splitConfig?: {
|
||||
sourceField: string;
|
||||
delimiter: string;
|
||||
targetField: string;
|
||||
};
|
||||
conditions?: Array<{
|
||||
id: string;
|
||||
type: string;
|
||||
field: string;
|
||||
operator_type: string;
|
||||
value: unknown;
|
||||
field?: string;
|
||||
operator_type?: string;
|
||||
value?: unknown;
|
||||
logicalOperator?: string;
|
||||
groupId?: string;
|
||||
groupLevel?: number;
|
||||
}>;
|
||||
}>) || [],
|
||||
}>
|
||||
)?.map((action) => ({
|
||||
...action,
|
||||
// fieldMappings에서 불필요한 transformFunction 제거
|
||||
fieldMappings: action.fieldMappings?.map((mapping) => ({
|
||||
sourceTable: mapping.sourceTable,
|
||||
sourceField: mapping.sourceField,
|
||||
targetTable: mapping.targetTable,
|
||||
targetField: mapping.targetField,
|
||||
defaultValue: mapping.defaultValue,
|
||||
// transformFunction 제거 - 불필요한 필드
|
||||
})),
|
||||
// splitConfig 처리 - 사용자가 설정하지 않은 경우 포함하지 않음
|
||||
...(action.splitConfig &&
|
||||
(action.splitConfig.sourceField ||
|
||||
action.splitConfig.delimiter ||
|
||||
action.splitConfig.targetField) && {
|
||||
splitConfig: {
|
||||
sourceField: action.splitConfig.sourceField || "",
|
||||
delimiter: action.splitConfig.delimiter || "",
|
||||
targetField: action.splitConfig.targetField || "",
|
||||
},
|
||||
}),
|
||||
// 🔥 조건 처리: 괄호 포함 모든 유효한 조건 유지
|
||||
...(action.conditions && {
|
||||
conditions: action.conditions
|
||||
.filter((cond) => {
|
||||
// 괄호 조건은 항상 유지
|
||||
if (cond.type === "group-start" || cond.type === "group-end") {
|
||||
return true;
|
||||
}
|
||||
// 실제 조건은 field와 value가 있어야 함
|
||||
return cond.type === "condition" && cond.field && cond.value !== undefined && cond.value !== "";
|
||||
})
|
||||
.map((cond, index) => {
|
||||
// 괄호 조건 처리
|
||||
if (cond.type === "group-start" || cond.type === "group-end") {
|
||||
return {
|
||||
id: cond.id,
|
||||
type: cond.type,
|
||||
...(cond.groupId && { groupId: cond.groupId }),
|
||||
...(cond.groupLevel !== undefined && { groupLevel: cond.groupLevel }),
|
||||
// 첫 번째가 아닐 때만 logicalOperator 포함
|
||||
...(index > 0 && cond.logicalOperator && { logicalOperator: cond.logicalOperator }),
|
||||
};
|
||||
}
|
||||
|
||||
// 일반 조건 처리
|
||||
return {
|
||||
id: cond.id,
|
||||
type: cond.type,
|
||||
field: cond.field,
|
||||
value: cond.value,
|
||||
dataType: "string", // 기본값
|
||||
operator_type: cond.operator_type,
|
||||
// 첫 번째 조건이 아닐 때만 logicalOperator 포함
|
||||
...(index > 0 && cond.logicalOperator && { logicalOperator: cond.logicalOperator }),
|
||||
};
|
||||
}),
|
||||
}),
|
||||
})) || [],
|
||||
})),
|
||||
};
|
||||
|
||||
|
|
@ -955,11 +1107,13 @@ export const DataFlowDesigner: React.FC<DataFlowDesignerProps> = ({
|
|||
|
||||
let savedDiagram;
|
||||
|
||||
// 편집 모드 vs 신규 생성 모드 구분
|
||||
if (diagramId && diagramId > 0) {
|
||||
// 편집 모드 vs 신규 생성 모드 구분 (currentDiagramId 우선 사용)
|
||||
const effectiveDiagramId = currentDiagramId || diagramId;
|
||||
|
||||
if (effectiveDiagramId && effectiveDiagramId > 0) {
|
||||
// 편집 모드: 기존 관계도 업데이트
|
||||
savedDiagram = await DataFlowAPI.updateJsonDataFlowDiagram(
|
||||
diagramId,
|
||||
effectiveDiagramId,
|
||||
createRequest,
|
||||
companyCode,
|
||||
user?.userId || "SYSTEM",
|
||||
|
|
@ -981,7 +1135,7 @@ export const DataFlowDesigner: React.FC<DataFlowDesignerProps> = ({
|
|||
setCurrentDiagramId(savedDiagram.diagram_id);
|
||||
|
||||
// 관계도 이름 업데이트 (편집 모드일 때만)
|
||||
if (diagramId && diagramId > 0 && onDiagramNameUpdate) {
|
||||
if (effectiveDiagramId && effectiveDiagramId > 0 && onDiagramNameUpdate) {
|
||||
onDiagramNameUpdate(diagramName);
|
||||
}
|
||||
|
||||
|
|
@ -993,7 +1147,7 @@ export const DataFlowDesigner: React.FC<DataFlowDesignerProps> = ({
|
|||
setIsSaving(false);
|
||||
}
|
||||
},
|
||||
[tempRelationships, diagramId, companyCode, user?.userId, nodes, onDiagramNameUpdate],
|
||||
[tempRelationships, diagramId, currentDiagramId, companyCode, user?.userId, nodes, onDiagramNameUpdate],
|
||||
);
|
||||
|
||||
// 저장 모달 열기
|
||||
|
|
@ -1204,7 +1358,7 @@ export const DataFlowDesigner: React.FC<DataFlowDesignerProps> = ({
|
|||
hasUnsavedChanges ? "animate-pulse" : ""
|
||||
}`}
|
||||
>
|
||||
💾 관계도 저장 {tempRelationships.length > 0 && `(${tempRelationships.length})`}
|
||||
💾 관계도 저장 {(tempRelationships || []).length > 0 && `(${(tempRelationships || []).length})`}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
|
|
@ -1222,7 +1376,7 @@ export const DataFlowDesigner: React.FC<DataFlowDesignerProps> = ({
|
|||
</div>
|
||||
<div className="flex justify-between">
|
||||
<span>메모리 관계:</span>
|
||||
<span className="font-medium text-orange-600">{tempRelationships.length}개</span>
|
||||
<span className="font-medium text-orange-600">{(tempRelationships || []).length}개</span>
|
||||
</div>
|
||||
<div className="flex justify-between">
|
||||
<span>관계도 ID:</span>
|
||||
|
|
@ -1313,7 +1467,7 @@ export const DataFlowDesigner: React.FC<DataFlowDesignerProps> = ({
|
|||
<div className="flex items-center gap-1">
|
||||
{/* 편집 버튼 */}
|
||||
<button
|
||||
onClick={(e) => {
|
||||
onClick={async (e) => {
|
||||
e.stopPropagation();
|
||||
// 관계 선택 시 수정 모드로 전환
|
||||
setEditingRelationshipId(relationship.id);
|
||||
|
|
@ -1328,7 +1482,34 @@ export const DataFlowDesigner: React.FC<DataFlowDesignerProps> = ({
|
|||
}
|
||||
setSelectedColumns(newSelectedColumns);
|
||||
|
||||
// 🔥 수정: 연결 설정 모달 열기
|
||||
// 🔥 수정: 데이터베이스에서 관계 설정 정보 로드
|
||||
let relationshipSettings = {};
|
||||
if (diagramId && diagramId > 0) {
|
||||
try {
|
||||
const jsonDiagram = await DataFlowAPI.getJsonDataFlowDiagramById(
|
||||
diagramId,
|
||||
companyCode,
|
||||
);
|
||||
if (jsonDiagram && relationship.connectionType === "data-save") {
|
||||
const control = jsonDiagram.control?.find((c) => c.id === relationship.id);
|
||||
const plan = jsonDiagram.plan?.find((p) => p.id === relationship.id);
|
||||
|
||||
relationshipSettings = {
|
||||
control: control
|
||||
? {
|
||||
triggerType: control.triggerType,
|
||||
conditionTree: control.conditions || [],
|
||||
}
|
||||
: undefined,
|
||||
actions: plan ? plan.actions || [] : [],
|
||||
};
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("관계 설정 정보 로드 실패:", error);
|
||||
}
|
||||
}
|
||||
|
||||
// 연결 설정 모달 열기
|
||||
const fromTable = nodes.find(
|
||||
(node) => node.data?.table?.tableName === relationship.fromTable,
|
||||
);
|
||||
|
|
@ -1361,7 +1542,7 @@ export const DataFlowDesigner: React.FC<DataFlowDesignerProps> = ({
|
|||
existingRelationship: {
|
||||
relationshipName: relationship.relationshipName,
|
||||
connectionType: relationship.connectionType,
|
||||
settings: relationship.settings || {},
|
||||
settings: relationshipSettings,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
|
@ -1559,6 +1740,11 @@ export const DataFlowDesigner: React.FC<DataFlowDesignerProps> = ({
|
|||
|
||||
{/* 연결 설정 모달 */}
|
||||
<ConnectionSetupModal
|
||||
key={
|
||||
pendingConnection
|
||||
? `${pendingConnection.fromNode?.tableName || "unknown"}-${pendingConnection.toNode?.tableName || "unknown"}`
|
||||
: "connection-modal"
|
||||
}
|
||||
isOpen={!!pendingConnection}
|
||||
connection={pendingConnection}
|
||||
companyCode={companyCode}
|
||||
|
|
|
|||
|
|
@ -195,7 +195,56 @@ export interface JsonDataFlowDiagram {
|
|||
tables: string[];
|
||||
};
|
||||
node_positions?: NodePositions;
|
||||
category?: string; // 연결 종류 ("simple-key", "data-save", "external-call")
|
||||
category?: Array<{
|
||||
id: string;
|
||||
category: string;
|
||||
}>;
|
||||
control?: Array<{
|
||||
id: string;
|
||||
triggerType: "insert" | "update" | "delete";
|
||||
conditions: Array<{
|
||||
id?: string;
|
||||
type?: string;
|
||||
field?: string;
|
||||
operator?: string;
|
||||
value?: unknown;
|
||||
logicalOperator?: string;
|
||||
groupId?: string;
|
||||
groupLevel?: number;
|
||||
}>;
|
||||
}>;
|
||||
plan?: Array<{
|
||||
id: string;
|
||||
sourceTable: string;
|
||||
actions: Array<{
|
||||
id: string;
|
||||
name: string;
|
||||
actionType: "insert" | "update" | "delete" | "upsert";
|
||||
conditions?: Array<{
|
||||
id: string;
|
||||
type: string;
|
||||
field?: string;
|
||||
operator_type?: string;
|
||||
value?: unknown;
|
||||
logicalOperator?: string;
|
||||
groupId?: string;
|
||||
groupLevel?: number;
|
||||
}>;
|
||||
fieldMappings: Array<{
|
||||
sourceTable?: string;
|
||||
sourceField: string;
|
||||
targetTable?: string;
|
||||
targetField: string;
|
||||
defaultValue?: string;
|
||||
transformFunction?: string;
|
||||
}>;
|
||||
splitConfig?: {
|
||||
sourceField: string;
|
||||
delimiter: string;
|
||||
targetField: string;
|
||||
};
|
||||
}>;
|
||||
}>;
|
||||
company_code: string;
|
||||
created_at?: string;
|
||||
updated_at?: string;
|
||||
|
|
@ -230,11 +279,16 @@ export interface CreateDiagramRequest {
|
|||
control?: Array<{
|
||||
id: string; // relationships의 id와 동일
|
||||
triggerType: "insert" | "update" | "delete";
|
||||
conditions?: Array<{
|
||||
field: string;
|
||||
operator: "=" | "!=" | ">" | "<" | ">=" | "<=" | "LIKE";
|
||||
value: unknown;
|
||||
conditions: Array<{
|
||||
id?: string;
|
||||
type?: string;
|
||||
field?: string;
|
||||
operator?: string;
|
||||
value?: unknown;
|
||||
dataType?: string;
|
||||
logicalOperator?: "AND" | "OR";
|
||||
groupId?: string;
|
||||
groupLevel?: number;
|
||||
}>;
|
||||
}>;
|
||||
// 🔥 저장 액션 - relationships의 id와 동일한 id 사용
|
||||
|
|
@ -251,15 +305,22 @@ export interface CreateDiagramRequest {
|
|||
targetTable?: string;
|
||||
targetField: string;
|
||||
defaultValue?: string;
|
||||
transformFunction?: string;
|
||||
}>;
|
||||
splitConfig?: {
|
||||
sourceField: string;
|
||||
delimiter: string;
|
||||
targetField: string;
|
||||
};
|
||||
conditions?: Array<{
|
||||
id: string;
|
||||
type: string;
|
||||
field: string;
|
||||
operator_type: string;
|
||||
value: unknown;
|
||||
field?: string;
|
||||
operator_type?: string;
|
||||
value?: unknown;
|
||||
dataType?: string;
|
||||
logicalOperator?: string;
|
||||
groupId?: string;
|
||||
groupLevel?: number;
|
||||
}>;
|
||||
}>;
|
||||
}>;
|
||||
|
|
@ -629,7 +690,7 @@ export class DataFlowAPI {
|
|||
to_table_name: rel.toTable,
|
||||
from_column_name: rel.fromColumns.join(","),
|
||||
to_column_name: rel.toColumns.join(","),
|
||||
connection_type: (jsonDiagram.category as "simple-key" | "data-save" | "external-call") || "simple-key", // 관계도의 category 사용
|
||||
connection_type: rel.connectionType || "simple-key", // 각 관계의 connectionType 사용
|
||||
company_code: companyCode, // 실제 사용자 회사 코드 사용
|
||||
settings: rel.settings || {},
|
||||
created_at: jsonDiagram.created_at,
|
||||
|
|
|
|||
Loading…
Reference in New Issue