refactor: update event handling to use GetEventoWithCuestionariosWithCupos type and replace EventoCard with CuestionarioCard

This commit is contained in:
miguel
2025-08-11 12:55:14 -06:00
parent 99b444ccf4
commit 5b0f0ad6bf
7 changed files with 151 additions and 136 deletions
@@ -3,7 +3,7 @@ import EditEvento from '@/containers/edit-evento';
import { useGetApi } from '@/hooks/use-get-api';
import {
CuestionarioWithCupo,
GetEventoWithCuestionarios,
GetEventoWithCuestionariosWithCupos,
} from '@/types/evento';
import { useParams } from 'next/navigation';
import React, { useState, useEffect } from 'react';
@@ -21,11 +21,12 @@ type Params = {
export default function Page() {
const params = useParams<Params>();
const { data } = useGetApi<GetEventoWithCuestionarios>(
const { data } = useGetApi<GetEventoWithCuestionariosWithCupos>(
`/evento/${params.id_evento}/cuestionarios`
);
const [evento, setEvento] = useState<GetEventoWithCuestionarios | null>(null);
const [evento, setEvento] =
useState<GetEventoWithCuestionariosWithCupos | null>(null);
const [loading, setLoading] = useState(false);
useEffect(() => {
@@ -47,7 +48,7 @@ export default function Page() {
};
const handleEventoActualizado = (
eventoActualizado: GetEventoWithCuestionarios
eventoActualizado: GetEventoWithCuestionariosWithCupos
) => {
setEvento(eventoActualizado);
};
@@ -133,10 +134,7 @@ export default function Page() {
return (
<div className="my-4">
<Link
href={`/administrador`}
className="btn btn-link mb-3"
>
<Link href={`/administrador`} className="btn btn-link mb-3">
<i className="bi bi-arrow-left me-2"></i>
Volver
</Link>
+10 -8
View File
@@ -1,19 +1,20 @@
'use client';
import Button from '@/components/button';
import EventoCard from '@/components/evento-card';
import CuestionarioCard from '@/components/cuestionario-card';
import { useGetApi } from '@/hooks/use-get-api';
import { GetEventoWithCuestionarios } from '@/types/evento';
import { GetEventoWithCuestionariosWithCupos } from '@/types/evento';
import Link from 'next/link';
import React from 'react';
export default function Page() {
const { loading, data, error } = useGetApi<GetEventoWithCuestionarios[]>(
'/evento/activos/cuestionarios'
);
const { data: recientes } = useGetApi<GetEventoWithCuestionarios[]>(
const { loading, data, error } = useGetApi<
GetEventoWithCuestionariosWithCupos[]
>('/evento/activos/cuestionarios');
const { data: recientes } = useGetApi<GetEventoWithCuestionariosWithCupos[]>(
'/evento/recientes/cuestionarios'
);
const { data: eventos } = useGetApi('/evento/activos');
if (loading) return <div>Cargando formularios...</div>;
if (error) return <div className="alert alert-danger">{error.message}</div>;
@@ -36,7 +37,7 @@ export default function Page() {
className={`col-md-6 col-lg-4 my-4 fade-in-up-bounce ${fadeClass}`}
key={key}
>
<EventoCard evento={item} user="administrador" />
<CuestionarioCard evento={item} user="administrador" />
</div>
);
})}
@@ -53,12 +54,13 @@ export default function Page() {
className={`col-md-6 col-lg-4 my-4 fade-in-up-bounce ${fadeClass}`}
key={key}
>
<EventoCard evento={item} user="administrador" />
<CuestionarioCard evento={item} user="administrador" />
</div>
);
})}
</div>
)}
<pre>{JSON.stringify(eventos, null, 2)}</pre>
</>
);
}
+14 -9
View File
@@ -1,13 +1,13 @@
'use client';
import ClientCarousel from '@/client-components/client-carousel';
import EventoCard from '@/components/evento-card';
import CuestionarioCard from '@/components/cuestionario-card';
import { useGetApi } from '@/hooks/use-get-api';
import { GetEventoWithCuestionarios } from '@/types/evento';
import { GetEventoWithCuestionariosWithCupos } from '@/types/evento';
import React from 'react';
export default function Page() {
const { loading, data } = useGetApi<GetEventoWithCuestionarios[]>(
const { loading, data } = useGetApi<GetEventoWithCuestionariosWithCupos[]>(
'/evento/activos/cuestionarios'
);
@@ -15,10 +15,12 @@ export default function Page() {
<div>
<div className="mx-auto mb-5 mt-3 fade-in-down-bounce">
<ClientCarousel
images={[{
src: '/banner.jpeg',
alt: 'Banner de eventos activos',
}]}
images={[
{
src: '/banner.jpeg',
alt: 'Banner de eventos activos',
},
]}
/>
</div>
{loading && <div>Cargando...</div>}
@@ -27,8 +29,11 @@ export default function Page() {
{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
className={`col-md-6 col-lg-4 my-4 fade-in-up-bounce ${fadeClass}`}
key={key}
>
<CuestionarioCard evento={item} />
</div>
);
})}
+106
View File
@@ -0,0 +1,106 @@
'use client';
import { GetEventoWithCuestionariosWithCupos } from '@/types/evento';
import Image from 'next/image';
import Link from 'next/link';
import React, { useState } from 'react';
import MarkdownRenderer from './markdown-render';
export default function EventoCard({
evento,
user = 'public',
}: {
evento: GetEventoWithCuestionariosWithCupos;
user?: 'public' | 'administrador' | 'staff';
}) {
const [verMas, setVerMas] = useState(false);
const descripcion = evento.descripcion_evento ?? '';
const limite = 100;
const descripcionRecortada =
descripcion.length > limite && !verMas
? `${descripcion.slice(0, limite)}...`
: descripcion;
return (
<div className="card card-blog d-flex flex-column justify-content-between">
<div>
<div className="card-image scale-hover-1">
<Link
href={
user === 'administrador' || user === 'staff'
? `/${user}/evento/${evento.id_evento}`
: `/evento/${evento.nombre_evento.split(' ').join('_')}/${
evento.id_evento
}/${evento.cuestionarios[0].id_cuestionario}`
}
>
<Image
width={400}
height={300}
className="img-fluid"
src={`${process.env.NEXT_PUBLIC_API_URL}/banners/${evento.banner}`}
alt="Banner formulario"
/>
<div className="card-caption">{evento.nombre_evento}</div>
</Link>
<div className="ripple-cont"></div>
</div>
<div className="card-body">
<div className="card-description mb-0">
{evento.cuestionarios[0]?.cupo_maximo === null ? (
<span className="badge bg-success mb-2">Sin límite</span>
) : (
<span className="badge bg-primary mb-2">
{evento.cuestionarios[0]?.cupos_disponibles} cupos disponibles
</span>
)}
<MarkdownRenderer markdown={descripcionRecortada} />
{descripcion.length > limite && (
<button
onClick={() => setVerMas(!verMas)}
className="btn btn-link btn-sm p-0 ms-1 align-baseline"
style={{ fontSize: '0.875rem' }}
>
{verMas ? 'Ver menos' : 'Ver más'}
</button>
)}
</div>
{evento.cuestionarios.length > 0 &&
evento.cuestionarios.map((cuestionario) => (
<div key={cuestionario.id_cuestionario} className="">
{user === 'administrador' || user === 'staff' ? (
<Link
href={`/${user}/evento/${evento.id_evento}`}
className="btn mt-2 w-100 btn-outline-primary"
>
Ver evento
</Link>
) : cuestionario.cupos_disponibles === 0 &&
cuestionario.cupo_maximo !== null ? (
<button
className="btn mt-2 w-100 btn-outline-secondary"
disabled
>
Cupo lleno
</button>
) : (
<Link
href={`/evento/${evento.nombre_evento
.split(' ')
.join('_')}/${evento.id_evento}/${
cuestionario.id_cuestionario
}`}
className="btn mt-2 w-100 btn-outline-primary"
>
Registro
</Link>
)}
</div>
))}
</div>
</div>
</div>
);
}
+3 -104
View File
@@ -1,106 +1,5 @@
'use client';
import { GetEventoWithCuestionarios } from '@/types/evento';
import Image from 'next/image';
import Link from 'next/link';
import React, { useState } from 'react';
import MarkdownRenderer from './markdown-render';
import React from 'react';
export default function EventoCard({
evento,
user = 'public',
}: {
evento: GetEventoWithCuestionarios;
user?: 'public' | 'administrador' | 'staff';
}) {
const [verMas, setVerMas] = useState(false);
const descripcion = evento.descripcion_evento ?? '';
const limite = 100;
const descripcionRecortada =
descripcion.length > limite && !verMas
? `${descripcion.slice(0, limite)}...`
: descripcion;
return (
<div className="card card-blog d-flex flex-column justify-content-between">
<div>
<div className="card-image scale-hover-1">
<Link
href={
user === 'administrador' || user === 'staff'
? `/${user}/evento/${evento.id_evento}`
: `/evento/${evento.nombre_evento.split(' ').join('_')}/${
evento.id_evento
}/${evento.cuestionarios[0].id_cuestionario}`
}
>
<Image
width={400}
height={300}
className="img-fluid"
src={`${process.env.NEXT_PUBLIC_API_URL}/banners/${evento.banner}`}
alt="Banner formulario"
/>
<div className="card-caption">{evento.nombre_evento}</div>
</Link>
<div className="ripple-cont"></div>
</div>
<div className="card-body">
<div className="card-description mb-0">
{evento.cuestionarios[0]?.cupo_maximo === null ? (
<span className="badge bg-success mb-2">Sin límite</span>
) : (
<span className="badge bg-primary mb-2">
{evento.cuestionarios[0]?.cupos_disponibles} cupos disponibles
</span>
)}
<MarkdownRenderer markdown={descripcionRecortada} />
{descripcion.length > limite && (
<button
onClick={() => setVerMas(!verMas)}
className="btn btn-link btn-sm p-0 ms-1 align-baseline"
style={{ fontSize: '0.875rem' }}
>
{verMas ? 'Ver menos' : 'Ver más'}
</button>
)}
</div>
{evento.cuestionarios.length > 0 &&
evento.cuestionarios.map((cuestionario) => (
<div key={cuestionario.id_cuestionario} className="">
{user === 'administrador' || user === 'staff' ? (
<Link
href={`/${user}/evento/${evento.id_evento}`}
className="btn mt-2 w-100 btn-outline-primary"
>
Ver evento
</Link>
) : cuestionario.cupos_disponibles === 0 &&
cuestionario.cupo_maximo !== null ? (
<button
className="btn mt-2 w-100 btn-outline-secondary"
disabled
>
Cupo lleno
</button>
) : (
<Link
href={`/evento/${evento.nombre_evento
.split(' ')
.join('_')}/${evento.id_evento}/${
cuestionario.id_cuestionario
}`}
className="btn mt-2 w-100 btn-outline-primary"
>
Registro
</Link>
)}
</div>
))}
</div>
</div>
</div>
);
export default function EventoCard() {
return <div>EventoCard</div>;
}
+5 -4
View File
@@ -4,7 +4,7 @@ import Button from '@/components/button';
import Input from '@/components/input';
import Select from '@/components/select';
import { CreateEventoType } from '@/types/create-evento';
import { GetEventoWithCuestionarios } from '@/types/evento';
import { GetEventoWithCuestionariosWithCupos } from '@/types/evento';
import axiosInstance from '@/utils/api-config';
import { formatDateLocal } from '@/utils/date-utils';
import { getAxiosError } from '@/utils/errors-utils';
@@ -15,9 +15,11 @@ import toast from 'react-hot-toast';
const MDEditor = dynamic(() => import('@uiw/react-md-editor'), { ssr: false });
interface EditEventoProps {
evento: GetEventoWithCuestionarios;
evento: GetEventoWithCuestionariosWithCupos;
handleChange: (field: keyof CreateEventoType, value: string | Date) => void;
handleOnChange: (eventoActualizado: GetEventoWithCuestionarios) => void;
handleOnChange: (
eventoActualizado: GetEventoWithCuestionariosWithCupos
) => void;
}
export default function EditEvento({
@@ -25,7 +27,6 @@ export default function EditEvento({
handleChange,
handleOnChange,
}: EditEventoProps) {
const [loading, setLoading] = useState(false);
const [nuevoBanner, setNuevoBanner] = useState<File | null>(null);
+7 -3
View File
@@ -1,4 +1,4 @@
import { TipoPregunta, TiposValidacion } from "./create-formulario";
import { TipoPregunta, TiposValidacion } from './create-formulario';
export interface GetEvento {
id_evento: number;
@@ -34,7 +34,6 @@ export interface CuestionarioWithCupo extends Cuestionario {
cupos_usados: number;
cupos_disponibles: number;
}
export interface GetCuestionario {
tipo_cuestionario: {
id_tipo_cuestionario: number;
@@ -82,10 +81,15 @@ export interface CuestionarioWithSecciones extends Cuestionario {
}[];
}
export interface GetEventoWithCuestionarios extends GetEvento {
export interface GetEventoWithCuestionariosWithCupos extends GetEvento {
cuestionarios: CuestionarioWithCupo[];
}
export interface GetEventoWithCuestionario extends GetEvento {
cuestionario: Cuestionario;
}
export interface GetEventoWithCuestionarios extends GetEvento {
cuestionarios: Cuestionario[];
total_cuestionarios: number;
}