add lazy error boundary componnet

This commit is contained in:
Nate Kelley 2025-09-22 19:37:41 -06:00
parent e962ffa5fa
commit 18a1c15412
No known key found for this signature in database
GPG Key ID: FD90372AB8D98B4F
3 changed files with 120 additions and 9 deletions

View File

@ -4,16 +4,19 @@ import { useEffect } from 'react';
import { Button } from '@/components/ui/buttons';
import { Card, CardContent, CardFooter } from '@/components/ui/card/CardBase';
import { Title } from '@/components/ui/typography';
import { cn } from '@/lib/utils';
import { useMount } from '../../../hooks/useMount';
export const ErrorCard = ({
footer,
className,
header = 'Looks like we hit an unexpected error',
message = "Our team has been notified via Slack. We'll take a look at the issue ASAP and get back to you.",
}: {
header?: string;
message?: string;
footer?: React.ReactNode;
className?: string;
}) => {
useMount(() => {
console.error('Error in card:', header, message);
@ -21,7 +24,10 @@ export const ErrorCard = ({
return (
<div
className=" flex h-full w-full flex-col items-center absolute inset-0 justify-center bg-linear-to-br bg-background p-8 backdrop-blur-xs backdrop-filter"
className={cn(
'flex h-full w-full flex-col items-center absolute inset-0 justify-center bg-linear-to-br bg-background p-8 backdrop-blur-xs backdrop-filter',
className
)}
role="alert"
>
<Card className="-mt-10 max-w-100">

View File

@ -1,19 +1,39 @@
import { useNavigate } from '@tanstack/react-router';
import type React from 'react';
import { useMemo, useState } from 'react';
import { ErrorBoundary } from 'react-error-boundary';
import { Button } from '@/components/ui/buttons';
import { Paragraph, Text, Title } from '@/components/ui/typography';
import { useIsVersionChanged } from '@/context/AppVersion/useAppVersion';
import { ErrorCard } from './GlobalErrorCard';
import { useMount } from '@/hooks/useMount';
import { cn } from '@/lib/utils';
import { Route as HomeRoute } from '@/routes/app/_app/home';
export const LazyErrorBoundary: React.FC<React.PropsWithChildren> = ({ children }) => {
const isChanged = useIsVersionChanged();
const navigate = useNavigate();
return (
<ErrorBoundary
fallbackRender={({ error, resetErrorBoundary }) => {
console.log(isChanged);
console.log(error);
fallbackRender={() => {
if (isChanged) {
return (
<ComponentErrorCard
highlightType="info"
message="The app has been updated. Please reload the page."
title="New version available"
buttonText="Reload"
onButtonClick={() => {
navigate({ reloadDocument: true });
}}
/>
);
}
return (
<ErrorCard
header="Oops, something went wrong"
message="Our team has been notified via Slack. We'll take a look at the issue ASAP and get back to you."
<ComponentErrorCard
onButtonClick={() => {
navigate({ to: HomeRoute.to, reloadDocument: true });
}}
/>
);
}}
@ -22,3 +42,88 @@ export const LazyErrorBoundary: React.FC<React.PropsWithChildren> = ({ children
</ErrorBoundary>
);
};
const TestThrow = () => {
const [showThrow, setShowThrow] = useState(false);
useMount(() => {
setTimeout(() => {
setShowThrow(true);
}, 1000);
});
if (showThrow) {
throw new Error('Test error');
}
return 'loading';
};
const ComponentErrorCard: React.FC<{
title?: string;
message?: string;
containerClassName?: string;
cardClassName?: string;
buttonText?: string;
onButtonClick?: () => void;
highlightType?: 'danger' | 'info' | 'none';
}> = ({
containerClassName,
cardClassName,
highlightType = 'danger',
onButtonClick,
buttonText = 'Take me home',
title = 'We hit an unexpected error',
message = "Our team has been notified via Slack. We'll take a look at the issue ASAP and get back to you.",
}) => {
const style: React.CSSProperties = useMemo(() => {
let vars: React.CSSProperties = {};
if (highlightType === 'danger') {
vars = {
'--color-highlight-background': 'var(--color-red-100)',
'--color-highlight-border': 'red',
} as React.CSSProperties;
}
if (highlightType === 'info') {
vars = {
'--color-highlight-background': 'var(--color-purple-50)',
'--color-highlight-border': 'blue',
} as React.CSSProperties;
}
return {
...vars,
'--color-highlight-to-background': 'transparent',
'--color-highlight-to-border': 'transparent',
'--duration': '600ms',
} as React.CSSProperties;
}, [highlightType]);
return (
<div
className={cn(
'animate-highlight-fade',
'flex h-full w-full flex-col items-center justify-center p-5 bg-background text-center gap-4',
containerClassName
)}
style={style}
role="alert"
>
<div
className={cn(
'bg-background flex flex-col gap-4 p-5 rounded duration-300 shadow hover:shadow-lg transition-all border',
highlightType === 'danger' && 'shadow-red-100 ',
cardClassName
)}
>
<Title as={'h4'}>{title}</Title>
<Paragraph>{message}</Paragraph>
<Button variant="black" block size="tall" onClick={onButtonClick}>
{buttonText}
</Button>
</div>
</div>
);
};

View File

@ -68,7 +68,7 @@ const AppVersionMessage = () => {
};
export const useIsVersionChanged = () => {
const { data } = useQuery({
const { data = false } = useQuery({
...versionGetAppVersion,
select: useCallback((data: { buildId: string }) => checkNewVersion(data.buildId), []),
});