Skip to main content

Overview

The Recognition module transforms employee engagement through gamification, public recognition, and competitive leaderboards. By rewarding participation and achievement, it creates a culture of appreciation and continuous improvement.
The gamification system is tightly integrated with all other modules - employees earn points from surveys, courses, mentorship usage, and peer recognition.

Key Features

Points System

Earn points for every action: surveys, courses, mentorship, achievements

Badge Collection

Unlock badges for milestones, streaks, and special accomplishments

Leaderboards

Hospital-wide and departmental rankings to encourage friendly competition

Recognition Wall

Public celebration of achievements visible to entire organization

Recognition Types

Automatic Recognition

The system automatically grants recognition for various achievements:
// From recognitionService.js:42-68
getRecognitionConfig(type) {
  const configs = {
    'encuesta_completada': {
      rewardType: 'puntos',
      title: '¡Encuesta Completada!',
      description: 'Gracias por compartir tu feedback semanal',
      points: 5,
      badge: 'participativo'
    },
    'curso_terminado': {
      rewardType: 'diploma',
      title: '¡Curso Completado!',
      description: 'Has terminado exitosamente tu capacitación',
      points: 20,
      badge: 'aprendiz'
    },
    'empleado_mes': {
      rewardType: 'mencion',
      title: '¡Empleado del Mes!',
      description: 'Reconocimiento por tu excelente desempeño',
      points: 100,
      badge: 'estrella'
    }
  };
  
  return configs[type] || configs['encuesta_completada'];
}

Recognition Schema

// From models/Recognition.js
const recognitionSchema = new mongoose.Schema({
  employeeId: { type: ObjectId, ref: 'Employee', required: true },
  recognitionType: {
    type: String,
    enum: ['encuesta_completada', 'curso_terminado', 'empleado_mes', 'racha_semanal', 'mentor_activo']
  },
  rewardType: {
    type: String,
    enum: ['puntos', 'diploma', 'mencion', 'badge']
  },
  title: String,
  description: String,
  pointsAwarded: { type: Number, default: 0 },
  badgeEarned: String,
  achievementData: mongoose.Schema.Types.Mixed,
  isPublic: { type: Boolean, default: true },
  issuedAt: { type: Date, default: Date.now }
});

Points System

Point Allocation

Different actions earn different point values:
ActionPointsIntegration
Complete quick survey5Diagnostic module
Complete full course20Microlearning module
Earn a badge10Recognition module
Level up25Gamification system
7-day streak15Diagnostic module
Employee of the month100Manual recognition
Voice note in survey8Diagnostic module
Complete gamified survey3-10Diagnostic module

Grant Recognition

// From recognitionService.js:9-40
async grantRecognition(employeeId, type, data = {}) {
  const recognitionConfig = this.getRecognitionConfig(type);
  
  const recognition = new Recognition({
    employeeId,
    recognitionType: type,
    rewardType: recognitionConfig.rewardType,
    title: recognitionConfig.title,
    description: recognitionConfig.description,
    pointsAwarded: recognitionConfig.points,
    badgeEarned: recognitionConfig.badge,
    achievementData: data
  });
  
  await recognition.save();
  
  // Update employee points (if implemented)
  // await this.updateEmployeePoints(employeeId, recognitionConfig.points);
  
  logger.info('Reconocimiento otorgado', {
    employeeId,
    type,
    points: recognitionConfig.points
  });
  
  return recognition;
}

API Usage

POST /api/recognition/grant
{
  "employeeId": "507f1f77bcf86cd799439011",
  "type": "curso_terminado",
  "data": {
    "courseTitle": "RCP Básico",
    "score": 95
  }
}

Response:
{
  "status": "success",
  "data": {
    "recognition": {
      "_id": "507f1f77bcf86cd799439066",
      "title": "¡Curso Completado!",
      "pointsAwarded": 20,
      "badgeEarned": "aprendiz",
      "issuedAt": "2024-01-15T14:30:00Z"
    }
  }
}

Badge System

Automatic Badge Awards

Badges are automatically awarded based on achievements:
// From diagnosticService.js:263-301
async checkAndAwardBadges(employee) {
  const badges = [];
  
  // Badge: Weekly participation
  if (employee.gamification.currentStreak >= 7) {
    badges.push({
      name: 'Participación Semanal',
      description: '7 días consecutivos respondiendo encuestas',
      earnedAt: new Date()
    });
  }
  
  // Badge: Level achievement
  if (employee.gamification.level === 5) {
    badges.push({
      name: 'Colaborador Comprometido',
      description: 'Alcanzó el nivel 5',
      earnedAt: new Date()
    });
  }
  
  // Badge: Positive mood
  if (employee.wellnessMetrics.currentMoodScore >= 4.5) {
    badges.push({
      name: 'Ánimo Positivo',
      description: 'Mantiene un excelente estado de ánimo',
      earnedAt: new Date()
    });
  }
  
  // Add only new badges
  badges.forEach(badge => {
    const exists = employee.gamification.badges.some(b => b.name === badge.name);
    if (!exists) {
      employee.gamification.badges.push(badge);
      employee.gamification.totalPoints += 10; // Bonus for badge
    }
  });
}

