ERP-node/frontend/components/dataflow/connection/ActionConditionsSection.tsx

203 lines
7.7 KiB
TypeScript
Raw Normal View History

2025-09-16 15:43:18 +09:00
"use client";
import React from "react";
import { Button } from "@/components/ui/button";
import { Plus, Trash2 } from "lucide-react";
import { ColumnInfo } from "@/lib/api/dataflow";
import { DataSaveSettings } from "@/types/connectionTypes";
import { generateConditionId } from "@/utils/connectionUtils";
import { useActionConditionHelpers } from "@/hooks/useConditionManager";
import { ActionConditionRenderer } from "./ActionConditionRenderer";
interface ActionConditionsSectionProps {
action: DataSaveSettings["actions"][0];
actionIndex: number;
settings: DataSaveSettings;
onSettingsChange: (settings: DataSaveSettings) => void;
fromTableColumns: ColumnInfo[];
toTableColumns: ColumnInfo[];
fromTableName?: string;
toTableName?: string;
2025-09-16 15:43:18 +09:00
}
export const ActionConditionsSection: React.FC<ActionConditionsSectionProps> = ({
action,
actionIndex,
settings,
onSettingsChange,
fromTableColumns,
toTableColumns,
fromTableName,
toTableName,
2025-09-16 15:43:18 +09:00
}) => {
const { addActionGroupStart, addActionGroupEnd, getActionCurrentGroupLevel } = useActionConditionHelpers();
// INSERT가 아닌 액션 타입인지 확인
const isConditionRequired = action.actionType !== "insert";
// 유효한 조건이 있는지 확인 (group-start, group-end만 있는 경우 제외)
const hasValidConditions =
action.conditions?.some((condition) => {
if (condition.type !== "condition") return false;
if (!condition.field || !condition.operator) return false;
// value가 null, undefined, 빈 문자열이면 유효하지 않음
const value = condition.value;
if (value === null || value === undefined || value === "") return false;
return true;
}) || false;
2025-09-16 15:43:18 +09:00
const addActionCondition = () => {
const newActions = [...settings.actions];
if (!newActions[actionIndex].conditions) {
newActions[actionIndex].conditions = [];
}
const currentConditions = newActions[actionIndex].conditions || [];
const newCondition = {
id: generateConditionId(),
type: "condition" as const,
field: "",
operator: "=" as const,
value: "",
dataType: "string",
tableType: undefined, // 사용자가 직접 선택하도록
2025-09-16 15:43:18 +09:00
// 첫 번째 조건이 아니고, 바로 앞이 group-start가 아니면 logicalOperator 추가
...(currentConditions.length > 0 &&
currentConditions[currentConditions.length - 1]?.type !== "group-start" && {
logicalOperator: "AND" as const,
}),
};
newActions[actionIndex].conditions = [...currentConditions, newCondition];
onSettingsChange({ ...settings, actions: newActions });
};
const clearAllConditions = () => {
const newActions = [...settings.actions];
newActions[actionIndex].conditions = [];
onSettingsChange({ ...settings, actions: newActions });
};
return (
<div className="mt-3">
<details className="group">
<summary
className={`flex cursor-pointer items-center justify-between rounded border p-2 text-xs font-medium hover:bg-gray-50 hover:text-gray-900 ${
isConditionRequired && !hasValidConditions
? "border-red-300 bg-destructive/10 text-red-700"
: "border-gray-200 text-gray-700"
}`}
>
2025-09-16 15:43:18 +09:00
<div className="flex items-center gap-2">
🔍
{isConditionRequired ? (
<span className="rounded bg-destructive/20 px-1 py-0.5 text-xs font-semibold text-red-700"></span>
) : (
<span className="text-gray-500">()</span>
)}
2025-09-16 15:43:18 +09:00
{action.conditions && action.conditions.length > 0 && (
<span className="rounded-full bg-primary/20 px-2 py-0.5 text-xs text-blue-700">
2025-09-16 15:43:18 +09:00
{action.conditions.length}
</span>
)}
{isConditionRequired && !hasValidConditions && (
<span className="rounded bg-destructive/20 px-1 py-0.5 text-xs text-destructive"> </span>
)}
2025-09-16 15:43:18 +09:00
</div>
{action.conditions && action.conditions.length > 0 && (
<Button
size="sm"
variant="ghost"
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
clearAllConditions();
}}
className="h-5 w-5 p-0 text-red-500 hover:text-red-700"
title="조건 모두 삭제"
>
<Trash2 className="h-3 w-3" />
</Button>
)}
</summary>
<div className="mt-2 space-y-2 border-l-2 border-gray-100 pl-4">
<div className="mb-2 flex items-center justify-between">
<div className="flex gap-1">
<Button size="sm" variant="outline" onClick={addActionCondition} className="h-6 text-xs">
<Plus className="mr-1 h-2 w-2" />
</Button>
<Button
size="sm"
variant="outline"
onClick={() => addActionGroupStart(actionIndex, settings, onSettingsChange)}
className="h-6 text-xs"
>
(
</Button>
<Button
size="sm"
variant="outline"
onClick={() => addActionGroupEnd(actionIndex, settings, onSettingsChange)}
className="h-6 text-xs"
>
)
</Button>
</div>
</div>
{/* 조건이 없을 때 안내 메시지 */}
{(!action.conditions || action.conditions.length === 0) && (
<div
className={`rounded border p-3 text-xs ${
isConditionRequired
? "border-destructive/20 bg-destructive/10 text-red-700"
: "border-gray-200 bg-gray-50 text-muted-foreground"
}`}
>
{isConditionRequired ? (
<div className="flex items-start gap-2">
<span className="text-red-500"></span>
<div>
<div className="font-medium"> </div>
<div className="mt-1">
{action.actionType.toUpperCase()} .
<br />
<strong>, , </strong> .
<br />
: user_name = '관리자우저' AND status = 'active'
</div>
</div>
</div>
) : (
<div> . INSERT .</div>
)}
</div>
)}
2025-09-16 15:43:18 +09:00
{action.conditions && action.conditions.length > 0 && (
<div className="space-y-2">
{action.conditions.map((condition, condIndex) => (
<div key={`action-${actionIndex}-condition-${condition.id}`}>
<ActionConditionRenderer
condition={condition}
condIndex={condIndex}
actionIndex={actionIndex}
settings={settings}
onSettingsChange={onSettingsChange}
fromTableColumns={fromTableColumns}
toTableColumns={toTableColumns}
fromTableName={fromTableName}
toTableName={toTableName}
2025-09-16 15:43:18 +09:00
getActionCurrentGroupLevel={getActionCurrentGroupLevel}
/>
</div>
))}
</div>
)}
</div>
</details>
</div>
);
};