From 7a94678fd46c93c4730f09a4fe5186a558a92f2f Mon Sep 17 00:00:00 2001 From: Yasiu1376 Date: Mon, 2 Oct 2023 15:13:12 +0330 Subject: [PATCH 01/37] CFE-12 implement changepasswordform implement changepasswordform test --- public/locales/fa/app.json | 4 +- .../__test__/index.test.js | 322 ++++++++++++++++++ .../change-password-form/index.js | 137 ++++++++ .../dashboard/change-password/index.jsx | 137 +------- 4 files changed, 465 insertions(+), 135 deletions(-) create mode 100644 src/components/dashboard/change-password/change-password-form/__test__/index.test.js create mode 100644 src/components/dashboard/change-password/change-password-form/index.js diff --git a/public/locales/fa/app.json b/public/locales/fa/app.json index dd72e71..885b641 100644 --- a/public/locales/fa/app.json +++ b/public/locales/fa/app.json @@ -79,7 +79,9 @@ "label_current_password": "رمز عبور فعلی", "label_new_password": "رمز عبور جدید", "label_confirm_password": "تکرار رمز عبور جدید", - "error_message_required": "اجباری !", + "error_message_current_password_required": "وارد کردن رمز عبور فعلی الزامیست!", + "error_message_new_password_required": "وارد کردن رمز عبور جدید الزامیست!", + "error_message_confirm_password_required": "وارد کردن تکرار رمز عبور جدید الزامیست!", "error_message_password_length": "رمز عبور باید حداقل 8 کاراکتر باشد.", "error_message_password_match": "پسورد ها یکسان هستند", "error_message_password_not_match": "پسورد و تکرار رمز عبور باید یکسان باشد" diff --git a/src/components/dashboard/change-password/change-password-form/__test__/index.test.js b/src/components/dashboard/change-password/change-password-form/__test__/index.test.js new file mode 100644 index 0000000..4a63240 --- /dev/null +++ b/src/components/dashboard/change-password/change-password-form/__test__/index.test.js @@ -0,0 +1,322 @@ +import { render, screen, waitFor, fireEvent , act } from '@testing-library/react'; +import MockAppWithProviders from "../../../../../../mocks/AppWithProvider"; +import ChangePasswordForm from "@/components/dashboard/change-password/change-password-form"; + +describe('Change Password Form Component From Change Password Page', () => { + describe('Rendering', () => { + it('Should see change password heading',() => { + render(); + const changePasswordElement = screen.getByRole('heading', { level: 4 }); + expect(changePasswordElement).toHaveTextContent('تغییر رمز عبور'); + }); + it('Should see the label for current password field', () => { + render(); + const currentPasswordLabel = screen.getByLabelText('رمز عبور فعلی'); + expect(currentPasswordLabel).toBeInTheDocument(); + }); + it('Should see the label for new password field', () => { + render(); + const newPasswordLabel = screen.getByLabelText('رمز عبور جدید'); + expect(newPasswordLabel).toBeInTheDocument(); + }); + it('Should see the label for confirm password field', () => { + render(); + const confirmPasswordLabel = screen.getByLabelText('تکرار رمز عبور جدید'); + expect(confirmPasswordLabel).toBeInTheDocument(); + }); + it('Should see the submit button with the submit label and should be disable', () => { + render(); + const submitButton = screen.getByRole('button', { name: 'ثبت' }); + expect(submitButton).toBeInTheDocument(); + expect(submitButton).toBeDisabled(); + }); + it('Should have empty input fields and not see any validation error for current password, new password, and confirm password', () => { + render(); + const currentPasswordInput = screen.getByLabelText('رمز عبور فعلی'); + const newPasswordInput = screen.getByLabelText('رمز عبور جدید'); + const confirmPasswordInput = screen.getByLabelText('تکرار رمز عبور جدید'); + + expect(currentPasswordInput).toHaveValue(''); + expect(newPasswordInput).toHaveValue(''); + expect(confirmPasswordInput).toHaveValue(''); + + expect(screen.queryByText("وارد کردن رمز عبور فعلی الزامیست!")).not.toBeInTheDocument(); + expect(screen.queryByText("وارد کردن رمز عبور جدید الزامیست!")).not.toBeInTheDocument(); + expect(screen.queryByText("وارد کردن تکرار رمز عبور جدید الزامیست!")).not.toBeInTheDocument(); + + }); + }); + describe("Behavior", ()=>{ + it('Should fill the current password field', async () => { + render(); + + const currentPasswordInput = screen.getByLabelText('رمز عبور فعلی'); + + // Simulate typing something in the current password input + fireEvent.change(currentPasswordInput, { target: { value: 'witel@fani0' } }); + + await waitFor(() => { + expect(screen.queryByText("وارد کردن رمز عبور فعلی الزامیست!")).not.toBeInTheDocument(); + expect(currentPasswordInput).toHaveValue('witel@fani0'); + }); + }); + it('Should fill the new password field', async () => { + render(); + + const newPasswordInput = screen.getByLabelText('رمز عبور جدید'); + + // Simulate typing something in the new password input + fireEvent.change(newPasswordInput, { target: { value: 'witel@fani1' } }); + + await waitFor(() => { + expect(screen.queryByText("وارد کردن رمز عبور جدید الزامیست!")).not.toBeInTheDocument(); + expect(newPasswordInput).toHaveValue('witel@fani1'); + }); + }); + it('Should fill the confirm new password field', async () => { + render(); + + const confirmPasswordInput = screen.getByLabelText('تکرار رمز عبور جدید'); + + // Simulate typing something in the confirmation new password input + fireEvent.change(confirmPasswordInput, { target: { value: 'witel@fani1' } }); + + await waitFor(() => { + expect(screen.queryByText("وارد کردن تکرار رمز عبور جدید الزامیست!")).not.toBeInTheDocument(); + expect(confirmPasswordInput).toHaveValue('witel@fani1'); + }); + }); + }); + describe("Validation", ()=>{ + it('Should see error while current password is empty on submit click or on blur event and not see any error while its not empty', async () => { + render(); + + // Simulate blur event on the current password input + const currentPasswordInput = screen.getByLabelText('رمز عبور فعلی'); + const submitButton = screen.getByRole('button', { name: 'ثبت' }); + + // Clear the input field + fireEvent.change(currentPasswordInput, { target: { value: '' } }); + + fireEvent.blur(currentPasswordInput); + + await waitFor(()=>{ + expect(screen.queryByText("وارد کردن رمز عبور فعلی الزامیست!")).toBeInTheDocument() + }); + + // Simulate clicking the submit button + fireEvent.click(submitButton); + + await waitFor(() => { + expect(screen.queryByText("وارد کردن رمز عبور فعلی الزامیست!")).toBeInTheDocument(); + }); + }); + it('Should see error while new password is empty on submit click or on blur event and not see any error while its not empty', async () => { + render(); + + // Simulate blur event on the new password input + const newPasswordInput = screen.getByLabelText('رمز عبور جدید'); + const submitButton = screen.getByRole('button', { name: 'ثبت' }); + + // Clear the input field + fireEvent.change(newPasswordInput, { target: { value: '' } }); + + fireEvent.blur(newPasswordInput); + + await waitFor(()=>{ + expect(screen.queryByText("وارد کردن رمز عبور جدید الزامیست!")).toBeInTheDocument() + }); + + // Simulate clicking the submit button + fireEvent.click(submitButton); + + await waitFor(() => { + expect(screen.queryByText("وارد کردن رمز عبور جدید الزامیست!")).toBeInTheDocument(); + }); + }); + it('Should see error while confirm new password is empty on submit click or on blur event and not see any error while its not empty', async () => { + render(); + + // Simulate blur event on the new password input + const confirmPasswordInput = screen.getByLabelText('تکرار رمز عبور جدید'); + const submitButton = screen.getByRole('button', { name: 'ثبت' }); + + // Clear the input field + fireEvent.change(confirmPasswordInput, { target: { value: '' } }); + + fireEvent.blur(confirmPasswordInput); + + await waitFor(()=>{ + expect(screen.queryByText("وارد کردن تکرار رمز عبور جدید الزامیست!")).toBeInTheDocument() + }); + + // Simulate clicking the submit button + fireEvent.click(submitButton); + + await waitFor(() => { + expect(screen.queryByText("وارد کردن تکرار رمز عبور جدید الزامیست!")).toBeInTheDocument(); + }); + }); + it('Should see error when new password is less than 8 characters', async () => { + render(); + + const newPasswordInput = screen.getByLabelText('رمز عبور جدید'); + const submitButton = screen.getByRole('button', { name: 'ثبت' }); + + // Simulate entering a short new password + fireEvent.change(newPasswordInput, { target: { value: 'test' } }); + fireEvent.blur(newPasswordInput); + + await waitFor(() => { + expect(screen.getByText("رمز عبور باید حداقل 8 کاراکتر باشد.")).toBeInTheDocument(); + }); + + // Simulate clicking the submit button + fireEvent.click(submitButton); + + await waitFor(() => { + expect(screen.getByText("رمز عبور باید حداقل 8 کاراکتر باشد.")).toBeInTheDocument(); + }); + }); + it('Should not see error when new password are 8 or more characters', async () => { + render(); + + const newPasswordInput = screen.getByLabelText('رمز عبور جدید'); + const submitButton = screen.getByRole('button', { name: 'ثبت' }); + + // Simulate entering a valid new password + fireEvent.change(newPasswordInput, { target: { value: 'Witel@fani1' } }); + fireEvent.blur(newPasswordInput); + + await waitFor(() => { + expect(screen.queryByText("رمز عبور باید حداقل 8 کاراکتر باشد.")).not.toBeInTheDocument(); + }); + + // Simulate clicking the submit button + fireEvent.click(submitButton); + + await waitFor(() => { + expect(screen.queryByText("رمز عبور باید حداقل 8 کاراکتر باشد.")).not.toBeInTheDocument(); + }); + }); + it('Should see error when new password and confirm password do not match on blur event and submit click', async () => { + render(); + + const newPasswordInput = screen.getByLabelText('رمز عبور جدید'); + const confirmPasswordInput = screen.getByLabelText('تکرار رمز عبور جدید'); + const submitButton = screen.getByRole('button', { name: 'ثبت' }); + + // Simulate entering a valid new password + fireEvent.change(newPasswordInput, { target: { value: 'Witel@fani1' } }); + fireEvent.blur(newPasswordInput); + + // Simulate entering a different value in the confirm password field + fireEvent.change(confirmPasswordInput, { target: { value: 'MismatchedPassword' } }); + fireEvent.blur(confirmPasswordInput); + + await waitFor(() => { + expect(screen.getByText("پسورد و تکرار رمز عبور باید یکسان باشد")).toBeInTheDocument(); + }); + + // Simulate clicking the submit button + fireEvent.click(submitButton); + + await waitFor(() => { + expect(screen.getByText("پسورد و تکرار رمز عبور باید یکسان باشد")).toBeInTheDocument(); + }); + }); + it('Should not see error when new password and confirm password match on blur event and submit click', async () => { + render(); + + const newPasswordInput = screen.getByLabelText('رمز عبور جدید'); + const confirmPasswordInput = screen.getByLabelText('تکرار رمز عبور جدید'); + const submitButton = screen.getByRole('button', { name: 'ثبت' }); + + // Simulate entering a valid new password + fireEvent.change(newPasswordInput, { target: { value: 'Witel@fani1' } }); + fireEvent.blur(newPasswordInput); + + // Simulate entering the same value in the confirmation password field + fireEvent.change(confirmPasswordInput, { target: { value: 'Witel@fani1' } }); + fireEvent.blur(confirmPasswordInput); + + await waitFor(() => { + expect(screen.queryByText("پسورد و تکرار رمز عبور باید یکسان باشد")).not.toBeInTheDocument(); + }); + + // Simulate clicking the submit button + fireEvent.click(submitButton); + + await waitFor(() => { + expect(screen.queryByText("پسورد و تکرار رمز عبور باید یکسان باشد")).not.toBeInTheDocument(); + }); + }); + }); + describe("Form Submission", () => { + it('Should enable the submit button when the inputs are valid', async () => { + render(); + + const submitButton = screen.getByRole('button', { name: 'ثبت' }); + + // Simulate entering valid values in the form fields + const currentPasswordInput = screen.getByLabelText('رمز عبور فعلی'); + const newPasswordInput = screen.getByLabelText('رمز عبور جدید'); + const confirmPasswordInput = screen.getByLabelText('تکرار رمز عبور جدید'); + + fireEvent.change(currentPasswordInput, { target: { value: 'Witel@fani0' } }); + fireEvent.change(newPasswordInput, { target: { value: 'Witel@fani1' } }); + fireEvent.change(confirmPasswordInput, { target: { value: 'Witel@fani1' } }); + + // Check if the submit button is enabled + await waitFor(() => { + expect(submitButton).not.toBeDisabled(); + }); + }); + // it('Should submit the form and reset it on success', async () => { + // server.use([rest.get(SET_USER_PASSWORD, (req, res, ctx) => { + // return res(ctx.status(200),) + // })]) + // render(); + // + // // Mock the requestServer function to simulate a successful API response + // const originalRequestServer = global.requestServer; + // global.requestServer = jest.fn().mockResolvedValue({}); + // + // const submitButton = screen.getByRole('button', { name: 'ثبت' }); + // + // // Simulate entering valid values in the form fields + // const currentPasswordInput = screen.getByLabelText('رمز عبور فعلی'); + // const newPasswordInput = screen.getByLabelText('رمز عبور جدید'); + // const confirmPasswordInput = screen.getByLabelText('تکرار رمز عبور جدید'); + // + // fireEvent.change(currentPasswordInput, { target: { value: 'Witel@fani0' } }); + // fireEvent.change(newPasswordInput, { target: { value: 'Witel@fani1' } }); + // fireEvent.change(confirmPasswordInput, { target: { value: 'Witel@fani1' } }); + // + // // Check if the submit button is enabled + // await waitFor(() => { + // expect(submitButton).not.toBeDisabled(); + // }); + // + // // Simulate form submission + // fireEvent.click(submitButton); + // + // // Wait for success message + // await waitFor(() => { + // // const successMessage = screen.getByText("Password changed successfully"); + // // expect(successMessage).toBeInTheDocument(); + // // Check if the form is reset + // + // }); + // + // expect(currentPasswordInput).toHaveValue(''); + // expect(newPasswordInput).toHaveValue(''); + // expect(confirmPasswordInput).toHaveValue(''); + // + // + // + // // Restore the original requestServer function + // global.requestServer = originalRequestServer; + // }); + }); +}) \ No newline at end of file diff --git a/src/components/dashboard/change-password/change-password-form/index.js b/src/components/dashboard/change-password/change-password-form/index.js new file mode 100644 index 0000000..9b7704a --- /dev/null +++ b/src/components/dashboard/change-password/change-password-form/index.js @@ -0,0 +1,137 @@ +import {Formik, Form} from "formik"; +import * as Yup from "yup"; +import {useTranslations} from "next-intl"; +import {Button, Paper, Stack, Typography} from "@mui/material"; +import PasswordField from "@/core/components/PasswordField"; +import StyledForm from "@/core/components/StyledForm"; +import useRequest from "@/lib/app/hooks/useRequest"; +import {SET_USER_PASSWORD} from "@/core/data/apiRoutes"; + +const ChangePasswordForm = ({onSubmit}) => { + const t = useTranslations(); + const requestServer = useRequest({auth: true}) + + const handleSubmit = (values, {setSubmitting, resetForm}) => { + requestServer(SET_USER_PASSWORD, 'post', { + data: { + current_password: values.current_password, + new_password: values.new_password, + new_password_confirmation: values.new_password_confirmation, + }, + }).then((response) => { + resetForm(); + }).catch(() => { + }).finally(() => { + setSubmitting(false); + }); + }; + const initialValues = { + current_password: "", + new_password: "", + new_password_confirmation: "", + }; + const validationSchema = Yup.object().shape({ + current_password: Yup.string().required( + t("ChangePassword.error_message_current_password_required") + ), + new_password: Yup.string() + .min(8, t("ChangePassword.error_message_password_length")) + .required(t("ChangePassword.error_message_new_password_required")), + new_password_confirmation: Yup.string() + .required(t("ChangePassword.error_message_confirm_password_required")) + .test( + t("ChangePassword.error_message_password_match"), + t("ChangePassword.error_message_password_not_match"), // Use the correct error message here + function (value) { + return this.parent.new_password === value; + } + ), + }); + + return ( + + {(props) => ( + { + e.preventDefault(); + props.handleSubmit(); + }} + > + + + + {t("ChangePassword.typography_change_password")} + + + + + + + + + + + + + + + )} + + ); +}; + +export default ChangePasswordForm; diff --git a/src/components/dashboard/change-password/index.jsx b/src/components/dashboard/change-password/index.jsx index ae74de6..b616274 100644 --- a/src/components/dashboard/change-password/index.jsx +++ b/src/components/dashboard/change-password/index.jsx @@ -1,146 +1,15 @@ import CenterLayout from "@/layouts/CenterLayout"; import DashboardLayouts from "@/layouts/dashboardLayouts"; -import {Button, Container, Paper, Stack, Typography,} from "@mui/material"; -import {Formik} from "formik"; -import * as Yup from "yup"; -import {useTranslations} from "next-intl"; -import PasswordField from "@/core/components/PasswordField"; -import StyledForm from "@/core/components/StyledForm"; -import {SET_USER_PASSWORD} from "@/core/data/apiRoutes"; -import SvgChangePassword from "@/core/components/svgs/SvgChangePassword"; -import useRequest from "@/lib/app/hooks/useRequest"; +import {Container} from "@mui/material"; +import ChangePasswordForm from "@/components/dashboard/change-password/change-password-form"; const DashboardChangePasswordComponent = () => { - const t = useTranslations(); - const requestServer = useRequest({auth: true}) - - const handleSubmit = (values, {setSubmitting, resetForm}) => { - requestServer(SET_USER_PASSWORD, 'post', { - data: { - current_password: values.current_password, - new_password: values.new_password, - new_password_confirmation: values.new_password_confirmation, - }, - }).then((response) => { - resetForm(); - }).catch(() => { - }).finally(() => { - setSubmitting(false); - }); - }; - const initialValues = { - current_password: "", - new_password: "", - new_password_confirmation: "", - }; - const validationSchema = Yup.object().shape({ - current_password: Yup.string().required( - t("ChangePassword.error_message_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")} - - - - - - - - - - - - - - - )} - + From f7d2341483e8c894018f00efd11511e355da53b8 Mon Sep 17 00:00:00 2001 From: Amin Ghasempoor Date: Mon, 2 Oct 2023 15:56:26 +0330 Subject: [PATCH 02/37] CFE-4 role management page component --- mocks/handler.js | 33 ++++- public/locales/fa/app.json | 35 +++++ .../Form/CreateForm/CreateContent.jsx | 139 ++++++++++++++++++ .../CreateForm/__test__/CreateContent.test.js | 83 +++++++++++ .../Form/CreateForm/__test__/index.test.js | 30 ++++ .../role-management/Form/CreateForm/index.jsx | 34 +++++ .../role-management/Form/DeleteForm.jsx | 67 +++++++++ .../Form/UpdateForm/UpdateContext.jsx | 138 +++++++++++++++++ .../role-management/Form/UpdateForm/index.jsx | 35 +++++ .../RoleManagementComponent.jsx | 94 ++++++++++++ .../role-management/TableRowActions.jsx | 24 +++ .../role-management/TableToolbar.jsx | 11 ++ .../__test__/RoleManagementComponent.test.js | 17 +++ .../dashboard/role-management/index.jsx | 13 ++ src/core/data/apiRoutes.js | 16 +- src/core/data/sidebarMenu.jsx | 9 ++ src/lib/app/hooks/usePermissions.jsx | 25 ++++ src/pages/dashboard/role-management/index.jsx | 26 ++++ 18 files changed, 822 insertions(+), 7 deletions(-) create mode 100644 src/components/dashboard/role-management/Form/CreateForm/CreateContent.jsx create mode 100644 src/components/dashboard/role-management/Form/CreateForm/__test__/CreateContent.test.js create mode 100644 src/components/dashboard/role-management/Form/CreateForm/__test__/index.test.js 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/UpdateContext.jsx create mode 100644 src/components/dashboard/role-management/Form/UpdateForm/index.jsx create mode 100644 src/components/dashboard/role-management/RoleManagementComponent.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/__test__/RoleManagementComponent.test.js 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/mocks/handler.js b/mocks/handler.js index 49e0f99..5e67894 100644 --- a/mocks/handler.js +++ b/mocks/handler.js @@ -1,8 +1,29 @@ -import {GET_USER_ROUTE} from "@/core/data/apiRoutes"; +import {GET_PERMISSIONS_LIST, GET_USER_ROUTE} from "@/core/data/apiRoutes"; import {rest} from "msw"; -export const handler = [rest.get(GET_USER_ROUTE, (req, res, ctx) => { - return res(ctx.json({ - id: 10 - })) -})] \ No newline at end of file +export const handler = [ + rest.get(GET_USER_ROUTE, (req, res, ctx) => { + return res(ctx.json({ + id: 10 + })) + }), + rest.get(GET_PERMISSIONS_LIST, (req, res, ctx) => { + return res(ctx.json( + { + data:[ + { + id: 1, + name: "manage_passenger_office_navgan", + name_fa: "مدیریت کارتابل رییس اداره مسافری استان" + }, + { + id: 2, + name: "manage_province_working_group_navgan", + name_fa:"مدیریت کارتابل کارگروه استانی" + } + ] + } + )) + }), + +] \ No newline at end of file diff --git a/public/locales/fa/app.json b/public/locales/fa/app.json index e813849..39531c9 100644 --- a/public/locales/fa/app.json +++ b/public/locales/fa/app.json @@ -32,6 +32,7 @@ "dashboard": "داشبورد", "change-password": "تغییر رمز عبور", "edit-profile": "ویرایش پروفایل", + "role-management": "مدیریت دسترسی", "admin": "مدیریت" }, "secondary": { @@ -118,5 +119,39 @@ "upload_file_format": "فرمت قابل قبول : png,jpg,pdf", "delete": "پاک کردن", "uploadfile_error": "حجم فایل بیشتر از 2 مگابایت می باشد" + }, + "RoleManagement": { + "id": "کد یکتا", + "name_fa": "نام فارسی ", + "name": "نام انگلیسی", + "created_at": "تاریخ درخواست", + "updated_at": "تاریخ بروزرسانی" + }, + "AddDialog": { + "add": "افزودن", + "name_en": "نام انگلیسی", + "name_en_error": "وارد کردن نام انگلیسی الزامیست", + "name_fa": "نام فارسی", + "name_fa_error": "وارد کردن نام فارسی الزامیست", + "permission": "دسترسی", + "permission_min_error": "حداقل باید یک دسترسی انتخاب شود", + "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/role-management/Form/CreateForm/CreateContent.jsx b/src/components/dashboard/role-management/Form/CreateForm/CreateContent.jsx new file mode 100644 index 0000000..117e009 --- /dev/null +++ b/src/components/dashboard/role-management/Form/CreateForm/CreateContent.jsx @@ -0,0 +1,139 @@ +import { + Button, + Checkbox, + DialogActions, + DialogContent, DialogTitle, + 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_en: Yup.string().required(t("AddDialog.name_en_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_en: "", + name_fa: "", + permissions: [], + }, validationSchema, onSubmit: (values, {setSubmitting}) => { + const formData = new FormData(); + formData.append("name_en", values.name_en); + 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.add")} + + + + + + + + {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/__test__/CreateContent.test.js b/src/components/dashboard/role-management/Form/CreateForm/__test__/CreateContent.test.js new file mode 100644 index 0000000..cd690d2 --- /dev/null +++ b/src/components/dashboard/role-management/Form/CreateForm/__test__/CreateContent.test.js @@ -0,0 +1,83 @@ +import {act, fireEvent, render, screen} from "@testing-library/react"; +import MockAppWithProviders from "../../../../../../../mocks/AppWithProvider"; +import CreateContent from "@/components/dashboard/role-management/Form/CreateForm/CreateContent"; + +describe("Create Content component from Create Form Component in Role Management Component",()=>{ + describe("Rendering", ()=>{ + it('should see AddDialog text in the top ', () => { + render( + + + + ) + const textElement = screen.queryByText("افزودن") + expect(textElement).toBeInTheDocument() + }); + it('should see name_en text', () => { + render( + + + + ) + const textElement = screen.queryByLabelText("نام انگلیسی") + expect(textElement).toBeInTheDocument() + }); + it('should see name_fa text', () => { + render( + + + + ) + const textElement = screen.queryByLabelText("نام فارسی") + expect(textElement).toBeInTheDocument() + }); + }) + + describe("Behavior",()=>{ + it('should see what fill in the name_en input', async () => { + render( + + + + ) + const nameEnglishInput = screen.getByLabelText('نام انگلیسی'); + + fireEvent.change(nameEnglishInput, {target: {value: 'testuser'}}); + await act(()=>{ + // Simulate user input + expect(nameEnglishInput).toHaveValue('testuser'); + }) + }); + it('should see what fill in the name_fa input', async () => { + localStorage.setItem("_token", "token") + render( + + + + ) + const nameFarsiInput = screen.getByLabelText('نام فارسی'); + + fireEvent.change(nameFarsiInput, {target: {value: 'testuser'}}); + await act(()=>{ + // Simulate user input + expect(nameFarsiInput).toHaveValue('testuser'); + }) + }); + + + it('should return permissions_list', async () => { + localStorage.setItem("_token", "token") + render( + + + + ) + const checkboxElement = await screen.findAllByTestId("PermissionList-checkbox") + expect(checkboxElement).toHaveLength(2) + }); + + it('should ', () => { + + }); + }) +}) \ No newline at end of file diff --git a/src/components/dashboard/role-management/Form/CreateForm/__test__/index.test.js b/src/components/dashboard/role-management/Form/CreateForm/__test__/index.test.js new file mode 100644 index 0000000..b4f0e82 --- /dev/null +++ b/src/components/dashboard/role-management/Form/CreateForm/__test__/index.test.js @@ -0,0 +1,30 @@ +import {fireEvent, queryByText, render, screen} from "@testing-library/react"; +import CreateForm from "@/components/dashboard/role-management/Form/CreateForm"; +import MockAppWithProviders from "../../../../../../../mocks/AppWithProvider"; + +describe("Create Form Component from Role Management Component",()=>{ + describe("Rendering",()=>{ + + it('should see Dialog text', () => { + render( + + + + ) + const textElement = screen.queryByText("افزودن") + expect(textElement).toBeInTheDocument() + }); + + it('should see Tooltip text when mouse over', () => { + render( + + + + ) + const buttonElement = screen.getByText("افزودن"); + fireEvent.mouseOver(buttonElement); + const tooltipTextElement = screen.getByText("افزودن"); + expect(tooltipTextElement).toBeInTheDocument(); + }); + }) +}) \ 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..8cbe859 --- /dev/null +++ b/src/components/dashboard/role-management/Form/CreateForm/index.jsx @@ -0,0 +1,34 @@ +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 ( + + + + + + + + + ) +} +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/UpdateContext.jsx b/src/components/dashboard/role-management/Form/UpdateForm/UpdateContext.jsx new file mode 100644 index 0000000..e2f2980 --- /dev/null +++ b/src/components/dashboard/role-management/Form/UpdateForm/UpdateContext.jsx @@ -0,0 +1,138 @@ +import { + Button, + Checkbox, DialogActions, + DialogContent, + FormControl, + FormControlLabel, + FormHelperText, + FormLabel, + Grid, + Stack, + TextField +} from "@mui/material"; +import {useFormik} from "formik"; +import {UPDATE_ROLE_MANAGEMENT} from "@/core/data/apiRoutes"; +import * as Yup from "yup"; +import usePermissions from "@/lib/app/hooks/usePermissions"; +import useNotification from "@/lib/app/hooks/useNotification"; +import useRequest from "@/lib/app/hooks/useRequest"; +import {useTranslations} from "next-intl"; +import {useState} from "react"; + +const UpdateContent = ({mutate, fetchUrl, setOpenConfirmDialog}) => { + const t = useTranslations(); + const requestServer = useRequest({auth: true}) + const {update_notification} = useNotification() + const {permissions_list} = usePermissions() + const [openConfirmDialog, setOpenConfirmDialog] = useState(false); + + 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( + <> + + + + + + + + {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 UpdateContent \ No newline at end of file diff --git a/src/components/dashboard/role-management/Form/UpdateForm/index.jsx b/src/components/dashboard/role-management/Form/UpdateForm/index.jsx new file mode 100644 index 0000000..115e284 --- /dev/null +++ b/src/components/dashboard/role-management/Form/UpdateForm/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 UpdateForm = ({mutate, fetchUrl}) => { + const t = useTranslations(); + const [openConfirmDialog, setOpenConfirmDialog] = useState(false); + + return ( + + + + + + {t("UpdateDialog.update ")} + + + + ) +} +export default UpdateForm \ No newline at end of file diff --git a/src/components/dashboard/role-management/RoleManagementComponent.jsx b/src/components/dashboard/role-management/RoleManagementComponent.jsx new file mode 100644 index 0000000..1aea1b7 --- /dev/null +++ b/src/components/dashboard/role-management/RoleManagementComponent.jsx @@ -0,0 +1,94 @@ +import DataTable from "@/core/components/DataTable"; +import {GET_ROLE_MANAGEMENT} from "@/core/data/apiRoutes"; +import TableToolbar from "@/components/dashboard/role-management/TableToolbar"; +import TableRowActions from "@/components/dashboard/role-management/TableRowActions"; +import {Box, Typography} from "@mui/material"; +import {useTranslations} from "next-intl"; +import {useMemo} from "react"; +import moment from "jalali-moment"; +import MuiDatePicker from "@/core/components/MuiDatePicker"; + +const RoleManagementComponent = () => { + 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 RoleManagementComponent \ 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..cd72356 --- /dev/null +++ b/src/components/dashboard/role-management/TableRowActions.jsx @@ -0,0 +1,24 @@ +import {Box} from "@mui/material"; +import DeleteForm from "./Form/DeleteForm" +import UpdateForm from "@/components/dashboard/role-management/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/__test__/RoleManagementComponent.test.js b/src/components/dashboard/role-management/__test__/RoleManagementComponent.test.js new file mode 100644 index 0000000..d21081c --- /dev/null +++ b/src/components/dashboard/role-management/__test__/RoleManagementComponent.test.js @@ -0,0 +1,17 @@ +import {render} from "@testing-library/react"; +import MockAppWithProviders from "../../../../../mocks/AppWithProvider"; +import RoleManagementComponent from "@/components/dashboard/role-management/RoleManagementComponent"; + +describe("Role Management Component from Role Management page",()=>{ + describe("Rendering",()=>{ + it('should show columns text', () => { + render(( + + + + )) + const textElement = screen.getByText("کد یکتا") + expect(textElement).toBeInTheDocument() + }); + }) +}) \ No newline at end of file diff --git a/src/components/dashboard/role-management/index.jsx b/src/components/dashboard/role-management/index.jsx new file mode 100644 index 0000000..09f15bf --- /dev/null +++ b/src/components/dashboard/role-management/index.jsx @@ -0,0 +1,13 @@ +import DashboardLayouts from "@/layouts/dashboardLayouts"; +import RoleManagementComponent from "@/components/dashboard/role-management/RoleManagementComponent"; + +function DashboardRoleManagementComponent() { + + return ( + + + + ); +} + +export default DashboardRoleManagementComponent; diff --git a/src/core/data/apiRoutes.js b/src/core/data/apiRoutes.js index 06dd4e9..31b0f8e 100644 --- a/src/core/data/apiRoutes.js +++ b/src/core/data/apiRoutes.js @@ -10,4 +10,18 @@ export const SET_USER_PASSWORD = BASE_URL + "/dashboard/profile/change_password" //user data export const GET_USER_ROUTE = BASE_URL + "/dashboard/profile/info"; -//user data \ No newline at end of file +//user data + +// role management +export const GET_ROLE_MANAGEMENT = + BASE_URL + "/api/roles" +export const ADD_ROLE_MANAGEMENT = + BASE_URL + "/api/roles" +export const UPDATE_ROLE_MANAGEMENT = + BASE_URL + "/api/roles" +export const DELETE_ROLE_MANAGEMENT = + BASE_URL + "/api/roles" + +export const GET_PERMISSIONS_LIST = + BASE_URL + "/api/roles/list" +//role management \ No newline at end of file diff --git a/src/core/data/sidebarMenu.jsx b/src/core/data/sidebarMenu.jsx index ca946a5..dfc7df7 100644 --- a/src/core/data/sidebarMenu.jsx +++ b/src/core/data/sidebarMenu.jsx @@ -10,6 +10,15 @@ const sidebarMenu = [ selected: false, permission: "all", }, + { + key: "sidebar.role-management", + name : "role_management", + type: "page", + route: "/dashboard/role-management", + icon: , + selected: false, + permission: "all", + }, ], ]; 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, + }, + }; +} From f507f38a00d61407ba6b0143cef202263b7e06fc Mon Sep 17 00:00:00 2001 From: Amin Ghasempoor Date: Mon, 2 Oct 2023 16:26:24 +0330 Subject: [PATCH 03/37] CFE-4 role management component before git pull --- .../Form/CreateForm/__test__/CreateContent.test.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/components/dashboard/role-management/Form/CreateForm/__test__/CreateContent.test.js b/src/components/dashboard/role-management/Form/CreateForm/__test__/CreateContent.test.js index cd690d2..1742b12 100644 --- a/src/components/dashboard/role-management/Form/CreateForm/__test__/CreateContent.test.js +++ b/src/components/dashboard/role-management/Form/CreateForm/__test__/CreateContent.test.js @@ -66,7 +66,6 @@ describe("Create Content component from Create Form Component in Role Management it('should return permissions_list', async () => { - localStorage.setItem("_token", "token") render( @@ -76,7 +75,7 @@ describe("Create Content component from Create Form Component in Role Management expect(checkboxElement).toHaveLength(2) }); - it('should ', () => { + it('should get checked if permission list item get clicked', () => { }); }) From 7902e92df2270bd618aba279f78948dd2b495b49 Mon Sep 17 00:00:00 2001 From: Yasiu1376 Date: Sat, 7 Oct 2023 11:39:43 +0330 Subject: [PATCH 04/37] CFE-12 complete change password test and mock for handle api --- mocks/handler.js | 21 +- .../__test__/index.test.js | 185 ++++++++---------- src/core/data/apiRoutes.js | 2 +- 3 files changed, 100 insertions(+), 108 deletions(-) diff --git a/mocks/handler.js b/mocks/handler.js index 49e0f99..65f5398 100644 --- a/mocks/handler.js +++ b/mocks/handler.js @@ -1,8 +1,17 @@ -import {GET_USER_ROUTE} from "@/core/data/apiRoutes"; +import {GET_USER_ROUTE, SET_USER_PASSWORD} from "@/core/data/apiRoutes"; import {rest} from "msw"; -export const handler = [rest.get(GET_USER_ROUTE, (req, res, ctx) => { - return res(ctx.json({ - id: 10 - })) -})] \ No newline at end of file +export const handler = [ + rest.get(GET_USER_ROUTE, (req, res, ctx) => { + return res(ctx.json({ + data:{ + id: 10 + } + })) + }), + rest.post(SET_USER_PASSWORD, (req, res, ctx) => { + return res( + ctx.status(200), + ); + }), +] \ No newline at end of file diff --git a/src/components/dashboard/change-password/change-password-form/__test__/index.test.js b/src/components/dashboard/change-password/change-password-form/__test__/index.test.js index 4a63240..503b571 100644 --- a/src/components/dashboard/change-password/change-password-form/__test__/index.test.js +++ b/src/components/dashboard/change-password/change-password-form/__test__/index.test.js @@ -1,6 +1,10 @@ import { render, screen, waitFor, fireEvent , act } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; import MockAppWithProviders from "../../../../../../mocks/AppWithProvider"; import ChangePasswordForm from "@/components/dashboard/change-password/change-password-form"; +import {SET_USER_PASSWORD} from "@/core/data/apiRoutes"; +import {server} from "../../../../../../mocks/server"; +import {rest} from "msw"; describe('Change Password Form Component From Change Password Page', () => { describe('Rendering', () => { @@ -11,17 +15,17 @@ describe('Change Password Form Component From Change Password Page', () => { }); it('Should see the label for current password field', () => { render(); - const currentPasswordLabel = screen.getByLabelText('رمز عبور فعلی'); + const currentPasswordLabel = screen.queryByLabelText('رمز عبور فعلی'); expect(currentPasswordLabel).toBeInTheDocument(); }); it('Should see the label for new password field', () => { render(); - const newPasswordLabel = screen.getByLabelText('رمز عبور جدید'); + const newPasswordLabel = screen.queryByLabelText('رمز عبور جدید'); expect(newPasswordLabel).toBeInTheDocument(); }); it('Should see the label for confirm password field', () => { render(); - const confirmPasswordLabel = screen.getByLabelText('تکرار رمز عبور جدید'); + const confirmPasswordLabel = screen.queryByLabelText('تکرار رمز عبور جدید'); expect(confirmPasswordLabel).toBeInTheDocument(); }); it('Should see the submit button with the submit label and should be disable', () => { @@ -32,9 +36,9 @@ describe('Change Password Form Component From Change Password Page', () => { }); it('Should have empty input fields and not see any validation error for current password, new password, and confirm password', () => { render(); - const currentPasswordInput = screen.getByLabelText('رمز عبور فعلی'); - const newPasswordInput = screen.getByLabelText('رمز عبور جدید'); - const confirmPasswordInput = screen.getByLabelText('تکرار رمز عبور جدید'); + const currentPasswordInput = screen.queryByLabelText('رمز عبور فعلی'); + const newPasswordInput = screen.queryByLabelText('رمز عبور جدید'); + const confirmPasswordInput = screen.queryByLabelText('تکرار رمز عبور جدید'); expect(currentPasswordInput).toHaveValue(''); expect(newPasswordInput).toHaveValue(''); @@ -47,10 +51,10 @@ describe('Change Password Form Component From Change Password Page', () => { }); }); describe("Behavior", ()=>{ - it('Should fill the current password field', async () => { + it('Should fill the current password field and not see any error while its not empty', async () => { render(); - const currentPasswordInput = screen.getByLabelText('رمز عبور فعلی'); + const currentPasswordInput = screen.queryByLabelText('رمز عبور فعلی'); // Simulate typing something in the current password input fireEvent.change(currentPasswordInput, { target: { value: 'witel@fani0' } }); @@ -60,10 +64,10 @@ describe('Change Password Form Component From Change Password Page', () => { expect(currentPasswordInput).toHaveValue('witel@fani0'); }); }); - it('Should fill the new password field', async () => { + it('Should fill the new password field and not see any error while its not empty', async () => { render(); - const newPasswordInput = screen.getByLabelText('رمز عبور جدید'); + const newPasswordInput = screen.queryByLabelText('رمز عبور جدید'); // Simulate typing something in the new password input fireEvent.change(newPasswordInput, { target: { value: 'witel@fani1' } }); @@ -73,10 +77,10 @@ describe('Change Password Form Component From Change Password Page', () => { expect(newPasswordInput).toHaveValue('witel@fani1'); }); }); - it('Should fill the confirm new password field', async () => { + it('Should fill the confirm new password field and not see any error while its not empty', async () => { render(); - const confirmPasswordInput = screen.getByLabelText('تکرار رمز عبور جدید'); + const confirmPasswordInput = screen.queryByLabelText('تکرار رمز عبور جدید'); // Simulate typing something in the confirmation new password input fireEvent.change(confirmPasswordInput, { target: { value: 'witel@fani1' } }); @@ -88,12 +92,11 @@ describe('Change Password Form Component From Change Password Page', () => { }); }); describe("Validation", ()=>{ - it('Should see error while current password is empty on submit click or on blur event and not see any error while its not empty', async () => { + it('Should see error while current password is empty on blur event', async () => { render(); // Simulate blur event on the current password input - const currentPasswordInput = screen.getByLabelText('رمز عبور فعلی'); - const submitButton = screen.getByRole('button', { name: 'ثبت' }); + const currentPasswordInput = screen.queryByLabelText('رمز عبور فعلی'); // Clear the input field fireEvent.change(currentPasswordInput, { target: { value: '' } }); @@ -103,20 +106,12 @@ describe('Change Password Form Component From Change Password Page', () => { await waitFor(()=>{ expect(screen.queryByText("وارد کردن رمز عبور فعلی الزامیست!")).toBeInTheDocument() }); - - // Simulate clicking the submit button - fireEvent.click(submitButton); - - await waitFor(() => { - expect(screen.queryByText("وارد کردن رمز عبور فعلی الزامیست!")).toBeInTheDocument(); - }); }); - it('Should see error while new password is empty on submit click or on blur event and not see any error while its not empty', async () => { + it('Should see error while new password is empty on blur event', async () => { render(); // Simulate blur event on the new password input - const newPasswordInput = screen.getByLabelText('رمز عبور جدید'); - const submitButton = screen.getByRole('button', { name: 'ثبت' }); + const newPasswordInput = screen.queryByLabelText('رمز عبور جدید'); // Clear the input field fireEvent.change(newPasswordInput, { target: { value: '' } }); @@ -126,20 +121,12 @@ describe('Change Password Form Component From Change Password Page', () => { await waitFor(()=>{ expect(screen.queryByText("وارد کردن رمز عبور جدید الزامیست!")).toBeInTheDocument() }); - - // Simulate clicking the submit button - fireEvent.click(submitButton); - - await waitFor(() => { - expect(screen.queryByText("وارد کردن رمز عبور جدید الزامیست!")).toBeInTheDocument(); - }); }); - it('Should see error while confirm new password is empty on submit click or on blur event and not see any error while its not empty', async () => { + it('Should see error while confirm new password is empty on blur event', async () => { render(); // Simulate blur event on the new password input - const confirmPasswordInput = screen.getByLabelText('تکرار رمز عبور جدید'); - const submitButton = screen.getByRole('button', { name: 'ثبت' }); + const confirmPasswordInput = screen.queryByLabelText('تکرار رمز عبور جدید'); // Clear the input field fireEvent.change(confirmPasswordInput, { target: { value: '' } }); @@ -149,18 +136,11 @@ describe('Change Password Form Component From Change Password Page', () => { await waitFor(()=>{ expect(screen.queryByText("وارد کردن تکرار رمز عبور جدید الزامیست!")).toBeInTheDocument() }); - - // Simulate clicking the submit button - fireEvent.click(submitButton); - - await waitFor(() => { - expect(screen.queryByText("وارد کردن تکرار رمز عبور جدید الزامیست!")).toBeInTheDocument(); - }); }); it('Should see error when new password is less than 8 characters', async () => { render(); - const newPasswordInput = screen.getByLabelText('رمز عبور جدید'); + const newPasswordInput = screen.queryByLabelText('رمز عبور جدید'); const submitButton = screen.getByRole('button', { name: 'ثبت' }); // Simulate entering a short new password @@ -168,20 +148,20 @@ describe('Change Password Form Component From Change Password Page', () => { fireEvent.blur(newPasswordInput); await waitFor(() => { - expect(screen.getByText("رمز عبور باید حداقل 8 کاراکتر باشد.")).toBeInTheDocument(); + expect(screen.queryByText("رمز عبور باید حداقل 8 کاراکتر باشد.")).toBeInTheDocument(); }); // Simulate clicking the submit button fireEvent.click(submitButton); await waitFor(() => { - expect(screen.getByText("رمز عبور باید حداقل 8 کاراکتر باشد.")).toBeInTheDocument(); + expect(screen.queryByText("رمز عبور باید حداقل 8 کاراکتر باشد.")).toBeInTheDocument(); }); }); it('Should not see error when new password are 8 or more characters', async () => { render(); - const newPasswordInput = screen.getByLabelText('رمز عبور جدید'); + const newPasswordInput = screen.queryByLabelText('رمز عبور جدید'); const submitButton = screen.getByRole('button', { name: 'ثبت' }); // Simulate entering a valid new password @@ -202,8 +182,8 @@ describe('Change Password Form Component From Change Password Page', () => { it('Should see error when new password and confirm password do not match on blur event and submit click', async () => { render(); - const newPasswordInput = screen.getByLabelText('رمز عبور جدید'); - const confirmPasswordInput = screen.getByLabelText('تکرار رمز عبور جدید'); + const newPasswordInput = screen.queryByLabelText('رمز عبور جدید'); + const confirmPasswordInput = screen.queryByLabelText('تکرار رمز عبور جدید'); const submitButton = screen.getByRole('button', { name: 'ثبت' }); // Simulate entering a valid new password @@ -215,21 +195,21 @@ describe('Change Password Form Component From Change Password Page', () => { fireEvent.blur(confirmPasswordInput); await waitFor(() => { - expect(screen.getByText("پسورد و تکرار رمز عبور باید یکسان باشد")).toBeInTheDocument(); + expect(screen.queryByText("پسورد و تکرار رمز عبور باید یکسان باشد")).toBeInTheDocument(); }); // Simulate clicking the submit button fireEvent.click(submitButton); await waitFor(() => { - expect(screen.getByText("پسورد و تکرار رمز عبور باید یکسان باشد")).toBeInTheDocument(); + expect(screen.queryByText("پسورد و تکرار رمز عبور باید یکسان باشد")).toBeInTheDocument(); }); }); it('Should not see error when new password and confirm password match on blur event and submit click', async () => { render(); - const newPasswordInput = screen.getByLabelText('رمز عبور جدید'); - const confirmPasswordInput = screen.getByLabelText('تکرار رمز عبور جدید'); + const newPasswordInput = screen.queryByLabelText('رمز عبور جدید'); + const confirmPasswordInput = screen.queryByLabelText('تکرار رمز عبور جدید'); const submitButton = screen.getByRole('button', { name: 'ثبت' }); // Simulate entering a valid new password @@ -259,9 +239,9 @@ describe('Change Password Form Component From Change Password Page', () => { const submitButton = screen.getByRole('button', { name: 'ثبت' }); // Simulate entering valid values in the form fields - const currentPasswordInput = screen.getByLabelText('رمز عبور فعلی'); - const newPasswordInput = screen.getByLabelText('رمز عبور جدید'); - const confirmPasswordInput = screen.getByLabelText('تکرار رمز عبور جدید'); + const currentPasswordInput = screen.queryByLabelText('رمز عبور فعلی'); + const newPasswordInput = screen.queryByLabelText('رمز عبور جدید'); + const confirmPasswordInput = screen.queryByLabelText('تکرار رمز عبور جدید'); fireEvent.change(currentPasswordInput, { target: { value: 'Witel@fani0' } }); fireEvent.change(newPasswordInput, { target: { value: 'Witel@fani1' } }); @@ -272,51 +252,54 @@ describe('Change Password Form Component From Change Password Page', () => { expect(submitButton).not.toBeDisabled(); }); }); - // it('Should submit the form and reset it on success', async () => { - // server.use([rest.get(SET_USER_PASSWORD, (req, res, ctx) => { - // return res(ctx.status(200),) - // })]) - // render(); - // - // // Mock the requestServer function to simulate a successful API response - // const originalRequestServer = global.requestServer; - // global.requestServer = jest.fn().mockResolvedValue({}); - // - // const submitButton = screen.getByRole('button', { name: 'ثبت' }); - // - // // Simulate entering valid values in the form fields - // const currentPasswordInput = screen.getByLabelText('رمز عبور فعلی'); - // const newPasswordInput = screen.getByLabelText('رمز عبور جدید'); - // const confirmPasswordInput = screen.getByLabelText('تکرار رمز عبور جدید'); - // - // fireEvent.change(currentPasswordInput, { target: { value: 'Witel@fani0' } }); - // fireEvent.change(newPasswordInput, { target: { value: 'Witel@fani1' } }); - // fireEvent.change(confirmPasswordInput, { target: { value: 'Witel@fani1' } }); - // - // // Check if the submit button is enabled - // await waitFor(() => { - // expect(submitButton).not.toBeDisabled(); - // }); - // - // // Simulate form submission - // fireEvent.click(submitButton); - // - // // Wait for success message - // await waitFor(() => { - // // const successMessage = screen.getByText("Password changed successfully"); - // // expect(successMessage).toBeInTheDocument(); - // // Check if the form is reset - // - // }); - // - // expect(currentPasswordInput).toHaveValue(''); - // expect(newPasswordInput).toHaveValue(''); - // expect(confirmPasswordInput).toHaveValue(''); - // - // - // - // // Restore the original requestServer function - // global.requestServer = originalRequestServer; - // }); + it('Should request to api and resetform in success response', async () => { + // Render the component + render(); + + // Fill in the form fields + const currentPasswordInput = screen.queryByLabelText('رمز عبور فعلی'); + const newPasswordInput = screen.queryByLabelText('رمز عبور جدید'); + const confirmPasswordInput = screen.queryByLabelText('تکرار رمز عبور جدید'); + + fireEvent.change(currentPasswordInput, { target: { value: 'Witel@fani0' } }); + fireEvent.change(newPasswordInput, { target: { value: 'Witel@fani1' } }); + fireEvent.change(confirmPasswordInput, { target: { value: 'Witel@fani1' } }); + + // Submit the form + const submitButton = screen.getByRole('button', { name: 'ثبت' }); + await userEvent.click(submitButton); + + await waitFor(() => { + expect(currentPasswordInput).toHaveValue(''); + expect(newPasswordInput).toHaveValue(''); + expect(confirmPasswordInput).toHaveValue(''); + }); + }); + it('Should request to api and not resetform in error response', async () => { + // Render the component + render(); + server.use([rest.get(SET_USER_PASSWORD, (req, res, ctx) => { + return res(ctx.status(422)) + })]) + + // Fill in the form fields wrong + const currentPasswordInput = screen.queryByLabelText('رمز عبور فعلی'); + const newPasswordInput = screen.queryByLabelText('رمز عبور جدید'); + const confirmPasswordInput = screen.queryByLabelText('تکرار رمز عبور جدید'); + + fireEvent.change(currentPasswordInput, { target: { value: 'incorrect' } }); + fireEvent.change(newPasswordInput, { target: { value: 'Witel@fani1' } }); + fireEvent.change(confirmPasswordInput, { target: { value: 'Witel@fani1' } }); + + // Submit the form + const submitButton = screen.getByRole('button', { name: 'ثبت' }); + await userEvent.click(submitButton); + + await waitFor(() => { + expect(currentPasswordInput.value).toBe('incorrect'); + expect(newPasswordInput.value).toBe('Witel@fani1'); + expect(confirmPasswordInput.value).toBe('Witel@fani1'); + }); + }); }); }) \ No newline at end of file diff --git a/src/core/data/apiRoutes.js b/src/core/data/apiRoutes.js index 06dd4e9..4d2d8c0 100644 --- a/src/core/data/apiRoutes.js +++ b/src/core/data/apiRoutes.js @@ -5,7 +5,7 @@ export const GET_USER_TOKEN = BASE_URL + "/dashboard/login"; //end login //change password -export const SET_USER_PASSWORD = BASE_URL + "/dashboard/profile/change_password"; +export const SET_USER_PASSWORD = BASE_URL + "/api/profile/change_password"; //end change password //user data From 84ce95b2723a0fea5c90bd4ddb88c768f256d6b7 Mon Sep 17 00:00:00 2001 From: Yasiu1376 Date: Sat, 7 Oct 2023 13:47:57 +0330 Subject: [PATCH 05/37] CFE-23 fix call DashboardLayout in change password form --- src/components/dashboard/change-password/index.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/dashboard/change-password/index.jsx b/src/components/dashboard/change-password/index.jsx index b616274..771915b 100644 --- a/src/components/dashboard/change-password/index.jsx +++ b/src/components/dashboard/change-password/index.jsx @@ -1,5 +1,5 @@ import CenterLayout from "@/layouts/CenterLayout"; -import DashboardLayouts from "@/layouts/dashboardLayouts"; +import DashboardLayouts from "@/layouts/DashboardLayout"; import {Container} from "@mui/material"; import ChangePasswordForm from "@/components/dashboard/change-password/change-password-form"; From 96e8d9220894bd73f6c85d588fd4883cf6a49114 Mon Sep 17 00:00:00 2001 From: Yasiu1376 Date: Sat, 7 Oct 2023 14:01:38 +0330 Subject: [PATCH 06/37] CFE-23 change name of DashboardLayouts to DashboardLayout --- src/components/dashboard/change-password/index.jsx | 6 +++--- src/components/dashboard/edit-profile/index.jsx | 6 +++--- src/components/dashboard/first/index.jsx | 4 ++-- src/layouts/DashboardLayout.jsx | 4 ++-- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/components/dashboard/change-password/index.jsx b/src/components/dashboard/change-password/index.jsx index 771915b..74896a6 100644 --- a/src/components/dashboard/change-password/index.jsx +++ b/src/components/dashboard/change-password/index.jsx @@ -1,18 +1,18 @@ import CenterLayout from "@/layouts/CenterLayout"; -import DashboardLayouts from "@/layouts/DashboardLayout"; +import DashboardLayout from "@/layouts/DashboardLayout"; import {Container} from "@mui/material"; import ChangePasswordForm from "@/components/dashboard/change-password/change-password-form"; const DashboardChangePasswordComponent = () => { return ( - + - + ); }; export default DashboardChangePasswordComponent; diff --git a/src/components/dashboard/edit-profile/index.jsx b/src/components/dashboard/edit-profile/index.jsx index 394a7b0..e35e3bf 100644 --- a/src/components/dashboard/edit-profile/index.jsx +++ b/src/components/dashboard/edit-profile/index.jsx @@ -1,6 +1,6 @@ import StyledForm from "@/core/components/StyledForm"; import CenterLayout from "@/layouts/CenterLayout"; -import DashboardLayouts from "@/layouts/DashboardLayout"; +import DashboardLayout from "@/layouts/DashboardLayout"; import useUser from "@/lib/app/hooks/useUser"; import {Box, Container, Grid, Paper, Stack, TextField, Typography,} from "@mui/material"; import * as Yup from "yup"; @@ -54,7 +54,7 @@ const DashboardEditProfile = () => { const validationSchema = Yup.object().shape({}); return ( - + { - + ); }; diff --git a/src/components/dashboard/first/index.jsx b/src/components/dashboard/first/index.jsx index 1785a67..841d677 100644 --- a/src/components/dashboard/first/index.jsx +++ b/src/components/dashboard/first/index.jsx @@ -1,7 +1,7 @@ -import DashboardLayouts from "@/layouts/DashboardLayout"; +import DashboardLayout from "@/layouts/DashboardLayout"; const DashboardFirstComponent = () => { - return ; + return ; }; export default DashboardFirstComponent; diff --git a/src/layouts/DashboardLayout.jsx b/src/layouts/DashboardLayout.jsx index 40b722d..3c62a77 100644 --- a/src/layouts/DashboardLayout.jsx +++ b/src/layouts/DashboardLayout.jsx @@ -1,7 +1,7 @@ import CallWidget from "src/components/layouts/Dashboard/CallWidget"; import Dashboard from "src/components/layouts/Dashboard"; -const DashboardLayouts = (props) => { +const DashboardLayout = (props) => { return ( <> @@ -11,4 +11,4 @@ const DashboardLayouts = (props) => { ); }; -export default DashboardLayouts; +export default DashboardLayout; From 77f5c86a0d31635da6bae9309879269e5ec165af Mon Sep 17 00:00:00 2001 From: AmirHossein Mahmoodi Date: Sat, 7 Oct 2023 15:03:20 +0330 Subject: [PATCH 07/37] CFE-22 fixed bug network hook and component --- src/core/components/NetworkComponent.jsx | 7 +++---- src/lib/app/hooks/useNetwork.jsx | 26 +----------------------- 2 files changed, 4 insertions(+), 29 deletions(-) diff --git a/src/core/components/NetworkComponent.jsx b/src/core/components/NetworkComponent.jsx index 8ebbb68..22d4620 100644 --- a/src/core/components/NetworkComponent.jsx +++ b/src/core/components/NetworkComponent.jsx @@ -6,13 +6,13 @@ import WifiOffIcon from '@mui/icons-material/WifiOff'; import {useTranslations} from "next-intl"; const NetworkComponent = () => { - const toastId = useRef(null); + const networkToastId = useRef(null); const network = useNetwork() const t = useTranslations() useEffect(() => { if (network.online) { - toast.update(toastId.current, { + toast.update(networkToastId.current, { type: toast.TYPE.SUCCESS, render: t('online_message'), autoClose: 2000, @@ -22,8 +22,7 @@ const NetworkComponent = () => { }); return } - toast.dismiss() - toastId.current = toast.warn(t('offline_message'), { + networkToastId.current = toast.warn(t('offline_message'), { autoClose: false, closeButton: false, closeOnClick: false, icon: }) }, [network.online]); diff --git a/src/lib/app/hooks/useNetwork.jsx b/src/lib/app/hooks/useNetwork.jsx index 84e2950..9c436a5 100644 --- a/src/lib/app/hooks/useNetwork.jsx +++ b/src/lib/app/hooks/useNetwork.jsx @@ -1,29 +1,5 @@ import {useEffect, useState} from "react"; -function getNetworkConnection() { - return ( - navigator.connection || - navigator.mozConnection || - navigator.webkitConnection || - null - ); -} - -function getNetworkConnectionInfo() { - const connection = getNetworkConnection(); - if (!connection) { - return {}; - } - return { - rtt: connection.rtt, - type: connection.type, - saveData: connection.saveData, - downLink: connection.downLink, - downLinkMax: connection.downLinkMax, - effectiveType: connection.effectiveType, - }; -} - function useNetwork() { const [state, setState] = useState(() => { return { @@ -51,7 +27,7 @@ function useNetwork() { window.removeEventListener("offline", handleOffline); }; }, []); - + return state; } From 05c5e13c6386e229ad4e53171af8bcf9ffc086f8 Mon Sep 17 00:00:00 2001 From: AmirHossein Mahmoodi Date: Sat, 7 Oct 2023 15:03:57 +0330 Subject: [PATCH 08/37] CFE-22 fixed bug project --- src/layouts/MuiLayout.jsx | 15 ++++++--------- src/lib/app/contexts/user.jsx | 6 ------ src/pages/_app.jsx | 30 +++++++++++++++--------------- 3 files changed, 21 insertions(+), 30 deletions(-) diff --git a/src/layouts/MuiLayout.jsx b/src/layouts/MuiLayout.jsx index 6dcd3ed..eba3449 100644 --- a/src/layouts/MuiLayout.jsx +++ b/src/layouts/MuiLayout.jsx @@ -22,26 +22,23 @@ const MuiLayout = ({children, isBot}) => { { export const UserContext = createContext(); export const UserProvider = ({children}) => { - const socket = useSocket() const [state, dispatch] = useReducer(reducer, initialUser); const clearUser = useCallback(() => { @@ -102,16 +100,12 @@ export const UserProvider = ({children}) => { clearUser(); changeAuthState(false); changeLanguageState(false); - - socket.auth.token = "" return; } getUser((data) => { changeUser(data); changeAuthState(true); changeLanguageState(true); - - socket.auth.token = data.telephone_id }); }, [state.token]); diff --git a/src/pages/_app.jsx b/src/pages/_app.jsx index fe249a6..c5fad77 100644 --- a/src/pages/_app.jsx +++ b/src/pages/_app.jsx @@ -12,22 +12,22 @@ import {SocketProvider} from "@/lib/app/contexts/socket"; const App = ({Component, pageProps}) => { return (<> - - - - - - {pageProps.messages ? : ''} - - + + + + + {pageProps.messages ? : ''} + + + - - - - - - - + + + + + + + ) }; From b0766f13461f1c337b8bb2ad1c2e80cd085e89a1 Mon Sep 17 00:00:00 2001 From: AmirHossein Mahmoodi Date: Sat, 7 Oct 2023 15:04:40 +0330 Subject: [PATCH 09/37] CFE-22 implementation of socket and dialog and button call widget --- public/locales/fa/app.json | 2 + .../Dashboard/CallWidget/CallWidgetButton.jsx | 6 ++- .../CallTabPanel/CallActions/index.jsx | 7 +++ .../CallTabPanel/CallHistory/index.jsx | 7 +++ .../CallTabs/CallTabPanel/index.jsx | 19 +++++++ .../CallTabs/CallTabsList/CallTabLabel.jsx | 23 ++++++++ .../CallTabs/CallTabsList/index.jsx | 27 ++++++++++ .../CallWidgetDialog/CallTabs/index.jsx | 17 ++++++ .../CallWidget/CallWidgetDialog/index.jsx | 52 +++++++++++++----- .../layouts/Dashboard/CallWidget/index.jsx | 25 ++++++--- src/core/components/StyledFab.jsx | 1 - src/lib/app/contexts/socket.jsx | 54 ++++++++++++++++++- src/lib/callWidget/contexts/answers.jsx | 31 +++++++++++ src/lib/callWidget/hooks/useAnswers.jsx | 38 +++++++++++++ src/lib/callWidget/hooks/useCallDialog.jsx | 10 ++++ 15 files changed, 295 insertions(+), 24 deletions(-) create mode 100644 src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallActions/index.jsx create mode 100644 src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallHistory/index.jsx create mode 100644 src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/index.jsx create mode 100644 src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabsList/CallTabLabel.jsx create mode 100644 src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabsList/index.jsx create mode 100644 src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/index.jsx create mode 100644 src/lib/callWidget/contexts/answers.jsx create mode 100644 src/lib/callWidget/hooks/useAnswers.jsx create mode 100644 src/lib/callWidget/hooks/useCallDialog.jsx diff --git a/public/locales/fa/app.json b/public/locales/fa/app.json index e813849..7366af7 100644 --- a/public/locales/fa/app.json +++ b/public/locales/fa/app.json @@ -15,6 +15,8 @@ "between": "میان", "online_message": "شما به اینترنت وصل هستید", "offline_message": "اتصال شما به اینترنت قطع شده است", + "socket_is_connect_message": "شما به سوکت وصل هستید", + "socket_is_not_connect_message": "اتصال شما به سوکت قطع شده است", "header": { "open_profile": "پروفایل", "edit_profile": " پروفایل", diff --git a/src/components/layouts/Dashboard/CallWidget/CallWidgetButton.jsx b/src/components/layouts/Dashboard/CallWidget/CallWidgetButton.jsx index 416e5c8..76af95c 100644 --- a/src/components/layouts/Dashboard/CallWidget/CallWidgetButton.jsx +++ b/src/components/layouts/Dashboard/CallWidget/CallWidgetButton.jsx @@ -1,10 +1,12 @@ import PermPhoneMsgIcon from '@mui/icons-material/PermPhoneMsg'; import StyledFab from "@/core/components/StyledFab"; +import useCallDialog from "@/lib/callWidget/hooks/useCallDialog"; -const CallWidgetButton = ({setOpen}) => { +const CallWidgetButton = () => { + const {setOpenCallDialog} = useCallDialog() return ( setOpen(true)}> + onClick={() => setOpenCallDialog(true)}> ) diff --git a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallActions/index.jsx b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallActions/index.jsx new file mode 100644 index 0000000..5b9326f --- /dev/null +++ b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallActions/index.jsx @@ -0,0 +1,7 @@ +const CallActions = ({tab}) => { + return ( + <>CallActions - {tab.id} + ) +} + +export default CallActions \ No newline at end of file diff --git a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallHistory/index.jsx b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallHistory/index.jsx new file mode 100644 index 0000000..292c640 --- /dev/null +++ b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallHistory/index.jsx @@ -0,0 +1,7 @@ +const CallHistory = ({tab}) => { + return ( + <>CallHistory - {tab.id} + ) +} + +export default CallHistory \ No newline at end of file diff --git a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/index.jsx b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/index.jsx new file mode 100644 index 0000000..79dc736 --- /dev/null +++ b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/index.jsx @@ -0,0 +1,19 @@ +import {Grid} from "@mui/material"; +import CallActions from "@/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallActions"; +import CallHistory from "@/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallHistory"; + +const CallTabPanel = ({tab}) => { + return ( + + + + + + + + + ) +} + +export default CallTabPanel \ No newline at end of file diff --git a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabsList/CallTabLabel.jsx b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabsList/CallTabLabel.jsx new file mode 100644 index 0000000..7a9b703 --- /dev/null +++ b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabsList/CallTabLabel.jsx @@ -0,0 +1,23 @@ +import {IconButton, Stack, Typography, Zoom} from "@mui/material"; +import CloseIcon from '@mui/icons-material/Close'; +import useAnswers from "@/lib/callWidget/hooks/useAnswers"; + +const CallTabLabel = ({tab}) => { + const {closeAnswerTab} = useAnswers() + const handleCloseClick = (id) => { + closeAnswerTab(id) + }; + + return ( + + {tab.phone_number} - {tab.id} + + handleCloseClick(tab.id)}> + + + + + ) +} + +export default CallTabLabel \ No newline at end of file diff --git a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabsList/index.jsx b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabsList/index.jsx new file mode 100644 index 0000000..70fccd9 --- /dev/null +++ b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabsList/index.jsx @@ -0,0 +1,27 @@ +import {Box, Tab, Tabs} from "@mui/material"; +import CallTabLabel + from "@/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabsList/CallTabLabel"; +import useAnswers from "@/lib/callWidget/hooks/useAnswers"; + +const CallTabsList = () => { + const {answersList, changeActiveTabAnswers, activeTab} = useAnswers() + const handleChange = (event, newValue) => { + changeActiveTabAnswers(newValue) + }; + + return ( + + + {answersList.map((answer) => ( + } key={answer.id}/> + ))} + + + ) +} + +export default CallTabsList \ No newline at end of file diff --git a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/index.jsx b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/index.jsx new file mode 100644 index 0000000..4e1d2f1 --- /dev/null +++ b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/index.jsx @@ -0,0 +1,17 @@ +import CallTabsList from "src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabsList"; +import CallTabPanel from "@/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel"; +import useAnswers from "@/lib/callWidget/hooks/useAnswers"; + +const CallTabs = () => { + const {answersList} = useAnswers() + + return ( + <> + + {answersList.map((answer) => ( + + ))} + + ) +} +export default CallTabs \ No newline at end of file diff --git a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/index.jsx b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/index.jsx index 77ed57d..5afb0a1 100644 --- a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/index.jsx +++ b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/index.jsx @@ -1,23 +1,47 @@ import {Dialog} from "@mui/material"; import {DialogTransition} from "@/core/components/DialogTransition"; +import {useEffect} from "react"; +import useCallDialog from "@/lib/callWidget/hooks/useCallDialog"; +import CallTabs from "@/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs"; +import useAnswers from "@/lib/callWidget/hooks/useAnswers"; +import useSocket from "@/lib/app/hooks/useSocket"; -const CallWidgetDialog = ({open, setOpen}) => { +const CallWidgetDialog = () => { + const {openCallDialog, setOpenCallDialog} = useCallDialog() + const {answersList, newAnswerTab} = useAnswers() + const socket = useSocket() - const handleClose = () => { - setOpen(false); - }; + useEffect(() => { + const answerCall = (data, act) => { + newAnswerTab({ + id: data.id, + phone_number: data.phone_number + }) + act() + } + socket.on('answer', answerCall) + + return () => { + socket.off('answer', answerCall) + } + }, []); + + useEffect(() => { + if (answersList.length === 0) { + setOpenCallDialog(false) + return + } + setOpenCallDialog(true) + }, [answersList.length]); return ( - <> - - - - + + + ) } diff --git a/src/components/layouts/Dashboard/CallWidget/index.jsx b/src/components/layouts/Dashboard/CallWidget/index.jsx index 4d871b2..d6fe5a0 100644 --- a/src/components/layouts/Dashboard/CallWidget/index.jsx +++ b/src/components/layouts/Dashboard/CallWidget/index.jsx @@ -1,14 +1,27 @@ import CallWidgetButton from "@/components/layouts/Dashboard/CallWidget/CallWidgetButton"; import CallWidgetDialog from "@/components/layouts/Dashboard/CallWidget/CallWidgetDialog"; -import {useState} from "react"; +import {AnswersProvider} from "@/lib/callWidget/contexts/answers"; +import useSocket from "@/lib/app/hooks/useSocket"; +import {useEffect} from "react"; const CallWidget = () => { - const [open, setOpen] = useState(false) + const socket = useSocket() + + useEffect(() => { + if (socket.connected) return + + socket.connect() + + return () => { + socket.disconnect() + } + }, []); + return ( - <> - - - + + + + ) } diff --git a/src/core/components/StyledFab.jsx b/src/core/components/StyledFab.jsx index dd935cc..95c5ff7 100644 --- a/src/core/components/StyledFab.jsx +++ b/src/core/components/StyledFab.jsx @@ -5,7 +5,6 @@ const StyledFab = styled(Fab)` bottom: 16px; /* @noflip */ left: 16px; - z-index: 1500; `; export default StyledFab; diff --git a/src/lib/app/contexts/socket.jsx b/src/lib/app/contexts/socket.jsx index 7da4f43..47154c8 100644 --- a/src/lib/app/contexts/socket.jsx +++ b/src/lib/app/contexts/socket.jsx @@ -1,9 +1,18 @@ -import {createContext, useMemo} from "react"; +import {createContext, useEffect, useMemo, useRef, useState} from "react"; import {io} from "socket.io-client"; +import useUser from "@/lib/app/hooks/useUser"; +import {useTranslations} from "next-intl"; +import {toast} from "react-toastify"; +import PowerIcon from "@mui/icons-material/Power"; +import PowerOffIcon from "@mui/icons-material/PowerOff"; export const SocketContext = createContext() export const SocketProvider = ({children}) => { + const {user, isAuth} = useUser() + const [connectionError, setConnectionError] = useState(false) + const socketToastId = useRef(null); + const t = useTranslations() const socket = useMemo(() => io(process.env.NEXT_PUBLIC_SERVER_SOCKET_URL, { autoConnect: false, auth: { @@ -11,5 +20,48 @@ export const SocketProvider = ({children}) => { } }), []) + useEffect(() => { + if (isAuth) { + socket.auth.token = user.telephone_id + return + } + socket.auth.token = '' + }, [isAuth]); + + useEffect(() => { + const connectErrorHandler = () => { + setConnectionError(true) + } + const connectHandler = () => { + setConnectionError(false) + } + socket.on("connect_error", connectErrorHandler); + socket.on("connect", connectHandler); + + return () => { + socket.off("connect_error", connectErrorHandler); + socket.off("connect", connectHandler); + } + }, []); + + useEffect(() => { + if (!connectionError) { + toast.update(socketToastId.current, { + type: toast.TYPE.SUCCESS, + render: t('socket_is_connect_message'), + autoClose: 2000, + closeButton: true, + closeOnClick: true, + icon: + }); + return + } + socketToastId.current = toast.warn(t('socket_is_not_connect_message'), { + draggable: false, + autoClose: false, closeButton: false, closeOnClick: false, icon: + }) + + }, [connectionError]); + return {children} } \ No newline at end of file diff --git a/src/lib/callWidget/contexts/answers.jsx b/src/lib/callWidget/contexts/answers.jsx new file mode 100644 index 0000000..a9f7ae2 --- /dev/null +++ b/src/lib/callWidget/contexts/answers.jsx @@ -0,0 +1,31 @@ +import {createContext, useReducer, useState} from "react"; + +const reducer = (state, action) => { + switch (action.type) { + case "CHANGE_ACTIVE_TAB": + return state.map((answer, index) => action.newValue === index ? {...answer, active: true} : { + ...answer, + active: false + }); + case "CHANGE_LIST": + return [...action.newList] + default: + return state; + } +} +export const AnswersContext = createContext() +export const AnswersProvider = ({children}) => { + const [openCallDialog, setOpenCallDialog] = useState(false) + const [activeTab, setActiveTab] = useState(0) + const [state, dispatch] = useReducer(reducer, []); + + return {children} +} diff --git a/src/lib/callWidget/hooks/useAnswers.jsx b/src/lib/callWidget/hooks/useAnswers.jsx new file mode 100644 index 0000000..2155cd7 --- /dev/null +++ b/src/lib/callWidget/hooks/useAnswers.jsx @@ -0,0 +1,38 @@ +import {useContext} from "react"; +import {AnswersContext} from "@/lib/callWidget/contexts/answers"; + +const useAnswers = () => { + const {state, dispatch, activeTab, setActiveTab} = useContext(AnswersContext) + + const changeActiveTabAnswers = (newValue) => { + dispatch({type: 'CHANGE_ACTIVE_TAB', newValue}) + setActiveTab(newValue) + } + + const newAnswerTab = (data) => { + const newAnswer = { + id: data.id, + phone_number: data.phone_number, + active: true + } + const newList = [...state, newAnswer] + dispatch({type: 'CHANGE_LIST', newList}) + changeActiveTabAnswers(newList.length - 1) + } + + const closeAnswerTab = (id) => { + const index = state.findIndex(answer => answer.id === id) + const newIndex = index === 0 ? 0 : index - 1 + const newList = [...state] + newList.splice(index, 1); + if (newList.length !== 0) { + newList[newIndex].active = true + setActiveTab(newIndex) + } + dispatch({type: 'CHANGE_LIST', newList}) + } + + return {answersList: state, activeTab, setActiveTab, changeActiveTabAnswers, closeAnswerTab, newAnswerTab} +} + +export default useAnswers \ No newline at end of file diff --git a/src/lib/callWidget/hooks/useCallDialog.jsx b/src/lib/callWidget/hooks/useCallDialog.jsx new file mode 100644 index 0000000..30dd599 --- /dev/null +++ b/src/lib/callWidget/hooks/useCallDialog.jsx @@ -0,0 +1,10 @@ +import {useContext} from "react"; +import {AnswersContext} from "@/lib/callWidget/contexts/answers"; + +const useCallDialog = () => { + const {openCallDialog, setOpenCallDialog} = useContext(AnswersContext) + + return {openCallDialog, setOpenCallDialog} +} + +export default useCallDialog \ No newline at end of file From e24922918784ff670d1c2b4575c8c4d0dbebf5b9 Mon Sep 17 00:00:00 2001 From: Amin Ghasempoor Date: Mon, 9 Oct 2023 11:15:41 +0330 Subject: [PATCH 10/37] CFE-4 role management test --- .env.test | 12 ++ public/locales/fa/app.json | 28 +++ .../CreateForm/__test__/CreateContent.test.js | 86 +++++++- .../role-management/Form/DeleteForm.jsx | 67 ------ .../Form/DeleteForm/DeleteContent.jsx | 42 ++++ .../DeleteForm/__test__/DeleteContent.test.js | 53 +++++ .../Form/DeleteForm/__test__/index.test.js | 29 +++ .../role-management/Form/DeleteForm/index.jsx | 39 ++++ .../{UpdateContext.jsx => UpdateContent.jsx} | 26 +-- .../UpdateForm/__test__/UpdateContent.test.js | 201 ++++++++++++++++++ .../Form/UpdateForm/__test__/index.test.js | 30 +++ .../role-management/Form/UpdateForm/index.jsx | 7 +- .../role-management/TableRowActions.jsx | 2 +- .../__test__/RoleManagementComponent.test.js | 2 +- src/core/components/DataTable.jsx | 1 - src/lib/app/contexts/language.jsx | 6 +- 16 files changed, 535 insertions(+), 96 deletions(-) create mode 100644 .env.test delete mode 100644 src/components/dashboard/role-management/Form/DeleteForm.jsx create mode 100644 src/components/dashboard/role-management/Form/DeleteForm/DeleteContent.jsx create mode 100644 src/components/dashboard/role-management/Form/DeleteForm/__test__/DeleteContent.test.js create mode 100644 src/components/dashboard/role-management/Form/DeleteForm/__test__/index.test.js create mode 100644 src/components/dashboard/role-management/Form/DeleteForm/index.jsx rename src/components/dashboard/role-management/Form/UpdateForm/{UpdateContext.jsx => UpdateContent.jsx} (87%) create mode 100644 src/components/dashboard/role-management/Form/UpdateForm/__test__/UpdateContent.test.js create mode 100644 src/components/dashboard/role-management/Form/UpdateForm/__test__/index.test.js diff --git a/.env.test b/.env.test new file mode 100644 index 0000000..0be16f0 --- /dev/null +++ b/.env.test @@ -0,0 +1,12 @@ +NEXT_PUBLIC_API_NAME = "CRM Dashboard" +NEXT_PUBLIC_API_VERSION = "0.0.1" +NEXT_PUBLIC_DEFAULT_LANGUAGE = "fa" +NEXT_PUBLIC_DEFAULT_DIRECTION = "rtl" + +NEXT_PUBLIC_PRIMARY_MAIN = "#1b4332" +NEXT_PUBLIC_SECONDARY_MAIN = "#800e13" + +NEXT_PUBLIC_BASE_URL = "https://crm.witel.ir" +NEXT_PUBLIC_POWERED_BY_URL = "https://witel.ir" + +NODE_ENV = "development" \ No newline at end of file diff --git a/public/locales/fa/app.json b/public/locales/fa/app.json index 39531c9..aa32ec4 100644 --- a/public/locales/fa/app.json +++ b/public/locales/fa/app.json @@ -153,5 +153,33 @@ "navgan_id": "کد ناوگان", "button-cancel": "انصراف", "button-add": "ثبت" + }, + "UpdateDialog": { + "update": "ویرایش", + "description": "توضیحات خود را وارد نمائید", + "description_error": "وارد کردن توضیحات الزامی است!", + "next-state-id": "وضعیت", + "next-state-id-error": "وارد کردن وضعیت الزامیست", + "update-tooltip": "ویرایش", + "button-cancel": "بستن", + "refahi": "رفاهی", + "navgan": "ناوگان", + "phone_number": "شماره تلفن", + "national_id": "کد ملی", + "name_en": "نام انگلیسی", + "name_en_error": "وارد کردن نام انگلیسی الزامیست", + "name_fa": "نام فارسی", + "name_fa_error": "وارد کردن نام فارسی الزامیست", + "permission": "دسترسی", + "permission_min_error": "حداقل باید یک دسترسی انتخاب شود", + "type_id": "نوع کاربر", + "navgan_id": "کد ناوگان", + "button-update": "ثبت" + }, + "DeleteDialog": { + "delete": "حذف", + "button-cancel": "انصراف", + "typography": "آیا از حدف این مورد اطمینان دارید ؟", + "button-delete": "حذف کردن" } } diff --git a/src/components/dashboard/role-management/Form/CreateForm/__test__/CreateContent.test.js b/src/components/dashboard/role-management/Form/CreateForm/__test__/CreateContent.test.js index 1742b12..3e31b8f 100644 --- a/src/components/dashboard/role-management/Form/CreateForm/__test__/CreateContent.test.js +++ b/src/components/dashboard/role-management/Form/CreateForm/__test__/CreateContent.test.js @@ -1,4 +1,4 @@ -import {act, fireEvent, render, screen} from "@testing-library/react"; +import {act, fireEvent, render, screen, waitFor} from "@testing-library/react"; import MockAppWithProviders from "../../../../../../../mocks/AppWithProvider"; import CreateContent from "@/components/dashboard/role-management/Form/CreateForm/CreateContent"; @@ -31,6 +31,16 @@ describe("Create Content component from Create Form Component in Role Management const textElement = screen.queryByLabelText("نام فارسی") expect(textElement).toBeInTheDocument() }); + it('should see name_fa text', () => { + render( + + + + ) + const textElement = screen.queryByLabelText("نام فارسی") + expect(textElement).toBeInTheDocument() + }); + }) describe("Behavior",()=>{ @@ -49,7 +59,6 @@ describe("Create Content component from Create Form Component in Role Management }) }); it('should see what fill in the name_fa input', async () => { - localStorage.setItem("_token", "token") render( @@ -63,8 +72,6 @@ describe("Create Content component from Create Form Component in Role Management expect(nameFarsiInput).toHaveValue('testuser'); }) }); - - it('should return permissions_list', async () => { render( @@ -76,7 +83,76 @@ describe("Create Content component from Create Form Component in Role Management }); it('should get checked if permission list item get clicked', () => { - + render( + + + + ) + const checkboxElement = screen.getByLabelText("مدیریت کارتابل رییس اداره مسافری استان") + + expect(checkboxElement).not.toBeChecked(); + + fireEvent.click(checkboxElement); + + expect(checkboxElement).toBeChecked(); + }); + }) + describe("Validation",()=>{ + it('should see error text when name_fa input is empty', async () => { + render( + + + + ) + const name_faInput = screen.getByLabelText("نام فارسی") + expect(screen.queryByText("وارد کردن نام فارسی الزامیست")).not.toBeInTheDocument() + fireEvent.blur(name_faInput); + + await waitFor(()=>{ + expect(screen.queryByText("وارد کردن نام فارسی الزامیست")).toBeInTheDocument() + }) + fireEvent.change(name_faInput, {target : {value : "نام فارسی"}}) + + await waitFor(()=>{ + expect(screen.queryByText("وارد کردن نام فارسی الزامیست")).not.toBeInTheDocument() + }) + }) + it('should see error text when name_en input is empty', async () => { + render( + + + + ) + const name_enInput = screen.getByLabelText("نام انگلیسی") + expect(screen.queryByText("وارد کردن نام انگلیسی الزامیست")).not.toBeInTheDocument() + fireEvent.blur(name_enInput); + + await waitFor(()=>{ + expect(screen.queryByText("وارد کردن نام انگلیسی الزامیست")).toBeInTheDocument() + }) + fireEvent.change(name_enInput, {target : {value : "english name"}}) + + await waitFor(()=>{ + expect(screen.queryByText("وارد کردن نام انگلیسی الزامیست")).not.toBeInTheDocument() + }) + }) + it('should see errors when click on submit button and field not completed', async () => { + render( + + + + ) + const submitButtonElement = screen.queryByText("ثبت") + fireEvent.click(submitButtonElement) + await waitFor(()=>{ + expect(screen.queryByText("وارد کردن نام انگلیسی الزامیست")).toBeInTheDocument() + }) + await waitFor(()=>{ + expect(screen.queryByText("وارد کردن نام فارسی الزامیست")).toBeInTheDocument() + }) + await waitFor(()=>{ + expect(screen.queryByText("حداقل باید یک دسترسی انتخاب شود")).toBeInTheDocument() + }) }); }) }) \ 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 deleted file mode 100644 index 9c613b1..0000000 --- a/src/components/dashboard/role-management/Form/DeleteForm.jsx +++ /dev/null @@ -1,67 +0,0 @@ -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/DeleteForm/DeleteContent.jsx b/src/components/dashboard/role-management/Form/DeleteForm/DeleteContent.jsx new file mode 100644 index 0000000..52674a5 --- /dev/null +++ b/src/components/dashboard/role-management/Form/DeleteForm/DeleteContent.jsx @@ -0,0 +1,42 @@ +import {Button, DialogActions, DialogContent, DialogTitle, Typography} from "@mui/material"; +import {DELETE_ROLE_MANAGEMENT} from "@/core/data/apiRoutes"; +import {useState} from "react"; +import useRequest from "@/lib/app/hooks/useRequest"; +import useNotification from "@/lib/app/hooks/useNotification"; +import {useTranslations} from "next-intl"; + +const DeleteContent = ({rowId, fetchUrl, mutate, setOpenConfirmDialog}) => { + const t = useTranslations(); + 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( + <> + {t("DeleteDialog.delete")} + + {t("DeleteDialog.typography")} + + + + + + + ) +} +export default DeleteContent \ No newline at end of file diff --git a/src/components/dashboard/role-management/Form/DeleteForm/__test__/DeleteContent.test.js b/src/components/dashboard/role-management/Form/DeleteForm/__test__/DeleteContent.test.js new file mode 100644 index 0000000..580dcb9 --- /dev/null +++ b/src/components/dashboard/role-management/Form/DeleteForm/__test__/DeleteContent.test.js @@ -0,0 +1,53 @@ +import {act, render, screen} from "@testing-library/react"; +import MockAppWithProviders from "../../../../../../../mocks/AppWithProvider"; +import DeleteContent from "@/components/dashboard/role-management/Form/DeleteForm/DeleteContent"; +import UpdateContent from "@/components/dashboard/role-management/Form/UpdateForm/UpdateContent"; + +describe("Create Content component from Create Form Component in Role Management Component",()=> { + describe("Rendering", () => { + it('should see DeleteDialog text in the top ', async () => { + render( + + + + ) + const textElement = screen.queryByText("حذف") + act(()=>{ + expect(textElement).toBeInTheDocument() + }) + }) + it('should see DeleteDialog text content ', async () => { + render( + + + + ) + const textElement = screen.queryByText("آیا از حدف این مورد اطمینان دارید ؟") + act(()=>{ + expect(textElement).toBeInTheDocument() + }) + }) + it('should see delete text in the delete button ', () => { + render( + + + + ) + const buttonElement = screen.queryByText("حذف کردن") + act(()=>{ + expect(buttonElement).toBeInTheDocument() + }) + }); + it('should see cancel text in the cancel button ', () => { + render( + + + + ) + const buttonElement = screen.queryByText("انصراف") + act(()=>{ + expect(buttonElement).toBeInTheDocument() + }) + }); + }) +}) \ No newline at end of file diff --git a/src/components/dashboard/role-management/Form/DeleteForm/__test__/index.test.js b/src/components/dashboard/role-management/Form/DeleteForm/__test__/index.test.js new file mode 100644 index 0000000..6c0c48b --- /dev/null +++ b/src/components/dashboard/role-management/Form/DeleteForm/__test__/index.test.js @@ -0,0 +1,29 @@ +import {fireEvent, render, screen} from "@testing-library/react"; +import MockAppWithProviders from "../../../../../../../mocks/AppWithProvider"; +import DeleteForm from "@/components/dashboard/role-management/Form/DeleteForm"; +import UpdateForm from "@/components/dashboard/role-management/Form/UpdateForm"; + +describe("Create Form Component from Role Management Component",()=> { + describe("Rendering", () => { + it('should see Dialog text', () => { + render( + + + + ) + const textElement = screen.queryByText("ویرایش") + expect(textElement).toBeInTheDocument() + }); + it('should see Dialog text when mouse is over', async () => { + render( + + + + ) + const textElement = screen.queryByText("حذف") + fireEvent.mouseOver(textElement); + const tooltipTextElement = screen.getByText("حذف"); + expect(tooltipTextElement).toBeInTheDocument(); + }); + }) +}) \ No newline at end of file diff --git a/src/components/dashboard/role-management/Form/DeleteForm/index.jsx b/src/components/dashboard/role-management/Form/DeleteForm/index.jsx new file mode 100644 index 0000000..b2c14fa --- /dev/null +++ b/src/components/dashboard/role-management/Form/DeleteForm/index.jsx @@ -0,0 +1,39 @@ +import {useTranslations} from "next-intl"; +import {useState} from "react"; +import { + Button, + Dialog, + Tooltip, +} from "@mui/material"; +import DeleteIcon from '@mui/icons-material/Delete'; +import DeleteContent from "@/components/dashboard/role-management/Form/DeleteForm/DeleteContent"; + +const DeleteForm = ({rowId, fetchUrl, mutate}) => { + const t = useTranslations(); + const [openConfirmDialog, setOpenConfirmDialog] = useState(false); + + + return ( + <> + + + + + + + + ); +}; +export default DeleteForm; \ No newline at end of file diff --git a/src/components/dashboard/role-management/Form/UpdateForm/UpdateContext.jsx b/src/components/dashboard/role-management/Form/UpdateForm/UpdateContent.jsx similarity index 87% rename from src/components/dashboard/role-management/Form/UpdateForm/UpdateContext.jsx rename to src/components/dashboard/role-management/Form/UpdateForm/UpdateContent.jsx index e2f2980..23fa4e4 100644 --- a/src/components/dashboard/role-management/Form/UpdateForm/UpdateContext.jsx +++ b/src/components/dashboard/role-management/Form/UpdateForm/UpdateContent.jsx @@ -1,7 +1,7 @@ import { Button, Checkbox, DialogActions, - DialogContent, + DialogContent, DialogTitle, FormControl, FormControlLabel, FormHelperText, @@ -17,29 +17,27 @@ import usePermissions from "@/lib/app/hooks/usePermissions"; import useNotification from "@/lib/app/hooks/useNotification"; import useRequest from "@/lib/app/hooks/useRequest"; import {useTranslations} from "next-intl"; -import {useState} from "react"; -const UpdateContent = ({mutate, fetchUrl, setOpenConfirmDialog}) => { +const UpdateContent = ({mutate, row, fetchUrl, setOpenConfirmDialog}) => { const t = useTranslations(); const requestServer = useRequest({auth: true}) const {update_notification} = useNotification() const {permissions_list} = usePermissions() - const [openConfirmDialog, setOpenConfirmDialog] = useState(false); const validationSchema = Yup.object().shape({ - name: Yup.string().required(t("UpdateDialog.name_error")), + name_en: Yup.string().required(t("UpdateDialog.name_en_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_en: 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", values.name_en); formData.append("name_fa", values.name_fa); for (let i = 0; i < values.permissions.length; i++) { formData.append(`permissions[${i}]`, values.permissions[i]); @@ -59,19 +57,20 @@ const UpdateContent = ({mutate, fetchUrl, setOpenConfirmDialog}) => { return( <> + {t("UpdateDialog.update")} { { if (e.target.checked) { diff --git a/src/components/dashboard/role-management/Form/UpdateForm/__test__/UpdateContent.test.js b/src/components/dashboard/role-management/Form/UpdateForm/__test__/UpdateContent.test.js new file mode 100644 index 0000000..d36fe05 --- /dev/null +++ b/src/components/dashboard/role-management/Form/UpdateForm/__test__/UpdateContent.test.js @@ -0,0 +1,201 @@ +import {act, fireEvent, render, screen, waitFor} from "@testing-library/react"; +import MockAppWithProviders from "../../../../../../../mocks/AppWithProvider"; +import UpdateContent from "@/components/dashboard/role-management/Form/UpdateForm/UpdateContent"; +const row = { + id : 0, + getValue : name => { + if (name === "name") { + return "manage_passenger_office_navgan"; + } + else if (name === "name_fa") { + return "مدیریت کارتابل رییس اداره مسافری استان"; + } + }, + original : { + permissions : [ + { + id: 1, + name: "manage_passenger_office_navgan", + name_fa: "مدیریت کارتابل رییس اداره مسافری استان" + }, + { + id: 2, + name: "manage_passenger_office_navgan", + name_fa: "مدیریت کارتابل رییس اداره مسافری استان" + } + ] + } +} + +describe("Create Content component from Create Form Component in Role Management Component",()=>{ + describe("Rendering", ()=>{ + it('should see AddDialog text in the top ', async () => { + render( + + + + ) + const textElement = screen.queryByText("ویرایش") + act(()=>{ + expect(textElement).toBeInTheDocument() + }) + }); + it('should see name_en text', async () => { + render( + + + + ) + const textElement = screen.queryByLabelText("نام انگلیسی") + act(()=>{ + expect(textElement).toBeInTheDocument() + }) + }); + it('should see name_fa text', async () => { + render( + + + + ) + const textElement = screen.queryByLabelText("نام فارسی") + act(()=>{ + expect(textElement).toBeInTheDocument() + }) + }); + it('should see update text in the submit button ', () => { + render( + + + + ) + const buttonElement = screen.queryByText("ثبت") + act(()=>{ + expect(buttonElement).toBeInTheDocument() + }) + }); + it('should see cancel text in the cancel button ', () => { + render( + + + + ) + const buttonElement = screen.queryByText("بستن") + act(()=>{ + expect(buttonElement).toBeInTheDocument() + }) + }); + }) + + describe("Behavior", ()=>{ + it('should see what fill in the name_en input', async () => { + render( + + + + ) + const nameEnglishInput = screen.getByLabelText('نام انگلیسی'); + + fireEvent.change(nameEnglishInput, {target: {value: 'testuser'}}); + await act(()=>{ + // Simulate user input + expect(nameEnglishInput).toHaveValue('testuser'); + }) + }); + it('should see what fill in the name_fa input', async () => { + render( + + + + ) + const nameFarsiInput = screen.getByLabelText('نام فارسی'); + + fireEvent.change(nameFarsiInput, {target: {value: 'testuser'}}); + await act(()=>{ + // Simulate user input + expect(nameFarsiInput).toHaveValue('testuser'); + }) + }); + it('should return permissions_list', async () => { + render( + + + + ) + const checkboxElement = await screen.findAllByTestId("PermissionList-checkbox") + expect(checkboxElement).toHaveLength(2) + }); + it('should see the value of the name_en that pass to input value', () => { + render( + + + + ) + const name_enElement = screen.getByLabelText("نام انگلیسی") + expect(name_enElement).toHaveValue("manage_passenger_office_navgan") + }); + it('should see the value of the name_fa that pass to input value', () => { + render( + + + + ) + const name_enElement = screen.getByLabelText("نام فارسی") + expect(name_enElement).toHaveValue("مدیریت کارتابل رییس اداره مسافری استان") + }); + it('should get checked if the id exists in permission lists', () => { + render( + + + + ) + const checkboxElement = screen.getByLabelText("مدیریت کارتابل رییس اداره مسافری استان") + + expect(checkboxElement).toBeChecked(); + }); + }) + describe("Validation", ()=>{ + it('should see error text when name_fa input is empty', async () => { + render( + + + + ) + const name_faInput = screen.getByLabelText("نام فارسی") + expect(screen.queryByText("وارد کردن نام فارسی الزامیست")).not.toBeInTheDocument() + fireEvent.change(name_faInput, {target : {value : ''}}); + + fireEvent.blur(name_faInput) + + await waitFor(()=>{ + expect(screen.queryByText("وارد کردن نام فارسی الزامیست")).toBeInTheDocument() + }) + fireEvent.change(name_faInput, {target : {value : "نام فارسی"}}) + + await waitFor(()=>{ + expect(screen.queryByText("وارد کردن نام فارسی الزامیست")).not.toBeInTheDocument() + }) + }) + it('should see error text when name_en input is empty', async () => { + render( + + + + ) + const name_enInput = screen.getByLabelText("نام انگلیسی") + expect(screen.queryByText("وارد کردن نام انگلیسی الزامیست")).not.toBeInTheDocument() + + fireEvent.change(name_enInput, {target : {value : null}}); + fireEvent.blur(name_enInput) + + await waitFor(()=>{ + + expect(screen.queryByText("وارد کردن نام انگلیسی الزامیست")).toBeInTheDocument() + }) + fireEvent.change(name_enInput, {target : {value : "english name"}}) + + await waitFor(()=>{ + expect(screen.queryByText("وارد کردن نام انگلیسی الزامیست")).not.toBeInTheDocument() + }) + }) + }) +}) \ No newline at end of file diff --git a/src/components/dashboard/role-management/Form/UpdateForm/__test__/index.test.js b/src/components/dashboard/role-management/Form/UpdateForm/__test__/index.test.js new file mode 100644 index 0000000..210ce6f --- /dev/null +++ b/src/components/dashboard/role-management/Form/UpdateForm/__test__/index.test.js @@ -0,0 +1,30 @@ +import {fireEvent, render, screen} from "@testing-library/react"; +import MockAppWithProviders from "../../../../../../../mocks/AppWithProvider"; +import UpdateContent from "@/components/dashboard/role-management/Form/UpdateForm/UpdateContent"; +import UpdateForm from "@/components/dashboard/role-management/Form/UpdateForm"; + +describe("Create Form Component from Role Management Component",()=>{ + describe("Rendering",()=>{ + it('should see Dialog text', () => { + render( + + + + ) + const textElement = screen.queryByText("ویرایش") + expect(textElement).toBeInTheDocument() + }); + + it('should see Tooltip text when mouse over', () => { + render( + + + + ) + const buttonElement = screen.getByText("ویرایش"); + fireEvent.mouseOver(buttonElement); + const tooltipTextElement = screen.getByText("ویرایش"); + expect(tooltipTextElement).toBeInTheDocument(); + }); + }) +}) \ No newline at end of file diff --git a/src/components/dashboard/role-management/Form/UpdateForm/index.jsx b/src/components/dashboard/role-management/Form/UpdateForm/index.jsx index 115e284..a6d05c6 100644 --- a/src/components/dashboard/role-management/Form/UpdateForm/index.jsx +++ b/src/components/dashboard/role-management/Form/UpdateForm/index.jsx @@ -2,9 +2,9 @@ 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"; +import UpdateContent from "@/components/dashboard/role-management/Form/UpdateForm/UpdateContent"; -const UpdateForm = ({mutate, fetchUrl}) => { +const UpdateForm = ({mutate, fetchUrl, row}) => { const t = useTranslations(); const [openConfirmDialog, setOpenConfirmDialog] = useState(false); @@ -26,8 +26,7 @@ const UpdateForm = ({mutate, fetchUrl}) => { - {t("UpdateDialog.update ")} - + ) diff --git a/src/components/dashboard/role-management/TableRowActions.jsx b/src/components/dashboard/role-management/TableRowActions.jsx index cd72356..500aeca 100644 --- a/src/components/dashboard/role-management/TableRowActions.jsx +++ b/src/components/dashboard/role-management/TableRowActions.jsx @@ -1,6 +1,6 @@ import {Box} from "@mui/material"; -import DeleteForm from "./Form/DeleteForm" import UpdateForm from "@/components/dashboard/role-management/Form/UpdateForm"; +import DeleteForm from "@/components/dashboard/role-management/Form/DeleteForm"; const TableRowActions = ({row, mutate, fetchUrl}) => { diff --git a/src/components/dashboard/role-management/__test__/RoleManagementComponent.test.js b/src/components/dashboard/role-management/__test__/RoleManagementComponent.test.js index d21081c..cf6254f 100644 --- a/src/components/dashboard/role-management/__test__/RoleManagementComponent.test.js +++ b/src/components/dashboard/role-management/__test__/RoleManagementComponent.test.js @@ -10,7 +10,7 @@ describe("Role Management Component from Role Management page",()=>{ )) - const textElement = screen.getByText("کد یکتا") + const textElement = screen.queryByText("کد یکتا") expect(textElement).toBeInTheDocument() }); }) diff --git a/src/core/components/DataTable.jsx b/src/core/components/DataTable.jsx index b3b0f26..350f37c 100644 --- a/src/core/components/DataTable.jsx +++ b/src/core/components/DataTable.jsx @@ -159,7 +159,6 @@ function DataTable(props) { )} state={{ - isLoading: isValidating, columnFilters, columnFilterFns, pagination, diff --git a/src/lib/app/contexts/language.jsx b/src/lib/app/contexts/language.jsx index e3f41f4..e9e24f7 100644 --- a/src/lib/app/contexts/language.jsx +++ b/src/lib/app/contexts/language.jsx @@ -18,7 +18,7 @@ export const LanguageProvider = ({children}) => { ]; const {user, userChangedLanguage, changeLanguageState} = useUser(); const [languageIsReady, setLanguageIsReady] = useState(false); - const [languageApp, setLanguageApp] = useState(); + const [languageApp, setLanguageApp] = useState(process.env.NEXT_PUBLIC_DEFAULT_LANGUAGE); const [directionApp, setDirectionApp] = useState( process.env.NEXT_PUBLIC_DEFAULT_DIRECTION ); @@ -26,9 +26,7 @@ export const LanguageProvider = ({children}) => { useEffect(() => { const lang = localStorage.getItem("_language"); - if (!lang && !languageApp) { - setLanguageApp(process.env.NEXT_PUBLIC_DEFAULT_LANGUAGE); - } else if (lang) { + if (lang) { setLanguageApp(lang); } }, []); From 18d6e93bd4bb8657b7332e2637c20f7d3c9e7316 Mon Sep 17 00:00:00 2001 From: Amin Ghasempoor Date: Tue, 10 Oct 2023 09:54:09 +0330 Subject: [PATCH 11/37] CFE-4 delete role index test --- .../__test__/RoleManagementComponent.test.js | 17 ----------------- 1 file changed, 17 deletions(-) delete mode 100644 src/components/dashboard/role-management/__test__/RoleManagementComponent.test.js diff --git a/src/components/dashboard/role-management/__test__/RoleManagementComponent.test.js b/src/components/dashboard/role-management/__test__/RoleManagementComponent.test.js deleted file mode 100644 index cf6254f..0000000 --- a/src/components/dashboard/role-management/__test__/RoleManagementComponent.test.js +++ /dev/null @@ -1,17 +0,0 @@ -import {render} from "@testing-library/react"; -import MockAppWithProviders from "../../../../../mocks/AppWithProvider"; -import RoleManagementComponent from "@/components/dashboard/role-management/RoleManagementComponent"; - -describe("Role Management Component from Role Management page",()=>{ - describe("Rendering",()=>{ - it('should show columns text', () => { - render(( - - - - )) - const textElement = screen.queryByText("کد یکتا") - expect(textElement).toBeInTheDocument() - }); - }) -}) \ No newline at end of file From 99b4128cd90b6f13b94a8cf82ad96efb034a9697 Mon Sep 17 00:00:00 2001 From: Amin Ghasempoor Date: Tue, 10 Oct 2023 10:05:29 +0330 Subject: [PATCH 12/37] CFE-4 rm cached .env.test --- .env.test | 12 ------------ 1 file changed, 12 deletions(-) delete mode 100644 .env.test diff --git a/.env.test b/.env.test deleted file mode 100644 index 0be16f0..0000000 --- a/.env.test +++ /dev/null @@ -1,12 +0,0 @@ -NEXT_PUBLIC_API_NAME = "CRM Dashboard" -NEXT_PUBLIC_API_VERSION = "0.0.1" -NEXT_PUBLIC_DEFAULT_LANGUAGE = "fa" -NEXT_PUBLIC_DEFAULT_DIRECTION = "rtl" - -NEXT_PUBLIC_PRIMARY_MAIN = "#1b4332" -NEXT_PUBLIC_SECONDARY_MAIN = "#800e13" - -NEXT_PUBLIC_BASE_URL = "https://crm.witel.ir" -NEXT_PUBLIC_POWERED_BY_URL = "https://witel.ir" - -NODE_ENV = "development" \ No newline at end of file From 9c74c180899846c1552748d7515ceee49b9f4130 Mon Sep 17 00:00:00 2001 From: Amin Ghasempoor Date: Tue, 10 Oct 2023 11:38:09 +0330 Subject: [PATCH 13/37] CFE-4 check and set the api --- public/locales/fa/app.json | 3 +- .../Form/CreateForm/CreateContent.jsx | 6 +-- .../Form/DeleteForm/__test__/index.test.js | 20 +++---- .../role-management/Form/DeleteForm/index.jsx | 14 ++--- .../Form/UpdateForm/UpdateContent.jsx | 2 +- .../Form/UpdateForm/__test__/index.test.js | 22 +++----- .../role-management/Form/UpdateForm/index.jsx | 19 +++---- .../RoleManagementComponent.jsx | 2 +- .../role-management/TableToolbar.jsx | 2 +- .../__test__/RoleManagementComponent.test.js | 53 +++++++++++++++++++ .../dashboard/role-management/index.jsx | 6 +-- .../__test__/LoginExpertComponent.test.js | 1 + src/core/components/DataTable.jsx | 4 +- src/core/data/apiRoutes.js | 2 +- 14 files changed, 94 insertions(+), 62 deletions(-) create mode 100644 src/components/dashboard/role-management/__test__/RoleManagementComponent.test.js diff --git a/public/locales/fa/app.json b/public/locales/fa/app.json index cea9212..38083ec 100644 --- a/public/locales/fa/app.json +++ b/public/locales/fa/app.json @@ -73,7 +73,8 @@ "Dashboard": { "dashboard_page": "داشبورد", "change_password": "تغییر رمز عبور", - "edit_profile": "ویرایش پروفایل" + "edit_profile": "ویرایش پروفایل", + "role_management_page": "مدیریت دسترسی" }, "MuiDatePicker": { "date_picker_birthday": "تاریخ" diff --git a/src/components/dashboard/role-management/Form/CreateForm/CreateContent.jsx b/src/components/dashboard/role-management/Form/CreateForm/CreateContent.jsx index 117e009..b646133 100644 --- a/src/components/dashboard/role-management/Form/CreateForm/CreateContent.jsx +++ b/src/components/dashboard/role-management/Form/CreateForm/CreateContent.jsx @@ -34,7 +34,7 @@ const CreateContent = ({mutate, fetchUrl, setOpenConfirmDialog}) => { permissions: [], }, validationSchema, onSubmit: (values, {setSubmitting}) => { const formData = new FormData(); - formData.append("name_en", values.name_en); + formData.append("name", values.name_en); formData.append("name_fa", values.name_fa); for (let i = 0; i < values.permissions.length; i++) { formData.append(`permissions[${i}]`, values.permissions[i]); @@ -42,7 +42,7 @@ const CreateContent = ({mutate, fetchUrl, setOpenConfirmDialog}) => { requestServer(ADD_ROLE_MANAGEMENT, 'post', { data: formData, - }).then((response) => { + }).then(() => { setOpenConfirmDialog(false) mutate(fetchUrl) update_notification() @@ -57,7 +57,7 @@ const CreateContent = ({mutate, fetchUrl, setOpenConfirmDialog}) => { <> {t("AddDialog.add")} - + { describe("Rendering", () => { - it('should see Dialog text', () => { - render( - - - - ) - const textElement = screen.queryByText("ویرایش") - expect(textElement).toBeInTheDocument() - }); it('should see Dialog text when mouse is over', async () => { render( ) - const textElement = screen.queryByText("حذف") - fireEvent.mouseOver(textElement); - const tooltipTextElement = screen.getByText("حذف"); - expect(tooltipTextElement).toBeInTheDocument(); + const textElement = screen.getByTestId("dialog_tooltip") + fireEvent.mouseOver(textElement) + await waitFor(()=>{ + expect(screen.getByText("حذف")).toBeInTheDocument(); + }) }); }) }) \ No newline at end of file diff --git a/src/components/dashboard/role-management/Form/DeleteForm/index.jsx b/src/components/dashboard/role-management/Form/DeleteForm/index.jsx index b2c14fa..b22a907 100644 --- a/src/components/dashboard/role-management/Form/DeleteForm/index.jsx +++ b/src/components/dashboard/role-management/Form/DeleteForm/index.jsx @@ -1,8 +1,7 @@ import {useTranslations} from "next-intl"; import {useState} from "react"; import { - Button, - Dialog, + Dialog, IconButton, Tooltip, } from "@mui/material"; import DeleteIcon from '@mui/icons-material/Delete'; @@ -16,18 +15,15 @@ const DeleteForm = ({rowId, fetchUrl, mutate}) => { return ( <> - + + diff --git a/src/components/dashboard/role-management/Form/UpdateForm/UpdateContent.jsx b/src/components/dashboard/role-management/Form/UpdateForm/UpdateContent.jsx index 23fa4e4..b149c85 100644 --- a/src/components/dashboard/role-management/Form/UpdateForm/UpdateContent.jsx +++ b/src/components/dashboard/role-management/Form/UpdateForm/UpdateContent.jsx @@ -59,7 +59,7 @@ const UpdateContent = ({mutate, row, fetchUrl, setOpenConfirmDialog}) => { <> {t("UpdateDialog.update")} - + { describe("Rendering",()=>{ - it('should see Dialog text', () => { - render( - - - - ) - const textElement = screen.queryByText("ویرایش") - expect(textElement).toBeInTheDocument() - }); - - it('should see Tooltip text when mouse over', () => { + it('should see Tooltip text when mouse over', async () => { render( ) - const buttonElement = screen.getByText("ویرایش"); + const buttonElement = screen.getByTestId("dialog_tooltip"); fireEvent.mouseOver(buttonElement); - const tooltipTextElement = screen.getByText("ویرایش"); - expect(tooltipTextElement).toBeInTheDocument(); + await waitFor(()=>{ + expect(screen.queryByText("ویرایش")).toBeVisible(); + }) }); }) }) \ No newline at end of file diff --git a/src/components/dashboard/role-management/Form/UpdateForm/index.jsx b/src/components/dashboard/role-management/Form/UpdateForm/index.jsx index a6d05c6..018fe32 100644 --- a/src/components/dashboard/role-management/Form/UpdateForm/index.jsx +++ b/src/components/dashboard/role-management/Form/UpdateForm/index.jsx @@ -1,8 +1,8 @@ -import {Button, Dialog, DialogTitle, Stack, Tooltip} from "@mui/material"; -import DataSaverOnIcon from "@mui/icons-material/DataSaverOn"; +import {Dialog, IconButton, Stack, Tooltip} from "@mui/material"; import {useTranslations} from "next-intl"; import {useState} from "react"; import UpdateContent from "@/components/dashboard/role-management/Form/UpdateForm/UpdateContent"; +import EditIcon from "@mui/icons-material/Edit"; const UpdateForm = ({mutate, fetchUrl, row}) => { const t = useTranslations(); @@ -10,19 +10,16 @@ const UpdateForm = ({mutate, fetchUrl, row}) => { return ( - - + + diff --git a/src/components/dashboard/role-management/RoleManagementComponent.jsx b/src/components/dashboard/role-management/RoleManagementComponent.jsx index 1aea1b7..0121c00 100644 --- a/src/components/dashboard/role-management/RoleManagementComponent.jsx +++ b/src/components/dashboard/role-management/RoleManagementComponent.jsx @@ -1,12 +1,12 @@ import DataTable from "@/core/components/DataTable"; import {GET_ROLE_MANAGEMENT} from "@/core/data/apiRoutes"; -import TableToolbar from "@/components/dashboard/role-management/TableToolbar"; import TableRowActions from "@/components/dashboard/role-management/TableRowActions"; import {Box, Typography} from "@mui/material"; import {useTranslations} from "next-intl"; import {useMemo} from "react"; import moment from "jalali-moment"; import MuiDatePicker from "@/core/components/MuiDatePicker"; +import TableToolbar from "@/components/dashboard/role-management/TableToolbar"; const RoleManagementComponent = () => { const t = useTranslations(); diff --git a/src/components/dashboard/role-management/TableToolbar.jsx b/src/components/dashboard/role-management/TableToolbar.jsx index c4cb13b..14dfef1 100644 --- a/src/components/dashboard/role-management/TableToolbar.jsx +++ b/src/components/dashboard/role-management/TableToolbar.jsx @@ -1,5 +1,5 @@ import {useTranslations} from "next-intl"; -import CreateForm from "./Form/CreateForm/index"; +import CreateForm from "@/components/dashboard/role-management/Form/CreateForm"; function TableToolbar({mutate, fetchUrl}) { const t = useTranslations(); diff --git a/src/components/dashboard/role-management/__test__/RoleManagementComponent.test.js b/src/components/dashboard/role-management/__test__/RoleManagementComponent.test.js new file mode 100644 index 0000000..2a50a99 --- /dev/null +++ b/src/components/dashboard/role-management/__test__/RoleManagementComponent.test.js @@ -0,0 +1,53 @@ +import {render, screen} from "@testing-library/react"; +import MockAppWithProviders from "../../../../../mocks/AppWithProvider"; +import RoleManagementComponent from "@/components/dashboard/role-management/RoleManagementComponent"; + +describe("Role Management", ()=>{ + describe("Rendering", ()=>{ + it('should see id title of data table', () => { + render( + + + + ) + const idEelement = screen.queryByText("کد یکتا") + expect(idEelement).toBeInTheDocument() + }); + it('should see name title of data table', () => { + render( + + + + ) + const nameEelement = screen.queryByText("نام انگلیسی") + expect(nameEelement).toBeInTheDocument() + }); + it('should see name_fa title of data table', () => { + render( + + + + ) + const name_faEelement = screen.queryByText("نام فارسی") + expect(name_faEelement).toBeInTheDocument() + }); + it('should see created at title of data table', () => { + render( + + + + ) + const createdEelement = screen.queryByText("تاریخ درخواست") + expect(createdEelement).toBeInTheDocument() + }); + it('should see updated at title of data table', () => { + render( + + + + ) + const updateEelement = screen.queryByText("تاریخ بروزرسانی") + expect(updateEelement).toBeInTheDocument() + }); + }) +}) \ No newline at end of file diff --git a/src/components/dashboard/role-management/index.jsx b/src/components/dashboard/role-management/index.jsx index 09f15bf..5251c8b 100644 --- a/src/components/dashboard/role-management/index.jsx +++ b/src/components/dashboard/role-management/index.jsx @@ -1,12 +1,12 @@ -import DashboardLayouts from "@/layouts/dashboardLayouts"; import RoleManagementComponent from "@/components/dashboard/role-management/RoleManagementComponent"; +import DashboardLayout from "@/layouts/DashboardLayout"; function DashboardRoleManagementComponent() { return ( - + - + ); } diff --git a/src/components/login-expert/__test__/LoginExpertComponent.test.js b/src/components/login-expert/__test__/LoginExpertComponent.test.js index d76e49d..695dbcb 100644 --- a/src/components/login-expert/__test__/LoginExpertComponent.test.js +++ b/src/components/login-expert/__test__/LoginExpertComponent.test.js @@ -124,6 +124,7 @@ describe('Login expert component from login page', () => { }) describe("validation", ()=>{ it("Disabled submit button until fields completed", async ()=> { + render( diff --git a/src/core/components/DataTable.jsx b/src/core/components/DataTable.jsx index 350f37c..aeac2c2 100644 --- a/src/core/components/DataTable.jsx +++ b/src/core/components/DataTable.jsx @@ -134,8 +134,8 @@ function DataTable(props) { renderTopToolbarCustomActions={({table}) => ( <> {props.enableCustomToolbar /* send condition */ - ? props.CustomToolbar /* send component */ - : ""} + ? /* send component */ + : ""} )} renderBottomToolbarCustomActions={({table}) => ( diff --git a/src/core/data/apiRoutes.js b/src/core/data/apiRoutes.js index 0155d4c..4d0231f 100644 --- a/src/core/data/apiRoutes.js +++ b/src/core/data/apiRoutes.js @@ -23,5 +23,5 @@ export const DELETE_ROLE_MANAGEMENT = BASE_URL + "/api/roles" export const GET_PERMISSIONS_LIST = - BASE_URL + "/api/roles/list" + BASE_URL + "/api/permissions/list" //role management \ No newline at end of file From db58573c0e61f86e7095efeba1299c936040662c Mon Sep 17 00:00:00 2001 From: Amin Ghasempoor Date: Tue, 10 Oct 2023 11:51:34 +0330 Subject: [PATCH 14/37] CFE-4 style create and update dialog --- .../role-management/Form/CreateForm/CreateContent.jsx | 3 ++- .../role-management/Form/UpdateForm/UpdateContent.jsx | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/components/dashboard/role-management/Form/CreateForm/CreateContent.jsx b/src/components/dashboard/role-management/Form/CreateForm/CreateContent.jsx index b646133..12523c2 100644 --- a/src/components/dashboard/role-management/Form/CreateForm/CreateContent.jsx +++ b/src/components/dashboard/role-management/Form/CreateForm/CreateContent.jsx @@ -57,7 +57,7 @@ const CreateContent = ({mutate, fetchUrl, setOpenConfirmDialog}) => { <> {t("AddDialog.add")} - + { error={formik.touched.permissions && Boolean(formik.errors.permissions)} onBlur={formik.handleBlur("permissions")} sx={{mt: 2}} + fullWidth > {formik.touched.permissions && formik.errors.permissions} diff --git a/src/components/dashboard/role-management/Form/UpdateForm/UpdateContent.jsx b/src/components/dashboard/role-management/Form/UpdateForm/UpdateContent.jsx index b149c85..a17c48d 100644 --- a/src/components/dashboard/role-management/Form/UpdateForm/UpdateContent.jsx +++ b/src/components/dashboard/role-management/Form/UpdateForm/UpdateContent.jsx @@ -91,6 +91,7 @@ const UpdateContent = ({mutate, row, fetchUrl, setOpenConfirmDialog}) => { error={formik.touched.permissions && Boolean(formik.errors.permissions)} onBlur={formik.handleBlur("permissions")} sx={{mt: 2}} + fullWidth > {formik.touched.permissions && formik.errors.permissions} From c95e52f33ce427cf6d64a496cb2ec47d44b7ec72 Mon Sep 17 00:00:00 2001 From: Amin Ghasempoor Date: Tue, 10 Oct 2023 13:09:48 +0330 Subject: [PATCH 15/37] CFE-4 role management icon --- src/core/data/sidebarMenu.jsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/core/data/sidebarMenu.jsx b/src/core/data/sidebarMenu.jsx index dfc7df7..57d97ed 100644 --- a/src/core/data/sidebarMenu.jsx +++ b/src/core/data/sidebarMenu.jsx @@ -1,4 +1,5 @@ import SpaceDashboardIcon from "@mui/icons-material/SpaceDashboard"; +import AccessibilityIcon from '@mui/icons-material/Accessibility'; const sidebarMenu = [ [ @@ -15,7 +16,7 @@ const sidebarMenu = [ name : "role_management", type: "page", route: "/dashboard/role-management", - icon: , + icon: , selected: false, permission: "all", }, From 3c9c9551058f21fa14a57eafd4a58ccdec242295 Mon Sep 17 00:00:00 2001 From: Amin Ghasempoor Date: Tue, 10 Oct 2023 13:12:15 +0330 Subject: [PATCH 16/37] CFE-4 permission manage role --- src/core/data/sidebarMenu.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/data/sidebarMenu.jsx b/src/core/data/sidebarMenu.jsx index 57d97ed..2aeea36 100644 --- a/src/core/data/sidebarMenu.jsx +++ b/src/core/data/sidebarMenu.jsx @@ -18,7 +18,7 @@ const sidebarMenu = [ route: "/dashboard/role-management", icon: , selected: false, - permission: "all", + permission: "manage_roles", }, ], ]; From 86cbefd60c36b921579dcc9f29f423a8def888e1 Mon Sep 17 00:00:00 2001 From: Amin Ghasempoor Date: Tue, 10 Oct 2023 14:56:57 +0330 Subject: [PATCH 17/37] CFE-4 write role management mock --- mocks/handler.js | 48 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/mocks/handler.js b/mocks/handler.js index 169b795..89071d4 100644 --- a/mocks/handler.js +++ b/mocks/handler.js @@ -1,4 +1,4 @@ -import {GET_PERMISSIONS_LIST, GET_USER_ROUTE,SET_USER_PASSWORD} from "@/core/data/apiRoutes"; +import {GET_PERMISSIONS_LIST, GET_ROLE_MANAGEMENT, GET_USER_ROUTE, SET_USER_PASSWORD} from "@/core/data/apiRoutes"; import {rest} from "msw"; export const handler = [ @@ -32,5 +32,51 @@ export const handler = [ } )) }), + rest.get(GET_ROLE_MANAGEMENT, (req, res, ctx) => { + return res( + ctx.json( + { + data : [ + { + created_at: "2023-10-01T07:20:07.000000Z", + guard_name: "api", + id: 1, + name: "admin", + name_fa: "ادمین", + permissions: [ + { + id: 1, + name: "manage_users", + }, + { + id: 2, + name: "manage_roles", + }, + ], + updated_at: "2023-10-10T07:38:12.000000Z" + }, + { + created_at: "2023-10-01T07:20:07.000000Z", + guard_name: "api", + id: 2, + name: "manager", + name_fa: "مدیر", + permissions: [ + { + id: 1, + name: "manage_users", + }, + { + id: 2, + name: "manage_roles", + }, + ], + updated_at: "2023-10-10T07:38:12.000000Z" + } + ] + } + ), + ); + }), ] \ No newline at end of file From 752e8a3662043a89621245ec872a98edae340b34 Mon Sep 17 00:00:00 2001 From: Amin Ghasempoor Date: Tue, 10 Oct 2023 15:36:25 +0330 Subject: [PATCH 18/37] CFE-4 correct act mistakes --- .../CreateForm/__test__/CreateContent.test.js | 37 ++++++++++------- .../Form/CreateForm/__test__/index.test.js | 14 ++++--- .../UpdateForm/__test__/UpdateContent.test.js | 40 +++++++++++-------- .../__test__/RoleManagementComponent.test.js | 32 ++++++++++----- 4 files changed, 77 insertions(+), 46 deletions(-) diff --git a/src/components/dashboard/role-management/Form/CreateForm/__test__/CreateContent.test.js b/src/components/dashboard/role-management/Form/CreateForm/__test__/CreateContent.test.js index 3e31b8f..610e224 100644 --- a/src/components/dashboard/role-management/Form/CreateForm/__test__/CreateContent.test.js +++ b/src/components/dashboard/role-management/Form/CreateForm/__test__/CreateContent.test.js @@ -4,45 +4,51 @@ import CreateContent from "@/components/dashboard/role-management/Form/CreateFor describe("Create Content component from Create Form Component in Role Management Component",()=>{ describe("Rendering", ()=>{ - it('should see AddDialog text in the top ', () => { + it('should see AddDialog text in the top ', async () => { render( ) const textElement = screen.queryByText("افزودن") - expect(textElement).toBeInTheDocument() + await waitFor(()=>{ + expect(textElement).toBeInTheDocument() + }) }); - it('should see name_en text', () => { + it('should see name_en text', async () => { render( ) const textElement = screen.queryByLabelText("نام انگلیسی") - expect(textElement).toBeInTheDocument() + await waitFor(()=>{ + expect(textElement).toBeInTheDocument() + }) }); - it('should see name_fa text', () => { + it('should see name_fa text', async () => { render( ) const textElement = screen.queryByLabelText("نام فارسی") - expect(textElement).toBeInTheDocument() + await waitFor(()=>{ + expect(textElement).toBeInTheDocument() + }) }); - it('should see name_fa text', () => { + it('should see name_fa text', async () => { render( ) const textElement = screen.queryByLabelText("نام فارسی") - expect(textElement).toBeInTheDocument() + await waitFor(()=>{ + expect(textElement).toBeInTheDocument() + }) }); - }) - describe("Behavior",()=>{ it('should see what fill in the name_en input', async () => { render( @@ -81,8 +87,7 @@ describe("Create Content component from Create Form Component in Role Management const checkboxElement = await screen.findAllByTestId("PermissionList-checkbox") expect(checkboxElement).toHaveLength(2) }); - - it('should get checked if permission list item get clicked', () => { + it('should get checked if permission list item get clicked', async () => { render( @@ -90,11 +95,15 @@ describe("Create Content component from Create Form Component in Role Management ) const checkboxElement = screen.getByLabelText("مدیریت کارتابل رییس اداره مسافری استان") - expect(checkboxElement).not.toBeChecked(); + await waitFor(()=>{ + expect(checkboxElement).not.toBeChecked(); + }) fireEvent.click(checkboxElement); - expect(checkboxElement).toBeChecked(); + await waitFor(()=>{ + expect(checkboxElement).toBeChecked(); + }) }); }) describe("Validation",()=>{ diff --git a/src/components/dashboard/role-management/Form/CreateForm/__test__/index.test.js b/src/components/dashboard/role-management/Form/CreateForm/__test__/index.test.js index b4f0e82..7bc9db2 100644 --- a/src/components/dashboard/role-management/Form/CreateForm/__test__/index.test.js +++ b/src/components/dashboard/role-management/Form/CreateForm/__test__/index.test.js @@ -1,21 +1,23 @@ -import {fireEvent, queryByText, render, screen} from "@testing-library/react"; +import {fireEvent, queryByText, render, screen, waitFor} from "@testing-library/react"; import CreateForm from "@/components/dashboard/role-management/Form/CreateForm"; import MockAppWithProviders from "../../../../../../../mocks/AppWithProvider"; describe("Create Form Component from Role Management Component",()=>{ describe("Rendering",()=>{ - it('should see Dialog text', () => { + it('should see Dialog text', async () => { render( ) const textElement = screen.queryByText("افزودن") - expect(textElement).toBeInTheDocument() + await waitFor(()=>{ + expect(textElement).toBeInTheDocument() + }) }); - it('should see Tooltip text when mouse over', () => { + it('should see Tooltip text when mouse over', async () => { render( @@ -24,7 +26,9 @@ describe("Create Form Component from Role Management Component",()=>{ const buttonElement = screen.getByText("افزودن"); fireEvent.mouseOver(buttonElement); const tooltipTextElement = screen.getByText("افزودن"); - expect(tooltipTextElement).toBeInTheDocument(); + await waitFor(()=>{ + expect(tooltipTextElement).toBeInTheDocument() + }) }); }) }) \ No newline at end of file diff --git a/src/components/dashboard/role-management/Form/UpdateForm/__test__/UpdateContent.test.js b/src/components/dashboard/role-management/Form/UpdateForm/__test__/UpdateContent.test.js index d36fe05..caaa470 100644 --- a/src/components/dashboard/role-management/Form/UpdateForm/__test__/UpdateContent.test.js +++ b/src/components/dashboard/role-management/Form/UpdateForm/__test__/UpdateContent.test.js @@ -36,7 +36,7 @@ describe("Create Content component from Create Form Component in Role Management ) const textElement = screen.queryByText("ویرایش") - act(()=>{ + await waitFor(()=>{ expect(textElement).toBeInTheDocument() }) }); @@ -47,7 +47,7 @@ describe("Create Content component from Create Form Component in Role Management ) const textElement = screen.queryByLabelText("نام انگلیسی") - act(()=>{ + await waitFor(()=>{ expect(textElement).toBeInTheDocument() }) }); @@ -58,29 +58,29 @@ describe("Create Content component from Create Form Component in Role Management ) const textElement = screen.queryByLabelText("نام فارسی") - act(()=>{ + await waitFor(()=>{ expect(textElement).toBeInTheDocument() }) }); - it('should see update text in the submit button ', () => { + it('should see update text in the submit button ', async () => { render( ) const buttonElement = screen.queryByText("ثبت") - act(()=>{ + await waitFor(()=>{ expect(buttonElement).toBeInTheDocument() }) }); - it('should see cancel text in the cancel button ', () => { + it('should see cancel text in the cancel button ', async () => { render( ) const buttonElement = screen.queryByText("بستن") - act(()=>{ + await waitFor(()=>{ expect(buttonElement).toBeInTheDocument() }) }); @@ -96,7 +96,7 @@ describe("Create Content component from Create Form Component in Role Management const nameEnglishInput = screen.getByLabelText('نام انگلیسی'); fireEvent.change(nameEnglishInput, {target: {value: 'testuser'}}); - await act(()=>{ + await waitFor(()=>{ // Simulate user input expect(nameEnglishInput).toHaveValue('testuser'); }) @@ -110,7 +110,7 @@ describe("Create Content component from Create Form Component in Role Management const nameFarsiInput = screen.getByLabelText('نام فارسی'); fireEvent.change(nameFarsiInput, {target: {value: 'testuser'}}); - await act(()=>{ + await waitFor(()=>{ // Simulate user input expect(nameFarsiInput).toHaveValue('testuser'); }) @@ -122,35 +122,43 @@ describe("Create Content component from Create Form Component in Role Management ) const checkboxElement = await screen.findAllByTestId("PermissionList-checkbox") - expect(checkboxElement).toHaveLength(2) + await waitFor(()=>{ + expect(checkboxElement).toHaveLength(2) + }) }); - it('should see the value of the name_en that pass to input value', () => { + it('should see the value of the name_en that pass to input value', async () => { render( ) const name_enElement = screen.getByLabelText("نام انگلیسی") - expect(name_enElement).toHaveValue("manage_passenger_office_navgan") + await waitFor(()=>{ + expect(name_enElement).toHaveValue("manage_passenger_office_navgan") + }) }); - it('should see the value of the name_fa that pass to input value', () => { + it('should see the value of the name_fa that pass to input value', async () => { render( ) const name_enElement = screen.getByLabelText("نام فارسی") - expect(name_enElement).toHaveValue("مدیریت کارتابل رییس اداره مسافری استان") + await waitFor(()=>{ + expect(name_enElement).toHaveValue("مدیریت کارتابل رییس اداره مسافری استان") + }) }); - it('should get checked if the id exists in permission lists', () => { + it('should get checked if the id exists in permission lists', async () => { render( ) const checkboxElement = screen.getByLabelText("مدیریت کارتابل رییس اداره مسافری استان") + await waitFor(()=>{ + expect(checkboxElement).toBeChecked(); + }) - expect(checkboxElement).toBeChecked(); }); }) describe("Validation", ()=>{ diff --git a/src/components/dashboard/role-management/__test__/RoleManagementComponent.test.js b/src/components/dashboard/role-management/__test__/RoleManagementComponent.test.js index 2a50a99..a60dc01 100644 --- a/src/components/dashboard/role-management/__test__/RoleManagementComponent.test.js +++ b/src/components/dashboard/role-management/__test__/RoleManagementComponent.test.js @@ -1,53 +1,63 @@ -import {render, screen} from "@testing-library/react"; +import {render, screen, waitFor} from "@testing-library/react"; import MockAppWithProviders from "../../../../../mocks/AppWithProvider"; import RoleManagementComponent from "@/components/dashboard/role-management/RoleManagementComponent"; describe("Role Management", ()=>{ describe("Rendering", ()=>{ - it('should see id title of data table', () => { + it('should see id title of data table', async () => { render( ) const idEelement = screen.queryByText("کد یکتا") - expect(idEelement).toBeInTheDocument() + await waitFor(()=>{ + expect(idEelement).toBeInTheDocument() + }) }); - it('should see name title of data table', () => { + it('should see name title of data table', async () => { render( ) const nameEelement = screen.queryByText("نام انگلیسی") - expect(nameEelement).toBeInTheDocument() + await waitFor(()=>{ + expect(nameEelement).toBeInTheDocument() + }) }); - it('should see name_fa title of data table', () => { + it('should see name_fa title of data table', async () => { render( ) const name_faEelement = screen.queryByText("نام فارسی") - expect(name_faEelement).toBeInTheDocument() + await waitFor(()=>{ + expect(name_faEelement).toBeInTheDocument() + }) }); - it('should see created at title of data table', () => { + it('should see created at title of data table', async () => { render( ) const createdEelement = screen.queryByText("تاریخ درخواست") - expect(createdEelement).toBeInTheDocument() + await waitFor(()=>{ + expect(createdEelement).toBeInTheDocument() + }) }); - it('should see updated at title of data table', () => { + it('should see updated at title of data table', async () => { render( ) const updateEelement = screen.queryByText("تاریخ بروزرسانی") - expect(updateEelement).toBeInTheDocument() + await waitFor(()=>{ + expect(updateEelement).toBeInTheDocument() + }) }); }) }) \ No newline at end of file From c265010d7f6f8d7dcaba79ac8b96d490d0a01274 Mon Sep 17 00:00:00 2001 From: AmirHossein Mahmoodi Date: Tue, 10 Oct 2023 16:06:17 +0330 Subject: [PATCH 19/37] CFE-24 fixed any bug and fixed performance datatable --- src/core/components/DataTable.jsx | 61 +++++++++++++++---------------- 1 file changed, 29 insertions(+), 32 deletions(-) diff --git a/src/core/components/DataTable.jsx b/src/core/components/DataTable.jsx index aeac2c2..adedf3d 100644 --- a/src/core/components/DataTable.jsx +++ b/src/core/components/DataTable.jsx @@ -9,56 +9,45 @@ import useRequest from "@/lib/app/hooks/useRequest"; function DataTable(props) { const requestServer = useRequest({auth: true}) - const fetcher = (...args) => { - return requestServer(args, 'get', { - pending: false, - success: {notification: {show: false}} - }).then((response) => { - setRowCount(response.data.meta.totalRowCount); - return response.data.data; - }).catch(() => { - }) - }; const t = useTranslations(); const {languageApp, languageList} = useLanguage(); const [columnFilters, setColumnFilters] = useState([]); const [sorting, setSorting] = useState([]); 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]: ""} ); - for (var key in list) { - var nestedObj = list[key]; - for (var nestedKey in nestedObj) { + for (const key in list) { + const nestedObj = list[key]; + for (const nestedKey in nestedObj) { output[nestedKey] = nestedObj[nestedKey]; } } return output; }); - const [updateTime, setupdateTime] = useState( + const [updateTime, setUpdateTime] = useState( moment().format("HH:mm | jYYYY/jM/jD") ); const tableLocalization = useMemo( () => - languageList.find((item) => item.key == languageApp).tableLocalization, + languageList.find((item) => item.key === languageApp).tableLocalization, [languageApp, languageList] ); const fetchUrl = useMemo(() => { - const url = new URL(props.tableUrl); - url.searchParams.set( + const params = new URLSearchParams(); + params.set( "start", `${pagination.pageIndex * pagination.pageSize}` ); const filters = columnFilters.map((filter) => { let datatype; for (const i in props.columns) { - if (props.columns[i].id == filter.id) { + if (props.columns[i].id === filter.id) { datatype = props.columns[i].datatype; } } @@ -68,10 +57,10 @@ function DataTable(props) { datatype: datatype, }; }); - url.searchParams.set("size", pagination.pageSize); - url.searchParams.set("filters", JSON.stringify(filters ?? [])); - url.searchParams.set("sorting", JSON.stringify(sorting ?? [])); - return url; + params.set("size", pagination.pageSize); + params.set("filters", JSON.stringify(filters ?? [])); + params.set("sorting", JSON.stringify(sorting ?? [])); + return `${props.tableUrl}?${params}`; }, [ props.tableUrl, columnFilters, @@ -81,20 +70,27 @@ function DataTable(props) { props.columns, ]); - const {data, isValidating, mutate} = useSWR(fetchUrl, fetcher, { - revalidateIfStale: false, - revalidateOnFocus: false, - revalidateOnReconnect: false - }); + const {data, isValidating, mutate} = useSWR(fetchUrl, (...args) => + requestServer(args, 'get', { + pending: false, + success: {notification: {show: false}} + }).then((response) => response.data).catch(() => { + }) + , { + revalidateIfStale: false, + revalidateOnFocus: false, + revalidateOnReconnect: true, + keepPreviousData: true + }); useEffect(() => { - setupdateTime(moment().format("HH:mm | jYYYY/jM/jD")); + setUpdateTime(moment().format("HH:mm | jYYYY/jM/jD")); }, [isValidating, languageApp]); return ( ( <> {props.enableCustomToolbar /* send condition */ - ? /* send component */ + ? /* send component */ : ""} )} @@ -159,6 +155,7 @@ function DataTable(props) { )} state={{ + showProgressBars: isValidating, columnFilters, columnFilterFns, pagination, From 5c870008a8c22c367526f80684ca7fb6b31e3a5c Mon Sep 17 00:00:00 2001 From: Yasiu1376 Date: Tue, 10 Oct 2023 16:15:10 +0330 Subject: [PATCH 20/37] CFE-20 implement middlewarecomponent and middleware test --- mocks/handler.js | 5 ++- package.json | 2 +- public/locales/fa/app.json | 2 +- .../Middleware/RolePermissionComponent.jsx | 33 ++++++++++++++++ .../Middleware/WithAuthComponent.jsx | 36 +++++++++++++++++ .../Middleware/WithoutAuthComponent.jsx | 25 ++++++++++++ .../__test__/RolePermissionComponent.test.js | 23 +++++++++++ .../__test__/WithAuthComponent.test.js | 39 +++++++++++++++++++ .../__test__/WithoutAuthComponent.test.js | 37 ++++++++++++++++++ src/middlewares/RolePermission.jsx | 33 +--------------- src/middlewares/WithAuth.jsx | 37 ++---------------- src/middlewares/WithoutAuth.jsx | 36 ++++------------- .../__test__/RolePermission.test.js | 34 ++++++++++++++++ 13 files changed, 245 insertions(+), 97 deletions(-) create mode 100644 src/core/components/Middleware/RolePermissionComponent.jsx create mode 100644 src/core/components/Middleware/WithAuthComponent.jsx create mode 100644 src/core/components/Middleware/WithoutAuthComponent.jsx create mode 100644 src/core/components/Middleware/__test__/RolePermissionComponent.test.js create mode 100644 src/core/components/Middleware/__test__/WithAuthComponent.test.js create mode 100644 src/core/components/Middleware/__test__/WithoutAuthComponent.test.js create mode 100644 src/middlewares/__test__/RolePermission.test.js diff --git a/mocks/handler.js b/mocks/handler.js index 65f5398..7e1d193 100644 --- a/mocks/handler.js +++ b/mocks/handler.js @@ -5,7 +5,10 @@ export const handler = [ rest.get(GET_USER_ROUTE, (req, res, ctx) => { return res(ctx.json({ data:{ - id: 10 + id: 10, + permissions:[ + "manage_users" + ], } })) }), diff --git a/package.json b/package.json index 5d6417a..f82c9f2 100644 --- a/package.json +++ b/package.json @@ -64,7 +64,7 @@ "jest": "^29.7.0", "jest-environment-jsdom": "^29.7.0", "msw": "^1.3.1", - "next-router-mock": "^0.9.9", + "next-router-mock": "^0.9.10", "run-script-os": "^1.1.6" } } diff --git a/public/locales/fa/app.json b/public/locales/fa/app.json index 3613a1c..4c242dd 100644 --- a/public/locales/fa/app.json +++ b/public/locales/fa/app.json @@ -40,7 +40,7 @@ "typography_your_login_is_valid_and_you_do_not_need_to_login_again": "شما دسترسی لازم به این صفحه را دارید نیاز به ورود مجدد نیست.", "typography_your_access_to_this_page_has_expired_Please_login_again": "دسترسی شما منقضی شده است لطفا دوباره ورود نمایید.", "typography_redirect_to": "درحال رفتن به صفحه", - "typography_routing_previuos_page": "صفحه قبل...", + "typography_routing_previuos_page": " قبل...", "typography_routing_dashbaord_page": "داشبورد..." }, "Permission": { diff --git a/src/core/components/Middleware/RolePermissionComponent.jsx b/src/core/components/Middleware/RolePermissionComponent.jsx new file mode 100644 index 0000000..869f78a --- /dev/null +++ b/src/core/components/Middleware/RolePermissionComponent.jsx @@ -0,0 +1,33 @@ +import { Button, Typography } from "@mui/material"; +import { useTranslations } from "next-intl"; +import { NextLinkComposed } from "@/core/components/LinkRouting"; +import Message from "@/core/components/Messages"; + +const RolePermissionComponent = () => { + const t = useTranslations(); + + return ( + + {t("Permission.typography_you_dont_have_access")} + + } + actions={ + <> + + + } + /> + ); +}; + +export default RolePermissionComponent; diff --git a/src/core/components/Middleware/WithAuthComponent.jsx b/src/core/components/Middleware/WithAuthComponent.jsx new file mode 100644 index 0000000..9a6ff5d --- /dev/null +++ b/src/core/components/Middleware/WithAuthComponent.jsx @@ -0,0 +1,36 @@ +import { Button, Typography } from "@mui/material"; +import { useRouter } from "next/router"; +import Message from "@/core/components/Messages"; +import { NextLinkComposed } from "@/core/components/LinkRouting"; +import {useTranslations} from "next-intl"; + +const WithAuthComponent = () => { + const router = useRouter(); + const t = useTranslations(); + + return ( + + {t("Authorization.typography_your_access_to_this_page_has_expired_Please_login_again")} + + } + actions={ + <> + + + } + /> + ); +}; + +export default WithAuthComponent; diff --git a/src/core/components/Middleware/WithoutAuthComponent.jsx b/src/core/components/Middleware/WithoutAuthComponent.jsx new file mode 100644 index 0000000..7dbb38d --- /dev/null +++ b/src/core/components/Middleware/WithoutAuthComponent.jsx @@ -0,0 +1,25 @@ +import {Button, Stack, Typography} from "@mui/material"; +import Message from "@/core/components/Messages"; +import {useTranslations} from "next-intl"; +const WithoutAuthComponent = ({ backUrlDecodedPath }) => { + const t = useTranslations(); + + return ( + + + {t("Authorization.typography_your_login_is_valid_and_you_do_not_need_to_login_again")} + + + {t("Authorization.typography_redirect_to")}{" "} + {backUrlDecodedPath + ? t("Authorization.typography_routing_previuos_page") + : t("Authorization.typography_routing_dashbaord_page")} + + + } + /> + ); +}; +export default WithoutAuthComponent; diff --git a/src/core/components/Middleware/__test__/RolePermissionComponent.test.js b/src/core/components/Middleware/__test__/RolePermissionComponent.test.js new file mode 100644 index 0000000..44a583d --- /dev/null +++ b/src/core/components/Middleware/__test__/RolePermissionComponent.test.js @@ -0,0 +1,23 @@ +import { render, screen } from '@testing-library/react'; +import MockAppWithProviders from "../../../../../mocks/AppWithProvider"; +import RolePermissionComponent from "@/core/components/Middleware/RolePermissionComponent"; + +describe('RolePermissionComponent From RolePermissionComponent page', () => { + describe('Rendering', () => { + it('should render the error message when user dont have permission', () => { + render(); + + expect(screen.queryByText('شما دسترسی لازم برای مشاهده این صفحه را ندارید!')).toBeInTheDocument(); + + }); + + it('should render the back dashboard button when user dont have permission', () => { + render(); + + const backButton = screen.queryByText('بازگشت به صفحه اصلی'); + + expect(backButton).toBeInTheDocument(); + + }); + }); +}); diff --git a/src/core/components/Middleware/__test__/WithAuthComponent.test.js b/src/core/components/Middleware/__test__/WithAuthComponent.test.js new file mode 100644 index 0000000..a43e20f --- /dev/null +++ b/src/core/components/Middleware/__test__/WithAuthComponent.test.js @@ -0,0 +1,39 @@ +import { render, screen, fireEvent, waitFor } from '@testing-library/react'; +import MockAppWithProviders from "../../../../../mocks/AppWithProvider"; +import WithAuthComponent from "@/core/components/Middleware/WithAuthComponent"; +import mockRouter from 'next-router-mock'; + +describe('WithAuthComponent From WithAuthMiddleware', () => { + describe('Rendering', () => { + it('should see expired text in render component', () => { + render(); + + const authText = screen.queryByText('دسترسی شما منقضی شده است لطفا دوباره ورود نمایید.'); + + expect(authText).toBeInTheDocument(); + }); + + it('should see login button in render component', () => { + render(); + + const loginButton = screen.queryByText('ورود'); + + expect(loginButton).toBeInTheDocument(); + }); + }); + describe('behavior', () => { + it('should see if router updated in login button', () => { + + mockRouter.query.back_url = 'back_url' + render(); + + const loginButton = screen.queryByText('ورود'); + + fireEvent.click(loginButton); + + expect(mockRouter).toMatchObject({ + query: { back_url: "back_url" }, + }); + }); + }); +}); diff --git a/src/core/components/Middleware/__test__/WithoutAuthComponent.test.js b/src/core/components/Middleware/__test__/WithoutAuthComponent.test.js new file mode 100644 index 0000000..520d7bd --- /dev/null +++ b/src/core/components/Middleware/__test__/WithoutAuthComponent.test.js @@ -0,0 +1,37 @@ +import {render, screen, waitFor} from '@testing-library/react'; +import WithoutAuthComponent from "@/core/components/Middleware/WithoutAuthComponent"; +import MockAppWithProviders from "../../../../../mocks/AppWithProvider"; +import WithoutAuthMiddleware from "@/middlewares/WithoutAuth"; + +describe('WithoutAuthComponent From WithoutAuthMiddleware', () => { + describe('Rendering', () => { + it('should render the message with correct text when backUrlDecodedPath is not provided', async () => { + + localStorage.setItem("_token", 'token'); + + render(); + + await waitFor(()=>{ + expect(screen.queryByText('شما دسترسی لازم به این صفحه را دارید نیاز به ورود مجدد نیست.')).toBeInTheDocument(); + + expect(screen.queryByText('درحال رفتن به صفحه داشبورد...')).toBeInTheDocument(); + }) + + }); + + it('should render the message with a different text when backUrlDecodedPath is provided', async() => { + render(); + + localStorage.setItem("_token", 'token'); + + render(); + + await waitFor(()=>{ + + expect(screen.queryByText('درحال رفتن به صفحه قبل...')).toBeInTheDocument(); + }) + + + }); + }); +}); diff --git a/src/middlewares/RolePermission.jsx b/src/middlewares/RolePermission.jsx index 67fc3f1..4b1cc8a 100644 --- a/src/middlewares/RolePermission.jsx +++ b/src/middlewares/RolePermission.jsx @@ -1,43 +1,14 @@ -import {NextLinkComposed} from "@/core/components/LinkRouting"; -import Message from "@/core/components/Messages"; import useUser from "@/lib/app/hooks/useUser"; -import {Button, Typography} from "@mui/material"; -import {useTranslations} from "next-intl"; +import RolePermissionComponent from "@/core/components/Middleware/RolePermissionComponent"; const RolePermissionMiddleware = ({children, requiredPermissions}) => { const {user} = useUser(); - const t = useTranslations(); const hasPermission = requiredPermissions.some((permission) => user?.permissions?.includes(permission) ); - if (!hasPermission) { - return ( - - {t("Permission.typography_you_dont_have_access")} - - } - actions={ - <> - - - } - /> - ); - } - - return <>{children}; + return !hasPermission ? : <>{children}; }; export default RolePermissionMiddleware; diff --git a/src/middlewares/WithAuth.jsx b/src/middlewares/WithAuth.jsx index b20d8b3..d3b0493 100644 --- a/src/middlewares/WithAuth.jsx +++ b/src/middlewares/WithAuth.jsx @@ -1,42 +1,11 @@ -import {NextLinkComposed} from "@/core/components/LinkRouting"; -import Message from "@/core/components/Messages"; import useUser from "@/lib/app/hooks/useUser"; -import {Button, Typography} from "@mui/material"; -import {useTranslations} from "next-intl"; -import {useRouter} from "next/router"; +import WithAuthComponent from "@/core/components/Middleware/WithAuthComponent"; + const WithAuthMiddleware = ({children}) => { const {isAuth} = useUser(); - const t = useTranslations(); - const router = useRouter(); - if (!isAuth) - return ( - - {t( - "Authorization.typography_your_access_to_this_page_has_expired_Please_login_again" - )} - - } - actions={ - <> - - - } - /> - ); - return <>{children}; + return isAuth ? <>{children} : ; }; export default WithAuthMiddleware; diff --git a/src/middlewares/WithoutAuth.jsx b/src/middlewares/WithoutAuth.jsx index 594ffc5..7fbcba6 100644 --- a/src/middlewares/WithoutAuth.jsx +++ b/src/middlewares/WithoutAuth.jsx @@ -1,19 +1,13 @@ -import Message from "@/core/components/Messages"; import useUser from "@/lib/app/hooks/useUser"; -import {Stack, Typography} from "@mui/material"; -import {useTranslations} from "next-intl"; -import {useSearchParams} from "next/navigation"; import {useRouter} from "next/router"; import {useEffect} from "react"; +import WithoutAuthComponent from "@/core/components/Middleware/WithoutAuthComponent"; const WithoutAuthMiddleware = ({children}) => { const {isAuth} = useUser(); - const t = useTranslations(); const router = useRouter(); - // gettin url query - const searchParams = useSearchParams(); - const backUrlDecodedPath = searchParams.get("back_url"); + const backUrlDecodedPath = router.query?.back_url; useEffect(() => { if (!isAuth) return; @@ -30,27 +24,11 @@ const WithoutAuthMiddleware = ({children}) => { }; }, [isAuth]); - if (isAuth) - return ( - - - {t( - "Authorization.typography_your_login_is_valid_and_you_do_not_need_to_login_again" - )} - - - {t("Authorization.typography_redirect_to")}{" "} - {backUrlDecodedPath - ? t("Authorization.typography_routing_previuos_page") - : t("Authorization.typography_routing_dashbaord_page")} - - - } - /> - ); - return <>{children}; + return isAuth ? ( + + ) : ( + <>{children} + ); }; export default WithoutAuthMiddleware; diff --git a/src/middlewares/__test__/RolePermission.test.js b/src/middlewares/__test__/RolePermission.test.js new file mode 100644 index 0000000..f37796f --- /dev/null +++ b/src/middlewares/__test__/RolePermission.test.js @@ -0,0 +1,34 @@ +import {render, screen, waitFor} from '@testing-library/react'; +import MockAppWithProviders from "../../../mocks/AppWithProvider"; +import RolePermissionMiddleware from "@/middlewares/RolePermission"; + + +describe('RolePermissionMiddleware From RolePermissionMiddleware', () => { + describe('Behavior', () => { + it('show related child if permission exist', async () => { + + const requiredPermissions = ["manage_users"]; + render({'children'}); + + await waitFor(()=>{ + expect(screen.queryByText('children')).toBeInTheDocument(); + + }) + + }); + it('show related error if permission not exist', async () => { + + const requiredPermissions = ["other-permission"]; + + render(); + + await waitFor(()=>{ + + expect(screen.queryByText('شما دسترسی لازم برای مشاهده این صفحه را ندارید!')).toBeInTheDocument(); + + }) + + + }); + }); +}); From d709cc8665c971e4265e6072e6ba77840a443420 Mon Sep 17 00:00:00 2001 From: Amin Ghasempoor Date: Tue, 10 Oct 2023 16:15:22 +0330 Subject: [PATCH 21/37] CFE-4 add meta in role management mock --- mocks/handler.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/mocks/handler.js b/mocks/handler.js index 89071d4..c312dfc 100644 --- a/mocks/handler.js +++ b/mocks/handler.js @@ -73,7 +73,10 @@ export const handler = [ ], updated_at: "2023-10-10T07:38:12.000000Z" } - ] + ], + meta : { + totalRowCount : 2 + } } ), ); From 76a0625ee93137839774a06317cbdea10d9697cd Mon Sep 17 00:00:00 2001 From: Amin Ghasempoor Date: Tue, 10 Oct 2023 17:04:06 +0330 Subject: [PATCH 22/37] CFE-4 permission list loading --- public/locales/fa/app.json | 6 +- .../Form/CreateForm/CreateContent.jsx | 58 ++++++++++-------- .../role-management/Form/CreateForm/index.jsx | 2 +- .../Form/UpdateForm/UpdateContent.jsx | 60 +++++++++++-------- src/lib/app/hooks/usePermissions.jsx | 4 +- 5 files changed, 74 insertions(+), 56 deletions(-) diff --git a/public/locales/fa/app.json b/public/locales/fa/app.json index 38083ec..d52b977 100644 --- a/public/locales/fa/app.json +++ b/public/locales/fa/app.json @@ -34,7 +34,7 @@ "dashboard": "داشبورد", "change-password": "تغییر رمز عبور", "edit-profile": "ویرایش پروفایل", - "role-management": "مدیریت دسترسی", + "role-management": "مدیریت نقش ها", "admin": "مدیریت" }, "secondary": { @@ -74,7 +74,7 @@ "dashboard_page": "داشبورد", "change_password": "تغییر رمز عبور", "edit_profile": "ویرایش پروفایل", - "role_management_page": "مدیریت دسترسی" + "role_management_page": "مدیریت نقش ها" }, "MuiDatePicker": { "date_picker_birthday": "تاریخ" @@ -157,6 +157,7 @@ "type_id": "نوع کاربر", "navgan_id": "کد ناوگان", "button-cancel": "انصراف", + "loading_permissions_list": "درحال دریافت لیست دسترسی ها", "button-add": "ثبت" }, "UpdateDialog": { @@ -179,6 +180,7 @@ "permission_min_error": "حداقل باید یک دسترسی انتخاب شود", "type_id": "نوع کاربر", "navgan_id": "کد ناوگان", + "loading_permissions_list": "درحال دریافت لیست دسترسی ها", "button-update": "ثبت" }, "DeleteDialog": { diff --git a/src/components/dashboard/role-management/Form/CreateForm/CreateContent.jsx b/src/components/dashboard/role-management/Form/CreateForm/CreateContent.jsx index 12523c2..db28f49 100644 --- a/src/components/dashboard/role-management/Form/CreateForm/CreateContent.jsx +++ b/src/components/dashboard/role-management/Form/CreateForm/CreateContent.jsx @@ -1,6 +1,6 @@ import { Button, - Checkbox, + Checkbox, CircularProgress, DialogActions, DialogContent, DialogTitle, FormControl, @@ -9,7 +9,7 @@ import { FormLabel, Grid, Stack, - TextField + TextField, Typography } from "@mui/material"; import {useTranslations} from "next-intl"; import useRequest from "@/lib/app/hooks/useRequest"; @@ -21,7 +21,7 @@ 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 {permissions_list, isLoading} = usePermissions() const validationSchema = Yup.object().shape({ name_en: Yup.string().required(t("AddDialog.name_en_error")), name_fa: Yup.string().required(t("AddDialog.name_fa_error")), @@ -95,30 +95,38 @@ const CreateContent = ({mutate, fetchUrl, setOpenConfirmDialog}) => { fullWidth > {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)) + {isLoading ? + + + {t("AddDialog.loading_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} - /> + label={permission.name_fa} + /> + + ))} + - )) - ) : null} - + ) + } diff --git a/src/components/dashboard/role-management/Form/CreateForm/index.jsx b/src/components/dashboard/role-management/Form/CreateForm/index.jsx index 8cbe859..2265035 100644 --- a/src/components/dashboard/role-management/Form/CreateForm/index.jsx +++ b/src/components/dashboard/role-management/Form/CreateForm/index.jsx @@ -24,7 +24,7 @@ const CreateForm = ({mutate, fetchUrl}) => { {t("AddDialog.add")} - diff --git a/src/components/dashboard/role-management/Form/UpdateForm/UpdateContent.jsx b/src/components/dashboard/role-management/Form/UpdateForm/UpdateContent.jsx index a17c48d..0815356 100644 --- a/src/components/dashboard/role-management/Form/UpdateForm/UpdateContent.jsx +++ b/src/components/dashboard/role-management/Form/UpdateForm/UpdateContent.jsx @@ -1,6 +1,7 @@ import { + Box, Button, - Checkbox, DialogActions, + Checkbox, CircularProgress, DialogActions, DialogContent, DialogTitle, FormControl, FormControlLabel, @@ -8,7 +9,7 @@ import { FormLabel, Grid, Stack, - TextField + TextField, Typography } from "@mui/material"; import {useFormik} from "formik"; import {UPDATE_ROLE_MANAGEMENT} from "@/core/data/apiRoutes"; @@ -22,7 +23,7 @@ const UpdateContent = ({mutate, row, fetchUrl, setOpenConfirmDialog}) => { const t = useTranslations(); const requestServer = useRequest({auth: true}) const {update_notification} = useNotification() - const {permissions_list} = usePermissions() + const {permissions_list, isLoading} = usePermissions() const validationSchema = Yup.object().shape({ name_en: Yup.string().required(t("UpdateDialog.name_en_error")), @@ -94,30 +95,37 @@ const UpdateContent = ({mutate, row, fetchUrl, setOpenConfirmDialog}) => { fullWidth > {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)) - } - }} + {isLoading ? + + + {t("UpdateDialog.loading_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} /> - } - label={permission.name_fa} - /> - - )) - ) : null} - + + ))} + + + )} diff --git a/src/lib/app/hooks/usePermissions.jsx b/src/lib/app/hooks/usePermissions.jsx index 0e6aa07..8d45fd2 100644 --- a/src/lib/app/hooks/usePermissions.jsx +++ b/src/lib/app/hooks/usePermissions.jsx @@ -11,7 +11,7 @@ const usePermissions = () => { }) }; - const {data} = useSWR(GET_PERMISSIONS_LIST, fetcher, { + const {data, isLoading} = useSWR(GET_PERMISSIONS_LIST, fetcher, { revalidateIfStale: false, revalidateOnFocus: false, revalidateOnReconnect: false @@ -20,6 +20,6 @@ const usePermissions = () => { //swr config // render data - return {permissions_list} + return {permissions_list, isLoading} } export default usePermissions; \ No newline at end of file From 48cb2ce8b154990554334c12a8150b0689ef3101 Mon Sep 17 00:00:00 2001 From: Amin Ghasempoor Date: Tue, 10 Oct 2023 17:06:35 +0330 Subject: [PATCH 23/37] CFE-4 data table keep previous data --- src/core/components/DataTable.jsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/core/components/DataTable.jsx b/src/core/components/DataTable.jsx index aeac2c2..d0ccd1d 100644 --- a/src/core/components/DataTable.jsx +++ b/src/core/components/DataTable.jsx @@ -84,7 +84,8 @@ function DataTable(props) { const {data, isValidating, mutate} = useSWR(fetchUrl, fetcher, { revalidateIfStale: false, revalidateOnFocus: false, - revalidateOnReconnect: false + revalidateOnReconnect: false, + keepPreviousData : true }); useEffect(() => { From 40beac6e9e27cf1ef80b24873a09f5b54b7076f5 Mon Sep 17 00:00:00 2001 From: Amin Ghasempoor Date: Tue, 10 Oct 2023 17:09:02 +0330 Subject: [PATCH 24/37] CFE-4 usePermission keep previous data --- src/core/components/DataTable.jsx | 1 - src/lib/app/hooks/usePermissions.jsx | 3 ++- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/components/DataTable.jsx b/src/core/components/DataTable.jsx index d0ccd1d..213f924 100644 --- a/src/core/components/DataTable.jsx +++ b/src/core/components/DataTable.jsx @@ -85,7 +85,6 @@ function DataTable(props) { revalidateIfStale: false, revalidateOnFocus: false, revalidateOnReconnect: false, - keepPreviousData : true }); useEffect(() => { diff --git a/src/lib/app/hooks/usePermissions.jsx b/src/lib/app/hooks/usePermissions.jsx index 8d45fd2..2e6fff5 100644 --- a/src/lib/app/hooks/usePermissions.jsx +++ b/src/lib/app/hooks/usePermissions.jsx @@ -14,7 +14,8 @@ const usePermissions = () => { const {data, isLoading} = useSWR(GET_PERMISSIONS_LIST, fetcher, { revalidateIfStale: false, revalidateOnFocus: false, - revalidateOnReconnect: false + revalidateOnReconnect: false, + keepPreviousData : true }) const permissions_list = data //swr config From a3ee52632f8d2affc006f7cffff2b0a54836a6b3 Mon Sep 17 00:00:00 2001 From: AmirHossein Mahmoodi Date: Wed, 11 Oct 2023 13:10:30 +0330 Subject: [PATCH 25/37] CFE-25 change structure call widget --- .../Dashboard/CallWidget/CallWidgetButton.jsx | 15 -------- .../CallPanel/CallActions/index.jsx | 10 ++++++ .../CallPanel/CallHistory/index.jsx | 10 ++++++ .../CallWidgetDialog/CallPanel/index.jsx | 18 ++++++++++ .../CallTabPanel/CallActions/index.jsx | 7 ---- .../CallTabPanel/CallHistory/index.jsx | 7 ---- .../CallTabs/CallTabPanel/index.jsx | 19 ---------- .../CallTabs/CallTabsList/CallTabLabel.jsx | 23 ------------ .../CallTabs/CallTabsList/index.jsx | 27 -------------- .../CallWidgetDialog/CallTabs/index.jsx | 17 --------- .../CallWidget/CallWidgetDialog/index.jsx | 23 ++++++------ .../layouts/Dashboard/CallWidget/index.jsx | 2 -- src/core/components/LoadingHardPage.jsx | 2 +- src/lib/callWidget/contexts/answers.jsx | 16 +++------ src/lib/callWidget/hooks/useAnswers.jsx | 35 ++++--------------- 15 files changed, 64 insertions(+), 167 deletions(-) delete mode 100644 src/components/layouts/Dashboard/CallWidget/CallWidgetButton.jsx create mode 100644 src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallPanel/CallActions/index.jsx create mode 100644 src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallPanel/CallHistory/index.jsx create mode 100644 src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallPanel/index.jsx delete mode 100644 src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallActions/index.jsx delete mode 100644 src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallHistory/index.jsx delete mode 100644 src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/index.jsx delete mode 100644 src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabsList/CallTabLabel.jsx delete mode 100644 src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabsList/index.jsx delete mode 100644 src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/index.jsx diff --git a/src/components/layouts/Dashboard/CallWidget/CallWidgetButton.jsx b/src/components/layouts/Dashboard/CallWidget/CallWidgetButton.jsx deleted file mode 100644 index 76af95c..0000000 --- a/src/components/layouts/Dashboard/CallWidget/CallWidgetButton.jsx +++ /dev/null @@ -1,15 +0,0 @@ -import PermPhoneMsgIcon from '@mui/icons-material/PermPhoneMsg'; -import StyledFab from "@/core/components/StyledFab"; -import useCallDialog from "@/lib/callWidget/hooks/useCallDialog"; - -const CallWidgetButton = () => { - const {setOpenCallDialog} = useCallDialog() - return ( - setOpenCallDialog(true)}> - - - ) -} - -export default CallWidgetButton \ No newline at end of file diff --git a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallPanel/CallActions/index.jsx b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallPanel/CallActions/index.jsx new file mode 100644 index 0000000..c7058fe --- /dev/null +++ b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallPanel/CallActions/index.jsx @@ -0,0 +1,10 @@ +import useAnswers from "@/lib/callWidget/hooks/useAnswers"; + +const CallActions = () => { + const {answer} = useAnswers() + return ( + <>CallActions - {answer.id} + ) +} + +export default CallActions \ No newline at end of file diff --git a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallPanel/CallHistory/index.jsx b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallPanel/CallHistory/index.jsx new file mode 100644 index 0000000..ed053d2 --- /dev/null +++ b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallPanel/CallHistory/index.jsx @@ -0,0 +1,10 @@ +import useAnswers from "@/lib/callWidget/hooks/useAnswers"; + +const CallHistory = () => { + const {answer} = useAnswers() + return ( + <>CallHistory - {answer.id} + ) +} + +export default CallHistory \ No newline at end of file diff --git a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallPanel/index.jsx b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallPanel/index.jsx new file mode 100644 index 0000000..9fa1b72 --- /dev/null +++ b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallPanel/index.jsx @@ -0,0 +1,18 @@ +import {Grid} from "@mui/material"; +import CallActions from "@/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallPanel/CallActions"; +import CallHistory from "@/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallPanel/CallHistory"; + +const CallPanel = () => { + return ( + + + + + + + + + ) +} + +export default CallPanel \ No newline at end of file diff --git a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallActions/index.jsx b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallActions/index.jsx deleted file mode 100644 index 5b9326f..0000000 --- a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallActions/index.jsx +++ /dev/null @@ -1,7 +0,0 @@ -const CallActions = ({tab}) => { - return ( - <>CallActions - {tab.id} - ) -} - -export default CallActions \ No newline at end of file diff --git a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallHistory/index.jsx b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallHistory/index.jsx deleted file mode 100644 index 292c640..0000000 --- a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallHistory/index.jsx +++ /dev/null @@ -1,7 +0,0 @@ -const CallHistory = ({tab}) => { - return ( - <>CallHistory - {tab.id} - ) -} - -export default CallHistory \ No newline at end of file diff --git a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/index.jsx b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/index.jsx deleted file mode 100644 index 79dc736..0000000 --- a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/index.jsx +++ /dev/null @@ -1,19 +0,0 @@ -import {Grid} from "@mui/material"; -import CallActions from "@/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallActions"; -import CallHistory from "@/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallHistory"; - -const CallTabPanel = ({tab}) => { - return ( - - - - - - - - - ) -} - -export default CallTabPanel \ No newline at end of file diff --git a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabsList/CallTabLabel.jsx b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabsList/CallTabLabel.jsx deleted file mode 100644 index 7a9b703..0000000 --- a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabsList/CallTabLabel.jsx +++ /dev/null @@ -1,23 +0,0 @@ -import {IconButton, Stack, Typography, Zoom} from "@mui/material"; -import CloseIcon from '@mui/icons-material/Close'; -import useAnswers from "@/lib/callWidget/hooks/useAnswers"; - -const CallTabLabel = ({tab}) => { - const {closeAnswerTab} = useAnswers() - const handleCloseClick = (id) => { - closeAnswerTab(id) - }; - - return ( - - {tab.phone_number} - {tab.id} - - handleCloseClick(tab.id)}> - - - - - ) -} - -export default CallTabLabel \ No newline at end of file diff --git a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabsList/index.jsx b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabsList/index.jsx deleted file mode 100644 index 70fccd9..0000000 --- a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabsList/index.jsx +++ /dev/null @@ -1,27 +0,0 @@ -import {Box, Tab, Tabs} from "@mui/material"; -import CallTabLabel - from "@/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabsList/CallTabLabel"; -import useAnswers from "@/lib/callWidget/hooks/useAnswers"; - -const CallTabsList = () => { - const {answersList, changeActiveTabAnswers, activeTab} = useAnswers() - const handleChange = (event, newValue) => { - changeActiveTabAnswers(newValue) - }; - - return ( - - - {answersList.map((answer) => ( - } key={answer.id}/> - ))} - - - ) -} - -export default CallTabsList \ No newline at end of file diff --git a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/index.jsx b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/index.jsx deleted file mode 100644 index 4e1d2f1..0000000 --- a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/index.jsx +++ /dev/null @@ -1,17 +0,0 @@ -import CallTabsList from "src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabsList"; -import CallTabPanel from "@/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel"; -import useAnswers from "@/lib/callWidget/hooks/useAnswers"; - -const CallTabs = () => { - const {answersList} = useAnswers() - - return ( - <> - - {answersList.map((answer) => ( - - ))} - - ) -} -export default CallTabs \ No newline at end of file diff --git a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/index.jsx b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/index.jsx index 5afb0a1..84e443c 100644 --- a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/index.jsx +++ b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/index.jsx @@ -2,18 +2,19 @@ import {Dialog} from "@mui/material"; import {DialogTransition} from "@/core/components/DialogTransition"; import {useEffect} from "react"; import useCallDialog from "@/lib/callWidget/hooks/useCallDialog"; -import CallTabs from "@/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs"; import useAnswers from "@/lib/callWidget/hooks/useAnswers"; import useSocket from "@/lib/app/hooks/useSocket"; +import CallPanel from "@/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallPanel"; const CallWidgetDialog = () => { const {openCallDialog, setOpenCallDialog} = useCallDialog() - const {answersList, newAnswerTab} = useAnswers() + const {answer, newAnswer} = useAnswers() const socket = useSocket() useEffect(() => { - const answerCall = (data, act) => { - newAnswerTab({ + const answerCall = (_data, act) => { + const data = JSON.parse(_data) + newAnswer({ id: data.id, phone_number: data.phone_number }) @@ -24,15 +25,15 @@ const CallWidgetDialog = () => { return () => { socket.off('answer', answerCall) } - }, []); + }); useEffect(() => { - if (answersList.length === 0) { - setOpenCallDialog(false) + if (answer) { + setOpenCallDialog(true) return } - setOpenCallDialog(true) - }, [answersList.length]); + setOpenCallDialog(false) + }, [answer]); return ( { open={openCallDialog} TransitionComponent={DialogTransition} > - + {answer && ( + + )} ) } diff --git a/src/components/layouts/Dashboard/CallWidget/index.jsx b/src/components/layouts/Dashboard/CallWidget/index.jsx index d6fe5a0..1480498 100644 --- a/src/components/layouts/Dashboard/CallWidget/index.jsx +++ b/src/components/layouts/Dashboard/CallWidget/index.jsx @@ -1,4 +1,3 @@ -import CallWidgetButton from "@/components/layouts/Dashboard/CallWidget/CallWidgetButton"; import CallWidgetDialog from "@/components/layouts/Dashboard/CallWidget/CallWidgetDialog"; import {AnswersProvider} from "@/lib/callWidget/contexts/answers"; import useSocket from "@/lib/app/hooks/useSocket"; @@ -19,7 +18,6 @@ const CallWidget = () => { return ( - ) diff --git a/src/core/components/LoadingHardPage.jsx b/src/core/components/LoadingHardPage.jsx index c3b24d6..4bbdbf4 100644 --- a/src/core/components/LoadingHardPage.jsx +++ b/src/core/components/LoadingHardPage.jsx @@ -23,7 +23,7 @@ const LoadingHardPage = ({children, loading}) => { return ( <> theme.zIndex.drawer + 1}} + sx={{bgcolor: "#fff", zIndex: (theme) => theme.zIndex.modal + 1}} open={loading} > { switch (action.type) { - case "CHANGE_ACTIVE_TAB": - return state.map((answer, index) => action.newValue === index ? {...answer, active: true} : { - ...answer, - active: false - }); - case "CHANGE_LIST": - return [...action.newList] + case "CREATE_ANSWER": + return {...action.data} + case "DELETE_ANSWER": + return null default: return state; } @@ -16,8 +13,7 @@ const reducer = (state, action) => { export const AnswersContext = createContext() export const AnswersProvider = ({children}) => { const [openCallDialog, setOpenCallDialog] = useState(false) - const [activeTab, setActiveTab] = useState(0) - const [state, dispatch] = useReducer(reducer, []); + const [state, dispatch] = useReducer(reducer, null); return { setOpenCallDialog, state, dispatch, - activeTab, - setActiveTab }}>{children} } diff --git a/src/lib/callWidget/hooks/useAnswers.jsx b/src/lib/callWidget/hooks/useAnswers.jsx index 2155cd7..ec606db 100644 --- a/src/lib/callWidget/hooks/useAnswers.jsx +++ b/src/lib/callWidget/hooks/useAnswers.jsx @@ -2,37 +2,16 @@ import {useContext} from "react"; import {AnswersContext} from "@/lib/callWidget/contexts/answers"; const useAnswers = () => { - const {state, dispatch, activeTab, setActiveTab} = useContext(AnswersContext) + const {state, dispatch} = useContext(AnswersContext) - const changeActiveTabAnswers = (newValue) => { - dispatch({type: 'CHANGE_ACTIVE_TAB', newValue}) - setActiveTab(newValue) + const newAnswer = (data) => { + dispatch({type: 'CREATE_ANSWER', data}) + } + const deleteAnswer = () => { + dispatch({type: 'DELETE_ANSWER'}) } - const newAnswerTab = (data) => { - const newAnswer = { - id: data.id, - phone_number: data.phone_number, - active: true - } - const newList = [...state, newAnswer] - dispatch({type: 'CHANGE_LIST', newList}) - changeActiveTabAnswers(newList.length - 1) - } - - const closeAnswerTab = (id) => { - const index = state.findIndex(answer => answer.id === id) - const newIndex = index === 0 ? 0 : index - 1 - const newList = [...state] - newList.splice(index, 1); - if (newList.length !== 0) { - newList[newIndex].active = true - setActiveTab(newIndex) - } - dispatch({type: 'CHANGE_LIST', newList}) - } - - return {answersList: state, activeTab, setActiveTab, changeActiveTabAnswers, closeAnswerTab, newAnswerTab} + return {answer: state, newAnswer, deleteAnswer} } export default useAnswers \ No newline at end of file From 33eeb37179909eae0ced257355e3ae469040b979 Mon Sep 17 00:00:00 2001 From: AmirHossein Mahmoodi Date: Sat, 14 Oct 2023 13:06:05 +0330 Subject: [PATCH 26/37] CFE-25 Return to previous structure call widget --- .../CallPanel/CallActions/index.jsx | 10 ------ .../CallPanel/CallHistory/index.jsx | 10 ------ .../CallWidgetDialog/CallPanel/index.jsx | 18 ----------- .../CallTabPanel/CallActions/index.jsx | 7 ++++ .../CallTabPanel/CallHistory/index.jsx | 7 ++++ .../CallTabs/CallTabPanel/index.jsx | 19 +++++++++++ .../CallTabs/callTabsList/CallTabLabel.jsx | 23 +++++++++++++ .../CallTabs/callTabsList/index.jsx | 27 ++++++++++++++++ .../CallWidgetDialog/CallTabs/index.jsx | 17 ++++++++++ .../CallWidget/CallWidgetDialog/index.jsx | 20 ++++++------ src/lib/callWidget/contexts/answers.jsx | 16 +++++++--- src/lib/callWidget/hooks/useAnswers.jsx | 32 +++++++++++++++---- 12 files changed, 146 insertions(+), 60 deletions(-) delete mode 100644 src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallPanel/CallActions/index.jsx delete mode 100644 src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallPanel/CallHistory/index.jsx delete mode 100644 src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallPanel/index.jsx create mode 100644 src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallActions/index.jsx create mode 100644 src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallHistory/index.jsx create mode 100644 src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/index.jsx create mode 100644 src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/callTabsList/CallTabLabel.jsx create mode 100644 src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/callTabsList/index.jsx create mode 100644 src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/index.jsx diff --git a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallPanel/CallActions/index.jsx b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallPanel/CallActions/index.jsx deleted file mode 100644 index c7058fe..0000000 --- a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallPanel/CallActions/index.jsx +++ /dev/null @@ -1,10 +0,0 @@ -import useAnswers from "@/lib/callWidget/hooks/useAnswers"; - -const CallActions = () => { - const {answer} = useAnswers() - return ( - <>CallActions - {answer.id} - ) -} - -export default CallActions \ No newline at end of file diff --git a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallPanel/CallHistory/index.jsx b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallPanel/CallHistory/index.jsx deleted file mode 100644 index ed053d2..0000000 --- a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallPanel/CallHistory/index.jsx +++ /dev/null @@ -1,10 +0,0 @@ -import useAnswers from "@/lib/callWidget/hooks/useAnswers"; - -const CallHistory = () => { - const {answer} = useAnswers() - return ( - <>CallHistory - {answer.id} - ) -} - -export default CallHistory \ No newline at end of file diff --git a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallPanel/index.jsx b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallPanel/index.jsx deleted file mode 100644 index 9fa1b72..0000000 --- a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallPanel/index.jsx +++ /dev/null @@ -1,18 +0,0 @@ -import {Grid} from "@mui/material"; -import CallActions from "@/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallPanel/CallActions"; -import CallHistory from "@/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallPanel/CallHistory"; - -const CallPanel = () => { - return ( - - - - - - - - - ) -} - -export default CallPanel \ No newline at end of file diff --git a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallActions/index.jsx b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallActions/index.jsx new file mode 100644 index 0000000..d1a68f7 --- /dev/null +++ b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallActions/index.jsx @@ -0,0 +1,7 @@ +const CallActions = ({tab}) => { + return ( + <>CallActions - {tab.id} + ) +} + +export default CallActions diff --git a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallHistory/index.jsx b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallHistory/index.jsx new file mode 100644 index 0000000..048c091 --- /dev/null +++ b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallHistory/index.jsx @@ -0,0 +1,7 @@ +const CallHistory = ({tab}) => { + return ( + <>CallHistory - {tab.id} + ) +} + +export default CallHistory diff --git a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/index.jsx b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/index.jsx new file mode 100644 index 0000000..79dc736 --- /dev/null +++ b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/index.jsx @@ -0,0 +1,19 @@ +import {Grid} from "@mui/material"; +import CallActions from "@/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallActions"; +import CallHistory from "@/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallHistory"; + +const CallTabPanel = ({tab}) => { + return ( + + + + + + + + + ) +} + +export default CallTabPanel \ No newline at end of file diff --git a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/callTabsList/CallTabLabel.jsx b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/callTabsList/CallTabLabel.jsx new file mode 100644 index 0000000..0137265 --- /dev/null +++ b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/callTabsList/CallTabLabel.jsx @@ -0,0 +1,23 @@ +import {IconButton, Stack, Typography, Zoom} from "@mui/material"; +import CloseIcon from '@mui/icons-material/Close'; +import useAnswers from "@/lib/callWidget/hooks/useAnswers"; + +const CallTabLabel = ({tab}) => { + const {closeAnswerTab} = useAnswers() + const handleCloseClick = (id) => { + closeAnswerTab(id) + }; + + return ( + + {tab.phone_number} - {tab.id} + + handleCloseClick(tab.id)}> + + + + + ) +} + +export default CallTabLabel diff --git a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/callTabsList/index.jsx b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/callTabsList/index.jsx new file mode 100644 index 0000000..249d005 --- /dev/null +++ b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/callTabsList/index.jsx @@ -0,0 +1,27 @@ +import {Box, Tab, Tabs} from "@mui/material"; +import CallTabLabel + from "@/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabsList/CallTabLabel"; +import useAnswers from "@/lib/callWidget/hooks/useAnswers"; + +const CallTabsList = () => { + const {answersList, changeActiveTabAnswers, activeTab} = useAnswers() + const handleChange = (event, newValue) => { + changeActiveTabAnswers(newValue) + }; + + return ( + + + {answersList.map((answer) => ( + } key={answer.id}/> + ))} + + + ) +} + +export default CallTabsList diff --git a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/index.jsx b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/index.jsx new file mode 100644 index 0000000..4e1d2f1 --- /dev/null +++ b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/index.jsx @@ -0,0 +1,17 @@ +import CallTabsList from "src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabsList"; +import CallTabPanel from "@/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel"; +import useAnswers from "@/lib/callWidget/hooks/useAnswers"; + +const CallTabs = () => { + const {answersList} = useAnswers() + + return ( + <> + + {answersList.map((answer) => ( + + ))} + + ) +} +export default CallTabs \ No newline at end of file diff --git a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/index.jsx b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/index.jsx index 84e443c..728f68d 100644 --- a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/index.jsx +++ b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/index.jsx @@ -4,18 +4,18 @@ import {useEffect} from "react"; import useCallDialog from "@/lib/callWidget/hooks/useCallDialog"; import useAnswers from "@/lib/callWidget/hooks/useAnswers"; import useSocket from "@/lib/app/hooks/useSocket"; -import CallPanel from "@/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallPanel"; +import CallTabs from "@/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs"; const CallWidgetDialog = () => { const {openCallDialog, setOpenCallDialog} = useCallDialog() - const {answer, newAnswer} = useAnswers() + const {answersList, newAnswerTab} = useAnswers() const socket = useSocket() useEffect(() => { const answerCall = (_data, act) => { const data = JSON.parse(_data) - newAnswer({ - id: data.id, + newAnswerTab({ + id: data.call_id, phone_number: data.phone_number }) act() @@ -28,12 +28,12 @@ const CallWidgetDialog = () => { }); useEffect(() => { - if (answer) { - setOpenCallDialog(true) + if (answersList.length === 0) { + setOpenCallDialog(false) return } - setOpenCallDialog(false) - }, [answer]); + setOpenCallDialog(true) + }, [answersList.length]); return ( { open={openCallDialog} TransitionComponent={DialogTransition} > - {answer && ( - - )} + ) } diff --git a/src/lib/callWidget/contexts/answers.jsx b/src/lib/callWidget/contexts/answers.jsx index adbca5c..a9f7ae2 100644 --- a/src/lib/callWidget/contexts/answers.jsx +++ b/src/lib/callWidget/contexts/answers.jsx @@ -2,10 +2,13 @@ import {createContext, useReducer, useState} from "react"; const reducer = (state, action) => { switch (action.type) { - case "CREATE_ANSWER": - return {...action.data} - case "DELETE_ANSWER": - return null + case "CHANGE_ACTIVE_TAB": + return state.map((answer, index) => action.newValue === index ? {...answer, active: true} : { + ...answer, + active: false + }); + case "CHANGE_LIST": + return [...action.newList] default: return state; } @@ -13,7 +16,8 @@ const reducer = (state, action) => { export const AnswersContext = createContext() export const AnswersProvider = ({children}) => { const [openCallDialog, setOpenCallDialog] = useState(false) - const [state, dispatch] = useReducer(reducer, null); + const [activeTab, setActiveTab] = useState(0) + const [state, dispatch] = useReducer(reducer, []); return { setOpenCallDialog, state, dispatch, + activeTab, + setActiveTab }}>{children} } diff --git a/src/lib/callWidget/hooks/useAnswers.jsx b/src/lib/callWidget/hooks/useAnswers.jsx index ec606db..f530e36 100644 --- a/src/lib/callWidget/hooks/useAnswers.jsx +++ b/src/lib/callWidget/hooks/useAnswers.jsx @@ -2,16 +2,36 @@ import {useContext} from "react"; import {AnswersContext} from "@/lib/callWidget/contexts/answers"; const useAnswers = () => { - const {state, dispatch} = useContext(AnswersContext) + const {state, dispatch, activeTab, setActiveTab} = useContext(AnswersContext) - const newAnswer = (data) => { - dispatch({type: 'CREATE_ANSWER', data}) + const changeActiveTabAnswers = (newValue) => { + dispatch({type: 'CHANGE_ACTIVE_TAB', newValue}) + setActiveTab(newValue) } - const deleteAnswer = () => { - dispatch({type: 'DELETE_ANSWER'}) + const newAnswerTab = (data) => { + const newAnswer = { + id: data.id, + phone_number: data.phone_number, + active: true + } + const newList = [...state, newAnswer] + dispatch({type: 'CHANGE_LIST', newList}) + changeActiveTabAnswers(newList.length - 1) } - return {answer: state, newAnswer, deleteAnswer} + const closeAnswerTab = (id) => { + const index = state.findIndex(answer => answer.id === id) + const newIndex = index === 0 ? 0 : index - 1 + const newList = [...state] + newList.splice(index, 1); + if (newList.length !== 0) { + newList[newIndex].active = true + setActiveTab(newIndex) + } + dispatch({type: 'CHANGE_LIST', newList}) + } + + return {answersList: state, activeTab, setActiveTab, changeActiveTabAnswers, closeAnswerTab, newAnswerTab} } export default useAnswers \ No newline at end of file From 4cabe9d171e169111e1de062d097856176b6aa06 Mon Sep 17 00:00:00 2001 From: AmirHossein Mahmoodi Date: Sat, 14 Oct 2023 13:09:21 +0000 Subject: [PATCH 27/37] Update .gitlab-ci.yml file --- .gitlab-ci.yml | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 .gitlab-ci.yml diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000..78ca1fd --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,24 @@ +image: node:latest + +stages: + - build + - test + +before_script: + - npm install + +build: + stage: build + script: + - echo "Run build project" + - npm run build + only: + - merge_requests + +test: + stage: test + script: + - echo "Run test project" + - npm run test + only: + - merge_requests \ No newline at end of file From df947537e45010c579cb63e40088b70c141045a7 Mon Sep 17 00:00:00 2001 From: AmirHossein Mahmoodi Date: Sun, 15 Oct 2023 11:18:14 +0330 Subject: [PATCH 28/37] Update .gitlab-ci.yml file --- .gitlab-ci.yml | 36 +++++++++++++++++++----------------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 78ca1fd..61ebb05 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,24 +1,26 @@ image: node:latest stages: - - build - - test - -before_script: - - npm install + - build + - test build: - stage: build - script: - - echo "Run build project" - - npm run build - only: - - merge_requests + stage: build + script: + - npm install + - npm run build + only: + - merge_requests + artifacts: + paths: + - node_modules test: - stage: test - script: - - echo "Run test project" - - npm run test - only: - - merge_requests \ No newline at end of file + stage: test + script: + - npm run test + only: + - merge_requests + artifacts: + paths: + - node_modules \ No newline at end of file From 9c10d5b249c8ddf768bb14935af82d91b3652151 Mon Sep 17 00:00:00 2001 From: AmirHossein Mahmoodi Date: Sun, 15 Oct 2023 07:57:37 +0000 Subject: [PATCH 29/37] Update .gitlab-ci.yml file --- .gitlab-ci.yml | 59 ++++++++++++++++++++++++++++++++++---------------- 1 file changed, 40 insertions(+), 19 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 61ebb05..cd22071 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,26 +1,47 @@ image: node:latest stages: - - build - - test + - build + - test + +merge:build: + stage: build + script: + - echo "Run build project" + - npm install + - npm run build + only: + - merge_requests + artifacts: + paths: + - node_modules + +merge:test: + stage: test + script: + - echo "Run test project" + - npm run test + only: + - merge_requests + artifacts: + paths: + - node_modules build: - stage: build - script: - - npm install - - npm run build - only: - - merge_requests - artifacts: - paths: - - node_modules + stage: build + script: + - echo "Run build project" + - npm install + - npm run build + artifacts: + paths: + - node_modules test: - stage: test - script: - - npm run test - only: - - merge_requests - artifacts: - paths: - - node_modules \ No newline at end of file + stage: test + script: + - echo "Run test project" + - npm run test + artifacts: + paths: + - node_modules \ No newline at end of file From 2a2be7615c166df50766bfe6b35427c9946108e4 Mon Sep 17 00:00:00 2001 From: AmirHossein Mahmoodi Date: Sun, 15 Oct 2023 08:12:14 +0000 Subject: [PATCH 30/37] Update .gitlab-ci.yml file --- .gitlab-ci.yml | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index cd22071..4d5868d 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -3,6 +3,7 @@ image: node:latest stages: - build - test + - deploy merge:build: stage: build @@ -33,6 +34,9 @@ build: - echo "Run build project" - npm install - npm run build + only: + - develop + - main artifacts: paths: - node_modules @@ -42,6 +46,16 @@ test: script: - echo "Run test project" - npm run test + only: + - develop + - main artifacts: paths: - - node_modules \ No newline at end of file + - node_modules + +deploy: + stage: deploy + script: + - echo "Run deploy project" + only: + - main \ No newline at end of file From 99782ad72dff8c2c58ec42888ec9f18623446eba Mon Sep 17 00:00:00 2001 From: AmirHossein Mahmoodi Date: Sun, 15 Oct 2023 08:35:59 +0000 Subject: [PATCH 31/37] Update .gitlab-ci.yml file --- .gitlab-ci.yml | 53 ++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 45 insertions(+), 8 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 4d5868d..29b710d 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -3,24 +3,22 @@ image: node:latest stages: - build - test + - test:build - deploy merge:build: stage: build script: - - echo "Run build project" - npm install - - npm run build only: - merge_requests artifacts: paths: - node_modules -merge:test: +merge:test:jest: stage: test script: - - echo "Run test project" - npm run test only: - merge_requests @@ -28,12 +26,30 @@ merge:test: paths: - node_modules +merge:test:lint: + stage: test + script: + - npm run lint + only: + - merge_requests + artifacts: + paths: + - node_modules + +merge:test:build: + stage: test:build + script: + - npm run build + only: + - merge_requests + artifacts: + paths: + - node_modules + build: stage: build script: - - echo "Run build project" - npm install - - npm run build only: - develop - main @@ -41,10 +57,9 @@ build: paths: - node_modules -test: +test:jest: stage: test script: - - echo "Run test project" - npm run test only: - develop @@ -53,6 +68,28 @@ test: paths: - node_modules +test:lint: + stage: test + script: + - npm run lint + only: + - develop + - main + artifacts: + paths: + - node_modules + +test:build: + stage: test:build + script: + - npm run build + only: + - develop + - main + artifacts: + paths: + - node_modules + deploy: stage: deploy script: From 45f4aa0c3152c1c66297075c2d196e9209a4c813 Mon Sep 17 00:00:00 2001 From: AmirHossein Mahmoodi Date: Sun, 15 Oct 2023 08:49:12 +0000 Subject: [PATCH 32/37] Update .gitlab-ci.yml file --- .gitlab-ci.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 29b710d..d06637a 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -10,6 +10,8 @@ merge:build: stage: build script: - npm install + - cp example.env.local .env.local + - cp example.env.local .env.test only: - merge_requests artifacts: @@ -50,6 +52,8 @@ build: stage: build script: - npm install + - cp example.env.local .env.local + - cp example.env.local .env.test only: - develop - main From f0a2a9383fd000c29383e92a1e8aebf8c4c1ffd6 Mon Sep 17 00:00:00 2001 From: AmirHossein Mahmoodi Date: Sun, 15 Oct 2023 08:54:37 +0000 Subject: [PATCH 33/37] Update .gitlab-ci.yml file --- .gitlab-ci.yml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index d06637a..d06af2b 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -17,6 +17,8 @@ merge:build: artifacts: paths: - node_modules + - .env.local + - .env.test merge:test:jest: stage: test @@ -27,6 +29,8 @@ merge:test:jest: artifacts: paths: - node_modules + - .env.local + - .env.test merge:test:lint: stage: test @@ -37,6 +41,8 @@ merge:test:lint: artifacts: paths: - node_modules + - .env.local + - .env.test merge:test:build: stage: test:build @@ -47,6 +53,8 @@ merge:test:build: artifacts: paths: - node_modules + - .env.local + - .env.test build: stage: build @@ -60,6 +68,8 @@ build: artifacts: paths: - node_modules + - .env.local + - .env.test test:jest: stage: test @@ -71,6 +81,8 @@ test:jest: artifacts: paths: - node_modules + - .env.local + - .env.test test:lint: stage: test @@ -82,6 +94,8 @@ test:lint: artifacts: paths: - node_modules + - .env.local + - .env.test test:build: stage: test:build @@ -93,6 +107,8 @@ test:build: artifacts: paths: - node_modules + - .env.local + - .env.test deploy: stage: deploy From 4700ea2d20aafaea0c3b159ad6358d0da058f910 Mon Sep 17 00:00:00 2001 From: AmirHossein Mahmoodi Date: Sun, 15 Oct 2023 13:11:37 +0330 Subject: [PATCH 34/37] Update .eslintrc.json file --- .eslintrc.json | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.eslintrc.json b/.eslintrc.json index c4f29da..cb6d4e4 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1,7 +1,5 @@ { "extends": [ - "next/core-web-vitals", - "plugin:testing-library/react", - "plugin:jest-dom/recommended" + "next/core-web-vitals" ] } From e6914bb8ad77f2ab255d4ad5e91fa06e5d25fd99 Mon Sep 17 00:00:00 2001 From: AmirHossein Mahmoodi Date: Sun, 15 Oct 2023 13:54:19 +0330 Subject: [PATCH 35/37] CFE-27 fixed mui bugs --- .../CallWidget/CallWidgetDialog/CallTabs/index.jsx | 2 +- src/components/layouts/Dashboard/Header/index.jsx | 4 +++- src/core/components/LoadingHardPage.jsx | 3 ++- src/core/components/StyledForm.jsx | 2 +- src/core/components/svgs/Svg403.jsx | 2 +- src/core/components/svgs/Svg404.jsx | 2 +- src/core/components/svgs/SvgChangePassword.jsx | 2 +- src/core/components/svgs/SvgDashboard.jsx | 4 ++-- src/core/components/svgs/SvgLoading.jsx | 2 +- src/core/components/svgs/SvgLogin.jsx | 2 +- src/core/data/apiRoutes.js | 6 ++++++ 11 files changed, 20 insertions(+), 11 deletions(-) diff --git a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/index.jsx b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/index.jsx index 4e1d2f1..9fd0111 100644 --- a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/index.jsx +++ b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/index.jsx @@ -1,6 +1,6 @@ -import CallTabsList from "src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabsList"; import CallTabPanel from "@/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel"; import useAnswers from "@/lib/callWidget/hooks/useAnswers"; +import CallTabsList from "@/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/callTabsList"; const CallTabs = () => { const {answersList} = useAnswers() diff --git a/src/components/layouts/Dashboard/Header/index.jsx b/src/components/layouts/Dashboard/Header/index.jsx index 8760960..e8af065 100644 --- a/src/components/layouts/Dashboard/Header/index.jsx +++ b/src/components/layouts/Dashboard/Header/index.jsx @@ -1,6 +1,8 @@ import MenuIcon from "@mui/icons-material/Menu"; -import {AppBar, Box, Container, CssBaseline, IconButton, Stack, Toolbar, useTheme} from "@mui/material"; import ProfileMenu from "./ProfileMenu"; +import {useTheme} from "@mui/material/styles"; +import CssBaseline from "@mui/material/CssBaseline"; +import {AppBar, Box, Container, IconButton, Stack, Toolbar} from "@mui/material"; function Header({drawerWidth, handleDrawerToggle}) { const theme = useTheme(); diff --git a/src/core/components/LoadingHardPage.jsx b/src/core/components/LoadingHardPage.jsx index 4bbdbf4..5d00cd0 100644 --- a/src/core/components/LoadingHardPage.jsx +++ b/src/core/components/LoadingHardPage.jsx @@ -1,5 +1,6 @@ -import {Backdrop, Box, styled} from "@mui/material"; import SvgLoading from "@/core/components/svgs/SvgLoading"; +import {Backdrop, Box} from "@mui/material"; +import {styled} from "@mui/material/styles"; const LoadingImage = styled(Box)({ "@keyframes load": { diff --git a/src/core/components/StyledForm.jsx b/src/core/components/StyledForm.jsx index 41eeabc..253fc12 100644 --- a/src/core/components/StyledForm.jsx +++ b/src/core/components/StyledForm.jsx @@ -1,5 +1,5 @@ -import {styled} from "@mui/material"; import {Form} from "formik"; +import {styled} from "@mui/material/styles"; const StyledForm = styled(Form)``; diff --git a/src/core/components/svgs/Svg403.jsx b/src/core/components/svgs/Svg403.jsx index 720828c..d706ed0 100644 --- a/src/core/components/svgs/Svg403.jsx +++ b/src/core/components/svgs/Svg403.jsx @@ -1,4 +1,4 @@ -import {useTheme} from "@mui/material"; +import {useTheme} from "@mui/material/styles"; const Svg403 = ({width, height}) => { const theme = useTheme() diff --git a/src/core/components/svgs/Svg404.jsx b/src/core/components/svgs/Svg404.jsx index e46ab12..159d7c6 100644 --- a/src/core/components/svgs/Svg404.jsx +++ b/src/core/components/svgs/Svg404.jsx @@ -1,4 +1,4 @@ -import {useTheme} from "@mui/material"; +import {useTheme} from "@mui/material/styles"; const Svg404 = ({width, height}) => { const theme = useTheme() diff --git a/src/core/components/svgs/SvgChangePassword.jsx b/src/core/components/svgs/SvgChangePassword.jsx index a7ee82f..1221d28 100644 --- a/src/core/components/svgs/SvgChangePassword.jsx +++ b/src/core/components/svgs/SvgChangePassword.jsx @@ -1,4 +1,4 @@ -import {useTheme} from "@mui/material"; +import {useTheme} from "@mui/material/styles"; const SvgChangePassword = ({width, height}) => { const theme = useTheme() diff --git a/src/core/components/svgs/SvgDashboard.jsx b/src/core/components/svgs/SvgDashboard.jsx index a5b40e0..a636bf4 100644 --- a/src/core/components/svgs/SvgDashboard.jsx +++ b/src/core/components/svgs/SvgDashboard.jsx @@ -1,9 +1,9 @@ -import {useTheme} from "@mui/material"; +import {useTheme} from "@mui/material/styles"; const SvgDashboard = ({width, height}) => { const theme = useTheme() const fillColor = theme.palette.primary.main - + return ( diff --git a/src/core/components/svgs/SvgLoading.jsx b/src/core/components/svgs/SvgLoading.jsx index f9cea4a..9ff61ff 100644 --- a/src/core/components/svgs/SvgLoading.jsx +++ b/src/core/components/svgs/SvgLoading.jsx @@ -1,4 +1,4 @@ -import {useTheme} from "@mui/material"; +import {useTheme} from "@mui/material/styles"; const SvgLoading = ({width, height}) => { const theme = useTheme() diff --git a/src/core/components/svgs/SvgLogin.jsx b/src/core/components/svgs/SvgLogin.jsx index 643c195..edf3b51 100644 --- a/src/core/components/svgs/SvgLogin.jsx +++ b/src/core/components/svgs/SvgLogin.jsx @@ -1,4 +1,4 @@ -import {useTheme} from "@mui/material"; +import {useTheme} from "@mui/material/styles"; const SvgLogin = ({width, height}) => { const theme = useTheme() diff --git a/src/core/data/apiRoutes.js b/src/core/data/apiRoutes.js index 4d0231f..66e839d 100644 --- a/src/core/data/apiRoutes.js +++ b/src/core/data/apiRoutes.js @@ -4,6 +4,10 @@ const BASE_URL = process.env.NEXT_PUBLIC_BASE_URL; export const GET_USER_TOKEN = BASE_URL + "/api/auth/login"; //end login +//edit profile +export const UPDATE_AVATAR = BASE_URL + ""; +//end edit profile + //change password export const SET_USER_PASSWORD = BASE_URL + "/api/profile/change_password"; //end change password @@ -12,6 +16,8 @@ export const SET_USER_PASSWORD = BASE_URL + "/api/profile/change_password"; export const GET_USER_ROUTE = BASE_URL + "/api/profile/info"; //user data +export const GET_SIDEBAR_NOTIFICATION = BASE_URL + ""; + // role management export const GET_ROLE_MANAGEMENT = BASE_URL + "/api/roles" From b7d60d687f9c52d855221463789d84cf8f2947ec Mon Sep 17 00:00:00 2001 From: AmirHossein Mahmoodi Date: Sun, 15 Oct 2023 14:04:08 +0330 Subject: [PATCH 36/37] CFE-27 fixed mui bugs --- .../CallTabs/{callTabsList => CallTabsList}/CallTabLabel.jsx | 0 .../CallTabs/{callTabsList => CallTabsList}/index.jsx | 0 .../Dashboard/CallWidget/CallWidgetDialog/CallTabs/index.jsx | 2 +- 3 files changed, 1 insertion(+), 1 deletion(-) rename src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/{callTabsList => CallTabsList}/CallTabLabel.jsx (100%) rename src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/{callTabsList => CallTabsList}/index.jsx (100%) diff --git a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/callTabsList/CallTabLabel.jsx b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabsList/CallTabLabel.jsx similarity index 100% rename from src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/callTabsList/CallTabLabel.jsx rename to src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabsList/CallTabLabel.jsx diff --git a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/callTabsList/index.jsx b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabsList/index.jsx similarity index 100% rename from src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/callTabsList/index.jsx rename to src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabsList/index.jsx diff --git a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/index.jsx b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/index.jsx index 9fd0111..8a96506 100644 --- a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/index.jsx +++ b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/index.jsx @@ -1,6 +1,6 @@ import CallTabPanel from "@/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel"; import useAnswers from "@/lib/callWidget/hooks/useAnswers"; -import CallTabsList from "@/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/callTabsList"; +import CallTabsList from "src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabsList"; const CallTabs = () => { const {answersList} = useAnswers() From 4ba0e92e8e9597704eea4056a4f1ef063782a2fd Mon Sep 17 00:00:00 2001 From: AmirHossein Mahmoodi Date: Sun, 15 Oct 2023 14:17:29 +0330 Subject: [PATCH 37/37] CFE-27 fixed mocks bug --- mocks/handler.js | 27 +++++++++++++++++++-------- src/core/data/apiRoutes.js | 5 ++++- 2 files changed, 23 insertions(+), 9 deletions(-) diff --git a/mocks/handler.js b/mocks/handler.js index 8d22664..b9ae85f 100644 --- a/mocks/handler.js +++ b/mocks/handler.js @@ -1,17 +1,28 @@ -import {GET_PERMISSIONS_LIST, GET_ROLE_MANAGEMENT, GET_USER_ROUTE, SET_USER_PASSWORD} from "@/core/data/apiRoutes"; +import { + GET_PERMISSIONS_LIST, + GET_ROLE_MANAGEMENT, + GET_SIDEBAR_NOTIFICATION, + GET_USER_ROUTE, + SET_USER_PASSWORD +} from "@/core/data/apiRoutes"; import {rest} from "msw"; export const handler = [ rest.get(GET_USER_ROUTE, (req, res, ctx) => { return res(ctx.json({ - data:{ + data: { id: 10, - permissions:[ + permissions: [ "manage_users" ], } })) }), + rest.get(GET_SIDEBAR_NOTIFICATION, (req, res, ctx) => { + return res(ctx.json({ + data: [] + })) + }), rest.post(SET_USER_PASSWORD, (req, res, ctx) => { return res( ctx.status(200), @@ -20,7 +31,7 @@ export const handler = [ rest.get(GET_PERMISSIONS_LIST, (req, res, ctx) => { return res(ctx.json( { - data:[ + data: [ { id: 1, name: "manage_passenger_office_navgan", @@ -29,7 +40,7 @@ export const handler = [ { id: 2, name: "manage_province_working_group_navgan", - name_fa:"مدیریت کارتابل کارگروه استانی" + name_fa: "مدیریت کارتابل کارگروه استانی" } ] } @@ -39,7 +50,7 @@ export const handler = [ return res( ctx.json( { - data : [ + data: [ { created_at: "2023-10-01T07:20:07.000000Z", guard_name: "api", @@ -77,8 +88,8 @@ export const handler = [ updated_at: "2023-10-10T07:38:12.000000Z" } ], - meta : { - totalRowCount : 2 + meta: { + totalRowCount: 2 } } ), diff --git a/src/core/data/apiRoutes.js b/src/core/data/apiRoutes.js index 66e839d..1a6e89c 100644 --- a/src/core/data/apiRoutes.js +++ b/src/core/data/apiRoutes.js @@ -16,7 +16,10 @@ export const SET_USER_PASSWORD = BASE_URL + "/api/profile/change_password"; export const GET_USER_ROUTE = BASE_URL + "/api/profile/info"; //user data -export const GET_SIDEBAR_NOTIFICATION = BASE_URL + ""; +//sidebar notification +export const GET_SIDEBAR_NOTIFICATION = BASE_URL + "/dashboard/notification" +//sidebar notification + // role management export const GET_ROLE_MANAGEMENT =