Badge Categories

  • Participación Semanal: 7-day streak
  • Participación Mensual: 30-day streak
  • Participación Completa: Complete all survey questions
  • Respuesta Rápida: Complete survey in under 3 minutes
  • Colaborador Comprometido: Reach level 5
  • Aprendiz: Complete a course
  • Maestro: Complete 5 courses
  • Mentor Activo: Use mentorship 10+ times
  • Ánimo Positivo: Maintain mood score >= 4.5
  • Equilibrio: Low stress levels for 30 days
  • Líder de Bienestar: Help 5 colleagues with wellness
  • Empleado del Mes: Manual award by supervisor
  • Estrella del Equipo: Exceptional peer reviews
  • Innovador: Suggest implemented improvement

Leveling System

Level Calculation

Employees level up based on total points:
// From recognitionService.js:116-123
calculateLevel(totalPoints) {
  if (totalPoints >= 500) return 5;
  if (totalPoints >= 300) return 4;
  if (totalPoints >= 150) return 3;
  if (totalPoints >= 50) return 2;
  return 1;
}

Level Progression

LevelPoints RequiredRewards
10-49Starting level
250-149+10 bonus points
3150-299+15 bonus points, special badge
4300-499+20 bonus points, priority support
5500++25 bonus points, VIP status

Level Up Bonus

// From diagnosticService.js:1020-1029
const levelUp = employee.gamification.level > oldLevel;
if (levelUp) {
  totalPoints += 25; // Bonus for level up
  employee.gamification.totalPoints += 25;
  newBadges.push({
    name: `Nivel ${employee.gamification.level}`,
    description: `Alcanzó el nivel ${employee.gamification.level}`,
    earnedAt: new Date()
  });
}

Leaderboards

Hospital-Wide Ranking

// From diagnosticService.js:437-486
async getGamificationStats(employeeId) {
  const employee = await Employee.findById(employeeId);
  
  // Get hospital ranking
  const hospitalEmployees = await Employee.find({
    hospitalId: employee.hospitalId,
    isActive: true
  }).sort({ 'gamification.totalPoints': -1 });
  
  const rank = hospitalEmployees.findIndex(
    emp => emp._id.toString() === employeeId
  ) + 1;
  
  const percentile = Math.round((1 - (rank / hospitalEmployees.length)) * 100);
  
  return {
    ranking: {
      position: rank,
      totalEmployees: hospitalEmployees.length,
      percentile
    },
    gamification: {
      totalPoints: employee.gamification.totalPoints,
      currentLevel: employee.gamification.level,
      currentStreak: employee.gamification.currentStreak,
      badges: employee.gamification.badges.length
    }
  };
}

API Usage

GET /api/recognition/stats/:employeeId

Response:
{
  "employee": {
    "name": "María González",
    "department": "urgencias",
    "position": "Enfermera"
  },
  "gamification": {
    "totalPoints": 245,
    "currentLevel": 3,
    "currentStreak": 12,
    "maxStreak": 15,
    "badges": 6,
    "badgesList": [
      {
        "name": "Participación Semanal",
        "earnedAt": "2024-01-08T10:00:00Z"
      }
    ]
  },
  "ranking": {
    "position": 8,
    "totalEmployees": 156,
    "percentile": 95
  },
  "progress": {
    "currentLevelPoints": 200,
    "nextLevelPoints": 300,
    "progressPercentage": 45,
    "pointsToNextLevel": 55
  }
}

Leaderboard Endpoint

GET /api/recognition/ranking/:hospitalId?department=urgencias&limit=10

Response:
{
  "leaderboard": [
    {
      "rank": 1,
      "employee": {
        "name": "Carlos Ruiz",
        "position": "Médico",
        "department": "urgencias"
      },
      "points": 580,
      "level": 5,
      "badges": 12
    },
    {
      "rank": 2,
      "employee": {
        "name": "Ana Torres",
        "position": "Enfermera",
        "department": "urgencias"
      },
      "points": 465,
      "level": 4,
      "badges": 9
    }
  ]
}

Recognition Wall

Public Recognition Feed

Display recent achievements to motivate the entire team:
// From recognitionService.js:70-92
async getRecognitionWall(hospitalId, limit = 10) {
  const recognitions = await Recognition.find({ isPublic: true })
    .populate({
      path: 'employeeId',
      match: { hospitalId },
      select: 'personalInfo.name jobInfo.position jobInfo.department'
    })
    .sort({ issuedAt: -1 })
    .limit(limit);
  
  // Filter recognitions where employee matches hospital
  const filteredRecognitions = recognitions.filter(r => r.employeeId);
  
  return filteredRecognitions;
}

