diff --git a/core/templates/core/payroll_dashboard.html b/core/templates/core/payroll_dashboard.html
index 0aa154d..e26ebf1 100644
--- a/core/templates/core/payroll_dashboard.html
+++ b/core/templates/core/payroll_dashboard.html
@@ -803,14 +803,34 @@
aria-label="Select all unpaid adjustments on this page"
title="Select all unpaid on this page">
-
Date |
- Worker |
+ {# === Sortable column headers — click toggles sort via filter form === #}
+ {# Each sortable reflects the current sort/order from adj_filter_values. #}
+ {# The arrow icon is fa-sort (inactive), fa-sort-down (desc) or fa-sort-up (asc). #}
+ {# JS click handler is wired up in the DOMContentLoaded block further down. #}
+ |
+ Date
+
+ |
+
+ Worker
+
+ |
Type |
- Amount |
+
+ Amount
+
+ |
Project |
Team |
Description |
- Status |
+
+ Status
+
+ |
Actions |
@@ -3752,6 +3772,51 @@ document.addEventListener('DOMContentLoaded', function() {
});
}
+ // === ADJUSTMENTS TAB — Sortable column headers ===
+ // Click a to sort by that column. Same column
+ // twice flips asc/desc. A different column resets to desc. Submits
+ // the filter form so the sort persists in the URL and survives
+ // pagination. Keyboard Enter/Space also works (role="button").
+ // Backend whitelists allowed columns in sort_map (see views.py),
+ // so the data-sort values here must match: date / worker / amount / status.
+ var adjFilterForm = document.getElementById('adjFilterForm');
+ if (adjFilterForm) {
+ // Scope the query to the adjustments tab. The table sits after the
+ // filter bar as a sibling, so we walk up to the tab container and
+ // collect every sortable header inside it.
+ var adjFilterBar = document.getElementById('adjustmentsFilters');
+ var adjTabRoot = adjFilterBar ? adjFilterBar.parentElement : document;
+ var sortInput = adjFilterForm.querySelector('input[name="sort"]');
+ var orderInput = adjFilterForm.querySelector('input[name="order"]');
+
+ adjTabRoot.querySelectorAll('th.sortable').forEach(function(th) {
+ // Clicking a sortable | mutates the hidden sort/order inputs
+ // and submits the form so the URL carries the new state.
+ function triggerSort() {
+ if (!sortInput || !orderInput) return;
+ var col = th.getAttribute('data-sort');
+ if (sortInput.value === col) {
+ // Same column clicked again — flip direction.
+ orderInput.value = orderInput.value === 'asc' ? 'desc' : 'asc';
+ } else {
+ // Different column — switch to it, start at desc (default).
+ sortInput.value = col;
+ orderInput.value = 'desc';
+ }
+ adjFilterForm.submit();
+ }
+ th.addEventListener('click', triggerSort);
+ // Keyboard access: Enter or Space triggers the same sort.
+ // preventDefault stops Space from scrolling the page.
+ th.addEventListener('keydown', function(ev) {
+ if (ev.key === 'Enter' || ev.key === ' ') {
+ ev.preventDefault();
+ triggerSort();
+ }
+ });
+ });
+ }
+
// --- Direct delete buttons on each unpaid row ---
// Short-circuits the edit modal's usual 2-step delete flow by opening
// #deleteConfirmModal directly with the correct form action + labels.
|