8.9 KiB
Adjustments Filter Bar v2 — Design (23 Apr 2026)
Origin
Konrad on the shipped Adjustments tab (commit 672c32c):
"this interface layout is very ugly. And the selection dropdown menus text is a bit large don't you think?"
Follow-up: "the spacing above the Show As section is bad".
Short redesign after the 22-task feature work shipped. Pure polish — no backend changes, no test changes, no new URL params.
Goal
Tighten the visual vocabulary of the Adjustments filter bar. Today the
bar mixes three control shapes (popover pills for Type/Workers/Teams, a
native <select> for Status, and a compound widget for Date with
separate inputs + preset links + mode-toggle button). Five different
control treatments crammed into one sticky row reads as noisy.
Secondary: the popover checkbox list uses Bootstrap's default
.form-check-input ~14–16px font and ~0.2rem row padding, which makes
the 7-type Type popover ~320px tall — larger than it needs to be.
Tertiary: the "Show as: Flat / By Type / By Worker" toggle sits directly below the filter bar with no vertical breathing room; they read as one cramped block.
Who it's for
Admins (is_staff or is_superuser). Same audience as the whole
Adjustments feature.
1. Unified pill vocabulary — 5 pills, identical treatment
Every filter becomes a .filter-pill.adj-filter-pill:
- Same height (~32px)
- Same border, same hover/focus, same chevron
- Same label typography
- Same popover anchoring (reuses Feature-1
.filter-popover)
| Filter | Current | After |
|---|---|---|
| Type | Pill popover with checkboxes | (unchanged — already correct) |
| Workers | Pill popover with checkboxes | (unchanged) |
| Teams | Pill popover with checkboxes | (unchanged) |
| Status | Native <select> + <label> above |
Pill popover with 3 radios (All / Unpaid / Paid) |
| Date | <label> + 2 <input>s + preset links + ... toggle, all inline |
Pill popover with mode toggle + picker(s) + preset buttons inside |
Pill label formatting
At-a-glance the pill label reveals what's active:
| State | Label |
|---|---|
| Empty | Type, Workers, Teams, Status, Date |
| 1 selected | Type: Bonus, Status: Unpaid, Date: 24 Apr 2026 |
| 2+ selected | Type with a (2) count badge after the label |
| Date range | Date: 20 Apr – 26 Apr |
2. Popover density — smaller font, tighter rows
| Property | Current | After |
|---|---|---|
| Checkbox-list font size | ~14px (Bootstrap default) | 0.8rem (~12.8px) |
| Checkbox row padding | 0.2rem 0.25rem |
0.15rem 0.25rem |
| Checkbox visual size | 1em | 0.9em |
| Popover footer buttons | Bootstrap btn-sm defaults |
font-size: 0.75rem; padding: 0.25rem 0.5rem |
| Checkbox-list max-height | 280px |
260px |
| Popover max width | 420px |
360px |
Net effect on the Type popover (7 rows): drops from ~320px tall to ~240px.
3. Status pill — popover with 3 radios
[Status ▾] Status popover:
┌──────────────────┐
│ ○ All │
│ ○ Unpaid │
│ ○ Paid │
├──────────────────┤
│ [Cancel] [OK] │
└──────────────────┘
- Hidden
<input type="hidden" name="adj_status" value="...">holds committed state, rewritten by popover OK - Pill label:
Status/Status: Unpaid/Status: Paid - Cancel reverts radio to committed state; Esc / click-outside close without committing (matches existing popover pattern)
4. Date pill — popover with mode + picker(s) + presets
[Date ▾] Date popover:
┌───────────────────────┐
│ [Single] Range Custom │ ← mode toggle
│ │
│ Date: [ 24/04/2026 ] │ ← picker(s)
│ │
│ Today · Week · Month │ ← presets
├───────────────────────┤
│ [Cancel] [OK] │
└───────────────────────┘
- Three modes:
- Single (default): one picker; submit sends
adj_date_from = adj_date_to = picked - Range: two pickers (From / To); submit sends both
- Custom — same as range but with a "(custom date, not month-aligned)" hint (Simplification possibility: drop Custom; Range covers it. Leaning YAGNI. Will decide at impl time — if no clean semantic gain, Custom goes.)
- Single (default): one picker; submit sends
- Presets (Today / This week / This month / Clear) work the same as the current inline presets, just live inside the popover now
- Pill label formatting:
- Empty:
Date - Single:
Date: 24 Apr 2026 - Range:
Date: 20 Apr – 26 Apr 2026
- Empty:
- Hidden form inputs
adj_date_from+adj_date_tohold committed state
5. Filter bar layout
┌────────────────────────────────────────────────────────────────────┐
│ [Type ▾] [Workers ▾] [Teams ▾] [Status ▾] [Date ▾] [Apply] C │
└────────────────────────────────────────────────────────────────────┘
↑ 1rem spacing
Show as: [Flat] [By Type] [By Worker]
- Remove all
<label>Status</label>/<label>Date</label>elements above native widgets — the pill text carries that job now .adjustments-filter-barkeepsdisplay: flex; flex-wrap: wrap; gap: 0.75rem; align-items: center;(wasendfor the native widget baseline;centernow that all children are the same pill height)- Apply + Clear sit at the right end with
margin-left: autoon their wrapper - The "Show as" row gains
margin-top: 1remso there's clear separation from the filter bar - The Show-as toggle buttons get
padding: 0.3rem 0.75rem; font-size: 0.8remto match pill height
6. Spacing + density tokens
Add two tokens inside the adjustments block of static/css/custom.css:
/* Adjustments tab — shared density tokens for the filter strip */
--adj-filter-font-size: 0.8rem;
--adj-filter-height: 32px;
Every filter control picks them up so height + text size stay in lockstep.
7. Implementation plan (short — it's template + CSS only)
- Template (
payroll_dashboard.html):- Replace the
<select name="adj_status">+ its<label>with a Status pill-popover (radios) - Replace the 6-line Date widget markup with a Date pill-popover (mode toggle, pickers, presets all inside)
- Keep the existing hidden
adj_status/adj_date_from/adj_date_topattern — just write them from the new popovers' OK handlers
- Replace the
- JS (same
<script>block):- Tiny Status popover wiring (radios → hidden input on OK; label update via pill-label helper)
- Move Date presets + mode toggle logic INSIDE the Date popover handler
- Reuse
commitCheckboxespattern for the Status/Date hidden-input rewrite
- CSS (
custom.css):- Density tokens (2 CSS vars)
- Font-size drops on
.adj-checkbox-list+ popover footer buttons - Shrink
.adj-cb-rowpadding + checkbox size .adj-groupby-togglegetsmargin-top: 1rem.adj-groupby-toggle .btnmatches pill size
8. Out of scope
- Backend changes — none. Query param contract stays identical
(
?status=adjustments&type=X&worker=Y&team=Z&adj_status=paid&adj_date_from=2026-04-20&adj_date_to=2026-04-26). - Test changes — none. All 65 tests exercise the URL contract, not the DOM shape.
- AJAX partial rendering — still full page reload on Apply. Matches the rest of the app.
- Saved filter presets — URLs are bookmarkable already; YAGNI.
- "Custom date" mode — if it's semantically identical to "Range", drop it at impl time.
9. Risks + rollback
Template-only; rollback = revert the commit. No data, schema, migration, or URL-contract impact.
Biggest risk: a copy-paste error in the Date popover's mode-toggle JS
could leave someone with a submit that posts empty dates. Mitigation:
the existing "apply single mode fallback" behaviour (blank adj_date_to
→ server treats as no-filter) already makes this safe.
10. Next step
Implement directly (no writing-plans needed — single-commit template +
CSS polish, no new business logic, no new tests). Ship alongside the
Adjustments feature in the same ai-dev branch.