docs(adjustments): design for filter-bar v2 (pill unification + density)
This commit is contained in:
parent
672c32cfb6
commit
620f433d06
199
docs/plans/2026-04-23-adjustments-filter-bar-v2-design.md
Normal file
199
docs/plans/2026-04-23-adjustments-filter-bar-v2-design.md
Normal 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` ~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.)*
|
||||
- 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.
|
||||
Loading…
x
Reference in New Issue
Block a user