Entrenadores
Endpoints para encontrar entrenadores, verificar horarios, gestionar sesiones de entrenamiento y reseñas.
Modelos de Datos
Section titled “Modelos de Datos”Los siguientes modelos de Prisma pertenecen al schema gym:
Trainer
Section titled “Trainer”model Trainer { id String @id @default(cuid()) userProfileId String @unique @map("user_profile_id") specializations String[] certifications String[] experience Int hourlyRate Float @map("hourly_rate") bio String? isActive Boolean @default(true) @map("is_active")
userProfile UserProfile @relation(fields: [userProfileId], references: [id], onDelete: Cascade)
classTrainers Class[] @relation("ClassTrainers") trainingSessions TrainingSession[] reviews TrainerReview[]
createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at")
@@map("trainers") @@schema("gym")}TrainingSession
Section titled “TrainingSession”model TrainingSession { id String @id @default(cuid()) userProfileId String @map("user_profile_id") trainerId String @map("trainer_id") scheduledDate DateTime @map("scheduled_date") duration Int price Float notes String? status ReservationStatus @default(CONFIRMED)
userProfile UserProfile @relation(fields: [userProfileId], references: [id], onDelete: Cascade) trainer Trainer @relation(fields: [trainerId], references: [id], onDelete: Cascade)
createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at")
@@map("training_sessions") @@schema("gym")}El modelo TrainerReview pertenece al schema reviews:
TrainerReview
Section titled “TrainerReview”model TrainerReview { id String @id @default(cuid()) userProfileId String @map("user_profile_id") trainerId String @map("trainer_id")
rating Int @db.SmallInt comment String?
isVisible Boolean @default(true) @map("is_visible") isVerified Boolean @default(false) @map("is_verified")
userProfile UserProfile @relation(fields: [userProfileId], references: [id], onDelete: Cascade) trainer Trainer @relation(fields: [trainerId], references: [id], onDelete: Cascade)
createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at")
@@unique([userProfileId, trainerId]) @@map("trainer_reviews") @@schema("reviews")}Endpoints
Section titled “Endpoints”Obtener Entrenadores
Section titled “Obtener Entrenadores”Obtener una lista de entrenadores con filtros.
- URL:
/api/v1/trainers - Método:
GET - Autenticación Requerida: Sí
Descripción Interna
Section titled “Descripción Interna”Este endpoint obtiene entrenadores con filtrado avanzado:
-
Filtros disponibles:
specialization: Filtra por especialización (usandohasSomeen array)minExperience: Años mínimos de experienciamaxHourlyRate: Tarifa máxima por horaisActive: Solo entrenadores activossearch: Búsqueda en nombre, especializaciones y bio
-
Incluye:
- userProfile con información del usuario
- Contadores de trainingSessions y classTrainers
-
Ordenamiento: isActive desc → experience desc → name asc
Parámetros de Consulta de Solicitud
Section titled “Parámetros de Consulta de Solicitud”specialization: Filtrar por especializaciónsearch: Buscar por nombre, especialización, biominExperience: Años mínimos de experienciamaxHourlyRate: Tarifa máxima por horaisActive: Filtrar entrenadores activospage: Número de páginalimit: Resultados por página
Respuesta
Section titled “Respuesta”{ "data": [ { "id": "...", "specializations": ["Yoga", "Pilates"], "bio": "Expert instructor", "experience": 5, "hourlyRate": 50, "isActive": true, "userProfile": {...}, "_count": { "trainingSessions": 120, "classTrainers": 3 } } ], "pagination": {...}}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.trainers.$get({ query: { specialization: 'Yoga' }})Obtener Entrenador por ID
Section titled “Obtener Entrenador por ID”Obtener detalles de un entrenador específico.
- URL:
/api/v1/trainers/:id - Método:
GET - Autenticación Requerida: Sí
Descripción Interna
Section titled “Descripción Interna”Este endpoint obtiene un entrenador específico:
- Busca por ID usando
findUniqueOrThrow - Incluye:
- userProfile completo
- classTrainers (clases que imparte)
- Contadores de sesiones y clases
Respuesta
Section titled “Respuesta”{ "data": { "id": "...", "specializations": ["Yoga"], "bio": "Expert yoga instructor", "experience": 5, "hourlyRate": 50, "certifications": ["RYT-200"], "userProfile": {...}, "classTrainers": [...], "_count": {...} }}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.trainers[':id'].$get({ param: { id: 'trainer_id' }})Obtener Horario del Entrenador
Section titled “Obtener Horario del Entrenador”Obtener el horario de un entrenador para un rango de fechas.
- URL:
/api/v1/trainers/:id/schedule - Método:
GET - Autenticación Requerida: Sí
Descripción Interna
Section titled “Descripción Interna”Este endpoint obtiene el horario completo de un entrenador:
-
Obtiene sesiones de entrenamiento personal:
- Filtradas por trainerId y rango de fechas
- Solo CONFIRMED y COMPLETED
- Incluye userProfile del cliente
-
Obtiene clases grupales:
- Busca ClassSchedules donde el trainer esté asignado
- Solo clases activas y schedules activos
- Incluye información de la clase
-
Retorna ambos tipos para visualización del horario
Parámetros de Consulta de Solicitud
Section titled “Parámetros de Consulta de Solicitud”dateFrom: Fecha de inicio (YYYY-MM-DD) - requeridodateTo: Fecha de fin (YYYY-MM-DD) - requerido
Respuesta
Section titled “Respuesta”{ "data": { "trainingSessions": [ { "id": "...", "scheduledDate": "2023-10-27T10:00:00Z", "duration": 60, "status": "CONFIRMED", "userProfile": {...} } ], "classSchedules": [ { "id": "...", "dayOfWeek": 1, "startTime": "09:00", "endTime": "10:00", "class": { "name": "Yoga", "type": "YOGA" } } ], "period": { "from": "2023-10-01", "to": "2023-10-31" } }}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.trainers[':id'].schedule.$get({ param: { id: 'trainer_id' }, query: { dateFrom: '2023-10-01', dateTo: '2023-10-31' }})Obtener Entrenadores Disponibles
Section titled “Obtener Entrenadores Disponibles”Encontrar entrenadores disponibles en un horario específico.
- URL:
/api/v1/trainers/available - Método:
GET - Autenticación Requerida: Sí
Descripción Interna
Section titled “Descripción Interna”Este endpoint busca entrenadores disponibles:
-
Valida formato de hora (HH:MM)
-
Calcula ventana de tiempo:
- startTime: fecha + hora especificada
- endTime: startTime + duración
-
Busca entrenadores donde:
- isActive: true
- No tengan trainingSessions CONFIRMED que se solapen
-
Incluye userProfile de cada entrenador disponible
Parámetros de Consulta de Solicitud
Section titled “Parámetros de Consulta de Solicitud”date: Fecha (YYYY-MM-DD)startTime: Hora de inicio (HH:MM)duration: Duración en minutos
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.trainers.available.$get({ query: { date: '2023-10-27', startTime: '10:00', duration: '60' }})Crear Entrenador (Admin/Staff)
Section titled “Crear Entrenador (Admin/Staff)”Registrar un nuevo entrenador.
- URL:
/api/v1/trainers - Método:
POST - Autenticación Requerida: Sí (Admin/Staff)
Descripción Interna
Section titled “Descripción Interna”Este endpoint crea un nuevo entrenador:
-
Verifica que el usuario existe y no es ya entrenador
-
Crea en transacción:
- Registro Trainer con datos proporcionados
- Actualiza UserProfile.role a TRAINER
-
Retorna entrenador con userProfile y user
Campos del Entrenador
Section titled “Campos del Entrenador”userProfileId: ID del perfil de usuario (requerido)specializations: Array de especializacionesbio: Biografía/descripciónexperience: Años de experienciahourlyRate: Tarifa por horacertifications: Array de certificaciones
Cuerpo de Solicitud
Section titled “Cuerpo de Solicitud”{ "userProfileId": "user_profile_id", "specializations": ["Yoga", "Pilates"], "bio": "Expert yoga instructor with 5 years experience", "experience": 5, "hourlyRate": 50, "certifications": ["RYT-200"]}Errores Comunes
Section titled “Errores Comunes”400 Bad Request: El usuario ya es un entrenador
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.trainers.$post({ json: { userProfileId: 'user_profile_id', specializations: ['Yoga'], bio: 'Expert yoga instructor', hourlyRate: 50 }})Crear Sesión de Entrenamiento
Section titled “Crear Sesión de Entrenamiento”Reservar una sesión de entrenamiento con un entrenador.
- URL:
/api/v1/trainers/sessions - Método:
POST - Autenticación Requerida: Sí
Descripción Interna
Section titled “Descripción Interna”Este endpoint crea una sesión de entrenamiento:
-
Verifica que el entrenador existe y está activo
-
Verifica conflictos de horario:
- Busca sesiones existentes del usuario o entrenador
- Calcula solapamiento con la nueva sesión
- Si hay conflicto con CONFIRMED, lanza error
-
Calcula precio: (hourlyRate / 60) × duration
-
Crea la sesión con status inicial (según lógica de negocio)
TODO: Enviar notificación al entrenador y usuario
Cuerpo de Solicitud
Section titled “Cuerpo de Solicitud”{ "trainerId": "trainer_id", "scheduledDate": "2023-10-27T10:00:00Z", "duration": 60, "type": "PERSONAL", "notes": "Focus on flexibility"}Respuesta
Section titled “Respuesta”{ "data": { "id": "...", "scheduledDate": "2023-10-27T10:00:00Z", "duration": 60, "price": 50, "status": "CONFIRMED", "trainer": {...}, "userProfile": {...} }}Errores Comunes
Section titled “Errores Comunes”400 Bad Request: Ya existe una sesión confirmada en ese horario404 Not Found: Entrenador inactivo
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.trainers.sessions.$post({ json: { trainerId: 'trainer_id', scheduledDate: '2023-10-27T10:00:00Z', duration: 60 }})Obtener Sesiones de Entrenamiento
Section titled “Obtener Sesiones de Entrenamiento”Obtener sesiones de entrenamiento con filtros.
- URL:
/api/v1/trainers/sessions - Método:
GET - Autenticación Requerida: Sí
Descripción Interna
Section titled “Descripción Interna”Este endpoint obtiene sesiones de entrenamiento:
-
Filtros disponibles:
trainerId: Por entrenadoruserProfileId: Por usuariostatus: Estado de la sesióndateFrom/dateTo: Rango de fechas
-
Incluye:
- trainer con userProfile
- userProfile del cliente
-
Ordenado por scheduledDate descendente
Cancelar Sesión de Entrenamiento
Section titled “Cancelar Sesión de Entrenamiento”Cancelar una sesión de entrenamiento.
- URL:
/api/v1/trainers/sessions/:id/cancel - Método:
POST - Autenticación Requerida: Sí
Descripción Interna
Section titled “Descripción Interna”Este endpoint cancela una sesión:
-
Verifica permisos:
- El usuario debe ser el cliente o el entrenador
- isTrainer flag indica si es el entrenador cancelando
-
Valida estado:
- No puede cancelar COMPLETED o CANCELLED
-
Actualiza status a CANCELLED
TODO: Enviar notificación de cancelación
Obtener Estadísticas del Entrenador
Section titled “Obtener Estadísticas del Entrenador”Obtener estadísticas para un entrenador.
- URL:
/api/v1/trainers/:id/stats - Método:
GET - Autenticación Requerida: Sí
Descripción Interna
Section titled “Descripción Interna”Este endpoint calcula estadísticas del entrenador:
-
Calcula fecha inicio según período (week, month, quarter, year)
-
Ejecuta consultas en transacción:
- Total de sesiones
- Sesiones completadas
- Sesiones canceladas
- Ingresos totales (suma de prices de COMPLETED)
- Clientes únicos
-
Calcula tasas:
- completionRate: (completadas / total) × 100
- cancellationRate: (canceladas / total) × 100
Parámetros de Consulta de Solicitud
Section titled “Parámetros de Consulta de Solicitud”period: Período de tiempo (week, month, quarter, year)
Crear Reseña de Entrenador
Section titled “Crear Reseña de Entrenador”Enviar una reseña para un entrenador.
- URL:
/api/v1/trainers/:id/reviews - Método:
POST - Autenticación Requerida: Sí
Descripción Interna
Section titled “Descripción Interna”Este endpoint crea una valoración:
-
Validaciones:
- Entrenador existe y está activo
- No puede valorarse a sí mismo
- No debe tener review existente para este entrenador
-
Verifica sesión completada:
- Busca si el usuario tuvo sesión COMPLETED con el trainer
- Si la tuvo:
isVerified: true
-
Crea la review con rating, comment y flags
TODO: Notificar al entrenador sobre nueva valoración
Cuerpo de Solicitud
Section titled “Cuerpo de Solicitud”{ "rating": 5, "comment": "Excellent trainer!"}Errores Comunes
Section titled “Errores Comunes”400 Bad Request: Ya has valorado a este entrenador400 Bad Request: No puedes valorarte a ti mismo
Obtener Reseñas del Entrenador
Section titled “Obtener Reseñas del Entrenador”Obtener reseñas de un entrenador.
- URL:
/api/v1/trainers/:id/reviews - Método:
GET - Autenticación Requerida: Sí
Descripción Interna
Section titled “Descripción Interna”Este endpoint obtiene valoraciones de un entrenador:
-
Filtro base: Solo reviews con
isVisible: true -
Filtros opcionales:
rating: Filtrar por rating específicohasComment: Solo reviews con comentariohasTrainerResponse: Solo reviews con respuesta del trainersortBy: newest, oldest, highest, lowest
-
Incluye userProfile y trainer con userProfile
Obtener Estadísticas de Valoración del Entrenador
Section titled “Obtener Estadísticas de Valoración del Entrenador”Obtener estadísticas de valoraciones de un entrenador.
- URL:
/api/v1/trainers/:id/rating-stats - Método:
GET - Autenticación Requerida: Sí
Descripción Interna
Section titled “Descripción Interna”Este endpoint calcula estadísticas de ratings:
- Agrupa reviews por rating (1-5)
- Calcula:
- Distribución de ratings (count por cada valor)
- Total de reviews
- Rating promedio ponderado
Respuesta
Section titled “Respuesta”{ "data": { "averageRating": 4.5, "totalReviews": 50, "ratingDistribution": [ { "rating": 5, "count": 30 }, { "rating": 4, "count": 15 }, { "rating": 3, "count": 5 } ] }}