Form: required submitter info section above the stage pathway
Captures the form-filler's name and email on every check-in. Both fields are required; values write back to Check_in_data__organizing_.Survey_completed_by and Survey_completed_by_email on the activity, giving us a per-submission record of who filled out which check-in. Implementation: - config/form.ts: new submitterInfo section (rank -1) at the head of the sections array. rank -1 keeps it out of the past/current/future stage pathway computation. - components/EngagementForm.tsx: filter the submitter section out of sectionsToRender and render it directly with FieldRenderer inside a bordered card above the stage list. The fields still flow through RHF registration, onInvalid scroll-to-error, and the onSubmit visibility filter the same as any other field. The staff report auto-discovers these fields via CustomField.get since they live in Check_in_data__organizing_, so the field history shows up in the report with no extra wiring.
This commit is contained in:
@@ -19,6 +19,7 @@ function sectionForField(
|
||||
import { evaluate } from "@/lib/conditional";
|
||||
import { STAGE_OPTION_GROUP_ID } from "@/config/form";
|
||||
import { StageSection } from "./StageSection";
|
||||
import { FieldRenderer } from "./fields/FieldRenderer";
|
||||
import { loadDraft, saveDraft, clearDraft } from "@/lib/draft";
|
||||
|
||||
interface EngagementFormProps {
|
||||
@@ -241,6 +242,26 @@ export function EngagementForm({ config, cid, cs }: EngagementFormProps) {
|
||||
|
||||
const currentRank = currentStageValue ? STAGE_RANK[currentStageValue] ?? 0 : 0;
|
||||
|
||||
// Section pathway state — past / current / future is determined entirely
|
||||
// by stage rank vs the org's current rank. Sections never hide; future
|
||||
// stages render in a locked treatment so users can preview what's ahead.
|
||||
//
|
||||
// Section-level visibleWhen is still consulted on submit to strip
|
||||
// locked-stage values from the payload (see onSubmit below), so a future
|
||||
// stage's data never gets accidentally written.
|
||||
// The submitter-info section (rank: -1) lives outside the stage pathway —
|
||||
// it renders directly above the section list (see JSX below), not inside
|
||||
// the rail/marker accordion model used for Stage 0..5. Filter it out here
|
||||
// so its rank doesn't pollute the past/current/future calculation.
|
||||
const submitterSection = useMemo(
|
||||
() => config.sections.find((s) => s.id === "submitter_info"),
|
||||
[config.sections],
|
||||
);
|
||||
const stageSections = useMemo(
|
||||
() => config.sections.filter((s) => s.id !== "submitter_info"),
|
||||
[config.sections],
|
||||
);
|
||||
|
||||
// Section pathway state — past / current / future is determined entirely
|
||||
// by stage rank vs the org's current rank. Sections never hide; future
|
||||
// stages render in a locked treatment so users can preview what's ahead.
|
||||
@@ -250,12 +271,12 @@ export function EngagementForm({ config, cid, cs }: EngagementFormProps) {
|
||||
// stage's data never gets accidentally written.
|
||||
const sectionsToRender = useMemo(
|
||||
() =>
|
||||
config.sections.map((s) => {
|
||||
stageSections.map((s) => {
|
||||
const pathwayState: PathwayState =
|
||||
s.rank < currentRank ? "past" : s.rank === currentRank ? "current" : "future";
|
||||
return { section: s, pathwayState, locked: pathwayState === "future" };
|
||||
}),
|
||||
[config.sections, currentRank],
|
||||
[stageSections, currentRank],
|
||||
);
|
||||
|
||||
// Track which sections the user has manually opened/closed so we can
|
||||
@@ -398,6 +419,38 @@ export function EngagementForm({ config, cid, cs }: EngagementFormProps) {
|
||||
}} />
|
||||
)}
|
||||
|
||||
{submitterSection && (
|
||||
<section
|
||||
aria-labelledby="submitter-info-heading"
|
||||
className="rounded-2xl border border-rule bg-paper p-4 sm:p-5"
|
||||
>
|
||||
<h2
|
||||
id="submitter-info-heading"
|
||||
className="font-display text-lg font-medium text-ink"
|
||||
>
|
||||
{submitterSection.label}
|
||||
</h2>
|
||||
{submitterSection.intro && (
|
||||
<p className="mt-1 text-sm text-ink-soft">{submitterSection.intro}</p>
|
||||
)}
|
||||
<div className="mt-3 grid grid-cols-1 gap-4 sm:grid-cols-2">
|
||||
{submitterSection.fields.map((f) => (
|
||||
<FieldRenderer
|
||||
key={f.name}
|
||||
field={f}
|
||||
register={register}
|
||||
setValue={setValue}
|
||||
control={control}
|
||||
errors={errors}
|
||||
cid={cid}
|
||||
cs={cs}
|
||||
onUploadStateChange={handleUploadStateChange}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
)}
|
||||
|
||||
<ol className="relative space-y-5 md:pl-12">
|
||||
{sectionsToRender.map((entry, i) => {
|
||||
const { section, pathwayState, locked } = entry;
|
||||
|
||||
+31
-1
@@ -54,6 +54,36 @@ const G5 = "Stage_5";
|
||||
// route through Contact.update on submit and Contact.get at form load.
|
||||
const G_ORG = "Food_Co_op_Organizing";
|
||||
|
||||
// Submitter information — captured on every check-in. Rendered above the
|
||||
// stage pathway (see EngagementForm) so it's not threaded into the past /
|
||||
// current / future stage rail. Both fields are required on every submission.
|
||||
// rank: -1 keeps the section out of the rank-based pathway computation;
|
||||
// EngagementForm filters this section out of `sectionsToRender` and renders
|
||||
// it directly via FieldRenderer above the stage list.
|
||||
const submitterInfo: StageSectionConfig = {
|
||||
rank: -1,
|
||||
id: "submitter_info",
|
||||
label: "Survey submitter",
|
||||
intro:
|
||||
"Who's filling this out? We log this with each submission so we can follow up if needed.",
|
||||
fields: [
|
||||
{
|
||||
name: "Survey_completed_by",
|
||||
label: "Survey completed by",
|
||||
type: "text",
|
||||
required: true,
|
||||
civiField: `${G0}.Survey_completed_by`,
|
||||
},
|
||||
{
|
||||
name: "Survey_completed_by_email",
|
||||
label: "Survey completed by — email",
|
||||
type: "email",
|
||||
required: true,
|
||||
civiField: `${G0}.Survey_completed_by_email`,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
// Stage 0 — Survey (always visible)
|
||||
const stage0: StageSectionConfig = {
|
||||
rank: 0,
|
||||
@@ -753,7 +783,7 @@ export const formConfig: FormConfig = {
|
||||
subtitle:
|
||||
"Thank you for updating your co-op information. The questions you'll see depend on where you are in the Framework.",
|
||||
stageField: "current_stage",
|
||||
sections: [stage0, stage1, stage2, stage3, stage4, stage5],
|
||||
sections: [submitterInfo, stage0, stage1, stage2, stage3, stage4, stage5],
|
||||
};
|
||||
|
||||
/** Convenience: flatten all field configs across all sections for lookup by name. */
|
||||
|
||||
Reference in New Issue
Block a user