Pantry move/expiry edit + smart shopping list with summary

- Move items between fridge↔freezer (freezer→fridge sets +7d or shelf life expiry, fridge→freezer clears expiry)
- Inline expiry date editor on fridge items (📅 button)
- Smart shopping list now shows recipe gaps (missing required slot ingredients)
- Summary card shows breakdown: X staple restocks, Y expiring, Z recipe gaps
This commit is contained in:
Caine
2026-04-02 23:18:01 +01:00
parent 726cb9d153
commit d0de1b44ea
6 changed files with 154 additions and 6 deletions
@@ -0,0 +1,17 @@
<tr id="pantry-row-{{ item.id }}">
<td>{{ item.ingredient.name }}</td>
<td>{{ item.quantity|floatformat:0 }} {{ item.unit }}</td>
<td colspan="2">
<form style="display: flex; gap: 0.4rem; align-items: center;"
hx-post="{% url 'app-pantry-save-expiry' item.id %}"
hx-target="#pantry-items"
hx-swap="innerHTML">
<input type="date" name="expiry_date" value="{{ item.expiry_date|date:'Y-m-d' }}" style="font-size: 0.8rem;">
<button type="submit" class="btn btn-primary btn-sm">Save</button>
<button type="button" class="btn btn-secondary btn-sm"
hx-get="{% url 'app-pantry-cancel-edit' %}"
hx-target="#pantry-items"
hx-swap="innerHTML">Cancel</button>
</form>
</td>
</tr>
@@ -19,8 +19,18 @@
{% else %}
<span style="color: var(--sage);"></span>
{% endif %}
<button class="btn btn-sm btn-secondary" style="margin-left: 0.25rem; padding: 0.1rem 0.3rem; font-size: 0.7rem;"
hx-post="{% url 'app-pantry-edit-expiry' item.id %}"
hx-target="#pantry-row-{{ item.id }}"
hx-swap="outerHTML"
title="Change expiry">📅</button>
</td>
<td style="text-align: right;">
<td style="text-align: right; white-space: nowrap;">
<button class="btn btn-sm btn-secondary" style="padding: 0.1rem 0.3rem; font-size: 0.7rem;"
hx-post="{% url 'app-pantry-move' item.id %}"
hx-target="#pantry-items"
hx-vals='{"to": "freezer"}'
title="Move to freezer">❄️</button>
<button class="btn btn-danger btn-sm"
hx-delete="{% url 'app-pantry-delete' item.id %}"
hx-target="#pantry-items"
@@ -41,7 +51,12 @@
<tr id="pantry-row-{{ item.id }}">
<td>{{ item.ingredient.name }}</td>
<td>{{ item.quantity|floatformat:0 }} {{ item.unit }}</td>
<td style="text-align: right;">
<td style="text-align: right; white-space: nowrap;">
<button class="btn btn-sm btn-secondary" style="padding: 0.1rem 0.3rem; font-size: 0.7rem;"
hx-post="{% url 'app-pantry-move' item.id %}"
hx-target="#pantry-items"
hx-vals='{"to": "fridge"}'
title="Defrost → Fridge">🧊→</button>
<button class="btn btn-danger btn-sm"
hx-delete="{% url 'app-pantry-delete' item.id %}"
hx-target="#pantry-items"
@@ -1,3 +1,14 @@
{% if summary %}
<div class="card" style="margin-bottom: 1rem;">
<div style="color: var(--teal-light); font-size: 0.85rem;">
<strong style="color: var(--yellow);">{{ summary.total }} items</strong> suggested:
{% if summary.staples %}<span>{{ summary.staples }} staple restock{{ summary.staples|pluralize }}</span>{% endif %}
{% if summary.expiring %}• <span>{{ summary.expiring }} expiring</span>{% endif %}
{% if summary.recipe_gaps %}• <span>{{ summary.recipe_gaps }} recipe gap{{ summary.recipe_gaps|pluralize }}</span>{% endif %}
</div>
</div>
{% endif %}
{% regroup items by section as sections %}
{% for section in sections %}
<div class="shop-section">{{ section.grouper|default:"Other" }}</div>
+4 -1
View File
@@ -4,7 +4,7 @@
{% block content %}
<h1>Shopping List</h1>
<div style="display: flex; gap: 0.75rem; margin-bottom: 1.5rem;">
<div style="display: flex; gap: 0.75rem; margin-bottom: 1rem; flex-wrap: wrap;">
<button class="btn btn-primary"
hx-post="{% url 'app-shopping-generate' %}"
hx-target="#shopping-items"
@@ -19,6 +19,9 @@
Clear Checked
</button>
</div>
<p style="color: var(--sage); font-size: 0.8rem; margin-bottom: 1.5rem;">
Smart list checks: staples at zero, items expiring within 2 days, and ingredients needed for your recipes.
</p>
<div id="shopping-items">
{% include "kitchen/partials/shopping_list.html" %}