feat(portal): multi-step volunteer registration form with public event endpoint

- Add GET /api/v1/public/events/{slug}/registration-data endpoint for fetching
  event sections and time slots without auth
- Create 5-step registration form: personal info, details, motivation, section
  preferences, availability
- VeeValidate + Zod validation per step with Dutch error messages
- Auth-aware: pre-fills name/email for authenticated users
- Mobile responsive with custom chip-based step indicator
- Success page with contextual actions (dashboard vs login)
- Types, composable (TanStack Query), and Zod schemas

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-10 18:41:20 +02:00
parent 60ea1f0b40
commit 3400e4cc7e
12 changed files with 1077 additions and 8 deletions

View File

@@ -0,0 +1,24 @@
import { z } from 'zod'
export const step1Schema = z.object({
name: z.string().min(1, 'Naam is verplicht').max(255),
email: z.string().min(1, 'E-mailadres is verplicht').email('Ongeldig e-mailadres').max(255),
phone: z.string().max(50).optional().or(z.literal('')),
})
export const step2Schema = z.object({
tshirt_size: z.enum(['XS', 'S', 'M', 'L', 'XL', 'XXL', 'XXXL']).optional().or(z.literal('')),
first_aid: z.boolean().default(false),
allergies: z.string().max(500).optional().or(z.literal('')),
access_requirements: z.string().max(500).optional().or(z.literal('')),
driving_licence: z.boolean().default(false),
})
export const step3Schema = z.object({
motivation: z.string().max(1000).optional().or(z.literal('')),
motivation_other: z.string().max(500).optional().or(z.literal('')),
})
export const fullRegistrationSchema = step1Schema
.merge(step2Schema)
.merge(step3Schema)