# 🎡 Ruleta Interactiva — SCORM 1.2
Actividad e-learning interactiva basada en una ruleta con preguntas de cultura general, construida con React, TypeScript, Tailwind CSS y empaquetada como contenido SCORM 1.2.
---
## 🛠️ Stack Tecnológico
| Tecnología | Versión | Uso |
|---|---|---|
| React | 19 | UI y manejo de estado |
| TypeScript | 5 | Tipado estático |
| Tailwind CSS | 4 | Estilos utilitarios |
| Vite | 6 | Bundler y dev server |
| Howler.js | 2 | Manejo de audio |
| scorm-again | 3 | Simulador SCORM en desarrollo |
---
## 📁 Estructura del Proyecto
```
src/
├── audio/
│ ├── ExtendedHowl.ts # Clase que extiende Howl con eventos de timeupdate (250ms)
│ ├── AudioController.ts # Controlador global de audio + sonidos precargados
│ └── audios.ts # Constantes con rutas de archivos de audio
│
├── components/
│ ├── ui/
│ │ └── Button.tsx # Botón reutilizable (variantes: primary, secondary, outline, danger)
│ ├── layout/
│ │ ├── Header.tsx # Cabecera de la aplicación
│ │ └── Footer.tsx # Pie de página
│ ├── screens/
│ │ ├── Cover.tsx # Pantalla de portada con introducción y botón comenzar
│ │ ├── Game.tsx # Pantalla principal del juego
│ │ └── Result.tsx # Pantalla de cierre (3 estados)
│ └── game/
│ ├── Ruleta/
│ │ └── RuletaSVG.tsx # Ruleta SVG con 4 secciones de colores
│ ├── QuestionModal.tsx # Modal de pregunta con temporizador y feedback
│ └── ResultModal.tsx # Modal interno (reservado)
│
├── data/
│ └── preguntas.json # 4 categorías × 8 preguntas + banco genérico
│
├── hooks/
│ ├── useAudio.ts # Hook para consumir el AudioController
│ ├── useIntentos.ts # Manejo de intentos con persistencia en sessionStorage
│ ├── useRuleta.ts # Lógica completa del juego
│ ├── useSCORM.ts # Hook para inicializar y usar SCORM 1.2
│ └── useScale.ts # Escalado adaptativo 1920×1080
│
├── layouts/
│ └── MainLayout.tsx # Template principal con escalado adaptativo
│
├── scorm/
│ └── scorm.ts # Funciones SCORM 1.2 con fallback a sessionStorage
│
├── types/
│ └── index.ts # Interfaces TypeScript (Pregunta, Categoria, PreguntasData)
│
├── App.tsx # Manejo de pantallas y flujo de intentos
└── main.tsx # Entry point con simulador SCORM en desarrollo
public/
├── audios/
│ ├── click.mp3 # Sonido de clic en botones
│ ├── wheel-spin.mp3 # Sonido al girar la ruleta
│ ├── feedback-correct.mpeg # Sonido de respuesta correcta
│ └── feedback-incorrect.mpeg # Sonido de respuesta incorrecta
├── img/
│ ├── ruleta.png # Imagen de ruleta (respaldo)
│ └── indicador.png # Imagen de indicador (respaldo)
└── imsmanifest.xml # Manifiesto SCORM 1.2 requerido por el LMS
```
---
## 🎮 Flujo de la Actividad
```
Portada (Cover)
│
│ clic en "Comenzar" → consume 1 intento + sonido click
▼
Juego (Game)
│
│ clic en la ruleta → gira + sonido wheel-spin (4s)
▼
Detecta sección (4 categorías × 90°)
│
▼
Modal de Pregunta (temporizador 30s)
├── Respuesta correcta → sonido correct → feedback → círculo de progreso
├── Respuesta incorrecta → sonido incorrect → feedback → círculo de progreso
└── Tiempo agotado → cuenta como incorrecta → feedback
│
│ 10 preguntas respondidas
▼
Pantalla de Resultado
├── ≥ 8 correctas → ¡Felicidades! (sin botón)
├── < 8 correctas + intentos disponibles → "Casi" + botón Reintentar
└── Sin intentos → Mensaje final (persiste al recargar)
```
---
## 🎯 Categorías de Preguntas
| # | Categoría | Color | Preguntas |
|---|---|---|---|
| 1 | 🏛️ Historia | Morado `#7C3AED` | 8 |
| 2 | 🌍 Geografía | Verde `#059669` | 8 |
| 3 | 🔬 Ciencia | Rojo `#DC2626` | 8 |
| 4 | 🎨 Arte y Literatura | Naranja `#D97706` | 8 |
> Las preguntas se seleccionan aleatoriamente sin repetir dentro de cada categoría.
> Si se agotan las preguntas de una categoría, se usa el **banco genérico** (5 preguntas).
---
## 🏆 Criterios de Aprobación
| Criterio | Valor |
|---|---|
| Preguntas por actividad | 10 |
| Mínimo para aprobar | 8 correctas |
| Intentos totales | 3 |
| Tiempo por pregunta | 30 segundos |
| Tiempo agotado | Cuenta como incorrecta |
---
## 🔄 Sistema de Intentos
- El intento se consume al hacer clic en **"Comenzar"**
- Se persiste en `sessionStorage`, por lo que recargar la página **no** recupera el intento
- Al agotar los 3 intentos, la pantalla de cierre final persiste aunque se recargue
---
## 📐 Escalado Adaptativo
El hook `useScale` escala toda la actividad a una resolución base de **1920×1080** manteniendo proporciones en cualquier pantalla:
```ts
const BASE_WIDTH = 1920
const BASE_HEIGHT = 1080
const scale = Math.min(window.innerWidth / BASE_WIDTH, window.innerHeight / BASE_HEIGHT)
```
> Solo orientación horizontal. No aplica diseño para pantallas verticales.
---
## 🔊 Audio
Los audios se precargan al iniciar la aplicación mediante `AudioController`:
```ts
sounds.click?.play() // Botones de navegación
sounds.wheelSpin?.play() // Al girar la ruleta
sounds.correct?.play() // Respuesta correcta
sounds.incorrect?.play() // Respuesta incorrecta
```
---
## 📡 SCORM 1.2
El módulo SCORM (`src/scorm/scorm.ts`) implementa todas las funciones estándar con **fallback automático a `sessionStorage`** cuando no hay LMS disponible:
| Función | CMI Key |
|---|---|
| `initSCORM()` | Inicializa y establece status `incomplete` |
| `getStudentName()` | `cmi.core.student_name` |
| `getLessonStatus()` / `setLessonStatus()` | `cmi.core.lesson_status` |
| `getLessonLocation()` / `setLessonLocation()` | `cmi.core.lesson_location` |
| `getScore()` / `setScore()` | `cmi.core.score.raw/max/min` |
| `getSuspendData()` / `setSuspendData()` | `cmi.suspend_data` |
| `getScormData()` / `setScormData()` | Cualquier clave CMI |
| `finishSCORM()` | Calcula `session_time` en HH:MM:SS y cierra |
| `completeLesson()` | Establece status `completed` |
> En desarrollo, `scorm-again` simula el `window.API` del LMS automáticamente.
---
## 🧩 Componente Button
```tsx
```
---
## 🚀 Comandos
```bash
# Instalar dependencias
npm install
# Desarrollo
npm run dev
# Build para producción
npm run build
# Verificar TypeScript
npx tsc --noEmit
```
---
## 📦 Empaquetado SCORM
Para publicar en un LMS (Moodle, Cornerstone, etc.):
1. Ejecutar `npm run build`
2. Abrir la carpeta `dist/` — el `imsmanifest.xml` se copia automáticamente
3. Seleccionar **todo** el contenido de `dist/` y comprimir en un `.zip`
4. Subir el `.zip` al LMS como paquete SCORM 1.2
> El `masteryscore` está configurado en **80** en el `imsmanifest.xml`.
---
## 📋 Pendientes
- [ ] Reemplazar categorías y preguntas de cultura general por el contenido real del cliente
- [ ] Integrar `setScore` y `completeLesson` con SCORM al finalizar la actividad
- [ ] Pruebas en LMS real (Moodle)
- [ ] Definir diseño visual final (colores, logo, imágenes del cliente)