_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(); } }); });