From a08baabe7ce2f2aece683fc74c0635c740b2d27a Mon Sep 17 00:00:00 2001 From: cas Date: Fri, 24 Jul 2026 03:02:17 +0200 Subject: [PATCH] =?UTF-8?q?fix:=20event-delegatie=20naar=20init()=20verpla?= =?UTF-8?q?atst=20(1x=20registreren,=20niet=20per=20render)=20=E2=80=94=20?= =?UTF-8?q?move/delete=20werken=20nu=20Root=20cause:=20listEl.addEventList?= =?UTF-8?q?ener=20werd=20elke=20renderDagboek=20call=20opnieuw=20toegevoeg?= =?UTF-8?q?d=20=E2=86=92=204+=20handlers=20per=20klik.=20Fix:=20delegatie?= =?UTF-8?q?=20in=20init()=20met=20=5FdelegationSetup=20flag,=20slechts=201?= =?UTF-8?q?=20registratie.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker/index.html | 174 ++++++++++++++++++++++++---------------------- index.html | 174 ++++++++++++++++++++++++---------------------- tests/e2e.spec.js | 13 ++-- 3 files changed, 182 insertions(+), 179 deletions(-) diff --git a/docker/index.html b/docker/index.html index fc3cc97..c8619e3 100644 --- a/docker/index.html +++ b/docker/index.html @@ -2913,85 +2913,6 @@ button:active, .btn:active, .meal-card-main:active, .dagboek-item:active, .food- }); }); - // === Unified meal-group action delegation === - listEl.addEventListener('click', (e) => { - const btn = e.target.closest('[data-action]'); - if (!btn) return; - const action = btn.dataset.action; - const momentId = btn.dataset.moment; - const entryIdx = btn.dataset.entry != null ? parseInt(btn.dataset.entry) : null; - - if (action === 'toggle-meal') { - const group = btn.closest('.eetmoment-meal-group'); - if (!group) return; - const wrap = group.querySelector('.eetmoment-meal-items-wrap'); - const actions = group.querySelector('.eetmoment-meal-actions'); - if (wrap) { - wrap.classList.toggle('hidden'); - if (actions) actions.classList.toggle('hidden'); - const arrow = btn.querySelector('span:last-child i'); - if (arrow) { - arrow.className = wrap.classList.contains('hidden') ? 'fas fa-chevron-down' : 'fas fa-chevron-up'; - } - } - } - else if (action === 'edit-porties') { - showBulkPortieEdit(activeDate, momentId, entryIdx); - } - else if (action === 'delete-meal') { - const dayData = getOrCreateDay(activeDate); - const entry = (dayData[momentId] || [])[entryIdx]; - const mealName = (entry && entry.mealNaam) || 'maaltijd'; - showConfirmModal('Maaltijd verwijderen', - 'Deze hele maaltijd verwijderen uit je dagboek?', - () => { - dayData[momentId].splice(entryIdx, 1); - saveDagboek(); - renderDagboek(); - }); - } - else if (action === 'move-meal') { - const overlay = document.createElement('div'); - overlay.className = 'modal-overlay'; - overlay.style.zIndex = '500'; - const options = EETMOMENTEN.map(m => - `` - ).join(''); - overlay.innerHTML = ` - - `; - document.body.appendChild(overlay); - const close = () => { - overlay.classList.add('closing'); - setTimeout(() => overlay.remove(), 250); - }; - overlay.querySelector('#moveCancel').addEventListener('click', close); - overlay.querySelector('#moveOk').addEventListener('click', () => { - const target = overlay.querySelector('#moveTarget').value; - const dayData = getOrCreateDay(activeDate); - const entry = (dayData[momentId] || [])[entryIdx]; - if (!entry) return close(); - dayData[momentId].splice(entryIdx, 1); - if (!dayData[target]) dayData[target] = []; - dayData[target].push(entry); - saveDagboek(); - close(); - renderDagboek(); - }); - overlay.addEventListener('click', (ev) => { if (ev.target === overlay) close(); }); - } - }); - listEl.querySelectorAll('.eetmoment-add-btn').forEach(btn => { btn.addEventListener('click', (e) => { e.stopPropagation(); @@ -4777,12 +4698,95 @@ button:active, .btn:active, .meal-card-main:active, .dagboek-item:active, .food- // ===== Init ===== function init() { - setupManifest(); - loadFromStorage(); - getOrCreateDay(activeDate); - setupEventListeners(); - loadVoedingsData(); - registerServiceWorker(); + setupManifest(); + loadFromStorage(); + getOrCreateDay(activeDate); + setupEventListeners(); + loadVoedingsData(); + registerServiceWorker(); + + // === Global meal-group action delegation (setup once, not per render) === + const listEl = document.getElementById('eetmomentList'); + if (listEl && !listEl._delegationSetup) { + listEl._delegationSetup = true; + listEl.addEventListener('click', (e) => { + const btn = e.target.closest('[data-action]'); + if (!btn) return; + const action = btn.dataset.action; + const momentId = btn.dataset.moment; + const entryIdx = btn.dataset.entry != null ? parseInt(btn.dataset.entry) : null; + + if (action === 'toggle-meal') { + const group = btn.closest('.eetmoment-meal-group'); + if (!group) return; + const wrap = group.querySelector('.eetmoment-meal-items-wrap'); + const actions = group.querySelector('.eetmoment-meal-actions'); + if (wrap) { + wrap.classList.toggle('hidden'); + if (actions) actions.classList.toggle('hidden'); + const arrow = btn.querySelector('span:last-child i'); + if (arrow) { + arrow.className = wrap.classList.contains('hidden') ? 'fas fa-chevron-down' : 'fas fa-chevron-up'; + } + } + } + else if (action === 'edit-porties') { + showBulkPortieEdit(activeDate, momentId, entryIdx); + } + else if (action === 'delete-meal') { + const dayData = getOrCreateDay(activeDate); + const entry = (dayData[momentId] || [])[entryIdx]; + const mealName = (entry && entry.mealNaam) || 'maaltijd'; + showConfirmModal('Maaltijd verwijderen', + 'Deze hele maaltijd verwijderen uit je dagboek?', + () => { + dayData[momentId].splice(entryIdx, 1); + saveDagboek(); + renderDagboek(); + }); + } + else if (action === 'move-meal') { + const overlay = document.createElement('div'); + overlay.className = 'modal-overlay'; + overlay.style.zIndex = '500'; + const options = EETMOMENTEN.map(m => + `` + ).join(''); + overlay.innerHTML = ` + + `; + document.body.appendChild(overlay); + const close = () => { + overlay.classList.add('closing'); + setTimeout(() => overlay.remove(), 250); + }; + overlay.querySelector('#moveCancel').addEventListener('click', close); + overlay.querySelector('#moveOk').addEventListener('click', () => { + const target = overlay.querySelector('#moveTarget').value; + const dayData = getOrCreateDay(activeDate); + const entry = (dayData[momentId] || [])[entryIdx]; + if (!entry) return close(); + dayData[momentId].splice(entryIdx, 1); + if (!dayData[target]) dayData[target] = []; + dayData[target].push(entry); + saveDagboek(); + close(); + renderDagboek(); + }); + overlay.addEventListener('click', (ev) => { if (ev.target === overlay) close(); }); + } + }); + } } // Start the app diff --git a/index.html b/index.html index fc3cc97..c8619e3 100644 --- a/index.html +++ b/index.html @@ -2913,85 +2913,6 @@ button:active, .btn:active, .meal-card-main:active, .dagboek-item:active, .food- }); }); - // === Unified meal-group action delegation === - listEl.addEventListener('click', (e) => { - const btn = e.target.closest('[data-action]'); - if (!btn) return; - const action = btn.dataset.action; - const momentId = btn.dataset.moment; - const entryIdx = btn.dataset.entry != null ? parseInt(btn.dataset.entry) : null; - - if (action === 'toggle-meal') { - const group = btn.closest('.eetmoment-meal-group'); - if (!group) return; - const wrap = group.querySelector('.eetmoment-meal-items-wrap'); - const actions = group.querySelector('.eetmoment-meal-actions'); - if (wrap) { - wrap.classList.toggle('hidden'); - if (actions) actions.classList.toggle('hidden'); - const arrow = btn.querySelector('span:last-child i'); - if (arrow) { - arrow.className = wrap.classList.contains('hidden') ? 'fas fa-chevron-down' : 'fas fa-chevron-up'; - } - } - } - else if (action === 'edit-porties') { - showBulkPortieEdit(activeDate, momentId, entryIdx); - } - else if (action === 'delete-meal') { - const dayData = getOrCreateDay(activeDate); - const entry = (dayData[momentId] || [])[entryIdx]; - const mealName = (entry && entry.mealNaam) || 'maaltijd'; - showConfirmModal('Maaltijd verwijderen', - 'Deze hele maaltijd verwijderen uit je dagboek?', - () => { - dayData[momentId].splice(entryIdx, 1); - saveDagboek(); - renderDagboek(); - }); - } - else if (action === 'move-meal') { - const overlay = document.createElement('div'); - overlay.className = 'modal-overlay'; - overlay.style.zIndex = '500'; - const options = EETMOMENTEN.map(m => - `` - ).join(''); - overlay.innerHTML = ` - - `; - document.body.appendChild(overlay); - const close = () => { - overlay.classList.add('closing'); - setTimeout(() => overlay.remove(), 250); - }; - overlay.querySelector('#moveCancel').addEventListener('click', close); - overlay.querySelector('#moveOk').addEventListener('click', () => { - const target = overlay.querySelector('#moveTarget').value; - const dayData = getOrCreateDay(activeDate); - const entry = (dayData[momentId] || [])[entryIdx]; - if (!entry) return close(); - dayData[momentId].splice(entryIdx, 1); - if (!dayData[target]) dayData[target] = []; - dayData[target].push(entry); - saveDagboek(); - close(); - renderDagboek(); - }); - overlay.addEventListener('click', (ev) => { if (ev.target === overlay) close(); }); - } - }); - listEl.querySelectorAll('.eetmoment-add-btn').forEach(btn => { btn.addEventListener('click', (e) => { e.stopPropagation(); @@ -4777,12 +4698,95 @@ button:active, .btn:active, .meal-card-main:active, .dagboek-item:active, .food- // ===== Init ===== function init() { - setupManifest(); - loadFromStorage(); - getOrCreateDay(activeDate); - setupEventListeners(); - loadVoedingsData(); - registerServiceWorker(); + setupManifest(); + loadFromStorage(); + getOrCreateDay(activeDate); + setupEventListeners(); + loadVoedingsData(); + registerServiceWorker(); + + // === Global meal-group action delegation (setup once, not per render) === + const listEl = document.getElementById('eetmomentList'); + if (listEl && !listEl._delegationSetup) { + listEl._delegationSetup = true; + listEl.addEventListener('click', (e) => { + const btn = e.target.closest('[data-action]'); + if (!btn) return; + const action = btn.dataset.action; + const momentId = btn.dataset.moment; + const entryIdx = btn.dataset.entry != null ? parseInt(btn.dataset.entry) : null; + + if (action === 'toggle-meal') { + const group = btn.closest('.eetmoment-meal-group'); + if (!group) return; + const wrap = group.querySelector('.eetmoment-meal-items-wrap'); + const actions = group.querySelector('.eetmoment-meal-actions'); + if (wrap) { + wrap.classList.toggle('hidden'); + if (actions) actions.classList.toggle('hidden'); + const arrow = btn.querySelector('span:last-child i'); + if (arrow) { + arrow.className = wrap.classList.contains('hidden') ? 'fas fa-chevron-down' : 'fas fa-chevron-up'; + } + } + } + else if (action === 'edit-porties') { + showBulkPortieEdit(activeDate, momentId, entryIdx); + } + else if (action === 'delete-meal') { + const dayData = getOrCreateDay(activeDate); + const entry = (dayData[momentId] || [])[entryIdx]; + const mealName = (entry && entry.mealNaam) || 'maaltijd'; + showConfirmModal('Maaltijd verwijderen', + 'Deze hele maaltijd verwijderen uit je dagboek?', + () => { + dayData[momentId].splice(entryIdx, 1); + saveDagboek(); + renderDagboek(); + }); + } + else if (action === 'move-meal') { + const overlay = document.createElement('div'); + overlay.className = 'modal-overlay'; + overlay.style.zIndex = '500'; + const options = EETMOMENTEN.map(m => + `` + ).join(''); + overlay.innerHTML = ` + + `; + document.body.appendChild(overlay); + const close = () => { + overlay.classList.add('closing'); + setTimeout(() => overlay.remove(), 250); + }; + overlay.querySelector('#moveCancel').addEventListener('click', close); + overlay.querySelector('#moveOk').addEventListener('click', () => { + const target = overlay.querySelector('#moveTarget').value; + const dayData = getOrCreateDay(activeDate); + const entry = (dayData[momentId] || [])[entryIdx]; + if (!entry) return close(); + dayData[momentId].splice(entryIdx, 1); + if (!dayData[target]) dayData[target] = []; + dayData[target].push(entry); + saveDagboek(); + close(); + renderDagboek(); + }); + overlay.addEventListener('click', (ev) => { if (ev.target === overlay) close(); }); + } + }); + } } // Start the app diff --git a/tests/e2e.spec.js b/tests/e2e.spec.js index 426cef5..847634c 100644 --- a/tests/e2e.spec.js +++ b/tests/e2e.spec.js @@ -358,18 +358,15 @@ test.describe('Karby Eetdagboek', () => { test('confirm modal: deletion dialog shows design-conforme buttons', async ({ page }) => { await page.goto(BASE); - // Go to maaltijden tab - await page.locator('#navMaaltijden').click(); - await page.waitForTimeout(600); - // Quick-save a meal first if none exist (via search) + // Add item + save as meal from search tab await page.locator('.search-input').fill('brood'); - await page.waitForTimeout(500); + await page.waitForTimeout(800); await page.locator('.food-item').first().click(); await page.waitForTimeout(500); await page.locator('#detailAddBtn').click(); await page.waitForTimeout(500); await page.locator('#addMealSubmit').click(); - await page.waitForTimeout(600); + await page.waitForTimeout(800); // Navigate to diary, save as meal await page.locator('#navDagboek').click(); await page.waitForTimeout(600); @@ -380,18 +377,16 @@ test.describe('Karby Eetdagboek', () => { await page.locator('#mealNameInput').fill('Test'); await page.locator('#mealNameSave').click(); await page.waitForTimeout(600); - // Now go to maaltijden and delete + // Go to maaltijden and delete await page.locator('#navMaaltijden').click(); await page.waitForTimeout(600); // Open first meal edit await page.locator('.meal-card-main').first().click(); await page.waitForTimeout(500); - // Click delete const deleteBtn = page.locator('#mealEditDelete'); if (await deleteBtn.isVisible().catch(() => false)) { await deleteBtn.click(); await page.waitForTimeout(300); - // Confirm modal should show await expect(page.locator('#confirmOk')).toBeVisible({ timeout: 3000 }); await expect(page.locator('#confirmCancel')).toBeVisible(); await page.locator('#confirmCancel').click();