diff --git a/src/Dashboard.js b/src/Dashboard.js
index 499fad4..5b51070 100644
--- a/src/Dashboard.js
+++ b/src/Dashboard.js
@@ -7,6 +7,7 @@ import Conversations from './Conversations';
import DiscussedTopics from './DiscussedTopics';
import Chart from 'chart.js/auto';
+import NotificationPrompt from './NotificationPrompt';
const Dashboard = () => {
const [isMenuOpen, setIsMenuOpen] = useState(false);
@@ -46,6 +47,25 @@ const Dashboard = () => {
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();
};
@@ -79,8 +99,7 @@ const Dashboard = () => {
});
if (response.status === 401 || response.status === 403) {
- localStorage.removeItem('token');
- navigate('/login');
+ handleLogout();
return;
}
@@ -126,8 +145,19 @@ const Dashboard = () => {
};
if (!checkOnce && 'serviceWorker' in navigator) {
- subscribeUser();
- setCheckOnce(false);
+ navigator.serviceWorker.ready.then(function (registration) {
+ registration.pushManager.getSubscription().then(function (subscription) {
+ setCheckOnce(false);
+ if (subscription === null) {
+ // Not subscribed yet — show modal asking user to subscribe
+ setModalContent();
+ } else {
+ // Already subscribed
+ setModalContent('')
+ console.log('User is already subscribed.');
+ }
+ });
+ });
}
fetchData(); // Jalankan langsung saat komponen di-mount
@@ -147,7 +177,7 @@ const Dashboard = () => {
});
const token = localStorage.getItem('token');
-
+
await fetch('https://bot.kediritechnopark.com/webhook/subscribe', {
method: 'POST',
body: JSON.stringify({
@@ -159,6 +189,7 @@ const Dashboard = () => {
},
});
+ setModalContent('')
};
function urlBase64ToUint8Array(base64String) {
@@ -189,17 +220,22 @@ const Dashboard = () => {
const prefixLabelMap = {
WEB: 'Web App',
TGG: 'Telegram',
- DME: 'Instagram',
+ WGG: 'Whatsapp',
};
const prefixColors = {
WEB: { border: '#4285F4', background: 'rgba(66, 133, 244, 0.2)' },
TGG: { border: '#25D366', background: 'rgba(37, 211, 102, 0.2)' },
- DME: { border: '#AA00FF', background: 'rgba(170, 0, 255, 0.2)' },
+ WGG: { border: '#AA00FF', background: 'rgba(170, 0, 255, 0.2)' },
};
const prefixes = Object.keys(prefixLabelMap);
- const hours = rawData.map(d => d.hour).sort((a, b) => parseFloat(a) - parseFloat(b));
+const hours = rawData.map(d => d.hour.split(' ')[1]).sort((a, b) => {
+ // Sort berdasarkan jam dan menit
+ const [h1, m1] = a.split(':').map(Number);
+ const [h2, m2] = b.split(':').map(Number);
+ return h1 !== h2 ? h1 - h2 : m1 - m2;
+});
const counts = {};
prefixes.forEach(prefix => {
@@ -298,7 +334,7 @@ const Dashboard = () => {
{stats.totalChats}
-
Total Percakapan Hari Ini
+
Total Percakapan selama 24 jam
{stats.userMessages}
diff --git a/src/NotificationPrompt.js b/src/NotificationPrompt.js
new file mode 100644
index 0000000..3bc4984
--- /dev/null
+++ b/src/NotificationPrompt.js
@@ -0,0 +1,23 @@
+import React from 'react';
+import styles from './NotificationPrompt.module.css';
+
+export default function NotificationPrompt({ onAllow, onDismiss }) {
+ return (
+
+
+
Enable Notifications
+
+ Stay up to date with important updates and alerts. Enable push notifications to never miss a thing.
+
+
+
+
+
+
+
+ );
+}
diff --git a/src/NotificationPrompt.module.css b/src/NotificationPrompt.module.css
new file mode 100644
index 0000000..5e26da1
--- /dev/null
+++ b/src/NotificationPrompt.module.css
@@ -0,0 +1,68 @@
+.backdrop {
+ position: fixed;
+ inset: 0;
+ background-color: rgba(0, 0, 0, 0.45);
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ z-index: 9999;
+}
+
+.modal {
+ background-color: #fff;
+ border-radius: 8px;
+ padding: 32px;
+ max-width: 420px;
+ width: 90%;
+ box-shadow: 0 12px 24px rgba(0, 0, 0, 0.15);
+ text-align: center;
+}
+
+.title {
+ margin-bottom: 16px;
+ font-size: 22px;
+ font-weight: 600;
+ color: #1f2937;
+}
+
+.description {
+ margin-bottom: 24px;
+ font-size: 16px;
+ color: #4b5563;
+ line-height: 1.5;
+}
+
+.actions {
+ display: flex;
+ flex-direction: column;
+ gap: 12px;
+}
+
+.primaryButton {
+ background-color: #0073bb;
+ color: white;
+ font-weight: 500;
+ padding: 12px 20px;
+ border: none;
+ border-radius: 6px;
+ cursor: pointer;
+ transition: background-color 0.2s;
+}
+
+.primaryButton:hover {
+ background-color: #005a99;
+}
+
+.secondaryButton {
+ background-color: transparent;
+ color: #374151;
+ border: 1px solid #d1d5db;
+ padding: 12px 20px;
+ border-radius: 6px;
+ cursor: pointer;
+ transition: background-color 0.2s;
+}
+
+.secondaryButton:hover {
+ background-color: #f3f4f6;
+}