diff --git a/.gitignore b/.gitignore
index 78b977c..1e62a6d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,34 @@
+# Logs
+logs
+*.log
+
+# Runtime data
+pids
+*.pid
+*.seed
+
+# Directory for instrumented libs generated by jscoverage/JSCover
+lib-cov
+
+# Coverage directory used by tools like istanbul
+coverage
+
+# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
+.grunt
+
+# node-waf configuration
+.lock-wscript
+
+# Compiled binary addons (http://nodejs.org/api/addons.html)
+build/Release
+
+# Dependency directory
node_modules
+cypress/videos
+cypress/screenshots
.next
-.env.local
+.env*
+.env
.idea
-package-lock.json
\ No newline at end of file
+package-lock.json
+ecosystem.config.js
\ No newline at end of file
diff --git a/ecosystem.config.js.example b/ecosystem.config.js.example
new file mode 100644
index 0000000..983fa1c
--- /dev/null
+++ b/ecosystem.config.js.example
@@ -0,0 +1,18 @@
+module.exports = {
+ apps: [{
+ name: "loan facilities expert",
+ script: 'node_modules/next/dist/bin/next', // cluster mode run with node only, not npm
+ args: 'start',
+ exec_mode: "cluster", // default fork
+ instances: "max",
+ kill_timeout: 4000,
+ wait_ready: true,
+ autorestart: true,
+ watch: false,
+ max_memory_restart: "1G",
+ log_date_format: "YYYY-MM-DD HH:mm Z",
+ env_prod: {
+ APP_ENV: 'prod' // APP_ENV=prod
+ }
+ }],
+};
\ No newline at end of file
diff --git a/example.env.local b/example.env.local
index 24d5b8e..b9a7e4c 100644
--- a/example.env.local
+++ b/example.env.local
@@ -1,5 +1,5 @@
NEXT_PUBLIC_API_NAME = "Loan Facilities Dashboard"
-NEXT_PUBLIC_API_VERSION = "1.8.6"
+NEXT_PUBLIC_API_VERSION = "1.8.10"
NEXT_PUBLIC_DEFAULT_LANGUAGE = "fa"
NEXT_PUBLIC_DEFAULT_DIRECTION = "rtl"
@@ -8,4 +8,7 @@ NEXT_PUBLIC_SECONDARY_MAIN = "#FF4E00"
NEXT_PUBLIC_BASE_URL = "https://loan.witel.ir"
+#["NEXT_PUBLIC_HAS_WIDGET" , "NEXT_PUBLIC_HAS_NOTIFICATION"]
+NEXT_PUBLIC_HAS_VALUE=["NEXT_PUBLIC_HAS_NOTIFICATION"]
+
NODE_ENV = "development"
\ No newline at end of file
diff --git a/public/locales/fa/app.json b/public/locales/fa/app.json
index f4df724..2a47ea5 100644
--- a/public/locales/fa/app.json
+++ b/public/locales/fa/app.json
@@ -64,7 +64,7 @@
"typography_your_login_is_valid_and_you_do_not_need_to_login_again": "شما دسترسی لازم به این صفحه را دارید نیاز به ورود مجدد نیست.",
"typography_your_access_to_this_page_has_expired_Please_login_again": "دسترسی شما منقضی شده است لطفا دوباره ورود نمایید.",
"typography_redirect_to": "درحال رفتن به صفحه",
- "typography_routing_previuos_page": "صفحه قبل...",
+ "typography_routing_previuos_page": " قبل...",
"typography_routing_dashbaord_page": "داشبورد..."
},
"Permission": {
diff --git a/src/components/dashboard/change-password/change-password-form/index.jsx b/src/components/dashboard/change-password/change-password-form/index.jsx
new file mode 100644
index 0000000..838b16d
--- /dev/null
+++ b/src/components/dashboard/change-password/change-password-form/index.jsx
@@ -0,0 +1,140 @@
+import {Formik} from "formik";
+import {useTranslations} from "next-intl";
+import useRequest from "@/lib/app/hooks/useRequest";
+import {SET_USER_PASSWORD} from "@/core/data/apiRoutes";
+import {Button, Paper, Stack, Typography} from "@mui/material";
+import * as Yup from "yup";
+import StyledForm from "@/core/components/StyledForm";
+import PasswordField from "@/core/components/PasswordField";
+
+const ChangePasswordForm = ({onSubmit}) => {
+ const t = useTranslations();
+ const requestServer = useRequest({auth: true})
+
+ const handleSubmit = (values, {setSubmitting, resetForm}) => {
+ requestServer(SET_USER_PASSWORD, 'post', {
+ data: {
+ current_password: values.current_password,
+ new_password: values.new_password,
+ new_password_confirmation: values.new_password_confirmation,
+ },
+ }).then((response) => {
+ resetForm();
+ }).catch(() => {
+ }).finally(() => {
+ setSubmitting(false);
+ });
+ };
+ const initialValues = {
+ current_password: "",
+ new_password: "",
+ new_password_confirmation: "",
+ };
+ const validationSchema = Yup.object().shape({
+ current_password: Yup.string().required(
+ t("ChangePassword.error_message_current_password")
+ ),
+ new_password: Yup.string()
+ .required(t("ExpertMangement.error_message_new_password"))
+ .matches(
+ /^(?=.*[a-zA-Z])(?=.*\d).{8,}$/,
+ t("ExpertMangement.error_message_new_password_regex")
+ ),
+ new_password_confirmation: Yup.string()
+ .required(t("ChangePassword.error_message_confirm_password"))
+ .test(
+ t("ChangePassword.error_message_password_match"),
+ t("ChangePassword.error_message_password_not_match"),
+ function (value) {
+ return this.parent.new_password === value;
+ }
+ ),
+ });
+
+ return (
+
+ {(props) => (
+ {
+ e.preventDefault();
+ props.handleSubmit();
+ }}
+ >
+
+
+
+ {t("ChangePassword.typography_change_password")}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )}
+
+ );
+};
+
+export default ChangePasswordForm;
\ No newline at end of file
diff --git a/src/components/dashboard/change-password/index.jsx b/src/components/dashboard/change-password/index.jsx
index 07213e6..b6337d4 100644
--- a/src/components/dashboard/change-password/index.jsx
+++ b/src/components/dashboard/change-password/index.jsx
@@ -1,149 +1,14 @@
import CenterLayout from "@/layouts/CenterLayout";
import DashboardLayouts from "@/layouts/dashboardLayouts";
-import {Button, Container, Paper, Stack, Typography,} from "@mui/material";
-import {Formik} from "formik";
-import * as Yup from "yup";
-import {useTranslations} from "next-intl";
-import PasswordField from "@/core/components/PasswordField";
-import StyledForm from "@/core/components/StyledForm";
-import {SET_USER_PASSWORD} from "@/core/data/apiRoutes";
-import SvgChangePassword from "@/core/components/svgs/SvgChangePassword";
-import useRequest from "@/lib/app/hooks/useRequest";
+import {Container} from "@mui/material";
+import ChangePasswordForm from "@/components/dashboard/change-password/change-password-form";
const DashboardChangePasswordComponent = () => {
- const t = useTranslations();
- const requestServer = useRequest({auth: true})
-
- const handleSubmit = (values, {setSubmitting, resetForm}) => {
- requestServer(SET_USER_PASSWORD, 'post', {
- data: {
- current_password: values.current_password,
- new_password: values.new_password,
- new_password_confirmation: values.new_password_confirmation,
- },
- }).then((response) => {
- resetForm();
- }).catch(() => {
- }).finally(() => {
- setSubmitting(false);
- });
- };
- const initialValues = {
- current_password: "",
- new_password: "",
- new_password_confirmation: "",
- };
- const validationSchema = Yup.object().shape({
- current_password: Yup.string().required(
- t("ChangePassword.error_message_current_password")
- ),
- new_password: Yup.string()
- .required(t("ExpertMangement.error_message_new_password"))
- .matches(
- /^(?=.*[a-zA-Z])(?=.*\d).{8,}$/,
- t("ExpertMangement.error_message_new_password_regex")
- ),
- new_password_confirmation: Yup.string()
- .required(t("ChangePassword.error_message_confirm_password"))
- .test(
- t("ChangePassword.error_message_password_match"),
- t("ChangePassword.error_message_password_not_match"),
- function (value) {
- return this.parent.new_password === value;
- }
- ),
- });
-
return (
-
- {(props) => (
- {
- e.preventDefault();
- props.handleSubmit();
- }}
- >
-
-
-
-
- {t("ChangePassword.typography_change_password")}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- )}
-
+
diff --git a/src/core/components/Middleware/RolePermissionComponent.jsx b/src/core/components/Middleware/RolePermissionComponent.jsx
new file mode 100644
index 0000000..a445008
--- /dev/null
+++ b/src/core/components/Middleware/RolePermissionComponent.jsx
@@ -0,0 +1,33 @@
+import { Button, Typography } from "@mui/material";
+import {useTranslations} from "next-intl";
+import {NextLinkComposed} from "@/core/components/LinkRouting";
+import Message from "@/core/components/Messages";
+
+const RolePermissionComponent = () => {
+ const t = useTranslations();
+
+ return (
+
+ {t("Permission.typography_you_dont_have_access")}
+
+ }
+ actions={
+ <>
+
+ >
+ }
+ />
+ );
+};
+
+export default RolePermissionComponent;
diff --git a/src/core/components/Middleware/WithAuthComponent.jsx b/src/core/components/Middleware/WithAuthComponent.jsx
new file mode 100644
index 0000000..9a6ff5d
--- /dev/null
+++ b/src/core/components/Middleware/WithAuthComponent.jsx
@@ -0,0 +1,36 @@
+import { Button, Typography } from "@mui/material";
+import { useRouter } from "next/router";
+import Message from "@/core/components/Messages";
+import { NextLinkComposed } from "@/core/components/LinkRouting";
+import {useTranslations} from "next-intl";
+
+const WithAuthComponent = () => {
+ const router = useRouter();
+ const t = useTranslations();
+
+ return (
+
+ {t("Authorization.typography_your_access_to_this_page_has_expired_Please_login_again")}
+
+ }
+ actions={
+ <>
+
+ >
+ }
+ />
+ );
+};
+
+export default WithAuthComponent;
diff --git a/src/core/components/Middleware/WithoutAuthComponent.jsx b/src/core/components/Middleware/WithoutAuthComponent.jsx
new file mode 100644
index 0000000..59b7cbf
--- /dev/null
+++ b/src/core/components/Middleware/WithoutAuthComponent.jsx
@@ -0,0 +1,25 @@
+import {Stack, Typography} from "@mui/material";
+import Message from "@/core/components/Messages";
+import {useTranslations} from "next-intl";
+const WithoutAuthComponent = ({ backUrlDecodedPath }) => {
+ const t = useTranslations();
+
+ return (
+
+
+ {t("Authorization.typography_your_login_is_valid_and_you_do_not_need_to_login_again")}
+
+
+ {t("Authorization.typography_redirect_to")}{" "}
+ {backUrlDecodedPath
+ ? t("Authorization.typography_routing_previuos_page")
+ : t("Authorization.typography_routing_dashbaord_page")}
+
+
+ }
+ />
+ );
+};
+export default WithoutAuthComponent;
diff --git a/src/lib/app/hooks/useNotification.jsx b/src/lib/app/hooks/useNotification.jsx
index e32f829..8685227 100644
--- a/src/lib/app/hooks/useNotification.jsx
+++ b/src/lib/app/hooks/useNotification.jsx
@@ -1,8 +1,12 @@
import useSWR from 'swr'
import {GET_SIDEBAR_NOTIFICATION} from "@/core/data/apiRoutes";
import useRequest from "@/lib/app/hooks/useRequest";
+const GLOBAL_HAS_VALUE = process.env.NEXT_PUBLIC_HAS_VALUE;
+const hasPermissionsValue = GLOBAL_HAS_VALUE ? JSON.parse(GLOBAL_HAS_VALUE) : [];
+const isNotificationEnabled = hasPermissionsValue.includes("NEXT_PUBLIC_HAS_NOTIFICATION");
const useNotification = () => {
+
const requestServer = useRequest({auth: true, notification: false})
//swr config
@@ -13,7 +17,7 @@ const useNotification = () => {
})
};
- const {data, mutate} = useSWR(GET_SIDEBAR_NOTIFICATION, fetcher)
+ const {data, mutate} = useSWR( isNotificationEnabled ? GET_SIDEBAR_NOTIFICATION : '', fetcher , {keepPreviousData:true})
const notification_count = data
//swr config
diff --git a/src/middlewares/RolePermission.jsx b/src/middlewares/RolePermission.jsx
index 67fc3f1..4b1cc8a 100644
--- a/src/middlewares/RolePermission.jsx
+++ b/src/middlewares/RolePermission.jsx
@@ -1,43 +1,14 @@
-import {NextLinkComposed} from "@/core/components/LinkRouting";
-import Message from "@/core/components/Messages";
import useUser from "@/lib/app/hooks/useUser";
-import {Button, Typography} from "@mui/material";
-import {useTranslations} from "next-intl";
+import RolePermissionComponent from "@/core/components/Middleware/RolePermissionComponent";
const RolePermissionMiddleware = ({children, requiredPermissions}) => {
const {user} = useUser();
- const t = useTranslations();
const hasPermission = requiredPermissions.some((permission) =>
user?.permissions?.includes(permission)
);
- if (!hasPermission) {
- return (
-
- {t("Permission.typography_you_dont_have_access")}
-
- }
- actions={
- <>
-
- >
- }
- />
- );
- }
-
- return <>{children}>;
+ return !hasPermission ? : <>{children}>;
};
export default RolePermissionMiddleware;
diff --git a/src/middlewares/WithAuth.jsx b/src/middlewares/WithAuth.jsx
index b20d8b3..4f55660 100644
--- a/src/middlewares/WithAuth.jsx
+++ b/src/middlewares/WithAuth.jsx
@@ -1,42 +1,10 @@
-import {NextLinkComposed} from "@/core/components/LinkRouting";
-import Message from "@/core/components/Messages";
import useUser from "@/lib/app/hooks/useUser";
-import {Button, Typography} from "@mui/material";
-import {useTranslations} from "next-intl";
-import {useRouter} from "next/router";
+import WithAuthComponent from "@/core/components/Middleware/WithAuthComponent";
const WithAuthMiddleware = ({children}) => {
const {isAuth} = useUser();
- const t = useTranslations();
- const router = useRouter();
- if (!isAuth)
- return (
-
- {t(
- "Authorization.typography_your_access_to_this_page_has_expired_Please_login_again"
- )}
-
- }
- actions={
- <>
-
- >
- }
- />
- );
- return <>{children}>;
+ return isAuth ? <>{children}> : ;
};
export default WithAuthMiddleware;
diff --git a/src/middlewares/WithoutAuth.jsx b/src/middlewares/WithoutAuth.jsx
index 594ffc5..80169c8 100644
--- a/src/middlewares/WithoutAuth.jsx
+++ b/src/middlewares/WithoutAuth.jsx
@@ -1,19 +1,14 @@
-import Message from "@/core/components/Messages";
import useUser from "@/lib/app/hooks/useUser";
-import {Stack, Typography} from "@mui/material";
-import {useTranslations} from "next-intl";
-import {useSearchParams} from "next/navigation";
import {useRouter} from "next/router";
import {useEffect} from "react";
+import WithoutAuthComponent from "@/core/components/Middleware/WithoutAuthComponent";
const WithoutAuthMiddleware = ({children}) => {
+
const {isAuth} = useUser();
- const t = useTranslations();
const router = useRouter();
- // gettin url query
- const searchParams = useSearchParams();
- const backUrlDecodedPath = searchParams.get("back_url");
+ const backUrlDecodedPath = router.query?.back_url;
useEffect(() => {
if (!isAuth) return;
@@ -30,27 +25,11 @@ const WithoutAuthMiddleware = ({children}) => {
};
}, [isAuth]);
- if (isAuth)
- return (
-
-
- {t(
- "Authorization.typography_your_login_is_valid_and_you_do_not_need_to_login_again"
- )}
-
-
- {t("Authorization.typography_redirect_to")}{" "}
- {backUrlDecodedPath
- ? t("Authorization.typography_routing_previuos_page")
- : t("Authorization.typography_routing_dashbaord_page")}
-
-
- }
- />
- );
- return <>{children}>;
+ return isAuth ? (
+
+ ) : (
+ <>{children}>
+ );
};
export default WithoutAuthMiddleware;