"use client"; /** * 플로우 에디터 상단 툴바 */ import { useState } from "react"; import { Save, Undo2, Redo2, ZoomIn, ZoomOut, Download, Trash2 } from "lucide-react"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { useFlowEditorStore } from "@/lib/stores/flowEditorStore"; import { useReactFlow } from "reactflow"; import { SaveConfirmDialog } from "./dialogs/SaveConfirmDialog"; import { validateFlow, summarizeValidations } from "@/lib/utils/flowValidation"; import type { FlowValidation } from "@/lib/utils/flowValidation"; import { useToast } from "@/hooks/use-toast"; interface FlowToolbarProps { validations?: FlowValidation[]; /** 임베디드 모드에서 저장 완료 시 호출되는 콜백 */ onSaveComplete?: (flowId: number, flowName: string) => void; } export function FlowToolbar({ validations = [], onSaveComplete }: FlowToolbarProps) { const { toast } = useToast(); const { zoomIn, zoomOut, fitView } = useReactFlow(); const { flowName, setFlowName, nodes, edges, saveFlow, exportFlow, isSaving, selectedNodes, removeNodes, undo, redo, canUndo, canRedo, } = useFlowEditorStore(); const [showSaveDialog, setShowSaveDialog] = useState(false); const handleSave = async () => { // 검증 수행 const currentValidations = validations.length > 0 ? validations : validateFlow(nodes, edges); const summary = summarizeValidations(currentValidations); // 오류나 경고가 있으면 다이얼로그 표시 if (currentValidations.length > 0) { setShowSaveDialog(true); return; } // 문제 없으면 바로 저장 await performSave(); }; const performSave = async () => { const result = await saveFlow(); if (result.success) { toast({ title: "저장 완료", description: `${result.message}\nFlow ID: ${result.flowId}`, variant: "default", }); // 임베디드 모드에서 저장 완료 콜백 호출 if (onSaveComplete && result.flowId) { onSaveComplete(result.flowId, flowName); } // 부모 창이 있으면 postMessage로 알림 (새 창에서 열린 경우) if (window.opener && result.flowId) { window.opener.postMessage({ type: "FLOW_SAVED", flowId: result.flowId, flowName: flowName, }, "*"); } } else { toast({ title: "저장 실패", description: result.message, variant: "destructive", }); } setShowSaveDialog(false); }; const handleExport = () => { const json = exportFlow(); const blob = new Blob([json], { type: "application/json" }); const url = URL.createObjectURL(blob); const a = document.createElement("a"); a.href = url; a.download = `${flowName || "flow"}.json`; a.click(); URL.revokeObjectURL(url); toast({ title: "✅ 내보내기 완료", description: "JSON 파일로 저장되었습니다.", variant: "default", }); }; const handleDelete = () => { if (selectedNodes.length === 0) { toast({ title: "⚠️ 선택된 노드 없음", description: "삭제할 노드를 선택해주세요.", variant: "default", }); return; } if (confirm(`선택된 ${selectedNodes.length}개 노드를 삭제하시겠습니까?`)) { removeNodes(selectedNodes); toast({ title: "✅ 노드 삭제 완료", description: `${selectedNodes.length}개 노드가 삭제되었습니다.`, variant: "default", }); } }; return ( <>