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,16 +1,18 @@
|
||||
import { z } from 'zod'
|
||||
import { z } from 'zod';
|
||||
|
||||
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 bookingStatusSchema = z.enum(['CONFIRMED', 'CANCELLED', 'COMPLETED', 'NOSHOW'])
|
||||
export const bookingStatusSchema = z.enum(['CONFIRMED', 'CANCELLED', 'COMPLETED', 'NOSHOW']);
|
||||
|
||||
export const recurringGroupStatusSchema = z.enum(['ACTIVE', 'CANCELLED']);
|
||||
|
||||
export const adminBookingSportSchema = z.object({
|
||||
id: z.uuid(),
|
||||
name: z.string(),
|
||||
slug: z.string(),
|
||||
})
|
||||
});
|
||||
|
||||
export const adminBookingSchema = z.object({
|
||||
id: z.uuid(),
|
||||
@@ -28,17 +30,18 @@ export const adminBookingSchema = z.object({
|
||||
customerEmail: z.string(),
|
||||
price: z.number().nonnegative(),
|
||||
status: bookingStatusSchema,
|
||||
recurringGroupId: z.string().uuid().nullable().optional(),
|
||||
createdAt: z.string().datetime(),
|
||||
updatedAt: z.string().datetime(),
|
||||
})
|
||||
});
|
||||
|
||||
export const listAdminBookingsQuerySchema = z.object({
|
||||
fromDate: z.string().regex(ISO_DATE_REGEX, 'La fecha debe tener formato YYYY-MM-DD.'),
|
||||
})
|
||||
});
|
||||
|
||||
export const listAdminBookingsResponseSchema = z.object({
|
||||
bookings: z.array(adminBookingSchema),
|
||||
})
|
||||
});
|
||||
|
||||
export const createAdminBookingSchema = z.object({
|
||||
date: z.string().regex(ISO_DATE_REGEX, 'La fecha debe tener formato YYYY-MM-DD.'),
|
||||
@@ -54,20 +57,46 @@ export const createAdminBookingSchema = 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.')
|
||||
.or(z.literal('')),
|
||||
})
|
||||
customerEmail: z.string().email('El email ingresado no es valido.').or(z.literal('')),
|
||||
});
|
||||
|
||||
export const updateAdminBookingStatusSchema = z.object({
|
||||
status: z.enum(['CANCELLED', 'COMPLETED', 'NOSHOW']),
|
||||
})
|
||||
});
|
||||
|
||||
export type BookingStatus = z.infer<typeof bookingStatusSchema>
|
||||
export type AdminBookingSport = z.infer<typeof adminBookingSportSchema>
|
||||
export type AdminBooking = z.infer<typeof adminBookingSchema>
|
||||
export type ListAdminBookingsQuery = z.infer<typeof listAdminBookingsQuerySchema>
|
||||
export type ListAdminBookingsResponse = z.infer<typeof listAdminBookingsResponseSchema>
|
||||
export type CreateAdminBookingInput = z.infer<typeof createAdminBookingSchema>
|
||||
export type UpdateAdminBookingStatusInput = z.infer<typeof updateAdminBookingStatusSchema>
|
||||
export const createRecurringBookingSchema = createAdminBookingSchema.extend({
|
||||
isRecurring: z.literal(true),
|
||||
recurringEndDate: z
|
||||
.string()
|
||||
.regex(ISO_DATE_REGEX, 'La fecha debe tener formato YYYY-MM-DD.')
|
||||
.optional(),
|
||||
});
|
||||
|
||||
export const recurringBookingGroupSchema = z.object({
|
||||
id: z.uuid(),
|
||||
complexId: z.uuid(),
|
||||
courtId: z.uuid(),
|
||||
startTime: z.string().regex(TIME_REGEX),
|
||||
endTime: z.string().regex(TIME_REGEX),
|
||||
dayOfWeek: z.enum(['MONDAY', 'TUESDAY', 'WEDNESDAY', 'THURSDAY', 'FRIDAY', 'SATURDAY', 'SUNDAY']),
|
||||
startDate: z.string().regex(ISO_DATE_REGEX),
|
||||
endDate: z.string().regex(ISO_DATE_REGEX).nullable(),
|
||||
status: recurringGroupStatusSchema,
|
||||
customerName: z.string(),
|
||||
customerPhone: z.string(),
|
||||
customerEmail: z.string(),
|
||||
bookings: z.array(adminBookingSchema),
|
||||
createdAt: z.string().datetime(),
|
||||
updatedAt: z.string().datetime(),
|
||||
});
|
||||
|
||||
export type BookingStatus = z.infer<typeof bookingStatusSchema>;
|
||||
export type RecurringGroupStatus = z.infer<typeof recurringGroupStatusSchema>;
|
||||
export type AdminBookingSport = z.infer<typeof adminBookingSportSchema>;
|
||||
export type AdminBooking = z.infer<typeof adminBookingSchema>;
|
||||
export type ListAdminBookingsQuery = z.infer<typeof listAdminBookingsQuerySchema>;
|
||||
export type ListAdminBookingsResponse = z.infer<typeof listAdminBookingsResponseSchema>;
|
||||
export type CreateAdminBookingInput = z.infer<typeof createAdminBookingSchema>;
|
||||
export type CreateRecurringBookingInput = z.infer<typeof createRecurringBookingSchema>;
|
||||
export type RecurringBookingGroup = z.infer<typeof recurringBookingGroupSchema>;
|
||||
export type UpdateAdminBookingStatusInput = z.infer<typeof updateAdminBookingStatusSchema>;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { z } from 'zod'
|
||||
import { z } from 'zod';
|
||||
|
||||
export const adminPaymentStatusSchema = z.enum(['active', 'no_plan', 'expired'])
|
||||
export const adminPaymentStatusSchema = z.enum(['active', 'no_plan', 'expired']);
|
||||
|
||||
export const adminComplexListItemSchema = z.object({
|
||||
id: z.string(),
|
||||
@@ -13,7 +13,7 @@ export const adminComplexListItemSchema = z.object({
|
||||
courtCount: z.number().int().nonnegative(),
|
||||
avgBookingsPerDay: z.number().nonnegative(),
|
||||
paymentStatus: adminPaymentStatusSchema,
|
||||
})
|
||||
});
|
||||
|
||||
export const adminComplexStatsSchema = z.object({
|
||||
id: z.string(),
|
||||
@@ -34,20 +34,20 @@ export const adminComplexStatsSchema = z.object({
|
||||
noshow: z.number().int().nonnegative(),
|
||||
}),
|
||||
paymentStatus: adminPaymentStatusSchema,
|
||||
})
|
||||
});
|
||||
|
||||
export const adminCreatePlanSchema = z.object({
|
||||
code: z.string().trim().min(1).max(10),
|
||||
name: z.string().trim().min(1).max(30),
|
||||
price: z.number().nonnegative(),
|
||||
rules: z.any(),
|
||||
})
|
||||
});
|
||||
|
||||
export const adminUpdatePlanSchema = z.object({
|
||||
name: z.string().trim().min(1).max(30).optional(),
|
||||
price: z.number().nonnegative().optional(),
|
||||
rules: z.any().optional(),
|
||||
})
|
||||
});
|
||||
|
||||
export const adminUserSchema = z.object({
|
||||
id: z.string(),
|
||||
@@ -60,18 +60,18 @@ export const adminUserSchema = z.object({
|
||||
createdAt: z.string().datetime(),
|
||||
complexCount: z.number().int().nonnegative(),
|
||||
activeSessions: z.number().int().nonnegative(),
|
||||
})
|
||||
});
|
||||
|
||||
export const adminBlockUserSchema = z.object({
|
||||
banReason: z.string().max(500).optional(),
|
||||
})
|
||||
});
|
||||
|
||||
export const adminGlobalStatsSchema = z.object({
|
||||
totalComplexes: z.number().int().nonnegative(),
|
||||
totalUsers: z.number().int().nonnegative(),
|
||||
totalCourts: z.number().int().nonnegative(),
|
||||
totalBookings: z.number().int().nonnegative(),
|
||||
})
|
||||
});
|
||||
|
||||
export const adminUserSessionSchema = z.object({
|
||||
id: z.string(),
|
||||
@@ -82,35 +82,35 @@ export const adminUserSessionSchema = z.object({
|
||||
city: z.string().nullable(),
|
||||
country: z.string().nullable(),
|
||||
countryCode: z.string().nullable(),
|
||||
})
|
||||
});
|
||||
|
||||
export type AdminComplexListItem = z.infer<typeof adminComplexListItemSchema>
|
||||
export type AdminComplexStats = z.infer<typeof adminComplexStatsSchema>
|
||||
export type AdminCreatePlanInput = z.infer<typeof adminCreatePlanSchema>
|
||||
export type AdminUpdatePlanInput = z.infer<typeof adminUpdatePlanSchema>
|
||||
export type AdminUser = z.infer<typeof adminUserSchema>
|
||||
export type AdminBlockUserInput = z.infer<typeof adminBlockUserSchema>
|
||||
export type AdminGlobalStats = z.infer<typeof adminGlobalStatsSchema>
|
||||
export type AdminPaymentStatus = z.infer<typeof adminPaymentStatusSchema>
|
||||
export type AdminUserSession = z.infer<typeof adminUserSessionSchema>
|
||||
export type AdminComplexListItem = z.infer<typeof adminComplexListItemSchema>;
|
||||
export type AdminComplexStats = z.infer<typeof adminComplexStatsSchema>;
|
||||
export type AdminCreatePlanInput = z.infer<typeof adminCreatePlanSchema>;
|
||||
export type AdminUpdatePlanInput = z.infer<typeof adminUpdatePlanSchema>;
|
||||
export type AdminUser = z.infer<typeof adminUserSchema>;
|
||||
export type AdminBlockUserInput = z.infer<typeof adminBlockUserSchema>;
|
||||
export type AdminGlobalStats = z.infer<typeof adminGlobalStatsSchema>;
|
||||
export type AdminPaymentStatus = z.infer<typeof adminPaymentStatusSchema>;
|
||||
export type AdminUserSession = z.infer<typeof adminUserSessionSchema>;
|
||||
|
||||
export const adminGeoCitySchema = z.object({
|
||||
city: z.string(),
|
||||
userCount: z.number().int().nonnegative(),
|
||||
})
|
||||
});
|
||||
|
||||
export const adminGeoCountrySchema = z.object({
|
||||
country: z.string(),
|
||||
countryCode: z.string(),
|
||||
totalUsers: z.number().int().nonnegative(),
|
||||
cities: z.array(adminGeoCitySchema),
|
||||
})
|
||||
});
|
||||
|
||||
export const adminGeoStatsSchema = z.object({
|
||||
countries: z.array(adminGeoCountrySchema),
|
||||
totalUniqueCountries: z.number().int().nonnegative(),
|
||||
})
|
||||
});
|
||||
|
||||
export type AdminGeoCity = z.infer<typeof adminGeoCitySchema>
|
||||
export type AdminGeoCountry = z.infer<typeof adminGeoCountrySchema>
|
||||
export type AdminGeoStats = z.infer<typeof adminGeoStatsSchema>
|
||||
export type AdminGeoCity = z.infer<typeof adminGeoCitySchema>;
|
||||
export type AdminGeoCountry = z.infer<typeof adminGeoCountrySchema>;
|
||||
export type AdminGeoStats = z.infer<typeof adminGeoStatsSchema>;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { z } from 'zod'
|
||||
import { z } from 'zod';
|
||||
import { planFeatureFlagsSchema } from './plan';
|
||||
|
||||
const dayOfWeekEnum = z.enum([
|
||||
'MONDAY',
|
||||
@@ -8,7 +9,7 @@ const dayOfWeekEnum = z.enum([
|
||||
'FRIDAY',
|
||||
'SATURDAY',
|
||||
'SUNDAY',
|
||||
])
|
||||
]);
|
||||
|
||||
export const complexSchema = z.object({
|
||||
id: z.uuid(),
|
||||
@@ -22,7 +23,7 @@ export const complexSchema = z.object({
|
||||
planCode: z.string().max(10).nullable(),
|
||||
createdAt: z.string().datetime(),
|
||||
updatedAt: z.string().datetime(),
|
||||
})
|
||||
});
|
||||
|
||||
export const createComplexSchema = z
|
||||
.object({
|
||||
@@ -36,21 +37,13 @@ export const createComplexSchema = z
|
||||
.trim()
|
||||
.min(5, 'La direccion debe tener al menos 5 caracteres.')
|
||||
.max(200, 'La direccion no puede superar los 200 caracteres.'),
|
||||
city: z
|
||||
.string()
|
||||
.trim()
|
||||
.max(100, 'La ciudad no puede superar los 100 caracteres.')
|
||||
.optional(),
|
||||
city: z.string().trim().max(100, 'La ciudad no puede superar los 100 caracteres.').optional(),
|
||||
state: z
|
||||
.string()
|
||||
.trim()
|
||||
.max(100, 'La provincia/estado no puede superar los 100 caracteres.')
|
||||
.optional(),
|
||||
country: z
|
||||
.string()
|
||||
.trim()
|
||||
.max(100, 'El país no puede superar los 100 caracteres.')
|
||||
.optional(),
|
||||
country: z.string().trim().max(100, 'El país no puede superar los 100 caracteres.').optional(),
|
||||
planCode: z
|
||||
.string()
|
||||
.trim()
|
||||
@@ -70,31 +63,31 @@ export const createComplexSchema = z
|
||||
code: z.ZodIssueCode.custom,
|
||||
path: ['courtSportId'],
|
||||
message: 'Debes seleccionar un deporte.',
|
||||
})
|
||||
});
|
||||
}
|
||||
if (!data.courtStartTime || !/^\d{2}:\d{2}$/.test(data.courtStartTime)) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
path: ['courtStartTime'],
|
||||
message: 'La hora de inicio debe tener formato HH:mm.',
|
||||
})
|
||||
});
|
||||
}
|
||||
if (!data.courtEndTime || !/^\d{2}:\d{2}$/.test(data.courtEndTime)) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
path: ['courtEndTime'],
|
||||
message: 'La hora de fin debe tener formato HH:mm.',
|
||||
})
|
||||
});
|
||||
}
|
||||
if (!data.courtDaysOfWeek || data.courtDaysOfWeek.length === 0) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
path: ['courtDaysOfWeek'],
|
||||
message: 'Debes seleccionar al menos un día.',
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
export const updateComplexSchema = z
|
||||
.object({
|
||||
@@ -154,16 +147,16 @@ export const updateComplexSchema = z
|
||||
payload.country !== undefined ||
|
||||
payload.complexSlug ||
|
||||
payload.adminEmail ||
|
||||
payload.planCode !== undefined,
|
||||
payload.planCode !== undefined
|
||||
),
|
||||
{
|
||||
message: 'Debes enviar al menos un campo para actualizar.',
|
||||
},
|
||||
)
|
||||
}
|
||||
);
|
||||
|
||||
export const complexUserRoleSchema = z.enum(['ADMIN', 'EMPLOYEE'])
|
||||
export const complexUserRoleSchema = z.enum(['ADMIN', 'EMPLOYEE']);
|
||||
|
||||
export type ComplexUserRole = z.infer<typeof complexUserRoleSchema>
|
||||
export type ComplexUserRole = z.infer<typeof complexUserRoleSchema>;
|
||||
|
||||
export const complexMemberSchema = z.object({
|
||||
userId: z.string(),
|
||||
@@ -172,13 +165,13 @@ export const complexMemberSchema = z.object({
|
||||
avatarUrl: z.url(),
|
||||
role: complexUserRoleSchema,
|
||||
createdAt: z.string().datetime(),
|
||||
})
|
||||
});
|
||||
|
||||
export type ComplexMember = z.infer<typeof complexMemberSchema>
|
||||
export type ComplexMember = z.infer<typeof complexMemberSchema>;
|
||||
|
||||
export const complexInvitationStatusSchema = z.enum(['PENDING', 'ACCEPTED', 'REVOKED', 'EXPIRED'])
|
||||
export const complexInvitationStatusSchema = z.enum(['PENDING', 'ACCEPTED', 'REVOKED', 'EXPIRED']);
|
||||
|
||||
export type ComplexInvitationStatus = z.infer<typeof complexInvitationStatusSchema>
|
||||
export type ComplexInvitationStatus = z.infer<typeof complexInvitationStatusSchema>;
|
||||
|
||||
export const complexInvitationSchema = z.object({
|
||||
id: z.uuid(),
|
||||
@@ -189,62 +182,63 @@ export const complexInvitationSchema = z.object({
|
||||
expiresAt: z.string().datetime(),
|
||||
acceptedAt: z.string().datetime().nullable(),
|
||||
revokedAt: z.string().datetime().nullable(),
|
||||
})
|
||||
});
|
||||
|
||||
export type ComplexInvitation = z.infer<typeof complexInvitationSchema>
|
||||
export type ComplexInvitation = z.infer<typeof complexInvitationSchema>;
|
||||
|
||||
export const complexUsersOverviewSchema = z.object({
|
||||
members: z.array(complexMemberSchema),
|
||||
invitations: z.array(complexInvitationSchema),
|
||||
})
|
||||
});
|
||||
|
||||
export type ComplexUsersOverview = z.infer<typeof complexUsersOverviewSchema>
|
||||
export type ComplexUsersOverview = z.infer<typeof complexUsersOverviewSchema>;
|
||||
|
||||
export const inviteComplexUserSchema = z.object({
|
||||
email: z.email('El email debe ser válido.'),
|
||||
})
|
||||
});
|
||||
|
||||
export type InviteComplexUserInput = z.infer<typeof inviteComplexUserSchema>
|
||||
export type InviteComplexUserInput = z.infer<typeof inviteComplexUserSchema>;
|
||||
|
||||
export const acceptComplexInvitationsSchema = z.object({
|
||||
inviteToken: z.string().min(1, 'El token de invitación no puede estar vacío.').optional(),
|
||||
})
|
||||
});
|
||||
|
||||
export type AcceptComplexInvitationsInput = z.infer<typeof acceptComplexInvitationsSchema>
|
||||
export type AcceptComplexInvitationsInput = z.infer<typeof acceptComplexInvitationsSchema>;
|
||||
|
||||
export const revokeComplexUserSchema = z.object({
|
||||
userId: z.string().min(1, 'El userId no puede estar vacío.'),
|
||||
})
|
||||
});
|
||||
|
||||
export type RevokeComplexUserInput = z.infer<typeof revokeComplexUserSchema>
|
||||
export type RevokeComplexUserInput = z.infer<typeof revokeComplexUserSchema>;
|
||||
|
||||
export const selectComplexSchema = z.object({
|
||||
complexId: z.string().uuid(),
|
||||
})
|
||||
});
|
||||
|
||||
export type SelectComplexInput = z.infer<typeof selectComplexSchema>
|
||||
export type SelectComplexInput = z.infer<typeof selectComplexSchema>;
|
||||
|
||||
export const complexWithRoleSchema = complexSchema.merge(
|
||||
z.object({
|
||||
role: complexUserRoleSchema,
|
||||
planFeatures: planFeatureFlagsSchema.nullable(),
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
export type ComplexWithRole = z.infer<typeof complexWithRoleSchema>
|
||||
export type ComplexWithRole = z.infer<typeof complexWithRoleSchema>;
|
||||
|
||||
export type Complex = z.infer<typeof complexSchema>
|
||||
export type CreateComplexInput = z.infer<typeof createComplexSchema>
|
||||
export type UpdateComplexInput = z.infer<typeof updateComplexSchema>
|
||||
export type CreateComplexPayload = CreateComplexInput
|
||||
export type CreateComplexResponse = Complex
|
||||
export type UpdateComplexPayload = UpdateComplexInput
|
||||
export type UpdateComplexResponse = Complex
|
||||
export type Complex = z.infer<typeof complexSchema>;
|
||||
export type CreateComplexInput = z.infer<typeof createComplexSchema>;
|
||||
export type UpdateComplexInput = z.infer<typeof updateComplexSchema>;
|
||||
export type CreateComplexPayload = CreateComplexInput;
|
||||
export type CreateComplexResponse = Complex;
|
||||
export type UpdateComplexPayload = UpdateComplexInput;
|
||||
export type UpdateComplexResponse = Complex;
|
||||
export type InviteComplexUserResponse = {
|
||||
invitation: ComplexInvitation
|
||||
}
|
||||
invitation: ComplexInvitation;
|
||||
};
|
||||
export type AcceptComplexInvitationsResponse = {
|
||||
acceptedCount: number
|
||||
}
|
||||
acceptedCount: number;
|
||||
};
|
||||
export type CancelComplexInvitationResponse = {
|
||||
ok: boolean
|
||||
}
|
||||
ok: boolean;
|
||||
};
|
||||
|
||||
@@ -1,65 +1,65 @@
|
||||
import { z } from 'zod'
|
||||
import { z } from 'zod';
|
||||
|
||||
const TIME_REGEX = /^([01]\d|2[0-3]):([0-5]\d)$/
|
||||
const TIME_REGEX = /^([01]\d|2[0-3]):([0-5]\d)$/;
|
||||
|
||||
function toMinutes(value: string): number {
|
||||
const [hours, minutes] = value.split(':').map((part) => Number(part))
|
||||
return hours * 60 + minutes
|
||||
const [hours, minutes] = value.split(':').map((part) => Number(part));
|
||||
return hours * 60 + minutes;
|
||||
}
|
||||
|
||||
function hasOverlaps(
|
||||
availability: Array<{
|
||||
dayOfWeek: DayOfWeek
|
||||
startTime: string
|
||||
endTime: string
|
||||
}>,
|
||||
dayOfWeek: DayOfWeek;
|
||||
startTime: string;
|
||||
endTime: string;
|
||||
}>
|
||||
): boolean {
|
||||
const grouped = new Map<
|
||||
DayOfWeek,
|
||||
Array<{
|
||||
start: number
|
||||
end: number
|
||||
start: number;
|
||||
end: number;
|
||||
}>
|
||||
>()
|
||||
>();
|
||||
|
||||
for (const range of availability) {
|
||||
const start = toMinutes(range.startTime)
|
||||
const end = toMinutes(range.endTime)
|
||||
const current = grouped.get(range.dayOfWeek) ?? []
|
||||
current.push({ start, end })
|
||||
grouped.set(range.dayOfWeek, current)
|
||||
const start = toMinutes(range.startTime);
|
||||
const end = toMinutes(range.endTime);
|
||||
const current = grouped.get(range.dayOfWeek) ?? [];
|
||||
current.push({ start, end });
|
||||
grouped.set(range.dayOfWeek, current);
|
||||
}
|
||||
|
||||
for (const ranges of grouped.values()) {
|
||||
ranges.sort((a, b) => a.start - b.start)
|
||||
ranges.sort((a, b) => a.start - b.start);
|
||||
|
||||
for (let index = 1; index < ranges.length; index += 1) {
|
||||
const previous = ranges[index - 1]
|
||||
const current = ranges[index]
|
||||
const previous = ranges[index - 1];
|
||||
const current = ranges[index];
|
||||
|
||||
if (previous.end > current.start) {
|
||||
return true
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
return false;
|
||||
}
|
||||
|
||||
function refineAvailability(
|
||||
availability: Array<{
|
||||
dayOfWeek: DayOfWeek
|
||||
startTime: string
|
||||
endTime: string
|
||||
dayOfWeek: DayOfWeek;
|
||||
startTime: string;
|
||||
endTime: string;
|
||||
}>,
|
||||
context: z.RefinementCtx,
|
||||
context: z.RefinementCtx
|
||||
) {
|
||||
for (const range of availability) {
|
||||
if (toMinutes(range.startTime) >= toMinutes(range.endTime)) {
|
||||
context.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: `El rango ${range.startTime}-${range.endTime} es invalido. La hora de inicio debe ser menor a la de fin.`,
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,7 +67,7 @@ function refineAvailability(
|
||||
context.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: 'Hay rangos horarios superpuestos para el mismo dia.',
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,22 +79,22 @@ export const dayOfWeekSchema = z.enum([
|
||||
'FRIDAY',
|
||||
'SATURDAY',
|
||||
'SUNDAY',
|
||||
])
|
||||
]);
|
||||
|
||||
export type DayOfWeek = z.infer<typeof dayOfWeekSchema>
|
||||
export type DayOfWeek = z.infer<typeof dayOfWeekSchema>;
|
||||
|
||||
export const courtAvailabilitySchema = z.object({
|
||||
id: z.uuid(),
|
||||
dayOfWeek: dayOfWeekSchema,
|
||||
startTime: z.string().regex(TIME_REGEX, 'La hora debe tener formato HH:mm.'),
|
||||
endTime: z.string().regex(TIME_REGEX, 'La hora debe tener formato HH:mm.'),
|
||||
})
|
||||
});
|
||||
|
||||
export const courtAvailabilityInputSchema = z.object({
|
||||
dayOfWeek: dayOfWeekSchema,
|
||||
startTime: z.string().regex(TIME_REGEX, 'La hora debe tener formato HH:mm.'),
|
||||
endTime: z.string().regex(TIME_REGEX, 'La hora debe tener formato HH:mm.'),
|
||||
})
|
||||
});
|
||||
|
||||
export const courtPriceRuleSchema = z.object({
|
||||
id: z.uuid(),
|
||||
@@ -103,13 +103,13 @@ export const courtPriceRuleSchema = z.object({
|
||||
endTime: z.string().nullable(),
|
||||
price: z.number().nonnegative(),
|
||||
isActive: z.boolean(),
|
||||
})
|
||||
});
|
||||
|
||||
export const courtSportSchema = z.object({
|
||||
id: z.uuid(),
|
||||
name: z.string(),
|
||||
slug: z.string(),
|
||||
})
|
||||
});
|
||||
|
||||
export const courtSchema = z.object({
|
||||
id: z.uuid(),
|
||||
@@ -124,7 +124,7 @@ export const courtSchema = z.object({
|
||||
hasCustomPricing: z.boolean(),
|
||||
createdAt: z.string().datetime(),
|
||||
updatedAt: z.string().datetime(),
|
||||
})
|
||||
});
|
||||
|
||||
export const createCourtSchema = z
|
||||
.object({
|
||||
@@ -139,9 +139,11 @@ export const createCourtSchema = z
|
||||
.min(15, 'La duracion minima por turno es 15 minutos.')
|
||||
.max(480, 'La duracion maxima por turno es 480 minutos.'),
|
||||
basePrice: z.number().nonnegative('El precio base no puede ser negativo.'),
|
||||
availability: z.array(courtAvailabilityInputSchema).min(1, 'Debes definir al menos un rango horario.'),
|
||||
availability: z
|
||||
.array(courtAvailabilityInputSchema)
|
||||
.min(1, 'Debes definir al menos un rango horario.'),
|
||||
})
|
||||
.superRefine((payload, context) => refineAvailability(payload.availability, context))
|
||||
.superRefine((payload, context) => refineAvailability(payload.availability, context));
|
||||
|
||||
export const updateCourtSchema = z
|
||||
.object({
|
||||
@@ -158,7 +160,10 @@ export const updateCourtSchema = z
|
||||
.max(480, 'La duracion maxima por turno es 480 minutos.')
|
||||
.optional(),
|
||||
basePrice: z.number().nonnegative('El precio base no puede ser negativo.').optional(),
|
||||
availability: z.array(courtAvailabilityInputSchema).min(1, 'Debes definir al menos un rango horario.').optional(),
|
||||
availability: z
|
||||
.array(courtAvailabilityInputSchema)
|
||||
.min(1, 'Debes definir al menos un rango horario.')
|
||||
.optional(),
|
||||
})
|
||||
.superRefine((payload, context) => {
|
||||
if (
|
||||
@@ -171,17 +176,17 @@ export const updateCourtSchema = z
|
||||
context.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: 'Debes enviar al menos un campo para actualizar.',
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
if (payload.availability) {
|
||||
refineAvailability(payload.availability, context)
|
||||
refineAvailability(payload.availability, context);
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
export type Court = z.infer<typeof courtSchema>
|
||||
export type CourtAvailability = z.infer<typeof courtAvailabilitySchema>
|
||||
export type CourtAvailabilityInput = z.infer<typeof courtAvailabilityInputSchema>
|
||||
export type CourtPriceRule = z.infer<typeof courtPriceRuleSchema>
|
||||
export type CreateCourtInput = z.infer<typeof createCourtSchema>
|
||||
export type UpdateCourtInput = z.infer<typeof updateCourtSchema>
|
||||
export type Court = z.infer<typeof courtSchema>;
|
||||
export type CourtAvailability = z.infer<typeof courtAvailabilitySchema>;
|
||||
export type CourtAvailabilityInput = z.infer<typeof courtAvailabilityInputSchema>;
|
||||
export type CourtPriceRule = z.infer<typeof courtPriceRuleSchema>;
|
||||
export type CreateCourtInput = z.infer<typeof createCourtSchema>;
|
||||
export type UpdateCourtInput = z.infer<typeof updateCourtSchema>;
|
||||
|
||||
@@ -6,7 +6,7 @@ export {
|
||||
planSummarySchema,
|
||||
planWithFeaturesSchema,
|
||||
planLimitsSchema,
|
||||
} from './plan'
|
||||
} from './plan';
|
||||
export type {
|
||||
Plan,
|
||||
PlanFeatureFlags,
|
||||
@@ -15,7 +15,7 @@ export type {
|
||||
PlanSummary,
|
||||
PlanWithFeatures,
|
||||
PlanLimits,
|
||||
} from './plan'
|
||||
} from './plan';
|
||||
export {
|
||||
acceptComplexInvitationsSchema,
|
||||
complexInvitationSchema,
|
||||
@@ -30,7 +30,7 @@ export {
|
||||
revokeComplexUserSchema,
|
||||
selectComplexSchema,
|
||||
updateComplexSchema,
|
||||
} from './complex'
|
||||
} from './complex';
|
||||
export type {
|
||||
AcceptComplexInvitationsInput,
|
||||
AcceptComplexInvitationsResponse,
|
||||
@@ -52,7 +52,7 @@ export type {
|
||||
UpdateComplexPayload,
|
||||
UpdateComplexResponse,
|
||||
RevokeComplexUserInput,
|
||||
} from './complex'
|
||||
} from './complex';
|
||||
export {
|
||||
dayOfWeekSchema,
|
||||
courtAvailabilityInputSchema,
|
||||
@@ -61,7 +61,7 @@ export {
|
||||
courtSchema,
|
||||
createCourtSchema,
|
||||
updateCourtSchema,
|
||||
} from './court'
|
||||
} from './court';
|
||||
export type {
|
||||
DayOfWeek,
|
||||
Court,
|
||||
@@ -70,25 +70,31 @@ export type {
|
||||
CourtPriceRule,
|
||||
CreateCourtInput,
|
||||
UpdateCourtInput,
|
||||
} from './court'
|
||||
} from './court';
|
||||
export {
|
||||
adminBookingSchema,
|
||||
adminBookingSportSchema,
|
||||
bookingStatusSchema,
|
||||
createAdminBookingSchema,
|
||||
createRecurringBookingSchema,
|
||||
listAdminBookingsQuerySchema,
|
||||
listAdminBookingsResponseSchema,
|
||||
recurringBookingGroupSchema,
|
||||
recurringGroupStatusSchema,
|
||||
updateAdminBookingStatusSchema,
|
||||
} from './admin-booking'
|
||||
} from './admin-booking';
|
||||
export type {
|
||||
AdminBooking,
|
||||
AdminBookingSport,
|
||||
BookingStatus,
|
||||
CreateAdminBookingInput,
|
||||
CreateRecurringBookingInput,
|
||||
ListAdminBookingsQuery,
|
||||
ListAdminBookingsResponse,
|
||||
RecurringBookingGroup,
|
||||
RecurringGroupStatus,
|
||||
UpdateAdminBookingStatusInput,
|
||||
} from './admin-booking'
|
||||
} from './admin-booking';
|
||||
export {
|
||||
createPublicBookingSchema,
|
||||
publicAvailabilityCourtSchema,
|
||||
@@ -99,7 +105,7 @@ export {
|
||||
publicBookingSlotSchema,
|
||||
publicBookingSportSchema,
|
||||
cancelPublicBookingSchema,
|
||||
} from './public-booking'
|
||||
} from './public-booking';
|
||||
export type {
|
||||
CreatePublicBookingInput,
|
||||
CancelPublicBookingInput,
|
||||
@@ -110,15 +116,15 @@ export type {
|
||||
PublicBookingConfirmation,
|
||||
PublicBookingSlot,
|
||||
PublicBookingSport,
|
||||
} from './public-booking'
|
||||
export { sportSchema, createSportSchema, updateSportSchema } from './sport'
|
||||
export type { Sport, CreateSportInput, UpdateSportInput } from './sport'
|
||||
} from './public-booking';
|
||||
export { sportSchema, createSportSchema, updateSportSchema } from './sport';
|
||||
export type { Sport, CreateSportInput, UpdateSportInput } from './sport';
|
||||
export {
|
||||
userProfileSchema,
|
||||
passwordResetStartSchema,
|
||||
passwordResetVerifySchema,
|
||||
passwordResetResendSchema,
|
||||
} from './user'
|
||||
} from './user';
|
||||
export type {
|
||||
UserProfile,
|
||||
UserProfileResponse,
|
||||
@@ -128,7 +134,7 @@ export type {
|
||||
PasswordResetStartResponse,
|
||||
PasswordResetVerifyResponse,
|
||||
PasswordResetResendResponse,
|
||||
} from './user'
|
||||
} from './user';
|
||||
export {
|
||||
adminComplexListItemSchema,
|
||||
adminComplexStatsSchema,
|
||||
@@ -142,7 +148,7 @@ export {
|
||||
adminGeoCitySchema,
|
||||
adminGeoCountrySchema,
|
||||
adminGeoStatsSchema,
|
||||
} from './admin'
|
||||
} from './admin';
|
||||
export type {
|
||||
AdminComplexListItem,
|
||||
AdminComplexStats,
|
||||
@@ -156,4 +162,4 @@ export type {
|
||||
AdminGeoCity,
|
||||
AdminGeoCountry,
|
||||
AdminGeoStats,
|
||||
} from './admin'
|
||||
} from './admin';
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
import { z } from 'zod'
|
||||
import { z } from 'zod';
|
||||
|
||||
const nonNegativeInt = z.int().nonnegative()
|
||||
const positiveInt = z.int().positive()
|
||||
const nonNegativeInt = z.int().nonnegative();
|
||||
const positiveInt = z.int().positive();
|
||||
|
||||
export const planFeatureFlagsSchema = z.object({
|
||||
onlinePayments: z.boolean().default(false),
|
||||
publicBookingPage: z.boolean().default(false),
|
||||
advancedReports: z.boolean().default(false),
|
||||
whatsappReminders: z.boolean().default(false),
|
||||
})
|
||||
fixedSlots: z.boolean().default(false),
|
||||
});
|
||||
|
||||
export const planRulesV1Schema = z.object({
|
||||
version: z.literal('v1'),
|
||||
@@ -31,13 +32,14 @@ export const planRulesV1Schema = z.object({
|
||||
publicBookingPage: false,
|
||||
advancedReports: false,
|
||||
whatsappReminders: false,
|
||||
fixedSlots: false,
|
||||
}),
|
||||
})
|
||||
});
|
||||
|
||||
export const planPriceOverrideSchema = z.object({
|
||||
amount: z.number().nonnegative(),
|
||||
currency: z.string().length(3),
|
||||
})
|
||||
});
|
||||
|
||||
export const planRulesV2Schema = planRulesV1Schema.extend({
|
||||
version: z.literal('v2'),
|
||||
@@ -46,12 +48,12 @@ export const planRulesV2Schema = planRulesV1Schema.extend({
|
||||
overrides: z.record(z.string(), planPriceOverrideSchema),
|
||||
})
|
||||
.optional(),
|
||||
})
|
||||
});
|
||||
|
||||
export const planRulesSchema = z.discriminatedUnion('version', [
|
||||
planRulesV1Schema,
|
||||
planRulesV2Schema,
|
||||
])
|
||||
]);
|
||||
|
||||
export const planSchema = z.object({
|
||||
code: z.string().trim().min(1).max(10),
|
||||
@@ -59,13 +61,13 @@ export const planSchema = z.object({
|
||||
price: z.number().nonnegative(),
|
||||
lastUpdatedAt: z.string().datetime(),
|
||||
rules: planRulesSchema,
|
||||
})
|
||||
});
|
||||
|
||||
export const planSummarySchema = z.object({
|
||||
code: z.string().trim().min(1).max(10),
|
||||
name: z.string().trim().min(1).max(30),
|
||||
price: z.number().nonnegative(),
|
||||
})
|
||||
});
|
||||
|
||||
export const planLimitsSchema = z.object({
|
||||
maxCourts: positiveInt,
|
||||
@@ -73,7 +75,7 @@ export const planLimitsSchema = z.object({
|
||||
maxActiveUsers: positiveInt.optional(),
|
||||
maxConcurrentBookingsPerSlot: positiveInt.optional(),
|
||||
maxSports: positiveInt.optional(),
|
||||
})
|
||||
});
|
||||
|
||||
export const planWithFeaturesSchema = z.object({
|
||||
code: z.string().trim().min(1).max(10),
|
||||
@@ -82,13 +84,13 @@ export const planWithFeaturesSchema = z.object({
|
||||
currency: z.string().length(3),
|
||||
features: planFeatureFlagsSchema,
|
||||
limits: planLimitsSchema,
|
||||
})
|
||||
});
|
||||
|
||||
export type PlanFeatureFlags = z.infer<typeof planFeatureFlagsSchema>
|
||||
export type PlanRulesV1 = z.infer<typeof planRulesV1Schema>
|
||||
export type PlanRulesV2 = z.infer<typeof planRulesV2Schema>
|
||||
export type PlanRules = z.infer<typeof planRulesSchema>
|
||||
export type Plan = z.infer<typeof planSchema>
|
||||
export type PlanSummary = z.infer<typeof planSummarySchema>
|
||||
export type PlanWithFeatures = z.infer<typeof planWithFeaturesSchema>
|
||||
export type PlanLimits = z.infer<typeof planLimitsSchema>
|
||||
export type PlanFeatureFlags = z.infer<typeof planFeatureFlagsSchema>;
|
||||
export type PlanRulesV1 = z.infer<typeof planRulesV1Schema>;
|
||||
export type PlanRulesV2 = z.infer<typeof planRulesV2Schema>;
|
||||
export type PlanRules = z.infer<typeof planRulesSchema>;
|
||||
export type Plan = z.infer<typeof planSchema>;
|
||||
export type PlanSummary = z.infer<typeof planSummarySchema>;
|
||||
export type PlanWithFeatures = z.infer<typeof planWithFeaturesSchema>;
|
||||
export type PlanLimits = z.infer<typeof planLimitsSchema>;
|
||||
|
||||
@@ -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>;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { z } from 'zod'
|
||||
import { z } from 'zod';
|
||||
|
||||
export const sportSchema = z.object({
|
||||
id: z.uuid(),
|
||||
@@ -7,7 +7,7 @@ export const sportSchema = z.object({
|
||||
isActive: z.boolean(),
|
||||
createdAt: z.string().datetime(),
|
||||
updatedAt: z.string().datetime(),
|
||||
})
|
||||
});
|
||||
|
||||
export const createSportSchema = z.object({
|
||||
name: z
|
||||
@@ -15,7 +15,7 @@ export const createSportSchema = z.object({
|
||||
.trim()
|
||||
.min(2, 'El deporte debe tener al menos 2 caracteres.')
|
||||
.max(80, 'El deporte no puede superar los 80 caracteres.'),
|
||||
})
|
||||
});
|
||||
|
||||
export const updateSportSchema = z
|
||||
.object({
|
||||
@@ -29,8 +29,8 @@ export const updateSportSchema = z
|
||||
})
|
||||
.refine((payload) => payload.name !== undefined || payload.isActive !== undefined, {
|
||||
message: 'Debes enviar al menos un campo para actualizar.',
|
||||
})
|
||||
});
|
||||
|
||||
export type Sport = z.infer<typeof sportSchema>
|
||||
export type CreateSportInput = z.infer<typeof createSportSchema>
|
||||
export type UpdateSportInput = z.infer<typeof updateSportSchema>
|
||||
export type Sport = z.infer<typeof sportSchema>;
|
||||
export type CreateSportInput = z.infer<typeof createSportSchema>;
|
||||
export type UpdateSportInput = z.infer<typeof updateSportSchema>;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { z } from 'zod'
|
||||
import { z } from 'zod';
|
||||
|
||||
export const userProfileSchema = z.object({
|
||||
id: z.string(),
|
||||
@@ -7,30 +7,30 @@ export const userProfileSchema = z.object({
|
||||
role: z.string(),
|
||||
avatarUrl: z.url().nullable(),
|
||||
createdAt: z.string(),
|
||||
})
|
||||
});
|
||||
|
||||
export type UserProfile = z.infer<typeof userProfileSchema>
|
||||
export type UserProfileResponse = UserProfile
|
||||
export type UserProfile = z.infer<typeof userProfileSchema>;
|
||||
export type UserProfileResponse = UserProfile;
|
||||
|
||||
export const passwordResetStartSchema = z.object({
|
||||
email: z.string().email('Ingresá un email válido.'),
|
||||
})
|
||||
});
|
||||
|
||||
export type PasswordResetStartInput = z.infer<typeof passwordResetStartSchema>
|
||||
export type PasswordResetStartInput = z.infer<typeof passwordResetStartSchema>;
|
||||
|
||||
export const passwordResetVerifySchema = z.object({
|
||||
requestId: z.string().uuid('Request ID inválido.'),
|
||||
otp: z.string().length(6, 'El código debe tener 6 dígitos.'),
|
||||
newPassword: z.string().min(6, 'La contraseña debe tener al menos 6 caracteres.'),
|
||||
})
|
||||
});
|
||||
|
||||
export type PasswordResetVerifyInput = z.infer<typeof passwordResetVerifySchema>
|
||||
export type PasswordResetVerifyInput = z.infer<typeof passwordResetVerifySchema>;
|
||||
|
||||
export const passwordResetResendSchema = z.object({
|
||||
requestId: z.string().uuid('Request ID inválido.'),
|
||||
})
|
||||
});
|
||||
|
||||
export type PasswordResetResendInput = z.infer<typeof passwordResetResendSchema>
|
||||
export type PasswordResetResendInput = z.infer<typeof passwordResetResendSchema>;
|
||||
|
||||
export type PasswordResetStartResponse = {
|
||||
message: string;
|
||||
@@ -38,11 +38,11 @@ export type PasswordResetStartResponse = {
|
||||
email: string;
|
||||
expiresAt: string;
|
||||
cooldownSeconds: number;
|
||||
}
|
||||
};
|
||||
|
||||
export type PasswordResetVerifyResponse = {
|
||||
message: string;
|
||||
}
|
||||
};
|
||||
|
||||
export type PasswordResetResendResponse = {
|
||||
message: string;
|
||||
@@ -50,4 +50,4 @@ export type PasswordResetResendResponse = {
|
||||
email: string;
|
||||
expiresAt: string;
|
||||
cooldownSeconds: number;
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user