first commit

This commit is contained in:
Jp
2026-01-30 15:03:43 +08:00
commit 656a510c73
32 changed files with 3721 additions and 0 deletions

View File

@@ -0,0 +1,300 @@
{% 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 %}