From 04e69ca04c6e2a64842a523bd945c6de8b7f19f5 Mon Sep 17 00:00:00 2001 From: Joel Brock Date: Wed, 13 May 2026 12:09:24 -0700 Subject: [PATCH] Header: show Civi stage label prominently; restore current_stage readonly in Stage 0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - New STAGE_OPTION_GROUP_ID constant (=75) added to config and consumed by both the in-card readonly and the header. Because the field carries optionGroupId, /api/data auto-includes the Stage option group in its parallel fetch (via the existing optionGroupIds derivation). - SubmissionContextHeader: dropped the small inline 'Stage Organizing' line. Replaced with an uppercase 'Current stage' eyebrow next to the progress dots, followed by a display-font 20/24px leaf-toned label underneath. Resolves the stored option value ('Organizing') to its Civi option label ('Stage 1 — Convene & Prepare') via the new resolveStageLabel helper; falls back to the raw value if the option group hasn't loaded. - Stage 0 'Check-in (organizing)' section: re-added a current_stage readonly field at the top. With optionGroupId set, FieldRenderer's readonly branch resolves the value to the Civi label for display. --- components/EngagementForm.tsx | 32 +++++++++++++++++++++++++------- config/form.ts | 23 +++++++++++++++++++---- 2 files changed, 44 insertions(+), 11 deletions(-) diff --git a/components/EngagementForm.tsx b/components/EngagementForm.tsx index 89a461a..ed2e538 100644 --- a/components/EngagementForm.tsx +++ b/components/EngagementForm.tsx @@ -17,6 +17,7 @@ function sectionForField( return undefined; } import { evaluate } from "@/lib/conditional"; +import { STAGE_OPTION_GROUP_ID } from "@/config/form"; import { StageSection } from "./StageSection"; import { loadDraft, saveDraft, clearDraft } from "@/lib/draft"; @@ -283,7 +284,7 @@ export function EngagementForm({ config, cid, cs }: EngagementFormProps) {
@@ -339,11 +340,11 @@ export function EngagementForm({ config, cid, cs }: EngagementFormProps) { function SubmissionContextHeader({ orgName, - currentStage, + currentStageLabel, currentRank, }: { orgName: string; - currentStage: string; + currentStageLabel: string; currentRank: number; }) { return ( @@ -352,17 +353,34 @@ function SubmissionContextHeader({

{orgName}

-
+
- - Stage{" "} - {currentStage || "—"} + + Current stage
+

+ {currentStageLabel || "—"} +

); } +/** + * Resolve a Stage option-value (e.g. "Organizing") to its Civi option label + * (e.g. "Stage 1 — Convene & Prepare"). Falls back to the raw value if the + * option group hasn't loaded or the value isn't in the group. + */ +function resolveStageLabel( + value: string, + options: Record | undefined, +): string { + if (!value) return ""; + const group = options?.[STAGE_OPTION_GROUP_ID]; + if (!group) return value; + return group.find((o) => o.value === value)?.label ?? value; +} + /** * Six small dots representing the six stages, with the current rank filled * and earlier ranks marked as visited. Quietly conveys progression without diff --git a/config/form.ts b/config/form.ts index 791e757..103d6b7 100644 --- a/config/form.ts +++ b/config/form.ts @@ -34,6 +34,13 @@ const visibleAtOrAfter = (...stages: string[]): VisibilityRule => ({ values: stages, }); +/** + * Option group ID for the `Stage` option group on `client.crm.fci.coop`. + * Used to resolve the current stage value (e.g. "Organizing") to its Civi + * label (e.g. "Stage 1 — Convene & Prepare") for display. + */ +export const STAGE_OPTION_GROUP_ID = 75; + // CiviCRM custom group machine names per the APIv4 inventory. const G0 = "Check_in_data__organizing_"; const G1 = "Stage_1"; @@ -50,10 +57,18 @@ const stage0: StageSectionConfig = { intro: "Core check-in fields. These are visible at every stage and capture the data we follow over time across the lifecycle of the co-op.", fields: [ - // `current_stage` is still tracked in form state (it gates every Stage - // 1–5 visibleWhen rule and feeds the journey rail), but it is no longer - // rendered as an in-card field — the SubmissionContextHeader displays - // the activity-derived Stage value as readonly above the cards. + // current_stage is a UI-only readonly field. Its value is the Civi + // option *value* (e.g. "Organizing") sourced by /api/data from the + // most recent stage-bearing Check-in (organizing) activity. Setting + // `optionGroupId` makes the readonly renderer resolve the value to + // its Civi option *label* (e.g. "Stage 1 — Convene & Prepare") for + // display. The form never writes Stage back to the activity. + { + name: "current_stage", + label: "Current stage", + type: "readonly", + optionGroupId: STAGE_OPTION_GROUP_ID, + }, { name: "Peer_Group_Participation", label: "Peer Group Participation",