harden(forms): prevent duplicate onboarding form submission on Back/Forward

Phase 4 self-audit: alreadySubmitted was a page-load snapshot, so a
member who submitted a form/waiver step then navigated Back and
Forward would insert a second submission. Track submitted steps in
session state and treat them as already-submitted.
This commit is contained in:
Moheeb Zara
2026-05-16 16:29:32 -07:00
parent 295d220849
commit da25950988
+8 -2
View File
@@ -60,6 +60,10 @@ export function OnboardingFlow({ spaceName, steps, canSkip, profile: initialProf
const [skillsText, setSkillsText] = useState(initialProfile.skills.join(', '))
const [formAnswers, setFormAnswers] = useState<Record<string, Record<string, unknown>>>({})
const [formConsent, setFormConsent] = useState<Record<string, boolean>>({})
// Form steps already submitted in THIS session. Prevents a second insert if
// the member navigates Back then Forward over a form/waiver step (the
// server-side alreadySubmitted prop is a snapshot from page load).
const [submittedNow, setSubmittedNow] = useState<Record<string, boolean>>({})
const step = steps[index]
const isLast = index === steps.length - 1
@@ -69,7 +73,8 @@ export function OnboardingFlow({ spaceName, steps, canSkip, profile: initialProf
const isFormStep = step.step_type === 'form'
const formDef = step.formDef ?? null
const formActionable = isFormStep && formDef && !step.alreadySubmitted
const effectiveSubmitted = step.alreadySubmitted === true || submittedNow[step.id] === true
const formActionable = isFormStep && !!formDef && !effectiveSubmitted
const isWaiver = formDef?.kind === 'waiver'
const consentOk = !formActionable || !isWaiver || formConsent[step.id] === true
@@ -131,6 +136,7 @@ export function OnboardingFlow({ spaceName, steps, canSkip, profile: initialProf
toast.error(res.error)
return
}
setSubmittedNow(p => ({ ...p, [step.id]: true }))
}
await markOnboardingStepDone(step.id)
setBusy(false)
@@ -243,7 +249,7 @@ export function OnboardingFlow({ spaceName, steps, canSkip, profile: initialProf
<p className="font-sans text-sm text-muted-foreground">
This form is not available right now. You can continue.
</p>
) : step.alreadySubmitted ? (
) : effectiveSubmitted ? (
<p className="font-sans text-sm text-muted-foreground">
You have already completed this. You can continue.
</p>