Files
brain-of-reese/.agents/phases/complete/171_admin_settings_layout/00_phase.md
T
ducoterra e9ae6e2ba3
Build and Push Containers / build-and-push-app (push) Successful in 1m34s
Build and Push Containers / build-and-push-db (push) Successful in 14s
phase: 171_admin_settings_layout
Expected state: only `00_phase.md` remains in `todo/` (the harness moves it on success; I must not touch it). All verification is complete.

---

**Phase 171 final verification pass — report**

- **Verified (all 4 tasks complete, CSS-only diff confirmed byte-exact to contract):** `#settings-content` flex column + `gap: 1.25rem` (tasks 01); `.settings-group-row th` band `--brand-soft` + 700 + `--line` top hairline (02); `#settings-save:disabled` `cursor: default` (03); new phase-dedicated Playwright suite (04). Mobile media queries and `settings.js`/`app/` byte-untouched, as pinned.
- **Gates:** `uv run pytest --cov=app --cov-report=term-missing` → **4069 passed, app/ 99%** (>90%); `uv run ruff check .` → clean; `uv run pyright` → **0 errors, 0 warnings**.
- **E2E (in isolation):** `test_admin_settings_layout.py` → **5 passed**; `test_admin_settings_panel.py` (phase 168) → **12 passed**; `test_wide_desktop_column.py` → **3 passed**.
- **Completion criteria:** search/table no-intersection + gapped action row at 1440/375 px ✅ · distinct group bands (bg ≠ card, weight 700, ≥1px top hairline) ✅ · disabled Save `cursor: default` with §7.4 dim + aria-busy lifecycle intact ✅ · full suite + coverage + lint ✅ · all three E2E suites isolated ✅ · atomic commit + dir move = **harness's job** (task files already in `complete/`; per protocol I left every change uncommitted in the working tree).
- **Documentation:** README.md (all 1487 lines) and AGENTS.md (all 80 lines) fully reviewed — every config default cross-checked vs `app/config.py`, all quick-reference commands verified against existing files, AGENTS.md rules/contracts verified vs code — **verified consistent, no changes needed** (phase 171 is CSS-only: no setup/command/env/API impact).
- **Notable:** one phase-168 test failed on the first isolated run solely because an unrelated `undercooked` dev server held port 8124 (the suite's `DISABLED_PORT`); re-ran with the suite's built-in `E2E_APP_PORT=8300` override — no code change, foreign process untouched. Post-run DB state clean (`app_settings` 0, `ui_settings` 0, e2e tokens 0).
- **Next pending phase:** none — phase 171 was the last `todo/` phase.
2026-10-10 04:45:34 -04:00

8.2 KiB
Raw Blame History

Phase 171 — Admin settings page: row rhythm, distinct group bands, a properly disabled Save

Source: TODO.md L4 — "In the admin settings page: The search settings text box overlaps settings table. The settings category headers are not differentiated from the settings themselves, making them confusing at a glance. The save settings and clear all overrides are smushed right up against the settings table. The save settings shows a loading cursor when moused over rather than just being disabled." Story: n/a (TODO-derived, owner roadmap confirmation 2026-10-10) Context: frontend/index.html — #view-settings (L1499), #settings-content (L1535, a plain block — its children #settings-disabled, #settings-status, #settings-error, .settings-search (L1562), the .table-wrap card (L1576, #settings-tbody created by settings.js), and .settings-actions (L1607: #settings-save disabled by default + #settings-clear-all + #settings-dirty) stack with zero vertical rhythm — the root of the "overlap" and the "smushed" complaints), frontend/assets/settings.js (637 lines — the group-row render L319–335: tr.settings-group-row + th[colspan=4][scope=colgroup] with the group name, the client-side search filter, the §7.4 Save lifecycle), frontend/assets/styles.css — .settings-shell (L4660–4665: display:flex; flex-direction:column; gap: 1.25rem;), .settings-group-row th (L4684–4690: background: var(--surface) — the same fill as the .table-wrap card (L2825–2831) → the band is effectively invisible, the "not differentiated" complaint), the sticky-th tint rule .table-wrap table thead th { background: var(--brand-soft); } (L2841–2848, the solid-tint precedent), .settings-search (L4780–4786), .settings-actions (L4804–4809), #settings-save (L4810–4824, incl. :disabled { opacity: 0.6; cursor: wait; } L4824 — the loading-cursor complaint), .settings-reset:disabled { opacity: 0.5; cursor: default; } (L4775 — the house idiom), the mobile media queries (L6396–6400: .settings-search / .settings-actions flex-direction: column, #settings-save width: 100%), tests/unit/test_settings_frontend_view.py (the phase-168 static pins, incl. the L561 media-query pin), tests/e2e/test_admin_settings_panel.py (the phase-168 suite — the regression set).

Objective

The four layout complaints on the Settings page — the search box overlapping the table card, the group headers indistinguishable from the rows, the Save / Clear row smushed against the table, and the disabled Save showing a loading cursor — are fixed with CSS only (plus the pin updates): one rhythm rule, one band rule, one cursor value. The phase-168 contract (the gate, the catalogue, the §7.4 Save lifecycle, the API, the bounded scroll + sticky header) stays behavior-identical — no app/ code, no JS logic change.

Design (shared by all tasks — the executor reads this, not the chat)

  • D1 — the rhythm (task 01). #settings-content becomes display: flex; flex-direction: column; gap: 1.25rem; — the same token .settings-shell already uses (L4664). The children (the disabled banner, the status line, the error line, the search row, the .table-wrap card, the .settings-actions row) then stack with one uniform gap: the search box clears the table card (the "overlap" dies) and the Save / Clear row is separated from the table (the "smushed" dies) in the same change. The min-height rules on #settings-status / #settings-error (L4849–4862) already reserve their lines — the gap is additive and uniform, no per-element margins are introduced.
  • D2 — the band (task 02). .settings-group-row th gets a band that is distinct from the row cells at a glance: background: var(--brand-soft) — the same solid tint the sticky thead rule already uses (L2847; B3: no new palette variable), font-weight: 700 (the 0.82rem / uppercase / letter-spacing stay), and border-top: 1px solid var(--line) so a band never merges with the card's top edge or a previous band. The row cells stay on the card's --surface. The group text stays --ink on --brand-soft — the pairing the sticky-th rule already documents. Static, text-only, no animation (nothing for prefers-reduced-motion to still).
  • D3 — the cursor (task 03). #settings-save:disabled → cursor: default — the .settings-reset:disabled idiom (L4775); "just disabled", per the owner. The §7.4 in-flight state is the dimmed :disabled (opacity: 0.6 stays) + aria-busy — the cursor: wait was never part of the contract; it was the "loading cursor on hover" the owner saw.
  • NOT touched: settings.js (the render / filter / save / reset / clear-all logic), app/api/admin_settings.py, the sign-in gate, the nav reveal, the .table-wrap bounded-scroll + sticky-th contract (L2825–2848), the #settings-status / #settings-error regions, the restart required flag, the mobile media queries (L6396–6400 — they compose with the new gap and stay byte-identical), app/ server code.

Dependencies

  • 168_admin_settings_panel (in flight — the code + the E2E suite are committed; only its directory move + final commit are pending) — the Settings view this phase polishes. Note: the abandoned 169 settings status card never landed — there is nothing to coordinate with.

Tasks

  1. 01_content_vertical_rhythm.md — #settings-content becomes a flex column with the shell's 1.25rem gap (fixes the search/table overlap and the smushed action row); the pin added.
  2. 02_group_band_distinction.md — .settings-group-row th gets the --brand-soft band + 700 weight + top hairline, so groups read at a glance.
  3. 03_disabled_save_cursor.md — #settings-save:disabled → cursor: default (the house idiom), killing the loading cursor.
  4. 04_e2e_settings_layout.md — tests/e2e/test_admin_settings_layout.py (the phase's dedicated Playwright suite, AGENTS rule 4), the regression set, the full gates, the atomic commit.

Testing & Quality

  • Unit: tests/unit/test_settings_frontend_view.py (the phase-168 static pin set, extended in tasks 01/02/03): the #settings-content flex-column + gap rule, the band's --brand-soft background + weight + top border, #settings-save:disabled cursor: default (and cursor: wait absent from the settings rules), plus the existing pins (the no-CDN pin, the router.js mapping, the media-query pin L561) staying green.
  • E2E (AGENTS rule 4, task 04): tests/e2e/test_admin_settings_layout.py, run in isolation — the four layout contracts, proven in a browser.
  • Coverage: >90% on app/ (this phase changes no app/ code — the gate must stay green, not drop).
  • Lint/types: uv run ruff check . && uv run pyright.

Completion Criteria

  • The search box and the table card do not intersect; the Save / Clear row is visibly separated from the table (the E2E bounding-box pins, at 1440 px and 375 px).
  • A settings group band is visually distinct from its rows (computed background ≠ the row cells' background; weight 700) and no band merges with the card edge.
  • A disabled Save settings shows cursor: default (not wait); the §7.4 dimmed-aria-busy in-flight state is intact.
  • uv run pytest green, app/ coverage >90% (uv run pytest --cov=app --cov-report=term-missing), ruff + pyright clean.
  • uv run pytest tests/e2e/test_admin_settings_layout.py -v --no-cov green in isolation; test_admin_settings_panel.py (phase 168) + test_wide_desktop_column.py green in isolation.
  • One atomic --no-gpg-sign Conventional Commit; the phase dir moves to .agents/phases/complete/.

Locked decisions (owner-confirmed 2026-10-10, roadmap confirmation)

  • A3 — the band tint is --brand-soft (the solid tint the sticky thead already uses — B3: no new palette variable) + a --line top hairline + weight 700; static, no animation.
  • A4 — the rhythm reuses the shell's existing gap: 1.25rem — no new spacing value; the existing mobile media queries (L6396–6400) keep working unchanged.

Commit

git add frontend/ tests/ .agents/phases/ && git commit --no-gpg-sign -m "fix(settings): settings page layout — row rhythm, distinct group bands, a properly disabled Save"