From 11d28622e8cf0aa0c6951f170c4bb35596d40d00 Mon Sep 17 00:00:00 2001 From: Bernd Bestel Date: Tue, 14 Jan 2025 17:54:06 +0100 Subject: [PATCH] Squashed commit Make it possible to actively not-check a mandatory checkbox Userfield (closes #2601) Pluralize the "opened" localization string (closes #2280) Added a trendline to the price history chart (closes #2237) Various minor style/code refinements --- changelog/52_2.5.0_2019-09-22.md | 2 +- changelog/77_UNRELEASED_xxxx-xx-xx.md | 3 ++ config-dist.php | 7 +-- localization/strings.pot | 4 +- package.json | 1 + public/css/grocy.css | 31 +++++------- public/js/grocy.js | 6 +++ public/viewjs/barcodescannertesting.js | 12 ++--- public/viewjs/batteriesoverview.js | 1 - public/viewjs/batteryform.js | 1 - public/viewjs/choreform.js | 1 - public/viewjs/choresoverview.js | 1 - .../viewjs/components/camerabarcodescanner.js | 23 +++++---- public/viewjs/components/productcard.js | 48 +++++++++++++++---- public/viewjs/components/productpicker.js | 10 ++-- public/viewjs/components/userfieldsform.js | 18 ++++++- public/viewjs/consume.js | 3 +- public/viewjs/productform.js | 1 - public/viewjs/purchase.js | 1 - public/viewjs/recipeform.js | 1 - public/viewjs/recipes.js | 1 - public/viewjs/shoppinglist.js | 2 - public/viewjs/stockentries.js | 3 +- public/viewjs/stockjournal.js | 1 - public/viewjs/stockoverview.js | 1 - public/viewjs/transfer.js | 2 +- .../components/camerabarcodescanner.blade.php | 6 +-- views/layout/default.blade.php | 1 + views/shoppinglist.blade.php | 3 ++ views/stockentries.blade.php | 2 +- views/stockentryform.blade.php | 2 +- yarn.lock | 21 ++++---- 32 files changed, 134 insertions(+), 86 deletions(-) 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: '
', + message: '
', title: __t('Scan a barcode'), size: 'large', backdrop: true, closeButton: true, - className: "form", + className: "form camerabarcodescanner-modal", buttons: { torch: { label: '', @@ -296,11 +295,11 @@ Grocy.Components.CameraBarcodeScanner.Init = function() { if ($(this).hasAttr("disabled")) { - $(this).after(''); + $(this).after(''); } else { - $(this).after(''); + $(this).after(''); } Grocy.Components.CameraBarcodeScanner.InitDone = true; diff --git a/public/viewjs/components/productcard.js b/public/viewjs/components/productcard.js index 970c3467..61f6f5ca 100644 --- a/public/viewjs/components/productcard.js +++ b/public/viewjs/components/productcard.js @@ -136,7 +136,10 @@ Grocy.Components.ProductCard.Refresh = function(productId) $("#productcard-no-price-data-hint").addClass("d-none"); Grocy.Components.ProductCard.ReInitPriceHistoryChart(); + var datasets = {}; + datasets["_TrendlineDataset"] = [] + var chart = Grocy.Components.ProductCard.PriceHistoryChart.data; priceHistoryDataPoints.forEach((dataPoint) => { @@ -150,18 +153,41 @@ Grocy.Components.ProductCard.Refresh = function(productId) { datasets[key] = [] } + chart.labels.push(moment(dataPoint.date).toDate()); datasets[key].push({ x: moment(dataPoint.date).toDate(), y: dataPoint.price * productDetails.qu_conversion_factor_price_to_stock }); - + datasets["_TrendlineDataset"].push({ x: moment(dataPoint.date).toDate(), y: dataPoint.price * productDetails.qu_conversion_factor_price_to_stock }); }); + Object.keys(datasets).forEach((key) => { - chart.datasets.push({ - data: datasets[key], - fill: false, - borderColor: "HSL(" + (129 * chart.datasets.length) + ",100%,50%)", - label: key, - }); + if (key != "_TrendlineDataset") + { + chart.datasets.push({ + data: datasets[key], + fill: false, + borderColor: "HSL(" + (129 * chart.datasets.length) + ",100%,50%)", + label: key + }); + } + else + { + chart.datasets.push({ + data: datasets[key], + fill: false, + borderColor: "HSL(" + (129 * chart.datasets.length) + ",100%,50%)", + label: key, + hidden: true, + alwaysShowTrendline: true, + trendlineLinear: { + colorMin: "rgba(0, 0, 0, 0.3)", + colorMax: "rgba(0, 0, 0, 0.3)", + lineStyle: "dotted", + width: 3 + } + }); + } + }); Grocy.Components.ProductCard.PriceHistoryChart.update(); @@ -234,7 +260,13 @@ Grocy.Components.ProductCard.ReInitPriceHistoryChart = function() }] }, legend: { - display: true + display: true, + labels: { + filter: function(item, chart) + { + return item.text != "_TrendlineDataset"; + } + } }, tooltips: { callbacks: { diff --git a/public/viewjs/components/productpicker.js b/public/viewjs/components/productpicker.js index d36815f2..c098b53b 100644 --- a/public/viewjs/components/productpicker.js +++ b/public/viewjs/components/productpicker.js @@ -76,15 +76,15 @@ Grocy.Components.ProductPicker.HideCustomError = function() Grocy.Components.ProductPicker.Disable = function() { Grocy.Components.ProductPicker.GetInputElement().attr("disabled", ""); - $("#barcodescanner-start-button").attr("disabled", ""); - $("#barcodescanner-start-button").addClass("disabled"); + $("#camerabarcodescanner-start-button").attr("disabled", ""); + $("#camerabarcodescanner-start-button").addClass("disabled"); } Grocy.Components.ProductPicker.Enable = function() { Grocy.Components.ProductPicker.GetInputElement().removeAttr("disabled"); - $("#barcodescanner-start-button").removeAttr("disabled"); - $("#barcodescanner-start-button").removeClass("disabled"); + $("#camerabarcodescanner-start-button").removeAttr("disabled"); + $("#camerabarcodescanner-start-button").removeClass("disabled"); } $('.product-combobox').combobox({ @@ -295,7 +295,7 @@ $('#product_id_text_input').on('blur', function(e) { Grocy.Components.ProductPicker.PopupOpen = false; Grocy.Components.ProductPicker.SetValue(''); - $("#barcodescanner-start-button").click(); + $("#camerabarcodescanner-start-button").click(); } }; } diff --git a/public/viewjs/components/userfieldsform.js b/public/viewjs/components/userfieldsform.js index 9dbf3a3b..c1fd6afb 100644 --- a/public/viewjs/components/userfieldsform.js +++ b/public/viewjs/components/userfieldsform.js @@ -182,11 +182,19 @@ Grocy.Components.UserfieldsForm.Load = function() if (userfield.type == "datetime" && userfield.default_value == "now") { - input.val(moment().format("YYYY-MM-DD HH:mm:ss")) + input.val(moment().format("YYYY-MM-DD HH:mm:ss")); } else if (userfield.type == "date" && userfield.default_value == "now") { - input.val(moment().format("YYYY-MM-DD")) + input.val(moment().format("YYYY-MM-DD")); + } + else if (userfield.type == "checkbox" && userfield.input_required == 1) + { + input.prop("indeterminate", true); + input.on("change", function() + { + input.removeAttr("required"); + }); } }); @@ -212,6 +220,12 @@ Grocy.Components.UserfieldsForm.Load = function() { var input = $(".userfield-input[data-userfield-name='" + key + "']"); + if (input.attr("type") == "checkbox") + { + // The required attribute for checkboxes is only relevant when creating objects + input.removeAttr("required"); + } + if (input.attr("type") == "checkbox" && value == 1) { input.prop("checked", true); diff --git a/public/viewjs/consume.js b/public/viewjs/consume.js index 42ff5a05..5ed74756 100644 --- a/public/viewjs/consume.js +++ b/public/viewjs/consume.js @@ -287,7 +287,7 @@ function OnLocationChange(locationId, stockId) var openTxt = __t("Not opened"); if (stockEntry.open == 1) { - openTxt = __t("Opened"); + openTxt = __n(stockEntry.amount, "Opened", "Opened"); } if (stockEntry.location_id == locationId) @@ -672,7 +672,6 @@ $(document).on("change", "#scan-mode", function(e) $("#scan-mode-button").on("click", function(e) { - document.activeElement.blur(); $("#scan-mode").click(); $("#scan-mode-button").toggleClass("btn-success").toggleClass("btn-danger"); if ($("#scan-mode").prop("checked")) diff --git a/public/viewjs/productform.js b/public/viewjs/productform.js index aa17a0ae..1bddebf0 100644 --- a/public/viewjs/productform.js +++ b/public/viewjs/productform.js @@ -267,7 +267,6 @@ setTimeout(function() $(document).on('click', '.product-grocycode-label-print', function(e) { e.preventDefault(); - document.activeElement.blur(); var productId = $(e.currentTarget).attr('data-product-id'); Grocy.Api.Get('stock/products/' + productId + '/printlabel', function(labelData) diff --git a/public/viewjs/purchase.js b/public/viewjs/purchase.js index ea87dd87..6effb07c 100644 --- a/public/viewjs/purchase.js +++ b/public/viewjs/purchase.js @@ -689,7 +689,6 @@ $("#scan-mode").on("change", function(e) $("#scan-mode-button").on("click", function(e) { - document.activeElement.blur(); $("#scan-mode").click(); $("#scan-mode-button").toggleClass("btn-success").toggleClass("btn-danger"); if ($("#scan-mode").prop("checked")) diff --git a/public/viewjs/recipeform.js b/public/viewjs/recipeform.js index 0a574443..125b0298 100644 --- a/public/viewjs/recipeform.js +++ b/public/viewjs/recipeform.js @@ -379,7 +379,6 @@ $(window).on("message", function(e) $(document).on('click', '.recipe-grocycode-label-print', function(e) { e.preventDefault(); - document.activeElement.blur(); var recipeId = $(e.currentTarget).attr('data-recipe-id'); Grocy.Api.Get('recipes/' + recipeId + '/printlabel', function(labelData) diff --git a/public/viewjs/recipes.js b/public/viewjs/recipes.js index 27c332c1..69534339 100644 --- a/public/viewjs/recipes.js +++ b/public/viewjs/recipes.js @@ -429,7 +429,6 @@ if (window.location.hash === "#fullscreen") $(document).on('click', '.recipe-grocycode-label-print', function(e) { e.preventDefault(); - document.activeElement.blur(); var recipeId = $(e.currentTarget).attr('data-recipe-id'); Grocy.Api.Get('recipes/' + recipeId + '/printlabel', function(labelData) diff --git a/public/viewjs/shoppinglist.js b/public/viewjs/shoppinglist.js index 3d15b6ac..93ea0023 100644 --- a/public/viewjs/shoppinglist.js +++ b/public/viewjs/shoppinglist.js @@ -559,7 +559,6 @@ $("#description").on("summernote.change", function() $(document).on("click", "#save-description-button", function(e) { e.preventDefault(); - document.activeElement.blur(); Grocy.Api.Put('objects/shopping_lists/' + $("#selected-shopping-list").val(), { description: $("#description").val() }, function(result) @@ -576,7 +575,6 @@ $(document).on("click", "#save-description-button", function(e) $(document).on("click", "#clear-description-button", function(e) { e.preventDefault(); - document.activeElement.blur(); $("#description").summernote("reset"); $("#save-description-button").click(); diff --git a/public/viewjs/stockentries.js b/public/viewjs/stockentries.js index 98e71e72..81eac38e 100644 --- a/public/viewjs/stockentries.js +++ b/public/viewjs/stockentries.js @@ -161,7 +161,6 @@ $(document).on('click', '.product-open-button', function(e) $(document).on('click', '.stockentry-grocycode-label-print', function(e) { e.preventDefault(); - document.activeElement.blur(); var stockId = $(e.currentTarget).attr('data-stock-id'); Grocy.Api.Get('stock/entry/' + stockId + '/printlabel', function(labelData) @@ -288,7 +287,7 @@ function RefreshStockEntryRow(stockRowId) if (result.open == 1) { - $('#stock-' + stockRowId + '-opened-amount').text(__t('Opened')); + $('#stock-' + stockRowId + '-opened-amount').text(__n(result.amount, 'Opened', 'Opened')); } else { diff --git a/public/viewjs/stockjournal.js b/public/viewjs/stockjournal.js index 33906b26..8c8762b3 100644 --- a/public/viewjs/stockjournal.js +++ b/public/viewjs/stockjournal.js @@ -137,7 +137,6 @@ $(document).on('click', '.undo-stock-booking-button', function(e) $(document).on('click', '.product-grocycode-label-print', function(e) { e.preventDefault(); - document.activeElement.blur(); var productId = $(e.currentTarget).attr('data-product-id'); Grocy.Api.Get('stock/products/' + productId + '/printlabel', function(labelData) diff --git a/public/viewjs/stockoverview.js b/public/viewjs/stockoverview.js index da01e92d..2cf64d83 100755 --- a/public/viewjs/stockoverview.js +++ b/public/viewjs/stockoverview.js @@ -113,7 +113,6 @@ $("#search").on("keyup", Delay(function() $(document).on('click', '.product-grocycode-label-print', function(e) { e.preventDefault(); - document.activeElement.blur(); var productId = $(e.currentTarget).attr('data-product-id'); Grocy.Api.Get('stock/products/' + productId + '/printlabel', function(labelData) diff --git a/public/viewjs/transfer.js b/public/viewjs/transfer.js index 35fc9c6c..6cb16330 100644 --- a/public/viewjs/transfer.js +++ b/public/viewjs/transfer.js @@ -340,7 +340,7 @@ $("#location_id_from").on('change', function(e) var openTxt = __t("Not opened"); if (stockEntry.open == 1) { - openTxt = __t("Opened"); + openTxt = __n(stockEntry.amount, "Opened", "Opened"); } if (stockEntry.location_id == locationId) diff --git a/views/components/camerabarcodescanner.blade.php b/views/components/camerabarcodescanner.blade.php index 087ecb0b..698fc983 100644 --- a/views/components/camerabarcodescanner.blade.php +++ b/views/components/camerabarcodescanner.blade.php @@ -10,7 +10,7 @@ @push('pageStyles') @endpush diff --git a/views/layout/default.blade.php b/views/layout/default.blade.php index 9134225b..6d468ba6 100644 --- a/views/layout/default.blade.php +++ b/views/layout/default.blade.php @@ -747,6 +747,7 @@ + @endif diff --git a/views/shoppinglist.blade.php b/views/shoppinglist.blade.php index d89acdbd..d2865def 100644 --- a/views/shoppinglist.blade.php +++ b/views/shoppinglist.blade.php @@ -249,8 +249,11 @@ $productQuConversion = FindObjectInArrayByPropertyValue($productQuConversions, 'to_qu_id', $listItem->qu_id); if ($productQuConversion) { + if (is_numeric($listItem->amount) && is_numeric($productQuConversion->factor)) // However people manage to have a non-number here, happened at least a trilion times for Grocy Mobile (iOS) users + { $listItem->amount = $listItem->amount * $productQuConversion->factor; } + } @endphp @endif diff --git a/views/stockentries.blade.php b/views/stockentries.blade.php index aaf75873..b92d59d0 100644 --- a/views/stockentries.blade.php +++ b/views/stockentries.blade.php @@ -259,7 +259,7 @@ {{ $stockEntry->amount }} {{ $__n($stockEntry->amount, FindObjectInArrayByPropertyValue($quantityunits, 'id', FindObjectInArrayByPropertyValue($products, 'id', $stockEntry->product_id)->qu_id_stock)->name, FindObjectInArrayByPropertyValue($quantityunits, 'id', FindObjectInArrayByPropertyValue($products, 'id', $stockEntry->product_id)->qu_id_stock)->name_plural, true) }} @if($stockEntry->open == 1){{ $__t('Opened') }}@endif + class="small font-italic">@if($stockEntry->open == 1){{ $__n($stockEntry->amount, 'Opened', 'Opened') }}@endif {{ $stockEntry->best_before_date }} diff --git a/views/stockentryform.blade.php b/views/stockentryform.blade.php index cddedd33..3dfd935a 100644 --- a/views/stockentryform.blade.php +++ b/views/stockentryform.blade.php @@ -137,7 +137,7 @@
open == 1) checked @endif class="form-check-input custom-control-input" type="checkbox" id="open" name="open" value="1"> + for="open">{{ $__n(1, 'Opened', 'Opened') }}
@endif diff --git a/yarn.lock b/yarn.lock index f706e7f2..e12d85ba 100644 --- a/yarn.lock +++ b/yarn.lock @@ -167,6 +167,11 @@ chartjs-plugin-piechart-outlabels@^0.1.4: resolved "https://registry.yarnpkg.com/chartjs-plugin-piechart-outlabels/-/chartjs-plugin-piechart-outlabels-0.1.4.tgz#e97e19a12202d74f9040d9e4641987c9d1e458fc" integrity sha512-IaYkh6ab8nLAvgioQ+BwU0awfMbxwmfO2AeBL+S45VVx9AdObovr9+aE+ShUO2Og96y6eJpCxZGJr4zXB7YnRw== +chartjs-plugin-trendline@^2.1.6: + version "2.1.6" + resolved "https://registry.yarnpkg.com/chartjs-plugin-trendline/-/chartjs-plugin-trendline-2.1.6.tgz#5090155cae06681c9888a63305991d868cff3596" + integrity sha512-73lpSv87RcIeu0so4ndEE48Xf08Q4scz079tSgYdfeGuIce4JKcSE64oluhz/j6NpBr50Z7PPwolGJ0cLtknRw== + color-convert@^1.9.3: version "1.9.3" resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" @@ -229,11 +234,11 @@ datatables.net-bs4@^1.10.22, datatables.net-bs4@^1.13.0: jquery "1.8 - 4" datatables.net-bs4@^2: - version "2.2.0" - resolved "https://registry.yarnpkg.com/datatables.net-bs4/-/datatables.net-bs4-2.2.0.tgz#bf35a667b05a3ee5dc4626d07c92a57d0dfc2090" - integrity sha512-dTgsblxSftIkLuyUItcQwbRfunAhFygxwpXxLCfKmE3xA8QBkl8KREAx9OQuVxJUMc5Ao+LCknd5kT+kzEqREg== + version "2.2.1" + resolved "https://registry.yarnpkg.com/datatables.net-bs4/-/datatables.net-bs4-2.2.1.tgz#07e9da60d88c6e872ffdbb63c5d96a164e8e11aa" + integrity sha512-YJpbGGTqrOhS6p8BolGv16mdmljaq4ewhc0dbm3yd02UQ86A2cWQ2tDq6lE966tvRD43JE0PPAc5wLgsRozReg== dependencies: - datatables.net "2.2.0" + datatables.net "2.2.1" jquery ">=1.7" datatables.net-colreorder-bs4@^1.5.2: @@ -302,10 +307,10 @@ datatables.net@1.13.11, datatables.net@^1.10.22, datatables.net@^1.13.0, datatab dependencies: jquery "1.8 - 4" -datatables.net@2.2.0, datatables.net@^2: - version "2.2.0" - resolved "https://registry.yarnpkg.com/datatables.net/-/datatables.net-2.2.0.tgz#d7e92ede9a3bf23ac4c360e475fbe6313f16fd56" - integrity sha512-q/G5ylL+AhMLFFHNYQAgvooCZtmbudc7wwzKkCZI4B1HnYec4SqnWtcTFjC7P6EtL1UGyx16FcjQ0U84S86mLg== +datatables.net@2.2.1, datatables.net@^2: + version "2.2.1" + resolved "https://registry.yarnpkg.com/datatables.net/-/datatables.net-2.2.1.tgz#30506074a42eb6f9bfdd823bd7e97d7458c6f1c4" + integrity sha512-Hz7f+a77xGdroAzej88aobT5nkQIJ2Oy1JI7yh2cl0QAXQSJDXOKkOLknu+nphVP8CP8w9MtLCmwMst/F8niiQ== dependencies: jquery ">=1.7"