Files
econ/frontend/src/components/exercises/modulo1/FactoresProduccionQuiz.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

415 lines
14 KiB
TypeScript

import { useState } from 'react';
import { motion, AnimatePresence } from 'framer-motion';
import { Card } from '../../ui/Card';
import { Button } from '../../ui/Button';
import { CheckCircle, XCircle, Trophy, RotateCcw, ArrowRight, Mountain, Users, Factory, Lightbulb } from 'lucide-react';
interface FactoresProduccionQuizProps {
ejercicioId: string;
onComplete?: (puntuacion: number) => void;
}
interface Pregunta {
id: number;
pregunta: string;
tipo: 'tierra' | 'trabajo' | 'capital' | 'emprendimiento';
opciones: string[];
respuestaCorrecta: number;
explicacion: string;
}
const PREGUNTAS: Pregunta[] = [
{
id: 1,
pregunta: '¿Cuál de los siguientes es un ejemplo de TIERRA como factor de producción?',
tipo: 'tierra',
opciones: [
'El trabajo de un obrero',
'Un terreno agrícola',
'Una máquina industrial',
'La habilidad de un gerente'
],
respuestaCorrecta: 1,
explicacion: 'La tierra incluye todos los recursos naturales: terrenos, minerales, agua, petróleo, etc. Es todo lo que nos proporciona la naturaleza sin transformar.'
},
{
id: 2,
pregunta: 'El TRABAJO como factor de producción se refiere a:',
tipo: 'trabajo',
opciones: [
'Solo el esfuerzo físico',
'Solo el esfuerzo mental',
'El esfuerzo físico y mental que aportan las personas',
'Las máquinas que reemplazan a los humanos'
],
respuestaCorrecta: 2,
explicacion: 'El trabajo incluye tanto el esfuerzo físico (como el de un albañil) como el mental (como el de un ingeniero). Es el factor humano en la producción.'
},
{
id: 3,
pregunta: '¿Qué se considera CAPITAL como factor de producción?',
tipo: 'capital',
opciones: [
'Dinero en una cuenta bancaria',
'Acciones de una empresa',
'Maquinaria, herramientas y equipos utilizados para producir',
'Terrenos y edificios'
],
respuestaCorrecta: 2,
explicacion: 'En economía, el capital físico (o capital real) son los bienes manufacturados utilizados para producir otros bienes: máquinas, herramientas, fábricas, etc. No es dinero.'
},
{
id: 4,
pregunta: '¿Cuál es la recompensa que reciben los propietarios del factor TIERRA?',
tipo: 'tierra',
opciones: [
'Salarios',
'Rentas o alquileres',
'Intereses',
'Beneficios'
],
respuestaCorrecta: 1,
explicacion: 'Los propietarios de tierra reciben RENTAS (o alquileres) como pago por el uso de sus recursos naturales.'
},
{
id: 5,
pregunta: 'Los trabajadores reciben _____ como recompensa por su factor de producción.',
tipo: 'trabajo',
opciones: [
'Intereses',
'Rentas',
'Salarios',
'Dividendos'
],
respuestaCorrecta: 2,
explicacion: 'El trabajo recibe SALARIOS (o sueldos) como compensación por el esfuerzo físico y mental aportado a la producción.'
},
{
id: 6,
pregunta: '¿Qué reciben los propietarios de CAPITAL como recompensa?',
tipo: 'capital',
opciones: [
'Salarios',
'Rentas',
'Intereses',
'Bonificaciones'
],
respuestaCorrecta: 2,
explicacion: 'El capital recibe INTERESES como recompensa. Si prestas tu capital (maquinaria o dinero para comprarla), recibes intereses a cambio.'
},
{
id: 7,
pregunta: 'El EMPRENDIMIENTO (o empresa) es el factor que:',
tipo: 'emprendimiento',
opciones: [
'Solo invierte dinero',
'Combina los otros factores de producción asumiendo riesgos',
'Trabaja en la fábrica',
'Solo vende los productos'
],
respuestaCorrecta: 1,
explicacion: 'El emprendimiento es el factor que organiza y combina tierra, trabajo y capital para producir bienes y servicios, asumiendo el riesgo del negocio.'
},
{
id: 8,
pregunta: '¿Cuál es la recompensa del EMPRENDIMIENTO?',
tipo: 'emprendimiento',
opciones: [
'Salario fijo',
'Intereses garantizados',
'Beneficios (o pérdidas)',
'Renta del terreno'
],
respuestaCorrecta: 2,
explicacion: 'El emprendimiento recibe BENEFICIOS cuando la empresa tiene éxito, pero también puede sufrir PÉRDIDAS. Es el factor con mayor riesgo y potencial de ganancia.'
}
];
export function FactoresProduccionQuiz({ ejercicioId: _ejercicioId, onComplete }: FactoresProduccionQuizProps) {
const [preguntaActual, setPreguntaActual] = useState(0);
const [respuestaSeleccionada, setRespuestaSeleccionada] = useState<number | null>(null);
const [mostrarResultado, setMostrarResultado] = useState(false);
const [puntuacion, setPuntuacion] = useState(0);
const [completado, setCompletado] = useState(false);
const [respuestasCorrectas, setRespuestasCorrectas] = useState(0);
const pregunta = PREGUNTAS[preguntaActual];
const getTipoIcon = (tipo: string) => {
switch (tipo) {
case 'tierra':
return <Mountain size={20} />;
case 'trabajo':
return <Users size={20} />;
case 'capital':
return <Factory size={20} />;
case 'emprendimiento':
return <Lightbulb size={20} />;
default:
return null;
}
};
const getTipoLabel = (tipo: string) => {
switch (tipo) {
case 'tierra':
return 'Tierra';
case 'trabajo':
return 'Trabajo';
case 'capital':
return 'Capital';
case 'emprendimiento':
return 'Emprendimiento';
default:
return '';
}
};
const getTipoColor = (tipo: string) => {
switch (tipo) {
case 'tierra':
return 'bg-green-100 text-green-700 border-green-200';
case 'trabajo':
return 'bg-blue-100 text-blue-700 border-blue-200';
case 'capital':
return 'bg-amber-100 text-amber-700 border-amber-200';
case 'emprendimiento':
return 'bg-purple-100 text-purple-700 border-purple-200';
default:
return 'bg-gray-100 text-gray-700 border-gray-200';
}
};
const handleSeleccionar = (index: number) => {
if (mostrarResultado) return;
setRespuestaSeleccionada(index);
};
const handleVerificar = () => {
if (respuestaSeleccionada === null) return;
const esCorrecta = respuestaSeleccionada === pregunta.respuestaCorrecta;
setMostrarResultado(true);
if (esCorrecta) {
setPuntuacion(prev => prev + Math.round(100 / PREGUNTAS.length));
setRespuestasCorrectas(prev => prev + 1);
}
if (preguntaActual === PREGUNTAS.length - 1) {
setTimeout(() => {
setCompletado(true);
const puntuacionFinal = puntuacion + (esCorrecta ? Math.round(100 / PREGUNTAS.length) : 0);
if (onComplete) {
onComplete(puntuacionFinal);
}
}, 2000);
}
};
const handleSiguiente = () => {
setPreguntaActual(prev => prev + 1);
setRespuestaSeleccionada(null);
setMostrarResultado(false);
};
const handleReiniciar = () => {
setPreguntaActual(0);
setRespuestaSeleccionada(null);
setMostrarResultado(false);
setPuntuacion(0);
setCompletado(false);
setRespuestasCorrectas(0);
};
if (completado) {
return (
<Card className="w-full max-w-2xl mx-auto">
<div className="text-center py-8 px-4">
<motion.div
initial={{ scale: 0 }}
animate={{ scale: 1 }}
className="inline-flex items-center justify-center w-20 h-20 bg-gradient-to-br from-yellow-400 to-yellow-600 rounded-full mb-6"
>
<Trophy size={40} className="text-white" />
</motion.div>
<h3 className="text-2xl font-bold text-gray-900 mb-2">
¡Quiz Completado!
</h3>
<p className="text-gray-600 mb-6">
Has respondido {respuestasCorrectas} de {PREGUNTAS.length} preguntas correctamente
</p>
<div className="grid grid-cols-2 gap-4 max-w-md mx-auto mb-6">
<div className={`p-4 rounded-xl border ${getTipoColor('tierra')}`}>
<Mountain className="mx-auto mb-2" size={24} />
<p className="font-semibold text-sm">Tierra</p>
<p className="text-xs opacity-75">Rentas</p>
</div>
<div className={`p-4 rounded-xl border ${getTipoColor('trabajo')}`}>
<Users className="mx-auto mb-2" size={24} />
<p className="font-semibold text-sm">Trabajo</p>
<p className="text-xs opacity-75">Salarios</p>
</div>
<div className={`p-4 rounded-xl border ${getTipoColor('capital')}`}>
<Factory className="mx-auto mb-2" size={24} />
<p className="font-semibold text-sm">Capital</p>
<p className="text-xs opacity-75">Intereses</p>
</div>
<div className={`p-4 rounded-xl border ${getTipoColor('emprendimiento')}`}>
<Lightbulb className="mx-auto mb-2" size={24} />
<p className="font-semibold text-sm">Emprendimiento</p>
<p className="text-xs opacity-75">Beneficios</p>
</div>
</div>
<div className="bg-blue-50 rounded-xl p-6 mb-6">
<p className="text-sm text-blue-600 mb-1">Puntuación Total</p>
<p className="text-4xl font-bold text-blue-700">{puntuacion}</p>
<p className="text-sm text-blue-500">puntos</p>
</div>
<Button onClick={handleReiniciar} variant="outline">
<RotateCcw size={16} className="mr-2" />
Intentar de Nuevo
</Button>
</div>
</Card>
);
}
return (
<Card className="w-full max-w-3xl mx-auto">
<div className="p-6">
<div className="flex items-center justify-between mb-6">
<div>
<h3 className="text-xl font-bold text-gray-900">Factores de Producción</h3>
<p className="text-sm text-gray-500">Pregunta {preguntaActual + 1} de {PREGUNTAS.length}</p>
</div>
<div className="text-right">
<p className="text-xs text-gray-500">Puntos</p>
<p className="text-xl font-bold text-blue-600">{puntuacion}</p>
</div>
</div>
<div className="w-full bg-gray-200 rounded-full h-2 mb-6">
<motion.div
className="h-2 bg-blue-500 rounded-full"
initial={{ width: 0 }}
animate={{ width: `${((preguntaActual + 1) / PREGUNTAS.length) * 100}%` }}
/>
</div>
<div className="mb-6">
<div className={`inline-flex items-center gap-2 px-3 py-1 rounded-full text-xs font-semibold mb-3 border ${getTipoColor(pregunta.tipo)}`}>
{getTipoIcon(pregunta.tipo)}
<span>{getTipoLabel(pregunta.tipo)}</span>
</div>
<h4 className="text-lg font-medium text-gray-800">
{pregunta.pregunta}
</h4>
</div>
<div className="space-y-3 mb-6">
{pregunta.opciones.map((opcion, index) => {
const estaSeleccionada = respuestaSeleccionada === index;
const esCorrecta = index === pregunta.respuestaCorrecta;
const mostrarCorrecta = mostrarResultado && esCorrecta;
const mostrarIncorrecta = mostrarResultado && estaSeleccionada && !esCorrecta;
return (
<motion.button
key={index}
onClick={() => handleSeleccionar(index)}
disabled={mostrarResultado}
whileHover={!mostrarResultado ? { scale: 1.01 } : {}}
whileTap={!mostrarResultado ? { scale: 0.99 } : {}}
className={`w-full p-4 rounded-xl border-2 text-left transition-all ${
mostrarCorrecta
? 'border-green-500 bg-green-50'
: mostrarIncorrecta
? 'border-red-500 bg-red-50'
: estaSeleccionada
? 'border-blue-500 bg-blue-50'
: 'border-gray-200 bg-white hover:border-blue-300'
}`}
>
<div className="flex items-center gap-3">
<div className={`w-6 h-6 rounded-full border-2 flex items-center justify-center ${
mostrarCorrecta
? 'border-green-500 bg-green-500 text-white'
: mostrarIncorrecta
? 'border-red-500 bg-red-500 text-white'
: estaSeleccionada
? 'border-blue-500 bg-blue-500 text-white'
: 'border-gray-300'
}`}>
{mostrarCorrecta && <CheckCircle size={14} />}
{mostrarIncorrecta && <XCircle size={14} />}
{!mostrarResultado && estaSeleccionada && (
<div className="w-2 h-2 bg-white rounded-full" />
)}
</div>
<span className={`font-medium ${
mostrarCorrecta ? 'text-green-800' :
mostrarIncorrecta ? 'text-red-800' :
'text-gray-700'
}`}>
{opcion}
</span>
</div>
</motion.button>
);
})}
</div>
<AnimatePresence>
{mostrarResultado && (
<motion.div
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -10 }}
className={`p-4 rounded-xl mb-6 ${
respuestaSeleccionada === pregunta.respuestaCorrecta
? 'bg-green-50 border border-green-200'
: 'bg-red-50 border border-red-200'
}`}
>
<p className={`font-medium mb-2 ${
respuestaSeleccionada === pregunta.respuestaCorrecta
? 'text-green-800'
: 'text-red-800'
}`}>
{respuestaSeleccionada === pregunta.respuestaCorrecta
? '¡Correcto!'
: 'Incorrecto'}
</p>
<p className="text-sm text-gray-700">{pregunta.explicacion}</p>
</motion.div>
)}
</AnimatePresence>
<div className="flex justify-end">
{!mostrarResultado ? (
<Button
onClick={handleVerificar}
disabled={respuestaSeleccionada === null}
>
Verificar Respuesta
</Button>
) : preguntaActual < PREGUNTAS.length - 1 ? (
<Button onClick={handleSiguiente}>
Siguiente
<ArrowRight size={16} className="ml-2" />
</Button>
) : null}
</div>
</div>
</Card>
);
}
export default FactoresProduccionQuiz;