"use client"; import { ExternalLink } from "lucide-react"; import { ReactNode } from "react"; import { Badge } from "@/components/shadcn"; import { cn } from "@/lib/utils"; // `null` means never checked, not disconnected: it must not get the fail tokens. const CONNECTION_BADGE = { connected: { label: "Connected", variant: "success", dotClassName: "bg-bg-pass", }, disconnected: { label: "Disconnected", variant: "error", dotClassName: "bg-bg-fail", }, unchecked: { label: "Not checked yet", variant: "tag", dotClassName: "bg-bg-data-muted", }, } as const; type ConnectionBadgeState = keyof typeof CONNECTION_BADGE; const connectionBadgeState = ( connected: boolean | null, ): ConnectionBadgeState => connected === null ? "unchecked" : connected ? "connected" : "disconnected"; interface IntegrationCardChip { label: string; className?: string; } interface IntegrationConnectionStatus { connected: boolean | null; label?: string; } interface IntegrationCardHeaderProps { icon: ReactNode; title: string; subtitle?: string; /** * A quiet third line under the subtitle, for a fact about the integration * rather than a claim about it — when its connection was last checked, say. */ meta?: ReactNode; chips?: IntegrationCardChip[]; connectionStatus?: IntegrationConnectionStatus; navigationUrl?: string; /** The integration's own controls, pinned to the end of the row. */ actions?: ReactNode; } export const IntegrationCardHeader = ({ icon, title, subtitle, meta, chips = [], connectionStatus, navigationUrl, actions, }: IntegrationCardHeaderProps) => { const badgeState = connectionStatus ? connectionBadgeState(connectionStatus.connected) : null; const badge = badgeState ? CONNECTION_BADGE[badgeState] : null; // The end of the row belongs to the controls wherever there are any, so the // status travels with the name it qualifies instead of across the card. const statusBesideTitle = Boolean(actions); const statusBadge = badge && badgeState ? ( ) : null; const hasAside = chips.length > 0 || Boolean(actions) || (statusBadge && !statusBesideTitle); return (
{icon}

{title}

{navigationUrl && ( )} {statusBesideTitle && statusBadge}
{subtitle && (

{subtitle}

)} {meta}
{hasAside && (
{chips.map((chip, index) => ( {chip.label} ))} {!statusBesideTitle && statusBadge} {actions}
)}
); };