From f427849e89c56df7aaf84681bd71e7cf1ed9c234 Mon Sep 17 00:00:00 2001 From: Bernd Bestel Date: Sun, 21 May 2023 10:56:38 +0200 Subject: [PATCH] Load all frontend packages conditionally --- changelog/70_UNRELEASED_xxxx.xx.xx.md | 1 + helpers/extensions.php | 9 + public/js/grocy.js | 399 +----------------- public/js/grocy_datatables.js | 369 ++++++++++++++++ public/js/grocy_summernote.js | 13 + views/barcodescannertesting.blade.php | 7 +- views/batteries.blade.php | 2 + views/batteriesjournal.blade.php | 2 + views/batteriesoverview.blade.php | 7 +- views/batterytracking.blade.php | 2 + views/calendar.blade.php | 13 +- views/choreform.blade.php | 2 + views/chores.blade.php | 2 + views/choresjournal.blade.php | 2 + views/choresoverview.blade.php | 7 +- views/choretracking.blade.php | 2 + views/components/barcodescanner.blade.php | 9 +- views/components/calendarcard.blade.php | 2 + views/components/datetimepicker.blade.php | 2 + views/components/datetimepicker2.blade.php | 2 + views/components/locationpicker.blade.php | 2 + views/components/productcard.blade.php | 3 +- views/components/productpicker.blade.php | 2 + views/components/recipepicker.blade.php | 2 + .../shoppinglocationpicker.blade.php | 2 + views/components/userfieldsform.blade.php | 2 + views/components/userpicker.blade.php | 2 + views/equipment.blade.php | 2 + views/equipmentform.blade.php | 2 + views/layout/default.blade.php | 82 +++- views/locationcontentsheet.blade.php | 2 + views/locations.blade.php | 2 + views/manageapikeys.blade.php | 11 +- views/mealplan.blade.php | 10 +- views/mealplansections.blade.php | 2 + views/productform.blade.php | 2 + views/productgroups.blade.php | 2 + views/products.blade.php | 2 + .../quantityunitconversionsresolved.blade.php | 2 + views/quantityunitform.blade.php | 2 + views/quantityunitpluraltesting.blade.php | 7 +- views/quantityunits.blade.php | 2 + views/recipeform.blade.php | 2 + views/recipes.blade.php | 2 + views/shoppinglist.blade.php | 10 +- views/shoppinglocations.blade.php | 2 + views/stockentries.blade.php | 7 +- views/stockjournal.blade.php | 2 + views/stockjournalsummary.blade.php | 2 + views/stockoverview.blade.php | 7 +- views/stockreportspendings.blade.php | 15 +- views/taskcategories.blade.php | 2 + views/tasks.blade.php | 7 +- views/userentities.blade.php | 2 + views/userfields.blade.php | 2 + views/userobjects.blade.php | 2 + views/users.blade.php | 2 + 57 files changed, 580 insertions(+), 487 deletions(-) create mode 100644 public/js/grocy_datatables.js create mode 100644 public/js/grocy_summernote.js diff --git a/changelog/70_UNRELEASED_xxxx.xx.xx.md b/changelog/70_UNRELEASED_xxxx.xx.xx.md index 68b6c54c..ff05c9a2 100644 --- a/changelog/70_UNRELEASED_xxxx.xx.xx.md +++ b/changelog/70_UNRELEASED_xxxx.xx.xx.md @@ -95,6 +95,7 @@ - Like already possible for products/chores/batteries, locations, stores, quantity units, product groups and task categories can now be disabled to keep them for existing references without deleting them, but to hide them everywhere for selections and so on (new option "Active") - Added a new `config.php` setting `ENERGY_UNIT` to customize the label to display energy values (was fixed `kcal` before and defaults to that, so no changed behavior when not configured) - New logo and "Grocy" is now officially spelled with a capital initial letter (before everything was lowercase) +- Various frontend performance enhancements - Fixed that users were unable to delete their own API keys (when not having the `All permissions` permission) - Fixed that button tooltips on some places didn't disappear after clicking the corresponding button - New translations: (thanks all the translators) diff --git a/helpers/extensions.php b/helpers/extensions.php index 27c25f60..c83f4e4c 100644 --- a/helpers/extensions.php +++ b/helpers/extensions.php @@ -253,3 +253,12 @@ function string_ends_with($haystack, $needle) return (substr($haystack, -$length) === $needle); } + +global $GROCY_REQUIRED_FRONTEND_PACKAGES; +$GROCY_REQUIRED_FRONTEND_PACKAGES = []; +function require_frontend_packages(array $packages) +{ + global $GROCY_REQUIRED_FRONTEND_PACKAGES; + + $GROCY_REQUIRED_FRONTEND_PACKAGES = array_unique(array_merge($GROCY_REQUIRED_FRONTEND_PACKAGES, $packages)); +} diff --git a/public/js/grocy.js b/public/js/grocy.js index efb466b6..336ace74 100644 --- a/public/js/grocy.js +++ b/public/js/grocy.js @@ -249,7 +249,14 @@ __t = function(text, ...placeholderValues) } } - return sprintf(Grocy.Translator.__(text, ...placeholderValues), ...placeholderValues); + // sprintf can fail due to invalid placeholders + try + { + return sprintf(Grocy.Translator.__(text, ...placeholderValues), ...placeholderValues); + } catch (e) + { + return Grocy.Translator.__(text, ...placeholderValues); + } } __n = function(number, singularForm, pluralForm, isQu = false) { @@ -272,13 +279,15 @@ __n = function(number, singularForm, pluralForm, isQu = false) pluralForm = singularForm; } + number = Math.abs(number); + if (isQu) { - return Grocy.TranslatorQu.n__(singularForm, pluralForm, Math.abs(number), Math.abs(number)) + return sprintf(Grocy.TranslatorQu.n__(singularForm, pluralForm, number, number), number.toString()); } else { - return Grocy.Translator.n__(singularForm, pluralForm, Math.abs(number), Math.abs(number)) + return sprintf(Grocy.Translator.n__(singularForm, pluralForm, number, number), number.toString()); } } @@ -665,20 +674,6 @@ $(document).on("click", ".easy-link-copy-textbox", function() $(this).select(); }); -$("textarea.wysiwyg-editor").summernote({ - minHeight: "300px", - lang: __t("summernote_locale"), - callbacks: { - onImageLinkInsert: function(url) - { - // Summernote workaround: Make images responsive - // By adding the "img-fluid" class to the img tag - $img = $('').attr({ src: url, class: "img-fluid" }) - $(this).summernote("insertNode", $img[0]); - } - } -}); - // Summernote workaround: Make embeds responsive // By wrapping any embeded video in a container with class "embed-responsive" $(".note-video-clip").each(function() @@ -745,128 +740,6 @@ $(document).on("click", ".show-as-dialog-link", function(e) // Init Bootstrap tooltips $('[data-toggle="tooltip"]').tooltip() -// Default DataTables initialisation settings -var collapsedGroups = {}; -$.extend(true, $.fn.dataTable.defaults, { - 'paginate': false, - 'deferRender': true, - 'language': IsJsonString(__t('datatables_localization')) ? JSON.parse(__t('datatables_localization')) : {}, - 'scrollY': false, - 'scrollX': true, - 'colReorder': true, - 'stateSave': true, - 'stateSaveParams': function(settings, data) - { - data.search.search = ""; - - data.columns.forEach(column => - { - column.search.search = ""; - }); - }, - 'stateSaveCallback': function(settings, data) - { - var settingKey = 'datatables_state_' + settings.sTableId; - if ($.isEmptyObject(data)) - { - //state.clear was called and unfortunately the table is not refresh, so we are reloading the page - Grocy.FrontendHelpers.DeleteUserSetting(settingKey, true); - } else - { - var stateData = JSON.stringify(data); - Grocy.FrontendHelpers.SaveUserSetting(settingKey, stateData); - } - }, - 'stateLoadCallback': function(settings, data) - { - var settingKey = 'datatables_state_' + settings.sTableId; - - if (Grocy.UserSettings[settingKey] == undefined) - { - return null; - } - else - { - return JSON.parse(Grocy.UserSettings[settingKey]); - } - }, - 'preDrawCallback': function(settings) - { - // Currently it is not possible to save the state of rowGroup via saveState events - var api = new $.fn.dataTable.Api(settings); - if (typeof api.rowGroup === "function") - { - var settingKey = 'datatables_rowGroup_' + settings.sTableId; - if (Grocy.UserSettings[settingKey] !== undefined) - { - var rowGroup = JSON.parse(Grocy.UserSettings[settingKey]); - - // Check if there way changed. the draw event is called often therefore we have to check if it's really necessary - if (rowGroup.enable !== api.rowGroup().enabled() - || ("dataSrc" in rowGroup && rowGroup.dataSrc !== api.rowGroup().dataSrc())) - { - - api.rowGroup().enable(rowGroup.enable); - - if ("dataSrc" in rowGroup) - { - api.rowGroup().dataSrc(rowGroup.dataSrc); - - // Apply fixed order for group column - api.order.fixed({ - pre: [rowGroup.dataSrc, 'asc'] - }); - } - else - { - // Remove fixed order - api.order.fixed({}); - } - } - } - } - }, - 'columnDefs': [ - { type: 'chinese-string', targets: '_all' } - ], - 'rowGroup': { - enable: false, - startRender: function(rows, group) - { - var collapsed = !!collapsedGroups[group]; - var toggleClass = collapsed ? "fa-caret-right" : "fa-caret-down"; - - rows.nodes().each(function(row) - { - row.style.display = collapsed ? "none" : ""; - }); - - return $("") - .append('' + group + ' ') - .attr("data-name", group) - .toggleClass("collapsed", collapsed); - } - } -}); -$(document).on("click", "tr.dtrg-group", function() -{ - var name = $(this).data('name'); - collapsedGroups[name] = !collapsedGroups[name]; - $("table").DataTable().draw(); -}); -$.fn.dataTable.ext.type.order["custom-sort-pre"] = function(data) -{ - // Workaround for https://github.com/DataTables/ColReorder/issues/85 - // - // Custom sorting can normally be provided by a "data-order" attribute on the element, - // however this causes issues when reordering such a column... - // - // This here is for a custom column type "custom-sort", - // the custom order value needs to be provided in the first child () of the - - return (Number.parseFloat($(data).get(0).innerText)); -}; - // serializeJSON defaults $.serializeJSON.defaultOptions.checkboxUncheckedValue = "0"; @@ -912,38 +785,7 @@ $('.dropdown-item').has('.form-check input[type=checkbox]').on('click', function { $(e.target).find('input[type=checkbox]').click(); } -}) - -$('.table').on('column-sizing.dt', function(e, settings) -{ - var dtScrollWidth = $('.dataTables_scroll').width(); - var tableWidth = $('.table').width() + 100; // Some extra padding, otherwise the scrollbar maybe only appears after a column is already completely out of the viewport - - if (dtScrollWidth < tableWidth) - { - $('.dataTables_scrollBody').addClass("no-force-overflow-visible"); - $('.dataTables_scrollBody').removeClass("force-overflow-visible"); - } - else - { - $('.dataTables_scrollBody').removeClass("no-force-overflow-visible"); - $('.dataTables_scrollBody').addClass("force-overflow-visible"); - } }); -$(document).on("show.bs.dropdown", "td .dropdown", function(e) -{ - if ($('.dataTables_scrollBody').hasClass("no-force-overflow-visible")) - { - $('.dataTables_scrollBody').addClass("force-overflow-visible"); - } -}); -$(document).on("hide.bs.dropdown", "td .dropdown", function(e) -{ - if ($('.dataTables_scrollBody').hasClass("no-force-overflow-visible")) - { - $('.dataTables_scrollBody').removeClass("force-overflow-visible"); - } -}) $(window).on("message", function(e) { @@ -955,223 +797,6 @@ $(window).on("message", function(e) } }); -$(".change-table-columns-visibility-button").on("click", function(e) -{ - e.preventDefault(); - - var dataTableSelector = $(e.currentTarget).attr("data-table-selector"); - var dataTable = $(dataTableSelector).DataTable(); - - var columnCheckBoxesHtml = ""; - var rowGroupRadioBoxesHtml = ""; - - var rowGroupDefined = typeof dataTable.rowGroup === "function"; - - if (rowGroupDefined) - { - var rowGroupChecked = (dataTable.rowGroup().enabled()) ? "" : "checked"; - rowGroupRadioBoxesHtml = ' \ -
\ - \ - \ -
'; - } - - dataTable.columns().every(function() - { - var index = this.index(); - var indexForGrouping = index; - var headerCell = $(this.header()); - var title = headerCell.text(); - var visible = this.visible(); - - if (!title || title.trim().length == 0 || title.startsWith("Hidden") || headerCell.hasClass("d-none")) - { - return; - } - - var shadowColumnIndex = headerCell.attr("data-shadow-rowgroup-column"); - if (shadowColumnIndex) - { - indexForGrouping = shadowColumnIndex; - } - - var checked = "checked"; - if (!visible) - { - checked = ""; - } - - columnCheckBoxesHtml += ' \ -
\ - \ - \ -
'; - - if (rowGroupDefined && headerCell.hasClass("allow-grouping")) - { - var rowGroupChecked = ""; - if (dataTable.rowGroup().enabled() && dataTable.rowGroup().dataSrc() == index) - { - rowGroupChecked = "checked"; - } - - rowGroupRadioBoxesHtml += ' \ -
\ - \ - \ -
'; - } - }); - - var message = '\ -
\ -
' + __t('Table options') + '
\ -
\ -
' + __t('Hide/view columns') + '
\ -
\ - ' + columnCheckBoxesHtml + ' \ -
\ -
'; - - if (rowGroupDefined) - { - message += ' \ -
\ -
' + __t('Group by') + '
\ -
\ - ' + rowGroupRadioBoxesHtml + ' \ -
\ -
'; - } - - bootbox.dialog({ - message: message, - size: 'small', - backdrop: true, - closeButton: false, - buttons: { - reset: { - label: __t('Reset'), - className: 'btn-outline-danger float-left responsive-button', - callback: function() - { - bootbox.confirm({ - message: __t("Are you sure to reset the table options?"), - buttons: { - cancel: { - label: 'No', - className: 'btn-danger' - }, - confirm: { - label: 'Yes', - className: 'btn-success' - } - }, - callback: function(result) - { - if (result) - { - var dataTable = $(dataTableSelector).DataTable(); - var tableId = dataTable.settings()[0].sTableId; - - // Delete rowgroup settings - Grocy.FrontendHelpers.DeleteUserSetting('datatables_rowGroup_' + tableId); - - // Delete state settings - dataTable.state.clear(); - } - bootbox.hideAll(); - } - }); - } - }, - ok: { - label: __t('OK'), - className: 'btn-primary responsive-button', - callback: function() - { - bootbox.hideAll(); - } - } - } - }); -}); - -$(document).on("click", ".change-table-columns-visibility-toggle", function() -{ - var dataTableSelector = $(this).attr("data-table-selector"); - var columnIndex = $(this).attr("data-column-index"); - var dataTable = $(dataTableSelector).DataTable(); - - dataTable.columns(columnIndex).visible(this.checked); - LoadImagesLazy(); -}); - - -$(document).on("click", ".change-table-columns-rowgroup-toggle", function() -{ - var dataTableSelector = $(this).attr("data-table-selector"); - var columnIndex = $(this).attr("data-column-index"); - var dataTable = $(dataTableSelector).DataTable(); - var rowGroup; - - if (columnIndex == -1) - { - rowGroup = { - enable: false - }; - - dataTable.rowGroup().enable(false); - - // Remove fixed order - dataTable.order.fixed({}); - } - else - { - rowGroup = { - enable: true, - dataSrc: columnIndex - } - - dataTable.rowGroup().enable(true); - dataTable.rowGroup().dataSrc(columnIndex); - - // Apply fixed order for group column - dataTable.order.fixed({ - pre: [columnIndex, 'asc'] - }); - } - - var settingKey = 'datatables_rowGroup_' + dataTable.settings()[0].sTableId; - Grocy.FrontendHelpers.SaveUserSetting(settingKey, JSON.stringify(rowGroup)); - - dataTable.draw(); -}); - if (Grocy.FeatureFlags.GROCY_FEATURE_FLAG_RECIPES) { if ($(window).width() < 768) diff --git a/public/js/grocy_datatables.js b/public/js/grocy_datatables.js new file mode 100644 index 00000000..d44c9207 --- /dev/null +++ b/public/js/grocy_datatables.js @@ -0,0 +1,369 @@ +// Default DataTables initialisation settings +var collapsedGroups = {}; +$.extend(true, $.fn.dataTable.defaults, { + 'paginate': false, + 'deferRender': true, + 'language': IsJsonString(__t('datatables_localization')) ? JSON.parse(__t('datatables_localization')) : {}, + 'scrollY': false, + 'scrollX': true, + 'colReorder': true, + 'stateSave': true, + 'stateSaveParams': function(settings, data) + { + data.search.search = ""; + + data.columns.forEach(column => + { + column.search.search = ""; + }); + }, + 'stateSaveCallback': function(settings, data) + { + var settingKey = 'datatables_state_' + settings.sTableId; + if ($.isEmptyObject(data)) + { + //state.clear was called and unfortunately the table is not refresh, so we are reloading the page + Grocy.FrontendHelpers.DeleteUserSetting(settingKey, true); + } else + { + var stateData = JSON.stringify(data); + Grocy.FrontendHelpers.SaveUserSetting(settingKey, stateData); + } + }, + 'stateLoadCallback': function(settings, data) + { + var settingKey = 'datatables_state_' + settings.sTableId; + + if (Grocy.UserSettings[settingKey] == undefined) + { + return null; + } + else + { + return JSON.parse(Grocy.UserSettings[settingKey]); + } + }, + 'preDrawCallback': function(settings) + { + // Currently it is not possible to save the state of rowGroup via saveState events + var api = new $.fn.dataTable.Api(settings); + if (typeof api.rowGroup === "function") + { + var settingKey = 'datatables_rowGroup_' + settings.sTableId; + if (Grocy.UserSettings[settingKey] !== undefined) + { + var rowGroup = JSON.parse(Grocy.UserSettings[settingKey]); + + // Check if there way changed. the draw event is called often therefore we have to check if it's really necessary + if (rowGroup.enable !== api.rowGroup().enabled() + || ("dataSrc" in rowGroup && rowGroup.dataSrc !== api.rowGroup().dataSrc())) + { + + api.rowGroup().enable(rowGroup.enable); + + if ("dataSrc" in rowGroup) + { + api.rowGroup().dataSrc(rowGroup.dataSrc); + + // Apply fixed order for group column + api.order.fixed({ + pre: [rowGroup.dataSrc, 'asc'] + }); + } + else + { + // Remove fixed order + api.order.fixed({}); + } + } + } + } + }, + 'columnDefs': [ + { type: 'chinese-string', targets: '_all' } + ], + 'rowGroup': { + enable: false, + startRender: function(rows, group) + { + var collapsed = !!collapsedGroups[group]; + var toggleClass = collapsed ? "fa-caret-right" : "fa-caret-down"; + + rows.nodes().each(function(row) + { + row.style.display = collapsed ? "none" : ""; + }); + + return $("") + .append('' + group + ' ') + .attr("data-name", group) + .toggleClass("collapsed", collapsed); + } + } +}); +$(document).on("click", "tr.dtrg-group", function() +{ + var name = $(this).data('name'); + collapsedGroups[name] = !collapsedGroups[name]; + $("table").DataTable().draw(); +}); +$.fn.dataTable.ext.type.order["custom-sort-pre"] = function(data) +{ + // Workaround for https://github.com/DataTables/ColReorder/issues/85 + // + // Custom sorting can normally be provided by a "data-order" attribute on the element, + // however this causes issues when reordering such a column... + // + // This here is for a custom column type "custom-sort", + // the custom order value needs to be provided in the first child () of the + + return (Number.parseFloat($(data).get(0).innerText)); +}; + +$('.table').on('column-sizing.dt', function(e, settings) +{ + var dtScrollWidth = $('.dataTables_scroll').width(); + var tableWidth = $('.table').width() + 100; // Some extra padding, otherwise the scrollbar maybe only appears after a column is already completely out of the viewport + + if (dtScrollWidth < tableWidth) + { + $('.dataTables_scrollBody').addClass("no-force-overflow-visible"); + $('.dataTables_scrollBody').removeClass("force-overflow-visible"); + } + else + { + $('.dataTables_scrollBody').removeClass("no-force-overflow-visible"); + $('.dataTables_scrollBody').addClass("force-overflow-visible"); + } +}); +$(document).on("show.bs.dropdown", "td .dropdown", function(e) +{ + if ($('.dataTables_scrollBody').hasClass("no-force-overflow-visible")) + { + $('.dataTables_scrollBody').addClass("force-overflow-visible"); + } +}); +$(document).on("hide.bs.dropdown", "td .dropdown", function(e) +{ + if ($('.dataTables_scrollBody').hasClass("no-force-overflow-visible")) + { + $('.dataTables_scrollBody').removeClass("force-overflow-visible"); + } +}); + +$(".change-table-columns-visibility-button").on("click", function(e) +{ + e.preventDefault(); + + var dataTableSelector = $(e.currentTarget).attr("data-table-selector"); + var dataTable = $(dataTableSelector).DataTable(); + + var columnCheckBoxesHtml = ""; + var rowGroupRadioBoxesHtml = ""; + + var rowGroupDefined = typeof dataTable.rowGroup === "function"; + + if (rowGroupDefined) + { + var rowGroupChecked = (dataTable.rowGroup().enabled()) ? "" : "checked"; + rowGroupRadioBoxesHtml = ' \ +
\ + \ + \ +
'; + } + + dataTable.columns().every(function() + { + var index = this.index(); + var indexForGrouping = index; + var headerCell = $(this.header()); + var title = headerCell.text(); + var visible = this.visible(); + + if (!title || title.trim().length == 0 || title.startsWith("Hidden") || headerCell.hasClass("d-none")) + { + return; + } + + var shadowColumnIndex = headerCell.attr("data-shadow-rowgroup-column"); + if (shadowColumnIndex) + { + indexForGrouping = shadowColumnIndex; + } + + var checked = "checked"; + if (!visible) + { + checked = ""; + } + + columnCheckBoxesHtml += ' \ +
\ + \ + \ +
'; + + if (rowGroupDefined && headerCell.hasClass("allow-grouping")) + { + var rowGroupChecked = ""; + if (dataTable.rowGroup().enabled() && dataTable.rowGroup().dataSrc() == index) + { + rowGroupChecked = "checked"; + } + + rowGroupRadioBoxesHtml += ' \ +
\ + \ + \ +
'; + } + }); + + var message = '\ +
\ +
' + __t('Table options') + '
\ +
\ +
' + __t('Hide/view columns') + '
\ +
\ + ' + columnCheckBoxesHtml + ' \ +
\ +
'; + + if (rowGroupDefined) + { + message += ' \ +
\ +
' + __t('Group by') + '
\ +
\ + ' + rowGroupRadioBoxesHtml + ' \ +
\ +
'; + } + + bootbox.dialog({ + message: message, + size: 'small', + backdrop: true, + closeButton: false, + buttons: { + reset: { + label: __t('Reset'), + className: 'btn-outline-danger float-left responsive-button', + callback: function() + { + bootbox.confirm({ + message: __t("Are you sure to reset the table options?"), + buttons: { + cancel: { + label: 'No', + className: 'btn-danger' + }, + confirm: { + label: 'Yes', + className: 'btn-success' + } + }, + callback: function(result) + { + if (result) + { + var dataTable = $(dataTableSelector).DataTable(); + var tableId = dataTable.settings()[0].sTableId; + + // Delete rowgroup settings + Grocy.FrontendHelpers.DeleteUserSetting('datatables_rowGroup_' + tableId); + + // Delete state settings + dataTable.state.clear(); + } + bootbox.hideAll(); + } + }); + } + }, + ok: { + label: __t('OK'), + className: 'btn-primary responsive-button', + callback: function() + { + bootbox.hideAll(); + } + } + } + }); +}); + +$(document).on("click", ".change-table-columns-visibility-toggle", function() +{ + var dataTableSelector = $(this).attr("data-table-selector"); + var columnIndex = $(this).attr("data-column-index"); + var dataTable = $(dataTableSelector).DataTable(); + + dataTable.columns(columnIndex).visible(this.checked); + LoadImagesLazy(); +}); + + +$(document).on("click", ".change-table-columns-rowgroup-toggle", function() +{ + var dataTableSelector = $(this).attr("data-table-selector"); + var columnIndex = $(this).attr("data-column-index"); + var dataTable = $(dataTableSelector).DataTable(); + var rowGroup; + + if (columnIndex == -1) + { + rowGroup = { + enable: false + }; + + dataTable.rowGroup().enable(false); + + // Remove fixed order + dataTable.order.fixed({}); + } + else + { + rowGroup = { + enable: true, + dataSrc: columnIndex + } + + dataTable.rowGroup().enable(true); + dataTable.rowGroup().dataSrc(columnIndex); + + // Apply fixed order for group column + dataTable.order.fixed({ + pre: [columnIndex, 'asc'] + }); + } + + var settingKey = 'datatables_rowGroup_' + dataTable.settings()[0].sTableId; + Grocy.FrontendHelpers.SaveUserSetting(settingKey, JSON.stringify(rowGroup)); + + dataTable.draw(); +}); diff --git a/public/js/grocy_summernote.js b/public/js/grocy_summernote.js new file mode 100644 index 00000000..3c8bf152 --- /dev/null +++ b/public/js/grocy_summernote.js @@ -0,0 +1,13 @@ +$("textarea.wysiwyg-editor").summernote({ + minHeight: "300px", + lang: __t("summernote_locale"), + callbacks: { + onImageLinkInsert: function(url) + { + // Summernote workaround: Make images responsive + // By adding the "img-fluid" class to the img tag + $img = $('').attr({ src: url, class: "img-fluid" }) + $(this).summernote("insertNode", $img[0]); + } + } +}); diff --git a/views/barcodescannertesting.blade.php b/views/barcodescannertesting.blade.php index ff3b17cb..91c80a00 100644 --- a/views/barcodescannertesting.blade.php +++ b/views/barcodescannertesting.blade.php @@ -1,12 +1,9 @@ +@php require_frontend_packages(['animatecss']); @endphp + @extends('layout.default') @section('title', $__t('Barcode scanner testing')) -@push('pageScripts') - -@endpush - @section('content')
diff --git a/views/batteries.blade.php b/views/batteries.blade.php index d95341e1..1e518305 100644 --- a/views/batteries.blade.php +++ b/views/batteries.blade.php @@ -1,3 +1,5 @@ +@php require_frontend_packages(['datatables']); @endphp + @extends('layout.default') @section('title', $__t('Batteries')) diff --git a/views/batteriesjournal.blade.php b/views/batteriesjournal.blade.php index 3af1a46e..2f891658 100644 --- a/views/batteriesjournal.blade.php +++ b/views/batteriesjournal.blade.php @@ -1,3 +1,5 @@ +@php require_frontend_packages(['datatables']); @endphp + @extends('layout.default') @section('title', $__t('Batteries journal')) diff --git a/views/batteriesoverview.blade.php b/views/batteriesoverview.blade.php index dd14df28..f865b643 100644 --- a/views/batteriesoverview.blade.php +++ b/views/batteriesoverview.blade.php @@ -1,12 +1,9 @@ +@php require_frontend_packages(['datatables', 'animatecss']); @endphp + @extends('layout.default') @section('title', $__t('Batteries overview')) -@push('pageStyles') - -@endpush - @section('content')
diff --git a/views/batterytracking.blade.php b/views/batterytracking.blade.php index 2691ccd5..1c50ab08 100644 --- a/views/batterytracking.blade.php +++ b/views/batterytracking.blade.php @@ -1,3 +1,5 @@ +@php require_frontend_packages(['bootstrap-combobox']); @endphp + @extends('layout.default') @section('title', $__t('Battery tracking')) diff --git a/views/calendar.blade.php b/views/calendar.blade.php index 9a4b71a8..9511b07b 100644 --- a/views/calendar.blade.php +++ b/views/calendar.blade.php @@ -1,18 +1,9 @@ +@php require_frontend_packages(['fullcalendar', 'bwipjs']); @endphp + @extends('layout.default') @section('title', $__t('Calendar')) -@push('pageScripts') - -@if(!empty($__t('fullcalendar_locale') && $__t('fullcalendar_locale') != 'x'))@endif - -@endpush - -@push('pageStyles') - -@endpush - @section('content')
diff --git a/views/choreform.blade.php b/views/choreform.blade.php index 37316b67..32801074 100644 --- a/views/choreform.blade.php +++ b/views/choreform.blade.php @@ -1,3 +1,5 @@ +@php require_frontend_packages(['bootstrap-select']); @endphp + @extends('layout.default') @if($mode == 'edit') diff --git a/views/chores.blade.php b/views/chores.blade.php index 4f1b3027..cc872c80 100644 --- a/views/chores.blade.php +++ b/views/chores.blade.php @@ -1,3 +1,5 @@ +@php require_frontend_packages(['datatables']); @endphp + @extends('layout.default') @section('title', $__t('Chores')) diff --git a/views/choresjournal.blade.php b/views/choresjournal.blade.php index 63fc921d..297ae83b 100644 --- a/views/choresjournal.blade.php +++ b/views/choresjournal.blade.php @@ -1,3 +1,5 @@ +@php require_frontend_packages(['datatables']); @endphp + @extends('layout.default') @section('title', $__t('Chores journal')) diff --git a/views/choresoverview.blade.php b/views/choresoverview.blade.php index 76960448..cf6d177b 100644 --- a/views/choresoverview.blade.php +++ b/views/choresoverview.blade.php @@ -1,12 +1,9 @@ +@php require_frontend_packages(['datatables', 'animatecss']); @endphp + @extends('layout.default') @section('title', $__t('Chores overview')) -@push('pageStyles') - -@endpush - @section('content')
diff --git a/views/choretracking.blade.php b/views/choretracking.blade.php index 372bd339..45d03440 100644 --- a/views/choretracking.blade.php +++ b/views/choretracking.blade.php @@ -1,3 +1,5 @@ +@php require_frontend_packages(['bootstrap-combobox']); @endphp + @extends('layout.default') @section('title', $__t('Chore tracking')) diff --git a/views/components/barcodescanner.blade.php b/views/components/barcodescanner.blade.php index 9f28729a..98b4ad66 100644 --- a/views/components/barcodescanner.blade.php +++ b/views/components/barcodescanner.blade.php @@ -1,17 +1,13 @@ @if (!GROCY_FEATURE_FLAG_DISABLE_BROWSER_BARCODE_CAMERA_SCANNING) +@php require_frontend_packages(['quagga2']); @endphp + @once @push('componentScripts') @endpush @endonce -@once -@push('pageScripts') - -@endpush -@endonce - @push('pageStyles') @endpush diff --git a/views/components/calendarcard.blade.php b/views/components/calendarcard.blade.php index 62b26872..8c37339b 100644 --- a/views/components/calendarcard.blade.php +++ b/views/components/calendarcard.blade.php @@ -1,3 +1,5 @@ +@php require_frontend_packages(['tempusdominus']); @endphp + @once @push('componentScripts') diff --git a/views/components/datetimepicker.blade.php b/views/components/datetimepicker.blade.php index 03512e31..12f60bfc 100644 --- a/views/components/datetimepicker.blade.php +++ b/views/components/datetimepicker.blade.php @@ -1,3 +1,5 @@ +@php require_frontend_packages(['tempusdominus']); @endphp + @once @push('componentScripts') diff --git a/views/components/datetimepicker2.blade.php b/views/components/datetimepicker2.blade.php index 6225fb31..6b770e8d 100644 --- a/views/components/datetimepicker2.blade.php +++ b/views/components/datetimepicker2.blade.php @@ -1,3 +1,5 @@ +@php require_frontend_packages(['tempusdominus']); @endphp + @once @push('componentScripts') diff --git a/views/components/locationpicker.blade.php b/views/components/locationpicker.blade.php index d26fcecc..46842f41 100644 --- a/views/components/locationpicker.blade.php +++ b/views/components/locationpicker.blade.php @@ -1,3 +1,5 @@ +@php require_frontend_packages(['bootstrap-combobox']); @endphp + @once @push('componentScripts') diff --git a/views/components/productcard.blade.php b/views/components/productcard.blade.php index 4a9a230a..2e3ae266 100644 --- a/views/components/productcard.blade.php +++ b/views/components/productcard.blade.php @@ -1,6 +1,7 @@ +@php require_frontend_packages(['chartjs']); @endphp + @once @push('componentScripts') - @endpush @endonce diff --git a/views/components/productpicker.blade.php b/views/components/productpicker.blade.php index 31b8b427..e9815c82 100644 --- a/views/components/productpicker.blade.php +++ b/views/components/productpicker.blade.php @@ -1,3 +1,5 @@ +@php require_frontend_packages(['bootstrap-combobox']); @endphp + @once @push('componentScripts') diff --git a/views/components/recipepicker.blade.php b/views/components/recipepicker.blade.php index ce47b30e..c2de6cfa 100644 --- a/views/components/recipepicker.blade.php +++ b/views/components/recipepicker.blade.php @@ -1,3 +1,5 @@ +@php require_frontend_packages(['bootstrap-combobox']); @endphp + @once @push('componentScripts') diff --git a/views/components/shoppinglocationpicker.blade.php b/views/components/shoppinglocationpicker.blade.php index fe3e4ce2..1a939e8b 100644 --- a/views/components/shoppinglocationpicker.blade.php +++ b/views/components/shoppinglocationpicker.blade.php @@ -1,3 +1,5 @@ +@php require_frontend_packages(['bootstrap-combobox']); @endphp + @once @push('componentScripts') diff --git a/views/components/userfieldsform.blade.php b/views/components/userfieldsform.blade.php index 6d226aa3..327dd5d2 100644 --- a/views/components/userfieldsform.blade.php +++ b/views/components/userfieldsform.blade.php @@ -1,3 +1,5 @@ +@php require_frontend_packages(['bootstrap-select']); @endphp + @once @push('componentScripts') diff --git a/views/components/userpicker.blade.php b/views/components/userpicker.blade.php index d532638c..ec1bbd4b 100644 --- a/views/components/userpicker.blade.php +++ b/views/components/userpicker.blade.php @@ -1,3 +1,5 @@ +@php require_frontend_packages(['bootstrap-combobox']); @endphp + @once @push('componentScripts') diff --git a/views/equipment.blade.php b/views/equipment.blade.php index d25dec29..a41f71ff 100644 --- a/views/equipment.blade.php +++ b/views/equipment.blade.php @@ -1,3 +1,5 @@ +@php require_frontend_packages(['datatables']); @endphp + @extends('layout.default') @section('title', $__t('Equipment')) diff --git a/views/equipmentform.blade.php b/views/equipmentform.blade.php index 3082d4c5..973eac45 100644 --- a/views/equipmentform.blade.php +++ b/views/equipmentform.blade.php @@ -1,3 +1,5 @@ +@php require_frontend_packages(['summernote']); @endphp + @extends('layout.default') @if($mode == 'edit') diff --git a/views/layout/default.blade.php b/views/layout/default.blade.php index 386b08cb..3d8ac45a 100644 --- a/views/layout/default.blade.php +++ b/views/layout/default.blade.php @@ -1,3 +1,5 @@ +@php global $GROCY_REQUIRED_FRONTEND_PACKAGES; @endphp + @@ -6,7 +8,6 @@ - @@ -19,12 +20,24 @@ @yield('title') | Grocy + + + + @if(in_array('bootstrap-combobox', $GROCY_REQUIRED_FRONTEND_PACKAGES)) + @endif + @if(in_array('bootstrap-select', $GROCY_REQUIRED_FRONTEND_PACKAGES)) + + @endif + @if(in_array('datatables', $GROCY_REQUIRED_FRONTEND_PACKAGES)) - + @endif + @if(in_array('tempusdominus', $GROCY_REQUIRED_FRONTEND_PACKAGES)) + @endif + @if(in_array('summernote', $GROCY_REQUIRED_FRONTEND_PACKAGES)) - - + @endif + @if(in_array('daterangepicker', $GROCY_REQUIRED_FRONTEND_PACKAGES)) + + @endif + @if(!empty($__t('moment_locale') && $__t('moment_locale') != 'x'))@endif + + + + + + + @if(in_array('bootstrap-combobox', $GROCY_REQUIRED_FRONTEND_PACKAGES)) + @endif + @if(in_array('datatables', $GROCY_REQUIRED_FRONTEND_PACKAGES)) @@ -681,16 +715,37 @@ - + @endif + @if(in_array('tempusdominus', $GROCY_REQUIRED_FRONTEND_PACKAGES)) - - + @endif + @if(in_array('summernote', $GROCY_REQUIRED_FRONTEND_PACKAGES)) @if(!empty($__t('summernote_locale') && $__t('summernote_locale') != 'x'))@endif + @endif + @if(in_array('bootstrap-select', $GROCY_REQUIRED_FRONTEND_PACKAGES)) @if(!empty($__t('bootstrap-select_locale') && $__t('bootstrap-select_locale') != 'x'))@endif - - + @endif + @if(in_array('fullcalendar', $GROCY_REQUIRED_FRONTEND_PACKAGES)) + + @if(!empty($__t('fullcalendar_locale') && $__t('fullcalendar_locale') != 'x'))@endif + @endif + @if(in_array('daterangepicker', $GROCY_REQUIRED_FRONTEND_PACKAGES)) + + @endif + @if(in_array('quagga2', $GROCY_REQUIRED_FRONTEND_PACKAGES)) + + @endif + @if(in_array('bwipjs', $GROCY_REQUIRED_FRONTEND_PACKAGES)) + + @endif + @if(in_array('chartjs', $GROCY_REQUIRED_FRONTEND_PACKAGES)) + + + + + @endif @@ -700,6 +755,13 @@ + @if(in_array('datatables', $GROCY_REQUIRED_FRONTEND_PACKAGES)) + + @endif + @if(in_array('summernote', $GROCY_REQUIRED_FRONTEND_PACKAGES)) + + @endif + @stack('pageScripts') @stack('componentScripts') diff --git a/views/locationcontentsheet.blade.php b/views/locationcontentsheet.blade.php index 81096f7f..082a2ea5 100644 --- a/views/locationcontentsheet.blade.php +++ b/views/locationcontentsheet.blade.php @@ -1,3 +1,5 @@ +@php require_frontend_packages(['datatables']); @endphp + @extends('layout.default') @section('title', $__t('Location Content Sheet')) diff --git a/views/locations.blade.php b/views/locations.blade.php index 68f77cad..a22b7463 100644 --- a/views/locations.blade.php +++ b/views/locations.blade.php @@ -1,3 +1,5 @@ +@php require_frontend_packages(['datatables']); @endphp + @extends('layout.default') @section('title', $__t('Locations')) diff --git a/views/manageapikeys.blade.php b/views/manageapikeys.blade.php index 2a0c20c0..9e342862 100644 --- a/views/manageapikeys.blade.php +++ b/views/manageapikeys.blade.php @@ -1,16 +1,9 @@ +@php require_frontend_packages(['datatables', 'animatecss', 'bwipjs']); @endphp + @extends('layout.default') @section('title', $__t('API keys')) -@push('pageScripts') - -@endpush - -@push('pageStyles') - -@endpush - @section('content')
diff --git a/views/mealplan.blade.php b/views/mealplan.blade.php index d282171a..14db4758 100644 --- a/views/mealplan.blade.php +++ b/views/mealplan.blade.php @@ -1,16 +1,10 @@ +@php require_frontend_packages(['fullcalendar']); @endphp + @extends('layout.default') @section('title', $__t('Meal plan')) -@push('pageScripts') - -@if(!empty($__t('fullcalendar_locale') && $__t('fullcalendar_locale') != 'x'))@endif -@endpush - @push('pageStyles') - -