Recipe templates

Fractions of the total ingredients required can be mentioned in recipes. This can be helpful when, for example, only half of some ingredient is needed at the start of the recipe. The proper values are automatically calculated when the number of servings is adjusted.
This commit is contained in:
M-Byte 2020-10-04 23:02:01 +02:00
parent 4b5b7bcb19
commit 48e300bba6
4 changed files with 121 additions and 10 deletions

View File

@ -28,6 +28,9 @@ $('.save-recipe').on('click', function(e)
{
e.preventDefault();
$(".note-editable span.ingredient").removeClass(["badge", "btn", "btn-secondary"]);
$("#description").text($("#description").siblings(".note-editor").find(".note-editable").html());
var jsonData = $('#recipe-form').serializeJSON();
Grocy.FrontendHelpers.BeginUiBusy("recipe-form");
@ -230,6 +233,62 @@ $(document).on('click', '.recipe-pos-edit-button', function(e)
});
});
$(document).on('click', '.recipe-pos-insert-button', function(e)
{
e.preventDefault();
var recipePosId = $(e.currentTarget).attr('data-recipe-pos-id').toString();
var recipePosName = $(e.currentTarget).attr('data-recipe-pos-name').toString();
var recipePosAmount = parseFloat($(e.currentTarget).attr('data-recipe-pos-amount').toString());
var recipePosQu = $(e.currentTarget).attr('data-recipe-pos-qu').toString();
var recipePosQuPlural = $(e.currentTarget).attr('data-recipe-pos-qu-plural').toString();
var range = $.summernote.range.create();
bootbox.dialog({
message: '<div>Factor?</div><input type="number" class="form-control" value="1" min="0" max="1">',
size: 'large',
backdrop: true,
closeButton: false,
buttons: {
cancel: {
label: __t('Cancel'),
className: 'btn-secondary responsive-button',
callback: function()
{
bootbox.hideAll();
}
},
ok: {
label: __t('Insert'),
className: 'btn-success responsive-button',
callback: function(e)
{
var factor = $(e.delegateTarget).find('input').val();
var amount = parseFloat(factor) * recipePosAmount;
amount = amount.toFixed(2).replace(/([0-9]+(\.[0-9]+[1-9])?)(\.?0+$)/,'$1');
var html = '&nbsp;<span class="ingredient" data-ingredient-id="' + recipePosId + '" data-ingredient-factor="' + factor + '">' + [amount, __n(amount, recipePosQu, recipePosQuPlural), recipePosName].join(' ') + '</span>&nbsp;';
if (range.so !== range.eo || range.sc !== range.ec)
{
range.deleteContents();
range = $.summernote.range.create();
var check = range.getWordRange(true);
if (check.toString().trim().length === 0)
check.deleteContents();
check = range.getWordRange(false);
if (check.toString().trim().length === 0)
check.deleteContents();
range = $.summernote.range.create();
}
range.pasteHTML(html);
updateIngredientTags();
bootbox.hideAll();
$("#description").summernote('focus');
}
}
}
});
});
$(document).on('click', '.recipe-include-edit-button', function(e)
{
var id = $(e.currentTarget).attr('data-recipe-include-id');
@ -379,6 +438,19 @@ $(window).on("message", function(e)
}
});
function updateIngredientTags()
{
$("#description").siblings(".note-editable").find("span.ingredient").not(".badge").addClass(["badge", "btn", "btn-secondary"]).on("click", function(evt)
{
$.summernote.range.createFromNode(evt.target).select();
});
}
$(".wysiwyg-editor").on("summernote.init", function()
{
updateIngredientTags();
});
updateIngredientTags();
// Grocy.Components.RecipePicker.GetPicker().on('change', function (e)
// {
// var value = Grocy.Components.RecipePicker.GetValue();

View File

