Files
Autoparts-DB/pos/static/js/push.js
consultoria-as 5d5a2777eb feat(pos): add 3 improvements — Spanish translations, PDF quotes, push notifications
1. Spanish translations for TecDoc catalog (translations.py) applied to
   catalog_service.py and dashboard server.py endpoints
2. Printable quotation HTML endpoint (/pos/api/quotations/<id>/pdf) with
   @media print CSS for clean browser-to-PDF output
3. Web Push notifications to owner/admin on sale cancellation, stock zero,
   and cash register differences > $500. Includes service worker, VAPID
   key management, and subscription endpoints.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-04 08:05:11 +00:00

96 lines
3.2 KiB
JavaScript

/**
* push.js — Web Push notification setup for Nexus POS
*
* Registers a service worker and subscribes to push notifications.
* Only activates for owner/admin roles.
*/
(function() {
'use strict';
// Only set up push for owner/admin
var employee = {};
try { employee = JSON.parse(localStorage.getItem('pos_employee') || '{}'); } catch(e) {}
var role = employee.role || '';
if (role !== 'owner' && role !== 'admin') return;
// Check browser support
if (!('serviceWorker' in navigator) || !('PushManager' in window)) {
console.log('[Push] Browser does not support push notifications');
return;
}
var token = localStorage.getItem('pos_token');
if (!token) return;
function urlBase64ToUint8Array(base64String) {
var padding = '='.repeat((4 - base64String.length % 4) % 4);
var base64 = (base64String + padding).replace(/-/g, '+').replace(/_/g, '/');
var rawData = window.atob(base64);
var outputArray = new Uint8Array(rawData.length);
for (var i = 0; i < rawData.length; ++i) {
outputArray[i] = rawData.charCodeAt(i);
}
return outputArray;
}
async function setupPush() {
try {
// Register service worker
var registration = await navigator.serviceWorker.register('/pos/static/sw-push.js', {
scope: '/pos/'
});
console.log('[Push] Service worker registered');
// Get VAPID key from server
var resp = await fetch('/pos/api/push/vapid-key', {
headers: { 'Authorization': 'Bearer ' + token }
});
if (!resp.ok) {
console.log('[Push] VAPID key not available:', resp.status);
return;
}
var data = await resp.json();
var vapidKey = data.public_key;
if (!vapidKey) return;
// Request permission
var permission = await Notification.requestPermission();
if (permission !== 'granted') {
console.log('[Push] Permission denied');
return;
}
// Subscribe
var subscription = await registration.pushManager.subscribe({
userVisibleOnly: true,
applicationServerKey: urlBase64ToUint8Array(vapidKey)
});
// Send subscription to server
var subResp = await fetch('/pos/api/push/subscribe', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + token
},
body: JSON.stringify({ subscription: subscription.toJSON() })
});
if (subResp.ok) {
console.log('[Push] Subscribed successfully');
}
} catch(err) {
console.log('[Push] Setup error:', err);
}
}
// Delay push setup to not block page load
if (document.readyState === 'complete') {
setTimeout(setupPush, 2000);
} else {
window.addEventListener('load', function() {
setTimeout(setupPush, 2000);
});
}
})();