293 lines
12 KiB
HTML
293 lines
12 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="ru" data-theme="auto">
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||
<title>Мой план питания</title>
|
||
<link rel="stylesheet" href="design-system/kt-ai-fonts.css">
|
||
<link rel="stylesheet" href="design-system/kt-ai-tokens.css">
|
||
<link rel="stylesheet" href="design-system/kt-ai-components.css">
|
||
<link rel="stylesheet" href="design-system/kt-ai-page.css">
|
||
<style>
|
||
a.kt-ai-btn { text-decoration: none; }
|
||
.meal-card { cursor: pointer; transition: transform 0.2s; }
|
||
.meal-card:hover { transform: translateY(-2px); }
|
||
.meal-card.selected { border: 2px solid var(--kt-ai-primary); }
|
||
.kpi-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); gap: var(--kt-ai-space-4); }
|
||
.kpi-card { text-align: center; padding: var(--kt-ai-space-5); background: var(--kt-ai-bg-soft); border-radius: var(--kt-ai-radius-xl); }
|
||
.kpi-value { font-size: var(--kt-ai-text-3xl); font-weight: var(--kt-ai-weight-bold); color: var(--kt-ai-primary); }
|
||
.kpi-label { font-size: var(--kt-ai-text-sm); color: var(--kt-ai-fg-muted); margin-top: var(--kt-ai-space-1); }
|
||
.day-selector { display: flex; gap: var(--kt-ai-space-2); overflow-x: auto; padding: var(--kt-ai-space-2) 0; }
|
||
.day-btn { flex-shrink: 0; padding: var(--kt-ai-space-2) var(--kt-ai-space-4); border-radius: var(--kt-ai-radius-lg); border: 1px solid var(--kt-ai-border); background: var(--kt-ai-bg); cursor: pointer; }
|
||
.day-btn.active { background: var(--kt-ai-primary); color: white; border-color: var(--kt-ai-primary); }
|
||
.meal-type { font-size: var(--kt-ai-text-sm); font-weight: var(--kt-ai-weight-semibold); color: var(--kt-ai-fg-muted); text-transform: uppercase; margin-bottom: var(--kt-ai-space-3); }
|
||
.progress-bar { height: 8px; background: var(--kt-ai-bg-soft); border-radius: var(--kt-ai-radius-full); overflow: hidden; }
|
||
.progress-fill { height: 100%; background: var(--kt-ai-primary); transition: width 0.3s; }
|
||
.hidden { display: none; }
|
||
</style>
|
||
</head>
|
||
<body>
|
||
|
||
<header class="kt-ai-pagehead">
|
||
<div class="kt-ai-wrap kt-ai-pagehead-in">
|
||
<strong>🍽 Мой план питания</strong>
|
||
<span class="kt-ai-spacer"></span>
|
||
<button class="kt-ai-btn" data-variant="ghost" id="resetBtn">Сбросить</button>
|
||
</div>
|
||
</header>
|
||
|
||
<section class="kt-ai-hero" data-align="center">
|
||
<div class="kt-ai-wrap">
|
||
<h1>Конструктор питания на день</h1>
|
||
<p class="kt-ai-hero-sub">Выбирайте блюда из готовой базы — система посчитает КБЖУ и сохранит ваш план</p>
|
||
</div>
|
||
</section>
|
||
|
||
<div class="kt-ai-wrap" style="margin-top: var(--kt-ai-space-8)">
|
||
<div class="kpi-grid" id="kpiGrid">
|
||
<div class="kpi-card">
|
||
<div class="kpi-value" id="kcalValue">0</div>
|
||
<div class="kpi-label">Ккал</div>
|
||
</div>
|
||
<div class="kpi-card">
|
||
<div class="kpi-value" id="proteinValue">0 г</div>
|
||
<div class="kpi-label">Белки</div>
|
||
</div>
|
||
<div class="kpi-card">
|
||
<div class="kpi-value" id="fatsValue">0 г</div>
|
||
<div class="kpi-label">Жиры</div>
|
||
</div>
|
||
<div class="kpi-card">
|
||
<div class="kpi-value" id="carbsValue">0 г</div>
|
||
<div class="kpi-label">Углеводы</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<section class="kt-ai-section" data-divider="true">
|
||
<div class="kt-ai-wrap">
|
||
<div class="kt-ai-section-head">
|
||
<h2>Выберите день</h2>
|
||
<p>Нажмите на день, чтобы настроить план питания</p>
|
||
</div>
|
||
<div class="day-selector" id="daySelector"></div>
|
||
</div>
|
||
</section>
|
||
|
||
<section class="kt-ai-section">
|
||
<div class="kt-ai-wrap">
|
||
<div class="kt-ai-section-head">
|
||
<h2>План на <span id="currentDayName">Понедельник</span></h2>
|
||
<p>Выберите блюда для каждого приёма пищи</p>
|
||
</div>
|
||
|
||
<div id="mealsContainer">
|
||
<div style="margin-bottom: var(--kt-ai-space-8)">
|
||
<div class="meal-type">Завтрак</div>
|
||
<div class="kt-ai-grid" data-cols="3" id="breakfastGrid"></div>
|
||
</div>
|
||
|
||
<div style="margin-bottom: var(--kt-ai-space-8)">
|
||
<div class="meal-type">Обед</div>
|
||
<div class="kt-ai-grid" data-cols="3" id="lunchGrid"></div>
|
||
</div>
|
||
|
||
<div style="margin-bottom: var(--kt-ai-space-8)">
|
||
<div class="meal-type">Ужин</div>
|
||
<div class="kt-ai-grid" data-cols="3" id="dinnerGrid"></div>
|
||
</div>
|
||
|
||
<div style="margin-bottom: var(--kt-ai-space-8)">
|
||
<div class="meal-type">Перекус</div>
|
||
<div class="kt-ai-grid" data-cols="3" id="snackGrid"></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<section class="kt-ai-cta">
|
||
<div class="kt-ai-wrap">
|
||
<h2>Ваш прогресс</h2>
|
||
<div style="max-width: 500px; margin: var(--kt-ai-space-6) auto">
|
||
<div style="display: flex; justify-content: space-between; margin-bottom: var(--kt-ai-space-2)">
|
||
<span>Выполнение плана</span>
|
||
<span id="progressPercent">0%</span>
|
||
</div>
|
||
<div class="progress-bar">
|
||
<div class="progress-fill" id="progressFill" style="width: 0%"></div>
|
||
</div>
|
||
<p style="margin-top: var(--kt-ai-space-4); color: var(--kt-ai-fg-muted)">
|
||
Цель: <span id="targetKcal">2000</span> ккал в день
|
||
</p>
|
||
</div>
|
||
<button class="kt-ai-btn" data-variant="primary" data-size="lg" id="saveBtn">Сохранить план</button>
|
||
</div>
|
||
</section>
|
||
|
||
<footer class="kt-ai-wrap kt-ai-pagefoot">
|
||
<span>© 2026 Мой план питания</span>
|
||
<span class="kt-ai-spacer"></span>
|
||
<span>Сделано для здоровья</span>
|
||
</footer>
|
||
|
||
<script>
|
||
const days = ['Понедельник', 'Вторник', 'Среда', 'Четверг', 'Пятница', 'Суббота', 'Воскресенье'];
|
||
const mealsDB = {
|
||
breakfast: [
|
||
{ id: 'b1', name: 'Овсянка с ягодами', kcal: 350, protein: 12, fats: 8, carbs: 55 },
|
||
{ id: 'b2', name: 'Омлет с овощами', kcal: 280, protein: 18, fats: 20, carbs: 6 },
|
||
{ id: 'b3', name: 'Творог с мёдом', kcal: 320, protein: 22, fats: 10, carbs: 35 },
|
||
{ id: 'b4', name: 'Гречка с яйцом', kcal: 400, protein: 16, fats: 14, carbs: 52 },
|
||
{ id: 'b5', name: 'Смузи с бананом', kcal: 250, protein: 8, fats: 6, carbs: 42 },
|
||
{ id: 'b6', name: 'Сырники', kcal: 380, protein: 20, fats: 16, carbs: 38 }
|
||
],
|
||
lunch: [
|
||
{ id: 'l1', name: 'Курица с рисом', kcal: 520, protein: 35, fats: 12, carbs: 65 },
|
||
{ id: 'l2', name: 'Борщ с хлебом', kcal: 380, protein: 14, fats: 18, carbs: 42 },
|
||
{ id: 'l3', name: 'Паста с мясом', kcal: 580, protein: 28, fats: 22, carbs: 68 },
|
||
{ id: 'l4', name: 'Салат с тунцом', kcal: 320, protein: 26, fats: 16, carbs: 18 },
|
||
{ id: 'l5', name: 'Плов с говядиной', kcal: 620, protein: 30, fats: 24, carbs: 72 },
|
||
{ id: 'l6', name: 'Суп-лапша', kcal: 340, protein: 18, fats: 12, carbs: 45 }
|
||
],
|
||
dinner: [
|
||
{ id: 'd1', name: 'Рыба с овощами', kcal: 380, protein: 32, fats: 14, carbs: 22 },
|
||
{ id: 'd2', name: 'Индейка гриль', kcal: 320, protein: 38, fats: 10, carbs: 12 },
|
||
{ id: 'd3', name: 'Овощное рагу', kcal: 280, protein: 10, fats: 12, carbs: 35 },
|
||
{ id: 'd4', name: 'Котлеты с пюре', kcal: 480, protein: 24, fats: 20, carbs: 48 },
|
||
{ id: 'd5', name: 'Салат Цезарь', kcal: 360, protein: 22, fats: 18, carbs: 20 },
|
||
{ id: 'd6', name: 'Запечённая курица', kcal: 420, protein: 40, fats: 16, carbs: 18 }
|
||
],
|
||
snack: [
|
||
{ id: 's1', name: 'Яблоко с орехами', kcal: 180, protein: 4, fats: 10, carbs: 22 },
|
||
{ id: 's2', name: 'Йогурт с мюсли', kcal: 220, protein: 10, fats: 8, carbs: 28 },
|
||
{ id: 's3', name: 'Протеиновый батончик', kcal: 200, protein: 20, fats: 6, carbs: 18 },
|
||
{ id: 's4', name: 'Банан', kcal: 105, protein: 1, fats: 0, carbs: 27 },
|
||
{ id: 's5', name: 'Творожок', kcal: 140, protein: 14, fats: 4, carbs: 12 },
|
||
{ id: 's6', name: 'Ореховая смесь', kcal: 250, protein: 8, fats: 22, carbs: 10 }
|
||
]
|
||
};
|
||
|
||
let currentDay = 0;
|
||
let weeklyPlan = JSON.parse(localStorage.getItem('weeklyPlan')) || {};
|
||
|
||
function init() {
|
||
renderDaySelector();
|
||
renderMeals();
|
||
updateKPI();
|
||
updateProgress();
|
||
|
||
document.getElementById('resetBtn').addEventListener('click', resetPlan);
|
||
document.getElementById('saveBtn').addEventListener('click', savePlan);
|
||
}
|
||
|
||
function renderDaySelector() {
|
||
const container = document.getElementById('daySelector');
|
||
container.innerHTML = days.map((day, idx) =>
|
||
`<button class="day-btn ${idx === currentDay ? 'active' : ''}" onclick="selectDay(${idx})">${day}</button>`
|
||
).join('');
|
||
document.getElementById('currentDayName').textContent = days[currentDay];
|
||
}
|
||
|
||
function selectDay(idx) {
|
||
currentDay = idx;
|
||
renderDaySelector();
|
||
renderMeals();
|
||
updateKPI();
|
||
updateProgress();
|
||
}
|
||
|
||
function renderMeals() {
|
||
const dayPlan = weeklyPlan[currentDay] || {};
|
||
|
||
['breakfast', 'lunch', 'dinner', 'snack'].forEach(type => {
|
||
const container = document.getElementById(type + 'Grid');
|
||
const selected = dayPlan[type];
|
||
container.innerHTML = mealsDB[type].map(meal => {
|
||
const isSelected = selected && selected.id === meal.id;
|
||
return `
|
||
<article class="kt-ai-tile meal-card ${isSelected ? 'selected' : ''}" onclick="selectMeal('${type}', '${meal.id}')">
|
||
<div style="display:flex;justify-content:space-between;align-items:start">
|
||
<h3 style="margin:0">${meal.name}</h3>
|
||
${isSelected ? '<span style="color:var(--kt-ai-primary)">✓</span>' : ''}
|
||
</div>
|
||
<p style="margin-top:var(--kt-ai-space-2);font-size:var(--kt-ai-text-sm)">
|
||
${meal.kcal} ккал • Б:${meal.protein}г • Ж:${meal.fats}г • У:${meal.carbs}г
|
||
</p>
|
||
</article>
|
||
`;
|
||
}).join('');
|
||
});
|
||
}
|
||
|
||
function selectMeal(type, mealId) {
|
||
if (!weeklyPlan[currentDay]) weeklyPlan[currentDay] = {};
|
||
|
||
const meal = mealsDB[type].find(m => m.id === mealId);
|
||
const current = weeklyPlan[currentDay][type];
|
||
|
||
if (current && current.id === mealId) {
|
||
delete weeklyPlan[currentDay][type];
|
||
} else {
|
||
weeklyPlan[currentDay][type] = meal;
|
||
}
|
||
|
||
renderMeals();
|
||
updateKPI();
|
||
updateProgress();
|
||
}
|
||
|
||
function updateKPI() {
|
||
const dayPlan = weeklyPlan[currentDay] || {};
|
||
let total = { kcal: 0, protein: 0, fats: 0, carbs: 0 };
|
||
|
||
Object.values(dayPlan).forEach(meal => {
|
||
total.kcal += meal.kcal;
|
||
total.protein += meal.protein;
|
||
total.fats += meal.fats;
|
||
total.carbs += meal.carbs;
|
||
});
|
||
|
||
document.getElementById('kcalValue').textContent = total.kcal;
|
||
document.getElementById('proteinValue').textContent = total.protein + ' г';
|
||
document.getElementById('fatsValue').textContent = total.fats + ' г';
|
||
document.getElementById('carbsValue').textContent = total.carbs + ' г';
|
||
}
|
||
|
||
function updateProgress() {
|
||
const targetKcal = 2000;
|
||
document.getElementById('targetKcal').textContent = targetKcal;
|
||
|
||
let filledDays = 0;
|
||
Object.keys(weeklyPlan).forEach(day => {
|
||
const dayPlan = weeklyPlan[day];
|
||
if (dayPlan && Object.keys(dayPlan).length >= 3) {
|
||
filledDays++;
|
||
}
|
||
});
|
||
|
||
const percent = Math.round((filledDays / 7) * 100);
|
||
document.getElementById('progressPercent').textContent = percent + '%';
|
||
document.getElementById('progressFill').style.width = percent + '%';
|
||
}
|
||
|
||
function savePlan() {
|
||
localStorage.setItem('weeklyPlan', JSON.stringify(weeklyPlan));
|
||
alert('План питания сохранён!');
|
||
}
|
||
|
||
function resetPlan() {
|
||
if (confirm('Сбросить весь план питания?')) {
|
||
weeklyPlan = {};
|
||
localStorage.removeItem('weeklyPlan');
|
||
renderMeals();
|
||
updateKPI();
|
||
updateProgress();
|
||
}
|
||
}
|
||
|
||
init();
|
||
</script>
|
||
|
||
</body>
|
||
</html>
|