import { z } from 'zod' import { texts } from '@/texts' const v = texts.validation.eventWizard function toDateTime(dateIso: string, minutes: number): Date | null { const date = new Date(dateIso) if (!Number.isFinite(date.getTime()) || !Number.isFinite(minutes)) return null date.setHours(Math.floor(minutes / 60), minutes % 60, 0, 0) return date } export interface ConsumerEventWizardStep2Values { startDate: string startTime: number endDate: string endTime: number address: string addressVisibility: 'public' | 'attendees_only' generalArea?: string } export type ConsumerEventWizardStep2FieldErrors = Partial> export const ConsumerEventWizardStep2Validation = z .object({ startDate: z.string().min(1, v.startDateRequired), startTime: z.number({ error: v.startTimeRequired }), endDate: z.string().min(1, v.endDateRequired), endTime: z.number({ error: v.endTimeRequired }), address: z.string().trim().min(1, v.addressRequired), addressVisibility: z.enum(['public', 'attendees_only']), generalArea: z.string().trim().optional().or(z.literal('')), }) .superRefine((values, ctx) => { const startsAt = toDateTime(values.startDate, values.startTime) const endsAt = toDateTime(values.endDate, values.endTime) if (!startsAt || !endsAt) { ctx.addIssue({ code: 'custom', message: v.startEndRequired, path: ['startDate'] }) return } if (endsAt <= startsAt) { ctx.addIssue({ code: 'custom', message: v.endAfterStart, path: ['endDate'] }) } if (values.addressVisibility === 'attendees_only' && !values.generalArea?.trim()) { ctx.addIssue({ code: 'custom', message: v.generalAreaRequired, path: ['generalArea'] }) } }) export function parseConsumerEventWizardStep2( payload: ConsumerEventWizardStep2Values ): { success: true; data: ConsumerEventWizardStep2Values } | { success: false; fieldErrors: ConsumerEventWizardStep2FieldErrors } { const result = ConsumerEventWizardStep2Validation.safeParse(payload) if (result.success) { return { success: true, data: { startDate: result.data.startDate, startTime: result.data.startTime, endDate: result.data.endDate, endTime: result.data.endTime, address: result.data.address, addressVisibility: result.data.addressVisibility, generalArea: result.data.generalArea, }, } } const fieldErrors: ConsumerEventWizardStep2FieldErrors = {} for (const issue of result.error.issues) { const key = issue.path[0] if (typeof key === 'string' && !(key in fieldErrors)) { fieldErrors[key as keyof ConsumerEventWizardStep2Values] = issue.message } } return { success: false, fieldErrors } }