import { useState, useEffect, useRef } from "react"; import { FaUser, FaRobot, FaPaperPlane, FaCircle, FaPlus, FaPizzaSlice, FaInfoCircle, FaTimes, FaFilePdf, FaFileWord, FaDatabase, FaGlobe, FaSun, FaMoon } from "react-icons/fa"; import { MdOutlineContentCopy, MdCheck } from "react-icons/md"; import ReactMarkdown from "react-markdown"; import "./ChatBot.css"; const BASE_URL = "https://tanmaivan-hutmind.hf.space"; const TRANSLATIONS = { vi: { newChat: "Trò chuyện mới", welcomeTitle: "Tôi có thể giúp gì cho bạn?", welcomeSubtitle: "Hãy hỏi tôi về chính sách JRG, thông tin hệ thống Pizza Hut VN hoặc về Data Analytics Team.", knowledgeBaseBtn: "Dữ liệu thông tin", suggestion1: "Giới thiệu về team data analytics, ban quản lý là ai?", suggestion1Chip: "Giới thiệu Data Analytics", suggestion2: "Kể cho tôi nghe lịch sử hình thành của Pizza Hut!", suggestion2Chip: "Lịch sử Pizza Hut", suggestion3: "Data Analytics Team có ai làm việc ở nước ngoài không?", suggestion3Chip: "Data Analytics nước ngoài", suggestion4: "Tập đoàn JRG đang quản lý thương hiệu Pizza Hut ở những nước nào?", suggestion4Chip: "Quy mô JRG toàn cầu", inputPlaceholder: "Gửi tin nhắn cho HutMind...", footerDisclaimer: "HutMind có thể mắc lỗi. Vui lòng kiểm tra lại các thông tin quan trọng.", modalTitle: "Dữ liệu tham khảo", modalDescription: "Dưới đây là các tài liệu và nguồn dữ liệu hiện đang được HutMind sử dụng để trả lời câu hỏi của bạn:", errorConnection: "Đã xảy ra lỗi khi kết nối với chatbot. Vui lòng thử lại sau.", errorInit: "Lỗi khi khởi tạo lại chatbot", copyTooltip: "Sao chép", themeLight: "Chế độ sáng", themeDark: "Chế độ tối", sources: [ { name: "Thông tin về Data Analytics Team (JRGVN)", type: "database" }, { name: "Hướng dẫn Chính sách Bảo mật Thông tin (2024)", type: "pdf" }, { name: "Quy định Nghỉ phép và Nghỉ bệnh (PSL)", type: "pdf" }, { name: "Chính sách Công tác và Tiếp khách nước ngoài (2025)", type: "pdf" }, { name: "Chính sách Bảo mật Dữ liệu PHV (2023)", type: "word" }, { name: "Chỉ thị Ứng phó Sự cố CNTT (DRP)", type: "pdf" }, { name: "Danh mục từ viết tắt và Thuật ngữ chuyên môn", type: "word" }, { name: "Quy trình và Chính sách Phần mềm", type: "pdf" }, ] }, en: { newChat: "New Chat", welcomeTitle: "How can I help you today?", welcomeSubtitle: "Ask me about JRG policies, Pizza Hut VN systems, or the Data Analytics Team.", knowledgeBaseBtn: "Knowledge Base", suggestion1: "Intro to Data Analytics Team, who's the management?", suggestion1Chip: "Data Analytics Intro", suggestion2: "Tell me about the history of Pizza Hut!", suggestion2Chip: "Pizza Hut History", suggestion3: "Does anyone in the Data Analytics Team work overseas?", suggestion3Chip: "Overseas Data Analytics Team", suggestion4: "Which countries does JRG manage the Pizza Hut brand in?", suggestion4Chip: "JRG Global Scale", inputPlaceholder: "Message HutMind...", footerDisclaimer: "HutMind can make mistakes. Check important info.", modalTitle: "Reference Data", modalDescription: "Below are the documents and data sources currently being used by HutMind to answer your questions:", errorConnection: "An error occurred connecting to the chatbot. Please try again later.", errorInit: "Error re-initializing chatbot", copyTooltip: "Copy", themeLight: "Light mode", themeDark: "Dark mode", sources: [ { name: "Data Analytics Team Info (JRGVN)", type: "database" }, { name: "Info Security Policy Guidelines (2024)", type: "pdf" }, { name: "Leave & Sick Leave Regulations (PSL)", type: "pdf" }, { name: "Overseas Travel & Entertainment Policy (2025)", type: "pdf" }, { name: "PHV Data Privacy Policy (2023)", type: "word" }, { name: "IT Disaster Recovery Directive (DRP)", type: "pdf" }, { name: "Abbreviations & Technical Terms", type: "word" }, { name: "Software Policy & Procedure", type: "pdf" }, ] } }; const ChatBot = () => { const [messages, setMessages] = useState([]); const [userInput, setUserInput] = useState(""); const [isLoading, setIsLoading] = useState(false); const [copiedIndex, setCopiedIndex] = useState(null); const [showKnowledgeModal, setShowKnowledgeModal] = useState(false); const [language, setLanguage] = useState(() => localStorage.getItem("hutmind-lang") || "vi"); const [theme, setTheme] = useState(() => localStorage.getItem("hutmind-theme") || "light"); const chatWindowRef = useRef(null); const textareaRef = useRef(null); const t = (key) => TRANSLATIONS[language][key]; // Persist settings useEffect(() => { localStorage.setItem("hutmind-lang", language); }, [language]); useEffect(() => { localStorage.setItem("hutmind-theme", theme); if (theme === 'dark') { document.body.classList.add('dark'); } else { document.body.classList.remove('dark'); } }, [theme]); // Auto scroll useEffect(() => { if (chatWindowRef.current) { chatWindowRef.current.scrollTop = chatWindowRef.current.scrollHeight; } }, [messages]); // Adjust textarea height automatically useEffect(() => { if (textareaRef.current) { textareaRef.current.style.height = 'auto'; textareaRef.current.style.height = `${Math.min(textareaRef.current.scrollHeight, 200)}px`; } }, [userInput]); const handleSend = async () => { if (userInput.trim() === "" || isLoading) return; setMessages((prevMessages) => [ ...prevMessages, { sender: "user", text: userInput }, { sender: "bot", text: "..." }, ]); setUserInput(""); setIsLoading(true); if (textareaRef.current) { textareaRef.current.style.height = 'auto'; } try { const response = await fetch(`${BASE_URL}/process_query_stream/`, { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ query: userInput }), }); if (!response.ok) { throw new Error(t("errorConnection")); } const reader = response.body.getReader(); const decoder = new TextDecoder(); let botResponse = ""; let isFirstChunk = true; while (true) { const { done, value } = await reader.read(); if (done) break; const chunk = decoder.decode(value); botResponse += chunk; if (isFirstChunk) { setMessages((prevMessages) => [ ...prevMessages.filter((msg) => msg.text !== "..."), { sender: "bot", text: botResponse }, ]); isFirstChunk = false; } else { setMessages((prevMessages) => { const newMessages = [...prevMessages]; const lastMessageIndex = newMessages.length - 1; if (newMessages[lastMessageIndex].sender === "bot") { newMessages[lastMessageIndex].text = botResponse; } return newMessages; }); } } } catch (error) { console.error("Lỗi:", error); setMessages((prevMessages) => [ ...prevMessages.filter((msg) => msg.text !== "..."), { sender: "bot", text: t("errorConnection") }, ]); } finally { setIsLoading(false); } }; const handleNewChat = async () => { setMessages([]); setIsLoading(false); // Reset loading state try { const response = await fetch(`${BASE_URL}/newchat/`, { method: "GET", }); if (!response.ok) { throw new Error(t("errorInit")); } const data = await response.json(); console.log(data.message); } catch (error) { console.error("Lỗi:", error); } }; const copyToClipboard = (text, index) => { navigator.clipboard.writeText(text).then(() => { setCopiedIndex(index); setTimeout(() => setCopiedIndex(null), 2000); }); }; const getSourceIcon = (type) => { switch (type) { case "pdf": return ; case "word": return ; case "database": return ; default: return ; } }; const toggleLanguage = () => { setLanguage(prev => prev === "vi" ? "en" : "vi"); }; const toggleTheme = () => { setTheme(prev => prev === "light" ? "dark" : "light"); }; return (
Logo

HutMind

{messages.length === 0 && (
Mascot

{t("welcomeTitle")}

{t("welcomeSubtitle")}

)}
{messages.map((message, index) => { const lastBotIndex = [...messages].reverse().findIndex(m => m.sender === 'bot'); const actualLastBotIndex = lastBotIndex !== -1 ? messages.length - 1 - lastBotIndex : -1; const isLatestBotMessage = index === actualLastBotIndex; return (
{message.sender === "bot" && message.text === "..." ? (
) : message.sender === "bot" ? (
{message.text}
{message.text !== "..." && ( )}
) : (
{message.text}
)}
); })}