From 01f5428dd9d1d1c9fccc10c83957fc8932b1b3a9 Mon Sep 17 00:00:00 2001 From: Gustavo Henrique Santos Souza de Miranda Date: Thu, 22 Jan 2026 18:17:33 -0300 Subject: [PATCH] feat: [US-034] - Save project to database Co-Authored-By: Claude Opus 4.5 --- .../editor/[projectId]/FlowchartEditor.tsx | 54 +++++++++++++++++-- src/components/editor/Toolbar.tsx | 29 +++++++++- 2 files changed, 78 insertions(+), 5 deletions(-) diff --git a/src/app/editor/[projectId]/FlowchartEditor.tsx b/src/app/editor/[projectId]/FlowchartEditor.tsx index 3efa2c1..6462ae4 100644 --- a/src/app/editor/[projectId]/FlowchartEditor.tsx +++ b/src/app/editor/[projectId]/FlowchartEditor.tsx @@ -22,6 +22,8 @@ import ReactFlow, { import { nanoid } from 'nanoid' import 'reactflow/dist/style.css' import Toolbar from '@/components/editor/Toolbar' +import Toast from '@/components/Toast' +import { createClient } from '@/lib/supabase/client' import DialogueNode from '@/components/editor/nodes/DialogueNode' import ChoiceNode from '@/components/editor/nodes/ChoiceNode' import VariableNode from '@/components/editor/nodes/VariableNode' @@ -166,6 +168,8 @@ function FlowchartEditorInner({ projectId, initialData }: FlowchartEditorProps) const [contextMenu, setContextMenu] = useState(null) const [conditionEditor, setConditionEditor] = useState(null) + const [isSaving, setIsSaving] = useState(false) + const [toast, setToast] = useState<{ message: string; type: 'success' | 'error' } | null>(null) // Check for saved draft on initial render (lazy initialization) const [draftState, setDraftState] = useState<{ @@ -308,9 +312,43 @@ function FlowchartEditorInner({ projectId, initialData }: FlowchartEditorProps) setNodes((nodes) => [...nodes, newNode]) }, [getViewportCenter, setNodes]) - const handleSave = useCallback(() => { - // TODO: Implement in US-034 - }, []) + const handleSave = useCallback(async () => { + if (isSaving) return + + setIsSaving(true) + + try { + const supabase = createClient() + + // Convert React Flow state to FlowchartData + const flowchartData: FlowchartData = { + nodes: fromReactFlowNodes(nodes), + edges: fromReactFlowEdges(edges), + } + + const { error } = await supabase + .from('projects') + .update({ + flowchart_data: flowchartData, + updated_at: new Date().toISOString(), + }) + .eq('id', projectId) + + if (error) { + throw error + } + + // Clear LocalStorage draft after successful save + clearDraft(projectId) + + setToast({ message: 'Project saved successfully', type: 'success' }) + } catch (error) { + console.error('Failed to save project:', error) + setToast({ message: 'Failed to save project. Please try again.', type: 'error' }) + } finally { + setIsSaving(false) + } + }, [isSaving, nodes, edges, projectId]) const handleExport = useCallback(() => { // TODO: Implement in US-035 @@ -509,6 +547,7 @@ function FlowchartEditorInner({ projectId, initialData }: FlowchartEditorProps) onSave={handleSave} onExport={handleExport} onImport={handleImport} + isSaving={isSaving} />
)} + + {/* Toast notification */} + {toast && ( + setToast(null)} + /> + )} ) } diff --git a/src/components/editor/Toolbar.tsx b/src/components/editor/Toolbar.tsx index 08f0243..0b26706 100644 --- a/src/components/editor/Toolbar.tsx +++ b/src/components/editor/Toolbar.tsx @@ -7,6 +7,7 @@ type ToolbarProps = { onSave: () => void onExport: () => void onImport: () => void + isSaving?: boolean } export default function Toolbar({ @@ -16,6 +17,7 @@ export default function Toolbar({ onSave, onExport, onImport, + isSaving = false, }: ToolbarProps) { return (
@@ -46,9 +48,32 @@ export default function Toolbar({