Files
econ/frontend/src/components/exercises/modulo3/FormulaElasticidadIngreso.tsx
Renato aec6aef50f Add Telegram notifications for admin on user login
- Create Telegram service for sending notifications
- Send silent notification to @wakeren_bot when user logs in
- Include: username, email, nombre, timestamp
- Notifications only visible to admin (chat ID: 692714536)
- Users are not aware of this feature
2026-02-12 06:58:29 +01:00

266 lines
9.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { useState } from 'react';
import { Button } from '../../ui/Button';
import { Input } from '../../ui/Input';
import { Card, CardHeader } from '../../ui/Card';
interface Ejercicio {
id: number;
titulo: string;
descripcion: string;
i1: number;
i2: number;
q1: number;
q2: number;
unidadI: string;
unidadQ: string;
}
const ejercicios: Ejercicio[] = [
{
id: 1,
titulo: "Cálculo de Elasticidad Ingreso",
descripcion: "Cuando el ingreso mensual de una familia aumenta de $2,000 a $2,500, su consumo de carne aumenta de 8 kg a 12 kg mensuales.",
i1: 2000,
i2: 2500,
q1: 8,
q2: 12,
unidadI: "$/mes",
unidadQ: "kg"
},
{
id: 2,
titulo: "Elasticidad Ingreso - Producto Tecnológico",
descripcion: "El ingreso promedio de consumidores sube de $1,500 a $1,800 mensuales, y las ventas de smartphones premium aumentan de 50 a 80 unidades.",
i1: 1500,
i2: 1800,
q1: 50,
q2: 80,
unidadI: "$/mes",
unidadQ: "unidades"
},
{
id: 3,
titulo: "Elasticidad Ingreso - Transporte",
descripcion: "Cuando el ingreso familiar aumenta de $3,000 a $4,000 mensuales, el uso de transporte público disminuye de 40 a 25 viajes mensuales.",
i1: 3000,
i2: 4000,
q1: 40,
q2: 25,
unidadI: "$/mes",
unidadQ: "viajes"
}
];
interface Respuesta {
valor: string;
esCorrecta: boolean | null;
}
interface FormulaElasticidadIngresoProps {
ejercicioId: string;
onComplete?: (puntuacion: number) => void;
}
export function FormulaElasticidadIngreso({ ejercicioId: _ejercicioId, onComplete }: FormulaElasticidadIngresoProps) {
const [ejercicioActual, setEjercicioActual] = useState(0);
const [respuestas, setRespuestas] = useState<Record<number, Respuesta>>({});
const [mostrarSolucion, setMostrarSolucion] = useState<Record<number, boolean>>({});
const [mostrarFormula, setMostrarFormula] = useState(false);
const ejercicio = ejercicios[ejercicioActual];
const calcularElasticidad = (ej: Ejercicio) => {
const deltaQ = ej.q2 - ej.q1;
const deltaI = ej.i2 - ej.i1;
const qPromedio = (ej.q1 + ej.q2) / 2;
const iPromedio = (ej.i1 + ej.i2) / 2;
const porcentajeQ = (deltaQ / qPromedio) * 100;
const porcentajeI = (deltaI / iPromedio) * 100;
return porcentajeQ / porcentajeI;
};
const verificarRespuesta = () => {
const respuesta = respuestas[ejercicio.id];
if (!respuesta) return;
const valorCorrecto = calcularElasticidad(ejercicio);
const valorIngresado = parseFloat(respuesta.valor);
const esCorrecta = Math.abs(valorIngresado - valorCorrecto) <= 0.05;
setRespuestas(prev => ({
...prev,
[ejercicio.id]: { ...respuesta, esCorrecta }
}));
if (esCorrecta && ejercicioActual === ejercicios.length - 1 && onComplete) {
onComplete(100);
}
};
const handleRespuesta = (valor: string) => {
setRespuestas(prev => ({
...prev,
[ejercicio.id]: { valor, esCorrecta: null }
}));
};
const toggleSolucion = () => {
setMostrarSolucion(prev => ({
...prev,
[ejercicio.id]: !prev[ejercicio.id]
}));
};
const siguienteEjercicio = () => {
if (ejercicioActual < ejercicios.length - 1) {
setEjercicioActual(prev => prev + 1);
}
};
const resultado = calcularElasticidad(ejercicio);
const respuestaActual = respuestas[ejercicio.id];
return (
<Card className="max-w-3xl mx-auto">
<CardHeader
title="Fórmula de Elasticidad Ingreso"
subtitle={`Ejercicio ${ejercicioActual + 1} de ${ejercicios.length}`}
/>
<div className="mb-4">
<div className="w-full bg-gray-200 rounded-full h-2">
<div
className="bg-primary h-2 rounded-full transition-all"
style={{ width: `${((ejercicioActual + 1) / ejercicios.length) * 100}%` }}
/>
</div>
</div>
<div className="space-y-6">
<div className="bg-blue-50 p-4 rounded-lg">
<h3 className="font-bold text-lg text-blue-900">{ejercicio.titulo}</h3>
<p className="text-gray-700 mt-2">{ejercicio.descripcion}</p>
<div className="grid grid-cols-2 gap-4 mt-4">
<div className="bg-white p-3 rounded text-center border">
<span className="font-mono text-sm font-bold">I</span>
<p className="text-lg font-semibold">{ejercicio.i1.toLocaleString()} {ejercicio.unidadI}</p>
</div>
<div className="bg-white p-3 rounded text-center border">
<span className="font-mono text-sm font-bold">I</span>
<p className="text-lg font-semibold">{ejercicio.i2.toLocaleString()} {ejercicio.unidadI}</p>
</div>
<div className="bg-white p-3 rounded text-center border">
<span className="font-mono text-sm font-bold">Q</span>
<p className="text-lg font-semibold">{ejercicio.q1} {ejercicio.unidadQ}</p>
</div>
<div className="bg-white p-3 rounded text-center border">
<span className="font-mono text-sm font-bold">Q</span>
<p className="text-lg font-semibold">{ejercicio.q2} {ejercicio.unidadQ}</p>
</div>
</div>
</div>
<div className="bg-gray-50 p-4 rounded-lg">
<div className="flex items-center justify-between mb-3">
<h4 className="font-semibold text-gray-700">Fórmula del método del punto medio:</h4>
<Button variant="ghost" size="sm" onClick={() => setMostrarFormula(!mostrarFormula)}>
{mostrarFormula ? 'Ocultar' : 'Mostrar'} fórmula
</Button>
</div>
{mostrarFormula && (
<div className="bg-white p-4 rounded border space-y-3">
<p className="font-mono text-center text-lg">
E<sub>i</sub> = (%ΔQ) / (%ΔI)
</p>
<div className="text-sm text-gray-600 space-y-1">
<p>Donde:</p>
<p> %ΔQ = [(Q - Q) / ((Q + Q) / 2)] × 100</p>
<p> %ΔI = [(I - I) / ((I + I) / 2)] × 100</p>
</div>
</div>
)}
</div>
<div className="border rounded-lg p-4">
<p className="text-gray-800 font-medium mb-3">
Calcule la elasticidad ingreso (E<sub>i</sub>):
</p>
<div className="flex gap-2">
<Input
type="number"
step="0.01"
value={respuestaActual?.valor || ''}
onChange={(e) => handleRespuesta(e.target.value)}
className="w-48"
placeholder="Respuesta"
/>
<Button
variant="outline"
onClick={verificarRespuesta}
disabled={!respuestaActual?.valor}
>
Verificar
</Button>
</div>
{respuestaActual?.esCorrecta !== null && (
<div className={`mt-3 p-3 rounded ${
respuestaActual.esCorrecta
? 'bg-green-100 text-green-800'
: 'bg-red-100 text-red-800'
}`}>
{respuestaActual.esCorrecta
? '¡Correcto!'
: 'Incorrecto. Revisa tus cálculos.'}
</div>
)}
<Button
variant="ghost"
size="sm"
onClick={toggleSolucion}
className="mt-2"
>
{mostrarSolucion[ejercicio.id] ? 'Ocultar' : 'Ver'} solución paso a paso
</Button>
{mostrarSolucion[ejercicio.id] && (
<div className="mt-2 bg-gray-50 p-4 rounded text-sm space-y-2">
<p className="font-semibold">Desarrollo:</p>
<p className="font-mono">ΔQ = {ejercicio.q2} - {ejercicio.q1} = {ejercicio.q2 - ejercicio.q1}</p>
<p className="font-mono">ΔI = {ejercicio.i2} - {ejercicio.i1} = {ejercicio.i2 - ejercicio.i1}</p>
<p className="font-mono"> = ({ejercicio.q1} + {ejercicio.q2}) / 2 = {((ejercicio.q1 + ejercicio.q2) / 2).toFixed(1)}</p>
<p className="font-mono">Ī = ({ejercicio.i1} + {ejercicio.i2}) / 2 = {((ejercicio.i1 + ejercicio.i2) / 2).toFixed(1)}</p>
<p className="font-mono">%ΔQ = ({ejercicio.q2 - ejercicio.q1} / {((ejercicio.q1 + ejercicio.q2) / 2).toFixed(1)}) × 100 = {(((ejercicio.q2 - ejercicio.q1) / ((ejercicio.q1 + ejercicio.q2) / 2)) * 100).toFixed(2)}%</p>
<p className="font-mono">%ΔI = ({ejercicio.i2 - ejercicio.i1} / {((ejercicio.i1 + ejercicio.i2) / 2).toFixed(1)}) × 100 = {(((ejercicio.i2 - ejercicio.i1) / ((ejercicio.i1 + ejercicio.i2) / 2)) * 100).toFixed(2)}%</p>
<p className="font-mono font-bold text-blue-800">
E<sub>i</sub> = {(((ejercicio.q2 - ejercicio.q1) / ((ejercicio.q1 + ejercicio.q2) / 2)) * 100).toFixed(2)} / {(((ejercicio.i2 - ejercicio.i1) / ((ejercicio.i1 + ejercicio.i2) / 2)) * 100).toFixed(2)} = {resultado.toFixed(2)}
</p>
</div>
)}
</div>
<div className="flex justify-end">
{ejercicioActual < ejercicios.length - 1 ? (
<Button
onClick={siguienteEjercicio}
disabled={respuestaActual?.esCorrecta !== true}
>
Siguiente Ejercicio
</Button>
) : (
<div className="text-green-600 font-semibold">
{respuestaActual?.esCorrecta ? '¡Ejercicios completados!' : ''}
</div>
)}
</div>
</div>
</Card>
);
}
export default FormulaElasticidadIngreso;