feat: enhance event forms with date formatting and registration link updates; add utility for local date formatting
This commit is contained in:
@@ -62,13 +62,15 @@ export default function EventoCard({
|
||||
<div key={cuestionario.id_cuestionario} className="">
|
||||
<Link
|
||||
href={
|
||||
user === 'public'
|
||||
? `/evento/${evento.nombre_evento.split(' ').join('_')}/${evento.id_evento}/${cuestionario.id_cuestionario}`
|
||||
: `/${user}/evento/${evento.id_evento}/${cuestionario.id_cuestionario}`
|
||||
user === 'public'
|
||||
? `/evento/${evento.nombre_evento.split(' ').join('_')}/${
|
||||
evento.id_evento
|
||||
}/${cuestionario.id_cuestionario}`
|
||||
: `/${user}/evento/${evento.id_evento}/${cuestionario.id_cuestionario}`
|
||||
}
|
||||
className="btn mt-2 w-100 btn-outline-primary"
|
||||
>
|
||||
{cuestionario.nombre_form}
|
||||
Regístrate
|
||||
</Link>
|
||||
</div>
|
||||
))}
|
||||
|
||||
@@ -3,6 +3,7 @@ import ImageUploader from '@/components/banner-uploader';
|
||||
import Input from '@/components/input';
|
||||
import Select from '@/components/select';
|
||||
import { CreateEventoType } from '@/types/create-evento';
|
||||
import { formatDateLocal } from '@/utils/date-utils';
|
||||
import { commands } from '@uiw/react-md-editor';
|
||||
import dynamic from 'next/dynamic';
|
||||
import React from 'react';
|
||||
@@ -72,9 +73,9 @@ export default function CreateEvento({
|
||||
<div className="col-md-6">
|
||||
<Input
|
||||
label="Fecha de inicio"
|
||||
type="date"
|
||||
type="datetime-local"
|
||||
className={{ container: 'mb-3' }}
|
||||
value={evento.fecha_inicio.toISOString().split('T')[0]}
|
||||
value={formatDateLocal(evento.fecha_inicio)}
|
||||
onChange={(e) =>
|
||||
handleChange('fecha_inicio', new Date(e.target.value))
|
||||
}
|
||||
@@ -84,10 +85,12 @@ export default function CreateEvento({
|
||||
<div className="col-md-6">
|
||||
<Input
|
||||
label="Fecha de fin"
|
||||
type="date"
|
||||
type="datetime-local"
|
||||
className={{ container: 'mb-3' }}
|
||||
value={evento.fecha_fin.toISOString().split('T')[0]}
|
||||
onChange={(e) => handleChange('fecha_fin', new Date(e.target.value))}
|
||||
value={formatDateLocal(evento.fecha_fin)}
|
||||
onChange={(e) =>
|
||||
handleChange('fecha_fin', new Date(e.target.value))
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -159,7 +159,7 @@ export default function FormularioEditor({ formulario, onChange }: Props) {
|
||||
<div className="col-md-6">
|
||||
<SimpleInput
|
||||
label="Fecha de Inicio"
|
||||
type="datetime-local"
|
||||
type="date"
|
||||
value={formulario.fecha_inicio.slice(0, 16)}
|
||||
onChange={(e) => actualizarCampo('fecha_inicio', e.target.value)}
|
||||
/>
|
||||
@@ -167,7 +167,7 @@ export default function FormularioEditor({ formulario, onChange }: Props) {
|
||||
<div className="col-md-6">
|
||||
<SimpleInput
|
||||
label="Fecha de Fin"
|
||||
type="datetime-local"
|
||||
type="date"
|
||||
value={formulario.fecha_fin.slice(0, 16)}
|
||||
onChange={(e) => actualizarCampo('fecha_fin', e.target.value)}
|
||||
/>
|
||||
|
||||
@@ -11,6 +11,15 @@ import toast from 'react-hot-toast';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { getAxiosError } from '@/utils/errors-utils';
|
||||
|
||||
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
|
||||
@@ -79,7 +88,8 @@ export default function FormularioRegistro({
|
||||
const puedeBuscar =
|
||||
cuenta &&
|
||||
typeof cuenta === 'string' &&
|
||||
((esCuentaAlumno && cuenta.length === 9) || (esCuentaTrabajador && cuenta.length === 5));
|
||||
((esCuentaAlumno && cuenta.length === 9) ||
|
||||
(esCuentaTrabajador && cuenta.length === 10));
|
||||
|
||||
const fetchCuentaInfo = async () => {
|
||||
try {
|
||||
@@ -87,14 +97,20 @@ export default function FormularioRegistro({
|
||||
? `/alumnos/${cuenta}`
|
||||
: `/trabajadores/${cuenta}`;
|
||||
|
||||
const res = await axiosInstance.get(endpoint);
|
||||
const res = await axiosInstance.get<UsuarioDataResponse>(endpoint);
|
||||
|
||||
console.log('Datos de cuenta obtenidos:', res.data);
|
||||
setCuentaInfo({
|
||||
nombre: res.data.nombre,
|
||||
apellidos: res.data.apellidos,
|
||||
correo: '',
|
||||
genero: res.data.genero,
|
||||
carrera: res.data.carrera ? res.data.carrera : '',
|
||||
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({
|
||||
@@ -197,6 +213,7 @@ export default function FormularioRegistro({
|
||||
};
|
||||
|
||||
const handleOnSubmit = async () => {
|
||||
console.log('Enviando respuestas:', respuestas);
|
||||
setIsSending(true);
|
||||
if (!data?.cuestionario.id_cuestionario) {
|
||||
alert('No se ha cargado correctamente el formulario');
|
||||
@@ -217,13 +234,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.');
|
||||
return;
|
||||
}
|
||||
|
||||
// Buscar un posible correo en las respuestas si no hay cuentaInfo
|
||||
let correo = cuentaInfo?.correo;
|
||||
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@]+$/;
|
||||
@@ -245,8 +271,6 @@ export default function FormularioRegistro({
|
||||
})),
|
||||
};
|
||||
|
||||
console.log('Payload para enviar:', payload);
|
||||
|
||||
try {
|
||||
const res = await axiosInstance.post(
|
||||
'/cuestionario-respondido/submit',
|
||||
@@ -473,7 +497,7 @@ export default function FormularioRegistro({
|
||||
<Button
|
||||
className="btn btn-primary"
|
||||
onClick={handleOnSubmit}
|
||||
disabled={!mostrarFormularioRestante}
|
||||
disabled={!mostrarFormularioRestante || isSending}
|
||||
>
|
||||
{isSending ? 'Enviando...' : 'Enviar Formulario'}
|
||||
</Button>
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
'use client'
|
||||
'use client';
|
||||
import MarkdownRenderer from '@/components/markdown-render';
|
||||
import { useGetApi } from '@/hooks/use-get-api';
|
||||
import Image from 'next/image';
|
||||
import React from 'react';
|
||||
import FormularioRegistro from '../formulario/formulario-registro';
|
||||
import { GetEventoWithCuestionario } from '@/types/evento';
|
||||
import { formatFecha } from '@/utils/date-utils';
|
||||
|
||||
export default function FormularioPage({
|
||||
id_evento,
|
||||
@@ -38,6 +39,25 @@ export default function FormularioPage({
|
||||
{data?.descripcion_evento && (
|
||||
<MarkdownRenderer markdown={data?.descripcion_evento} />
|
||||
)}
|
||||
|
||||
{data?.fecha_fin && data?.fecha_inicio && (
|
||||
<>
|
||||
<p className="mb-0 mt-2 text-muted">Horario:</p>
|
||||
<p className="text-muted">
|
||||
<i className="bi bi-clock me-1 text-primary"></i>
|
||||
{formatFecha(data.fecha_inicio!, {
|
||||
horas: true,
|
||||
minutos: true,
|
||||
})}{' '}
|
||||
al{' '}
|
||||
{formatFecha(data.fecha_fin!, {
|
||||
horas: true,
|
||||
minutos: true,
|
||||
})}
|
||||
</p>
|
||||
</>
|
||||
)}
|
||||
|
||||
{data && (
|
||||
<FormularioRegistro
|
||||
id_cuestionario={data?.cuestionario.id_cuestionario}
|
||||
|
||||
+10
-4
@@ -48,13 +48,13 @@ export const plantillasDisponibles: Plantilla[] = [
|
||||
titulo: 'Nombre(s)',
|
||||
tipo: 'Abierta (Respuesta corta)',
|
||||
obligatoria: true,
|
||||
validacion: 'nombre'
|
||||
validacion: 'nombre',
|
||||
},
|
||||
{
|
||||
titulo: 'Apellidos',
|
||||
tipo: 'Abierta (Respuesta corta)',
|
||||
obligatoria: true,
|
||||
validacion: 'apellidos'
|
||||
validacion: 'apellidos',
|
||||
},
|
||||
{
|
||||
titulo: 'Género',
|
||||
@@ -67,6 +67,12 @@ export const plantillasDisponibles: Plantilla[] = [
|
||||
{ valor: 'Prefiero no decirlo' },
|
||||
],
|
||||
},
|
||||
{
|
||||
titulo: 'Carrera',
|
||||
tipo: 'Abierta (Respuesta corta)',
|
||||
validacion: 'carrera',
|
||||
obligatoria: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
@@ -159,5 +165,5 @@ Inicia esta nueva etapa con un recorrido especial por nuestras instalaciones, pe
|
||||
/*
|
||||
Recorrido de bienvenida para nuevo personal
|
||||
Damos la más cordial bienvenida al personal de nuevo ingreso a nuestra Facultad. Este recorrido está diseñado para familiarizarlos con las principales áreas administrativas, académicas y de servicios. También tendrán la oportunidad de conocer al equipo de trabajo y obtener información clave para integrarse con éxito a la comunidad universitaria.
|
||||
¡Les esperamos con gusto para comenzar juntos esta nueva etapa profesional!
|
||||
*/
|
||||
|
||||
*/
|
||||
|
||||
@@ -1,4 +1,106 @@
|
||||
/**
|
||||
* Formatea una fecha en formato legible en español, con opción a incluir hora, minutos y día de la semana.
|
||||
*
|
||||
* @param {string} fechaStr - Cadena de texto representando una fecha válida (ISO 8601 o similar).
|
||||
* @param {Object} [config] - Configuración opcional para mostrar hora, minutos y día de la semana.
|
||||
* @param {boolean} [config.horas=false] - Indica si se debe incluir la hora.
|
||||
* @param {boolean} [config.minutos=false] - Indica si se deben incluir los minutos (requiere config.horas).
|
||||
* @param {boolean} [config.diaSemana=false] - Indica si se debe incluir el día de la semana.
|
||||
* @returns {string} Fecha formateada en español.
|
||||
* @throws {Error} Lanza un error si la fecha proporcionada no es válida.
|
||||
*
|
||||
* @example
|
||||
* formatFecha('2025-03-25T14:30:00Z');
|
||||
* // Retorna: "25 de marzo del 2025"
|
||||
*
|
||||
* @example
|
||||
* formatFecha('2025-03-25T14:30:00Z', { horas: true });
|
||||
* // Retorna: "25 de marzo del 2025 - 14"
|
||||
*
|
||||
* @example
|
||||
* formatFecha('2025-03-25T14:30:00Z', { horas: true, minutos: true });
|
||||
* // Retorna: "25 de marzo del 2025 - 14:30"
|
||||
*
|
||||
* @example
|
||||
* formatFecha('2025-03-25T14:30:00Z', { diaSemana: true });
|
||||
* // Retorna: "martes, 25 de marzo del 2025"
|
||||
*/
|
||||
export function formatFecha(
|
||||
fechaStr: string,
|
||||
config: { horas?: boolean; minutos?: boolean; diaSemana?: boolean } = {
|
||||
|
||||
horas: false,
|
||||
minutos: false,
|
||||
diaSemana: false,
|
||||
}
|
||||
): string {
|
||||
const fecha = new Date(fechaStr);
|
||||
|
||||
if (isNaN(fecha.getTime())) {
|
||||
throw new Error(`Fecha inválida: ${fechaStr}`);
|
||||
}
|
||||
|
||||
const diasSemana = [
|
||||
'domingo',
|
||||
'lunes',
|
||||
'martes',
|
||||
'miércoles',
|
||||
'jueves',
|
||||
'viernes',
|
||||
'sábado',
|
||||
];
|
||||
const meses = [
|
||||
'enero',
|
||||
'febrero',
|
||||
'marzo',
|
||||
'abril',
|
||||
'mayo',
|
||||
'junio',
|
||||
'julio',
|
||||
'agosto',
|
||||
'septiembre',
|
||||
'octubre',
|
||||
'noviembre',
|
||||
'diciembre',
|
||||
];
|
||||
|
||||
const dia = fecha.getUTCDate();
|
||||
const mes = meses[fecha.getUTCMonth()];
|
||||
const año = fecha.getUTCFullYear();
|
||||
const hora = (fecha.getUTCHours() - 6 + 24) % 24;
|
||||
const minutos = fecha.getUTCMinutes().toString().padStart(2, '0');
|
||||
const diaSemana = diasSemana[fecha.getUTCDay()];
|
||||
|
||||
let fechaFormateada = '';
|
||||
|
||||
if (config.diaSemana) {
|
||||
fechaFormateada += `${diaSemana}, `;
|
||||
}
|
||||
|
||||
fechaFormateada += `${dia} de ${mes} del ${año}`;
|
||||
|
||||
if (config.horas) {
|
||||
fechaFormateada += ` - ${hora}`;
|
||||
if (config.minutos) {
|
||||
fechaFormateada += `:${minutos}`;
|
||||
}
|
||||
}
|
||||
|
||||
return fechaFormateada;
|
||||
}
|
||||
|
||||
export function formatearFecha(fecha: string, tipo: 'inicio' | 'fin'): string {
|
||||
const sufijo = tipo === 'inicio' ? 'T00:00:01' : 'T23:59:59';
|
||||
return `${fecha}${sufijo}`;
|
||||
}
|
||||
|
||||
export function formatDateLocal(date: Date): string {
|
||||
const pad = (n: number) => n.toString().padStart(2, '0');
|
||||
const yyyy = date.getFullYear();
|
||||
const MM = pad(date.getMonth() + 1);
|
||||
const dd = pad(date.getDate());
|
||||
const hh = pad(date.getHours());
|
||||
const mm = pad(date.getMinutes());
|
||||
|
||||
return `${yyyy}-${MM}-${dd}T${hh}:${mm}`;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user