diff --git a/public/locales/fa/app.json b/public/locales/fa/app.json
index 8a7b730..e5a6462 100644
--- a/public/locales/fa/app.json
+++ b/public/locales/fa/app.json
@@ -22,7 +22,9 @@
"machinary-office": "ماشین آلات",
"passenger-boss": "رئیس مسافر",
"transportation-assistance": "معاونت حمل و نقل",
- "master": "مدیر کل"
+ "manager": "مدیر کل",
+ "change-password": "تغییر رمز عبور",
+ "edit-profile": "ویرایش پروفایل"
},
"Authorization": {
"typography_your_login_is_valid_and_you_do_not_need_to_login_again": "شما دسترسی لازم به این صفحه را دارید نیاز به ورود مجدد نیست.",
@@ -44,9 +46,50 @@
},
"Dashboard": {
"dashboard_page": "داشبورد",
+<<<<<<< HEAD
"passenger_office_page": "اداره مسافر"
+=======
+ "change_password": "تغییر رمز عبور",
+ "edit_profile": "ویرایش پروفایل"
+>>>>>>> 135f6d1e2f3495abe68e3ca758870023dcd85d76
},
"MuiDatePicker": {
"date_picker_birthday": "تاریخ"
+ },
+ "ChangePassword": {
+ "typography_change_password": "تغییر رمز عبور",
+ "label_current_password": "رمز عبور فعلی",
+ "label_new_password": "رمز عبور جدید",
+ "label_confirm_password": "تکرار رمز عبور جدید",
+ "error_message_required": "اجباری !",
+ "error_message_password_length": "رمز عبور باید حداقل 8 کاراکتر باشد.",
+ "error_message_password_match": "پسورد ها یکسان هستند",
+ "error_message_password_not_match": "پسورد و تکرار رمز عبور باید یکسان باشد"
+ },
+ "SubmitButton": {
+ "button_while_submit": "در حال ثبت",
+ "button_submit": "ثبت"
+ },
+ "UpdateProfile": {
+ "error_invalid_email": "ایمیل نامعتبر است",
+ "typography_edit_profile": "ویرایش پروفایل",
+ "text_field_email": "ایمیل",
+ "text_field_enter_your_password": "رمز عبور خود را وارد کنید",
+ "text_field_username": "نام کاربری",
+ "text_field_name": "نام",
+ "text_field_phone_number": "شماره تماس",
+ "text_field_position": "سمت",
+ "text_field_province_name": "نام استان",
+ "error_message_required": "اجباری !"
+ },
+ "notifications": {
+ "code": "کد",
+ "error": "خطا",
+ "warning": "خطر",
+ "success": "موفق",
+ "error_static_text": "عملیات شما با خطا مواجه شد",
+ "warning_static_text": "خطا سرور",
+ "success_static_text": "عملیات شما با موفقیت انجام شد",
+ "pending": "در حال انجام..."
}
}
diff --git a/src/components/dashboard/change-password/index.jsx b/src/components/dashboard/change-password/index.jsx
new file mode 100644
index 0000000..c2b5fd4
--- /dev/null
+++ b/src/components/dashboard/change-password/index.jsx
@@ -0,0 +1,184 @@
+import CenterLayout from "@/layouts/CenterLayout";
+import DashboardLayouts from "@/layouts/dashboardLayouts";
+import {
+ Button,
+ Typography,
+ Stack,
+ Paper,
+ Container,
+ Box,
+} from "@mui/material";
+import Image from "next/image";
+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 { CHANGE_PASSWORD } from "@/core/data/apiRoutes";
+import useUser from "@/lib/app/hooks/useUser";
+import axios from "axios";
+import Notifications from "@/core/components/notifications";
+import useDirection from "@/lib/app/hooks/useDirection";
+import { toast } from "react-toastify";
+
+const DashboardChangePasswordComponent = () => {
+ const t = useTranslations();
+ const { token } = useUser();
+ const { directionApp } = useDirection();
+
+ const handleSubmit = (values, { setSubmitting, resetForm }) => {
+ toast.dismiss();
+ const pendingToast = toast(t("notifications.pending"), {
+ position: directionApp === "ltr" ? "top-left" : "top-right",
+ autoClose: false,
+ closeOnClick: false,
+ draggable: false,
+ });
+ axios
+ .post(
+ CHANGE_PASSWORD,
+ {
+ current_password: values.current_password,
+ new_password: values.new_password,
+ new_password_confirmation: values.new_password_confirmation,
+ },
+ {
+ headers: { authorization: `Bearer ${token}` },
+ }
+ )
+ .then((response) => {
+ toast.dismiss(pendingToast); // Dismiss the pending toast notification
+ Notifications(directionApp, response, t);
+ resetForm();
+ })
+ .catch((error) => {
+ toast.dismiss(pendingToast); // Dismiss the pending toast notification
+ Notifications(directionApp, error.response, t);
+ })
+ .finally(() => {
+ setSubmitting(false); // Set `setSubmitting` to false after the API request completes
+ });
+ };
+ const initialValues = {
+ current_password: "",
+ new_password: "",
+ new_password_confirmation: "",
+ };
+ const validationSchema = Yup.object().shape({
+ current_password: Yup.string().required(
+ t("ChangePassword.error_message_required")
+ ),
+ new_password: Yup.string()
+ .min(8, t("ChangePassword.error_message_password_length"))
+ .required(t("ChangePassword.error_message_required")),
+ new_password_confirmation: Yup.string()
+ .required(t("ChangePassword.error_message_required"))
+ .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 DashboardChangePasswordComponent;
diff --git a/src/components/dashboard/edit-profile/index.jsx b/src/components/dashboard/edit-profile/index.jsx
new file mode 100644
index 0000000..fa3e2e1
--- /dev/null
+++ b/src/components/dashboard/edit-profile/index.jsx
@@ -0,0 +1,299 @@
+import StyledForm from "@/core/components/StyledForm";
+import CenterLayout from "@/layouts/CenterLayout";
+import DashboardLayouts from "@/layouts/dashboardLayouts";
+import useUser from "@/lib/app/hooks/useUser";
+import {
+ Box,
+ Button,
+ Container,
+ Grid,
+ Paper,
+ Stack,
+ TextField,
+ Typography,
+} from "@mui/material";
+import * as Yup from "yup";
+import { Field, Formik } from "formik";
+import { useTranslations } from "next-intl";
+import AvatarUpload from "@/core/components/AvatarUpload";
+import axios from "axios";
+import { UPDATE_PROFILE, UPDATE_AVATAR } from "@/core/data/apiRoutes";
+import useDirection from "@/lib/app/hooks/useDirection";
+import Notifications from "@/core/components/notifications";
+import ImageResizer from "@/core/components/ImageConvertor";
+
+const DashboardEditProfile = () => {
+ const t = useTranslations();
+ const { user, token, getUser, changeUser } = useUser();
+ console.log(user);
+ const { directionApp } = useDirection();
+
+ const editAvatar = async (avatar) => {
+ try {
+ const formData = new FormData();
+
+ if (avatar != null) {
+ var resizedAvatar;
+ resizedAvatar = await ImageResizer(avatar);
+ formData.append("avatar", resizedAvatar);
+ }
+
+ await axios.post(UPDATE_AVATAR, formData, {
+ headers: {
+ authorization: `Bearer ${token}`,
+ "Content-Type": "multipart/form-data",
+ },
+ });
+ } catch (error) {
+ Notifications(directionApp, error.response, t);
+ throw error;
+ }
+ };
+ const handleSubmit = (values, { setSubmitting }) => {
+ // const formData = new FormData();
+ // formData.append("email", values.expert_email);
+ // if (values.change_avatar !== false) {
+ // editAvatar(values.expert_avatar)
+ // .then(() => {
+ // return axios.post(UPDATE_PROFILE, formData, {
+ // headers: {
+ // authorization: `Bearer ${token}`,
+ // "Content-Type": "multipart/form-data",
+ // },
+ // });
+ // })
+ // .then((response) => {
+ // Notifications(directionApp, response, t);
+ // getUser((data) => {
+ // changeUser(data);
+ // });
+ // })
+ // .catch((error) => {
+ // Notifications(directionApp, error.response, t);
+ // })
+ // .finally(() => {
+ // setSubmitting(false);
+ // });
+ // } else {
+ // axios
+ // .post(UPDATE_PROFILE, formData, {
+ // headers: {
+ // authorization: `Bearer ${token}`,
+ // "Content-Type": "multipart/form-data",
+ // },
+ // })
+ // .then((response) => {
+ // Notifications(directionApp, response, t);
+ // getUser((data) => {
+ // changeUser(data);
+ // });
+ // })
+ // .catch((error) => {
+ // Notifications(directionApp, error.response, t);
+ // })
+ // .finally(() => {
+ // setSubmitting(false);
+ // });
+ // }
+ };
+ const initialValues = {
+ expert_avatar: null,
+ username: user.username,
+ name: user.name,
+ email: user.email,
+ province_name: user.province_name,
+ position: user.position,
+ };
+ const validationSchema = Yup.object().shape({
+ // username: Yup.string().required(t("UpdateProfile.error_message_required")),
+ // name: Yup.string().required(t("UpdateProfile.error_message_required")),
+ // email: Yup.string()
+ // .email(t("UpdateProfile.error_invalid_email"))
+ // .required(t("UpdateProfile.error_message_required")),
+ // province_name: Yup.string().required(
+ // t("UpdateProfile.error_message_required")
+ // ),
+ // position: Yup.string().required(t("UpdateProfile.error_message_required")),
+ });
+
+ return (
+
+
+
+
+ {(props) => (
+ {
+ e.preventDefault();
+ props.handleSubmit();
+ }}
+ >
+
+
+
+ {t("UpdateProfile.typography_edit_profile")}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {/* */}
+
+
+
+ )}
+
+
+
+
+ );
+};
+
+export default DashboardEditProfile;
diff --git a/src/components/login-expert/index.jsx b/src/components/login-expert/index.jsx
index 65b1e1c..bcc3b1a 100644
--- a/src/components/login-expert/index.jsx
+++ b/src/components/login-expert/index.jsx
@@ -2,7 +2,7 @@ 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";
+import { GET_USER_TOKEN } from "@/core/data/apiRoutes";
import CenterLayout from "@/layouts/CenterLayout";
import FullPageLayout from "@/layouts/FullPageLayout";
import useDirection from "@/lib/app/hooks/useDirection";
@@ -17,7 +17,7 @@ import {
TextField,
Typography,
} from "@mui/material";
-// import axios from "axios";
+import axios from "axios";
import { Field, Formik } from "formik";
import { useTranslations } from "next-intl";
import Image from "next/image";
@@ -33,21 +33,23 @@ const LoginComponent = () => {
const searchParams = useSearchParams();
const backUrlDecodedPath = searchParams.get("back_url");
- // formik properties
- // const handleSubmit = async (values, props) => {
- // await axios
- // .post(GET_USER_TOKEN, {
- // username: values.username,
- // password: values.password,
- // })
- // .then(function (response) {
- // setToken(response.data.token);
- // })
- // .catch(function (error) {
- // Notifications(directionApp, error.response, t);
- // props.setSubmitting(false);
- // });
- // };
+ //formik properties
+ const handleSubmit = async (values, props) => {
+ await axios
+ .post(GET_USER_TOKEN, {
+ username: values.username,
+ password: values.password,
+ })
+ .then(function (response) {
+ console.log(response);
+ setToken(response.data.token);
+ })
+ .catch(function (error) {
+ // Notifications(directionApp, error.response, t);
+ console.log(error);
+ props.setSubmitting(false);
+ });
+ };
const initialValues = {
username: "",
password: "",
@@ -64,7 +66,7 @@ const LoginComponent = () => {
{(props) => (
diff --git a/src/core/components/AvatarUpload.jsx b/src/core/components/AvatarUpload.jsx
new file mode 100644
index 0000000..e1ade5c
--- /dev/null
+++ b/src/core/components/AvatarUpload.jsx
@@ -0,0 +1,128 @@
+import { Avatar, Box, TextField } from "@mui/material";
+import { useState } from "react";
+import DeleteIcon from "@mui/icons-material/Delete";
+import AddIcon from "@mui/icons-material/Add";
+
+const AvatarUpload = ({ user, setFieldValue, valueAvatar, changeFlag }) => {
+ const [selectedImage, setSelectedImage] = useState(user.expert_avatar);
+ const [isHovered, setIsHovered] = useState(false);
+
+ const handleImageChange = (event) => {
+ const newImage = event.target?.files?.[0];
+ if (newImage) {
+ setSelectedImage(URL.createObjectURL(newImage));
+ setFieldValue(valueAvatar, newImage);
+ setFieldValue(changeFlag, true);
+ } else {
+ setSelectedImage("");
+ setFieldValue(valueAvatar, null);
+ }
+ };
+
+ const handleDeleteImage = () => {
+ setSelectedImage("");
+ setFieldValue(valueAvatar, null);
+ setFieldValue(changeFlag, true);
+ };
+
+ const handleMouseEnter = () => {
+ setIsHovered(true);
+ };
+
+ const handleMouseLeave = () => {
+ setIsHovered(false);
+ };
+
+ return (
+
+
+
+
+ {isHovered && (
+
+ {selectedImage ? (
+
+ ) : (
+
+ )}
+
+ )}
+
+
+
+
+ );
+};
+
+export default AvatarUpload;
diff --git a/src/core/data/apiRoutes.js b/src/core/data/apiRoutes.js
index e69de29..1b299cc 100644
--- a/src/core/data/apiRoutes.js
+++ b/src/core/data/apiRoutes.js
@@ -0,0 +1,13 @@
+const BASE_URL = process.env.NEXT_PUBLIC_BASE_URL;
+
+//login
+export const GET_USER_TOKEN = BASE_URL + "/dashboard/login";
+//end login
+
+//change password
+export const CHANGE_PASSWORD = BASE_URL + "/dashboard/profile/change_password";
+//end change password
+
+//user data
+export const GET_USER_ROUTE = BASE_URL + "/dashboard/profile/info";
+//user data
\ No newline at end of file
diff --git a/src/core/data/sidebarMenu.jsx b/src/core/data/sidebarMenu.jsx
index 40f08c8..cf2aacb 100644
--- a/src/core/data/sidebarMenu.jsx
+++ b/src/core/data/sidebarMenu.jsx
@@ -47,12 +47,12 @@ const sidebarMenu = [
role: "transportation-assistance",
},
{
- key: "sidebar.master",
+ key: "sidebar.manager",
type: "page",
- route: "/dashboard/master",
+ route: "/dashboard/manager",
icon: ,
selected: false,
- role: "master",
+ role: "manager",
},
],
];
diff --git a/src/lib/app/contexts/user.jsx b/src/lib/app/contexts/user.jsx
index 49d55af..30875d9 100644
--- a/src/lib/app/contexts/user.jsx
+++ b/src/lib/app/contexts/user.jsx
@@ -51,27 +51,7 @@ export const UserProvider = ({ children }) => {
dispatch({ type: "CHANGE_USER", user });
}, []);
- const changeUserLanguage = useCallback(
- (language) => {
- dispatch({ type: "CHANGE_USER_LANGUAGE", language });
- axios
- .post(
- SET_LANGUAGE,
- { language: language },
- {
- headers: {
- authorization: `Bearer ${state.token}`,
- },
- }
- )
- .then(() => {
- getUser((data) => {
- changeUser(data);
- });
- });
- },
- [state.token]
- );
+ const changeUserLanguage = useCallback(/* use in multi language app */);
const changeAuthState = useCallback((isAuth) => {
dispatch({ type: "CHANGE_AUTH_STATE", isAuth });
diff --git a/src/lib/app/hooks/useUser.jsx b/src/lib/app/hooks/useUser.jsx
index baa0aaa..56bdf7c 100644
--- a/src/lib/app/hooks/useUser.jsx
+++ b/src/lib/app/hooks/useUser.jsx
@@ -10,7 +10,6 @@ const useUser = () => {
getUser,
clearUser,
changeUser,
- changeUserLanguage,
changeAuthState,
changeLanguageState,
clearToken,
@@ -25,7 +24,6 @@ const useUser = () => {
getUser,
clearUser,
changeUser,
- changeUserLanguage,
changeAuthState,
changeLanguageState,
clearToken,
diff --git a/src/middlewares/WithAuth.jsx b/src/middlewares/WithAuth.jsx
index d158bf3..3fa5f43 100644
--- a/src/middlewares/WithAuth.jsx
+++ b/src/middlewares/WithAuth.jsx
@@ -16,7 +16,7 @@ const WithAuthMiddleware = ({ children }) => {
text={
{t(
- "typography_your_access_to_this_page_has_expired_Please_login_again"
+ "Authorization.typography_your_access_to_this_page_has_expired_Please_login_again"
)}
}
@@ -26,7 +26,7 @@ const WithAuthMiddleware = ({ children }) => {
variant="contained"
component={NextLinkComposed}
to={{
- pathname: "/login",
+ pathname: "/login-expert",
query: { back_url: encodeURIComponent(router.asPath) },
}}
>
diff --git a/src/middlewares/WithoutAuth.jsx b/src/middlewares/WithoutAuth.jsx
index 5fb4f93..cb8e571 100644
--- a/src/middlewares/WithoutAuth.jsx
+++ b/src/middlewares/WithoutAuth.jsx
@@ -37,14 +37,14 @@ const WithoutAuthMiddleware = ({ children }) => {
{t(
- "typography_your_login_is_valid_and_you_do_not_need_to_login_again"
+ "Authorization.typography_your_login_is_valid_and_you_do_not_need_to_login_again"
)}
- {t("typography_redirect_to")}{" "}
+ {t("Authorization.typography_redirect_to")}{" "}
{backUrlDecodedPath
- ? t("typography_routing_previuos_page")
- : t("typography_routing_dashbaord_page")}
+ ? t("Authorization.typography_routing_previuos_page")
+ : t("Authorization.typography_routing_dashbaord_page")}
}
diff --git a/src/pages/dashboard/change-password/index.jsx b/src/pages/dashboard/change-password/index.jsx
new file mode 100644
index 0000000..30685f1
--- /dev/null
+++ b/src/pages/dashboard/change-password/index.jsx
@@ -0,0 +1,23 @@
+import DashboardChangePasswordComponent from "@/components/dashboard/change-password";
+import TitlePage from "@/core/components/TitlePage";
+import WithAuthMiddleware from "@/middlewares/WithAuth";
+import { parse } from "next-useragent";
+
+export default function LoanFollowUp() {
+ return (
+
+
+
+
+ );
+}
+
+export async function getServerSideProps({ req, locale }) {
+ const { isBot } = parse(req.headers["user-agent"]);
+ return {
+ props: {
+ messages: (await import(`&/locales/${locale}/app.json`)).default,
+ isBot,
+ },
+ };
+}
diff --git a/src/pages/dashboard/edit-profile/index.jsx b/src/pages/dashboard/edit-profile/index.jsx
new file mode 100644
index 0000000..0adabf6
--- /dev/null
+++ b/src/pages/dashboard/edit-profile/index.jsx
@@ -0,0 +1,23 @@
+import DashboardEditProfile from "@/components/dashboard/edit-profile";
+import TitlePage from "@/core/components/TitlePage";
+import WithAuthMiddleware from "@/middlewares/WithAuth";
+import { parse } from "next-useragent";
+
+export default function LoanFollowUp() {
+ return (
+
+
+
+
+ );
+}
+
+export async function getServerSideProps({ req, locale }) {
+ const { isBot } = parse(req.headers["user-agent"]);
+ return {
+ props: {
+ messages: (await import(`&/locales/${locale}/app.json`)).default,
+ isBot,
+ },
+ };
+}
diff --git a/src/pages/dashboard/index.jsx b/src/pages/dashboard/index.jsx
index 5ebc479..c3c3933 100644
--- a/src/pages/dashboard/index.jsx
+++ b/src/pages/dashboard/index.jsx
@@ -1,12 +1,12 @@
import DashboardFirstComponent from "@/components/dashboard/first";
import TitlePage from "@/core/components/TitlePage";
-import WithAuthMiddleware from "@/middlewares/WithoutAuth";
+import WithAuthMiddleware from "@/middlewares/WithAuth";
import { parse } from "next-useragent";
export default function Dashboard() {
return (
-
+
);
diff --git a/src/pages/dashboard/loan-follow-up/index.jsx b/src/pages/dashboard/loan-follow-up/index.jsx
index fc299c4..6bab35f 100644
--- a/src/pages/dashboard/loan-follow-up/index.jsx
+++ b/src/pages/dashboard/loan-follow-up/index.jsx
@@ -6,7 +6,7 @@ import { parse } from "next-useragent";
export default function LoanFollowUp() {
return (
-
+
);
diff --git a/src/pages/dashboard/loan-request/index.jsx b/src/pages/dashboard/loan-request/index.jsx
index b76f172..ad64ce8 100644
--- a/src/pages/dashboard/loan-request/index.jsx
+++ b/src/pages/dashboard/loan-request/index.jsx
@@ -6,7 +6,7 @@ import { parse } from "next-useragent";
export default function LoanRequest() {
return (
-
+
);