UX: Save-meal checkbox-selectie — kies welke items in maaltijd gaan

- Checkboxes per item (2+ items) met counter in knoptekst
- Save-meal knop terug naar full-width footer stijl (.save-meal-footer-btn)
- suggestMealName vult nu meteen slimme naam in
- Worker hernoemde .save-meal-btn → .save-meal-footer-btn
- Tests geüpdatet: pre-filled name ipv empty, clear-input voor valideertest
- Coder profiel verwijderd (overbodig naast frontend/backend-eng)

39/39 tests groen
This commit is contained in:
cas 2026-07-24 21:00:50 +02:00
parent 45a32ca964
commit 0bceb721ca
3 changed files with 211 additions and 61 deletions

View file

@ -1246,28 +1246,62 @@ body {
opacity: 0.6; opacity: 0.6;
} }
/* ===== Save Meal Icon in Header ===== */ /* ===== Save Meal — checkbox + footer ===== */
.save-meal-btn { .item-checkbox-wrapper {
display: inline-flex; display: none;
align-items: center; align-items: center;
justify-content: center; flex-shrink: 0;
width: 24px; }
height: 24px; .eetmoment-card.has-multi-items .item-checkbox-wrapper {
font-size: 14px; display: inline-flex;
color: var(--primary-dark); }
.item-checkbox {
flex-shrink: 0;
width: 18px;
height: 18px;
accent-color: var(--primary);
cursor: pointer; cursor: pointer;
border-radius: 50%; margin: 0 6px 0 0;
transition: background 0.15s, transform 0.15s;
user-select: none;
-webkit-user-select: none;
} }
.save-meal-btn:hover { /* Meal-group checkbox: on the header line */
.eetmoment-meal-group .item-checkbox {
margin: 0 6px 0 -2px;
}
.save-meal-footer {
padding: 4px 14px 10px;
}
.save-meal-footer-btn {
display: block;
width: 100%;
padding: 10px 16px;
background: var(--primary-light); background: var(--primary-light);
transform: scale(1.1); border: 1px solid var(--primary);
border-radius: var(--radius-sm);
color: var(--primary-dark);
font-size: var(--font-sm);
font-weight: 600;
cursor: pointer;
transition: background 0.15s, opacity 0.15s;
text-align: center;
font-family: var(--body-font);
min-height: var(--touch-min);
line-height: 1.3;
} }
.save-meal-btn:active { .save-meal-footer-btn:hover {
background: var(--accent); background: var(--primary);
transform: scale(0.95); color: var(--white);
}
.save-meal-footer-btn:active {
background: var(--primary-dark);
color: var(--white);
}
.save-meal-footer-btn:disabled {
opacity: 0.4;
cursor: default;
background: var(--primary-lighter);
color: var(--primary-dark);
border-color: var(--border);
} }
/* ===== Diary — nested meal groups ===== */ /* ===== Diary — nested meal groups ===== */
@ -3322,6 +3356,7 @@ button:active, .btn:active, .meal-card-main:active, .dagboek-item:active, .food-
return ` return `
<li class="eetmoment-meal-group"> <li class="eetmoment-meal-group">
<div class="eetmoment-meal-header" data-action="toggle-meal" data-entry="${i}"> <div class="eetmoment-meal-header" data-action="toggle-meal" data-entry="${i}">
<span class="item-checkbox-wrapper"><input type="checkbox" class="item-checkbox" data-index="${i}" checked></span>
<span class="eetmoment-meal-name">🍽 ${escapeHtml(entry.mealNaam)}</span> <span class="eetmoment-meal-name">🍽 ${escapeHtml(entry.mealNaam)}</span>
<span class="eetmoment-meal-kh">${formatKh(mealKh)} g</span> <span class="eetmoment-meal-kh">${formatKh(mealKh)} g</span>
<span style="font-size:0.7rem;color:var(--text-muted);"><i class="fas fa-chevron-up"></i></span> <span style="font-size:0.7rem;color:var(--text-muted);"><i class="fas fa-chevron-up"></i></span>
@ -3349,6 +3384,9 @@ button:active, .btn:active, .meal-card-main:active, .dagboek-item:active, .food-
const itemOverLimit = overLimit && itemKh > 0; const itemOverLimit = overLimit && itemKh > 0;
return ` return `
<li class="eetmoment-item${itemOverLimit ? ' over-limit' : ''}"> <li class="eetmoment-item${itemOverLimit ? ' over-limit' : ''}">
<span class="item-checkbox-wrapper">
<input type="checkbox" class="item-checkbox" data-index="${i}" checked>
</span>
<div class="eetmoment-item-info"> <div class="eetmoment-item-info">
<div class="eetmoment-item-name">${escapeHtml(displayNaam(entry.item.naam))}</div> <div class="eetmoment-item-name">${escapeHtml(displayNaam(entry.item.naam))}</div>
<div class="eetmoment-item-portion"> <div class="eetmoment-item-portion">
@ -3372,21 +3410,26 @@ button:active, .btn:active, .meal-card-main:active, .dagboek-item:active, .food-
const inlineGlucoseHtml = glucoseMoment ? renderInlineGlucoseItem(glucoseMoment) : ''; const inlineGlucoseHtml = glucoseMoment ? renderInlineGlucoseItem(glucoseMoment) : '';
return ` return `
<li class="eetmoment-card${cardOverLimit ? ' over-limit' : ''}" style="--i:${mi}"> <li class="eetmoment-card ${cardOverLimit ? 'over-limit' : ''}${items.length > 1 ? ' has-multi-items' : ''}" style="--i:${mi}">
<div class="eetmoment-header" data-moment="${moment.id}"> <div class="eetmoment-header" data-moment="${moment.id}">
<div class="eetmoment-header-left"> <div class="eetmoment-header-left">
<span class="eetmoment-icon">${moment.icon}</span> <span class="eetmoment-icon">${moment.icon}</span>
<span class="eetmoment-name">${moment.name}</span> <span class="eetmoment-name">${moment.name}</span>
</div> </div>
<span class="eetmoment-kh">${roundedKh > 0 ? formatKh(roundedKh) + ' g' : '—'}</span> <span class="eetmoment-kh">${roundedKh > 0 ? formatKh(roundedKh) + ' g' : '—'}</span>
${items.length > 0 ? '<span class=\"save-meal-btn\" data-moment=\"' + moment.id + '\"><i class=\"fas fa-star\"></i></span>' : ''} <span style="font-size:0.8rem;color:var(--text-muted)">${items.length > 0 ? '<i class="fas fa-chevron-down"></i>' : ''}</span>
<span style="font-size:0.8rem;color:var(--text-muted)">${items.length > 0 ? '<i class=\"fas fa-chevron-down\"></i>' : ''}</span>
</div> </div>
${items.length > 0 ? `<ul class="eetmoment-items">${itemsHtml}</ul>` : ''} ${items.length > 0 ? `<ul class="eetmoment-items">${itemsHtml}</ul>` : ''}
${inlineGlucoseHtml} ${inlineGlucoseHtml}
<div class="eetmoment-add-btn" data-moment="${moment.id}"> <div class="eetmoment-add-btn" data-moment="${moment.id}">
<span>+</span> Voedingsmiddel toevoegen <span>+</span> Voedingsmiddel toevoegen
</div> </div>
${items.length > 0 ? `
<div class="save-meal-footer" data-moment="${moment.id}">
<button class="save-meal-footer-btn" data-moment="${moment.id}">
<i class="fas fa-star"></i> Opslaan ${items.length > 1 ? items.length + ' geselecteerde items' : '1 item'} als maaltijd
</button>
</div>` : ''}
</li> </li>
`; `;
}).join(''); }).join('');
@ -3443,18 +3486,43 @@ button:active, .btn:active, .meal-card-main:active, .dagboek-item:active, .food-
}); });
}); });
// Save meal buttons // Save meal footer button
listEl.querySelectorAll('.save-meal-btn').forEach(btn => { listEl.querySelectorAll('.save-meal-footer-btn').forEach(btn => {
btn.addEventListener('click', (e) => { btn.addEventListener('click', (e) => {
e.stopPropagation(); e.stopPropagation();
saveCurrentMomentAsMeal(btn.dataset.moment); const momentId = btn.dataset.moment;
const card = btn.closest('.eetmoment-card');
// Collect selected indices from checked checkboxes
const checked = card.querySelectorAll('.item-checkbox:checked');
const selectedIndices = Array.from(checked).map(cb => parseInt(cb.dataset.index));
saveCurrentMomentAsMeal(momentId, selectedIndices);
});
});
// Checkbox change → update save button text
listEl.querySelectorAll('.item-checkbox').forEach(cb => {
cb.addEventListener('change', () => {
const card = cb.closest('.eetmoment-card');
if (!card) return;
const footer = card.querySelector('.save-meal-footer');
if (!footer) return;
const btn = footer.querySelector('.save-meal-footer-btn');
const checked = card.querySelectorAll('.item-checkbox:checked').length;
const total = card.querySelectorAll('.item-checkbox').length;
if (checked === 0) {
btn.disabled = true;
btn.textContent = '⭐ 0 geselecteerde items als maaltijd';
} else {
btn.disabled = false;
btn.textContent = `⭐ Opslaan ${checked} geselecteerde ${checked === 1 ? 'item' : 'items'} als maaltijd`;
}
}); });
}); });
// Meal group toggle expand/collapse // Meal group toggle expand/collapse
listEl.querySelectorAll('.meal-group-header').forEach(header => { listEl.querySelectorAll('.meal-group-header').forEach(header => {
header.addEventListener('click', (e) => { header.addEventListener('click', (e) => {
if (e.target.closest('.meal-group-edit, .eetmoment-remove')) return; if (e.target.closest('.meal-group-edit, .eetmoment-remove, .item-checkbox')) return;
header.classList.toggle('expanded'); header.classList.toggle('expanded');
}); });
}); });
@ -4286,13 +4354,19 @@ button:active, .btn:active, .meal-card-main:active, .dagboek-item:active, .food-
return r; return r;
} }
function saveCurrentMomentAsMeal(momentId) { function saveCurrentMomentAsMeal(momentId, selectedIndices) {
const dayData = getOrCreateDay(activeDate); const dayData = getOrCreateDay(activeDate);
const rawItems = dayData[momentId] || []; const rawItems = dayData[momentId] || [];
if (rawItems.length === 0) return; if (rawItems.length === 0) return;
// Filter to only selected items (by index in rawItems)
const filteredRaw = selectedIndices
? rawItems.filter((_, i) => selectedIndices.includes(i))
: rawItems;
if (filteredRaw.length === 0) return;
// Flatten: unwrap meal-group entries into uniform {item, portie} items // Flatten: unwrap meal-group entries into uniform {item, portie} items
const items = flattenEntries(rawItems); const items = flattenEntries(filteredRaw);
// Calculate total kh // Calculate total kh
let khTotaal = 0; let khTotaal = 0;
@ -4325,7 +4399,7 @@ button:active, .btn:active, .meal-card-main:active, .dagboek-item:active, .food-
</div> </div>
<div class="modal-body"> <div class="modal-body">
<label class="meal-name-label">Maaltijdnaam</label> <label class="meal-name-label">Maaltijdnaam</label>
<input type="text" class="meal-name-input" id="mealNameInput" value="" placeholder="Geef een naam aan deze maaltijd..." autofocus> <input type="text" class="meal-name-input" id="mealNameInput" value="${escapeHtml(defaultName)}" placeholder="Geef een naam aan deze maaltijd..." autofocus>
<div class="meal-name-items-preview">${previewText}</div> <div class="meal-name-items-preview">${previewText}</div>
<div class="meal-name-actions"> <div class="meal-name-actions">
<button class="meal-name-cancel" id="mealNameCancel">Annuleren</button> <button class="meal-name-cancel" id="mealNameCancel">Annuleren</button>

View file

@ -1246,28 +1246,62 @@ body {
opacity: 0.6; opacity: 0.6;
} }
/* ===== Save Meal Icon in Header ===== */ /* ===== Save Meal — checkbox + footer ===== */
.save-meal-btn { .item-checkbox-wrapper {
display: inline-flex; display: none;
align-items: center; align-items: center;
justify-content: center; flex-shrink: 0;
width: 24px; }
height: 24px; .eetmoment-card.has-multi-items .item-checkbox-wrapper {
font-size: 14px; display: inline-flex;
color: var(--primary-dark); }
.item-checkbox {
flex-shrink: 0;
width: 18px;
height: 18px;
accent-color: var(--primary);
cursor: pointer; cursor: pointer;
border-radius: 50%; margin: 0 6px 0 0;
transition: background 0.15s, transform 0.15s;
user-select: none;
-webkit-user-select: none;
} }
.save-meal-btn:hover { /* Meal-group checkbox: on the header line */
.eetmoment-meal-group .item-checkbox {
margin: 0 6px 0 -2px;
}
.save-meal-footer {
padding: 4px 14px 10px;
}
.save-meal-footer-btn {
display: block;
width: 100%;
padding: 10px 16px;
background: var(--primary-light); background: var(--primary-light);
transform: scale(1.1); border: 1px solid var(--primary);
border-radius: var(--radius-sm);
color: var(--primary-dark);
font-size: var(--font-sm);
font-weight: 600;
cursor: pointer;
transition: background 0.15s, opacity 0.15s;
text-align: center;
font-family: var(--body-font);
min-height: var(--touch-min);
line-height: 1.3;
} }
.save-meal-btn:active { .save-meal-footer-btn:hover {
background: var(--accent); background: var(--primary);
transform: scale(0.95); color: var(--white);
}
.save-meal-footer-btn:active {
background: var(--primary-dark);
color: var(--white);
}
.save-meal-footer-btn:disabled {
opacity: 0.4;
cursor: default;
background: var(--primary-lighter);
color: var(--primary-dark);
border-color: var(--border);
} }
/* ===== Diary — nested meal groups ===== */ /* ===== Diary — nested meal groups ===== */
@ -3322,6 +3356,7 @@ button:active, .btn:active, .meal-card-main:active, .dagboek-item:active, .food-
return ` return `
<li class="eetmoment-meal-group"> <li class="eetmoment-meal-group">
<div class="eetmoment-meal-header" data-action="toggle-meal" data-entry="${i}"> <div class="eetmoment-meal-header" data-action="toggle-meal" data-entry="${i}">
<span class="item-checkbox-wrapper"><input type="checkbox" class="item-checkbox" data-index="${i}" checked></span>
<span class="eetmoment-meal-name">🍽 ${escapeHtml(entry.mealNaam)}</span> <span class="eetmoment-meal-name">🍽 ${escapeHtml(entry.mealNaam)}</span>
<span class="eetmoment-meal-kh">${formatKh(mealKh)} g</span> <span class="eetmoment-meal-kh">${formatKh(mealKh)} g</span>
<span style="font-size:0.7rem;color:var(--text-muted);"><i class="fas fa-chevron-up"></i></span> <span style="font-size:0.7rem;color:var(--text-muted);"><i class="fas fa-chevron-up"></i></span>
@ -3349,6 +3384,9 @@ button:active, .btn:active, .meal-card-main:active, .dagboek-item:active, .food-
const itemOverLimit = overLimit && itemKh > 0; const itemOverLimit = overLimit && itemKh > 0;
return ` return `
<li class="eetmoment-item${itemOverLimit ? ' over-limit' : ''}"> <li class="eetmoment-item${itemOverLimit ? ' over-limit' : ''}">
<span class="item-checkbox-wrapper">
<input type="checkbox" class="item-checkbox" data-index="${i}" checked>
</span>
<div class="eetmoment-item-info"> <div class="eetmoment-item-info">
<div class="eetmoment-item-name">${escapeHtml(displayNaam(entry.item.naam))}</div> <div class="eetmoment-item-name">${escapeHtml(displayNaam(entry.item.naam))}</div>
<div class="eetmoment-item-portion"> <div class="eetmoment-item-portion">
@ -3372,21 +3410,26 @@ button:active, .btn:active, .meal-card-main:active, .dagboek-item:active, .food-
const inlineGlucoseHtml = glucoseMoment ? renderInlineGlucoseItem(glucoseMoment) : ''; const inlineGlucoseHtml = glucoseMoment ? renderInlineGlucoseItem(glucoseMoment) : '';
return ` return `
<li class="eetmoment-card${cardOverLimit ? ' over-limit' : ''}" style="--i:${mi}"> <li class="eetmoment-card ${cardOverLimit ? 'over-limit' : ''}${items.length > 1 ? ' has-multi-items' : ''}" style="--i:${mi}">
<div class="eetmoment-header" data-moment="${moment.id}"> <div class="eetmoment-header" data-moment="${moment.id}">
<div class="eetmoment-header-left"> <div class="eetmoment-header-left">
<span class="eetmoment-icon">${moment.icon}</span> <span class="eetmoment-icon">${moment.icon}</span>
<span class="eetmoment-name">${moment.name}</span> <span class="eetmoment-name">${moment.name}</span>
</div> </div>
<span class="eetmoment-kh">${roundedKh > 0 ? formatKh(roundedKh) + ' g' : '—'}</span> <span class="eetmoment-kh">${roundedKh > 0 ? formatKh(roundedKh) + ' g' : '—'}</span>
${items.length > 0 ? '<span class=\"save-meal-btn\" data-moment=\"' + moment.id + '\"><i class=\"fas fa-star\"></i></span>' : ''} <span style="font-size:0.8rem;color:var(--text-muted)">${items.length > 0 ? '<i class="fas fa-chevron-down"></i>' : ''}</span>
<span style="font-size:0.8rem;color:var(--text-muted)">${items.length > 0 ? '<i class=\"fas fa-chevron-down\"></i>' : ''}</span>
</div> </div>
${items.length > 0 ? `<ul class="eetmoment-items">${itemsHtml}</ul>` : ''} ${items.length > 0 ? `<ul class="eetmoment-items">${itemsHtml}</ul>` : ''}
${inlineGlucoseHtml} ${inlineGlucoseHtml}
<div class="eetmoment-add-btn" data-moment="${moment.id}"> <div class="eetmoment-add-btn" data-moment="${moment.id}">
<span>+</span> Voedingsmiddel toevoegen <span>+</span> Voedingsmiddel toevoegen
</div> </div>
${items.length > 0 ? `
<div class="save-meal-footer" data-moment="${moment.id}">
<button class="save-meal-footer-btn" data-moment="${moment.id}">
<i class="fas fa-star"></i> Opslaan ${items.length > 1 ? items.length + ' geselecteerde items' : '1 item'} als maaltijd
</button>
</div>` : ''}
</li> </li>
`; `;
}).join(''); }).join('');
@ -3443,18 +3486,43 @@ button:active, .btn:active, .meal-card-main:active, .dagboek-item:active, .food-
}); });
}); });
// Save meal buttons // Save meal footer button
listEl.querySelectorAll('.save-meal-btn').forEach(btn => { listEl.querySelectorAll('.save-meal-footer-btn').forEach(btn => {
btn.addEventListener('click', (e) => { btn.addEventListener('click', (e) => {
e.stopPropagation(); e.stopPropagation();
saveCurrentMomentAsMeal(btn.dataset.moment); const momentId = btn.dataset.moment;
const card = btn.closest('.eetmoment-card');
// Collect selected indices from checked checkboxes
const checked = card.querySelectorAll('.item-checkbox:checked');
const selectedIndices = Array.from(checked).map(cb => parseInt(cb.dataset.index));
saveCurrentMomentAsMeal(momentId, selectedIndices);
});
});
// Checkbox change → update save button text
listEl.querySelectorAll('.item-checkbox').forEach(cb => {
cb.addEventListener('change', () => {
const card = cb.closest('.eetmoment-card');
if (!card) return;
const footer = card.querySelector('.save-meal-footer');
if (!footer) return;
const btn = footer.querySelector('.save-meal-footer-btn');
const checked = card.querySelectorAll('.item-checkbox:checked').length;
const total = card.querySelectorAll('.item-checkbox').length;
if (checked === 0) {
btn.disabled = true;
btn.textContent = '⭐ 0 geselecteerde items als maaltijd';
} else {
btn.disabled = false;
btn.textContent = `⭐ Opslaan ${checked} geselecteerde ${checked === 1 ? 'item' : 'items'} als maaltijd`;
}
}); });
}); });
// Meal group toggle expand/collapse // Meal group toggle expand/collapse
listEl.querySelectorAll('.meal-group-header').forEach(header => { listEl.querySelectorAll('.meal-group-header').forEach(header => {
header.addEventListener('click', (e) => { header.addEventListener('click', (e) => {
if (e.target.closest('.meal-group-edit, .eetmoment-remove')) return; if (e.target.closest('.meal-group-edit, .eetmoment-remove, .item-checkbox')) return;
header.classList.toggle('expanded'); header.classList.toggle('expanded');
}); });
}); });
@ -4286,13 +4354,19 @@ button:active, .btn:active, .meal-card-main:active, .dagboek-item:active, .food-
return r; return r;
} }
function saveCurrentMomentAsMeal(momentId) { function saveCurrentMomentAsMeal(momentId, selectedIndices) {
const dayData = getOrCreateDay(activeDate); const dayData = getOrCreateDay(activeDate);
const rawItems = dayData[momentId] || []; const rawItems = dayData[momentId] || [];
if (rawItems.length === 0) return; if (rawItems.length === 0) return;
// Filter to only selected items (by index in rawItems)
const filteredRaw = selectedIndices
? rawItems.filter((_, i) => selectedIndices.includes(i))
: rawItems;
if (filteredRaw.length === 0) return;
// Flatten: unwrap meal-group entries into uniform {item, portie} items // Flatten: unwrap meal-group entries into uniform {item, portie} items
const items = flattenEntries(rawItems); const items = flattenEntries(filteredRaw);
// Calculate total kh // Calculate total kh
let khTotaal = 0; let khTotaal = 0;
@ -4325,7 +4399,7 @@ button:active, .btn:active, .meal-card-main:active, .dagboek-item:active, .food-
</div> </div>
<div class="modal-body"> <div class="modal-body">
<label class="meal-name-label">Maaltijdnaam</label> <label class="meal-name-label">Maaltijdnaam</label>
<input type="text" class="meal-name-input" id="mealNameInput" value="" placeholder="Geef een naam aan deze maaltijd..." autofocus> <input type="text" class="meal-name-input" id="mealNameInput" value="${escapeHtml(defaultName)}" placeholder="Geef een naam aan deze maaltijd..." autofocus>
<div class="meal-name-items-preview">${previewText}</div> <div class="meal-name-items-preview">${previewText}</div>
<div class="meal-name-actions"> <div class="meal-name-actions">
<button class="meal-name-cancel" id="mealNameCancel">Annuleren</button> <button class="meal-name-cancel" id="mealNameCancel">Annuleren</button>

View file

@ -192,7 +192,7 @@ test.describe('Karby Eetdagboek', () => {
// ---------- Maaltijden ---------- // ---------- Maaltijden ----------
test('meal save: name input is empty by default', async ({ page }) => { test('meal save: name input pre-filled with smart suggestion', async ({ page }) => {
await page.goto(BASE); await page.goto(BASE);
await page.locator('.search-input').fill('brood'); await page.locator('.search-input').fill('brood');
await page.waitForTimeout(800); await page.waitForTimeout(800);
@ -203,13 +203,14 @@ test.describe('Karby Eetdagboek', () => {
// Item toevoegen via addMealSubmit → sluit modal + gaat naar dagboek // Item toevoegen via addMealSubmit → sluit modal + gaat naar dagboek
await page.locator('#addMealSubmit').click(); await page.locator('#addMealSubmit').click();
await page.waitForTimeout(800); await page.waitForTimeout(800);
const saveBtn = page.locator('.save-meal-btn'); const saveBtn = page.locator('.save-meal-footer-btn');
await expect(saveBtn.first()).toBeVisible({ timeout: 5000 }); await expect(saveBtn.first()).toBeVisible({ timeout: 5000 });
await saveBtn.first().click(); await saveBtn.first().click();
await page.waitForTimeout(500); await page.waitForTimeout(500);
const nameInput = page.locator('#mealNameInput'); const nameInput = page.locator('#mealNameInput');
await expect(nameInput).toBeVisible({ timeout: 5000 }); await expect(nameInput).toBeVisible({ timeout: 5000 });
await expect(nameInput).toHaveValue(''); // Smart suggestion should pre-fill with a non-empty name
await expect(nameInput).not.toHaveValue('');
await page.locator('#mealNameCancel').click(); await page.locator('#mealNameCancel').click();
await page.waitForTimeout(300); await page.waitForTimeout(300);
}); });
@ -224,13 +225,14 @@ test.describe('Karby Eetdagboek', () => {
await page.waitForTimeout(500); await page.waitForTimeout(500);
await page.locator('#addMealSubmit').click(); await page.locator('#addMealSubmit').click();
await page.waitForTimeout(800); await page.waitForTimeout(800);
const saveBtn = page.locator('.save-meal-btn'); const saveBtn = page.locator('.save-meal-footer-btn');
await expect(saveBtn.first()).toBeVisible({ timeout: 5000 }); await expect(saveBtn.first()).toBeVisible({ timeout: 5000 });
await saveBtn.first().click(); await saveBtn.first().click();
await page.waitForTimeout(500); await page.waitForTimeout(500);
const nameInput = page.locator('#mealNameInput'); const nameInput = page.locator('#mealNameInput');
await expect(nameInput).toBeVisible({ timeout: 5000 }); await expect(nameInput).toBeVisible({ timeout: 5000 });
await expect(nameInput).toHaveValue(''); // Clear the smart-suggested name to test empty validation
await nameInput.fill('');
// Opslaan zonder naam → validatiefout // Opslaan zonder naam → validatiefout
await page.locator('#mealNameSave').click(); await page.locator('#mealNameSave').click();
await page.waitForTimeout(300); await page.waitForTimeout(300);
@ -301,7 +303,7 @@ test.describe('Karby Eetdagboek', () => {
await page.locator('#addMealSubmit').click(); await page.locator('#addMealSubmit').click();
await page.waitForTimeout(800); await page.waitForTimeout(800);
// Stap 2: Sla op als maaltijd // Stap 2: Sla op als maaltijd
const saveBtn = page.locator('.save-meal-btn'); const saveBtn = page.locator('.save-meal-footer-btn');
await expect(saveBtn.first()).toBeVisible({ timeout: 5000 }); await expect(saveBtn.first()).toBeVisible({ timeout: 5000 });
await saveBtn.first().click(); await saveBtn.first().click();
await page.waitForTimeout(500); await page.waitForTimeout(500);
@ -370,7 +372,7 @@ test.describe('Karby Eetdagboek', () => {
// Navigate to diary, save as meal // Navigate to diary, save as meal
await page.locator('#navDagboek').click(); await page.locator('#navDagboek').click();
await page.waitForTimeout(600); await page.waitForTimeout(600);
const saveBtn = page.locator('.save-meal-btn'); const saveBtn = page.locator('.save-meal-footer-btn');
if (await saveBtn.count() === 0) { test.skip(); return; } if (await saveBtn.count() === 0) { test.skip(); return; }
await saveBtn.first().click(); await saveBtn.first().click();
await page.waitForTimeout(500); await page.waitForTimeout(500);