import React, { forwardRef } from 'react'; import { clsx } from 'clsx'; import { Button } from '../../ui'; import type { ButtonProps } from '../../ui'; export interface EmptyStateAction { /** Texto del botón */ label: string; /** Función al hacer click */ onClick: () => void; /** Variante del botón */ variant?: ButtonProps['variant']; /** Icono del botón */ icon?: React.ReactNode; /** Mostrar loading en el botón */ isLoading?: boolean; } export interface EmptyStateProps { /** Icono o ilustración */ icon?: React.ReactNode; /** Título del estado vacío */ title?: string; /** Descripción del estado vacío */ description?: string; /** Variante del estado vacío */ variant?: 'no-data' | 'error' | 'search' | 'filter'; /** Acción principal */ primaryAction?: EmptyStateAction; /** Acción secundaria */ secondaryAction?: EmptyStateAction; /** Componente personalizado para ilustración */ illustration?: React.ReactNode; /** Clase CSS adicional */ className?: string; /** Tamaño del componente */ size?: 'sm' | 'md' | 'lg'; } // Iconos SVG por defecto para cada variante const DefaultIcons = { 'no-data': ( ), 'error': ( ), 'search': ( ), 'filter': ( ) }; // Mensajes por defecto en español para cada variante const DefaultMessages = { 'no-data': { title: 'No hay datos disponibles', description: 'Aún no se han registrado elementos en esta sección. Comience agregando su primer elemento.' }, 'error': { title: 'Ha ocurrido un error', description: 'No se pudieron cargar los datos. Por favor, inténtelo de nuevo más tarde.' }, 'search': { title: 'Sin resultados de búsqueda', description: 'No se encontraron elementos que coincidan con su búsqueda. Intente con términos diferentes.' }, 'filter': { title: 'Sin resultados con estos filtros', description: 'No se encontraron elementos que coincidan con los filtros aplicados. Ajuste los filtros para ver más resultados.' } }; const EmptyState = forwardRef(({ icon, title, description, variant = 'no-data', primaryAction, secondaryAction, illustration, className, size = 'md', ...props }, ref) => { const defaultMessage = DefaultMessages[variant]; const displayTitle = title || defaultMessage.title; const displayDescription = description || defaultMessage.description; const displayIcon = illustration || icon || DefaultIcons[variant]; const sizeClasses = { sm: 'py-8 px-4', md: 'py-12 px-6', lg: 'py-20 px-8' }; const titleSizeClasses = { sm: 'text-lg', md: 'text-xl', lg: 'text-2xl' }; const descriptionSizeClasses = { sm: 'text-sm', md: 'text-base', lg: 'text-lg' }; const iconContainerClasses = { sm: 'mb-4', md: 'mb-6', lg: 'mb-8' }; const containerClasses = clsx( 'flex flex-col items-center justify-center text-center', 'min-h-[200px] max-w-md mx-auto', sizeClasses[size], className ); return (
{/* Icono o Ilustración */} {displayIcon && (
{displayIcon}
)} {/* Título */} {displayTitle && (

{displayTitle}

)} {/* Descripción */} {displayDescription && (

{displayDescription}

)} {/* Acciones */} {(primaryAction || secondaryAction) && (
{primaryAction && ( )} {secondaryAction && ( )}
)}
); }); EmptyState.displayName = 'EmptyState'; export default EmptyState;