diff --git a/src/App.css b/src/App.css
index 5ff7858..289ce63 100644
--- a/src/App.css
+++ b/src/App.css
@@ -1,7 +1,14 @@
+html, body, #root {
+ height: 100%;
+ margin: 0;
+}
+
.App {
- display: flex;
- align-items: end;
- justify-content: center;
+ height: 100%;
+ display: flex;
+ flex-direction: column;
+ background: #fff;
+ align-items: center;
}
.App-logo {
diff --git a/src/ChatBot.js b/src/ChatBot.js
index b9c6164..33d17a3 100644
--- a/src/ChatBot.js
+++ b/src/ChatBot.js
@@ -1,220 +1,203 @@
- import React, { useState, useEffect } from 'react';
- import styles from './ChatBot.module.css';
+import React, { useState, useEffect } from 'react';
+import styles from './ChatBot.module.css';
- const ChatBot = ({ existingConversation, readOnly, hh }) => {
- const [messages, setMessages] = useState([
- {
- sender: 'bot',
- text: 'Hai Dermalovers! 👋 Saya siap membantu anda tampil lebih percaya diri. Ada pertanyaan seputar perawatan kulit atau kecantikan hari ini?',
- time: getTime(),
- quickReplies: [
- 'List harga layanan Dermalounge',
- 'Beri saya info jadwal dokter',
- 'Apa saja layanan disini',
- ],
- },
- ]);
+const ChatBot = ({ existingConversation, readOnly, hh }) => {
+ const [messages, setMessages] = useState([
+ {
+ sender: 'bot',
+ text: 'Hai Dermalovers! 👋 Saya siap membantu anda tampil lebih percaya diri. Ada pertanyaan seputar perawatan kulit atau kecantikan hari ini?',
+ time: getTime(),
+ quickReplies: [
+ 'Info layanan Dermalounge',
+ 'Apa perawatan wajah recommended',
+ 'Saya ingin konsultasi masalah kulit',
+ 'Info lokasi & cara booking',
+ ],
+ },
+ ]);
- const [input, setInput] = useState('');
- const [isLoading, setIsLoading] = useState(false);
- useEffect(() => {
+ const [input, setInput] = useState('');
+ const [isLoading, setIsLoading] = useState(false);
+ useEffect(() => {
- if (existingConversation && existingConversation.length > 0) {
- setMessages(existingConversation);
- }
- }, [existingConversation])
- useEffect(() => {
- if (!localStorage.getItem('session')) {
- function generateUUID() {
- return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
- const r = Math.random() * 16 | 0;
- const v = c === 'x' ? r : (r & 0x3 | 0x8);
- return v.toString(16);
- });
- }
-
- const sessionId = generateUUID();
- const dateNow = new Date().toISOString();
-
- localStorage.setItem('session', JSON.stringify({ sessionId: sessionId, lastSeen: dateNow }))
- }
- }, []);
-
- const sendMessage = async (textOverride = null) => {
- const message = textOverride || input.trim();
- if (message === '') return;
-
- // Show user's message immediately
- const newMessages = [
- ...messages,
- { sender: 'user', text: message, time: getTime() },
- ];
-
- setMessages(newMessages);
- setInput('');
-
- setIsLoading(true);
- try {
- // Send to backend
- const response = await fetch('https://bot.kediritechnopark.com/webhook/master-agent/ask', {
- method: 'POST',
- headers: { 'Content-Type': 'application/json' },
- body: JSON.stringify({ pertanyaan: message, sessionId: JSON.parse(localStorage.getItem('session')).sessionId, lastSeen: new Date().toISOString() }),
+ if (existingConversation && existingConversation.length > 0) {
+ setMessages(existingConversation);
+ }
+ }, [existingConversation])
+ useEffect(() => {
+ if (!localStorage.getItem('session')) {
+ function generateUUID() {
+ return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
+ const r = Math.random() * 16 | 0;
+ const v = c === 'x' ? r : (r & 0x3 | 0x8);
+ return v.toString(16);
});
+ }
- const data = await response.json();
- console.log(data)
- // Assuming your backend sends back something like: { answer: "text" }
- // Adjust this according to your actual response shape
- const botAnswer = data[0].output[0].text || data[0].output || 'Maaf, saya tidak mengerti.';
+ const sessionId = generateUUID();
+ const dateNow = new Date().toISOString();
- // Add bot's reply
+ localStorage.setItem('session', JSON.stringify({ sessionId: sessionId, lastSeen: dateNow }))
+ }
+ }, []);
+
+ const sendMessage = async (textOverride = null, tryCount = 0) => {
+ const message = textOverride || input.trim();
+ if (message === '') return;
+
+ // Show user's message immediately
+ const newMessages = [
+ ...messages,
+ { sender: 'user', text: message, time: getTime() },
+ ];
+
+ setMessages(newMessages);
+ setInput('');
+
+ setIsLoading(true);
+ try {
+ // Send to backend
+ const response = await fetch('https://bot.kediritechnopark.com/webhook/master-agent/ask/dev', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({ pertanyaan: message, sessionId: JSON.parse(localStorage.getItem('session')).sessionId, lastSeen: new Date().toISOString() }),
+ });
+
+ const data = await response.json();
+ console.log(data)
+ // Assuming your backend sends back something like: { answer: "text" }
+ // Adjust this according to your actual response shape
+ const botAnswer = data[0].output[0].text || data[0].output || 'Maaf, saya tidak mengerti.';
+
+ // Add bot's reply
+ setMessages(prev => [
+ ...prev,
+ { sender: 'bot', text: botAnswer, time: getTime() },
+ ]);
+
+ setIsLoading(false);
+ } catch (error) {
+ console.log(tryCount)
+ if (tryCount > 3) {
+ // Add bot's error reply
setMessages(prev => [
...prev,
- { sender: 'bot', text: botAnswer, time: getTime() },
+ { sender: 'bot', text: 'Maaf saya sedang tidak tersedia sekarang, coba lagi nanti', time: getTime() },
]);
-
- setIsLoading(false);
- } catch (error) {
- sendMessage('gimana')
- console.error('Fetch error:', error);
- } finally {
setIsLoading(false);
+ return;
}
- };
+ setTimeout(() => sendMessage(message, tryCount + 1), 3000);
- return (
-
-
-
-
DERMALOUNGE
-
-
-
-
- {isLoading && (
-
- )}
- {messages.slice().reverse().map((msg, index) => (
-
-
- {msg.sender !== 'bot'
- ? msg.text
- : (() => {
- try {let cleanText = msg.text.replace(/`/g, ''); // Remove backticks
-cleanText = cleanText.substring(4); // Remove first 4 characters
-let parsedObj = JSON.parse(cleanText);
-
-return parsedObj.jawaban;
-} catch (e) {
- return msg.text; // Return an empty string if there is an error
-}
-
- })()}
- {msg.quickReplies && (
-
- {msg.quickReplies.map((reply, i) => (
-
sendMessage(reply)}
- >
- {reply}
-
- ))}
-
- )}
-
{msg.time}
-
-
- ))}
-
-
-
- setInput(e.target.value)}
- onKeyDown={(e) => e.key === 'Enter' && sendMessage()}
- disabled={isLoading}
- />
- sendMessage()} disabled={isLoading}>
- Kirim
-
-
-
-
-
sendMessage('Dapatkah bopeng dihilangkan?')}
- >
- Dapatkah bopeng dihilangkan?
-
-
sendMessage('Bisa booking treatment untuk besok?')}
- >
- Bisa booking treatment untuk besok?
-
-
sendMessage('Bisa booking treatment untuk besok?')}
- >
- Ada treatment untuk jerawat?
-
-
-
-
-
- );
+ console.error('Fetch error:', error);
+ }
};
- function getTime() {
- const now = new Date();
- return now.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
- }
+ return (
+
+
+
+
DERMALOUNGE
+
- export default ChatBot;
+
+
+ {isLoading && (
+
+ )}
+ {messages.slice().reverse().map((msg, index) => (
+
+
+ {msg.sender !== 'bot'
+ ? msg.text
+ : (() => {
+ try {
+ let cleanText = msg.text.replace(/`/g, ''); // Remove backticks
+ cleanText = cleanText.substring(4); // Remove first 4 characters
+ let parsedObj = JSON.parse(cleanText);
+
+ return parsedObj.jawaban;
+ } catch (e) {
+ return msg.text; // Return an empty string if there is an error
+ }
+
+ })()}
+ {msg.quickReplies && (
+
+ {msg.quickReplies.map((reply, i) => (
+
sendMessage(reply)}
+ >
+ {reply}
+
+ ))}
+
+ )}
+
{msg.time}
+
+
+ ))}
+
+
+
+
setInput(e.target.value)}
+ onKeyDown={(e) => e.key === 'Enter' && sendMessage()}
+ disabled={isLoading}
+ />
+
sendMessage()} disabled={isLoading}>
+ Kirim
+
+
+
+
+
+ Untuk bisa membantu Anda lebih jauh, boleh saya tahu nama dan nomor telepon Anda?
+ Informasi ini juga membantu tim admin kami jika perlu melakukan follow-up nantinya 😊
+
+
console.log('Nama focused')}
+ />
+
+ +62
+ console.log('Telepon focused')}
+ style={{border: 0, width: '100%'}}
+ />
+
+
+
+ Lanjut
+
+
+
+
+
+ );
+};
+
+function getTime() {
+ const now = new Date();
+ return now.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
+}
+
+export default ChatBot;
diff --git a/src/ChatBot.module.css b/src/ChatBot.module.css
index c90f962..eba772f 100644
--- a/src/ChatBot.module.css
+++ b/src/ChatBot.module.css
@@ -1,14 +1,23 @@
.chatContainer {
- max-width: 500px;
- background: #fff;
- border-radius: 10px;
- box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2);
- display: flex;
- flex-direction: column;
- height: 100vh;
- overflow: auto;
+ height: 100%;
+ background: #fff;
+ border-radius: 10px;
+ box-shadow: 0 5px 15px #0003;
+ display: flex;
+ flex-direction: column;
+ max-width: 500px;
+ overflow: auto;
+ position: relative;
}
+.PopUp{
+ background-color: #555454ab;
+ position: absolute;
+ height: 100%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
.chatHeader {
background: #075e54;
color: #fff;
@@ -117,6 +126,22 @@
gap: 8px;
margin: 10px 0 0;
}
+.inputGroup {
+ display: flex;
+ align-items: center;
+ border: 1px solid #ccc;
+ border-radius: 20px;
+ overflow: hidden;
+ width: 100%;
+}
+
+.prefix {
+ background-color: #f0f0f0;
+ padding: 0.5rem 0.75rem;
+ border-right: 1px solid #ccc;
+ font-size: 12px;
+ color: #555;
+}
.quickReply {
background: #fff;
diff --git a/src/Dashboard.js b/src/Dashboard.js
index 87d7ca5..fac9731 100644
--- a/src/Dashboard.js
+++ b/src/Dashboard.js
@@ -9,6 +9,8 @@ import DiscussedTopics from './DiscussedTopics';
import Chart from 'chart.js/auto';
const Dashboard = () => {
+ const [isMenuOpen, setIsMenuOpen] = useState(false);
+
const chartRef = useRef(null);
const chartInstanceRef = useRef(null);
const [conversations, setConversations] = useState([]);
@@ -46,6 +48,22 @@ const Dashboard = () => {
window.location.reload();
};
+ const menuRef = useRef(null);
+
+ // Close dropdown if click outside
+ useEffect(() => {
+ const handleClickOutside = (event) => {
+ if (menuRef.current && !menuRef.current.contains(event.target)) {
+ setIsMenuOpen(false);
+ }
+ };
+
+ document.addEventListener('mousedown', handleClickOutside);
+ return () => {
+ document.removeEventListener('mousedown', handleClickOutside);
+ };
+ }, []);
+
useEffect(() => {
const fetchData = async () => {
const token = localStorage.getItem('token');
@@ -98,7 +116,7 @@ const Dashboard = () => {
userMessages,
botMessages,
});
-
+
setLoading(false); // ⬅️ Setelah berhasil, hilangkan loading
} catch (error) {
console.error('Error:', error);
@@ -206,14 +224,33 @@ const Dashboard = () => {
{isLoggedIn ? (
-
Logout
+
+
+
setIsMenuOpen(!isMenuOpen)}
+ className={styles.dropdownToggle}
+ ref={menuRef}
+ >
+ ☰ Menu
+
+
+ {isMenuOpen && (
+
+
+ Ganti Password
+
+
+ Logout
+
+
+ )}
+
) : (
Login
)}
Dermalounge AI Admin Dashboard
-
Statistik penggunaan chatbot secara real-time
diff --git a/src/Dashboard.module.css b/src/Dashboard.module.css
index e677d0a..e742a07 100644
--- a/src/Dashboard.module.css
+++ b/src/Dashboard.module.css
@@ -43,8 +43,8 @@
}
.dashboardHeader img {
- width: 50px;
- height: 50px;
+ width: 75px;
+ height: 75px;
border-radius: 50%;
}
@@ -191,4 +191,46 @@ position: absolute;
.logoutButton:hover {
background-color: #cb0f0f;
-}
\ No newline at end of file
+}
+.dropdownContainer {
+ position: relative;
+ display: inline-block;
+
+position: absolute;
+ top: 10px;
+ right: 10px;
+}
+
+.dropdownToggle {
+ background-color: #007bff;
+ color: white;
+ padding: 8px 12px;
+ border: none;
+ border-radius: 4px;
+ cursor: pointer;
+}
+
+.dropdownMenu {
+ position: absolute;
+ right: 0;
+ top: 100%;
+ background-color: white;
+ border: 1px solid #ccc;
+ min-width: 160px;
+ box-shadow: 0 8px 16px rgba(0,0,0,0.1);
+ z-index: 1;
+ border-radius: 4px;
+}
+
+.dropdownItem {
+ padding: 10px 16px;
+ text-align: left;
+ width: 100%;
+ background: none;
+ border: none;
+ cursor: pointer;
+}
+
+.dropdownItem:hover {
+ background-color: #f0f0f0;
+}