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;