Files
baya-monorepo/client/src/components/common/ErrorState/ErrorState.tsx
T
2026-07-17 17:10:39 +03:30

64 lines
2.3 KiB
TypeScript

import { FunctionComponent } from 'react';
import Paper from '@mui/material/Paper';
import Stack from '@mui/material/Stack';
import Typography from '@mui/material/Typography';
import AppButton from '../AppButton';
import AppIcon from '../AppIcon';
export interface ErrorStateProps {
/** Already-translated message — page-specific copy stays the caller's job. */
message: string;
/** Already-translated retry label (e.g. `t('common.retry')`) — kept caller-owned rather than a
* component-internal default so this file never imports next-intl: it sits at the top of the
* `common` barrel, and an eager i18n dependency there would drag `next-intl` into every test that
* imports anything from `@/components`, mocked or not (see `ErrorBoundary` for the same rule). */
retryLabel: string;
/** Required — the convention this component exists to enforce: an error is never a dead end. */
onRetry: () => void;
}
/**
* The calm, branded "something went wrong" panel with a **required** retry affordance — the fix for the
* false-empty class of defect (a failed query rendering as "no data" instead of an error). `onRetry` is
* mandatory by the type; there is no way to render this component without a way back. Presentational,
* caller-owned i18n throughout — everything is already-translated copy.
* @component ErrorState
*/
const ErrorState: FunctionComponent<ErrorStateProps> = ({ message, retryLabel, onRetry }) => (
<Paper
elevation={0}
data-error-state
sx={{
p: { xs: 3, sm: 5 },
textAlign: 'center',
border: '1px solid',
borderColor: 'divider',
borderRadius: 'var(--bal-radius-md)',
bgcolor: 'var(--bal-bg-paper)',
}}
>
<Stack sx={{ alignItems: 'center', gap: 1.5 }}>
<Stack
sx={{
width: 56,
height: 56,
borderRadius: '50%',
alignItems: 'center',
justifyContent: 'center',
bgcolor: 'var(--bal-error-soft)',
}}
>
<AppIcon icon="error" size={28} color="var(--bal-error)" />
</Stack>
<Typography variant="body2" sx={{ color: 'text.secondary', maxWidth: 360 }}>
{message}
</Typography>
<AppButton variant="outlined" color="primary" startIcon="refresh" onClick={onRetry}>
{retryLabel}
</AppButton>
</Stack>
</Paper>
);
export default ErrorState;