Reservas
Endpoints para crear y gestionar reservas de sesiones de clases.
Modelos de Datos
Section titled “Modelos de Datos”Los siguientes modelos de Prisma pertenecen al schema gym:
Reservation
Section titled “Reservation”Las reservaciones ahora se hacen sobre sesiones específicas (ClassSession), no sobre clases directamente.
model Reservation { id String @id @default(cuid()) userProfileId String @map("user_profile_id") classSessionId String @map("class_session_id") status ReservationStatus @default(CONFIRMED) notes String?
userProfile UserProfile @relation(fields: [userProfileId], references: [id], onDelete: Cascade) classSession ClassSession @relation(fields: [classSessionId], references: [id], onDelete: Restrict)
createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at")
@@unique([userProfileId, classSessionId]) @@map("reservations") @@schema("gym")}ReservationStatus (Enum)
Section titled “ReservationStatus (Enum)”enum ReservationStatus { CONFIRMED CANCELLED COMPLETED NO_SHOW
@@schema("gym")}Endpoints
Section titled “Endpoints”Obtener Reservas
Section titled “Obtener Reservas”Obtener las reservas del usuario actual o todas las reservas (Admin/Staff).
- URL:
/api/v1/reservations - Método:
GET - Autenticación Requerida: Sí
Descripción Interna
Section titled “Descripción Interna”Este endpoint obtiene reservas con filtrado:
-
Filtros disponibles:
userId: Filtrar por usuario (solo Admin/Staff pueden ver otros usuarios)classSessionId: Filtrar por sesión específicaclassId: Filtrar por clase (busca en las sesiones relacionadas)status: CONFIRMED, CANCELLED, COMPLETED, NO_SHOWdateFrom/dateTo: Rango de fechas de la sesión
-
Incluye:
- userProfile del reservante (id, name, avatar_url)
- classSession con información de la clase
-
Ordenado por fecha de creación descendente
-
Paginación estándar con page/limit
Parámetros de Consulta de Solicitud (Opcionales)
Section titled “Parámetros de Consulta de Solicitud (Opcionales)”status: Filtrar por status (CONFIRMED, CANCELLED, COMPLETED, NO_SHOW)dateFrom: Filtrar desde fecha (de la sesión)dateTo: Filtrar hasta fecha (de la sesión)userId: Filtrar por usuario Admin StaffclassSessionId: Filtrar por sesión específicaclassId: Filtrar por clasepage: Número de página (por defecto: 1)limit: Resultados por página (por defecto: 10)
Respuesta
Section titled “Respuesta”{ "data": [ { "id": "...", "status": "CONFIRMED", "notes": null, "classSession": { "id": "...", "sessionDate": "2023-10-27", "startTime": "09:00", "endTime": "10:00", "class": { "id": "...", "name": "Yoga", "description": "..." } }, "userProfile": { "id": "...", "name": "John Doe", "avatar_url": "..." } } ], "pagination": { "page": 1, "limit": 10, "total": 5, "totalPages": 1 }}Ejemplo de Cliente Hono
Section titled “Ejemplo de Cliente Hono”import { hcWithType } from '@vitality-gym/api/client'
const client = hcWithType('http://localhost:3000')
const res = await client.api.v1.reservations.$get({ query: { status: 'CONFIRMED', dateFrom: '2023-10-27', dateTo: '2023-11-03' }})Crear Reserva
Section titled “Crear Reserva”Crear una nueva reserva para una sesión de clase.
- URL:
/api/v1/reservations - Método:
POST - Autenticación Requerida: Sí
Descripción Interna
Section titled “Descripción Interna”Este endpoint crea una nueva reserva:
-
Validación completa (validateReservation):
- Membresía activa: Verifica que el usuario tenga membresía ACTIVE con fechas vigentes
- Sesión válida: Verifica que la sesión existe y tiene status SCHEDULED o IN_PROGRESS
- Fecha futura: Verifica que la sesión no haya pasado
- Capacidad disponible: Compara
currentBookingsconmaxCapacityde la sesión (o de la clase) - Sin duplicados: Verifica que no exista reserva activa del usuario para esa sesión
-
Si validación falla, retorna error 400 con razón específica:
- “No tienes una membresía activa”
- “La sesión no existe”
- “La sesión no está disponible”
- “La sesión ya ha pasado”
- “La sesión ha alcanzado su capacidad máxima”
- “Ya tienes una reserva para esta sesión”
-
En una transacción:
- Crea reserva con userProfileId, classSessionId y status: CONFIRMED
- Incrementa
currentBookingsde la sesión
-
Retorna reserva con userProfile y classSession incluidos
Cuerpo de Solicitud
Section titled “Cuerpo de Solicitud”{ "classSessionId": "session_id_uuid", "notes": "Notas opcionales"}Respuesta (Éxito)
Section titled “Respuesta (Éxito)”{ "message": "Reserva creada exitosamente", "data": { "id": "...", "status": "CONFIRMED", "notes": null, "classSession": { "id": "...", "sessionDate": "2023-10-27", "startTime": "09:00", "class": {...} }, "userProfile": {...} }}Ejemplo de Cliente Hono
Section titled “Ejemplo de Cliente Hono”import { hcWithType } from '@vitality-gym/api/client'
const client = hcWithType('http://localhost:3000')
const res = await client.api.v1.reservations.$post({ json: { classSessionId: 'session_id_uuid' }})Obtener Reserva por ID
Section titled “Obtener Reserva por ID”Obtener detalles de una reserva específica.
- URL:
/api/v1/reservations/:id - Método:
GET - Autenticación Requerida: Sí
Descripción Interna
Section titled “Descripción Interna”Este endpoint obtiene una reserva específica:
- Busca reserva por ID
- Incluye userProfile y classSession con clase
- Lanza 404 si no existe
Nota: Usuarios normales solo pueden ver sus propias reservas. Admin/Staff pueden ver cualquier reserva.
Respuesta
Section titled “Respuesta”{ "message": "Reserva obtenida exitosamente", "data": { "id": "...", "status": "CONFIRMED", "notes": null, "classSession": { "id": "...", "sessionDate": "2023-10-27", "startTime": "09:00", "endTime": "10:00", "class": { "id": "...", "name": "Yoga", "description": "..." } }, "userProfile": { "id": "...", "name": "John Doe", "avatar_url": "..." } }}Ejemplo de Cliente Hono
Section titled “Ejemplo de Cliente Hono”import { hcWithType } from '@vitality-gym/api/client'
const client = hcWithType('http://localhost:3000')
const res = await client.api.v1.reservations[':id'].$get({ param: { id: 'reservation_id' }})Actualizar Reserva (Admin/Staff)
Section titled “Actualizar Reserva (Admin/Staff)”Actualizar el status de una reserva.
- URL:
/api/v1/reservations/:id - Método:
PATCH - Autenticación Requerida: Sí Admin Staff
Descripción Interna
Section titled “Descripción Interna”Este endpoint actualiza el estado de una reserva:
-
Obtiene reserva existente con información de la sesión y clase
-
Valida transición de estado (isValidStatusTransition):
- Desde CONFIRMED: puede ir a CANCELLED, COMPLETED, NO_SHOW
- Desde CANCELLED: no puede cambiar (array vacío)
- Desde COMPLETED: no puede cambiar (array vacío)
- Desde NO_SHOW: solo puede ir a COMPLETED
-
Si la transición es inválida, lanza error 400 con mensaje explicativo
-
Si se cancela (CONFIRMED → CANCELLED):
- Decrementa
currentBookingsde la sesión
- Decrementa
-
Actualiza status y notes opcionales
-
Retorna reserva actualizada con userProfile y classSession
Cuerpo de Solicitud
Section titled “Cuerpo de Solicitud”{ "status": "COMPLETED", "notes": "Asistió puntualmente"}Ejemplo de Cliente Hono
Section titled “Ejemplo de Cliente Hono”import { hcWithType } from '@vitality-gym/api/client'
const client = hcWithType('http://localhost:3000')
const res = await client.api.v1.reservations[':id'].$patch({ param: { id: 'reservation_id' }, json: { status: 'COMPLETED' }})Cancelar Reserva
Section titled “Cancelar Reserva”Cancelar una reserva.
- URL:
/api/v1/reservations/:id - Método:
DELETE - Autenticación Requerida: Sí
Descripción Interna
Section titled “Descripción Interna”Este endpoint cancela una reserva:
-
Busca la reserva con información de estado y sesión
-
Validaciones:
- No puede cancelar si ya está CANCELLED: “La reserva ya está cancelada”
- No puede cancelar si está COMPLETED: “No se puede cancelar una reserva completada”
- No puede cancelar si la fecha de la sesión < ahora: “No se puede cancelar una reserva que ya ha pasado”
-
Actualiza status a CANCELLED
-
Decrementa
currentBookingsde la sesión -
Retorna reserva cancelada
Ejemplo de Cliente Hono
Section titled “Ejemplo de Cliente Hono”import { hcWithType } from '@vitality-gym/api/client'
const client = hcWithType('http://localhost:3000')
const res = await client.api.v1.reservations[':id'].$delete({ param: { id: 'reservation_id' }})Validar Reserva
Section titled “Validar Reserva”Verificar si se puede hacer una reserva sin crearla.
- URL:
/api/v1/reservations/validate - Método:
POST - Autenticación Requerida: Sí
Descripción Interna
Section titled “Descripción Interna”Este endpoint valida si se puede hacer una reserva sin crearla:
-
Verifica membresía activa: Busca membresía ACTIVE con fechas vigentes
-
Verifica sesión: Busca la sesión de clase y valida que esté activa (status SCHEDULED)
-
Verifica capacidad: Compara
currentBookingsvsmaxCapacityde la sesión -
Verifica duplicados: Busca reservas existentes no canceladas para esa sesión
-
Retorna objeto de validación:
canReserve: true si todas las validaciones pasanhasActiveMembership: booleansessionCapacityAvailable: booleanhasExistingReservation: boolean (true = ya tiene reserva)isSessionActive: booleanreason: Mensaje explicativo si no puede reservar
Cuerpo de Solicitud
Section titled “Cuerpo de Solicitud”{ "classSessionId": "class_session_id_uuid"}Respuesta
Section titled “Respuesta”{ "data": { "canReserve": true, "hasActiveMembership": true, "sessionCapacityAvailable": true, "hasExistingReservation": false, "isSessionActive": true, "reason": null }}Ejemplo de Cliente Hono
Section titled “Ejemplo de Cliente Hono”import { hcWithType } from '@vitality-gym/api/client'
const client = hcWithType('http://localhost:3000')
const res = await client.api.v1.reservations.validate.$post({ json: { classSessionId: 'class_session_id_uuid' }})