Files
formularios_front/src/components/formulario/formulario-card-user.tsx
T
miguel 99bfb46b8b feat: add admin management page and types
- Implemented the admin management page with a table displaying admin details.
- Created the Administrador type definition for better type safety.
- Added SubmitResponse type for handling submission responses.
- Included placeholder components for form card previews.
2025-08-19 10:26:02 -06:00

212 lines
7.0 KiB
TypeScript

'use client';
import { CuestionarioWithCupo, GetEvento } from '@/types/evento';
import Image from 'next/image';
import Link from 'next/link';
import React, { useState } from 'react';
import MarkdownRenderer from '../markdown-render';
import { toParam } from '@/utils/slugify';
import { formatearFechaCard, formatearHorario } from '@/utils/date-utils';
import Button from '../button';
export default function FormularioCardUser({
evento,
formulario,
}: {
evento: GetEvento;
formulario: CuestionarioWithCupo;
}) {
const [verMas, setVerMas] = useState(false);
const descripcion = formulario.descripcion ?? '';
const limite = 100;
// Determinar si hay cupos disponibles
const sinCupos =
formulario.cupo_maximo !== null && formulario.cupos_disponibles <= 0;
// Verificar si el evento es el mismo día
const fechaInicio = new Date(formulario.fecha_inicio);
const fechaFin = new Date(formulario.fecha_fin);
const esElMismoDia = fechaInicio.toDateString() === fechaFin.toDateString();
// Obtener información de fecha formateada usando las utilidades
const { mesTexto, diaTexto } = formatearFechaCard(
formulario.fecha_inicio,
formulario.fecha_fin
);
// Obtener horario formateado si es el mismo día
const horarioFormateado = esElMismoDia
? formatearHorario(formulario.fecha_inicio, formulario.fecha_fin)
: null;
const descripcionRecortada =
descripcion.length > limite && !verMas
? `${descripcion.slice(0, limite)}...`
: descripcion;
const imgSrc = formulario.banner
? `${process.env.NEXT_PUBLIC_API_URL}/banners/${formulario.banner}`
: evento.banner
? `${process.env.NEXT_PUBLIC_API_URL}/banners/${evento.banner}`
: `/default-banner.png`;
return (
<div
className="card shadow-sm border-0"
style={{ borderRadius: '12px', overflow: 'hidden' }}
>
<div className="position-relative">
{sinCupos ? (
<div className="position-relative">
<Image
width={400}
height={250}
className="img-fluid w-100"
src={imgSrc}
alt="Banner formulario"
style={{
objectFit: 'cover',
height: '200px',
filter: 'grayscale(50%) opacity(0.7)',
}}
/>
<div className="position-absolute top-50 start-50 translate-middle bg-gradient bg-black bg-opacity-75 text-white px-3 py-2 rounded-pill">
Sin cupos disponibles
</div>
</div>
) : (
<Link
href={`/evento/${toParam(
evento.nombre_evento,
evento.id_evento
)}/${toParam(formulario.nombre_form, formulario.id_cuestionario)}`}
>
<Image
width={400}
height={250}
className="img-fluid w-100"
src={imgSrc}
alt="Banner formulario"
style={{ objectFit: 'cover', height: '200px' }}
/>
</Link>
)}
{/* Badge de cupos en la esquina superior derecha */}
<div className="position-absolute top-0 end-0 m-2">
<span className="badge bg-primary">
{formulario.tipoCuestionario.tipo_cuestionario}{' '}
</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">
<div className="text-muted text-center small">{mesTexto}</div>
<div
className="fw-bold h4 mb-0"
style={{ lineHeight: 1, whiteSpace: 'nowrap' }}
>
{diaTexto}
</div>
</div>
<div>
<span className="text-muted fw-bold">{evento.nombre_evento}</span>
<h5 className="card-title mb-1 fw-bold">
{formulario.nombre_form}
</h5>
{formulario.tipoEvento && (
<h6 className="small text-muted">
{formulario.tipoEvento.tipo_evento}
</h6>
)}
</div>
</div>
{/* Descripción */}
<div className="card-description flex-grow-1 mb-2">
<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>
)}
</div>
{/* Información de cupos y estadísticas */}
<div className="row mb-3">
{/* Columna de horario - solo visible si es el mismo día */}
{esElMismoDia && (
<div className="col-6">
<div
className={`py-2 px-3 rounded ${
sinCupos ? 'bg-primary-subtle' : 'bg-light'
}`}
>
<div className="d-flex align-items-center">
<i className={`bi bi-clock me-2 text-azul`}></i>
<div>
<small className="text-muted d-block">Horario</small>
<span className={`fw-semibold text-azul`}>
{horarioFormateado}
</span>
</div>
</div>
</div>
</div>
)}
{/* Columna de cupos - siempre visible */}
<div className={esElMismoDia ? 'col-6' : 'col-12'}>
<div
className={`py-2 px-3 rounded ${
sinCupos ? 'bg-primary-subtle' : 'bg-light'
}`}
>
<div className="d-flex align-items-center">
<i className={`bi bi-people-fill me-2 text-primary`}></i>
<div>
<small className="text-muted d-block">Cupos</small>
<span className={`fw-semibold text-primary`}>
{formulario.cupo_maximo !== null
? `${formulario.cupos_usados} / ${formulario.cupo_maximo}`
: 'Sin límite'}
</span>
{sinCupos && (
<div className="small text-primary mt-1">
<i className="bi bi-exclamation-circle me-1"></i>
Cupos agotados
</div>
)}
</div>
</div>
</div>
</div>
</div>
{/* Botón de registro */}
{sinCupos ? (
<Button disabled className="rounded-pill">
Sin cupos disponibles
</Button>
) : (
<Link
href={`/evento/${toParam(
evento.nombre_evento,
evento.id_evento
)}/${toParam(formulario.nombre_form, formulario.id_cuestionario)}`}
className="btn btn-primary w-100 rounded-pill"
>
Registro
</Link>
)}
</div>
</div>
);
}