# UX Polish Pass — Design (24 Apr 2026) ## Origin Konrad, after the Perf Quick-Wins Pass shipped: > _1. I named Loans — "New Loan" because in the dropdown for adjustments, > I wanted to log a new loan. But now throughout the app it says "New > Loan". Can we change it everywhere to just "Loan". (How Will this > handle historic data and formulas? Make sure we do not break the > app)_ > > _2. Is it Possible to change Advance Payment to Adv Pay, Advance > Repayment to Adv Repay. It will take less space in the tables (all > tables)_ — refined in the brainstorm to **"Advance" / "Advance > Repaid"** for readability > > _3. Can we change the colors for flags/tags in Pending Payments, > Payment History, Loans and Advances to the colors we decided on in > Adjustments (colors for Loans, advances, bonusses etc). Uniform > colors throughout the app. Please make a note in some file regarding > the colors for easy reference in the future._ > > _4. The group summary Column in Adjustments is a bit narrow, can we > have it a bit wider?_ Four independent UX polish asks — no behavioural change, no schema change, no formula change. ## Goal Tighten the visual vocabulary of the payroll area: 1. Cleaner, shorter adjustment type labels in tables 2. Consistent colour semantics across every payroll tab (one colour per concept, everywhere it appears) 3. Fix a CSS bug squashing the group-summary row All four items ship in one pass. Collectively they change how payroll LOOKS without touching how it WORKS. ## Who it's for Konrad — who sees these labels and colours dozens of times a day — and any future viewer of the payroll pages. ## Scope decision — Path A: display-only rename (chosen) Konrad's own words: _"How will this handle historic data and formulas? Make sure we do not break the app."_ That anxiety is the exact scenario where **display-only** shines. Two paths were on the table: - **A — display-only**: `TYPE_CHOICES` second tuple value (the "human label") gets shortened; the first tuple value (the DB value) stays forever identical to today. No data migration. No constants touched. No test changes. Every historic row keeps `type='New Loan'` forever. - **B — full rename w/ data migration**: rename canonical value everywhere; add `UPDATE payrolladjustment SET type='Loan' WHERE type='New Loan'` migration; touch ~30 source files. Path A picked. Every Konrad-visible goal is satisfied; every risk ground-sourced in historic data/formulas is eliminated. ## 1. Display-only rename — the mechanics **`core/models.py`** — the only data-layer change: ```python TYPE_CHOICES = [ ('Bonus', 'Bonus'), # unchanged ('Overtime', 'Overtime'), # unchanged ('Deduction', 'Deduction'), # unchanged ('Loan Repayment', 'Loan Repayment'), # unchanged ('New Loan', 'Loan'), # DB='New Loan', shown='Loan' ('Advance Payment', 'Advance'), # DB='Advance Payment', shown='Advance' ('Advance Repayment', 'Advance Repaid'), # DB='Advance Repayment', shown='Advance Repaid' ] ``` Django's `makemigrations` will detect the changed `choices` metadata and generate a one-op `AlterField` migration. This is a **no-op at the database level** — the column type and data are untouched — but Django requires the migration to keep its model state in sync with what it thinks is on disk. ### Template switches Wherever a template currently renders the type as VISIBLE text, swap to the display method: - `{{ adj.type }}` → `{{ adj.get_type_display }}` - `{% if adj.type == 'New Loan' %}...{% endif %}` — these stay on the raw type (DB value), because they're CONTROL FLOW, not display - `{{ choice.0 }}` / `{{ choice.1 }}` in dropdown iterations — already render the display value by Django convention (the `