Stand: SMTP-Test, Admin-Mail-Tab, Notifiable-Fix, Lazy-Quill

- Fix: Notifiable-Trait zum User-Model hinzugefuegt (behebt notify()-500er)
- Installer: SMTP-Verbindungstest mit EsmtpTransport + Ueberspringen-Link
- Admin: Neuer E-Mail-Tab mit SMTP-Konfiguration + Verbindungstest
- Admin: Lazy Quill-Initialisierung (nur sichtbare Locale wird geladen)
- Uebersetzungen: 17 neue Mail-Keys in allen 6 Sprachen

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Rhino
2026-03-02 07:30:37 +01:00
commit 2e24a40d68
9633 changed files with 1300799 additions and 0 deletions

95
public/sw.js Normal file
View File

@@ -0,0 +1,95 @@
// ============================================================
// Service Worker SG Wölfe Handball WebApp
// Strategie: Lokale Assets cachen, HTML Network-First
// ============================================================
const CACHE_NAME = 'handball-v3';
const OFFLINE_URL = '/offline';
// Lokale Assets, die beim Install gecached werden
// (CDN-Assets wie Tailwind/Alpine werden nicht gecached
// sie kommen extern und sind zu groß/dynamisch)
const PRECACHE_ASSETS = [
'/offline',
'/images/icon-192x192.png',
'/images/icon-512x512.png',
'/manifest.json',
'/images/logo_woelfe.png'
];
// ---- INSTALL ----
self.addEventListener('install', (event) => {
event.waitUntil(
caches.open(CACHE_NAME).then((cache) => {
// Einzeln cachen, damit ein 404 nicht den gesamten Install blockiert
return Promise.all(
PRECACHE_ASSETS.map((url) =>
cache.add(url).catch((err) => {
console.warn('SW: Precache fehlgeschlagen für', url, err);
})
)
);
})
);
self.skipWaiting();
});
// ---- ACTIVATE ----
self.addEventListener('activate', (event) => {
event.waitUntil(
caches.keys().then((cacheNames) => {
return Promise.all(
cacheNames
.filter((name) => name !== CACHE_NAME)
.map((name) => caches.delete(name))
);
})
);
self.clients.claim();
});
// ---- FETCH ----
self.addEventListener('fetch', (event) => {
const { request } = event;
// Nur GET-Requests behandeln (keine Formulare/POST)
if (request.method !== 'GET') return;
// Keine externen Requests behandeln (CDN, Leaflet-Tiles, Nominatim)
if (!request.url.startsWith(self.location.origin)) return;
// Statische Assets: Cache-First
if (isStaticAsset(request.url)) {
event.respondWith(
caches.match(request).then((cached) => {
return cached || fetch(request).then((response) => {
if (response.ok) {
const clone = response.clone();
caches.open(CACHE_NAME).then((cache) => cache.put(request, clone));
}
return response;
});
})
);
return;
}
// HTML-Seiten: Network-First mit Offline-Fallback
if (request.headers.get('Accept')?.includes('text/html')) {
event.respondWith(
fetch(request)
.then((response) => {
return response;
})
.catch(() => {
return caches.match(OFFLINE_URL);
})
);
return;
}
});
// ---- HILFSFUNKTIONEN ----
function isStaticAsset(url) {
return /\.(css|js|png|jpg|jpeg|gif|svg|ico|woff2?|ttf|eot|webp)(\?.*)?$/.test(url);
}