Files
formularios_front/src/components/evento/evento-card-preview.tsx
T
miguel 24642d1c3b feat: Enhance event and form management with previews and improved UI
- 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.
2025-08-14 21:59:38 -06:00

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>
);
}