atas/static/pageDriver.js
2025-01-30 00:32:10 +01:00

370 lines
11 KiB
JavaScript

_pageDriverVersion = '1.0.1';
// Handle color for icon svg with id="logo" based on the current theme
const logo = document.getElementById('logo');
if (logo) {
logo.style.fill = getComputedStyle(document.documentElement).getPropertyValue('--bulma-text');
}
if (_wrapperVersion === undefined) {
console.error('API Wrapper not found; Please include the API Wrapper before including the Page Driver');
exit();
} else {
console.log('API Wrapper found; Page Driver is ready to use');
}
// Find all tables on the page which have data-dataSource attribute
var tables = document.querySelectorAll('table[data-dataSource]');
//var tables = []
// Get all single values with data-dataSource, data-dataCol and data-dataAction
var singleValues = document.querySelectorAll('span[data-dataSource]');
// Find all search fields with data-searchTargetId
var searchFields = document.querySelectorAll('input[data-searchTargetId]');
// Find all modalForms
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(singleValues);
// Iterate over all single values
singleValues.forEach(async (singleValue) => {
writeSingelton(singleValue);
});
async function writeSingelton(element) {
const table = element.getAttribute('data-dataSource');
console.log('Table: ', table, ' Action: ', element.getAttribute('data-dataAction'), ' Element: ', element);
switch (element.getAttribute('data-dataAction')) {
case 'COUNT': {
console.log('Count action found');
element.innerHTML = await getCountByTable(table);
break;
}
case 'SPECIAL': {
if (table == 'version') {
element.innerHTML = (await getServerVersion())['version'];
break;
}
}
default: {
console.error('Unknown action found: ', element.getAttribute('data-dataAction'));
break;
}
}
element.classList.remove('is-skeleton');
}
// Attach listeners to search fields
searchFields.forEach((searchField) => {
// Apply restrictions to search field (min, max, chars, etc)
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 table = target.getAttribute('data-dataSource');
const column = target.getAttribute('data-dataCol');
const value = searchField.value;
console.log('Searching for ', value, ' in ', table, ' column ', column);
//const result = await returnTableDataByTableNameWithSearch(table, value);
//clearTable(target);
//writeDataToTable(target, result);
refreshTableByName(table);
}
});
});
// Attach listeners to modal forms
modalForms.forEach((modalForm) => {
// 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();
// Check what button submitted the form and if it has data-actionBtn = save
// If not, close modal
const pressedBtn = event.submitter;
if (pressedBtn.getAttribute('data-actionBtn') != 'save') {
modalForm.closest('.modal').classList.remove('is-active');
return;
}
// Find .entryPhase and hide it
const entryPhase = modalForm.querySelector('.entryPhase');
const loadPhase = modalForm.querySelector('.loadPhase');
if (entryPhase) {
entryPhase.classList.add('is-hidden');
}
if (loadPhase) {
loadPhase.classList.remove('is-hidden');
}
console.log('Form submitted: ', modalForm);
const table = modalForm.getAttribute('data-targetTable');
const data = new FormData(modalForm);
// Convert to JSON object
let jsonData = {};
data.forEach((value, key) => {
jsonData[key] = value;
});
console.log('JSON Data: ', jsonData);
let resp = await createEntry(table, jsonData);
console.log('Response: ', resp);
if (resp['status'] == 'CREATED') {
console.log('Entry created successfully');
modalForm.closest('.modal').classList.remove('is-active');
modalForm.reset();
// Hide loadPhase
if (loadPhase) {
loadPhase.classList.add('is-hidden');
}
// Show entryPhase
if (entryPhase) {
entryPhase.classList.remove('is-hidden');
}
} else {
// Hide loadPhase
if (loadPhase) {
loadPhase.classList.add('is-hidden');
}
// Show entryPhase
if (entryPhase) {
entryPhase.classList.remove('is-hidden');
}
// TODO: Show error message
}
// const target = document.getElementById(table);
// writeDataToTable(target, result);
// Find all tables with data-searchTargetId set to table
setTimeout(() => {
refreshTableByName(table);
updateSingeltonsByTableName(table);
}, 500);
});
});
// Helper
async function refreshTable(table) {
// Refresh a table while keeping (optionally set) search value
const searchField = document.querySelector("input[data-searchTargetId='" + table.id + "']");
if (searchField && searchField.value != '') {
const value = searchField.value;
const dbTable = table.getAttribute('data-dataSource');
const result = await returnTableDataByTableNameWithSearch(dbTable, value);
clearTable(table);
writeDataToTable(table, result);
} else {
const result = await returnTableDataByTableName(table.getAttribute('data-dataSource'));
clearTable(table);
writeDataToTable(table, result);
}
}
async function refreshTableByName(name) {
const dirtyTables = document.querySelectorAll("table[data-dataSource='" + name + "']");
for (dirty of dirtyTables) {
refreshTable(dirty);
}
}
async function updateSingeltonsByTableName(name) {
const dirtySingles = document.querySelectorAll("span[data-dataSource='" + name + "']");
for (dirty of dirtySingles) {
writeSingelton(dirty);
}
}
function clearTable(table) {
const tbody = table.querySelector('tbody');
tbody.innerHTML = '';
}
function writeDataToTable(table, data) {
console.log('Writing data to table: ', table, data);
// 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'));
});
for (resultIndex in data) {
const row = data[resultIndex];
const tr = document.createElement('tr');
requiredCols.forEach((column) => {
const td = document.createElement('td');
td.innerHTML = row[column];
tr.appendChild(td);
});
tbody.appendChild(tr);
}
}
// Handle modal
document.addEventListener('DOMContentLoaded', () => {
// Functions to open and close a modal
function openModal($el) {
$el.classList.add('is-active');
}
function closeModal($el) {
$el.classList.remove('is-active');
}
function closeAllModals() {
(document.querySelectorAll('.modal') || []).forEach(($modal) => {
closeModal($modal);
});
}
// Add a click event on buttons to open a specific modal
(document.querySelectorAll('.js-modal-trigger') || []).forEach(($trigger) => {
const modal = $trigger.dataset.target;
const $target = document.getElementById(modal);
$trigger.addEventListener('click', () => {
openModal($target);
});
});
// Add a click event on various child elements to close the parent modal
(document.querySelectorAll('.modal-close, .modal-card-head .delete, .modal-card-foot .button') || []).forEach(($close) => {
const $target = $close.closest('.modal');
$close.addEventListener('click', () => {
closeModal($target);
});
});
// Add a keyboard event to close all modals
document.addEventListener('keydown', (event) => {
if (event.key === 'Escape') {
closeAllModals();
}
});
});