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; +}