workspace-v1/orchestration/analisis/ANALISIS-ACHIEVEMENTS-PAGE-2026-01-10.md
rckrdmrd e56e927a4d [MAINT-001] docs(orchestration): Actualizacion directivas SIMCO, perfiles y documentacion
Cambios incluidos:
- INDICE-DIRECTIVAS-WORKSPACE.yml actualizado
- Perfiles de agentes: PERFIL-ML.md, PERFIL-SECURITY.md
- Directivas SIMCO actualizadas:
  - SIMCO-ASIGNACION-PERFILES.md
  - SIMCO-CCA-SUBAGENTE.md
  - SIMCO-CONTEXT-ENGINEERING.md
  - SIMCO-CONTEXT-RESOLUTION.md
  - SIMCO-DELEGACION-PARALELA.md
- Inventarios actualizados: DEVENV-MASTER, DEVENV-PORTS
- Documentos de analisis agregados:
  - Analisis y planes de fix student portal
  - Analisis scripts BD
  - Analisis achievements, duplicados, gamification
  - Auditoria documentacion gamilit
  - Backlog discrepancias NEXUS
  - Planes maestros de resolucion
- Reportes de ejecucion agregados
- Knowledge base gamilit README actualizado
- Referencia submodulo gamilit actualizada (commit beb94f7)

Validaciones:
- Plan validado contra directivas SIMCO-GIT
- Dependencias verificadas
- Build gamilit: EXITOSO
2026-01-10 04:51:28 -06:00

659 lines
30 KiB
Markdown

