From 9aba9b8fb8d14fbc7c2e6dba4f04846c3d145900 Mon Sep 17 00:00:00 2001 From: Konrad du Plessis Date: Fri, 24 Apr 2026 09:32:23 +0200 Subject: [PATCH] docs(ux): design for UX Polish Pass MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Four UX asks bundled in one pass: 1+2. Display-only rename of adjustment types: 'New Loan' (DB) → 'Loan' (UI), 'Advance Payment' → 'Advance', 'Advance Repayment' → 'Advance Repaid'. DB values preserved forever — zero data migration, zero formula / constant / CSS / test changes. 3. Unify badge colours across all payroll tabs using the existing .badge-type-* semantic palette. Recolour Pending "With loans" flag to match the Loan type colour. 4. Fix CSS bug in .adj-group-meta (margin-left:auto doesn't work in a — make the td a flex container). Plus: new docs/design-tokens.md as the canonical colour reference, and a crucial CLAUDE.md section documenting the UI-vs-DB naming drift so future Claude sessions don't chase ghosts when writing formulas / filters / ORM queries that reference the display label instead of the DB value. Co-Authored-By: Claude Opus 4.7 (1M context) --- docs/plans/2026-04-24-ux-polish-design.md | 356 ++++++++++++++++++++++ 1 file changed, 356 insertions(+) create mode 100644 docs/plans/2026-04-24-ux-polish-design.md diff --git a/docs/plans/2026-04-24-ux-polish-design.md b/docs/plans/2026-04-24-ux-polish-design.md new file mode 100644 index 0000000..1869399 --- /dev/null +++ b/docs/plans/2026-04-24-ux-polish-design.md @@ -0,0 +1,356 @@ +# 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 `