244 lines
8.6 KiB
TypeScript
244 lines
8.6 KiB
TypeScript
'use client';
|
|
import ImageUploader from '@/components/banner-uploader';
|
|
import Button from '@/components/button';
|
|
import Input from '@/components/input';
|
|
import Select from '@/components/select';
|
|
import EventoCardPreview from '@/components/evento/evento-card-preview';
|
|
import { CreateEventoType } from '@/types/create-evento';
|
|
import { GetEventoWithCuestionariosWithCupos } from '@/types/evento';
|
|
import axiosInstance from '@/utils/api-config';
|
|
import { tipoEventos } from '@/utils/arrays';
|
|
import { formatDateLocal } from '@/utils/date-utils';
|
|
import { getAxiosError } from '@/utils/errors-utils';
|
|
import { commands } from '@uiw/react-md-editor';
|
|
import dynamic from 'next/dynamic';
|
|
import React, { useState } from 'react';
|
|
import toast from 'react-hot-toast';
|
|
const MDEditor = dynamic(() => import('@uiw/react-md-editor'), { ssr: false });
|
|
|
|
const MAX_LENGTH = 500;
|
|
|
|
interface EditEventoProps {
|
|
evento: GetEventoWithCuestionariosWithCupos;
|
|
handleChange: (field: keyof CreateEventoType, value: string | Date) => void;
|
|
handleOnChange: (
|
|
eventoActualizado: GetEventoWithCuestionariosWithCupos
|
|
) => void;
|
|
}
|
|
|
|
export default function EditEvento({
|
|
evento,
|
|
handleChange,
|
|
handleOnChange,
|
|
}: EditEventoProps) {
|
|
const tipo_usuario = sessionStorage.getItem('axxxd')?.replace(/"/g, '') || '';
|
|
|
|
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);
|
|
}
|
|
};
|
|
|
|
// Convertir evento a CreateEventoType para el preview
|
|
const eventoPreview: CreateEventoType = {
|
|
tipo_evento: evento.tipo_evento,
|
|
nombre_evento: evento.nombre_evento,
|
|
descripcion_evento: evento.descripcion_evento,
|
|
fecha_inicio: new Date(evento.fecha_inicio),
|
|
fecha_fin: new Date(evento.fecha_fin),
|
|
};
|
|
|
|
return (
|
|
<div className="container-fluid my-4">
|
|
{/* Header */}
|
|
<div className="row mb-4">
|
|
<div className="col-12">
|
|
<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 confirmalos.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="row">
|
|
{/* Columna Izquierda - Formulario */}
|
|
<div className="col-xl-7 col-lg-6">
|
|
<div className="pe-lg-4">
|
|
<div className="p-4">
|
|
{/* Banner */}
|
|
<div className="mb-4">
|
|
{tipo_usuario === 'administrador' && (
|
|
<>
|
|
<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>
|
|
|
|
{/* Detalles del evento */}
|
|
<div className="col-12">
|
|
<h4 className="mb-3">Detalles del Evento</h4>
|
|
|
|
<Input
|
|
label="Nombre del evento"
|
|
required
|
|
value={evento.nombre_evento}
|
|
onChange={(e) =>
|
|
handleChange('nombre_evento', e.target.value)
|
|
}
|
|
disabled={tipo_usuario !== 'administrador'}
|
|
|
|
/>
|
|
|
|
<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) => {
|
|
if (tipo_usuario !== 'administrador') return;
|
|
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}
|
|
disabled={tipo_usuario !== 'administrador'}
|
|
/>
|
|
|
|
<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>
|
|
|
|
{/* Botón de guardar */}
|
|
{tipo_usuario === "administrador" && <div className="d-flex justify-content-end mt-4">
|
|
<Button icon="save" disabled={loading} onClick={handleOnSave}>
|
|
{loading ? 'Guardando...' : 'Guardar Cambios'}
|
|
</Button>
|
|
</div>}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Columna Derecha - Preview */}
|
|
<div className="col-xl-5 col-lg-6">
|
|
<div className="ps-lg-4">
|
|
<div className="sticky-top" style={{ top: '20px' }}>
|
|
<h4 className="mb-3 d-none d-lg-block">Preview del Evento</h4>
|
|
<div className="d-lg-none mt-4">
|
|
<h4 className="mb-3">Preview del Evento</h4>
|
|
</div>
|
|
<EventoCardPreview
|
|
evento={eventoPreview}
|
|
eventoBanner={nuevoBanner}
|
|
existingBanner={evento.banner}
|
|
cuestionariosCount={evento.cuestionarios?.length || 0}
|
|
/>
|
|
<div className="alert alert-info text-center mt-3">
|
|
<strong>Vista previa:</strong> Así se verá tu evento en las
|
|
listas de eventos.
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|