diff --git a/example.env.local b/example.env.local index 3dff93b..5c69c4b 100644 --- a/example.env.local +++ b/example.env.local @@ -1,4 +1,5 @@ NEXT_PUBLIC_API_NAME = "Loan Facilities Dashboard" +NEXT_PUBLIC_API_VERSION = "1.0.0" NEXT_PUBLIC_DEFAULT_LANGUAGE = "fa" NEXT_PUBLIC_DEFAULT_DIRECTION = "rtl" diff --git a/src/components/dashboard/loan-request/NavyForm.jsx b/src/components/dashboard/loan-request/NavyForm.jsx index 99e0d57..3a380f1 100644 --- a/src/components/dashboard/loan-request/NavyForm.jsx +++ b/src/components/dashboard/loan-request/NavyForm.jsx @@ -34,7 +34,7 @@ const NavyFormComponent = () => { setProvinceList(formattedData); }) .catch((error) => { - Notifications(directionApp, t, error.response); + Notifications(t, error.response); }); }, []); // end get province list @@ -115,7 +115,7 @@ const NavyFormComponent = () => { // submit const handleSubmit = async (values, props) => { - Notifications(directionApp, t, undefined); + Notifications(t, undefined); const formData = new FormData(); formData.append("name", values.name); formData.append("phone_number", values.phone_number); @@ -138,10 +138,10 @@ const NavyFormComponent = () => { }, }) .then(function (response) { - Notifications(directionApp, t, response); + Notifications(t, response); }) .catch(function (error) { - Notifications(directionApp, t, error.response); + Notifications(t, error.response); props.setSubmitting(false); }); }; diff --git a/src/components/dashboard/loan-request/WelfareServicesForm.jsx b/src/components/dashboard/loan-request/WelfareServicesForm.jsx index a3462e2..782a256 100644 --- a/src/components/dashboard/loan-request/WelfareServicesForm.jsx +++ b/src/components/dashboard/loan-request/WelfareServicesForm.jsx @@ -87,7 +87,7 @@ const WelfareServicesFormComponent = () => { }); const handleSubmit = async (values, props) => { - Notifications(directionApp, t, undefined); + Notifications(t, undefined); const formData = new FormData(); formData.append("name", values.name); formData.append("phone_number", values.phone_number); @@ -109,10 +109,10 @@ const WelfareServicesFormComponent = () => { }, }) .then(function (response) { - Notifications(directionApp, t, response); + Notifications(t, response); }) .catch(function (error) { - Notifications(directionApp, t, error.response); + Notifications(t, error.response); props.setSubmitting(false); }); }; diff --git a/src/components/first/index.jsx b/src/components/first/index.jsx index 1d07f9d..c949fac 100644 --- a/src/components/first/index.jsx +++ b/src/components/first/index.jsx @@ -2,7 +2,7 @@ import {NextLinkComposed} from "@/core/components/LinkRouting"; import CenterLayout from "@/layouts/CenterLayout"; import FullPageLayout from "@/layouts/FullPageLayout"; import useUser from "@/lib/app/hooks/useUser"; -import {Button, Typography} from "@mui/material"; +import {Button, Stack, Typography} from "@mui/material"; import {useTranslations} from "next-intl"; import SvgDashboard from "@/core/components/svgs/SvgDashboard"; @@ -41,6 +41,14 @@ const FirstComponent = () => { )} + + v{process.env.NEXT_PUBLIC_API_VERSION} + ); }; diff --git a/src/components/login/SendToken.jsx b/src/components/login/SendToken.jsx index 846e3dd..4f1c1eb 100644 --- a/src/components/login/SendToken.jsx +++ b/src/components/login/SendToken.jsx @@ -37,18 +37,18 @@ const SendTokenComponent = ({ }); const handleSubmit = async (values, props) => { - Notifications(directionApp, t, undefined); + Notifications(t, undefined); await axios .post(LOGIN, { phone_number: values.phone_number, verification_code: values.verification_code, }) .then(function (response) { - Notifications(directionApp, t, response); + Notifications(t, response); setToken(response.data.token); }) .catch(function (error) { - Notifications(directionApp, t, error.response); + Notifications(t, error.response); props.setSubmitting(false); }); }; diff --git a/src/components/login/SendUserData.jsx b/src/components/login/SendUserData.jsx index 0799294..8f04b4e 100644 --- a/src/components/login/SendUserData.jsx +++ b/src/components/login/SendUserData.jsx @@ -36,19 +36,19 @@ const SendUserDataComponent = ({ }); const handleSubmit = async (values, props) => { - Notifications(directionApp, t, undefined); + Notifications(t, undefined); await axios .post(SEND_OTP_TOKEN, { phone_number: values.phone_number, }) .then(function (response) { - Notifications(directionApp, t, response); + Notifications(t, response); setPhoneNumber(values.phone_number); setOtpToken(true); setTimer(initialTimerValue); }) .catch(function (error) { - Notifications(directionApp, t, error.response); + Notifications(t, error.response); props.setSubmitting(false); }); }; diff --git a/src/components/register/RequestOtp.jsx b/src/components/register/RequestOtp.jsx index 61bbd60..38f2d1f 100644 --- a/src/components/register/RequestOtp.jsx +++ b/src/components/register/RequestOtp.jsx @@ -37,19 +37,19 @@ const RequestOtpComponent = ({ }); const handleSubmit = async (values, props) => { - Notifications(directionApp, t, undefined); + Notifications(t, undefined); await axios .post(SEND_OTP_TOKEN, { phone_number: values.phone_number, }) .then(function (response) { - Notifications(directionApp, t, response); + Notifications(t, response); setPhoneNumber(values.phone_number); setOtpToken(true); setTimer(initialTimerValue); }) .catch(function (error) { - Notifications(directionApp, t, error.response); + Notifications(t, error.response); props.setSubmitting(false); }); }; diff --git a/src/components/register/navy/UserRegister.jsx b/src/components/register/navy/UserRegister.jsx index d236384..a55a9e5 100644 --- a/src/components/register/navy/UserRegister.jsx +++ b/src/components/register/navy/UserRegister.jsx @@ -45,7 +45,7 @@ const UserRegisterComponent = ({ }); const handleSubmit = async (values, props) => { - Notifications(directionApp, t, undefined); + Notifications(t, undefined); console.log(initialValues); await axios .post(REGISTER, { @@ -56,12 +56,12 @@ const UserRegisterComponent = ({ verification_code: values.verification_code, }) .then(function (response) { - Notifications(directionApp, t, response); + Notifications(t, response); setOtpToken(true); setToken(response.data.token); }) .catch(function (error) { - Notifications(directionApp, t, error.response); + Notifications(t, error.response); props.setSubmitting(false); }); }; diff --git a/src/components/register/welfare-services/UserRegister.jsx b/src/components/register/welfare-services/UserRegister.jsx index 18b539a..90055ca 100644 --- a/src/components/register/welfare-services/UserRegister.jsx +++ b/src/components/register/welfare-services/UserRegister.jsx @@ -43,7 +43,7 @@ const UserRegisterComponent = ({ }); const handleSubmit = async (values, props) => { - Notifications(directionApp, t, undefined); + Notifications(t, undefined); console.log(initialValues); await axios .post(REGISTER, { @@ -53,12 +53,12 @@ const UserRegisterComponent = ({ verification_code: values.verification_code, }) .then(function (response) { - Notifications(directionApp, t, response); + Notifications(t, response); setOtpToken(true); setToken(response.data.token); }) .catch(function (error) { - Notifications(directionApp, t, error.response); + Notifications(t, error.response); props.setSubmitting(false); }); }; diff --git a/src/core/components/ResendToken.jsx b/src/core/components/ResendToken.jsx index 1bb900e..dab6161 100644 --- a/src/core/components/ResendToken.jsx +++ b/src/core/components/ResendToken.jsx @@ -29,17 +29,17 @@ const ResendToken = ({initialTimerValue, timer, setTimer, PhoneNumber}) => { const handleResendClick = async () => { // work on it if (timer != 0) return; - Notifications(directionApp, t, undefined); + Notifications(t, undefined); await axios .post(SEND_OTP_TOKEN, { phone_number: PhoneNumber, }) .then(function (response) { - Notifications(directionApp, t, response); + Notifications(t, response); setTimer(initialTimerValue); }) .catch(function (error) { - Notifications(directionApp, t, error.response); + Notifications(t, error.response); }); }; // End Handle Resend Token diff --git a/src/core/components/notifications/ErrorNotification.jsx b/src/core/components/notifications/ErrorNotification.jsx index de83927..4c2e857 100644 --- a/src/core/components/notifications/ErrorNotification.jsx +++ b/src/core/components/notifications/ErrorNotification.jsx @@ -2,7 +2,7 @@ import DangerousIcon from "@mui/icons-material/Dangerous"; import {Box, Typography} from "@mui/material"; import {toast} from "react-toastify"; -const ErrorNotification = (directionApp, t, status, message) => { +const ErrorNotification = (t, status, message) => { toast( ({closeToast}) => ( <> @@ -29,7 +29,6 @@ const ErrorNotification = (directionApp, t, status, message) => { ), { - position: directionApp === "ltr" ? "top-left" : "top-right", autoClose: false, closeOnClick: false, draggable: false, diff --git a/src/core/components/notifications/PendingNotification.jsx b/src/core/components/notifications/PendingNotification.jsx new file mode 100644 index 0000000..f3a0af4 --- /dev/null +++ b/src/core/components/notifications/PendingNotification.jsx @@ -0,0 +1,11 @@ +import {toast} from "react-toastify"; + +const PendingNotification = (t) => { + toast(t("notifications.pending"), { + autoClose: false, + closeOnClick: false, + draggable: false, + }); +}; + +export default PendingNotification; diff --git a/src/core/components/notifications/SuccessNotification.jsx b/src/core/components/notifications/SuccessNotification.jsx index d45e0ae..998deaa 100644 --- a/src/core/components/notifications/SuccessNotification.jsx +++ b/src/core/components/notifications/SuccessNotification.jsx @@ -2,7 +2,7 @@ import BeenhereIcon from "@mui/icons-material/Beenhere"; import {Box, Typography} from "@mui/material"; import {toast} from "react-toastify"; -const SuccessNotification = (directionApp, t, status) => { +const SuccessNotification = (t, status) => { toast( () => ( <> @@ -30,7 +30,6 @@ const SuccessNotification = (directionApp, t, status) => { ), { - position: directionApp === "ltr" ? "top-left" : "top-right", autoClose: 3000, hideProgressBar: true, pauseOnHover: true, diff --git a/src/core/components/notifications/WarningNotification.jsx b/src/core/components/notifications/WarningNotification.jsx index 59ea1ba..5e931f0 100644 --- a/src/core/components/notifications/WarningNotification.jsx +++ b/src/core/components/notifications/WarningNotification.jsx @@ -2,7 +2,7 @@ import ReportIcon from "@mui/icons-material/Report"; import {Box, Divider, Typography} from "@mui/material"; import {toast} from "react-toastify"; -const WarningNotification = (directionApp, t, status) => { +const WarningNotification = (t, status) => { toast( ({closeToast}) => ( <> @@ -30,7 +30,6 @@ const WarningNotification = (directionApp, t, status) => { ), { - position: directionApp === "ltr" ? "top-left" : "top-right", autoClose: false, closeOnClick: false, draggable: false, diff --git a/src/core/components/notifications/index.jsx b/src/core/components/notifications/index.jsx index 1bf664e..9425aef 100644 --- a/src/core/components/notifications/index.jsx +++ b/src/core/components/notifications/index.jsx @@ -3,37 +3,36 @@ import ErrorNotification from "./ErrorNotification"; import SuccessNotification from "./SuccessNotification"; import WarningNotification from "./WarningNotification"; -const Notifications = async (directionApp, t, response) => { +const Notifications = async (t, response) => { const {status, data} = response != undefined ? response : "" toast.dismiss(); switch (status) { case 200: - SuccessNotification(directionApp, t, status); + SuccessNotification(t, status); break; case 400: - ErrorNotification(directionApp, t, status); + ErrorNotification(t, status); break; case 401: - ErrorNotification(directionApp, t, status); + ErrorNotification(t, status); break; case 403: - ErrorNotification(directionApp, t, status); + ErrorNotification(t, status); break; case 422: - ErrorNotification(directionApp, t, status, data.message); + ErrorNotification(t, status, data.message); break; case 500: - WarningNotification(directionApp, t, status); + WarningNotification(t, status); break; case 503: - WarningNotification(directionApp, t, status); + WarningNotification(t, status); break; case 504: - WarningNotification(directionApp, t, status); + WarningNotification(t, status); break; default: toast(t("pending"), { - position: directionApp === "ltr" ? "top-left" : "top-right", autoClose: false, closeOnClick: false, draggable: false, @@ -47,9 +46,9 @@ export default Notifications; /* usage document -** for pending use ( Notifications(directionApp, t, undefined) ) this before your request. -** for success use ( Notifications(directionApp, t, response) ) this inside .then() of your request. -** for Error and Warning use ( Notifications(directionApp, t, error.response) ) this inside .catche() of your request. +** for pending use ( Notifications( t, undefined) ) this before your request. +** for success use ( Notifications( t, response) ) this inside .then() of your request. +** for Error and Warning use ( Notifications( t, error.response) ) this inside .catche() of your request. end usage document */ diff --git a/src/core/utils/errorHandler.js b/src/core/utils/errorHandler.js new file mode 100644 index 0000000..c758851 --- /dev/null +++ b/src/core/utils/errorHandler.js @@ -0,0 +1,62 @@ +import ErrorNotification from "@/core/components/notifications/ErrorNotification"; +import WarningNotification from "@/core/components/notifications/WarningNotification"; +import {toast} from "react-toastify"; + +export const errorSetting = (t, notification) => { + //todo +} + +export const errorRequest = (t, notification) => { + //todo +} + +export const errorResponse = (response, clearToken, t, notification) => { + toast.dismiss(); + if (isServerError(response.status)) { + errorServer(response, t, notification) + } else if (isClientError(response.status)) { + errorClient(response, clearToken, t, notification) + } +} + +const errorServer = (response, t, notification) => { + if (notification) WarningNotification(t, response.status); +} +const errorClient = (response, clearToken, t, notification) => { + switch (response.status) { + case 401: + clearToken() + if (notification) ErrorNotification(t, response.status) + break; + case 422: + if ('type' in response.data) { + errorLogic(response, t, notification) + break; + } + errorValidation(response, t, notification) + break; + case 429: + if (notification) ErrorNotification(t, response.status) + break + default: + if (notification) ErrorNotification(t, response.status) + break + } +} + +const isServerError = status => status >= 500 && status <= 599; +const isClientError = status => status >= 400 && status <= 499; + +const errorLogic = (response, t, notification) => { + if (notification) ErrorNotification(t, response.status) +} +const errorValidation = (response, t, notification) => { + if (notification) { + const errorsMap = Object.keys(response.data.errors) + const errorsArray = response.data.errors + + errorsMap.map((item, index) => { + ErrorNotification(t, response.status, errorsArray[item][0]); + }) + } +} diff --git a/src/core/utils/succesHandler.js b/src/core/utils/succesHandler.js new file mode 100644 index 0000000..b698967 --- /dev/null +++ b/src/core/utils/succesHandler.js @@ -0,0 +1,9 @@ +import SuccessNotification from "@/core/components/notifications/SuccessNotification"; +import {toast} from "react-toastify"; + +export const successRequest = (response, t, options) => { + toast.dismiss(); + if (options.notification && options.success.notification.show) { + SuccessNotification(t, response.status) + } +} \ No newline at end of file diff --git a/src/core/utils/theme.jsx b/src/core/utils/theme.jsx index 605c893..672e8a8 100644 --- a/src/core/utils/theme.jsx +++ b/src/core/utils/theme.jsx @@ -15,6 +15,15 @@ const theme = { dark: colord(process.env.NEXT_PUBLIC_SECONDARY_MAIN).darken(0.1).toHex(), }, }, + components: { + MuiBackdrop: { + styleOverrides: { + root: { + backgroundColor: colord(process.env.NEXT_PUBLIC_PRIMARY_MAIN).darken(0.2).alpha(0.7).toHex(), + }, + }, + }, + }, }; export default theme; diff --git a/src/layouts/AppLayout.jsx b/src/layouts/AppLayout.jsx index d0f26d7..611e707 100644 --- a/src/layouts/AppLayout.jsx +++ b/src/layouts/AppLayout.jsx @@ -96,7 +96,7 @@ function AppLayout({children, isBot}) { color={theme.palette.secondary.dark} options={{showSpinner: false}} /> - {directionApp === "rtl" ? : } + {children} ); diff --git a/src/lib/app/contexts/user.jsx b/src/lib/app/contexts/user.jsx index ccfdb22..827a8af 100644 --- a/src/lib/app/contexts/user.jsx +++ b/src/lib/app/contexts/user.jsx @@ -1,6 +1,7 @@ import {USER_INFO} from "@/core/data/apiRoutes"; import axios from "axios"; import {createContext, useCallback, useEffect, useReducer} from "react"; +import {errorRequest, errorResponse, errorSetting} from "@/core/utils/errorHandler"; const initialUser = { isAuth: false, @@ -75,7 +76,17 @@ export const UserProvider = ({children}) => { }) .then(({data}) => { if (typeof callback === "function") callback(data); - }); + }) + .catch(error => { + if (error.response) { + errorResponse(error.response, clearToken, null, false) + } else if (error.request) { + errorRequest(null, false) + } else { + errorSetting(null, false) + } + }) + }, [state.token] ); diff --git a/src/lib/app/hooks/useRequest.js b/src/lib/app/hooks/useRequest.js new file mode 100644 index 0000000..f7db024 --- /dev/null +++ b/src/lib/app/hooks/useRequest.js @@ -0,0 +1,59 @@ +import axios from "axios"; +import {successRequest} from "@/core/utils/succesHandler"; +import PendingNotification from "@/core/components/notifications/PendingNotification"; +import {useTranslations} from "next-intl"; +import useUser from "@/lib/app/hooks/useUser"; +import {errorRequest, errorResponse, errorSetting} from "@/core/utils/errorHandler"; + +const defaultOptions = { + data: {}, + requestOptions: {}, + notification: true, + pending: true, + success: { + notification: { + show: true, + }, + }, + failed: { + notification: { + show: true, + }, + }, +} +const useRequest = (initOptions) => { + const t = useTranslations() + const {clearToken} = useUser() + let _options = {...defaultOptions, ...initOptions} + + function requestServer(url = '', method = 'get', options) { + _options = {..._options, ...options} + return new Promise((resolve, reject) => { + if (_options.notification && _options.failed.notification.show && _options.pending) PendingNotification(t) + axios({ + url: url, + method: method, + data: _options.data, + ..._options.requestOptions + }) + .then(response => { + successRequest(response, t, _options) + resolve(response) + }) + .catch(error => { + if (error.response) { + errorResponse(error.response, clearToken, t, _options.notification && _options.failed.notification.show) + } else if (error.request) { + errorRequest(t, _options.notification && _options.failed.notification.show) + } else { + errorSetting(t, _options.notification && _options.failed.notification.show) + } + reject(error) + }) + }); + } + + return requestServer +} + +export default useRequest \ No newline at end of file diff --git a/src/middlewares/WithAuth.jsx b/src/middlewares/WithAuth.jsx index d8a6bba..b317492 100644 --- a/src/middlewares/WithAuth.jsx +++ b/src/middlewares/WithAuth.jsx @@ -26,7 +26,7 @@ const WithAuthMiddleware = ({children}) => { variant="contained" component={NextLinkComposed} to={{ - pathname: "/", + pathname: "/login", }} > {t("login")}