import { z } from "zod"; /** * Single source of truth for the installation wizard's shape — shared * between the client wizard component and the final Server Action (see * actions/install.ts). Deliberately one flat schema, not one per wizard * step: the wizard itself is client-side-only state (see * install-wizard.tsx's own header for why) and nothing is validated or * written server-side until the single final submission, so there is only * ever one real validation boundary to define. */ export const installAdministratorSchema = z .object({ firstName: z.string().trim().min(1, "First name is required"), lastName: z.string().trim().min(1, "Last name is required"), email: z.email("Enter a valid email address"), phone: z.string().trim().optional().or(z.literal("")), password: z.string().min(8, "Password must be at least 8 characters"), confirmPassword: z.string(), }) .refine((data) => data.password === data.confirmPassword, { message: "Passwords don't match", path: ["confirmPassword"], }); export const installBusinessSchema = z.object({ name: z.string().trim().min(1, "Business name is required"), shortName: z.string().trim().max(40).optional().or(z.literal("")), description: z.string().trim().optional().or(z.literal("")), email: z.email("Enter a valid email address").optional().or(z.literal("")), phone: z.string().trim().optional().or(z.literal("")), website: z.string().trim().optional().or(z.literal("")), }); export const installLocationSchema = z.object({ addressLine1: z.string().trim().min(1, "Address line 1 is required"), addressLine2: z.string().trim().optional().or(z.literal("")), city: z.string().trim().min(1, "Town or city is required"), region: z.string().trim().optional().or(z.literal("")), postalCode: z.string().trim().min(1, "Postcode is required"), country: z.string().trim().min(1, "Country is required"), timezone: z.string().trim().min(1, "Timezone is required"), currency: z.string().trim().length(3, "Currency should be a 3-letter code, e.g. GBP"), locale: z.string().trim().min(1, "Locale is required"), }); const weekdaySchema = z.enum(["MONDAY", "TUESDAY", "WEDNESDAY", "THURSDAY", "FRIDAY", "SATURDAY", "SUNDAY"]); export const installHoursDaySchema = z.object({ dayOfWeek: weekdaySchema, isClosed: z.boolean(), opensAtMinutes: z.number().int().min(0).max(1439).optional(), closesAtMinutes: z.number().int().min(0).max(1439).optional(), }); export const installBookingRulesSchema = z.object({ allowGuestBooking: z.boolean(), minNoticeMinutes: z.number().int().min(0), maxAdvanceDays: z.number().int().min(1), cancellationWindowHours: z.number().int().min(0), rescheduleWindowHours: z.number().int().min(0), slotIntervalMinutes: z.number().int().min(5).max(120), bufferBeforeMinutes: z.number().int().min(0), bufferAfterMinutes: z.number().int().min(0), maxAppointmentsPerDay: z.number().int().min(1).nullable(), maxAppointmentsPerStaffPerDay: z.number().int().min(1).nullable(), customerCanChooseStaff: z.boolean(), requireApproval: z.boolean(), }); export const installBrandingSchema = z.object({ logoUrl: z.string().trim().optional().or(z.literal("")), coverImageUrl: z.string().trim().optional().or(z.literal("")), themeColor: z.string().trim().optional().or(z.literal("")), backgroundColor: z.string().trim().optional().or(z.literal("")), }); const decimalPrice = z.string().regex(/^\d+(\.\d{1,2})?$/, "Enter a price like 22.50"); export const installStarterServiceSchema = z.object({ categoryName: z.string().trim().min(1), name: z.string().trim().min(1), description: z.string().trim().optional().or(z.literal("")), price: decimalPrice, durationMinutes: z.number().int().min(5).max(480), active: z.boolean(), allowOnlineBooking: z.boolean(), }); export const installSchema = z.object({ installationToken: z.string().min(1, "Installation token is required"), administrator: installAdministratorSchema, business: installBusinessSchema, location: installLocationSchema, hours: z.array(installHoursDaySchema).length(7), bookingRules: installBookingRulesSchema, branding: installBrandingSchema, starterServices: z.array(installStarterServiceSchema).max(20), }); export type InstallAdministratorInput = z.infer; export type InstallBusinessInput = z.infer; export type InstallLocationInput = z.infer; export type InstallHoursDayInput = z.infer; export type InstallBookingRulesInput = z.infer; export type InstallBrandingInput = z.infer; export type InstallStarterServiceInput = z.infer; export type InstallInput = z.infer;