diff --git a/.gitignore b/.gitignore
index 3dfb407..662e667 100644
--- a/.gitignore
+++ b/.gitignore
@@ -27,7 +27,7 @@ node_modules
cypress/videos
cypress/screenshots
.next
-.env.local
+.env*
.env
.idea
package-lock.json
\ No newline at end of file
diff --git a/example.env.local b/example.env.local
index 0be16f0..922eec0 100644
--- a/example.env.local
+++ b/example.env.local
@@ -7,6 +7,7 @@ NEXT_PUBLIC_PRIMARY_MAIN = "#1b4332"
NEXT_PUBLIC_SECONDARY_MAIN = "#800e13"
NEXT_PUBLIC_BASE_URL = "https://crm.witel.ir"
+NEXT_PUBLIC_SERVER_SOCKET_URL = "wss://crmws.witel.ir"
NEXT_PUBLIC_POWERED_BY_URL = "https://witel.ir"
NODE_ENV = "development"
\ No newline at end of file
diff --git a/jest.setup.js b/jest.setup.js
index 7d55f67..fadd766 100644
--- a/jest.setup.js
+++ b/jest.setup.js
@@ -2,8 +2,6 @@ import '@testing-library/jest-dom'
import {server} from "./mocks/server";
import mockRouter from 'next-router-mock'
-require('dotenv').config({path: './.env.local'});
-
jest.mock('next/router', () => jest.requireActual('next-router-mock'))
beforeAll(() => {
diff --git a/mocks/handler.js b/mocks/handler.js
index 5e67894..169b795 100644
--- a/mocks/handler.js
+++ b/mocks/handler.js
@@ -1,12 +1,19 @@
-import {GET_PERMISSIONS_LIST, GET_USER_ROUTE} from "@/core/data/apiRoutes";
+import {GET_PERMISSIONS_LIST, 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
+ data:{
+ id: 10
+ }
}))
}),
+ rest.post(SET_USER_PASSWORD, (req, res, ctx) => {
+ return res(
+ ctx.status(200),
+ );
+ }),
rest.get(GET_PERMISSIONS_LIST, (req, res, ctx) => {
return res(ctx.json(
{
diff --git a/package.json b/package.json
index 6e19bf6..5d6417a 100644
--- a/package.json
+++ b/package.json
@@ -46,6 +46,7 @@
"react-dom": "^18.2.0",
"react-toastify": "^9.1.3",
"sass": "^1.62.0",
+ "socket.io-client": "^4.7.2",
"stylis": "^4.1.3",
"stylis-plugin-rtl": "^2.1.1",
"swr": "^2.1.5",
diff --git a/public/locales/fa/app.json b/public/locales/fa/app.json
index aa32ec4..cea9212 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": " پروفایل",
@@ -81,7 +83,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..503b571
--- /dev/null
+++ b/src/components/dashboard/change-password/change-password-form/__test__/index.test.js
@@ -0,0 +1,305 @@
+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', () => {
+ 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.queryByLabelText('رمز عبور فعلی');
+ expect(currentPasswordLabel).toBeInTheDocument();
+ });
+ it('Should see the label for new password field', () => {
+ render();
+ const newPasswordLabel = screen.queryByLabelText('رمز عبور جدید');
+ expect(newPasswordLabel).toBeInTheDocument();
+ });
+ it('Should see the label for confirm password field', () => {
+ render();
+ const confirmPasswordLabel = screen.queryByLabelText('تکرار رمز عبور جدید');
+ 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.queryByLabelText('رمز عبور فعلی');
+ const newPasswordInput = screen.queryByLabelText('رمز عبور جدید');
+ const confirmPasswordInput = screen.queryByLabelText('تکرار رمز عبور جدید');
+
+ 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 and not see any error while its not empty', async () => {
+ render();
+
+ const currentPasswordInput = screen.queryByLabelText('رمز عبور فعلی');
+
+ // 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 and not see any error while its not empty', async () => {
+ render();
+
+ const newPasswordInput = screen.queryByLabelText('رمز عبور جدید');
+
+ // 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 and not see any error while its not empty', async () => {
+ render();
+
+ const confirmPasswordInput = screen.queryByLabelText('تکرار رمز عبور جدید');
+
+ // 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 blur event', async () => {
+ render();
+
+ // Simulate blur event on the current password input
+ const currentPasswordInput = screen.queryByLabelText('رمز عبور فعلی');
+
+ // Clear the input field
+ fireEvent.change(currentPasswordInput, { target: { value: '' } });
+
+ fireEvent.blur(currentPasswordInput);
+
+ await waitFor(()=>{
+ expect(screen.queryByText("وارد کردن رمز عبور فعلی الزامیست!")).toBeInTheDocument()
+ });
+ });
+ 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.queryByLabelText('رمز عبور جدید');
+
+ // Clear the input field
+ fireEvent.change(newPasswordInput, { target: { value: '' } });
+
+ fireEvent.blur(newPasswordInput);
+
+ await waitFor(()=>{
+ expect(screen.queryByText("وارد کردن رمز عبور جدید الزامیست!")).toBeInTheDocument()
+ });
+ });
+ 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.queryByLabelText('تکرار رمز عبور جدید');
+
+ // Clear the input field
+ fireEvent.change(confirmPasswordInput, { target: { value: '' } });
+
+ fireEvent.blur(confirmPasswordInput);
+
+ await waitFor(()=>{
+ expect(screen.queryByText("وارد کردن تکرار رمز عبور جدید الزامیست!")).toBeInTheDocument()
+ });
+ });
+ it('Should see error when new password is less than 8 characters', async () => {
+ render();
+
+ const newPasswordInput = screen.queryByLabelText('رمز عبور جدید');
+ 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.queryByText("رمز عبور باید حداقل 8 کاراکتر باشد.")).toBeInTheDocument();
+ });
+
+ // Simulate clicking the submit button
+ fireEvent.click(submitButton);
+
+ await waitFor(() => {
+ expect(screen.queryByText("رمز عبور باید حداقل 8 کاراکتر باشد.")).toBeInTheDocument();
+ });
+ });
+ it('Should not see error when new password are 8 or more characters', async () => {
+ render();
+
+ const newPasswordInput = screen.queryByLabelText('رمز عبور جدید');
+ 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.queryByLabelText('رمز عبور جدید');
+ const confirmPasswordInput = screen.queryByLabelText('تکرار رمز عبور جدید');
+ 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.queryByText("پسورد و تکرار رمز عبور باید یکسان باشد")).toBeInTheDocument();
+ });
+
+ // Simulate clicking the submit button
+ fireEvent.click(submitButton);
+
+ await waitFor(() => {
+ 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.queryByLabelText('رمز عبور جدید');
+ const confirmPasswordInput = screen.queryByLabelText('تکرار رمز عبور جدید');
+ 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.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' } });
+
+ // Check if the submit button is enabled
+ await waitFor(() => {
+ expect(submitButton).not.toBeDisabled();
+ });
+ });
+ 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/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..74896a6 100644
--- a/src/components/dashboard/change-password/index.jsx
+++ b/src/components/dashboard/change-password/index.jsx
@@ -1,149 +1,18 @@
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 DashboardLayout from "@/layouts/DashboardLayout";
+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")}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- )}
-
+
-
+
);
};
export default DashboardChangePasswordComponent;
diff --git a/src/components/dashboard/edit-profile/index.jsx b/src/components/dashboard/edit-profile/index.jsx
index 3018aa3..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/dashboardLayouts";
+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 ee4f80a..841d677 100644
--- a/src/components/dashboard/first/index.jsx
+++ b/src/components/dashboard/first/index.jsx
@@ -1,7 +1,7 @@
-import DashboardLayouts from "@/layouts/dashboardLayouts";
+import DashboardLayout from "@/layouts/DashboardLayout";
const DashboardFirstComponent = () => {
- return ;
+ return ;
};
export default DashboardFirstComponent;
diff --git a/src/layouts/dashboardLayouts/breadcrumbs/BreadcrumbItem.jsx b/src/components/layouts/Dashboard/Breadcrumbs/BreadcrumbItem.jsx
similarity index 100%
rename from src/layouts/dashboardLayouts/breadcrumbs/BreadcrumbItem.jsx
rename to src/components/layouts/Dashboard/Breadcrumbs/BreadcrumbItem.jsx
diff --git a/src/layouts/dashboardLayouts/breadcrumbs/index.jsx b/src/components/layouts/Dashboard/Breadcrumbs/index.jsx
similarity index 100%
rename from src/layouts/dashboardLayouts/breadcrumbs/index.jsx
rename to src/components/layouts/Dashboard/Breadcrumbs/index.jsx
diff --git a/src/components/layouts/Dashboard/CallWidget/CallWidgetButton.jsx b/src/components/layouts/Dashboard/CallWidget/CallWidgetButton.jsx
new file mode 100644
index 0000000..76af95c
--- /dev/null
+++ b/src/components/layouts/Dashboard/CallWidget/CallWidgetButton.jsx
@@ -0,0 +1,15 @@
+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/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
new file mode 100644
index 0000000..5afb0a1
--- /dev/null
+++ b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/index.jsx
@@ -0,0 +1,48 @@
+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 = () => {
+ const {openCallDialog, setOpenCallDialog} = useCallDialog()
+ const {answersList, newAnswerTab} = useAnswers()
+ const socket = useSocket()
+
+ 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 (
+
+ )
+}
+
+export default CallWidgetDialog
\ No newline at end of file
diff --git a/src/components/layouts/Dashboard/CallWidget/index.jsx b/src/components/layouts/Dashboard/CallWidget/index.jsx
new file mode 100644
index 0000000..d6fe5a0
--- /dev/null
+++ b/src/components/layouts/Dashboard/CallWidget/index.jsx
@@ -0,0 +1,28 @@
+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";
+import {useEffect} from "react";
+
+const CallWidget = () => {
+ const socket = useSocket()
+
+ useEffect(() => {
+ if (socket.connected) return
+
+ socket.connect()
+
+ return () => {
+ socket.disconnect()
+ }
+ }, []);
+
+ return (
+
+
+
+
+ )
+}
+
+export default CallWidget
\ No newline at end of file
diff --git a/src/layouts/dashboardLayouts/header/ProfileData.jsx b/src/components/layouts/Dashboard/Header/ProfileData.jsx
similarity index 100%
rename from src/layouts/dashboardLayouts/header/ProfileData.jsx
rename to src/components/layouts/Dashboard/Header/ProfileData.jsx
diff --git a/src/layouts/dashboardLayouts/header/ProfileMenu.jsx b/src/components/layouts/Dashboard/Header/ProfileMenu.jsx
similarity index 100%
rename from src/layouts/dashboardLayouts/header/ProfileMenu.jsx
rename to src/components/layouts/Dashboard/Header/ProfileMenu.jsx
diff --git a/src/layouts/dashboardLayouts/header/ProfileOptionLogOut.jsx b/src/components/layouts/Dashboard/Header/ProfileOptionLogOut.jsx
similarity index 100%
rename from src/layouts/dashboardLayouts/header/ProfileOptionLogOut.jsx
rename to src/components/layouts/Dashboard/Header/ProfileOptionLogOut.jsx
diff --git a/src/layouts/dashboardLayouts/header/ProfileOptions.jsx b/src/components/layouts/Dashboard/Header/ProfileOptions.jsx
similarity index 100%
rename from src/layouts/dashboardLayouts/header/ProfileOptions.jsx
rename to src/components/layouts/Dashboard/Header/ProfileOptions.jsx
diff --git a/src/layouts/dashboardLayouts/header/index.jsx b/src/components/layouts/Dashboard/Header/index.jsx
similarity index 100%
rename from src/layouts/dashboardLayouts/header/index.jsx
rename to src/components/layouts/Dashboard/Header/index.jsx
diff --git a/src/layouts/dashboardLayouts/sidebar/SidebarDrawer.jsx b/src/components/layouts/Dashboard/Sidebar/SidebarDrawer.jsx
similarity index 93%
rename from src/layouts/dashboardLayouts/sidebar/SidebarDrawer.jsx
rename to src/components/layouts/Dashboard/Sidebar/SidebarDrawer.jsx
index f155dce..33348a5 100644
--- a/src/layouts/dashboardLayouts/sidebar/SidebarDrawer.jsx
+++ b/src/components/layouts/Dashboard/Sidebar/SidebarDrawer.jsx
@@ -14,7 +14,7 @@ const SidebarDrawer = ({handleDrawerToggle}) => {
{t("app_short_name")}
- {user.name} | {user.position}
+ {user.full_name} | {user.position}
diff --git a/src/layouts/dashboardLayouts/sidebar/SidebarList.jsx b/src/components/layouts/Dashboard/Sidebar/SidebarList.jsx
similarity index 98%
rename from src/layouts/dashboardLayouts/sidebar/SidebarList.jsx
rename to src/components/layouts/Dashboard/Sidebar/SidebarList.jsx
index 12d268d..040b455 100644
--- a/src/layouts/dashboardLayouts/sidebar/SidebarList.jsx
+++ b/src/components/layouts/Dashboard/Sidebar/SidebarList.jsx
@@ -18,7 +18,6 @@ function reducer(state, action) {
case "SELECTED":
return state.map((itemsArr) =>
itemsArr.map((item) => {
- console.log(item)
if (item.type === "page") {
if (action.route === item.route)
return {...item, selected: true}
diff --git a/src/layouts/dashboardLayouts/sidebar/SidebarListItem.jsx b/src/components/layouts/Dashboard/Sidebar/SidebarListItem.jsx
similarity index 100%
rename from src/layouts/dashboardLayouts/sidebar/SidebarListItem.jsx
rename to src/components/layouts/Dashboard/Sidebar/SidebarListItem.jsx
diff --git a/src/layouts/dashboardLayouts/sidebar/SidebarListSubItem.jsx b/src/components/layouts/Dashboard/Sidebar/SidebarListSubItem.jsx
similarity index 100%
rename from src/layouts/dashboardLayouts/sidebar/SidebarListSubItem.jsx
rename to src/components/layouts/Dashboard/Sidebar/SidebarListSubItem.jsx
diff --git a/src/layouts/dashboardLayouts/sidebar/index.jsx b/src/components/layouts/Dashboard/Sidebar/index.jsx
similarity index 100%
rename from src/layouts/dashboardLayouts/sidebar/index.jsx
rename to src/components/layouts/Dashboard/Sidebar/index.jsx
diff --git a/src/layouts/dashboardLayouts/index.jsx b/src/components/layouts/Dashboard/index.jsx
similarity index 78%
rename from src/layouts/dashboardLayouts/index.jsx
rename to src/components/layouts/Dashboard/index.jsx
index fae51d2..3ee24c6 100644
--- a/src/layouts/dashboardLayouts/index.jsx
+++ b/src/components/layouts/Dashboard/index.jsx
@@ -1,13 +1,13 @@
-import {useState} from "react";
-import FullPageLayout from "../FullPageLayout";
-import Header from "./header";
-import Sidebar from "./sidebar";
+import Header from "src/components/layouts/Dashboard/Header";
+import Sidebar from "src/components/layouts/Dashboard/Sidebar";
+import FullPageLayout from "@/layouts/FullPageLayout";
import {Toolbar} from "@mui/material";
-import BreadCrumbs from "./breadcrumbs";
+import BreadCrumbs from "src/components/layouts/Dashboard/Breadcrumbs";
+import {useState} from "react";
const drawerWidth = 240;
-const DashboardLayouts = (props) => {
+const Dashboard = (props) => {
const {window} = props;
const [mobileOpen, setMobileOpen] = useState(false);
const container =
@@ -39,7 +39,7 @@ const DashboardLayouts = (props) => {
- );
-};
+ )
+}
-export default DashboardLayouts;
+export default Dashboard
\ No newline at end of file
diff --git a/src/components/login-expert/LoginExpertComponent.jsx b/src/components/login-expert/LoginExpertComponent.jsx
index 9be301b..d2a2d13 100644
--- a/src/components/login-expert/LoginExpertComponent.jsx
+++ b/src/components/login-expert/LoginExpertComponent.jsx
@@ -10,7 +10,7 @@ import useRequest from "@/lib/app/hooks/useRequest";
import useUser from "@/lib/app/hooks/useUser";
import {useTranslations} from "next-intl";
-const LoginExpertComponent = () =>{
+const LoginExpertComponent = () => {
const t = useTranslations();
const requestServer = useRequest()
const {setToken} = useUser(); // pass token to set token
@@ -24,7 +24,7 @@ const LoginExpertComponent = () =>{
notification: {show: false}
}
}).then((response) => {
- setToken(response.data.token)
+ setToken(response.data.data.token)
}).catch(() => {
props.setSubmitting(false)
})
@@ -38,7 +38,7 @@ const LoginExpertComponent = () =>{
password: Yup.string().required(t("LoginPage.password_error_message_required")),
});
- return(
+ return (
;
+});
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/core/components/StyledFab.jsx b/src/core/components/StyledFab.jsx
new file mode 100644
index 0000000..95c5ff7
--- /dev/null
+++ b/src/core/components/StyledFab.jsx
@@ -0,0 +1,10 @@
+import {Fab, styled} from "@mui/material";
+
+const StyledFab = styled(Fab)`
+ position: absolute;
+ bottom: 16px;
+ /* @noflip */
+ left: 16px;
+`;
+
+export default StyledFab;
diff --git a/src/core/components/notifications/ErrorNotification.jsx b/src/core/components/notifications/ErrorNotification.jsx
index 4ff1ab0..05e2fb9 100644
--- a/src/core/components/notifications/ErrorNotification.jsx
+++ b/src/core/components/notifications/ErrorNotification.jsx
@@ -36,4 +36,4 @@ const ErrorNotification = (t, status, message) => {
);
};
-export default ErrorNotification;
+export default ErrorNotification;
\ No newline at end of file
diff --git a/src/core/components/notifications/PendingNotification.jsx b/src/core/components/notifications/PendingNotification.jsx
index 846b847..6183f6b 100644
--- a/src/core/components/notifications/PendingNotification.jsx
+++ b/src/core/components/notifications/PendingNotification.jsx
@@ -9,4 +9,4 @@ const PendingNotification = (t) => {
});
};
-export default PendingNotification;
+export default PendingNotification;
\ No newline at end of file
diff --git a/src/core/components/notifications/SuccessNotification.jsx b/src/core/components/notifications/SuccessNotification.jsx
index 998deaa..3507cef 100644
--- a/src/core/components/notifications/SuccessNotification.jsx
+++ b/src/core/components/notifications/SuccessNotification.jsx
@@ -39,4 +39,4 @@ const SuccessNotification = (t, status) => {
);
};
-export default SuccessNotification;
+export default SuccessNotification;
\ No newline at end of file
diff --git a/src/core/components/notifications/UploadFileNotification.jsx b/src/core/components/notifications/UploadFileNotification.jsx
index 6556944..89ebbb2 100644
--- a/src/core/components/notifications/UploadFileNotification.jsx
+++ b/src/core/components/notifications/UploadFileNotification.jsx
@@ -35,4 +35,4 @@ const UploadFileNotification = (t) => {
);
};
-export default UploadFileNotification;
+export default UploadFileNotification;
\ No newline at end of file
diff --git a/src/core/components/notifications/WarningNotification.jsx b/src/core/components/notifications/WarningNotification.jsx
index 8493180..19d6a2f 100644
--- a/src/core/components/notifications/WarningNotification.jsx
+++ b/src/core/components/notifications/WarningNotification.jsx
@@ -37,4 +37,4 @@ const WarningNotification = (t, status) => {
);
};
-export default WarningNotification;
+export default WarningNotification;
\ No newline at end of file
diff --git a/src/core/data/apiRoutes.js b/src/core/data/apiRoutes.js
index 31b0f8e..0155d4c 100644
--- a/src/core/data/apiRoutes.js
+++ b/src/core/data/apiRoutes.js
@@ -1,15 +1,15 @@
const BASE_URL = process.env.NEXT_PUBLIC_BASE_URL;
//login
-export const GET_USER_TOKEN = BASE_URL + "/dashboard/login";
+export const GET_USER_TOKEN = BASE_URL + "/api/auth/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
-export const GET_USER_ROUTE = BASE_URL + "/dashboard/profile/info";
+export const GET_USER_ROUTE = BASE_URL + "/api/profile/info";
//user data
// role management
diff --git a/src/core/utils/errorHandler.js b/src/core/utils/errorHandler.js
index 9bd18f1..8dbd364 100644
--- a/src/core/utils/errorHandler.js
+++ b/src/core/utils/errorHandler.js
@@ -48,7 +48,7 @@ const isServerError = status => status >= 500 && status <= 599;
const isClientError = status => status >= 400 && status <= 499;
const errorLogic = (response, t, notification) => {
- if (notification) ErrorNotification(t, response.status)
+ if (notification) ErrorNotification(t, response.status, response.data.message)
}
const errorValidation = (response, t, notification) => {
if (notification) {
diff --git a/src/layouts/DashboardLayout.jsx b/src/layouts/DashboardLayout.jsx
new file mode 100644
index 0000000..3c62a77
--- /dev/null
+++ b/src/layouts/DashboardLayout.jsx
@@ -0,0 +1,14 @@
+import CallWidget from "src/components/layouts/Dashboard/CallWidget";
+import Dashboard from "src/components/layouts/Dashboard";
+
+const DashboardLayout = (props) => {
+
+ return (
+ <>
+
+
+ >
+ );
+};
+
+export default DashboardLayout;
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}) => {
{
+ 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: {
+ token: ""
+ }
+ }), [])
+
+ 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/app/contexts/user.jsx b/src/lib/app/contexts/user.jsx
index 10611b8..899ac1b 100644
--- a/src/lib/app/contexts/user.jsx
+++ b/src/lib/app/contexts/user.jsx
@@ -75,7 +75,7 @@ export const UserProvider = ({children}) => {
headers: {authorization: `Bearer ${state.token}`},
})
.then(({data}) => {
- if (typeof callback === "function") callback(data);
+ if (typeof callback === "function") callback(data.data);
})
.catch(error => {
if (error.response) {
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;
}
diff --git a/src/lib/app/hooks/useSocket.jsx b/src/lib/app/hooks/useSocket.jsx
new file mode 100644
index 0000000..3b9855d
--- /dev/null
+++ b/src/lib/app/hooks/useSocket.jsx
@@ -0,0 +1,10 @@
+import {useContext} from "react";
+import {SocketContext} from "@/lib/app/contexts/socket";
+
+const useSocket = () => {
+ const {socket} = useContext(SocketContext)
+
+ return socket
+}
+
+export default useSocket
\ 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
diff --git a/src/pages/_app.jsx b/src/pages/_app.jsx
index e76fbcb..c5fad77 100644
--- a/src/pages/_app.jsx
+++ b/src/pages/_app.jsx
@@ -7,6 +7,7 @@ import {UserProvider} from "@/lib/app/contexts/user";
import "moment/locale/fa";
import {NextIntlProvider} from "next-intl";
import TitlePage from "@/core/components/TitlePage";
+import {SocketProvider} from "@/lib/app/contexts/socket";
const App = ({Component, pageProps}) => {
@@ -18,7 +19,9 @@ const App = ({Component, pageProps}) => {
{pageProps.messages ? : ''}
-
+
+
+