From 4c69478fa89b3ef167195f6ad899d8ac966ee067 Mon Sep 17 00:00:00 2001 From: elpatron Date: Tue, 18 Mar 2025 16:16:47 +0100 Subject: [PATCH] =?UTF-8?q?Feat:=20PWA-Implementierung=20-=20Offline-Funkt?= =?UTF-8?q?ionalit=C3=A4t=20hinzugef=C3=BCgt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app.py | 12 ++++++- static/manifest.json | 28 ++++++++++++++++ static/sw.js | 75 ++++++++++++++++++++++++++++++++++++++++++ templates/index.html | 28 ++++++++++++++++ templates/offline.html | 58 ++++++++++++++++++++++++++++++++ 5 files changed, 200 insertions(+), 1 deletion(-) create mode 100644 static/manifest.json create mode 100644 static/sw.js create mode 100644 templates/offline.html diff --git a/app.py b/app.py index accc008..5b1267b 100644 --- a/app.py +++ b/app.py @@ -1,4 +1,4 @@ -from flask import Flask, render_template, request, jsonify, url_for, redirect, session +from flask import Flask, render_template, request, jsonify, url_for, redirect, session, make_response, send_from_directory import pandas as pd import os import logging @@ -320,6 +320,16 @@ def search(): logger.error(f'Fehler bei der Suche: {str(e)}') return jsonify({"error": str(e)}), 500 +@app.route('/sw.js') +def sw(): + response = make_response(send_from_directory('static', 'sw.js')) + response.headers['Content-Type'] = 'application/javascript' + return response + +@app.route('/offline') +def offline(): + return render_template('offline.html') + def init_app(app): """Initialisiert die Anwendung mit allen notwendigen Einstellungen.""" with app.app_context(): diff --git a/static/manifest.json b/static/manifest.json new file mode 100644 index 0000000..30b0bec --- /dev/null +++ b/static/manifest.json @@ -0,0 +1,28 @@ +{ + "name": "MEDI Kunden", + "short_name": "MEDI", + "description": "MEDI Kundenverwaltung - Offline-fähige PWA", + "start_url": "/", + "display": "standalone", + "background_color": "#ffffff", + "theme_color": "#4CAF50", + "orientation": "portrait", + "icons": [ + { + "src": "/static/images/icon-192x192.png", + "sizes": "192x192", + "type": "image/png", + "purpose": "any maskable" + }, + { + "src": "/static/images/icon-512x512.png", + "sizes": "512x512", + "type": "image/png", + "purpose": "any maskable" + } + ], + "categories": ["business", "productivity"], + "lang": "de-DE", + "dir": "ltr", + "prefer_related_applications": false +} \ No newline at end of file diff --git a/static/sw.js b/static/sw.js new file mode 100644 index 0000000..941c692 --- /dev/null +++ b/static/sw.js @@ -0,0 +1,75 @@ +const CACHE_NAME = 'medi-customers-v1'; +const urlsToCache = [ + '/', + '/static/css/styles.css', + '/static/js/script.js', + '/static/images/logo.png', + '/static/images/icon-192x192.png', + '/static/images/icon-512x512.png' +]; + +// Installation des Service Workers +self.addEventListener('install', event => { + event.waitUntil( + caches.open(CACHE_NAME) + .then(cache => { + console.log('Cache geöffnet'); + return cache.addAll(urlsToCache); + }) + ); +}); + +// Aktivierung des Service Workers +self.addEventListener('activate', event => { + event.waitUntil( + caches.keys().then(cacheNames => { + return Promise.all( + cacheNames.map(cacheName => { + if (cacheName !== CACHE_NAME) { + console.log('Lösche alten Cache:', cacheName); + return caches.delete(cacheName); + } + }) + ); + }) + ); +}); + +// Fetch-Event-Handler +self.addEventListener('fetch', event => { + event.respondWith( + caches.match(event.request) + .then(response => { + // Cache-Treffer - gib die Antwort zurück + if (response) { + return response; + } + + // Kein Cache-Treffer - führe Netzwerkanfrage durch + return fetch(event.request) + .then(response => { + // Prüfe, ob wir eine gültige Antwort erhalten haben + if (!response || response.status !== 200 || response.type !== 'basic') { + return response; + } + + // Klone die Antwort + const responseToCache = response.clone(); + + // Speichere die Antwort im Cache + caches.open(CACHE_NAME) + .then(cache => { + cache.put(event.request, responseToCache); + }); + + return response; + }) + .catch(() => { + // Fallback für Offline-Zugriff + if (event.request.mode === 'navigate') { + return caches.match('/offline.html'); + } + }); + }) + ); +}); \ No newline at end of file diff --git a/templates/index.html b/templates/index.html index 145eca4..61c6496 100644 --- a/templates/index.html +++ b/templates/index.html @@ -5,6 +5,12 @@ medisoftware Kundensuche + + + + + + @@ -105,6 +111,28 @@ + + \ No newline at end of file