import { Box, Button, Typography } from '@mui/joy' import PropTypes from 'prop-types' import { Link } from 'react-router-dom' /** * The single empty/error surface for the app. * * variant drives the tone, the icon tile color and the a11y role: * - 'empty' nothing exists yet. Teach the feature, offer the way in. * - 'no-results' something exists, the current search/filter hides it. * - 'error' we failed to load. Say what happened, offer a retry. * * Actions are objects instead of nodes so every call site gets the same * button vocabulary (solid primary lead, plain neutral follow). */ const TONES = { empty: { tileBg: 'primary.softHoverBg', halo: 'primary.softBg', iconColor: 'primary.softColor', role: 'status', }, 'no-results': { tileBg: 'neutral.softHoverBg', halo: 'neutral.softBg', iconColor: 'neutral.softColor', role: 'status', }, error: { tileBg: 'danger.softHoverBg', halo: 'danger.softBg', iconColor: 'danger.softColor', role: 'alert', }, } const SIZES = { sm: { tile: 48, icon: '1.375rem', halo: 6, py: 5, title: 'title-sm', titleSize: '1rem', }, md: { tile: 68, icon: '1.875rem', halo: 10, py: 8, title: 'title-md', titleSize: '1.25rem', }, } const ActionButton = ({ action, ...buttonProps }) => { const { label, onClick, to, ...rest } = action return ( ) } ActionButton.propTypes = { action: PropTypes.object.isRequired, } const EmptyState = ({ description, fullHeight = false, icon, primaryAction, secondaryAction, size = 'md', sx, title, variant = 'empty', ...rest }) => { const tone = TONES[variant] || TONES.empty const dimensions = SIZES[size] || SIZES.md const buttonSize = size === 'sm' ? 'sm' : 'md' return ( {icon && ( )} {title} {description && ( {description} )} {(primaryAction || secondaryAction) && ( {primaryAction && ( )} {secondaryAction && ( )} )} ) } EmptyState.propTypes = { variant: PropTypes.oneOf(['empty', 'no-results', 'error']), icon: PropTypes.node, title: PropTypes.node.isRequired, description: PropTypes.node, primaryAction: PropTypes.shape({ label: PropTypes.node.isRequired, onClick: PropTypes.func, to: PropTypes.string, startDecorator: PropTypes.node, }), secondaryAction: PropTypes.shape({ label: PropTypes.node.isRequired, onClick: PropTypes.func, to: PropTypes.string, startDecorator: PropTypes.node, }), size: PropTypes.oneOf(['sm', 'md']), fullHeight: PropTypes.bool, sx: PropTypes.object, } export default EmptyState