import type { IconDefinition } from "@fortawesome/fontawesome-svg-core"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { faTriangleExclamation, faRotateRight, faChevronDown, faChevronUp, faClipboard, } from "@fortawesome/free-solid-svg-icons"; import { useState, type FC } from "react"; type Variant = "inline" | "card" | "banner"; export type ErrorStateProps = { /** Main heading shown to the user */ title?: string; /** Friendly message for the user */ message?: string; /** Raw error to help devs (object, string, whatever) */ error?: unknown; /** Called when user clicks Retry */ onRetry?: () => Promise | void; /** Show a Retry button */ showRetry?: boolean; /** Optional custom icon */ icon?: IconDefinition; /** Visual style */ variant?: Variant; /** Additional actions (e.g. “Report”) */ actions?: React.ReactNode; /** Test id for testing */ "data-testid"?: string; /** ClassName passthrough */ className?: string; }; function formatError(err: unknown) { if (!err) return ""; if (typeof err === "string") return err; if (err instanceof Error) return err.stack || err.message; try { return JSON.stringify(err, null, 2); } catch { return String(err); } } const baseStyles = "w-full text-left flex items-start gap-3 rounded-md border"; const variants: Record = { inline: "p-3 border-red-200 bg-red-50 text-red-800", card: "p-4 border-red-200 bg-red-50 text-red-800 shadow-sm", banner: "p-3 border-red-200 bg-red-50 text-red-800 rounded-none border-x-0", }; export const ErrorState: FC = ({ title = "Something went wrong", message = "Please try again or contact support if the problem persists.", error, onRetry, showRetry = !!onRetry, icon = faTriangleExclamation, variant = "inline", actions, className = "", ...rest }) => { const [expanded, setExpanded] = useState(false); const [retrying, setRetrying] = useState(false); const details = formatError(error); async function handleRetry() { if (!onRetry) return; try { setRetrying(true); await onRetry(); } finally { setRetrying(false); } } function copyDetails() { if (!details) return; navigator.clipboard?.writeText(details).catch(() => {}); } return (

{title}

{message &&

{message}

} {/* Controls */}
{showRetry && ( )} {details && ( <> )} {actions}
{/* Dev details (collapsible) */} {expanded && details && (
            {details}
          
)}
); }; export default ErrorState;