1
0
mirror of https://gitlab.crans.org/bde/nk20 synced 2025-11-21 05:48:26 +01:00

Add model Recipe

This commit is contained in:
Ehouarn
2025-11-06 23:49:16 +01:00
parent 48b1ef9ec8
commit 6a77cfd4dd
13 changed files with 639 additions and 22 deletions

View File

@@ -47,6 +47,9 @@ SPDX-License-Identifier: GPL-3.0-or-later
<a class="btn btn-sm btn-secondary" href="{% url "food:manage_ingredients" pk=food.pk %}">
{% trans "Manage ingredients" %}
</a>
<a class="btn btn-sm btn-secondary" href="{% url "food:recipe_use" pk=food.pk %}">
{% trans "Use a recipe" %}
</a>
{% endif %}
<a class="btn btn-sm btn-primary" href="{% url "food:food_list" %}">
{% trans "Return to the food list" %}

View File

@@ -70,6 +70,16 @@ SPDX-License-Identifier: GPL-3.0-or-later
{% trans "New meal" %}
</a>
{% endif %}
{% if can_view_recipes %}
<a class="btn btn-sm btn-secondary" href="{% url 'food:recipe_list' %}">
{% trans "View recipes" %}
</a>
{% endif %}
{% if can_add_recipe %}
<a class="btn btn-sm btn-primary" href="{% url 'food:recipe_create' %}">
{% trans "New recipe" %}
</a>
{% endif %}
{% for activity in open_activities %}
<a class="btn btn-sm btn-secondary" href="{% url 'food:dish_list' activity_pk=activity.pk %}">
{% trans "View" %} {{ activity.name }}

View File

@@ -90,7 +90,7 @@ function delete_form_data (form_id) {
document.getElementById(prefix + "name").value = "";
document.getElementById(prefix + "qrcode_pk").value = "";
document.getElementById(prefix + "qrcode").value = "";
document.getElementById(prefix + "fully_used").checked = true;
document.getElementById(prefix + "fully_used").checked = false;
}
var form_count = {{ ingredients_count }} + 1;

View File

@@ -0,0 +1,31 @@
{% extends "base.html" %}
{% comment %}
Copyright (C) 2018-2025 by BDE ENS Paris-Saclay
SPDX-License-Identifier: GPL-3.0-or-later
{% endcomment %}
{% load render_table from django_tables2 %}
{% load i18n pretty_money %}
{% block content %}
<div class="card bg-white mb-3">
<h3 class="card-header text-center">
{{ title }} {{ recipe.name }}
</h3>
<div class="card-body">
<ul>
<li> {% trans "Creater" %} : {{ recipe.creater }}</li>
<li> {% trans "Ingredients" %} :
{% for ingredient in ingredients %} {{ ingredient }}{% if not forloop.last %},{% endif %}{% endfor %}
</li>
</ul>
{% if update %}
<a class="btn btn-sm btn-secondary" href="{% url "food:recipe_update" pk=recipe.pk %}">
{% trans "Update" %}
</a>
{% endif %}
<a class="btn btn-sm btn-primary" href="{% url "food:recipe_list" %}">
{% trans "Return to recipe list" %}
</a>
</div>
</div>
{% endblock %}

View File

