328 lines
13 KiB
JavaScript
328 lines
13 KiB
JavaScript
/**
|
||
* EveAI List View Component
|
||
* JavaScript functionaliteit voor het beheren van lijst-weergaven
|
||
*/
|
||
|
||
// Namespace aanmaken als deze nog niet bestaat
|
||
if (typeof window.EveAI === 'undefined') {
|
||
window.EveAI = {};
|
||
}
|
||
|
||
// List View namespace
|
||
window.EveAI.ListView = {
|
||
// Opslag voor lijst-view instanties
|
||
instances: {},
|
||
// Registry voor custom formatters (kan uitgebreid worden door templates)
|
||
formatters: {
|
||
// typeBadge: toont een badge voor agent/task/tool (robuust met Bootstrap 5 classes)
|
||
typeBadge: function(cell) {
|
||
const raw = (cell.getValue() || '').toString();
|
||
const val = raw.toLowerCase();
|
||
const map = {
|
||
'agent': { cls: 'badge text-bg-primary', label: 'Agent' },
|
||
'task': { cls: 'badge text-bg-warning', label: 'Task' },
|
||
'tool': { cls: 'badge text-bg-info', label: 'Tool' },
|
||
};
|
||
const conf = map[val] || { cls: 'badge text-bg-secondary', label: (raw ? raw : 'Item') };
|
||
return `<span class="${conf.cls}">${conf.label}</span>`;
|
||
}
|
||
},
|
||
|
||
/**
|
||
* Initialiseer een Tabulator lijst-view
|
||
* @param {string} elementId - ID van het HTML element
|
||
* @param {object} config - Configuratie object voor Tabulator
|
||
* @returns {Tabulator} - Tabulator instantie
|
||
*/
|
||
initialize: function(elementId, config) {
|
||
// Combineer standaard configuratie met aangepaste configuratie
|
||
const defaultConfig = {
|
||
height: 600,
|
||
layout: "fitColumns",
|
||
selectable: 1, // single-row selection for consistent UX across Tabulator versions
|
||
movableColumns: true,
|
||
pagination: "local",
|
||
paginationSize: 15,
|
||
paginationSizeSelector: [10, 15, 20, 50, 100],
|
||
};
|
||
|
||
// Respecteer eventueel meegegeven tableHeight alias
|
||
if (config && typeof config.tableHeight !== 'undefined' && typeof config.height === 'undefined') {
|
||
config.height = config.tableHeight;
|
||
}
|
||
|
||
// Los string-formatters op naar functies via registry
|
||
if (config && Array.isArray(config.columns)) {
|
||
config.columns = config.columns.map(col => {
|
||
const newCol = { ...col };
|
||
if (typeof newCol.formatter === 'string' && window.EveAI && window.EveAI.ListView && window.EveAI.ListView.formatters) {
|
||
const key = newCol.formatter.trim();
|
||
const fmt = window.EveAI.ListView.formatters[key];
|
||
if (typeof fmt === 'function') {
|
||
newCol.formatter = fmt;
|
||
}
|
||
}
|
||
return newCol;
|
||
});
|
||
}
|
||
|
||
const tableConfig = {...defaultConfig, ...config};
|
||
|
||
// Enforce single-row selection across Tabulator versions
|
||
if (tableConfig.selectable === true) {
|
||
tableConfig.selectable = 1;
|
||
}
|
||
|
||
// Respect and enforce unique row index across Tabulator versions
|
||
if (config && typeof config.index === 'string' && config.index) {
|
||
// Tabulator v4/v5
|
||
tableConfig.index = config.index;
|
||
// Tabulator v6+ (alias)
|
||
tableConfig.indexField = config.index;
|
||
}
|
||
|
||
// Voeg rij selectie event toe
|
||
tableConfig.rowSelectionChanged = (data, rows) => {
|
||
// Update de geselecteerde rij in onze instance
|
||
if (this.instances[elementId]) {
|
||
this.instances[elementId].selectedRow = rows.length > 0 ? rows[0].getData() : null;
|
||
this.updateActionButtons(elementId);
|
||
}
|
||
};
|
||
|
||
// Initialiseer de Tabulator
|
||
try {
|
||
const table = new Tabulator(`#${elementId}`, tableConfig);
|
||
|
||
// Bewaar de instance
|
||
this.instances[elementId] = {
|
||
table: table,
|
||
config: config || {},
|
||
selectedRow: null
|
||
};
|
||
|
||
// Bij initialisatie, update de knoppen (standaard inactief voor requiresSelection=true)
|
||
setTimeout(() => {
|
||
this.updateActionButtons(elementId);
|
||
}, 0);
|
||
|
||
// Forceer enkelvoudige selectie op klik voor consistente UX
|
||
try {
|
||
table.on('rowClick', function(e, row) {
|
||
// voorkom multi-select: altijd eerst deselecteren
|
||
row.getTable().deselectRow();
|
||
row.select();
|
||
});
|
||
table.on('cellClick', function(e, cell) {
|
||
const row = cell.getRow();
|
||
row.getTable().deselectRow();
|
||
row.select();
|
||
});
|
||
// Optioneel: cursor als pointer bij hover
|
||
table.on('rowFormatter', function(row) {
|
||
row.getElement().style.cursor = 'pointer';
|
||
});
|
||
} catch (e) {
|
||
console.warn('Kon click-selectie handlers niet registreren:', e);
|
||
}
|
||
|
||
return table;
|
||
} catch (error) {
|
||
console.error(`Fout bij het initialiseren van Tabulator voor ${elementId}:`, error);
|
||
return null;
|
||
}
|
||
},
|
||
|
||
/**
|
||
* Afhandelen van actieknoppen
|
||
* @param {string} action - Actie identifier
|
||
* @param {boolean} requiresSelection - Of de actie een selectie vereist
|
||
* @param {string} tableId - ID van de tabel
|
||
* @returns {boolean} - Succes indicator
|
||
*/
|
||
/**
|
||
* Update actieknoppen op basis van geselecteerde rij
|
||
* @param {string} tableId - ID van de tabel
|
||
*/
|
||
updateActionButtons: function(tableId) {
|
||
const instance = this.instances[tableId];
|
||
if (!instance) return;
|
||
|
||
const container = document.getElementById(tableId);
|
||
if (!container) return;
|
||
|
||
const buttons = container.parentElement.querySelectorAll('button[onclick*="handleListViewAction"]');
|
||
|
||
buttons.forEach(button => {
|
||
// Parse de onclick attribuut om de actiewaarde te krijgen
|
||
const onclickAttr = button.getAttribute('onclick');
|
||
const match = onclickAttr.match(/handleListViewAction\('([^']+)'/);
|
||
if (match) {
|
||
const actionValue = match[1];
|
||
// Vind de actie in de configuratie
|
||
const action = instance.config.actions.find(a => a.value === actionValue);
|
||
if (action && action.requiresSelection === true) {
|
||
// Schakel de knop in/uit op basis van selectie
|
||
button.disabled = !instance.selectedRow;
|
||
}
|
||
}
|
||
});
|
||
|
||
// Update de verborgen input met geselecteerde rij data
|
||
this._updateSelectedRowInput(tableId);
|
||
},
|
||
|
||
/**
|
||
* Update de verborgen input met geselecteerde rij gegevens
|
||
* @param {string} tableId - ID van de tabel
|
||
* @private
|
||
*/
|
||
_updateSelectedRowInput: function(tableId) {
|
||
const instance = this.instances[tableId];
|
||
const hiddenInput = document.getElementById(`${tableId}-selected-row`);
|
||
|
||
if (hiddenInput && instance && instance.selectedRow) {
|
||
// Bewaar de geselecteerde rij-ID
|
||
hiddenInput.value = JSON.stringify({value: instance.selectedRow.id});
|
||
} else if (hiddenInput) {
|
||
hiddenInput.value = '';
|
||
}
|
||
},
|
||
|
||
handleAction: function(action, requiresSelection, tableId) {
|
||
const selectedRowInput = document.getElementById(`${tableId}-selected-row`);
|
||
const actionInput = document.getElementById(`${tableId}-action`);
|
||
const table = Tabulator.findTable(`#${tableId}`)[0];
|
||
|
||
if (!table) {
|
||
console.error(`Tabulator tabel met ID ${tableId} niet gevonden`);
|
||
return false;
|
||
}
|
||
|
||
// Als de actie een selectie vereist, controleer of er een rij is geselecteerd
|
||
if (requiresSelection) {
|
||
const selectedRows = table.getSelectedRows();
|
||
if (selectedRows.length === 0) {
|
||
alert('Selecteer a.u.b. eerst een item uit de lijst.');
|
||
return false;
|
||
}
|
||
|
||
// Haal de data van de geselecteerde rij op en sla deze op
|
||
const rowData = selectedRows[0].getData();
|
||
selectedRowInput.value = JSON.stringify({ value: rowData.id });
|
||
|
||
// Update de instance als deze bestaat
|
||
if (this.instances[tableId]) {
|
||
this.instances[tableId].selectedRow = rowData;
|
||
}
|
||
}
|
||
|
||
// Stel de actie in en verstuur het formulier
|
||
actionInput.value = action;
|
||
|
||
// Zoek het juiste formulier en verstuur het
|
||
const form = document.getElementById(`${tableId}-form`) ||
|
||
table.element.closest('form');
|
||
|
||
if (form) {
|
||
form.submit();
|
||
return true;
|
||
} else {
|
||
console.error(`Geen formulier gevonden voor tabel ${tableId}`);
|
||
return false;
|
||
}
|
||
}
|
||
};
|
||
|
||
// Functie om beschikbaar te maken in templates (met guard en expliciete event-parameter)
|
||
if (typeof window.handleListViewAction !== 'function') {
|
||
window.handleListViewAction = function(action, requiresSelection, e) {
|
||
const evt = e || undefined; // geen gebruik van deprecated window.event
|
||
const target = evt && (evt.target || evt.srcElement);
|
||
|
||
// 1) Bepaal tableId zo robuust mogelijk
|
||
let tableId = null;
|
||
if (target) {
|
||
// Zoek het werkelijke trigger element (button/anchor) i.p.v. een child node
|
||
const trigger = (typeof target.closest === 'function') ? target.closest('button, a') : target;
|
||
|
||
// a) Respecteer expliciete data-attribute op knop
|
||
tableId = trigger && trigger.getAttribute ? trigger.getAttribute('data-table-id') : null;
|
||
|
||
if (!tableId) {
|
||
// b) Zoek dichtstbijzijnde container met een tabulator-list-view erin
|
||
const containerEl = trigger && typeof trigger.closest === 'function' ? trigger.closest('.container') : null;
|
||
const scopedTable = containerEl ? containerEl.querySelector('.tabulator-list-view') : null;
|
||
tableId = scopedTable ? scopedTable.id : null;
|
||
}
|
||
if (!tableId) {
|
||
// c) Val terug op dichtstbijzijnde form id-afleiding (enkel als het een -form suffix heeft)
|
||
const form = trigger && typeof trigger.closest === 'function' ? trigger.closest('form') : null;
|
||
if (form && typeof form.id === 'string' && form.id.endsWith('-form')) {
|
||
tableId = form.id.slice(0, -'-form'.length);
|
||
}
|
||
}
|
||
}
|
||
if (!tableId) {
|
||
// d) Laatste redmiddel: pak de eerste tabulator-list-view op de pagina
|
||
const anyTable = document.querySelector('.tabulator-list-view');
|
||
tableId = anyTable ? anyTable.id : null;
|
||
}
|
||
if (!tableId) {
|
||
console.error('Kan tableId niet bepalen voor action:', action);
|
||
return false;
|
||
}
|
||
|
||
const listView = window.EveAI && window.EveAI.ListView ? window.EveAI.ListView : null;
|
||
const instance = listView && listView.instances ? listView.instances[tableId] : null;
|
||
|
||
// 2) Indien selectie vereist, enforce
|
||
if (requiresSelection === true) {
|
||
if (!instance || !instance.selectedRow) {
|
||
// Probeer nog de Tabulator API als instance ontbreekt
|
||
try {
|
||
const table = Tabulator.findTable(`#${tableId}`)[0];
|
||
const rows = table ? table.getSelectedRows() : [];
|
||
if (!rows || rows.length === 0) {
|
||
alert('Selecteer eerst een item uit de lijst.');
|
||
return false;
|
||
}
|
||
if (instance) instance.selectedRow = rows[0].getData();
|
||
} catch (_) {
|
||
alert('Selecteer eerst een item uit de lijst.');
|
||
return false;
|
||
}
|
||
}
|
||
}
|
||
|
||
// 3) Embedded handler krijgt voorrang
|
||
const embeddedHandlers = listView && listView.embeddedHandlers ? listView.embeddedHandlers : null;
|
||
const embedded = embeddedHandlers && embeddedHandlers[tableId];
|
||
if (typeof embedded === 'function') {
|
||
try {
|
||
embedded(action, instance ? instance.selectedRow : null, tableId);
|
||
return true;
|
||
} catch (err) {
|
||
console.error('Embedded handler error:', err);
|
||
return false;
|
||
}
|
||
}
|
||
|
||
// 4) Vervallen naar legacy form submit/JS handler
|
||
if (listView && typeof listView.handleAction === 'function') {
|
||
return listView.handleAction(action, requiresSelection, tableId);
|
||
}
|
||
|
||
// 5) Allerbeste laatste fallback – probeer form submit met hidden inputs
|
||
const actionInput = document.getElementById(`${tableId}-action`);
|
||
if (actionInput) actionInput.value = action;
|
||
const form = document.getElementById(`${tableId}-form`);
|
||
if (form) { form.submit(); return true; }
|
||
|
||
console.error('Geen geldige handler gevonden voor action:', action);
|
||
return false;
|
||
}
|
||
}
|
||
|
||
console.log('EveAI List View component geladen');
|