diff --git a/example.env.local b/example.env.local
index f11ad6a..793dd69 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/change-password/index.jsx b/src/components/dashboard/change-password/index.jsx
index 59e739a..ccbab93 100644
--- a/src/components/dashboard/change-password/index.jsx
+++ b/src/components/dashboard/change-password/index.jsx
@@ -20,7 +20,7 @@ const DashboardChangePasswordComponent = () => {
const {directionApp} = useDirection();
const handleSubmit = (values, {setSubmitting, resetForm}) => {
- Notifications(directionApp, t);
+ Notifications(t);
axios
.post(
CHANGE_PASSWORD,
@@ -35,12 +35,12 @@ const DashboardChangePasswordComponent = () => {
)
.then((response) => {
toast.dismiss(pendingToast); // Dismiss the pending toast notification
- Notifications(directionApp, t, response);
+ Notifications(t, response);
resetForm();
})
.catch((error) => {
toast.dismiss(pendingToast); // Dismiss the pending toast notification
- Notifications(directionApp, t, error.response);
+ Notifications(t, error.response);
})
.finally(() => {
setSubmitting(false); // Set `setSubmitting` to false after the API request completes
diff --git a/src/components/dashboard/edit-profile/index.jsx b/src/components/dashboard/edit-profile/index.jsx
index 5884639..d377123 100644
--- a/src/components/dashboard/edit-profile/index.jsx
+++ b/src/components/dashboard/edit-profile/index.jsx
@@ -20,7 +20,7 @@ const DashboardEditProfile = () => {
const {directionApp} = useDirection();
const editAvatar = async (avatar) => {
- Notifications(directionApp, t);
+ Notifications(t);
try {
const formData = new FormData();
@@ -37,7 +37,7 @@ const DashboardEditProfile = () => {
},
});
} catch (error) {
- Notifications(directionApp, t, error.response);
+ Notifications(t, error.response);
throw error;
}
};
@@ -55,13 +55,13 @@ const DashboardEditProfile = () => {
// });
// })
// .then((response) => {
- // Notifications(directionApp, t, response);
+ // Notifications( t, response);
// getUser((data) => {
// changeUser(data);
// });
// })
// .catch((error) => {
- // Notifications(directionApp, t, error.response);
+ // Notifications( t, error.response);
// })
// .finally(() => {
// setSubmitting(false);
@@ -75,13 +75,13 @@ const DashboardEditProfile = () => {
// },
// })
// .then((response) => {
- // Notifications(directionApp, t, response);
+ // Notifications( t, response);
// getUser((data) => {
// changeUser(data);
// });
// })
// .catch((error) => {
- // Notifications(directionApp, t, error.response);
+ // Notifications( t, error.response);
// })
// .finally(() => {
// setSubmitting(false);
diff --git a/src/components/first/index.jsx b/src/components/first/index.jsx
index 03407ff..cd5b5a5 100644
--- a/src/components/first/index.jsx
+++ b/src/components/first/index.jsx
@@ -2,9 +2,10 @@ 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";
+import process from "next/dist/build/webpack/loaders/resolve-url-loader/lib/postcss";
const FirstComponent = () => {
const t = useTranslations();
@@ -40,6 +41,14 @@ const FirstComponent = () => {
)}
+
+ v{process.env.NEXT_PUBLIC_API_VERSION}
+
);
};
diff --git a/src/components/login-expert/index.jsx b/src/components/login-expert/index.jsx
index c714564..2eaa3bd 100644
--- a/src/components/login-expert/index.jsx
+++ b/src/components/login-expert/index.jsx
@@ -1,5 +1,4 @@
import LinkRouting from "@/core/components/LinkRouting";
-import Notifications from "@/core/components/notifications";
import PasswordField from "@/core/components/PasswordField";
import StyledForm from "@/core/components/StyledForm";
import {GET_USER_TOKEN} from "@/core/data/apiRoutes";
@@ -8,38 +7,39 @@ import FullPageLayout from "@/layouts/FullPageLayout";
import useUser from "@/lib/app/hooks/useUser";
import LoginIcon from "@mui/icons-material/Login";
import {Box, Button, Container, Paper, Stack, TextField, Typography,} from "@mui/material";
-import axios from "axios";
import {Field, Formik} from "formik";
import {useTranslations} from "next-intl";
import {useSearchParams} from "next/navigation";
import * as Yup from "yup";
import useDirection from "@/lib/app/hooks/useDirection";
import SvgLogin from "@/core/components/svgs/SvgLogin";
+import useRequest from "@/lib/app/hooks/useRequest";
const LoginComponent = () => {
const t = useTranslations();
const {directionApp} = useDirection(); // should delete because we don't have direction anymore
const {setToken} = useUser(); // pass token to set token
+ const requestServer = useRequest()
// getting url query
const searchParams = useSearchParams();
const backUrlDecodedPath = searchParams.get("back_url");
//formik properties
- const handleSubmit = async (values, props) => {
- Notifications(directionApp, t);
- await axios
- .post(GET_USER_TOKEN, {
+ const handleSubmit = (values, props) => {
+ requestServer(GET_USER_TOKEN, 'post', {
+ data: {
username: values.username,
password: values.password,
- })
- .then(function (response) {
- setToken(response.data.token);
- })
- .catch(function (error) {
- Notifications(directionApp, t, error.response);
- props.setSubmitting(false);
- });
+ },
+ success: {
+ notification: {show: false}
+ }
+ }).then((response) => {
+ setToken(response.data.token)
+ }).catch(() => {
+ props.setSubmitting(false)
+ })
};
const initialValues = {
username: "",
@@ -120,7 +120,7 @@ const LoginComponent = () => {
fullWidth
size="medium"
endIcon={}
- disabled={props.isSubmitting ? true : false}
+ disabled={props.isSubmitting}
>
{t("LoginPage.button_submit")}
diff --git a/src/core/components/notifications/ErrorNotification.jsx b/src/core/components/notifications/ErrorNotification.jsx
index e503607..4ff1ab0 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(
() => (
<>
@@ -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/UploadFileNotification.jsx b/src/core/components/notifications/UploadFileNotification.jsx
index 51e2761..4d3b462 100644
--- a/src/core/components/notifications/UploadFileNotification.jsx
+++ b/src/core/components/notifications/UploadFileNotification.jsx
@@ -2,7 +2,7 @@ import DangerousIcon from "@mui/icons-material/Dangerous";
import {Box, Typography} from "@mui/material";
import {toast} from "react-toastify";
-const UploadFileNotification = (directionApp, t) => {
+const UploadFileNotification = (t) => {
toast(
({closeToast}) => (
<>
@@ -26,7 +26,6 @@ const UploadFileNotification = (directionApp, t) => {
>
),
{
- position: directionApp === "ltr" ? "top-left" : "top-right",
autoClose: false,
closeOnClick: false,
draggable: false,
diff --git a/src/core/components/notifications/WarningNotification.jsx b/src/core/components/notifications/WarningNotification.jsx
index b8b72a6..8493180 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, Typography} from "@mui/material";
import {toast} from "react-toastify";
-const WarningNotification = (directionApp, t, status) => {
+const WarningNotification = (t, status) => {
toast(
() => (
<>
@@ -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 bf76d37..bc8eb66 100644
--- a/src/core/components/notifications/index.jsx
+++ b/src/core/components/notifications/index.jsx
@@ -3,37 +3,36 @@ import ErrorNotification from "./ErrorNotification";
import WarningNotification from "./WarningNotification";
import SuccessNotification from "./SuccessNotification";
-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("notifications.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..6bee241
--- /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]);
+ })
+ }
+}
\ No newline at end of file
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 14e35a2..672e8a8 100644
--- a/src/core/utils/theme.jsx
+++ b/src/core/utils/theme.jsx
@@ -19,7 +19,7 @@ const theme = {
MuiBackdrop: {
styleOverrides: {
root: {
- backgroundColor: "#02020226", // Replace with your desired backdrop color and opacity
+ backgroundColor: colord(process.env.NEXT_PUBLIC_PRIMARY_MAIN).darken(0.2).alpha(0.7).toHex(),
},
},
},
diff --git a/src/layouts/AppLayout.jsx b/src/layouts/AppLayout.jsx
index c86d1f2..6d9bd96 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 1dcdb1f..10611b8 100644
--- a/src/lib/app/contexts/user.jsx
+++ b/src/lib/app/contexts/user.jsx
@@ -1,6 +1,7 @@
import {GET_USER_ROUTE} 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,16 @@ 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.jsx b/src/lib/app/hooks/useRequest.jsx
new file mode 100644
index 0000000..f7db024
--- /dev/null
+++ b/src/lib/app/hooks/useRequest.jsx
@@ -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