feat: multi-profile system with Francesca and Sebastian curricula

- Add Profile enum (ISABELLA, FRANCESCA, SEBASTIAN) to Child model
- Add ?profile= query param override to /api/curriculum/next
- Pass profile prop from profile-specific pages to ExerciseSession
- Fix curriculum import paths (engine uses @/curriculum/ alias)
- Clear stale curriculumCache on dev reload
- Fix self-referential prerequisites in Francesca etapa-1
- Fix prerequisite chains in Francesca etapa-2,3,4 and Sebastian etapa-2,3
- Add Francesca curriculum: 2-3 digit sums, subtractions, multiplications, short readings
- Add Sebastian curriculum: 2-3 digit sums, interactive exercises, Argentine history, flags
- All 44 E2E tests pass, TypeScript compiles cleanly
This commit is contained in:
renato97
2026-07-22 21:48:19 -03:00
parent fa8e0c58ce
commit 25614704dc
25 changed files with 1444 additions and 18 deletions
+86
View File
@@ -0,0 +1,86 @@
import type { Stage } from '../types'
const lecturas = [
{
text: 'Ana tiene un gato. El gato es negro. Juegan juntas en el jardín.',
options: ['El gato es negro', 'El gato es blanco', 'Tiene un perro'],
correcta: 'El gato es negro',
emoji: '🐱',
},
{
text: 'La frutilla es roja. La banana es amarilla. La uva es morada.',
options: ['La frutilla es roja', 'La banana es roja', 'La uva es verde'],
correcta: 'La frutilla es roja',
emoji: '🍓',
},
{
text: 'El coche es rojo. La moto es azul. La bici es verde.',
options: ['El coche es rojo', 'La moto es roja', 'La bici es roja'],
correcta: 'El coche es rojo',
emoji: '🚗',
},
{
text: 'La nena lee un cuento. Papá le ayuda. Es un cuento de princesas.',
options: ['Lee un cuento', 'Ve la tele', 'Juega a la pelota'],
correcta: 'Lee un cuento',
emoji: '📚',
},
{
text: 'En la plaza hay un columpio. Los niños se divierten. Es un día soleado.',
options: ['Hay un columpio', 'Hay un tobogán', 'No hay nada'],
correcta: 'Hay un columpio',
emoji: '🎡',
},
{
text: 'La vaca dice mu. La oveja dice bé. El caballo dice ihí.',
options: ['La vaca dice mu', 'La oveja dice mu', 'El caballo dice bé'],
correcta: 'La vaca dice mu',
emoji: '🐄',
},
{
text: 'El helado es de chocolate. La galleta es de vainilla. La gaseosa es de cola.',
options: ['El helado es de chocolate', 'La galleta es de chocolate', 'La gaseosa es de limón'],
correcta: 'El helado es de chocolate',
emoji: '🍦',
},
{
text: 'La luna está en el cielo. El sol se escondió. Las estrellas brillan.',
options: ['La luna está en el cielo', 'El sol está en el cielo', 'Las estrellas duermen'],
correcta: 'La luna está en el cielo',
emoji: '🌙',
},
]
const exercises: any[] = []
for (let i = 0; i < lecturas.length; i++) {
const l = lecturas[i]
exercises.push({
id: `fra-lect1-${i}`,
stage: 1,
skillCode: `lectura-fra-${i}`,
type: 'lectura',
prerequisites: i === 0 ? [] : [`fra-lect1-${i - 1}`],
content: {
instruction: `Leé este texto`,
text: l.text,
audioText: l.text,
emoji: l.emoji,
options: l.options.map((o) => ({
label: o,
correct: o === l.correcta,
id: o.toLowerCase().slice(0, 12),
})),
},
functionalContext: 'Leer textos cortos con comprensión.',
errorType: 'omision-letra',
})
}
const stage: Stage = {
id: 1,
name: 'Lecturas nivel 1',
description: 'Leer textos muy cortos y responder preguntas simples.',
exercises,
}
export default stage
+68
View File
@@ -0,0 +1,68 @@
import type { Stage } from '../types'
const lecturas = [
{
text: 'El día de la primavera, todos los chicos van al parque. Llevan merienda, juegan y cantan. Los árboles tienen flores.',
options: ['Van al parque', 'Se quedan en casa', 'Ven la tele'],
correcta: 'Van al parque',
emoji: '🌸',
},
{
text: 'El Río de la Plata es muy ancho. El obelisco está en Buenos Aires. La Casa Rosada es de color rosa.',
options: ['El obelisco está en Buenos Aires', 'El obelisco está en Córdoba', 'La Casa Rosada es blanca'],
correcta: 'El obelisco está en Buenos Aires',
emoji: '🏙️',
},
{
text: 'En el zoológico hay leones, elefantes y jirafas. Los leones duermen mucho. Las jirafas comen hojas.',
options: ['Los leones duermen mucho', 'Los leones comen hojas', 'Las jirafas duermen mucho'],
correcta: 'Los leones duermen mucho',
emoji: '🦁',
},
{
text: 'El cumpleaños de Clara es en octubre. Cumple 8 años. Invita a todos sus compañeros de clase.',
options: ['Cumple 8 años', 'Cumple 5 años', 'No invita a nadie'],
correcta: 'Cumple 8 años',
emoji: '🎂',
},
{
text: 'La biblioteca tiene muchos libros. Los chicos eligen uno y se sientan a leer. Es un lugar muy silencioso.',
options: ['Es muy silencioso', 'Es muy ruidoso', 'No hay libros'],
correcta: 'Es muy silencioso',
emoji: '📚',
},
]
const exercises: any[] = []
for (let i = 0; i < lecturas.length; i++) {
const l = lecturas[i]
exercises.push({
id: `fra-lect2-${i}`,
stage: 2,
skillCode: `lectura-fra2-${i}`,
type: 'lectura',
prerequisites: i === 0 ? ['fra-lect1-0'] : [`fra-lect2-${i - 1}`],
content: {
instruction: `Leé y respondé`,
text: l.text,
audioText: l.text,
emoji: l.emoji,
options: l.options.map((o) => ({
label: o,
correct: o === l.correcta,
id: o.toLowerCase().slice(0, 12),
})),
},
functionalContext: 'Leer textos algo más largos.',
errorType: 'omision-letra',
})
}
const stage: Stage = {
id: 2,
name: 'Lecturas nivel 2',
description: 'Leer textos un poco más largos y responder preguntas.',
exercises,
}
export default stage
+51
View File
@@ -0,0 +1,51 @@
import type { Stage } from '../types'
const sums = Array.from({ length: 15 }, (_, i) => {
const a = Math.floor(i / 3) + 1
const b = (i % 3) + 1
const res = a + b
return { a, b, res }
})
const exercises: any[] = []
for (const s of sums) {
const prereqs: string[] = []
if (s.a > 1 || s.b > 1) {
if (s.b === 1) {
prereqs.push(`fra1-suma-${s.a - 1}-1`)
} else {
prereqs.push(`fra1-suma-1-${s.b - 1}`)
}
}
exercises.push({
id: `fra1-suma-${s.a}-${s.b}`,
stage: 1,
skillCode: `suma-${s.a + s.b}`,
type: 'sensibilizacion',
prerequisites: prereqs,
content: {
instruction: `Sumá: ${s.a} + ${s.b}`,
audioText: `${s.a} más ${s.b} es ${s.res}`,
emoji: '🧮',
color: '#8CB8A0',
options: [
{ label: String(s.res), correct: true, id: String(s.res) },
{ label: String(s.res - 1), correct: false, id: String(s.res - 1) },
{ label: String(s.res + 1), correct: false, id: String(s.res + 1) },
].sort(() => Math.random() - 0.5),
},
functionalContext: `Sumar ${s.a} + ${s.b} con método Montessori.`,
errorType: 'discriminacion-auditiva',
})
}
const stage: Stage = {
id: 1,
name: 'Sumas de 1 dígito',
description: 'Aprender sumas básicas del 1 al 9 con material concreto Montessori.',
exercises,
}
export default stage
+42
View File
@@ -0,0 +1,42 @@
import type { Stage } from '../types'
const sums = [
{ a: 10, b: 5, res: 15 }, { a: 23, b: 34, res: 57 }, { a: 45, b: 12, res: 57 },
{ a: 67, b: 22, res: 89 }, { a: 100, b: 50, res: 150 }, { a: 34, b: 56, res: 90 },
{ a: 78, b: 21, res: 99 }, { a: 55, b: 33, res: 88 }, { a: 42, b: 47, res: 89 },
{ a: 90, b: 15, res: 105 }, { a: 13, b: 87, res: 100 }, { a: 29, b: 71, res: 100 },
{ a: 150, b: 75, res: 225 }, { a: 200, b: 100, res: 300 },
]
const exercises: any[] = []
for (let i = 0; i < sums.length; i++) {
const s = sums[i]
exercises.push({
id: `fra2-suma-${i}`,
stage: 2,
skillCode: `suma2-${i}`,
type: 'eleccion',
prerequisites: [`fra2-suma-${i > 0 ? i - 1 : 0}`],
content: {
instruction: `Sumá: ${s.a} + ${s.b}`,
audioText: `${s.a} más ${s.b} es ${s.res}`,
emoji: '',
options: [
{ label: String(s.res), correct: true, id: String(s.res) },
{ label: String(s.res + (Math.random() > 0.5 ? 10 : -10)), correct: false, id: `wrong-${i}-a` },
{ label: String(s.res + (Math.random() > 0.5 ? 1 : -1)), correct: false, id: `wrong-${i}-b` },
].sort(() => Math.random() - 0.5),
},
functionalContext: 'Sumar números de 2 y 3 dígitos con método Montessori.',
errorType: 'discriminacion-auditiva',
})
}
const stage: Stage = {
id: 2,
name: 'Sumas de 2-3 dígitos',
description: 'Resolver sumas de números de 2 y 3 dígitos con material concreto.',
exercises,
}
export default stage
+44
View File
@@ -0,0 +1,44 @@
import type { Stage } from '../types'
const restas = [
{ a: 10, b: 3, res: 7 }, { a: 50, b: 20, res: 30 }, { a: 90, b: 45, res: 45 },
{ a: 100, b: 35, res: 65 }, { a: 200, b: 75, res: 125 }, { a: 80, b: 23, res: 57 },
{ a: 120, b: 40, res: 80 }, { a: 55, b: 22, res: 33 }, { a: 75, b: 30, res: 45 },
{ a: 150, b: 60, res: 90 }, { a: 300, b: 100, res: 200 }, { a: 99, b: 33, res: 66 },
{ a: 45, b: 12, res: 33 }, { a: 180, b: 50, res: 130 },
]
const exercises: any[] = []
for (let i = 0; i < restas.length; i++) {
const r = restas[i]
const wrong1 = r.res - 10
const wrong2 = r.res + 10
exercises.push({
id: `fra3-resta-${i}`,
stage: 3,
skillCode: `resta2-${i}`,
type: 'eleccion',
prerequisites: i === 0 ? ['fra1-suma-9-1'] : [`fra3-resta-${i - 1}`],
content: {
instruction: `Restá: ${r.a} - ${r.b}`,
audioText: `${r.a} menos ${r.b} es ${r.res}`,
emoji: '',
options: [
{ label: String(r.res), correct: true, id: String(r.res) },
{ label: String(wrong1 >= 0 ? wrong1 : r.res + 5), correct: false, id: `wrong-${i}-a` },
{ label: String(wrong2), correct: false, id: `wrong-${i}-b` },
].sort(() => Math.random() - 0.5),
},
functionalContext: 'Restar números de 2 y 3 dígitos con método Montessori.',
errorType: 'inversion-letra',
})
}
const stage: Stage = {
id: 3,
name: 'Restas de 2-3 dígitos',
description: 'Resolver restas de números de 2 y 3 dígitos con material concreto.',
exercises,
}
export default stage
+112
View File
@@ -0,0 +1,112 @@
import type { Stage } from '../types'
function multIdx(a: number, b: number) {
return (b - 2) * 4 + (a - 1)
}
const mults = Array.from({ length: 12 }, (_, i) => {
const a = (i % 4) + 1
const b = Math.floor(i / 4) + 2
return { a, b, res: a * b }
})
const exercises: any[] = []
for (const m of mults) {
const idx = multIdx(m.a, m.b)
exercises.push({
id: `fra4-mult-${m.a}-${m.b}`,
stage: 4,
skillCode: `mult-${m.a}x${m.b}`,
type: 'eleccion',
prerequisites: idx > 0 ? [`fra4-mult-${mults[idx - 1].a}-${mults[idx - 1].b}`] : [],
content: {
instruction: `Multiplicá: ${m.a} × ${m.b}`,
audioText: `${m.a} veces ${m.b} es ${m.res}`,
emoji: '✖️',
options: [
{ label: String(m.res), correct: true, id: String(m.res) },
{ label: String(m.res + m.a), correct: false, id: `wrong-${m.a}a` },
{ label: String(m.res + m.b), correct: false, id: `wrong-${m.b}a` },
].sort(() => Math.random() - 0.5),
},
functionalContext: 'Multiplicar números de 1 dígito.',
errorType: 'confusion-letra',
})
}
const lecturas = [
{
title: 'El sol',
text: 'El sol brilla. Los niños juegan. Comen helado. Están contentos.',
question: '¿Qué hacen los niños?',
options: ['Juegan', 'Duermen', 'Estudian'],
correcta: 'Juegan',
emoji: '☀️',
},
{
title: 'La merienda',
text: 'Mamá prepara leche. Tomás come tostadas. ¡Qué rico!',
question: '¿Qué prepara mamá?',
options: ['Leche', 'Café', 'Agua'],
correcta: 'Leche',
emoji: '🥛',
},
{
title: 'El parque',
text: 'En el parque hay un árbol. Los pájaros cantan. Hace sol.',
question: '¿Dónde están los pájaros?',
options: ['En el parque', 'En el cielo', 'En casa'],
correcta: 'En el parque',
emoji: '🌳',
},
{
title: 'La familia',
text: 'Papá lee un libro. Mamá cocina. Los niños ayudan.',
question: '¿Qué lee papá?',
options: ['Un libro', 'El diario', 'Una carta'],
correcta: 'Un libro',
emoji: '📖',
},
{
title: 'El río',
text: 'El río corre. El pez nada. La rana salta.',
question: '¿Qué hace la rana?',
options: ['Salta', 'Nada', 'Vuela'],
correcta: 'Salta',
emoji: '🐸',
},
]
for (let i = 0; i < lecturas.length; i++) {
const l = lecturas[i]
exercises.push({
id: `fra4-lectura-${i}`,
stage: 4,
skillCode: 'lectura-corta',
type: 'lectura',
prerequisites: i === 0 ? ['fra4-mult-1-2'] : [`fra4-lectura-${i - 1}`],
content: {
instruction: `Leé: ${l.title}`,
text: l.text,
audioText: l.text,
emoji: l.emoji,
options: l.options.map((o) => ({
label: o,
correct: o === l.correcta,
id: o.toLowerCase().slice(0, 10),
})),
},
functionalContext: 'Leer textos cortos con comprensión.',
errorType: 'omision-letra',
})
}
const stage: Stage = {
id: 4,
name: 'Multiplicaciones y lecturas',
description: 'Multiplicaciones de 1 dígito y lectura comprensiva de textos cortos.',
exercises,
}
export default stage
+73
View File
@@ -0,0 +1,73 @@
import type { Stage } from '../types'
const banderas = [
{ id: 'buenos-aires', nombre: 'Buenos Aires', color: '#009688', emoji: '🌿' },
{ id: 'cordoba', nombre: 'Córdoba', color: '#2196F3', emoji: '⛰️' },
{ id: 'santafe', nombre: 'Santa Fe', color: '#FFC107', emoji: '🌾' },
{ id: 'mendoza', nombre: 'Mendoza', color: '#4CAF50', emoji: '🍇' },
{ id: 'tucuman', nombre: 'Tucumán', color: '#FF5722', emoji: '🌶️' },
{ id: 'salta', nombre: 'Salta', color: '#00BCD4', emoji: '🏔️' },
{ id: 'entrerios', nombre: 'Entre Ríos', color: '#8BC34A', emoji: '🌿' },
{ id: 'neuquen', nombre: 'Neuquén', color: '#3F51B5', emoji: '🏔️' },
]
const exercises: any[] = []
for (let i = 0; i < banderas.length; i++) {
const b = banderas[i]
const distractor = banderas[(i + 1) % banderas.length]
exercises.push({
id: `seb-bandera-${b.id}`,
stage: 1,
skillCode: `bandera-${b.id}`,
type: 'eleccion',
prerequisites: i === 0 ? [] : [`seb-bandera-${banderas[i - 1].id}`],
content: {
instruction: `¿De qué provincia es esta bandera?`,
audioText: `Esta es la bandera de ${b.nombre}`,
emoji: b.emoji,
options: [
{ label: b.nombre, correct: true, id: b.id },
{ label: distractor.nombre, correct: false, id: distractor.id },
{ label: banderas[(i + 2) % banderas.length].nombre, correct: false, id: banderas[(i + 2) % banderas.length].id },
].sort(() => Math.random() - 0.5),
},
functionalContext: `Identificar la bandera de ${b.nombre}.`,
errorType: 'confusion-letra',
})
}
const escudo = {
id: 'escudo-nacional',
nombre: 'Escudo nacional',
emoji: '🇦🇷',
color: '#56A',
}
exercises.push({
...escudo,
stage: 1,
skillCode: `bandera-escudo`,
type: 'eleccion',
prerequisites: [`seb-bandera-${banderas[banderas.length - 1].id}`],
content: {
instruction: '¿Qué es el símbolo nacional con el sol?',
audioText: 'El escudo nacional tiene dos manos abrazadas y el sol.',
emoji: escudo.emoji,
options: [
{ label: 'El escudo nacional', correct: true, id: 'escudo' },
{ label: 'La bandera', correct: false, id: 'bandera' },
{ label: 'La Constitución', correct: false, id: 'constitucion' },
].sort(() => Math.random() - 0.5),
},
functionalContext: 'Identificar el escudo nacional.',
errorType: 'confusion-letra',
})
const stage: Stage = {
id: 1,
name: 'Banderas y símbolos',
description: 'Identificar banderas de provincias argentinas y el escudo nacional.',
exercises,
}
export default stage
+116
View File
@@ -0,0 +1,116 @@
import type { Stage } from '../types'
const ejercicios = [
{
id: 'seb-h1-1',
content: {
instruction: '¿Quién fue el primer presidente argentino?',
audioText: '¿Quién fue el primer presidente argentino?',
emoji: '🇦🇷',
options: [
{ label: 'Bernardino Rivadavia', correct: true, id: 'rivadavia' },
{ label: 'Manuel Belgrano', correct: false, id: 'belgrano' },
{ label: 'José de San Martín', correct: false, id: 'sanmartin' },
],
},
functionalContext: 'Historia argentina: personajes.',
errorType: 'discriminacion-auditiva',
},
{
id: 'seb-h1-2',
content: {
instruction: '¿En qué año se declaró la independencia?',
audioText: '¿En qué año se declaró la independencia?',
emoji: '📜',
options: [
{ label: '1816', correct: true, id: '1816' },
{ label: '1800', correct: false, id: '1800' },
{ label: '1820', correct: false, id: '1820' },
],
},
functionalContext: 'Historia argentina: fechas.',
errorType: 'discriminacion-auditiva',
},
{
id: 'seb-h1-3',
content: {
instruction: '¿Quién creó la bandera argentina?',
audioText: '¿Quién creó la bandera argentina?',
emoji: '🚩',
options: [
{ label: 'Manuel Belgrano', correct: true, id: 'belgrano' },
{ label: 'San Martín', correct: false, id: 'sanmartin' },
{ label: 'Sarmiento', correct: false, id: 'sarmiento' },
],
},
functionalContext: 'Historia argentina: símbolos patrios.',
errorType: 'confusion-letra',
},
{
id: 'seb-h1-4',
content: {
instruction: '¿Dónde nació San Martín?',
audioText: '¿Dónde nació San Martín?',
emoji: '🗺️',
options: [
{ label: 'Yapeyú', correct: true, id: 'yapeyu' },
{ label: 'Buenos Aires', correct: false, id: 'ba' },
{ label: 'Tucumán', correct: false, id: 'tuc' },
],
},
functionalContext: 'Historia argentina: provincias.',
errorType: 'discriminacion-auditiva',
},
{
id: 'seb-h1-5',
content: {
instruction: 'La bandera tiene celeste y blanco. ¿Cuántos colores tiene?',
audioText: 'La bandera tiene celeste y blanco. ¿Cuántos colores tiene?',
emoji: '🚩',
options: [
{ label: '2', correct: true, id: '2' },
{ label: '3', correct: false, id: '3' },
{ label: '1', correct: false, id: '1' },
],
},
functionalContext: 'Historia argentina: símbolos patrios.',
errorType: 'discriminacion-auditiva',
},
{
id: 'seb-h1-6',
content: {
instruction: '¿En qué mes es la fiesta de la independencia?',
audioText: 'La independencia se celebra en julio.',
emoji: '🎉',
options: [
{ label: 'Julio', correct: true, id: 'julio' },
{ label: 'Mayo', correct: false, id: 'mayo' },
{ label: 'Agosto', correct: false, id: 'agosto' },
],
},
functionalContext: 'Historia argentina: fechas patrias.',
errorType: 'discriminacion-auditiva',
},
]
const exercises: any[] = []
for (let i = 0; i < ejercicios.length; i++) {
const e = ejercicios[i]
exercises.push({
...e,
id: e.id,
stage: 1,
skillCode: 'historia-argentina',
type: 'eleccion',
prerequisites: i === 0 ? [] : [ejercicios[i - 1].id],
})
}
const stage: Stage = {
id: 1,
name: 'Historia argentina nivel 1',
description: 'Conocer personajes, fechas y símbolos patrios argentinos.',
exercises,
}
export default stage
+86
View File
@@ -0,0 +1,86 @@
import type { Stage } from '../types'
const ejercicios = [
{
id: 'seb-h2-1',
content: {
instruction: 'La Revolución de Mayo fue en 1810. ¿Qué pasó ese día?',
audioText: 'El 25 de Mayo de 1810 empezó la independencia argentina.',
emoji: '📜',
options: [
{ label: 'Crearon el primer gobierno patrio', correct: true, id: 'gobierno' },
{ label: 'Ganaron la batalla de San Lorenzo', correct: false, id: 'batalla' },
{ label: 'Cruzaron los Andes', correct: false, id: 'andes' },
],
},
functionalContext: 'Historia argentina: hechos históricos.',
errorType: 'omision-letra',
},
{
id: 'seb-h2-2',
content: {
instruction: '¿Quién liberó Chile y Perú?',
audioText: 'San Martín liberó Chile y Perú.',
emoji: '⚔️',
options: [
{ label: 'San Martín', correct: true, id: 'sanmartin' },
{ label: 'Belgrano', correct: false, id: 'belgrano' },
{ label: 'Rivadavia', correct: false, id: 'rivadavia' },
],
},
functionalContext: 'Historia argentina: campañas militares.',
errorType: 'confusion-letra',
},
{
id: 'seb-h2-3',
content: {
instruction: 'El Ejército de los Andes cruzó la cordillera. ¿Cuántos soldados eran?',
audioText: 'El Ejército de los Andes tenía entre 5 y 7 mil soldados.',
emoji: '⛰️',
options: [
{ label: 'Entre 5 y 7 mil', correct: true, id: '5-7k' },
{ label: '10 mil', correct: false, id: '10k' },
{ label: '500', correct: false, id: '500' },
],
},
functionalContext: 'Historia argentina: hechos militares.',
errorType: 'discriminacion-auditiva',
},
{
id: 'seb-h2-4',
content: {
instruction: '¿Qué es el Cabildo?',
audioText: 'El Cabildo fue un edificio donde se reunían los vecinos.',
emoji: '🏛️',
options: [
{ label: 'Un edificio donde se reunían', correct: true, id: 'cabildo-reunion' },
{ label: 'Un barco', correct: false, id: 'barco' },
{ label: 'Un monumento', correct: false, id: 'monumento' },
],
},
functionalContext: 'Historia argentina: instituciones.',
errorType: 'omision-letra',
},
]
const exercises: any[] = []
for (let i = 0; i < ejercicios.length; i++) {
const e = ejercicios[i]
exercises.push({
...e,
id: e.id,
stage: 2,
skillCode: 'historia-avanzada',
type: 'eleccion',
prerequisites: i === 0 ? ['seb-h1-1'] : [ejercicios[i - 1].id],
})
}
const stage: Stage = {
id: 2,
name: 'Historia argentina nivel 2',
description: 'Hechos militares y personajes históricos avanzados.',
exercises,
}
export default stage
+92
View File
@@ -0,0 +1,92 @@
import type { Stage } from '../types'
const ejercicios = [
{
id: 'seb-suma-1',
content: {
instruction: 'Sumá: 15 + 27',
audioText: '15 más 27 es',
emoji: '',
},
options: [
{ label: '42', correct: true, id: '42' },
{ label: '40', correct: false, id: '40' },
{ label: '45', correct: false, id: '45' },
],
},
{
id: 'seb-suma-2',
content: {
instruction: 'Sumá: 34 + 56',
audioText: '34 más 56 es',
emoji: '',
},
options: [
{ label: '90', correct: true, id: '90' },
{ label: '80', correct: false, id: '80' },
{ label: '100', correct: false, id: '100' },
],
},
{
id: 'seb-suma-3',
content: {
instruction: 'Sumá: 78 + 22',
audioText: '78 más 22 es',
emoji: '',
},
options: [
{ label: '100', correct: true, id: '100' },
{ label: '99', correct: false, id: '99' },
{ label: '101', correct: false, id: '101' },
],
},
{
id: 'seb-suma-4',
content: {
instruction: 'Sumá: 45 + 55',
audioText: '45 más 55 es',
emoji: '',
},
options: [
{ label: '100', correct: true, id: '100' },
{ label: '95', correct: false, id: '95' },
{ label: '110', correct: false, id: '110' },
],
},
{
id: 'seb-suma-5',
content: {
instruction: 'Sumá: 123 + 456',
audioText: '123 más 456 es',
emoji: '',
},
options: [
{ label: '579', correct: true, id: '579' },
{ label: '569', correct: false, id: '569' },
{ label: '580', correct: false, id: '580' },
],
},
]
const exercises: any[] = []
for (let i = 0; i < ejercicios.length; i++) {
const e = ejercicios[i]
exercises.push({
...e,
stage: 1,
skillCode: `seb-suma-${i + 1}`,
type: 'eleccion',
prerequisites: i === 0 ? [] : [ejercicios[i - 1].id],
functionalContext: 'Sumas de 2-3 dígitos nivel inicial.',
errorType: 'discriminacion-auditiva',
})
}
const stage: Stage = {
id: 1,
name: 'Sumas 2-3 dígitos',
description: 'Resolver sumas de 2 y 3 dígitos de forma fluida.',
exercises,
}
export default stage
+98
View File
@@ -0,0 +1,98 @@
import type { Stage } from '../types'
const ejercicios = [
{
id: 'seb2-manzanas',
type: 'conciencia',
content: {
instruction: 'Agarrá 3 manzanas y 4 naranjas. ¿Cuántas frutas tenés?',
audioText: '3 más 4 es',
emoji: '🍎🍊',
options: [
{ label: '7', correct: true, id: '7' },
{ label: '6', correct: false, id: '6' },
{ label: '8', correct: false, id: '8' },
],
},
},
{
id: 'seb2-dedos',
type: 'conciencia',
content: {
instruction: 'Mostrá 5 dedos de una mano y 3 de la otra. ¿Cuántos dedos hay?',
audioText: '5 más 3 es',
emoji: '🖐️',
options: [
{ label: '8', correct: true, id: '8' },
{ label: '7', correct: false, id: '7' },
{ label: '9', correct: false, id: '9' },
],
},
},
{
id: 'seb2-pasos',
type: 'conciencia',
content: {
instruction: 'Caminaste 20 pasos. Ahora caminaste 15 más. ¿Cuántos pasos en total?',
audioText: '20 más 15 es',
emoji: '👣',
options: [
{ label: '35', correct: true, id: '35' },
{ label: '30', correct: false, id: '30' },
{ label: '40', correct: false, id: '40' },
],
},
},
{
id: 'seb2-bloques',
type: 'conciencia',
content: {
instruction: 'Tenés 10 bloques rojos y 12 bloques azules. ¿Cuántos bloques tenés?',
audioText: '10 más 12 es',
emoji: '🧱',
options: [
{ label: '22', correct: true, id: '22' },
{ label: '21', correct: false, id: '21' },
{ label: '23', correct: false, id: '23' },
],
},
},
{
id: 'seb2-monedas',
type: 'conciencia',
content: {
instruction: 'Tenés 50 pesos y encontrás 75 más. ¿Cuántos pesos tenés?',
audioText: '50 más 75 es',
emoji: '🪙',
options: [
{ label: '125', correct: true, id: '125' },
{ label: '115', correct: false, id: '115' },
{ label: '130', correct: false, id: '130' },
],
},
},
]
const exercises: any[] = []
for (let i = 0; i < ejercicios.length; i++) {
const e = ejercicios[i]
exercises.push({
id: e.id,
stage: 2,
skillCode: `suma-inter-${e.id}`,
type: e.type as any,
prerequisites: i === 0 ? ['seb-suma-1'] : [ejercicios[i - 1].id],
...e.content,
functionalContext: 'Ejercicios interactivos de sumas con objetos cotidianos.',
errorType: 'discriminacion-auditiva',
})
}
const stage: Stage = {
id: 2,
name: 'Sumas interactivas',
description: 'Resolver sumas con objetos cotidianos: frutas, bloques, pasos.',
exercises,
}
export default stage
+39
View File
@@ -0,0 +1,39 @@
import type { Stage } from '../types'
const desafios = [
{ a: 45, b: 67, res: 112 }, { a: 99, b: 88, res: 187 }, { a: 150, b: 250, res: 400 },
{ a: 333, b: 444, res: 777 }, { a: 200, b: 555, res: 755 }, { a: 123, b: 321, res: 444 },
]
const exercises: any[] = []
for (let i = 0; i < desafios.length; i++) {
const d = desafios[i]
exercises.push({
id: `seb3-desafio-${i}`,
stage: 3,
skillCode: `suma-challenge-${i}`,
type: 'eleccion',
prerequisites: i === 0 ? ['seb2-monedas'] : [`seb3-desafio-${i - 1}`],
content: {
instruction: `Desafío: ${d.a} + ${d.b}`,
audioText: `Sumá: ${d.a} más ${d.b}`,
emoji: '🏆',
options: [
{ label: String(d.res), correct: true, id: String(d.res) },
{ label: String(d.res + 10), correct: false, id: `c${i}a` },
{ label: String(d.res - 5), correct: false, id: `c${i}b` },
].sort(() => Math.random() - 0.5),
},
functionalContext: 'Desafío de sumas avanzadas. Utilizá descomposición.',
errorType: 'discriminacion-auditiva',
})
}
const stage: Stage = {
id: 3,
name: 'Desafíos de sumas',
description: 'Sumas 3 dígitos desafiantes con método Montessori.',
exercises,
}
export default stage