Display a generic fallback component when initial config load fails (#8588)
Fixes: #8487 #5027
1. Summary
The purpose of these changes is to elevate the dev/user experience when
the initial config load call fails for whatever reason by displaying a
fallback component.
2. Solution
I ended up making more changes than I initially planned. I had to update
the order of the contexts a bit because `GenericErrorFallback` is
dependent on `AppThemeProvider` for styling and `AppThemeProvider` is
dependent on `ObjectMetadataItemsProvider` for
[`useObjectMetadataItem`](ae2f193d68/packages/twenty-front/src/modules/object-metadata/hooks/useObjectMetadataItem.ts (L22))
hook (`AppThemeProvider` -> `useColorScheme` -> `useUpdateOneRecord` ->
`useObjectMetadataItem`). I had to create a wrapper component for
`AppThemeProvider` and stylize it in a way that it looks responsive on
both mobile and desktop devices. Finally, I had to introduce the
`isErrored` flag to differentiate the loading and error states.
There are some improvements we can make later -
- Display a loading state for the initial config load
- Implement a refetch logic for the initial config loading failure
3. Recording
https://github.com/user-attachments/assets/c2f43573-8006-4118-8e18-8576099d78fd
https://github.com/user-attachments/assets/9c5853d3-539b-4880-aa38-c416c3e13594
---------
Co-authored-by: Félix Malfait <felix@twenty.com>
This commit is contained in:
@ -12,10 +12,16 @@ export const AppErrorBoundary = ({ children }: { children: ReactNode }) => {
|
||||
});
|
||||
};
|
||||
|
||||
// TODO: Implement a better reset strategy, hard reload for now
|
||||
const handleReset = () => {
|
||||
window.location.reload();
|
||||
};
|
||||
|
||||
return (
|
||||
<ErrorBoundary
|
||||
FallbackComponent={GenericErrorFallback}
|
||||
onError={handleError}
|
||||
onReset={handleReset}
|
||||
>
|
||||
{children}
|
||||
</ErrorBoundary>
|
||||
|
||||
@ -0,0 +1,41 @@
|
||||
import styled from '@emotion/styled';
|
||||
import { MOBILE_VIEWPORT } from 'twenty-ui';
|
||||
import { GenericErrorFallback } from './GenericErrorFallback';
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
background: ${({ theme }) => theme.background.noisy};
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
height: 100dvh;
|
||||
width: 100%;
|
||||
padding-top: ${({ theme }) => theme.spacing(3)};
|
||||
padding-left: ${({ theme }) => theme.spacing(3)};
|
||||
padding-bottom: 0;
|
||||
|
||||
@media (max-width: ${MOBILE_VIEWPORT}px) {
|
||||
padding-left: 0;
|
||||
padding-bottom: ${({ theme }) => theme.spacing(3)};
|
||||
}
|
||||
`;
|
||||
|
||||
type ClientConfigErrorProps = {
|
||||
error: Error;
|
||||
};
|
||||
|
||||
export const ClientConfigError = ({ error }: ClientConfigErrorProps) => {
|
||||
// TODO: Implement a better loading strategy
|
||||
const handleReset = () => {
|
||||
window.location.reload();
|
||||
};
|
||||
|
||||
return (
|
||||
<StyledContainer>
|
||||
<GenericErrorFallback
|
||||
error={error}
|
||||
resetErrorBoundary={handleReset}
|
||||
title="Unable to Reach Back-end"
|
||||
hidePageHeader
|
||||
/>
|
||||
</StyledContainer>
|
||||
);
|
||||
};
|
||||
@ -15,11 +15,16 @@ import {
|
||||
} from 'twenty-ui';
|
||||
import { isDeeplyEqual } from '~/utils/isDeeplyEqual';
|
||||
|
||||
type GenericErrorFallbackProps = FallbackProps;
|
||||
type GenericErrorFallbackProps = FallbackProps & {
|
||||
title?: string;
|
||||
hidePageHeader?: boolean;
|
||||
};
|
||||
|
||||
export const GenericErrorFallback = ({
|
||||
error,
|
||||
resetErrorBoundary,
|
||||
title = 'Sorry, something went wrong',
|
||||
hidePageHeader = false,
|
||||
}: GenericErrorFallbackProps) => {
|
||||
const location = useLocation();
|
||||
|
||||
@ -33,13 +38,14 @@ export const GenericErrorFallback = ({
|
||||
|
||||
return (
|
||||
<PageContainer>
|
||||
<PageHeader />
|
||||
{!hidePageHeader && <PageHeader />}
|
||||
|
||||
<PageBody>
|
||||
<AnimatedPlaceholderEmptyContainer>
|
||||
<AnimatedPlaceholder type="errorIndex" />
|
||||
<AnimatedPlaceholderEmptyTextContainer>
|
||||
<AnimatedPlaceholderEmptyTitle>
|
||||
Server’s on a coffee break
|
||||
{title}
|
||||
</AnimatedPlaceholderEmptyTitle>
|
||||
<AnimatedPlaceholderEmptySubTitle>
|
||||
{error.message}
|
||||
@ -49,7 +55,7 @@ export const GenericErrorFallback = ({
|
||||
Icon={IconRefresh}
|
||||
title="Reload"
|
||||
variant={'secondary'}
|
||||
onClick={() => resetErrorBoundary()}
|
||||
onClick={resetErrorBoundary}
|
||||
/>
|
||||
</AnimatedPlaceholderEmptyContainer>
|
||||
</PageBody>
|
||||
|
||||
Reference in New Issue
Block a user