notification popup

This commit is contained in:
chris 2025-10-09 12:53:14 -04:00
parent 28f4523014
commit 1729d077ab

View File

@ -24,7 +24,65 @@ let authToken = null;
let lastAdminTab = 'overview';
export { lastAdminTab };
// --- EVENT HANDLERS (The "Logic") ---
// --- NOTIFICATION LOGIC ---
// This helper function converts the VAPID public key for the browser
function urlBase64ToUint8Array(base64String) {
const padding = '='.repeat((4 - base64String.length % 4) % 4);
const base64 = (base64String + padding).replace(/\-/g, '+').replace(/_/g, '/');
const rawData = window.atob(base64);
const outputArray = new Uint8Array(rawData.length);
for (let i = 0; i < rawData.length; ++i) {
outputArray[i] = rawData.charCodeAt(i);
}
return outputArray;
}
// This function handles the actual subscription process
async function subscribeToNotifications() {
try {
const publicVapidKey = process.env.PUBLIC_VAPID_KEY; // Make sure this is set in your .env
const token = localStorage.getItem('authToken');
if (!token || !publicVapidKey) {
return console.error('Auth token or VAPID key is missing.');
}
const register = await navigator.serviceWorker.register('/sw.js', { scope: '/' });
const subscription = await register.pushManager.subscribe({
userVisibleOnly: true,
applicationServerKey: urlBase64ToUint8Array(publicVapidKey)
});
await apiCall('/subscribe', 'POST', subscription);
showMessage('You are now subscribed to notifications!', 'success');
} catch (error) {
console.error('Error subscribing to notifications:', error);
showMessage('Failed to subscribe. Please ensure notifications are allowed for this site.', 'error');
}
}
// NEW: This function creates the pop-up prompt for the user
function promptForNotifications() {
// 1. Don't ask if permission is already granted or denied
if (Notification.permission !== 'default') {
return;
}
// 2. Don't ask if we've already prompted them before
if (localStorage.getItem('notificationPrompted')) {
return;
}
// 3. Wait a couple of seconds after login to ask
setTimeout(() => {
if (confirm("Enable notifications to receive important updates about your time-off requests and notes?")) {
subscribeToNotifications();
}
// Remember that we've prompted them, so we don't ask again
localStorage.setItem('notificationPrompted', 'true');
}, 2000);
}
// --- EVENT HANDLERS ---
async function handleAuthSubmit(e) {
e.preventDefault();
const username = e.target.elements.username.value;
@ -251,7 +309,6 @@ async function handleEditTimeOffSubmit(e) {
}
}
// --- NEW: Handler for clicks specifically on the Time Off History page ---
function handleTimeOffHistoryClick(e) {
const target = e.target.closest('button');
if (!target) return;
@ -264,7 +321,7 @@ function handleTimeOffHistoryClick(e) {
.then(res => {
if (res.success) {
showMessage('Request status set to pending.', 'success');
renderTimeOffHistoryView(); // Refresh the history view
renderTimeOffHistoryView();
}
});
}
@ -276,7 +333,7 @@ function handleTimeOffHistoryClick(e) {
.then(res => {
if (res.success) {
showMessage('Request deleted.', 'success');
renderTimeOffHistoryView(); // Refresh the history view
renderTimeOffHistoryView();
}
});
}
@ -334,7 +391,6 @@ export function attachAdminDashboardListeners() {
setupTabbedInterface();
}
// --- NEW: Listener attachment function for the history page ---
export function attachTimeOffHistoryListeners() {
const historyView = document.getElementById('admin-time-off-history-view');
if (historyView) {
@ -348,8 +404,8 @@ function initializeApp() {
const userString = localStorage.getItem('user');
user = userString ? JSON.parse(userString) : null;
if (authToken && user) {
const userControls = document.getElementById('nav-user-controls');
if (authToken && user) {
userControls.classList.remove('hidden');
userControls.querySelector('#welcome-message').textContent = `Welcome, ${user.username}`;
if (user.role === 'admin') {
@ -357,8 +413,10 @@ function initializeApp() {
} else {
renderEmployeeDashboard();
}
// Ask for notification permission after user is logged in
promptForNotifications();
} else {
document.getElementById('nav-user-controls').classList.add('hidden');
userControls.classList.add('hidden');
renderAuthView();
}
}
@ -388,72 +446,9 @@ function setupTabbedInterface() {
document.getElementById(`tab-content-${tabTarget}`).classList.remove('hidden');
});
}
// This converts the VAPID public key string to the format the browser needs.
function urlBase64ToUint8Array(base64String) {
const padding = '='.repeat((4 - base64String.length % 4) % 4);
const base64 = (base64String + padding)
.replace(/\-/g, '+')
.replace(/_/g, '/');
const rawData = window.atob(base64);
const outputArray = new Uint8Array(rawData.length);
for (let i = 0; i < rawData.length; ++i) {
outputArray[i] = rawData.charCodeAt(i);
}
return outputArray;
}
// --- Subscription Logic ---
async function subscribeToNotifications() {
// IMPORTANT: Replace with your actual public VAPID key
const publicVapidKey = 'YOUR_PUBLIC_VAPID_KEY';
const token = localStorage.getItem('token');
// Check if user is logged in first
if (!token) {
console.error('User is not logged in.');
alert('You must be logged in to enable notifications.');
return;
}
// 1. Register the service worker
const register = await navigator.serviceWorker.register('/sw.js', {
scope: '/'
});
// 2. Get the push subscription
const subscription = await register.pushManager.subscribe({
userVisibleOnly: true,
applicationServerKey: urlBase64ToUint8Array(publicVapidKey)
});
// 3. Send the subscription to your authenticated backend
await fetch('/subscribe', {
method: 'POST',
body: JSON.stringify(subscription),
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}` // The crucial update!
}
});
alert('You are now subscribed to notifications!');
}
// --- Attach to a Button ---
// Assuming you have a button in your HTML with id="notifyBtn"
const notifyBtn = document.getElementById('notifyBtn');
if (notifyBtn) {
notifyBtn.addEventListener('click', () => {
subscribeToNotifications().catch(error => {
console.error('Error subscribing to notifications:', error);
alert('Failed to subscribe. Please make sure notifications are allowed for this site.');
});
});
}
// --- START THE APP ---
// Register the service worker when the page loads
if ('serviceWorker' in navigator) {
window.addEventListener('load', () => {
navigator.serviceWorker.register('/sw.js')