- 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
415 lines
14 KiB
TypeScript
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;
|