diff --git a/src/app/(admins)/user/layout.tsx b/src/app/(admins)/user/layout.tsx index 239525a..948611a 100644 --- a/src/app/(admins)/user/layout.tsx +++ b/src/app/(admins)/user/layout.tsx @@ -1,6 +1,8 @@ +'use client' +import dynamic from 'next/dynamic'; import Footer from '@/components/layout/footer'; import Header from '@/components/layout/header'; -import Navbar from '@/components/navbar'; +const Navbar = dynamic(() => import('@/components/navbar'), { ssr: false }); import React from 'react'; export default function Layout({ children }: { children: React.ReactNode }) { diff --git a/src/app/(admins)/user/qr/page.tsx b/src/app/(admins)/user/qr/page.tsx index d29168d..974a3f8 100644 --- a/src/app/(admins)/user/qr/page.tsx +++ b/src/app/(admins)/user/qr/page.tsx @@ -10,34 +10,30 @@ import toast from 'react-hot-toast'; const Modal = dynamic(() => import('@/components/modal'), { ssr: false }); export default function Page() { - const [scannedData, setScannedData] = useState<{ - id_participante: number; - id_cuestionario: number; - } | null>(null); - const [showModal, setShowModal] = useState(false); const [enableScan, setEnableScan] = useState(true); const [statusMessage, setStatusMessage] = useState(''); const [participante, setParticipante] = useState(''); + const [message, setMessage] = useState(''); + const [token, setToken] = useState(''); + const [tokenValid, setTokenValid] = useState(false); const handleScan = async (rawValue: string) => { console.log('QR Escaneado:', rawValue); if (!enableScan) return; try { - const data = JSON.parse(rawValue); - if (data.id_participante && data.id_cuestionario) { + if (rawValue) { try { - const response = await axiosInstance.get( - `/participante-evento/${data.id_participante}/${data.id_cuestionario}` + await axiosInstance.post( + `/participante-evento/decode-token`, { + token: rawValue + } ); - setParticipante(response.data.participante.correo); - setScannedData({ - id_participante: data.id_participante, - id_cuestionario: data.id_cuestionario, - }); + setToken(rawValue); + setTokenValid(true); setShowModal(true); setEnableScan(false); } catch (err) { @@ -56,13 +52,17 @@ export default function Page() { }; const handleConfirm = async () => { - if (!scannedData) return; + if (!tokenValid) return; try { const response = await axiosInstance.post( - `/participante-evento/asistencia/${scannedData.id_participante}/${scannedData.id_cuestionario}` + `/participante-evento/asistencia`, { + token: token + } ); + setParticipante(response.data.data.participante); + setMessage(response.data.message); console.log('Asistencia registrada:', response.data); toast.success('✅ Asistencia registrada correctamente'); } catch (err) { @@ -72,7 +72,6 @@ export default function Page() { setShowModal(false); setEnableScan(true); - setScannedData(null); }; return ( @@ -124,6 +123,24 @@ export default function Page() { + {participante && ( +
+
+ + Ultimo participante registrado: {participante} +
+
+ )} + + {message && ( +
+
+ + {message} +
+
+ )} + - + } diff --git a/src/containers/edit-formulario.tsx b/src/containers/edit-formulario.tsx index 3d7da8f..720a618 100644 --- a/src/containers/edit-formulario.tsx +++ b/src/containers/edit-formulario.tsx @@ -23,6 +23,7 @@ export default function EditFormulario({ evento, handleChange, }: EditFormularioProps) { + const tipo_usuario = sessionStorage.getItem('axxxd')?.replace(/"/g, '') || ''; const [loading, setLoading] = useState(false); const [nuevoBanner, setNuevoBanner] = useState(null); @@ -213,11 +214,13 @@ export default function EditFormulario({ {/* Botón de guardar */} -
- -
+ {tipo_usuario === "administrador" && ( +
+ +
+ )} diff --git a/src/containers/formulario/formulario-registro-old.tsx b/src/containers/formulario/formulario-registro-old.tsx new file mode 100644 index 0000000..67149c4 --- /dev/null +++ b/src/containers/formulario/formulario-registro-old.tsx @@ -0,0 +1,591 @@ +import React, { useState, useEffect } from 'react'; +import { useGetApi } from '@/hooks/use-get-api'; +import { GetCuestionario } from '@/types/evento'; +import SimpleInput from '@/components/input'; +import RadioOptionGroup, { RadioOption } from '@/components/radio-option-group'; +import axiosInstance from '@/utils/api-config'; +import PrefetchAbiertaCorta from './prefetch-abierta-corta'; +import Button from '@/components/button'; +import toast from 'react-hot-toast'; +import { useRouter } from 'next/navigation'; +import { getAxiosError } from '@/utils/errors-utils'; +import { SubmitResponse } from '@/types/submit'; + +export type UsuarioDataResponse = { + cuenta: string | null; + nombre: string | null; + apellidos: string | null; + carrera: string | null; + genero: string | null; + rfc?: string | null; // Solo para trabajadores +}; + +type UsuarioData = { + nombre: string; // alumno o trabajador + apellidos: string; // alumno o trabajador + correo: string; // alumno o trabajador (en los alumnos se usa el id_ncuenta en trabajadores ellos deben escribirlo) + genero: 'M' | 'F'; // alumno o trabajador + carrera: string; // solo para alumnos + rfc?: string; // solo para trabajadores se usa para buscar información de cuenta +}; + +const confirmativeOption = ['Sí', 'Si', 'Si, claro', 'Claro que sí']; +const negativeOption = ['No', 'No, gracias', 'No, de momento']; + +type RespuestaFormulario = Record; // id_pregunta: respuesta + +export default function FormularioRegistro({ + id_cuestionario, + handleSubmitFormulario, +}: { + evento: string; + cuestionario: string; + id_evento: number; + id_cuestionario: number; + handleSubmitFormulario: (data: SubmitResponse) => void; +}) { + // ------------------------ + // Estados + // ------------------------ + const [isComunidad, setIsComunidad] = useState>(); + const [respuestas, setRespuestas] = useState({}); + const [cuentaInfo, setCuentaInfo] = useState(null); + const [isSending, setIsSending] = useState(false); + const router = useRouter(); + + // ------------------------ + // Carga del cuestionario + // ------------------------ + const { data, error } = useGetApi( + `/cuestionario/${id_cuestionario}/formulario` + ); + // ------------------------ + // Extracción de preguntas clave + // ------------------------ + const preguntas = data?.cuestionario.secciones.flatMap((seccion) => + seccion.preguntas.map((pregunta) => pregunta.pregunta) + ); + + const preguntaComunidad = preguntas?.find( + (pregunta) => + pregunta.validacion === 'comunidad_alumno' || + pregunta.validacion === 'comunidad_trabajador' + ); + + const preguntaCuenta = preguntas?.find( + (pregunta) => + pregunta.validacion === 'cuenta_alumno' || + pregunta.validacion === 'cuenta_trabajador' || + pregunta.validacion === 'rfc' + ); + + // Solución: Agregar estado para controlar si ya se hizo la búsqueda + const [cuentaBuscada, setCuentaBuscada] = useState(''); // Nuevo estado + + // ------------------------ + // Efecto: buscar información de cuenta + // ------------------------ + useEffect(() => { + const cuentaId = preguntaCuenta?.id_pregunta ?? ''; + const cuenta = respuestas[cuentaId]; + + const esCuentaAlumno = preguntaCuenta?.validacion === 'cuenta_alumno'; + const esCuentaTrabajador = + preguntaCuenta?.validacion === 'cuenta_trabajador' || + preguntaCuenta?.validacion === 'rfc'; + + const puedeBuscar = + cuenta && + typeof cuenta === 'string' && + ((esCuentaAlumno && cuenta.length === 9) || + (esCuentaTrabajador && cuenta.length === 10)); + + const fetchCuentaInfo = async () => { + try { + const endpoint = esCuentaAlumno + ? `/alumnos/${cuenta}` + : `/trabajadores/${cuenta}`; + + const res = await axiosInstance.get(endpoint); + + console.log('Datos de cuenta obtenidos:', res.data); + + // Verificar si realmente hay datos válidos + const hayDatosValidos = + res.data.nombre || + res.data.apellidos || + res.data.carrera || + res.data.genero; + + if (hayDatosValidos) { + setCuentaInfo({ + nombre: res.data.nombre ? res.data.nombre : '', + apellidos: res.data.apellidos ? res.data.apellidos : '', + correo: res.data.cuenta + ? `${res.data.cuenta}@pcpuma.acatlan.unam.mx` + : '', + genero: + res.data.genero === 'M' || res.data.genero === 'F' + ? res.data.genero + : 'M', + carrera: res.data.carrera ? res.data.carrera : '', + }); + } else { + // Si no hay datos válidos, establecer como null + setCuentaInfo(null); + } + + // Marcar que ya se buscó esta cuenta + setCuentaBuscada(cuenta as string); + } catch (error) { + console.error('Error al obtener datos de cuenta:', error); + setCuentaInfo(null); + setCuentaBuscada(cuenta as string); // También marcar en caso de error + } + }; + + // Solo buscar si puede buscar Y no se ha buscado ya esta cuenta + if (puedeBuscar && cuentaBuscada !== cuenta) { + fetchCuentaInfo(); + } else if (!puedeBuscar) { + setCuentaInfo(null); + setCuentaBuscada(''); // Reset cuando no se puede buscar + } + }, [ + respuestas, + preguntaCuenta?.id_pregunta, + preguntaCuenta?.validacion, + cuentaBuscada, + ]); + + // También resetear cuentaBuscada cuando cambie el número de cuenta + useEffect(() => { + const cuentaId = preguntaCuenta?.id_pregunta ?? ''; + const cuenta = respuestas[cuentaId]; + + // Si la cuenta cambió, resetear el estado de búsqueda + if (cuentaBuscada && cuenta !== cuentaBuscada) { + setCuentaBuscada(''); + } + }, [respuestas, preguntaCuenta?.id_pregunta, cuentaBuscada]); + + // ------------------------ + // Efecto: precargar respuestas si hay cuentaInfo + // ------------------------ + useEffect(() => { + if (!cuentaInfo) { + if (data?.cuestionario.secciones) { + const preguntas = data.cuestionario.secciones.flatMap((seccion) => + seccion.preguntas.map((pregunta) => pregunta.pregunta) + ); + + const idsPrefetch = preguntas + .filter((pregunta) => + [ + 'nombre', + 'apellidos', + 'correo', + 'institucion', + 'carrera', + 'genero', + ].includes(pregunta.validacion) + ) + .map((pregunta) => pregunta.id_pregunta); + + setRespuestas((prev) => { + const nuevas = { ...prev }; + idsPrefetch.forEach((id) => { + delete nuevas[id]; + }); + return nuevas; + }); + } + return; + } + + const nuevasRespuestas: RespuestaFormulario = {}; + + if (data?.cuestionario.secciones) { + const preguntas = data.cuestionario.secciones.flatMap((seccion) => + seccion.preguntas.map((pregunta) => pregunta.pregunta) + ); + + for (const pregunta of preguntas) { + const id = pregunta.id_pregunta; + switch (pregunta.validacion) { + case 'nombre': + nuevasRespuestas[id] = cuentaInfo.nombre; + break; + case 'apellidos': + nuevasRespuestas[id] = cuentaInfo.apellidos; + break; + case 'correo': + nuevasRespuestas[id] = cuentaInfo.correo; + break; + case 'institucion': + nuevasRespuestas[id] = 'FES Acatlán'; + break; + case 'carrera': + nuevasRespuestas[id] = cuentaInfo.carrera; + break; + case 'genero': + const generoTexto = + cuentaInfo.genero === 'M' ? 'Masculino' : 'Femenino'; + const opcion = pregunta.opciones?.find( + (op) => + op.opcion.opcion.toLowerCase() === generoTexto.toLowerCase() + ); + if (opcion) { + nuevasRespuestas[id] = String(opcion.id_opcion); + } + break; + } + } + + setRespuestas((prev) => ({ + ...prev, + ...nuevasRespuestas, + })); + } + }, [cuentaInfo, data?.cuestionario.secciones]); + + // ------------------------ + // Funciones auxiliares + // ------------------------ + const actualizarRespuesta = (idPregunta: string, valor: string) => { + setRespuestas((prev) => ({ ...prev, [idPregunta]: valor })); + }; + + const handleOnSubmit = async () => { + setIsSending(true); + if (!data?.cuestionario.id_cuestionario) { + alert('No se ha cargado correctamente el formulario'); + return; + } + + const preguntasObligatorias = + data?.cuestionario.secciones.flatMap((seccion) => + seccion.preguntas + .filter((pregunta) => pregunta.pregunta.obligatoria) + .map((pregunta) => pregunta.pregunta) + ) || []; + + const faltantes = preguntasObligatorias.filter( + (pregunta) => + respuestas[pregunta.id_pregunta] === undefined || + respuestas[pregunta.id_pregunta] === '' || + respuestas[pregunta.id_pregunta] === null + ); + + const preguntaCorreo = preguntasObligatorias.find( + (pregunta) => + pregunta.validacion === 'correo' || + pregunta.validacion === 'correo_institucional' + ); + + if (faltantes.length > 0) { + toast.error('Por favor responde todas las preguntas obligatorias.'); + setIsSending(false); + return; + } + + // Buscar un posible correo en las respuestas si no hay cuentaInfo + let correo = + cuentaInfo?.correo === respuestas[preguntaCorreo?.id_pregunta ?? ''] + ? cuentaInfo?.correo + : respuestas[preguntaCorreo?.id_pregunta ?? '']; + + if (!correo) { + // Buscar en las respuestas una que parezca correo + const correoRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; + for (const valor of Object.values(respuestas)) { + if (typeof valor === 'string' && correoRegex.test(valor)) { + correo = valor; + break; + } + } + } + + const payload = { + id_cuestionario: id_cuestionario, + correo: correo || '', + fecha_envio: new Date().toISOString(), + respuestas: Object.entries(respuestas).map(([id_pregunta, valor]) => ({ + id_pregunta: Number(id_pregunta), + valor: isNaN(Number(valor)) ? valor : Number(valor), + })), + }; + + try { + const res = await axiosInstance.post( + '/cuestionario-respondido/submit', + payload + ); + toast( + () => ( + + {res.data.message.includes('ya ha respondido') ? ( + <> + + Respuestas Enviadas Con Éxito + + ) : ( + <> + + {res.data.message || '¡Formulario enviado!'} + + )} + + ), + { + duration: 6000, + position: 'bottom-center', + style: { + maxWidth: '425px', + }, + } + ); + + handleSubmitFormulario(res.data); + } catch (error) { + console.log(getAxiosError(error)); + toast.error('Hubo un error al enviar el formulario', { + duration: 5000, + }); + router.push('/'); + } + }; + + // ------------------------ + // Condición para mostrar el formulario + // ------------------------ + const mostrarFormularioRestante = + !preguntaComunidad || // Si no hay pregunta de comunidad, mostrar el formulario + (isComunidad && + confirmativeOption.some((opt) => + isComunidad.label?.toLowerCase().includes(opt.toLowerCase()) + ) && + !!cuentaInfo) || + (isComunidad && + negativeOption.some((opt) => + isComunidad.label?.toLowerCase().includes(opt.toLowerCase()) + )); + + if (error) { + console.error('Error al cargar el cuestionario:', error); + return
Error al cargar el cuestionario
; + } + + return ( +
+ {preguntaComunidad && ( +
+ + ({ + label: op.opcion.opcion, + value: op.id_opcion, + }))} + selectedValue={isComunidad?.value} + onChange={(opt) => { + console.log('Opción comunidad seleccionada:', opt); + setIsComunidad(opt); + actualizarRespuesta( + preguntaComunidad.id_pregunta.toString(), + String(opt.value) + ); + }} + /> +
+ )} + {isComunidad && + confirmativeOption.some((opt) => + isComunidad.label?.toLowerCase().includes(opt.toLowerCase()) + ) && + preguntaCuenta && ( +
+ + + setRespuestas((prev) => ({ + ...prev, + [preguntaCuenta.id_pregunta]: e.target.value, + })) + } + /> +
+ )} + {mostrarFormularioRestante && + data?.cuestionario.secciones.map((seccion, i) => ( +
+ {seccion.preguntas.map((pregunta) => { + // Evitar renderizar las preguntas ya manejadas + if ( + pregunta.pregunta.id_pregunta === + preguntaComunidad?.id_pregunta || + pregunta.pregunta.id_pregunta === preguntaCuenta?.id_pregunta + ) + return null; + + if ( + pregunta.pregunta.tipo_pregunta.tipo_pregunta === + 'Abierta (Respuesta corta)' + ) { + return ( + + ); + } + + if ( + pregunta.pregunta.tipo_pregunta.tipo_pregunta === + 'Abierta (Parrafo)' + ) { + return ( +
+ + + setRespuestas((prev) => ({ + ...prev, + [pregunta.pregunta.id_pregunta]: e.target.value, + })) + } + /> +
+ ); + } + + if (pregunta.pregunta.tipo_pregunta.tipo_pregunta === 'Cerrada') { + const opciones: RadioOption[] = + pregunta.pregunta.opciones.map((op) => ({ + label: op.opcion.opcion, + value: op.id_opcion, + })); + let respuestaActual = + respuestas[`${pregunta.pregunta.id_pregunta}`]; + + if ( + (!preguntaComunidad || + (confirmativeOption.includes(isComunidad?.label || '') && + cuentaInfo)) && + respuestaActual === undefined // solo si no ha respondido + ) { + if (pregunta.pregunta.validacion === 'genero' && cuentaInfo) { + const generoTexto = + cuentaInfo.genero === 'M' ? 'Masculino' : 'Femenino'; + + const opcionGenero = pregunta.pregunta.opciones.find( + (op) => + op.opcion.opcion.toLowerCase() === + generoTexto.toLowerCase() + ); + if (opcionGenero) { + respuestaActual = String(opcionGenero.id_opcion); + } + } + } + + return ( +
+ + { + actualizarRespuesta( + `${pregunta.pregunta.id_pregunta}`, + String(opt.value) + ); + }} + /> +
+ ); + } + + return ( +
+ +
+ ); + })} +
+ ))} + +
+ ); +} diff --git a/src/containers/formulario/formulario-registro.tsx b/src/containers/formulario/formulario-registro.tsx index 32b07e8..eb4a6e6 100644 --- a/src/containers/formulario/formulario-registro.tsx +++ b/src/containers/formulario/formulario-registro.tsx @@ -1,15 +1,13 @@ import React, { useState, useEffect } from 'react'; import { useGetApi } from '@/hooks/use-get-api'; import { GetCuestionario } from '@/types/evento'; +import { SubmitResponse } from '@/types/submit'; import SimpleInput from '@/components/input'; import RadioOptionGroup, { RadioOption } from '@/components/radio-option-group'; -import axiosInstance from '@/utils/api-config'; -import PrefetchAbiertaCorta from './prefetch-abierta-corta'; import Button from '@/components/button'; +import axiosInstance from '@/utils/api-config'; import toast from 'react-hot-toast'; -import { useRouter } from 'next/navigation'; import { getAxiosError } from '@/utils/errors-utils'; -import { SubmitResponse } from '@/types/submit'; export type UsuarioDataResponse = { cuenta: string | null; @@ -17,22 +15,19 @@ export type UsuarioDataResponse = { apellidos: string | null; carrera: string | null; genero: string | null; - rfc?: string | null; // Solo para trabajadores + rfc?: string | null; }; type UsuarioData = { - nombre: string; // alumno o trabajador - apellidos: string; // alumno o trabajador - correo: string; // alumno o trabajador (en los alumnos se usa el id_ncuenta en trabajadores ellos deben escribirlo) - genero: 'M' | 'F'; // alumno o trabajador - carrera: string; // solo para alumnos - rfc?: string; // solo para trabajadores se usa para buscar información de cuenta + nombre: string; + apellidos: string; + correo: string; + genero: 'M' | 'F'; + carrera: string; + rfc?: string; }; -const confirmativeOption = ['Sí', 'Si', 'Si, claro', 'Claro que sí']; -const negativeOption = ['No', 'No, gracias', 'No, de momento']; - -type RespuestaFormulario = Record; // id_pregunta: respuesta +type RespuestaFormulario = Record; export default function FormularioRegistro({ id_cuestionario, @@ -44,28 +39,24 @@ export default function FormularioRegistro({ id_cuestionario: number; handleSubmitFormulario: (data: SubmitResponse) => void; }) { - // ------------------------ - // Estados - // ------------------------ - const [isComunidad, setIsComunidad] = useState>(); + // Estados principales const [respuestas, setRespuestas] = useState({}); const [cuentaInfo, setCuentaInfo] = useState(null); + const [busquedaCompletada, setBusquedaCompletada] = useState(false); + const [cuentaBuscada, setCuentaBuscada] = useState(''); const [isSending, setIsSending] = useState(false); - const router = useRouter(); - - // ------------------------ + // Carga del cuestionario - // ------------------------ const { data, error } = useGetApi( `/cuestionario/${id_cuestionario}/formulario` ); - // ------------------------ - // Extracción de preguntas clave - // ------------------------ + + // Extraer todas las preguntas del cuestionario const preguntas = data?.cuestionario.secciones.flatMap((seccion) => seccion.preguntas.map((pregunta) => pregunta.pregunta) ); + // Identificar preguntas especiales const preguntaComunidad = preguntas?.find( (pregunta) => pregunta.validacion === 'comunidad_alumno' || @@ -79,166 +70,168 @@ export default function FormularioRegistro({ pregunta.validacion === 'rfc' ); - // ------------------------ - // Efecto: buscar información de cuenta - // ------------------------ + // Determinar tipo de comunidad seleccionada + const comunidadSeleccionada = preguntaComunidad + ? respuestas[preguntaComunidad.id_pregunta] + : null; + + const esComunidadSi = comunidadSeleccionada && + preguntaComunidad?.opciones.find(op => + op.id_opcion === Number(comunidadSeleccionada) && + op.opcion.opcion.toLowerCase().includes('si') + ); + + // Efecto para buscar información de cuenta useEffect(() => { - const cuentaId = preguntaCuenta?.id_pregunta ?? ''; - const cuenta = respuestas[cuentaId]; - - const esCuentaAlumno = preguntaCuenta?.validacion === 'cuenta_alumno'; - const esCuentaTrabajador = - preguntaCuenta?.validacion === 'cuenta_trabajador' || - preguntaCuenta?.validacion === 'rfc'; - - const puedeBuscar = - cuenta && - typeof cuenta === 'string' && - ((esCuentaAlumno && cuenta.length === 9) || - (esCuentaTrabajador && cuenta.length === 10)); - - const fetchCuentaInfo = async () => { - try { - const endpoint = esCuentaAlumno - ? `/alumnos/${cuenta}` - : `/trabajadores/${cuenta}`; - - const res = await axiosInstance.get(endpoint); - - console.log('Datos de cuenta obtenidos:', res.data); - setCuentaInfo({ - nombre: res.data.nombre ? res.data.nombre : '', - apellidos: res.data.apellidos ? res.data.apellidos : '', - correo: res.data.cuenta - ? `${res.data.cuenta}@pcpuma.acatlan.unam.mx` - : '', - genero: - res.data.genero === 'M' || res.data.genero === 'F' - ? res.data.genero - : 'M', - carrera: res.data.carrera ? res.data.carrera : '', - }); - // Usar datos fake para pruebas - /* setCuentaInfo({ - nombre: 'Juan', - apellidos: 'Pérez', - correo: 'juan.perez@pcpuma.acatlan.unam.mx', - genero: 'M', - carrera: 'Ingeniería en Computación', - }); */ - } catch (error) { - console.error('Error al obtener datos de cuenta:', error); - setCuentaInfo(null); - } - }; - - if (puedeBuscar) { - fetchCuentaInfo(); - } else { + if (!preguntaCuenta || !esComunidadSi) { setCuentaInfo(null); - } - }, [respuestas, preguntaCuenta?.id_pregunta, preguntaCuenta?.validacion]); - - // ------------------------ - // Efecto: precargar respuestas si hay cuentaInfo - // ------------------------ - useEffect(() => { - if (!cuentaInfo) { - if (data?.cuestionario.secciones) { - const preguntas = data.cuestionario.secciones.flatMap((seccion) => - seccion.preguntas.map((pregunta) => pregunta.pregunta) - ); - - const idsPrefetch = preguntas - .filter((pregunta) => - [ - 'nombre', - 'apellidos', - 'correo', - 'institucion', - 'carrera', - 'genero', - ].includes(pregunta.validacion) - ) - .map((pregunta) => pregunta.id_pregunta); - - setRespuestas((prev) => { - const nuevas = { ...prev }; - idsPrefetch.forEach((id) => { - delete nuevas[id]; - }); - return nuevas; - }); - } + setBusquedaCompletada(false); + setCuentaBuscada(''); return; } + const cuenta = respuestas[preguntaCuenta.id_pregunta]; + + if (!cuenta || typeof cuenta !== 'string') { + setCuentaInfo(null); + setBusquedaCompletada(false); + setCuentaBuscada(''); + return; + } + + // Determinar longitudes válidas según el tipo + const longitudesValidas = { + 'cuenta_alumno': 9, + 'cuenta_trabajador': 6, + 'rfc': 10 + }; + + const longitudRequerida = longitudesValidas[preguntaCuenta.validacion as keyof typeof longitudesValidas]; + const puedeBuscar = cuenta.length === longitudRequerida; + + // Solo buscar si no se ha buscado ya esta cuenta + if (puedeBuscar && cuentaBuscada !== cuenta) { + setBusquedaCompletada(false); + + const fetchCuentaInfo = async () => { + try { + let endpoint: string; + + switch (preguntaCuenta.validacion) { + case 'cuenta_alumno': + endpoint = `/alumnos/${cuenta}`; + break; + case 'cuenta_trabajador': + endpoint = `/trabajadores/${cuenta}`; + break; + case 'rfc': + endpoint = `/trabajadores/${cuenta}`; + break; + default: + throw new Error('Tipo de validación no reconocido'); + } + + const res = await axiosInstance.get(endpoint); + console.log('Datos de cuenta obtenidos:', res.data); + + // Verificar si hay datos válidos + const hayDatosValidos = res.data.nombre || res.data.apellidos || + res.data.carrera || res.data.genero; + + if (hayDatosValidos) { + setCuentaInfo({ + nombre: res.data.nombre || '', + apellidos: res.data.apellidos || '', + correo: res.data.cuenta + ? `${res.data.cuenta}@pcpuma.acatlan.unam.mx` + : '', + genero: (res.data.genero === 'M' || res.data.genero === 'F') + ? res.data.genero + : 'M', + carrera: res.data.carrera || '', + rfc: res.data.rfc || undefined, + }); + } else { + setCuentaInfo(null); + } + + setCuentaBuscada(cuenta); + setBusquedaCompletada(true); + + } catch (error) { + console.error('Error al obtener datos de cuenta:', error); + setCuentaInfo(null); + setCuentaBuscada(cuenta); + setBusquedaCompletada(true); + } + }; + + fetchCuentaInfo(); + } else if (!puedeBuscar) { + setCuentaInfo(null); + setBusquedaCompletada(false); + setCuentaBuscada(''); + } + }, [respuestas, preguntaCuenta, esComunidadSi, cuentaBuscada]); + + // Efecto para prellenar respuestas cuando se obtiene información de cuenta + useEffect(() => { + if (!cuentaInfo || !data?.cuestionario.secciones) return; + const nuevasRespuestas: RespuestaFormulario = {}; - if (data?.cuestionario.secciones) { - const preguntas = data.cuestionario.secciones.flatMap((seccion) => - seccion.preguntas.map((pregunta) => pregunta.pregunta) - ); - - for (const pregunta of preguntas) { - const id = pregunta.id_pregunta; - switch (pregunta.validacion) { - case 'nombre': - nuevasRespuestas[id] = cuentaInfo.nombre; - break; - case 'apellidos': - nuevasRespuestas[id] = cuentaInfo.apellidos; - break; - case 'correo': - nuevasRespuestas[id] = cuentaInfo.correo; - break; - case 'institucion': - nuevasRespuestas[id] = 'FES Acatlán'; - break; - case 'carrera': - nuevasRespuestas[id] = cuentaInfo.carrera; - break; - case 'genero': - const generoTexto = - cuentaInfo.genero === 'M' ? 'Masculino' : 'Femenino'; - const opcion = pregunta.opciones?.find( - (op) => - op.opcion.opcion.toLowerCase() === generoTexto.toLowerCase() - ); - if (opcion) { - nuevasRespuestas[id] = String(opcion.id_opcion); - } - break; - } + preguntas?.forEach((pregunta) => { + const id = pregunta.id_pregunta; + + switch (pregunta.validacion) { + case 'nombre': + nuevasRespuestas[id] = cuentaInfo.nombre; + break; + case 'apellidos': + nuevasRespuestas[id] = cuentaInfo.apellidos; + break; + case 'correo': + nuevasRespuestas[id] = cuentaInfo.correo; + break; + case 'carrera': + nuevasRespuestas[id] = cuentaInfo.carrera; + break; + case 'genero': + const generoTexto = cuentaInfo.genero === 'M' ? 'Masculino' : 'Femenino'; + const opcionGenero = pregunta.opciones?.find( + (op) => op.opcion.opcion.toLowerCase() === generoTexto.toLowerCase() + ); + if (opcionGenero) { + nuevasRespuestas[id] = String(opcionGenero.id_opcion); + } + break; } + }); - setRespuestas((prev) => ({ - ...prev, - ...nuevasRespuestas, - })); - } - }, [cuentaInfo, data?.cuestionario.secciones]); + setRespuestas(prev => ({ ...prev, ...nuevasRespuestas })); + }, [cuentaInfo, data?.cuestionario.secciones, preguntas]); - // ------------------------ - // Funciones auxiliares - // ------------------------ + // Función para actualizar respuestas const actualizarRespuesta = (idPregunta: string, valor: string) => { - setRespuestas((prev) => ({ ...prev, [idPregunta]: valor })); + setRespuestas(prev => ({ ...prev, [idPregunta]: valor })); }; + // Función para enviar formulario const handleOnSubmit = async () => { setIsSending(true); + if (!data?.cuestionario.id_cuestionario) { - alert('No se ha cargado correctamente el formulario'); + toast.error('No se ha cargado correctamente el formulario'); + setIsSending(false); return; } - const preguntasObligatorias = - data?.cuestionario.secciones.flatMap((seccion) => - seccion.preguntas - .filter((pregunta) => pregunta.pregunta.obligatoria) - .map((pregunta) => pregunta.pregunta) - ) || []; + // Validar preguntas obligatorias + const preguntasObligatorias = data.cuestionario.secciones.flatMap((seccion) => + seccion.preguntas + .filter((pregunta) => pregunta.pregunta.obligatoria) + .map((pregunta) => pregunta.pregunta) + ); const faltantes = preguntasObligatorias.filter( (pregunta) => @@ -247,26 +240,22 @@ export default function FormularioRegistro({ respuestas[pregunta.id_pregunta] === null ); - const preguntaCorreo = preguntasObligatorias.find( - (pregunta) => - pregunta.validacion === 'correo' || - pregunta.validacion === 'correo_institucional' - ); - if (faltantes.length > 0) { toast.error('Por favor responde todas las preguntas obligatorias.'); setIsSending(false); return; } - // Buscar un posible correo en las respuestas si no hay cuentaInfo - let correo = - cuentaInfo?.correo === respuestas[preguntaCorreo?.id_pregunta ?? ''] - ? cuentaInfo?.correo - : respuestas[preguntaCorreo?.id_pregunta ?? '']; + // Buscar correo + const preguntaCorreo = preguntas?.find(p => + p.validacion === 'correo' || p.validacion === 'correo_institucional' + ); + + let correo = preguntaCorreo + ? String(respuestas[preguntaCorreo.id_pregunta] || '') + : ''; if (!correo) { - // Buscar en las respuestas una que parezca correo const correoRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; for (const valor of Object.values(respuestas)) { if (typeof valor === 'string' && correoRegex.test(valor)) { @@ -278,7 +267,7 @@ export default function FormularioRegistro({ const payload = { id_cuestionario: id_cuestionario, - correo: correo || '', + correo: correo, fecha_envio: new Date().toISOString(), respuestas: Object.entries(respuestas).map(([id_pregunta, valor]) => ({ id_pregunta: Number(id_pregunta), @@ -291,77 +280,38 @@ export default function FormularioRegistro({ '/cuestionario-respondido/submit', payload ); - toast( - () => ( - - {res.data.message.includes('ya ha respondido') ? ( - <> - - Respuestas Enviadas Con Éxito - - ) : ( - <> - - {res.data.message || '¡Formulario enviado!'} - - )} - - ), - { - duration: 6000, - position: 'bottom-center', - style: { - maxWidth: '425px', - }, - } - ); - + + toast.success(res.data.message || '¡Formulario enviado exitosamente!'); handleSubmitFormulario(res.data); } catch (error) { - console.log(getAxiosError(error)); - toast.error('Hubo un error al enviar el formulario', { - duration: 5000, - }); - router.push('/'); + console.error('Error al enviar formulario:', getAxiosError(error)); + toast.error('Hubo un error al enviar el formulario'); + } finally { + setIsSending(false); } }; - // ------------------------ - // Condición para mostrar el formulario - // ------------------------ - const mostrarFormularioRestante = - !preguntaComunidad || // Si no hay pregunta de comunidad, mostrar el formulario - (isComunidad && - confirmativeOption.some((opt) => - isComunidad.label?.toLowerCase().includes(opt.toLowerCase()) - ) && - !!cuentaInfo) || - (isComunidad && - negativeOption.some((opt) => - isComunidad.label?.toLowerCase().includes(opt.toLowerCase()) - )); + // Condición para mostrar el resto del formulario + const mostrarFormularioCompleto = + !preguntaComunidad || // Sin pregunta de comunidad + (esComunidadSi && busquedaCompletada) || // Con comunidad "Sí" y búsqueda completada + (!esComunidadSi && comunidadSeleccionada); // Con comunidad "No" if (error) { console.error('Error al cargar el cuestionario:', error); return
Error al cargar el cuestionario
; } + if (!data) { + return
Cargando...
; + } + return (
+ {/* Pregunta de comunidad */} {preguntaComunidad && (
-
)} - {isComunidad && - confirmativeOption.some((opt) => - isComunidad.label?.toLowerCase().includes(opt.toLowerCase()) - ) && - preguntaCuenta && ( -
- - - setRespuestas((prev) => ({ - ...prev, - [preguntaCuenta.id_pregunta]: e.target.value, - })) - } - /> -
- )} - {mostrarFormularioRestante && - data?.cuestionario.secciones.map((seccion, i) => ( -
- {seccion.preguntas.map((pregunta) => { - // Evitar renderizar las preguntas ya manejadas - if ( - pregunta.pregunta.id_pregunta === - preguntaComunidad?.id_pregunta || - pregunta.pregunta.id_pregunta === preguntaCuenta?.id_pregunta - ) - return null; - if ( - pregunta.pregunta.tipo_pregunta.tipo_pregunta === - 'Abierta (Respuesta corta)' - ) { - return ( - + + actualizarRespuesta( + preguntaCuenta.id_pregunta.toString(), + e.target.value + )} + /> +
+ )} + + {/* Resto del formulario */} + {mostrarFormularioCompleto && data.cuestionario.secciones.map((seccion, i) => ( +
+ {seccion.preguntas.map((pregunta) => { + // No renderizar preguntas ya manejadas + if ( + pregunta.pregunta.id_pregunta === preguntaComunidad?.id_pregunta || + pregunta.pregunta.id_pregunta === preguntaCuenta?.id_pregunta + ) { + return null; + } + + const valor = respuestas[pregunta.pregunta.id_pregunta] || ''; + + // Pregunta abierta corta + if (pregunta.pregunta.tipo_pregunta.tipo_pregunta === 'Abierta (Respuesta corta)') { + return ( +
+ + actualizarRespuesta( + pregunta.pregunta.id_pregunta.toString(), + e.target.value + )} /> - ); - } +
+ ); + } - if ( - pregunta.pregunta.tipo_pregunta.tipo_pregunta === - 'Abierta (Parrafo)' - ) { - return ( -
- - - setRespuestas((prev) => ({ - ...prev, - [pregunta.pregunta.id_pregunta]: e.target.value, - })) - } - /> -
- ); - } + // Pregunta abierta párrafo + if (pregunta.pregunta.tipo_pregunta.tipo_pregunta === 'Abierta (Parrafo)') { + return ( +
+ + actualizarRespuesta( + pregunta.pregunta.id_pregunta.toString(), + e.target.value + )} + /> +
+ ); + } - if (pregunta.pregunta.tipo_pregunta.tipo_pregunta === 'Cerrada') { - const opciones: RadioOption[] = - pregunta.pregunta.opciones.map((op) => ({ - label: op.opcion.opcion, - value: op.id_opcion, - })); - let respuestaActual = - respuestas[`${pregunta.pregunta.id_pregunta}`]; - - if ( - (!preguntaComunidad || - (confirmativeOption.includes(isComunidad?.label || '') && - cuentaInfo)) && - respuestaActual === undefined // solo si no ha respondido - ) { - if (pregunta.pregunta.validacion === 'genero' && cuentaInfo) { - const generoTexto = - cuentaInfo.genero === 'M' ? 'Masculino' : 'Femenino'; - - const opcionGenero = pregunta.pregunta.opciones.find( - (op) => - op.opcion.opcion.toLowerCase() === - generoTexto.toLowerCase() - ); - if (opcionGenero) { - respuestaActual = String(opcionGenero.id_opcion); - } - } - } - - return ( -
- - { - actualizarRespuesta( - `${pregunta.pregunta.id_pregunta}`, - String(opt.value) - ); - }} - /> -
- ); - } + // Pregunta cerrada + if (pregunta.pregunta.tipo_pregunta.tipo_pregunta === 'Cerrada') { + const opciones: RadioOption[] = pregunta.pregunta.opciones.map((op) => ({ + label: op.opcion.opcion, + value: op.id_opcion, + })); return (
-
); - })} -
- ))} - + } + + return null; + })} +
+ ))} + + {/* Botón de envío */} + {mostrarFormularioCompleto && ( + + )} ); } + +/* +curl -X 'POST' \ + 'https://venus.acatlan.unam.mx/registro_test/administrador' \ + -H 'accept: application/json' \ + -H 'Content-Type: application/json' \ + -d '{ + "nombre_usuario": "staffmike", + "correo": "staffmike@ejemplo.com", + "password": "password", + "id_tipo_user": 2 +}' +*/ \ No newline at end of file