Mobile Phase 3a: read + write-supported Fundraising Grid surface
Adds the mobile-first Fundraising Grid (<768px): a lean MobileFundraisingGrid that reads /api/fundraising/state once and renders an investor card list over the active view (name, committed $, pipeline-stage chip, staleness-colored recency, Existing-Investor accent, Priority corner; graveyard muted) with a bottom-sheet view picker and search. Tap a card -> full-screen detail with read-only commitments/contacts/notes plus edit sheets: log a note, pipeline stage, set a reminder, and a "+ New" investor create flow with client-side dedup typeahead. All writes go through the targeted one-row endpoints (log-communication, pipeline link, opportunities stage PATCH, reminders) — NEVER the whole-grid PUT, which would race the multi-user grid (BRIEF §3a). FundraisingGridPage is now a useIsMobile() wrapper over the renamed-but-untouched desktop grid and the new mobile one (rules-of-hooks-safe; desktop unchanged). Backend: inject a read-only opportunity_id into grid rows (opportunity_id_by_source_row; added to both strip points) so the mobile detail can PATCH a linked opp's stage directly. Earliest-opp-wins ordering keeps it consistent with pipeline_stage and the link's canonical pick. Editing an existing investor's name + contact pills stays read-only here (deferred to P3b — needs a narrow per-row PATCH + pill editor). Tests: test_grid_pipeline_link extended (opportunity_id inject/strip/round-trip); 36/36 backend green, render-smoke green.
This commit is contained in:
+25
-7
@@ -352,13 +352,31 @@ migration into each surface's build, behind one shared foundation step. No upfro
|
||||
Verified: render-smoke green + a throwaway jsdom interaction harness (mounted the real app at 375px,
|
||||
stubbed `/api/contacts` — list/grouping/sort-sheet/detail/back all asserted, 14/14). **No browser/real-phone
|
||||
check yet** (same deferral as Phase 1 + view-reorder). **Deploy:** folds into the next s9pk build.
|
||||
- **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`
|
||||
§3a "Backend reality":** single-investor edits → `POST /api/fundraising/log-communication` (one-row,
|
||||
no version race; can create investor+contact); stage → `POST /api/fundraising/pipeline/link` (needs ≥1
|
||||
contact) then `PATCH /api/opportunities/{id}/stage`; commitments/amounts read-only; **never whole-grid
|
||||
`PUT /state`**. Renders Phase 0's stage chip + Existing-Investor star + staleness ramp.
|
||||
- **Phase 3 — Fundraising Grid (the crux). P3a BUILT 2026-06-19 (deploy pending); P3b (name/pill edit) deferred.**
|
||||
Split confirmed with Grant 2026-06-19: P3a ships the readable + already-write-supported surface now;
|
||||
editing an existing investor's **name + contact pills** is **P3b** (needs a new narrow per-row PATCH +
|
||||
a pill-editor UI — `log-communication` can't rename/edit pills, and the whole-grid PUT is forbidden on
|
||||
mobile).
|
||||
- **P3a (built):** lean **`MobileFundraisingGrid`** (separate component — the desktop grid's debounced
|
||||
whole-grid-PUT autosave would race on every mobile edit, so it's NOT reused; `FundraisingGridPage` is
|
||||
now a `useIsMobile()` wrapper → `Desktop`/`Mobile`, desktop untouched). Card list over the **active
|
||||
view** (ported the desktop view-filter predicate — graveyard/follow-up/lead flags + columnFilters — to
|
||||
a shared pure helper so it can't drift), tappable view-name → **view-picker sheet**, search, the locked
|
||||
**card model** (name · committed $ via `formatMoneyMobile` · stage chip · staleness-colored recency ·
|
||||
Existing-Investor left-accent · Priority corner; graveyard muted). Full-screen detail (read-only:
|
||||
commitments/funds, contact pills, notes) + **edit sheets**: **log a note** (`log-communication`),
|
||||
**pipeline stage** (linked → `PATCH /api/opportunities/{id}/stage` via the new injected `opportunity_id`;
|
||||
unlinked → `pipeline/link` then it; + remove-from-pipeline), **set a reminder** (`POST /api/reminders`),
|
||||
and **`+ New` investor** (`log-communication` + `create_investor_if_missing`, client-side dedup
|
||||
typeahead). **Never whole-grid `PUT /state`.** Backend: one small hook — read-only **`opportunity_id`**
|
||||
injected into grid rows (`opportunity_id_by_source_row`, added to both strip points), so the detail can
|
||||
PATCH the linked opp directly. Tests: `test_grid_pipeline_link` extended (opp_id inject/strip/round-trip),
|
||||
36/36 green; render-smoke green; a throwaway stateful jsdom harness drove the real surface at 375px
|
||||
(view filter, picker, detail, stage-PATCH, log-note, reminder, create+dedup — 18/18). **No real-phone
|
||||
check yet** (same deferral as P1/P2). **Deploy:** folds into the next s9pk.
|
||||
- **P3b (deferred):** `POST /api/fundraising/update-row` (version-safe single-row name/contacts mutation,
|
||||
+test) + the bottom-sheet **pill editor** (add/edit/remove pills, client-side dedup). Then name + pills
|
||||
become editable on an existing investor, completing BRIEF §3a's editable set.
|
||||
- **Phase 4 — Pipeline.** ~7 inline styles. Swipe-between-stages (snap-scroll + segmented control +
|
||||
dots), per-card stage move sharing the Grid detail's opportunities endpoints.
|
||||
- **Phase 5 — Reminders.** ~18 inline styles. Urgency-grouped list, swipe complete/snooze, add/edit
|
||||
|
||||
Reference in New Issue
Block a user