docs(adjustments): design for filter-bar v2 (pill unification + density)

This commit is contained in:
Konrad du Plessis 2026-04-23 21:54:13 +02:00
parent 672c32cfb6
commit 620f433d06

View File

@ -0,0 +1,199 @@
# 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`:
```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.