202 lines
7.6 KiB
JavaScript
202 lines
7.6 KiB
JavaScript
document.addEventListener("DOMContentLoaded", () => {
|
|
const chatbotToggler = document.querySelector(".chatbot-toggler");
|
|
const chatbotContainer = document.querySelector(".chatbot-container");
|
|
const closeBtn = document.querySelector(".chatbot-header .close-btn");
|
|
const chatbox = document.querySelector(".chatbox");
|
|
const chatInput = document.querySelector(".chat-input textarea");
|
|
const sendChatBtn = document.querySelector(".chat-input span");
|
|
const headerChatbotLink = document.getElementById('header-chatbot-link');
|
|
|
|
const apiUrl = document.querySelector('meta[name="chatbot-api-url"]').getAttribute('content');
|
|
const csrfToken = document.querySelector('meta[name="csrf-token"]').getAttribute('content');
|
|
|
|
let userMessage;
|
|
let isTyping = false;
|
|
|
|
const createChatLi = (message, className) => {
|
|
const chatLi = document.createElement("li");
|
|
chatLi.classList.add("chat", className);
|
|
|
|
let chatContent = className === "outgoing"
|
|
? `<p>${message}</p>`
|
|
: `<span class="material-symbols-rounded">smart_toy</span><div class="message-content"><p>${message}</p></div>`;
|
|
|
|
chatLi.innerHTML = chatContent;
|
|
return chatLi;
|
|
};
|
|
|
|
const saveChatHistory = () => {
|
|
localStorage.setItem("riwayat_chat_bkkbn", chatbox.innerHTML);
|
|
};
|
|
|
|
const loadChatHistory = () => {
|
|
const savedChat = localStorage.getItem("riwayat_chat_bkkbn");
|
|
if (savedChat) {
|
|
chatbox.innerHTML = savedChat;
|
|
chatbox.scrollTo(0, chatbox.scrollHeight);
|
|
}
|
|
};
|
|
|
|
loadChatHistory();
|
|
|
|
const typeText = (element, htmlString, index, speed, callback) => {
|
|
if (index < htmlString.length) {
|
|
if (htmlString.charAt(index) === '<') {
|
|
let closingTagIndex = htmlString.indexOf('>', index);
|
|
if (closingTagIndex !== -1) {
|
|
index = closingTagIndex;
|
|
}
|
|
}
|
|
|
|
element.innerHTML = htmlString.substring(0, index + 1);
|
|
|
|
chatbox.scrollTo(0, chatbox.scrollHeight);
|
|
|
|
setTimeout(() => {
|
|
typeText(element, htmlString, index + 1, speed, callback);
|
|
}, speed);
|
|
} else {
|
|
if (callback) callback();
|
|
}
|
|
};
|
|
|
|
const handleChat = () => {
|
|
if (isTyping) return;
|
|
|
|
userMessage = chatInput.value.trim();
|
|
if (!userMessage) {
|
|
alert("Peringatan: Pesan tidak boleh kosong. Silakan ketik pertanyaan Anda terlebih dahulu.");
|
|
chatInput.focus();
|
|
return;
|
|
}
|
|
|
|
isTyping = true;
|
|
chatInput.disabled = true;
|
|
chatInput.placeholder = "Tulis pertanyaan Anda di sini...";
|
|
|
|
const oldSuggestions = document.querySelectorAll('.suggestions-container');
|
|
oldSuggestions.forEach(box => box.remove());
|
|
|
|
chatInput.value = "";
|
|
chatInput.style.height = "auto";
|
|
|
|
chatbox.appendChild(createChatLi(userMessage, "outgoing"));
|
|
chatbox.scrollTo(0, chatbox.scrollHeight);
|
|
saveChatHistory();
|
|
|
|
const incomingChatLi = createChatLi('<span class="typing-animation"></span> <i class="text-xs text-slate-400">Asisten sedang mencari jawaban...</i>', "incoming");
|
|
chatbox.appendChild(incomingChatLi);
|
|
chatbox.scrollTo(0, chatbox.scrollHeight);
|
|
|
|
const waktuMulai = performance.now();
|
|
fetch(apiUrl, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'X-CSRF-TOKEN': csrfToken
|
|
},
|
|
body: JSON.stringify({ message: userMessage })
|
|
})
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
const messageWrapper = incomingChatLi.querySelector(".message-content") || incomingChatLi.querySelector("p");
|
|
const waktuSelesai = performance.now();
|
|
|
|
const waktuRespons = ((waktuSelesai - waktuMulai) / 1000).toFixed(2);
|
|
|
|
console.log(`[TESTING] Waktu Respons TTFT: ${waktuRespons} detik`);
|
|
if (data.reply) {
|
|
const parsedHTML = marked.parse(data.reply);
|
|
messageWrapper.innerHTML = "";
|
|
|
|
typeText(messageWrapper, parsedHTML, 0, 5, () => {
|
|
|
|
if (data.suggestions && data.suggestions.length > 0) {
|
|
const suggestionDiv = document.createElement("div");
|
|
suggestionDiv.classList.add("suggestions-container");
|
|
chatbox.appendChild(suggestionDiv);
|
|
|
|
data.suggestions.forEach((suggestion, index) => {
|
|
const cleanSuggestion = suggestion.replace(/^\d+\.\s*/, '').replace(/[*"]/g, '');
|
|
|
|
if (cleanSuggestion.trim() !== "") {
|
|
const btn = document.createElement("button");
|
|
btn.classList.add("suggestion-btn", "animate-in");
|
|
btn.innerText = cleanSuggestion;
|
|
|
|
setTimeout(() => {
|
|
suggestionDiv.appendChild(btn);
|
|
}, index * 150);
|
|
|
|
btn.addEventListener("click", () => {
|
|
chatInput.value = cleanSuggestion;
|
|
sendChatBtn.click();
|
|
suggestionDiv.remove();
|
|
});
|
|
}
|
|
});
|
|
}
|
|
|
|
setTimeout(() => {
|
|
chatbox.scrollTo({
|
|
top: incomingChatLi.offsetTop - 20,
|
|
behavior: 'smooth'
|
|
});
|
|
}, 100);
|
|
|
|
saveChatHistory();
|
|
|
|
isTyping = false;
|
|
chatInput.disabled = false;
|
|
chatInput.focus();
|
|
|
|
});
|
|
|
|
} else {
|
|
messageWrapper.textContent = data.error || "Terjadi kesalahan.";
|
|
isTyping = false;
|
|
chatInput.disabled = false;
|
|
chatInput.placeholder = "Ketik pertanyaan Anda di sini...";
|
|
}
|
|
})
|
|
.catch(error => {
|
|
console.error('Error:', error);
|
|
const messageWrapper = incomingChatLi.querySelector(".message-content") || incomingChatLi.querySelector("p");
|
|
messageWrapper.textContent = "Maaf, tidak dapat terhubung ke asisten. Periksa koneksi Anda.";
|
|
messageWrapper.style.color = "#ef4444";
|
|
|
|
isTyping = false;
|
|
chatInput.disabled = false;
|
|
chatInput.placeholder = "Ketik pertanyaan Anda di sini...";
|
|
});
|
|
}
|
|
|
|
const handleTextareaInput = () => {
|
|
chatInput.style.height = "auto";
|
|
chatInput.style.height = `${chatInput.scrollHeight}px`;
|
|
};
|
|
|
|
const openChatFromLink = (event) => {
|
|
event.preventDefault();
|
|
document.body.classList.add("show-chatbot");
|
|
};
|
|
|
|
sendChatBtn.addEventListener("click", handleChat);
|
|
chatInput.addEventListener("keydown", (e) => {
|
|
if (e.key === "Enter" && !e.shiftKey) {
|
|
e.preventDefault();
|
|
handleChat();
|
|
}
|
|
});
|
|
|
|
chatInput.addEventListener("input", handleTextareaInput);
|
|
|
|
chatbotToggler.addEventListener("click", () =>
|
|
document.body.classList.toggle("show-chatbot"));
|
|
closeBtn.addEventListener("click", () =>
|
|
document.body.classList.remove("show-chatbot"));
|
|
|
|
if (headerChatbotLink) {
|
|
headerChatbotLink.addEventListener('click', openChatFromLink);
|
|
}
|
|
}); |