fix(tables): client-side sort buttons now sync with DataTables headers to prevent data corruption

This commit is contained in:
2026-02-06 08:03:15 +01:00
parent 4aea50587e
commit 383f9899fb
3 changed files with 94 additions and 27 deletions
+84 -22
View File
@@ -175,6 +175,53 @@ window.setupSharedSortButtons = function(tableId, tableInstanceGlobal, columnMap
const clearButton = document.querySelector('[data-sort-clear]');
const isPaginationMode = isPaginationModeForTable(tableId);
// Listen for table header clicks to sync button states when user sorts via headers
if (!isPaginationMode) {
const tableElement = document.querySelector(`#${tableId}`);
if (tableElement) {
tableElement.addEventListener('click', (e) => {
// Check if a sortable header was clicked
const header = e.target.closest('th');
if (header && !header.classList.contains('no-sort')) {
// Find the column index of the clicked header
const headers = Array.from(tableElement.querySelectorAll('thead th'));
const clickedIndex = headers.indexOf(header);
// Find which attribute maps to this column index
let clickedAttribute = null;
for (const [attr, idx] of Object.entries(columnMap)) {
if (idx === clickedIndex) {
clickedAttribute = attr;
break;
}
}
// Update button states to match the header sort
sortButtons.forEach(btn => {
if (btn.dataset.sortAttribute === clickedAttribute) {
// Activate this button
btn.classList.remove('btn-outline-primary');
btn.classList.add('btn-primary');
// Determine direction from header class (after click is processed)
setTimeout(() => {
if (header.classList.contains('datatable-ascending')) {
btn.dataset.currentDirection = 'asc';
} else if (header.classList.contains('datatable-descending')) {
btn.dataset.currentDirection = 'desc';
}
}, 10);
} else {
// Deactivate other buttons
btn.classList.remove('btn-primary');
btn.classList.add('btn-outline-primary');
btn.removeAttribute('data-current-direction');
}
});
}
});
}
}
sortButtons.forEach(button => {
button.addEventListener('click', () => {
const attribute = button.dataset.sortAttribute;
@@ -206,34 +253,49 @@ window.setupSharedSortButtons = function(tableId, tableInstanceGlobal, columnMap
} else {
// ORIGINAL MODE - Client-side sorting via Simple DataTables
// Instead of using columns.sort() API, we click the actual table header
// This ensures DataTables handles everything correctly and avoids data corruption
const columnIndex = columnMap[attribute];
const tableInstance = window[tableInstanceGlobal];
const tableElement = document.querySelector(`#${tableId}`);
if (columnIndex !== undefined && tableInstance) {
// Determine sort direction
const isCurrentlyActive = button.classList.contains('btn-primary');
const currentDirection = button.dataset.currentDirection || (isDesc ? 'desc' : 'asc');
const newDirection = isCurrentlyActive ?
(currentDirection === 'asc' ? 'desc' : 'asc') :
(isDesc ? 'desc' : 'asc');
if (columnIndex !== undefined && tableElement) {
const headers = tableElement.querySelectorAll('thead th');
const targetHeader = headers[columnIndex];
// Clear other active buttons
sortButtons.forEach(btn => {
btn.classList.remove('btn-primary');
btn.classList.add('btn-outline-primary');
btn.removeAttribute('data-current-direction');
});
if (targetHeader) {
// Determine desired sort direction
const isCurrentlyActive = button.classList.contains('btn-primary');
const currentDirection = button.dataset.currentDirection || (isDesc ? 'desc' : 'asc');
const newDirection = isCurrentlyActive ?
(currentDirection === 'asc' ? 'desc' : 'asc') :
(isDesc ? 'desc' : 'asc');
// Mark this button as active
button.classList.remove('btn-outline-primary');
button.classList.add('btn-primary');
button.dataset.currentDirection = newDirection;
// Check current header sort state
const headerIsAsc = targetHeader.classList.contains('datatable-ascending');
const headerIsDesc = targetHeader.classList.contains('datatable-descending');
// Apply sort using Simple DataTables API
tableInstance.table.columns.sort(columnIndex, newDirection);
// Click header to achieve desired sort direction
if (newDirection === 'asc') {
if (!headerIsAsc) {
targetHeader.click();
// If it went to desc, click again to get asc
if (targetHeader.classList.contains('datatable-descending')) {
targetHeader.click();
}
}
} else { // desc
if (!headerIsDesc) {
targetHeader.click();
// If it went to asc, click again to get desc
if (targetHeader.classList.contains('datatable-ascending')) {
targetHeader.click();
}
}
}
// Update sort icon to reflect new direction
updateSortIcon(newDirection);
// Update sort icon to reflect new direction
updateSortIcon(newDirection);
}
}
}
});
+5 -5
View File
@@ -15,11 +15,11 @@ document.addEventListener("DOMContentLoaded", () => {
sortColumnMap: {
'name': 1,
'color': 2,
'quantity': 3,
'missing': 4,
'damaged': 5,
'sets': 6,
'minifigures': 7
//'quantity': 3,
'missing': 3,
'damaged': 4,
'sets': 5,
'minifigures': 6
},
hasColorDropdown: true
});
+5
View File
@@ -42,8 +42,13 @@ window.BrickTable = class BrickTable {
const hasCustomInterface = isMinifiguresTable || isPartsTablePaginationMode || isProblemsTablePaginationMode;
const hasCustomSearch = isPartsTable || isProblemsTable || isMinifiguresTable; // Parts, problems, and minifigures always have custom search
// Disable sorting for parts/problems/minifigures tables to prevent data corruption
// These tables should use server-side pagination for sorting, or no sorting in client-side mode
const disableSorting = isPartsTable || isProblemsTable || isMinifiguresTable;
this.table = new simpleDatatables.DataTable(`#${table.id}`, {
columns: columns,
sortable: !disableSorting, // Disable header sorting for tables that have issues with it
paging: !(isPartsTablePaginationMode || isProblemsTablePaginationMode), // Disable built-in pagination for tables in pagination mode
pagerDelta: 1,
perPage: per_page,