/** * EmptyState - Reusable placeholder for empty data views. * * Displays an illustration (SVG or icon), title, description, * and an optional call-to-action button. Used when a list, table, * or content area has no data to show. */ import PropTypes from "prop-types"; import { motion } from "framer-motion"; // --------------------------------------------------------------------------- // Built-in Illustrations // --------------------------------------------------------------------------- function NoDataIllustration({ className = "" }) { return ( ); } NoDataIllustration.propTypes = { className: PropTypes.string, }; function SearchIllustration({ className = "" }) { return ( ); } SearchIllustration.propTypes = { className: PropTypes.string, }; const ILLUSTRATIONS = { noData: NoDataIllustration, search: SearchIllustration, }; // --------------------------------------------------------------------------- // Component // --------------------------------------------------------------------------- function EmptyState({ illustration = "noData", icon, title, description, actionLabel, onAction, secondaryLabel, onSecondaryAction, className = "", }) { const IllustrationComponent = ILLUSTRATIONS[illustration]; return ( {/* Illustration or custom icon */} {icon ? (
{icon}
) : IllustrationComponent ? ( ) : null} {/* Title */} {title && (

{title}

)} {/* Description */} {description && (

{description}

)} {/* Actions */} {(actionLabel || secondaryLabel) && (
{actionLabel && onAction && ( )} {secondaryLabel && onSecondaryAction && ( )}
)}
); } EmptyState.propTypes = { illustration: PropTypes.oneOf(["noData", "search"]), icon: PropTypes.node, title: PropTypes.string, description: PropTypes.string, actionLabel: PropTypes.string, onAction: PropTypes.func, secondaryLabel: PropTypes.string, onSecondaryAction: PropTypes.func, className: PropTypes.string, }; export default EmptyState;