24642d1c3b
- Added EventoCardPreview component to display event details with existing and new banners. - Updated FormularioCardAdmin and FormularioCardUser components to handle event images and display questionnaire counts. - Improved FormularioEditor with better layout and added preview functionality for forms. - Introduced EmptyEventsState component for better user experience when no events are available. - Enhanced FormularioCardPreview to support both existing and new forms with dynamic banner handling. - Refactored code for better readability and maintainability across components.
162 lines
5.0 KiB
TypeScript
162 lines
5.0 KiB
TypeScript
'use client';
|
|
|
|
import { CreateEventoType } from '@/types/create-evento';
|
|
import Image from 'next/image';
|
|
import React, { useState, useEffect } from 'react';
|
|
import MarkdownRenderer from '@/components/markdown-render';
|
|
import { formatearFechaCard } from '@/utils/date-utils';
|
|
import Button from '../button';
|
|
|
|
interface EventoPreviewProps {
|
|
evento: CreateEventoType;
|
|
eventoBanner?: File | null;
|
|
existingBanner?: string | null; // Banner existente desde la API
|
|
cuestionariosCount?: number; // Número de formularios existentes
|
|
}
|
|
|
|
export default function EventoCardPreview({
|
|
evento,
|
|
eventoBanner,
|
|
existingBanner,
|
|
cuestionariosCount = 0,
|
|
}: EventoPreviewProps) {
|
|
const [verMas, setVerMas] = useState(false);
|
|
const [bannerUrl, setBannerUrl] = useState<string>('/default-banner.png');
|
|
|
|
const descripcion = evento.descripcion_evento ?? '';
|
|
const limite = 100;
|
|
|
|
const descripcionRecortada =
|
|
descripcion.length > limite && !verMas
|
|
? `${descripcion.slice(0, limite)}...`
|
|
: descripcion;
|
|
|
|
// Actualizar URL del banner cuando cambie el archivo o cuando haya un banner existente
|
|
useEffect(() => {
|
|
if (eventoBanner) {
|
|
// Si hay un nuevo banner seleccionado, usarlo
|
|
const url = URL.createObjectURL(eventoBanner);
|
|
setBannerUrl(url);
|
|
|
|
// Limpiar URL anterior cuando el componente se desmonte
|
|
return () => URL.revokeObjectURL(url);
|
|
} else if (existingBanner) {
|
|
// Si no hay nuevo banner pero sí hay uno existente desde la API, usarlo
|
|
setBannerUrl(
|
|
`${process.env.NEXT_PUBLIC_API_URL}/banners/${existingBanner}`
|
|
);
|
|
} else {
|
|
// Si no hay ningún banner, usar el banner por defecto
|
|
setBannerUrl('/default-banner.png');
|
|
}
|
|
}, [eventoBanner, existingBanner]);
|
|
|
|
// Obtener información de fecha usando la utilidad
|
|
const getDateInfo = () => {
|
|
if (!evento.fecha_inicio || !evento.fecha_fin) {
|
|
return { mesTexto: 'MES', diaTexto: 'DD' };
|
|
}
|
|
|
|
return formatearFechaCard(evento.fecha_inicio, evento.fecha_fin);
|
|
};
|
|
|
|
const { mesTexto, diaTexto } = getDateInfo();
|
|
|
|
return (
|
|
<div
|
|
className="card shadow-sm border-0 mb-2"
|
|
style={{ borderRadius: '12px', overflow: 'hidden' }}
|
|
>
|
|
<div className="position-relative">
|
|
<Image
|
|
width={400}
|
|
height={250}
|
|
className="img-fluid w-100"
|
|
src={bannerUrl}
|
|
alt="Banner formulario"
|
|
style={{ objectFit: 'cover', height: '200px' }}
|
|
/>
|
|
|
|
{/* Badge de cantidad de formularios en la esquina superior derecha */}
|
|
<div className="position-absolute top-0 end-0 m-2">
|
|
{cuestionariosCount > 0 ? (
|
|
<span className="badge bg-primary">
|
|
{cuestionariosCount} formulario
|
|
{cuestionariosCount !== 1 ? 's' : ''}
|
|
</span>
|
|
) : (
|
|
<span className="badge bg-secondary">Sin formularios</span>
|
|
)}
|
|
</div>
|
|
</div>
|
|
|
|
<div className="card-body p-3 d-flex flex-column">
|
|
{/* Fecha del evento */}
|
|
<div className="d-flex align-items-center mb-2">
|
|
<div className="me-3" style={{ minWidth: '50px' }}>
|
|
<div
|
|
className="text-muted text-center small"
|
|
style={{
|
|
fontSize: mesTexto.includes(' - ') ? '0.65rem' : '0.75rem',
|
|
lineHeight: 1.1,
|
|
}}
|
|
>
|
|
{mesTexto}
|
|
</div>
|
|
<div
|
|
className="fw-bold mb-0 text-center"
|
|
style={{
|
|
lineHeight: 1,
|
|
whiteSpace: 'nowrap',
|
|
fontSize: diaTexto.toString().length > 2 ? '1rem' : '1.25rem',
|
|
}}
|
|
>
|
|
{diaTexto}
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<h5 className="card-title mb-1 fw-bold">
|
|
{evento.nombre_evento || 'Nombre del evento'}
|
|
</h5>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Tipo de evento */}
|
|
{evento.tipo_evento && (
|
|
<div className="mb-2">
|
|
<small className="text-muted">
|
|
<i className="bi bi-tag-fill me-1"></i>
|
|
{evento.tipo_evento}
|
|
</small>
|
|
</div>
|
|
)}
|
|
|
|
{/* Descripción */}
|
|
<div className="card-description flex-grow-1 mb-3">
|
|
{descripcion ? (
|
|
<>
|
|
<MarkdownRenderer markdown={descripcionRecortada} />
|
|
{descripcion.length > limite && (
|
|
<button
|
|
onClick={() => setVerMas(!verMas)}
|
|
className="btn btn-link btn-sm p-0 ms-1 align-baseline text-decoration-none"
|
|
style={{ fontSize: '0.875rem' }}
|
|
>
|
|
{verMas ? 'Ver menos' : 'Ver más'}
|
|
</button>
|
|
)}
|
|
</>
|
|
) : (
|
|
<p className="text-muted small">Descripción del evento...</p>
|
|
)}
|
|
</div>
|
|
|
|
{/* Botón principal - deshabilitado */}
|
|
<Button variant="primary" className="w-100 rounded-pill" disabled>
|
|
Ver/Editar evento
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|