import type { ActivitySummary } from "@/types/form"; /** * CiviCRM activity stage values → Framework Stage rank (1..5). "Inquiry" * predates Stage 1 and is conventionally rank 0 (pre-engagement); callers * that render stage lanes 1..5 should treat 0 as "no rank in scope". */ export const STAGE_RANK: Record = { Inquiry: 0, Organizing: 1, Feasibility: 2, "Business feasibility": 3, "Store Implementation": 4, "Stabilize newly opened co-op": 5, }; /** * Build a resolver that returns the co-op's Framework Stage rank at a given * point in time, derived from the activity stream. Only activities that * carry a non-empty `stage` value are stage transitions; their stage holds * from that activity's date forward until the next transition. * * Returns `null` when the date precedes any known transition (i.e. we have * no evidence of which stage the co-op was in at that point) — callers can * fall back to a field-section default in that case. */ export function buildStageRankAtDate( activities: ActivitySummary[] ): (isoDate: string) => number | null { const transitions = activities .filter( (a): a is ActivitySummary & { stage: string } => typeof a.stage === "string" && a.stage.length > 0 ) .map((a) => ({ t: new Date(a.date).getTime(), stage: a.stage })) .filter((tr) => Number.isFinite(tr.t)) .sort((a, b) => a.t - b.t); return (isoDate: string) => { const t = new Date(isoDate).getTime(); if (!Number.isFinite(t)) return null; let last: number | null = null; for (const tr of transitions) { if (tr.t > t) break; const r = STAGE_RANK[tr.stage]; if (typeof r === "number") last = r; } return last; }; }