# 🎡 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. ### Integración con el flujo del juego `useSCORM` se inicializa en `App.tsx` al montar la aplicación. Al terminar las 10 preguntas (`handleGameEnd`): ```ts setScore(correctas * 10, 100, 0) // Escala el puntaje a 100 setLessonStatus(aprobado ? 'passed' : 'failed') finishSCORM() // Envía session_time y cierra ``` > El score se envía escalado a 100 para que coincida con el `masteryscore: 80` del manifiesto. --- ## 🧩 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 - [ ] Definir diseño visual final (colores, logo, imágenes del cliente) ## ✅ Resuelto - [x] Integrar `setScore` y `setLessonStatus` con SCORM al finalizar la actividad - [x] Llamar `finishSCORM` al terminar el juego - [x] Configurar `base: './'` en Vite para rutas relativas en Moodle - [x] Rutas de audios relativas (`./audios/...`) para compatibilidad con LMS - [x] Pruebas en LMS real (Moodle)