import React, { useState, useEffect, useRef } from 'react'; import { useNavigate } from 'react-router-dom'; import { Bot, User, CheckCircle2, XCircle, Send, RefreshCcw, ShieldCheck, FileText, BotIcon, Zap, ImageIcon, ChevronRight, ArrowLeft, Mic, MicOff, Paperclip, Plus, Search, ListChecks, ArrowUpRight, Loader2, LifeBuoy } from 'lucide-react'; import { motion, AnimatePresence } from 'framer-motion'; import ReactMarkdown from 'react-markdown'; import remarkGfm from 'remark-gfm'; import useTicketStore from '../../store/ticketStore'; import { Card, CardContent } from "../../components/ui/card"; import { askAI } from '../../services/aiAssistant'; import useToastStore from '../../store/toastStore'; const AutoResolveChat = () => { const { aiTicket } = useTicketStore(); const navigate = useNavigate(); const [messages, setMessages] = useState([]); const [isThinking, setIsThinking] = useState(false); const [isFinal, setIsFinal] = useState(false); const [inputText, setInputText] = useState(''); const [isListening, setIsListening] = useState(false); const { showToast } = useToastStore(); const scrollRef = useRef(null); const fileInputRef = useRef(null); // Initial Plan Generation useEffect(() => { if (!aiTicket) { navigate('/create-ticket'); return; } // Update status for timeline sync if (aiTicket.status !== 'auto_resolve') { useTicketStore.getState().setAITicket({ ...aiTicket, status: 'auto_resolve' }); } const generateInitialPlan = async () => { setIsThinking(true); try { const prompt = `Based on the incident report, generate a 4-step troubleshooting plan. Focus on high-level actions. Format clearly with numbered steps 1-4.`; const response = await askAI(prompt, aiTicket, []); const lines = response.split('\n'); const newSteps = []; let id = 1; for (const line of lines) { if (id > 4) break; const trimmed = line.trim(); if (!trimmed || trimmed.length < 5) continue; const cleaned = trimmed .replace(/^\*{1,2}/, '') .replace(/\*{1,2}$/, '') .replace(/^#{1,3}\s*/, '') .replace(/^[\d]+[.)]\s*/, '') .replace(/^[-*•]\s*/, '') .replace(/^Step\s*\d+[:.)]\s*/i, '') .trim(); const isStep = /^\d+[.)]\s/.test(trimmed) || /^[-*•]\s/.test(trimmed) || /^\*{1,2}\d/.test(trimmed) || /^#{1,3}\s/.test(trimmed) || /^Step\s*\d/i.test(trimmed); if (isStep && cleaned.length > 5) { newSteps.push({ id: id++, task: cleaned, completed: false }); } } if (newSteps.length >= 2) { // Steps parsed successfully; welcome message sent below } else { const sentences = response .replace(/\*\*/g, '') .split(/[.\n]/) .map(s => s.trim()) .filter(s => s.length > 15) .slice(0, 4); if (sentences.length >= 2) { // Plan parsed but not rendered; welcome message sent below } else { throw new Error("Could not parse steps from AI response."); } } const now = new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }); setMessages([{ role: 'bot', text: `Hello! I've analyzed your request: "${aiTicket.summary}". I've put together a 4-step troubleshooting plan to resolve this. How would you like to start?`, timestamp: now }]); } catch (error) { console.error("AI Plan Generation Failed:", error); const now = new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }); setMessages([{ role: 'bot', text: `I've analyzed your request. I've prepared a standard troubleshooting plan for "${aiTicket.summary}". Let's start with the first step.`, timestamp: now }]); } finally { setIsThinking(false); } }; if (messages.length === 0) { generateInitialPlan(); } }, [aiTicket, navigate, messages.length]); useEffect(() => { if (scrollRef.current) { scrollRef.current.scrollTop = scrollRef.current.scrollHeight; } }, [messages, isThinking]); const handleSendMessage = async (textOverride, imageOverride = null) => { const text = textOverride || inputText; if (!text.trim() && !imageOverride) return; const now = new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }); const newUserMsg = { role: 'user', text: text, image: imageOverride, timestamp: now }; setMessages(prev => [...prev, newUserMsg]); setInputText(''); setIsThinking(true); try { const aiResponse = await askAI(text || "Sent an image for analysis", aiTicket, messages, imageOverride); const botNow = new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }); setMessages(prev => [...prev, { role: 'bot', text: aiResponse, timestamp: botNow }]); const lowerResponse = String(aiResponse || '').toLowerCase(); if (lowerResponse.includes("resolved") || lowerResponse.includes("successfully")) { setIsFinal(true); } } catch (error) { console.error("Troubleshooting Error:", error); const botNow = new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }); setMessages(prev => [...prev, { role: 'bot', text: "I'm having a bit of trouble concentrating. Could you try sending that again?", timestamp: botNow }]); } finally { setIsThinking(false); } }; const handleFileUpload = (e) => { const file = e.target.files[0]; if (file) { const reader = new FileReader(); reader.onloadend = () => { handleSendMessage("I've uploaded a screenshot for you to check.", reader.result); }; reader.readAsDataURL(file); } }; const toggleMic = () => { if (!('webkitSpeechRecognition' in window) && !('SpeechRecognition' in window)) { showToast('Voice interface unavailable: Browser lacks speech protocols.', 'warning'); return; } const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition; const recognition = new SpeechRecognition(); recognition.continuous = false; recognition.interimResults = false; recognition.lang = 'en-US'; recognition.onstart = () => setIsListening(true); recognition.onresult = (event) => setInputText(event.results[0][0].transcript); recognition.onerror = () => setIsListening(false); recognition.onend = () => setIsListening(false); if (isListening) recognition.stop(); else recognition.start(); }; if (!aiTicket) return null; return (
Neural Connection Active
:
),
strong: ({ ...props }) =>
}}
>
{msg.text}
Goal Satisfied?
Our AI is learning from your input to provide the most helpful response.