67 lines · 2.0 KB
import { Link, useRouter } from "@tanstack/react-router";
import type { ErrorComponentProps } from "@tanstack/react-router";
import { ERROR_COPY, errorKind, type ErrorKind } from "~/lib/error-copy";
import { HttpError } from "~/server/errors";
import { Button, buttonClass } from "~/ui/Button";
import { ErrorScreen } from "~/ui/ErrorScreen";

const INTERNAL = 500;
const RETRYABLE: ReadonlySet<ErrorKind> = new Set(["server", "unavailable", "rate-limited"]);

/** HttpErrors carry user-facing messages; anything else may leak internals, so only show it in dev. */
function errorDetail(error: unknown): string | null {
  if (error instanceof HttpError) return error.message;
  if (import.meta.env.DEV && error instanceof Error) return error.message;
  return null;
}

function PrimaryAction({ kind, onRetry }: { kind: ErrorKind; onRetry: () => void }) {
  if (kind === "unauthorized") {
    return (
      <Link to="/login" className={buttonClass("primary")}>
        Sign in
      </Link>
    );
  }
  if (!RETRYABLE.has(kind)) return null;
  return (
    <Button variant="primary" onClick={onRetry}>
      Try again
    </Button>
  );
}

export function DefaultCatchBoundary({ error, reset }: ErrorComponentProps) {
  const router = useRouter();
  console.error("Route error:", error);

  const status = error instanceof HttpError ? error.status : INTERNAL;
  const kind = errorKind(status);
  const copy = ERROR_COPY[kind];