diff --git a/changelog/77_UNRELEASED_xxxx-xx-xx.md b/changelog/77_UNRELEASED_xxxx-xx-xx.md
index dee98563..b4a3c4a1 100644
--- a/changelog/77_UNRELEASED_xxxx-xx-xx.md
+++ b/changelog/77_UNRELEASED_xxxx-xx-xx.md
@@ -34,6 +34,7 @@
- Optimized that when adding missing recipe ingredients with the option "Only check if any amount is in stock" enabled to the shopping list and when no corresponding unit conversion exists, the amount/unit is now taken "as is" (as defined in the recipe ingredient) into the created shopping list item
- Added a trendline to the price history chart (product card)
+- Added a "Add to shopping list"-button on the product card
- Added a new stock setting (top right corner settings menu) "Show all out of stock products" to optionally also show all out of stock products on the stock overview page (defaults to disabled, so no changed behavior when not configured)
- By default the stock overview page lists all products which are currently in-stock or below their min. stock amount
- When this new setting is enabled, all (active) products are always shown
diff --git a/migrations/0245.sql b/migrations/0245.sql
index 1f892552..2e96bd2c 100644
--- a/migrations/0245.sql
+++ b/migrations/0245.sql
@@ -9,7 +9,7 @@ SELECT
sc.amount AS amount,
sc.value as value,
sc.product_id AS product_id,
- CASE WHEN sc.is_in_stock_or_below_min_stock = 1 THEN sc.best_before_date ELSE '2888-12-31' END AS best_before_date,
+ IFNULL(sc.best_before_date, '2888-12-31') AS best_before_date,
EXISTS(SELECT id FROM stock_missing_products WHERE id = sc.product_id) AS product_missing,
p.name AS product_name,
pg.name AS product_group_name,
diff --git a/public/js/grocy_summernote.js b/public/js/grocy_summernote.js
index 51d10925..f75d141f 100644
--- a/public/js/grocy_summernote.js
+++ b/public/js/grocy_summernote.js
@@ -9,7 +9,16 @@ $("textarea.wysiwyg-editor").summernote({
$img = $('
').attr({ src: url, class: "img-fluid", loading: "lazy" })
$(this).summernote("insertNode", $img[0]);
}
- }
+ },
+ toolbar: [
+ ['fontsize', ['fontsize']],
+ ['font', ['bold', 'underline', 'clear']],
+ ['color', ['color']],
+ ['para', ['ul', 'ol', 'paragraph']],
+ ['table', ['table']],
+ ['insert', ['link', 'picture', 'video']],
+ ['view', ['codeview', 'fullscreen']]
+ ]
});
// Summernote workaround: Make embeds responsive
diff --git a/public/viewjs/barcodescannertesting.js b/public/viewjs/barcodescannertesting.js
index d5b115ee..ab6e0ef0 100644
--- a/public/viewjs/barcodescannertesting.js
+++ b/public/viewjs/barcodescannertesting.js
@@ -62,7 +62,7 @@ function OnBarcodeScanned(barcode)
bgClass = "bg-danger";
$("#miss-count").text(Grocy.BarCodeScannerTestingMissCount);
- animateCSS("#miss-count", "pulse");
+ animateCSS("#miss-count", "flash");
}
else
{
@@ -70,7 +70,7 @@ function OnBarcodeScanned(barcode)
bgClass = "bg-success";
$("#hit-count").text(Grocy.BarCodeScannerTestingHitCount);
- animateCSS("#hit-count", "pulse");
+ animateCSS("#hit-count", "flash");
}
$("#scanned_codes").prepend("");
diff --git a/public/viewjs/batteriesoverview.js b/public/viewjs/batteriesoverview.js
index 4727510a..37c46e87 100644
--- a/public/viewjs/batteriesoverview.js
+++ b/public/viewjs/batteriesoverview.js
@@ -84,7 +84,7 @@ $(document).on('click', '.track-charge-cycle-button', function(e)
batteryRow.addClass("table-warning");
}
- animateCSS("#battery-" + batteryId + "-row td:not(:first)", "shake");
+ animateCSS("#battery-" + batteryId + "-row td:not(:first)", "flash");
$('#battery-' + batteryId + '-last-tracked-time').text(trackedTime);
$('#battery-' + batteryId + '-last-tracked-time-timeago').attr('datetime', trackedTime);
diff --git a/public/viewjs/choresoverview.js b/public/viewjs/choresoverview.js
index de1734c0..3d300713 100644
--- a/public/viewjs/choresoverview.js
+++ b/public/viewjs/choresoverview.js
@@ -146,7 +146,7 @@ $(document).on('click', '.track-chore-button', function(e)
$('#chore-' + choreId + '-due-filter-column').html("duesoon");
}
- animateCSS("#chore-" + choreId + "-row td:not(:first)", "shake");
+ animateCSS("#chore-" + choreId + "-row td:not(:first)", "flash");
$('#chore-' + choreId + '-last-tracked-time').text(trackedTime);
$('#chore-' + choreId + '-last-tracked-time-timeago').attr('datetime', trackedTime);
diff --git a/public/viewjs/components/productcard.js b/public/viewjs/components/productcard.js
index 61f6f5ca..87ae587e 100644
--- a/public/viewjs/components/productcard.js
+++ b/public/viewjs/components/productcard.js
@@ -77,10 +77,12 @@ Grocy.Components.ProductCard.Refresh = function(productId)
$('#productcard-product-edit-button').attr("href", U("/product/" + productDetails.product.id.toString() + '?' + 'returnto=' + encodeURIComponent(Grocy.CurrentUrlRelative)));
$('#productcard-product-journal-button').attr("href", U("/stockjournal?embedded&product=" + productDetails.product.id.toString()));
+ $('#productcard-product-shoppinglist-button').attr("href", U("/shoppinglistitem/new?embedded&updateexistingproduct&list=1&product=" + productDetails.product.id.toString()));
$('#productcard-product-stock-button').attr("href", U("/stockentries?embedded&product=" + productDetails.product.id.toString()));
$('#productcard-product-stock-button').removeClass("disabled");
$('#productcard-product-edit-button').removeClass("disabled");
$('#productcard-product-journal-button').removeClass("disabled");
+ $('#productcard-product-shoppinglist-button').removeClass("disabled");
if (productDetails.last_price !== null)
{
diff --git a/public/viewjs/quantityunitpluraltesting.js b/public/viewjs/quantityunitpluraltesting.js
index 9cd04449..4c6847ae 100644
--- a/public/viewjs/quantityunitpluraltesting.js
+++ b/public/viewjs/quantityunitpluraltesting.js
@@ -24,7 +24,7 @@ function RefreshQuPluralTestingResult()
return;
}
- animateCSS("h2", "shake");
+ animateCSS("h2", "flash");
$("#result").text(__n(amount, singularForm, pluralForm, true));
}
diff --git a/public/viewjs/stockentries.js b/public/viewjs/stockentries.js
index 81eac38e..1d442a0c 100644
--- a/public/viewjs/stockentries.js
+++ b/public/viewjs/stockentries.js
@@ -219,7 +219,7 @@ function RefreshStockEntryRow(stockRowId)
stockRow.addClass("table-warning");
}
- animateCSS("#stock-" + stockRowId + "-row td:not(:first)", "shake");
+ animateCSS("#stock-" + stockRowId + "-row td:not(:first)", "flash");
$('#stock-' + stockRowId + '-amount').text(result.amount);
$('#stock-' + stockRowId + '-due-date').text(result.best_before_date);
@@ -317,8 +317,11 @@ $(window).on("message", function(e)
if (data.Message == "ProductChanged")
{
- window.location.reload();
- }
+ $(".stock-consume-button[data-product-id='" + data.Payload + "']").each(function()
+ {
+ RefreshStockEntryRow($(this).attr("data-stockrow-id"));
+ });
+ };
});
Grocy.Components.ProductPicker.GetPicker().trigger('change');
diff --git a/public/viewjs/stockoverview.js b/public/viewjs/stockoverview.js
index e288a92c..efa3a532 100755
--- a/public/viewjs/stockoverview.js
+++ b/public/viewjs/stockoverview.js
@@ -335,7 +335,7 @@ function RefreshProductRow(productId)
}
else
{
- animateCSS("#product-" + productId + "-row td:not(:first)", "shake");
+ animateCSS("#product-" + productId + "-row td:not(:first)", "flash");
$('#product-' + productId + '-qu-name').text(__n(result.stock_amount, result.quantity_unit_stock.name, result.quantity_unit_stock.name_plural, true));
$('#product-' + productId + '-amount').text(result.stock_amount);
@@ -359,6 +359,11 @@ function RefreshProductRow(productId)
$(".product-consume-button[data-product-id='" + productId + "']").addClass("disabled");
$(".product-open-button[data-product-id='" + productId + "']").addClass("disabled");
}
+ else
+ {
+ $(".product-consume-button[data-product-id='" + productId + "']").removeClass("disabled");
+ $(".product-open-button[data-product-id='" + productId + "']").removeClass("disabled");
+ }
}
$('#product-' + productId + '-next-due-date').text(result.next_due_date);
diff --git a/views/components/productcard.blade.php b/views/components/productcard.blade.php
index 9536e131..4e0fe6f3 100644
--- a/views/components/productcard.blade.php
+++ b/views/components/productcard.blade.php
@@ -27,6 +27,13 @@
title="{{ $__t('Edit product') }}">
+
+
+