Files
romfast-website/chatbot_maria.html
Claude Agent 6afd3840ec PDF QR: link separat pdfqr.roa.romfast.ro (nu qr)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-26 21:36:50 +00:00

1286 lines
52 KiB
HTML
Raw 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">
<meta name="robots" content="noindex, nofollow">
<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 -->
<!--NAV-START-->
<!-- ============ STICKY NAV (canonic — identic în toate paginile) ============ -->
<header class="professional-navbar sticky top-0 z-50">
<nav class="container mx-auto px-4 lg:px-10 py-3">
<div class="flex items-center justify-between gap-6">
<!-- Logo -->
<a href="/index.html" class="flex items-center gap-3">
<img src="/images/romfast_logo.png" alt="Romfast Logo" class="romfast-logo">
<span class="brand-tagline hidden md:inline">Aduce informația în mâinile tale</span>
</a>
<!-- Centered Navigation -->
<div class="hidden lg:flex items-center gap-7">
<div class="relative group">
<button class="nav-link flex items-center gap-1">
<span>ROA</span>
<i data-lucide="chevron-down" class="w-4 h-4"></i>
</button>
<div class="nav-dropdown absolute top-full left-0 mt-2 w-56 opacity-0 invisible group-hover:opacity-100 group-hover:visible transition-all duration-300 overflow-hidden">
<a href="/roa/aplicatii-erp.html" class="block px-4 py-3">Toate aplicațiile</a>
<a href="/roa/roa-financiar-contabilitate.html" class="block px-4 py-3">Financiar &amp; Contabilitate</a>
<a href="/roa/roa-gestiune.html" class="block px-4 py-3">Gestiune</a>
<a href="/roa/roa-facturare.html" class="block px-4 py-3">Facturare &amp; e-Factura</a>
<a href="/roa/roa-contracte-clienti.html" class="block px-4 py-3">Contracte clienți și furnizori</a>
<a href="/roa/roa-imobilizari.html" class="block px-4 py-3">Imobilizări</a>
<a href="/roa/roa-horeca.html" class="block px-4 py-3">HORECA</a>
<a href="/roa/roa-service-auto.html" class="block px-4 py-3">Service Auto</a>
<a href="/roa/roa-proiecte-devize.html" class="block px-4 py-3">Proiecte &amp; Devize</a>
</div>
</div>
<a href="/menu/servicii.html" class="nav-link">Servicii</a>
<a href="/menu/aplicatii-online.html" class="nav-link">Aplicații online</a>
<a href="/menu/referinte.html" class="nav-link">Referințe</a>
<a href="/menu/angajari.html" class="nav-link">Angajări</a>
<a href="/menu/contact.html" class="nav-cta">Contact</a>
</div>
<!-- Theme Toggle & Mobile Menu -->
<div class="flex items-center gap-2">
<button id="theme-toggle" class="p-2" aria-label="Comută tema">
<i data-lucide="sun" class="w-5 h-5"></i>
</button>
<button id="mobile-menu-toggle" class="lg:hidden p-2" style="color: var(--text-secondary);" aria-label="Meniu">
<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 mt-3 rounded-xl overflow-hidden" style="background: var(--surface); border: 1px solid var(--border);">
<div class="px-4 py-4 space-y-1">
<div class="pl-4 py-1 space-y-1" style="border-left: 2px solid var(--border);">
<p class="eyebrow">ROA</p>
<a href="/roa/aplicatii-erp.html" class="nav-link block py-1 text-sm">Toate aplicațiile</a>
<a href="/roa/roa-financiar-contabilitate.html" class="nav-link block py-1 text-sm">Financiar &amp; Contabilitate</a>
<a href="/roa/roa-gestiune.html" class="nav-link block py-1 text-sm">Gestiune</a>
<a href="/roa/roa-facturare.html" class="nav-link block py-1 text-sm">Facturare &amp; e-Factura</a>
<a href="/roa/roa-contracte-clienti.html" class="nav-link block py-1 text-sm">Contracte clienți și furnizori</a>
<a href="/roa/roa-imobilizari.html" class="nav-link block py-1 text-sm">Imobilizări</a>
<a href="/roa/roa-horeca.html" class="nav-link block py-1 text-sm">HORECA</a>
<a href="/roa/roa-service-auto.html" class="nav-link block py-1 text-sm">Service Auto</a>
<a href="/roa/roa-proiecte-devize.html" class="nav-link block py-1 text-sm">Proiecte &amp; Devize</a>
</div>
<a href="/menu/servicii.html" class="nav-link block py-2">Servicii</a>
<a href="/menu/aplicatii-online.html" class="nav-link block py-2">Aplicații online</a>
<a href="/menu/referinte.html" class="nav-link block py-2">Referințe</a>
<a href="/menu/angajari.html" class="nav-link block py-2">Angajări</a>
<a href="/menu/contact.html" class="nav-cta inline-block mt-2">Contact</a>
</div>
</div>
</nav>
</header>
<!--NAV-END-->
<!-- 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="site-footer">
<div class="max-w-6xl mx-auto px-4 lg:px-10 pt-12 pb-7">
<!-- Aplicații online -->
<div class="pb-6 footer-divider" style="border-bottom: 1px solid;">
<div class="flex flex-wrap gap-x-5 gap-y-2.5">
<a href="https://efactura.roa.romfast.ro" target="_blank" rel="noopener" title="Editor e-factură (XML)" class="inline-flex items-center gap-1.5 text-sm"><i data-lucide="file-text" class="w-4 h-4"></i>Editor Factură Electronică</a>
<a href="https://qr.roa.romfast.ro" target="_blank" rel="noopener" title="Generator coduri QR" class="inline-flex items-center gap-1.5 text-sm"><i data-lucide="qr-code" class="w-4 h-4"></i>QR Code Generator</a>
<a href="https://ico.roa.romfast.ro" target="_blank" rel="noopener" title="Generator iconuri" class="inline-flex items-center gap-1.5 text-sm"><i data-lucide="shapes" class="w-4 h-4"></i>Icon Generator</a>
<a href="https://pdfqr.roa.romfast.ro" target="_blank" rel="noopener" title="Generează cod QR și-l adaugă la PDF" class="inline-flex items-center gap-1.5 text-sm"><i data-lucide="scan-line" class="w-4 h-4"></i>PDF QR</a>
<a href="https://city-discovery.roa.romfast.ro" target="_blank" rel="noopener" title="Joc City Adventure Game" class="inline-flex items-center gap-1.5 text-sm"><i data-lucide="gamepad-2" class="w-4 h-4"></i>City Discovery</a>
<a href="https://space.roa.romfast.ro" target="_blank" rel="noopener" title="Rezervări spații" class="inline-flex items-center gap-1.5 text-sm"><i data-lucide="calendar-check" class="w-4 h-4"></i>Space Booking</a>
</div>
</div>
<div class="flex items-center justify-between gap-4 flex-wrap pt-5">
<span class="footer-muted text-sm">Copyright © 2025 Romfast SRL. Toate drepturile rezervate.</span>
<div class="flex flex-wrap gap-5">
<a href="/menu/politica-confidentialitate.html" class="footer-muted text-sm">Politica de confidențialitate</a>
</div>
</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>