94 lines
3.5 KiB
TypeScript
94 lines
3.5 KiB
TypeScript
"use client";
|
|
|
|
import { memo } from "react";
|
|
import { Handle, Position, NodeProps } from "reactflow";
|
|
import { GitBranch } from "lucide-react";
|
|
import { CompactNodeShell } from "./CompactNodeShell";
|
|
import type { ConditionNodeData } from "@/types/node-editor";
|
|
|
|
const OPERATOR_LABELS: Record<string, string> = {
|
|
EQUALS: "=", NOT_EQUALS: "!=",
|
|
GREATER_THAN: ">", LESS_THAN: "<",
|
|
GREATER_THAN_OR_EQUAL: ">=", LESS_THAN_OR_EQUAL: "<=",
|
|
LIKE: "포함", NOT_LIKE: "미포함",
|
|
IN: "IN", NOT_IN: "NOT IN",
|
|
IS_NULL: "NULL", IS_NOT_NULL: "NOT NULL",
|
|
EXISTS_IN: "EXISTS", NOT_EXISTS_IN: "NOT EXISTS",
|
|
};
|
|
|
|
export const ConditionNode = memo(({ data, selected }: NodeProps<ConditionNodeData>) => {
|
|
const condCount = data.conditions?.length || 0;
|
|
const summary = condCount > 0
|
|
? `${condCount}개 조건 (${data.logic || "AND"})`
|
|
: "조건을 설정해 주세요";
|
|
|
|
return (
|
|
<div
|
|
className={`rounded-lg border bg-zinc-900 shadow-lg transition-all ${
|
|
selected ? "border-violet-500 shadow-violet-500/20" : "border-zinc-700"
|
|
}`}
|
|
style={{ minWidth: "260px", maxWidth: "320px" }}
|
|
>
|
|
<Handle
|
|
type="target"
|
|
position={Position.Left}
|
|
className="!h-2.5 !w-2.5 !border-2 !border-amber-500 !bg-zinc-900"
|
|
/>
|
|
|
|
{/* 헤더 */}
|
|
<div className="flex items-center gap-2.5 px-3 py-2.5">
|
|
<div className="flex h-7 w-7 flex-shrink-0 items-center justify-center rounded-md bg-amber-500/20">
|
|
<GitBranch className="h-3.5 w-3.5 text-amber-400" />
|
|
</div>
|
|
<div className="min-w-0 flex-1">
|
|
<div className="text-xs font-semibold text-zinc-200">
|
|
{data.displayName || "조건 분기"}
|
|
</div>
|
|
<div className="line-clamp-2 text-[10px] leading-relaxed text-zinc-500">{summary}</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* 조건 미리보기 */}
|
|
{condCount > 0 && (
|
|
<div className="space-y-0.5 border-t border-zinc-800 px-3 py-2 text-[10px] text-zinc-400">
|
|
{data.conditions!.slice(0, 2).map((c, i) => (
|
|
<div key={i} className="flex items-center gap-1 flex-wrap">
|
|
{i > 0 && <span className="text-amber-500">{data.logic}</span>}
|
|
<span className="font-mono text-zinc-300">{c.field}</span>
|
|
<span className="text-amber-400">{OPERATOR_LABELS[c.operator] || c.operator}</span>
|
|
{c.value !== undefined && c.value !== null && (
|
|
<span className="text-zinc-500">{String(c.value)}</span>
|
|
)}
|
|
</div>
|
|
))}
|
|
{condCount > 2 && <span className="text-zinc-600">외 {condCount - 2}개</span>}
|
|
</div>
|
|
)}
|
|
|
|
{/* 분기 출력 */}
|
|
<div className="border-t border-zinc-800">
|
|
<div className="relative flex items-center justify-end px-3 py-1.5">
|
|
<span className="text-[10px] font-medium text-emerald-400">통과</span>
|
|
<Handle
|
|
type="source"
|
|
position={Position.Right}
|
|
id="true"
|
|
className="!h-2.5 !w-2.5 !border-2 !border-emerald-500 !bg-zinc-900"
|
|
/>
|
|
</div>
|
|
<div className="relative flex items-center justify-end border-t border-zinc-800/50 px-3 py-1.5">
|
|
<span className="text-[10px] font-medium text-pink-400">미통과</span>
|
|
<Handle
|
|
type="source"
|
|
position={Position.Right}
|
|
id="false"
|
|
className="!h-2.5 !w-2.5 !border-2 !border-pink-500 !bg-zinc-900"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
});
|
|
|
|
ConditionNode.displayName = "ConditionNode";
|