@@ -0,0 +1,122 @@
{% extends "base.html" %}
{% comment %}
Copyright (C) 2018-2025 by BDE ENS Paris-Saclay
SPDX-License-Identifier: GPL-3.0-or-later
{% endcomment %}
{% load i18n crispy_forms_tags %}
{% block content %}
<div class="card bg-white mb-3">
<h3 class="card-header text-center">
{{ title }}
</h3>
<form method="post" action="" id="recipe_form">
{% csrf_token %}
<div class="card-body">
{% crispy recipe_form %}
{# Keep all form elements in the same card-body for proper structure #}
{{ formset.management_form }}
<h3 class="text-center mt-4">{% trans "Add ingredients" %}</h3>
<table class="table table-condensed table-striped">
{% for form in formset %}
{% if forloop.first %}
<thead>
<tr>
<th>{{ form.name.label }}</th>
</tr>
</thead>
<tbody id="form_body">
{% endif %}
<tr class="row-formset ingredients">
<td>
{# Force prefix on the form fields #}
{{ form.name.as_widget }}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{# Display buttons to add and remove ingredients #}
<div class="card-body">
<div class="btn-group btn-block" role="group">
<button type="button" id="add_more" class="btn btn-success">{% trans "Add ingredient" %}</button>
<button type="button" id="remove_one" class="btn btn-danger">{% trans "Remove ingredient" %}</button>
</div>
<button class="btn btn-primary" type="submit" form="recipe_form">{% trans "Submit"%}</button>
</div>
</form>
</div>
{# Hidden div that store an empty supplement form, to be copied into new forms #}
<div id="empty_form" style="display: none;">
<table class='no_error'>
<tbody id="for_real">
<tr class="row-formset">
<td>{{ formset.empty_form.name }}</td>
</tr>
</tbody>
</table>
</div>
{% endblock %}
{% block extrajavascript %}
<script>
/* script that handles add and remove lines */
$(document).ready(function() {
const totalFormsInput = $('input[name$="-TOTAL_FORMS"]');
const initialFormsInput = $('input[name$="-INITIAL_FORMS"]');
function updateTotalForms(n) {
if (totalFormsInput.length) {
totalFormsInput.val(n);
}
}
const initialCount = $('#form_body .row-formset').length;
updateTotalForms(initialCount);
const foods = {{ ingredients | safe }};
function prepopulate () {
for (var i = 0; i < {{ ingredients_count }}; i++) {
let prefix = 'id_form-' + parseInt(i) + '-';
document.getElementById(prefix + 'name').value = foods[i]['name'];
};
}
prepopulate();
$('#add_more').click(function() {
let formIdx = totalFormsInput.length ? parseInt(totalFormsInput.val(), 10) : $('#form_body .row-formset').length;
let newForm = $('#for_real').html().replace(/__prefix__/g, formIdx);
$('#form_body').append(newForm);
updateTotalForms(formIdx + 1);
});
$('#remove_one').click(function() {
let formIdx = totalFormsInput.length ? parseInt(totalFormsInput.val(), 10) : $('#form_body .row-formset').length;
if (formIdx > 1) {
$('#form_body tr.row-formset:last').remove();
updateTotalForms(formIdx - 1);
}
});
$('#recipe_form').on('submit', function() {
const totalInput = $('input[name$="-TOTAL_FORMS"]');
const prefix = totalInput.length ? totalInput.attr('name').replace(/-TOTAL_FORMS$/, '') : 'form';
$('#form_body tr.row-formset').each(function(i) {
const input = $(this).find('input,select,textarea').first();
if (input.length) {
const newName = `${prefix}-${i}-name`;
input.attr('name', newName).attr('id', `id_${newName}`).prop('disabled', false);
}
});
const visibleCount = $('#form_body tr.row-formset').length;
if (totalInput.length) totalInput.val(visibleCount);
});
});
</script>
{% endblock %}

View File

@@ -0,0 +1,32 @@
{% extends "base.html" %}
{% comment %}
Copyright (C) 2018-2025 by BDE ENS Paris-Saclay
SPDX-License-Identifier: GPL-3.0-or-later
{% endcomment %}
{% load render_table from django_tables2 %}
{% load i18n %}
{% block content %}
<div class="card bg-white mb-3">
<h3 class="card-header text-center">
{{ title }}
</h3>
{% render_table table %}
<div class="card-footer">
{% if can_add_recipe %}
<a class="btn btn-sm btn-success" href="{% url 'food:recipe_create' %}">{% trans "New recipe" %}</a>
{% endif %}
<a class="btn btn-sm btn-primary" href="{% url "food:food_list" %}">
{% trans "Return to the food list" %}
</a>
</div>
</div>
{% endblock %}
{% block extrajavascript %}
<script type="text/javascript">
$(".table-row").click(function () {
window.document.location = $(this).data("href");
});
</script>
{% endblock %}

View File

@@ -0,0 +1,80 @@
{% extends "base.html" %}
{% comment %}
Copyright (C) 2018-2025 by BDE ENS Paris-Saclay
SPDX-License-Identifier: GPL-3.0-or-later
{% endcomment %}
{% load i18n crispy_forms_tags %}
{% block content %}
<div class="card bg-white mb-3">
<h3 class="card-header text-center">
{{ title }} {{ object.name }}
</h3>
<div class="card-body" id="form">
<form method="post">
{% csrf_token %}
{{ form | crispy }}
<button class="btn btn-primary" type="submit">{% trans "Submit"%}</button>
</form>
</div>
</div>
{% endblock %}
{% block extrajavascript %}
<script>
$(document).ready(function () {
function refreshIngredients() {
// 1⃣ on récupère l'id de la recette sélectionnée
let recipe_id = $("#id_recipe").val() || $("input[name='recipe']:checked").val();
if (!recipe_id) {
// 2⃣ rien sélectionné → on vide la zone d'ingrédients
$("#div_id_ingredients > div").empty().html("<em>Aucune recette sélectionnée</em>");
return;
}
// 3⃣ on interroge le serveur
$.getJSON("{% url 'food:get_ingredients' %}", { recipe_id: recipe_id })
.done(function (data) {
// 4⃣ on cible le bon conteneur
const $container = $("#div_id_ingredients > div");
$container.empty();
if (data.ingredients && data.ingredients.length > 0) {
// 5⃣ on crée les cases à cocher
data.ingredients.forEach(function (ing, i) {
const html = `
<div class="form-check">
<input type="checkbox"
name="ingredients"
value="${ing.id}"
id="id_ingredients_${i}"
class="form-check-input"
checked>
<label class="form-check-label" for="id_ingredients_${i}">
${ing.name} (${ing.qr_code_numbers})
</label>
</div>
`;
$container.append(html);
});
} else {
$container.html("<em>Aucun ingrédient trouvé</em>");
}
})
.fail(function (xhr) {
console.error("Erreur AJAX:", xhr);
$("#div_id_ingredients > div").html("<em>Erreur de chargement des ingrédients</em>");
});
}
// 6⃣ déclenche quand la recette change
$("#id_recipe, input[name='recipe']").change(refreshIngredients);
// 7⃣ initial
refreshIngredients();
});
</script>
{% endblock %}