Enhance trainees table with sortable columns and improved bulk edit options

This commit is contained in:
Ross
2025-10-27 15:22:41 +00:00
parent fba8d5ab05
commit e503576e7a
+65 -5
View File
@@ -19,10 +19,13 @@
{% endif %}Trainees {% endif %}Trainees
</h2> </h2>
<form> <form>
<table> <table id="trainees-table">
<tr class="header"> <tr class="header">
<th>Name</th> <th class="sortable" data-col="0">Name <span class="sort-indicator"></span></th>
<th>Grade</th><th>Email</th><th>Supervisor</th><th>Edit</th> <th class="sortable" data-col="1">Grade <span class="sort-indicator"></span></th>
<th class="sortable" data-col="2">Email <span class="sort-indicator"></span></th>
<th class="sortable" data-col="3">Supervisor <span class="sort-indicator"></span></th>
<th>Edit</th>
<th class="trainee-bulk-edit">Bulk Edit<br/> <th class="trainee-bulk-edit">Bulk Edit<br/>
<span _="on click log 'test' then log 'hello' then repeat for i in <input/> <span _="on click log 'test' then log 'hello' then repeat for i in <input/>
-- set i.checked to not i.checked -- set i.checked to not i.checked
@@ -80,7 +83,7 @@
_="on click toggle .show on .trainee-bulk-edit" _="on click toggle .show on .trainee-bulk-edit"
>Bulk edit trainees</button> >Bulk edit trainees</button>
<div id="bulk-edit" class="trainee-bulk-edit"> <div id="bulk-edit" class="trainee-bulk-edit">
<h3>Bulk edit options</h3> <h3>Bulk edit options</h3>
These actions will act on all the selected trainees. Refresh the page to cancel / clear. These actions will act on all the selected trainees. Refresh the page to cancel / clear.
<div id="htmx-info"></div> <div id="htmx-info"></div>
@@ -108,7 +111,7 @@
title="Deactivates the selected users" title="Deactivates the selected users"
>Deactivate</button> >Deactivate</button>
</div> </div>
<style> <style>
.add-supervisor, .add-grade { .add-supervisor, .add-grade {
@@ -144,6 +147,63 @@
background-color: darkgrey; background-color: darkgrey;
color: white; color: white;
} }
th.sortable {
cursor: pointer;
user-select: none;
}
th .sort-indicator {
font-size: 0.8em;
margin-left: 6px;
opacity: 0.6;
}
</style> </style>
<script>
(function(){
const table = document.getElementById('trainees-table');
if (!table) return;
const getCellValue = (tr, idx) => {
const cell = tr.children[idx];
return cell ? cell.innerText.trim().toLowerCase() : '';
};
const comparer = function(idx, asc){
return function(a,b){
const v1 = getCellValue(a, idx);
const v2 = getCellValue(b, idx);
// attempt numeric compare
const n1 = parseFloat(v1.replace(/[^0-9.\-]/g, ''));
const n2 = parseFloat(v2.replace(/[^0-9.\-]/g, ''));
if (!isNaN(n1) && !isNaN(n2)){
return (n1 - n2) * (asc ? 1 : -1);
}
return v1.localeCompare(v2) * (asc ? 1 : -1);
};
};
let lastSorted = {idx: null, asc: true};
table.querySelectorAll('th.sortable').forEach(th => {
th.addEventListener('click', function(){
const idx = parseInt(th.getAttribute('data-col'));
const tbodyRows = Array.from(table.querySelectorAll('tr.trainee'));
const asc = (lastSorted.idx === idx) ? !lastSorted.asc : true;
tbodyRows.sort(comparer(idx, asc));
// re-append rows in new order
const parent = table;
tbodyRows.forEach(r => parent.appendChild(r));
// update indicators
table.querySelectorAll('th.sortable .sort-indicator').forEach(el => el.textContent = '');
th.querySelector('.sort-indicator').textContent = asc ? '▲' : '▼';
lastSorted = {idx, asc};
});
});
})();
</script>
{% endblock %} {% endblock %}