feat: add AI chat builder frontend — types, API client, store, components, page, routing

- TypeScript types for chat session, messages, and responses
- API client module with all 6 endpoints
- Zustand store with session management, message sending, tree generation, import, resume
- 7 chat components: ChatMessage, ChatInput, ChatPanel, PhaseIndicator, ChatToolbar, EmptyPreview, StaticTreePreview
- AIChatBuilderPage with split-panel layout (60% chat / 40% preview)
- Route at /ai/chat with lazy loading
- "Build with AI" button on TreeLibraryPage
- Session resume via URL search params

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
chihlasm
2026-02-27 07:20:04 -05:00
parent 0da67586da
commit 596153085a
15 changed files with 844 additions and 6 deletions

View File

@@ -0,0 +1,76 @@
import { Sparkles, Download, RotateCcw, ArrowRight } from 'lucide-react'
import { PhaseIndicator } from './PhaseIndicator'
import { cn } from '@/lib/utils'
import type { InterviewPhase } from '@/types'
interface ChatToolbarProps {
currentPhase: InterviewPhase
status: 'idle' | 'active' | 'completed' | 'abandoned'
isGenerating: boolean
hasGeneratedTree: boolean
onGenerate: () => void
onImport: () => void
onReset: () => void
}
export function ChatToolbar({
currentPhase,
status,
isGenerating,
hasGeneratedTree,
onGenerate,
onImport,
onReset,
}: ChatToolbarProps) {
return (
<div className="flex items-center justify-between border-b border-border bg-card px-4 py-2">
<div className="flex items-center gap-3">
<div className="flex items-center gap-2 text-sm font-medium text-foreground">
<Sparkles className="h-4 w-4 text-primary" />
Build with AI
</div>
<PhaseIndicator currentPhase={currentPhase} />
</div>
<div className="flex items-center gap-2">
{status === 'active' && !hasGeneratedTree && (
<button
onClick={onGenerate}
disabled={isGenerating}
className={cn(
'flex items-center gap-1.5 rounded-lg px-3 py-1.5 text-sm font-medium',
'bg-gradient-brand text-white shadow-lg shadow-primary/20',
'hover:opacity-90 transition-opacity',
'disabled:opacity-50 disabled:cursor-not-allowed'
)}
>
<Download className="h-3.5 w-3.5" />
{isGenerating ? 'Generating...' : 'Generate Flow'}
</button>
)}
{hasGeneratedTree && (
<button
onClick={onImport}
className={cn(
'flex items-center gap-1.5 rounded-lg px-3 py-1.5 text-sm font-medium',
'bg-gradient-brand text-white shadow-lg shadow-primary/20',
'hover:opacity-90 transition-opacity'
)}
>
<ArrowRight className="h-3.5 w-3.5" />
Import to Editor
</button>
)}
<button
onClick={onReset}
className="flex items-center gap-1.5 rounded-lg border border-border px-3 py-1.5 text-sm text-muted-foreground hover:bg-accent hover:text-foreground transition-colors"
>
<RotateCcw className="h-3.5 w-3.5" />
Start Over
</button>
</div>
</div>
)
}