diff --git a/src/features/History/History.tsx b/src/features/History/History.tsx index fe425d3..67ea013 100644 --- a/src/features/History/History.tsx +++ b/src/features/History/History.tsx @@ -1,21 +1,11 @@ import { Container, Group, Button } from '@mantine/core'; -import { notifications } from '@mantine/notifications'; import dayjs from 'dayjs'; import { useState } from 'react'; import { DateRangePicker } from 'features/History/components/DateRangePicker/DateRangePicker'; import { Graph } from 'features/History/components/Graph/Graph'; import { useLazyGetSensorDataRangeQuery } from 'features/db/dbApi'; - -const handleError = () => { - notifications.show({ - title: 'Oops!', - message: 'It seems something went wrong on our end. Please try again later', - color: 'red', - withBorder: true, - withCloseButton: false, - }); -}; +import { errorNotificationCurried } from 'utility/notificationUtils'; // TODO: Replace with actual arduino id from the store const arduinoId = 0; @@ -27,7 +17,15 @@ export const History = () => { const [getData, { isLoading, data = [] }] = useLazyGetSensorDataRangeQuery(); const handleGetData = () => - getData({ arduinoId, from, to }).unwrap().catch(handleError); + getData({ arduinoId, from, to }) + .unwrap() + .catch( + errorNotificationCurried({ + title: "Couldn't get sensor data", + message: + 'It seems something went wrong on our end. Please try again later', + }), + ); return ( diff --git a/src/features/Settings/SettingsFormProvider.tsx b/src/features/Settings/SettingsFormProvider.tsx index 59edbc0..84b19c7 100644 --- a/src/features/Settings/SettingsFormProvider.tsx +++ b/src/features/Settings/SettingsFormProvider.tsx @@ -1,6 +1,5 @@ import { zodResolver } from '@hookform/resolvers/zod'; import { Button, Group } from '@mantine/core'; -import { notifications } from '@mantine/notifications'; import { PropsWithChildren, useEffect } from 'react'; import { useForm, FormProvider } from 'react-hook-form'; @@ -14,6 +13,10 @@ import { useSetSettingsMutation, } from 'features/db/dbApi'; import { useAppSelector } from 'store/hooks'; +import { + errorNotificationCurried, + successNotificationCurried, +} from 'utility/notificationUtils'; export const SettingsFormProvider = ({ children }: PropsWithChildren) => { const [getSettings] = useLazyGetSettingsQuery(); @@ -32,24 +35,19 @@ export const SettingsFormProvider = ({ children }: PropsWithChildren) => { const onSubmit = (settings: Settings) => { setSettings({ arduinoId: selectedDevice, settings }) .unwrap() - .then(() => { - notifications.show({ - title: 'Congratulations!', + .then( + successNotificationCurried({ + title: 'Settings saved', message: 'Settings saved successfully!', - color: 'green', - withBorder: true, - withCloseButton: false, - }); - }) - .catch((error) => { - notifications.show({ - title: 'Error saving settings:', - message: error, - color: 'red', - withBorder: true, - withCloseButton: false, - }); - }); + }), + ) + .catch( + errorNotificationCurried({ + title: 'Settings save error', + message: + 'It seems something went wrong on our end. Please try again later', + }), + ); }; return ( diff --git a/src/features/auth/pages/login/LoginForm.tsx b/src/features/auth/pages/login/LoginForm.tsx index 6e16a78..dd93213 100644 --- a/src/features/auth/pages/login/LoginForm.tsx +++ b/src/features/auth/pages/login/LoginForm.tsx @@ -1,13 +1,12 @@ import { zodResolver } from '@hookform/resolvers/zod'; import { Paper, TextInput, PasswordInput, Button } from '@mantine/core'; -import { notifications } from '@mantine/notifications'; -import { FirebaseError } from 'firebase/app'; import { useForm, Controller, SubmitHandler } from 'react-hook-form'; import { useNavigate } from 'react-router-dom'; import { useLoginMutation } from 'features/auth/authApi'; import { LoginSchema } from 'features/auth/pages/validation'; import { routes } from 'router/routes'; +import { errorNotification } from 'utility/notificationUtils'; interface ILoginForm { email: string; @@ -31,36 +30,22 @@ export const LoginForm = () => { resolver: zodResolver(LoginSchema), }); - const onSubmit: SubmitHandler = async ({ email, password }) => { - try { - notifications.clean(); - await login({ email, password }).unwrap(); - navigate(routes.dashboard); - } catch (error) { - handleErrors(error); - } - }; + const onSubmit: SubmitHandler = (props) => { + login(props) + .unwrap() + .then(() => { + navigate(routes.dashboard); + }) + .catch((error) => { + const message = + error.code === 'auth/user-not-found' + ? "User not found. Please check your credentials or sign up if you're a new user." + : error.code === 'auth/wrong-password' + ? 'Incorrect username or password. Please try again.' + : 'It seems something went wrong on our end. Please try again later'; - const handleErrors = (error: unknown) => { - console.error('authService.login error...', error); - let err = - 'It seems something went wrong on our end. Please try again later'; - if (error instanceof FirebaseError) { - if ( - error.code === 'auth/user-not-found' || - error.code === 'auth/wrong-password' - ) { - err = - "User not found. Please check your credentials or sign up if you're new"; - } - } - notifications.show({ - title: 'Oops!', - message: err, - color: 'red', - withBorder: true, - withCloseButton: false, - }); + errorNotification({ title: 'Login Error', message }); + }); }; return ( diff --git a/src/features/auth/pages/register/RegisterForm.tsx b/src/features/auth/pages/register/RegisterForm.tsx index 0e75dd4..8e06501 100644 --- a/src/features/auth/pages/register/RegisterForm.tsx +++ b/src/features/auth/pages/register/RegisterForm.tsx @@ -6,6 +6,10 @@ import { useForm, Controller, SubmitHandler } from 'react-hook-form'; import { useRegisterMutation } from 'features/auth/authApi'; import { RegisterSchema } from 'features/auth/pages/validation'; import { useSetUserDetailsMutation } from 'features/db/dbApi'; +import { + errorNotification, + successNotification, +} from 'utility/notificationUtils'; interface IRegisterForm { name: string; @@ -52,23 +56,16 @@ export const RegisterForm = () => { lastname, username, }).unwrap(); - notifications.show({ - title: 'Congratulations!', + successNotification({ + title: "You're all set!", message: 'Your account is all set up and you will now be redirected to the Login page', - color: 'green', - withBorder: true, - withCloseButton: false, }); } catch (error) { - console.error('authService.register error...', error); - notifications.show({ - title: 'Oops!', + errorNotification({ + title: "We're sorry!", message: 'It seems something went wrong on our end. Please try again later', - color: 'red', - withBorder: true, - withCloseButton: false, }); } }; diff --git a/src/features/auth/pages/resetPassword/ResetPasswordForm.tsx b/src/features/auth/pages/resetPassword/ResetPasswordForm.tsx index 81699d8..910e031 100644 --- a/src/features/auth/pages/resetPassword/ResetPasswordForm.tsx +++ b/src/features/auth/pages/resetPassword/ResetPasswordForm.tsx @@ -1,6 +1,5 @@ import { zodResolver } from '@hookform/resolvers/zod'; import { Paper, Button } from '@mantine/core'; -import { notifications } from '@mantine/notifications'; import { useForm } from 'react-hook-form'; import { FormDevTools } from 'components/FormDevTools'; @@ -9,6 +8,10 @@ import { useResetPasswordMutation } from 'features/auth/authApi'; import { ResetPasswordSchema } from 'features/auth/pages/validation'; import { routes } from 'router/routes'; import { navigate } from 'router/utility/navigate'; +import { + errorNotification, + successNotification, +} from 'utility/notificationUtils'; export type ResetPasswordFormData = { email: string; @@ -28,24 +31,19 @@ export const ResetPasswordForm = () => { resetPassword({ email: data.email }) .unwrap() .then(() => { - notifications.show({ - title: 'Success', + successNotification({ + title: 'Reset Password', message: 'Please check your email to reset your password.', - color: 'green', }); navigate(routes.login); }) .catch((error) => { - let errorMessage = 'An error occurred, please try again.'; - if (error?.code === 'auth/user-not-found') { - errorMessage = - 'No user found with this email address. Please check and try again.'; - } - notifications.show({ - title: 'Error', - message: errorMessage, - color: 'red', - }); + const message = + error?.code === 'auth/user-not-found' + ? 'No user found with this email address. Please check and try again.' + : 'An error occurred, please try again.'; + + errorNotification({ title: 'Reset Password Error', message }); }); }; diff --git a/src/features/layout/components/Header/Header.tsx b/src/features/layout/components/Header/Header.tsx index f52a1c6..d1804c7 100644 --- a/src/features/layout/components/Header/Header.tsx +++ b/src/features/layout/components/Header/Header.tsx @@ -6,7 +6,6 @@ import { Title, useMantineColorScheme, } from '@mantine/core'; -import { notifications } from '@mantine/notifications'; import { skipToken } from '@reduxjs/toolkit/query'; import { IconGlassFullFilled, @@ -23,6 +22,7 @@ import { useAuth } from 'features/auth/hooks/useAuth'; import { useGetUserDetailsQuery } from 'features/db/dbApi'; import { useSidebar } from 'features/layout/hooks/useSidebar'; import { getUserInitials } from 'features/layout/utils/getUserInitials'; +import { errorNotificationCurried } from 'utility/notificationUtils'; export const Header = () => { const [isOpen, toggleSidebar] = useSidebar(); @@ -40,16 +40,13 @@ export const Header = () => { const handleLogout = () => logout() .unwrap() - .catch(() => { - notifications.show({ + .catch( + errorNotificationCurried({ title: 'Logout Error', message: 'An error occurred while logging out. Please try again later.', - color: 'red', - withBorder: true, - withCloseButton: true, - }); - }); + }), + ); return ( diff --git a/src/utility/notificationUtils.tsx b/src/utility/notificationUtils.tsx new file mode 100644 index 0000000..f2e73ad --- /dev/null +++ b/src/utility/notificationUtils.tsx @@ -0,0 +1,53 @@ +import { showNotification } from '@mantine/notifications'; +import { IconCheck, IconX } from '@tabler/icons-react'; + +type NotificationOptions = { + title?: string; + message?: string; +}; + +interface ErrorNotificationProps extends NotificationOptions { + error?: unknown; +} + +const genericErrorMessage = 'An error occurred. Please try again later.'; +const genericSuccessMessage = 'Everything went well.'; + +const parseErrorMessage = (error: unknown) => + error instanceof Error + ? error.message + : typeof error === 'string' + ? error + : genericErrorMessage; + +export const errorNotification = ({ + title = 'Error occurred', + message, + error, +}: ErrorNotificationProps) => { + showNotification({ + title, + message: message ?? parseErrorMessage(error), + icon: , + color: 'red', + }); +}; + +export const errorNotificationCurried = + (props?: NotificationOptions) => (error?: unknown) => { + errorNotification({ ...props, error }); + }; + +export const successNotification = (props?: NotificationOptions) => { + showNotification({ + title: props?.title ?? 'Success', + message: props?.message ?? genericSuccessMessage, + icon: , + color: 'green', + }); +}; + +export const successNotificationCurried = + (props?: NotificationOptions) => () => { + successNotification(props); + };