diff --git a/changelog/52_2.5.0_2019-09-22.md b/changelog/52_2.5.0_2019-09-22.md
index 5334feb5..feb38458 100644
--- a/changelog/52_2.5.0_2019-09-22.md
+++ b/changelog/52_2.5.0_2019-09-22.md
@@ -67,7 +67,7 @@
### Userfield improvements/fixes
- New Userfield type "Select list" for a list of predefined values where a single or also multiple values can then be selected on the entity object
- New Userfield type "Link" - a single-line-textbox where the content will be rendered as a clickable link
-- Userfields of type "checkbox" are rendered as a checkmark in tables when checked (instead of "1" as till now)
+- Userfields of type "Checkbox" are rendered as a checkmark in tables when checked (instead of "1" as till now)
- Product Userfields are now also rendered on the shopping list (for items which have a product referenced)
- Fixed that the Userfield type "Preset list" had always the caption "Product group" instead of the configured one (thanks @oncleben31)
diff --git a/changelog/77_UNRELEASED_xxxx-xx-xx.md b/changelog/77_UNRELEASED_xxxx-xx-xx.md
index 324b967e..1b0d7ae2 100644
--- a/changelog/77_UNRELEASED_xxxx-xx-xx.md
+++ b/changelog/77_UNRELEASED_xxxx-xx-xx.md
@@ -32,6 +32,7 @@
### Recipes
- 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)
- Fixed that calories/costs of recipe ingredients were wrong when the ingredient option "Only check if any amount is in stock" was set and the on the ingredient used quantity unit was different from the product's QU stock
- Fixed that multi-nested recipes (at least 3 levels of "included recipes") resulted in wrong amounts/costs/calories calculated for the ingredients orginating in those nested recipes (also affected the meal plan)
@@ -61,6 +62,8 @@
### Userfields
+- Optimized Userfields of type "Checkbox"
+ - When it's a mandatory Userfield, the initial state of the corresponding checkbox is now indeterminate, means it's now also possible to actively not check it (previously mandatory meant the checkbox has to be set)
- Fixed that Userfield default values were not initialized for the `stock` entity (so affecting the purchase and inventory page)
- Fixed that uploading bigger or multiple files (so when the upload usually takes a little longer) didn't work (Userfield type "File")
diff --git a/config-dist.php b/config-dist.php
index e763c5e2..2fc115bf 100644
--- a/config-dist.php
+++ b/config-dist.php
@@ -221,11 +221,12 @@ DefaultUserSetting('calendar_color_chores', '#ffc107'); // The event color (hex
DefaultUserSetting('calendar_color_batteries', '#17a2b8'); // The event color (hex code) for due battery charge cycles
DefaultUserSetting('calendar_color_meal_plan', '#6c757d'); // The event color (hex code) for meal plan items
-// Component configuration for Quagga2 - read https://github.com/ericblade/quagga2#configobject for details
+// Component configuration for Quagga2
+// See https://github.com/ericblade/quagga2#configobject for details
// Below is a generic good configuration,
-// for an iPhone 7 Plus, halfsample = true, patchsize = small, frequency = 5 yields very good results
+// For an iPhone 7 Plus, halfsample = true, patchsize = small, frequency = 5 yields very good results
DefaultUserSetting('quagga2_numofworkers', 4);
DefaultUserSetting('quagga2_halfsample', false);
DefaultUserSetting('quagga2_patchsize', 'medium');
DefaultUserSetting('quagga2_frequency', 10);
-DefaultUserSetting('quagga2_debug', true);
+DefaultUserSetting('quagga2_debug', false);
diff --git a/localization/strings.pot b/localization/strings.pot
index f0e77718..6b15a4fa 100644
--- a/localization/strings.pot
+++ b/localization/strings.pot
@@ -836,7 +836,9 @@ msgid "Not opened"
msgstr ""
msgid "Opened"
-msgstr ""
+msgid_plural "Opened"
+msgstr[0] ""
+msgstr[1] ""
msgid "%s opened"
msgstr ""
diff --git a/package.json b/package.json
index e719e6cb..da3e1849 100644
--- a/package.json
+++ b/package.json
@@ -15,6 +15,7 @@
"chartjs-plugin-colorschemes": "^0.4.0",
"chartjs-plugin-doughnutlabel": "^2.0.3",
"chartjs-plugin-piechart-outlabels": "^0.1.4",
+ "chartjs-plugin-trendline": "^2.1.6",
"datatables.net": "^1.10.22",
"datatables.net-bs4": "^1.10.22",
"datatables.net-colreorder": "^1.5.2",
diff --git a/public/css/grocy.css b/public/css/grocy.css
index 239fa9fa..8bfa4c7d 100755
--- a/public/css/grocy.css
+++ b/public/css/grocy.css
@@ -209,21 +209,16 @@ a:not([href]) {
white-space: pre-wrap;
}
-/* Barcodescanner Quagga2 */
-#barcodescanner-container {
- max-height: 90vw;
+/* Camera Barcodescanner Quagga2 */
+#camerabarcodescanner-container {
+ max-height: 90vh;
}
-#livestream-container {
- max-height: 100%;
-}
-
-#barcodescanner-livestream video {
+#camerabarcodescanner-livestream video {
width: 100%;
-
}
-#barcodescanner-livestream canvas {
+#camerabarcodescanner-livestream canvas {
width: 100%;
}
@@ -304,6 +299,14 @@ a:not([href]) {
overflow-y: auto;
}
+.btn.dropdown-toggle:after {
+ vertical-align: 0;
+ margin: 0;
+ border: none;
+ content: '\f107';
+ font-family: 'Font Awesome 6 Free';
+}
+
/* Third party component customizations - DataTables */
.dataTable td {
vertical-align: middle !important;
@@ -362,14 +365,6 @@ html {
min-height: inherit;
}
-.dropdown-toggle:after {
- vertical-align: 0;
- margin: 0;
- border: none;
- content: '\f107';
- font-family: 'Font Awesome 6 Free';
-}
-
/* Third party component customizations - Tempus Dominus */
.bootstrap-datetimepicker-widget.dropdown-menu {
width: auto !important;
diff --git a/public/js/grocy.js b/public/js/grocy.js
index 6d20398a..452a99b5 100644
--- a/public/js/grocy.js
+++ b/public/js/grocy.js
@@ -874,3 +874,9 @@ $(document).on("click", "#clear-filter-button", function(e)
{
$(".tooltip").tooltip("hide");
});
+
+$(document).on("click", '.btn, a, button', function(e)
+{
+ // Remove focus after click
+ document.activeElement.blur();
+});
diff --git a/public/viewjs/barcodescannertesting.js b/public/viewjs/barcodescannertesting.js
index 7c7dd6a5..d5b115ee 100644
--- a/public/viewjs/barcodescannertesting.js
+++ b/public/viewjs/barcodescannertesting.js
@@ -20,21 +20,21 @@ $("#expected_barcode").on("keyup", function(e)
if ($("#expected_barcode").val().length > 1)
{
$("#scanned_barcode").removeAttr("disabled");
- $("#barcodescanner-start-button").removeAttr("disabled");
- $("#barcodescanner-start-button").removeClass("disabled");
+ $("#camerabarcodescanner-start-button").removeAttr("disabled");
+ $("#camerabarcodescanner-start-button").removeClass("disabled");
}
else
{
$("#scanned_barcode").attr("disabled", "");
- $("#barcodescanner-start-button").attr("disabled", "");
- $("#barcodescanner-start-button").addClass("disabled");
+ $("#camerabarcodescanner-start-button").attr("disabled", "");
+ $("#camerabarcodescanner-start-button").addClass("disabled");
}
});
setTimeout(function()
{
- $("#barcodescanner-start-button").attr("disabled", "");
- $("#barcodescanner-start-button").addClass("disabled");
+ $("#camerabarcodescanner-start-button").attr("disabled", "");
+ $("#camerabarcodescanner-start-button").addClass("disabled");
$("#expected_barcode").focus();
}, 500);
diff --git a/public/viewjs/batteriesoverview.js b/public/viewjs/batteriesoverview.js
index b5718de4..4727510a 100644
--- a/public/viewjs/batteriesoverview.js
+++ b/public/viewjs/batteriesoverview.js
@@ -117,7 +117,6 @@ $(document).on('click', '.track-charge-cycle-button', function(e)
$(document).on('click', '.battery-grocycode-label-print', function(e)
{
e.preventDefault();
- document.activeElement.blur();
var batteryId = $(e.currentTarget).attr('data-battery-id');
Grocy.Api.Get('batteries/' + batteryId + '/printlabel', function(labelData)
diff --git a/public/viewjs/batteryform.js b/public/viewjs/batteryform.js
index 79678e75..686ac0bf 100644
--- a/public/viewjs/batteryform.js
+++ b/public/viewjs/batteryform.js
@@ -91,7 +91,6 @@ $('#battery-form input').keydown(function(event)
$(document).on('click', '.battery-grocycode-label-print', function(e)
{
e.preventDefault();
- document.activeElement.blur();
var batteryId = $(e.currentTarget).attr('data-battery-id');
Grocy.Api.Get('batteries/' + batteryId + '/printlabel', function(labelData)
diff --git a/public/viewjs/choreform.js b/public/viewjs/choreform.js
index 08cc9180..8d1455ac 100644
--- a/public/viewjs/choreform.js
+++ b/public/viewjs/choreform.js
@@ -271,7 +271,6 @@ Grocy.Components.ProductPicker.GetPicker().on('change', function(e)
$(document).on('click', '.chore-grocycode-label-print', function(e)
{
e.preventDefault();
- document.activeElement.blur();
var choreId = $(e.currentTarget).attr('data-chore-id');
Grocy.Api.Get('chores/' + choreId + '/printlabel', function(labelData)
diff --git a/public/viewjs/choresoverview.js b/public/viewjs/choresoverview.js
index 27cddf85..de1734c0 100644
--- a/public/viewjs/choresoverview.js
+++ b/public/viewjs/choresoverview.js
@@ -213,7 +213,6 @@ $(document).on('click', '.track-chore-button', function(e)
$(document).on('click', '.chore-grocycode-label-print', function(e)
{
e.preventDefault();
- document.activeElement.blur();
var choreId = $(e.currentTarget).attr('data-chore-id');
Grocy.Api.Get('chores/' + choreId + '/printlabel', function(labelData)
diff --git a/public/viewjs/components/camerabarcodescanner.js b/public/viewjs/components/camerabarcodescanner.js
index 6a6467c8..78a02592 100644
--- a/public/viewjs/components/camerabarcodescanner.js
+++ b/public/viewjs/components/camerabarcodescanner.js
@@ -37,30 +37,29 @@ Grocy.Components.CameraBarcodeScanner.CheckCapabilities = async function()
var hasTorch = typeof capabilities.torch === 'boolean' && capabilities.torch;
// Remove the torch button if the select camera doesn't have a torch
- var button = document.querySelector('.torch');
if (!hasTorch)
{
- button.classList.add('disabled');
+ document.querySelector('.camerabarcodescanner-modal .modal-footer').setAttribute('style', 'display:none !important;');
}
else
{
- button.classList.remove('disabled');
+ document.querySelector('.camerabarcodescanner-modal .modal-footer').setAttribute('style', 'flex;');
}
// Reduce the height of the video, if it's higher than then the viewport
if (!Grocy.Components.CameraBarcodeScanner.LiveVideoSizeAdjusted)
{
- var bc = document.getElementById('barcodescanner-container');
+ var bc = document.getElementById('camerabarcodescanner-container');
if (bc)
{
var bcAspectRatio = bc.offsetWidth / bc.offsetHeight;
var settings = track.getSettings();
if (bcAspectRatio > settings.aspectRatio)
{
- var v = document.querySelector('#barcodescanner-livestream video');
+ var v = document.querySelector('#camerabarcodescanner-livestream video');
if (v)
{
- var c = document.querySelector('#barcodescanner-livestream canvas')
+ var c = document.querySelector('#camerabarcodescanner-livestream canvas')
var newWidth = v.clientWidth / bcAspectRatio * settings.aspectRatio + 'px';
v.style.width = newWidth;
c.style.width = newWidth;
@@ -81,7 +80,7 @@ Grocy.Components.CameraBarcodeScanner.StartScanning = function()
inputStream: {
name: "Live",
type: "LiveStream",
- target: document.querySelector("#barcodescanner-livestream"),
+ target: document.querySelector("#camerabarcodescanner-livestream"),
constraints: {
facingMode: "environment",
...(window.localStorage.getItem('cameraId') && { deviceId: window.localStorage.getItem('cameraId') }), // If preferred cameraId is set, request to use that specific camera
@@ -234,7 +233,7 @@ Quagga.onProcessed(function(result)
}
});
-$(document).on("click", "#barcodescanner-start-button", async function(e)
+$(document).on("click", "#camerabarcodescanner-start-button", async function(e)
{
e.preventDefault();
var inputElement = $(e.currentTarget).prev();
@@ -248,12 +247,12 @@ $(document).on("click", "#barcodescanner-start-button", async function(e)
Grocy.Components.CameraBarcodeScanner.CurrentTarget = inputElement.attr("data-target");
var dialog = bootbox.dialog({
- message: '