From eb4af5c824b36594d4eb18ffc45e6d48801d4d5e Mon Sep 17 00:00:00 2001 From: Amin Ghasempoor Date: Mon, 18 Sep 2023 14:14:51 +0330 Subject: [PATCH] TF-96 role management initialize --- public/locales/fa/app.json | 21 +++ .../Form/CreateForm/CreateContent.jsx | 137 +++++++++++++++ .../role-management/Form/CreateForm/index.jsx | 35 ++++ .../role-management/Form/DeleteForm.jsx | 67 ++++++++ .../role-management/Form/UpdateForm.jsx | 156 ++++++++++++++++++ .../role-management/TableRowActions.jsx | 23 +++ .../role-management/TableToolbar.jsx | 11 ++ .../dashboard/role-management/index.jsx | 96 +++++++++++ src/core/data/apiRoutes.js | 14 ++ src/core/data/sidebarMenu.jsx | 10 ++ src/lib/app/hooks/usePermissions.jsx | 25 +++ src/pages/dashboard/role-management/index.jsx | 26 +++ 12 files changed, 621 insertions(+) create mode 100644 src/components/dashboard/role-management/Form/CreateForm/CreateContent.jsx create mode 100644 src/components/dashboard/role-management/Form/CreateForm/index.jsx create mode 100644 src/components/dashboard/role-management/Form/DeleteForm.jsx create mode 100644 src/components/dashboard/role-management/Form/UpdateForm.jsx create mode 100644 src/components/dashboard/role-management/TableRowActions.jsx create mode 100644 src/components/dashboard/role-management/TableToolbar.jsx create mode 100644 src/components/dashboard/role-management/index.jsx create mode 100644 src/lib/app/hooks/usePermissions.jsx create mode 100644 src/pages/dashboard/role-management/index.jsx diff --git a/public/locales/fa/app.json b/public/locales/fa/app.json index 071daf8..b070875 100644 --- a/public/locales/fa/app.json +++ b/public/locales/fa/app.json @@ -48,6 +48,7 @@ "change-password": "تغییر رمز عبور", "development-assistant": "معاون توسعه", "user-management": "مدیریت کاربر", + "role-management": "مدیریت نقش ها", "edit-profile": "ویرایش پروفایل" }, "secondary": { @@ -103,6 +104,7 @@ "inspector_expert_page": "بازدید کارشناس", "commercial_chief_page": "رئیس اداره بازرگانی", "user_management_page": "مدیریت کاربر", + "role_management_page": "مدیریت نقش ها", "edit_profile": "ویرایش پروفایل" }, "MuiDatePicker": { @@ -290,6 +292,13 @@ "vehicle_type": "نوع ماشین", "state_name": "وضعیت درخواست" }, + "RoleManagement": { + "id": "کد یکتا", + "name_fa": "نام فارسی ", + "name": "نام انگلیسی", + "created_at": "تاریخ درخواست", + "updated_at": "تاریخ بروزرسانی" + }, "ConfirmDialog": { "confirm": "تایید", "context": "آیا از تایید این آیتم اطمینان دارید؟", @@ -334,6 +343,12 @@ "navgan": "ناوگان", "phone_number": "شماره تلفن", "national_id": "کد ملی", + "name": "نام انگلیسی", + "name_error": "وارد کردن نام انگلیسی الزامیست", + "name_fa": "نام فارسی", + "name_fa_error": "وارد کردن نام فارسی الزامیست", + "permission": "دسترسی", + "permission_min_error": "حداقل باید یک دسترسی انتخاب شود", "type_id": "نوع کاربر", "navgan_id": "کد ناوگان", "button-update": "ویرایش" @@ -358,6 +373,12 @@ }, "AddDialog": { "add": "افزودن کاربر جدید", + "name": "نام انگلیسی", + "name_error": "وارد کردن نام انگلیسی الزامیست", + "name_fa": "نام فارسی", + "name_fa_error": "وارد کردن نام فارسی الزامیست", + "permission": "دسترسی", + "permission_min_error": "حداقل باید یک دسترسی انتخاب شود", "phone_number": "شماره تلفن", "phone_number_positive": "شماره تلفن باید مثبت باشد", "phone_number_error": "وارد کردن شماره تلفن الزامیست", diff --git a/src/components/dashboard/role-management/Form/CreateForm/CreateContent.jsx b/src/components/dashboard/role-management/Form/CreateForm/CreateContent.jsx new file mode 100644 index 0000000..a7f5f37 --- /dev/null +++ b/src/components/dashboard/role-management/Form/CreateForm/CreateContent.jsx @@ -0,0 +1,137 @@ +import { + Button, + Checkbox, + DialogActions, + DialogContent, + FormControl, + FormControlLabel, + FormHelperText, + FormLabel, + Grid, + Stack, + TextField +} from "@mui/material"; +import {useTranslations} from "next-intl"; +import useRequest from "@/lib/app/hooks/useRequest"; +import * as Yup from "yup"; +import {useFormik} from "formik"; +import {ADD_ROLE_MANAGEMENT} from "@/core/data/apiRoutes"; +import usePermissions from "@/lib/app/hooks/usePermissions"; + +const CreateContent = ({mutate, fetchUrl, setOpenConfirmDialog}) => { + const t = useTranslations(); + const requestServer = useRequest({auth: true}) + const {permissions_list} = usePermissions() + const validationSchema = Yup.object().shape({ + name: Yup.string().required(t("AddDialog.name_error")), + name_fa: Yup.string().required(t("AddDialog.name_fa_error")), + permissions: Yup.array().min(1, t("AddDialog.permission_min_error")).required(t("AddDialog.permission")), + }); + const formik = useFormik({ + initialValues: { + name: "", + name_fa: "", + permissions: [], + }, validationSchema, onSubmit: (values, {setSubmitting}) => { + const formData = new FormData(); + formData.append("name", values.name); + formData.append("name_fa", values.name_fa); + for (let i = 0; i < values.permissions.length; i++) { + formData.append(`permissions[${i}]`, values.permissions[i]); + } + + requestServer(ADD_ROLE_MANAGEMENT, 'post', { + data: formData, + }).then((response) => { + setOpenConfirmDialog(false) + mutate(fetchUrl) + update_notification() + }).catch(() => { + }).finally(() => { + setSubmitting(false); + }); + }, + }); + + return ( + <> + + + + + + + + {t("AddDialog.permission")}
+ + {formik.touched.permissions && formik.errors.permissions} + + {permissions_list ? ( + permissions_list.map((permission) => ( + + { + if (e.target.checked) { + formik.setFieldValue("permissions", [...formik.values.permissions, permission.id]) + } else { + formik.setFieldValue("permissions", formik.values.permissions.filter((id) => id !== permission.id)) + } + }} + /> + } + label={permission.name_fa} + /> + + )) + ) : null} + + +
+
+
+
+ + + + + + ) +} +export default CreateContent \ No newline at end of file diff --git a/src/components/dashboard/role-management/Form/CreateForm/index.jsx b/src/components/dashboard/role-management/Form/CreateForm/index.jsx new file mode 100644 index 0000000..f0a27e7 --- /dev/null +++ b/src/components/dashboard/role-management/Form/CreateForm/index.jsx @@ -0,0 +1,35 @@ +import {Button, Dialog, DialogTitle, Stack, Tooltip} from "@mui/material"; +import DataSaverOnIcon from "@mui/icons-material/DataSaverOn"; +import {useTranslations} from "next-intl"; +import {useState} from "react"; +import CreateContent from "./CreateContent"; + +const CreateForm = ({mutate, fetchUrl}) => { + const t = useTranslations(); + const [openConfirmDialog, setOpenConfirmDialog] = useState(false); + + return ( + + + + + + {t("AddDialog.add")} + + + + ) +} +export default CreateForm \ No newline at end of file diff --git a/src/components/dashboard/role-management/Form/DeleteForm.jsx b/src/components/dashboard/role-management/Form/DeleteForm.jsx new file mode 100644 index 0000000..9c613b1 --- /dev/null +++ b/src/components/dashboard/role-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 useRequest from "@/lib/app/hooks/useRequest"; +import useNotification from "@/lib/app/hooks/useNotification"; +import {DELETE_ROLE_MANAGEMENT} from "@/core/data/apiRoutes"; + +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_ROLE_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/role-management/Form/UpdateForm.jsx b/src/components/dashboard/role-management/Form/UpdateForm.jsx new file mode 100644 index 0000000..7fd4459 --- /dev/null +++ b/src/components/dashboard/role-management/Form/UpdateForm.jsx @@ -0,0 +1,156 @@ +import {useTranslations} from "next-intl"; +import {useState} from "react"; +import { + Button, + Checkbox, + Dialog, + DialogActions, + DialogContent, + DialogTitle, + FormControl, + FormControlLabel, + FormHelperText, + FormLabel, + Grid, + IconButton, + Stack, + TextField, + Tooltip +} from "@mui/material"; +import {useFormik} from "formik"; +import EditIcon from '@mui/icons-material/Edit'; +import {UPDATE_ROLE_MANAGEMENT} from "@/core/data/apiRoutes"; +import useRequest from "@/lib/app/hooks/useRequest"; +import useNotification from "@/lib/app/hooks/useNotification"; +import * as Yup from "yup"; +import usePermissions from "@/lib/app/hooks/usePermissions"; + +const UpdateForm = ({row, fetchUrl, mutate}) => { + const t = useTranslations(); + const [openConfirmDialog, setOpenConfirmDialog] = useState(false); + const requestServer = useRequest({auth: true}) + const {update_notification} = useNotification() + const {permissions_list} = usePermissions() + + const validationSchema = Yup.object().shape({ + name: Yup.string().required(t("UpdateDialog.name_error")), + name_fa: Yup.string().required(t("UpdateDialog.name_fa_error")), + permissions: Yup.array().min(1, t("UpdateDialog.permission_min_error")).required(t("UpdateDialog.permission")), + }); + + const formik = useFormik({ + initialValues: { + name: row.getValue("name"), + name_fa: row.getValue("name_fa"), + permissions: row.original.permissions.map((obj) => obj.id), + }, validationSchema, onSubmit: (values, {setSubmitting}) => { + const formData = new FormData(); + formData.append("name", values.name); + formData.append("name_fa", values.name_fa); + for (let i = 0; i < values.permissions.length; i++) { + formData.append(`permissions[${i}]`, values.permissions[i]); + } + + requestServer(`${UPDATE_ROLE_MANAGEMENT}/${row.getValue("id")}`, 'post', { + data: formData, + }).then((response) => { + mutate(fetchUrl) + update_notification() + }).catch(() => { + }).finally(() => { + setSubmitting(false); + }); + }, + }); + + return (<> + + { + setOpenConfirmDialog(true) + }} + > + + + + + {t("UpdateDialog.update")} + + + + + + + + {t("UpdateDialog.permission")}
+ + {formik.touched.permissions && formik.errors.permissions} + + {permissions_list ? ( + permissions_list.map((permission) => ( + + { + if (e.target.checked) { + formik.setFieldValue("permissions", [...formik.values.permissions, permission.id]) + } else { + formik.setFieldValue("permissions", formik.values.permissions.filter((id) => id !== permission.id)) + } + }} + /> + } + label={permission.name_fa} + /> + + )) + ) : null} + + +
+
+
+
+ + + + +
+ ); +}; +export default UpdateForm; \ No newline at end of file diff --git a/src/components/dashboard/role-management/TableRowActions.jsx b/src/components/dashboard/role-management/TableRowActions.jsx new file mode 100644 index 0000000..5b0f031 --- /dev/null +++ b/src/components/dashboard/role-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/role-management/TableToolbar.jsx b/src/components/dashboard/role-management/TableToolbar.jsx new file mode 100644 index 0000000..c4cb13b --- /dev/null +++ b/src/components/dashboard/role-management/TableToolbar.jsx @@ -0,0 +1,11 @@ +import {useTranslations} from "next-intl"; +import CreateForm from "./Form/CreateForm/index"; + +function TableToolbar({mutate, fetchUrl}) { + const t = useTranslations(); + + return + +} + +export default TableToolbar; diff --git a/src/components/dashboard/role-management/index.jsx b/src/components/dashboard/role-management/index.jsx new file mode 100644 index 0000000..5d55575 --- /dev/null +++ b/src/components/dashboard/role-management/index.jsx @@ -0,0 +1,96 @@ +import DashboardLayouts from "@/layouts/dashboardLayouts"; +import {Box, Typography} from "@mui/material"; +import {useMemo} from "react"; +import {GET_ROLE_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/role-management/TableToolbar"; +import TableRowActions from "./TableRowActions" + +function DashboardRoleManagementComponent() { + const t = useTranslations(); + + const columns = useMemo(() => [{ + accessorFn: (row) => row.id, + id: "id", + sortDescFirst: true, + header: t("RoleManagement.id"), + enableColumnFilter: true, + datatype: "numeric", + filterFn: "equals", + columnFilterModeOptions: ["equals", "notEquals", "contains", "lessThan", "greaterThan", "between",], + Cell: ({renderedCellValue}) => ({renderedCellValue}), + }, + { + accessorFn: (row) => row.name_fa, + id: "name_fa", + header: t("RoleManagement.name_fa"), + enableColumnFilter: true, + datatype: "text", + filterFn: "contains", + columnFilterModeOptions: ["contains", "equals", "notEquals"], + Cell: ({renderedCellValue}) => ({renderedCellValue}), + }, + { + accessorFn: (row) => row.name, + id: "name", + header: t("RoleManagement.name"), + enableColumnFilter: true, + datatype: "text", + filterFn: "contains", + columnFilterModeOptions: ["contains"], + Cell: ({renderedCellValue}) => ({renderedCellValue}), + }, + { + accessorFn: (row) => moment(row.created_at).locale("fa").format("HH:mm | yyyy/MM/DD"), + id: "created_at", + header: t("RoleManagement.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("RoleManagement.updated_at"), + enableColumnFilter: false, + datatype: "numeric", + Cell: ({renderedCellValue}) => ({renderedCellValue}), + }], []); + return ( + + + + ); +} + +export default DashboardRoleManagementComponent; diff --git a/src/core/data/apiRoutes.js b/src/core/data/apiRoutes.js index 19b89a2..127e205 100644 --- a/src/core/data/apiRoutes.js +++ b/src/core/data/apiRoutes.js @@ -161,3 +161,17 @@ export const UPDATE_USER_MANAGEMENT = export const DELETE_USER_MANAGEMENT = BASE_URL + "/dashboard/user_management/delete" //user management + +// role management +export const GET_ROLE_MANAGEMENT = + BASE_URL + "/dashboard/roles" +export const ADD_ROLE_MANAGEMENT = + BASE_URL + "/dashboard/roles/store" +export const UPDATE_ROLE_MANAGEMENT = + BASE_URL + "/dashboard/roles/update" +export const DELETE_ROLE_MANAGEMENT = + BASE_URL + "/dashboard/roles/delete" + +export const GET_PERMISSIONS_LIST = + BASE_URL + "/dashboard/permissions/list" +//role management diff --git a/src/core/data/sidebarMenu.jsx b/src/core/data/sidebarMenu.jsx index 4957bad..de305d7 100644 --- a/src/core/data/sidebarMenu.jsx +++ b/src/core/data/sidebarMenu.jsx @@ -11,6 +11,7 @@ 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'; +import AccessibilityIcon from '@mui/icons-material/Accessibility'; const sidebarMenu = [ [ @@ -146,6 +147,15 @@ const sidebarMenu = [ selected: false, permission: "manage_users", }, + { + key: "sidebar.role-management", + name: "role_management", + type: "page", + route: "/dashboard/role-management", + icon: , + selected: false, + permission: "manage_roles", + }, ], ]; diff --git a/src/lib/app/hooks/usePermissions.jsx b/src/lib/app/hooks/usePermissions.jsx new file mode 100644 index 0000000..0e6aa07 --- /dev/null +++ b/src/lib/app/hooks/usePermissions.jsx @@ -0,0 +1,25 @@ +import useRequest from "@/lib/app/hooks/useRequest"; +import useSWR from "swr"; +import {GET_PERMISSIONS_LIST} from "@/core/data/apiRoutes"; + +const usePermissions = () => { + const requestServer = useRequest({auth: true, notification: false}) + const fetcher = (...args) => { + return requestServer(args, 'get').then((response) => { + return response.data.data; + }).catch(() => { + }) + }; + + const {data} = useSWR(GET_PERMISSIONS_LIST, fetcher, { + revalidateIfStale: false, + revalidateOnFocus: false, + revalidateOnReconnect: false + }) + const permissions_list = data + //swr config + + // render data + return {permissions_list} +} +export default usePermissions; \ No newline at end of file diff --git a/src/pages/dashboard/role-management/index.jsx b/src/pages/dashboard/role-management/index.jsx new file mode 100644 index 0000000..c309729 --- /dev/null +++ b/src/pages/dashboard/role-management/index.jsx @@ -0,0 +1,26 @@ +import RolePermissionMiddleware from "@/middlewares/RolePermission"; +import WithAuthMiddleware from "@/middlewares/WithAuth"; +import {parse} from "next-useragent"; +import DashboardRoleManagementComponent from "@/components/dashboard/role-management"; + +const requiredPermissions = ["manage_roles"]; +export default function RoleManagement() { + 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.role_management_page", + isBot, + }, + }; +}