Skip to content

Tx Status

Install this component.

Controls

Installation

npx shadcn@latest add https://ui.monark.io/r/tx-status.json

Source

"use client"

import * as React from "react"
import {
  CheckCircle2Icon,
  ExternalLinkIcon,
  Loader2Icon,
  XCircleIcon,
} from "lucide-react"

import { cn } from "@/lib/utils"

type TxStatusKind = "pending" | "confirmed" | "failed"

const statusConfig: Record<
  TxStatusKind,
  {
    icon: React.ComponentType<{ className?: string }>
    label: string
    tone: string
  }
> = {
  pending: {
    icon: Loader2Icon,
    label: "Pending",
    tone: "text-muted-foreground [&>svg]:animate-spin",
  },
  confirmed: {
    icon: CheckCircle2Icon,
    label: "Confirmed",
    tone: "text-emerald-600 dark:text-emerald-400",
  },
  failed: {
    icon: XCircleIcon,
    label: "Failed",
    tone: "text-destructive",
  },
}

function truncateHash(hash: string, start = 8, end = 6) {
  if (hash.length <= start + end + 1) return hash
  return `${hash.slice(0, start)}…${hash.slice(-end)}`
}

function TxStatus({
  status,
  hash,
  label,
  explorerUrl,
  href,
  className,
  ...props
}: React.HTMLAttributes<HTMLDivElement> & {
  status: TxStatusKind
  hash: string
  label?: React.ReactNode
  explorerUrl?: string
  href?: string
}) {
  const config = statusConfig[status]
  const Icon = config.icon
  const finalHref =
    href ??
    (explorerUrl
      ? `${explorerUrl.replace(/\/$/, "")}/tx/${hash}`
      : undefined)

  return (
    <div
      data-slot="tx-status"
      data-status={status}
      className={cn(
        "inline-flex items-center gap-2 rounded-md border bg-card px-3 py-2 text-sm shadow-xs",
        className
      )}
      {...props}
    >
      <span className={cn("inline-flex items-center gap-1.5", config.tone)}>
        <Icon className="size-4" />
        <span className="font-medium">{label ?? config.label}</span>
      </span>
      <span aria-hidden="true" className="text-muted-foreground">
        ·
      </span>
      {finalHref ? (
        <a
          href={finalHref}
          target="_blank"
          rel="noopener noreferrer"
          title={hash}
          className="inline-flex items-center gap-1 font-mono text-xs text-muted-foreground tabular-nums hover:text-foreground hover:underline underline-offset-4"
        >
          {truncateHash(hash)}
          <ExternalLinkIcon className="size-3" />
        </a>
      ) : (
        <span
          title={hash}
          className="font-mono text-xs text-muted-foreground tabular-nums"
        >
          {truncateHash(hash)}
        </span>
      )}
    </div>
  )
}

export { TxStatus, truncateHash }