From 00f16df8b142d94ab614090e7697d66066667798 Mon Sep 17 00:00:00 2001 From: Konrad du Plessis Date: Tue, 24 Mar 2026 22:37:48 +0200 Subject: [PATCH] Add team filter dropdown to batch pay modal MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Client-side filter lets admin narrow batch payment list by team. Selecting a team hides other workers, unchecks them (so they won't be paid), and updates the summary total. Select All respects the filter — only toggles visible rows. Filter resets when switching between schedule/pay-all modes. Co-Authored-By: Claude Opus 4.6 --- core/templates/core/payroll_dashboard.html | 76 ++++++++++++++++++++-- 1 file changed, 70 insertions(+), 6 deletions(-) diff --git a/core/templates/core/payroll_dashboard.html b/core/templates/core/payroll_dashboard.html index 939a882..4f6f983 100644 --- a/core/templates/core/payroll_dashboard.html +++ b/core/templates/core/payroll_dashboard.html @@ -2078,6 +2078,7 @@ document.addEventListener('DOMContentLoaded', function() { // --- Helper: Build a table row for an eligible worker --- function buildWorkerRow(w, idx) { var tr = document.createElement('tr'); + tr.dataset.team = w.team_name; // Used by team filter // Checkbox cell var tdCb = document.createElement('td'); @@ -2237,6 +2238,42 @@ document.addEventListener('DOMContentLoaded', function() { return; } + // --- Team filter dropdown --- + // Extract unique team names from the eligible workers + var teamNames = []; + data.eligible.forEach(function(w) { + if (teamNames.indexOf(w.team_name) === -1) teamNames.push(w.team_name); + }); + teamNames.sort(); + + var filterRow = document.createElement('div'); + filterRow.className = 'd-flex align-items-center gap-2 mb-3'; + + var filterLabel = document.createElement('label'); + filterLabel.className = 'text-muted small mb-0'; + filterLabel.textContent = 'Filter by team:'; + filterLabel.setAttribute('for', 'batchTeamFilter'); + filterRow.appendChild(filterLabel); + + var filterSelect = document.createElement('select'); + filterSelect.id = 'batchTeamFilter'; + filterSelect.className = 'form-select form-select-sm'; + filterSelect.style.width = 'auto'; + + var optAll = document.createElement('option'); + optAll.value = ''; + optAll.textContent = 'All Teams'; + filterSelect.appendChild(optAll); + + teamNames.forEach(function(name) { + var opt = document.createElement('option'); + opt.value = name; + opt.textContent = name; + filterSelect.appendChild(opt); + }); + filterRow.appendChild(filterSelect); + body.appendChild(filterRow); + // --- Summary header --- var summary = document.createElement('div'); summary.className = 'alert alert-info d-flex justify-content-between align-items-center mb-3'; @@ -2292,10 +2329,13 @@ document.addEventListener('DOMContentLoaded', function() { body.appendChild(table); // --- Select All checkbox behavior --- + // Only affects VISIBLE rows (respects team filter) selectAllCb.addEventListener('change', function() { - var cbs = body.querySelectorAll('.batch-worker-cb'); - for (var i = 0; i < cbs.length; i++) { - cbs[i].checked = selectAllCb.checked; + var rows = tbody.querySelectorAll('tr'); + for (var i = 0; i < rows.length; i++) { + if (rows[i].style.display !== 'none') { + rows[i].querySelector('.batch-worker-cb').checked = selectAllCb.checked; + } } updateBatchSummary(data, summary); }); @@ -2304,15 +2344,39 @@ document.addEventListener('DOMContentLoaded', function() { body.addEventListener('change', function(e) { if (e.target.classList.contains('batch-worker-cb')) { updateBatchSummary(data, summary); - var allCbs = body.querySelectorAll('.batch-worker-cb'); + // Check if all VISIBLE checkboxes are checked var allChecked = true; - for (var i = 0; i < allCbs.length; i++) { - if (!allCbs[i].checked) { allChecked = false; break; } + var rows = tbody.querySelectorAll('tr'); + for (var i = 0; i < rows.length; i++) { + if (rows[i].style.display !== 'none') { + if (!rows[i].querySelector('.batch-worker-cb').checked) { + allChecked = false; break; + } + } } selectAllCb.checked = allChecked; } }); + // --- Team filter behavior --- + // Shows/hides rows, checks matching workers, unchecks hidden ones + filterSelect.addEventListener('change', function() { + var selectedTeam = filterSelect.value; + var rows = tbody.querySelectorAll('tr'); + for (var i = 0; i < rows.length; i++) { + var row = rows[i]; + if (!selectedTeam || row.dataset.team === selectedTeam) { + row.style.display = ''; + row.querySelector('.batch-worker-cb').checked = true; + } else { + row.style.display = 'none'; + row.querySelector('.batch-worker-cb').checked = false; + } + } + selectAllCb.checked = true; + updateBatchSummary(data, summary); + }); + // --- Skipped workers (collapsible) --- if (data.skipped.length > 0) { body.appendChild(buildSkippedSection(data.skipped));