Files
calorie_tracker/calorie_tracker_app/templates/dashboard.html
2026-01-30 15:03:43 +08:00

301 lines
14 KiB
HTML
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.

{% extends "base.html" %}
{% block title %}Dashboard - Calorie Tracker{% endblock %}
{% block content %}
<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">{{ today.strftime('%A, %B %d, %Y') }}</p>
</div>
<div class="space-x-2">
<a href="{{ url_for('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">{{ nutrition.calories|round|int }}</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">
{% set cal_percent = (nutrition.calories / current_user.target_daily_calories * 100)|int if current_user.target_daily_calories > 0 else 0 %}
<div class="bg-primary h-3 rounded-full transition-all" style="width: {{ [cal_percent, 100]|min }}%"></div>
</div>
<p class="text-sm mt-2 {% if remaining.calories >= 0 %}text-success{% else %}text-red-600{% endif %}">
{{ remaining.calories|round|int }} 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">{{ nutrition.protein|round|int }}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">
{% set prot_percent = (nutrition.protein / goals.target_protein_g * 100)|int if goals.target_protein_g > 0 else 0 %}
<div class="bg-secondary h-3 rounded-full" style="width: {{ [prot_percent, 100]|min }}%"></div>
</div>
<p class="text-sm mt-2 {% if remaining.protein >= 0 %}text-success{% else %}text-red-600{% endif %}">
{{ remaining.protein|round|int }}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">{{ nutrition.carbs|round|int }}g / {{ goals.target_carbs_g }}g</span>
</div>
<div class="w-full bg-gray-200 rounded-full h-2 mt-1">
{% set carb_percent = (nutrition.carbs / goals.target_carbs_g * 100)|int if goals.target_carbs_g > 0 else 0 %}
<div class="bg-warning h-2 rounded-full" style="width: {{ [carb_percent, 100]|min }}%"></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">{{ nutrition.fat|round|int }}g / {{ goals.target_fat_g }}g</span>
</div>
<div class="w-full bg-gray-200 rounded-full h-2 mt-1">
{% set fat_percent = (nutrition.fat / goals.target_fat_g * 100)|int if goals.target_fat_g > 0 else 0 %}
<div class="bg-orange-600 h-2 rounded-full" style="width: {{ [fat_percent, 100]|min }}%"></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">
{% set glasses_filled = (water.total_ml / 250)|int %}
{% for i in range(8) %}
<span class="{% if i < glasses_filled %}text-blue-500{% else %}text-gray-300{% endif %}">💧</span>
{% endfor %}
</div>
<!-- Quick add water buttons -->
<form method="POST" action="{{ url_for('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 weight_today %}
<div class="text-right">
<span class="font-bold">{{ weight_today.weight_kg }}kg</span>
{% if weight_change %}
<span class="text-xs {% if weight_change < 0 %}text-success{% else %}text-red-600{% endif %}">
{{ weight_change|round(1) }}kg
</span>
{% endif %}
</div>
{% else %}
<form method="POST" action="{{ url_for('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>
{% endif %}
</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 %}
<div class="space-y-3">
{% for suggestion in suggestions %}
<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>
{% endfor %}
</div>
{% else %}
<p class="text-gray-600">You're on track! Keep up the good work! 🎉</p>
{% endif %}
</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="{{ url_for('add_meal') }}" class="text-primary hover:underline">+ Add Meal</a>
</div>
{% if nutrition.meals %}
<div class="space-y-4">
{% for meal in nutrition.meals %}
<div class="border-l-4 {% if meal.type == 'breakfast' %}border-yellow-500{% elif meal.type == 'lunch' %}border-green-500{% elif meal.type == 'dinner' %}border-blue-500{% else %}border-purple-500{% endif %} 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' %}🌅{% elif meal.type == 'lunch' %}🌞{% elif meal.type == 'dinner' %}🌙{% else %}🍪{% endif %}
</span>
<h4 class="font-bold text-lg capitalize">{{ meal.type }}</h4>
{% if meal.time %}
<span class="text-sm text-gray-500">{{ meal.time }}</span>
{% endif %}
</div>
<div class="mt-2 space-y-1">
{% for food in meal.foods %}
<p class="text-sm text-gray-700">• {{ food.name }} ({{ food.quantity }}x) - {{ food.calories|round|int }} cal</p>
{% endfor %}
</div>
</div>
<div class="text-right">
<p class="text-2xl font-bold text-primary">{{ meal.totals.calories|round|int }}</p>
<p class="text-xs text-gray-500">calories</p>
<p class="text-xs text-gray-600 mt-1">
P: {{ meal.totals.protein|round|int }}g |
C: {{ meal.totals.carbs|round|int }}g |
F: {{ meal.totals.fat|round|int }}g
</p>
</div>
</div>
</div>
{% endfor %}
</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="{{ url_for('add_meal') }}" class="text-primary hover:underline mt-2 inline-block">Add your first meal</a>
</div>
{% endif %}
</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: [{% for day in calorie_trend %}'{{ day.date }}'{% if not loop.last %}, {% endif %}{% endfor %}],
datasets: [{
label: 'Calories',
data: [{% for day in calorie_trend %}{{ day.calories }}{% if not loop.last %}, {% endif %}{% endfor %}],
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 %}
const weightCtx = document.getElementById('weightChart').getContext('2d');
new Chart(weightCtx, {
type: 'line',
data: {
labels: [{% for day in weight_trend %}'{{ day.date }}'{% if not loop.last %}, {% endif %}{% endfor %}],
datasets: [{
label: 'Weight (kg)',
data: [{% for day in weight_trend %}{{ day.weight_kg }}{% if not loop.last %}, {% endif %}{% endfor %}],
borderColor: '#003F87',
backgroundColor: 'rgba(0, 63, 135, 0.1)',
tension: 0.4,
fill: true
}]
},
options: {
responsive: true,
maintainAspectRatio: true,
plugins: {
legend: {
display: false
}
}
}
});
{% endif %}
</script>
{% endblock %}