Files
romfast-website/chatbot_maria.html
Marius Mutu 971744b66c 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>
2025-10-15 16:22:33 +03:00

1262 lines
51 KiB
HTML
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="ro">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Maria ChatBot - Romfast Suport</title>
<meta name="description" content="Maria ChatBot - Specialist suport tehnic Romfast pentru ROA. Răspunsuri expert la întrebări despre sistem și link-uri partajabile.">
<!-- Tailwind CSS -->
<script src="https://cdn.tailwindcss.com"></script>
<!-- Flowbite -->
<script src="https://cdn.jsdelivr.net/npm/flowbite@2.0.0/dist/flowbite.min.js"></script>
<!-- Lucide Icons -->
<script src="https://unpkg.com/lucide@latest/dist/umd/lucide.min.js"></script>
<!-- Google Fonts -->
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=Merriweather:wght@300;400;700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
<!-- Professional Theme CSS -->
<link href="professional-theme.css" rel="stylesheet">
<style>
/* Enhanced professional styling for Maria's advanced features */
body {
font-family: var(--font-sans) !important;
background: var(--background) !important;
color: var(--foreground) !important;
letter-spacing: var(--tracking-normal) !important;
line-height: 1.6 !important;
}
/* Professional styling for link generation button */
.link-button-small {
background: transparent !important;
border: none !important;
padding: 0 !important;
margin: 0 0 0 8px !important;
height: 56px !important;
width: 56px !important;
display: flex !important;
align-items: center !important;
justify-content: center !important;
cursor: pointer !important;
transition: all 0.2s ease !important;
border-radius: var(--radius) !important;
font-size: 18px !important;
color: var(--primary) !important;
opacity: 0.8 !important;
flex-shrink: 0 !important;
}
.link-button-small:hover {
background-color: var(--card-hover) !important;
opacity: 1 !important;
transform: scale(1.05) !important;
color: var(--secondary) !important;
}
.link-button-small:active {
transform: scale(0.95) !important;
}
.link-button-small.success {
color: var(--accent) !important;
background-color: rgba(184, 85, 85, 0.1) !important;
}
/* Professional toast notifications */
.toast {
position: fixed;
top: 20px;
right: 20px;
background: var(--primary) !important;
color: var(--primary-foreground) !important;
padding: 12px 20px;
border-radius: var(--radius-lg) !important;
z-index: 10000;
opacity: 0;
transform: translateX(100%);
transition: all 0.3s ease;
box-shadow: var(--shadow-lg) !important;
font-size: 14px;
font-family: var(--font-sans) !important;
}
.toast.show {
opacity: 1;
transform: translateX(0);
}
.toast.error {
background: var(--accent) !important;
color: var(--accent-foreground) !important;
}
/* Professional API message container */
.api-message-container {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(250, 251, 252, 0.95);
backdrop-filter: blur(8px);
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
z-index: 9999;
padding: 20px;
box-sizing: border-box;
}
.api-message-box {
background: var(--card) !important;
border-radius: var(--radius-xl) !important;
box-shadow: var(--shadow-2xl) !important;
border: 1px solid var(--border) !important;
max-width: 600px;
width: 90%;
max-height: 90vh;
display: flex;
flex-direction: column;
overflow: hidden;
}
.api-message-header {
display: flex;
align-items: center;
padding: 20px 20px 10px 20px;
border-bottom: 1px solid var(--border) !important;
flex-shrink: 0;
}
.api-message-avatar {
width: 40px;
height: 40px;
border-radius: 50%;
margin-right: 15px;
background: var(--primary) !important;
display: flex;
align-items: center;
justify-content: center;
color: var(--primary-foreground) !important;
font-weight: bold;
font-size: 18px;
}
.api-message-title {
font-size: 18px;
font-weight: bold;
color: var(--foreground) !important;
font-family: var(--font-sans) !important;
}
.api-message-scrollable {
flex: 1;
overflow-y: auto;
padding: 0 20px;
}
.api-message-content {
font-size: 16px;
line-height: 1.6;
color: var(--foreground) !important;
white-space: pre-wrap;
word-wrap: break-word;
padding: 15px 0;
font-family: var(--font-sans) !important;
}
.api-message-footer {
padding: 20px;
display: flex;
justify-content: flex-end;
border-top: 1px solid var(--border) !important;
flex-shrink: 0;
}
.api-message-button {
background: var(--button-gradient) !important;
color: var(--primary-foreground) !important;
border: none;
border-radius: var(--radius-lg) !important;
padding: 12px 24px;
font-size: 14px;
font-weight: 600;
cursor: pointer;
transition: opacity 0.2s;
font-family: var(--font-sans) !important;
}
.api-message-button:hover {
opacity: 0.9;
}
.user-query {
background: var(--muted) !important;
color: var(--muted-foreground) !important;
padding: 10px 15px;
border-radius: var(--radius-lg) !important;
margin-bottom: 15px;
display: inline-block;
max-width: 100%;
font-size: 15px;
word-wrap: break-word;
font-family: var(--font-sans) !important;
}
.api-message-scrollable::-webkit-scrollbar {
width: 8px;
}
.api-message-scrollable::-webkit-scrollbar-track {
background: #f1f1f1;
border-radius: 4px;
}
.api-message-scrollable::-webkit-scrollbar-thumb {
background: #c1c1c1;
border-radius: 4px;
}
.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 -->
<header class="professional-navbar sticky top-0 z-50">
<nav class="container mx-auto px-4 py-4">
<div class="flex items-center justify-between">
<!-- Logo Section -->
<div class="flex items-center space-x-3">
<img src="images/romfast_logo.png" alt="Romfast Logo" class="romfast-logo">
<div class="hidden md:block">
<p class="text-white text-sm italic font-light">Aduce informația în mâinile tale</p>
</div>
</div>
<!-- Centered Navigation -->
<div class="hidden lg:flex items-center space-x-8">
<a href="index.html" class="text-white hover:text-blue-200 transition-colors font-medium nav-link">Prima pagina</a>
<a href="menu/desprenoi.html" class="text-white hover:text-blue-200 transition-colors nav-link">Despre noi</a>
<a href="roa/aplicatii-erp.html" class="text-white hover:text-blue-200 transition-colors nav-link">ROA</a>
<div class="relative group">
<button class="text-white hover:text-blue-200 transition-colors flex items-center space-x-1 nav-link">
<span>Servicii</span>
<i data-lucide="chevron-down" class="w-4 h-4"></i>
</button>
<div class="absolute top-full left-0 mt-2 w-48 bg-white rounded-lg shadow-lg border border-gray-200 opacity-0 invisible group-hover:opacity-100 group-hover:visible transition-all duration-300">
<a href="menu/analiza.html" class="block px-4 py-3 text-gray-700 hover:bg-blue-50 hover:text-blue-800 rounded-t-lg">Analiza</a>
<a href="menu/implementare.html" class="block px-4 py-3 text-gray-700 hover:bg-blue-50 hover:text-blue-800">Implementare</a>
<a href="menu/roa-suport-tehnic.html" class="block px-4 py-3 text-gray-700 hover:bg-blue-50 hover:text-blue-800">Suport tehnic ROA</a>
<a href="menu/alteservicii.html" class="block px-4 py-3 text-gray-700 hover:bg-blue-50 hover:text-blue-800 rounded-b-lg">Alte servicii</a>
</div>
</div>
<a href="menu/referinte.html" class="text-white hover:text-blue-200 transition-colors nav-link">Referinte</a>
<a href="menu/angajari.html" class="text-white hover:text-blue-200 transition-colors nav-link">Angajari</a>
<a href="menu/contact.html" class="text-white hover:text-blue-200 transition-colors nav-link">Contact</a>
</div>
<!-- Theme Toggle & Mobile Menu -->
<div class="flex items-center space-x-4">
<button id="theme-toggle" class="text-white p-2 rounded-lg hover:bg-white hover:bg-opacity-20 transition-all duration-300">
<i data-lucide="sun" class="w-5 h-5"></i>
</button>
<button id="mobile-menu-toggle" class="lg:hidden text-white p-2 rounded-lg hover:bg-white hover:bg-opacity-20 transition-all duration-300">
<i data-lucide="menu" class="w-6 h-6" id="menu-icon"></i>
<i data-lucide="x" class="w-6 h-6 hidden" id="close-icon"></i>
</button>
</div>
</div>
<!-- Mobile Menu -->
<div id="mobile-menu" class="lg:hidden hidden bg-white border-t border-gray-200 shadow-lg">
<div class="px-4 py-4 space-y-3">
<a href="index.html" class="block text-gray-700 hover:text-blue-600 transition-colors font-medium py-2">Prima pagina</a>
<a href="menu/desprenoi.html" class="block text-gray-700 hover:text-blue-600 transition-colors py-2">Despre noi</a>
<a href="roa/aplicatii-erp.html" class="block text-gray-700 hover:text-blue-600 transition-colors py-2">ROA</a>
<div class="border-l-2 border-gray-300 pl-4 space-y-2">
<p class="text-gray-600 font-medium text-sm">Servicii</p>
<a href="menu/analiza.html" class="block text-gray-600 hover:text-blue-600 transition-colors py-1 text-sm">Analiza</a>
<a href="menu/implementare.html" class="block text-gray-600 hover:text-blue-600 transition-colors py-1 text-sm">Implementare</a>
<a href="menu/roa-suport-tehnic.html" class="block text-gray-600 hover:text-blue-600 transition-colors py-1 text-sm">Suport tehnic ROA</a>
<a href="menu/alteservicii.html" class="block text-gray-600 hover:text-blue-600 transition-colors py-1 text-sm">Alte servicii</a>
</div>
<a href="menu/referinte.html" class="block text-gray-700 hover:text-blue-600 transition-colors py-2">Referinte</a>
<a href="menu/angajari.html" class="block text-gray-700 hover:text-blue-600 transition-colors py-2">Angajari</a>
<a href="menu/contact.html" class="block text-gray-700 hover:text-blue-600 transition-colors py-2">Contact</a>
</div>
</div>
</nav>
</header>
<!-- Main Content Container -->
<main class="container mx-auto px-4 py-8 max-w-7xl">
<!-- Chatbot Container -->
<div class="professional-card p-8 max-w-4xl mx-auto">
<!-- Flowise Chatbot -->
<div class="flex justify-center">
<flowise-fullchatbot></flowise-fullchatbot>
</div>
</div>
</main>
<!-- Professional Footer -->
<footer class="professional-navbar mt-16">
<div class="container mx-auto px-4 py-8 text-center">
<div class="text-white">
<p class="mb-2">&copy; 2025 Romfast. Toate drepturile rezervate.</p>
<p class="text-sm opacity-80">ROA - Romfast Applications - Sisteme informatice pentru managementul afacerii</p>
</div>
</div>
</footer>
<script type="module">
import Chatbot from "https://cdn.jsdelivr.net/npm/flowise-embed/dist/web.js"
// Funcție pentru a obține parametrii din URL
function getUrlParameter(name) {
try {
const urlParams = new URLSearchParams(window.location.search);
const value = urlParams.get(name);
if (value === null) {
return '';
}
return value;
} catch (error) {
console.error("Eroare cu URLSearchParams:", error);
try {
name = name.replace(/[\[]/, '\\[').replace(/[\]]/, '\\]');
var regex = new RegExp('[\\?&]' + name + '=([^&#]*)');
var results = regex.exec(location.search);
if (results === null) {
return '';
}
let rawValue = results[1];
function safeDecodeURIComponent(str) {
str = str.replace(/\+/g, ' ');
const chunks = str.split('%');
let result = chunks[0];
for (let i = 1; i < chunks.length; i++) {
const chunk = chunks[i];
if (chunk.length >= 2) {
const hexCode = chunk.substring(0, 2);
const rest = chunk.substring(2);
if (/^[0-9A-Fa-f]{2}$/.test(hexCode)) {
try {
result += decodeURIComponent('%' + hexCode) + rest;
} catch (e) {
result += '%' + chunk;
}
} else {
result += '%' + chunk;
}
} else {
result += '%' + chunk;
}
}
return result;
}
const decoded = safeDecodeURIComponent(rawValue);
return decoded;
} catch (fallbackError) {
console.error("Eroare și la fallback sigur:", fallbackError);
return '';
}
}
}
// Obține mesajul inițial din parametrul URL (dacă există)
const initialMessage = getUrlParameter('message');
// Definește prompt-urile inițiale standard
const defaultStarterPrompts = [
'Cum se actualizeaza tokenul eFactura?',
'Cand se completeaza codul de plata pentru declaratia SAFT?',
'Cum se configurează o politică de prețuri? Afișeaza pașii complet',
'Care sunt pasii pentru emiterea unei facturi service auto?',
'Cum se valideaza o comanda service auto?',
'Cum se deschide o comanda service auto?',
'Cum se modifica datele initiale ale unei comenzi service auto (ex: nr. km)?'
];
// Determină dacă să afișeze prompt-urile inițiale sau nu
const starterPrompts = initialMessage ? [] : defaultStarterPrompts;
// 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",
theme: {
chatWindow: {
showTitle: true,
title: 'Maria ChatBot - Romfast Suport',
titleAvatarSrc: 'https://raw.githubusercontent.com/walkxcode/dashboard-icons/main/svg/google-messages.svg',
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: themeColors.backgroundColor,
height: 700,
width: 500,
fontSize: 16,
starterPrompts: starterPrompts,
starterPromptFontSize: 15,
clearChatOnReload: true,
botMessage: {
backgroundColor: themeColors.botMessageBg,
textColor: themeColors.botMessageText,
showAvatar: true,
avatarSrc: "https://raw.githubusercontent.com/zahidkhawaja/langchain-chat-nextjs/main/public/parroticon.png",
},
userMessage: {
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: 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,
sendMessageSound: false,
receiveMessageSound: false,
},
feedback: {
color: themeColors.feedbackColor,
},
footer: {
textColor: themeColors.footerTextColor,
text: 'Powered by',
company: 'Romfast',
companyLink: 'https://www.romfast.ro',
}
}
}
});
// 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';
const messageBox = document.createElement('div');
messageBox.className = 'api-message-box';
const header = document.createElement('div');
header.className = 'api-message-header';
const avatar = document.createElement('div');
avatar.className = 'api-message-avatar';
avatar.textContent = 'M';
const title = document.createElement('div');
title.className = 'api-message-title';
title.textContent = 'Maria - Asistent Romfast';
header.appendChild(avatar);
header.appendChild(title);
const scrollableArea = document.createElement('div');
scrollableArea.className = 'api-message-scrollable';
const userQuery = document.createElement('div');
userQuery.className = 'user-query';
userQuery.textContent = question;
const content = document.createElement('div');
content.className = 'api-message-content';
content.textContent = response.text || response;
scrollableArea.appendChild(userQuery);
scrollableArea.appendChild(content);
const footer = document.createElement('div');
footer.className = 'api-message-footer';
const button = document.createElement('button');
button.className = 'api-message-button';
button.textContent = 'Continuă conversația';
button.onclick = function() {
container.style.opacity = '0';
setTimeout(() => {
container.remove();
}, 300);
const chatHistory = [{
message: question,
type: 'userMessage'
}, {
message: response.text || response,
type: 'apiMessage'
}];
try {
localStorage.setItem('flowise-chatbot-history', JSON.stringify(chatHistory));
} catch (e) {
console.error('Eroare la salvarea istoricului în localStorage:', e);
}
const url = new URL(window.location.href);
url.searchParams.delete('message');
window.location.href = url.toString();
};
footer.appendChild(button);
messageBox.appendChild(header);
messageBox.appendChild(scrollableArea);
messageBox.appendChild(footer);
container.appendChild(messageBox);
document.body.appendChild(container);
setTimeout(() => {
container.style.opacity = '1';
}, 10);
}
// 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: {
'Content-Type': 'application/json'
},
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);
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);
// 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;
}
}
// Funcție pentru a afișa toast-ul
function showToast(message, type = 'success') {
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(() => {
if (document.body.contains(toast)) {
document.body.removeChild(toast);
}
}, 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() {
return new Promise((resolve) => {
// Mai multe selectori posibili
const selectors = [
'textarea',
'input[type="text"]',
'input',
'textarea[placeholder*="Scrieți întrebarea"]',
'textarea[placeholder*="Type your message"]',
'.text-input',
'[contenteditable="true"]',
'[class*="text-input"]',
'[class*="textarea"]',
'[class*="input"]'
];
// Funcție pentru a căuta în shadow DOM
function searchInShadowDOM() {
const chatbotElement = document.querySelector('flowise-fullchatbot');
if (chatbotElement && chatbotElement.shadowRoot) {
for (const selector of selectors) {
const elements = chatbotElement.shadowRoot.querySelectorAll(selector);
for (const element of elements) {
if (element.offsetParent !== null && !element.disabled && !element.readOnly) {
return element;
}
}
}
}
return null;
}
// Încearcă să găsească textarea-ul imediat
for (const selector of selectors) {
const elements = document.querySelectorAll(selector);
for (const element of elements) {
if (element.offsetParent !== null && !element.disabled && !element.readOnly) {
resolve(element);
return;
}
}
}
// Caută în shadow DOM
const shadowElement = searchInShadowDOM();
if (shadowElement) {
resolve(shadowElement);
return;
}
// Dacă nu îl găsește, folosește MutationObserver
const observer = new MutationObserver((mutations) => {
// Caută în DOM normal
for (const selector of selectors) {
const elements = document.querySelectorAll(selector);
for (const element of elements) {
if (element.offsetParent !== null && !element.disabled && !element.readOnly) {
observer.disconnect();
resolve(element);
return;
}
}
}
// Caută în shadow DOM
const shadowElement = searchInShadowDOM();
if (shadowElement) {
observer.disconnect();
resolve(shadowElement);
return;
}
});
observer.observe(document.body, {
childList: true,
subtree: true
});
// Timeout după 15 secunde
setTimeout(() => {
observer.disconnect();
console.error('❌ Nu s-a găsit textarea-ul chatbot-ului în 15 secunde');
resolve(null);
}, 15000);
});
}
// Variabilă globală pentru a urmări dacă butonul a fost adăugat
let linkButtonAdded = false;
let intervalId = null;
// Funcție pentru a adăuga butonul mic lângă butonul de send
async function addLinkButtonToInput() {
// Dacă butonul a fost deja adăugat, nu mai continua
if (linkButtonAdded) {
return;
}
const textarea = await waitForChatbotInput();
if (!textarea) {
console.error('❌ Nu s-a găsit textarea-ul chatbot-ului');
return;
}
// Găsește containerul cu textarea
let inputContainer = textarea.parentElement;
let attempts = 0;
// Caută containerul potrivit mergând în sus prin DOM
while (inputContainer && attempts < 10) {
if (inputContainer.classList.contains('flex') ||
inputContainer.style.display === 'flex' ||
inputContainer.querySelector('button')) {
break;
}
inputContainer = inputContainer.parentElement;
attempts++;
}
if (!inputContainer) {
console.error('❌ Nu s-a găsit containerul input-ului');
return;
}
// Verifică dacă butonul există deja
if (inputContainer.querySelector('.link-button-small')) {
linkButtonAdded = true;
// Oprește intervalul dacă rulează
if (intervalId) {
clearInterval(intervalId);
intervalId = null;
}
return;
}
// Creează butonul mic
const linkButton = document.createElement('button');
linkButton.className = 'link-button-small';
linkButton.innerHTML = '🔗';
linkButton.title = 'Generează link pentru această întrebare';
linkButton.type = 'button';
// Adaugă stiluri inline pentru a fi sigur că se aplică în Shadow DOM
linkButton.style.cssText = `
background: transparent !important;
border: none !important;
padding: 0 !important;
margin: 0 0 0 8px !important;
height: 56px !important;
width: 56px !important;
display: flex !important;
align-items: center !important;
justify-content: center !important;
cursor: pointer !important;
transition: all 0.2s ease !important;
border-radius: 6px !important;
font-size: 18px !important;
color: #3B81F6 !important;
opacity: 0.8 !important;
flex-shrink: 0 !important;
z-index: 1000 !important;
`;
// Adaugă event listener pentru hover
linkButton.addEventListener('mouseenter', () => {
linkButton.style.backgroundColor = 'rgba(59, 129, 246, 0.1)';
linkButton.style.opacity = '1';
linkButton.style.transform = 'scale(1.05)';
});
linkButton.addEventListener('mouseleave', () => {
linkButton.style.backgroundColor = 'transparent';
linkButton.style.opacity = '0.8';
linkButton.style.transform = 'scale(1)';
});
// Adaugă event listener pentru click
linkButton.addEventListener('click', (e) => {
e.preventDefault();
e.stopPropagation();
generateLinkFromInput(textarea, linkButton);
});
// Adaugă butonul în container
inputContainer.appendChild(linkButton);
console.log('✅ Butonul de link a fost adăugat cu succes');
// Marchează că butonul a fost adăugat
linkButtonAdded = true;
// Oprește intervalul dacă rulează
if (intervalId) {
clearInterval(intervalId);
intervalId = null;
}
// Verifică dacă butonul este vizibil (debugging final)
setTimeout(() => {
const rect = linkButton.getBoundingClientRect();
if (rect.width > 0 && rect.height > 0) {
console.log('✅ Butonul este vizibil și funcțional');
} else {
console.warn('⚠️ Butonul a fost adăugat dar nu este vizibil');
}
}, 100);
}
// Funcție pentru generarea linkului din input
function generateLinkFromInput(textarea, button) {
const message = textarea.value.trim();
if (!message) {
showToast('Vă rugăm să introduceți o întrebare în casuta de chat mai întâi!', 'error');
return;
}
// Generează URL-ul
const url = `${window.location.origin}${window.location.pathname}?message=${encodeURIComponent(message)}`;
// Feedback vizual pe buton
const originalHTML = button.innerHTML;
button.innerHTML = '✓';
button.classList.add('success');
// Copiază în clipboard
navigator.clipboard.writeText(url).then(() => {
showToast('Link-ul a fost copiat în clipboard!');
// Resetează butonul după 2 secunde
setTimeout(() => {
button.innerHTML = originalHTML;
button.classList.remove('success');
}, 2000);
}).catch(() => {
// Fallback pentru browsere mai vechi
const textArea = document.createElement('textarea');
textArea.value = url;
textArea.style.position = 'fixed';
textArea.style.opacity = '0';
document.body.appendChild(textArea);
textArea.focus();
textArea.select();
try {
document.execCommand('copy');
showToast('Link-ul a fost copiat în clipboard!');
} catch (err) {
showToast('Eroare la copierea linkului. Vă rugăm să încercați din nou.', 'error');
}
document.body.removeChild(textArea);
// Resetează butonul după 2 secunde
setTimeout(() => {
button.innerHTML = originalHTML;
button.classList.remove('success');
}, 2000);
});
}
// 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();
}
}, 10000);
});
// 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();
}
}, 1000);
});
// 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) {
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();
}
} 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');
}
</script>
<!-- Professional Theme JavaScript -->
<script>
// Theme toggle functionality
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 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');
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() {
// 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
const mobileMenuToggle = document.getElementById('mobile-menu-toggle');
const mobileMenu = document.getElementById('mobile-menu');
const menuIcon = document.getElementById('menu-icon');
const closeIcon = document.getElementById('close-icon');
mobileMenuToggle.addEventListener('click', function() {
mobileMenu.classList.toggle('hidden');
menuIcon.classList.toggle('hidden');
closeIcon.classList.toggle('hidden');
});
});
</script>
</body>
</html>