Initial commit
This commit is contained in:
70
app/templates/base.html
Normal file
70
app/templates/base.html
Normal file
@@ -0,0 +1,70 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{% block title %}WeightTracker{% endblock %}</title>
|
||||
<meta name="description" content="Track your weight loss competition with friends">
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
|
||||
<script src="https://unpkg.com/htmx.org@2.0.4"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.7/dist/chart.umd.min.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
{% if session.get('user_id') %}
|
||||
<nav class="navbar">
|
||||
<div class="nav-brand">
|
||||
<a href="{{ url_for('dashboard.index') }}">
|
||||
<span class="nav-icon">⚖️</span>
|
||||
<span class="nav-title">WeightTracker</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="nav-links">
|
||||
<a href="{{ url_for('dashboard.index') }}" class="{{ 'active' if request.endpoint == 'dashboard.index' }}">
|
||||
<span class="nav-link-icon">📊</span>
|
||||
<span>Dashboard</span>
|
||||
</a>
|
||||
<a href="{{ url_for('checkin.index') }}" class="{{ 'active' if request.endpoint and request.endpoint.startswith('checkin') }}">
|
||||
<span class="nav-link-icon">✏️</span>
|
||||
<span>Check-in</span>
|
||||
</a>
|
||||
<a href="{{ url_for('leaderboard.index') }}" class="{{ 'active' if request.endpoint == 'leaderboard.index' }}">
|
||||
<span class="nav-link-icon">🏆</span>
|
||||
<span>Leaderboard</span>
|
||||
</a>
|
||||
<a href="{{ url_for('profile.index') }}" class="{{ 'active' if request.endpoint and request.endpoint.startswith('profile') }}">
|
||||
<span class="nav-link-icon">👤</span>
|
||||
<span>Profile</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="nav-actions">
|
||||
<a href="{{ url_for('auth.logout') }}" class="btn btn-ghost btn-sm">Logout</a>
|
||||
</div>
|
||||
<button class="nav-toggle" onclick="document.querySelector('.nav-links').classList.toggle('open')">
|
||||
<span></span><span></span><span></span>
|
||||
</button>
|
||||
</nav>
|
||||
{% endif %}
|
||||
|
||||
<main class="container">
|
||||
{% with messages = get_flashed_messages(with_categories=true) %}
|
||||
{% if messages %}
|
||||
<div class="flash-messages">
|
||||
{% for category, message in messages %}
|
||||
<div class="flash flash-{{ category }}">
|
||||
{{ message }}
|
||||
<button class="flash-close" onclick="this.parentElement.remove()">×</button>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
|
||||
{% block content %}{% endblock %}
|
||||
</main>
|
||||
|
||||
{% block scripts %}{% endblock %}
|
||||
</body>
|
||||
</html>
|
||||
64
app/templates/checkin.html
Normal file
64
app/templates/checkin.html
Normal file
@@ -0,0 +1,64 @@
|
||||
{% extends "base.html" %}
|
||||
{% block title %}Check-in — WeightTracker{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="page-header">
|
||||
<h1>✏️ Check-in</h1>
|
||||
<p>Record your weight. Keep it consistent!</p>
|
||||
</div>
|
||||
|
||||
<!-- Check-in Form -->
|
||||
<div class="card" style="margin-bottom: 1.5rem;">
|
||||
<form hx-post="{{ url_for('checkin.create') }}" hx-target="#checkin-list" hx-swap="afterbegin"
|
||||
hx-on::after-request="this.reset()">
|
||||
<div class="form-inline">
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="weight_kg">Weight (kg)</label>
|
||||
<input class="form-input" type="number" id="weight_kg" name="weight_kg" step="0.1"
|
||||
placeholder="e.g. 78.5" required autofocus>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="notes">Notes (optional)</label>
|
||||
<input class="form-input" type="text" id="notes" name="notes" placeholder="How are you feeling?">
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary">Log Weight</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<!-- Check-in History -->
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h2>📋 History</h2>
|
||||
<span class="badge">{{ checkins | length }} entries</span>
|
||||
</div>
|
||||
|
||||
{% if checkins %}
|
||||
<div class="table-wrap">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Date</th>
|
||||
<th>Weight</th>
|
||||
<th>BMI</th>
|
||||
<th>Notes</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="checkin-list">
|
||||
{% for c in checkins %}
|
||||
{% include "partials/checkin_row.html" %}
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% else %}
|
||||
<div id="checkin-list"></div>
|
||||
<div class="empty-state">
|
||||
<div class="empty-state-icon">⚖️</div>
|
||||
<h3>No check-ins yet</h3>
|
||||
<p>Enter your weight above to start tracking.</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
172
app/templates/dashboard.html
Normal file
172
app/templates/dashboard.html
Normal file
@@ -0,0 +1,172 @@
|
||||
{% 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.strftime('%d %b %Y, %H:%M') }}
|
||||
</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.strftime('%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 %}
|
||||
103
app/templates/leaderboard.html
Normal file
103
app/templates/leaderboard.html
Normal file
@@ -0,0 +1,103 @@
|
||||
{% extends "base.html" %}
|
||||
{% block title %}Leaderboard — WeightTracker{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="page-header">
|
||||
<h1>🏆 Leaderboard</h1>
|
||||
<p>Ranked by % body weight lost. May the best loser win!</p>
|
||||
</div>
|
||||
|
||||
<!-- Comparison Chart -->
|
||||
<div class="card" style="margin-bottom: 1.5rem;">
|
||||
<div class="card-header">
|
||||
<h2>📊 % Weight Lost Comparison</h2>
|
||||
</div>
|
||||
<div class="chart-container">
|
||||
<canvas id="comparisonChart"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Leaderboard Table -->
|
||||
<div class="card">
|
||||
{% if ranked %}
|
||||
<div class="table-wrap">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Rank</th>
|
||||
<th>Name</th>
|
||||
<th>Start</th>
|
||||
<th>Current</th>
|
||||
<th>Lost (kg)</th>
|
||||
<th>Lost (%)</th>
|
||||
<th>Goal Progress</th>
|
||||
<th>Check-ins</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for u in ranked %}
|
||||
<tr>
|
||||
<td>
|
||||
<span
|
||||
class="rank-badge {{ 'rank-1' if loop.index == 1 else 'rank-2' if loop.index == 2 else 'rank-3' if loop.index == 3 else 'rank-other' }}">
|
||||
{{ loop.index }}
|
||||
</span>
|
||||
</td>
|
||||
<td style="font-weight: 600; color: var(--text-primary);">{{ u.display_name or u.username }}</td>
|
||||
<td>{{ '%.1f' % (u.starting_weight_kg | float) if u.starting_weight_kg else '—' }}</td>
|
||||
<td>{{ '%.1f' % (u.current_weight | float) if u.current_weight else '—' }}</td>
|
||||
<td>
|
||||
<span
|
||||
style="color: {{ 'var(--success)' if u.weight_lost > 0 else 'var(--danger)' if u.weight_lost < 0 else 'var(--text-muted)' }}; font-weight: 600;">
|
||||
{{ '%+.1f' % (-u.weight_lost) if u.weight_lost != 0 else '0.0' }}
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<span
|
||||
style="color: {{ 'var(--success)' if u.pct_lost > 0 else 'var(--danger)' if u.pct_lost < 0 else 'var(--text-muted)' }}; font-weight: 700;">
|
||||
{{ '%.1f' % u.pct_lost }}%
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
{% if u.goal_progress is not none %}
|
||||
<div style="display: flex; align-items: center; gap: 0.5rem;">
|
||||
<div class="progress-bar-track" style="flex: 1;">
|
||||
<div class="progress-bar-fill" style="width: {{ u.goal_progress }}%;"></div>
|
||||
</div>
|
||||
<span style="font-size: 0.75rem; color: var(--text-muted); white-space: nowrap;">{{ '%.0f' %
|
||||
u.goal_progress }}%</span>
|
||||
</div>
|
||||
{% else %}
|
||||
<span style="color: var(--text-muted);">—</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>{{ u.total_checkins }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="empty-state">
|
||||
<div class="empty-state-icon">🏆</div>
|
||||
<h3>No competitors yet</h3>
|
||||
<p>Start checking in to appear on the leaderboard!</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block scripts %}
|
||||
<script src="{{ url_for('static', filename='js/charts.js') }}"></script>
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
fetch('/api/comparison')
|
||||
.then(r => r.json())
|
||||
.then(data => {
|
||||
if (data.names.length > 0) {
|
||||
createComparisonChart('comparisonChart', data.names, data.pct_lost);
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
29
app/templates/login.html
Normal file
29
app/templates/login.html
Normal file
@@ -0,0 +1,29 @@
|
||||
{% extends "base.html" %}
|
||||
{% block title %}Log In — WeightTracker{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="auth-page">
|
||||
<div class="auth-card">
|
||||
<h1>⚖️ WeightTracker</h1>
|
||||
<p class="auth-subtitle">Welcome back. Let's check your progress.</p>
|
||||
|
||||
<form method="POST" action="{{ url_for('auth.login') }}">
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="username">Username</label>
|
||||
<input class="form-input" type="text" id="username" name="username" placeholder="Your username" required
|
||||
autofocus>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="password">Password</label>
|
||||
<input class="form-input" type="password" id="password" name="password" placeholder="Your password"
|
||||
required>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-primary btn-block btn-lg" style="margin-top: 0.5rem;">Log In</button>
|
||||
</form>
|
||||
|
||||
<p class="auth-footer">Don't have an account? <a href="{{ url_for('auth.signup') }}">Sign up</a></p>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
10
app/templates/partials/checkin_row.html
Normal file
10
app/templates/partials/checkin_row.html
Normal file
@@ -0,0 +1,10 @@
|
||||
<tr id="checkin-{{ c.id }}">
|
||||
<td>{{ c.checked_in_at.strftime('%d %b %Y, %H:%M') }}</td>
|
||||
<td style="font-weight: 600;">{{ '%.1f' % (c.weight_kg | float) }} kg</td>
|
||||
<td>{{ '%.1f' % (c.bmi | float) if c.bmi else '—' }}</td>
|
||||
<td>{{ c.notes or '—' }}</td>
|
||||
<td>
|
||||
<button class="btn-icon" hx-delete="/checkin/{{ c.id }}" hx-target="#checkin-{{ c.id }}" hx-swap="outerHTML"
|
||||
hx-confirm="Delete this check-in?">🗑️</button>
|
||||
</td>
|
||||
</tr>
|
||||
56
app/templates/profile.html
Normal file
56
app/templates/profile.html
Normal file
@@ -0,0 +1,56 @@
|
||||
{% extends "base.html" %}
|
||||
{% block title %}Profile — WeightTracker{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="page-header">
|
||||
<h1>👤 Profile</h1>
|
||||
<p>Update your stats and goals.</p>
|
||||
</div>
|
||||
|
||||
<div class="card" style="max-width: 600px;">
|
||||
<form method="POST" action="{{ url_for('profile.update') }}">
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="display_name">Display Name</label>
|
||||
<input class="form-input" type="text" id="display_name" name="display_name"
|
||||
value="{{ user.display_name or '' }}">
|
||||
</div>
|
||||
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="height_cm">Height (cm)</label>
|
||||
<input class="form-input" type="number" id="height_cm" name="height_cm"
|
||||
value="{{ user.height_cm or '' }}" step="0.1">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="age">Age</label>
|
||||
<input class="form-input" type="number" id="age" name="age" value="{{ user.age or '' }}">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="gender">Gender</label>
|
||||
<select class="form-input" id="gender" name="gender">
|
||||
<option value="">Select...</option>
|
||||
<option value="male" {{ 'selected' if user.gender=='male' }}>Male</option>
|
||||
<option value="female" {{ 'selected' if user.gender=='female' }}>Female</option>
|
||||
<option value="other" {{ 'selected' if user.gender=='other' }}>Other</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="starting_weight_kg">Starting Weight (kg)</label>
|
||||
<input class="form-input" type="number" id="starting_weight_kg" name="starting_weight_kg"
|
||||
value="{{ user.starting_weight_kg or '' }}" step="0.1">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="goal_weight_kg">Goal Weight (kg)</label>
|
||||
<input class="form-input" type="number" id="goal_weight_kg" name="goal_weight_kg"
|
||||
value="{{ user.goal_weight_kg or '' }}" step="0.1">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-primary" style="margin-top: 0.5rem;">Save Changes</button>
|
||||
</form>
|
||||
</div>
|
||||
{% endblock %}
|
||||
71
app/templates/signup.html
Normal file
71
app/templates/signup.html
Normal file
@@ -0,0 +1,71 @@
|
||||
{% extends "base.html" %}
|
||||
{% block title %}Sign Up — WeightTracker{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="auth-page">
|
||||
<div class="auth-card">
|
||||
<h1>⚖️ WeightTracker</h1>
|
||||
<p class="auth-subtitle">Join the competition. Track your progress.</p>
|
||||
|
||||
<form method="POST" action="{{ url_for('auth.signup') }}">
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="username">Username</label>
|
||||
<input class="form-input" type="text" id="username" name="username" placeholder="Pick a username"
|
||||
required autofocus>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="password">Password</label>
|
||||
<input class="form-input" type="password" id="password" name="password"
|
||||
placeholder="At least 4 characters" required minlength="4">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="display_name">Display Name</label>
|
||||
<input class="form-input" type="text" id="display_name" name="display_name"
|
||||
placeholder="How your friends will see you">
|
||||
</div>
|
||||
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="height_cm">Height (cm)</label>
|
||||
<input class="form-input" type="number" id="height_cm" name="height_cm" placeholder="175"
|
||||
step="0.1">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="starting_weight_kg">Starting Weight (kg)</label>
|
||||
<input class="form-input" type="number" id="starting_weight_kg" name="starting_weight_kg"
|
||||
placeholder="80" step="0.1">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="age">Age</label>
|
||||
<input class="form-input" type="number" id="age" name="age" placeholder="25">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="gender">Gender</label>
|
||||
<select class="form-input" id="gender" name="gender">
|
||||
<option value="">Select...</option>
|
||||
<option value="male">Male</option>
|
||||
<option value="female">Female</option>
|
||||
<option value="other">Other</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="goal_weight_kg">Goal Weight (kg)</label>
|
||||
<input class="form-input" type="number" id="goal_weight_kg" name="goal_weight_kg" placeholder="70"
|
||||
step="0.1">
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-primary btn-block btn-lg" style="margin-top: 0.5rem;">Create
|
||||
Account</button>
|
||||
</form>
|
||||
|
||||
<p class="auth-footer">Already have an account? <a href="{{ url_for('auth.login') }}">Log in</a></p>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user