cauldron/cauldron/templates/recipe_detail.html
Kayos 8e53a84121 fix: build mealie permalink as /g/<group>/r/<slug>, not /recipe/<slug>
Cobb caught it. Mealie's web URL format is:
  https://recipes.sulkta.com/g/hayes-house/r/dairy-free-bread-recipe

Was emitting /recipe/<slug> which 404s.

Added _user_group_slug() helper that pulls .group.slug from /api/users/self
(handles dict-or-string-or-fallback shapes for cross-version compat),
threads the proper URL through both:
- /api/recipes/<slug>.json (used by the modal)
- /recipes/<slug> server-rendered detail (used as fallback for direct links)

Falls back to the old /recipe/<slug> path if we can't resolve a group
slug (won't break, will just 404 in Mealie if that ever happens).
2026-04-28 21:33:10 -07:00

92 lines
3 KiB
HTML

{% extends "_base.html" %}
{% block title %}{{ recipe.name }} · Cauldron{% endblock %}
{% block content %}
<div class="page-head">
<div class="crumb">// recipes / {{ recipe.slug }}</div>
<h1>{{ recipe.name }}</h1>
{% if recipe.description %}<div class="lede">{{ recipe.description }}</div>{% endif %}
</div>
<section class="panel">
<div class="panel-head">
<h2>meta</h2>
</div>
<dl class="kv">
{% if recipe.totalTime %}<dt>total</dt><dd>{{ recipe.totalTime }}</dd>{% endif %}
{% if recipe.prepTime %}<dt>prep</dt><dd>{{ recipe.prepTime }}</dd>{% endif %}
{% if recipe.cookTime %}<dt>cook</dt><dd>{{ recipe.cookTime }}</dd>{% endif %}
{% if recipe.recipeYield %}<dt>yield</dt><dd>{{ recipe.recipeYield }}</dd>{% endif %}
{% if recipe.recipeServings %}<dt>servings</dt><dd>{{ recipe.recipeServings }}</dd>{% endif %}
</dl>
</section>
<section class="panel green">
<div class="panel-head">
<h2>ingredients</h2>
<span class="ctx">{{ (recipe.recipeIngredient or []) | length }} items</span>
</div>
{% if recipe.recipeIngredient %}
<ul style="list-style: none; padding: 0; margin: 0;">
{% for ing in recipe.recipeIngredient %}
<li style="padding: 4px 0; border-bottom: 1px dashed var(--line); color: var(--bone);">
{% if ing.display %}{{ ing.display }}{% else %}{{ ing.note or '' }}{% endif %}
</li>
{% endfor %}
</ul>
{% else %}
<p class="muted">no ingredients listed.</p>
{% endif %}
</section>
<section class="panel">
<div class="panel-head">
<h2>instructions</h2>
</div>
{% if recipe.recipeInstructions %}
<ol>
{% for step in recipe.recipeInstructions %}
<li style="margin: .8em 0; color: var(--bone);">{{ step.text }}</li>
{% endfor %}
</ol>
{% else %}
<p class="muted">no instructions listed.</p>
{% endif %}
</section>
<div class="btn-row">
<a class="btn btn-purple" href="/recipes">← back to grimoire</a>
<button id="pick-toggle" type="button" class="btn {% if picked %}btn-primary{% endif %}"
data-slug="{{ recipe.slug }}" data-name="{{ recipe.name }}"
onclick="togglePick(this)">
🍄 {% if picked %}pinned · remove{% else %}pin for ai plan{% endif %}
</button>
<a class="btn" href="{{ mealie_url }}" target="_blank" rel="noopener">view in mealie ↗</a>
</div>
<script>
async function togglePick(btn) {
const slug = btn.dataset.slug;
const name = btn.dataset.name;
const isOn = btn.classList.contains('btn-primary');
btn.disabled = true;
try {
const r = await fetch(`/api/picks/${encodeURIComponent(slug)}`, {
method: isOn ? 'DELETE' : 'POST',
headers: { 'Content-Type': 'application/json' },
body: isOn ? null : JSON.stringify({ name })
});
if (!r.ok) throw new Error(r.status);
btn.classList.toggle('btn-primary');
btn.innerHTML = btn.classList.contains('btn-primary')
? '🍄 pinned · remove'
: '🍄 pin for ai plan';
} catch (e) {
/* leave as-is */
} finally {
btn.disabled = false;
}
}
</script>
{% endblock %}