/** * 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 && ( {actionLabel} )} {secondaryLabel && onSecondaryAction && ( {secondaryLabel} )} )} ); } 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;
{description}