Mobile foundation (Phase 1) + harden opportunity stage validation

Phase 1 mobile foundation (additive, no desktop change): :root mobile vars, a
4-tab bottom nav bar + mobile account/logout popover wired into App, a
bottom-sheet CSS primitive, and .mobile-only/.desktop-only utilities -- all
display:none >=768px. The <BottomSheet> React component + useIsMobile() + the
per-surface 15px type bump are deferred to Phase 2 (first use); light theme to
Phase 6.

Review hardening (fresh-eyes pass on the Phase 0+1 diff): validate stage in
handle_create_opportunity + handle_update_opportunity against PIPELINE_STAGES --
the narrower 4-stage enum makes a stale-client write of a legacy value invisible
to the report ORDER BY CASEs and unsettable from the UI. Use the canonical
pipelineStageLabel in the opportunity detail select; document the intentional
graveyard omission in the existing_investor / staleness helpers.

Tests: stage-validation regression in test_grid_pipeline_link.py + empty
source_row_id guard in test_pipeline_stages_v2.py; 36/36 green, render-smoke green.
This commit is contained in:
Keysat
2026-06-19 13:15:53 -05:00
parent e46dd36517
commit 634fc4260f
6 changed files with 221 additions and 15 deletions
+15 -6
View File
@@ -323,15 +323,24 @@ migration into each surface's build, behind one shared foundation step. No upfro
staleness-colored recency column + the seeded "Stale" saved view — the data is injected and
test-locked now, so Phase 3 is pure frontend. W1b nudge specialization is a separate fast-follow.
**Deploy:** needs an s9pk build + install (**authorize first**).
- **Phase 1 — Shared mobile foundation (the only do-once part of the migration).** Extend `:root` with
the missing tokens (semantic colors + the `mobile` token group + a `[data-theme="light"]` block); add
CSS for the bottom-tab-bar, the bottom-sheet primitive, `env(safe-area-inset-bottom)`, and the 13→15px
type bump; build the viewport-gated shell in `App` (bottom 4-tab bar <768px, hide sidebar, top-bar
account/logout control). Touches ~2 inline styles.
- **Phase 1 — Shared mobile foundation — BUILT 2026-06-19 (deploy pending).** Shipped: `:root` mobile
vars (`--mobile-tab-bar-h`/`--mobile-touch-target`/`--mobile-input-h`/`--mobile-sheet-radius`/screen-pad +
fonts + `--text-subtle`/`--border-strong`); CSS for the safe-area-aware **`.bottom-tab-bar`**, the
**`.bottom-sheet`/`.sheet-scrim`/`.sheet-handle`** primitive (styling), and `.mobile-only`/`.desktop-only`
utilities — all `display:none` on desktop so **zero desktop change**; the **4-tab bottom bar**
(Grid·Pipeline·Reminders·Contacts → `setPage`) + a **mobile account/logout popover** wired into `App`
(sidebar already CSS-hidden <768px). Render-smoke green. **Deliberately deferred:** (a) the
**`<BottomSheet>` React component + `useIsMobile()` hook** → Phase 2, designed against their first real
consumer (no dead code); (b) the **13→15px type bump is per-surface**, not a global body rule — `body`
has no base font-size, so it lands as each surface is re-authored (Phases 25); (c) the
`[data-theme="light"]` block → Phase 6 (dead without the toggle). Browser-interaction (the bar on a real
phone) untested, like view-reorder.
- **Phase 2 — Contacts (pattern-validator spike, BEFORE the Grid).** ~17 inline styles; read-only AZ
list + segmented tabs + search → full-screen read-only detail. Proves the list→detail→sheet pattern
and the per-surface migration mechanics on the lowest-risk surface before the crux. *(Reorders the
earlier "Grid first" draft — de-risk the pattern cheaply, then attack the Grid.)*
earlier "Grid first" draft — de-risk the pattern cheaply, then attack the Grid.)* Also lands the
**`<BottomSheet>` React component + `useIsMobile()` hook** (deferred from Phase 1, first consumed here,
built against the Phase 1 `.bottom-sheet` CSS) and this surface's **15px type bump**.
- **Phase 3 — Fundraising Grid (the crux).** ~70 inline styles → classes. Card list + bottom-sheet view
picker + search; full-screen detail with per-field bottom-sheet edits (name, contact pills, stage,
reminder, log note) + the `+`-create flow with client-side dedup typeahead. **Writes per `BRIEF.md`