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