219 lines
7.6 KiB
TypeScript
219 lines
7.6 KiB
TypeScript
"use client";
|
|
|
|
import React, { useState, useEffect } from "react";
|
|
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";
|
|
import { Settings, Clock, Info, Workflow } from "lucide-react";
|
|
import { ComponentData } from "@/types/screen";
|
|
import { getNodeFlows, NodeFlow } from "@/lib/api/nodeFlows";
|
|
|
|
interface ImprovedButtonControlConfigPanelProps {
|
|
component: ComponentData;
|
|
onUpdateProperty: (path: string, value: any) => void;
|
|
}
|
|
|
|
/**
|
|
* 🔥 단순화된 버튼 제어 설정 패널
|
|
*
|
|
* 노드 플로우 실행만 지원:
|
|
* - 플로우 선택 및 실행 타이밍 설정
|
|
*/
|
|
export const ImprovedButtonControlConfigPanel: React.FC<ImprovedButtonControlConfigPanelProps> = ({
|
|
component,
|
|
onUpdateProperty,
|
|
}) => {
|
|
const config = component.webTypeConfig || {};
|
|
const dataflowConfig = config.dataflowConfig || {};
|
|
|
|
// 🔥 State 관리
|
|
const [flows, setFlows] = useState<NodeFlow[]>([]);
|
|
const [loading, setLoading] = useState(false);
|
|
|
|
// 🔥 플로우 목록 로딩
|
|
useEffect(() => {
|
|
if (config.enableDataflowControl) {
|
|
loadFlows();
|
|
}
|
|
}, [config.enableDataflowControl]);
|
|
|
|
/**
|
|
* 🔥 플로우 목록 로드
|
|
*/
|
|
const loadFlows = async () => {
|
|
try {
|
|
setLoading(true);
|
|
console.log("🔍 플로우 목록 로딩...");
|
|
|
|
const flowList = await getNodeFlows();
|
|
setFlows(flowList);
|
|
console.log(`✅ 플로우 ${flowList.length}개 로딩 완료`);
|
|
} catch (error) {
|
|
console.error("❌ 플로우 목록 로딩 실패:", error);
|
|
setFlows([]);
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* 🔥 플로우 선택 핸들러
|
|
*/
|
|
const handleFlowSelect = (flowId: string) => {
|
|
const selectedFlow = flows.find((f) => f.flowId.toString() === flowId);
|
|
if (selectedFlow) {
|
|
onUpdateProperty("webTypeConfig.dataflowConfig.flowConfig", {
|
|
flowId: selectedFlow.flowId,
|
|
flowName: selectedFlow.flowName,
|
|
executionTiming: "before", // 기본값
|
|
contextData: {},
|
|
});
|
|
}
|
|
};
|
|
|
|
return (
|
|
<div className="space-y-6">
|
|
{/* 🔥 제어관리 활성화 스위치 */}
|
|
<div className="flex items-center justify-between">
|
|
<div className="flex items-center space-x-2">
|
|
<Settings className="text-primary h-4 w-4" />
|
|
<div>
|
|
<Label className="text-sm font-medium">제어 기능</Label>
|
|
<p className="text-muted-foreground mt-1 text-xs">버튼 클릭 시 추가 작업을 자동으로 실행합니다</p>
|
|
</div>
|
|
</div>
|
|
<Switch
|
|
checked={config.enableDataflowControl || false}
|
|
onCheckedChange={(checked) => onUpdateProperty("webTypeConfig.enableDataflowControl", checked)}
|
|
/>
|
|
</div>
|
|
|
|
{/* 🔥 제어관리가 활성화된 경우에만 설정 표시 */}
|
|
{config.enableDataflowControl && (
|
|
<div className="space-y-4">
|
|
<FlowSelector
|
|
flows={flows}
|
|
selectedFlowId={dataflowConfig.flowConfig?.flowId}
|
|
onSelect={handleFlowSelect}
|
|
loading={loading}
|
|
/>
|
|
|
|
{dataflowConfig.flowConfig && (
|
|
<div className="space-y-4">
|
|
<Separator />
|
|
<ExecutionTimingSelector
|
|
value={dataflowConfig.flowConfig.executionTiming}
|
|
onChange={(timing) =>
|
|
onUpdateProperty("webTypeConfig.dataflowConfig.flowConfig.executionTiming", timing)
|
|
}
|
|
/>
|
|
|
|
<div className="rounded bg-green-50 p-3">
|
|
<div className="flex items-start space-x-2">
|
|
<Info className="mt-0.5 h-4 w-4 text-green-600" />
|
|
<div className="text-xs text-green-800">
|
|
<p className="font-medium">노드 플로우 실행 정보:</p>
|
|
<p className="mt-1">선택한 플로우의 모든 노드가 순차적/병렬로 실행됩니다.</p>
|
|
<p className="mt-1">• 독립 트랜잭션: 각 액션은 독립적으로 커밋/롤백</p>
|
|
<p>• 연쇄 중단: 부모 노드 실패 시 자식 노드 스킵</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
};
|
|
|
|
/**
|
|
* 🔥 플로우 선택 컴포넌트
|
|
*/
|
|
const FlowSelector: React.FC<{
|
|
flows: NodeFlow[];
|
|
selectedFlowId?: number;
|
|
onSelect: (flowId: string) => void;
|
|
loading: boolean;
|
|
}> = ({ flows, selectedFlowId, onSelect, loading }) => {
|
|
return (
|
|
<div className="space-y-4">
|
|
<div className="flex items-center space-x-2">
|
|
<Workflow className="h-4 w-4 text-green-600" />
|
|
<Label>실행할 노드 플로우 선택</Label>
|
|
</div>
|
|
|
|
<Select value={selectedFlowId?.toString() || ""} onValueChange={onSelect}>
|
|
<SelectTrigger>
|
|
<SelectValue placeholder="플로우를 선택하세요" />
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
{loading ? (
|
|
<div className="p-4 text-center text-sm text-gray-500">플로우 목록을 불러오는 중...</div>
|
|
) : flows.length === 0 ? (
|
|
<div className="p-4 text-center text-sm text-gray-500">
|
|
<p>사용 가능한 플로우가 없습니다</p>
|
|
<p className="mt-2 text-xs">노드 편집기에서 플로우를 먼저 생성하세요</p>
|
|
</div>
|
|
) : (
|
|
flows.map((flow) => (
|
|
<SelectItem key={flow.flowId} value={flow.flowId.toString()}>
|
|
<div className="flex flex-col">
|
|
<span className="font-medium">{flow.flowName}</span>
|
|
{flow.flowDescription && (
|
|
<span className="text-muted-foreground text-xs">{flow.flowDescription}</span>
|
|
)}
|
|
</div>
|
|
</SelectItem>
|
|
))
|
|
)}
|
|
</SelectContent>
|
|
</Select>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
/**
|
|
* 🔥 실행 타이밍 선택 컴포넌트
|
|
*/
|
|
const ExecutionTimingSelector: React.FC<{
|
|
value: string;
|
|
onChange: (timing: "before" | "after" | "replace") => void;
|
|
}> = ({ value, onChange }) => {
|
|
return (
|
|
<div className="space-y-2">
|
|
<div className="flex items-center space-x-2">
|
|
<Clock className="h-4 w-4 text-orange-600" />
|
|
<Label>실행 타이밍</Label>
|
|
</div>
|
|
|
|
<Select value={value} onValueChange={onChange}>
|
|
<SelectTrigger>
|
|
<SelectValue placeholder="실행 타이밍을 선택하세요" />
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
<SelectItem value="before">
|
|
<div className="flex flex-col">
|
|
<span className="font-medium">Before (사전 실행)</span>
|
|
<span className="text-muted-foreground text-xs">버튼 액션 실행 전에 플로우를 실행합니다</span>
|
|
</div>
|
|
</SelectItem>
|
|
<SelectItem value="after">
|
|
<div className="flex flex-col">
|
|
<span className="font-medium">After (사후 실행)</span>
|
|
<span className="text-muted-foreground text-xs">버튼 액션 실행 후에 플로우를 실행합니다</span>
|
|
</div>
|
|
</SelectItem>
|
|
<SelectItem value="replace">
|
|
<div className="flex flex-col">
|
|
<span className="font-medium">Replace (대체 실행)</span>
|
|
<span className="text-muted-foreground text-xs">버튼 액션 대신 플로우만 실행합니다</span>
|
|
</div>
|
|
</SelectItem>
|
|
</SelectContent>
|
|
</Select>
|
|
</div>
|
|
);
|
|
};
|