import React, { useState, useRef, useEffect } from "react"; import axios from "axios"; import { MessageCircle, Send, Cpu, User, LogOut, Activity, Mic, MicOff, Database, Globe as GlobeIcon, Radio } from "lucide-react"; import { motion, AnimatePresence } from "framer-motion"; import { clsx, type ClassValue } from "clsx"; import { twMerge } from "tailwind-merge"; import { DndContext, useSensor, useSensors, PointerSensor, DragEndEvent } from '@dnd-kit/core'; import SentimentDashboard from "./SentimentDashboard"; import RideMemoryBank from "./RideMemoryBank"; import GlobalNetworkMap from "./GlobalNetworkMap"; import QuantumInsight from "./QuantumInsight"; import ReputationSystem from "./ReputationSystem"; import LiveFeed from "./LiveFeed"; import AudioVisualizer from "./AudioVisualizer"; import ThemeSelector from "./ThemeSelector"; import DraggablePanel from "./DraggablePanel"; function cn(...inputs: ClassValue[]) { return twMerge(clsx(inputs)); } const NeonButton = ({ children, onClick, className, disabled }: any) => ( {children} ); interface Message { id: number; text: string; sender: "user" | "ai"; timestamp: string; } interface ChatInterfaceProps { onLogout: () => void; userId: string; } const ChatInterface: React.FC = ({ onLogout, userId }) => { const [messages, setMessages] = useState([ { id: 1, text: "Hello! I'm your Ride Intelligence Assistant. How can I help verify your trip or analyze feedback today?", sender: "ai", timestamp: new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }), }, ]); const [input, setInput] = useState(""); const [isLoading, setIsLoading] = useState(false); // Panel Visibility State const [showDashboard, setShowDashboard] = useState(false); const [showHistory, setShowHistory] = useState(false); const [showMap, setShowMap] = useState(false); // Panel Position State (x, y) const [dashboardPos, setDashboardPos] = useState({ x: 20, y: 100 }); const [historyPos, setHistoryPos] = useState({ x: 50, y: 150 }); const [mapPos, setMapPos] = useState({ x: 80, y: 200 }); const [showFeed, setShowFeed] = useState(true); const [isListening, setIsListening] = useState(false); const [persona, setPersona] = useState<"Guardian" | "Crimson" | "Zen">("Guardian"); const [actionCount, setActionCount] = useState(0); const [customTheme, setCustomTheme] = useState(null); const messagesEndRef = useRef(null); const recognitionRef = useRef(null); const themes = { Guardian: { color: "text-neon-blue", border: "border-neon-blue", bg: "bg-neon-blue", grad: "from-neon-blue" }, Crimson: { color: "text-red-500", border: "border-red-500", bg: "bg-red-500", grad: "from-red-600" }, Zen: { color: "text-green-500", border: "border-green-500", bg: "bg-green-500", grad: "from-green-500" }, }; const activeTheme = customTheme || themes[persona]; // Drag Sensors const sensors = useSensors( useSensor(PointerSensor, { activationConstraint: { distance: 8, }, }) ); const handleDragEnd = (event: DragEndEvent) => { const { active, delta } = event; const id = active.id as string; if (id === 'dashboard-panel') { setDashboardPos(prev => ({ x: prev.x + delta.x, y: prev.y + delta.y })); } else if (id === 'history-panel') { setHistoryPos(prev => ({ x: prev.x + delta.x, y: prev.y + delta.y })); } else if (id === 'map-panel') { setMapPos(prev => ({ x: prev.x + delta.x, y: prev.y + delta.y })); } }; const handleThemeChange = (colorKey: string) => { const themeMap: any = { "neon-blue": themes.Guardian, "neon-purple": { color: "text-purple-500", border: "border-purple-500", bg: "bg-purple-500", grad: "from-purple-600" }, "neon-green": themes.Zen, "amber-500": { color: "text-yellow-500", border: "border-yellow-500", bg: "bg-yellow-500", grad: "from-yellow-600" } }; if (themeMap[colorKey]) setCustomTheme(themeMap[colorKey]); }; const scrollToBottom = () => { messagesEndRef.current?.scrollIntoView({ behavior: "smooth" }); }; useEffect(() => { scrollToBottom(); }, [messages]); useEffect(() => { if ('webkitSpeechRecognition' in window || 'SpeechRecognition' in window) { const SpeechRecognition = (window as any).SpeechRecognition || (window as any).webkitSpeechRecognition; recognitionRef.current = new SpeechRecognition(); recognitionRef.current.continuous = false; recognitionRef.current.interimResults = false; recognitionRef.current.onresult = (event: any) => { const transcript = event.results[0][0].transcript; setInput(transcript); setIsListening(false); }; recognitionRef.current.onerror = (event: any) => { console.error("Speech error", event); setIsListening(false); }; recognitionRef.current.onend = () => { setIsListening(false); }; } }, []); const toggleListening = () => { if (isListening) { recognitionRef.current?.stop(); } else { recognitionRef.current?.start(); setIsListening(true); } }; const speak = (text: string) => { if ('speechSynthesis' in window) { const utterance = new SpeechSynthesisUtterance(text); utterance.pitch = persona === "Crimson" ? 0.8 : persona === "Zen" ? 0.9 : 1; utterance.rate = persona === "Crimson" ? 1.2 : persona === "Zen" ? 0.8 : 1; window.speechSynthesis.speak(utterance); } }; const handleSendMessage = async (e?: React.FormEvent) => { e?.preventDefault(); if (!input.trim() || isLoading) return; const userMessage: Message = { id: Date.now(), text: input, sender: "user", timestamp: new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }), }; setMessages((prev) => [...prev, userMessage]); setInput(""); setIsLoading(true); setActionCount(prev => prev + 1); try { const response = await axios.post("/api/ai/chat", { message: userMessage.text, }); const aiResponseText = response.data.response; const aiMessage: Message = { id: Date.now() + 1, text: aiResponseText, sender: "ai", timestamp: new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }), }; setMessages((prev) => [...prev, aiMessage]); speak(aiResponseText); axios.post("/api/auth/history", { userId, feedback: userMessage.text, aiResponse: aiResponseText, sentiment: "Neutral" }).catch(e => console.error("Failed to save history", e)); } catch (error) { console.error("Chat error:", error); const errorMessage: Message = { id: Date.now() + 1, text: "Network link unstable. Please retry transmission.", sender: "ai", timestamp: new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }), }; setMessages((prev) => [...prev, errorMessage]); } finally { setIsLoading(false); } }; return ( {/* Live Feed Overlay */} {showFeed && } {/* Background Ambience */} {/* Header */} RIDEAI System Online setShowMap(!showMap)} className={cn("p-2 rounded-lg transition-colors border group mr-2", showMap ? `bg-white/10 ${activeTheme.border} ${activeTheme.color}` : "border-transparent hover:bg-white/5 text-gray-400 hover:text-white")} title="Holographic Map" > setShowDashboard(!showDashboard)} className={cn("p-2 rounded-lg transition-colors border group mr-2", showDashboard ? `bg-white/10 ${activeTheme.border} ${activeTheme.color}` : "border-transparent hover:bg-white/5 text-gray-400 hover:text-white")} title="Toggle Analytics" > setShowHistory(!showHistory)} className={cn("p-2 rounded-lg transition-colors border group mr-2", showHistory ? `bg-white/10 ${activeTheme.border} ${activeTheme.color}` : "border-transparent hover:bg-white/5 text-gray-400 hover:text-white")} title="Neural Memory Bank" > setShowFeed(!showFeed)} className={cn("p-2 rounded-lg transition-colors border group mr-2", showFeed ? `bg-white/10 ${activeTheme.border} ${activeTheme.color}` : "border-transparent hover:bg-white/5 text-gray-400 hover:text-white")} title="Toggle Network Feed" > {/* Draggable Panels Area */} {showDashboard && ( )} {showHistory && ( )} {showMap && ( )} {/* Messages Area */} {messages.map((msg) => ( {msg.sender === "user" ? : } {msg.text} {msg.timestamp} ))} {isLoading && ( )} {/* Input Area */} {isListening ? ( ) : ( )} setInput(e.target.value)} placeholder={isListening ? "Listening..." : "Enter ride details for analysis..."} className={`w-full bg-deep-card border border-deep-border text-white placeholder-gray-500 rounded-xl py-4 pl-6 pr-12 focus:outline-none focus:${activeTheme.border}/50 focus:ring-1 focus:ring-neon-blue/30 transition-all duration-300 shadow-inner`} disabled={isLoading} /> ); }; export default ChatInterface;
{msg.text}