Ruleta actvidad completa en React
This commit is contained in:
@@ -0,0 +1,238 @@
|
||||
# 🎡 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
|
||||
<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
|
||||
|
||||
```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)
|
||||
Reference in New Issue
Block a user