diff --git a/chatbot_maria.html b/chatbot_maria.html index d3aaf95..66741f7 100644 --- a/chatbot_maria.html +++ b/chatbot_maria.html @@ -222,8 +222,146 @@ .api-message-scrollable::-webkit-scrollbar-thumb:hover { background: #a8a8a8; } + + /* Professional loading overlay */ + .loading-overlay { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-color: rgba(255, 255, 255, 0.98); + backdrop-filter: blur(12px); + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + z-index: 99999; + opacity: 0; + transition: opacity 0.3s ease; + } + + .loading-overlay.show { + opacity: 1; + } + + .loading-spinner { + width: 56px; + height: 56px; + border: 5px solid rgba(59, 129, 246, 0.2); + border-top-color: #3B81F6; + border-radius: 50%; + animation: spin 1s ease-in-out infinite; + } + + @keyframes spin { + to { transform: rotate(360deg); } + } + + .loading-text { + margin-top: 24px; + font-size: 20px; + color: #000000 !important; + font-family: var(--font-sans) !important; + font-weight: 700; + text-shadow: none; + letter-spacing: 0.5px; + opacity: 1 !important; + } + + /* Dark mode support for loading overlay */ + .dark-mode .loading-overlay, + body.dark-mode .loading-overlay { + background-color: rgba(34, 40, 49, 0.98) !important; + } + + .dark-mode .loading-spinner, + body.dark-mode .loading-spinner { + border-color: rgba(59, 129, 246, 0.3) !important; + border-top-color: #5B9BFF !important; + } + + .dark-mode .loading-text, + body.dark-mode .loading-text { + color: #ffffff !important; + } + + /* Dark mode support for API message container */ + .dark-mode .api-message-container, + body.dark-mode .api-message-container { + background-color: rgba(34, 40, 49, 0.95) !important; + } + + .dark-mode .api-message-box, + body.dark-mode .api-message-box { + background: #2a2a2a !important; + border-color: #3a3a3a !important; + } + + .dark-mode .api-message-header, + body.dark-mode .api-message-header { + border-bottom-color: #3a3a3a !important; + } + + .dark-mode .api-message-title, + body.dark-mode .api-message-title { + color: #ffffff !important; + } + + .dark-mode .api-message-content, + body.dark-mode .api-message-content { + color: #e0e0e0 !important; + } + + .dark-mode .api-message-footer, + body.dark-mode .api-message-footer { + border-top-color: #3a3a3a !important; + } + + .dark-mode .user-query, + body.dark-mode .user-query { + background: #3a3a3a !important; + color: #e0e0e0 !important; + } + + .dark-mode .api-message-scrollable::-webkit-scrollbar-track, + body.dark-mode .api-message-scrollable::-webkit-scrollbar-track { + background: #2a2a2a !important; + } + + .dark-mode .api-message-scrollable::-webkit-scrollbar-thumb, + body.dark-mode .api-message-scrollable::-webkit-scrollbar-thumb { + background: #555555 !important; + } + + .dark-mode .api-message-scrollable::-webkit-scrollbar-thumb:hover, + body.dark-mode .api-message-scrollbar-thumb:hover { + background: #666666 !important; + } + + /* Dark mode support for header theme toggle button */ + .dark-mode #theme-toggle, + body.dark-mode #theme-toggle { + color: #ffffff !important; + opacity: 1 !important; + } + + .dark-mode #theme-toggle:hover, + body.dark-mode #theme-toggle:hover { + background-color: rgba(255, 255, 255, 0.2) !important; + } + + +
@@ -399,8 +537,40 @@ // Determină dacă să afișeze prompt-urile inițiale sau nu const starterPrompts = initialMessage ? [] : defaultStarterPrompts; - - // Inițializează chatbot-ul + + // Detectează tema activă (dark-mode sau light-mode) + const isDarkMode = document.body.classList.contains('dark-mode') || + document.documentElement.classList.contains('dark-mode') || + localStorage.getItem('theme') === 'dark'; + + console.log('🎨 Current theme:', isDarkMode ? 'dark' : 'light'); + + // Configurează culorile în funcție de temă + const themeColors = isDarkMode ? { + backgroundColor: "#2a2a2a", + botMessageBg: "#3a3a3a", + botMessageText: "#e0e0e0", + userMessageBg: "#5B9BFF", + userMessageText: "#ffffff", + inputBg: "#3a3a3a", + inputText: "#e0e0e0", + sendButtonColor: "#5B9BFF", + feedbackColor: "#e0e0e0", + footerTextColor: "#e0e0e0" + } : { + backgroundColor: "#ffffff", + botMessageBg: "#f7f8ff", + botMessageText: "#303235", + userMessageBg: "#3B81F6", + userMessageText: "#ffffff", + inputBg: "#ffffff", + inputText: "#303235", + sendButtonColor: "#3B81F6", + feedbackColor: "#303235", + footerTextColor: "#303235" + }; + + // Inițializează chatbot-ul cu tema detectată const chatbotInstance = Chatbot.initFull({ chatflowid: "d4911620-07fe-41f8-adb4-f2f52d6ec766", apiHost: "https://mutual-special-koala.ngrok-free.app", @@ -412,7 +582,7 @@ showAgentMessages: true, welcomeMessage: 'Bună! Eu sunt Maria, specialistul dvs. de suport tehnic Romfast. Sunt aici pentru a vă ajuta cu orice întrebări sau probleme tehnice legate de utilizarea sistemului ROA. Cum vă pot ajuta astăzi?', errorMessage: 'Am o eroare! Revino mai tarziu', - backgroundColor: "#ffffff", + backgroundColor: themeColors.backgroundColor, height: 700, width: 500, fontSize: 16, @@ -420,22 +590,22 @@ starterPromptFontSize: 15, clearChatOnReload: true, botMessage: { - backgroundColor: "#f7f8ff", - textColor: "#303235", + backgroundColor: themeColors.botMessageBg, + textColor: themeColors.botMessageText, showAvatar: true, avatarSrc: "https://raw.githubusercontent.com/zahidkhawaja/langchain-chat-nextjs/main/public/parroticon.png", }, userMessage: { - backgroundColor: "#3B81F6", - textColor: "#ffffff", + backgroundColor: themeColors.userMessageBg, + textColor: themeColors.userMessageText, showAvatar: true, avatarSrc: "https://raw.githubusercontent.com/zahidkhawaja/langchain-chat-nextjs/main/public/usericon.png", }, textInput: { placeholder: 'Scrieți întrebarea dvs.', - backgroundColor: '#ffffff', - textColor: '#303235', - sendButtonColor: '#3B81F6', + backgroundColor: themeColors.inputBg, + textColor: themeColors.inputText, + sendButtonColor: themeColors.sendButtonColor, maxChars: 2000, maxCharsWarningMessage: 'Ați depășit limita de caractere. Vă rugăm să introduceți mai puțin de 2000 de caractere.', autoFocus: true, @@ -443,10 +613,10 @@ receiveMessageSound: false, }, feedback: { - color: '#303235', + color: themeColors.feedbackColor, }, footer: { - textColor: '#303235', + textColor: themeColors.footerTextColor, text: 'Powered by', company: 'Romfast', companyLink: 'https://www.romfast.ro', @@ -457,6 +627,9 @@ // Funcție pentru a afișa răspunsul API direct în pagină function displayApiResponse(question, response) { + // Ascunde indicatorul de loading + hideLoadingIndicator(); + const container = document.createElement('div'); container.className = 'api-message-container'; @@ -538,18 +711,20 @@ // Funcție pentru a trimite mesajul direct prin API Flowise async function sendMessageViaAPI(message) { + console.log('📡 sendMessageViaAPI started'); try { const apiHost = "https://mutual-special-koala.ngrok-free.app"; const chatflowId = "d4911620-07fe-41f8-adb4-f2f52d6ec766"; - + const requestBody = { question: message, history: [] }; - + + console.log('🔄 Sending request to API...'); const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), 30000); - + const response = await fetch(`${apiHost}/api/v1/prediction/${chatflowId}`, { method: 'POST', headers: { @@ -558,27 +733,31 @@ body: JSON.stringify(requestBody), signal: controller.signal }); - + clearTimeout(timeoutId); - + if (response.ok) { + console.log('✅ API response received successfully'); const data = await response.json(); displayApiResponse(message, data); return data; } else { - console.error("Eroare la apelul API:", response.status); + console.error("❌ Eroare la apelul API:", response.status); displayApiResponse(message, `Ne pare rău, a apărut o eroare în procesarea cererii dumneavoastră (cod: ${response.status}). Vă rugăm să încercați din nou mai târziu.`); return null; } } catch (error) { - console.error("Eroare la trimiterea mesajului prin API:", error); - + console.error("❌ Eroare la trimiterea mesajului prin API:", error); + + // Ascunde indicatorul de loading în caz de eroare + hideLoadingIndicator(); + let errorMessage = "Ne pare rău, a apărut o eroare în conectarea la serviciile noastre. Vă rugăm să verificați conexiunea la internet și să încercați din nou."; - + if (error.name === 'AbortError') { errorMessage = "Request-ul a fost întrerupt din cauza timeout-ului. Vă rugăm să încercați din nou."; } - + displayApiResponse(message, errorMessage); return null; } @@ -589,17 +768,17 @@ const toast = document.createElement('div'); toast.className = 'toast'; toast.textContent = message; - + if (type === 'error') { toast.classList.add('error'); } - + document.body.appendChild(toast); - + setTimeout(() => { toast.classList.add('show'); }, 100); - + setTimeout(() => { toast.classList.remove('show'); setTimeout(() => { @@ -609,6 +788,59 @@ }, 300); }, 3000); } + + // Funcție pentru a afișa indicatorul de loading + function showLoadingIndicator() { + console.log('🔄 showLoadingIndicator called'); + + // Verifică dacă există deja un indicator de loading + if (document.querySelector('.loading-overlay')) { + console.log('⚠️ Loading overlay already exists'); + return; + } + + const overlay = document.createElement('div'); + overlay.className = 'loading-overlay'; + overlay.id = 'loading-indicator'; + + const spinner = document.createElement('div'); + spinner.className = 'loading-spinner'; + + const text = document.createElement('div'); + text.className = 'loading-text'; + text.textContent = 'Se încarcă răspunsul...'; + + overlay.appendChild(spinner); + overlay.appendChild(text); + document.body.appendChild(overlay); + + console.log('✅ Loading overlay created and added to DOM'); + + // Activează animația de fade-in + setTimeout(() => { + overlay.classList.add('show'); + console.log('✅ Loading overlay faded in'); + }, 10); + } + + // Funcție pentru a ascunde indicatorul de loading + function hideLoadingIndicator() { + console.log('🔽 hideLoadingIndicator called'); + + const overlay = document.getElementById('loading-indicator'); + if (overlay) { + overlay.classList.remove('show'); + console.log('✅ Loading overlay fade-out started'); + setTimeout(() => { + if (document.body.contains(overlay)) { + document.body.removeChild(overlay); + console.log('✅ Loading overlay removed from DOM'); + } + }, 300); + } else { + console.log('⚠️ No loading overlay found to hide'); + } + } // Funcție pentru a găsi textarea-ul chatbot-ului cu MutationObserver function waitForChatbotInput() { @@ -875,24 +1107,30 @@ // Inițializează adăugarea butonului când pagina se încarcă window.addEventListener('load', () => { + // Nu căuta textarea dacă pagina a fost apelată cu parametru GET + if (initialMessage && initialMessage.trim() !== '') { + console.log('ℹ️ Page called with GET parameter - skipping textarea search'); + return; + } + if (linkButtonAdded) return; - + // Încearcă imediat addLinkButtonToInput(); - + // Încearcă din nou doar dacă butonul nu a fost adăugat setTimeout(() => { if (!linkButtonAdded) { addLinkButtonToInput(); } }, 2000); - + setTimeout(() => { if (!linkButtonAdded) { addLinkButtonToInput(); } }, 5000); - + setTimeout(() => { if (!linkButtonAdded) { addLinkButtonToInput(); @@ -902,8 +1140,14 @@ // Adaugă și un event listener pentru DOMContentLoaded document.addEventListener('DOMContentLoaded', () => { + // Nu căuta textarea dacă pagina a fost apelată cu parametru GET + if (initialMessage && initialMessage.trim() !== '') { + console.log('ℹ️ DOMContentLoaded - Page called with GET parameter - skipping textarea search'); + return; + } + if (linkButtonAdded) return; - + setTimeout(() => { if (!linkButtonAdded) { addLinkButtonToInput(); @@ -912,44 +1156,60 @@ }); // Verifică periodic dacă butonul există și îl adaugă dacă lipsește - intervalId = setInterval(() => { - // Oprește intervalul dacă butonul a fost adăugat - if (linkButtonAdded) { - clearInterval(intervalId); - intervalId = null; - return; - } - - // Verifică dacă butonul există - const existingButton = document.querySelector('.link-button-small'); - if (!existingButton) { - // Verifică dacă textarea-ul există - const textarea = document.querySelector('textarea') || - document.querySelector('input[type="text"]'); - - if (textarea && textarea.offsetParent !== null) { - addLinkButtonToInput(); + // Nu rula intervalul dacă pagina a fost apelată cu parametru GET + if (!initialMessage || initialMessage.trim() === '') { + intervalId = setInterval(() => { + // Oprește intervalul dacă butonul a fost adăugat + if (linkButtonAdded) { + clearInterval(intervalId); + intervalId = null; + return; } - } else { - // Dacă butonul există, marchează că a fost adăugat și oprește intervalul - linkButtonAdded = true; - clearInterval(intervalId); - intervalId = null; - } - }, 3000); // Verifică la fiecare 3 secunde + + // Verifică dacă butonul există + const existingButton = document.querySelector('.link-button-small'); + if (!existingButton) { + // Verifică dacă textarea-ul există + const textarea = document.querySelector('textarea') || + document.querySelector('input[type="text"]'); + + if (textarea && textarea.offsetParent !== null) { + addLinkButtonToInput(); + } + } else { + // Dacă butonul există, marchează că a fost adăugat și oprește intervalul + linkButtonAdded = true; + clearInterval(intervalId); + intervalId = null; + } + }, 3000); // Verifică la fiecare 3 secunde + } else { + console.log('ℹ️ Periodic interval - Page called with GET parameter - skipping textarea search'); + } // Dacă există un mesaj inițial, trimite-l direct prin API if (initialMessage) { + console.log('📩 Initial message detected:', initialMessage.substring(0, 100) + '...'); if (initialMessage.trim() !== '') { + console.log('✅ Message is valid, proceeding with API call'); + const chatbotElement = document.querySelector('flowise-fullchatbot'); if (chatbotElement) { chatbotElement.style.display = 'none'; + console.log('✅ Chatbot element hidden'); } - + + // Afișează indicatorul de loading + console.log('🔄 About to show loading indicator...'); + showLoadingIndicator(); + + console.log('📡 Sending message to API...'); sendMessageViaAPI(initialMessage); } else { console.error("❌ Mesajul initial este gol după decodare"); } + } else { + console.log('ℹ️ No initial message parameter detected'); } @@ -959,36 +1219,30 @@ document.addEventListener('DOMContentLoaded', function() { // Initialize Lucide icons lucide.createIcons(); - + // Theme toggle const themeToggle = document.getElementById('theme-toggle'); const body = document.body; const html = document.documentElement; - - // Check for saved theme preference or default to 'light' - const currentTheme = localStorage.getItem('theme') || 'light'; - - if (currentTheme === 'dark') { + + // Check if dark-mode was already set by inline script + const isDarkMode = html.classList.contains('dark-mode'); + + if (isDarkMode) { + // Dark mode already set by inline script, just add to body and update icon body.classList.add('dark-mode'); - html.classList.add('dark-mode'); themeToggle.innerHTML = ''; - } - - themeToggle.addEventListener('click', function() { - body.classList.toggle('dark-mode'); - html.classList.toggle('dark-mode'); - - // Update icon - if (body.classList.contains('dark-mode')) { - themeToggle.innerHTML = ''; - localStorage.setItem('theme', 'dark'); - } else { - themeToggle.innerHTML = ''; - localStorage.setItem('theme', 'light'); - } - - // Reinitialize icons + // Reinitialize icons to render the moon icon lucide.createIcons(); + } + + themeToggle.addEventListener('click', function() { + // Save the new theme preference + const newTheme = body.classList.contains('dark-mode') ? 'light' : 'dark'; + localStorage.setItem('theme', newTheme); + + // Reload page to reinitialize chatbot with new theme + window.location.reload(); }); // Mobile menu toggle