1. Logout callback fix.\n 2. Store permutations report improvement for demo.

This commit is contained in:
2024-08-07 09:01:33 +01:00
parent 545c328b0b
commit 18977c8cac
2096 changed files with 381570 additions and 388 deletions

277
static/js/extras.js Normal file
View File

@@ -0,0 +1,277 @@
/* Page elements */
function displayOverlay(message, show, force) {
if (show) {
_overlayLoadingCount += 1;
}
else if (force) {
_overlayLoadingCount = 0;
}
else {
_overlayLoadingCount -= 1;
if (_overlayLoadingCount < 0) _overlayLoadingCount = 0;
}
var loadingImg = $(idImageLoading);
var overlay = $(loadingImg.closest("div.overlay"));
if (_overlayLoadingCount == 0) {
// Prevent short glimpse of prev. content before switch to new content
// caused by data load but not fully rendered
setTimeout(function() {
overlay.fadeOut();
}, 100);
}
else if (show && _overlayLoadingCount == 1) {
// only show once
loadingImg.html(message);
overlay.show();
}
}
function setBackgroundToLoading(elId, isLoading) {
if (isEmpty(el)) {
var elObj = $(elId);
if (isLoading) {
setTimeout(function() {
elObj.html("");
elObj.css({
"background-image": "url(" + urlImgLoading + ")",
"background-position": "center",
"background-repeat": "no-repeat"
});
}, 0);
}
else {
elObj.css("background-image", "");
}
}
}
function allowClick() {
return !$("body").hasClass(_dataLoadingFlag);
}
function imageExists(url, callback) {
var img = new Image();
img.onload = function() { callback(true); };
img.onerror = function() { callback(false); };
img.src = url;
}
function validateImageUrl(id, img) {
imageExists(img, function(exists) {
if (exists) {
$("#" + id).css({ "background-image": "url(" + url + ")", "background-size": "35px 35px"})
}
})
}
// Date picker inputs
/*
function hookupInputDatePickers(dateInputs, notFuture, notPast, parent, addClearOption) {
if (!isEmpty(dateInputs)) {
let currentInput, currentDateString, currentDate, exceptionsArray;
for (let i = 0; i < dateInputs.length; i++) {
currentInput = $(dateInputs[i]);
currentDateString = currentInput.val();
currentDate = (!isEmpty(currentDateString)) ? convertDDMMYYYYString2Date(currentDateString, false) : null;
exceptionsArray = (currentDate != null) ? [currentDate] : null;
turnInputIntoDatePicker(currentInput, notFuture, notPast, exceptionsArray);
}
if (!isEmpty(parent)) {
// stop user from manually typing date except backspace and delete
// which will clear the whole value to ensure we either have a whole
// date string or none
parent.on("keydown", isDatePickerSelector, function(event) {
if (event.keyCode == 46 | event.keyCode == 8) { // delete or backspace
$(this).val('');
}
else {
event.preventDefault();
event.stopPropagation();
}
return false
});
if (addClearOption) {
// if user right-clicks in date input, give option to clear the date
parent.contextMenu({
selector: isDatePickerSelector,
delay: 100,
autoHide: true,
position: function(opt, x, y) {
var event = opt.$trigger[0]?.ownerDocument?.defaultView?.event || event;
opt.$menu.position({ my: 'center top', at: 'center top', of: event });
},
items: {
"clears": {
name: "Clear Date",
icon: "delete",
disabled: function(key, opt) { return isEmpty($(opt.$trigger)); }, // if it's already empty, don't do anything
callback: function(itemKey, opt, rootMenu, originalEvent) { var input = $(opt.$trigger); input.val(''); input.trigger('change'); }
}
}
});
}
}
}
}
function turnInputIntoDatePicker(input, notFuture, notPast, exceptionValueArray) {
var beforeShowDayCallBack = null;
if (notFuture || notPast) {
var today = new Date();
today.setHours(0, 0, 0, 0);
var tomorrow = new Date();
tomorrow.setDate(today.getDate() + 1);
tomorrow.setHours(0, 0, 0, 0);
var hasExceptions = !isEmpty(exceptionValueArray);
beforeShowDayCallBack = function(date) {
var selectedDate = date.getTime();
var fieldHasException = hasExceptions && arrayContainsItem(exceptionValueArray, date);
if (notFuture && (tomorrow < selectedDate) && fieldHasException) return [false, 'redday', 'You cannot choose a future date'];
if (notPast && (selectedDate < today) && fieldHasException) return [false, 'redday', 'You cannot choose a past date'];
return [true, '', ''];
};
}
input.datepicker({
dateFormat: 'dd-mm-yy',
navigationAsDateFormat: true,
beforeShowDay: beforeShowDayCallBack
});
// prevent datepicker from appearing on right click
input.on('contextmenu', function() { $(this).datepicker('hide'); });
// Disable autocomplete suggestions appearing when clicking on input
input.attr('autocomplete', 'off');
}
function setDatePickerDate(input, objDate) {
if (!isEmpty(objDate)) {
input.val('');
}
else {
input.datepicker('setDate', objDate);
}
}
function getDatePickerDate(input, adjust4DayLightSavings) {
var date = null;
if (!isEmpty(input)) {
date = input.datepicker('getDate');
if (adjust4DayLightSavings) {
formatDateDayLightSavingsTime(date);
}
}
return date;
}
function formatDateDayLightSavingsTime(date) {
// JSON.stringify removes hour delta for daylight savings
// e.g. 13/11/2023 01:00:00 goes to 13/11/2023 00:00:00
// this adds an hour so it becomes the correct time when stringified
if (!isEmpty(date)) {
date.setTime(date.getTime() - date.getTimezoneOffset() * 60 * 1000)
}
}
*/
function convertJSONDateString2Date(dateStr) {
if (isEmpty(dateStr)) return null;
if (dateStr instanceof Date) return dateStr;
return new Date(parseInt(dateStr.substr(6)));
}
function convertDDMMYYYYString2Date(dateStr, adjust4DayLightSavings) {
var date = null;
if (!isEmpty(dateStr)) {
if (dateStr instanceof Date) {
date = dateStr;
}
else {
var dateParts = dateStr.split('-');
if (dateParts.length == 3) {
date = new Date(dateParts[2], dateParts[1] - 1, dateParts[0]);
}
}
if (adjust4DayLightSavings && !isEmpty(date)) {
formatDateDayLightSavingsTime(date);
}
}
return date;
}
function convertDate2DDMMYYYYString(date) {
if (isEmpty(date)) return '';
try {
var dd = date.getDate();
var mm = date.getMonth() + 1;
var yyyy = date.getFullYear();
if (dd < 10) dd = '0' + dd;
if (dd < 10) mm = '0' + mm;
return dd + '-' + mm + '-' + yyyy;
}
catch (err) {
return 'Formatting error';
}
}
// Data tables
function getDataTableCellByNode(table, elRow, indexColumn) {
// normal jQuery selector won't pick up hidden columns
return $(table.DataTable().cells(elRow, indexColumn, null).nodes());
}
function outputTableElementDateInput(table, elRow, indexColumn, value) {
let currentCell = getDataTableCellByNode(table, elRow, indexColumn);
let dateTxt = '';
if (!isEmpty(value)) {
if (typeof value === 'string') value = convertJSONDateString2Date(value);
}
}

