/** * 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 `${conf.label}`; } }, /** * 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');