import * as React from "react"; import { Bot, RotateCcw, X } from "lucide-react"; import { cn } from "@/lib/utils"; import { Sheet, SheetContent } from "@/components/ui/sheet"; import { Button } from "@/components/ui/button"; import { Badge } from "@/components/ui/badge"; import { ChatInputArea } from "@/components/ui/chat-input-area"; import { Spinner } from "@/components/ui/spinner"; /** * AiAssistantDrawer — WealthX DS (L5 Drawer) * * Right-side panel that provides an AI conversation interface for a specific * opportunity/deal. Used in the Pipeline board via the "Launch Assistant" button * on OpportunityCard. * * Layout: * • Header — bot icon + title + reload + close * • Content — empty state (task suggestions) or chat message list * • Footer — textarea input + send button * * Pure display component: all message state and API calls are managed by the * consuming page. The drawer only handles local `inputValue` state. * * Data source: `ai-chat.ts` / `conversation.ts` hooks in the backoffice */ // --------------------------------------------------------------------------- // Types // --------------------------------------------------------------------------- export interface AiChatMessage { id: string; role: "user" | "assistant"; content: string; /** True while the assistant response is still streaming in. */ isStreaming?: boolean; /** True if the message failed to send or receive. */ isErrored?: boolean; } export interface AiTaskSuggestion { id: string; /** Short label shown as the suggestion title. */ title: string; /** Supporting description shown below the title. */ description: string; } export interface AiAssistantDrawerProps { open: boolean; onClose: () => void; /** Opportunity or contact name shown in the header subtitle. */ opportunityName?: string; /** * Suggested tasks shown in the empty state. * Typically the opportunity's incomplete tasks. * Clicking a suggestion pre-fills the input. */ taskSuggestions?: AiTaskSuggestion[]; /** Chat message history. Empty array = show empty/welcome state. */ messages?: AiChatMessage[]; /** True while the assistant is generating a response. */ isStreaming?: boolean; /** True while initial data is loading (shows full-panel spinner). */ isLoading?: boolean; /** Called when the user submits a message. Input is cleared after this fires. */ onSendMessage?: (text: string) => void; /** Called when the user selects files via the attachment button. */ onAttachFile?: (files: FileList) => void; /** Called when the user selects images via the image upload button. */ onAttachImage?: (files: FileList) => void; /** Called when the user clicks the reload/reset button. */ onReset?: () => void; className?: string; } // --------------------------------------------------------------------------- // Typing indicator (three bouncing dots) // --------------------------------------------------------------------------- function AiTypingIndicator() { return ( {[0, 150, 300].map((delay) => ( ))} ); } // --------------------------------------------------------------------------- // Chat message bubble // --------------------------------------------------------------------------- function AiChatBubble({ message }: { message: AiChatMessage }) { const isUser = message.role === "user"; const isEmpty = !message.content.trim(); return (
Failed to send. Please try again.
)}Initialising…
A safe and secure way to chat about insights that run your business and utilise AI. All chats stay within your environment, all closed off and compliant.
Suggested tasks