- Added cancelPublicBooking handler to manage booking cancellations. - Updated public booking service to include cancellation logic. - Created cancelPublicBooking schema for input validation. - Modified public booking confirmation page to allow users to cancel their bookings. - Enhanced email service to send cancellation confirmation emails. - Made customerEmail field required in booking schemas and updated related components. - Updated API client to support cancellation requests. - Added migration to enforce non-nullable customer_email fields in the database.
119 lines
4.2 KiB
TypeScript
119 lines
4.2 KiB
TypeScript
import { z } from 'zod'
|
|
import { dayOfWeekSchema } from './court'
|
|
|
|
const TIME_REGEX = /^([01]\d|2[0-3]):([0-5]\d)$/
|
|
const ISO_DATE_REGEX = /^\d{4}-\d{2}-\d{2}$/
|
|
const BOOKING_CODE_REGEX = /^[A-Z0-9]{6,8}$/
|
|
|
|
export const publicAvailabilityQuerySchema = z.object({
|
|
date: z.string().regex(ISO_DATE_REGEX, 'La fecha debe tener formato YYYY-MM-DD.'),
|
|
sportId: z.uuid('El sportId debe ser un UUID valido.').optional(),
|
|
})
|
|
|
|
export const publicBookingSportSchema = z.object({
|
|
id: z.uuid(),
|
|
name: z.string(),
|
|
slug: z.string(),
|
|
})
|
|
|
|
export const publicBookingSlotSchema = z.object({
|
|
startTime: z.string().regex(TIME_REGEX),
|
|
endTime: z.string().regex(TIME_REGEX),
|
|
price: z.number().nonnegative(),
|
|
})
|
|
|
|
export const publicAvailabilityCourtSchema = z.object({
|
|
courtId: z.uuid(),
|
|
courtName: z.string(),
|
|
sport: publicBookingSportSchema,
|
|
slotDurationMinutes: z.int().positive(),
|
|
availabilityDay: dayOfWeekSchema,
|
|
availableSlots: z.array(publicBookingSlotSchema),
|
|
})
|
|
|
|
export const publicAvailabilityResponseSchema = z.object({
|
|
complexId: z.uuid(),
|
|
complexName: z.string(),
|
|
complexAddress: z.string().optional(),
|
|
complexSlug: z.string(),
|
|
date: z.string().regex(ISO_DATE_REGEX),
|
|
sportSelectionRequired: z.boolean(),
|
|
sports: z.array(publicBookingSportSchema),
|
|
courts: z.array(publicAvailabilityCourtSchema),
|
|
})
|
|
|
|
export const createPublicBookingSchema = z.object({
|
|
date: z.string().regex(ISO_DATE_REGEX, 'La fecha debe tener formato YYYY-MM-DD.'),
|
|
sportId: z.uuid('El sportId debe ser un UUID valido.').optional(),
|
|
courtId: z.uuid('El courtId debe ser un UUID valido.'),
|
|
startTime: z.string().regex(TIME_REGEX, 'La hora debe tener formato HH:mm.'),
|
|
customerName: z
|
|
.string()
|
|
.trim()
|
|
.min(2, 'El nombre debe tener al menos 2 caracteres.')
|
|
.max(120, 'El nombre no puede superar los 120 caracteres.'),
|
|
customerPhone: z
|
|
.string()
|
|
.trim()
|
|
.min(6, 'El telefono debe tener al menos 6 caracteres.')
|
|
.max(30, 'El telefono no puede superar los 30 caracteres.'),
|
|
customerEmail: z
|
|
.string()
|
|
.email('El email ingresado no es valido.'),
|
|
})
|
|
|
|
export const cancelPublicBookingSchema = z.object({
|
|
bookingCode: z.string().regex(BOOKING_CODE_REGEX, 'El codigo de reserva no es valido.'),
|
|
customerPhone: z
|
|
.string()
|
|
.trim()
|
|
.min(6, 'El telefono debe tener al menos 6 caracteres.')
|
|
.max(30, 'El telefono no puede superar los 30 caracteres.'),
|
|
})
|
|
|
|
export const publicBookingSchema = z.object({
|
|
id: z.uuid(),
|
|
bookingCode: z.string().regex(BOOKING_CODE_REGEX),
|
|
complexId: z.uuid(),
|
|
complexName: z.string(),
|
|
complexSlug: z.string(),
|
|
courtId: z.uuid(),
|
|
courtName: z.string(),
|
|
sport: publicBookingSportSchema,
|
|
date: z.string().regex(ISO_DATE_REGEX),
|
|
startTime: z.string().regex(TIME_REGEX),
|
|
endTime: z.string().regex(TIME_REGEX),
|
|
customerName: z.string(),
|
|
customerPhone: z.string(),
|
|
customerEmail: z.string(),
|
|
status: z.enum(['CONFIRMED', 'CANCELLED', 'COMPLETED']),
|
|
price: z.number().nonnegative(),
|
|
createdAt: z.string().datetime(),
|
|
})
|
|
|
|
export const publicBookingConfirmationSchema = z.object({
|
|
bookingCode: z.string().regex(BOOKING_CODE_REGEX),
|
|
complexName: z.string(),
|
|
complexAddress: z.string().optional(),
|
|
complexSlug: z.string(),
|
|
date: z.string().regex(ISO_DATE_REGEX),
|
|
startTime: z.string().regex(TIME_REGEX),
|
|
endTime: z.string().regex(TIME_REGEX),
|
|
price: z.number().nonnegative(),
|
|
courtName: z.string(),
|
|
sport: publicBookingSportSchema,
|
|
status: z.enum(['CONFIRMED', 'CANCELLED', 'COMPLETED']),
|
|
customerEmail: z.string(),
|
|
createdAt: z.string().datetime(),
|
|
})
|
|
|
|
export type PublicAvailabilityQuery = z.infer<typeof publicAvailabilityQuerySchema>
|
|
export type PublicBookingSport = z.infer<typeof publicBookingSportSchema>
|
|
export type PublicBookingSlot = z.infer<typeof publicBookingSlotSchema>
|
|
export type PublicAvailabilityCourt = z.infer<typeof publicAvailabilityCourtSchema>
|
|
export type PublicAvailabilityResponse = z.infer<typeof publicAvailabilityResponseSchema>
|
|
export type CreatePublicBookingInput = z.infer<typeof createPublicBookingSchema>
|
|
export type CancelPublicBookingInput = z.infer<typeof cancelPublicBookingSchema>
|
|
export type PublicBooking = z.infer<typeof publicBookingSchema>
|
|
export type PublicBookingConfirmation = z.infer<typeof publicBookingConfirmationSchema>
|