From c5c7630457924098fc983bd24216dcf595a48d5b Mon Sep 17 00:00:00 2001 From: AmirHossein Mahmoodi Date: Wed, 1 Nov 2023 15:59:53 +0330 Subject: [PATCH 01/25] Fixed bug permission user management page --- src/pages/dashboard/expert-management/index.jsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/pages/dashboard/expert-management/index.jsx b/src/pages/dashboard/expert-management/index.jsx index 60dc6e2..9a3f5f0 100644 --- a/src/pages/dashboard/expert-management/index.jsx +++ b/src/pages/dashboard/expert-management/index.jsx @@ -1,11 +1,15 @@ import WithAuthMiddleware from "@/middlewares/WithAuth"; import {parse} from "next-useragent"; import DashboardExpertManagementComponent from "@/components/dashboard/expert-management"; +import RolePermissionMiddleware from "@/middlewares/RolePermission"; +const requiredPermissions = ["manage_users"]; export default function ExpertManagement() { return ( - + + + ); } From 759babc0c79f68eb1c071c9bbcb2dcf342b14b9f Mon Sep 17 00:00:00 2001 From: AmirHossein Mahmoodi Date: Wed, 1 Nov 2023 16:25:41 +0330 Subject: [PATCH 02/25] Fixed bug locale role management page --- src/pages/dashboard/role-management/index.jsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/pages/dashboard/role-management/index.jsx b/src/pages/dashboard/role-management/index.jsx index c309729..fa2bedb 100644 --- a/src/pages/dashboard/role-management/index.jsx +++ b/src/pages/dashboard/role-management/index.jsx @@ -21,6 +21,7 @@ export async function getServerSideProps({req, locale}) { messages: (await import(`&/locales/${locale}/app.json`)).default, title: "Dashboard.role_management_page", isBot, + locale }, }; } From f1d090d25dc2c0b5331270dbff119e9e52a81e2c Mon Sep 17 00:00:00 2001 From: AminGhasempoor Date: Sat, 4 Nov 2023 09:30:21 +0330 Subject: [PATCH 03/25] CFE-10 disable submit button in role management --- .../dashboard/role-management/Form/CreateForm/CreateContent.jsx | 2 +- .../dashboard/role-management/Form/UpdateForm/UpdateContent.jsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/dashboard/role-management/Form/CreateForm/CreateContent.jsx b/src/components/dashboard/role-management/Form/CreateForm/CreateContent.jsx index d06659b..8537aab 100644 --- a/src/components/dashboard/role-management/Form/CreateForm/CreateContent.jsx +++ b/src/components/dashboard/role-management/Form/CreateForm/CreateContent.jsx @@ -143,7 +143,7 @@ const CreateContent = ({mutate, setOpenConfirmDialog}) => { {t("AddDialog.button-cancel")} diff --git a/src/components/dashboard/role-management/Form/UpdateForm/UpdateContent.jsx b/src/components/dashboard/role-management/Form/UpdateForm/UpdateContent.jsx index 3fee10a..390d7bb 100644 --- a/src/components/dashboard/role-management/Form/UpdateForm/UpdateContent.jsx +++ b/src/components/dashboard/role-management/Form/UpdateForm/UpdateContent.jsx @@ -143,7 +143,7 @@ const UpdateContent = ({mutate, row, setOpenConfirmDialog}) => { {t("UpdateDialog.button-cancel")} From b7b660cf001454f152d1a129d50a0ad93c992dab Mon Sep 17 00:00:00 2001 From: AminGhasempoor Date: Sat, 4 Nov 2023 09:54:09 +0330 Subject: [PATCH 04/25] CFE-10 correct tests --- .../CreateForm/__test__/CreateContent.test.js | 18 ------------------ 1 file changed, 18 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 610e224..921a3d9 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 @@ -145,23 +145,5 @@ describe("Create Content component from Create Form Component in Role Management 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 From 6c571f679005a3eac9bd89623b2c85b853caa248 Mon Sep 17 00:00:00 2001 From: AminGhasempoor Date: Sat, 4 Nov 2023 10:12:39 +0330 Subject: [PATCH 05/25] CFE-10 test if submit button is disabled at first time --- .../Form/CreateForm/__test__/CreateContent.test.js | 9 +++++++++ .../Form/UpdateForm/__test__/UpdateContent.test.js | 11 ++++++++++- 2 files changed, 19 insertions(+), 1 deletion(-) 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 921a3d9..af8842b 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 @@ -145,5 +145,14 @@ describe("Create Content component from Create Form Component in Role Management expect(screen.queryByText("وارد کردن نام انگلیسی الزامیست")).not.toBeInTheDocument() }) }) + it('should submit button be disabled', () => { + render( + + + + ) + const submitButtonElement = screen.queryByText("ثبت") + expect(submitButtonElement).toBeDisabled() + }); }) }) \ 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 caaa470..56bbcf1 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 @@ -1,4 +1,4 @@ -import {act, fireEvent, render, screen, waitFor} from "@testing-library/react"; +import {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 = { @@ -205,5 +205,14 @@ describe("Create Content component from Create Form Component in Role Management expect(screen.queryByText("وارد کردن نام انگلیسی الزامیست")).not.toBeInTheDocument() }) }) + it('should submit button be disabled', () => { + render( + + + + ) + const submitButtonElement = screen.queryByText("ثبت") + expect(submitButtonElement).toBeDisabled() + }); }) }) \ No newline at end of file From 0b159e15dee55596af86426d7b5107f78bc5d151 Mon Sep 17 00:00:00 2001 From: AmirHossein Mahmoodi Date: Sat, 4 Nov 2023 10:33:37 +0330 Subject: [PATCH 06/25] Change structure Dashboard layout --- .../dashboard/change-password/index.jsx | 13 +- .../dashboard/edit-profile/index.jsx | 279 +++++++++--------- .../dashboard/expert-management/index.jsx | 5 +- src/components/dashboard/first/index.jsx | 4 +- .../dashboard/role-management/index.jsx | 6 +- src/components/layouts/Dashboard/index.jsx | 7 +- src/layouts/DashboardLayout.jsx | 7 +- src/layouts/index.jsx | 20 ++ src/middlewares/RolePermission.jsx | 6 +- src/pages/_app.jsx | 5 +- src/pages/dashboard/change-password/index.jsx | 8 +- src/pages/dashboard/edit-profile/index.jsx | 8 +- .../dashboard/expert-management/index.jsx | 12 +- src/pages/dashboard/index.jsx | 8 +- src/pages/dashboard/role-management/index.jsx | 12 +- 15 files changed, 197 insertions(+), 203 deletions(-) create mode 100644 src/layouts/index.jsx diff --git a/src/components/dashboard/change-password/index.jsx b/src/components/dashboard/change-password/index.jsx index 74896a6..7f21558 100644 --- a/src/components/dashboard/change-password/index.jsx +++ b/src/components/dashboard/change-password/index.jsx @@ -1,18 +1,15 @@ import CenterLayout from "@/layouts/CenterLayout"; -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 d15d467..af9a869 100644 --- a/src/components/dashboard/edit-profile/index.jsx +++ b/src/components/dashboard/edit-profile/index.jsx @@ -1,6 +1,5 @@ import StyledForm from "@/core/components/StyledForm"; import CenterLayout from "@/layouts/CenterLayout"; -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"; @@ -50,148 +49,146 @@ const DashboardEditProfile = () => { const validationSchema = Yup.object().shape({}); return ( - - - - - {(props) => ( - { - e.preventDefault(); - props.handleSubmit(); - }} - > - - - - {t("UpdateProfile.typography_edit_profile")} - - - + + + {(props) => ( + { + e.preventDefault(); + props.handleSubmit(); + }} + > + + + + {t("UpdateProfile.typography_edit_profile")} + + + + + + + - - - - - - - - - - - - - - - - - - - - - - )} - - - - + + + + + + + + + + + + + + + + + + )} + + + ); }; diff --git a/src/components/dashboard/expert-management/index.jsx b/src/components/dashboard/expert-management/index.jsx index 632d89b..cd90e4f 100644 --- a/src/components/dashboard/expert-management/index.jsx +++ b/src/components/dashboard/expert-management/index.jsx @@ -1,11 +1,8 @@ -import DashboardLayouts from "@/layouts/DashboardLayout"; import ExpertManagementDataTable from "@/components/dashboard/expert-management/DataTable"; function DashboardExpertManagementComponent() { return ( - - - + ); } diff --git a/src/components/dashboard/first/index.jsx b/src/components/dashboard/first/index.jsx index 841d677..02e5672 100644 --- a/src/components/dashboard/first/index.jsx +++ b/src/components/dashboard/first/index.jsx @@ -1,7 +1,5 @@ -import DashboardLayout from "@/layouts/DashboardLayout"; - const DashboardFirstComponent = () => { - return ; + return <>; }; export default DashboardFirstComponent; diff --git a/src/components/dashboard/role-management/index.jsx b/src/components/dashboard/role-management/index.jsx index 6376b30..fac37e2 100644 --- a/src/components/dashboard/role-management/index.jsx +++ b/src/components/dashboard/role-management/index.jsx @@ -1,12 +1,8 @@ import RoleManagementComponent from "@/components/dashboard/role-management/RoleManagementComponent"; -import DashboardLayout from "@/layouts/DashboardLayout"; -import moment from "jalali-moment"; function DashboardRoleManagementComponent() { return ( - - - + ); } diff --git a/src/components/layouts/Dashboard/index.jsx b/src/components/layouts/Dashboard/index.jsx index 3ee24c6..e715e04 100644 --- a/src/components/layouts/Dashboard/index.jsx +++ b/src/components/layouts/Dashboard/index.jsx @@ -4,6 +4,7 @@ import FullPageLayout from "@/layouts/FullPageLayout"; import {Toolbar} from "@mui/material"; import BreadCrumbs from "src/components/layouts/Dashboard/Breadcrumbs"; import {useState} from "react"; +import RolePermissionMiddleware from "@/middlewares/RolePermission"; const drawerWidth = 240; @@ -34,8 +35,10 @@ const Dashboard = (props) => { > - - {props.children} + + + {props.children} + diff --git a/src/layouts/DashboardLayout.jsx b/src/layouts/DashboardLayout.jsx index 72344d6..59b1269 100644 --- a/src/layouts/DashboardLayout.jsx +++ b/src/layouts/DashboardLayout.jsx @@ -1,16 +1,17 @@ import Dashboard from "src/components/layouts/Dashboard"; import AppPermission from "@/middlewares/AppPermission"; import CallWidget from "@/components/layouts/Dashboard/CallWidget"; +import WithAuthMiddleware from "@/middlewares/WithAuth"; const DashboardLayout = (props) => { const permissions = ["NEXT_PUBLIC_HAS_WIDGET"]; return ( - <> + - + - + ); }; diff --git a/src/layouts/index.jsx b/src/layouts/index.jsx new file mode 100644 index 0000000..a620a0e --- /dev/null +++ b/src/layouts/index.jsx @@ -0,0 +1,20 @@ +import DashboardLayout from "@/layouts/DashboardLayout"; +import {Fragment} from "react"; + +const layoutList = { + DashboardLayout +} + +const Layout = ({layout, children}) => { + + const Component = layoutList[layout?.name] || Fragment + const props = layout?.props || {} + + return ( + + {children} + + ) +} + +export default Layout \ No newline at end of file diff --git a/src/middlewares/RolePermission.jsx b/src/middlewares/RolePermission.jsx index 4b1cc8a..c546220 100644 --- a/src/middlewares/RolePermission.jsx +++ b/src/middlewares/RolePermission.jsx @@ -1,14 +1,14 @@ import useUser from "@/lib/app/hooks/useUser"; import RolePermissionComponent from "@/core/components/Middleware/RolePermissionComponent"; -const RolePermissionMiddleware = ({children, requiredPermissions}) => { +const RolePermissionMiddleware = ({children, requiredPermissions = []}) => { const {user} = useUser(); - const hasPermission = requiredPermissions.some((permission) => + const hasPermission = requiredPermissions.length === 0 ? true : requiredPermissions.some((permission) => user?.permissions?.includes(permission) ); - return !hasPermission ? : <>{children}; + return !hasPermission ? : <>{children}; }; export default RolePermissionMiddleware; diff --git a/src/pages/_app.jsx b/src/pages/_app.jsx index 2d23745..c7b75d4 100644 --- a/src/pages/_app.jsx +++ b/src/pages/_app.jsx @@ -9,6 +9,7 @@ import {NextIntlProvider} from "next-intl"; import TitlePage from "@/core/components/TitlePage"; import {SocketProvider} from "@/lib/app/contexts/socket"; import {ToastProvider} from "@/lib/app/contexts/toast"; +import Layout from "@/layouts"; const App = ({Component, pageProps}) => { @@ -22,7 +23,9 @@ const App = ({Component, pageProps}) => { - + + + diff --git a/src/pages/dashboard/change-password/index.jsx b/src/pages/dashboard/change-password/index.jsx index f744382..ef75fb7 100644 --- a/src/pages/dashboard/change-password/index.jsx +++ b/src/pages/dashboard/change-password/index.jsx @@ -1,12 +1,9 @@ import DashboardChangePasswordComponent from "@/components/dashboard/change-password"; -import WithAuthMiddleware from "@/middlewares/WithAuth"; import {parse} from "next-useragent"; export default function LoanFollowUp() { return ( - - - + ); } @@ -17,7 +14,8 @@ export async function getServerSideProps({req, locale}) { messages: (await import(`&/locales/${locale}/app.json`)).default, title: "Dashboard.change_password", isBot, - locale + locale, + layout: {name: 'DashboardLayout'} }, }; } diff --git a/src/pages/dashboard/edit-profile/index.jsx b/src/pages/dashboard/edit-profile/index.jsx index dfa2eec..032a607 100644 --- a/src/pages/dashboard/edit-profile/index.jsx +++ b/src/pages/dashboard/edit-profile/index.jsx @@ -1,12 +1,9 @@ import DashboardEditProfile from "@/components/dashboard/edit-profile"; -import WithAuthMiddleware from "@/middlewares/WithAuth"; import {parse} from "next-useragent"; export default function LoanFollowUp() { return ( - - - + ); } @@ -17,7 +14,8 @@ export async function getServerSideProps({req, locale}) { messages: (await import(`&/locales/${locale}/app.json`)).default, title: "Dashboard.edit_profile", isBot, - locale + locale, + layout: {name: 'DashboardLayout'} }, }; } diff --git a/src/pages/dashboard/expert-management/index.jsx b/src/pages/dashboard/expert-management/index.jsx index 9a3f5f0..2daf0bb 100644 --- a/src/pages/dashboard/expert-management/index.jsx +++ b/src/pages/dashboard/expert-management/index.jsx @@ -1,16 +1,9 @@ -import WithAuthMiddleware from "@/middlewares/WithAuth"; import {parse} from "next-useragent"; import DashboardExpertManagementComponent from "@/components/dashboard/expert-management"; -import RolePermissionMiddleware from "@/middlewares/RolePermission"; -const requiredPermissions = ["manage_users"]; export default function ExpertManagement() { return ( - - - - - + ); } @@ -21,7 +14,8 @@ export async function getServerSideProps({req, locale}) { messages: (await import(`&/locales/${locale}/app.json`)).default, title: "Dashboard.expert_management", isBot, - locale + locale, + layout: {name: 'DashboardLayout', props: {permissions: ["manage_users"]}} }, }; } diff --git a/src/pages/dashboard/index.jsx b/src/pages/dashboard/index.jsx index eed56eb..0e309c3 100644 --- a/src/pages/dashboard/index.jsx +++ b/src/pages/dashboard/index.jsx @@ -1,12 +1,9 @@ import DashboardFirstComponent from "@/components/dashboard/first"; -import WithAuthMiddleware from "@/middlewares/WithAuth"; import {parse} from "next-useragent"; export default function Dashboard() { return ( - - - + ); } @@ -17,7 +14,8 @@ export async function getServerSideProps({req, locale}) { messages: (await import(`&/locales/${locale}/app.json`)).default, title: "Dashboard.dashboard_page", isBot, - locale + locale, + layout: {name: 'DashboardLayout'} }, }; } diff --git a/src/pages/dashboard/role-management/index.jsx b/src/pages/dashboard/role-management/index.jsx index fa2bedb..7ae6d45 100644 --- a/src/pages/dashboard/role-management/index.jsx +++ b/src/pages/dashboard/role-management/index.jsx @@ -1,16 +1,9 @@ -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 ( - - - - - + ); } @@ -21,7 +14,8 @@ export async function getServerSideProps({req, locale}) { messages: (await import(`&/locales/${locale}/app.json`)).default, title: "Dashboard.role_management_page", isBot, - locale + locale, + layout: {name: 'DashboardLayout', props: {permissions: ["manage_roles"]}} }, }; } From 0c4158aa74b8f50438be2d318e8637a9d21eabe4 Mon Sep 17 00:00:00 2001 From: AmirHossein Mahmoodi Date: Sat, 4 Nov 2023 10:38:53 +0330 Subject: [PATCH 07/25] fixed bug imports --- src/layouts/DashboardLayout.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/layouts/DashboardLayout.jsx b/src/layouts/DashboardLayout.jsx index 59b1269..668f9d8 100644 --- a/src/layouts/DashboardLayout.jsx +++ b/src/layouts/DashboardLayout.jsx @@ -1,7 +1,7 @@ -import Dashboard from "src/components/layouts/Dashboard"; import AppPermission from "@/middlewares/AppPermission"; import CallWidget from "@/components/layouts/Dashboard/CallWidget"; import WithAuthMiddleware from "@/middlewares/WithAuth"; +import Dashboard from "@/components/layouts/Dashboard"; const DashboardLayout = (props) => { const permissions = ["NEXT_PUBLIC_HAS_WIDGET"]; From 1e8b14cff62acddf23f4fc05bffdf346f020b5bd Mon Sep 17 00:00:00 2001 From: AmirHossein Mahmoodi Date: Sat, 4 Nov 2023 10:48:32 +0330 Subject: [PATCH 08/25] fixed bug imports --- .../CallWidget/CallWidgetDialog/CallTabs/index.jsx | 2 +- src/components/layouts/Dashboard/index.jsx | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/index.jsx b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/index.jsx index 8a96506..975a8b7 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 "src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabsList"; +import CallTabsList from "@/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabsList"; const CallTabs = () => { const {answersList} = useAnswers() diff --git a/src/components/layouts/Dashboard/index.jsx b/src/components/layouts/Dashboard/index.jsx index e715e04..de3b229 100644 --- a/src/components/layouts/Dashboard/index.jsx +++ b/src/components/layouts/Dashboard/index.jsx @@ -1,10 +1,10 @@ -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 "src/components/layouts/Dashboard/Breadcrumbs"; import {useState} from "react"; import RolePermissionMiddleware from "@/middlewares/RolePermission"; +import Header from "@/components/layouts/Dashboard/Header"; +import Sidebar from "@/components/layouts/Dashboard/Sidebar"; +import BreadCrumbs from "@/components/layouts/Dashboard/Breadcrumbs"; const drawerWidth = 240; From 18375f1c5ccfacf9c8bea113da3ce9f50f586168 Mon Sep 17 00:00:00 2001 From: AminGhasempoor Date: Sat, 4 Nov 2023 11:25:18 +0330 Subject: [PATCH 09/25] CFE-10 testing disabled button --- mocks/handlers/roles.js | 4 ++-- .../Form/CreateForm/CreateContent.jsx | 6 +++--- .../CreateForm/__test__/CreateContent.test.js | 19 +++++++++++++++++++ 3 files changed, 24 insertions(+), 5 deletions(-) diff --git a/mocks/handlers/roles.js b/mocks/handlers/roles.js index 1c26ef5..2ab045d 100644 --- a/mocks/handlers/roles.js +++ b/mocks/handlers/roles.js @@ -1,5 +1,5 @@ import {rest} from "msw"; -import {GET_ROLE_LIST, GET_ROLES} from "@/core/data/apiRoutes"; +import {GET_PERMISSIONS_LIST, GET_ROLE_LIST, GET_ROLES} from "@/core/data/apiRoutes"; export const rolesHandler = [ rest.get(GET_ROLES, (req, res, ctx) => { @@ -51,7 +51,7 @@ export const rolesHandler = [ ), ); }), - rest.get(GET_ROLE_LIST, (req, res, ctx) => { + rest.get(GET_PERMISSIONS_LIST, (req, res, ctx) => { return res(ctx.json({ data: [ { diff --git a/src/components/dashboard/role-management/Form/CreateForm/CreateContent.jsx b/src/components/dashboard/role-management/Form/CreateForm/CreateContent.jsx index 8537aab..8a8f5e7 100644 --- a/src/components/dashboard/role-management/Form/CreateForm/CreateContent.jsx +++ b/src/components/dashboard/role-management/Form/CreateForm/CreateContent.jsx @@ -108,12 +108,12 @@ const CreateContent = ({mutate, setOpenConfirmDialog}) => { : ( <> - {permissions_list.map((permission) => ( - + {permissions_list.map((permission, index) => ( + { if (e.target.checked) { 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 af8842b..2733f9f 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 @@ -154,5 +154,24 @@ describe("Create Content component from Create Form Component in Role Management const submitButtonElement = screen.queryByText("ثبت") expect(submitButtonElement).toBeDisabled() }); + + it('should submit button be able if inputs get filled', async () => { + render( + + + + ) + const submitButtonElement = screen.queryByText("ثبت") + expect(submitButtonElement).toBeDisabled() + const nameElement = screen.getByLabelText("نام انگلیسی") + const name_faElement = screen.getByLabelText("نام فارسی") + const roleElement = await screen.findByTestId("PermissionList-checkbox-0"); + fireEvent.change(nameElement, { target: { value: 'amin' } }) + fireEvent.change(name_faElement, { target: { value: 'امین' } }) + fireEvent.click(roleElement) + await waitFor(()=>{ + expect(submitButtonElement).not.toBeDisabled() + }) + }); }) }) \ No newline at end of file From d9a24256659fca995b35243df689b566b1dfc775 Mon Sep 17 00:00:00 2001 From: AminGhasempoor Date: Sat, 4 Nov 2023 11:38:54 +0330 Subject: [PATCH 10/25] CFE-10 write mock for permission lists --- mocks/handlers/roles.js | 16 +++++ .../UpdateForm/__test__/UpdateContent.test.js | 64 +++++++++---------- 2 files changed, 48 insertions(+), 32 deletions(-) diff --git a/mocks/handlers/roles.js b/mocks/handlers/roles.js index 2ab045d..2760fc2 100644 --- a/mocks/handlers/roles.js +++ b/mocks/handlers/roles.js @@ -51,6 +51,22 @@ export const rolesHandler = [ ), ); }), + rest.get(GET_ROLE_LIST, (req, res, ctx) => { + return res(ctx.json({ + data: [ + { + id: 1, + name: "admin", + name_fa: "ادمین" + }, + { + id: 2, + name: "manager", + name_fa: "مدیر" + } + ] + })) + }), rest.get(GET_PERMISSIONS_LIST, (req, res, ctx) => { return res(ctx.json({ data: [ 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 56bbcf1..1130d94 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 @@ -1,18 +1,18 @@ import {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 => { + id: 0, + getValue: name => { if (name === "name") { return "manage_passenger_office_navgan"; - } - else if (name === "name_fa") { + } else if (name === "name_fa") { return "مدیریت کارتابل رییس اداره مسافری استان"; } }, - original : { - permissions : [ + original: { + permissions: [ { id: 1, name: "manage_passenger_office_navgan", @@ -27,8 +27,8 @@ const row = { } } -describe("Create Content component from Create Form Component in Role Management Component",()=>{ - describe("Rendering", ()=>{ +describe("Create Content component from Create Form Component in Role Management Component", () => { + describe("Rendering", () => { it('should see AddDialog text in the top ', async () => { render( @@ -36,8 +36,8 @@ describe("Create Content component from Create Form Component in Role Management ) const textElement = screen.queryByText("ویرایش") - await waitFor(()=>{ - expect(textElement).toBeInTheDocument() + await waitFor(() => { + expect(textElement).toBeInTheDocument() }) }); it('should see name_en text', async () => { @@ -47,8 +47,8 @@ describe("Create Content component from Create Form Component in Role Management ) const textElement = screen.queryByLabelText("نام انگلیسی") - await waitFor(()=>{ - expect(textElement).toBeInTheDocument() + await waitFor(() => { + expect(textElement).toBeInTheDocument() }) }); it('should see name_fa text', async () => { @@ -58,8 +58,8 @@ describe("Create Content component from Create Form Component in Role Management ) const textElement = screen.queryByLabelText("نام فارسی") - await waitFor(()=>{ - expect(textElement).toBeInTheDocument() + await waitFor(() => { + expect(textElement).toBeInTheDocument() }) }); it('should see update text in the submit button ', async () => { @@ -69,7 +69,7 @@ describe("Create Content component from Create Form Component in Role Management ) const buttonElement = screen.queryByText("ثبت") - await waitFor(()=>{ + await waitFor(() => { expect(buttonElement).toBeInTheDocument() }) }); @@ -80,13 +80,13 @@ describe("Create Content component from Create Form Component in Role Management ) const buttonElement = screen.queryByText("بستن") - await waitFor(()=>{ + await waitFor(() => { expect(buttonElement).toBeInTheDocument() }) }); }) - describe("Behavior", ()=>{ + describe("Behavior", () => { it('should see what fill in the name_en input', async () => { render( @@ -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 waitFor(()=>{ + 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 waitFor(()=>{ + await waitFor(() => { // Simulate user input expect(nameFarsiInput).toHaveValue('testuser'); }) @@ -122,7 +122,7 @@ describe("Create Content component from Create Form Component in Role Management ) const checkboxElement = await screen.findAllByTestId("PermissionList-checkbox") - await waitFor(()=>{ + await waitFor(() => { expect(checkboxElement).toHaveLength(2) }) }); @@ -133,7 +133,7 @@ describe("Create Content component from Create Form Component in Role Management ) const name_enElement = screen.getByLabelText("نام انگلیسی") - await waitFor(()=>{ + await waitFor(() => { expect(name_enElement).toHaveValue("manage_passenger_office_navgan") }) }); @@ -144,7 +144,7 @@ describe("Create Content component from Create Form Component in Role Management ) const name_enElement = screen.getByLabelText("نام فارسی") - await waitFor(()=>{ + await waitFor(() => { expect(name_enElement).toHaveValue("مدیریت کارتابل رییس اداره مسافری استان") }) }); @@ -155,13 +155,13 @@ describe("Create Content component from Create Form Component in Role Management ) const checkboxElement = screen.getByLabelText("مدیریت کارتابل رییس اداره مسافری استان") - await waitFor(()=>{ + await waitFor(() => { expect(checkboxElement).toBeChecked(); }) }); }) - describe("Validation", ()=>{ + describe("Validation", () => { it('should see error text when name_fa input is empty', async () => { render( @@ -170,16 +170,16 @@ describe("Create Content component from Create Form Component in Role Management ) const name_faInput = screen.getByLabelText("نام فارسی") expect(screen.queryByText("وارد کردن نام فارسی الزامیست")).not.toBeInTheDocument() - fireEvent.change(name_faInput, {target : {value : ''}}); + fireEvent.change(name_faInput, {target: {value: ''}}); fireEvent.blur(name_faInput) - await waitFor(()=>{ + await waitFor(() => { expect(screen.queryByText("وارد کردن نام فارسی الزامیست")).toBeInTheDocument() }) - fireEvent.change(name_faInput, {target : {value : "نام فارسی"}}) + fireEvent.change(name_faInput, {target: {value: "نام فارسی"}}) - await waitFor(()=>{ + await waitFor(() => { expect(screen.queryByText("وارد کردن نام فارسی الزامیست")).not.toBeInTheDocument() }) }) @@ -192,16 +192,16 @@ describe("Create Content component from Create Form Component in Role Management const name_enInput = screen.getByLabelText("نام انگلیسی") expect(screen.queryByText("وارد کردن نام انگلیسی الزامیست")).not.toBeInTheDocument() - fireEvent.change(name_enInput, {target : {value : null}}); + fireEvent.change(name_enInput, {target: {value: null}}); fireEvent.blur(name_enInput) - await waitFor(()=>{ + await waitFor(() => { expect(screen.queryByText("وارد کردن نام انگلیسی الزامیست")).toBeInTheDocument() }) - fireEvent.change(name_enInput, {target : {value : "english name"}}) + fireEvent.change(name_enInput, {target: {value: "english name"}}) - await waitFor(()=>{ + await waitFor(() => { expect(screen.queryByText("وارد کردن نام انگلیسی الزامیست")).not.toBeInTheDocument() }) }) From 3b7f9c8b2a57a8034323e7fe51795f76106420e7 Mon Sep 17 00:00:00 2001 From: Yasiu1376 Date: Mon, 6 Nov 2023 11:35:27 +0330 Subject: [PATCH 11/25] CFE-12 implement of sidebar --- .../layouts/Dashboard/Sidebar/SidebarList.jsx | 34 +++++++++++++++---- .../Dashboard/Sidebar/SidebarListItem.jsx | 32 ++++++++--------- .../Dashboard/Sidebar/SidebarListSubItem.jsx | 7 ++-- .../layouts/Dashboard/Sidebar/index.jsx | 2 ++ 4 files changed, 50 insertions(+), 25 deletions(-) diff --git a/src/components/layouts/Dashboard/Sidebar/SidebarList.jsx b/src/components/layouts/Dashboard/Sidebar/SidebarList.jsx index 1e570f5..265da6c 100644 --- a/src/components/layouts/Dashboard/Sidebar/SidebarList.jsx +++ b/src/components/layouts/Dashboard/Sidebar/SidebarList.jsx @@ -1,5 +1,5 @@ import {Divider, List} from "@mui/material"; -import {Fragment, useEffect, useReducer} from "react"; +import {Fragment, useEffect, useReducer, useRef, useState} from "react"; import SidebarListItem from "./SidebarListItem"; import sidebarMenu from "@/core/data/sidebarMenu"; import {useRouter} from "next/router"; @@ -15,10 +15,22 @@ function reducer(state, action) { ); case "SELECTED": return state.map((itemsArr) => - itemsArr.map((item) => ({ - ...item, - selected: item.type === "page" && action.route === item.route, - })) + itemsArr.map((item) => { + return item.type === "page" + ? { ...item, selected: action.route === item.route } + : item.subItem && Array.isArray(item.subItem) + ? { + ...item, + subItem: item.subItem.map((subitem) => ({ + ...subitem, + selected: subitem.route === action.route, + })), + showSubItem: item.subItem.some( + (subitem) => subitem.selected + ), + } + : item; + }) ); default: throw new Error(); @@ -29,13 +41,23 @@ export default function SidebarList({handleDrawerToggle}) { const [itemMenu, dispatch] = useReducer(reducer, sidebarMenu); const {user} = useUser(); const router = useRouter(); + const [selectedKey, setSelectedKey] = useState(null); useEffect(() => { dispatch({type: "SELECTED", route: router.pathname}); + setSelectedKey(router.pathname); }, [router.pathname]); + + useEffect(() => { + selectedKey && document.querySelector(`[data-route="${selectedKey}"]`)?.scrollIntoView({ + behavior: "smooth", + block: "center" + }); + }, [selectedKey]); + return ( - + {itemMenu.map((itemArr, index) => ( {itemArr.map((item) => diff --git a/src/components/layouts/Dashboard/Sidebar/SidebarListItem.jsx b/src/components/layouts/Dashboard/Sidebar/SidebarListItem.jsx index 2195ed3..e9657a9 100644 --- a/src/components/layouts/Dashboard/Sidebar/SidebarListItem.jsx +++ b/src/components/layouts/Dashboard/Sidebar/SidebarListItem.jsx @@ -10,26 +10,26 @@ import useNotification from "@/lib/app/hooks/useNotification"; const SidebarListItem = ({item, dispatch, handleDrawerToggle}) => { const t = useTranslations(); const { notification_count } = useNotification(); - const hasSubItems = item.type === "menu" && item.subItem && item.subItem.length > 0; - - const renderBadge = () => ( - - - - ); + const renderBadge = () => { + return !hasSubItems ? ( + + + + ) : null; + }; return ( <> - + { +const SidebarListSubItem = ({item, handleDrawerToggle }) => { const t = useTranslations(); const {notification_count} = useNotification(); - const renderBadge = (subitem) => ( { ); return ( + - + {item.subItem.map((subitem) => ( { ))} + ); }; diff --git a/src/components/layouts/Dashboard/Sidebar/index.jsx b/src/components/layouts/Dashboard/Sidebar/index.jsx index c0b6d6d..3c4cf12 100644 --- a/src/components/layouts/Dashboard/Sidebar/index.jsx +++ b/src/components/layouts/Dashboard/Sidebar/index.jsx @@ -22,6 +22,7 @@ const Sidebar = (props) => { "& .MuiDrawer-paper": { boxSizing: "border-box", width: props.drawerWidth, + overflow: "hidden", }, }} > @@ -34,6 +35,7 @@ const Sidebar = (props) => { "& .MuiDrawer-paper": { boxSizing: "border-box", width: props.drawerWidth, + overflow: "hidden", }, }} open From 98325695b30005050dd738e2b12c3f095813615f Mon Sep 17 00:00:00 2001 From: AmirHossein Mahmoodi Date: Mon, 6 Nov 2023 14:56:42 +0330 Subject: [PATCH 12/25] CFE-15 Implementation of loading for navigation pages dashboard --- public/locales/fa/app.json | 11 +-- .../layouts/Dashboard/Content/index.jsx | 65 ++++++++++++++++ .../layouts/Dashboard/Sidebar/SidebarList.jsx | 40 ++++++++-- .../Dashboard/Sidebar/SidebarListItem.jsx | 22 ++++-- .../Dashboard/Sidebar/SidebarListSubItem.jsx | 8 +- .../layouts/Dashboard/Sidebar/index.jsx | 77 ++++++++++--------- src/components/layouts/Dashboard/index.jsx | 10 +-- src/core/components/LoadingHardPage.jsx | 31 +++++--- src/core/data/sidebarMenu.jsx | 8 +- src/layouts/index.jsx | 2 +- 10 files changed, 198 insertions(+), 76 deletions(-) create mode 100644 src/components/layouts/Dashboard/Content/index.jsx diff --git a/public/locales/fa/app.json b/public/locales/fa/app.json index 7d89691..21776ae 100644 --- a/public/locales/fa/app.json +++ b/public/locales/fa/app.json @@ -15,6 +15,7 @@ "between": "میان", "online_message": "شما به اینترنت وصل هستید", "offline_message": "اتصال شما به اینترنت قطع شده است", + "routing_to": "در حال انتقال به صفحه", "socket_is_connect_message": "شما به سوکت وصل هستید", "socket_is_not_connect_message": "اتصال شما به سوکت قطع شده است", "header": { @@ -264,10 +265,10 @@ }, "CallAction": { "call_history_of": "عملیات های مربوط به تماس", - "categories" : "موضوع ها", - "subcategories" : "زیر موضوع ها", - "description" : "توضیحات تماس (اختیاری)", - "text_field_label" : "توضیحات", - "text_field_palacholder" : "لطفا توضیحات خود را وارد نمایید" + "categories": "موضوع ها", + "subcategories": "زیر موضوع ها", + "description": "توضیحات تماس (اختیاری)", + "text_field_label": "توضیحات", + "text_field_palacholder": "لطفا توضیحات خود را وارد نمایید" } } diff --git a/src/components/layouts/Dashboard/Content/index.jsx b/src/components/layouts/Dashboard/Content/index.jsx new file mode 100644 index 0000000..e3f39db --- /dev/null +++ b/src/components/layouts/Dashboard/Content/index.jsx @@ -0,0 +1,65 @@ +import LoadingHardPage from "@/core/components/LoadingHardPage"; +import RolePermissionMiddleware from "@/middlewares/RolePermission"; +import BreadCrumbs from "@/components/layouts/Dashboard/Breadcrumbs"; +import FullPageLayout from "@/layouts/FullPageLayout"; +import {useEffect, useMemo, useState} from "react"; +import sidebarMenu from "@/core/data/sidebarMenu"; +import {useRouter} from "next/router"; +import {useTranslations} from "next-intl"; + +const Content = (props) => { + const t = useTranslations() + const router = useRouter() + const [routing, setRouting] = useState(false) + const [routingItem, setRoutingItem] = useState({}) + const pageList = useMemo(() => { + const list = [] + for (const menu of sidebarMenu) { + for (const item of menu) { + if (item.type === 'menu') { + for (const subItem of item.subItem) { + list.push(subItem) + } + } else { + list.push(item) + } + } + } + + return list + }, []) + + useEffect(() => { + const handlerStartRoute = (url) => { + setRoutingItem(pageList.find(page => page.route === url)) + setRouting(true) + } + const handlerCompleteRoute = () => { + setRouting(false) + } + + router.events.on('routeChangeStart', handlerStartRoute) + router.events.on('routeChangeComplete', handlerCompleteRoute) + + return () => { + router.events.off('routeChangeStart', handlerStartRoute) + router.events.off('routeChangeComplete', handlerCompleteRoute) + } + }, [router]); + + return ( + + + + + {props.children} + + + + ) +} + +export default Content \ No newline at end of file diff --git a/src/components/layouts/Dashboard/Sidebar/SidebarList.jsx b/src/components/layouts/Dashboard/Sidebar/SidebarList.jsx index 265da6c..3a2eb44 100644 --- a/src/components/layouts/Dashboard/Sidebar/SidebarList.jsx +++ b/src/components/layouts/Dashboard/Sidebar/SidebarList.jsx @@ -1,5 +1,5 @@ import {Divider, List} from "@mui/material"; -import {Fragment, useEffect, useReducer, useRef, useState} from "react"; +import {Fragment, useEffect, useReducer, useState} from "react"; import SidebarListItem from "./SidebarListItem"; import sidebarMenu from "@/core/data/sidebarMenu"; import {useRouter} from "next/router"; @@ -10,20 +10,41 @@ function reducer(state, action) { case "COLLAPSE_MENU": return state.map((itemsArr) => itemsArr.map((item) => - action.key === item.key ? { ...item, showSubItem: !item.showSubItem } : item + action.key === item.key ? {...item, showSubItem: !item.showSubItem} : item ) ); case "SELECTED": return state.map((itemsArr) => itemsArr.map((item) => { return item.type === "page" - ? { ...item, selected: action.route === item.route } + ? {...item, selected: action.route === item.route, routing: false} : item.subItem && Array.isArray(item.subItem) ? { ...item, subItem: item.subItem.map((subitem) => ({ ...subitem, selected: subitem.route === action.route, + routing: false + })), + showSubItem: item.subItem.some( + (subitem) => subitem.selected + ), + } + : item; + }) + ); + case "SELECTING": + return state.map((itemsArr) => + itemsArr.map((item) => { + return item.type === "page" + ? {...item, selected: action.route === item.route, routing: action.route === item.route} + : item.subItem && Array.isArray(item.subItem) + ? { + ...item, + subItem: item.subItem.map((subitem) => ({ + ...subitem, + selected: subitem.route === action.route, + routing: subitem.route === action.route })), showSubItem: item.subItem.some( (subitem) => subitem.selected @@ -46,7 +67,16 @@ export default function SidebarList({handleDrawerToggle}) { useEffect(() => { dispatch({type: "SELECTED", route: router.pathname}); setSelectedKey(router.pathname); - }, [router.pathname]); + const handlerStartRoute = (url) => { + dispatch({type: "SELECTING", route: url}); + } + + router.events.on('routeChangeStart', handlerStartRoute) + + return () => { + router.events.off('routeChangeStart', handlerStartRoute) + } + }, [router]); useEffect(() => { @@ -57,7 +87,7 @@ export default function SidebarList({handleDrawerToggle}) { }, [selectedKey]); return ( - + {itemMenu.map((itemArr, index) => ( {itemArr.map((item) => diff --git a/src/components/layouts/Dashboard/Sidebar/SidebarListItem.jsx b/src/components/layouts/Dashboard/Sidebar/SidebarListItem.jsx index e9657a9..81fd444 100644 --- a/src/components/layouts/Dashboard/Sidebar/SidebarListItem.jsx +++ b/src/components/layouts/Dashboard/Sidebar/SidebarListItem.jsx @@ -1,7 +1,16 @@ import {NextLinkComposed} from "@/core/components/LinkRouting"; import ExpandLess from "@mui/icons-material/ExpandLess"; import ExpandMore from "@mui/icons-material/ExpandMore"; -import {Badge, IconButton, ListItem, ListItemButton, ListItemIcon, ListItemText, Typography,} from "@mui/material"; +import { + Badge, + CircularProgress, + IconButton, + ListItem, + ListItemButton, + ListItemIcon, + ListItemText, + Typography, +} from "@mui/material"; import {useTranslations} from "next-intl"; import {Fragment} from "react"; import SidebarListSubItem from "./SidebarListSubItem"; @@ -9,7 +18,7 @@ import useNotification from "@/lib/app/hooks/useNotification"; const SidebarListItem = ({item, dispatch, handleDrawerToggle}) => { const t = useTranslations(); - const { notification_count } = useNotification(); + const {notification_count} = useNotification(); const hasSubItems = item.type === "menu" && item.subItem && item.subItem.length > 0; const renderBadge = () => { return !hasSubItems ? ( @@ -40,7 +49,7 @@ const SidebarListItem = ({item, dispatch, handleDrawerToggle}) => { })} onClick={() => { if (hasSubItems) { - dispatch({ type: "COLLAPSE_MENU", key: item.key }); + dispatch({type: "COLLAPSE_MENU", key: item.key}); } }} sx={{ @@ -53,10 +62,13 @@ const SidebarListItem = ({item, dispatch, handleDrawerToggle}) => { minWidth: 0, justifyContent: "center", color: "primary.main", + width: 40, + height: 24, pr: 2, }} > - {item.icon} + {item.routing ? + : item.icon} { } /> - {hasSubItems && (item.showSubItem ? : )} + {hasSubItems && (item.showSubItem ? : )} {item.subItem && ( diff --git a/src/components/layouts/Dashboard/Sidebar/SidebarListSubItem.jsx b/src/components/layouts/Dashboard/Sidebar/SidebarListSubItem.jsx index d663d2c..3ef8731 100644 --- a/src/components/layouts/Dashboard/Sidebar/SidebarListSubItem.jsx +++ b/src/components/layouts/Dashboard/Sidebar/SidebarListSubItem.jsx @@ -1,6 +1,7 @@ import {NextLinkComposed} from "@/core/components/LinkRouting"; import { Badge, + CircularProgress, Collapse, IconButton, List, @@ -13,7 +14,7 @@ import { import {useTranslations} from "next-intl"; import useNotification from "@/lib/app/hooks/useNotification"; -const SidebarListSubItem = ({item, handleDrawerToggle }) => { +const SidebarListSubItem = ({item, handleDrawerToggle}) => { const t = useTranslations(); const {notification_count} = useNotification(); const renderBadge = (subitem) => ( @@ -50,10 +51,13 @@ const SidebarListSubItem = ({item, handleDrawerToggle }) => { sx={{ minWidth: 0, justifyContent: "center", + width: 40, + height: 24, pr: 2, }} > - {subitem.icon} + {subitem.routing ? + : subitem.icon} { + const theme = useTheme(); + const isUpSm = useMediaQuery((theme.breakpoints.up('sm'))) + return ( - - - - - - + {isUpSm ? ( + + + + ) : ( + + + + )} ); }; diff --git a/src/components/layouts/Dashboard/index.jsx b/src/components/layouts/Dashboard/index.jsx index de3b229..8201e54 100644 --- a/src/components/layouts/Dashboard/index.jsx +++ b/src/components/layouts/Dashboard/index.jsx @@ -1,10 +1,9 @@ import FullPageLayout from "@/layouts/FullPageLayout"; import {Toolbar} from "@mui/material"; import {useState} from "react"; -import RolePermissionMiddleware from "@/middlewares/RolePermission"; import Header from "@/components/layouts/Dashboard/Header"; import Sidebar from "@/components/layouts/Dashboard/Sidebar"; -import BreadCrumbs from "@/components/layouts/Dashboard/Breadcrumbs"; +import Content from "@/components/layouts/Dashboard/Content"; const drawerWidth = 240; @@ -34,12 +33,7 @@ const Dashboard = (props) => { sx={{flexGrow: 1, width: {sm: `calc(100% - ${drawerWidth}px)`}}} > - - - - {props.children} - - + ) diff --git a/src/core/components/LoadingHardPage.jsx b/src/core/components/LoadingHardPage.jsx index 5d00cd0..3001c6c 100644 --- a/src/core/components/LoadingHardPage.jsx +++ b/src/core/components/LoadingHardPage.jsx @@ -1,5 +1,5 @@ import SvgLoading from "@/core/components/svgs/SvgLoading"; -import {Backdrop, Box} from "@mui/material"; +import {Backdrop, Box, Stack, Typography} from "@mui/material"; import {styled} from "@mui/material/styles"; const LoadingImage = styled(Box)({ @@ -10,7 +10,7 @@ const LoadingImage = styled(Box)({ }, "50%": { // opacity: 1, - transform: "scale(2)", + transform: "scale(0.5)", }, "100%": { // opacity: 0, @@ -20,20 +20,29 @@ const LoadingImage = styled(Box)({ animation: "load 2s infinite", }); -const LoadingHardPage = ({children, loading}) => { +const LoadingHardPage = ({children, loading, sx = {}, icon = null, width = 200, height = 200, label = ''}) => { return ( <> theme.zIndex.modal + 1}} + sx={{bgcolor: "#fff", zIndex: (theme) => theme.zIndex.modal + 1, ...sx}} open={loading} > - - - + + + {icon ? ( + + {icon} + + ) : ( + + )} + + {label} + {children} diff --git a/src/core/data/sidebarMenu.jsx b/src/core/data/sidebarMenu.jsx index 5c14462..1fcc48c 100644 --- a/src/core/data/sidebarMenu.jsx +++ b/src/core/data/sidebarMenu.jsx @@ -8,7 +8,7 @@ const sidebarMenu = [ key: "sidebar.dashboard", type: "page", route: "/dashboard", - icon: , + icon: , selected: false, permission: "all", }, @@ -17,16 +17,16 @@ const sidebarMenu = [ type: "page", name: "expert_management", route: "/dashboard/expert-management", - icon: , + icon: , selected: false, permission: "all", }, { key: "sidebar.role-management", - name : "role_management", + name: "role_management", type: "page", route: "/dashboard/role-management", - icon: , + icon: , selected: false, permission: "manage_roles", }, diff --git a/src/layouts/index.jsx b/src/layouts/index.jsx index a620a0e..ce7a9c9 100644 --- a/src/layouts/index.jsx +++ b/src/layouts/index.jsx @@ -2,7 +2,7 @@ import DashboardLayout from "@/layouts/DashboardLayout"; import {Fragment} from "react"; const layoutList = { - DashboardLayout + DashboardLayout, } const Layout = ({layout, children}) => { From 0d88145c7456cab479164b39a5ee2350a3c13702 Mon Sep 17 00:00:00 2001 From: AmirHossein Mahmoodi Date: Mon, 13 Nov 2023 15:59:34 +0330 Subject: [PATCH 13/25] change style list sidebar --- src/components/layouts/Dashboard/Sidebar/SidebarList.jsx | 2 +- src/components/layouts/Dashboard/Sidebar/SidebarListSubItem.jsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/layouts/Dashboard/Sidebar/SidebarList.jsx b/src/components/layouts/Dashboard/Sidebar/SidebarList.jsx index 3a2eb44..1c76f07 100644 --- a/src/components/layouts/Dashboard/Sidebar/SidebarList.jsx +++ b/src/components/layouts/Dashboard/Sidebar/SidebarList.jsx @@ -87,7 +87,7 @@ export default function SidebarList({handleDrawerToggle}) { }, [selectedKey]); return ( - + {itemMenu.map((itemArr, index) => ( {itemArr.map((item) => diff --git a/src/components/layouts/Dashboard/Sidebar/SidebarListSubItem.jsx b/src/components/layouts/Dashboard/Sidebar/SidebarListSubItem.jsx index 3ef8731..5233f15 100644 --- a/src/components/layouts/Dashboard/Sidebar/SidebarListSubItem.jsx +++ b/src/components/layouts/Dashboard/Sidebar/SidebarListSubItem.jsx @@ -34,7 +34,7 @@ const SidebarListSubItem = ({item, handleDrawerToggle}) => { return ( - + {item.subItem.map((subitem) => ( Date: Sun, 19 Nov 2023 10:56:21 +0330 Subject: [PATCH 14/25] CFE-16 fixed pending notification and success notification --- .../CallTabPanel/CallActions/index.jsx | 2 +- src/lib/callWidget/contexts/answers.jsx | 37 ++++++++++--------- 2 files changed, 20 insertions(+), 19 deletions(-) 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 index 082d701..b2dc9de 100644 --- a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallActions/index.jsx +++ b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallActions/index.jsx @@ -21,7 +21,7 @@ function CallActions({tab}) { description }, success: { - notification: {show: false}, + notification: {show: true}, }, }) .then(() => { diff --git a/src/lib/callWidget/contexts/answers.jsx b/src/lib/callWidget/contexts/answers.jsx index 96a150c..ac6d4cc 100644 --- a/src/lib/callWidget/contexts/answers.jsx +++ b/src/lib/callWidget/contexts/answers.jsx @@ -1,5 +1,5 @@ -import { createContext, useEffect, useReducer, useState } from "react"; -import { GET_CATEGORY } from "@/core/data/apiRoutes"; +import {createContext, useEffect, useReducer, useState} from "react"; +import {GET_CATEGORY} from "@/core/data/apiRoutes"; import useRequest from "@/lib/app/hooks/useRequest"; const reducer = (state, action) => { @@ -7,11 +7,11 @@ const reducer = (state, action) => { case "CHANGE_ACTIVE_TAB": return state.map((answer, index) => action.newValue === index - ? { ...answer, active: true } + ? {...answer, active: true} : { - ...answer, - active: false, - } + ...answer, + active: false, + } ); case "CHANGE_LIST": return [...action.newList]; @@ -19,10 +19,10 @@ const reducer = (state, action) => { return state.map((answer, index) => action.tab_id === answer.id ? { - ...answer, - active_category_id: action.category_id, - } - : { ...answer } + ...answer, + active_category_id: action.category_id, + } + : {...answer} ); default: return state; @@ -32,17 +32,17 @@ const reducer = (state, action) => { const submission = (handleCategories, operation) => { switch (operation.type) { case "SET_CATEGORIES": - return { ...handleCategories, categoryLists: operation.categoriesByAPI }; + return {...handleCategories, categoryLists: operation.categoriesByAPI}; case "SET_SUBCATEGORIES": - return { ...handleCategories, subCategoryLists: operation.subCategoriesByAPI }; + return {...handleCategories, subCategoryLists: operation.subCategoriesByAPI}; default: return handleCategories; } }; export const AnswersContext = createContext(); -export const AnswersProvider = ({ children }) => { - const requestServer = useRequest({ auth: true, notification: false }); +export const AnswersProvider = ({children}) => { + const requestServer = useRequest({auth: true, notification: false}); const [openCallDialog, setOpenCallDialog] = useState(false); const [activeTab, setActiveTab] = useState(0); const [handleCategories, operation] = useReducer(submission, { @@ -53,7 +53,7 @@ export const AnswersProvider = ({ children }) => { useEffect(() => { requestServer(GET_CATEGORY, "get") - .then(({ data }) => { + .then(({data}) => { const subCategoriesByAPI = data.data; const categoriesByAPI = subCategoriesByAPI.reduce((accumulator, currentValue) => { if (!accumulator.find((item) => item.category_id === currentValue.category_id)) { @@ -61,10 +61,11 @@ export const AnswersProvider = ({ children }) => { } return accumulator; }, []); - operation({ type: "SET_CATEGORIES", categoriesByAPI }); - operation({ type: "SET_SUBCATEGORIES", subCategoriesByAPI }); + operation({type: "SET_CATEGORIES", categoriesByAPI}); + operation({type: "SET_SUBCATEGORIES", subCategoriesByAPI}); }) - .catch(() => {}); + .catch(() => { + }); }, []); return ( From da7b7852b02bb51acb1f782a3a467e66d6f5dbc8 Mon Sep 17 00:00:00 2001 From: Mohammad Jalali Date: Sun, 19 Nov 2023 14:23:06 +0330 Subject: [PATCH 15/25] CFE-17 fixed porblem of call history showing --- public/locales/fa/app.json | 3 ++- .../ListItemOfCalls/PreviousOperatorData/index.jsx | 7 +++++-- .../CallHistory/ListItemOfCalls/Topics/index.jsx | 4 ++-- .../CallTabs/CallTabPanel/CallHistory/index.jsx | 8 ++++---- 4 files changed, 13 insertions(+), 9 deletions(-) diff --git a/public/locales/fa/app.json b/public/locales/fa/app.json index 21776ae..3224263 100644 --- a/public/locales/fa/app.json +++ b/public/locales/fa/app.json @@ -261,7 +261,8 @@ "call_history_of": "تاریخچه تماس های", "call_history_not_found": "تاریخچه ای برای تماس دریافتی یافت نشد", "call_history_error_fetching": "خطا در دریافت تاریخچه تماس دریافتی", - "show_more": "نمایش موارد بیشتر" + "show_more": "نمایش موارد بیشتر", + "no_data_exist": "اطلاعاتی موجود نیست" }, "CallAction": { "call_history_of": "عملیات های مربوط به تماس", diff --git a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallHistory/ListItemOfCalls/PreviousOperatorData/index.jsx b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallHistory/ListItemOfCalls/PreviousOperatorData/index.jsx index afd2ad7..357c6e8 100644 --- a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallHistory/ListItemOfCalls/PreviousOperatorData/index.jsx +++ b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallHistory/ListItemOfCalls/PreviousOperatorData/index.jsx @@ -1,8 +1,11 @@ import {useTranslations} from "next-intl"; import {Avatar, Box, ListItem, ListItemAvatar, ListItemText, Typography} from "@mui/material"; import HeadsetMicIcon from '@mui/icons-material/HeadsetMic'; +import useLanguage from "@/lib/app/hooks/useLanguage"; +import moment from "jalali-moment"; const PreviousOperatorData = ({historyItem}) => { + const {languageApp} = useLanguage() const t = useTranslations(); return ( @@ -14,13 +17,13 @@ const PreviousOperatorData = ({historyItem}) => { - {historyItem.operator_name} + {historyItem.operator_full_name} ({t("CallHistory.operator")}) - } secondary={historyItem.answer_date}/> + } secondary={moment(historyItem.created_at).locale(languageApp).format("HH:mm:ss | YYYY/MM/DD")}/> ) } diff --git a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallHistory/ListItemOfCalls/Topics/index.jsx b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallHistory/ListItemOfCalls/Topics/index.jsx index 3b57744..c9be4c0 100644 --- a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallHistory/ListItemOfCalls/Topics/index.jsx +++ b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallHistory/ListItemOfCalls/Topics/index.jsx @@ -12,7 +12,7 @@ const Topics = ({historyItem}) => { {t("CallHistory.category_name")}: - {historyItem.category_name} + {historyItem.category_name || t("CallHistory.no_data_exist")} @@ -31,7 +31,7 @@ const Topics = ({historyItem}) => { textAlign: "end", color: "#837e7e" }}> - {historyItem.subcategory_name} + {historyItem.subcategory_name || t("CallHistory.no_data_exist")} 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 index 9d61cce..1ac6e1e 100644 --- a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallHistory/index.jsx +++ b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallHistory/index.jsx @@ -6,7 +6,7 @@ import HistoryHeader from "./HistoryHeader"; import ErrorOrEmpty from "./ErrorOrEmpty"; import useCallerHistory from "@/lib/callWidget/hooks/useCallerHistory"; import ListItemOfCalls from "./ListItemOfCalls"; -import {useState} from "react"; +import {Fragment, useState} from "react"; const max_size = 10; const CallHistory = ({tab}) => { @@ -40,7 +40,7 @@ const CallHistory = ({tab}) => { } onClick={() => setShowRestOfHistory(true)} - label={t("show_more")} + label={t("CallHistory.show_more")} disabled={showRestOfHistory} /> @@ -48,10 +48,10 @@ const CallHistory = ({tab}) => { {showRestOfHistory && ( <> {historyList.map((historyItem, index) => ( - <> + - + ))} ) } From f2cdf2eb9d18346f1fb65d522f0158958f9e2bee Mon Sep 17 00:00:00 2001 From: AminGhasempoor Date: Sun, 19 Nov 2023 14:23:53 +0330 Subject: [PATCH 16/25] CFE-16 delete description validation --- .../CallTabs/CallTabPanel/CallActions/index.jsx | 5 +---- src/lib/callWidget/contexts/answers.jsx | 8 +++++++- 2 files changed, 8 insertions(+), 5 deletions(-) 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 index b2dc9de..f93ad56 100644 --- a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallActions/index.jsx +++ b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallActions/index.jsx @@ -9,7 +9,7 @@ import {useState} from "react"; import {Stack} from "@mui/material"; function CallActions({tab}) { - const [description, setDescription] = useState('') + const [description, setDescription] = useState() const requestServer = useRequest({auth: true}); const {closeAnswerTab} = useAnswers(); @@ -20,9 +20,6 @@ function CallActions({tab}) { subcategory_id, description }, - success: { - notification: {show: true}, - }, }) .then(() => { closeAnswerTab(tab.id); diff --git a/src/lib/callWidget/contexts/answers.jsx b/src/lib/callWidget/contexts/answers.jsx index ac6d4cc..139ba88 100644 --- a/src/lib/callWidget/contexts/answers.jsx +++ b/src/lib/callWidget/contexts/answers.jsx @@ -49,7 +49,13 @@ export const AnswersProvider = ({children}) => { categoryLists: [], subCategoryLists: [], }); - const [state, dispatch] = useReducer(reducer, []); + const [state, dispatch] = useReducer(reducer, [{ + id: 1, + phone_number: '09134849737', + date: new Date(), + active: true, + active_category_id: 1, + }]); useEffect(() => { requestServer(GET_CATEGORY, "get") From eafade3eca1ff68e55cecaec776b46f409396218 Mon Sep 17 00:00:00 2001 From: Mohammad Jalali Date: Sun, 19 Nov 2023 14:37:33 +0330 Subject: [PATCH 17/25] CFE-17 debug test of this feature --- .../PreviousOperatorData/__tests__/index.test.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallHistory/ListItemOfCalls/PreviousOperatorData/__tests__/index.test.js b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallHistory/ListItemOfCalls/PreviousOperatorData/__tests__/index.test.js index 4fd4519..2dc3622 100644 --- a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallHistory/ListItemOfCalls/PreviousOperatorData/__tests__/index.test.js +++ b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallHistory/ListItemOfCalls/PreviousOperatorData/__tests__/index.test.js @@ -5,8 +5,8 @@ import PreviousOperatorData from "../../PreviousOperatorData"; describe("PreviousOperatorData Component From call history", () => { describe("Rendering", () => { const historyItem = { - operator_name: "test name", - answer_date: "test date" + operator_full_name: "test name", + created_at: "2023-11-19T10:32:48.000000Z" } it("operator name rendered", () => { render(); @@ -15,7 +15,7 @@ describe("PreviousOperatorData Component From call history", () => { }); it("operator answer date rendered", () => { render(); - const operatorAnswerDate = screen.queryByText("test date"); + const operatorAnswerDate = screen.queryByText("14:02:48 | 1402/08/28"); expect(operatorAnswerDate).toBeInTheDocument(); }); }); From 16a890fe823799bef80f486bbbf8d3cff8f20f3f Mon Sep 17 00:00:00 2001 From: AmirHossein Mahmoodi Date: Mon, 20 Nov 2023 10:41:00 +0330 Subject: [PATCH 18/25] CFE-17 Fixed bug revalidating call history --- src/lib/callWidget/hooks/useCallerHistory.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/callWidget/hooks/useCallerHistory.jsx b/src/lib/callWidget/hooks/useCallerHistory.jsx index 810209d..7fe1100 100644 --- a/src/lib/callWidget/hooks/useCallerHistory.jsx +++ b/src/lib/callWidget/hooks/useCallerHistory.jsx @@ -16,7 +16,7 @@ const useCallerHistory = (call_id, phone_number, max_size) => { }; const {data, isLoading} = useSWR(`${GET_CALLER_HISTORY}?phone_number=${phone_number}&size=${max_size}`, fetcher, { - revalidateIfStale: false, + revalidateIfStale: true, revalidateOnFocus: false, revalidateOnReconnect: false, keepPreviousData: true From cf5de60faec16c2b8b97a9fd9e4ab4dc510cb9ee Mon Sep 17 00:00:00 2001 From: AmirHossein Mahmoodi Date: Mon, 20 Nov 2023 10:56:17 +0330 Subject: [PATCH 19/25] CFE-17 Fixed bug test --- .../PreviousOperatorData/__tests__/index.test.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallHistory/ListItemOfCalls/PreviousOperatorData/__tests__/index.test.js b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallHistory/ListItemOfCalls/PreviousOperatorData/__tests__/index.test.js index 2dc3622..c82a07c 100644 --- a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallHistory/ListItemOfCalls/PreviousOperatorData/__tests__/index.test.js +++ b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallHistory/ListItemOfCalls/PreviousOperatorData/__tests__/index.test.js @@ -1,6 +1,7 @@ import {render, screen} from "@testing-library/react"; import MockAppWithProviders from "../../../../../../../../../../../../mocks/AppWithProvider"; import PreviousOperatorData from "../../PreviousOperatorData"; +import moment from "jalali-moment"; describe("PreviousOperatorData Component From call history", () => { describe("Rendering", () => { @@ -14,6 +15,7 @@ describe("PreviousOperatorData Component From call history", () => { expect(operatorName).toBeInTheDocument(); }); it("operator answer date rendered", () => { + console.log(moment(historyItem.created_at).locale('fa').format("HH:mm:ss | YYYY/MM/DD")) render(); const operatorAnswerDate = screen.queryByText("14:02:48 | 1402/08/28"); expect(operatorAnswerDate).toBeInTheDocument(); From bb6aa6ba81236f6c91d1441c1322b724c675d3d4 Mon Sep 17 00:00:00 2001 From: AmirHossein Mahmoodi Date: Mon, 20 Nov 2023 11:10:48 +0330 Subject: [PATCH 20/25] CFE-17 Fixed bug test --- .../PreviousOperatorData/__tests__/index.test.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallHistory/ListItemOfCalls/PreviousOperatorData/__tests__/index.test.js b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallHistory/ListItemOfCalls/PreviousOperatorData/__tests__/index.test.js index c82a07c..c057146 100644 --- a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallHistory/ListItemOfCalls/PreviousOperatorData/__tests__/index.test.js +++ b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallHistory/ListItemOfCalls/PreviousOperatorData/__tests__/index.test.js @@ -7,7 +7,7 @@ describe("PreviousOperatorData Component From call history", () => { describe("Rendering", () => { const historyItem = { operator_full_name: "test name", - created_at: "2023-11-19T10:32:48.000000Z" + created_at: "2023-11-19T10:32:48" } it("operator name rendered", () => { render(); @@ -15,9 +15,9 @@ describe("PreviousOperatorData Component From call history", () => { expect(operatorName).toBeInTheDocument(); }); it("operator answer date rendered", () => { - console.log(moment(historyItem.created_at).locale('fa').format("HH:mm:ss | YYYY/MM/DD")) + const dateTime = moment(historyItem.created_at).locale('fa').format("HH:mm:ss | YYYY/MM/DD") render(); - const operatorAnswerDate = screen.queryByText("14:02:48 | 1402/08/28"); + const operatorAnswerDate = screen.queryByText(dateTime); expect(operatorAnswerDate).toBeInTheDocument(); }); }); From 3d488e622041128bd0a5429371ad400042c37833 Mon Sep 17 00:00:00 2001 From: AminGhasempoor Date: Mon, 20 Nov 2023 11:15:50 +0330 Subject: [PATCH 21/25] CFE-16 description validating --- .../CallTabs/CallTabPanel/CallActions/index.jsx | 16 +++++++++------- src/lib/callWidget/contexts/answers.jsx | 8 +------- 2 files changed, 10 insertions(+), 14 deletions(-) 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 index f93ad56..837d354 100644 --- a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallActions/index.jsx +++ b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallActions/index.jsx @@ -2,24 +2,26 @@ import CallActionsCategories from "./CallActionCategories"; import CallActionsSubcategories from "./CallActionSubcategories"; import ActionHeader from "./ActionHeader"; import CallActionDescription from "./CallActionDescription"; -import {CALL_ACTION} from "@/core/data/apiRoutes"; import useRequest from "@/lib/app/hooks/useRequest"; import useAnswers from "@/lib/callWidget/hooks/useAnswers"; import {useState} from "react"; import {Stack} from "@mui/material"; +import {CALL_ACTION} from "@/core/data/apiRoutes"; function CallActions({tab}) { - const [description, setDescription] = useState() + const [description, setDescription] = useState("") const requestServer = useRequest({auth: true}); const {closeAnswerTab} = useAnswers(); const handleSubcategoryButton = (category_id, subcategory_id) => { + let data = {} + data['category_id'] = category_id + data['subcategory_id'] = subcategory_id + if (description !== "") + data["description"] = description + requestServer(`${CALL_ACTION}/${tab.id}`, "post", { - data: { - category_id, - subcategory_id, - description - }, + data }) .then(() => { closeAnswerTab(tab.id); diff --git a/src/lib/callWidget/contexts/answers.jsx b/src/lib/callWidget/contexts/answers.jsx index 139ba88..ac6d4cc 100644 --- a/src/lib/callWidget/contexts/answers.jsx +++ b/src/lib/callWidget/contexts/answers.jsx @@ -49,13 +49,7 @@ export const AnswersProvider = ({children}) => { categoryLists: [], subCategoryLists: [], }); - const [state, dispatch] = useReducer(reducer, [{ - id: 1, - phone_number: '09134849737', - date: new Date(), - active: true, - active_category_id: 1, - }]); + const [state, dispatch] = useReducer(reducer, []); useEffect(() => { requestServer(GET_CATEGORY, "get") From 1c5ec9d2a8d81d1d2e0620e598996f602fc00a92 Mon Sep 17 00:00:00 2001 From: Mohammad Jalali Date: Mon, 20 Nov 2023 12:04:07 +0330 Subject: [PATCH 22/25] CFE-18 description added to call history --- public/locales/fa/app.json | 3 ++- .../CallHistory/ListItemOfCalls/Topics/index.jsx | 13 +++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/public/locales/fa/app.json b/public/locales/fa/app.json index 3224263..1bfe8d3 100644 --- a/public/locales/fa/app.json +++ b/public/locales/fa/app.json @@ -262,7 +262,8 @@ "call_history_not_found": "تاریخچه ای برای تماس دریافتی یافت نشد", "call_history_error_fetching": "خطا در دریافت تاریخچه تماس دریافتی", "show_more": "نمایش موارد بیشتر", - "no_data_exist": "اطلاعاتی موجود نیست" + "no_data_exist": "اطلاعاتی موجود نیست", + "description": "توضیحات" }, "CallAction": { "call_history_of": "عملیات های مربوط به تماس", diff --git a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallHistory/ListItemOfCalls/Topics/index.jsx b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallHistory/ListItemOfCalls/Topics/index.jsx index c9be4c0..064c37b 100644 --- a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallHistory/ListItemOfCalls/Topics/index.jsx +++ b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallHistory/ListItemOfCalls/Topics/index.jsx @@ -36,6 +36,19 @@ const Topics = ({historyItem}) => { + {historyItem.description ? + + + + {t("CallHistory.description")}: + + + {historyItem.description} + + + + : ""} + ) } From 98e240ae49a5b06ada2757b2e02eecf86e8768a8 Mon Sep 17 00:00:00 2001 From: Mohammad Jalali Date: Mon, 20 Nov 2023 12:17:13 +0330 Subject: [PATCH 23/25] CFE-18 fix test and add desc testing --- .../ListItemOfCalls/Topics/__tests__/index.test.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallHistory/ListItemOfCalls/Topics/__tests__/index.test.js b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallHistory/ListItemOfCalls/Topics/__tests__/index.test.js index d9d77e4..6f1c3cb 100644 --- a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallHistory/ListItemOfCalls/Topics/__tests__/index.test.js +++ b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallHistory/ListItemOfCalls/Topics/__tests__/index.test.js @@ -6,7 +6,8 @@ describe("Topics Component From call history", () => { describe("Rendering", () => { const historyItem = { category_name: "test category", - subcategory_name: "test sub category" + subcategory_name: "test sub category", + description: "test desc" } it("operator name rendered", () => { render(); @@ -18,5 +19,10 @@ describe("Topics Component From call history", () => { const operatorAnswerDate = screen.queryByText("test sub category"); expect(operatorAnswerDate).toBeInTheDocument(); }); + it("operator description rendered", () => { + render(); + const operatorAnswerDate = screen.queryByText("test desc"); + expect(operatorAnswerDate).toBeInTheDocument(); + }); }); }); \ No newline at end of file From 9bcddb6610896bef0d97928d50cf759508485661 Mon Sep 17 00:00:00 2001 From: AmirHossein Mahmoodi Date: Mon, 5 May 2025 09:57:50 +0000 Subject: [PATCH 24/25] Feature/migration --- .editorconfig | 485 -- .eslintrc.json | 5 - .gitignore | 59 +- .gitlab-ci.yml | 81 - README.md | 37 +- cypress.config.js | 16 - cypress/support/commands.js | 25 - cypress/support/component-index.html | 14 - cypress/support/component.js | 27 - cypress/support/e2e.js | 20 - ecosystem.config.js.example | 18 - eslint.config.mjs | 14 + example.env.local | 17 - jest.config.mjs | 17 - jest.setup.js | 23 - jsconfig.json | 7 +- mocks/AppWithProvider.jsx | 13 - mocks/handler.js | 15 - mocks/handlers/categories.js | 25 - mocks/handlers/experts.js | 20 - mocks/handlers/permissions.js | 23 - mocks/handlers/provinces.js | 19 - mocks/handlers/roles.js | 86 - mocks/handlers/user.js | 29 - mocks/server.js | 4 - next.config.js | 12 - next.config.mjs | 4 + package.json | 83 +- pnpm-lock.yaml | 5206 +++++++++++++++++ public/fontiran.css | 169 - public/icons/favicon.png | Bin 5300 -> 0 bytes public/locales/fa/app.json | 276 - public/locales/fa/datatable.js | 92 - public/manifest.json | 57 - .../(dashboardLayout)/dashboard/loading.jsx | 7 + .../(dashboardLayout)/dashboard/page.js | 9 + .../(withAuth)/(dashboardLayout)/layout.js | 19 + src/app/(withAuth)/layout.js | 7 + src/app/(withoutAuth)/layout.js | 13 + src/app/(withoutAuth)/login/page.js | 10 + src/app/default.js | 3 + src/app/layout.js | 29 + src/app/not-found.js | 21 + src/app/page.js | 8 + src/app/template.js | 40 + .../assets}/fonts/eot/IRANSansWeb(FaNum).eot | Bin .../fonts/eot/IRANSansWeb(FaNum)_Black.eot | Bin .../fonts/eot/IRANSansWeb(FaNum)_Bold.eot | Bin .../fonts/eot/IRANSansWeb(FaNum)_Light.eot | Bin .../fonts/eot/IRANSansWeb(FaNum)_Medium.eot | Bin .../eot/IRANSansWeb(FaNum)_UltraLight.eot | Bin .../assets}/fonts/eot/IRANSansWeb.eot | Bin .../assets}/fonts/eot/IRANSansWeb_Black.eot | Bin .../assets}/fonts/eot/IRANSansWeb_Bold.eot | Bin .../assets}/fonts/eot/IRANSansWeb_Light.eot | Bin .../assets}/fonts/eot/IRANSansWeb_Medium.eot | Bin .../fonts/eot/IRANSansWeb_UltraLight.eot | Bin .../assets}/fonts/eot/Parastoo-Bold.eot | Bin {public => src/assets}/fonts/eot/Parastoo.eot | Bin src/assets/fonts/ttf/BNazanin.ttf | Bin 0 -> 60852 bytes .../assets}/fonts/ttf/IRANSansWeb(FaNum).ttf | Bin .../fonts/ttf/IRANSansWeb(FaNum)_Black.ttf | Bin .../fonts/ttf/IRANSansWeb(FaNum)_Bold.ttf | Bin .../fonts/ttf/IRANSansWeb(FaNum)_Light.ttf | Bin .../fonts/ttf/IRANSansWeb(FaNum)_Medium.ttf | Bin .../ttf/IRANSansWeb(FaNum)_UltraLight.ttf | Bin .../assets}/fonts/ttf/IRANSansWeb.ttf | Bin .../assets}/fonts/ttf/IRANSansWeb_Black.ttf | Bin .../assets}/fonts/ttf/IRANSansWeb_Bold.ttf | Bin .../assets}/fonts/ttf/IRANSansWeb_Light.ttf | Bin .../assets}/fonts/ttf/IRANSansWeb_Medium.ttf | Bin .../fonts/ttf/IRANSansWeb_UltraLight.ttf | Bin .../assets}/fonts/ttf/Parastoo-Bold.ttf | Bin {public => src/assets}/fonts/ttf/Parastoo.ttf | Bin .../fonts/woff/IRANSansWeb(FaNum).woff | Bin .../fonts/woff/IRANSansWeb(FaNum)_Black.woff | Bin .../fonts/woff/IRANSansWeb(FaNum)_Bold.woff | Bin .../fonts/woff/IRANSansWeb(FaNum)_Light.woff | Bin .../fonts/woff/IRANSansWeb(FaNum)_Medium.woff | Bin .../woff/IRANSansWeb(FaNum)_UltraLight.woff | Bin .../assets}/fonts/woff/IRANSansWeb.woff | Bin .../assets}/fonts/woff/IRANSansWeb_Black.woff | Bin .../assets}/fonts/woff/IRANSansWeb_Bold.woff | Bin .../assets}/fonts/woff/IRANSansWeb_Light.woff | Bin .../fonts/woff/IRANSansWeb_Medium.woff | Bin .../fonts/woff/IRANSansWeb_UltraLight.woff | Bin .../assets}/fonts/woff/Parastoo-Bold.woff | Bin .../assets}/fonts/woff/Parastoo.woff | Bin .../fonts/woff2/IRANSansWeb(FaNum).woff2 | Bin .../woff2/IRANSansWeb(FaNum)_Black.woff2 | Bin .../fonts/woff2/IRANSansWeb(FaNum)_Bold.woff2 | Bin .../woff2/IRANSansWeb(FaNum)_Light.woff2 | Bin .../woff2/IRANSansWeb(FaNum)_Medium.woff2 | Bin .../woff2/IRANSansWeb(FaNum)_UltraLight.woff2 | Bin .../assets}/fonts/woff2/IRANSansWeb.woff2 | Bin .../fonts/woff2/IRANSansWeb_Black.woff2 | Bin .../fonts/woff2/IRANSansWeb_Bold.woff2 | Bin .../fonts/woff2/IRANSansWeb_Light.woff2 | Bin .../fonts/woff2/IRANSansWeb_Medium.woff2 | Bin .../fonts/woff2/IRANSansWeb_UltraLight.woff2 | Bin src/assets/images/empty_list.svg | 1 + src/assets/images/examine_marker.png | Bin 0 -> 15040 bytes src/assets/images/examine_marker_active.png | Bin 0 -> 13021 bytes src/assets/images/locationMarker.png | Bin 0 -> 1349 bytes src/assets/scss/fontiran.scss | 212 + src/assets/scss/global.scss | 6 + src/components/Login/Form/index.jsx | 128 + src/components/Login/LoginLinkRouting.jsx | 25 + src/components/Login/index.jsx | 19 + .../__test__/index.test.js | 305 - .../change-password-form/index.js | 137 - .../dashboard/change-password/index.jsx | 15 - .../dashboard/edit-profile/index.jsx | 195 - .../DataTable/__tests__/index.test.js | 35 - .../expert-management/DataTable/index.jsx | 202 - .../Form/ChangePassword/index.jsx | 7 - .../PersonalInfo/__tests__/index.test.js | 219 - .../CreateContent/PersonalInfo/index.jsx | 128 - .../PositionAndRole/__tests__/index.test.js | 96 - .../RestInfo/PositionAndRole/index.jsx | 74 - .../RestInfo/Province/__tests__/index.test.js | 71 - .../CreateContent/RestInfo/Province/index.jsx | 61 - .../CreateContent/RestInfo/index.jsx | 12 - .../UserInfo/__test__/index.test.js | 123 - .../CreateContent/UserInfo/index.jsx | 61 - .../CreateContent/__tests__/index.test.js | 67 - .../Form/CreateForm/CreateContent/index.jsx | 116 - .../Form/CreateForm/__tests__/index.test.js | 131 - .../Form/CreateForm/index.jsx | 39 - .../Form/DeleteForm/__tests__/index.test.js | 66 - .../Form/DeleteForm/index.jsx | 74 - .../PersonalInfo/__tests__/index.test.js | 219 - .../UpdateContent/PersonalInfo/index.jsx | 128 - .../PositionAndRole/__tests__/index.test.js | 96 - .../RestInfo/PositionAndRole/index.jsx | 75 - .../RestInfo/Province/__tests__/index.test.js | 71 - .../UpdateContent/RestInfo/Province/index.jsx | 61 - .../UpdateContent/RestInfo/index.jsx | 13 - .../UserInfo/__tests__/index.test.js | 62 - .../UpdateContent/UserInfo/index.jsx | 29 - .../UpdateContent/__tests__/index.test.js | 87 - .../Form/UpdateForm/UpdateContent/index.jsx | 108 - .../Form/UpdateForm/__tests__/index.test.js | 147 - .../Form/UpdateForm/index.jsx | 33 - .../expert-management/TableRowActions.jsx | 24 - .../expert-management/TableToolbar.jsx | 14 - .../dashboard/expert-management/index.jsx | 9 - src/components/dashboard/first/index.jsx | 5 - .../Form/CreateForm/CreateContent.jsx | 153 - .../CreateForm/__test__/CreateContent.test.js | 177 - .../Form/CreateForm/__test__/index.test.js | 34 - .../role-management/Form/CreateForm/index.jsx | 34 - .../Form/DeleteForm/DeleteContent.jsx | 42 - .../DeleteForm/__test__/DeleteContent.test.js | 53 - .../Form/DeleteForm/__test__/index.test.js | 21 - .../role-management/Form/DeleteForm/index.jsx | 32 - .../Form/UpdateForm/UpdateContent.jsx | 153 - .../UpdateForm/__test__/UpdateContent.test.js | 218 - .../Form/UpdateForm/__test__/index.test.js | 22 - .../role-management/Form/UpdateForm/index.jsx | 31 - .../RoleManagementComponent.jsx | 94 - .../role-management/TableRowActions.jsx | 22 - .../role-management/TableToolbar.jsx | 11 - .../__test__/RoleManagementComponent.test.js | 63 - .../dashboard/role-management/index.jsx | 9 - src/components/errors/403.jsx | 37 - src/components/errors/404.jsx | 37 - src/components/errors/500.jsx | 48 - src/components/first/__tests__/index.test.js | 61 - src/components/first/index.jsx | 55 - .../Dashboard/Breadcrumbs/BreadcrumbItem.jsx | 19 - .../layouts/Dashboard/Breadcrumbs/index.jsx | 49 - .../ActionHeader/__test__/index.test.js | 43 - .../CallActions/ActionHeader/index.jsx | 25 - .../CallActionCategoriesButton.jsx | 25 - .../CallActionCategoriesButton.test.js | 51 - .../__test__/index.test.js | 29 - .../CallActionCategories/index.jsx | 22 - .../__test__/index.test.js | 54 - .../CallActionDescription/index.jsx | 27 - .../CallActionSubcategoriesButton.jsx | 14 - .../CallActionSubcategoriesButton.test.js | 37 - .../__test__/index.test.js | 29 - .../CallActionSubcategories/index.jsx | 22 - .../CallTabPanel/CallActions/index.jsx | 49 - .../ErrorOrEmpty/__tests__/index.test.js | 19 - .../CallHistory/ErrorOrEmpty/index.jsx | 33 - .../HistoryHeader/__tests__/index.test.js | 21 - .../CallHistory/HistoryHeader/index.jsx | 25 - .../__tests__/index.test.js | 24 - .../PreviousOperatorData/index.jsx | 31 - .../Topics/__tests__/index.test.js | 28 - .../ListItemOfCalls/Topics/index.jsx | 56 - .../CallHistory/ListItemOfCalls/index.jsx | 21 - .../LoadingHistory/__tests__/intex.test.js | 68 - .../CallHistory/LoadingHistory/index.jsx | 86 - .../CallTabPanel/CallHistory/index.jsx | 65 - .../CallTabs/CallTabPanel/index.jsx | 23 - .../CallTabs/CallTabsList/CallTabDetails.jsx | 17 - .../CallTabs/CallTabsList/CallTabLabel.jsx | 30 - .../CallTabs/CallTabsList/CallTabTime.jsx | 29 - .../__test__/CallTabDetails.test.js | 28 - .../CallTabsList/__test__/CallTabTime.test.js | 25 - .../CallTabs/CallTabsList/index.jsx | 33 - .../CallWidgetDialog/CallTabs/index.jsx | 17 - .../CallWidget/CallWidgetDialog/index.jsx | 49 - .../layouts/Dashboard/CallWidget/index.jsx | 26 - .../layouts/Dashboard/Content/index.jsx | 65 - .../layouts/Dashboard/Header/ProfileData.jsx | 18 - .../layouts/Dashboard/Header/ProfileMenu.jsx | 62 - .../Dashboard/Header/ProfileOptionLogOut.jsx | 49 - .../Dashboard/Header/ProfileOptions.jsx | 49 - .../layouts/Dashboard/Header/index.jsx | 74 - .../Dashboard/Sidebar/SidebarDrawer.jsx | 27 - .../layouts/Dashboard/Sidebar/SidebarList.jsx | 109 - .../Dashboard/Sidebar/SidebarListItem.jsx | 97 - .../Dashboard/Sidebar/SidebarListSubItem.jsx | 78 - .../Sidebar/__test__/SidebarDrawer.test.js | 23 - .../Sidebar/__test__/SidebarList.test.js | 16 - .../Sidebar/__test__/SidebarListItem.test.js | 125 - .../__test__/SidebarListSubitem.test.js | 103 - .../Dashboard/Sidebar/__test__/index.test.js | 17 - .../layouts/Dashboard/Sidebar/index.jsx | 56 - src/components/layouts/Dashboard/index.jsx | 42 - .../headerWithSidebar/HeaderMenu/index.jsx | 60 + .../Sidebar/SidebarBadge.jsx | 19 + .../Sidebar/SidebarListItems.jsx | 92 + .../headerWithSidebar/Sidebar/SidebarMenu.jsx | 128 + .../Sidebar/SidebarSubitems.jsx | 96 + .../dashboard/headerWithSidebar/index.jsx | 190 + .../login-expert/LoginExpertComponent.jsx | 123 - .../login-expert/LoginLinkRouting.jsx | 28 - .../__test__/LoginExpertComponent.test.js | 154 - .../__test__/LoginLinkRouting.test.js | 18 - src/components/login-expert/index.jsx | 18 - .../CallTabPanel/CallActions/ActionHeader.jsx | 29 + .../CallActionCategoriesButton.jsx | 27 + .../CallActions/CallActionDescription.jsx | 44 + .../CallActionSubcategoriesButton.jsx | 38 + .../CallActions/CallActionsCategories.jsx | 24 + .../CallActions/CallActionsSubcategories.jsx | 27 + .../CallTabPanel/CallActions/index.jsx | 71 + .../CallTabPanel/CallHistory/ErrorOrEmpty.jsx | 39 + .../CallHistory/HistoryHeader.jsx | 25 + .../ListItemOfCalls/PreviousOperatorData.jsx | 30 + .../CallHistory/ListItemOfCalls/Topics..jsx | 60 + .../CallHistory/ListItemOfCalls/index.jsx | 14 + .../CallHistory/LoadingHistory.jsx | 115 + .../CallTabPanel/CallHistory/index.jsx | 61 + .../call/CallTabs/CallTabPanel/index.jsx | 29 + .../CallTabs/CallTabsList/CallTabDetails.jsx | 21 + .../CallTabs/CallTabsList/CallTabLabel.jsx | 35 + .../CallTabs/CallTabsList/CallTabTime.jsx | 28 + .../call/CallTabs/CallTabsList/index.jsx | 40 + src/components/widget/call/CallTabs/index.jsx | 17 + .../widget/call/CallWidgetDialog.jsx | 48 + src/components/widget/call/index.jsx | 29 + src/core/components/AvatarUpload.jsx | 128 - src/core/components/DataTable.jsx | 172 - src/core/components/DialogTransition.jsx | 12 +- src/core/components/GlobalHead.jsx | 58 - src/core/components/ImageConvertor.jsx | 22 - src/core/components/LinkRouting.jsx | 116 - src/core/components/LoadingHardPage.jsx | 116 +- src/core/components/LtrTextField.jsx | 10 + src/core/components/Messages.jsx | 18 - .../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/core/components/MuiDatePicker.jsx | 61 - src/core/components/NetworkComponent.jsx | 35 - src/core/components/NoSsrHandler.jsx | 8 - src/core/components/PageLoading.jsx | 25 + .../PasswordField/__test__/index.test.js | 31 - src/core/components/PasswordField/index.jsx | 33 - src/core/components/PriceField.jsx | 23 - .../components/Profile/ProfileActions.jsx | 73 + src/core/components/Profile/ProfileInfo.jsx | 49 + src/core/components/Profile/index.jsx | 22 + src/core/components/StyledFab.jsx | 10 - src/core/components/StyledForm.jsx | 5 +- src/core/components/StyledImage.jsx | 6 - src/core/components/TitlePage.jsx | 15 - src/core/components/Toasts/error.jsx | 222 + src/core/components/Toasts/success.jsx | 35 + src/core/components/UploadSystem.jsx | 175 - .../notifications/ErrorNotification.jsx | 41 - .../notifications/PendingNotification.jsx | 14 - .../notifications/SuccessNotification.jsx | 44 - .../notifications/UploadFileNotification.jsx | 40 - .../notifications/WarningNotification.jsx | 42 - src/core/components/notifications/index.jsx | 27 - src/core/components/svgs/Svg403.jsx | 131 - src/core/components/svgs/Svg404.jsx | 101 - .../components/svgs/SvgChangePassword.jsx | 77 - src/core/components/svgs/SvgDashboard.jsx | 117 - src/core/components/svgs/SvgError.jsx | 100 + src/core/components/svgs/SvgLoading.jsx | 423 +- src/core/components/svgs/SvgLogin.jsx | 75 - src/core/components/svgs/SvgNotFound.jsx | 162 + src/core/data/apiRoutes.js | 54 - src/core/data/headerProfileItems.jsx | 19 - src/core/data/sidebarMenu.jsx | 36 - src/core/middlewares/withAuth.js | 44 + src/core/middlewares/withPermission.js | 47 + src/core/middlewares/withWidget.js | 5 + src/core/middlewares/withoutAuth.js | 45 + src/core/utils/cacheRtl.js | 15 + src/core/utils/createEmotionCache.jsx | 38 - src/core/utils/errorHandler.js | 65 - src/core/utils/errorResponse.js | 72 + src/core/utils/filterMenuItems.js | 19 + src/core/utils/getValueByPath.js | 14 + src/core/utils/headerMenu.js | 15 + src/core/utils/pageMenu.js | 12 + src/core/utils/routes.js | 12 + src/core/utils/succesHandler.js | 8 - src/core/utils/successRequest.js | 10 + src/core/utils/theme-rtl.jsx | 14 - src/core/utils/theme.js | 31 + src/core/utils/theme.jsx | 29 - src/layouts/AppLayout.jsx | 67 - src/layouts/CenterLayout.jsx | 18 - src/layouts/DashboardLayout.jsx | 18 - src/layouts/FullPageLayout.jsx | 21 - src/layouts/MuiLayout.jsx | 70 - src/layouts/ScrollableLayout.jsx | 25 - src/layouts/index.jsx | 20 - src/lib/app/contexts/language.jsx | 97 - src/lib/app/contexts/loading.jsx | 14 - src/lib/app/contexts/socket.jsx | 75 - src/lib/app/contexts/toast.jsx | 37 - src/lib/app/contexts/user.jsx | 125 - src/lib/app/hooks/useDirection.jsx | 10 - src/lib/app/hooks/useLanguage.jsx | 28 - src/lib/app/hooks/useLoading.jsx | 10 - src/lib/app/hooks/useNetwork.jsx | 34 - src/lib/app/hooks/useNotification.jsx | 26 - src/lib/app/hooks/usePermissions.jsx | 26 - src/lib/app/hooks/useProvince.jsx | 30 - src/lib/app/hooks/useRequest.jsx | 71 - src/lib/app/hooks/useRole.jsx | 30 - src/lib/app/hooks/useSocket.jsx | 10 - src/lib/app/hooks/useToast.jsx | 21 - src/lib/app/hooks/useUser.jsx | 34 - src/lib/callWidget/contexts/answers.jsx | 87 - src/lib/callWidget/contexts/categories.jsx | 59 - src/lib/callWidget/hooks/useAnswers.jsx | 46 - src/lib/callWidget/hooks/useCallDialog.jsx | 10 - src/lib/callWidget/hooks/useCallerHistory.jsx | 37 - src/lib/callWidget/hooks/useCategories.jsx | 17 - src/lib/contexts/auth.js | 109 + src/lib/contexts/call.js | 106 + src/lib/contexts/category.js | 72 + src/lib/contexts/socket.js | 104 + src/lib/contexts/tableSetting.js | 189 + src/lib/hooks/useCallerHistory.js | 50 + src/lib/hooks/usePermissions.js | 21 + src/lib/hooks/useRequest.js | 66 + src/lib/hooks/useSidebarBadge.js | 23 + src/middlewares/AppPermission.jsx | 8 - src/middlewares/RolePermission.jsx | 14 - src/middlewares/WithAuth.jsx | 11 - src/middlewares/WithoutAuth.jsx | 34 - .../__test__/AppPermission.test.js | 26 - .../__test__/RolePermission.test.js | 34 - src/pages/403.jsx | 24 - src/pages/404.jsx | 25 - src/pages/500.jsx | 24 - src/pages/_app.jsx | 40 - src/pages/_document.jsx | 61 - src/pages/dashboard/change-password/index.jsx | 21 - src/pages/dashboard/edit-profile/index.jsx | 21 - .../dashboard/expert-management/index.jsx | 21 - src/pages/dashboard/index.jsx | 21 - src/pages/dashboard/role-management/index.jsx | 21 - src/pages/index.jsx | 22 - src/pages/login-expert.jsx | 23 - 381 files changed, 9465 insertions(+), 12983 deletions(-) delete mode 100644 .editorconfig delete mode 100644 .eslintrc.json delete mode 100644 .gitlab-ci.yml delete mode 100644 cypress.config.js delete mode 100644 cypress/support/commands.js delete mode 100644 cypress/support/component-index.html delete mode 100644 cypress/support/component.js delete mode 100644 cypress/support/e2e.js delete mode 100644 ecosystem.config.js.example create mode 100644 eslint.config.mjs delete mode 100644 example.env.local delete mode 100644 jest.config.mjs delete mode 100644 jest.setup.js delete mode 100644 mocks/AppWithProvider.jsx delete mode 100644 mocks/handler.js delete mode 100644 mocks/handlers/categories.js delete mode 100644 mocks/handlers/experts.js delete mode 100644 mocks/handlers/permissions.js delete mode 100644 mocks/handlers/provinces.js delete mode 100644 mocks/handlers/roles.js delete mode 100644 mocks/handlers/user.js delete mode 100644 mocks/server.js delete mode 100644 next.config.js create mode 100644 next.config.mjs create mode 100644 pnpm-lock.yaml delete mode 100644 public/fontiran.css delete mode 100644 public/icons/favicon.png delete mode 100644 public/locales/fa/app.json delete mode 100644 public/locales/fa/datatable.js delete mode 100644 public/manifest.json create mode 100644 src/app/(withAuth)/(dashboardLayout)/dashboard/loading.jsx create mode 100644 src/app/(withAuth)/(dashboardLayout)/dashboard/page.js create mode 100644 src/app/(withAuth)/(dashboardLayout)/layout.js create mode 100644 src/app/(withAuth)/layout.js create mode 100644 src/app/(withoutAuth)/layout.js create mode 100644 src/app/(withoutAuth)/login/page.js create mode 100644 src/app/default.js create mode 100644 src/app/layout.js create mode 100644 src/app/not-found.js create mode 100644 src/app/page.js create mode 100644 src/app/template.js rename {public => src/assets}/fonts/eot/IRANSansWeb(FaNum).eot (100%) rename {public => src/assets}/fonts/eot/IRANSansWeb(FaNum)_Black.eot (100%) rename {public => src/assets}/fonts/eot/IRANSansWeb(FaNum)_Bold.eot (100%) rename {public => src/assets}/fonts/eot/IRANSansWeb(FaNum)_Light.eot (100%) rename {public => src/assets}/fonts/eot/IRANSansWeb(FaNum)_Medium.eot (100%) rename {public => src/assets}/fonts/eot/IRANSansWeb(FaNum)_UltraLight.eot (100%) rename {public => src/assets}/fonts/eot/IRANSansWeb.eot (100%) rename {public => src/assets}/fonts/eot/IRANSansWeb_Black.eot (100%) rename {public => src/assets}/fonts/eot/IRANSansWeb_Bold.eot (100%) rename {public => src/assets}/fonts/eot/IRANSansWeb_Light.eot (100%) rename {public => src/assets}/fonts/eot/IRANSansWeb_Medium.eot (100%) rename {public => src/assets}/fonts/eot/IRANSansWeb_UltraLight.eot (100%) rename {public => src/assets}/fonts/eot/Parastoo-Bold.eot (100%) rename {public => src/assets}/fonts/eot/Parastoo.eot (100%) create mode 100644 src/assets/fonts/ttf/BNazanin.ttf rename {public => src/assets}/fonts/ttf/IRANSansWeb(FaNum).ttf (100%) rename {public => src/assets}/fonts/ttf/IRANSansWeb(FaNum)_Black.ttf (100%) rename {public => src/assets}/fonts/ttf/IRANSansWeb(FaNum)_Bold.ttf (100%) rename {public => src/assets}/fonts/ttf/IRANSansWeb(FaNum)_Light.ttf (100%) rename {public => src/assets}/fonts/ttf/IRANSansWeb(FaNum)_Medium.ttf (100%) rename {public => src/assets}/fonts/ttf/IRANSansWeb(FaNum)_UltraLight.ttf (100%) rename {public => src/assets}/fonts/ttf/IRANSansWeb.ttf (100%) rename {public => src/assets}/fonts/ttf/IRANSansWeb_Black.ttf (100%) rename {public => src/assets}/fonts/ttf/IRANSansWeb_Bold.ttf (100%) rename {public => src/assets}/fonts/ttf/IRANSansWeb_Light.ttf (100%) rename {public => src/assets}/fonts/ttf/IRANSansWeb_Medium.ttf (100%) rename {public => src/assets}/fonts/ttf/IRANSansWeb_UltraLight.ttf (100%) rename {public => src/assets}/fonts/ttf/Parastoo-Bold.ttf (100%) rename {public => src/assets}/fonts/ttf/Parastoo.ttf (100%) rename {public => src/assets}/fonts/woff/IRANSansWeb(FaNum).woff (100%) rename {public => src/assets}/fonts/woff/IRANSansWeb(FaNum)_Black.woff (100%) rename {public => src/assets}/fonts/woff/IRANSansWeb(FaNum)_Bold.woff (100%) rename {public => src/assets}/fonts/woff/IRANSansWeb(FaNum)_Light.woff (100%) rename {public => src/assets}/fonts/woff/IRANSansWeb(FaNum)_Medium.woff (100%) rename {public => src/assets}/fonts/woff/IRANSansWeb(FaNum)_UltraLight.woff (100%) rename {public => src/assets}/fonts/woff/IRANSansWeb.woff (100%) rename {public => src/assets}/fonts/woff/IRANSansWeb_Black.woff (100%) rename {public => src/assets}/fonts/woff/IRANSansWeb_Bold.woff (100%) rename {public => src/assets}/fonts/woff/IRANSansWeb_Light.woff (100%) rename {public => src/assets}/fonts/woff/IRANSansWeb_Medium.woff (100%) rename {public => src/assets}/fonts/woff/IRANSansWeb_UltraLight.woff (100%) rename {public => src/assets}/fonts/woff/Parastoo-Bold.woff (100%) rename {public => src/assets}/fonts/woff/Parastoo.woff (100%) rename {public => src/assets}/fonts/woff2/IRANSansWeb(FaNum).woff2 (100%) rename {public => src/assets}/fonts/woff2/IRANSansWeb(FaNum)_Black.woff2 (100%) rename {public => src/assets}/fonts/woff2/IRANSansWeb(FaNum)_Bold.woff2 (100%) rename {public => src/assets}/fonts/woff2/IRANSansWeb(FaNum)_Light.woff2 (100%) rename {public => src/assets}/fonts/woff2/IRANSansWeb(FaNum)_Medium.woff2 (100%) rename {public => src/assets}/fonts/woff2/IRANSansWeb(FaNum)_UltraLight.woff2 (100%) rename {public => src/assets}/fonts/woff2/IRANSansWeb.woff2 (100%) rename {public => src/assets}/fonts/woff2/IRANSansWeb_Black.woff2 (100%) rename {public => src/assets}/fonts/woff2/IRANSansWeb_Bold.woff2 (100%) rename {public => src/assets}/fonts/woff2/IRANSansWeb_Light.woff2 (100%) rename {public => src/assets}/fonts/woff2/IRANSansWeb_Medium.woff2 (100%) rename {public => src/assets}/fonts/woff2/IRANSansWeb_UltraLight.woff2 (100%) create mode 100644 src/assets/images/empty_list.svg create mode 100644 src/assets/images/examine_marker.png create mode 100644 src/assets/images/examine_marker_active.png create mode 100644 src/assets/images/locationMarker.png create mode 100644 src/assets/scss/fontiran.scss create mode 100644 src/assets/scss/global.scss create mode 100644 src/components/Login/Form/index.jsx create mode 100644 src/components/Login/LoginLinkRouting.jsx create mode 100644 src/components/Login/index.jsx delete mode 100644 src/components/dashboard/change-password/change-password-form/__test__/index.test.js delete mode 100644 src/components/dashboard/change-password/change-password-form/index.js delete mode 100644 src/components/dashboard/change-password/index.jsx delete mode 100644 src/components/dashboard/edit-profile/index.jsx delete mode 100644 src/components/dashboard/expert-management/DataTable/__tests__/index.test.js delete mode 100644 src/components/dashboard/expert-management/DataTable/index.jsx delete mode 100644 src/components/dashboard/expert-management/Form/ChangePassword/index.jsx delete mode 100644 src/components/dashboard/expert-management/Form/CreateForm/CreateContent/PersonalInfo/__tests__/index.test.js delete mode 100644 src/components/dashboard/expert-management/Form/CreateForm/CreateContent/PersonalInfo/index.jsx delete mode 100644 src/components/dashboard/expert-management/Form/CreateForm/CreateContent/RestInfo/PositionAndRole/__tests__/index.test.js delete mode 100644 src/components/dashboard/expert-management/Form/CreateForm/CreateContent/RestInfo/PositionAndRole/index.jsx delete mode 100644 src/components/dashboard/expert-management/Form/CreateForm/CreateContent/RestInfo/Province/__tests__/index.test.js delete mode 100644 src/components/dashboard/expert-management/Form/CreateForm/CreateContent/RestInfo/Province/index.jsx delete mode 100644 src/components/dashboard/expert-management/Form/CreateForm/CreateContent/RestInfo/index.jsx delete mode 100644 src/components/dashboard/expert-management/Form/CreateForm/CreateContent/UserInfo/__test__/index.test.js delete mode 100644 src/components/dashboard/expert-management/Form/CreateForm/CreateContent/UserInfo/index.jsx delete mode 100644 src/components/dashboard/expert-management/Form/CreateForm/CreateContent/__tests__/index.test.js delete mode 100644 src/components/dashboard/expert-management/Form/CreateForm/CreateContent/index.jsx delete mode 100644 src/components/dashboard/expert-management/Form/CreateForm/__tests__/index.test.js delete mode 100644 src/components/dashboard/expert-management/Form/CreateForm/index.jsx delete mode 100644 src/components/dashboard/expert-management/Form/DeleteForm/__tests__/index.test.js delete mode 100644 src/components/dashboard/expert-management/Form/DeleteForm/index.jsx delete mode 100644 src/components/dashboard/expert-management/Form/UpdateForm/UpdateContent/PersonalInfo/__tests__/index.test.js delete mode 100644 src/components/dashboard/expert-management/Form/UpdateForm/UpdateContent/PersonalInfo/index.jsx delete mode 100644 src/components/dashboard/expert-management/Form/UpdateForm/UpdateContent/RestInfo/PositionAndRole/__tests__/index.test.js delete mode 100644 src/components/dashboard/expert-management/Form/UpdateForm/UpdateContent/RestInfo/PositionAndRole/index.jsx delete mode 100644 src/components/dashboard/expert-management/Form/UpdateForm/UpdateContent/RestInfo/Province/__tests__/index.test.js delete mode 100644 src/components/dashboard/expert-management/Form/UpdateForm/UpdateContent/RestInfo/Province/index.jsx delete mode 100644 src/components/dashboard/expert-management/Form/UpdateForm/UpdateContent/RestInfo/index.jsx delete mode 100644 src/components/dashboard/expert-management/Form/UpdateForm/UpdateContent/UserInfo/__tests__/index.test.js delete mode 100644 src/components/dashboard/expert-management/Form/UpdateForm/UpdateContent/UserInfo/index.jsx delete mode 100644 src/components/dashboard/expert-management/Form/UpdateForm/UpdateContent/__tests__/index.test.js delete mode 100644 src/components/dashboard/expert-management/Form/UpdateForm/UpdateContent/index.jsx delete mode 100644 src/components/dashboard/expert-management/Form/UpdateForm/__tests__/index.test.js delete mode 100644 src/components/dashboard/expert-management/Form/UpdateForm/index.jsx delete mode 100644 src/components/dashboard/expert-management/TableRowActions.jsx delete mode 100644 src/components/dashboard/expert-management/TableToolbar.jsx delete mode 100644 src/components/dashboard/expert-management/index.jsx delete mode 100644 src/components/dashboard/first/index.jsx delete mode 100644 src/components/dashboard/role-management/Form/CreateForm/CreateContent.jsx delete mode 100644 src/components/dashboard/role-management/Form/CreateForm/__test__/CreateContent.test.js delete mode 100644 src/components/dashboard/role-management/Form/CreateForm/__test__/index.test.js delete mode 100644 src/components/dashboard/role-management/Form/CreateForm/index.jsx delete mode 100644 src/components/dashboard/role-management/Form/DeleteForm/DeleteContent.jsx delete mode 100644 src/components/dashboard/role-management/Form/DeleteForm/__test__/DeleteContent.test.js delete mode 100644 src/components/dashboard/role-management/Form/DeleteForm/__test__/index.test.js delete mode 100644 src/components/dashboard/role-management/Form/DeleteForm/index.jsx delete mode 100644 src/components/dashboard/role-management/Form/UpdateForm/UpdateContent.jsx delete mode 100644 src/components/dashboard/role-management/Form/UpdateForm/__test__/UpdateContent.test.js delete mode 100644 src/components/dashboard/role-management/Form/UpdateForm/__test__/index.test.js delete mode 100644 src/components/dashboard/role-management/Form/UpdateForm/index.jsx delete mode 100644 src/components/dashboard/role-management/RoleManagementComponent.jsx delete mode 100644 src/components/dashboard/role-management/TableRowActions.jsx delete mode 100644 src/components/dashboard/role-management/TableToolbar.jsx delete mode 100644 src/components/dashboard/role-management/__test__/RoleManagementComponent.test.js delete mode 100644 src/components/dashboard/role-management/index.jsx delete mode 100644 src/components/errors/403.jsx delete mode 100644 src/components/errors/404.jsx delete mode 100644 src/components/errors/500.jsx delete mode 100644 src/components/first/__tests__/index.test.js delete mode 100644 src/components/first/index.jsx delete mode 100644 src/components/layouts/Dashboard/Breadcrumbs/BreadcrumbItem.jsx delete mode 100644 src/components/layouts/Dashboard/Breadcrumbs/index.jsx delete mode 100644 src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallActions/ActionHeader/__test__/index.test.js delete mode 100644 src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallActions/ActionHeader/index.jsx delete mode 100644 src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallActions/CallActionCategories/CallActionCategoriesButton.jsx delete mode 100644 src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallActions/CallActionCategories/__test__/CallActionCategoriesButton.test.js delete mode 100644 src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallActions/CallActionCategories/__test__/index.test.js delete mode 100644 src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallActions/CallActionCategories/index.jsx delete mode 100644 src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallActions/CallActionDescription/__test__/index.test.js delete mode 100644 src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallActions/CallActionDescription/index.jsx delete mode 100644 src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallActions/CallActionSubcategories/CallActionSubcategoriesButton.jsx delete mode 100644 src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallActions/CallActionSubcategories/__test__/CallActionSubcategoriesButton.test.js delete mode 100644 src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallActions/CallActionSubcategories/__test__/index.test.js delete mode 100644 src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallActions/CallActionSubcategories/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/ErrorOrEmpty/__tests__/index.test.js delete mode 100644 src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallHistory/ErrorOrEmpty/index.jsx delete mode 100644 src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallHistory/HistoryHeader/__tests__/index.test.js delete mode 100644 src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallHistory/HistoryHeader/index.jsx delete mode 100644 src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallHistory/ListItemOfCalls/PreviousOperatorData/__tests__/index.test.js delete mode 100644 src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallHistory/ListItemOfCalls/PreviousOperatorData/index.jsx delete mode 100644 src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallHistory/ListItemOfCalls/Topics/__tests__/index.test.js delete mode 100644 src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallHistory/ListItemOfCalls/Topics/index.jsx delete mode 100644 src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallHistory/ListItemOfCalls/index.jsx delete mode 100644 src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallHistory/LoadingHistory/__tests__/intex.test.js delete mode 100644 src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallHistory/LoadingHistory/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/CallTabDetails.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/CallTabTime.jsx delete mode 100644 src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabsList/__test__/CallTabDetails.test.js delete mode 100644 src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabsList/__test__/CallTabTime.test.js 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 delete mode 100644 src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/index.jsx delete mode 100644 src/components/layouts/Dashboard/CallWidget/index.jsx delete mode 100644 src/components/layouts/Dashboard/Content/index.jsx delete mode 100644 src/components/layouts/Dashboard/Header/ProfileData.jsx delete mode 100644 src/components/layouts/Dashboard/Header/ProfileMenu.jsx delete mode 100644 src/components/layouts/Dashboard/Header/ProfileOptionLogOut.jsx delete mode 100644 src/components/layouts/Dashboard/Header/ProfileOptions.jsx delete mode 100644 src/components/layouts/Dashboard/Header/index.jsx delete mode 100644 src/components/layouts/Dashboard/Sidebar/SidebarDrawer.jsx delete mode 100644 src/components/layouts/Dashboard/Sidebar/SidebarList.jsx delete mode 100644 src/components/layouts/Dashboard/Sidebar/SidebarListItem.jsx delete mode 100644 src/components/layouts/Dashboard/Sidebar/SidebarListSubItem.jsx delete mode 100644 src/components/layouts/Dashboard/Sidebar/__test__/SidebarDrawer.test.js delete mode 100644 src/components/layouts/Dashboard/Sidebar/__test__/SidebarList.test.js delete mode 100644 src/components/layouts/Dashboard/Sidebar/__test__/SidebarListItem.test.js delete mode 100644 src/components/layouts/Dashboard/Sidebar/__test__/SidebarListSubitem.test.js delete mode 100644 src/components/layouts/Dashboard/Sidebar/__test__/index.test.js delete mode 100644 src/components/layouts/Dashboard/Sidebar/index.jsx delete mode 100644 src/components/layouts/Dashboard/index.jsx create mode 100644 src/components/layouts/dashboard/headerWithSidebar/HeaderMenu/index.jsx create mode 100644 src/components/layouts/dashboard/headerWithSidebar/Sidebar/SidebarBadge.jsx create mode 100644 src/components/layouts/dashboard/headerWithSidebar/Sidebar/SidebarListItems.jsx create mode 100644 src/components/layouts/dashboard/headerWithSidebar/Sidebar/SidebarMenu.jsx create mode 100644 src/components/layouts/dashboard/headerWithSidebar/Sidebar/SidebarSubitems.jsx create mode 100644 src/components/layouts/dashboard/headerWithSidebar/index.jsx delete mode 100644 src/components/login-expert/LoginExpertComponent.jsx delete mode 100644 src/components/login-expert/LoginLinkRouting.jsx delete mode 100644 src/components/login-expert/__test__/LoginExpertComponent.test.js delete mode 100644 src/components/login-expert/__test__/LoginLinkRouting.test.js delete mode 100644 src/components/login-expert/index.jsx create mode 100644 src/components/widget/call/CallTabs/CallTabPanel/CallActions/ActionHeader.jsx create mode 100644 src/components/widget/call/CallTabs/CallTabPanel/CallActions/CallActionCategoriesButton.jsx create mode 100644 src/components/widget/call/CallTabs/CallTabPanel/CallActions/CallActionDescription.jsx create mode 100644 src/components/widget/call/CallTabs/CallTabPanel/CallActions/CallActionSubcategoriesButton.jsx create mode 100644 src/components/widget/call/CallTabs/CallTabPanel/CallActions/CallActionsCategories.jsx create mode 100644 src/components/widget/call/CallTabs/CallTabPanel/CallActions/CallActionsSubcategories.jsx create mode 100644 src/components/widget/call/CallTabs/CallTabPanel/CallActions/index.jsx create mode 100644 src/components/widget/call/CallTabs/CallTabPanel/CallHistory/ErrorOrEmpty.jsx create mode 100644 src/components/widget/call/CallTabs/CallTabPanel/CallHistory/HistoryHeader.jsx create mode 100644 src/components/widget/call/CallTabs/CallTabPanel/CallHistory/ListItemOfCalls/PreviousOperatorData.jsx create mode 100644 src/components/widget/call/CallTabs/CallTabPanel/CallHistory/ListItemOfCalls/Topics..jsx create mode 100644 src/components/widget/call/CallTabs/CallTabPanel/CallHistory/ListItemOfCalls/index.jsx create mode 100644 src/components/widget/call/CallTabs/CallTabPanel/CallHistory/LoadingHistory.jsx create mode 100644 src/components/widget/call/CallTabs/CallTabPanel/CallHistory/index.jsx create mode 100644 src/components/widget/call/CallTabs/CallTabPanel/index.jsx create mode 100644 src/components/widget/call/CallTabs/CallTabsList/CallTabDetails.jsx create mode 100644 src/components/widget/call/CallTabs/CallTabsList/CallTabLabel.jsx create mode 100644 src/components/widget/call/CallTabs/CallTabsList/CallTabTime.jsx create mode 100644 src/components/widget/call/CallTabs/CallTabsList/index.jsx create mode 100644 src/components/widget/call/CallTabs/index.jsx create mode 100644 src/components/widget/call/CallWidgetDialog.jsx create mode 100644 src/components/widget/call/index.jsx delete mode 100644 src/core/components/AvatarUpload.jsx delete mode 100644 src/core/components/DataTable.jsx delete mode 100644 src/core/components/GlobalHead.jsx delete mode 100644 src/core/components/ImageConvertor.jsx delete mode 100644 src/core/components/LinkRouting.jsx create mode 100644 src/core/components/LtrTextField.jsx delete mode 100644 src/core/components/Messages.jsx delete mode 100644 src/core/components/Middleware/RolePermissionComponent.jsx delete mode 100644 src/core/components/Middleware/WithAuthComponent.jsx delete mode 100644 src/core/components/Middleware/WithoutAuthComponent.jsx delete mode 100644 src/core/components/Middleware/__test__/RolePermissionComponent.test.js delete mode 100644 src/core/components/Middleware/__test__/WithAuthComponent.test.js delete mode 100644 src/core/components/Middleware/__test__/WithoutAuthComponent.test.js delete mode 100644 src/core/components/MuiDatePicker.jsx delete mode 100644 src/core/components/NetworkComponent.jsx delete mode 100644 src/core/components/NoSsrHandler.jsx create mode 100644 src/core/components/PageLoading.jsx delete mode 100644 src/core/components/PasswordField/__test__/index.test.js delete mode 100644 src/core/components/PasswordField/index.jsx delete mode 100644 src/core/components/PriceField.jsx create mode 100644 src/core/components/Profile/ProfileActions.jsx create mode 100644 src/core/components/Profile/ProfileInfo.jsx create mode 100644 src/core/components/Profile/index.jsx delete mode 100644 src/core/components/StyledFab.jsx delete mode 100644 src/core/components/StyledImage.jsx delete mode 100644 src/core/components/TitlePage.jsx create mode 100644 src/core/components/Toasts/error.jsx create mode 100644 src/core/components/Toasts/success.jsx delete mode 100644 src/core/components/UploadSystem.jsx delete mode 100644 src/core/components/notifications/ErrorNotification.jsx delete mode 100644 src/core/components/notifications/PendingNotification.jsx delete mode 100644 src/core/components/notifications/SuccessNotification.jsx delete mode 100644 src/core/components/notifications/UploadFileNotification.jsx delete mode 100644 src/core/components/notifications/WarningNotification.jsx delete mode 100644 src/core/components/notifications/index.jsx delete mode 100644 src/core/components/svgs/Svg403.jsx delete mode 100644 src/core/components/svgs/Svg404.jsx delete mode 100644 src/core/components/svgs/SvgChangePassword.jsx delete mode 100644 src/core/components/svgs/SvgDashboard.jsx create mode 100644 src/core/components/svgs/SvgError.jsx delete mode 100644 src/core/components/svgs/SvgLogin.jsx create mode 100644 src/core/components/svgs/SvgNotFound.jsx delete mode 100644 src/core/data/apiRoutes.js delete mode 100644 src/core/data/headerProfileItems.jsx delete mode 100644 src/core/data/sidebarMenu.jsx create mode 100644 src/core/middlewares/withAuth.js create mode 100644 src/core/middlewares/withPermission.js create mode 100644 src/core/middlewares/withWidget.js create mode 100644 src/core/middlewares/withoutAuth.js create mode 100644 src/core/utils/cacheRtl.js delete mode 100644 src/core/utils/createEmotionCache.jsx delete mode 100644 src/core/utils/errorHandler.js create mode 100644 src/core/utils/errorResponse.js create mode 100644 src/core/utils/filterMenuItems.js create mode 100644 src/core/utils/getValueByPath.js create mode 100644 src/core/utils/headerMenu.js create mode 100644 src/core/utils/pageMenu.js create mode 100644 src/core/utils/routes.js delete mode 100644 src/core/utils/succesHandler.js create mode 100644 src/core/utils/successRequest.js delete mode 100644 src/core/utils/theme-rtl.jsx create mode 100644 src/core/utils/theme.js delete mode 100644 src/core/utils/theme.jsx delete mode 100644 src/layouts/AppLayout.jsx delete mode 100644 src/layouts/CenterLayout.jsx delete mode 100644 src/layouts/DashboardLayout.jsx delete mode 100644 src/layouts/FullPageLayout.jsx delete mode 100644 src/layouts/MuiLayout.jsx delete mode 100644 src/layouts/ScrollableLayout.jsx delete mode 100644 src/layouts/index.jsx delete mode 100644 src/lib/app/contexts/language.jsx delete mode 100644 src/lib/app/contexts/loading.jsx delete mode 100644 src/lib/app/contexts/socket.jsx delete mode 100644 src/lib/app/contexts/toast.jsx delete mode 100644 src/lib/app/contexts/user.jsx delete mode 100644 src/lib/app/hooks/useDirection.jsx delete mode 100644 src/lib/app/hooks/useLanguage.jsx delete mode 100644 src/lib/app/hooks/useLoading.jsx delete mode 100644 src/lib/app/hooks/useNetwork.jsx delete mode 100644 src/lib/app/hooks/useNotification.jsx delete mode 100644 src/lib/app/hooks/usePermissions.jsx delete mode 100644 src/lib/app/hooks/useProvince.jsx delete mode 100644 src/lib/app/hooks/useRequest.jsx delete mode 100644 src/lib/app/hooks/useRole.jsx delete mode 100644 src/lib/app/hooks/useSocket.jsx delete mode 100644 src/lib/app/hooks/useToast.jsx delete mode 100644 src/lib/app/hooks/useUser.jsx delete mode 100644 src/lib/callWidget/contexts/answers.jsx delete mode 100644 src/lib/callWidget/contexts/categories.jsx delete mode 100644 src/lib/callWidget/hooks/useAnswers.jsx delete mode 100644 src/lib/callWidget/hooks/useCallDialog.jsx delete mode 100644 src/lib/callWidget/hooks/useCallerHistory.jsx delete mode 100644 src/lib/callWidget/hooks/useCategories.jsx create mode 100644 src/lib/contexts/auth.js create mode 100644 src/lib/contexts/call.js create mode 100644 src/lib/contexts/category.js create mode 100644 src/lib/contexts/socket.js create mode 100644 src/lib/contexts/tableSetting.js create mode 100644 src/lib/hooks/useCallerHistory.js create mode 100644 src/lib/hooks/usePermissions.js create mode 100644 src/lib/hooks/useRequest.js create mode 100644 src/lib/hooks/useSidebarBadge.js delete mode 100644 src/middlewares/AppPermission.jsx delete mode 100644 src/middlewares/RolePermission.jsx delete mode 100644 src/middlewares/WithAuth.jsx delete mode 100644 src/middlewares/WithoutAuth.jsx delete mode 100644 src/middlewares/__test__/AppPermission.test.js delete mode 100644 src/middlewares/__test__/RolePermission.test.js delete mode 100644 src/pages/403.jsx delete mode 100644 src/pages/404.jsx delete mode 100644 src/pages/500.jsx delete mode 100644 src/pages/_app.jsx delete mode 100644 src/pages/_document.jsx delete mode 100644 src/pages/dashboard/change-password/index.jsx delete mode 100644 src/pages/dashboard/edit-profile/index.jsx delete mode 100644 src/pages/dashboard/expert-management/index.jsx delete mode 100644 src/pages/dashboard/index.jsx delete mode 100644 src/pages/dashboard/role-management/index.jsx delete mode 100644 src/pages/index.jsx delete mode 100644 src/pages/login-expert.jsx diff --git a/.editorconfig b/.editorconfig deleted file mode 100644 index 05e5a39..0000000 --- a/.editorconfig +++ /dev/null @@ -1,485 +0,0 @@ -root = true - -[*] -charset = utf-8 -end_of_line = crlf -indent_size = 4 -indent_style = space -insert_final_newline = false -max_line_length = 120 -tab_width = 4 -ij_continuation_indent_size = 4 -ij_formatter_off_tag = @formatter:off -ij_formatter_on_tag = @formatter:on -ij_formatter_tags_enabled = true -ij_smart_tabs = false -ij_visual_guides = none -ij_wrap_on_typing = false - -[*.css] -ij_css_align_closing_brace_with_properties = false -ij_css_blank_lines_around_nested_selector = 1 -ij_css_blank_lines_between_blocks = 1 -ij_css_block_comment_add_space = false -ij_css_brace_placement = end_of_line -ij_css_enforce_quotes_on_format = false -ij_css_hex_color_long_format = false -ij_css_hex_color_lower_case = false -ij_css_hex_color_short_format = false -ij_css_hex_color_upper_case = false -ij_css_keep_blank_lines_in_code = 2 -ij_css_keep_indents_on_empty_lines = false -ij_css_keep_single_line_blocks = false -ij_css_properties_order = font, font-family, font-size, font-weight, font-style, font-variant, font-size-adjust, font-stretch, line-height, position, z-index, top, right, bottom, left, display, visibility, float, clear, overflow, overflow-x, overflow-y, clip, zoom, align-content, align-items, align-self, flex, flex-flow, flex-basis, flex-direction, flex-grow, flex-shrink, flex-wrap, justify-content, order, box-sizing, width, min-width, max-width, height, min-height, max-height, margin, margin-top, margin-right, margin-bottom, margin-left, padding, padding-top, padding-right, padding-bottom, padding-left, table-layout, empty-cells, caption-side, border-spacing, border-collapse, list-style, list-style-position, list-style-type, list-style-image, content, quotes, counter-reset, counter-increment, resize, cursor, user-select, nav-index, nav-up, nav-right, nav-down, nav-left, transition, transition-delay, transition-timing-function, transition-duration, transition-property, transform, transform-origin, animation, animation-name, animation-duration, animation-play-state, animation-timing-function, animation-delay, animation-iteration-count, animation-direction, text-align, text-align-last, vertical-align, white-space, text-decoration, text-emphasis, text-emphasis-color, text-emphasis-style, text-emphasis-position, text-indent, text-justify, letter-spacing, word-spacing, text-outline, text-transform, text-wrap, text-overflow, text-overflow-ellipsis, text-overflow-mode, word-wrap, word-break, tab-size, hyphens, pointer-events, opacity, color, border, border-width, border-style, border-color, border-top, border-top-width, border-top-style, border-top-color, border-right, border-right-width, border-right-style, border-right-color, border-bottom, border-bottom-width, border-bottom-style, border-bottom-color, border-left, border-left-width, border-left-style, border-left-color, border-radius, border-top-left-radius, border-top-right-radius, border-bottom-right-radius, border-bottom-left-radius, border-image, border-image-source, border-image-slice, border-image-width, border-image-outset, border-image-repeat, outline, outline-width, outline-style, outline-color, outline-offset, background, background-color, background-image, background-repeat, background-attachment, background-position, background-position-x, background-position-y, background-clip, background-origin, background-size, box-decoration-break, box-shadow, text-shadow -ij_css_space_after_colon = true -ij_css_space_before_opening_brace = true -ij_css_use_double_quotes = true -ij_css_value_alignment = do_not_align - -[*.sass] -indent_size = 2 -ij_sass_align_closing_brace_with_properties = false -ij_sass_blank_lines_around_nested_selector = 1 -ij_sass_blank_lines_between_blocks = 1 -ij_sass_brace_placement = 0 -ij_sass_enforce_quotes_on_format = false -ij_sass_hex_color_long_format = false -ij_sass_hex_color_lower_case = false -ij_sass_hex_color_short_format = false -ij_sass_hex_color_upper_case = false -ij_sass_keep_blank_lines_in_code = 2 -ij_sass_keep_indents_on_empty_lines = false -ij_sass_keep_single_line_blocks = false -ij_sass_line_comment_add_space = false -ij_sass_line_comment_at_first_column = false -ij_sass_properties_order = font, font-family, font-size, font-weight, font-style, font-variant, font-size-adjust, font-stretch, line-height, position, z-index, top, right, bottom, left, display, visibility, float, clear, overflow, overflow-x, overflow-y, clip, zoom, align-content, align-items, align-self, flex, flex-flow, flex-basis, flex-direction, flex-grow, flex-shrink, flex-wrap, justify-content, order, box-sizing, width, min-width, max-width, height, min-height, max-height, margin, margin-top, margin-right, margin-bottom, margin-left, padding, padding-top, padding-right, padding-bottom, padding-left, table-layout, empty-cells, caption-side, border-spacing, border-collapse, list-style, list-style-position, list-style-type, list-style-image, content, quotes, counter-reset, counter-increment, resize, cursor, user-select, nav-index, nav-up, nav-right, nav-down, nav-left, transition, transition-delay, transition-timing-function, transition-duration, transition-property, transform, transform-origin, animation, animation-name, animation-duration, animation-play-state, animation-timing-function, animation-delay, animation-iteration-count, animation-direction, text-align, text-align-last, vertical-align, white-space, text-decoration, text-emphasis, text-emphasis-color, text-emphasis-style, text-emphasis-position, text-indent, text-justify, letter-spacing, word-spacing, text-outline, text-transform, text-wrap, text-overflow, text-overflow-ellipsis, text-overflow-mode, word-wrap, word-break, tab-size, hyphens, pointer-events, opacity, color, border, border-width, border-style, border-color, border-top, border-top-width, border-top-style, border-top-color, border-right, border-right-width, border-right-style, border-right-color, border-bottom, border-bottom-width, border-bottom-style, border-bottom-color, border-left, border-left-width, border-left-style, border-left-color, border-radius, border-top-left-radius, border-top-right-radius, border-bottom-right-radius, border-bottom-left-radius, border-image, border-image-source, border-image-slice, border-image-width, border-image-outset, border-image-repeat, outline, outline-width, outline-style, outline-color, outline-offset, background, background-color, background-image, background-repeat, background-attachment, background-position, background-position-x, background-position-y, background-clip, background-origin, background-size, box-decoration-break, box-shadow, text-shadow -ij_sass_space_after_colon = true -ij_sass_space_before_opening_brace = true -ij_sass_use_double_quotes = true -ij_sass_value_alignment = 0 - -[*.scss] -indent_size = 2 -ij_scss_align_closing_brace_with_properties = false -ij_scss_blank_lines_around_nested_selector = 1 -ij_scss_blank_lines_between_blocks = 1 -ij_scss_block_comment_add_space = false -ij_scss_brace_placement = 0 -ij_scss_enforce_quotes_on_format = false -ij_scss_hex_color_long_format = false -ij_scss_hex_color_lower_case = false -ij_scss_hex_color_short_format = false -ij_scss_hex_color_upper_case = false -ij_scss_keep_blank_lines_in_code = 2 -ij_scss_keep_indents_on_empty_lines = false -ij_scss_keep_single_line_blocks = false -ij_scss_line_comment_add_space = false -ij_scss_line_comment_at_first_column = false -ij_scss_properties_order = font, font-family, font-size, font-weight, font-style, font-variant, font-size-adjust, font-stretch, line-height, position, z-index, top, right, bottom, left, display, visibility, float, clear, overflow, overflow-x, overflow-y, clip, zoom, align-content, align-items, align-self, flex, flex-flow, flex-basis, flex-direction, flex-grow, flex-shrink, flex-wrap, justify-content, order, box-sizing, width, min-width, max-width, height, min-height, max-height, margin, margin-top, margin-right, margin-bottom, margin-left, padding, padding-top, padding-right, padding-bottom, padding-left, table-layout, empty-cells, caption-side, border-spacing, border-collapse, list-style, list-style-position, list-style-type, list-style-image, content, quotes, counter-reset, counter-increment, resize, cursor, user-select, nav-index, nav-up, nav-right, nav-down, nav-left, transition, transition-delay, transition-timing-function, transition-duration, transition-property, transform, transform-origin, animation, animation-name, animation-duration, animation-play-state, animation-timing-function, animation-delay, animation-iteration-count, animation-direction, text-align, text-align-last, vertical-align, white-space, text-decoration, text-emphasis, text-emphasis-color, text-emphasis-style, text-emphasis-position, text-indent, text-justify, letter-spacing, word-spacing, text-outline, text-transform, text-wrap, text-overflow, text-overflow-ellipsis, text-overflow-mode, word-wrap, word-break, tab-size, hyphens, pointer-events, opacity, color, border, border-width, border-style, border-color, border-top, border-top-width, border-top-style, border-top-color, border-right, border-right-width, border-right-style, border-right-color, border-bottom, border-bottom-width, border-bottom-style, border-bottom-color, border-left, border-left-width, border-left-style, border-left-color, border-radius, border-top-left-radius, border-top-right-radius, border-bottom-right-radius, border-bottom-left-radius, border-image, border-image-source, border-image-slice, border-image-width, border-image-outset, border-image-repeat, outline, outline-width, outline-style, outline-color, outline-offset, background, background-color, background-image, background-repeat, background-attachment, background-position, background-position-x, background-position-y, background-clip, background-origin, background-size, box-decoration-break, box-shadow, text-shadow -ij_scss_space_after_colon = true -ij_scss_space_before_opening_brace = true -ij_scss_use_double_quotes = true -ij_scss_value_alignment = 0 - -[{*.ats,*.cts,*.mts,*.ts}] -ij_continuation_indent_size = 4 -ij_typescript_align_imports = false -ij_typescript_align_multiline_array_initializer_expression = false -ij_typescript_align_multiline_binary_operation = false -ij_typescript_align_multiline_chained_methods = false -ij_typescript_align_multiline_extends_list = false -ij_typescript_align_multiline_for = true -ij_typescript_align_multiline_parameters = true -ij_typescript_align_multiline_parameters_in_calls = false -ij_typescript_align_multiline_ternary_operation = false -ij_typescript_align_object_properties = 0 -ij_typescript_align_union_types = false -ij_typescript_align_var_statements = 0 -ij_typescript_array_initializer_new_line_after_left_brace = false -ij_typescript_array_initializer_right_brace_on_new_line = false -ij_typescript_array_initializer_wrap = off -ij_typescript_assignment_wrap = off -ij_typescript_binary_operation_sign_on_next_line = false -ij_typescript_binary_operation_wrap = off -ij_typescript_blacklist_imports = rxjs/Rx, node_modules/**, **/node_modules/**, @angular/material, @angular/material/typings/** -ij_typescript_blank_lines_after_imports = 1 -ij_typescript_blank_lines_around_class = 1 -ij_typescript_blank_lines_around_field = 0 -ij_typescript_blank_lines_around_field_in_interface = 0 -ij_typescript_blank_lines_around_function = 1 -ij_typescript_blank_lines_around_method = 1 -ij_typescript_blank_lines_around_method_in_interface = 1 -ij_typescript_block_brace_style = end_of_line -ij_typescript_block_comment_add_space = false -ij_typescript_block_comment_at_first_column = true -ij_typescript_call_parameters_new_line_after_left_paren = false -ij_typescript_call_parameters_right_paren_on_new_line = false -ij_typescript_call_parameters_wrap = off -ij_typescript_catch_on_new_line = false -ij_typescript_chained_call_dot_on_new_line = true -ij_typescript_class_brace_style = end_of_line -ij_typescript_comma_on_new_line = false -ij_typescript_do_while_brace_force = never -ij_typescript_else_on_new_line = false -ij_typescript_enforce_trailing_comma = keep -ij_typescript_enum_constants_wrap = on_every_item -ij_typescript_extends_keyword_wrap = off -ij_typescript_extends_list_wrap = off -ij_typescript_field_prefix = _ -ij_typescript_file_name_style = relaxed -ij_typescript_finally_on_new_line = false -ij_typescript_for_brace_force = never -ij_typescript_for_statement_new_line_after_left_paren = false -ij_typescript_for_statement_right_paren_on_new_line = false -ij_typescript_for_statement_wrap = off -ij_typescript_force_quote_style = false -ij_typescript_force_semicolon_style = false -ij_typescript_function_expression_brace_style = end_of_line -ij_typescript_if_brace_force = never -ij_typescript_import_merge_members = global -ij_typescript_import_prefer_absolute_path = global -ij_typescript_import_sort_members = true -ij_typescript_import_sort_module_name = false -ij_typescript_import_use_node_resolution = true -ij_typescript_imports_wrap = on_every_item -ij_typescript_indent_case_from_switch = true -ij_typescript_indent_chained_calls = true -ij_typescript_indent_package_children = 0 -ij_typescript_jsdoc_include_types = false -ij_typescript_jsx_attribute_value = braces -ij_typescript_keep_blank_lines_in_code = 2 -ij_typescript_keep_first_column_comment = true -ij_typescript_keep_indents_on_empty_lines = false -ij_typescript_keep_line_breaks = true -ij_typescript_keep_simple_blocks_in_one_line = false -ij_typescript_keep_simple_methods_in_one_line = false -ij_typescript_line_comment_add_space = true -ij_typescript_line_comment_at_first_column = false -ij_typescript_method_brace_style = end_of_line -ij_typescript_method_call_chain_wrap = off -ij_typescript_method_parameters_new_line_after_left_paren = false -ij_typescript_method_parameters_right_paren_on_new_line = false -ij_typescript_method_parameters_wrap = off -ij_typescript_object_literal_wrap = on_every_item -ij_typescript_object_types_wrap = on_every_item -ij_typescript_parentheses_expression_new_line_after_left_paren = false -ij_typescript_parentheses_expression_right_paren_on_new_line = false -ij_typescript_place_assignment_sign_on_next_line = false -ij_typescript_prefer_as_type_cast = false -ij_typescript_prefer_explicit_types_function_expression_returns = false -ij_typescript_prefer_explicit_types_function_returns = false -ij_typescript_prefer_explicit_types_vars_fields = false -ij_typescript_prefer_parameters_wrap = false -ij_typescript_reformat_c_style_comments = false -ij_typescript_space_after_colon = true -ij_typescript_space_after_comma = true -ij_typescript_space_after_dots_in_rest_parameter = false -ij_typescript_space_after_generator_mult = true -ij_typescript_space_after_property_colon = true -ij_typescript_space_after_quest = true -ij_typescript_space_after_type_colon = true -ij_typescript_space_after_unary_not = false -ij_typescript_space_before_async_arrow_lparen = true -ij_typescript_space_before_catch_keyword = true -ij_typescript_space_before_catch_left_brace = true -ij_typescript_space_before_catch_parentheses = true -ij_typescript_space_before_class_lbrace = true -ij_typescript_space_before_class_left_brace = true -ij_typescript_space_before_colon = true -ij_typescript_space_before_comma = false -ij_typescript_space_before_do_left_brace = true -ij_typescript_space_before_else_keyword = true -ij_typescript_space_before_else_left_brace = true -ij_typescript_space_before_finally_keyword = true -ij_typescript_space_before_finally_left_brace = true -ij_typescript_space_before_for_left_brace = true -ij_typescript_space_before_for_parentheses = true -ij_typescript_space_before_for_semicolon = false -ij_typescript_space_before_function_left_parenth = true -ij_typescript_space_before_generator_mult = false -ij_typescript_space_before_if_left_brace = true -ij_typescript_space_before_if_parentheses = true -ij_typescript_space_before_method_call_parentheses = false -ij_typescript_space_before_method_left_brace = true -ij_typescript_space_before_method_parentheses = false -ij_typescript_space_before_property_colon = false -ij_typescript_space_before_quest = true -ij_typescript_space_before_switch_left_brace = true -ij_typescript_space_before_switch_parentheses = true -ij_typescript_space_before_try_left_brace = true -ij_typescript_space_before_type_colon = false -ij_typescript_space_before_unary_not = false -ij_typescript_space_before_while_keyword = true -ij_typescript_space_before_while_left_brace = true -ij_typescript_space_before_while_parentheses = true -ij_typescript_spaces_around_additive_operators = true -ij_typescript_spaces_around_arrow_function_operator = true -ij_typescript_spaces_around_assignment_operators = true -ij_typescript_spaces_around_bitwise_operators = true -ij_typescript_spaces_around_equality_operators = true -ij_typescript_spaces_around_logical_operators = true -ij_typescript_spaces_around_multiplicative_operators = true -ij_typescript_spaces_around_relational_operators = true -ij_typescript_spaces_around_shift_operators = true -ij_typescript_spaces_around_unary_operator = false -ij_typescript_spaces_within_array_initializer_brackets = false -ij_typescript_spaces_within_brackets = false -ij_typescript_spaces_within_catch_parentheses = false -ij_typescript_spaces_within_for_parentheses = false -ij_typescript_spaces_within_if_parentheses = false -ij_typescript_spaces_within_imports = false -ij_typescript_spaces_within_interpolation_expressions = false -ij_typescript_spaces_within_method_call_parentheses = false -ij_typescript_spaces_within_method_parentheses = false -ij_typescript_spaces_within_object_literal_braces = false -ij_typescript_spaces_within_object_type_braces = true -ij_typescript_spaces_within_parentheses = false -ij_typescript_spaces_within_switch_parentheses = false -ij_typescript_spaces_within_type_assertion = false -ij_typescript_spaces_within_union_types = true -ij_typescript_spaces_within_while_parentheses = false -ij_typescript_special_else_if_treatment = true -ij_typescript_ternary_operation_signs_on_next_line = false -ij_typescript_ternary_operation_wrap = off -ij_typescript_union_types_wrap = on_every_item -ij_typescript_use_chained_calls_group_indents = false -ij_typescript_use_double_quotes = true -ij_typescript_use_explicit_js_extension = auto -ij_typescript_use_path_mapping = always -ij_typescript_use_public_modifier = false -ij_typescript_use_semicolon_after_statement = true -ij_typescript_var_declaration_wrap = normal -ij_typescript_while_brace_force = never -ij_typescript_while_on_new_line = false -ij_typescript_wrap_comments = false - -[{*.cjs,*.js}] -ij_continuation_indent_size = 4 -ij_javascript_align_imports = false -ij_javascript_align_multiline_array_initializer_expression = false -ij_javascript_align_multiline_binary_operation = false -ij_javascript_align_multiline_chained_methods = false -ij_javascript_align_multiline_extends_list = false -ij_javascript_align_multiline_for = true -ij_javascript_align_multiline_parameters = true -ij_javascript_align_multiline_parameters_in_calls = false -ij_javascript_align_multiline_ternary_operation = false -ij_javascript_align_object_properties = 0 -ij_javascript_align_union_types = false -ij_javascript_align_var_statements = 0 -ij_javascript_array_initializer_new_line_after_left_brace = false -ij_javascript_array_initializer_right_brace_on_new_line = false -ij_javascript_array_initializer_wrap = off -ij_javascript_assignment_wrap = off -ij_javascript_binary_operation_sign_on_next_line = false -ij_javascript_binary_operation_wrap = off -ij_javascript_blacklist_imports = rxjs/Rx, node_modules/**, **/node_modules/**, @angular/material, @angular/material/typings/** -ij_javascript_blank_lines_after_imports = 1 -ij_javascript_blank_lines_around_class = 1 -ij_javascript_blank_lines_around_field = 0 -ij_javascript_blank_lines_around_function = 1 -ij_javascript_blank_lines_around_method = 1 -ij_javascript_block_brace_style = end_of_line -ij_javascript_block_comment_add_space = false -ij_javascript_block_comment_at_first_column = true -ij_javascript_call_parameters_new_line_after_left_paren = false -ij_javascript_call_parameters_right_paren_on_new_line = false -ij_javascript_call_parameters_wrap = off -ij_javascript_catch_on_new_line = false -ij_javascript_chained_call_dot_on_new_line = true -ij_javascript_class_brace_style = end_of_line -ij_javascript_comma_on_new_line = false -ij_javascript_do_while_brace_force = never -ij_javascript_else_on_new_line = false -ij_javascript_enforce_trailing_comma = keep -ij_javascript_extends_keyword_wrap = off -ij_javascript_extends_list_wrap = off -ij_javascript_field_prefix = _ -ij_javascript_file_name_style = relaxed -ij_javascript_finally_on_new_line = false -ij_javascript_for_brace_force = never -ij_javascript_for_statement_new_line_after_left_paren = false -ij_javascript_for_statement_right_paren_on_new_line = false -ij_javascript_for_statement_wrap = off -ij_javascript_force_quote_style = false -ij_javascript_force_semicolon_style = false -ij_javascript_function_expression_brace_style = end_of_line -ij_javascript_if_brace_force = never -ij_javascript_import_merge_members = global -ij_javascript_import_prefer_absolute_path = global -ij_javascript_import_sort_members = true -ij_javascript_import_sort_module_name = false -ij_javascript_import_use_node_resolution = true -ij_javascript_imports_wrap = on_every_item -ij_javascript_indent_case_from_switch = true -ij_javascript_indent_chained_calls = true -ij_javascript_indent_package_children = 0 -ij_javascript_jsx_attribute_value = braces -ij_javascript_keep_blank_lines_in_code = 2 -ij_javascript_keep_first_column_comment = true -ij_javascript_keep_indents_on_empty_lines = false -ij_javascript_keep_line_breaks = true -ij_javascript_keep_simple_blocks_in_one_line = false -ij_javascript_keep_simple_methods_in_one_line = false -ij_javascript_line_comment_add_space = true -ij_javascript_line_comment_at_first_column = false -ij_javascript_method_brace_style = end_of_line -ij_javascript_method_call_chain_wrap = off -ij_javascript_method_parameters_new_line_after_left_paren = false -ij_javascript_method_parameters_right_paren_on_new_line = false -ij_javascript_method_parameters_wrap = off -ij_javascript_object_literal_wrap = on_every_item -ij_javascript_object_types_wrap = on_every_item -ij_javascript_parentheses_expression_new_line_after_left_paren = false -ij_javascript_parentheses_expression_right_paren_on_new_line = false -ij_javascript_place_assignment_sign_on_next_line = false -ij_javascript_prefer_as_type_cast = false -ij_javascript_prefer_explicit_types_function_expression_returns = false -ij_javascript_prefer_explicit_types_function_returns = false -ij_javascript_prefer_explicit_types_vars_fields = false -ij_javascript_prefer_parameters_wrap = false -ij_javascript_reformat_c_style_comments = false -ij_javascript_space_after_colon = true -ij_javascript_space_after_comma = true -ij_javascript_space_after_dots_in_rest_parameter = false -ij_javascript_space_after_generator_mult = true -ij_javascript_space_after_property_colon = true -ij_javascript_space_after_quest = true -ij_javascript_space_after_type_colon = true -ij_javascript_space_after_unary_not = false -ij_javascript_space_before_async_arrow_lparen = true -ij_javascript_space_before_catch_keyword = true -ij_javascript_space_before_catch_left_brace = true -ij_javascript_space_before_catch_parentheses = true -ij_javascript_space_before_class_lbrace = true -ij_javascript_space_before_class_left_brace = true -ij_javascript_space_before_colon = true -ij_javascript_space_before_comma = false -ij_javascript_space_before_do_left_brace = true -ij_javascript_space_before_else_keyword = true -ij_javascript_space_before_else_left_brace = true -ij_javascript_space_before_finally_keyword = true -ij_javascript_space_before_finally_left_brace = true -ij_javascript_space_before_for_left_brace = true -ij_javascript_space_before_for_parentheses = true -ij_javascript_space_before_for_semicolon = false -ij_javascript_space_before_function_left_parenth = true -ij_javascript_space_before_generator_mult = false -ij_javascript_space_before_if_left_brace = true -ij_javascript_space_before_if_parentheses = true -ij_javascript_space_before_method_call_parentheses = false -ij_javascript_space_before_method_left_brace = true -ij_javascript_space_before_method_parentheses = false -ij_javascript_space_before_property_colon = false -ij_javascript_space_before_quest = true -ij_javascript_space_before_switch_left_brace = true -ij_javascript_space_before_switch_parentheses = true -ij_javascript_space_before_try_left_brace = true -ij_javascript_space_before_type_colon = false -ij_javascript_space_before_unary_not = false -ij_javascript_space_before_while_keyword = true -ij_javascript_space_before_while_left_brace = true -ij_javascript_space_before_while_parentheses = true -ij_javascript_spaces_around_additive_operators = true -ij_javascript_spaces_around_arrow_function_operator = true -ij_javascript_spaces_around_assignment_operators = true -ij_javascript_spaces_around_bitwise_operators = true -ij_javascript_spaces_around_equality_operators = true -ij_javascript_spaces_around_logical_operators = true -ij_javascript_spaces_around_multiplicative_operators = true -ij_javascript_spaces_around_relational_operators = true -ij_javascript_spaces_around_shift_operators = true -ij_javascript_spaces_around_unary_operator = false -ij_javascript_spaces_within_array_initializer_brackets = false -ij_javascript_spaces_within_brackets = false -ij_javascript_spaces_within_catch_parentheses = false -ij_javascript_spaces_within_for_parentheses = false -ij_javascript_spaces_within_if_parentheses = false -ij_javascript_spaces_within_imports = false -ij_javascript_spaces_within_interpolation_expressions = false -ij_javascript_spaces_within_method_call_parentheses = false -ij_javascript_spaces_within_method_parentheses = false -ij_javascript_spaces_within_object_literal_braces = false -ij_javascript_spaces_within_object_type_braces = true -ij_javascript_spaces_within_parentheses = false -ij_javascript_spaces_within_switch_parentheses = false -ij_javascript_spaces_within_type_assertion = false -ij_javascript_spaces_within_union_types = true -ij_javascript_spaces_within_while_parentheses = false -ij_javascript_special_else_if_treatment = true -ij_javascript_ternary_operation_signs_on_next_line = false -ij_javascript_ternary_operation_wrap = off -ij_javascript_union_types_wrap = on_every_item -ij_javascript_use_chained_calls_group_indents = false -ij_javascript_use_double_quotes = true -ij_javascript_use_explicit_js_extension = auto -ij_javascript_use_path_mapping = always -ij_javascript_use_public_modifier = false -ij_javascript_use_semicolon_after_statement = true -ij_javascript_var_declaration_wrap = normal -ij_javascript_while_brace_force = never -ij_javascript_while_on_new_line = false -ij_javascript_wrap_comments = false - -[{*.har,*.jsb2,*.jsb3,*.json,.babelrc,.eslintrc,.prettierrc,.stylelintrc,bowerrc,jest.config}] -indent_size = 2 -ij_json_array_wrapping = split_into_lines -ij_json_keep_blank_lines_in_code = 0 -ij_json_keep_indents_on_empty_lines = false -ij_json_keep_line_breaks = true -ij_json_keep_trailing_comma = false -ij_json_object_wrapping = split_into_lines -ij_json_property_alignment = do_not_align -ij_json_space_after_colon = true -ij_json_space_after_comma = true -ij_json_space_before_colon = false -ij_json_space_before_comma = false -ij_json_spaces_within_braces = false -ij_json_spaces_within_brackets = false -ij_json_wrap_long_lines = false - -[{*.htm,*.html,*.ng,*.sht,*.shtm,*.shtml}] -ij_html_add_new_line_before_tags = body, div, p, form, h1, h2, h3 -ij_html_align_attributes = true -ij_html_align_text = false -ij_html_attribute_wrap = normal -ij_html_block_comment_add_space = false -ij_html_block_comment_at_first_column = true -ij_html_do_not_align_children_of_min_lines = 0 -ij_html_do_not_break_if_inline_tags = title, h1, h2, h3, h4, h5, h6, p -ij_html_do_not_indent_children_of_tags = html, body, thead, tbody, tfoot -ij_html_enforce_quotes = false -ij_html_inline_tags = a, abbr, acronym, b, basefont, bdo, big, br, cite, cite, code, dfn, em, font, i, img, input, kbd, label, q, s, samp, select, small, span, strike, strong, sub, sup, textarea, tt, u, var -ij_html_keep_blank_lines = 2 -ij_html_keep_indents_on_empty_lines = false -ij_html_keep_line_breaks = true -ij_html_keep_line_breaks_in_text = true -ij_html_keep_whitespaces = false -ij_html_keep_whitespaces_inside = span, pre, textarea -ij_html_line_comment_at_first_column = true -ij_html_new_line_after_last_attribute = never -ij_html_new_line_before_first_attribute = never -ij_html_quote_style = double -ij_html_remove_new_line_before_tags = br -ij_html_space_after_tag_name = false -ij_html_space_around_equality_in_attribute = false -ij_html_space_inside_empty_tag = false -ij_html_text_wrap = normal - -[{*.markdown,*.md}] -ij_markdown_force_one_space_after_blockquote_symbol = true -ij_markdown_force_one_space_after_header_symbol = true -ij_markdown_force_one_space_after_list_bullet = true -ij_markdown_force_one_space_between_words = true -ij_markdown_format_tables = true -ij_markdown_insert_quote_arrows_on_wrap = true -ij_markdown_keep_indents_on_empty_lines = false -ij_markdown_keep_line_breaks_inside_text_blocks = true -ij_markdown_max_lines_around_block_elements = 1 -ij_markdown_max_lines_around_header = 1 -ij_markdown_max_lines_between_paragraphs = 1 -ij_markdown_min_lines_around_block_elements = 1 -ij_markdown_min_lines_around_header = 1 -ij_markdown_min_lines_between_paragraphs = 1 -ij_markdown_wrap_text_if_long = true -ij_markdown_wrap_text_inside_blockquotes = true diff --git a/.eslintrc.json b/.eslintrc.json deleted file mode 100644 index cb6d4e4..0000000 --- a/.eslintrc.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "extends": [ - "next/core-web-vitals" - ] -} diff --git a/.gitignore b/.gitignore index 1e62a6d..5ef6a52 100644 --- a/.gitignore +++ b/.gitignore @@ -1,34 +1,41 @@ -# Logs -logs -*.log +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. -# Runtime data -pids -*.pid -*.seed +# dependencies +/node_modules +/.pnp +.pnp.* +.yarn/* +!.yarn/patches +!.yarn/plugins +!.yarn/releases +!.yarn/versions -# Directory for instrumented libs generated by jscoverage/JSCover -lib-cov +# testing +/coverage -# Coverage directory used by tools like istanbul -coverage +# next.js +/.next/ +/out/ -# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) -.grunt +# production +/build -# node-waf configuration -.lock-wscript +# misc +.DS_Store +*.pem -# Compiled binary addons (http://nodejs.org/api/addons.html) -build/Release +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* +.pnpm-debug.log* -# Dependency directory -node_modules -cypress/videos -cypress/screenshots -.next +# env files (can opt-in for committing if needed) .env* -.env -.idea -package-lock.json -ecosystem.config.js \ No newline at end of file + +# vercel +.vercel + +# typescript +*.tsbuildinfo +next-env.d.ts diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml deleted file mode 100644 index a4cba6c..0000000 --- a/.gitlab-ci.yml +++ /dev/null @@ -1,81 +0,0 @@ -image: node:latest - -cache: - paths: - - node_modules - -stages: - - lint - - test - - test:build - - deploy - -merge:test:lint: - stage: lint - script: - - npm install - - cp example.env.local .env.local - - cp example.env.local .env.test - - npm run lint - only: - - merge_requests - -merge:test:jest: - stage: test - script: - - npm install - - cp example.env.local .env.local - - cp example.env.local .env.test - - npm run test - only: - - merge_requests - -#merge:test:build: -# stage: test:build -# script: -# - npm install -# - cp example.env.local .env.local -# - cp example.env.local .env.test -# - npm run build -# only: -# - merge_requests - -test:lint: - stage: lint - script: - - npm install - - cp example.env.local .env.local - - cp example.env.local .env.test - - npm run lint - only: - - develop - - main - -test:jest: - stage: test - script: - - npm install - - cp example.env.local .env.local - - cp example.env.local .env.test - - npm run test - only: - - develop - - main - -#test:build: -# stage: test:build -# script: -# - npm install -# - cp example.env.local .env.local -# - cp example.env.local .env.test -# - npm run build -# only: -# - develop -# - main - -deploy: - stage: deploy - script: - - echo "Run deploy project" - only: - - main \ No newline at end of file diff --git a/README.md b/README.md index 8d1c8b6..09a8a4d 100644 --- a/README.md +++ b/README.md @@ -1 +1,36 @@ - +This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app). + +## Getting Started + +First, run the development server: + +```bash +npm run dev +# or +yarn dev +# or +pnpm dev +# or +bun dev +``` + +Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. + +You can start editing the page by modifying `app/page.js`. The page auto-updates as you edit the file. + +This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel. + +## Learn More + +To learn more about Next.js, take a look at the following resources: + +- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. +- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. + +You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome! + +## Deploy on Vercel + +The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. + +Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details. diff --git a/cypress.config.js b/cypress.config.js deleted file mode 100644 index 3b67130..0000000 --- a/cypress.config.js +++ /dev/null @@ -1,16 +0,0 @@ -const { defineConfig } = require("cypress"); - -module.exports = defineConfig({ - e2e: { - setupNodeEvents(on, config) { - // implement node event listeners here - }, - }, - - component: { - devServer: { - framework: "next", - bundler: "webpack", - }, - }, -}); diff --git a/cypress/support/commands.js b/cypress/support/commands.js deleted file mode 100644 index 66ea16e..0000000 --- a/cypress/support/commands.js +++ /dev/null @@ -1,25 +0,0 @@ -// *********************************************** -// This example commands.js shows you how to -// create various custom commands and overwrite -// existing commands. -// -// For more comprehensive examples of custom -// commands please read more here: -// https://on.cypress.io/custom-commands -// *********************************************** -// -// -// -- This is a parent command -- -// Cypress.Commands.add('login', (email, password) => { ... }) -// -// -// -- This is a child command -- -// Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... }) -// -// -// -- This is a dual command -- -// Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... }) -// -// -// -- This will overwrite an existing command -- -// Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... }) \ No newline at end of file diff --git a/cypress/support/component-index.html b/cypress/support/component-index.html deleted file mode 100644 index 3e16e9b..0000000 --- a/cypress/support/component-index.html +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - Components App - -
- - -
- - \ No newline at end of file diff --git a/cypress/support/component.js b/cypress/support/component.js deleted file mode 100644 index 8f9154b..0000000 --- a/cypress/support/component.js +++ /dev/null @@ -1,27 +0,0 @@ -// *********************************************************** -// This example support/component.js is processed and -// loaded automatically before your test files. -// -// This is a great place to put global configuration and -// behavior that modifies Cypress. -// -// You can change the location of this file or turn off -// automatically serving support files with the -// 'supportFile' configuration option. -// -// You can read more here: -// https://on.cypress.io/configuration -// *********************************************************** - -// Import commands.js using ES2015 syntax: -import './commands' - -// Alternatively you can use CommonJS syntax: -// require('./commands') - -import { mount } from 'cypress/react18' - -Cypress.Commands.add('mount', mount) - -// Example use: -// cy.mount() \ No newline at end of file diff --git a/cypress/support/e2e.js b/cypress/support/e2e.js deleted file mode 100644 index 0e7290a..0000000 --- a/cypress/support/e2e.js +++ /dev/null @@ -1,20 +0,0 @@ -// *********************************************************** -// This example support/e2e.js is processed and -// loaded automatically before your test files. -// -// This is a great place to put global configuration and -// behavior that modifies Cypress. -// -// You can change the location of this file or turn off -// automatically serving support files with the -// 'supportFile' configuration option. -// -// You can read more here: -// https://on.cypress.io/configuration -// *********************************************************** - -// Import commands.js using ES2015 syntax: -import './commands' - -// Alternatively you can use CommonJS syntax: -// require('./commands') \ No newline at end of file diff --git a/ecosystem.config.js.example b/ecosystem.config.js.example deleted file mode 100644 index 6847796..0000000 --- a/ecosystem.config.js.example +++ /dev/null @@ -1,18 +0,0 @@ -module.exports = { - apps: [{ - name: "crm_app", - script: 'node_modules/next/dist/bin/next', // cluster mode run with node only, not npm - args: 'start -p 3003', - exec_mode: "cluster", // default fork - instances: "10", - kill_timeout: 4000, - wait_ready: true, - autorestart: true, - watch: false, - max_memory_restart: "1G", - log_date_format: "YYYY-MM-DD HH:mm Z", - env_prod: { - APP_ENV: 'prod' // APP_ENV=prod - } - }], -}; \ No newline at end of file diff --git a/eslint.config.mjs b/eslint.config.mjs new file mode 100644 index 0000000..348c45a --- /dev/null +++ b/eslint.config.mjs @@ -0,0 +1,14 @@ +import { dirname } from "path"; +import { fileURLToPath } from "url"; +import { FlatCompat } from "@eslint/eslintrc"; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); + +const compat = new FlatCompat({ + baseDirectory: __dirname, +}); + +const eslintConfig = [...compat.extends("next/core-web-vitals")]; + +export default eslintConfig; diff --git a/example.env.local b/example.env.local deleted file mode 100644 index ddb10b8..0000000 --- a/example.env.local +++ /dev/null @@ -1,17 +0,0 @@ -NEXT_PUBLIC_API_NAME = "CRM Dashboard" -NEXT_PUBLIC_API_VERSION = "1.21.10" -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_SERVER_SOCKET_URL = "wss://crmws.witel.ir" -NEXT_PUBLIC_POWERED_BY_URL = "https://witel.ir" - -#["NEXT_PUBLIC_HAS_WIDGET" , "NEXT_PUBLIC_HAS_NOTIFICATION"] -NEXT_PUBLIC_HAS_VALUE=["NEXT_PUBLIC_HAS_WIDGET"] - -NODE_ENV = "development" - diff --git a/jest.config.mjs b/jest.config.mjs deleted file mode 100644 index 5bda02f..0000000 --- a/jest.config.mjs +++ /dev/null @@ -1,17 +0,0 @@ -import nextJest from 'next/jest.js' - -const createJestConfig = nextJest({ - // Provide the path to your Next.js app to load next.config.js and .env files in your test environment - dir: './', -}) - -// Add any custom config to be passed to Jest -/** @type {import('jest').Config} */ -const config = { - // Add more setup options before each test is run - setupFilesAfterEnv: ['/jest.setup.js'], - testEnvironment: 'jest-environment-jsdom', -} - -// createJestConfig is exported this way to ensure that next/jest can load the Next.js config which is async -export default createJestConfig(config) \ No newline at end of file diff --git a/jest.setup.js b/jest.setup.js deleted file mode 100644 index fadd766..0000000 --- a/jest.setup.js +++ /dev/null @@ -1,23 +0,0 @@ -import '@testing-library/jest-dom' -import {server} from "./mocks/server"; -import mockRouter from 'next-router-mock' - -jest.mock('next/router', () => jest.requireActual('next-router-mock')) - -beforeAll(() => { - server.listen() -}) -beforeEach(() => { - localStorage.clear(); - mockRouter.query = {} - -}) - -afterEach(() => { - server.resetHandlers() -}) - -afterAll(() => { - server.close() -}) - diff --git a/jsconfig.json b/jsconfig.json index fcd02aa..b8d6842 100644 --- a/jsconfig.json +++ b/jsconfig.json @@ -1,12 +1,7 @@ { "compilerOptions": { "paths": { - "@/*": [ - "./src/*" - ], - "&/*": [ - "./public/*" - ] + "@/*": ["./src/*"] } } } diff --git a/mocks/AppWithProvider.jsx b/mocks/AppWithProvider.jsx deleted file mode 100644 index a81bb3c..0000000 --- a/mocks/AppWithProvider.jsx +++ /dev/null @@ -1,13 +0,0 @@ -import App from "@/pages/_app"; -import fa from "&/locales/fa/app.json" - -const translations = {fa}; -const MockAppWithProviders = ({children, locale, title, isBot}) => { - const pageProps = { - title: title || '', isBot: isBot || true, locale: locale || 'fa', messages: translations[locale || 'fa'] - } - - return ( (<>{children})} pageProps={pageProps}/>) -} - -export default MockAppWithProviders \ No newline at end of file diff --git a/mocks/handler.js b/mocks/handler.js deleted file mode 100644 index 232c91e..0000000 --- a/mocks/handler.js +++ /dev/null @@ -1,15 +0,0 @@ -import { userHandler } from "./handlers/user"; -import { permissionsHandler } from "./handlers/permissions"; -import { rolesHandler } from "./handlers/roles"; -import { provincesHandler } from "./handlers/provinces"; -import { expertsHandler } from "./handlers/experts"; -import { categoriesHandler } from "./handlers/categories"; - -export const handler = [ - ...userHandler, - ...permissionsHandler, - ...rolesHandler, - ...provincesHandler, - ...expertsHandler, - ...categoriesHandler, -]; diff --git a/mocks/handlers/categories.js b/mocks/handlers/categories.js deleted file mode 100644 index 6864331..0000000 --- a/mocks/handlers/categories.js +++ /dev/null @@ -1,25 +0,0 @@ -import { rest } from "msw"; -import { GET_CATEGORY } from "@/core/data/apiRoutes"; - -export const categoriesHandler = [ - rest.get(GET_CATEGORY, (req, res, ctx) => { - return res( - ctx.json({ - data: [ - { - category_id: 1, - category_name: "اطلاعات راه", - subcategory_id: 1, - subcategory_name: "اطلاعات باز و بسته", - }, - { - category_id: 3, - category_name: "سایر", - subcategory_id: 2, - subcategory_name: "مزاحم", - }, - ], - }) - ); - }), -]; diff --git a/mocks/handlers/experts.js b/mocks/handlers/experts.js deleted file mode 100644 index 1bf2a31..0000000 --- a/mocks/handlers/experts.js +++ /dev/null @@ -1,20 +0,0 @@ -import {rest} from "msw"; -import {ADD_EXPERT, DELETE_EXPERT, UPDATE_EXPERT} from "@/core/data/apiRoutes"; - -export const expertsHandler = [ - rest.post(ADD_EXPERT, (req, res, ctx) => { - return res( - ctx.status(200), - ); - }), - rest.delete(`${DELETE_EXPERT}/:id`, (req, res, ctx) => { - return res( - ctx.status(200), - ); - }), - rest.post(`${UPDATE_EXPERT}/:id`, (req, res, ctx) => { - return res( - ctx.status(200), - ); - }), -] \ No newline at end of file diff --git a/mocks/handlers/permissions.js b/mocks/handlers/permissions.js deleted file mode 100644 index dfe792b..0000000 --- a/mocks/handlers/permissions.js +++ /dev/null @@ -1,23 +0,0 @@ -import {rest} from "msw"; -import {GET_PERMISSIONS_LIST} from "@/core/data/apiRoutes"; - -export const permissionsHandler = [ - 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/mocks/handlers/provinces.js b/mocks/handlers/provinces.js deleted file mode 100644 index 9065c12..0000000 --- a/mocks/handlers/provinces.js +++ /dev/null @@ -1,19 +0,0 @@ -import {rest} from "msw"; -import {GET_PROVINCE_LIST} from "@/core/data/apiRoutes"; - -export const provincesHandler = [ - rest.get(GET_PROVINCE_LIST, (req, res, ctx) => { - return res(ctx.json({ - data: [ - { - id: 1, - name: "آذربایجان شرقی" - }, - { - id: 3, - name: "اردبیل" - } - ] - })) - }), -] \ No newline at end of file diff --git a/mocks/handlers/roles.js b/mocks/handlers/roles.js deleted file mode 100644 index 2760fc2..0000000 --- a/mocks/handlers/roles.js +++ /dev/null @@ -1,86 +0,0 @@ -import {rest} from "msw"; -import {GET_PERMISSIONS_LIST, GET_ROLE_LIST, GET_ROLES} from "@/core/data/apiRoutes"; - -export const rolesHandler = [ - rest.get(GET_ROLES, (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" - } - ], - meta: { - totalRowCount: 2 - } - } - ), - ); - }), - rest.get(GET_ROLE_LIST, (req, res, ctx) => { - return res(ctx.json({ - data: [ - { - id: 1, - name: "admin", - name_fa: "ادمین" - }, - { - id: 2, - name: "manager", - name_fa: "مدیر" - } - ] - })) - }), - rest.get(GET_PERMISSIONS_LIST, (req, res, ctx) => { - return res(ctx.json({ - data: [ - { - id: 1, - name: "admin", - name_fa: "ادمین" - }, - { - id: 2, - name: "manager", - name_fa: "مدیر" - } - ] - })) - }), -] \ No newline at end of file diff --git a/mocks/handlers/user.js b/mocks/handlers/user.js deleted file mode 100644 index 7e84703..0000000 --- a/mocks/handlers/user.js +++ /dev/null @@ -1,29 +0,0 @@ -import {rest} from "msw"; -import {GET_SIDEBAR_NOTIFICATION, GET_USER, SET_USER_PASSWORD} from "@/core/data/apiRoutes"; - -export const userHandler = [ - rest.get(GET_USER, (req, res, ctx) => { - return res(ctx.json({ - data:{ - id: 10, - full_name: "Witel Company", - position: "Software Engineer", - permissions:[ - "manage_users", - "manage_roles", - "manage_boss" - ], - } - })) - }), - 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), - ); - }), -] \ No newline at end of file diff --git a/mocks/server.js b/mocks/server.js deleted file mode 100644 index 0bf9e08..0000000 --- a/mocks/server.js +++ /dev/null @@ -1,4 +0,0 @@ -import {setupServer} from "msw/node"; -import {handler} from "./handler"; - -export const server = setupServer(...handler) \ No newline at end of file diff --git a/next.config.js b/next.config.js deleted file mode 100644 index a288e34..0000000 --- a/next.config.js +++ /dev/null @@ -1,12 +0,0 @@ -/** @type {import('next').NextConfig} */ - -const nextConfig = { - reactStrictMode: true, - i18n: { - defaultLocale: 'fa', - locales: ['fa'], - localeDetection: false, - } -} - -module.exports = nextConfig diff --git a/next.config.mjs b/next.config.mjs new file mode 100644 index 0000000..4678774 --- /dev/null +++ b/next.config.mjs @@ -0,0 +1,4 @@ +/** @type {import('next').NextConfig} */ +const nextConfig = {}; + +export default nextConfig; diff --git a/package.json b/package.json index f82c9f2..5551615 100644 --- a/package.json +++ b/package.json @@ -1,70 +1,41 @@ { - "name": "dashboard", + "name": "app", "version": "0.1.0", "private": true, "scripts": { - "dev": "next dev", + "dev": "next dev --turbopack", "build": "next build", "start": "next start", - "publish-3002": "next start -p 3002", "lint": "next lint", - "update": "run-script-os", - "update:win32": "echo 'ok'", - "update:linux": "rm -rf .next/ && npm i && npm run build && sudo systemctl restart crm.service", - "test": "jest", - "test:watch": "jest --watchAll", - "cypress:open": "cypress open", - "cypress:run": "cypress run" + "format": "prettier --write \"src/**/*.{js,jsx,ts,tsx,html,css,scss}\"" }, "dependencies": { - "@emotion/react": "^11.10.6", - "@emotion/server": "^11.10.0", - "@emotion/styled": "^11.10.6", - "@mui/icons-material": "^5.11.16", - "@mui/material": "^5.12.0", - "@mui/x-date-pickers": "^6.9.2", - "axios": "^1.4.0", - "colord": "^2.9.3", - "date-fns-jalali": "^2.13.0-0", - "dayjs": "^1.11.9", - "dotenv": "^16.3.1", - "eslint": "8.36.0", - "formik": "^2.2.9", - "fs-extra": "^11.1.1", - "image-resize": "^1.3.2", - "image-to-file-converter": "^1.0.6", + "@emotion/cache": "^11.14.0", + "@emotion/react": "^11.14.0", + "@emotion/styled": "^11.14.0", + "@hookform/resolvers": "^5.0.1", + "@mui/icons-material": "^7.0.2", + "@mui/material": "^7.0.2", + "@mui/material-nextjs": "^7.0.2", + "axios": "^1.9.0", "jalali-moment": "^3.3.11", - "material-react-table": "^1.11.1", - "moment": "^2.29.4", - "moment-jalaali": "^0.10.0", - "next": "^13.3.0", - "next-intl": "^2.13.1", - "next-useragent": "^2.8.0", - "nextjs-progressbar": "^0.0.16", - "prop-types": "^15.8.1", - "react": "^18.2.0", - "react-dom": "^18.2.0", - "react-toastify": "^9.1.3", - "sass": "^1.62.0", - "socket.io-client": "^4.7.2", - "stylis": "^4.1.3", + "lz-string": "^1.5.0", + "next": "15.3.1", + "react": "^19.0.0", + "react-dom": "^19.0.0", + "react-hook-form": "^7.56.1", + "react-toastify": "^11.0.5", + "socket.io-client": "^4.8.1", + "stylis": "^4.3.6", "stylis-plugin-rtl": "^2.1.1", - "swr": "^2.1.5", - "yup": "^1.1.1" + "swr": "^2.3.3", + "yup": "^1.6.1" }, "devDependencies": { - "@faker-js/faker": "^7.6.0", - "@testing-library/jest-dom": "^6.1.3", - "@testing-library/react": "^14.0.0", - "@testing-library/user-event": "^14.5.1", - "cypress": "^13.2.0", - "eslint-config-next": "^13.3.0", - "eslint-plugin-jest-dom": "^5.1.0", - "eslint-plugin-testing-library": "^6.0.1", - "jest": "^29.7.0", - "jest-environment-jsdom": "^29.7.0", - "msw": "^1.3.1", - "next-router-mock": "^0.9.10", - "run-script-os": "^1.1.6" + "@eslint/eslintrc": "^3", + "eslint": "^9", + "eslint-config-next": "15.3.1", + "prettier": "3.5.3", + "sass": "^1.87.0" } -} +} \ No newline at end of file diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 0000000..5900a63 --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,5206 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + '@emotion/cache': + specifier: ^11.14.0 + version: 11.14.0 + '@emotion/react': + specifier: ^11.14.0 + version: 11.14.0(@types/react@19.1.2)(react@19.1.0) + '@emotion/styled': + specifier: ^11.14.0 + version: 11.14.0(@emotion/react@11.14.0(@types/react@19.1.2)(react@19.1.0))(@types/react@19.1.2)(react@19.1.0) + '@hookform/resolvers': + specifier: ^5.0.1 + version: 5.0.1(react-hook-form@7.56.2(react@19.1.0)) + '@mui/icons-material': + specifier: ^7.0.2 + version: 7.0.2(@mui/material@7.0.2(@emotion/react@11.14.0(@types/react@19.1.2)(react@19.1.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.1.2)(react@19.1.0))(@types/react@19.1.2)(react@19.1.0))(@types/react@19.1.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(@types/react@19.1.2)(react@19.1.0) + '@mui/material': + specifier: ^7.0.2 + version: 7.0.2(@emotion/react@11.14.0(@types/react@19.1.2)(react@19.1.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.1.2)(react@19.1.0))(@types/react@19.1.2)(react@19.1.0))(@types/react@19.1.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@mui/material-nextjs': + specifier: ^7.0.2 + version: 7.0.2(@emotion/cache@11.14.0)(@emotion/react@11.14.0(@types/react@19.1.2)(react@19.1.0))(@types/react@19.1.2)(next@15.3.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.87.0))(react@19.1.0) + axios: + specifier: ^1.9.0 + version: 1.9.0 + jalali-moment: + specifier: ^3.3.11 + version: 3.3.11 + lz-string: + specifier: ^1.5.0 + version: 1.5.0 + next: + specifier: 15.3.1 + version: 15.3.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.87.0) + react: + specifier: ^19.0.0 + version: 19.1.0 + react-dom: + specifier: ^19.0.0 + version: 19.1.0(react@19.1.0) + react-hook-form: + specifier: ^7.56.1 + version: 7.56.2(react@19.1.0) + react-toastify: + specifier: ^11.0.5 + version: 11.0.5(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + socket.io-client: + specifier: ^4.8.1 + version: 4.8.1 + stylis: + specifier: ^4.3.6 + version: 4.3.6 + stylis-plugin-rtl: + specifier: ^2.1.1 + version: 2.1.1(stylis@4.3.6) + swr: + specifier: ^2.3.3 + version: 2.3.3(react@19.1.0) + yup: + specifier: ^1.6.1 + version: 1.6.1 + devDependencies: + '@eslint/eslintrc': + specifier: ^3 + version: 3.3.1 + eslint: + specifier: ^9 + version: 9.26.0 + eslint-config-next: + specifier: 15.3.1 + version: 15.3.1(eslint@9.26.0)(typescript@5.8.3) + prettier: + specifier: 3.5.3 + version: 3.5.3 + sass: + specifier: ^1.87.0 + version: 1.87.0 + +packages: + + '@babel/code-frame@7.27.1': + resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==} + engines: {node: '>=6.9.0'} + + '@babel/generator@7.27.1': + resolution: {integrity: sha512-UnJfnIpc/+JO0/+KRVQNGU+y5taA5vCbwN8+azkX6beii/ZF+enZJSOKo11ZSzGJjlNfJHfQtmQT8H+9TXPG2w==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-imports@7.27.1': + resolution: {integrity: sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==} + engines: {node: '>=6.9.0'} + + '@babel/helper-string-parser@7.27.1': + resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.27.1': + resolution: {integrity: sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.27.1': + resolution: {integrity: sha512-I0dZ3ZpCrJ1c04OqlNsQcKiZlsrXf/kkE4FXzID9rIOYICsAbA8mMDzhW/luRNAHdCNt7os/u8wenklZDlUVUQ==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/runtime@7.27.1': + resolution: {integrity: sha512-1x3D2xEk2fRo3PAhwQwu5UubzgiVWSXTBfWpVd2Mx2AzRqJuDJCsgaDVZ7HB5iGzDW1Hl1sWN2mFyKjmR9uAog==} + engines: {node: '>=6.9.0'} + + '@babel/template@7.27.1': + resolution: {integrity: sha512-Fyo3ghWMqkHHpHQCoBs2VnYjR4iWFFjguTDEqA5WgZDOrFesVjMhMM2FSqTKSoUSDO1VQtavj8NFpdRBEvJTtg==} + engines: {node: '>=6.9.0'} + + '@babel/traverse@7.27.1': + resolution: {integrity: sha512-ZCYtZciz1IWJB4U61UPu4KEaqyfj+r5T1Q5mqPo+IBpcG9kHv30Z0aD8LXPgC1trYa6rK0orRyAhqUgk4MjmEg==} + engines: {node: '>=6.9.0'} + + '@babel/types@7.27.1': + resolution: {integrity: sha512-+EzkxvLNfiUeKMgy/3luqfsCWFRXLb7U6wNQTk60tovuckwB15B191tJWvpp4HjiQWdJkCxO3Wbvc6jlk3Xb2Q==} + engines: {node: '>=6.9.0'} + + '@emnapi/core@1.4.3': + resolution: {integrity: sha512-4m62DuCE07lw01soJwPiBGC0nAww0Q+RY70VZ+n49yDIO13yyinhbWCeNnaob0lakDtWQzSdtNWzJeOJt2ma+g==} + + '@emnapi/runtime@1.4.3': + resolution: {integrity: sha512-pBPWdu6MLKROBX05wSNKcNb++m5Er+KQ9QkB+WVM+pW2Kx9hoSrVTnu3BdkI5eBLZoKu/J6mW/B6i6bJB2ytXQ==} + + '@emnapi/wasi-threads@1.0.2': + resolution: {integrity: sha512-5n3nTJblwRi8LlXkJ9eBzu+kZR8Yxcc7ubakyQTFzPMtIhFpUBRbsnc2Dv88IZDIbCDlBiWrknhB4Lsz7mg6BA==} + + '@emotion/babel-plugin@11.13.5': + resolution: {integrity: sha512-pxHCpT2ex+0q+HH91/zsdHkw/lXd468DIN2zvfvLtPKLLMo6gQj7oLObq8PhkrxOZb/gGCq03S3Z7PDhS8pduQ==} + + '@emotion/cache@11.14.0': + resolution: {integrity: sha512-L/B1lc/TViYk4DcpGxtAVbx0ZyiKM5ktoIyafGkH6zg/tj+mA+NE//aPYKG0k8kCHSHVJrpLpcAlOBEXQ3SavA==} + + '@emotion/hash@0.9.2': + resolution: {integrity: sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==} + + '@emotion/is-prop-valid@1.3.1': + resolution: {integrity: sha512-/ACwoqx7XQi9knQs/G0qKvv5teDMhD7bXYns9N/wM8ah8iNb8jZ2uNO0YOgiq2o2poIvVtJS2YALasQuMSQ7Kw==} + + '@emotion/memoize@0.9.0': + resolution: {integrity: sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==} + + '@emotion/react@11.14.0': + resolution: {integrity: sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA==} + peerDependencies: + '@types/react': '*' + react: '>=16.8.0' + peerDependenciesMeta: + '@types/react': + optional: true + + '@emotion/serialize@1.3.3': + resolution: {integrity: sha512-EISGqt7sSNWHGI76hC7x1CksiXPahbxEOrC5RjmFRJTqLyEK9/9hZvBbiYn70dw4wuwMKiEMCUlR6ZXTSWQqxA==} + + '@emotion/sheet@1.4.0': + resolution: {integrity: sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg==} + + '@emotion/styled@11.14.0': + resolution: {integrity: sha512-XxfOnXFffatap2IyCeJyNov3kiDQWoR08gPUQxvbL7fxKryGBKUZUkG6Hz48DZwVrJSVh9sJboyV1Ds4OW6SgA==} + peerDependencies: + '@emotion/react': ^11.0.0-rc.0 + '@types/react': '*' + react: '>=16.8.0' + peerDependenciesMeta: + '@types/react': + optional: true + + '@emotion/unitless@0.10.0': + resolution: {integrity: sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg==} + + '@emotion/use-insertion-effect-with-fallbacks@1.2.0': + resolution: {integrity: sha512-yJMtVdH59sxi/aVJBpk9FQq+OR8ll5GT8oWd57UpeaKEVGab41JWaCFA7FRLoMLloOZF/c/wsPoe+bfGmRKgDg==} + peerDependencies: + react: '>=16.8.0' + + '@emotion/utils@1.4.2': + resolution: {integrity: sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA==} + + '@emotion/weak-memoize@0.4.0': + resolution: {integrity: sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==} + + '@eslint-community/eslint-utils@4.7.0': + resolution: {integrity: sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + + '@eslint-community/regexpp@4.12.1': + resolution: {integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + + '@eslint/config-array@0.20.0': + resolution: {integrity: sha512-fxlS1kkIjx8+vy2SjuCB94q3htSNrufYTXubwiBFeaQHbH6Ipi43gFJq2zCMt6PHhImH3Xmr0NksKDvchWlpQQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/config-helpers@0.2.2': + resolution: {integrity: sha512-+GPzk8PlG0sPpzdU5ZvIRMPidzAnZDl/s9L+y13iodqvb8leL53bTannOrQ/Im7UkpsmFU5Ily5U60LWixnmLg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/core@0.13.0': + resolution: {integrity: sha512-yfkgDw1KR66rkT5A8ci4irzDysN7FRpq3ttJolR88OqQikAWqwA8j5VZyas+vjyBNFIJ7MfybJ9plMILI2UrCw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/eslintrc@3.3.1': + resolution: {integrity: sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/js@9.26.0': + resolution: {integrity: sha512-I9XlJawFdSMvWjDt6wksMCrgns5ggLNfFwFvnShsleWruvXM514Qxk8V246efTw+eo9JABvVz+u3q2RiAowKxQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/object-schema@2.1.6': + resolution: {integrity: sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/plugin-kit@0.2.8': + resolution: {integrity: sha512-ZAoA40rNMPwSm+AeHpCq8STiNAwzWLJuP8Xv4CHIc9wv/PSuExjMrmjfYNj682vW0OOiZ1HKxzvjQr9XZIisQA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@hookform/resolvers@5.0.1': + resolution: {integrity: sha512-u/+Jp83luQNx9AdyW2fIPGY6Y7NG68eN2ZW8FOJYL+M0i4s49+refdJdOp/A9n9HFQtQs3HIDHQvX3ZET2o7YA==} + peerDependencies: + react-hook-form: ^7.55.0 + + '@humanfs/core@0.19.1': + resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} + engines: {node: '>=18.18.0'} + + '@humanfs/node@0.16.6': + resolution: {integrity: sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==} + engines: {node: '>=18.18.0'} + + '@humanwhocodes/module-importer@1.0.1': + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} + engines: {node: '>=12.22'} + + '@humanwhocodes/retry@0.3.1': + resolution: {integrity: sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==} + engines: {node: '>=18.18'} + + '@humanwhocodes/retry@0.4.2': + resolution: {integrity: sha512-xeO57FpIu4p1Ri3Jq/EXq4ClRm86dVF2z/+kvFnyqVYRavTZmaFaUBbWCOuuTh0o/g7DSsk6kc2vrS4Vl5oPOQ==} + engines: {node: '>=18.18'} + + '@img/sharp-darwin-arm64@0.34.1': + resolution: {integrity: sha512-pn44xgBtgpEbZsu+lWf2KNb6OAf70X68k+yk69Ic2Xz11zHR/w24/U49XT7AeRwJ0Px+mhALhU5LPci1Aymk7A==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [darwin] + + '@img/sharp-darwin-x64@0.34.1': + resolution: {integrity: sha512-VfuYgG2r8BpYiOUN+BfYeFo69nP/MIwAtSJ7/Zpxc5QF3KS22z8Pvg3FkrSFJBPNQ7mmcUcYQFBmEQp7eu1F8Q==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [darwin] + + '@img/sharp-libvips-darwin-arm64@1.1.0': + resolution: {integrity: sha512-HZ/JUmPwrJSoM4DIQPv/BfNh9yrOA8tlBbqbLz4JZ5uew2+o22Ik+tHQJcih7QJuSa0zo5coHTfD5J8inqj9DA==} + cpu: [arm64] + os: [darwin] + + '@img/sharp-libvips-darwin-x64@1.1.0': + resolution: {integrity: sha512-Xzc2ToEmHN+hfvsl9wja0RlnXEgpKNmftriQp6XzY/RaSfwD9th+MSh0WQKzUreLKKINb3afirxW7A0fz2YWuQ==} + cpu: [x64] + os: [darwin] + + '@img/sharp-libvips-linux-arm64@1.1.0': + resolution: {integrity: sha512-IVfGJa7gjChDET1dK9SekxFFdflarnUB8PwW8aGwEoF3oAsSDuNUTYS+SKDOyOJxQyDC1aPFMuRYLoDInyV9Ew==} + cpu: [arm64] + os: [linux] + + '@img/sharp-libvips-linux-arm@1.1.0': + resolution: {integrity: sha512-s8BAd0lwUIvYCJyRdFqvsj+BJIpDBSxs6ivrOPm/R7piTs5UIwY5OjXrP2bqXC9/moGsyRa37eYWYCOGVXxVrA==} + cpu: [arm] + os: [linux] + + '@img/sharp-libvips-linux-ppc64@1.1.0': + resolution: {integrity: sha512-tiXxFZFbhnkWE2LA8oQj7KYR+bWBkiV2nilRldT7bqoEZ4HiDOcePr9wVDAZPi/Id5fT1oY9iGnDq20cwUz8lQ==} + cpu: [ppc64] + os: [linux] + + '@img/sharp-libvips-linux-s390x@1.1.0': + resolution: {integrity: sha512-xukSwvhguw7COyzvmjydRb3x/09+21HykyapcZchiCUkTThEQEOMtBj9UhkaBRLuBrgLFzQ2wbxdeCCJW/jgJA==} + cpu: [s390x] + os: [linux] + + '@img/sharp-libvips-linux-x64@1.1.0': + resolution: {integrity: sha512-yRj2+reB8iMg9W5sULM3S74jVS7zqSzHG3Ol/twnAAkAhnGQnpjj6e4ayUz7V+FpKypwgs82xbRdYtchTTUB+Q==} + cpu: [x64] + os: [linux] + + '@img/sharp-libvips-linuxmusl-arm64@1.1.0': + resolution: {integrity: sha512-jYZdG+whg0MDK+q2COKbYidaqW/WTz0cc1E+tMAusiDygrM4ypmSCjOJPmFTvHHJ8j/6cAGyeDWZOsK06tP33w==} + cpu: [arm64] + os: [linux] + + '@img/sharp-libvips-linuxmusl-x64@1.1.0': + resolution: {integrity: sha512-wK7SBdwrAiycjXdkPnGCPLjYb9lD4l6Ze2gSdAGVZrEL05AOUJESWU2lhlC+Ffn5/G+VKuSm6zzbQSzFX/P65A==} + cpu: [x64] + os: [linux] + + '@img/sharp-linux-arm64@0.34.1': + resolution: {integrity: sha512-kX2c+vbvaXC6vly1RDf/IWNXxrlxLNpBVWkdpRq5Ka7OOKj6nr66etKy2IENf6FtOgklkg9ZdGpEu9kwdlcwOQ==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [linux] + + '@img/sharp-linux-arm@0.34.1': + resolution: {integrity: sha512-anKiszvACti2sGy9CirTlNyk7BjjZPiML1jt2ZkTdcvpLU1YH6CXwRAZCA2UmRXnhiIftXQ7+Oh62Ji25W72jA==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm] + os: [linux] + + '@img/sharp-linux-s390x@0.34.1': + resolution: {integrity: sha512-7s0KX2tI9mZI2buRipKIw2X1ufdTeaRgwmRabt5bi9chYfhur+/C1OXg3TKg/eag1W+6CCWLVmSauV1owmRPxA==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [s390x] + os: [linux] + + '@img/sharp-linux-x64@0.34.1': + resolution: {integrity: sha512-wExv7SH9nmoBW3Wr2gvQopX1k8q2g5V5Iag8Zk6AVENsjwd+3adjwxtp3Dcu2QhOXr8W9NusBU6XcQUohBZ5MA==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [linux] + + '@img/sharp-linuxmusl-arm64@0.34.1': + resolution: {integrity: sha512-DfvyxzHxw4WGdPiTF0SOHnm11Xv4aQexvqhRDAoD00MzHekAj9a/jADXeXYCDFH/DzYruwHbXU7uz+H+nWmSOQ==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [linux] + + '@img/sharp-linuxmusl-x64@0.34.1': + resolution: {integrity: sha512-pax/kTR407vNb9qaSIiWVnQplPcGU8LRIJpDT5o8PdAx5aAA7AS3X9PS8Isw1/WfqgQorPotjrZL3Pqh6C5EBg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [linux] + + '@img/sharp-wasm32@0.34.1': + resolution: {integrity: sha512-YDybQnYrLQfEpzGOQe7OKcyLUCML4YOXl428gOOzBgN6Gw0rv8dpsJ7PqTHxBnXnwXr8S1mYFSLSa727tpz0xg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [wasm32] + + '@img/sharp-win32-ia32@0.34.1': + resolution: {integrity: sha512-WKf/NAZITnonBf3U1LfdjoMgNO5JYRSlhovhRhMxXVdvWYveM4kM3L8m35onYIdh75cOMCo1BexgVQcCDzyoWw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [ia32] + os: [win32] + + '@img/sharp-win32-x64@0.34.1': + resolution: {integrity: sha512-hw1iIAHpNE8q3uMIRCgGOeDoz9KtFNarFLQclLxr/LK1VBkj8nby18RjFvr6aP7USRYAjTZW6yisnBWMX571Tw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [win32] + + '@jridgewell/gen-mapping@0.3.8': + resolution: {integrity: sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==} + engines: {node: '>=6.0.0'} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/set-array@1.2.1': + resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==} + engines: {node: '>=6.0.0'} + + '@jridgewell/sourcemap-codec@1.5.0': + resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} + + '@jridgewell/trace-mapping@0.3.25': + resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} + + '@modelcontextprotocol/sdk@1.11.0': + resolution: {integrity: sha512-k/1pb70eD638anoi0e8wUGAlbMJXyvdV4p62Ko+EZ7eBe1xMx8Uhak1R5DgfoofsK5IBBnRwsYGTaLZl+6/+RQ==} + engines: {node: '>=18'} + + '@mui/core-downloads-tracker@7.0.2': + resolution: {integrity: sha512-TfeFU9TgN1N06hyb/pV/63FfO34nijZRMqgHk0TJ3gkl4Fbd+wZ73+ZtOd7jag6hMmzO9HSrBc6Vdn591nhkAg==} + + '@mui/icons-material@7.0.2': + resolution: {integrity: sha512-Bo57PFLOqXOqPNrXjd8AhzH5s6TCsNUQbvnQ0VKZ8D+lIlteqKnrk/O1luMJUc/BXONK7BfIdTdc7qOnXYbMdw==} + engines: {node: '>=14.0.0'} + peerDependencies: + '@mui/material': ^7.0.2 + '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + + '@mui/material-nextjs@7.0.2': + resolution: {integrity: sha512-hjm0MFSjx7HWbORMRldbwfKrQPHTSMXD6dkCCSTQZ2XX8fkKlnOXNnoXUFzlzFtMKMzs9QOfe3dHooTvnDEfuQ==} + engines: {node: '>=14.0.0'} + peerDependencies: + '@emotion/cache': ^11.11.0 + '@emotion/react': ^11.11.4 + '@emotion/server': ^11.11.0 + '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 + next: ^13.0.0 || ^14.0.0 || ^15.0.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@emotion/cache': + optional: true + '@emotion/server': + optional: true + '@types/react': + optional: true + + '@mui/material@7.0.2': + resolution: {integrity: sha512-rjJlJ13+3LdLfobRplkXbjIFEIkn6LgpetgU/Cs3Xd8qINCCQK9qXQIjjQ6P0FXFTPFzEVMj0VgBR1mN+FhOcA==} + engines: {node: '>=14.0.0'} + peerDependencies: + '@emotion/react': ^11.5.0 + '@emotion/styled': ^11.3.0 + '@mui/material-pigment-css': ^7.0.2 + '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@emotion/react': + optional: true + '@emotion/styled': + optional: true + '@mui/material-pigment-css': + optional: true + '@types/react': + optional: true + + '@mui/private-theming@7.0.2': + resolution: {integrity: sha512-6lt8heDC9wN8YaRqEdhqnm0cFCv08AMf4IlttFvOVn7ZdKd81PNpD/rEtPGLLwQAFyyKSxBG4/2XCgpbcdNKiA==} + engines: {node: '>=14.0.0'} + peerDependencies: + '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + + '@mui/styled-engine@7.0.2': + resolution: {integrity: sha512-11Bt4YdHGlh7sB8P75S9mRCUxTlgv7HGbr0UKz6m6Z9KLeiw1Bm9y/t3iqLLVMvSHYB6zL8X8X+LmfTE++gyBw==} + engines: {node: '>=14.0.0'} + peerDependencies: + '@emotion/react': ^11.4.1 + '@emotion/styled': ^11.3.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@emotion/react': + optional: true + '@emotion/styled': + optional: true + + '@mui/system@7.0.2': + resolution: {integrity: sha512-yFUraAWYWuKIISPPEVPSQ1NLeqmTT4qiQ+ktmyS8LO/KwHxB+NNVOacEZaIofh5x1NxY8rzphvU5X2heRZ/RDA==} + engines: {node: '>=14.0.0'} + peerDependencies: + '@emotion/react': ^11.5.0 + '@emotion/styled': ^11.3.0 + '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@emotion/react': + optional: true + '@emotion/styled': + optional: true + '@types/react': + optional: true + + '@mui/types@7.4.1': + resolution: {integrity: sha512-gUL8IIAI52CRXP/MixT1tJKt3SI6tVv4U/9soFsTtAsHzaJQptZ42ffdHZV3niX1ei0aUgMvOxBBN0KYqdG39g==} + peerDependencies: + '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + + '@mui/utils@7.0.2': + resolution: {integrity: sha512-72gcuQjPzhj/MLmPHLCgZjy2VjOH4KniR/4qRtXTTXIEwbkgcN+Y5W/rC90rWtMmZbjt9svZev/z+QHUI4j74w==} + engines: {node: '>=14.0.0'} + peerDependencies: + '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + + '@napi-rs/wasm-runtime@0.2.9': + resolution: {integrity: sha512-OKRBiajrrxB9ATokgEQoG87Z25c67pCpYcCwmXYX8PBftC9pBfN18gnm/fh1wurSLEKIAt+QRFLFCQISrb66Jg==} + + '@next/env@15.3.1': + resolution: {integrity: sha512-cwK27QdzrMblHSn9DZRV+DQscHXRuJv6MydlJRpFSqJWZrTYMLzKDeyueJNN9MGd8NNiUKzDQADAf+dMLXX7YQ==} + + '@next/eslint-plugin-next@15.3.1': + resolution: {integrity: sha512-oEs4dsfM6iyER3jTzMm4kDSbrQJq8wZw5fmT6fg2V3SMo+kgG+cShzLfEV20senZzv8VF+puNLheiGPlBGsv2A==} + + '@next/swc-darwin-arm64@15.3.1': + resolution: {integrity: sha512-hjDw4f4/nla+6wysBL07z52Gs55Gttp5Bsk5/8AncQLJoisvTBP0pRIBK/B16/KqQyH+uN4Ww8KkcAqJODYH3w==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + + '@next/swc-darwin-x64@15.3.1': + resolution: {integrity: sha512-q+aw+cJ2ooVYdCEqZVk+T4Ni10jF6Fo5DfpEV51OupMaV5XL6pf3GCzrk6kSSZBsMKZtVC1Zm/xaNBFpA6bJ2g==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + + '@next/swc-linux-arm64-gnu@15.3.1': + resolution: {integrity: sha512-wBQ+jGUI3N0QZyWmmvRHjXjTWFy8o+zPFLSOyAyGFI94oJi+kK/LIZFJXeykvgXUk1NLDAEFDZw/NVINhdk9FQ==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + + '@next/swc-linux-arm64-musl@15.3.1': + resolution: {integrity: sha512-IIxXEXRti/AulO9lWRHiCpUUR8AR/ZYLPALgiIg/9ENzMzLn3l0NSxVdva7R/VDcuSEBo0eGVCe3evSIHNz0Hg==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + + '@next/swc-linux-x64-gnu@15.3.1': + resolution: {integrity: sha512-bfI4AMhySJbyXQIKH5rmLJ5/BP7bPwuxauTvVEiJ/ADoddaA9fgyNNCcsbu9SlqfHDoZmfI6g2EjzLwbsVTr5A==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + + '@next/swc-linux-x64-musl@15.3.1': + resolution: {integrity: sha512-FeAbR7FYMWR+Z+M5iSGytVryKHiAsc0x3Nc3J+FD5NVbD5Mqz7fTSy8CYliXinn7T26nDMbpExRUI/4ekTvoiA==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + + '@next/swc-win32-arm64-msvc@15.3.1': + resolution: {integrity: sha512-yP7FueWjphQEPpJQ2oKmshk/ppOt+0/bB8JC8svPUZNy0Pi3KbPx2Llkzv1p8CoQa+D2wknINlJpHf3vtChVBw==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + + '@next/swc-win32-x64-msvc@15.3.1': + resolution: {integrity: sha512-3PMvF2zRJAifcRNni9uMk/gulWfWS+qVI/pagd+4yLF5bcXPZPPH2xlYRYOsUjmCJOXSTAC2PjRzbhsRzR2fDQ==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + + '@nodelib/fs.scandir@2.1.5': + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + + '@nodelib/fs.stat@2.0.5': + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + + '@nodelib/fs.walk@1.2.8': + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + + '@nolyfill/is-core-module@1.0.39': + resolution: {integrity: sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA==} + engines: {node: '>=12.4.0'} + + '@parcel/watcher-android-arm64@2.5.1': + resolution: {integrity: sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [android] + + '@parcel/watcher-darwin-arm64@2.5.1': + resolution: {integrity: sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [darwin] + + '@parcel/watcher-darwin-x64@2.5.1': + resolution: {integrity: sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [darwin] + + '@parcel/watcher-freebsd-x64@2.5.1': + resolution: {integrity: sha512-SI4eljM7Flp9yPuKi8W0ird8TI/JK6CSxju3NojVI6BjHsTyK7zxA9urjVjEKJ5MBYC+bLmMcbAWlZ+rFkLpJQ==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [freebsd] + + '@parcel/watcher-linux-arm-glibc@2.5.1': + resolution: {integrity: sha512-RCdZlEyTs8geyBkkcnPWvtXLY44BCeZKmGYRtSgtwwnHR4dxfHRG3gR99XdMEdQ7KeiDdasJwwvNSF5jKtDwdA==} + engines: {node: '>= 10.0.0'} + cpu: [arm] + os: [linux] + + '@parcel/watcher-linux-arm-musl@2.5.1': + resolution: {integrity: sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q==} + engines: {node: '>= 10.0.0'} + cpu: [arm] + os: [linux] + + '@parcel/watcher-linux-arm64-glibc@2.5.1': + resolution: {integrity: sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [linux] + + '@parcel/watcher-linux-arm64-musl@2.5.1': + resolution: {integrity: sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [linux] + + '@parcel/watcher-linux-x64-glibc@2.5.1': + resolution: {integrity: sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [linux] + + '@parcel/watcher-linux-x64-musl@2.5.1': + resolution: {integrity: sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [linux] + + '@parcel/watcher-win32-arm64@2.5.1': + resolution: {integrity: sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [win32] + + '@parcel/watcher-win32-ia32@2.5.1': + resolution: {integrity: sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ==} + engines: {node: '>= 10.0.0'} + cpu: [ia32] + os: [win32] + + '@parcel/watcher-win32-x64@2.5.1': + resolution: {integrity: sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [win32] + + '@parcel/watcher@2.5.1': + resolution: {integrity: sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==} + engines: {node: '>= 10.0.0'} + + '@popperjs/core@2.11.8': + resolution: {integrity: sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==} + + '@rtsao/scc@1.1.0': + resolution: {integrity: sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==} + + '@rushstack/eslint-patch@1.11.0': + resolution: {integrity: sha512-zxnHvoMQVqewTJr/W4pKjF0bMGiKJv1WX7bSrkl46Hg0QjESbzBROWK0Wg4RphzSOS5Jiy7eFimmM3UgMrMZbQ==} + + '@socket.io/component-emitter@3.1.2': + resolution: {integrity: sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==} + + '@standard-schema/utils@0.3.0': + resolution: {integrity: sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==} + + '@swc/counter@0.1.3': + resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==} + + '@swc/helpers@0.5.15': + resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==} + + '@tybys/wasm-util@0.9.0': + resolution: {integrity: sha512-6+7nlbMVX/PVDCwaIQ8nTOPveOcFLSt8GcXdx8hD0bt39uWxYT88uXzqTd4fTvqta7oeUJqudepapKNt2DYJFw==} + + '@types/estree@1.0.7': + resolution: {integrity: sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==} + + '@types/json-schema@7.0.15': + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + + '@types/json5@0.0.29': + resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} + + '@types/parse-json@4.0.2': + resolution: {integrity: sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==} + + '@types/prop-types@15.7.14': + resolution: {integrity: sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ==} + + '@types/react-transition-group@4.4.12': + resolution: {integrity: sha512-8TV6R3h2j7a91c+1DXdJi3Syo69zzIZbz7Lg5tORM5LEJG7X/E6a1V3drRyBRZq7/utz7A+c4OgYLiLcYGHG6w==} + peerDependencies: + '@types/react': '*' + + '@types/react@19.1.2': + resolution: {integrity: sha512-oxLPMytKchWGbnQM9O7D67uPa9paTNxO7jVoNMXgkkErULBPhPARCfkKL9ytcIJJRGjbsVwW4ugJzyFFvm/Tiw==} + + '@typescript-eslint/eslint-plugin@8.31.1': + resolution: {integrity: sha512-oUlH4h1ABavI4F0Xnl8/fOtML/eu8nI2A1nYd+f+55XI0BLu+RIqKoCiZKNo6DtqZBEQm5aNKA20G3Z5w3R6GQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + '@typescript-eslint/parser': ^8.0.0 || ^8.0.0-alpha.0 + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.9.0' + + '@typescript-eslint/parser@8.31.1': + resolution: {integrity: sha512-oU/OtYVydhXnumd0BobL9rkJg7wFJ9bFFPmSmB/bf/XWN85hlViji59ko6bSKBXyseT9V8l+CN1nwmlbiN0G7Q==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.9.0' + + '@typescript-eslint/scope-manager@8.31.1': + resolution: {integrity: sha512-BMNLOElPxrtNQMIsFHE+3P0Yf1z0dJqV9zLdDxN/xLlWMlXK/ApEsVEKzpizg9oal8bAT5Sc7+ocal7AC1HCVw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/type-utils@8.31.1': + resolution: {integrity: sha512-fNaT/m9n0+dpSp8G/iOQ05GoHYXbxw81x+yvr7TArTuZuCA6VVKbqWYVZrV5dVagpDTtj/O8k5HBEE/p/HM5LA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.9.0' + + '@typescript-eslint/types@8.31.1': + resolution: {integrity: sha512-SfepaEFUDQYRoA70DD9GtytljBePSj17qPxFHA/h3eg6lPTqGJ5mWOtbXCk1YrVU1cTJRd14nhaXWFu0l2troQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/typescript-estree@8.31.1': + resolution: {integrity: sha512-kaA0ueLe2v7KunYOyWYtlf/QhhZb7+qh4Yw6Ni5kgukMIG+iP773tjgBiLWIXYumWCwEq3nLW+TUywEp8uEeag==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <5.9.0' + + '@typescript-eslint/utils@8.31.1': + resolution: {integrity: sha512-2DSI4SNfF5T4oRveQ4nUrSjUqjMND0nLq9rEkz0gfGr3tg0S5KB6DhwR+WZPCjzkZl3cH+4x2ce3EsL50FubjQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.9.0' + + '@typescript-eslint/visitor-keys@8.31.1': + resolution: {integrity: sha512-I+/rgqOVBn6f0o7NDTmAPWWC6NuqhV174lfYvAm9fUaWeiefLdux9/YI3/nLugEn9L8fcSi0XmpKi/r5u0nmpw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@unrs/resolver-binding-darwin-arm64@1.7.2': + resolution: {integrity: sha512-vxtBno4xvowwNmO/ASL0Y45TpHqmNkAaDtz4Jqb+clmcVSSl8XCG/PNFFkGsXXXS6AMjP+ja/TtNCFFa1QwLRg==} + cpu: [arm64] + os: [darwin] + + '@unrs/resolver-binding-darwin-x64@1.7.2': + resolution: {integrity: sha512-qhVa8ozu92C23Hsmv0BF4+5Dyyd5STT1FolV4whNgbY6mj3kA0qsrGPe35zNR3wAN7eFict3s4Rc2dDTPBTuFQ==} + cpu: [x64] + os: [darwin] + + '@unrs/resolver-binding-freebsd-x64@1.7.2': + resolution: {integrity: sha512-zKKdm2uMXqLFX6Ac7K5ElnnG5VIXbDlFWzg4WJ8CGUedJryM5A3cTgHuGMw1+P5ziV8CRhnSEgOnurTI4vpHpg==} + cpu: [x64] + os: [freebsd] + + '@unrs/resolver-binding-linux-arm-gnueabihf@1.7.2': + resolution: {integrity: sha512-8N1z1TbPnHH+iDS/42GJ0bMPLiGK+cUqOhNbMKtWJ4oFGzqSJk/zoXFzcQkgtI63qMcUI7wW1tq2usZQSb2jxw==} + cpu: [arm] + os: [linux] + + '@unrs/resolver-binding-linux-arm-musleabihf@1.7.2': + resolution: {integrity: sha512-tjYzI9LcAXR9MYd9rO45m1s0B/6bJNuZ6jeOxo1pq1K6OBuRMMmfyvJYval3s9FPPGmrldYA3mi4gWDlWuTFGA==} + cpu: [arm] + os: [linux] + + '@unrs/resolver-binding-linux-arm64-gnu@1.7.2': + resolution: {integrity: sha512-jon9M7DKRLGZ9VYSkFMflvNqu9hDtOCEnO2QAryFWgT6o6AXU8du56V7YqnaLKr6rAbZBWYsYpikF226v423QA==} + cpu: [arm64] + os: [linux] + + '@unrs/resolver-binding-linux-arm64-musl@1.7.2': + resolution: {integrity: sha512-c8Cg4/h+kQ63pL43wBNaVMmOjXI/X62wQmru51qjfTvI7kmCy5uHTJvK/9LrF0G8Jdx8r34d019P1DVJmhXQpA==} + cpu: [arm64] + os: [linux] + + '@unrs/resolver-binding-linux-ppc64-gnu@1.7.2': + resolution: {integrity: sha512-A+lcwRFyrjeJmv3JJvhz5NbcCkLQL6Mk16kHTNm6/aGNc4FwPHPE4DR9DwuCvCnVHvF5IAd9U4VIs/VvVir5lg==} + cpu: [ppc64] + os: [linux] + + '@unrs/resolver-binding-linux-riscv64-gnu@1.7.2': + resolution: {integrity: sha512-hQQ4TJQrSQW8JlPm7tRpXN8OCNP9ez7PajJNjRD1ZTHQAy685OYqPrKjfaMw/8LiHCt8AZ74rfUVHP9vn0N69Q==} + cpu: [riscv64] + os: [linux] + + '@unrs/resolver-binding-linux-riscv64-musl@1.7.2': + resolution: {integrity: sha512-NoAGbiqrxtY8kVooZ24i70CjLDlUFI7nDj3I9y54U94p+3kPxwd2L692YsdLa+cqQ0VoqMWoehDFp21PKRUoIQ==} + cpu: [riscv64] + os: [linux] + + '@unrs/resolver-binding-linux-s390x-gnu@1.7.2': + resolution: {integrity: sha512-KaZByo8xuQZbUhhreBTW+yUnOIHUsv04P8lKjQ5otiGoSJ17ISGYArc+4vKdLEpGaLbemGzr4ZeUbYQQsLWFjA==} + cpu: [s390x] + os: [linux] + + '@unrs/resolver-binding-linux-x64-gnu@1.7.2': + resolution: {integrity: sha512-dEidzJDubxxhUCBJ/SHSMJD/9q7JkyfBMT77Px1npl4xpg9t0POLvnWywSk66BgZS/b2Hy9Y1yFaoMTFJUe9yg==} + cpu: [x64] + os: [linux] + + '@unrs/resolver-binding-linux-x64-musl@1.7.2': + resolution: {integrity: sha512-RvP+Ux3wDjmnZDT4XWFfNBRVG0fMsc+yVzNFUqOflnDfZ9OYujv6nkh+GOr+watwrW4wdp6ASfG/e7bkDradsw==} + cpu: [x64] + os: [linux] + + '@unrs/resolver-binding-wasm32-wasi@1.7.2': + resolution: {integrity: sha512-y797JBmO9IsvXVRCKDXOxjyAE4+CcZpla2GSoBQ33TVb3ILXuFnMrbR/QQZoauBYeOFuu4w3ifWLw52sdHGz6g==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + + '@unrs/resolver-binding-win32-arm64-msvc@1.7.2': + resolution: {integrity: sha512-gtYTh4/VREVSLA+gHrfbWxaMO/00y+34htY7XpioBTy56YN2eBjkPrY1ML1Zys89X3RJDKVaogzwxlM1qU7egg==} + cpu: [arm64] + os: [win32] + + '@unrs/resolver-binding-win32-ia32-msvc@1.7.2': + resolution: {integrity: sha512-Ywv20XHvHTDRQs12jd3MY8X5C8KLjDbg/jyaal/QLKx3fAShhJyD4blEANInsjxW3P7isHx1Blt56iUDDJO3jg==} + cpu: [ia32] + os: [win32] + + '@unrs/resolver-binding-win32-x64-msvc@1.7.2': + resolution: {integrity: sha512-friS8NEQfHaDbkThxopGk+LuE5v3iY0StruifjQEt7SLbA46OnfgMO15sOTkbpJkol6RB+1l1TYPXh0sCddpvA==} + cpu: [x64] + os: [win32] + + accepts@2.0.0: + resolution: {integrity: sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==} + engines: {node: '>= 0.6'} + + acorn-jsx@5.3.2: + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + + acorn@8.14.1: + resolution: {integrity: sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==} + engines: {node: '>=0.4.0'} + hasBin: true + + ajv@6.12.6: + resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + + ansi-escapes@4.3.2: + resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} + engines: {node: '>=8'} + + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + + aria-query@5.3.2: + resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==} + engines: {node: '>= 0.4'} + + array-buffer-byte-length@1.0.2: + resolution: {integrity: sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==} + engines: {node: '>= 0.4'} + + array-includes@3.1.8: + resolution: {integrity: sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==} + engines: {node: '>= 0.4'} + + array.prototype.findlast@1.2.5: + resolution: {integrity: sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==} + engines: {node: '>= 0.4'} + + array.prototype.findlastindex@1.2.6: + resolution: {integrity: sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ==} + engines: {node: '>= 0.4'} + + array.prototype.flat@1.3.3: + resolution: {integrity: sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==} + engines: {node: '>= 0.4'} + + array.prototype.flatmap@1.3.3: + resolution: {integrity: sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==} + engines: {node: '>= 0.4'} + + array.prototype.tosorted@1.1.4: + resolution: {integrity: sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==} + engines: {node: '>= 0.4'} + + arraybuffer.prototype.slice@1.0.4: + resolution: {integrity: sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==} + engines: {node: '>= 0.4'} + + ast-types-flow@0.0.8: + resolution: {integrity: sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==} + + async-function@1.0.0: + resolution: {integrity: sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==} + engines: {node: '>= 0.4'} + + asynckit@0.4.0: + resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + + available-typed-arrays@1.0.7: + resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} + engines: {node: '>= 0.4'} + + axe-core@4.10.3: + resolution: {integrity: sha512-Xm7bpRXnDSX2YE2YFfBk2FnF0ep6tmG7xPh8iHee8MIcrgq762Nkce856dYtJYLkuIoYZvGfTs/PbZhideTcEg==} + engines: {node: '>=4'} + + axios@1.9.0: + resolution: {integrity: sha512-re4CqKTJaURpzbLHtIi6XpDv20/CnpXOtjRY5/CU32L8gU8ek9UIivcfvSWvmKEngmVbrUtPpdDwWDWL7DNHvg==} + + axobject-query@4.1.0: + resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==} + engines: {node: '>= 0.4'} + + babel-plugin-macros@3.1.0: + resolution: {integrity: sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==} + engines: {node: '>=10', npm: '>=6'} + + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + + base64-js@1.5.1: + resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + + bl@4.1.0: + resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} + + body-parser@2.2.0: + resolution: {integrity: sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==} + engines: {node: '>=18'} + + brace-expansion@1.1.11: + resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} + + brace-expansion@2.0.1: + resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} + + braces@3.0.3: + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} + engines: {node: '>=8'} + + buffer@5.7.1: + resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} + + busboy@1.6.0: + resolution: {integrity: sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==} + engines: {node: '>=10.16.0'} + + bytes@3.1.2: + resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} + engines: {node: '>= 0.8'} + + call-bind-apply-helpers@1.0.2: + resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} + engines: {node: '>= 0.4'} + + call-bind@1.0.8: + resolution: {integrity: sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==} + engines: {node: '>= 0.4'} + + call-bound@1.0.4: + resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} + engines: {node: '>= 0.4'} + + callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + + caniuse-lite@1.0.30001717: + resolution: {integrity: sha512-auPpttCq6BDEG8ZAuHJIplGw6GODhjw+/11e7IjpnYCxZcW/ONgPs0KVBJ0d1bY3e2+7PRe5RCLyP+PfwVgkYw==} + + chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + + chardet@0.7.0: + resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==} + + chokidar@4.0.3: + resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} + engines: {node: '>= 14.16.0'} + + cli-cursor@3.1.0: + resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==} + engines: {node: '>=8'} + + cli-spinners@2.9.2: + resolution: {integrity: sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==} + engines: {node: '>=6'} + + cli-width@3.0.0: + resolution: {integrity: sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==} + engines: {node: '>= 10'} + + client-only@0.0.1: + resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==} + + clone@1.0.4: + resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==} + engines: {node: '>=0.8'} + + clsx@2.1.1: + resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} + engines: {node: '>=6'} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + color-string@1.9.1: + resolution: {integrity: sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==} + + color@4.2.3: + resolution: {integrity: sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==} + engines: {node: '>=12.5.0'} + + combined-stream@1.0.8: + resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} + engines: {node: '>= 0.8'} + + commander@7.2.0: + resolution: {integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==} + engines: {node: '>= 10'} + + concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + + content-disposition@1.0.0: + resolution: {integrity: sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==} + engines: {node: '>= 0.6'} + + content-type@1.0.5: + resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} + engines: {node: '>= 0.6'} + + convert-source-map@1.9.0: + resolution: {integrity: sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==} + + cookie-signature@1.2.2: + resolution: {integrity: sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==} + engines: {node: '>=6.6.0'} + + cookie@0.7.2: + resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==} + engines: {node: '>= 0.6'} + + cors@2.8.5: + resolution: {integrity: sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==} + engines: {node: '>= 0.10'} + + cosmiconfig@7.1.0: + resolution: {integrity: sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==} + engines: {node: '>=10'} + + cross-spawn@7.0.6: + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} + engines: {node: '>= 8'} + + cssjanus@2.3.0: + resolution: {integrity: sha512-ZZXXn51SnxRxAZ6fdY7mBDPmA4OZd83q/J9Gdqz3YmE9TUq+9tZl+tdOnCi7PpNygI6PEkehj9rgifv5+W8a5A==} + engines: {node: '>=10.0.0'} + + csstype@3.1.3: + resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} + + damerau-levenshtein@1.0.8: + resolution: {integrity: sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==} + + data-view-buffer@1.0.2: + resolution: {integrity: sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==} + engines: {node: '>= 0.4'} + + data-view-byte-length@1.0.2: + resolution: {integrity: sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==} + engines: {node: '>= 0.4'} + + data-view-byte-offset@1.0.1: + resolution: {integrity: sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==} + engines: {node: '>= 0.4'} + + debug@3.2.7: + resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + debug@4.3.7: + resolution: {integrity: sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + debug@4.4.0: + resolution: {integrity: sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + deep-is@0.1.4: + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + + defaults@1.0.4: + resolution: {integrity: sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==} + + define-data-property@1.1.4: + resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} + engines: {node: '>= 0.4'} + + define-properties@1.2.1: + resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} + engines: {node: '>= 0.4'} + + delayed-stream@1.0.0: + resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} + engines: {node: '>=0.4.0'} + + depd@2.0.0: + resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} + engines: {node: '>= 0.8'} + + dequal@2.0.3: + resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} + engines: {node: '>=6'} + + detect-libc@1.0.3: + resolution: {integrity: sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==} + engines: {node: '>=0.10'} + hasBin: true + + detect-libc@2.0.4: + resolution: {integrity: sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==} + engines: {node: '>=8'} + + doctrine@2.1.0: + resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} + engines: {node: '>=0.10.0'} + + dom-helpers@5.2.1: + resolution: {integrity: sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==} + + dunder-proto@1.0.1: + resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} + engines: {node: '>= 0.4'} + + ee-first@1.1.1: + resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} + + emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + + emoji-regex@9.2.2: + resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + + encodeurl@2.0.0: + resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} + engines: {node: '>= 0.8'} + + engine.io-client@6.6.3: + resolution: {integrity: sha512-T0iLjnyNWahNyv/lcjS2y4oE358tVS/SYQNxYXGAJ9/GLgH4VCvOQ/mhTjqU88mLZCQgiG8RIegFHYCdVC+j5w==} + + engine.io-parser@5.2.3: + resolution: {integrity: sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==} + engines: {node: '>=10.0.0'} + + error-ex@1.3.2: + resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} + + es-abstract@1.23.9: + resolution: {integrity: sha512-py07lI0wjxAC/DcfK1S6G7iANonniZwTISvdPzk9hzeH0IZIshbuuFxLIU96OyF89Yb9hiqWn8M/bY83KY5vzA==} + engines: {node: '>= 0.4'} + + es-define-property@1.0.1: + resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} + engines: {node: '>= 0.4'} + + es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + + es-iterator-helpers@1.2.1: + resolution: {integrity: sha512-uDn+FE1yrDzyC0pCo961B2IHbdM8y/ACZsKD4dG6WqrjV53BADjwa7D+1aom2rsNVfLyDgU/eigvlJGJ08OQ4w==} + engines: {node: '>= 0.4'} + + es-object-atoms@1.1.1: + resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} + engines: {node: '>= 0.4'} + + es-set-tostringtag@2.1.0: + resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} + engines: {node: '>= 0.4'} + + es-shim-unscopables@1.1.0: + resolution: {integrity: sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==} + engines: {node: '>= 0.4'} + + es-to-primitive@1.3.0: + resolution: {integrity: sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==} + engines: {node: '>= 0.4'} + + escape-html@1.0.3: + resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} + + escape-string-regexp@1.0.5: + resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} + engines: {node: '>=0.8.0'} + + escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + + eslint-config-next@15.3.1: + resolution: {integrity: sha512-GnmyVd9TE/Ihe3RrvcafFhXErErtr2jS0JDeCSp3vWvy86AXwHsRBt0E3MqP/m8ACS1ivcsi5uaqjbhsG18qKw==} + peerDependencies: + eslint: ^7.23.0 || ^8.0.0 || ^9.0.0 + typescript: '>=3.3.1' + peerDependenciesMeta: + typescript: + optional: true + + eslint-import-resolver-node@0.3.9: + resolution: {integrity: sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==} + + eslint-import-resolver-typescript@3.10.1: + resolution: {integrity: sha512-A1rHYb06zjMGAxdLSkN2fXPBwuSaQ0iO5M/hdyS0Ajj1VBaRp0sPD3dn1FhME3c/JluGFbwSxyCfqdSbtQLAHQ==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + eslint: '*' + eslint-plugin-import: '*' + eslint-plugin-import-x: '*' + peerDependenciesMeta: + eslint-plugin-import: + optional: true + eslint-plugin-import-x: + optional: true + + eslint-module-utils@2.12.0: + resolution: {integrity: sha512-wALZ0HFoytlyh/1+4wuZ9FJCD/leWHQzzrxJ8+rebyReSLk7LApMyd3WJaLVoN+D5+WIdJyDK1c6JnE65V4Zyg==} + engines: {node: '>=4'} + peerDependencies: + '@typescript-eslint/parser': '*' + eslint: '*' + eslint-import-resolver-node: '*' + eslint-import-resolver-typescript: '*' + eslint-import-resolver-webpack: '*' + peerDependenciesMeta: + '@typescript-eslint/parser': + optional: true + eslint: + optional: true + eslint-import-resolver-node: + optional: true + eslint-import-resolver-typescript: + optional: true + eslint-import-resolver-webpack: + optional: true + + eslint-plugin-import@2.31.0: + resolution: {integrity: sha512-ixmkI62Rbc2/w8Vfxyh1jQRTdRTF52VxwRVHl/ykPAmqG+Nb7/kNn+byLP0LxPgI7zWA16Jt82SybJInmMia3A==} + engines: {node: '>=4'} + peerDependencies: + '@typescript-eslint/parser': '*' + eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9 + peerDependenciesMeta: + '@typescript-eslint/parser': + optional: true + + eslint-plugin-jsx-a11y@6.10.2: + resolution: {integrity: sha512-scB3nz4WmG75pV8+3eRUQOHZlNSUhFNq37xnpgRkCCELU3XMvXAxLk1eqWWyE22Ki4Q01Fnsw9BA3cJHDPgn2Q==} + engines: {node: '>=4.0'} + peerDependencies: + eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9 + + eslint-plugin-react-hooks@5.2.0: + resolution: {integrity: sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==} + engines: {node: '>=10'} + peerDependencies: + eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 + + eslint-plugin-react@7.37.5: + resolution: {integrity: sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==} + engines: {node: '>=4'} + peerDependencies: + eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7 + + eslint-scope@8.3.0: + resolution: {integrity: sha512-pUNxi75F8MJ/GdeKtVLSbYg4ZI34J6C0C7sbL4YOp2exGwen7ZsuBqKzUhXd0qMQ362yET3z+uPwKeg/0C2XCQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + eslint-visitor-keys@3.4.3: + resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + eslint-visitor-keys@4.2.0: + resolution: {integrity: sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + eslint@9.26.0: + resolution: {integrity: sha512-Hx0MOjPh6uK9oq9nVsATZKE/Wlbai7KFjfCuw9UHaguDW3x+HF0O5nIi3ud39TWgrTjTO5nHxmL3R1eANinWHQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + hasBin: true + peerDependencies: + jiti: '*' + peerDependenciesMeta: + jiti: + optional: true + + espree@10.3.0: + resolution: {integrity: sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + esquery@1.6.0: + resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==} + engines: {node: '>=0.10'} + + esrecurse@4.3.0: + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + + estraverse@5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + + esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + + etag@1.8.1: + resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} + engines: {node: '>= 0.6'} + + eventsource-parser@3.0.1: + resolution: {integrity: sha512-VARTJ9CYeuQYb0pZEPbzi740OWFgpHe7AYJ2WFZVnUDUQp5Dk2yJUgF36YsZ81cOyxT0QxmXD2EQpapAouzWVA==} + engines: {node: '>=18.0.0'} + + eventsource@3.0.6: + resolution: {integrity: sha512-l19WpE2m9hSuyP06+FbuUUf1G+R0SFLrtQfbRb9PRr+oimOfxQhgGCbVaXg5IvZyyTThJsxh6L/srkMiCeBPDA==} + engines: {node: '>=18.0.0'} + + express-rate-limit@7.5.0: + resolution: {integrity: sha512-eB5zbQh5h+VenMPM3fh+nw1YExi5nMr6HUCR62ELSP11huvxm/Uir1H1QEyTkk5QX6A58pX6NmaTMceKZ0Eodg==} + engines: {node: '>= 16'} + peerDependencies: + express: ^4.11 || 5 || ^5.0.0-beta.1 + + express@5.1.0: + resolution: {integrity: sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==} + engines: {node: '>= 18'} + + external-editor@3.1.0: + resolution: {integrity: sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==} + engines: {node: '>=4'} + + fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + + fast-glob@3.3.1: + resolution: {integrity: sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==} + engines: {node: '>=8.6.0'} + + fast-glob@3.3.3: + resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} + engines: {node: '>=8.6.0'} + + fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + + fast-levenshtein@2.0.6: + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + + fastq@1.19.1: + resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==} + + fdir@6.4.4: + resolution: {integrity: sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + figures@3.2.0: + resolution: {integrity: sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==} + engines: {node: '>=8'} + + file-entry-cache@8.0.0: + resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} + engines: {node: '>=16.0.0'} + + fill-range@7.1.1: + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} + engines: {node: '>=8'} + + finalhandler@2.1.0: + resolution: {integrity: sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==} + engines: {node: '>= 0.8'} + + find-root@1.1.0: + resolution: {integrity: sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==} + + find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + + flat-cache@4.0.1: + resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} + engines: {node: '>=16'} + + flatted@3.3.3: + resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==} + + follow-redirects@1.15.9: + resolution: {integrity: sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==} + engines: {node: '>=4.0'} + peerDependencies: + debug: '*' + peerDependenciesMeta: + debug: + optional: true + + for-each@0.3.5: + resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==} + engines: {node: '>= 0.4'} + + form-data@4.0.2: + resolution: {integrity: sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==} + engines: {node: '>= 6'} + + forwarded@0.2.0: + resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} + engines: {node: '>= 0.6'} + + fresh@2.0.0: + resolution: {integrity: sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==} + engines: {node: '>= 0.8'} + + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + function.prototype.name@1.1.8: + resolution: {integrity: sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==} + engines: {node: '>= 0.4'} + + functions-have-names@1.2.3: + resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} + + get-intrinsic@1.3.0: + resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} + engines: {node: '>= 0.4'} + + get-proto@1.0.1: + resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} + engines: {node: '>= 0.4'} + + get-symbol-description@1.1.0: + resolution: {integrity: sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==} + engines: {node: '>= 0.4'} + + get-tsconfig@4.10.0: + resolution: {integrity: sha512-kGzZ3LWWQcGIAmg6iWvXn0ei6WDtV26wzHRMwDSzmAbcXrTEXxHy6IehI6/4eT6VRKyMP1eF1VqwrVUmE/LR7A==} + + glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + + glob-parent@6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + + globals@11.12.0: + resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} + engines: {node: '>=4'} + + globals@14.0.0: + resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} + engines: {node: '>=18'} + + globalthis@1.0.4: + resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==} + engines: {node: '>= 0.4'} + + gopd@1.2.0: + resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} + engines: {node: '>= 0.4'} + + graphemer@1.4.0: + resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} + + has-bigints@1.1.0: + resolution: {integrity: sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==} + engines: {node: '>= 0.4'} + + has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + + has-property-descriptors@1.0.2: + resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} + + has-proto@1.2.0: + resolution: {integrity: sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==} + engines: {node: '>= 0.4'} + + has-symbols@1.1.0: + resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} + engines: {node: '>= 0.4'} + + has-tostringtag@1.0.2: + resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} + engines: {node: '>= 0.4'} + + hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} + + hoist-non-react-statics@3.3.2: + resolution: {integrity: sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==} + + http-errors@2.0.0: + resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==} + engines: {node: '>= 0.8'} + + iconv-lite@0.4.24: + resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} + engines: {node: '>=0.10.0'} + + iconv-lite@0.6.3: + resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} + engines: {node: '>=0.10.0'} + + ieee754@1.2.1: + resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} + + ignore@5.3.2: + resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} + engines: {node: '>= 4'} + + immutable@5.1.1: + resolution: {integrity: sha512-3jatXi9ObIsPGr3N5hGw/vWWcTkq6hUYhpQz4k0wLC+owqWi/LiugIw9x0EdNZ2yGedKN/HzePiBvaJRXa0Ujg==} + + import-fresh@3.3.1: + resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} + engines: {node: '>=6'} + + imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + + inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + + inquirer@8.2.6: + resolution: {integrity: sha512-M1WuAmb7pn9zdFRtQYk26ZBoY043Sse0wVDdk4Bppr+JOXyQYybdtvK+l9wUibhtjdjvtoiNy8tk+EgsYIUqKg==} + engines: {node: '>=12.0.0'} + + internal-slot@1.1.0: + resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==} + engines: {node: '>= 0.4'} + + ipaddr.js@1.9.1: + resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} + engines: {node: '>= 0.10'} + + is-array-buffer@3.0.5: + resolution: {integrity: sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==} + engines: {node: '>= 0.4'} + + is-arrayish@0.2.1: + resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} + + is-arrayish@0.3.2: + resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==} + + is-async-function@2.1.1: + resolution: {integrity: sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==} + engines: {node: '>= 0.4'} + + is-bigint@1.1.0: + resolution: {integrity: sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==} + engines: {node: '>= 0.4'} + + is-boolean-object@1.2.2: + resolution: {integrity: sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==} + engines: {node: '>= 0.4'} + + is-bun-module@2.0.0: + resolution: {integrity: sha512-gNCGbnnnnFAUGKeZ9PdbyeGYJqewpmc2aKHUEMO5nQPWU9lOmv7jcmQIv+qHD8fXW6W7qfuCwX4rY9LNRjXrkQ==} + + is-callable@1.2.7: + resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} + engines: {node: '>= 0.4'} + + is-core-module@2.16.1: + resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==} + engines: {node: '>= 0.4'} + + is-data-view@1.0.2: + resolution: {integrity: sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==} + engines: {node: '>= 0.4'} + + is-date-object@1.1.0: + resolution: {integrity: sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==} + engines: {node: '>= 0.4'} + + is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + + is-finalizationregistry@1.1.1: + resolution: {integrity: sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==} + engines: {node: '>= 0.4'} + + is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + + is-generator-function@1.1.0: + resolution: {integrity: sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==} + engines: {node: '>= 0.4'} + + is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + + is-interactive@1.0.0: + resolution: {integrity: sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==} + engines: {node: '>=8'} + + is-map@2.0.3: + resolution: {integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==} + engines: {node: '>= 0.4'} + + is-number-object@1.1.1: + resolution: {integrity: sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==} + engines: {node: '>= 0.4'} + + is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + + is-promise@4.0.0: + resolution: {integrity: sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==} + + is-regex@1.2.1: + resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==} + engines: {node: '>= 0.4'} + + is-set@2.0.3: + resolution: {integrity: sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==} + engines: {node: '>= 0.4'} + + is-shared-array-buffer@1.0.4: + resolution: {integrity: sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==} + engines: {node: '>= 0.4'} + + is-string@1.1.1: + resolution: {integrity: sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==} + engines: {node: '>= 0.4'} + + is-symbol@1.1.1: + resolution: {integrity: sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==} + engines: {node: '>= 0.4'} + + is-typed-array@1.1.15: + resolution: {integrity: sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==} + engines: {node: '>= 0.4'} + + is-unicode-supported@0.1.0: + resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==} + engines: {node: '>=10'} + + is-weakmap@2.0.2: + resolution: {integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==} + engines: {node: '>= 0.4'} + + is-weakref@1.1.1: + resolution: {integrity: sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==} + engines: {node: '>= 0.4'} + + is-weakset@2.0.4: + resolution: {integrity: sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==} + engines: {node: '>= 0.4'} + + isarray@2.0.5: + resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + iterator.prototype@1.1.5: + resolution: {integrity: sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==} + engines: {node: '>= 0.4'} + + jalali-moment@3.3.11: + resolution: {integrity: sha512-tdSaRs9cjWjOIaWhcsGFZMhZQhfgok5J0TwqFpBIZPudZxxa6yjUPoLCOwuvbAtRpiZn7k/mvazAJh+vEN5suw==} + hasBin: true + + js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + + js-yaml@4.1.0: + resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} + hasBin: true + + jsesc@3.1.0: + resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} + engines: {node: '>=6'} + hasBin: true + + json-buffer@3.0.1: + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + + json-parse-even-better-errors@2.3.1: + resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} + + json-schema-traverse@0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + + json-stable-stringify-without-jsonify@1.0.1: + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + + json5@1.0.2: + resolution: {integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==} + hasBin: true + + jsx-ast-utils@3.3.5: + resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==} + engines: {node: '>=4.0'} + + keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + + language-subtag-registry@0.3.23: + resolution: {integrity: sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==} + + language-tags@1.0.9: + resolution: {integrity: sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==} + engines: {node: '>=0.10'} + + levn@0.4.1: + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} + + lines-and-columns@1.2.4: + resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + + locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + + lodash.merge@4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + + lodash@4.17.21: + resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + + log-symbols@4.1.0: + resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==} + engines: {node: '>=10'} + + loose-envify@1.4.0: + resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} + hasBin: true + + lz-string@1.5.0: + resolution: {integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==} + hasBin: true + + math-intrinsics@1.1.0: + resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} + engines: {node: '>= 0.4'} + + media-typer@1.1.0: + resolution: {integrity: sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==} + engines: {node: '>= 0.8'} + + merge-descriptors@2.0.0: + resolution: {integrity: sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==} + engines: {node: '>=18'} + + merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + + micromatch@4.0.8: + resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} + engines: {node: '>=8.6'} + + mime-db@1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} + + mime-db@1.54.0: + resolution: {integrity: sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==} + engines: {node: '>= 0.6'} + + mime-types@2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} + + mime-types@3.0.1: + resolution: {integrity: sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==} + engines: {node: '>= 0.6'} + + mimic-fn@2.1.0: + resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} + engines: {node: '>=6'} + + minimatch@3.1.2: + resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + + minimatch@9.0.5: + resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} + engines: {node: '>=16 || 14 >=14.17'} + + minimist@1.2.8: + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + + moment@2.30.1: + resolution: {integrity: sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + mute-stream@0.0.8: + resolution: {integrity: sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==} + + nanoid@3.3.11: + resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + napi-postinstall@0.2.3: + resolution: {integrity: sha512-Mi7JISo/4Ij2tDZ2xBE2WH+/KvVlkhA6juEjpEeRAVPNCpN3nxJo/5FhDNKgBcdmcmhaH6JjgST4xY/23ZYK0w==} + engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} + hasBin: true + + natural-compare@1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + + negotiator@1.0.0: + resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==} + engines: {node: '>= 0.6'} + + next@15.3.1: + resolution: {integrity: sha512-8+dDV0xNLOgHlyBxP1GwHGVaNXsmp+2NhZEYrXr24GWLHtt27YrBPbPuHvzlhi7kZNYjeJNR93IF5zfFu5UL0g==} + engines: {node: ^18.18.0 || ^19.8.0 || >= 20.0.0} + hasBin: true + peerDependencies: + '@opentelemetry/api': ^1.1.0 + '@playwright/test': ^1.41.2 + babel-plugin-react-compiler: '*' + react: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0 + react-dom: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0 + sass: ^1.3.0 + peerDependenciesMeta: + '@opentelemetry/api': + optional: true + '@playwright/test': + optional: true + babel-plugin-react-compiler: + optional: true + sass: + optional: true + + node-addon-api@7.1.1: + resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==} + + object-assign@4.1.1: + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} + + object-inspect@1.13.4: + resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} + engines: {node: '>= 0.4'} + + object-keys@1.1.1: + resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} + engines: {node: '>= 0.4'} + + object.assign@4.1.7: + resolution: {integrity: sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==} + engines: {node: '>= 0.4'} + + object.entries@1.1.9: + resolution: {integrity: sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==} + engines: {node: '>= 0.4'} + + object.fromentries@2.0.8: + resolution: {integrity: sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==} + engines: {node: '>= 0.4'} + + object.groupby@1.0.3: + resolution: {integrity: sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==} + engines: {node: '>= 0.4'} + + object.values@1.2.1: + resolution: {integrity: sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==} + engines: {node: '>= 0.4'} + + on-finished@2.4.1: + resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} + engines: {node: '>= 0.8'} + + once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + + onetime@5.1.2: + resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} + engines: {node: '>=6'} + + optionator@0.9.4: + resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} + engines: {node: '>= 0.8.0'} + + ora@5.4.1: + resolution: {integrity: sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==} + engines: {node: '>=10'} + + os-tmpdir@1.0.2: + resolution: {integrity: sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==} + engines: {node: '>=0.10.0'} + + own-keys@1.0.1: + resolution: {integrity: sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==} + engines: {node: '>= 0.4'} + + p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + + p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + + parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + + parse-json@5.2.0: + resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} + engines: {node: '>=8'} + + parseurl@1.3.3: + resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} + engines: {node: '>= 0.8'} + + path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + + path-parse@1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + + path-to-regexp@8.2.0: + resolution: {integrity: sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==} + engines: {node: '>=16'} + + path-type@4.0.0: + resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} + engines: {node: '>=8'} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + picomatch@2.3.1: + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + engines: {node: '>=8.6'} + + picomatch@4.0.2: + resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==} + engines: {node: '>=12'} + + pkce-challenge@5.0.0: + resolution: {integrity: sha512-ueGLflrrnvwB3xuo/uGob5pd5FN7l0MsLf0Z87o/UQmRtwjvfylfc9MurIxRAWywCYTgrvpXBcqjV4OfCYGCIQ==} + engines: {node: '>=16.20.0'} + + possible-typed-array-names@1.1.0: + resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==} + engines: {node: '>= 0.4'} + + postcss@8.4.31: + resolution: {integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==} + engines: {node: ^10 || ^12 || >=14} + + prelude-ls@1.2.1: + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} + + prettier@3.5.3: + resolution: {integrity: sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==} + engines: {node: '>=14'} + hasBin: true + + prop-types@15.8.1: + resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} + + property-expr@2.0.6: + resolution: {integrity: sha512-SVtmxhRE/CGkn3eZY1T6pC8Nln6Fr/lu1mKSgRud0eC73whjGfoAogbn78LkD8aFL0zz3bAFerKSnOl7NlErBA==} + + proxy-addr@2.0.7: + resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} + engines: {node: '>= 0.10'} + + proxy-from-env@1.1.0: + resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} + + punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + + qs@6.14.0: + resolution: {integrity: sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==} + engines: {node: '>=0.6'} + + queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + + range-parser@1.2.1: + resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} + engines: {node: '>= 0.6'} + + raw-body@3.0.0: + resolution: {integrity: sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==} + engines: {node: '>= 0.8'} + + react-dom@19.1.0: + resolution: {integrity: sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==} + peerDependencies: + react: ^19.1.0 + + react-hook-form@7.56.2: + resolution: {integrity: sha512-vpfuHuQMF/L6GpuQ4c3ZDo+pRYxIi40gQqsCmmfUBwm+oqvBhKhwghCuj2o00YCgSfU6bR9KC/xnQGWm3Gr08A==} + engines: {node: '>=18.0.0'} + peerDependencies: + react: ^16.8.0 || ^17 || ^18 || ^19 + + react-is@16.13.1: + resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} + + react-is@19.1.0: + resolution: {integrity: sha512-Oe56aUPnkHyyDxxkvqtd7KkdQP5uIUfHxd5XTb3wE9d/kRnZLmKbDB0GWk919tdQ+mxxPtG6EAs6RMT6i1qtHg==} + + react-toastify@11.0.5: + resolution: {integrity: sha512-EpqHBGvnSTtHYhCPLxML05NLY2ZX0JURbAdNYa6BUkk+amz4wbKBQvoKQAB0ardvSarUBuY4Q4s1sluAzZwkmA==} + peerDependencies: + react: ^18 || ^19 + react-dom: ^18 || ^19 + + react-transition-group@4.4.5: + resolution: {integrity: sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==} + peerDependencies: + react: '>=16.6.0' + react-dom: '>=16.6.0' + + react@19.1.0: + resolution: {integrity: sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==} + engines: {node: '>=0.10.0'} + + readable-stream@3.6.2: + resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} + engines: {node: '>= 6'} + + readdirp@4.1.2: + resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} + engines: {node: '>= 14.18.0'} + + reflect.getprototypeof@1.0.10: + resolution: {integrity: sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==} + engines: {node: '>= 0.4'} + + regexp.prototype.flags@1.5.4: + resolution: {integrity: sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==} + engines: {node: '>= 0.4'} + + resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + + resolve-pkg-maps@1.0.0: + resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} + + resolve@1.22.10: + resolution: {integrity: sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==} + engines: {node: '>= 0.4'} + hasBin: true + + resolve@2.0.0-next.5: + resolution: {integrity: sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==} + hasBin: true + + restore-cursor@3.1.0: + resolution: {integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==} + engines: {node: '>=8'} + + reusify@1.1.0: + resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + + router@2.2.0: + resolution: {integrity: sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==} + engines: {node: '>= 18'} + + run-async@2.4.1: + resolution: {integrity: sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==} + engines: {node: '>=0.12.0'} + + run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + + rxjs@7.8.2: + resolution: {integrity: sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==} + + safe-array-concat@1.1.3: + resolution: {integrity: sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==} + engines: {node: '>=0.4'} + + safe-buffer@5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + + safe-push-apply@1.0.0: + resolution: {integrity: sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==} + engines: {node: '>= 0.4'} + + safe-regex-test@1.1.0: + resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==} + engines: {node: '>= 0.4'} + + safer-buffer@2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + + sass@1.87.0: + resolution: {integrity: sha512-d0NoFH4v6SjEK7BoX810Jsrhj7IQSYHAHLi/iSpgqKc7LaIDshFRlSg5LOymf9FqQhxEHs2W5ZQXlvy0KD45Uw==} + engines: {node: '>=14.0.0'} + hasBin: true + + scheduler@0.26.0: + resolution: {integrity: sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==} + + semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + + semver@7.7.1: + resolution: {integrity: sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==} + engines: {node: '>=10'} + hasBin: true + + send@1.2.0: + resolution: {integrity: sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==} + engines: {node: '>= 18'} + + serve-static@2.2.0: + resolution: {integrity: sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==} + engines: {node: '>= 18'} + + set-function-length@1.2.2: + resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} + engines: {node: '>= 0.4'} + + set-function-name@2.0.2: + resolution: {integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==} + engines: {node: '>= 0.4'} + + set-proto@1.0.0: + resolution: {integrity: sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==} + engines: {node: '>= 0.4'} + + setprototypeof@1.2.0: + resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} + + sharp@0.34.1: + resolution: {integrity: sha512-1j0w61+eVxu7DawFJtnfYcvSv6qPFvfTaqzTQ2BLknVhHTwGS8sc63ZBF4rzkWMBVKybo4S5OBtDdZahh2A1xg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + + side-channel-list@1.0.0: + resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==} + engines: {node: '>= 0.4'} + + side-channel-map@1.0.1: + resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==} + engines: {node: '>= 0.4'} + + side-channel-weakmap@1.0.2: + resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==} + engines: {node: '>= 0.4'} + + side-channel@1.1.0: + resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==} + engines: {node: '>= 0.4'} + + signal-exit@3.0.7: + resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} + + simple-swizzle@0.2.2: + resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==} + + socket.io-client@4.8.1: + resolution: {integrity: sha512-hJVXfu3E28NmzGk8o1sHhN3om52tRvwYeidbj7xKy2eIIse5IoKX3USlS6Tqt3BHAtflLIkCQBkzVrEEfWUyYQ==} + engines: {node: '>=10.0.0'} + + socket.io-parser@4.2.4: + resolution: {integrity: sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==} + engines: {node: '>=10.0.0'} + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + + source-map@0.5.7: + resolution: {integrity: sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==} + engines: {node: '>=0.10.0'} + + stable-hash@0.0.5: + resolution: {integrity: sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA==} + + statuses@2.0.1: + resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} + engines: {node: '>= 0.8'} + + streamsearch@1.1.0: + resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==} + engines: {node: '>=10.0.0'} + + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + + string.prototype.includes@2.0.1: + resolution: {integrity: sha512-o7+c9bW6zpAdJHTtujeePODAhkuicdAryFsfVKwA+wGw89wJ4GTY484WTucM9hLtDEOpOvI+aHnzqnC5lHp4Rg==} + engines: {node: '>= 0.4'} + + string.prototype.matchall@4.0.12: + resolution: {integrity: sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==} + engines: {node: '>= 0.4'} + + string.prototype.repeat@1.0.0: + resolution: {integrity: sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==} + + string.prototype.trim@1.2.10: + resolution: {integrity: sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==} + engines: {node: '>= 0.4'} + + string.prototype.trimend@1.0.9: + resolution: {integrity: sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==} + engines: {node: '>= 0.4'} + + string.prototype.trimstart@1.0.8: + resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==} + engines: {node: '>= 0.4'} + + string_decoder@1.3.0: + resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} + + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + + strip-bom@3.0.0: + resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} + engines: {node: '>=4'} + + strip-json-comments@3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} + + styled-jsx@5.1.6: + resolution: {integrity: sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==} + engines: {node: '>= 12.0.0'} + peerDependencies: + '@babel/core': '*' + babel-plugin-macros: '*' + react: '>= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0' + peerDependenciesMeta: + '@babel/core': + optional: true + babel-plugin-macros: + optional: true + + stylis-plugin-rtl@2.1.1: + resolution: {integrity: sha512-q6xIkri6fBufIO/sV55md2CbgS5c6gg9EhSVATtHHCdOnbN/jcI0u3lYhNVeuI65c4lQPo67g8xmq5jrREvzlg==} + peerDependencies: + stylis: 4.x + + stylis@4.2.0: + resolution: {integrity: sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==} + + stylis@4.3.6: + resolution: {integrity: sha512-yQ3rwFWRfwNUY7H5vpU0wfdkNSnvnJinhF9830Swlaxl03zsOjCfmX0ugac+3LtK0lYSgwL/KXc8oYL3mG4YFQ==} + + supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + + supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + + swr@2.3.3: + resolution: {integrity: sha512-dshNvs3ExOqtZ6kJBaAsabhPdHyeY4P2cKwRCniDVifBMoG/SVI7tfLWqPXriVspf2Rg4tPzXJTnwaihIeFw2A==} + peerDependencies: + react: ^16.11.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + through@2.3.8: + resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} + + tiny-case@1.0.3: + resolution: {integrity: sha512-Eet/eeMhkO6TX8mnUteS9zgPbUMQa4I6Kkp5ORiBD5476/m+PIRiumP5tmh5ioJpH7k51Kehawy2UDfsnxxY8Q==} + + tinyglobby@0.2.13: + resolution: {integrity: sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==} + engines: {node: '>=12.0.0'} + + tmp@0.0.33: + resolution: {integrity: sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==} + engines: {node: '>=0.6.0'} + + to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + + toidentifier@1.0.1: + resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} + engines: {node: '>=0.6'} + + toposort@2.0.2: + resolution: {integrity: sha512-0a5EOkAUp8D4moMi2W8ZF8jcga7BgZd91O/yabJCFY8az+XSzeGyTKs0Aoo897iV1Nj6guFq8orWDS96z91oGg==} + + ts-api-utils@2.1.0: + resolution: {integrity: sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==} + engines: {node: '>=18.12'} + peerDependencies: + typescript: '>=4.8.4' + + tsconfig-paths@3.15.0: + resolution: {integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==} + + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + + type-check@0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} + + type-fest@0.21.3: + resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} + engines: {node: '>=10'} + + type-fest@2.19.0: + resolution: {integrity: sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==} + engines: {node: '>=12.20'} + + type-is@2.0.1: + resolution: {integrity: sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==} + engines: {node: '>= 0.6'} + + typed-array-buffer@1.0.3: + resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==} + engines: {node: '>= 0.4'} + + typed-array-byte-length@1.0.3: + resolution: {integrity: sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==} + engines: {node: '>= 0.4'} + + typed-array-byte-offset@1.0.4: + resolution: {integrity: sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==} + engines: {node: '>= 0.4'} + + typed-array-length@1.0.7: + resolution: {integrity: sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==} + engines: {node: '>= 0.4'} + + typescript@5.8.3: + resolution: {integrity: sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==} + engines: {node: '>=14.17'} + hasBin: true + + unbox-primitive@1.1.0: + resolution: {integrity: sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==} + engines: {node: '>= 0.4'} + + unpipe@1.0.0: + resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} + engines: {node: '>= 0.8'} + + unrs-resolver@1.7.2: + resolution: {integrity: sha512-BBKpaylOW8KbHsu378Zky/dGh4ckT/4NW/0SHRABdqRLcQJ2dAOjDo9g97p04sWflm0kqPqpUatxReNV/dqI5A==} + + uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + + use-sync-external-store@1.5.0: + resolution: {integrity: sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + + vary@1.1.2: + resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} + engines: {node: '>= 0.8'} + + wcwidth@1.0.1: + resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==} + + which-boxed-primitive@1.1.1: + resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==} + engines: {node: '>= 0.4'} + + which-builtin-type@1.2.1: + resolution: {integrity: sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==} + engines: {node: '>= 0.4'} + + which-collection@1.0.2: + resolution: {integrity: sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==} + engines: {node: '>= 0.4'} + + which-typed-array@1.1.19: + resolution: {integrity: sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==} + engines: {node: '>= 0.4'} + + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + + word-wrap@1.2.5: + resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} + engines: {node: '>=0.10.0'} + + wrap-ansi@6.2.0: + resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} + engines: {node: '>=8'} + + wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + + ws@8.17.1: + resolution: {integrity: sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + + xmlhttprequest-ssl@2.1.2: + resolution: {integrity: sha512-TEU+nJVUUnA4CYJFLvK5X9AOeH4KvDvhIfm0vV1GaQRtchnG0hgK5p8hw/xjv8cunWYCsiPCSDzObPyhEwq3KQ==} + engines: {node: '>=0.4.0'} + + yaml@1.10.2: + resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==} + engines: {node: '>= 6'} + + yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + + yup@1.6.1: + resolution: {integrity: sha512-JED8pB50qbA4FOkDol0bYF/p60qSEDQqBD0/qeIrUCG1KbPBIQ776fCUNb9ldbPcSTxA69g/47XTo4TqWiuXOA==} + + zod-to-json-schema@3.24.5: + resolution: {integrity: sha512-/AuWwMP+YqiPbsJx5D6TfgRTc4kTLjsh5SOcd4bLsfUg2RcEXrFMJl1DGgdHy2aCfsIA/cr/1JM0xcB2GZji8g==} + peerDependencies: + zod: ^3.24.1 + + zod@3.24.4: + resolution: {integrity: sha512-OdqJE9UDRPwWsrHjLN2F8bPxvwJBK22EHLWtanu0LSYr5YqzsaaW3RMgmjwr8Rypg5k+meEJdSPXJZXE/yqOMg==} + +snapshots: + + '@babel/code-frame@7.27.1': + dependencies: + '@babel/helper-validator-identifier': 7.27.1 + js-tokens: 4.0.0 + picocolors: 1.1.1 + + '@babel/generator@7.27.1': + dependencies: + '@babel/parser': 7.27.1 + '@babel/types': 7.27.1 + '@jridgewell/gen-mapping': 0.3.8 + '@jridgewell/trace-mapping': 0.3.25 + jsesc: 3.1.0 + + '@babel/helper-module-imports@7.27.1': + dependencies: + '@babel/traverse': 7.27.1 + '@babel/types': 7.27.1 + transitivePeerDependencies: + - supports-color + + '@babel/helper-string-parser@7.27.1': {} + + '@babel/helper-validator-identifier@7.27.1': {} + + '@babel/parser@7.27.1': + dependencies: + '@babel/types': 7.27.1 + + '@babel/runtime@7.27.1': {} + + '@babel/template@7.27.1': + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/parser': 7.27.1 + '@babel/types': 7.27.1 + + '@babel/traverse@7.27.1': + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/generator': 7.27.1 + '@babel/parser': 7.27.1 + '@babel/template': 7.27.1 + '@babel/types': 7.27.1 + debug: 4.4.0 + globals: 11.12.0 + transitivePeerDependencies: + - supports-color + + '@babel/types@7.27.1': + dependencies: + '@babel/helper-string-parser': 7.27.1 + '@babel/helper-validator-identifier': 7.27.1 + + '@emnapi/core@1.4.3': + dependencies: + '@emnapi/wasi-threads': 1.0.2 + tslib: 2.8.1 + optional: true + + '@emnapi/runtime@1.4.3': + dependencies: + tslib: 2.8.1 + optional: true + + '@emnapi/wasi-threads@1.0.2': + dependencies: + tslib: 2.8.1 + optional: true + + '@emotion/babel-plugin@11.13.5': + dependencies: + '@babel/helper-module-imports': 7.27.1 + '@babel/runtime': 7.27.1 + '@emotion/hash': 0.9.2 + '@emotion/memoize': 0.9.0 + '@emotion/serialize': 1.3.3 + babel-plugin-macros: 3.1.0 + convert-source-map: 1.9.0 + escape-string-regexp: 4.0.0 + find-root: 1.1.0 + source-map: 0.5.7 + stylis: 4.2.0 + transitivePeerDependencies: + - supports-color + + '@emotion/cache@11.14.0': + dependencies: + '@emotion/memoize': 0.9.0 + '@emotion/sheet': 1.4.0 + '@emotion/utils': 1.4.2 + '@emotion/weak-memoize': 0.4.0 + stylis: 4.2.0 + + '@emotion/hash@0.9.2': {} + + '@emotion/is-prop-valid@1.3.1': + dependencies: + '@emotion/memoize': 0.9.0 + + '@emotion/memoize@0.9.0': {} + + '@emotion/react@11.14.0(@types/react@19.1.2)(react@19.1.0)': + dependencies: + '@babel/runtime': 7.27.1 + '@emotion/babel-plugin': 11.13.5 + '@emotion/cache': 11.14.0 + '@emotion/serialize': 1.3.3 + '@emotion/use-insertion-effect-with-fallbacks': 1.2.0(react@19.1.0) + '@emotion/utils': 1.4.2 + '@emotion/weak-memoize': 0.4.0 + hoist-non-react-statics: 3.3.2 + react: 19.1.0 + optionalDependencies: + '@types/react': 19.1.2 + transitivePeerDependencies: + - supports-color + + '@emotion/serialize@1.3.3': + dependencies: + '@emotion/hash': 0.9.2 + '@emotion/memoize': 0.9.0 + '@emotion/unitless': 0.10.0 + '@emotion/utils': 1.4.2 + csstype: 3.1.3 + + '@emotion/sheet@1.4.0': {} + + '@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.1.2)(react@19.1.0))(@types/react@19.1.2)(react@19.1.0)': + dependencies: + '@babel/runtime': 7.27.1 + '@emotion/babel-plugin': 11.13.5 + '@emotion/is-prop-valid': 1.3.1 + '@emotion/react': 11.14.0(@types/react@19.1.2)(react@19.1.0) + '@emotion/serialize': 1.3.3 + '@emotion/use-insertion-effect-with-fallbacks': 1.2.0(react@19.1.0) + '@emotion/utils': 1.4.2 + react: 19.1.0 + optionalDependencies: + '@types/react': 19.1.2 + transitivePeerDependencies: + - supports-color + + '@emotion/unitless@0.10.0': {} + + '@emotion/use-insertion-effect-with-fallbacks@1.2.0(react@19.1.0)': + dependencies: + react: 19.1.0 + + '@emotion/utils@1.4.2': {} + + '@emotion/weak-memoize@0.4.0': {} + + '@eslint-community/eslint-utils@4.7.0(eslint@9.26.0)': + dependencies: + eslint: 9.26.0 + eslint-visitor-keys: 3.4.3 + + '@eslint-community/regexpp@4.12.1': {} + + '@eslint/config-array@0.20.0': + dependencies: + '@eslint/object-schema': 2.1.6 + debug: 4.4.0 + minimatch: 3.1.2 + transitivePeerDependencies: + - supports-color + + '@eslint/config-helpers@0.2.2': {} + + '@eslint/core@0.13.0': + dependencies: + '@types/json-schema': 7.0.15 + + '@eslint/eslintrc@3.3.1': + dependencies: + ajv: 6.12.6 + debug: 4.4.0 + espree: 10.3.0 + globals: 14.0.0 + ignore: 5.3.2 + import-fresh: 3.3.1 + js-yaml: 4.1.0 + minimatch: 3.1.2 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + + '@eslint/js@9.26.0': {} + + '@eslint/object-schema@2.1.6': {} + + '@eslint/plugin-kit@0.2.8': + dependencies: + '@eslint/core': 0.13.0 + levn: 0.4.1 + + '@hookform/resolvers@5.0.1(react-hook-form@7.56.2(react@19.1.0))': + dependencies: + '@standard-schema/utils': 0.3.0 + react-hook-form: 7.56.2(react@19.1.0) + + '@humanfs/core@0.19.1': {} + + '@humanfs/node@0.16.6': + dependencies: + '@humanfs/core': 0.19.1 + '@humanwhocodes/retry': 0.3.1 + + '@humanwhocodes/module-importer@1.0.1': {} + + '@humanwhocodes/retry@0.3.1': {} + + '@humanwhocodes/retry@0.4.2': {} + + '@img/sharp-darwin-arm64@0.34.1': + optionalDependencies: + '@img/sharp-libvips-darwin-arm64': 1.1.0 + optional: true + + '@img/sharp-darwin-x64@0.34.1': + optionalDependencies: + '@img/sharp-libvips-darwin-x64': 1.1.0 + optional: true + + '@img/sharp-libvips-darwin-arm64@1.1.0': + optional: true + + '@img/sharp-libvips-darwin-x64@1.1.0': + optional: true + + '@img/sharp-libvips-linux-arm64@1.1.0': + optional: true + + '@img/sharp-libvips-linux-arm@1.1.0': + optional: true + + '@img/sharp-libvips-linux-ppc64@1.1.0': + optional: true + + '@img/sharp-libvips-linux-s390x@1.1.0': + optional: true + + '@img/sharp-libvips-linux-x64@1.1.0': + optional: true + + '@img/sharp-libvips-linuxmusl-arm64@1.1.0': + optional: true + + '@img/sharp-libvips-linuxmusl-x64@1.1.0': + optional: true + + '@img/sharp-linux-arm64@0.34.1': + optionalDependencies: + '@img/sharp-libvips-linux-arm64': 1.1.0 + optional: true + + '@img/sharp-linux-arm@0.34.1': + optionalDependencies: + '@img/sharp-libvips-linux-arm': 1.1.0 + optional: true + + '@img/sharp-linux-s390x@0.34.1': + optionalDependencies: + '@img/sharp-libvips-linux-s390x': 1.1.0 + optional: true + + '@img/sharp-linux-x64@0.34.1': + optionalDependencies: + '@img/sharp-libvips-linux-x64': 1.1.0 + optional: true + + '@img/sharp-linuxmusl-arm64@0.34.1': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-arm64': 1.1.0 + optional: true + + '@img/sharp-linuxmusl-x64@0.34.1': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-x64': 1.1.0 + optional: true + + '@img/sharp-wasm32@0.34.1': + dependencies: + '@emnapi/runtime': 1.4.3 + optional: true + + '@img/sharp-win32-ia32@0.34.1': + optional: true + + '@img/sharp-win32-x64@0.34.1': + optional: true + + '@jridgewell/gen-mapping@0.3.8': + dependencies: + '@jridgewell/set-array': 1.2.1 + '@jridgewell/sourcemap-codec': 1.5.0 + '@jridgewell/trace-mapping': 0.3.25 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/set-array@1.2.1': {} + + '@jridgewell/sourcemap-codec@1.5.0': {} + + '@jridgewell/trace-mapping@0.3.25': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.0 + + '@modelcontextprotocol/sdk@1.11.0': + dependencies: + content-type: 1.0.5 + cors: 2.8.5 + cross-spawn: 7.0.6 + eventsource: 3.0.6 + express: 5.1.0 + express-rate-limit: 7.5.0(express@5.1.0) + pkce-challenge: 5.0.0 + raw-body: 3.0.0 + zod: 3.24.4 + zod-to-json-schema: 3.24.5(zod@3.24.4) + transitivePeerDependencies: + - supports-color + + '@mui/core-downloads-tracker@7.0.2': {} + + '@mui/icons-material@7.0.2(@mui/material@7.0.2(@emotion/react@11.14.0(@types/react@19.1.2)(react@19.1.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.1.2)(react@19.1.0))(@types/react@19.1.2)(react@19.1.0))(@types/react@19.1.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(@types/react@19.1.2)(react@19.1.0)': + dependencies: + '@babel/runtime': 7.27.1 + '@mui/material': 7.0.2(@emotion/react@11.14.0(@types/react@19.1.2)(react@19.1.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.1.2)(react@19.1.0))(@types/react@19.1.2)(react@19.1.0))(@types/react@19.1.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + react: 19.1.0 + optionalDependencies: + '@types/react': 19.1.2 + + '@mui/material-nextjs@7.0.2(@emotion/cache@11.14.0)(@emotion/react@11.14.0(@types/react@19.1.2)(react@19.1.0))(@types/react@19.1.2)(next@15.3.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.87.0))(react@19.1.0)': + dependencies: + '@babel/runtime': 7.27.1 + '@emotion/react': 11.14.0(@types/react@19.1.2)(react@19.1.0) + next: 15.3.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.87.0) + react: 19.1.0 + optionalDependencies: + '@emotion/cache': 11.14.0 + '@types/react': 19.1.2 + + '@mui/material@7.0.2(@emotion/react@11.14.0(@types/react@19.1.2)(react@19.1.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.1.2)(react@19.1.0))(@types/react@19.1.2)(react@19.1.0))(@types/react@19.1.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + dependencies: + '@babel/runtime': 7.27.1 + '@mui/core-downloads-tracker': 7.0.2 + '@mui/system': 7.0.2(@emotion/react@11.14.0(@types/react@19.1.2)(react@19.1.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.1.2)(react@19.1.0))(@types/react@19.1.2)(react@19.1.0))(@types/react@19.1.2)(react@19.1.0) + '@mui/types': 7.4.1(@types/react@19.1.2) + '@mui/utils': 7.0.2(@types/react@19.1.2)(react@19.1.0) + '@popperjs/core': 2.11.8 + '@types/react-transition-group': 4.4.12(@types/react@19.1.2) + clsx: 2.1.1 + csstype: 3.1.3 + prop-types: 15.8.1 + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + react-is: 19.1.0 + react-transition-group: 4.4.5(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + optionalDependencies: + '@emotion/react': 11.14.0(@types/react@19.1.2)(react@19.1.0) + '@emotion/styled': 11.14.0(@emotion/react@11.14.0(@types/react@19.1.2)(react@19.1.0))(@types/react@19.1.2)(react@19.1.0) + '@types/react': 19.1.2 + + '@mui/private-theming@7.0.2(@types/react@19.1.2)(react@19.1.0)': + dependencies: + '@babel/runtime': 7.27.1 + '@mui/utils': 7.0.2(@types/react@19.1.2)(react@19.1.0) + prop-types: 15.8.1 + react: 19.1.0 + optionalDependencies: + '@types/react': 19.1.2 + + '@mui/styled-engine@7.0.2(@emotion/react@11.14.0(@types/react@19.1.2)(react@19.1.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.1.2)(react@19.1.0))(@types/react@19.1.2)(react@19.1.0))(react@19.1.0)': + dependencies: + '@babel/runtime': 7.27.1 + '@emotion/cache': 11.14.0 + '@emotion/serialize': 1.3.3 + '@emotion/sheet': 1.4.0 + csstype: 3.1.3 + prop-types: 15.8.1 + react: 19.1.0 + optionalDependencies: + '@emotion/react': 11.14.0(@types/react@19.1.2)(react@19.1.0) + '@emotion/styled': 11.14.0(@emotion/react@11.14.0(@types/react@19.1.2)(react@19.1.0))(@types/react@19.1.2)(react@19.1.0) + + '@mui/system@7.0.2(@emotion/react@11.14.0(@types/react@19.1.2)(react@19.1.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.1.2)(react@19.1.0))(@types/react@19.1.2)(react@19.1.0))(@types/react@19.1.2)(react@19.1.0)': + dependencies: + '@babel/runtime': 7.27.1 + '@mui/private-theming': 7.0.2(@types/react@19.1.2)(react@19.1.0) + '@mui/styled-engine': 7.0.2(@emotion/react@11.14.0(@types/react@19.1.2)(react@19.1.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.1.2)(react@19.1.0))(@types/react@19.1.2)(react@19.1.0))(react@19.1.0) + '@mui/types': 7.4.1(@types/react@19.1.2) + '@mui/utils': 7.0.2(@types/react@19.1.2)(react@19.1.0) + clsx: 2.1.1 + csstype: 3.1.3 + prop-types: 15.8.1 + react: 19.1.0 + optionalDependencies: + '@emotion/react': 11.14.0(@types/react@19.1.2)(react@19.1.0) + '@emotion/styled': 11.14.0(@emotion/react@11.14.0(@types/react@19.1.2)(react@19.1.0))(@types/react@19.1.2)(react@19.1.0) + '@types/react': 19.1.2 + + '@mui/types@7.4.1(@types/react@19.1.2)': + dependencies: + '@babel/runtime': 7.27.1 + optionalDependencies: + '@types/react': 19.1.2 + + '@mui/utils@7.0.2(@types/react@19.1.2)(react@19.1.0)': + dependencies: + '@babel/runtime': 7.27.1 + '@mui/types': 7.4.1(@types/react@19.1.2) + '@types/prop-types': 15.7.14 + clsx: 2.1.1 + prop-types: 15.8.1 + react: 19.1.0 + react-is: 19.1.0 + optionalDependencies: + '@types/react': 19.1.2 + + '@napi-rs/wasm-runtime@0.2.9': + dependencies: + '@emnapi/core': 1.4.3 + '@emnapi/runtime': 1.4.3 + '@tybys/wasm-util': 0.9.0 + optional: true + + '@next/env@15.3.1': {} + + '@next/eslint-plugin-next@15.3.1': + dependencies: + fast-glob: 3.3.1 + + '@next/swc-darwin-arm64@15.3.1': + optional: true + + '@next/swc-darwin-x64@15.3.1': + optional: true + + '@next/swc-linux-arm64-gnu@15.3.1': + optional: true + + '@next/swc-linux-arm64-musl@15.3.1': + optional: true + + '@next/swc-linux-x64-gnu@15.3.1': + optional: true + + '@next/swc-linux-x64-musl@15.3.1': + optional: true + + '@next/swc-win32-arm64-msvc@15.3.1': + optional: true + + '@next/swc-win32-x64-msvc@15.3.1': + optional: true + + '@nodelib/fs.scandir@2.1.5': + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + + '@nodelib/fs.stat@2.0.5': {} + + '@nodelib/fs.walk@1.2.8': + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.19.1 + + '@nolyfill/is-core-module@1.0.39': {} + + '@parcel/watcher-android-arm64@2.5.1': + optional: true + + '@parcel/watcher-darwin-arm64@2.5.1': + optional: true + + '@parcel/watcher-darwin-x64@2.5.1': + optional: true + + '@parcel/watcher-freebsd-x64@2.5.1': + optional: true + + '@parcel/watcher-linux-arm-glibc@2.5.1': + optional: true + + '@parcel/watcher-linux-arm-musl@2.5.1': + optional: true + + '@parcel/watcher-linux-arm64-glibc@2.5.1': + optional: true + + '@parcel/watcher-linux-arm64-musl@2.5.1': + optional: true + + '@parcel/watcher-linux-x64-glibc@2.5.1': + optional: true + + '@parcel/watcher-linux-x64-musl@2.5.1': + optional: true + + '@parcel/watcher-win32-arm64@2.5.1': + optional: true + + '@parcel/watcher-win32-ia32@2.5.1': + optional: true + + '@parcel/watcher-win32-x64@2.5.1': + optional: true + + '@parcel/watcher@2.5.1': + dependencies: + detect-libc: 1.0.3 + is-glob: 4.0.3 + micromatch: 4.0.8 + node-addon-api: 7.1.1 + optionalDependencies: + '@parcel/watcher-android-arm64': 2.5.1 + '@parcel/watcher-darwin-arm64': 2.5.1 + '@parcel/watcher-darwin-x64': 2.5.1 + '@parcel/watcher-freebsd-x64': 2.5.1 + '@parcel/watcher-linux-arm-glibc': 2.5.1 + '@parcel/watcher-linux-arm-musl': 2.5.1 + '@parcel/watcher-linux-arm64-glibc': 2.5.1 + '@parcel/watcher-linux-arm64-musl': 2.5.1 + '@parcel/watcher-linux-x64-glibc': 2.5.1 + '@parcel/watcher-linux-x64-musl': 2.5.1 + '@parcel/watcher-win32-arm64': 2.5.1 + '@parcel/watcher-win32-ia32': 2.5.1 + '@parcel/watcher-win32-x64': 2.5.1 + optional: true + + '@popperjs/core@2.11.8': {} + + '@rtsao/scc@1.1.0': {} + + '@rushstack/eslint-patch@1.11.0': {} + + '@socket.io/component-emitter@3.1.2': {} + + '@standard-schema/utils@0.3.0': {} + + '@swc/counter@0.1.3': {} + + '@swc/helpers@0.5.15': + dependencies: + tslib: 2.8.1 + + '@tybys/wasm-util@0.9.0': + dependencies: + tslib: 2.8.1 + optional: true + + '@types/estree@1.0.7': {} + + '@types/json-schema@7.0.15': {} + + '@types/json5@0.0.29': {} + + '@types/parse-json@4.0.2': {} + + '@types/prop-types@15.7.14': {} + + '@types/react-transition-group@4.4.12(@types/react@19.1.2)': + dependencies: + '@types/react': 19.1.2 + + '@types/react@19.1.2': + dependencies: + csstype: 3.1.3 + + '@typescript-eslint/eslint-plugin@8.31.1(@typescript-eslint/parser@8.31.1(eslint@9.26.0)(typescript@5.8.3))(eslint@9.26.0)(typescript@5.8.3)': + dependencies: + '@eslint-community/regexpp': 4.12.1 + '@typescript-eslint/parser': 8.31.1(eslint@9.26.0)(typescript@5.8.3) + '@typescript-eslint/scope-manager': 8.31.1 + '@typescript-eslint/type-utils': 8.31.1(eslint@9.26.0)(typescript@5.8.3) + '@typescript-eslint/utils': 8.31.1(eslint@9.26.0)(typescript@5.8.3) + '@typescript-eslint/visitor-keys': 8.31.1 + eslint: 9.26.0 + graphemer: 1.4.0 + ignore: 5.3.2 + natural-compare: 1.4.0 + ts-api-utils: 2.1.0(typescript@5.8.3) + typescript: 5.8.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/parser@8.31.1(eslint@9.26.0)(typescript@5.8.3)': + dependencies: + '@typescript-eslint/scope-manager': 8.31.1 + '@typescript-eslint/types': 8.31.1 + '@typescript-eslint/typescript-estree': 8.31.1(typescript@5.8.3) + '@typescript-eslint/visitor-keys': 8.31.1 + debug: 4.4.0 + eslint: 9.26.0 + typescript: 5.8.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/scope-manager@8.31.1': + dependencies: + '@typescript-eslint/types': 8.31.1 + '@typescript-eslint/visitor-keys': 8.31.1 + + '@typescript-eslint/type-utils@8.31.1(eslint@9.26.0)(typescript@5.8.3)': + dependencies: + '@typescript-eslint/typescript-estree': 8.31.1(typescript@5.8.3) + '@typescript-eslint/utils': 8.31.1(eslint@9.26.0)(typescript@5.8.3) + debug: 4.4.0 + eslint: 9.26.0 + ts-api-utils: 2.1.0(typescript@5.8.3) + typescript: 5.8.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/types@8.31.1': {} + + '@typescript-eslint/typescript-estree@8.31.1(typescript@5.8.3)': + dependencies: + '@typescript-eslint/types': 8.31.1 + '@typescript-eslint/visitor-keys': 8.31.1 + debug: 4.4.0 + fast-glob: 3.3.3 + is-glob: 4.0.3 + minimatch: 9.0.5 + semver: 7.7.1 + ts-api-utils: 2.1.0(typescript@5.8.3) + typescript: 5.8.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/utils@8.31.1(eslint@9.26.0)(typescript@5.8.3)': + dependencies: + '@eslint-community/eslint-utils': 4.7.0(eslint@9.26.0) + '@typescript-eslint/scope-manager': 8.31.1 + '@typescript-eslint/types': 8.31.1 + '@typescript-eslint/typescript-estree': 8.31.1(typescript@5.8.3) + eslint: 9.26.0 + typescript: 5.8.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/visitor-keys@8.31.1': + dependencies: + '@typescript-eslint/types': 8.31.1 + eslint-visitor-keys: 4.2.0 + + '@unrs/resolver-binding-darwin-arm64@1.7.2': + optional: true + + '@unrs/resolver-binding-darwin-x64@1.7.2': + optional: true + + '@unrs/resolver-binding-freebsd-x64@1.7.2': + optional: true + + '@unrs/resolver-binding-linux-arm-gnueabihf@1.7.2': + optional: true + + '@unrs/resolver-binding-linux-arm-musleabihf@1.7.2': + optional: true + + '@unrs/resolver-binding-linux-arm64-gnu@1.7.2': + optional: true + + '@unrs/resolver-binding-linux-arm64-musl@1.7.2': + optional: true + + '@unrs/resolver-binding-linux-ppc64-gnu@1.7.2': + optional: true + + '@unrs/resolver-binding-linux-riscv64-gnu@1.7.2': + optional: true + + '@unrs/resolver-binding-linux-riscv64-musl@1.7.2': + optional: true + + '@unrs/resolver-binding-linux-s390x-gnu@1.7.2': + optional: true + + '@unrs/resolver-binding-linux-x64-gnu@1.7.2': + optional: true + + '@unrs/resolver-binding-linux-x64-musl@1.7.2': + optional: true + + '@unrs/resolver-binding-wasm32-wasi@1.7.2': + dependencies: + '@napi-rs/wasm-runtime': 0.2.9 + optional: true + + '@unrs/resolver-binding-win32-arm64-msvc@1.7.2': + optional: true + + '@unrs/resolver-binding-win32-ia32-msvc@1.7.2': + optional: true + + '@unrs/resolver-binding-win32-x64-msvc@1.7.2': + optional: true + + accepts@2.0.0: + dependencies: + mime-types: 3.0.1 + negotiator: 1.0.0 + + acorn-jsx@5.3.2(acorn@8.14.1): + dependencies: + acorn: 8.14.1 + + acorn@8.14.1: {} + + ajv@6.12.6: + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + + ansi-escapes@4.3.2: + dependencies: + type-fest: 0.21.3 + + ansi-regex@5.0.1: {} + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + argparse@2.0.1: {} + + aria-query@5.3.2: {} + + array-buffer-byte-length@1.0.2: + dependencies: + call-bound: 1.0.4 + is-array-buffer: 3.0.5 + + array-includes@3.1.8: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.23.9 + es-object-atoms: 1.1.1 + get-intrinsic: 1.3.0 + is-string: 1.1.1 + + array.prototype.findlast@1.2.5: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.23.9 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + es-shim-unscopables: 1.1.0 + + array.prototype.findlastindex@1.2.6: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-abstract: 1.23.9 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + es-shim-unscopables: 1.1.0 + + array.prototype.flat@1.3.3: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.23.9 + es-shim-unscopables: 1.1.0 + + array.prototype.flatmap@1.3.3: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.23.9 + es-shim-unscopables: 1.1.0 + + array.prototype.tosorted@1.1.4: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.23.9 + es-errors: 1.3.0 + es-shim-unscopables: 1.1.0 + + arraybuffer.prototype.slice@1.0.4: + dependencies: + array-buffer-byte-length: 1.0.2 + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.23.9 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + is-array-buffer: 3.0.5 + + ast-types-flow@0.0.8: {} + + async-function@1.0.0: {} + + asynckit@0.4.0: {} + + available-typed-arrays@1.0.7: + dependencies: + possible-typed-array-names: 1.1.0 + + axe-core@4.10.3: {} + + axios@1.9.0: + dependencies: + follow-redirects: 1.15.9 + form-data: 4.0.2 + proxy-from-env: 1.1.0 + transitivePeerDependencies: + - debug + + axobject-query@4.1.0: {} + + babel-plugin-macros@3.1.0: + dependencies: + '@babel/runtime': 7.27.1 + cosmiconfig: 7.1.0 + resolve: 1.22.10 + + balanced-match@1.0.2: {} + + base64-js@1.5.1: {} + + bl@4.1.0: + dependencies: + buffer: 5.7.1 + inherits: 2.0.4 + readable-stream: 3.6.2 + + body-parser@2.2.0: + dependencies: + bytes: 3.1.2 + content-type: 1.0.5 + debug: 4.4.0 + http-errors: 2.0.0 + iconv-lite: 0.6.3 + on-finished: 2.4.1 + qs: 6.14.0 + raw-body: 3.0.0 + type-is: 2.0.1 + transitivePeerDependencies: + - supports-color + + brace-expansion@1.1.11: + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + + brace-expansion@2.0.1: + dependencies: + balanced-match: 1.0.2 + + braces@3.0.3: + dependencies: + fill-range: 7.1.1 + + buffer@5.7.1: + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + + busboy@1.6.0: + dependencies: + streamsearch: 1.1.0 + + bytes@3.1.2: {} + + call-bind-apply-helpers@1.0.2: + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + + call-bind@1.0.8: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + get-intrinsic: 1.3.0 + set-function-length: 1.2.2 + + call-bound@1.0.4: + dependencies: + call-bind-apply-helpers: 1.0.2 + get-intrinsic: 1.3.0 + + callsites@3.1.0: {} + + caniuse-lite@1.0.30001717: {} + + chalk@4.1.2: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + + chardet@0.7.0: {} + + chokidar@4.0.3: + dependencies: + readdirp: 4.1.2 + + cli-cursor@3.1.0: + dependencies: + restore-cursor: 3.1.0 + + cli-spinners@2.9.2: {} + + cli-width@3.0.0: {} + + client-only@0.0.1: {} + + clone@1.0.4: {} + + clsx@2.1.1: {} + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.4: {} + + color-string@1.9.1: + dependencies: + color-name: 1.1.4 + simple-swizzle: 0.2.2 + optional: true + + color@4.2.3: + dependencies: + color-convert: 2.0.1 + color-string: 1.9.1 + optional: true + + combined-stream@1.0.8: + dependencies: + delayed-stream: 1.0.0 + + commander@7.2.0: {} + + concat-map@0.0.1: {} + + content-disposition@1.0.0: + dependencies: + safe-buffer: 5.2.1 + + content-type@1.0.5: {} + + convert-source-map@1.9.0: {} + + cookie-signature@1.2.2: {} + + cookie@0.7.2: {} + + cors@2.8.5: + dependencies: + object-assign: 4.1.1 + vary: 1.1.2 + + cosmiconfig@7.1.0: + dependencies: + '@types/parse-json': 4.0.2 + import-fresh: 3.3.1 + parse-json: 5.2.0 + path-type: 4.0.0 + yaml: 1.10.2 + + cross-spawn@7.0.6: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + + cssjanus@2.3.0: {} + + csstype@3.1.3: {} + + damerau-levenshtein@1.0.8: {} + + data-view-buffer@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-data-view: 1.0.2 + + data-view-byte-length@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-data-view: 1.0.2 + + data-view-byte-offset@1.0.1: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-data-view: 1.0.2 + + debug@3.2.7: + dependencies: + ms: 2.1.3 + + debug@4.3.7: + dependencies: + ms: 2.1.3 + + debug@4.4.0: + dependencies: + ms: 2.1.3 + + deep-is@0.1.4: {} + + defaults@1.0.4: + dependencies: + clone: 1.0.4 + + define-data-property@1.1.4: + dependencies: + es-define-property: 1.0.1 + es-errors: 1.3.0 + gopd: 1.2.0 + + define-properties@1.2.1: + dependencies: + define-data-property: 1.1.4 + has-property-descriptors: 1.0.2 + object-keys: 1.1.1 + + delayed-stream@1.0.0: {} + + depd@2.0.0: {} + + dequal@2.0.3: {} + + detect-libc@1.0.3: + optional: true + + detect-libc@2.0.4: + optional: true + + doctrine@2.1.0: + dependencies: + esutils: 2.0.3 + + dom-helpers@5.2.1: + dependencies: + '@babel/runtime': 7.27.1 + csstype: 3.1.3 + + dunder-proto@1.0.1: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-errors: 1.3.0 + gopd: 1.2.0 + + ee-first@1.1.1: {} + + emoji-regex@8.0.0: {} + + emoji-regex@9.2.2: {} + + encodeurl@2.0.0: {} + + engine.io-client@6.6.3: + dependencies: + '@socket.io/component-emitter': 3.1.2 + debug: 4.3.7 + engine.io-parser: 5.2.3 + ws: 8.17.1 + xmlhttprequest-ssl: 2.1.2 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + engine.io-parser@5.2.3: {} + + error-ex@1.3.2: + dependencies: + is-arrayish: 0.2.1 + + es-abstract@1.23.9: + dependencies: + array-buffer-byte-length: 1.0.2 + arraybuffer.prototype.slice: 1.0.4 + available-typed-arrays: 1.0.7 + call-bind: 1.0.8 + call-bound: 1.0.4 + data-view-buffer: 1.0.2 + data-view-byte-length: 1.0.2 + data-view-byte-offset: 1.0.1 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + es-set-tostringtag: 2.1.0 + es-to-primitive: 1.3.0 + function.prototype.name: 1.1.8 + get-intrinsic: 1.3.0 + get-proto: 1.0.1 + get-symbol-description: 1.1.0 + globalthis: 1.0.4 + gopd: 1.2.0 + has-property-descriptors: 1.0.2 + has-proto: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.2 + internal-slot: 1.1.0 + is-array-buffer: 3.0.5 + is-callable: 1.2.7 + is-data-view: 1.0.2 + is-regex: 1.2.1 + is-shared-array-buffer: 1.0.4 + is-string: 1.1.1 + is-typed-array: 1.1.15 + is-weakref: 1.1.1 + math-intrinsics: 1.1.0 + object-inspect: 1.13.4 + object-keys: 1.1.1 + object.assign: 4.1.7 + own-keys: 1.0.1 + regexp.prototype.flags: 1.5.4 + safe-array-concat: 1.1.3 + safe-push-apply: 1.0.0 + safe-regex-test: 1.1.0 + set-proto: 1.0.0 + string.prototype.trim: 1.2.10 + string.prototype.trimend: 1.0.9 + string.prototype.trimstart: 1.0.8 + typed-array-buffer: 1.0.3 + typed-array-byte-length: 1.0.3 + typed-array-byte-offset: 1.0.4 + typed-array-length: 1.0.7 + unbox-primitive: 1.1.0 + which-typed-array: 1.1.19 + + es-define-property@1.0.1: {} + + es-errors@1.3.0: {} + + es-iterator-helpers@1.2.1: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-abstract: 1.23.9 + es-errors: 1.3.0 + es-set-tostringtag: 2.1.0 + function-bind: 1.1.2 + get-intrinsic: 1.3.0 + globalthis: 1.0.4 + gopd: 1.2.0 + has-property-descriptors: 1.0.2 + has-proto: 1.2.0 + has-symbols: 1.1.0 + internal-slot: 1.1.0 + iterator.prototype: 1.1.5 + safe-array-concat: 1.1.3 + + es-object-atoms@1.1.1: + dependencies: + es-errors: 1.3.0 + + es-set-tostringtag@2.1.0: + dependencies: + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + has-tostringtag: 1.0.2 + hasown: 2.0.2 + + es-shim-unscopables@1.1.0: + dependencies: + hasown: 2.0.2 + + es-to-primitive@1.3.0: + dependencies: + is-callable: 1.2.7 + is-date-object: 1.1.0 + is-symbol: 1.1.1 + + escape-html@1.0.3: {} + + escape-string-regexp@1.0.5: {} + + escape-string-regexp@4.0.0: {} + + eslint-config-next@15.3.1(eslint@9.26.0)(typescript@5.8.3): + dependencies: + '@next/eslint-plugin-next': 15.3.1 + '@rushstack/eslint-patch': 1.11.0 + '@typescript-eslint/eslint-plugin': 8.31.1(@typescript-eslint/parser@8.31.1(eslint@9.26.0)(typescript@5.8.3))(eslint@9.26.0)(typescript@5.8.3) + '@typescript-eslint/parser': 8.31.1(eslint@9.26.0)(typescript@5.8.3) + eslint: 9.26.0 + eslint-import-resolver-node: 0.3.9 + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.31.0)(eslint@9.26.0) + eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.31.1(eslint@9.26.0)(typescript@5.8.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.26.0) + eslint-plugin-jsx-a11y: 6.10.2(eslint@9.26.0) + eslint-plugin-react: 7.37.5(eslint@9.26.0) + eslint-plugin-react-hooks: 5.2.0(eslint@9.26.0) + optionalDependencies: + typescript: 5.8.3 + transitivePeerDependencies: + - eslint-import-resolver-webpack + - eslint-plugin-import-x + - supports-color + + eslint-import-resolver-node@0.3.9: + dependencies: + debug: 3.2.7 + is-core-module: 2.16.1 + resolve: 1.22.10 + transitivePeerDependencies: + - supports-color + + eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.31.0)(eslint@9.26.0): + dependencies: + '@nolyfill/is-core-module': 1.0.39 + debug: 4.4.0 + eslint: 9.26.0 + get-tsconfig: 4.10.0 + is-bun-module: 2.0.0 + stable-hash: 0.0.5 + tinyglobby: 0.2.13 + unrs-resolver: 1.7.2 + optionalDependencies: + eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.31.1(eslint@9.26.0)(typescript@5.8.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.26.0) + transitivePeerDependencies: + - supports-color + + eslint-module-utils@2.12.0(@typescript-eslint/parser@8.31.1(eslint@9.26.0)(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.26.0): + dependencies: + debug: 3.2.7 + optionalDependencies: + '@typescript-eslint/parser': 8.31.1(eslint@9.26.0)(typescript@5.8.3) + eslint: 9.26.0 + eslint-import-resolver-node: 0.3.9 + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.31.0)(eslint@9.26.0) + transitivePeerDependencies: + - supports-color + + eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.31.1(eslint@9.26.0)(typescript@5.8.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.26.0): + dependencies: + '@rtsao/scc': 1.1.0 + array-includes: 3.1.8 + array.prototype.findlastindex: 1.2.6 + array.prototype.flat: 1.3.3 + array.prototype.flatmap: 1.3.3 + debug: 3.2.7 + doctrine: 2.1.0 + eslint: 9.26.0 + eslint-import-resolver-node: 0.3.9 + eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.31.1(eslint@9.26.0)(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.26.0) + hasown: 2.0.2 + is-core-module: 2.16.1 + is-glob: 4.0.3 + minimatch: 3.1.2 + object.fromentries: 2.0.8 + object.groupby: 1.0.3 + object.values: 1.2.1 + semver: 6.3.1 + string.prototype.trimend: 1.0.9 + tsconfig-paths: 3.15.0 + optionalDependencies: + '@typescript-eslint/parser': 8.31.1(eslint@9.26.0)(typescript@5.8.3) + transitivePeerDependencies: + - eslint-import-resolver-typescript + - eslint-import-resolver-webpack + - supports-color + + eslint-plugin-jsx-a11y@6.10.2(eslint@9.26.0): + dependencies: + aria-query: 5.3.2 + array-includes: 3.1.8 + array.prototype.flatmap: 1.3.3 + ast-types-flow: 0.0.8 + axe-core: 4.10.3 + axobject-query: 4.1.0 + damerau-levenshtein: 1.0.8 + emoji-regex: 9.2.2 + eslint: 9.26.0 + hasown: 2.0.2 + jsx-ast-utils: 3.3.5 + language-tags: 1.0.9 + minimatch: 3.1.2 + object.fromentries: 2.0.8 + safe-regex-test: 1.1.0 + string.prototype.includes: 2.0.1 + + eslint-plugin-react-hooks@5.2.0(eslint@9.26.0): + dependencies: + eslint: 9.26.0 + + eslint-plugin-react@7.37.5(eslint@9.26.0): + dependencies: + array-includes: 3.1.8 + array.prototype.findlast: 1.2.5 + array.prototype.flatmap: 1.3.3 + array.prototype.tosorted: 1.1.4 + doctrine: 2.1.0 + es-iterator-helpers: 1.2.1 + eslint: 9.26.0 + estraverse: 5.3.0 + hasown: 2.0.2 + jsx-ast-utils: 3.3.5 + minimatch: 3.1.2 + object.entries: 1.1.9 + object.fromentries: 2.0.8 + object.values: 1.2.1 + prop-types: 15.8.1 + resolve: 2.0.0-next.5 + semver: 6.3.1 + string.prototype.matchall: 4.0.12 + string.prototype.repeat: 1.0.0 + + eslint-scope@8.3.0: + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + + eslint-visitor-keys@3.4.3: {} + + eslint-visitor-keys@4.2.0: {} + + eslint@9.26.0: + dependencies: + '@eslint-community/eslint-utils': 4.7.0(eslint@9.26.0) + '@eslint-community/regexpp': 4.12.1 + '@eslint/config-array': 0.20.0 + '@eslint/config-helpers': 0.2.2 + '@eslint/core': 0.13.0 + '@eslint/eslintrc': 3.3.1 + '@eslint/js': 9.26.0 + '@eslint/plugin-kit': 0.2.8 + '@humanfs/node': 0.16.6 + '@humanwhocodes/module-importer': 1.0.1 + '@humanwhocodes/retry': 0.4.2 + '@modelcontextprotocol/sdk': 1.11.0 + '@types/estree': 1.0.7 + '@types/json-schema': 7.0.15 + ajv: 6.12.6 + chalk: 4.1.2 + cross-spawn: 7.0.6 + debug: 4.4.0 + escape-string-regexp: 4.0.0 + eslint-scope: 8.3.0 + eslint-visitor-keys: 4.2.0 + espree: 10.3.0 + esquery: 1.6.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 8.0.0 + find-up: 5.0.0 + glob-parent: 6.0.2 + ignore: 5.3.2 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + json-stable-stringify-without-jsonify: 1.0.1 + lodash.merge: 4.6.2 + minimatch: 3.1.2 + natural-compare: 1.4.0 + optionator: 0.9.4 + zod: 3.24.4 + transitivePeerDependencies: + - supports-color + + espree@10.3.0: + dependencies: + acorn: 8.14.1 + acorn-jsx: 5.3.2(acorn@8.14.1) + eslint-visitor-keys: 4.2.0 + + esquery@1.6.0: + dependencies: + estraverse: 5.3.0 + + esrecurse@4.3.0: + dependencies: + estraverse: 5.3.0 + + estraverse@5.3.0: {} + + esutils@2.0.3: {} + + etag@1.8.1: {} + + eventsource-parser@3.0.1: {} + + eventsource@3.0.6: + dependencies: + eventsource-parser: 3.0.1 + + express-rate-limit@7.5.0(express@5.1.0): + dependencies: + express: 5.1.0 + + express@5.1.0: + dependencies: + accepts: 2.0.0 + body-parser: 2.2.0 + content-disposition: 1.0.0 + content-type: 1.0.5 + cookie: 0.7.2 + cookie-signature: 1.2.2 + debug: 4.4.0 + encodeurl: 2.0.0 + escape-html: 1.0.3 + etag: 1.8.1 + finalhandler: 2.1.0 + fresh: 2.0.0 + http-errors: 2.0.0 + merge-descriptors: 2.0.0 + mime-types: 3.0.1 + on-finished: 2.4.1 + once: 1.4.0 + parseurl: 1.3.3 + proxy-addr: 2.0.7 + qs: 6.14.0 + range-parser: 1.2.1 + router: 2.2.0 + send: 1.2.0 + serve-static: 2.2.0 + statuses: 2.0.1 + type-is: 2.0.1 + vary: 1.1.2 + transitivePeerDependencies: + - supports-color + + external-editor@3.1.0: + dependencies: + chardet: 0.7.0 + iconv-lite: 0.4.24 + tmp: 0.0.33 + + fast-deep-equal@3.1.3: {} + + fast-glob@3.3.1: + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.8 + + fast-glob@3.3.3: + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.8 + + fast-json-stable-stringify@2.1.0: {} + + fast-levenshtein@2.0.6: {} + + fastq@1.19.1: + dependencies: + reusify: 1.1.0 + + fdir@6.4.4(picomatch@4.0.2): + optionalDependencies: + picomatch: 4.0.2 + + figures@3.2.0: + dependencies: + escape-string-regexp: 1.0.5 + + file-entry-cache@8.0.0: + dependencies: + flat-cache: 4.0.1 + + fill-range@7.1.1: + dependencies: + to-regex-range: 5.0.1 + + finalhandler@2.1.0: + dependencies: + debug: 4.4.0 + encodeurl: 2.0.0 + escape-html: 1.0.3 + on-finished: 2.4.1 + parseurl: 1.3.3 + statuses: 2.0.1 + transitivePeerDependencies: + - supports-color + + find-root@1.1.0: {} + + find-up@5.0.0: + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + + flat-cache@4.0.1: + dependencies: + flatted: 3.3.3 + keyv: 4.5.4 + + flatted@3.3.3: {} + + follow-redirects@1.15.9: {} + + for-each@0.3.5: + dependencies: + is-callable: 1.2.7 + + form-data@4.0.2: + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + es-set-tostringtag: 2.1.0 + mime-types: 2.1.35 + + forwarded@0.2.0: {} + + fresh@2.0.0: {} + + function-bind@1.1.2: {} + + function.prototype.name@1.1.8: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + functions-have-names: 1.2.3 + hasown: 2.0.2 + is-callable: 1.2.7 + + functions-have-names@1.2.3: {} + + get-intrinsic@1.3.0: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + function-bind: 1.1.2 + get-proto: 1.0.1 + gopd: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.2 + math-intrinsics: 1.1.0 + + get-proto@1.0.1: + dependencies: + dunder-proto: 1.0.1 + es-object-atoms: 1.1.1 + + get-symbol-description@1.1.0: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + + get-tsconfig@4.10.0: + dependencies: + resolve-pkg-maps: 1.0.0 + + glob-parent@5.1.2: + dependencies: + is-glob: 4.0.3 + + glob-parent@6.0.2: + dependencies: + is-glob: 4.0.3 + + globals@11.12.0: {} + + globals@14.0.0: {} + + globalthis@1.0.4: + dependencies: + define-properties: 1.2.1 + gopd: 1.2.0 + + gopd@1.2.0: {} + + graphemer@1.4.0: {} + + has-bigints@1.1.0: {} + + has-flag@4.0.0: {} + + has-property-descriptors@1.0.2: + dependencies: + es-define-property: 1.0.1 + + has-proto@1.2.0: + dependencies: + dunder-proto: 1.0.1 + + has-symbols@1.1.0: {} + + has-tostringtag@1.0.2: + dependencies: + has-symbols: 1.1.0 + + hasown@2.0.2: + dependencies: + function-bind: 1.1.2 + + hoist-non-react-statics@3.3.2: + dependencies: + react-is: 16.13.1 + + http-errors@2.0.0: + dependencies: + depd: 2.0.0 + inherits: 2.0.4 + setprototypeof: 1.2.0 + statuses: 2.0.1 + toidentifier: 1.0.1 + + iconv-lite@0.4.24: + dependencies: + safer-buffer: 2.1.2 + + iconv-lite@0.6.3: + dependencies: + safer-buffer: 2.1.2 + + ieee754@1.2.1: {} + + ignore@5.3.2: {} + + immutable@5.1.1: {} + + import-fresh@3.3.1: + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + + imurmurhash@0.1.4: {} + + inherits@2.0.4: {} + + inquirer@8.2.6: + dependencies: + ansi-escapes: 4.3.2 + chalk: 4.1.2 + cli-cursor: 3.1.0 + cli-width: 3.0.0 + external-editor: 3.1.0 + figures: 3.2.0 + lodash: 4.17.21 + mute-stream: 0.0.8 + ora: 5.4.1 + run-async: 2.4.1 + rxjs: 7.8.2 + string-width: 4.2.3 + strip-ansi: 6.0.1 + through: 2.3.8 + wrap-ansi: 6.2.0 + + internal-slot@1.1.0: + dependencies: + es-errors: 1.3.0 + hasown: 2.0.2 + side-channel: 1.1.0 + + ipaddr.js@1.9.1: {} + + is-array-buffer@3.0.5: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + + is-arrayish@0.2.1: {} + + is-arrayish@0.3.2: + optional: true + + is-async-function@2.1.1: + dependencies: + async-function: 1.0.0 + call-bound: 1.0.4 + get-proto: 1.0.1 + has-tostringtag: 1.0.2 + safe-regex-test: 1.1.0 + + is-bigint@1.1.0: + dependencies: + has-bigints: 1.1.0 + + is-boolean-object@1.2.2: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-bun-module@2.0.0: + dependencies: + semver: 7.7.1 + + is-callable@1.2.7: {} + + is-core-module@2.16.1: + dependencies: + hasown: 2.0.2 + + is-data-view@1.0.2: + dependencies: + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + is-typed-array: 1.1.15 + + is-date-object@1.1.0: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-extglob@2.1.1: {} + + is-finalizationregistry@1.1.1: + dependencies: + call-bound: 1.0.4 + + is-fullwidth-code-point@3.0.0: {} + + is-generator-function@1.1.0: + dependencies: + call-bound: 1.0.4 + get-proto: 1.0.1 + has-tostringtag: 1.0.2 + safe-regex-test: 1.1.0 + + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + + is-interactive@1.0.0: {} + + is-map@2.0.3: {} + + is-number-object@1.1.1: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-number@7.0.0: {} + + is-promise@4.0.0: {} + + is-regex@1.2.1: + dependencies: + call-bound: 1.0.4 + gopd: 1.2.0 + has-tostringtag: 1.0.2 + hasown: 2.0.2 + + is-set@2.0.3: {} + + is-shared-array-buffer@1.0.4: + dependencies: + call-bound: 1.0.4 + + is-string@1.1.1: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-symbol@1.1.1: + dependencies: + call-bound: 1.0.4 + has-symbols: 1.1.0 + safe-regex-test: 1.1.0 + + is-typed-array@1.1.15: + dependencies: + which-typed-array: 1.1.19 + + is-unicode-supported@0.1.0: {} + + is-weakmap@2.0.2: {} + + is-weakref@1.1.1: + dependencies: + call-bound: 1.0.4 + + is-weakset@2.0.4: + dependencies: + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + + isarray@2.0.5: {} + + isexe@2.0.0: {} + + iterator.prototype@1.1.5: + dependencies: + define-data-property: 1.1.4 + es-object-atoms: 1.1.1 + get-intrinsic: 1.3.0 + get-proto: 1.0.1 + has-symbols: 1.1.0 + set-function-name: 2.0.2 + + jalali-moment@3.3.11: + dependencies: + commander: 7.2.0 + inquirer: 8.2.6 + moment: 2.30.1 + + js-tokens@4.0.0: {} + + js-yaml@4.1.0: + dependencies: + argparse: 2.0.1 + + jsesc@3.1.0: {} + + json-buffer@3.0.1: {} + + json-parse-even-better-errors@2.3.1: {} + + json-schema-traverse@0.4.1: {} + + json-stable-stringify-without-jsonify@1.0.1: {} + + json5@1.0.2: + dependencies: + minimist: 1.2.8 + + jsx-ast-utils@3.3.5: + dependencies: + array-includes: 3.1.8 + array.prototype.flat: 1.3.3 + object.assign: 4.1.7 + object.values: 1.2.1 + + keyv@4.5.4: + dependencies: + json-buffer: 3.0.1 + + language-subtag-registry@0.3.23: {} + + language-tags@1.0.9: + dependencies: + language-subtag-registry: 0.3.23 + + levn@0.4.1: + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + + lines-and-columns@1.2.4: {} + + locate-path@6.0.0: + dependencies: + p-locate: 5.0.0 + + lodash.merge@4.6.2: {} + + lodash@4.17.21: {} + + log-symbols@4.1.0: + dependencies: + chalk: 4.1.2 + is-unicode-supported: 0.1.0 + + loose-envify@1.4.0: + dependencies: + js-tokens: 4.0.0 + + lz-string@1.5.0: {} + + math-intrinsics@1.1.0: {} + + media-typer@1.1.0: {} + + merge-descriptors@2.0.0: {} + + merge2@1.4.1: {} + + micromatch@4.0.8: + dependencies: + braces: 3.0.3 + picomatch: 2.3.1 + + mime-db@1.52.0: {} + + mime-db@1.54.0: {} + + mime-types@2.1.35: + dependencies: + mime-db: 1.52.0 + + mime-types@3.0.1: + dependencies: + mime-db: 1.54.0 + + mimic-fn@2.1.0: {} + + minimatch@3.1.2: + dependencies: + brace-expansion: 1.1.11 + + minimatch@9.0.5: + dependencies: + brace-expansion: 2.0.1 + + minimist@1.2.8: {} + + moment@2.30.1: {} + + ms@2.1.3: {} + + mute-stream@0.0.8: {} + + nanoid@3.3.11: {} + + napi-postinstall@0.2.3: {} + + natural-compare@1.4.0: {} + + negotiator@1.0.0: {} + + next@15.3.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.87.0): + dependencies: + '@next/env': 15.3.1 + '@swc/counter': 0.1.3 + '@swc/helpers': 0.5.15 + busboy: 1.6.0 + caniuse-lite: 1.0.30001717 + postcss: 8.4.31 + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + styled-jsx: 5.1.6(react@19.1.0) + optionalDependencies: + '@next/swc-darwin-arm64': 15.3.1 + '@next/swc-darwin-x64': 15.3.1 + '@next/swc-linux-arm64-gnu': 15.3.1 + '@next/swc-linux-arm64-musl': 15.3.1 + '@next/swc-linux-x64-gnu': 15.3.1 + '@next/swc-linux-x64-musl': 15.3.1 + '@next/swc-win32-arm64-msvc': 15.3.1 + '@next/swc-win32-x64-msvc': 15.3.1 + sass: 1.87.0 + sharp: 0.34.1 + transitivePeerDependencies: + - '@babel/core' + - babel-plugin-macros + + node-addon-api@7.1.1: + optional: true + + object-assign@4.1.1: {} + + object-inspect@1.13.4: {} + + object-keys@1.1.1: {} + + object.assign@4.1.7: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + has-symbols: 1.1.0 + object-keys: 1.1.1 + + object.entries@1.1.9: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + + object.fromentries@2.0.8: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.23.9 + es-object-atoms: 1.1.1 + + object.groupby@1.0.3: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.23.9 + + object.values@1.2.1: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + + on-finished@2.4.1: + dependencies: + ee-first: 1.1.1 + + once@1.4.0: + dependencies: + wrappy: 1.0.2 + + onetime@5.1.2: + dependencies: + mimic-fn: 2.1.0 + + optionator@0.9.4: + dependencies: + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 + word-wrap: 1.2.5 + + ora@5.4.1: + dependencies: + bl: 4.1.0 + chalk: 4.1.2 + cli-cursor: 3.1.0 + cli-spinners: 2.9.2 + is-interactive: 1.0.0 + is-unicode-supported: 0.1.0 + log-symbols: 4.1.0 + strip-ansi: 6.0.1 + wcwidth: 1.0.1 + + os-tmpdir@1.0.2: {} + + own-keys@1.0.1: + dependencies: + get-intrinsic: 1.3.0 + object-keys: 1.1.1 + safe-push-apply: 1.0.0 + + p-limit@3.1.0: + dependencies: + yocto-queue: 0.1.0 + + p-locate@5.0.0: + dependencies: + p-limit: 3.1.0 + + parent-module@1.0.1: + dependencies: + callsites: 3.1.0 + + parse-json@5.2.0: + dependencies: + '@babel/code-frame': 7.27.1 + error-ex: 1.3.2 + json-parse-even-better-errors: 2.3.1 + lines-and-columns: 1.2.4 + + parseurl@1.3.3: {} + + path-exists@4.0.0: {} + + path-key@3.1.1: {} + + path-parse@1.0.7: {} + + path-to-regexp@8.2.0: {} + + path-type@4.0.0: {} + + picocolors@1.1.1: {} + + picomatch@2.3.1: {} + + picomatch@4.0.2: {} + + pkce-challenge@5.0.0: {} + + possible-typed-array-names@1.1.0: {} + + postcss@8.4.31: + dependencies: + nanoid: 3.3.11 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + prelude-ls@1.2.1: {} + + prettier@3.5.3: {} + + prop-types@15.8.1: + dependencies: + loose-envify: 1.4.0 + object-assign: 4.1.1 + react-is: 16.13.1 + + property-expr@2.0.6: {} + + proxy-addr@2.0.7: + dependencies: + forwarded: 0.2.0 + ipaddr.js: 1.9.1 + + proxy-from-env@1.1.0: {} + + punycode@2.3.1: {} + + qs@6.14.0: + dependencies: + side-channel: 1.1.0 + + queue-microtask@1.2.3: {} + + range-parser@1.2.1: {} + + raw-body@3.0.0: + dependencies: + bytes: 3.1.2 + http-errors: 2.0.0 + iconv-lite: 0.6.3 + unpipe: 1.0.0 + + react-dom@19.1.0(react@19.1.0): + dependencies: + react: 19.1.0 + scheduler: 0.26.0 + + react-hook-form@7.56.2(react@19.1.0): + dependencies: + react: 19.1.0 + + react-is@16.13.1: {} + + react-is@19.1.0: {} + + react-toastify@11.0.5(react-dom@19.1.0(react@19.1.0))(react@19.1.0): + dependencies: + clsx: 2.1.1 + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + + react-transition-group@4.4.5(react-dom@19.1.0(react@19.1.0))(react@19.1.0): + dependencies: + '@babel/runtime': 7.27.1 + dom-helpers: 5.2.1 + loose-envify: 1.4.0 + prop-types: 15.8.1 + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + + react@19.1.0: {} + + readable-stream@3.6.2: + dependencies: + inherits: 2.0.4 + string_decoder: 1.3.0 + util-deprecate: 1.0.2 + + readdirp@4.1.2: {} + + reflect.getprototypeof@1.0.10: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.23.9 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + get-intrinsic: 1.3.0 + get-proto: 1.0.1 + which-builtin-type: 1.2.1 + + regexp.prototype.flags@1.5.4: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-errors: 1.3.0 + get-proto: 1.0.1 + gopd: 1.2.0 + set-function-name: 2.0.2 + + resolve-from@4.0.0: {} + + resolve-pkg-maps@1.0.0: {} + + resolve@1.22.10: + dependencies: + is-core-module: 2.16.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + + resolve@2.0.0-next.5: + dependencies: + is-core-module: 2.16.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + + restore-cursor@3.1.0: + dependencies: + onetime: 5.1.2 + signal-exit: 3.0.7 + + reusify@1.1.0: {} + + router@2.2.0: + dependencies: + debug: 4.4.0 + depd: 2.0.0 + is-promise: 4.0.0 + parseurl: 1.3.3 + path-to-regexp: 8.2.0 + transitivePeerDependencies: + - supports-color + + run-async@2.4.1: {} + + run-parallel@1.2.0: + dependencies: + queue-microtask: 1.2.3 + + rxjs@7.8.2: + dependencies: + tslib: 2.8.1 + + safe-array-concat@1.1.3: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + has-symbols: 1.1.0 + isarray: 2.0.5 + + safe-buffer@5.2.1: {} + + safe-push-apply@1.0.0: + dependencies: + es-errors: 1.3.0 + isarray: 2.0.5 + + safe-regex-test@1.1.0: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-regex: 1.2.1 + + safer-buffer@2.1.2: {} + + sass@1.87.0: + dependencies: + chokidar: 4.0.3 + immutable: 5.1.1 + source-map-js: 1.2.1 + optionalDependencies: + '@parcel/watcher': 2.5.1 + + scheduler@0.26.0: {} + + semver@6.3.1: {} + + semver@7.7.1: {} + + send@1.2.0: + dependencies: + debug: 4.4.0 + encodeurl: 2.0.0 + escape-html: 1.0.3 + etag: 1.8.1 + fresh: 2.0.0 + http-errors: 2.0.0 + mime-types: 3.0.1 + ms: 2.1.3 + on-finished: 2.4.1 + range-parser: 1.2.1 + statuses: 2.0.1 + transitivePeerDependencies: + - supports-color + + serve-static@2.2.0: + dependencies: + encodeurl: 2.0.0 + escape-html: 1.0.3 + parseurl: 1.3.3 + send: 1.2.0 + transitivePeerDependencies: + - supports-color + + set-function-length@1.2.2: + dependencies: + define-data-property: 1.1.4 + es-errors: 1.3.0 + function-bind: 1.1.2 + get-intrinsic: 1.3.0 + gopd: 1.2.0 + has-property-descriptors: 1.0.2 + + set-function-name@2.0.2: + dependencies: + define-data-property: 1.1.4 + es-errors: 1.3.0 + functions-have-names: 1.2.3 + has-property-descriptors: 1.0.2 + + set-proto@1.0.0: + dependencies: + dunder-proto: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + + setprototypeof@1.2.0: {} + + sharp@0.34.1: + dependencies: + color: 4.2.3 + detect-libc: 2.0.4 + semver: 7.7.1 + optionalDependencies: + '@img/sharp-darwin-arm64': 0.34.1 + '@img/sharp-darwin-x64': 0.34.1 + '@img/sharp-libvips-darwin-arm64': 1.1.0 + '@img/sharp-libvips-darwin-x64': 1.1.0 + '@img/sharp-libvips-linux-arm': 1.1.0 + '@img/sharp-libvips-linux-arm64': 1.1.0 + '@img/sharp-libvips-linux-ppc64': 1.1.0 + '@img/sharp-libvips-linux-s390x': 1.1.0 + '@img/sharp-libvips-linux-x64': 1.1.0 + '@img/sharp-libvips-linuxmusl-arm64': 1.1.0 + '@img/sharp-libvips-linuxmusl-x64': 1.1.0 + '@img/sharp-linux-arm': 0.34.1 + '@img/sharp-linux-arm64': 0.34.1 + '@img/sharp-linux-s390x': 0.34.1 + '@img/sharp-linux-x64': 0.34.1 + '@img/sharp-linuxmusl-arm64': 0.34.1 + '@img/sharp-linuxmusl-x64': 0.34.1 + '@img/sharp-wasm32': 0.34.1 + '@img/sharp-win32-ia32': 0.34.1 + '@img/sharp-win32-x64': 0.34.1 + optional: true + + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + + side-channel-list@1.0.0: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + + side-channel-map@1.0.1: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + + side-channel-weakmap@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + side-channel-map: 1.0.1 + + side-channel@1.1.0: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + side-channel-list: 1.0.0 + side-channel-map: 1.0.1 + side-channel-weakmap: 1.0.2 + + signal-exit@3.0.7: {} + + simple-swizzle@0.2.2: + dependencies: + is-arrayish: 0.3.2 + optional: true + + socket.io-client@4.8.1: + dependencies: + '@socket.io/component-emitter': 3.1.2 + debug: 4.3.7 + engine.io-client: 6.6.3 + socket.io-parser: 4.2.4 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + socket.io-parser@4.2.4: + dependencies: + '@socket.io/component-emitter': 3.1.2 + debug: 4.3.7 + transitivePeerDependencies: + - supports-color + + source-map-js@1.2.1: {} + + source-map@0.5.7: {} + + stable-hash@0.0.5: {} + + statuses@2.0.1: {} + + streamsearch@1.1.0: {} + + string-width@4.2.3: + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + + string.prototype.includes@2.0.1: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.23.9 + + string.prototype.matchall@4.0.12: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-abstract: 1.23.9 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + get-intrinsic: 1.3.0 + gopd: 1.2.0 + has-symbols: 1.1.0 + internal-slot: 1.1.0 + regexp.prototype.flags: 1.5.4 + set-function-name: 2.0.2 + side-channel: 1.1.0 + + string.prototype.repeat@1.0.0: + dependencies: + define-properties: 1.2.1 + es-abstract: 1.23.9 + + string.prototype.trim@1.2.10: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-data-property: 1.1.4 + define-properties: 1.2.1 + es-abstract: 1.23.9 + es-object-atoms: 1.1.1 + has-property-descriptors: 1.0.2 + + string.prototype.trimend@1.0.9: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + + string.prototype.trimstart@1.0.8: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + + string_decoder@1.3.0: + dependencies: + safe-buffer: 5.2.1 + + strip-ansi@6.0.1: + dependencies: + ansi-regex: 5.0.1 + + strip-bom@3.0.0: {} + + strip-json-comments@3.1.1: {} + + styled-jsx@5.1.6(react@19.1.0): + dependencies: + client-only: 0.0.1 + react: 19.1.0 + + stylis-plugin-rtl@2.1.1(stylis@4.3.6): + dependencies: + cssjanus: 2.3.0 + stylis: 4.3.6 + + stylis@4.2.0: {} + + stylis@4.3.6: {} + + supports-color@7.2.0: + dependencies: + has-flag: 4.0.0 + + supports-preserve-symlinks-flag@1.0.0: {} + + swr@2.3.3(react@19.1.0): + dependencies: + dequal: 2.0.3 + react: 19.1.0 + use-sync-external-store: 1.5.0(react@19.1.0) + + through@2.3.8: {} + + tiny-case@1.0.3: {} + + tinyglobby@0.2.13: + dependencies: + fdir: 6.4.4(picomatch@4.0.2) + picomatch: 4.0.2 + + tmp@0.0.33: + dependencies: + os-tmpdir: 1.0.2 + + to-regex-range@5.0.1: + dependencies: + is-number: 7.0.0 + + toidentifier@1.0.1: {} + + toposort@2.0.2: {} + + ts-api-utils@2.1.0(typescript@5.8.3): + dependencies: + typescript: 5.8.3 + + tsconfig-paths@3.15.0: + dependencies: + '@types/json5': 0.0.29 + json5: 1.0.2 + minimist: 1.2.8 + strip-bom: 3.0.0 + + tslib@2.8.1: {} + + type-check@0.4.0: + dependencies: + prelude-ls: 1.2.1 + + type-fest@0.21.3: {} + + type-fest@2.19.0: {} + + type-is@2.0.1: + dependencies: + content-type: 1.0.5 + media-typer: 1.1.0 + mime-types: 3.0.1 + + typed-array-buffer@1.0.3: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-typed-array: 1.1.15 + + typed-array-byte-length@1.0.3: + dependencies: + call-bind: 1.0.8 + for-each: 0.3.5 + gopd: 1.2.0 + has-proto: 1.2.0 + is-typed-array: 1.1.15 + + typed-array-byte-offset@1.0.4: + dependencies: + available-typed-arrays: 1.0.7 + call-bind: 1.0.8 + for-each: 0.3.5 + gopd: 1.2.0 + has-proto: 1.2.0 + is-typed-array: 1.1.15 + reflect.getprototypeof: 1.0.10 + + typed-array-length@1.0.7: + dependencies: + call-bind: 1.0.8 + for-each: 0.3.5 + gopd: 1.2.0 + is-typed-array: 1.1.15 + possible-typed-array-names: 1.1.0 + reflect.getprototypeof: 1.0.10 + + typescript@5.8.3: {} + + unbox-primitive@1.1.0: + dependencies: + call-bound: 1.0.4 + has-bigints: 1.1.0 + has-symbols: 1.1.0 + which-boxed-primitive: 1.1.1 + + unpipe@1.0.0: {} + + unrs-resolver@1.7.2: + dependencies: + napi-postinstall: 0.2.3 + optionalDependencies: + '@unrs/resolver-binding-darwin-arm64': 1.7.2 + '@unrs/resolver-binding-darwin-x64': 1.7.2 + '@unrs/resolver-binding-freebsd-x64': 1.7.2 + '@unrs/resolver-binding-linux-arm-gnueabihf': 1.7.2 + '@unrs/resolver-binding-linux-arm-musleabihf': 1.7.2 + '@unrs/resolver-binding-linux-arm64-gnu': 1.7.2 + '@unrs/resolver-binding-linux-arm64-musl': 1.7.2 + '@unrs/resolver-binding-linux-ppc64-gnu': 1.7.2 + '@unrs/resolver-binding-linux-riscv64-gnu': 1.7.2 + '@unrs/resolver-binding-linux-riscv64-musl': 1.7.2 + '@unrs/resolver-binding-linux-s390x-gnu': 1.7.2 + '@unrs/resolver-binding-linux-x64-gnu': 1.7.2 + '@unrs/resolver-binding-linux-x64-musl': 1.7.2 + '@unrs/resolver-binding-wasm32-wasi': 1.7.2 + '@unrs/resolver-binding-win32-arm64-msvc': 1.7.2 + '@unrs/resolver-binding-win32-ia32-msvc': 1.7.2 + '@unrs/resolver-binding-win32-x64-msvc': 1.7.2 + + uri-js@4.4.1: + dependencies: + punycode: 2.3.1 + + use-sync-external-store@1.5.0(react@19.1.0): + dependencies: + react: 19.1.0 + + util-deprecate@1.0.2: {} + + vary@1.1.2: {} + + wcwidth@1.0.1: + dependencies: + defaults: 1.0.4 + + which-boxed-primitive@1.1.1: + dependencies: + is-bigint: 1.1.0 + is-boolean-object: 1.2.2 + is-number-object: 1.1.1 + is-string: 1.1.1 + is-symbol: 1.1.1 + + which-builtin-type@1.2.1: + dependencies: + call-bound: 1.0.4 + function.prototype.name: 1.1.8 + has-tostringtag: 1.0.2 + is-async-function: 2.1.1 + is-date-object: 1.1.0 + is-finalizationregistry: 1.1.1 + is-generator-function: 1.1.0 + is-regex: 1.2.1 + is-weakref: 1.1.1 + isarray: 2.0.5 + which-boxed-primitive: 1.1.1 + which-collection: 1.0.2 + which-typed-array: 1.1.19 + + which-collection@1.0.2: + dependencies: + is-map: 2.0.3 + is-set: 2.0.3 + is-weakmap: 2.0.2 + is-weakset: 2.0.4 + + which-typed-array@1.1.19: + dependencies: + available-typed-arrays: 1.0.7 + call-bind: 1.0.8 + call-bound: 1.0.4 + for-each: 0.3.5 + get-proto: 1.0.1 + gopd: 1.2.0 + has-tostringtag: 1.0.2 + + which@2.0.2: + dependencies: + isexe: 2.0.0 + + word-wrap@1.2.5: {} + + wrap-ansi@6.2.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + wrappy@1.0.2: {} + + ws@8.17.1: {} + + xmlhttprequest-ssl@2.1.2: {} + + yaml@1.10.2: {} + + yocto-queue@0.1.0: {} + + yup@1.6.1: + dependencies: + property-expr: 2.0.6 + tiny-case: 1.0.3 + toposort: 2.0.2 + type-fest: 2.19.0 + + zod-to-json-schema@3.24.5(zod@3.24.4): + dependencies: + zod: 3.24.4 + + zod@3.24.4: {} diff --git a/public/fontiran.css b/public/fontiran.css deleted file mode 100644 index 86d585d..0000000 --- a/public/fontiran.css +++ /dev/null @@ -1,169 +0,0 @@ -/** -* -* Name: IRAN Sans-Serif Font -* Version: 5.0 -* Author: Moslem Ebrahimi (moslemebrahimi.com) -* Created on: Dec 25, 2012 -* Updated on: Sep 01, 2017 -* Website: http://fontiran.com -* Copyright: Commercial/Proprietary Software --------------------------------------------------------------------------------------- -فونت های ایران سن سریف یک نرم افزار مالکیتی محسوب می شود. جهت آگاهی از قوانین استفاده از این فونت ها لطفا به وب سایت (فونت ایران دات کام) مراجعه نمایید --------------------------------------------------------------------------------------- -IRAN Sans-serif fonts are considered a proprietary software. To gain information about the laws regarding the use of these fonts, please visit www.fontiran.com --------------------------------------------------------------------------------------- -This set of fonts are used in this project under the license: (.....) --------------------------------------------------------------------------------------- -* -**/ -@font-face { - font-family: IRANSansFaNum; - font-style: normal; - font-weight: 900; - src: url('./fonts/eot/IRANSansWeb(FaNum)_Black.eot'); - src: url('./fonts/eot/IRANSansWeb(FaNum)_Black.eot?#iefix') format('embedded-opentype'), /* IE6-8 */ - url('./fonts/woff2/IRANSansWeb(FaNum)_Black.woff2') format('woff2'), /* FF39+,Chrome36+, Opera24+*/ - url('./fonts/woff/IRANSansWeb(FaNum)_Black.woff') format('woff'), /* FF3.6+, IE9, Chrome6+, Saf5.1+*/ - url('./fonts/ttf/IRANSansWeb(FaNum)_Black.ttf') format('truetype'); -} - -@font-face { - font-family: IRANSansFaNum; - font-style: normal; - font-weight: bold; - src: url('./fonts/eot/IRANSansWeb(FaNum)_Bold.eot'); - src: url('./fonts/eot/IRANSansWeb(FaNum)_Bold.eot?#iefix') format('embedded-opentype'), /* IE6-8 */ - url('./fonts/woff2/IRANSansWeb(FaNum)_Bold.woff2') format('woff2'), /* FF39+,Chrome36+, Opera24+*/ - url('./fonts/woff/IRANSansWeb(FaNum)_Bold.woff') format('woff'), /* FF3.6+, IE9, Chrome6+, Saf5.1+*/ - url('./fonts/ttf/IRANSansWeb(FaNum)_Bold.ttf') format('truetype'); -} - -@font-face { - font-family: IRANSansFaNum; - font-style: normal; - font-weight: 500; - src: url('./fonts/eot/IRANSansWeb(FaNum)_Medium.eot'); - src: url('./fonts/eot/IRANSansWeb(FaNum)_Medium.eot?#iefix') format('embedded-opentype'), /* IE6-8 */ - url('./fonts/woff2/IRANSansWeb(FaNum)_Medium.woff2') format('woff2'), /* FF39+,Chrome36+, Opera24+*/ - url('./fonts/woff/IRANSansWeb(FaNum)_Medium.woff') format('woff'), /* FF3.6+, IE9, Chrome6+, Saf5.1+*/ - url('./fonts/ttf/IRANSansWeb(FaNum)_Medium.ttf') format('truetype'); -} - -@font-face { - font-family: IRANSansFaNum; - font-style: normal; - font-weight: 300; - src: url('./fonts/eot/IRANSansWeb(FaNum)_Light.eot'); - src: url('./fonts/eot/IRANSansWeb(FaNum)_Light.eot?#iefix') format('embedded-opentype'), /* IE6-8 */ - url('./fonts/woff2/IRANSansWeb(FaNum)_Light.woff2') format('woff2'), /* FF39+,Chrome36+, Opera24+*/ - url('./fonts/woff/IRANSansWeb(FaNum)_Light.woff') format('woff'), /* FF3.6+, IE9, Chrome6+, Saf5.1+*/ - url('./fonts/ttf/IRANSansWeb(FaNum)_Light.ttf') format('truetype'); -} - -@font-face { - font-family: IRANSansFaNum; - font-style: normal; - font-weight: 200; - src: url('./fonts/eot/IRANSansWeb(FaNum)_UltraLight.eot'); - src: url('./fonts/eot/IRANSansWeb(FaNum)_UltraLight.eot?#iefix') format('embedded-opentype'), /* IE6-8 */ - url('./fonts/woff2/IRANSansWeb(FaNum)_UltraLight.woff2') format('woff2'), /* FF39+,Chrome36+, Opera24+*/ - url('./fonts/woff/IRANSansWeb(FaNum)_UltraLight.woff') format('woff'), /* FF3.6+, IE9, Chrome6+, Saf5.1+*/ - url('./fonts/ttf/IRANSansWeb(FaNum)_UltraLight.ttf') format('truetype'); -} - -@font-face { - font-family: IRANSansFaNum; - font-style: normal; - font-weight: normal; - src: url('./fonts/eot/IRANSansWeb(FaNum).eot'); - src: url('./fonts/eot/IRANSansWeb(FaNum).eot?#iefix') format('embedded-opentype'), /* IE6-8 */ - url('./fonts/woff2/IRANSansWeb(FaNum).woff2') format('woff2'), /* FF39+,Chrome36+, Opera24+*/ - url('./fonts/woff/IRANSansWeb(FaNum).woff') format('woff'), /* FF3.6+, IE9, Chrome6+, Saf5.1+*/ - url('./fonts/ttf/IRANSansWeb(FaNum).ttf') format('truetype'); -} - -@font-face { - font-family: IRANSans; - font-style: normal; - font-weight: 900; - src: url('./fonts/eot/IRANSansWeb_Black.eot'); - src: url('./fonts/eot/IRANSansWeb_Black.eot?#iefix') format('embedded-opentype'), /* IE6-8 */ - url('./fonts/woff2/IRANSansWeb_Black.woff2') format('woff2'), /* FF39+,Chrome36+, Opera24+*/ - url('./fonts/woff/IRANSansWeb_Black.woff') format('woff'), /* FF3.6+, IE9, Chrome6+, Saf5.1+*/ - url('./fonts/ttf/IRANSansWeb_Black.ttf') format('truetype'); -} - -@font-face { - font-family: IRANSans; - font-style: normal; - font-weight: bold; - src: url('./fonts/eot/IRANSansWeb_Bold.eot'); - src: url('./fonts/eot/IRANSansWeb_Bold.eot?#iefix') format('embedded-opentype'), /* IE6-8 */ - url('./fonts/woff2/IRANSansWeb_Bold.woff2') format('woff2'), /* FF39+,Chrome36+, Opera24+*/ - url('./fonts/woff/IRANSansWeb_Bold.woff') format('woff'), /* FF3.6+, IE9, Chrome6+, Saf5.1+*/ - url('./fonts/ttf/IRANSansWeb_Bold.ttf') format('truetype'); -} - -@font-face { - font-family: IRANSans; - font-style: normal; - font-weight: 500; - src: url('./fonts/eot/IRANSansWeb_Medium.eot'); - src: url('./fonts/eot/IRANSansWeb_Medium.eot?#iefix') format('embedded-opentype'), /* IE6-8 */ - url('./fonts/woff2/IRANSansWeb_Medium.woff2') format('woff2'), /* FF39+,Chrome36+, Opera24+*/ - url('./fonts/woff/IRANSansWeb_Medium.woff') format('woff'), /* FF3.6+, IE9, Chrome6+, Saf5.1+*/ - url('./fonts/ttf/IRANSansWeb_Medium.ttf') format('truetype'); -} - -@font-face { - font-family: IRANSans; - font-style: normal; - font-weight: 300; - src: url('./fonts/eot/IRANSansWeb_Light.eot'); - src: url('./fonts/eot/IRANSansWeb_Light.eot?#iefix') format('embedded-opentype'), /* IE6-8 */ - url('./fonts/woff2/IRANSansWeb_Light.woff2') format('woff2'), /* FF39+,Chrome36+, Opera24+*/ - url('./fonts/woff/IRANSansWeb_Light.woff') format('woff'), /* FF3.6+, IE9, Chrome6+, Saf5.1+*/ - url('./fonts/ttf/IRANSansWeb_Light.ttf') format('truetype'); -} - -@font-face { - font-family: IRANSans; - font-style: normal; - font-weight: 200; - src: url('./fonts/eot/IRANSansWeb_UltraLight.eot'); - src: url('./fonts/eot/IRANSansWeb_UltraLight.eot?#iefix') format('embedded-opentype'), /* IE6-8 */ - url('./fonts/woff2/IRANSansWeb_UltraLight.woff2') format('woff2'), /* FF39+,Chrome36+, Opera24+*/ - url('./fonts/woff/IRANSansWeb_UltraLight.woff') format('woff'), /* FF3.6+, IE9, Chrome6+, Saf5.1+*/ - url('./fonts/ttf/IRANSansWeb_UltraLight.ttf') format('truetype'); -} - -@font-face { - font-family: IRANSans; - font-style: normal; - font-weight: normal; - src: url('./fonts/eot/IRANSansWeb.eot'); - src: url('./fonts/eot/IRANSansWeb.eot?#iefix') format('embedded-opentype'), /* IE6-8 */ - url('./fonts/woff2/IRANSansWeb.woff2') format('woff2'), /* FF39+,Chrome36+, Opera24+*/ - url('./fonts/woff/IRANSansWeb.woff') format('woff'), /* FF3.6+, IE9, Chrome6+, Saf5.1+*/ - url('./fonts/ttf/IRANSansWeb.ttf') format('truetype'); -} - -@font-face { - font-family: Parastoo; - font-style: normal; - font-weight: normal; - src: url('./fonts/eot/Parastoo.eot'); - src: url('./fonts/eot/Parastoo.eot?#iefix') format('embedded-opentype'), /* IE6-8 */ - url('./fonts/woff/Parastoo.woff') format('woff'), /* FF3.6+, IE9, Chrome6+, Saf5.1+*/ - url('./fonts/ttf/Parastoo.ttf') format('truetype'); -} - -@font-face { - font-family: Parastoo; - font-style: normal; - font-weight: bold; - src: url('./fonts/eot/Parastoo-Bold.eot'); - src: url('./fonts/eot/Parastoo-Bold.eot?#iefix') format('embedded-opentype'), /* IE6-8 */ - url('./fonts/woff/Parastoo-Bold.woff') format('woff'), /* FF3.6+, IE9, Chrome6+, Saf5.1+*/ - url('./fonts/ttf/Parastoo-Bold.ttf') format('truetype'); -} diff --git a/public/icons/favicon.png b/public/icons/favicon.png deleted file mode 100644 index c82bf9ae9a460e07f08680dac85d1b898b36ed25..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5300 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 { + return ; +}; +export default Loading; diff --git a/src/app/(withAuth)/(dashboardLayout)/dashboard/page.js b/src/app/(withAuth)/(dashboardLayout)/dashboard/page.js new file mode 100644 index 0000000..ed0df92 --- /dev/null +++ b/src/app/(withAuth)/(dashboardLayout)/dashboard/page.js @@ -0,0 +1,9 @@ +export const metadata = { + title: "پیشخوان", +}; + +const Page = () => { + return <>; +}; + +export default Page; diff --git a/src/app/(withAuth)/(dashboardLayout)/layout.js b/src/app/(withAuth)/(dashboardLayout)/layout.js new file mode 100644 index 0000000..c6cc316 --- /dev/null +++ b/src/app/(withAuth)/(dashboardLayout)/layout.js @@ -0,0 +1,19 @@ +import HeaderWithSidebar from "@/components/layouts/dashboard/headerWithSidebar"; +import CallWidget from "@/components/widget/call"; +import WithWidgetMiddleware from "@/core/middlewares/withWidget"; +import { SocketProvider } from "@/lib/contexts/socket"; +import { Stack } from "@mui/material"; + +const Layout = ({ children }) => { + return ( + + + {children} + + + + + + ); +}; +export default Layout; diff --git a/src/app/(withAuth)/layout.js b/src/app/(withAuth)/layout.js new file mode 100644 index 0000000..315d04e --- /dev/null +++ b/src/app/(withAuth)/layout.js @@ -0,0 +1,7 @@ +import WithAuthMiddleware from "@/core/middlewares/withAuth"; + +const Layout = ({ children }) => { + return {children}; +}; + +export default Layout; diff --git a/src/app/(withoutAuth)/layout.js b/src/app/(withoutAuth)/layout.js new file mode 100644 index 0000000..d3d734f --- /dev/null +++ b/src/app/(withoutAuth)/layout.js @@ -0,0 +1,13 @@ +"use client"; +import WithoutAuthMiddleware from "@/core/middlewares/withoutAuth"; +import { Suspense } from "react"; + +const Layout = ({ children }) => { + return ( + + {children} + + ); +}; + +export default Layout; diff --git a/src/app/(withoutAuth)/login/page.js b/src/app/(withoutAuth)/login/page.js new file mode 100644 index 0000000..74eab32 --- /dev/null +++ b/src/app/(withoutAuth)/login/page.js @@ -0,0 +1,10 @@ +import LoginPage from "@/components/Login"; + +export const metadata = { + title: "ورود", +}; + +const Page = () => { + return ; +}; +export default Page; diff --git a/src/app/default.js b/src/app/default.js new file mode 100644 index 0000000..6ddf1b7 --- /dev/null +++ b/src/app/default.js @@ -0,0 +1,3 @@ +export default function Default() { + return null; +} diff --git a/src/app/layout.js b/src/app/layout.js new file mode 100644 index 0000000..4c01ad4 --- /dev/null +++ b/src/app/layout.js @@ -0,0 +1,29 @@ +import { Rtl } from "@/core/utils/cacheRtl"; +import { AuthProvider } from "@/lib/contexts/auth"; +import { TableSettingProvider } from "@/lib/contexts/tableSetting"; +import { AppRouterCacheProvider } from "@mui/material-nextjs/v15-appRouter"; + +export const metadata = { + title: { + template: "%s | سامانه CRM", + default: "سامانه CRM", + }, +}; + +export default function RootLayout({ children }) { + return ( + + + + + + {children} + + + + + ); +} diff --git a/src/app/not-found.js b/src/app/not-found.js new file mode 100644 index 0000000..7174a75 --- /dev/null +++ b/src/app/not-found.js @@ -0,0 +1,21 @@ +"use client"; +import { Box, Stack, Typography } from "@mui/material"; +import SvgNotFound from "@/core/components/svgs/SvgNotFound"; + +export default function NotFound() { + return ( + + + + + + صفحه موردنظر یافت نشد ... + + + ); +} diff --git a/src/app/page.js b/src/app/page.js new file mode 100644 index 0000000..651714b --- /dev/null +++ b/src/app/page.js @@ -0,0 +1,8 @@ +import { redirect } from "next/navigation"; + +function Page() { + redirect("/dashboard", "replace"); + return null; +} + +export default Page; diff --git a/src/app/template.js b/src/app/template.js new file mode 100644 index 0000000..505ebf8 --- /dev/null +++ b/src/app/template.js @@ -0,0 +1,40 @@ +"use client"; +import "@/assets/scss/fontiran.scss"; +import "@/assets/scss/global.scss"; +import theme from "@/core/utils/theme"; +import { CssBaseline, GlobalStyles, ThemeProvider } from "@mui/material"; + +import { ToastContainer } from "react-toastify"; + +const Template = ({ children }) => { + return ( + + + + {children} + + + + + ); +}; + +export default Template; diff --git a/public/fonts/eot/IRANSansWeb(FaNum).eot b/src/assets/fonts/eot/IRANSansWeb(FaNum).eot similarity index 100% rename from public/fonts/eot/IRANSansWeb(FaNum).eot rename to src/assets/fonts/eot/IRANSansWeb(FaNum).eot diff --git a/public/fonts/eot/IRANSansWeb(FaNum)_Black.eot b/src/assets/fonts/eot/IRANSansWeb(FaNum)_Black.eot similarity index 100% rename from public/fonts/eot/IRANSansWeb(FaNum)_Black.eot rename to src/assets/fonts/eot/IRANSansWeb(FaNum)_Black.eot diff --git a/public/fonts/eot/IRANSansWeb(FaNum)_Bold.eot b/src/assets/fonts/eot/IRANSansWeb(FaNum)_Bold.eot similarity index 100% rename from public/fonts/eot/IRANSansWeb(FaNum)_Bold.eot rename to src/assets/fonts/eot/IRANSansWeb(FaNum)_Bold.eot diff --git a/public/fonts/eot/IRANSansWeb(FaNum)_Light.eot b/src/assets/fonts/eot/IRANSansWeb(FaNum)_Light.eot similarity index 100% rename from public/fonts/eot/IRANSansWeb(FaNum)_Light.eot rename to src/assets/fonts/eot/IRANSansWeb(FaNum)_Light.eot diff --git a/public/fonts/eot/IRANSansWeb(FaNum)_Medium.eot b/src/assets/fonts/eot/IRANSansWeb(FaNum)_Medium.eot similarity index 100% rename from public/fonts/eot/IRANSansWeb(FaNum)_Medium.eot rename to src/assets/fonts/eot/IRANSansWeb(FaNum)_Medium.eot diff --git a/public/fonts/eot/IRANSansWeb(FaNum)_UltraLight.eot b/src/assets/fonts/eot/IRANSansWeb(FaNum)_UltraLight.eot similarity index 100% rename from public/fonts/eot/IRANSansWeb(FaNum)_UltraLight.eot rename to src/assets/fonts/eot/IRANSansWeb(FaNum)_UltraLight.eot diff --git a/public/fonts/eot/IRANSansWeb.eot b/src/assets/fonts/eot/IRANSansWeb.eot similarity index 100% rename from public/fonts/eot/IRANSansWeb.eot rename to src/assets/fonts/eot/IRANSansWeb.eot diff --git a/public/fonts/eot/IRANSansWeb_Black.eot b/src/assets/fonts/eot/IRANSansWeb_Black.eot similarity index 100% rename from public/fonts/eot/IRANSansWeb_Black.eot rename to src/assets/fonts/eot/IRANSansWeb_Black.eot diff --git a/public/fonts/eot/IRANSansWeb_Bold.eot b/src/assets/fonts/eot/IRANSansWeb_Bold.eot similarity index 100% rename from public/fonts/eot/IRANSansWeb_Bold.eot rename to src/assets/fonts/eot/IRANSansWeb_Bold.eot diff --git a/public/fonts/eot/IRANSansWeb_Light.eot b/src/assets/fonts/eot/IRANSansWeb_Light.eot similarity index 100% rename from public/fonts/eot/IRANSansWeb_Light.eot rename to src/assets/fonts/eot/IRANSansWeb_Light.eot diff --git a/public/fonts/eot/IRANSansWeb_Medium.eot b/src/assets/fonts/eot/IRANSansWeb_Medium.eot similarity index 100% rename from public/fonts/eot/IRANSansWeb_Medium.eot rename to src/assets/fonts/eot/IRANSansWeb_Medium.eot diff --git a/public/fonts/eot/IRANSansWeb_UltraLight.eot b/src/assets/fonts/eot/IRANSansWeb_UltraLight.eot similarity index 100% rename from public/fonts/eot/IRANSansWeb_UltraLight.eot rename to src/assets/fonts/eot/IRANSansWeb_UltraLight.eot diff --git a/public/fonts/eot/Parastoo-Bold.eot b/src/assets/fonts/eot/Parastoo-Bold.eot similarity index 100% rename from public/fonts/eot/Parastoo-Bold.eot rename to src/assets/fonts/eot/Parastoo-Bold.eot diff --git a/public/fonts/eot/Parastoo.eot b/src/assets/fonts/eot/Parastoo.eot similarity index 100% rename from public/fonts/eot/Parastoo.eot rename to src/assets/fonts/eot/Parastoo.eot diff --git a/src/assets/fonts/ttf/BNazanin.ttf b/src/assets/fonts/ttf/BNazanin.ttf new file mode 100644 index 0000000000000000000000000000000000000000..4e59251727ccee3c2c680fcbda2a3185d45d7e2b GIT binary patch literal 60852 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 z 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 \ No newline at end of file diff --git a/src/assets/images/examine_marker.png b/src/assets/images/examine_marker.png new file mode 100644 index 0000000000000000000000000000000000000000..ac2446dc43e7040cc612847335003cc7ecf6591c GIT binary patch literal 15040 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 literal 0 HcmV?d00001 diff --git a/src/assets/images/examine_marker_active.png b/src/assets/images/examine_marker_active.png new file mode 100644 index 0000000000000000000000000000000000000000..07cbca51f4cff8ee207e6f3e7890b937fc73e6d8 GIT binary patch literal 13021 zcmc(Gi9gie_x~MZDTK-vVKB(fyNDr_WGnl=FGZ+qO(M&*i0mdyi?M|4dqeg;OQE-A zlq}g2Q`TfRzdP^G?|=Au)T4Ucd(S=hoO91T%d=-j2HM9Mc^Clz$8>crn*cxq|4IXn z(8E9e+#lYBf6)40(lt8*e}a!(kA(j|>Z4=r4**LC^*16}l7$z3Jat#g>aMA`+uZi)Q!Jeoq9Z*<(W}Yg=1s58Oq2t~(z; zQOS1E!s}lXvW0DhjqN#;EIT*R1SS0!jEp7+MDvOdV}(SLgqasA8g;$xrajJucqTvd zj27qDO9>)Jt-I4dg|1wfni9Aut=}fKH9lbNFwoT=5wBe|7oPfZWg)cLJrT0_fA`Zf zQ;Y<|Ukc674u5Agr~Y!&!3(Kd_n+6pH$?ee+^C}Hhw7|99@uvekYsLTkIkRi3BiwT5`5I$&M~GE~geBmoh4)%qOwPws@YxKRSTb8Z^*V zN#7YY=((M`Pp@m(UBAirNQ~p^WTuG+`YkGjh1~D#C3-CL$liN=s=2} zGM{9D6M?fzP?uHr5TU!JU*|agRE2o+-=Poo8q0D3_>vD?swA$JnSD1*4#?9BdYt20 zTy?^&DyJSD_PU|ozP*)F*cj4{VeI~g9%PbA4+aGpd8W{dhUhKPo(!~jd8{5mP`5c@ z$dF^_wE}ThSo$Y_c^pmU69jmXZ=1sT&f~8*uCEelblBfVYoQnO{0_Yew<*K5`ieFT2#*$7x$jP-;viy!y8ikK{%!-7 z#UUy6kAL9SF^)Ih`NNy}ShfT+X3*kq?>~QST>m}GpQ}NiRM}< z??>R_DH3wbi(Z{8sMDB~#{Y5eJv`VBJS(*L!0*uWcR{HB#Ka9Us)Yv$)_>Q{dmGKw z&0$m0-r6i=%2anR+Vq5JN!xGPb^!}+kf46vo|NDCd`f&pCh-r3e45*DS*FMFt?>Ec zAJv!UjzJRH?w!W?{?xl!Iqk7t2~w{`>;)Zn0*F}enmEMLLj?F*6Ey#Ne*b|;qp8VF z!sE-l33p@Dn$u$|%5Y=T zu#f|&i4>V*cW1`^=QrioqU?ve2GEqF#Cr#+*dubsnSo9j*`i4+krI<*X`P3PALnCy z!@87K`mIDCx8?*ic8i;4=KZGC^@HlMjD7?9%jBp(0XAROUSOH!IN|E}^kjdLu!0-| zUljrTjmv|U>~;Eq3i5-G5TIvtuhUH{RvM*k}4}S`mW6-aLM4Cal0m8GN ztS#|=D}Q(`6U&H_2dJZWtQ4(7!UuHfH1^LvyTzOMN4IZ$)vzIyo3wp{$=0hhxFpyo zIcFB`jj1zLv&yOd8&RT};K&ThE2x_@3MAewc%LV*a}K?eYg51G_+Alkas#8V(c++R z#*w1=2}!t=cDreAY-FqgDr%<<;C8L+&V7Eip@v>j7cIC+K%$=x-O*YO@g%?hfT7!I zg%f_-@0>8lmds6g5@^t=%~$51HV?khIv|O3tI>mwGWQYQ+J(dr>Yq zl+1SB)`<_tyfNv~w;rAQbI)as;R^Y3;Ff8L^`r~mqg$>FVC|>9qP?+mctd}s0m9@L zW4P&5S9CZV13eg?d}Usi65k~1@EtkaHo#%gZ&C1|5qqRETo??KmN-Oqenvoop7X3` z()*W+EDtNFV+cT;ZZsWOZ%#Ii&(S3*YkW8@y5Maw5Y=4Ca`B!IE$&(UV&#?JFB|Oq z7Hsl>vd>G>o%c`j7+l_P13CGkR6ZH5P)$GYcLs>{_7Kg!`Hgp(4_fMI7druNt=?@! zlKsm0$c@mq=&qsC&lq*_ZT-CI%**``S)DfTuG1msxH_!)pIih5CfNyX+<2N-#l$>&d zRGmS~&&}cn9Y0H539CGCKc$Cpe{;!tCl#(_g)0rHr`+f2zzS=aDCmU723g#-i9qpP zA_H{VmVwymgFt0_sI2c=ba>96zs~+$B#%;tG4wqjYLriZq29|EH74uPC8i)I(O zfrt(1!O-@!9tWXbo9kh4pMQFKYQsjC<|p9ESnh%HsaG6fVr7Qb?jX_}ou}ow*g(S2 z{^bTJXg5MD7SUn+IMp+feoXJmXX{CV<(;zo4dmMOYbL8xcpiA3;b)_2%9Jk8-*w+G zboaQy`mIe4SxdG_(Xvw{ajfuRbes(e{K{*hZD?uKF`yf`&wr9h+G6D>h`trP204$9;OpRr$M81 zkofniwaAL{iT+dcxQ3S&`)N7np+Iz!EK00*e91?KxlTH!|B+HR^s}UXM$6XXZB&}> z@IBgf0^QN+^JVz)-*{2$JfL%?EJWh{XyJbjVW#W22ESd%=5hR9wbef2bj*;M`TqIg z`df8a#+fdVfUS{|&bO*Huv=qwI+tc(q|gu*O;Z(*K#{Ba57OqQt`iWbYvZSEs@640 z9Fbfn9a2ghzDg|o$3m`7q6#5^{yG;@!(y{IO>)tpXCp}#%o6RBdsmhkmitJ8`4{3q z<q^fNW@UR}&M-h6$w%3g@TOd=X1VxV4qVrD{Ud;n ztL_I0x?%;g`^8vV&j?{$u~n5zRzqn8{OsW{nUPgp%3G`{=gSPN%Y(j7`-L;0`J4$f zsEz6~JZjcVvR`iSJ$g2M1a*=zHM&zkBjl6W=j{Q~@fS+>z+kES&Nq>OY%D#_ZVew+ zWK-Yd&G%@{73frXmA-uIvyIU8FE#~p{g`l5L*s&TH{l?p)Ymp0c05`g@OBl{ZDo^= zbE`vAo}0buDH}DB18g`#RN8}B8%(G(-=hjw22^Z{w4`3>OQ@(X5;}F48XHxErNX(v z;7!kIA!ULS0RdnPr4_PZ>+bYxoBpMurDMnwo?fqcg?0=bNUtyz4xc67Ut=M&#qg^D zk0H5rL#ab>;)jufg$yySt4AbIaM&YPBKUCU)|NM* zKTNnvN7w3JEG!qq3_L2_0;AIoyD><>(p^79bnDE3Ti;cp(Rut@#%nlT+^e%F6oH=7 z9J5u{qDCETr%%I;%LCR=RBR)4BA%Zs`Ru{>kgJPDW3a?#BlZ*zSEg9(LUU)dL=14^k^ADW^0hU z2k^yXcb`Ei1qB~`0?4K3cV&TM#0AQSIxz47 zB%m*Rq+A|cfsROn*`XvoxvVCx#T$~0+RMZuK*xuj=4yi$Z(m*~lRu?O7tJrA)lP!1 z=H_#8qiu0r#hNWF8h)2;grL2D<&^ldQWe({Dy5|R`P;Y5rKVWCgA0KM$bZXPJgCC% zYPFLD1G_?A>KZMo=*M*m5a)S#Ij(vpd3oX`Ip?G3YF!?))BE-7PM_t)jp$779Z($j z6J;&uKHk`Ab=G?PcwJbyI!w6xRr|ZV9a*%!J0Cs(p67e26&I@Wk;e3&0>-!3%s-D8 z`*>D85`UhXc||PPI48f%ail3!Dafd7%kLd(Xal*jMtI4l87)#ie zlC$X-Pm{kk?HnPYI>#11^j7a8fwH!iSomPqY@$|wgipnMqRMi;sp*)`Sy?Xryp#Y+ zF9tpE1yTMZyZJ=4x~=kJ)j zi|1;Y#wkjqVH{z;fq8Bukj%-|sQa~J*=GT+6 zZ#~w%49sVH#OB=AD9v`R*{>Wz--uFLSH#oQ`Y;SB!9La4OAfxYPSf8c-+F4;id?HS zxJaPO;)%Opb4+_6`Luc{9=&2d`A$pNOJIH%7d*q=azq7i(r?OCY%dtx?`FOhcu+JV z7sek=q0Fa2O?V8F9ZGyY+$Hl2PnE?ZjNF+4XTz8E-jfT%KA(cGVK&)ob+3^WoBAb@91DNG_u`zcb-ckk9gM($TW-J#7W z?5}2%PUSZuj?qOFP;+K9>lZ|R>W5x($4rLuv5kd_ZQe8egzTArH;T_1J$OE16_|~^ z>>vk*_2(#`F$kjh3v3QgoXQ7`a1Bqq`jyUXHyig%63rGT`ALHl$mc3kuQbs3&Ca48 zQ}vVfrs^VXcm$s(;i1j?@-Zl^R$#>XF@<9uW`E4XSL6cmJdVC6EVIQfejol5^_+b* zE?jCSWa6$h+>;;f**@I165T~{d&H!vKv-At3x9-VOrMU@QfQjF&fk#47dv)geydn` z9f!!2In719etA~6OY&yA3*q6ykvv1w_6k{IzsB!|NeqgW8!WX3(&L!VoZdOX;l}4d zc!<3DLftSt_4FOc-4U4VK2e4I$B)`DIJwxlKISpROk$9-Z`S5c&haR#8C)1((YPQ7 zI!Xe*s_F(y>(+ia8J{Y-&K;5Ce@sh(f1u>$DLMVo(z(Ymr;r-sh2fSqVoolMXXA~g zkGQ>mN428qth2>u<%}2&mSBPl`1`W3y+Sa|Ik&?lW~hL@dh*S^}FSqYu;Y55l9N-e6Mk=K~bqO zxC0C1T|ne>RtGl%u7XKa0lHFsXfMK@wN-OG_n&G?)Z%1~sZ)|+Y??LGpd7q6fJco} zUJAu>0~dG-S$bTOZ@5wT*DAQowX@cLuI)1h>G~lxUkP$Q4?{#SCF0j* zE#`FV(GNE|)7K?u*`E9Co}=qCg*vck`ezCwSOk@D!uK#kG5n?7P6jqySeaf&;QW*l zs){<`^WT6Ji*;8*>Xkc9z8Y?juTB+~)28T~cP!*Q@K|~m&a3I4)z*FrtiSQu79<_zW)hnupFRm!!6=MV zhMB|QHMB#_QTI;?H0hfOLbtwlz^HvQKMk{+j|)b%$k>dBBb^SY?hPyJLJ z--r^Fj9{UkTe48A*&U{jughPGslC->UsX|{hsr+EfpFD(|MZca@Cr{4@96Lu%QAi zo%0R0l6L};bVv=G@*{JpUf=a^c-|j~;GmxAa$I|7l27L&EKTw1*Sv_6?pQcgqba(Qo#^ZhOcc0Q=^ZbT2L zX$kc&xwHUW+6DK+7hIra&7JzLWu&E2HisRIIH(+WSW>b z*?23lmdG+Wkn?o*2$f33Dl-tDj)1n&%et2PkHUK<6f8OUuDSr7qw3v436}q&y!1S2 zj|*5pk?Q7xOScxok&yxXITh z)AiG5-O{(9yNn%mB^|Zy1Z=!DJ#`>^LHo*kUKB&2N`P;p_84vJ$^Q+eMiwaCmau(4 zqC}Rk4SaKJX|?{GB0QFFC;?0J;qCe##dMa`8^$@kLw1HKm^cJV1`ZyTgescV?k&7E z%j;rco<8~EZ`f|knOi@U6hz^$ICdqmV>C8NT)mU{+S)ucir3%d=h0+6b8=)cbb?&< zK-_eqHaZn~!)RDNbIAbSaEwE|t=M(=R-~ySHJ0v|tv}~2ku-9h-yGLV*LxUxgOKo_ z^9>rtAY3gc--TDrFs}*UNIt)Iec3E(ADV#m2f!v)MufHyamIxUrP*s(ocac)X0y|e z9BuwbnXzsZgP1jSXf_UgVvl>bF<;rsTN|mGD-`}1-Vta)2CR3i#0mzW<6S5+k5z50 z^&N{?X=Q`nruc^g~!#>gC?OJLC*!|INdTvHMF$;X^mr zogM#?l@-1WF4eH|XlSDi%gP$#db!7SVY$f3D2w>BNGqICRdZN^rF3s zippI@%B~+^@L^D6E9Ug5VRThiU@oPS7K-F)=uX0%^?4K7L25MD0_RK~bYl=FxsI`T zY&$!wBs_Fcwmz zH%q%0|G)S}v%vLr#{)#A%l5Rk*+gR(204(w{53nVE4>`XCF5(Kh^CRy1P*+d6F3q_ zKZmfcC$-Q<(np-TJAgFlptpTme&8qugS{C(ijjMy06*!o{Z?=Ms#a%|Y^uVa=)G;` zN>Lv>VD;aeKbmCPVT*H0Ij!|!Jp=@OFtM<;e%bt-oD<>En8e3plg@mJT#(q{SjEM4 z)pRM>H;sGe?rzBj{s_LH<)FExmU&Y^HtJ)hzy^KGt~1Cl`mREv%WqsNo#3gXI4`T0 zMckzP4jtjMTg%XOIBzXB2jAceJ3pI?PqM}LrsOGXNAD@IM4Fn7d~Vz-s@(J-pXd;Y zgp!IxWTv|@j|)BfT@q8cJihrU{NW99(xT$qCdlgq@yf+04Rq5UD%behA9M(*~++b_$*o@U% z(%RcSw^~)t{(Cm;UoFcKHGEm7be!* z#}_h?k6$VJ6=5$=!hqA(Ye7^wtPD%Pgo--XaBt!;b=b%E`%kUJrvf%bS$hIM!fG0P z4mqWN{>Wm1OgIS+Zjz4XXY~Fk;o1LcKVGB8XdFg4Bs`C8@DaCcI2^(V#&d(KPcA?~ zmgR?OAk~{6P!hoa(|xrSn7tVz1I9avCwI=iF`BB3>lSwBOR&8lxtw@63Nj%3Vyqa( zn3mLcT~FxpgDsDrS=#KsC)^1%Oa@aX`U~~NXV>$c_!1nOwEdWYGW&1x(04+!kuxdL zQ+4`=U*{Vv{-~pj*Ib2|`D9C}PHAAx2aI5u5>5FDiw7MOpw{ARgE>-tr?!{({5%<3 zooE?2D|&4F&_8+9`}*2IO=vRHc^mCH4sDPUWHBy7t<9A$@bH+a2F3CG=u3I@86(C& zSp?l>Z1DrTf4^s9kDX{OvGD4;M2>2hInsjPxUe1v3sl0wJl0z;dJS(G`7YmrxpShV z+g#U>cHyl+8{znph0VtMiUJh}-e-iHolPW;l$wrEUT>dXjX7ADG0bn67Kho52~2%q zh-&Q~tA9=|v)A-B%q*=#b6g0IJACWUWm6uII08Q$#}U&x&g;SM4Lx2`_4-rBsyMUh z$o7+At(mf^=7B-=@SYadJ*dI5;6E+u!wT?cjPStr504c_dGH-oSbwjm&a39BK!9n%u0??u{~KF7X7_plx-sB}%0F!VOXE|dn_e~w(+2(86lf?3N|^NCthWA%kyZpnZM8x;q4`^11> zz)CW?aLKwtZ?LP_G(iU1@SCQ?sRo~J1x1OQ>2ES?ty`0H8AT28UY;hOhtJf?_wP((-gWhJoJ{Emem)SSYIS$?6+u1F>yY|Ut!?<7VA=Sr!Ylm@7+w|UgOH47>^ z+j3mPuh=@UvB8mAXOyqMT0-Em)y9b-L?hzAoIoao^gF_5`T%Yyl=gQDpZ=~N;J7E= zrj+iL2$u^WHB6H9%zHvwVU=vAedRsc8R+2srWtiriq8TReZ2Us!?#%4&}aloms5Hv zwSx#iWmj$3Wb@Fo9Qeq;@>^rVKZR2>pH@rUJS@c0rpLk>8>e)T%?b50pt5~&Ln1jy z`ELsVCk+0>0x;ZdlLP3)qEHJHSfd6C<(7|IQ%i(bgPDQuu-~DuUmeWmfas?k*`h{* zrKTtwQp1HVBdNn{1mt`zSFJe|;eb37-%W|h9_>ee53Y_M$9HTTq zCohz)Rg79+gx^T_4v~0USGbw$kAnqs&ncL7BH>F{C%5|oJ=w>Ix{!9SD09Ht{rh() zq*B<>%_*~jl=A~A9?5n0Z2tQ`=v;v2e}h0BQ!J_ts>RYeoJMN!yt{V?ugW?I`}C9? zx(Oud_;0V9LHuu^>mmhrEp$i&WJEAC&G!(BgRY}D9|p>c)N6>k&NlSe(t z%wSu3>l87K5{aez@DzcHWo%^-`AzHxK$q7{CU^|ahE0MixqqeocHzqkbe7AAH;tR& zWEdxv{53l?9}I!K_b`T5>2nPc^LL_pG%Y z1Dgw*$E9yMx)UB!1BLp<5P|2TkEvAV^CYg$2U5i0miO`;&q)>NL*3G#xSn#<$xve} zwyVrY4bFGiLo+|eN?6lJItUmSJhty*h8>V7pH)pfd}Tqxjb#>&x1p1;ozMyrIA8^M z4x646g=NHDt%<^Mra;On$Km|x@#fzv0n-Nmu z1csIf&tP6+PZ8OXrFLuN7g7pKHWc9#2irm)+dKpkhGa|5f}DiWzM%UTy!ra%-1%@X zHj7`$Y6a+F#$iLqQ?*2IJ{eQ?pbC3rrkzEDM66cN>Ly4&dk}z#xndpF@4PmkMvqgq z`m$ypxaABrDgOOnZ=`O{0&Ws7j8k2-uCOsFf+x)JlN=fQ*Teqdt`h^wu&saqTl{eP zv%9a`er#E3XNJOFRE`;snF2T2grm*Q^PxF8}YO=+j z9POR_CjlzmYP{^`95ZLwK935xUrlZ5Q-pOls7=047+r4@`23UE&6L+vK!yo9ZV55JFxho7(7o(=ao1{}V${lym2 zhe>!uz|DMDZ6u$0A1a~l0Uw{y(x6-#3bIE4mg$|68c4m1eQ7(hPG1l$t*Zxd2K zE+R>%G*~7i9Rs7s>S}BwP>s}Xw=^N+`F>vUS=_kKA%86U;ge2*lb!Y;KQ^7iG2w0^ zmOjCwe=0kF*Mc)_m_`MDC-Tbtd-x5B=xE(}aL(@fHvxrq7T}pzQ*$vnWc1_WZIZag z+x?(5$A%ZF{#kAib6|U3>UD8b8$~Cm5zClxRO&GDs5ZziflhKg`7HVZ^bXfsCg4{l z6%a{9>foF02kjt)AUUmGehvLF53jD9)7az-llNZqhqmme}@s`$xQz zFD-6c>XxGzmsG;28eFKwZ=SoJ2PyJGic?rg1Jj%p9Z%TJKXE6Uy}`KTx1m)8YVQj| zW50i~L2I%`rx145k5*q(yDu57z68+&oBxtFr2_|eX&-XUyt7^RHnrVrCy95%e*3VY z?Wj3k20GUhZyo!aHB0ORpdcOJ(}T?#w@&x@z-Sc8!zjwYl8mQnRh=2+tFI79#WpfK zEM)c;BkO)bK}sAi95OtOfB=GxSG7&Hg8|SX37ac7Cv*y=d_mv{|Fsd;_V8gPSVkjl z2a_5bm9lp%7n>@OK!vG}fqjX9=O-EbC}iwcz)L$4URuW92U%H<_= zQ0BY?Nq6BLlDqPNgU)-jtyBz>eC!M~{0I-09m2zhL~cXYuXtR3sG`sGZKZ+*;lK=8 zEP1yrt{k>|xRr~1_CsVU_kYNoE03wjo6h~Y$n!RfSq z+d2FCzs)*yI-F%;#$H{`u&d_^ku862l7A-p>%)_|`xlLjt<{h?TC5<4_Wd4|C+KSH zL}rJYn?a5dX|C>!1?>QLcc1@MQmlxQ;YGUCf{VbrZz!Z*B8XmiKxYyp5=)CD?Mg9EtlQfmJ<|ho?Ss ze%Vqxuw_;WD`JbJ!JP}bVK)d5$KUIe*f(U;yo434KVHA>1Xr4z2oGcYcSjG(is&n; zFs{|^-R6|Ljad4B$SRC){mRt;36BPeeAE^xaZyn>&nge436+zz9+X_S1&y8vy~wRa zYu}`t4E2F z(Sx~;nv&i2n>#F!e?mzX+rcx5myU!|HUyxNQq zW>+v=YhgYrQ~+77+hBYXP&C{s!{!$YcPB!S%K9O-iBlhD^bzk3ZyE{m>%Aun$QAt> z6XW?r-osW5jSdam{I$Qs^ss?0#f$LhOUaOtPkM^XymTTJg$coFW>(7jgF__o$ZHwF zoA#dG3vGtqPcZ`=8fr~))m@LL!r)?Gpz>5!^@cz>%YQ(_ZK8kOomBsontqMG5It#o z$ErAhhXA+jW*f|*X1_FSW5wQ|DE3*Jf?<7Gd3MM%drcpz+)f$A&1w2~j2 zW;x18cwF3q$c6-1OFk4b5Z;X?!@Tm;`{k$~#^{+aSMCej2;TT9^4NyU?Tbs5h65hTa#`?2QN z?yHd*QGIDxkl=Yivh=wbZ0x-Z4PRz8Tl)T$xrMeI39epN8t&12wDf6@$$J0cGl+P5 zk*{ERT2^3E+ymALSc0`m2)`kBoYc8yA9v!rckYm5K5YaU&Cfi5KB?FV0MD#kuh<;P z;+r7yp%Zg z=l+uz+U^tg0M5qdOH*7b{mAe=bBPrn;af*;tsQ2<|<60f#le?XY&&N0*#E(O0_#Au5|N{xC9x zqEES-j=eIY6rwLpSb?d<`V&5oncmyEr4!VR5q4Jec>nnZAA*X9H8O;a_m4aHoQve7 zC#!x-gEO5f#hSPV2m)RzftW?*F3mgWp8Ai&gFAeXOIQs8OwxQ5!Nnjlvuk|*=Y2u) z5mXucD%mtBE?y>E_bew$)B4@vGiM03+u^JX1k=YN`%P7ZGy2_VuP3EVw)Po)viF|Uexr7OFHSC6XAsV zmIx8M|GAC^KqNRbw0(TirY$n#^L2_d`3Ts93eo*{Z0AN#7_g!;@MU{#zIy8bDc%Y|jut$F@)kci2DyGn^I!fBxcMOAYxp4c?^xnk zUDFK$!s!p>_}$Tiz2xG08pR&g<|zzK%NOVS&PpHn>IZt9VW^2O2rzhuV$d?Bvz&nrm#27`_k>-B(Ky7|4ZgqPUNA7krZ0I4F0Opc2T;*L zmVIW#pv1X45k5?Rb1zFEY>ao5TsELktsoBnmuUIlQK1p-IYoy?XqIiey9V(x$U*T zwfDDw@Q2|H=ddp4eEZvbt-U`e6bgB8GO!R>1-t_61oi^^fg@h~{cG-j8-SJmJttLd zd@jrZo&t6Qe*@B^+(WhSRF25gTQkJ0xliuMPNFa;vCy(Ajs0C-UOzR8Sbb5f`Q;lk6NGG<;(h*AsuKn znQ%>?%N}Ax&WGzroYN!EyhT|D{M;!?28>PmB#^eGI-cBWAb6srn@Mn?@OuM+6D55E z97{qxY9MH$tcOX28Nd$)0wxib?vtpeEH@A=iKL|@z%B!Ul1OruN7GzlAV?Bf7h;&F zBL(3r;8|cEFyhCp8UAng+88hq$(AQD%x-1pzxJipPF7zVzR)zTY}2P0?witFv)br8 zQo6;xXOp5`e*q_AkcEm)ul3*fUTOHx(6Z0uqT?zhXW#La#(rri7RXj$kPS*>Ml+y| z?z1M93Rvd&M8VP973{oI!Ora%WL)9ac2L32X$rT*uEES|0Y^z(lGsoe; zPL3+tIzOquZ*UCF^5TWdV1zlTBA2X=By(bjc=ZFOz3=6EQulM@L|4DZJv3j#YSz#R&9 zE>W=aN(^$gf}@jx@3veo)jkC~Pr)Dq{(ju@^7|$Q``nU|S+*%SEXE5|n=#CjN=}Ra zb~k&xOueOO*CQBao}%O8R|D?rqRC;A(wOmIU4~(X{K}^&Yr@Y{M*XaN*lWa3`qo8i z13I8|YaWy>5dtAb^acsA!a$%TlI|t}E;0}(i6GaHbsCBAxq*O*vfd{lRvHMHD9d%P zB*QtGY=xF7iyE{SV18F7z+fJ&dbf{ZxY?}m~@wT%o} zW+1TAqpl`HhAKWzT>99Dp5%dLx&BH<$(W;nJqCg-9m+M3XU{DL0xccLl@I1p0~-F3 zL}Fa8Q6sK05O8TzfYRLKXp*-L1YV}l8fwV7k(%nHhy8-bQBzhMNPx6xF*Rl=!U1E_ zg?9AWWuFB%8c2lrgAd%9kp<-w-=sr5Q!?@YrzredVKTl<B&4VSM6v@%mxlACSzZU-Ro7eM6u4{ zb_*AboCUn5WTL+g`n>^@vD2543w>~Dpc79I`ok{^0eCP#Lfm8g3IT70-|ZtAua``O zXO^$#b(@Smb*=zvb9tBJ$#1}%s*TQ_d%H-;ZB-jv7oP7R8SbHBg-9IJW*_`VWxf%g zGo~2i-L~aZE3l`)zfB2;S_bhf_5r5Md|wkE_kR}(1#;yd6cEN^ncC#R00000NkvXX Hu0mjfHiT*3 literal 0 HcmV?d00001 diff --git a/src/assets/scss/fontiran.scss b/src/assets/scss/fontiran.scss new file mode 100644 index 0000000..6aaa085 --- /dev/null +++ b/src/assets/scss/fontiran.scss @@ -0,0 +1,212 @@ +@font-face { + font-family: IRANSansFaNum; + font-style: normal; + font-weight: 900; + src: url("../fonts/eot/IRANSansWeb(FaNum)_Black.eot"); + src: + url("../fonts/eot/IRANSansWeb(FaNum)_Black.eot?#iefix") + format("embedded-opentype"), + /* IE6-8 */ url("../fonts/woff2/IRANSansWeb(FaNum)_Black.woff2") + format("woff2"), + /* FF39+,Chrome36+, Opera24+*/ + url("../fonts/woff/IRANSansWeb(FaNum)_Black.woff") format("woff"), + /* FF3.6+, IE9, Chrome6+, Saf5.1+*/ + url("../fonts/ttf/IRANSansWeb(FaNum)_Black.ttf") format("truetype"); +} + +@font-face { + font-family: IRANSansFaNum; + font-style: normal; + font-weight: bold; + src: url("../fonts/eot/IRANSansWeb(FaNum)_Bold.eot"); + src: + url("../fonts/eot/IRANSansWeb(FaNum)_Bold.eot?#iefix") + format("embedded-opentype"), + /* IE6-8 */ url("../fonts/woff2/IRANSansWeb(FaNum)_Bold.woff2") + format("woff2"), + /* FF39+,Chrome36+, Opera24+*/ + url("../fonts/woff/IRANSansWeb(FaNum)_Bold.woff") format("woff"), + /* FF3.6+, IE9, Chrome6+, Saf5.1+*/ + url("../fonts/ttf/IRANSansWeb(FaNum)_Bold.ttf") format("truetype"); +} + +@font-face { + font-family: IRANSansFaNum; + font-style: normal; + font-weight: 500; + src: url("../fonts/eot/IRANSansWeb(FaNum)_Medium.eot"); + src: + url("../fonts/eot/IRANSansWeb(FaNum)_Medium.eot?#iefix") + format("embedded-opentype"), + /* IE6-8 */ url("../fonts/woff2/IRANSansWeb(FaNum)_Medium.woff2") + format("woff2"), + /* FF39+,Chrome36+, Opera24+*/ + url("../fonts/woff/IRANSansWeb(FaNum)_Medium.woff") format("woff"), + /* FF3.6+, IE9, Chrome6+, Saf5.1+*/ + url("../fonts/ttf/IRANSansWeb(FaNum)_Medium.ttf") format("truetype"); +} + +@font-face { + font-family: IRANSansFaNum; + font-style: normal; + font-weight: 300; + src: url("../fonts/eot/IRANSansWeb(FaNum)_Light.eot"); + src: + url("../fonts/eot/IRANSansWeb(FaNum)_Light.eot?#iefix") + format("embedded-opentype"), + /* IE6-8 */ url("../fonts/woff2/IRANSansWeb(FaNum)_Light.woff2") + format("woff2"), + /* FF39+,Chrome36+, Opera24+*/ + url("../fonts/woff/IRANSansWeb(FaNum)_Light.woff") format("woff"), + /* FF3.6+, IE9, Chrome6+, Saf5.1+*/ + url("../fonts/ttf/IRANSansWeb(FaNum)_Light.ttf") format("truetype"); +} + +@font-face { + font-family: IRANSansFaNum; + font-style: normal; + font-weight: 200; + src: url("../fonts/eot/IRANSansWeb(FaNum)_UltraLight.eot"); + src: + url("../fonts/eot/IRANSansWeb(FaNum)_UltraLight.eot?#iefix") + format("embedded-opentype"), + /* IE6-8 */ url("../fonts/woff2/IRANSansWeb(FaNum)_UltraLight.woff2") + format("woff2"), + /* FF39+,Chrome36+, Opera24+*/ + url("../fonts/woff/IRANSansWeb(FaNum)_UltraLight.woff") format("woff"), + /* FF3.6+, IE9, Chrome6+, Saf5.1+*/ + url("../fonts/ttf/IRANSansWeb(FaNum)_UltraLight.ttf") format("truetype"); +} + +@font-face { + font-family: IRANSansFaNum; + font-style: normal; + font-weight: normal; + src: url("../fonts/eot/IRANSansWeb(FaNum).eot"); + src: + url("../fonts/eot/IRANSansWeb(FaNum).eot?#iefix") + format("embedded-opentype"), + /* IE6-8 */ url("../fonts/woff2/IRANSansWeb(FaNum).woff2") format("woff2"), + /* FF39+,Chrome36+, Opera24+*/ url("../fonts/woff/IRANSansWeb(FaNum).woff") + format("woff"), + /* FF3.6+, IE9, Chrome6+, Saf5.1+*/ + url("../fonts/ttf/IRANSansWeb(FaNum).ttf") format("truetype"); +} + +@font-face { + font-family: IRANSans; + font-style: normal; + font-weight: 900; + src: url("../fonts/eot/IRANSansWeb_Black.eot"); + src: + url("../fonts/eot/IRANSansWeb_Black.eot?#iefix") format("embedded-opentype"), + /* IE6-8 */ url("../fonts/woff2/IRANSansWeb_Black.woff2") format("woff2"), + /* FF39+,Chrome36+, Opera24+*/ url("../fonts/woff/IRANSansWeb_Black.woff") + format("woff"), + /* FF3.6+, IE9, Chrome6+, Saf5.1+*/ + url("../fonts/ttf/IRANSansWeb_Black.ttf") format("truetype"); +} + +@font-face { + font-family: IRANSans; + font-style: normal; + font-weight: bold; + src: url("../fonts/eot/IRANSansWeb_Bold.eot"); + src: + url("../fonts/eot/IRANSansWeb_Bold.eot?#iefix") format("embedded-opentype"), + /* IE6-8 */ url("../fonts/woff2/IRANSansWeb_Bold.woff2") format("woff2"), + /* FF39+,Chrome36+, Opera24+*/ url("../fonts/woff/IRANSansWeb_Bold.woff") + format("woff"), + /* FF3.6+, IE9, Chrome6+, Saf5.1+*/ url("../fonts/ttf/IRANSansWeb_Bold.ttf") + format("truetype"); +} + +@font-face { + font-family: IRANSans; + font-style: normal; + font-weight: 500; + src: url("../fonts/eot/IRANSansWeb_Medium.eot"); + src: + url("../fonts/eot/IRANSansWeb_Medium.eot?#iefix") + format("embedded-opentype"), + /* IE6-8 */ url("../fonts/woff2/IRANSansWeb_Medium.woff2") format("woff2"), + /* FF39+,Chrome36+, Opera24+*/ url("../fonts/woff/IRANSansWeb_Medium.woff") + format("woff"), + /* FF3.6+, IE9, Chrome6+, Saf5.1+*/ + url("../fonts/ttf/IRANSansWeb_Medium.ttf") format("truetype"); +} + +@font-face { + font-family: IRANSans; + font-style: normal; + font-weight: 300; + src: url("../fonts/eot/IRANSansWeb_Light.eot"); + src: + url("../fonts/eot/IRANSansWeb_Light.eot?#iefix") format("embedded-opentype"), + /* IE6-8 */ url("../fonts/woff2/IRANSansWeb_Light.woff2") format("woff2"), + /* FF39+,Chrome36+, Opera24+*/ url("../fonts/woff/IRANSansWeb_Light.woff") + format("woff"), + /* FF3.6+, IE9, Chrome6+, Saf5.1+*/ + url("../fonts/ttf/IRANSansWeb_Light.ttf") format("truetype"); +} + +@font-face { + font-family: IRANSans; + font-style: normal; + font-weight: 200; + src: url("../fonts/eot/IRANSansWeb_UltraLight.eot"); + src: + url("../fonts/eot/IRANSansWeb_UltraLight.eot?#iefix") + format("embedded-opentype"), + /* IE6-8 */ url("../fonts/woff2/IRANSansWeb_UltraLight.woff2") + format("woff2"), + /* FF39+,Chrome36+, Opera24+*/ + url("../fonts/woff/IRANSansWeb_UltraLight.woff") format("woff"), + /* FF3.6+, IE9, Chrome6+, Saf5.1+*/ + url("../fonts/ttf/IRANSansWeb_UltraLight.ttf") format("truetype"); +} + +@font-face { + font-family: IRANSans; + font-style: normal; + font-weight: normal; + src: url("../fonts/eot/IRANSansWeb.eot"); + src: + url("../fonts/eot/IRANSansWeb.eot?#iefix") format("embedded-opentype"), + /* IE6-8 */ url("../fonts/woff2/IRANSansWeb.woff2") format("woff2"), + /* FF39+,Chrome36+, Opera24+*/ url("../fonts/woff/IRANSansWeb.woff") + format("woff"), + /* FF3.6+, IE9, Chrome6+, Saf5.1+*/ url("../fonts/ttf/IRANSansWeb.ttf") + format("truetype"); +} + +@font-face { + font-family: Parastoo; + font-style: normal; + font-weight: normal; + src: url("../fonts/eot/Parastoo.eot"); + src: + url("../fonts/eot/Parastoo.eot?#iefix") format("embedded-opentype"), + /* IE6-8 */ url("../fonts/woff/Parastoo.woff") format("woff"), + /* FF3.6+, IE9, Chrome6+, Saf5.1+*/ url("../fonts/ttf/Parastoo.ttf") + format("truetype"); +} + +@font-face { + font-family: Parastoo; + font-style: normal; + font-weight: bold; + src: url("../fonts/eot/Parastoo-Bold.eot"); + src: + url("../fonts/eot/Parastoo-Bold.eot?#iefix") format("embedded-opentype"), + /* IE6-8 */ url("../fonts/woff/Parastoo-Bold.woff") format("woff"), + /* FF3.6+, IE9, Chrome6+, Saf5.1+*/ url("../fonts/ttf/Parastoo-Bold.ttf") + format("truetype"); +} + +@font-face { + font-family: Bnazanin; + font-style: normal; + font-weight: normal; + src: url("../fonts/ttf/BNazanin.ttf") format("truetype"); +} diff --git a/src/assets/scss/global.scss b/src/assets/scss/global.scss new file mode 100644 index 0000000..ec424d5 --- /dev/null +++ b/src/assets/scss/global.scss @@ -0,0 +1,6 @@ +.filter-toast { + box-shadow: + rgba(50, 50, 93, 0.25) 0px 13px 27px -5px, + rgba(0, 0, 0, 0.3) 0px 8px 16px -8px; + background-color: #d0dfe8; +} diff --git a/src/components/Login/Form/index.jsx b/src/components/Login/Form/index.jsx new file mode 100644 index 0000000..99ae58d --- /dev/null +++ b/src/components/Login/Form/index.jsx @@ -0,0 +1,128 @@ +"use client"; +import LtrTextField from "@/core/components/LtrTextField"; +import StyledForm from "@/core/components/StyledForm"; +import { GET_USER_LOGIN_ROUTE } from "@/core/utils/routes"; +import { useAuth } from "@/lib/contexts/auth"; +import useRequest from "@/lib/hooks/useRequest"; +import { yupResolver } from "@hookform/resolvers/yup"; +import { Button, Container, Stack, Typography } from "@mui/material"; +import { Controller, useForm } from "react-hook-form"; +import { object, string } from "yup"; + +const LoginForm = () => { + const requestServer = useRequest(); + const { getUser } = useAuth(); + const defaultValues = { + username: "", + password: "", + }; + + const validationSchema = object({ + username: string().required("لطفا نام کاربری را وارد کنید!"), + password: string().required("لطفا رمز عبور را وارد کنید!"), + }); + + const { + control, + handleSubmit, + formState: { isSubmitting }, + } = useForm({ + defaultValues, + resolver: yupResolver(validationSchema), + mode: "all", + }); + + const onSubmit = async (data) => { + try { + const formData = new FormData(); + formData.append("username", data.username); + formData.append("password", data.password); + await requestServer(GET_USER_LOGIN_ROUTE, "post", { + data: formData, + }); + getUser(); + } catch (error) { + console.error("Login error:", error); + } + }; + + return ( + + + + + + ورود به سامانه CRM + + + + + { + return ( + + ); + }} + name={"username"} + /> + { + return ( + + ); + }} + name={"password"} + /> + + + + + + + + + ); +}; +export default LoginForm; diff --git a/src/components/Login/LoginLinkRouting.jsx b/src/components/Login/LoginLinkRouting.jsx new file mode 100644 index 0000000..83c5165 --- /dev/null +++ b/src/components/Login/LoginLinkRouting.jsx @@ -0,0 +1,25 @@ +"use client"; +import { Button, Stack } from "@mui/material"; +import Link from "next/link"; +import { useSearchParams } from "next/navigation"; + +const LoginLinkRouting = () => { + const searchParams = useSearchParams(); + + const redirect = searchParams.get("redirect"); + + return ( + + + + ); +}; +export default LoginLinkRouting; diff --git a/src/components/Login/index.jsx b/src/components/Login/index.jsx new file mode 100644 index 0000000..d0f485d --- /dev/null +++ b/src/components/Login/index.jsx @@ -0,0 +1,19 @@ +"use client"; +import { Fade, Stack } from "@mui/material"; +import LoginForm from "./Form"; +import LoginLinkRouting from "./LoginLinkRouting"; +import { Suspense } from "react"; + +const LoginPage = () => { + return ( + + + + + + + + + ); +}; +export default LoginPage; 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 deleted file mode 100644 index 503b571..0000000 --- a/src/components/dashboard/change-password/change-password-form/__test__/index.test.js +++ /dev/null @@ -1,305 +0,0 @@ -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 deleted file mode 100644 index 9b7704a..0000000 --- a/src/components/dashboard/change-password/change-password-form/index.js +++ /dev/null @@ -1,137 +0,0 @@ -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 deleted file mode 100644 index 7f21558..0000000 --- a/src/components/dashboard/change-password/index.jsx +++ /dev/null @@ -1,15 +0,0 @@ -import CenterLayout from "@/layouts/CenterLayout"; -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 deleted file mode 100644 index af9a869..0000000 --- a/src/components/dashboard/edit-profile/index.jsx +++ /dev/null @@ -1,195 +0,0 @@ -import StyledForm from "@/core/components/StyledForm"; -import CenterLayout from "@/layouts/CenterLayout"; -import useUser from "@/lib/app/hooks/useUser"; -import {Box, Container, Grid, Paper, Stack, TextField, Typography,} from "@mui/material"; -import * as Yup from "yup"; -import {Field, Formik} from "formik"; -import {useTranslations} from "next-intl"; -import AvatarUpload from "@/core/components/AvatarUpload"; -import {UPDATE_AVATAR} from "@/core/data/apiRoutes"; -import useDirection from "@/lib/app/hooks/useDirection"; -import ImageResizer from "@/core/components/ImageConvertor"; -import useRequest from "@/lib/app/hooks/useRequest"; - -const DashboardEditProfile = () => { - const t = useTranslations(); - const requestServer = useRequest({auth: true}) - const {user, token, getUser, changeUser} = useUser(); - const {directionApp} = useDirection(); - - const editAvatar = async (avatar) => { - try { - const formData = new FormData(); - - if (avatar != null) { - var resizedAvatar; - resizedAvatar = await ImageResizer(avatar); - formData.append("avatar", resizedAvatar); - } - await requestServer(UPDATE_AVATAR, 'post', {formData}).then((response) => { - }) - .catch(() => { - }).finally(() => { - }); - } catch (error) { - throw error; - } - }; - const handleSubmit = (values, {setSubmitting}) => { - - }; - const initialValues = { - expert_avatar: null, - username: user.username, - name: user.name, - email: user.email, - province_name: user.province_name, - position: user.position, - }; - const validationSchema = Yup.object().shape({}); - - return ( - - - - {(props) => ( - { - e.preventDefault(); - props.handleSubmit(); - }} - > - - - - {t("UpdateProfile.typography_edit_profile")} - - - - - - - - - - - - - - - - - - - - - - - - - - )} - - - - ); -}; - -export default DashboardEditProfile; diff --git a/src/components/dashboard/expert-management/DataTable/__tests__/index.test.js b/src/components/dashboard/expert-management/DataTable/__tests__/index.test.js deleted file mode 100644 index cfc0a60..0000000 --- a/src/components/dashboard/expert-management/DataTable/__tests__/index.test.js +++ /dev/null @@ -1,35 +0,0 @@ -import {render, screen} from "@testing-library/react"; -import MockAppWithProviders from "../../../../../../mocks/AppWithProvider"; -import ExpertManagementDataTable from "@/components/dashboard/expert-management/DataTable"; - -describe("ExpertManagementDatatable Component From Expert Management", () => { - describe("Rendering", () => { - it("Table Headers Rendered", () => { - render(); - const idHeader = screen.queryByText("کد یکتا"); - const nameHeader = screen.queryByText("نام کامل"); - const usernameHeader = screen.queryByText("نام کاربری"); - const emailHeader = screen.queryByText("پست الکترونیک"); - const telephone_idHeader = screen.queryByText("کد تلفن"); - const phone_numberHeader = screen.queryByText("شماره همراه"); - const national_idHeader = screen.queryByText("کد ملی"); - const positionHeader = screen.queryByText("سمت"); - const province_nameHeader = screen.queryByText("استان"); - const role_nameHeader = screen.queryByText("نقش"); - const genderHeader = screen.queryByText("جنسیت"); - const updated_atHeader = screen.queryByText("آخرین بروزرسانی"); - expect(idHeader).toBeInTheDocument(); - expect(nameHeader).toBeInTheDocument(); - expect(usernameHeader).toBeInTheDocument(); - expect(emailHeader).toBeInTheDocument(); - expect(telephone_idHeader).toBeInTheDocument(); - expect(phone_numberHeader).toBeInTheDocument(); - expect(national_idHeader).toBeInTheDocument(); - expect(positionHeader).toBeInTheDocument(); - expect(province_nameHeader).toBeInTheDocument(); - expect(role_nameHeader).toBeInTheDocument(); - expect(genderHeader).toBeInTheDocument(); - expect(updated_atHeader).toBeInTheDocument(); - }); - }); -}); \ No newline at end of file diff --git a/src/components/dashboard/expert-management/DataTable/index.jsx b/src/components/dashboard/expert-management/DataTable/index.jsx deleted file mode 100644 index 41dd1e6..0000000 --- a/src/components/dashboard/expert-management/DataTable/index.jsx +++ /dev/null @@ -1,202 +0,0 @@ -import DataTable from "@/core/components/DataTable"; -import MuiDatePicker from "@/core/components/MuiDatePicker"; -import {GET_EXPERTS} from "@/core/data/apiRoutes"; -import TableToolbar from "../TableToolbar"; -import TableRowActions from "../TableRowActions"; -import {Box, Typography} from "@mui/material"; -import {useTranslations} from "next-intl"; -import {useMemo} from "react"; -import moment from "jalali-moment"; - -function ExpertManagementDataTable() { - const t = useTranslations(); - const columns = useMemo( - () => [ - { - accessorFn: (row) => row.id, - id: "id", - header: t("ExpertMangement.id"), - enableColumnFilter: true, - datatype: "numeric", - filterFn: "equals", - columnFilterModeOptions: [ - "equals", - "notEquals", - "contains", - "lessThan", - "greaterThan", - "between", - ], - Cell: ({renderedCellValue}) => ( - {renderedCellValue} - ), - }, - { - accessorFn: (row) => row.full_name, - id: "full_name", - header: t("ExpertMangement.name"), - enableColumnFilter: true, - datatype: "text", - filterFn: "contains", - columnFilterModeOptions: ["contains", "equals", "notEquals"], - Cell: ({renderedCellValue}) => ( - {renderedCellValue} - ), - }, - { - accessorFn: (row) => row.username, - id: "username", - header: t("ExpertMangement.username"), - enableColumnFilter: true, - datatype: "text", - filterFn: "contains", - columnFilterModeOptions: ["contains", "equals", "notEquals"], - Cell: ({renderedCellValue}) => ( - {renderedCellValue} - ), - }, - { - accessorFn: (row) => row.email, - id: "email", - header: t("ExpertMangement.email"), - enableColumnFilter: true, - datatype: "text", - filterFn: "contains", - columnFilterModeOptions: ["contains", "equals", "notEquals"], - Cell: ({renderedCellValue}) => ( - {renderedCellValue} - ), - }, - { - accessorFn: (row) => row.telephone_id, - id: "telephone_id", - header: t("ExpertMangement.telephone_id"), - enableColumnFilter: true, - datatype: "numeric", - filterFn: "equals", - columnFilterModeOptions: ["contains", "equals", "notEquals"], - Cell: ({renderedCellValue}) => ( - {renderedCellValue} - ), - }, - { - accessorFn: (row) => row.phone_number, - id: "phone_number", - header: t("ExpertMangement.phone_number"), - enableColumnFilter: true, - datatype: "numeric", - filterFn: "equals", - columnFilterModeOptions: ["contains", "equals", "notEquals"], - Cell: ({renderedCellValue}) => ( - {renderedCellValue} - ), - }, - { - accessorFn: (row) => row.national_id, - id: "national_id", - header: t("ExpertMangement.national_id"), - enableColumnFilter: true, - datatype: "numeric", - filterFn: "equals", - columnFilterModeOptions: ["contains", "equals", "notEquals"], - Cell: ({renderedCellValue}) => ( - {renderedCellValue} - ), - }, - { - accessorFn: (row) => row.position, - id: "position", - header: t("ExpertMangement.position"), - enableColumnFilter: true, - datatype: "text", - filterFn: "contains", - columnFilterModeOptions: ["contains", "equals", "notEquals"], - Cell: ({renderedCellValue}) => ( - {renderedCellValue} - ), - }, - { - accessorFn: (row) => row.province_fa, - id: "province_id", - header: t("ExpertMangement.province_fa"), - enableColumnFilter: false, - datatype: "text", - filterFn: "contains", - columnFilterModeOptions: ["contains", "equals", "notEquals"], - Cell: ({renderedCellValue}) => ( - {renderedCellValue} - ), - }, - { - accessorFn: (row) => row.roles[0]?.name_fa, - id: "roles[0].name_fa", - header: t("ExpertMangement.role_name"), - enableColumnFilter: false, - enableSorting: false, - datatype: "text", - filterFn: "contains", - columnFilterModeOptions: ["contains", "equals", "notEquals"], - Cell: ({renderedCellValue}) => ( - {renderedCellValue} - ), - }, - { - accessorFn: (row) => row.gender, - id: "gender", - header: t("ExpertMangement.gender"), - enableColumnFilter: false, - datatype: "text", - filterFn: "contains", - columnFilterModeOptions: ["contains", "equals", "notEquals"], - Cell: ({renderedCellValue}) => ( - {renderedCellValue === "male" ? t("ExpertMangement.male") : t("ExpertMangement.female")} - ), - }, - { - accessorFn: (row) => - moment(row.updated_at).locale("fa").format("HH:mm | yyyy/MM/DD"), - id: "updated_at", - header: t("ExpertMangement.updated_at"), - enableColumnFilter: true, - datatype: "date", - filterFn: "lessThan", - columnFilterModeOptions: ["lessThan", "greaterThan"], - Cell: ({renderedCellValue}) => { - return {renderedCellValue}; - }, - Header: ({column}) => <>{column.columnDef.header}, - Filter: ({column}) => { - return ( - - ); - }, - }, - ], - [] - ); - return ( - - - - ); -} - -export default ExpertManagementDataTable; \ No newline at end of file diff --git a/src/components/dashboard/expert-management/Form/ChangePassword/index.jsx b/src/components/dashboard/expert-management/Form/ChangePassword/index.jsx deleted file mode 100644 index ea3d11c..0000000 --- a/src/components/dashboard/expert-management/Form/ChangePassword/index.jsx +++ /dev/null @@ -1,7 +0,0 @@ -const ChangePassword = () => { - return ( - <> - ) -} - -export default ChangePassword \ No newline at end of file diff --git a/src/components/dashboard/expert-management/Form/CreateForm/CreateContent/PersonalInfo/__tests__/index.test.js b/src/components/dashboard/expert-management/Form/CreateForm/CreateContent/PersonalInfo/__tests__/index.test.js deleted file mode 100644 index 913c37a..0000000 --- a/src/components/dashboard/expert-management/Form/CreateForm/CreateContent/PersonalInfo/__tests__/index.test.js +++ /dev/null @@ -1,219 +0,0 @@ -import {act, fireEvent, render, screen, waitFor} from "@testing-library/react"; -import React from 'react'; -import MockAppWithProviders from "../../../../../../../../../mocks/AppWithProvider"; -import * as Yup from "yup"; -import {Formik} from "formik"; -import PersonalInfo from "@/components/dashboard/expert-management/Form/CreateForm/CreateContent/PersonalInfo"; -import {useTranslations} from "next-intl"; -import Province from "@/components/dashboard/expert-management/Form/CreateForm/CreateContent/RestInfo/Province"; - - -const CreateFormMock = ({Component}) => { - const t = useTranslations(); - const validationSchema = Yup.object().shape({ - full_name: Yup.string().required(t("ExpertMangement.error_message_full_name")), - email: Yup.string().required(t("ExpertMangement.error_message_email")), - phone_number: Yup.string().required(t("ExpertMangement.error_message_phone_number")), - telephone_id: Yup.string().required(t("ExpertMangement.error_message_telephone_id")), - national_id: Yup.string().required(t("ExpertMangement.error_message_national_id")), - gender: Yup.string().required(t("ExpertMangement.error_message_gender")) - }); - return ( - { - }} - > - {formikProps => ()} - - ); -}; - - -describe("PersonalInfo Component From Expert Management (create)", () => { - describe("Rendering", () => { - it("full_name TextField Rendered", async () => { - render(); - await waitFor(() => { - const fullNameTextField = screen.queryByLabelText('نام کامل'); - expect(fullNameTextField).toBeInTheDocument(); - }); - }); - it("Email TextField Rendered", async () => { - render(); - await waitFor(() => { - const emailTextField = screen.queryByLabelText('پست الکترونیک'); - expect(emailTextField).toBeInTheDocument(); - }); - }); - it("Phone Number TextField Rendered", async () => { - render(); - await waitFor(() => { - const phoneNumberTextField = screen.queryByLabelText('شماره همراه'); - expect(phoneNumberTextField).toBeInTheDocument(); - }); - }); - it("telephone id TextField Rendered", async () => { - render(); - await waitFor(() => { - const telephoneIdTextField = screen.queryByLabelText('کد تلفن'); - expect(telephoneIdTextField).toBeInTheDocument(); - }); - }); - it("National Id TextField Rendered", async () => { - render(); - await waitFor(() => { - const nationalIdTextField = screen.queryByLabelText('کد ملی'); - expect(nationalIdTextField).toBeInTheDocument(); - }); - }); - it("Gender Select Box Rendered", async () => { - render(); - await waitFor(() => { - const genderSelectBox = screen.getByTestId('select-box'); - expect(genderSelectBox).toBeInTheDocument(); - }); - }); - }); - describe("Behavioral", () => { - it("Name TextField Work Correctly", async () => { - render(); - const nameTextField = screen.queryByLabelText('نام کامل'); - fireEvent.change(nameTextField, {target: {value: 'exampleName'}}); - await act(() => { - expect(nameTextField).toHaveValue('exampleName'); - }) - }); - it("Email TextField Work Correctly", async () => { - render(); - const emailTextField = screen.queryByLabelText('پست الکترونیک'); - fireEvent.change(emailTextField, {target: {value: 'exampleEmail'}}); - await act(() => { - expect(emailTextField).toHaveValue('exampleEmail'); - }) - }); - it("Phone Number TextField Work Correctly", async () => { - render(); - const phoneNumberTextField = screen.queryByLabelText('شماره همراه'); - fireEvent.change(phoneNumberTextField, {target: {value: 'examplePhoneNumber'}}); - await act(() => { - expect(phoneNumberTextField).toHaveValue('examplePhoneNumber'); - }) - }); - it("Telephone Id TextField Work Correctly", async () => { - render(); - const telephoneIdTextField = screen.queryByLabelText('کد تلفن'); - fireEvent.change(telephoneIdTextField, {target: {value: 'exampleTelephoneId'}}); - await act(() => { - expect(telephoneIdTextField).toHaveValue('exampleTelephoneId'); - }) - }); - it("National Id TextField Work Correctly", async () => { - render(); - const nationalIdTextField = screen.queryByLabelText('کد ملی'); - fireEvent.change(nationalIdTextField, {target: {value: 'exampleNationalId'}}); - await act(() => { - expect(nationalIdTextField).toHaveValue('exampleNationalId'); - }) - }); - it("Gender Select Box Work Correctly", async () => { - render(); - const genderSelectBox = screen.getByTestId('select-box'); - const genderSelectOpener = await waitFor(() => screen.getByTestId("option-opener-gender")); - - await waitFor(() => { - expect(genderSelectBox).toBeInTheDocument(); - }); - - fireEvent.mouseDown(genderSelectOpener); - await waitFor(() => { - const selectItem = screen.queryByText("مرد"); - expect(selectItem).toBeInTheDocument(); - }); - - }); - }); - describe("validation", () => { - it('Should See Error When Name Input Is Empty', async () => { - render(); - - const nameInput = screen.queryByLabelText('نام کامل'); - - fireEvent.change(nameInput, {target: {value: ''}}); - fireEvent.blur(nameInput); - - await waitFor(() => { - expect(screen.queryByText("نام کامل خود را وارد کنید")).toBeInTheDocument() - }); - }); - it('Should See Error When Email Input Is Empty', async () => { - render(); - - const emailInput = screen.queryByLabelText('پست الکترونیک'); - - fireEvent.change(emailInput, {target: {value: ''}}); - fireEvent.blur(emailInput); - - await waitFor(() => { - expect(screen.queryByText("پست الکترونیک خود را وارد کنید")).toBeInTheDocument() - }); - }); - it('Should See Error When Phone Number Input Is Empty', async () => { - render(); - - const phoneNumberInput = screen.queryByLabelText('شماره همراه'); - - fireEvent.change(phoneNumberInput, {target: {value: ''}}); - fireEvent.blur(phoneNumberInput); - - await waitFor(() => { - expect(screen.queryByText("شماره همراه خود را وارد کنید")).toBeInTheDocument() - }); - }); - it('Should See Error When telephone Id Input Is Empty', async () => { - render(); - - const telephoneIdTextField = screen.queryByLabelText('کد تلفن'); - - fireEvent.change(telephoneIdTextField, {target: {value: ''}}); - fireEvent.blur(telephoneIdTextField); - - await waitFor(() => { - expect(screen.queryByText("کد تلفن خود را وارد کنید")).toBeInTheDocument() - }); - }); - it('Should See Error When National Id Input Is Empty', async () => { - render(); - - const nationalIdInput = screen.queryByLabelText('کد ملی'); - - fireEvent.change(nationalIdInput, {target: {value: ''}}); - fireEvent.blur(nationalIdInput); - - await waitFor(() => { - expect(screen.queryByText("کد ملی خود را وارد کنید")).toBeInTheDocument() - }); - }); - it('Should Select An Item With Valid Value of gender', async () => { - render(); - const genderInput = screen.getByTestId("input-gender") - const genderSelectOpener = await waitFor(() => screen.getByTestId("option-opener-gender")); - - fireEvent.mouseDown(genderSelectOpener); - const selectItem = await waitFor(() => screen.queryByText("مرد")); - expect(selectItem).toBeInTheDocument(); - fireEvent.click(selectItem); - await waitFor(() => { - expect(genderInput.value).toBe('male') - }); - }); - }) -}); \ No newline at end of file diff --git a/src/components/dashboard/expert-management/Form/CreateForm/CreateContent/PersonalInfo/index.jsx b/src/components/dashboard/expert-management/Form/CreateForm/CreateContent/PersonalInfo/index.jsx deleted file mode 100644 index f92bf38..0000000 --- a/src/components/dashboard/expert-management/Form/CreateForm/CreateContent/PersonalInfo/index.jsx +++ /dev/null @@ -1,128 +0,0 @@ -import {FormControl, FormHelperText, Grid, InputLabel, MenuItem, Select, TextField} from "@mui/material"; -import {useTranslations} from "next-intl"; - -const PersonalInfo = ({formik}) => { - const t = useTranslations(); - - const genderList = [ - {id: 1, name_en: "male", name_fa: "مرد"}, - {id: 2, name_en: "female", name_fa: "زن"} - ] - - return ( - - - - - - - - - - - - - - - - - - - {t("ExpertMangement.text_field_gender")} - - - {formik.touched.gender && formik.errors.gender ? formik.errors.gender : ""} - - - - - ) -} -export default PersonalInfo \ No newline at end of file diff --git a/src/components/dashboard/expert-management/Form/CreateForm/CreateContent/RestInfo/PositionAndRole/__tests__/index.test.js b/src/components/dashboard/expert-management/Form/CreateForm/CreateContent/RestInfo/PositionAndRole/__tests__/index.test.js deleted file mode 100644 index 6f32bac..0000000 --- a/src/components/dashboard/expert-management/Form/CreateForm/CreateContent/RestInfo/PositionAndRole/__tests__/index.test.js +++ /dev/null @@ -1,96 +0,0 @@ -import {fireEvent, render, screen, waitFor} from "@testing-library/react"; -import React from 'react'; -import {useTranslations} from "next-intl"; -import * as Yup from "yup"; -import {Formik} from "formik"; -import MockAppWithProviders from "../../../../../../../../../../mocks/AppWithProvider"; -import PositionAndRole from "../../PositionAndRole"; - -const CreateFormMock = ({Component}) => { - const t = useTranslations(); - const validationSchema = Yup.object().shape({ - position: Yup.string().required(t("ExpertMangement.error_message_position")), - roles: Yup.string().required(t("ExpertMangement.error_message_roles")) - }); - return ( - { - }} - > - {formikProps => ()} - - ); -}; - -describe("PositionAndRole Component From Expert Management (create)", () => { - describe("Rendering", () => { - it("Position TextField Rendered", async () => { - render(); - await waitFor(() => { - const positionTextField = screen.queryByLabelText('سمت'); - expect(positionTextField).toBeInTheDocument(); - }); - }); - it("Role Select Box Rendered", async () => { - render(); - const roleSelect = screen.getByTestId('select-box'); - await waitFor(() => { - expect(roleSelect).toBeInTheDocument(); - }); - }); - }); - describe("Behavioral", () => { - it("Position TextField Work Correctly", async () => { - render(); - const positionTextField = screen.queryByLabelText('سمت'); - fireEvent.change(positionTextField, {target: {value: 'examplePosition'}}); - await waitFor(() => { - expect(positionTextField).toHaveValue('examplePosition'); - }) - }); - it("Role Select Box Work Correctly", async () => { - render(); - const roleSelect = screen.getByTestId("option-opener-role"); - await waitFor(() => { - expect(roleSelect).toBeInTheDocument(); - }); - fireEvent.mouseDown(roleSelect); - await waitFor(() => { - const selectItem = screen.queryByText("ادمین"); - expect(selectItem).toBeInTheDocument(); - }); - }); - }); - describe("validation", () => { - it('Should See Error When Position Input Is Empty', async () => { - render(); - - const positionInput = screen.queryByLabelText('سمت'); - - fireEvent.change(positionInput, {target: {value: ''}}); - fireEvent.blur(positionInput); - - await waitFor(() => { - expect(screen.queryByText("سمت خود را وارد کنید")).toBeInTheDocument() - }); - }); - it('Should Select An Item With Valid Value of role', async () => { - render(); - const roleInput = await waitFor(() => screen.getByTestId("input-role-id")); - const roleSelectOpener = await waitFor(() => screen.getByTestId("option-opener-role")); - - fireEvent.mouseDown(roleSelectOpener); - const selectItem = await waitFor(() => screen.queryByText("ادمین")); - expect(selectItem).toBeInTheDocument(); - fireEvent.click(selectItem); - await waitFor(() => { - expect(roleInput.value).toBe('1') - }); - }); - }) -}); \ No newline at end of file diff --git a/src/components/dashboard/expert-management/Form/CreateForm/CreateContent/RestInfo/PositionAndRole/index.jsx b/src/components/dashboard/expert-management/Form/CreateForm/CreateContent/RestInfo/PositionAndRole/index.jsx deleted file mode 100644 index 6befbff..0000000 --- a/src/components/dashboard/expert-management/Form/CreateForm/CreateContent/RestInfo/PositionAndRole/index.jsx +++ /dev/null @@ -1,74 +0,0 @@ -import {FormControl, FormHelperText, Grid, InputLabel, MenuItem, Select, TextField} from "@mui/material"; -import {useTranslations} from "next-intl"; -import useRequest from "@/lib/app/hooks/useRequest"; -import useRole from "@/lib/app/hooks/useRole"; -import {log} from "next/dist/server/typescript/utils"; - -const PositionAndRole = ({formik}) => { - const t = useTranslations(); - const requestServer = useRequest() - const {roleList, isLoadingRoleList, errorRoleList} = useRole(); - - return ( - - - - - - - {t("ExpertMangement.text_field_roles")} - - - {formik.touched.roles && formik.errors.roles ? formik.errors.roles : ""} - - - - - ) -} -export default PositionAndRole \ No newline at end of file diff --git a/src/components/dashboard/expert-management/Form/CreateForm/CreateContent/RestInfo/Province/__tests__/index.test.js b/src/components/dashboard/expert-management/Form/CreateForm/CreateContent/RestInfo/Province/__tests__/index.test.js deleted file mode 100644 index 87da19f..0000000 --- a/src/components/dashboard/expert-management/Form/CreateForm/CreateContent/RestInfo/Province/__tests__/index.test.js +++ /dev/null @@ -1,71 +0,0 @@ -import {fireEvent, render, screen, waitFor} from "@testing-library/react"; -import React from 'react'; -import {useTranslations} from "next-intl"; -import * as Yup from "yup"; -import {Formik} from "formik"; -import Province from "../../Province"; -import MockAppWithProviders from "../../../../../../../../../../mocks/AppWithProvider"; - -const CreateFormMock = ({Component}) => { - const t = useTranslations(); - const validationSchema = Yup.object().shape({ - province_id: Yup.string().required(t("ExpertMangement.error_message_province_id")), - }); - return ( - { - }} - > - {formikProps => ()} - - ); -}; - -describe("Province Component From Expert Management (create)", () => { - describe("Rendering", () => { - it("Province Select Box Rendered", async () => { - render(); - await waitFor(() => { - const provinceSelectBox = screen.getByTestId('select-box'); - expect(provinceSelectBox).toBeInTheDocument(); - }); - }); - }); - describe("Behavioral", () => { - it("Province Select Box Work Correctly", async () => { - render(); - const provinceSelectBox = screen.getByTestId('select-box'); - const provinceSelectOpener = await waitFor(() => screen.getByTestId("option-opener-province")); - - await waitFor(() => { - expect(provinceSelectBox).toBeInTheDocument(); - }); - - fireEvent.mouseDown(provinceSelectOpener); - await waitFor(() => { - const selectItem = screen.queryByText("آذربایجان شرقی"); - expect(selectItem).toBeInTheDocument(); - }); - - }); - }); - describe("validation", () => { - it('Should Select An Item With Valid Value of province', async () => { - render(); - const provinceInput = screen.getByTestId("input-province-id") - const provinceSelectOpener = await waitFor(() => screen.getByTestId("option-opener-province")); - - fireEvent.mouseDown(provinceSelectOpener); - const selectItem = await waitFor(() => screen.queryByText("آذربایجان شرقی")); - expect(selectItem).toBeInTheDocument(); - fireEvent.click(selectItem); - await waitFor(() => { - expect(provinceInput.value).toBe('1') - }); - }); - }) -}); \ No newline at end of file diff --git a/src/components/dashboard/expert-management/Form/CreateForm/CreateContent/RestInfo/Province/index.jsx b/src/components/dashboard/expert-management/Form/CreateForm/CreateContent/RestInfo/Province/index.jsx deleted file mode 100644 index 5ac94b2..0000000 --- a/src/components/dashboard/expert-management/Form/CreateForm/CreateContent/RestInfo/Province/index.jsx +++ /dev/null @@ -1,61 +0,0 @@ -import {FormControl, FormHelperText, Grid, InputLabel, MenuItem, Select} from "@mui/material"; -import {useTranslations} from "next-intl"; -import useProvince from "@/lib/app/hooks/useProvince"; -import useRequest from "@/lib/app/hooks/useRequest"; - -const Province = ({formik}) => { - const t = useTranslations(); - const requestServer = useRequest() - const {provinceList, isLoadingProvinceList, errorProvinceList} = useProvince(); - - return ( - - - - {t("ExpertMangement.text_field_province_id")} - - - {formik.touched.province_id && formik.errors.province_id ? formik.errors.province_id : ""} - - - - - ) -} -export default Province \ No newline at end of file diff --git a/src/components/dashboard/expert-management/Form/CreateForm/CreateContent/RestInfo/index.jsx b/src/components/dashboard/expert-management/Form/CreateForm/CreateContent/RestInfo/index.jsx deleted file mode 100644 index 072c096..0000000 --- a/src/components/dashboard/expert-management/Form/CreateForm/CreateContent/RestInfo/index.jsx +++ /dev/null @@ -1,12 +0,0 @@ -import Province from "./Province"; -import PositionAndRole from "./PositionAndRole"; - -const RestInfo = ({formik}) => { - return ( - <> - - - - ) -} -export default RestInfo \ No newline at end of file diff --git a/src/components/dashboard/expert-management/Form/CreateForm/CreateContent/UserInfo/__test__/index.test.js b/src/components/dashboard/expert-management/Form/CreateForm/CreateContent/UserInfo/__test__/index.test.js deleted file mode 100644 index 519cf9d..0000000 --- a/src/components/dashboard/expert-management/Form/CreateForm/CreateContent/UserInfo/__test__/index.test.js +++ /dev/null @@ -1,123 +0,0 @@ -import {act, fireEvent, render, screen, waitFor} from "@testing-library/react"; -import React from 'react'; -import MockAppWithProviders from "../../../../../../../../../mocks/AppWithProvider"; -import {useTranslations} from "next-intl"; -import * as Yup from "yup"; -import {Formik} from "formik"; -import UserInfo from "../../UserInfo"; - -const CreateFormMock = ({Component}) => { - const t = useTranslations(); - const validationSchema = Yup.object().shape({ - username: Yup.string().required(t("ExpertMangement.error_message_username")), - password: Yup.string() - .required(t("ExpertMangement.error_message_password")) - .matches( - /^(?=.*[a-zA-Z])(?=.*\d).{8,}$/, - t("ExpertMangement.error_message_password_regex") - ), - }); - return ( - { - }} - > - {formikProps => ()} - - ); -}; - -describe("UserInfo Component From Expert Management (create)", () => { - describe("Rendering", () => { - it("UserName TextField Rendered", async () => { - render(); - await waitFor(() => { - const usernameTextField = screen.queryByLabelText('نام کاربری'); - expect(usernameTextField).toBeInTheDocument(); - }); - }); - it("Password TextField Rendered", async () => { - render(); - await waitFor(() => { - const passwordTextField = screen.queryByLabelText('رمز عبور'); - expect(passwordTextField).toBeInTheDocument(); - }); - - }); - }); - describe("Behavioral", () => { - it("Username TextField Work Correctly", async () => { - render(); - const usernameTextField = screen.queryByLabelText('نام کاربری'); - fireEvent.change(usernameTextField, {target: {value: 'exampleUsername'}}); - await act(() => { - expect(usernameTextField).toHaveValue('exampleUsername'); - }) - }); - it("Password TextField Work Correctly", async () => { - render(); - const passwordTextField = screen.queryByLabelText('رمز عبور'); - fireEvent.change(passwordTextField, {target: {value: 'examplePassword'}}); - await act(() => { - expect(passwordTextField).toHaveValue('examplePassword'); - }) - }); - }); - describe("validation", () => { - it('Should See Error When Username Input Is Empty', async () => { - render(); - - const usernameInput = screen.queryByLabelText('نام کاربری'); - - fireEvent.change(usernameInput, {target: {value: ''}}); - fireEvent.blur(usernameInput); - - await waitFor(() => { - expect(screen.queryByText("نام کاربری خود را وارد کنید")).toBeInTheDocument() - }); - }); - it('Should See Error When Password Input Is Empty', async () => { - render(); - - const passwordInput = screen.queryByLabelText('رمز عبور'); - - fireEvent.change(passwordInput, {target: {value: ''}}); - fireEvent.blur(passwordInput); - - await waitFor(() => { - expect(screen.queryByText("رمز عبور خود را وارد کنید")).toBeInTheDocument() - }); - }); - it('Should See Error When Password Field Is Not On Correct Format', async () => { - render(); - - const passwordInput = screen.queryByLabelText('رمز عبور'); - - // check without text or symbol - fireEvent.change(passwordInput, {target: {value: '12345678'}}); - fireEvent.blur(passwordInput); - await waitFor(() => { - expect(screen.queryByText("رمز عبور باید حداقل 8 کاراکتر و متشکل از عدد، حرف یا سمبل باشد")).toBeInTheDocument() - }); - - // check without number - fireEvent.change(passwordInput, {target: {value: 'abcdefgh'}}); - fireEvent.blur(passwordInput); - await waitFor(() => { - expect(screen.queryByText("رمز عبور باید حداقل 8 کاراکتر و متشکل از عدد، حرف یا سمبل باشد")).toBeInTheDocument() - }); - - // check under 8 character - fireEvent.change(passwordInput, {target: {value: '11Sa'}}); - fireEvent.blur(passwordInput); - await waitFor(() => { - expect(screen.queryByText("رمز عبور باید حداقل 8 کاراکتر و متشکل از عدد، حرف یا سمبل باشد")).toBeInTheDocument() - }); - }); - }) -}); \ No newline at end of file diff --git a/src/components/dashboard/expert-management/Form/CreateForm/CreateContent/UserInfo/index.jsx b/src/components/dashboard/expert-management/Form/CreateForm/CreateContent/UserInfo/index.jsx deleted file mode 100644 index 2455b26..0000000 --- a/src/components/dashboard/expert-management/Form/CreateForm/CreateContent/UserInfo/index.jsx +++ /dev/null @@ -1,61 +0,0 @@ -import {Grid, IconButton, InputAdornment, TextField} from "@mui/material"; -import {useTranslations} from "next-intl"; -import {Visibility, VisibilityOff} from "@mui/icons-material"; -import {useState} from "react"; - -const UserInfo = ({formik}) => { - const t = useTranslations(); - const [showPassword, setShowPassword] = useState(false); - - const handleClickShowPassword = () => { - setShowPassword(!showPassword); - }; - - return ( - <> - - - - - - - - {showPassword ? : } - - - ), - }} - sx={{width: "100%"}} - /> - - - - ) -} -export default UserInfo \ No newline at end of file diff --git a/src/components/dashboard/expert-management/Form/CreateForm/CreateContent/__tests__/index.test.js b/src/components/dashboard/expert-management/Form/CreateForm/CreateContent/__tests__/index.test.js deleted file mode 100644 index 85751c5..0000000 --- a/src/components/dashboard/expert-management/Form/CreateForm/CreateContent/__tests__/index.test.js +++ /dev/null @@ -1,67 +0,0 @@ -import {fireEvent, render, screen, waitFor} from "@testing-library/react"; -import React from 'react'; -import CreateContent from "../../CreateContent"; -import MockAppWithProviders from "../../../../../../../../mocks/AppWithProvider"; - -function selectDropdownItem(screen, openerTestId, itemText) { - const opener = screen.getByTestId(openerTestId); - fireEvent.mouseDown(opener); - const selectItem = screen.queryByText(itemText); - fireEvent.click(selectItem); -} - -function setInputValue(screen, inputElement, value) { - fireEvent.change(inputElement, {target: {value}}); -} - -describe("CreateContent Component From Expert Management (create)", () => { - describe("Rendering", () => { - it("close button Rendered", async () => { - render(); - await waitFor(() => { - const cancelBtn = screen.queryByText('بستن'); - expect(cancelBtn).toBeInTheDocument(); - }); - }); - it("confirm button Rendered", async () => { - render(); - await waitFor(() => { - const cancelBtn = screen.queryByText('ثبت'); - expect(cancelBtn).toBeInTheDocument(); - }); - }); - }); - describe("Form Submission", () => { - it('Should enable the submit button when the inputs are valid', async () => { - render(); - - const submitButton = screen.queryByText('ثبت'); - - const nameInput = screen.queryByLabelText('نام کامل'); - const usernameInput = screen.queryByLabelText('نام کاربری'); - const emailInput = screen.queryByLabelText('پست الکترونیک'); - const phoneNumberInput = screen.queryByLabelText('شماره همراه'); - const telephoneIdInput = screen.queryByLabelText('کد تلفن'); - const nationalIdInput = screen.queryByLabelText('کد ملی'); - const passwordInput = screen.queryByLabelText('رمز عبور'); - const positionInput = screen.queryByLabelText('سمت'); - - selectDropdownItem(screen, "option-opener-province", "آذربایجان شرقی"); - selectDropdownItem(screen, "option-opener-role", "ادمین"); - selectDropdownItem(screen, "option-opener-gender", "مرد"); - - setInputValue(screen, nameInput, 'nameTest'); - setInputValue(screen, usernameInput, 'usernameTest'); - setInputValue(screen, emailInput, 'emailTest'); - setInputValue(screen, phoneNumberInput, 'phoneNumberTest'); - setInputValue(screen, telephoneIdInput, 'telephoneIdTest'); - setInputValue(screen, nationalIdInput, 'nationalIdTest'); - setInputValue(screen, passwordInput, 'passwordTest'); - setInputValue(screen, positionInput, 'positionTest'); - - await waitFor(() => { - expect(submitButton).not.toBeDisabled(); - }); - }); - }) -}); \ No newline at end of file diff --git a/src/components/dashboard/expert-management/Form/CreateForm/CreateContent/index.jsx b/src/components/dashboard/expert-management/Form/CreateForm/CreateContent/index.jsx deleted file mode 100644 index 084da99..0000000 --- a/src/components/dashboard/expert-management/Form/CreateForm/CreateContent/index.jsx +++ /dev/null @@ -1,116 +0,0 @@ -import {Box, Button, Chip, DialogActions, DialogContent, Divider,} from "@mui/material"; -import * as Yup from "yup"; -import {useTranslations} from "next-intl"; -import {useFormik} from "formik"; -import useRequest from "@/lib/app/hooks/useRequest"; -import {ADD_EXPERT} from "@/core/data/apiRoutes"; -import PersonalInfo from "@/components/dashboard/expert-management/Form/CreateForm/CreateContent/PersonalInfo"; -import UserInfo from "@/components/dashboard/expert-management/Form/CreateForm/CreateContent/UserInfo"; -import RestInfo from "@/components/dashboard/expert-management/Form/CreateForm/CreateContent/RestInfo"; - -const CreateContent = ({setOpenCreateDialog, mutate}) => { - const t = useTranslations(); - const requestServer = useRequest() - - const validationSchema = Yup.object().shape({ - full_name: Yup.string().required(t("ExpertMangement.error_message_full_name")), - username: Yup.string().required(t("ExpertMangement.error_message_username")), - email: Yup.string().required(t("ExpertMangement.error_message_email")), - phone_number: Yup.string().required(t("ExpertMangement.error_message_phone_number")), - telephone_id: Yup.string().required(t("ExpertMangement.error_message_telephone_id")), - gender: Yup.string().required(t("ExpertMangement.error_message_gender")), - national_id: Yup.string().required(t("ExpertMangement.error_message_national_id")), - password: Yup.string() - .required(t("ExpertMangement.error_message_password")) - .matches( - /^(?=.*[a-zA-Z])(?=.*\d).{8,}$/, - t("ExpertMangement.error_message_password_regex") - ), - position: Yup.string().required(t("ExpertMangement.error_message_position")), - province_id: Yup.string().required(t("ExpertMangement.error_message_province_id")), - roles: Yup.string().required(t("ExpertMangement.error_message_roles")) - }); - - const formik = useFormik({ - initialValues: { - full_name: "", - username: "", - email: "", - phone_number: "", - telephone_id: "", - gender: "", - national_id: "", - password: "", - position: "", - province_id: "", - roles: "" - }, - validationSchema, - onSubmit: (values, {setSubmitting}) => { - const formData = new FormData(); - formData.append("full_name", values.full_name); - formData.append("national_id", values.national_id); - formData.append("phone_number", values.phone_number); - formData.append("telephone_id", values.telephone_id); - formData.append("gender", values.gender); - formData.append("email", values.email); - formData.append("username", values.username); - formData.append("password", values.password); - formData.append("province_id", values.province_id); - formData.append("position", values.position); - formData.append("roles", values.roles); - - requestServer(`${ADD_EXPERT}`, 'post', {auth: true, data: formData}) - .then((response) => { - setOpenCreateDialog(false) - mutate() - }).catch(() => { - }).finally(() => { - setSubmitting(false); - }); - }, - }); - - return ( - <> - - - - - - - - - - - - - - - - - - - - - - - - - - ) -} - -export default CreateContent \ No newline at end of file diff --git a/src/components/dashboard/expert-management/Form/CreateForm/__tests__/index.test.js b/src/components/dashboard/expert-management/Form/CreateForm/__tests__/index.test.js deleted file mode 100644 index dda7f82..0000000 --- a/src/components/dashboard/expert-management/Form/CreateForm/__tests__/index.test.js +++ /dev/null @@ -1,131 +0,0 @@ -import {act, fireEvent, render, screen, waitFor} from "@testing-library/react"; -import React from 'react'; -import MockAppWithProviders from "../../../../../../../mocks/AppWithProvider"; -import CreateForm from "../../CreateForm"; - -function selectDropdownItem(screen, openerTestId, itemText) { - const opener = screen.getByTestId(openerTestId); - fireEvent.mouseDown(opener); - const selectItem = screen.queryByText(itemText); - fireEvent.click(selectItem); -} - -function setInputValue(screen, inputElement, value) { - fireEvent.change(inputElement, {target: {value}}); -} - -describe("CreateForm Component From Expert Management", () => { - describe("Rendering", () => { - it("Create Button Rendered", () => { - render(); - const CreateButton = screen.getByRole('button', {name: "افزودن"}); - expect(CreateButton).toBeInTheDocument(); - }); - it("Dialog Header Rendered", () => { - render(); - const CreateDialogHeader = screen.queryByText("افزودن"); - expect(CreateDialogHeader).toBeInTheDocument(); - }); - }); - describe("Behavioral", () => { - it("by Clicking Create Button Dialog Should Append To Document", async () => { - render(); - const CreateButton = screen.getByRole('button', {name: "افزودن"}); - fireEvent.click(CreateButton); - await act(() => { - const CreateDialog = screen.getByRole('dialog', {name: "افزودن"}) - expect(CreateDialog).toBeInTheDocument(); - }); - }); - }); - describe("Form Submission", () => { - it('Should request to api and if get success close dialog', async () => { - render(); - - const CreateButton = screen.getByRole('button', {name: "افزودن"}); - fireEvent.click(CreateButton); - - const submitButton = screen.queryByText('ثبت'); - - const nameInput = screen.queryByLabelText('نام کامل'); - const usernameInput = screen.queryByLabelText('نام کاربری'); - const emailInput = screen.queryByLabelText('پست الکترونیک'); - const phoneNumberInput = screen.queryByLabelText('شماره همراه'); - const telephoneIdInput = screen.queryByLabelText('کد تلفن'); - const nationalIdInput = screen.queryByLabelText('کد ملی'); - const passwordInput = screen.queryByLabelText('رمز عبور'); - const positionInput = screen.queryByLabelText('سمت'); - - selectDropdownItem(screen, "option-opener-province", "آذربایجان شرقی"); - selectDropdownItem(screen, "option-opener-role", "ادمین"); - selectDropdownItem(screen, "option-opener-gender", "مرد"); - - setInputValue(screen, nameInput, 'nameTest'); - setInputValue(screen, usernameInput, 'usernameTest'); - setInputValue(screen, emailInput, 'emailTest@gmail.com'); - setInputValue(screen, phoneNumberInput, '0914577458'); - setInputValue(screen, telephoneIdInput, '091'); - setInputValue(screen, nationalIdInput, 'nationalIdTest'); - setInputValue(screen, passwordInput, 'passwordTest12'); - setInputValue(screen, positionInput, 'positionTest'); - - expect(submitButton).not.toBeDisabled(); - fireEvent.click(submitButton); - - await waitFor(() => { - const dialogContent = screen.queryByTestId('create-dialog-content'); - expect(dialogContent).not.toBeInTheDocument(); - }); - }); - it('Should request to api and if get error keep previous data', async () => { - render(); - - const CreateButton = screen.getByRole('button', {name: "افزودن"}); - fireEvent.click(CreateButton); - - const submitButton = screen.queryByText('ثبت'); - - const nameInput = screen.queryByLabelText('نام کامل'); - const usernameInput = screen.queryByLabelText('نام کاربری'); - const emailInput = screen.queryByLabelText('پست الکترونیک'); - const phoneNumberInput = screen.queryByLabelText('شماره همراه'); - const telephoneIdInput = screen.queryByLabelText('کد تلفن'); - const nationalIdInput = screen.queryByLabelText('کد ملی'); - const passwordInput = screen.queryByLabelText('رمز عبور'); - const positionInput = screen.queryByLabelText('سمت'); - const provinceInput = screen.getByTestId("input-province-id") - const roleInput = screen.getByTestId("input-role-id") - const genderInput = screen.getByTestId("input-gender") - - selectDropdownItem(screen, "option-opener-province", "آذربایجان شرقی"); - selectDropdownItem(screen, "option-opener-role", "ادمین"); - selectDropdownItem(screen, "option-opener-gender", "مرد"); - - setInputValue(screen, nameInput, 'nameTest'); - setInputValue(screen, usernameInput, 'usernameTest'); - setInputValue(screen, emailInput, 'emailTest@gmail.com'); - setInputValue(screen, phoneNumberInput, '0914577458'); - setInputValue(screen, telephoneIdInput, '091'); - setInputValue(screen, nationalIdInput, 'nationalIdTest'); - setInputValue(screen, passwordInput, 'passwordTest12'); - setInputValue(screen, positionInput, 'positionTest'); - - expect(submitButton).not.toBeDisabled(); - await fireEvent.click(submitButton); - - await waitFor(() => { - expect(nameInput).toHaveValue('nameTest'); - expect(usernameInput).toHaveValue('usernameTest'); - expect(emailInput).toHaveValue('emailTest@gmail.com'); - expect(phoneNumberInput).toHaveValue('0914577458'); - expect(telephoneIdInput).toHaveValue('091'); - expect(nationalIdInput).toHaveValue('nationalIdTest'); - expect(passwordInput).toHaveValue('passwordTest12'); - expect(positionInput).toHaveValue('positionTest'); - expect(provinceInput).toHaveValue('1'); - expect(roleInput).toHaveValue('1'); - expect(genderInput).toHaveValue('male'); - }); - }); - }) -}); \ No newline at end of file diff --git a/src/components/dashboard/expert-management/Form/CreateForm/index.jsx b/src/components/dashboard/expert-management/Form/CreateForm/index.jsx deleted file mode 100644 index 92f822f..0000000 --- a/src/components/dashboard/expert-management/Form/CreateForm/index.jsx +++ /dev/null @@ -1,39 +0,0 @@ -import {Button, Dialog, DialogTitle, Stack, Tooltip} from "@mui/material"; -import {useTranslations} from "next-intl"; -import DataSaverOnIcon from "@mui/icons-material/DataSaverOn"; -import {useState} from "react"; -import CreateContent from "./CreateContent"; - -const Create = ({mutate}) => { - const t = useTranslations(); - const [openCreateDialog, setOpenCreateDialog] = useState(false); - - return ( - <> - - - - - - - {t("ExpertMangement.create")} - - - - ) -} - -export default Create \ No newline at end of file diff --git a/src/components/dashboard/expert-management/Form/DeleteForm/__tests__/index.test.js b/src/components/dashboard/expert-management/Form/DeleteForm/__tests__/index.test.js deleted file mode 100644 index f2e52b0..0000000 --- a/src/components/dashboard/expert-management/Form/DeleteForm/__tests__/index.test.js +++ /dev/null @@ -1,66 +0,0 @@ -import {fireEvent, render, screen, waitFor} from "@testing-library/react"; -import MockAppWithProviders from "../../../../../../../mocks/AppWithProvider"; -import React from "react"; -import DeleteForm from "@/components/dashboard/expert-management/Form/DeleteForm"; - -describe("CreateForm Component From Expert Management", () => { - describe("Rendering", () => { - it("Delete Expert Button And Tooltip Rendered", () => { - render(); - const DeleteButton = screen.getByTestId('delete-button'); - expect(DeleteButton).toBeInTheDocument(); - }); - it("Delete Dialog Rendered", () => { - render(); - const DeleteButton = screen.getByTestId('delete-button'); - fireEvent.click(DeleteButton); - const DeleteDialogHeader = screen.queryByText("حذف کارشناس"); - expect(DeleteDialogHeader).toBeInTheDocument(); - }); - it("Delete Dialog Text Rendered", () => { - render(); - const DeleteButton = screen.getByTestId('delete-button'); - fireEvent.click(DeleteButton); - const DeleteDialogText = screen.queryByText("آیا از انجام این عملیات اطمینان دارید؟"); - expect(DeleteDialogText).toBeInTheDocument(); - }); - it("Delete Dialog Delete Button Rendered", () => { - render(); - const DeleteButton = screen.getByTestId('delete-button'); - fireEvent.click(DeleteButton); - const DeleteButtonDialog = screen.getByRole('button', {name: "حذف"}); - expect(DeleteButtonDialog).toBeInTheDocument(); - }); - it("Delete Dialog Cancel Button Rendered", () => { - render(); - const DeleteButton = screen.getByTestId('delete-button'); - fireEvent.click(DeleteButton); - const CancelButtonDialog = screen.getByRole('button', {name: "بستن"}); - expect(CancelButtonDialog).toBeInTheDocument(); - }); - }); - describe("Behavioral", () => { - it("By Clicking Delete Button Dialog Should Append To Document", async () => { - render(); - const DeleteButton = screen.getByTestId('delete-button'); - fireEvent.click(DeleteButton); - await waitFor(() => { - const DeleteDialog = screen.getByRole('dialog', {name: "حذف کارشناس"}) - expect(DeleteDialog).toBeInTheDocument(); - }); - }); - }); - describe("Form Submission", () => { - it("By Clicking Delete Button Request To Api And Delete Item", async () => { - render(); - const DeleteButton = screen.getByTestId('delete-button'); - fireEvent.click(DeleteButton); - const DeleteButtonDialog = screen.getByRole('button', {name: "حذف"}); - fireEvent.click(DeleteButtonDialog); - await waitFor(() => { - const DeleteDialog = screen.queryByRole('dialog', {name: "حذف کارشناس"}); - expect(DeleteDialog).not.toBeInTheDocument(); - }); - }); - }); -}); \ No newline at end of file diff --git a/src/components/dashboard/expert-management/Form/DeleteForm/index.jsx b/src/components/dashboard/expert-management/Form/DeleteForm/index.jsx deleted file mode 100644 index 0d8e592..0000000 --- a/src/components/dashboard/expert-management/Form/DeleteForm/index.jsx +++ /dev/null @@ -1,74 +0,0 @@ -import { - Button, - Dialog, - DialogActions, - DialogContent, - DialogContentText, - DialogTitle, - IconButton, - Tooltip -} from "@mui/material"; -import DeleteIcon from '@mui/icons-material/Delete'; -import {useState} from "react"; -import {DELETE_EXPERT} from "@/core/data/apiRoutes"; -import useRequest from "@/lib/app/hooks/useRequest"; -import {useTranslations} from "next-intl"; - -const Delete = ({rowId, mutate}) => { - const t = useTranslations(); - const requestServer = useRequest({auth: true}); - const [openDeleteDialog, setOpenDeleteDialog] = useState(false); - const [isSubmitting, setIsSubmitting] = useState(false); - - const handleDelete = () => { - setIsSubmitting(true) - requestServer(`${DELETE_EXPERT}/${rowId}`, 'DELETE') - .then((response) => { - setOpenDeleteDialog(false); - mutate() - }) - .catch(() => { - }) - .finally(() => { - setIsSubmitting(false) - }); - }; - - return ( - <> - - { - setOpenDeleteDialog(true); - }} - > - - - - - {t("ExpertMangement.delete_expert")} - - {t("ExpertMangement.are_you_sure_text")} - - - - - - - - ); -}; -export default Delete; \ No newline at end of file diff --git a/src/components/dashboard/expert-management/Form/UpdateForm/UpdateContent/PersonalInfo/__tests__/index.test.js b/src/components/dashboard/expert-management/Form/UpdateForm/UpdateContent/PersonalInfo/__tests__/index.test.js deleted file mode 100644 index b8692d5..0000000 --- a/src/components/dashboard/expert-management/Form/UpdateForm/UpdateContent/PersonalInfo/__tests__/index.test.js +++ /dev/null @@ -1,219 +0,0 @@ -import {act, fireEvent, render, screen, waitFor} from "@testing-library/react"; -import React from 'react'; -import MockAppWithProviders from "../../../../../../../../../mocks/AppWithProvider"; -import * as Yup from "yup"; -import {Formik} from "formik"; -import PersonalInfo from "@/components/dashboard/expert-management/Form/CreateForm/CreateContent/PersonalInfo"; -import {useTranslations} from "next-intl"; -import Province from "@/components/dashboard/expert-management/Form/CreateForm/CreateContent/RestInfo/Province"; - - -const CreateFormMock = ({Component}) => { - const t = useTranslations(); - const validationSchema = Yup.object().shape({ - full_name: Yup.string().required(t("ExpertMangement.error_message_full_name")), - email: Yup.string().required(t("ExpertMangement.error_message_email")), - phone_number: Yup.string().required(t("ExpertMangement.error_message_phone_number")), - telephone_id: Yup.string().required(t("ExpertMangement.error_message_telephone_id")), - national_id: Yup.string().required(t("ExpertMangement.error_message_national_id")), - gender: Yup.string().required(t("ExpertMangement.error_message_gender")) - }); - return ( - { - }} - > - {formikProps => ()} - - ); -}; - - -describe("PersonalInfo Component From Expert Management (update)", () => { - describe("Rendering", () => { - it("full_name TextField Rendered", async () => { - render(); - await waitFor(() => { - const fullNameTextField = screen.queryByLabelText('نام کامل'); - expect(fullNameTextField).toBeInTheDocument(); - }); - }); - it("Email TextField Rendered", async () => { - render(); - await waitFor(() => { - const emailTextField = screen.queryByLabelText('پست الکترونیک'); - expect(emailTextField).toBeInTheDocument(); - }); - }); - it("Phone Number TextField Rendered", async () => { - render(); - await waitFor(() => { - const phoneNumberTextField = screen.queryByLabelText('شماره همراه'); - expect(phoneNumberTextField).toBeInTheDocument(); - }); - }); - it("telephone id TextField Rendered", async () => { - render(); - await waitFor(() => { - const telephoneIdTextField = screen.queryByLabelText('کد تلفن'); - expect(telephoneIdTextField).toBeInTheDocument(); - }); - }); - it("National Id TextField Rendered", async () => { - render(); - await waitFor(() => { - const nationalIdTextField = screen.queryByLabelText('کد ملی'); - expect(nationalIdTextField).toBeInTheDocument(); - }); - }); - it("Gender Select Box Rendered", async () => { - render(); - await waitFor(() => { - const genderSelectBox = screen.getByTestId('select-box'); - expect(genderSelectBox).toBeInTheDocument(); - }); - }); - }); - describe("Behavioral", () => { - it("Name TextField Work Correctly", async () => { - render(); - const nameTextField = screen.queryByLabelText('نام کامل'); - fireEvent.change(nameTextField, {target: {value: 'exampleName'}}); - await act(() => { - expect(nameTextField).toHaveValue('exampleName'); - }) - }); - it("Email TextField Work Correctly", async () => { - render(); - const emailTextField = screen.queryByLabelText('پست الکترونیک'); - fireEvent.change(emailTextField, {target: {value: 'exampleEmail'}}); - await act(() => { - expect(emailTextField).toHaveValue('exampleEmail'); - }) - }); - it("Phone Number TextField Work Correctly", async () => { - render(); - const phoneNumberTextField = screen.queryByLabelText('شماره همراه'); - fireEvent.change(phoneNumberTextField, {target: {value: 'examplePhoneNumber'}}); - await act(() => { - expect(phoneNumberTextField).toHaveValue('examplePhoneNumber'); - }) - }); - it("Telephone Id TextField Work Correctly", async () => { - render(); - const telephoneIdTextField = screen.queryByLabelText('کد تلفن'); - fireEvent.change(telephoneIdTextField, {target: {value: 'exampleTelephoneId'}}); - await act(() => { - expect(telephoneIdTextField).toHaveValue('exampleTelephoneId'); - }) - }); - it("National Id TextField Work Correctly", async () => { - render(); - const nationalIdTextField = screen.queryByLabelText('کد ملی'); - fireEvent.change(nationalIdTextField, {target: {value: 'exampleNationalId'}}); - await act(() => { - expect(nationalIdTextField).toHaveValue('exampleNationalId'); - }) - }); - it("Gender Select Box Work Correctly", async () => { - render(); - const genderSelectBox = screen.getByTestId('select-box'); - const genderSelectOpener = await waitFor(() => screen.getByTestId("option-opener-gender")); - - await waitFor(() => { - expect(genderSelectBox).toBeInTheDocument(); - }); - - fireEvent.mouseDown(genderSelectOpener); - await waitFor(() => { - const selectItem = screen.queryByText("مرد"); - expect(selectItem).toBeInTheDocument(); - }); - - }); - }); - describe("validation", () => { - it('Should See Error When Name Input Is Empty', async () => { - render(); - - const nameInput = screen.queryByLabelText('نام کامل'); - - fireEvent.change(nameInput, {target: {value: ''}}); - fireEvent.blur(nameInput); - - await waitFor(() => { - expect(screen.queryByText("نام کامل خود را وارد کنید")).toBeInTheDocument() - }); - }); - it('Should See Error When Email Input Is Empty', async () => { - render(); - - const emailInput = screen.queryByLabelText('پست الکترونیک'); - - fireEvent.change(emailInput, {target: {value: ''}}); - fireEvent.blur(emailInput); - - await waitFor(() => { - expect(screen.queryByText("پست الکترونیک خود را وارد کنید")).toBeInTheDocument() - }); - }); - it('Should See Error When Phone Number Input Is Empty', async () => { - render(); - - const phoneNumberInput = screen.queryByLabelText('شماره همراه'); - - fireEvent.change(phoneNumberInput, {target: {value: ''}}); - fireEvent.blur(phoneNumberInput); - - await waitFor(() => { - expect(screen.queryByText("شماره همراه خود را وارد کنید")).toBeInTheDocument() - }); - }); - it('Should See Error When telephone Id Input Is Empty', async () => { - render(); - - const telephoneIdTextField = screen.queryByLabelText('کد تلفن'); - - fireEvent.change(telephoneIdTextField, {target: {value: ''}}); - fireEvent.blur(telephoneIdTextField); - - await waitFor(() => { - expect(screen.queryByText("کد تلفن خود را وارد کنید")).toBeInTheDocument() - }); - }); - it('Should See Error When National Id Input Is Empty', async () => { - render(); - - const nationalIdInput = screen.queryByLabelText('کد ملی'); - - fireEvent.change(nationalIdInput, {target: {value: ''}}); - fireEvent.blur(nationalIdInput); - - await waitFor(() => { - expect(screen.queryByText("کد ملی خود را وارد کنید")).toBeInTheDocument() - }); - }); - it('Should Select An Item With Valid Value of gender', async () => { - render(); - const genderInput = screen.getByTestId("input-gender") - const genderSelectOpener = await waitFor(() => screen.getByTestId("option-opener-gender")); - - fireEvent.mouseDown(genderSelectOpener); - const selectItem = await waitFor(() => screen.queryByText("مرد")); - expect(selectItem).toBeInTheDocument(); - fireEvent.click(selectItem); - await waitFor(() => { - expect(genderInput.value).toBe('male') - }); - }); - }) -}); \ No newline at end of file diff --git a/src/components/dashboard/expert-management/Form/UpdateForm/UpdateContent/PersonalInfo/index.jsx b/src/components/dashboard/expert-management/Form/UpdateForm/UpdateContent/PersonalInfo/index.jsx deleted file mode 100644 index f92bf38..0000000 --- a/src/components/dashboard/expert-management/Form/UpdateForm/UpdateContent/PersonalInfo/index.jsx +++ /dev/null @@ -1,128 +0,0 @@ -import {FormControl, FormHelperText, Grid, InputLabel, MenuItem, Select, TextField} from "@mui/material"; -import {useTranslations} from "next-intl"; - -const PersonalInfo = ({formik}) => { - const t = useTranslations(); - - const genderList = [ - {id: 1, name_en: "male", name_fa: "مرد"}, - {id: 2, name_en: "female", name_fa: "زن"} - ] - - return ( - - - - - - - - - - - - - - - - - - - {t("ExpertMangement.text_field_gender")} - - - {formik.touched.gender && formik.errors.gender ? formik.errors.gender : ""} - - - - - ) -} -export default PersonalInfo \ No newline at end of file diff --git a/src/components/dashboard/expert-management/Form/UpdateForm/UpdateContent/RestInfo/PositionAndRole/__tests__/index.test.js b/src/components/dashboard/expert-management/Form/UpdateForm/UpdateContent/RestInfo/PositionAndRole/__tests__/index.test.js deleted file mode 100644 index d6062c5..0000000 --- a/src/components/dashboard/expert-management/Form/UpdateForm/UpdateContent/RestInfo/PositionAndRole/__tests__/index.test.js +++ /dev/null @@ -1,96 +0,0 @@ -import {fireEvent, render, screen, waitFor} from "@testing-library/react"; -import React from 'react'; -import {useTranslations} from "next-intl"; -import * as Yup from "yup"; -import {Formik} from "formik"; -import MockAppWithProviders from "../../../../../../../../../../mocks/AppWithProvider"; -import PositionAndRole from "../../PositionAndRole"; - -const CreateFormMock = ({Component}) => { - const t = useTranslations(); - const validationSchema = Yup.object().shape({ - position: Yup.string().required(t("ExpertMangement.error_message_position")), - roles: Yup.string().required(t("ExpertMangement.error_message_roles")) - }); - return ( - { - }} - > - {formikProps => ()} - - ); -}; - -describe("PositionAndRole Component From Expert Management (update)", () => { - describe("Rendering", () => { - it("Position TextField Rendered", async () => { - render(); - await waitFor(() => { - const positionTextField = screen.queryByLabelText('سمت'); - expect(positionTextField).toBeInTheDocument(); - }); - }); - it("Role Select Box Rendered", async () => { - render(); - const roleSelect = screen.getByTestId('select-box'); - await waitFor(() => { - expect(roleSelect).toBeInTheDocument(); - }); - }); - }); - describe("Behavioral", () => { - it("Position TextField Work Correctly", async () => { - render(); - const positionTextField = screen.queryByLabelText('سمت'); - fireEvent.change(positionTextField, {target: {value: 'examplePosition'}}); - await waitFor(() => { - expect(positionTextField).toHaveValue('examplePosition'); - }) - }); - it("Role Select Box Work Correctly", async () => { - render(); - const roleSelect = screen.getByTestId("option-opener-role"); - await waitFor(() => { - expect(roleSelect).toBeInTheDocument(); - }); - fireEvent.mouseDown(roleSelect); - await waitFor(() => { - const selectItem = screen.queryByText("ادمین"); - expect(selectItem).toBeInTheDocument(); - }); - }); - }); - describe("validation", () => { - it('Should See Error When Position Input Is Empty', async () => { - render(); - - const positionInput = screen.queryByLabelText('سمت'); - - fireEvent.change(positionInput, {target: {value: ''}}); - fireEvent.blur(positionInput); - - await waitFor(() => { - expect(screen.queryByText("سمت خود را وارد کنید")).toBeInTheDocument() - }); - }); - it('Should Select An Item With Valid Value of role', async () => { - render(); - const roleInput = await waitFor(() => screen.getByTestId("input-role-id")); - const roleSelectOpener = await waitFor(() => screen.getByTestId("option-opener-role")); - - fireEvent.mouseDown(roleSelectOpener); - const selectItem = await waitFor(() => screen.queryByText("ادمین")); - expect(selectItem).toBeInTheDocument(); - fireEvent.click(selectItem); - await waitFor(() => { - expect(roleInput.value).toBe('1') - }); - }); - }) -}); \ No newline at end of file diff --git a/src/components/dashboard/expert-management/Form/UpdateForm/UpdateContent/RestInfo/PositionAndRole/index.jsx b/src/components/dashboard/expert-management/Form/UpdateForm/UpdateContent/RestInfo/PositionAndRole/index.jsx deleted file mode 100644 index 4fc402a..0000000 --- a/src/components/dashboard/expert-management/Form/UpdateForm/UpdateContent/RestInfo/PositionAndRole/index.jsx +++ /dev/null @@ -1,75 +0,0 @@ -import {FormControl, FormHelperText, Grid, InputLabel, MenuItem, Select, TextField} from "@mui/material"; -import {useTranslations} from "next-intl"; -import useRequest from "@/lib/app/hooks/useRequest"; -import useRole from "@/lib/app/hooks/useRole"; -import {log} from "next/dist/server/typescript/utils"; - -const PositionAndRole = ({formik}) => { - const t = useTranslations(); - const requestServer = useRequest() - const {roleList, isLoadingRoleList, errorRoleList} = useRole(); - - return ( - - - - - - - {t("ExpertMangement.text_field_roles")} - - - {formik.touched.roles && formik.errors.roles ? formik.errors.roles : ""} - - - - - ) -} -export default PositionAndRole \ No newline at end of file diff --git a/src/components/dashboard/expert-management/Form/UpdateForm/UpdateContent/RestInfo/Province/__tests__/index.test.js b/src/components/dashboard/expert-management/Form/UpdateForm/UpdateContent/RestInfo/Province/__tests__/index.test.js deleted file mode 100644 index d727188..0000000 --- a/src/components/dashboard/expert-management/Form/UpdateForm/UpdateContent/RestInfo/Province/__tests__/index.test.js +++ /dev/null @@ -1,71 +0,0 @@ -import {fireEvent, render, screen, waitFor} from "@testing-library/react"; -import React from 'react'; -import {useTranslations} from "next-intl"; -import * as Yup from "yup"; -import {Formik} from "formik"; -import Province from "../../Province"; -import MockAppWithProviders from "../../../../../../../../../../mocks/AppWithProvider"; - -const CreateFormMock = ({Component}) => { - const t = useTranslations(); - const validationSchema = Yup.object().shape({ - province_id: Yup.string().required(t("ExpertMangement.error_message_province_id")), - }); - return ( - { - }} - > - {formikProps => ()} - - ); -}; - -describe("Province Component From Expert Management (update)", () => { - describe("Rendering", () => { - it("Province Select Box Rendered", async () => { - render(); - await waitFor(() => { - const provinceSelectBox = screen.getByTestId('select-box'); - expect(provinceSelectBox).toBeInTheDocument(); - }); - }); - }); - describe("Behavioral", () => { - it("Province Select Box Work Correctly", async () => { - render(); - const provinceSelectBox = screen.getByTestId('select-box'); - const provinceSelectOpener = await waitFor(() => screen.getByTestId("option-opener-province")); - - await waitFor(() => { - expect(provinceSelectBox).toBeInTheDocument(); - }); - - fireEvent.mouseDown(provinceSelectOpener); - await waitFor(() => { - const selectItem = screen.queryByText("آذربایجان شرقی"); - expect(selectItem).toBeInTheDocument(); - }); - - }); - }); - describe("validation", () => { - it('Should Select An Item With Valid Value of province', async () => { - render(); - const provinceInput = screen.getByTestId("input-province-id") - const provinceSelectOpener = await waitFor(() => screen.getByTestId("option-opener-province")); - - fireEvent.mouseDown(provinceSelectOpener); - const selectItem = await waitFor(() => screen.queryByText("آذربایجان شرقی")); - expect(selectItem).toBeInTheDocument(); - fireEvent.click(selectItem); - await waitFor(() => { - expect(provinceInput.value).toBe('1') - }); - }); - }) -}); \ No newline at end of file diff --git a/src/components/dashboard/expert-management/Form/UpdateForm/UpdateContent/RestInfo/Province/index.jsx b/src/components/dashboard/expert-management/Form/UpdateForm/UpdateContent/RestInfo/Province/index.jsx deleted file mode 100644 index 763465e..0000000 --- a/src/components/dashboard/expert-management/Form/UpdateForm/UpdateContent/RestInfo/Province/index.jsx +++ /dev/null @@ -1,61 +0,0 @@ -import {FormControl, FormHelperText, Grid, InputLabel, MenuItem, Select} from "@mui/material"; -import {useTranslations} from "next-intl"; -import useProvince from "@/lib/app/hooks/useProvince"; -import useRequest from "@/lib/app/hooks/useRequest"; - -const Province = ({formik}) => { - const t = useTranslations(); - const requestServer = useRequest() - const {provinceList, isLoadingProvinceList, errorProvinceList} = useProvince(); - - return ( - - - - {t("ExpertMangement.text_field_province_id")} - - - {formik.touched.province_id && formik.errors.province_id ? formik.errors.province_id : ""} - - - - - ) -} -export default Province \ No newline at end of file diff --git a/src/components/dashboard/expert-management/Form/UpdateForm/UpdateContent/RestInfo/index.jsx b/src/components/dashboard/expert-management/Form/UpdateForm/UpdateContent/RestInfo/index.jsx deleted file mode 100644 index 36c1877..0000000 --- a/src/components/dashboard/expert-management/Form/UpdateForm/UpdateContent/RestInfo/index.jsx +++ /dev/null @@ -1,13 +0,0 @@ -import Province from "@/components/dashboard/expert-management/Form/UpdateForm/UpdateContent/RestInfo/Province"; -import PositionAndRole - from "@/components/dashboard/expert-management/Form/UpdateForm/UpdateContent/RestInfo/PositionAndRole"; - -const RestInfo = ({formik}) => { - return ( - <> - - - - ) -} -export default RestInfo \ No newline at end of file diff --git a/src/components/dashboard/expert-management/Form/UpdateForm/UpdateContent/UserInfo/__tests__/index.test.js b/src/components/dashboard/expert-management/Form/UpdateForm/UpdateContent/UserInfo/__tests__/index.test.js deleted file mode 100644 index 3fd341d..0000000 --- a/src/components/dashboard/expert-management/Form/UpdateForm/UpdateContent/UserInfo/__tests__/index.test.js +++ /dev/null @@ -1,62 +0,0 @@ -import {act, fireEvent, render, screen, waitFor} from "@testing-library/react"; -import React from 'react'; -import MockAppWithProviders from "../../../../../../../../../mocks/AppWithProvider"; -import {useTranslations} from "next-intl"; -import * as Yup from "yup"; -import {Formik} from "formik"; -import UserInfo from "../../UserInfo"; - -const CreateFormMock = ({Component}) => { - const t = useTranslations(); - const validationSchema = Yup.object().shape({ - username: Yup.string().required(t("ExpertMangement.error_message_username")), - }); - return ( - { - }} - > - {formikProps => ()} - - ); -}; - -describe("UserInfo Component From Expert Management (update)", () => { - describe("Rendering", () => { - it("UserName TextField Rendered", async () => { - render(); - await waitFor(() => { - const usernameTextField = screen.queryByLabelText('نام کاربری'); - expect(usernameTextField).toBeInTheDocument(); - }); - }); - }); - describe("Behavioral", () => { - it("Username TextField Work Correctly", async () => { - render(); - const usernameTextField = screen.queryByLabelText('نام کاربری'); - fireEvent.change(usernameTextField, {target: {value: 'exampleUsername'}}); - await act(() => { - expect(usernameTextField).toHaveValue('exampleUsername'); - }) - }); - }); - describe("validation", () => { - it('Should See Error When Username Input Is Empty', async () => { - render(); - - const usernameInput = screen.queryByLabelText('نام کاربری'); - - fireEvent.change(usernameInput, {target: {value: ''}}); - fireEvent.blur(usernameInput); - - await waitFor(() => { - expect(screen.queryByText("نام کاربری خود را وارد کنید")).toBeInTheDocument() - }); - }); - }) -}); \ No newline at end of file diff --git a/src/components/dashboard/expert-management/Form/UpdateForm/UpdateContent/UserInfo/index.jsx b/src/components/dashboard/expert-management/Form/UpdateForm/UpdateContent/UserInfo/index.jsx deleted file mode 100644 index d592ea5..0000000 --- a/src/components/dashboard/expert-management/Form/UpdateForm/UpdateContent/UserInfo/index.jsx +++ /dev/null @@ -1,29 +0,0 @@ -import {Grid, TextField} from "@mui/material"; -import {useTranslations} from "next-intl"; - -const UserInfo = ({formik}) => { - const t = useTranslations(); - - return ( - <> - - - - - - - ) -} -export default UserInfo \ No newline at end of file diff --git a/src/components/dashboard/expert-management/Form/UpdateForm/UpdateContent/__tests__/index.test.js b/src/components/dashboard/expert-management/Form/UpdateForm/UpdateContent/__tests__/index.test.js deleted file mode 100644 index 765bc09..0000000 --- a/src/components/dashboard/expert-management/Form/UpdateForm/UpdateContent/__tests__/index.test.js +++ /dev/null @@ -1,87 +0,0 @@ -import {fireEvent, render, screen, waitFor} from "@testing-library/react"; -import React from 'react'; -import MockAppWithProviders from "../../../../../../../../mocks/AppWithProvider"; -import UpdateContent from "../../UpdateContent"; - -const row = { - original: { - avatar: null, - created_at: "2023-10-10T13:05:01.000000Z", - email: "fisher.sigrid@yahoo.com", - full_name: "Anibal Labadie", - gender: "female", - id: 85, - national_id: "5944646138", - phone_number: "09731311720", - position: "boss1", - province_fa: "اردبیل", - province_id: 3, - roles: [{ - created_at: "2023-10-10T13:03:18.000000Z", - guard_name: "api", - id: 2, - name: "manager", - name_fa: "مدیر" - }], - telephone_id: "tel-3110", - updated_at: "2023-10-17T10:57:12.000000Z", - username: "florence.kihn", - } -} - -function selectDropdownItem(screen, openerTestId, itemText) { - const opener = screen.getByTestId(openerTestId); - fireEvent.mouseDown(opener); - const selectItem = screen.queryByText(itemText); - fireEvent.click(selectItem); -} - -function setInputValue(screen, inputElement, value) { - fireEvent.change(inputElement, {target: {value}}); -} - -describe("UpdateContent Component From Expert Management (update)", () => { - describe("Rendering", () => { - it("close button Rendered", async () => { - render(); - await waitFor(() => { - const cancelBtn = screen.queryByText('بستن'); - expect(cancelBtn).toBeInTheDocument(); - }); - }); - it("confirm button Rendered", async () => { - render(); - await waitFor(() => { - const confirmBtn = screen.queryByText('ثبت'); - expect(confirmBtn).toBeInTheDocument(); - }); - }); - }); - describe("Form Submission", () => { - it('Should enable the submit button when the inputs are valid', async () => { - render(); - - const submitButton = screen.queryByText('ثبت'); - - const nameInput = screen.queryByLabelText('نام کامل'); - const usernameInput = screen.queryByLabelText('نام کاربری'); - const emailInput = screen.queryByLabelText('پست الکترونیک'); - const phoneNumberInput = screen.queryByLabelText('شماره همراه'); - const telephoneIdInput = screen.queryByLabelText('کد تلفن'); - const nationalIdInput = screen.queryByLabelText('کد ملی'); - const positionInput = screen.queryByLabelText('سمت'); - - setInputValue(screen, nameInput, 'nameTest'); - setInputValue(screen, usernameInput, 'usernameTest'); - setInputValue(screen, emailInput, 'emailTest'); - setInputValue(screen, phoneNumberInput, 'phoneNumberTest'); - setInputValue(screen, telephoneIdInput, 'telephoneIdTest'); - setInputValue(screen, nationalIdInput, 'nationalIdTest'); - setInputValue(screen, positionInput, 'positionTest'); - - await waitFor(() => { - expect(submitButton).not.toBeDisabled(); - }); - }); - }) -}); \ No newline at end of file diff --git a/src/components/dashboard/expert-management/Form/UpdateForm/UpdateContent/index.jsx b/src/components/dashboard/expert-management/Form/UpdateForm/UpdateContent/index.jsx deleted file mode 100644 index 1f482dc..0000000 --- a/src/components/dashboard/expert-management/Form/UpdateForm/UpdateContent/index.jsx +++ /dev/null @@ -1,108 +0,0 @@ -import {Box, Button, Chip, DialogActions, DialogContent, Divider,} from "@mui/material"; -import * as Yup from "yup"; -import {useTranslations} from "next-intl"; -import {useFormik} from "formik"; -import useRequest from "@/lib/app/hooks/useRequest"; -import {UPDATE_EXPERT} from "@/core/data/apiRoutes"; -import PersonalInfo from "./PersonalInfo"; -import UserInfo from "./UserInfo"; -import RestInfo from "./RestInfo"; - -const UpdateContent = ({row, mutate, setOpenUpdateDialog}) => { - const t = useTranslations(); - const requestServer = useRequest() - - const validationSchema = Yup.object().shape({ - full_name: Yup.string().required(t("ExpertMangement.error_message_full_name")), - username: Yup.string().required(t("ExpertMangement.error_message_username")), - email: Yup.string().required(t("ExpertMangement.error_message_email")), - phone_number: Yup.string().required(t("ExpertMangement.error_message_phone_number")), - telephone_id: Yup.string().required(t("ExpertMangement.error_message_telephone_id")), - gender: Yup.string().required(t("ExpertMangement.error_message_gender")), - national_id: Yup.string().required(t("ExpertMangement.error_message_national_id")), - position: Yup.string().required(t("ExpertMangement.error_message_position")), - province_id: Yup.string().required(t("ExpertMangement.error_message_province_id")), - roles: Yup.string().required(t("ExpertMangement.error_message_roles")) - }); - - const formik = useFormik({ - initialValues: { - full_name: row.original.full_name, - username: row.original.username, - email: row.original.email, - phone_number: row.original.phone_number, - telephone_id: row.original.telephone_id, - gender: row.original.gender, - national_id: row.original.national_id, - position: row.original.position, - province_id: row.original.province_id, - roles: row.original.roles[0]?.id - }, - validationSchema, - onSubmit: (values, {setSubmitting}) => { - const formData = new FormData(); - formData.append("full_name", values.full_name); - formData.append("national_id", values.national_id); - formData.append("phone_number", values.phone_number); - formData.append("telephone_id", values.telephone_id); - formData.append("gender", values.gender); - formData.append("email", values.email); - formData.append("username", values.username); - formData.append("province_id", values.province_id); - formData.append("position", values.position); - formData.append("roles", values.roles); - - requestServer(`${UPDATE_EXPERT}/${row.original.id}`, 'post', {auth: true, data: formData}) - .then((response) => { - setOpenUpdateDialog(false) - mutate() - }).catch(() => { - }).finally(() => { - setSubmitting(false); - }); - }, - }); - - return ( - <> - - - - - - - - - - - - - - - - - - - - - - - - - - ) -} - -export default UpdateContent \ No newline at end of file diff --git a/src/components/dashboard/expert-management/Form/UpdateForm/__tests__/index.test.js b/src/components/dashboard/expert-management/Form/UpdateForm/__tests__/index.test.js deleted file mode 100644 index f824d6d..0000000 --- a/src/components/dashboard/expert-management/Form/UpdateForm/__tests__/index.test.js +++ /dev/null @@ -1,147 +0,0 @@ -import {act, fireEvent, render, screen, waitFor} from "@testing-library/react"; -import React from 'react'; -import MockAppWithProviders from "../../../../../../../mocks/AppWithProvider"; -import UpdateForm from "../../UpdateForm"; - -const row = { - original: { - avatar: null, - created_at: "2023-10-10T13:05:01.000000Z", - email: "fisher.sigrid@yahoo.com", - full_name: "Anibal Labadie", - gender: "female", - id: 85, - national_id: "5944646138", - phone_number: "09731311720", - position: "boss1", - province_fa: "اردبیل", - province_id: 3, - roles: [{ - created_at: "2023-10-10T13:03:18.000000Z", - guard_name: "api", - id: 2, - name: "manager", - name_fa: "مدیر" - }], - telephone_id: "tel-3110", - updated_at: "2023-10-17T10:57:12.000000Z", - username: "florence.kihn", - } -} - -function selectDropdownItem(screen, openerTestId, itemText) { - const opener = screen.getByTestId(openerTestId); - fireEvent.mouseDown(opener); - const selectItem = screen.queryByText(itemText); - fireEvent.click(selectItem); -} - -function setInputValue(screen, inputElement, value) { - fireEvent.change(inputElement, {target: {value}}); -} - -describe("CreateForm Component From Expert Management", () => { - describe("Rendering", () => { - it("Update Expert Button Rendered", () => { - render(); - const UpdateButton = screen.getByTestId('update-button'); - expect(UpdateButton).toBeInTheDocument(); - }); - }); - describe("Behavioral", () => { - it("by Clicking Update Button Dialog Should Append To Document", async () => { - render(); - const UpdateButton = screen.getByTestId('update-button'); - fireEvent.click(UpdateButton); - await act(() => { - const UpdateDialog = screen.getByRole('dialog', {name: "ویرایش کارشناس"}) - expect(UpdateDialog).toBeInTheDocument(); - }); - }); - }); - describe("Form Submission", () => { - it('Should request to api and if get success close dialog', async () => { - render(); - - const UpdateButton = screen.getByTestId('update-button'); - fireEvent.click(UpdateButton); - - const submitButton = screen.queryByText('ثبت'); - - const nameInput = screen.queryByLabelText('نام کامل'); - const usernameInput = screen.queryByLabelText('نام کاربری'); - const emailInput = screen.queryByLabelText('پست الکترونیک'); - const phoneNumberInput = screen.queryByLabelText('شماره همراه'); - const telephoneIdInput = screen.queryByLabelText('کد تلفن'); - const nationalIdInput = screen.queryByLabelText('کد ملی'); - const positionInput = screen.queryByLabelText('سمت'); - - selectDropdownItem(screen, "option-opener-province", "آذربایجان شرقی"); - selectDropdownItem(screen, "option-opener-role", "ادمین"); - selectDropdownItem(screen, "option-opener-gender", "مرد"); - - setInputValue(screen, nameInput, 'nameTest'); - setInputValue(screen, usernameInput, 'usernameTest'); - setInputValue(screen, emailInput, 'email@gmail.com'); - setInputValue(screen, phoneNumberInput, '0914577458'); - setInputValue(screen, telephoneIdInput, '091'); - setInputValue(screen, nationalIdInput, 'nationalIdTest'); - setInputValue(screen, positionInput, 'positionTest'); - - expect(submitButton).not.toBeDisabled(); - fireEvent.click(submitButton); - - await waitFor(() => { - const dialogContent = screen.queryByTestId('update-dialog-content'); - expect(dialogContent).not.toBeInTheDocument(); - }); - }); - it('Should request to api and if get error keep previous data', async () => { - render(); - - const UpdateButton = screen.getByTestId('update-button'); - fireEvent.click(UpdateButton); - - const submitButton = screen.queryByText('ثبت'); - - const nameInput = screen.queryByLabelText('نام کامل'); - const usernameInput = screen.queryByLabelText('نام کاربری'); - const emailInput = screen.queryByLabelText('پست الکترونیک'); - const phoneNumberInput = screen.queryByLabelText('شماره همراه'); - const telephoneIdInput = screen.queryByLabelText('کد تلفن'); - const nationalIdInput = screen.queryByLabelText('کد ملی'); - const positionInput = screen.queryByLabelText('سمت'); - const provinceInput = screen.getByTestId("input-province-id") - const roleInput = screen.getByTestId("input-role-id") - const genderInput = screen.getByTestId("input-gender") - - selectDropdownItem(screen, "option-opener-province", "آذربایجان شرقی"); - selectDropdownItem(screen, "option-opener-role", "ادمین"); - selectDropdownItem(screen, "option-opener-gender", "مرد"); - - setInputValue(screen, nameInput, 'nameTest'); - setInputValue(screen, usernameInput, 'usernameTest'); - setInputValue(screen, emailInput, 'emailTest@gmail.com'); - setInputValue(screen, phoneNumberInput, '0914577458'); - setInputValue(screen, telephoneIdInput, '091'); - setInputValue(screen, nationalIdInput, 'nationalIdTest'); - setInputValue(screen, positionInput, 'positionTest'); - - expect(submitButton).not.toBeDisabled(); - await fireEvent.click(submitButton); - - await waitFor(() => { - expect(nameInput).toHaveValue('nameTest'); - expect(usernameInput).toHaveValue('usernameTest'); - expect(emailInput).toHaveValue('emailTest@gmail.com'); - expect(phoneNumberInput).toHaveValue('0914577458'); - expect(telephoneIdInput).toHaveValue('091'); - expect(nationalIdInput).toHaveValue('nationalIdTest'); - expect(positionInput).toHaveValue('positionTest'); - expect(provinceInput).toHaveValue('1'); - expect(roleInput).toHaveValue('1'); - expect(genderInput).toHaveValue('male'); - }); - }); - }) -}); \ No newline at end of file diff --git a/src/components/dashboard/expert-management/Form/UpdateForm/index.jsx b/src/components/dashboard/expert-management/Form/UpdateForm/index.jsx deleted file mode 100644 index 27fae5c..0000000 --- a/src/components/dashboard/expert-management/Form/UpdateForm/index.jsx +++ /dev/null @@ -1,33 +0,0 @@ -import {useTranslations} from "next-intl"; -import {Dialog, DialogTitle, IconButton, Tooltip} from "@mui/material"; -import EditIcon from '@mui/icons-material/Edit'; -import {useState} from "react"; -import UpdateContent from "./UpdateContent"; - - -const Update = ({row, mutate}) => { - const t = useTranslations(); - const [openUpdateDialog, setOpenUpdateDialog] = useState(false); - - return ( - <> - - { - setOpenUpdateDialog(true) - }} - > - - - - - {t("ExpertMangement.update_expert")} - - - - ); -}; -export default Update; \ No newline at end of file diff --git a/src/components/dashboard/expert-management/TableRowActions.jsx b/src/components/dashboard/expert-management/TableRowActions.jsx deleted file mode 100644 index 91e778e..0000000 --- a/src/components/dashboard/expert-management/TableRowActions.jsx +++ /dev/null @@ -1,24 +0,0 @@ -import {Box} from "@mui/material"; -import Update from "./Form/UpdateForm" -import Delete from "./Form/DeleteForm"; - -const TableRowActions = ({row, mutate}) => { - return ( - - - {/**/} - - - ); -}; - -export default TableRowActions; diff --git a/src/components/dashboard/expert-management/TableToolbar.jsx b/src/components/dashboard/expert-management/TableToolbar.jsx deleted file mode 100644 index 24f26f7..0000000 --- a/src/components/dashboard/expert-management/TableToolbar.jsx +++ /dev/null @@ -1,14 +0,0 @@ -import {useTranslations} from "next-intl"; -import Create from "./Form/CreateForm"; -import {Box} from "@mui/material"; - -function TableToolbar({mutate}) { - const t = useTranslations(); - return ( - - - - ); -} - -export default TableToolbar; diff --git a/src/components/dashboard/expert-management/index.jsx b/src/components/dashboard/expert-management/index.jsx deleted file mode 100644 index cd90e4f..0000000 --- a/src/components/dashboard/expert-management/index.jsx +++ /dev/null @@ -1,9 +0,0 @@ -import ExpertManagementDataTable from "@/components/dashboard/expert-management/DataTable"; - -function DashboardExpertManagementComponent() { - return ( - - ); -} - -export default DashboardExpertManagementComponent; diff --git a/src/components/dashboard/first/index.jsx b/src/components/dashboard/first/index.jsx deleted file mode 100644 index 02e5672..0000000 --- a/src/components/dashboard/first/index.jsx +++ /dev/null @@ -1,5 +0,0 @@ -const DashboardFirstComponent = () => { - return <>; -}; - -export default DashboardFirstComponent; diff --git a/src/components/dashboard/role-management/Form/CreateForm/CreateContent.jsx b/src/components/dashboard/role-management/Form/CreateForm/CreateContent.jsx deleted file mode 100644 index 8a8f5e7..0000000 --- a/src/components/dashboard/role-management/Form/CreateForm/CreateContent.jsx +++ /dev/null @@ -1,153 +0,0 @@ -import { - Button, - Checkbox, - CircularProgress, - DialogActions, - DialogContent, - DialogTitle, - FormControl, - FormControlLabel, - FormHelperText, - FormLabel, - Grid, - Stack, - TextField, - Typography -} 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} from "@/core/data/apiRoutes"; -import usePermissions from "@/lib/app/hooks/usePermissions"; - -const CreateContent = ({mutate, setOpenConfirmDialog}) => { - const t = useTranslations(); - const requestServer = useRequest({auth: true}) - 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")), - 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", 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, 'post', { - data: formData, - }).then(() => { - setOpenConfirmDialog(false) - mutate() - update_notification() - }).catch(() => { - }).finally(() => { - setSubmitting(false); - }); - }, - }); - - return ( - <> - {t("AddDialog.add")} - - - - - - - - {t("AddDialog.permission")}
- - {formik.touched.permissions && formik.errors.permissions} - {isLoading ? - - - {t("AddDialog.loading_permissions_list")} - - : ( - - <> - {permissions_list.map((permission, index) => ( - - { - 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} - /> - - ))} - - - ) - } - -
-
-
-
- - - - - - ) -} -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 deleted file mode 100644 index 2733f9f..0000000 --- a/src/components/dashboard/role-management/Form/CreateForm/__test__/CreateContent.test.js +++ /dev/null @@ -1,177 +0,0 @@ -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"; - -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("افزودن") - await waitFor(()=>{ - expect(textElement).toBeInTheDocument() - }) - }); - it('should see name_en text', async () => { - render( - - - - ) - const textElement = screen.queryByLabelText("نام انگلیسی") - await waitFor(()=>{ - expect(textElement).toBeInTheDocument() - }) - }); - it('should see name_fa text', async () => { - render( - - - - ) - const textElement = screen.queryByLabelText("نام فارسی") - await waitFor(()=>{ - expect(textElement).toBeInTheDocument() - }) - }); - it('should see name_fa text', async () => { - render( - - - - ) - const textElement = screen.queryByLabelText("نام فارسی") - await waitFor(()=>{ - 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 () => { - 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 get checked if permission list item get clicked', async () => { - render( - - - - ) - const checkboxElement = screen.getByLabelText("مدیریت کارتابل رییس اداره مسافری استان") - - await waitFor(()=>{ - expect(checkboxElement).not.toBeChecked(); - }) - - fireEvent.click(checkboxElement); - - await waitFor(()=>{ - 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 submit button be disabled', () => { - render( - - - - ) - const submitButtonElement = screen.queryByText("ثبت") - expect(submitButtonElement).toBeDisabled() - }); - - it('should submit button be able if inputs get filled', async () => { - render( - - - - ) - const submitButtonElement = screen.queryByText("ثبت") - expect(submitButtonElement).toBeDisabled() - const nameElement = screen.getByLabelText("نام انگلیسی") - const name_faElement = screen.getByLabelText("نام فارسی") - const roleElement = await screen.findByTestId("PermissionList-checkbox-0"); - fireEvent.change(nameElement, { target: { value: 'amin' } }) - fireEvent.change(name_faElement, { target: { value: 'امین' } }) - fireEvent.click(roleElement) - await waitFor(()=>{ - expect(submitButtonElement).not.toBeDisabled() - }) - }); - }) -}) \ 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 deleted file mode 100644 index 7bc9db2..0000000 --- a/src/components/dashboard/role-management/Form/CreateForm/__test__/index.test.js +++ /dev/null @@ -1,34 +0,0 @@ -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', async () => { - render( - - - - ) - const textElement = screen.queryByText("افزودن") - await waitFor(()=>{ - expect(textElement).toBeInTheDocument() - }) - }); - - it('should see Tooltip text when mouse over', async () => { - render( - - - - ) - const buttonElement = screen.getByText("افزودن"); - fireEvent.mouseOver(buttonElement); - const tooltipTextElement = screen.getByText("افزودن"); - await waitFor(()=>{ - 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 deleted file mode 100644 index 455d9a2..0000000 --- a/src/components/dashboard/role-management/Form/CreateForm/index.jsx +++ /dev/null @@ -1,34 +0,0 @@ -import {Button, Dialog, 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}) => { - 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/DeleteContent.jsx b/src/components/dashboard/role-management/Form/DeleteForm/DeleteContent.jsx deleted file mode 100644 index f1580ce..0000000 --- a/src/components/dashboard/role-management/Form/DeleteForm/DeleteContent.jsx +++ /dev/null @@ -1,42 +0,0 @@ -import {Button, DialogActions, DialogContent, DialogTitle, Typography} from "@mui/material"; -import {DELETE_ROLE} 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, 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}/${rowId}`, 'delete').then((response) => { - mutate() - 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 deleted file mode 100644 index 7258e10..0000000 --- a/src/components/dashboard/role-management/Form/DeleteForm/__test__/DeleteContent.test.js +++ /dev/null @@ -1,53 +0,0 @@ -import {act, render, screen, waitFor} 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("حذف") - await waitFor(() => { - expect(textElement).toBeInTheDocument() - }) - }) - it('should see DeleteDialog text content ', async () => { - render( - - - - ) - const textElement = screen.queryByText("آیا از حدف این مورد اطمینان دارید ؟") - await waitFor(() => { - expect(textElement).toBeInTheDocument() - }) - }) - it('should see delete text in the delete button ', async () => { - render( - - - - ) - const buttonElement = screen.queryByText("حذف کردن") - await waitFor(() => { - expect(buttonElement).toBeInTheDocument() - }) - }); - it('should see cancel text in the cancel button ', async () => { - render( - - - - ) - const buttonElement = screen.queryByText("انصراف") - await waitFor(() => { - 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 deleted file mode 100644 index d8eaaa2..0000000 --- a/src/components/dashboard/role-management/Form/DeleteForm/__test__/index.test.js +++ /dev/null @@ -1,21 +0,0 @@ -import {fireEvent, render, screen, waitFor} 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 when mouse is over', async () => { - render( - - - - ) - 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 deleted file mode 100644 index 4ff70bd..0000000 --- a/src/components/dashboard/role-management/Form/DeleteForm/index.jsx +++ /dev/null @@ -1,32 +0,0 @@ -import {useTranslations} from "next-intl"; -import {useState} from "react"; -import {Dialog, IconButton, Tooltip} from "@mui/material"; -import DeleteIcon from '@mui/icons-material/Delete'; -import DeleteContent from "@/components/dashboard/role-management/Form/DeleteForm/DeleteContent"; - -const DeleteForm = ({rowId, mutate}) => { - const t = useTranslations(); - const [openConfirmDialog, setOpenConfirmDialog] = useState(false); - - - return ( - <> - - { - setOpenConfirmDialog(true) - }} - > - - - - - - - - ); -}; -export default DeleteForm; \ No newline at end of file diff --git a/src/components/dashboard/role-management/Form/UpdateForm/UpdateContent.jsx b/src/components/dashboard/role-management/Form/UpdateForm/UpdateContent.jsx deleted file mode 100644 index 390d7bb..0000000 --- a/src/components/dashboard/role-management/Form/UpdateForm/UpdateContent.jsx +++ /dev/null @@ -1,153 +0,0 @@ -import { - Button, - Checkbox, - CircularProgress, - DialogActions, - DialogContent, - DialogTitle, - FormControl, - FormControlLabel, - FormHelperText, - FormLabel, - Grid, - Stack, - TextField, - Typography -} from "@mui/material"; -import {useFormik} from "formik"; -import {UPDATE_ROLE} 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"; - -const UpdateContent = ({mutate, row, setOpenConfirmDialog}) => { - const t = useTranslations(); - const requestServer = useRequest({auth: true}) - const {update_notification} = useNotification() - const {permissions_list, isLoading} = usePermissions() - - const validationSchema = Yup.object().shape({ - 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_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_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(`${UPDATE_ROLE}/${row.getValue("id")}`, 'post', { - data: formData, - }).then((response) => { - setOpenConfirmDialog(false) - mutate() - update_notification() - }).catch(() => { - }).finally(() => { - setSubmitting(false); - }); - }, - }); - - return ( - <> - {t("UpdateDialog.update")} - - - - - - - - {t("UpdateDialog.permission")}
- - {formik.touched.permissions && formik.errors.permissions} - {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} - /> - - ))} - - - )} - -
-
-
-
- - - - - - ) -} -export default UpdateContent \ 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 deleted file mode 100644 index 1130d94..0000000 --- a/src/components/dashboard/role-management/Form/UpdateForm/__test__/UpdateContent.test.js +++ /dev/null @@ -1,218 +0,0 @@ -import {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("ویرایش") - await waitFor(() => { - expect(textElement).toBeInTheDocument() - }) - }); - it('should see name_en text', async () => { - render( - - - - ) - const textElement = screen.queryByLabelText("نام انگلیسی") - await waitFor(() => { - expect(textElement).toBeInTheDocument() - }) - }); - it('should see name_fa text', async () => { - render( - - - - ) - const textElement = screen.queryByLabelText("نام فارسی") - await waitFor(() => { - expect(textElement).toBeInTheDocument() - }) - }); - it('should see update text in the submit button ', async () => { - render( - - - - ) - const buttonElement = screen.queryByText("ثبت") - await waitFor(() => { - expect(buttonElement).toBeInTheDocument() - }) - }); - it('should see cancel text in the cancel button ', async () => { - render( - - - - ) - const buttonElement = screen.queryByText("بستن") - await waitFor(() => { - 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 waitFor(() => { - // 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 waitFor(() => { - // Simulate user input - expect(nameFarsiInput).toHaveValue('testuser'); - }) - }); - it('should return permissions_list', async () => { - render( - - - - ) - const checkboxElement = await screen.findAllByTestId("PermissionList-checkbox") - await waitFor(() => { - expect(checkboxElement).toHaveLength(2) - }) - }); - it('should see the value of the name_en that pass to input value', async () => { - render( - - - - ) - const name_enElement = screen.getByLabelText("نام انگلیسی") - await waitFor(() => { - expect(name_enElement).toHaveValue("manage_passenger_office_navgan") - }) - }); - it('should see the value of the name_fa that pass to input value', async () => { - render( - - - - ) - const name_enElement = screen.getByLabelText("نام فارسی") - await waitFor(() => { - expect(name_enElement).toHaveValue("مدیریت کارتابل رییس اداره مسافری استان") - }) - }); - it('should get checked if the id exists in permission lists', async () => { - render( - - - - ) - const checkboxElement = screen.getByLabelText("مدیریت کارتابل رییس اداره مسافری استان") - await waitFor(() => { - 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() - }) - }) - it('should submit button be disabled', () => { - render( - - - - ) - const submitButtonElement = screen.queryByText("ثبت") - expect(submitButtonElement).toBeDisabled() - }); - }) -}) \ 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 deleted file mode 100644 index 2a0ee82..0000000 --- a/src/components/dashboard/role-management/Form/UpdateForm/__test__/index.test.js +++ /dev/null @@ -1,22 +0,0 @@ -import {fireEvent, render, screen, waitFor} 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"; -import userEvent from "@testing-library/user-event"; - -describe("Create Form Component from Role Management Component",()=>{ - describe("Rendering",()=>{ - it('should see Tooltip text when mouse over', async () => { - render( - - - - ) - const buttonElement = screen.getByTestId("dialog_tooltip"); - fireEvent.mouseOver(buttonElement); - 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 deleted file mode 100644 index f9defe7..0000000 --- a/src/components/dashboard/role-management/Form/UpdateForm/index.jsx +++ /dev/null @@ -1,31 +0,0 @@ -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, row}) => { - const t = useTranslations(); - const [openConfirmDialog, setOpenConfirmDialog] = useState(false); - - return ( - - - { - setOpenConfirmDialog(true); - }} - > - - - - - - - - ) -} -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 deleted file mode 100644 index 72cba70..0000000 --- a/src/components/dashboard/role-management/RoleManagementComponent.jsx +++ /dev/null @@ -1,94 +0,0 @@ -import DataTable from "@/core/components/DataTable"; -import {GET_ROLES} from "@/core/data/apiRoutes"; -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(); - - 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 deleted file mode 100644 index d655f09..0000000 --- a/src/components/dashboard/role-management/TableRowActions.jsx +++ /dev/null @@ -1,22 +0,0 @@ -import {Box} from "@mui/material"; -import UpdateForm from "@/components/dashboard/role-management/Form/UpdateForm"; -import DeleteForm from "@/components/dashboard/role-management/Form/DeleteForm"; - - -const TableRowActions = ({row, mutate}) => { - - return ( - - - - - ); -}; - -export default TableRowActions; diff --git a/src/components/dashboard/role-management/TableToolbar.jsx b/src/components/dashboard/role-management/TableToolbar.jsx deleted file mode 100644 index 6a739a7..0000000 --- a/src/components/dashboard/role-management/TableToolbar.jsx +++ /dev/null @@ -1,11 +0,0 @@ -import {useTranslations} from "next-intl"; -import CreateForm from "@/components/dashboard/role-management/Form/CreateForm"; - -function TableToolbar({mutate}) { - 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 deleted file mode 100644 index a60dc01..0000000 --- a/src/components/dashboard/role-management/__test__/RoleManagementComponent.test.js +++ /dev/null @@ -1,63 +0,0 @@ -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', async () => { - render( - - - - ) - const idEelement = screen.queryByText("کد یکتا") - await waitFor(()=>{ - expect(idEelement).toBeInTheDocument() - }) - }); - it('should see name title of data table', async () => { - render( - - - - ) - const nameEelement = screen.queryByText("نام انگلیسی") - await waitFor(()=>{ - expect(nameEelement).toBeInTheDocument() - }) - }); - it('should see name_fa title of data table', async () => { - render( - - - - ) - const name_faEelement = screen.queryByText("نام فارسی") - await waitFor(()=>{ - expect(name_faEelement).toBeInTheDocument() - }) - }); - it('should see created at title of data table', async () => { - render( - - - - ) - const createdEelement = screen.queryByText("تاریخ درخواست") - await waitFor(()=>{ - expect(createdEelement).toBeInTheDocument() - }) - }); - it('should see updated at title of data table', async () => { - render( - - - - ) - const updateEelement = screen.queryByText("تاریخ بروزرسانی") - await waitFor(()=>{ - 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 deleted file mode 100644 index fac37e2..0000000 --- a/src/components/dashboard/role-management/index.jsx +++ /dev/null @@ -1,9 +0,0 @@ -import RoleManagementComponent from "@/components/dashboard/role-management/RoleManagementComponent"; - -function DashboardRoleManagementComponent() { - return ( - - ); -} - -export default DashboardRoleManagementComponent; diff --git a/src/components/errors/403.jsx b/src/components/errors/403.jsx deleted file mode 100644 index ce91443..0000000 --- a/src/components/errors/403.jsx +++ /dev/null @@ -1,37 +0,0 @@ -import CenterLayout from "@/layouts/CenterLayout"; -import FullPageLayout from "@/layouts/FullPageLayout"; -import {Button, Typography} from "@mui/material"; -import {NextLinkComposed} from "@/core/components/LinkRouting"; -import {useTranslations} from "next-intl"; -import TitlePage from "@/core/components/TitlePage"; -import Svg403 from "@/core/components/svgs/Svg403"; - -const UnAuthorizedComponent = () => { - const t = useTranslations(); - - return ( - <> - - - - - - {t("ErrorPage.custom_403")} - - - - - - ); -}; - -export default UnAuthorizedComponent; diff --git a/src/components/errors/404.jsx b/src/components/errors/404.jsx deleted file mode 100644 index acfd418..0000000 --- a/src/components/errors/404.jsx +++ /dev/null @@ -1,37 +0,0 @@ -import CenterLayout from "@/layouts/CenterLayout"; -import FullPageLayout from "@/layouts/FullPageLayout"; -import {Button, Typography} from "@mui/material"; -import {NextLinkComposed} from "@/core/components/LinkRouting"; -import {useTranslations} from "next-intl"; -import TitlePage from "@/core/components/TitlePage"; -import Svg404 from "@/core/components/svgs/Svg404"; - -const NotFoundComponent = () => { - const t = useTranslations(); - - return ( - <> - - - - - - {t("ErrorPage.custom_404")} - - - - - - ); -}; - -export default NotFoundComponent; diff --git a/src/components/errors/500.jsx b/src/components/errors/500.jsx deleted file mode 100644 index 10e9d12..0000000 --- a/src/components/errors/500.jsx +++ /dev/null @@ -1,48 +0,0 @@ -import CenterLayout from "@/layouts/CenterLayout"; -import FullPageLayout from "@/layouts/FullPageLayout"; -import {Box, Button, Container, Stack, Typography} from "@mui/material"; -import {NextLinkComposed} from "@/core/components/LinkRouting"; -import {useTranslations} from "next-intl"; -import Image from "next/image"; -import TitlePage from "@/core/components/TitlePage"; - -const ServerErrorComponent = () => { - const t = useTranslations(); - - return ( - <> - - - - - - - {t("app_name")} - - - {t("ErrorPage.custom_500")} - - - - - - - - ); -}; - -export default ServerErrorComponent; diff --git a/src/components/first/__tests__/index.test.js b/src/components/first/__tests__/index.test.js deleted file mode 100644 index 01a6d94..0000000 --- a/src/components/first/__tests__/index.test.js +++ /dev/null @@ -1,61 +0,0 @@ -import {render, screen, waitFor} from "@testing-library/react"; -import FirstComponent from "@/components/first"; -import MockAppWithProviders from "../../../../mocks/AppWithProvider"; -import {server} from "../../../../mocks/server"; -import {rest} from "msw"; -import {GET_USER} from "@/core/data/apiRoutes"; - -describe("First Component From First Page", () => { - describe("Rendering", () => { - it("App Name Text Rendered", () => { - render(); - const appNameElement = screen.queryByText(/سامانه CRM/i); - expect(appNameElement).toBeInTheDocument() - }); - it("App version Text Rendered", () => { - render(); - const versionControler = screen.queryByText(process.env.NEXT_PUBLIC_API_VERSION, {exact: false}); - expect(versionControler).toBeInTheDocument() - }); - it("Powered By Rendered With Currect URL", () => { - render(); - const linkElement = screen.queryByText('توسعه یافته توسط وایتل'); - expect(linkElement).toBeInTheDocument() - expect(linkElement).toHaveAttribute('href', process.env.NEXT_PUBLIC_POWERED_BY_URL); - }); - }); - describe("Behavioral", () => { - it("Show Login Button And Do Not Show Dashboard Button When User Is Not Authenticated", async () => { - render(); - await waitFor(() => { - const authenticationButtonLogin = screen.queryByText(/ورود کارشناس/i) - const authenticationButtonDashboard = screen.queryByText(/داشبورد/i) - expect(authenticationButtonLogin).toBeInTheDocument() - expect(authenticationButtonDashboard).not.toBeInTheDocument() - }) - }); - it("Show Dashboard Button And Do Not Show Login Button When User Is Authenticated", async () => { - localStorage.setItem("_token", 'token'); - render(); - await waitFor(() => { - const authenticationButtonLogin = screen.queryByText(/ورود کارشناس/i) - const authenticationButtonDashboard = screen.queryByText(/داشبورد/i) - expect(authenticationButtonLogin).not.toBeInTheDocument() - expect(authenticationButtonDashboard).toBeInTheDocument() - }) - }); - it("Show Login Button And Do Not Show Dashboard Button When User Authentication Is Expired", async () => { - localStorage.setItem("_token", 'token'); - server.use(rest.get(GET_USER, (req, res, ctx) => { - return res(ctx.status(401)) - })) - render(); - await waitFor(() => { - const authenticationButtonLogin = screen.queryByText(/ورود کارشناس/i) - const authenticationButtonDashboard = screen.queryByText(/داشبورد/i) - expect(authenticationButtonLogin).toBeInTheDocument() - expect(authenticationButtonDashboard).not.toBeInTheDocument() - }) - }); - }); -}); \ No newline at end of file diff --git a/src/components/first/index.jsx b/src/components/first/index.jsx deleted file mode 100644 index 1d7166d..0000000 --- a/src/components/first/index.jsx +++ /dev/null @@ -1,55 +0,0 @@ -import LinkRouting, {NextLinkComposed} from "@/core/components/LinkRouting"; -import CenterLayout from "@/layouts/CenterLayout"; -import FullPageLayout from "@/layouts/FullPageLayout"; -import useUser from "@/lib/app/hooks/useUser"; -import {Button, Stack, Typography} from "@mui/material"; -import {useTranslations} from "next-intl"; -import SvgDashboard from "@/core/components/svgs/SvgDashboard"; - -const FirstComponent = () => { - const t = useTranslations(); - const {isAuth} = useUser(); - - return ( - - - - - {t("app_name")} - - - - - - v{process.env.NEXT_PUBLIC_API_VERSION} - - - - - {t("powered_by_witel")} - - - - ); -}; - -export default FirstComponent; diff --git a/src/components/layouts/Dashboard/Breadcrumbs/BreadcrumbItem.jsx b/src/components/layouts/Dashboard/Breadcrumbs/BreadcrumbItem.jsx deleted file mode 100644 index c233be3..0000000 --- a/src/components/layouts/Dashboard/Breadcrumbs/BreadcrumbItem.jsx +++ /dev/null @@ -1,19 +0,0 @@ -import LinkRouting from "@/core/components/LinkRouting"; -import {Typography} from "@mui/material"; -import {useTranslations} from "next-intl"; - -export default function BreadcrumbItem(props) { - const t = useTranslations(); - const isLast = props.index === props.RouterArray.length - 1; - const url = `/${props.RouterArray.slice(0, props.index + 1).join("/")}`; - - return isLast ? ( - - {t("sidebar." + props.label)} - - ) : ( - - {t("sidebar." + props.label)} - - ); -} diff --git a/src/components/layouts/Dashboard/Breadcrumbs/index.jsx b/src/components/layouts/Dashboard/Breadcrumbs/index.jsx deleted file mode 100644 index 9ab234e..0000000 --- a/src/components/layouts/Dashboard/Breadcrumbs/index.jsx +++ /dev/null @@ -1,49 +0,0 @@ -import {useRouter} from "next/router"; -import {Box, Breadcrumbs} from "@mui/material"; -import {useTheme} from "@mui/material/styles"; -import {NavigateBefore, NavigateNext} from "@mui/icons-material"; -import BreadcrumbItem from "./BreadcrumbItem"; - -const BreadCrumbs = (props) => { - const {isVisible} = props; - const theme = useTheme(); - const router = useRouter(); - - if (!isVisible) { - return null; - } - - const {pathname} = router; - const RouterArray = pathname.split("/").filter((segment) => segment !== ""); - - if (RouterArray.length === 1) { - return null; - } - - return ( - - - ) : ( - - ) - } - aria-label="breadcrumb" - > - {RouterArray.map((segment, index) => ( - - ))} - - - ); -}; - -export default BreadCrumbs; diff --git a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallActions/ActionHeader/__test__/index.test.js b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallActions/ActionHeader/__test__/index.test.js deleted file mode 100644 index 69f9b52..0000000 --- a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallActions/ActionHeader/__test__/index.test.js +++ /dev/null @@ -1,43 +0,0 @@ -import {render, screen, waitFor} from "@testing-library/react"; -import MockAppWithProviders from "../../../../../../../../../../../mocks/AppWithProvider"; -import {AnswersProvider} from "@/lib/callWidget/contexts/answers"; -import ActionHeader from ".."; - -const tab = { - active: true, - active_category_id: 1, - phone_number : "09134849737" -} - -describe('Action Header Button from Call Actions Categories', () => { - describe('Rendering', () => { - it('should see header text', async () => { - render( - - - - - - ) - const headingElement = screen.getByRole('heading', { - name: /\.\.\.\. عملیات های مربوط به تماس:/i - }) - await waitFor(() => { - expect(headingElement).toBeInTheDocument() - }) - }); - it('should see phone number', async () => { - render( - - - - - - ) - const phone_numberElement = screen.queryByText(/09134849737/i) - await waitFor(() => { - expect(phone_numberElement).toBeInTheDocument() - }) - }); - }); -}); \ No newline at end of file diff --git a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallActions/ActionHeader/index.jsx b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallActions/ActionHeader/index.jsx deleted file mode 100644 index 97b4297..0000000 --- a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallActions/ActionHeader/index.jsx +++ /dev/null @@ -1,25 +0,0 @@ -import {useTranslations} from "next-intl"; -import {Stack, Typography} from "@mui/material"; - -const ActionHeader = ({tab}) => { - const t = useTranslations(); - return ( - - - .... {t("CallAction.call_history_of")}: - - - {tab.phone_number} .... - - - ) -} - -export default ActionHeader \ No newline at end of file diff --git a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallActions/CallActionCategories/CallActionCategoriesButton.jsx b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallActions/CallActionCategories/CallActionCategoriesButton.jsx deleted file mode 100644 index 40c2b18..0000000 --- a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallActions/CallActionCategories/CallActionCategoriesButton.jsx +++ /dev/null @@ -1,25 +0,0 @@ -import { Button, Grid } from "@mui/material"; -import useCategories from "@/lib/callWidget/hooks/useCategories"; - -const CallActionCategoriesButton = ({ category, tab }) => { - const { setActiveCategoryID } = useCategories(); - - return ( - - - - - - ); -}; -export default CallActionCategoriesButton; diff --git a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallActions/CallActionCategories/__test__/CallActionCategoriesButton.test.js b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallActions/CallActionCategories/__test__/CallActionCategoriesButton.test.js deleted file mode 100644 index 195df02..0000000 --- a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallActions/CallActionCategories/__test__/CallActionCategoriesButton.test.js +++ /dev/null @@ -1,51 +0,0 @@ -import {fireEvent, render, screen, waitFor} from "@testing-library/react"; -import MockAppWithProviders from "../../../../../../../../../../../mocks/AppWithProvider"; -import {AnswersProvider} from "@/lib/callWidget/contexts/answers"; -import CallActionCategoriesButton from "../CallActionCategoriesButton"; - -const category = { - category_id: 1, - category_name: "اطلاعات راه ها", -} -const tab = { - active: true, - active_category_id: 1, -} - -describe('Call Action Categories Button from Call Actions Categories', () => { - describe('Rendering', () => { - it('should categories names', async () => { - render( - - - - - - ) - const categoryElement = screen.queryByText("اطلاعات راه ها") - await waitFor(() => { - expect(categoryElement).toBeInTheDocument() - }) - }); - }); - describe("Behavior", ()=>{ - it("Should change the color when button clecked",async()=>{ - render( - - - - - - ) - const buttonElement = screen.getByRole('button', { - name: /اطلاعات راه ها/i - }) - - fireEvent.click(buttonElement); - - await waitFor(() => { - expect(buttonElement).toHaveStyle('background-color: rgb(12, 31, 23)'); - }) - }) - }) -}) \ No newline at end of file diff --git a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallActions/CallActionCategories/__test__/index.test.js b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallActions/CallActionCategories/__test__/index.test.js deleted file mode 100644 index ee223ff..0000000 --- a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallActions/CallActionCategories/__test__/index.test.js +++ /dev/null @@ -1,29 +0,0 @@ -import { AnswersProvider } from "@/lib/callWidget/contexts/answers"; -import CallActionsCategories from ".."; -import MockAppWithProviders from "../../../../../../../../../../../mocks/AppWithProvider"; -import { render, screen, waitFor } from "@testing-library/react"; - -const tab = { - active: true, - active_category_id: 1, - id : 1, - phone_number : "09134849737" -} - -describe('Call Action Categories Button from Call Actions Categories', () => { - describe('Rendering', () => { - it('should see divider text', async () => { - render( - - - - - - ) - const divider_textElement = screen.queryByText("موضوع ها") - await waitFor(() => { - expect(divider_textElement).toBeInTheDocument() - }) - }); - }); -}) \ No newline at end of file diff --git a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallActions/CallActionCategories/index.jsx b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallActions/CallActionCategories/index.jsx deleted file mode 100644 index dc814ec..0000000 --- a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallActions/CallActionCategories/index.jsx +++ /dev/null @@ -1,22 +0,0 @@ -import { Chip, Divider, Grid, Stack } from "@mui/material"; -import useCategories from "@/lib/callWidget/hooks/useCategories"; -import CallActionCategoriesButton from "./CallActionCategoriesButton"; -import { useTranslations } from "next-intl"; - -const CallActionsCategories = ({ tab }) => { - const { categoryLists } = useCategories(); - const t = useTranslations(); - return ( - <> - - - - {categoryLists.map((category) => { - return ; - })} - - - - ); -}; -export default CallActionsCategories; diff --git a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallActions/CallActionDescription/__test__/index.test.js b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallActions/CallActionDescription/__test__/index.test.js deleted file mode 100644 index b7e0543..0000000 --- a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallActions/CallActionDescription/__test__/index.test.js +++ /dev/null @@ -1,54 +0,0 @@ -import {fireEvent, render, screen, waitFor} from "@testing-library/react"; -import MockAppWithProviders from "../../../../../../../../../../../mocks/AppWithProvider"; -import {AnswersProvider} from "@/lib/callWidget/contexts/answers"; -import CallActionDescription from ".."; - -describe('Action Header Button from Call Actions Categories', () => { - describe('Rendering', () => { - it('should see placeholder text', async () => { - render( - - - - - - ) - const textElement = screen.getByPlaceholderText('لطفا توضیحات خود را وارد نمایید') - await waitFor(() => { - expect(textElement).toBeInTheDocument() - }) - }); - it('should see header text', async () => { - render( - - - - - - ) - const textElement = screen.getByText('توضیحات تماس (اختیاری)') - await waitFor(() => { - expect(textElement).toBeInTheDocument() - }) - }); - }); - describe("Behavior", ()=>{ - it("Should see what write in input", async()=>{ - const setDescription = jest.fn(); - render( - - - - - - ) - const textElement = screen.getByPlaceholderText('لطفا توضیحات خود را وارد نمایید') - - fireEvent.change(textElement, {target : {value : "امین"}}) - - await waitFor(() => { - expect(textElement).toHaveValue("امین") - }) - }) - }) -}); \ No newline at end of file diff --git a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallActions/CallActionDescription/index.jsx b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallActions/CallActionDescription/index.jsx deleted file mode 100644 index 6a29324..0000000 --- a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallActions/CallActionDescription/index.jsx +++ /dev/null @@ -1,27 +0,0 @@ -import { Box, Chip, Divider, TextField } from "@mui/material" -import { useTranslations } from "next-intl"; - -const CallActionDescription = ({setDescription}) => { - const t = useTranslations(); - return( - <> - - - { - setDescription(e.target.value) - }} - /> - - - ) -} -export default CallActionDescription \ No newline at end of file diff --git a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallActions/CallActionSubcategories/CallActionSubcategoriesButton.jsx b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallActions/CallActionSubcategories/CallActionSubcategoriesButton.jsx deleted file mode 100644 index 2b4a479..0000000 --- a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallActions/CallActionSubcategories/CallActionSubcategoriesButton.jsx +++ /dev/null @@ -1,14 +0,0 @@ -import { Button, Grid } from "@mui/material"; - -const CallActionSubcategoriesButton = ({ sub_category, tab, handleSubcategoryButton }) => { - - return sub_category.category_id === tab.active_category_id ? ( - - - - ) : null; -}; -export default CallActionSubcategoriesButton; diff --git a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallActions/CallActionSubcategories/__test__/CallActionSubcategoriesButton.test.js b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallActions/CallActionSubcategories/__test__/CallActionSubcategoriesButton.test.js deleted file mode 100644 index 718f7cd..0000000 --- a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallActions/CallActionSubcategories/__test__/CallActionSubcategoriesButton.test.js +++ /dev/null @@ -1,37 +0,0 @@ -import {render, screen, waitFor} from "@testing-library/react"; -import MockAppWithProviders from "../../../../../../../../../../../mocks/AppWithProvider"; -import {AnswersProvider} from "@/lib/callWidget/contexts/answers"; -import CallActionSubcategoriesButton from "../CallActionSubcategoriesButton"; - -const sub_category = { - category_id: 1, - category_name: "اطلاعات راه", - subcategory_id: 1, - subcategory_name: "اطلاعات باز و بسته" -}; - -const tab = { - active: true, - active_category_id: 1, - date: new Date(), - id: 1, - phone_number: "09134849737" -}; - -describe('Call Action Subcategories Button from Call Actions Categories', () => { - describe('Rendering', () => { - it('should render subcategory name', async () => { - render( - - - - - - ); - const subCategoryElement = screen.queryByText(/اطلاعات باز و بسته/i); - await waitFor(() => { - expect(subCategoryElement).toBeInTheDocument(); - }); - }); - }); -}); \ No newline at end of file diff --git a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallActions/CallActionSubcategories/__test__/index.test.js b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallActions/CallActionSubcategories/__test__/index.test.js deleted file mode 100644 index 2195bde..0000000 --- a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallActions/CallActionSubcategories/__test__/index.test.js +++ /dev/null @@ -1,29 +0,0 @@ -import { AnswersProvider } from "@/lib/callWidget/contexts/answers"; -import CallActionsSubcategories from ".."; -import MockAppWithProviders from "../../../../../../../../../../../mocks/AppWithProvider"; -import { render, screen, waitFor } from "@testing-library/react"; - -const tab = { - active: true, - active_category_id: 1, - id : 1, - phone_number : "09134849737" -} - -describe('Call Action Categories Button from Call Actions Categories', () => { - describe('Rendering', () => { - it('should see divider text', async () => { - render( - - - - - - ) - const divider_textElement = screen.queryByText("زیر موضوع ها") - await waitFor(() => { - expect(divider_textElement).toBeInTheDocument() - }) - }); - }); -}) \ No newline at end of file diff --git a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallActions/CallActionSubcategories/index.jsx b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallActions/CallActionSubcategories/index.jsx deleted file mode 100644 index 0b88cfa..0000000 --- a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallActions/CallActionSubcategories/index.jsx +++ /dev/null @@ -1,22 +0,0 @@ -import useCategories from "@/lib/callWidget/hooks/useCategories"; -import { Chip, Divider, Grid, Stack } from "@mui/material"; -import CallActionSubcategoriesButton from "@/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallActions/CallActionSubcategories/CallActionSubcategoriesButton"; -import { useTranslations } from "next-intl"; - -const CallActionsSubcategories = ({ tab, handleSubcategoryButton }) => { - const { subCategoryLists } = useCategories(); - const t = useTranslations(); - return ( - <> - - - - {subCategoryLists.map((sub_category, index) => { - return ; - })} - - - - ); -}; -export default CallActionsSubcategories; 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 837d354..0000000 --- a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallActions/index.jsx +++ /dev/null @@ -1,49 +0,0 @@ -import CallActionsCategories from "./CallActionCategories"; -import CallActionsSubcategories from "./CallActionSubcategories"; -import ActionHeader from "./ActionHeader"; -import CallActionDescription from "./CallActionDescription"; -import useRequest from "@/lib/app/hooks/useRequest"; -import useAnswers from "@/lib/callWidget/hooks/useAnswers"; -import {useState} from "react"; -import {Stack} from "@mui/material"; -import {CALL_ACTION} from "@/core/data/apiRoutes"; - -function CallActions({tab}) { - const [description, setDescription] = useState("") - const requestServer = useRequest({auth: true}); - const {closeAnswerTab} = useAnswers(); - - const handleSubcategoryButton = (category_id, subcategory_id) => { - let data = {} - data['category_id'] = category_id - data['subcategory_id'] = subcategory_id - if (description !== "") - data["description"] = description - - requestServer(`${CALL_ACTION}/${tab.id}`, "post", { - data - }) - .then(() => { - closeAnswerTab(tab.id); - }) - .catch(() => { - }); - }; - - - return ( - - - - - - - - - ); -} - -export default CallActions; diff --git a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallHistory/ErrorOrEmpty/__tests__/index.test.js b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallHistory/ErrorOrEmpty/__tests__/index.test.js deleted file mode 100644 index 91d5651..0000000 --- a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallHistory/ErrorOrEmpty/__tests__/index.test.js +++ /dev/null @@ -1,19 +0,0 @@ -import {render, screen} from "@testing-library/react"; -import ErrorOrEmpty from "../../ErrorOrEmpty"; -import MockAppWithProviders from "../../../../../../../../../../../mocks/AppWithProvider"; - -describe("ErrorOrEmpty Component From call history", () => { - describe("Rendering", () => { - it("error fetching history of call text rendered", () => { - render(); - const errorText = screen.queryByText('خطا در دریافت تاریخچه تماس دریافتی'); - expect(errorText).toBeInTheDocument(); - }); - it("empty fetching history of call text rendered", () => { - render(); - const emptyText = screen.queryByText('تاریخچه ای برای تماس دریافتی یافت نشد'); - expect(emptyText).toBeInTheDocument(); - - }); - }); -}); \ No newline at end of file diff --git a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallHistory/ErrorOrEmpty/index.jsx b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallHistory/ErrorOrEmpty/index.jsx deleted file mode 100644 index bb0f818..0000000 --- a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallHistory/ErrorOrEmpty/index.jsx +++ /dev/null @@ -1,33 +0,0 @@ -import {useTranslations} from "next-intl"; -import {Typography} from "@mui/material"; - -const ErrorOrEmpty = ({isErrorOrEmpty}) => { - const t = useTranslations(); - return ( - <> - {isErrorOrEmpty === "error" ? - - {t("CallHistory.call_history_error_fetching")} - - : isErrorOrEmpty === "empty" ? - - {t("CallHistory.call_history_not_found")} - : <> - } - - ) -} - -export default ErrorOrEmpty diff --git a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallHistory/HistoryHeader/__tests__/index.test.js b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallHistory/HistoryHeader/__tests__/index.test.js deleted file mode 100644 index 51d700e..0000000 --- a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallHistory/HistoryHeader/__tests__/index.test.js +++ /dev/null @@ -1,21 +0,0 @@ -import {render, screen} from "@testing-library/react"; -import HistoryHeader from "../../HistoryHeader"; -import MockAppWithProviders from "../../../../../../../../../../../mocks/AppWithProvider"; - -describe("HistoryHeader Component From call history", () => { - describe("Rendering", () => { - const tab = { - phone_number: "09111111111" - } - it("text of header rendered", () => { - render(); - const textHeader = screen.queryByText(/تاریخچه تماس های/i); - expect(textHeader).toBeInTheDocument(); - }); - it("caller number in header rendered", () => { - render(); - const callerNumber = screen.queryByText(/09111111111/i); - expect(callerNumber).toBeInTheDocument(); - }); - }); -}); \ No newline at end of file diff --git a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallHistory/HistoryHeader/index.jsx b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallHistory/HistoryHeader/index.jsx deleted file mode 100644 index 21a3270..0000000 --- a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallHistory/HistoryHeader/index.jsx +++ /dev/null @@ -1,25 +0,0 @@ -import {useTranslations} from "next-intl"; -import {Stack, Typography} from "@mui/material"; - -const HistoryHeader = ({tab}) => { - const t = useTranslations(); - return ( - - - .... {t("CallHistory.call_history_of")}: - - - {tab.phone_number} .... - - - ) -} - -export default HistoryHeader diff --git a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallHistory/ListItemOfCalls/PreviousOperatorData/__tests__/index.test.js b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallHistory/ListItemOfCalls/PreviousOperatorData/__tests__/index.test.js deleted file mode 100644 index c057146..0000000 --- a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallHistory/ListItemOfCalls/PreviousOperatorData/__tests__/index.test.js +++ /dev/null @@ -1,24 +0,0 @@ -import {render, screen} from "@testing-library/react"; -import MockAppWithProviders from "../../../../../../../../../../../../mocks/AppWithProvider"; -import PreviousOperatorData from "../../PreviousOperatorData"; -import moment from "jalali-moment"; - -describe("PreviousOperatorData Component From call history", () => { - describe("Rendering", () => { - const historyItem = { - operator_full_name: "test name", - created_at: "2023-11-19T10:32:48" - } - it("operator name rendered", () => { - render(); - const operatorName = screen.queryByText("test name"); - expect(operatorName).toBeInTheDocument(); - }); - it("operator answer date rendered", () => { - const dateTime = moment(historyItem.created_at).locale('fa').format("HH:mm:ss | YYYY/MM/DD") - render(); - const operatorAnswerDate = screen.queryByText(dateTime); - expect(operatorAnswerDate).toBeInTheDocument(); - }); - }); -}); \ No newline at end of file diff --git a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallHistory/ListItemOfCalls/PreviousOperatorData/index.jsx b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallHistory/ListItemOfCalls/PreviousOperatorData/index.jsx deleted file mode 100644 index 357c6e8..0000000 --- a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallHistory/ListItemOfCalls/PreviousOperatorData/index.jsx +++ /dev/null @@ -1,31 +0,0 @@ -import {useTranslations} from "next-intl"; -import {Avatar, Box, ListItem, ListItemAvatar, ListItemText, Typography} from "@mui/material"; -import HeadsetMicIcon from '@mui/icons-material/HeadsetMic'; -import useLanguage from "@/lib/app/hooks/useLanguage"; -import moment from "jalali-moment"; - -const PreviousOperatorData = ({historyItem}) => { - const {languageApp} = useLanguage() - const t = useTranslations(); - return ( - - - - - - - - - {historyItem.operator_full_name} - - - ({t("CallHistory.operator")}) - - - } secondary={moment(historyItem.created_at).locale(languageApp).format("HH:mm:ss | YYYY/MM/DD")}/> - - ) -} - -export default PreviousOperatorData diff --git a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallHistory/ListItemOfCalls/Topics/__tests__/index.test.js b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallHistory/ListItemOfCalls/Topics/__tests__/index.test.js deleted file mode 100644 index 6f1c3cb..0000000 --- a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallHistory/ListItemOfCalls/Topics/__tests__/index.test.js +++ /dev/null @@ -1,28 +0,0 @@ -import {render, screen} from "@testing-library/react"; -import MockAppWithProviders from "../../../../../../../../../../../../mocks/AppWithProvider"; -import Topics from "../../Topics"; - -describe("Topics Component From call history", () => { - describe("Rendering", () => { - const historyItem = { - category_name: "test category", - subcategory_name: "test sub category", - description: "test desc" - } - it("operator name rendered", () => { - render(); - const operatorName = screen.queryByText("test category"); - expect(operatorName).toBeInTheDocument(); - }); - it("operator answer date rendered", () => { - render(); - const operatorAnswerDate = screen.queryByText("test sub category"); - expect(operatorAnswerDate).toBeInTheDocument(); - }); - it("operator description rendered", () => { - render(); - const operatorAnswerDate = screen.queryByText("test desc"); - expect(operatorAnswerDate).toBeInTheDocument(); - }); - }); -}); \ No newline at end of file diff --git a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallHistory/ListItemOfCalls/Topics/index.jsx b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallHistory/ListItemOfCalls/Topics/index.jsx deleted file mode 100644 index 064c37b..0000000 --- a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallHistory/ListItemOfCalls/Topics/index.jsx +++ /dev/null @@ -1,56 +0,0 @@ -import {useTranslations} from "next-intl"; -import {Box, List, ListItem, Typography} from "@mui/material"; - -const Topics = ({historyItem}) => { - const t = useTranslations(); - return ( - - - - - - {t("CallHistory.category_name")}: - - - {historyItem.category_name || t("CallHistory.no_data_exist")} - - - - - - - - - {t("CallHistory.subcategory_name")}: - - - {historyItem.subcategory_name || t("CallHistory.no_data_exist")} - - - - - {historyItem.description ? - - - - {t("CallHistory.description")}: - - - {historyItem.description} - - - - : ""} - - - ) -} - -export default Topics diff --git a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallHistory/ListItemOfCalls/index.jsx b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallHistory/ListItemOfCalls/index.jsx deleted file mode 100644 index 7af4fd5..0000000 --- a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallHistory/ListItemOfCalls/index.jsx +++ /dev/null @@ -1,21 +0,0 @@ -import {useTranslations} from "next-intl"; -import {Grow, List} from "@mui/material"; -import PreviousOperatorData from "./PreviousOperatorData"; -import Topics from "./Topics"; - -const ListItemOfCalls = ({historyItem}) => { - const t = useTranslations(); - - // console.log(historyItem) - - return ( - - - - - - - ) -} - -export default ListItemOfCalls diff --git a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallHistory/LoadingHistory/__tests__/intex.test.js b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallHistory/LoadingHistory/__tests__/intex.test.js deleted file mode 100644 index 1592a02..0000000 --- a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallHistory/LoadingHistory/__tests__/intex.test.js +++ /dev/null @@ -1,68 +0,0 @@ -import {render, screen} from "@testing-library/react"; -import LoadingHistory from "../../LoadingHistory"; -import MockAppWithProviders from "../../../../../../../../../../../mocks/AppWithProvider"; - -describe("LoadingHistory Component From call history", () => { - describe("Rendering", () => { - it("loading list rendered", () => { - render(); - const loadingList = screen.queryByTestId("loading-list"); - expect(loadingList).toBeInTheDocument(); - }); - it("loading list item avatar rendered", () => { - render(); - const loadingListItemAvatar = screen.queryByTestId("loading-listItemAvatar"); - expect(loadingListItemAvatar).toBeInTheDocument(); - }); - it("loading list item text rendered", () => { - render(); - const loadingListItemText = screen.queryByTestId("loading-listItemText"); - expect(loadingListItemText).toBeInTheDocument(); - }); - it("loading list item category rendered", () => { - render(); - const loadingListItemCat = screen.queryByTestId("loading-listItem-cat"); - expect(loadingListItemCat).toBeInTheDocument(); - }); - it("loading list item sub category rendered", () => { - render(); - const loadingListItemSubCat = screen.queryByTestId("loading-listItem-subCat"); - expect(loadingListItemSubCat).toBeInTheDocument(); - }); - it("operator avatar skeleton rendered", () => { - render(); - const operatorAvatarSkeleton = screen.queryByTestId("operator-avatar-skeleton"); - expect(operatorAvatarSkeleton).toBeInTheDocument(); - }); - it("operator name skeleton rendered", () => { - render(); - const operatorNameSkeleton = screen.queryByTestId("operator-name-skeleton"); - expect(operatorNameSkeleton).toBeInTheDocument(); - }); - it("operator answer date skeleton rendered", () => { - render(); - const operatorAnswerDateSkeleton = screen.queryByTestId("operator-answer-date-skeleton"); - expect(operatorAnswerDateSkeleton).toBeInTheDocument(); - }); - it("operator category header skeleton rendered", () => { - render(); - const operatorCategoryHeaderSkeleton = screen.queryByTestId("operator-category-header-skeleton"); - expect(operatorCategoryHeaderSkeleton).toBeInTheDocument(); - }); - it("operator category value skeleton rendered", () => { - render(); - const operatorCategoryValueSkeleton = screen.queryByTestId("operator-category-value-skeleton"); - expect(operatorCategoryValueSkeleton).toBeInTheDocument(); - }); - it("operator subCategory header skeleton rendered", () => { - render(); - const operatorSubCategoryHeaderSkeleton = screen.queryByTestId("operator-subCategory-header-skeleton"); - expect(operatorSubCategoryHeaderSkeleton).toBeInTheDocument(); - }); - it("operator subCategory value skeleton rendered", () => { - render(); - const operatorSubCategoryValueSkeleton = screen.queryByTestId("operator-subCategory-value-skeleton"); - expect(operatorSubCategoryValueSkeleton).toBeInTheDocument(); - }); - }); -}); \ No newline at end of file diff --git a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallHistory/LoadingHistory/index.jsx b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallHistory/LoadingHistory/index.jsx deleted file mode 100644 index 2c9900e..0000000 --- a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallHistory/LoadingHistory/index.jsx +++ /dev/null @@ -1,86 +0,0 @@ -import {useTranslations} from "next-intl"; -import {Box, List, ListItem, ListItemAvatar, ListItemText, Skeleton} from "@mui/material"; - -const LoadingHistory = () => { - const t = useTranslations(); - return ( - <> - - - - - - - - - } secondary={ - - }/> - - - - - - - - - - - - - - - - - - - - - - - ) -} - -export default LoadingHistory 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 1ac6e1e..0000000 --- a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallHistory/index.jsx +++ /dev/null @@ -1,65 +0,0 @@ -import {useTranslations} from "next-intl"; -import {Box, Chip, Divider, Stack} from "@mui/material"; -import HeadphonesIcon from '@mui/icons-material/Headphones'; -import LoadingHistory from "./LoadingHistory"; -import HistoryHeader from "./HistoryHeader"; -import ErrorOrEmpty from "./ErrorOrEmpty"; -import useCallerHistory from "@/lib/callWidget/hooks/useCallerHistory"; -import ListItemOfCalls from "./ListItemOfCalls"; -import {Fragment, useState} from "react"; - -const max_size = 10; -const CallHistory = ({tab}) => { - const t = useTranslations(); - const { - firstItemOfHistory, - historyList, - isLoadingHistoryList, - errorHistoryList - } = useCallerHistory(tab.id, tab.phone_number, max_size); - const [showRestOfHistory, setShowRestOfHistory] = useState(false); - - return ( - - - - {isLoadingHistoryList ? ( - - ) : errorHistoryList ? ( - - ) : !firstItemOfHistory ? ( - - ) : ( - - - {historyList && ( - - } - onClick={() => setShowRestOfHistory(true)} - label={t("CallHistory.show_more")} - disabled={showRestOfHistory} - /> - - )} - {showRestOfHistory && ( - <> - {historyList.map((historyItem, index) => ( - - - - - ))} - ) - } - - )} - - - ) -} - -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 deleted file mode 100644 index be7e9d2..0000000 --- a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/index.jsx +++ /dev/null @@ -1,23 +0,0 @@ -import {Divider, 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/CallTabDetails.jsx b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabsList/CallTabDetails.jsx deleted file mode 100644 index 9427201..0000000 --- a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabsList/CallTabDetails.jsx +++ /dev/null @@ -1,17 +0,0 @@ -import {Stack, Typography} from "@mui/material"; -import CallIcon from '@mui/icons-material/Call'; -import CallTabTime from "@/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabsList/CallTabTime"; - -const CallTabDetails = ({tab}) => { - - return ( - - - - {tab.phone_number} - - - - ) -} -export default CallTabDetails \ 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 e49bcdc..0000000 --- a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabsList/CallTabLabel.jsx +++ /dev/null @@ -1,30 +0,0 @@ -import {IconButton, Stack, Zoom} from "@mui/material"; -import CloseIcon from '@mui/icons-material/Close'; -import useAnswers from "@/lib/callWidget/hooks/useAnswers"; -import CallTabDetails - from "@/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabsList/CallTabDetails"; - -const CallTabLabel = ({tab, index}) => { - const {closeAnswerTab, activeTab} = useAnswers() - const handleCloseClick = (id) => { - closeAnswerTab(id) - }; - - return ( - - - - handleCloseClick(tab.id)}> - - - - - ) -} - -export default CallTabLabel diff --git a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabsList/CallTabTime.jsx b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabsList/CallTabTime.jsx deleted file mode 100644 index 5d627e4..0000000 --- a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabsList/CallTabTime.jsx +++ /dev/null @@ -1,29 +0,0 @@ -import {Typography} from "@mui/material"; -import {useEffect, useState} from "react"; -import moment from "jalali-moment"; -import useLanguage from "@/lib/app/hooks/useLanguage"; - -const CallTabTime = ({realTimeDate}) => { - const {languageApp} = useLanguage() - const [date, setDate] = useState('...') - const changeTabTime = (tabTime) => { - moment.relativeTimeThreshold('ss', 1) - return moment(tabTime).locale(languageApp).fromNow() - } - - useEffect(() => { - const timer = setInterval(() => { - setDate(changeTabTime(realTimeDate)) - }, 1000) - - return () => { - clearInterval(timer) - } - }, [realTimeDate]); - - return ( - {date} - ) -} -export default CallTabTime \ No newline at end of file diff --git a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabsList/__test__/CallTabDetails.test.js b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabsList/__test__/CallTabDetails.test.js deleted file mode 100644 index 7d50bff..0000000 --- a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabsList/__test__/CallTabDetails.test.js +++ /dev/null @@ -1,28 +0,0 @@ -import {render, screen, waitFor} from "@testing-library/react"; -import MockAppWithProviders from "../../../../../../../../../mocks/AppWithProvider"; -import CallTabDetails - from "@/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabsList/CallTabDetails"; - - -const tab = { - phone_number: "09134849737", - id: 1, - date: new Date() -} - -describe("Call Tab Details from Call Tan List", () => { - describe("Rendering", () => { - it('Should see the phone number on tab detail', async () => { - render( - - - - ) - const phoneNumberElement = screen.getByText("09134849737") - await waitFor(() => { - expect(phoneNumberElement).toHaveTextContent("09134849737") - }) - }); - - }) -}) \ No newline at end of file diff --git a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabsList/__test__/CallTabTime.test.js b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabsList/__test__/CallTabTime.test.js deleted file mode 100644 index 31b9a1b..0000000 --- a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabsList/__test__/CallTabTime.test.js +++ /dev/null @@ -1,25 +0,0 @@ -import {render, screen, waitFor} from "@testing-library/react"; -import MockAppWithProviders from "../../../../../../../../../mocks/AppWithProvider"; -import CallTabTime from "@/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabsList/CallTabTime"; - -const tab = { - phone_number: "09134849737", - id: 1, - date: new Date() -} - -describe("Call Tab Details from Call Tan List", () => { - describe("Rendering", () => { - it('Should see the date on tab detail', async () => { - render( - - - - ) - - await waitFor(() => { - expect(screen.getByText(/ثانیه پیش/i)).toBeInTheDocument() - }) - }); - }) -}) \ 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 ff26544..0000000 --- a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabsList/index.jsx +++ /dev/null @@ -1,33 +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, index) => ( - } 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 deleted file mode 100644 index 975a8b7..0000000 --- a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/index.jsx +++ /dev/null @@ -1,17 +0,0 @@ -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() - - 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 deleted file mode 100644 index e7d4278..0000000 --- a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/index.jsx +++ /dev/null @@ -1,49 +0,0 @@ -import {Dialog} from "@mui/material"; -import {DialogTransition} from "@/core/components/DialogTransition"; -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 CallTabs from "@/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs"; - -const CallWidgetDialog = () => { - const {openCallDialog, setOpenCallDialog} = useCallDialog() - const {answersList, newAnswerTab} = useAnswers() - const socket = useSocket() - - useEffect(() => { - const answerCall = (_data, act) => { - const data = JSON.parse(_data) - newAnswerTab({ - id: data.call_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 deleted file mode 100644 index 1480498..0000000 --- a/src/components/layouts/Dashboard/CallWidget/index.jsx +++ /dev/null @@ -1,26 +0,0 @@ -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/components/layouts/Dashboard/Content/index.jsx b/src/components/layouts/Dashboard/Content/index.jsx deleted file mode 100644 index e3f39db..0000000 --- a/src/components/layouts/Dashboard/Content/index.jsx +++ /dev/null @@ -1,65 +0,0 @@ -import LoadingHardPage from "@/core/components/LoadingHardPage"; -import RolePermissionMiddleware from "@/middlewares/RolePermission"; -import BreadCrumbs from "@/components/layouts/Dashboard/Breadcrumbs"; -import FullPageLayout from "@/layouts/FullPageLayout"; -import {useEffect, useMemo, useState} from "react"; -import sidebarMenu from "@/core/data/sidebarMenu"; -import {useRouter} from "next/router"; -import {useTranslations} from "next-intl"; - -const Content = (props) => { - const t = useTranslations() - const router = useRouter() - const [routing, setRouting] = useState(false) - const [routingItem, setRoutingItem] = useState({}) - const pageList = useMemo(() => { - const list = [] - for (const menu of sidebarMenu) { - for (const item of menu) { - if (item.type === 'menu') { - for (const subItem of item.subItem) { - list.push(subItem) - } - } else { - list.push(item) - } - } - } - - return list - }, []) - - useEffect(() => { - const handlerStartRoute = (url) => { - setRoutingItem(pageList.find(page => page.route === url)) - setRouting(true) - } - const handlerCompleteRoute = () => { - setRouting(false) - } - - router.events.on('routeChangeStart', handlerStartRoute) - router.events.on('routeChangeComplete', handlerCompleteRoute) - - return () => { - router.events.off('routeChangeStart', handlerStartRoute) - router.events.off('routeChangeComplete', handlerCompleteRoute) - } - }, [router]); - - return ( - - - - - {props.children} - - - - ) -} - -export default Content \ No newline at end of file diff --git a/src/components/layouts/Dashboard/Header/ProfileData.jsx b/src/components/layouts/Dashboard/Header/ProfileData.jsx deleted file mode 100644 index 548378b..0000000 --- a/src/components/layouts/Dashboard/Header/ProfileData.jsx +++ /dev/null @@ -1,18 +0,0 @@ -import {Avatar, Stack, Typography} from "@mui/material"; -import useUser from "@/lib/app/hooks/useUser"; - -export default function ProfileData() { - const {user} = useUser(); - return ( - - - - {user.username} - - - ); -} diff --git a/src/components/layouts/Dashboard/Header/ProfileMenu.jsx b/src/components/layouts/Dashboard/Header/ProfileMenu.jsx deleted file mode 100644 index 415d501..0000000 --- a/src/components/layouts/Dashboard/Header/ProfileMenu.jsx +++ /dev/null @@ -1,62 +0,0 @@ -import {Avatar, IconButton, Menu, Tooltip} from "@mui/material"; -import {useState} from "react"; -import ProfileData from "./ProfileData"; -import ProfileOptions from "./ProfileOptions"; -import {useTranslations} from "next-intl"; -import useUser from "@/lib/app/hooks/useUser"; - -function ProfileMenu() { - const t = useTranslations(); - const [anchorElUser, setAnchorElUser] = useState(null); - const {user} = useUser(); - - const handleOpenUserMenu = (event) => { - setAnchorElUser(event.currentTarget); - }; - const handleCloseUserMenu = () => { - setAnchorElUser(null); - }; - - return ( - <> - - - - - - - - - - - ); -} - -export default ProfileMenu; diff --git a/src/components/layouts/Dashboard/Header/ProfileOptionLogOut.jsx b/src/components/layouts/Dashboard/Header/ProfileOptionLogOut.jsx deleted file mode 100644 index 2a6cf85..0000000 --- a/src/components/layouts/Dashboard/Header/ProfileOptionLogOut.jsx +++ /dev/null @@ -1,49 +0,0 @@ -import {Box, Button, MenuItem, Typography} from "@mui/material"; -import MeetingRoomIcon from "@mui/icons-material/MeetingRoom"; -import useUser from "@/lib/app/hooks/useUser"; -import {useTranslations} from "next-intl"; - -export default function ProfileOptionLogOut({handleCloseUserMenu}) { - const t = useTranslations(); - const {clearToken} = useUser(); - const handleClickLogOut = () => { - handleCloseUserMenu(); - clearToken(); - }; - return ( - <> - - - - - - - {t("header.logout")} - - - - - ); -} diff --git a/src/components/layouts/Dashboard/Header/ProfileOptions.jsx b/src/components/layouts/Dashboard/Header/ProfileOptions.jsx deleted file mode 100644 index 6e7bdba..0000000 --- a/src/components/layouts/Dashboard/Header/ProfileOptions.jsx +++ /dev/null @@ -1,49 +0,0 @@ -import {Box, MenuItem, Typography} from "@mui/material"; -import {NextLinkComposed} from "@/core/components/LinkRouting"; -import {useTranslations} from "next-intl"; -import headerProfileItems from "@/core/data/headerProfileItems"; -import ProfileOptionLogOut from "./ProfileOptionLogOut"; - -export default function ProfileOptions({handleCloseUserMenu}) { - const t = useTranslations(); - - return ( - <> - {headerProfileItems.map((profile_item) => ( - - - - {profile_item.icon} - - - {t(profile_item.name)} - - - - ))} - - - ); -} diff --git a/src/components/layouts/Dashboard/Header/index.jsx b/src/components/layouts/Dashboard/Header/index.jsx deleted file mode 100644 index e8af065..0000000 --- a/src/components/layouts/Dashboard/Header/index.jsx +++ /dev/null @@ -1,74 +0,0 @@ -import MenuIcon from "@mui/icons-material/Menu"; -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(); - - return ( - <> - - - - - - - - - - - - - - - - - - - - - ); -} - -export default Header; diff --git a/src/components/layouts/Dashboard/Sidebar/SidebarDrawer.jsx b/src/components/layouts/Dashboard/Sidebar/SidebarDrawer.jsx deleted file mode 100644 index 3f1d449..0000000 --- a/src/components/layouts/Dashboard/Sidebar/SidebarDrawer.jsx +++ /dev/null @@ -1,27 +0,0 @@ -import {Divider, Stack, Toolbar, Typography} from "@mui/material"; -import {useTranslations} from "next-intl"; -import SidebarList from "./SidebarList"; -import useUser from "@/lib/app/hooks/useUser"; - -const SidebarDrawer = ({handleDrawerToggle}) => { - const {user} = useUser(); - const t = useTranslations(); - return ( - <> - - - - {t("app_short_name")} - - - {user.full_name} | {user.position} - - - - - - - ); -}; - -export default SidebarDrawer; diff --git a/src/components/layouts/Dashboard/Sidebar/SidebarList.jsx b/src/components/layouts/Dashboard/Sidebar/SidebarList.jsx deleted file mode 100644 index 1c76f07..0000000 --- a/src/components/layouts/Dashboard/Sidebar/SidebarList.jsx +++ /dev/null @@ -1,109 +0,0 @@ -import {Divider, List} from "@mui/material"; -import {Fragment, useEffect, useReducer, useState} from "react"; -import SidebarListItem from "./SidebarListItem"; -import sidebarMenu from "@/core/data/sidebarMenu"; -import {useRouter} from "next/router"; -import useUser from "@/lib/app/hooks/useUser"; - -function reducer(state, action) { - switch (action.type) { - case "COLLAPSE_MENU": - return state.map((itemsArr) => - itemsArr.map((item) => - action.key === item.key ? {...item, showSubItem: !item.showSubItem} : item - ) - ); - case "SELECTED": - return state.map((itemsArr) => - itemsArr.map((item) => { - return item.type === "page" - ? {...item, selected: action.route === item.route, routing: false} - : item.subItem && Array.isArray(item.subItem) - ? { - ...item, - subItem: item.subItem.map((subitem) => ({ - ...subitem, - selected: subitem.route === action.route, - routing: false - })), - showSubItem: item.subItem.some( - (subitem) => subitem.selected - ), - } - : item; - }) - ); - case "SELECTING": - return state.map((itemsArr) => - itemsArr.map((item) => { - return item.type === "page" - ? {...item, selected: action.route === item.route, routing: action.route === item.route} - : item.subItem && Array.isArray(item.subItem) - ? { - ...item, - subItem: item.subItem.map((subitem) => ({ - ...subitem, - selected: subitem.route === action.route, - routing: subitem.route === action.route - })), - showSubItem: item.subItem.some( - (subitem) => subitem.selected - ), - } - : item; - }) - ); - default: - throw new Error(); - } -} - -export default function SidebarList({handleDrawerToggle}) { - const [itemMenu, dispatch] = useReducer(reducer, sidebarMenu); - const {user} = useUser(); - const router = useRouter(); - const [selectedKey, setSelectedKey] = useState(null); - - useEffect(() => { - dispatch({type: "SELECTED", route: router.pathname}); - setSelectedKey(router.pathname); - const handlerStartRoute = (url) => { - dispatch({type: "SELECTING", route: url}); - } - - router.events.on('routeChangeStart', handlerStartRoute) - - return () => { - router.events.off('routeChangeStart', handlerStartRoute) - } - }, [router]); - - - useEffect(() => { - selectedKey && document.querySelector(`[data-route="${selectedKey}"]`)?.scrollIntoView({ - behavior: "smooth", - block: "center" - }); - }, [selectedKey]); - - return ( - - {itemMenu.map((itemArr, index) => ( - - {itemArr.map((item) => - - {(user?.permissions?.includes(item.permission) || item.permission === "all") && - } - - )} - - - ))} - - - ); -} diff --git a/src/components/layouts/Dashboard/Sidebar/SidebarListItem.jsx b/src/components/layouts/Dashboard/Sidebar/SidebarListItem.jsx deleted file mode 100644 index 81fd444..0000000 --- a/src/components/layouts/Dashboard/Sidebar/SidebarListItem.jsx +++ /dev/null @@ -1,97 +0,0 @@ -import {NextLinkComposed} from "@/core/components/LinkRouting"; -import ExpandLess from "@mui/icons-material/ExpandLess"; -import ExpandMore from "@mui/icons-material/ExpandMore"; -import { - Badge, - CircularProgress, - IconButton, - ListItem, - ListItemButton, - ListItemIcon, - ListItemText, - Typography, -} from "@mui/material"; -import {useTranslations} from "next-intl"; -import {Fragment} from "react"; -import SidebarListSubItem from "./SidebarListSubItem"; -import useNotification from "@/lib/app/hooks/useNotification"; - -const SidebarListItem = ({item, dispatch, handleDrawerToggle}) => { - const t = useTranslations(); - const {notification_count} = useNotification(); - const hasSubItems = item.type === "menu" && item.subItem && item.subItem.length > 0; - const renderBadge = () => { - return !hasSubItems ? ( - - - - ) : null; - }; - - return ( - <> - - { - if (hasSubItems) { - dispatch({type: "COLLAPSE_MENU", key: item.key}); - } - }} - sx={{ - minHeight: 48, - }} - - > - - {item.routing ? - : item.icon} - - - {t(item.secondary)} - - ) : null - } - /> - - {hasSubItems && (item.showSubItem ? : )} - - - {item.subItem && ( - - )} - - ); -}; - -export default SidebarListItem; diff --git a/src/components/layouts/Dashboard/Sidebar/SidebarListSubItem.jsx b/src/components/layouts/Dashboard/Sidebar/SidebarListSubItem.jsx deleted file mode 100644 index 5233f15..0000000 --- a/src/components/layouts/Dashboard/Sidebar/SidebarListSubItem.jsx +++ /dev/null @@ -1,78 +0,0 @@ -import {NextLinkComposed} from "@/core/components/LinkRouting"; -import { - Badge, - CircularProgress, - Collapse, - IconButton, - List, - ListItem, - ListItemButton, - ListItemIcon, - ListItemText, - Typography, -} from "@mui/material"; -import {useTranslations} from "next-intl"; -import useNotification from "@/lib/app/hooks/useNotification"; - -const SidebarListSubItem = ({item, handleDrawerToggle}) => { - const t = useTranslations(); - const {notification_count} = useNotification(); - const renderBadge = (subitem) => ( - - - - ); - - return ( - - - - {item.subItem.map((subitem) => ( - - - - {subitem.routing ? - : subitem.icon} - - - {t(subitem.secondary)} - - ) : null - }/> - - - ))} - - - - ); -}; - -export default SidebarListSubItem; diff --git a/src/components/layouts/Dashboard/Sidebar/__test__/SidebarDrawer.test.js b/src/components/layouts/Dashboard/Sidebar/__test__/SidebarDrawer.test.js deleted file mode 100644 index e095ffc..0000000 --- a/src/components/layouts/Dashboard/Sidebar/__test__/SidebarDrawer.test.js +++ /dev/null @@ -1,23 +0,0 @@ -import {render, screen, waitFor} from "@testing-library/react"; -import MockAppWithProviders from "../../../../../../mocks/AppWithProvider"; -import SidebarDrawer from "@/components/layouts/Dashboard/Sidebar/SidebarDrawer"; - -describe('SidebarDrawer', () => { - describe('Rendering', () => { - it('renders system name in sidebar', async () => { - render(); - await waitFor(()=>{ - const systemName = screen.getByRole('heading', { level: 6 }); - expect(systemName).toHaveTextContent('سامانه CRM'); - }) - }); - it('renders user full name and position in sidebar', async () => { - localStorage.setItem("_token", 'token'); - render(); - await waitFor(()=>{ - const userName = screen.queryByText(/Witel Company | Software Engineer/i); - expect(userName).toBeInTheDocument(); - }) - }); - }) -}) diff --git a/src/components/layouts/Dashboard/Sidebar/__test__/SidebarList.test.js b/src/components/layouts/Dashboard/Sidebar/__test__/SidebarList.test.js deleted file mode 100644 index 1897c9b..0000000 --- a/src/components/layouts/Dashboard/Sidebar/__test__/SidebarList.test.js +++ /dev/null @@ -1,16 +0,0 @@ -import {render, screen, waitFor} from "@testing-library/react"; -import MockAppWithProviders from "../../../../../../mocks/AppWithProvider"; -import SidebarList from "@/components/layouts/Dashboard/Sidebar/SidebarList"; - -describe('SidebarList', () => { - describe('Rendering', () => { - it('renders sidebar items based on user permissions', async () => { - localStorage.setItem("_token", 'token'); - render(); - await waitFor(()=>{ - const sidebarItem = screen.getByText('مدیریت نقش ها'); - expect(sidebarItem).toBeInTheDocument(); - }) - }); - }) -}) diff --git a/src/components/layouts/Dashboard/Sidebar/__test__/SidebarListItem.test.js b/src/components/layouts/Dashboard/Sidebar/__test__/SidebarListItem.test.js deleted file mode 100644 index 18313c5..0000000 --- a/src/components/layouts/Dashboard/Sidebar/__test__/SidebarListItem.test.js +++ /dev/null @@ -1,125 +0,0 @@ -import AccessibilityIcon from "@mui/icons-material/Accessibility"; -import {fireEvent, render, screen} from "@testing-library/react"; -import SidebarListItem from "@/components/layouts/Dashboard/Sidebar/SidebarListItem"; -import MockAppWithProviders from "../../../../../../mocks/AppWithProvider"; - -describe('SidebarListItemComponent', () => { - describe('Rendering', () => { - it('should show sidebarlistitem name in sidebar', () => { - const sampleItem={ - key: "sidebar.role-management", - name : "role_management", - type: "page", - route: "/dashboard/role-management", - icon: , - selected: false, - permission: "manage_roles", - } - render(); - expect(screen.getByText('مدیریت نقش ها')).toBeInTheDocument(); - }); - it('should show sidebarlistitem icon in sidebar', () => { - const sampleItem={ - key: "sidebar.role-management", - name : "role_management", - type: "page", - route: "/dashboard/role-management", - icon: , - selected: false, - permission: "manage_roles", - } - render(); - const iconElement = screen.getByLabelText("Sidebar Icon"); - expect(iconElement).toBeInTheDocument(); - }); - it('should have selected class for subitem', () => { - const sampleItem={ - key: "sidebar.role-management", - name : "role_management", - type: "page", - route: "/dashboard/role-management", - icon: , - selected: true, - permission: "manage_roles", - } - render(); - const element = screen.getByText('مدیریت نقش ها'); - expect(element.parentElement.parentElement).toHaveClass('Mui-selected'); - }); - - it('should not have selected class for subitem', () => { - const sampleItem={ - key: "sidebar.role-management", - name : "role_management", - type: "menu", - route: "/dashboard/role-management", - icon: , - selected: false, - permission: "manage_roles", - } - render(); - const element = screen.getByText('مدیریت نقش ها'); - expect(element.parentElement.parentElement).not.toHaveClass('Mui-selected'); - }); - - it('should show sidebarlistitem secondary in sidebar', () => { - const sampleItem={ - key: "sidebar.role-management", - name : "role_management", - secondary: "between", - type: "menu", - route: "/dashboard/role-management", - icon: , - selected: false, - permission: "manage_roles", - } - render(); - expect(screen.getByText('میان')).toBeInTheDocument(); - }); - - it('should not show sidebarlistitem secondary in sidebar', () => { - const sampleItem={ - key: "sidebar.role-management", - name : "role_management", - type: "menu", - route: "/dashboard/role-management", - icon: , - selected: false, - permission: "manage_roles", - } - render(); - expect(screen.queryByText('secondry')).not.toBeInTheDocument(); - }); - }) - describe('Behavior', () => { - it('should call dispatch when hasSubItems is true and item is clicked', () => { - const dispatchMock = jest.fn(); - const sampleItem={ - key: "sidebar.role-management", - name : "role_management", - type: "menu", - route: "/dashboard/role-management", - icon: , - selected: false, - permission: "manage_roles", - subItem :[ - { - key: "greaterThan", - route: "/dashboard/role-management", - secondary: "between", - name : "sidebar.admin", - selected: true, - icon: , - } - ] - } - render(); - const listItem = screen.getByText('مدیریت نقش ها'); - fireEvent.click(listItem); - expect(dispatchMock).toHaveBeenCalledWith({ - type: 'COLLAPSE_MENU', - key: "sidebar.role-management", - }); - }); - }) -}) \ No newline at end of file diff --git a/src/components/layouts/Dashboard/Sidebar/__test__/SidebarListSubitem.test.js b/src/components/layouts/Dashboard/Sidebar/__test__/SidebarListSubitem.test.js deleted file mode 100644 index 9551937..0000000 --- a/src/components/layouts/Dashboard/Sidebar/__test__/SidebarListSubitem.test.js +++ /dev/null @@ -1,103 +0,0 @@ -import {render, screen} from "@testing-library/react"; -import MockAppWithProviders from "../../../../../../mocks/AppWithProvider"; -import SidebarListSubItem from "@/components/layouts/Dashboard/Sidebar/SidebarListSubItem"; -import AccessibilityIcon from "@mui/icons-material/Accessibility"; - -describe('SidebarListSubItemComponent', () => { - describe('Rendering', () => { - const sampleItem={ - showSubItem:true, - key: "sidebar.role-management", - name : "role_management", - type: "menu", - route: "/dashboard/role-management", - icon: , - selected: false, - permission: "manage_roles", - subItem :[ - { - key: "greaterThan", - route: "/dashboard/role-management", - secondary: "between", - name : "sidebar.admin", - selected: true, - icon: , - } - ] - } - it('should show subitem name in sidebar', () => { - render(); - expect(screen.getByText('بزرگتر از')).toBeInTheDocument(); - - }); - it('should not show subitem name in sidebar', () => { - const sampleItem={ - showSubItem:false, - key: "sidebar.role-management", - name : "role_management", - type: "menu", - route: "/dashboard/role-management", - icon: , - selected: false, - permission: "manage_roles", - subItem :[ - { - key: "greaterThan", - route: "/dashboard/role-management", - secondary: "between", - name : "sidebar.admin", - selected: false, - icon: , - } - ] - } - render(); - expect(screen.queryByText('مدیریت نقش ها')).not.toBeInTheDocument(); - }); - it('should show subitem secondary in sidebar', () => { - render(); - expect(screen.getByText('میان')).toBeInTheDocument(); - }); - - it('should not show subitem secondary in sidebar', () => { - render(); - expect(screen.queryByText('secondry')).not.toBeInTheDocument(); - }); - it('should have selected class for subitem', () => { - render(); - const parentElement = screen.queryByText('بزرگتر از').parentElement.parentElement; - expect(parentElement).toHaveClass('Mui-selected'); - }); - - it('should not have selected class for subitem', () => { - const sampleItem={ - showSubItem:true, - key: "sidebar.role-management", - name : "role_management", - type: "menu", - route: "/dashboard/role-management", - icon: , - selected: false, - permission: "manage_roles", - subItem :[ - { - key: "greaterThan", - route: "/dashboard/role-management", - secondary: "between", - name : "sidebar.admin", - selected: false, - icon: , - } - ] - } - render(); - const parentElement = screen.queryByText('بزرگتر از').parentElement.parentElement; - expect(parentElement).not.toHaveClass('Mui-selected'); - }); - it('should show subitem icon', () => { - render(); - const iconElement = screen.getByLabelText("Sidebar Icon"); - expect(iconElement).toBeInTheDocument(); - }); - }) -}) \ No newline at end of file diff --git a/src/components/layouts/Dashboard/Sidebar/__test__/index.test.js b/src/components/layouts/Dashboard/Sidebar/__test__/index.test.js deleted file mode 100644 index 740c384..0000000 --- a/src/components/layouts/Dashboard/Sidebar/__test__/index.test.js +++ /dev/null @@ -1,17 +0,0 @@ -import {render, screen, act} from "@testing-library/react"; -import MockAppWithProviders from "../../../../../../mocks/AppWithProvider"; -import Sidebar from "@/components/layouts/Dashboard/Sidebar"; - - -describe('Sidebar', () => { - describe('Rendering', () => { - it('Mobile Drawer is displayed when mobileOpen is true', () => { - const props={ - mobileOpen: true, - }; - render(); - const mobileDrawer = screen.getByTestId('mobile-drawer'); - expect(mobileDrawer).toBeVisible(); - }); - }) -}) diff --git a/src/components/layouts/Dashboard/Sidebar/index.jsx b/src/components/layouts/Dashboard/Sidebar/index.jsx deleted file mode 100644 index d85dd8d..0000000 --- a/src/components/layouts/Dashboard/Sidebar/index.jsx +++ /dev/null @@ -1,56 +0,0 @@ -import {Box, Drawer, useMediaQuery} from "@mui/material"; -import SidebarDrawer from "./SidebarDrawer"; -import {useTheme} from "@mui/material/styles"; - -const Sidebar = (props) => { - const theme = useTheme(); - const isUpSm = useMediaQuery((theme.breakpoints.up('sm'))) - - return ( - - {isUpSm ? ( - - - - ) : ( - - - - )} - - ); -}; - -export default Sidebar; diff --git a/src/components/layouts/Dashboard/index.jsx b/src/components/layouts/Dashboard/index.jsx deleted file mode 100644 index 8201e54..0000000 --- a/src/components/layouts/Dashboard/index.jsx +++ /dev/null @@ -1,42 +0,0 @@ -import FullPageLayout from "@/layouts/FullPageLayout"; -import {Toolbar} from "@mui/material"; -import {useState} from "react"; -import Header from "@/components/layouts/Dashboard/Header"; -import Sidebar from "@/components/layouts/Dashboard/Sidebar"; -import Content from "@/components/layouts/Dashboard/Content"; - -const drawerWidth = 240; - -const Dashboard = (props) => { - const {window} = props; - const [mobileOpen, setMobileOpen] = useState(false); - const container = - window !== undefined ? () => window().document.body : undefined; - - const handleDrawerToggle = () => { - setMobileOpen(!mobileOpen); - }; - return ( - -
- - - - - - - ) -} - -export default Dashboard \ No newline at end of file diff --git a/src/components/layouts/dashboard/headerWithSidebar/HeaderMenu/index.jsx b/src/components/layouts/dashboard/headerWithSidebar/HeaderMenu/index.jsx new file mode 100644 index 0000000..74c471a --- /dev/null +++ b/src/components/layouts/dashboard/headerWithSidebar/HeaderMenu/index.jsx @@ -0,0 +1,60 @@ +import { ExpandLess, ExpandMore, Link } from "@mui/icons-material"; +import { + Button, + Divider, + ListItemIcon, + ListItemText, + Menu, + MenuItem, + Stack, +} from "@mui/material"; +import NextLink from "next/link"; +import { useState } from "react"; + +const HeaderMenu = ({ menu }) => { + const [anchorEl, setAnchorEl] = useState(null); + const open = Boolean(anchorEl); + + const handleClick = (event) => { + setAnchorEl(event.currentTarget); + }; + const handleClose = () => { + setAnchorEl(null); + }; + + return ( + + + + {menu.subMenu.flatMap((subMenu, index) => [ + ...subMenu.map((sub) => ( + + + + + {sub.title} + + )), + index < menu.subMenu.length - 1 && ( + + ), + ])} + + + ); +}; + +export default HeaderMenu; diff --git a/src/components/layouts/dashboard/headerWithSidebar/Sidebar/SidebarBadge.jsx b/src/components/layouts/dashboard/headerWithSidebar/Sidebar/SidebarBadge.jsx new file mode 100644 index 0000000..239a5e6 --- /dev/null +++ b/src/components/layouts/dashboard/headerWithSidebar/Sidebar/SidebarBadge.jsx @@ -0,0 +1,19 @@ +import { getValueByPath } from "@/core/utils/getValueByPath"; +import { useSidebarBadge } from "@/lib/hooks/useSidebarBadge"; +import { Chip } from "@mui/material"; +import { useEffect, useState } from "react"; + +const SidebarBadge = ({ badge, chipProps = {} }) => { + const { data } = useSidebarBadge(); + const [value, setValue] = useState(); + + useEffect(() => { + setValue(getValueByPath(data, badge)); + }, [data, badge]); + + if (!data) return null; + if (!value) return null; + + return ; +}; +export default SidebarBadge; diff --git a/src/components/layouts/dashboard/headerWithSidebar/Sidebar/SidebarListItems.jsx b/src/components/layouts/dashboard/headerWithSidebar/Sidebar/SidebarListItems.jsx new file mode 100644 index 0000000..4b4e640 --- /dev/null +++ b/src/components/layouts/dashboard/headerWithSidebar/Sidebar/SidebarListItems.jsx @@ -0,0 +1,92 @@ +import { ExpandLess, ExpandMore } from "@mui/icons-material"; +import { + Collapse, + List, + ListItem, + ListItemButton, + ListItemIcon, + ListItemText, + Stack, +} from "@mui/material"; +import Link from "next/link"; +import SidebarBadge from "./SidebarBadge"; +import SidebarSubitems from "./SidebarSubitems"; + +const SidebarListItems = ({ menuItem, dispatch }) => { + return ( + <> + + { + if (menuItem.type !== "page") { + dispatch({ type: "COLLAPSE_MENU", id: menuItem.id }); + } + }} + > + + {menuItem.icon} + + + {menuItem.badges && ( + + {menuItem.badges.map((badge, i) => ( + + ))} + + )} + {menuItem.hasSubitems ? ( + menuItem.showSubitems ? ( + + ) : ( + + ) + ) : null} + + + + {menuItem.hasSubitems ? ( + + {menuItem.Subitems.map((subitem, index) => { + return ( + + ); + })} + + ) : null} + + + ); +}; +export default SidebarListItems; diff --git a/src/components/layouts/dashboard/headerWithSidebar/Sidebar/SidebarMenu.jsx b/src/components/layouts/dashboard/headerWithSidebar/Sidebar/SidebarMenu.jsx new file mode 100644 index 0000000..29c2b09 --- /dev/null +++ b/src/components/layouts/dashboard/headerWithSidebar/Sidebar/SidebarMenu.jsx @@ -0,0 +1,128 @@ +"use client"; +import { usePermissions } from "@/lib/hooks/usePermissions"; +import { List } from "@mui/material"; +import { usePathname } from "next/navigation"; +import { useEffect, useReducer, useState } from "react"; +import { filterMenuItems } from "@/core/utils/filterMenuItems"; +import { pageMenu } from "@/core/utils/pageMenu"; +import SidebarListItems from "./SidebarListItems"; +import Profile from "@/core/components/Profile"; + +function selectPage(item, route) { + if (item.type === "page") { + return { + ...item, + selected: item.route === route, + showSubitems: item.route === route, + }; + } else if (item.Subitems && Array.isArray(item.Subitems)) { + const updatedSubitems = item.Subitems.map((subitem) => + selectPage(subitem, route), + ); + return { + ...item, + Subitems: updatedSubitems, + showSubitems: updatedSubitems.some( + (subitem) => subitem.showSubitems || subitem.route === route, + ), + }; + } + return item; +} + +function toggleSubitems(items, actionId) { + return items.map((item) => + actionId === item.id + ? { ...item, showSubitems: !item.showSubitems } + : { ...item, showSubitems: false }, + ); +} + +function reducer(state, action) { + switch (action.type) { + case "UPDATE_MENU": + const _permissions = action.permissions || []; + return filterMenuItems(state, ["all", ..._permissions]); + + case "COLLAPSE_MENU": + return state.map((item) => ({ + ...item, + showSubitems: item.id === action.id ? !item.showSubitems : false, + })); + + case "COLLAPSE_SUB_ITEMS": + return state.map((item) => + item.hasSubitems + ? { ...item, Subitems: toggleSubitems(item.Subitems, action.id) } + : item, + ); + + case "COLLAPSE_SUB_SECOND_ITEMS": + return state.map((item) => + item.hasSubitems + ? { + ...item, + Subitems: item.Subitems.map((subitem) => + subitem.hasSubitems + ? { + ...subitem, + Subitems: toggleSubitems(subitem.Subitems, action.id), + } + : subitem, + ), + } + : item, + ); + + case "SELECTED": + return state.map((item) => selectPage(item, action.route)); + + default: + throw new Error(); + } +} + +const SidebarMenu = () => { + const { data: userPermissions } = usePermissions(); + const [menuItems, dispatch] = useReducer(reducer, pageMenu); + const pathname = usePathname(); + const [selectedKey, setSelectedKey] = useState(null); + + useEffect(() => { + dispatch({ type: "SELECTED", route: pathname }); + setSelectedKey(pathname); + }, [userPermissions, pathname]); + + useEffect(() => { + if (!userPermissions) return; + dispatch({ type: "UPDATE_MENU", permissions: userPermissions }); + }, [userPermissions]); + + useEffect(() => { + selectedKey && + document.querySelector(`[data-route="${selectedKey}"]`)?.scrollIntoView({ + behavior: "smooth", + block: "center", + }); + }, [selectedKey]); + + return ( + <> + + {userPermissions && ( + + {menuItems.map((menuItem) => { + return ( + + ); + })} + + )} + + ); +}; +export default SidebarMenu; diff --git a/src/components/layouts/dashboard/headerWithSidebar/Sidebar/SidebarSubitems.jsx b/src/components/layouts/dashboard/headerWithSidebar/Sidebar/SidebarSubitems.jsx new file mode 100644 index 0000000..e74204b --- /dev/null +++ b/src/components/layouts/dashboard/headerWithSidebar/Sidebar/SidebarSubitems.jsx @@ -0,0 +1,96 @@ +import { ExpandLess, ExpandMore } from "@mui/icons-material"; +import { + Box, + Collapse, + List, + ListItem, + ListItemButton, + ListItemIcon, + ListItemText, + Stack, +} from "@mui/material"; +import Link from "next/link"; +import SidebarBadge from "./SidebarBadge"; + +const SidebarSubitems = ({ subitem, dispatch }) => { + return ( + <> + + { + if (subitem.type !== "page") { + dispatch({ type: "COLLAPSE_SUB_ITEMS", id: subitem.id }); + } + }} + > + + {subitem.icon} + + + {subitem.badges && ( + + {subitem.badges.map((badge, i) => ( + + ))} + + )} + {subitem.hasSubitems ? ( + subitem.showSubitems ? ( + + ) : ( + + ) + ) : ( + + )} + + + + {subitem.hasSubitems ? ( + + {subitem.Subitems.map((subSubitem, index) => { + return ( + + ); + })} + + ) : null} + + + ); +}; +export default SidebarSubitems; diff --git a/src/components/layouts/dashboard/headerWithSidebar/index.jsx b/src/components/layouts/dashboard/headerWithSidebar/index.jsx new file mode 100644 index 0000000..6fb683a --- /dev/null +++ b/src/components/layouts/dashboard/headerWithSidebar/index.jsx @@ -0,0 +1,190 @@ +"use client"; +import ChevronRightIcon from "@mui/icons-material/ChevronRight"; +import MenuIcon from "@mui/icons-material/Menu"; +import { + Box, + Drawer, + IconButton, + Stack, + styled, + Toolbar, + Typography, + useMediaQuery, + useTheme, +} from "@mui/material"; +import MuiAppBar from "@mui/material/AppBar"; +import { useState, useEffect } from "react"; +import HeaderMenu from "./HeaderMenu"; +import moment from "jalali-moment"; +import { headerMenu } from "@/core/utils/headerMenu"; +import SidebarMenu from "./Sidebar/SidebarMenu"; + +const drawerWidth = 300; + +const Main = styled(Stack, { shouldForwardProp: (prop) => prop !== "open" })( + ({ theme, open }) => ({ + flexGrow: 1, + height: "100%", + transition: theme.transitions.create("margin", { + easing: theme.transitions.easing.sharp, + duration: theme.transitions.duration.leavingScreen, + }), + marginLeft: `-${drawerWidth}px`, + ...(open && { + transition: theme.transitions.create("margin", { + easing: theme.transitions.easing.easeOut, + duration: theme.transitions.duration.enteringScreen, + }), + marginLeft: 0, + }), + }), +); + +const AppBar = styled(MuiAppBar, { + shouldForwardProp: (prop) => prop !== "open", +})(({ theme, open }) => ({ + transition: theme.transitions.create(["margin", "width"], { + easing: theme.transitions.easing.sharp, + duration: theme.transitions.duration.leavingScreen, + }), + ...(open && { + width: `calc(100% - ${drawerWidth}px)`, + marginLeft: `${drawerWidth}px`, + transition: theme.transitions.create(["margin", "width"], { + easing: theme.transitions.easing.easeOut, + duration: theme.transitions.duration.enteringScreen, + }), + }), +})); + +const DrawerHeader = styled("div")(({ theme }) => ({ + display: "flex", + alignItems: "center", + padding: theme.spacing(0, 1), + ...theme.mixins.toolbar, + minHeight: "40px !important", + justifyContent: "flex-end", +})); + +const HeaderWithSidebar = ({ children }) => { + const theme = useTheme(); + const isMobile = useMediaQuery(theme.breakpoints.down("md")); // Detect mobile/tablet screen + const [open, setOpen] = useState(!isMobile); // Initial state based on screen size + + useEffect(() => { + setOpen(!isMobile); // Toggle state when screen size changes + }, [isMobile]); + + const now = moment().locale("fa").format("YYYY/MM/DD"); + + const handleDrawerOpen = () => { + setOpen(true); + }; + const handleDrawerClose = () => { + setOpen(false); + }; + + return ( + + + + + + + + {headerMenu.map((menu) => ( + + ))} + + + + + + + تاریخ امروز: {now} + + + + + + + + v{process.env.NEXT_PUBLIC_VERSION} + + +
+ + + {children} + +
+
+ ); +}; + +export default HeaderWithSidebar; diff --git a/src/components/login-expert/LoginExpertComponent.jsx b/src/components/login-expert/LoginExpertComponent.jsx deleted file mode 100644 index d2a2d13..0000000 --- a/src/components/login-expert/LoginExpertComponent.jsx +++ /dev/null @@ -1,123 +0,0 @@ -import {Box, Button, Container, Paper, Stack, TextField, Typography} from "@mui/material"; -import {Field, Formik} from "formik"; -import SvgLogin from "@/core/components/svgs/SvgLogin"; -import StyledForm from "@/core/components/StyledForm"; -import PasswordField from "@/core/components/PasswordField"; -import LoginIcon from "@mui/icons-material/Login"; -import {GET_USER_TOKEN} from "@/core/data/apiRoutes"; -import * as Yup from "yup"; -import useRequest from "@/lib/app/hooks/useRequest"; -import useUser from "@/lib/app/hooks/useUser"; -import {useTranslations} from "next-intl"; - -const LoginExpertComponent = () => { - const t = useTranslations(); - const requestServer = useRequest() - const {setToken} = useUser(); // pass token to set token - const handleSubmit = (values, props) => { - requestServer(GET_USER_TOKEN, 'post', { - data: { - username: values.username, - password: values.password, - }, - success: { - notification: {show: false} - } - }).then((response) => { - setToken(response.data.data.token) - }).catch(() => { - props.setSubmitting(false) - }) - }; - const initialValues = { - username: "", - password: "", - }; - const validationSchema = Yup.object().shape({ - username: Yup.string().required(t("LoginPage.username_error_message_required")), - password: Yup.string().required(t("LoginPage.password_error_message_required")), - }); - - return ( - - - - {(props) => ( - - - - - - {t("login_expert")} - - - - - - - - - - - - )} - - - - ) -} -export default LoginExpertComponent \ No newline at end of file diff --git a/src/components/login-expert/LoginLinkRouting.jsx b/src/components/login-expert/LoginLinkRouting.jsx deleted file mode 100644 index 1930b3c..0000000 --- a/src/components/login-expert/LoginLinkRouting.jsx +++ /dev/null @@ -1,28 +0,0 @@ -import LinkRouting from "@/core/components/LinkRouting"; -import {Stack} from "@mui/material"; -import {useRouter} from "next/router"; -import {useTranslations} from "next-intl"; - -const LoginLinkRouting = () => { - const t = useTranslations(); - const router = useRouter() - const backUrlDecodedPath = router.query?.back_url - - return ( - - - {t("LoginPage.link_routing_back_to")}{" "} - {backUrlDecodedPath - ? t("LoginPage.link_routing_previuos_page") - : t("LoginPage.link_routing_main_page")} - - - ) -} -export default LoginLinkRouting \ No newline at end of file diff --git a/src/components/login-expert/__test__/LoginExpertComponent.test.js b/src/components/login-expert/__test__/LoginExpertComponent.test.js deleted file mode 100644 index 695dbcb..0000000 --- a/src/components/login-expert/__test__/LoginExpertComponent.test.js +++ /dev/null @@ -1,154 +0,0 @@ -import {act, findByText, fireEvent, getByText, render, screen, waitFor} from '@testing-library/react'; -import MockAppWithProviders from '../../../../mocks/AppWithProvider'; -import mockRouter from 'next-router-mock' -import LoginExpertComponent from "@/components/login-expert/LoginExpertComponent"; -import LoginLinkRouting from "@/components/login-expert/LoginLinkRouting"; - -describe('Login expert component from login page', () => { - - describe('Rendering', () => { - it('LoginExpertComponent should see login expert text on the page',() => { - render( - - - - ); - const loginExpertElement = screen.getByRole('heading', { level: 4 }); - expect(loginExpertElement).toHaveTextContent('ورود کارشناس'); - }); - - it('LoginExpertComponent from LoginComponent Button should render login text', () => { - render( - - - - ); - const button = screen.getByText("ورود"); - // Assertions for button props - expect(button).toHaveAttribute('type', 'submit'); - expect(button).toHaveTextContent('ورود'); - }); - - it("Show error when username is empty", async ()=>{ - render( - - - - ); - - const usernameInput = screen.getByLabelText("نام کاربری") - - expect(screen.queryByText("وارد کردن نام کاربری الزامیست")).not.toBeInTheDocument() - - fireEvent.blur(usernameInput); - - await waitFor(()=>{ - expect(screen.queryByText("وارد کردن نام کاربری الزامیست")).toBeInTheDocument() - }) - - fireEvent.change(usernameInput, {target : {value : "testuser"}}) - - await waitFor(()=>{ - expect(screen.queryByText("وارد کردن نام کاربری الزامیست")).not.toBeInTheDocument() - }) - }) - it("Show error when password is empty", async ()=>{ - render( - - - - ); - - const passwordInput = screen.getByLabelText("رمز عبور") - - expect(screen.queryByText("وارد کردن رمز عبور الزامیست")).not.toBeInTheDocument() - - fireEvent.blur(passwordInput); - - await waitFor(()=>{ - expect(screen.queryByText("وارد کردن رمز عبور الزامیست")).toBeInTheDocument() - }) - - fireEvent.change(passwordInput, {target : {value : "testuser"}}) - - await waitFor(()=>{ - expect(screen.queryByText("وارد کردن رمز عبور الزامیست")).not.toBeInTheDocument() - }) - }) - }); - - describe("Behavior", ()=>{ - it('Should fill the username field', async () => { - render( - - - - ); - // Get username and password input elements by their name attributes - const usernameInput = screen.getByLabelText('نام کاربری'); - - fireEvent.change(usernameInput, {target: {value: 'testuser'}}); - await act(async ()=>{ - // Simulate user input - expect(usernameInput).toHaveValue('testuser'); - }) - }); - - it('Should fill the password fields', async () => { - render( - - - - ); - // Get username and password input elements by their name attributes - const passwordInput = screen.getByLabelText('رمز عبور'); - fireEvent.change(passwordInput, {target: {value: 'password123'}}); - - await act(async ()=>{ - // Simulate user input - expect(passwordInput).toHaveValue('password123'); - }) - }); - - // this test is for when back url does not exist to run this test you should comment if in mock func - it('Should get main page when back url dos not exist', async () => { - render( - - - - ) - const linkElement = await screen.findByTestId("link_routing") - expect(linkElement).toHaveTextContent("بازگشت به صفحه اصلی") - }); - - }) - describe("validation", ()=>{ - it("Disabled submit button until fields completed", async ()=> { - - render( - - - - ) - const usernameInput = screen.getByLabelText('نام کاربری'); - const passwordInput = screen.getByLabelText('رمز عبور'); - const submitButton = screen.getByText('ورود'); - - expect(submitButton).toBeDisabled(); - - fireEvent.change(usernameInput, {target: {value: 'testuser'}}); - - await waitFor(async () => { - // Now, the submit button should be enabled because both fields are completed - expect(submitButton).toBeDisabled(); - }); - - fireEvent.change(passwordInput, {target: {value: 'password123'}}); - - await waitFor(async () => { - // Now, the submit button should be enabled because both fields are completed - expect(submitButton).toBeEnabled(); - }); - }) - }) -}); \ No newline at end of file diff --git a/src/components/login-expert/__test__/LoginLinkRouting.test.js b/src/components/login-expert/__test__/LoginLinkRouting.test.js deleted file mode 100644 index a63d314..0000000 --- a/src/components/login-expert/__test__/LoginLinkRouting.test.js +++ /dev/null @@ -1,18 +0,0 @@ -import {render, screen} from "@testing-library/react"; -import MockAppWithProviders from "../../../../mocks/AppWithProvider"; -import LoginLinkRouting from "@/components/login-expert/LoginLinkRouting"; - -describe("Login expert component from login page",()=>{ - - describe('Rendering', () => { - it('LoginLinkRouting from LoginComponent Should get main page when back url dos not exist', async () => { - render( - - - - ) - const linkElement = await screen.findByTestId("link_routing") - expect(linkElement).toHaveTextContent("بازگشت به صفحه اصلی") - }); - }) -}) diff --git a/src/components/login-expert/index.jsx b/src/components/login-expert/index.jsx deleted file mode 100644 index dc36d9a..0000000 --- a/src/components/login-expert/index.jsx +++ /dev/null @@ -1,18 +0,0 @@ -import CenterLayout from "@/layouts/CenterLayout"; -import FullPageLayout from "@/layouts/FullPageLayout"; -import LoginLinkRouting from "@/components/login-expert/LoginLinkRouting"; -import LoginExpertComponent from "@/components/login-expert/LoginExpertComponent"; - -const LoginComponent = () => { - - return ( - - - - - - - ); -}; - -export default LoginComponent; diff --git a/src/components/widget/call/CallTabs/CallTabPanel/CallActions/ActionHeader.jsx b/src/components/widget/call/CallTabs/CallTabPanel/CallActions/ActionHeader.jsx new file mode 100644 index 0000000..368c7b1 --- /dev/null +++ b/src/components/widget/call/CallTabs/CallTabPanel/CallActions/ActionHeader.jsx @@ -0,0 +1,29 @@ +import { Stack, Typography } from "@mui/material"; + +const ActionHeader = ({ tab }) => { + return ( + + + .... عملیات های مربوط به تماس: + + + {tab.phone_number} .... + + + ); +}; + +export default ActionHeader; diff --git a/src/components/widget/call/CallTabs/CallTabPanel/CallActions/CallActionCategoriesButton.jsx b/src/components/widget/call/CallTabs/CallTabPanel/CallActions/CallActionCategoriesButton.jsx new file mode 100644 index 0000000..c21d2dc --- /dev/null +++ b/src/components/widget/call/CallTabs/CallTabPanel/CallActions/CallActionCategoriesButton.jsx @@ -0,0 +1,27 @@ +import { Button, Grid } from "@mui/material"; +import { Controller } from "react-hook-form"; + +const CallActionCategoriesButton = ({ category, control }) => { + return ( + ( + + )} + name={"category_id"} + /> + ); +}; +export default CallActionCategoriesButton; diff --git a/src/components/widget/call/CallTabs/CallTabPanel/CallActions/CallActionDescription.jsx b/src/components/widget/call/CallTabs/CallTabPanel/CallActions/CallActionDescription.jsx new file mode 100644 index 0000000..6eacebd --- /dev/null +++ b/src/components/widget/call/CallTabs/CallTabPanel/CallActions/CallActionDescription.jsx @@ -0,0 +1,44 @@ +import { Box, Chip, Divider, TextField } from "@mui/material"; +import { useEffect, useRef } from "react"; +import { Controller } from "react-hook-form"; + +const CallActionDescription = ({ control }) => { + const inputRef = useRef(null); + + useEffect(() => { + const timer = setTimeout(() => { + if (inputRef.current) { + inputRef.current.focus(); + } + }, 100); + + return () => clearTimeout(timer); + }, []); + + return ( + <> + + + + + ( + + )} + name={"description"} + /> + + + ); +}; +export default CallActionDescription; diff --git a/src/components/widget/call/CallTabs/CallTabPanel/CallActions/CallActionSubcategoriesButton.jsx b/src/components/widget/call/CallTabs/CallTabPanel/CallActions/CallActionSubcategoriesButton.jsx new file mode 100644 index 0000000..1783b32 --- /dev/null +++ b/src/components/widget/call/CallTabs/CallTabPanel/CallActions/CallActionSubcategoriesButton.jsx @@ -0,0 +1,38 @@ +import { Button, Grid } from "@mui/material"; +import { useCallback } from "react"; +import { Controller, useWatch } from "react-hook-form"; + +const CallActionSubcategoriesButton = ({ sub_category, control, onSubmit }) => { + const category_id = useWatch({ control, name: "category_id" }); + + const handleClick = useCallback( + (onChange) => { + onChange(sub_category.subcategory_id); + setTimeout(() => { + onSubmit(); + }, 0); + }, + [sub_category.subcategory_id], + ); + + return ( + { + return sub_category.category_id === category_id ? ( + + ) : null; + }} + name={"subcategory_id"} + /> + ); +}; +export default CallActionSubcategoriesButton; diff --git a/src/components/widget/call/CallTabs/CallTabPanel/CallActions/CallActionsCategories.jsx b/src/components/widget/call/CallTabs/CallTabPanel/CallActions/CallActionsCategories.jsx new file mode 100644 index 0000000..32cd698 --- /dev/null +++ b/src/components/widget/call/CallTabs/CallTabPanel/CallActions/CallActionsCategories.jsx @@ -0,0 +1,24 @@ +import { useCategory } from "@/lib/contexts/category"; +import CallActionCategoriesButton from "./CallActionCategoriesButton"; +import { Chip, Divider, Grid, Stack } from "@mui/material"; + +const CallActionsCategories = ({ control }) => { + const { categoryLists } = useCategory(); + return ( + <> + + + + + {categoryLists.map((category) => ( + + ))} + + + ); +}; +export default CallActionsCategories; diff --git a/src/components/widget/call/CallTabs/CallTabPanel/CallActions/CallActionsSubcategories.jsx b/src/components/widget/call/CallTabs/CallTabPanel/CallActions/CallActionsSubcategories.jsx new file mode 100644 index 0000000..f67b385 --- /dev/null +++ b/src/components/widget/call/CallTabs/CallTabPanel/CallActions/CallActionsSubcategories.jsx @@ -0,0 +1,27 @@ +import { useCategory } from "@/lib/contexts/category"; +import CallActionSubcategoriesButton from "./CallActionSubcategoriesButton"; +import { Chip, Divider, Grid, Stack } from "@mui/material"; + +const CallActionsSubcategories = ({ control, onSubmit }) => { + const { subCategoryLists } = useCategory(); + return ( + <> + + + + + {subCategoryLists.map((sub_category, index) => { + return ( + + ); + })} + + + ); +}; +export default CallActionsSubcategories; diff --git a/src/components/widget/call/CallTabs/CallTabPanel/CallActions/index.jsx b/src/components/widget/call/CallTabs/CallTabPanel/CallActions/index.jsx new file mode 100644 index 0000000..aa011ae --- /dev/null +++ b/src/components/widget/call/CallTabs/CallTabPanel/CallActions/index.jsx @@ -0,0 +1,71 @@ +import StyledForm from "@/core/components/StyledForm"; +import { CALL_ACTION } from "@/core/utils/routes"; +import { useCall } from "@/lib/contexts/call"; +import useRequest from "@/lib/hooks/useRequest"; +import { yupResolver } from "@hookform/resolvers/yup"; +import { Stack } from "@mui/material"; +import { useForm } from "react-hook-form"; +import { object, string } from "yup"; +import ActionHeader from "./ActionHeader"; +import CallActionDescription from "./CallActionDescription"; +import CallActionsCategories from "./CallActionsCategories"; +import CallActionsSubcategories from "./CallActionsSubcategories"; + +const defaultValues = { + description: "", + category_id: "", + subcategory_id: "", +}; + +const validationSchema = object({ + category_id: string().required("لطفا نام کاربری را وارد کنید!"), + subcategory_id: string().required("لطفا رمز عبور را وارد کنید!"), +}); + +function CallActions({ tab }) { + const requestServer = useRequest(); + const { closeCall } = useCall(); + + const { + control, + handleSubmit, + formState: { isSubmitting }, + } = useForm({ + defaultValues, + resolver: yupResolver(validationSchema), + mode: "all", + }); + + const onSubmit = handleSubmit(async (data) => { + try { + const formData = new FormData(); + data.description != "" && + formData.append("description", data.description); + formData.append("category_id", data.category_id); + formData.append("subcategory_id", data.subcategory_id); + await requestServer(`${CALL_ACTION}/${tab.id}`, "post", { + data: formData, + }); + closeCall(tab.id); + } catch (error) { + console.error("Login error:", error); + } + }); + + return ( + + + + + + + + + ); +} + +export default CallActions; diff --git a/src/components/widget/call/CallTabs/CallTabPanel/CallHistory/ErrorOrEmpty.jsx b/src/components/widget/call/CallTabs/CallTabPanel/CallHistory/ErrorOrEmpty.jsx new file mode 100644 index 0000000..e76a269 --- /dev/null +++ b/src/components/widget/call/CallTabs/CallTabPanel/CallHistory/ErrorOrEmpty.jsx @@ -0,0 +1,39 @@ +import { Typography } from "@mui/material"; + +const ErrorOrEmpty = ({ isErrorOrEmpty }) => { + return ( + <> + {isErrorOrEmpty === "error" ? ( + + خطا در دریافت تاریخچه تماس دریافتی + + ) : isErrorOrEmpty === "empty" ? ( + + تاریخچه ای برای تماس دریافتی یافت نشد + + ) : ( + <> + )} + + ); +}; + +export default ErrorOrEmpty; diff --git a/src/components/widget/call/CallTabs/CallTabPanel/CallHistory/HistoryHeader.jsx b/src/components/widget/call/CallTabs/CallTabPanel/CallHistory/HistoryHeader.jsx new file mode 100644 index 0000000..e3498d1 --- /dev/null +++ b/src/components/widget/call/CallTabs/CallTabPanel/CallHistory/HistoryHeader.jsx @@ -0,0 +1,25 @@ +import { Stack, Typography } from "@mui/material"; + +const HistoryHeader = ({ tab }) => { + return ( + + + .... تاریخچه تماس های: + + + {tab.phone_number} .... + + + ); +}; + +export default HistoryHeader; diff --git a/src/components/widget/call/CallTabs/CallTabPanel/CallHistory/ListItemOfCalls/PreviousOperatorData.jsx b/src/components/widget/call/CallTabs/CallTabPanel/CallHistory/ListItemOfCalls/PreviousOperatorData.jsx new file mode 100644 index 0000000..5197ca8 --- /dev/null +++ b/src/components/widget/call/CallTabs/CallTabPanel/CallHistory/ListItemOfCalls/PreviousOperatorData.jsx @@ -0,0 +1,30 @@ +import HeadsetMicIcon from "@mui/icons-material/HeadsetMic"; +import { Avatar, Box, Stack, Typography } from "@mui/material"; +import moment from "jalali-moment"; + +const PreviousOperatorData = ({ historyItem }) => { + return ( + + + + + + + + + + {historyItem.operator_full_name} + + + (کارشناس) + + + + {moment(historyItem.created_at).locale("fa").fromNow()} + + + + ); +}; + +export default PreviousOperatorData; diff --git a/src/components/widget/call/CallTabs/CallTabPanel/CallHistory/ListItemOfCalls/Topics..jsx b/src/components/widget/call/CallTabs/CallTabPanel/CallHistory/ListItemOfCalls/Topics..jsx new file mode 100644 index 0000000..13e7063 --- /dev/null +++ b/src/components/widget/call/CallTabs/CallTabPanel/CallHistory/ListItemOfCalls/Topics..jsx @@ -0,0 +1,60 @@ +import { Chip, Divider, Stack } from "@mui/material"; + +const Topics = ({ historyItem }) => { + return ( + + + + + {historyItem.category_name && ( + + )} + + + + + {historyItem.subcategory_name && ( + + )} + + + + + {historyItem.description && } + + + ); +}; + +export default Topics; diff --git a/src/components/widget/call/CallTabs/CallTabPanel/CallHistory/ListItemOfCalls/index.jsx b/src/components/widget/call/CallTabs/CallTabPanel/CallHistory/ListItemOfCalls/index.jsx new file mode 100644 index 0000000..f06ba45 --- /dev/null +++ b/src/components/widget/call/CallTabs/CallTabPanel/CallHistory/ListItemOfCalls/index.jsx @@ -0,0 +1,14 @@ +import { Stack } from "@mui/material"; +import PreviousOperatorData from "./PreviousOperatorData"; +import Topics from "./Topics."; + +const ListItemOfCalls = ({ historyItem }) => { + return ( + + + + + ); +}; + +export default ListItemOfCalls; diff --git a/src/components/widget/call/CallTabs/CallTabPanel/CallHistory/LoadingHistory.jsx b/src/components/widget/call/CallTabs/CallTabPanel/CallHistory/LoadingHistory.jsx new file mode 100644 index 0000000..674b7eb --- /dev/null +++ b/src/components/widget/call/CallTabs/CallTabPanel/CallHistory/LoadingHistory.jsx @@ -0,0 +1,115 @@ +import { + Box, + List, + ListItem, + ListItemAvatar, + ListItemText, + Skeleton, +} from "@mui/material"; + +const LoadingHistory = () => { + return ( + <> + + + + + + + + + } + secondary={ + + } + /> + + + + + + + + + + + + + + + + + + + + + + ); +}; + +export default LoadingHistory; diff --git a/src/components/widget/call/CallTabs/CallTabPanel/CallHistory/index.jsx b/src/components/widget/call/CallTabs/CallTabPanel/CallHistory/index.jsx new file mode 100644 index 0000000..77e7263 --- /dev/null +++ b/src/components/widget/call/CallTabs/CallTabPanel/CallHistory/index.jsx @@ -0,0 +1,61 @@ +import useCallerHistory from "@/lib/hooks/useCallerHistory"; +import { Box, Chip, Divider, Fade, List, Stack } from "@mui/material"; +import { useState } from "react"; +import HistoryHeader from "./HistoryHeader"; +import LoadingHistory from "./LoadingHistory"; +import ErrorOrEmpty from "./ErrorOrEmpty"; +import ListItemOfCalls from "./ListItemOfCalls"; +import { Headphones } from "@mui/icons-material"; + +const max_size = 10; +const CallHistory = ({ tab }) => { + const { + firstItemOfHistory, + historyList, + isLoadingHistoryList, + errorHistoryList, + } = useCallerHistory(tab.id, tab.phone_number, max_size); + const [showRestOfHistory, setShowRestOfHistory] = useState(false); + + return ( + + + + {isLoadingHistoryList ? ( + + ) : errorHistoryList ? ( + + ) : !firstItemOfHistory ? ( + + ) : ( + + + {historyList && ( + + } + onClick={() => setShowRestOfHistory(true)} + label={"نمایش موارد بیشتر"} + disabled={showRestOfHistory} + /> + + )} + {showRestOfHistory && + historyList.map((historyItem, index) => ( + <> + + + + ))} + + )} + + + ); +}; + +export default CallHistory; diff --git a/src/components/widget/call/CallTabs/CallTabPanel/index.jsx b/src/components/widget/call/CallTabs/CallTabPanel/index.jsx new file mode 100644 index 0000000..a22af79 --- /dev/null +++ b/src/components/widget/call/CallTabs/CallTabPanel/index.jsx @@ -0,0 +1,29 @@ +import { Box, Divider, Stack } from "@mui/material"; +import CallActions from "./CallActions"; +import CallHistory from "./CallHistory"; + +const CallTabPanel = ({ tab }) => { + return ( + + + + + + + + + + ); +}; + +export default CallTabPanel; diff --git a/src/components/widget/call/CallTabs/CallTabsList/CallTabDetails.jsx b/src/components/widget/call/CallTabs/CallTabsList/CallTabDetails.jsx new file mode 100644 index 0000000..a7c0290 --- /dev/null +++ b/src/components/widget/call/CallTabs/CallTabsList/CallTabDetails.jsx @@ -0,0 +1,21 @@ +import { Call } from "@mui/icons-material"; +import { Stack, Typography } from "@mui/material"; +import CallTabTime from "./CallTabTime"; + +const CallTabDetails = ({ tab }) => { + return ( + + + + {tab.phone_number} + + + + ); +}; +export default CallTabDetails; diff --git a/src/components/widget/call/CallTabs/CallTabsList/CallTabLabel.jsx b/src/components/widget/call/CallTabs/CallTabsList/CallTabLabel.jsx new file mode 100644 index 0000000..3b45b80 --- /dev/null +++ b/src/components/widget/call/CallTabs/CallTabsList/CallTabLabel.jsx @@ -0,0 +1,35 @@ +import { useCall } from "@/lib/contexts/call"; +import { Close } from "@mui/icons-material"; +import { IconButton, Stack, Zoom } from "@mui/material"; +import CallTabDetails from "./CallTabDetails"; + +const CallTabLabel = ({ tab, index }) => { + const { closeCall, activeCall } = useCall(); + const handleCloseClick = (id) => { + closeCall(id); + }; + + return ( + + + + handleCloseClick(tab.id)}> + + + + + ); +}; + +export default CallTabLabel; diff --git a/src/components/widget/call/CallTabs/CallTabsList/CallTabTime.jsx b/src/components/widget/call/CallTabs/CallTabsList/CallTabTime.jsx new file mode 100644 index 0000000..a2ce2b3 --- /dev/null +++ b/src/components/widget/call/CallTabs/CallTabsList/CallTabTime.jsx @@ -0,0 +1,28 @@ +import { Typography } from "@mui/material"; +import moment from "jalali-moment"; +import { useEffect, useState } from "react"; + +const CallTabTime = ({ realTimeDate }) => { + const [date, setDate] = useState("..."); + const changeTabTime = (tabTime) => { + moment.relativeTimeThreshold("ss", 1); + return moment(tabTime).locale("fa").fromNow(); + }; + + useEffect(() => { + const timer = setInterval(() => { + setDate(changeTabTime(realTimeDate)); + }, 1000); + + return () => { + clearInterval(timer); + }; + }, [realTimeDate]); + + return ( + + {date} + + ); +}; +export default CallTabTime; diff --git a/src/components/widget/call/CallTabs/CallTabsList/index.jsx b/src/components/widget/call/CallTabs/CallTabsList/index.jsx new file mode 100644 index 0000000..67e02d9 --- /dev/null +++ b/src/components/widget/call/CallTabs/CallTabsList/index.jsx @@ -0,0 +1,40 @@ +import { useCall } from "@/lib/contexts/call"; +import { Box, Tab, Tabs } from "@mui/material"; +import CallTabLabel from "./CallTabLabel"; + +const CallTabsList = () => { + const { calls, changeActiveTabCall, activeCall } = useCall(); + const handleChange = (event, newValue) => { + changeActiveTabCall(newValue); + }; + + return ( + + + {calls.map((call, index) => ( + } + key={call.id} + /> + ))} + + + ); +}; + +export default CallTabsList; diff --git a/src/components/widget/call/CallTabs/index.jsx b/src/components/widget/call/CallTabs/index.jsx new file mode 100644 index 0000000..58bb4e7 --- /dev/null +++ b/src/components/widget/call/CallTabs/index.jsx @@ -0,0 +1,17 @@ +import { useCall } from "@/lib/contexts/call"; +import CallTabPanel from "./CallTabPanel"; +import CallTabsList from "./CallTabsList"; + +const CallTabs = () => { + const { calls } = useCall(); + + return ( + <> + + {calls.map((call) => ( + + ))} + + ); +}; +export default CallTabs; diff --git a/src/components/widget/call/CallWidgetDialog.jsx b/src/components/widget/call/CallWidgetDialog.jsx new file mode 100644 index 0000000..ea29f13 --- /dev/null +++ b/src/components/widget/call/CallWidgetDialog.jsx @@ -0,0 +1,48 @@ +"use client"; +import { DialogTransition } from "@/core/components/DialogTransition"; +import { useCall } from "@/lib/contexts/call"; +import { useSocket } from "@/lib/contexts/socket"; +import { Dialog } from "@mui/material"; +import { useEffect } from "react"; +import CallTabs from "./CallTabs"; + +const CallWidgetDialog = () => { + const { openCallDialog, setOpenCallDialog, calls, newCall } = useCall(); + const { socket } = useSocket(); + + useEffect(() => { + const call = (_data, act) => { + const data = JSON.parse(_data); + newCall({ + id: data.call_id, + phone_number: data.phone_number, + }); + act(); + }; + socket.on("call", call); + + return () => { + socket.off("call", call); + }; + }); + + useEffect(() => { + if (calls.length === 0) { + setOpenCallDialog(false); + return; + } + setOpenCallDialog(true); + }, [calls.length]); + + return ( + + + + ); +}; + +export default CallWidgetDialog; diff --git a/src/components/widget/call/index.jsx b/src/components/widget/call/index.jsx new file mode 100644 index 0000000..51280d3 --- /dev/null +++ b/src/components/widget/call/index.jsx @@ -0,0 +1,29 @@ +"use client"; +import { CallProvider } from "@/lib/contexts/call"; +import { CategoriesProvider } from "@/lib/contexts/category"; +import { useSocket } from "@/lib/contexts/socket"; +import { useEffect } from "react"; +import CallWidgetDialog from "./CallWidgetDialog"; + +const CallWidget = () => { + const { socket, status } = useSocket(); + + useEffect(() => { + if (status == "connected") return; + + socket.connect(); + + return () => { + socket.disconnect(); + }; + }, [socket]); + + return ( + + + + + + ); +}; +export default CallWidget; diff --git a/src/core/components/AvatarUpload.jsx b/src/core/components/AvatarUpload.jsx deleted file mode 100644 index 03a9cba..0000000 --- a/src/core/components/AvatarUpload.jsx +++ /dev/null @@ -1,128 +0,0 @@ -import {Avatar, Box, TextField} from "@mui/material"; -import {useState} from "react"; -import DeleteIcon from "@mui/icons-material/Delete"; -import AddIcon from "@mui/icons-material/Add"; - -const AvatarUpload = ({user, setFieldValue, valueAvatar, changeFlag}) => { - const [selectedImage, setSelectedImage] = useState(user.expert_avatar); - const [isHovered, setIsHovered] = useState(false); - - const handleImageChange = (event) => { - const newImage = event.target?.files?.[0]; - if (newImage) { - setSelectedImage(URL.createObjectURL(newImage)); - setFieldValue(valueAvatar, newImage); - setFieldValue(changeFlag, true); - } else { - setSelectedImage(""); - setFieldValue(valueAvatar, null); - } - }; - - const handleDeleteImage = () => { - setSelectedImage(""); - setFieldValue(valueAvatar, null); - setFieldValue(changeFlag, true); - }; - - const handleMouseEnter = () => { - setIsHovered(true); - }; - - const handleMouseLeave = () => { - setIsHovered(false); - }; - - return ( - - - - - {isHovered && ( - - {selectedImage ? ( - - ) : ( - - )} - - )} - - - - - ); -}; - -export default AvatarUpload; diff --git a/src/core/components/DataTable.jsx b/src/core/components/DataTable.jsx deleted file mode 100644 index 41bdbaa..0000000 --- a/src/core/components/DataTable.jsx +++ /dev/null @@ -1,172 +0,0 @@ -import {useTranslations} from "next-intl"; -import useLanguage from "@/lib/app/hooks/useLanguage"; -import {useEffect, useMemo, useState} from "react"; -import moment from "moment-jalaali"; -import useSWR from "swr"; -import {Typography} from "@mui/material"; -import MaterialReactTable from "material-react-table"; -import useRequest from "@/lib/app/hooks/useRequest"; - -function DataTable(props) { - const requestServer = useRequest({auth: true}) - const t = useTranslations(); - const {languageApp, languageList} = useLanguage(); - const [columnFilters, setColumnFilters] = useState([]); - const [sorting, setSorting] = useState([]); - const [pagination, setPagination] = useState({pageIndex: 0, pageSize: 10}); - const [columnFilterFns, setColumnFilterFns] = useState(() => { - let output = {}; - const list = props.columns.map((item) => - item.enableColumnFilter ? {[item.id]: item.filterFn} : {[item.id]: ""} - ); - for (const key in list) { - const nestedObj = list[key]; - for (const nestedKey in nestedObj) { - output[nestedKey] = nestedObj[nestedKey]; - } - } - return output; - }); - - const [updateTime, setUpdateTime] = useState( - moment().format("HH:mm | jYYYY/jMM/jDD") - ); - - const tableLocalization = useMemo( - () => - languageList.find((item) => item.key === languageApp).tableLocalization, - [languageApp, languageList] - ); - - const fetchUrl = useMemo(() => { - 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) { - datatype = props.columns[i].datatype; - } - } - return { - ...filter, - fn: columnFilterFns[filter.id], - datatype: datatype, - }; - }); - params.set("size", pagination.pageSize); - params.set("filters", JSON.stringify(filters ?? [])); - params.set("sorting", JSON.stringify(sorting ?? [])); - return `${props.tableUrl}?${params}`; - }, [ - props.tableUrl, - columnFilters, - columnFilterFns, - pagination, - sorting, - props.columns, - ]); - - 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/jMM/jDD")); - }, [isValidating, languageApp]); - - return ( - ( - <> - {props.enableCustomToolbar /* send condition */ - ? /* send component */ - : ""} - - )} - renderBottomToolbarCustomActions={({table}) => ( - <> - {props.enableLastUpdate /* send condition */ ? ( - - {t("last_updated_at")}: {updateTime} - - ) : ( - "" - )} - - )} - state={{ - showProgressBars: isValidating, - columnFilters, - columnFilterFns, - pagination, - sorting, - }} - positionActionsColumn={"last"} - enableRowActions={props.enableRowActions} - renderRowActions={({row}) => } - {...props} - /> - ); -} - -export default DataTable; \ No newline at end of file diff --git a/src/core/components/DialogTransition.jsx b/src/core/components/DialogTransition.jsx index d94765f..de09a75 100644 --- a/src/core/components/DialogTransition.jsx +++ b/src/core/components/DialogTransition.jsx @@ -1,6 +1,8 @@ -import {forwardRef} from "react"; -import {Slide} from "@mui/material"; +import { forwardRef } from "react"; +import { Slide } from "@mui/material"; -export const DialogTransition = forwardRef(function DialogTransition(props, ref) { - return ; -}); +export const DialogTransition = forwardRef( + function DialogTransition(props, ref) { + return ; + }, +); diff --git a/src/core/components/GlobalHead.jsx b/src/core/components/GlobalHead.jsx deleted file mode 100644 index 5d6186d..0000000 --- a/src/core/components/GlobalHead.jsx +++ /dev/null @@ -1,58 +0,0 @@ -import Head from "next/head"; - -const GlobalHead = () => { - return ( - - - - - - - - - - - - - - - - - - - - - ) -} - -export default GlobalHead \ No newline at end of file diff --git a/src/core/components/ImageConvertor.jsx b/src/core/components/ImageConvertor.jsx deleted file mode 100644 index 9f99bc1..0000000 --- a/src/core/components/ImageConvertor.jsx +++ /dev/null @@ -1,22 +0,0 @@ -import ImageResize from "image-resize"; - -const ImageResizer = async (image) => { - const imageResize = new ImageResize({ - quality: 1, - format: "jpg", - outputType: "base64", - width: 400, - }); - - const get = await imageResize.get(image); - const resize = await imageResize.resize(get).then(); - const output = await imageResize.output(resize).then(); - - const avatar = await (await import("image-to-file-converter")) - .base64ToFile(output) - .then(); - - return avatar; -}; - -export default ImageResizer; diff --git a/src/core/components/LinkRouting.jsx b/src/core/components/LinkRouting.jsx deleted file mode 100644 index f8fac0a..0000000 --- a/src/core/components/LinkRouting.jsx +++ /dev/null @@ -1,116 +0,0 @@ -import MuiLink from "@mui/material/Link"; -import {styled} from "@mui/material/styles"; -import clsx from "clsx"; -import NextLink from "next/link"; -import {useRouter} from "next/router"; -import * as React from "react"; - -// Add support for the sx prop for consistency with the other branches. -const Anchor = styled("a")({}); - -export const NextLinkComposed = React.forwardRef(function NextLinkComposed( - props, - ref -) { - const { - to, - linkAs, - replace, - scroll, - shallow, - prefetch, - legacyBehavior = true, - locale, - ...other - } = props; - - return ( - - - - ); -}); - -// A styled version of the Next.js Link component: -// https://nextjs.org/docs/api-reference/next/link -const LinkRouting = React.forwardRef(function Link(props, ref) { - const { - activeClassName = "active", - as, - className: classNameProps, - href, - legacyBehavior, - linkAs: linkAsProp, - locale, - noLinkStyle, - prefetch, - replace, - role, // Link don't have roles. - scroll, - shallow, - ...other - } = props; - - const router = useRouter(); - const pathname = typeof href === "string" ? href : href.pathname; - const className = clsx(classNameProps, { - [activeClassName]: router.pathname === pathname && activeClassName, - }); - - const isExternal = - typeof href === "string" && - (href.indexOf("http") === 0 || href.indexOf("mailto:") === 0); - - if (isExternal) { - if (noLinkStyle) { - return ; - } - - return ; - } - - const linkAs = linkAsProp || as; - const nextjsProps = { - to: href, - linkAs, - replace, - scroll, - shallow, - prefetch, - legacyBehavior, - locale, - }; - - if (noLinkStyle) { - return ( - - ); - } - - return ( - - ); -}); - -export default LinkRouting; diff --git a/src/core/components/LoadingHardPage.jsx b/src/core/components/LoadingHardPage.jsx index 3001c6c..4e9125a 100644 --- a/src/core/components/LoadingHardPage.jsx +++ b/src/core/components/LoadingHardPage.jsx @@ -1,52 +1,82 @@ +"use client"; +import SvgError from "@/core/components/svgs/SvgError"; import SvgLoading from "@/core/components/svgs/SvgLoading"; -import {Backdrop, Box, Stack, Typography} from "@mui/material"; -import {styled} from "@mui/material/styles"; +import { Backdrop, Box, Stack, styled, useTheme } from "@mui/material"; const LoadingImage = styled(Box)({ - "@keyframes load": { - "0%": { - // opacity: 0, - transform: "scale(1)", - }, - "50%": { - // opacity: 1, - transform: "scale(0.5)", - }, - "100%": { - // opacity: 0, - transform: "scale(1)", - }, + "@keyframes load": { + "0%": { + transform: "scale(1)", }, - animation: "load 2s infinite", + "50%": { + transform: "scale(.5)", + }, + "100%": { + transform: "scale(1)", + }, + }, + animation: "load 2s infinite", }); -const LoadingHardPage = ({children, loading, sx = {}, icon = null, width = 200, height = 200, label = ''}) => { - return ( - <> - theme.zIndex.modal + 1, ...sx}} - open={loading} - > - - - {icon ? ( - - {icon} - - ) : ( - - )} - - {label} - - - {children} - - ); +const LoadingHardPage = ({ + children, + loading, + authState = false, + sx = {}, + icon = null, + width = 200, + height = 200, + label = "", +}) => { + const theme = useTheme(); + return ( + <> + theme.zIndex.drawer + 1, + ...sx, + }} + open={loading} + > + + {authState ? ( + + {icon ? ( + + {icon} + + ) : ( + + )} + + ) : ( + + {icon ? ( + + {icon} + + ) : ( + + )} + + )} + + {label} + + + + {children} + + ); }; export default LoadingHardPage; diff --git a/src/core/components/LtrTextField.jsx b/src/core/components/LtrTextField.jsx new file mode 100644 index 0000000..fd58d17 --- /dev/null +++ b/src/core/components/LtrTextField.jsx @@ -0,0 +1,10 @@ +import { styled, TextField } from "@mui/material"; + +const LtrTextField = styled(TextField)` + .MuiInputBase-input { + /* @noflip */ + direction: ltr; + } +`; + +export default LtrTextField; diff --git a/src/core/components/Messages.jsx b/src/core/components/Messages.jsx deleted file mode 100644 index 8a5cfc9..0000000 --- a/src/core/components/Messages.jsx +++ /dev/null @@ -1,18 +0,0 @@ -import CenterLayout from "@/layouts/CenterLayout"; -import FullPageLayout from "@/layouts/FullPageLayout"; -import SvgLoading from "@/core/components/svgs/SvgLoading"; - -const Message = ({text, actions}) => { - return ( - - - - {text} - {actions} - - - ); -}; - -export default Message; diff --git a/src/core/components/Middleware/RolePermissionComponent.jsx b/src/core/components/Middleware/RolePermissionComponent.jsx deleted file mode 100644 index 869f78a..0000000 --- a/src/core/components/Middleware/RolePermissionComponent.jsx +++ /dev/null @@ -1,33 +0,0 @@ -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 deleted file mode 100644 index 9a6ff5d..0000000 --- a/src/core/components/Middleware/WithAuthComponent.jsx +++ /dev/null @@ -1,36 +0,0 @@ -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 deleted file mode 100644 index 7dbb38d..0000000 --- a/src/core/components/Middleware/WithoutAuthComponent.jsx +++ /dev/null @@ -1,25 +0,0 @@ -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 deleted file mode 100644 index 44a583d..0000000 --- a/src/core/components/Middleware/__test__/RolePermissionComponent.test.js +++ /dev/null @@ -1,23 +0,0 @@ -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 deleted file mode 100644 index a43e20f..0000000 --- a/src/core/components/Middleware/__test__/WithAuthComponent.test.js +++ /dev/null @@ -1,39 +0,0 @@ -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 deleted file mode 100644 index 520d7bd..0000000 --- a/src/core/components/Middleware/__test__/WithoutAuthComponent.test.js +++ /dev/null @@ -1,37 +0,0 @@ -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/core/components/MuiDatePicker.jsx b/src/core/components/MuiDatePicker.jsx deleted file mode 100644 index 18be3cc..0000000 --- a/src/core/components/MuiDatePicker.jsx +++ /dev/null @@ -1,61 +0,0 @@ -import {LocalizationProvider, MobileDateTimePicker} from "@mui/x-date-pickers"; -import {AdapterDateFnsJalali} from "@mui/x-date-pickers/AdapterDateFnsJalali"; -import moment from "jalali-moment"; -import {faIR} from "@mui/x-date-pickers/locales"; -import {Box, IconButton} from "@mui/material"; -import ClearIcon from "@mui/icons-material/Clear"; -import {useTranslations} from "next-intl"; - -export default function MuiDatePicker({column}) { - const t = useTranslations(); - const filterFnValue = column.columnDef._filterFn; - - return ( - - - { - const date = new Date(newValue); - const formattedDate = moment(date) - .locale("en") - .format("YYYY-MM-DD HH:mm"); - column.setFilterValue(formattedDate); - }} - slotProps={{ - textField: { - placeholder: "تاریخ خود را وارد کنید", - helperText: `${t("filter_mode")}: ${t(filterFnValue)}`, - sx: {minWidth: "120px"}, - variant: "standard", - }, - }} - value={ - column.getFilterValue() - ? new Date(column.getFilterValue()) - : null - } - /> - - { - column.setFilterValue(null); - }} - sx={{ - color: column.getFilterValue() - ? "rgba(0, 0, 0, 0.54)" - : "#bfbfbf", - }} - > - - - - ); -} diff --git a/src/core/components/NetworkComponent.jsx b/src/core/components/NetworkComponent.jsx deleted file mode 100644 index 9610b1c..0000000 --- a/src/core/components/NetworkComponent.jsx +++ /dev/null @@ -1,35 +0,0 @@ -import useNetwork from "@/lib/app/hooks/useNetwork"; -import {useEffect, useRef} from "react"; -import {toast} from "react-toastify"; -import WifiIcon from '@mui/icons-material/Wifi'; -import WifiOffIcon from '@mui/icons-material/WifiOff'; -import {useTranslations} from "next-intl"; - -const NetworkComponent = () => { - const networkToastId = useRef(null); - const network = useNetwork() - const t = useTranslations() - - useEffect(() => { - if (network.online) { - toast.update(networkToastId.current, { - type: toast.TYPE.SUCCESS, - render: t('online_message'), - autoClose: 2000, - closeButton: true, - closeOnClick: true, - icon: - }); - return - } - networkToastId.current = toast.warn(t('offline_message'), { - containerId: 'connection', - draggable: false, - autoClose: false, closeButton: false, closeOnClick: false, icon: - }) - }, [network.online]); - - return '' -} - -export default NetworkComponent \ No newline at end of file diff --git a/src/core/components/NoSsrHandler.jsx b/src/core/components/NoSsrHandler.jsx deleted file mode 100644 index 5a4c4e1..0000000 --- a/src/core/components/NoSsrHandler.jsx +++ /dev/null @@ -1,8 +0,0 @@ -import {NoSsr} from "@mui/material"; - -const NoSsrHandler = ({isBot, children}) => { - if (isBot) return children; - return {children}; -}; - -export default NoSsrHandler; diff --git a/src/core/components/PageLoading.jsx b/src/core/components/PageLoading.jsx new file mode 100644 index 0000000..5b36836 --- /dev/null +++ b/src/core/components/PageLoading.jsx @@ -0,0 +1,25 @@ +"use client"; +import { Paper } from "@mui/material"; +import LoadingHardPage from "./LoadingHardPage"; + +const PageLoading = () => { + return ( + + + + ); +}; +export default PageLoading; diff --git a/src/core/components/PasswordField/__test__/index.test.js b/src/core/components/PasswordField/__test__/index.test.js deleted file mode 100644 index 479f91f..0000000 --- a/src/core/components/PasswordField/__test__/index.test.js +++ /dev/null @@ -1,31 +0,0 @@ -import {fireEvent, getByRole, render, screen} from "@testing-library/react"; -import PasswordField from "@/core/components/PasswordField"; -import {Formik} from "formik"; - -describe('Password Field component in core folder', () => { - describe('Rendering', () => { - it('Show password when visibility icon clicked',() => { - render ( - {}}> - {(props)=>( - - )} - - ) - - const iconElement = screen.getByLabelText("رمز عبور") - const iconButton = screen.getByRole("button") - - - expect(iconElement).toHaveAttribute('type', 'password'); - - fireEvent.click(iconButton); - - expect(iconElement).toHaveAttribute("type", "text"); - - fireEvent.click(iconButton); - - expect(iconElement).toHaveAttribute('type', 'password'); - }) - }) -}) \ No newline at end of file diff --git a/src/core/components/PasswordField/index.jsx b/src/core/components/PasswordField/index.jsx deleted file mode 100644 index d109b73..0000000 --- a/src/core/components/PasswordField/index.jsx +++ /dev/null @@ -1,33 +0,0 @@ -import {useState} from "react"; -import {IconButton, InputAdornment, TextField} from "@mui/material"; -import {Visibility, VisibilityOff} from "@mui/icons-material"; -import {Field} from "formik"; - -const PasswordField = (props) => { - const [showPassword, setShowPassword] = useState(false); - - const handleClickShowPassword = () => { - setShowPassword(!showPassword); - }; - - return ( - - - {showPassword ? : } - - - ), - }} - /> - ); -}; - -export default PasswordField; diff --git a/src/core/components/PriceField.jsx b/src/core/components/PriceField.jsx deleted file mode 100644 index 29d8763..0000000 --- a/src/core/components/PriceField.jsx +++ /dev/null @@ -1,23 +0,0 @@ -import {InputAdornment, TextField, Typography} from "@mui/material"; -import {useTranslations} from "next-intl"; - -const PriceField = (props) => { - const t = useTranslations(); - return ( - - {((props.value) / 10).toLocaleString()} - {t("ConfirmDialog.toman")} - - ), - }} - {...props} - /> - ) -} -export default PriceField \ No newline at end of file diff --git a/src/core/components/Profile/ProfileActions.jsx b/src/core/components/Profile/ProfileActions.jsx new file mode 100644 index 0000000..eeb500c --- /dev/null +++ b/src/core/components/Profile/ProfileActions.jsx @@ -0,0 +1,73 @@ +"use client"; +import { GET_USER_LOGOUT_ROUTE, LOGOUT_USER_ROUTE } from "@/core/utils/routes"; +import { useAuth } from "@/lib/contexts/auth"; +import useRequest from "@/lib/hooks/useRequest"; +import ModeEditIcon from "@mui/icons-material/ModeEdit"; +import PowerSettingsNewIcon from "@mui/icons-material/PowerSettingsNew"; +import VpnKeyIcon from "@mui/icons-material/VpnKey"; +import { Box, Divider, IconButton, Tooltip } from "@mui/material"; +import { useState } from "react"; + +const ProfileActions = () => { + const { getUser, logout } = useAuth(); + const requestServer = useRequest(); + const [openUpdate, setOpenUpdate] = useState(false); + const [openChangePass, setOpenChangePass] = useState(false); + + const openUpdateProfile = () => { + getUser(); + setOpenUpdate(true); + }; + const handleLogout = () => { + requestServer(GET_USER_LOGOUT_ROUTE, "post").then(() => { + logout(); + }); + }; + + return ( + <> + + + + + + + + + + + + + + + { + setOpenChangePass(true); + }} + > + + + + + {/* + */} + + ); +}; +export default ProfileActions; diff --git a/src/core/components/Profile/ProfileInfo.jsx b/src/core/components/Profile/ProfileInfo.jsx new file mode 100644 index 0000000..ec84b2a --- /dev/null +++ b/src/core/components/Profile/ProfileInfo.jsx @@ -0,0 +1,49 @@ +"use client"; + +import { useAuth } from "@/lib/contexts/auth"; +import { Avatar, Box, Chip, Divider, Stack, Typography } from "@mui/material"; +import moment from "jalali-moment"; + +const ProfileInfo = () => { + const { user } = useAuth(); + + return ( + <> + + + + + {`${user.full_name}`} + + + شماره تماس داخلی : {user.telephone_id} + + + + + + آخرین ورود + + + + {moment(user.last_login).locale("fa").fromNow()} + + + + + + + + + ); +}; +export default ProfileInfo; diff --git a/src/core/components/Profile/index.jsx b/src/core/components/Profile/index.jsx new file mode 100644 index 0000000..d538990 --- /dev/null +++ b/src/core/components/Profile/index.jsx @@ -0,0 +1,22 @@ +"use client"; + +import { Stack } from "@mui/material"; +import ProfileInfo from "./ProfileInfo"; +import ProfileActions from "./ProfileActions"; + +const Profile = () => { + return ( + + + + + ); +}; +export default Profile; diff --git a/src/core/components/StyledFab.jsx b/src/core/components/StyledFab.jsx deleted file mode 100644 index 95c5ff7..0000000 --- a/src/core/components/StyledFab.jsx +++ /dev/null @@ -1,10 +0,0 @@ -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/StyledForm.jsx b/src/core/components/StyledForm.jsx index 253fc12..accf0ab 100644 --- a/src/core/components/StyledForm.jsx +++ b/src/core/components/StyledForm.jsx @@ -1,6 +1,5 @@ -import {Form} from "formik"; -import {styled} from "@mui/material/styles"; +import { styled } from "@mui/material"; -const StyledForm = styled(Form)``; +const StyledForm = styled("form")``; export default StyledForm; diff --git a/src/core/components/StyledImage.jsx b/src/core/components/StyledImage.jsx deleted file mode 100644 index 58dd65f..0000000 --- a/src/core/components/StyledImage.jsx +++ /dev/null @@ -1,6 +0,0 @@ -import {styled} from "@mui/material"; -import Image from "next/image"; - -const StyledImage = styled(Image)``; - -export default StyledImage; diff --git a/src/core/components/TitlePage.jsx b/src/core/components/TitlePage.jsx deleted file mode 100644 index 77c72ee..0000000 --- a/src/core/components/TitlePage.jsx +++ /dev/null @@ -1,15 +0,0 @@ -import {useTranslations} from "next-intl"; -import Head from "next/head"; - -const TitlePage = ({text}) => { - const t = useTranslations(); - return ( - - - {text ? `${t("app_short_name")} | ${t(text)}` : t("app_short_name")} - - - ); -}; - -export default TitlePage; diff --git a/src/core/components/Toasts/error.jsx b/src/core/components/Toasts/error.jsx new file mode 100644 index 0000000..ffd6381 --- /dev/null +++ b/src/core/components/Toasts/error.jsx @@ -0,0 +1,222 @@ +import { toast } from "react-toastify"; +import { Box, Typography } from "@mui/material"; +import { Dangerous } from "@mui/icons-material"; + +export const errorServerToast = (toastContainer) => + toast.error( + () => ( + + + + + + {"The request failed due to an internal error."} + + + + + ), + { + icon: false, + containerId: toastContainer, + autoClose: 5000, + hideProgressBar: true, + pauseOnHover: true, + closeOnClick: false, + draggable: true, + }, + ); + +export const errorUnauthorizedToast = (toastContainer) => + toast.error( + () => ( + + + + + + {"The user is not authorized to make the request."} + + + + + ), + { + icon: false, + containerId: toastContainer, + autoClose: 3000, + hideProgressBar: true, + pauseOnHover: true, + closeOnClick: false, + draggable: true, + }, + ); +export const errorAccessDeniedToast = (message, toastContainer) => + toast.error( + () => ( + + + + + + {message || "Access Denied"} + + + + + ), + { + icon: false, + containerId: toastContainer, + autoClose: 3000, + hideProgressBar: true, + pauseOnHover: true, + closeOnClick: false, + draggable: true, + }, + ); + +export const errorLogicToast = (message, toastContainer) => + toast.error( + () => ( + + + + + + {message || + "The request was well-formed but was unable to be followed due to semantic errors."} + + + + + ), + { + icon: false, + containerId: toastContainer, + autoClose: false, + closeOnClick: false, + draggable: false, + }, + ); + +export const errorValidationToast = (message, toastContainer) => + toast.error( + () => ( + + + + + + {message || + "The request was well-formed but was unable to be followed due to semantic errors."} + + + + + ), + { + icon: false, + containerId: toastContainer, + autoClose: false, + closeOnClick: false, + draggable: false, + }, + ); + +export const errorTooManyToast = (toastContainer) => + toast.error( + () => ( + + + + + + {"Too many requests have been sent within a given time span."} + + + + + ), + { + icon: false, + containerId: toastContainer, + autoClose: false, + closeOnClick: false, + draggable: false, + }, + ); + +export const errorClientToast = (toastContainer) => + toast.error( + () => ( + + + + + + { + "The API request is invalid or improperly formed. Consequently, the API server could not understand the request." + } + + + + + ), + { + icon: false, + containerId: toastContainer, + autoClose: false, + closeOnClick: false, + draggable: false, + }, + ); diff --git a/src/core/components/Toasts/success.jsx b/src/core/components/Toasts/success.jsx new file mode 100644 index 0000000..7cdea7f --- /dev/null +++ b/src/core/components/Toasts/success.jsx @@ -0,0 +1,35 @@ +import { toast } from "react-toastify"; +import { Box, Typography } from "@mui/material"; +import { Beenhere } from "@mui/icons-material"; + +export const successToast = (toastContainer) => + toast.success( + () => ( + + + + + + {"عملیات با موفقیت انجام شد"} + + + + + ), + { + icon: false, + containerId: toastContainer, + autoClose: 3000, + hideProgressBar: true, + pauseOnHover: true, + closeOnClick: false, + draggable: true, + }, + ); diff --git a/src/core/components/UploadSystem.jsx b/src/core/components/UploadSystem.jsx deleted file mode 100644 index b40fb46..0000000 --- a/src/core/components/UploadSystem.jsx +++ /dev/null @@ -1,175 +0,0 @@ -import {Box, Button, Paper, Typography} from "@mui/material"; -import {useTranslations} from "next-intl"; -import AddIcon from "@mui/icons-material/Add"; -import DeleteForeverIcon from "@mui/icons-material/DeleteForever"; -import {useRef} from "react"; - -const UploadSystem = ({ - selectedImage, - setselectedImage, - handleUploadChange, - fieldname, - setFieldValue, - imageAlt, - imageSize, - fileType, - fileName, - setShowAddIcon, - showAddIcon, - }) => { - const t = useTranslations(); - const fileInputRef = useRef(null); - - const handleClick = () => { - fileInputRef.current.click(); - }; - - const handleDeleteImage = () => { - setselectedImage(null); - setFieldValue(fieldname, null); - setShowAddIcon(true); - if (fileInputRef.current) { - fileInputRef.current.value = ""; - } - }; - - const isDocumentFormat = (fileType) => { - const documentFormats = [ - "application/pdf", - "application/msword", - "application/vnd.openxmlformats-officedocument.wordprocessingml.document", - "application/vnd.ms-excel", - "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", - ]; - return documentFormats.includes(fileType); - }; - - return ( - - {showAddIcon ? ( - // Show the add icon and "Upload File" text when no image is selected - <> - - - - {t("UploadSystem.upload_file")} - - - - ) : ( - // Show the uploaded content along with the delete button when an image or document is selected - <> - {fileType && fileType.startsWith("image/") ? ( - - - - ) : ( - fileType && - isDocumentFormat(fileType) && ( - - - {fileName} - - - ) - )} - - - )} - - - ); -}; - -export default UploadSystem; diff --git a/src/core/components/notifications/ErrorNotification.jsx b/src/core/components/notifications/ErrorNotification.jsx deleted file mode 100644 index 776c904..0000000 --- a/src/core/components/notifications/ErrorNotification.jsx +++ /dev/null @@ -1,41 +0,0 @@ -import DangerousIcon from "@mui/icons-material/Dangerous"; -import {Box, Typography} from "@mui/material"; -import {toast} from "react-toastify"; - -const ErrorNotification = (pushToastList, notificationType, t, status, message) => { - const toastId = toast( - () => ( - <> - - - - - - {t("notifications.error")} ({t("notifications.code")}: {status}) - - - {message || t("notifications.error_static_text")} - - - - - - ), - { - containerId: 'validation', - autoClose: false, - closeOnClick: false, - draggable: false, - } - ); - pushToastList(notificationType, toastId); -}; - -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 deleted file mode 100644 index cc28a58..0000000 --- a/src/core/components/notifications/PendingNotification.jsx +++ /dev/null @@ -1,14 +0,0 @@ -import {toast} from "react-toastify"; - -const PendingNotification = (pushToastList, notificationType, t) => { - const toastId = toast(t("notifications.pending"), { - containerId: 'validation', - autoClose: false, - closeButton: false, - closeOnClick: false, - draggable: false, - }); - pushToastList(notificationType, toastId); -}; - -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 deleted file mode 100644 index 1207000..0000000 --- a/src/core/components/notifications/SuccessNotification.jsx +++ /dev/null @@ -1,44 +0,0 @@ -import BeenhereIcon from "@mui/icons-material/Beenhere"; -import {Box, Typography} from "@mui/material"; -import {toast} from "react-toastify"; - -const SuccessNotification = (pushToastList, notificationType, t, status) => { - const toastId = toast( - () => ( - <> - - - - - - {t("notifications.success")} ({t("notifications.code")}:{" "} - {status}) - - - {t("notifications.success_static_text")} - - - - - - ), - { - containerId: 'validation', - autoClose: 3000, - hideProgressBar: true, - pauseOnHover: true, - closeOnClick: false, - draggable: true, - } - ); - pushToastList(notificationType, toastId); -}; - -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 deleted file mode 100644 index 1c20ba3..0000000 --- a/src/core/components/notifications/UploadFileNotification.jsx +++ /dev/null @@ -1,40 +0,0 @@ -import DangerousIcon from "@mui/icons-material/Dangerous"; -import {Box, Typography} from "@mui/material"; -import {toast} from "react-toastify"; - -const UploadFileNotification = (t) => { - toast( - ({closeToast}) => ( - <> - - - - - - {t("UploadSystem.uploadfile_error")} - - - - - - ), - { - containerId: 'validation', - toastId: 'upload', - autoClose: 3000, - hideProgressBar: true, - pauseOnHover: true, - closeOnClick: false, - draggable: true, - } - ); -}; - -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 deleted file mode 100644 index bc2a1fb..0000000 --- a/src/core/components/notifications/WarningNotification.jsx +++ /dev/null @@ -1,42 +0,0 @@ -import ReportIcon from "@mui/icons-material/Report"; -import {Box, Typography} from "@mui/material"; -import {toast} from "react-toastify"; - -const WarningNotification = (pushToastList, notificationType, t, status) => { - const toastId = toast( - () => ( - <> - - - - - - {t("notifications.warning")} ({t("notifications.code")}:{" "} - {status}) - - - {t("notifications.warning_static_text")} - - - - - - ), - { - containerId: 'validation', - autoClose: false, - closeOnClick: false, - draggable: false, - } - ); - pushToastList(notificationType, toastId); -}; - -export default WarningNotification; \ No newline at end of file diff --git a/src/core/components/notifications/index.jsx b/src/core/components/notifications/index.jsx deleted file mode 100644 index 34e44f3..0000000 --- a/src/core/components/notifications/index.jsx +++ /dev/null @@ -1,27 +0,0 @@ -import pendingNotification from "@/core/components/notifications/PendingNotification"; -import WarningNotification from "@/core/components/notifications/WarningNotification"; -import ErrorNotification from "@/core/components/notifications/ErrorNotification"; -import SuccessNotification from "@/core/components/notifications/SuccessNotification"; - -const Notifications = (pushToastList, notificationType, t, status, message) => { - switch (notificationType) { - case "pending": - pendingNotification(pushToastList, notificationType, t); - break; - case "warning": - WarningNotification(pushToastList, notificationType, t, status); - break; - case "error": - if (message) { - ErrorNotification(pushToastList, notificationType, t, status, message) - } else { - ErrorNotification(pushToastList, notificationType, t, status) - } - break; - case "success": - SuccessNotification(pushToastList, notificationType, t, status); - break; - } -}; - -export default Notifications; diff --git a/src/core/components/svgs/Svg403.jsx b/src/core/components/svgs/Svg403.jsx deleted file mode 100644 index d706ed0..0000000 --- a/src/core/components/svgs/Svg403.jsx +++ /dev/null @@ -1,131 +0,0 @@ -import {useTheme} from "@mui/material/styles"; - -const Svg403 = ({width, height}) => { - const theme = useTheme() - const fillColor = theme.palette.primary.main - - return ( - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ) - -} - -export default Svg403 \ No newline at end of file diff --git a/src/core/components/svgs/Svg404.jsx b/src/core/components/svgs/Svg404.jsx deleted file mode 100644 index 159d7c6..0000000 --- a/src/core/components/svgs/Svg404.jsx +++ /dev/null @@ -1,101 +0,0 @@ -import {useTheme} from "@mui/material/styles"; - -const Svg404 = ({width, height}) => { - const theme = useTheme() - const fillColor = theme.palette.primary.main - - return ( - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ) -} - -export default Svg404 \ No newline at end of file diff --git a/src/core/components/svgs/SvgChangePassword.jsx b/src/core/components/svgs/SvgChangePassword.jsx deleted file mode 100644 index 1221d28..0000000 --- a/src/core/components/svgs/SvgChangePassword.jsx +++ /dev/null @@ -1,77 +0,0 @@ -import {useTheme} from "@mui/material/styles"; - -const SvgChangePassword = ({width, height}) => { - const theme = useTheme() - const fillColor = theme.palette.primary.main - - return ( - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ) -} - -export default SvgChangePassword \ No newline at end of file diff --git a/src/core/components/svgs/SvgDashboard.jsx b/src/core/components/svgs/SvgDashboard.jsx deleted file mode 100644 index a636bf4..0000000 --- a/src/core/components/svgs/SvgDashboard.jsx +++ /dev/null @@ -1,117 +0,0 @@ -import {useTheme} from "@mui/material/styles"; - -const SvgDashboard = ({width, height}) => { - const theme = useTheme() - const fillColor = theme.palette.primary.main - - return ( - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ) -} - -export default SvgDashboard \ No newline at end of file diff --git a/src/core/components/svgs/SvgError.jsx b/src/core/components/svgs/SvgError.jsx new file mode 100644 index 0000000..756496e --- /dev/null +++ b/src/core/components/svgs/SvgError.jsx @@ -0,0 +1,100 @@ +"use client"; +import { useTheme } from "@mui/material"; + +const SvgError = ({ width, height, color = null }) => { + const theme = useTheme(); + const fillColor = color || theme.palette.primary.main; + + return ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ); +}; + +export default SvgError; diff --git a/src/core/components/svgs/SvgLoading.jsx b/src/core/components/svgs/SvgLoading.jsx index 9ff61ff..661441e 100644 --- a/src/core/components/svgs/SvgLoading.jsx +++ b/src/core/components/svgs/SvgLoading.jsx @@ -1,136 +1,293 @@ -import {useTheme} from "@mui/material/styles"; +"use client"; +import { useTheme } from "@mui/material"; -const SvgLoading = ({width, height}) => { - const theme = useTheme() - const fillColor = theme.palette.primary.main +const SvgLoading = ({ width, height, color = null }) => { + const theme = useTheme(); + const fillColor = color || theme.palette.primary.main; - return ( - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ) -} + return ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ); +}; -export default SvgLoading \ No newline at end of file +export default SvgLoading; diff --git a/src/core/components/svgs/SvgLogin.jsx b/src/core/components/svgs/SvgLogin.jsx deleted file mode 100644 index edf3b51..0000000 --- a/src/core/components/svgs/SvgLogin.jsx +++ /dev/null @@ -1,75 +0,0 @@ -import {useTheme} from "@mui/material/styles"; - -const SvgLogin = ({width, height}) => { - const theme = useTheme() - const fillColor = theme.palette.primary.main - - return ( - - - - - - - - - - - - - - - - - - - - - - - - - - - - ) - -} - -export default SvgLogin \ No newline at end of file diff --git a/src/core/components/svgs/SvgNotFound.jsx b/src/core/components/svgs/SvgNotFound.jsx new file mode 100644 index 0000000..7c57e88 --- /dev/null +++ b/src/core/components/svgs/SvgNotFound.jsx @@ -0,0 +1,162 @@ +import { useTheme } from "@mui/material"; + +const SvgNotFound = ({ width, height, color = null }) => { + const theme = useTheme(); + const fillColor = color || theme.palette.primary.main; + + return ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ); +}; +export default SvgNotFound; diff --git a/src/core/data/apiRoutes.js b/src/core/data/apiRoutes.js deleted file mode 100644 index f949dac..0000000 --- a/src/core/data/apiRoutes.js +++ /dev/null @@ -1,54 +0,0 @@ -const BASE_URL = process.env.NEXT_PUBLIC_BASE_URL; - -//login -export const GET_USER_TOKEN = BASE_URL + "/api/auth/login"; -//end login - -//edit profile -export const UPDATE_AVATAR = BASE_URL + ""; -//end edit profile - -// role list -export const GET_ROLE_LIST = BASE_URL + "/api/roles/list"; -// role list - -// province list -export const GET_PROVINCE_LIST = BASE_URL + "/api/provinces"; -// province list - -// province list -export const GET_CALLER_HISTORY = BASE_URL + "/api/calls/list"; -// province list - -//change password -export const SET_USER_PASSWORD = BASE_URL + "/api/profile/change_password"; -//end change password - -//user data -export const GET_USER = BASE_URL + "/api/profile/info"; -//user data - -//sidebar notification -export const GET_SIDEBAR_NOTIFICATION = BASE_URL + "/dashboard/notification"; -//sidebar notification - -// role management -export const GET_ROLES = BASE_URL + "/api/roles"; -export const ADD_ROLE = BASE_URL + "/api/roles"; -export const UPDATE_ROLE = BASE_URL + "/api/roles"; -export const DELETE_ROLE = BASE_URL + "/api/roles"; - -export const GET_PERMISSIONS_LIST = BASE_URL + "/api/permissions/list"; -//role management - -//expert management -export const GET_EXPERTS = BASE_URL + "/api/users"; -export const ADD_EXPERT = BASE_URL + "/api/users"; -export const UPDATE_EXPERT = BASE_URL + "/api/users"; -export const DELETE_EXPERT = BASE_URL + "/api/users"; -//expert management - -//call action history -export const GET_CATEGORY = BASE_URL + "/api/categories"; -export const CALL_ACTION = BASE_URL + "/api/calls"; -//call action history diff --git a/src/core/data/headerProfileItems.jsx b/src/core/data/headerProfileItems.jsx deleted file mode 100644 index 577fba3..0000000 --- a/src/core/data/headerProfileItems.jsx +++ /dev/null @@ -1,19 +0,0 @@ -import BorderColorIcon from "@mui/icons-material/BorderColor"; -import PasswordIcon from "@mui/icons-material/Password"; - -const headerProfileItems = [ - { - key: 0, - name: "header.edit_profile", - route: "/dashboard/edit-profile", - icon: , - }, - { - key: 1, - name: "header.change_password", - route: "/dashboard/change-password", - icon: , - }, -]; - -export default headerProfileItems; diff --git a/src/core/data/sidebarMenu.jsx b/src/core/data/sidebarMenu.jsx deleted file mode 100644 index 1fcc48c..0000000 --- a/src/core/data/sidebarMenu.jsx +++ /dev/null @@ -1,36 +0,0 @@ -import SpaceDashboardIcon from "@mui/icons-material/SpaceDashboard"; -import AccessibilityIcon from '@mui/icons-material/Accessibility'; -import ManageAccountsIcon from '@mui/icons-material/ManageAccounts'; - -const sidebarMenu = [ - [ - { - key: "sidebar.dashboard", - type: "page", - route: "/dashboard", - icon: , - selected: false, - permission: "all", - }, - { - key: "sidebar.expert-management", - type: "page", - name: "expert_management", - route: "/dashboard/expert-management", - icon: , - selected: false, - permission: "all", - }, - { - key: "sidebar.role-management", - name: "role_management", - type: "page", - route: "/dashboard/role-management", - icon: , - selected: false, - permission: "manage_roles", - }, - ], -]; - -export default sidebarMenu; diff --git a/src/core/middlewares/withAuth.js b/src/core/middlewares/withAuth.js new file mode 100644 index 0000000..c9286e7 --- /dev/null +++ b/src/core/middlewares/withAuth.js @@ -0,0 +1,44 @@ +"use client"; +import { useAuth } from "@/lib/contexts/auth"; +import { usePathname, useRouter } from "next/navigation"; +import { useEffect } from "react"; +import LoadingHardPage from "../components/LoadingHardPage"; +import { Stack, Typography } from "@mui/material"; + +function WithAuthMiddleware({ children }) { + const router = useRouter(); + const pathName = usePathname(); + const { isAuth, initAuthState, errorState } = useAuth(); + + useEffect(() => { + if (!initAuthState) return; + if (!isAuth) { + router.replace(`/login?redirect=${encodeURIComponent(pathName)}`); + } + }, [isAuth, initAuthState]); + + if (!initAuthState || !isAuth) + return ( + + {errorState.message} + + {" "} + کد : {errorState.status} + + + ) : ( + درحال احراز هویت... + ) + } + loading={true} + /> + ); + + return <>{children}; +} + +export default WithAuthMiddleware; diff --git a/src/core/middlewares/withPermission.js b/src/core/middlewares/withPermission.js new file mode 100644 index 0000000..f8b121a --- /dev/null +++ b/src/core/middlewares/withPermission.js @@ -0,0 +1,47 @@ +"use client"; + +import { Box, Typography } from "@mui/material"; +import { usePermissions } from "@/lib/hooks/usePermissions"; +import { useEffect, useState } from "react"; + +function WithPermission({ children, permission_name }) { + const { data, isLoading } = usePermissions(); + const [cachedData, setCachedData] = useState(null); + + useEffect(() => { + if (data) { + setCachedData(data); + } + }, [data]); + + if (isLoading || !cachedData || !permission_name) { + return null; + } + + const hasPermission = + permission_name.includes("all") || + permission_name.some((permission) => cachedData.includes(permission)); + + if (!hasPermission) { + return ( + + + شما دسترسی لازم به این صفحه را ندارید + + + ); + } + return <>{children}; +} + +export default WithPermission; diff --git a/src/core/middlewares/withWidget.js b/src/core/middlewares/withWidget.js new file mode 100644 index 0000000..95180ab --- /dev/null +++ b/src/core/middlewares/withWidget.js @@ -0,0 +1,5 @@ +const WithWidgetMiddleware = ({ children, enable }) => { + return enable ? <>{children} : null; +}; + +export default WithWidgetMiddleware; diff --git a/src/core/middlewares/withoutAuth.js b/src/core/middlewares/withoutAuth.js new file mode 100644 index 0000000..396b848 --- /dev/null +++ b/src/core/middlewares/withoutAuth.js @@ -0,0 +1,45 @@ +"use client"; +import { useAuth } from "@/lib/contexts/auth"; +import { useRouter, useSearchParams } from "next/navigation"; +import { useEffect } from "react"; +import LoadingHardPage from "../components/LoadingHardPage"; +import { Stack, Typography } from "@mui/material"; + +function WithoutAuthMiddleware({ children }) { + const router = useRouter(); + const { isAuth, initAuthState, errorState } = useAuth(); + const searchParams = useSearchParams(); + + const redirect = searchParams.get("redirect"); + + useEffect(() => { + if (!initAuthState) return; + if (isAuth) { + router.replace(redirect ? decodeURIComponent(redirect) : "/dashboard"); + } + }, [isAuth, initAuthState]); + + if (!initAuthState || isAuth) + return ( + + {errorState.message} + + {" "} + کد : {errorState.status} + + + ) : ( + درحال احراز هویت... + ) + } + loading={true} + /> + ); + return <>{children}; +} + +export default WithoutAuthMiddleware; diff --git a/src/core/utils/cacheRtl.js b/src/core/utils/cacheRtl.js new file mode 100644 index 0000000..5830941 --- /dev/null +++ b/src/core/utils/cacheRtl.js @@ -0,0 +1,15 @@ +"use client"; +import createCache from "@emotion/cache"; +import { CacheProvider } from "@emotion/react"; +import { prefixer } from "stylis"; +import rtlPlugin from "stylis-plugin-rtl"; + +// Create rtl cache +export const rtlCache = createCache({ + key: "mui-rtl", + stylisPlugins: [prefixer, rtlPlugin], +}); + +export function Rtl(props) { + return {props.children}; +} diff --git a/src/core/utils/createEmotionCache.jsx b/src/core/utils/createEmotionCache.jsx deleted file mode 100644 index 413fafe..0000000 --- a/src/core/utils/createEmotionCache.jsx +++ /dev/null @@ -1,38 +0,0 @@ -import createCache from "@emotion/cache"; -import {prefixer} from "stylis"; -import stylisRTLPlugin from "stylis-plugin-rtl"; - -const isBrowser = typeof document !== "undefined"; - -export const createEmotionCacheLtr = () => { - let insertionPoint; - - if (isBrowser) { - const emotionInsertionPoint = document.querySelector( - 'meta[name="emotion-insertion-point"]' - ); - insertionPoint = emotionInsertionPoint ?? undefined; - } - - return createCache({ - key: "mui-style", - insertionPoint, - }); -}; - -export const createEmotionCacheRtl = () => { - let insertionPoint; - - if (isBrowser) { - const emotionInsertionPoint = document.querySelector( - 'meta[name="emotion-insertion-point"]' - ); - insertionPoint = emotionInsertionPoint ?? undefined; - } - - return createCache({ - key: "muirtl", - stylisPlugins: [prefixer, stylisRTLPlugin], - insertionPoint, - }); -}; diff --git a/src/core/utils/errorHandler.js b/src/core/utils/errorHandler.js deleted file mode 100644 index e380e84..0000000 --- a/src/core/utils/errorHandler.js +++ /dev/null @@ -1,65 +0,0 @@ -import Notifications from "@/core/components/notifications"; - -export const errorSetting = (dismissToastList, t, notification) => { - if (notification) { - dismissToastList(["pending", "warning", "error", "success"]) - } -} -export const errorRequest = (dismissToastList, t, notification) => { - if (notification) { - dismissToastList(["pending", "warning", "error", "success"]) - } -} - -export const errorResponse = (pushToastList, dismissToastList, response, clearToken, t, notification) => { - if (notification) { - dismissToastList(["pending", "warning", "error", "success"]) - } - if (isServerError(response.status)) { - errorServer(pushToastList, response, t, notification) - } else if (isClientError(response.status)) { - errorClient(pushToastList, response, clearToken, t, notification) - } -} - -const errorServer = (pushToastList, response, t, notification) => { - if (notification) Notifications(pushToastList, "warning", t, response.status); -} -const errorClient = (pushToastList, response, clearToken, t, notification) => { - switch (response.status) { - case 401: - clearToken() - if (notification) Notifications(pushToastList, "error", t, response.status); - break; - case 422: - if ('type' in response.data) { - errorLogic(pushToastList, response, t, notification) - break; - } - errorValidation(pushToastList, response, t, notification) - break; - case 429: - if (notification) Notifications(pushToastList, "error", t, response.status); - break - default: - if (notification) Notifications(pushToastList, "error", t, response.status); - break - } -} - -const isServerError = status => status >= 500 && status <= 599; -const isClientError = status => status >= 400 && status <= 499; - -const errorLogic = (pushToastList, response, t, notification) => { - if (notification) Notifications(pushToastList, "error", t, response.status, response.data.message); -} -const errorValidation = (pushToastList, response, t, notification) => { - if (notification) { - const errorsMap = Object.keys(response.data.errors) - const errorsArray = response.data.errors - - errorsMap.map((item, index) => { - Notifications(pushToastList, "error", t, response.status, errorsArray[item][0]); - }) - } -} \ No newline at end of file diff --git a/src/core/utils/errorResponse.js b/src/core/utils/errorResponse.js new file mode 100644 index 0000000..8d281ba --- /dev/null +++ b/src/core/utils/errorResponse.js @@ -0,0 +1,72 @@ +"use client"; +import { toast } from "react-toastify"; +import { + errorAccessDeniedToast, + errorClientToast, + errorLogicToast, + errorServerToast, + errorTooManyToast, + errorUnauthorizedToast, + errorValidationToast, +} from "@/core/components/Toasts/error"; + +const isServerError = (status) => status >= 500 && status <= 599; +const isClientError = (status) => status >= 400 && status <= 499; + +const errorServer = (response, notification, toastContainer) => { + if (notification) errorServerToast(toastContainer); +}; + +const errorClient = (response, notification, toastContainer, logout) => { + switch (response.status) { + case 401: + logout(); + if (notification) errorUnauthorizedToast(toastContainer); + break; + case 403: + if (notification) + errorAccessDeniedToast(response.data.message, toastContainer); + break; + case 422: + if ("type" in response.data) { + if (Array.isArray(response.data.message)) { + response.data.message.map((item) => { + if (notification) errorLogicToast(item, toastContainer); + }); + } else { + if (notification) + errorLogicToast(response.data.message, toastContainer); + } + break; + } + if (notification) { + const errorsMap = Object.keys(response.data.errors); + const errorsArray = response.data.errors; + + errorsMap.map((item, index) => { + errorValidationToast(errorsArray[item][0], toastContainer); + }); + } + break; + case 429: + if (notification) errorTooManyToast(toastContainer); + break; + default: + if (notification) errorClientToast(toastContainer); + break; + } +}; + +export const errorResponse = ( + response, + notification, + toastContainer, + logout, +) => { + if (notification) toast.dismiss({ containerId: toastContainer }); + if (isServerError(response.status)) { + errorServer(response, notification, toastContainer); + } else if (isClientError(response.status)) { + errorClient(response, notification, toastContainer, logout); + } +}; diff --git a/src/core/utils/filterMenuItems.js b/src/core/utils/filterMenuItems.js new file mode 100644 index 0000000..ad10189 --- /dev/null +++ b/src/core/utils/filterMenuItems.js @@ -0,0 +1,19 @@ +export function filterMenuItems(items, _permissions = []) { + return items.reduce((acc, item) => { + if (item.permissions) { + if ( + item.permissions.some((permission) => _permissions.includes(permission)) + ) { + acc.push(item); + } + } else if (item.hasSubitems) { + const filteredSubItems = filterMenuItems(item.Subitems, _permissions); + if (filteredSubItems.length > 0) { + acc.push({ ...item, Subitems: filteredSubItems }); + } + } else { + acc.push(item); + } + return acc; + }, []); +} diff --git a/src/core/utils/getValueByPath.js b/src/core/utils/getValueByPath.js new file mode 100644 index 0000000..a03958a --- /dev/null +++ b/src/core/utils/getValueByPath.js @@ -0,0 +1,14 @@ +export const getValueByPath = (obj, path) => { + const keys = path.split("."); + + let value = obj; + + for (const key of keys) { + if (value === undefined) { + return undefined; + } + value = value[key]; + } + + return value; +}; diff --git a/src/core/utils/headerMenu.js b/src/core/utils/headerMenu.js new file mode 100644 index 0000000..cd33e61 --- /dev/null +++ b/src/core/utils/headerMenu.js @@ -0,0 +1,15 @@ +const api = process.env.NEXT_PUBLIC_API_URL; + +export const headerMenu = [ + // { + // title: "تصادفات", + // subMenu: [ + // [ + // { + // title: "تصادفات روزانه", + // href: api + "/v2/daily_accidents/create", + // }, + // ], + // ], + // }, +]; diff --git a/src/core/utils/pageMenu.js b/src/core/utils/pageMenu.js new file mode 100644 index 0000000..9f08204 --- /dev/null +++ b/src/core/utils/pageMenu.js @@ -0,0 +1,12 @@ +import SpaceDashboardIcon from "@mui/icons-material/SpaceDashboard"; + +export const pageMenu = [ + { + id: "dashboard", + label: "پیشخوان", + type: "page", + route: "/dashboard", + icon: , + permissions: ["all"], + }, +]; diff --git a/src/core/utils/routes.js b/src/core/utils/routes.js new file mode 100644 index 0000000..37e72fe --- /dev/null +++ b/src/core/utils/routes.js @@ -0,0 +1,12 @@ +const api = process.env.NEXT_PUBLIC_API_URL; + +export const GET_USER_ROUTE = api + "/profile/info"; +export const GET_USER_LOGIN_ROUTE = api + "/auth/login"; +export const GET_USER_LOGOUT_ROUTE = api + "/auth/logout"; +export const GET_PERMISSIONS_ROUTE = ""; +export const GET_SIDEBAR_BADGE_ROUTE = ""; + +export const GET_CATEGORY = api + "/categories"; + +export const GET_CALLER_HISTORY = api + "/calls/list"; +export const CALL_ACTION = api + "/calls"; diff --git a/src/core/utils/succesHandler.js b/src/core/utils/succesHandler.js deleted file mode 100644 index c852f86..0000000 --- a/src/core/utils/succesHandler.js +++ /dev/null @@ -1,8 +0,0 @@ -import Notifications from "@/core/components/notifications"; - -export const successRequest = (pushToastList, dismissToastList, response, t, options) => { - if (options.notification && options.success.notification.show) { - dismissToastList(["pending", "warning", "error", "success"]) - Notifications(pushToastList, "success", t, response.status); - } -} \ No newline at end of file diff --git a/src/core/utils/successRequest.js b/src/core/utils/successRequest.js new file mode 100644 index 0000000..daa2ab7 --- /dev/null +++ b/src/core/utils/successRequest.js @@ -0,0 +1,10 @@ +"use client"; +import { toast } from "react-toastify"; +import { successToast } from "@/core/components/Toasts/success"; + +export const successRequest = (notification, toastContainer) => { + if (notification) { + toast.dismiss({ containerId: toastContainer }); + successToast(toastContainer); + } +}; diff --git a/src/core/utils/theme-rtl.jsx b/src/core/utils/theme-rtl.jsx deleted file mode 100644 index 33a9ae7..0000000 --- a/src/core/utils/theme-rtl.jsx +++ /dev/null @@ -1,14 +0,0 @@ -import {createTheme} from "@mui/material/styles"; -import theme from "./theme"; -import {faIR} from "@mui/x-date-pickers/locales"; - -const themeRtl = createTheme({ - direction: "rtl", - typography: { - fontFamily: `IRANSansFaNum, sans-serif`, - }, - faIR, - ...theme, -}); - -export default themeRtl; diff --git a/src/core/utils/theme.js b/src/core/utils/theme.js new file mode 100644 index 0000000..64f93f8 --- /dev/null +++ b/src/core/utils/theme.js @@ -0,0 +1,31 @@ +"use client"; +import { createTheme } from "@mui/material"; + +const theme = createTheme({ + direction: "rtl", + typography: { + fontFamily: `IRANSansFaNum, sans-serif`, + fontSize: 12, + }, + palette: { + primary: { + main: "#0D47A1", + contrastText: "#fff", + }, + secondary: { + main: "#90A4AE", + }, + }, + components: { + MuiBackdrop: { + styleOverrides: { + root: { + backdropFilter: "blur(2px)", + backgroundColor: "rgba(0, 0, 0, 0.5)", + }, + }, + }, + }, +}); + +export default theme; diff --git a/src/core/utils/theme.jsx b/src/core/utils/theme.jsx deleted file mode 100644 index 672e8a8..0000000 --- a/src/core/utils/theme.jsx +++ /dev/null @@ -1,29 +0,0 @@ -import {colord} from "colord"; - -const theme = { - palette: { - primary: { - main: process.env.NEXT_PUBLIC_PRIMARY_MAIN, - contrastText: "#FFFFFF", - light: colord(process.env.NEXT_PUBLIC_PRIMARY_MAIN).lighten(0.1).toHex(), - dark: colord(process.env.NEXT_PUBLIC_PRIMARY_MAIN).darken(0.1).toHex(), - }, - secondary: { - main: process.env.NEXT_PUBLIC_SECONDARY_MAIN, - contrastText: "#FFFFFF", - light: colord(process.env.NEXT_PUBLIC_SECONDARY_MAIN).lighten(0.1).toHex(), - dark: colord(process.env.NEXT_PUBLIC_SECONDARY_MAIN).darken(0.1).toHex(), - }, - }, - components: { - MuiBackdrop: { - styleOverrides: { - root: { - backgroundColor: colord(process.env.NEXT_PUBLIC_PRIMARY_MAIN).darken(0.2).alpha(0.7).toHex(), - }, - }, - }, - }, -}; - -export default theme; diff --git a/src/layouts/AppLayout.jsx b/src/layouts/AppLayout.jsx deleted file mode 100644 index 61984f6..0000000 --- a/src/layouts/AppLayout.jsx +++ /dev/null @@ -1,67 +0,0 @@ -import theme from "@/core/utils/theme"; -import useLanguage from "@/lib/app/hooks/useLanguage"; -import useLoading from "@/lib/app/hooks/useLoading"; -import useUser from "@/lib/app/hooks/useUser"; -import NextNProgress from "nextjs-progressbar"; -import {useEffect} from "react"; -import {ToastContainer} from "react-toastify"; -import useDirection from "@/lib/app/hooks/useDirection"; -import "react-toastify/dist/ReactToastify.css"; -import NetworkComponent from "@/core/components/NetworkComponent"; -import GlobalHead from "@/core/components/GlobalHead"; - -function AppLayout({children, isBot}) { - const {languageIsReady} = useLanguage(); - const {setLoadingPage} = useLoading(); - const {userChangedLanguage, token, isAuth} = useUser(); - const {directionApp} = useDirection(); - - useEffect(() => { - if (languageIsReady) { - if (token) { - if (isAuth) { - if (userChangedLanguage) { - setLoadingPage(true); - return; - } - setLoadingPage(false); - return; - } - setLoadingPage(true); - return; - } - setLoadingPage(false); - return; - } - setLoadingPage(true); - }, [languageIsReady, token, isAuth, userChangedLanguage]); - - if (!isBot) { - if (userChangedLanguage) return; - if (!languageIsReady) return; - } - - return ( - <> - - - - - - {children} - - ); -} - -export default AppLayout; diff --git a/src/layouts/CenterLayout.jsx b/src/layouts/CenterLayout.jsx deleted file mode 100644 index 1e1b8bf..0000000 --- a/src/layouts/CenterLayout.jsx +++ /dev/null @@ -1,18 +0,0 @@ -import {Fade, Stack} from "@mui/material"; - -const CenterLayout = (props) => { - return ( - - - {props.children} - - - ); -}; - -export default CenterLayout; diff --git a/src/layouts/DashboardLayout.jsx b/src/layouts/DashboardLayout.jsx deleted file mode 100644 index 668f9d8..0000000 --- a/src/layouts/DashboardLayout.jsx +++ /dev/null @@ -1,18 +0,0 @@ -import AppPermission from "@/middlewares/AppPermission"; -import CallWidget from "@/components/layouts/Dashboard/CallWidget"; -import WithAuthMiddleware from "@/middlewares/WithAuth"; -import Dashboard from "@/components/layouts/Dashboard"; - -const DashboardLayout = (props) => { - const permissions = ["NEXT_PUBLIC_HAS_WIDGET"]; - return ( - - - - - - - ); -}; - -export default DashboardLayout; diff --git a/src/layouts/FullPageLayout.jsx b/src/layouts/FullPageLayout.jsx deleted file mode 100644 index d6ed61e..0000000 --- a/src/layouts/FullPageLayout.jsx +++ /dev/null @@ -1,21 +0,0 @@ -import {Stack} from "@mui/material"; - -const FullPageLayout = (props) => { - return ( - - {props.children} - - ); -}; - -export default FullPageLayout; diff --git a/src/layouts/MuiLayout.jsx b/src/layouts/MuiLayout.jsx deleted file mode 100644 index eba3449..0000000 --- a/src/layouts/MuiLayout.jsx +++ /dev/null @@ -1,70 +0,0 @@ -import NoSsrHandler from "@/core/components/NoSsrHandler"; -import {createEmotionCacheRtl} from "@/core/utils/createEmotionCache"; -import themeRtl from "@/core/utils/theme-rtl"; -import {CacheProvider} from "@emotion/react"; -import {GlobalStyles} from "@mui/material"; -import CssBaseline from "@mui/material/CssBaseline"; -import {ThemeProvider} from "@mui/material/styles"; -import Head from "next/head"; - -const clientSideEmotionCacheRtl = createEmotionCacheRtl(); - -const MuiLayout = ({children, isBot}) => { - const emotionCache = clientSideEmotionCacheRtl; - const theme = themeRtl; - - return ( - - - - - - - - - - {children} - - - - ); -}; - -export default MuiLayout; diff --git a/src/layouts/ScrollableLayout.jsx b/src/layouts/ScrollableLayout.jsx deleted file mode 100644 index 583edd7..0000000 --- a/src/layouts/ScrollableLayout.jsx +++ /dev/null @@ -1,25 +0,0 @@ -import {Box, Fade} from "@mui/material"; - -const ScrollableLayout = (props) => { - const overflowY = props?.y || "hidden"; - const overflowX = props?.x || "hidden"; - - return ( - - - {props.children} - - - ); -}; - -export default ScrollableLayout; diff --git a/src/layouts/index.jsx b/src/layouts/index.jsx deleted file mode 100644 index ce7a9c9..0000000 --- a/src/layouts/index.jsx +++ /dev/null @@ -1,20 +0,0 @@ -import DashboardLayout from "@/layouts/DashboardLayout"; -import {Fragment} from "react"; - -const layoutList = { - DashboardLayout, -} - -const Layout = ({layout, children}) => { - - const Component = layoutList[layout?.name] || Fragment - const props = layout?.props || {} - - return ( - - {children} - - ) -} - -export default Layout \ No newline at end of file diff --git a/src/lib/app/contexts/language.jsx b/src/lib/app/contexts/language.jsx deleted file mode 100644 index e9e24f7..0000000 --- a/src/lib/app/contexts/language.jsx +++ /dev/null @@ -1,97 +0,0 @@ -import {FA_DATATABLE_LOCALIZATION} from "&/locales/fa/datatable"; -import {useRouter} from "next/router"; -import {createContext, useEffect, useState} from "react"; -import useUser from "../hooks/useUser"; - -export const LanguageContext = createContext(); - -export const LanguageProvider = ({children}) => { - const router = useRouter(); - const languageList = [ - { - key: "fa", - dir: "rtl", - name: "فارسی", - fontFamily: `IRANSans, sans-serif`, - tableLocalization: FA_DATATABLE_LOCALIZATION, - } - ]; - const {user, userChangedLanguage, changeLanguageState} = useUser(); - const [languageIsReady, setLanguageIsReady] = useState(false); - const [languageApp, setLanguageApp] = useState(process.env.NEXT_PUBLIC_DEFAULT_LANGUAGE); - const [directionApp, setDirectionApp] = useState( - process.env.NEXT_PUBLIC_DEFAULT_DIRECTION - ); - - useEffect(() => { - const lang = localStorage.getItem("_language"); - - if (lang) { - setLanguageApp(lang); - } - }, []); - - useEffect(() => { - if (!languageApp) return; - - const lang = localStorage.getItem("_language"); - - if (!lang) { - localStorage.setItem("_language", languageApp); - } else { - if (languageApp != lang) { - localStorage.setItem("_language", languageApp); - } - } - }, [languageApp]); - - useEffect(() => { - if (!router.isReady) return; - if (user.user_language) { - if (user.user_language != languageApp) { - setLanguageApp(user.user_language); - return; - } - } - - if (languageApp != router.locale) { - router.replace( - {pathname: router.pathname, query: router.query}, - router.asPath, - { - locale: languageApp, - } - ); - return; - } - for (const lang of languageList) { - if (languageApp != lang.key) continue; - setDirectionApp(lang.dir); - document.dir = lang.dir; - } - - const timer = setTimeout(() => { - changeLanguageState(false); - setLanguageIsReady(true); - }, 1000); - - return () => { - clearTimeout(timer); - }; - }, [router.locale, router.isReady, userChangedLanguage, languageApp]); - - return ( - - {children} - - ); -}; diff --git a/src/lib/app/contexts/loading.jsx b/src/lib/app/contexts/loading.jsx deleted file mode 100644 index f5b8a47..0000000 --- a/src/lib/app/contexts/loading.jsx +++ /dev/null @@ -1,14 +0,0 @@ -import LoadingHardPage from "@/core/components/LoadingHardPage"; -import {createContext, useState} from "react"; - -export const LoadingContext = createContext(); - -export const LoadingProvider = ({children}) => { - const [loadingPage, setLoadingPage] = useState(false); - return ( - - - {children} - - ); -}; diff --git a/src/lib/app/contexts/socket.jsx b/src/lib/app/contexts/socket.jsx deleted file mode 100644 index a95bccf..0000000 --- a/src/lib/app/contexts/socket.jsx +++ /dev/null @@ -1,75 +0,0 @@ -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"; -import useDirection from "@/lib/app/hooks/useDirection"; - -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 {directionApp} = useDirection(); - const socket = useMemo(() => io(process.env.NEXT_PUBLIC_SERVER_SOCKET_URL, { - autoConnect: false, - // transports: ['websocket'], - 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'), { - containerId: 'connection', - 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/toast.jsx b/src/lib/app/contexts/toast.jsx deleted file mode 100644 index b9d4cbc..0000000 --- a/src/lib/app/contexts/toast.jsx +++ /dev/null @@ -1,37 +0,0 @@ -import {createContext, useReducer} from "react"; -import {toast} from "react-toastify"; - -export const ToastContext = createContext() - -const reducer = (state, action) => { - switch (action.type) { - case "PUSH": - return { - ...state, - [action.toast_type]: [...state[action.toast_type], action.toast_id] - }; - case "DISMISS": - action.toast_type.map((item) => { - state[item].map((id) => { - toast.dismiss(id); - }) - state[item] = [] - }); - return state; - } -}; - -export const ToastProvider = ({children}) => { - const [state, dispatch] = useReducer(reducer, { - pending: [], - error: [], - warning: [], - success: [] - }); - - return ( - - {children} - - ); -}; diff --git a/src/lib/app/contexts/user.jsx b/src/lib/app/contexts/user.jsx deleted file mode 100644 index da83e93..0000000 --- a/src/lib/app/contexts/user.jsx +++ /dev/null @@ -1,125 +0,0 @@ -import {GET_USER} from "@/core/data/apiRoutes"; -import axios from "axios"; -import {createContext, useCallback, useEffect, useReducer} from "react"; - -const initialUser = { - isAuth: false, - userChangedLanguage: false, - token: null, - user: {}, -}; - -const reducer = (state, action) => { - switch (action.type) { - case "CLEAR_USER": - return {...state, user: {}}; - case "CHANGE_USER": - return {...state, user: action.user}; - case "CHANGE_USER_LANGUAGE": - return { - ...state, - user: {...state.user, user_language: action.language}, - }; - case "CHANGE_AUTH_STATE": - return {...state, isAuth: action.isAuth}; - case "CHANGE_LANGUAGE_STATE": - return {...state, userChangedLanguage: action.userChangedLanguage}; - case "CLEAR_TOKEN": - localStorage.removeItem("_token"); - return {...state, token: null}; - case "SET_TOKEN": - localStorage.setItem("_token", action.token); - return {...state, token: action.token}; - default: - return state; - } -}; - -export const UserContext = createContext(); - -export const UserProvider = ({children}) => { - const [state, dispatch] = useReducer(reducer, initialUser); - - const clearUser = useCallback(() => { - dispatch({type: "CLEAR_USER"}); - }, []); - - const changeUser = useCallback((user) => { - dispatch({type: "CHANGE_USER", user}); - }, []); - - const changeUserLanguage = useCallback(/* use in multi language app */); - - const changeAuthState = useCallback((isAuth) => { - dispatch({type: "CHANGE_AUTH_STATE", isAuth}); - }, []); - - const changeLanguageState = useCallback((userChangedLanguage) => { - dispatch({type: "CHANGE_LANGUAGE_STATE", userChangedLanguage}); - }, []); - - const clearToken = useCallback(() => { - dispatch({type: "CLEAR_TOKEN"}); - }, []); - - const setToken = useCallback((token) => { - dispatch({type: "SET_TOKEN", token}); - }, []); - - const getUser = useCallback( - (callback = () => { - }) => { - axios - .get(GET_USER, { - headers: {authorization: `Bearer ${state.token}`}, - }) - .then(({data}) => { - if (typeof callback === "function") callback(data.data); - }) - .catch(error => { - if (error.response.status === 401) clearToken() - }) - }, - [state.token] - ); - - useEffect(() => { - const localToken = localStorage.getItem("_token"); - if (localToken) dispatch({type: "SET_TOKEN", token: localToken}); - }, []); - - useEffect(() => { - if (!state.token) { - clearUser(); - changeAuthState(false); - changeLanguageState(false); - return; - } - getUser((data) => { - changeUser(data); - changeAuthState(true); - changeLanguageState(true); - }); - }, [state.token]); - - return ( - - {children} - - ); -}; diff --git a/src/lib/app/hooks/useDirection.jsx b/src/lib/app/hooks/useDirection.jsx deleted file mode 100644 index b2e01fe..0000000 --- a/src/lib/app/hooks/useDirection.jsx +++ /dev/null @@ -1,10 +0,0 @@ -import {useContext} from "react"; -import {LanguageContext} from "../contexts/language"; - -const useDirection = () => { - const {directionApp} = useContext(LanguageContext); - - return {directionApp}; -}; - -export default useDirection; diff --git a/src/lib/app/hooks/useLanguage.jsx b/src/lib/app/hooks/useLanguage.jsx deleted file mode 100644 index d663835..0000000 --- a/src/lib/app/hooks/useLanguage.jsx +++ /dev/null @@ -1,28 +0,0 @@ -import {useContext} from "react"; -import {LanguageContext} from "../contexts/language"; -import useUser from "./useUser"; - -const useLanguage = () => { - const {isAuth, changeUserLanguage} = useUser(); - const { - languageApp, - setLanguageApp, - languageIsReady, - setLanguageIsReady, - languageList, - } = useContext(LanguageContext); - - const changeLanguage = (lang) => { - if (lang == languageApp) return; - - setLanguageIsReady(false); - setLanguageApp(lang); - if (isAuth) { - changeUserLanguage(lang); - } - }; - - return {languageApp, changeLanguage, languageIsReady, languageList}; -}; - -export default useLanguage; diff --git a/src/lib/app/hooks/useLoading.jsx b/src/lib/app/hooks/useLoading.jsx deleted file mode 100644 index a691bd7..0000000 --- a/src/lib/app/hooks/useLoading.jsx +++ /dev/null @@ -1,10 +0,0 @@ -import {useContext} from "react"; -import {LoadingContext} from "../contexts/loading"; - -const useLoading = () => { - const {setLoadingPage} = useContext(LoadingContext); - - return {setLoadingPage}; -}; - -export default useLoading; diff --git a/src/lib/app/hooks/useNetwork.jsx b/src/lib/app/hooks/useNetwork.jsx deleted file mode 100644 index 9c436a5..0000000 --- a/src/lib/app/hooks/useNetwork.jsx +++ /dev/null @@ -1,34 +0,0 @@ -import {useEffect, useState} from "react"; - -function useNetwork() { - const [state, setState] = useState(() => { - return { - online: navigator.onLine, - }; - }); - useEffect(() => { - const handleOnline = () => { - setState((prevState) => ({ - ...prevState, - online: true, - })); - }; - const handleOffline = () => { - setState((prevState) => ({ - ...prevState, - online: false, - })); - }; - - window.addEventListener("online", handleOnline); - window.addEventListener("offline", handleOffline); - return () => { - window.removeEventListener("online", handleOnline); - window.removeEventListener("offline", handleOffline); - }; - }, []); - - return state; -} - -export default useNetwork; \ No newline at end of file diff --git a/src/lib/app/hooks/useNotification.jsx b/src/lib/app/hooks/useNotification.jsx deleted file mode 100644 index 6e80bca..0000000 --- a/src/lib/app/hooks/useNotification.jsx +++ /dev/null @@ -1,26 +0,0 @@ -import useSWR from 'swr' -import {GET_SIDEBAR_NOTIFICATION} from "@/core/data/apiRoutes"; -import useRequest from "@/lib/app/hooks/useRequest"; -const GLOBAL_HAS_VALUE = process.env.NEXT_PUBLIC_HAS_VALUE; -const hasPermissionsValue = GLOBAL_HAS_VALUE ? JSON.parse(GLOBAL_HAS_VALUE) : []; -const isNotificationEnabled = hasPermissionsValue.includes("NEXT_PUBLIC_HAS_NOTIFICATION"); - -const useNotification = () => { - - const requestServer = useRequest({auth: true, notification: false}) - - //swr config - const fetcher = (...args) => { - return requestServer(args, 'get').then((response) => { - return response.data.data; - }).catch(() => { - }) - }; - - const {data, mutate} = useSWR( isNotificationEnabled ? GET_SIDEBAR_NOTIFICATION : '', fetcher , {keepPreviousData:true}) - //swr config - - // render data - return {notification_count: data, update_notification: mutate} -} -export default useNotification \ No newline at end of file diff --git a/src/lib/app/hooks/usePermissions.jsx b/src/lib/app/hooks/usePermissions.jsx deleted file mode 100644 index 2e6fff5..0000000 --- a/src/lib/app/hooks/usePermissions.jsx +++ /dev/null @@ -1,26 +0,0 @@ -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, isLoading} = useSWR(GET_PERMISSIONS_LIST, fetcher, { - revalidateIfStale: false, - revalidateOnFocus: false, - revalidateOnReconnect: false, - keepPreviousData : true - }) - const permissions_list = data - //swr config - - // render data - return {permissions_list, isLoading} -} -export default usePermissions; \ No newline at end of file diff --git a/src/lib/app/hooks/useProvince.jsx b/src/lib/app/hooks/useProvince.jsx deleted file mode 100644 index 2bc8c6b..0000000 --- a/src/lib/app/hooks/useProvince.jsx +++ /dev/null @@ -1,30 +0,0 @@ -import {GET_PROVINCE_LIST} from "@/core/data/apiRoutes"; -import useRequest from "@/lib/app/hooks/useRequest"; -import useSWR from "swr"; - -const useProvince = () => { - const requestServer = useRequest({auth: true, notification: false}) - - //swr config - const fetcher = (...args) => { - return requestServer(args, 'get').then(({data}) => { - return data.data; - }).catch(() => { - }) - }; - - const {data, isLoading} = useSWR(GET_PROVINCE_LIST, fetcher, { - revalidateIfStale: false, - revalidateOnFocus: false, - revalidateOnReconnect: false, - keepPreviousData: true - }); - - return { - provinceList: data, - isLoadingProvinceList: isLoading, - errorProvinceList: !data - } -}; - -export default useProvince; \ No newline at end of file diff --git a/src/lib/app/hooks/useRequest.jsx b/src/lib/app/hooks/useRequest.jsx deleted file mode 100644 index 4c3be1d..0000000 --- a/src/lib/app/hooks/useRequest.jsx +++ /dev/null @@ -1,71 +0,0 @@ -import axios from "axios"; -import {successRequest} from "@/core/utils/succesHandler"; -import {useTranslations} from "next-intl"; -import useUser from "@/lib/app/hooks/useUser"; -import {errorRequest, errorResponse, errorSetting} from "@/core/utils/errorHandler"; -import useNetwork from "@/lib/app/hooks/useNetwork"; -import Notifications from "@/core/components/notifications"; -import useToast from "@/lib/app/hooks/useToast"; - -const defaultOptions = { - auth: false, data: {}, requestOptions: { - headers: {} - }, notification: true, pending: true, success: { - notification: { - show: true, - }, - }, failed: { - notification: { - show: true, - }, - }, -} -const useRequest = (initOptions) => { - const network = useNetwork() - const t = useTranslations() - const {token, clearToken} = useUser() - const {pushToastList, dismissToastList} = useToast(); - let _options = {...defaultOptions, ...initOptions} - - function requestServer(url = '', method = 'get', options) { - _options = {..._options, ...options} - if (_options.auth) _options = { - ..._options, requestOptions: { - ..._options.requestOptions, - headers: {..._options.requestOptions.headers, authorization: `Bearer ${token}`} - } - } - return new Promise((resolve, reject) => { - if (!network.online) { - reject() - return - } - if (_options.notification && _options.failed.notification.show && _options.pending) { - dismissToastList(["pending", "warning", "error", "success"]); - Notifications(pushToastList, "pending", t); - } - - axios({ - url: url, method: method, data: _options.data, ..._options.requestOptions - }) - .then(response => { - successRequest(pushToastList, dismissToastList, response, t, _options) - resolve(response) - }) - .catch(error => { - if (error.response) { - errorResponse(pushToastList, dismissToastList, error.response, clearToken, t, _options.notification && _options.failed.notification.show) - } else if (error.request) { - errorRequest(dismissToastList, t, _options.notification && _options.failed.notification.show) - } else { - errorSetting(dismissToastList, t, _options.notification && _options.failed.notification.show) - } - reject(error) - }) - }); - } - - return requestServer -} - -export default useRequest \ No newline at end of file diff --git a/src/lib/app/hooks/useRole.jsx b/src/lib/app/hooks/useRole.jsx deleted file mode 100644 index b4c231c..0000000 --- a/src/lib/app/hooks/useRole.jsx +++ /dev/null @@ -1,30 +0,0 @@ -import {GET_ROLE_LIST} from "@/core/data/apiRoutes"; -import useRequest from "@/lib/app/hooks/useRequest"; -import useSWR from "swr"; - -const useRole = () => { - const requestServer = useRequest({auth: true, notification: false}) - - //swr config - const fetcher = (...args) => { - return requestServer(args, 'get').then(({data}) => { - return data.data; - }).catch(() => { - }) - }; - - const {data, isLoading} = useSWR(GET_ROLE_LIST, fetcher, { - revalidateIfStale: false, - revalidateOnFocus: false, - revalidateOnReconnect: false, - keepPreviousData: true - }); - - return { - roleList: data, - isLoadingRoleList: isLoading, - errorRoleList: !data - } -}; - -export default useRole; \ No newline at end of file diff --git a/src/lib/app/hooks/useSocket.jsx b/src/lib/app/hooks/useSocket.jsx deleted file mode 100644 index 3b9855d..0000000 --- a/src/lib/app/hooks/useSocket.jsx +++ /dev/null @@ -1,10 +0,0 @@ -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/app/hooks/useToast.jsx b/src/lib/app/hooks/useToast.jsx deleted file mode 100644 index 5e1d5b4..0000000 --- a/src/lib/app/hooks/useToast.jsx +++ /dev/null @@ -1,21 +0,0 @@ -import {useContext} from "react"; -import {ToastContext} from "@/lib/app/contexts/toast"; - -const useToast = () => { - const {dispatch} = useContext(ToastContext); - - const pushToastList = (toast_type, toast_id) => { - dispatch({type: "PUSH", toast_type, toast_id}); - }; - - const dismissToastList = (toast_type) => { - dispatch({type: "DISMISS", toast_type}); - }; - - return { - pushToastList, - dismissToastList - } -}; - -export default useToast; \ No newline at end of file diff --git a/src/lib/app/hooks/useUser.jsx b/src/lib/app/hooks/useUser.jsx deleted file mode 100644 index b0bbc1a..0000000 --- a/src/lib/app/hooks/useUser.jsx +++ /dev/null @@ -1,34 +0,0 @@ -import {useContext} from "react"; -import {UserContext} from "../contexts/user"; - -const useUser = () => { - const { - isAuth, - userChangedLanguage, - token, - user, - getUser, - clearUser, - changeUser, - changeAuthState, - changeLanguageState, - clearToken, - setToken, - } = useContext(UserContext); - - return { - isAuth, - userChangedLanguage, - token, - user, - getUser, - clearUser, - changeUser, - changeAuthState, - changeLanguageState, - clearToken, - setToken, - }; -}; - -export default useUser; diff --git a/src/lib/callWidget/contexts/answers.jsx b/src/lib/callWidget/contexts/answers.jsx deleted file mode 100644 index ac6d4cc..0000000 --- a/src/lib/callWidget/contexts/answers.jsx +++ /dev/null @@ -1,87 +0,0 @@ -import {createContext, useEffect, useReducer, useState} from "react"; -import {GET_CATEGORY} from "@/core/data/apiRoutes"; -import useRequest from "@/lib/app/hooks/useRequest"; - -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]; - case "CHANGE_ACTIVE_ID": - return state.map((answer, index) => - action.tab_id === answer.id - ? { - ...answer, - active_category_id: action.category_id, - } - : {...answer} - ); - default: - return state; - } -}; - -const submission = (handleCategories, operation) => { - switch (operation.type) { - case "SET_CATEGORIES": - return {...handleCategories, categoryLists: operation.categoriesByAPI}; - case "SET_SUBCATEGORIES": - return {...handleCategories, subCategoryLists: operation.subCategoriesByAPI}; - default: - return handleCategories; - } -}; - -export const AnswersContext = createContext(); -export const AnswersProvider = ({children}) => { - const requestServer = useRequest({auth: true, notification: false}); - const [openCallDialog, setOpenCallDialog] = useState(false); - const [activeTab, setActiveTab] = useState(0); - const [handleCategories, operation] = useReducer(submission, { - categoryLists: [], - subCategoryLists: [], - }); - const [state, dispatch] = useReducer(reducer, []); - - useEffect(() => { - requestServer(GET_CATEGORY, "get") - .then(({data}) => { - const subCategoriesByAPI = data.data; - const categoriesByAPI = subCategoriesByAPI.reduce((accumulator, currentValue) => { - if (!accumulator.find((item) => item.category_id === currentValue.category_id)) { - accumulator.push(currentValue); - } - return accumulator; - }, []); - operation({type: "SET_CATEGORIES", categoriesByAPI}); - operation({type: "SET_SUBCATEGORIES", subCategoriesByAPI}); - }) - .catch(() => { - }); - }, []); - - return ( - - {children} - - ); -}; diff --git a/src/lib/callWidget/contexts/categories.jsx b/src/lib/callWidget/contexts/categories.jsx deleted file mode 100644 index 533dfc3..0000000 --- a/src/lib/callWidget/contexts/categories.jsx +++ /dev/null @@ -1,59 +0,0 @@ -import { createContext, useCallback, useEffect, useReducer } from "react"; -import useRequest from "@/lib/app/hooks/useRequest"; -import { GET_CATEGORY } from "@/core/data/apiRoutes"; - -const reducer = (state, action) => { - switch (action.type) { - case "CHANGE_ACTIVE_ID": - return { ...state, active_id: action.id }; - case "SET_CATEGORIES": - return { ...state, categoryLists: action.cat }; - case "SET_SUBCATEGORIES": - return { ...state, subCategoryLists: action.subCat }; - default: - return state; - } -}; -export const CategoriesContext = createContext(); - -export const CategoriesProvider = ({ children }) => { - const requestServer = useRequest({ auth: true, notification: false }); - const [state, dispatch] = useReducer(reducer, { - categoryLists: [], - subCategoryLists: [], - active_id: null, - }); - - useEffect(() => { - requestServer(GET_CATEGORY, "get") - .then(({ data }) => { - const subCat = data.data; - const cat = subCat.reduce((accumulator, currentValue) => { - if (!accumulator.find((item) => item.category_id === currentValue.category_id)) { - accumulator.push(currentValue); - } - return accumulator; - }, []); - dispatch({ type: "SET_CATEGORIES", cat }); - dispatch({ type: "SET_SUBCATEGORIES", subCat }); - }) - .catch(() => {}); - }, []); - - const setCategoryID = useCallback((id) => { - dispatch({ type: "CHANGE_ACTIVE_ID", id }); - }); - - return ( - - {children} - - ); -}; diff --git a/src/lib/callWidget/hooks/useAnswers.jsx b/src/lib/callWidget/hooks/useAnswers.jsx deleted file mode 100644 index 149b1d3..0000000 --- a/src/lib/callWidget/hooks/useAnswers.jsx +++ /dev/null @@ -1,46 +0,0 @@ -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, - date: new Date(), - active: true, - active_category_id: null, - }; - 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; diff --git a/src/lib/callWidget/hooks/useCallDialog.jsx b/src/lib/callWidget/hooks/useCallDialog.jsx deleted file mode 100644 index 30dd599..0000000 --- a/src/lib/callWidget/hooks/useCallDialog.jsx +++ /dev/null @@ -1,10 +0,0 @@ -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/lib/callWidget/hooks/useCallerHistory.jsx b/src/lib/callWidget/hooks/useCallerHistory.jsx deleted file mode 100644 index 7fe1100..0000000 --- a/src/lib/callWidget/hooks/useCallerHistory.jsx +++ /dev/null @@ -1,37 +0,0 @@ -import useRequest from "@/lib/app/hooks/useRequest"; -import {useEffect, useState} from "react"; -import useSWR from "swr"; -import {GET_CALLER_HISTORY} from "@/core/data/apiRoutes"; - -const useCallerHistory = (call_id, phone_number, max_size) => { - const requestServer = useRequest({auth: true, notification: false}); - const [validData, setValidData] = useState([]); - - //swr config - const fetcher = (...args) => { - return requestServer(args, 'get').then(({data}) => { - return data.data; - }).catch(() => { - }) - }; - - const {data, isLoading} = useSWR(`${GET_CALLER_HISTORY}?phone_number=${phone_number}&size=${max_size}`, fetcher, { - revalidateIfStale: true, - revalidateOnFocus: false, - revalidateOnReconnect: false, - keepPreviousData: true - }); - - useEffect(() => { - data && setValidData(data.filter((item) => item.id != call_id)); - }, [data, call_id]); - - return { - firstItemOfHistory: validData.length === 0 ? false : validData[0], - historyList: validData.length < 2 ? false : validData.slice(1), - isLoadingHistoryList: isLoading, - errorHistoryList: !data - } -}; - -export default useCallerHistory; \ No newline at end of file diff --git a/src/lib/callWidget/hooks/useCategories.jsx b/src/lib/callWidget/hooks/useCategories.jsx deleted file mode 100644 index 423bd67..0000000 --- a/src/lib/callWidget/hooks/useCategories.jsx +++ /dev/null @@ -1,17 +0,0 @@ -import { useContext } from "react"; -import { AnswersContext } from "@/lib/callWidget/contexts/answers"; - -const useCategories = () => { - const { categoryLists, subCategoryLists, active_category_id, dispatch } = useContext(AnswersContext); - - const setActiveCategoryID = (tab_id, category_id) => { - dispatch({ type: "CHANGE_ACTIVE_ID", tab_id, category_id }); - }; - return { - categoryLists, - subCategoryLists, - active_category_id, - setActiveCategoryID, - }; -}; -export default useCategories; diff --git a/src/lib/contexts/auth.js b/src/lib/contexts/auth.js new file mode 100644 index 0000000..e56dd22 --- /dev/null +++ b/src/lib/contexts/auth.js @@ -0,0 +1,109 @@ +"use client"; +import { GET_USER_ROUTE } from "@/core/utils/routes"; +import axios from "axios"; +import { + createContext, + useCallback, + useContext, + useEffect, + useReducer, + useState, +} from "react"; + +const initAuth = { + initAuthState: false, + isAuth: false, + user: {}, +}; + +const authReducer = (state, action) => { + switch (action.type) { + case "CLEAR_USER": + return { ...state, user: {} }; + case "CHANGE_USER": + return { ...state, user: action.user }; + case "CHANGE_AUTH_STATE": + return { ...state, isAuth: action.isAuth }; + case "CHANGE_INIT_AUTH": + return { ...state, initAuthState: action.initAuthState }; + default: + return state; + } +}; + +const AuthContext = createContext(); + +export const AuthProvider = ({ children }) => { + const [errorState, setErrorState] = useState({ status: null, message: "" }); + const [state, dispatch] = useReducer(authReducer, initAuth); + + const clearUser = useCallback(() => { + dispatch({ type: "CLEAR_USER" }); + }, []); + + const changeUser = useCallback((user) => { + dispatch({ type: "CHANGE_USER", user }); + }, []); + + const changeAuthState = useCallback((isAuth) => { + dispatch({ type: "CHANGE_AUTH_STATE", isAuth }); + }, []); + + const changeInitAuth = useCallback((initAuthState) => { + dispatch({ type: "CHANGE_INIT_AUTH", initAuthState }); + }, []); + + const logout = () => { + clearUser(); + changeAuthState(false); + changeInitAuth(true); + }; + + const getUser = useCallback(async () => { + try { + const { data } = await axios.get(GET_USER_ROUTE, { + withCredentials: true, + }); + changeUser(data.data); + changeAuthState(true); + changeInitAuth(true); + setErrorState(false); + } catch (error) { + if (error.response && error.response.status === 401) { + clearUser(); + changeAuthState(false); + changeInitAuth(true); + return; + } + let status = + error.response && error.response.status + ? error.response.status + : "نامشخص"; + setErrorState({ + status, + message: " مشکلی در احراز هویت رخ داده است . دقایقی بعد امتحان کنید!!!", + }); + } + }, [clearUser, changeUser, changeAuthState, changeInitAuth]); + + useEffect(() => { + getUser(); + }, []); + + return ( + + {children} + + ); +}; + +export const useAuth = () => useContext(AuthContext); diff --git a/src/lib/contexts/call.js b/src/lib/contexts/call.js new file mode 100644 index 0000000..df634b2 --- /dev/null +++ b/src/lib/contexts/call.js @@ -0,0 +1,106 @@ +"use client"; +import { + createContext, + useReducer, + useState, + useCallback, + useMemo, + useContext, +} from "react"; + +const reducer = (state, action) => { + switch (action.type) { + case "CHANGE_ACTIVE_TAB": + return state.map((call, index) => ({ + ...call, + active: index === action.index_call, + })); + case "CHANGE_LIST": + return [...action.new_list]; + case "CHANGE_ACTIVE_CATEGORY_ID": + return state.map((call) => + call.id === action.tab_id + ? { ...call, active_category_id: action.category_id } + : call, + ); + default: + return state; + } +}; + +export const CallContext = createContext(null); + +export const CallProvider = ({ children }) => { + const [openCallDialog, setOpenCallDialog] = useState(false); + const [activeCall, setActiveCall] = useState(0); + const [calls, dispatch] = useReducer(reducer, []); + + const changeActiveTab = useCallback((index_call) => { + dispatch({ type: "CHANGE_ACTIVE_TAB", index_call }); + }, []); + + const changeList = useCallback((new_list) => { + dispatch({ type: "CHANGE_LIST", new_list }); + }, []); + + const changeActiveCategoryId = useCallback((tab_id, category_id) => { + dispatch({ type: "CHANGE_ACTIVE_CATEGORY_ID", tab_id, category_id }); + }, []); + + const changeActiveTabCall = (newValue) => { + changeActiveTab(newValue); + setActiveCall(newValue); + }; + + const newCall = useCallback( + (data) => { + const newCall = { + id: data.id, + phone_number: data.phone_number, + date: new Date(), + active: true, + active_category_id: null, + }; + const newList = [...calls, newCall]; + changeList(newList); + changeActiveTabCall(newList.length - 1); + }, + [calls], + ); + + const closeCall = useCallback( + (id) => { + const index = calls.findIndex((answer) => answer.id === id); + const newIndex = index === 0 ? 0 : index - 1; + const newList = [...calls]; + newList.splice(index, 1); + if (newList.length !== 0) { + newList[newIndex].active = true; + changeActiveTabCall(newIndex); + } + changeList(newList); + }, + [calls], + ); + + const contextValue = useMemo( + () => ({ + openCallDialog, + setOpenCallDialog, + calls, + newCall, + closeCall, + activeCall, + setActiveCall, + changeActiveCategoryId, + changeActiveTabCall, + }), + [openCallDialog, calls, activeCall], + ); + + return ( + {children} + ); +}; + +export const useCall = () => useContext(CallContext); diff --git a/src/lib/contexts/category.js b/src/lib/contexts/category.js new file mode 100644 index 0000000..79e8360 --- /dev/null +++ b/src/lib/contexts/category.js @@ -0,0 +1,72 @@ +import { GET_CATEGORY } from "@/core/utils/routes"; +import { + createContext, + useCallback, + useContext, + useEffect, + useMemo, + useReducer, +} from "react"; +import useRequest from "../hooks/useRequest"; + +const initialState = { + categoryLists: [], + subCategoryLists: [], + active_category_id: null, +}; + +const reducer = (state, action) => { + switch (action.type) { + case "SET_CATEGORIES": + return { ...state, categoryLists: action.payload }; + case "SET_SUBCATEGORIES": + return { ...state, subCategoryLists: action.payload }; + default: + return state; + } +}; + +export const CategoriesContext = createContext(null); + +export const CategoriesProvider = ({ children }) => { + const requestServer = useRequest({ notificationShow: false }); + + const [state, dispatch] = useReducer(reducer, initialState); + + useEffect(() => { + const fetchCategories = async () => { + try { + const { data } = await requestServer(GET_CATEGORY, "get"); + const subCategories = data.data; + + const categories = Array.from( + new Map( + subCategories.map((item) => [item.category_id, item]), + ).values(), + ); + + dispatch({ type: "SET_CATEGORIES", payload: categories }); + dispatch({ type: "SET_SUBCATEGORIES", payload: subCategories }); + } catch (error) {} + }; + + fetchCategories(); + }, []); + + const contextValue = useMemo( + () => ({ + categoryLists: state.categoryLists, + subCategoryLists: state.subCategoryLists, + active_category_id: state.active_category_id, + }), + [state], + ); + + return ( + + {children} + + ); +}; + +export const useCategory = () => useContext(CategoriesContext); diff --git a/src/lib/contexts/socket.js b/src/lib/contexts/socket.js new file mode 100644 index 0000000..15a4e31 --- /dev/null +++ b/src/lib/contexts/socket.js @@ -0,0 +1,104 @@ +"use client"; +import { Power, PowerOff } from "@mui/icons-material"; +import { + createContext, + useContext, + useEffect, + useMemo, + useRef, + useState, + useCallback, +} from "react"; +import { toast } from "react-toastify"; +import { io } from "socket.io-client"; +import { useAuth } from "./auth"; + +const SocketContext = createContext({ + socket: null, + status: "", +}); + +export const useSocket = () => useContext(SocketContext); + +export const SocketProvider = ({ children }) => { + const { user } = useAuth(); + const socketToastId = useRef(null); + const [status, setStatus] = useState(""); + + const socket = useMemo(() => { + return io(`${process.env.NEXT_PUBLIC_SERVER_SOCKET_URL}`, { + path: "/notification/socket.io", + autoConnect: false, + auth: { token: null }, + }); + }, []); + + useEffect(() => { + if (user?.telephone_id) { + socket.auth.token = user.telephone_id; + } + }, [user, socket]); + + const handleConnect = useCallback(() => setStatus("connected"), []); + const handleDisconnect = useCallback(() => setStatus("disconnected"), []); + const handleConnectError = useCallback(() => setStatus("error"), []); + + useEffect(() => { + socket.on("connect", handleConnect); + socket.on("disconnect", handleDisconnect); + socket.on("connect_error", handleConnectError); + + return () => { + socket.off("connect", handleConnect); + socket.off("disconnect", handleDisconnect); + socket.off("connect_error", handleConnectError); + }; + }, [socket, handleConnect, handleDisconnect, handleConnectError]); + + useEffect(() => { + if (status === "connected") { + toast.update(socketToastId.current, { + containerId: "socket_connection", + type: "success", + render: "ارتباط با سرور برقرار شد", + autoClose: 2000, + closeButton: true, + closeOnClick: true, + icon: ({ theme, type }) => , + }); + } else if (status === "error" || status === "disconnected") { + if (socketToastId.current) { + toast.update(socketToastId.current, { + containerId: "socket_connection", + type: "warning", + render: "ارتباط با سرور قطع شد. در حال تلاش برای بازیابی…", + autoClose: false, + closeButton: false, + draggable: false, + icon: ({ theme, type }) => , + }); + } else { + socketToastId.current = toast.warn( + "ارتباط با سرور قطع شد. در حال تلاش برای بازیابی…", + { + containerId: "socket_connection", + autoClose: false, + closeButton: false, + draggable: false, + icon: ({ theme, type }) => , + }, + ); + } + } + }, [status]); + + const contextValue = useMemo(() => ({ socket, status }), [socket, status]); + + return ( + + {children} + + ); +}; + +export default SocketContext; diff --git a/src/lib/contexts/tableSetting.js b/src/lib/contexts/tableSetting.js new file mode 100644 index 0000000..6009f88 --- /dev/null +++ b/src/lib/contexts/tableSetting.js @@ -0,0 +1,189 @@ +"use client"; + +import { createContext, useCallback, useEffect, useState } from "react"; +import LZString from "lz-string"; + +export const TableSettingContext = createContext(); + +const decompressData = (compressedData) => { + try { + const decompressed = LZString.decompressFromUTF16(compressedData); + if (decompressed === null) { + localStorage.removeItem("_setting-app"); + return null; + } else { + return JSON.parse(decompressed); + } + } catch (error) { + localStorage.removeItem("_setting-app"); + return null; + } +}; + +export const TableSettingProvider = ({ children }) => { + const [settingStore, setSettingStore] = useState({}); + + useEffect(() => { + const compressedData = localStorage.getItem("_setting-app"); + + const data = compressedData ? decompressData(compressedData) : null; + if (data) { + setSettingStore(data); + } + }, []); + + const hideAction = useCallback( + (user_id, page_name, table_name, settingValue) => { + clearAction(user_id, page_name, table_name, "hides"); + updateSettingStorage( + user_id, + page_name, + table_name, + "hides", + settingValue, + ); + }, + [], + ); + + const sortAction = useCallback( + (user_id, page_name, table_name, settingValue) => { + updateSettingStorage( + user_id, + page_name, + table_name, + "sorts", + settingValue, + ); + }, + [], + ); + + const filterAction = (user_id, page_name, table_name, settingValue) => { + updateSettingStorage( + user_id, + page_name, + table_name, + "filters", + settingValue, + ); + }; + + const resetAction = (user_id, page_name, table_name) => { + const compressedData = localStorage.getItem("_setting-app"); + const prevLocalStorage = compressedData + ? decompressData(compressedData) + : {}; + + const userSettings = prevLocalStorage[user_id] || {}; + const pageSettings = userSettings[page_name] || {}; + delete pageSettings[table_name]; + + const resetData = { + ...prevLocalStorage, + [user_id]: { + ...userSettings, + [page_name]: { + ...pageSettings, + }, + }, + }; + + localStorage.setItem( + "_setting-app", + LZString.compressToUTF16(JSON.stringify(resetData)), + ); + setSettingStore(resetData); + }; + + const updateSettingStorage = ( + user_id, + page_name, + table_name, + settingType, + settingValue, + ) => { + const compressedData = localStorage.getItem("_setting-app"); + const prevLocalStorage = compressedData + ? decompressData(compressedData) + : {}; + + const userSettings = prevLocalStorage[user_id] || {}; + const pageSettings = userSettings[page_name] || {}; + const tableSettings = pageSettings[table_name] || {}; + + let newSettings; + if (settingType === "sorts" || settingType === "filters") { + newSettings = [...settingValue]; + } else { + newSettings = { ...(tableSettings[settingType] || {}), ...settingValue }; + } + + const updatedLocalStorage = { + ...prevLocalStorage, + [user_id]: { + ...userSettings, + [page_name]: { + ...pageSettings, + [table_name]: { + ...tableSettings, + [settingType]: Array.isArray(settingValue) + ? [...newSettings] + : { ...newSettings }, + }, + }, + }, + }; + + localStorage.setItem( + "_setting-app", + LZString.compressToUTF16(JSON.stringify(updatedLocalStorage)), + ); + setSettingStore(updatedLocalStorage); + }; + + const clearAction = (user_id, page_name, table_name, key) => { + const compressedData = localStorage.getItem("_setting-app"); + const prevLocalStorage = compressedData + ? decompressData(compressedData) + : {}; + + const userSettings = prevLocalStorage[user_id] || {}; + const pageSettings = userSettings[page_name] || {}; + const tableSettings = pageSettings[table_name] || {}; + + const updatedSettings = { + ...prevLocalStorage, + [user_id]: { + ...userSettings, + [page_name]: { + ...pageSettings, + [table_name]: { + ...tableSettings, + [key]: Array.isArray(tableSettings[key]) ? [] : {}, + }, + }, + }, + }; + + localStorage.setItem( + "_setting-app", + LZString.compressToUTF16(JSON.stringify(updatedSettings)), + ); + setSettingStore(updatedSettings); + }; + + return ( + + {children} + + ); +}; diff --git a/src/lib/hooks/useCallerHistory.js b/src/lib/hooks/useCallerHistory.js new file mode 100644 index 0000000..90857ba --- /dev/null +++ b/src/lib/hooks/useCallerHistory.js @@ -0,0 +1,50 @@ +import { useEffect, useState } from "react"; +import useRequest from "./useRequest"; +import { GET_CALLER_HISTORY } from "@/core/utils/routes"; + +const useCallerHistory = (callId, phoneNumber, maxSize) => { + const requestServer = useRequest(); + + const [historyData, setHistoryData] = useState([]); + const [isLoading, setIsLoading] = useState(false); + const [hasError, setHasError] = useState(false); + + useEffect(() => { + const fetchHistory = async () => { + if (!phoneNumber || !maxSize) return; + + setIsLoading(true); + setHasError(false); + + try { + const url = `${GET_CALLER_HISTORY}?phone_number=${phoneNumber}&size=${maxSize}`; + const { data } = await requestServer(url, "get"); + const items = data?.data || []; + + const filtered = items.filter((item) => item.id !== callId); + const unique = Array.from( + new Map(filtered.map((item) => [item.id, item])).values(), + ); + + setHistoryData(unique); + } catch (error) { + console.error("Error fetching caller history:", error); + setHasError(true); + setHistoryData([]); + } finally { + setIsLoading(false); + } + }; + + fetchHistory(); + }, [callId, phoneNumber, maxSize]); + + return { + firstItemOfHistory: historyData[0] || false, + historyList: historyData.length > 1 ? historyData.slice(1) : false, + isLoadingHistoryList: isLoading, + errorHistoryList: hasError, + }; +}; + +export default useCallerHistory; diff --git a/src/lib/hooks/usePermissions.js b/src/lib/hooks/usePermissions.js new file mode 100644 index 0000000..b1f2856 --- /dev/null +++ b/src/lib/hooks/usePermissions.js @@ -0,0 +1,21 @@ +import { GET_PERMISSIONS_ROUTE } from "@/core/utils/routes"; +import useSWR from "swr"; +import useRequest from "./useRequest"; + +export const usePermissions = () => { + const request = useRequest(); + + const fetcher = async (url) => { + try { + const response = await request(url); + return response.data.data; + } catch (error) { + throw new Error(); + } + }; + + return useSWR(GET_PERMISSIONS_ROUTE, fetcher, { + keepPreviousData: true, + dedupingInterval: 30000, + }); +}; diff --git a/src/lib/hooks/useRequest.js b/src/lib/hooks/useRequest.js new file mode 100644 index 0000000..9f4f2ff --- /dev/null +++ b/src/lib/hooks/useRequest.js @@ -0,0 +1,66 @@ +import { errorResponse } from "@/core/utils/errorResponse"; +import { successRequest } from "@/core/utils/successRequest"; +import axios from "axios"; +import { useAuth } from "../contexts/auth"; +import { useSidebarBadge } from "./useSidebarBadge"; + +const defaultOptions = { + data: {}, + requestOptions: { + headers: {}, + }, + notificationShow: true, + notificationSuccess: false, + notificationFailed: true, + hasSidebarUpdate: false, +}; + +const useRequest = (initOptions) => { + const _options = Object.assign({}, defaultOptions, initOptions); + + const { mutate: sidebarUpdate } = useSidebarBadge({ + enable: _options.hasSidebarUpdate, + }); + const { logout } = useAuth(); + + /** + * Performs an HTTP request. + * @param {string} url - The URL to send the request to. + * @param {'get' | 'post' | 'put' | 'delete'} [method='get'] - HTTP method. + * @param {object} [options] - Additional request options. + * @returns {Promise} - Axios response. + */ + return async (url = "", method = "get", options) => { + const mergedOptions = Object.assign({}, _options, options); + try { + const response = await axios({ + url, + method, + data: method === "get" ? null : mergedOptions.data, + withCredentials: true, + ...mergedOptions.requestOptions, + }); + if (mergedOptions.hasSidebarUpdate) { + if (method === "post" || method === "put" || method === "delete") + sidebarUpdate(); + } + successRequest( + mergedOptions.notificationShow && mergedOptions.notificationSuccess, + "request_data", + ); + return response; + } catch (error) { + if (error.response) { + errorResponse( + error.response, + mergedOptions.notificationShow && mergedOptions.notificationFailed, + "request_data", + logout, + ); + } + throw error; + } + }; +}; + +export default useRequest; diff --git a/src/lib/hooks/useSidebarBadge.js b/src/lib/hooks/useSidebarBadge.js new file mode 100644 index 0000000..10e7461 --- /dev/null +++ b/src/lib/hooks/useSidebarBadge.js @@ -0,0 +1,23 @@ +import { GET_SIDEBAR_BADGE_ROUTE } from "@/core/utils/routes"; +import axios from "axios"; +import useSWR from "swr"; + +export const useSidebarBadge = ({ enable = true }) => { + const fetcher = async (url) => { + try { + const response = await axios({ + url, + method: "get", + withCredentials: true, + }); + return response.data.data; + } catch (error) { + throw new Error(); + } + }; + + return useSWR(enable ? GET_SIDEBAR_BADGE_ROUTE : "", fetcher, { + keepPreviousData: true, + dedupingInterval: 30000, + }); +}; diff --git a/src/middlewares/AppPermission.jsx b/src/middlewares/AppPermission.jsx deleted file mode 100644 index 8f93bd3..0000000 --- a/src/middlewares/AppPermission.jsx +++ /dev/null @@ -1,8 +0,0 @@ -const GLOBAL_HAS_VALUE = process.env.NEXT_PUBLIC_HAS_VALUE; -const hasPermissionsValue = GLOBAL_HAS_VALUE ? JSON.parse(GLOBAL_HAS_VALUE) : []; - -const AppPermission = ({ children , permissions }) => { - return permissions.some(permission => hasPermissionsValue.includes(permission)) ? <>{children} : null; -}; - -export default AppPermission; diff --git a/src/middlewares/RolePermission.jsx b/src/middlewares/RolePermission.jsx deleted file mode 100644 index c546220..0000000 --- a/src/middlewares/RolePermission.jsx +++ /dev/null @@ -1,14 +0,0 @@ -import useUser from "@/lib/app/hooks/useUser"; -import RolePermissionComponent from "@/core/components/Middleware/RolePermissionComponent"; - -const RolePermissionMiddleware = ({children, requiredPermissions = []}) => { - const {user} = useUser(); - - const hasPermission = requiredPermissions.length === 0 ? true : requiredPermissions.some((permission) => - user?.permissions?.includes(permission) - ); - - return !hasPermission ? : <>{children}; -}; - -export default RolePermissionMiddleware; diff --git a/src/middlewares/WithAuth.jsx b/src/middlewares/WithAuth.jsx deleted file mode 100644 index d3b0493..0000000 --- a/src/middlewares/WithAuth.jsx +++ /dev/null @@ -1,11 +0,0 @@ -import useUser from "@/lib/app/hooks/useUser"; -import WithAuthComponent from "@/core/components/Middleware/WithAuthComponent"; - - -const WithAuthMiddleware = ({children}) => { - const {isAuth} = useUser(); - - return isAuth ? <>{children} : ; -}; - -export default WithAuthMiddleware; diff --git a/src/middlewares/WithoutAuth.jsx b/src/middlewares/WithoutAuth.jsx deleted file mode 100644 index 7fbcba6..0000000 --- a/src/middlewares/WithoutAuth.jsx +++ /dev/null @@ -1,34 +0,0 @@ -import useUser from "@/lib/app/hooks/useUser"; -import {useRouter} from "next/router"; -import {useEffect} from "react"; -import WithoutAuthComponent from "@/core/components/Middleware/WithoutAuthComponent"; - -const WithoutAuthMiddleware = ({children}) => { - const {isAuth} = useUser(); - const router = useRouter(); - - const backUrlDecodedPath = router.query?.back_url; - - useEffect(() => { - if (!isAuth) return; - const timer = setTimeout(() => { - router.replace( - backUrlDecodedPath - ? decodeURIComponent(backUrlDecodedPath) - : "/dashboard" - ); - }, 2000); - - return () => { - clearTimeout(timer); - }; - }, [isAuth]); - - return isAuth ? ( - - ) : ( - <>{children} - ); -}; - -export default WithoutAuthMiddleware; diff --git a/src/middlewares/__test__/AppPermission.test.js b/src/middlewares/__test__/AppPermission.test.js deleted file mode 100644 index 82377ba..0000000 --- a/src/middlewares/__test__/AppPermission.test.js +++ /dev/null @@ -1,26 +0,0 @@ -import {render, screen, waitFor} from '@testing-library/react'; -import MockAppWithProviders from "../../../mocks/AppWithProvider"; -import AppPermission from "@/middlewares/AppPermission"; - -describe('AppPermission From middlewares', () => { - describe('Behavior', () => { - it('show related child if permission exist', async () => { - - const requiredPermissions = ["NEXT_PUBLIC_HAS_WIDGET"]; - render({'children'}); - - await waitFor(()=>{ - expect(screen.queryByText('children')).toBeInTheDocument(); - }) - }); - it('do not show related child if permission does not exist', async () => { - - const requiredPermissions = ["MISSING_PERMISSION"]; - render(); - - await waitFor(() => { - expect(screen.queryByText('children')).not.toBeInTheDocument(); - }); - }); - }); -}); diff --git a/src/middlewares/__test__/RolePermission.test.js b/src/middlewares/__test__/RolePermission.test.js deleted file mode 100644 index f37796f..0000000 --- a/src/middlewares/__test__/RolePermission.test.js +++ /dev/null @@ -1,34 +0,0 @@ -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(); - - }) - - - }); - }); -}); diff --git a/src/pages/403.jsx b/src/pages/403.jsx deleted file mode 100644 index 15ba915..0000000 --- a/src/pages/403.jsx +++ /dev/null @@ -1,24 +0,0 @@ -import {useEffect, useState} from "react"; -import {useRouter} from "next/router"; -import {NextIntlProvider} from "next-intl"; -import UnAuthorizedComponent from "@/components/errors/403"; - -export default function Custom404() { - const router = useRouter() - const [messages, setMessages] = useState(null) - useEffect(() => { - const fetch = async () => { - const tempMessages = (await import(`&/locales/${router.locale}/app.json`)).default - setMessages(tempMessages) - } - fetch() - }, []); - - if (!messages) return - - return ( - - - - ); -} diff --git a/src/pages/404.jsx b/src/pages/404.jsx deleted file mode 100644 index 26de038..0000000 --- a/src/pages/404.jsx +++ /dev/null @@ -1,25 +0,0 @@ -import {useEffect, useState} from "react"; -import {useRouter} from "next/router"; -import {NextIntlProvider} from "next-intl"; -import NotFoundComponent from "@/components/errors/404"; - -export default function Custom404() { - const router = useRouter() - const [messages, setMessages] = useState(null) - useEffect(() => { - - const fetch = async () => { - const tempMessages = (await import(`&/locales/${router.locale}/app.json`)).default - setMessages(tempMessages) - } - fetch() - }, []); - - if (!messages) return - - return ( - - - - ); -} diff --git a/src/pages/500.jsx b/src/pages/500.jsx deleted file mode 100644 index 4b85bc2..0000000 --- a/src/pages/500.jsx +++ /dev/null @@ -1,24 +0,0 @@ -import {useEffect, useState} from "react"; -import {useRouter} from "next/router"; -import {NextIntlProvider} from "next-intl"; -import ServerErrorComponent from "@/components/errors/500"; - -export default function Custom500() { - const router = useRouter() - const [messages, setMessages] = useState(null) - useEffect(() => { - const fetch = async () => { - const tempMessages = (await import(`&/locales/${router.locale}/app.json`)).default - setMessages(tempMessages) - } - fetch() - }, []); - - if (!messages) return - - return ( - - - - ); -} diff --git a/src/pages/_app.jsx b/src/pages/_app.jsx deleted file mode 100644 index c7b75d4..0000000 --- a/src/pages/_app.jsx +++ /dev/null @@ -1,40 +0,0 @@ -import "&/fontiran.css"; -import AppLayout from "@/layouts/AppLayout"; -import MuiLayout from "@/layouts/MuiLayout"; -import {LanguageProvider} from "@/lib/app/contexts/language"; -import {LoadingProvider} from "@/lib/app/contexts/loading"; -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"; -import {ToastProvider} from "@/lib/app/contexts/toast"; -import Layout from "@/layouts"; - -const App = ({Component, pageProps}) => { - - return (<> - - - - - {pageProps.messages ? : ''} - - - - - - - - - - - - - - - - ) -}; - -export default App; diff --git a/src/pages/_document.jsx b/src/pages/_document.jsx deleted file mode 100644 index 474e29b..0000000 --- a/src/pages/_document.jsx +++ /dev/null @@ -1,61 +0,0 @@ -import {createEmotionCacheRtl} from "@/core/utils/createEmotionCache"; -import theme from "@/core/utils/theme"; -import createEmotionServer from "@emotion/server/create-instance"; -import Document, {Head, Html, Main, NextScript} from "next/document"; - -export default function MyDocument(props) { - const {emotionStyleTags} = props; - - return ( - - - - - - - {emotionStyleTags} - - -
- - - - ); -} - -MyDocument.getInitialProps = async (ctx) => { - const originalRenderPage = ctx.renderPage; - let cache; - switch (ctx.locale) { - case "fa": - cache = createEmotionCacheRtl(); - break; - } - - const {extractCriticalToChunks} = createEmotionServer(cache); - - ctx.renderPage = () => - originalRenderPage({ - enhanceApp: (App) => - function EnhanceApp(props) { - return ; - }, - }); - - const initialProps = await Document.getInitialProps(ctx); - - const emotionStyles = extractCriticalToChunks(initialProps.html); - const emotionStyleTags = emotionStyles.styles.map((style) => ( -