diff --git a/public/locales/fa/app.json b/public/locales/fa/app.json
index 071daf8..1bb88be 100644
--- a/public/locales/fa/app.json
+++ b/public/locales/fa/app.json
@@ -47,6 +47,7 @@
"province-head-expert": "کارشناس ستادی",
"change-password": "تغییر رمز عبور",
"development-assistant": "معاون توسعه",
+ "expert-management": "مدیریت کارشناسان",
"user-management": "مدیریت کاربر",
"edit-profile": "ویرایش پروفایل"
},
@@ -102,8 +103,9 @@
"development_assistant_page": "معاون توسعه",
"inspector_expert_page": "بازدید کارشناس",
"commercial_chief_page": "رئیس اداره بازرگانی",
- "user_management_page": "مدیریت کاربر",
- "edit_profile": "ویرایش پروفایل"
+ "edit_profile": "ویرایش پروفایل",
+ "expert_management": "مدیریت کارشناسان",
+ "user_management_page": "مدیریت کاربر"
},
"MuiDatePicker": {
"date_picker_birthday": "تاریخ"
@@ -350,6 +352,68 @@
"type_id": "نوع کاربر",
"uploadfile_error": "حجم فایل بیشتر از 2 مگابایت می باشد"
},
+ "ExpertMangement": {
+ "id": "کد یکتا",
+ "name": "نام",
+ "username": "نام کاربری",
+ "email": "پست الکترونیک",
+ "phone_number": "شماره همراه",
+ "national_id": "کد ملی",
+ "position": "سمت",
+ "province_name": "استان",
+ "city_name": "شهر",
+ "role_name": "نقش",
+ "updated_at": "آخرین بروزرسانی",
+ "button_while_submiting": "درحال افزودن کارشناس",
+ "button_submit": "افزودن کارشناس",
+ "button_close": "بستن",
+ "button_delete": "حذف",
+ "add": "افزودن کارشناس",
+ "button-cancel": "بستن",
+ "button-confirm": "ثبت",
+ "create": "افزودن کارشناس",
+ "update": "ویرایش کارشناس",
+ "change_password": "تغییر رمز عبور",
+ "text_field_name": "نام",
+ "text_field_username": "نام کاربری",
+ "text_field_email": "پست الکترونیک",
+ "text_field_phone_number": "شماره همراه",
+ "text_field_national_id": "کد ملی",
+ "text_field_password": "رمز عبور",
+ "text_field_position": "سمت",
+ "text_field_province_id": "استان",
+ "text_field_city_id": "شهر",
+ "text_field_role_id": "نقش",
+ "text_field_loading_provinces_list": "درحال دریافت شهر ها",
+ "text_field_error_fetching_provinces": "خطا در دریافت شهر ها",
+ "text_field_loading_roles_list": "درحال دریافت نقش ها",
+ "text_field_error_fetching_roles": "خطا در دریافت نقش ها",
+ "text_field_please_select_province": "ابتدا استان خود را انتخاب کنید",
+ "text_field_loading_cities_list": "درحال دریافت شهر ها",
+ "text_field_error_fetching_cities": "خطا در دریافت شهر ها",
+ "text_field_new_password": "رمز عبور جدید",
+ "error_message_name": "نام خود را وارد کنید",
+ "error_message_username": "نام کاربری خود را وارد کنید",
+ "error_message_email": "پست الکترونیک خود را وارد کنید",
+ "error_message_phone_number": "شماره همراه خود را وارد کنید",
+ "error_message_national_id": "کد ملی خود را وارد کنید",
+ "error_message_password": "رمز عبور خود را وارد کنید",
+ "error_message_password_regex": "رمز عبور باید حداقل 8 کاراکتر و متشکل از عدد، حرف یا سمبل باشد",
+ "error_message_position": "سمت خود را وارد کنید",
+ "error_message_province_id": "استان خود را وارد کنید",
+ "error_message_city_id": "شهر خود را وارد کنید",
+ "error_message_role_id": "نقش خود را وارد کنید",
+ "error_message_new_password": "رمز عبور جدید را وارد کنید",
+ "error_message_new_password_regex": "رمز عبور باید حداقل 8 کاراکتر و متشکل از عدد، حرف یا سمبل باشد",
+ "personal_info": "مشخصات کارشناس",
+ "user_info": "اطلاعات کاربری",
+ "rest_info": "اطلاعات تکمیلی",
+ "delete_tooltip": "حذف",
+ "update_tooltip": "ویرایش کارشناس",
+ "change_password_tooltip": "تغییر رمز عبور",
+ "delete_expert": "حذف کارشناس",
+ "are_you_sure_text": "آیا از انجام این عملیات اطمینان دارید؟"
+ },
"DeleteDialog": {
"delete": "حذف",
"button-cancel": "انصراف",
diff --git a/src/components/dashboard/expert-management/Form/ChangePassword/ChangePasswordContent.jsx b/src/components/dashboard/expert-management/Form/ChangePassword/ChangePasswordContent.jsx
new file mode 100644
index 0000000..da9abab
--- /dev/null
+++ b/src/components/dashboard/expert-management/Form/ChangePassword/ChangePasswordContent.jsx
@@ -0,0 +1,91 @@
+import {useTranslations} from "next-intl";
+import {Button, DialogActions, DialogContent, IconButton, InputAdornment, TextField} from "@mui/material";
+import {CHANGE_PASSWORD_EXPERT_MANAGEMENT} from "@/core/data/apiRoutes";
+import {useFormik} from "formik";
+import * as Yup from "yup";
+import {Visibility, VisibilityOff} from "@mui/icons-material";
+import {useState} from "react";
+import useRequest from "@/lib/app/hooks/useRequest";
+
+
+const ChnagePasswordContent = ({rowId, fetchUrl, mutate, setOpenChangePasswordDialog}) => {
+ const t = useTranslations();
+ const requestServer = useRequest();
+ const [showPassword, setShowPassword] = useState(false);
+
+ const handleClickShowPassword = () => {
+ setShowPassword(!showPassword);
+ };
+
+ const validationSchema = Yup.object().shape({
+ new_password: Yup.string()
+ .required(t("ExpertMangement.error_message_new_password"))
+ .matches(
+ /^(?=.*[0-9])(?=.*[A-Za-z!@#$%^&*])[A-Za-z!@#$%^&*\d]{8,}$/,
+ t("ExpertMangement.error_message_new_password_regex")
+ ),
+ });
+
+ const formik = useFormik({
+ initialValues: {
+ new_password: "",
+ },
+ validationSchema,
+ onSubmit: (values, {setSubmitting}) => {
+ const formData = new FormData();
+ formData.append("new_password", values.new_password);
+
+ requestServer(`${CHANGE_PASSWORD_EXPERT_MANAGEMENT}/${rowId}`, 'post', {
+ auth: true,
+ data: formData
+ })
+ .then((response) => {
+ mutate(fetchUrl)
+ }).catch(() => {
+ }).finally(() => {
+ setSubmitting(false);
+ });
+ },
+ });
+
+ return (
+ <>
+
+
+
+ {showPassword ? : }
+
+
+ ),
+ }}
+ sx={{width: "100%"}}
+ />
+
+
+
+
+
+ >
+ );
+};
+export default ChnagePasswordContent;
\ No newline at end of file
diff --git a/src/components/dashboard/expert-management/Form/ChangePassword/index.jsx b/src/components/dashboard/expert-management/Form/ChangePassword/index.jsx
new file mode 100644
index 0000000..958d1c8
--- /dev/null
+++ b/src/components/dashboard/expert-management/Form/ChangePassword/index.jsx
@@ -0,0 +1,33 @@
+import {useTranslations} from "next-intl";
+import {Dialog, DialogTitle, IconButton, Tooltip} from "@mui/material";
+import KeyIcon from '@mui/icons-material/Key';
+import {useState} from "react";
+import ChangePasswordContent from "./ChangePasswordContent";
+
+
+const ChangePassword = ({rowId, fetchUrl, mutate}) => {
+ const t = useTranslations();
+ const [openChnagePasswordDialog, setOpenChangePasswordDialog] = useState(false);
+
+ return (
+ <>
+
+ {
+ setOpenChangePasswordDialog(true)
+ }}
+ >
+
+
+
+
+ >
+ );
+};
+export default ChangePassword;
\ No newline at end of file
diff --git a/src/components/dashboard/expert-management/Form/CreateForm/CreateContent.jsx b/src/components/dashboard/expert-management/Form/CreateForm/CreateContent.jsx
new file mode 100644
index 0000000..c22783a
--- /dev/null
+++ b/src/components/dashboard/expert-management/Form/CreateForm/CreateContent.jsx
@@ -0,0 +1,385 @@
+import {
+ Box,
+ Button,
+ Chip,
+ DialogActions,
+ DialogContent,
+ Divider,
+ FormControl,
+ FormHelperText,
+ Grid,
+ IconButton,
+ InputAdornment,
+ InputLabel,
+ MenuItem,
+ Select,
+ TextField,
+} from "@mui/material";
+import {useFormik} from "formik";
+import {useTranslations} from "next-intl";
+import * as Yup from "yup";
+import {useState} from "react";
+import {CREATE_EXPERT_MANAGEMENT, GET_CITY_LIST} from "@/core/data/apiRoutes";
+import useRequest from "@/lib/app/hooks/useRequest";
+import useProvince from "@/lib/app/hooks/useProvince";
+import useRole from "@/lib/app/hooks/useRole";
+import {Visibility, VisibilityOff} from "@mui/icons-material";
+
+const CreateContent = ({setOpenCreateDialog, mutate, fetchUrl}) => {
+ const t = useTranslations();
+ const requestServer = useRequest()
+ const [cityList, setCityList] = useState([]);
+ const [cityTextField, setCityTextField] = useState(t("ExpertMangement.text_field_please_select_province"));
+ const {provinceList, isLoadingProvinceList, errorProvinceList} = useProvince();
+ const {roleList, isLoadingRoleList, errorRoleList} = useRole();
+ const [showPassword, setShowPassword] = useState(false);
+
+ const handleClickShowPassword = () => {
+ setShowPassword(!showPassword);
+ };
+
+ function getCities(province_id) {
+ formik.setFieldTouched("city_id", false, false);
+ formik.setFieldValue("city_id", "")
+ setCityTextField(t("ExpertMangement.text_field_loading_cities_list"))
+ requestServer(`${GET_CITY_LIST}?province_id=${province_id}`, "get", {auth: true, notification: false})
+ .then(({data}) => {
+ const result = data.data;
+ const formattedData = result.map((city, index) => ({
+ id: index,
+ name: city.name,
+ value: city.id,
+ }));
+ setCityList(formattedData);
+ setCityTextField(t("ExpertMangement.text_field_city_id"))
+ }).catch(() => {
+ setCityTextField(t("ExpertMangement.text_field_error_fetching_cities"))
+ });
+ }
+
+
+ const validationSchema = Yup.object().shape({
+ name: Yup.string().required(t("ExpertMangement.error_message_name")),
+ username: Yup.string().required(t("ExpertMangement.error_message_username")),
+ email: Yup.string().required(t("ExpertMangement.error_message_email")),
+ phone_number: Yup.string().required(t("ExpertMangement.error_message_phone_number")),
+ national_id: Yup.string().required(t("ExpertMangement.error_message_national_id")),
+ password: Yup.string()
+ .required(t("ExpertMangement.error_message_password"))
+ .matches(
+ /^(?=.*[0-9])(?=.*[A-Za-z!@#$%^&*])[A-Za-z!@#$%^&*\d]{8,}$/,
+ t("ExpertMangement.error_message_password_regex")
+ ),
+ position: Yup.string().required(t("ExpertMangement.error_message_position")),
+ province_id: Yup.string().required(t("ExpertMangement.error_message_province_id")),
+ city_id: Yup.string().required(t("ExpertMangement.error_message_city_id")),
+ role_id: Yup.string().required(t("ExpertMangement.error_message_role_id"))
+ });
+
+ const formik = useFormik({
+ initialValues: {
+ name: "",
+ username: "",
+ email: "",
+ phone_number: "",
+ national_id: "",
+ password: "",
+ position: "",
+ province_id: "",
+ city_id: "",
+ role_id: ""
+ },
+ validationSchema,
+ onSubmit: (values, {setSubmitting}) => {
+ const formData = new FormData();
+ formData.append("name", values.name);
+ formData.append("national_id", values.national_id);
+ formData.append("phone_number", values.phone_number);
+ formData.append("email", values.email);
+ formData.append("username", values.username);
+ formData.append("password", values.password);
+ formData.append("province_id", values.province_id);
+ formData.append("city_id", values.city_id);
+ formData.append("position", values.position);
+ formData.append("role_id", values.role_id);
+
+ requestServer(`${CREATE_EXPERT_MANAGEMENT}`, 'post', {auth: true, data: formData})
+ .then((response) => {
+ mutate(fetchUrl)
+ setOpenCreateDialog(false)
+ }).catch(() => {
+ }).finally(() => {
+ setSubmitting(false);
+ });
+ },
+ });
+
+ return (
+ <>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {showPassword ? : }
+
+
+ ),
+ }}
+ sx={{width: "100%"}}
+ />
+
+
+
+
+
+
+
+
+
+
+ {t("ExpertMangement.text_field_province_id")}
+
+
+ {formik.touched.province_id && formik.errors.province_id ? formik.errors.province_id : ""}
+
+
+
+
+
+ {cityTextField}
+
+
+ {formik.touched.city_id && formik.errors.city_id ? formik.errors.city_id : ""}
+
+
+
+
+
+
+
+
+
+
+ {t("ExpertMangement.text_field_role_id")}
+
+
+ {formik.touched.role_id && formik.errors.role_id ? formik.errors.role_id : ""}
+
+
+
+
+
+
+
+
+
+ >
+ );
+};
+
+export default CreateContent;
diff --git a/src/components/dashboard/expert-management/Form/CreateForm/index.jsx b/src/components/dashboard/expert-management/Form/CreateForm/index.jsx
new file mode 100644
index 0000000..8556b11
--- /dev/null
+++ b/src/components/dashboard/expert-management/Form/CreateForm/index.jsx
@@ -0,0 +1,39 @@
+import {Button, Dialog, DialogTitle, Stack, Tooltip,} from "@mui/material";
+import {useTranslations} from "next-intl";
+import DataSaverOnIcon from "@mui/icons-material/DataSaverOn";
+import {useState} from "react";
+import CreateContent from "./CreateContent";
+
+const Create = ({mutate, fetchUrl}) => {
+ const t = useTranslations();
+ const [openCreateDialog, setOpenCreateDialog] = useState(false);
+
+ return (
+ <>
+
+
+
+
+
+
+ >
+ );
+};
+
+export default Create;
diff --git a/src/components/dashboard/expert-management/Form/DeleteForm.jsx b/src/components/dashboard/expert-management/Form/DeleteForm.jsx
new file mode 100644
index 0000000..87a097c
--- /dev/null
+++ b/src/components/dashboard/expert-management/Form/DeleteForm.jsx
@@ -0,0 +1,73 @@
+import {
+ Button,
+ Dialog,
+ DialogActions,
+ DialogContent,
+ DialogContentText,
+ DialogTitle,
+ IconButton,
+ Tooltip
+} from "@mui/material";
+import DeleteIcon from '@mui/icons-material/Delete';
+import {useState} from "react";
+import {DELETE_EXPERT_MANAGEMENT} from "@/core/data/apiRoutes";
+import useRequest from "@/lib/app/hooks/useRequest";
+import {useTranslations} from "next-intl";
+
+const Delete = ({rowId, fetchUrl, mutate}) => {
+ const t = useTranslations();
+ const requestServer = useRequest({auth: true});
+ const [openDeleteDialog, setOpenDeleteDialog] = useState(false);
+ const [isSubmitting, setIsSubmitting] = useState(false);
+
+ const handleDelete = () => {
+ setIsSubmitting(true)
+ requestServer(`${DELETE_EXPERT_MANAGEMENT}/${rowId}`, 'post')
+ .then((response) => {
+ mutate(fetchUrl)
+ })
+ .catch(() => {
+ })
+ .finally(() => {
+ setIsSubmitting(false)
+ setOpenDeleteDialog(false);
+ });
+ };
+
+ return (
+ <>
+
+ {
+ setOpenDeleteDialog(true);
+ }}
+ >
+
+
+
+
+ >
+ );
+};
+export default Delete;
\ No newline at end of file
diff --git a/src/components/dashboard/expert-management/Form/UpdateForm/UpdateContent.jsx b/src/components/dashboard/expert-management/Form/UpdateForm/UpdateContent.jsx
new file mode 100644
index 0000000..a18dd01
--- /dev/null
+++ b/src/components/dashboard/expert-management/Form/UpdateForm/UpdateContent.jsx
@@ -0,0 +1,342 @@
+import {useTranslations} from "next-intl";
+import {
+ Box,
+ Button,
+ Chip,
+ DialogActions,
+ DialogContent,
+ Divider,
+ FormControl,
+ FormHelperText,
+ Grid,
+ InputLabel,
+ MenuItem,
+ Select,
+ TextField
+} from "@mui/material";
+import {useEffect, useState} from "react";
+import {useFormik} from "formik";
+import {GET_CITY_LIST, UPDATE_EXPERT_MANAGEMENT} from "@/core/data/apiRoutes";
+import * as Yup from "yup";
+import useRequest from "@/lib/app/hooks/useRequest";
+import useProvince from "@/lib/app/hooks/useProvince";
+import useRole from "@/lib/app/hooks/useRole";
+
+const UpdateContent = ({row, fetchUrl, mutate, setOpenUpdateDialog}) => {
+ const t = useTranslations();
+ const requestServer = useRequest();
+ const [cityList, setCityList] = useState([]);
+ const [citiesExist, setCitiesExist] = useState(false);
+ const [cityTextField, setCityTextField] = useState(t("ExpertMangement.text_field_please_select_province"));
+ const {provinceList, isLoadingProvinceList, errorProvinceList} = useProvince();
+ const {roleList, isLoadingRoleList, errorRoleList} = useRole();
+
+ function getCities(province_id) {
+ formik.setFieldTouched("city_id", false, false);
+ citiesExist && formik.setFieldValue("city_id", "")
+ setCityTextField(t("ExpertMangement.text_field_loading_cities_list"))
+ requestServer(`${GET_CITY_LIST}?province_id=${province_id}`, "get", {auth: true, notification: false})
+ .then(({data}) => {
+ const result = data.data;
+ const formattedData = result.map((city, index) => ({
+ id: index,
+ name: city.name,
+ value: city.id,
+ }));
+ setCitiesExist(true);
+ setCityList(formattedData);
+ setCityTextField(t("ExpertMangement.text_field_city_id"))
+ }).catch(() => {
+ setCityTextField(t("ExpertMangement.text_field_error_fetching_cities"))
+ });
+ }
+
+ const validationSchema = Yup.object().shape({
+ name: Yup.string().required(t("ExpertMangement.error_message_name")),
+ username: Yup.string().required(t("ExpertMangement.error_message_username")),
+ email: Yup.string().required(t("ExpertMangement.error_message_email")),
+ phone_number: Yup.string().required(t("ExpertMangement.error_message_phone_number")),
+ national_id: Yup.string().required(t("ExpertMangement.error_message_national_id")),
+ position: Yup.string().required(t("ExpertMangement.error_message_position")),
+ province_id: Yup.string().required(t("ExpertMangement.error_message_province_id")),
+ city_id: Yup.string().required(t("ExpertMangement.error_message_city_id")),
+ role_id: Yup.string().required(t("ExpertMangement.error_message_role_id"))
+ });
+ const formik = useFormik({
+ initialValues: {
+ name: row.original.name,
+ username: row.original.username,
+ email: row.original.email,
+ phone_number: row.original.phone_number,
+ national_id: row.original.national_id,
+ position: row.original.position,
+ province_id: row.original.province_id,
+ city_id: row.original.city_id,
+ role_id: row.original.roles[0].id
+ },
+ validationSchema,
+ onSubmit: (values, {setSubmitting}) => {
+ const formData = new FormData();
+ formData.append("name", values.name);
+ formData.append("national_id", values.national_id);
+ formData.append("phone_number", values.phone_number);
+ formData.append("email", values.email);
+ formData.append("username", values.username);
+ formData.append("province_id", values.province_id);
+ formData.append("city_id", values.city_id);
+ formData.append("position", values.position);
+ formData.append("role_id", values.role_id);
+
+ requestServer(`${UPDATE_EXPERT_MANAGEMENT}/${row.original.id}`, 'post', {auth: true, data: formData})
+ .then((response) => {
+ mutate(fetchUrl)
+ }).catch(() => {
+ }).finally(() => {
+ setSubmitting(false);
+ });
+ },
+ });
+
+ useEffect(() => {
+ getCities(formik.values.province_id);
+ }, [formik.values.province_id]);
+
+ return (
+ <>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {t("ExpertMangement.text_field_province_id")}
+
+
+ {formik.touched.province_id && formik.errors.province_id ? formik.errors.province_id : ""}
+
+
+
+
+
+ {cityTextField}
+
+
+ {formik.touched.city_id && formik.errors.city_id ? formik.errors.city_id : ""}
+
+
+
+
+
+
+
+
+
+
+ {t("ExpertMangement.text_field_role_id")}
+
+
+ {formik.touched.role_id && formik.errors.role_id ? formik.errors.role_id : ""}
+
+
+
+
+
+
+
+
+
+ >
+ );
+};
+export default UpdateContent;
\ No newline at end of file
diff --git a/src/components/dashboard/expert-management/Form/UpdateForm/index.jsx b/src/components/dashboard/expert-management/Form/UpdateForm/index.jsx
new file mode 100644
index 0000000..ff20452
--- /dev/null
+++ b/src/components/dashboard/expert-management/Form/UpdateForm/index.jsx
@@ -0,0 +1,32 @@
+import {useTranslations} from "next-intl";
+import {Dialog, DialogTitle, IconButton, Tooltip} from "@mui/material";
+import EditIcon from '@mui/icons-material/Edit';
+import {useState} from "react";
+import UpdateContent from "./UpdateContent";
+
+
+const Update = ({row, fetchUrl, mutate}) => {
+ const t = useTranslations();
+ const [openUpdateDialog, setOpenUpdateDialog] = useState(false);
+
+ return (
+ <>
+
+ {
+ setOpenUpdateDialog(true)
+ }}
+ >
+
+
+
+
+ >
+ );
+};
+export default Update;
\ No newline at end of file
diff --git a/src/components/dashboard/expert-management/TableRowActions.jsx b/src/components/dashboard/expert-management/TableRowActions.jsx
new file mode 100644
index 0000000..b6976d3
--- /dev/null
+++ b/src/components/dashboard/expert-management/TableRowActions.jsx
@@ -0,0 +1,29 @@
+import {Box} from "@mui/material";
+import Update from "./Form/UpdateForm"
+import Delete from "./Form/DeleteForm";
+import ChangePassword from "./Form/ChangePassword";
+
+const TableRowActions = ({row, mutate, fetchUrl}) => {
+ return (
+
+
+
+
+
+
+ );
+};
+
+export default TableRowActions;
diff --git a/src/components/dashboard/expert-management/TableToolbar.jsx b/src/components/dashboard/expert-management/TableToolbar.jsx
new file mode 100644
index 0000000..de2a387
--- /dev/null
+++ b/src/components/dashboard/expert-management/TableToolbar.jsx
@@ -0,0 +1,13 @@
+import {useTranslations} from "next-intl";
+import Create from "./Form/CreateForm";
+
+function TableToolbar({mutate, fetchUrl}) {
+ const t = useTranslations();
+ return (
+ <>
+
+ >
+ );
+}
+
+export default TableToolbar;
diff --git a/src/components/dashboard/expert-management/index.jsx b/src/components/dashboard/expert-management/index.jsx
new file mode 100644
index 0000000..34e6785
--- /dev/null
+++ b/src/components/dashboard/expert-management/index.jsx
@@ -0,0 +1,191 @@
+import DashboardLayouts from "@/layouts/dashboardLayouts";
+import {Box, Typography} from "@mui/material";
+import {useMemo} from "react";
+import {GET_EXPERT_MANAGEMENT} from "@/core/data/apiRoutes";
+import {useTranslations} from "next-intl";
+import TableRowActions from "./TableRowActions";
+import DataTable from "@/core/components/DataTable";
+import TableToolbar from "@/components/dashboard/expert-management/TableToolbar";
+import moment from "jalali-moment";
+import MuiDatePicker from "@/core/components/MuiDatePicker";
+
+function DashboardExpertManagementComponent() {
+ const t = useTranslations();
+ const columns = useMemo(
+ () => [
+ {
+ accessorFn: (row) => row.id,
+ id: "id",
+ header: t("ExpertMangement.id"),
+ enableColumnFilter: true,
+ datatype: "numeric",
+ filterFn: "equals",
+ columnFilterModeOptions: [
+ "equals",
+ "notEquals",
+ "contains",
+ "lessThan",
+ "greaterThan",
+ "between",
+ ],
+ Cell: ({renderedCellValue}) => (
+ {renderedCellValue}
+ ),
+ },
+ {
+ accessorFn: (row) => row.name,
+ id: "name",
+ header: t("ExpertMangement.name"),
+ enableColumnFilter: true,
+ datatype: "text",
+ filterFn: "contains",
+ columnFilterModeOptions: ["contains", "equals", "notEquals"],
+ Cell: ({renderedCellValue}) => (
+ {renderedCellValue}
+ ),
+ },
+ {
+ accessorFn: (row) => row.username,
+ id: "username",
+ header: t("ExpertMangement.username"),
+ enableColumnFilter: true,
+ datatype: "text",
+ filterFn: "contains",
+ columnFilterModeOptions: ["contains", "equals", "notEquals"],
+ Cell: ({renderedCellValue}) => (
+ {renderedCellValue}
+ ),
+ },
+ {
+ accessorFn: (row) => row.email,
+ id: "email",
+ header: t("ExpertMangement.email"),
+ enableColumnFilter: true,
+ datatype: "text",
+ filterFn: "contains",
+ columnFilterModeOptions: ["contains", "equals", "notEquals"],
+ Cell: ({renderedCellValue}) => (
+ {renderedCellValue}
+ ),
+ },
+ {
+ accessorFn: (row) => row.phone_number,
+ id: "phone_number",
+ header: t("ExpertMangement.phone_number"),
+ enableColumnFilter: true,
+ datatype: "numeric",
+ filterFn: "equals",
+ columnFilterModeOptions: ["contains", "equals", "notEquals"],
+ Cell: ({renderedCellValue}) => (
+ {renderedCellValue}
+ ),
+ },
+ {
+ accessorFn: (row) => row.national_id,
+ id: "national_id",
+ header: t("ExpertMangement.national_id"),
+ enableColumnFilter: true,
+ datatype: "numeric",
+ filterFn: "equals",
+ columnFilterModeOptions: ["contains", "equals", "notEquals"],
+ Cell: ({renderedCellValue}) => (
+ {renderedCellValue}
+ ),
+ },
+ {
+ accessorFn: (row) => row.position,
+ id: "position",
+ header: t("ExpertMangement.position"),
+ enableColumnFilter: true,
+ datatype: "text",
+ filterFn: "contains",
+ columnFilterModeOptions: ["contains", "equals", "notEquals"],
+ Cell: ({renderedCellValue}) => (
+ {renderedCellValue}
+ ),
+ },
+ {
+ accessorFn: (row) => row.province_name,
+ id: "province_name",
+ header: t("ExpertMangement.province_name"),
+ enableColumnFilter: false,
+ datatype: "text",
+ filterFn: "contains",
+ columnFilterModeOptions: ["contains", "equals", "notEquals"],
+ Cell: ({renderedCellValue}) => (
+ {renderedCellValue}
+ ),
+ },
+ {
+ accessorFn: (row) => row.city_name,
+ id: "city_name",
+ header: t("ExpertMangement.city_name"),
+ enableColumnFilter: false,
+ datatype: "text",
+ filterFn: "contains",
+ columnFilterModeOptions: ["contains", "equals", "notEquals"],
+ Cell: ({renderedCellValue}) => (
+ {renderedCellValue}
+ ),
+ },
+ {
+ accessorFn: (row) => row.roles[0].name_fa,
+ id: "roles[0].name_fa",
+ header: t("ExpertMangement.role_name"),
+ enableColumnFilter: false,
+ datatype: "text",
+ filterFn: "contains",
+ columnFilterModeOptions: ["contains", "equals", "notEquals"],
+ Cell: ({renderedCellValue}) => (
+ {renderedCellValue}
+ ),
+ },
+ {
+ accessorFn: (row) =>
+ moment(row.updated_at).locale("fa").format("HH:mm | yyyy/MM/DD"),
+ id: "updated_at",
+ header: t("ExpertMangement.updated_at"),
+ enableColumnFilter: true,
+ datatype: "date",
+ filterFn: "lessThan",
+ columnFilterModeOptions: ["lessThan", "greaterThan"],
+ Cell: ({renderedCellValue}) => {
+ return {renderedCellValue};
+ },
+ Header: ({column}) => <>{column.columnDef.header}>,
+ Filter: ({column}) => {
+ return (
+
+ );
+ },
+ },
+ ],
+ []
+ );
+ return (
+
+
+
+
+
+ );
+}
+
+export default DashboardExpertManagementComponent;
diff --git a/src/components/dashboard/passenger-boss/index.jsx b/src/components/dashboard/passenger-boss/index.jsx
index 392cac3..d046da0 100644
--- a/src/components/dashboard/passenger-boss/index.jsx
+++ b/src/components/dashboard/passenger-boss/index.jsx
@@ -177,6 +177,7 @@ function DashboardPassengerOfficeComponent() {
enableGlobalFilter={false}
enableColumnResizing={false} // if you want true this you shold change renderRowActions props ** see https://www.material-react-table.com/docs/guides/row-actions
enableRowActions={true}
+ renderMainAction={TableToolbar}
TableRowAction={TableRowActions}
/>
diff --git a/src/core/data/apiRoutes.js b/src/core/data/apiRoutes.js
index 19b89a2..94615f8 100644
--- a/src/core/data/apiRoutes.js
+++ b/src/core/data/apiRoutes.js
@@ -151,6 +151,28 @@ export const GET_LOAN_STATE_NAVGAN =
BASE_URL + "/dashboard/loan_states/navgan"
//loan management navgan
+//expert management
+export const GET_EXPERT_MANAGEMENT =
+ BASE_URL + "/dashboard/experts/show"
+export const CREATE_EXPERT_MANAGEMENT =
+ BASE_URL + "/dashboard/experts/store"
+export const UPDATE_EXPERT_MANAGEMENT =
+ BASE_URL + "/dashboard/experts/update"
+export const DELETE_EXPERT_MANAGEMENT =
+ BASE_URL + "/dashboard/experts/delete"
+export const CHANGE_PASSWORD_EXPERT_MANAGEMENT =
+ BASE_URL + "/dashboard/experts/change_password"
+//expert management
+
+// province/city list
+export const GET_PROVINCE_LIST = BASE_URL + "/api/provinces";
+export const GET_CITY_LIST = BASE_URL + "/api/cities";
+// province/city list
+
+// role list
+export const GET_ROLE_LIST = BASE_URL + "/dashboard/roles/list";
+// role list
+
//user management
export const GET_USER_MANAGEMENT =
BASE_URL + "/dashboard/user_management/show"
diff --git a/src/core/data/sidebarMenu.jsx b/src/core/data/sidebarMenu.jsx
index 4957bad..8c7c035 100644
--- a/src/core/data/sidebarMenu.jsx
+++ b/src/core/data/sidebarMenu.jsx
@@ -10,6 +10,7 @@ import GradingIcon from '@mui/icons-material/Grading';
import SupervisedUserCircleIcon from '@mui/icons-material/SupervisedUserCircle';
import Diversity3Icon from '@mui/icons-material/Diversity3';
import PaidIcon from '@mui/icons-material/Paid';
+import ManageAccountsIcon from '@mui/icons-material/ManageAccounts';
import PersonIcon from '@mui/icons-material/Person';
const sidebarMenu = [
@@ -137,6 +138,15 @@ const sidebarMenu = [
selected: false,
permission: "manage_navgan_loan",
},
+ {
+ key: "sidebar.expert-management",
+ name: "expert_management",
+ type: "page",
+ route: "/dashboard/expert-management",
+ icon: ,
+ selected: false,
+ permission: "manage_navgan_loan",
+ },
{
key: "sidebar.user-management",
name: "user_management",
diff --git a/src/lib/app/hooks/useProvince.jsx b/src/lib/app/hooks/useProvince.jsx
new file mode 100644
index 0000000..d0fbda0
--- /dev/null
+++ b/src/lib/app/hooks/useProvince.jsx
@@ -0,0 +1,29 @@
+import {GET_PROVINCE_LIST} from "@/core/data/apiRoutes";
+import useRequest from "@/lib/app/hooks/useRequest";
+import useSWR from "swr";
+
+const useProvince = () => {
+ const requestServer = useRequest({auth: true, notification: false})
+
+ //swr config
+ const fetcher = (...args) => {
+ return requestServer(args, 'get').then(({data}) => {
+ return data;
+ }).catch(() => {
+ })
+ };
+
+ const {data, isLoading} = useSWR(GET_PROVINCE_LIST, fetcher, {
+ revalidateIfStale: false,
+ revalidateOnFocus: false,
+ revalidateOnReconnect: false
+ });
+
+ return {
+ provinceList: data,
+ isLoadingProvinceList: isLoading,
+ errorProvinceList: !data
+ }
+};
+
+export default useProvince;
\ No newline at end of file
diff --git a/src/lib/app/hooks/useRole.jsx b/src/lib/app/hooks/useRole.jsx
new file mode 100644
index 0000000..fc3dd18
--- /dev/null
+++ b/src/lib/app/hooks/useRole.jsx
@@ -0,0 +1,29 @@
+import {GET_ROLE_LIST} from "@/core/data/apiRoutes";
+import useRequest from "@/lib/app/hooks/useRequest";
+import useSWR from "swr";
+
+const useRole = () => {
+ const requestServer = useRequest({auth: true, notification: false})
+
+ //swr config
+ const fetcher = (...args) => {
+ return requestServer(args, 'get').then(({data}) => {
+ return data.data;
+ }).catch(() => {
+ })
+ };
+
+ const {data, isLoading} = useSWR(GET_ROLE_LIST, fetcher, {
+ revalidateIfStale: false,
+ revalidateOnFocus: false,
+ revalidateOnReconnect: false
+ });
+
+ return {
+ roleList: data,
+ isLoadingRoleList: isLoading,
+ errorRoleList: !data
+ }
+};
+
+export default useRole;
\ No newline at end of file
diff --git a/src/pages/dashboard/expert-management/index.jsx b/src/pages/dashboard/expert-management/index.jsx
new file mode 100644
index 0000000..ce91ee6
--- /dev/null
+++ b/src/pages/dashboard/expert-management/index.jsx
@@ -0,0 +1,26 @@
+import RolePermissionMiddleware from "@/middlewares/RolePermission";
+import WithAuthMiddleware from "@/middlewares/WithAuth";
+import {parse} from "next-useragent";
+import DashboardExpertManagementComponent from "@/components/dashboard/expert-management";
+
+const requiredPermissions = ["manage_transportation_navgan"];
+export default function ExpertManagement() {
+ return (
+
+
+
+
+
+ );
+}
+
+export async function getServerSideProps({req, locale}) {
+ const {isBot} = parse(req.headers["user-agent"]);
+ return {
+ props: {
+ messages: (await import(`&/locales/${locale}/app.json`)).default,
+ title: "Dashboard.expert_management",
+ isBot,
+ },
+ };
+}