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

460 lines
30 KiB
JavaScript
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.

const MEALS_DATA = [
{"id":"m1","name":"Овсяная каша с ягодами и орехами","type":"завтрак","servings":4,"time_min":15,"calories":420,"protein":12,"fat":14,"carbs":62,"ingredients":[{"product_id":"p1","name":"Овсяные хлопья","amount":320,"unit":"г"},{"product_id":"p2","name":"Молоко 2.5%","amount":800,"unit":"мл"},{"product_id":"p3","name":"Ягоды замороженные","amount":200,"unit":"г"},{"product_id":"p4","name":"Орехи грецкие","amount":40,"unit":"г"},{"product_id":"p5","name":"Мёд","amount":40,"unit":"г"}]},
{"id":"m2","name":"Омлет с овощами и сыром","type":"завтрак","servings":4,"time_min":20,"calories":380,"protein":22,"fat":28,"carbs":8,"ingredients":[{"product_id":"p6","name":"Яйца","amount":12,"unit":"шт"},{"product_id":"p2","name":"Молоко 2.5%","amount":200,"unit":"мл"},{"product_id":"p7","name":"Сыр твёрдый","amount":120,"unit":"г"},{"product_id":"p8","name":"Помидоры","amount":300,"unit":"г"},{"product_id":"p9","name":"Перец болгарский","amount":200,"unit":"г"},{"product_id":"p10","name":"Масло сливочное","amount":30,"unit":"г"}]},
{"id":"m3","name":"Творожная запеканка с изюмом","type":"завтрак","servings":4,"time_min":45,"calories":340,"protein":18,"fat":16,"carbs":32,"ingredients":[{"product_id":"p11","name":"Творог 5%","amount":600,"unit":"г"},{"product_id":"p6","name":"Яйца","amount":4,"unit":"шт"},{"product_id":"p12","name":"Манка","amount":80,"unit":"г"},{"product_id":"p13","name":"Изюм","amount":80,"unit":"г"},{"product_id":"p14","name":"Сметана 15%","amount":100,"unit":"г"},{"product_id":"p15","name":"Сахар","amount":60,"unit":"г"}]},
{"id":"m4","name":"Борщ с говядиной","type":"обед","servings":6,"time_min":90,"calories":320,"protein":18,"fat":14,"carbs":28,"ingredients":[{"product_id":"p16","name":"Говядина","amount":500,"unit":"г"},{"product_id":"p17","name":"Свёкла","amount":400,"unit":"г"},{"product_id":"p18","name":"Капуста белокочанная","amount":400,"unit":"г"},{"product_id":"p19","name":"Картофель","amount":400,"unit":"г"},{"product_id":"p20","name":"Морковь","amount":150,"unit":"г"},{"product_id":"p21","name":"Лук репчатый","amount":150,"unit":"г"},{"product_id":"p22","name":"Томатная паста","amount":60,"unit":"г"},{"product_id":"p14","name":"Сметана 15%","amount":120,"unit":"г"}]},
{"id":"m5","name":"Куриный суп с лапшой","type":"обед","servings":6,"time_min":60,"calories":280,"protein":20,"fat":10,"carbs":26,"ingredients":[{"product_id":"p23","name":"Курица (филе/голени)","amount":500,"unit":"г"},{"product_id":"p19","name":"Картофель","amount":300,"unit":"г"},{"product_id":"p20","name":"Морковь","amount":100,"unit":"г"},{"product_id":"p21","name":"Лук репчатый","amount":100,"unit":"г"},{"product_id":"p24","name":"Лапша яичная","amount":150,"unit":"г"}]},
{"id":"m6","name":"Плов с курицей","type":"обед","servings":6,"time_min":70,"calories":420,"protein":22,"fat":16,"carbs":48,"ingredients":[{"product_id":"p23","name":"Курица (филе/голени)","amount":600,"unit":"г"},{"product_id":"p25","name":"Рис длиннозерный","amount":400,"unit":"г"},{"product_id":"p20","name":"Морковь","amount":300,"unit":"г"},{"product_id":"p21","name":"Лук репчатый","amount":200,"unit":"г"},{"product_id":"p26","name":"Масло растительное","amount":60,"unit":"мл"}]},
{"id":"m7","name":"Макароны по-флотски","type":"обед","servings":4,"time_min":35,"calories":450,"protein":24,"fat":18,"carbs":52,"ingredients":[{"product_id":"p27","name":"Фарш говяжий","amount":400,"unit":"г"},{"product_id":"p28","name":"Макароны твёрдых сортов","amount":400,"unit":"г"},{"product_id":"p21","name":"Лук репчатый","amount":150,"unit":"г"},{"product_id":"p20","name":"Морковь","amount":150,"unit":"г"},{"product_id":"p26","name":"Масло растительное","amount":30,"unit":"мл"}]},
{"id":"m8","name":"Котлеты домашние с пюре","type":"обед","servings":4,"time_min":50,"calories":480,"protein":26,"fat":22,"carbs":42,"ingredients":[{"product_id":"p27","name":"Фарш говяжий","amount":500,"unit":"г"},{"product_id":"p19","name":"Картофель","amount":800,"unit":"г"},{"product_id":"p29","name":"Хлеб белый","amount":100,"unit":"г"},{"product_id":"p6","name":"Яйца","amount":2,"unit":"шт"},{"product_id":"p21","name":"Лук репчатый","amount":100,"unit":"г"},{"product_id":"p10","name":"Масло сливочное","amount":50,"unit":"г"}]},
{"id":"m9","name":"Гречка с куриной печенью","type":"обед","servings":4,"time_min":40,"calories":380,"protein":24,"fat":12,"carbs":44,"ingredients":[{"product_id":"p30","name":"Печень куриная","amount":500,"unit":"г"},{"product_id":"p31","name":"Гречка","amount":320,"unit":"г"},{"product_id":"p21","name":"Лук репчатый","amount":150,"unit":"г"},{"product_id":"p20","name":"Морковь","amount":150,"unit":"г"},{"product_id":"p26","name":"Масло растительное","amount":40,"unit":"мл"}]},
{"id":"m10","name":"Запечённая рыба с овощами","type":"ужин","servings":4,"time_min":45,"calories":320,"protein":28,"fat":14,"carbs":18,"ingredients":[{"product_id":"p32","name":"Рыба белая (минтай/треска)","amount":600,"unit":"г"},{"product_id":"p19","name":"Картофель","amount":600,"unit":"г"},{"product_id":"p20","name":"Морковь","amount":200,"unit":"г"},{"product_id":"p33","name":"Кабачок","amount":300,"unit":"г"},{"product_id":"p26","name":"Масло растительное","amount":30,"unit":"мл"}]},
{"id":"m11","name":"Куриные котлеты с салатом","type":"ужин","servings":4,"time_min":40,"calories":340,"protein":30,"fat":12,"carbs":28,"ingredients":[{"product_id":"p34","name":"Куриное филе","amount":600,"unit":"г"},{"product_id":"p29","name":"Хлеб белый","amount":80,"unit":"г"},{"product_id":"p6","name":"Яйца","amount":2,"unit":"шт"},{"product_id":"p35","name":"Огурцы свежие","amount":300,"unit":"г"},{"product_id":"p8","name":"Помидоры","amount":300,"unit":"г"},{"product_id":"p36","name":"Зелень","amount":40,"unit":"г"}]},
{"id":"m12","name":"Тушёная капуста с мясом","type":"ужин","servings":4,"time_min":50,"calories":290,"protein":20,"fat":14,"carbs":22,"ingredients":[{"product_id":"p27","name":"Фарш говяжий","amount":400,"unit":"г"},{"product_id":"p18","name":"Капуста белокочанная","amount":800,"unit":"г"},{"product_id":"p21","name":"Лук репчатый","amount":150,"unit":"г"},{"product_id":"p20","name":"Морковь","amount":150,"unit":"г"},{"product_id":"p26","name":"Масло растительное","amount":30,"unit":"мл"}]},
{"id":"m13","name":"Плов с говядиной","type":"ужин","servings":6,"time_min":80,"calories":440,"protein":24,"fat":18,"carbs":46,"ingredients":[{"product_id":"p16","name":"Говядина","amount":500,"unit":"г"},{"product_id":"p25","name":"Рис длиннозерный","amount":400,"unit":"г"},{"product_id":"p20","name":"Морковь","amount":300,"unit":"г"},{"product_id":"p21","name":"Лук репчатый","amount":200,"unit":"г"},{"product_id":"p26","name":"Масло растительное","amount":60,"unit":"мл"}]},
{"id":"m14","name":"Салат Цезарь с курицей","type":"ужин","servings":4,"time_min":25,"calories":360,"protein":26,"fat":22,"carbs":14,"ingredients":[{"product_id":"p34","name":"Куриное филе","amount":400,"unit":"г"},{"product_id":"p37","name":"Салат айсберг","amount":300,"unit":"г"},{"product_id":"p38","name":"Сухарики","amount":80,"unit":"г"},{"product_id":"p7","name":"Сыр твёрдый","amount":100,"unit":"г"},{"product_id":"p39","name":"Соус Цезарь","amount":120,"unit":"мл"}]},
{"id":"m15","name":"Винегрет с селёдкой","type":"ужин","servings":4,"time_min":30,"calories":260,"protein":12,"fat":14,"carbs":24,"ingredients":[{"product_id":"p17","name":"Свёкла","amount":300,"unit":"г"},{"product_id":"p20","name":"Морковь","amount":200,"unit":"г"},{"product_id":"p19","name":"Картофель","amount":300,"unit":"г"},{"product_id":"p40","name":"Сельдь солёная","amount":200,"unit":"г"},{"product_id":"p41","name":"Огурцы солёные","amount":150,"unit":"г"},{"product_id":"p26","name":"Масло растительное","amount":40,"unit":"мл"}]},
{"id":"m16","name":"Яичница с колбасой и овощами","type":"завтрак","servings":4,"time_min":15,"calories":400,"protein":20,"fat":30,"carbs":12,"ingredients":[{"product_id":"p6","name":"Яйца","amount":12,"unit":"шт"},{"product_id":"p42","name":"Колбаса варёная","amount":200,"unit":"г"},{"product_id":"p8","name":"Помидоры","amount":200,"unit":"г"},{"product_id":"p9","name":"Перец болгарский","amount":150,"unit":"г"},{"product_id":"p10","name":"Масло сливочное","amount":30,"unit":"г"}]},
{"id":"m17","name":"Сырники со сметаной","type":"завтрак","servings":4,"time_min":30,"calories":380,"protein":16,"fat":18,"carbs":38,"ingredients":[{"product_id":"p11","name":"Творог 5%","amount":500,"unit":"г"},{"product_id":"p6","name":"Яйца","amount":3,"unit":"шт"},{"product_id":"p43","name":"Мука пшеничная","amount":100,"unit":"г"},{"product_id":"p15","name":"Сахар","amount":40,"unit":"г"},{"product_id":"p14","name":"Сметана 15%","amount":120,"unit":"г"}]},
{"id":"m18","name":"Рассольник с курицей","type":"обед","servings":6,"time_min":70,"calories":260,"protein":16,"fat":10,"carbs":26,"ingredients":[{"product_id":"p23","name":"Курица (филе/голени)","amount":400,"unit":"г"},{"product_id":"p19","name":"Картофель","amount":400,"unit":"г"},{"product_id":"p41","name":"Огурцы солёные","amount":200,"unit":"г"},{"product_id":"p25","name":"Рис длиннозерный","amount":100,"unit":"г"},{"product_id":"p20","name":"Морковь","amount":150,"unit":"г"},{"product_id":"p21","name":"Лук репчатый","amount":150,"unit":"г"}]},
{"id":"m19","name":"Гуляш из говядины с гречкой","type":"обед","servings":4,"time_min":80,"calories":460,"protein":28,"fat":18,"carbs":46,"ingredients":[{"product_id":"p16","name":"Говядина","amount":500,"unit":"г"},{"product_id":"p31","name":"Гречка","amount":320,"unit":"г"},{"product_id":"p21","name":"Лук репчатый","amount":150,"unit":"г"},{"product_id":"p20","name":"Морковь","amount":150,"unit":"г"},{"product_id":"p22","name":"Томатная паста","amount":60,"unit":"г"}]},
{"id":"m20","name":"Овощное рагу с курицей","type":"ужин","servings":4,"time_min":50,"calories":310,"protein":24,"fat":12,"carbs":26,"ingredients":[{"product_id":"p34","name":"Куриное филе","amount":500,"unit":"г"},{"product_id":"p33","name":"Кабачок","amount":300,"unit":"г"},{"product_id":"p9","name":"Перец болгарский","amount":200,"unit":"г"},{"product_id":"p8","name":"Помидоры","amount":300,"unit":"г"},{"product_id":"p21","name":"Лук репчатый","amount":150,"unit":"г"},{"product_id":"p26","name":"Масло растительное","amount":30,"unit":"мл"}]}
];
const PRODUCTS_DATA = [
{"id":"p1","name":"Овсяные хлопья","category":"Крупы","price_per_kg":450},
{"id":"p2","name":"Молоко 2.5%","category":"Молочные","price_per_kg":450},
{"id":"p3","name":"Ягоды замороженные","category":"Заморозка","price_per_kg":2500},
{"id":"p4","name":"Орехи грецкие","category":"Бакалея","price_per_kg":4500},
{"id":"p5","name":"Мёд","category":"Бакалея","price_per_kg":3500},
{"id":"p6","name":"Яйца","category":"Молочные","price_per_kg":650},
{"id":"p7","name":"Сыр твёрдый","category":"Молочные","price_per_kg":4200},
{"id":"p8","name":"Помидоры","category":"Овощи","price_per_kg":1200},
{"id":"p9","name":"Перец болгарский","category":"Овощи","price_per_kg":1400},
{"id":"p10","name":"Масло сливочное","category":"Молочные","price_per_kg":3800},
{"id":"p11","name":"Творог 5%","category":"Молочные","price_per_kg":3600},
{"id":"p12","name":"Манка","category":"Крупы","price_per_kg":400},
{"id":"p13","name":"Изюм","category":"Бакалея","price_per_kg":2200},
{"id":"p14","name":"Сметана 15%","category":"Молочные","price_per_kg":1800},
{"id":"p15","name":"Сахар","category":"Бакалея","price_per_kg":550},
{"id":"p16","name":"Говядина","category":"Мясо","price_per_kg":3200},
{"id":"p17","name":"Свёкла","category":"Овощи","price_per_kg":250},
{"id":"p18","name":"Капуста белокочанная","category":"Овощи","price_per_kg":200},
{"id":"p19","name":"Картофель","category":"Овощи","price_per_kg":300},
{"id":"p20","name":"Морковь","category":"Овощи","price_per_kg":250},
{"id":"p21","name":"Лук репчатый","category":"Овощи","price_per_kg":200},
{"id":"p22","name":"Томатная паста","category":"Консервы","price_per_kg":1200},
{"id":"p23","name":"Курица (филе/голени)","category":"Мясо","price_per_kg":2200},
{"id":"p24","name":"Лапша яичная","category":"Макароны","price_per_kg":800},
{"id":"p25","name":"Рис длиннозерный","category":"Крупы","price_per_kg":700},
{"id":"p26","name":"Масло растительное","category":"Бакалея","price_per_kg":900},
{"id":"p27","name":"Фарш говяжий","category":"Мясо","price_per_kg":2800},
{"id":"p28","name":"Макароны твёрдых сортов","category":"Макароны","price_per_kg":900},
{"id":"p29","name":"Хлеб белый","category":"Хлеб","price_per_kg":400},
{"id":"p30","name":"Печень куриная","category":"Мясо","price_per_kg":1600},
{"id":"p31","name":"Гречка","category":"Крупы","price_per_kg":650},
{"id":"p32","name":"Рыба белая (минтай/треска)","category":"Рыба","price_per_kg":2400},
{"id":"p33","name":"Кабачок","category":"Овощи","price_per_kg":600},
{"id":"p34","name":"Куриное филе","category":"Мясо","price_per_kg":2600},
{"id":"p35","name":"Огурцы свежие","category":"Овощи","price_per_kg":800},
{"id":"p36","name":"Зелень","category":"Овощи","price_per_kg":1000},
{"id":"p37","name":"Салат айсберг","category":"Овощи","price_per_kg":900},
{"id":"p38","name":"Сухарики","category":"Бакалея","price_per_kg":800},
{"id":"p39","name":"Соус Цезарь","category":"Соусы","price_per_kg":1800},
{"id":"p40","name":"Сельдь солёная","category":"Рыба","price_per_kg":1800},
{"id":"p41","name":"Огурцы солёные","category":"Консервы","price_per_kg":600},
{"id":"p42","name":"Колбаса варёная","category":"Мясо","price_per_kg":2400},
{"id":"p43","name":"Мука пшеничная","category":"Бакалея","price_per_kg":400}
];
let currentPlan = [];
document.addEventListener("DOMContentLoaded", () => {
setupTabs();
renderMeals(MEALS_DATA);
renderRecipes(MEALS_DATA);
setupFilters();
setupButtons();
console.log("✅ Данные загружены:", MEALS_DATA.length, "блюд,", PRODUCTS_DATA.length, "продуктов");
});
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");
});
});
}
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(MEALS_DATA);
} else {
renderMeals(MEALS_DATA.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("");
}
function showRecipe(id) {
const meal = MEALS_DATA.find(m => m.id === id);
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" });
}
function generatePlan() {
const btn = document.getElementById("btn-generate");
btn.textContent = "⏳ Генерация...";
btn.disabled = true;
try {
console.log("🔄 Начало генерации...");
const breakfasts = MEALS_DATA.filter(m => m.type === "завтрак");
const lunches = MEALS_DATA.filter(m => m.type === "обед");
const dinners = MEALS_DATA.filter(m => m.type === "ужин");
const days = ["Понедельник", "Вторник", "Среда", "Четверг", "Пятница", "Суббота", "Воскресенье"];
const plan = [];
let totalCalories = 0, totalProtein = 0, totalFat = 0, totalCarbs = 0, totalCost = 0;
const productMap = {};
PRODUCTS_DATA.forEach(p => { productMap[p.id] = { ...p, total_amount: 0 }; });
days.forEach((day, i) => {
const b = breakfasts[i % breakfasts.length];
const l = lunches[i % lunches.length];
const d = dinners[i % dinners.length];
const dayCalories = b.calories + l.calories + d.calories;
const dayProtein = b.protein + l.protein + d.protein;
const dayFat = b.fat + l.fat + d.fat;
const dayCarbs = b.carbs + l.carbs + d.carbs;
const dayCost = calculateMealCost(b) + calculateMealCost(l) + calculateMealCost(d);
plan.push({
day,
date: new Date(Date.now() + i * 86400000).toISOString().slice(0, 10),
breakfast: { id: b.id, name: b.name, calories: b.calories, protein: b.protein, fat: b.fat, carbs: b.carbs, time_min: b.time_min },
lunch: { id: l.id, name: l.name, calories: l.calories, protein: l.protein, fat: l.fat, carbs: l.carbs, time_min: l.time_min },
dinner: { id: d.id, name: d.name, calories: d.calories, protein: d.protein, fat: d.fat, carbs: d.carbs, time_min: d.time_min },
daily_total: { calories: dayCalories, protein: dayProtein, fat: dayFat, carbs: dayCarbs, cost: dayCost }
});
totalCalories += dayCalories;
totalProtein += dayProtein;
totalFat += dayFat;
totalCarbs += dayCarbs;
totalCost += dayCost;
[b, l, d].forEach(meal => {
meal.ingredients.forEach(ing => {
if (productMap[ing.product_id]) {
productMap[ing.product_id].total_amount += ing.amount;
}
});
});
});
currentPlan = plan;
console.log("✅ План сгенерирован:", plan.length, "дней");
const weekly_totals = {
calories: Math.round(totalCalories),
protein: Math.round(totalProtein),
fat: Math.round(totalFat),
carbs: Math.round(totalCarbs),
cost: Math.round(totalCost),
avg_daily_calories: Math.round(totalCalories / 7)
};
renderWeeklyTotals(weekly_totals);
renderWeekPlan(plan);
const shoppingList = [];
let shoppingTotal = 0;
Object.values(productMap).forEach(p => {
if (p.total_amount > 0) {
const cost = (p.total_amount / 1000) * p.price_per_kg;
shoppingList.push({
id: p.id,
name: p.name,
category: p.category,
amount: Math.round(p.total_amount),
unit: p.total_amount >= 1000 ? "кг" : "г",
price_per_kg: p.price_per_kg,
cost: Math.round(cost)
});
shoppingTotal += cost;
}
});
shoppingList.sort((a, b) => a.category.localeCompare(b.category) || a.name.localeCompare(b.name));
renderShoppingList({ items: shoppingList, total_cost: Math.round(shoppingTotal), budget: 100000, remaining: 100000 - Math.round(shoppingTotal) });
renderTimeline(plan);
btn.textContent = "🔄 Сгенерировать заново";
console.log("✅ Генерация завершена");
} catch (e) {
console.error("❌ Ошибка генерации:", e);
alert("Ошибка: " + e.message);
btn.textContent = "⚠️ Ошибка";
}
btn.disabled = false;
}
function calculateMealCost(meal) {
let cost = 0;
meal.ingredients.forEach(ing => {
const product = PRODUCTS_DATA.find(p => p.id === ing.product_id);
if (product) {
cost += (ing.amount / 1000) * product.price_per_kg;
}
});
return cost;
}
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(plan) {
const container = document.getElementById("timeline-list");
const timeline = [];
timeline.push({
day: "Воскресенье (перед неделей)",
action: "🛒 Закупка продуктов",
details: "Купить все продукты по списку",
time_min: 90
});
plan.forEach((day, dayIndex) => {
[day.breakfast, day.lunch, day.dinner].forEach((meal, mealIdx) => {
const mealData = MEALS_DATA.find(m => m.id === meal.id);
if (!mealData) return;
const mealTypes = ["🌅 Завтрак", "☀️ Обед", "🌙 Ужин"];
timeline.push({
day: day.day,
action: `${mealTypes[mealIdx]}: ${mealData.name}`,
details: `Ингредиенты: ${mealData.ingredients.map(i => i.name).slice(0, 4).join(", ")}${mealData.ingredients.length > 4 ? "..." : ""}`,
time_min: mealData.time_min
});
});
});
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>
</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;