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 { evaluate } from "@/lib/conditional";
|
||||||
import { STAGE_OPTION_GROUP_ID } from "@/config/form";
|
import { STAGE_OPTION_GROUP_ID } from "@/config/form";
|
||||||
import { StageSection } from "./StageSection";
|
import { StageSection } from "./StageSection";
|
||||||
|
import { FieldRenderer } from "./fields/FieldRenderer";
|
||||||
import { loadDraft, saveDraft, clearDraft } from "@/lib/draft";
|
import { loadDraft, saveDraft, clearDraft } from "@/lib/draft";
|
||||||
|
|
||||||
interface EngagementFormProps {
|
interface EngagementFormProps {
|
||||||
@@ -241,6 +242,26 @@ export function EngagementForm({ config, cid, cs }: EngagementFormProps) {
|
|||||||
|
|
||||||
const currentRank = currentStageValue ? STAGE_RANK[currentStageValue] ?? 0 : 0;
|
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
|
// Section pathway state — past / current / future is determined entirely
|
||||||
// by stage rank vs the org's current rank. Sections never hide; future
|
// 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.
|
// 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.
|
// stage's data never gets accidentally written.
|
||||||
const sectionsToRender = useMemo(
|
const sectionsToRender = useMemo(
|
||||||
() =>
|
() =>
|
||||||
config.sections.map((s) => {
|
stageSections.map((s) => {
|
||||||
const pathwayState: PathwayState =
|
const pathwayState: PathwayState =
|
||||||
s.rank < currentRank ? "past" : s.rank === currentRank ? "current" : "future";
|
s.rank < currentRank ? "past" : s.rank === currentRank ? "current" : "future";
|
||||||
return { section: s, pathwayState, locked: pathwayState === "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
|
// 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">
|
<ol className="relative space-y-5 md:pl-12">
|
||||||
{sectionsToRender.map((entry, i) => {
|
{sectionsToRender.map((entry, i) => {
|
||||||
const { section, pathwayState, locked } = entry;
|
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.
|
// route through Contact.update on submit and Contact.get at form load.
|
||||||
const G_ORG = "Food_Co_op_Organizing";
|
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)
|
// Stage 0 — Survey (always visible)
|
||||||
const stage0: StageSectionConfig = {
|
const stage0: StageSectionConfig = {
|
||||||
rank: 0,
|
rank: 0,
|
||||||
@@ -753,7 +783,7 @@ export const formConfig: FormConfig = {
|
|||||||
subtitle:
|
subtitle:
|
||||||
"Thank you for updating your co-op information. The questions you'll see depend on where you are in the Framework.",
|
"Thank you for updating your co-op information. The questions you'll see depend on where you are in the Framework.",
|
||||||
stageField: "current_stage",
|
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. */
|
/** Convenience: flatten all field configs across all sections for lookup by name. */
|
||||||
|
|||||||
Reference in New Issue
Block a user