API Usage

GET /api/recognition/wall/:hospitalId?limit=20

Response:
{
  "recognitions": [
    {
      "_id": "507f1f77bcf86cd799439088",
      "employee": {
        "name": "Laura Mendez",
        "position": "Enfermera",
        "department": "urgencias"
      },
      "title": "¡Curso Completado!",
      "description": "Has terminado exitosamente tu capacitación",
      "pointsAwarded": 20,
      "badgeEarned": "aprendiz",
      "issuedAt": "2024-01-15T16:45:00Z"
    },
    {
      "_id": "507f1f77bcf86cd799439099",
      "employee": {
        "name": "Pedro Sánchez",
        "position": "Médico",
        "department": "consulta_externa"
      },
      "title": "¡Empleado del Mes!",
      "description": "Reconocimiento por tu excelente desempeño",
      "pointsAwarded": 100,
      "badgeEarned": "estrella",
      "issuedAt": "2024-01-15T09:00:00Z"
    }
  ]
}

Gamification Statistics

Employee Game Stats

// From recognitionService.js:94-114
async getEmployeeGameStats(employeeId) {
  const recognitions = await Recognition.find({ employeeId });
  
  const stats = {
    totalPoints: recognitions.reduce((sum, r) => sum + r.pointsAwarded, 0),
    totalRecognitions: recognitions.length,
    badges: [...new Set(recognitions.map(r => r.badgeEarned).filter(Boolean))],
    level: this.calculateLevel(recognitions.reduce((sum, r) => sum + r.pointsAwarded, 0)),
    recentAchievements: recognitions.slice(-5)
  };
  
  return stats;
}

Upcoming Milestones

Show employees what they’re working towards:
// From diagnosticService.js:489-512
getUpcomingMilestones(employee) {
  const milestones = [];
  
  // Streak milestone
  const nextStreakMilestone = [10, 15, 30].find(
    ms => ms > employee.gamification.currentStreak
  );
  if (nextStreakMilestone) {
    milestones.push({
      name: `Racha de ${nextStreakMilestone} días`,
      progress: Math.round(
        (employee.gamification.currentStreak / nextStreakMilestone) * 100
      ),
      reward: `${nextStreakMilestone * 2} puntos bonus`
    });
  }
  
  // Level milestone
  if (employee.gamification.level < 10) {
    milestones.push({
      name: `Nivel ${employee.gamification.level + 1}`,
      progress: Math.round(
        ((employee.gamification.totalPoints % 100) / 100) * 100
      ),
      reward: 'Nueva insignia'
    });
  }
  
  return milestones.slice(0, 2);
}

Streak Tracking

Daily Streak System

Track consecutive days of participation:
// From Employee model (implied from diagnosticService.js)
employee.updateStreak(); // Method called after survey completion

// Streak logic:
// - Increments currentStreak if activity today
// - Resets to 1 if gap > 1 day
// - Updates maxStreak if currentStreak exceeds it

Streak Rewards

Streak LengthReward
7 days”Participación Semanal” badge + 15 points
14 days20 bonus points
30 days”Participación Mensual” badge + 50 points
60 days100 bonus points
90 daysSpecial “Dedicación” badge + 150 points

Integration with Other Modules

Diagnostic Module Integration

// From diagnosticService.js:242-260
async updateEmployeeMetrics(employee, responses, riskAnalysis) {
  // Update wellness metrics
  employee.wellnessMetrics = {
    currentMoodScore: this.moodToScore(responses.moodToday) * 5,
    riskLevel: riskAnalysis.riskLevel
  };
  
  // Update gamification
  employee.gamification.totalPoints += 5;
  employee.updateStreak();
  employee.gamification.level = employee.calculateLevel();
  
  // Check for badges
  await this.checkAndAwardBadges(employee);
  
  await employee.save();
}

Microlearning Integration

// Automatic recognition on course completion
await recognitionService.grantRecognition(
  employeeId,
  'curso_terminado',
  {
    courseTitle: course.title,
    score: finalScore
  }
);

API Reference

Endpoints

// Grant recognition to employee
{
  "employeeId": "507f1f77bcf86cd799439011",
  "type": "empleado_mes",
  "data": { "reason": "Exceptional patient care" }
}

Best Practices

Celebrate Small Wins

Recognize every achievement, no matter how small, to build momentum

Public Recognition

Make achievements visible to create a culture of appreciation

Fair Competition

Segment leaderboards by department to ensure fair comparison

Meaningful Rewards

Connect points to real rewards (time off, parking, etc.)

Next Steps

Diagnostic Module

See how surveys contribute to gamification

Microlearning

Learn about course completion rewards

Correlation Analytics

Analyze impact of recognition on performance

API Reference

Complete API documentation for the Recognition module

Build docs developers (and LLMs) love