From 1cf13048c2f9d591906f06862eb485e324f59344 Mon Sep 17 00:00:00 2001 From: Konrad du Plessis Date: Fri, 24 Apr 2026 09:58:05 +0200 Subject: [PATCH] ux(labels): extend display labels to AJAX-sourced modal renders MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes the Task 3 design-goal gap: two user-facing modals (work-log payroll preview in base.html, split-payslip preview in payroll_dashboard.html) render adjustment types via JS reading AJAX JSON. After Task 3's TYPE_CHOICES rename they were still showing the old long labels because the backend endpoints (work_log_payroll_ajax, preview_payslip) only emitted adj.type (DB value), not the display label. Added a 'type_label' field to the JSON payloads alongside the existing 'type' field. JS at both render sites now reads `adj.type_label || adj.type` — with the fallback so any stale client-side JSON degrades gracefully to the DB value rather than rendering blank. Path A still holds: adj.type in JSON stays the DB value for any identifier purposes; the new type_label is additive. Tests: 69/69. Co-Authored-By: Claude Opus 4.7 (1M context) --- core/templates/base.html | 5 ++++- core/templates/core/payroll_dashboard.html | 5 ++++- core/views.py | 7 +++++++ 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/core/templates/base.html b/core/templates/base.html index 98ec260..23b9e9c 100644 --- a/core/templates/base.html +++ b/core/templates/base.html @@ -549,7 +549,10 @@ document.addEventListener('DOMContentLoaded', function() { var adjBody = document.createElement('tbody'); data.adjustments.forEach(function(adj) { var tr = document.createElement('tr'); - tr.appendChild(el('td', null, adj.type)); + // Prefer the short display label from the server; fall back to the + // raw DB value if a stale JSON response doesn't include type_label + // (graceful degradation — never render a blank cell). + tr.appendChild(el('td', null, adj.type_label || adj.type)); var wTd = document.createElement('td'); wTd.appendChild(link('/workers/' + adj.worker_id + '/', adj.worker_name)); tr.appendChild(wTd); diff --git a/core/templates/core/payroll_dashboard.html b/core/templates/core/payroll_dashboard.html index 523e1d8..011fbe8 100644 --- a/core/templates/core/payroll_dashboard.html +++ b/core/templates/core/payroll_dashboard.html @@ -2395,7 +2395,10 @@ document.addEventListener('DOMContentLoaded', function() { labelWrap.className = 'd-flex justify-content-between flex-grow-1'; var label = document.createElement('span'); - label.textContent = adj.type + (adj.project ? ' (' + adj.project + ')' : ''); + // Prefer the short display label ('Loan' / 'Advance' / 'Advance Repaid') + // from the server; fall back to the raw DB value if a stale JSON + // response doesn't include type_label (graceful degradation). + label.textContent = (adj.type_label || adj.type) + (adj.project ? ' (' + adj.project + ')' : ''); if (adj.description) { var descSmall = document.createElement('small'); descSmall.className = 'text-muted ms-1'; diff --git a/core/views.py b/core/views.py index 2f1b9d7..86bacbb 100644 --- a/core/views.py +++ b/core/views.py @@ -1020,8 +1020,11 @@ def work_log_payroll_ajax(request, log_id): } for row in ctx['worker_rows']] # Adjustments linked directly to this work_log (Overtime, etc.). + # We emit BOTH 'type' (raw DB value — stable identifier for any JS logic) + # AND 'type_label' (short display label from get_type_display()) — visible UI. adjustments = [{ 'type': adj.type, + 'type_label': adj.get_type_display(), 'amount': float(adj.amount), 'worker_id': adj.worker.id, 'worker_name': adj.worker.name, @@ -4236,7 +4239,11 @@ def preview_payslip(request, worker_id): adj_total += float(adj.amount) if adj.type in ADDITIVE_TYPES else -float(adj.amount) adjustments_list.append({ 'id': adj.id, + # 'type' keeps the raw DB value so any JS that uses it as an + # identifier keeps working; 'type_label' is the short display + # label ('Loan' / 'Advance' / 'Advance Repaid' etc.) for visible UI. 'type': adj.type, + 'type_label': adj.get_type_display(), 'amount': float(adj.amount), 'sign': sign, 'description': adj.description,