Support for tables in frontend
This commit is contained in:
		@@ -12,16 +12,17 @@ import alertContactsRoute_schema from './alertContacts_schema.js';
 | 
				
			|||||||
// Router base is '/api/v1'
 | 
					// Router base is '/api/v1'
 | 
				
			||||||
const Router = express.Router({ strict: false });
 | 
					const Router = express.Router({ strict: false });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// All empty strings are null values.
 | 
					// All empty strings are null values (body)
 | 
				
			||||||
Router.use('*', function (req, res, next) {
 | 
					Router.use('*', function (req, res, next) {
 | 
				
			||||||
	for (let key in req.body) {
 | 
						for (let key in req.body) {
 | 
				
			||||||
		if (req.body[key] === '') {
 | 
							if (req.body[key] === '') {
 | 
				
			||||||
			req.body[key] = null;
 | 
								req.body[key] = undefined;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	next();
 | 
						next();
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// All api routes lowercase! Yea I know but when strict: true it matters.
 | 
					// All api routes lowercase! Yea I know but when strict: true it matters.
 | 
				
			||||||
Router.route('/alertcontacts').get(alertContactsRoute.get).post(alertContactsRoute.post).patch(alertContactsRoute.patch).delete(alertContactsRoute.del);
 | 
					Router.route('/alertcontacts').get(alertContactsRoute.get).post(alertContactsRoute.post).patch(alertContactsRoute.patch).delete(alertContactsRoute.del);
 | 
				
			||||||
Router.route('/alertcontacts/describe').get(alertContactsRoute_schema);
 | 
					Router.route('/alertcontacts/describe').get(alertContactsRoute_schema);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -21,12 +21,18 @@ let _api = {
 | 
				
			|||||||
		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) {
 | 
				
			||||||
 | 
								console.error('Failed to fetch:', response.statusText);
 | 
				
			||||||
			_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) {
 | 
				
			||||||
 | 
								// Is it a number instead?
 | 
				
			||||||
 | 
								if (typeof result === 'number') {
 | 
				
			||||||
 | 
									return result;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								console.error('Invalid JSON response');
 | 
				
			||||||
			_testPageFail('Invalid JSON response');
 | 
								_testPageFail('Invalid JSON response');
 | 
				
			||||||
			return;
 | 
								return;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
@@ -53,15 +59,6 @@ let _api = {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return result;
 | 
							return result;
 | 
				
			||||||
	},
 | 
					 | 
				
			||||||
	getAsync: function (path, callback) {
 | 
					 | 
				
			||||||
		const options = {
 | 
					 | 
				
			||||||
			headers: new Headers({ 'content-type': 'application/json' })
 | 
					 | 
				
			||||||
		};
 | 
					 | 
				
			||||||
		fetch(_apiConfig.basePath + path, options)
 | 
					 | 
				
			||||||
			.then((response) => response.json())
 | 
					 | 
				
			||||||
			.then((data) => callback(data))
 | 
					 | 
				
			||||||
			.catch((error) => _testPageFail(error));
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -113,21 +110,37 @@ function getApiDescriptionByTable(tableName) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function returnTableDataByTableName(tableName) {
 | 
					function returnTableDataByTableName(tableName, search="", orderBy="asc", sort="", take=-1, skip=0) {
 | 
				
			||||||
	return _api.get(tableName);
 | 
						var orderBy = orderBy.toLowerCase();
 | 
				
			||||||
 | 
						if(orderBy == "") {
 | 
				
			||||||
 | 
							orderBy = "asc";
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						var baseString = tableName + "?order=" + orderBy;
 | 
				
			||||||
 | 
						if(sort && sort.length > 0) {
 | 
				
			||||||
 | 
							baseString += "&sort=" + sort;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if(take > 0) {
 | 
				
			||||||
 | 
							baseString += "&take=" + take;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if(skip > 0) {
 | 
				
			||||||
 | 
							baseString += "&skip=" + skip;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (search && search.length > 0) {
 | 
				
			||||||
 | 
							return _api.get(baseString + '&search=' + search);
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							return _api.get(baseString);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function returnTableDataByTableNameWithSearch(tableName, search) {
 | 
					async function getCountByTable(tableName, search="") {
 | 
				
			||||||
	return _api.get(tableName + '?search=' + search);
 | 
						let baseString = tableName + '?count=true';
 | 
				
			||||||
}
 | 
						if (search && search.length > 0) {
 | 
				
			||||||
 | 
							baseString += '&search=' + search;
 | 
				
			||||||
function returnTableDataByTableNameAsync(tableName, callback) {
 | 
						}
 | 
				
			||||||
	_api.getAsync(tableName, callback);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
async function getCountByTable(tableName) {
 | 
					 | 
				
			||||||
	// Stored in `data:count:${tableName}`
 | 
						// Stored in `data:count:${tableName}`
 | 
				
			||||||
	let result = await _api.get(tableName + '?count=true');
 | 
						let result = await _api.get(baseString);
 | 
				
			||||||
 | 
						console.debug('Count result:', result);
 | 
				
			||||||
	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);
 | 
				
			||||||
@@ -136,10 +149,6 @@ async function getCountByTable(tableName) {
 | 
				
			|||||||
	return result;
 | 
						return result;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function getRowsByTableAndColumnList(tableName, columnList) {
 | 
					 | 
				
			||||||
	//return _api.get(tableName + '/rows/' + columnList.join(','))
 | 
					 | 
				
			||||||
	return undefined;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
function _testPageFail(reason) {
 | 
					function _testPageFail(reason) {
 | 
				
			||||||
	document.getElementById('heroStatus').classList.remove('is-success');
 | 
						document.getElementById('heroStatus').classList.remove('is-success');
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -26,45 +26,6 @@ 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
 | 
					 | 
				
			||||||
tables.forEach(async (table) => {
 | 
					 | 
				
			||||||
	console.log('Table found: ', table);
 | 
					 | 
				
			||||||
	// Get THEAD and TBODY elements
 | 
					 | 
				
			||||||
	const thead = table.querySelector('thead');
 | 
					 | 
				
			||||||
	const tbody = table.querySelector('tbody');
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// get index per column
 | 
					 | 
				
			||||||
	const columns = thead.querySelectorAll('th');
 | 
					 | 
				
			||||||
	const columnIndices = [];
 | 
					 | 
				
			||||||
	columns.forEach((column, index) => {
 | 
					 | 
				
			||||||
		columnIndices[column.getAttribute('data-dataCol')] = index;
 | 
					 | 
				
			||||||
	});
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// All required cols
 | 
					 | 
				
			||||||
	let requiredCols = [];
 | 
					 | 
				
			||||||
	columns.forEach((column) => {
 | 
					 | 
				
			||||||
		requiredCols.push(column.getAttribute('data-dataCol'));
 | 
					 | 
				
			||||||
	});
 | 
					 | 
				
			||||||
	console.log('Required columns: ', requiredCols);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Get data from API
 | 
					 | 
				
			||||||
	//let result = getRowsByTableAndColumnList(table.getAttribute("data-dataSource"), requiredCols);
 | 
					 | 
				
			||||||
	let result = await returnTableDataByTableName(table.getAttribute('data-dataSource'));
 | 
					 | 
				
			||||||
	// for (resultIndex in result) {
 | 
					 | 
				
			||||||
	// 	const row = result[resultIndex];
 | 
					 | 
				
			||||||
	// 	const tr = document.createElement("tr");
 | 
					 | 
				
			||||||
	// 	requiredCols.forEach(column => {
 | 
					 | 
				
			||||||
	// 		const td = document.createElement("td");
 | 
					 | 
				
			||||||
	// 		td.innerHTML = row[column];
 | 
					 | 
				
			||||||
	// 		tr.appendChild(td);
 | 
					 | 
				
			||||||
	// 	});
 | 
					 | 
				
			||||||
	// 	tbody.appendChild(tr);
 | 
					 | 
				
			||||||
	// }
 | 
					 | 
				
			||||||
	writeDataToTable(table, result);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	console.log('Column indices: ', columnIndices);
 | 
					 | 
				
			||||||
});
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
console.info('Processing single values');
 | 
					console.info('Processing single values');
 | 
				
			||||||
console.info(singleValues);
 | 
					console.info(singleValues);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -73,6 +34,42 @@ singleValues.forEach(async (singleValue) => {
 | 
				
			|||||||
	writeSingelton(singleValue);
 | 
						writeSingelton(singleValue);
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Iterate over all tables
 | 
				
			||||||
 | 
					tables.forEach(async (table) => {
 | 
				
			||||||
 | 
						// Get THs and attach onClick event to sort
 | 
				
			||||||
 | 
						const ths = table.querySelectorAll('th');
 | 
				
			||||||
 | 
						ths.forEach((th) => {
 | 
				
			||||||
 | 
							th.style.cursor = 'pointer';
 | 
				
			||||||
 | 
							th.style.userSelect = 'none';
 | 
				
			||||||
 | 
							th.addEventListener('click', async function () {
 | 
				
			||||||
 | 
								const table = th.closest('table');
 | 
				
			||||||
 | 
								const order = th.getAttribute('data-order');
 | 
				
			||||||
 | 
								// Clear all other order attributes
 | 
				
			||||||
 | 
								ths.forEach((th) => {
 | 
				
			||||||
 | 
									if (th != this) {
 | 
				
			||||||
 | 
										th.removeAttribute('data-order');
 | 
				
			||||||
 | 
										th.classList.remove("bi-caret-up-fill")
 | 
				
			||||||
 | 
										th.classList.remove("bi-caret-down-fill")
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								});
 | 
				
			||||||
 | 
								if (order == 'ASC') {
 | 
				
			||||||
 | 
									th.setAttribute('data-order', 'DESC');
 | 
				
			||||||
 | 
									th.classList.add("bi-caret-down-fill")
 | 
				
			||||||
 | 
									th.classList.remove("bi-caret-up-fill")
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									th.setAttribute('data-order', 'ASC');
 | 
				
			||||||
 | 
									th.classList.add("bi-caret-up-fill")
 | 
				
			||||||
 | 
									th.classList.remove("bi-caret-down-fill")
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								refreshTable(table);
 | 
				
			||||||
 | 
							});
 | 
				
			||||||
 | 
						});
 | 
				
			||||||
 | 
						refreshTable(table);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
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);
 | 
				
			||||||
@@ -131,9 +128,6 @@ searchFields.forEach((searchField) => {
 | 
				
			|||||||
			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);
 | 
					 | 
				
			||||||
			//clearTable(target);
 | 
					 | 
				
			||||||
			//writeDataToTable(target, result);
 | 
					 | 
				
			||||||
			refreshTableByName(table);
 | 
								refreshTableByName(table);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	});
 | 
						});
 | 
				
			||||||
@@ -152,6 +146,8 @@ modalForms.forEach((modalForm) => {
 | 
				
			|||||||
			event.target.classList.remove('is-danger');
 | 
								event.target.classList.remove('is-danger');
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	});
 | 
						});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	getApiDescriptionByTable(modalForm.getAttribute('data-targetTable')).then((desc) => {
 | 
						getApiDescriptionByTable(modalForm.getAttribute('data-targetTable')).then((desc) => {
 | 
				
			||||||
		console.log('Description: ', desc);
 | 
							console.log('Description: ', desc);
 | 
				
			||||||
		const keys = desc['POST']['keys'];
 | 
							const keys = desc['POST']['keys'];
 | 
				
			||||||
@@ -246,9 +242,6 @@ modalForms.forEach((modalForm) => {
 | 
				
			|||||||
			// TODO: Show error message
 | 
								// TODO: Show error message
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// const target = document.getElementById(table);
 | 
					 | 
				
			||||||
		// 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);
 | 
				
			||||||
@@ -261,19 +254,73 @@ 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 && searchField.value != '') {
 | 
						// Get state of order and sort
 | 
				
			||||||
 | 
						const ths = table.querySelectorAll('th');
 | 
				
			||||||
 | 
						const columnIndices = [];
 | 
				
			||||||
 | 
						ths.forEach((th, index) => {
 | 
				
			||||||
 | 
							columnIndices[th.getAttribute('data-dataCol')] = index;
 | 
				
			||||||
 | 
						});
 | 
				
			||||||
 | 
						let order = '';
 | 
				
			||||||
 | 
						let column = '';
 | 
				
			||||||
 | 
						ths.forEach((th) => {
 | 
				
			||||||
 | 
							if (th.hasAttribute('data-order')) {
 | 
				
			||||||
 | 
								order = th.getAttribute('data-order');
 | 
				
			||||||
 | 
								column = th.getAttribute('data-dataCol');
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						});
 | 
				
			||||||
 | 
						console.log('Order: ', order, ' Column: ', column);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const maxLinesPerPage = table.getAttribute('data-pageSize');
 | 
				
			||||||
 | 
						let currentPage = table.getAttribute('data-currentPage');
 | 
				
			||||||
 | 
						if(currentPage == null) {
 | 
				
			||||||
 | 
							table.setAttribute('data-currentPage', 1);
 | 
				
			||||||
 | 
							currentPage = 1;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const start = (currentPage - 1) * maxLinesPerPage;
 | 
				
			||||||
 | 
						const end = start + maxLinesPerPage;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						let paginationPassOnPre = {
 | 
				
			||||||
 | 
							'start': start,
 | 
				
			||||||
 | 
							'end': end,
 | 
				
			||||||
 | 
							'currentPage': currentPage,
 | 
				
			||||||
 | 
							'maxLinesPerPage': maxLinesPerPage
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (searchField) {
 | 
				
			||||||
		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 returnTableDataByTableName(dbTable, value, order, column, take= maxLinesPerPage, skip= start);
 | 
				
			||||||
 | 
							const totalResultCount = await getCountByTable(dbTable, value);
 | 
				
			||||||
 | 
							paginationPassOnPre['dataLength'] = totalResultCount;
 | 
				
			||||||
 | 
							var magMiddl = managePaginationMiddleware(result, paginationPassOnPre);
 | 
				
			||||||
 | 
							var data = magMiddl[0];
 | 
				
			||||||
 | 
							var paginationPassOn = magMiddl[1];
 | 
				
			||||||
		clearTable(table);
 | 
							clearTable(table);
 | 
				
			||||||
		writeDataToTable(table, result);
 | 
							writeDataToTable(table, data, paginationPassOn);
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		const result = await returnTableDataByTableName(table.getAttribute('data-dataSource'));
 | 
							const result = await returnTableDataByTableName(table.getAttribute('data-dataSource'), undefined, order, column, take= maxLinesPerPage, skip= start);
 | 
				
			||||||
 | 
							const resultCount = await getCountByTable(table.getAttribute('data-dataSource'));
 | 
				
			||||||
 | 
							paginationPassOnPre['dataLength'] = resultCount;
 | 
				
			||||||
 | 
							var magMiddl = managePaginationMiddleware(result, paginationPassOnPre);
 | 
				
			||||||
 | 
							var data = magMiddl[0];
 | 
				
			||||||
 | 
							var paginationPassOn = magMiddl[1];
 | 
				
			||||||
		clearTable(table);
 | 
							clearTable(table);
 | 
				
			||||||
		writeDataToTable(table, result);
 | 
							writeDataToTable(table, data, paginationPassOn);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function managePaginationMiddleware(data, paginationPassOnPre) {
 | 
				
			||||||
 | 
						const maxLinesPerPage = paginationPassOnPre['maxLinesPerPage'];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const dataLength = paginationPassOnPre['dataLength'];
 | 
				
			||||||
 | 
						const maxPages = Math.ceil(dataLength / maxLinesPerPage);
 | 
				
			||||||
 | 
						paginationPassOn = paginationPassOnPre;
 | 
				
			||||||
 | 
						paginationPassOn['maxPages'] = maxPages;
 | 
				
			||||||
 | 
						// paginationPassOn['dataLength'] = dataLength;
 | 
				
			||||||
 | 
						return [data, paginationPassOn];
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
async function refreshTableByName(name) {
 | 
					async function refreshTableByName(name) {
 | 
				
			||||||
	const dirtyTables = document.querySelectorAll("table[data-dataSource='" + name + "']");
 | 
						const dirtyTables = document.querySelectorAll("table[data-dataSource='" + name + "']");
 | 
				
			||||||
	for (dirty of dirtyTables) {
 | 
						for (dirty of dirtyTables) {
 | 
				
			||||||
@@ -293,7 +340,10 @@ function clearTable(table) {
 | 
				
			|||||||
	tbody.innerHTML = '';
 | 
						tbody.innerHTML = '';
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function writeDataToTable(table, data) {
 | 
					function writeDataToTable(table, data, paginationPassOn) {
 | 
				
			||||||
 | 
						if(data == undefined || data == null || data.length == 0) {
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	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');
 | 
				
			||||||
@@ -312,18 +362,106 @@ function writeDataToTable(table, data) {
 | 
				
			|||||||
		requiredCols.push(column.getAttribute('data-dataCol'));
 | 
							requiredCols.push(column.getAttribute('data-dataCol'));
 | 
				
			||||||
	});
 | 
						});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Get paginationPassOn
 | 
				
			||||||
 | 
						const start = paginationPassOn['start'];
 | 
				
			||||||
 | 
						const end = paginationPassOn['end'];
 | 
				
			||||||
 | 
						const currentPage = paginationPassOn['currentPage'];
 | 
				
			||||||
 | 
						const maxLinesPerPage = paginationPassOn['maxLinesPerPage'];
 | 
				
			||||||
 | 
						const maxPages = paginationPassOn['maxPages'];
 | 
				
			||||||
 | 
						const dataLength = paginationPassOn['dataLength'];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Find nav with class pagination and data-targetTable="table.id"
 | 
				
			||||||
 | 
						const paginationElement = document.querySelector("nav.pagination[data-targetTable='" + table.id + "']");
 | 
				
			||||||
 | 
						const paginationList = paginationElement.querySelector('ul.pagination-list');
 | 
				
			||||||
 | 
						console.log('Data length: ', dataLength, ' Max pages: ', maxPages);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if(maxPages > 1) {
 | 
				
			||||||
 | 
							// Clear pagination list
 | 
				
			||||||
 | 
							paginationList.innerHTML = '';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							for (let i = 1; i <= maxPages; i++) {
 | 
				
			||||||
 | 
								const li = document.createElement('li');
 | 
				
			||||||
 | 
								li.innerHTML = '<a class="pagination-link" aria-label="Goto page ' + i + '" data-page="' + i + '">' + i + '</a>';
 | 
				
			||||||
 | 
								if(i == currentPage) {
 | 
				
			||||||
 | 
									li.querySelector('a').classList.add('is-current');
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								paginationList.appendChild(li);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Remove unused pages, only leave first, last, current and 2 neighbors
 | 
				
			||||||
 | 
							let pages = paginationList.querySelectorAll('li');
 | 
				
			||||||
 | 
							let friends = []
 | 
				
			||||||
 | 
							// Always add first and last
 | 
				
			||||||
 | 
							friends.push(0);
 | 
				
			||||||
 | 
							friends.push(pages.length - 1);
 | 
				
			||||||
 | 
							friends.push(currentPage-1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Add direct neighbors
 | 
				
			||||||
 | 
							// friends.push(currentPage - 2);
 | 
				
			||||||
 | 
							friends.push(currentPage);
 | 
				
			||||||
 | 
							friends.push(currentPage - 2);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Deduplicate friends
 | 
				
			||||||
 | 
							friends = [...new Set(friends)];
 | 
				
			||||||
 | 
							// Sort friends
 | 
				
			||||||
 | 
							friends.sort((a, b) => a - b);
 | 
				
			||||||
 | 
							// Parse friends (string to int)
 | 
				
			||||||
 | 
							friends = friends.map((x) => parseInt(x));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							console.log('Friends: ', friends, ' Pages: ', pages.length, ' Current: ', currentPage);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Remove everyone who is not a friend
 | 
				
			||||||
 | 
							for(let i = 0; i < pages.length; i++) {
 | 
				
			||||||
 | 
								if(friends.includes(i)) {
 | 
				
			||||||
 | 
									continue;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								pages[i].remove();
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Find all gaps (step size bigger then 1) and add an ellipsis in between the two numbers
 | 
				
			||||||
 | 
							let last = 0;
 | 
				
			||||||
 | 
							for(let i = 0; i < friends.length; i++) {
 | 
				
			||||||
 | 
								if(friends[i] - last > 1) {
 | 
				
			||||||
 | 
									const li = document.createElement('li');
 | 
				
			||||||
 | 
									li.innerHTML = '<span class="pagination-ellipsis">…</span>';
 | 
				
			||||||
 | 
									paginationList.insertBefore(li, pages[friends[i]]);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								last = friends[i];
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Append on click event to all pagination links
 | 
				
			||||||
 | 
							paginationList.querySelectorAll('a').forEach((link) => {
 | 
				
			||||||
 | 
								link.addEventListener('click', async function() {
 | 
				
			||||||
 | 
									const page = link.getAttribute('data-page');
 | 
				
			||||||
 | 
									table.setAttribute('data-currentPage', page);
 | 
				
			||||||
 | 
									refreshTable(table);
 | 
				
			||||||
 | 
								});
 | 
				
			||||||
 | 
							});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							paginationElement.classList.remove('is-hidden');
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							paginationElement.classList.add('is-hidden');
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	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.innerText = row[column];
 | 
				
			||||||
			tr.appendChild(td);
 | 
								tr.appendChild(td);
 | 
				
			||||||
		});
 | 
							});
 | 
				
			||||||
		tbody.appendChild(tr);
 | 
							tbody.appendChild(tr);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Handle modal
 | 
					// Handle modal
 | 
				
			||||||
document.addEventListener('DOMContentLoaded', () => {
 | 
					document.addEventListener('DOMContentLoaded', () => {
 | 
				
			||||||
	// Functions to open and close a modal
 | 
						// Functions to open and close a modal
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -101,7 +101,7 @@
 | 
				
			|||||||
<section class="section">
 | 
					<section class="section">
 | 
				
			||||||
	<h1 class="title" data-tK="start-recent-header">Alarm Kontakte</h1>
 | 
						<h1 class="title" data-tK="start-recent-header">Alarm Kontakte</h1>
 | 
				
			||||||
	<input class="input" type="text"  data-searchTargetId="contactTable" placeholder="Search..." />
 | 
						<input class="input" type="text"  data-searchTargetId="contactTable" placeholder="Search..." />
 | 
				
			||||||
	<table class="table is-striped is-fullwidth" data-dataSource="AlertContacts" id="contactTable">
 | 
						<table class="table is-striped is-fullwidth is-hoverable" data-dataSource="AlertContacts" id="contactTable" data-pageSize="5">
 | 
				
			||||||
		<thead>
 | 
							<thead>
 | 
				
			||||||
			<tr>
 | 
								<tr>
 | 
				
			||||||
				<th data-dataCol = "id"><abbr title="Position">Pos</abbr></th>
 | 
									<th data-dataCol = "id"><abbr title="Position">Pos</abbr></th>
 | 
				
			||||||
@@ -112,7 +112,13 @@
 | 
				
			|||||||
		</thead>
 | 
							</thead>
 | 
				
			||||||
		<tbody>
 | 
							<tbody>
 | 
				
			||||||
		</tbody>
 | 
							</tbody>
 | 
				
			||||||
 | 
							
 | 
				
			||||||
	</table>
 | 
						</table>
 | 
				
			||||||
 | 
						<nav class="pagination is-hidden" role="navigation" aria-label="pagination" data-targetTable="contactTable">
 | 
				
			||||||
 | 
							<ul class="pagination-list">
 | 
				
			||||||
 | 
								
 | 
				
			||||||
 | 
							</ul>
 | 
				
			||||||
 | 
							</nav>
 | 
				
			||||||
</section>
 | 
					</section>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<%~ include("partials/footer.eta") %>
 | 
					<%~ include("partials/footer.eta") %>
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user