2025-09-29 12:17:10 +09:00
|
|
|
"use client";
|
|
|
|
|
|
2025-12-12 18:28:58 +09:00
|
|
|
import React, { useState, useEffect, useCallback } from "react";
|
2025-09-29 12:17:10 +09:00
|
|
|
import { Label } from "@/components/ui/label";
|
|
|
|
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
|
|
|
|
import { Switch } from "@/components/ui/switch";
|
|
|
|
|
import { Separator } from "@/components/ui/separator";
|
2025-12-12 18:28:58 +09:00
|
|
|
import { Button } from "@/components/ui/button";
|
|
|
|
|
import { Card } from "@/components/ui/card";
|
|
|
|
|
import { Badge } from "@/components/ui/badge";
|
|
|
|
|
import { Settings, Clock, Info, Workflow, Plus, Trash2, GripVertical, ChevronUp, ChevronDown } from "lucide-react";
|
feat: 노드 기반 데이터 플로우 시스템 구현
- 노드 에디터 UI 구현 (React Flow 기반)
- TableSource, DataTransform, INSERT, UPDATE, DELETE, UPSERT 노드
- 드래그앤드롭 노드 추가 및 연결
- 속성 패널을 통한 노드 설정
- 실시간 필드 라벨 표시 (column_labels 테이블 연동)
- 데이터 변환 노드 (DataTransform) 기능
- EXPLODE: 구분자로 1개 행 → 여러 행 확장
- UPPERCASE, LOWERCASE, TRIM, CONCAT, SPLIT, REPLACE 등 12가지 변환 타입
- In-place 변환 지원 (타겟 필드 생략 시 소스 필드 덮어쓰기)
- 변환된 필드가 하위 액션 노드에 자동 전달
- 노드 플로우 실행 엔진
- 위상 정렬을 통한 노드 실행 순서 결정
- 레벨별 병렬 실행 (Promise.allSettled)
- 부분 실패 허용 (한 노드 실패 시 연결된 하위 노드만 스킵)
- 트랜잭션 기반 안전한 데이터 처리
- UPSERT 액션 로직 구현
- DB 제약 조건 없이 SELECT → UPDATE or INSERT 방식
- 복합 충돌 키 지원 (예: sales_no + product_name)
- 파라미터 인덱스 정확한 매핑
- 데이터 소스 자동 감지
- 테이블 선택 데이터 (selectedRowsData) 자동 주입
- 폼 입력 데이터 (formData) 자동 주입
- TableSource 노드가 외부 데이터 우선 사용
- 버튼 컴포넌트 통합
- 기존 관계 실행 + 새 노드 플로우 실행 하이브리드 지원
- 노드 플로우 선택 UI 추가
- API 클라이언트 통합 (Axios)
- 개발 문서 작성
- 노드 기반 제어 시스템 개선 계획
- 노드 연결 규칙 설계
- 노드 실행 엔진 설계
- 노드 구조 개선안
- 버튼 통합 분석
2025-10-02 16:22:29 +09:00
|
|
|
import { ComponentData } from "@/types/screen";
|
|
|
|
|
import { getNodeFlows, NodeFlow } from "@/lib/api/nodeFlows";
|
2025-09-29 12:17:10 +09:00
|
|
|
|
|
|
|
|
interface ImprovedButtonControlConfigPanelProps {
|
|
|
|
|
component: ComponentData;
|
|
|
|
|
onUpdateProperty: (path: string, value: any) => void;
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-12 18:28:58 +09:00
|
|
|
// 다중 제어 설정 인터페이스
|
|
|
|
|
interface FlowControlConfig {
|
|
|
|
|
id: string;
|
|
|
|
|
flowId: number;
|
|
|
|
|
flowName: string;
|
|
|
|
|
executionTiming: "before" | "after" | "replace";
|
|
|
|
|
order: number;
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-29 12:17:10 +09:00
|
|
|
/**
|
2025-12-12 18:28:58 +09:00
|
|
|
* 🔥 다중 제어 지원 버튼 설정 패널
|
feat: 노드 기반 데이터 플로우 시스템 구현
- 노드 에디터 UI 구현 (React Flow 기반)
- TableSource, DataTransform, INSERT, UPDATE, DELETE, UPSERT 노드
- 드래그앤드롭 노드 추가 및 연결
- 속성 패널을 통한 노드 설정
- 실시간 필드 라벨 표시 (column_labels 테이블 연동)
- 데이터 변환 노드 (DataTransform) 기능
- EXPLODE: 구분자로 1개 행 → 여러 행 확장
- UPPERCASE, LOWERCASE, TRIM, CONCAT, SPLIT, REPLACE 등 12가지 변환 타입
- In-place 변환 지원 (타겟 필드 생략 시 소스 필드 덮어쓰기)
- 변환된 필드가 하위 액션 노드에 자동 전달
- 노드 플로우 실행 엔진
- 위상 정렬을 통한 노드 실행 순서 결정
- 레벨별 병렬 실행 (Promise.allSettled)
- 부분 실패 허용 (한 노드 실패 시 연결된 하위 노드만 스킵)
- 트랜잭션 기반 안전한 데이터 처리
- UPSERT 액션 로직 구현
- DB 제약 조건 없이 SELECT → UPDATE or INSERT 방식
- 복합 충돌 키 지원 (예: sales_no + product_name)
- 파라미터 인덱스 정확한 매핑
- 데이터 소스 자동 감지
- 테이블 선택 데이터 (selectedRowsData) 자동 주입
- 폼 입력 데이터 (formData) 자동 주입
- TableSource 노드가 외부 데이터 우선 사용
- 버튼 컴포넌트 통합
- 기존 관계 실행 + 새 노드 플로우 실행 하이브리드 지원
- 노드 플로우 선택 UI 추가
- API 클라이언트 통합 (Axios)
- 개발 문서 작성
- 노드 기반 제어 시스템 개선 계획
- 노드 연결 규칙 설계
- 노드 실행 엔진 설계
- 노드 구조 개선안
- 버튼 통합 분석
2025-10-02 16:22:29 +09:00
|
|
|
*
|
2025-12-12 18:28:58 +09:00
|
|
|
* 기능:
|
|
|
|
|
* - 여러 개의 노드 플로우 선택 및 순서 지정
|
|
|
|
|
* - 각 플로우별 실행 타이밍 설정
|
|
|
|
|
* - 드래그앤드롭 또는 버튼으로 순서 변경
|
2025-09-29 12:17:10 +09:00
|
|
|
*/
|
|
|
|
|
export const ImprovedButtonControlConfigPanel: React.FC<ImprovedButtonControlConfigPanelProps> = ({
|
|
|
|
|
component,
|
|
|
|
|
onUpdateProperty,
|
|
|
|
|
}) => {
|
|
|
|
|
const config = component.webTypeConfig || {};
|
|
|
|
|
const dataflowConfig = config.dataflowConfig || {};
|
|
|
|
|
|
2025-12-12 18:28:58 +09:00
|
|
|
// 다중 제어 설정 (배열)
|
|
|
|
|
const flowControls: FlowControlConfig[] = dataflowConfig.flowControls || [];
|
|
|
|
|
|
2025-09-29 12:17:10 +09:00
|
|
|
// 🔥 State 관리
|
feat: 노드 기반 데이터 플로우 시스템 구현
- 노드 에디터 UI 구현 (React Flow 기반)
- TableSource, DataTransform, INSERT, UPDATE, DELETE, UPSERT 노드
- 드래그앤드롭 노드 추가 및 연결
- 속성 패널을 통한 노드 설정
- 실시간 필드 라벨 표시 (column_labels 테이블 연동)
- 데이터 변환 노드 (DataTransform) 기능
- EXPLODE: 구분자로 1개 행 → 여러 행 확장
- UPPERCASE, LOWERCASE, TRIM, CONCAT, SPLIT, REPLACE 등 12가지 변환 타입
- In-place 변환 지원 (타겟 필드 생략 시 소스 필드 덮어쓰기)
- 변환된 필드가 하위 액션 노드에 자동 전달
- 노드 플로우 실행 엔진
- 위상 정렬을 통한 노드 실행 순서 결정
- 레벨별 병렬 실행 (Promise.allSettled)
- 부분 실패 허용 (한 노드 실패 시 연결된 하위 노드만 스킵)
- 트랜잭션 기반 안전한 데이터 처리
- UPSERT 액션 로직 구현
- DB 제약 조건 없이 SELECT → UPDATE or INSERT 방식
- 복합 충돌 키 지원 (예: sales_no + product_name)
- 파라미터 인덱스 정확한 매핑
- 데이터 소스 자동 감지
- 테이블 선택 데이터 (selectedRowsData) 자동 주입
- 폼 입력 데이터 (formData) 자동 주입
- TableSource 노드가 외부 데이터 우선 사용
- 버튼 컴포넌트 통합
- 기존 관계 실행 + 새 노드 플로우 실행 하이브리드 지원
- 노드 플로우 선택 UI 추가
- API 클라이언트 통합 (Axios)
- 개발 문서 작성
- 노드 기반 제어 시스템 개선 계획
- 노드 연결 규칙 설계
- 노드 실행 엔진 설계
- 노드 구조 개선안
- 버튼 통합 분석
2025-10-02 16:22:29 +09:00
|
|
|
const [flows, setFlows] = useState<NodeFlow[]>([]);
|
2025-09-29 12:17:10 +09:00
|
|
|
const [loading, setLoading] = useState(false);
|
|
|
|
|
|
feat: 노드 기반 데이터 플로우 시스템 구현
- 노드 에디터 UI 구현 (React Flow 기반)
- TableSource, DataTransform, INSERT, UPDATE, DELETE, UPSERT 노드
- 드래그앤드롭 노드 추가 및 연결
- 속성 패널을 통한 노드 설정
- 실시간 필드 라벨 표시 (column_labels 테이블 연동)
- 데이터 변환 노드 (DataTransform) 기능
- EXPLODE: 구분자로 1개 행 → 여러 행 확장
- UPPERCASE, LOWERCASE, TRIM, CONCAT, SPLIT, REPLACE 등 12가지 변환 타입
- In-place 변환 지원 (타겟 필드 생략 시 소스 필드 덮어쓰기)
- 변환된 필드가 하위 액션 노드에 자동 전달
- 노드 플로우 실행 엔진
- 위상 정렬을 통한 노드 실행 순서 결정
- 레벨별 병렬 실행 (Promise.allSettled)
- 부분 실패 허용 (한 노드 실패 시 연결된 하위 노드만 스킵)
- 트랜잭션 기반 안전한 데이터 처리
- UPSERT 액션 로직 구현
- DB 제약 조건 없이 SELECT → UPDATE or INSERT 방식
- 복합 충돌 키 지원 (예: sales_no + product_name)
- 파라미터 인덱스 정확한 매핑
- 데이터 소스 자동 감지
- 테이블 선택 데이터 (selectedRowsData) 자동 주입
- 폼 입력 데이터 (formData) 자동 주입
- TableSource 노드가 외부 데이터 우선 사용
- 버튼 컴포넌트 통합
- 기존 관계 실행 + 새 노드 플로우 실행 하이브리드 지원
- 노드 플로우 선택 UI 추가
- API 클라이언트 통합 (Axios)
- 개발 문서 작성
- 노드 기반 제어 시스템 개선 계획
- 노드 연결 규칙 설계
- 노드 실행 엔진 설계
- 노드 구조 개선안
- 버튼 통합 분석
2025-10-02 16:22:29 +09:00
|
|
|
// 🔥 플로우 목록 로딩
|
2025-09-29 12:17:10 +09:00
|
|
|
useEffect(() => {
|
|
|
|
|
if (config.enableDataflowControl) {
|
feat: 노드 기반 데이터 플로우 시스템 구현
- 노드 에디터 UI 구현 (React Flow 기반)
- TableSource, DataTransform, INSERT, UPDATE, DELETE, UPSERT 노드
- 드래그앤드롭 노드 추가 및 연결
- 속성 패널을 통한 노드 설정
- 실시간 필드 라벨 표시 (column_labels 테이블 연동)
- 데이터 변환 노드 (DataTransform) 기능
- EXPLODE: 구분자로 1개 행 → 여러 행 확장
- UPPERCASE, LOWERCASE, TRIM, CONCAT, SPLIT, REPLACE 등 12가지 변환 타입
- In-place 변환 지원 (타겟 필드 생략 시 소스 필드 덮어쓰기)
- 변환된 필드가 하위 액션 노드에 자동 전달
- 노드 플로우 실행 엔진
- 위상 정렬을 통한 노드 실행 순서 결정
- 레벨별 병렬 실행 (Promise.allSettled)
- 부분 실패 허용 (한 노드 실패 시 연결된 하위 노드만 스킵)
- 트랜잭션 기반 안전한 데이터 처리
- UPSERT 액션 로직 구현
- DB 제약 조건 없이 SELECT → UPDATE or INSERT 방식
- 복합 충돌 키 지원 (예: sales_no + product_name)
- 파라미터 인덱스 정확한 매핑
- 데이터 소스 자동 감지
- 테이블 선택 데이터 (selectedRowsData) 자동 주입
- 폼 입력 데이터 (formData) 자동 주입
- TableSource 노드가 외부 데이터 우선 사용
- 버튼 컴포넌트 통합
- 기존 관계 실행 + 새 노드 플로우 실행 하이브리드 지원
- 노드 플로우 선택 UI 추가
- API 클라이언트 통합 (Axios)
- 개발 문서 작성
- 노드 기반 제어 시스템 개선 계획
- 노드 연결 규칙 설계
- 노드 실행 엔진 설계
- 노드 구조 개선안
- 버튼 통합 분석
2025-10-02 16:22:29 +09:00
|
|
|
loadFlows();
|
2025-09-29 12:17:10 +09:00
|
|
|
}
|
|
|
|
|
}, [config.enableDataflowControl]);
|
|
|
|
|
|
|
|
|
|
/**
|
feat: 노드 기반 데이터 플로우 시스템 구현
- 노드 에디터 UI 구현 (React Flow 기반)
- TableSource, DataTransform, INSERT, UPDATE, DELETE, UPSERT 노드
- 드래그앤드롭 노드 추가 및 연결
- 속성 패널을 통한 노드 설정
- 실시간 필드 라벨 표시 (column_labels 테이블 연동)
- 데이터 변환 노드 (DataTransform) 기능
- EXPLODE: 구분자로 1개 행 → 여러 행 확장
- UPPERCASE, LOWERCASE, TRIM, CONCAT, SPLIT, REPLACE 등 12가지 변환 타입
- In-place 변환 지원 (타겟 필드 생략 시 소스 필드 덮어쓰기)
- 변환된 필드가 하위 액션 노드에 자동 전달
- 노드 플로우 실행 엔진
- 위상 정렬을 통한 노드 실행 순서 결정
- 레벨별 병렬 실행 (Promise.allSettled)
- 부분 실패 허용 (한 노드 실패 시 연결된 하위 노드만 스킵)
- 트랜잭션 기반 안전한 데이터 처리
- UPSERT 액션 로직 구현
- DB 제약 조건 없이 SELECT → UPDATE or INSERT 방식
- 복합 충돌 키 지원 (예: sales_no + product_name)
- 파라미터 인덱스 정확한 매핑
- 데이터 소스 자동 감지
- 테이블 선택 데이터 (selectedRowsData) 자동 주입
- 폼 입력 데이터 (formData) 자동 주입
- TableSource 노드가 외부 데이터 우선 사용
- 버튼 컴포넌트 통합
- 기존 관계 실행 + 새 노드 플로우 실행 하이브리드 지원
- 노드 플로우 선택 UI 추가
- API 클라이언트 통합 (Axios)
- 개발 문서 작성
- 노드 기반 제어 시스템 개선 계획
- 노드 연결 규칙 설계
- 노드 실행 엔진 설계
- 노드 구조 개선안
- 버튼 통합 분석
2025-10-02 16:22:29 +09:00
|
|
|
* 🔥 플로우 목록 로드
|
2025-09-29 12:17:10 +09:00
|
|
|
*/
|
feat: 노드 기반 데이터 플로우 시스템 구현
- 노드 에디터 UI 구현 (React Flow 기반)
- TableSource, DataTransform, INSERT, UPDATE, DELETE, UPSERT 노드
- 드래그앤드롭 노드 추가 및 연결
- 속성 패널을 통한 노드 설정
- 실시간 필드 라벨 표시 (column_labels 테이블 연동)
- 데이터 변환 노드 (DataTransform) 기능
- EXPLODE: 구분자로 1개 행 → 여러 행 확장
- UPPERCASE, LOWERCASE, TRIM, CONCAT, SPLIT, REPLACE 등 12가지 변환 타입
- In-place 변환 지원 (타겟 필드 생략 시 소스 필드 덮어쓰기)
- 변환된 필드가 하위 액션 노드에 자동 전달
- 노드 플로우 실행 엔진
- 위상 정렬을 통한 노드 실행 순서 결정
- 레벨별 병렬 실행 (Promise.allSettled)
- 부분 실패 허용 (한 노드 실패 시 연결된 하위 노드만 스킵)
- 트랜잭션 기반 안전한 데이터 처리
- UPSERT 액션 로직 구현
- DB 제약 조건 없이 SELECT → UPDATE or INSERT 방식
- 복합 충돌 키 지원 (예: sales_no + product_name)
- 파라미터 인덱스 정확한 매핑
- 데이터 소스 자동 감지
- 테이블 선택 데이터 (selectedRowsData) 자동 주입
- 폼 입력 데이터 (formData) 자동 주입
- TableSource 노드가 외부 데이터 우선 사용
- 버튼 컴포넌트 통합
- 기존 관계 실행 + 새 노드 플로우 실행 하이브리드 지원
- 노드 플로우 선택 UI 추가
- API 클라이언트 통합 (Axios)
- 개발 문서 작성
- 노드 기반 제어 시스템 개선 계획
- 노드 연결 규칙 설계
- 노드 실행 엔진 설계
- 노드 구조 개선안
- 버튼 통합 분석
2025-10-02 16:22:29 +09:00
|
|
|
const loadFlows = async () => {
|
2025-09-29 12:17:10 +09:00
|
|
|
try {
|
|
|
|
|
setLoading(true);
|
feat: 노드 기반 데이터 플로우 시스템 구현
- 노드 에디터 UI 구현 (React Flow 기반)
- TableSource, DataTransform, INSERT, UPDATE, DELETE, UPSERT 노드
- 드래그앤드롭 노드 추가 및 연결
- 속성 패널을 통한 노드 설정
- 실시간 필드 라벨 표시 (column_labels 테이블 연동)
- 데이터 변환 노드 (DataTransform) 기능
- EXPLODE: 구분자로 1개 행 → 여러 행 확장
- UPPERCASE, LOWERCASE, TRIM, CONCAT, SPLIT, REPLACE 등 12가지 변환 타입
- In-place 변환 지원 (타겟 필드 생략 시 소스 필드 덮어쓰기)
- 변환된 필드가 하위 액션 노드에 자동 전달
- 노드 플로우 실행 엔진
- 위상 정렬을 통한 노드 실행 순서 결정
- 레벨별 병렬 실행 (Promise.allSettled)
- 부분 실패 허용 (한 노드 실패 시 연결된 하위 노드만 스킵)
- 트랜잭션 기반 안전한 데이터 처리
- UPSERT 액션 로직 구현
- DB 제약 조건 없이 SELECT → UPDATE or INSERT 방식
- 복합 충돌 키 지원 (예: sales_no + product_name)
- 파라미터 인덱스 정확한 매핑
- 데이터 소스 자동 감지
- 테이블 선택 데이터 (selectedRowsData) 자동 주입
- 폼 입력 데이터 (formData) 자동 주입
- TableSource 노드가 외부 데이터 우선 사용
- 버튼 컴포넌트 통합
- 기존 관계 실행 + 새 노드 플로우 실행 하이브리드 지원
- 노드 플로우 선택 UI 추가
- API 클라이언트 통합 (Axios)
- 개발 문서 작성
- 노드 기반 제어 시스템 개선 계획
- 노드 연결 규칙 설계
- 노드 실행 엔진 설계
- 노드 구조 개선안
- 버튼 통합 분석
2025-10-02 16:22:29 +09:00
|
|
|
console.log("🔍 플로우 목록 로딩...");
|
2025-09-29 12:17:10 +09:00
|
|
|
|
feat: 노드 기반 데이터 플로우 시스템 구현
- 노드 에디터 UI 구현 (React Flow 기반)
- TableSource, DataTransform, INSERT, UPDATE, DELETE, UPSERT 노드
- 드래그앤드롭 노드 추가 및 연결
- 속성 패널을 통한 노드 설정
- 실시간 필드 라벨 표시 (column_labels 테이블 연동)
- 데이터 변환 노드 (DataTransform) 기능
- EXPLODE: 구분자로 1개 행 → 여러 행 확장
- UPPERCASE, LOWERCASE, TRIM, CONCAT, SPLIT, REPLACE 등 12가지 변환 타입
- In-place 변환 지원 (타겟 필드 생략 시 소스 필드 덮어쓰기)
- 변환된 필드가 하위 액션 노드에 자동 전달
- 노드 플로우 실행 엔진
- 위상 정렬을 통한 노드 실행 순서 결정
- 레벨별 병렬 실행 (Promise.allSettled)
- 부분 실패 허용 (한 노드 실패 시 연결된 하위 노드만 스킵)
- 트랜잭션 기반 안전한 데이터 처리
- UPSERT 액션 로직 구현
- DB 제약 조건 없이 SELECT → UPDATE or INSERT 방식
- 복합 충돌 키 지원 (예: sales_no + product_name)
- 파라미터 인덱스 정확한 매핑
- 데이터 소스 자동 감지
- 테이블 선택 데이터 (selectedRowsData) 자동 주입
- 폼 입력 데이터 (formData) 자동 주입
- TableSource 노드가 외부 데이터 우선 사용
- 버튼 컴포넌트 통합
- 기존 관계 실행 + 새 노드 플로우 실행 하이브리드 지원
- 노드 플로우 선택 UI 추가
- API 클라이언트 통합 (Axios)
- 개발 문서 작성
- 노드 기반 제어 시스템 개선 계획
- 노드 연결 규칙 설계
- 노드 실행 엔진 설계
- 노드 구조 개선안
- 버튼 통합 분석
2025-10-02 16:22:29 +09:00
|
|
|
const flowList = await getNodeFlows();
|
|
|
|
|
setFlows(flowList);
|
|
|
|
|
console.log(`✅ 플로우 ${flowList.length}개 로딩 완료`);
|
2025-09-29 12:17:10 +09:00
|
|
|
} catch (error) {
|
feat: 노드 기반 데이터 플로우 시스템 구현
- 노드 에디터 UI 구현 (React Flow 기반)
- TableSource, DataTransform, INSERT, UPDATE, DELETE, UPSERT 노드
- 드래그앤드롭 노드 추가 및 연결
- 속성 패널을 통한 노드 설정
- 실시간 필드 라벨 표시 (column_labels 테이블 연동)
- 데이터 변환 노드 (DataTransform) 기능
- EXPLODE: 구분자로 1개 행 → 여러 행 확장
- UPPERCASE, LOWERCASE, TRIM, CONCAT, SPLIT, REPLACE 등 12가지 변환 타입
- In-place 변환 지원 (타겟 필드 생략 시 소스 필드 덮어쓰기)
- 변환된 필드가 하위 액션 노드에 자동 전달
- 노드 플로우 실행 엔진
- 위상 정렬을 통한 노드 실행 순서 결정
- 레벨별 병렬 실행 (Promise.allSettled)
- 부분 실패 허용 (한 노드 실패 시 연결된 하위 노드만 스킵)
- 트랜잭션 기반 안전한 데이터 처리
- UPSERT 액션 로직 구현
- DB 제약 조건 없이 SELECT → UPDATE or INSERT 방식
- 복합 충돌 키 지원 (예: sales_no + product_name)
- 파라미터 인덱스 정확한 매핑
- 데이터 소스 자동 감지
- 테이블 선택 데이터 (selectedRowsData) 자동 주입
- 폼 입력 데이터 (formData) 자동 주입
- TableSource 노드가 외부 데이터 우선 사용
- 버튼 컴포넌트 통합
- 기존 관계 실행 + 새 노드 플로우 실행 하이브리드 지원
- 노드 플로우 선택 UI 추가
- API 클라이언트 통합 (Axios)
- 개발 문서 작성
- 노드 기반 제어 시스템 개선 계획
- 노드 연결 규칙 설계
- 노드 실행 엔진 설계
- 노드 구조 개선안
- 버튼 통합 분석
2025-10-02 16:22:29 +09:00
|
|
|
console.error("❌ 플로우 목록 로딩 실패:", error);
|
|
|
|
|
setFlows([]);
|
2025-09-29 12:17:10 +09:00
|
|
|
} finally {
|
|
|
|
|
setLoading(false);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
2025-12-12 18:28:58 +09:00
|
|
|
* 🔥 제어 추가
|
2025-09-29 12:17:10 +09:00
|
|
|
*/
|
2025-12-12 18:28:58 +09:00
|
|
|
const handleAddControl = useCallback(() => {
|
|
|
|
|
const newControl: FlowControlConfig = {
|
|
|
|
|
id: `control_${Date.now()}`,
|
|
|
|
|
flowId: 0,
|
|
|
|
|
flowName: "",
|
|
|
|
|
executionTiming: "after",
|
|
|
|
|
order: flowControls.length + 1,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const updatedControls = [...flowControls, newControl];
|
|
|
|
|
updateFlowControls(updatedControls);
|
|
|
|
|
}, [flowControls]);
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 🔥 제어 삭제
|
|
|
|
|
*/
|
|
|
|
|
const handleRemoveControl = useCallback(
|
|
|
|
|
(controlId: string) => {
|
|
|
|
|
const updatedControls = flowControls
|
|
|
|
|
.filter((c) => c.id !== controlId)
|
|
|
|
|
.map((c, index) => ({ ...c, order: index + 1 }));
|
|
|
|
|
updateFlowControls(updatedControls);
|
|
|
|
|
},
|
|
|
|
|
[flowControls],
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 🔥 제어 플로우 선택
|
|
|
|
|
*/
|
|
|
|
|
const handleFlowSelect = useCallback(
|
|
|
|
|
(controlId: string, flowId: string) => {
|
|
|
|
|
const selectedFlow = flows.find((f) => f.flowId.toString() === flowId);
|
|
|
|
|
if (selectedFlow) {
|
|
|
|
|
const updatedControls = flowControls.map((c) =>
|
|
|
|
|
c.id === controlId ? { ...c, flowId: selectedFlow.flowId, flowName: selectedFlow.flowName } : c,
|
|
|
|
|
);
|
|
|
|
|
updateFlowControls(updatedControls);
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
[flows, flowControls],
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 🔥 실행 타이밍 변경
|
|
|
|
|
*/
|
|
|
|
|
const handleTimingChange = useCallback(
|
|
|
|
|
(controlId: string, timing: "before" | "after" | "replace") => {
|
|
|
|
|
const updatedControls = flowControls.map((c) => (c.id === controlId ? { ...c, executionTiming: timing } : c));
|
|
|
|
|
updateFlowControls(updatedControls);
|
|
|
|
|
},
|
|
|
|
|
[flowControls],
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 🔥 순서 위로 이동
|
|
|
|
|
*/
|
|
|
|
|
const handleMoveUp = useCallback(
|
|
|
|
|
(controlId: string) => {
|
|
|
|
|
const index = flowControls.findIndex((c) => c.id === controlId);
|
|
|
|
|
if (index > 0) {
|
|
|
|
|
const updatedControls = [...flowControls];
|
|
|
|
|
[updatedControls[index - 1], updatedControls[index]] = [updatedControls[index], updatedControls[index - 1]];
|
|
|
|
|
// 순서 번호 재정렬
|
|
|
|
|
updatedControls.forEach((c, i) => (c.order = i + 1));
|
|
|
|
|
updateFlowControls(updatedControls);
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
[flowControls],
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 🔥 순서 아래로 이동
|
|
|
|
|
*/
|
|
|
|
|
const handleMoveDown = useCallback(
|
|
|
|
|
(controlId: string) => {
|
|
|
|
|
const index = flowControls.findIndex((c) => c.id === controlId);
|
|
|
|
|
if (index < flowControls.length - 1) {
|
|
|
|
|
const updatedControls = [...flowControls];
|
|
|
|
|
[updatedControls[index], updatedControls[index + 1]] = [updatedControls[index + 1], updatedControls[index]];
|
|
|
|
|
// 순서 번호 재정렬
|
|
|
|
|
updatedControls.forEach((c, i) => (c.order = i + 1));
|
|
|
|
|
updateFlowControls(updatedControls);
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
[flowControls],
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 🔥 제어 목록 업데이트 (백엔드 호환성 유지)
|
|
|
|
|
*/
|
|
|
|
|
const updateFlowControls = (controls: FlowControlConfig[]) => {
|
|
|
|
|
// 첫 번째 제어를 기존 형식으로도 저장 (하위 호환성)
|
|
|
|
|
const firstValidControl = controls.find((c) => c.flowId > 0);
|
|
|
|
|
|
|
|
|
|
onUpdateProperty("webTypeConfig.dataflowConfig", {
|
|
|
|
|
...dataflowConfig,
|
|
|
|
|
// 기존 형식 (하위 호환성)
|
|
|
|
|
selectedDiagramId: firstValidControl?.flowId || null,
|
|
|
|
|
selectedRelationshipId: null,
|
|
|
|
|
flowConfig: firstValidControl
|
|
|
|
|
? {
|
|
|
|
|
flowId: firstValidControl.flowId,
|
|
|
|
|
flowName: firstValidControl.flowName,
|
|
|
|
|
executionTiming: firstValidControl.executionTiming,
|
|
|
|
|
contextData: {},
|
|
|
|
|
}
|
|
|
|
|
: null,
|
|
|
|
|
// 새로운 다중 제어 형식
|
|
|
|
|
flowControls: controls,
|
|
|
|
|
});
|
2025-09-29 12:17:10 +09:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<div className="space-y-6">
|
|
|
|
|
{/* 🔥 제어관리 활성화 스위치 */}
|
2025-10-27 16:40:59 +09:00
|
|
|
<div className="flex items-center justify-between">
|
2025-09-29 12:17:10 +09:00
|
|
|
<div className="flex items-center space-x-2">
|
2025-10-08 09:45:59 +09:00
|
|
|
<Settings className="text-primary h-4 w-4" />
|
2025-09-29 12:17:10 +09:00
|
|
|
<div>
|
2025-10-27 16:40:59 +09:00
|
|
|
<Label className="text-sm font-medium">제어 기능</Label>
|
2025-10-08 09:45:59 +09:00
|
|
|
<p className="text-muted-foreground mt-1 text-xs">버튼 클릭 시 추가 작업을 자동으로 실행합니다</p>
|
2025-09-29 12:17:10 +09:00
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<Switch
|
|
|
|
|
checked={config.enableDataflowControl || false}
|
|
|
|
|
onCheckedChange={(checked) => onUpdateProperty("webTypeConfig.enableDataflowControl", checked)}
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{/* 🔥 제어관리가 활성화된 경우에만 설정 표시 */}
|
|
|
|
|
{config.enableDataflowControl && (
|
2025-10-27 16:40:59 +09:00
|
|
|
<div className="space-y-4">
|
2025-12-12 18:28:58 +09:00
|
|
|
{/* 제어 목록 헤더 */}
|
|
|
|
|
<div className="flex items-center justify-between">
|
|
|
|
|
<div className="flex items-center space-x-2">
|
|
|
|
|
<Workflow className="h-4 w-4 text-green-600" />
|
|
|
|
|
<Label>제어 목록 (순서대로 실행)</Label>
|
|
|
|
|
</div>
|
|
|
|
|
<Button variant="outline" size="sm" onClick={handleAddControl} className="h-8">
|
|
|
|
|
<Plus className="mr-1 h-3 w-3" />
|
|
|
|
|
제어 추가
|
|
|
|
|
</Button>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{/* 제어 목록 */}
|
|
|
|
|
{flowControls.length === 0 ? (
|
|
|
|
|
<div className="rounded-md border border-dashed p-6 text-center">
|
|
|
|
|
<Workflow className="mx-auto h-8 w-8 text-gray-400" />
|
|
|
|
|
<p className="mt-2 text-sm text-gray-500">등록된 제어가 없습니다</p>
|
|
|
|
|
<Button variant="outline" size="sm" onClick={handleAddControl} className="mt-3">
|
|
|
|
|
<Plus className="mr-1 h-3 w-3" />첫 번째 제어 추가
|
|
|
|
|
</Button>
|
|
|
|
|
</div>
|
|
|
|
|
) : (
|
|
|
|
|
<div className="space-y-2">
|
|
|
|
|
{flowControls.map((control, index) => (
|
|
|
|
|
<FlowControlItem
|
|
|
|
|
key={control.id}
|
|
|
|
|
control={control}
|
|
|
|
|
flows={flows}
|
|
|
|
|
loading={loading}
|
|
|
|
|
isFirst={index === 0}
|
|
|
|
|
isLast={index === flowControls.length - 1}
|
|
|
|
|
onFlowSelect={(flowId) => handleFlowSelect(control.id, flowId)}
|
|
|
|
|
onTimingChange={(timing) => handleTimingChange(control.id, timing)}
|
|
|
|
|
onMoveUp={() => handleMoveUp(control.id)}
|
|
|
|
|
onMoveDown={() => handleMoveDown(control.id)}
|
|
|
|
|
onRemove={() => handleRemoveControl(control.id)}
|
|
|
|
|
/>
|
|
|
|
|
))}
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
{/* 안내 메시지 */}
|
|
|
|
|
{flowControls.length > 0 && (
|
|
|
|
|
<div className="rounded bg-blue-50 p-3">
|
|
|
|
|
<div className="flex items-start space-x-2">
|
|
|
|
|
<Info className="mt-0.5 h-4 w-4 text-blue-600" />
|
|
|
|
|
<div className="text-xs text-blue-800">
|
|
|
|
|
<p className="font-medium">다중 제어 실행 정보:</p>
|
|
|
|
|
<p className="mt-1">• 제어는 위에서 아래 순서대로 순차 실행됩니다</p>
|
|
|
|
|
<p>• 각 제어는 독립 트랜잭션으로 처리됩니다</p>
|
|
|
|
|
<p>• 이전 제어 실패 시 다음 제어는 실행되지 않습니다</p>
|
2025-10-27 16:40:59 +09:00
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
2025-09-29 12:17:10 +09:00
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
2025-12-12 18:28:58 +09:00
|
|
|
* 🔥 개별 제어 아이템 컴포넌트
|
2025-09-29 12:17:10 +09:00
|
|
|
*/
|
2025-12-12 18:28:58 +09:00
|
|
|
const FlowControlItem: React.FC<{
|
|
|
|
|
control: FlowControlConfig;
|
feat: 노드 기반 데이터 플로우 시스템 구현
- 노드 에디터 UI 구현 (React Flow 기반)
- TableSource, DataTransform, INSERT, UPDATE, DELETE, UPSERT 노드
- 드래그앤드롭 노드 추가 및 연결
- 속성 패널을 통한 노드 설정
- 실시간 필드 라벨 표시 (column_labels 테이블 연동)
- 데이터 변환 노드 (DataTransform) 기능
- EXPLODE: 구분자로 1개 행 → 여러 행 확장
- UPPERCASE, LOWERCASE, TRIM, CONCAT, SPLIT, REPLACE 등 12가지 변환 타입
- In-place 변환 지원 (타겟 필드 생략 시 소스 필드 덮어쓰기)
- 변환된 필드가 하위 액션 노드에 자동 전달
- 노드 플로우 실행 엔진
- 위상 정렬을 통한 노드 실행 순서 결정
- 레벨별 병렬 실행 (Promise.allSettled)
- 부분 실패 허용 (한 노드 실패 시 연결된 하위 노드만 스킵)
- 트랜잭션 기반 안전한 데이터 처리
- UPSERT 액션 로직 구현
- DB 제약 조건 없이 SELECT → UPDATE or INSERT 방식
- 복합 충돌 키 지원 (예: sales_no + product_name)
- 파라미터 인덱스 정확한 매핑
- 데이터 소스 자동 감지
- 테이블 선택 데이터 (selectedRowsData) 자동 주입
- 폼 입력 데이터 (formData) 자동 주입
- TableSource 노드가 외부 데이터 우선 사용
- 버튼 컴포넌트 통합
- 기존 관계 실행 + 새 노드 플로우 실행 하이브리드 지원
- 노드 플로우 선택 UI 추가
- API 클라이언트 통합 (Axios)
- 개발 문서 작성
- 노드 기반 제어 시스템 개선 계획
- 노드 연결 규칙 설계
- 노드 실행 엔진 설계
- 노드 구조 개선안
- 버튼 통합 분석
2025-10-02 16:22:29 +09:00
|
|
|
flows: NodeFlow[];
|
2025-09-29 12:17:10 +09:00
|
|
|
loading: boolean;
|
2025-12-12 18:28:58 +09:00
|
|
|
isFirst: boolean;
|
|
|
|
|
isLast: boolean;
|
|
|
|
|
onFlowSelect: (flowId: string) => void;
|
|
|
|
|
onTimingChange: (timing: "before" | "after" | "replace") => void;
|
|
|
|
|
onMoveUp: () => void;
|
|
|
|
|
onMoveDown: () => void;
|
|
|
|
|
onRemove: () => void;
|
|
|
|
|
}> = ({ control, flows, loading, isFirst, isLast, onFlowSelect, onTimingChange, onMoveUp, onMoveDown, onRemove }) => {
|
2025-09-29 12:17:10 +09:00
|
|
|
return (
|
2025-12-12 18:28:58 +09:00
|
|
|
<Card className="p-3">
|
|
|
|
|
<div className="flex items-start gap-2">
|
|
|
|
|
{/* 순서 표시 및 이동 버튼 */}
|
|
|
|
|
<div className="flex flex-col items-center gap-1">
|
|
|
|
|
<Badge variant="secondary" className="h-6 w-6 justify-center rounded-full p-0 text-xs">
|
|
|
|
|
{control.order}
|
|
|
|
|
</Badge>
|
|
|
|
|
<div className="flex flex-col">
|
|
|
|
|
<Button variant="ghost" size="icon" className="h-5 w-5" onClick={onMoveUp} disabled={isFirst}>
|
|
|
|
|
<ChevronUp className="h-3 w-3" />
|
|
|
|
|
</Button>
|
|
|
|
|
<Button variant="ghost" size="icon" className="h-5 w-5" onClick={onMoveDown} disabled={isLast}>
|
|
|
|
|
<ChevronDown className="h-3 w-3" />
|
|
|
|
|
</Button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
feat: 노드 기반 데이터 플로우 시스템 구현
- 노드 에디터 UI 구현 (React Flow 기반)
- TableSource, DataTransform, INSERT, UPDATE, DELETE, UPSERT 노드
- 드래그앤드롭 노드 추가 및 연결
- 속성 패널을 통한 노드 설정
- 실시간 필드 라벨 표시 (column_labels 테이블 연동)
- 데이터 변환 노드 (DataTransform) 기능
- EXPLODE: 구분자로 1개 행 → 여러 행 확장
- UPPERCASE, LOWERCASE, TRIM, CONCAT, SPLIT, REPLACE 등 12가지 변환 타입
- In-place 변환 지원 (타겟 필드 생략 시 소스 필드 덮어쓰기)
- 변환된 필드가 하위 액션 노드에 자동 전달
- 노드 플로우 실행 엔진
- 위상 정렬을 통한 노드 실행 순서 결정
- 레벨별 병렬 실행 (Promise.allSettled)
- 부분 실패 허용 (한 노드 실패 시 연결된 하위 노드만 스킵)
- 트랜잭션 기반 안전한 데이터 처리
- UPSERT 액션 로직 구현
- DB 제약 조건 없이 SELECT → UPDATE or INSERT 방식
- 복합 충돌 키 지원 (예: sales_no + product_name)
- 파라미터 인덱스 정확한 매핑
- 데이터 소스 자동 감지
- 테이블 선택 데이터 (selectedRowsData) 자동 주입
- 폼 입력 데이터 (formData) 자동 주입
- TableSource 노드가 외부 데이터 우선 사용
- 버튼 컴포넌트 통합
- 기존 관계 실행 + 새 노드 플로우 실행 하이브리드 지원
- 노드 플로우 선택 UI 추가
- API 클라이언트 통합 (Axios)
- 개발 문서 작성
- 노드 기반 제어 시스템 개선 계획
- 노드 연결 규칙 설계
- 노드 실행 엔진 설계
- 노드 구조 개선안
- 버튼 통합 분석
2025-10-02 16:22:29 +09:00
|
|
|
|
2025-12-12 18:28:58 +09:00
|
|
|
{/* 플로우 선택 및 설정 */}
|
|
|
|
|
<div className="flex-1 space-y-2">
|
|
|
|
|
{/* 플로우 선택 */}
|
|
|
|
|
<Select value={control.flowId > 0 ? control.flowId.toString() : ""} onValueChange={onFlowSelect}>
|
|
|
|
|
<SelectTrigger className="h-8 text-xs">
|
|
|
|
|
<SelectValue placeholder="플로우를 선택하세요" />
|
|
|
|
|
</SelectTrigger>
|
|
|
|
|
<SelectContent>
|
|
|
|
|
{loading ? (
|
|
|
|
|
<div className="p-2 text-center text-xs text-gray-500">로딩 중...</div>
|
|
|
|
|
) : flows.length === 0 ? (
|
|
|
|
|
<div className="p-2 text-center text-xs text-gray-500">플로우가 없습니다</div>
|
|
|
|
|
) : (
|
|
|
|
|
flows.map((flow) => (
|
|
|
|
|
<SelectItem key={flow.flowId} value={flow.flowId.toString()}>
|
|
|
|
|
<span className="text-xs">{flow.flowName}</span>
|
|
|
|
|
</SelectItem>
|
|
|
|
|
))
|
|
|
|
|
)}
|
|
|
|
|
</SelectContent>
|
|
|
|
|
</Select>
|
|
|
|
|
|
|
|
|
|
{/* 실행 타이밍 */}
|
|
|
|
|
<Select value={control.executionTiming} onValueChange={onTimingChange}>
|
|
|
|
|
<SelectTrigger className="h-8 text-xs">
|
|
|
|
|
<SelectValue />
|
|
|
|
|
</SelectTrigger>
|
|
|
|
|
<SelectContent>
|
|
|
|
|
<SelectItem value="before">
|
|
|
|
|
<span className="text-xs">Before (사전 실행)</span>
|
2025-09-29 12:17:10 +09:00
|
|
|
</SelectItem>
|
2025-12-12 18:28:58 +09:00
|
|
|
<SelectItem value="after">
|
|
|
|
|
<span className="text-xs">After (사후 실행)</span>
|
|
|
|
|
</SelectItem>
|
|
|
|
|
<SelectItem value="replace">
|
|
|
|
|
<span className="text-xs">Replace (대체 실행)</span>
|
|
|
|
|
</SelectItem>
|
|
|
|
|
</SelectContent>
|
|
|
|
|
</Select>
|
|
|
|
|
</div>
|
2025-09-29 12:17:10 +09:00
|
|
|
|
2025-12-12 18:28:58 +09:00
|
|
|
{/* 삭제 버튼 */}
|
|
|
|
|
<Button
|
|
|
|
|
variant="ghost"
|
|
|
|
|
size="icon"
|
|
|
|
|
className="h-8 w-8 text-red-500 hover:bg-red-50 hover:text-red-600"
|
|
|
|
|
onClick={onRemove}
|
|
|
|
|
>
|
|
|
|
|
<Trash2 className="h-4 w-4" />
|
|
|
|
|
</Button>
|
2025-09-29 12:17:10 +09:00
|
|
|
</div>
|
2025-12-12 18:28:58 +09:00
|
|
|
</Card>
|
2025-09-29 12:17:10 +09:00
|
|
|
);
|
|
|
|
|
};
|