Frontend updates
This commit is contained in:
		@@ -1,109 +1,175 @@
 | 
				
			|||||||
_wrapperVersion = "1.0.0"
 | 
					_wrapperVersion = '1.0.0';
 | 
				
			||||||
_minApiVersion = "1.0.0"
 | 
					_minApiVersion = '1.0.0';
 | 
				
			||||||
_maxApiVersion = "1.0.0"
 | 
					_maxApiVersion = '1.0.0';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					_defaultTTL = 60000;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
_apiConfig = {
 | 
					_apiConfig = {
 | 
				
			||||||
	"basePath": "/api/v1/"
 | 
						basePath: '/api/v1/'
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if (!window.localStorage) {
 | 
				
			||||||
 | 
						console.warn('Local Storage is not available, some features may not work');
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Generic driver functions
 | 
					// Generic driver functions
 | 
				
			||||||
let _api = {
 | 
					let _api = {
 | 
				
			||||||
	"get": async function(path) {
 | 
						get: async function (path) {
 | 
				
			||||||
		const options = {
 | 
							const options = {
 | 
				
			||||||
			headers: new Headers({ 'content-type': 'application/json' })
 | 
								headers: new Headers({ 'content-type': 'application/json' })
 | 
				
			||||||
		};
 | 
							};
 | 
				
			||||||
		const response = await fetch(_apiConfig.basePath + path, options)
 | 
							const response = await fetch(_apiConfig.basePath + path, options);
 | 
				
			||||||
		// Handle the response
 | 
							// Handle the response
 | 
				
			||||||
		if (!response.ok) {
 | 
							if (!response.ok) {
 | 
				
			||||||
			_testPageFail(response.statusText)
 | 
								_testPageFail(response.statusText);
 | 
				
			||||||
			return
 | 
								return;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		const result = await response.json()
 | 
							const result = await response.json();
 | 
				
			||||||
		// Handle the result, was json valid?
 | 
							// Handle the result, was json valid?
 | 
				
			||||||
		if (!result) {
 | 
							if (!result) {
 | 
				
			||||||
			_testPageFail("Invalid JSON response")
 | 
								_testPageFail('Invalid JSON response');
 | 
				
			||||||
			return
 | 
								return;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return result;
 | 
							return result;
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
	"post": async function(path, data) {
 | 
						post: async function (path, data) {
 | 
				
			||||||
		const options = {
 | 
							const options = {
 | 
				
			||||||
			method: 'POST',
 | 
								method: 'POST',
 | 
				
			||||||
			headers: new Headers({ 'content-type': 'application/json' }),
 | 
								headers: new Headers({ 'content-type': 'application/json' }),
 | 
				
			||||||
			body: JSON.stringify(data)
 | 
								body: JSON.stringify(data)
 | 
				
			||||||
		};
 | 
							};
 | 
				
			||||||
		const response = await fetch(_apiConfig.basePath + path, options)
 | 
							const response = await fetch(_apiConfig.basePath + path, options);
 | 
				
			||||||
		// Handle the response
 | 
							// Handle the response
 | 
				
			||||||
		if (!response.ok) {
 | 
							if (!response.ok) {
 | 
				
			||||||
			_testPageFail(response.statusText)
 | 
								_testPageFail(response.statusText);
 | 
				
			||||||
			return
 | 
								return;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		const result = await response.json()
 | 
							const result = await response.json();
 | 
				
			||||||
		// Handle the result, was json valid?
 | 
							// Handle the result, was json valid?
 | 
				
			||||||
		if (!result) {
 | 
							if (!result) {
 | 
				
			||||||
			_testPageFail("Invalid JSON response")
 | 
								_testPageFail('Invalid JSON response');
 | 
				
			||||||
			return
 | 
								return;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return result;
 | 
							return result;
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
	"getAsync": function(path, callback) {
 | 
						getAsync: function (path, callback) {
 | 
				
			||||||
		const options = {
 | 
							const options = {
 | 
				
			||||||
			headers: new Headers({ 'content-type': 'application/json' })
 | 
								headers: new Headers({ 'content-type': 'application/json' })
 | 
				
			||||||
		};
 | 
							};
 | 
				
			||||||
		fetch(_apiConfig.basePath + path, options).then(response => response.json()).then(data => callback(data)).catch(error => _testPageFail(error))
 | 
							fetch(_apiConfig.basePath + path, options)
 | 
				
			||||||
 | 
								.then((response) => response.json())
 | 
				
			||||||
 | 
								.then((data) => callback(data))
 | 
				
			||||||
 | 
								.catch((error) => _testPageFail(error));
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function getApiDescriptionByTable(tableName) {
 | 
				
			||||||
 | 
						const keyDesc = `desc:${tableName}`;
 | 
				
			||||||
 | 
						const keyTime = `${keyDesc}:time`;
 | 
				
			||||||
 | 
						const keyTTL = `${keyDesc}:ttl`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Retrieve cached data
 | 
				
			||||||
 | 
						const description = JSON.parse(localStorage.getItem(keyDesc));
 | 
				
			||||||
 | 
						const timeCreated = parseInt(localStorage.getItem(keyTime));
 | 
				
			||||||
 | 
						const ttl = parseInt(localStorage.getItem(keyTTL));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Check if valid cached data exists
 | 
				
			||||||
 | 
						if (description && timeCreated && ttl) {
 | 
				
			||||||
 | 
							const currentTime = Date.now();
 | 
				
			||||||
 | 
							const age = currentTime - parseInt(timeCreated, 10);
 | 
				
			||||||
 | 
							if (age < parseInt(ttl, 10)) {
 | 
				
			||||||
 | 
								// Return cached data immediately
 | 
				
			||||||
 | 
								return Promise.resolve(description);
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								console.warn('Cached description expired; fetching new data');
 | 
				
			||||||
 | 
								// Fetch new data, update cache, and return it
 | 
				
			||||||
 | 
								return fetchAndUpdateCache(tableName);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							console.warn('No cached description; fetching from server');
 | 
				
			||||||
 | 
							// Fetch data, update cache, and return it
 | 
				
			||||||
 | 
							return fetchAndUpdateCache(tableName);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						function fetchAndUpdateCache(tableName) {
 | 
				
			||||||
 | 
							return _api
 | 
				
			||||||
 | 
								.get(`${tableName}/describe`)
 | 
				
			||||||
 | 
								.then((data) => {
 | 
				
			||||||
 | 
									if (data) {
 | 
				
			||||||
 | 
										// Update local storage with new data
 | 
				
			||||||
 | 
										localStorage.setItem(keyDesc, JSON.stringify(data));
 | 
				
			||||||
 | 
										localStorage.setItem(keyTime, Date.now().toString());
 | 
				
			||||||
 | 
										localStorage.setItem(keyTTL, '60000'); // 60 seconds TTL
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									return data; // Return the fetched data
 | 
				
			||||||
 | 
								})
 | 
				
			||||||
 | 
								.catch((error) => {
 | 
				
			||||||
 | 
									console.error('Failed to fetch description:', error);
 | 
				
			||||||
 | 
									// Fallback to cached data if available (even if expired)
 | 
				
			||||||
 | 
									return description || null;
 | 
				
			||||||
 | 
								});
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function returnTableDataByTableName(tableName) {
 | 
					function returnTableDataByTableName(tableName) {
 | 
				
			||||||
    return _api.get(tableName)
 | 
						return _api.get(tableName);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function returnTableDataByTableNameWithSearch(tableName, search) {
 | 
					function returnTableDataByTableNameWithSearch(tableName, search) {
 | 
				
			||||||
	return _api.get(tableName + "?search=" + search)
 | 
						return _api.get(tableName + '?search=' + search);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function returnTableDataByTableNameAsync(tableName, callback) {
 | 
					function returnTableDataByTableNameAsync(tableName, callback) {
 | 
				
			||||||
	    _api.getAsync(tableName, callback)
 | 
						_api.getAsync(tableName, callback);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
async function getCountByTable(tableName) {
 | 
					async function getCountByTable(tableName) {
 | 
				
			||||||
	let result = await(_api.get(tableName + "?count"))
 | 
						// Stored in `data:count:${tableName}`
 | 
				
			||||||
 | 
						let result = await _api.get(tableName + '?count=true');
 | 
				
			||||||
	if (typeof result !== 'number') {
 | 
						if (typeof result !== 'number') {
 | 
				
			||||||
		_testPageWarn("Count was not a number, was: " + result)
 | 
							_testPageWarn('Count was not a number, was: ' + result);
 | 
				
			||||||
		console.warn("Count was not a number, was: " + result)
 | 
							console.warn('Count was not a number, was: ' + result);
 | 
				
			||||||
		return -1
 | 
							return -1;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	    return result
 | 
						return result;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function getRowsByTableAndColumnList(tableName, columnList) {
 | 
					function getRowsByTableAndColumnList(tableName, columnList) {
 | 
				
			||||||
	//return _api.get(tableName + '/rows/' + columnList.join(','))
 | 
						//return _api.get(tableName + '/rows/' + columnList.join(','))
 | 
				
			||||||
	    return undefined
 | 
						return undefined;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function _testPageFail(reason) {
 | 
					function _testPageFail(reason) {
 | 
				
			||||||
	document.getElementById("heroStatus").classList.remove("is-success")
 | 
						document.getElementById('heroStatus').classList.remove('is-success');
 | 
				
			||||||
	document.getElementById("heroStatus").classList.add("is-danger")
 | 
						document.getElementById('heroStatus').classList.add('is-danger');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	document.getElementById("heroExplainer").innerHTML = "API Wrapper Test Failed, reason: " + reason
 | 
						document.getElementById('heroExplainer').innerHTML = 'API Wrapper Test Failed, reason: ' + reason;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function _testPageWarn(reason) {
 | 
					function _testPageWarn(reason) {
 | 
				
			||||||
	document.getElementById("heroStatus").classList.remove("is-success")
 | 
						document.getElementById('heroStatus').classList.remove('is-success');
 | 
				
			||||||
	document.getElementById("heroStatus").classList.add("is-warning")
 | 
						document.getElementById('heroStatus').classList.add('is-warning');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	document.getElementById("heroExplainer").innerHTML = "API Wrapper Test Warning, reason: " + reason
 | 
						document.getElementById('heroExplainer').innerHTML = 'API Wrapper Test Warning, reason: ' + reason;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function getServerVersion() {
 | 
					function getServerVersion() {
 | 
				
			||||||
	return _api.get('version')
 | 
						return _api.get('version');
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function createEntry(tableName, data) {
 | 
					function createEntry(tableName, data) {
 | 
				
			||||||
	return _api.post(tableName, data)
 | 
						invalidateCache(tableName);
 | 
				
			||||||
 | 
						return _api.post(tableName, data);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function invalidateCache(tableName) {
 | 
				
			||||||
 | 
						const keyDesc = `desc:${tableName}`;
 | 
				
			||||||
 | 
						const keyTime = `${keyDesc}:time`;
 | 
				
			||||||
 | 
						const keyTTL = `${keyDesc}:ttl`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						localStorage.removeItem(keyDesc);
 | 
				
			||||||
 | 
						localStorage.removeItem(keyTime);
 | 
				
			||||||
 | 
						localStorage.removeItem(keyTTL);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,55 +1,55 @@
 | 
				
			|||||||
_pageDriverVersion = "1.0.1";
 | 
					_pageDriverVersion = '1.0.1';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Handle color for icon svg with id="logo" based on the current theme
 | 
					// Handle color for icon svg with id="logo" based on the current theme
 | 
				
			||||||
const logo = document.getElementById("logo");
 | 
					const logo = document.getElementById('logo');
 | 
				
			||||||
if (logo) {
 | 
					if (logo) {
 | 
				
			||||||
	logo.style.fill = getComputedStyle(document.documentElement).getPropertyValue("--bulma-text"); 
 | 
						logo.style.fill = getComputedStyle(document.documentElement).getPropertyValue('--bulma-text');
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
if (_wrapperVersion === undefined) {
 | 
					if (_wrapperVersion === undefined) {
 | 
				
			||||||
	console.error("API Wrapper not found; Please include the API Wrapper before including the Page Driver");
 | 
						console.error('API Wrapper not found; Please include the API Wrapper before including the Page Driver');
 | 
				
			||||||
	exit();
 | 
						exit();
 | 
				
			||||||
} else {
 | 
					} else {
 | 
				
			||||||
	console.log("API Wrapper found; Page Driver is ready to use");
 | 
						console.log('API Wrapper found; Page Driver is ready to use');
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Find all tables on the page which have data-dataSource attribute
 | 
					// Find all tables on the page which have data-dataSource attribute
 | 
				
			||||||
var tables = document.querySelectorAll("table[data-dataSource]");
 | 
					var tables = document.querySelectorAll('table[data-dataSource]');
 | 
				
			||||||
//var tables = []
 | 
					//var tables = []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Get all single values with data-dataSource, data-dataCol and data-dataAction
 | 
					// Get all single values with data-dataSource, data-dataCol and data-dataAction
 | 
				
			||||||
var singleValues = document.querySelectorAll("span[data-dataSource]");
 | 
					var singleValues = document.querySelectorAll('span[data-dataSource]');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Find all search fields with data-searchTargetId
 | 
					// Find all search fields with data-searchTargetId
 | 
				
			||||||
var searchFields = document.querySelectorAll("input[data-searchTargetId]");
 | 
					var searchFields = document.querySelectorAll('input[data-searchTargetId]');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Find all modalForms
 | 
					// Find all modalForms
 | 
				
			||||||
var modalForms = document.querySelectorAll("form[data-targetTable]");
 | 
					var modalForms = document.querySelectorAll('form[data-targetTable]');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Iterate over all tables
 | 
					// Iterate over all tables
 | 
				
			||||||
tables.forEach(async table => {
 | 
					tables.forEach(async (table) => {
 | 
				
			||||||
	console.log("Table found: ", table);
 | 
						console.log('Table found: ', table);
 | 
				
			||||||
	// Get THEAD and TBODY elements
 | 
						// Get THEAD and TBODY elements
 | 
				
			||||||
	const thead = table.querySelector("thead");
 | 
						const thead = table.querySelector('thead');
 | 
				
			||||||
	const tbody = table.querySelector("tbody");
 | 
						const tbody = table.querySelector('tbody');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// get index per column
 | 
						// get index per column
 | 
				
			||||||
	const columns = thead.querySelectorAll("th");
 | 
						const columns = thead.querySelectorAll('th');
 | 
				
			||||||
	const columnIndices = [];
 | 
						const columnIndices = [];
 | 
				
			||||||
	columns.forEach((column, index) => {
 | 
						columns.forEach((column, index) => {
 | 
				
			||||||
		columnIndices[column.getAttribute("data-dataCol")] = index;
 | 
							columnIndices[column.getAttribute('data-dataCol')] = index;
 | 
				
			||||||
	});
 | 
						});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// All required cols
 | 
						// All required cols
 | 
				
			||||||
	let requiredCols = [];
 | 
						let requiredCols = [];
 | 
				
			||||||
	columns.forEach(column => {
 | 
						columns.forEach((column) => {
 | 
				
			||||||
		requiredCols.push(column.getAttribute("data-dataCol"));
 | 
							requiredCols.push(column.getAttribute('data-dataCol'));
 | 
				
			||||||
	});
 | 
						});
 | 
				
			||||||
	console.log("Required columns: ", requiredCols);
 | 
						console.log('Required columns: ', requiredCols);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Get data from API
 | 
						// Get data from API
 | 
				
			||||||
	//let result = getRowsByTableAndColumnList(table.getAttribute("data-dataSource"), requiredCols);
 | 
						//let result = getRowsByTableAndColumnList(table.getAttribute("data-dataSource"), requiredCols);
 | 
				
			||||||
	let result = await returnTableDataByTableName(table.getAttribute("data-dataSource"))
 | 
						let result = await returnTableDataByTableName(table.getAttribute('data-dataSource'));
 | 
				
			||||||
	// for (resultIndex in result) {
 | 
						// for (resultIndex in result) {
 | 
				
			||||||
	// 	const row = result[resultIndex];
 | 
						// 	const row = result[resultIndex];
 | 
				
			||||||
	// 	const tr = document.createElement("tr");
 | 
						// 	const tr = document.createElement("tr");
 | 
				
			||||||
@@ -62,114 +62,186 @@ tables.forEach(async table => {
 | 
				
			|||||||
	// }
 | 
						// }
 | 
				
			||||||
	writeDataToTable(table, result);
 | 
						writeDataToTable(table, result);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	console.log("Column indices: ", columnIndices);
 | 
						console.log('Column indices: ', columnIndices);
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
console.info("Processing single values");
 | 
					console.info('Processing single values');
 | 
				
			||||||
console.info(singleValues);
 | 
					console.info(singleValues);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Iterate over all single values
 | 
					// Iterate over all single values
 | 
				
			||||||
singleValues.forEach(async singleValue => {
 | 
					singleValues.forEach(async (singleValue) => {
 | 
				
			||||||
	writeSingelton(singleValue);
 | 
						writeSingelton(singleValue);
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
async function writeSingelton(element) {
 | 
					async function writeSingelton(element) {
 | 
				
			||||||
	const table = element.getAttribute("data-dataSource");
 | 
						const table = element.getAttribute('data-dataSource');
 | 
				
			||||||
	console.log("Table: ", table, " Action: ", element.getAttribute("data-dataAction"), " Element: ", element);
 | 
						console.log('Table: ', table, ' Action: ', element.getAttribute('data-dataAction'), ' Element: ', element);
 | 
				
			||||||
	switch(element.getAttribute("data-dataAction")) {
 | 
						switch (element.getAttribute('data-dataAction')) {
 | 
				
			||||||
		case "COUNT": {
 | 
							case 'COUNT': {
 | 
				
			||||||
			console.log("Count action found");
 | 
								console.log('Count action found');
 | 
				
			||||||
			element.innerHTML = (await getCountByTable(table))
 | 
								element.innerHTML = await getCountByTable(table);
 | 
				
			||||||
			break;
 | 
								break;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		case "SPECIAL": {
 | 
							case 'SPECIAL': {
 | 
				
			||||||
			if(table == "version") {
 | 
								if (table == 'version') {
 | 
				
			||||||
				element.innerHTML = (await getServerVersion())["version"];
 | 
									element.innerHTML = (await getServerVersion())['version'];
 | 
				
			||||||
				break
 | 
									break;
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		default: {
 | 
							default: {
 | 
				
			||||||
			console.error("Unknown action found: ", element.getAttribute("data-dataAction"));
 | 
								console.error('Unknown action found: ', element.getAttribute('data-dataAction'));
 | 
				
			||||||
			break;
 | 
								break;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	element.classList.remove("is-skeleton");
 | 
						element.classList.remove('is-skeleton');
 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
// Attach listeners to search fields
 | 
					// Attach listeners to search fields
 | 
				
			||||||
searchFields.forEach(searchField => {
 | 
					searchFields.forEach((searchField) => {
 | 
				
			||||||
	searchField.addEventListener("input", async function() {
 | 
						// Apply restrictions to search field (min, max, chars, etc)
 | 
				
			||||||
		console.log("Search field changed: ", searchField);
 | 
					
 | 
				
			||||||
		const targetId = searchField.getAttribute("data-searchTargetId");
 | 
						getApiDescriptionByTable(document.getElementById(searchField.getAttribute('data-searchTargetId')).getAttribute('data-dataSource')).then((desc) => {
 | 
				
			||||||
 | 
							desc = desc['GET']['keys']['search'];
 | 
				
			||||||
 | 
							var rules = desc['rules'];
 | 
				
			||||||
 | 
							rules.forEach((rule) => {
 | 
				
			||||||
 | 
								switch (rule['name']) {
 | 
				
			||||||
 | 
									case 'min': {
 | 
				
			||||||
 | 
										searchField.setAttribute('minlength', rule['args']['limit']);
 | 
				
			||||||
 | 
										break;
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									case 'max': {
 | 
				
			||||||
 | 
										searchField.setAttribute('maxlength', rule['args']['limit']);
 | 
				
			||||||
 | 
										break;
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							});
 | 
				
			||||||
 | 
						});
 | 
				
			||||||
 | 
						searchField.addEventListener('input', async function () {
 | 
				
			||||||
 | 
							console.log('Search field changed: ', searchField);
 | 
				
			||||||
 | 
							if (searchField.checkValidity() == false) {
 | 
				
			||||||
 | 
								console.log('Invalid input');
 | 
				
			||||||
 | 
								searchField.classList.add('is-danger');
 | 
				
			||||||
 | 
								return;
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								searchField.classList.remove('is-danger');
 | 
				
			||||||
 | 
								const targetId = searchField.getAttribute('data-searchTargetId');
 | 
				
			||||||
			const target = document.getElementById(targetId);
 | 
								const target = document.getElementById(targetId);
 | 
				
			||||||
		const table = target.getAttribute("data-dataSource");
 | 
								const table = target.getAttribute('data-dataSource');
 | 
				
			||||||
		const column = target.getAttribute("data-dataCol");
 | 
								const column = target.getAttribute('data-dataCol');
 | 
				
			||||||
			const value = searchField.value;
 | 
								const value = searchField.value;
 | 
				
			||||||
		console.log("Searching for ", value, " in ", table, " column ", column);
 | 
								console.log('Searching for ', value, ' in ', table, ' column ', column);
 | 
				
			||||||
		const result = await returnTableDataByTableNameWithSearch(table, value);
 | 
								//const result = await returnTableDataByTableNameWithSearch(table, value);
 | 
				
			||||||
		console.log("Result: ", result);
 | 
								//clearTable(target);
 | 
				
			||||||
		clearTable(target);
 | 
								//writeDataToTable(target, result);
 | 
				
			||||||
		writeDataToTable(target, result);
 | 
								refreshTableByName(table);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
	});
 | 
						});
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Attach listeners to modal forms
 | 
					// Attach listeners to modal forms
 | 
				
			||||||
modalForms.forEach(modalForm => {
 | 
					modalForms.forEach((modalForm) => {
 | 
				
			||||||
	modalForm.addEventListener("submit", async function(event) {
 | 
						// Add validation to form by using API description (everything is assumed POST for now)
 | 
				
			||||||
 | 
						modalForm.addEventListener('input', async function (event) {
 | 
				
			||||||
 | 
							if (event.target.checkValidity() == false) {
 | 
				
			||||||
 | 
								modalForm.querySelector("input[type='submit']").setAttribute('disabled', true);
 | 
				
			||||||
 | 
								event.target.classList.add('is-danger');
 | 
				
			||||||
 | 
								return;
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								modalForm.querySelector("input[type='submit']").removeAttribute('disabled');
 | 
				
			||||||
 | 
								event.target.classList.remove('is-danger');
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						});
 | 
				
			||||||
 | 
						getApiDescriptionByTable(modalForm.getAttribute('data-targetTable')).then((desc) => {
 | 
				
			||||||
 | 
							console.log('Description: ', desc);
 | 
				
			||||||
 | 
							const keys = desc['POST']['keys'];
 | 
				
			||||||
 | 
							// Apply resitrictions and types to form fields
 | 
				
			||||||
 | 
							for (key in keys) {
 | 
				
			||||||
 | 
								const field = modalForm.querySelector("input[name='" + key + "']");
 | 
				
			||||||
 | 
								if (field) {
 | 
				
			||||||
 | 
									const rules = keys[key]['rules'];
 | 
				
			||||||
 | 
									const flags = keys[key]['flags'];
 | 
				
			||||||
 | 
									console.log('Field: ', field, ' Rules: ', rules, ' Flags: ', flags);
 | 
				
			||||||
 | 
									rules.forEach((rule) => {
 | 
				
			||||||
 | 
										switch (rule['name']) {
 | 
				
			||||||
 | 
											case 'min': {
 | 
				
			||||||
 | 
												field.setAttribute('minlength', rule['args']['limit']);
 | 
				
			||||||
 | 
												break;
 | 
				
			||||||
 | 
											}
 | 
				
			||||||
 | 
											case 'max': {
 | 
				
			||||||
 | 
												field.setAttribute('maxlength', rule['args']['limit']);
 | 
				
			||||||
 | 
												break;
 | 
				
			||||||
 | 
											}
 | 
				
			||||||
 | 
											case 'pattern': {
 | 
				
			||||||
 | 
												field.setAttribute('pattern', rule['args']['regex'].substring(1, rule['args']['regex'].length - 1));
 | 
				
			||||||
 | 
												//field.setAttribute("pattern", "^[\\+]?[\\(]?[0-9]{3}[\\)]?[\\-\\s\\.]?[0-9]{3}[\\-\\s\\.]?[0-9]{4,9}$");
 | 
				
			||||||
 | 
												break;
 | 
				
			||||||
 | 
											}
 | 
				
			||||||
 | 
											case 'type': {
 | 
				
			||||||
 | 
												//field.setAttribute("type", rule["args"]["type"]);
 | 
				
			||||||
 | 
												console.log('Type: ', rule['args']['type']);
 | 
				
			||||||
 | 
												break;
 | 
				
			||||||
 | 
											}
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									});
 | 
				
			||||||
 | 
									if (flags) {
 | 
				
			||||||
 | 
										flags['presence'] == 'required' ? field.setAttribute('required', true) : field.removeAttribute('required');
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							console.log('Keys: ', keys);
 | 
				
			||||||
 | 
						});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						modalForm.addEventListener('submit', async function (event) {
 | 
				
			||||||
		event.preventDefault();
 | 
							event.preventDefault();
 | 
				
			||||||
		// Check what button submitted the form and if it has data-actionBtn = save
 | 
							// Check what button submitted the form and if it has data-actionBtn = save
 | 
				
			||||||
		// If not, close modal
 | 
							// If not, close modal
 | 
				
			||||||
		const pressedBtn = event.submitter;
 | 
							const pressedBtn = event.submitter;
 | 
				
			||||||
		if(pressedBtn.getAttribute("data-actionBtn") != "save") {
 | 
							if (pressedBtn.getAttribute('data-actionBtn') != 'save') {
 | 
				
			||||||
			modalForm.closest(".modal").classList.remove('is-active');
 | 
								modalForm.closest('.modal').classList.remove('is-active');
 | 
				
			||||||
			return;
 | 
								return;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	
 | 
					 | 
				
			||||||
		// Find .entryPhase and hide it
 | 
							// Find .entryPhase and hide it
 | 
				
			||||||
		const entryPhase = modalForm.querySelector(".entryPhase");
 | 
							const entryPhase = modalForm.querySelector('.entryPhase');
 | 
				
			||||||
		const loadPhase = modalForm.querySelector(".loadPhase");
 | 
							const loadPhase = modalForm.querySelector('.loadPhase');
 | 
				
			||||||
		if (entryPhase) {
 | 
							if (entryPhase) {
 | 
				
			||||||
			entryPhase.classList.add("is-hidden");
 | 
								entryPhase.classList.add('is-hidden');
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if (loadPhase) {
 | 
							if (loadPhase) {
 | 
				
			||||||
			loadPhase.classList.remove("is-hidden");
 | 
								loadPhase.classList.remove('is-hidden');
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		console.log("Form submitted: ", modalForm);
 | 
							console.log('Form submitted: ', modalForm);
 | 
				
			||||||
		const table = modalForm.getAttribute("data-targetTable");
 | 
							const table = modalForm.getAttribute('data-targetTable');
 | 
				
			||||||
		const data = new FormData(modalForm);
 | 
							const data = new FormData(modalForm);
 | 
				
			||||||
		// Convert to JSON object
 | 
							// Convert to JSON object
 | 
				
			||||||
		let jsonData = {};
 | 
							let jsonData = {};
 | 
				
			||||||
		data.forEach((value, key) => {
 | 
							data.forEach((value, key) => {
 | 
				
			||||||
			jsonData[key] = value;
 | 
								jsonData[key] = value;
 | 
				
			||||||
		});
 | 
							});
 | 
				
			||||||
		console.log("JSON Data: ", jsonData);
 | 
							console.log('JSON Data: ', jsonData);
 | 
				
			||||||
		let resp = await createEntry(table, jsonData);
 | 
							let resp = await createEntry(table, jsonData);
 | 
				
			||||||
		console.log("Response: ", resp);
 | 
							console.log('Response: ', resp);
 | 
				
			||||||
		if(resp["status"] == "CREATED") {
 | 
							if (resp['status'] == 'CREATED') {
 | 
				
			||||||
			console.log("Entry created successfully");
 | 
								console.log('Entry created successfully');
 | 
				
			||||||
			modalForm.closest(".modal").classList.remove('is-active');
 | 
								modalForm.closest('.modal').classList.remove('is-active');
 | 
				
			||||||
			modalForm.reset();
 | 
								modalForm.reset();
 | 
				
			||||||
			// Hide loadPhase
 | 
								// Hide loadPhase
 | 
				
			||||||
			if (loadPhase) {
 | 
								if (loadPhase) {
 | 
				
			||||||
				loadPhase.classList.add("is-hidden");
 | 
									loadPhase.classList.add('is-hidden');
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			// Show entryPhase
 | 
								// Show entryPhase
 | 
				
			||||||
			if (entryPhase) {
 | 
								if (entryPhase) {
 | 
				
			||||||
				entryPhase.classList.remove("is-hidden");
 | 
									entryPhase.classList.remove('is-hidden');
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			// Hide loadPhase
 | 
								// Hide loadPhase
 | 
				
			||||||
			if (loadPhase) {
 | 
								if (loadPhase) {
 | 
				
			||||||
				loadPhase.classList.add("is-hidden");
 | 
									loadPhase.classList.add('is-hidden');
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			// Show entryPhase
 | 
								// Show entryPhase
 | 
				
			||||||
			if (entryPhase) {
 | 
								if (entryPhase) {
 | 
				
			||||||
				entryPhase.classList.remove("is-hidden");
 | 
									entryPhase.classList.remove('is-hidden');
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			// TODO: Show error message
 | 
								// TODO: Show error message
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
@@ -177,8 +249,6 @@ modalForms.forEach(modalForm => {
 | 
				
			|||||||
		// const target = document.getElementById(table);
 | 
							// const target = document.getElementById(table);
 | 
				
			||||||
		// writeDataToTable(target, result);
 | 
							// writeDataToTable(target, result);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		// Find all tables with data-searchTargetId set to table
 | 
							// Find all tables with data-searchTargetId set to table
 | 
				
			||||||
		setTimeout(() => {
 | 
							setTimeout(() => {
 | 
				
			||||||
			refreshTableByName(table);
 | 
								refreshTableByName(table);
 | 
				
			||||||
@@ -191,14 +261,14 @@ modalForms.forEach(modalForm => {
 | 
				
			|||||||
async function refreshTable(table) {
 | 
					async function refreshTable(table) {
 | 
				
			||||||
	// Refresh a table while keeping (optionally set) search value
 | 
						// Refresh a table while keeping (optionally set) search value
 | 
				
			||||||
	const searchField = document.querySelector("input[data-searchTargetId='" + table.id + "']");
 | 
						const searchField = document.querySelector("input[data-searchTargetId='" + table.id + "']");
 | 
				
			||||||
	if(searchField) {
 | 
						if (searchField && searchField.value != '') {
 | 
				
			||||||
		const value = searchField.value;
 | 
							const value = searchField.value;
 | 
				
			||||||
		const dbTable = table.getAttribute("data-dataSource");
 | 
							const dbTable = table.getAttribute('data-dataSource');
 | 
				
			||||||
		const result = await returnTableDataByTableNameWithSearch(dbTable, value);
 | 
							const result = await returnTableDataByTableNameWithSearch(dbTable, value);
 | 
				
			||||||
		clearTable(table);
 | 
							clearTable(table);
 | 
				
			||||||
		writeDataToTable(table, result);
 | 
							writeDataToTable(table, result);
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		const result = await returnTableDataByTableName(table.getAttribute("data-dataSource"));
 | 
							const result = await returnTableDataByTableName(table.getAttribute('data-dataSource'));
 | 
				
			||||||
		clearTable(table);
 | 
							clearTable(table);
 | 
				
			||||||
		writeDataToTable(table, result);
 | 
							writeDataToTable(table, result);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -218,36 +288,35 @@ async function updateSingeltonsByTableName(name) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
function clearTable(table) {
 | 
					function clearTable(table) {
 | 
				
			||||||
	const tbody = table.querySelector("tbody");
 | 
						const tbody = table.querySelector('tbody');
 | 
				
			||||||
	tbody.innerHTML = "";
 | 
						tbody.innerHTML = '';
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function writeDataToTable(table, data) {
 | 
					function writeDataToTable(table, data) {
 | 
				
			||||||
	console.log("Writing data to table: ", table, data);
 | 
						console.log('Writing data to table: ', table, data);
 | 
				
			||||||
	// Get THEAD and TBODY elements
 | 
						// Get THEAD and TBODY elements
 | 
				
			||||||
	const thead = table.querySelector("thead");
 | 
						const thead = table.querySelector('thead');
 | 
				
			||||||
	const tbody = table.querySelector("tbody");
 | 
						const tbody = table.querySelector('tbody');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// get index per column
 | 
						// get index per column
 | 
				
			||||||
	const columns = thead.querySelectorAll("th");
 | 
						const columns = thead.querySelectorAll('th');
 | 
				
			||||||
	const columnIndices = [];
 | 
						const columnIndices = [];
 | 
				
			||||||
	columns.forEach((column, index) => {
 | 
						columns.forEach((column, index) => {
 | 
				
			||||||
		columnIndices[column.getAttribute("data-dataCol")] = index;
 | 
							columnIndices[column.getAttribute('data-dataCol')] = index;
 | 
				
			||||||
	});
 | 
						});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// All required cols
 | 
						// All required cols
 | 
				
			||||||
	let requiredCols = [];
 | 
						let requiredCols = [];
 | 
				
			||||||
	columns.forEach(column => {
 | 
						columns.forEach((column) => {
 | 
				
			||||||
		requiredCols.push(column.getAttribute("data-dataCol"));
 | 
							requiredCols.push(column.getAttribute('data-dataCol'));
 | 
				
			||||||
	});
 | 
						});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for (resultIndex in data) {
 | 
						for (resultIndex in data) {
 | 
				
			||||||
		const row = data[resultIndex];
 | 
							const row = data[resultIndex];
 | 
				
			||||||
		const tr = document.createElement("tr");
 | 
							const tr = document.createElement('tr');
 | 
				
			||||||
		requiredCols.forEach(column => {
 | 
							requiredCols.forEach((column) => {
 | 
				
			||||||
			const td = document.createElement("td");
 | 
								const td = document.createElement('td');
 | 
				
			||||||
			td.innerHTML = row[column];
 | 
								td.innerHTML = row[column];
 | 
				
			||||||
			tr.appendChild(td);
 | 
								tr.appendChild(td);
 | 
				
			||||||
		});
 | 
							});
 | 
				
			||||||
@@ -255,7 +324,6 @@ function writeDataToTable(table, data) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
// Handle modal
 | 
					// Handle modal
 | 
				
			||||||
document.addEventListener('DOMContentLoaded', () => {
 | 
					document.addEventListener('DOMContentLoaded', () => {
 | 
				
			||||||
	// Functions to open and close a modal
 | 
						// Functions to open and close a modal
 | 
				
			||||||
@@ -284,7 +352,7 @@ document.addEventListener('DOMContentLoaded', () => {
 | 
				
			|||||||
	});
 | 
						});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Add a click event on various child elements to close the parent modal
 | 
						// Add a click event on various child elements to close the parent modal
 | 
				
			||||||
	(document.querySelectorAll('.modal-background, .modal-close, .modal-card-head .delete, .modal-card-foot .button') || []).forEach(($close) => {
 | 
						(document.querySelectorAll('.modal-close, .modal-card-head .delete, .modal-card-foot .button') || []).forEach(($close) => {
 | 
				
			||||||
		const $target = $close.closest('.modal');
 | 
							const $target = $close.closest('.modal');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		$close.addEventListener('click', () => {
 | 
							$close.addEventListener('click', () => {
 | 
				
			||||||
@@ -294,7 +362,7 @@ document.addEventListener('DOMContentLoaded', () => {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	// Add a keyboard event to close all modals
 | 
						// Add a keyboard event to close all modals
 | 
				
			||||||
	document.addEventListener('keydown', (event) => {
 | 
						document.addEventListener('keydown', (event) => {
 | 
				
			||||||
	  if(event.key === "Escape") {
 | 
							if (event.key === 'Escape') {
 | 
				
			||||||
			closeAllModals();
 | 
								closeAllModals();
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	});
 | 
						});
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -74,14 +74,15 @@
 | 
				
			|||||||
				</span>
 | 
									</span>
 | 
				
			||||||
			</div>
 | 
								</div>
 | 
				
			||||||
			</div>
 | 
								</div>
 | 
				
			||||||
 | 
								<br>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			<div class="field is-grouped">
 | 
								<div class="field is-grouped">
 | 
				
			||||||
			<div class="control">
 | 
								<div class="control">
 | 
				
			||||||
				<input type="submit" class="button is-link" value="Save" data-actionBtn="save">
 | 
									<input type="submit" class="button is-link" value="Save" data-actionBtn="save">
 | 
				
			||||||
			</div>
 | 
								</div>
 | 
				
			||||||
			<div class="control">
 | 
								<!--<div class="control">
 | 
				
			||||||
			  <button class="button is-link is-light" data-actionBtn="cancel">Cancel</button>
 | 
									<button type="button" class="button is-link is-light" data-actionBtn="cancel">Cancel</button>
 | 
				
			||||||
			</div>
 | 
								</div>-->
 | 
				
			||||||
			</div>
 | 
								</div>
 | 
				
			||||||
		</form>
 | 
							</form>
 | 
				
			||||||
			
 | 
								
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user