initial commit
This commit is contained in:
294
views/dashboard.ejs
Normal file
294
views/dashboard.ejs
Normal file
@@ -0,0 +1,294 @@
|
||||
<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>
|
||||
Reference in New Issue
Block a user