let allMeals = [];
let currentPlan = [];
document.addEventListener("DOMContentLoaded", () => {
setupTabs();
loadMeals();
setupFilters();
setupButtons();
});
function setupTabs() {
document.querySelectorAll(".tab").forEach(tab => {
tab.addEventListener("click", () => {
document.querySelectorAll(".tab").forEach(t => t.classList.remove("active"));
document.querySelectorAll(".tab-content").forEach(c => c.classList.remove("active"));
tab.classList.add("active");
document.getElementById(tab.dataset.tab).classList.add("active");
});
});
}
async function loadMeals() {
try {
const res = await fetch("/api/meals");
allMeals = await res.json();
renderMeals(allMeals);
renderRecipes(allMeals);
} catch (e) {
console.error("Ошибка загрузки блюд:", e);
}
}
function setupFilters() {
document.querySelectorAll(".filter-btn").forEach(btn => {
btn.addEventListener("click", () => {
document.querySelectorAll(".filter-btn").forEach(b => b.classList.remove("active"));
btn.classList.add("active");
const type = btn.dataset.type;
if (type === "all") {
renderMeals(allMeals);
} else {
renderMeals(allMeals.filter(m => m.type === type));
}
});
});
}
function setupButtons() {
document.getElementById("btn-generate").addEventListener("click", generatePlan);
document.getElementById("btn-export").addEventListener("click", exportToExcel);
}
function renderMeals(meals) {
const container = document.getElementById("meals-list");
if (meals.length === 0) {
container.innerHTML = '
';
return;
}
container.innerHTML = meals.map(m => `
${m.type}
${m.name}
${m.calories} ккал
Б: ${m.protein}г
Ж: ${m.fat}г
У: ${m.carbs}г
⏱ ${m.time_min} мин | 🍽 ${m.servings} порц.
`).join("");
}
function renderRecipes(meals) {
const container = document.getElementById("recipes-list");
container.innerHTML = meals.map(m => `
${m.type}
${m.name}
${m.calories} ккал
🍽 ${m.servings} порц.
`).join("");
}
async function showRecipe(id) {
const res = await fetch(`/api/meal/${id}`);
const meal = await res.json();
if (!meal) return;
document.getElementById("recipe-detail").innerHTML = `
${meal.name}
Ингредиенты (на ${meal.servings} порций)
${meal.ingredients.map(i => `- ${i.name} — ${i.amount} ${i.unit}
`).join("")}
КБЖУ на порцию
| Калории | ${meal.calories} ккал |
| Белки | ${meal.protein} г |
| Жиры | ${meal.fat} г |
| Углеводы | ${meal.carbs} г |
⏱ Время приготовления: ${meal.time_min} мин
`;
document.getElementById("recipe-detail").classList.add("active");
document.getElementById("recipe-detail").scrollIntoView({ behavior: "smooth" });
}
async function generatePlan() {
const btn = document.getElementById("btn-generate");
btn.textContent = "⏳ Генерация...";
btn.disabled = true;
try {
console.log("Генерация плана...");
const res = await fetch("/api/generate-plan", { method: "POST" });
console.log("Статус ответа:", res.status);
if (!res.ok) {
const errText = await res.text();
throw new Error("Сервер вернул ошибку " + res.status + ": " + errText);
}
const result = await res.json();
console.log("План получен:", result);
currentPlan = result.plan;
renderWeeklyTotals(result.weekly_totals);
renderWeekPlan(result.plan);
const shoppingRes = await fetch("/api/shopping-list", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ plan: result.plan })
});
if (!shoppingRes.ok) {
throw new Error("Список продуктов: ошибка " + shoppingRes.status);
}
const shopping = await shoppingRes.json();
console.log("Список продуктов:", shopping);
renderShoppingList(shopping);
const timelineRes = await fetch("/api/timeline", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ plan: result.plan })
});
if (!timelineRes.ok) {
throw new Error("Хронология: ошибка " + timelineRes.status);
}
const timeline = await timelineRes.json();
console.log("Хронология:", timeline);
renderTimeline(timeline);
btn.textContent = "🔄 Сгенерировать заново";
} catch (e) {
console.error("❌ Ошибка генерации:", e);
alert("Ошибка: " + e.message);
btn.textContent = "⚠️ Ошибка: " + e.message;
}
btn.disabled = false;
}
function renderWeeklyTotals(totals) {
document.getElementById("weekly-totals").innerHTML = `
📊 Итого за неделю (на 4 человек)
${totals.calories.toLocaleString()}
ккал всего
${totals.avg_daily_calories.toLocaleString()}
ккал в день (среднее)
${totals.carbs}г
Углеводы
${totals.cost.toLocaleString()} ₸
Примерная стоимость
`;
}
function renderWeekPlan(plan) {
const container = document.getElementById("week-plan");
container.innerHTML = plan.map(day => `
${day.day} ${day.date}
🌅 Завтрак
${day.breakfast.name}
${day.breakfast.calories} ккал | Б:${day.breakfast.protein}г Ж:${day.breakfast.fat}г У:${day.breakfast.carbs}г
☀️ Обед
${day.lunch.name}
${day.lunch.calories} ккал | Б:${day.lunch.protein}г Ж:${day.lunch.fat}г У:${day.lunch.carbs}г
🌙 Ужин
${day.dinner.name}
${day.dinner.calories} ккал | Б:${day.dinner.protein}г Ж:${day.dinner.fat}г У:${day.dinner.carbs}г
Итого за день: ${day.daily_total.calories} ккал
Б:${day.daily_total.protein}г | Ж:${day.daily_total.fat}г | У:${day.daily_total.carbs}г | ~${day.daily_total.cost} ₸
`).join("");
}
function renderShoppingList(shopping) {
const container = document.getElementById("shopping-list");
const summary = document.getElementById("shopping-summary");
summary.innerHTML = `
🛒 Список продуктов на неделю
${shopping.total_cost.toLocaleString()} ₸
Общая стоимость
${shopping.budget.toLocaleString()} ₸
Бюджет
${shopping.remaining.toLocaleString()} ₸
Остаток
`;
const byCategory = {};
shopping.items.forEach(item => {
if (!byCategory[item.category]) byCategory[item.category] = [];
byCategory[item.category].push(item);
});
container.innerHTML = Object.entries(byCategory).map(([cat, items]) => `
${cat}
${items.map(p => `
${p.name}
${p.amount >= 1000 ? (p.amount/1000).toFixed(2) + ' кг' : p.amount + ' г'}
${p.cost.toLocaleString()} ₸
`).join("")}
`).join("");
}
function renderTimeline(timeline) {
const container = document.getElementById("timeline-list");
container.innerHTML = timeline.map(item => `
${item.day}
${item.action}
${item.details}
${item.ingredients ? `📝 Продукты: ${item.ingredients.slice(0, 5).join(", ")}${item.ingredients.length > 5 ? "..." : ""}
` : ""}
⏱ ${item.time_min} мин
`).join("");
}
function exportToExcel() {
const shopping = document.getElementById("shopping-list");
if (!shopping || shopping.children.length === 0) {
alert("Сначала сгенерируйте план питания!");
return;
}
let csv = "\uFEFF";
csv += "Категория;Продукт;Количество;Стоимость (₸)\n";
document.querySelectorAll(".category-group").forEach(group => {
const category = group.querySelector("h3").textContent;
group.querySelectorAll(".product-item").forEach(item => {
const name = item.querySelector(".name").textContent;
const amount = item.querySelector(".amount").textContent;
const cost = item.querySelector(".cost").textContent.replace(" ₸", "");
csv += `${category};${name};${amount};${cost}\n`;
});
});
const blob = new Blob([csv], { type: "text/csv;charset=utf-8;" });
const link = document.createElement("a");
link.href = URL.createObjectURL(blob);
link.download = "spisok_produktov.csv";
link.click();
}
window.showRecipe = showRecipe;