diff --git a/public/locales/fa/app.json b/public/locales/fa/app.json index c17c938..0a1c31b 100644 --- a/public/locales/fa/app.json +++ b/public/locales/fa/app.json @@ -250,5 +250,15 @@ "delete_tooltip": "حذف", "update_tooltip": "ویرایش", "update_expert": "ویرایش کارشناس" + }, + "CallHistory": { + "category_name": "موضوع", + "subcategory_name": "زیر موضوع", + "operator": "اپراتور", + "date": "تاریخ", + "call_history_of": "تاریخچه تماس های", + "call_history_not_found": "تاریخچه ای برای تماس دریافتی یافت نشد", + "call_history_error_fetching": "خطا در دریافت تاریخچه تماس دریافتی", + "show_more": "نمایش موارد بیشتر" } } diff --git a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallHistory/ErrorOrEmpty/__tests__/index.test.js b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallHistory/ErrorOrEmpty/__tests__/index.test.js new file mode 100644 index 0000000..91d5651 --- /dev/null +++ b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallHistory/ErrorOrEmpty/__tests__/index.test.js @@ -0,0 +1,19 @@ +import {render, screen} from "@testing-library/react"; +import ErrorOrEmpty from "../../ErrorOrEmpty"; +import MockAppWithProviders from "../../../../../../../../../../../mocks/AppWithProvider"; + +describe("ErrorOrEmpty Component From call history", () => { + describe("Rendering", () => { + it("error fetching history of call text rendered", () => { + render(); + const errorText = screen.queryByText('خطا در دریافت تاریخچه تماس دریافتی'); + expect(errorText).toBeInTheDocument(); + }); + it("empty fetching history of call text rendered", () => { + render(); + const emptyText = screen.queryByText('تاریخچه ای برای تماس دریافتی یافت نشد'); + expect(emptyText).toBeInTheDocument(); + + }); + }); +}); \ No newline at end of file diff --git a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallHistory/ErrorOrEmpty/index.jsx b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallHistory/ErrorOrEmpty/index.jsx new file mode 100644 index 0000000..bb0f818 --- /dev/null +++ b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallHistory/ErrorOrEmpty/index.jsx @@ -0,0 +1,33 @@ +import {useTranslations} from "next-intl"; +import {Typography} from "@mui/material"; + +const ErrorOrEmpty = ({isErrorOrEmpty}) => { + const t = useTranslations(); + return ( + <> + {isErrorOrEmpty === "error" ? + + {t("CallHistory.call_history_error_fetching")} + + : isErrorOrEmpty === "empty" ? + + {t("CallHistory.call_history_not_found")} + : <> + } + + ) +} + +export default ErrorOrEmpty diff --git a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallHistory/HistoryHeader/__tests__/index.test.js b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallHistory/HistoryHeader/__tests__/index.test.js new file mode 100644 index 0000000..51d700e --- /dev/null +++ b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallHistory/HistoryHeader/__tests__/index.test.js @@ -0,0 +1,21 @@ +import {render, screen} from "@testing-library/react"; +import HistoryHeader from "../../HistoryHeader"; +import MockAppWithProviders from "../../../../../../../../../../../mocks/AppWithProvider"; + +describe("HistoryHeader Component From call history", () => { + describe("Rendering", () => { + const tab = { + phone_number: "09111111111" + } + it("text of header rendered", () => { + render(); + const textHeader = screen.queryByText(/تاریخچه تماس های/i); + expect(textHeader).toBeInTheDocument(); + }); + it("caller number in header rendered", () => { + render(); + const callerNumber = screen.queryByText(/09111111111/i); + expect(callerNumber).toBeInTheDocument(); + }); + }); +}); \ No newline at end of file diff --git a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallHistory/HistoryHeader/index.jsx b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallHistory/HistoryHeader/index.jsx new file mode 100644 index 0000000..21a3270 --- /dev/null +++ b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallHistory/HistoryHeader/index.jsx @@ -0,0 +1,25 @@ +import {useTranslations} from "next-intl"; +import {Stack, Typography} from "@mui/material"; + +const HistoryHeader = ({tab}) => { + const t = useTranslations(); + return ( + + + .... {t("CallHistory.call_history_of")}: + + + {tab.phone_number} .... + + + ) +} + +export default HistoryHeader diff --git a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallHistory/ListItemOfCalls/PreviousOperatorData/__tests__/index.test.js b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallHistory/ListItemOfCalls/PreviousOperatorData/__tests__/index.test.js new file mode 100644 index 0000000..4fd4519 --- /dev/null +++ b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallHistory/ListItemOfCalls/PreviousOperatorData/__tests__/index.test.js @@ -0,0 +1,22 @@ +import {render, screen} from "@testing-library/react"; +import MockAppWithProviders from "../../../../../../../../../../../../mocks/AppWithProvider"; +import PreviousOperatorData from "../../PreviousOperatorData"; + +describe("PreviousOperatorData Component From call history", () => { + describe("Rendering", () => { + const historyItem = { + operator_name: "test name", + answer_date: "test date" + } + it("operator name rendered", () => { + render(); + const operatorName = screen.queryByText("test name"); + expect(operatorName).toBeInTheDocument(); + }); + it("operator answer date rendered", () => { + render(); + const operatorAnswerDate = screen.queryByText("test date"); + expect(operatorAnswerDate).toBeInTheDocument(); + }); + }); +}); \ No newline at end of file diff --git a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallHistory/ListItemOfCalls/PreviousOperatorData/index.jsx b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallHistory/ListItemOfCalls/PreviousOperatorData/index.jsx new file mode 100644 index 0000000..afd2ad7 --- /dev/null +++ b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallHistory/ListItemOfCalls/PreviousOperatorData/index.jsx @@ -0,0 +1,28 @@ +import {useTranslations} from "next-intl"; +import {Avatar, Box, ListItem, ListItemAvatar, ListItemText, Typography} from "@mui/material"; +import HeadsetMicIcon from '@mui/icons-material/HeadsetMic'; + +const PreviousOperatorData = ({historyItem}) => { + const t = useTranslations(); + return ( + + + + + + + + + {historyItem.operator_name} + + + ({t("CallHistory.operator")}) + + + } secondary={historyItem.answer_date}/> + + ) +} + +export default PreviousOperatorData diff --git a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallHistory/ListItemOfCalls/Topics/__tests__/index.test.js b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallHistory/ListItemOfCalls/Topics/__tests__/index.test.js new file mode 100644 index 0000000..d9d77e4 --- /dev/null +++ b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallHistory/ListItemOfCalls/Topics/__tests__/index.test.js @@ -0,0 +1,22 @@ +import {render, screen} from "@testing-library/react"; +import MockAppWithProviders from "../../../../../../../../../../../../mocks/AppWithProvider"; +import Topics from "../../Topics"; + +describe("Topics Component From call history", () => { + describe("Rendering", () => { + const historyItem = { + category_name: "test category", + subcategory_name: "test sub category" + } + it("operator name rendered", () => { + render(); + const operatorName = screen.queryByText("test category"); + expect(operatorName).toBeInTheDocument(); + }); + it("operator answer date rendered", () => { + render(); + const operatorAnswerDate = screen.queryByText("test sub category"); + expect(operatorAnswerDate).toBeInTheDocument(); + }); + }); +}); \ No newline at end of file diff --git a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallHistory/ListItemOfCalls/Topics/index.jsx b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallHistory/ListItemOfCalls/Topics/index.jsx new file mode 100644 index 0000000..3b57744 --- /dev/null +++ b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallHistory/ListItemOfCalls/Topics/index.jsx @@ -0,0 +1,43 @@ +import {useTranslations} from "next-intl"; +import {Box, List, ListItem, Typography} from "@mui/material"; + +const Topics = ({historyItem}) => { + const t = useTranslations(); + return ( + + + + + + {t("CallHistory.category_name")}: + + + {historyItem.category_name} + + + + + + + + + {t("CallHistory.subcategory_name")}: + + + {historyItem.subcategory_name} + + + + + + ) +} + +export default Topics diff --git a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallHistory/ListItemOfCalls/index.jsx b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallHistory/ListItemOfCalls/index.jsx new file mode 100644 index 0000000..7af4fd5 --- /dev/null +++ b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallHistory/ListItemOfCalls/index.jsx @@ -0,0 +1,21 @@ +import {useTranslations} from "next-intl"; +import {Grow, List} from "@mui/material"; +import PreviousOperatorData from "./PreviousOperatorData"; +import Topics from "./Topics"; + +const ListItemOfCalls = ({historyItem}) => { + const t = useTranslations(); + + // console.log(historyItem) + + return ( + + + + + + + ) +} + +export default ListItemOfCalls diff --git a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallHistory/LoadingHistory/__tests__/intex.test.js b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallHistory/LoadingHistory/__tests__/intex.test.js new file mode 100644 index 0000000..1592a02 --- /dev/null +++ b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallHistory/LoadingHistory/__tests__/intex.test.js @@ -0,0 +1,68 @@ +import {render, screen} from "@testing-library/react"; +import LoadingHistory from "../../LoadingHistory"; +import MockAppWithProviders from "../../../../../../../../../../../mocks/AppWithProvider"; + +describe("LoadingHistory Component From call history", () => { + describe("Rendering", () => { + it("loading list rendered", () => { + render(); + const loadingList = screen.queryByTestId("loading-list"); + expect(loadingList).toBeInTheDocument(); + }); + it("loading list item avatar rendered", () => { + render(); + const loadingListItemAvatar = screen.queryByTestId("loading-listItemAvatar"); + expect(loadingListItemAvatar).toBeInTheDocument(); + }); + it("loading list item text rendered", () => { + render(); + const loadingListItemText = screen.queryByTestId("loading-listItemText"); + expect(loadingListItemText).toBeInTheDocument(); + }); + it("loading list item category rendered", () => { + render(); + const loadingListItemCat = screen.queryByTestId("loading-listItem-cat"); + expect(loadingListItemCat).toBeInTheDocument(); + }); + it("loading list item sub category rendered", () => { + render(); + const loadingListItemSubCat = screen.queryByTestId("loading-listItem-subCat"); + expect(loadingListItemSubCat).toBeInTheDocument(); + }); + it("operator avatar skeleton rendered", () => { + render(); + const operatorAvatarSkeleton = screen.queryByTestId("operator-avatar-skeleton"); + expect(operatorAvatarSkeleton).toBeInTheDocument(); + }); + it("operator name skeleton rendered", () => { + render(); + const operatorNameSkeleton = screen.queryByTestId("operator-name-skeleton"); + expect(operatorNameSkeleton).toBeInTheDocument(); + }); + it("operator answer date skeleton rendered", () => { + render(); + const operatorAnswerDateSkeleton = screen.queryByTestId("operator-answer-date-skeleton"); + expect(operatorAnswerDateSkeleton).toBeInTheDocument(); + }); + it("operator category header skeleton rendered", () => { + render(); + const operatorCategoryHeaderSkeleton = screen.queryByTestId("operator-category-header-skeleton"); + expect(operatorCategoryHeaderSkeleton).toBeInTheDocument(); + }); + it("operator category value skeleton rendered", () => { + render(); + const operatorCategoryValueSkeleton = screen.queryByTestId("operator-category-value-skeleton"); + expect(operatorCategoryValueSkeleton).toBeInTheDocument(); + }); + it("operator subCategory header skeleton rendered", () => { + render(); + const operatorSubCategoryHeaderSkeleton = screen.queryByTestId("operator-subCategory-header-skeleton"); + expect(operatorSubCategoryHeaderSkeleton).toBeInTheDocument(); + }); + it("operator subCategory value skeleton rendered", () => { + render(); + const operatorSubCategoryValueSkeleton = screen.queryByTestId("operator-subCategory-value-skeleton"); + expect(operatorSubCategoryValueSkeleton).toBeInTheDocument(); + }); + }); +}); \ No newline at end of file diff --git a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallHistory/LoadingHistory/index.jsx b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallHistory/LoadingHistory/index.jsx new file mode 100644 index 0000000..2c9900e --- /dev/null +++ b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallHistory/LoadingHistory/index.jsx @@ -0,0 +1,86 @@ +import {useTranslations} from "next-intl"; +import {Box, List, ListItem, ListItemAvatar, ListItemText, Skeleton} from "@mui/material"; + +const LoadingHistory = () => { + const t = useTranslations(); + return ( + <> + + + + + + + + + } secondary={ + + }/> + + + + + + + + + + + + + + + + + + + + + + + ) +} + +export default LoadingHistory diff --git a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallHistory/index.jsx b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallHistory/index.jsx index 048c091..c83bc49 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 @@ -1,6 +1,64 @@ +import {useTranslations} from "next-intl"; +import {Box, Chip, Divider, Stack} from "@mui/material"; +import HeadphonesIcon from '@mui/icons-material/Headphones'; +import LoadingHistory from "./LoadingHistory"; +import HistoryHeader from "./HistoryHeader"; +import ErrorOrEmpty from "./ErrorOrEmpty"; +import useCallerHistory from "@/lib/callWidget/hooks/useCallerHistory"; +import ListItemOfCalls from "./ListItemOfCalls"; +import {useState} from "react"; + +const max_size = 10; const CallHistory = ({tab}) => { + const t = useTranslations(); + const { + firstItemOfHistory, + historyList, + isLoadingHistoryList, + errorHistoryList + } = useCallerHistory(tab.id, tab.phone_number, max_size); + const [showRestOfHistory, setShowRestOfHistory] = useState(false); + return ( - <>CallHistory - {tab.id} + + + + {errorHistoryList ? ( + + ) : isLoadingHistoryList ? ( + + ) : !firstItemOfHistory ? ( + + ) : ( + + + {historyList && ( + + } + onClick={() => setShowRestOfHistory(true)} + label={t("show_more")} + disabled={showRestOfHistory} + /> + + )} + {showRestOfHistory && ( + <> + {historyList.map((historyItem, index) => ( + <> + + + + ))} + ) + } + + )} + + ) } 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 79dc736..8fc1d87 100644 --- a/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/index.jsx +++ b/src/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/index.jsx @@ -1,15 +1,19 @@ -import {Grid} from "@mui/material"; +import {Divider, Grid} from "@mui/material"; import CallActions from "@/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallActions"; import CallHistory from "@/components/layouts/Dashboard/CallWidget/CallWidgetDialog/CallTabs/CallTabPanel/CallHistory"; const CallTabPanel = ({tab}) => { return ( - - + + + + diff --git a/src/core/data/apiRoutes.js b/src/core/data/apiRoutes.js index 19534ca..127d1df 100644 --- a/src/core/data/apiRoutes.js +++ b/src/core/data/apiRoutes.js @@ -16,6 +16,10 @@ export const GET_ROLE_LIST = BASE_URL + "/api/roles/list"; export const GET_PROVINCE_LIST = BASE_URL + "/api/provinces"; // province list +// province list +export const GET_CALLER_HISTORY = BASE_URL + "/api/calls/list"; +// province list + //change password export const SET_USER_PASSWORD = BASE_URL + "/api/profile/change_password"; //end change password diff --git a/src/lib/callWidget/hooks/useCallerHistory.jsx b/src/lib/callWidget/hooks/useCallerHistory.jsx new file mode 100644 index 0000000..68ee6d0 --- /dev/null +++ b/src/lib/callWidget/hooks/useCallerHistory.jsx @@ -0,0 +1,37 @@ +import useRequest from "@/lib/app/hooks/useRequest"; +import {useEffect, useState} from "react"; +import useSWR from "swr"; +import {GET_CALLER_HISTORY} from "@/core/data/apiRoutes"; + +const useCallerHistory = (call_id, phone_number, max_size) => { + const requestServer = useRequest({auth: true, notification: false}); + const [validData, setValidData] = useState([]); + + //swr config + const fetcher = (...args) => { + return requestServer(args, 'get').then(({data}) => { + return data.data; + }).catch(() => { + }) + }; + + const {data, isLoading} = useSWR(`${GET_CALLER_HISTORY}?phone_number=${phone_number}&size=${max_size}`, fetcher, { + revalidateIfStale: false, + revalidateOnFocus: false, + revalidateOnReconnect: false, + keepPreviousData: true + }); + + useEffect(() => { + data && setValidData(data.filter((item) => item.id != call_id)); + }, [data, call_id]); + + return { + firstItemOfHistory: validData.length === 0 ? false : validData[0], + historyList: validData.length < 2 ? false : validData.slice(1), + isLoadingHistoryList: true, + errorHistoryList: false + } +}; + +export default useCallerHistory; \ No newline at end of file