From 4cc6ee4797bf00301688c04d44b3287b8974ae38 Mon Sep 17 00:00:00 2001 From: chihlasm Date: Thu, 2 Apr 2026 17:20:34 +0000 Subject: [PATCH] feat: create FlowPilotPage with classic chat layout Recreates the production AssistantChatPage as FlowPilotPage using the shared useAssistantSession hook. Classic chat interface with ChatMessage bubbles, TaskLane side panel, rich input with file uploads, and conclude/status update modals. ViewToggle commented out pending Task 4. Co-Authored-By: Claude Opus 4.6 (1M context) --- frontend/src/pages/FlowPilotPage.tsx | 368 +++++++++++++++++++++++++++ 1 file changed, 368 insertions(+) create mode 100644 frontend/src/pages/FlowPilotPage.tsx diff --git a/frontend/src/pages/FlowPilotPage.tsx b/frontend/src/pages/FlowPilotPage.tsx new file mode 100644 index 00000000..6ff76b53 --- /dev/null +++ b/frontend/src/pages/FlowPilotPage.tsx @@ -0,0 +1,368 @@ +import { useEffect } from 'react' +import { Sparkles, Send, Loader2, Flag, MessageSquare, Paperclip, Terminal, X, RotateCcw, ImagePlus, ListChecks, FileText } from 'lucide-react' +import { cn } from '@/lib/utils' +import { PageMeta } from '@/components/common/PageMeta' +import { aiSessionsApi } from '@/api/aiSessions' +import { ChatSidebar, ChatSidebarCollapsedBar } from '@/components/assistant/ChatSidebar' +import { ChatMessage } from '@/components/assistant/ChatMessage' +import { TaskLane } from '@/components/assistant/TaskLane' +import { ConcludeSessionModal } from '@/components/assistant/ConcludeSessionModal' +import { StatusUpdateModal } from '@/components/flowpilot/StatusUpdateModal' +// TODO: uncomment after Task 4 +// import { ViewToggle } from '@/components/assistant/ViewToggle' +import { useAssistantSession } from '@/hooks/useAssistantSession' + +export default function FlowPilotPage() { + const session = useAssistantSession() + + // Handle prefill from dashboard / command palette + useEffect(() => { + session.handlePrefill('/assistant') + }, []) // eslint-disable-line react-hooks/exhaustive-deps + + const handleTaskSubmit = async (responses: Array<{ type: string; state: string; value: string; text?: string; label?: string }>) => { + if (!session.activeChatId || session.loading) return + + const parts: string[] = [] + for (const r of responses) { + const name = r.type === 'question' ? `Q: ${r.text}` : r.label || 'Check' + if (r.state === 'done' && r.value.trim()) { + parts.push(`**${name}:**\n\`\`\`\n${r.value.trim()}\n\`\`\``) + } else if (r.state === 'skipped') { + parts.push(`**${name}:** _(skipped)_`) + } + } + const userMessage = parts.join('\n\n') + const sendChatId = session.activeChatId + + session.setInput('') + session.setShowTaskLane(false) + session.setActiveQuestions([]) + session.setActiveActions([]) + + try { + const response = await aiSessionsApi.sendChatMessage(sendChatId, { message: userMessage }) + if (session.currentChatRef.current !== sendChatId) return + session.processResponse(response, sendChatId) + + const hasQuestions = response.questions && response.questions.length > 0 + const hasActions = response.actions && response.actions.length > 0 + if (!hasQuestions && !hasActions) { + session.setShowTaskLane(false) + session.setActiveQuestions([]) + session.setActiveActions([]) + } + } catch { + // Error handled by processResponse guard + } + } + + return ( + <> + +
+ {/* Chat Sidebar — desktop */} + {!session.sidebarCollapsed && ( +
+ +
+ )} + {/* Chat Sidebar — mobile */} +
+ session.setMobileSidebarOpen(false)} + /> +
+ + {/* Main area */} +
+ {/* Collapsed sidebar bar */} + {session.sidebarCollapsed && ( +
+ +
+ )} + + {/* Chat content row */} +
+
+ {/* Mobile header */} +
+ +
+ {/* TODO: uncomment after Task 4 */} + {/* {session.activeChatId && ( + + )} */} + +
+ + {session.activeChatId ? ( + <> + {/* Desktop view toggle bar */} +
+ {/* TODO: uncomment after Task 4 */} + {/* */} +
+ + {/* Messages */} +
+ {session.messages.length === 0 && !session.loading && ( +
+
+ +
+

+ FlowPilot +

+

+ Ask me anything about IT infrastructure, networking, Active Directory, + cloud platforms, or troubleshooting. I'll also suggest relevant flows from your team's library. +

+
+ )} + {session.messages.map((msg, i) => ( + + ))} + {session.loading && ( +
+
+ +
+
+ +
+
+ )} +
+
+ + {/* Rich Input */} +
+
+
+ {session.isDragOver && ( +
+
+ + Drop files to attach +
+
+ )} + +