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
313 lines
11 KiB
Markdown
313 lines
11 KiB
Markdown
# 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<string, FrontendCategory> = {
|
|
'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
|