38686-vm/docs/plans/2026-04-23-adjustments-filter-bar-v2-design.md

8.9 KiB
Raw Blame History

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 ~1416px 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.)
  • 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
  • Hidden form inputs adj_date_from + adj_date_to hold 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-bar keeps display: flex; flex-wrap: wrap; gap: 0.75rem; align-items: center; (was end for the native widget baseline; center now that all children are the same pill height)
  • Apply + Clear sit at the right end with margin-left: auto on their wrapper
  • The "Show as" row gains margin-top: 1rem so there's clear separation from the filter bar
  • The Show-as toggle buttons get padding: 0.3rem 0.75rem; font-size: 0.8rem to 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)

  1. 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_to pattern — just write them from the new popovers' OK handlers
  2. 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 commitCheckboxes pattern for the Status/Date hidden-input rewrite
  3. CSS (custom.css):
    • Density tokens (2 CSS vars)
    • Font-size drops on .adj-checkbox-list + popover footer buttons
    • Shrink .adj-cb-row padding + checkbox size
    • .adj-groupby-toggle gets margin-top: 1rem
    • .adj-groupby-toggle .btn matches 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.