feature: chatbot

This commit is contained in:
2025-02-28 17:53:16 +07:00
parent 7f8a2e4936
commit fefef609ac
14 changed files with 856 additions and 75 deletions

View File

@@ -0,0 +1,176 @@
import GlobalConfig from "../global-config.js";
document.addEventListener("DOMContentLoaded", function () {
document.querySelectorAll(".nav-link").forEach(tab => {
tab.addEventListener("click", function () {
setTimeout(() => {
const tab_active = getActiveTabId();
console.log("Active Tab ID:", tab_active);
}, 100); // Timeout untuk memastikan class `active` sudah diperbarui
});
});
const textarea = document.getElementById("user-message");
const sendButton = document.getElementById("send");
const conversationArea = document.querySelector(".row.flex-grow");
// Fungsi untuk mengirim pesan
async function sendMessage() {
const userText = textarea.value.trim();
if (userText !== "") {
// Kosongkan textarea setelah mengirim
textarea.value = "";
// Ambil tab aktif saat ini
const currentTab = getActiveTabId();
// Tambahkan pesan user ke UI
addMessage(userText, "user");
// Tambahkan pesan bot sementara dengan "Loading..."
const botMessageElement = addMessage('<div class="loader w-auto"></div>', "bot");
// Panggil API untuk mendapatkan response dari bot
const botResponse = await getBotResponse(currentTab, userText);
// Perbarui pesan bot dengan respons yang sebenarnya
botMessageElement.innerHTML = botResponse;;
}
}
// Event listener untuk klik tombol
sendButton.addEventListener("click", sendMessage);
// Event listener untuk menekan Enter di textarea
textarea.addEventListener("keydown", function (event) {
if (event.key === "Enter" && !event.shiftKey) {
event.preventDefault(); // Mencegah newline di textarea
sendMessage(); // Panggil fungsi kirim pesan
}
});
function addMessage(text, sender) {
const messageRow = document.createElement("div");
messageRow.classList.add("row", "flex-grow", "overflow-auto");
const messageCol = document.createElement("div");
messageCol.classList.add("w-auto", "d-inline-block"); // Menyesuaikan lebar konten
if (sender === "user") {
messageCol.classList.add("ms-auto", "max-w-50"); // Rata kanan, max 50% (setara col-6)
} else {
messageCol.classList.add("max-w-75"); // Max 75% (setara col-9)
// Tambahkan avatar hanya untuk bot
const avatarSpan = document.createElement("span");
avatarSpan.classList.add("d-flex", "align-items-center", "mb-1");
const avatarImg = document.createElement("img");
avatarImg.classList.add("rounded-circle");
avatarImg.width = 32;
avatarImg.src = "/images/iconchatbot.jpeg";
avatarImg.alt = "bot-avatar";
avatarSpan.appendChild(avatarImg);
messageCol.appendChild(avatarSpan);
}
const messageDiv = document.createElement("div");
messageDiv.classList.add("p-2", "rounded", "mb-2");
if (sender === "user") {
messageDiv.classList.add("user-response", "bg-primary", "text-white");
} else {
messageDiv.classList.add("bot-response", "bg-light");
}
// Menyisipkan konten HTML langsung (bisa berupa teks atau loader)
messageDiv.innerHTML = text;
messageCol.appendChild(messageDiv);
messageRow.appendChild(messageCol);
// Tambahkan ke area percakapan
conversationArea.appendChild(messageRow);
// Scroll otomatis ke bawah
conversationArea.scrollTop = conversationArea.scrollHeight;
return messageDiv; // Mengembalikan elemen agar bisa diperbarui nanti
}
// function addMessage(text, sender) {
// const messageRow = document.createElement("div");
// messageRow.classList.add("row", "flex-grow", "overflow-auto");
// const messageCol = document.createElement("div");
// messageCol.classList.add("col-9", "w-auto");
// if (sender === "user") {
// messageCol.classList.add("ms-auto"); // Geser ke kanan untuk user
// }
// const messageDiv = document.createElement("div");
// messageDiv.classList.add("p-2", "rounded", "mb-2");
// if (sender === "user") {
// messageDiv.classList.add("user-response", "bg-primary", "text-white");
// } else {
// messageDiv.classList.add("bot-response", "bg-light");
// // Tambahkan avatar hanya untuk bot
// const avatarSpan = document.createElement("span");
// avatarSpan.classList.add("d-flex", "align-items-center", "mb-1");
// const avatarImg = document.createElement("img");
// avatarImg.classList.add("rounded-circle");
// avatarImg.width = 32;
// avatarImg.src = "/images/iconchatbot.jpeg";
// avatarImg.alt = "bot-avatar";
// avatarSpan.appendChild(avatarImg);
// messageCol.appendChild(avatarSpan);
// }
// // Menyisipkan konten HTML langsung (bisa berupa teks atau loader)
// messageDiv.innerHTML = text;
// messageCol.appendChild(messageDiv);
// messageRow.appendChild(messageCol);
// // Tambahkan ke area percakapan
// conversationArea.appendChild(messageRow);
// // Scroll otomatis ke bawah
// conversationArea.scrollTop = conversationArea.scrollHeight;
// return messageDiv; // Mengembalikan elemen agar bisa diperbarui nanti
// }
// Fungsi untuk memanggil API
async function getBotResponse(tab_active, userText) {
try {
const url = `${GlobalConfig.apiHost}/api/generate-text`;
const response = await fetch(url, {
method: "POST",
body: JSON.stringify({tab_active:tab_active, prompt: userText }),
headers: {
Authorization: `Bearer ${document
.querySelector('meta[name="api-token"]')
.getAttribute("content")}`,
"Content-Type": "application/json",
},
});
const data = await response.json();
return data.response || "Maaf, saya tidak mengerti.";
} catch (error) {
console.error("Error fetching bot response:", error);
return "Terjadi kesalahan, coba lagi nanti.";
}
}
});
function getActiveTabId() {
const activeTab = document.querySelector(".nav-link.active");
return activeTab ? activeTab.id : null;
}

