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:
+42
-2
@@ -163,12 +163,31 @@ export async function GET(req: NextRequest) {
|
||||
}
|
||||
const orgId = orgs[0].contact_id_b;
|
||||
|
||||
// Org-contact custom fields configured on form fields. Each
|
||||
// FieldConfig.civiContactField is `<group_name>.<field_name>` and is
|
||||
// selected directly off the Organization Contact record.
|
||||
const orgContactFieldRefs = allFields
|
||||
.map((f) => f.civiContactField)
|
||||
.filter((s): s is string => Boolean(s));
|
||||
// For file-typed org-contact fields, also pull the joined .file_name so
|
||||
// we can surface a human-readable filename in the readonly indicator.
|
||||
const orgContactFileRefs = allFields
|
||||
.filter((f) => f.type === "file" || (f.type === "readonly" && f.civiContactField?.toLowerCase().includes("certificate")))
|
||||
.map((f) => f.civiContactField)
|
||||
.filter((s): s is string => Boolean(s));
|
||||
const orgContactSelect = [
|
||||
"id",
|
||||
"display_name",
|
||||
...orgContactFieldRefs,
|
||||
...orgContactFileRefs.map((ref) => `${ref}.file_name`),
|
||||
];
|
||||
|
||||
// Fire org-name lookup, contact-identity lookup, stage-bearing-activity
|
||||
// lookup, prefill walk, and option-group fetch in parallel — they're
|
||||
// independent.
|
||||
const [orgRes, contactRes, stageActivityRes, { values: prefill }, options] = await Promise.all([
|
||||
civi<{ id: number; display_name: string }>("Contact", "get", {
|
||||
select: ["id", "display_name"],
|
||||
civi<{ id: number; display_name: string; [key: string]: unknown }>("Contact", "get", {
|
||||
select: orgContactSelect,
|
||||
where: [["id", "=", orgId]],
|
||||
}),
|
||||
// Identifying details for the form-filler. APIv4 lets us chain through
|
||||
@@ -216,6 +235,27 @@ export async function GET(req: NextRequest) {
|
||||
}
|
||||
: undefined;
|
||||
|
||||
// Merge org-contact custom-field values into prefill, keyed by form-side
|
||||
// field name. Activity-based prefill values take precedence only when an
|
||||
// explicit non-empty entry exists — but org-contact fields don't appear
|
||||
// in the activity walk, so collisions can't happen in practice.
|
||||
for (const f of allFields) {
|
||||
if (!f.civiContactField) continue;
|
||||
const raw = org[f.civiContactField];
|
||||
if (raw === null || raw === undefined || raw === "") continue;
|
||||
if (f.type === "file" || f.type === "readonly") {
|
||||
// File or readonly fields: surface as {id, file_name} so the renderer
|
||||
// can show a filename indicator. Plain readonly text fields fall
|
||||
// through to the else branch.
|
||||
const fname = org[`${f.civiContactField}.file_name`];
|
||||
if (typeof fname === "string" && fname) {
|
||||
prefill[f.name] = { id: raw, file_name: fname };
|
||||
continue;
|
||||
}
|
||||
}
|
||||
prefill[f.name] = raw;
|
||||
}
|
||||
|
||||
const payload: FormDataPayload = {
|
||||
orgName: org.display_name,
|
||||
currentStage,
|
||||
|
||||
Reference in New Issue
Block a user