Add loading indicator and complete dark mode support for Maria chatbot
- Add professional loading spinner with "Se încarcă răspunsul..." text - Implement full dark mode support for loading overlay and API response containers - Add automatic theme detection for Flowise chatbot initialization - Add inline script to set theme before page render for instant visibility - Simplify theme toggle to reload page and reinitialize chatbot with new theme - Prevent textarea search when page is called with GET parameter - Fix theme toggle visibility in dark mode 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -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;
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
||||
<!-- Set theme BEFORE page renders to prevent flash and ensure toggle visibility -->
|
||||
<script>
|
||||
(function() {
|
||||
const theme = localStorage.getItem('theme') || 'light';
|
||||
if (theme === 'dark') {
|
||||
document.documentElement.classList.add('dark-mode');
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
</head>
|
||||
<body class="min-h-screen">
|
||||
<!-- Soft Professional Blue Header -->
|
||||
@@ -400,7 +538,39 @@
|
||||
// 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,6 +711,7 @@
|
||||
|
||||
// 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";
|
||||
@@ -547,6 +721,7 @@
|
||||
history: []
|
||||
};
|
||||
|
||||
console.log('🔄 Sending request to API...');
|
||||
const controller = new AbortController();
|
||||
const timeoutId = setTimeout(() => controller.abort(), 30000);
|
||||
|
||||
@@ -562,16 +737,20 @@
|
||||
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.";
|
||||
|
||||
@@ -610,6 +789,59 @@
|
||||
}, 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() {
|
||||
return new Promise((resolve) => {
|
||||
@@ -875,6 +1107,12 @@
|
||||
|
||||
// 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
|
||||
@@ -902,6 +1140,12 @@
|
||||
|
||||
// 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(() => {
|
||||
@@ -912,6 +1156,8 @@
|
||||
});
|
||||
|
||||
// Verifică periodic dacă butonul există și îl adaugă dacă lipsește
|
||||
// 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) {
|
||||
@@ -937,19 +1183,33 @@
|
||||
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');
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -965,30 +1225,24 @@
|
||||
const body = document.body;
|
||||
const html = document.documentElement;
|
||||
|
||||
// Check for saved theme preference or default to 'light'
|
||||
const currentTheme = localStorage.getItem('theme') || 'light';
|
||||
// Check if dark-mode was already set by inline script
|
||||
const isDarkMode = html.classList.contains('dark-mode');
|
||||
|
||||
if (currentTheme === 'dark') {
|
||||
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 = '<i data-lucide="moon" class="w-5 h-5"></i>';
|
||||
// Reinitialize icons to render the moon icon
|
||||
lucide.createIcons();
|
||||
}
|
||||
|
||||
themeToggle.addEventListener('click', function() {
|
||||
body.classList.toggle('dark-mode');
|
||||
html.classList.toggle('dark-mode');
|
||||
// Save the new theme preference
|
||||
const newTheme = body.classList.contains('dark-mode') ? 'light' : 'dark';
|
||||
localStorage.setItem('theme', newTheme);
|
||||
|
||||
// Update icon
|
||||
if (body.classList.contains('dark-mode')) {
|
||||
themeToggle.innerHTML = '<i data-lucide="moon" class="w-5 h-5"></i>';
|
||||
localStorage.setItem('theme', 'dark');
|
||||
} else {
|
||||
themeToggle.innerHTML = '<i data-lucide="sun" class="w-5 h-5"></i>';
|
||||
localStorage.setItem('theme', 'light');
|
||||
}
|
||||
|
||||
// Reinitialize icons
|
||||
lucide.createIcons();
|
||||
// Reload page to reinitialize chatbot with new theme
|
||||
window.location.reload();
|
||||
});
|
||||
|
||||
// Mobile menu toggle
|
||||
|
||||
Reference in New Issue
Block a user