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
659 lines
30 KiB
Markdown
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
|