Werk hoofdapplicatie bij

Legt de bestaande wijzigingen in de hoofdworktree vast voordat de laatste worktree wordt gemerged.\n\nCo-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
2026-08-07 20:45:36 +02:00
parent 26a820b41a
commit c07d503d83
41 changed files with 1837 additions and 313 deletions
+34 -3
View File
@@ -1,12 +1,43 @@
body {
background: #f5f7fb;
}
background-color: #f6f8fb;
.navbar {
margin-bottom: 25px;
}
.card {
border: 0;
border-radius: 12px;
box-shadow: 0 2px 8px rgba(0,0,0,.06);
}
.page-title {
font-weight: 700;
}
.alert {
animation: fadeIn .3s ease;
}
@keyframes fadeIn {
.navbar-brand {
from{
font-weight: 700;
opacity:0;
transform:translateY(-10px);
}
to{
opacity:1;
transform:translateY(0);
}
}
+5
View File
@@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" role="img" aria-labelledby="title">
<title id="title">PantryHub</title>
<rect width="256" height="256" rx="48" fill="#206bc4"/>
<path fill="#fff" d="M59 69h18l13 72h106l17-53H94l-5-26H59zm39 88a17 17 0 1 0 0 34 17 17 0 0 0 0-34zm87 0a17 17 0 1 0 0 34 17 17 0 0 0 0-34z"/>
</svg>

After

Width:  |  Height:  |  Size: 347 B

+48
View File
@@ -0,0 +1,48 @@
// Socket.IO Client
const socket = typeof io === 'function' ? io() : null;
// Join household room if available
const householdId = document.querySelector('[data-household-id]')?.dataset.householdId;
if (socket && householdId) {
socket.emit('join:household', householdId);
}
// Real-time list updates
socket?.on('list:updated', (data) => {
console.log('📝 Lijst bijgewerkt:', data);
window.location.reload();
});
socket?.on('item:added', (data) => {
console.log(' Item toegevoegd:', data);
window.location.reload();
});
socket?.on('item:toggled', (data) => {
console.log('✓ Item afgevinkt:', data);
window.location.reload();
});
socket?.on('item:removed', (data) => {
console.log('✕ Item verwijderd:', data);
window.location.reload();
});
// Auto-remove alerts
document.addEventListener("DOMContentLoaded", () => {
setTimeout(() => {
document
.querySelectorAll(".alert")
.forEach(alert => {
alert.remove();
});
}, 4000);
});
console.log('✅ PantryHub app geladen');
+43
View File
@@ -0,0 +1,43 @@
{
"name": "PantryHub",
"short_name": "PantryHub",
"description": "Realtime boodschappenapp",
"start_url": "/",
"scope": "/",
"display": "standalone",
"background_color": "#ffffff",
"theme_color": "#0066cc",
"icons": [
{
"src": "/icons/icon.svg",
"sizes": "any",
"type": "image/svg+xml",
"purpose": "any"
},
{
"src": "/icons/icon.svg",
"sizes": "any",
"type": "image/svg+xml",
"purpose": "any"
}
],
"shortcuts": [
{
"name": "Lijsten",
"short_name": "Lijsten",
"description": "Bekijk je boodschappenlijsten",
"url": "/lists",
"icons": [
{
"src": "/icons/icon.svg",
"sizes": "any",
"type": "image/svg+xml"
}
]
}
],
"categories": [
"shopping",
"lifestyle"
]
}
+98
View File
@@ -0,0 +1,98 @@
const CACHE_NAME = 'pantryhub-v2';
const urlsToCache = [
'/',
'/css/style.css',
'/js/app.js',
'/manifest.json',
'/icons/icon.svg'
];
// Install event
self.addEventListener('install', (event) => {
event.waitUntil(
caches.open(CACHE_NAME).then((cache) => {
return cache.addAll(urlsToCache).catch(err => {
console.log('Cache addAll error:', err);
});
})
);
});
// Activate event
self.addEventListener('activate', (event) => {
event.waitUntil(
caches.keys().then((cacheNames) => {
return Promise.all(
cacheNames.map((cacheName) => {
if (cacheName !== CACHE_NAME) {
return caches.delete(cacheName);
}
})
);
})
);
});
// Fetch event
self.addEventListener('fetch', (event) => {
// Skip non-GET requests
if (event.request.method !== 'GET') {
return;
}
// Browser extensions and third-party resources cannot be stored by Cache API.
// Only handle same-origin HTTP(S) requests belonging to PantryHub.
const url = new URL(event.request.url);
if (!['http:', 'https:'].includes(url.protocol) || url.origin !== self.location.origin) {
return;
}
// For API calls, use network-first strategy
if (event.request.url.includes('/api/')) {
event.respondWith(
fetch(event.request)
.then(response => {
const responseClone = response.clone();
caches.open(CACHE_NAME).then(cache => {
cache.put(event.request, responseClone);
});
return response;
})
.catch(() => {
return caches.match(event.request);
})
);
return;
}
// For other requests, use cache-first strategy
event.respondWith(
caches.match(event.request)
.then(response => {
if (response) {
return response;
}
return fetch(event.request).then(response => {
if (!response || response.status !== 200 || response.type !== 'basic') {
return response;
}
const responseClone = response.clone();
caches.open(CACHE_NAME)
.then(cache => {
cache.put(event.request, responseClone);
});
return response;
});
})
.catch(() => {
// Return offline page or empty response
return new Response('Offline - pagina niet beschikbaar', {
status: 503,
statusText: 'Service Unavailable',
headers: new Headers({
'Content-Type': 'text/plain'
})
});
})
);
});