Phase 8: Visitor Management - Visitor/VisitorLog/AccessZone models - Check-in/out with badge generation - Zone occupancy tracking - Kiosk and management pages Phase 9: Messaging & Communication - Announcements with priority levels - Acknowledgement tracking - Shift notes for team handoffs - AnnouncementBanner component Phase 10: Compliance & Audit Trail - Immutable AuditLog model - Document versioning and approval workflow - Acknowledgement tracking for SOPs - CSV export for audit logs Phase 11: Accessibility & i18n - WCAG 2.1 AA compliance utilities - react-i18next with EN/ES translations - User preferences context (theme, font size, etc) - High contrast and reduced motion support Phase 12: Hardware Integration - QR code generation for batches/plants/visitors - Printable label system - Visitor badge printing Phase 13: Advanced Features - Environmental monitoring (sensors, readings, alerts) - Financial tracking (transactions, P&L reports) - AI/ML insights (yield predictions, anomaly detection)
154 lines
4 KiB
JavaScript
154 lines
4 KiB
JavaScript
/// <reference lib="webworker" />
|
|
|
|
const CACHE_NAME = 'wolfpack-v1';
|
|
const STATIC_ASSETS = [
|
|
'/',
|
|
'/index.html',
|
|
'/manifest.json'
|
|
];
|
|
|
|
const API_CACHE = 'wolfpack-api-v1';
|
|
const API_CACHE_MAX_AGE = 5 * 60 * 1000; // 5 minutes
|
|
|
|
declare const self: ServiceWorkerGlobalScope;
|
|
|
|
// Install event - cache static assets
|
|
self.addEventListener('install', (event) => {
|
|
event.waitUntil(
|
|
caches.open(CACHE_NAME).then((cache) => {
|
|
console.log('[SW] Caching static assets');
|
|
return cache.addAll(STATIC_ASSETS);
|
|
})
|
|
);
|
|
self.skipWaiting();
|
|
});
|
|
|
|
// Activate event - clean old caches
|
|
self.addEventListener('activate', (event) => {
|
|
event.waitUntil(
|
|
caches.keys().then((cacheNames) => {
|
|
return Promise.all(
|
|
cacheNames
|
|
.filter((name) => name !== CACHE_NAME && name !== API_CACHE)
|
|
.map((name) => caches.delete(name))
|
|
);
|
|
})
|
|
);
|
|
self.clients.claim();
|
|
});
|
|
|
|
// Fetch event - network-first for API, cache-first for assets
|
|
self.addEventListener('fetch', (event) => {
|
|
const { request } = event;
|
|
const url = new URL(request.url);
|
|
|
|
// Skip non-GET requests
|
|
if (request.method !== 'GET') return;
|
|
|
|
// API requests - network first, fallback to cache
|
|
if (url.pathname.startsWith('/api')) {
|
|
event.respondWith(networkFirstWithCache(request));
|
|
return;
|
|
}
|
|
|
|
// Static assets - cache first, fallback to network
|
|
event.respondWith(cacheFirstWithNetwork(request));
|
|
});
|
|
|
|
async function networkFirstWithCache(request: Request): Promise<Response> {
|
|
try {
|
|
const response = await fetch(request);
|
|
|
|
// Cache successful GET responses
|
|
if (response.ok) {
|
|
const cache = await caches.open(API_CACHE);
|
|
cache.put(request, response.clone());
|
|
}
|
|
|
|
return response;
|
|
} catch (error) {
|
|
// Network failed, try cache
|
|
const cached = await caches.match(request);
|
|
if (cached) {
|
|
console.log('[SW] Serving from cache:', request.url);
|
|
return cached;
|
|
}
|
|
|
|
// Return offline response for API
|
|
return new Response(
|
|
JSON.stringify({ error: 'You are offline', offline: true }),
|
|
{
|
|
status: 503,
|
|
headers: { 'Content-Type': 'application/json' }
|
|
}
|
|
);
|
|
}
|
|
}
|
|
|
|
async function cacheFirstWithNetwork(request: Request): Promise<Response> {
|
|
const cached = await caches.match(request);
|
|
if (cached) {
|
|
return cached;
|
|
}
|
|
|
|
try {
|
|
const response = await fetch(request);
|
|
|
|
// Cache successful responses
|
|
if (response.ok) {
|
|
const cache = await caches.open(CACHE_NAME);
|
|
cache.put(request, response.clone());
|
|
}
|
|
|
|
return response;
|
|
} catch (error) {
|
|
// Return offline page for navigation requests
|
|
if (request.mode === 'navigate') {
|
|
const offlinePage = await caches.match('/');
|
|
if (offlinePage) return offlinePage;
|
|
}
|
|
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
// Background sync for offline actions
|
|
self.addEventListener('sync', (event) => {
|
|
if (event.tag === 'sync-touchpoints') {
|
|
event.waitUntil(syncTouchpoints());
|
|
}
|
|
});
|
|
|
|
async function syncTouchpoints() {
|
|
// This would sync any queued offline actions
|
|
console.log('[SW] Syncing offline actions...');
|
|
}
|
|
|
|
// Push notifications
|
|
self.addEventListener('push', (event) => {
|
|
if (!event.data) return;
|
|
|
|
const data = event.data.json();
|
|
|
|
event.waitUntil(
|
|
self.registration.showNotification(data.title || '777 Wolfpack', {
|
|
body: data.body,
|
|
icon: '/icon-192.png',
|
|
badge: '/badge-72.png',
|
|
tag: data.tag || 'notification',
|
|
data: data.url
|
|
})
|
|
);
|
|
});
|
|
|
|
self.addEventListener('notificationclick', (event) => {
|
|
event.notification.close();
|
|
|
|
if (event.notification.data) {
|
|
event.waitUntil(
|
|
self.clients.openWindow(event.notification.data)
|
|
);
|
|
}
|
|
});
|
|
|
|
export { };
|