developing #6

Closed
GHMiranda wants to merge 32 commits from developing into master
2 changed files with 78 additions and 5 deletions
Showing only changes of commit 01f5428dd9 - Show all commits

View File

@ -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<ContextMenuState>(null)
const [conditionEditor, setConditionEditor] = useState<ConditionEditorState>(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}
/>
<div className="flex-1">
<ReactFlow
@ -586,6 +625,15 @@ function FlowchartEditorInner({ projectId, initialData }: FlowchartEditorProps)
</div>
</div>
)}
{/* Toast notification */}
{toast && (
<Toast
message={toast.message}
type={toast.type}
onClose={() => setToast(null)}
/>
)}
</div>
)
}

View File

@ -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 (
<div className="flex items-center justify-between border-b border-zinc-200 bg-zinc-50 px-4 py-2 dark:border-zinc-700 dark:bg-zinc-800">
@ -46,9 +48,32 @@ export default function Toolbar({
<div className="flex items-center gap-2">
<button
onClick={onSave}
className="rounded border border-zinc-300 bg-white px-3 py-1.5 text-sm font-medium text-zinc-700 hover:bg-zinc-50 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 dark:border-zinc-600 dark:bg-zinc-700 dark:text-zinc-200 dark:hover:bg-zinc-600 dark:focus:ring-offset-zinc-800"
disabled={isSaving}
className="flex items-center gap-1.5 rounded border border-zinc-300 bg-white px-3 py-1.5 text-sm font-medium text-zinc-700 hover:bg-zinc-50 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 dark:border-zinc-600 dark:bg-zinc-700 dark:text-zinc-200 dark:hover:bg-zinc-600 dark:focus:ring-offset-zinc-800"
>
Save
{isSaving && (
<svg
className="h-4 w-4 animate-spin"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
>
<circle
className="opacity-25"
cx="12"
cy="12"
r="10"
stroke="currentColor"
strokeWidth="4"
/>
<path
className="opacity-75"
fill="currentColor"
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
/>
</svg>
)}
{isSaving ? 'Saving...' : 'Save'}
</button>
<button
onClick={onExport}