{% extends "base.html" %}
{% block title %}{{ name }} - Cook{% endblock %}
{% block content %}
<div id="menu-content">
<nav class="mb-4 print:hidden">
<ol class="flex items-center space-x-2 text-sm text-gray-600">
<li><a href="{{ prefix }}/{% if static_mode %}index.html{% endif %}" class="hover:text-gray-900">{{ tr.t("nav-recipes") }}</a></li>
{% for crumb in breadcrumbs %}
<li class="flex items-center">
<span class="mx-2 text-gray-400">/</span>
{% if loop.last %}
<span class="text-gray-900">{{ crumb }}</span>
{% else %}
<a href="{{ prefix }}/directory/{{ breadcrumbs[..loop.index0 + 1].join("/") }}{% if static_mode %}.html{% endif %}" class="hover:text-gray-900">{{ crumb }}</a>
{% endif %}
</li>
{% endfor %}
</ol>
</nav>
<div class="mb-4">
{% match image_path %}
{% when Some with (img) %}
<div class="mb-4 max-w-4xl mx-auto print:hidden">
<div class="rounded-xl overflow-hidden shadow-lg bg-gray-50">
<img src="{{ img }}" alt="{{ name }}" class="w-full h-auto max-h-[500px] object-contain mx-auto">
</div>
</div>
{% when None %}
{% endmatch %}
<div class="flex flex-col md:flex-row md:items-center md:justify-between gap-4 mb-4 print:mb-1">
<div class="flex items-center gap-3">
<h1 class="text-3xl md:text-4xl font-bold bg-gradient-to-r from-purple-600 to-pink-600 bg-clip-text text-transparent print:text-2xl">{{ name }}</h1>
<span class="px-3 py-1 bg-gradient-to-r from-purple-100 to-pink-100 text-purple-700 rounded-full text-sm font-semibold print:hidden">{{ tr.t("recipe-type-menu") }}</span>
</div>
{% if !static_mode %}
<div class="flex flex-wrap items-center gap-2 lg:gap-3 print:hidden">
<div class="flex items-center">
<label for="scale" class="text-sm font-medium text-gray-700 mr-2">{{ tr.t("recipe-scale-label") }}:</label>
<input type="number"
id="scale"
value="{{ scale }}"
min="0.5"
max="200"
step="0.5"
class="px-3 py-2 border border-gray-300 rounded-lg w-20 text-center"
onchange="window.location.href = `{{ prefix }}/recipe/{{ recipe_path }}?scale=${this.value}`">
</div>
<a href="{{ prefix }}/edit/{{ recipe_path }}"
class="px-3 lg:px-4 py-2 text-sm lg:text-base bg-gradient-to-r from-blue-500 to-cyan-500 text-white rounded-lg hover:from-blue-600 hover:to-cyan-600 transition-all shadow-md flex items-center gap-1.5 lg:gap-2 whitespace-nowrap"
title="{{ tr.t("action-edit") }}">
<svg class="w-4 h-4 lg:w-5 lg:h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"></path>
</svg>
<span class="hidden lg:inline">{{ tr.t("action-edit") }}</span>
</a>
<button onclick="addToShoppingList(event, {{ recipe_path|json }})"
class="px-3 lg:px-4 py-2 text-sm lg:text-base bg-gradient-to-r from-purple-500 to-pink-500 text-white rounded-lg hover:from-purple-600 hover:to-pink-600 transition-all shadow-md flex items-center gap-1.5 lg:gap-2 whitespace-nowrap"
title="{{ tr.t("recipe-add-all-to-shopping") }}">
<svg class="w-4 h-4 lg:w-5 lg:h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 3h2l.4 2M7 13h10l4-8H5.4M7 13L5.4 5M7 13l-2.293 2.293c-.63.63-.184 1.707.707 1.707H17m0 0a2 2 0 100 4 2 2 0 000-4zm-8 2a2 2 0 11-4 0 2 2 0 014 0z"></path>
</svg>
<span class="hidden lg:inline">{{ tr.t("recipe-add-all-to-shopping") }}</span>
</button>
</div>
{% endif %}
</div>
<div class="space-y-2 print:hidden">
{% match metadata %}
{% when Some with (metadata) %}
{% match metadata.description %}
{% when Some with (description) %}
<div class="bg-gradient-to-r from-purple-50 to-pink-50 p-4 rounded-xl border border-purple-200">
<p class="text-gray-700 italic">{{ description }}</p>
</div>
{% when None %}
{% endmatch %}
<div id="metadata-container" class="flex flex-wrap gap-3">
{% match metadata.servings %}
{% when Some with (servings) %}
<span class="metadata-pill metadata-servings">👥 {{ servings }} {{ tr.t("recipe-servings-label") }}</span>
{% when None %}
{% endmatch %}
{% match metadata.time %}
{% when Some with (time) %}
<span class="metadata-pill metadata-time">⏱️ {{ time }}</span>
{% when None %}
{% endmatch %}
{% match metadata.author %}
{% when Some with (author) %}
<span class="metadata-pill metadata-author">👤 {{ author }}</span>
{% when None %}
{% endmatch %}
{% match metadata.source %}
{% when Some with (source) %}
<span class="metadata-pill metadata-source">📖 {{ source }}</span>
{% when None %}
{% endmatch %}
{% match metadata.source_url %}
{% when Some with (source_url) %}
<span class="metadata-pill metadata-source-url">🔗 <a href="{{ source_url }}" class="text-blue-600 hover:text-blue-800 hover:underline">{{ source_url|hostname }}</a></span>
{% when None %}
{% endmatch %}
{% for (key, value) in metadata.custom %}
<span class="metadata-pill metadata-custom">{{ key }}: {{ value }}</span>
{% endfor %}
</div>
{% when None %}
{% endmatch %}
</div>
</div>
<div class="space-y-6">
{% for section in sections %}
<div class="menu-section bg-white rounded-xl shadow-xs border border-gray-200 overflow-hidden">
{% match section.name %}
{% when Some with (name) %}
<div class="menu-section-header bg-gradient-to-r from-purple-500 to-pink-500 text-white px-6 py-3">
<h2 class="text-xl font-semibold">{{ name }}</h2>
</div>
{% when None %}
{% endmatch %}
<div class="menu-section-body p-6 space-y-2">
{% for line in section.lines %}
{% if line.len() == 1 %}
{% match line[0] %}
{% when crate::web::templates::MenuSectionItem::Text with (text) %}
{% if text.trim().ends_with(":") %}
<h3 class="text-lg font-semibold text-gray-800 mt-2 mb-1">{{ text }}</h3>
{% else %}
<div class="pl-6">
<span class="text-gray-700">{{ text }}</span>
</div>
{% endif %}
{% when _ %}
<div class="pl-6 flex items-baseline gap-x-2">
{% match line[0] %}
{% when crate::web::templates::MenuSectionItem::RecipeReference with { name, scale } %}
<span class="inline-flex items-center gap-1">
<a href="{{ prefix }}/recipe/{{ name.strip_prefix("./").unwrap_or(name) }}{% if static_mode %}.html{% endif %}"
class="text-purple-600 hover:text-purple-800 font-medium hover:underline">
{{ name.strip_prefix("./").unwrap_or(name).replace("/", " › ") }}
</a>
{% match scale %}
{% when Some with (s) %}
<span class="text-sm text-gray-500">(×{{ s }})</span>
{% when None %}
{% endmatch %}
</span>
{% when crate::web::templates::MenuSectionItem::Ingredient with { name, quantity, unit } %}
<span class="inline-flex items-center gap-1">
<span class="ingredient-badge">
{{ name }}
{% match quantity %}
{% when Some with (q) %}
<span class="font-semibold">{{ q }}</span>
{% when None %}
{% endmatch %}
{% match unit %}
{% when Some with (u) %}
<span class="text-orange-700">{{ u }}</span>
{% when None %}
{% endmatch %}
</span>
</span>
{% when _ %}
{% endmatch %}
</div>
{% endmatch %}
{% else %}
<div class="pl-6 flex flex-wrap items-baseline gap-x-2">
{% for item in line %}
{% match item %}
{% when crate::web::templates::MenuSectionItem::Text with (text) %}
<span class="text-gray-700">{{ text }}</span>
{% when crate::web::templates::MenuSectionItem::RecipeReference with { name, scale } %}
<span class="inline-flex items-center gap-1">
<a href="{{ prefix }}/recipe/{{ name.strip_prefix("./").unwrap_or(name) }}{% if static_mode %}.html{% endif %}"
class="text-purple-600 hover:text-purple-800 font-medium hover:underline">
{{ name.strip_prefix("./").unwrap_or(name).replace("/", " › ") }}
</a>
{% match scale %}
{% when Some with (s) %}
<span class="text-sm text-gray-500">(×{{ s }})</span>
{% when None %}
{% endmatch %}
</span>
{% when crate::web::templates::MenuSectionItem::Ingredient with { name, quantity, unit } %}
<span class="inline-flex items-center gap-1">
<span class="ingredient-badge">
{{ name }}
{% match quantity %}
{% when Some with (q) %}
<span class="font-semibold">{{ q }}</span>
{% when None %}
{% endmatch %}
{% match unit %}
{% when Some with (u) %}
<span class="text-orange-700">{{ u }}</span>
{% when None %}
{% endmatch %}
</span>
</span>
{% endmatch %}
{% endfor %}
</div>
{% endif %}
{% endfor %}
</div>
</div>
{% endfor %}
</div>
</div>
{% if !static_mode %}
<script>
function escHtml(s) {
return String(s == null ? '' : s)
.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''');
}
async function addToShoppingList(event, recipePath) {
event.preventDefault();
const button = event.currentTarget;
const originalText = button.innerHTML;
try {
const scale = document.getElementById('scale')?.value || 1;
const response = await fetch('{{ prefix }}/api/shopping_list/add_menu', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
path: recipePath,
scale: parseFloat(scale)
})
});
if (response.ok) {
button.innerHTML = '<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"></path></svg> ' + escHtml({{ tr.t("recipe-added")|json|safe }});
button.classList.add('bg-green-500', 'hover:bg-green-600');
button.classList.remove('from-purple-500', 'to-pink-500', 'hover:from-purple-600', 'hover:to-pink-600');
setTimeout(() => {
button.innerHTML = originalText;
button.classList.remove('bg-green-500', 'hover:bg-green-600');
button.classList.add('from-purple-500', 'to-pink-500', 'hover:from-purple-600', 'hover:to-pink-600');
}, 2000);
} else {
throw new Error({{ tr.t("shopping-failed-to-add")|json|safe }});
}
} catch (error) {
console.error('Error:', error);
button.innerHTML = '<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path></svg> ' + escHtml({{ tr.t("shopping-error")|json|safe }});
button.classList.add('bg-red-500', 'hover:bg-red-600');
setTimeout(() => {
button.innerHTML = originalText;
button.classList.remove('bg-red-500', 'hover:bg-red-600');
}, 2000);
}
}
</script>
{% endif %}
{% endblock %}