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';
});
}