diff --git a/public/locales/fa/app.json b/public/locales/fa/app.json index 065e317..baecc48 100644 --- a/public/locales/fa/app.json +++ b/public/locales/fa/app.json @@ -11,6 +11,7 @@ "date_picker_label": "فیلتر بر اساس تاریخ", "lessThan": "کوچکتر از", "greaterThan": "بزرگتر از", + "equals": "برابر", "between": "میان", "online_message": "شما به اینترنت وصل هستید", "offline_message": "اتصال شما به اینترنت قطع شده است", @@ -18,8 +19,12 @@ "open_profile": "پروفایل", "edit_profile": " پروفایل", "change_password": "تغییر رمز عبور", + "equals": "برابر", "logout": "خروج" }, + "Select": { + "menu_item": "نوع کاربر را وارد کنید" + }, "Titles": { "title_login_page": "صفحه ورود", "title_login_expert_page": "ورود کارشناس", @@ -42,6 +47,7 @@ "province-head-expert": "کارشناس ستادی", "change-password": "تغییر رمز عبور", "development-assistant": "معاون توسعه", + "user-management": "مدیریت کاربر", "edit-profile": "ویرایش پروفایل" }, "secondary": { @@ -95,6 +101,7 @@ "development_assistant_page": "معاون توسعه", "inspector_expert_page": "بازدید کارشناس", "commercial_chief_page": "رئیس اداره بازرگانی", + "user_management_page": "مدیریت کاربر", "edit_profile": "ویرایش پروفایل" }, "MuiDatePicker": { @@ -268,6 +275,18 @@ "vehicle_type": "نوع ماشین", "state_name": "وضعیت درخواست" }, + "UserManagement": { + "name": "نام", + "id": "کد یکتا", + "national_id": "کد ملی", + "phone_number": "موبایل", + "created_at": "تاریخ درخواست", + "updated_at": "تاریخ بروزرسانی", + "navgan_id": "کد ناوگان", + "type_name": "نوع کاربر", + "vehicle_type": "نوع ماشین", + "state_name": "وضعیت درخواست" + }, "ConfirmDialog": { "confirm": "تایید", "context": "آیا از تایید این آیتم اطمینان دارید؟", @@ -305,14 +324,16 @@ "description_error": "وارد کردن توضیحات الزامی است!" }, "UpdateDialog": { - "update": "تغییر وضعیت درخواست", - "update-tooltip": "بروزرسانی", + "update": "ویرایش", + "update-tooltip": "ویرایش", "button-cancel": "بستن", - "button-update": "بروزرسانی", - "description": "توضیحات خود را وارد نمائید", - "description_error": "وارد کردن توضیحات الزامی است!", - "next-state-id": "وضعیت بعدی", - "next-state-id-error": "وارد کردن وضعیت بعدی الزامی است!" + "refahi": "رفاهی", + "navgan": "ناوگان", + "phone_number": "شماره تلفن", + "national_id": "کد ملی", + "type_id": "نوع کاربر", + "navgan_id": "کد ناوگان", + "button-update": "ویرایش" }, "UploadSystem": { "upload_file": "فایل خود را بارگذاری کنید", @@ -320,6 +341,37 @@ "upload_file_unit": "حداکثر 2Mb", "upload_file_format": "فرمت قابل قبول : png,jpg,pdf", "delete": "پاک کردن", + "phone_number": "شماره تلفن", + "national_id": "کد ملی", + "navgan_id": "کد ناوگان", + "type_id": "نوع کاربر", "uploadfile_error": "حجم فایل بیشتر از 2 مگابایت می باشد" + }, + "DeleteDialog": { + "delete": "حذف", + "button-cancel": "انصراف", + "typography": "آیا از حدف این مورد اطمینان دارید ؟", + "button-delete": "حذف کردن" + }, + "AddDialog": { + "add": "کاربر جدید", + "phone_number": "شماره تلفن", + "phone_number_positive": "شماره تلفن باید مثبت باشد", + "phone_number_error": "وارد کردن شماره تلفن الزامیست", + "phone_number_number": "شماره تلفن باید شامل اعداد باشد", + "phone_number_max": "شماره تلفن باید شامل 11 رقم باشد", + "national_id_positive": "کد ملی باید مثبت باشد", + "national_id_number": "کد ملی باید شامل اعداد باشد", + "national_id_max": "کد ملی باید شامل 10 رقم باشد", + "national_id": "کد ملی", + "national_id_error": "وارد کردن کد ملی الزامیست", + "type_id_error": "وارد کردن نوع کاربر الزامیست", + "navgan_id_error": "وارد کردن کد ناوگان الزامیست", + "refahi": "رفاهی", + "navgan": "ناوگان", + "type_id": "نوع کاربر", + "navgan_id": "کد ناوگان", + "button-cancel": "انصراف", + "button-add": "ثبت" } } diff --git a/src/components/dashboard/commercial-chief/index.jsx b/src/components/dashboard/commercial-chief/index.jsx index 9f84193..b04253a 100644 --- a/src/components/dashboard/commercial-chief/index.jsx +++ b/src/components/dashboard/commercial-chief/index.jsx @@ -131,8 +131,11 @@ function DashboardCommercialChiefComponent() { tableUrl={GET_COMMERCIAL_CHIEF} columns={columns} selectableRow={false} - enableCustomToolbar={true} + enableCustomToolbar={false} enableLastUpdate={true} + sorting={[{ + id: 'id', desc: false + }]} enablePinning={true} enableDensityToggle={false} initialState={{density: 'compact'}} //compact (small) //comfortable (medium) //spacious (large) diff --git a/src/components/dashboard/development-assistant/index.jsx b/src/components/dashboard/development-assistant/index.jsx index ed46fb0..0e058dd 100644 --- a/src/components/dashboard/development-assistant/index.jsx +++ b/src/components/dashboard/development-assistant/index.jsx @@ -132,10 +132,13 @@ function DashboardDevelopmentAssistantComponent() { tableUrl={GET_DEVELOPMENT_ASSISTANT} columns={columns} selectableRow={false} - enableCustomToolbar={true} + enableCustomToolbar={false} enableLastUpdate={true} enablePinning={true} enableDensityToggle={false} + sorting={[{ + id: 'id', desc: false + }]} initialState={{density: 'compact'}} //compact (small) //comfortable (medium) //spacious (large) enableColumnFilters={true} enableHiding={true} diff --git a/src/components/dashboard/inspector-expert/index.jsx b/src/components/dashboard/inspector-expert/index.jsx index 1be98ed..d1c959e 100644 --- a/src/components/dashboard/inspector-expert/index.jsx +++ b/src/components/dashboard/inspector-expert/index.jsx @@ -131,9 +131,12 @@ function DashboardInspectorExpertComponent() { tableUrl={GET_INSPECTOR_EXPERT} columns={columns} selectableRow={false} - enableCustomToolbar={true} + enableCustomToolbar={false} enableLastUpdate={true} enablePinning={true} + sorting={[{ + id: 'id', desc: false + }]} enableDensityToggle={false} initialState={{density: 'compact'}} //compact (small) //comfortable (medium) //spacious (large) enableColumnFilters={true} diff --git a/src/components/dashboard/machinary-office/index.jsx b/src/components/dashboard/machinary-office/index.jsx index 73c373c..2dfbbb1 100644 --- a/src/components/dashboard/machinary-office/index.jsx +++ b/src/components/dashboard/machinary-office/index.jsx @@ -162,8 +162,11 @@ function DashboardMachinaryOfficeComponent() { tableUrl={GET_MACHINARY_OFFICE} columns={columns} selectableRow={false} - enableCustomToolbar={true} + enableCustomToolbar={false} enableLastUpdate={true} + sorting={[{ + id: 'id', desc: false + }]} enablePinning={true} enableDensityToggle={false} initialState={{density: 'compact'}} //compact (small) //comfortable (medium) //spacious (large) diff --git a/src/components/dashboard/navgan-loan-management/index.jsx b/src/components/dashboard/navgan-loan-management/index.jsx index eddf02b..97879be 100644 --- a/src/components/dashboard/navgan-loan-management/index.jsx +++ b/src/components/dashboard/navgan-loan-management/index.jsx @@ -132,9 +132,11 @@ function DashboardNavganLoanManagementComponent() { tableUrl={GET_LOAN_MANAGEMENT_NAVGAN} columns={columns} selectableRow={false} - enableCustomToolbar={true} - // CustomToolbar={} + enableCustomToolbar={false} enableLastUpdate={true} + sorting={[{ + id: 'id', desc: false + }]} enablePinning={true} enableDensityToggle={false} initialState={{density: 'compact'}} //compact (small) //comfortable (medium) //spacious (large) diff --git a/src/components/dashboard/navgan-province-manager/index.jsx b/src/components/dashboard/navgan-province-manager/index.jsx index 8823e37..d2dc745 100644 --- a/src/components/dashboard/navgan-province-manager/index.jsx +++ b/src/components/dashboard/navgan-province-manager/index.jsx @@ -163,8 +163,11 @@ function DashboardNavganProvinceManagerComponent() { tableUrl={GET_NAVGAN_PROVINCE_MANAGER} columns={columns} selectableRow={false} - enableCustomToolbar={true} + enableCustomToolbar={false} enableLastUpdate={true} + sorting={[{ + id: 'id', desc: false + }]} enablePinning={true} enableDensityToggle={false} initialState={{density: 'compact'}} //compact (small) //comfortable (medium) //spacious (large) diff --git a/src/components/dashboard/passenger-boss/Form/ConfirmForm.jsx b/src/components/dashboard/passenger-boss/Form/ConfirmForm.jsx index 4b188f2..7349e22 100644 --- a/src/components/dashboard/passenger-boss/Form/ConfirmForm.jsx +++ b/src/components/dashboard/passenger-boss/Form/ConfirmForm.jsx @@ -40,7 +40,7 @@ const Confirm = ({rowId, fetchUrl, mutate}) => { "is-number", `${t("ConfirmDialog.approved_amount_number")}`, (value) => !isNaN(value) - ).test("positive", `${t("ConfirmDialog.proposed_amount_positive")}`, (value) => value >= 0) + ).test("positive", `${t("ConfirmDialog.approved_amount_positive")}`, (value) => value >= 0) .required(t("ConfirmDialog.approved_amount_error")) }); const formik = useFormik({ diff --git a/src/components/dashboard/passenger-boss/index.jsx b/src/components/dashboard/passenger-boss/index.jsx index ad6848f..392cac3 100644 --- a/src/components/dashboard/passenger-boss/index.jsx +++ b/src/components/dashboard/passenger-boss/index.jsx @@ -163,9 +163,11 @@ function DashboardPassengerOfficeComponent() { tableUrl={GET_PASSENGER_BOSS} columns={columns} selectableRow={false} - enableCustomToolbar={true} - // CustomToolbar={} + enableCustomToolbar={false} enableLastUpdate={true} + sorting={[{ + id: 'id', desc: false + }]} enablePinning={true} enableDensityToggle={false} initialState={{density: 'compact'}} //compact (small) //comfortable (medium) //spacious (large) diff --git a/src/components/dashboard/passenger-office/index.jsx b/src/components/dashboard/passenger-office/index.jsx index 3f50345..7d9e39a 100644 --- a/src/components/dashboard/passenger-office/index.jsx +++ b/src/components/dashboard/passenger-office/index.jsx @@ -163,9 +163,11 @@ function DashboardPassengerOfficeComponent() { tableUrl={GET_PASSENGER_OFFICE} columns={columns} selectableRow={false} - enableCustomToolbar={true} - // CustomToolbar={} + enableCustomToolbar={false} enableLastUpdate={true} + sorting={[{ + id: 'id', desc: false + }]} enablePinning={true} enableDensityToggle={false} initialState={{density: 'compact'}} //compact (small) //comfortable (medium) //spacious (large) diff --git a/src/components/dashboard/province-head-expert/index.jsx b/src/components/dashboard/province-head-expert/index.jsx index c76c96d..a647af9 100644 --- a/src/components/dashboard/province-head-expert/index.jsx +++ b/src/components/dashboard/province-head-expert/index.jsx @@ -132,8 +132,11 @@ function DashboardProvinceHeadExpertComponent() { tableUrl={GET_PROVINCE_HEAD_EXPERT} columns={columns} selectableRow={false} - enableCustomToolbar={true} + enableCustomToolbar={false} enableLastUpdate={true} + sorting={[{ + id: 'id', desc: false + }]} enablePinning={true} enableDensityToggle={false} initialState={{density: 'compact'}} //compact (small) //comfortable (medium) //spacious (large) diff --git a/src/components/dashboard/refahi-loan-management/index.jsx b/src/components/dashboard/refahi-loan-management/index.jsx index 4f0b3e1..6bb3753 100644 --- a/src/components/dashboard/refahi-loan-management/index.jsx +++ b/src/components/dashboard/refahi-loan-management/index.jsx @@ -132,8 +132,10 @@ function DashboardRefahiLoanManagementComponent() { tableUrl={GET_LOAN_MANAGEMENT_REFAHI} columns={columns} selectableRow={false} - enableCustomToolbar={true} - // CustomToolbar={} + enableCustomToolbar={false} + sorting={[{ + id: 'id', desc: false + }]} enableLastUpdate={true} enablePinning={true} enableDensityToggle={false} diff --git a/src/components/dashboard/refahi-province-manager/index.jsx b/src/components/dashboard/refahi-province-manager/index.jsx index 740f6e7..b649948 100644 --- a/src/components/dashboard/refahi-province-manager/index.jsx +++ b/src/components/dashboard/refahi-province-manager/index.jsx @@ -132,8 +132,10 @@ function DashboardRefahiProvinceManagerComponent() { tableUrl={GET_REFAHI_PROVINCE_MANAGER} columns={columns} selectableRow={false} - enableCustomToolbar={true} - // CustomToolbar={} + enableCustomToolbar={false} + sorting={[{ + id: 'id', desc: false + }]} enableLastUpdate={true} enablePinning={true} enableDensityToggle={false} diff --git a/src/components/dashboard/transportation-assistance/index.jsx b/src/components/dashboard/transportation-assistance/index.jsx index 01314d1..0786159 100644 --- a/src/components/dashboard/transportation-assistance/index.jsx +++ b/src/components/dashboard/transportation-assistance/index.jsx @@ -163,8 +163,11 @@ function DashboardTransportationAssistanceComponent() { tableUrl={GET_TRANSPORTATION_ASSISTANCE} columns={columns} selectableRow={false} - enableCustomToolbar={true} + enableCustomToolbar={false} enableLastUpdate={true} + sorting={[{ + id: 'id', desc: false + }]} enablePinning={true} enableDensityToggle={false} initialState={{density: 'compact'}} //compact (small) //comfortable (medium) //spacious (large) diff --git a/src/components/dashboard/user-management/Form/AddForm.jsx b/src/components/dashboard/user-management/Form/AddForm.jsx new file mode 100644 index 0000000..97e6c3b --- /dev/null +++ b/src/components/dashboard/user-management/Form/AddForm.jsx @@ -0,0 +1,186 @@ +import {useTranslations} from "next-intl"; +import * as Yup from "yup"; +import {ADD_USER_MANAGEMENT} from "@/core/data/apiRoutes"; +import {useFormik} from "formik"; +import { + Button, + Dialog, + DialogActions, + DialogContent, + DialogTitle, + FormControl, + FormHelperText, + InputLabel, + MenuItem, + Select, + Stack, + TextField, + Tooltip +} from "@mui/material"; +import useRequest from "@/lib/app/hooks/useRequest"; +import DataSaverOnIcon from "@mui/icons-material/DataSaverOn"; +import {useState} from "react"; + +const AddForm = ({mutate, fetchUrl}) => { + const t = useTranslations(); + const requestServer = useRequest({auth: true}) + const [openConfirmDialog, setOpenConfirmDialog] = useState(false); + const validationSchema = Yup.object().shape({ + phone_number: Yup.mixed().test("is-number", `${t("AddDialog.phone_number_number")}`, (value) => !isNaN(value)).test("positive", `${t("AddDialog.phone_number_positive")}`, (value) => value >= 0) + .test("max", `${t("AddDialog.phone_number_max")}`, (value) => value.length <= 11) + .required(t("AddDialog.phone_number_error")), + national_id: Yup.mixed().test("is-number", `${t("AddDialog.national_id_number")}`, (value) => !isNaN(value)).test("positive", `${t("AddDialog.national_id_positive")}`, (value) => value >= 0) + .test("max", `${t("AddDialog.national_id_max")}`, (value) => value.length <= 10) + .required(t("AddDialog.national_id_error")), + type_id: Yup.number().required(t("AddDialog.type_id_error")), + navgan_id: Yup.number().when('type_id', { + is: 1, // Condition: Check if type_id is equal to 1 + then: (schema) => schema.required(t("AddDialog.navgan_id_error")), + otherwise: (schema) => schema, // No validation for navgan_id when type_id is not 1 + }) + }); + + // validationSchema.describe({value: {type_id: true}}); + + const formik = useFormik({ + initialValues: { + phone_number: "", + national_id: "", + type_id: "", + navgan_id: "", + }, validationSchema, onSubmit: (values, {setSubmitting}) => { + const formData = new FormData(); + formData.append("phone_number", values.phone_number); + formData.append("national_id", values.national_id); + formData.append("type_id", values.type_id); + if (formik.values.type_id === 1) formData.append("navgan_id", values.navgan_id); + + requestServer(ADD_USER_MANAGEMENT, 'post', { + data: formData, + }).then((response) => { + setOpenConfirmDialog(false) + mutate(fetchUrl) + update_notification() + }).catch(() => { + }).finally(() => { + setSubmitting(false); + }); + }, + }); + return ( + + + + + {t("AddDialog.add")} + + + + { + if (event.target.value.length <= 11) return formik.handleChange(event) + }} + fullWidth + variant="outlined" + onBlur={formik.handleBlur("phone_number")} + error={formik.touched.phone_number && Boolean(formik.errors.phone_number)} + helperText={formik.touched.phone_number && formik.errors.phone_number} + sx={{mt: 1}} + /> + + + { + if (event.target.value.length <= 10) return formik.handleChange(event) + }} + fullWidth + variant="outlined" + onBlur={formik.handleBlur("national_id")} + error={formik.touched.national_id && Boolean(formik.errors.national_id)} + helperText={formik.touched.national_id && formik.errors.national_id} + sx={{mt: 1}} + /> + + + + {t("AddDialog.type_id")} + + + {formik.touched.type_id && formik.errors.type_id} + + + + + {formik.values.type_id === 1 ? () : null} + + + + + + + + + ); +}; +export default AddForm; \ No newline at end of file diff --git a/src/components/dashboard/user-management/Form/DeleteForm.jsx b/src/components/dashboard/user-management/Form/DeleteForm.jsx new file mode 100644 index 0000000..5cf2eb3 --- /dev/null +++ b/src/components/dashboard/user-management/Form/DeleteForm.jsx @@ -0,0 +1,67 @@ +import {useTranslations} from "next-intl"; +import {useState} from "react"; +import { + Button, + Dialog, + DialogActions, + DialogContent, + DialogTitle, + IconButton, + Tooltip, + Typography +} from "@mui/material"; +import DeleteIcon from '@mui/icons-material/Delete'; +import {DELETE_USER_MANAGEMENT} from "@/core/data/apiRoutes"; +import useRequest from "@/lib/app/hooks/useRequest"; +import useNotification from "@/lib/app/hooks/useNotification"; + +const DeleteForm = ({rowId, fetchUrl, mutate}) => { + const t = useTranslations(); + const [openConfirmDialog, setOpenConfirmDialog] = useState(false); + const [isSubmitting, setIsSubmitting] = useState(false) + const requestServer = useRequest({auth: true}) + const {update_notification} = useNotification() + const handleSubmit = () => { + setIsSubmitting(true) + requestServer(`${DELETE_USER_MANAGEMENT}/${rowId}`, 'delete').then((response) => { + mutate(fetchUrl) + update_notification() + }).catch(() => { + }).finally(() => { + setIsSubmitting(false) + }); + } + + return ( + <> + + { + setOpenConfirmDialog(true) + }} + > + + + + + {t("DeleteDialog.delete")} + + {t("DeleteDialog.typography")} + + + + + + + + ); +}; +export default DeleteForm; \ No newline at end of file diff --git a/src/components/dashboard/user-management/Form/UpdateForm.jsx b/src/components/dashboard/user-management/Form/UpdateForm.jsx new file mode 100644 index 0000000..77e495f --- /dev/null +++ b/src/components/dashboard/user-management/Form/UpdateForm.jsx @@ -0,0 +1,178 @@ +import {useTranslations} from "next-intl"; +import {useState} from "react"; +import { + Button, + Dialog, + DialogActions, + DialogContent, + DialogTitle, + FormControl, + FormHelperText, + IconButton, + InputLabel, + MenuItem, + Select, + Stack, + TextField, + Tooltip +} from "@mui/material"; +import {useFormik} from "formik"; +import EditIcon from '@mui/icons-material/Edit'; +import {UPDATE_USER_MANAGEMENT} from "@/core/data/apiRoutes"; +import useRequest from "@/lib/app/hooks/useRequest"; +import useNotification from "@/lib/app/hooks/useNotification"; +import * as Yup from "yup"; + +const UpdateForm = ({row, fetchUrl, mutate}) => { + const t = useTranslations(); + const [openConfirmDialog, setOpenConfirmDialog] = useState(false); + const requestServer = useRequest({auth: true}) + const {update_notification} = useNotification() + + const validationSchema = Yup.object().shape({ + phone_number: Yup.mixed().test("is-number", `${t("AddDialog.phone_number_number")}`, (value) => !isNaN(value)).test("positive", `${t("AddDialog.phone_number_positive")}`, (value) => value >= 0) + .test("max", `${t("AddDialog.phone_number_max")}`, (value) => value.length <= 11) + .required(t("AddDialog.phone_number_error")), + national_id: Yup.mixed().test("is-number", `${t("AddDialog.national_id_number")}`, (value) => !isNaN(value)).test("positive", `${t("AddDialog.national_id_positive")}`, (value) => value >= 0) + .test("max", `${t("AddDialog.national_id_max")}`, (value) => value.length <= 10) + .required(t("AddDialog.national_id_error")), + type_id: Yup.number().required(t("AddDialog.type_id_error")), + navgan_id: Yup.number().when('type_id', { + is: 1, // Condition: Check if type_id is equal to 1 + then: (schema) => schema.required(t("AddDialog.navgan_id_error")), + otherwise: (schema) => schema, // No validation for navgan_id when type_id is not 1 + }) + }); + + const formik = useFormik({ + initialValues: { + phone_number: row.getValue("phone_number"), + national_id: row.getValue("national_id"), + type_id: row.original.type_id, + navgan_id: row.getValue("navgan_id"), + }, validationSchema, onSubmit: (values, {setSubmitting}) => { + const formData = new FormData(); + formData.append("phone_number", values.phone_number); + formData.append("national_id", values.national_id); + formData.append("type_id", values.type_id); + if (formik.values.type_id === 1) formData.append("navgan_id", values.navgan_id); + requestServer(`${UPDATE_USER_MANAGEMENT}/${row.getValue("id")}`, 'post', { + data: formData, + }).then((response) => { + mutate(fetchUrl) + update_notification() + }).catch(() => { + }).finally(() => { + setSubmitting(false); + }); + }, + }); + + return (<> + + { + setOpenConfirmDialog(true) + }} + > + + + + + {t("UpdateDialog.update")} + + + + { + if (event.target.value.length <= 11) return formik.handleChange(event) + }} + fullWidth + variant="outlined" + onBlur={formik.handleBlur("phone_number")} + error={formik.touched.phone_number && Boolean(formik.errors.phone_number)} + helperText={formik.touched.phone_number && formik.errors.phone_number} + sx={{mt: 1}} + /> + + + { + if (event.target.value.length <= 10) return formik.handleChange(event) + }} + fullWidth + variant="outlined" + onBlur={formik.handleBlur("national_id")} + error={formik.touched.national_id && Boolean(formik.errors.national_id)} + helperText={formik.touched.national_id && formik.errors.national_id} + sx={{mt: 1}} + /> + + + + {t("UpdateDialog.type_id")} + + + {formik.touched.type_id && formik.errors.type_id} + + + + + {formik.values.type_id === 1 ? () : null} + + + + + + + + + ); +}; +export default UpdateForm; \ No newline at end of file diff --git a/src/components/dashboard/user-management/TableRowActions.jsx b/src/components/dashboard/user-management/TableRowActions.jsx new file mode 100644 index 0000000..5b0f031 --- /dev/null +++ b/src/components/dashboard/user-management/TableRowActions.jsx @@ -0,0 +1,23 @@ +import {Box} from "@mui/material"; +import DeleteForm from "./Form/DeleteForm" +import UpdateForm from "./Form/UpdateForm" + +const TableRowActions = ({row, mutate, fetchUrl}) => { + + return ( + + + + + ); +}; + +export default TableRowActions; diff --git a/src/components/dashboard/user-management/TableToolbar.jsx b/src/components/dashboard/user-management/TableToolbar.jsx new file mode 100644 index 0000000..558cdc5 --- /dev/null +++ b/src/components/dashboard/user-management/TableToolbar.jsx @@ -0,0 +1,11 @@ +import {useTranslations} from "next-intl"; +import AddForm from "./Form/AddForm"; + +function TableToolbar({mutate, fetchUrl}) { + const t = useTranslations(); + + return + +} + +export default TableToolbar; diff --git a/src/components/dashboard/user-management/index.jsx b/src/components/dashboard/user-management/index.jsx new file mode 100644 index 0000000..ddf9099 --- /dev/null +++ b/src/components/dashboard/user-management/index.jsx @@ -0,0 +1,143 @@ +import DashboardLayouts from "@/layouts/dashboardLayouts"; +import {Box, FormControl, FormHelperText, IconButton, MenuItem, Select, Stack, Typography} from "@mui/material"; +import {useMemo} from "react"; +import {GET_USER_MANAGEMENT} from "@/core/data/apiRoutes"; +import {useTranslations} from "next-intl"; +import DataTable from "@/core/components/DataTable"; +import moment from "jalali-moment"; +import MuiDatePicker from "@/core/components/MuiDatePicker"; +import TableToolbar from "@/components/dashboard/user-management/TableToolbar"; +import TableRowActions from "./TableRowActions" +import ClearIcon from "@mui/icons-material/Clear"; + +function DashboardUserManagementComponent() { + const t = useTranslations(); + + const columns = useMemo(() => [{ + accessorFn: (row) => row.id, + id: "id", + sortDescFirst: true, + header: t("UserManagement.id"), + enableColumnFilter: true, + datatype: "numeric", + filterFn: "equals", + columnFilterModeOptions: ["equals", "notEquals", "contains", "lessThan", "greaterThan", "between",], + Cell: ({renderedCellValue}) => ({renderedCellValue}), + }, { + accessorFn: (row) => row.navgan_id, + id: "navgan_id", + header: t("UserManagement.navgan_id"), + enableColumnFilter: true, + datatype: "text", + filterFn: "contains", + columnFilterModeOptions: ["contains", "equals", "notEquals"], + Cell: ({renderedCellValue}) => ({renderedCellValue}), + }, { + accessorFn: (row) => row.national_id, + id: "national_id", + header: t("UserManagement.national_id"), + enableColumnFilter: true, + datatype: "numeric", + filterFn: "equals", + columnFilterModeOptions: ["equals", "notEquals", "contains", "lessThan", "greaterThan", "between",], + Cell: ({renderedCellValue}) => ({renderedCellValue}), + }, { + accessorFn: (row) => row.phone_number, + id: "phone_number", + header: t("UserManagement.phone_number"), + enableColumnFilter: true, + datatype: "numeric", + filterFn: "equals", + columnFilterModeOptions: ["equals", "notEquals", "contains", "lessThan", "greaterThan", "between",], + Cell: ({renderedCellValue}) => ({renderedCellValue}), + }, { + accessorFn: (row) => moment(row.created_at).locale("fa").format("HH:mm | yyyy/MM/DD"), + id: "created_at", + header: t("UserManagement.created_at"), + enableColumnFilter: true, + datatype: "date", + filterFn: "lessThan", + columnFilterModeOptions: ["lessThan", "greaterThan"], + Cell: ({renderedCellValue}) => { + return {renderedCellValue}; + }, + Header: ({column}) => <>{column.columnDef.header}, + Filter: ({column}) => { + return (); + }, + }, { + accessorFn: (row) => moment(row.updated_at).locale("fa").format("HH:mm | yyyy/MM/DD"), + id: "updated_at", + header: t("UserManagement.updated_at"), + enableColumnFilter: false, + datatype: "numeric", + Cell: ({renderedCellValue}) => ({renderedCellValue}), + }, { + accessorFn: (row) => row.type_name, + id: "type_id", + header: t("UserManagement.type_name"), + enableColumnFilter: true, + datatype: "numeric", + filterFn: "equals", + columnFilterModeOptions: ["equals"], + Cell: ({renderedCellValue}) => ({renderedCellValue}), + Header: ({column}) => <>{column.columnDef.header}, + Filter: ({column}) => { + return ( + + + {t("filter_mode")}: {t(column.columnDef._filterFn)} + + { + column.setFilterValue(null); + }} + sx={{ + color: column.getFilterValue() ? "rgba(0, 0, 0, 0.54)" : "#bfbfbf" + }} + > + + + ) + } + },], []); + return ( + + + + ); +} + +export default DashboardUserManagementComponent; diff --git a/src/core/components/DataTable.jsx b/src/core/components/DataTable.jsx index b3b0f26..1ec20b4 100644 --- a/src/core/components/DataTable.jsx +++ b/src/core/components/DataTable.jsx @@ -11,8 +11,7 @@ function DataTable(props) { const requestServer = useRequest({auth: true}) const fetcher = (...args) => { return requestServer(args, 'get', { - pending: false, - success: {notification: {show: false}} + pending: false, success: {notification: {show: false}} }).then((response) => { setRowCount(response.data.meta.totalRowCount); return response.data.data; @@ -22,14 +21,12 @@ function DataTable(props) { const t = useTranslations(); const {languageApp, languageList} = useLanguage(); const [columnFilters, setColumnFilters] = useState([]); - const [sorting, setSorting] = useState([]); + const [sorting, setSorting] = useState(props.sorting || []); const [pagination, setPagination] = useState({pageIndex: 0, pageSize: 10}); const [rowCount, setRowCount] = useState(0); const [columnFilterFns, setColumnFilterFns] = useState(() => { let output = {}; - const list = props.columns.map((item) => - item.enableColumnFilter ? {[item.id]: item.filterFn} : {[item.id]: ""} - ); + const list = props.columns.map((item) => item.enableColumnFilter ? {[item.id]: item.filterFn} : {[item.id]: ""}); for (var key in list) { var nestedObj = list[key]; for (var nestedKey in nestedObj) { @@ -39,22 +36,13 @@ function DataTable(props) { return output; }); - const [updateTime, setupdateTime] = useState( - moment().format("HH:mm | jYYYY/jM/jD") - ); + const [updateTime, setupdateTime] = useState(moment().format("HH:mm | jYYYY/jM/jD")); - const tableLocalization = useMemo( - () => - languageList.find((item) => item.key == languageApp).tableLocalization, - [languageApp, languageList] - ); + const tableLocalization = useMemo(() => languageList.find((item) => item.key == languageApp).tableLocalization, [languageApp, languageList]); const fetchUrl = useMemo(() => { const url = new URL(props.tableUrl); - url.searchParams.set( - "start", - `${pagination.pageIndex * pagination.pageSize}` - ); + url.searchParams.set("start", `${pagination.pageIndex * pagination.pageSize}`); const filters = columnFilters.map((filter) => { let datatype; for (const i in props.columns) { @@ -63,114 +51,84 @@ function DataTable(props) { } } return { - ...filter, - fn: columnFilterFns[filter.id], - datatype: datatype, + ...filter, fn: columnFilterFns[filter.id], datatype: datatype, }; }); url.searchParams.set("size", pagination.pageSize); url.searchParams.set("filters", JSON.stringify(filters ?? [])); url.searchParams.set("sorting", JSON.stringify(sorting ?? [])); return url; - }, [ - props.tableUrl, - columnFilters, - columnFilterFns, - pagination, - sorting, - props.columns, - ]); + }, [props.tableUrl, columnFilters, columnFilterFns, pagination, sorting, props.columns,]); const {data, isValidating, mutate} = useSWR(fetchUrl, fetcher, { - revalidateIfStale: false, - revalidateOnFocus: false, - revalidateOnReconnect: false + revalidateIfStale: false, revalidateOnFocus: false, revalidateOnReconnect: false }); useEffect(() => { setupdateTime(moment().format("HH:mm | jYYYY/jM/jD")); }, [isValidating, languageApp]); - return ( - (<> + {props.enableCustomToolbar /* send condition */ ? + /* send component */ : ""} + )} + renderBottomToolbarCustomActions={({table}) => (<> + {props.enableLastUpdate /* send condition */ ? ( ( - <> - {props.enableCustomToolbar /* send condition */ - ? props.CustomToolbar /* send component */ - : ""} - - )} - renderBottomToolbarCustomActions={({table}) => ( - <> - {props.enableLastUpdate /* send condition */ ? ( - - {t("last_updated_at")}: {updateTime} - - ) : ( - "" - )} - - )} - state={{ - isLoading: isValidating, - columnFilters, - columnFilterFns, - pagination, - sorting, - }} - positionActionsColumn={"last"} - enableRowActions={props.enableRowActions} - renderRowActions={({row}) => } - {...props} - /> - ); + alignSelf: "center", + whiteSpace: "nowrap", + maxWidth: {xs: 100, sm: "100%"}, + overflowX: "scroll", + }} + variant="caption" + > + {t("last_updated_at")}: {updateTime} + ) : ("")} + )} + state={{ + isLoading: isValidating, columnFilters, columnFilterFns, pagination, sorting, + }} + positionActionsColumn={"last"} + enableRowActions={props.enableRowActions} + renderRowActions={({row}) => } + {...props} + />); } export default DataTable; \ No newline at end of file diff --git a/src/core/data/apiRoutes.js b/src/core/data/apiRoutes.js index aba46f1..19b89a2 100644 --- a/src/core/data/apiRoutes.js +++ b/src/core/data/apiRoutes.js @@ -150,3 +150,14 @@ export const UPDATE_LOAN_MANAGEMENT_NAVGAN = export const GET_LOAN_STATE_NAVGAN = BASE_URL + "/dashboard/loan_states/navgan" //loan management navgan + +//user management +export const GET_USER_MANAGEMENT = + BASE_URL + "/dashboard/user_management/show" +export const ADD_USER_MANAGEMENT = + BASE_URL + "/dashboard/user_management/store" +export const UPDATE_USER_MANAGEMENT = + BASE_URL + "/dashboard/user_management/update" +export const DELETE_USER_MANAGEMENT = + BASE_URL + "/dashboard/user_management/delete" +//user management diff --git a/src/core/data/sidebarMenu.jsx b/src/core/data/sidebarMenu.jsx index 21a0952..4957bad 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 PersonIcon from '@mui/icons-material/Person'; const sidebarMenu = [ [ @@ -136,6 +137,15 @@ const sidebarMenu = [ selected: false, permission: "manage_navgan_loan", }, + { + key: "sidebar.user-management", + name: "user_management", + type: "page", + route: "/dashboard/user-management", + icon: , + selected: false, + permission: "manage_users", + }, ], ]; diff --git a/src/pages/dashboard/user-management/index.jsx b/src/pages/dashboard/user-management/index.jsx new file mode 100644 index 0000000..5a43454 --- /dev/null +++ b/src/pages/dashboard/user-management/index.jsx @@ -0,0 +1,26 @@ +import RolePermissionMiddleware from "@/middlewares/RolePermission"; +import WithAuthMiddleware from "@/middlewares/WithAuth"; +import {parse} from "next-useragent"; +import DashboardUserManagementComponent from "@/components/dashboard/user-management"; + +const requiredPermissions = ["manage_users"]; +export default function UserManagement() { + 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.user_management_page", + isBot, + }, + }; +}