# PLAN DE IMPLEMENTACIÓN: CORRECCIONES ACHIEVEMENTS PAGE **Fecha:** 2026-01-10 **Proyecto:** Gamilit **Componente:** /achievements (Student Portal) **Basado en:** ANALISIS-ACHIEVEMENTS-PAGE-2026-01-10.md **Estado:** EN PLANEACIÓN --- ## RESUMEN DE PROBLEMAS IDENTIFICADOS ### Críticos (Deben corregirse) | ID | Problema | Prioridad | |----|----------|-----------| | P1 | Función SQL `claim_achievement_reward()` usa columnas inexistentes | 🔴 CRÍTICO | | P2 | Store usa `\|\|` en lugar de `??` para recompensas | 🔴 CRÍTICO | | P3 | `completion_percentage` es STRING, no se parsea en Store | 🔴 CRÍTICO | ### Importantes (Deberían corregirse) | ID | Problema | Prioridad | |----|----------|-----------| | P4 | Duplicación de `ml_coins_reward` | 🟡 IMPORTANTE | | P5 | Duplicación de `points_value` vs `rewards.xp` | 🟡 IMPORTANTE | | P6 | Alias conflictivo `Achievement` en achievementsTypes.ts | 🟡 IMPORTANTE | | P7 | `unlockedAt` es string vs Date inconsistente | 🟡 IMPORTANTE | | P8 | Category mapping incompleto | 🟡 IMPORTANTE | --- ## PLAN DE CORRECCIONES ### FASE A: Correcciones Críticas de Base de Datos #### A.1 Reparar función claim_achievement_reward() **Archivo:** `/apps/database/ddl/schemas/gamification_system/functions/claim_achievement_reward.sql` **Cambios requeridos:** 1. Cambiar referencia a `v_user_achievement.reward_claimed_at` por `v_user_achievement.rewards_claimed` 2. Usar `v_user_achievement.completed_at` para verificar timestamp 3. Extraer XP de `v_achievement.rewards->>'xp'` en lugar de `v_achievement.xp_reward` **Verificación:** - [ ] Función compila sin errores - [ ] Test de reclamar recompensa funciona - [ ] No se puede reclamar dos veces --- ### FASE B: Correcciones Críticas de Frontend #### B.1 Corregir achievementsStore.ts - Operador nullish **Archivo:** `/apps/frontend/src/features/gamification/social/store/achievementsStore.ts` **Líneas a modificar:** ~165-180 **Cambios:** ```typescript // ANTES mlCoinsReward: ach.rewards?.ml_coins || ach.ml_coins_reward || 0, xpReward: ach.rewards?.xp || 0, // DESPUÉS mlCoinsReward: ach.rewards?.ml_coins ?? ach.ml_coins_reward ?? 0, xpReward: ach.rewards?.xp ?? ach.points_value ?? 0, ``` **Verificación:** - [ ] Achievements con 0 ML Coins muestran correctamente - [ ] Achievements con 0 XP muestran correctamente - [ ] No hay errores en consola #### B.2 Parsear completion_percentage en Store **Archivo:** `/apps/frontend/src/features/gamification/social/store/achievementsStore.ts` **Cambios:** ```typescript // Agregar transformación al mapear achievements completionPercentage: typeof ach.completion_percentage === 'string' ? parseFloat(ach.completion_percentage) : ach.completion_percentage ?? 0, ``` **Verificación:** - [ ] Porcentaje se muestra correctamente como número - [ ] Progress bars funcionan con valores decimales --- ### FASE C: Mejoras de Consistencia de Tipos #### C.1 Completar mapeo de categorías **Archivo:** `/apps/frontend/src/features/gamification/social/api/achievementsAPI.ts` **Función:** `mapCategory()` **Cambios:** ```typescript function mapCategory(backendCategory: string): FrontendCategory { const categoryMap: Record = { 'educational': 'progress', 'progress': 'progress', 'streak': 'streak', // ← AGREGAR 'mastery': 'mastery', 'skill': 'mastery', 'social': 'social', 'hidden': 'hidden', 'special': 'hidden', 'exploration': 'exploration', // ← AGREGAR 'collection': 'collection', // ← AGREGAR 'completion': 'completion', // ← AGREGAR 'missions': 'progress', }; return categoryMap[backendCategory.toLowerCase()] ?? 'progress'; } ``` **Verificación:** - [ ] Todas las categorías de la BD se mapean correctamente - [ ] Filtros de categoría funcionan - [ ] Badges de categoría muestran correctamente #### C.2 Estandarizar timestamp unlockedAt **Archivos a revisar:** - `/apps/frontend/src/features/gamification/social/types/achievementsTypes.ts` - `/apps/frontend/src/shared/types/achievement.types.ts` **Cambios:** 1. Definir `unlockedAt` como campo canónico (siempre `string | undefined`) 2. Eliminar duplicados (`earnedAt`, `claimedAt` si no se usan) 3. Documentar en comentarios **Verificación:** - [ ] TypeScript no muestra errores de tipo - [ ] Fechas se formatean correctamente en UI --- ### FASE D: Validación de Seeds #### D.1 Verificar consistencia de seeds con DDL **Archivos a verificar:** - `/apps/database/seeds/dev/gamification_system/01-achievement_categories.sql` - `/apps/database/seeds/dev/gamification_system/04-achievements.sql` - `/apps/database/seeds/dev/gamification_system/08-user_achievements.sql` **Checklist:** - [ ] Todas las columnas insertadas existen en DDL - [ ] Tipos de datos coinciden - [ ] FKs apuntan a registros existentes - [ ] Valores de ENUM son válidos - [ ] Estructura de JSONB `conditions` es correcta - [ ] Estructura de JSONB `rewards` es correcta #### D.2 Validar datos de user_achievements **Verificar:** - [ ] `user_id` existe en `auth_management.profiles` - [ ] `achievement_id` existe en `gamification_system.achievements` - [ ] `progress` <= `max_progress` - [ ] `completion_percentage` = `(progress / max_progress) * 100` - [ ] Si `is_completed = true`, entonces `completed_at` no es NULL --- ### FASE E: Testing End-to-End #### E.1 Verificar flujo completo de achievements **Test 1: Carga inicial** - [ ] GET /achievements retorna lista correcta - [ ] GET /users/:id/achievements retorna progreso - [ ] GET /users/:id/achievements/summary retorna estadísticas **Test 2: Filtrado y ordenamiento** - [ ] Filtro por categoría funciona - [ ] Filtro por estado funciona - [ ] Búsqueda por nombre funciona - [ ] Ordenamiento por todos los criterios funciona **Test 3: Reclamar recompensas** - [ ] POST claim funciona para achievements completados - [ ] ML Coins se suman al usuario - [ ] XP se suma al usuario - [ ] No se puede reclamar dos veces **Test 4: UI/UX** - [ ] Cards muestran información correcta - [ ] Modal de detalle muestra progreso - [ ] Achievements ocultos no se muestran si locked - [ ] Achievements ocultos se muestran si unlocked --- ## MATRIZ DE ARCHIVOS A MODIFICAR | Archivo | Fase | Cambio | |---------|------|--------| | `claim_achievement_reward.sql` | A | Reparar columnas | | `achievementsStore.ts` | B | `??` operator + parseFloat | | `achievementsAPI.ts` | C | Category mapping | | `achievementsTypes.ts` | C | Estandarizar timestamps | | `achievement.types.ts` | C | Documentar campos canónicos | --- ## DEPENDENCIAS ENTRE CORRECCIONES ``` ┌─────────────────────────────────────────────────────────────┐ │ FASE A (Base de Datos) │ │ claim_achievement_reward.sql │ └─────────────────────────────────────────────────────────────┘ │ │ (Debe completarse primero para que el backend funcione) ▼ ┌─────────────────────────────────────────────────────────────┐ │ FASE B (Frontend Store) │ │ achievementsStore.ts - nullish coalescing │ │ achievementsStore.ts - parseFloat │ └─────────────────────────────────────────────────────────────┘ │ │ (Store corregido antes de tipos) ▼ ┌─────────────────────────────────────────────────────────────┐ │ FASE C (Tipos y API) │ │ achievementsAPI.ts - category mapping │ │ achievementsTypes.ts - estandarizar timestamps │ └─────────────────────────────────────────────────────────────┘ │ │ (Código corregido antes de validar seeds) ▼ ┌─────────────────────────────────────────────────────────────┐ │ FASE D (Seeds) │ │ Verificar consistencia de datos │ └─────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ FASE E (Testing) │ │ Validación end-to-end │ └─────────────────────────────────────────────────────────────┘ ``` --- ## CRITERIOS DE ACEPTACIÓN ### Para considerar correcciones COMPLETADAS: 1. **Base de Datos:** - [ ] Función `claim_achievement_reward()` ejecuta sin errores - [ ] Trigger `trg_achievement_unlocked` funciona correctamente 2. **Backend:** - [ ] Todos los endpoints responden correctamente - [ ] Tipos de datos son correctos en responses 3. **Frontend:** - [ ] No hay errores en consola del navegador - [ ] TypeScript compila sin errores - [ ] Datos se muestran correctamente en UI 4. **Seeds:** - [ ] Datos de prueba cargan sin errores - [ ] Relaciones FK son válidas 5. **Testing:** - [ ] Flujo completo de achievements funciona - [ ] Reclamar recompensas funciona - [ ] Filtros y búsqueda funcionan --- ## NOTAS IMPORTANTES ### Decisiones de Diseño 1. **Fuente única de verdad para recompensas:** - Se usará `rewards` JSONB como SSOT - Las columnas `ml_coins_reward` y `points_value` se mantienen por compatibilidad - Frontend debe leer primero de `rewards`, luego de columnas 2. **Timestamp canónico:** - `unlockedAt` (frontend) = `completed_at` (backend) - Siempre como string ISO 8601 3. **Category mapping:** - Backend puede enviar cualquier string - Frontend mapea a categorías conocidas - Default: 'progress' ### Riesgos Identificados 1. **Función SQL rota:** Actualmente `claim_achievement_reward()` no funciona. Reclamar recompensas via SQL fallará. 2. **Datos legacy:** Pueden existir registros con `completion_percentage` como string. El frontend debe manejar ambos tipos. 3. **Categorías desconocidas:** Si el backend agrega nuevas categorías, el frontend las mostrará como 'progress' hasta actualizar el mapeo. --- **Documento generado:** 2026-01-10 **Siguiente Fase:** Validación de planeación contra requisitos