Enhance batch management functionality by adding node flow execution support and improving batch configuration creation. Introduce new API endpoint for retrieving node flows and update existing batch services to handle execution types. Update frontend components to support new scheduling options and node flow selection.

This commit is contained in:
DDD1542
2026-03-19 15:07:07 +09:00
parent 7f781b0177
commit 43cf91e748
41 changed files with 4020 additions and 3155 deletions

View File

@@ -1,87 +1,38 @@
"use client";
/**
* 데이터 변환 노드
*/
import { memo } from "react";
import { Handle, Position, NodeProps } from "reactflow";
import { Wand2, ArrowRight } from "lucide-react";
import { NodeProps } from "reactflow";
import { Repeat } from "lucide-react";
import { CompactNodeShell } from "./CompactNodeShell";
import type { DataTransformNodeData } from "@/types/node-editor";
export const DataTransformNode = memo(({ data, selected }: NodeProps<DataTransformNodeData>) => {
const ruleCount = data.transformRules?.length || 0;
const summary = ruleCount > 0
? `${ruleCount}개 변환 규칙`
: "변환 규칙을 설정해 주세요";
return (
<div
className={`min-w-[250px] rounded-lg border-2 bg-white shadow-md transition-all ${
selected ? "border-orange-500 shadow-lg" : "border-border"
}`}
<CompactNodeShell
color="#06B6D4"
label={data.displayName || "데이터 변환"}
summary={summary}
icon={<Repeat className="h-3.5 w-3.5" />}
selected={selected}
>
{/* 헤더 */}
<div className="flex items-center gap-2 rounded-t-lg bg-primary px-3 py-2 text-white">
<Wand2 className="h-4 w-4" />
<div className="flex-1">
<div className="text-sm font-semibold">{data.displayName || "데이터 변환"}</div>
<div className="text-xs opacity-80">{data.transformations?.length || 0} </div>
{ruleCount > 0 && (
<div className="space-y-0.5">
{data.transformRules!.slice(0, 3).map((r: any, i: number) => (
<div key={i} className="flex items-center gap-1.5">
<div className="h-1 w-1 rounded-full bg-cyan-400" />
<span>{r.sourceField || r.field || `규칙 ${i + 1}`}</span>
{r.targetField && <span className="text-zinc-600"> {r.targetField}</span>}
</div>
))}
{ruleCount > 3 && <span className="text-zinc-600"> {ruleCount - 3}</span>}
</div>
</div>
{/* 본문 */}
<div className="p-3">
{data.transformations && data.transformations.length > 0 ? (
<div className="space-y-2">
{data.transformations.slice(0, 3).map((transform, idx) => {
const sourceLabel = transform.sourceFieldLabel || transform.sourceField || "소스";
const targetField = transform.targetField || transform.sourceField;
const targetLabel = transform.targetFieldLabel || targetField;
const isInPlace = !transform.targetField || transform.targetField === transform.sourceField;
return (
<div key={idx} className="rounded bg-indigo-50 p-2">
<div className="mb-1 flex items-center gap-2 text-xs">
<span className="font-medium text-indigo-700">{transform.type}</span>
</div>
<div className="text-xs text-muted-foreground">
{sourceLabel}
<span className="mx-1 text-muted-foreground/70"></span>
{isInPlace ? (
<span className="font-medium text-primary">()</span>
) : (
<span>{targetLabel}</span>
)}
</div>
{/* 타입별 추가 정보 */}
{transform.type === "EXPLODE" && transform.delimiter && (
<div className="mt-1 text-xs text-muted-foreground">: {transform.delimiter}</div>
)}
{transform.type === "CONCAT" && transform.separator && (
<div className="mt-1 text-xs text-muted-foreground">: {transform.separator}</div>
)}
{transform.type === "REPLACE" && (
<div className="mt-1 text-xs text-muted-foreground">
"{transform.searchValue}" "{transform.replaceValue}"
</div>
)}
{transform.expression && (
<div className="mt-1 text-xs text-muted-foreground">
<code className="rounded bg-white px-1 py-0.5">{transform.expression}</code>
</div>
)}
</div>
);
})}
{data.transformations.length > 3 && (
<div className="text-xs text-muted-foreground/70">... {data.transformations.length - 3}</div>
)}
</div>
) : (
<div className="py-4 text-center text-xs text-muted-foreground/70"> </div>
)}
</div>
{/* 핸들 */}
<Handle type="target" position={Position.Left} className="!h-3 !w-3 !bg-primary" />
<Handle type="source" position={Position.Right} className="!h-3 !w-3 !bg-primary" />
</div>
)}
</CompactNodeShell>
);
});