[BUILD-426] feat: Update chatbot element style (#2156)

[BUILD-426] feat: Update chatbot element style (#2156)

Co-authored-by: Heitor Carvalho <heitor@labcodes.com.br>
diff --git a/ankihub/templates/ai/chatbot.html b/ankihub/templates/ai/chatbot.html
index 0521ed2..147341d 100644
--- a/ankihub/templates/ai/chatbot.html
+++ b/ankihub/templates/ai/chatbot.html
@@ -12,70 +12,132 @@
 {% endblock css %}
 
 {% block content %}
-  <div class="ml-5 mt-5 mr-5">
-    <div class="container mx-auto p-4">
-      <h1 class="text-3xl font-bold mb-6 text-center">Chatbot</h1>
-
-      <div class="bg-white rounded-lg shadow-lg p-6">
-          <div id="output" class="h-80 overflow-y-auto mb-4 p-4 bg-gray-50 border border-gray-200 rounded"></div>
-
-          <form id="postForm" class="flex">
-              <input type="text" id="questionData" name="question" class="flex-1 p-2 border border-gray-300 rounded-l-md focus:outline-none focus:ring-2 focus:ring-blue-500" placeholder="Type your message..." required>
-              <button type="submit" class="p-2 bg-blue-500 text-white rounded-r-md hover:bg-blue-600 focus:outline-none focus:ring-2 focus:ring-blue-500">Send</button>
-          </form>
-      </div>
-  </div>
-
-  <script>
-      document.getElementById('postForm').addEventListener('submit', async function(event) {
-          event.preventDefault();
-
-          const outputElement = document.getElementById('output');
-          const inputData = document.getElementById('questionData');
-          const question = inputData.value;
-          inputData.value = '';
-
-          outputElement.innerHTML += `<div class="mb-2"><strong>You:</strong> ${question}</div>`;
-
-          const formData = new FormData();
-          formData.append('question', question);
-
-          try {
-              const response = await fetch(`{% url "ai:stream_chatbot_response" %}`, {
-                  method: 'POST',
-                  headers: {
-                      'Content-Type': 'application/x-www-form-urlencoded',
-                      'X-CSRFToken': `{{ csrf_token }}`
-                  },
-                  body: new URLSearchParams(formData)
-              });
-              if (!response.ok) {
-                  throw new Error('Network response was not ok');
-              }
-
-              const reader = response.body.getReader();
-              const decoder = new TextDecoder();
-
-              let botMessage = document.createElement('div');
-              botMessage.classList.add('mb-2');
-              const strongElement = document.createElement('strong');
-              strongElement.textContent = 'Bot: ';
-
-              botMessage.appendChild(strongElement);
-              outputElement.appendChild(botMessage)
-              while (true) {
+<div class="min-h-screen w-full flex justify-center px-4 sm:px-2" x-data="{ inputClicked: false }">
+
+    <main class="py-2 flex flex-col justify-between w-full overflow-x-hidden">
+        <div id="chatbot-chat-container" class="h-full mt-10 mb-32 w-full flex flex-col container mx-auto">
+            <template id="question-template">
+                <div class="user-question flex px-2 items-end mb-3">
+                    <div class="text-gray-800 dark:text-gray-200 bg-indigo-50 dark:bg-indigo-950 mr-3 w-full flex flex-col py-3 px-4 rounded-tr-[20px] rounded-tl-[20px] rounded-bl-[20px]">
+                        <h4 class="mb-2 font-extrabold text-indigo-600 dark:text-indigo-400">QUESTION</h4>
+                        <span class="question-content"></span>
+                    </div>
+                    <div class="w-10 h-10 rounded-full bg-indigo-50 dark:bg-indigo-950 flex items-center justify-center">
+                        {% Icons icon="academic-cap" class="h-6 w-6 text-indigo-600 dark:text-indigo-400" %}
+                    </div>
+                </div>
+            </template>
+
+            <template id="answer-template">
+                <div class="bot-answer flex flex-row-reverse px-2 mb-3 items-end">
+                    <div class="text-gray-800 dark:text-gray-200 bg-purple-50 dark:bg-purple-950 ml-3 w-full flex flex-col py-3 px-4 rounded-tr-[20px] rounded-tl-[20px] rounded-br-[20px]">
+                        <h4 class="mb-2 font-extrabold text-purple-600 dark:text-purple-400">ANSWER</h4>
+                        <div class="flex flex-col">
+                            <div class="loading-dots flex items-center space-x-1 animate-pulse">
+                                <div class="w-2 h-2 bg-purple-600 dark:bg-purple-400 rounded-full"></div>
+                                <div class="w-2 h-2 bg-purple-600 dark:bg-purple-400 rounded-full"></div>
+                                <div class="w-2 h-2 bg-purple-600 dark:bg-purple-400 rounded-full"></div>
+                            </div>
+                            <span class="answer-content"></span>
+                            <span class="ankihub-powered-text text-purple-600 dark:text-purple-400 font-medium text-xs leading-5 mt-5 hidden">Generated from Anking flashcard content</span>
+                        </div>
+                    </div>
+                    <div class="w-10 h-10 rounded-full bg-purple-50 dark:bg-purple-950 flex items-center justify-center">
+                        {% Icons icon="sparkles" class="h-6 w-6 text-purple-600 dark:text-purple-400" %}
+                    </div>
+                </div>
+            </template>
+        </div>
+    </main>
+
+    <footer class="fixed bottom-0 w-full flex flex-col container mx-auto px-4 sm:px-2">
+        <div class="bg-white dark:bg-gray-950 w-full flex flex-col">
+            <form id="postForm" x-data="{ questionContent: '' }" :class="{ 'border border-indigo-600 dark:border-indigo-400': inputClicked }" class="flex items-center py-2 px-5 rounded-xl bg-gray-100 dark:bg-gray-900">
+                <label for="chatbot-user-input">
+                    {% Icons icon="academic-cap" class="h-6 w-6 text-gray-400 dark:text-gray-600 mr-3" %}
+                </label>
+                <input id="chatbot-user-input" x-model="questionContent" @click="inputClicked = true" @click.away="inputClicked = false" type="text" name="question" class="text-gray-950 dark:text-gray-50 w-full bg-gray-100 dark:bg-gray-900 focus:outline-none placeholder-gray-400 dark:placeholder-gray-600" placeholder="Ask any follow up question..." required>
+                <button id="chatbot-btn-submit" type="submit" :disabled="!questionContent" class="text-white bg-indigo-600 dark:bg-indigo-400 p-[10px] rounded disabled:text-gray-500 disabled:bg-gray-200 dark:disabled:bg-gray-800">
+                    {% Icons icon="arrow-up" class="h-5 w-5" %}
+                </button>
+            </form>
+            <div class="flex justify-center p-2 text-gray-500 text-xs leading-5">
+                <span class="flex items-center">{%Icons icon="ankihub-logo" class="h-4 w-4 mr-2" %}Powered by AnkiHub</span>
+            </div>
+        </div>
+    </footer>
+
+</div>
+
+<script>
+    document.getElementById('postForm').addEventListener('submit', async function(event) {
+        event.preventDefault();
+        let html = document.documentElement;
+
+        const inputData = document.getElementById('chatbot-user-input');
+        const question = inputData.value;
+        inputData.value = '';
+
+        const submitButton = document.getElementById('chatbot-btn-submit');
+        submitButton.disabled = true
+
+        const formData = new FormData();
+        formData.append('question', question);
+
+        const chatContainer = document.getElementById('chatbot-chat-container');
+        const questionTemplate = document.getElementById("question-template");
+
+        const questionMessage = questionTemplate.content.cloneNode(true);
+        questionMessageContent = questionMessage.querySelector('.question-content')
+        questionMessageContent.innerHTML = question
+
+        chatContainer.appendChild(questionMessage)
+        html.scrollTop += chatContainer.lastElementChild.scrollHeight;
+
+        const answerTemplate = document.getElementById("answer-template");

[... diff too long, it was truncated ...]

GitHub
sha: d08292676bfea96d050786479b02ecc03314f19e