feat: volunteer self-registration with email confirmation - #955
Conversation
Mirrors the NGO/agent registration screen but for volunteers, headline "Become a volunteer" instead of "Register your organisation". Same account fields, POSTs role: UserRole.VOLUNTEER to the existing POST /user endpoint, which already self-registers volunteers freely and sends the verification email (no BE/SDK change needed). Generalizes AccountStep/validateStep with a translation-namespace parameter (default unchanged, so the agent flow is unaffected) instead of duplicating the step markup and validation logic.
Review findings1. Consent checkbox still shows agent-only legal copy to volunteers (
2. The 3. No dedupe check against an existing
This third one is a pre-existing backend gap, not something this PR introduces, so I filed it as a standalone follow-up rather than blocking this PR on it: need4deed-org/be#923 (tracked under the epic need4deed-org/be#922). |
| import { AccountRegistrationData, defaultAccountRegistrationData } from "@/components/AgentRegistration/types"; | ||
| import Link from "next/link"; | ||
|
|
||
| const NAMESPACE = "volunteerRegistration"; |
There was a problem hiding this comment.
this seems pointless to me as you're not really reusing a component, just duplicating it. Also in the AgentRegistration component you're using the raw string instead
e.g.
<PageTitle>{t("agentRegistration.title")}</PageTitle>compared to
<PageTitle>{t(`${NAMESPACE}.title`)}</PageTitle>| const [submitError, setSubmitError] = useState<string | null>(null); | ||
| const [isSubmitting, setIsSubmitting] = useState(false); | ||
| const [isSuccess, setIsSuccess] = useState(false); | ||
|
|
There was a problem hiding this comment.
with our legacy BecomeVolunteer form found on components/forms/BecomeVolunteer/BecomeVolunteer.tsx, we used tanstack useForm hook which reduced the need for all of these useState
| } finally { | ||
| setIsSubmitting(false); | ||
| } | ||
| }; |
There was a problem hiding this comment.
line 70-99 would be cleaner as a custom hook. We do have a custom usePostRequest hook but it seems a bit outdated (uses fetch API over axios)
| min-height: 0; | ||
| flex: 1; | ||
| `; | ||
|
|
There was a problem hiding this comment.
if you move this PageWrapper to and export this from @/components/AgentRegistration/styled (like the other styled components) then you don't have to declare it twice such as here & in the AgentRegistration.tsx component

Description
Adds a public "Become a volunteer" registration page, mirroring the existing NGO/agent registration screen (
/register/agent) but for volunteers. Same account fields (first name, last name, email, password, confirm password, phone, consent), headline "Become a volunteer" instead of "Register your organisation".No backend or SDK change was needed:
POST /useralready acceptsrole: UserRole.VOLUNTEERand self-registers it freely (its pre-handler comment: "Volunteers and users self-register freely", unlikeAGENTwhich requires a matching org email domain).verify-email/[token]/page.tsxalready redirects any non-"agent" role straight to/dashboardafter verification, so no extra profile-completion step is needed for volunteers.Related Issues
Closes #954
Changes
/register/volunteer→VolunteerRegistrationcomponent, POSTsrole: UserRole.VOLUNTEER.AccountStepandvalidateStep(underAgentRegistration/) with an optionalnamespaceparam (defaults to"agentRegistration", so the existing agent flow is unchanged) instead of duplicating the ~150 lines of identical field markup/validation for a second flow.AgentRegistration'sstyled.tsprimitives and added avolunteerRegistrationi18n namespace (en/de) for the copy that does differ (headline, subtitle, success screen).Verification
tsc --noEmitandyarn lintpass./en/register/volunteerrenders the headline, all fields, and the consent links correctly; verified client-side validation triggers on empty submit. Did not exercise the livePOST /usercall (no backend running locally), that endpoint itself is unchanged.Checklist