Mobile Phase 8d: sort controls across Grid, Pipeline, Contacts

Add a shared SortPill + SortSheet (label+hint option rows) and per-surface sort
tables:
- Grid: Name / Pipeline stage / Committed / Last contact / Priority, applied in
  the displayed memo (name is the tiebreak; staleness ranks longest-since-contact
  first, no-activity treated as most stale; committed uses the fund rollup).
- Pipeline: Name / Amount / Last activity / Priority, sorted within each stage.
  "Last activity" uses opp.updated_at as a recency proxy until the Pipeline card
  wires true last-contact recency (8f).
- Contacts: drop the investor/prospect type tabs (the prospect type is unused);
  add a Priority sort alongside Name A-Z/Z-A and Last contact.

contact_grid_signals() now also surfaces the linked investor's priority flag,
injected on both contact read paths (same derive-on-read contract as committed /
pipeline_stage), powering the Contacts Priority sort. Extended
test_contacts_grid_signals.py covers it; 39/39 backend green.
This commit is contained in:
Keysat
2026-06-19 22:06:14 -05:00
parent 93ac0c240f
commit 42c169559c
4 changed files with 165 additions and 56 deletions
+4 -3
View File
@@ -107,13 +107,14 @@ Subsystem rules live in `docs/guides/` and lazy-load in Claude Code via `.claude
## Current state
_**Box live at v0.1.0:94**; `main` ahead by mobile Phases 07 + P3b + drag-reorder + **8a + 8b + 8c** — **all deploy-pending** (no s9pk built). **The fundraising grid + email capture is the canonical system of record.** Active thread: **mobile-first redesign → Phase 8**, building to `design/phase8-conformance.md` (the 8a8i spec, anchored on each `*.dc.html` DEFAULT `data-props` — NOT the `screenshots/` PNGs). **Plan (Grant, 2026-06-19): finish features first → then Grant device-tests + deploys** (nothing verified on a real phone). History: git log + `start9/0.4/startos/versions/`._
_**Box live at v0.1.0:94**; `main` ahead by mobile Phases 07 + P3b + drag-reorder + **8a + 8b + 8c + 8d** — **all deploy-pending** (no s9pk built). **The fundraising grid + email capture is the canonical system of record.** Active thread: **mobile-first redesign → Phase 8**, building to `design/phase8-conformance.md` (the 8a8i spec, anchored on each `*.dc.html` DEFAULT `data-props` — NOT the `screenshots/` PNGs). **Plan (Grant, 2026-06-19): finish features first → then Grant device-tests + deploys** (nothing verified on a real phone). History: git log + `start9/0.4/startos/versions/`._
- **Mobile redesign — 4 core surfaces built (Grid · Contacts · Pipeline · Reminders), each a rules-of-hooks-safe `useIsMobile()``Mobile*`/`Desktop*` pair (desktop untouched).** Foundation: bottom-tab bar + `:root` mobile vars; 4-stage enum; derived grid signals injected-on-GET/stripped-on-write at both points; mobile writes use **one-row endpoints only** (log-communication, pipeline link/stage, reminders, `update-row`) — never whole-grid PUT.
- **Phase 8a — Grid + Contacts cards re-authored (this session).** Grid card: existing-LP **earmark** corner-triangle (replaces left-border), right-side **PRIORITY pill** (replaces ★), 4-stage chip, zero-commit dim; detail ★→"Existing LP" pill. Contacts card: two-letter avatar initials + existing-LP **ring** + stage pill + recency; disposition badge dropped. Backend: `contact_grid_signals()` injects derived read-only `committed`/`pipeline_stage` on the contacts read path (see Design convention). `DESIGN.md` §4/§8 reconciled.
- **Phase 8b — Contacts + Pipeline detail → drag-dismiss bottom sheets (this session).** Contacts: email-copy pill, Log/Email actions, Organization card (earmark·stage·committed·last-contact·last-note·Open-in-Grid). Pipeline: stat tiles, **inline move-stage list**, notes **timeline** + Log sheet. Both log via `POST /api/communications`; `<BottomSheet stacked>` layers the Log sheet over a detail. Reviewer pass applied: stale-fetch race guard (cancelled-flag effects + reload key), keyed single-contact signals query, dedup test.
- **Phase 8c — Grid-detail notes timeline + top-bar quick-log pencil (this session).** Grid detail (G6): the single `row.notes` blob → a `<NoteTimeline>` fed by a new investor-level read `GET /api/communications?source_row_id=<grid row id>` (backend filter maps row → `fundraising_investors.source_row_id``fundraising_contacts.contact_id` → comms, soft-delete-respecting; cancelled-flag fetch + `commsReload` after a log); note-logging switched to the shared `<LogCommunicationSheet>` (dropped `noteForm` + dead `.fs-note-log`). New `MobileQuickLog` (shell `.mobile-only` top-bar pencil, all 4 tabs): two-step sheet — pick investor (search + recent-first pool) → inline log form → one-row `/api/fundraising/log-communication`. Reviewer pass: `contact_id`/`source_row_id` kept mutually exclusive (`elif`), re-open guard, dropped the rejected ★ from the picker. Guarded by `test_grid_comm_timeline.py`.
- **Phase 8d — sort controls across Grid · Pipeline · Contacts (this session).** Shared `SortPill` + `SortSheet` (label+hint option rows) + per-surface sort tables: Grid (Name/Stage/Committed/Staleness/Priority), Pipeline (Name/Amount/Staleness/Priority, sorted **within each stage**; staleness = `opp.updated_at` proxy until 8f wires real recency), Contacts (**type tabs dropped** per Grant; Name AZ/ZA, Last contact, **Priority**). Backend: `contact_grid_signals()` now also injects the investor's `priority` flag on both contacts read paths (powers the Contacts Priority sort). Reviewer pass: type-annotated the boolean-vs-`'high'` priority comparators, `sortPillLabel` empty-guard. Guarded by extended `test_contacts_grid_signals.py` (priority signal + dedup).
- **Live (deployed):** W2 NL query (v94); W1 reminders (v93); grid Pipeline (v88); Matrix intake + Gmail capture (DWD) + daily digest; Thesis/Architect (dual-approval); outreach — all draft-only.
- **Tests:** **39/39 backend green** (`python3 backend/run_tests.py`; +`test_grid_comm_timeline.py`), `py_compile` clean, render-smoke green; 8c surfaces (quick-log + grid timeline) interaction-verified via a throwaway 375px jsdom harness (deleted after).
- **Next — Phase 8, in order, build to `design/phase8-conformance.md`:** **8d** sort (Grid+Pipeline sort sheet; Contacts = drop type tabs + add Priority sort) → **8e** reminders (due-chip + Overdue/Today/This-week/Later buckets + dots + snooze sheet + investor picker) → **8f** Pipeline card (earmark/Priority/recency + horizontal-scroll stage pills + dots) → **8g** add-investor stage+priority → **8h** loose ends (incl. Grid detail G4/G5/G6 stage-card/reminder-card/timeline; "Open-in-Grid" deep-link-to-investor) → **8i** shell SVG icons + `·Ten31·` wordmark. **Skip Pipeline accordion** (Grant). **Then (after feature-complete):** deploy P0P8 + P3b in one s9pk (**authorize + version-bump first**) and device-test light/dark on a phone.
- **Tests:** **39/39 backend green** (`python3 backend/run_tests.py`; +`test_grid_comm_timeline.py` for the 8c timeline filter, +priority assertions in `test_contacts_grid_signals.py`), `py_compile` clean; 8c+8d surfaces interaction-verified via throwaway 375px jsdom harnesses (deleted after).
- **Next — Phase 8, in order, build to `design/phase8-conformance.md`:** **8e** reminders (due-chip + Overdue/Today/This-week/Later buckets + dots + snooze sheet + investor picker) → **8f** Pipeline card (earmark/Priority/recency + horizontal-scroll stage pills + dots) → **8g** add-investor stage+priority → **8h** loose ends (incl. Grid detail G4/G5/G6 stage-card/reminder-card/timeline; "Open-in-Grid" deep-link-to-investor) → **8i** shell SVG icons + `·Ten31·` wordmark. **Skip Pipeline accordion** (Grant). **Then (after feature-complete):** deploy P0P8 + P3b in one s9pk (**authorize + version-bump first**) and device-test light/dark on a phone.
- **Open / risks:** all mobile work + light theme **built but never deployed or device-tested** (smoke/jsdom only); `MobileDetailRow` now unused-but-retained (legacy-usage sweep); Pipeline detail "Committed" tile shows grid-committed not deal-expected (deal forecast in a footnote); W2 happy-path only; **Claude/Architect path unverified live on the box**; v2.0 reserve-asset spine **not canonical**; doc drift — `crm-overview.md`/`EVALUATION.md` still call `lp_profiles` live.