auto complete alumnos
This commit is contained in:
@@ -0,0 +1,299 @@
|
||||
'use client';
|
||||
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { CuestionarioSeccion } from '@/types/responder-formulario';
|
||||
import RadioOptionGroup, { RadioOption } from '@/components/radio-option-group';
|
||||
import Input from '@/components/input';
|
||||
import Button from '@/components/button';
|
||||
import { validarRespuesta } from '@/utils/validador';
|
||||
import toast from 'react-hot-toast';
|
||||
import axiosInstance from '@/utils/api-config';
|
||||
import { useRouter } from 'next/navigation';
|
||||
|
||||
export default function Formulario({
|
||||
id_formulario,
|
||||
secciones,
|
||||
}: {
|
||||
id_formulario: number;
|
||||
secciones: CuestionarioSeccion[];
|
||||
}) {
|
||||
const router = useRouter();
|
||||
const [esDeFES, setEsDeFES] = useState<boolean | null>(null);
|
||||
const [cuenta, setCuenta] = useState('');
|
||||
const [datosAuto, setDatosAuto] = useState<null | {
|
||||
nombre: string;
|
||||
apellidos: string;
|
||||
correo: string;
|
||||
}>(null);
|
||||
const [respuestas, setRespuestas] = useState<Record<string, string>>({});
|
||||
const [isSubmitting, setIsSubmitting] = useState(false); // New state for submission
|
||||
|
||||
const actualizarRespuesta = (idPregunta: string, valor: string) => {
|
||||
setRespuestas((prev) => ({ ...prev, [idPregunta]: valor }));
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (esDeFES && cuenta.length === 9) {
|
||||
fetch(`/datos/alumnos/${cuenta}`)
|
||||
.then((res) => res.json())
|
||||
.then((data) => setDatosAuto(data))
|
||||
.catch((err) => {
|
||||
console.error('Error al obtener datos del alumno:', err);
|
||||
setDatosAuto(null);
|
||||
});
|
||||
}
|
||||
}, [cuenta, esDeFES]);
|
||||
|
||||
// Encuentra preguntas por texto o validación
|
||||
const todasPreguntas = secciones.flatMap((s) =>
|
||||
s.preguntas.map((p) => p.pregunta)
|
||||
);
|
||||
const preguntaFES = todasPreguntas.find(
|
||||
(p) => p.pregunta === '¿Eres parte de la comunidad de la FES Acatlán?'
|
||||
);
|
||||
const preguntaCuenta = todasPreguntas.find(
|
||||
(p) => p.validacion === 'cuenta_alumno'
|
||||
);
|
||||
const preguntasRestantes = todasPreguntas.filter(
|
||||
(p) =>
|
||||
![preguntaFES?.id_pregunta, preguntaCuenta?.id_pregunta].includes(
|
||||
p.id_pregunta
|
||||
)
|
||||
);
|
||||
const respuestaFES = respuestas[`pregunta_${preguntaFES?.id_pregunta}`];
|
||||
|
||||
const formatearRespuestas = async () => {
|
||||
if (!validarRespuestasObligatorias()) return;
|
||||
|
||||
const respuestasArray = Object.entries(respuestas).flatMap(
|
||||
([key, valor]) => {
|
||||
const idNumerico = Number(key.replace('pregunta_', ''));
|
||||
const pregunta = secciones
|
||||
.flatMap((s) => s.preguntas)
|
||||
.find((p) => p.pregunta.id_pregunta === idNumerico);
|
||||
|
||||
const tipo = pregunta?.pregunta.tipo_pregunta.tipo_pregunta;
|
||||
|
||||
// Si es múltiple, convertir cada valor individual si corresponde
|
||||
if (Array.isArray(valor)) {
|
||||
return valor.map((v) => ({
|
||||
id_pregunta: idNumerico,
|
||||
valor: tipo === 'Cerrada' || tipo === 'Multiple' ? Number(v) : v,
|
||||
}));
|
||||
}
|
||||
|
||||
return [
|
||||
{
|
||||
id_pregunta: idNumerico,
|
||||
valor: tipo === 'Cerrada' ? Number(valor) : valor,
|
||||
},
|
||||
];
|
||||
}
|
||||
);
|
||||
|
||||
const correoEntry = respuestasArray.find(
|
||||
(r) =>
|
||||
typeof r.valor === 'string' &&
|
||||
/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(r.valor)
|
||||
);
|
||||
|
||||
const resultado = {
|
||||
id_formulario: Number(id_formulario),
|
||||
correo: correoEntry?.valor || 'correo@no-encontrado.com',
|
||||
respuestas: respuestasArray,
|
||||
fecha_envio: new Date().toISOString(),
|
||||
};
|
||||
|
||||
console.log('Resultado a enviar:', resultado);
|
||||
|
||||
setIsSubmitting(true);
|
||||
toast
|
||||
.promise(
|
||||
axiosInstance.post('/cuestionario-respondido/submit', resultado),
|
||||
{
|
||||
loading: 'Enviando formulario...',
|
||||
success: 'Formulario enviado con éxito!',
|
||||
error: 'Error al enviar el formulario.',
|
||||
}
|
||||
)
|
||||
.then(() => router.push('/'))
|
||||
.finally(() => setIsSubmitting(false));
|
||||
};
|
||||
|
||||
const validarRespuestasObligatorias = (): boolean => {
|
||||
const faltantes: number[] = [];
|
||||
const errores: { id: number; mensaje: string }[] = [];
|
||||
console.log('Validando respuestas:', respuestas);
|
||||
|
||||
secciones.forEach((seccion) => {
|
||||
seccion.preguntas.forEach(({ pregunta }) => {
|
||||
const id = pregunta.id_pregunta;
|
||||
const valor = respuestas[`pregunta_${id}`];
|
||||
const tipo = pregunta.tipo_pregunta.tipo_pregunta;
|
||||
const validacion = pregunta.validacion;
|
||||
|
||||
const respondida =
|
||||
(tipo === 'Abierta' &&
|
||||
typeof valor === 'string' &&
|
||||
valor.trim() !== '') ||
|
||||
(tipo === 'Cerrada' && valor !== undefined) ||
|
||||
(tipo === 'Multiple' && Array.isArray(valor) && valor.length > 0);
|
||||
|
||||
if (!respondida && pregunta.obligatoria) {
|
||||
faltantes.push(id);
|
||||
}
|
||||
|
||||
if (tipo === 'Abierta' && validacion && typeof valor === 'string') {
|
||||
const resultado = validarRespuesta(valor, validacion);
|
||||
if (!resultado.valido) {
|
||||
errores.push({ id, mensaje: resultado.mensaje });
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
if (faltantes.length > 0) {
|
||||
console.log('Faltan preguntas obligatorias:', faltantes);
|
||||
toast.error('Responde todas las preguntas obligatorias antes de enviar.');
|
||||
return false;
|
||||
}
|
||||
|
||||
if (errores.length > 0) {
|
||||
toast.error(
|
||||
`Corrige las respuestas con error de formato:\n${errores
|
||||
.map((e) => `• ${e.mensaje}`)
|
||||
.join('\n')}`
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
return (
|
||||
<form className="container py-4">
|
||||
{/* Pregunta inicial */}
|
||||
{preguntaFES && (
|
||||
<div className="mb-4">
|
||||
<label className="form-label">{preguntaFES.pregunta}</label>
|
||||
<RadioOptionGroup
|
||||
name="comunidad_fes"
|
||||
options={preguntaFES.opciones.map((op) => ({
|
||||
label: op.opcion.opcion,
|
||||
value: op.id_opcion,
|
||||
}))}
|
||||
selectedValue={respuestaFES ? Number(respuestaFES) : undefined}
|
||||
onChange={(opt) => {
|
||||
const idSeleccionado = opt.value;
|
||||
const opcionSeleccionada = preguntaFES.opciones.find(
|
||||
(o) => o.id_opcion === idSeleccionado
|
||||
);
|
||||
|
||||
const valorTexto = opcionSeleccionada?.opcion.opcion ?? '';
|
||||
setEsDeFES(valorTexto === 'Si'); // Se actualiza esDeFES con el texto
|
||||
setCuenta('');
|
||||
setDatosAuto(null);
|
||||
|
||||
actualizarRespuesta(
|
||||
`pregunta_${preguntaFES.id_pregunta}`,
|
||||
String(idSeleccionado)
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Si dijo que sí, pedir número de cuenta */}
|
||||
{esDeFES && preguntaCuenta && (
|
||||
<Input
|
||||
key={preguntaCuenta.id_pregunta}
|
||||
label={preguntaCuenta.pregunta}
|
||||
name={`pregunta_${preguntaCuenta.id_pregunta}`}
|
||||
value={cuenta}
|
||||
onChange={(e) => {
|
||||
const value = e.target.value;
|
||||
setCuenta(value);
|
||||
actualizarRespuesta(
|
||||
`pregunta_${preguntaCuenta.id_pregunta}`,
|
||||
value
|
||||
); // ✅ Guarda la respuesta
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* Si ya tiene datos o dijo que no es de la FES, mostrar el resto */}
|
||||
{(esDeFES === false || (esDeFES && datosAuto)) &&
|
||||
preguntasRestantes.map((p) => {
|
||||
if (p.tipo_pregunta.tipo_pregunta === 'Abierta') {
|
||||
let defaultValue = '';
|
||||
let disabled = false;
|
||||
|
||||
if (esDeFES && datosAuto) {
|
||||
if (p.validacion === 'nombre') {
|
||||
defaultValue = p.pregunta.toLowerCase().includes('apellido')
|
||||
? datosAuto.apellidos
|
||||
: datosAuto.nombre;
|
||||
disabled = true;
|
||||
}
|
||||
if (p.validacion === 'correo') {
|
||||
defaultValue = datosAuto.correo;
|
||||
disabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Input
|
||||
key={p.id_pregunta}
|
||||
label={p.pregunta}
|
||||
name={`pregunta_${p.id_pregunta}`}
|
||||
defaultValue={defaultValue}
|
||||
disabled={disabled}
|
||||
onChange={(e) =>
|
||||
actualizarRespuesta(
|
||||
`pregunta_${p.id_pregunta}`,
|
||||
e.target.value
|
||||
)
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
if (p.tipo_pregunta.tipo_pregunta === 'Cerrada') {
|
||||
const opciones: RadioOption<number>[] = p.opciones.map(
|
||||
(op) => ({
|
||||
label: op.opcion.opcion,
|
||||
value: op.id_opcion,
|
||||
})
|
||||
);
|
||||
|
||||
const respuestaActual = respuestas[`pregunta_${p.id_pregunta}`];
|
||||
|
||||
return (
|
||||
<div key={p.id_pregunta}>
|
||||
<label className="form-label">{p.pregunta}</label>
|
||||
<RadioOptionGroup
|
||||
name={`pregunta_${p.id_pregunta}`}
|
||||
options={opciones}
|
||||
selectedValue={
|
||||
respuestaActual ? Number(respuestaActual) : undefined
|
||||
} // ✅ Aquí se fija la opción
|
||||
onChange={(opt) =>
|
||||
actualizarRespuesta(
|
||||
`pregunta_${p.id_pregunta}`,
|
||||
String(opt.value)
|
||||
)
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
})}
|
||||
|
||||
{/* Botón de enviar */}
|
||||
<Button onClick={formatearRespuestas} disabled={isSubmitting}>
|
||||
{isSubmitting ? 'Enviando...' : 'Enviar'}
|
||||
</Button>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user