Ctrl K
ComunidadGuías…

webapps/console/components/GlobalError/GlobalError.tsx

Rama newjitsu · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
import React, { ErrorInfo, ReactNode } from "react";
import { CodeBlock } from "../CodeBlock/CodeBlock";
import { FaExclamationCircle } from "react-icons/fa";
import { Button, Collapse } from "antd";
import { getErrorMessage, getLog } from "juava";
import { PropsWithChildrenClassname } from "../../lib/ui";
import { AlertCircle } from "lucide-react";
import classNames from "classnames";
import { useUserSessionControls } from "../../lib/context";

export type GlobalErrorProps = {
  error?: any;
  title?: string;
  children?: ReactNode;
  hideActions?: boolean;
};

export type ErrorProps = { error: Error; errorInfo: ErrorInfo };

export class ErrorBoundary extends React.Component<
  { children: ReactNode; renderError: (props: ErrorProps) => ReactNode },
  ErrorProps
> {
  constructor(props) {
    super(props);
  }

  componentDidCatch(error, errorInfo) {
    console.log(`Unhandled React error: ${errorInfo?.componentStack}  `, error);
    this.setState({
      error: error,
      errorInfo: errorInfo,
    });
  }

  render() {
    if (this.state?.error) {
      return this.props.renderError(this.state);
    }
    return this.props.children;
  }
}

export function ErrorDetails(props: { error: any }) {
  const message = props.error?.message && `${props.error.message}`;
  const stack = props.error?.stack && `${props.error.stack}`;
  return (
    <Collapse defaultActiveKey={"error"}>
      {message && (
        <Collapse.Panel key={"error"} header={<div className="font-bold text-sm">Error</div>}>
          <CodeBlock breaks={"words"}>{message}</CodeBlock>
        </Collapse.Panel>
      )}
      {props.error.response && (
        <Collapse.Panel key={"response"} header={<div className="font-bold text-sm">Response</div>}>
          <CodeBlock breaks={"words"} lang="json">
            {JSON.stringify(props.error.response, null, 2)}
          </CodeBlock>
        </Collapse.Panel>
      )}
      {props.error.request && (
        <Collapse.Panel key={"request"} header={<div className="font-bold text-sm">Request</div>}>
          <CodeBlock breaks={"words"} lang="json">
            {JSON.stringify(props.error.request, null, 2)}
          </CodeBlock>
        </Collapse.Panel>
      )}
      {stack && (
        <Collapse.Panel key={"stack"} header={<div className="font-bold text-sm">Stack</div>}>
          <CodeBlock breaks={"words"}>{stack}</CodeBlock>
        </Collapse.Panel>
      )}
    </Collapse>
  );
}

const log = getLog("error");

export const EmbeddedErrorMessage: React.FC<PropsWithChildrenClassname<{ actions?: ReactNode }>> = ({
  children,
  className,
  actions,
}) => (
  <div
    className={classNames(
      `border border-error bg-error/5 px-3 py-4 text-error rounded-md w-full flex  flex-nowrap items-start`,
      className
    )}
  >
    <AlertCircle className="h-4 w-4 mr-4 p-0 m-0" />
    <div>
      {children}
      {actions && <div className="ml-auto mt-4">{actions}</div>}
    </div>
  </div>
);

function maxLen(str: string, maxLen = 200) {
  return str.length <= maxLen - 2 ? str : str.substring(0, maxLen) + "...";
}

export function SimpleErrorCard(props: { error?: any; title?: string }) {
  const title = props.title || (props.error ? maxLen(getErrorMessage(props.error), 60) : "Error");
  const description = (
    <>
      {getErrorMessage(props.error) ||
        `We encountered an error processing your request. Please try refreshing the page, or logging
        out and back in. If the problem persists, please contact support.`}
    </>
  );
  return (
    <div
      className="max-w-screen-md  px-6 py-4 border border-error/20 rounded-xl bg-error/5 w-full overflow-auto mx-auto"
      style={{ maxHeight: "80vh", maxWidth: "90vw" }}
    >
      <div className="text-error text-lg flex items-center">
        <div className="flex items-center">
          <FaExclamationCircle className="h-4 w-4 mr-2" />
          {title}
        </div>
      </div>
      <div className="pl-6 text-sm text-error font-light">
        <div>{description}</div>
      </div>
    </div>
  );
}

export function ErrorCard(props: { error?: any; title?: string; hideActions?: boolean; children?: ReactNode }) {
  const sessionControl = useUserSessionControls();

  const title = props.title || (props.error ? maxLen(getErrorMessage(props.error), 60) : "Error");
  const description = (
    <>
      {props.children || `We encountered an error processing your request`}. Please try refreshing the page, or logging
      out and back in. If the problem persists, please contact support.
    </>
  );
  const [detailsVisible, setDetailsVisible] = React.useState(false);
  return (
    <div
      className="max-w-screen-md  px-6 py-4 border border-error/20 rounded-xl bg-error/5 w-full overflow-auto mx-auto"
      style={{ maxHeight: "80vh", maxWidth: "90vw" }}
    >
      <div className="text-error text-lg flex items-center">
        <div className="flex items-center">
          <FaExclamationCircle className="h-4 w-4 mr-2" />
          {title}
        </div>
      </div>
      <div className="pl-6 text-sm text-error font-light">
        <div>{description}</div>
        {!props.hideActions && (
          <div className="pt-2 -ml-2 flex items-center justify-start gap-2">
            <Button size="small" type="link" onClick={sessionControl.logout}>
              Logout
            </Button>
            <Button size="small" type="link" onClick={() => window.location.reload()} className="mt-0.5">
              Reload
            </Button>
            {props.error && (
              <Button size="small" type="link" onClick={() => setDetailsVisible(!detailsVisible)} className="mt-0.5">
                {detailsVisible ? "Hide technical details" : "Show technical details"}
              </Button>
            )}
          </div>
        )}
      </div>
      {props.error && detailsVisible && (
        <div className="mt-6">
          {(props.error?.stack || props.error?.message) && <ErrorDetails error={props.error} />}
          {typeof props.error === "string" && <ErrorDetails error={{ message: props.error }} />}
        </div>
      )}
    </div>
  );
}

export const GlobalOverlay: React.FC<PropsWithChildrenClassname<{}>> = ({ children, className }) => (
  <div
    className={`fixed top-0 left-0 flex justify-center flex-col items-center justify-center my-4 m-0 p-0 z-50 overflow-hidden w-screen h-screen bg-background ${
      className || ""
    }`}
  >
    {children}
  </div>
);

export const GlobalError: React.FC<GlobalErrorProps> = props => (
  <GlobalOverlay>
    <ErrorCard {...props} />
  </GlobalOverlay>
);