"use client"; import React, { useState, useEffect, useMemo } from "react"; import { Label } from "@/components/ui/label"; import { Checkbox } from "@/components/ui/checkbox"; import { Button } from "@/components/ui/button"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"; import { Badge } from "@/components/ui/badge"; import { Alert, AlertDescription } from "@/components/ui/alert"; import { Input } from "@/components/ui/input"; import { Card, CardHeader, CardTitle, CardDescription, CardContent } from "@/components/ui/card"; import { Workflow, Info, CheckCircle, XCircle, Loader2, ArrowRight, ArrowDown } from "lucide-react"; import { ComponentData } from "@/types/screen"; import { FlowVisibilityConfig } from "@/types/control-management"; import { getFlowById, getFlowSteps } from "@/lib/api/flow"; import type { FlowDefinition, FlowStep } from "@/types/flow"; import { toast } from "sonner"; interface FlowVisibilityConfigPanelProps { component: ComponentData; // 현재 선택된 버튼 allComponents: ComponentData[]; // 화면의 모든 컴포넌트 onUpdateProperty: (path: string, value: any) => void; } /** * 플로우 단계별 버튼 표시 설정 패널 * * 플로우 위젯이 화면에 있을 때, 버튼이 특정 플로우 단계에서만 표시되도록 설정할 수 있습니다. */ export const FlowVisibilityConfigPanel: React.FC = ({ component, allComponents, onUpdateProperty, }) => { // 현재 설정 const currentConfig: FlowVisibilityConfig | undefined = (component as any).webTypeConfig?.flowVisibilityConfig; // 화면의 모든 플로우 위젯 찾기 const flowWidgets = useMemo(() => { return allComponents.filter((comp) => { const isFlowWidget = comp.type === "flow" || (comp.type === "component" && (comp as any).componentConfig?.type === "flow-widget"); return isFlowWidget; }); }, [allComponents]); // State const [enabled, setEnabled] = useState(currentConfig?.enabled || false); const [selectedFlowComponentId, setSelectedFlowComponentId] = useState( currentConfig?.targetFlowComponentId || null, ); const [mode, setMode] = useState<"whitelist" | "blacklist" | "all">(currentConfig?.mode || "whitelist"); const [visibleSteps, setVisibleSteps] = useState(currentConfig?.visibleSteps || []); const [hiddenSteps, setHiddenSteps] = useState(currentConfig?.hiddenSteps || []); const [layoutBehavior, setLayoutBehavior] = useState<"preserve-position" | "auto-compact">( currentConfig?.layoutBehavior || "auto-compact", ); // 🆕 그룹 설정 (auto-compact 모드에서만 사용) const [groupId, setGroupId] = useState(currentConfig?.groupId || `group-${Date.now()}`); const [groupDirection, setGroupDirection] = useState<"horizontal" | "vertical">( currentConfig?.groupDirection || "horizontal", ); const [groupGap, setGroupGap] = useState(currentConfig?.groupGap ?? 8); const [groupAlign, setGroupAlign] = useState<"start" | "center" | "end" | "space-between" | "space-around">( currentConfig?.groupAlign || "start", ); // 선택된 플로우의 스텝 목록 const [flowSteps, setFlowSteps] = useState([]); const [flowInfo, setFlowInfo] = useState(null); const [loading, setLoading] = useState(false); // 플로우가 없을 때 if (flowWidgets.length === 0) { return ( 플로우 단계별 표시 설정 화면에 플로우 위젯을 추가하면 단계별 버튼 표시 제어가 가능합니다. ); } // 선택된 플로우의 스텝 로드 useEffect(() => { if (!selectedFlowComponentId) { setFlowSteps([]); setFlowInfo(null); return; } const loadFlowSteps = async () => { try { setLoading(true); // 선택된 플로우 위젯 찾기 const flowWidget = flowWidgets.find((fw) => fw.id === selectedFlowComponentId); if (!flowWidget) return; // flowId 추출 const flowConfig = (flowWidget as any).componentConfig || {}; const flowId = flowConfig.flowId; if (!flowId) { toast.error("플로우 ID를 찾을 수 없습니다"); return; } // 플로우 정보 조회 const flowResponse = await getFlowById(flowId); if (!flowResponse.success || !flowResponse.data) { throw new Error("플로우를 찾을 수 없습니다"); } setFlowInfo(flowResponse.data); // 스텝 목록 조회 const stepsResponse = await getFlowSteps(flowId); if (!stepsResponse.success) { throw new Error("스텝 목록을 불러올 수 없습니다"); } if (stepsResponse.data) { const sortedSteps = stepsResponse.data.sort((a: FlowStep, b: FlowStep) => a.stepOrder - b.stepOrder); setFlowSteps(sortedSteps); } } catch (error: any) { console.error("플로우 스텝 로딩 실패:", error); toast.error(error.message || "플로우 정보를 불러오는데 실패했습니다"); } finally { setLoading(false); } }; loadFlowSteps(); }, [selectedFlowComponentId, flowWidgets]); // 🆕 설정 자동 저장 (즉시 적용) - 오버라이드 가능한 파라미터 지원 const applyConfig = (overrides?: Partial) => { const config: FlowVisibilityConfig = { enabled, targetFlowComponentId: selectedFlowComponentId || "", targetFlowId: flowInfo?.id, targetFlowName: flowInfo?.name, mode, visibleSteps: mode === "whitelist" ? visibleSteps : undefined, hiddenSteps: mode === "blacklist" ? hiddenSteps : undefined, layoutBehavior, // 🆕 그룹 설정 (auto-compact 모드일 때만) ...(layoutBehavior === "auto-compact" && { groupId, groupDirection, groupGap, groupAlign, }), // 오버라이드 적용 ...overrides, }; console.log("💾 [FlowVisibilityConfig] 설정 자동 저장:", { componentId: component.id, config, timestamp: new Date().toISOString(), }); // 현재 버튼에 설정 적용 (그룹 설정은 ScreenDesigner에서 자동으로 일괄 적용됨) onUpdateProperty("webTypeConfig.flowVisibilityConfig", config); }; // 체크박스 토글 const toggleStep = (stepId: number) => { if (mode === "whitelist") { const newSteps = visibleSteps.includes(stepId) ? visibleSteps.filter((id) => id !== stepId) : [...visibleSteps, stepId]; setVisibleSteps(newSteps); // 🆕 새 상태값을 직접 전달하여 즉시 저장 applyConfig({ visibleSteps: newSteps }); } else if (mode === "blacklist") { const newSteps = hiddenSteps.includes(stepId) ? hiddenSteps.filter((id) => id !== stepId) : [...hiddenSteps, stepId]; setHiddenSteps(newSteps); // 🆕 새 상태값을 직접 전달하여 즉시 저장 applyConfig({ hiddenSteps: newSteps }); } }; // 빠른 선택 const selectAll = () => { if (mode === "whitelist") { const newSteps = flowSteps.map((s) => s.id); setVisibleSteps(newSteps); applyConfig({ visibleSteps: newSteps }); } else if (mode === "blacklist") { setHiddenSteps([]); applyConfig({ hiddenSteps: [] }); } }; const selectNone = () => { if (mode === "whitelist") { setVisibleSteps([]); applyConfig({ visibleSteps: [] }); } else if (mode === "blacklist") { const newSteps = flowSteps.map((s) => s.id); setHiddenSteps(newSteps); applyConfig({ hiddenSteps: newSteps }); } }; const invertSelection = () => { if (mode === "whitelist") { const allStepIds = flowSteps.map((s) => s.id); const newSteps = allStepIds.filter((id) => !visibleSteps.includes(id)); setVisibleSteps(newSteps); applyConfig({ visibleSteps: newSteps }); } else if (mode === "blacklist") { const allStepIds = flowSteps.map((s) => s.id); const newSteps = allStepIds.filter((id) => !hiddenSteps.includes(id)); setHiddenSteps(newSteps); applyConfig({ hiddenSteps: newSteps }); } }; return (

플로우 단계별 표시 설정

플로우의 특정 단계에서만 이 버튼을 표시하거나 숨길 수 있습니다

{/* 활성화 체크박스 */}
{ setEnabled(!!checked); setTimeout(() => applyConfig(), 0); }} />
{enabled && ( <> {/* 대상 플로우 선택 */}
{/* 플로우가 선택되면 스텝 목록 표시 */} {selectedFlowComponentId && flowSteps.length > 0 && ( <> {/* 단계 선택 */}
{/* 스텝 체크박스 목록 */}
{flowSteps.map((step) => { const isChecked = visibleSteps.includes(step.id); return (
toggleStep(step.id)} />
); })}
{/* 정렬 방식 */}
)} {/* 플로우 선택 안내 */} {selectedFlowComponentId && flowSteps.length === 0 && !loading && ( 선택한 플로우에 단계가 없습니다. )} {loading && (
플로우 정보를 불러오는 중...
)} )}
); };