63 lines · 2.6 KB
import { cn } from "~/utils/cn";

type Props = {
  status: number;
  title: string;
  description: React.ReactNode;
  /** A git-style output line shown above the status, e.g. `fatal: couldn't find remote ref`. */
  command: string;
  /** Extra technical context (e.g. the server's message). */
  detail?: string | null;
  actions: React.ReactNode;
  tone?: "accent" | "danger";
};

/** A branch that leaves main and ends at a dashed, detached commit — the visual for every error page. */
function DetachedBranch({ tone }: { tone: "accent" | "danger" }) {
  const stroke = tone === "danger" ? "stroke-danger-400" : "stroke-accent-500";
  return (
    <svg viewBox="0 0 240 200" aria-hidden="true" className="w-full max-w-xs">
      <path d="M40 10V190" className="stroke-gray-800" strokeWidth="3" fill="none" strokeLinecap="round" />
      <path
        d="M40 70c0 40 120 20 120 70"
        className={cn("animate-graph-draw", stroke)}
        strokeWidth="3"
        fill="none"
        strokeLinecap="round"
      />
      <path d="M160 140v20" className={stroke} strokeWidth="3" strokeDasharray="4 6" strokeLinecap="round" />
      {[30, 70, 130, 170].map((y) => (
        <circle key={y} cx="40" cy={y} r="7" className="fill-gray-950 stroke-gray-700" strokeWidth="3" />
      ))}
      <circle cx="160" cy="176" r="12" className={cn("fill-gray-950", stroke)} strokeWidth="3" strokeDasharray="5 5" />
    </svg>
  );
}

export function ErrorScreen({ status, title, description, command, detail, actions, tone = "accent" }: Props) {
  return (
    <div>
      <div className="mx-auto grid max-w-5xl items-center gap-12 px-6 py-20 md:grid-cols-[1fr_auto] md:py-28">