@ -37,6 +37,35 @@ if (typeof recipe !== "undefined")
// Scroll to recipe card on mobile
$("#selectedRecipeCard")[0].scrollIntoView();
}
$("#selectedRecipeCard .ingredient").each(function(id, element)
{
var data = $(element).closest(".tab-pane").data("ingredients");
if (!data)
{
$(element).text('<???>');
return;
}
var ingredient = FindObjectInArrayByPropertyValue(data.recipePositions, 'recipe_pos_id', element.dataset.ingredientId);
if (!ingredient)
{
$(element).text('<???>');
}
var product = FindObjectInArrayByPropertyValue(data.products, 'id', ingredient.product_id);
var amount = ingredient.recipe_amount;
if (element.dataset.ingredientFactor && parseFloat(element.dataset.ingredientFactor))
{
amount *= parseFloat(element.dataset.ingredientFactor);
}
amount = amount.toFixed(2).replace(/([0-9]+(\.[0-9]+[1-9])?)(\.?0+$)/,'$1');
var qu = FindObjectInArrayByPropertyValue(data.quantityUnits, 'id', ingredient.qu_id);
if (!qu || !product)
{
$(element).text('<???>');
return;
}
$(element).text([amount, __n(amount, qu.name, qu.name_plural), product.name].join(' '));
});
}
if (GetUriParam("search") !== undefined)

View File

@ -192,6 +192,16 @@
@if($mode == "edit")
@foreach($recipePositions as $recipePosition)
<tr>
@php
$product = FindObjectInArrayByPropertyValue($products, 'id', $recipePosition->product_id);
$productQuConversions = FindAllObjectsInArrayByPropertyValue($quantityUnitConversionsResolved, 'product_id', $product->id);
$productQuConversions = FindAllObjectsInArrayByPropertyValue($productQuConversions, 'from_qu_id', $product->qu_id_stock);
$productQuConversion = FindObjectInArrayByPropertyValue($productQuConversions, 'to_qu_id', $recipePosition->qu_id);
if ($productQuConversion)
{
$recipePosition->amount = $recipePosition->amount * $productQuConversion->factor;
}
@endphp
<td class="fit-content border-right">
<a class="btn btn-sm btn-info recipe-pos-edit-button"
type="button"
@ -200,6 +210,15 @@
data-product-id="{{ $recipePosition->product_id }}">
<i class="fas fa-edit"></i>
</a>
<a class="btn btn-sm btn-info recipe-pos-insert-button"
href="#"
data-recipe-pos-id="{{ $recipePosition->id }}"
data-recipe-pos-name="{{ FindObjectInArrayByPropertyValue($products, 'id', $recipePosition->product_id)->name }}"
data-recipe-pos-amount="{{ $recipePosition->amount }}"
data-recipe-pos-qu="{{ FindObjectInArrayByPropertyValue($quantityunits, 'id', $recipePosition->qu_id)->name }}"
data-recipe-pos-qu-plural="{{ FindObjectInArrayByPropertyValue($quantityunits, 'id', $recipePosition->qu_id)->name_plural }}">
<i class="fas fa-map-marker"></i>
</a>
<a class="btn btn-sm btn-danger recipe-pos-delete-button"
href="#"
data-recipe-pos-id="{{ $recipePosition->id }}"
@ -211,16 +230,6 @@
{{ FindObjectInArrayByPropertyValue($products, 'id', $recipePosition->product_id)->name }}
</td>
<td>
@php
$product = FindObjectInArrayByPropertyValue($products, 'id', $recipePosition->product_id);
$productQuConversions = FindAllObjectsInArrayByPropertyValue($quantityUnitConversionsResolved, 'product_id', $product->id);
$productQuConversions = FindAllObjectsInArrayByPropertyValue($productQuConversions, 'from_qu_id', $product->qu_id_stock);
$productQuConversion = FindObjectInArrayByPropertyValue($productQuConversions, 'to_qu_id', $recipePosition->qu_id);
if ($productQuConversion)
{
$recipePosition->amount = $recipePosition->amount * $productQuConversion->factor;
}
@endphp
@if(!empty($recipePosition->variable_amount))
{{ $recipePosition->variable_amount }}
@else

View File

@ -394,6 +394,7 @@
@endif
<div class="tab-pane @if(count($recipePositionsFiltered) == 0) active @endif"
id="prep-{{ $index }}"
data-ingredients="{{ json_encode(array('recipePositions' => $recipePositionsFiltered, 'products' => $products, 'quantityUnits' => $quantityUnits)) }}"
role="tabpanel">
<div class="mb-2 d-none d-print-block">
<h3 class="mb-0">{{ $__t('Preparation') }}</h3>