{% extends "_base.html" %} {% block title %}Shopping List · Cauldron{% endblock %} {% block content %}
// list · week of {{ plan.week_start.strftime('%b %-d') }}

shopping list

{% if empty_reason == 'no_plan' %} no plan yet — head over to /plan and summon one first. {% elif lines %} {{ lines|length }} line{{ '' if lines|length == 1 else 's' }} · {{ plan.slots|length }} recipe{{ '' if plan.slots|length == 1 else 's' }} · check off as you shop, state survives refresh. {% else %} plan is set but no aggregatable ingredients came back. {% endif %}
{% if empty_reason == 'no_plan' %}

no plan yet

🪄

summon this week's plan first — every recipe's ingredients flow here automatically, density-aware aggregated.

go to /plan →
{% else %} {% if missing_recipes %}
⚠ {{ missing_recipes|length }} recipe{{ '' if missing_recipes|length == 1 else 's' }} couldn't be loaded from mealie: {% for slug in missing_recipes %}{{ slug }}{% if not loop.last %}, {% endif %}{% endfor %}. the list below is partial — check /plan and re-roll if needed.
{% endif %}

the list

{{ lines|length }} item{{ '' if lines|length == 1 else 's' }}
{% if lines %} {# Flat list for now — aggregator output doesn't carry category. v0.4 may re-introduce per-category sections via foods.category once dedupe lands. #} {% else %}

no aggregatable ingredients. check /plan.

{% endif %}
{% endif %} {% endblock %}