diff --git a/changelog/62_UNRELEASED_xxxx-xx-xx.md b/changelog/62_UNRELEASED_xxxx-xx-xx.md index 5d8afdfc..765cbd27 100644 --- a/changelog/62_UNRELEASED_xxxx-xx-xx.md +++ b/changelog/62_UNRELEASED_xxxx-xx-xx.md @@ -64,8 +64,10 @@ - Fixed that the ingredient amount calculation for included/nested recipes was (for most cases) wrong - Fixed that the ingredient amount was wrong when using a to the product indirectly related quantity unit -### Meal plan fixes +### Meal plan improvements/fixes - Improved the meal plan page loading time (drastically when having a big history of meal plan entries) +- Meal plan entries can now be visually marked as "done" (new button per entry) + - This happens automatically on consuming a recipe/product from the meal plan page - Fixed that stock fulfillment checking used the desired servings of the recipe (those selected on the recipes page, not them from the meal plan entry) ### Chores fixes diff --git a/localization/strings.pot b/localization/strings.pot index cc8ae0c6..ad9acd4b 100644 --- a/localization/strings.pot +++ b/localization/strings.pot @@ -2154,3 +2154,6 @@ msgstr "" # Example: *3.21 USD* per *Pack* msgid "%1$s per %2$s" msgstr "" + +msgid "Mark this item as undone" +msgstr "" diff --git a/migrations/0142.sql b/migrations/0142.sql new file mode 100644 index 00000000..fab1b671 --- /dev/null +++ b/migrations/0142.sql @@ -0,0 +1,2 @@ +ALTER TABLE meal_plan +ADD done TINYINT NOT NULL DEFAULT 0 CHECK(done IN (0, 1)); diff --git a/public/viewjs/mealplan.js b/public/viewjs/mealplan.js index 19c484a1..274cc0c6 100644 --- a/public/viewjs/mealplan.js +++ b/public/viewjs/mealplan.js @@ -77,6 +77,14 @@ var calendar = $("#calendar").fullCalendar({ var mealPlanEntry = JSON.parse(event.mealPlanEntry); + var additionalTitleCssClasses = ""; + var doneButtonHtml = ''; + if (BoolVal(mealPlanEntry.done)) + { + additionalTitleCssClasses = "text-strike-through text-muted"; + doneButtonHtml = ''; + } + if (event.type == "recipe") { var recipe = JSON.parse(event.recipe); @@ -127,16 +135,17 @@ var calendar = $("#calendar").fullCalendar({ element.html('\
\ -
' + recipe.name + '
\ +
' + recipe.name + '
\
' + __n(mealPlanEntry.recipe_servings, "%s serving", "%s servings") + '
\
' + fulfillmentIconHtml + " " + fulfillmentInfoHtml + '
\ ' + costsAndCaloriesPerServing + ' \
\ \ - \ + \ \ - \ + \ \ + ' + doneButtonHtml + ' \
\
'); @@ -213,15 +222,16 @@ var calendar = $("#calendar").fullCalendar({ element.html('\
\ -
' + productDetails.product.name + '
\ +
' + productDetails.product.name + '
\
' + mealPlanEntry.product_amount + " " + __n(mealPlanEntry.product_amount, productDetails.quantity_unit_stock.name, productDetails.quantity_unit_stock.name_plural) + '
\
' + fulfillmentIconHtml + " " + fulfillmentInfoHtml + '
\ ' + costsAndCaloriesPerServing + ' \
\ \ - \ - \ - \ + \ + \ + \ + ' + doneButtonHtml + ' \
\
'); @@ -255,10 +265,11 @@ var calendar = $("#calendar").fullCalendar({ { element.html('\
\ -
' + mealPlanEntry.note + '
\ +
' + mealPlanEntry.note + '
\
\ \ - \ + \ + ' + doneButtonHtml + ' \
\
'); } @@ -654,6 +665,7 @@ $(document).on('click', '.product-consume-button', function(e) 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) @@ -663,9 +675,18 @@ $(document).on('click', '.product-consume-button', function(e) { 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) + '
' + __t("Undo") + ''; - Grocy.FrontendHelpers.EndUiBusy(); - toastr.success(toastMessage); - window.location.reload(); + 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) { @@ -690,7 +711,7 @@ $(document).on('click', '.recipe-consume-button', function(e) var objectName = $(e.currentTarget).attr('data-recipe-name'); var objectId = $(e.currentTarget).attr('data-recipe-id'); - var servings = $(e.currentTarget).attr('data-mealplan-servings'); + 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), @@ -711,11 +732,10 @@ $(document).on('click', '.recipe-consume-button', function(e) { 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 }, + Grocy.Api.Post('recipes/' + objectId + '/consume', {}, function(result) { - Grocy.Api.Post('recipes/' + objectId + '/consume', {}, + Grocy.Api.Put('objects/meal_plan/' + mealPlanEntryId, { "done": 1 }, function(result) { Grocy.FrontendHelpers.EndUiBusy(); @@ -724,14 +744,14 @@ $(document).on('click', '.recipe-consume-button', function(e) }, function(xhr) { - toastr.warning(__t('Not all ingredients of recipe "%s" are in stock, nothing removed', objectName)); - Grocy.FrontendHelpers.EndUiBusy(); - console.error(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); } ); @@ -777,6 +797,48 @@ $(document).on("click", ".recipe-popup-button", function(e) ); }); +$(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