Files
calorie_tracker_2/views/dashboard.ejs
2026-01-30 23:32:43 +08:00

295 lines
14 KiB
Plaintext
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.

<div class="max-w-7xl mx-auto">
<!-- Header -->
<div class="flex justify-between items-center mb-6">
<div>
<h1 class="text-3xl font-bold text-gray-800">Today's Summary</h1>
<p class="text-gray-600"><%= new Date().toLocaleDateString('en-US', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' }) %></p>
</div>
<div class="space-x-2">
<a href="/add-meal" class="bg-primary text-white px-6 py-3 rounded-lg hover:bg-red-700 transition inline-block">
Add Meal
</a>
</div>
</div>
<!-- Top Cards Row -->
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-6">
<!-- Calories Card -->
<div class="glass rounded-xl p-6 shadow-lg">
<div class="flex justify-between items-start mb-4">
<div>
<p class="text-gray-600 text-sm">Calories</p>
<h2 class="text-3xl font-bold text-primary"><%= Math.round(nutrition.calories) %></h2>
<p class="text-sm text-gray-500">/ <%= current_user.target_daily_calories %></p>
</div>
<span class="text-4xl">🔥</span>
</div>
<!-- Progress Bar -->
<div class="w-full bg-gray-200 rounded-full h-3">
<% let cal_percent = current_user.target_daily_calories > 0 ? Math.round(nutrition.calories / current_user.target_daily_calories * 100) : 0; %>
<div class="bg-primary h-3 rounded-full transition-all" style="width: <%= Math.min(cal_percent, 100) %>%"></div>
</div>
<p class="text-sm mt-2 <%= remaining.calories >= 0 ? 'text-success' : 'text-red-600' %>">
<%= Math.round(remaining.calories) %> remaining
</p>
</div>
<!-- Protein Card -->
<div class="glass rounded-xl p-6 shadow-lg">
<div class="flex justify-between items-start mb-4">
<div>
<p class="text-gray-600 text-sm">Protein</p>
<h2 class="text-3xl font-bold text-secondary"><%= Math.round(nutrition.protein) %>g</h2>
<p class="text-sm text-gray-500">/ <%= goals.target_protein_g %>g</p>
</div>
<span class="text-4xl">💪</span>
</div>
<div class="w-full bg-gray-200 rounded-full h-3">
<% let prot_percent = goals.target_protein_g > 0 ? Math.round(nutrition.protein / goals.target_protein_g * 100) : 0; %>
<div class="bg-secondary h-3 rounded-full" style="width: <%= Math.min(prot_percent, 100) %>%"></div>
</div>
<p class="text-sm mt-2 <%= remaining.protein >= 0 ? 'text-success' : 'text-red-600' %>">
<%= Math.round(remaining.protein) %>g remaining
</p>
</div>
<!-- Carbs & Fat Card -->
<div class="glass rounded-xl p-6 shadow-lg">
<div class="mb-3">
<div class="flex justify-between items-center">
<span class="text-gray-600 text-sm">Carbs</span>
<span class="font-bold text-warning"><%= Math.round(nutrition.carbs) %>g / <%= goals.target_carbs_g %>g</span>
</div>
<div class="w-full bg-gray-200 rounded-full h-2 mt-1">
<% let carb_percent = goals.target_carbs_g > 0 ? Math.round(nutrition.carbs / goals.target_carbs_g * 100) : 0; %>
<div class="bg-warning h-2 rounded-full" style="width: <%= Math.min(carb_percent, 100) %>%"></div>
</div>
</div>
<div>
<div class="flex justify-between items-center">
<span class="text-gray-600 text-sm">Fat</span>
<span class="font-bold text-orange-600"><%= Math.round(nutrition.fat) %>g / <%= goals.target_fat_g %>g</span>
</div>
<div class="w-full bg-gray-200 rounded-full h-2 mt-1">
<% let fat_percent = goals.target_fat_g > 0 ? Math.round(nutrition.fat / goals.target_fat_g * 100) : 0; %>
<div class="bg-orange-600 h-2 rounded-full" style="width: <%= Math.min(fat_percent, 100) %>%"></div>
</div>
</div>
</div>
<!-- Water & Weight Card -->
<div class="glass rounded-xl p-6 shadow-lg">
<div class="mb-3">
<div class="flex justify-between items-center">
<span class="text-gray-600 text-sm">Water</span>
<span class="font-bold text-blue-600"><%= water.total_ml %>ml / <%= goals.target_water_ml %>ml</span>
</div>
<div class="flex space-x-1 mt-2">
<% let glasses_filled = Math.floor(water.total_ml / 250); %>
<% for (let i = 0; i < 8; i++) { %>
<span class="<%= i < glasses_filled ? 'text-blue-500' : 'text-gray-300' %>">💧</span>
<% } %>
</div>
<!-- Quick add water buttons -->
<form method="POST" action="/add-water" class="flex space-x-1 mt-2">
<button type="submit" name="amount_ml" value="250" class="bg-blue-100 text-blue-700 px-2 py-1 rounded text-xs hover:bg-blue-200">+250ml</button>
<button type="submit" name="amount_ml" value="500" class="bg-blue-100 text-blue-700 px-2 py-1 rounded text-xs hover:bg-blue-200">+500ml</button>
</form>
</div>
<div class="pt-3 border-t">
<div class="flex justify-between items-center">
<span class="text-gray-600 text-sm">Weight</span>
<% if (typeof weight_today !== 'undefined' && weight_today) { %>
<div class="text-right">
<span class="font-bold"><%= weight_today.weight_kg %>kg</span>
<% if (typeof weight_change !== 'undefined' && weight_change !== null) { %>
<span class="text-xs <%= weight_change < 0 ? 'text-success' : 'text-red-600' %>">
<%= weight_change.toFixed(1) %>kg
</span>
<% } %>
</div>
<% } else { %>
<form method="POST" action="/add-weight" class="flex items-center space-x-2">
<input type="number" step="0.1" name="weight_kg" placeholder="kg" class="w-20 px-2 py-1 border rounded text-sm" required>
<button type="submit" class="bg-green-500 text-white px-2 py-1 rounded text-xs hover:bg-green-600">Log</button>
</form>
<% } %>
</div>
</div>
</div>
</div>
<!-- Macro Distribution Pie Chart -->
<div class="grid grid-cols-1 lg:grid-cols-3 gap-6 mb-6">
<div class="glass rounded-xl p-6 shadow-lg">
<h3 class="text-lg font-bold mb-4">Macro Distribution</h3>
<canvas id="macroChart" height="200"></canvas>
</div>
<!-- Suggestions -->
<div class="glass rounded-xl p-6 shadow-lg lg:col-span-2">
<h3 class="text-lg font-bold mb-4">💡 Smart Suggestions</h3>
<% if (suggestions && suggestions.length > 0) { %>
<div class="space-y-3">
<% suggestions.forEach(function(suggestion) { %>
<div class="bg-blue-50 border-l-4 border-blue-500 p-3 rounded">
<p class="font-semibold text-blue-900"><%= suggestion.category %></p>
<p class="text-sm text-blue-700">Try: <%= suggestion.examples.join(', ') %></p>
</div>
<% }); %>
</div>
<% } else { %>
<p class="text-gray-600">You're on track! Keep up the good work! 🎉</p>
<% } %>
</div>
</div>
<!-- Today's Meals -->
<div class="glass rounded-xl p-6 shadow-lg mb-6">
<div class="flex justify-between items-center mb-4">
<h3 class="text-xl font-bold">Today's Meals</h3>
<a href="/add-meal" class="text-primary hover:underline">+ Add Meal</a>
</div>
<% if (nutrition.meals && nutrition.meals.length > 0) { %>
<div class="space-y-4">
<% nutrition.meals.forEach(function(meal) { %>
<div class="border-l-4 <%= meal.type == 'breakfast' ? 'border-yellow-500' : (meal.type == 'lunch' ? 'border-green-500' : (meal.type == 'dinner' ? 'border-blue-500' : 'border-purple-500')) %> pl-4 py-2">
<div class="flex justify-between items-start">
<div class="flex-1">
<div class="flex items-center space-x-2">
<span class="text-2xl">
<% if (meal.type == 'breakfast') { %>🌅<% } else if (meal.type == 'lunch') { %>🌞<% } else if (meal.type == 'dinner') { %>🌙<% } else { %>🍪<% } %>
</span>
<h4 class="font-bold text-lg capitalize"><%= meal.type %></h4>
<% if (meal.time) { %>
<span class="text-sm text-gray-500"><%= meal.time %></span>
<% } %>
</div>
<div class="mt-2 space-y-1">
<% meal.foods.forEach(function(food) { %>
<p class="text-sm text-gray-700">• <%= food.name %> (<%= food.quantity %>x) - <%= Math.round(food.calories) %> cal</p>
<% }); %>
</div>
</div>
<div class="text-right">
<p class="text-2xl font-bold text-primary"><%= Math.round(meal.totals.calories) %></p>
<p class="text-xs text-gray-500">calories</p>
<p class="text-xs text-gray-600 mt-1">
P: <%= Math.round(meal.totals.protein) %>g |
C: <%= Math.round(meal.totals.carbs) %>g |
F: <%= Math.round(meal.totals.fat) %>g
</p>
</div>
</div>
</div>
<% }); %>
</div>
<% } else { %>
<div class="text-center py-12 text-gray-500">
<p class="text-4xl mb-4">🍽️</p>
<p>No meals logged yet today.</p>
<a href="/add-meal" class="text-primary hover:underline mt-2 inline-block">Add your first meal</a>
</div>
<% } %>
</div>
<!-- Weekly Trends -->
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
<!-- Calorie Trend -->
<div class="glass rounded-xl p-6 shadow-lg">
<h3 class="text-lg font-bold mb-4">📈 Calorie Trend (7 Days)</h3>
<canvas id="calorieChart" height="200"></canvas>
</div>
<!-- Weight Trend -->
<div class="glass rounded-xl p-6 shadow-lg">
<h3 class="text-lg font-bold mb-4">⚖️ Weight Trend (7 Days)</h3>
<canvas id="weightChart" height="200"></canvas>
</div>
</div>
</div>
<script>
// Macro Distribution Chart
const macroCtx = document.getElementById('macroChart').getContext('2d');
new Chart(macroCtx, {
type: 'doughnut',
data: {
labels: ['Protein', 'Carbs', 'Fat'],
datasets: [{
data: [<%= macro_percentages.protein %>, <%= macro_percentages.carbs %>, <%= macro_percentages.fat %>],
backgroundColor: ['#003F87', '#FFB703', '#FF6B35']
}]
},
options: {
responsive: true,
maintainAspectRatio: true,
plugins: {
legend: {
position: 'bottom'
}
}
}
});
// Calorie Trend Chart
const calorieCtx = document.getElementById('calorieChart').getContext('2d');
new Chart(calorieCtx, {
type: 'line',
data: {
labels: <%- JSON.stringify(calorie_trend.map(d => d.date)) %>,
datasets: [{
label: 'Calories',
data: <%- JSON.stringify(calorie_trend.map(d => d.calories)) %>,
borderColor: '#D62828',
backgroundColor: 'rgba(214, 40, 40, 0.1)',
tension: 0.4
}, {
label: 'Target',
data: Array(7).fill(<%= current_user.target_daily_calories %>),
borderColor: '#06D6A0',
borderDash: [5, 5],
pointRadius: 0
}]
},
options: {
responsive: true,
maintainAspectRatio: true,
plugins: {
legend: {
display: true
}
}
}
});
// Weight Trend Chart
<% if (weight_trend && weight_trend.length > 0) { %>
const weightCtx = document.getElementById('weightChart').getContext('2d');
new Chart(weightCtx, {
type: 'line',
data: {
labels: <%- JSON.stringify(weight_trend.map(d => d.date)) %>,
datasets: [{
label: 'Weight (kg)',
data: <%- JSON.stringify(weight_trend.map(d => d.weight_kg)) %>,
borderColor: '#003F87',
backgroundColor: 'rgba(0, 63, 135, 0.1)',
tension: 0.4,
fill: true
}]
},
options: {
responsive: true,
maintainAspectRatio: true,
plugins: {
legend: {
display: false
}
}
}
});
<% } %>
</script>