Three quick fixes off first-deploy testing:
1. /api/staff/file 404'd for valid files. Refactor fileBelongsToOrg
to SELECT the org's and activities' file columns and JS-compare
instead of WHERE ... OR with custom field refs (APIv4 fragility
around nested OR + dotted custom fields). Same ownership probe,
same shape /api/staff/report itself uses to read file values.
2. Lightbox ballooned to full report height because the staff iframe
auto-grows to fit content (often 3000+ px). Cap to a fixed
640px x min(92vw, 900px) box so it stays a reasonable preview
regardless of iframe document size.
3. Production frame-ancestors blocked crm.fci.coop from iframing
survey.fci.coop -- the CSP only included the dev Civi origin
derived from CIVI_BASE_URL. Add CIVI_FRAME_ALLOWED_ORIGINS
(comma-separated) so one app deploy can be embedded by both
dev and prod Civi. Falls back to CIVI_BASE_URL for single-Civi
compatibility.
PRODUCTION_CUTOVER.md updated inline and in the change log.
Adds a /api/staff/file proxy that re-streams Civi attachments with
Content-Disposition: inline so a native <dialog> lightbox can preview
images and PDFs in place. Office docs keep their plain download link
and gain a "View in Google Docs" secondary link (uses the Civi-signed
URL so Google can fetch without our staff key).
Also threads mime through /api/staff/report (Attachment.get mime_type)
so the dispatcher picks the right affordance without relying solely on
filename inference.
Each stage-transition activity now becomes a horizontal range on its lane: start = the activity's date, end = the next activity with a higher stage rank, or extending to today if still in effect. Milestone date-field dots overlay on top of the ranges. Activity dots at each range start carry a tooltip with the activity subject. Added computeStageRanges in lib/stageRank.ts and switched DateTimeline to take activities directly (deriving both the rank resolver and the ranges internally). Wide year-spanning timelines now show their full extent even when no milestone dates have been entered.
Each date-field event is now plotted on the lane corresponding to the Framework Stage the co-op was in on the field's stored date, resolved from the activity stream's stage transitions. Date_Opened is pinned to Stage 5 as the journey anchor. Events that pre-date any known transition fall back to the field's section rank so they still surface somewhere. Extracts the STAGE_RANK map (previously inline in ReportView) into lib/stageRank.ts alongside the new buildStageRankAtDate resolver factory.
DateTimeline now skips section rank 0 (intake) and renders stage lanes
top-to-bottom 5→1, matching how the framework actually numbers stages.
MembershipChart's latest-Actual label was placed at the circle's
x + 6, which overflowed the SVG when the most-recent point landed near
the right padding. Anchor it to xOf(maxT) - 4 with textAnchor="end",
mirroring the Goal label so both endpoint labels stay inside the chart.