View File

@@ -0,0 +1,92 @@
@extends('layouts.vertical', ['subtitle' => 'Chatbot'])
@section('css')
@vite(['node_modules/gridjs/dist/theme/mermaid.min.css'])
<style>
#user-message {
height: 60px; /* Menambah tinggi textarea */
font-size: 1.1rem; /* Memperbesar font */
padding: 10px; /* Menambah ruang di dalam textarea */
resize: none; /* Mencegah resize manual */
}
.loader {
width: 10px;
aspect-ratio: 1;
border-radius: 50%;
animation: l5 1s infinite linear alternate;
}
@keyframes l5 {
0% {box-shadow: 10px 0 #000, -10px 0 #0002; background: #000 }
33% {box-shadow: 10px 0 #000, -10px 0 #0002; background: #0002}
66% {box-shadow: 10px 0 #0002, -10px 0 #000; background: #0002}
100%{box-shadow: 10px 0 #0002, -10px 0 #000; background: #000 }
}
</style>
@endsection
@section('content')
@include('layouts.partials/page-title', ['title' => 'Chatbot', 'subtitle' => 'Chatbot'])
<div class="card">
<div class="card-header">
<ul class="nav nav-tabs nav-justified">
<li class="nav-item">
<button id="count-retribusi" data-bs-toggle="tab" aria-expanded="false" class="nav-link">
<span class="d-block d-sm-none"><i class="bx bx-home"></i></span>
<span class="d-none d-sm-block">Perhitungan Retribusi</span>
</button>
</li>
<li class="nav-item">
<button id="document-validation" data-bs-toggle="tab" aria-expanded="true"
class="nav-link active">
<span class="d-block d-sm-none"><i class="bx bx-user"></i></span>
<span class="d-none d-sm-block">Validasi Dokumen PBG</span>
</button>
</li>
<li class="nav-item">
<button id="data-information" data-bs-toggle="tab" aria-expanded="false" class="nav-link">
<span class="d-block d-sm-none"><i class="bx bx-envelope"></i></span>
<span class="d-none d-sm-block">Pengumpulan Data PBG</span>
</button>
</li>
</ul>
</div>
<div class="card-body d-flex flex-column" style="height: 700px;">
<!-- Conversation Area -->
<!-- Bot Response -->
<div class="row flex-grow overflow-auto">
<div class="col-9 w-auto">
<span class="d-flex align-items-center mb-1">
<img class="rounded-circle" width="32" src="/images/iconchatbot.jpeg" alt="avatar-3">
</span>
<div class="bot-response p-2 bg-light rounded mb-2">
<p>Halo! Ada yang bisa saya bantu?</p>
</div>
</div>
</div>
<!-- Input & Button (Selalu di Bawah) -->
<div class="row mt-auto">
<div class="col-xl-12 d-flex align-items-end gap-1">
<textarea class="form-control" id="user-message"></textarea>
<button id="send" class="btn btn-primary btn-lg rounded-pill">
<i class='bx bx-send'></i>
</button>
</div>
{{-- <div class="col-xl-2 d-flex justify-content-end">
</div> --}}
</div>
</div>
</div>
</div>
@endsection
@section('scripts')
@vite(['resources/js/chatbot/index.js'])
@endsection

View File

@@ -22,14 +22,20 @@
<div class="d-flex align-items-center gap-2">
<!-- Theme Color (Light/Dark) -->
<!-- <div class="topbar-item">
{{-- <div class="topbar-item">
<button type="button" class="topbar-button" id="light-dark-mode">
<iconify-icon icon="solar:moon-outline"
class="fs-22 align-middle light-mode"></iconify-icon>
<iconify-icon icon="solar:sun-2-outline"
class="fs-22 align-middle dark-mode"></iconify-icon>
</button>
</div> -->
</div> --}}
<div class="topbar-item">
<a href="{{ route('chatbot.index') }}" class="topbar-button">
<iconify-icon icon="solar:chat-square-outline" class="fs-22 align-middle"></iconify-icon>
</a>
</div>
<!-- Notification -->
<div class="dropdown topbar-item">

View File

@@ -1,6 +1,36 @@
<!DOCTYPE html>
<html lang="en" @yield('html-attribute')>
<style>
/* .floating-icon {
position: fixed;
right: 30px;
bottom: 70px;
background: white;
padding: 10px;
border-radius: 50%;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
cursor: pointer;
z-index: 1000;
} */
.floating-icon {
position: fixed;
right: 40px;
bottom: 100px;
width: 70px; /* Sesuaikan ukuran */
height: 70px; /* Sesuaikan ukuran */
background: white;
border-radius: 50%;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
cursor: pointer;
z-index: 1000;
background-image: url('/images/iconchatbot.jpeg'); /* Path ke gambar */
background-size: cover; /* Agar gambar menyesuaikan */
background-position: center; /* Memusatkan gambar */
}
</style>
<head>
@include('layouts.partials/title-meta')
@@ -17,10 +47,25 @@
<div class="page-content">
<div class="container-fluid">
{{-- <div class="container-fluid">
@yield('content')
<div>
<iconify-icon icon="solar:chat-square-outline" class="fs-35 align-middle"></iconify-icon>
</div>
</div> --}}
<div class="container-fluid">
@yield('content')
{{-- <div class="floating-icon">
</div> --}}
@if (!Request::is('chatbot'))
<a href="{{ route('chatbot.index') }}" class="floating-icon">
</a>
@endif
</div>
@include('layouts.partials/footer')