# ANÁLISIS DETALLADO: PÁGINA DE ACHIEVEMENTS - STUDENT PORTAL
**Fecha:** 2026-01-10
**Proyecto:** Gamilit
**Componente:** /achievements (Student Portal)
**Estado:** EN ANÁLISIS
---
## ÍNDICE
1. [Resumen Ejecutivo](#1-resumen-ejecutivo)
2. [Arquitectura del Sistema](#2-arquitectura-del-sistema)
3. [Análisis Frontend](#3-análisis-frontend)
4. [Análisis Backend](#4-análisis-backend)
5. [Análisis Base de Datos](#5-análisis-base-de-datos)
6. [Análisis de Tipos e Interfaces](#6-análisis-de-tipos-e-interfaces)
7. [Flujo de Datos Completo](#7-flujo-de-datos-completo)
8. [Problemas Identificados](#8-problemas-identificados)
9. [Dependencias y Relaciones](#9-dependencias-y-relaciones)
10. [Seeds e Integración](#10-seeds-e-integración)
11. [Recomendaciones](#11-recomendaciones)
---
## 1. RESUMEN EJECUTIVO
### 1.1 Descripción General
El sistema de Achievements es una funcionalidad de gamificación que permite a los estudiantes:
- Ver todos los logros disponibles en el sistema
- Rastrear su progreso hacia cada logro
- Reclamar recompensas (ML Coins, XP) al completar logros
- Filtrar y buscar logros por categoría, rareza y estado
### 1.2 Rutas y Archivos Principales
| Capa | Ruta Principal |
|------|----------------|
| **Página Frontend** | `/home/isem/workspace-v1/projects/gamilit/apps/frontend/src/pages/AchievementsPage.tsx` |
| **Componentes Student** | `/home/isem/workspace-v1/projects/gamilit/apps/frontend/src/apps/student/components/achievements/` |
| **Controller Backend** | `/home/isem/workspace-v1/projects/gamilit/apps/backend/src/modules/gamification/controllers/achievements.controller.ts` |
| **Service Backend** | `/home/isem/workspace-v1/projects/gamilit/apps/backend/src/modules/gamification/services/achievements.service.ts` |
| **DDL Achievements** | `/home/isem/workspace-v1/projects/gamilit/apps/database/ddl/schemas/gamification_system/tables/03-achievements.sql` |
| **DDL User Achievements** | `/home/isem/workspace-v1/projects/gamilit/apps/database/ddl/schemas/gamification_system/tables/04-user_achievements.sql` |
| **Seeds Dev** | `/home/isem/workspace-v1/projects/gamilit/apps/database/seeds/dev/gamification_system/` |
### 1.3 Endpoints API
| Método | Endpoint | Descripción |
|--------|----------|-------------|
| GET | `/api/v1/gamification/achievements` | Obtener todos los achievements |
| GET | `/api/v1/gamification/achievements/:id` | Obtener achievement específico |
| GET | `/api/v1/gamification/users/:userId/achievements` | Obtener achievements del usuario |
| GET | `/api/v1/gamification/users/:userId/achievements/summary` | Resumen estadístico |
| POST | `/api/v1/gamification/users/:userId/achievements/:achievementId/claim` | Reclamar recompensas |
---
## 2. ARQUITECTURA DEL SISTEMA
### 2.1 Diagrama de Capas
```
┌─────────────────────────────────────────────────────────────────────────┐
│ FRONTEND (React + Vite) │
├─────────────────────────────────────────────────────────────────────────┤
│ pages/AchievementsPage.tsx │
│ ├── GamifiedHeader │
│ ├── AchievementFilter │
│ ├── AchievementCard (grid) │
│ └── AchievementModal │
├─────────────────────────────────────────────────────────────────────────┤
│ Hooks │ Store (Zustand) │
│ ├── useAuth │ └── achievementsStore.ts │
│ ├── useUserGamification │ ├── achievements[] │
│ └── useAchievementsEnhanced │ ├── stats │
│ │ └── actions │
├─────────────────────────────────────────────────────────────────────────┤
│ API Layer │
│ ├── achievementsAPI.ts (features/gamification) │
│ └── gamificationApi (shared services) │
└─────────────────────────────────────────────────────────────────────────┘
▼ HTTP
┌─────────────────────────────────────────────────────────────────────────┐
│ BACKEND (NestJS) │
├─────────────────────────────────────────────────────────────────────────┤
│ Controllers │
│ └── achievements.controller.ts │
├─────────────────────────────────────────────────────────────────────────┤
│ Services │
│ └── achievements.service.ts │
│ ├── findAll() │
│ ├── getAllUserAchievements() │
│ ├── grantAchievement() │
│ ├── claimRewards() │
│ └── meetsConditions() │
├─────────────────────────────────────────────────────────────────────────┤
│ Entities │
│ ├── achievement.entity.ts │
│ ├── user-achievement.entity.ts │
│ └── user-stats.entity.ts │
└─────────────────────────────────────────────────────────────────────────┘
▼ TypeORM
┌─────────────────────────────────────────────────────────────────────────┐
│ DATABASE (PostgreSQL) │
├─────────────────────────────────────────────────────────────────────────┤
│ Schema: gamification_system │
│ ├── achievements (tabla maestra) │
│ ├── user_achievements (progreso usuario) │
│ ├── achievement_categories (metadatos UI) │
│ └── user_stats (estadísticas gamificación) │
├─────────────────────────────────────────────────────────────────────────┤
│ Funciones SQL │
│ ├── check_and_award_achievements() │
│ └── claim_achievement_reward() │
├─────────────────────────────────────────────────────────────────────────┤
│ Triggers │
│ └── trg_achievement_unlocked (otorga XP/coins, crea notificación) │
└─────────────────────────────────────────────────────────────────────────┘
```
---
## 3. ANÁLISIS FRONTEND
### 3.1 AchievementsPage.tsx - Componente Principal
**Ubicación:** `/apps/frontend/src/pages/AchievementsPage.tsx`
#### Estados Gestionados
| Estado | Tipo | Propósito |
|--------|------|-----------|
| `allAchievements` | `Achievement[]` | Lista de todos los logros del sistema |
| `userAchievements` | `UserAchievement[]` | Progreso del usuario en cada logro |
| `summary` | `AchievementSummary | null` | Estadísticas resumidas |
| `isLoadingAchievements` | `boolean` | Loading para logros del sistema |
| `isLoadingUserData` | `boolean` | Loading para datos del usuario |
| `error` | `string | null` | Mensajes de error |
| `filter` | `AchievementFilterType` | Filtros activos (categoría, estado, búsqueda, orden) |
| `selectedAchievement` | `Achievement | null` | Logro seleccionado para modal |
| `isModalOpen` | `boolean` | Control de visibilidad del modal |
#### Hooks Utilizados
```typescript
const { user, logout } = useAuth();
const { gamificationData } = useUserGamification(user?.id);
```
#### Llamadas a API
1. **Carga inicial de achievements:**
```typescript
useEffect(() => {
const data = await gamificationApi.getAllAchievements();
setAllAchievements(data);
}, []);
```
2. **Carga de datos del usuario:**
```typescript
useEffect(() => {
const [userAchData, summaryData] = await Promise.all([
gamificationApi.getUserAchievements(user.id),
gamificationApi.getAchievementSummary(user.id).catch(() => null),
]);
}, [user?.id]);
```
3. **Reclamar recompensas:**
```typescript
const handleClaimRewards = async (achievementId: string) => {
const updatedAchievement = await gamificationApi.claimAchievement(user.id, achievementId);
// Update optimista del estado local
};
```
#### Lógica de Negocio (useMemo)
1. **Combinación de datos:** Merge de `allAchievements` + `userAchievements` usando Map
2. **Filtrado:** Por categoría → estado → búsqueda (case-insensitive)
3. **Ordenamiento:** Por nombre, progreso, fecha o rareza
4. **Separación:** Achievements visibles vs. ocultos (isHidden && status === 'locked')
5. **Cálculo de Summary:** Fallback local si API no devuelve summary
### 3.2 Componentes del Student Portal
| Componente | Archivo | Propósito |
|------------|---------|-----------|
| `AchievementsPageHeader` | `AchievementsPageHeader.tsx` | Hero section con estadísticas visuales |
| `AchievementFilters` | `AchievementFilters.tsx` | Controles de filtrado (categoría, rareza, estado, búsqueda) |
| `AchievementGrid` | `AchievementGrid.tsx` | Grid responsivo de cards |
| `AchievementStatistics` | `AchievementStatistics.tsx` | Panel de analytics y breakdown |
| `AchievementDetailModal` | `AchievementDetailModal.tsx` | Modal de detalle con navegación |
| `AchievementCard` | `features/gamification/social/components/` | Card individual reutilizable |
### 3.3 Hook useAchievementsEnhanced
**Ubicación:** `/apps/frontend/src/apps/student/hooks/useAchievementsEnhanced.ts`
Proporciona:
- Filtrado avanzado con debounce (300ms)
- Ordenamiento múltiple
- Navegación entre achievements (prev/next)
- Persistencia de filtros en localStorage
- Cálculo de estadísticas detalladas
---
## 4. ANÁLISIS BACKEND
### 4.1 achievements.controller.ts
**Ubicación:** `/apps/backend/src/modules/gamification/controllers/achievements.controller.ts`
#### Endpoints Implementados
| Decorador | Ruta | Método Service |
|-----------|------|----------------|
| `@Get('achievements')` | `/api/v1/gamification/achievements` | `findAll(includeSecret)` |
| `@Get('achievements/:id')` | `/api/v1/gamification/achievements/:id` | `findById(id)` |
| `@Get('users/:userId/achievements')` | `/api/v1/gamification/users/:userId/achievements` | `getAllUserAchievements(userId)` |
| `@Get('users/:userId/achievements/summary')` | `/api/v1/gamification/users/:userId/achievements/summary` | `getUserAchievementStats(userId)` |
| `@Post('users/:userId/achievements/:achievementId')` | POST grant | `grantAchievement(userId, dto)` |
| `@Post('users/:userId/achievements/:achievementId/claim')` | POST claim | `claimRewards(userId, achievementId)` |
| `@Patch('achievements/:id')` | PATCH toggle | `updateAchievementStatus(id, isActive)` |
### 4.2 achievements.service.ts
**Ubicación:** `/apps/backend/src/modules/gamification/services/achievements.service.ts`
#### Métodos Principales
| Método | Descripción |
|--------|-------------|
| `findAll(includeSecret)` | Obtiene achievements activos (opcionalmente secretos) |
| `findById(id)` | Obtiene achievement por ID con NotFoundException |
| `getAllUserAchievements(userId)` | Obtiene todos los achievements con progreso del usuario |
| `grantAchievement(userId, dto)` | Otorga/actualiza progreso de un achievement |
| `claimRewards(userId, achievementId)` | Reclama recompensas (valida completado y no reclamado) |
| `meetsConditions(userId, userStats, conditions)` | Evalúa si se cumplen condiciones de un achievement |
| `detectAndGrantEarned(userId)` | Detecta y otorga automáticamente achievements |
| `getUserAchievementStats(userId)` | Calcula estadísticas de achievements del usuario |
#### Tipos de Condiciones Soportadas (meetsConditions)
| Tipo | Evaluación |
|------|------------|
| `exercise_completion` | `userStats.exercises_completed >= requirements.exercises_completed` |
| `streak` | `userStats.current_streak >= requirements.consecutive_days` |
| `module_completion` | Query a `progress_tracking.module_progress` |
| `all_modules_completion` | `modules_completed >= X && average_score >= Y` |
| `perfect_score` | `userStats.perfect_scores >= requirements.perfect_exercises` |
| `skill_mastery` | `userStats.perfect_scores >= 10` |
| `exploration` | `modules_completed > 0 || exercises_completed >= 5` |
| `social` | Query a `social_features.classroom_members/friendships` |
| `special` | `first_login` check |
| `module_first_exercise` | Query por `module_code` |
| `exercise_score` | Query por `exercise_type` y `min_score` |
| `exercise_repetition` | Conteo de ejercicios con score mínimo |
| `exercise_speed` | Tiempo de completación |
| `content_analysis` | Análisis de contenido |
| `module_average_score` | Promedio por módulo |
---
## 5. ANÁLISIS BASE DE DATOS
### 5.1 Tabla: gamification_system.achievements
**Ubicación DDL:** `/apps/database/ddl/schemas/gamification_system/tables/03-achievements.sql`
#### Columnas Principales
| Columna | Tipo | Default | Descripción |
|---------|------|---------|-------------|
| `id` | UUID | `gen_random_uuid()` | PK |
| `tenant_id` | UUID | NULL | Multi-tenant |
| `name` | TEXT | - | Nombre del achievement |
| `description` | TEXT | NULL | Descripción |
| `icon` | TEXT | `'trophy'` | Icono/emoji |
| `category` | ENUM | - | achievement_category |
| `rarity` | TEXT | `'common'` | common/rare/epic/legendary |
| `difficulty_level` | ENUM | `'beginner'` | Nivel de dificultad |
| `conditions` | JSONB | `{type, requirements}` | Condiciones para desbloquear |
| `rewards` | JSONB | `{xp, ml_coins, badge}` | Recompensas |
| `ml_coins_reward` | INTEGER | 0 | ML Coins (duplicado de rewards) |
| `is_secret` | BOOLEAN | false | Si está oculto |
| `is_active` | BOOLEAN | true | Si está activo |
| `is_repeatable` | BOOLEAN | false | Si es repetible |
| `order_index` | INTEGER | 0 | Orden en UI |
| `points_value` | INTEGER | 0 | XP (duplicado de rewards) |
#### Índices
- `idx_achievements_active` - WHERE is_active = true
- `idx_achievements_category` - Por categoría
- `idx_achievements_conditions_gin` - GIN para JSONB
- `idx_achievements_secret` - WHERE is_secret = true
### 5.2 Tabla: gamification_system.user_achievements
**Ubicación DDL:** `/apps/database/ddl/schemas/gamification_system/tables/04-user_achievements.sql`
#### Columnas Principales
| Columna | Tipo | Default | Descripción |
|---------|------|---------|-------------|
| `id` | UUID | `gen_random_uuid()` | PK |
| `user_id` | UUID | - | FK a profiles |
| `achievement_id` | UUID | - | FK a achievements |
| `progress` | INTEGER | 0 | Progreso actual |
| `max_progress` | INTEGER | 100 | Progreso máximo |
| `is_completed` | BOOLEAN | false | Si está completado |
| `completion_percentage` | NUMERIC(5,2) | 0.00 | Porcentaje (0-100) |
| `completed_at` | TIMESTAMPTZ | NULL | Fecha de completación |
| `notified` | BOOLEAN | false | Si se notificó |
| `viewed` | BOOLEAN | false | Si se vio la notificación |
| `rewards_claimed` | BOOLEAN | false | Si reclamó recompensas |
| `rewards_received` | JSONB | `{}` | Detalle de recompensas |
| `progress_data` | JSONB | `{}` | Datos de progreso |
| `milestones_reached` | TEXT[] | NULL | Hitos alcanzados |
#### Constraints
- `UNIQUE (user_id, achievement_id)` - Un usuario no puede duplicar logro
- `FK user_id → auth_management.profiles(id) ON DELETE CASCADE`
- `FK achievement_id → gamification_system.achievements(id) ON DELETE CASCADE`
### 5.3 ENUM: achievement_category
**Valores:** `progress`, `streak`, `completion`, `social`, `special`, `mastery`, `exploration`, `collection`, `hidden`
### 5.4 Trigger: trg_achievement_unlocked
**Evento:** AFTER INSERT OR UPDATE en user_achievements
**Acciones:**
1. Verifica si `is_completed = true`
2. Otorga XP a `user_stats.total_xp`
3. Otorga ML Coins con row lock
4. Crea notificación en `notifications.notifications`
5. Marca `notified = true`
---
## 6. ANÁLISIS DE TIPOS E INTERFACES
### 6.1 Tipos Canónicos (SSOT)
**Ubicación:** `/apps/frontend/src/shared/types/achievement.types.ts`
```typescript
type AchievementCategory = 'progress' | 'streak' | 'completion' | 'social' |
'special' | 'mastery' | 'exploration' | 'collection' | 'hidden'
type AchievementStatus = 'locked' | 'in_progress' | 'earned' | 'claimed'
enum AchievementType {
BADGE = 'badge',
MILESTONE = 'milestone',
SPECIAL = 'special',
RANK_PROMOTION = 'rank_promotion',
}
interface Achievement {
id: string;
name: string;
description: string;
icon: string;
category: AchievementCategory;
type: AchievementType;
conditions: AchievementConditionsType;
rewards: AchievementReward;
isHidden: boolean;
rarity?: 'common' | 'rare' | 'epic' | 'legendary';
// ... más campos
}
interface UserAchievement {
id: string;
userId: string;
achievementId: string;
progress: number;
earnedAt?: string;
claimedAt?: string;
achievement: Achievement;
status: AchievementStatus;
}
```
### 6.2 Mapeo Backend → Frontend
| Backend (snake_case) | Frontend (camelCase) | Transformación |
|---------------------|----------------------|----------------|
| `name` | `title` | Mapeo directo |
| `ml_coins_reward` | `mlCoinsReward` | `rewards?.ml_coins ?? ml_coins_reward` |
| `points_value` | `xpReward` | `rewards?.xp ?? points_value` |
| `is_secret` | `isHidden` | Mapeo + categoría |
| `is_completed` | `isUnlocked` | Mapeo directo |
| `completed_at` | `unlockedAt` | Parse a Date |
| `completion_percentage` | Parseado | `parseFloat()` - **¡Backend devuelve string!** |
---
## 7. FLUJO DE DATOS COMPLETO
```
Usuario abre /achievements
┌────────────────────────────────────────────┐
│ AchievementsPage.tsx - useEffect (mount) │
│ ├── gamificationApi.getAllAchievements() │
│ └── gamificationApi.getUserAchievements() │
│ gamificationApi.getAchievementSummary()│
└────────────────────────────────────────────┘
▼ HTTP GET
┌────────────────────────────────────────────┐
│ achievements.controller.ts │
│ ├── @Get('achievements') │
│ └── @Get('users/:userId/achievements') │
└────────────────────────────────────────────┘
┌────────────────────────────────────────────┐
│ achievements.service.ts │
│ ├── findAll() - QueryBuilder │
│ └── getAllUserAchievements() - Repository │
└────────────────────────────────────────────┘
▼ TypeORM
┌────────────────────────────────────────────┐
│ PostgreSQL │
│ ├── gamification_system.achievements │
│ └── gamification_system.user_achievements │
└────────────────────────────────────────────┘
▼ Response
┌────────────────────────────────────────────┐
│ Frontend - Transformación │
│ ├── mapToFrontendAchievement() │
│ └── useMemo combinedAchievements │
└────────────────────────────────────────────┘
▼ Render
┌────────────────────────────────────────────┐
│ UI Components │
│ ├── AchievementCard (grid) │
│ ├── AchievementModal (detalle) │
│ └── AchievementStatistics (analytics) │
└────────────────────────────────────────────┘
```
---
## 8. PROBLEMAS IDENTIFICADOS
### 8.1 CRÍTICOS 🔴
| ID | Problema | Ubicación | Impacto |
|----|----------|-----------|---------|
| **P1** | Función `claim_achievement_reward()` usa columnas inexistentes (`reward_claimed_at`) | DDL functions | Función SQL falla al ejecutarse |
| **P2** | Store usa `\|\|` en lugar de `??` para recompensas (0 es falsy) | achievementsStore.ts:172 | Recompensas de 0 podrían fallar |
| **P3** | Backend `completion_percentage` es STRING, no se parsea en Store | achievementsStore | Tipo incorrecto en runtime |
### 8.2 IMPORTANTES 🟡
| ID | Problema | Ubicación | Impacto |
|----|----------|-----------|---------|
| **P4** | Duplicación de `ml_coins_reward` (columna y JSONB `rewards`) | DDL + Backend | Inconsistencia potencial |
| **P5** | Duplicación de `points_value` (columna) vs `rewards.xp` | DDL + Backend | Inconsistencia potencial |
| **P6** | Alias conflictivo `Achievement` en achievementsTypes.ts | Types | Confusión de tipos |
| **P7** | `unlockedAt` es `string` vs `Date` inconsistente | Múltiples archivos | Bugs de tipo |
| **P8** | Category mapping incompleto (falta 'streak', 'exploration') | achievementsAPI.ts | Categorías mal mapeadas |
| **P9** | ENUM vs Tabla para categorías (2 valores no en tabla) | DDL | Integridad referencial |
### 8.3 MENORES 🟢
| ID | Problema | Ubicación | Impacto |
|----|----------|-----------|---------|
| **P10** | `rarity` es TEXT no ENUM | DDL achievements | Podría tener valores inválidos |
| **P11** | Múltiples timestamps (earnedAt, claimedAt, unlockedAt) | Types | Documentación faltante |
---
## 9. DEPENDENCIAS Y RELACIONES
### 9.1 Dependencias de Tablas
```
gamification_system.achievements
├── FK created_by → auth_management.profiles(id)
└── FK tenant_id → auth_management.tenants(id)
gamification_system.user_achievements
├── FK user_id → auth_management.profiles(id)
└── FK achievement_id → gamification_system.achievements(id)
gamification_system.user_stats
└── FK user_id → auth_management.profiles(id)
```
### 9.2 Dependencias de Servicios (Backend)
El `AchievementsService` realiza queries cruzadas a:
- `progress_tracking.module_progress`
- `progress_tracking.exercise_submissions`
- `educational_content.modules`
- `educational_content.exercises`
- `social_features.classroom_members`
- `social_features.friendships`
### 9.3 Dependencias Frontend
```
AchievementsPage
├── useAuth (auth context)
├── useUserGamification (React Query)
├── gamificationApi (axios client)
├── GamifiedHeader (shared component)
├── AchievementFilter (student component)
├── AchievementCard (feature component)
└── AchievementModal (shared component)
```
---
## 10. SEEDS E INTEGRACIÓN
### 10.1 Seeds de Achievement Categories
**Archivo:** `/apps/database/seeds/dev/gamification_system/01-achievement_categories.sql`
| Nombre | Icono | Orden |
|--------|-------|-------|
| Progreso | 🎯 | 1 |
| Racha | 🔥 | 2 |
| Completación | ✅ | 3 |
| Maestría | 👑 | 4 |
| Exploración | 🔍 | 5 |
| Social | 👥 | 6 |
| Especial | ⭐ | 7 |
### 10.2 Seeds de Achievements
**Archivo:** `/apps/database/seeds/dev/gamification_system/04-achievements.sql`
**Total:** 20 achievements de demostración
| Categoría | Cantidad | Ejemplo |
|-----------|----------|---------|
| Progress | 5 | "Primeros Pasos" (1 ejercicio) → "Maestro de Lectura" (200 ejercicios) |
| Streak | 3 | "Racha 3 Días" → "Racha 30 Días" |
| Completion | 4 | "Módulo 1 Completado" → "Completista Total" |
| Mastery | 3 | "Perfeccionista" (10 ejercicios 100%) |
| Exploration | 2 | "Explorador Curioso" |
| Social | 2 | "Compañero de Aula" |
| Special | 1 | "Primera Visita" |
### 10.3 Seeds de User Achievements
**Archivo:** `/apps/database/seeds/dev/gamification_system/08-user_achievements.sql`
**Usuarios de Demo:** 7 usuarios con achievements pre-asignados para testing
---
## 11. RECOMENDACIONES
### 11.1 Correcciones Críticas (Inmediato)
1. **Reparar función SQL `claim_achievement_reward()`:**
- Cambiar `reward_claimed_at` por `rewards_claimed` (boolean)
- Usar `completed_at` para timestamp
2. **Corregir Store con nullish coalescing:**
```typescript
// ANTES
mlCoinsReward: ach.rewards?.ml_coins || ach.ml_coins_reward || 0,
// DESPUÉS
mlCoinsReward: ach.rewards?.ml_coins ?? ach.ml_coins_reward ?? 0,
```
3. **Parsear completion_percentage en Store:**
```typescript
completionPercentage: typeof ach.completion_percentage === 'string'
? parseFloat(ach.completion_percentage)
: ach.completion_percentage,
```
### 11.2 Mejoras de Consistencia (Corto Plazo)
4. **Unificar fuente de recompensas:**
- Definir `rewards` JSONB como SSOT
- Deprecar columnas `ml_coins_reward` y `points_value`
5. **Estandarizar timestamps:**
- Definir `unlockedAt` como campo canónico
- Siempre como ISO string en API responses
6. **Completar mapeo de categorías:**
```typescript
const categoryMap = {
'streak': 'streak',
'exploration': 'exploration',
'collection': 'collection',
// ... completar
};
```
### 11.3 Mejoras Estructurales (Mediano Plazo)
7. **Convertir `rarity` a ENUM**
8. **Sincronizar ENUM achievement_category con tabla achievement_categories**
9. **Agregar índices para queries comunes del frontend**
10. **Documentar flujo de datos end-to-end**
---
## APÉNDICE A: ARCHIVOS ANALIZADOS
### Frontend
- `/apps/frontend/src/pages/AchievementsPage.tsx`
- `/apps/frontend/src/apps/student/components/achievements/*.tsx`
- `/apps/frontend/src/apps/student/hooks/useAchievementsEnhanced.ts`
- `/apps/frontend/src/features/gamification/social/api/achievementsAPI.ts`
- `/apps/frontend/src/features/gamification/social/store/achievementsStore.ts`
- `/apps/frontend/src/shared/types/achievement.types.ts`
### Backend
- `/apps/backend/src/modules/gamification/controllers/achievements.controller.ts`
- `/apps/backend/src/modules/gamification/services/achievements.service.ts`
- `/apps/backend/src/modules/gamification/entities/achievement.entity.ts`
- `/apps/backend/src/modules/gamification/entities/user-achievement.entity.ts`
- `/apps/backend/src/modules/gamification/dto/achievements/*.ts`
### Database
- `/apps/database/ddl/schemas/gamification_system/tables/03-achievements.sql`
- `/apps/database/ddl/schemas/gamification_system/tables/04-user_achievements.sql`
- `/apps/database/ddl/schemas/gamification_system/tables/10-achievement_categories.sql`
- `/apps/database/ddl/schemas/gamification_system/enums/achievement_category.sql`
- `/apps/database/ddl/schemas/gamification_system/functions/check_and_award_achievements.sql`
- `/apps/database/ddl/schemas/gamification_system/functions/claim_achievement_reward.sql`
- `/apps/database/ddl/schemas/gamification_system/triggers/01-trg_achievement_unlocked.sql`
- `/apps/database/seeds/dev/gamification_system/*.sql`
---
**Documento generado:** 2026-01-10
**Analista:** Claude (Arquitecto Técnico)
**Siguiente Fase:** Planeación basada en análisis detallado