ok
This commit is contained in:
@@ -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(<NotificationPrompt onAllow={subscribeUser} onDismiss={null} />);
|
||||
} else {
|
||||
// Already subscribed
|
||||
setModalContent('')
|
||||
console.log('User is already subscribed.');
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
fetchData(); // Jalankan langsung saat komponen di-mount
|
||||
@@ -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 = () => {
|
||||
<div className={styles.statsGrid}>
|
||||
<div className={styles.statCard} onClick={openConversationsModal}>
|
||||
<h2>{stats.totalChats}</h2>
|
||||
<p>Total Percakapan Hari Ini</p>
|
||||
<p>Total Percakapan selama 24 jam</p>
|
||||
</div>
|
||||
<div className={styles.statCard}>
|
||||
<h2>{stats.userMessages}</h2>
|
||||
|
||||
23
src/NotificationPrompt.js
Normal file
23
src/NotificationPrompt.js
Normal file
@@ -0,0 +1,23 @@
|
||||
import React from 'react';
|
||||
import styles from './NotificationPrompt.module.css';
|
||||
|
||||
export default function NotificationPrompt({ onAllow, onDismiss }) {
|
||||
return (
|
||||
<div className={styles.backdrop}>
|
||||
<div className={styles.modal}>
|
||||
<h2 className={styles.title}>Enable Notifications</h2>
|
||||
<p className={styles.description}>
|
||||
Stay up to date with important updates and alerts. Enable push notifications to never miss a thing.
|
||||
</p>
|
||||
<div className={styles.actions}>
|
||||
<button onClick={onAllow} className={styles.primaryButton}>
|
||||
Enable Notifications
|
||||
</button>
|
||||
<button onClick={onDismiss} className={styles.secondaryButton}>
|
||||
Maybe Later
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
68
src/NotificationPrompt.module.css
Normal file
68
src/NotificationPrompt.module.css
Normal file
@@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user