summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKen D'Ambrosio <ken@claude>2026-05-25 00:52:19 +0000
committerKen D'Ambrosio <ken@claude>2026-05-25 00:52:19 +0000
commitb5f0c3ee2c3060dd9821d42f4e1bcbb87cbbee10 (patch)
treee3ae9394a10871e0acfb67329200e8a1939a5bb1
parent55bcec90c14db6f2956ed51cf4df1503c0767f81 (diff)
Add recipe overview page linked from dashboard stat card
Clicking "Active Recipes" on the dashboard goes to /recipes/overview, which shows favorites and the 12 most recently added recipes as cards. Favorites are highlighted with a red border. Stat card has a hover lift. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
-rw-r--r--app/app.py20
-rw-r--r--app/static/css/style.css9
-rw-r--r--app/templates/index.html10
-rw-r--r--app/templates/recipe_overview.html81
4 files changed, 116 insertions, 4 deletions
diff --git a/app/app.py b/app/app.py
index bdeb215..026907e 100644
--- a/app/app.py
+++ b/app/app.py
@@ -253,6 +253,26 @@ def index():
# ── Recipes ───────────────────────────────────────────────────────────────────
+@app.route('/recipes/overview')
+def recipes_overview():
+ db = database.get_db()
+ favorites = db.execute(
+ "SELECT * FROM recipes WHERE status = 'favorited' ORDER BY name"
+ ).fetchall()
+ recent = db.execute(
+ """SELECT * FROM recipes WHERE status != 'ignored'
+ ORDER BY id DESC LIMIT 12"""
+ ).fetchall()
+ db.close()
+ fav_ids = {r['id'] for r in favorites}
+ recent_new = [r for r in recent if r['id'] not in fav_ids]
+ return render_template(
+ 'recipe_overview.html',
+ favorites=favorites, recent=recent_new,
+ cuisine_emoji=CUISINE_EMOJI_MAP,
+ )
+
+
SORT_OPTIONS = {
'name': 'name',
'prep': 'prep_time, name',
diff --git a/app/static/css/style.css b/app/static/css/style.css
index 7167e05..37d36f6 100644
--- a/app/static/css/style.css
+++ b/app/static/css/style.css
@@ -67,6 +67,15 @@ body {
line-height: 1;
}
+.stat-card-link {
+ transition: box-shadow 0.15s, transform 0.15s;
+ cursor: pointer;
+}
+.stat-card-link:hover {
+ box-shadow: 0 4px 12px rgba(0,0,0,0.12) !important;
+ transform: translateY(-2px);
+}
+
.stat-label {
font-size: 0.8rem;
color: var(--muted);
diff --git a/app/templates/index.html b/app/templates/index.html
index c33e348..55b5ef0 100644
--- a/app/templates/index.html
+++ b/app/templates/index.html
@@ -28,10 +28,12 @@
</div>
</div>
<div class="col-6 col-md-3">
- <div class="stat-card">
- <div class="stat-number">{{ stat_map.get('candidate', 0) + stat_map.get('favorited', 0) }}</div>
- <div class="stat-label">Active Recipes</div>
- </div>
+ <a href="/recipes/overview" class="text-decoration-none">
+ <div class="stat-card stat-card-link">
+ <div class="stat-number">{{ stat_map.get('candidate', 0) + stat_map.get('favorited', 0) }}</div>
+ <div class="stat-label">Active Recipes</div>
+ </div>
+ </a>
</div>
<div class="col-6 col-md-3">
<div class="stat-card">
diff --git a/app/templates/recipe_overview.html b/app/templates/recipe_overview.html
new file mode 100644
index 0000000..252aed2
--- /dev/null
+++ b/app/templates/recipe_overview.html
@@ -0,0 +1,81 @@
+{% extends "base.html" %}
+{% block title %}Recipe Overview — Menu Planner{% endblock %}
+
+{% block content %}
+<div class="d-flex align-items-center justify-content-between mb-4">
+ <h1 class="h3 fw-bold mb-0">Recipe Overview</h1>
+ <a href="/recipes" class="btn btn-sm btn-outline-secondary"><i class="bi bi-grid me-1"></i>Full Library</a>
+</div>
+
+{% if favorites %}
+<h2 class="h5 fw-semibold mb-3"><i class="bi bi-heart-fill text-danger me-2"></i>Favorites</h2>
+<div class="row g-3 mb-5">
+ {% for r in favorites %}
+ <div class="col-sm-6 col-lg-4 col-xl-3">
+ <a href="/recipes/{{ r.id }}" class="text-decoration-none">
+ <div class="recipe-card card h-100 shadow-sm border-danger border-opacity-25">
+ <div class="card-body d-flex flex-column">
+ <div class="d-flex align-items-start justify-content-between mb-2">
+ <span class="cuisine-pill">{{ cuisine_emoji.get(r.cuisine) }} {{ r.cuisine }}</span>
+ <i class="bi bi-heart-fill text-danger"></i>
+ </div>
+ <h5 class="card-title mb-1 text-dark">{{ r.name }}</h5>
+ <p class="card-text text-muted small flex-grow-1">{{ r.description[:90] }}{% if r.description|length > 90 %}…{% endif %}</p>
+ {% if r.rating %}
+ <div class="mb-1" style="color:#f59e0b;font-size:0.8rem">
+ {% for i in range(1,6) %}<i class="bi bi-star{% if r.rating >= i %}-fill{% endif %}"></i>{% endfor %}
+ </div>
+ {% endif %}
+ <div class="nutrition-row mt-auto">
+ <span class="nutri-badge"><i class="bi bi-lightning-charge"></i> {{ r.calories_per_serving|int }} cal</span>
+ <span class="nutri-badge"><i class="bi bi-circle-half"></i> {{ r.carbs_per_serving|int }}g carbs</span>
+ <span class="nutri-badge"><i class="bi bi-clock"></i> {{ r.prep_time + r.cook_time }} min</span>
+ </div>
+ </div>
+ </div>
+ </a>
+ </div>
+ {% endfor %}
+</div>
+{% endif %}
+
+{% if recent %}
+<h2 class="h5 fw-semibold mb-3"><i class="bi bi-clock-history text-primary me-2"></i>Recently Added</h2>
+<div class="row g-3">
+ {% for r in recent %}
+ <div class="col-sm-6 col-lg-4 col-xl-3">
+ <a href="/recipes/{{ r.id }}" class="text-decoration-none">
+ <div class="recipe-card card h-100 shadow-sm">
+ <div class="card-body d-flex flex-column">
+ <div class="d-flex align-items-start justify-content-between mb-2">
+ <span class="cuisine-pill">{{ cuisine_emoji.get(r.cuisine) }} {{ r.cuisine }}</span>
+ {% if r.status == 'favorited' %}<i class="bi bi-heart-fill text-danger"></i>{% endif %}
+ </div>
+ <h5 class="card-title mb-1 text-dark">{{ r.name }}</h5>
+ <p class="card-text text-muted small flex-grow-1">{{ r.description[:90] }}{% if r.description|length > 90 %}…{% endif %}</p>
+ {% if r.added_by %}<p class="text-muted small mb-1"><i class="bi bi-person me-1"></i>{{ r.added_by }}</p>{% endif %}
+ {% if r.rating %}
+ <div class="mb-1" style="color:#f59e0b;font-size:0.8rem">
+ {% for i in range(1,6) %}<i class="bi bi-star{% if r.rating >= i %}-fill{% endif %}"></i>{% endfor %}
+ </div>
+ {% endif %}
+ <div class="nutrition-row mt-auto">
+ <span class="nutri-badge"><i class="bi bi-lightning-charge"></i> {{ r.calories_per_serving|int }} cal</span>
+ <span class="nutri-badge"><i class="bi bi-circle-half"></i> {{ r.carbs_per_serving|int }}g carbs</span>
+ <span class="nutri-badge"><i class="bi bi-clock"></i> {{ r.prep_time + r.cook_time }} min</span>
+ </div>
+ </div>
+ </div>
+ </a>
+ </div>
+ {% endfor %}
+</div>
+{% endif %}
+
+{% if not favorites and not recent %}
+<div class="text-center py-5 text-muted">
+ <i class="bi bi-book fs-1"></i>
+ <p class="mt-3">No recipes yet. <a href="/recipes/add">Add one</a> or use the <a href="/ai">AI assistant</a>.</p>
+</div>
+{% endif %}
+{% endblock %}