Mobile Phases 4–5: Pipeline (swipe-between-stages) + Reminders

Completes the four mobile-first surfaces. Both phases follow the established
useIsMobile() wrapper → Mobile*/Desktop* pattern; desktop is untouched (only
renamed Desktop*). No backend change.

P4 Pipeline (MobilePipeline): CSS scroll-snap swipe between the four stages +
count-forward segmented control + page dots; per-card ‹/› stage move and
tap → full-screen opp detail with a stage-picker sheet. Opp-centric — shares
PATCH /api/opportunities/{id}/stage with the desktop board and the Grid's
stage edit; view + advance-stage only (removal stays on the Grid/desktop board).

P5 Reminders (MobileReminders): urgency-grouped list over /api/reminders
(Overdue/Due soon/Later/Done/Cancelled) with an Active/Done/All filter; each
row is a pointer-drag swipe (left = mark done, right = snooze +7d, keeping
status open and pushing due_date, per the desktop rationale); tap → create/edit
BottomSheet (investor is create-only, matching the backend PATCH field set).
formatDueShort/reminderDueDelta fix the desktop formatter mis-rendering future
due dates.

Verified: render-smoke + throwaway jsdom 375px interaction harnesses (12/12
each); reviewer passes applied — notably P5 pointercancel no longer fires a
spurious mark-done. Deploy-pending (no s9pk built); not yet tested on a phone.
This commit is contained in:
Keysat
2026-06-19 15:44:49 -05:00
parent 95beb7bb19
commit ee9db6425a
3 changed files with 655 additions and 10 deletions
+35 -4
View File
@@ -377,10 +377,41 @@ migration into each surface's build, behind one shared foundation step. No upfro
- **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
sheets on `/api/reminders`.
- **Phase 4 — Pipeline — BUILT 2026-06-19 (deploy pending).** Lean **`MobilePipeline`** (separate
component; `PipelinePage` is now a `useIsMobile()` wrapper → `Desktop`/`Mobile`, desktop kanban
untouched, just renamed `DesktopPipelinePage`). **Swipe-between-stages:** a count-forward segmented
stage control (`.pipeline-seg`) + a horizontal **CSS scroll-snap** container of four full-width stage
pages (`.pipeline-swipe`/`.pipeline-stage-page`) + page dots; tapping a segment scrolls to its page,
scrolling syncs the active segment/dots. Each card shows opp name · contact·org · expected $, with
per-card **/ stage move** (`PATCH /api/opportunities/{id}/stage`, disabled at the lead/commitment
boundaries) — the kanban "advance" without opening the detail. Tap a card → full-screen `.fs-detail`
(read-only `OpportunityDetailPanel`-equivalent fields via `MobileDetailRow` + a `StageChip`) with a
**stage-picker `BottomSheet`**. **Opp-centric** (operates on the same `opportunities` rows + stage
endpoint as the desktop board and the Grid detail's stage edit), amounts read-only; **no Existing-Investor
star** (opps carry `fundraising_investor_id` but not `total_invested`). Removal/deletion stays on the
desktop board + the Grid detail's "remove from pipeline" — the Pipeline tab is **view + advance-stage
only**. A `reviewer` pass was applied (reset the detail's stage-sheet open-state on back; `moveStage`
awaits the PATCH). Verified: render-smoke green + a throwaway jsdom 375px harness drove the real surface
(seg counts, stage pages, segment/dot sync, / move re-bucketing, detail + stage-sheet PATCH, back — 12/12).
No real-phone check yet (same deferral as P1P3a). Reuses the P2/P3a primitives directly; **no backend
change.** **Deploy:** folds into the next s9pk.
- **Phase 5 — Reminders — BUILT 2026-06-19 (deploy pending).** Lean **`MobileReminders`**
(`RemindersPage` is now a `useIsMobile()` wrapper → `Desktop`/`Mobile`; the desktop page renamed
`DesktopRemindersPage`, otherwise untouched). **Urgency-grouped list** over `/api/reminders`
(Overdue → Due soon → Later → Done → Cancelled buckets via `reminderBucket`; group headers carry the
overdue-red/due-soon-amber tint) with a compact **Active/Done/All** segmented filter + **`+ New`**.
Each row is a **`ReminderRow`** pointer-drag swipe (own per-row drag state): **swipe-left → mark done**,
**swipe-right → snooze +7d** (threshold 70px; snooze keeps status `open` and pushes `due_date`, mirroring
the desktop's "no wake mechanism" rationale), a **tap → create/edit `BottomSheet`** (title · due date ·
investor *(create-only free-text label — PATCH can't change investor, matching the backend)* · assignee
*(if `/api/users` is readable)* · details · status *(edit-only)* · Delete). Vertical-dominant drags release
to list scroll; non-swipeable (done/cancelled) rows stay tap-to-edit. Added `formatDueShort`/`reminderDueDelta`
(local-midnight delta — the desktop `formatDate` mis-renders FUTURE dates). A `reviewer` pass was applied
(**`pointercancel` no longer fires a spurious mark-done** — the key fix; stray drag on a non-swipeable row
recovers as a tap; cancelled gets its own bucket header). **No backend change.** Verified: render-smoke
green + a throwaway jsdom 375px harness (grouping/counts, swipe done + snooze PATCH, tap→edit prefilled,
create POST, Done-filter reload — 12/12). No real-phone check yet (same deferral as P1P4). **Deploy:**
folds into the next s9pk.
- **Phase 6 — Light theme + toggle (adopted as a planned feature, 2026-06-19).** The inline-hex→`var()`
axis (183 literals) + ship the light palette (`tokens.tokens.json` `color.light`) behind a
`[data-theme]` switch + a top-bar toggle; dark stays the default. Mechanical; co-lands after the