+
+
+
+ {recipeCount > 0 && <>{recipeCount} Rezept{recipeCount !== 1 ? 'e' : ''} · >}
+ {totalItems} Artikel · {totalChecked} erledigt
+
+ {totalItems > 0 ? Math.round((totalChecked / totalItems) * 100) : 0}%
+
+
+
0 ? (totalChecked / totalItems) * 100 : 0}%` }}
+ />
+
+
+
+ )}
+
{/* Content */}
{groups.length === 0 ? (
diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts
index 9aac067..4207f70 100644
--- a/frontend/vite.config.ts
+++ b/frontend/vite.config.ts
@@ -9,6 +9,31 @@ export default defineConfig({
tailwindcss(),
VitePWA({
registerType: 'autoUpdate',
+ workbox: {
+ globPatterns: ['**/*.{js,css,html,ico,png,svg,webp,woff2}'],
+ runtimeCaching: [
+ {
+ urlPattern: /\/api\/shopping/,
+ handler: 'NetworkFirst',
+ options: { cacheName: 'shopping-api', expiration: { maxEntries: 10, maxAgeSeconds: 86400 } },
+ },
+ {
+ urlPattern: /\/api\/recipes/,
+ handler: 'StaleWhileRevalidate',
+ options: { cacheName: 'recipes-api', expiration: { maxEntries: 50, maxAgeSeconds: 86400 } },
+ },
+ {
+ urlPattern: /\/api\/categories/,
+ handler: 'CacheFirst',
+ options: { cacheName: 'categories-api', expiration: { maxEntries: 10, maxAgeSeconds: 604800 } },
+ },
+ {
+ urlPattern: /\/images\//,
+ handler: 'CacheFirst',
+ options: { cacheName: 'recipe-images', expiration: { maxEntries: 100, maxAgeSeconds: 2592000 } },
+ },
+ ],
+ },
manifest: {
name: 'Luna Recipes',
short_name: 'Luna',