Files
2026-06-06 18:43:00 +08:00

1724 lines
57 KiB
JavaScript

function hrefonclickButtonGO(pagename, pagedatastring = '') {
if (!pagename) { return false; }
return `href="javascript:void(0);" onclick="ButtonGo('${pagename}',${pagedatastring});return false;"`;
}
function generateHTMLfromarray(prepend, append, array, func) {
if (!func || !array) { return false; }
let htmlres = prepend;
array.forEach((value) => {
if (value.length > 0) {
htmlres += func(...value);
}
});
htmlres += append;
return htmlres;
}
function imgiconuserdefault(imgsrc, imgwidth = '', imgheight = '', id = '', onclick = '') {
if (!imgsrc) { return false; }
if (!imgwidth) { imgwidth = 30; }
if (!imgheight) { imgheight = 30; }
if (id) { id = 'id="' + id + '"'; } else { id = '' }
return `<img src="${imgsrc}" style="width: ${imgwidth}; height: ${imgheight};" ${id} onclick="${onclick}" class="icon-user"/>`;
}
function UICardStatsDetails(Title = '', number = 0, Unit = '', leftORright = 'left', numberid = '') {
if (!leftORright) { leftORright = 'left'; }
return `<div class="col br-right">
<div class="inner-${leftORright}">
<p>${Title}</p>
<h3 id="${numberid}">${number}</h3>
<span>${Unit}</span>
</div>
</div>`;
}
/**
* Generates an HTML string for displaying stats details.
*
* @param {array} statsarray - A 2D array containing stats data, where each inner array has the format [title, number, unit, leftorright,numberid]
* @returns {string} HTML string representing the stats details
*/
function UIStatsDetailsArray(statsarray) {
return generateHTMLfromarray('<div class="row">', '</div>', statsarray, UICardStatsDetails);
}
/**
* Generates an HTML string for displaying stats details.
*
* @param {array} statsarray - A 2D array containing stats data, where each inner array has the format [title, number, unit, leftorright,numberid]
* @returns {string} HTML string representing the stats details including the balance wrapper
*/
function UIBalance_WrapperfromArray(statsarray) {
if (!statsarray) { return false; }
let htmlres = `<div id="balance_wrapper">
<div class="balance">`;
htmlres += UIStatsDetailsArray(statsarray);
htmlres += '</div></div>'
return htmlres;
}
function UIBalance_Wrapper_WalletFooter_Item(maintitle = '', onclickstring = '', imgsrc = '', href = '', subtitleline1 = '', subtitleline2 = '', subtitleline3 = '', subtitleline4 = '', imgwidth = '', imgheight = '') {
if (!href) { href = "javascript:void(0);"; }
if (!imgwidth) { imgwidth = "30"; }
if (!imgheight) { imgheight = "30"; }
return `<li class="wallet-card-item">
<a class="fw_6 text-center" id="btn-popup-down" href="${href}"
onclick="${onclickstring}">
<ul>
<li class="path1">${subtitleline1}</li>
<li class="path2">${subtitleline2}</li>
<li class="path3">${subtitleline3}</li>
<li class="path4">${subtitleline4}</li>
</ul>
${imgiconuserdefault(imgsrc, imgwidth, imgheight)}
${maintitle}
</a>
</li>`;
}
function UIBalance_Wrapper_WalletFooter_Item_ButtonGO(maintitle, pagename, pagestring = '', iconclass = '', subtitleline1 = '', subtitleline2 = '', subtitleline3 = '', subtitleline4 = '') {
if (!pagename) { return false; }
const onclickstring = `ButtonGo('${pagename}','${pagestring}');return false;`;
//const iconclass =`icon far fa-hdd`;
const htmlres = UIBalance_Wrapper_WalletFooter_Item(maintitle, onclickstring, iconclass, false, subtitleline1, subtitleline2, subtitleline3, subtitleline4);
return htmlres;
}
/**
* Generates an HTML string for a balance wrapper item button array.
*
* @param {array} balancewrapper_item_array - A 2D array containing balance wrapper item data, where each inner array has the format [maintitle, pagename, pagestring, imagesrc, subtitleline1, subtitleline2, subtitleline3, subtitleline4,imgwidth,imgheight]
* @returns {string} HTML string representing the balance wrapper item button array
*/
function UIBalance_Wrapper_WalletFooter_ARRAY(balancewrapper_item_array) {
return generateHTMLfromarray(`<div class="wallet-footer">
<ul class="d-flex justify-content-between align-items-center">`, '</ul></div>', balancewrapper_item_array, UIBalance_Wrapper_WalletFooter_Item_ButtonGO);
}
/**
* Generates an HTML string for a balance wrapper item button array.
*
* @param {array} statsarray - A 2D array containing stats data, where each inner array has the format [title, number, unit, leftorright]
* @param {array} balancewrapper_item_array - A 2D array containing balance wrapper item data, where each inner array has the format [maintitle, pagename, pagestring, iconclass, subtitleline1, subtitleline2, subtitleline3, subtitleline4]
* @param {string} style - a string containing custom style for the balance box
* @returns {string} HTML string for the whole balance box
*/
function UIcreateBalanceBoxfromArray(statsarray, balancewrapper_item_array, style = 'border: solid 3px #000d88;') {
let htmlres = `<div class="tf-container">
<div class="tf-balance-box" style="${style}">`;
htmlres += UIBalance_WrapperfromArray(statsarray);
htmlres += UIBalance_Wrapper_WalletFooter_ARRAY(balancewrapper_item_array);
return htmlres;
}
function UIServices_Button(imgiconsrc, title = '', onclick = '', href = '', bgcolor8 = false) {
if (!bgcolor8) { bgcolor8 = '' } else { bgcolor8 = 'bg_color_8'; }
return `<li><a href="${href}" onclick="${onclick}"><div class="icon-box ${bgcolor8}" ><img src="${imgiconsrc}"/></div>${title}</a></li>`;
}
function UIServices_Button_GOTOPAGE(imgiconsrc, title, pagename, pagedatastring = '') {
return UIServices_Button(imgiconsrc, title, `ButtonGo('${pagename}','${pagedatastring}');return false;`, '');
}
function UIServices_FullDIV_GOTOPAGE_Array(title, viewallhref = '', viewallonclick = '', viewalltext = '', services_button_array) {
return generateHTMLfromarray(`<div class="mt-5">
<div class="tf-container">
<div class="tf-title d-flex justify-content-between">
<h3 class="fw_6">${title}</h3>
<a href="${viewallhref}" onclick="${viewallonclick}" class="primary_color fw_6">${viewalltext}</a>
</div>
<ul class="box-service mt-3">`, '</ul></div></div>', services_button_array, UIServices_Button_GOTOPAGE);
}
function UIServices_FullDIV_ONCLICK_Array(title, viewallhref = '', viewallonclick = '', viewalltext = '', services_button_array) {
return generateHTMLfromarray(`<div class="mt-5">
<div class="tf-container">
<div class="tf-title d-flex justify-content-between">
<h3 class="fw_6">${title}</h3>
<a href="${viewallhref}" onclick="${viewallonclick}" class="primary_color fw_6">${viewalltext}</a>
</div>
<ul class="box-service mt-3">`, '</ul></div></div>', services_button_array, UIServices_Button);
}
function UISideText_DualColumnButton(text, pagename, pagedatastring = '', imgsrc = '', imgwidth = '', imgheight = '') {
if (!pagename || !text) { return false; }
if (!imgwidth) { imgwidth = 30; } if (!imgheight) { imgheight = 30; }
return `<li onclick="ButtonGo('${pagename}','${pagedatastring}');return false;">
<div class="">
${imgiconuserdefault(imgsrc, imgwidth, imgheight)}
</div>${text}
</li>`;
}
function UISideText_DualColumnButton_Array(sidetext_button_dualcolumn_array) {
return generateHTMLfromarray('<ul class="mt-3 box-outstanding-service">', '</ul>', sidetext_button_dualcolumn_array, UISideText_DualColumnButton);
}
function UICardSimple(title, text = '', id = '') {
return CreateCardSimple(title, text, id);
}
function UIRecentSearchBar(searchplaceholdertext, classtosearch, id, imgsrclefticon = '', imgsrcrighticon = '', imgwidth = '', imgheight = '') {
if (!imgwidth) { imgwidth = 30; }
if (!imgheight) { imgheight = 30; }
if (!classtosearch) { classtosearch = ''; }
if (!imgsrclefticon) { imgsrclefticon = '<span class="icon-search"></span>'; } else {
imgsrclefticon = imgiconuserdefault(imgsrclefticon, imgwidth, imgheight);
}
if (!searchplaceholdertext) { searchplaceholdertext = 'Search'; }
let righticonhtml = '';
if (imgsrcrighticon) {
righticonhtml = imgiconuserdefault(imgsrcrighticon, imgwidth, imgheight, id + '-leftsearchicon');
} else {
righticonhtml = '';
}
return `<div class="box-search mt-3" >
<div class="input-field">
${imgsrclefticon}
<input id=${id} class="search-field value_input" placeholder="${searchplaceholdertext}" type="text">
<span class="icon-clear" id="clear-${id}"></span>
</div>
${righticonhtml}
</div>
<script>
$(document).ready(function() {
$('#${id}').on('keyup', function() {
let searchTerm_${id} = $(this).val().toLowerCase();
$('.list-card-invoice.${classtosearch}').filter(function() {
let contentText_${id} = $(this).find('.content-right h4 a').text().toLowerCase() +
$(this).find('.content-right p').text().toLowerCase();
});
});
$('#clear-${id}').on('click', function() {
$('#${id}').val('');
$('#${id}').trigger('keyup');
});
});
</script>
`;
}
function UIRecentSearchable_Button_GO(title, subtitle, rightsidetext, classforsearch, pagename, pagedatastring = '', imgsrc, imgwidth = '', imgheight = '') {
if (!pagename) { return false; }
if (!imgwidth) { imgwidth = 30; }
if (!imgheight) { imgheight = 30; }
if (!classforsearch) { classforsearch = ''; }
return `<li class="list-card-invoice ${classforsearch}">
<div class="logo">
<img src="${imgsrc}" style="width: ${imgwidth}; height: ${imgheight};" class="icon-user">
</div>
<div class="content-right">
<h4><a href="" onclick="ButtonGo('${pagename}',${pagedatastring});return false;">${title}
<span class="critical_color">${rightsidetext}</span></a></h4>
<p>
${subtitle}
</p>
</div>
</li>`;
}
function UIRecentSearchable_Button_GO_ARRAY(title, recentssearchablebuttonarray, viewallpagetarget, viewallpagedata = '', viewalltext = '') {
return generateHTMLfromarray(`<h3 class="fw_6 d-flex justify-content-between mt-3">${title}<a ${hrefonclickButtonGO(viewallpagetarget, viewallpagedata)}>${viewalltext}</a></h3>
<ul class="mt-3 mb-5">`, '</ul>', recentssearchablebuttonarray, UIRecentSearchable_Button_GO);
}
function UIListArrowButton_GO(title, subtitle, pagename, pagedatastring = '', imgsrc, imgwidth = '', imgheight = '') {
if (!pagename) { return false; }
if (!imgwidth) { imgwidth = 30; }
if (!imgheight) { imgheight = 30; }
return `<div class="list-bill-view mb-4">
${imgiconuserdefault(imgsrc, imgwidth, imgheight)}
<div class="content">
<h4><a ${hrefonclickButtonGO(PAGENAME, pagedatastring)} class="fw_6">${title}</a></h4>
<p>${subtitle}</p>
</div>
<i class="icon-right"></i>
</div>`;
}
function UIListArrowButton_GO_ARRAY(UIListArrowButtonGOARRAY) {
return generateHTMLfromarray('', '', UIListArrowButtonGOARRAY, UIListArrowButton_GO);
}
function UISearchBox_with_BUTTONS_FULL(title, recentssearchablebuttonarray, UIListArrowButtonGOARRAY = '', viewallpagetarget, viewallpagedata = '', viewalltext = '', classtosearch = '', searchid = '', searchplaceholdertext = '', searchlefticon = '', searchrighticon = '', imgwidth = '', imgheight = '') {
let htmlres = '';
if (classtosearch && searchid) {
htmlres += UIRecentSearchBar(searchplaceholdertext, classtosearch, searchid, searchlefticon, searchrighticon, imgwidth, imgheight);
}
htmlres += UIRecentSearchable_Button_GO_ARRAY(title, recentssearchablebuttonarray, viewallpagetarget, viewallpagedata, viewalltext);
if (UIListArrowButtonGOARRAY) {
htmlres += UIListArrowButton_GO_ARRAY(UIListArrowButtonGOARRAY);
}
return htmlres;
}
function UIinputgroupcore(innerhtml = '', label = '', inputgroupid = '') {
let htmllabel;
if (!label && !inputgroupid) { htmllabel = ''; }
else { htmllabel = `<label for="${inputgroupid}">${label}</label>`; }
return `${htmllabel}<div class="input-group mb-3" id="${inputgroupid}" style="">
${innerhtml}
</div>`;
}
//Input group
function UIInputGroup(placeholder, type, id, label = '', classinput = '', spanclass = '', imginsteadofspan = '', required = false, textvalue = '', datalist = '', imgwidth = '', imgheight = '', disabled = false) {
if (!required) { required = ''; } else { required = 'required'; }
if (!type) { return false; }
let span; let html; let inner; let labelid;
if (!type) { type = 'text'; }
if (!classinput) { classinput = 'form-control'; }
if (datalist) { datalist = ' list="' + datalist + '" '; } else { datalist = ''; }
span = `<span class="${spanclass}" id="${id}-span"></span>`;
if (!imgwidth) { imgwidth = '40px'; } if (!imgheight) { imgheight = '40px'; }
if (imginsteadofspan) { span = imgiconuserdefault(imginsteadofspan, imgwidth, imgheight, 'imgspan' + id); }
if (!imginsteadofspan && !spanclass) { span = ''; } else {
/*
span =`<div class="input-group-append">
<div class="input-group-text" id="${id}-input-group-text">
${span}
</div>
</div>`;
*/
}
if (disabled) {
disabled = ' disabled ';
}
if (label) { }
inner = `<input type="${type}" id="${id}" class="${classinput}" ${disabled} ${datalist} placeholder="${placeholder}" value="${textvalue}" name="${id}" ${required}>${span}`;
labelid = id + '-div-mb3';
html = UIinputgroupcore(inner, label, labelid);
return html;
}
function UIInputGroupFileUploadDropzone(id, label = '', url = '', inputgroupid = '', width = '100%', disabled = '') {
if (!url) { url = '/File/Upload/Unknown'; }
clearbuttonid = 'ClearUploadButton-' + id;
if (width) {
width = ' style="width: ' + width + ';" ';
} else {
width = '';
}
if (disabled) {
disabled = 'style=" pointer-events: none;"';
}
const clearuploadsbutton = ' ' + imgiconuserdefault('https://cdn.jsdelivr.net/gh/telemagnadon/obj-vault-3a@v2026.05.14-vendor-2/a/b6dc254166b4.bin', '', '', clearbuttonid, `LoadDataPageFunc.ClearUpload['${id}']();`);
return UIinputgroupcore('<form ' + disabled + ' action="' + url + '" ' + width + ' class="dropzone" id="' + id + '"></form>', label + clearuploadsbutton, inputgroupid);
}
function DropZoneLOADDATAPAGEFUNC(url, dropzoneid, acceptedfiles = '', maxsizeMB = 100) {
if (!url) { url = '/File/Upload/Unknown'; }
const clearbuttonid = 'ClearUploadButton-' + dropzoneid;
$('#' + clearbuttonid).hide();
window.Target_Uploaded_Files = [];
if (!acceptedfiles) { acceptedfiles = DropZoneFunc.AcceptedFilesString.images; }
if (typeof window.currentDropzone === 'undefined') { window.currentDropzone = {}; }
// Initialize Dropzone
const dzInstance = DropZoneFunc.InitializeDropZone(
url,
(response) => {
if (response.success && response.hashkey) {
if (!Target_Uploaded_Files.includes(response.hashkey)) {
Target_Uploaded_Files.push(response.hashkey);
}
} else {
dzInstance.removeFile(dzInstance.files[dzInstance.files.length - 1]);
}
if (Target_Uploaded_Files.length > 0) {
$('#' + clearbuttonid).show();
} else {
$('#' + clearbuttonid).hide();
}
},
dropzoneid,
acceptedfiles,
'file',
maxsizeMB,
false,
(errorresponse) => {
// handle error if needed
}
);
// Store globally
window.currentDropzone[dropzoneid] = dzInstance;
//dzInstance.dropzoneobject = dzInstance;
dzInstance.functions = {};
dzInstance.functions.AddPreviouslyUploadedFiles = function (filesarray) {
return DropZoneFunc.AddPreviouslyUploadedFiles(dzInstance, filesarray);
};
dzInstance.functions.ReplaceDropzoneFiles = function (newFilesArray) {
return DropZoneFunc.ReplaceDropzoneFiles(dropzoneid, newFilesArray);
};
dzInstance.functions.ResetDropzoneUpload = function (url, obj = null, reqtype = 'POST', successfunc = false) {
return DropZoneFunc.ResetDropzoneUpload(url, obj, reqtype, successfunc);
};
dzInstance.functions.ClearDropzoneUpload = function () {
return DropZoneFunc.ClearDropzoneUpload(dropzoneid, dzInstance);
};
dzInstance.functions.hasOngoingUploads = function () {
return DropZoneFunc.hasOngoingUploads(dzInstance);
};
dzInstance.functions.RemoveLastUploadedDropzone = function () {
return DropZoneFunc.RemoveLastUploadedDropzone(dzInstance);
};
dzInstance.functions.AddFiles = function (dropzonemodal = 'Dropzone-Modal', errorcallback) {
return DropZoneFunc.AddFiles(url = '/File/Upload', errorcallback, filename = 'file', maxfilesize = 100, acceptedFiles, dropzoneid, dropzonemodal);
};
if (typeof LoadDataPageFunc.ClearUpload === 'undefined') {
LoadDataPageFunc.ClearUpload = {};
}
LoadDataPageFunc.ClearUpload[dropzoneid] = function () {
$('#' + clearbuttonid).hide();
DropZoneFunc.ClearDropzoneUpload(dropzoneid, dzInstance);
};
}
function InitializeLoadDataPageFuncDropZonePhotoUpload(url, dropzoneid, acceptedfiles = '', maxsizeMB = 100) {
if (typeof LoadDataPageFunc === 'undefined') { return false; }
// console.log('initializing photodropzone');
LoadDataPageFunc.InitializePhotoDropZone = function () { return DropZoneLOADDATAPAGEFUNC(url, dropzoneid, acceptedfiles, maxsizeMB); };
}
function SendPostDataURLData(url, data, sucessfunc, errorfunc = false, fromvarcache = false) {
if (!url || !data) { return false; }
let SendPostDataReqq = new RequestData(false);
SendPostDataReqq.url(url).type('POST').data(data)
.fromVarCache(fromvarcache)
.success((response) => {
if (typeof sucessfunc === 'function') { sucessfunc(response); }
}).error((response) => {
if (typeof errorfunc === 'function') { errorfunc(response); }
}).go();
}
function getInputAndTextareaValuesbyCSSClassName(className) {
const elements = document.getElementsByClassName(className);
const results = [];
elements.forEach(element => {
const id = element.id;
const value = element.value;
results.push({ id, value });
});
return results;
}
function getInputElementsValuesObjectbyCSSClassname(classname) {
const elements = document.getElementsByClassName(classname);
const results = {};
elements.forEach(element => {
const id = element.id;
const value = element.value;
results[id] = value;
});
return results;
}
// Helper function to extract hashkey from response (handles both string and JSON object responses)
function getHashkeyFromResponse(response) {
if (!response) return null;
// If response is already a string/hash key
if (typeof response === 'string') {
return response;
}
// If response is an object with hashkey property (JSON response from backend)
if (response && typeof response.hashkey === 'string') {
return response.hashkey;
}
return null;
}
// Helper function to extract hashkey and call callback with it
function getResponseHashkeyOrErrorMessage(response, successCallback, errorCallback) {
const hashkey = getHashkeyFromResponse(response);
if (hashkey) {
successCallback(hashkey);
} else {
// Check if response is an error object
if (response && typeof response === 'object' && !Array.isArray(response)) {
const errorMessage = response.message || response.error || 'Unknown error';
errorCallback(errorMessage);
} else {
errorCallback('Invalid response format');
}
}
}
function SendPostDataFormwithTARGETUPLOADEDFILES(url, forminputclass, successfunc, WithTarget_Uploaded_Files = true, errorfunc = false) {
if (!url || !forminputclass || !successfunc) { return false; }
let datatosend = getInputElementsValuesObjectbyCSSClassname(forminputclass);
if (isObjectEmpty(datatosend)) { return false; }
if (WithTarget_Uploaded_Files && typeof window.Target_Uploaded_Files !== 'undefined' && window.Target_Uploaded_Files.length > 0) {
datatosend['files'] = Target_Uploaded_Files;
}
let target;
let data = null;
if (typeof currenttarget === 'object') {
try {
target = currenttarget['target'];
} catch (error) {
target = currenttarget;
}
try {
data = currenttarget['data'];
} catch (error) {
data = null;
}
} else {
target = currenttarget;
data = null;
}
if (typeof datatosend.target === 'undefined') {
datatosend['target'] = target;
datatosend['data'] = data;
}
// Wrap successfunc to extract hashkey from JSON responses
const wrappedSuccessFunc = function(response) {
const hashkey = getHashkeyFromResponse(response);
if (hashkey) {
successfunc(hashkey);
} else {
// Pass original response for error handling
successfunc(response);
}
};
SendPostDataURLData(url, datatosend, wrappedSuccessFunc, errorfunc, fromvarcache = false);
}
function UIInputGroupTEXTAREA(label = '', textareaID = '', required = false, textareaCONTENT = '', textareaCLASS = '', inputgroupid = '', disabled = '') {
if (required) { required = ' required '; } else { required = ''; }
if (disabled) {
disabled = 'disabled'
}
const innerhtml = `<textarea ${disabled} id="${textareaID}" name="${textareaID}" ${required} class="${textareaCLASS}">${textareaCONTENT}</textarea>`;
return UIinputgroupcore(innerhtml, label, inputgroupid);
}
function UIInputGroupSelect(id = '', label = '', optionsarray = '', spanclass = '', imginsteadofspan = '', selectedvalue = '', imgwidth = '', imgheight = '', select_class = '', disabled = '') {
//optionsarray =[['value1','text1'],['value2','text2',selectedtrue] ]
let inner; let labelid; let htmlspan; let optionshtml = '';
if (!spanclass && !imginsteadofspan) { htmlspan = ''; }
if (!imgwidth) { imgwidth = '40px'; } if (!imgheight) { imgheight = '40px'; }
optionshtml = UIArraytoOptionforSelect(optionsarray, selectedvalue);
if (spanclass) {
htmlspan = `<span class="spanclass"></span>`;
}
if (imginsteadofspan) {
htmlspan = imgiconuserdefault(imginsteadofspan, '10px', '10px');
}
if (!spanclass && !imginsteadofspan) {
htmlspan = '';
} else {
htmlspan = `<div class="input-group-append">
<div class="input-group-text" id="${id}-input-group-text">
${htmlspan}
</div>
</div>`;
}
if (disabled) {
disabled = 'disabled';
} else {
disabled = '';
}
inner = `<select ${disabled} class="form-control ${select_class}" id="${id}">
${optionshtml}
</select>${htmlspan}`;
labelid = id + '-select-div-mb3';
html = UIinputgroupcore(inner, label, labelid);
return html;
}
function UIInputGroupDatePicker() {
}
function UIArraytoOptionforSelect(array, selectedvalue = '') {
if (!array) { return ''; } let optionshtml = '';
let val; let selected;
for (let i = 0; i < array.length; i++) {
val = array[i];
// selected = (val[2] !== undefined) ? val[2] : '';
selected = '';
if (selectedvalue !== '' && selectedvalue == val[0]) { selected = 'selected'; }
optionshtml += `<option value="${val[0]}" ${selected}>${val[1]}</option>`;
}
return optionshtml;
}
function UIReplaceCurrentOptionsSelect(selectid, optionsarray, selectedvalue = '') {
if (!selectid || !optionsarray) { return false; }
let optionshtml = UIArraytoOptionforSelect(optionsarray, selectedvalue);
$('#' + selectid).html(optionshtml);
}
function UIInputGroupNumber(placeholder, id, label = '', classinput = '', min = '', max = '', spanclass = '', imginsteadofspan = '', required = false, val = 0, datalist = '', imgwidth = '', imgheight = '', disabled = false) {
let UI = UIInputGroup(placeholder, 'number', id, label, classinput, spanclass, imginsteadofspan, required, val, datalist, imgwidth, imgheight, disabled);
// console.log('placeholder ',placeholder,' label ', label);
UI = '<div>' + UI + '</div>';
UI = $(UI);
let input = UI.find('input');
input.attr('min', min);
input.attr('max', max);
input.val(val);
return UI.html();
}
function UIInputGroupButton(buttontext, buttonclass = '', buttonid = '', onclick = '', buttonstyle = '') {
if (!buttontext) { return false; }
if (!buttonclass) { buttonclass = 'btn-primary'; }
return `<div class="input-group mb-3">
<button class="form-control ${buttonclass}" id="${buttonid}" onclick="${onclick}" style="${buttonstyle}">${buttontext}</button>
</div>`;
}
function UIInputGroupCheckBox(input_id, label, checked = false, additional_input_class = '', additional_label_class = '') {
if (checked === true) { checked = 'checked'; } else {
checked = '';
}
return `<div class="form-check">
<input class="form-check-input ${additional_input_class}" type="checkbox" value="" ${checked} id="${input_id}">
<label class="form-check-label ${additional_label_class}" for="${input_id}">
${label}
</label>
</div>`
}
function UISetDarkMode() {
$('body').addClass('dark-mode'); $('.tf-balance-box').removeAttr('style');
$('.card').addClass('dark-mode');
$('.card-header').addClass('dark-mode');
$('.card-body').addClass('dark-mode');
$('.modal-content').addClass('dark-mode');
$('.btn').addClass('dark-mode');
$('input').addClass('dark-mode');
$('textarea').addClass('dark-mode');
$('select').addClass('dark-mode');
$('form').addClass('dark-mode');
}
function UIUpdateBodyHTML(html = '') {
$('#main-body').html(html);
}
function changeTopbarTitle(title) {
$('#topbar-title').html(title);
}
function setDefaultBackOnclickifNoHistory(functtoCall) {
if (!functtoCall) {
defaultBackOnclick = null;
}
if (historylist.length < 2) {
if (typeof functtoCall === 'function') {
defaultBackOnclick = functtoCall;
}
}
}
function setDefaultbackGotoPageifNoHistory(pageName, targetdata) {
const functiongoto = function () {
gotoPage(pageName, targetdata);
};
setDefaultBackOnclickifNoHistory(functiongoto);
}
function getcurrenttargetHash() {
const url = new URL(window.location.href);
const parts = url.pathname.split('/').filter(Boolean);
return parts.at(-1) || null;
}
function requestGetData(targetURL, functionsuccess, cachefirst = false) {
request.url(targetURL)
.success((response) => {
if (typeof functionsuccess === 'function') {
functionsuccess(response);
}
})
.data()
.fromVarCache(cachefirst)
.type("GET")
.go();
}
let ElementDOM = {};
function getUIElementsAndAttributes() {
const uiElements = document.querySelectorAll('UI');
const uiData = {};
uiElements.forEach(uiElement => {
const attributes = {};
attributes.innerHTML = uiElement.innerHTML;
let Elementname = uiElement.id;
if (!Elementname) {
Elementname = uiElement.attributes.name.value;
}
console.log(Elementname);
Array.from(uiElement.attributes).forEach(attr => {
attributes[attr.name] = attr.value;
});
uiData[Elementname] = attributes;
uiData[Elementname].id = uiElement.id;
uiData[Elementname].name = uiElement.attributes.name.value;
});
return uiData;
}
function CheckChangesVariableVSDOM(domID) {
if (!domID) { return null; }
const DOMElementData = getUIElementsAndAttributes()[domID] || false;
if (!DOMElementData) { return null; }
const VarElementData = ElementDOM[domID] || false;
}
function UIElementsUpdateCard(elementData) {
if (!elementData) { return false; }
}
const ReplaceMainViewORTargetDiv = function (html, targetDiv, append = '', removeDivID = '') {
if (!targetDiv) {
targetDiv = 'main-body';
}
if (typeof append === 'undefined' || !append) {
append = '';
}
let elem = document.getElementById(targetDiv);
if (targetDiv === 'main-body') {
const mainview = document.getElementById('MainView');
if (mainview) {
elem = mainview;
} else {
elem.innerHTML = '<div id="MainView" class="MainView"></div>' + elem.innerHTML;
}
}
if (removeDivID) {
let removeElem = document.getElementById(removeDivID);
if (removeElem) {
removeElem.remove();
logDev('Removed element with ID: ' + removeDivID);
} else {
logDev(`Element with ID: ${removeDivID} not found.`);
}
}
html += '</div>';
html += append || '';
document.getElementById(targetDiv).innerHTML = html;
// logDev(`Element with ID: ${targetDiv} replaced html with. ${html}`);
}
const processTopBarTitleAndDefaultBack = function (topbartitle, defaultback) {
if (typeof topbartitle !== 'undefined') {
changeTopbarTitle(topbartitle);
}
if (typeof defaultback !== 'undefined' && typeof defaultback.page !== 'undefined' && typeof defaultback.data !== 'undefined') {
setDefaultbackGotoPageifNoHistory(defaultback.page, defaultback.data || 0);
}
}
function setDynamicCSS(cssText) {
let styleTag = document.getElementById("dynamic-css");
if (!styleTag) {
styleTag = document.createElement("style");
styleTag.id = "dynamic-css";
document.head.appendChild(styleTag);
}
styleTag.innerHTML = cssText;
}
function resetDynamicCSS() {
setDynamicCSS('');
}
const FormBuilder = {
config: {},
dropzones: {},
build: function (options) {
this.config = options;
const { targetDiv, fields, formClass, append, topbartitle, defaultback, onFormGenerated } = options;
processTopBarTitleAndDefaultBack(topbartitle, defaultback);
// document.getElementById(targetDiv).innerHTML = '<center>Please Wait..<center>';
ReplaceMainViewORTargetDiv('<div id="loadingText"><center>Please Wait..<center></div>', targetDiv);
let requestData;
let targetHash, targetData;
if (typeof currenttarget === 'object') {
try {
targetHash = currenttarget['target'];
} catch (error) {
targetHash = currenttarget;
}
} else {
targetHash = currenttarget;
}
try {
targetData = currenttarget['data'];
} catch (error) {
targetData = [];
}
if (typeof this.config.requestData === 'undefined') {
requestData = { 'target': targetHash, 'data': targetData };
} else {
requestData = this.config.requestData;
}
loadDataWithCache({
url: this.config.currentDataUrl,
current_page: this.config.pageName,
executeFunction: function (response) {
const html = this.createForm(fields, targetDiv, formClass, append);
if (typeof onFormGenerated === 'function') {
onFormGenerated(response, html);
}
}.bind(this),
'requestData': requestData,
'errorFunction': this.config.unabletoLoadFunc,
});
},
createForm: function (fields, targetDiv, formClass, append) {
let html = '';
fields.forEach(field => {
const currentDataVal = currentData[field.currentdatavar] || '';
const valuetoDisplay = field.value || currentDataVal || '';
let newValue;
const datalistMaker = function (id) {
return ' <datalist id="' + id + 'DataList"></datalist> ';
}
switch (field.type) {
case 'text':
html += UIInputGroup(field.placeholder || '', 'text', field.id, field.label, formClass,
'', '', field.required, valuetoDisplay, field.datalist || '', field.image_width || '', field.image_height || '', field.disabled || false) + datalistMaker(field.id);
break;
case 'number':
html += UIInputGroupNumber(field.placeholder || '', field.id, field.label, formClass,
1, '', '', '', field.required, valuetoDisplay, field.datalist || '', field.image_width || '', field.image_height || '', field.disabled || false) + datalistMaker(field.id);
break;
case 'textarea':
html += UIInputGroupTEXTAREA(field.label, field.id, field.required || false, valuetoDisplay, formClass, '', field.disabled || '');
break;
case 'dropzone':
html += UIInputGroupFileUploadDropzone(field.id, field.label, field.uploadUrl, '', 'width:100%', field.disabled || '');
break;
case 'select':
html += UIInputGroupSelect(field.id, field.label, valuetoDisplay || '', field.spanclass || '', field.imginsteadofspan || '', field.selectedvalue || '', field.imgwidth || '', field.imgheight || '', formClass || '', field.disabled || false)
break;
case 'button':
html += UIInputGroupButton(field.label, formClass, '', field.onClick || 'FormBuilder.submit()');
break;
}
});
html = CreateCardSimple(false, html);
ReplaceMainViewORTargetDiv(html, targetDiv, append, 'loadingText');
this.initializeDropzones(fields);
this.enforceBarcodeNumeric(fields);
return html;
},
initializeDropzones: function (fields) {
fields.filter(f => f.type === 'dropzone').forEach(f => {
// InitializeLoadDataPageFuncDropZonePhotoUpload(
// f.uploadUrl,
// f.id,
// '',
// 'clearuploadbutton',
// '',
// 100
// );
DropZoneLOADDATAPAGEFUNC(f.uploadUrl, f.id, '', f.maxsize);
const currentDataVal = currentData[f.currentdatavar] || '';
this.dropzones[f.id] = currentDropzone[f.id];
if (f.existingFiles && Array.isArray(f.existingFiles) && f.existingFiles.length > 0) {
this.dropzones[f.id].functions.ReplaceDropzoneFiles(f.existingFiles);
console.log(`[FormBuilder] Restored ${f.existingFiles.length} files into dropzone ${f.id}`);
} else if (currentDataVal) {
this.dropzones[f.id].functions.ReplaceDropzoneFiles(currentDataVal);
console.log(`[FormBuilder] Restored ${currentDataVal.length} files into dropzone ${f.id}`);
}
});
},
enforceBarcodeNumeric: function (fields) {
fields.filter(f => f.id && f.id.toLowerCase().includes('barcode')).forEach(f => {
const input = document.getElementById(f.id);
if (input) {
input.addEventListener('input', function () {
this.value = this.value.replace(/[^0-9]/g, '');
});
}
});
},
validate: function () {
const inputs = getInputElementsValuesObjectbyCSSClassname(this.config.formClass);
if (isObjectEmpty(inputs)) return false;
const invalid = Object.keys(inputs).some(k => {
const val = inputs[k];
return (!val && document.getElementById(k)?.required);
});
if (invalid) {
ModalQuickDismiss('Error', 'Please fill out all required fields.');
return false;
}
return true;
},
submit: function () {
if (!this.validate()) return;
const { submitUrl, formClass, onSuccess, onError } = this.config;
SendPostDataFormwithTARGETUPLOADEDFILES(submitUrl, formClass, (response) => {
getResponseHashkeyOrErrorMessage(response, (hashkey) => {
if (onSuccess) onSuccess(response);
}, (err) => {
// if (onError) onError(err);
});
}, true, function (err) { if (onError) onError(err); });
}
};
const ViewBuilder = {
config: {},
loadPhotos: [],
targetData: null,
targetHash: null,
build: function (options) {
this.config = options;
let { targetDiv, sections, dataurl, pageName, append, topbartitle, defaultback, initialize } = options;
logDev('viewbuilder starting build with options:', options);
processTopBarTitleAndDefaultBack(topbartitle, defaultback);
this.targetData = currenttarget;
if (typeof this.targetData === 'object') {
this.targetHash = this.targetData.target;
} else {
this.targetHash = this.targetData;
}
let requestData;
if (typeof this.config.requestData === 'undefined') {
requestData = { 'target': this.targetHash, 'data': this.targetData };
} else {
requestData = this.config.requestData;
}
logDev('viewbuilder requestData set to:', requestData);
const renderErrorPage = (response) => {
sections = null;
sections = [{
type: 'card',
id: 'errorCard',
hidden: false,
value: this.config.defaultErrorHTML || 'Error Loading Data',
}];
logDev('viewbuilder Error:');
this.buildhtml(sections, targetDiv, append);
}
logDev('viewbuilder loading data from URL:', dataurl);
loadDataWithCache({
url: dataurl,
current_page: pageName,
executeFunction: function (response) {
logDev('viewbuilder Response Data:', response);
if (typeof initialize === 'function') {
initialize(response);
logDev('Initialize function ran after response:');
}
if (!response) {
logDev('viewbuilder No response received, rendering error page:');
renderErrorPage(response);
}
try {
logDev('viewbuilder building html with sections:', sections);
this.buildhtml(sections, targetDiv, append);
} catch (error) {
logDev('viewbuilder Error building html:', error);
console.error(error);
renderErrorPage(response);
}
}.bind(this),
'requestData': requestData,
'errorFunction': function (err) {
logDev('viewbuilder Error loading data:', err);
if (typeof this.config.unabletoLoadFunc === 'function') {
this.config.unabletoLoadFunc();
}
}.bind(this),
});
},
getValorCurrentData: function (section) {
if (!section) {
logDev('viewbuilder getValorCurrentData called with no section, returning null.');
return null;
}
let currentDataVal = null;
if (
typeof currentData !== 'undefined' &&
section.currentdatavar &&
currentData[section.currentdatavar] !== undefined
) {
currentDataVal = currentData[section.currentdatavar];
}
return section.value ?? currentDataVal ?? null;
},
buildhtml: function (sections, targetDiv, append) {
logDev('viewbuilder building html with sections:', sections);
let html = '<div class="row">';
sections.forEach((section, i) => {
html += this.renderSection(section, i);
});
if (!targetDiv) {
targetDiv = 'main-body';
}
// let elem = document.getElementById(targetDiv);
html += '</div>';
html += append || '';
// document.getElementById(targetDiv).innerHTML = html;
ReplaceMainViewORTargetDiv(html, targetDiv, append);
if (html && targetDiv) {
logDev('viewbuilder replaced main view or target div with html:' + html);
} else {
logDev('viewbuilder html or targetDiv is empty, skipping ReplaceMainViewORTargetDiv.');
}
this.postRender();
},
renderSection: function (section, index) {
switch (section.type) {
case 'photos':
return this.renderPhotosCard(section, index);
case 'details':
return this.renderDetailsCard(section, index);
case 'buttons':
return this.renderButtonsCard(section, index);
case 'card':
return this.renderCustomCard(section, index);
default:
return '';
}
},
renderPhotosCard: function (section, index) {
logDev('viewbuiler Rendering Photos Card for section:', section);
if (typeof section.target === 'undefined' || !section.target) { section.target = this.targetHash; }
Preloaders.ImageList(section.target, section.phototype);
const photosData = this.getValorCurrentData(section);
let loadingText = 'Loading photos...';
if (photosData) {
this.loadPhotos.push(photosData);
} else {
loadingText = 'No Photos';
}
logDev('viewbuilder Rendering Photos Card Success', section);
return `
<div class="col-md-12" style="overflow:hidden; margin:5px;">
<div class="card ListRowCard" id="PhotosCard-${index}">
<div class="card-body ListCardRow">
<div style="text-align:center;" id="${section.id || 'PhotosCard'}">
<center>
<div id="PhotosPlaceholder-${index}"
style="width: 100%; max-width: 600px; height: 350px; display: flex; justify-content: center; align-items: center; background: #f0f0f0; color: #555; font-size: 18px;">
${loadingText}
</div>
</center>
</div>
</div>
</div>
</div>`;
},
renderDetailsCard: function (section, index) {
let htmlcols = '';
logDev('viewbuiler Rendering Details Card for section:', section);
const detailscol = section.data;
detailscol.forEach((arrVar) => {
if (arrVar.type !== 'text') return;
let textToDisplay = this.getValorCurrentData(arrVar);
if (typeof arrVar.func === 'function') {
try {
textToDisplay = arrVar.func();
} catch (error) {
textToDisplay = '';
}
}
textToDisplay = this.detailsCard.text(textToDisplay);
if (typeof arrVar.bold === 'boolean' && arrVar) {
textToDisplay = '<b>' + textToDisplay + '</b>';
}
if (typeof arrVar.h === 'number' && arrVar) {
textToDisplay = '<h' + arrVar.h + '>' + textToDisplay + '</h' + arrVar.h + '>';
}
if (textToDisplay) {
htmlcols += this.detailsCard.text(textToDisplay);
} else {
console.warn('No text or current data available for:', arrVar);
}
});
logDev('viewbuilder Rendering Details Card Success', section);
return `
<div class="col-md-12" style="overflow:hidden; margin:5px;">
<div class="card ListRowCard" id="DetailsCard-${index}">
<div class="card-body ListCardRow">
<div class="row">
${htmlcols}
</div>
</div>
</div>
</div>`;
},
detailsCard: {
text: function (text) {
return `<div class="col">${text}</div>`;
},
},
renderButtonsCard: function (section, index) {
logDev('viewbuiler Rendering Buttons Card for section:', section);
const buttonsHTML = section.buttons.map(btn => `
<li onclick="${btn.onclick}">
<div><img src="${btn.icon}" style="width:30px;height:30px;"></div>
${btn.label}
</li>`).join('');
logDev('viewbuilder Rendering Buttons Card Success', section);
return `
<div class="col-md-12" style="overflow:hidden; margin:5px;">
<div class="card ListRowCard" id="ButtonsCard-${index}">
<div class="card-body ListCardRow">
<ul class="mt-3 box-outstanding-service">${buttonsHTML}</ul>
</div>
</div>
</div>`;
},
renderCustomCard: function (section, index) {
let data = this.getValorCurrentData(section);
logDev('viewbuiler Rendering Custom Card for section:', section, 'with data:', data);
if (typeof section.render === 'function') {
data = section.render(data);
}
logDev('viewbuilder Rendering Custom Card Success', section);
return `
<div class="col-md-12" style="overflow:hidden;margin:5px; min-height: 100px;${section.hidden ? 'display:none;' : ''}">
<div class="card ListRowCard" id="${section.id || 'CustomCard-' + index}" style="height:100%;">
<div class="card-body ListCardRow">${data || ''}</div>
</div>
</div>`;
},
postRender: function () {
logDev('viewbuilder postRender starting photo loads for:', this.loadPhotos);
this.loadPhotos.forEach((arrVar) => {
if (Array.isArray(arrVar) || typeof arrVar === 'object') {
LoadPhotosCard('PhotosCard', `ButtonGo('ViewAllPhotos','${this.targetHash}');`, arrVar);
}
});
}
};
const ListBuilder = (() => {
const defaultConfig = {
targetDiv: "",
title: "List",
dataUrl: "",
method: "POST",
search: true,
pageName: "",
cols: 6,
type: "tiled",
defaultback: "",
renderCard: null,
customCss: null,
width: null,
height: null,
card: {
imageField: null,
nameField: null,
descriptionField: null,
priceField: null,
unitField: null,
onClick: null,
fallbackImage: "https://cdn.jsdelivr.net/gh/telemagnadon/obj-vault-3a@v2026.05.14-vendor-2/a/146710fe9ece.bin",
}
};
function build(config) {
if (config.defaultback) {
setDefaultbackGotoPageifNoHistory(config.defaultback, currenttarget);
}
config = deepMerge(structuredClone(defaultConfig), config);
if (!config.targetDiv) {
console.error("ListBuilder: targetDiv is required");
return;
}
setupUI(config);
loadList(config);
}
function setupUI(config) {
resetDynamicCSS();
let searchHTML = "";
if (config.search) {
searchHTML = UIInputGroup(
"Search",
"text",
"LB_Search",
"",
"",
"",
"https://cdn.jsdelivr.net/gh/telemagnadon/obj-vault-3a@v2026.05.14-vendor-2/a/839d72e8ef8a.bin"
);
}
const body = `
${searchHTML}
<div id="LB_ListContainer" style="padding:5px;">
<center>Loading Please Wait...</center>
</div>
`;
const mainCard = UICardSimple(config.title, body, "LB_MainCardBody");
$("#" + config.targetDiv).html(mainCard);
$("#imgspanLB_Search").attr("onclick", "ListBuilder.clearSearch()");
$("#LB_Search").on("keyup", ListBuilder.filterList);
}
function loadList(config) {
// loadDataWithCache({
// url: config.dataUrl,
// currentPage: this.config.pageName,
// executeFunction: function () {
// this.createForm(fields, targetDiv, formClass, append);
// }.bind(this),
// 'requestData': requestData,
// 'errorFunction': this.config.unabletoLoadFunc,
// });
loadDataWithCache({
url: config.dataUrl,
type: config.method,
current_page: config.pageName,
executeFunction: function (response) {
if (config.customCss !== null) {
setDynamicCSS(config.customCss);
} else if (config.type === 'tiled') {
setCSStoTiled();
}
renderList(response, config)
changeTopbarTitle(config.title);
},
'errorFunction': config.unabletoLoadFunc,
});
}
function setCSStoTiled() {
const tiledcss = `.ListRowCard {
height: 100%;
display: flex;
flex-direction: column;
}
.ListCardRow {
flex: 1;
}
.equal-height {
display: flex;
}`;
setDynamicCSS(tiledcss);
}
function renderList(data, config) {
if (!Array.isArray(data)) data = [];
if (data.length === 0) {
$("#LB_ListContainer").html("<center>No Data</center>");
return;
}
let html = [`<div class="row">`];
data.forEach((item, index) => {
html.push(renderCard(item, index, config));
});
html.push(`</div>`);
$("#LB_ListContainer").html(html.join(""));
postLoadImages();
setupIntersectionObserver();
}
function renderCard(item, index, config) {
if (typeof config.renderCard === 'function') {
return config.renderCard(item, index, config);
}
const c = config.card;
let img = item[c.imageField];
if (Array.isArray(img)) img = img[0];
const imageId = `LB_photo_${index}`;
queueImageLoad(imageId, img);
let imageHtml = `
<img id="${imageId}" src="${c.fallbackImage}" loading="lazy"
style="width: 60%; height: 60%; object-fit: contain;">
`;
let description = item[c.descriptionField] || "";
if (description.length > 80) {
description = description.substring(0, 80) + "...";
}
const clickAction = c.onClick ? c.onClick(item) : "javascript:void(0)";
let width = '';
let height = '';
if (config.width) {
width = ' width: ' + config.width + '; ';
}
if (config.height) {
height = ' height: ' + config.height + '; ';
}
return `
<div class="col-${config.cols}" class="equal-height" style="overflow:hidden; padding-bottom:10px; ${width} ${height}">
<a href="javascript:void(0);" onclick="${clickAction}">
<div class="card ListRowCard" id="LB_Card_${index}">
<div class="card-body ListCardRow">
<div style="text-align:center;">${imageHtml}</div>
<br>
<div class="row">
<div class="col"><h3>${item[c.nameField]}</h3></div>
<div class="col">P${item[c.priceField]} / ${item[c.unitField]}</div>
</div>
<br>
<h5>${description}</h5>
<br>
</div>
</div>
</a>
</div>`;
}
/* ------------------------------ IMAGE HANDLING ------------------------------ */
const preloadQueue = [];
function queueImageLoad(imageId, fileHash) {
preloadQueue.push([imageId, fileHash]);
}
function postLoadImages() {
preloadQueue.forEach(([imageId, hash]) => {
LoadAndCreateURLfromFileHash(hash).then(url => {
const ElementIMG = document.getElementById(imageId);
if (ElementIMG) {
ElementIMG.src = url;
}
});
});
}
/* ------------------------------ OBSERVER ------------------------------ */
function setupIntersectionObserver() {
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (!entry.isIntersecting) return;
const rowIndex = entry.target.id.split("_")[2];
const hashDiv = $("#LB_Hash_" + rowIndex).text();
if (hashDiv) {
request.url('/View/Product/Details/data')
.success(() => { })
.data({ target: hashDiv })
.fromVarCache(true)
.type("POST")
.go();
}
});
}, { threshold: 0.1 });
$(".ListRowCard").each(function () {
observer.observe(this);
});
}
/* ------------------------------ SEARCH ------------------------------ */
function filterList() {
const searchTerm = $("#LB_Search").val().toLowerCase();
$("#LB_ListContainer .card").each(function () {
const text = $(this).text().toLowerCase();
if (text.includes(searchTerm)) {
this.style.display = "block";
if (this.parentElement.parentElement.id !== 'LB_ListContainer') { this.parentElement.parentElement.style.display = "block"; }
} else {
this.style.display = "none";
if (this.parentElement.parentElement.id !== 'LB_ListContainer') { this.parentElement.parentElement.style.display = "none"; }
}
});
}
function clearSearch() {
$("#LB_Search").val("");
filterList();
}
/* ------------------------------ UTILS ------------------------------ */
function deepMerge(target, source) {
for (const key in source) {
if (source[key] && typeof source[key] === "object") {
if (!target[key]) target[key] = {};
deepMerge(target[key], source[key]);
} else {
target[key] = source[key];
}
}
return target;
}
return {
build,
filterList,
clearSearch,
queueImageLoad,
};
})();
/**
* Create a searchable, paginated table using jQuery DataTables
*
* @param {Object} options
* @param {Array} options.data - Array of objects OR array of arrays
* @param {Array} [options.headers] - Optional table headers
* @param {String} [options.tableId] - Table ID
* @param {Number} [options.defaultSortColumn] - Column index
* @param {String} [options.defaultSortDirection] - 'asc' | 'desc'
* @param {String} [options.defaultSearch] - Default search text
* @param {Number} [options.pageLength] - Rows per page
* @param {Boolean} [options.initializeTable]
* @param {Boolean} [options.placeTable]
*/
function createSearchableTable({
data,
headers = [],
tableId = "dynamicTable",
defaultSortColumn = 0,
defaultSortDirection = "asc",
defaultSearch = "",
pageLength = 10,
initializeTable = true,
placeTable = true,
}) {
if (!Array.isArray(data) || data.length === 0) {
console.error("Data must be a non-empty array");
return null;
}
const isObjectData =
data[0] !== null &&
typeof data[0] === "object" &&
!Array.isArray(data[0]);
if (isObjectData && headers.length === 0) {
headers = Object.keys(data[0]);
}
let tableHTML = `<table id="${tableId}" class="display"><thead><tr>`;
headers.forEach(header => {
tableHTML += `<th>${header}</th>`;
});
tableHTML += `</tr></thead><tbody>`;
data.forEach(row => {
tableHTML += `<tr>`;
if (isObjectData) {
headers.forEach(key => {
tableHTML += `<td>${row[key] ?? ""}</td>`;
});
} else {
row.forEach(cell => {
tableHTML += `<td>${cell}</td>`;
});
}
tableHTML += `</tr>`;
});
tableHTML += `</tbody></table>`;
let dataTable = null;
if (placeTable) {
$("#tableContainer").html(tableHTML);
if (initializeTable) {
dataTable = $(`#${tableId}`).DataTable({
pageLength,
order: [[defaultSortColumn, defaultSortDirection]],
search: { search: defaultSearch }
});
}
}
return {
html: tableHTML,
dataTable
};
}