import React, { useState, useEffect, useRef } from 'react'; import { Send, Sparkles, Lock, ArrowRight, Loader2, Trash2 } from 'lucide-react'; import Logo from './Logo'; interface ChartChatbotProps { ticker: string; markers: Array<{ price: number; label: string; color: string }>; activeIndicators: { sma: boolean; ema: boolean; rsi: boolean }; user: any; onOpenRecharge?: () => void; liveData?: { price: number | null; sma: number | null; ema: number | null; rsi: number | null; }; } export default function ChartChatbot({ ticker, markers, activeIndicators, user, onOpenRecharge, liveData }: ChartChatbotProps) { const [messages, setMessages] = useState>(() => { const saved = localStorage.getItem(`quantiq_chat_history_${ticker}`); if (saved) { try { return JSON.parse(saved); } catch (e) { console.error('Failed to parse chat history:', e); } } return [ { role: 'assistant', content: `Hello! I am your QuantIQ AI Strategy Advisor. I have analyzed the chart for ${ticker} and loaded your custom reference markers. Ask me anything about your entry/exit levels or risk setup!` } ]; }); const [input, setInput] = useState(''); const [loading, setLoading] = useState(false); const [showConfirm, setShowConfirm] = useState(false); // Local quota tracking synchronized with parent user prop const [localRemaining, setLocalRemaining] = useState(() => user?.messagesRemaining ?? 0); const [localUsed, setLocalUsed] = useState(() => user?.monthlyMessagesUsed ?? 0); const [localTier, setLocalTier] = useState(() => user?.subscriptionTier || 'free'); useEffect(() => { if (user) { setLocalRemaining(user.messagesRemaining ?? 0); setLocalUsed(user.monthlyMessagesUsed ?? 0); setLocalTier(user.subscriptionTier || 'free'); } }, [user]); const messagesEndRef = useRef(null); const textareaRef = useRef(null); const API_URL = import.meta.env.VITE_API_URL || 'http://localhost:8000'; const isAdmin = user?.email === 'karanshelar8775@gmail.com'; const isLimitReached = !isAdmin && ( (localTier !== 'pro' && localRemaining <= 0) || (localTier === 'pro' && localUsed >= 100) ); const getQuotaDisplay = () => { if (isAdmin) return 'Unlimited (Admin)'; if (localTier === 'pro') { return `${100 - localUsed} left`; } return `${localRemaining} left`; }; // Helper to format text markdown bold/italics, headers, bullets, and clean up stray symbols const formatMessageContent = (text: string) => { if (!text) return ''; const lines = text.split('\n'); return lines.map((line, lineIdx) => { let cleanedLine = line.trim(); // 1. Convert headers (e.g., #### Header or ### Header) to clean bold blocks const headerMatch = cleanedLine.match(/^(#{1,6})\s*(.*)$/); if (headerMatch) { let headerText = headerMatch[2]; headerText = headerText.replace(/\*\*(.*?)\*\*/g, '$1'); headerText = headerText.replace(/\*(.*?)\*/g, '$1'); return (

{headerText}

); } // Check if it is a bullet point (must have content after the bullet character) const isBullet = (cleanedLine.startsWith('-') || cleanedLine.startsWith('*')) && cleanedLine.replace(/^[\s-*]+/, '').trim() !== ''; if (isBullet) { cleanedLine = cleanedLine.replace(/^[\s-*]+/, ''); } // Parse bold (**text**) and italic (*text*) segments into react elements const parts: Array = []; const formattingRegex = /(\*\*.*?\*\*|\*.*?\*)/g; const splitSegments = cleanedLine.split(formattingRegex); splitSegments.forEach((seg, segIdx) => { if (seg.startsWith('**') && seg.endsWith('**')) { const inner = seg.slice(2, -2); parts.push({inner}); } else if (seg.startsWith('*') && seg.endsWith('*')) { const inner = seg.slice(1, -1); parts.push({inner}); } else { parts.push(seg); } }); const content = parts.length > 0 ? parts : cleanedLine; if (isBullet) { return (
{content}
); } return (

{content}

); }); }; const adjustHeight = () => { if (textareaRef.current) { textareaRef.current.style.height = 'auto'; textareaRef.current.style.height = `${Math.min(textareaRef.current.scrollHeight, 100)}px`; } }; useEffect(() => { adjustHeight(); }, [input]); // Load chat history on ticker change useEffect(() => { const saved = localStorage.getItem(`quantiq_chat_history_${ticker}`); if (saved) { try { setMessages(JSON.parse(saved)); return; } catch (e) { console.error('Failed to parse chat history on ticker change:', e); } } setMessages([ { role: 'assistant', content: `Hello! I am your QuantIQ AI Strategy Advisor. I have analyzed the chart for ${ticker} and loaded your custom reference markers. Ask me anything about your entry/exit levels or risk setup!` } ]); }, [ticker]); // Save chat history to localStorage and scroll to bottom useEffect(() => { localStorage.setItem(`quantiq_chat_history_${ticker}`, JSON.stringify(messages)); messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' }); }, [messages, ticker]); const handleSendMessage = async (e?: React.FormEvent | React.KeyboardEvent) => { if (e) e.preventDefault(); if (!input.trim() || loading || isLimitReached) return; const userMessage = input.trim(); setInput(''); if (textareaRef.current) { textareaRef.current.style.height = '38px'; } setLoading(true); const updatedMessages = [...messages, { role: 'user' as const, content: userMessage }]; setMessages(updatedMessages); try { const response = await fetch(`${API_URL}/api/v1/analyst/chat`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${localStorage.getItem('quantiq_jwt')}` }, body: JSON.stringify({ ticker, message: userMessage, history: updatedMessages.slice(0, -1), markers, activeIndicators, currentPrice: liveData?.price || null, smaValue: liveData?.sma || null, emaValue: liveData?.ema || null, rsiValue: liveData?.rsi || null }) }); if (response.ok) { const data = await response.json(); setMessages(prev => [...prev, { role: 'assistant', content: data.response }]); // Update local limits state if (data.subscription_tier !== undefined) { setLocalTier(data.subscription_tier); setLocalRemaining(data.messages_remaining ?? 0); setLocalUsed(data.monthly_messages_used ?? 0); } } else { const errData = await response.json(); const errMessage = errData.detail || "Quota exhausted or query error. Check your subscription."; setMessages(prev => [...prev, { role: 'assistant', content: `Sorry, I encountered an issue: ${errMessage}` }]); } } catch (err) { console.error('Failed to chat with AI analyst:', err); setMessages(prev => [...prev, { role: 'assistant', content: "Failed to connect to the AI Strategy engine. Verify your internet connection." }]); } finally { setLoading(false); } }; return (
{/* Clear Chat Confirmation Overlay */} {showConfirm && (

Confirm Deletion

Are you sure you want to clear the chat history for {ticker} from your screen?

)} {/* Background Cyber Glowing Orbs */}
{/* Header */}

Quant AI Advisor

Real-time AI Copilot & Strategy Engine
{/* Quota display badge */} {getQuotaDisplay()} {/* Clear Chat Button */}
{/* Message Feed */}
{/* Faint Logo Watermark Background */}
{messages.map((msg, index) => { const isUser = msg.role === 'user'; return (
{formatMessageContent(msg.content)}
); })} {loading && (
Analyzing Quant AI Strategy...
)}
{/* Input or Lock overlay */}
{isLimitReached ? (
Message Limit Reached

Unlock high-accuracy Wall Street strategies and indicator calculations.

) : (