sozday-veb-prilozhenie-dlya-3/script.js

320 lines
12 KiB
JavaScript
Raw 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.

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 = '<div class="empty-state"><p>Нет блюд</p></div>';
return;
}
container.innerHTML = meals.map(m => `
<div class="meal-card" onclick="showRecipe('${m.id}')">
<span class="type-badge" data-type="${m.type}">${m.type}</span>
<h4>${m.name}</h4>
<div class="kbzu-row">
<span class="kbzu-item"><strong>${m.calories}</strong> ккал</span>
<span class="kbzu-item">Б: <strong>${m.protein}</strong>г</span>
<span class="kbzu-item">Ж: <strong>${m.fat}</strong>г</span>
<span class="kbzu-item">У: <strong>${m.carbs}</strong>г</span>
</div>
<div style="margin-top: 0.8rem; font-size: 0.85rem; color: #718096;">
⏱ ${m.time_min} мин | 🍽 ${m.servings} порц.
</div>
</div>
`).join("");
}
function renderRecipes(meals) {
const container = document.getElementById("recipes-list");
container.innerHTML = meals.map(m => `
<div class="recipe-card" onclick="showRecipe('${m.id}')">
<span class="type-badge" data-type="${m.type}">${m.type}</span>
<h4>${m.name}</h4>
<div class="kbzu-row">
<span class="kbzu-item"><strong>${m.calories}</strong> ккал</span>
<span class="kbzu-item">🍽 ${m.servings} порц.</span>
</div>
</div>
`).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 = `
<h3>${meal.name}</h3>
<div class="detail-grid">
<div class="ingredients-list">
<h4>Ингредиенты (на ${meal.servings} порций)</h4>
<ul>
${meal.ingredients.map(i => `<li>${i.name} — ${i.amount} ${i.unit}</li>`).join("")}
</ul>
</div>
<div class="kbzu-table">
<h4>КБЖУ на порцию</h4>
<table>
<tr><th>Калории</th><td><strong>${meal.calories}</strong> ккал</td></tr>
<tr><th>Белки</th><td><strong>${meal.protein}</strong> г</td></tr>
<tr><th>Жиры</th><td><strong>${meal.fat}</strong> г</td></tr>
<tr><th>Углеводы</th><td><strong>${meal.carbs}</strong> г</td></tr>
</table>
<p style="margin-top: 1rem; color: #718096; font-size: 0.9rem;">
⏱ Время приготовления: <strong>${meal.time_min} мин</strong>
</p>
</div>
</div>
`;
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 = `
<h3>📊 Итого за неделю (на 4 человек)</h3>
<div class="totals-grid">
<div class="total-item">
<div class="value">${totals.calories.toLocaleString()}</div>
<div class="label">ккал всего</div>
</div>
<div class="total-item">
<div class="value">${totals.avg_daily_calories.toLocaleString()}</div>
<div class="label">ккал в день (среднее)</div>
</div>
<div class="total-item">
<div class="value">${totals.protein}г</div>
<div class="label">Белки</div>
</div>
<div class="total-item">
<div class="value">${totals.fat}г</div>
<div class="label">Жиры</div>
</div>
<div class="total-item">
<div class="value">${totals.carbs}г</div>
<div class="label">Углеводы</div>
</div>
<div class="total-item">
<div class="value">${totals.cost.toLocaleString()} ₸</div>
<div class="label">Примерная стоимость</div>
</div>
</div>
`;
}
function renderWeekPlan(plan) {
const container = document.getElementById("week-plan");
container.innerHTML = plan.map(day => `
<div class="day-card">
<h3>${day.day} <span class="date">${day.date}</span></h3>
<div class="meal-row">
<span class="meal-type">🌅 Завтрак</span>
<span class="meal-name">${day.breakfast.name}</span>
<span class="meal-kbzu">${day.breakfast.calories} ккал | Б:${day.breakfast.protein}г Ж:${day.breakfast.fat}г У:${day.breakfast.carbs}г</span>
</div>
<div class="meal-row">
<span class="meal-type">☀️ Обед</span>
<span class="meal-name">${day.lunch.name}</span>
<span class="meal-kbzu">${day.lunch.calories} ккал | Б:${day.lunch.protein}г Ж:${day.lunch.fat}г У:${day.lunch.carbs}г</span>
</div>
<div class="meal-row">
<span class="meal-type">🌙 Ужин</span>
<span class="meal-name">${day.dinner.name}</span>
<span class="meal-kbzu">${day.dinner.calories} ккал | Б:${day.dinner.protein}г Ж:${day.dinner.fat}г У:${day.dinner.carbs}г</span>
</div>
<div style="margin-top: 1rem; padding-top: 1rem; border-top: 2px solid #e2e8f0; display: flex; justify-content: space-between; align-items: center;">
<strong style="color: #667eea;">Итого за день: ${day.daily_total.calories} ккал</strong>
<span style="color: #718096; font-size: 0.9rem;">Б:${day.daily_total.protein}г | Ж:${day.daily_total.fat}г | У:${day.daily_total.carbs}г | ~${day.daily_total.cost} ₸</span>
</div>
</div>
`).join("");
}
function renderShoppingList(shopping) {
const container = document.getElementById("shopping-list");
const summary = document.getElementById("shopping-summary");
summary.innerHTML = `
<h3>🛒 Список продуктов на неделю</h3>
<div class="totals-grid">
<div class="total-item">
<div class="value">${shopping.total_cost.toLocaleString()} ₸</div>
<div class="label">Общая стоимость</div>
</div>
<div class="total-item">
<div class="value">${shopping.budget.toLocaleString()} ₸</div>
<div class="label">Бюджет</div>
</div>
<div class="total-item">
<div class="value" style="color: ${shopping.remaining >= 0 ? '#48bb78' : '#f56565'}">${shopping.remaining.toLocaleString()} ₸</div>
<div class="label">Остаток</div>
</div>
</div>
`;
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]) => `
<div class="category-group">
<h3>${cat}</h3>
${items.map(p => `
<div class="product-item">
<span class="name">${p.name}</span>
<span class="amount">${p.amount >= 1000 ? (p.amount/1000).toFixed(2) + ' кг' : p.amount + ' г'}</span>
<span class="cost">${p.cost.toLocaleString()} ₸</span>
</div>
`).join("")}
</div>
`).join("");
}
function renderTimeline(timeline) {
const container = document.getElementById("timeline-list");
container.innerHTML = timeline.map(item => `
<div class="timeline-item">
<span class="day">${item.day}</span>
<span class="action">
<strong>${item.action}</strong>
<div class="details">${item.details}</div>
${item.ingredients ? `<div class="details" style="font-size: 0.8rem; color: #a0aec0;">📝 Продукты: ${item.ingredients.slice(0, 5).join(", ")}${item.ingredients.length > 5 ? "..." : ""}</div>` : ""}
</span>
<span class="time">⏱ ${item.time_min} мин</span>
</div>
`).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;