Derive current stage from most-recent stage-bearing activity

Stage authority moves from Organization.Food_Co_op_Organizing.Stage to the
most recent Check-in (organizing) activity whose Stage custom field is set.
Staff create these activities manually to mark transitions; org-owner form
submissions no longer write the Stage field at all, so they cannot override
a staff-set transition.

- /api/data: removed the Contact.get for org-side Stage; added an
  Activity.get filtered to ACTIVITY_TYPE_NAME + ACTIVITY_STAGE_FIELD IS
  NOT EMPTY, ordered by activity_date_time DESC, id DESC, limit 1.
  Fallback when no such activity exists: Inquiry (rank 0). Org-name
  lookup, stage activity, prefill, and option-group fetch all run in
  parallel via Promise.all.
- /api/submit: removed the stageAtSubmission read + the
  [ACTIVITY_STAGE_FIELD] write on the activity record. The form's
  activities are stage-null by design now.
- config/form.ts: dropped the stage_at_submission readonly field (no
  longer being set or displayed). Kept ACTIVITY_STAGE_FIELD export — it's
  now used by /api/data to find stage-bearing activities. Updated the
  current_stage field comment to reflect the new source.
- components/EngagementForm.tsx: dropped stage_at_submission from
  evalState (no longer referenced by any visibility rule or readonly
  display).

Org.Food_Co_op_Organizing.Stage remains in CiviCRM for staff list views;
the middleware no longer reads or writes it. No backfill required —
orgs without a stage-bearing activity simply read as Inquiry.
This commit is contained in:
Joel Brock
2026-05-13 11:41:25 -07:00
parent 18b7a67fa1
commit b4e80517a7
4 changed files with 132 additions and 113 deletions

View File

@@ -76,13 +76,11 @@ export function EngagementForm({ config, cid, cs }: EngagementFormProps) {
// State map passed to the conditional engine and to FieldRenderer for
// readonly display values. Only fields referenced by visibility rules or
// by readonly displays need to be here. Both readonly fields in this form
// (current_stage, stage_at_submission) display the org's current stage.
// by readonly displays need to be here — currently just current_stage,
// which gates every Stage 15 visibleWhen rule and is shown as a readonly
// field in the Stage 0 header.
const evalState = useMemo(
() => ({
current_stage: currentStageValue,
stage_at_submission: currentStageValue,
}),
() => ({ current_stage: currentStageValue }),
[currentStageValue],
);