200 lines
6.5 KiB
JavaScript
200 lines
6.5 KiB
JavaScript
let profile = null;
|
||
let entries = [];
|
||
|
||
document.addEventListener('DOMContentLoaded', () => {
|
||
document.getElementById('entryDate').valueAsDate = new Date();
|
||
document.getElementById('filterDate').valueAsDate = new Date();
|
||
loadProfile();
|
||
loadEntries();
|
||
});
|
||
|
||
document.getElementById('profileForm').addEventListener('submit', async (e) => {
|
||
e.preventDefault();
|
||
|
||
const gender = document.getElementById('gender').value;
|
||
const age = parseInt(document.getElementById('age').value);
|
||
const height = parseInt(document.getElementById('height').value);
|
||
const weight = parseInt(document.getElementById('weight').value);
|
||
const activity = parseFloat(document.getElementById('activity').value);
|
||
const goal = document.getElementById('goal').value;
|
||
|
||
let bmr;
|
||
if (gender === 'male') {
|
||
bmr = 88.362 + (13.397 * weight) + (4.799 * height) - (5.677 * age);
|
||
} else {
|
||
bmr = 447.593 + (9.247 * weight) + (3.098 * height) - (4.330 * age);
|
||
}
|
||
|
||
const tdee = Math.round(bmr * activity);
|
||
let goalCalories;
|
||
if (goal === 'lose') {
|
||
goalCalories = Math.round(tdee * 0.8);
|
||
} else if (goal === 'gain') {
|
||
goalCalories = Math.round(tdee * 1.15);
|
||
} else {
|
||
goalCalories = tdee;
|
||
}
|
||
|
||
profile = { gender, age, height, weight, activity, goal, bmr: Math.round(bmr), tdee, goalCalories };
|
||
|
||
await fetch('/api/profile', {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify(profile)
|
||
});
|
||
|
||
document.getElementById('bmrValue').textContent = Math.round(bmr) + ' ккал';
|
||
document.getElementById('tdeeValue').textContent = tdee + ' ккал';
|
||
document.getElementById('goalValue').textContent = goalCalories + ' ккал';
|
||
document.getElementById('result').style.display = 'block';
|
||
document.getElementById('goalCalories').textContent = goalCalories + ' ккал';
|
||
|
||
updateStats();
|
||
});
|
||
|
||
document.getElementById('entryForm').addEventListener('submit', async (e) => {
|
||
e.preventDefault();
|
||
|
||
const entry = {
|
||
foodName: document.getElementById('foodName').value,
|
||
calories: parseInt(document.getElementById('calories').value),
|
||
protein: parseFloat(document.getElementById('protein').value) || 0,
|
||
fat: parseFloat(document.getElementById('fat').value) || 0,
|
||
carbs: parseFloat(document.getElementById('carbs').value) || 0,
|
||
date: document.getElementById('entryDate').value
|
||
};
|
||
|
||
await fetch('/api/entries', {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify(entry)
|
||
});
|
||
|
||
document.getElementById('foodName').value = '';
|
||
document.getElementById('calories').value = '';
|
||
document.getElementById('protein').value = '';
|
||
document.getElementById('fat').value = '';
|
||
document.getElementById('carbs').value = '';
|
||
|
||
loadEntries();
|
||
});
|
||
|
||
async function loadProfile() {
|
||
const res = await fetch('/api/profile');
|
||
profile = await res.json();
|
||
if (profile) {
|
||
document.getElementById('gender').value = profile.gender;
|
||
document.getElementById('age').value = profile.age;
|
||
document.getElementById('height').value = profile.height;
|
||
document.getElementById('weight').value = profile.weight;
|
||
document.getElementById('activity').value = profile.activity;
|
||
document.getElementById('goal').value = profile.goal;
|
||
document.getElementById('goalCalories').textContent = profile.goalCalories + ' ккал';
|
||
updateStats();
|
||
}
|
||
}
|
||
|
||
async function loadEntries() {
|
||
const res = await fetch('/api/entries');
|
||
entries = await res.json();
|
||
renderEntries(entries);
|
||
updateStats();
|
||
}
|
||
|
||
function renderEntries(entriesToRender) {
|
||
const container = document.getElementById('entriesList');
|
||
|
||
if (entriesToRender.length === 0) {
|
||
container.innerHTML = '<div class="empty-state">Пока нет записей. Добавь первую!</div>';
|
||
return;
|
||
}
|
||
|
||
const sorted = [...entriesToRender].sort((a, b) => new Date(b.date) - new Date(a.date));
|
||
|
||
container.innerHTML = sorted.map(entry => `
|
||
<div class="entry-item">
|
||
<div class="entry-info">
|
||
<div class="entry-name">${escapeHtml(entry.foodName)}</div>
|
||
<div class="entry-details">${formatDate(entry.date)}</div>
|
||
<div class="entry-macros">Б: ${entry.protein}г | Ж: ${entry.fat}г | У: ${entry.carbs}г</div>
|
||
</div>
|
||
<span class="entry-calories">${entry.calories} ккал</span>
|
||
<span class="entry-date">${formatDate(entry.date)}</span>
|
||
<button onclick="deleteEntry(${entry.id})" class="btn btn-danger">Удалить</button>
|
||
</div>
|
||
`).join('');
|
||
}
|
||
|
||
function deleteEntry(id) {
|
||
if (confirm('Удалить эту запись?')) {
|
||
fetch('/api/entries', {
|
||
method: 'DELETE',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({ id })
|
||
}).then(loadEntries);
|
||
}
|
||
}
|
||
|
||
function filterByDate() {
|
||
const date = document.getElementById('filterDate').value;
|
||
const filtered = entries.filter(e => e.date === date);
|
||
renderEntries(filtered);
|
||
}
|
||
|
||
function clearFilter() {
|
||
renderEntries(entries);
|
||
}
|
||
|
||
function updateStats() {
|
||
if (!profile) return;
|
||
|
||
const today = new Date().toISOString().split('T')[0];
|
||
const todayEntries = entries.filter(e => e.date === today);
|
||
const todayCalories = todayEntries.reduce((sum, e) => sum + e.calories, 0);
|
||
|
||
document.getElementById('todayCalories').textContent = todayCalories + ' ккал';
|
||
document.getElementById('goalCalories').textContent = profile.goalCalories + ' ккал';
|
||
|
||
const remaining = profile.goalCalories - todayCalories;
|
||
const remainingEl = document.getElementById('remainingCalories');
|
||
remainingEl.textContent = remaining + ' ккал';
|
||
remainingEl.style.color = remaining >= 0 ? '#10b981' : '#ef4444';
|
||
}
|
||
|
||
function exportCsv() {
|
||
if (entries.length === 0) {
|
||
alert('Нет записей для экспорта');
|
||
return;
|
||
}
|
||
|
||
const head = ['Дата', 'Продукт', 'Ккал', 'Белки (г)', 'Жиры (г)', 'Углеводы (г)'];
|
||
const body = entries.map(e => [
|
||
e.date,
|
||
'"' + e.foodName.replace(/"/g, '""') + '"',
|
||
e.calories,
|
||
e.protein,
|
||
e.fat,
|
||
e.carbs
|
||
].join(';'));
|
||
|
||
const csv = '\uFEFF' + [head.join(';'), ...body].join('\n');
|
||
const blob = new Blob([csv], { type: 'text/csv;charset=utf-8;' });
|
||
const url = URL.createObjectURL(blob);
|
||
const a = document.createElement('a');
|
||
a.href = url;
|
||
a.download = 'dnevnik_pitaniya.csv';
|
||
a.click();
|
||
URL.revokeObjectURL(url);
|
||
}
|
||
|
||
function escapeHtml(text) {
|
||
const div = document.createElement('div');
|
||
div.textContent = text;
|
||
return div.innerHTML;
|
||
}
|
||
|
||
function formatDate(dateStr) {
|
||
const [year, month, day] = dateStr.split('-');
|
||
return `${day}.${month}.${year}`;
|
||
}
|