From a3a6fab6685249e9d02c16a0843db6de4e7c3cbf Mon Sep 17 00:00:00 2001 From: Wind-Explorer Date: Fri, 28 Jun 2024 09:21:03 +0800 Subject: [PATCH] Toasts --- client/src/components/SignInModule.tsx | 7 +++-- client/src/components/SignUpModule.tsx | 5 ++-- client/src/components/UpdateAccountModule.tsx | 5 ++-- client/src/components/UserProfilePicture.tsx | 3 +- client/src/layouts/default.tsx | 2 ++ client/src/utilities.ts | 30 +++++++++++++++++++ server/routes/users.js | 13 ++++++-- 7 files changed, 55 insertions(+), 10 deletions(-) diff --git a/client/src/components/SignInModule.tsx b/client/src/components/SignInModule.tsx index 9afb306..1ae48a8 100644 --- a/client/src/components/SignInModule.tsx +++ b/client/src/components/SignInModule.tsx @@ -1,11 +1,12 @@ import { Button, Link } from "@nextui-org/react"; import { Formik, Form } from "formik"; import * as Yup from "yup"; -import axios, { AxiosError } from "axios"; +import axios from "axios"; import config from "../config"; import NextUIFormikInput from "./NextUIFormikInput"; import { useNavigate } from "react-router-dom"; import { ChevronLeftIcon } from "../icons"; +import { popErrorToast } from "../utilities"; const validationSchema = Yup.object({ email: Yup.string() @@ -33,14 +34,14 @@ export default function SignInModule() { password: "", }; - const handleSubmit = (values: any) => { + const handleSubmit = (values: any): void => { axios .post(config.serverAddress + "/users/login", values) .then((response) => { navigate("/springboard/" + response.data.accessToken); }) .catch((error) => { - throw ((error as AxiosError).response?.data as any).message; + popErrorToast(error); }); }; diff --git a/client/src/components/SignUpModule.tsx b/client/src/components/SignUpModule.tsx index 7456e9f..1a216cb 100644 --- a/client/src/components/SignUpModule.tsx +++ b/client/src/components/SignUpModule.tsx @@ -1,10 +1,11 @@ import { Button, Checkbox, Link } from "@nextui-org/react"; import { Formik, Form, Field, ErrorMessage } from "formik"; import * as Yup from "yup"; -import axios, { AxiosError } from "axios"; +import axios from "axios"; import config from "../config"; import NextUIFormikInput from "./NextUIFormikInput"; import { useNavigate } from "react-router-dom"; +import { popErrorToast } from "../utilities"; const validationSchema = Yup.object({ firstName: Yup.string() @@ -64,7 +65,7 @@ export default function SignUpModule() { ); console.log("User created successfully:", response.data); } catch (error) { - throw ((error as AxiosError).response?.data as any).message; + popErrorToast(error); } }; diff --git a/client/src/components/UpdateAccountModule.tsx b/client/src/components/UpdateAccountModule.tsx index 2b7e60c..dd4086c 100644 --- a/client/src/components/UpdateAccountModule.tsx +++ b/client/src/components/UpdateAccountModule.tsx @@ -1,4 +1,4 @@ -import axios, { AxiosError } from "axios"; +import axios from "axios"; import * as Yup from "yup"; import config from "../config"; import { useEffect, useState } from "react"; @@ -18,6 +18,7 @@ import { Form, Formik } from "formik"; import NextUIFormikInput from "./NextUIFormikInput"; import { useNavigate } from "react-router-dom"; import UserProfilePicture from "./UserProfilePicture"; +import { popErrorToast } from "../utilities"; export default function UpdateAccountModule({ accessToken, @@ -81,7 +82,7 @@ export default function UpdateAccountModule({ console.log("User updated successfully:", response.data); navigate("/springboard/" + accessToken); } catch (error) { - throw ((error as AxiosError).response?.data as any).message; + popErrorToast(error); } }; diff --git a/client/src/components/UserProfilePicture.tsx b/client/src/components/UserProfilePicture.tsx index 490266f..6de7779 100644 --- a/client/src/components/UserProfilePicture.tsx +++ b/client/src/components/UserProfilePicture.tsx @@ -2,6 +2,7 @@ import axios from "axios"; import React, { useRef, useState } from "react"; import config from "../config"; import { Button, Image } from "@nextui-org/react"; +import { popErrorToast } from "../utilities"; export default function UserProfilePicture({ userId, @@ -36,7 +37,7 @@ export default function UserProfilePicture({ ); return response.data; } catch (error) { - throw error; + popErrorToast(error); } }; diff --git a/client/src/layouts/default.tsx b/client/src/layouts/default.tsx index 67292bf..fb93b64 100644 --- a/client/src/layouts/default.tsx +++ b/client/src/layouts/default.tsx @@ -1,3 +1,4 @@ +import { Toaster } from "react-hot-toast"; import SingaporeAgencyStrip from "../components/SingaporeAgencyStrip"; export default function DefaultLayout({ @@ -9,6 +10,7 @@ export default function DefaultLayout({
{children}
+
); } diff --git a/client/src/utilities.ts b/client/src/utilities.ts index 59d8bcc..42b8451 100644 --- a/client/src/utilities.ts +++ b/client/src/utilities.ts @@ -1,3 +1,6 @@ +import { AxiosError } from "axios"; +import toast from "react-hot-toast"; + export function getTimeOfDay(): number { const currentHour = new Date().getHours(); @@ -9,3 +12,30 @@ export function getTimeOfDay(): number { return 2; // Evening } } + +export const popToast = (message: string, type: number) => { + const words = message.split(" "); + const duration = Math.max(4, words.length * 1); + + toast(message, { + duration: duration * 1000, // Convert to milliseconds + position: "top-center", + + // Custom Icon + icon: type === 0 ? "â„šī¸" : type === 1 ? "✅" : type === 2 ? "❌" : undefined, + + // Aria + ariaProps: { + role: "status", + "aria-live": "polite", + }, + }); +}; + +export const popErrorToast = (error: any) => { + try { + popToast(((error as AxiosError).response?.data as any).message, 2); + } catch { + popToast("An unexpected error occured!\nPlease try again later.", 2); + } +}; diff --git a/server/routes/users.js b/server/routes/users.js index 765dc31..edeb1df 100644 --- a/server/routes/users.js +++ b/server/routes/users.js @@ -270,11 +270,20 @@ router.put( const maxSize = 5 * 1024 * 1024; // 5MB if (!allowedTypes.includes(mimetype)) { - return res.status(400).json({ message: "Invalid file type" }); + return res.status(400).json({ + message: + "Invalid file type\nSupported: jpeg, png, gif\nUploaded: " + + mimetype.substring(6), + }); } if (size > maxSize) { - return res.status(400).json({ message: "File too large" }); + return res.status(400).json({ + message: + "File too large!\nMaximum: 5MB, Uploaded: " + + (size / 1000000).toFixed(2) + + "MB", + }); } // Crop the image to a square