import type { ReactNode } from "react"; import { useTranslation } from "react-i18next"; import { Icon, type IconName } from "./Icons"; export function PageHeader({ eyebrow, title, description, actions, }: { eyebrow: string; title: string; description?: string; actions?: ReactNode; }) { return (

{eyebrow}

{title}

{description &&

{description}

}
{actions &&
{actions}
}
); } export function SectionHeading({ title, description, action, headingId, }: { title: string; description?: string; action?: ReactNode; headingId?: string; }) { return (

{title}

{description &&

{description}

}
{action}
); } export function LoadingState({ label }: { label?: string }) { const { t } = useTranslation("common"); return (
); } export function ErrorState({ message }: { message: string }) { const { t } = useTranslation("common"); return (
{t("states.errorTitle")}

{message}

); } // Shared rendering for describeApiError()'s output: a localized title + explanation + // optional next step, with the raw backend/network text demoted to a "Technical // details" disclosure -- never shown as the primary message. See // frontend/src/api/errorMessages.ts and docs/fleet-ops-final-localization/audit.md. export function ApiErrorNotice({ error }: { error: import("../api/errorMessages").ApiErrorInfo | null }) { const { t } = useTranslation("common"); if (!error) return null; return (
{error.title}

{error.explanation}

{error.nextStep &&

{error.nextStep}

}
{t("actions.technicalDetails")}
{error.technical}
); } export function EmptyState({ icon = "check", title, detail, }: { icon?: IconName; title: string; detail: string; }) { return (
{title}

{detail}

); } export function IntegrationMark({ kind }: { kind: "n8n" | "rag" | "mcp" }) { const labels = { n8n: "n8n", rag: "RA", mcp: "MC" }; return ; }