feat: implement frontend login functionality with form handling and error management
This commit is contained in:
110
apps/frontend/app/hooks/ResponseHelper.tsx
Normal file
110
apps/frontend/app/hooks/ResponseHelper.tsx
Normal file
@@ -0,0 +1,110 @@
|
||||
import { Text } from '@radix-ui/themes';
|
||||
import { AxiosError } from 'axios';
|
||||
import { useLocation, useNavigate } from 'react-router';
|
||||
import { toast } from 'react-toastify';
|
||||
|
||||
export enum ResponseErrorToastId {
|
||||
NetworkError = 'network-error',
|
||||
}
|
||||
|
||||
export type DefaultResponseErrorHandlerOptions = {
|
||||
disableUnauthorizedHandling?: boolean;
|
||||
disableHandleUnexpectedErrors?: boolean;
|
||||
disableIgnoreCanceledRequests?: boolean;
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param err error value
|
||||
* @returns {boolean} true if the error was handled, false otherwise
|
||||
*/
|
||||
const defaultResponseErrorHandler =
|
||||
(navigate: ReturnType<typeof useNavigate>, location: ReturnType<typeof useLocation>) =>
|
||||
(err: unknown, options?: DefaultResponseErrorHandlerOptions): boolean => {
|
||||
if (!(err instanceof AxiosError) && !options?.disableHandleUnexpectedErrors) {
|
||||
toast.error(
|
||||
<div>
|
||||
<Text weight="bold">Unexpected Error:</Text>
|
||||
<br /> An unexpected error occurred. Please try again later.
|
||||
</div>,
|
||||
{
|
||||
position: 'top-center',
|
||||
autoClose: false,
|
||||
hideProgressBar: false,
|
||||
closeOnClick: true,
|
||||
pauseOnHover: true,
|
||||
draggable: false,
|
||||
progress: undefined,
|
||||
theme: 'colored',
|
||||
}
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!(err instanceof AxiosError)) return false;
|
||||
|
||||
if (err.message === 'canceled') {
|
||||
// request was aborted, ignore but return true to indicate it was handled
|
||||
return !options?.disableIgnoreCanceledRequests;
|
||||
}
|
||||
|
||||
if (err.message === 'Network Error') {
|
||||
toast.error(
|
||||
<div>
|
||||
<Text weight="bold">Network Error:</Text>
|
||||
<br /> Unable to reach the server. Please check your internet connection and try again.
|
||||
</div>,
|
||||
{
|
||||
toastId: ResponseErrorToastId.NetworkError,
|
||||
position: 'top-center',
|
||||
autoClose: false,
|
||||
hideProgressBar: false,
|
||||
closeOnClick: true,
|
||||
pauseOnHover: true,
|
||||
draggable: false,
|
||||
progress: undefined,
|
||||
theme: 'colored',
|
||||
}
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
// handle 401 Unauthorized globally
|
||||
if (err.status === 401 && !options?.disableUnauthorizedHandling) {
|
||||
// store current path for redirect after login
|
||||
const currentPath = location.pathname + location.search;
|
||||
const searchParam = new URLSearchParams();
|
||||
searchParam.set('redirect', currentPath);
|
||||
searchParam.set('message', 'Session expired, please log in again');
|
||||
navigate(`/login?${searchParam.toString()}`);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (err.status === 403) {
|
||||
toast.error(
|
||||
<div>
|
||||
<Text weight="bold">Forbidden:</Text>
|
||||
<br /> You do not have permission to perform this action.
|
||||
</div>,
|
||||
{
|
||||
position: 'top-center',
|
||||
autoClose: 5000,
|
||||
hideProgressBar: false,
|
||||
closeOnClick: true,
|
||||
pauseOnHover: true,
|
||||
draggable: false,
|
||||
progress: undefined,
|
||||
theme: 'colored',
|
||||
}
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
export function useResponseErrorHandler() {
|
||||
const navigate = useNavigate();
|
||||
const location = useLocation();
|
||||
return { defaultResponseErrorHandler: defaultResponseErrorHandler(navigate, location) };
|
||||
}
|
||||
Reference in New Issue
Block a user