feat(chat): Integrate chatbot API for dynamic responses

This commit replaces the static, simulated chat functionality with a live integration to a chatbot backend API.

The chat page now sends user messages to a webhook endpoint using the `fetch` API. It implements session management by generating and storing a `sessionId` in `localStorage` to maintain conversation context between requests.

Key changes:
- Implement `async` `sendMessage` function to post to the chatbot API.
- Add session ID generation and persistence.
- Dynamically render text and image responses from the API.
- Remove hardcoded example messages from the HTML.
- Update the virtual assistant's name to "Maya".
- Fix navigation links from the dashboard to the chat page.
This commit is contained in:
2025-07-21 14:44:06 +07:00
parent 158b6d0886
commit 84c7cbd8ce
5 changed files with 158 additions and 80 deletions

View File

@@ -16,31 +16,31 @@
<link rel="stylesheet" href="styles/main.css">
</head>
<body style="font-family: Inter, 'Noto Sans', sans-serif; display: flex; flex-direction: column; min-height: 100vh; padding: 0; background: linear-gradient(135deg, #0077b6, #023e8a) !important;">
<header>
<div class="mobile-header-user-info">
<div
<header>
<div class="mobile-header-user-info">
<div
class="mobile-header-avatar"
style='background-image: url("https://lh3.googleusercontent.com/aida-public/AB6AXuDdsEbNptWuGWnUFVjxjJbETG3Ux0dsBq6vJXBqGqxirKQZ5h08CrhYiwt89C2-bjXZjh3bpwhhheIQH7TY4OfQvZjIE93FvpVViGxSvYuMDJylveD2lc-daeSlCOr8t64jpRbHrEXM0JGuM-CGz99i7dCKdfgcBu7EEecYOxvaPyTNOskRBL3yUkN9tJ659LAKvkEeum2-SYZ1htR83YgVFVP-n8-XoZBoQaa5W7vOC0TZtlOldU-Cq8EkbxM_HQH3prpaomyTJdCF");'
></div>
<h2 class="mobile-header-title">BeautyBot Analytics</h2>
</div>
<div class="mobile-header-right">
<button class="mobile-header-menu-button">
></div>
<h2 class="mobile-header-title">BeautyBot Analytics</h2>
</div>
<div class="mobile-header-right">
<button class="mobile-header-menu-button">
<svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 256 256">
<path
d="M224,128a8,8,0,0,1-8,8H40a8,8,0,0,1,0-16H216A8,8,0,0,1,224,128ZM40,72H216a8,8,0,0,0,0-16H40a8,8,0,0,0,0,16ZM216,184H40a8,8,0,0,0,0,16H216a8,8,0,0,0,0-16Z"
></path>
<path
d="M224,128a8,8,0,0,1-8,8H40a8,8,0,0,1,0-16H216A8,8,0,0,1,224,128ZM40,72H216a8,8,0,0,0,0-16H40a8,8,0,0,0,0,16ZM216,184H40a8,8,0,0,0,0,16H216a8,8,0,0,0,0-16Z"
></path>
</svg>
</button>
<div class="dropdown-menu hidden">
</button>
<div class="dropdown-menu hidden">
<button class="control-button">
<span class="truncate">Ekspor Data</span>
<span class="truncate">Ekspor Data</span>
</button>
</div>
</div>
</header>
</div>
</header>
<main>
<main class="container mx-auto px-4">
<div class="layout-content-container">
<h2 class="text-[#141414] tracking-light text-[28px] font-bold leading-tight px-4 text-left pb-3 pt-5">Chatbot Performance Overview</h2>
<div class="controls-container">
@@ -229,6 +229,7 @@
</div>
</main>
<script src="scripts/main.js"></script>
<!-- Floating circles background -->
<ul class="floating-circles">
<li></li><li></li><li></li><li></li><li></li>

View File

@@ -4,13 +4,13 @@ const dropdownMenu = document.querySelector('.dropdown-menu');
if (menuButton && dropdownMenu) {
menuButton.addEventListener('click', function() {
dropdownMenu.classList.toggle('visible');
dropdownMenu.classList.toggle('hidden');
});
}
// Close dropdown when clicking outside
document.addEventListener('click', function(event) {
if (!event.target.closest('.mobile-header-right') && dropdownMenu) {
dropdownMenu.classList.remove('visible');
dropdownMenu.classList.add('hidden');
}
});

View File

@@ -18,6 +18,14 @@ body {
padding: 0 20px; /* Tambahan padding horizontal */
}
/* Container for responsive design */
.container {
width: 100%;
max-width: 1200px;
margin: 0 auto;
padding: 0 15px;
}
/* Universal Header (works for both desktop and mobile) */
header {
position: sticky;
@@ -27,18 +35,19 @@ header {
backdrop-filter: blur(35px);
border-radius: 0 0 20px 20px;
margin: 0;
padding: 20px 25px; /* Increased vertical padding */
padding: 20px 25px;
box-shadow: 0 5px 25px rgba(0, 0, 0, 0.15);
min-height: 110px; /* Significantly taller header */
min-height: 110px;
width: 100%;
max-width: 100%;
display: flex;
flex-wrap: wrap; /* Allow elements to wrap on smaller screens */
flex-wrap: wrap;
justify-content: space-between;
align-items: center;
gap: 15px; /* Consistent spacing between elements */
gap: 15px;
}
/* Universal Header Elements */
.mobile-header-user-info {
display: flex;
@@ -253,12 +262,37 @@ th:nth-child(3), td:nth-child(3) { width: 30%; }
/* Responsivitas */
@media (max-width: 768px) {
/* Show mobile elements, hide desktop nav */
.mobile-header-user-info,
.mobile-header-right {
display: flex;
}
.desktop-nav {
display: none;
}
/* Stack cards vertically on mobile */
#stat-cards, #secondary-stats {
grid-template-columns: 1fr;
}
/* Adjust header layout for mobile */
header {
flex-direction: column;
align-items: flex-start;
padding: 15px;
}
.mobile-header-right {
align-self: flex-end;
margin-top: -50px;
}
body {
padding: 0 10px;
}
/* Removed mobile-specific scaling as we now have universal styles */
/* Stat Cards */
#stat-cards, #secondary-stats {
flex-direction: column;
@@ -285,24 +319,35 @@ th:nth-child(3), td:nth-child(3) { width: 30%; }
}
}
@media (min-width: 769px) {
/* Adjust header layout for desktop */
header {
padding: 15px 25px;
}
.mobile-header-user-info {
display: flex;
align-items: center;
}
.mobile-header-right {
display: flex;
}
}
/* Definitive Vertical Layout */
.controls-container,
.card-container {
display: block;
}
/* Definitive Horizontal Layout for Stat Cards */
/* Responsive Layout for Cards */
#stat-cards, #secondary-stats {
display: flex;
flex-direction: row;
justify-content: space-between;
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 20px;
}
#stat-cards > div, #secondary-stats > div {
flex: 1; /* Ensure cards share space equally */
}
/* Ensure cards within the container stack vertically */
.card-container > div {
margin-bottom: 20px; /* Add space between vertical cards */