Skip to main content
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.
1

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
2

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);
}
3

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);
  }
};
4

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

EndpointMethodDescriptionAuth Required
/api/rutinas/generarPOSTGenerate AI workout routineYes
/api/rutinas/guardarPOSTSave routine to user accountYes
/api/rutinas/:userIdGETGet user’s saved routinesYes
/api/entrenamientos/registrarPOSTRecord completed workoutYes
/api/entrenamientos/historial/:userIdGETGet workout historyYes
Users can regenerate their routine at any time to get fresh workout variations while maintaining their fitness goals.

Build docs developers (and LLMs) love