Stage 0: org-contact custom fields (Food_Co_op_Organizing)

Adds four fields from the Organization Contact's Food_Co_op_Organizing
custom group to the Stage 0 (always-visible) section:

  Date Incorporated                (date, editable)
  Name on Incorporation Certificate (text, editable)
  Certificate of Incorporation     (readonly; see note)
  Equity share                     (currency, editable)

These live on the Organization Contact record, not on the Check-in
activity, so they read/write through a different code path:

  - FieldConfig gains civiContactField, mutually exclusive with civiField
  - /api/data extends the org Contact.get select to include them and
    merges values into the prefill payload keyed by form-side name
  - /api/submit splits incoming values: contact-bound fields go through
    Contact.update (run first), activity-bound fields stay in the
    Activity.create call (run second)
  - FieldRenderer readonly branch now detects file-shaped values
    ({id, file_name}) and displays the filename rather than [object Object]

Certificate_of_Incorporation is wired readonly only: the form's
file-upload pipeline is not actually wired end-to-end (FileList drops
at JSON.stringify in onSubmit; no /api/upload endpoint exists). A
follow-up will close that gap.

Also adds scripts/inspect-org-custom-fields.mjs, a one-off introspection
script for dumping CustomField metadata when wiring a new group.
This commit is contained in:
Joel Brock
2026-06-04 17:37:36 -07:00
parent b120075ca2
commit 8ecf64c79b
6 changed files with 205 additions and 12 deletions
+40
View File
@@ -49,6 +49,11 @@ const G3 = "Stage_3";
const G4 = "Stage_4";
const G5 = "Stage_5";
// Organization-contact custom group ("Food_Co_op_Organizing"). Fields here
// live on the Organization Contact, not on the Check-in activity, so they
// route through Contact.update on submit and Contact.get at form load.
const G_ORG = "Food_Co_op_Organizing";
// Stage 0 — Survey (always visible)
const stage0: StageSectionConfig = {
rank: 0,
@@ -86,6 +91,41 @@ const stage0: StageSectionConfig = {
name: "contact_email",
label: "Email",
type: "readonly",
},
// Org-contact fields (Food_Co_op_Organizing custom group). These live
// on the Organization Contact record, not on the Check-in activity, so
// they read/write via Contact.get / Contact.update instead of the
// activity prefill walk.
{
name: "Date_Incorporated",
label: "Date Incorporated",
type: "date",
civiContactField: `${G_ORG}.Date_Incorporated`,
help: "The date this co-op was legally incorporated.",
},
{
name: "Name_on_Incorporation_Certificate",
label: "Name on Incorporation Certificate",
type: "text",
civiContactField: `${G_ORG}.Name_on_Incorporation_Certificate`,
help: "The legal name as it appears on the incorporation certificate.",
},
{
// Read-only until the form gains a file-upload pipeline. The field
// is shown as the prior attachment filename; fresh uploads aren't
// supported because /api/submit serializes as JSON (FileList drops).
name: "Certificate_of_Incorporation",
label: "Certificate of Incorporation",
type: "readonly",
civiContactField: `${G_ORG}.Certificate_of_Incorporation`,
help: "Contact staff to update the certificate on file.",
},
{
name: "Equity_share",
label: "Equity share",
type: "currency",
civiContactField: `${G_ORG}.Equity_share`,
help: "The cost of a single member-owner equity share.",
},
// {
// name: "Peer_Group_Participation",