${formatKh(kh)}
@@ -3352,28 +3484,71 @@ button:active, .btn:active, .meal-card-main:active, .dagboek-item:active, .food-
if (el._clickAttached) return;
el._clickAttached = true;
el.addEventListener('click', () => {
- const idx = parseInt(el.dataset.index);
- if (idx >= 0 && idx < voedingsmiddelen.length) {
- showDetail(voedingsmiddelen[idx]);
+ const nummer = el.dataset.nummer;
+ if (!nummer || !voedingsmiddelenByN.has(nummer)) {
+ // If the item is not in the local lookup (e.g. pure API item),
+ // try finding by index in the food list as last resort
+ const items = listEl.querySelectorAll('.food-item');
+ const idx = Array.from(items).indexOf(el);
+ if (idx >= 0) {
+ showToast('Item niet gevonden in lokale data');
+ }
+ return;
+ }
+ const item = voedingsmiddelenByN.get(nummer);
+ // If in add-to-moment mode, go directly to add modal with preselected moment
+ if (window._targetMoment) {
+ showAddToMeal(item, window._targetMoment);
+ } else {
+ showDetail(item);
}
});
});
}
- function setupSearchObserver(allItems) {
+ function setupSearchObserver() {
disconnectSearchObserver();
const sentinel = document.getElementById('searchSentinel');
if (!sentinel) return;
- searchObserver = new IntersectionObserver((entries) => {
- if (entries[0].isIntersecting) {
- // Load up to 3 batches at once when user scrolls fast
- // This keeps the sentinel pushed far ahead of the viewport
- let batchesLoaded = 0;
- const maxBatches = 3;
- while (batchesLoaded < maxBatches && searchVisibleCount < allItems.length) {
- appendSearchBatch(allItems);
- batchesLoaded++;
+ searchObserver = new IntersectionObserver(async (entries) => {
+ if (entries[0].isIntersecting && !isSearchLoading) {
+ isSearchLoading = true;
+ try {
+ if (searchTotal > 0) {
+ // API mode: fetch next page
+ const nextPage = searchPage + 1;
+ const apiResult = await fetchFoodPage(searchQuery, selectedCategory, nextPage, SEARCH_PAGE_SIZE);
+ if (apiResult && apiResult.data.length > 0) {
+ appendSearchBatchFromApi(apiResult.data);
+ searchPage = nextPage;
+ // All items loaded — hide sentinel and disconnect
+ if (searchVisibleCount >= searchTotal) {
+ disconnectSearchObserver();
+ const sentinelEl = document.getElementById('searchSentinel');
+ if (sentinelEl) sentinelEl.classList.add('hidden');
+ }
+ } else if (!apiResult) {
+ // API failed — try fallback from local data if available
+ const fallbackItems = getFilteredItems();
+ if (searchVisibleCount < fallbackItems.length) {
+ appendSearchBatchLocal(fallbackItems);
+ }
+ } else {
+ // API returned empty — all done
+ disconnectSearchObserver();
+ const sentinelEl = document.getElementById('searchSentinel');
+ if (sentinelEl) sentinelEl.classList.add('hidden');
+ }
+ } else {
+ // Fallback mode: slice from local filtered items
+ const fallbackItems = getFilteredItems();
+ if (searchVisibleCount < fallbackItems.length) {
+ appendSearchBatchLocal(fallbackItems);
+ }
+ }
+ } finally {
+ isSearchLoading = false;
}
}
}, { rootMargin: '1200px' });
@@ -3381,7 +3556,17 @@ button:active, .btn:active, .meal-card-main:active, .dagboek-item:active, .food-
searchObserver.observe(sentinel);
}
- function appendSearchBatch(allItems) {
+ function appendSearchBatchFromApi(items) {
+ const listEl = document.getElementById('foodList');
+ const sentinelEl = document.getElementById('searchSentinel');
+ if (!listEl) return;
+
+ listEl.insertAdjacentHTML('beforeend', renderFoodItemsHtml(items, searchVisibleCount));
+ attachFoodItemHandlers(listEl);
+ searchVisibleCount += items.length;
+ }
+
+ function appendSearchBatchLocal(allItems) {
const listEl = document.getElementById('foodList');
const sentinelEl = document.getElementById('searchSentinel');
if (!listEl || !sentinelEl) return;
@@ -3415,6 +3600,8 @@ button:active, .btn:active, .meal-card-main:active, .dagboek-item:active, .food-
function resetSearchPagination() {
searchVisibleCount = SEARCH_PAGE_SIZE;
+ searchPage = 1;
+ searchTotal = 0;
disconnectSearchObserver();
}
@@ -3751,7 +3938,7 @@ button:active, .btn:active, .meal-card-main:active, .dagboek-item:active, .food-
body.innerHTML = `
${escapeHtml(displayNaam(item.naam))}
-
${escapeHtml(item.cat)}
+
${escapeHtml(displayCategory(item.cat))}
@@ -3924,30 +4111,26 @@ button:active, .btn:active, .meal-card-main:active, .dagboek-item:active, .food-
});
}
- function openAddToMealForMoment(momentId) {
- // Open a mini search-to-add flow from diary
- switchTab('zoeken');
- // Give user a hint
- const moment = EETMOMENTEN.find(m => m.id === momentId);
- showToast(`Selecteer een voedingsmiddel voor ${moment.name}`);
- // Store the target moment so next food tap adds to it
- window._targetMoment = momentId;
+ function clearAddContext() {
+ window._targetMoment = null;
+ const banner = document.getElementById('addContextBanner');
+ if (banner) banner.classList.add('hidden');
+ }
- // Use event delegation so dynamically loaded items also work
- const listEl = document.getElementById('foodList');
- const handler = (e) => {
- const itemEl = e.target.closest('.food-item');
- if (!itemEl) return;
- const idx = parseInt(itemEl.dataset.index);
- if (idx >= 0 && idx < voedingsmiddelen.length && window._targetMoment) {
- e.stopPropagation();
- e.preventDefault();
- showAddToMeal(voedingsmiddelen[idx], window._targetMoment);
- window._targetMoment = null;
- listEl.removeEventListener('click', handler);
- }
- };
- listEl.addEventListener('click', handler);
+ function openAddToMealForMoment(momentId) {
+ // Navigate to search page with eetmoment context
+ switchTab('zoeken');
+ const moment = EETMOMENTEN.find(m => m.id === momentId);
+ if (!moment) return;
+ // Store the target moment so clicking a food item adds to it
+ window._targetMoment = momentId;
+ // Show visual context banner instead of toast
+ const banner = document.getElementById('addContextBanner');
+ const textEl = document.getElementById('addContextText');
+ if (banner && textEl) {
+ textEl.innerHTML = `
Voeg toe aan: ${moment.icon} ${moment.name}`;
+ banner.classList.remove('hidden');
+ }
}
function addToMeal(item, portie, momentId) {
@@ -4220,7 +4403,7 @@ button:active, .btn:active, .meal-card-main:active, .dagboek-item:active, .food-
${escapeHtml(displayNaam(mealItem.item.naam))}
-
${escapeHtml(mealItem.item.cat || '')} · ${formatKh(itemKh)} g kh
+
${escapeHtml(displayCategory(mealItem.item.cat || ''))} · ${formatKh(itemKh)} g kh