vereinshaus/app/src/sw.ts
rene 77c6f513b5 Feature: SEPA-Export, Push-Notifications, Onboarding + vollständige UI
- Phosphor Icons (Icon.svelte, svg-Registry)
- Schema-Abgleich: alle Felder zwischen PB-Migrations und types.ts konsistent
- Stripe entfernt, SEPA pain.008 XML-Export implementiert (sepa.ts)
- Beiträge: vollständiges CRUD + SEPA-Einzug-Sheet mit Vorschau
- Termine: vollständiges CRUD (upcoming/vergangen, datetime-local)
- Mitglieder: Formulare um alle Felder erweitert (Adresse, SEPA-Mandat, Notizen)
- Nachrichten: Brevo E-Mail via PocketBase-Hook, UI mit Gruppen-Filter
- Push-Notifications: VAPID, Custom Service Worker (injectManifest),
  Subscribe/Send API-Routen, automatische Subscription nach Login
- Onboarding: 3-Schritt-Flow für neue Vereine, Guard im App-Layout
- Makefile: .env wird vollständig zur DS übertragen
2026-05-20 13:01:11 +02:00

45 lines
1.4 KiB
TypeScript

/// <reference lib="webworker" />
import { cleanupOutdatedCaches, precacheAndRoute } from 'workbox-precaching';
import { registerRoute } from 'workbox-routing';
import { NetworkFirst } from 'workbox-strategies';
declare const self: ServiceWorkerGlobalScope;
cleanupOutdatedCaches();
precacheAndRoute(self.__WB_MANIFEST);
// PocketBase API: NetworkFirst (offline-fähig aus Cache)
registerRoute(
({ url }) => url.hostname === 'api.vereins.haus',
new NetworkFirst({
cacheName: 'pocketbase-api',
networkTimeoutSeconds: 5,
}),
);
self.addEventListener('push', (event) => {
const data = event.data?.json() ?? {};
const title = data.title ?? 'vereins.haus';
const options: NotificationOptions = {
body: data.body ?? '',
icon: '/icons/icon-192.png',
badge: '/icons/icon-192.png',
tag: data.tag ?? 'vereinshaus',
data: { url: data.url ?? '/nachrichten' },
};
event.waitUntil(self.registration.showNotification(title, options));
});
self.addEventListener('notificationclick', (event) => {
event.notification.close();
const url = (event.notification.data as { url: string })?.url ?? '/';
event.waitUntil(
self.clients
.matchAll({ type: 'window', includeUncontrolled: true })
.then((list) => {
const existing = list.find((c) => c.url.includes(url));
if (existing) return existing.focus();
return self.clients.openWindow(url);
}),
);
});