diff --git a/src/Dashboard.module.css b/src/Dashboard.module.css
index 52dcf65..860f527 100644
--- a/src/Dashboard.module.css
+++ b/src/Dashboard.module.css
@@ -126,7 +126,10 @@
/* Mobile styles */
@media (max-width: 768px) {
-
+.h1 {
+ color: white;
+ font-size: 23px;
+}
.dashboardContainer {
max-width: 900px;
margin: 30px auto;
@@ -192,11 +195,11 @@ position: absolute;
.logoutButton:hover {
background-color: #cb0f0f;
}
+
.dropdownContainer {
position: relative;
display: inline-block;
-
-position: absolute;
+ position: absolute;
top: 10px;
right: 10px;
}
diff --git a/src/FollowUps.js b/src/FollowUps.js
new file mode 100644
index 0000000..4fe0020
--- /dev/null
+++ b/src/FollowUps.js
@@ -0,0 +1,40 @@
+import React from 'react';
+import styles from './FollowUps.module.css';
+
+const FollowUps = ({ data }) => {
+ return (
+
+
User yang tertarik
+
+ {data.map(user => (
+
+
+
{user.name}
+
+ {new Date(user.created_at).toLocaleString('id-ID', {
+ dateStyle: 'medium',
+ timeStyle: 'short',
+ timeZone: 'Asia/Jakarta'
+ })}
+
+
+
{user.notes}
+
+
+ ))}
+
+
+ );
+};
+
+export default FollowUps;
diff --git a/src/FollowUps.module.css b/src/FollowUps.module.css
new file mode 100644
index 0000000..348a108
--- /dev/null
+++ b/src/FollowUps.module.css
@@ -0,0 +1,111 @@
+.container {
+ padding: 24px;
+ background-color: #f7f9fa;
+ font-family: 'Amazon Ember', sans-serif;
+}
+
+.title {
+ font-size: 22px;
+ font-weight: 600;
+ color: #16191f;
+ margin-bottom: 20px;
+ text-align: center;
+}
+
+.grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
+ gap: 16px;
+}
+
+.card {
+ background-color: #ffffff;
+ border: 1px solid #d1d5db;
+ border-radius: 8px;
+ padding: 16px;
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
+ display: flex;
+ flex-direction: column;
+ justify-content: space-between;
+ transition: box-shadow 0.2s ease;
+}
+
+.card:hover {
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
+}
+
+.header {
+ display: flex;
+ justify-content: space-between;
+ align-items: flex-start;
+ flex-wrap: wrap;
+ gap: 4px;
+}
+
+.header h3 {
+ font-size: 18px;
+ margin: 0;
+ color: #232f3e;
+}
+
+.date {
+ font-size: 12px;
+ color: #6b7280;
+ white-space: nowrap;
+}
+
+.notes {
+ margin: 12px 0;
+ color: #374151;
+ font-size: 14px;
+}
+
+.footer {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ flex-wrap: wrap;
+ gap: 8px;
+ margin-top: 8px;
+}
+
+.contact {
+ font-size: 13px;
+ color: #374151;
+ word-break: break-all;
+}
+
+.chatBtn {
+ background-color: #25d366;
+ color: #ffffff;
+ text-decoration: none;
+ padding: 6px 12px;
+ border-radius: 4px;
+ font-size: 13px;
+ font-weight: 500;
+ transition: background-color 0.2s ease;
+ white-space: nowrap;
+}
+
+.chatBtn:hover {
+ background-color: #1da851;
+}
+
+@media (max-width: 480px) {
+ .title {
+ font-size: 18px;
+ }
+
+ .card {
+ padding: 12px;
+ }
+
+ .notes {
+ font-size: 13px;
+ }
+
+ .chatBtn {
+ padding: 6px 10px;
+ font-size: 12px;
+ }
+}
diff --git a/src/Login.js b/src/Login.js
index 047c20e..d366719 100644
--- a/src/Login.js
+++ b/src/Login.js
@@ -30,24 +30,7 @@ const Login = () => {
if (loginData?.success && loginData?.token) {
localStorage.setItem('token', loginData.token);
-
- const profileResponse = await fetch('https://bot.kediritechnopark.com/webhook/profile', {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json',
- 'Authorization': `Bearer ${loginData.token}`
- }
- });
-
- const profileDataRaw = await profileResponse.json();
- const profileData = Array.isArray(profileDataRaw) ? profileDataRaw[0] : profileDataRaw;
-
- if (profileData?.success) {
- localStorage.setItem('user', JSON.stringify(profileData.user));
- window.location.href = '/dashboard';
- } else {
- setError('Token tidak valid');
- }
+ window.location.href = '/dashboard';
} else {
setError(loginData?.message || 'Username atau password salah');
}
diff --git a/src/ProfileTab.js b/src/ProfileTab.js
new file mode 100644
index 0000000..2bd8c7a
--- /dev/null
+++ b/src/ProfileTab.js
@@ -0,0 +1,207 @@
+import React, { useState, useRef, useEffect } from 'react';
+import { FaPen } from 'react-icons/fa';
+import styles from './ProfileTab.module.css';
+import { useNavigate } from 'react-router-dom';
+
+const ProfileTab = () => {
+ const menuRef = useRef(null);
+ const navigate = useNavigate();
+ const [isEditing, setIsEditing] = useState(false);
+ const [isMenuOpen, setIsMenuOpen] = useState(false);
+
+ // 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);
+ };
+ }, []);
+
+ const handleLogout = () => {
+ localStorage.removeItem('token');
+ localStorage.removeItem('user');
+
+ navigator.serviceWorker.ready.then(function (registration) {
+ registration.pushManager.getSubscription().then(function (subscription) {
+ if (subscription) {
+ subscription.unsubscribe().then(function (successful) {
+ console.log('Push subscription unsubscribed on logout:', successful);
+ // Optional: also notify backend to clear the token
+ fetch('/api/clear-subscription', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify({ endpoint: subscription.endpoint }),
+ });
+ });
+ }
+ });
+ });
+
+ window.location.reload();
+ };
+
+ useEffect(() => {
+ const fetchData = async () => {
+ const token = localStorage.getItem('token');
+
+ try {
+ const response = await fetch('https://bot.kediritechnopark.com/webhook/dashboard?profileOnly=true', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ 'Authorization': `Bearer ${token}`,
+ },
+ });
+
+ if (response.status === 401 || response.status === 403) {
+ handleLogout();
+ return;
+ }
+
+ if (!response.ok) {
+ throw new Error('Fetch gagal dengan status: ' + response.status);
+ }
+
+ const data = await response.json();
+ console.log(data);
+
+ setProfile(data.profile_data);
+ } catch (error) {
+ console.error('Error:', error);
+ navigate('/login');
+ }
+ };
+
+ fetchData(); // Jalankan langsung saat komponen di-mount
+
+ }, [navigate]);
+
+ const [profile, setProfile] = useState({
+ name: "Rikolo",
+ company: "Dermalounge",
+ address: "Jl. Pahlawan No.123, Kediri",
+ email: "admin@dermalounge.com",
+ phone: "08123456789",
+ image: "/dermalounge.jpg"
+ });
+
+ const licenses = [
+ { id: 1, type: "AI Bot License", number: "DL-2025-AI001", validUntil: "2026-12-31" },
+ { id: 2, type: "Clinic Data Access", number: "DL-2025-CL002", validUntil: "2026-06-30" }
+ ];
+
+ const handleChange = (e) => {
+ const { name, value } = e.target;
+ setProfile((prev) => ({ ...prev, [name]: value }));
+ };
+ const handleSave = async () => {
+ try {
+ const token = localStorage.getItem('token');
+
+ const response = await fetch('https://bot.kediritechnopark.com/webhook/profile', {
+ method: 'PUT',
+ headers: {
+ 'Content-Type': 'application/json',
+ 'Authorization': `Bearer ${token}`,
+ },
+ body: JSON.stringify(profile),
+ });
+
+ if (!response.ok) throw new Error('Gagal menyimpan profil');
+
+ const result = await response.json();
+ console.log('Profil berhasil diperbarui:', result);
+
+ setIsEditing(false);
+ alert('Profil berhasil disimpan!');
+ } catch (error) {
+ console.error('Error saat menyimpan profil:', error);
+ alert('Terjadi kesalahan saat menyimpan profil.');
+ }
+ };
+
+ return (
+
+
+
Profil Perusahaan
+
+
+
+ {isMenuOpen && (
+
+
+
+
+
+
+
+
+
+ )}
+
+
+
+
+

+
+ {["name", "company", "address", "email", "phone"].map((field) => (
+
+
+
+
+
+ ))}
+ {isEditing &&
+
+ Simpan
+
+ }
+
+
+
+
+
License
+
+ {licenses.map((item) => (
+
+
{item.type}
+
No: {item.number}
+
Berlaku sampai: {item.validUntil}
+
+ ))}
+
+
+
+ );
+};
+
+export default ProfileTab;
diff --git a/src/ProfileTab.module.css b/src/ProfileTab.module.css
new file mode 100644
index 0000000..b14a068
--- /dev/null
+++ b/src/ProfileTab.module.css
@@ -0,0 +1,218 @@
+/* Container */
+.dashboardContainer {
+ width: 100%;
+ padding: 20px;
+ max-width: 1200px;
+ margin: 0 auto;
+ box-sizing: border-box;
+}
+
+/* Header */
+.profileHeader {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ flex-wrap: wrap;
+ margin-bottom: 20px;
+ gap: 12px;
+}
+
+.editButton {
+ background-color: #075e54;
+ color: white;
+ padding: 6px 12px;
+ font-size: 12px;
+ border: none;
+ border-radius: 8px;
+ display: flex;
+ align-items: center;
+ gap: 6px;
+ cursor: pointer;
+ transition: background-color 0.3s ease;
+}
+
+.editButton:hover {
+ background-color: #0f9b8a;
+}
+
+/* Profile Section */
+.profileSection {
+ display: flex;
+ flex-direction: row;
+ align-items: flex-start;
+ gap: 20px;
+ flex-wrap: wrap;
+ margin-bottom: 30px;
+}
+
+.companyImage {
+ width: 100px;
+ height: 100px;
+ border-radius: 12px;
+ object-fit: cover;
+ flex-shrink: 0;
+}
+
+.profileDetails {
+ flex: 1;
+ min-width: 260px;
+}
+
+.profileDetails p {
+ margin: 4px 0;
+ font-size: 14px;
+}
+
+/* License Section */
+.licenseSection {
+ margin-top: 20px;
+}
+
+.licenseCards {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
+ gap: 16px;
+ margin-top: 10px;
+}
+
+.licenseCard {
+ background: linear-gradient(135deg, #075e54, #128c7e);
+ color: white;
+ padding: 16px;
+ border-radius: 12px;
+ box-shadow: 0 4px 8px rgba(0,0,0,0.1);
+ font-size: 14px;
+}
+
+/* ====================
+ Responsive Breakpoints
+==================== */
+
+/* Tablet (768px – 1023px) */
+@media screen and (max-width: 1023px) {
+ .profileSection {
+ flex-direction: row;
+ justify-content: flex-start;
+ }
+
+ .profileDetails {
+ min-width: 100%;
+ }
+
+ .licenseCards {
+ grid-template-columns: repeat(2, 1fr);
+ }
+}
+
+/* Mobile (≤ 767px) */
+@media screen and (max-width: 767px) {
+ .profileHeader {
+ flex-direction: column;
+ align-items: flex-start;
+ }
+
+ .editButton {
+ align-self: flex-end;
+ }
+
+ .profileSection {
+ flex-direction: column;
+ align-items: center;
+ text-align: center;
+ }
+
+ .companyImage {
+ margin-bottom: 12px;
+ }
+
+ .profileDetails {
+ text-align: left;
+ width: 100%;
+ }
+
+ .licenseCards {
+ grid-template-columns: 1fr;
+ }
+
+ .licenseCard {
+ font-size: 13px;
+ }
+}
+.profileInputGroup {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ flex-wrap: wrap;
+ margin-bottom: 10px;
+}
+
+.profileInputGroup label {
+ min-width: 100px; /* atau sesuai label terpanjang */
+ font-size: 14px;
+}
+
+.editableInput {
+ font-size: 14px;
+ padding: 4px 8px;
+ border: 1px solid #ccc;
+ border-radius: 6px;
+ background-color: #f0f8ff;
+ transition: all 0.3s ease;
+ color: #333;
+
+ width: auto;
+ max-width: 100%;
+ flex: 1; /* biar input bisa melar jika ruang tersedia */
+}
+
+/* Saat tidak dalam mode editing */
+.readOnly {
+ border-color: transparent;
+ background-color: transparent;
+ pointer-events: none;
+ color: #000;
+}
+
+
+.dropdownContainer {
+ position: relative;
+ display: inline-block;
+ position: absolute;
+ top: 37px;
+ right: 10px;
+}
+
+.dropdownToggle {
+ color: #ffff;
+ background-color: #255e54;
+ 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;
+}
+