237 lines
9.0 KiB
TypeScript
237 lines
9.0 KiB
TypeScript
import { useState, useCallback } from 'react';
|
|
import { Card, CardHeader } from '../../ui/Card';
|
|
import { Button } from '../../ui/Button';
|
|
import { CheckCircle, RotateCcw, HelpCircle, AlertCircle } from 'lucide-react';
|
|
|
|
interface ClasificacionElasticidadProps {
|
|
ejercicioId: string;
|
|
onComplete?: (puntuacion: number) => void;
|
|
}
|
|
|
|
type TipoElasticidad = 'elastica' | 'unitaria' | 'inelastica';
|
|
|
|
interface EjercicioData {
|
|
ep: number;
|
|
descripcion: string;
|
|
explicacion: string;
|
|
}
|
|
|
|
const ejercicios: EjercicioData[] = [
|
|
{
|
|
ep: -2.5,
|
|
descripcion: 'Un producto tiene una elasticidad precio de -2.5. ¿Cómo se clasifica?',
|
|
explicacion: '|Ep| = 2.5 > 1 → Demanda ELÁSTICA. El % de cambio en cantidad es mayor que el % de cambio en precio.',
|
|
},
|
|
{
|
|
ep: -0.3,
|
|
descripcion: 'La elasticidad precio de un bien es -0.3. ¿Qué tipo de demanda tiene?',
|
|
explicacion: '|Ep| = 0.3 < 1 → Demanda INELÁSTICA. El % de cambio en cantidad es menor que el % de cambio en precio.',
|
|
},
|
|
{
|
|
ep: -1.0,
|
|
descripcion: 'Un artículo tiene elasticidad precio igual a -1. ¿Cómo se clasifica?',
|
|
explicacion: '|Ep| = 1 → Demanda UNITARIA. El % de cambio en cantidad es igual al % de cambio en precio.',
|
|
},
|
|
{
|
|
ep: -0.8,
|
|
descripcion: 'La elasticidad de un medicamento es de -0.8. ¿Qué tipo de elasticidad tiene?',
|
|
explicacion: '|Ep| = 0.8 < 1 → Demanda INELÁSTICA. Los medicamentos suelen ser inelásticos porque son necesidades básicas.',
|
|
},
|
|
{
|
|
ep: -4.2,
|
|
descripcion: 'Un restaurante de lujo tiene elasticidad de -4.2. ¿Cómo se clasifica?',
|
|
explicacion: '|Ep| = 4.2 > 1 → Demanda ELÁSTICA. Los lujos suelen tener demanda muy elástica porque son opcionales.',
|
|
},
|
|
];
|
|
|
|
export function ClasificacionElasticidad({ ejercicioId: _ejercicioId, onComplete }: ClasificacionElasticidadProps) {
|
|
const [ejercicioIndex, setEjercicioIndex] = useState(0);
|
|
const [respuesta, setRespuesta] = useState<TipoElasticidad | null>(null);
|
|
const [validado, setValidado] = useState(false);
|
|
const [aciertos, setAciertos] = useState(0);
|
|
const [completado, setCompletado] = useState(false);
|
|
|
|
const ejercicio = ejercicios[ejercicioIndex];
|
|
|
|
const obtenerRespuestaCorrecta = useCallback((ep: number): TipoElasticidad => {
|
|
const valorAbs = Math.abs(ep);
|
|
if (valorAbs > 1) return 'elastica';
|
|
if (valorAbs < 1) return 'inelastica';
|
|
return 'unitaria';
|
|
}, []);
|
|
|
|
const validarRespuesta = () => {
|
|
if (!respuesta) return;
|
|
|
|
const correcta = obtenerRespuestaCorrecta(ejercicio.ep);
|
|
const esCorrecto = respuesta === correcta;
|
|
|
|
setValidado(true);
|
|
|
|
if (esCorrecto) {
|
|
setAciertos((prev) => prev + 1);
|
|
}
|
|
|
|
if (ejercicioIndex === ejercicios.length - 1) {
|
|
setCompletado(true);
|
|
if (onComplete) {
|
|
const puntuacion = Math.round((aciertos + (esCorrecto ? 1 : 0)) / ejercicios.length * 100);
|
|
onComplete(puntuacion);
|
|
}
|
|
}
|
|
};
|
|
|
|
const siguienteEjercicio = () => {
|
|
if (ejercicioIndex < ejercicios.length - 1) {
|
|
setEjercicioIndex((prev) => prev + 1);
|
|
setRespuesta(null);
|
|
setValidado(false);
|
|
}
|
|
};
|
|
|
|
const reiniciar = () => {
|
|
setEjercicioIndex(0);
|
|
setRespuesta(null);
|
|
setValidado(false);
|
|
setAciertos(0);
|
|
setCompletado(false);
|
|
};
|
|
|
|
const respuestaCorrecta = obtenerRespuestaCorrecta(ejercicio.ep);
|
|
|
|
const opciones: { value: TipoElasticidad; label: string; color: string }[] = [
|
|
{ value: 'elastica', label: 'Elástica (|Ep| > 1)', color: 'bg-green-100 border-green-300 text-green-800' },
|
|
{ value: 'unitaria', label: 'Unitaria (|Ep| = 1)', color: 'bg-yellow-100 border-yellow-300 text-yellow-800' },
|
|
{ value: 'inelastica', label: 'Inelástica (|Ep| < 1)', color: 'bg-blue-100 border-blue-300 text-blue-800' },
|
|
];
|
|
|
|
return (
|
|
<div className="space-y-6">
|
|
<Card>
|
|
<CardHeader
|
|
title="Clasificación de Elasticidad"
|
|
subtitle="Identifica si la demanda es elástica, unitaria o inelástica"
|
|
/>
|
|
|
|
<div className="grid grid-cols-3 gap-4 mb-6">
|
|
<div className="bg-green-50 border-2 border-green-200 rounded-lg p-4 text-center">
|
|
<h4 className="font-bold text-green-800 mb-1">ELÁSTICA</h4>
|
|
<p className="text-2xl font-bold text-green-600">|Ep| > 1</p>
|
|
<p className="text-xs text-green-700 mt-1">%ΔQ > %ΔP</p>
|
|
</div>
|
|
<div className="bg-yellow-50 border-2 border-yellow-200 rounded-lg p-4 text-center">
|
|
<h4 className="font-bold text-yellow-800 mb-1">UNITARIA</h4>
|
|
<p className="text-2xl font-bold text-yellow-600">|Ep| = 1</p>
|
|
<p className="text-xs text-yellow-700 mt-1">%ΔQ = %ΔP</p>
|
|
</div>
|
|
<div className="bg-blue-50 border-2 border-blue-200 rounded-lg p-4 text-center">
|
|
<h4 className="font-bold text-blue-800 mb-1">INELÁSTICA</h4>
|
|
<p className="text-2xl font-bold text-blue-600">|Ep| < 1</p>
|
|
<p className="text-xs text-blue-700 mt-1">%ΔQ < %ΔP</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="mb-6">
|
|
<div className="flex items-center gap-2 mb-3">
|
|
<span className="bg-primary text-white text-xs font-bold px-2 py-1 rounded">
|
|
{ejercicioIndex + 1}/{ejercicios.length}
|
|
</span>
|
|
<h4 className="font-medium text-gray-700">Pregunta:</h4>
|
|
</div>
|
|
<div className="bg-gray-50 rounded-lg p-4">
|
|
<p className="text-gray-800 mb-3">{ejercicio.descripcion}</p>
|
|
<p className="text-lg font-bold text-primary">
|
|
E<sub>p</sub> = {ejercicio.ep}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="space-y-3 mb-6">
|
|
{opciones.map((opcion) => (
|
|
<button
|
|
key={opcion.value}
|
|
onClick={() => {
|
|
setRespuesta(opcion.value);
|
|
setValidado(false);
|
|
}}
|
|
disabled={validado}
|
|
className={`w-full p-4 rounded-lg border-2 text-left transition-all ${
|
|
respuesta === opcion.value
|
|
? opcion.color
|
|
: 'bg-white border-gray-200 hover:border-gray-300'
|
|
} ${validado && respuestaCorrecta === opcion.value ? 'ring-2 ring-success' : ''}`}
|
|
>
|
|
<div className="flex items-center justify-between">
|
|
<span className="font-medium">{opcion.label}</span>
|
|
{validado && respuestaCorrecta === opcion.value && (
|
|
<CheckCircle className="w-5 h-5 text-success" />
|
|
)}
|
|
{validado && respuesta === opcion.value && respuesta !== respuestaCorrecta && (
|
|
<AlertCircle className="w-5 h-5 text-error" />
|
|
)}
|
|
</div>
|
|
</button>
|
|
))}
|
|
</div>
|
|
|
|
{validado && (
|
|
<div className="bg-blue-50 border-l-4 border-blue-500 p-4 mb-6">
|
|
<h4 className="font-medium text-blue-900 mb-1 flex items-center gap-2">
|
|
<HelpCircle className="w-4 h-4" />
|
|
Explicación:
|
|
</h4>
|
|
<p className="text-blue-800">{ejercicio.explicacion}</p>
|
|
</div>
|
|
)}
|
|
|
|
<div className="flex gap-3">
|
|
{!validado ? (
|
|
<Button onClick={validarRespuesta} variant="primary" disabled={!respuesta}>
|
|
<CheckCircle className="w-4 h-4 mr-2" />
|
|
Validar Respuesta
|
|
</Button>
|
|
) : ejercicioIndex < ejercicios.length - 1 ? (
|
|
<Button onClick={siguienteEjercicio} variant="primary">
|
|
Siguiente Ejercicio
|
|
</Button>
|
|
) : (
|
|
<Button onClick={reiniciar} variant="primary">
|
|
<RotateCcw className="w-4 h-4 mr-2" />
|
|
Reiniciar
|
|
</Button>
|
|
)}
|
|
</div>
|
|
|
|
{completado && (
|
|
<div className="mt-4 p-4 bg-success/10 border border-success rounded-lg">
|
|
<p className="text-success font-medium text-center">
|
|
¡Completado! Has acertado {aciertos + (respuesta === respuestaCorrecta ? 1 : 0)} de{' '}
|
|
{ejercicios.length} ejercicios
|
|
</p>
|
|
</div>
|
|
)}
|
|
</Card>
|
|
|
|
<Card className="bg-purple-50 border-purple-200">
|
|
<h4 className="font-semibold text-purple-900 mb-2">Interpretación Económica:</h4>
|
|
<ul className="space-y-2 text-sm text-purple-800">
|
|
<li>
|
|
<strong>Elástica (|Ep| > 1):</strong> Los consumidores son muy sensibles al precio.
|
|
Un cambio de precio genera un cambio proporcionalmente mayor en cantidad demandada.
|
|
</li>
|
|
<li>
|
|
<strong>Unitaria (|Ep| = 1):</strong> Sensibilidad proporcional.
|
|
El gasto total de los consumidores se mantiene constante ante cambios de precio.
|
|
</li>
|
|
<li>
|
|
<strong>Inelástica (|Ep| < 1):</strong> Los consumidores son poco sensibles al precio.
|
|
La cantidad demandada cambia menos que proporcionalmente al precio.
|
|
</li>
|
|
</ul>
|
|
</Card>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export default ClasificacionElasticidad;
|