diff --git a/backend/app/api/endpoints/ai_chat.py b/backend/app/api/endpoints/ai_chat.py index 7fb16748..defebd5e 100644 --- a/backend/app/api/endpoints/ai_chat.py +++ b/backend/app/api/endpoints/ai_chat.py @@ -387,12 +387,8 @@ async def import_tree( detail="Session must be completed with a generated tree before importing", ) - if session.generated_tree_id: - return AIChatImportResponse( - tree_id=session.generated_tree_id, - tree_type=session.flow_type, - ) - + # Always create a new Tree record (no duplicate check — user may + # want multiple copies or re-import after edits) metadata = session.tree_metadata or {} tree = Tree( name=data.name or metadata.get("name", "AI-Generated Flow"), diff --git a/frontend/src/components/ai-chat/ChatToolbar.tsx b/frontend/src/components/ai-chat/ChatToolbar.tsx index 6624d96a..e5905801 100644 --- a/frontend/src/components/ai-chat/ChatToolbar.tsx +++ b/frontend/src/components/ai-chat/ChatToolbar.tsx @@ -1,4 +1,4 @@ -import { Sparkles, Download, RotateCcw, ArrowRight } from 'lucide-react' +import { Sparkles, Save, RotateCcw, Loader2 } from 'lucide-react' import { PhaseIndicator } from './PhaseIndicator' import { cn } from '@/lib/utils' import type { InterviewPhase } from '@/types' @@ -8,8 +8,9 @@ interface ChatToolbarProps { status: 'idle' | 'active' | 'completed' | 'abandoned' isGenerating: boolean hasGeneratedTree: boolean + isSaving: boolean onGenerate: () => void - onImport: () => void + onSave: () => void onReset: () => void } @@ -18,8 +19,9 @@ export function ChatToolbar({ status, isGenerating, hasGeneratedTree, + isSaving, onGenerate, - onImport, + onSave, onReset, }: ChatToolbarProps) { return ( @@ -44,22 +46,42 @@ export function ChatToolbar({ 'disabled:opacity-50 disabled:cursor-not-allowed' )} > - - {isGenerating ? 'Generating...' : 'Generate Flow'} + {isGenerating ? ( + <> + + Generating... + + ) : ( + <> + + Generate Flow + + )} )} {hasGeneratedTree && ( )} diff --git a/frontend/src/pages/AIChatBuilderPage.tsx b/frontend/src/pages/AIChatBuilderPage.tsx index 005fad1d..bcd3ba9b 100644 --- a/frontend/src/pages/AIChatBuilderPage.tsx +++ b/frontend/src/pages/AIChatBuilderPage.tsx @@ -1,4 +1,4 @@ -import { useCallback, useEffect } from 'react' +import { useCallback, useEffect, useState } from 'react' import { useNavigate, useSearchParams } from 'react-router-dom' import { useAIChatStore } from '@/store/aiChatStore' import { ChatPanel } from '@/components/ai-chat/ChatPanel' @@ -62,11 +62,15 @@ export function AIChatBuilderPage() { [sendMessage] ) + const [isSaving, setIsSaving] = useState(false) + const handleGenerate = useCallback(() => { generateTree() }, [generateTree]) - const handleImport = useCallback(async () => { + const handleSave = useCallback(async () => { + if (isSaving) return + setIsSaving(true) try { const treeId = await importToEditor({ name: treeMetadata?.name, @@ -75,11 +79,13 @@ export function AIChatBuilderPage() { }) const path = getTreeEditorPath(treeId, flowType) navigate(path) - toast.success('Flow imported to editor') + toast.success('Flow saved to library') } catch { - toast.error('Failed to import flow') + toast.error('Failed to save flow') + } finally { + setIsSaving(false) } - }, [importToEditor, treeMetadata, flowType, navigate]) + }, [isSaving, importToEditor, treeMetadata, flowType, navigate]) const handleReset = useCallback(async () => { await abandonSession() @@ -116,8 +122,9 @@ export function AIChatBuilderPage() { status={status} isGenerating={isGenerating} hasGeneratedTree={!!generatedTree} + isSaving={isSaving} onGenerate={handleGenerate} - onImport={handleImport} + onSave={handleSave} onReset={handleReset} />