FitAiid uses AI to generate personalized workout programs tailored to each user’s fitness level, goals, and preferences collected from the questionnaire.
Workout Program Overview
The workout generation system (entrenador.html) creates customized weekly training schedules based on user data.
AI-Powered Intelligent routine generation using OpenAI
Personalized Adapted to goals, level, and schedule
Flexible 1-5 training days per week
Trainer Page Layout
The entrenador page consists of several key sections:
Hero Section with Profile Summary
Displays the user’s fitness profile before generating a routine:
frontend/pages/entrenador.html
< section class = "hero" >
< div class = "hero-content" >
< div class = "hero-badge" >
< i class = "fas fa-robot" ></ i >
< span > Entrenador IA Personalizado </ span >
</ div >
< h1 > TU RUTINA < span class = "accent" > PERFECTA </ span ></ h1 >
< p class = "hero-subtitle" >
Generada por inteligencia artificial basada en tu perfil fitness,
objetivos y disponibilidad. Cada ejercicio adaptado a ti.
</ p >
< div class = "profile-summary" id = "profileSummary" >
< div class = "profile-item" >
< i class = "fas fa-bullseye" ></ i >
< span id = "userGoal" > Cargando... </ span >
</ div >
< div class = "profile-item" >
< i class = "fas fa-signal" ></ i >
< span id = "userLevel" > Cargando... </ span >
</ div >
< div class = "profile-item" >
< i class = "fas fa-calendar-week" ></ i >
< span id = "userDays" > Cargando... </ span >
</ div >
< div class = "profile-item" >
< i class = "fas fa-location-dot" ></ i >
< span id = "userLocation" > Cargando... </ span >
</ div >
</ div >
< button class = "btn-generate" id = "btnGenerate" onclick = "generarRutina()" >
< i class = "fas fa-wand-magic-sparkles" ></ i >
Generar Mi Rutina Personalizada
</ button >
</ div >
</ section >
Fitty AI Coach Card
An interactive AI coach assistant that guides users:
< div class = "fitty-card" >
< div class = "fitty-header" >
< div class = "fitty-avatar" >
< i class = "fas fa-robot" ></ i >
</ div >
< div class = "fitty-info" >
< h3 > Fitty </ h3 >
< span class = "fitty-status" >
< i class = "fas fa-circle" ></ i > Tu Coach IA
</ span >
</ div >
</ div >
< div class = "fitty-message" id = "fittyMessage" >
¡Hola! Soy Fitty, tu entrenador personal con IA.
Estoy listo para crear una rutina perfecta basada en tus objetivos.
¡Haz clic en el botón para comenzar! 💪
</ div >
< div class = "fitty-tips" >
< h4 >< i class = "fas fa-lightbulb" ></ i > Tip del día </ h4 >
< p id = "dailyTip" >
La consistencia supera a la intensidad. Es mejor entrenar 30 minutos
todos los días que 2 horas una vez a la semana.
</ p >
</ div >
</ div >
Loading Profile Data
Before generating a routine, the user’s fitness profile is loaded from localStorage:
function cargarPerfilUsuario () {
const fitnessProfile = localStorage . getItem ( 'fitnessProfile' );
if ( ! fitnessProfile ) {
alert ( 'Completa el cuestionario primero' );
window . location . href = 'preguntas.html' ;
return null ;
}
const profile = JSON . parse ( fitnessProfile );
// Update UI with profile data
document . getElementById ( 'userGoal' ). textContent =
profile . mainGoal ?. charAt ( 0 ). toUpperCase () + profile . mainGoal ?. slice ( 1 );
document . getElementById ( 'userLevel' ). textContent =
profile . fitnessLevel ?. charAt ( 0 ). toUpperCase () + profile . fitnessLevel ?. slice ( 1 );
document . getElementById ( 'userDays' ). textContent =
` ${ profile . trainingDaysPerWeek } días/semana` ;
document . getElementById ( 'userLocation' ). textContent =
profile . trainingLocation === 'casa' ? 'En Casa' : 'Gimnasio' ;
return profile ;
}
If the user hasn’t completed the questionnaire, they are automatically redirected to complete it first.
Generating Workout Routines
The routine generation follows a multi-step process with visual feedback.
Show Loading Overlay
Display animated loading screen with progress steps: function generarRutina () {
const profile = cargarPerfilUsuario ();
if ( ! profile ) return ;
// Show loading overlay
const loadingOverlay = document . getElementById ( 'loadingOverlay' );
loadingOverlay . style . display = 'flex' ;
// Animate loading steps
setTimeout (() => activateStep ( 'step1' ), 500 );
setTimeout (() => activateStep ( 'step2' ), 1500 );
setTimeout (() => activateStep ( 'step3' ), 2500 );
setTimeout (() => activateStep ( 'step4' ), 3500 );
// Call API to generate routine
generarRutinaIA ( profile );
}
The loading overlay includes:
Rotating dumbbell icon
Progress message
4-step visual indicator
Build AI Prompt
Construct a detailed prompt for the AI based on user profile: async function generarRutinaIA ( profile ) {
const prompt = `
Eres un entrenador personal profesional. Crea una rutina de entrenamiento
personalizada para un usuario con las siguientes características:
- Género: ${ profile . gender }
- Edad: ${ profile . age } años
- Nivel de experiencia: ${ profile . fitnessLevel }
- Objetivo principal: ${ profile . mainGoal }
- Lugar de entrenamiento: ${ profile . trainingLocation }
- Días disponibles: ${ profile . trainingDaysPerWeek } días por semana
- Duración por sesión: ${ profile . sessionDuration }
- Condiciones médicas: ${ profile . medicalConditions }
Genera una rutina semanal con ejercicios específicos, series, repeticiones,
y tiempo de descanso. Incluye calentamiento y enfriamiento.
Formato JSON:
{
"semana": [
{
"dia": "Lunes",
"enfoque": "Pecho y Tríceps",
"duracion": 60,
"calentamiento": [...],
"ejercicios": [
{
"nombre": "Press de banca",
"series": 4,
"repeticiones": "8-12",
"descanso": "90 seg",
"notas": "Mantén la técnica correcta"
}
],
"enfriamiento": [...]
}
]
}
` ;
// Call backend API that uses OpenAI
const response = await fetch ( ` ${ CONFIG . API_URL } /api/rutinas/generar` , {
method: 'POST' ,
headers: {
'Content-Type' : 'application/json' ,
'Authorization' : `Bearer ${ localStorage . getItem ( 'token' ) } `
},
body: JSON . stringify ({ prompt , profile })
});
const data = await response . json ();
mostrarRutina ( data . rutina );
}
Process AI Response
The backend processes the AI request and returns structured workout data: backend/src/controllers/rutinaController.js
const generarRutina = async ( req , res ) => {
const { prompt , profile } = req . body ;
const userId = req . user . _id ;
try {
// Call OpenAI API
const completion = await openai . chat . completions . create ({
model: "gpt-4" ,
messages: [
{
role: "system" ,
content: "Eres un entrenador personal certificado con 10 años de experiencia."
},
{
role: "user" ,
content: prompt
}
],
temperature: 0.7 ,
max_tokens: 2000
});
const rutinaText = completion . choices [ 0 ]. message . content ;
const rutina = JSON . parse ( rutinaText );
// Save routine to database
const nuevaRutina = new Rutina ({
userId ,
nombre: `Rutina ${ profile . mainGoal } ` ,
fitnessProfile: profile ,
semana: rutina . semana ,
generadoPor: 'IA' ,
fechaCreacion: new Date ()
});
await nuevaRutina . save ();
res . status ( 200 ). json ({
success: true ,
rutina: rutina ,
rutinaId: nuevaRutina . _id
});
} catch ( error ) {
console . error ( 'Error generando rutina:' , error );
throw new AppError ( 'Error al generar rutina con IA' , 500 );
}
};
Display Workout Routine
Render the generated routine with interactive day tabs: function mostrarRutina ( rutina ) {
// Hide loading, show routine section
document . getElementById ( 'loadingOverlay' ). style . display = 'none' ;
document . getElementById ( 'rutinaSection' ). style . display = 'block' ;
// Generate day tabs
const daysTabs = document . getElementById ( 'daysTabs' );
daysTabs . innerHTML = '' ;
rutina . semana . forEach (( dia , index ) => {
const tab = document . createElement ( 'button' );
tab . className = `day-tab ${ index === 0 ? 'active' : '' } ` ;
tab . textContent = dia . dia ;
tab . onclick = () => mostrarDia ( dia , index );
daysTabs . appendChild ( tab );
});
// Show first day by default
mostrarDia ( rutina . semana [ 0 ], 0 );
// Calculate weekly summary
calcularResumenSemanal ( rutina );
}
Workout Day Display
Each training day is displayed with detailed exercise information:
function mostrarDia ( dia , index ) {
const dayContent = document . getElementById ( 'dayContent' );
// Update active tab
document . querySelectorAll ( '.day-tab' ). forEach (( tab , i ) => {
tab . classList . toggle ( 'active' , i === index );
});
// Build day content HTML
let html = `
<div class="day-header">
<h3> ${ dia . dia } - ${ dia . enfoque } </h3>
<div class="day-meta">
<span><i class="fas fa-clock"></i> ${ dia . duracion } min</span>
<span><i class="fas fa-fire"></i> ~ ${ Math . round ( dia . duracion * 5 ) } kcal</span>
</div>
</div>
` ;
// Warmup section
if ( dia . calentamiento && dia . calentamiento . length > 0 ) {
html += `
<div class="section-block warmup">
<h4><i class="fas fa-heart"></i> Calentamiento</h4>
<div class="exercise-list">
` ;
dia . calentamiento . forEach ( ejercicio => {
html += `
<div class="exercise-card mini">
<span> ${ ejercicio . nombre } </span>
<span class="duration"> ${ ejercicio . duracion } </span>
</div>
` ;
});
html += `</div></div>` ;
}
// Main exercises
html += `
<div class="section-block main-exercises">
<h4><i class="fas fa-dumbbell"></i> Ejercicios Principales</h4>
<div class="exercise-list">
` ;
dia . ejercicios . forEach (( ejercicio , idx ) => {
html += `
<div class="exercise-card" data-exercise-id=" ${ idx } ">
<div class="exercise-header">
<div class="exercise-number"> ${ idx + 1 } </div>
<div class="exercise-info">
<h5> ${ ejercicio . nombre } </h5>
<p class="exercise-notes"> ${ ejercicio . notas || '' } </p>
</div>
<button class="exercise-check" onclick="toggleExerciseComplete( ${ idx } )">
<i class="far fa-circle"></i>
</button>
</div>
<div class="exercise-details">
<div class="detail-item">
<i class="fas fa-repeat"></i>
<span> ${ ejercicio . series } series</span>
</div>
<div class="detail-item">
<i class="fas fa-hashtag"></i>
<span> ${ ejercicio . repeticiones } reps</span>
</div>
<div class="detail-item">
<i class="fas fa-clock"></i>
<span> ${ ejercicio . descanso } descanso</span>
</div>
</div>
</div>
` ;
});
html += `</div></div>` ;
// Cooldown section
if ( dia . enfriamiento && dia . enfriamiento . length > 0 ) {
html += `
<div class="section-block cooldown">
<h4><i class="fas fa-wind"></i> Enfriamiento</h4>
<div class="exercise-list">
` ;
dia . enfriamiento . forEach ( ejercicio => {
html += `
<div class="exercise-card mini">
<span> ${ ejercicio . nombre } </span>
<span class="duration"> ${ ejercicio . duracion } </span>
</div>
` ;
});
html += `</div></div>` ;
}
dayContent . innerHTML = html ;
}
Each exercise card includes an interactive checkbox to mark exercises as completed during the workout.
Weekly Summary Statistics
The system calculates and displays weekly totals:
function calcularResumenSemanal ( rutina ) {
let totalMinutes = 0 ;
let totalExercises = 0 ;
let totalDays = rutina . semana . length ;
rutina . semana . forEach ( dia => {
totalMinutes += dia . duracion ;
totalExercises += dia . ejercicios . length ;
});
const totalCalories = Math . round ( totalMinutes * 5 ); // Estimate: 5 cal/min
// Update UI
document . getElementById ( 'totalMinutes' ). textContent = totalMinutes ;
document . getElementById ( 'totalCalories' ). textContent = totalCalories ;
document . getElementById ( 'totalExercises' ). textContent = totalExercises ;
document . getElementById ( 'totalDays' ). textContent = totalDays ;
}
Total Minutes Sum of all workout durations
Calories Estimated calories burned (5 cal/min)
Exercises Total number of exercises
Training Days Number of workout days
Saving Workout Routines
Users can save generated routines to their account:
async function guardarRutina () {
const rutinaId = localStorage . getItem ( 'currentRutinaId' );
const userId = localStorage . getItem ( 'userId' );
if ( ! rutinaId ) {
alert ( 'No hay rutina para guardar' );
return ;
}
try {
const response = await fetch ( ` ${ CONFIG . API_URL } /api/rutinas/guardar` , {
method: 'POST' ,
headers: {
'Content-Type' : 'application/json' ,
'Authorization' : `Bearer ${ localStorage . getItem ( 'token' ) } `
},
body: JSON . stringify ({
rutinaId ,
userId ,
nombre: 'Mi Rutina Personalizada' ,
favorita: true
})
});
const data = await response . json ();
if ( data . success ) {
alert ( '✅ Rutina guardada exitosamente' );
}
} catch ( error ) {
console . error ( 'Error guardando rutina:' , error );
alert ( '❌ Error al guardar la rutina' );
}
}
Recording Workout Progress
When users complete a workout, the progress is tracked:
backend/src/routes/entrenamientos_r.js
// POST /api/entrenamientos/registrar
router . post ( '/registrar' , protect , catchAsync ( async ( req , res ) => {
const { userId , rutinaId , diaIndex , ejerciciosCompletados , duracion , notas } = req . body ;
const entrenamiento = new WorkoutProgress ({
userId ,
rutinaId ,
diaIndex ,
ejerciciosCompletados ,
duracion ,
caloriasQuemadas: Math . round ( duracion * 5 ),
notas ,
fecha: new Date (),
completado: true
});
await entrenamiento . save ();
// Update user statistics
await actualizarEstadisticasUsuario ( userId );
res . status ( 201 ). json ({
success: true ,
message: 'Entrenamiento registrado' ,
data: entrenamiento
});
}));
Chat with Fitty
The floating chat button allows users to ask questions:
< div class = "chat-fab" id = "chatFab" onclick = "toggleChat()" >
< i class = "fas fa-comments" ></ i >
< span class = "chat-badge" > 1 </ span >
</ div >
< div class = "chat-window" id = "chatWindow" >
< div class = "chat-header" >
< div class = "chat-avatar" >
< i class = "fas fa-robot" ></ i >
</ div >
< div class = "chat-info" >
< h4 > Fitty </ h4 >
< span class = "chat-status" > En línea </ span >
</ div >
< button class = "chat-close" onclick = "toggleChat()" >
< i class = "fas fa-times" ></ i >
</ button >
</ div >
< div class = "chat-messages" id = "chatMessages" >
<!-- Messages rendered here -->
</ div >
< div class = "chat-input" >
< input type = "text" id = "chatInput" placeholder = "Pregunta sobre tu rutina..." />
< button onclick = "enviarMensaje()" >
< i class = "fas fa-paper-plane" ></ i >
</ button >
</ div >
</ div >
Workout Data Structure
The routine data follows this structure:
interface Rutina {
_id : string ;
userId : string ;
nombre : string ;
fitnessProfile : FitnessProfile ;
semana : DiaEntrenamiento [];
generadoPor : 'IA' | 'Manual' ;
fechaCreacion : Date ;
}
interface DiaEntrenamiento {
dia : string ; // "Lunes", "Martes", etc.
enfoque : string ; // "Pecho y Tríceps", "Piernas", etc.
duracion : number ; // minutes
calentamiento : Ejercicio [];
ejercicios : Ejercicio [];
enfriamiento : Ejercicio [];
}
interface Ejercicio {
nombre : string ;
series ?: number ;
repeticiones ?: string ; // "8-12", "15", "30 seg"
descanso ?: string ; // "90 seg", "2 min"
duracion ?: string ; // for warmup/cooldown
notas ?: string ;
}
API Endpoints
Endpoint Method Description Auth Required /api/rutinas/generarPOST Generate AI workout routine Yes /api/rutinas/guardarPOST Save routine to user account Yes /api/rutinas/:userIdGET Get user’s saved routines Yes /api/entrenamientos/registrarPOST Record completed workout Yes /api/entrenamientos/historial/:userIdGET Get workout history Yes
Users can regenerate their routine at any time to get fresh workout variations while maintaining their fitness goals.