Files
vrobbler/vrobbler/templates/drinks/drink_detail.html
Colin Powell 155ed2a4f8 feat: add Open Food Facts search selector for food and drinks
- Add openfoodfacts Python SDK dependency
- Create foods/sources/openfoodfacts.py API wrapper with text_search
- Add off_code field + find_or_create_from_search() to Food model
- Add nutrition fields (protein, fat, carbs, fiber, sugar, sodium),
  ingredients, image, off_code to Drink model
- Create FoodSearchView and FoodScrobbleFromSearchView at /foods/search/
- Route beverages to Drink model based on OFF category keywords
- Update ManualScrobbleView to redirect '-f Food Name' to food search
- Add Drinks section to home page and scrobble detail templates
- Update drink_detail.html with nutrition grid and image display
2026-07-21 17:09:13 -04:00

157 lines
4.4 KiB
HTML

{% extends "base_list.html" %}
{% load mathfilters %}
{% load static %}
{% load naturalduration %}
{% block title %}{{object.title}}{% endblock %}
{% block head_extra %}
<style>
.cover img {
width: 250px;
}
.cover {
float: left;
width: 252px;
padding: 0;
}
.summary {
float: left;
width: 600px;
margin-left: 10px;
}
.nutrition-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
gap: 0.5rem;
margin: 1rem 0;
}
.nutrition-item {
text-align: center;
padding: 0.5rem;
border: 1px solid #dee2e6;
border-radius: 4px;
}
.nutrition-item .value {
font-size: 1.25rem;
font-weight: 600;
}
.nutrition-item .label {
font-size: 0.75rem;
color: #6c757d;
}
</style>
{% endblock %}
{% block lists %}
<div class="row">
{% if object.drink_image %}
<div class="cover">
<img src="{{ object.drink_image.url }}" alt="{{ object.title }}">
</div>
{% endif %}
<div class="summary">
{% if object.description %}
<p>{{object.description|safe|linebreaks|truncatewords:160}}</p>
<hr />
{% endif %}
{% if object.calories or object.protein %}
<div class="nutrition-grid">
{% if object.calories %}
<div class="nutrition-item">
<div class="value">{{ object.calories }}</div>
<div class="label">kcal</div>
</div>
{% endif %}
{% if object.protein %}
<div class="nutrition-item">
<div class="value">{{ object.protein }}g</div>
<div class="label">Protein</div>
</div>
{% endif %}
{% if object.fat %}
<div class="nutrition-item">
<div class="value">{{ object.fat }}g</div>
<div class="label">Fat</div>
</div>
{% endif %}
{% if object.carbohydrates %}
<div class="nutrition-item">
<div class="value">{{ object.carbohydrates }}g</div>
<div class="label">Carbs</div>
</div>
{% endif %}
{% if object.fiber %}
<div class="nutrition-item">
<div class="value">{{ object.fiber }}g</div>
<div class="label">Fiber</div>
</div>
{% endif %}
{% if object.sugar %}
<div class="nutrition-item">
<div class="value">{{ object.sugar }}g</div>
<div class="label">Sugar</div>
</div>
{% endif %}
</div>
{% endif %}
{% if object.ingredients %}
<h5>Ingredients</h5>
<p>{{ object.ingredients }}</p>
{% endif %}
{% if object.off_code %}
<p><small class="text-muted">OFF: {{ object.off_code }}</small></p>
{% endif %}
</div>
</div>
<div class="row">
<p>{{scrobbles.count}} scrobbles</p>
<p>
<a href="{{object.start_url}}">Drink again</a>
</p>
</div>
<div class="row">
<div class="col-md">
<h3>Last scrobbles</h3>
<div class="table-responsive">
<table class="table table-striped table-sm">
<thead>
<tr>
<th scope="col">Date</th>
<th scope="col">Format</th>
<th scope="col">Size</th>
</tr>
</thead>
<tbody>
{% for scrobble in scrobbles.all|dictsortreversed:"timestamp" %}
<tr>
<td><a href="{{scrobble.get_absolute_url}}">{{scrobble.local_timestamp}}</a></td>
<td>{{ scrobble.logdata.format_display }}</td>
<td>
{% if scrobble.logdata.size_ml %}
{% if request.user.profile.volume_unit == "imperial" %}
{{ scrobble.logdata.size_oz }} oz
{% else %}
{{ scrobble.logdata.size_ml }} mL
{% endif %}
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
{% endblock %}