feat: Add event management features, including event editing and participant tracking; refactor layout and API integration
This commit is contained in:
@@ -8,6 +8,10 @@ const nextConfig: NextConfig = {
|
||||
hostname: 'localhost',
|
||||
port: '4200',
|
||||
},
|
||||
{
|
||||
protocol: 'https',
|
||||
hostname: 'venus.acatlan.unam.mx'
|
||||
}
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
+94
-96
@@ -1,42 +1,56 @@
|
||||
'use client';
|
||||
import Button from '@/components/button';
|
||||
import Input from '@/components/input';
|
||||
import Table, { Header } from '@/components/table';
|
||||
import SimpleInput from '@/components/input';
|
||||
import EditEvento from '@/containers/edit-evento';
|
||||
import { useGetApi } from '@/hooks/use-get-api';
|
||||
import { GetEventoWithCuestionario } from '@/types/evento';
|
||||
import { useParams } from 'next/navigation';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { CreateEventoType } from '@/types/create-evento';
|
||||
import { ParticipacionEvento } from '@/types/participante-evento';
|
||||
import axiosInstance from '@/utils/api-config';
|
||||
import { downloadFile } from '@/utils/downloas-utils';
|
||||
import { useParams } from 'next/navigation';
|
||||
import React, { useEffect } from 'react';
|
||||
import toast from 'react-hot-toast';
|
||||
import Table, { Header } from '@/components/table';
|
||||
import Button from '@/components/button';
|
||||
import { downloadFile } from '@/utils/downloas-utils';
|
||||
|
||||
type Params = {
|
||||
id_evento: string;
|
||||
id_cuestionario: string;
|
||||
};
|
||||
|
||||
export default function Page() {
|
||||
const params = useParams<{ id_formulario: string }>();
|
||||
const id_formulario = Number(params?.id_formulario);
|
||||
const params = useParams<Params>();
|
||||
const { data } = useGetApi<GetEventoWithCuestionario>(
|
||||
`/evento/${params.id_evento}/cuestionario/${params.id_cuestionario}`
|
||||
);
|
||||
const { data: participantes, setData } = useGetApi<ParticipacionEvento[]>(
|
||||
`/participante-evento/evento/${params.id_cuestionario}`
|
||||
);
|
||||
|
||||
const [participantes, setParticipantes] = React.useState<
|
||||
ParticipacionEvento[]
|
||||
>([]);
|
||||
const [evento, setEvento] = useState<GetEventoWithCuestionario | null>(null);
|
||||
const [busquedaCorreo, setBusquedaCorreo] = React.useState('');
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [loadingAsistencia, setLoadingAsistencia] = React.useState<
|
||||
Record<number, boolean>
|
||||
>({});
|
||||
const [busquedaCorreo, setBusquedaCorreo] = React.useState('');
|
||||
const [loading, setLoading] = React.useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (!id_formulario) return;
|
||||
const fetchParticipantes = async () => {
|
||||
try {
|
||||
const { data } = await axiosInstance.get<ParticipacionEvento[]>(
|
||||
`/participante-evento/evento/${id_formulario}`
|
||||
);
|
||||
setParticipantes(data);
|
||||
} catch (error) {
|
||||
console.error('Error al obtener participantes:', error);
|
||||
}
|
||||
};
|
||||
if (data) setEvento(data);
|
||||
}, [data]);
|
||||
|
||||
fetchParticipantes();
|
||||
}, [id_formulario]);
|
||||
const handleChange = (
|
||||
field: keyof CreateEventoType,
|
||||
value: string | Date
|
||||
) => {
|
||||
setEvento((prev) =>
|
||||
prev
|
||||
? {
|
||||
...prev,
|
||||
[field]: value,
|
||||
}
|
||||
: null
|
||||
);
|
||||
};
|
||||
|
||||
const confirmarAsistencia = async (
|
||||
id_participante: number,
|
||||
@@ -53,7 +67,7 @@ export default function Page() {
|
||||
const { data } = await axiosInstance.get<ParticipacionEvento[]>(
|
||||
`/participante-evento/evento/${id_evento}`
|
||||
);
|
||||
setParticipantes(data);
|
||||
setData(data);
|
||||
} catch (error) {
|
||||
toast.error('Error al confirmar asistencia');
|
||||
console.error(error);
|
||||
@@ -62,6 +76,12 @@ export default function Page() {
|
||||
}
|
||||
};
|
||||
|
||||
const handleEventoActualizado = (
|
||||
eventoActualizado: GetEventoWithCuestionario
|
||||
) => {
|
||||
setEvento(eventoActualizado);
|
||||
};
|
||||
|
||||
const headers: Header<ParticipacionEvento>[] = [
|
||||
{
|
||||
key: 'id_participante',
|
||||
@@ -74,9 +94,12 @@ export default function Page() {
|
||||
render: (_, row) => row.participante.correo,
|
||||
},
|
||||
{
|
||||
key: 'fecha_inscripcion',
|
||||
label: 'Fecha inscripción',
|
||||
render: (val) => new Date(val as string).toLocaleString(),
|
||||
key: 'fecha_registro',
|
||||
label: 'Fecha registro',
|
||||
render: (_, row) =>
|
||||
row.fecha_registro
|
||||
? new Date(row.fecha_registro).toLocaleString()
|
||||
: 'Sin registro',
|
||||
},
|
||||
{
|
||||
key: 'estatus',
|
||||
@@ -93,7 +116,7 @@ export default function Page() {
|
||||
<button
|
||||
className="btn btn-sm btn-outline-success"
|
||||
onClick={() =>
|
||||
confirmarAsistencia(row.id_participante, row.id_evento)
|
||||
confirmarAsistencia(row.id_participante, row.id_cuestionario)
|
||||
}
|
||||
disabled={isLoading}
|
||||
>
|
||||
@@ -115,53 +138,21 @@ export default function Page() {
|
||||
},
|
||||
];
|
||||
|
||||
const participantesFiltrados = participantes.filter((p) =>
|
||||
p.participante.correo.toLowerCase().includes(busquedaCorreo.toLowerCase())
|
||||
);
|
||||
const participantesFiltrados =
|
||||
participantes &&
|
||||
participantes.filter((p) =>
|
||||
p.participante.correo.toLowerCase().includes(busquedaCorreo.toLowerCase())
|
||||
);
|
||||
|
||||
/* const handleDownload = () => {
|
||||
if (participantesFiltrados.length === 0) {
|
||||
toast.error('No hay participantes para exportar');
|
||||
return;
|
||||
}
|
||||
|
||||
const rows = participantesFiltrados.map((item) => ({
|
||||
ID_Participante: item.id_participante,
|
||||
ID_Evento: item.id_evento,
|
||||
Correo: item.participante.correo,
|
||||
Fecha_Inscripcion: new Date(item.fecha_inscripcion).toLocaleString(),
|
||||
Estatus: item.fecha_asistencia ? 'Asistio' : 'No asistio',
|
||||
Fecha_Asistencia: item.fecha_asistencia
|
||||
? new Date(item.fecha_asistencia).toLocaleString()
|
||||
: '',
|
||||
}));
|
||||
|
||||
const worksheet = XLSX.utils.json_to_sheet(rows);
|
||||
const workbook = XLSX.utils.book_new();
|
||||
XLSX.utils.book_append_sheet(workbook, worksheet, 'Participantes');
|
||||
|
||||
const excelBuffer = XLSX.write(workbook, {
|
||||
bookType: 'xlsx',
|
||||
type: 'array',
|
||||
});
|
||||
|
||||
const blob = new Blob([excelBuffer], {
|
||||
type: 'application/octet-stream',
|
||||
});
|
||||
|
||||
downloadFile(blob, `participantes_formulario_${id_formulario}`, 'xlsx');
|
||||
}; */
|
||||
if (!evento) return <p>Cargando...</p>;
|
||||
|
||||
const handleDownload = async () => {
|
||||
setLoading(true);
|
||||
if (participantesFiltrados.length === 0) {
|
||||
toast.error('No hay participantes para exportar');
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
const res = await axiosInstance.get(
|
||||
`/cuestionario-respondido/reporte-respuestas/${id_formulario}`,
|
||||
`/cuestionario-respondido/reporte-respuestas/${params.id_cuestionario}`,
|
||||
{
|
||||
responseType: 'blob',
|
||||
}
|
||||
@@ -169,7 +160,7 @@ export default function Page() {
|
||||
|
||||
downloadFile(
|
||||
res.data,
|
||||
`reporte_respuestas_formulario_${id_formulario}`,
|
||||
`reporte_respuestas_formulario_${params.id_cuestionario}`,
|
||||
'csv'
|
||||
);
|
||||
} catch (error) {
|
||||
@@ -181,31 +172,38 @@ export default function Page() {
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h2 className="mb-4">Participantes del formulario #{id_formulario}</h2>
|
||||
<div className="container my-4">
|
||||
<h2 className="mb-4">Evento</h2>
|
||||
|
||||
<Button
|
||||
icon="download"
|
||||
onClick={handleDownload}
|
||||
className="my-2"
|
||||
disabled={loading}
|
||||
>
|
||||
{loading ? (
|
||||
<>
|
||||
<span
|
||||
className="spinner-border spinner-border-sm me-2"
|
||||
role="status"
|
||||
aria-hidden="true"
|
||||
></span>
|
||||
Descargando...
|
||||
</>
|
||||
) : (
|
||||
'Descargar respuestas'
|
||||
)}
|
||||
</Button>
|
||||
{participantes.length > 0 && (
|
||||
<EditEvento
|
||||
evento={evento}
|
||||
handleChange={handleChange}
|
||||
handleOnChange={handleEventoActualizado}
|
||||
/>
|
||||
|
||||
<div className="d-flex justify-content-between align-items-center mt-4">
|
||||
<h3 className="mt-5">Participantes</h3>
|
||||
<div>
|
||||
<Button onClick={handleDownload} icon="download" variant='success' disabled={loading}>
|
||||
{loading ? (
|
||||
<>
|
||||
<span
|
||||
className="spinner-border spinner-border-sm me-2"
|
||||
role="status"
|
||||
aria-hidden="true"
|
||||
></span>
|
||||
Descargando...
|
||||
</>
|
||||
) : (
|
||||
'Descargar respuestas'
|
||||
)}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{participantes && participantes.length > 0 && (
|
||||
<>
|
||||
<Input
|
||||
<SimpleInput
|
||||
label="Buscar por correo"
|
||||
placeholder="ejemplo@correo.com"
|
||||
value={busquedaCorreo}
|
||||
@@ -215,7 +213,7 @@ export default function Page() {
|
||||
<div className="mt-4">
|
||||
<Table
|
||||
headers={headers}
|
||||
data={participantesFiltrados}
|
||||
data={participantesFiltrados ?? []}
|
||||
rowKey={(row) => row.id_participante}
|
||||
/>
|
||||
</div>
|
||||
@@ -1,25 +0,0 @@
|
||||
'use client';
|
||||
import Link from 'next/link';
|
||||
import { useParams } from 'next/navigation';
|
||||
import React from 'react';
|
||||
|
||||
export default function Layout({ children }: { children: React.ReactNode }) {
|
||||
const params = useParams<{ id_formulario: string }>();
|
||||
|
||||
return (
|
||||
<div>
|
||||
<nav className='d-flex gap-2 my-4'>
|
||||
<Link href={`/administrador/formulario/${params.id_formulario}`} className='text-decoration-none'>
|
||||
<div className='box'>Registros</div>
|
||||
</Link>
|
||||
{/* <Link
|
||||
href={'/administrador/crear-formulario'}
|
||||
className='text-decoration-none'
|
||||
>
|
||||
<div className='box'>Editar formulario</div>
|
||||
</Link> */}
|
||||
</nav>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -5,7 +5,7 @@ import React from 'react';
|
||||
export default function Layout({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<div className="d-flex flex-column min-vh-100">
|
||||
<Header />
|
||||
<Header small/>
|
||||
<main className="container flex-grow-1">{children}</main>
|
||||
<Footer />
|
||||
</div>
|
||||
|
||||
@@ -1,48 +1,19 @@
|
||||
'use client';
|
||||
|
||||
import Button from '@/components/button';
|
||||
import FormularioCard from '@/components/formulario-card';
|
||||
import { GetCuestionario } from '@/types/cuestionario';
|
||||
import EventoCard from '@/components/evento-card';
|
||||
import { useGetApi } from '@/hooks/use-get-api';
|
||||
import { GetEventoWithCuestionarios } from '@/types/evento';
|
||||
import Link from 'next/link';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import React from 'react';
|
||||
|
||||
export default function Page() {
|
||||
const [eventos, setEventos] = useState<GetCuestionario[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
async function getEventos() {
|
||||
try {
|
||||
const res = await fetch(
|
||||
`${process.env.NEXT_PUBLIC_API_URL}/cuestionario`,
|
||||
{
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
if (!res.ok) {
|
||||
throw new Error(`Error ${res.status}: ${res.statusText}`);
|
||||
}
|
||||
|
||||
const data: GetCuestionario[] = await res.json();
|
||||
setEventos(data);
|
||||
} catch (err) {
|
||||
console.error('Error en getEventos:', err);
|
||||
setError('Error al cargar los eventos');
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}
|
||||
|
||||
getEventos();
|
||||
}, []);
|
||||
const { loading, data, error } = useGetApi<GetEventoWithCuestionarios[]>(
|
||||
'/evento/activos/cuestionarios'
|
||||
);
|
||||
|
||||
if (loading) return <div>Cargando formularios...</div>;
|
||||
if (error) return <div>{error}</div>;
|
||||
if (error) return <div className="alert alert-danger">{error.message}</div>;
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -52,28 +23,25 @@ export default function Page() {
|
||||
<Button>Crear Evento</Button>
|
||||
</Link>
|
||||
</div>
|
||||
<div className="cards-1">
|
||||
<div className="container">
|
||||
<div className="row">
|
||||
{eventos.map((cuestionario, index) => {
|
||||
const fadeClass = `delay-${(index % 5) + 1}`;
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`col-md-6 col-lg-4 my-4 fade-in-up-bounce ${fadeClass}`}
|
||||
key={cuestionario.id_cuestionario}
|
||||
>
|
||||
<FormularioCard
|
||||
cuestionario={cuestionario}
|
||||
link={`/administrador/formulario/${cuestionario.id_cuestionario}`}
|
||||
button_message="Registrarse"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
{loading && <div>Cargando...</div>}
|
||||
{data && (
|
||||
<div className="row">
|
||||
{data.map((item, key) => {
|
||||
const fadeClass = `delay-${(key % 5) + 1}`;
|
||||
return (
|
||||
<div
|
||||
className={`col-md-6 col-lg-4 my-4 fade-in-up-bounce ${fadeClass}`}
|
||||
key={key}
|
||||
>
|
||||
<EventoCard
|
||||
evento={item}
|
||||
user='administrador'
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -22,8 +22,8 @@ export default function Page() {
|
||||
render: (_, row) => row.participante.correo,
|
||||
},
|
||||
{
|
||||
key: 'fecha_inscripcion',
|
||||
label: 'Fecha inscripción',
|
||||
key: 'fecha_registro',
|
||||
label: 'Fecha registro',
|
||||
render: (val) => new Date(val as string).toLocaleString(),
|
||||
},
|
||||
{
|
||||
|
||||
@@ -26,11 +26,14 @@ export default function Page() {
|
||||
{loading && <div>Cargando...</div>}
|
||||
{data && (
|
||||
<div className="row">
|
||||
{data.map((item, key) => (
|
||||
<div className="col-6 col-md-4" key={key}>
|
||||
<EventoCard evento={item} />
|
||||
</div>
|
||||
))}
|
||||
{data.map((item, key) => {
|
||||
const fadeClass = `delay-${(key % 5) + 1}`;
|
||||
return (
|
||||
<div className={`col-md-6 col-lg-4 my-4 fade-in-up-bounce ${fadeClass}`} key={key}>
|
||||
<EventoCard evento={item} />
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -6,8 +6,10 @@ import MarkdownRenderer from './markdown-render';
|
||||
|
||||
export default function EventoCard({
|
||||
evento,
|
||||
user = 'public',
|
||||
}: {
|
||||
evento: GetEventoWithCuestionarios;
|
||||
user?: 'public' | 'administrador' | 'staff';
|
||||
}) {
|
||||
const [verMas, setVerMas] = useState(false);
|
||||
const descripcion = evento.descripcion_evento;
|
||||
@@ -22,7 +24,15 @@ export default function EventoCard({
|
||||
<div className="card card-blog d-flex flex-column justify-content-between">
|
||||
<div>
|
||||
<div className="card-image scale-hover-1">
|
||||
<Link href={`/evento/${evento.id_evento}`}>
|
||||
<Link
|
||||
href={
|
||||
user === 'public'
|
||||
? `/evento/${evento.nombre_evento.split(' ').join('_')}/${
|
||||
evento.id_evento
|
||||
}/${evento.cuestionarios[0]?.id_cuestionario}`
|
||||
: `/${user}/evento/${evento.id_evento}/${evento.cuestionarios[0]?.id_cuestionario}`
|
||||
}
|
||||
>
|
||||
<Image
|
||||
width={400}
|
||||
height={300}
|
||||
@@ -51,7 +61,11 @@ export default function EventoCard({
|
||||
evento.cuestionarios.map((cuestionario) => (
|
||||
<div key={cuestionario.id_cuestionario} className="">
|
||||
<Link
|
||||
href={`/evento/${evento.nombre_evento.split(' ').join('_')}/${evento.id_evento}/${cuestionario.id_cuestionario}`}
|
||||
href={
|
||||
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}
|
||||
|
||||
@@ -0,0 +1,142 @@
|
||||
'use client';
|
||||
import ImageUploader from '@/components/banner-uploader';
|
||||
import Button from '@/components/button';
|
||||
import Input from '@/components/input';
|
||||
import Select from '@/components/select';
|
||||
import { CreateEventoType } from '@/types/create-evento';
|
||||
import { GetEventoWithCuestionario } from '@/types/evento';
|
||||
import axiosInstance from '@/utils/api-config';
|
||||
import { getAxiosError } from '@/utils/errors-utils';
|
||||
import React, { useState } from 'react';
|
||||
import toast from 'react-hot-toast';
|
||||
|
||||
interface EditEventoProps {
|
||||
evento: GetEventoWithCuestionario;
|
||||
handleChange: (field: keyof CreateEventoType, value: string | Date) => void;
|
||||
handleOnChange: (eventoActualizado: GetEventoWithCuestionario) => void;
|
||||
}
|
||||
|
||||
export default function EditEvento({
|
||||
evento,
|
||||
handleChange,
|
||||
handleOnChange,
|
||||
}: EditEventoProps) {
|
||||
const toDateInputValue = (isoString: string) => isoString.slice(0, 10);
|
||||
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [nuevoBanner, setNuevoBanner] = useState<File | null>(null);
|
||||
|
||||
const handleOnSave = async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
// 1. Subir nuevo banner si fue cambiado
|
||||
if (nuevoBanner) {
|
||||
const formData = new FormData();
|
||||
formData.append('banner', nuevoBanner);
|
||||
|
||||
await axiosInstance.post(
|
||||
`/evento/${evento.id_evento}/banner`,
|
||||
formData,
|
||||
{
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data',
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// 2. Actualizar el evento (sin incluir 'banner')
|
||||
const res = await axiosInstance.patch(`/evento/${evento.id_evento}`, {
|
||||
nombre_evento: evento.nombre_evento,
|
||||
descripcion_evento: evento.descripcion_evento,
|
||||
tipo_evento: evento.tipo_evento,
|
||||
fecha_inicio: evento.fecha_inicio,
|
||||
fecha_fin: evento.fecha_fin,
|
||||
});
|
||||
|
||||
toast.success('Evento actualizado correctamente');
|
||||
handleOnChange(res.data); // Actualiza en el padre
|
||||
} catch (error) {
|
||||
const msg = getAxiosError(error);
|
||||
toast.error(`Error al actualizar el evento: ${msg.message}`);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="row border p-3 rounded my-4">
|
||||
<h2>Editar Información del Evento</h2>
|
||||
<p>Modifica los datos del evento que desees actualizar.</p>
|
||||
|
||||
<ImageUploader
|
||||
label="Banner del evento"
|
||||
defaultBanner={
|
||||
evento.banner
|
||||
? `${process.env.NEXT_PUBLIC_API_URL}/banners/${evento.banner}`
|
||||
: undefined
|
||||
}
|
||||
onChange={(file) => {
|
||||
setNuevoBanner(file);
|
||||
}}
|
||||
/>
|
||||
|
||||
<Input
|
||||
label="Nombre del evento"
|
||||
required
|
||||
value={evento.nombre_evento}
|
||||
onChange={(e) => handleChange('nombre_evento', e.target.value)}
|
||||
/>
|
||||
|
||||
<div className="mb-3">
|
||||
<label className="form-label">Descripción del evento</label>
|
||||
<textarea
|
||||
className="form-control bg-white"
|
||||
rows={5}
|
||||
placeholder="Describe el evento..."
|
||||
value={evento.descripcion_evento}
|
||||
onChange={(e) => handleChange('descripcion_evento', e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Select
|
||||
label="Tipo de evento"
|
||||
value={evento.tipo_evento}
|
||||
placeholder="Selecciona un tipo de evento"
|
||||
onChange={(e) => handleChange('tipo_evento', e.target.value)}
|
||||
options={[
|
||||
{ label: 'Conferencia', value: 'conferencia' },
|
||||
{ label: 'Taller', value: 'taller' },
|
||||
{ label: 'Seminario', value: 'seminario' },
|
||||
{ label: 'Otro', value: 'otro' },
|
||||
]}
|
||||
/>
|
||||
|
||||
<div className="col-md-6">
|
||||
<Input
|
||||
label="Fecha de inicio"
|
||||
type="date"
|
||||
className={{ container: 'mb-3' }}
|
||||
value={toDateInputValue(evento.fecha_inicio)}
|
||||
onChange={(e) => handleChange('fecha_inicio', e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="col-md-6">
|
||||
<Input
|
||||
label="Fecha de fin"
|
||||
type="date"
|
||||
className={{ container: 'mb-3' }}
|
||||
value={toDateInputValue(evento.fecha_fin)}
|
||||
onChange={(e) => handleChange('fecha_fin', e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Button icon="save" disabled={loading} onClick={handleOnSave}>
|
||||
{loading ? 'Guardando...' : 'Guardar Cambios'}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -84,15 +84,17 @@ export default function FormularioRegistro({
|
||||
? `/alumnos/${cuenta}`
|
||||
: `/trabajadores/${cuenta}`;
|
||||
|
||||
console.log('Buscando información de cuenta:', endpoint);
|
||||
const res = await axiosInstance.get(endpoint);
|
||||
console.log('Datos de cuenta obtenidos:', res.data);
|
||||
|
||||
// Usar datos fake para pruebas
|
||||
// Usar datos fake para pruebas
|
||||
setCuentaInfo({
|
||||
nombre: 'Juan',
|
||||
apellidos: 'Pérez',
|
||||
correo: '421010301@pcpuma.acatlan.unam.mx',
|
||||
correo: 'juan.perez@pcpuma.acatlan.unam.mx',
|
||||
genero: 'M',
|
||||
carrera: 'Ingeniería',
|
||||
carrera: 'Ingeniería en Computación',
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Error al obtener datos de cuenta:', error);
|
||||
@@ -267,7 +269,7 @@ export default function FormularioRegistro({
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="preguntas">
|
||||
<hr />
|
||||
<h2 className="text-xl font-bold">{data?.cuestionario.nombre_form}</h2>
|
||||
{data?.cuestionario.descripcion && (
|
||||
|
||||
@@ -34,13 +34,13 @@ export const plantillasDisponibles: Plantilla[] = [
|
||||
validacion: 'comunidad_trabajador',
|
||||
},
|
||||
{
|
||||
titulo: 'Número de trabajador',
|
||||
titulo: 'RFC',
|
||||
tipo: 'Abierta (Respuesta corta)',
|
||||
obligatoria: false,
|
||||
validacion: 'cuenta_trabajador',
|
||||
validacion: 'rfc',
|
||||
},
|
||||
{
|
||||
titulo: 'Correo electrónico institucional',
|
||||
titulo: 'Correo electrónico',
|
||||
tipo: 'Abierta (Respuesta corta)',
|
||||
obligatoria: true,
|
||||
validacion: 'correo',
|
||||
|
||||
Vendored
+11
-1
@@ -163,4 +163,14 @@ input {
|
||||
font-weight: bold;
|
||||
margin-left: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.preguntas {
|
||||
@include media-breakpoint-up(md) {
|
||||
margin-inline: 5rem;
|
||||
}
|
||||
|
||||
@include media-breakpoint-up(lg) {
|
||||
margin-inline: 14rem;
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+1
@@ -13,6 +13,7 @@ export type TiposValidacion =
|
||||
| 'genero'
|
||||
| 'carrera'
|
||||
| 'institucion'
|
||||
| 'rfc'
|
||||
|
||||
export type TipoPregunta =
|
||||
| 'Abierta (Respuesta corta)'
|
||||
|
||||
Vendored
+2
-1
@@ -1,7 +1,8 @@
|
||||
export interface ParticipacionEvento {
|
||||
id_participante: number;
|
||||
id_evento: number;
|
||||
fecha_inscripcion: string; // formato ISO
|
||||
id_cuestionario: number;
|
||||
fecha_registro: string; // formato ISO
|
||||
estatus: boolean;
|
||||
fecha_asistencia: string | null;
|
||||
participante: Participante;
|
||||
|
||||
Reference in New Issue
Block a user