172 lines
6.5 KiB
HTML
172 lines
6.5 KiB
HTML
{% extends "base.html" %}
|
|
{% block title %}Dashboard — WeightTracker{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="page-header">
|
|
<h1>Hey, {{ user.display_name or user.username }}! 👋</h1>
|
|
<p>Here's your progress at a glance.</p>
|
|
</div>
|
|
|
|
<!-- Stats Cards -->
|
|
<div class="stats-grid">
|
|
<div class="stat-card">
|
|
<div class="stat-label">Current Weight</div>
|
|
<div class="stat-value">{{ '%.1f' % (latest.weight_kg | float) if latest else '—' }} <span
|
|
style="font-size: 0.9rem; font-weight: 400; color: var(--text-muted);">kg</span></div>
|
|
</div>
|
|
<div class="stat-card">
|
|
<div class="stat-label">Weight Change</div>
|
|
<div class="stat-value">
|
|
{% if weight_change is not none %}
|
|
{{ '%+.1f' % weight_change }} <span
|
|
style="font-size: 0.9rem; font-weight: 400; color: var(--text-muted);">kg</span>
|
|
{% else %}
|
|
—
|
|
{% endif %}
|
|
</div>
|
|
{% if weight_change_pct is not none %}
|
|
<div class="stat-change {{ 'positive' if weight_change < 0 else 'negative' }}">
|
|
{{ '%+.1f' % weight_change_pct }}% from start
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
<div class="stat-card">
|
|
<div class="stat-label">Current BMI</div>
|
|
<div class="stat-value">{{ '%.1f' % (latest.bmi | float) if latest and latest.bmi else '—' }}</div>
|
|
</div>
|
|
<div class="stat-card">
|
|
<div class="stat-label">Check-ins</div>
|
|
<div class="stat-value">{{ stats.total_checkins if stats else 0 }}</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Charts -->
|
|
<div class="charts-grid">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h2>📈 Weight Over Time</h2>
|
|
</div>
|
|
<div class="chart-container">
|
|
<canvas id="weightChart"></canvas>
|
|
</div>
|
|
</div>
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h2>📊 Weekly Change</h2>
|
|
</div>
|
|
<div class="chart-container">
|
|
<canvas id="weeklyChart"></canvas>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="grid-3">
|
|
<!-- Recent Check-ins -->
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h2>🕐 Recent Check-ins</h2>
|
|
<a href="{{ url_for('checkin.index') }}" class="btn btn-ghost btn-sm">View All</a>
|
|
</div>
|
|
{% if recent_checkins %}
|
|
<div>
|
|
{% for c in recent_checkins %}
|
|
<div class="activity-item">
|
|
<div style="flex: 1;">
|
|
<div style="font-weight: 600; font-size: 1.1rem;">{{ '%.1f' % (c.weight_kg | float) }} kg</div>
|
|
<div class="activity-detail">
|
|
{% if c.bmi %}BMI {{ '%.1f' % (c.bmi | float) }} · {% endif %}
|
|
{{ c.checked_in_at | sydney }}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
{% else %}
|
|
<div class="empty-state">
|
|
<div class="empty-state-icon">📝</div>
|
|
<h3>No check-ins yet</h3>
|
|
<p>Log your first weigh-in to start tracking!</p>
|
|
<a href="{{ url_for('checkin.index') }}" class="btn btn-primary btn-sm">Check In Now</a>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<!-- Activity Feed & Milestones -->
|
|
<div>
|
|
{% if milestones %}
|
|
<div class="card" style="margin-bottom: 1rem;">
|
|
<div class="card-header">
|
|
<h2>🏅 Milestones</h2>
|
|
</div>
|
|
<div class="milestones-grid">
|
|
{% for m in milestones %}
|
|
<span class="milestone-badge {{ 'gold' if 'lost' in m.milestone_key else '' }}">
|
|
{% if m.milestone_key == 'first_checkin' %}✅ First Check-in
|
|
{% elif m.milestone_key == '5_checkins' %}🔥 5 Check-ins
|
|
{% elif m.milestone_key == '10_checkins' %}💪 10 Check-ins
|
|
{% elif m.milestone_key == '25_checkins' %}🎯 25 Check-ins
|
|
{% elif m.milestone_key == 'lost_1kg' %}⭐ 1kg Lost
|
|
{% elif m.milestone_key == 'lost_2kg' %}⭐ 2kg Lost
|
|
{% elif m.milestone_key == 'lost_5kg' %}🌟 5kg Lost
|
|
{% elif m.milestone_key == 'lost_10kg' %}💎 10kg Lost
|
|
{% elif m.milestone_key == 'lost_15kg' %}👑 15kg Lost
|
|
{% elif m.milestone_key == 'lost_20kg' %}🏆 20kg Lost
|
|
{% else %}{{ m.milestone_key }}
|
|
{% endif %}
|
|
</span>
|
|
{% endfor %}
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h2>🔔 Activity Feed</h2>
|
|
</div>
|
|
{% if activity %}
|
|
<div>
|
|
{% for a in activity %}
|
|
<div class="activity-item">
|
|
<div class="activity-avatar">{{ (a.display_name or a.username)[:1] | upper }}</div>
|
|
<div class="activity-content">
|
|
<div class="activity-name">{{ a.display_name or a.username }}</div>
|
|
<div class="activity-detail">Logged {{ '%.1f' % (a.weight_kg | float) }} kg · {{
|
|
a.checked_in_at | sydney('%d %b, %H:%M') }}</div>
|
|
</div>
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
{% else %}
|
|
<div class="empty-state">
|
|
<div class="empty-state-icon">📢</div>
|
|
<p>No activity yet. Be the first to check in!</p>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{% endblock %}
|
|
|
|
{% block scripts %}
|
|
<script src="{{ url_for('static', filename='js/charts.js') }}"></script>
|
|
<script>
|
|
document.addEventListener('DOMContentLoaded', function () {
|
|
fetch('/api/chart-data/{{ user.id }}')
|
|
.then(r => r.json())
|
|
.then(data => {
|
|
if (data.labels.length > 0) {
|
|
createWeightChart('weightChart', data.labels, data.weights);
|
|
}
|
|
});
|
|
|
|
fetch('/api/weekly-change/{{ user.id }}')
|
|
.then(r => r.json())
|
|
.then(data => {
|
|
if (data.labels.length > 0) {
|
|
createWeeklyChangeChart('weeklyChart', data.labels, data.changes);
|
|
}
|
|
});
|
|
});
|
|
</script>
|
|
{% endblock %} |