feat: add recurring bookings feature with related handlers and services
- Introduced recurring booking groups with the ability to create and cancel them. - Added database migrations for recurring bookings, including new tables and relationships. - Implemented handlers for creating and canceling recurring bookings in the admin booking module. - Enhanced existing booking services to support recurring bookings logic. - Updated API contract to include new schemas for recurring bookings. - Refactored existing code for improved readability and maintainability.
This commit is contained in:
@@ -1,26 +1,26 @@
|
||||
import { z } from 'zod'
|
||||
import { dayOfWeekSchema } from './court'
|
||||
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}$/
|
||||
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(),
|
||||
@@ -29,7 +29,7 @@ export const publicAvailabilityCourtSchema = z.object({
|
||||
slotDurationMinutes: z.int().positive(),
|
||||
availabilityDay: dayOfWeekSchema,
|
||||
availableSlots: z.array(publicBookingSlotSchema),
|
||||
})
|
||||
});
|
||||
|
||||
export const publicAvailabilityResponseSchema = z.object({
|
||||
complexId: z.uuid(),
|
||||
@@ -40,7 +40,7 @@ export const publicAvailabilityResponseSchema = z.object({
|
||||
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.'),
|
||||
@@ -57,10 +57,8 @@ export const createPublicBookingSchema = z.object({
|
||||
.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.'),
|
||||
})
|
||||
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.'),
|
||||
@@ -69,7 +67,7 @@ export const cancelPublicBookingSchema = z.object({
|
||||
.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(),
|
||||
@@ -89,7 +87,7 @@ export const publicBookingSchema = z.object({
|
||||
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),
|
||||
@@ -105,14 +103,14 @@ export const publicBookingConfirmationSchema = z.object({
|
||||
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>
|
||||
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>;
|
||||
|
||||
Reference in New Issue
Block a user