mirror of https://github.com/buster-so/buster.git
update error card
This commit is contained in:
parent
36c0b6da71
commit
000f66911c
|
@ -1,64 +1,47 @@
|
|||
'use client';
|
||||
|
||||
import { Component, ErrorInfo, ReactNode } from 'react';
|
||||
import React, { ReactNode } from 'react';
|
||||
import { Button } from '@/components/ui/buttons/Button';
|
||||
import { Card, CardContent, CardFooter } from '@/components/ui/card/CardBase';
|
||||
import { ErrorBoundary } from '@/components/ui/error';
|
||||
|
||||
interface Props {
|
||||
children: ReactNode;
|
||||
}
|
||||
|
||||
interface State {
|
||||
hasError: boolean;
|
||||
}
|
||||
const ErrorCard = () => {
|
||||
return (
|
||||
<div
|
||||
className="bg-opacity-90 flex min-h-screen w-screen flex-col items-center justify-center bg-linear-to-br from-gray-50 to-gray-200 p-8 backdrop-blur-xs backdrop-brightness-95 backdrop-filter"
|
||||
role="alert">
|
||||
<Card className="-mt-10">
|
||||
<CardContent>
|
||||
<div className="flex flex-col gap-4">
|
||||
<h1 className="animate-fade-in text-2xl font-semibold">
|
||||
Looks like we hit an error! 😅
|
||||
</h1>
|
||||
|
||||
export class GlobalErrorComponent extends Component<Props, State> {
|
||||
state = {
|
||||
hasError: false
|
||||
};
|
||||
<h5 className="animate-slide-up m-0 text-base font-medium text-gray-600">
|
||||
Don't worry, it's not you - it's us!
|
||||
</h5>
|
||||
<h5 className="animate-slide-up m-0 text-base font-medium text-gray-500">
|
||||
If this error persists, please contact Buster support!
|
||||
</h5>
|
||||
</div>
|
||||
</CardContent>
|
||||
|
||||
static getDerivedStateFromError(_: Error): State {
|
||||
return { hasError: true };
|
||||
}
|
||||
<CardFooter className="w-full pt-0">
|
||||
<a href="/" className="w-full">
|
||||
<Button variant="black" block size="tall">
|
||||
Take Me Home 🏠
|
||||
</Button>
|
||||
</a>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
componentDidCatch(error: Error, errorInfo: ErrorInfo) {
|
||||
console.error('Global error:', error, errorInfo);
|
||||
}
|
||||
|
||||
render() {
|
||||
if (this.state.hasError) {
|
||||
return (
|
||||
<div
|
||||
className="bg-opacity-90 flex min-h-screen w-screen flex-col items-center justify-center bg-linear-to-br from-gray-50 to-gray-200 p-8 backdrop-blur-xs backdrop-brightness-95 backdrop-filter"
|
||||
role="alert">
|
||||
<Card className="-mt-10">
|
||||
<CardContent>
|
||||
<div className="flex flex-col gap-4">
|
||||
<h1 className="animate-fade-in text-2xl font-semibold">
|
||||
Looks like we hit an error! 😅
|
||||
</h1>
|
||||
|
||||
<h5 className="animate-slide-up m-0 text-base font-medium text-gray-600">
|
||||
Don't worry, it's not you - it's us!
|
||||
</h5>
|
||||
<h5 className="animate-slide-up m-0 text-base font-medium text-gray-500">
|
||||
If this error persists, please contact Buster support!
|
||||
</h5>
|
||||
</div>
|
||||
</CardContent>
|
||||
|
||||
<CardFooter className="w-full pt-0">
|
||||
<a href="/" className="w-full">
|
||||
<Button variant="black" block size="tall">
|
||||
Take Me Home 🏠
|
||||
</Button>
|
||||
</a>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return this.props.children;
|
||||
}
|
||||
}
|
||||
export const GlobalErrorComponent = ({ children }: Props) => {
|
||||
return <ErrorBoundary errorComponent={<ErrorCard />}>{children}</ErrorBoundary>;
|
||||
};
|
||||
|
|
|
@ -23,20 +23,15 @@ type Story = StoryObj<typeof SidebarUserFooterComponent>;
|
|||
export const Default: Story = {
|
||||
args: {
|
||||
name: 'John Doe',
|
||||
email: 'john.doe@example.com'
|
||||
}
|
||||
};
|
||||
|
||||
export const WithoutAvatar: Story = {
|
||||
args: {
|
||||
name: 'Jane Smith',
|
||||
email: 'jane.smith@example.com'
|
||||
email: 'john.doe@example.com',
|
||||
signOut: () => Promise.resolve({ error: '' })
|
||||
}
|
||||
};
|
||||
|
||||
export const LongName: Story = {
|
||||
args: {
|
||||
name: 'Alexander Bartholomew Christopherson III',
|
||||
email: 'alexander@example.com'
|
||||
email: 'alexander@example.com',
|
||||
signOut: () => Promise.resolve({ error: '' })
|
||||
}
|
||||
};
|
||||
|
|
|
@ -25,7 +25,7 @@ const sampleCode = `function helloWorld() {
|
|||
}`;
|
||||
|
||||
const longCode = `import React from 'react';
|
||||
import { Button } from 'antd';
|
||||
import { Button } from '../buttons';
|
||||
|
||||
export const MyComponent = () => {
|
||||
const handleClick = () => {
|
||||
|
|
|
@ -1,40 +1,17 @@
|
|||
import { Alert } from 'antd';
|
||||
import { Component, ErrorInfo, ReactNode } from 'react';
|
||||
import { ErrorCard } from '@/components/ui/error/ErrorCard';
|
||||
import { ReactNode } from 'react';
|
||||
import { ErrorBoundary } from '../error/ErrorBoundary';
|
||||
|
||||
interface Props {
|
||||
children: ReactNode;
|
||||
}
|
||||
|
||||
interface State {
|
||||
hasError: boolean;
|
||||
}
|
||||
const ErrorCardComponent: React.FC = () => {
|
||||
return (
|
||||
<ErrorCard error="Something went wrong rendering the chart. This is likely an error on our end. Please contact Buster support." />
|
||||
);
|
||||
};
|
||||
|
||||
export class BusterChartErrorWrapper extends Component<Props, State> {
|
||||
state = {
|
||||
hasError: false
|
||||
};
|
||||
|
||||
static getDerivedStateFromError(_: Error): State {
|
||||
return { hasError: true };
|
||||
}
|
||||
|
||||
componentDidCatch(error: Error, errorInfo: ErrorInfo) {
|
||||
console.error('Chart Error:', error, errorInfo);
|
||||
}
|
||||
|
||||
render() {
|
||||
if (this.state.hasError) {
|
||||
return (
|
||||
<div className="flex h-full w-full items-center justify-center" role="alert">
|
||||
<Alert
|
||||
message="Something went wrong rendering the chart. This is likely an error on our end. Please contact Buster support."
|
||||
type="error"
|
||||
showIcon
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return this.props.children;
|
||||
}
|
||||
}
|
||||
export const BusterChartErrorWrapper: React.FC<Props> = ({ children }) => {
|
||||
return <ErrorBoundary errorComponent={<ErrorCardComponent />}>{children}</ErrorBoundary>;
|
||||
};
|
||||
|
|
|
@ -2,6 +2,7 @@ import React, { Component, ErrorInfo, ReactNode } from 'react';
|
|||
|
||||
interface Props {
|
||||
children: ReactNode;
|
||||
errorComponent?: ReactNode;
|
||||
onError?: () => void;
|
||||
}
|
||||
|
||||
|
@ -24,7 +25,7 @@ export class ErrorBoundary extends Component<Props, State> {
|
|||
|
||||
render() {
|
||||
if (this.state.hasError) {
|
||||
return <span className="hidden">Error boundary...</span>;
|
||||
return this.props.errorComponent;
|
||||
}
|
||||
|
||||
return this.props.children;
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
import type { Meta, StoryObj } from '@storybook/react';
|
||||
import { ErrorCard } from './ErrorCard';
|
||||
|
||||
const meta: Meta<typeof ErrorCard> = {
|
||||
title: 'UI/Error/ErrorCard',
|
||||
component: ErrorCard,
|
||||
tags: ['autodocs'],
|
||||
argTypes: {
|
||||
error: {
|
||||
control: 'text',
|
||||
description: 'The error message to display'
|
||||
},
|
||||
title: {
|
||||
control: 'text',
|
||||
description: 'Optional title for the error alert'
|
||||
},
|
||||
variant: {
|
||||
control: { type: 'select' },
|
||||
options: ['danger', 'default'],
|
||||
description: 'The visual style of the error alert'
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof ErrorCard>;
|
||||
|
||||
export const Default: Story = {
|
||||
args: {
|
||||
error: 'Something went wrong. Please try again later.',
|
||||
title: 'Error',
|
||||
variant: 'default'
|
||||
}
|
||||
};
|
||||
|
||||
export const Danger: Story = {
|
||||
args: {
|
||||
error: 'Failed to save changes. Please check your connection and try again.',
|
||||
title: 'Connection Error',
|
||||
variant: 'danger'
|
||||
}
|
||||
};
|
||||
|
||||
export const WithoutTitle: Story = {
|
||||
args: {
|
||||
error: 'This is an error message without a title.',
|
||||
variant: 'default'
|
||||
}
|
||||
};
|
||||
|
||||
export const LongErrorMessage: Story = {
|
||||
args: {
|
||||
error:
|
||||
'This is a very long error message that might wrap to multiple lines. It contains detailed information about what went wrong and possibly some suggestions on how to fix the issue.',
|
||||
title: 'Detailed Error',
|
||||
variant: 'danger'
|
||||
}
|
||||
};
|
|
@ -0,0 +1,42 @@
|
|||
import { cva, VariantProps } from 'class-variance-authority';
|
||||
import React from 'react';
|
||||
import { cn } from '@/lib/classMerge';
|
||||
import { Text } from '../typography';
|
||||
import { Xmark } from '../icons';
|
||||
|
||||
const errorVariants = cva('shadow p-3 rounded', {
|
||||
variants: {
|
||||
variant: {
|
||||
danger: 'bg-danger-foreground text-white',
|
||||
default: 'bg-background text-foreground'
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
export const ErrorCard: React.FC<
|
||||
{
|
||||
error: string;
|
||||
title?: string;
|
||||
className?: string;
|
||||
onClose?: () => void;
|
||||
} & VariantProps<typeof errorVariants>
|
||||
> = ({ error, title, variant, className, onClose }) => {
|
||||
return (
|
||||
<div className={cn('flex flex-col gap-1', errorVariants({ variant }), className)}>
|
||||
{title && <Text variant={'inherit'}>{title}</Text>}
|
||||
<Text
|
||||
className={cn(
|
||||
variant === 'danger' && 'text-white',
|
||||
variant === 'default' && 'text-text-secondary',
|
||||
'leading-[1.3]'
|
||||
)}>
|
||||
{error}
|
||||
</Text>
|
||||
{onClose && (
|
||||
<div onClick={onClose} className="absolute top-2 right-2">
|
||||
<Xmark />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
Loading…
Reference in New Issue