diff --git a/core/templates/core/absences/log.html b/core/templates/core/absences/log.html index d4931eb..94708d2 100644 --- a/core/templates/core/absences/log.html +++ b/core/templates/core/absences/log.html @@ -156,6 +156,17 @@ them from the WorkLog). // re-runs the combined visibility pass via applyWorkerFilters(). // // Empty selection in either filter = "no restriction" for that filter. +// +// IMPLEMENTATION NOTE: we read the worker's id from the inner +// value attribute, NOT from data-worker-id on +// the row div. This matches the attendance form's proven pattern and +// avoids brittleness around how Django renders choice_value for +// ModelMultipleChoiceField iteration. +// +// REGRESSION: this used to read data-worker-id, which silently hid +// all workers in some renders. Reading from input[name="workers"] +// value is the same pattern attendance_log.html uses and is proven. +// See 7d4d7b1+ Konrad's bug report on production. (function() { var searchInput = document.getElementById('workerSearch'); var teamSelect = document.querySelector('[name="team"]'); @@ -166,18 +177,33 @@ them from the WorkLog). function applyWorkerFilters() { var q = searchInput ? searchInput.value.toLowerCase() : ''; var teamId = teamSelect ? teamSelect.value : ''; - // Workers allowed by the team filter — null means "no team filter". - var allowedWorkerIds = null; - if (teamId && teamWorkersMap[teamId]) { - // Stringify so we can compare against data-worker-id (which is a string). - allowedWorkerIds = teamWorkersMap[teamId].map(function(id) { return String(id); }); + + // Build allowed set as STRINGS (matching DOM input.value strings). + // null means "no team filter applied" (show everyone). + var allowedSet = null; + if (teamId) { + // Defensive fallback: object keys are always strings in JS, but + // be explicit about handling both raw and stringified keys just + // in case the JSON payload ever changes shape. + var teamWorkers = teamWorkersMap[teamId] || teamWorkersMap[String(teamId)]; + if (teamWorkers) { + allowedSet = {}; + teamWorkers.forEach(function(id) { allowedSet[String(id)] = true; }); + } else { + // Team key not in map — show no one (defensive) + allowedSet = {}; + } } + document.querySelectorAll('.worker-row').forEach(function(row) { var name = row.dataset.name || ''; - var wid = row.dataset.workerId || ''; - // Visible only if BOTH conditions pass. - var matchesSearch = name.indexOf(q) > -1; - var matchesTeam = (allowedWorkerIds === null) || (allowedWorkerIds.indexOf(wid) > -1); + // Read worker id from the actual checkbox input — proven reliable. + var input = row.querySelector('input[name="workers"]'); + var wid = input ? String(input.value) : ''; + + var matchesSearch = q === '' || name.indexOf(q) > -1; + var matchesTeam = (allowedSet === null) || (allowedSet[wid] === true); + row.style.display = (matchesSearch && matchesTeam) ? '' : 'none'; }); }