mirror of
https://github.com/grocy/grocy.git
synced 2026-04-05 12:26:15 +02:00
Move components to main grocy bundle
Less stalled requests. Still not optimal though.
This commit is contained in:
parent
b7a1c870f0
commit
598860017b
306
js/components/barcodescanner.js
Normal file
306
js/components/barcodescanner.js
Normal file
|
|
@ -0,0 +1,306 @@
|
|||
import Quagga from '@ericblade/quagga2/dist/quagga';
|
||||
|
||||
function barcodescanner(Grocy)
|
||||
{
|
||||
Grocy.Components.BarcodeScanner = {};
|
||||
|
||||
Grocy.Components.BarcodeScanner.LiveVideoSizeAdjusted = false;
|
||||
Grocy.Components.BarcodeScanner.CheckCapabilities = async function()
|
||||
{
|
||||
var track = Quagga.CameraAccess.getActiveTrack();
|
||||
var capabilities = {};
|
||||
if (typeof track.getCapabilities === 'function')
|
||||
{
|
||||
capabilities = track.getCapabilities();
|
||||
}
|
||||
|
||||
// If there is more than 1 camera, show the camera selection
|
||||
var cameras = await Quagga.CameraAccess.enumerateVideoDevices();
|
||||
var cameraSelect = document.querySelector('.cameraSelect-wrapper');
|
||||
if (cameraSelect)
|
||||
{
|
||||
cameraSelect.style.display = cameras.length > 1 ? 'inline-block' : 'none';
|
||||
}
|
||||
|
||||
// Check if the camera is capable to turn on a torch.
|
||||
var canTorch = typeof capabilities.torch === 'boolean' && capabilities.torch
|
||||
// Remove the torch button, if either the device can not torch or AutoTorchOn is set.
|
||||
var node = document.querySelector('.torch');
|
||||
if (node)
|
||||
{
|
||||
node.style.display = canTorch && !Grocy.FeatureFlags.GROCY_FEATURE_FLAG_AUTO_TORCH_ON_WITH_CAMERA ? 'inline-block' : 'none';
|
||||
}
|
||||
// If AutoTorchOn is set, turn on the torch.
|
||||
if (canTorch && Grocy.FeatureFlags.GROCY_FEATURE_FLAG_AUTO_TORCH_ON_WITH_CAMERA)
|
||||
{
|
||||
Grocy.Components.BarcodeScanner.TorchOn(track);
|
||||
}
|
||||
|
||||
// Reduce the height of the video, if it's higher than then the viewport
|
||||
if (!Grocy.Components.BarcodeScanner.LiveVideoSizeAdjusted)
|
||||
{
|
||||
var bc = document.getElementById('barcodescanner-container');
|
||||
if (bc)
|
||||
{
|
||||
var bcAspectRatio = bc.offsetWidth / bc.offsetHeight;
|
||||
var settings = track.getSettings();
|
||||
if (bcAspectRatio > settings.aspectRatio)
|
||||
{
|
||||
var v = document.querySelector('#barcodescanner-livestream video')
|
||||
if (v)
|
||||
{
|
||||
var c = document.querySelector('#barcodescanner-livestream canvas')
|
||||
var newWidth = v.clientWidth / bcAspectRatio * settings.aspectRatio + 'px';
|
||||
v.style.width = newWidth;
|
||||
c.style.width = newWidth;
|
||||
}
|
||||
}
|
||||
|
||||
Grocy.Components.BarcodeScanner.LiveVideoSizeAdjusted = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Grocy.Components.BarcodeScanner.StartScanning = function()
|
||||
{
|
||||
Grocy.Components.BarcodeScanner.DecodedCodesCount = 0;
|
||||
Grocy.Components.BarcodeScanner.DecodedCodesErrorCount = 0;
|
||||
|
||||
Quagga.init({
|
||||
inputStream: {
|
||||
name: "Live",
|
||||
type: "LiveStream",
|
||||
target: document.querySelector("#barcodescanner-livestream"),
|
||||
constraints: {
|
||||
facingMode: "environment",
|
||||
...(window.localStorage.getItem('cameraId') && { deviceId: window.localStorage.getItem('cameraId') }) // If preferred cameraId is set, request to use that specific camera
|
||||
}
|
||||
},
|
||||
locator: {
|
||||
patchSize: Grocy.UserSettings.quagga2_patchsize,
|
||||
halfSample: Grocy.UserSettings.quagga2_halfsample,
|
||||
debug: {
|
||||
showCanvas: Grocy.UserSettings.quagga2_debug,
|
||||
showPatches: Grocy.UserSettings.quagga2_debug,
|
||||
showFoundPatches: Grocy.UserSettings.quagga2_debug,
|
||||
showSkeleton: Grocy.UserSettings.quagga2_debug,
|
||||
showLabels: Grocy.UserSettings.quagga2_debug,
|
||||
showPatchLabels: Grocy.UserSettings.quagga2_debug,
|
||||
showRemainingPatchLabels: Grocy.UserSettings.quagga2_debug,
|
||||
boxFromPatches: {
|
||||
showTransformed: Grocy.UserSettings.quagga2_debug,
|
||||
showTransformedBox: Grocy.UserSettings.quagga2_debug,
|
||||
showBB: Grocy.UserSettings.quagga2_debug
|
||||
}
|
||||
}
|
||||
},
|
||||
numOfWorkers: Grocy.UserSettings.quagga2_numofworkers,
|
||||
frequency: Grocy.UserSettings.quagga2_frequency,
|
||||
decoder: {
|
||||
readers: [
|
||||
"ean_reader",
|
||||
"ean_8_reader",
|
||||
"code_128_reader"
|
||||
],
|
||||
debug: {
|
||||
showCanvas: Grocy.UserSettings.quagga2_debug,
|
||||
showPatches: Grocy.UserSettings.quagga2_debug,
|
||||
showFoundPatches: Grocy.UserSettings.quagga2_debug,
|
||||
showSkeleton: Grocy.UserSettings.quagga2_debug,
|
||||
showLabels: Grocy.UserSettings.quagga2_debug,
|
||||
showPatchLabels: Grocy.UserSettings.quagga2_debug,
|
||||
showRemainingPatchLabels: Grocy.UserSettings.quagga2_debug,
|
||||
boxFromPatches: {
|
||||
showTransformed: Grocy.UserSettings.quagga2_debug,
|
||||
showTransformedBox: Grocy.UserSettings.quagga2_debug,
|
||||
showBB: Grocy.UserSettings.quagga2_debug
|
||||
}
|
||||
}
|
||||
},
|
||||
locate: true
|
||||
}, function(error)
|
||||
{
|
||||
// error *needs* to be logged here, otherwise the stack trace is lying.
|
||||
console.error(error);
|
||||
if (error)
|
||||
{
|
||||
Grocy.FrontendHelpers.ShowGenericError("Error while initializing the barcode scanning library", error.message);
|
||||
toastr.info(Grocy.translate("Camera access is only possible when supported and allowed by your browser and when grocy is served via a secure (https://) connection"));
|
||||
window.localStorage.removeItem("cameraId");
|
||||
setTimeout(function()
|
||||
{
|
||||
bootbox.hideAll();
|
||||
}, 500);
|
||||
return;
|
||||
}
|
||||
|
||||
Grocy.Components.BarcodeScanner.CheckCapabilities();
|
||||
|
||||
Quagga.start();
|
||||
});
|
||||
}
|
||||
|
||||
Grocy.Components.BarcodeScanner.StopScanning = function()
|
||||
{
|
||||
Quagga.stop();
|
||||
|
||||
Grocy.Components.BarcodeScanner.DecodedCodesCount = 0;
|
||||
Grocy.Components.BarcodeScanner.DecodedCodesErrorCount = 0;
|
||||
|
||||
bootbox.hideAll();
|
||||
}
|
||||
|
||||
Grocy.Components.BarcodeScanner.TorchOn = function(track)
|
||||
{
|
||||
if (track)
|
||||
{
|
||||
track.applyConstraints({
|
||||
advanced: [
|
||||
{
|
||||
torch: true
|
||||
}
|
||||
]
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Quagga.onDetected(function(result)
|
||||
{
|
||||
$.each(result.codeResult.decodedCodes, function(id, error)
|
||||
{
|
||||
if (error.error != undefined)
|
||||
{
|
||||
Grocy.Components.BarcodeScanner.DecodedCodesCount++;
|
||||
Grocy.Components.BarcodeScanner.DecodedCodesErrorCount += parseFloat(error.error);
|
||||
}
|
||||
});
|
||||
|
||||
if (Grocy.Components.BarcodeScanner.DecodedCodesErrorCount / Grocy.Components.BarcodeScanner.DecodedCodesCount < 0.15)
|
||||
{
|
||||
Grocy.Components.BarcodeScanner.StopScanning();
|
||||
$(document).trigger("Grocy.BarcodeScanned", [result.codeResult.code, Grocy.Components.BarcodeScanner.CurrentTarget]);
|
||||
}
|
||||
});
|
||||
|
||||
Quagga.onProcessed(function(result)
|
||||
{
|
||||
var drawingCtx = Quagga.canvas.ctx.overlay;
|
||||
var drawingCanvas = Quagga.canvas.dom.overlay;
|
||||
|
||||
if (result)
|
||||
{
|
||||
if (result.boxes)
|
||||
{
|
||||
drawingCtx.clearRect(0, 0, parseInt(drawingCanvas.getAttribute("width")), parseInt(drawingCanvas.getAttribute("height")));
|
||||
result.boxes.filter(function(box)
|
||||
{
|
||||
return box !== result.box;
|
||||
}).forEach(function(box)
|
||||
{
|
||||
Quagga.ImageDebug.drawPath(box, { x: 0, y: 1 }, drawingCtx, { color: "yellow", lineWidth: 4 });
|
||||
});
|
||||
}
|
||||
|
||||
if (result.box)
|
||||
{
|
||||
Quagga.ImageDebug.drawPath(result.box, { x: 0, y: 1 }, drawingCtx, { color: "green", lineWidth: 4 });
|
||||
}
|
||||
|
||||
if (result.codeResult && result.codeResult.code)
|
||||
{
|
||||
Quagga.ImageDebug.drawPath(result.line, { x: 'x', y: 'y' }, drawingCtx, { color: "red", lineWidth: 4 });
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
$(document).on("click", "#barcodescanner-start-button", async function(e)
|
||||
{
|
||||
e.preventDefault();
|
||||
var inputElement = $(e.currentTarget).prev();
|
||||
if (inputElement.hasAttr("disabled"))
|
||||
{
|
||||
// Do nothing and disable the barcode scanner start button
|
||||
$(e.currentTarget).addClass("disabled");
|
||||
return;
|
||||
}
|
||||
|
||||
Grocy.Components.BarcodeScanner.CurrentTarget = inputElement.attr("data-target");
|
||||
|
||||
var dialog = bootbox.dialog({
|
||||
message: '<div id="barcodescanner-container" class="col"><div id="barcodescanner-livestream"></div><div id="debug"></div></div>',
|
||||
title: Grocy.translate('Scan a barcode'),
|
||||
onEscape: function()
|
||||
{
|
||||
Grocy.Components.BarcodeScanner.StopScanning();
|
||||
},
|
||||
size: 'big',
|
||||
backdrop: true,
|
||||
closeButton: true,
|
||||
buttons: {
|
||||
torch: {
|
||||
label: '<i class="far fa-lightbulb"></i>',
|
||||
className: 'btn-warning responsive-button torch',
|
||||
callback: function()
|
||||
{
|
||||
Grocy.Components.BarcodeScanner.TorchOn(Quagga.CameraAccess.getActiveTrack());
|
||||
return false;
|
||||
}
|
||||
},
|
||||
cancel: {
|
||||
label: Grocy.translate('Cancel'),
|
||||
className: 'btn-secondary responsive-button',
|
||||
callback: function()
|
||||
{
|
||||
Grocy.Components.BarcodeScanner.StopScanning();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Add camera select to existing dialog
|
||||
dialog.find('.bootbox-body').append('<div class="form-group py-0 my-1 cameraSelect-wrapper"><select class="custom-control custom-select cameraSelect"><select class="custom-control custom-select cameraSelect" style="display: none"></select></div>');
|
||||
var cameraSelect = document.querySelector('.cameraSelect');
|
||||
|
||||
if (cameraSelect != null)
|
||||
{
|
||||
var cameras = await Quagga.CameraAccess.enumerateVideoDevices();
|
||||
cameras.forEach(camera =>
|
||||
{
|
||||
var option = document.createElement("option");
|
||||
option.text = camera.label ? camera.label : camera.deviceId; // Use camera label if it exists, else show device id
|
||||
option.value = camera.deviceId;
|
||||
cameraSelect.appendChild(option);
|
||||
});
|
||||
|
||||
// Set initial value to preferred camera if one exists - and if not, start out empty
|
||||
cameraSelect.value = window.localStorage.getItem('cameraId');
|
||||
|
||||
cameraSelect.onchange = function()
|
||||
{
|
||||
window.localStorage.setItem('cameraId', cameraSelect.value);
|
||||
Quagga.stop();
|
||||
Grocy.Components.BarcodeScanner.StartScanning();
|
||||
};
|
||||
}
|
||||
|
||||
Grocy.Components.BarcodeScanner.StartScanning();
|
||||
});
|
||||
|
||||
setTimeout(function()
|
||||
{
|
||||
$(".barcodescanner-input:visible").each(function()
|
||||
{
|
||||
if ($(this).hasAttr("disabled"))
|
||||
{
|
||||
$(this).after('<a id="barcodescanner-start-button" class="btn btn-sm btn-primary text-white disabled" data-target="' + $(this).attr("data-target") + '"><i class="fas fa-camera"></i></a>');
|
||||
}
|
||||
else
|
||||
{
|
||||
$(this).after('<a id="barcodescanner-start-button" class="btn btn-sm btn-primary text-white" data-target="' + $(this).attr("data-target") + '"><i class="fas fa-camera"></i></a>');
|
||||
}
|
||||
});
|
||||
}, 50);
|
||||
|
||||
}
|
||||
|
||||
export { barcodescanner }
|
||||
36
js/components/batterycard.js
Normal file
36
js/components/batterycard.js
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
import { EmptyElementWhenMatches } from '../helpers/extensions'
|
||||
import { RefreshContextualTimeago } from '../configs/timeago'
|
||||
|
||||
function batterycard(Grocy)
|
||||
{
|
||||
|
||||
Grocy.Components.BatteryCard = {};
|
||||
|
||||
Grocy.Components.BatteryCard.Refresh = function(batteryId)
|
||||
{
|
||||
Grocy.Api.Get('batteries/' + batteryId,
|
||||
function(batteryDetails)
|
||||
{
|
||||
$('#batterycard-battery-name').text(batteryDetails.battery.name);
|
||||
$('#batterycard-battery-used_in').text(batteryDetails.battery.used_in);
|
||||
$('#batterycard-battery-last-charged').text((batteryDetails.last_charged || Grocy.translate('never')));
|
||||
$('#batterycard-battery-last-charged-timeago').attr("datetime", batteryDetails.last_charged || '');
|
||||
$('#batterycard-battery-charge-cycles-count').text((batteryDetails.charge_cycles_count || '0'));
|
||||
|
||||
$('#batterycard-battery-edit-button').attr("href", Grocy.FormatUrl("/battery/" + batteryDetails.battery.id.toString()));
|
||||
$('#batterycard-battery-journal-button').attr("href", Grocy.FormatUrl("/batteriesjournal?embedded&battery=" + batteryDetails.battery.id.toString()));
|
||||
$('#batterycard-battery-edit-button').removeClass("disabled");
|
||||
$('#batterycard-battery-journal-button').removeClass("disabled");
|
||||
|
||||
EmptyElementWhenMatches('#batterycard-battery-last-charged-timeago', Grocy.translate('timeago_nan'));
|
||||
RefreshContextualTimeago(".batterycard");
|
||||
},
|
||||
function(xhr)
|
||||
{
|
||||
console.error(xhr);
|
||||
}
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
export { batterycard }
|
||||
45
js/components/calendarcard.js
Normal file
45
js/components/calendarcard.js
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
function calendarcard(Grocy)
|
||||
{
|
||||
$('#calendar').datetimepicker(
|
||||
{
|
||||
format: 'L',
|
||||
buttons: {
|
||||
showToday: true,
|
||||
showClose: false
|
||||
},
|
||||
calendarWeeks: true,
|
||||
locale: moment.locale(),
|
||||
icons: {
|
||||
time: 'far fa-clock',
|
||||
date: 'far fa-calendar',
|
||||
up: 'fas fa-arrow-up',
|
||||
down: 'fas fa-arrow-down',
|
||||
previous: 'fas fa-chevron-left',
|
||||
next: 'fas fa-chevron-right',
|
||||
today: 'fas fa-calendar-check',
|
||||
clear: 'far fa-trash-alt',
|
||||
close: 'far fa-times-circle'
|
||||
},
|
||||
keepOpen: true,
|
||||
inline: true,
|
||||
keyBinds: {
|
||||
up: function(widget) { },
|
||||
down: function(widget) { },
|
||||
'control up': function(widget) { },
|
||||
'control down': function(widget) { },
|
||||
left: function(widget) { },
|
||||
right: function(widget) { },
|
||||
pageUp: function(widget) { },
|
||||
pageDown: function(widget) { },
|
||||
enter: function(widget) { },
|
||||
escape: function(widget) { },
|
||||
'control space': function(widget) { },
|
||||
t: function(widget) { },
|
||||
'delete': function(widget) { }
|
||||
}
|
||||
});
|
||||
|
||||
$('#calendar').datetimepicker('show');
|
||||
}
|
||||
|
||||
export { calendarcard }
|
||||
43
js/components/chorecard.js
Normal file
43
js/components/chorecard.js
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
import { EmptyElementWhenMatches } from '../helpers/extensions'
|
||||
import { RefreshContextualTimeago } from '../configs/timeago'
|
||||
|
||||
function chorecard(Grocy)
|
||||
{
|
||||
Grocy.Components.ChoreCard = {};
|
||||
|
||||
Grocy.Components.ChoreCard.Refresh = function(choreId)
|
||||
{
|
||||
Grocy.Api.Get('chores/' + choreId,
|
||||
function(choreDetails)
|
||||
{
|
||||
$('#chorecard-chore-name').text(choreDetails.chore.name);
|
||||
$('#chorecard-chore-last-tracked').text((choreDetails.last_tracked || Grocy.translate('never')));
|
||||
$('#chorecard-chore-last-tracked-timeago').attr("datetime", choreDetails.last_tracked || '');
|
||||
$('#chorecard-chore-tracked-count').text((choreDetails.tracked_count || '0'));
|
||||
$('#chorecard-chore-last-done-by').text((choreDetails.last_done_by.display_name || Grocy.translate('Unknown')));
|
||||
|
||||
$('#chorecard-chore-edit-button').attr("href", Grocy.FormatUrl("/chore/" + choreDetails.chore.id.toString()));
|
||||
$('#chorecard-chore-journal-button').attr("href", Grocy.FormatUrl("/choresjournal?embedded&chore=" + choreDetails.chore.id.toString()));
|
||||
$('#chorecard-chore-edit-button').removeClass("disabled");
|
||||
$('#chorecard-chore-journal-button').removeClass("disabled");
|
||||
|
||||
if (choreDetails.chore.track_date_only == 1)
|
||||
{
|
||||
$("#chorecard-chore-last-tracked-timeago").addClass("timeago-date-only");
|
||||
}
|
||||
else
|
||||
{
|
||||
$("#chorecard-chore-last-tracked-timeago").removeClass("timeago-date-only");
|
||||
}
|
||||
|
||||
EmptyElementWhenMatches('#chorecard-chore-last-tracked-timeago', Grocy.translate('timeago_nan'));
|
||||
RefreshContextualTimeago(".chorecard");
|
||||
},
|
||||
function(xhr)
|
||||
{
|
||||
console.error(xhr);
|
||||
}
|
||||
);
|
||||
};
|
||||
}
|
||||
export { chorecard }
|
||||
317
js/components/datetimepicker.js
Normal file
317
js/components/datetimepicker.js
Normal file
|
|
@ -0,0 +1,317 @@
|
|||
import { EmptyElementWhenMatches } from '../helpers/extensions'
|
||||
import { RefreshContextualTimeago } from '../configs/timeago'
|
||||
|
||||
function datetimepicker(Grocy)
|
||||
{
|
||||
Grocy.Components.DateTimePicker = {};
|
||||
|
||||
Grocy.Components.DateTimePicker.GetInputElement = function()
|
||||
{
|
||||
return $('.datetimepicker').find('input').not(".form-check-input");
|
||||
}
|
||||
|
||||
Grocy.Components.DateTimePicker.GetValue = function()
|
||||
{
|
||||
return Grocy.Components.DateTimePicker.GetInputElement().val();
|
||||
}
|
||||
|
||||
Grocy.Components.DateTimePicker.SetValue = function(value)
|
||||
{
|
||||
// "Click" the shortcut checkbox when the desired value is
|
||||
// not the shortcut value and it is currently set
|
||||
var shortcutValue = $("#datetimepicker-shortcut").data("datetimepicker-shortcut-value");
|
||||
if (value != shortcutValue && $("#datetimepicker-shortcut").is(":checked"))
|
||||
{
|
||||
$("#datetimepicker-shortcut").click();
|
||||
}
|
||||
Grocy.Components.DateTimePicker.GetInputElement().val(value);
|
||||
Grocy.Components.DateTimePicker.GetInputElement().trigger('change');
|
||||
|
||||
Grocy.Components.DateTimePicker.GetInputElement().keyup();
|
||||
}
|
||||
|
||||
Grocy.Components.DateTimePicker.Clear = function()
|
||||
{
|
||||
$(".datetimepicker").datetimepicker("destroy");
|
||||
Grocy.Components.DateTimePicker.Init();
|
||||
|
||||
Grocy.Components.DateTimePicker.GetInputElement().val("");
|
||||
|
||||
// "Click" the shortcut checkbox when the desired value is
|
||||
// not the shortcut value and it is currently set
|
||||
var value = "";
|
||||
var shortcutValue = $("#datetimepicker-shortcut").data("datetimepicker-shortcut-value");
|
||||
if (value != shortcutValue && $("#datetimepicker-shortcut").is(":checked"))
|
||||
{
|
||||
$("#datetimepicker-shortcut").click();
|
||||
}
|
||||
|
||||
$('#datetimepicker-timeago').text('');
|
||||
}
|
||||
|
||||
Grocy.Components.DateTimePicker.ChangeFormat = function(format)
|
||||
{
|
||||
$(".datetimepicker").datetimepicker("destroy");
|
||||
Grocy.Components.DateTimePicker.GetInputElement().data("format", format);
|
||||
Grocy.Components.DateTimePicker.Init();
|
||||
|
||||
if (format == "YYYY-MM-DD")
|
||||
{
|
||||
Grocy.Components.DateTimePicker.GetInputElement().addClass("date-only-datetimepicker");
|
||||
}
|
||||
else
|
||||
{
|
||||
Grocy.Components.DateTimePicker.GetInputElement().removeClass("date-only-datetimepicker");
|
||||
}
|
||||
}
|
||||
|
||||
var startDate = null;
|
||||
var inputElement = Grocy.Components.DateTimePicker.GetInputElement();
|
||||
if (inputElement.data('init-with-now') === true)
|
||||
{
|
||||
startDate = moment().format(inputElement.data('format'));
|
||||
}
|
||||
if (inputElement.data('init-value').length > 0)
|
||||
{
|
||||
startDate = moment(inputElement.data('init-value')).format(Grocy.Components.DateTimePicker.GetInputElement().data('format'));
|
||||
}
|
||||
|
||||
var limitDate = moment('2999-12-31 23:59:59');
|
||||
if (Grocy.Components.DateTimePicker.GetInputElement().data('limit-end-to-now') === true)
|
||||
{
|
||||
limitDate = moment();
|
||||
}
|
||||
|
||||
Grocy.Components.DateTimePicker.Init = function()
|
||||
{
|
||||
$('.datetimepicker').datetimepicker(
|
||||
{
|
||||
format: Grocy.Components.DateTimePicker.GetInputElement().data('format'),
|
||||
buttons: {
|
||||
showToday: true,
|
||||
showClose: true
|
||||
},
|
||||
calendarWeeks: Grocy.CalendarShowWeekNumbers,
|
||||
maxDate: limitDate,
|
||||
locale: moment.locale(),
|
||||
defaultDate: startDate,
|
||||
useCurrent: false,
|
||||
icons: {
|
||||
time: 'far fa-clock',
|
||||
date: 'far fa-calendar',
|
||||
up: 'fas fa-arrow-up',
|
||||
down: 'fas fa-arrow-down',
|
||||
previous: 'fas fa-chevron-left',
|
||||
next: 'fas fa-chevron-right',
|
||||
today: 'fas fa-calendar-check',
|
||||
clear: 'far fa-trash-alt',
|
||||
close: 'far fa-times-circle'
|
||||
},
|
||||
sideBySide: true,
|
||||
keyBinds: {
|
||||
up: function(widget) { },
|
||||
down: function(widget) { },
|
||||
'control up': function(widget) { },
|
||||
'control down': function(widget) { },
|
||||
left: function(widget) { },
|
||||
right: function(widget) { },
|
||||
pageUp: function(widget) { },
|
||||
pageDown: function(widget) { },
|
||||
enter: function(widget) { },
|
||||
escape: function(widget) { },
|
||||
'control space': function(widget) { },
|
||||
t: function(widget) { },
|
||||
'delete': function(widget) { }
|
||||
}
|
||||
});
|
||||
}
|
||||
Grocy.Components.DateTimePicker.Init();
|
||||
|
||||
Grocy.Components.DateTimePicker.GetInputElement().on('keyup', function(e)
|
||||
{
|
||||
$('.datetimepicker').datetimepicker('hide');
|
||||
|
||||
var value = Grocy.Components.DateTimePicker.GetValue();
|
||||
var now = new Date();
|
||||
var centuryStart = Number.parseInt(now.getFullYear().toString().substring(0, 2) + '00');
|
||||
var centuryEnd = Number.parseInt(now.getFullYear().toString().substring(0, 2) + '99');
|
||||
var format = Grocy.Components.DateTimePicker.GetInputElement().data('format');
|
||||
var nextInputElement = $(Grocy.Components.DateTimePicker.GetInputElement().data('next-input-selector'));
|
||||
|
||||
//If input is empty and any arrow key is pressed, set date to today
|
||||
if (value.length === 0 && (e.keyCode === 38 || e.keyCode === 40 || e.keyCode === 37 || e.keyCode === 39))
|
||||
{
|
||||
Grocy.Components.DateTimePicker.SetValue(moment(new Date(), format, true).format(format));
|
||||
nextInputElement.focus();
|
||||
}
|
||||
else if (value === 'x' || value === 'X')
|
||||
{
|
||||
Grocy.Components.DateTimePicker.SetValue(moment('2999-12-31 23:59:59').format(format));
|
||||
nextInputElement.focus();
|
||||
}
|
||||
else if (value.length === 4 && !(Number.parseInt(value) > centuryStart && Number.parseInt(value) < centuryEnd))
|
||||
{
|
||||
var date = moment((new Date()).getFullYear().toString() + value);
|
||||
if (date.isBefore(moment()))
|
||||
{
|
||||
date.add(1, "year");
|
||||
}
|
||||
Grocy.Components.DateTimePicker.SetValue(date.format(format));
|
||||
nextInputElement.focus();
|
||||
}
|
||||
else if (value.length === 8 && $.isNumeric(value))
|
||||
{
|
||||
Grocy.Components.DateTimePicker.SetValue(value.replace(/(\d{4})(\d{2})(\d{2})/, '$1-$2-$3'));
|
||||
nextInputElement.focus();
|
||||
}
|
||||
else if (value.length === 7 && $.isNumeric(value.substring(0, 6)) && (value.substring(6, 7).toLowerCase() === "e" || value.substring(6, 7).toLowerCase() === "+"))
|
||||
{
|
||||
var endOfMonth = moment(value.substring(0, 4) + "-" + value.substring(4, 6) + "-01").endOf("month");
|
||||
Grocy.Components.DateTimePicker.SetValue(endOfMonth.format(format));
|
||||
nextInputElement.focus();
|
||||
}
|
||||
else
|
||||
{
|
||||
var dateObj = moment(value, format, true);
|
||||
if (dateObj.isValid())
|
||||
{
|
||||
if (e.shiftKey)
|
||||
{
|
||||
// WITH shift modifier key
|
||||
|
||||
if (e.keyCode === 38) // Up
|
||||
{
|
||||
Grocy.Components.DateTimePicker.SetValue(dateObj.add(-1, 'months').format(format));
|
||||
}
|
||||
else if (e.keyCode === 40) // Down
|
||||
{
|
||||
Grocy.Components.DateTimePicker.SetValue(dateObj.add(1, 'months').format(format));
|
||||
}
|
||||
else if (e.keyCode === 37) // Left
|
||||
{
|
||||
Grocy.Components.DateTimePicker.SetValue(dateObj.add(-1, 'years').format(format));
|
||||
}
|
||||
else if (e.keyCode === 39) // Right
|
||||
{
|
||||
Grocy.Components.DateTimePicker.SetValue(dateObj.add(1, 'years').format(format));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// WITHOUT shift modifier key
|
||||
|
||||
if (e.keyCode === 38) // Up
|
||||
{
|
||||
Grocy.Components.DateTimePicker.SetValue(dateObj.add(-1, 'days').format(format));
|
||||
}
|
||||
else if (e.keyCode === 40) // Down
|
||||
{
|
||||
Grocy.Components.DateTimePicker.SetValue(dateObj.add(1, 'days').format(format));
|
||||
}
|
||||
else if (e.keyCode === 37) // Left
|
||||
{
|
||||
Grocy.Components.DateTimePicker.SetValue(dateObj.add(-1, 'weeks').format(format));
|
||||
}
|
||||
else if (e.keyCode === 39) // Right
|
||||
{
|
||||
Grocy.Components.DateTimePicker.SetValue(dateObj.add(1, 'weeks').format(format));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Custom validation
|
||||
value = Grocy.Components.DateTimePicker.GetValue();
|
||||
dateObj = moment(value, format, true);
|
||||
var element = Grocy.Components.DateTimePicker.GetInputElement()[0];
|
||||
if (!dateObj.isValid())
|
||||
{
|
||||
if ($(element).hasAttr("required"))
|
||||
{
|
||||
element.setCustomValidity("error");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Grocy.Components.DateTimePicker.GetInputElement().data('limit-end-to-now') === true && dateObj.isAfter(moment()))
|
||||
{
|
||||
element.setCustomValidity("error");
|
||||
}
|
||||
else if (Grocy.Components.DateTimePicker.GetInputElement().data('limit-start-to-now') === true && dateObj.isBefore(moment()))
|
||||
{
|
||||
element.setCustomValidity("error");
|
||||
}
|
||||
else
|
||||
{
|
||||
element.setCustomValidity("");
|
||||
}
|
||||
|
||||
var earlierThanLimit = Grocy.Components.DateTimePicker.GetInputElement().data("earlier-than-limit");
|
||||
if (!earlierThanLimit.isEmpty())
|
||||
{
|
||||
if (moment(value).isBefore(moment(earlierThanLimit)))
|
||||
{
|
||||
$("#datetimepicker-earlier-than-info").removeClass("d-none");
|
||||
}
|
||||
else
|
||||
{
|
||||
$("#datetimepicker-earlier-than-info").addClass("d-none");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// "Click" the shortcut checkbox when the shortcut value was
|
||||
// entered manually and it is currently not set
|
||||
var shortcutValue = $("#datetimepicker-shortcut").data("datetimepicker-shortcut-value");
|
||||
if (value == shortcutValue && !$("#datetimepicker-shortcut").is(":checked"))
|
||||
{
|
||||
$("#datetimepicker-shortcut").click();
|
||||
}
|
||||
});
|
||||
|
||||
Grocy.Components.DateTimePicker.GetInputElement().on('input', function(e)
|
||||
{
|
||||
$('#datetimepicker-timeago').attr("datetime", Grocy.Components.DateTimePicker.GetValue());
|
||||
EmptyElementWhenMatches('#datetimepicker-timeago', Grocy.translate('timeago_nan'));
|
||||
RefreshContextualTimeago("#datetimepicker-wrapper");
|
||||
});
|
||||
|
||||
$('.datetimepicker').on('update.datetimepicker', function(e)
|
||||
{
|
||||
Grocy.Components.DateTimePicker.GetInputElement().trigger('input');
|
||||
Grocy.Components.DateTimePicker.GetInputElement().trigger('change');
|
||||
Grocy.Components.DateTimePicker.GetInputElement().trigger('keypress');
|
||||
Grocy.Components.DateTimePicker.GetInputElement().trigger('keyup');
|
||||
});
|
||||
|
||||
$('.datetimepicker').on('hide.datetimepicker', function(e)
|
||||
{
|
||||
Grocy.Components.DateTimePicker.GetInputElement().trigger('input');
|
||||
Grocy.Components.DateTimePicker.GetInputElement().trigger('change');
|
||||
Grocy.Components.DateTimePicker.GetInputElement().trigger('keypress');
|
||||
Grocy.Components.DateTimePicker.GetInputElement().trigger('keyup');
|
||||
});
|
||||
|
||||
$("#datetimepicker-shortcut").on("click", function()
|
||||
{
|
||||
if (this.checked)
|
||||
{
|
||||
var value = $("#datetimepicker-shortcut").data("datetimepicker-shortcut-value");
|
||||
Grocy.Components.DateTimePicker.SetValue(value);
|
||||
Grocy.Components.DateTimePicker.GetInputElement().attr("readonly", "");
|
||||
$(Grocy.Components.DateTimePicker.GetInputElement().data('next-input-selector')).focus();
|
||||
}
|
||||
else
|
||||
{
|
||||
Grocy.Components.DateTimePicker.SetValue("");
|
||||
Grocy.Components.DateTimePicker.GetInputElement().removeAttr("readonly");
|
||||
Grocy.Components.DateTimePicker.GetInputElement().focus();
|
||||
}
|
||||
|
||||
Grocy.Components.DateTimePicker.GetInputElement().trigger('input');
|
||||
Grocy.Components.DateTimePicker.GetInputElement().trigger('change');
|
||||
Grocy.Components.DateTimePicker.GetInputElement().trigger('keypress');
|
||||
});
|
||||
}
|
||||
|
||||
export { datetimepicker }
|
||||
317
js/components/datetimepicker2.js
Normal file
317
js/components/datetimepicker2.js
Normal file
|
|
@ -0,0 +1,317 @@
|
|||
import { EmptyElementWhenMatches } from '../helpers/extensions'
|
||||
import { RefreshContextualTimeago } from '../configs/timeago'
|
||||
|
||||
function datetimepicker2(Grocy)
|
||||
{
|
||||
Grocy.Components.DateTimePicker2 = {};
|
||||
|
||||
Grocy.Components.DateTimePicker2.GetInputElement = function()
|
||||
{
|
||||
return $('.datetimepicker2').find('input').not(".form-check-input");
|
||||
}
|
||||
|
||||
Grocy.Components.DateTimePicker2.GetValue = function()
|
||||
{
|
||||
return Grocy.Components.DateTimePicker2.GetInputElement().val();
|
||||
}
|
||||
|
||||
Grocy.Components.DateTimePicker2.SetValue = function(value)
|
||||
{
|
||||
// "Click" the shortcut checkbox when the desired value is
|
||||
// not the shortcut value and it is currently set
|
||||
var shortcutValue = $("#datetimepicker2-shortcut").data("datetimepicker2-shortcut-value");
|
||||
if (value != shortcutValue && $("#datetimepicker2-shortcut").is(":checked"))
|
||||
{
|
||||
$("#datetimepicker2-shortcut").click();
|
||||
}
|
||||
|
||||
Grocy.Components.DateTimePicker2.GetInputElement().val(value);
|
||||
Grocy.Components.DateTimePicker2.GetInputElement().trigger('change');
|
||||
|
||||
Grocy.Components.DateTimePicker2.GetInputElement().keyup();
|
||||
}
|
||||
|
||||
Grocy.Components.DateTimePicker2.Clear = function()
|
||||
{
|
||||
$(".datetimepicker2").datetimepicker("destroy");
|
||||
Grocy.Components.DateTimePicker2.Init();
|
||||
|
||||
Grocy.Components.DateTimePicker2.GetInputElement().val("");
|
||||
|
||||
// "Click" the shortcut checkbox when the desired value is
|
||||
// not the shortcut value and it is currently set
|
||||
var value = "";
|
||||
var shortcutValue = $("#datetimepicker2-shortcut").data("datetimepicker2-shortcut-value");
|
||||
if (value != shortcutValue && $("#datetimepicker2-shortcut").is(":checked"))
|
||||
{
|
||||
$("#datetimepicker2-shortcut").click();
|
||||
}
|
||||
|
||||
$('#datetimepicker2-timeago').text('');
|
||||
}
|
||||
|
||||
Grocy.Components.DateTimePicker2.ChangeFormat = function(format)
|
||||
{
|
||||
$(".datetimepicker2").datetimepicker("destroy");
|
||||
Grocy.Components.DateTimePicker2.GetInputElement().data("format", format);
|
||||
Grocy.Components.DateTimePicker2.Init();
|
||||
|
||||
if (format == "YYYY-MM-DD")
|
||||
{
|
||||
Grocy.Components.DateTimePicker2.GetInputElement().addClass("date-only-datetimepicker");
|
||||
}
|
||||
else
|
||||
{
|
||||
Grocy.Components.DateTimePicker2.GetInputElement().removeClass("date-only-datetimepicker");
|
||||
}
|
||||
}
|
||||
|
||||
var startDate = null;
|
||||
if (Grocy.Components.DateTimePicker2.GetInputElement().data('init-with-now') === true)
|
||||
{
|
||||
startDate = moment().format(Grocy.Components.DateTimePicker2.GetInputElement().data('format'));
|
||||
}
|
||||
if (Grocy.Components.DateTimePicker2.GetInputElement().data('init-value').length > 0)
|
||||
{
|
||||
startDate = moment(Grocy.Components.DateTimePicker2.GetInputElement().data('init-value')).format(Grocy.Components.DateTimePicker2.GetInputElement().data('format'));
|
||||
}
|
||||
|
||||
var limitDate = moment('2999-12-31 23:59:59');
|
||||
if (Grocy.Components.DateTimePicker2.GetInputElement().data('limit-end-to-now') === true)
|
||||
{
|
||||
limitDate = moment();
|
||||
}
|
||||
|
||||
Grocy.Components.DateTimePicker2.Init = function()
|
||||
{
|
||||
$('.datetimepicker2').datetimepicker(
|
||||
{
|
||||
format: Grocy.Components.DateTimePicker2.GetInputElement().data('format'),
|
||||
buttons: {
|
||||
showToday: true,
|
||||
showClose: true
|
||||
},
|
||||
calendarWeeks: Grocy.CalendarShowWeekNumbers,
|
||||
maxDate: limitDate,
|
||||
locale: moment.locale(),
|
||||
defaultDate: startDate,
|
||||
useCurrent: false,
|
||||
icons: {
|
||||
time: 'far fa-clock',
|
||||
date: 'far fa-calendar',
|
||||
up: 'fas fa-arrow-up',
|
||||
down: 'fas fa-arrow-down',
|
||||
previous: 'fas fa-chevron-left',
|
||||
next: 'fas fa-chevron-right',
|
||||
today: 'fas fa-calendar-check',
|
||||
clear: 'far fa-trash-alt',
|
||||
close: 'far fa-times-circle'
|
||||
},
|
||||
sideBySide: true,
|
||||
keyBinds: {
|
||||
up: function(widget) { },
|
||||
down: function(widget) { },
|
||||
'control up': function(widget) { },
|
||||
'control down': function(widget) { },
|
||||
left: function(widget) { },
|
||||
right: function(widget) { },
|
||||
pageUp: function(widget) { },
|
||||
pageDown: function(widget) { },
|
||||
enter: function(widget) { },
|
||||
escape: function(widget) { },
|
||||
'control space': function(widget) { },
|
||||
t: function(widget) { },
|
||||
'delete': function(widget) { }
|
||||
}
|
||||
});
|
||||
}
|
||||
Grocy.Components.DateTimePicker2.Init();
|
||||
|
||||
Grocy.Components.DateTimePicker2.GetInputElement().on('keyup', function(e)
|
||||
{
|
||||
$('.datetimepicker2').datetimepicker('hide');
|
||||
|
||||
var value = Grocy.Components.DateTimePicker2.GetValue();
|
||||
var now = new Date();
|
||||
var centuryStart = Number.parseInt(now.getFullYear().toString().substring(0, 2) + '00');
|
||||
var centuryEnd = Number.parseInt(now.getFullYear().toString().substring(0, 2) + '99');
|
||||
var format = Grocy.Components.DateTimePicker2.GetInputElement().data('format');
|
||||
var nextInputElement = $(Grocy.Components.DateTimePicker2.GetInputElement().data('next-input-selector'));
|
||||
|
||||
//If input is empty and any arrow key is pressed, set date to today
|
||||
if (value.length === 0 && (e.keyCode === 38 || e.keyCode === 40 || e.keyCode === 37 || e.keyCode === 39))
|
||||
{
|
||||
Grocy.Components.DateTimePicker2.SetValue(moment(new Date(), format, true).format(format));
|
||||
nextInputElement.focus();
|
||||
}
|
||||
else if (value === 'x' || value === 'X')
|
||||
{
|
||||
Grocy.Components.DateTimePicker2.SetValue(moment('2999-12-31 23:59:59').format(format));
|
||||
nextInputElement.focus();
|
||||
}
|
||||
else if (value.length === 4 && !(Number.parseInt(value) > centuryStart && Number.parseInt(value) < centuryEnd))
|
||||
{
|
||||
var date = moment((new Date()).getFullYear().toString() + value);
|
||||
if (date.isBefore(moment()))
|
||||
{
|
||||
date.add(1, "year");
|
||||
}
|
||||
Grocy.Components.DateTimePicker2.SetValue(date.format(format));
|
||||
nextInputElement.focus();
|
||||
}
|
||||
else if (value.length === 8 && $.isNumeric(value))
|
||||
{
|
||||
Grocy.Components.DateTimePicker2.SetValue(value.replace(/(\d{4})(\d{2})(\d{2})/, '$1-$2-$3'));
|
||||
nextInputElement.focus();
|
||||
}
|
||||
else if (value.length === 7 && $.isNumeric(value.substring(0, 6)) && (value.substring(6, 7).toLowerCase() === "e" || value.substring(6, 7).toLowerCase() === "+"))
|
||||
{
|
||||
var endOfMonth = moment(value.substring(0, 4) + "-" + value.substring(4, 6) + "-01").endOf("month");
|
||||
Grocy.Components.DateTimePicker2.SetValue(endOfMonth.format(format));
|
||||
nextInputElement.focus();
|
||||
}
|
||||
else
|
||||
{
|
||||
var dateObj = moment(value, format, true);
|
||||
if (dateObj.isValid())
|
||||
{
|
||||
if (e.shiftKey)
|
||||
{
|
||||
// WITH shift modifier key
|
||||
|
||||
if (e.keyCode === 38) // Up
|
||||
{
|
||||
Grocy.Components.DateTimePicker2.SetValue(dateObj.add(-1, 'months').format(format));
|
||||
}
|
||||
else if (e.keyCode === 40) // Down
|
||||
{
|
||||
Grocy.Components.DateTimePicker2.SetValue(dateObj.add(1, 'months').format(format));
|
||||
}
|
||||
else if (e.keyCode === 37) // Left
|
||||
{
|
||||
Grocy.Components.DateTimePicker2.SetValue(dateObj.add(-1, 'years').format(format));
|
||||
}
|
||||
else if (e.keyCode === 39) // Right
|
||||
{
|
||||
Grocy.Components.DateTimePicker2.SetValue(dateObj.add(1, 'years').format(format));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// WITHOUT shift modifier key
|
||||
|
||||
if (e.keyCode === 38) // Up
|
||||
{
|
||||
Grocy.Components.DateTimePicker2.SetValue(dateObj.add(-1, 'days').format(format));
|
||||
}
|
||||
else if (e.keyCode === 40) // Down
|
||||
{
|
||||
Grocy.Components.DateTimePicker2.SetValue(dateObj.add(1, 'days').format(format));
|
||||
}
|
||||
else if (e.keyCode === 37) // Left
|
||||
{
|
||||
Grocy.Components.DateTimePicker2.SetValue(dateObj.add(-1, 'weeks').format(format));
|
||||
}
|
||||
else if (e.keyCode === 39) // Right
|
||||
{
|
||||
Grocy.Components.DateTimePicker2.SetValue(dateObj.add(1, 'weeks').format(format));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Custom validation
|
||||
value = Grocy.Components.DateTimePicker2.GetValue();
|
||||
dateObj = moment(value, format, true);
|
||||
var element = Grocy.Components.DateTimePicker2.GetInputElement()[0];
|
||||
if (!dateObj.isValid())
|
||||
{
|
||||
if ($(element).hasAttr("required"))
|
||||
{
|
||||
element.setCustomValidity("error");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Grocy.Components.DateTimePicker2.GetInputElement().data('limit-end-to-now') === true && dateObj.isAfter(moment()))
|
||||
{
|
||||
element.setCustomValidity("error");
|
||||
}
|
||||
else if (Grocy.Components.DateTimePicker2.GetInputElement().data('limit-start-to-now') === true && dateObj.isBefore(moment()))
|
||||
{
|
||||
element.setCustomValidity("error");
|
||||
}
|
||||
else
|
||||
{
|
||||
element.setCustomValidity("");
|
||||
}
|
||||
|
||||
var earlierThanLimit = Grocy.Components.DateTimePicker2.GetInputElement().data("earlier-than-limit");
|
||||
if (!earlierThanLimit.isEmpty())
|
||||
{
|
||||
if (moment(value).isBefore(moment(earlierThanLimit)))
|
||||
{
|
||||
$("#datetimepicker-earlier-than-info").removeClass("d-none");
|
||||
}
|
||||
else
|
||||
{
|
||||
$("#datetimepicker-earlier-than-info").addClass("d-none");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// "Click" the shortcut checkbox when the shortcut value was
|
||||
// entered manually and it is currently not set
|
||||
var shortcutValue = $("#datetimepicker2-shortcut").data("datetimepicker2-shortcut-value");
|
||||
if (value == shortcutValue && !$("#datetimepicker2-shortcut").is(":checked"))
|
||||
{
|
||||
$("#datetimepicker2-shortcut").click();
|
||||
}
|
||||
});
|
||||
|
||||
Grocy.Components.DateTimePicker2.GetInputElement().on('input', function(e)
|
||||
{
|
||||
$('#datetimepicker2-timeago').attr("datetime", Grocy.Components.DateTimePicker2.GetValue());
|
||||
EmptyElementWhenMatches('#datetimepicker2-timeago', Grocy.translate('timeago_nan'));
|
||||
RefreshContextualTimeago("#datetimepicker2-wrapper");
|
||||
});
|
||||
|
||||
$('.datetimepicker2').on('update.datetimepicker', function(e)
|
||||
{
|
||||
Grocy.Components.DateTimePicker2.GetInputElement().trigger('input');
|
||||
Grocy.Components.DateTimePicker2.GetInputElement().trigger('change');
|
||||
Grocy.Components.DateTimePicker2.GetInputElement().trigger('keypress');
|
||||
Grocy.Components.DateTimePicker2.GetInputElement().trigger('keyup');
|
||||
});
|
||||
|
||||
$('.datetimepicker2').on('hide.datetimepicker', function(e)
|
||||
{
|
||||
Grocy.Components.DateTimePicker2.GetInputElement().trigger('input');
|
||||
Grocy.Components.DateTimePicker2.GetInputElement().trigger('change');
|
||||
Grocy.Components.DateTimePicker2.GetInputElement().trigger('keypress');
|
||||
Grocy.Components.DateTimePicker2.GetInputElement().trigger('keyup');
|
||||
});
|
||||
|
||||
$("#datetimepicker2-shortcut").on("click", function()
|
||||
{
|
||||
if (this.checked)
|
||||
{
|
||||
var value = $("#datetimepicker2-shortcut").data("datetimepicker2-shortcut-value");
|
||||
Grocy.Components.DateTimePicker2.SetValue(value);
|
||||
Grocy.Components.DateTimePicker2.GetInputElement().attr("readonly", "");
|
||||
$(Grocy.Components.DateTimePicker2.GetInputElement().data('next-input-selector')).focus();
|
||||
}
|
||||
else
|
||||
{
|
||||
Grocy.Components.DateTimePicker2.SetValue("");
|
||||
Grocy.Components.DateTimePicker2.GetInputElement().removeAttr("readonly");
|
||||
Grocy.Components.DateTimePicker2.GetInputElement().focus();
|
||||
}
|
||||
|
||||
Grocy.Components.DateTimePicker2.GetInputElement().trigger('input');
|
||||
Grocy.Components.DateTimePicker2.GetInputElement().trigger('change');
|
||||
Grocy.Components.DateTimePicker2.GetInputElement().trigger('keypress');
|
||||
});
|
||||
}
|
||||
|
||||
export { datetimepicker2 }
|
||||
15
js/components/index.js
Normal file
15
js/components/index.js
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
export * from './barcodescanner';
|
||||
export * from './batterycard';
|
||||
export * from './calendarcard';
|
||||
export * from './chorecard';
|
||||
export * from './datetimepicker';
|
||||
export * from './datetimepicker2';
|
||||
export * from './locationpicker';
|
||||
export * from './numberpicker';
|
||||
export * from './productamountpicker';
|
||||
export * from './productcard';
|
||||
export * from './productpicker';
|
||||
export * from './recipepicker';
|
||||
export * from './shoppinglocationpicker';
|
||||
export * from './userfieldsform';
|
||||
export * from './userpicker';
|
||||
80
js/components/locationpicker.js
Normal file
80
js/components/locationpicker.js
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
function locationpicker(Grocy)
|
||||
{
|
||||
|
||||
Grocy.Components.LocationPicker = {};
|
||||
|
||||
Grocy.Components.LocationPicker.GetPicker = function()
|
||||
{
|
||||
return $('#location_id');
|
||||
}
|
||||
|
||||
Grocy.Components.LocationPicker.GetInputElement = function()
|
||||
{
|
||||
return $('#location_id_text_input');
|
||||
}
|
||||
|
||||
Grocy.Components.LocationPicker.GetValue = function()
|
||||
{
|
||||
return $('#location_id').val();
|
||||
}
|
||||
|
||||
Grocy.Components.LocationPicker.SetValue = function(value)
|
||||
{
|
||||
Grocy.Components.LocationPicker.GetInputElement().val(value);
|
||||
Grocy.Components.LocationPicker.GetInputElement().trigger('change');
|
||||
}
|
||||
|
||||
Grocy.Components.LocationPicker.SetId = function(value)
|
||||
{
|
||||
Grocy.Components.LocationPicker.GetPicker().val(value);
|
||||
Grocy.Components.LocationPicker.GetPicker().data('combobox').refresh();
|
||||
Grocy.Components.LocationPicker.GetInputElement().trigger('change');
|
||||
}
|
||||
|
||||
Grocy.Components.LocationPicker.Clear = function()
|
||||
{
|
||||
Grocy.Components.LocationPicker.SetValue('');
|
||||
Grocy.Components.LocationPicker.SetId(null);
|
||||
}
|
||||
|
||||
$('.location-combobox').combobox({
|
||||
appendId: '_text_input',
|
||||
bsVersion: '4',
|
||||
clearIfNoMatch: true
|
||||
});
|
||||
|
||||
// these names seem a bit long, but as they live in global space
|
||||
// and this is a component, they need to be unique.
|
||||
var locationpicker_doFocus = false;
|
||||
var this_location_picker = Grocy.Components.LocationPicker.GetPicker();
|
||||
|
||||
var prefillByName = this_location_picker.parent().data('prefill-by-name').toString();
|
||||
if (typeof prefillByName !== "undefined")
|
||||
{
|
||||
var possibleOptionElement = $("#location_id option:contains(\"" + prefillByName + "\")").first();
|
||||
|
||||
if (possibleOptionElement.length > 0)
|
||||
{
|
||||
locationpicker_doFocus = true;
|
||||
this_location_picker.val(possibleOptionElement.val());
|
||||
}
|
||||
}
|
||||
|
||||
var prefillById = this_location_picker.parent().data('prefill-by-id').toString();
|
||||
if (typeof prefillById !== "undefined")
|
||||
{
|
||||
locationpicker_doFocus = true;
|
||||
this_location_picker.val(prefillById);
|
||||
}
|
||||
|
||||
if (locationpicker_doFocus)
|
||||
{
|
||||
this_location_picker.data('combobox').refresh();
|
||||
this_location_picker.trigger('change');
|
||||
|
||||
$(this_location_picker.parent().data('next-input-selector').toString())
|
||||
.focus();
|
||||
}
|
||||
}
|
||||
|
||||
export { locationpicker }
|
||||
99
js/components/numberpicker.js
Normal file
99
js/components/numberpicker.js
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
function numberpicker(Grocy)
|
||||
{
|
||||
$(".numberpicker-down-button").unbind('click').on("click", function()
|
||||
{
|
||||
var inputElement = $(this).parent().parent().find('input[type="number"]');
|
||||
inputElement.val(parseFloat(inputElement.val() || 1) - 1);
|
||||
inputElement.trigger('keyup');
|
||||
inputElement.trigger('change');
|
||||
});
|
||||
|
||||
$(".numberpicker-up-button").unbind('click').on("click", function()
|
||||
{
|
||||
var inputElement = $(this).parent().parent().find('input[type="number"]');
|
||||
inputElement.val(parseFloat(inputElement.val() || 0) + 1);
|
||||
inputElement.trigger('keyup');
|
||||
inputElement.trigger('change');
|
||||
});
|
||||
|
||||
$(".numberpicker").on("keyup", function()
|
||||
{
|
||||
if ($(this).attr("data-not-equal") && !$(this).attr("data-not-equal").toString().isEmpty() && $(this).attr("data-not-equal") == $(this).val())
|
||||
{
|
||||
$(this)[0].setCustomValidity("error");
|
||||
}
|
||||
else
|
||||
{
|
||||
$(this)[0].setCustomValidity("");
|
||||
}
|
||||
});
|
||||
|
||||
$(".numberpicker").each(function()
|
||||
{
|
||||
new MutationObserver(function(mutations)
|
||||
{
|
||||
mutations.forEach(function(mutation)
|
||||
{
|
||||
if (mutation.type == "attributes" && (mutation.attributeName == "min" || mutation.attributeName == "max" || mutation.attributeName == "data-not-equal" || mutation.attributeName == "data-initialised"))
|
||||
{
|
||||
var element = $(mutation.target);
|
||||
var min = element.attr("min");
|
||||
var decimals = element.attr("data-decimals");
|
||||
|
||||
var max = "";
|
||||
if (element.hasAttr("max"))
|
||||
{
|
||||
max = element.attr("max");
|
||||
}
|
||||
|
||||
if (element.hasAttr("data-not-equal"))
|
||||
{
|
||||
var notEqual = element.attr("data-not-equal");
|
||||
|
||||
if (notEqual != "NaN")
|
||||
{
|
||||
if (max.isEmpty())
|
||||
{
|
||||
element.parent().find(".invalid-feedback").text(Grocy.translate("This cannot be lower than %1$s or equal %2$s and needs to be a valid number with max. %3$s decimal places", parseFloat(min).toLocaleString(undefined, { minimumFractionDigits: 0, maximumFractionDigits: decimals }), parseFloat(notEqual).toLocaleString(undefined, { minimumFractionDigits: 0, maximumFractionDigits: decimals }), decimals));
|
||||
}
|
||||
else
|
||||
{
|
||||
element.parent().find(".invalid-feedback").text(Grocy.translate("This must be between %1$s and %2$s, cannot equal %3$s and needs to be a valid number with max. %4$s decimal places", parseFloat(min).toLocaleString(undefined, { minimumFractionDigits: 0, maximumFractionDigits: decimals }), parseFloat(max).toLocaleString(undefined, { minimumFractionDigits: 0, maximumFractionDigits: decimals }), parseFloat(notEqual).toLocaleString(undefined, { minimumFractionDigits: decimals, maximumFractionDigits: decimals }), decimals));
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (max.isEmpty())
|
||||
{
|
||||
element.parent().find(".invalid-feedback").text(Grocy.translate("This cannot be lower than %1$s and needs to be a valid number with max. %2$s decimal places", parseFloat(min).toLocaleString(undefined, { minimumFractionDigits: 0, maximumFractionDigits: decimals }), decimals));
|
||||
}
|
||||
else
|
||||
{
|
||||
element.parent().find(".invalid-feedback").text(Grocy.translate("This must between %1$s and %2$s and needs to be a valid number with max. %3$s decimal places", parseFloat(min).toLocaleString(undefined, { minimumFractionDigits: 0, maximumFractionDigits: decimals }), parseFloat(max).toLocaleString(undefined, { minimumFractionDigits: 0, maximumFractionDigits: decimals }), decimals));
|
||||
}
|
||||
}
|
||||
});
|
||||
}).observe(this, {
|
||||
attributes: true
|
||||
});
|
||||
});
|
||||
$(".numberpicker").attr("data-initialised", "true"); // Dummy change to trigger MutationObserver above once
|
||||
|
||||
$(".numberpicker").on("keydown", function(e)
|
||||
{
|
||||
if (e.key == "ArrowUp")
|
||||
{
|
||||
e.preventDefault();
|
||||
$(this).parent().find(".numberpicker-up-button").click();
|
||||
}
|
||||
else if (e.key == "ArrowDown")
|
||||
{
|
||||
e.preventDefault();
|
||||
$(this).parent().find(".numberpicker-down-button").click();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export { numberpicker }
|
||||
123
js/components/productamountpicker.js
Normal file
123
js/components/productamountpicker.js
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
function productamountpicker(Grocy)
|
||||
{
|
||||
Grocy.Use("numberpicker");
|
||||
|
||||
Grocy.Components.ProductAmountPicker = {};
|
||||
Grocy.Components.ProductAmountPicker.AllowAnyQuEnabled = false;
|
||||
|
||||
Grocy.Components.ProductAmountPicker.Reload = function(productId, destinationQuId, forceInitialDisplayQu = false)
|
||||
{
|
||||
var conversionsForProduct = Grocy.QuantityUnitConversionsResolved.filter(elem => elem.product_id == productId);
|
||||
|
||||
if (!Grocy.Components.ProductAmountPicker.AllowAnyQuEnabled)
|
||||
{
|
||||
var qu = Grocy.QuantityUnits.find(elem => elem.id == destinationQuId);
|
||||
$("#qu_id").find("option").remove().end();
|
||||
$("#qu_id").attr("data-destination-qu-name", qu.name);
|
||||
$("#qu_id").attr("data-destination-qu-name-plural", qu.name_plural);
|
||||
|
||||
conversionsForProduct.forEach(conversion =>
|
||||
{
|
||||
var factor = parseFloat(conversion.factor);
|
||||
if (conversion.to_qu_id == destinationQuId)
|
||||
{
|
||||
factor = 1;
|
||||
}
|
||||
|
||||
if (!$('#qu_id option[value="' + conversion.to_qu_id + '"]').length) // Don't add the destination QU multiple times
|
||||
{
|
||||
$("#qu_id").append('<option value="' + conversion.to_qu_id + '" data-qu-factor="' + factor + '">' + conversion.to_qu_name + '</option>');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (!Grocy.Components.ProductAmountPicker.InitialValueSet || forceInitialDisplayQu)
|
||||
{
|
||||
$("#qu_id").val($("#qu_id").attr("data-initial-qu-id"));
|
||||
}
|
||||
|
||||
if (!Grocy.Components.ProductAmountPicker.InitialValueSet)
|
||||
{
|
||||
var convertedAmount = $("#display_amount").val() * $("#qu_id option:selected").attr("data-qu-factor");
|
||||
$("#display_amount").val(convertedAmount);
|
||||
|
||||
Grocy.Components.ProductAmountPicker.InitialValueSet = true;
|
||||
}
|
||||
|
||||
if (conversionsForProduct.length === 1 && !forceInitialDisplayQu)
|
||||
{
|
||||
$("#qu_id").val($("#qu_id option:first").val());
|
||||
}
|
||||
|
||||
if ($('#qu_id option').length == 1)
|
||||
{
|
||||
$("#qu_id").attr("disabled", "");
|
||||
}
|
||||
else
|
||||
{
|
||||
$("#qu_id").removeAttr("disabled");
|
||||
}
|
||||
|
||||
$(".input-group-productamountpicker").trigger("change");
|
||||
}
|
||||
|
||||
Grocy.Components.ProductAmountPicker.SetQuantityUnit = function(quId)
|
||||
{
|
||||
$("#qu_id").val(quId);
|
||||
}
|
||||
|
||||
Grocy.Components.ProductAmountPicker.AllowAnyQu = function(keepInitialQu = false)
|
||||
{
|
||||
Grocy.Components.ProductAmountPicker.AllowAnyQuEnabled = true;
|
||||
|
||||
$("#qu_id").find("option").remove().end();
|
||||
Grocy.QuantityUnits.forEach(qu =>
|
||||
{
|
||||
$("#qu_id").append('<option value="' + qu.id + '" data-qu-factor="1">' + qu.name + '</option>');
|
||||
});
|
||||
|
||||
if (keepInitialQu)
|
||||
{
|
||||
Grocy.Components.ProductAmountPicker.SetQuantityUnit($("#qu_id").attr("data-initial-qu-id"));
|
||||
}
|
||||
|
||||
$("#qu_id").removeAttr("disabled");
|
||||
|
||||
$(".input-group-productamountpicker").trigger("change");
|
||||
}
|
||||
|
||||
Grocy.Components.ProductAmountPicker.Reset = function()
|
||||
{
|
||||
$("#qu_id").find("option").remove();
|
||||
$("#qu-conversion-info").addClass("d-none");
|
||||
$("#qu-display_amount-info").val("");
|
||||
}
|
||||
|
||||
$(".input-group-productamountpicker").on("change", function()
|
||||
{
|
||||
var selectedQuName = $("#qu_id option:selected").text();
|
||||
var quFactor = $("#qu_id option:selected").attr("data-qu-factor");
|
||||
var amount = $("#display_amount").val();
|
||||
var destinationAmount = amount / quFactor;
|
||||
var destinationQuName = Grocy.translaten(destinationAmount, $("#qu_id").attr("data-destination-qu-name"), $("#qu_id").attr("data-destination-qu-name-plural"))
|
||||
|
||||
if ($("#qu_id").attr("data-destination-qu-name") == selectedQuName || Grocy.Components.ProductAmountPicker.AllowAnyQuEnabled || amount.toString().isEmpty() || selectedQuName.toString().isEmpty())
|
||||
{
|
||||
$("#qu-conversion-info").addClass("d-none");
|
||||
}
|
||||
else
|
||||
{
|
||||
$("#qu-conversion-info").removeClass("d-none");
|
||||
$("#qu-conversion-info").text(Grocy.translate("This equals %1$s %2$s", destinationAmount.toLocaleString({ minimumFractionDigits: 0, maximumFractionDigits: Grocy.UserSettings.stock_decimal_places_amounts }), destinationQuName));
|
||||
}
|
||||
|
||||
$("#amount").val(destinationAmount.toFixed(Grocy.UserSettings.stock_decimal_places_amounts).replace(/0*$/g, ''));
|
||||
});
|
||||
|
||||
$("#display_amount").on("keyup", function()
|
||||
{
|
||||
$(".input-group-productamountpicker").trigger("change");
|
||||
});
|
||||
}
|
||||
|
||||
export { productamountpicker }
|
||||
245
js/components/productcard.js
Normal file
245
js/components/productcard.js
Normal file
|
|
@ -0,0 +1,245 @@
|
|||
import Chart from 'chart.js';
|
||||
import { EmptyElementWhenMatches } from '../helpers/extensions'
|
||||
import { RefreshContextualTimeago } from '../configs/timeago'
|
||||
|
||||
function productcard(Grocy)
|
||||
{
|
||||
|
||||
Grocy.Components.ProductCard = {};
|
||||
|
||||
Grocy.Components.ProductCard.Refresh = function(productId)
|
||||
{
|
||||
Grocy.Api.Get('stock/products/' + productId,
|
||||
function(productDetails)
|
||||
{
|
||||
var stockAmount = productDetails.stock_amount || '0';
|
||||
var stockValue = productDetails.stock_value || '0';
|
||||
var stockAmountOpened = productDetails.stock_amount_opened || '0';
|
||||
$('#productcard-product-name').text(productDetails.product.name);
|
||||
$('#productcard-product-description').html(productDetails.product.description);
|
||||
$('#productcard-product-stock-amount').text(stockAmount);
|
||||
$('#productcard-product-stock-qu-name').text(Grocy.translaten(stockAmount, productDetails.quantity_unit_stock.name, productDetails.quantity_unit_stock.name_plural));
|
||||
$('#productcard-product-stock-value').text(stockValue + ' ' + Grocy.Currency);
|
||||
$('#productcard-product-last-purchased').text((productDetails.last_purchased || '2999-12-31').substring(0, 10));
|
||||
$('#productcard-product-last-purchased-timeago').attr("datetime", productDetails.last_purchased || '2999-12-31');
|
||||
$('#productcard-product-last-used').text((productDetails.last_used || '2999-12-31').substring(0, 10));
|
||||
$('#productcard-product-last-used-timeago').attr("datetime", productDetails.last_used || '2999-12-31');
|
||||
if (productDetails.location != null)
|
||||
{
|
||||
$('#productcard-product-location').text(productDetails.location.name);
|
||||
}
|
||||
$('#productcard-product-spoil-rate').text((parseFloat(productDetails.spoil_rate_percent) / 100).toLocaleString(undefined, { style: "percent" }));
|
||||
|
||||
if (productDetails.is_aggregated_amount == 1)
|
||||
{
|
||||
$('#productcard-product-stock-amount-aggregated').text(productDetails.stock_amount_aggregated);
|
||||
$('#productcard-product-stock-qu-name-aggregated').text(Grocy.translaten(productDetails.stock_amount_aggregated, productDetails.quantity_unit_stock.name, productDetails.quantity_unit_stock.name_plural));
|
||||
|
||||
if (productDetails.stock_amount_opened_aggregated > 0)
|
||||
{
|
||||
$('#productcard-product-stock-opened-amount-aggregated').text(Grocy.translate('%s opened', productDetails.stock_amount_opened_aggregated));
|
||||
}
|
||||
else
|
||||
{
|
||||
$('#productcard-product-stock-opened-amount-aggregated').text("");
|
||||
}
|
||||
|
||||
$("#productcard-aggregated-amounts").removeClass("d-none");
|
||||
}
|
||||
else
|
||||
{
|
||||
$("#productcard-aggregated-amounts").addClass("d-none");
|
||||
}
|
||||
|
||||
if (productDetails.product.description != null && !productDetails.product.description.isEmpty())
|
||||
{
|
||||
$("#productcard-product-description-wrapper").removeClass("d-none");
|
||||
}
|
||||
else
|
||||
{
|
||||
$("#productcard-product-description-wrapper").addClass("d-none");
|
||||
}
|
||||
|
||||
if (productDetails.average_shelf_life_days == -1)
|
||||
{
|
||||
$('#productcard-product-average-shelf-life').text(Grocy.translate("Unknown"));
|
||||
}
|
||||
else if (parseInt(productDetails.average_shelf_life_days) > 73000) // > 200 years aka forever
|
||||
{
|
||||
$('#productcard-product-average-shelf-life').text(Grocy.translate("Unlimited"));
|
||||
}
|
||||
else
|
||||
{
|
||||
$('#productcard-product-average-shelf-life').text(moment.duration(productDetails.average_shelf_life_days, "days").humanize());
|
||||
}
|
||||
|
||||
if (stockAmountOpened > 0)
|
||||
{
|
||||
$('#productcard-product-stock-opened-amount').text(Grocy.translate('%s opened', stockAmountOpened));
|
||||
}
|
||||
else
|
||||
{
|
||||
$('#productcard-product-stock-opened-amount').text("");
|
||||
}
|
||||
|
||||
$('#productcard-product-edit-button').attr("href", Grocy.FormatUrl("/product/" + productDetails.product.id.toString() + '?' + 'returnto=' + encodeURIComponent(Grocy.CurrentUrlRelative)));
|
||||
$('#productcard-product-journal-button').attr("href", Grocy.FormatUrl("/stockjournal?embedded&product=" + productDetails.product.id.toString()));
|
||||
$('#productcard-product-stock-button').attr("href", Grocy.FormatUrl("/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");
|
||||
|
||||
if (productDetails.last_price !== null)
|
||||
{
|
||||
$('#productcard-product-last-price').text(Number.parseFloat(productDetails.last_price).toLocaleString() + ' ' + Grocy.Currency + ' per ' + productDetails.quantity_unit_stock.name);
|
||||
}
|
||||
else
|
||||
{
|
||||
$('#productcard-product-last-price').text(Grocy.translate('Unknown'));
|
||||
}
|
||||
|
||||
if (productDetails.avg_price !== null)
|
||||
{
|
||||
$('#productcard-product-average-price').text(Number.parseFloat(productDetails.avg_price).toLocaleString() + ' ' + Grocy.Currency + ' per ' + productDetails.quantity_unit_stock.name);
|
||||
}
|
||||
else
|
||||
{
|
||||
$('#productcard-product-average-price').text(Grocy.translate('Unknown'));
|
||||
}
|
||||
|
||||
if (productDetails.product.picture_file_name !== null && !productDetails.product.picture_file_name.isEmpty())
|
||||
{
|
||||
$("#productcard-product-picture").removeClass("d-none");
|
||||
$("#productcard-product-picture").attr("src", Grocy.FormatUrl('/api/files/productpictures/' + btoa(productDetails.product.picture_file_name) + '?force_serve_as=picture&best_fit_width=400'));
|
||||
}
|
||||
else
|
||||
{
|
||||
$("#productcard-product-picture").addClass("d-none");
|
||||
}
|
||||
|
||||
EmptyElementWhenMatches('#productcard-product-last-purchased-timeago', Grocy.translate('timeago_nan'));
|
||||
EmptyElementWhenMatches('#productcard-product-last-used-timeago', Grocy.translate('timeago_nan'));
|
||||
RefreshContextualTimeago(".productcard");
|
||||
},
|
||||
function(xhr)
|
||||
{
|
||||
console.error(xhr);
|
||||
}
|
||||
);
|
||||
|
||||
if (Grocy.FeatureFlags.GROCY_FEATURE_FLAG_STOCK_PRICE_TRACKING)
|
||||
{
|
||||
Grocy.Api.Get('stock/products/' + productId + '/price-history',
|
||||
function(priceHistoryDataPoints)
|
||||
{
|
||||
if (priceHistoryDataPoints.length > 0)
|
||||
{
|
||||
$("#productcard-product-price-history-chart").removeClass("d-none");
|
||||
$("#productcard-no-price-data-hint").addClass("d-none");
|
||||
|
||||
Grocy.Components.ProductCard.ReInitPriceHistoryChart();
|
||||
var datasets = {};
|
||||
var chart = Grocy.Components.ProductCard.PriceHistoryChart.data;
|
||||
priceHistoryDataPoints.forEach((dataPoint) =>
|
||||
{
|
||||
var key = Grocy.translate("Unknown store");
|
||||
if (dataPoint.shopping_location)
|
||||
{
|
||||
key = dataPoint.shopping_location.name
|
||||
}
|
||||
|
||||
if (!datasets[key])
|
||||
{
|
||||
datasets[key] = []
|
||||
}
|
||||
chart.labels.push(moment(dataPoint.date).toDate());
|
||||
datasets[key].push(dataPoint.price);
|
||||
|
||||
});
|
||||
Object.keys(datasets).forEach((key) =>
|
||||
{
|
||||
chart.datasets.push({
|
||||
data: datasets[key],
|
||||
fill: false,
|
||||
borderColor: "HSL(" + (129 * chart.datasets.length) + ",100%,50%)",
|
||||
label: key,
|
||||
});
|
||||
});
|
||||
Grocy.Components.ProductCard.PriceHistoryChart.update();
|
||||
}
|
||||
else
|
||||
{
|
||||
$("#productcard-product-price-history-chart").addClass("d-none");
|
||||
$("#productcard-no-price-data-hint").removeClass("d-none");
|
||||
}
|
||||
},
|
||||
function(xhr)
|
||||
{
|
||||
console.error(xhr);
|
||||
}
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
Grocy.Components.ProductCard.ReInitPriceHistoryChart = function()
|
||||
{
|
||||
if (typeof Grocy.Components.ProductCard.PriceHistoryChart !== "undefined")
|
||||
{
|
||||
Grocy.Components.ProductCard.PriceHistoryChart.destroy();
|
||||
}
|
||||
|
||||
var format = 'YYYY-MM-DD';
|
||||
Grocy.Components.ProductCard.PriceHistoryChart = new Chart(document.getElementById("productcard-product-price-history-chart"), {
|
||||
type: "line",
|
||||
data: {
|
||||
labels: [ //Date objects
|
||||
// Will be populated in Grocy.Components.ProductCard.Refresh
|
||||
],
|
||||
datasets: [ //Datasets
|
||||
// Will be populated in Grocy.Components.ProductCard.Refresh
|
||||
]
|
||||
},
|
||||
options: {
|
||||
scales: {
|
||||
xAxes: [{
|
||||
type: 'time',
|
||||
time: {
|
||||
parser: format,
|
||||
round: 'day',
|
||||
tooltipFormat: format,
|
||||
unit: 'day',
|
||||
unitStepSize: 10,
|
||||
displayFormats: {
|
||||
'day': format
|
||||
}
|
||||
},
|
||||
ticks: {
|
||||
autoSkip: true,
|
||||
maxRotation: 0
|
||||
}
|
||||
}],
|
||||
yAxes: [{
|
||||
ticks: {
|
||||
beginAtZero: true
|
||||
}
|
||||
}]
|
||||
},
|
||||
legend: {
|
||||
display: true
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
$("#productcard-product-description").on("shown.bs.collapse", function()
|
||||
{
|
||||
$(".expandable-text").find("a[data-toggle='collapse']").text(Grocy.translate("Show less"));
|
||||
})
|
||||
|
||||
$("#productcard-product-description").on("hidden.bs.collapse", function()
|
||||
{
|
||||
$(".expandable-text").find("a[data-toggle='collapse']").text(Grocy.translate("Show more"));
|
||||
})
|
||||
}
|
||||
|
||||
export { productcard }
|
||||
339
js/components/productpicker.js
Normal file
339
js/components/productpicker.js
Normal file
|
|
@ -0,0 +1,339 @@
|
|||
import { GetUriParam, RemoveUriParam } from '../helpers/extensions';
|
||||
|
||||
function productpicker(Grocy)
|
||||
{
|
||||
Grocy.Use('barcodescanner');
|
||||
Grocy.Components.ProductPicker = {};
|
||||
|
||||
Grocy.Components.ProductPicker.GetPicker = function()
|
||||
{
|
||||
return $('#product_id');
|
||||
}
|
||||
|
||||
Grocy.Components.ProductPicker.GetInputElement = function()
|
||||
{
|
||||
return $('#product_id_text_input');
|
||||
}
|
||||
|
||||
Grocy.Components.ProductPicker.GetValue = function()
|
||||
{
|
||||
return $('#product_id').val();
|
||||
}
|
||||
|
||||
Grocy.Components.ProductPicker.SetValue = function(value)
|
||||
{
|
||||
Grocy.Components.ProductPicker.GetInputElement().val(value);
|
||||
Grocy.Components.ProductPicker.GetInputElement().trigger('change');
|
||||
}
|
||||
|
||||
Grocy.Components.ProductPicker.SetId = function(value)
|
||||
{
|
||||
Grocy.Components.ProductPicker.GetPicker().val(value);
|
||||
Grocy.Components.ProductPicker.GetPicker().data('combobox').refresh();
|
||||
Grocy.Components.ProductPicker.GetInputElement().trigger('change');
|
||||
}
|
||||
|
||||
Grocy.Components.ProductPicker.Clear = function()
|
||||
{
|
||||
Grocy.Components.ProductPicker.SetValue('');
|
||||
Grocy.Components.ProductPicker.SetId(null);
|
||||
}
|
||||
|
||||
Grocy.Components.ProductPicker.InProductAddWorkflow = function()
|
||||
{
|
||||
return GetUriParam('flow') == "InplaceNewProductWithName";
|
||||
}
|
||||
|
||||
Grocy.Components.ProductPicker.InProductModifyWorkflow = function()
|
||||
{
|
||||
return GetUriParam('flow') == "InplaceAddBarcodeToExistingProduct";
|
||||
}
|
||||
|
||||
Grocy.Components.ProductPicker.InAnyFlow = function()
|
||||
{
|
||||
return Grocy.Components.ProductPicker.InProductAddWorkflow() || Grocy.Components.ProductPicker.InProductModifyWorkflow();
|
||||
}
|
||||
|
||||
Grocy.Components.ProductPicker.FinishFlow = function()
|
||||
{
|
||||
RemoveUriParam("flow");
|
||||
RemoveUriParam("barcode");
|
||||
RemoveUriParam("product-name");
|
||||
}
|
||||
|
||||
Grocy.Components.ProductPicker.ShowCustomError = function(text)
|
||||
{
|
||||
var element = $("#custom-productpicker-error");
|
||||
element.text(text);
|
||||
element.removeClass("d-none");
|
||||
}
|
||||
|
||||
Grocy.Components.ProductPicker.HideCustomError = function()
|
||||
{
|
||||
$("#custom-productpicker-error").addClass("d-none");
|
||||
}
|
||||
|
||||
Grocy.Components.ProductPicker.Disable = function()
|
||||
{
|
||||
Grocy.Components.ProductPicker.GetInputElement().attr("disabled", "");
|
||||
$("#barcodescanner-start-button").attr("disabled", "");
|
||||
$("#barcodescanner-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");
|
||||
}
|
||||
|
||||
$('.product-combobox').combobox({
|
||||
appendId: '_text_input',
|
||||
bsVersion: '4',
|
||||
clearIfNoMatch: false
|
||||
});
|
||||
|
||||
var this_product_picker = Grocy.Components.ProductPicker.GetPicker();
|
||||
var productpicker_doFocus = false;
|
||||
|
||||
var prefillProduct = GetUriParam('product-name');
|
||||
var prefillProduct2 = this_product_picker.parent().data('prefill-by-name').toString();
|
||||
if (!prefillProduct2.isEmpty())
|
||||
{
|
||||
prefillProduct = prefillProduct2;
|
||||
}
|
||||
if (typeof prefillProduct !== "undefined")
|
||||
{
|
||||
var possibleOptionElement = $("#product_id option[data-additional-searchdata*=\"" + prefillProduct + "\"]").first();
|
||||
if (possibleOptionElement.length === 0)
|
||||
{
|
||||
possibleOptionElement = $("#product_id option:contains(\"" + prefillProduct + "\")").first();
|
||||
}
|
||||
|
||||
if (possibleOptionElement.length > 0)
|
||||
{
|
||||
productpicker_doFocus = true;
|
||||
this_product_picker.val(possibleOptionElement.val());
|
||||
}
|
||||
}
|
||||
|
||||
var prefillProductId = GetUriParam("product");
|
||||
var prefillProductId2 = this_product_picker.parent().data('prefill-by-id').toString();
|
||||
if (!prefillProductId2.isEmpty())
|
||||
{
|
||||
prefillProductId = prefillProductId2;
|
||||
}
|
||||
if (typeof prefillProductId !== "undefined")
|
||||
{
|
||||
this_product_picker.val(prefillProductId);
|
||||
productpicker_doFocus = true;
|
||||
}
|
||||
|
||||
if (productpicker_doFocus)
|
||||
{
|
||||
this_product_picker.data('combobox').refresh();
|
||||
this_product_picker.trigger('change');
|
||||
|
||||
$(this_product_picker.parent().data('next-input-selector').toString())
|
||||
.focus();
|
||||
}
|
||||
|
||||
if (GetUriParam("flow") === "InplaceAddBarcodeToExistingProduct")
|
||||
{
|
||||
$('#InplaceAddBarcodeToExistingProduct').text(GetUriParam("barcode"));
|
||||
$('#flow-info-InplaceAddBarcodeToExistingProduct').removeClass('d-none');
|
||||
$('#barcode-lookup-disabled-hint').removeClass('d-none');
|
||||
$('#barcode-lookup-hint').addClass('d-none');
|
||||
}
|
||||
|
||||
Grocy.Components.ProductPicker.PopupOpen = false;
|
||||
$('#product_id_text_input').on('blur', function(e)
|
||||
{
|
||||
if (Grocy.Components.ProductPicker.GetPicker().hasClass("combobox-menu-visible"))
|
||||
{
|
||||
return;
|
||||
}
|
||||
$('#product_id').attr("barcode", "null");
|
||||
|
||||
var input = $('#product_id_text_input').val().toString();
|
||||
var possibleOptionElement = [];
|
||||
|
||||
// did we enter a grocycode?
|
||||
if (input.startsWith("grcy"))
|
||||
{
|
||||
var gc = input.split(":");
|
||||
if (gc[1] == "p")
|
||||
{
|
||||
// find product id
|
||||
possibleOptionElement = $("#product_id option[value=\"" + gc[2] + "\"]").first();
|
||||
$("#product_id").data("grocycode", true);
|
||||
}
|
||||
}
|
||||
else // process barcode as usual
|
||||
{
|
||||
possibleOptionElement = $("#product_id option[data-additional-searchdata*=\"" + input + ",\"]").first();
|
||||
}
|
||||
|
||||
if (GetUriParam('flow') === undefined && input.length > 0 && possibleOptionElement.length > 0)
|
||||
{
|
||||
$('#product_id').val(possibleOptionElement.val());
|
||||
$('#product_id').attr("barcode", input);
|
||||
$('#product_id').data('combobox').refresh();
|
||||
$('#product_id').trigger('change');
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Grocy.Components.ProductPicker.PopupOpen === true)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var optionElement = $("#product_id option:contains(\"" + input + "\")").first();
|
||||
if (input.length > 0 && optionElement.length === 0 && GetUriParam('flow') === undefined && Grocy.Components.ProductPicker.GetPicker().parent().data('disallow-all-product-workflows').toString() === "false")
|
||||
{
|
||||
var addProductWorkflowsAdditionalCssClasses = "";
|
||||
if (Grocy.Components.ProductPicker.GetPicker().parent().data('disallow-add-product-workflows').toString() === "true")
|
||||
{
|
||||
addProductWorkflowsAdditionalCssClasses = "d-none";
|
||||
}
|
||||
|
||||
var embedded = "";
|
||||
if (GetUriParam("embedded") !== undefined)
|
||||
{
|
||||
embedded = "embedded";
|
||||
}
|
||||
|
||||
var buttons = {
|
||||
cancel: {
|
||||
label: Grocy.translate('Cancel'),
|
||||
className: 'btn-secondary responsive-button',
|
||||
callback: function()
|
||||
{
|
||||
Grocy.Components.ProductPicker.PopupOpen = false;
|
||||
Grocy.Components.ProductPicker.SetValue('');
|
||||
}
|
||||
},
|
||||
addnewproduct: {
|
||||
label: '<strong>P</strong> ' + Grocy.translate('Add as new product'),
|
||||
className: 'btn-success add-new-product-dialog-button responsive-button ' + addProductWorkflowsAdditionalCssClasses,
|
||||
callback: function()
|
||||
{
|
||||
|
||||
Grocy.Components.ProductPicker.PopupOpen = false;
|
||||
window.location.href = Grocy.FormatUrl('/product/new?flow=InplaceNewProductWithName&name=' + encodeURIComponent(input) + '&returnto=' + encodeURIComponent(Grocy.CurrentUrlRelative + "?flow=InplaceNewProductWithName&" + embedded) + "&" + embedded);
|
||||
}
|
||||
},
|
||||
addbarcode: {
|
||||
label: '<strong>B</strong> ' + Grocy.translate('Add as barcode to existing product'),
|
||||
className: 'btn-info add-new-barcode-dialog-button responsive-button',
|
||||
callback: function()
|
||||
{
|
||||
Grocy.Components.ProductPicker.PopupOpen = false;
|
||||
window.location.href = Grocy.FormatUrl(Grocy.CurrentUrlRelative + '?flow=InplaceAddBarcodeToExistingProduct&barcode=' + encodeURIComponent(input));
|
||||
}
|
||||
},
|
||||
addnewproductwithbarcode: {
|
||||
label: '<strong>A</strong> ' + Grocy.translate('Add as new product and prefill barcode'),
|
||||
className: 'btn-warning add-new-product-with-barcode-dialog-button responsive-button ' + addProductWorkflowsAdditionalCssClasses,
|
||||
callback: function()
|
||||
{
|
||||
Grocy.Components.ProductPicker.PopupOpen = false;
|
||||
window.location.href = Grocy.FormatUrl('/product/new?flow=InplaceNewProductWithBarcode&barcode=' + encodeURIComponent(input) + '&returnto=' + encodeURIComponent(Grocy.CurrentUrlRelative + "?flow=InplaceAddBarcodeToExistingProduct&barcode=" + input + "&" + embedded) + "&" + embedded);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (!Grocy.FeatureFlags.DISABLE_BROWSER_BARCODE_CAMERA_SCANNING)
|
||||
{
|
||||
buttons.retrycamerascanning = {
|
||||
label: '<strong>C</strong> <i class="fas fa-camera"></i>',
|
||||
className: 'btn-primary responsive-button retry-camera-scanning-button',
|
||||
callback: function()
|
||||
{
|
||||
Grocy.Components.ProductPicker.PopupOpen = false;
|
||||
Grocy.Components.ProductPicker.SetValue('');
|
||||
$("#barcodescanner-start-button").click();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
Grocy.Components.ProductPicker.PopupOpen = true;
|
||||
bootbox.dialog({
|
||||
message: Grocy.translate('"%s" could not be resolved to a product, how do you want to proceed?', input),
|
||||
title: Grocy.translate('Create or assign product'),
|
||||
onEscape: function()
|
||||
{
|
||||
Grocy.Components.ProductPicker.PopupOpen = false;
|
||||
Grocy.Components.ProductPicker.SetValue('');
|
||||
},
|
||||
size: 'large',
|
||||
backdrop: true,
|
||||
closeButton: false,
|
||||
buttons: buttons
|
||||
}).on('keypress', function(e)
|
||||
{
|
||||
if (e.key === 'B' || e.key === 'b')
|
||||
{
|
||||
$('.add-new-barcode-dialog-button').not(".d-none").click();
|
||||
}
|
||||
if (e.key === 'p' || e.key === 'P')
|
||||
{
|
||||
$('.add-new-product-dialog-button').not(".d-none").click();
|
||||
}
|
||||
if (e.key === 'a' || e.key === 'A')
|
||||
{
|
||||
$('.add-new-product-with-barcode-dialog-button').not(".d-none").click();
|
||||
}
|
||||
if (e.key === 'c' || e.key === 'C')
|
||||
{
|
||||
$('.retry-camera-scanning-button').not(".d-none").click();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
$(document).on("Grocy.BarcodeScanned", function(e, barcode, target)
|
||||
{
|
||||
if (!(target == "@productpicker" || target == "undefined" || target == undefined)) // Default target
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Don't know why the blur event does not fire immediately ... this works...
|
||||
Grocy.Components.ProductPicker.GetInputElement().focusout();
|
||||
Grocy.Components.ProductPicker.GetInputElement().focus();
|
||||
Grocy.Components.ProductPicker.GetInputElement().blur();
|
||||
|
||||
Grocy.Components.ProductPicker.GetInputElement().val(barcode);
|
||||
|
||||
setTimeout(function()
|
||||
{
|
||||
Grocy.Components.ProductPicker.GetInputElement().focusout();
|
||||
Grocy.Components.ProductPicker.GetInputElement().focus();
|
||||
Grocy.Components.ProductPicker.GetInputElement().blur();
|
||||
}, 200);
|
||||
});
|
||||
|
||||
$(document).on("shown.bs.modal", function(e)
|
||||
{
|
||||
$(".modal-footer").addClass("d-block").addClass("d-sm-flex");
|
||||
$(".modal-footer").find("button").addClass("mt-2").addClass("mt-sm-0");
|
||||
})
|
||||
|
||||
// Make that ENTER behaves the same like TAB (trigger blur to start workflows, but only when the dropdown is not opened)
|
||||
$('#product_id_text_input').keydown(function(event)
|
||||
{
|
||||
if (event.keyCode === 13) // Enter
|
||||
{
|
||||
if (Grocy.Components.ProductPicker.GetPicker().hasClass("combobox-menu-visible"))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
$("#product_id_text_input").trigger("blur");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export { productpicker }
|
||||
77
js/components/recipepicker.js
Normal file
77
js/components/recipepicker.js
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
function recipepicker(Grocy)
|
||||
{
|
||||
|
||||
Grocy.Components.RecipePicker = {};
|
||||
|
||||
Grocy.Components.RecipePicker.GetPicker = function()
|
||||
{
|
||||
return $('#recipe_id');
|
||||
}
|
||||
|
||||
Grocy.Components.RecipePicker.GetInputElement = function()
|
||||
{
|
||||
return $('#recipe_id_text_input');
|
||||
}
|
||||
|
||||
Grocy.Components.RecipePicker.GetValue = function()
|
||||
{
|
||||
return $('#recipe_id').val();
|
||||
}
|
||||
|
||||
Grocy.Components.RecipePicker.SetValue = function(value)
|
||||
{
|
||||
Grocy.Components.RecipePicker.GetInputElement().val(value);
|
||||
Grocy.Components.RecipePicker.GetInputElement().trigger('change');
|
||||
}
|
||||
|
||||
Grocy.Components.RecipePicker.SetId = function(value)
|
||||
{
|
||||
Grocy.Components.RecipePicker.GetPicker().val(value);
|
||||
Grocy.Components.RecipePicker.GetPicker().data('combobox').refresh();
|
||||
Grocy.Components.RecipePicker.GetInputElement().trigger('change');
|
||||
}
|
||||
|
||||
Grocy.Components.RecipePicker.Clear = function()
|
||||
{
|
||||
Grocy.Components.RecipePicker.SetValue('');
|
||||
Grocy.Components.RecipePicker.SetId(null);
|
||||
}
|
||||
|
||||
$('.recipe-combobox').combobox({
|
||||
appendId: '_text_input',
|
||||
bsVersion: '4',
|
||||
clearIfNoMatch: true
|
||||
});
|
||||
|
||||
var this_recipe_picker = Grocy.Components.RecipePicker.GetPicker();
|
||||
var recipe_picker_doFocus = false;
|
||||
|
||||
var prefillByName = this_recipe_picker.parent().data('prefill-by-name').toString();
|
||||
if (typeof prefillByName !== "undefined")
|
||||
{
|
||||
var possibleOptionElement = $("#recipe_id option:contains(\"" + prefillByName + "\")").first();
|
||||
|
||||
if (possibleOptionElement.length > 0)
|
||||
{
|
||||
recipe_picker_doFocus = true;
|
||||
this_recipe_picker.val(possibleOptionElement.val());
|
||||
}
|
||||
}
|
||||
|
||||
var prefillById = this_recipe_picker.parent().data('prefill-by-id').toString();
|
||||
if (typeof prefillById !== "undefined")
|
||||
{
|
||||
recipe_picker_doFocus = true;
|
||||
this_recipe_picker.val(prefillById);
|
||||
}
|
||||
|
||||
if (recipe_picker_doFocus)
|
||||
{
|
||||
this_recipe_picker.data('combobox').refresh();
|
||||
this_recipe_picker.trigger('change');
|
||||
|
||||
$(this_recipe_picker.parent().data('next-input-selector').toString()).focus();
|
||||
}
|
||||
}
|
||||
|
||||
export { recipepicker }
|
||||
77
js/components/shoppinglocationpicker.js
Normal file
77
js/components/shoppinglocationpicker.js
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
function shoppinglocationpicker(Grocy)
|
||||
{
|
||||
Grocy.Components.ShoppingLocationPicker = {};
|
||||
|
||||
Grocy.Components.ShoppingLocationPicker.GetPicker = function()
|
||||
{
|
||||
return $('#shopping_location_id');
|
||||
}
|
||||
|
||||
Grocy.Components.ShoppingLocationPicker.GetInputElement = function()
|
||||
{
|
||||
return $('#shopping_location_id_text_input');
|
||||
}
|
||||
|
||||
Grocy.Components.ShoppingLocationPicker.GetValue = function()
|
||||
{
|
||||
return $('#shopping_location_id').val();
|
||||
}
|
||||
|
||||
Grocy.Components.ShoppingLocationPicker.SetValue = function(value)
|
||||
{
|
||||
Grocy.Components.ShoppingLocationPicker.GetInputElement().val(value);
|
||||
Grocy.Components.ShoppingLocationPicker.GetInputElement().trigger('change');
|
||||
}
|
||||
|
||||
Grocy.Components.ShoppingLocationPicker.SetId = function(value)
|
||||
{
|
||||
Grocy.Components.ShoppingLocationPicker.GetPicker().val(value);
|
||||
Grocy.Components.ShoppingLocationPicker.GetPicker().data('combobox').refresh();
|
||||
Grocy.Components.ShoppingLocationPicker.GetInputElement().trigger('change');
|
||||
}
|
||||
|
||||
Grocy.Components.ShoppingLocationPicker.Clear = function()
|
||||
{
|
||||
Grocy.Components.ShoppingLocationPicker.SetValue('');
|
||||
Grocy.Components.ShoppingLocationPicker.SetId(null);
|
||||
}
|
||||
|
||||
$('.shopping-location-combobox').combobox({
|
||||
appendId: '_text_input',
|
||||
bsVersion: '4',
|
||||
clearIfNoMatch: true
|
||||
});
|
||||
|
||||
var shoppinglocationpicker_doFocus = false;
|
||||
var this_shoppinglocation_picker = $('#shopping_location_id');
|
||||
|
||||
var prefillByName = this_shoppinglocation_picker.parent().data('prefill-by-name').toString();
|
||||
if (typeof prefillByName !== "undefined")
|
||||
{
|
||||
var possibleOptionElement = $("#shopping_location_id option:contains(\"" + prefillByName + "\")").first();
|
||||
|
||||
if (possibleOptionElement.length > 0)
|
||||
{
|
||||
this_shoppinglocation_picker.val(possibleOptionElement.val());
|
||||
shoppinglocationpicker_doFocus = true;
|
||||
}
|
||||
}
|
||||
|
||||
var prefillById = this_shoppinglocation_picker.parent().data('prefill-by-id').toString();
|
||||
if (typeof prefillById !== "undefined")
|
||||
{
|
||||
this_shoppinglocation_picker.val(prefillById);
|
||||
shoppinglocationpicker_doFocus = true;
|
||||
}
|
||||
|
||||
if (shoppinglocationpicker_doFocus)
|
||||
{
|
||||
this_shoppinglocation_picker.data('combobox').refresh();
|
||||
this_shoppinglocation_picker.trigger('change');
|
||||
|
||||
$(this_shoppinglocation_picker.parent().data('next-input-selector').toString())
|
||||
.focus();
|
||||
}
|
||||
}
|
||||
|
||||
export { shoppinglocationpicker }
|
||||
191
js/components/userfieldsform.js
Normal file
191
js/components/userfieldsform.js
Normal file
|
|
@ -0,0 +1,191 @@
|
|||
import { RandomString } from '../helpers/extensions';
|
||||
import { LoadImagesLazy } from '../configs/lazy'
|
||||
|
||||
function userfieldsform(Grocy)
|
||||
{
|
||||
Grocy.Use("datetimepicker");
|
||||
|
||||
Grocy.Components.UserfieldsForm = {};
|
||||
|
||||
Grocy.Components.UserfieldsForm.Save = function(success, error)
|
||||
{
|
||||
if (!$("#userfields-form").length)
|
||||
{
|
||||
if (success)
|
||||
{
|
||||
success();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
var jsonData = {};
|
||||
|
||||
$("#userfields-form .userfield-input").not("div").each(function()
|
||||
{
|
||||
var input = $(this);
|
||||
var fieldName = input.attr("data-userfield-name");
|
||||
var fieldValue = input.val();
|
||||
|
||||
if (input.attr("type") == "checkbox")
|
||||
{
|
||||
jsonData[fieldName] = "0";
|
||||
if (input.is(":checked"))
|
||||
{
|
||||
jsonData[fieldName] = "1";
|
||||
}
|
||||
}
|
||||
else if (input.attr("type") == "file")
|
||||
{
|
||||
var oldFile = input.data('old-file')
|
||||
if (oldFile)
|
||||
{
|
||||
Grocy.Api.Delete('files/userfiles/' + oldFile, null, null,
|
||||
function(xhr)
|
||||
{
|
||||
Grocy.FrontendHelpers.ShowGenericError('Could not delete file', xhr);
|
||||
});
|
||||
jsonData[fieldName] = "";
|
||||
}
|
||||
|
||||
if (input[0].files.length > 0)
|
||||
{
|
||||
// Files service requires an extension
|
||||
var fileName = RandomString() + '.' + input[0].files[0].name.split('.').reverse()[0];
|
||||
|
||||
jsonData[fieldName] = btoa(fileName) + '_' + btoa(input[0].files[0].name);
|
||||
Grocy.Api.UploadFile(input[0].files[0], 'userfiles', fileName,
|
||||
function(result)
|
||||
{
|
||||
},
|
||||
function(xhr)
|
||||
{
|
||||
// When navigating away immediately from the current page, this is maybe a false positive - so ignore this for now
|
||||
// Grocy.FrontendHelpers.ShowGenericError('Error while saving, probably this item already exists', xhr.response)
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
else if ($(this).hasAttr("multiple"))
|
||||
{
|
||||
jsonData[fieldName] = $(this).val().join(",");
|
||||
}
|
||||
else
|
||||
{
|
||||
jsonData[fieldName] = fieldValue;
|
||||
}
|
||||
});
|
||||
|
||||
Grocy.Api.Put('userfields/' + $("#userfields-form").data("entity") + '/' + Grocy.EditObjectId, jsonData,
|
||||
function(result)
|
||||
{
|
||||
if (success)
|
||||
{
|
||||
success();
|
||||
}
|
||||
},
|
||||
function(xhr)
|
||||
{
|
||||
if (error)
|
||||
{
|
||||
error();
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
Grocy.Components.UserfieldsForm.Load = function()
|
||||
{
|
||||
if (!$("#userfields-form").length)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Grocy.Api.Get('userfields/' + $("#userfields-form").data("entity") + '/' + Grocy.EditObjectId,
|
||||
function(result)
|
||||
{
|
||||
$.each(result, function(key, value)
|
||||
{
|
||||
var input = $(".userfield-input[data-userfield-name='" + key + "']");
|
||||
|
||||
if (input.attr("type") == "checkbox" && value == 1)
|
||||
{
|
||||
input.prop("checked", true);
|
||||
}
|
||||
else if (input.hasAttr("multiple"))
|
||||
{
|
||||
input.val(value.split(","));
|
||||
$(".selectpicker").selectpicker("render");
|
||||
}
|
||||
else if (input.attr('type') == "file")
|
||||
{
|
||||
if (value != null && !value.isEmpty())
|
||||
{
|
||||
var fileName = atob(value.split('_')[1]);
|
||||
var fileSrc = value.split('_')[0];
|
||||
var formGroup = input.parent().parent().parent();
|
||||
|
||||
formGroup.find("label.custom-file-label").text(fileName);
|
||||
formGroup.find(".userfield-file-show").attr('href', Grocy.FormatUrl('/files/userfiles/' + value));
|
||||
formGroup.find('.userfield-file-show').removeClass('d-none');
|
||||
formGroup.find('img.userfield-current-file')
|
||||
.attr('src', Grocy.FormatUrl('/files/userfiles/' + value + '?force_serve_as=picture&best_fit_width=250&best_fit_height=250'));
|
||||
LoadImagesLazy();
|
||||
|
||||
formGroup.find('.userfield-file-delete').click(
|
||||
function()
|
||||
{
|
||||
formGroup.find("label.custom-file-label").text(Grocy.translate("No file selected"));
|
||||
formGroup.find(".userfield-file-show").addClass('d-none');
|
||||
input.attr('data-old-file', fileSrc);
|
||||
}
|
||||
);
|
||||
|
||||
input.on("change", function(e)
|
||||
{
|
||||
formGroup.find(".userfield-file-show").addClass('d-none');
|
||||
});
|
||||
}
|
||||
}
|
||||
else if (input.attr("data-userfield-type") == "link")
|
||||
{
|
||||
if (!value.isEmpty())
|
||||
{
|
||||
var data = JSON.parse(value);
|
||||
|
||||
var formRow = input.parent().parent();
|
||||
formRow.find(".userfield-link-title").val(data.title);
|
||||
formRow.find(".userfield-link-link").val(data.link);
|
||||
|
||||
input.val(value);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
input.val(value);
|
||||
}
|
||||
});
|
||||
},
|
||||
function(xhr)
|
||||
{
|
||||
console.error(xhr);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
$(".userfield-link").keyup(function(e)
|
||||
{
|
||||
var formRow = $(this).parent().parent();
|
||||
var title = formRow.find(".userfield-link-title").val();
|
||||
var link = formRow.find(".userfield-link-link").val();
|
||||
|
||||
var value = {
|
||||
"title": title,
|
||||
"link": link
|
||||
};
|
||||
|
||||
formRow.find(".userfield-input").val(JSON.stringify(value));
|
||||
});
|
||||
}
|
||||
|
||||
export { userfieldsform }
|
||||
86
js/components/userpicker.js
Normal file
86
js/components/userpicker.js
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
function userpicker(Grocy)
|
||||
{
|
||||
|
||||
Grocy.Components.UserPicker = {};
|
||||
|
||||
Grocy.Components.UserPicker.GetPicker = function()
|
||||
{
|
||||
return $('#user_id');
|
||||
}
|
||||
|
||||
Grocy.Components.UserPicker.GetInputElement = function()
|
||||
{
|
||||
return $('#user_id_text_input');
|
||||
}
|
||||
|
||||
Grocy.Components.UserPicker.GetValue = function()
|
||||
{
|
||||
return $('#user_id').val();
|
||||
}
|
||||
|
||||
Grocy.Components.UserPicker.SetValue = function(value)
|
||||
{
|
||||
Grocy.Components.UserPicker.GetInputElement().val(value);
|
||||
Grocy.Components.UserPicker.GetInputElement().trigger('change');
|
||||
}
|
||||
|
||||
Grocy.Components.UserPicker.SetId = function(value)
|
||||
{
|
||||
Grocy.Components.UserPicker.GetPicker().val(value);
|
||||
Grocy.Components.UserPicker.GetPicker().data('combobox').refresh();
|
||||
Grocy.Components.UserPicker.GetInputElement().trigger('change');
|
||||
}
|
||||
|
||||
Grocy.Components.UserPicker.Clear = function()
|
||||
{
|
||||
Grocy.Components.UserPicker.SetValue('');
|
||||
Grocy.Components.UserPicker.SetId(null);
|
||||
}
|
||||
|
||||
$('.user-combobox').combobox({
|
||||
appendId: '_text_input',
|
||||
bsVersion: '4'
|
||||
});
|
||||
|
||||
var this_user_picker = Grocy.Components.UserPicker.GetPicker();
|
||||
var user_picker_doFocus = false;
|
||||
var possibleOptionElement = null;
|
||||
|
||||
var prefillUser = this_user_picker.parent().data('prefill-by-username').toString();
|
||||
if (typeof prefillUser !== "undefined")
|
||||
{
|
||||
possibleOptionElement = $("#user_id option[data-additional-searchdata*=\"" + prefillUser + "\"]").first();
|
||||
if (possibleOptionElement.length === 0)
|
||||
{
|
||||
possibleOptionElement = $("#user_id option:contains(\"" + prefillUser + "\")").first();
|
||||
}
|
||||
|
||||
if (possibleOptionElement.length > 0)
|
||||
{
|
||||
user_picker_doFocus = true;
|
||||
this_user_picker.val(possibleOptionElement.val());
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
var prefillUserId = this_user_picker.parent().data('prefill-by-user-id').toString();
|
||||
if (typeof prefillUserId !== "undefined")
|
||||
{
|
||||
possibleOptionElement = $("#user_id option[value='" + prefillUserId + "']").first();
|
||||
if (possibleOptionElement.length > 0)
|
||||
{
|
||||
user_picker_doFocus = true;
|
||||
this_user_picker.val(possibleOptionElement.val());
|
||||
}
|
||||
}
|
||||
|
||||
if (user_picker_doFocus)
|
||||
{
|
||||
this_user_picker.data('combobox').refresh();
|
||||
this_user_picker.trigger('change');
|
||||
|
||||
$(this_user_picker.parent().data('next-input-selector').toString())
|
||||
.focus();
|
||||
}
|
||||
}
|
||||
export { userpicker }
|
||||
|
|
@ -18,10 +18,11 @@ function setInitialGlobalState(Grocy)
|
|||
var parentMenuSelector = menuItem.data("sub-menu-of");
|
||||
if (typeof parentMenuSelector !== "undefined")
|
||||
{
|
||||
$(parentMenuSelector).collapse("show");
|
||||
$(parentMenuSelector).prev(".nav-link-collapse").addClass("active-page");
|
||||
var pMenu = $(parentMenuSelector);
|
||||
pMenu.collapse("show");
|
||||
pMenu.prev(".nav-link-collapse").addClass("active-page");
|
||||
|
||||
$(parentMenuSelector).on("shown.bs.collapse", function()
|
||||
pMenu.on("shown.bs.collapse", function()
|
||||
{
|
||||
if (!menuItem.isVisibleInViewport(75))
|
||||
{
|
||||
|
|
|
|||
28
js/grocy.js
28
js/grocy.js
|
|
@ -12,6 +12,7 @@ import { HeaderClock } from "./helpers/clock";
|
|||
import { animateCSS, BoolVal, EmptyElementWhenMatches, GetUriParam, RemoveUriParam, UpdateUriParam } from "./helpers/extensions";
|
||||
import Translator from "gettext-translator";
|
||||
import { WindowMessageBag } from './helpers/messagebag';
|
||||
import * as components from './components';
|
||||
|
||||
import "./helpers/string";
|
||||
|
||||
|
|
@ -47,6 +48,7 @@ class GrocyClass
|
|||
this.Locale = config.Locale;
|
||||
|
||||
this.Components = {};
|
||||
this.initComponents = [];
|
||||
|
||||
// Init some classes
|
||||
this.Api = new GrocyApi(this);
|
||||
|
|
@ -150,21 +152,21 @@ class GrocyClass
|
|||
|
||||
translate(text, ...placeholderValues)
|
||||
{
|
||||
if (this.Mode === "dev")
|
||||
/*if (this.Mode === "dev")
|
||||
{
|
||||
var text2 = text;
|
||||
this.Api.Post('system/log-missing-localization', { "text": text2 });
|
||||
}
|
||||
}*/
|
||||
|
||||
return this.Translator.__(text, ...placeholderValues)
|
||||
}
|
||||
translaten(number, singularForm, pluralForm)
|
||||
{
|
||||
if (this.Mode === "dev")
|
||||
/*if (this.Mode === "dev")
|
||||
{
|
||||
var singularForm2 = singularForm;
|
||||
this.Api.Post('system/log-missing-localization', { "text": singularForm2 });
|
||||
}
|
||||
}*/
|
||||
|
||||
return this.Translator.n__(singularForm, pluralForm, number, number)
|
||||
}
|
||||
|
|
@ -212,6 +214,24 @@ class GrocyClass
|
|||
this.IdleTime += 1;
|
||||
}
|
||||
|
||||
Use(componentName)
|
||||
{
|
||||
// initialize Components only once
|
||||
if (this.initComponents.find(elem => elem == componentName))
|
||||
return;
|
||||
|
||||
if (Object.prototype.hasOwnProperty.call(components, componentName))
|
||||
{
|
||||
// add-then-init to resolve circular dependencies
|
||||
this.initComponents.push(componentName);
|
||||
components[componentName](this);
|
||||
}
|
||||
else
|
||||
{
|
||||
console.error("Unable to find component " + componentName);
|
||||
}
|
||||
}
|
||||
|
||||
UndoStockBooking(bookingId)
|
||||
{
|
||||
var self = this;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
Grocy.BarCodeScannerTestingHitCount = 0;
|
||||
Grocy.Use("barcodescanner");
|
||||
Grocy.BarCodeScannerTestingHitCount = 0;
|
||||
Grocy.BarCodeScannerTestingMissCount = 0;
|
||||
|
||||
$("#scanned_barcode").on("blur", function(e)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
var batteriesOverviewTable = $('#batteries-overview-table').DataTable({
|
||||
Grocy.Use("batterycard");
|
||||
|
||||
var batteriesOverviewTable = $('#batteries-overview-table').DataTable({
|
||||
'order': [[4, 'asc']],
|
||||
'columnDefs': [
|
||||
{ 'orderable': false, 'targets': 0 },
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
$("#batteries_due_soon_days").val(Grocy.UserSettings.batteries_due_soon_days);
|
||||
Grocy.Use("numberpicker");
|
||||
|
||||
$("#batteries_due_soon_days").val(Grocy.UserSettings.batteries_due_soon_days);
|
||||
|
||||
RefreshLocaleNumberInput();
|
||||
|
|
|
|||
|
|
@ -1,5 +1,8 @@
|
|||
import { WindowMessageBag } from '../helpers/messagebag';
|
||||
|
||||
Grocy.Use("numberpicker");
|
||||
Grocy.Use("userfieldsform");
|
||||
|
||||
$('#save-battery-button').on('click', function(e)
|
||||
{
|
||||
e.preventDefault();
|
||||
|
|
|
|||
|
|
@ -1,4 +1,7 @@
|
|||
$('#save-batterytracking-button').on('click', function(e)
|
||||
Grocy.Use("batterycard");
|
||||
Grocy.Use("datetimepicker");
|
||||
|
||||
$('#save-batterytracking-button').on('click', function(e)
|
||||
{
|
||||
e.preventDefault();
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,7 @@
|
|||
$('#save-chore-button').on('click', function(e)
|
||||
Grocy.Use("numberpicker");
|
||||
Grocy.Use("userfieldsform");
|
||||
|
||||
$('#save-chore-button').on('click', function(e)
|
||||
{
|
||||
e.preventDefault();
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
var choresOverviewTable = $('#chores-overview-table').DataTable({
|
||||
Grocy.Use("chorecard");
|
||||
|
||||
var choresOverviewTable = $('#chores-overview-table').DataTable({
|
||||
'order': [[2, 'asc']],
|
||||
'columnDefs': [
|
||||
{ 'orderable': false, 'targets': 0 },
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
$("#chores_due_soon_days").val(Grocy.UserSettings.chores_due_soon_days);
|
||||
Grocy.Use("numberpicker");
|
||||
|
||||
$("#chores_due_soon_days").val(Grocy.UserSettings.chores_due_soon_days);
|
||||
|
||||
RefreshLocaleNumberInput();
|
||||
|
|
|
|||
|
|
@ -1,4 +1,8 @@
|
|||
$('#save-choretracking-button').on('click', function(e)
|
||||
Grocy.Use("chorecard");
|
||||
Grocy.Use("datetimepicker");
|
||||
Grocy.Use("userpicker");
|
||||
|
||||
$('#save-choretracking-button').on('click', function(e)
|
||||
{
|
||||
e.preventDefault();
|
||||
|
||||
|
|
|
|||
|
|
@ -1,304 +0,0 @@
|
|||
/* global Quagga2DatamatrixReader */
|
||||
import Quagga from '@ericblade/quagga2/dist/quagga';
|
||||
|
||||
Grocy.Components.BarcodeScanner = {};
|
||||
|
||||
Quagga.registerReader("datamatrix", Quagga2DatamatrixReader);
|
||||
|
||||
Grocy.Components.BarcodeScanner.LiveVideoSizeAdjusted = false;
|
||||
Grocy.Components.BarcodeScanner.CheckCapabilities = async function()
|
||||
{
|
||||
var track = Quagga.CameraAccess.getActiveTrack();
|
||||
var capabilities = {};
|
||||
if (typeof track.getCapabilities === 'function')
|
||||
{
|
||||
capabilities = track.getCapabilities();
|
||||
}
|
||||
|
||||
// If there is more than 1 camera, show the camera selection
|
||||
var cameras = await Quagga.CameraAccess.enumerateVideoDevices();
|
||||
var cameraSelect = document.querySelector('.cameraSelect-wrapper');
|
||||
if (cameraSelect)
|
||||
{
|
||||
cameraSelect.style.display = cameras.length > 1 ? 'inline-block' : 'none';
|
||||
}
|
||||
|
||||
// Check if the camera is capable to turn on a torch.
|
||||
var canTorch = typeof capabilities.torch === 'boolean' && capabilities.torch
|
||||
// Remove the torch button, if either the device can not torch or AutoTorchOn is set.
|
||||
var node = document.querySelector('.torch');
|
||||
if (node)
|
||||
{
|
||||
node.style.display = canTorch && !Grocy.FeatureFlags.GROCY_FEATURE_FLAG_AUTO_TORCH_ON_WITH_CAMERA ? 'inline-block' : 'none';
|
||||
}
|
||||
// If AutoTorchOn is set, turn on the torch.
|
||||
if (canTorch && Grocy.FeatureFlags.GROCY_FEATURE_FLAG_AUTO_TORCH_ON_WITH_CAMERA)
|
||||
{
|
||||
Grocy.Components.BarcodeScanner.TorchOn(track);
|
||||
}
|
||||
|
||||
// Reduce the height of the video, if it's higher than then the viewport
|
||||
if (!Grocy.Components.BarcodeScanner.LiveVideoSizeAdjusted)
|
||||
{
|
||||
var bc = document.getElementById('barcodescanner-container');
|
||||
if (bc)
|
||||
{
|
||||
var bcAspectRatio = bc.offsetWidth / bc.offsetHeight;
|
||||
var settings = track.getSettings();
|
||||
if (bcAspectRatio > settings.aspectRatio)
|
||||
{
|
||||
var v = document.querySelector('#barcodescanner-livestream video')
|
||||
if (v)
|
||||
{
|
||||
var c = document.querySelector('#barcodescanner-livestream canvas')
|
||||
var newWidth = v.clientWidth / bcAspectRatio * settings.aspectRatio + 'px';
|
||||
v.style.width = newWidth;
|
||||
c.style.width = newWidth;
|
||||
}
|
||||
}
|
||||
|
||||
Grocy.Components.BarcodeScanner.LiveVideoSizeAdjusted = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Grocy.Components.BarcodeScanner.StartScanning = function()
|
||||
{
|
||||
Grocy.Components.BarcodeScanner.DecodedCodesCount = 0;
|
||||
Grocy.Components.BarcodeScanner.DecodedCodesErrorCount = 0;
|
||||
|
||||
Quagga.init({
|
||||
inputStream: {
|
||||
name: "Live",
|
||||
type: "LiveStream",
|
||||
target: document.querySelector("#barcodescanner-livestream"),
|
||||
constraints: {
|
||||
facingMode: "environment",
|
||||
...(window.localStorage.getItem('cameraId') && { deviceId: window.localStorage.getItem('cameraId') }) // If preferred cameraId is set, request to use that specific camera
|
||||
}
|
||||
},
|
||||
locator: {
|
||||
patchSize: Grocy.UserSettings.quagga2_patchsize,
|
||||
halfSample: Grocy.UserSettings.quagga2_halfsample,
|
||||
debug: {
|
||||
showCanvas: Grocy.UserSettings.quagga2_debug,
|
||||
showPatches: Grocy.UserSettings.quagga2_debug,
|
||||
showFoundPatches: Grocy.UserSettings.quagga2_debug,
|
||||
showSkeleton: Grocy.UserSettings.quagga2_debug,
|
||||
showLabels: Grocy.UserSettings.quagga2_debug,
|
||||
showPatchLabels: Grocy.UserSettings.quagga2_debug,
|
||||
showRemainingPatchLabels: Grocy.UserSettings.quagga2_debug,
|
||||
boxFromPatches: {
|
||||
showTransformed: Grocy.UserSettings.quagga2_debug,
|
||||
showTransformedBox: Grocy.UserSettings.quagga2_debug,
|
||||
showBB: Grocy.UserSettings.quagga2_debug
|
||||
}
|
||||
}
|
||||
},
|
||||
numOfWorkers: Grocy.UserSettings.quagga2_numofworkers,
|
||||
frequency: Grocy.UserSettings.quagga2_frequency,
|
||||
decoder: {
|
||||
readers: [
|
||||
"ean_reader",
|
||||
"ean_8_reader",
|
||||
"code_128_reader",
|
||||
"datamatrix"
|
||||
],
|
||||
debug: {
|
||||
showCanvas: Grocy.UserSettings.quagga2_debug,
|
||||
showPatches: Grocy.UserSettings.quagga2_debug,
|
||||
showFoundPatches: Grocy.UserSettings.quagga2_debug,
|
||||
showSkeleton: Grocy.UserSettings.quagga2_debug,
|
||||
showLabels: Grocy.UserSettings.quagga2_debug,
|
||||
showPatchLabels: Grocy.UserSettings.quagga2_debug,
|
||||
showRemainingPatchLabels: Grocy.UserSettings.quagga2_debug,
|
||||
boxFromPatches: {
|
||||
showTransformed: Grocy.UserSettings.quagga2_debug,
|
||||
showTransformedBox: Grocy.UserSettings.quagga2_debug,
|
||||
showBB: Grocy.UserSettings.quagga2_debug
|
||||
}
|
||||
}
|
||||
},
|
||||
locate: true
|
||||
}, function(error)
|
||||
{
|
||||
// error *needs* to be logged here, otherwise the stack trace is lying.
|
||||
console.error(error);
|
||||
if (error)
|
||||
{
|
||||
Grocy.FrontendHelpers.ShowGenericError("Error while initializing the barcode scanning library", error.message);
|
||||
toastr.info(__t("Camera access is only possible when supported and allowed by your browser and when grocy is served via a secure (https://) connection"));
|
||||
window.localStorage.removeItem("cameraId");
|
||||
setTimeout(function()
|
||||
{
|
||||
bootbox.hideAll();
|
||||
}, 500);
|
||||
return;
|
||||
}
|
||||
|
||||
Grocy.Components.BarcodeScanner.CheckCapabilities();
|
||||
|
||||
Quagga.start();
|
||||
});
|
||||
}
|
||||
|
||||
Grocy.Components.BarcodeScanner.StopScanning = function()
|
||||
{
|
||||
Quagga.stop();
|
||||
|
||||
Grocy.Components.BarcodeScanner.DecodedCodesCount = 0;
|
||||
Grocy.Components.BarcodeScanner.DecodedCodesErrorCount = 0;
|
||||
|
||||
bootbox.hideAll();
|
||||
}
|
||||
|
||||
Grocy.Components.BarcodeScanner.TorchOn = function(track)
|
||||
{
|
||||
if (track)
|
||||
{
|
||||
track.applyConstraints({
|
||||
advanced: [
|
||||
{
|
||||
torch: true
|
||||
}
|
||||
]
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Quagga.onDetected(function(result)
|
||||
{
|
||||
$.each(result.codeResult.decodedCodes, function(id, error)
|
||||
{
|
||||
if (error.error != undefined)
|
||||
{
|
||||
Grocy.Components.BarcodeScanner.DecodedCodesCount++;
|
||||
Grocy.Components.BarcodeScanner.DecodedCodesErrorCount += parseFloat(error.error);
|
||||
}
|
||||
});
|
||||
|
||||
if (Grocy.Components.BarcodeScanner.DecodedCodesErrorCount / Grocy.Components.BarcodeScanner.DecodedCodesCount < 0.15)
|
||||
{
|
||||
Grocy.Components.BarcodeScanner.StopScanning();
|
||||
$(document).trigger("Grocy.BarcodeScanned", [result.codeResult.code, Grocy.Components.BarcodeScanner.CurrentTarget]);
|
||||
}
|
||||
});
|
||||
|
||||
Quagga.onProcessed(function(result)
|
||||
{
|
||||
var drawingCtx = Quagga.canvas.ctx.overlay;
|
||||
var drawingCanvas = Quagga.canvas.dom.overlay;
|
||||
|
||||
if (result)
|
||||
{
|
||||
if (result.boxes)
|
||||
{
|
||||
drawingCtx.clearRect(0, 0, parseInt(drawingCanvas.getAttribute("width")), parseInt(drawingCanvas.getAttribute("height")));
|
||||
result.boxes.filter(function(box)
|
||||
{
|
||||
return box !== result.box;
|
||||
}).forEach(function(box)
|
||||
{
|
||||
Quagga.ImageDebug.drawPath(box, { x: 0, y: 1 }, drawingCtx, { color: "yellow", lineWidth: 4 });
|
||||
});
|
||||
}
|
||||
|
||||
if (result.box)
|
||||
{
|
||||
Quagga.ImageDebug.drawPath(result.box, { x: 0, y: 1 }, drawingCtx, { color: "green", lineWidth: 4 });
|
||||
}
|
||||
|
||||
if (result.codeResult && result.codeResult.code)
|
||||
{
|
||||
Quagga.ImageDebug.drawPath(result.line, { x: 'x', y: 'y' }, drawingCtx, { color: "red", lineWidth: 4 });
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
$(document).on("click", "#barcodescanner-start-button", async function(e)
|
||||
{
|
||||
e.preventDefault();
|
||||
var inputElement = $(e.currentTarget).prev();
|
||||
if (inputElement.hasAttr("disabled"))
|
||||
{
|
||||
// Do nothing and disable the barcode scanner start button
|
||||
$(e.currentTarget).addClass("disabled");
|
||||
return;
|
||||
}
|
||||
|
||||
Grocy.Components.BarcodeScanner.CurrentTarget = inputElement.attr("data-target");
|
||||
|
||||
var dialog = bootbox.dialog({
|
||||
message: '<div id="barcodescanner-container" class="col"><div id="barcodescanner-livestream"></div><div id="debug"></div></div>',
|
||||
title: __t('Scan a barcode'),
|
||||
onEscape: function()
|
||||
{
|
||||
Grocy.Components.BarcodeScanner.StopScanning();
|
||||
},
|
||||
size: 'big',
|
||||
backdrop: true,
|
||||
closeButton: true,
|
||||
buttons: {
|
||||
torch: {
|
||||
label: '<i class="far fa-lightbulb"></i>',
|
||||
className: 'btn-warning responsive-button torch',
|
||||
callback: function()
|
||||
{
|
||||
Grocy.Components.BarcodeScanner.TorchOn(Quagga.CameraAccess.getActiveTrack());
|
||||
return false;
|
||||
}
|
||||
},
|
||||
cancel: {
|
||||
label: __t('Cancel'),
|
||||
className: 'btn-secondary responsive-button',
|
||||
callback: function()
|
||||
{
|
||||
Grocy.Components.BarcodeScanner.StopScanning();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Add camera select to existing dialog
|
||||
dialog.find('.bootbox-body').append('<div class="form-group py-0 my-1 cameraSelect-wrapper"><select class="custom-control custom-select cameraSelect"><select class="custom-control custom-select cameraSelect" style="display: none"></select></div>');
|
||||
var cameraSelect = document.querySelector('.cameraSelect');
|
||||
|
||||
if (cameraSelect != null)
|
||||
{
|
||||
var cameras = await Quagga.CameraAccess.enumerateVideoDevices();
|
||||
cameras.forEach(camera =>
|
||||
{
|
||||
var option = document.createElement("option");
|
||||
option.text = camera.label ? camera.label : camera.deviceId; // Use camera label if it exists, else show device id
|
||||
option.value = camera.deviceId;
|
||||
cameraSelect.appendChild(option);
|
||||
});
|
||||
|
||||
// Set initial value to preferred camera if one exists - and if not, start out empty
|
||||
cameraSelect.value = window.localStorage.getItem('cameraId');
|
||||
|
||||
cameraSelect.onchange = function()
|
||||
{
|
||||
window.localStorage.setItem('cameraId', cameraSelect.value);
|
||||
Quagga.stop();
|
||||
Grocy.Components.BarcodeScanner.StartScanning();
|
||||
};
|
||||
}
|
||||
|
||||
Grocy.Components.BarcodeScanner.StartScanning();
|
||||
});
|
||||
|
||||
setTimeout(function()
|
||||
{
|
||||
$(".barcodescanner-input:visible").each(function()
|
||||
{
|
||||
if ($(this).hasAttr("disabled"))
|
||||
{
|
||||
$(this).after('<a id="barcodescanner-start-button" class="btn btn-sm btn-primary text-white disabled" data-target="' + $(this).attr("data-target") + '"><i class="fas fa-camera"></i></a>');
|
||||
}
|
||||
else
|
||||
{
|
||||
$(this).after('<a id="barcodescanner-start-button" class="btn btn-sm btn-primary text-white" data-target="' + $(this).attr("data-target") + '"><i class="fas fa-camera"></i></a>');
|
||||
}
|
||||
});
|
||||
}, 50);
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
Grocy.Components.BatteryCard = {};
|
||||
|
||||
Grocy.Components.BatteryCard.Refresh = function(batteryId)
|
||||
{
|
||||
Grocy.Api.Get('batteries/' + batteryId,
|
||||
function(batteryDetails)
|
||||
{
|
||||
$('#batterycard-battery-name').text(batteryDetails.battery.name);
|
||||
$('#batterycard-battery-used_in').text(batteryDetails.battery.used_in);
|
||||
$('#batterycard-battery-last-charged').text((batteryDetails.last_charged || __t('never')));
|
||||
$('#batterycard-battery-last-charged-timeago').attr("datetime", batteryDetails.last_charged || '');
|
||||
$('#batterycard-battery-charge-cycles-count').text((batteryDetails.charge_cycles_count || '0'));
|
||||
|
||||
$('#batterycard-battery-edit-button').attr("href", U("/battery/" + batteryDetails.battery.id.toString()));
|
||||
$('#batterycard-battery-journal-button').attr("href", U("/batteriesjournal?embedded&battery=" + batteryDetails.battery.id.toString()));
|
||||
$('#batterycard-battery-edit-button').removeClass("disabled");
|
||||
$('#batterycard-battery-journal-button').removeClass("disabled");
|
||||
|
||||
EmptyElementWhenMatches('#batterycard-battery-last-charged-timeago', __t('timeago_nan'));
|
||||
RefreshContextualTimeago(".batterycard");
|
||||
},
|
||||
function(xhr)
|
||||
{
|
||||
console.error(xhr);
|
||||
}
|
||||
);
|
||||
};
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
$('#calendar').datetimepicker(
|
||||
{
|
||||
format: 'L',
|
||||
buttons: {
|
||||
showToday: true,
|
||||
showClose: false
|
||||
},
|
||||
calendarWeeks: true,
|
||||
locale: moment.locale(),
|
||||
icons: {
|
||||
time: 'far fa-clock',
|
||||
date: 'far fa-calendar',
|
||||
up: 'fas fa-arrow-up',
|
||||
down: 'fas fa-arrow-down',
|
||||
previous: 'fas fa-chevron-left',
|
||||
next: 'fas fa-chevron-right',
|
||||
today: 'fas fa-calendar-check',
|
||||
clear: 'far fa-trash-alt',
|
||||
close: 'far fa-times-circle'
|
||||
},
|
||||
keepOpen: true,
|
||||
inline: true,
|
||||
keyBinds: {
|
||||
up: function(widget) { },
|
||||
down: function(widget) { },
|
||||
'control up': function(widget) { },
|
||||
'control down': function(widget) { },
|
||||
left: function(widget) { },
|
||||
right: function(widget) { },
|
||||
pageUp: function(widget) { },
|
||||
pageDown: function(widget) { },
|
||||
enter: function(widget) { },
|
||||
escape: function(widget) { },
|
||||
'control space': function(widget) { },
|
||||
t: function(widget) { },
|
||||
'delete': function(widget) { }
|
||||
}
|
||||
});
|
||||
|
||||
$('#calendar').datetimepicker('show');
|
||||
|
|
@ -1,36 +0,0 @@
|
|||
Grocy.Components.ChoreCard = {};
|
||||
|
||||
Grocy.Components.ChoreCard.Refresh = function(choreId)
|
||||
{
|
||||
Grocy.Api.Get('chores/' + choreId,
|
||||
function(choreDetails)
|
||||
{
|
||||
$('#chorecard-chore-name').text(choreDetails.chore.name);
|
||||
$('#chorecard-chore-last-tracked').text((choreDetails.last_tracked || __t('never')));
|
||||
$('#chorecard-chore-last-tracked-timeago').attr("datetime", choreDetails.last_tracked || '');
|
||||
$('#chorecard-chore-tracked-count').text((choreDetails.tracked_count || '0'));
|
||||
$('#chorecard-chore-last-done-by').text((choreDetails.last_done_by.display_name || __t('Unknown')));
|
||||
|
||||
$('#chorecard-chore-edit-button').attr("href", U("/chore/" + choreDetails.chore.id.toString()));
|
||||
$('#chorecard-chore-journal-button').attr("href", U("/choresjournal?embedded&chore=" + choreDetails.chore.id.toString()));
|
||||
$('#chorecard-chore-edit-button').removeClass("disabled");
|
||||
$('#chorecard-chore-journal-button').removeClass("disabled");
|
||||
|
||||
if (choreDetails.chore.track_date_only == 1)
|
||||
{
|
||||
$("#chorecard-chore-last-tracked-timeago").addClass("timeago-date-only");
|
||||
}
|
||||
else
|
||||
{
|
||||
$("#chorecard-chore-last-tracked-timeago").removeClass("timeago-date-only");
|
||||
}
|
||||
|
||||
EmptyElementWhenMatches('#chorecard-chore-last-tracked-timeago', __t('timeago_nan'));
|
||||
RefreshContextualTimeago(".chorecard");
|
||||
},
|
||||
function(xhr)
|
||||
{
|
||||
console.error(xhr);
|
||||
}
|
||||
);
|
||||
};
|
||||
|
|
@ -1,308 +0,0 @@
|
|||
Grocy.Components.DateTimePicker = {};
|
||||
|
||||
Grocy.Components.DateTimePicker.GetInputElement = function()
|
||||
{
|
||||
return $('.datetimepicker').find('input').not(".form-check-input");
|
||||
}
|
||||
|
||||
Grocy.Components.DateTimePicker.GetValue = function()
|
||||
{
|
||||
return Grocy.Components.DateTimePicker.GetInputElement().val();
|
||||
}
|
||||
|
||||
Grocy.Components.DateTimePicker.SetValue = function(value)
|
||||
{
|
||||
// "Click" the shortcut checkbox when the desired value is
|
||||
// not the shortcut value and it is currently set
|
||||
var shortcutValue = $("#datetimepicker-shortcut").data("datetimepicker-shortcut-value");
|
||||
if (value != shortcutValue && $("#datetimepicker-shortcut").is(":checked"))
|
||||
{
|
||||
$("#datetimepicker-shortcut").click();
|
||||
}
|
||||
Grocy.Components.DateTimePicker.GetInputElement().val(value);
|
||||
Grocy.Components.DateTimePicker.GetInputElement().trigger('change');
|
||||
|
||||
Grocy.Components.DateTimePicker.GetInputElement().keyup();
|
||||
}
|
||||
|
||||
Grocy.Components.DateTimePicker.Clear = function()
|
||||
{
|
||||
$(".datetimepicker").datetimepicker("destroy");
|
||||
Grocy.Components.DateTimePicker.Init();
|
||||
|
||||
Grocy.Components.DateTimePicker.GetInputElement().val("");
|
||||
|
||||
// "Click" the shortcut checkbox when the desired value is
|
||||
// not the shortcut value and it is currently set
|
||||
var value = "";
|
||||
var shortcutValue = $("#datetimepicker-shortcut").data("datetimepicker-shortcut-value");
|
||||
if (value != shortcutValue && $("#datetimepicker-shortcut").is(":checked"))
|
||||
{
|
||||
$("#datetimepicker-shortcut").click();
|
||||
}
|
||||
|
||||
$('#datetimepicker-timeago').text('');
|
||||
}
|
||||
|
||||
Grocy.Components.DateTimePicker.ChangeFormat = function(format)
|
||||
{
|
||||
$(".datetimepicker").datetimepicker("destroy");
|
||||
Grocy.Components.DateTimePicker.GetInputElement().data("format", format);
|
||||
Grocy.Components.DateTimePicker.Init();
|
||||
|
||||
if (format == "YYYY-MM-DD")
|
||||
{
|
||||
Grocy.Components.DateTimePicker.GetInputElement().addClass("date-only-datetimepicker");
|
||||
}
|
||||
else
|
||||
{
|
||||
Grocy.Components.DateTimePicker.GetInputElement().removeClass("date-only-datetimepicker");
|
||||
}
|
||||
}
|
||||
|
||||
var startDate = null;
|
||||
if (Grocy.Components.DateTimePicker.GetInputElement().data('init-with-now') === true)
|
||||
{
|
||||
startDate = moment().format(Grocy.Components.DateTimePicker.GetInputElement().data('format'));
|
||||
}
|
||||
if (Grocy.Components.DateTimePicker.GetInputElement().data('init-value').length > 0)
|
||||
{
|
||||
startDate = moment(Grocy.Components.DateTimePicker.GetInputElement().data('init-value')).format(Grocy.Components.DateTimePicker.GetInputElement().data('format'));
|
||||
}
|
||||
|
||||
var limitDate = moment('2999-12-31 23:59:59');
|
||||
if (Grocy.Components.DateTimePicker.GetInputElement().data('limit-end-to-now') === true)
|
||||
{
|
||||
limitDate = moment();
|
||||
}
|
||||
|
||||
Grocy.Components.DateTimePicker.Init = function()
|
||||
{
|
||||
$('.datetimepicker').datetimepicker(
|
||||
{
|
||||
format: Grocy.Components.DateTimePicker.GetInputElement().data('format'),
|
||||
buttons: {
|
||||
showToday: true,
|
||||
showClose: true
|
||||
},
|
||||
calendarWeeks: Grocy.CalendarShowWeekNumbers,
|
||||
maxDate: limitDate,
|
||||
locale: moment.locale(),
|
||||
defaultDate: startDate,
|
||||
useCurrent: false,
|
||||
icons: {
|
||||
time: 'far fa-clock',
|
||||
date: 'far fa-calendar',
|
||||
up: 'fas fa-arrow-up',
|
||||
down: 'fas fa-arrow-down',
|
||||
previous: 'fas fa-chevron-left',
|
||||
next: 'fas fa-chevron-right',
|
||||
today: 'fas fa-calendar-check',
|
||||
clear: 'far fa-trash-alt',
|
||||
close: 'far fa-times-circle'
|
||||
},
|
||||
sideBySide: true,
|
||||
keyBinds: {
|
||||
up: function(widget) { },
|
||||
down: function(widget) { },
|
||||
'control up': function(widget) { },
|
||||
'control down': function(widget) { },
|
||||
left: function(widget) { },
|
||||
right: function(widget) { },
|
||||
pageUp: function(widget) { },
|
||||
pageDown: function(widget) { },
|
||||
enter: function(widget) { },
|
||||
escape: function(widget) { },
|
||||
'control space': function(widget) { },
|
||||
t: function(widget) { },
|
||||
'delete': function(widget) { }
|
||||
}
|
||||
});
|
||||
}
|
||||
Grocy.Components.DateTimePicker.Init();
|
||||
|
||||
Grocy.Components.DateTimePicker.GetInputElement().on('keyup', function(e)
|
||||
{
|
||||
$('.datetimepicker').datetimepicker('hide');
|
||||
|
||||
var value = Grocy.Components.DateTimePicker.GetValue();
|
||||
var now = new Date();
|
||||
var centuryStart = Number.parseInt(now.getFullYear().toString().substring(0, 2) + '00');
|
||||
var centuryEnd = Number.parseInt(now.getFullYear().toString().substring(0, 2) + '99');
|
||||
var format = Grocy.Components.DateTimePicker.GetInputElement().data('format');
|
||||
var nextInputElement = $(Grocy.Components.DateTimePicker.GetInputElement().data('next-input-selector'));
|
||||
|
||||
//If input is empty and any arrow key is pressed, set date to today
|
||||
if (value.length === 0 && (e.keyCode === 38 || e.keyCode === 40 || e.keyCode === 37 || e.keyCode === 39))
|
||||
{
|
||||
Grocy.Components.DateTimePicker.SetValue(moment(new Date(), format, true).format(format));
|
||||
nextInputElement.focus();
|
||||
}
|
||||
else if (value === 'x' || value === 'X')
|
||||
{
|
||||
Grocy.Components.DateTimePicker.SetValue(moment('2999-12-31 23:59:59').format(format));
|
||||
nextInputElement.focus();
|
||||
}
|
||||
else if (value.length === 4 && !(Number.parseInt(value) > centuryStart && Number.parseInt(value) < centuryEnd))
|
||||
{
|
||||
var date = moment((new Date()).getFullYear().toString() + value);
|
||||
if (date.isBefore(moment()))
|
||||
{
|
||||
date.add(1, "year");
|
||||
}
|
||||
Grocy.Components.DateTimePicker.SetValue(date.format(format));
|
||||
nextInputElement.focus();
|
||||
}
|
||||
else if (value.length === 8 && $.isNumeric(value))
|
||||
{
|
||||
Grocy.Components.DateTimePicker.SetValue(value.replace(/(\d{4})(\d{2})(\d{2})/, '$1-$2-$3'));
|
||||
nextInputElement.focus();
|
||||
}
|
||||
else if (value.length === 7 && $.isNumeric(value.substring(0, 6)) && (value.substring(6, 7).toLowerCase() === "e" || value.substring(6, 7).toLowerCase() === "+"))
|
||||
{
|
||||
var endOfMonth = moment(value.substring(0, 4) + "-" + value.substring(4, 6) + "-01").endOf("month");
|
||||
Grocy.Components.DateTimePicker.SetValue(endOfMonth.format(format));
|
||||
nextInputElement.focus();
|
||||
}
|
||||
else
|
||||
{
|
||||
var dateObj = moment(value, format, true);
|
||||
if (dateObj.isValid())
|
||||
{
|
||||
if (e.shiftKey)
|
||||
{
|
||||
// WITH shift modifier key
|
||||
|
||||
if (e.keyCode === 38) // Up
|
||||
{
|
||||
Grocy.Components.DateTimePicker.SetValue(dateObj.add(-1, 'months').format(format));
|
||||
}
|
||||
else if (e.keyCode === 40) // Down
|
||||
{
|
||||
Grocy.Components.DateTimePicker.SetValue(dateObj.add(1, 'months').format(format));
|
||||
}
|
||||
else if (e.keyCode === 37) // Left
|
||||
{
|
||||
Grocy.Components.DateTimePicker.SetValue(dateObj.add(-1, 'years').format(format));
|
||||
}
|
||||
else if (e.keyCode === 39) // Right
|
||||
{
|
||||
Grocy.Components.DateTimePicker.SetValue(dateObj.add(1, 'years').format(format));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// WITHOUT shift modifier key
|
||||
|
||||
if (e.keyCode === 38) // Up
|
||||
{
|
||||
Grocy.Components.DateTimePicker.SetValue(dateObj.add(-1, 'days').format(format));
|
||||
}
|
||||
else if (e.keyCode === 40) // Down
|
||||
{
|
||||
Grocy.Components.DateTimePicker.SetValue(dateObj.add(1, 'days').format(format));
|
||||
}
|
||||
else if (e.keyCode === 37) // Left
|
||||
{
|
||||
Grocy.Components.DateTimePicker.SetValue(dateObj.add(-1, 'weeks').format(format));
|
||||
}
|
||||
else if (e.keyCode === 39) // Right
|
||||
{
|
||||
Grocy.Components.DateTimePicker.SetValue(dateObj.add(1, 'weeks').format(format));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Custom validation
|
||||
value = Grocy.Components.DateTimePicker.GetValue();
|
||||
dateObj = moment(value, format, true);
|
||||
var element = Grocy.Components.DateTimePicker.GetInputElement()[0];
|
||||
if (!dateObj.isValid())
|
||||
{
|
||||
if ($(element).hasAttr("required"))
|
||||
{
|
||||
element.setCustomValidity("error");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Grocy.Components.DateTimePicker.GetInputElement().data('limit-end-to-now') === true && dateObj.isAfter(moment()))
|
||||
{
|
||||
element.setCustomValidity("error");
|
||||
}
|
||||
else if (Grocy.Components.DateTimePicker.GetInputElement().data('limit-start-to-now') === true && dateObj.isBefore(moment()))
|
||||
{
|
||||
element.setCustomValidity("error");
|
||||
}
|
||||
else
|
||||
{
|
||||
element.setCustomValidity("");
|
||||
}
|
||||
|
||||
var earlierThanLimit = Grocy.Components.DateTimePicker.GetInputElement().data("earlier-than-limit");
|
||||
if (!earlierThanLimit.isEmpty())
|
||||
{
|
||||
if (moment(value).isBefore(moment(earlierThanLimit)))
|
||||
{
|
||||
$("#datetimepicker-earlier-than-info").removeClass("d-none");
|
||||
}
|
||||
else
|
||||
{
|
||||
$("#datetimepicker-earlier-than-info").addClass("d-none");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// "Click" the shortcut checkbox when the shortcut value was
|
||||
// entered manually and it is currently not set
|
||||
var shortcutValue = $("#datetimepicker-shortcut").data("datetimepicker-shortcut-value");
|
||||
if (value == shortcutValue && !$("#datetimepicker-shortcut").is(":checked"))
|
||||
{
|
||||
$("#datetimepicker-shortcut").click();
|
||||
}
|
||||
});
|
||||
|
||||
Grocy.Components.DateTimePicker.GetInputElement().on('input', function(e)
|
||||
{
|
||||
$('#datetimepicker-timeago').attr("datetime", Grocy.Components.DateTimePicker.GetValue());
|
||||
EmptyElementWhenMatches('#datetimepicker-timeago', __t('timeago_nan'));
|
||||
RefreshContextualTimeago("#datetimepicker-wrapper");
|
||||
});
|
||||
|
||||
$('.datetimepicker').on('update.datetimepicker', function(e)
|
||||
{
|
||||
Grocy.Components.DateTimePicker.GetInputElement().trigger('input');
|
||||
Grocy.Components.DateTimePicker.GetInputElement().trigger('change');
|
||||
Grocy.Components.DateTimePicker.GetInputElement().trigger('keypress');
|
||||
Grocy.Components.DateTimePicker.GetInputElement().trigger('keyup');
|
||||
});
|
||||
|
||||
$('.datetimepicker').on('hide.datetimepicker', function(e)
|
||||
{
|
||||
Grocy.Components.DateTimePicker.GetInputElement().trigger('input');
|
||||
Grocy.Components.DateTimePicker.GetInputElement().trigger('change');
|
||||
Grocy.Components.DateTimePicker.GetInputElement().trigger('keypress');
|
||||
Grocy.Components.DateTimePicker.GetInputElement().trigger('keyup');
|
||||
});
|
||||
|
||||
$("#datetimepicker-shortcut").on("click", function()
|
||||
{
|
||||
if (this.checked)
|
||||
{
|
||||
var value = $("#datetimepicker-shortcut").data("datetimepicker-shortcut-value");
|
||||
Grocy.Components.DateTimePicker.SetValue(value);
|
||||
Grocy.Components.DateTimePicker.GetInputElement().attr("readonly", "");
|
||||
$(Grocy.Components.DateTimePicker.GetInputElement().data('next-input-selector')).focus();
|
||||
}
|
||||
else
|
||||
{
|
||||
Grocy.Components.DateTimePicker.SetValue("");
|
||||
Grocy.Components.DateTimePicker.GetInputElement().removeAttr("readonly");
|
||||
Grocy.Components.DateTimePicker.GetInputElement().focus();
|
||||
}
|
||||
|
||||
Grocy.Components.DateTimePicker.GetInputElement().trigger('input');
|
||||
Grocy.Components.DateTimePicker.GetInputElement().trigger('change');
|
||||
Grocy.Components.DateTimePicker.GetInputElement().trigger('keypress');
|
||||
});
|
||||
|
|
@ -1,309 +0,0 @@
|
|||
Grocy.Components.DateTimePicker2 = {};
|
||||
|
||||
Grocy.Components.DateTimePicker2.GetInputElement = function()
|
||||
{
|
||||
return $('.datetimepicker2').find('input').not(".form-check-input");
|
||||
}
|
||||
|
||||
Grocy.Components.DateTimePicker2.GetValue = function()
|
||||
{
|
||||
return Grocy.Components.DateTimePicker2.GetInputElement().val();
|
||||
}
|
||||
|
||||
Grocy.Components.DateTimePicker2.SetValue = function(value)
|
||||
{
|
||||
// "Click" the shortcut checkbox when the desired value is
|
||||
// not the shortcut value and it is currently set
|
||||
var shortcutValue = $("#datetimepicker2-shortcut").data("datetimepicker2-shortcut-value");
|
||||
if (value != shortcutValue && $("#datetimepicker2-shortcut").is(":checked"))
|
||||
{
|
||||
$("#datetimepicker2-shortcut").click();
|
||||
}
|
||||
|
||||
Grocy.Components.DateTimePicker2.GetInputElement().val(value);
|
||||
Grocy.Components.DateTimePicker2.GetInputElement().trigger('change');
|
||||
|
||||
Grocy.Components.DateTimePicker2.GetInputElement().keyup();
|
||||
}
|
||||
|
||||
Grocy.Components.DateTimePicker2.Clear = function()
|
||||
{
|
||||
$(".datetimepicker2").datetimepicker("destroy");
|
||||
Grocy.Components.DateTimePicker2.Init();
|
||||
|
||||
Grocy.Components.DateTimePicker2.GetInputElement().val("");
|
||||
|
||||
// "Click" the shortcut checkbox when the desired value is
|
||||
// not the shortcut value and it is currently set
|
||||
var value = "";
|
||||
var shortcutValue = $("#datetimepicker2-shortcut").data("datetimepicker2-shortcut-value");
|
||||
if (value != shortcutValue && $("#datetimepicker2-shortcut").is(":checked"))
|
||||
{
|
||||
$("#datetimepicker2-shortcut").click();
|
||||
}
|
||||
|
||||
$('#datetimepicker2-timeago').text('');
|
||||
}
|
||||
|
||||
Grocy.Components.DateTimePicker2.ChangeFormat = function(format)
|
||||
{
|
||||
$(".datetimepicker2").datetimepicker("destroy");
|
||||
Grocy.Components.DateTimePicker2.GetInputElement().data("format", format);
|
||||
Grocy.Components.DateTimePicker2.Init();
|
||||
|
||||
if (format == "YYYY-MM-DD")
|
||||
{
|
||||
Grocy.Components.DateTimePicker2.GetInputElement().addClass("date-only-datetimepicker");
|
||||
}
|
||||
else
|
||||
{
|
||||
Grocy.Components.DateTimePicker2.GetInputElement().removeClass("date-only-datetimepicker");
|
||||
}
|
||||
}
|
||||
|
||||
var startDate = null;
|
||||
if (Grocy.Components.DateTimePicker2.GetInputElement().data('init-with-now') === true)
|
||||
{
|
||||
startDate = moment().format(Grocy.Components.DateTimePicker2.GetInputElement().data('format'));
|
||||
}
|
||||
if (Grocy.Components.DateTimePicker2.GetInputElement().data('init-value').length > 0)
|
||||
{
|
||||
startDate = moment(Grocy.Components.DateTimePicker2.GetInputElement().data('init-value')).format(Grocy.Components.DateTimePicker2.GetInputElement().data('format'));
|
||||
}
|
||||
|
||||
var limitDate = moment('2999-12-31 23:59:59');
|
||||
if (Grocy.Components.DateTimePicker2.GetInputElement().data('limit-end-to-now') === true)
|
||||
{
|
||||
limitDate = moment();
|
||||
}
|
||||
|
||||
Grocy.Components.DateTimePicker2.Init = function()
|
||||
{
|
||||
$('.datetimepicker2').datetimepicker(
|
||||
{
|
||||
format: Grocy.Components.DateTimePicker2.GetInputElement().data('format'),
|
||||
buttons: {
|
||||
showToday: true,
|
||||
showClose: true
|
||||
},
|
||||
calendarWeeks: Grocy.CalendarShowWeekNumbers,
|
||||
maxDate: limitDate,
|
||||
locale: moment.locale(),
|
||||
defaultDate: startDate,
|
||||
useCurrent: false,
|
||||
icons: {
|
||||
time: 'far fa-clock',
|
||||
date: 'far fa-calendar',
|
||||
up: 'fas fa-arrow-up',
|
||||
down: 'fas fa-arrow-down',
|
||||
previous: 'fas fa-chevron-left',
|
||||
next: 'fas fa-chevron-right',
|
||||
today: 'fas fa-calendar-check',
|
||||
clear: 'far fa-trash-alt',
|
||||
close: 'far fa-times-circle'
|
||||
},
|
||||
sideBySide: true,
|
||||
keyBinds: {
|
||||
up: function(widget) { },
|
||||
down: function(widget) { },
|
||||
'control up': function(widget) { },
|
||||
'control down': function(widget) { },
|
||||
left: function(widget) { },
|
||||
right: function(widget) { },
|
||||
pageUp: function(widget) { },
|
||||
pageDown: function(widget) { },
|
||||
enter: function(widget) { },
|
||||
escape: function(widget) { },
|
||||
'control space': function(widget) { },
|
||||
t: function(widget) { },
|
||||
'delete': function(widget) { }
|
||||
}
|
||||
});
|
||||
}
|
||||
Grocy.Components.DateTimePicker2.Init();
|
||||
|
||||
Grocy.Components.DateTimePicker2.GetInputElement().on('keyup', function(e)
|
||||
{
|
||||
$('.datetimepicker2').datetimepicker('hide');
|
||||
|
||||
var value = Grocy.Components.DateTimePicker2.GetValue();
|
||||
var now = new Date();
|
||||
var centuryStart = Number.parseInt(now.getFullYear().toString().substring(0, 2) + '00');
|
||||
var centuryEnd = Number.parseInt(now.getFullYear().toString().substring(0, 2) + '99');
|
||||
var format = Grocy.Components.DateTimePicker2.GetInputElement().data('format');
|
||||
var nextInputElement = $(Grocy.Components.DateTimePicker2.GetInputElement().data('next-input-selector'));
|
||||
|
||||
//If input is empty and any arrow key is pressed, set date to today
|
||||
if (value.length === 0 && (e.keyCode === 38 || e.keyCode === 40 || e.keyCode === 37 || e.keyCode === 39))
|
||||
{
|
||||
Grocy.Components.DateTimePicker2.SetValue(moment(new Date(), format, true).format(format));
|
||||
nextInputElement.focus();
|
||||
}
|
||||
else if (value === 'x' || value === 'X')
|
||||
{
|
||||
Grocy.Components.DateTimePicker2.SetValue(moment('2999-12-31 23:59:59').format(format));
|
||||
nextInputElement.focus();
|
||||
}
|
||||
else if (value.length === 4 && !(Number.parseInt(value) > centuryStart && Number.parseInt(value) < centuryEnd))
|
||||
{
|
||||
var date = moment((new Date()).getFullYear().toString() + value);
|
||||
if (date.isBefore(moment()))
|
||||
{
|
||||
date.add(1, "year");
|
||||
}
|
||||
Grocy.Components.DateTimePicker2.SetValue(date.format(format));
|
||||
nextInputElement.focus();
|
||||
}
|
||||
else if (value.length === 8 && $.isNumeric(value))
|
||||
{
|
||||
Grocy.Components.DateTimePicker2.SetValue(value.replace(/(\d{4})(\d{2})(\d{2})/, '$1-$2-$3'));
|
||||
nextInputElement.focus();
|
||||
}
|
||||
else if (value.length === 7 && $.isNumeric(value.substring(0, 6)) && (value.substring(6, 7).toLowerCase() === "e" || value.substring(6, 7).toLowerCase() === "+"))
|
||||
{
|
||||
var endOfMonth = moment(value.substring(0, 4) + "-" + value.substring(4, 6) + "-01").endOf("month");
|
||||
Grocy.Components.DateTimePicker2.SetValue(endOfMonth.format(format));
|
||||
nextInputElement.focus();
|
||||
}
|
||||
else
|
||||
{
|
||||
var dateObj = moment(value, format, true);
|
||||
if (dateObj.isValid())
|
||||
{
|
||||
if (e.shiftKey)
|
||||
{
|
||||
// WITH shift modifier key
|
||||
|
||||
if (e.keyCode === 38) // Up
|
||||
{
|
||||
Grocy.Components.DateTimePicker2.SetValue(dateObj.add(-1, 'months').format(format));
|
||||
}
|
||||
else if (e.keyCode === 40) // Down
|
||||
{
|
||||
Grocy.Components.DateTimePicker2.SetValue(dateObj.add(1, 'months').format(format));
|
||||
}
|
||||
else if (e.keyCode === 37) // Left
|
||||
{
|
||||
Grocy.Components.DateTimePicker2.SetValue(dateObj.add(-1, 'years').format(format));
|
||||
}
|
||||
else if (e.keyCode === 39) // Right
|
||||
{
|
||||
Grocy.Components.DateTimePicker2.SetValue(dateObj.add(1, 'years').format(format));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// WITHOUT shift modifier key
|
||||
|
||||
if (e.keyCode === 38) // Up
|
||||
{
|
||||
Grocy.Components.DateTimePicker2.SetValue(dateObj.add(-1, 'days').format(format));
|
||||
}
|
||||
else if (e.keyCode === 40) // Down
|
||||
{
|
||||
Grocy.Components.DateTimePicker2.SetValue(dateObj.add(1, 'days').format(format));
|
||||
}
|
||||
else if (e.keyCode === 37) // Left
|
||||
{
|
||||
Grocy.Components.DateTimePicker2.SetValue(dateObj.add(-1, 'weeks').format(format));
|
||||
}
|
||||
else if (e.keyCode === 39) // Right
|
||||
{
|
||||
Grocy.Components.DateTimePicker2.SetValue(dateObj.add(1, 'weeks').format(format));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Custom validation
|
||||
value = Grocy.Components.DateTimePicker2.GetValue();
|
||||
dateObj = moment(value, format, true);
|
||||
var element = Grocy.Components.DateTimePicker2.GetInputElement()[0];
|
||||
if (!dateObj.isValid())
|
||||
{
|
||||
if ($(element).hasAttr("required"))
|
||||
{
|
||||
element.setCustomValidity("error");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Grocy.Components.DateTimePicker2.GetInputElement().data('limit-end-to-now') === true && dateObj.isAfter(moment()))
|
||||
{
|
||||
element.setCustomValidity("error");
|
||||
}
|
||||
else if (Grocy.Components.DateTimePicker2.GetInputElement().data('limit-start-to-now') === true && dateObj.isBefore(moment()))
|
||||
{
|
||||
element.setCustomValidity("error");
|
||||
}
|
||||
else
|
||||
{
|
||||
element.setCustomValidity("");
|
||||
}
|
||||
|
||||
var earlierThanLimit = Grocy.Components.DateTimePicker2.GetInputElement().data("earlier-than-limit");
|
||||
if (!earlierThanLimit.isEmpty())
|
||||
{
|
||||
if (moment(value).isBefore(moment(earlierThanLimit)))
|
||||
{
|
||||
$("#datetimepicker-earlier-than-info").removeClass("d-none");
|
||||
}
|
||||
else
|
||||
{
|
||||
$("#datetimepicker-earlier-than-info").addClass("d-none");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// "Click" the shortcut checkbox when the shortcut value was
|
||||
// entered manually and it is currently not set
|
||||
var shortcutValue = $("#datetimepicker2-shortcut").data("datetimepicker2-shortcut-value");
|
||||
if (value == shortcutValue && !$("#datetimepicker2-shortcut").is(":checked"))
|
||||
{
|
||||
$("#datetimepicker2-shortcut").click();
|
||||
}
|
||||
});
|
||||
|
||||
Grocy.Components.DateTimePicker2.GetInputElement().on('input', function(e)
|
||||
{
|
||||
$('#datetimepicker2-timeago').attr("datetime", Grocy.Components.DateTimePicker2.GetValue());
|
||||
EmptyElementWhenMatches('#datetimepicker2-timeago', __t('timeago_nan'));
|
||||
RefreshContextualTimeago("#datetimepicker2-wrapper");
|
||||
});
|
||||
|
||||
$('.datetimepicker2').on('update.datetimepicker', function(e)
|
||||
{
|
||||
Grocy.Components.DateTimePicker2.GetInputElement().trigger('input');
|
||||
Grocy.Components.DateTimePicker2.GetInputElement().trigger('change');
|
||||
Grocy.Components.DateTimePicker2.GetInputElement().trigger('keypress');
|
||||
Grocy.Components.DateTimePicker2.GetInputElement().trigger('keyup');
|
||||
});
|
||||
|
||||
$('.datetimepicker2').on('hide.datetimepicker', function(e)
|
||||
{
|
||||
Grocy.Components.DateTimePicker2.GetInputElement().trigger('input');
|
||||
Grocy.Components.DateTimePicker2.GetInputElement().trigger('change');
|
||||
Grocy.Components.DateTimePicker2.GetInputElement().trigger('keypress');
|
||||
Grocy.Components.DateTimePicker2.GetInputElement().trigger('keyup');
|
||||
});
|
||||
|
||||
$("#datetimepicker2-shortcut").on("click", function()
|
||||
{
|
||||
if (this.checked)
|
||||
{
|
||||
var value = $("#datetimepicker2-shortcut").data("datetimepicker2-shortcut-value");
|
||||
Grocy.Components.DateTimePicker2.SetValue(value);
|
||||
Grocy.Components.DateTimePicker2.GetInputElement().attr("readonly", "");
|
||||
$(Grocy.Components.DateTimePicker2.GetInputElement().data('next-input-selector')).focus();
|
||||
}
|
||||
else
|
||||
{
|
||||
Grocy.Components.DateTimePicker2.SetValue("");
|
||||
Grocy.Components.DateTimePicker2.GetInputElement().removeAttr("readonly");
|
||||
Grocy.Components.DateTimePicker2.GetInputElement().focus();
|
||||
}
|
||||
|
||||
Grocy.Components.DateTimePicker2.GetInputElement().trigger('input');
|
||||
Grocy.Components.DateTimePicker2.GetInputElement().trigger('change');
|
||||
Grocy.Components.DateTimePicker2.GetInputElement().trigger('keypress');
|
||||
});
|
||||
|
|
@ -1,74 +0,0 @@
|
|||
Grocy.Components.LocationPicker = {};
|
||||
|
||||
Grocy.Components.LocationPicker.GetPicker = function()
|
||||
{
|
||||
return $('#location_id');
|
||||
}
|
||||
|
||||
Grocy.Components.LocationPicker.GetInputElement = function()
|
||||
{
|
||||
return $('#location_id_text_input');
|
||||
}
|
||||
|
||||
Grocy.Components.LocationPicker.GetValue = function()
|
||||
{
|
||||
return $('#location_id').val();
|
||||
}
|
||||
|
||||
Grocy.Components.LocationPicker.SetValue = function(value)
|
||||
{
|
||||
Grocy.Components.LocationPicker.GetInputElement().val(value);
|
||||
Grocy.Components.LocationPicker.GetInputElement().trigger('change');
|
||||
}
|
||||
|
||||
Grocy.Components.LocationPicker.SetId = function(value)
|
||||
{
|
||||
Grocy.Components.LocationPicker.GetPicker().val(value);
|
||||
Grocy.Components.LocationPicker.GetPicker().data('combobox').refresh();
|
||||
Grocy.Components.LocationPicker.GetInputElement().trigger('change');
|
||||
}
|
||||
|
||||
Grocy.Components.LocationPicker.Clear = function()
|
||||
{
|
||||
Grocy.Components.LocationPicker.SetValue('');
|
||||
Grocy.Components.LocationPicker.SetId(null);
|
||||
}
|
||||
|
||||
$('.location-combobox').combobox({
|
||||
appendId: '_text_input',
|
||||
bsVersion: '4',
|
||||
clearIfNoMatch: true
|
||||
});
|
||||
|
||||
// these names seem a bit long, but as they live in global space
|
||||
// and this is a component, they need to be unique.
|
||||
var locationpicker_doFocus = false;
|
||||
var this_location_picker = Grocy.Components.LocationPicker.GetPicker();
|
||||
|
||||
var prefillByName = this_location_picker.parent().data('prefill-by-name').toString();
|
||||
if (typeof prefillByName !== "undefined")
|
||||
{
|
||||
var possibleOptionElement = $("#location_id option:contains(\"" + prefillByName + "\")").first();
|
||||
|
||||
if (possibleOptionElement.length > 0)
|
||||
{
|
||||
locationpicker_doFocus = true;
|
||||
this_location_picker.val(possibleOptionElement.val());
|
||||
}
|
||||
}
|
||||
|
||||
var prefillById = this_location_picker.parent().data('prefill-by-id').toString();
|
||||
if (typeof prefillById !== "undefined")
|
||||
{
|
||||
locationpicker_doFocus = true;
|
||||
this_location_picker.val(prefillById);
|
||||
}
|
||||
|
||||
if (locationpicker_doFocus)
|
||||
{
|
||||
this_location_picker.data('combobox').refresh();
|
||||
this_location_picker.trigger('change');
|
||||
|
||||
$(this_location_picker.parent().data('next-input-selector').toString())
|
||||
.focus();
|
||||
}
|
||||
|
|
@ -1,94 +0,0 @@
|
|||
$(".numberpicker-down-button").unbind('click').on("click", function()
|
||||
{
|
||||
var inputElement = $(this).parent().parent().find('input[type="number"]');
|
||||
inputElement.val(parseFloat(inputElement.val() || 1) - 1);
|
||||
inputElement.trigger('keyup');
|
||||
inputElement.trigger('change');
|
||||
});
|
||||
|
||||
$(".numberpicker-up-button").unbind('click').on("click", function()
|
||||
{
|
||||
var inputElement = $(this).parent().parent().find('input[type="number"]');
|
||||
inputElement.val(parseFloat(inputElement.val() || 0) + 1);
|
||||
inputElement.trigger('keyup');
|
||||
inputElement.trigger('change');
|
||||
});
|
||||
|
||||
$(".numberpicker").on("keyup", function()
|
||||
{
|
||||
if ($(this).attr("data-not-equal") && !$(this).attr("data-not-equal").toString().isEmpty() && $(this).attr("data-not-equal") == $(this).val())
|
||||
{
|
||||
$(this)[0].setCustomValidity("error");
|
||||
}
|
||||
else
|
||||
{
|
||||
$(this)[0].setCustomValidity("");
|
||||
}
|
||||
});
|
||||
|
||||
$(".numberpicker").each(function()
|
||||
{
|
||||
new MutationObserver(function(mutations)
|
||||
{
|
||||
mutations.forEach(function(mutation)
|
||||
{
|
||||
if (mutation.type == "attributes" && (mutation.attributeName == "min" || mutation.attributeName == "max" || mutation.attributeName == "data-not-equal" || mutation.attributeName == "data-initialised"))
|
||||
{
|
||||
var element = $(mutation.target);
|
||||
var min = element.attr("min");
|
||||
var decimals = element.attr("data-decimals");
|
||||
|
||||
var max = "";
|
||||
if (element.hasAttr("max"))
|
||||
{
|
||||
max = element.attr("max");
|
||||
}
|
||||
|
||||
if (element.hasAttr("data-not-equal"))
|
||||
{
|
||||
var notEqual = element.attr("data-not-equal");
|
||||
|
||||
if (notEqual != "NaN")
|
||||
{
|
||||
if (max.isEmpty())
|
||||
{
|
||||
element.parent().find(".invalid-feedback").text(__t("This cannot be lower than %1$s or equal %2$s and needs to be a valid number with max. %3$s decimal places", parseFloat(min).toLocaleString(undefined, { minimumFractionDigits: 0, maximumFractionDigits: decimals }), parseFloat(notEqual).toLocaleString(undefined, { minimumFractionDigits: 0, maximumFractionDigits: decimals }), decimals));
|
||||
}
|
||||
else
|
||||
{
|
||||
element.parent().find(".invalid-feedback").text(__t("This must be between %1$s and %2$s, cannot equal %3$s and needs to be a valid number with max. %4$s decimal places", parseFloat(min).toLocaleString(undefined, { minimumFractionDigits: 0, maximumFractionDigits: decimals }), parseFloat(max).toLocaleString(undefined, { minimumFractionDigits: 0, maximumFractionDigits: decimals }), parseFloat(notEqual).toLocaleString(undefined, { minimumFractionDigits: decimals, maximumFractionDigits: decimals }), decimals));
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (max.isEmpty())
|
||||
{
|
||||
element.parent().find(".invalid-feedback").text(__t("This cannot be lower than %1$s and needs to be a valid number with max. %2$s decimal places", parseFloat(min).toLocaleString(undefined, { minimumFractionDigits: 0, maximumFractionDigits: decimals }), decimals));
|
||||
}
|
||||
else
|
||||
{
|
||||
element.parent().find(".invalid-feedback").text(__t("This must between %1$s and %2$s and needs to be a valid number with max. %3$s decimal places", parseFloat(min).toLocaleString(undefined, { minimumFractionDigits: 0, maximumFractionDigits: decimals }), parseFloat(max).toLocaleString(undefined, { minimumFractionDigits: 0, maximumFractionDigits: decimals }), decimals));
|
||||
}
|
||||
}
|
||||
});
|
||||
}).observe(this, {
|
||||
attributes: true
|
||||
});
|
||||
});
|
||||
$(".numberpicker").attr("data-initialised", "true"); // Dummy change to trigger MutationObserver above once
|
||||
|
||||
$(".numberpicker").on("keydown", function(e)
|
||||
{
|
||||
if (e.key == "ArrowUp")
|
||||
{
|
||||
e.preventDefault();
|
||||
$(this).parent().find(".numberpicker-up-button").click();
|
||||
}
|
||||
else if (e.key == "ArrowDown")
|
||||
{
|
||||
e.preventDefault();
|
||||
$(this).parent().find(".numberpicker-down-button").click();
|
||||
}
|
||||
});
|
||||
|
|
@ -1,116 +0,0 @@
|
|||
Grocy.Components.ProductAmountPicker = {};
|
||||
Grocy.Components.ProductAmountPicker.AllowAnyQuEnabled = false;
|
||||
|
||||
Grocy.Components.ProductAmountPicker.Reload = function(productId, destinationQuId, forceInitialDisplayQu = false)
|
||||
{
|
||||
var conversionsForProduct = Grocy.QuantityUnitConversionsResolved.filter(elem => elem.product_id == productId);
|
||||
|
||||
if (!Grocy.Components.ProductAmountPicker.AllowAnyQuEnabled)
|
||||
{
|
||||
var qu = Grocy.QuantityUnits.find(elem => elem.id == destinationQuId);
|
||||
$("#qu_id").find("option").remove().end();
|
||||
$("#qu_id").attr("data-destination-qu-name", qu.name);
|
||||
$("#qu_id").attr("data-destination-qu-name-plural", qu.name_plural);
|
||||
|
||||
conversionsForProduct.forEach(conversion =>
|
||||
{
|
||||
var factor = parseFloat(conversion.factor);
|
||||
if (conversion.to_qu_id == destinationQuId)
|
||||
{
|
||||
factor = 1;
|
||||
}
|
||||
|
||||
if (!$('#qu_id option[value="' + conversion.to_qu_id + '"]').length) // Don't add the destination QU multiple times
|
||||
{
|
||||
$("#qu_id").append('<option value="' + conversion.to_qu_id + '" data-qu-factor="' + factor + '">' + conversion.to_qu_name + '</option>');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (!Grocy.Components.ProductAmountPicker.InitialValueSet || forceInitialDisplayQu)
|
||||
{
|
||||
$("#qu_id").val($("#qu_id").attr("data-initial-qu-id"));
|
||||
}
|
||||
|
||||
if (!Grocy.Components.ProductAmountPicker.InitialValueSet)
|
||||
{
|
||||
var convertedAmount = $("#display_amount").val() * $("#qu_id option:selected").attr("data-qu-factor");
|
||||
$("#display_amount").val(convertedAmount);
|
||||
|
||||
Grocy.Components.ProductAmountPicker.InitialValueSet = true;
|
||||
}
|
||||
|
||||
if (conversionsForProduct.length === 1 && !forceInitialDisplayQu)
|
||||
{
|
||||
$("#qu_id").val($("#qu_id option:first").val());
|
||||
}
|
||||
|
||||
if ($('#qu_id option').length == 1)
|
||||
{
|
||||
$("#qu_id").attr("disabled", "");
|
||||
}
|
||||
else
|
||||
{
|
||||
$("#qu_id").removeAttr("disabled");
|
||||
}
|
||||
|
||||
$(".input-group-productamountpicker").trigger("change");
|
||||
}
|
||||
|
||||
Grocy.Components.ProductAmountPicker.SetQuantityUnit = function(quId)
|
||||
{
|
||||
$("#qu_id").val(quId);
|
||||
}
|
||||
|
||||
Grocy.Components.ProductAmountPicker.AllowAnyQu = function(keepInitialQu = false)
|
||||
{
|
||||
Grocy.Components.ProductAmountPicker.AllowAnyQuEnabled = true;
|
||||
|
||||
$("#qu_id").find("option").remove().end();
|
||||
Grocy.QuantityUnits.forEach(qu =>
|
||||
{
|
||||
$("#qu_id").append('<option value="' + qu.id + '" data-qu-factor="1">' + qu.name + '</option>');
|
||||
});
|
||||
|
||||
if (keepInitialQu)
|
||||
{
|
||||
Grocy.Components.ProductAmountPicker.SetQuantityUnit($("#qu_id").attr("data-initial-qu-id"));
|
||||
}
|
||||
|
||||
$("#qu_id").removeAttr("disabled");
|
||||
|
||||
$(".input-group-productamountpicker").trigger("change");
|
||||
}
|
||||
|
||||
Grocy.Components.ProductAmountPicker.Reset = function()
|
||||
{
|
||||
$("#qu_id").find("option").remove();
|
||||
$("#qu-conversion-info").addClass("d-none");
|
||||
$("#qu-display_amount-info").val("");
|
||||
}
|
||||
|
||||
$(".input-group-productamountpicker").on("change", function()
|
||||
{
|
||||
var selectedQuName = $("#qu_id option:selected").text();
|
||||
var quFactor = $("#qu_id option:selected").attr("data-qu-factor");
|
||||
var amount = $("#display_amount").val();
|
||||
var destinationAmount = amount / quFactor;
|
||||
var destinationQuName = __n(destinationAmount, $("#qu_id").attr("data-destination-qu-name"), $("#qu_id").attr("data-destination-qu-name-plural"))
|
||||
|
||||
if ($("#qu_id").attr("data-destination-qu-name") == selectedQuName || Grocy.Components.ProductAmountPicker.AllowAnyQuEnabled || amount.toString().isEmpty() || selectedQuName.toString().isEmpty())
|
||||
{
|
||||
$("#qu-conversion-info").addClass("d-none");
|
||||
}
|
||||
else
|
||||
{
|
||||
$("#qu-conversion-info").removeClass("d-none");
|
||||
$("#qu-conversion-info").text(__t("This equals %1$s %2$s", destinationAmount.toLocaleString({ minimumFractionDigits: 0, maximumFractionDigits: Grocy.UserSettings.stock_decimal_places_amounts }), destinationQuName));
|
||||
}
|
||||
|
||||
$("#amount").val(destinationAmount.toFixed(Grocy.UserSettings.stock_decimal_places_amounts).replace(/0*$/g, ''));
|
||||
});
|
||||
|
||||
$("#display_amount").on("keyup", function()
|
||||
{
|
||||
$(".input-group-productamountpicker").trigger("change");
|
||||
});
|
||||
|
|
@ -1,237 +0,0 @@
|
|||
import Chart from 'chart.js';
|
||||
|
||||
Grocy.Components.ProductCard = {};
|
||||
|
||||
Grocy.Components.ProductCard.Refresh = function(productId)
|
||||
{
|
||||
Grocy.Api.Get('stock/products/' + productId,
|
||||
function(productDetails)
|
||||
{
|
||||
var stockAmount = productDetails.stock_amount || '0';
|
||||
var stockValue = productDetails.stock_value || '0';
|
||||
var stockAmountOpened = productDetails.stock_amount_opened || '0';
|
||||
$('#productcard-product-name').text(productDetails.product.name);
|
||||
$('#productcard-product-description').html(productDetails.product.description);
|
||||
$('#productcard-product-stock-amount').text(stockAmount);
|
||||
$('#productcard-product-stock-qu-name').text(__n(stockAmount, productDetails.quantity_unit_stock.name, productDetails.quantity_unit_stock.name_plural));
|
||||
$('#productcard-product-stock-value').text(stockValue + ' ' + Grocy.Currency);
|
||||
$('#productcard-product-last-purchased').text((productDetails.last_purchased || '2999-12-31').substring(0, 10));
|
||||
$('#productcard-product-last-purchased-timeago').attr("datetime", productDetails.last_purchased || '2999-12-31');
|
||||
$('#productcard-product-last-used').text((productDetails.last_used || '2999-12-31').substring(0, 10));
|
||||
$('#productcard-product-last-used-timeago').attr("datetime", productDetails.last_used || '2999-12-31');
|
||||
if (productDetails.location != null)
|
||||
{
|
||||
$('#productcard-product-location').text(productDetails.location.name);
|
||||
}
|
||||
$('#productcard-product-spoil-rate').text((parseFloat(productDetails.spoil_rate_percent) / 100).toLocaleString(undefined, { style: "percent" }));
|
||||
|
||||
if (productDetails.is_aggregated_amount == 1)
|
||||
{
|
||||
$('#productcard-product-stock-amount-aggregated').text(productDetails.stock_amount_aggregated);
|
||||
$('#productcard-product-stock-qu-name-aggregated').text(__n(productDetails.stock_amount_aggregated, productDetails.quantity_unit_stock.name, productDetails.quantity_unit_stock.name_plural));
|
||||
|
||||
if (productDetails.stock_amount_opened_aggregated > 0)
|
||||
{
|
||||
$('#productcard-product-stock-opened-amount-aggregated').text(__t('%s opened', productDetails.stock_amount_opened_aggregated));
|
||||
}
|
||||
else
|
||||
{
|
||||
$('#productcard-product-stock-opened-amount-aggregated').text("");
|
||||
}
|
||||
|
||||
$("#productcard-aggregated-amounts").removeClass("d-none");
|
||||
}
|
||||
else
|
||||
{
|
||||
$("#productcard-aggregated-amounts").addClass("d-none");
|
||||
}
|
||||
|
||||
if (productDetails.product.description != null && !productDetails.product.description.isEmpty())
|
||||
{
|
||||
$("#productcard-product-description-wrapper").removeClass("d-none");
|
||||
}
|
||||
else
|
||||
{
|
||||
$("#productcard-product-description-wrapper").addClass("d-none");
|
||||
}
|
||||
|
||||
if (productDetails.average_shelf_life_days == -1)
|
||||
{
|
||||
$('#productcard-product-average-shelf-life').text(__t("Unknown"));
|
||||
}
|
||||
else if (parseInt(productDetails.average_shelf_life_days) > 73000) // > 200 years aka forever
|
||||
{
|
||||
$('#productcard-product-average-shelf-life').text(__t("Unlimited"));
|
||||
}
|
||||
else
|
||||
{
|
||||
$('#productcard-product-average-shelf-life').text(moment.duration(productDetails.average_shelf_life_days, "days").humanize());
|
||||
}
|
||||
|
||||
if (stockAmountOpened > 0)
|
||||
{
|
||||
$('#productcard-product-stock-opened-amount').text(__t('%s opened', stockAmountOpened));
|
||||
}
|
||||
else
|
||||
{
|
||||
$('#productcard-product-stock-opened-amount').text("");
|
||||
}
|
||||
|
||||
$('#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-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");
|
||||
|
||||
if (productDetails.last_price !== null)
|
||||
{
|
||||
$('#productcard-product-last-price').text(Number.parseFloat(productDetails.last_price).toLocaleString() + ' ' + Grocy.Currency + ' per ' + productDetails.quantity_unit_stock.name);
|
||||
}
|
||||
else
|
||||
{
|
||||
$('#productcard-product-last-price').text(__t('Unknown'));
|
||||
}
|
||||
|
||||
if (productDetails.avg_price !== null)
|
||||
{
|
||||
$('#productcard-product-average-price').text(Number.parseFloat(productDetails.avg_price).toLocaleString() + ' ' + Grocy.Currency + ' per ' + productDetails.quantity_unit_stock.name);
|
||||
}
|
||||
else
|
||||
{
|
||||
$('#productcard-product-average-price').text(__t('Unknown'));
|
||||
}
|
||||
|
||||
if (productDetails.product.picture_file_name !== null && !productDetails.product.picture_file_name.isEmpty())
|
||||
{
|
||||
$("#productcard-product-picture").removeClass("d-none");
|
||||
$("#productcard-product-picture").attr("src", U('/api/files/productpictures/' + btoa(productDetails.product.picture_file_name) + '?force_serve_as=picture&best_fit_width=400'));
|
||||
}
|
||||
else
|
||||
{
|
||||
$("#productcard-product-picture").addClass("d-none");
|
||||
}
|
||||
|
||||
EmptyElementWhenMatches('#productcard-product-last-purchased-timeago', __t('timeago_nan'));
|
||||
EmptyElementWhenMatches('#productcard-product-last-used-timeago', __t('timeago_nan'));
|
||||
RefreshContextualTimeago(".productcard");
|
||||
},
|
||||
function(xhr)
|
||||
{
|
||||
console.error(xhr);
|
||||
}
|
||||
);
|
||||
|
||||
if (Grocy.FeatureFlags.GROCY_FEATURE_FLAG_STOCK_PRICE_TRACKING)
|
||||
{
|
||||
Grocy.Api.Get('stock/products/' + productId + '/price-history',
|
||||
function(priceHistoryDataPoints)
|
||||
{
|
||||
if (priceHistoryDataPoints.length > 0)
|
||||
{
|
||||
$("#productcard-product-price-history-chart").removeClass("d-none");
|
||||
$("#productcard-no-price-data-hint").addClass("d-none");
|
||||
|
||||
Grocy.Components.ProductCard.ReInitPriceHistoryChart();
|
||||
var datasets = {};
|
||||
var chart = Grocy.Components.ProductCard.PriceHistoryChart.data;
|
||||
priceHistoryDataPoints.forEach((dataPoint) =>
|
||||
{
|
||||
var key = __t("Unknown store");
|
||||
if (dataPoint.shopping_location)
|
||||
{
|
||||
key = dataPoint.shopping_location.name
|
||||
}
|
||||
|
||||
if (!datasets[key])
|
||||
{
|
||||
datasets[key] = []
|
||||
}
|
||||
chart.labels.push(moment(dataPoint.date).toDate());
|
||||
datasets[key].push(dataPoint.price);
|
||||
|
||||
});
|
||||
Object.keys(datasets).forEach((key) =>
|
||||
{
|
||||
chart.datasets.push({
|
||||
data: datasets[key],
|
||||
fill: false,
|
||||
borderColor: "HSL(" + (129 * chart.datasets.length) + ",100%,50%)",
|
||||
label: key,
|
||||
});
|
||||
});
|
||||
Grocy.Components.ProductCard.PriceHistoryChart.update();
|
||||
}
|
||||
else
|
||||
{
|
||||
$("#productcard-product-price-history-chart").addClass("d-none");
|
||||
$("#productcard-no-price-data-hint").removeClass("d-none");
|
||||
}
|
||||
},
|
||||
function(xhr)
|
||||
{
|
||||
console.error(xhr);
|
||||
}
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
Grocy.Components.ProductCard.ReInitPriceHistoryChart = function()
|
||||
{
|
||||
if (typeof Grocy.Components.ProductCard.PriceHistoryChart !== "undefined")
|
||||
{
|
||||
Grocy.Components.ProductCard.PriceHistoryChart.destroy();
|
||||
}
|
||||
|
||||
var format = 'YYYY-MM-DD';
|
||||
Grocy.Components.ProductCard.PriceHistoryChart = new Chart(document.getElementById("productcard-product-price-history-chart"), {
|
||||
type: "line",
|
||||
data: {
|
||||
labels: [ //Date objects
|
||||
// Will be populated in Grocy.Components.ProductCard.Refresh
|
||||
],
|
||||
datasets: [ //Datasets
|
||||
// Will be populated in Grocy.Components.ProductCard.Refresh
|
||||
]
|
||||
},
|
||||
options: {
|
||||
scales: {
|
||||
xAxes: [{
|
||||
type: 'time',
|
||||
time: {
|
||||
parser: format,
|
||||
round: 'day',
|
||||
tooltipFormat: format,
|
||||
unit: 'day',
|
||||
unitStepSize: 10,
|
||||
displayFormats: {
|
||||
'day': format
|
||||
}
|
||||
},
|
||||
ticks: {
|
||||
autoSkip: true,
|
||||
maxRotation: 0
|
||||
}
|
||||
}],
|
||||
yAxes: [{
|
||||
ticks: {
|
||||
beginAtZero: true
|
||||
}
|
||||
}]
|
||||
},
|
||||
legend: {
|
||||
display: true
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
$("#productcard-product-description").on("shown.bs.collapse", function()
|
||||
{
|
||||
$(".expandable-text").find("a[data-toggle='collapse']").text(__t("Show less"));
|
||||
})
|
||||
|
||||
$("#productcard-product-description").on("hidden.bs.collapse", function()
|
||||
{
|
||||
$(".expandable-text").find("a[data-toggle='collapse']").text(__t("Show more"));
|
||||
})
|
||||
|
|
@ -1,331 +0,0 @@
|
|||
Grocy.Components.ProductPicker = {};
|
||||
|
||||
Grocy.Components.ProductPicker.GetPicker = function()
|
||||
{
|
||||
return $('#product_id');
|
||||
}
|
||||
|
||||
Grocy.Components.ProductPicker.GetInputElement = function()
|
||||
{
|
||||
return $('#product_id_text_input');
|
||||
}
|
||||
|
||||
Grocy.Components.ProductPicker.GetValue = function()
|
||||
{
|
||||
return $('#product_id').val();
|
||||
}
|
||||
|
||||
Grocy.Components.ProductPicker.SetValue = function(value)
|
||||
{
|
||||
Grocy.Components.ProductPicker.GetInputElement().val(value);
|
||||
Grocy.Components.ProductPicker.GetInputElement().trigger('change');
|
||||
}
|
||||
|
||||
Grocy.Components.ProductPicker.SetId = function(value)
|
||||
{
|
||||
Grocy.Components.ProductPicker.GetPicker().val(value);
|
||||
Grocy.Components.ProductPicker.GetPicker().data('combobox').refresh();
|
||||
Grocy.Components.ProductPicker.GetInputElement().trigger('change');
|
||||
}
|
||||
|
||||
Grocy.Components.ProductPicker.Clear = function()
|
||||
{
|
||||
Grocy.Components.ProductPicker.SetValue('');
|
||||
Grocy.Components.ProductPicker.SetId(null);
|
||||
}
|
||||
|
||||
Grocy.Components.ProductPicker.InProductAddWorkflow = function()
|
||||
{
|
||||
return GetUriParam('flow') == "InplaceNewProductWithName";
|
||||
}
|
||||
|
||||
Grocy.Components.ProductPicker.InProductModifyWorkflow = function()
|
||||
{
|
||||
return GetUriParam('flow') == "InplaceAddBarcodeToExistingProduct";
|
||||
}
|
||||
|
||||
Grocy.Components.ProductPicker.InAnyFlow = function()
|
||||
{
|
||||
return Grocy.Components.ProductPicker.InProductAddWorkflow() || Grocy.Components.ProductPicker.InProductModifyWorkflow();
|
||||
}
|
||||
|
||||
Grocy.Components.ProductPicker.FinishFlow = function()
|
||||
{
|
||||
RemoveUriParam("flow");
|
||||
RemoveUriParam("barcode");
|
||||
RemoveUriParam("product-name");
|
||||
}
|
||||
|
||||
Grocy.Components.ProductPicker.ShowCustomError = function(text)
|
||||
{
|
||||
var element = $("#custom-productpicker-error");
|
||||
element.text(text);
|
||||
element.removeClass("d-none");
|
||||
}
|
||||
|
||||
Grocy.Components.ProductPicker.HideCustomError = function()
|
||||
{
|
||||
$("#custom-productpicker-error").addClass("d-none");
|
||||
}
|
||||
|
||||
Grocy.Components.ProductPicker.Disable = function()
|
||||
{
|
||||
Grocy.Components.ProductPicker.GetInputElement().attr("disabled", "");
|
||||
$("#barcodescanner-start-button").attr("disabled", "");
|
||||
$("#barcodescanner-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");
|
||||
}
|
||||
|
||||
$('.product-combobox').combobox({
|
||||
appendId: '_text_input',
|
||||
bsVersion: '4',
|
||||
clearIfNoMatch: false
|
||||
});
|
||||
|
||||
var this_product_picker = Grocy.Components.ProductPicker.GetPicker();
|
||||
var productpicker_doFocus = false;
|
||||
|
||||
var prefillProduct = GetUriParam('product-name');
|
||||
var prefillProduct2 = this_product_picker.parent().data('prefill-by-name').toString();
|
||||
if (!prefillProduct2.isEmpty())
|
||||
{
|
||||
prefillProduct = prefillProduct2;
|
||||
}
|
||||
if (typeof prefillProduct !== "undefined")
|
||||
{
|
||||
var possibleOptionElement = $("#product_id option[data-additional-searchdata*=\"" + prefillProduct + "\"]").first();
|
||||
if (possibleOptionElement.length === 0)
|
||||
{
|
||||
possibleOptionElement = $("#product_id option:contains(\"" + prefillProduct + "\")").first();
|
||||
}
|
||||
|
||||
if (possibleOptionElement.length > 0)
|
||||
{
|
||||
productpicker_doFocus = true;
|
||||
this_product_picker.val(possibleOptionElement.val());
|
||||
}
|
||||
}
|
||||
|
||||
var prefillProductId = GetUriParam("product");
|
||||
var prefillProductId2 = this_product_picker.parent().data('prefill-by-id').toString();
|
||||
if (!prefillProductId2.isEmpty())
|
||||
{
|
||||
prefillProductId = prefillProductId2;
|
||||
}
|
||||
if (typeof prefillProductId !== "undefined")
|
||||
{
|
||||
this_product_picker.val(prefillProductId);
|
||||
productpicker_doFocus = true;
|
||||
}
|
||||
|
||||
if (productpicker_doFocus)
|
||||
{
|
||||
this_product_picker.data('combobox').refresh();
|
||||
this_product_picker.trigger('change');
|
||||
|
||||
$(this_product_picker.parent().data('next-input-selector').toString())
|
||||
.focus();
|
||||
}
|
||||
|
||||
if (GetUriParam("flow") === "InplaceAddBarcodeToExistingProduct")
|
||||
{
|
||||
$('#InplaceAddBarcodeToExistingProduct').text(GetUriParam("barcode"));
|
||||
$('#flow-info-InplaceAddBarcodeToExistingProduct').removeClass('d-none');
|
||||
$('#barcode-lookup-disabled-hint').removeClass('d-none');
|
||||
$('#barcode-lookup-hint').addClass('d-none');
|
||||
}
|
||||
|
||||
Grocy.Components.ProductPicker.PopupOpen = false;
|
||||
$('#product_id_text_input').on('blur', function(e)
|
||||
{
|
||||
if (Grocy.Components.ProductPicker.GetPicker().hasClass("combobox-menu-visible"))
|
||||
{
|
||||
return;
|
||||
}
|
||||
$('#product_id').attr("barcode", "null");
|
||||
|
||||
var input = $('#product_id_text_input').val().toString();
|
||||
var possibleOptionElement = [];
|
||||
|
||||
// did we enter a grocycode?
|
||||
if (input.startsWith("grcy"))
|
||||
{
|
||||
var gc = input.split(":");
|
||||
if (gc[1] == "p")
|
||||
{
|
||||
// find product id
|
||||
possibleOptionElement = $("#product_id option[value=\"" + gc[2] + "\"]").first();
|
||||
$("#product_id").data("grocycode", true);
|
||||
}
|
||||
}
|
||||
else // process barcode as usual
|
||||
{
|
||||
possibleOptionElement = $("#product_id option[data-additional-searchdata*=\"" + input + ",\"]").first();
|
||||
}
|
||||
|
||||
if (GetUriParam('flow') === undefined && input.length > 0 && possibleOptionElement.length > 0)
|
||||
{
|
||||
$('#product_id').val(possibleOptionElement.val());
|
||||
$('#product_id').attr("barcode", input);
|
||||
$('#product_id').data('combobox').refresh();
|
||||
$('#product_id').trigger('change');
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Grocy.Components.ProductPicker.PopupOpen === true)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var optionElement = $("#product_id option:contains(\"" + input + "\")").first();
|
||||
if (input.length > 0 && optionElement.length === 0 && GetUriParam('flow') === undefined && Grocy.Components.ProductPicker.GetPicker().parent().data('disallow-all-product-workflows').toString() === "false")
|
||||
{
|
||||
var addProductWorkflowsAdditionalCssClasses = "";
|
||||
if (Grocy.Components.ProductPicker.GetPicker().parent().data('disallow-add-product-workflows').toString() === "true")
|
||||
{
|
||||
addProductWorkflowsAdditionalCssClasses = "d-none";
|
||||
}
|
||||
|
||||
var embedded = "";
|
||||
if (GetUriParam("embedded") !== undefined)
|
||||
{
|
||||
embedded = "embedded";
|
||||
}
|
||||
|
||||
var buttons = {
|
||||
cancel: {
|
||||
label: __t('Cancel'),
|
||||
className: 'btn-secondary responsive-button',
|
||||
callback: function()
|
||||
{
|
||||
Grocy.Components.ProductPicker.PopupOpen = false;
|
||||
Grocy.Components.ProductPicker.SetValue('');
|
||||
}
|
||||
},
|
||||
addnewproduct: {
|
||||
label: '<strong>P</strong> ' + __t('Add as new product'),
|
||||
className: 'btn-success add-new-product-dialog-button responsive-button ' + addProductWorkflowsAdditionalCssClasses,
|
||||
callback: function()
|
||||
{
|
||||
|
||||
Grocy.Components.ProductPicker.PopupOpen = false;
|
||||
window.location.href = U('/product/new?flow=InplaceNewProductWithName&name=' + encodeURIComponent(input) + '&returnto=' + encodeURIComponent(Grocy.CurrentUrlRelative + "?flow=InplaceNewProductWithName&" + embedded) + "&" + embedded);
|
||||
}
|
||||
},
|
||||
addbarcode: {
|
||||
label: '<strong>B</strong> ' + __t('Add as barcode to existing product'),
|
||||
className: 'btn-info add-new-barcode-dialog-button responsive-button',
|
||||
callback: function()
|
||||
{
|
||||
Grocy.Components.ProductPicker.PopupOpen = false;
|
||||
window.location.href = U(Grocy.CurrentUrlRelative + '?flow=InplaceAddBarcodeToExistingProduct&barcode=' + encodeURIComponent(input));
|
||||
}
|
||||
},
|
||||
addnewproductwithbarcode: {
|
||||
label: '<strong>A</strong> ' + __t('Add as new product and prefill barcode'),
|
||||
className: 'btn-warning add-new-product-with-barcode-dialog-button responsive-button ' + addProductWorkflowsAdditionalCssClasses,
|
||||
callback: function()
|
||||
{
|
||||
Grocy.Components.ProductPicker.PopupOpen = false;
|
||||
window.location.href = U('/product/new?flow=InplaceNewProductWithBarcode&barcode=' + encodeURIComponent(input) + '&returnto=' + encodeURIComponent(Grocy.CurrentUrlRelative + "?flow=InplaceAddBarcodeToExistingProduct&barcode=" + input + "&" + embedded) + "&" + embedded);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (!Grocy.FeatureFlags.DISABLE_BROWSER_BARCODE_CAMERA_SCANNING)
|
||||
{
|
||||
buttons.retrycamerascanning = {
|
||||
label: '<strong>C</strong> <i class="fas fa-camera"></i>',
|
||||
className: 'btn-primary responsive-button retry-camera-scanning-button',
|
||||
callback: function()
|
||||
{
|
||||
Grocy.Components.ProductPicker.PopupOpen = false;
|
||||
Grocy.Components.ProductPicker.SetValue('');
|
||||
$("#barcodescanner-start-button").click();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
Grocy.Components.ProductPicker.PopupOpen = true;
|
||||
bootbox.dialog({
|
||||
message: __t('"%s" could not be resolved to a product, how do you want to proceed?', input),
|
||||
title: __t('Create or assign product'),
|
||||
onEscape: function()
|
||||
{
|
||||
Grocy.Components.ProductPicker.PopupOpen = false;
|
||||
Grocy.Components.ProductPicker.SetValue('');
|
||||
},
|
||||
size: 'large',
|
||||
backdrop: true,
|
||||
closeButton: false,
|
||||
buttons: buttons
|
||||
}).on('keypress', function(e)
|
||||
{
|
||||
if (e.key === 'B' || e.key === 'b')
|
||||
{
|
||||
$('.add-new-barcode-dialog-button').not(".d-none").click();
|
||||
}
|
||||
if (e.key === 'p' || e.key === 'P')
|
||||
{
|
||||
$('.add-new-product-dialog-button').not(".d-none").click();
|
||||
}
|
||||
if (e.key === 'a' || e.key === 'A')
|
||||
{
|
||||
$('.add-new-product-with-barcode-dialog-button').not(".d-none").click();
|
||||
}
|
||||
if (e.key === 'c' || e.key === 'C')
|
||||
{
|
||||
$('.retry-camera-scanning-button').not(".d-none").click();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
$(document).on("Grocy.BarcodeScanned", function(e, barcode, target)
|
||||
{
|
||||
if (!(target == "@productpicker" || target == "undefined" || target == undefined)) // Default target
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Don't know why the blur event does not fire immediately ... this works...
|
||||
Grocy.Components.ProductPicker.GetInputElement().focusout();
|
||||
Grocy.Components.ProductPicker.GetInputElement().focus();
|
||||
Grocy.Components.ProductPicker.GetInputElement().blur();
|
||||
|
||||
Grocy.Components.ProductPicker.GetInputElement().val(barcode);
|
||||
|
||||
setTimeout(function()
|
||||
{
|
||||
Grocy.Components.ProductPicker.GetInputElement().focusout();
|
||||
Grocy.Components.ProductPicker.GetInputElement().focus();
|
||||
Grocy.Components.ProductPicker.GetInputElement().blur();
|
||||
}, 200);
|
||||
});
|
||||
|
||||
$(document).on("shown.bs.modal", function(e)
|
||||
{
|
||||
$(".modal-footer").addClass("d-block").addClass("d-sm-flex");
|
||||
$(".modal-footer").find("button").addClass("mt-2").addClass("mt-sm-0");
|
||||
})
|
||||
|
||||
// Make that ENTER behaves the same like TAB (trigger blur to start workflows, but only when the dropdown is not opened)
|
||||
$('#product_id_text_input').keydown(function(event)
|
||||
{
|
||||
if (event.keyCode === 13) // Enter
|
||||
{
|
||||
if (Grocy.Components.ProductPicker.GetPicker().hasClass("combobox-menu-visible"))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
$("#product_id_text_input").trigger("blur");
|
||||
}
|
||||
});
|
||||
|
|
@ -1,71 +0,0 @@
|
|||
Grocy.Components.RecipePicker = {};
|
||||
|
||||
Grocy.Components.RecipePicker.GetPicker = function()
|
||||
{
|
||||
return $('#recipe_id');
|
||||
}
|
||||
|
||||
Grocy.Components.RecipePicker.GetInputElement = function()
|
||||
{
|
||||
return $('#recipe_id_text_input');
|
||||
}
|
||||
|
||||
Grocy.Components.RecipePicker.GetValue = function()
|
||||
{
|
||||
return $('#recipe_id').val();
|
||||
}
|
||||
|
||||
Grocy.Components.RecipePicker.SetValue = function(value)
|
||||
{
|
||||
Grocy.Components.RecipePicker.GetInputElement().val(value);
|
||||
Grocy.Components.RecipePicker.GetInputElement().trigger('change');
|
||||
}
|
||||
|
||||
Grocy.Components.RecipePicker.SetId = function(value)
|
||||
{
|
||||
Grocy.Components.RecipePicker.GetPicker().val(value);
|
||||
Grocy.Components.RecipePicker.GetPicker().data('combobox').refresh();
|
||||
Grocy.Components.RecipePicker.GetInputElement().trigger('change');
|
||||
}
|
||||
|
||||
Grocy.Components.RecipePicker.Clear = function()
|
||||
{
|
||||
Grocy.Components.RecipePicker.SetValue('');
|
||||
Grocy.Components.RecipePicker.SetId(null);
|
||||
}
|
||||
|
||||
$('.recipe-combobox').combobox({
|
||||
appendId: '_text_input',
|
||||
bsVersion: '4',
|
||||
clearIfNoMatch: true
|
||||
});
|
||||
|
||||
var this_recipe_picker = Grocy.Components.RecipePicker.GetPicker();
|
||||
var recipe_picker_doFocus = false;
|
||||
|
||||
var prefillByName = this_recipe_picker.parent().data('prefill-by-name').toString();
|
||||
if (typeof prefillByName !== "undefined")
|
||||
{
|
||||
var possibleOptionElement = $("#recipe_id option:contains(\"" + prefillByName + "\")").first();
|
||||
|
||||
if (possibleOptionElement.length > 0)
|
||||
{
|
||||
recipe_picker_doFocus = true;
|
||||
this_recipe_picker.val(possibleOptionElement.val());
|
||||
}
|
||||
}
|
||||
|
||||
var prefillById = this_recipe_picker.parent().data('prefill-by-id').toString();
|
||||
if (typeof prefillById !== "undefined")
|
||||
{
|
||||
recipe_picker_doFocus = true;
|
||||
this_recipe_picker.val(prefillById);
|
||||
}
|
||||
|
||||
if (recipe_picker_doFocus)
|
||||
{
|
||||
this_recipe_picker.data('combobox').refresh();
|
||||
this_recipe_picker.trigger('change');
|
||||
|
||||
$(this_recipe_picker.parent().data('next-input-selector').toString()).focus();
|
||||
}
|
||||
|
|
@ -1,72 +0,0 @@
|
|||
Grocy.Components.ShoppingLocationPicker = {};
|
||||
|
||||
Grocy.Components.ShoppingLocationPicker.GetPicker = function()
|
||||
{
|
||||
return $('#shopping_location_id');
|
||||
}
|
||||
|
||||
Grocy.Components.ShoppingLocationPicker.GetInputElement = function()
|
||||
{
|
||||
return $('#shopping_location_id_text_input');
|
||||
}
|
||||
|
||||
Grocy.Components.ShoppingLocationPicker.GetValue = function()
|
||||
{
|
||||
return $('#shopping_location_id').val();
|
||||
}
|
||||
|
||||
Grocy.Components.ShoppingLocationPicker.SetValue = function(value)
|
||||
{
|
||||
Grocy.Components.ShoppingLocationPicker.GetInputElement().val(value);
|
||||
Grocy.Components.ShoppingLocationPicker.GetInputElement().trigger('change');
|
||||
}
|
||||
|
||||
Grocy.Components.ShoppingLocationPicker.SetId = function(value)
|
||||
{
|
||||
Grocy.Components.ShoppingLocationPicker.GetPicker().val(value);
|
||||
Grocy.Components.ShoppingLocationPicker.GetPicker().data('combobox').refresh();
|
||||
Grocy.Components.ShoppingLocationPicker.GetInputElement().trigger('change');
|
||||
}
|
||||
|
||||
Grocy.Components.ShoppingLocationPicker.Clear = function()
|
||||
{
|
||||
Grocy.Components.ShoppingLocationPicker.SetValue('');
|
||||
Grocy.Components.ShoppingLocationPicker.SetId(null);
|
||||
}
|
||||
|
||||
$('.shopping-location-combobox').combobox({
|
||||
appendId: '_text_input',
|
||||
bsVersion: '4',
|
||||
clearIfNoMatch: true
|
||||
});
|
||||
|
||||
var shoppinglocationpicker_doFocus = false;
|
||||
var this_shoppinglocation_picker = $('#shopping_location_id');
|
||||
|
||||
var prefillByName = this_shoppinglocation_picker.parent().data('prefill-by-name').toString();
|
||||
if (typeof prefillByName !== "undefined")
|
||||
{
|
||||
var possibleOptionElement = $("#shopping_location_id option:contains(\"" + prefillByName + "\")").first();
|
||||
|
||||
if (possibleOptionElement.length > 0)
|
||||
{
|
||||
this_shoppinglocation_picker.val(possibleOptionElement.val());
|
||||
shoppinglocationpicker_doFocus = true;
|
||||
}
|
||||
}
|
||||
|
||||
var prefillById = this_shoppinglocation_picker.parent().data('prefill-by-id').toString();
|
||||
if (typeof prefillById !== "undefined")
|
||||
{
|
||||
this_shoppinglocation_picker.val(prefillById);
|
||||
shoppinglocationpicker_doFocus = true;
|
||||
}
|
||||
|
||||
if (shoppinglocationpicker_doFocus)
|
||||
{
|
||||
this_shoppinglocation_picker.data('combobox').refresh();
|
||||
this_shoppinglocation_picker.trigger('change');
|
||||
|
||||
$(this_shoppinglocation_picker.parent().data('next-input-selector').toString())
|
||||
.focus();
|
||||
}
|
||||
|
|
@ -1,183 +0,0 @@
|
|||
import { RandomString } from '../../helpers/extensions';
|
||||
|
||||
Grocy.Components.UserfieldsForm = {};
|
||||
|
||||
Grocy.Components.UserfieldsForm.Save = function(success, error)
|
||||
{
|
||||
if (!$("#userfields-form").length)
|
||||
{
|
||||
if (success)
|
||||
{
|
||||
success();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
var jsonData = {};
|
||||
|
||||
$("#userfields-form .userfield-input").not("div").each(function()
|
||||
{
|
||||
var input = $(this);
|
||||
var fieldName = input.attr("data-userfield-name");
|
||||
var fieldValue = input.val();
|
||||
|
||||
if (input.attr("type") == "checkbox")
|
||||
{
|
||||
jsonData[fieldName] = "0";
|
||||
if (input.is(":checked"))
|
||||
{
|
||||
jsonData[fieldName] = "1";
|
||||
}
|
||||
}
|
||||
else if (input.attr("type") == "file")
|
||||
{
|
||||
var oldFile = input.data('old-file')
|
||||
if (oldFile)
|
||||
{
|
||||
Grocy.Api.Delete('files/userfiles/' + oldFile, null, null,
|
||||
function(xhr)
|
||||
{
|
||||
Grocy.FrontendHelpers.ShowGenericError('Could not delete file', xhr);
|
||||
});
|
||||
jsonData[fieldName] = "";
|
||||
}
|
||||
|
||||
if (input[0].files.length > 0)
|
||||
{
|
||||
// Files service requires an extension
|
||||
var fileName = RandomString() + '.' + input[0].files[0].name.split('.').reverse()[0];
|
||||
|
||||
jsonData[fieldName] = btoa(fileName) + '_' + btoa(input[0].files[0].name);
|
||||
Grocy.Api.UploadFile(input[0].files[0], 'userfiles', fileName,
|
||||
function(result)
|
||||
{
|
||||
},
|
||||
function(xhr)
|
||||
{
|
||||
// When navigating away immediately from the current page, this is maybe a false positive - so ignore this for now
|
||||
// Grocy.FrontendHelpers.ShowGenericError('Error while saving, probably this item already exists', xhr.response)
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
else if ($(this).hasAttr("multiple"))
|
||||
{
|
||||
jsonData[fieldName] = $(this).val().join(",");
|
||||
}
|
||||
else
|
||||
{
|
||||
jsonData[fieldName] = fieldValue;
|
||||
}
|
||||
});
|
||||
|
||||
Grocy.Api.Put('userfields/' + $("#userfields-form").data("entity") + '/' + Grocy.EditObjectId, jsonData,
|
||||
function(result)
|
||||
{
|
||||
if (success)
|
||||
{
|
||||
success();
|
||||
}
|
||||
},
|
||||
function(xhr)
|
||||
{
|
||||
if (error)
|
||||
{
|
||||
error();
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
Grocy.Components.UserfieldsForm.Load = function()
|
||||
{
|
||||
if (!$("#userfields-form").length)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Grocy.Api.Get('userfields/' + $("#userfields-form").data("entity") + '/' + Grocy.EditObjectId,
|
||||
function(result)
|
||||
{
|
||||
$.each(result, function(key, value)
|
||||
{
|
||||
var input = $(".userfield-input[data-userfield-name='" + key + "']");
|
||||
|
||||
if (input.attr("type") == "checkbox" && value == 1)
|
||||
{
|
||||
input.prop("checked", true);
|
||||
}
|
||||
else if (input.hasAttr("multiple"))
|
||||
{
|
||||
input.val(value.split(","));
|
||||
$(".selectpicker").selectpicker("render");
|
||||
}
|
||||
else if (input.attr('type') == "file")
|
||||
{
|
||||
if (value != null && !value.isEmpty())
|
||||
{
|
||||
var fileName = atob(value.split('_')[1]);
|
||||
var fileSrc = value.split('_')[0];
|
||||
var formGroup = input.parent().parent().parent();
|
||||
|
||||
formGroup.find("label.custom-file-label").text(fileName);
|
||||
formGroup.find(".userfield-file-show").attr('href', U('/files/userfiles/' + value));
|
||||
formGroup.find('.userfield-file-show').removeClass('d-none');
|
||||
formGroup.find('img.userfield-current-file')
|
||||
.attr('src', U('/files/userfiles/' + value + '?force_serve_as=picture&best_fit_width=250&best_fit_height=250'));
|
||||
LoadImagesLazy();
|
||||
|
||||
formGroup.find('.userfield-file-delete').click(
|
||||
function()
|
||||
{
|
||||
formGroup.find("label.custom-file-label").text(__t("No file selected"));
|
||||
formGroup.find(".userfield-file-show").addClass('d-none');
|
||||
input.attr('data-old-file', fileSrc);
|
||||
}
|
||||
);
|
||||
|
||||
input.on("change", function(e)
|
||||
{
|
||||
formGroup.find(".userfield-file-show").addClass('d-none');
|
||||
});
|
||||
}
|
||||
}
|
||||
else if (input.attr("data-userfield-type") == "link")
|
||||
{
|
||||
if (!value.isEmpty())
|
||||
{
|
||||
var data = JSON.parse(value);
|
||||
|
||||
var formRow = input.parent().parent();
|
||||
formRow.find(".userfield-link-title").val(data.title);
|
||||
formRow.find(".userfield-link-link").val(data.link);
|
||||
|
||||
input.val(value);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
input.val(value);
|
||||
}
|
||||
});
|
||||
},
|
||||
function(xhr)
|
||||
{
|
||||
console.error(xhr);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
$(".userfield-link").keyup(function(e)
|
||||
{
|
||||
var formRow = $(this).parent().parent();
|
||||
var title = formRow.find(".userfield-link-title").val();
|
||||
var link = formRow.find(".userfield-link-link").val();
|
||||
|
||||
var value = {
|
||||
"title": title,
|
||||
"link": link
|
||||
};
|
||||
|
||||
formRow.find(".userfield-input").val(JSON.stringify(value));
|
||||
});
|
||||
|
|
@ -1,81 +0,0 @@
|
|||
Grocy.Components.UserPicker = {};
|
||||
|
||||
Grocy.Components.UserPicker.GetPicker = function()
|
||||
{
|
||||
return $('#user_id');
|
||||
}
|
||||
|
||||
Grocy.Components.UserPicker.GetInputElement = function()
|
||||
{
|
||||
return $('#user_id_text_input');
|
||||
}
|
||||
|
||||
Grocy.Components.UserPicker.GetValue = function()
|
||||
{
|
||||
return $('#user_id').val();
|
||||
}
|
||||
|
||||
Grocy.Components.UserPicker.SetValue = function(value)
|
||||
{
|
||||
Grocy.Components.UserPicker.GetInputElement().val(value);
|
||||
Grocy.Components.UserPicker.GetInputElement().trigger('change');
|
||||
}
|
||||
|
||||
Grocy.Components.UserPicker.SetId = function(value)
|
||||
{
|
||||
Grocy.Components.UserPicker.GetPicker().val(value);
|
||||
Grocy.Components.UserPicker.GetPicker().data('combobox').refresh();
|
||||
Grocy.Components.UserPicker.GetInputElement().trigger('change');
|
||||
}
|
||||
|
||||
Grocy.Components.UserPicker.Clear = function()
|
||||
{
|
||||
Grocy.Components.UserPicker.SetValue('');
|
||||
Grocy.Components.UserPicker.SetId(null);
|
||||
}
|
||||
|
||||
$('.user-combobox').combobox({
|
||||
appendId: '_text_input',
|
||||
bsVersion: '4'
|
||||
});
|
||||
|
||||
var this_user_picker = Grocy.Components.UserPicker.GetPicker();
|
||||
var user_picker_doFocus = false;
|
||||
var possibleOptionElement = null;
|
||||
|
||||
var prefillUser = this_user_picker.parent().data('prefill-by-username').toString();
|
||||
if (typeof prefillUser !== "undefined")
|
||||
{
|
||||
possibleOptionElement = $("#user_id option[data-additional-searchdata*=\"" + prefillUser + "\"]").first();
|
||||
if (possibleOptionElement.length === 0)
|
||||
{
|
||||
possibleOptionElement = $("#user_id option:contains(\"" + prefillUser + "\")").first();
|
||||
}
|
||||
|
||||
if (possibleOptionElement.length > 0)
|
||||
{
|
||||
user_picker_doFocus = true;
|
||||
this_user_picker.val(possibleOptionElement.val());
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
var prefillUserId = this_user_picker.parent().data('prefill-by-user-id').toString();
|
||||
if (typeof prefillUserId !== "undefined")
|
||||
{
|
||||
possibleOptionElement = $("#user_id option[value='" + prefillUserId + "']").first();
|
||||
if (possibleOptionElement.length > 0)
|
||||
{
|
||||
user_picker_doFocus = true;
|
||||
this_user_picker.val(possibleOptionElement.val());
|
||||
}
|
||||
}
|
||||
|
||||
if (user_picker_doFocus)
|
||||
{
|
||||
this_user_picker.data('combobox').refresh();
|
||||
this_user_picker.trigger('change');
|
||||
|
||||
$(this_user_picker.parent().data('next-input-selector').toString())
|
||||
.focus();
|
||||
}
|
||||
|
|
@ -1,6 +1,11 @@
|
|||
import { BoolVal } from '../helpers/extensions';
|
||||
import { WindowMessageBag } from '../helpers/messagebag';
|
||||
|
||||
Grocy.Use("productamountpicker");
|
||||
Grocy.Use("productcard");
|
||||
Grocy.Use("productpicker");
|
||||
Grocy.Use("recipepicker");
|
||||
|
||||
$('#save-consume-button').on('click', function(e)
|
||||
{
|
||||
e.preventDefault();
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
import { RandomString } from '../helpers/extensions';
|
||||
import { ResizeResponsiveEmbeds } from '../helpers/embeds';
|
||||
|
||||
Grocy.Use("userfieldsform");
|
||||
|
||||
$('#save-equipment-button').on('click', function(e)
|
||||
{
|
||||
e.preventDefault();
|
||||
|
|
|
|||
|
|
@ -1,5 +1,17 @@
|
|||
import { WindowMessageBag } from '../helpers/messagebag';
|
||||
|
||||
Grocy.Use("datetimepicker");
|
||||
if (Grocy.UserSettings.show_purchased_date_on_purchase)
|
||||
{
|
||||
Grocy.Use("datetimepicker2");
|
||||
}
|
||||
Grocy.Use("locationpicker");
|
||||
Grocy.Use("numberpicker");
|
||||
Grocy.Use("productpicker");
|
||||
Grocy.Use("productamountpicker");
|
||||
Grocy.Use("productcard");
|
||||
Grocy.Use("shoppinglocationpicker");
|
||||
|
||||
$('#save-inventory-button').on('click', function(e)
|
||||
{
|
||||
e.preventDefault();
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
import { WindowMessageBag } from '../helpers/messagebag';
|
||||
|
||||
Grocy.Use("userfieldsform");
|
||||
|
||||
$('#save-location-button').on('click', function(e)
|
||||
{
|
||||
e.preventDefault();
|
||||
|
|
|
|||
|
|
@ -10,6 +10,10 @@ import '@fullcalendar/core/main.css';
|
|||
import '@fullcalendar/daygrid/main.css';
|
||||
import '@fullcalendar/bootstrap/main.css';
|
||||
|
||||
Grocy.Use("numberpicker");
|
||||
Grocy.Use("productamountpicker");
|
||||
Grocy.Use("recipepicker");
|
||||
|
||||
var setLocale = false;
|
||||
if (__t('fullcalendar_locale').replace(" ", "") !== "" && __t('fullcalendar_locale') != 'x')
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,5 +1,9 @@
|
|||
import { WindowMessageBag } from '../helpers/messagebag';
|
||||
|
||||
Grocy.Use('barcodescanner');
|
||||
Grocy.Use("productamountpicker");
|
||||
Grocy.Use("userfieldsform");
|
||||
|
||||
$('#save-barcode-button').on('click', function(e)
|
||||
{
|
||||
e.preventDefault();
|
||||
|
|
|
|||
|
|
@ -1,5 +1,9 @@
|
|||
import { BoolVal } from '../helpers/extensions';
|
||||
|
||||
Grocy.Use("numberpicker");
|
||||
Grocy.Use("shoppinglocationpicker");
|
||||
Grocy.Use("userfieldsform");
|
||||
|
||||
function saveProductPicture(result, location, jsonData)
|
||||
{
|
||||
var productId = Grocy.EditObjectId || result.created_object_id;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
import { WindowMessageBag } from '../helpers/messagebag';
|
||||
|
||||
Grocy.Use("userfieldsform");
|
||||
|
||||
$('#save-product-group-button').on('click', function(e)
|
||||
{
|
||||
e.preventDefault();
|
||||
|
|
|
|||
|
|
@ -1,6 +1,17 @@
|
|||
import { BoolVal } from '../helpers/extensions';
|
||||
import { WindowMessageBag } from '../helpers/messagebag';
|
||||
|
||||
Grocy.Use("datetimepicker");
|
||||
if (Grocy.UserSettings.show_purchased_date_on_purchase)
|
||||
{
|
||||
Grocy.Use("datetimepicker2");
|
||||
}
|
||||
Grocy.Use("locationpicker");
|
||||
Grocy.Use("numberpicker");
|
||||
Grocy.Use("productamountpicker");
|
||||
Grocy.Use("productcard");
|
||||
Grocy.Use("shoppinglocationpicker");
|
||||
|
||||
var CurrentProductDetails;
|
||||
|
||||
$('#save-purchase-button').on('click', function(e)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,8 @@
|
|||
import { WindowMessageBag } from '../helpers/messagebag';
|
||||
|
||||
Grocy.Use("numberpicker");
|
||||
Grocy.Use("userfieldsform");
|
||||
|
||||
$('#save-quconversion-button').on('click', function(e)
|
||||
{
|
||||
e.preventDefault();
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
import { WindowMessageBag } from '../helpers/messagebag';
|
||||
|
||||
Grocy.Use("userfieldsform");
|
||||
|
||||
$('.save-quantityunit-button').on('click', function(e)
|
||||
{
|
||||
e.preventDefault();
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
$("#qu_id").change(function(event)
|
||||
Grocy.Use("numberpicker");
|
||||
|
||||
$("#qu_id").change(function(event)
|
||||
{
|
||||
RefreshQuPluralTestingResult();
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,5 +1,9 @@
|
|||
import { WindowMessageBag } from '../helpers/messagebag';
|
||||
|
||||
Grocy.Use("numberpicker");
|
||||
Grocy.Use("recipepicker");
|
||||
Grocy.Use("userfieldsform");
|
||||
|
||||
function saveRecipePicture(result, location, jsonData)
|
||||
{
|
||||
var recipeId = Grocy.EditObjectId || result.created_object_id;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,9 @@
|
|||
import { WindowMessageBag } from '../helpers/messagebag';
|
||||
|
||||
Grocy.Use("numberpicker");
|
||||
Grocy.Use("productamountpicker");
|
||||
Grocy.Use("productcard");
|
||||
|
||||
Grocy.RecipePosFormInitialLoadDone = false;
|
||||
|
||||
$('#save-recipe-pos-button').on('click', function(e)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
var recipesTables = $('#recipes-table').DataTable({
|
||||
Grocy.Use("numberpicker");
|
||||
|
||||
var recipesTables = $('#recipes-table').DataTable({
|
||||
'order': [[1, 'asc']],
|
||||
'columnDefs': [
|
||||
{ 'orderable': false, 'targets': 0 },
|
||||
|
|
|
|||
|
|
@ -3,6 +3,9 @@
|
|||
import bwipjs from '../../node_modules/bwip-js/dist/bwip-js.mjs';
|
||||
import { WindowMessageBag } from '../helpers/messagebag';
|
||||
|
||||
Grocy.Use("calendarcard");
|
||||
Grocy.Use("productcard");
|
||||
|
||||
var shoppingListTable = $('#shoppinglist-table').DataTable({
|
||||
'order': [[1, 'asc']],
|
||||
"orderFixed": [[3, 'asc']],
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
import { WindowMessageBag } from '../helpers/messagebag';
|
||||
|
||||
Grocy.Use("userfieldsform");
|
||||
|
||||
$('#save-shopping-list-button').on('click', function(e)
|
||||
{
|
||||
e.preventDefault();
|
||||
|
|
|
|||
|
|
@ -1,5 +1,8 @@
|
|||
import { WindowMessageBag } from '../helpers/messagebag';
|
||||
|
||||
Grocy.Use("productamountpicker");
|
||||
Grocy.Use("userfieldsform");
|
||||
|
||||
Grocy.ShoppingListItemFormInitialLoadDone = false;
|
||||
|
||||
$('#save-shoppinglist-button').on('click', function(e)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
import { WindowMessageBag } from '../helpers/messagebag';
|
||||
|
||||
Grocy.Use("userfieldsform");
|
||||
|
||||
$('#save-shopping-location-button').on('click', function(e)
|
||||
{
|
||||
e.preventDefault();
|
||||
|
|
|
|||
|
|
@ -1,4 +1,7 @@
|
|||
var stockEntriesTable = $('#stockentries-table').DataTable({
|
||||
Grocy.Use("productcard");
|
||||
Grocy.Use("productpicker");
|
||||
|
||||
var stockEntriesTable = $('#stockentries-table').DataTable({
|
||||
'order': [[2, 'asc']],
|
||||
'columnDefs': [
|
||||
{ 'orderable': false, 'targets': 0 },
|
||||
|
|
|
|||
|
|
@ -1,5 +1,11 @@
|
|||
import { WindowMessageBag } from '../helpers/messagebag';
|
||||
|
||||
Grocy.Use("datetimepicker");
|
||||
Grocy.Use("datetimepicker2");
|
||||
Grocy.Use("locationpicker");
|
||||
Grocy.Use("numberpicker");
|
||||
Grocy.Use("shoppinglocationpicker");
|
||||
|
||||
$('#save-stockentry-button').on('click', function(e)
|
||||
{
|
||||
e.preventDefault();
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
var stockOverviewTable = $('#stock-overview-table').DataTable({
|
||||
Grocy.Use("productcard");
|
||||
|
||||
var stockOverviewTable = $('#stock-overview-table').DataTable({
|
||||
'order': [[5, 'asc']],
|
||||
'columnDefs': [
|
||||
{ 'orderable': false, 'targets': 0 },
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
import { BoolVal } from '../helpers/extensions';
|
||||
|
||||
Grocy.Use("numberpicker");
|
||||
|
||||
$("#product_presets_location_id").val(Grocy.UserSettings.product_presets_location_id);
|
||||
$("#product_presets_product_group_id").val(Grocy.UserSettings.product_presets_product_group_id);
|
||||
$("#product_presets_qu_id").val(Grocy.UserSettings.product_presets_qu_id);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,8 @@
|
|||
import { WindowMessageBag } from '../helpers/messagebag';
|
||||
|
||||
Grocy.Use("datetimepicker");
|
||||
Grocy.Use("userfieldsform");
|
||||
|
||||
$('#save-task-button').on('click', function(e)
|
||||
{
|
||||
e.preventDefault();
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
var tasksTable = $('#tasks-table').DataTable({
|
||||
Grocy.Use("userpicker");
|
||||
|
||||
var tasksTable = $('#tasks-table').DataTable({
|
||||
'order': [[2, 'asc']],
|
||||
'columnDefs': [
|
||||
{ 'orderable': false, 'targets': 0 },
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
$("#tasks_due_soon_days").val(Grocy.UserSettings.tasks_due_soon_days);
|
||||
Grocy.Use("numberpicker");
|
||||
|
||||
$("#tasks_due_soon_days").val(Grocy.UserSettings.tasks_due_soon_days);
|
||||
|
||||
RefreshLocaleNumberInput();
|
||||
|
|
|
|||
|
|
@ -1,5 +1,9 @@
|
|||
import { WindowMessageBag } from '../helpers/messagebag';
|
||||
|
||||
Grocy.Use("productpicker");
|
||||
Grocy.Use("productamountpicker");
|
||||
Grocy.Use("productcard");
|
||||
|
||||
$('#save-transfer-button').on('click', function(e)
|
||||
{
|
||||
e.preventDefault();
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
import { WindowMessageBag } from '../helpers/messagebag';
|
||||
|
||||
Grocy.Use("numberpicker");
|
||||
|
||||
$('#save-userfield-button').on('click', function(e)
|
||||
{
|
||||
e.preventDefault();
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
function SaveUserPicture(result, jsonData)
|
||||
Grocy.Use("userfieldsform");
|
||||
|
||||
function SaveUserPicture(result, jsonData)
|
||||
{
|
||||
Grocy.Components.UserfieldsForm.Save(() =>
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
import { WindowMessageBag } from '../helpers/messagebag';
|
||||
|
||||
Grocy.Use("userfieldsform");
|
||||
|
||||
$('#save-userobject-button').on('click', function(e)
|
||||
{
|
||||
e.preventDefault();
|
||||
|
|
|
|||
|
|
@ -1,13 +1,5 @@
|
|||
@if (!GROCY_DISABLE_BROWSER_BARCODE_CAMERA_SCANNING)
|
||||
|
||||
@push('componentScripts')
|
||||
<script src="{{ $U('/viewjs/components/barcodescanner.js', true) }}?v={{ $version }}"></script>
|
||||
@endpush
|
||||
|
||||
@push('pageScripts')
|
||||
<script src="{{ $U('/components_unmanaged/quagga2-reader-datamatrix/index.js', true) }}?v={{ $version }}"></script>
|
||||
@endpush
|
||||
|
||||
@push('pageStyles')
|
||||
<style>
|
||||
#barcodescanner-start-button {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,3 @@
|
|||
@push('componentScripts')
|
||||
<script src="{{ $U('/viewjs/components/batterycard.js', true) }}?v={{ $version }}"></script>
|
||||
@endpush
|
||||
|
||||
<div class="card batterycard">
|
||||
<div class="card-header">
|
||||
<span class="float-left">{{ $__t('Battery overview') }}</span>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,3 @@
|
|||
@push('componentScripts')
|
||||
<script src="{{ $U('/viewjs/components/calendarcard.js', true) }}?v={{ $version }}"></script>
|
||||
@endpush
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<i class="fas fa-calendar"></i> {{ $__t('Calendar') }}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,3 @@
|
|||
@push('componentScripts')
|
||||
<script src="{{ $U('/viewjs/components/chorecard.js', true) }}?v={{ $version }}"></script>
|
||||
@endpush
|
||||
|
||||
<div class="card chorecard">
|
||||
<div class="card-header">
|
||||
<span class="float-left">{{ $__t('Chore overview') }}</span>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,3 @@
|
|||
@push('componentScripts')
|
||||
<script src="{{ $U('/viewjs/components/datetimepicker.js', true) }}?v={{ $version }}"></script>
|
||||
@endpush
|
||||
|
||||
@php if(!isset($isRequired)) { $isRequired = true; } @endphp
|
||||
@php if(!isset($initialValue)) { $initialValue = ''; } @endphp
|
||||
@php if(empty($earlierThanInfoLimit)) { $earlierThanInfoLimit = ''; } @endphp
|
||||
|
|
|
|||
|
|
@ -1,7 +1,3 @@
|
|||
@push('componentScripts')
|
||||
<script src="{{ $U('/viewjs/components/datetimepicker2.js', true) }}?v={{ $version }}"></script>
|
||||
@endpush
|
||||
|
||||
@php if(!isset($isRequired)) { $isRequired = true; } @endphp
|
||||
@php if(!isset($initialValue)) { $initialValue = ''; } @endphp
|
||||
@php if(empty($earlierThanInfoLimit)) { $earlierThanInfoLimit = ''; } @endphp
|
||||
|
|
|
|||
|
|
@ -1,7 +1,3 @@
|
|||
@push('componentScripts')
|
||||
<script src="{{ $U('/viewjs/components/locationpicker.js', true) }}?v={{ $version }}"></script>
|
||||
@endpush
|
||||
|
||||
@php if(empty($prefillByName)) { $prefillByName = ''; } @endphp
|
||||
@php if(empty($prefillById)) { $prefillById = ''; } @endphp
|
||||
@php if(!isset($isRequired)) { $isRequired = true; } @endphp
|
||||
|
|
|
|||
|
|
@ -1,7 +1,3 @@
|
|||
@push('componentScripts')
|
||||
<script src="{{ $U('/viewjs/components/numberpicker.js', true) }}?v={{ $version }}"></script>
|
||||
@endpush
|
||||
|
||||
@php if(!isset($value)) { $value = 1; } @endphp
|
||||
@php if(empty($min)) { $min = 0; } @endphp
|
||||
@php if(!isset($max)) { $max = ''; } @endphp
|
||||
|
|
|
|||
|
|
@ -1,6 +1,3 @@
|
|||
@push('componentScripts')
|
||||
<script src="{{ $U('/viewjs/components/productamountpicker.js', true) }}?v={{ $version }}"></script>
|
||||
@endpush
|
||||
|
||||
@php if(empty($additionalGroupCssClasses)) { $additionalGroupCssClasses = ''; } @endphp
|
||||
@php if(empty($additionalHtmlContextHelp)) { $additionalHtmlContextHelp = ''; } @endphp
|
||||
|
|
|
|||
|
|
@ -1,7 +1,3 @@
|
|||
@push('componentScripts')
|
||||
<script src="{{ $U('/viewjs/components/productcard.js', true) }}?v={{ $version }}"></script>
|
||||
@endpush
|
||||
|
||||
<div class="card productcard">
|
||||
<div class="card-header">
|
||||
<span class="float-left">{{ $__t('Product overview') }}</span>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,3 @@
|
|||
@push('componentScripts')
|
||||
<script src="{{ $U('/viewjs/components/productpicker.js', true) }}?v={{ $version }}"></script>
|
||||
@endpush
|
||||
|
||||
@php if(empty($disallowAddProductWorkflows)) { $disallowAddProductWorkflows = false; } @endphp
|
||||
@php if(empty($disallowAllProductWorkflows)) { $disallowAllProductWorkflows = false; } @endphp
|
||||
@php if(empty($prefillByName)) { $prefillByName = ''; } @endphp
|
||||
|
|
|
|||
|
|
@ -1,7 +1,3 @@
|
|||
@push('componentScripts')
|
||||
<script src="{{ $U('/viewjs/components/recipepicker.js', true) }}?v={{ $version }}"></script>
|
||||
@endpush
|
||||
|
||||
@php if(empty($prefillByName)) { $prefillByName = ''; } @endphp
|
||||
@php if(empty($prefillById)) { $prefillById = ''; } @endphp
|
||||
@php if(!isset($isRequired)) { $isRequired = true; } @endphp
|
||||
|
|
|
|||
|
|
@ -1,7 +1,3 @@
|
|||
@push('componentScripts')
|
||||
<script src="{{ $U('/viewjs/components/shoppinglocationpicker.js', true) }}?v={{ $version }}"></script>
|
||||
@endpush
|
||||
|
||||
@php if(empty($prefillByName)) { $prefillByName = ''; } @endphp
|
||||
@php if(empty($prefillById)) { $prefillById = ''; } @endphp
|
||||
@php if(!isset($isRequired)) { $isRequired = false; } @endphp
|
||||
|
|
|
|||
|
|
@ -1,7 +1,3 @@
|
|||
@push('componentScripts')
|
||||
<script src="{{ $U('/viewjs/components/userfieldsform.js', true) }}?v={{ $version }}"></script>
|
||||
@endpush
|
||||
|
||||
@if(count($userfields) > 0)
|
||||
|
||||
<div id="userfields-form"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,3 @@
|
|||
@push('componentScripts')
|
||||
<script src="{{ $U('/viewjs/components/userpicker.js', true) }}?v={{ $version }}"></script>
|
||||
@endpush
|
||||
|
||||
@php if(empty($prefillByUsername)) { $prefillByUsername = ''; } @endphp
|
||||
@php if(empty($prefillByUserId)) { $prefillByUserId = ''; } @endphp
|
||||
@php if(!isset($nextInputSelector)) { $nextInputSelector = ''; } @endphp
|
||||
|
|
|
|||
|
|
@ -8,11 +8,6 @@
|
|||
<script src="{{ $U('/node_modules/bwip-js/dist/bwip-js-min.js?v=', true) }}{{ $version }}"></script>
|
||||
@endpush
|
||||
|
||||
@push('pageStyles')
|
||||
<link href="{{ $U('/node_modules/animate.css/animate.min.css?v=', true) }}{{ $version }}"
|
||||
rel="stylesheet">
|
||||
@endpush
|
||||
|
||||
@section('content')
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
|
|
|
|||
|
|
@ -4,11 +4,6 @@
|
|||
|
||||
@section('viewJsName', 'quantityunitpluraltesting')
|
||||
|
||||
@push('pageStyles')
|
||||
<link href="{{ $U('/node_modules/animate.css/animate.min.css?v=', true) }}{{ $version }}"
|
||||
rel="stylesheet">
|
||||
@endpush
|
||||
|
||||
@section('content')
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
|
|
|
|||
|
|
@ -4,10 +4,6 @@
|
|||
@section('activeNav', 'shoppinglist')
|
||||
@section('viewJsName', 'shoppinglist')
|
||||
|
||||
@push('pageScripts')
|
||||
<script src="{{ $U('/viewjs/purchase.js?v=', true) }}{{ $version }}"></script>
|
||||
@endpush
|
||||
|
||||
@section('content')
|
||||
<div class="row d-print-none hide-on-fullscreen-card">
|
||||
<div class="col">
|
||||
|
|
|
|||
|
|
@ -3,10 +3,6 @@
|
|||
@section('title', $__t('Stock entries'))
|
||||
@section('viewJsName', 'stockentries')
|
||||
|
||||
@push('pageScripts')
|
||||
<script src="{{ $U('/viewjs/purchase.js?v=', true) }}{{ $version }}"></script>
|
||||
@endpush
|
||||
|
||||
@section('content')
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
|
|
|
|||
|
|
@ -4,10 +4,6 @@
|
|||
@section('activeNav', 'stockoverview')
|
||||
@section('viewJsName', 'stockoverview')
|
||||
|
||||
@push('pageScripts')
|
||||
<script src="{{ $U('/viewjs/purchase.js?v=', true) }}{{ $version }}"></script>
|
||||
@endpush
|
||||
|
||||
@section('content')
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user