feat: Implement event and questionnaire management features
- Added event creation page with form handling and validation. - Introduced layout for event management with context provider. - Created event listing page with active and recent events display. - Developed questionnaire creation page linked to events. - Implemented breadcrumb navigation for better user experience. - Added reusable components for event and questionnaire cards. - Integrated API calls for fetching and managing event data. - Enhanced slugify utility for better URL handling. - Established context for managing event state across components.
This commit is contained in:
@@ -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 { tipoEventos } from '@/utils/arrays';
|
||||
import { formatDateLocal } from '@/utils/date-utils';
|
||||
import { commands } from '@uiw/react-md-editor';
|
||||
import dynamic from 'next/dynamic';
|
||||
@@ -24,80 +25,109 @@ export default function CreateEvento({
|
||||
handleBannerChange,
|
||||
}: CreateEventoProps) {
|
||||
return (
|
||||
<div className="row border p-3 rounded my-4">
|
||||
<h2>Información del Evento</h2>
|
||||
<p>
|
||||
Completa la siguiente información para describir los detalles generales
|
||||
del evento.
|
||||
</p>
|
||||
<div className="mt-2">
|
||||
<div className="p-4 border rounded bg-light">
|
||||
<h2 className="mb-2">Creación del Evento</h2>
|
||||
<p className="mb-0 text-muted">
|
||||
Completa la siguiente información para describir los detalles
|
||||
generales del evento.
|
||||
</p>
|
||||
<p className="mb-0 text-muted">
|
||||
Presiona siguiente para continuar con la creacion del formulario.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<ImageUploader label="Banner del evento" onChange={handleBannerChange} />
|
||||
|
||||
<Input
|
||||
label="Nombre del evento"
|
||||
required
|
||||
value={evento.nombre_evento}
|
||||
onChange={(e) => handleChange('nombre_evento', e.target.value)}
|
||||
/>
|
||||
|
||||
<div className="mb-4">
|
||||
<label className="form-label">Descripción del evento</label>
|
||||
<div data-color-mode="light">
|
||||
<MDEditor
|
||||
value={evento.descripcion_evento}
|
||||
onChange={(value) => {
|
||||
const texto = value || '';
|
||||
if (texto.length <= MAX_LENGTH) {
|
||||
handleChange('descripcion_evento', texto);
|
||||
}
|
||||
}}
|
||||
height={300}
|
||||
commands={[commands.bold, commands.italic, commands.hr]}
|
||||
/>
|
||||
<div className="row mt-3">
|
||||
{/* Columna Izquierda - Banner */}
|
||||
<div className="col-lg-6">
|
||||
<div className="pe-lg-3">
|
||||
<ImageUploader
|
||||
label="Banner del evento"
|
||||
onChange={handleBannerChange}
|
||||
/>
|
||||
<p className="text-muted mt-2 small">
|
||||
<strong>
|
||||
<i className="bi bi-info-circle-fill me-2"></i>
|
||||
Esta es la imagen que se verá en el carrusel del evento.
|
||||
</strong>
|
||||
<br />
|
||||
<strong>
|
||||
<i className="bi bi-image me-2"></i>
|
||||
Dimensiones recomendadas: 1200x600 píxeles.
|
||||
</strong>
|
||||
<br />
|
||||
<strong>
|
||||
<i className="bi bi-file-earmark-image me-2"></i>
|
||||
Formatos soportados: PNG, JPG, JPEG.
|
||||
</strong>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</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: 'Curso', value: 'curso' },
|
||||
{ label: 'Webinar', value: 'webinar' },
|
||||
{ label: 'Reunión', value: 'reunion' },
|
||||
{ label: 'Feria', value: 'feria' },
|
||||
{ label: 'Concierto', value: 'concierto' },
|
||||
{ label: 'Exposición', value: 'exposicion' },
|
||||
{ label: 'Deportivo', value: 'deportivo' },
|
||||
{ label: 'Cultural', value: 'cultural' },
|
||||
{ label: 'Otro', value: 'otro' },
|
||||
]}
|
||||
/>
|
||||
{/* Columna Derecha - Detalles del Evento */}
|
||||
<div className="col-lg-6">
|
||||
<div className="ps-lg-3">
|
||||
<h4 className="mb-3">Detalles del Evento</h4>
|
||||
|
||||
<div className="col-md-6">
|
||||
<Input
|
||||
label="Fecha de inicio"
|
||||
type="datetime-local"
|
||||
className={{ container: 'mb-3' }}
|
||||
value={formatDateLocal(evento.fecha_inicio)}
|
||||
onChange={(e) =>
|
||||
handleChange('fecha_inicio', new Date(e.target.value))
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<Input
|
||||
label="Nombre del evento"
|
||||
required
|
||||
value={evento.nombre_evento}
|
||||
onChange={(e) => handleChange('nombre_evento', e.target.value)}
|
||||
/>
|
||||
|
||||
<div className="col-md-6">
|
||||
<Input
|
||||
label="Fecha de fin"
|
||||
type="datetime-local"
|
||||
className={{ container: 'mb-3' }}
|
||||
value={formatDateLocal(evento.fecha_fin)}
|
||||
onChange={(e) => handleChange('fecha_fin', new Date(e.target.value))}
|
||||
/>
|
||||
<div className="mb-4">
|
||||
<label className="form-label">Descripción del evento</label>
|
||||
<div data-color-mode="light">
|
||||
<MDEditor
|
||||
value={evento.descripcion_evento}
|
||||
onChange={(value) => {
|
||||
const texto = value || '';
|
||||
if (texto.length <= MAX_LENGTH) {
|
||||
handleChange('descripcion_evento', texto);
|
||||
}
|
||||
}}
|
||||
height={200}
|
||||
commands={[commands.bold, commands.italic, commands.hr]}
|
||||
/>
|
||||
</div>
|
||||
</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={tipoEventos}
|
||||
/>
|
||||
|
||||
<div className="row">
|
||||
<div className="col-md-6">
|
||||
<Input
|
||||
label="Fecha de inicio"
|
||||
type="datetime-local"
|
||||
className={{ container: 'mb-3' }}
|
||||
value={formatDateLocal(evento.fecha_inicio)}
|
||||
onChange={(e) =>
|
||||
handleChange('fecha_inicio', new Date(e.target.value))
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="col-md-6">
|
||||
<Input
|
||||
label="Fecha de fin"
|
||||
type="datetime-local"
|
||||
className={{ container: 'mb-3' }}
|
||||
value={formatDateLocal(evento.fecha_fin)}
|
||||
onChange={(e) =>
|
||||
handleChange('fecha_fin', new Date(e.target.value))
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -7,8 +7,11 @@ import {
|
||||
import SimpleInput from '@/components/input';
|
||||
import Select from '@/components/select';
|
||||
import Button from '@/components/button';
|
||||
import { GetEventoWithCuestionariosWithCupos } from '@/types/evento';
|
||||
import { formatearRangoFechas } from '@/utils/date-utils';
|
||||
|
||||
interface Props {
|
||||
evento?: GetEventoWithCuestionariosWithCupos;
|
||||
formulario: FormularioCreacion;
|
||||
onChange: (formularioActualizado: FormularioCreacion) => void;
|
||||
}
|
||||
@@ -49,7 +52,11 @@ const tiposCuestionario = [
|
||||
{ label: 'Evaluación', value: 2 },
|
||||
];
|
||||
|
||||
export default function FormularioEditor({ formulario, onChange }: Props) {
|
||||
export default function FormularioEditor({
|
||||
formulario,
|
||||
onChange,
|
||||
evento,
|
||||
}: Props) {
|
||||
function actualizarCampo<K extends keyof FormularioCreacion>(
|
||||
campo: K,
|
||||
valor: FormularioCreacion[K]
|
||||
@@ -173,7 +180,7 @@ export default function FormularioEditor({ formulario, onChange }: Props) {
|
||||
<div className="col-md-6">
|
||||
<SimpleInput
|
||||
label="Fecha de Inicio"
|
||||
type="date"
|
||||
type="datetime-local"
|
||||
value={formulario.fecha_inicio.slice(0, 16)}
|
||||
onChange={(e) => actualizarCampo('fecha_inicio', e.target.value)}
|
||||
/>
|
||||
@@ -181,12 +188,18 @@ export default function FormularioEditor({ formulario, onChange }: Props) {
|
||||
<div className="col-md-6">
|
||||
<SimpleInput
|
||||
label="Fecha de Fin"
|
||||
type="date"
|
||||
type="datetime-local"
|
||||
value={formulario.fecha_fin.slice(0, 16)}
|
||||
onChange={(e) => actualizarCampo('fecha_fin', e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{evento && (
|
||||
<div className="badge bg-primary fs-6 w-100 mb-3">
|
||||
Este evento se realizara{' '}
|
||||
{formatearRangoFechas(evento.fecha_inicio, evento.fecha_fin)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<Select
|
||||
label="Tipo de Cuestionario"
|
||||
|
||||
+127
-82
@@ -69,93 +69,138 @@ export default function EditEvento({
|
||||
};
|
||||
|
||||
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>
|
||||
<div className="my-2">
|
||||
<div className="p-4 border rounded bg-light">
|
||||
<h2 className="mb-2">Editar Información del Evento</h2>
|
||||
<p className="mb-0 text-muted">
|
||||
Modifica los datos del evento que desees actualizar y confrimalos.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<ImageUploader
|
||||
label="Banner del evento"
|
||||
defaultBanner={
|
||||
evento.banner
|
||||
? `${process.env.NEXT_PUBLIC_API_URL}/banners/${evento.banner}`
|
||||
: undefined
|
||||
}
|
||||
onChange={(file) => {
|
||||
setNuevoBanner(file);
|
||||
}}
|
||||
/>
|
||||
<div className="p-4">
|
||||
<div className="row">
|
||||
{/* Columna Izquierda - Banner y Nombre */}
|
||||
<div className="col-lg-6">
|
||||
<div className="pe-lg-3">
|
||||
<ImageUploader
|
||||
label="Banner del evento"
|
||||
defaultBanner={
|
||||
evento.banner
|
||||
? `${process.env.NEXT_PUBLIC_API_URL}/banners/${evento.banner}`
|
||||
: undefined
|
||||
}
|
||||
onChange={(file) => {
|
||||
setNuevoBanner(file);
|
||||
}}
|
||||
/>
|
||||
<p className="text-muted mt-2 small">
|
||||
<strong>
|
||||
<i className="bi bi-info-circle-fill me-2"></i>
|
||||
Esta es la imagen que se verá en el carrusel del evento.
|
||||
</strong>
|
||||
<br />
|
||||
<strong>
|
||||
<i className="bi bi-image me-2"></i>
|
||||
Dimensiones recomendadas: 1200x600 píxeles.
|
||||
</strong>
|
||||
<br />
|
||||
<strong>
|
||||
<i className="bi bi-file-earmark-image me-2"></i>
|
||||
Formatos soportados: PNG, JPG, JPEG.
|
||||
</strong>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Input
|
||||
label="Nombre del evento"
|
||||
required
|
||||
value={evento.nombre_evento}
|
||||
onChange={(e) => handleChange('nombre_evento', e.target.value)}
|
||||
/>
|
||||
{/* Columna Derecha - Descripción, Tipo y Fechas */}
|
||||
<div className="col-lg-6">
|
||||
<div className="ps-lg-3">
|
||||
<h4 className="mb-3">Detalles del Evento</h4>
|
||||
|
||||
<div className="mb-4">
|
||||
<label className="form-label">Descripción del evento</label>
|
||||
<div data-color-mode="light">
|
||||
<MDEditor
|
||||
value={evento.descripcion_evento ?? ''}
|
||||
onChange={(value) => {
|
||||
const texto = value || '';
|
||||
if (texto.length <= 500) {
|
||||
handleChange('descripcion_evento', texto);
|
||||
}
|
||||
}}
|
||||
height={250}
|
||||
commands={[commands.bold, commands.italic, commands.hr]}
|
||||
/>
|
||||
<Input
|
||||
label="Nombre del evento"
|
||||
required
|
||||
value={evento.nombre_evento}
|
||||
onChange={(e) => handleChange('nombre_evento', e.target.value)}
|
||||
/>
|
||||
|
||||
<div className="mb-4">
|
||||
<label className="form-label">Descripción del evento</label>
|
||||
<div data-color-mode="light">
|
||||
<MDEditor
|
||||
value={evento.descripcion_evento ?? ''}
|
||||
onChange={(value) => {
|
||||
const texto = value || '';
|
||||
if (texto.length <= 500) {
|
||||
handleChange('descripcion_evento', texto);
|
||||
}
|
||||
}}
|
||||
height={200}
|
||||
commands={[commands.bold, commands.italic, commands.hr]}
|
||||
/>
|
||||
</div>
|
||||
</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: 'Curso', value: 'curso' },
|
||||
{ label: 'Webinar', value: 'webinar' },
|
||||
{ label: 'Reunión', value: 'reunion' },
|
||||
{ label: 'Feria', value: 'feria' },
|
||||
{ label: 'Concierto', value: 'concierto' },
|
||||
{ label: 'Exposición', value: 'exposicion' },
|
||||
{ label: 'Deportivo', value: 'deportivo' },
|
||||
{ label: 'Cultural', value: 'cultural' },
|
||||
{ label: 'Otro', value: 'otro' },
|
||||
]}
|
||||
/>
|
||||
|
||||
<div className="row">
|
||||
<div className="col-md-6">
|
||||
<Input
|
||||
label="Fecha de inicio"
|
||||
type="datetime-local"
|
||||
className={{ container: 'mb-3' }}
|
||||
value={formatDateLocal(new Date(evento.fecha_inicio))}
|
||||
onChange={(e) =>
|
||||
handleChange('fecha_inicio', new Date(e.target.value))
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="col-md-6">
|
||||
<Input
|
||||
label="Fecha de fin"
|
||||
type="datetime-local"
|
||||
className={{ container: 'mb-3' }}
|
||||
value={formatDateLocal(new Date(evento.fecha_fin))}
|
||||
onChange={(e) =>
|
||||
handleChange('fecha_fin', new Date(e.target.value))
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</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: 'Curso', value: 'curso' },
|
||||
{ label: 'Webinar', value: 'webinar' },
|
||||
{ label: 'Reunión', value: 'reunion' },
|
||||
{ label: 'Feria', value: 'feria' },
|
||||
{ label: 'Concierto', value: 'concierto' },
|
||||
{ label: 'Exposición', value: 'exposicion' },
|
||||
{ label: 'Deportivo', value: 'deportivo' },
|
||||
{ label: 'Cultural', value: 'cultural' },
|
||||
{ label: 'Otro', value: 'otro' },
|
||||
]}
|
||||
/>
|
||||
|
||||
<div className="col-md-6">
|
||||
<Input
|
||||
label="Fecha de inicio"
|
||||
type="datetime-local"
|
||||
className={{ container: 'mb-3' }}
|
||||
value={formatDateLocal(new Date(evento.fecha_inicio))}
|
||||
onChange={(e) =>
|
||||
handleChange('fecha_inicio', new Date(e.target.value))
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="col-md-6">
|
||||
<Input
|
||||
label="Fecha de fin"
|
||||
type="datetime-local"
|
||||
className={{ container: 'mb-3' }}
|
||||
value={formatDateLocal(new Date(evento.fecha_fin))}
|
||||
onChange={(e) => handleChange('fecha_fin', new Date(e.target.value))}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Button icon="save" disabled={loading} onClick={handleOnSave}>
|
||||
{loading ? 'Guardando...' : 'Guardar Cambios'}
|
||||
</Button>
|
||||
{/* Botón de guardar en la parte inferior */}
|
||||
<div className="row mt-4">
|
||||
<div className="col-12">
|
||||
<div className="d-flex justify-content-end">
|
||||
<Button icon="save" disabled={loading} onClick={handleOnSave}>
|
||||
{loading ? 'Guardando...' : 'Guardar Cambios'}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -65,87 +65,133 @@ export default function EditFormulario({
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="row border p-3 rounded my-4">
|
||||
<h2>Editar Información del formulario</h2>
|
||||
<p>Modifica los datos del evento que desees actualizar.</p>
|
||||
|
||||
<ImageUploader
|
||||
label="Banner del formulario"
|
||||
defaultBanner={
|
||||
cuestionario.banner
|
||||
? `${process.env.NEXT_PUBLIC_API_URL}/banners/${cuestionario.banner}`
|
||||
: undefined
|
||||
}
|
||||
onChange={(file) => {
|
||||
setNuevoBanner(file);
|
||||
}}
|
||||
/>
|
||||
|
||||
<div className="col-md-8">
|
||||
<SimpleInput
|
||||
label="Nombre del formulario"
|
||||
required
|
||||
value={cuestionario.nombre_form}
|
||||
onChange={(e) => handleChange('nombre_form', e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<div className="col-md-4">
|
||||
<SimpleInput
|
||||
label="Cupo máximo (Si no se requiere, dejar en blanco)"
|
||||
type="number"
|
||||
value={cuestionario.cupo_maximo ?? ''}
|
||||
onChange={(e) =>
|
||||
handleChange('cupo_maximo', e.target.value ? e.target.value : '')
|
||||
}
|
||||
/>
|
||||
<div className="my-2">
|
||||
<div className="p-4 border rounded bg-light">
|
||||
<h2 className="mb-2">Editar Información del Formulario</h2>
|
||||
<p className="mb-0 text-muted">
|
||||
Modifica los datos del formulario que desees actualizar y confrimalos.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="mb-4">
|
||||
<label className="form-label">Descripción del evento</label>
|
||||
<div data-color-mode="light">
|
||||
<MDEditor
|
||||
value={cuestionario.descripcion ?? ''}
|
||||
onChange={(value) => {
|
||||
const texto = value || '';
|
||||
if (texto.length <= 500) {
|
||||
handleChange(
|
||||
'descripcion' as keyof GetCuestionario,
|
||||
texto
|
||||
);
|
||||
}
|
||||
}}
|
||||
height={250}
|
||||
commands={[commands.bold, commands.italic, commands.hr]}
|
||||
/>
|
||||
<div className="p-4">
|
||||
<div className="row">
|
||||
{/* Columna Izquierda - Banner */}
|
||||
<div className="col-lg-6">
|
||||
<div className="pe-lg-3">
|
||||
<ImageUploader
|
||||
label="Banner del formulario"
|
||||
defaultBanner={
|
||||
cuestionario.banner
|
||||
? `${process.env.NEXT_PUBLIC_API_URL}/banners/${cuestionario.banner}`
|
||||
: undefined
|
||||
}
|
||||
onChange={(file) => {
|
||||
setNuevoBanner(file);
|
||||
}}
|
||||
/>
|
||||
<p className="text-muted mt-2 small">
|
||||
<strong>
|
||||
<i className="bi bi-info-circle-fill me-2"></i>
|
||||
Esta es la imagen que se usara para la carta de presentación
|
||||
del formulario.
|
||||
</strong>
|
||||
<br />
|
||||
<strong>
|
||||
<i className="bi bi-image me-2"></i>
|
||||
Dimensiones recomendadas: 1200x600 píxeles.
|
||||
</strong>
|
||||
<br />
|
||||
<strong>
|
||||
<i className="bi bi-file-earmark-image me-2"></i>
|
||||
Formatos soportados: PNG, JPG, JPEG.
|
||||
</strong>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Columna Derecha - Detalles del formulario */}
|
||||
<div className="col-lg-6">
|
||||
<div className="ps-lg-3">
|
||||
<h4 className="mb-3">Detalles del Formulario</h4>
|
||||
|
||||
<SimpleInput
|
||||
label="Nombre del formulario"
|
||||
required
|
||||
value={cuestionario.nombre_form}
|
||||
onChange={(e) => handleChange('nombre_form', e.target.value)}
|
||||
/>
|
||||
|
||||
<SimpleInput
|
||||
label="Cupo máximo (Si no se requiere, dejar en blanco)"
|
||||
type="number"
|
||||
value={cuestionario.cupo_maximo ?? ''}
|
||||
onChange={(e) =>
|
||||
handleChange(
|
||||
'cupo_maximo',
|
||||
e.target.value ? e.target.value : ''
|
||||
)
|
||||
}
|
||||
/>
|
||||
|
||||
<div className="mb-4">
|
||||
<label className="form-label">Descripción del formulario</label>
|
||||
<div data-color-mode="light">
|
||||
<MDEditor
|
||||
value={cuestionario.descripcion ?? ''}
|
||||
onChange={(value) => {
|
||||
const texto = value || '';
|
||||
if (texto.length <= 500) {
|
||||
handleChange(
|
||||
'descripcion' as keyof GetCuestionario,
|
||||
texto
|
||||
);
|
||||
}
|
||||
}}
|
||||
height={200}
|
||||
commands={[commands.bold, commands.italic, commands.hr]}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="row">
|
||||
<div className="col-md-6">
|
||||
<SimpleInput
|
||||
label="Fecha de inicio"
|
||||
type="datetime-local"
|
||||
className={{ container: 'mb-3' }}
|
||||
value={formatDateLocal(new Date(cuestionario.fecha_inicio))}
|
||||
onChange={(e) =>
|
||||
handleChange('fecha_inicio', new Date(e.target.value))
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="col-md-6">
|
||||
<SimpleInput
|
||||
label="Fecha de fin"
|
||||
type="datetime-local"
|
||||
className={{ container: 'mb-3' }}
|
||||
value={formatDateLocal(new Date(cuestionario.fecha_fin))}
|
||||
onChange={(e) =>
|
||||
handleChange('fecha_fin', new Date(e.target.value))
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="col-md-6">
|
||||
<SimpleInput
|
||||
label="Fecha de inicio"
|
||||
type="datetime-local"
|
||||
className={{ container: 'mb-3' }}
|
||||
value={formatDateLocal(new Date(cuestionario.fecha_inicio))}
|
||||
onChange={(e) =>
|
||||
handleChange('fecha_inicio', new Date(e.target.value))
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="col-md-6">
|
||||
<SimpleInput
|
||||
label="Fecha de fin"
|
||||
type="datetime-local"
|
||||
className={{ container: 'mb-3' }}
|
||||
value={formatDateLocal(new Date(cuestionario.fecha_fin))}
|
||||
onChange={(e) => handleChange('fecha_fin', new Date(e.target.value))}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Button icon="save" disabled={loading} onClick={handleOnSave}>
|
||||
{loading ? 'Guardando...' : 'Guardar Cambios'}
|
||||
</Button>
|
||||
{/* Botón de guardar en la parte inferior */}
|
||||
<div className="row mt-4">
|
||||
<div className="col-12">
|
||||
<div className="d-flex justify-content-end">
|
||||
<Button icon="save" disabled={loading} onClick={handleOnSave}>
|
||||
{loading ? 'Guardando...' : 'Guardar Cambios'}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import MarkdownRenderer from '@/components/markdown-render';
|
||||
import { useGetApi } from '@/hooks/use-get-api';
|
||||
import { GetCuestionario } from '@/types/evento';
|
||||
import SimpleInput from '@/components/input';
|
||||
@@ -340,13 +339,7 @@ export default function FormularioRegistro({
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="preguntas">
|
||||
<hr />
|
||||
<h2 className="text-xl font-bold">{data?.cuestionario.nombre_form}</h2>
|
||||
{data?.cuestionario.descripcion && (
|
||||
<MarkdownRenderer markdown={data.cuestionario.descripcion} />
|
||||
)}
|
||||
|
||||
<div>
|
||||
{preguntaComunidad && (
|
||||
<div className="my-4">
|
||||
<label
|
||||
|
||||
@@ -24,45 +24,186 @@ export default function FormularioPage({
|
||||
|
||||
return (
|
||||
<div className="container my-5">
|
||||
{/* Sección de banners mejorada */}
|
||||
<div className="text-center mb-4">
|
||||
{data?.banner && (
|
||||
<Image
|
||||
src={`${process.env.NEXT_PUBLIC_API_URL}/banners/${data.banner}`}
|
||||
alt={data?.nombre_evento || ''}
|
||||
width={800}
|
||||
height={400}
|
||||
className="img-fluid rounded"
|
||||
/>
|
||||
)}
|
||||
{data?.banner && data?.cuestionario.banner ? (
|
||||
/* Ambos banners disponibles */
|
||||
<div className="d-flex align-items-center justify-content-center gap-3 flex-wrap">
|
||||
<div className="position-relative">
|
||||
<Image
|
||||
src={`${process.env.NEXT_PUBLIC_API_URL}/banners/${data.banner}`}
|
||||
alt={`Banner del evento: ${data.nombre_evento}`}
|
||||
width={350}
|
||||
height={200}
|
||||
className="img-fluid rounded shadow-sm"
|
||||
style={{ objectFit: 'cover' }}
|
||||
/>
|
||||
<div className="position-absolute bottom-0 start-0 m-2">
|
||||
<span className="badge bg-primary bg-opacity-90">
|
||||
<i className="bi bi-calendar-event me-1"></i>
|
||||
Evento
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="d-flex align-items-center justify-content-center">
|
||||
<div
|
||||
className="rounded-circle bg-light border d-flex align-items-center justify-content-center"
|
||||
style={{ width: '50px', height: '50px' }}
|
||||
>
|
||||
<i className="bi bi-x-lg fs-4 text-muted"></i>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="position-relative">
|
||||
<Image
|
||||
src={`${process.env.NEXT_PUBLIC_API_URL}/banners/${data.cuestionario.banner}`}
|
||||
alt={`Banner del formulario: ${data.cuestionario.nombre_form}`}
|
||||
width={350}
|
||||
height={200}
|
||||
className="img-fluid rounded shadow-sm"
|
||||
style={{ objectFit: 'cover' }}
|
||||
/>
|
||||
<div className="position-absolute bottom-0 start-0 m-2">
|
||||
<span className="badge bg-success bg-opacity-90">
|
||||
<i className="bi bi-file-earmark-text me-1"></i>
|
||||
Formulario
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
) : data?.banner ? (
|
||||
/* Solo banner del evento */
|
||||
<div>
|
||||
<Image
|
||||
src={`${process.env.NEXT_PUBLIC_API_URL}/banners/${data.banner}`}
|
||||
alt={data?.nombre_evento || ''}
|
||||
width={800}
|
||||
height={400}
|
||||
className="img-fluid rounded shadow"
|
||||
/>
|
||||
<p className="text-muted mt-2 small">
|
||||
<i className="bi bi-calendar-event me-1"></i>
|
||||
Banner del evento
|
||||
</p>
|
||||
</div>
|
||||
) : data?.cuestionario.banner ? (
|
||||
/* Solo banner del formulario */
|
||||
<div>
|
||||
<Image
|
||||
src={`${process.env.NEXT_PUBLIC_API_URL}/banners/${data.cuestionario.banner}`}
|
||||
alt={data?.cuestionario.nombre_form || ''}
|
||||
width={800}
|
||||
height={400}
|
||||
className="img-fluid rounded shadow"
|
||||
/>
|
||||
<p className="text-muted mt-2 small">
|
||||
<i className="bi bi-file-earmark-text me-1"></i>
|
||||
Banner del formulario
|
||||
</p>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
<h1>{data?.nombre_evento}</h1>
|
||||
{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>
|
||||
</>
|
||||
)}
|
||||
{/* Layout de dos columnas */}
|
||||
<div className="row">
|
||||
{/* Columna izquierda - Información */}
|
||||
<div className="col-lg-5">
|
||||
{/* Información del evento */}
|
||||
<div className="mb-4">
|
||||
<h1 className="mb-3">{data?.nombre_evento}</h1>
|
||||
{data?.descripcion_evento && (
|
||||
<div className="mb-3">
|
||||
<MarkdownRenderer markdown={data?.descripcion_evento} />
|
||||
</div>
|
||||
)}
|
||||
|
||||
{data && (
|
||||
<FormularioRegistro
|
||||
id_cuestionario={data?.cuestionario.id_cuestionario}
|
||||
/>
|
||||
)}
|
||||
{data?.fecha_fin && data?.fecha_inicio && (
|
||||
<div className="mb-3">
|
||||
<p className="mb-1 fw-semibold text-muted">
|
||||
<i className="bi bi-calendar-event me-2"></i>
|
||||
Horario del evento:
|
||||
</p>
|
||||
<p className="text-muted ps-4">
|
||||
<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>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Información del formulario */}
|
||||
{data?.cuestionario && (
|
||||
<div className="card border-0 bg-light">
|
||||
<div className="card-body">
|
||||
<h5 className="card-title text-primary mb-3">
|
||||
<i className="bi bi-file-earmark-text me-2"></i>
|
||||
Registro al formulario
|
||||
</h5>
|
||||
|
||||
<div className="mb-3">
|
||||
<h6 className="fw-bold mb-2">Te estás registrando a:</h6>
|
||||
<p className="mb-1">
|
||||
<span className="badge bg-primary me-2">Formulario</span>
|
||||
<strong>{data.cuestionario.nombre_form}</strong>
|
||||
</p>
|
||||
<p className="mb-0 text-muted small">
|
||||
<span className="badge bg-secondary me-2">Evento</span>
|
||||
{data.nombre_evento}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{data.cuestionario.descripcion && (
|
||||
<div className="mb-3">
|
||||
<h6 className="fw-bold mb-2">Descripción:</h6>
|
||||
<MarkdownRenderer
|
||||
markdown={data.cuestionario.descripcion}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{data.cuestionario.fecha_inicio &&
|
||||
data.cuestionario.fecha_fin && (
|
||||
<div className="mb-0">
|
||||
<h6 className="fw-bold mb-2">Horario del formulario:</h6>
|
||||
<p className="text-muted small mb-0">
|
||||
<i className="bi bi-clock me-1"></i>
|
||||
{formatFecha(data.cuestionario.fecha_inicio, {
|
||||
horas: true,
|
||||
minutos: true,
|
||||
})}{' '}
|
||||
al{' '}
|
||||
{formatFecha(data.cuestionario.fecha_fin, {
|
||||
horas: true,
|
||||
minutos: true,
|
||||
})}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Columna derecha - Formulario */}
|
||||
<div className="col-lg-7">
|
||||
<div className="ps-lg-4">
|
||||
{data && (
|
||||
<FormularioRegistro
|
||||
id_cuestionario={data?.cuestionario.id_cuestionario}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user