"use client"; import React from "react"; import { Card, CardContent } from "@/components/ui/card"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Database, Filter, Zap, CheckCircle, XCircle, Edit } from "lucide-react"; import { DataConnectionState } from "../types/redesigned"; interface DataflowVisualizationProps { state: Partial & { dataflowActions?: Array<{ actionType: string; targetTable?: string; name?: string; fieldMappings?: any[]; }>; }; onEdit: (step: "source" | "conditions" | "actions") => void; } /** * ๐ŸŽฏ Sankey ๋‹ค์ด์–ด๊ทธ๋žจ ์Šคํƒ€์ผ ๋ฐ์ดํ„ฐ ํ๋ฆ„ ์‹œ๊ฐํ™” */ export const DataflowVisualization: React.FC = ({ state, onEdit }) => { const { fromTable, toTable, controlConditions = [], dataflowActions = [], fromColumns = [], toColumns = [] } = state; // ์ƒํƒœ ๊ณ„์‚ฐ const hasSource = !!fromTable; const hasConditions = controlConditions.length > 0; const hasActions = dataflowActions.length > 0; const isComplete = hasSource && hasActions; // ํ•„๋“œ๋ช…์„ ๋ผ๋ฒจ๋ช…์œผ๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ํ•จ์ˆ˜ const getFieldLabel = (fieldName: string) => { // fromColumns์™€ toColumns์—์„œ ํ•ด๋‹น ํ•„๋“œ ์ฐพ๊ธฐ const allColumns = [...fromColumns, ...toColumns]; const column = allColumns.find((col) => col.columnName === fieldName); return column?.displayName || column?.labelKo || fieldName; }; // ํ…Œ์ด๋ธ”๋ช…์„ ๋ผ๋ฒจ๋ช…์œผ๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ํ•จ์ˆ˜ const getTableLabel = (tableName: string) => { // fromTable ๋˜๋Š” toTable์˜ ๋ผ๋ฒจ ๋ฐ˜ํ™˜ if (fromTable?.tableName === tableName) { return fromTable?.tableLabel || fromTable?.displayName || tableName; } if (toTable?.tableName === tableName) { return toTable?.tableLabel || toTable?.displayName || tableName; } return tableName; }; // ์•ก์…˜ ๊ทธ๋ฃน๋ณ„๋กœ ๋Œ€ํ‘œ ์•ก์…˜ 1๊ฐœ์”ฉ๋งŒ ํ‘œ์‹œ const actionGroups = dataflowActions.reduce( (acc, action, index) => { // ๊ฐ ์•ก์…˜์„ ๊ฐœ๋ณ„ ๊ทธ๋ฃน์œผ๋กœ ์ฒ˜๋ฆฌ (์‹ค์ œ๋กœ๋Š” actionGroups์—์„œ ์˜จ ๊ฒƒ) const groupKey = `group_${index}`; acc[groupKey] = [action]; return acc; }, {} as Record, ); return (
{/* ํ—ค๋” */}

์ „์ฒด ๋ฐ์ดํ„ฐ ํ๋ฆ„์„ ํ•œ๋ˆˆ์— ํ™•์ธ

{isComplete ? "โœ… ์„ค์ • ์™„๋ฃŒ" : "โš ๏ธ ์„ค์ • ํ•„์š”"}
{/* Sankey ๋‹ค์ด์–ด๊ทธ๋žจ */}
{/* ์—ฐ๊ฒฐ์„  ๋ ˆ์ด์–ด */} {/* ์†Œ์Šค โ†’ ์กฐ๊ฑด ์„  */} {hasSource && } {/* ์กฐ๊ฑด โ†’ ์•ก์…˜๋“ค ์„  (์—ฌ๋Ÿฌ ๊ฐœ) */} {hasConditions && hasActions && Object.keys(actionGroups).map((groupKey, index) => { const totalActions = Object.keys(actionGroups).length; const startY = 50; // ์กฐ๊ฑด ๋…ธ๋“œ ์ค‘์•™ // ์•ก์…˜์ด ์—ฌ๋Ÿฌ ๊ฐœ๋ฉด ์œ„์—์„œ ์•„๋ž˜๋กœ ๋ถ„์‚ฐ const endY = totalActions > 1 ? 30 + (index * 40) / (totalActions - 1) // 30%~70% ์‚ฌ์ด์— ๋ถ„์‚ฐ : 50; // ์•ก์…˜์ด 1๊ฐœ๋ฉด ์ค‘์•™ return ( ); })}
{/* 1. ์†Œ์Šค ๋…ธ๋“œ */}
onEdit("source")} >
๋ฐ์ดํ„ฐ ์†Œ์Šค
{hasSource ? (

{fromTable.tableLabel || fromTable.displayName || fromTable.tableName}

{(fromTable.tableLabel || fromTable.displayName) && (

({fromTable.tableName})

)}
) : (

๋ฏธ์„ค์ •

)}
{/* 2. ์กฐ๊ฑด ๋…ธ๋“œ (์ค‘์•™) */}
onEdit("conditions")} >
์กฐ๊ฑด ๊ฒ€์ฆ
{hasConditions ? (
{/* ์‹ค์ œ ์กฐ๊ฑด๋“ค ํ‘œ์‹œ */}
{controlConditions.slice(0, 3).map((condition, index) => (

{index > 0 && ( {condition.logicalOperator || "AND"} )} {getFieldLabel(condition.field)}{" "} {condition.operator}{" "} {condition.value}

))} {controlConditions.length > 3 && (

์™ธ {controlConditions.length - 3}๊ฐœ...

)}
์ถฉ์กฑ โ†’ ์‹คํ–‰
๋ถˆ๋งŒ์กฑ โ†’ ์ค‘๋‹จ
) : (

์กฐ๊ฑด ์—†์Œ (ํ•ญ์ƒ ์‹คํ–‰)

)}
{/* 3. ์•ก์…˜ ๋…ธ๋“œ๋“ค (์šฐ์ธก) */}
{hasActions ? (
{Object.entries(actionGroups).map(([groupKey, actions]) => ( ))}
) : (

์•ก์…˜ ๋ฏธ์„ค์ •

)}
{/* ์กฐ๊ฑด ๋ถˆ๋งŒ์กฑ ์‹œ ์ค‘๋‹จ ํ‘œ์‹œ (ํ•˜๋‹จ) */} {hasConditions && (
์กฐ๊ฑด ๋ถˆ๋งŒ์กฑ โ†’ ์‹คํ–‰ ์ค‘๋‹จ
)}
{/* ํ†ต๊ณ„ ์š”์•ฝ */}

์†Œ์Šค

{hasSource ? 1 : 0}

์กฐ๊ฑด

{controlConditions.length}

์•ก์…˜

{dataflowActions.length}

); }; // ์•ก์…˜ ํ”Œ๋กœ์šฐ ์นด๋“œ ์ปดํฌ๋„ŒํŠธ interface ActionFlowCardProps { type: string; actions: Array<{ actionType: string; targetTable?: string; name?: string; fieldMappings?: any[]; }>; getTableLabel: (tableName: string) => string; } const ActionFlowCard: React.FC = ({ type, actions, getTableLabel }) => { const actionColors = { insert: { bg: "bg-primary/10", border: "border-primary/30", text: "text-primary", icon: "text-primary" }, update: { bg: "bg-success/10", border: "border-success/30", text: "text-success", icon: "text-success" }, delete: { bg: "bg-destructive/10", border: "border-destructive/30", text: "text-destructive", icon: "text-destructive" }, upsert: { bg: "bg-primary/10", border: "border-primary/30", text: "text-primary", icon: "text-primary" }, }; const colors = actionColors[type as keyof typeof actionColors] || actionColors.insert; const action = actions[0]; // ๊ทธ๋ฃน๋‹น 1๊ฐœ๋งŒ ํ‘œ์‹œ const displayName = action.targetTable ? getTableLabel(action.targetTable) : action.name || "์•ก์…˜"; const isTableLabel = action.targetTable && getTableLabel(action.targetTable) !== action.targetTable; return (
{type.toUpperCase()}
{displayName}
{isTableLabel && action.targetTable && ( ({action.targetTable}) )}
); };