fix: DockerSecretsResolver - don't normalize absolute paths like /var/www/html/...
Some checks failed
Deploy Application / deploy (push) Has been cancelled
Some checks failed
Deploy Application / deploy (push) Has been cancelled
This commit is contained in:
@@ -1,5 +1,3 @@
|
||||
import { Module } from '../core/module.js';
|
||||
|
||||
/**
|
||||
* Admin Data Table Module
|
||||
*
|
||||
@@ -9,9 +7,9 @@ import { Module } from '../core/module.js';
|
||||
* - Search/filtering
|
||||
* - Auto-refresh
|
||||
*/
|
||||
export class AdminDataTable extends Module {
|
||||
export class AdminDataTable {
|
||||
constructor(element) {
|
||||
super(element);
|
||||
this.element = element;
|
||||
|
||||
this.resource = element.dataset.resource;
|
||||
this.apiEndpoint = element.dataset.apiEndpoint;
|
||||
@@ -26,11 +24,6 @@ export class AdminDataTable extends Module {
|
||||
}
|
||||
|
||||
async init() {
|
||||
if (!this.apiEndpoint) {
|
||||
console.warn('AdminDataTable: No API endpoint configured');
|
||||
return;
|
||||
}
|
||||
|
||||
this.tbody = this.element.querySelector('tbody');
|
||||
this.thead = this.element.querySelector('thead');
|
||||
|
||||
@@ -39,27 +32,38 @@ export class AdminDataTable extends Module {
|
||||
return;
|
||||
}
|
||||
|
||||
// Always setup event listeners for sorting, even if data is already loaded
|
||||
this.setupEventListeners();
|
||||
|
||||
// Only load data if table is initially empty
|
||||
if (this.tbody.children.length === 0) {
|
||||
// Only load data via AJAX if API endpoint is configured and table is empty
|
||||
if (this.apiEndpoint && this.tbody.children.length === 0) {
|
||||
await this.loadData();
|
||||
}
|
||||
}
|
||||
|
||||
setupEventListeners() {
|
||||
// Sortable columns
|
||||
if (this.sortable && this.thead) {
|
||||
this.thead.querySelectorAll('th').forEach((th, index) => {
|
||||
const columnKey = th.dataset.column || this.getColumnKeyFromIndex(index);
|
||||
// Sortable columns - only for columns with data-column attribute
|
||||
// Check both table-level sortable flag and individual column sortable attribute
|
||||
if (this.thead) {
|
||||
this.thead.querySelectorAll('th[data-column]').forEach((th) => {
|
||||
const columnKey = th.dataset.column;
|
||||
|
||||
if (columnKey) {
|
||||
th.style.cursor = 'pointer';
|
||||
th.classList.add('sortable');
|
||||
// Only make sortable if table-level sortable is enabled OR column has data-column
|
||||
if (this.sortable || th.hasAttribute('data-column')) {
|
||||
th.style.cursor = 'pointer';
|
||||
th.classList.add('sortable');
|
||||
|
||||
th.addEventListener('click', () => {
|
||||
this.sortByColumn(columnKey, th);
|
||||
});
|
||||
th.addEventListener('click', () => {
|
||||
// If API endpoint is available, use AJAX sorting
|
||||
if (this.apiEndpoint) {
|
||||
this.sortByColumn(columnKey, th);
|
||||
} else {
|
||||
// Otherwise, do client-side sorting
|
||||
this.sortByColumnClientSide(columnKey, th);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -114,8 +118,49 @@ export class AdminDataTable extends Module {
|
||||
th.dataset.sortDir = newDir;
|
||||
th.classList.add(`sort-${newDir}`);
|
||||
|
||||
// Reload data
|
||||
await this.loadData();
|
||||
// Reload data if API endpoint is available
|
||||
if (this.apiEndpoint) {
|
||||
await this.loadData();
|
||||
}
|
||||
}
|
||||
|
||||
sortByColumnClientSide(columnKey, th) {
|
||||
// Client-side sorting for tables without API endpoint
|
||||
const currentDir = th.dataset.sortDir;
|
||||
const newDir = currentDir === 'asc' ? 'desc' : 'asc';
|
||||
|
||||
// Update UI
|
||||
this.thead.querySelectorAll('th').forEach(header => {
|
||||
header.removeAttribute('data-sort-dir');
|
||||
header.classList.remove('sort-asc', 'sort-desc');
|
||||
});
|
||||
|
||||
th.dataset.sortDir = newDir;
|
||||
th.classList.add(`sort-${newDir}`);
|
||||
|
||||
// Get all rows
|
||||
const rows = Array.from(this.tbody.querySelectorAll('tr'));
|
||||
const columnIndex = Array.from(this.thead.querySelectorAll('th')).indexOf(th);
|
||||
|
||||
// Sort rows
|
||||
rows.sort((a, b) => {
|
||||
const aValue = a.cells[columnIndex]?.textContent?.trim() || '';
|
||||
const bValue = b.cells[columnIndex]?.textContent?.trim() || '';
|
||||
|
||||
// Try numeric comparison first
|
||||
const aNum = parseFloat(aValue);
|
||||
const bNum = parseFloat(bValue);
|
||||
if (!isNaN(aNum) && !isNaN(bNum)) {
|
||||
return newDir === 'asc' ? aNum - bNum : bNum - aNum;
|
||||
}
|
||||
|
||||
// String comparison
|
||||
const comparison = aValue.localeCompare(bValue);
|
||||
return newDir === 'asc' ? comparison : -comparison;
|
||||
});
|
||||
|
||||
// Re-append sorted rows
|
||||
rows.forEach(row => this.tbody.appendChild(row));
|
||||
}
|
||||
|
||||
async loadData() {
|
||||
@@ -138,7 +183,17 @@ export class AdminDataTable extends Module {
|
||||
try {
|
||||
this.setLoadingState(true);
|
||||
|
||||
const response = await fetch(url);
|
||||
const response = await fetch(url, {
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
'X-Requested-With': 'XMLHttpRequest'
|
||||
}
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (result.success) {
|
||||
@@ -152,7 +207,20 @@ export class AdminDataTable extends Module {
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('AdminDataTable: Failed to load data', error);
|
||||
this.showError('Failed to load data');
|
||||
console.error('AdminDataTable: URL was', url);
|
||||
|
||||
// Fallback to client-side sorting if API fails
|
||||
if (this.currentSort && this.tbody.children.length > 0) {
|
||||
console.warn('AdminDataTable: API failed, falling back to client-side sorting');
|
||||
const th = this.thead.querySelector(`th[data-column="${this.currentSort.column}"]`);
|
||||
if (th) {
|
||||
this.sortByColumnClientSide(this.currentSort.column, th);
|
||||
} else {
|
||||
this.showError('Failed to load data. Please refresh the page.');
|
||||
}
|
||||
} else {
|
||||
this.showError('Failed to load data');
|
||||
}
|
||||
} finally {
|
||||
this.setLoadingState(false);
|
||||
}
|
||||
@@ -288,8 +356,19 @@ export class AdminDataTable extends Module {
|
||||
}
|
||||
|
||||
// Auto-initialize all admin data tables
|
||||
// Initialize tables with API endpoints (AJAX loading)
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
document.querySelectorAll('[data-resource][data-api-endpoint]').forEach(table => {
|
||||
new AdminDataTable(table).init();
|
||||
});
|
||||
|
||||
// Also initialize tables with sortable attribute but without API endpoint (client-side sorting)
|
||||
document.querySelectorAll('table.admin-table[data-sortable="true"]').forEach(table => {
|
||||
if (!table.dataset.apiEndpoint) {
|
||||
// Create a minimal AdminDataTable instance for client-side sorting
|
||||
const dataTable = new AdminDataTable(table);
|
||||
dataTable.sortable = true;
|
||||
dataTable.init();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user