import React, { useState, useRef, useEffect } from 'react'; import { ArrowRight } from 'lucide-react'; import { ChatMessage, FinancialData } from '../lib/types'; interface Props { data: FinancialData[]; userApiKey: string; } function renderMd(t: string): string { return (t || '') .replace(/\*\*(.+?)\*\*/g, '$1') .replace(/\*(.+?)\*/g, '$1') .replace(/^### (.+)$/gm, '$1
') .replace(/^## (.+)$/gm, '$1
') .replace(/^- (.+)$/gm, '• $1
') .replace(/\n\n/g, '

') .replace(/\n/g, '
'); } const QUICK_PROMPTS = [ 'Summarise the 3-year financial trend', 'Identify key forensic risks', 'What is the DuPont decomposition of ROE?', 'Evaluate debt sustainability', 'Compare operating leverage across years', ]; export default function ChatPanel({ data, userApiKey }: Props) { const [messages, setMessages] = useState([]); const [input, setInput] = useState(''); const [loading, setLoading] = useState(false); const [quickHidden, setQuickHidden] = useState(false); const endRef = useRef(null); const taRef = useRef(null); useEffect(() => { endRef.current?.scrollIntoView({ behavior: 'smooth' }); }, [messages, loading]); const context = data.reduce((acc: any, d) => { acc[`FY${d.year}`] = d; return acc; }, { company: data[0]?.companyName, sector: data[0]?.sector }); const send = async (text: string) => { if (!text.trim() || loading) return; setQuickHidden(true); const userMsg: ChatMessage = { role: 'user', text, timestamp: Date.now() }; setMessages(prev => [...prev, userMsg]); setInput(''); if (taRef.current) { taRef.current.style.height = 'auto'; } setLoading(true); try { const history = messages.map(m => ({ role: m.role === 'user' ? 'user' : 'model', text: m.text })); const res = await fetch('/api/discuss', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ message: text, history, context, userApiKey }), }); const json = await res.json(); if (json.error) throw new Error(json.error); setMessages(prev => [...prev, { role: 'assistant', text: json.text, timestamp: Date.now() }]); } catch (e: any) { setMessages(prev => [...prev, { role: 'assistant', text: `**Error:** ${e.message}. Please check your API key and try again.`, timestamp: Date.now(), }]); } finally { setLoading(false); } }; const canSend = input.trim().length > 0 && !loading; const handleInput = () => { if (!taRef.current) return; taRef.current.style.height = 'auto'; taRef.current.style.height = Math.min(taRef.current.scrollHeight, 100) + 'px'; setInput(taRef.current.value); }; return (
{/* Header */}
AI Analyst Chat
Institutional-grade analysis of {data[0]?.companyName || 'loaded data'}
{/* Messages */}
{/* Quick prompts */} {!quickHidden && messages.length === 0 && (

Quick prompts

{QUICK_PROMPTS.map(p => ( ))}
)} {/* Message list */} {messages.map((m, i) => (
{m.role === 'assistant' && (
)} {m.role === 'assistant' ? (
) : (
{m.text}
)} {m.role === 'user' && (
)}
))} {/* Typing indicator */} {loading && (
)}
{/* Input area */}