templates/functionality/chatbot/chatbot.html.twig line 1

Open in your IDE?
  1. <!-- Chatbot button -->
  2. <div id="chatbot-button" class="chatbot-button">
  3. <i class="far fa-comment-dots"></i>
  4. </div>
  5. <!-- Chatbot popup -->
  6. <div id="chatbot-popup" class="chatbot-popup">
  7. <div class="chatbot-header">
  8. <div class="chatbot-header-title">
  9. <i class="fas fa-circle-info"></i>
  10. </div>
  11. <div class="chatbot-header-title">
  12. <span>{{ 'chatbot.title'|trans({}, 'chatbot')|default('Assistència') }}</span>
  13. </div>
  14. <div class="chatbot-header-actions">
  15. <button class="chatbot-clear-button" title="{{ 'chatbot.clear_conversation'|trans({}, 'chatbot')|default('Borrar conversación') }}">
  16. <i class="fas fa-trash-alt"></i>
  17. </button>
  18. <div class="chatbot-header-close">
  19. <i class="fas fa-times"></i>
  20. </div>
  21. </div>
  22. </div>
  23. <div class="chatbot-content">
  24. <div class="chatbot-messages">
  25. {% if conversation is defined and conversation|length > 0 %}
  26. {# Mostrar conversación previa desde la sesión #}
  27. {% for message in conversation %}
  28. <div class="chatbot-message chatbot-message-{{ message.type }}">
  29. <div class="chatbot-message-icon">
  30. <i class="fas {% if message.type == 'user' %}fa-user{% else %}fa-circle-info{% endif %}"></i>
  31. </div>
  32. <div class="chatbot-message-content">
  33. <p>{{ message.message }}</p>
  34. </div>
  35. </div>
  36. {% endfor %}
  37. {% else %}
  38. {# Mensaje de bienvenida y temas de ayuda si no hay conversación previa #}
  39. <div class="chatbot-message chatbot-message-bot">
  40. <div class="chatbot-message-icon">
  41. <i class="fas fa-circle-info"></i>
  42. </div>
  43. <div class="chatbot-message-content">
  44. <p>{{ 'chatbot.welcome'|trans({}, 'chatbot')|default('Hola! En què puc ajudar-te avui?') }}</p>
  45. </div>
  46. </div>
  47. {# <div class="chatbot-help-topics">#}
  48. {# <div class="chatbot-help-topic" data-query="Com puc trobar un curs?">#}
  49. {# <div class="chatbot-help-topic-icon">#}
  50. {# <i class="fas fa-circle-question"></i>#}
  51. {# </div>#}
  52. {# <div class="chatbot-help-topic-content">#}
  53. {# <p>{{ 'chatbot.help.find_course'|trans({}, 'chatbot')|default('Com puc trobar un curs?') }}</p>#}
  54. {# </div>#}
  55. {# </div>#}
  56. {# <div class="chatbot-help-topic" data-query="Informació sobre inscripcions">#}
  57. {# <div class="chatbot-help-topic-icon">#}
  58. {# <i class="fas fa-circle-info"></i>#}
  59. {# </div>#}
  60. {# <div class="chatbot-help-topic-content">#}
  61. {# <p>{{ 'chatbot.help.inscriptions'|trans({}, 'chatbot')|default('Informació sobre inscripcions') }}</p>#}
  62. {# </div>#}
  63. {# </div>#}
  64. {# <div class="chatbot-help-topic" data-query="Com contactar amb un punt d'orientació?">#}
  65. {# <div class="chatbot-help-topic-icon">#}
  66. {# <i class="fas fa-comments"></i>#}
  67. {# </div>#}
  68. {# <div class="chatbot-help-topic-content">#}
  69. {# <p>{{ 'chatbot.help.orientation'|trans({}, 'chatbot')|default('Com contactar amb un punt d\'orientació?') }}</p>#}
  70. {# </div>#}
  71. {# </div>#}
  72. {# </div>#}
  73. {% endif %}
  74. </div>
  75. <div class="chatbot-input">
  76. <input type="text" placeholder="{{ 'chatbot.input_placeholder'|trans({}, 'chatbot')|default('Escriu la teva pregunta...') }}">
  77. <button type="button">
  78. <i class="fas fa-paper-plane"></i>
  79. </button>
  80. </div>
  81. </div>
  82. </div>
  83. <script type="text/javascript">
  84. document.addEventListener('DOMContentLoaded', function() {
  85. // Debug: Check if we have cookies
  86. console.log('Cookies available:', document.cookie);
  87. // Manual check for conversation cookie and display
  88. function getCookieValue(cookieName) {
  89. const name = cookieName + "=";
  90. const decodedCookie = decodeURIComponent(document.cookie);
  91. const cookieArray = decodedCookie.split(';');
  92. for (let i = 0; i < cookieArray.length; i++) {
  93. let cookie = cookieArray[i].trim();
  94. if (cookie.indexOf(name) === 0) {
  95. return cookie.substring(name.length, cookie.length);
  96. }
  97. }
  98. return "";
  99. }
  100. const chatbotCookie = getCookieValue('chatbot_conversation');
  101. console.log('Chatbot cookie found:', chatbotCookie ? 'Yes' : 'No');
  102. if (chatbotCookie) {
  103. try {
  104. // Try to decode the cookie value
  105. const decodedValue = atob(chatbotCookie);
  106. const conversation = JSON.parse(decodedValue);
  107. console.log('Parsed conversation:', conversation);
  108. // If the server didn't render the conversation, let's do it manually
  109. const chatbotMessages = document.querySelector('.chatbot-messages');
  110. const helpTopics = document.querySelector('.chatbot-help-topics');
  111. if (conversation && conversation.length > 0 && helpTopics) {
  112. console.log('Rendering conversation from cookie...');
  113. // Clear existing welcome message
  114. chatbotMessages.innerHTML = '';
  115. // Add messages from conversation
  116. conversation.forEach(message => {
  117. const messageDiv = document.createElement('div');
  118. messageDiv.className = `chatbot-message chatbot-message-${message.type}`;
  119. const iconDiv = document.createElement('div');
  120. iconDiv.className = 'chatbot-message-icon';
  121. const icon = document.createElement('i');
  122. if (message.type === 'user') {
  123. icon.className = 'fas fa-user';
  124. } else {
  125. icon.className = 'fas fa-circle-info';
  126. }
  127. iconDiv.appendChild(icon);
  128. const contentDiv = document.createElement('div');
  129. contentDiv.className = 'chatbot-message-content';
  130. const paragraph = document.createElement('p');
  131. // For bot messages, process Markdown-like formatting and preserve line breaks
  132. if (message.type === 'bot') {
  133. // Convert ** text ** to <strong>text</strong>
  134. const formattedMessage = message.message
  135. .replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>')
  136. .replace(/\n/g, '<br>');
  137. paragraph.innerHTML = formattedMessage;
  138. } else {
  139. // For user messages, keep using textContent for security
  140. paragraph.textContent = message.message;
  141. }
  142. contentDiv.appendChild(paragraph);
  143. messageDiv.appendChild(iconDiv);
  144. messageDiv.appendChild(contentDiv);
  145. chatbotMessages.appendChild(messageDiv);
  146. });
  147. // Hide help topics since we have a conversation
  148. helpTopics.style.display = 'none';
  149. }
  150. } catch (error) {
  151. console.error('Error parsing conversation cookie:', error);
  152. }
  153. }
  154. const chatbotButton = document.getElementById('chatbot-button');
  155. const chatbotPopup = document.getElementById('chatbot-popup');
  156. const chatbotClose = document.querySelector('.chatbot-header-close');
  157. const chatbotClear = document.querySelector('.chatbot-clear-button');
  158. const chatbotMessages = document.querySelector('.chatbot-messages');
  159. const chatbotInput = document.querySelector('.chatbot-input input');
  160. const chatbotSendButton = document.querySelector('.chatbot-input button');
  161. const chatbotHelpTopics = document.querySelectorAll('.chatbot-help-topic');
  162. // Toggle chatbot popup
  163. chatbotButton.addEventListener('click', function() {
  164. chatbotPopup.classList.toggle('open');
  165. if (chatbotPopup.classList.contains('open')) {
  166. chatbotInput.focus();
  167. }
  168. });
  169. // Close chatbot popup
  170. chatbotClose.addEventListener('click', function() {
  171. chatbotPopup.classList.remove('open');
  172. });
  173. // Clear conversation
  174. chatbotClear.addEventListener('click', function() {
  175. // Send request to clear the conversation
  176. fetch('{{ path("chatbot_clear") }}', {
  177. method: 'POST',
  178. })
  179. .then(response => response.json())
  180. .then(data => {
  181. if (data.success) {
  182. // Clear all messages in the UI
  183. chatbotMessages.innerHTML = '';
  184. // Add welcome message back
  185. const welcomeMessage = document.createElement('div');
  186. welcomeMessage.className = 'chatbot-message chatbot-message-bot';
  187. welcomeMessage.innerHTML = `
  188. <div class="chatbot-message-icon">
  189. <i class="fas fa-circle-info"></i>
  190. </div>
  191. <div class="chatbot-message-content">
  192. <p>{{ 'chatbot.welcome'|trans({}, 'chatbot')|default('Hola! En què puc ajudar-te avui?') }}</p>
  193. </div>
  194. `;
  195. chatbotMessages.appendChild(welcomeMessage);
  196. // Add help topics back
  197. const helpTopics = document.createElement('div');
  198. helpTopics.className = 'chatbot-help-topics';
  199. helpTopics.innerHTML = ``;
  200. {#helpTopics.innerHTML = `#}
  201. {# <div class="chatbot-help-topic" data-query="Com puc trobar un curs?">#}
  202. {# <div class="chatbot-help-topic-icon">#}
  203. {# <i class="fas fa-circle-question"></i>#}
  204. {# </div>#}
  205. {# <div class="chatbot-help-topic-content">#}
  206. {# <p>{{ 'chatbot.help.find_course'|trans({}, 'chatbot')|default('Com puc trobar un curs?') }}</p>#}
  207. {# </div>#}
  208. {# </div>#}
  209. {# <div class="chatbot-help-topic" data-query="Informació sobre inscripcions">#}
  210. {# <div class="chatbot-help-topic-icon">#}
  211. {# <i class="fas fa-circle-info"></i>#}
  212. {# </div>#}
  213. {# <div class="chatbot-help-topic-content">#}
  214. {# <p>{{ 'chatbot.help.inscriptions'|trans({}, 'chatbot')|default('Informació sobre inscripcions') }}</p>#}
  215. {# </div>#}
  216. {# </div>#}
  217. {# <div class="chatbot-help-topic" data-query="Com contactar amb un punt d'orientació?">#}
  218. {# <div class="chatbot-help-topic-icon">#}
  219. {# <i class="fas fa-comments"></i>#}
  220. {# </div>#}
  221. {# <div class="chatbot-help-topic-content">#}
  222. {# <p>{{ 'chatbot.help.orientation'|trans({}, 'chatbot')|default('Com contactar amb un punt d\'orientació?') }}</p>#}
  223. {# </div>#}
  224. {# </div>#}
  225. {#`;#}
  226. chatbotMessages.appendChild(helpTopics);
  227. // Re-attach event listeners to new help topics
  228. document.querySelectorAll('.chatbot-help-topic').forEach(topic => {
  229. topic.addEventListener('click', function() {
  230. const query = this.getAttribute('data-query');
  231. chatbotInput.value = query;
  232. sendMessage(query);
  233. });
  234. });
  235. }
  236. })
  237. .catch(error => {
  238. console.error('Error clearing conversation:', error);
  239. });
  240. });
  241. // Handle help topic clicks
  242. chatbotHelpTopics.forEach(topic => {
  243. topic.addEventListener('click', function() {
  244. const query = this.getAttribute('data-query');
  245. chatbotInput.value = query;
  246. sendMessage(query);
  247. });
  248. });
  249. // Send message on button click
  250. chatbotSendButton.addEventListener('click', function() {
  251. sendMessage(chatbotInput.value);
  252. });
  253. // Send message on Enter key press
  254. chatbotInput.addEventListener('keypress', function(e) {
  255. if (e.key === 'Enter') {
  256. sendMessage(chatbotInput.value);
  257. }
  258. });
  259. // Function to send message to the backend
  260. function sendMessage(message) {
  261. if (!message.trim()) return;
  262. // Add user message to chat
  263. addMessage(message, 'user');
  264. // Clear input
  265. chatbotInput.value = '';
  266. // Show loading indicator
  267. addLoadingMessage();
  268. // Send request to backend
  269. fetch('{{ path("chatbot_query") }}', {
  270. method: 'POST',
  271. headers: {
  272. 'Content-Type': 'application/json',
  273. },
  274. body: JSON.stringify({ query: message }),
  275. })
  276. .then(response => response.json())
  277. .then(data => {
  278. // Remove loading indicator
  279. removeLoadingMessage();
  280. if (data.success) {
  281. // Add bot response to chat
  282. const botResponse = data.data.respuesta || data.data.response || '{{ 'chatbot.no_response'|trans({}, 'chatbot')|default('No s ha trobat una resposta.') }}';
  283. addMessage(botResponse, 'bot');
  284. // Check if we received an updated conversation from the server
  285. if (data.conversation) {
  286. console.log('Updated conversation received from server');
  287. }
  288. } else {
  289. // Handle error
  290. addMessage('{{ 'chatbot.error.processing'|trans({}, 'chatbot')|default('Ho sento, no he pogut processar la teva pregunta. Si us plau, intenta-ho de nou més tard.') }}', 'bot error');
  291. console.error('Error:', data.error);
  292. }
  293. })
  294. .catch(error => {
  295. // Remove loading indicator
  296. removeLoadingMessage();
  297. // Handle network error
  298. addMessage('{{ 'chatbot.error.connection'|trans({}, 'chatbot')|default('Hi ha hagut un problema de connexió. Si us plau, intenta-ho de nou més tard.') }}', 'bot error');
  299. console.error('Error:', error);
  300. });
  301. }
  302. // Function to add a message to the chat
  303. function addMessage(message, type) {
  304. const messageDiv = document.createElement('div');
  305. messageDiv.className = `chatbot-message chatbot-message-${type}`;
  306. const iconDiv = document.createElement('div');
  307. iconDiv.className = 'chatbot-message-icon';
  308. const icon = document.createElement('i');
  309. if (type === 'user') {
  310. icon.className = 'fas fa-user';
  311. } else {
  312. icon.className = 'fas fa-circle-info';
  313. }
  314. iconDiv.appendChild(icon);
  315. const contentDiv = document.createElement('div');
  316. contentDiv.className = 'chatbot-message-content';
  317. const paragraph = document.createElement('p');
  318. // For bot messages, process Markdown-like formatting and preserve line breaks
  319. if (type.includes('bot')) {
  320. // Convert ** text ** to <strong>text</strong>
  321. const formattedMessage = message
  322. .replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>')
  323. .replace(/\n/g, '<br>');
  324. paragraph.innerHTML = formattedMessage;
  325. } else {
  326. // For user messages, keep using textContent for security
  327. paragraph.textContent = message;
  328. }
  329. contentDiv.appendChild(paragraph);
  330. messageDiv.appendChild(iconDiv);
  331. messageDiv.appendChild(contentDiv);
  332. // Hide help topics after first user message
  333. if (type === 'user') {
  334. const helpTopics = document.querySelector('.chatbot-help-topics');
  335. if (helpTopics) {
  336. helpTopics.style.display = 'none';
  337. }
  338. }
  339. chatbotMessages.appendChild(messageDiv);
  340. // Scroll to bottom
  341. chatbotMessages.scrollTop = chatbotMessages.scrollHeight;
  342. }
  343. // Function to add loading indicator
  344. function addLoadingMessage() {
  345. const loadingDiv = document.createElement('div');
  346. loadingDiv.className = 'chatbot-message chatbot-message-bot chatbot-loading';
  347. const iconDiv = document.createElement('div');
  348. iconDiv.className = 'chatbot-message-icon';
  349. const icon = document.createElement('i');
  350. icon.className = 'fas fa-circle-info';
  351. iconDiv.appendChild(icon);
  352. const contentDiv = document.createElement('div');
  353. contentDiv.className = 'chatbot-message-content';
  354. const loadingDots = document.createElement('div');
  355. loadingDots.className = 'chatbot-loading-dots';
  356. for (let i = 0; i < 3; i++) {
  357. const dot = document.createElement('span');
  358. loadingDots.appendChild(dot);
  359. }
  360. contentDiv.appendChild(loadingDots);
  361. loadingDiv.appendChild(iconDiv);
  362. loadingDiv.appendChild(contentDiv);
  363. chatbotMessages.appendChild(loadingDiv);
  364. // Scroll to bottom
  365. chatbotMessages.scrollTop = chatbotMessages.scrollHeight;
  366. }
  367. // Function to remove loading indicator
  368. function removeLoadingMessage() {
  369. const loadingMessage = document.querySelector('.chatbot-loading');
  370. if (loadingMessage) {
  371. loadingMessage.remove();
  372. }
  373. }
  374. });
  375. </script>