1 line
26 KiB
JSON
1 line
26 KiB
JSON
{"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});"}}}} |