From 3e3b04a7692d307840e6d2a4952e11d21dc090e4 Mon Sep 17 00:00:00 2001 From: Amin Ghasempoor Date: Thu, 26 Oct 2023 09:27:36 +0330 Subject: [PATCH 01/15] CFE-2 reinitializing call action tab --- mocks/handler.js | 16 +-- mocks/handlers/categories.js | 25 ++++ .../CallActionCategoriesButton.jsx | 21 ++++ .../CallActionCategoriesButton.test.js | 0 .../CallActionCategories/index.jsx | 15 +++ .../CallActionSubcategoriesButton.jsx | 33 +++++ .../CallActionSubcategoriesButton.test.js | 0 .../CallActionSubcategories/index.jsx | 15 +++ .../CallTabPanel/CallActions/index.jsx | 14 ++- src/core/data/apiRoutes.js | 24 ++-- src/lib/callWidget/contexts/answers.jsx | 114 ++++++++++++++---- src/lib/callWidget/contexts/categories.jsx | 59 +++++++++ src/lib/callWidget/hooks/useAnswers.jsx | 52 ++++---- src/lib/callWidget/hooks/useCategories.jsx | 17 +++ 14 files changed, 338 insertions(+), 67 deletions(-) create mode 100644 mocks/handlers/categories.js create mode 100644 src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallActions/CallActionCategories/CallActionCategoriesButton.jsx create mode 100644 src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallActions/CallActionCategories/__test__/CallActionCategoriesButton.test.js create mode 100644 src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallActions/CallActionCategories/index.jsx create mode 100644 src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallActions/CallActionSubcategories/CallActionSubcategoriesButton.jsx create mode 100644 src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallActions/CallActionSubcategories/__test__/CallActionSubcategoriesButton.test.js create mode 100644 src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallActions/CallActionSubcategories/index.jsx create mode 100644 src/lib/callWidget/contexts/categories.jsx create mode 100644 src/lib/callWidget/hooks/useCategories.jsx diff --git a/mocks/handler.js b/mocks/handler.js index fbb803a..232c91e 100644 --- a/mocks/handler.js +++ b/mocks/handler.js @@ -1,13 +1,15 @@ -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 { 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 -] \ No newline at end of file + ...expertsHandler, + ...categoriesHandler, +]; diff --git a/mocks/handlers/categories.js b/mocks/handlers/categories.js new file mode 100644 index 0000000..6864331 --- /dev/null +++ b/mocks/handlers/categories.js @@ -0,0 +1,25 @@ +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/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 new file mode 100644 index 0000000..d7c70f6 --- /dev/null +++ b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallActions/CallActionCategories/CallActionCategoriesButton.jsx @@ -0,0 +1,21 @@ +import { Button, Stack } 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 new file mode 100644 index 0000000..e69de29 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 new file mode 100644 index 0000000..c40d31c --- /dev/null +++ b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallActions/CallActionCategories/index.jsx @@ -0,0 +1,15 @@ +import { Stack } from "@mui/material"; +import useCategories from "@/lib/callWidget/hooks/useCategories"; +import CallActionCategoriesButton from "./CallActionCategoriesButton"; + +const CallActionsCategories = ({ tab }) => { + const { categoryLists } = useCategories(); + return ( + + {categoryLists.map((category) => { + return ; + })} + + ); +}; +export default CallActionsCategories; 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 new file mode 100644 index 0000000..4051511 --- /dev/null +++ b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallActions/CallActionSubcategories/CallActionSubcategoriesButton.jsx @@ -0,0 +1,33 @@ +import { Button, Stack } from "@mui/material"; +import useCategories from "@/lib/callWidget/hooks/useCategories"; +import useRequest from "@/lib/app/hooks/useRequest"; +import { CALL_ACTION } from "@/core/data/apiRoutes"; +import useAnswers from "@/lib/callWidget/hooks/useAnswers"; + +const CallActionSubcategoriesButton = ({ sub_category, tab }) => { + const requestServer = useRequest({ auth: true }); + const { closeAnswerTab } = useAnswers(); + const handleSubcategoryButton = () => { + requestServer(`${CALL_ACTION}/${tab.id}`, "post", { + data: { + category_id: sub_category.category_id, + subcategory_id: sub_category.subcategory_id, + }, + success: { + notification: { show: false }, + }, + }) + .then(() => { + closeAnswerTab(tab.id); + }) + .catch(() => {}); + }; + 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 new file mode 100644 index 0000000..e69de29 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 new file mode 100644 index 0000000..7e270bb --- /dev/null +++ b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallActions/CallActionSubcategories/index.jsx @@ -0,0 +1,15 @@ +import useCategories from "@/lib/callWidget/hooks/useCategories"; +import { Stack } from "@mui/material"; +import CallActionSubcategoriesButton from "@/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallActions/CallActionSubcategories/CallActionSubcategoriesButton"; + +const CallActionsSubcategories = ({ tab }) => { + const { subCategoryLists } = useCategories(); + 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 index d1a68f7..d713c76 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 @@ -1,7 +1,13 @@ -const CallActions = ({tab}) => { +import CallActionsCategories from "./CallActionCategories"; +import CallActionSubcategoriesButton from "./CallActionSubcategories/CallActionSubcategoriesButton"; + +function CallActions({ tab }) { return ( - <>CallActions - {tab.id} - ) + <> + + + + ); } -export default CallActions +export default CallActions; diff --git a/src/core/data/apiRoutes.js b/src/core/data/apiRoutes.js index 19534ca..7df6b2e 100644 --- a/src/core/data/apiRoutes.js +++ b/src/core/data/apiRoutes.js @@ -25,21 +25,16 @@ export const GET_USER = BASE_URL + "/api/profile/info"; //user data //sidebar notification -export const GET_SIDEBAR_NOTIFICATION = BASE_URL + "/dashboard/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_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" +export const GET_PERMISSIONS_LIST = BASE_URL + "/api/permissions/list"; //role management //expert management @@ -47,4 +42,9 @@ 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 \ No newline at end of file +//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/lib/callWidget/contexts/answers.jsx b/src/lib/callWidget/contexts/answers.jsx index b5c9c0b..4aa3c3c 100644 --- a/src/lib/callWidget/contexts/answers.jsx +++ b/src/lib/callWidget/contexts/answers.jsx @@ -1,31 +1,101 @@ -import {createContext, useReducer, useState} from "react"; +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 - }); + return state.map((answer, index) => + action.newValue === index + ? { ...answer, active: true } + : { + ...answer, + active: false, + } + ); case "CHANGE_LIST": - return [...action.newList] + 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; } -} -export const AnswersContext = createContext() -export const AnswersProvider = ({children}) => { - const [openCallDialog, setOpenCallDialog] = useState(false) - const [activeTab, setActiveTab] = useState(0) - const [state, dispatch] = useReducer(reducer, []); +}; - return {children} -} +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, [ + { + id: 1, + phone_number: "09134849737", + date: new Date(), + active: true, + active_category_id: 1, + }, + { + id: 2, + phone_number: "09134849737", + date: new Date(), + active: false, + active_category_id: 2, + }, + ]); + + 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 new file mode 100644 index 0000000..533dfc3 --- /dev/null +++ b/src/lib/callWidget/contexts/categories.jsx @@ -0,0 +1,59 @@ +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 index 68f8b46..149b1d3 100644 --- a/src/lib/callWidget/hooks/useAnswers.jsx +++ b/src/lib/callWidget/hooks/useAnswers.jsx @@ -1,38 +1,46 @@ -import {useContext} from "react"; -import {AnswersContext} from "@/lib/callWidget/contexts/answers"; +import { useContext } from "react"; +import { AnswersContext } from "@/lib/callWidget/contexts/answers"; const useAnswers = () => { - const {state, dispatch, activeTab, setActiveTab} = useContext(AnswersContext) + const { state, dispatch, activeTab, setActiveTab } = useContext(AnswersContext); const changeActiveTabAnswers = (newValue) => { - dispatch({type: 'CHANGE_ACTIVE_TAB', newValue}) - setActiveTab(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 - } - const newList = [...state, newAnswer] - dispatch({type: 'CHANGE_LIST', newList}) - changeActiveTabAnswers(newList.length - 1) - } + 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] + 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) + newList[newIndex].active = true; + setActiveTab(newIndex); } - dispatch({type: 'CHANGE_LIST', newList}) - } + dispatch({ type: "CHANGE_LIST", newList }); + }; - return {answersList: state, activeTab, setActiveTab, changeActiveTabAnswers, closeAnswerTab, newAnswerTab} -} + return { + answersList: state, + activeTab, + setActiveTab, + changeActiveTabAnswers, + closeAnswerTab, + newAnswerTab, + }; +}; -export default useAnswers \ No newline at end of file +export default useAnswers; diff --git a/src/lib/callWidget/hooks/useCategories.jsx b/src/lib/callWidget/hooks/useCategories.jsx new file mode 100644 index 0000000..423bd67 --- /dev/null +++ b/src/lib/callWidget/hooks/useCategories.jsx @@ -0,0 +1,17 @@ +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; From a2fa85ce6aa5b6e8be509608a3c0c4318a7aecf3 Mon Sep 17 00:00:00 2001 From: Amin Ghasempoor Date: Thu, 26 Oct 2023 09:54:23 +0330 Subject: [PATCH 02/15] CFE-2 show categories and sub categories --- .../CallActionSubcategories/CallActionSubcategoriesButton.jsx | 1 - .../CallTabs/CallTabPanel/CallActions/index.jsx | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) 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 index 4051511..381b56b 100644 --- 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 @@ -1,5 +1,4 @@ import { Button, Stack } from "@mui/material"; -import useCategories from "@/lib/callWidget/hooks/useCategories"; import useRequest from "@/lib/app/hooks/useRequest"; import { CALL_ACTION } from "@/core/data/apiRoutes"; import useAnswers from "@/lib/callWidget/hooks/useAnswers"; 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 d713c76..1243278 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 @@ -1,11 +1,11 @@ import CallActionsCategories from "./CallActionCategories"; -import CallActionSubcategoriesButton from "./CallActionSubcategories/CallActionSubcategoriesButton"; +import CallActionsSubcategories from "./CallActionSubcategories"; function CallActions({ tab }) { return ( <> - + ); } From c6586eb1f5ed7bc46c3e33e9f7ac322ff26d7981 Mon Sep 17 00:00:00 2001 From: Yasiu1376 Date: Sat, 28 Oct 2023 14:51:18 +0330 Subject: [PATCH 03/15] CFE-21 sidebar test --- example.env.local | 2 + mocks/handler.js | 6 +- .../Dashboard/Sidebar/SidebarDrawer.jsx | 2 +- .../layouts/Dashboard/Sidebar/SidebarList.jsx | 26 +--- .../Dashboard/Sidebar/SidebarListItem.jsx | 41 +++--- .../Dashboard/Sidebar/SidebarListSubItem.jsx | 32 ++--- .../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 | 1 + src/lib/app/hooks/useNotification.jsx | 4 +- 13 files changed, 340 insertions(+), 58 deletions(-) create mode 100644 src/components/layouts/Dashboard/Sidebar/__test__/SidebarDrawer.test.js create mode 100644 src/components/layouts/Dashboard/Sidebar/__test__/SidebarList.test.js create mode 100644 src/components/layouts/Dashboard/Sidebar/__test__/SidebarListItem.test.js create mode 100644 src/components/layouts/Dashboard/Sidebar/__test__/SidebarListSubitem.test.js create mode 100644 src/components/layouts/Dashboard/Sidebar/__test__/index.test.js diff --git a/example.env.local b/example.env.local index 922eec0..43f706c 100644 --- a/example.env.local +++ b/example.env.local @@ -10,4 +10,6 @@ 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_HAS_NOTIFICATION = "false" + NODE_ENV = "development" \ No newline at end of file diff --git a/mocks/handler.js b/mocks/handler.js index 8d22664..fee5afa 100644 --- a/mocks/handler.js +++ b/mocks/handler.js @@ -6,8 +6,12 @@ export const handler = [ return res(ctx.json({ data:{ id: 10, + full_name: "Witel Company", + position: "Software Engineer", permissions:[ - "manage_users" + "manage_users", + "manage_roles", + "manage_boss" ], } })) diff --git a/src/components/layouts/Dashboard/Sidebar/SidebarDrawer.jsx b/src/components/layouts/Dashboard/Sidebar/SidebarDrawer.jsx index 33348a5..3f1d449 100644 --- a/src/components/layouts/Dashboard/Sidebar/SidebarDrawer.jsx +++ b/src/components/layouts/Dashboard/Sidebar/SidebarDrawer.jsx @@ -4,7 +4,7 @@ import SidebarList from "./SidebarList"; import useUser from "@/lib/app/hooks/useUser"; const SidebarDrawer = ({handleDrawerToggle}) => { - const {user} = useUser() + const {user} = useUser(); const t = useTranslations(); return ( <> diff --git a/src/components/layouts/Dashboard/Sidebar/SidebarList.jsx b/src/components/layouts/Dashboard/Sidebar/SidebarList.jsx index 040b455..1e570f5 100644 --- a/src/components/layouts/Dashboard/Sidebar/SidebarList.jsx +++ b/src/components/layouts/Dashboard/Sidebar/SidebarList.jsx @@ -10,29 +10,15 @@ 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) => { - if (item.type === "page") { - if (action.route === item.route) - return {...item, selected: true} - else - return {...item, selected: false} - } - return {...item} - // item.subItem.map((subitem) => { - // if (action.route === subitem.route) - // return {...item, showSubItem: true, subItem: {...subitem, selected: true}} - // else - // return {...item, showSubItem: false, subItem: {...subitem, selected: false}} - // }) - } - ) + itemsArr.map((item) => ({ + ...item, + selected: item.type === "page" && action.route === item.route, + })) ); default: throw new Error(); @@ -54,7 +40,7 @@ export default function SidebarList({handleDrawerToggle}) { {itemArr.map((item) => - {(user.permissions.includes(item.permission) || item.permission === "all") && + {(user?.permissions?.includes(item.permission) || item.permission === "all") && { 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 ( <> - - - - }> + { }, })} onClick={() => { - if (item.type == "menu") { - dispatch({type: "COLLAPSE_MENU", key: item.key}); + if (hasSubItems) { + dispatch({ type: "COLLAPSE_MENU", key: item.key }); } - handleDrawerToggle(); }} sx={{ minHeight: 48, @@ -66,9 +69,7 @@ const SidebarListItem = ({item, dispatch, handleDrawerToggle}) => { } /> - - {item.type == "menu" && - (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 c9c8a36..1803b96 100644 --- a/src/components/layouts/Dashboard/Sidebar/SidebarListSubItem.jsx +++ b/src/components/layouts/Dashboard/Sidebar/SidebarListSubItem.jsx @@ -15,25 +15,27 @@ import useNotification from "@/lib/app/hooks/useNotification"; const SidebarListSubItem = ({item, handleDrawerToggle}) => { const t = useTranslations(); - const {notification_count} = useNotification() + const {notification_count} = useNotification(); + + const renderBadge = (subitem) => ( + + + + ); return ( - {item.subItem.map((subitem, index) => ( - - - - }> + {item.subItem.map((subitem) => ( + { + 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 new file mode 100644 index 0000000..1897c9b --- /dev/null +++ b/src/components/layouts/Dashboard/Sidebar/__test__/SidebarList.test.js @@ -0,0 +1,16 @@ +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 new file mode 100644 index 0000000..18313c5 --- /dev/null +++ b/src/components/layouts/Dashboard/Sidebar/__test__/SidebarListItem.test.js @@ -0,0 +1,125 @@ +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 new file mode 100644 index 0000000..9551937 --- /dev/null +++ b/src/components/layouts/Dashboard/Sidebar/__test__/SidebarListSubitem.test.js @@ -0,0 +1,103 @@ +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 new file mode 100644 index 0000000..740c384 --- /dev/null +++ b/src/components/layouts/Dashboard/Sidebar/__test__/index.test.js @@ -0,0 +1,17 @@ +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 index 2498dfd..c0b6d6d 100644 --- a/src/components/layouts/Dashboard/Sidebar/index.jsx +++ b/src/components/layouts/Dashboard/Sidebar/index.jsx @@ -9,6 +9,7 @@ const Sidebar = (props) => { aria-label="mailbox folders" > { + const requestServer = useRequest({auth: true, notification: false}) //swr config @@ -13,7 +15,7 @@ const useNotification = () => { }) }; - const {data, mutate} = useSWR(GET_SIDEBAR_NOTIFICATION, fetcher) + const {data, mutate} = useSWR( isNotificationEnabled ? GET_SIDEBAR_NOTIFICATION : '', fetcher , {keepPreviousData:true}) const notification_count = data //swr config From 45a9c3b2a080567d910e0f436a3a9f3c2768ecd2 Mon Sep 17 00:00:00 2001 From: AminGhasempoor Date: Sat, 28 Oct 2023 15:17:48 +0330 Subject: [PATCH 04/15] CFE-2 styling buttons --- public/locales/fa/app.json | 5 +++ .../CallActions/ActionHeader/index.jsx | 25 +++++++++++ .../CallActionCategoriesButton.jsx | 27 ++++++------ .../CallActionCategoriesButton.test.js | 31 ++++++++++++++ .../CallActionCategories/index.jsx | 19 ++++++--- .../CallActionSubcategoriesButton.jsx | 8 ++-- .../CallActionSubcategoriesButton.test.js | 41 +++++++++++++++++++ .../CallActionSubcategories/index.jsx | 19 ++++++--- .../CallTabPanel/CallActions/index.jsx | 3 ++ 9 files changed, 150 insertions(+), 28 deletions(-) create mode 100644 src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallActions/ActionHeader/index.jsx diff --git a/public/locales/fa/app.json b/public/locales/fa/app.json index 0a1c31b..6ff5c7b 100644 --- a/public/locales/fa/app.json +++ b/public/locales/fa/app.json @@ -260,5 +260,10 @@ "call_history_not_found": "تاریخچه ای برای تماس دریافتی یافت نشد", "call_history_error_fetching": "خطا در دریافت تاریخچه تماس دریافتی", "show_more": "نمایش موارد بیشتر" + }, + "CallAction": { + "call_history_of": "عملیات های مربوط به تماس", + "categories" : "موضوع ها", + "subcategories" : "زیر موضوع ها" } } 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 new file mode 100644 index 0000000..9ae87c0 --- /dev/null +++ b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallActions/ActionHeader/index.jsx @@ -0,0 +1,25 @@ +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 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 index d7c70f6..f568732 100644 --- 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 @@ -1,21 +1,24 @@ -import { Button, Stack } from "@mui/material"; +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 index e69de29..817bcde 100644 --- 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 @@ -0,0 +1,31 @@ +import {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() + }) + }); + }); +}); \ 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 index c40d31c..d4dec8a 100644 --- 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 @@ -1,15 +1,22 @@ -import { Stack } from "@mui/material"; +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 ; - })} - + <> + + + + {categoryLists.map((category) => { + return ; + })} + + + ); }; export default CallActionsCategories; 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 index 381b56b..d3c893d 100644 --- 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 @@ -1,4 +1,4 @@ -import { Button, Stack } from "@mui/material"; +import { Button, Grid, Stack } from "@mui/material"; import useRequest from "@/lib/app/hooks/useRequest"; import { CALL_ACTION } from "@/core/data/apiRoutes"; import useAnswers from "@/lib/callWidget/hooks/useAnswers"; @@ -22,11 +22,11 @@ const CallActionSubcategoriesButton = ({ sub_category, tab }) => { .catch(() => {}); }; 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 index e69de29..55e69e0 100644 --- 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 @@ -0,0 +1,41 @@ +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 categories names', async () => { + render( + + + + + + ) + const subCategoryElement = screen.queryByText("اطلاعات باز و بسته") + await waitFor(()=>{ + expect(subCategoryElement).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 index 7e270bb..dba6fc1 100644 --- 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 @@ -1,15 +1,22 @@ import useCategories from "@/lib/callWidget/hooks/useCategories"; -import { Stack } from "@mui/material"; +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 }) => { const { subCategoryLists } = useCategories(); + const t = useTranslations(); return ( - - {subCategoryLists.map((sub_category, index) => { - 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 index 1243278..d0c21f9 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 @@ -1,9 +1,12 @@ +import { Divider } from "@mui/material"; import CallActionsCategories from "./CallActionCategories"; import CallActionsSubcategories from "./CallActionSubcategories"; +import ActionHeader from "./ActionHeader"; function CallActions({ tab }) { return ( <> + From fafc61b320179b46eef5dddf6a597785af301d31 Mon Sep 17 00:00:00 2001 From: AminGhasempoor Date: Sun, 29 Oct 2023 10:38:50 +0330 Subject: [PATCH 05/15] CFE-2 write tests --- .../ActionHeader/__test__/index.test.js | 43 +++++++++++++++++++ .../CallActions/ActionHeader/index.jsx | 4 +- .../CallActionCategoriesButton.jsx | 1 + .../CallActionCategoriesButton.test.js | 24 ++++++++++- .../__test__/index.test.js | 29 +++++++++++++ .../CallActionCategories/index.jsx | 4 +- .../CallActionSubcategoriesButton.jsx | 2 +- .../CallActionSubcategoriesButton.test.js | 42 ++++++++---------- .../__test__/index.test.js | 29 +++++++++++++ .../CallActionSubcategories/index.jsx | 4 +- src/lib/callWidget/hooks/useCallerHistory.jsx | 4 +- 11 files changed, 152 insertions(+), 34 deletions(-) create mode 100644 src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallActions/ActionHeader/__test__/index.test.js create mode 100644 src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallActions/CallActionCategories/__test__/index.test.js create mode 100644 src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallActions/CallActionSubcategories/__test__/index.test.js 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 new file mode 100644 index 0000000..69f9b52 --- /dev/null +++ b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallActions/ActionHeader/__test__/index.test.js @@ -0,0 +1,43 @@ +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 index 9ae87c0..97b4297 100644 --- 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 @@ -15,11 +15,11 @@ const ActionHeader = ({tab}) => { .... {t("CallAction.call_history_of")}: - + {tab.phone_number} .... ) } -export default ActionHeader +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 index f568732..40c2b18 100644 --- 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 @@ -8,6 +8,7 @@ const CallActionCategoriesButton = ({ category, tab }) => { 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 index 55e69e0..718f7cd 100644 --- 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 @@ -3,39 +3,35 @@ import MockAppWithProviders from "../../../../../../../../../../../mocks/AppWith 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 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" - } -] +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 categories names', async () => { + it('should render subcategory name', async () => { render( - ) - const subCategoryElement = screen.queryByText("اطلاعات باز و بسته") - await waitFor(()=>{ - expect(subCategoryElement).toBeInTheDocument() - }) + ); + 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 new file mode 100644 index 0000000..2195bde --- /dev/null +++ b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallActions/CallActionSubcategories/__test__/index.test.js @@ -0,0 +1,29 @@ +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 index dba6fc1..0978e4d 100644 --- 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 @@ -9,8 +9,8 @@ const CallActionsSubcategories = ({ tab }) => { return ( <> - - + + {subCategoryLists.map((sub_category, index) => { return ; })} diff --git a/src/lib/callWidget/hooks/useCallerHistory.jsx b/src/lib/callWidget/hooks/useCallerHistory.jsx index 68ee6d0..810209d 100644 --- a/src/lib/callWidget/hooks/useCallerHistory.jsx +++ b/src/lib/callWidget/hooks/useCallerHistory.jsx @@ -29,8 +29,8 @@ const useCallerHistory = (call_id, phone_number, max_size) => { return { firstItemOfHistory: validData.length === 0 ? false : validData[0], historyList: validData.length < 2 ? false : validData.slice(1), - isLoadingHistoryList: true, - errorHistoryList: false + isLoadingHistoryList: isLoading, + errorHistoryList: !data } }; From 32a3ed707baf8a99ec6a6db5b8ea51b972ee68f6 Mon Sep 17 00:00:00 2001 From: AminGhasempoor Date: Sun, 29 Oct 2023 15:38:10 +0330 Subject: [PATCH 06/15] CFE-2 add header and description --- public/locales/fa/app.json | 5 +- .../__test__/index.test.js | 54 +++++++++++++++++++ .../CallActionDescription/index.jsx | 27 ++++++++++ .../CallActionSubcategoriesButton.jsx | 28 ++-------- .../CallActionSubcategories/index.jsx | 4 +- .../CallTabPanel/CallActions/index.jsx | 31 ++++++++++- 6 files changed, 121 insertions(+), 28 deletions(-) create mode 100644 src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallActions/CallActionDescription/__test__/index.test.js create mode 100644 src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallActions/CallActionDescription/index.jsx diff --git a/public/locales/fa/app.json b/public/locales/fa/app.json index 6ff5c7b..04ffca2 100644 --- a/public/locales/fa/app.json +++ b/public/locales/fa/app.json @@ -264,6 +264,9 @@ "CallAction": { "call_history_of": "عملیات های مربوط به تماس", "categories" : "موضوع ها", - "subcategories" : "زیر موضوع ها" + "subcategories" : "زیر موضوع ها", + "description" : "توضیحات تماس (اختیاری)", + "text_field_label" : "توضیحات", + "text_field_palacholder" : "لطفا توضیحات خود را وارد نمایید" } } 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 new file mode 100644 index 0000000..b7e0543 --- /dev/null +++ b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallActions/CallActionDescription/__test__/index.test.js @@ -0,0 +1,54 @@ +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 new file mode 100644 index 0000000..6a29324 --- /dev/null +++ b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallActions/CallActionDescription/index.jsx @@ -0,0 +1,27 @@ +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 index d2f581a..2b4a479 100644 --- 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 @@ -1,29 +1,11 @@ -import { Button, Grid, Stack } from "@mui/material"; -import useRequest from "@/lib/app/hooks/useRequest"; -import { CALL_ACTION } from "@/core/data/apiRoutes"; -import useAnswers from "@/lib/callWidget/hooks/useAnswers"; +import { Button, Grid } from "@mui/material"; -const CallActionSubcategoriesButton = ({ sub_category, tab }) => { - const requestServer = useRequest({ auth: true }); - const { closeAnswerTab } = useAnswers(); - const handleSubcategoryButton = () => { - requestServer(`${CALL_ACTION}/${tab.id}`, "post", { - data: { - category_id: sub_category.category_id, - subcategory_id: sub_category.subcategory_id, - }, - success: { - notification: { show: false }, - }, - }) - .then(() => { - closeAnswerTab(tab.id); - }) - .catch(() => {}); - }; +const CallActionSubcategoriesButton = ({ sub_category, tab, handleSubcategoryButton }) => { + return sub_category.category_id === tab.active_category_id ? ( - 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 index 0978e4d..0b88cfa 100644 --- 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 @@ -3,7 +3,7 @@ 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 }) => { +const CallActionsSubcategories = ({ tab, handleSubcategoryButton }) => { const { subCategoryLists } = useCategories(); const t = useTranslations(); return ( @@ -12,7 +12,7 @@ const CallActionsSubcategories = ({ tab }) => { {subCategoryLists.map((sub_category, index) => { - return ; + return ; })} 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 d0c21f9..f5c76b0 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 @@ -1,14 +1,41 @@ -import { Divider } from "@mui/material"; 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"; function CallActions({ tab }) { + const [description, setDescription] = useState('') + const requestServer = useRequest({ auth: true }); + const { closeAnswerTab } = useAnswers(); + + const handleSubcategoryButton = (category_id, subcategory_id) => { + requestServer(`${CALL_ACTION}/${tab.id}`, "post", { + data: { + category_id, + subcategory_id, + description + }, + success: { + notification: { show: false }, + }, + }) + .then(() => { + closeAnswerTab(tab.id); + }) + .catch(() => {}); + }; + + return ( <> + - + ); } From c598d82ed08b7c7dd3d339cc49ad77e0ccdbd869 Mon Sep 17 00:00:00 2001 From: AminGhasempoor Date: Sun, 29 Oct 2023 16:01:39 +0330 Subject: [PATCH 07/15] CFE-2 delete data in answer --- src/lib/callWidget/contexts/answers.jsx | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/src/lib/callWidget/contexts/answers.jsx b/src/lib/callWidget/contexts/answers.jsx index 4aa3c3c..96a150c 100644 --- a/src/lib/callWidget/contexts/answers.jsx +++ b/src/lib/callWidget/contexts/answers.jsx @@ -49,22 +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, - }, - { - id: 2, - phone_number: "09134849737", - date: new Date(), - active: false, - active_category_id: 2, - }, - ]); + const [state, dispatch] = useReducer(reducer, []); useEffect(() => { requestServer(GET_CATEGORY, "get") From 4bbdc61da29cc0c1d49638a628b2a558ae55d563 Mon Sep 17 00:00:00 2001 From: Yasiu1376 Date: Mon, 30 Oct 2023 12:06:21 +0330 Subject: [PATCH 08/15] CFE-3 implementation permission --- src/layouts/DashboardLayout.jsx | 9 ++++--- src/lib/app/hooks/useNotification.jsx | 4 ++- src/middlewares/AppPermission.jsx | 8 ++++++ .../__test__/AppPermission.test.js | 26 +++++++++++++++++++ 4 files changed, 43 insertions(+), 4 deletions(-) create mode 100644 src/middlewares/AppPermission.jsx create mode 100644 src/middlewares/__test__/AppPermission.test.js diff --git a/src/layouts/DashboardLayout.jsx b/src/layouts/DashboardLayout.jsx index 3c62a77..72344d6 100644 --- a/src/layouts/DashboardLayout.jsx +++ b/src/layouts/DashboardLayout.jsx @@ -1,12 +1,15 @@ -import CallWidget from "src/components/layouts/Dashboard/CallWidget"; import Dashboard from "src/components/layouts/Dashboard"; +import AppPermission from "@/middlewares/AppPermission"; +import CallWidget from "@/components/layouts/Dashboard/CallWidget"; const DashboardLayout = (props) => { - + const permissions = ["NEXT_PUBLIC_HAS_WIDGET"]; return ( <> - + + + ); }; diff --git a/src/lib/app/hooks/useNotification.jsx b/src/lib/app/hooks/useNotification.jsx index 6453ee8..6e80bca 100644 --- a/src/lib/app/hooks/useNotification.jsx +++ b/src/lib/app/hooks/useNotification.jsx @@ -1,7 +1,9 @@ import useSWR from 'swr' import {GET_SIDEBAR_NOTIFICATION} from "@/core/data/apiRoutes"; import useRequest from "@/lib/app/hooks/useRequest"; -const isNotificationEnabled = process.env.NEXT_HAS_NOTIFICATION === "true"; +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 = () => { diff --git a/src/middlewares/AppPermission.jsx b/src/middlewares/AppPermission.jsx new file mode 100644 index 0000000..8f93bd3 --- /dev/null +++ b/src/middlewares/AppPermission.jsx @@ -0,0 +1,8 @@ +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/__test__/AppPermission.test.js b/src/middlewares/__test__/AppPermission.test.js new file mode 100644 index 0000000..82377ba --- /dev/null +++ b/src/middlewares/__test__/AppPermission.test.js @@ -0,0 +1,26 @@ +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(); + }); + }); + }); +}); From e8e9292038eb26cad26826e8e5ce9e59eaff6036 Mon Sep 17 00:00:00 2001 From: Yasiu1376 Date: Mon, 30 Oct 2023 12:10:38 +0330 Subject: [PATCH 09/15] correct example.env --- example.env.local | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/example.env.local b/example.env.local index 43f706c..f6f0f33 100644 --- a/example.env.local +++ b/example.env.local @@ -10,6 +10,7 @@ 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_HAS_NOTIFICATION = "false" +NEXT_PUBLIC_HAS_VALUE=["NEXT_PUBLIC_HAS_WIDGET"] + +NODE_ENV = "development" -NODE_ENV = "development" \ No newline at end of file From 67ff293ed32b80534d0a0208d066988f0db2aa59 Mon Sep 17 00:00:00 2001 From: Yasiu1376 Date: Mon, 30 Oct 2023 13:50:55 +0330 Subject: [PATCH 10/15] add comment example.env --- example.env.local | 1 + 1 file changed, 1 insertion(+) diff --git a/example.env.local b/example.env.local index f6f0f33..3cf2def 100644 --- a/example.env.local +++ b/example.env.local @@ -10,6 +10,7 @@ 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" From bf032a2ee674ecab0176ed3b0e7eb6204743e43b Mon Sep 17 00:00:00 2001 From: AmirHossein Mahmoodi Date: Tue, 31 Oct 2023 09:30:51 +0330 Subject: [PATCH 11/15] CFE-7 change socket transport --- ecosystem.config.js | 18 ++++++++++++++++++ src/core/components/DataTable.jsx | 4 ++-- src/lib/app/contexts/socket.jsx | 1 + 3 files changed, 21 insertions(+), 2 deletions(-) create mode 100644 ecosystem.config.js diff --git a/ecosystem.config.js b/ecosystem.config.js new file mode 100644 index 0000000..6847796 --- /dev/null +++ b/ecosystem.config.js @@ -0,0 +1,18 @@ +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/src/core/components/DataTable.jsx b/src/core/components/DataTable.jsx index 32259fc..41bdbaa 100644 --- a/src/core/components/DataTable.jsx +++ b/src/core/components/DataTable.jsx @@ -29,7 +29,7 @@ function DataTable(props) { }); const [updateTime, setUpdateTime] = useState( - moment().format("HH:mm | jYYYY/jM/jD") + moment().format("HH:mm | jYYYY/jMM/jDD") ); const tableLocalization = useMemo( @@ -84,7 +84,7 @@ function DataTable(props) { }); useEffect(() => { - setUpdateTime(moment().format("HH:mm | jYYYY/jM/jD")); + setUpdateTime(moment().format("HH:mm | jYYYY/jMM/jDD")); }, [isValidating, languageApp]); return ( diff --git a/src/lib/app/contexts/socket.jsx b/src/lib/app/contexts/socket.jsx index 47154c8..7d51194 100644 --- a/src/lib/app/contexts/socket.jsx +++ b/src/lib/app/contexts/socket.jsx @@ -15,6 +15,7 @@ export const SocketProvider = ({children}) => { const t = useTranslations() const socket = useMemo(() => io(process.env.NEXT_PUBLIC_SERVER_SOCKET_URL, { autoConnect: false, + transports: ['websocket'], auth: { token: "" } From 523d0f310aba7238f23b9c4d27e6a249da13280d Mon Sep 17 00:00:00 2001 From: AmirHossein Mahmoodi Date: Tue, 31 Oct 2023 09:35:29 +0330 Subject: [PATCH 12/15] update ecosystem.config.js --- .gitignore | 3 ++- ecosystem.config.js.example | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 ecosystem.config.js.example diff --git a/.gitignore b/.gitignore index 662e667..1e62a6d 100644 --- a/.gitignore +++ b/.gitignore @@ -30,4 +30,5 @@ cypress/screenshots .env* .env .idea -package-lock.json \ No newline at end of file +package-lock.json +ecosystem.config.js \ No newline at end of file diff --git a/ecosystem.config.js.example b/ecosystem.config.js.example new file mode 100644 index 0000000..6847796 --- /dev/null +++ b/ecosystem.config.js.example @@ -0,0 +1,18 @@ +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 From a9c462d969600671a3cd4c57cf31d44abd17df09 Mon Sep 17 00:00:00 2001 From: AmirHossein Mahmoodi Date: Tue, 31 Oct 2023 09:36:58 +0330 Subject: [PATCH 13/15] update ecosystem.config.js --- ecosystem.config.js | 18 ------------------ 1 file changed, 18 deletions(-) delete mode 100644 ecosystem.config.js diff --git a/ecosystem.config.js b/ecosystem.config.js deleted file mode 100644 index 6847796..0000000 --- a/ecosystem.config.js +++ /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 From 180dcc8e48569302d2216a576185dc4e27fffe27 Mon Sep 17 00:00:00 2001 From: AmirHossein Mahmoodi Date: Tue, 31 Oct 2023 10:00:52 +0330 Subject: [PATCH 14/15] change version --- example.env.local | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/example.env.local b/example.env.local index 3cf2def..cc363e7 100644 --- a/example.env.local +++ b/example.env.local @@ -1,5 +1,5 @@ NEXT_PUBLIC_API_NAME = "CRM Dashboard" -NEXT_PUBLIC_API_VERSION = "0.0.1" +NEXT_PUBLIC_API_VERSION = "1.21.6" NEXT_PUBLIC_DEFAULT_LANGUAGE = "fa" NEXT_PUBLIC_DEFAULT_DIRECTION = "rtl" From 49c24de47385c4e3e58585163c918f201decfa54 Mon Sep 17 00:00:00 2001 From: AmirHossein Mahmoodi Date: Tue, 31 Oct 2023 12:02:02 +0330 Subject: [PATCH 15/15] CFE-8 Fixed scroll actions in call widget --- .../CallTabPanel/CallActions/index.jsx | 35 +++++++++++-------- .../CallTabPanel/CallHistory/index.jsx | 2 +- .../CallTabs/CallTabPanel/index.jsx | 2 +- 3 files changed, 23 insertions(+), 16 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 f5c76b0..082d701 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,15 +2,16 @@ import CallActionsCategories from "./CallActionCategories"; import CallActionsSubcategories from "./CallActionSubcategories"; import ActionHeader from "./ActionHeader"; import CallActionDescription from "./CallActionDescription"; -import { CALL_ACTION } from "@/core/data/apiRoutes"; +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 {useState} from "react"; +import {Stack} from "@mui/material"; -function CallActions({ tab }) { +function CallActions({tab}) { const [description, setDescription] = useState('') - const requestServer = useRequest({ auth: true }); - const { closeAnswerTab } = useAnswers(); + const requestServer = useRequest({auth: true}); + const {closeAnswerTab} = useAnswers(); const handleSubcategoryButton = (category_id, subcategory_id) => { requestServer(`${CALL_ACTION}/${tab.id}`, "post", { @@ -20,23 +21,29 @@ function CallActions({ tab }) { description }, success: { - notification: { show: false }, + notification: {show: false}, }, }) .then(() => { closeAnswerTab(tab.id); }) - .catch(() => {}); + .catch(() => { + }); }; - + return ( - <> - - - - - + + + + + + + + ); } 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 c83bc49..1e940cb 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 @@ -25,7 +25,7 @@ const CallHistory = ({tab}) => { overflow: "hidden" }}> - + {errorHistoryList ? ( ) : isLoadingHistoryList ? ( diff --git a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/index.jsx b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/index.jsx index 8fc1d87..be7e9d2 100644 --- a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/index.jsx +++ b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/index.jsx @@ -7,7 +7,7 @@ const CallTabPanel = ({tab}) => { - +