import { CheckIcon, ChevronRightIcon, CircleAlertIcon, CopyIcon } from "lucide-react"; import { useContext, useEffect, useId, useRef, useState, type ReactNode } from "react"; import { SectionCardContext } from "./section-variant"; import { cn } from "../lib/cn"; /* ---------------------------------------------------------------------------- ErrorMessage (design brief 6 and 7, "Also build"). What happened, then what to do. Never the raw "Opengeni API 404 ... Reference: " string: the request reference and other system facts live behind one collapsed "Technical details", with Copy. - "inline": one line inside a row, section or field group. - "block": replaces the content of a section or list that failed to load. -------------------------------------------------------------------------- */ export interface ErrorDetail { label: string; value: string; /** Render mono with a Copy button (references, IDs). */ copyable?: boolean | undefined; } export interface ErrorMessageProps { /** What happened, in product words: "Couldn't load this connection's tools." */ title: ReactNode; /** What to do next, when it isn't just "Try again". */ children?: ReactNode | undefined; /** Usually a small "Try again" button; for inline, a link-style button. */ action?: ReactNode | undefined; /** The request reference. Goes into Technical details with Copy. */ reference?: string | undefined; /** Other system facts for Technical details: status code, endpoint. */ details?: ErrorDetail[] | undefined; /** Start with Technical details open. */ defaultDetailsOpen?: boolean | undefined; variant?: "inline" | "block" | undefined; /** "center" for a failed list or page area, "start" inside a section. */ align?: "start" | "center" | undefined; /** * Announce it to screen readers as it appears. Use for errors caused by * something the person just did; leave off for errors present on load. */ announce?: boolean | undefined; className?: string | undefined; } export function ErrorMessage({ title, children, action, reference, details, defaultDetailsOpen = false, variant = "block", align = "start", announce = false, className, }: ErrorMessageProps) { const facts: ErrorDetail[] = [ ...(details ?? []), ...(reference ? [{ label: "Reference", value: reference, copyable: true }] : []), ]; const role = announce ? "alert" : undefined; if (variant === "inline") { return (

{facts.length > 0 ? (
) : null}
); } const centered = align === "center"; return (

{title}

{children ? (

{children}

) : null} {action ?
{action}
: null} {facts.length > 0 ? (
) : null}
); } /** The one collapsed "Technical details" line, with Copy on references. */ export function TechnicalDetails({ facts, align = "start", defaultOpen = false, }: { facts: ErrorDetail[]; align?: "start" | "center" | undefined; defaultOpen?: boolean | undefined; }) { const [open, setOpen] = useState(defaultOpen); const panelId = useId(); // Inside a settings card the facts sit on the card: never a box in a box. const inCard = useContext(SectionCardContext); return (
); } function CopyButton({ value, label }: { value: string; label: string }) { const [copied, setCopied] = useState(false); const timer = useRef | null>(null); useEffect( () => () => { if (timer.current) clearTimeout(timer.current); }, [], ); return ( ); }