🎡 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:

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:

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

<Button variant="primary" size="lg" onClick={fn}>Texto</Button>
<Button variant="secondary" size="md">Texto</Button>
<Button variant="outline" size="sm">Texto</Button>
<Button variant="danger" disabled>Texto</Button>

🚀 Comandos

# 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)
S
Description
No description provided
Readme 24 MiB
Languages
TypeScript 96.9%
JavaScript 2.5%
HTML 0.5%
CSS 0.1%