diff --git a/frontend/app/(main)/screens/[screenId]/page.tsx b/frontend/app/(main)/screens/[screenId]/page.tsx index afa93a69..cd01afd4 100644 --- a/frontend/app/(main)/screens/[screenId]/page.tsx +++ b/frontend/app/(main)/screens/[screenId]/page.tsx @@ -27,6 +27,10 @@ export default function ScreenViewPage() { // 테이블에서 선택된 행 데이터 (버튼 액션에 전달) const [selectedRowsData, setSelectedRowsData] = useState([]); + // 플로우에서 선택된 데이터 (버튼 액션에 전달) + const [flowSelectedData, setFlowSelectedData] = useState([]); + const [flowSelectedStepId, setFlowSelectedStepId] = useState(null); + // 테이블 새로고침을 위한 키 (값이 변경되면 테이블이 리렌더링됨) const [tableRefreshKey, setTableRefreshKey] = useState(0); @@ -228,6 +232,18 @@ export default function ScreenViewPage() { console.log("🔍 화면에서 선택된 행 데이터:", selectedData); setSelectedRowsData(selectedData); }} + flowSelectedData={flowSelectedData} + flowSelectedStepId={flowSelectedStepId} + onFlowSelectedDataChange={(selectedData: any[], stepId: number | null) => { + console.log("🔍 [page.tsx] 플로우 선택된 데이터 받음:", { + dataCount: selectedData.length, + selectedData, + stepId, + }); + setFlowSelectedData(selectedData); + setFlowSelectedStepId(stepId); + console.log("🔍 [page.tsx] 상태 업데이트 완료"); + }} refreshKey={tableRefreshKey} onRefresh={() => { console.log("🔄 테이블 새로고침 요청됨"); diff --git a/frontend/components/screen/InteractiveScreenViewerDynamic.tsx b/frontend/components/screen/InteractiveScreenViewerDynamic.tsx index 2765e57e..e334cc54 100644 --- a/frontend/components/screen/InteractiveScreenViewerDynamic.tsx +++ b/frontend/components/screen/InteractiveScreenViewerDynamic.tsx @@ -51,6 +51,10 @@ export const InteractiveScreenViewerDynamic: React.FC([]); + // 플로우에서 선택된 데이터 (버튼 액션에 전달) + const [flowSelectedData, setFlowSelectedData] = useState([]); + const [flowSelectedStepId, setFlowSelectedStepId] = useState(null); + // 팝업 화면 상태 const [popupScreen, setPopupScreen] = useState<{ screenId: number; @@ -194,6 +198,13 @@ export const InteractiveScreenViewerDynamic: React.FC { + console.log("🔍 플로우에서 선택된 데이터:", { selectedData, stepId }); + setFlowSelectedData(selectedData); + setFlowSelectedStepId(stepId); + }} onRefresh={() => { console.log("🔄 버튼에서 테이블 새로고침 요청됨"); // 테이블 컴포넌트는 자체적으로 loadData 호출 diff --git a/frontend/components/screen/OptimizedButtonComponent.tsx b/frontend/components/screen/OptimizedButtonComponent.tsx index d0f1ac8e..a30ce66a 100644 --- a/frontend/components/screen/OptimizedButtonComponent.tsx +++ b/frontend/components/screen/OptimizedButtonComponent.tsx @@ -22,6 +22,10 @@ interface OptimizedButtonProps { formData?: Record; companyCode?: string; disabled?: boolean; + selectedRows?: any[]; + selectedRowsData?: any[]; + flowSelectedData?: any[]; + flowSelectedStepId?: number | null; } /** @@ -41,6 +45,10 @@ export const OptimizedButtonComponent: React.FC = ({ formData = {}, companyCode = "DEFAULT", disabled = false, + selectedRows = [], + selectedRowsData = [], + flowSelectedData = [], + flowSelectedStepId = null, }) => { // 🔥 상태 관리 const [isExecuting, setIsExecuting] = useState(false); @@ -79,6 +87,8 @@ export const OptimizedButtonComponent: React.FC = ({ formData, selectedRows: selectedRows || [], selectedRowsData: selectedRowsData || [], + flowSelectedData: flowSelectedData || [], + flowSelectedStepId: flowSelectedStepId, controlDataSource: config?.dataflowConfig?.controlDataSource || "form", buttonId: component.id, componentData: component, diff --git a/frontend/components/screen/RealtimePreview.tsx b/frontend/components/screen/RealtimePreview.tsx index e0d6f978..1c841828 100644 --- a/frontend/components/screen/RealtimePreview.tsx +++ b/frontend/components/screen/RealtimePreview.tsx @@ -60,6 +60,8 @@ interface RealtimePreviewProps { onDragEnd?: () => void; onGroupToggle?: (groupId: string) => void; // 그룹 접기/펼치기 children?: React.ReactNode; // 그룹 내 자식 컴포넌트들 + // 플로우 선택 데이터 전달용 + onFlowSelectedDataChange?: (selectedData: any[], stepId: number | null) => void; } // 영역 레이아웃에 따른 아이콘 반환 @@ -218,6 +220,7 @@ export const RealtimePreviewDynamic: React.FC = ({ onDragEnd, onGroupToggle, children, + onFlowSelectedDataChange, }) => { const { user } = useAuth(); const { type, id, position, size, style = {} } = component; @@ -494,7 +497,10 @@ export const RealtimePreviewDynamic: React.FC = ({ return (
- +
); })()} diff --git a/frontend/components/screen/RealtimePreviewDynamic.tsx b/frontend/components/screen/RealtimePreviewDynamic.tsx index 67e802f0..6ce31bfd 100644 --- a/frontend/components/screen/RealtimePreviewDynamic.tsx +++ b/frontend/components/screen/RealtimePreviewDynamic.tsx @@ -40,6 +40,9 @@ interface RealtimePreviewProps { tableName?: string; selectedRowsData?: any[]; onSelectedRowsChange?: (selectedRows: any[], selectedRowsData: any[]) => void; + flowSelectedData?: any[]; + flowSelectedStepId?: number | null; + onFlowSelectedDataChange?: (selectedData: any[], stepId: number | null) => void; refreshKey?: number; onRefresh?: () => void; @@ -93,6 +96,9 @@ export const RealtimePreviewDynamic: React.FC = ({ tableName, selectedRowsData, onSelectedRowsChange, + flowSelectedData, + flowSelectedStepId, + onFlowSelectedDataChange, refreshKey, onRefresh, formData, @@ -288,6 +294,9 @@ export const RealtimePreviewDynamic: React.FC = ({ tableName={tableName} selectedRowsData={selectedRowsData} onSelectedRowsChange={onSelectedRowsChange} + flowSelectedData={flowSelectedData} + flowSelectedStepId={flowSelectedStepId} + onFlowSelectedDataChange={onFlowSelectedDataChange} refreshKey={refreshKey} onRefresh={onRefresh} formData={formData} diff --git a/frontend/components/screen/widgets/FlowWidget.tsx b/frontend/components/screen/widgets/FlowWidget.tsx index 152e233e..6ef38e90 100644 --- a/frontend/components/screen/widgets/FlowWidget.tsx +++ b/frontend/components/screen/widgets/FlowWidget.tsx @@ -31,9 +31,10 @@ import { interface FlowWidgetProps { component: FlowComponent; onStepClick?: (stepId: number, stepName: string) => void; + onSelectedDataChange?: (selectedData: any[], stepId: number | null) => void; } -export function FlowWidget({ component, onStepClick }: FlowWidgetProps) { +export function FlowWidget({ component, onStepClick, onSelectedDataChange }: FlowWidgetProps) { const [flowData, setFlowData] = useState(null); const [steps, setSteps] = useState([]); const [stepCounts, setStepCounts] = useState>({}); @@ -156,6 +157,8 @@ export function FlowWidget({ component, onStepClick }: FlowWidgetProps) { setStepData([]); setStepDataColumns([]); setSelectedRows(new Set()); + // 선택 초기화 전달 + onSelectedDataChange?.([], null); return; } @@ -163,6 +166,8 @@ export function FlowWidget({ component, onStepClick }: FlowWidgetProps) { setSelectedStepId(stepId); setStepDataLoading(true); setSelectedRows(new Set()); + // 선택 초기화 전달 + onSelectedDataChange?.([], stepId); try { const response = await getStepDataList(flowId!, stepId, 1, 100); @@ -197,15 +202,32 @@ export function FlowWidget({ component, onStepClick }: FlowWidgetProps) { newSelected.add(rowIndex); } setSelectedRows(newSelected); + + // 선택된 데이터를 상위로 전달 + const selectedData = Array.from(newSelected).map((index) => stepData[index]); + console.log("🌊 FlowWidget - 체크박스 토글, 상위로 전달:", { + rowIndex, + newSelectedSize: newSelected.size, + selectedData, + selectedStepId, + hasCallback: !!onSelectedDataChange, + }); + onSelectedDataChange?.(selectedData, selectedStepId); }; // 전체 선택/해제 const toggleAllRows = () => { + let newSelected: Set; if (selectedRows.size === stepData.length) { - setSelectedRows(new Set()); + newSelected = new Set(); } else { - setSelectedRows(new Set(stepData.map((_, index) => index))); + newSelected = new Set(stepData.map((_, index) => index)); } + setSelectedRows(newSelected); + + // 선택된 데이터를 상위로 전달 + const selectedData = Array.from(newSelected).map((index) => stepData[index]); + onSelectedDataChange?.(selectedData, selectedStepId); }; // 현재 단계에서 가능한 다음 단계들 찾기 @@ -264,6 +286,8 @@ export function FlowWidget({ component, onStepClick }: FlowWidgetProps) { // 선택 초기화 setSelectedNextStepId(null); setSelectedRows(new Set()); + // 선택 초기화 전달 + onSelectedDataChange?.([], selectedStepId); // 데이터 새로고침 await handleStepClick(selectedStepId, steps.find((s) => s.id === selectedStepId)?.stepName || ""); diff --git a/frontend/lib/registry/DynamicComponentRenderer.tsx b/frontend/lib/registry/DynamicComponentRenderer.tsx index adc07129..970a74a0 100644 --- a/frontend/lib/registry/DynamicComponentRenderer.tsx +++ b/frontend/lib/registry/DynamicComponentRenderer.tsx @@ -30,6 +30,10 @@ export interface ComponentRenderer { selectedRows?: any[]; selectedRowsData?: any[]; onSelectedRowsChange?: (selectedRows: any[], selectedRowsData: any[]) => void; + // 플로우 선택된 데이터 정보 (플로우 위젯 선택 액션용) + flowSelectedData?: any[]; + flowSelectedStepId?: number | null; + onFlowSelectedDataChange?: (selectedData: any[], stepId: number | null) => void; // 테이블 새로고침 키 refreshKey?: number; // 편집 모드 @@ -95,6 +99,10 @@ export interface DynamicComponentRendererProps { selectedRows?: any[]; selectedRowsData?: any[]; onSelectedRowsChange?: (selectedRows: any[], selectedRowsData: any[]) => void; + // 플로우 선택된 데이터 정보 (플로우 위젯 선택 액션용) + flowSelectedData?: any[]; + flowSelectedStepId?: number | null; + onFlowSelectedDataChange?: (selectedData: any[], stepId: number | null) => void; // 테이블 새로고침 키 refreshKey?: number; // 편집 모드 @@ -174,6 +182,9 @@ export const DynamicComponentRenderer: React.FC = selectedRows, selectedRowsData, onSelectedRowsChange, + flowSelectedData, + flowSelectedStepId, + onFlowSelectedDataChange, refreshKey, onConfigChange, isPreview, @@ -289,6 +300,10 @@ export const DynamicComponentRenderer: React.FC = selectedRows, selectedRowsData, onSelectedRowsChange, + // 플로우 선택된 데이터 정보 전달 + flowSelectedData, + flowSelectedStepId, + onFlowSelectedDataChange, // 설정 변경 핸들러 전달 onConfigChange, refreshKey, @@ -299,16 +314,26 @@ export const DynamicComponentRenderer: React.FC = }; // 렌더러가 클래스인지 함수인지 확인 + console.log("🔍🔍 DynamicComponentRenderer - 렌더러 타입 확인:", { + componentType, + isFunction: typeof NewComponentRenderer === "function", + hasPrototype: !!NewComponentRenderer.prototype, + hasRenderMethod: !!NewComponentRenderer.prototype?.render, + rendererName: NewComponentRenderer.name, + }); + if ( typeof NewComponentRenderer === "function" && NewComponentRenderer.prototype && NewComponentRenderer.prototype.render ) { // 클래스 기반 렌더러 (AutoRegisteringComponentRenderer 상속) + console.log("✅ 클래스 기반 렌더러로 렌더링:", componentType); const rendererInstance = new NewComponentRenderer(rendererProps); return rendererInstance.render(); } else { // 함수형 컴포넌트 + console.log("✅ 함수형 컴포넌트로 렌더링:", componentType); return ; } } @@ -371,6 +396,10 @@ export const DynamicComponentRenderer: React.FC = selectedRows: props.selectedRows, selectedRowsData: props.selectedRowsData, onSelectedRowsChange: props.onSelectedRowsChange, + // 플로우 선택된 데이터 정보 전달 + flowSelectedData: props.flowSelectedData, + flowSelectedStepId: props.flowSelectedStepId, + onFlowSelectedDataChange: props.onFlowSelectedDataChange, refreshKey: props.refreshKey, // DOM 안전한 props들 ...safeLegacyProps, diff --git a/frontend/lib/registry/components/button-primary/ButtonPrimaryComponent.tsx b/frontend/lib/registry/components/button-primary/ButtonPrimaryComponent.tsx index e371f6bc..f77fd120 100644 --- a/frontend/lib/registry/components/button-primary/ButtonPrimaryComponent.tsx +++ b/frontend/lib/registry/components/button-primary/ButtonPrimaryComponent.tsx @@ -36,6 +36,10 @@ export interface ButtonPrimaryComponentProps extends ComponentRendererProps { // 테이블 선택된 행 정보 (다중 선택 액션용) selectedRows?: any[]; selectedRowsData?: any[]; + + // 플로우 선택된 데이터 정보 (플로우 위젯 선택 액션용) + flowSelectedData?: any[]; + flowSelectedStepId?: number | null; } /** @@ -62,6 +66,8 @@ export const ButtonPrimaryComponent: React.FC = ({ onClose, selectedRows, selectedRowsData, + flowSelectedData, + flowSelectedStepId, ...props }) => { console.log("🔵 ButtonPrimaryComponent 렌더링, 받은 props:", { @@ -69,6 +75,10 @@ export const ButtonPrimaryComponent: React.FC = ({ hasSelectedRowsData: !!selectedRowsData, selectedRowsDataLength: selectedRowsData?.length, selectedRowsData, + hasFlowSelectedData: !!flowSelectedData, + flowSelectedDataLength: flowSelectedData?.length, + flowSelectedData, + flowSelectedStepId, tableName, screenId, }); @@ -425,12 +435,19 @@ export const ButtonPrimaryComponent: React.FC = ({ // 테이블 선택된 행 정보 추가 selectedRows, selectedRowsData, + // 플로우 선택된 데이터 정보 추가 + flowSelectedData, + flowSelectedStepId, }; console.log("🔍 버튼 액션 실행 전 context 확인:", { hasSelectedRowsData: !!selectedRowsData, selectedRowsDataLength: selectedRowsData?.length, selectedRowsData, + hasFlowSelectedData: !!flowSelectedData, + flowSelectedDataLength: flowSelectedData?.length, + flowSelectedData, + flowSelectedStepId, tableName, screenId, formData, @@ -498,6 +515,8 @@ export const ButtonPrimaryComponent: React.FC = ({ selectedRows: _selectedRows, selectedRowsData: _selectedRowsData, onSelectedRowsChange: _onSelectedRowsChange, + flowSelectedData: _flowSelectedData, // 플로우 선택 데이터 필터링 + flowSelectedStepId: _flowSelectedStepId, // 플로우 선택 스텝 ID 필터링 originalData: _originalData, // 부분 업데이트용 원본 데이터 필터링 refreshKey: _refreshKey, // 필터링 추가 isInModal: _isInModal, // 필터링 추가 diff --git a/frontend/lib/registry/components/flow-widget/FlowWidgetRenderer.tsx b/frontend/lib/registry/components/flow-widget/FlowWidgetRenderer.tsx index d7c3edb7..f75510c5 100644 --- a/frontend/lib/registry/components/flow-widget/FlowWidgetRenderer.tsx +++ b/frontend/lib/registry/components/flow-widget/FlowWidgetRenderer.tsx @@ -4,16 +4,41 @@ import React from "react"; import { AutoRegisteringComponentRenderer } from "../../AutoRegisteringComponentRenderer"; import { FlowWidgetDefinition } from "./index"; import { FlowWidget } from "@/components/screen/widgets/FlowWidget"; +import { createComponentDefinition } from "../../utils/createComponentDefinition"; /** * FlowWidget 렌더러 * 자동 등록 시스템을 사용하여 컴포넌트를 레지스트리에 등록 */ export class FlowWidgetRenderer extends AutoRegisteringComponentRenderer { - static componentDefinition = FlowWidgetDefinition; + // 먼저 Definition에 컴포넌트 설정 + static componentDefinition = (() => { + // FlowWidgetRenderer를 컴포넌트로 설정 + const definition = { ...FlowWidgetDefinition, component: FlowWidgetRenderer }; + // createComponentDefinition으로 검증 및 처리 + return createComponentDefinition(definition as any); + })(); render(): React.ReactElement { - return ; + console.log("🎨🎨🎨 FlowWidgetRenderer - render 호출 시작 🎨🎨🎨"); + console.log("🎨 FlowWidgetRenderer - render 호출:", { + componentId: this.props.component.id, + hasOnFlowSelectedDataChange: !!this.props.onFlowSelectedDataChange, + onFlowSelectedDataChangeType: typeof this.props.onFlowSelectedDataChange, + allPropsKeys: Object.keys(this.props), + allPropsValues: this.props, + }); + console.log("🎨🎨🎨 FlowWidget에 전달할 prop:", { + hasComponent: !!this.props.component, + hasOnSelectedDataChange: !!this.props.onFlowSelectedDataChange, + }); + + return ( + + ); } } diff --git a/frontend/lib/registry/components/flow-widget/index.ts b/frontend/lib/registry/components/flow-widget/index.ts index 5c2d48ec..aea08017 100644 --- a/frontend/lib/registry/components/flow-widget/index.ts +++ b/frontend/lib/registry/components/flow-widget/index.ts @@ -1,22 +1,20 @@ "use client"; -import { createComponentDefinition } from "../../utils/createComponentDefinition"; import { ComponentCategory } from "@/types/component"; -import { FlowWidget } from "@/components/screen/widgets/FlowWidget"; import { FlowWidgetConfigPanel } from "@/components/screen/config-panels/FlowWidgetConfigPanel"; /** - * FlowWidget 컴포넌트 정의 + * FlowWidget 컴포넌트 정의 (컴포넌트는 FlowWidgetRenderer에서 설정됨) * 플로우 관리 시스템의 플로우를 화면에 표시 */ -export const FlowWidgetDefinition = createComponentDefinition({ +export const FlowWidgetDefinition = { id: "flow-widget", name: "플로우 위젯", nameEng: "Flow Widget", description: "플로우 관리 시스템의 플로우를 화면에 표시합니다", category: ComponentCategory.DISPLAY, - webType: "text", // 기본 웹타입 (필수) - component: FlowWidget, + webType: "text" as const, // 기본 웹타입 (필수) + component: null as any, // FlowWidgetRenderer에서 설정됩니다 defaultConfig: { flowId: undefined, flowName: undefined, @@ -30,11 +28,11 @@ export const FlowWidgetDefinition = createComponentDefinition({ gridColumnSpan: "full", // 전체 너비 사용 }, configPanel: FlowWidgetConfigPanel, - icon: "Workflow", + icon: "Workflow" as const, tags: ["플로우", "워크플로우", "프로세스", "상태"], version: "1.0.0", author: "개발팀", documentation: "", -}); +} as const; // 컴포넌트는 FlowWidgetRenderer에서 자동 등록됩니다 diff --git a/frontend/lib/utils/buttonActions.ts b/frontend/lib/utils/buttonActions.ts index abacc70b..5f29de70 100644 --- a/frontend/lib/utils/buttonActions.ts +++ b/frontend/lib/utils/buttonActions.ts @@ -63,6 +63,10 @@ export interface ButtonActionContext { // 테이블 선택된 행 정보 (다중 선택 액션용) selectedRows?: any[]; selectedRowsData?: any[]; + + // 플로우 선택된 데이터 정보 (플로우 위젯 선택 액션용) + flowSelectedData?: any[]; + flowSelectedStepId?: number | null; // 제어 실행을 위한 추가 정보 buttonId?: string; @@ -302,12 +306,23 @@ export class ButtonActionExecutor { * 삭제 액션 처리 */ private static async handleDelete(config: ButtonActionConfig, context: ButtonActionContext): Promise { - const { formData, tableName, screenId, selectedRowsData } = context; + const { formData, tableName, screenId, selectedRowsData, flowSelectedData } = context; try { - // 다중 선택된 행이 있는 경우 (테이블에서 체크박스로 선택) - if (selectedRowsData && selectedRowsData.length > 0) { - console.log(`다중 삭제 액션 실행: ${selectedRowsData.length}개 항목`, selectedRowsData); + // 플로우 선택 데이터 우선 사용 + let dataToDelete = flowSelectedData && flowSelectedData.length > 0 ? flowSelectedData : selectedRowsData; + + console.log("🔍 handleDelete - 데이터 소스 확인:", { + hasFlowSelectedData: !!(flowSelectedData && flowSelectedData.length > 0), + flowSelectedDataLength: flowSelectedData?.length || 0, + hasSelectedRowsData: !!(selectedRowsData && selectedRowsData.length > 0), + selectedRowsDataLength: selectedRowsData?.length || 0, + dataToDeleteLength: dataToDelete?.length || 0, + }); + + // 다중 선택된 데이터가 있는 경우 + if (dataToDelete && dataToDelete.length > 0) { + console.log(`다중 삭제 액션 실행: ${dataToDelete.length}개 항목`, dataToDelete); // 테이블의 기본키 조회 let primaryKeys: string[] = []; @@ -324,7 +339,7 @@ export class ButtonActionExecutor { } // 각 선택된 항목을 삭제 - for (const rowData of selectedRowsData) { + for (const rowData of dataToDelete) { let deleteId: any = null; // 1순위: 데이터베이스에서 조회한 기본키 사용 @@ -386,7 +401,7 @@ export class ButtonActionExecutor { } } - console.log(`✅ 다중 삭제 성공: ${selectedRowsData.length}개 항목`); + console.log(`✅ 다중 삭제 성공: ${dataToDelete.length}개 항목`); context.onRefresh?.(); // 테이블 새로고침 return true; } @@ -581,10 +596,21 @@ export class ButtonActionExecutor { * 편집 액션 처리 */ private static handleEdit(config: ButtonActionConfig, context: ButtonActionContext): boolean { - const { selectedRowsData } = context; + const { selectedRowsData, flowSelectedData } = context; - // 선택된 행이 없는 경우 - if (!selectedRowsData || selectedRowsData.length === 0) { + // 플로우 선택 데이터 우선 사용 + let dataToEdit = flowSelectedData && flowSelectedData.length > 0 ? flowSelectedData : selectedRowsData; + + console.log("🔍 handleEdit - 데이터 소스 확인:", { + hasFlowSelectedData: !!(flowSelectedData && flowSelectedData.length > 0), + flowSelectedDataLength: flowSelectedData?.length || 0, + hasSelectedRowsData: !!(selectedRowsData && selectedRowsData.length > 0), + selectedRowsDataLength: selectedRowsData?.length || 0, + dataToEditLength: dataToEdit?.length || 0, + }); + + // 선택된 데이터가 없는 경우 + if (!dataToEdit || dataToEdit.length === 0) { toast.error("수정할 항목을 선택해주세요."); return false; } @@ -595,15 +621,15 @@ export class ButtonActionExecutor { return false; } - console.log(`📝 편집 액션 실행: ${selectedRowsData.length}개 항목`, { - selectedRowsData, + console.log(`📝 편집 액션 실행: ${dataToEdit.length}개 항목`, { + dataToEdit, targetScreenId: config.targetScreenId, editMode: config.editMode, }); - if (selectedRowsData.length === 1) { + if (dataToEdit.length === 1) { // 단일 항목 편집 - const rowData = selectedRowsData[0]; + const rowData = dataToEdit[0]; console.log("📝 단일 항목 편집:", rowData); this.openEditForm(config, rowData, context); @@ -710,6 +736,8 @@ export class ButtonActionExecutor { formData: context.formData, selectedRows: context.selectedRows, selectedRowsData: context.selectedRowsData, + flowSelectedData: context.flowSelectedData, + flowSelectedStepId: context.flowSelectedStepId, config, }); @@ -739,7 +767,10 @@ export class ButtonActionExecutor { if (!controlDataSource) { // 설정이 없으면 자동 판단 - if (context.selectedRowsData && context.selectedRowsData.length > 0) { + if (context.flowSelectedData && context.flowSelectedData.length > 0) { + controlDataSource = "flow-selection"; + console.log("🔄 자동 판단: flow-selection 모드 사용"); + } else if (context.selectedRowsData && context.selectedRowsData.length > 0) { controlDataSource = "table-selection"; console.log("🔄 자동 판단: table-selection 모드 사용"); } else if (context.formData && Object.keys(context.formData).length > 0) { @@ -755,6 +786,8 @@ export class ButtonActionExecutor { formData: context.formData || {}, selectedRows: context.selectedRows || [], selectedRowsData: context.selectedRowsData || [], + flowSelectedData: context.flowSelectedData || [], + flowSelectedStepId: context.flowSelectedStepId, controlDataSource, }; @@ -779,11 +812,20 @@ export class ButtonActionExecutor { // 노드 플로우 실행 API 호출 (API 클라이언트 사용) const { executeNodeFlow } = await import("@/lib/api/nodeFlows"); - // 데이터 소스 준비: 선택된 행 또는 폼 데이터 + // 데이터 소스 준비: 플로우 선택, 테이블 선택, 또는 폼 데이터 let sourceData: any = null; let dataSourceType: string = "none"; - if (context.selectedRowsData && context.selectedRowsData.length > 0) { + if (context.flowSelectedData && context.flowSelectedData.length > 0) { + // 플로우에서 선택된 데이터 사용 + sourceData = context.flowSelectedData; + dataSourceType = "flow-selection"; + console.log("🌊 플로우 선택 데이터 사용:", { + stepId: context.flowSelectedStepId, + dataCount: sourceData.length, + sourceData, + }); + } else if (context.selectedRowsData && context.selectedRowsData.length > 0) { // 테이블에서 선택된 행 데이터 사용 sourceData = context.selectedRowsData; dataSourceType = "table-selection"; diff --git a/frontend/lib/utils/improvedButtonActionExecutor.ts b/frontend/lib/utils/improvedButtonActionExecutor.ts index 0e84956b..ddad52d5 100644 --- a/frontend/lib/utils/improvedButtonActionExecutor.ts +++ b/frontend/lib/utils/improvedButtonActionExecutor.ts @@ -23,6 +23,8 @@ export interface ButtonExecutionContext { formData?: Record; selectedRows?: any[]; tableData?: any[]; + flowSelectedData?: any[]; + flowSelectedStepId?: number | null; } export interface ExecutionResult { diff --git a/frontend/types/control-management.ts b/frontend/types/control-management.ts index 36760778..ca0e5c94 100644 --- a/frontend/types/control-management.ts +++ b/frontend/types/control-management.ts @@ -97,7 +97,7 @@ export interface ButtonDataflowConfig { /** * 제어 데이터 소스 타입 */ -export type ControlDataSource = "form" | "table-selection" | "both"; +export type ControlDataSource = "form" | "table-selection" | "flow-selection" | "both"; /** * 직접 제어 설정 @@ -376,13 +376,17 @@ export interface ExtendedControlContext { selectedRows?: unknown[]; selectedRowsData?: Record[]; + // 플로우 선택 데이터 + flowSelectedData?: Record[]; + flowSelectedStepId?: number | null; + // 제어 데이터 소스 타입 controlDataSource: ControlDataSource; // 기타 컨텍스트 - buttonId: string; + buttonId?: string; componentData?: unknown; - timestamp: string; + timestamp?: string; clickCount?: number; // 사용자 정보