grocy/public/viewjs/mealplan.js
Bernd Bestel 91d8eaeb74
Squashed commit
Improve journal pages loading time (new date range filter)
Various small style adjustments (meal plan page and others)
Pulled German translations from Transifex
Show the shopping list total value (closes #1309)
Make it possible to copy recipes (closes #714)
Implemented optional "auto decimal separator for price inputs" (closes #1345)
Removed table grouped column fixed order restriction (closes #1402)
Don't filter out style, class, id attributes of html text (closes #1298)
Added product picture as column on the stock overview page (closes #1283)
Added grocycodes also for chores and batteries (+ camera barcode scanning for /choretracking and /batterytracking, this now closes #221)
2021-07-13 19:29:23 +02:00

997 lines
35 KiB
JavaScript

var firstRender = true;
Grocy.IsMealPlanEntryEditAction = false;
Grocy.MealPlanEntryEditObjectId = -1;
var firstDay = null;
if (!Grocy.CalendarFirstDayOfWeek.isEmpty())
{
firstDay = parseInt(Grocy.CalendarFirstDayOfWeek);
}
if (!Grocy.MealPlanFirstDayOfWeek.isEmpty())
{
firstDay = parseInt(Grocy.MealPlanFirstDayOfWeek);
}
var calendar = $("#calendar").fullCalendar({
"themeSystem": "bootstrap4",
"header": {
"left": "title",
"center": "",
"right": "prev,today,next"
},
"weekNumbers": false,
"eventLimit": false,
"eventSources": fullcalendarEventSources,
"defaultView": ($(window).width() < 768) ? "basicDay" : "basicWeek",
"firstDay": firstDay,
"height": "auto",
"defaultDate": GetUriParam("week"),
"viewRender": function(view)
{
$(".fc-day-header").prepend('\
<div class="btn-group mr-2 my-1"> \
<button type="button" class="btn btn-outline-dark btn-xs add-recipe-button" data-toggle="tooltip" title="' + __t('Add recipe') + '"><i class="fas fa-plus"></i></a></button> \
<button type="button" class="btn btn-outline-dark btn-xs dropdown-toggle dropdown-toggle-split" data-toggle="dropdown"></button> \
<div class="table-inline-menu dropdown-menu"> \
<a class="dropdown-item add-note-button" href="#"><span class="dropdown-item-text">' + __t('Add note') + '</span></a> \
<a class="dropdown-item add-product-button" href="#"><span class="dropdown-item-text">' + __t('Add product') + '</span></a> \
<a class="dropdown-item copy-day-button" href="#"><span class="dropdown-item-text">' + __t('Copy this day') + '</span></a> \
</div> \
</div>');
var weekRecipeName = view.start.year().toString() + "-" + ((view.start.week() - 1).toString().padStart(2, "0")).toString();
var weekRecipe = FindObjectInArrayByPropertyValue(internalRecipes, "name", weekRecipeName);
var weekCosts = 0;
var weekRecipeOrderMissingButtonHtml = "";
var weekRecipeConsumeButtonHtml = "";
var weekCostsHtml = "";
if (weekRecipe !== null)
{
if (Grocy.FeatureFlags.GROCY_FEATURE_FLAG_STOCK_PRICE_TRACKING)
{
weekCosts = FindObjectInArrayByPropertyValue(recipesResolved, "recipe_id", weekRecipe.id).costs;
weekCostsHtml = __t("Week costs") + ': <span class="locale-number locale-number-currency">' + weekCosts.toString() + "</span> ";
}
var weekRecipeOrderMissingButtonDisabledClasses = "";
if (FindObjectInArrayByPropertyValue(recipesResolved, "recipe_id", weekRecipe.id).need_fulfilled_with_shopping_list == 1)
{
weekRecipeOrderMissingButtonDisabledClasses = "disabled";
}
var weekRecipeConsumeButtonDisabledClasses = "";
if (FindObjectInArrayByPropertyValue(recipesResolved, "recipe_id", weekRecipe.id).need_fulfilled == 0 || weekCosts == 0)
{
weekRecipeConsumeButtonDisabledClasses = "disabled";
}
weekRecipeOrderMissingButtonHtml = '<a class="ml-1 btn btn-outline-primary btn-xs recipe-order-missing-button ' + weekRecipeOrderMissingButtonDisabledClasses + '" href="#" data-toggle="tooltip" title="' + __t("Put missing products on shopping list") + '" data-recipe-id="' + weekRecipe.id.toString() + '" data-recipe-name="' + weekRecipe.name + '" data-recipe-type="' + weekRecipe.type + '"><i class="fas fa-cart-plus"></i></a>'
weekRecipeConsumeButtonHtml = '<a class="ml-1 btn btn-outline-success btn-xs recipe-consume-button ' + weekRecipeConsumeButtonDisabledClasses + '" href="#" data-toggle="tooltip" title="' + __t("Consume all ingredients needed by this weeks recipes or products") + '" data-recipe-id="' + weekRecipe.id.toString() + '" data-recipe-name="' + weekRecipe.name + '" data-recipe-type="' + weekRecipe.type + '"><i class="fas fa-utensils"></i></a>'
}
$(".fc-header-toolbar .fc-center").html("<h4>" + weekCostsHtml + weekRecipeOrderMissingButtonHtml + weekRecipeConsumeButtonHtml + "</h4>");
},
"eventRender": function(event, element)
{
element.removeClass("fc-event");
element.addClass("text-center");
element.attr("data-meal-plan-entry", event.mealPlanEntry);
var mealPlanEntry = JSON.parse(event.mealPlanEntry);
var additionalTitleCssClasses = "";
var doneButtonHtml = '<a class="ml-1 btn btn-outline-secondary btn-xs mealplan-entry-done-button" href="#" data-toggle="tooltip" title="' + __t("Mark this item as done") + '" data-mealplan-entry-id="' + mealPlanEntry.id.toString() + '"><i class="fas fa-check"></i></a>';
if (BoolVal(mealPlanEntry.done))
{
additionalTitleCssClasses = "text-strike-through text-muted";
doneButtonHtml = '<a class="ml-1 btn btn-outline-secondary btn-xs mealplan-entry-undone-button" href="#" data-toggle="tooltip" title="' + __t("Mark this item as undone") + '" data-mealplan-entry-id="' + mealPlanEntry.id.toString() + '"><i class="fas fa-undo"></i></a>';
}
if (event.type == "recipe")
{
var recipe = JSON.parse(event.recipe);
if (recipe === null || recipe === undefined)
{
return false;
}
var internalShadowRecipe = FindObjectInArrayByPropertyValue(internalRecipes, "name", mealPlanEntry.day + "#" + mealPlanEntry.id);
var resolvedRecipe = FindObjectInArrayByPropertyValue(recipesResolved, "recipe_id", internalShadowRecipe.id);
element.attr("data-recipe", event.recipe);
var recipeOrderMissingButtonDisabledClasses = "";
if (resolvedRecipe.need_fulfilled_with_shopping_list == 1)
{
recipeOrderMissingButtonDisabledClasses = "disabled";
}
var recipeConsumeButtonDisabledClasses = "";
if (resolvedRecipe.need_fulfilled == 0)
{
recipeConsumeButtonDisabledClasses = "disabled";
}
var fulfillmentInfoHtml = __t('Enough in stock');
var fulfillmentIconHtml = '<i class="fas fa-check text-success"></i>';
if (resolvedRecipe.need_fulfilled != 1)
{
fulfillmentInfoHtml = __t('Not enough in stock');
var fulfillmentIconHtml = '<i class="fas fa-times text-danger"></i>';
}
var costsAndCaloriesPerServing = ""
if (Grocy.FeatureFlags.GROCY_FEATURE_FLAG_STOCK_PRICE_TRACKING)
{
costsAndCaloriesPerServing = '<h5 class="small text-truncate mb-1"><span class="locale-number locale-number-currency">' + resolvedRecipe.costs + '</span> / <span class="locale-number locale-number-generic">' + resolvedRecipe.calories + '</span> kcal ' + __t('per serving') + '</h5>';
}
else
{
costsAndCaloriesPerServing = '<h5 class="small text-truncate mb-1"><span class="locale-number locale-number-generic">' + resolvedRecipe.calories + '</span> kcal ' + __t('per serving') + '</h5>';
}
if (!Grocy.FeatureFlags.GROCY_FEATURE_FLAG_STOCK)
{
fulfillmentIconHtml = "";
fulfillmentInfoHtml = "";
}
element.html('\
<div> \
<h5 class="text-truncate mb-1 cursor-link display-recipe-button ' + additionalTitleCssClasses + '" data-toggle="tooltip" title="' + __t("Display recipe") + '" data-recipe-id="' + recipe.id.toString() + '" data-recipe-name="' + recipe.name + '" data-mealplan-servings="' + mealPlanEntry.recipe_servings + '" data-recipe-type="' + recipe.type + '">' + recipe.name + '</h5> \
<h5 class="small text-truncate mb-1">' + __n(mealPlanEntry.recipe_servings, "%s serving", "%s servings") + '</h5> \
<h5 class="small timeago-contextual text-truncate mb-1">' + fulfillmentIconHtml + " " + fulfillmentInfoHtml + '</h5> \
' + costsAndCaloriesPerServing + ' \
<h5> \
<a class="ml-1 btn btn-outline-info btn-xs edit-meal-plan-entry-button" href="#" data-toggle="tooltip" title="' + __t("Edit this item") + '"><i class="fas fa-edit"></i></a> \
<a class="btn btn-outline-danger btn-xs remove-recipe-button" href="#" data-toggle="tooltip" title="' + __t("Delete this item") + '"><i class="fas fa-trash"></i></a> \
<a class="ml-1 btn btn-outline-primary btn-xs recipe-order-missing-button ' + recipeOrderMissingButtonDisabledClasses + '" href="#" data-toggle="tooltip" title="' + __t("Put missing products on shopping list") + '" data-recipe-id="' + recipe.id.toString() + '" data-mealplan-servings="' + mealPlanEntry.recipe_servings + '" data-recipe-name="' + recipe.name + '" data-recipe-type="' + recipe.type + '"><i class="fas fa-cart-plus"></i></a> \
<a class="btn btn-outline-success btn-xs recipe-consume-button ' + recipeConsumeButtonDisabledClasses + '" href="#" data-toggle="tooltip" title="' + __t("Consume all ingredients needed by this recipe") + '" data-recipe-id="' + internalShadowRecipe.id.toString() + '" data-mealplan-entry-id="' + mealPlanEntry.id.toString() + '" data-recipe-name="' + recipe.name + '" data-recipe-type="' + recipe.type + '"><i class="fas fa-utensils"></i></a> \
' + doneButtonHtml + ' \
</h5> \
</div>');
if (recipe.picture_file_name && !recipe.picture_file_name.isEmpty())
{
element.prepend('<div class="mx-auto mb-1"><img data-src="' + U("/api/files/recipepictures/") + btoa(recipe.picture_file_name) + '?force_serve_as=picture&best_fit_width=400" class="img-fluid rounded-circle lazy"></div>')
}
var dayRecipeName = event.start.format("YYYY-MM-DD");
if (!$("#day-summary-" + dayRecipeName).length) // This runs for every event/recipe, so maybe multiple times per day, so only add the day summary once
{
var dayRecipe = FindObjectInArrayByPropertyValue(internalRecipes, "name", dayRecipeName);
if (dayRecipe != null)
{
var dayRecipeResolved = FindObjectInArrayByPropertyValue(recipesResolved, "recipe_id", dayRecipe.id);
var costsAndCaloriesPerDay = ""
if (Grocy.FeatureFlags.GROCY_FEATURE_FLAG_STOCK_PRICE_TRACKING)
{
costsAndCaloriesPerDay = '<h5 class="small text-truncate"><span class="locale-number locale-number-currency">' + dayRecipeResolved.costs + '</span> / <span class="locale-number locale-number-generic">' + dayRecipeResolved.calories + '</span> kcal ' + __t('per day') + '</h5>';
}
else
{
costsAndCaloriesPerDay = '<h5 class="small text-truncate"><span class="locale-number locale-number-generic">' + dayRecipeResolved.calories + '</span> kcal ' + __t('per day') + '</h5>';
}
$(".fc-day-header[data-date='" + dayRecipeName + "']").append('<h5 id="day-summary-' + dayRecipeName + '" class="small text-truncate border-top pt-1 pb-0">' + costsAndCaloriesPerDay + '</h5>');
}
}
}
if (event.type == "product")
{
var productDetails = JSON.parse(event.productDetails);
if (productDetails === null || productDetails === undefined)
{
return false;
}
if (productDetails.last_price === null)
{
productDetails.last_price = 0;
}
element.attr("data-product-details", event.productDetails);
var productOrderMissingButtonDisabledClasses = "disabled";
if (parseFloat(productDetails.stock_amount_aggregated) < parseFloat(mealPlanEntry.product_amount))
{
productOrderMissingButtonDisabledClasses = "";
}
var productConsumeButtonDisabledClasses = "disabled";
if (parseFloat(productDetails.stock_amount_aggregated) >= parseFloat(mealPlanEntry.product_amount))
{
productConsumeButtonDisabledClasses = "";
}
fulfillmentInfoHtml = __t('Not enough in stock');
var fulfillmentIconHtml = '<i class="fas fa-times text-danger"></i>';
if (parseFloat(productDetails.stock_amount_aggregated) >= parseFloat(mealPlanEntry.product_amount))
{
var fulfillmentInfoHtml = __t('Enough in stock');
var fulfillmentIconHtml = '<i class="fas fa-check text-success"></i>';
}
var costsAndCaloriesPerServing = ""
if (Grocy.FeatureFlags.GROCY_FEATURE_FLAG_STOCK_PRICE_TRACKING)
{
costsAndCaloriesPerServing = '<h5 class="small text-truncate mb-1"><span class="locale-number locale-number-currency">' + productDetails.last_price * mealPlanEntry.product_amount + '</span> / <span class="locale-number locale-number-generic">' + productDetails.product.calories * mealPlanEntry.product_amount + '</span> kcal ' + '</h5>';
}
else
{
costsAndCaloriesPerServing = '<h5 class="small text-truncate mb-1"><span class="locale-number locale-number-generic">' + productDetails.product.calories * mealPlanEntry.product_amount + '</span> kcal ' + '</h5>';
}
element.html('\
<div> \
<h5 class="text-truncate mb-1 cursor-link display-product-button ' + additionalTitleCssClasses + '" data-toggle="tooltip" title="' + __t("Display product") + '" data-product-id="' + productDetails.product.id.toString() + '">' + productDetails.product.name + '</h5> \
<h5 class="small text-truncate mb-1"><span class="locale-number locale-number-quantity-amount">' + mealPlanEntry.product_amount + "</span> " + __n(mealPlanEntry.product_amount, productDetails.quantity_unit_stock.name, productDetails.quantity_unit_stock.name_plural) + '</h5> \
<h5 class="small timeago-contextual text-truncate mb-1">' + fulfillmentIconHtml + " " + fulfillmentInfoHtml + '</h5> \
' + costsAndCaloriesPerServing + ' \
<h5> \
<a class="ml-1 btn btn-outline-danger btn-xs remove-product-button" href="#" data-toggle="tooltip" title="' + __t("Delete this item") + '"><i class="fas fa-trash"></i></a> \
<a class="btn btn-outline-info btn-xs edit-meal-plan-entry-button" href="#" data-toggle="tooltip" title="' + __t("Edit this item") + '"><i class="fas fa-edit"></i></a> \
<a class="ml-1 btn btn-outline-success btn-xs product-consume-button ' + productConsumeButtonDisabledClasses + '" href="#" data-toggle="tooltip" title="' + __t("Consume %1$s of %2$s", parseFloat(mealPlanEntry.product_amount).toLocaleString() + ' ' + __n(mealPlanEntry.product_amount, productDetails.quantity_unit_stock.name, productDetails.quantity_unit_stock.name_plural), productDetails.product.name) + '" data-product-id="' + productDetails.product.id.toString() + '" data-product-name="' + productDetails.product.name + '" data-product-amount="' + mealPlanEntry.product_amount + '" data-mealplan-entry-id="' + mealPlanEntry.id.toString() + '"><i class="fas fa-utensils"></i></a> \
<a class="btn btn-outline-primary btn-xs show-as-dialog-link ' + productOrderMissingButtonDisabledClasses + '" href="' + U("/shoppinglistitem/new?embedded&updateexistingproduct&product=") + mealPlanEntry.product_id + '&amount=' + mealPlanEntry.product_amount + '" data-toggle="tooltip" title="' + __t("Add to shopping list") + '" data-product-id="' + productDetails.product.id.toString() + '" data-product-name="' + productDetails.product.name + '" data-product-amount="' + mealPlanEntry.product_amount + '"><i class="fas fa-cart-plus"></i></a> \
' + doneButtonHtml + ' \
</h5> \
</div>');
if (productDetails.product.picture_file_name && !productDetails.product.picture_file_name.isEmpty())
{
element.prepend('<div class="mx-auto mb-1"><img data-src="' + U("/api/files/productpictures/") + btoa(productDetails.product.picture_file_name) + '?force_serve_as=picture&best_fit_width=400" class="img-fluid rounded-circle lazy"></div>')
}
var dayRecipeName = event.start.format("YYYY-MM-DD");
if (!$("#day-summary-" + dayRecipeName).length) // This runs for every event/recipe, so maybe multiple times per day, so only add the day summary once
{
var dayRecipe = FindObjectInArrayByPropertyValue(internalRecipes, "name", dayRecipeName);
if (dayRecipe != null)
{
var dayRecipeResolved = FindObjectInArrayByPropertyValue(recipesResolved, "recipe_id", dayRecipe.id);
var costsAndCaloriesPerDay = ""
if (Grocy.FeatureFlags.GROCY_FEATURE_FLAG_STOCK_PRICE_TRACKING)
{
costsAndCaloriesPerDay = '<h5 class="small text-truncate"><span class="locale-number locale-number-currency">' + dayRecipeResolved.costs + '</span> / <span class="locale-number locale-number-generic">' + dayRecipeResolved.calories + '</span> kcal ' + __t('per day') + '</h5>';
}
else
{
costsAndCaloriesPerDay = '<h5 class="small text-truncate"><span class="locale-number locale-number-generic">' + dayRecipeResolved.calories + '</span> kcal ' + __t('per day') + '</h5>';
}
$(".fc-day-header[data-date='" + dayRecipeName + "']").append('<h5 id="day-summary-' + dayRecipeName + '" class="small text-truncate border-top pt-1 pb-0">' + costsAndCaloriesPerDay + '</h5>');
}
}
}
else if (event.type == "note")
{
element.html('\
<div> \
<h5 class="text-wrap text-break mb-1 ' + additionalTitleCssClasses + '">' + mealPlanEntry.note + '</h5> \
<h5> \
<a class="ml-1 btn btn-outline-danger btn-xs remove-note-button" href="#" data-toggle="tooltip" title="' + __t("Delete this item") + '"><i class="fas fa-trash"></i></a> \
<a class="btn btn-outline-info btn-xs edit-meal-plan-entry-button" href="#" data-toggle="tooltip" title="' + __t("Delete this item") + '"><i class="fas fa-edit"></i></a> \
' + doneButtonHtml + ' \
</h5> \
</div>');
}
},
"eventAfterAllRender": function(view)
{
UpdateUriParam("week", view.start.format("YYYY-MM-DD"));
if (firstRender)
{
firstRender = false
}
else
{
window.location.reload();
return false;
}
RefreshLocaleNumberDisplay();
LoadImagesLazy();
$('[data-toggle="tooltip"]').tooltip();
if (!Grocy.FeatureFlags.GROCY_FEATURE_FLAG_STOCK)
{
$(".recipe-order-missing-button").addClass("d-none");
$(".recipe-consume-button").addClass("d-none");
}
},
});
$(document).on("click", ".add-recipe-button", function(e)
{
var day = $(this).parent().parent().data("date");
$("#add-recipe-modal-title").text(__t("Add recipe on %s", day.toString()));
$("#day").val(day.toString());
Grocy.Components.RecipePicker.Clear();
$("#add-recipe-modal").modal("show");
Grocy.FrontendHelpers.ValidateForm("add-recipe-form");
Grocy.IsMealPlanEntryEditAction = false;
});
$(document).on("click", ".add-note-button", function(e)
{
var day = $(this).parent().parent().parent().data("date");
$("#add-note-modal-title").text(__t("Add note on %s", day.toString()));
$("#day").val(day.toString());
$("#note").val("");
$("#add-note-modal").modal("show");
Grocy.FrontendHelpers.ValidateForm("add-note-form");
Grocy.IsMealPlanEntryEditAction = false;
});
$(document).on("click", ".add-product-button", function(e)
{
var day = $(this).parent().parent().parent().data("date");
$("#add-product-modal-title").text(__t("Add product on %s", day.toString()));
$("#day").val(day.toString());
Grocy.Components.ProductPicker.Clear();
$("#add-product-modal").modal("show");
Grocy.FrontendHelpers.ValidateForm("add-product-form");
Grocy.IsMealPlanEntryEditAction = false;
});
$(document).on("click", ".edit-meal-plan-entry-button", function(e)
{
var mealPlanEntry = JSON.parse($(this).parents(".fc-h-event:first").attr("data-meal-plan-entry"));
if (mealPlanEntry.type == "recipe")
{
$("#add-recipe-modal-title").text(__t("Edit recipe on %s", mealPlanEntry.day.toString()));
$("#day").val(mealPlanEntry.day.toString());
$("#recipe_servings").val(mealPlanEntry.recipe_servings);
Grocy.Components.RecipePicker.SetId(mealPlanEntry.recipe_id);
$("#add-recipe-modal").modal("show");
Grocy.FrontendHelpers.ValidateForm("add-recipe-form");
}
else if (mealPlanEntry.type == "product")
{
$("#add-product-modal-title").text(__t("Edit product on %s", mealPlanEntry.day.toString()));
$("#day").val(mealPlanEntry.day.toString());
Grocy.Components.ProductPicker.SetId(mealPlanEntry.product_id);
$("#add-product-modal").modal("show");
Grocy.FrontendHelpers.ValidateForm("add-product-form");
Grocy.Components.ProductPicker.GetPicker().trigger("change");
}
else if (mealPlanEntry.type == "note")
{
$("#add-note-modal-title").text(__t("Edit note on %s", mealPlanEntry.day.toString()));
$("#day").val(mealPlanEntry.day.toString());
$("#note").val(mealPlanEntry.note);
$("#add-note-modal").modal("show");
Grocy.FrontendHelpers.ValidateForm("add-note-form");
}
Grocy.IsMealPlanEntryEditAction = true;
Grocy.MealPlanEntryEditObjectId = mealPlanEntry.id;
});
$(document).on("click", ".copy-day-button", function(e)
{
var day = $(this).parent().parent().parent().data("date");
$("#copy-day-modal-title").text(__t("Copy all meal plan entries of %s", day.toString()));
$("#day").val(day.toString());
Grocy.Components.DateTimePicker.Clear();
$("#copy-day-modal").modal("show");
Grocy.FrontendHelpers.ValidateForm("copy-day-form");
Grocy.IsMealPlanEntryEditAction = false;
});
$("#add-recipe-modal").on("shown.bs.modal", function(e)
{
Grocy.Components.RecipePicker.GetInputElement().focus();
})
$("#add-note-modal").on("shown.bs.modal", function(e)
{
$("#note").focus();
})
$("#add-product-modal").on("shown.bs.modal", function(e)
{
Grocy.Components.ProductPicker.GetInputElement().focus();
})
$("#copy-day-modal").on("shown.bs.modal", function(e)
{
Grocy.Components.DateTimePicker.GetInputElement().focus();
})
$(document).on("click", ".remove-recipe-button, .remove-note-button, .remove-product-button", function(e)
{
var mealPlanEntry = JSON.parse($(this).parents(".fc-h-event:first").attr("data-meal-plan-entry"));
Grocy.Api.Delete('objects/meal_plan/' + mealPlanEntry.id.toString(), {},
function(result)
{
window.location.reload();
},
function(xhr)
{
Grocy.FrontendHelpers.ShowGenericError('Error while saving, probably this item already exists', xhr.response)
}
);
});
$('#save-add-recipe-button').on('click', function(e)
{
e.preventDefault();
if ($(".combobox-menu-visible").length)
{
return;
}
if (document.getElementById("add-recipe-form").checkValidity() === false) //There is at least one validation error
{
return false;
}
if (Grocy.IsMealPlanEntryEditAction)
{
Grocy.Api.Put('objects/meal_plan/' + Grocy.MealPlanEntryEditObjectId.toString(), $('#add-recipe-form').serializeJSON(),
function(result)
{
window.location.reload();
},
function(xhr)
{
Grocy.FrontendHelpers.ShowGenericError('Error while saving, probably this item already exists', xhr.response)
}
);
}
else
{
Grocy.Api.Post('objects/meal_plan', $('#add-recipe-form').serializeJSON(),
function(result)
{
window.location.reload();
},
function(xhr)
{
Grocy.FrontendHelpers.ShowGenericError('Error while saving, probably this item already exists', xhr.response)
}
);
}
});
$('#save-add-note-button').on('click', function(e)
{
e.preventDefault();
if ($(".combobox-menu-visible").length)
{
return;
}
if (document.getElementById("add-note-form").checkValidity() === false) //There is at least one validation error
{
return false;
}
var jsonData = $('#add-note-form').serializeJSON();
jsonData.day = $("#day").val();
if (Grocy.IsMealPlanEntryEditAction)
{
Grocy.Api.Put('objects/meal_plan/' + Grocy.MealPlanEntryEditObjectId.toString(), jsonData,
function(result)
{
window.location.reload();
},
function(xhr)
{
Grocy.FrontendHelpers.ShowGenericError('Error while saving, probably this item already exists', xhr.response)
}
);
}
else
{
Grocy.Api.Post('objects/meal_plan', jsonData,
function(result)
{
window.location.reload();
},
function(xhr)
{
Grocy.FrontendHelpers.ShowGenericError('Error while saving, probably this item already exists', xhr.response)
}
);
}
});
$('#save-add-product-button').on('click', function(e)
{
e.preventDefault();
if ($(".combobox-menu-visible").length)
{
return;
}
if (document.getElementById("add-product-form").checkValidity() === false) //There is at least one validation error
{
return false;
}
var jsonData = $('#add-product-form').serializeJSON();
jsonData.day = $("#day").val();
delete jsonData.display_amount;
jsonData.product_amount = jsonData.amount;
delete jsonData.amount;
jsonData.product_qu_id = $("#qu_id").val();;
delete jsonData.qu_id;
if (Grocy.IsMealPlanEntryEditAction)
{
Grocy.Api.Put('objects/meal_plan/' + Grocy.MealPlanEntryEditObjectId.toString(), jsonData,
function(result)
{
window.location.reload();
},
function(xhr)
{
Grocy.FrontendHelpers.ShowGenericError('Error while saving, probably this item already exists', xhr.response)
}
);
}
else
{
Grocy.Api.Post('objects/meal_plan', jsonData,
function(result)
{
window.location.reload();
},
function(xhr)
{
Grocy.FrontendHelpers.ShowGenericError('Error while saving, probably this item already exists', xhr.response)
}
);
}
});
var itemsToCopy = 0;
var itemsCopied = 0;
$('#save-copy-day-button').on('click', function(e)
{
e.preventDefault();
if (document.getElementById("copy-day-form").checkValidity() === false) //There is at least one validation error
{
return false;
}
var dayFrom = $("#day").val();
var dayTo = Grocy.Components.DateTimePicker.GetValue();
Grocy.Api.Get('objects/meal_plan?query[]=day=' + dayFrom,
function(sourceMealPlanEntries)
{
itemsToCopy = sourceMealPlanEntries.length;
sourceMealPlanEntries.forEach((item) =>
{
item.day = dayTo;
item.done = 0;
delete item.id;
delete item.row_created_timestamp;
Grocy.Api.Post("objects/meal_plan", item,
function(result)
{
itemsCopied++;
if (itemsCopied == itemsToCopy)
{
window.location.reload();
}
},
function(xhr)
{
Grocy.FrontendHelpers.ShowGenericError('Error while saving, probably this item already exists', xhr.response);
}
);
});
//window.location.reload();
},
function(xhr)
{
console.error(xhr);
}
);
});
$('#add-recipe-form input').keydown(function(event)
{
if (event.keyCode === 13) //Enter
{
event.preventDefault();
if (document.getElementById("add-recipe-form").checkValidity() === false) //There is at least one validation error
{
return false;
}
else
{
$("#save-add-recipe-button").click();
}
}
});
$('#add-product-form input').keydown(function(event)
{
if (event.keyCode === 13) //Enter
{
event.preventDefault();
if (document.getElementById("add-product-form").checkValidity() === false) //There is at least one validation error
{
return false;
}
else
{
$("#save-add-product-button").click();
}
}
});
$(document).on("keydown", "#servings", function(e)
{
if (event.keyCode === 13) //Enter
{
event.preventDefault();
if (document.getElementById("add-recipe-form").checkValidity() === false) //There is at least one validation error
{
return false;
}
else
{
$("#save-add-recipe-button").click();
}
}
});
$(document).on('click', '.recipe-order-missing-button', function(e)
{
// Remove the focus from the current button
// to prevent that the tooltip stays until clicked anywhere else
document.activeElement.blur();
var objectName = $(e.currentTarget).attr('data-recipe-name');
var objectId = $(e.currentTarget).attr('data-recipe-id');
var button = $(this);
var servings = $(e.currentTarget).attr('data-mealplan-servings');
bootbox.confirm({
message: __t('Are you sure to put all missing ingredients for recipe "%s" on the shopping list?', objectName),
closeButton: false,
buttons: {
confirm: {
label: __t('Yes'),
className: 'btn-success'
},
cancel: {
label: __t('No'),
className: 'btn-danger'
}
},
callback: function(result)
{
if (result === true)
{
Grocy.FrontendHelpers.BeginUiBusy();
// Set the recipes desired_servings so that the "recipes resolved"-views resolve correctly based on the meal plan entry servings
Grocy.Api.Put('objects/recipes/' + objectId, { "desired_servings": servings },
function(result)
{
Grocy.Api.Post('recipes/' + objectId + '/add-not-fulfilled-products-to-shoppinglist', {},
function(result)
{
if (button.attr("data-recipe-type") == "normal")
{
button.addClass("disabled");
Grocy.FrontendHelpers.EndUiBusy();
}
else
{
window.location.reload();
}
},
function(xhr)
{
Grocy.FrontendHelpers.EndUiBusy();
console.error(xhr);
}
);
},
function(xhr)
{
console.error(xhr);
}
);
}
}
});
});
$(document).on('click', '.product-consume-button', function(e)
{
e.preventDefault();
// Remove the focus from the current button
// to prevent that the tooltip stays until clicked anywhere else
document.activeElement.blur();
Grocy.FrontendHelpers.BeginUiBusy();
var productId = $(e.currentTarget).attr('data-product-id');
var consumeAmount = parseFloat($(e.currentTarget).attr('data-product-amount'));
var mealPlanEntryId = $(e.currentTarget).attr('data-mealplan-entry-id');
Grocy.Api.Post('stock/products/' + productId + '/consume', { 'amount': consumeAmount, 'spoiled': false },
function(bookingResponse)
{
Grocy.Api.Get('stock/products/' + productId,
function(result)
{
var toastMessage = __t('Removed %1$s of %2$s from stock', consumeAmount.toString() + " " + __n(consumeAmount, result.quantity_unit_stock.name, result.quantity_unit_stock.name_plural), result.product.name) + '<br><a class="btn btn-secondary btn-sm mt-2" href="#" onclick="UndoStockTransaction(\'' + bookingResponse[0].transaction_id + '\')"><i class="fas fa-undo"></i> ' + __t("Undo") + '</a>';
Grocy.Api.Put('objects/meal_plan/' + mealPlanEntryId, { "done": 1 },
function(result)
{
Grocy.FrontendHelpers.EndUiBusy();
toastr.success(toastMessage);
window.location.reload();
},
function(xhr)
{
Grocy.FrontendHelpers.ShowGenericError('Error while saving, probably this item already exists', xhr.response)
}
);
},
function(xhr)
{
Grocy.FrontendHelpers.EndUiBusy();
console.error(xhr);
}
);
},
function(xhr)
{
Grocy.FrontendHelpers.EndUiBusy();
console.error(xhr);
}
);
});
$(document).on('click', '.recipe-consume-button', function(e)
{
// Remove the focus from the current button
// to prevent that the tooltip stays until clicked anywhere else
document.activeElement.blur();
var objectName = $(e.currentTarget).attr('data-recipe-name');
var objectId = $(e.currentTarget).attr('data-recipe-id');
var mealPlanEntryId = $(e.currentTarget).attr('data-mealplan-entry-id');
bootbox.confirm({
message: __t('Are you sure to consume all ingredients needed by recipe "%s" (ingredients marked with "only check if any amount is in stock" will be ignored)?', objectName),
closeButton: false,
buttons: {
confirm: {
label: __t('Yes'),
className: 'btn-success'
},
cancel: {
label: __t('No'),
className: 'btn-danger'
}
},
callback: function(result)
{
if (result === true)
{
Grocy.FrontendHelpers.BeginUiBusy();
Grocy.Api.Post('recipes/' + objectId + '/consume', {},
function(result)
{
Grocy.Api.Put('objects/meal_plan/' + mealPlanEntryId, { "done": 1 },
function(result)
{
Grocy.FrontendHelpers.EndUiBusy();
toastr.success(__t('Removed all ingredients of recipe "%s" from stock', objectName));
window.location.reload();
},
function(xhr)
{
Grocy.FrontendHelpers.ShowGenericError('Error while saving, probably this item already exists', xhr.response)
}
);
},
function(xhr)
{
toastr.warning(__t('Not all ingredients of recipe "%s" are in stock, nothing removed', objectName));
Grocy.FrontendHelpers.EndUiBusy();
console.error(xhr);
}
);
}
}
});
});
$(document).on("click", ".display-recipe-button", function(e)
{
// Remove the focus from the current button
// to prevent that the tooltip stays until clicked anywhere else
document.activeElement.blur();
var objectId = $(e.currentTarget).attr('data-recipe-id');
var servings = $(e.currentTarget).attr('data-mealplan-servings');
// Set the recipes desired_servings so that the "recipes resolved"-views resolve correctly based on the meal plan entry servings
Grocy.Api.Put('objects/recipes/' + objectId, { "desired_servings": servings },
function(result)
{
bootbox.dialog({
message: '<iframe height="650px" class="embed-responsive" src="' + U("/recipes?embedded&recipe=") + objectId + '#fullscreen"></iframe>',
size: 'extra-large',
backdrop: true,
closeButton: false,
buttons: {
cancel: {
label: __t('Close'),
className: 'btn-secondary responsive-button',
callback: function()
{
bootbox.hideAll();
}
}
}
});
},
function(xhr)
{
console.error(xhr);
}
);
});
$(document).on("click", ".display-product-button", function(e)
{
// Remove the focus from the current button
// to prevent that the tooltip stays until clicked anywhere else
document.activeElement.blur();
Grocy.Components.ProductCard.Refresh($(e.currentTarget).attr('data-product-id'));
$("#mealplan-productcard-modal").modal("show");
});
$(document).on("click", ".mealplan-entry-done-button", function(e)
{
e.preventDefault();
// Remove the focus from the current button
// to prevent that the tooltip stays until clicked anywhere else
document.activeElement.blur();
var mealPlanEntryId = $(e.currentTarget).attr("data-mealplan-entry-id");
Grocy.Api.Put("objects/meal_plan/" + mealPlanEntryId, { "done": 1 },
function(result)
{
window.location.reload();
},
function(xhr)
{
Grocy.FrontendHelpers.ShowGenericError('Error while saving, probably this item already exists', xhr.response)
}
);
});
$(document).on("click", ".mealplan-entry-undone-button", function(e)
{
e.preventDefault();
// Remove the focus from the current button
// to prevent that the tooltip stays until clicked anywhere else
document.activeElement.blur();
var mealPlanEntryId = $(e.currentTarget).attr("data-mealplan-entry-id");
Grocy.Api.Put("objects/meal_plan/" + mealPlanEntryId, { "done": 0 },
function(result)
{
window.location.reload();
},
function(xhr)
{
Grocy.FrontendHelpers.ShowGenericError('Error while saving, probably this item already exists', xhr.response)
}
);
});
$(window).one("resize", function()
{
// Automatically switch the calendar to "basicDay" view on small screens
// and to "basicWeek" otherwise
if ($(window).width() < 768)
{
calendar.fullCalendar("changeView", "basicDay");
}
else
{
calendar.fullCalendar("changeView", "basicWeek");
}
});
Grocy.Components.ProductPicker.GetPicker().on('change', function(e)
{
var productId = $(e.target).val();
if (productId)
{
Grocy.Api.Get('stock/products/' + productId,
function(productDetails)
{
Grocy.Components.ProductAmountPicker.Reload(productDetails.product.id, productDetails.quantity_unit_stock.id);
Grocy.Components.ProductAmountPicker.SetQuantityUnit(productDetails.quantity_unit_stock.id);
$('#display_amount').val(1);
RefreshLocaleNumberInput();
$('#display_amount').focus();
$('#display_amount').select();
$(".input-group-productamountpicker").trigger("change");
Grocy.FrontendHelpers.ValidateForm('add-product-form');
},
function(xhr)
{
console.error(xhr);
}
);
}
});
function UndoStockTransaction(transactionId)
{
Grocy.Api.Post('stock/transactions/' + transactionId.toString() + '/undo', {},
function(result)
{
toastr.success(__t("Transaction successfully undone"));
},
function(xhr)
{
console.error(xhr);
}
);
};
Grocy.Components.RecipePicker.GetPicker().on('change', function(e)
{
var recipeId = $(e.target).val();
if (recipeId)
{
Grocy.Api.Get('objects/recipes/' + recipeId,
function(recipe)
{
$("#recipe_servings").val(recipe.base_servings);
$("#recipe_servings").focus();
$("#recipe_servings").select();
},
function(xhr)
{
console.error(xhr);
}
);
}
});