View File

@@ -642,38 +642,6 @@ function setPageToLoading(isLoading) {
}
}
/*
function displayOverlay(message, show, force) {
if (show) {
_overlayLoadingCount += 1;
}
else if (force) {
_overlayLoadingCount = 0;
}
else {
_overlayLoadingCount -= 1;
if (_overlayLoadingCount < 0) _overlayLoadingCount = 0;
}
var loadingImg = $(idImageLoading);
var overlay = $(loadingImg.closest("div.overlay"));
if (_overlayLoadingCount == 0) {
// Prevent short glimpse of prev. content before switch to new content
// caused by data load but not fully rendered
setTimeout(function() {
overlay.fadeOut();
}, 100);
}
else if (show && _overlayLoadingCount == 1) {
// only show once
loadingImg.html(message);
overlay.show();
}
}
*/
function setBackgroundToLoading(elId, isLoading) {
@@ -702,23 +670,6 @@ function allowClick() {
return !$("body").hasClass(_dataLoadingFlag);
}
function imageExists(url, callback) {
var img = new Image();
img.onload = function() { callback(true); };
img.onerror = function() { callback(false); };
img.src = url;
}
function validateImageUrl(id, img) {
imageExists(img, function(exists) {
if (exists) {
$("#" + id).css({ "background-image": "url(" + url + ")", "background-size": "35px 35px"})
}
})
}
function getElementCurrentValue(el) {
let returnVal = '';
let element = $(el);
@@ -790,185 +741,7 @@ function getCellFromElement(element) {
return $(element).closest('td');
}
// Date picker inputs
/*
function hookupInputDatePickers(dateInputs, notFuture, notPast, parent, addClearOption) {
if (!isEmpty(dateInputs)) {
let currentInput, currentDateString, currentDate, exceptionsArray;
for (let i = 0; i < dateInputs.length; i++) {
currentInput = $(dateInputs[i]);
currentDateString = currentInput.val();
currentDate = (!isEmpty(currentDateString)) ? convertDDMMYYYYString2Date(currentDateString, false) : null;
exceptionsArray = (currentDate != null) ? [currentDate] : null;
turnInputIntoDatePicker(currentInput, notFuture, notPast, exceptionsArray);
}
if (!isEmpty(parent)) {
// stop user from manually typing date except backspace and delete
// which will clear the whole value to ensure we either have a whole
// date string or none
parent.on("keydown", isDatePickerSelector, function(event) {
if (event.keyCode == 46 | event.keyCode == 8) { // delete or backspace
$(this).val('');
}
else {
event.preventDefault();
event.stopPropagation();
}
return false
});
if (addClearOption) {
// if user right-clicks in date input, give option to clear the date
parent.contextMenu({
selector: isDatePickerSelector,
delay: 100,
autoHide: true,
position: function(opt, x, y) {
var event = opt.$trigger[0]?.ownerDocument?.defaultView?.event || event;
opt.$menu.position({ my: 'center top', at: 'center top', of: event });
},
items: {
"clears": {
name: "Clear Date",
icon: "delete",
disabled: function(key, opt) { return isEmpty($(opt.$trigger)); }, // if it's already empty, don't do anything
callback: function(itemKey, opt, rootMenu, originalEvent) { var input = $(opt.$trigger); input.val(''); input.trigger('change'); }
}
}
});
}
}
}
}
function turnInputIntoDatePicker(input, notFuture, notPast, exceptionValueArray) {
var beforeShowDayCallBack = null;
if (notFuture || notPast) {
var today = new Date();
today.setHours(0, 0, 0, 0);
var tomorrow = new Date();
tomorrow.setDate(today.getDate() + 1);
tomorrow.setHours(0, 0, 0, 0);
var hasExceptions = !isEmpty(exceptionValueArray);
beforeShowDayCallBack = function(date) {
var selectedDate = date.getTime();
var fieldHasException = hasExceptions && arrayContainsItem(exceptionValueArray, date);
if (notFuture && (tomorrow < selectedDate) && fieldHasException) return [false, 'redday', 'You cannot choose a future date'];
if (notPast && (selectedDate < today) && fieldHasException) return [false, 'redday', 'You cannot choose a past date'];
return [true, '', ''];
};
}
input.datepicker({
dateFormat: 'dd-mm-yy',
navigationAsDateFormat: true,
beforeShowDay: beforeShowDayCallBack
});
// prevent datepicker from appearing on right click
input.on('contextmenu', function() { $(this).datepicker('hide'); });
// Disable autocomplete suggestions appearing when clicking on input
input.attr('autocomplete', 'off');
}
function setDatePickerDate(input, objDate) {
if (!isEmpty(objDate)) {
input.val('');
}
else {
input.datepicker('setDate', objDate);
}
}
function getDatePickerDate(input, adjust4DayLightSavings) {
var date = null;
if (!isEmpty(input)) {
date = input.datepicker('getDate');
if (adjust4DayLightSavings) {
formatDateDayLightSavingsTime(date);
}
}
return date;
}
function formatDateDayLightSavingsTime(date) {
// JSON.stringify removes hour delta for daylight savings
// e.g. 13/11/2023 01:00:00 goes to 13/11/2023 00:00:00
// this adds an hour so it becomes the correct time when stringified
if (!isEmpty(date)) {
date.setTime(date.getTime() - date.getTimezoneOffset() * 60 * 1000)
}
}
*/
function convertJSONDateString2Date(dateStr) {
if (isEmpty(dateStr)) return null;
if (dateStr instanceof Date) return dateStr;
return new Date(parseInt(dateStr.substr(6)));
}
function convertDDMMYYYYString2Date(dateStr, adjust4DayLightSavings) {
var date = null;
if (!isEmpty(dateStr)) {
if (dateStr instanceof Date) {
date = dateStr;
}
else {
var dateParts = dateStr.split('-');
if (dateParts.length == 3) {
date = new Date(dateParts[2], dateParts[1] - 1, dateParts[0]);
}
}
if (adjust4DayLightSavings && !isEmpty(date)) {
formatDateDayLightSavingsTime(date);
}
}
return date;
}
function convertDate2DDMMYYYYString(date) {
if (isEmpty(date)) return '';
try {
var dd = date.getDate();
var mm = date.getMonth() + 1;
var yyyy = date.getFullYear();
if (dd < 10) dd = '0' + dd;
if (dd < 10) mm = '0' + mm;
return dd + '-' + mm + '-' + yyyy;
}
catch (err) {
return 'Formatting error';
}
}
// Textareas
function removeBlankTextAreaLines(textarea) {
@@ -1011,23 +784,6 @@ function fitTextAreaToContent(textarea) {
}
// Data tables
function getDataTableCellByNode(table, elRow, indexColumn) {
// normal jQuery selector won't pick up hidden columns
return $(table.DataTable().cells(elRow, indexColumn, null).nodes());
}
function outputTableElementDateInput(table, elRow, indexColumn, value) {
let currentCell = getDataTableCellByNode(table, elRow, indexColumn);
let dateTxt = '';
if (!isEmpty(value)) {
if (typeof value === 'string') value = convertJSONDateString2Date(value);
}
}
// Local storage
/*

View File

@@ -117,7 +117,7 @@ function callbackLoadPermutations(response) {
row.find('td.' + flagQuantityStock + ' input').val(dataRow[flagQuantityStock]);
row.find('td.' + flagQuantityMin + ' input').val(dataRow[flagQuantityMin]);
row.find('td.' + flagQuantityMax + ' input').val(dataRow[flagQuantityMax]);
row.find('td.' + flagCostLocal).html(dataRow[flagCostLocal]);
row.find('td.' + flagCostLocalVATIncl).html(dataRow[flagCostLocalVATIncl]);
row.attr(attrIdCategory, dataRow[flagCategory]);
row.attr(attrIdProduct, dataRow[flagProduct]);
row.attr(attrIdPermutation, dataRow[attrIdPermutation]);
@@ -135,13 +135,15 @@ function hookupButtonsSaveCancel() {
event.stopPropagation();
showOverlayConfirm();
});
btnSave.addClass(flagCollapsed);
let parentSave = btnSave.closest('div.' + flagColumn);
// parentSave.addClass(flagCollapsed);
btnCancel.on("click", function(event) {
event.stopPropagation();
loadPermutations();
});
btnCancel.addClass(flagCollapsed);
let parentCancel = btnCancel.closest('div.' + flagColumn);
// parentCancel.addClass(flagCollapsed);
btnAdd.on("click", function(event) {
event.stopPropagation();
@@ -231,7 +233,7 @@ function hookupTableMain() {
});
*/
let ddlCategory, ddlProduct, variations, quantityStock, quantityMin, quantityMax, costLocal;
let ddlCategory, ddlProduct, variations, quantityStock, quantityMin, quantityMax, costLocal, detail;
table.find('tbody tr').each(function(index, row) {
console.log("hooking up row ", index);
row = $(row);
@@ -241,7 +243,8 @@ function hookupTableMain() {
quantityStock = row.find('td.' + flagQuantityStock + ' input');
quantityMin = row.find('td.' + flagQuantityMin + ' input');
quantityMax = row.find('td.' + flagQuantityMax + ' input');
detail = row.find('td.' + flagDetail + ' button');
initialiseEventHandler(ddlCategory, flagInitialised, function() {
// ddlCategory = $(ddlCategory);
ddlCategory.on('change', function() {
@@ -273,8 +276,8 @@ function hookupTableMain() {
initialiseEventHandler(variations, flagInitialised, function() {
// variations = $(variations);
variations.on('change', function() {
handleChangeInputPermutations(this);
variations.on('click', function() {
handleClickPermutationsInputVariations(this);
});
});
@@ -300,6 +303,12 @@ function hookupTableMain() {
handleChangeInputPermutations(this);
});
});
initialiseEventHandler(detail, flagInitialised, function() {
detail.on('click', function() {
console.log("not implemented error: detail clicked");
});
});
});
}
@@ -313,7 +322,7 @@ function handleChangeInputPermutations(element) {
let wasDirty = isElementDirty(objJQuery);
if (objJQuery.hasClass(flagVariations)) {
objJQuery.attr(attrValueCurrent, getVariationsCurrentValue(objJQuery));
objJQuery.attr(attrValueCurrent, getProductVariationsText(objJQuery));
} else {
objJQuery.attr(attrValueCurrent, getElementCurrentValue(objJQuery));
}
@@ -368,7 +377,9 @@ function isRowDirty(row) {
return isDirty;
}
function getVariationsCurrentValue(element) {
function getProductVariationsText(element) {
element = $(element);
/*
let value = element.val();
let variations = value.split('\n');
variations = variations.map(function(variation) {
@@ -377,5 +388,113 @@ function getVariationsCurrentValue(element) {
variations = variations.filter(function(variation) {
return variation.length > 0;
});
return variations.join(',');
*/
let variations = dictVariations[element.attr(attrIdVariation)].map((variation, index) => {
return variation[keyNameVariationType] + ': ' + variation[keyNameVariation];
});
return variations.join(',\n');
}
function getElementProductVariations(element) {
element = $(element);
let variations = element.attr(attrValueCurrent);
let objVariations = [];
if (!isEmpty(variations)) {
variations = variations.split(',');
variations.forEach((variation) => {
let parts = variation.split(':');
if (parts.length == 2) {
objVariations.push({
[attrIdVariationType]: parts[0].trim(),
[attrIdVariation]: parts[1].trim(),
});
}
});
}
return objVariations;
}
function handleClickPermutationsInputVariations(element) {
element = $(element);
let jsonVariation, jsonVariationType, tr, tdVariationType, variationType, attributesVariationType, tdNameVariation, nameVariation, attributesNameVariation, tdDelete, buttonDelete, tmpJsonVariation, tmpJsonVariationType;
let variations = getElementProductVariations(element);
let tblVariations = $("<table>");
tblVariations.append("<thead><tr><th>Type</th><th>Name</th></tr></thead>");
let tbody = $("<tbody>");
console.log('variations:', variations);
if (isEmpty(variations)) {
return;
}
variations.forEach((variation, index) => {
jsonVariationType = dictVariations[variation[attrIdVariationType]];
jsonVariation = dictVariations[variation[attrIdVariation]];
tdVariationType = $("<td>", {
class: attrIdVariationType,
});
attributesVariationType = {
class: attrIdVariationType,
value: variation[attrIdVariationType],
};
attributesVariationType[attrValueCurrent] = jsonVariation[attrIdVariationType];
attributesVariationType[attrValuePrevious] = jsonVariation[attrIdVariationType];
variationType = $("<select>", attributesVariationType);
listVariationTypes.forEach((idVariationType) => {
tmpJsonVariationType = dictVariationTypes[idVariationType];
variationType.append($('<option>', {
value: jsonVariationType[attrIdVariationType],
text: jsonVariationType[keyNameVariationType],
selected: (idVariationType == jsonVariationType[attrIdVariationType]),
}));
});
tdNameVariation = $("<td>", {
class: attrIdVariation,
});
attributesNameVariation = {
class: attrIdVariation,
value: variation[attrIdVariation],
};
attributesNameVariation[attrValueCurrent] = jsonVariation[attrIdVariation];
attributesNameVariation[attrValuePrevious] = jsonVariation[attrIdVariation];
nameVariation = $("<select>", attributesNameVariation);
listVariations.forEach((idVariation) => {
tmpJsonVariation = dictVariations[idVariation];
console.log("id_variation: ", idVariation);
console.log("tmpJsonVariation: ", tmpJsonVariation);
nameVariation.append($('<option>', {
value: tmpJsonVariation[attrIdVariation],
text: tmpJsonVariation[keyNameVariation],
selected: (idVariation == jsonVariation[attrIdVariation]),
}));
});
tdDelete = $("<td>", {
class: flagDelete,
});
buttonDelete = $("<button>", {
class: flagDelete,
text: 'x',
});
tr = $("<tr>");
tdVariationType.append(variationType);
tr.append(tdVariationType);
tdNameVariation.append(nameVariation);
tr.append(tdNameVariation);
tdDelete.append(buttonDelete);
tr.append(tdDelete);
tbody.append(tr);
});
tr = $("<tr>");
let buttonAdd = $("<button>", {
class: flagAdd,
text: '+',
});
let tdAdd = $("<td>");
tdAdd.append(buttonAdd);
tr.append(tdAdd);
tbody.append(tr);
tblVariations.append(tbody);
let parent = element.parent();
parent.html('');
parent.append(tblVariations);
console.log("tblVariations: ", tblVariations);
}