Files

1 line
26 KiB
JSON
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{"repo": "bagoadi/athlion", "files": {"frontend/diet.js": {"d41e20b6871487b2a11594fcd13e4f1df7c213b1": {"t": 1785768936.4513454, "n": 25158, "h": "c66582238817fea9", "c": "// =================================================\n// DIET.HTML LOGIKA (VÉGLEGES JAVÍTOTT VERZIÓ)\n// =================================================\n\n// --- ÁLLAPOT (State) ---\nconst token = localStorage.getItem(\"token\");\nlet selectedDate = new Date();\n// DÁTUM JAVÍTÁS: Kezdeti érték beállítása a javított függvénnyel\nlet selectedDateKey = getLocalISODate(selectedDate); \n\nlet activeMealType = 'breakfast'; \nlet dailyLog = {}; \nlet currentWaterState = 0; \nconst mealTitles = { breakfast: 'Reggeli', lunch: 'Ebéd', dinner: 'Vacsora', snacks: 'Nasi' };\nlet currentSelectedFood = null;\n\n// --- JAVÍTOTT DÁTUM FÜGGVÉNY (Időzóna hiba ellen) ---\n// Ez biztosan a helyi (magyar) napot adja vissza, nem az UTC-t.\nfunction getLocalISODate(d) {\n const year = d.getFullYear();\n const month = String(d.getMonth() + 1).padStart(2, '0');\n const day = String(d.getDate()).padStart(2, '0');\n return `${year}-${month}-${day}`;\n}\n\n// Elemek\nconst dayLabel = document.querySelector('#dayLabel');\nconst modal = document.querySelector('#foodSearchModal');\nconst modalTitle = document.querySelector('#modalTitle');\nconst modalQueryInput = document.querySelector('#modalFoodQuery');\nconst modalResults = document.querySelector('#modalFoodResults');\nconst dailyFoodList = document.querySelector('#dailyFoodList');\nconst emptyListMsg = document.querySelector('#emptyListMsg');\n\n// Modal elemek\nconst foodPlaceholder = document.querySelector('#foodSelectionPlaceholder');\nconst foodDetails = document.querySelector('#foodSelectionDetails');\nconst selFoodName = document.querySelector('#selFoodName');\nconst selBaseMacros = document.querySelector('#selBaseMacros');\nconst selQuantity = document.querySelector('#selQuantity');\nconst btnAddSelection = document.querySelector('#btnAddSelection');\n\n// Preview elemek\nconst prevKcal = document.querySelector('#prevKcal');\nconst prevP = document.querySelector('#prevP');\nconst prevC = document.querySelector('#prevC');\nconst prevF = document.querySelector('#prevF');\n\n// --- TOAST ---\nfunction showToast(message, type = 'success') {\n const toast = document.createElement('div');\n toast.className = `custom-toast ${type}`;\n toast.textContent = message;\n document.body.appendChild(toast);\n requestAnimationFrame(() => toast.classList.add('show'));\n setTimeout(() => {\n toast.classList.remove('show');\n setTimeout(() => document.body.removeChild(toast), 300);\n }, 3000);\n}\n\n// --- CONFIRM ---\nfunction showConfirm() {\n const confirmModal = document.getElementById('confirmModal');\n const confirmYes = document.getElementById('confirmYes');\n const confirmNo = document.getElementById('confirmNo');\n\n return new Promise((resolve) => {\n confirmModal.style.display = 'grid';\n confirmYes.onclick = () => {\n confirmModal.style.display = 'none';\n resolve(true);\n };\n confirmNo.onclick = () => {\n confirmModal.style.display = 'none';\n resolve(false);\n };\n });\n}\n\n// =================================================\n// 1. NAPVÁLASZTÓ\n// =================================================\nfunction initDayNavigator() {\n const prevBtn = document.getElementById('dayPrev');\n const nextBtn = document.getElementById('dayNext');\n const todayBtn = document.getElementById('dayToday');\n\n if (!prevBtn) return;\n\n prevBtn.addEventListener('click', () => {\n selectedDate.setDate(selectedDate.getDate() - 1);\n updateDayView();\n });\n nextBtn.addEventListener('click', () => {\n selectedDate.setDate(selectedDate.getDate() + 1);\n updateDayView();\n });\n todayBtn.addEventListener('click', () => {\n selectedDate = new Date();\n updateDayView();\n });\n\n updateDayView();\n}\n\nfunction updateDayView() {\n if (!dayLabel) return;\n \n const today = new Date();\n selectedDateKey = getLocalISODate(selectedDate); // Használjuk a javított függvényt\n \n if (getLocalISODate(today) === selectedDateKey) {\n dayLabel.textContent = \"Mai nap\";\n } else {\n dayLabel.textContent = selectedDate.toLocaleDateString('hu-HU', {\n month: 'short', day: 'numeric', weekday: 'short'\n });\n }\n\n if (token) {\n fetchDailyLog(); \n fetchDailyWater(); \n } else {\n renderDailyFoodList();\n }\n}\n\n// =================================================\n// 2. SÚLY KEZELÉS\n// =================================================\nfunction initWeightControl() {\n const wInput = document.getElementById('weightInput');\n const wDec = document.getElementById('weightDec');\n const wInc = document.getElementById('weightInc');\n const wStatus = document.getElementById('weightSaveStatus');\n \n if (!wInput || !token) return;\n\n fetch(\"/api/v1/weight/latest\", {\n headers: { \"Authorization\": `Bearer ${token}` }\n })\n .then(res => res.ok ? res.json() : null)\n .then(data => {\n if (data && data.weight_kg) {\n wInput.value = data.weight_kg;\n }\n })\n .catch(console.error);\n\n const saveWeight = async (newVal) => {\n if (!newVal || newVal <= 0) return;\n wInput.style.opacity = \"0.7\";\n\n try {\n const todayISO = getLocalISODate(new Date());\n const res = await fetch(\"/api/v1/weight/\", {\n method: \"POST\",\n headers: { \n \"Content-Type\": \"application/json\",\n \"Authorization\": `Bearer ${token}` \n },\n body: JSON.stringify({\n weight_kg: parseFloat(newVal),\n date: todayISO\n })\n });\n\n if (res.ok) {\n wStatus.style.opacity = '1';\n setTimeout(() => { wStatus.style.opacity = '0'; }, 2000);\n if (typeof fetchUserGoals === 'function') await fetchUserGoals(); \n }\n } catch (err) { console.error(err); } \n finally { wInput.style.opacity = \"1\"; }\n };\n\n wDec.addEventListener('click', () => {\n let val = parseFloat(wInput.value) || 0;\n val = Math.max(0, val - 0.5);\n wInput.value = val.toFixed(1);\n saveWeight(val);\n });\n\n wInc.addEventListener('click', () => {\n let val = parseFloat(wInput.value) || 0;\n val += 0.5;\n wInput.value = val.toFixed(1);\n saveWeight(val);\n });\n\n wInput.addEventListener('change', () => saveWeight(parseFloat(wInput.value)));\n}\n\n// =================================================\n// 3. VÍZ KEZELÉS (JAVÍTOTT LEKÉRDEZÉSSEL)\n// =================================================\nfunction initWaterControl() {\n const btnDec = document.getElementById('waterDec');\n const btnInc = document.getElementById('waterInc');\n \n if (btnDec && btnInc) {\n btnDec.addEventListener('click', () => changeWater(-250));\n btnInc.addEventListener('click', () => changeWater(250));\n }\n}\n\nasync function fetchDailyWater() {\n if (!token) return;\n \n // 1. Azonnali reset (hogy ne ragadjon be a régi szám)\n currentWaterState = 0;\n updateWaterDisplay();\n \n const dateKey = getLocalISODate(selectedDate);\n \n try {\n // 2. KÉNYSZERÍTETT FRISSÍTÉS (?temp=...)\n // Minden kérés egyedi lesz, így a böngésző nem tudja cache-ből kiszolgálni.\n const url = `/api/v1/water/daily_sum?date_str=${dateKey}&temp=${Date.now()}`;\n \n const res = await fetch(url, {\n headers: { \"Authorization\": `Bearer ${token}` }\n });\n\n if (res.ok) {\n const data = await res.json();\n console.log(`Víz (${dateKey}):`, data.total_ml); // Ellenőrzés a konzolon\n \n currentWaterState = data.total_ml;\n updateWaterDisplay();\n } else {\n console.error(\"Szerver hiba:\", res.status);\n }\n } catch (err) {\n console.error(\"Hálózati hiba:\", err);\n }\n}\n\nasync function changeWater(amount) {\n if (!token) return;\n \n const originalState = currentWaterState;\n currentWaterState = Math.max(0, currentWaterState + amount);\n updateWaterDisplay();\n\n const dateKey = getLocalISODate(selectedDate); // Itt is a javított dátumot küldjük\n\n try {\n const res = await fetch(\"/api/v1/water/add\", {\n method: \"POST\",\n headers: { \n \"Content-Type\": \"application/json\",\n \"Authorization\": `Bearer ${token}` \n },\n body: JSON.stringify({\n amount_ml: amount,\n date: dateKey\n })\n });\n\n if (!res.ok) throw new Error(\"Hiba a mentéskor\");\n \n } catch (err) {\n console.error(err);\n currentWaterState = originalState;\n updateWaterDisplay();\n showToast(\"Hiba a víz mentésekor!\", \"error\");\n }\n}\n\nfunction updateWaterDisplay() {\n const disp = document.getElementById('waterValueDisplay');\n const bar = document.getElementById('waterProgressBarSmall');\n \n if (disp) disp.textContent = currentWaterState;\n \n if (bar) {\n const pct = Math.min(100, (currentWaterState / 2500) * 100);\n bar.style.width = `${pct}%`;\n }\n}\n\n// =================================================\n// 4. ÉTKEZÉS MODAL\n// =================================================\nfunction initMealTabs() {\n const tabs = document.querySelectorAll('#mealTabs .meal-tab');\n const closeBtn = document.getElementById('modalCloseBtn');\n\n if (!tabs.length || !modal || !closeBtn) return;\n\n tabs.forEach(tab => {\n tab.addEventListener('click', () => {\n activeMealType = tab.dataset.meal;\n modalTitle.textContent = tab.textContent;\n resetModalRightSide();\n modalQueryInput.value = '';\n modalResults.innerHTML = `<p class=\"muted small\" style=\"padding: 10px 0;\">Írj be legalább 3 karaktert.</p>`;\n modal.style.display = 'grid';\n modalQueryInput.focus();\n });\n });\n\n const closeModal = () => {\n modal.style.display = 'none';\n resetModalRightSide();\n };\n\n closeBtn.addEventListener('click', closeModal);\n modal.addEventListener('click', (e) => { if (e.target === modal) closeModal(); });\n\n document.addEventListener('keydown', (e) => {\n if (modal.style.display === 'none') return;\n if (e.key === 'Escape') { e.preventDefault(); closeModal(); }\n if (e.key === 'Enter') {\n if (document.activeElement === modalQueryInput) return;\n if (currentSelectedFood) { e.preventDefault(); handleAddSelectionClick(); }\n }\n });\n\n modalQueryInput.addEventListener('input', () => doFoodSearch(true));\n if (selQuantity) selQuantity.addEventListener('input', updatePreviewCalculation);\n if (btnAddSelection) btnAddSelection.addEventListener('click', handleAddSelectionClick);\n}\n\nasync function doFoodSearch(isModal = false) {\n if (!isModal) return;\n const q = (modalQueryInput.value || '').toLowerCase().trim();\n if (q.length < 3) {\n modalResults.innerHTML = `<p class=\"muted small\" style=\"padding: 10px 0;\">Írj be legalább 3 karaktert.</p>`;\n return;\n }\n modalResults.innerHTML = `<div class=\"muted small\">Keresés...</div>`;\n\n try {\n const res = await fetch(`/api/v1/foods/search?q=${encodeURIComponent(q)}`);\n if (!res.ok) throw new Error('Hiba');\n const list = await res.json();\n modalResults.innerHTML = '';\n if (!list.length) { modalResults.innerHTML = `<div class=\"muted small\">Nincs találat.</div>`; return; }\n\n list.forEach(item => {\n const div = document.createElement('div');\n div.className = 'result';\n div.innerHTML = `<div style=\"font-weight:600; color:#fff; font-size:14px; padding-block: 2px;\">${item.food_name}</div>`;\n div.addEventListener('click', () => {\n modalResults.querySelectorAll('.result').forEach(r => r.classList.remove('active'));\n div.classList.add('active');\n selectFoodInModal(item);\n });\n modalResults.appendChild(div);\n });\n } catch (err) {\n console.error(err);\n modalResults.innerHTML = `<div style=\"color:var(--err)\">Hiba történt.</div>`;\n }\n}\n\nasync function selectFoodInModal(item) {\n currentSelectedFood = item;\n if(foodPlaceholder) foodPlaceholder.style.display = 'none';\n if(foodDetails) { foodDetails.style.display = 'flex'; foodDetails.style.flexDirection = 'column'; }\n if(selFoodName) selFoodName.textContent = item.food_name;\n if(selBaseMacros) {\n const r = (val) => Math.round(val || 0);\n selBaseMacros.textContent = `${r(item.kcal_100g)}kcal | Feh:${r(item.protein_100g)} SzH:${r(item.carbs_100g)} Zs:${r(item.fat_100g)}`;\n }\n if(selQuantity) selQuantity.value = 100;\n updatePreviewCalculation();\n\n let warningBox = document.getElementById('foodWarningBox');\n if (!warningBox) {\n warningBox = document.createElement('div');\n warningBox.id = 'foodWarningBox';\n warningBox.style.padding = '10px'; warningBox.style.marginTop = '15px'; warningBox.style.borderRadius = '8px'; warningBox.style.fontSize = '13px'; warningBox.style.display = 'none';\n if (btnAddSelection) btnAddSelection.parentNode.insertBefore(warningBox, btnAddSelection);\n }\n warningBox.style.display = 'none'; warningBox.innerHTML = '';\n\n if (token) {\n try {\n const res = await fetch(`/api/v1/foods/${item.food_id}/check`, { headers: { \"Authorization\": `Bearer ${token}` } });\n if (res.ok) {\n const checkData = await res.json();\n if (checkData.warnings && checkData.warnings.length > 0) {\n warningBox.style.display = 'block';\n const isDanger = !checkData.is_safe;\n warningBox.style.backgroundColor = isDanger ? 'rgba(255, 0, 0, 0.2)' : 'rgba(255, 200, 0, 0.2)';\n warningBox.style.border = isDanger ? '1px solid var(--err)' : '1px solid #ffcc00';\n warningBox.style.color = isDanger ? '#ffcccc' : '#ffdd99';\n warningBox.innerHTML = checkData.warnings.map(w => `<div>${isDanger ? '⚠️' : 'ℹ️'} <strong>${w}</strong></div>`).join('');\n }\n }\n } catch (err) { console.error(err); }\n }\n}\n\nfunction updatePreviewCalculation() {\n if (!currentSelectedFood || !prevKcal) return;\n const qty = parseFloat(selQuantity.value);\n if (isNaN(qty) || qty < 0) return;\n const ratio = qty / 100;\n prevKcal.textContent = Math.round(currentSelectedFood.kcal_100g * ratio);\n prevP.textContent = Math.round(currentSelectedFood.protein_100g * ratio);\n prevC.textContent = Math.round(currentSelectedFood.carbs_100g * ratio);\n prevF.textContent = Math.round(currentSelectedFood.fat_100g * ratio);\n}\n\nfunction resetModalRightSide() {\n currentSelectedFood = null;\n if(foodPlaceholder) foodPlaceholder.style.display = 'block';\n if(foodDetails) foodDetails.style.display = 'none';\n const activeItems = modalResults ? modalResults.querySelectorAll('.active') : [];\n activeItems.forEach(el => el.classList.remove('active'));\n}\n\nasync function handleAddSelectionClick() {\n if (!currentSelectedFood) return;\n if (!token) return alert(\"Jelentkezz be!\");\n\n const qty = parseFloat(selQuantity.value);\n if (!qty || qty <= 0) return alert(\"Érvényes mennyiséget adj meg!\");\n\n const payload = {\n food_id: currentSelectedFood.food_id,\n meal_type: activeMealType,\n quantity_grams: qty,\n date: selectedDateKey\n };\n\n btnAddSelection.textContent = \"Mentés...\";\n btnAddSelection.disabled = true;\n\n try {\n const res = await fetch(\"/api/v1/food_log/\", {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\", \"Authorization\": `Bearer ${token}` },\n body: JSON.stringify(payload)\n });\n if (!res.ok) throw new Error(\"Hiba a mentéskor\");\n\n await fetchDailyLog();\n modal.style.display = 'none';\n resetModalRightSide();\n showToast(\"Étel hozzáadva! 🍎\");\n\n } catch (err) {\n console.error(err);\n alert(`Hiba: ${err.message}`);\n } finally {\n btnAddSelection.textContent = \"Hozzáadás a naplóhoz\";\n btnAddSelection.disabled = false;\n }\n}\n\n// =================================================\n// 5. CÉLOK ÉS MAKRÓK\n// =================================================\nasync function fetchUserGoals() {\n if (!token) return;\n try {\n const res = await fetch(\"/api/v1/diet/calculate\", {\n method: \"POST\",\n headers: { \"Authorization\": `Bearer ${token}` }\n });\n if (res.ok) {\n const data = await res.json();\n const wInput = document.getElementById('weightInput');\n if (wInput && data.current_weight && document.activeElement !== wInput) {\n wInput.value = data.current_weight;\n }\n targetMacros = { cal: data.calories, p: data.protein, c: data.carbs, f: data.fat };\n updateMacroDisplays(); \n }\n } catch (err) { console.error(err); }\n}\n\nasync function fetchDailyLog() {\n if (!token) return;\n selectedDateKey = getLocalISODate(selectedDate); // Javított dátum\n \n try {\n const res = await fetch(`/api/v1/food_log/?date_str=${selectedDateKey}`, {\n headers: { \"Authorization\": `Bearer ${token}` }\n });\n if (res.ok || res.status === 404) {\n const logData = (res.ok) ? await res.json() : [];\n dailyLog = logData.reduce((acc, entry) => {\n const q = entry.quantity_grams / 100;\n const item = {\n log_id: entry.log_id,\n food_id: entry.food_id,\n name: entry.food_name,\n kcal: Math.round(entry.kcal_100g * q),\n p: Math.round(entry.protein_100g * q),\n c: Math.round(entry.carbs_100g * q),\n f: Math.round(entry.fat_100g * q),\n quantity: entry.quantity_grams,\n };\n acc[entry.meal_type] = acc[entry.meal_type] || [];\n acc[entry.meal_type].push(item);\n return acc;\n }, {});\n renderDailyFoodList();\n updateMacroDisplays();\n }\n } catch (err) { console.error(err); }\n}\n\nfunction renderDailyFoodList() {\n if (!dailyFoodList) return;\n dailyFoodList.innerHTML = '';\n let totalItems = 0;\n \n for (const mealType in dailyLog) {\n const items = dailyLog[mealType];\n if (items.length > 0) {\n totalItems += items.length;\n const header = document.createElement('div');\n header.className = 'meal-header-row';\n header.innerHTML = `\n <h4 style=\"margin:0\">${mealTitles[mealType] || mealType}</h4>\n <button class=\"btn-icon-small danger\" title=\"Törlés\" onclick=\"deleteMealGroup('${mealType}')\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 24 24\" stroke-width=\"2\" stroke=\"currentColor\" style=\"width:18px;height:18px;\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M14.74 9l-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 01-2.244 2.077H8.084a2.25 2.25 0 01-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 00-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 013.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 00-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 00-7.5 0\" /></svg>\n </button>\n `;\n dailyFoodList.appendChild(header);\n\n items.forEach(item => {\n const row = document.createElement('div');\n row.className = 'meal-item';\n row.innerHTML = `\n <div class=\"meal-info\"><span>${item.name} <small class=\"muted\">(${item.quantity}g)</small></span><span class=\"kcal\">${item.kcal} kcal</span></div>\n <button class=\"btn-delete-log\" onclick=\"deleteFoodLogEntry(${item.log_id})\"><svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 24 24\" stroke-width=\"2\" stroke=\"currentColor\" style=\"width:16px;height:16px;\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M6 18L18 6M6 6l12 12\" /></svg></button>\n `;\n dailyFoodList.appendChild(row);\n });\n }\n }\n if (emptyListMsg) emptyListMsg.style.display = totalItems === 0 ? 'block' : 'none';\n}\n\nwindow.deleteMealGroup = async (mealType) => {\n if (!(await showConfirm())) return;\n try {\n const res = await fetch(`/api/v1/food_log/meal?date_str=${selectedDateKey}&meal_type=${mealType}`, {\n method: \"DELETE\", headers: { \"Authorization\": `Bearer ${token}` }\n });\n if (res.ok) { showToast(\"Étkezés törölve!\"); fetchDailyLog(); }\n } catch (err) { console.error(err); }\n};\n\nwindow.deleteFoodLogEntry = async (logId) => {\n if (!(await showConfirm())) return;\n try {\n const res = await fetch(`/api/v1/food_log/${logId}`, {\n method: \"DELETE\", headers: { \"Authorization\": `Bearer ${token}` }\n });\n if (res.ok) fetchDailyLog();\n } catch (err) { console.error(err); }\n};\n\nfunction updateMacroDisplays() {\n let pConsumed = 0, cConsumed = 0, fConsumed = 0, calConsumed = 0;\n for (const mealType in dailyLog) {\n dailyLog[mealType].forEach(item => {\n pConsumed += (item.p || 0); cConsumed += (item.c || 0); fConsumed += (item.f || 0); calConsumed += (item.kcal || 0);\n });\n }\n\n const pGoal = targetMacros.p || 1;\n const cGoal = targetMacros.c || 1;\n const fGoal = targetMacros.f || 1;\n \n const calRemaining = Math.round(targetMacros.cal - calConsumed);\n const remKcalVal = document.getElementById('remainingKcalValue');\n const calCirc = document.getElementById('calorieCircle');\n if(remKcalVal) remKcalVal.textContent = calRemaining;\n if(calCirc) calCirc.classList.toggle('over-limit', calRemaining < 0);\n\n const updateCircle = (cid, vid, cons, goal) => {\n const el = document.getElementById(cid);\n const val = document.getElementById(vid);\n if (el && val) {\n const rem = Math.round(goal - cons);\n const pct = Math.max(0, Math.min(100, (rem / goal) * 100));\n val.textContent = rem;\n el.style.setProperty('--progress-percent', `${pct}%`);\n el.classList.toggle('over-limit', rem < 0);\n }\n };\n updateCircle('proteinCircle', 'proteinValue', pConsumed, pGoal);\n updateCircle('carbsCircle', 'carbsValue', cConsumed, cGoal);\n updateCircle('fatCircle', 'fatValue', fConsumed, fGoal);\n}\n\n// =================================================\n// 6. INITIALIZATION\n// =================================================\ndocument.addEventListener('DOMContentLoaded', () => {\n if (document.getElementById('diet-panel-top')) {\n initDayNavigator();\n initMealTabs();\n initWeightControl();\n initWaterControl();\n \n if (token) {\n fetchDailyLog();\n fetchUserGoals();\n } else {\n renderDailyFoodList();\n }\n }\n\n const btnOpenAi = document.getElementById('btnOpenAiModal');\n const modalAi = document.getElementById('aiChoiceModal');\n const closeAi = document.getElementById('closeAiModal');\n const aiBtns = document.querySelectorAll('.ai-meal-btn');\n\n if (btnOpenAi && modalAi) {\n btnOpenAi.addEventListener('click', () => modalAi.style.display = 'grid');\n if(closeAi) closeAi.addEventListener('click', () => modalAi.style.display = 'none');\n\n aiBtns.forEach(btn => {\n btn.addEventListener('click', async () => {\n const mealType = btn.dataset.meal;\n modalAi.style.display = 'none';\n showToast(\"⏳ Egy pillanat...\", \"info\");\n try {\n const res = await fetch(`/api/v1/diet/recommendation/suggest/${mealType}`, {\n headers: { \"Authorization\": `Bearer ${token}` }\n });\n if (!res.ok) throw new Error(\"Hiba\");\n const suggestion = await res.json();\n activeMealType = mealType;\n modalTitle.textContent = (mealTitles[mealType] || mealType) + \" (Ajánlat)\";\n modal.style.display = 'grid';\n selectFoodInModal(suggestion);\n const qInput = document.getElementById('selQuantity');\n if (qInput) {\n qInput.value = suggestion.suggested_quantity;\n qInput.dispatchEvent(new Event('input'));\n }\n showToast(\"💡 Ajánlat betöltve!\");\n } catch (e) { showToast(\"Nem sikerült ajánlani.\", \"error\"); }\n });\n });\n }\n});"}}}}