+
Powered by Crewli
@@ -1095,14 +1166,79 @@ async function onSubmit() {
.registration-container {
position: relative;
z-index: 1;
- max-inline-size: 1000px;
+ max-inline-size: 1040px;
+}
+
+.registration-header-fallback {
+ background: rgb(var(--v-theme-surface));
+ border-color: rgba(var(--v-border-color), var(--v-border-opacity)) !important;
+}
+
+.registration-welcome-html :deep(p) {
+ margin-block-end: 0.5rem;
+}
+
+.registration-welcome-html :deep(p:last-child) {
+ margin-block-end: 0;
+}
+
+.step-indicator {
+ inline-size: 48px;
+ block-size: 48px;
+ border-radius: 10px;
+ font-size: 16px;
+ font-weight: 700;
+ transition: background-color 0.2s ease, color 0.2s ease;
+}
+
+.step-indicator--active {
+ background-color: rgb(var(--v-theme-primary));
+ color: rgb(var(--v-theme-on-primary));
+}
+
+.step-indicator--done {
+ background-color: rgba(var(--v-theme-primary), 0.12);
+ color: rgb(var(--v-theme-primary));
+}
+
+.step-indicator--todo {
+ background-color: rgba(var(--v-theme-on-surface), 0.06);
+ color: rgba(var(--v-theme-on-surface), 0.35);
+}
+
+.section-category-heading {
+ padding-block-end: 8px;
+ border-block-end: 1px solid rgba(var(--v-border-color), var(--v-border-opacity));
+}
+
+.registration-section-grid {
+ align-items: stretch;
+}
+
+.section-pref-card {
+ border-color: rgba(var(--v-border-color), var(--v-border-opacity)) !important;
+ transition: border-color 0.15s ease, background-color 0.15s ease;
+}
+
+.section-pref-card--selected {
+ border: 2px solid rgb(var(--v-theme-primary)) !important;
+ background-color: rgba(var(--v-theme-primary), 0.06);
+}
+
+.priority-pill {
+ position: absolute;
+ inset-block-start: 10px;
+ inset-inline-end: 10px;
+ z-index: 1;
+ font-size: 11px;
+ font-weight: 600;
+ padding-block: 4px;
+ padding-inline: 10px;
+ border-radius: 999px;
+ background-color: rgba(var(--v-theme-on-surface), 0.75);
+ color: rgb(var(--v-theme-surface));
}
-/*
- Native date inputs have a large intrinsic min-width; without min-width: 0 the
- grid cell grows and the prepend-inner icon no longer sits inside the outline
- next to the value (Vuetify v-field grid: prepend-inner | field).
-*/
.volunteer-reg-dob-field :deep(.v-field__field) {
min-inline-size: 0;
}
@@ -1113,10 +1249,6 @@ async function onSubmit() {
min-inline-size: 0;
}
-/*
- Vuetify’s .v-list uses overflow: auto, which shows scrollbars on this step when
- list rows are slightly wider than the card (e.g. checkbox + text + rating).
-*/
.registration-availability-list {
overflow: visible !important;
}
diff --git a/apps/portal/src/schemas/registrationSchema.ts b/apps/portal/src/schemas/registrationSchema.ts
index 34f30601..3f228c49 100644
--- a/apps/portal/src/schemas/registrationSchema.ts
+++ b/apps/portal/src/schemas/registrationSchema.ts
@@ -1,6 +1,7 @@
import { z } from 'zod'
-export const step1Schema = z.object({
+/** Fixed fields for step "Over jou" (portal volunteer registration). */
+export const personalStepSchema = z.object({
first_name: z.string().min(1, 'Voornaam is verplicht').max(255),
last_name: z.string().min(1, 'Achternaam is verplicht').max(255),
email: z.string().min(1, 'E-mailadres is verplicht').email('Ongeldig e-mailadres').max(255),
@@ -8,18 +9,4 @@ export const step1Schema = z.object({
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('')),
- 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)
+export const fullRegistrationSchema = personalStepSchema
diff --git a/apps/portal/src/types/registration.ts b/apps/portal/src/types/registration.ts
index 9a7b1a14..f4d52da9 100644
--- a/apps/portal/src/types/registration.ts
+++ b/apps/portal/src/types/registration.ts
@@ -1,3 +1,33 @@
+export type RegistrationFieldType =
+ | 'text'
+ | 'textarea'
+ | 'select'
+ | 'multiselect'
+ | 'checkbox'
+ | 'radio'
+ | 'boolean'
+ | 'number'
+ | 'tag_picker'
+
+export interface RegistrationTagOption {
+ id: string
+ name: string
+ category: string | null
+}
+
+export interface RegistrationField {
+ id: string
+ label: string
+ slug: string
+ field_type: RegistrationFieldType
+ options: string[] | null
+ tag_category: string | null
+ is_required: boolean
+ section: string | null
+ help_text: string | null
+ available_tags?: RegistrationTagOption[]
+}
+
export interface EventRegistrationData {
event: {
id: string
@@ -8,9 +38,12 @@ export interface EventRegistrationData {
registration_banner_url: string | null
registration_welcome_text: string | null
registration_logo_url: string | null
+ registration_show_section_preferences: boolean
+ registration_show_availability: boolean
}
sections: SectionOption[]
time_slots: TimeSlotOption[]
+ registration_fields: RegistrationField[]
}
export interface SectionOption {
@@ -30,33 +63,25 @@ export interface TimeSlotOption {
duration_hours: number
}
-export interface SectionPreference {
- section_name: string
+export interface SectionPreferencePayload {
+ festival_section_id: string
priority: number
}
-export interface VolunteerAvailability {
+export interface VolunteerAvailabilityPayload {
time_slot_id: string
- preference_level: number
+ preference_level?: number
}
+export type FieldValuePayload = string | number | boolean | string[] | null
+
export interface VolunteerRegistrationForm {
- // Step 1
first_name: string
last_name: string
- date_of_birth: string
+ date_of_birth?: string
email: string
- phone: string
- // Step 2
- tshirt_size: string
- first_aid: boolean
- allergies: string
- driving_licence: boolean
- // Step 3
- motivation: string
- motivation_other: string
- // Step 4
- section_preferences: SectionPreference[]
- // Step 5
- availabilities: VolunteerAvailability[]
+ phone?: string
+ field_values?: Record
+ section_preferences?: SectionPreferencePayload[]
+ availabilities?: VolunteerAvailabilityPayload[]
}