diff --git a/apps/meteor/client/views/omnichannel/realTimeMonitoring/RealTimeMonitoringPage.js b/apps/meteor/client/views/omnichannel/realTimeMonitoring/RealTimeMonitoringPage.tsx similarity index 71% rename from apps/meteor/client/views/omnichannel/realTimeMonitoring/RealTimeMonitoringPage.js rename to apps/meteor/client/views/omnichannel/realTimeMonitoring/RealTimeMonitoringPage.tsx index b6e29530b5e72..4b00d7e714aae 100644 --- a/apps/meteor/client/views/omnichannel/realTimeMonitoring/RealTimeMonitoringPage.js +++ b/apps/meteor/client/views/omnichannel/realTimeMonitoring/RealTimeMonitoringPage.tsx @@ -1,6 +1,9 @@ +import type { SelectOption } from '@rocket.chat/fuselage'; import { Box, Select, Margins, Option } from '@rocket.chat/fuselage'; import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; import { useTranslation } from '@rocket.chat/ui-contexts'; +import { useQueryClient } from '@tanstack/react-query'; +import type { MutableRefObject } from 'react'; import React, { useRef, useState, useMemo, useEffect, Fragment } from 'react'; import AutoCompleteDepartment from '../../../components/AutoCompleteDepartment'; @@ -18,7 +21,7 @@ import ChatsOverview from './overviews/ChatsOverview'; import ConversationOverview from './overviews/ConversationOverview'; import ProductivityOverview from './overviews/ProductivityOverview'; -const randomizeKeys = (keys) => { +const randomizeKeys = (keys: MutableRefObject) => { keys.current = keys.current.map((_key, i) => { return `${i}_${new Date().getTime()}`; }); @@ -28,13 +31,14 @@ const dateRange = getDateRange(); const RealTimeMonitoringPage = () => { const t = useTranslation(); + const queryClient = useQueryClient(); - const keys = useRef([...Array(10).keys()]); + const keys = useRef([...Array(10).map((_, i) => `${i}_${new Date().getTime()}`)]); - const [reloadFrequency, setReloadFrequency] = useState(5); + const [reloadFrequency, setReloadFrequency] = useState(5); const [departmentId, setDepartment] = useState(''); - const reloadRef = useRef({}); + const reloadRef = useRef<{ [x: string]: () => void }>({}); const departmentParams = useMemo( () => ({ @@ -62,20 +66,22 @@ const RealTimeMonitoringPage = () => { }); useEffect(() => { - const interval = setInterval(reloadCharts, reloadFrequency * 1000); + const interval = setInterval(reloadCharts, Number(reloadFrequency) * 1000); return () => { clearInterval(interval); randomizeKeys(keys); }; - }, [reloadCharts, reloadFrequency]); + }, [queryClient, reloadCharts, reloadFrequency]); + // TODO Check if Select Options does indeed accepts Elements as labels const reloadOptions = useMemo( - () => [ - [5, 5 {t('seconds')}], - [10, 10 {t('seconds')}], - [30, 30 {t('seconds')}], - [60, 1 {t('minute')}], - ], + () => + [ + ['5', 5 {t('seconds')}], + ['10', 10 {t('seconds')}], + ['30', 30 {t('seconds')}], + ['60', 1 {t('minute')}], + ] as unknown as SelectOption[], [t], ); @@ -99,21 +105,33 @@ const RealTimeMonitoringPage = () => { - setReloadFrequency(Number(val)))} + value={reloadFrequency} + /> - + @@ -122,21 +140,21 @@ const RealTimeMonitoringPage = () => { @@ -144,13 +162,27 @@ const RealTimeMonitoringPage = () => { - + - + diff --git a/apps/meteor/client/views/omnichannel/realTimeMonitoring/charts/AgentStatusChart.tsx b/apps/meteor/client/views/omnichannel/realTimeMonitoring/charts/AgentStatusChart.tsx index f1b2d0eed3376..c9cbad908ad3f 100644 --- a/apps/meteor/client/views/omnichannel/realTimeMonitoring/charts/AgentStatusChart.tsx +++ b/apps/meteor/client/views/omnichannel/realTimeMonitoring/charts/AgentStatusChart.tsx @@ -1,13 +1,13 @@ +import type { Box } from '@rocket.chat/fuselage'; import type { OperationParams } from '@rocket.chat/rest-typings'; import type { TranslationContextValue, TranslationKey } from '@rocket.chat/ui-contexts'; -import { useTranslation } from '@rocket.chat/ui-contexts'; +import { useTranslation, useEndpoint } from '@rocket.chat/ui-contexts'; +import { useQuery } from '@tanstack/react-query'; import type { Chart as ChartType } from 'chart.js'; -import type { MutableRefObject } from 'react'; -import React, { useRef, useEffect } from 'react'; +import type { ComponentProps, MutableRefObject } from 'react'; +import React, { useRef, useEffect, useMemo, useState } from 'react'; import { drawDoughnutChart } from '../../../../../app/livechat/client/lib/chartHandler'; -import { AsyncStatePhase } from '../../../../hooks/useAsyncState'; -import { useEndpointData } from '../../../../hooks/useEndpointData'; import Chart from './Chart'; import { useUpdateChartData } from './useUpdateChartData'; @@ -31,14 +31,15 @@ const init = (canvas: HTMLCanvasElement, context: ChartType | undefined, t: Tran type AgentStatusChartsProps = { params: OperationParams<'GET', '/v1/livechat/analytics/dashboards/charts/agents-status'>; - reloadRef: MutableRefObject<{ [x: string]: () => void }>; -}; + reloadFrequency: number; +} & ComponentProps; -const AgentStatusChart = ({ params, reloadRef, ...props }: AgentStatusChartsProps) => { +const AgentStatusChart = ({ params, reloadFrequency, ...props }: AgentStatusChartsProps) => { const t = useTranslation(); const canvas: MutableRefObject = useRef(null); const context: MutableRefObject = useRef(); + const [isInitialized, setIsInitialized] = useState(false); const updateChartData = useUpdateChartData({ context, @@ -47,9 +48,13 @@ const AgentStatusChart = ({ params, reloadRef, ...props }: AgentStatusChartsProp init, }); - const { value: data, phase: state, reload } = useEndpointData('/v1/livechat/analytics/dashboards/charts/agents-status', { params }); + const memoizedParams = useMemo(() => params, [params]); + + const getChartData = useEndpoint('GET', '/v1/livechat/analytics/dashboards/charts/agents-status'); - reloadRef.current.agentStatusChart = reload; + const { data, isLoading } = useQuery(['AgentStatusChart', memoizedParams], async () => getChartData(memoizedParams), { + refetchInterval: reloadFrequency * 1000, + }); const { offline = 0, available = 0, away = 0, busy = 0 } = data ?? initialData; @@ -57,19 +62,20 @@ const AgentStatusChart = ({ params, reloadRef, ...props }: AgentStatusChartsProp const initChart = async () => { if (canvas?.current) { context.current = await init(canvas.current, context.current, t); + setIsInitialized(true); } }; initChart(); }, [t]); useEffect(() => { - if (state === AsyncStatePhase.RESOLVED && context.current) { + if (!isLoading && isInitialized) { updateChartData(t('Offline'), [offline]); updateChartData(t('Available'), [available]); updateChartData(t('Away'), [away]); updateChartData(t('Busy'), [busy]); } - }, [available, away, busy, offline, state, t, updateChartData]); + }, [context, available, away, busy, isLoading, offline, t, updateChartData, isInitialized]); return ; }; diff --git a/apps/meteor/client/views/omnichannel/realTimeMonitoring/charts/Chart.tsx b/apps/meteor/client/views/omnichannel/realTimeMonitoring/charts/Chart.tsx index 5a47906ce92d7..0ab7959656f55 100644 --- a/apps/meteor/client/views/omnichannel/realTimeMonitoring/charts/Chart.tsx +++ b/apps/meteor/client/views/omnichannel/realTimeMonitoring/charts/Chart.tsx @@ -1,9 +1,8 @@ import { Box } from '@rocket.chat/fuselage'; -import type { MutableRefObject } from 'react'; +import type { ComponentProps, MutableRefObject } from 'react'; import React from 'react'; -type ChartProps = { canvasRef: MutableRefObject }; - +type ChartProps = { canvasRef: MutableRefObject } & ComponentProps; const style = { minHeight: '250px', }; diff --git a/apps/meteor/client/views/omnichannel/realTimeMonitoring/charts/ChatDurationChart.tsx b/apps/meteor/client/views/omnichannel/realTimeMonitoring/charts/ChatDurationChart.tsx index 78f8028a2d3ff..719df6322a2b2 100644 --- a/apps/meteor/client/views/omnichannel/realTimeMonitoring/charts/ChatDurationChart.tsx +++ b/apps/meteor/client/views/omnichannel/realTimeMonitoring/charts/ChatDurationChart.tsx @@ -1,14 +1,14 @@ +import type { Box } from '@rocket.chat/fuselage'; import type { OperationParams } from '@rocket.chat/rest-typings'; import type { TranslationContextValue } from '@rocket.chat/ui-contexts'; -import { useTranslation } from '@rocket.chat/ui-contexts'; +import { useEndpoint, useTranslation } from '@rocket.chat/ui-contexts'; +import { useQuery } from '@tanstack/react-query'; import type { Chart as ChartType } from 'chart.js'; -import type { MutableRefObject } from 'react'; -import React, { useRef, useEffect } from 'react'; +import type { ComponentProps, MutableRefObject } from 'react'; +import React, { useRef, useEffect, useMemo, useState } from 'react'; import { drawLineChart } from '../../../../../app/livechat/client/lib/chartHandler'; import { secondsToHHMMSS } from '../../../../../lib/utils/secondsToHHMMSS'; -import { AsyncStatePhase } from '../../../../hooks/useAsyncState'; -import { useEndpointData } from '../../../../hooks/useEndpointData'; import Chart from './Chart'; import { getMomentChartLabelsAndData } from './getMomentChartLabelsAndData'; import { getMomentCurrentLabel } from './getMomentCurrentLabel'; @@ -36,12 +36,19 @@ const init = (canvas: HTMLCanvasElement, context: ChartType | undefined, t: Tran tooltipCallbacks, }); +const defaultData = { + chatDuration: { + avg: 0, + longest: 0, + }, +}; + type ChatDurationChartProps = { params: OperationParams<'GET', '/v1/livechat/analytics/dashboards/charts/timings'>; - reloadRef: MutableRefObject<{ [x: string]: () => void }>; -}; + reloadFrequency: number; +} & ComponentProps; -const ChatDurationChart = ({ params, reloadRef, ...props }: ChatDurationChartProps) => { +const ChatDurationChart = ({ params, reloadFrequency, ...props }: ChatDurationChartProps) => { const t = useTranslation(); const canvas: MutableRefObject = useRef(null); @@ -54,34 +61,34 @@ const ChatDurationChart = ({ params, reloadRef, ...props }: ChatDurationChartPro init, }); - const { value: data, phase: state, reload } = useEndpointData('/v1/livechat/analytics/dashboards/charts/timings', { params }); + const [isInitialized, setIsInitialized] = useState(false); - reloadRef.current.chatDurationChart = reload; + const memoizedParams = useMemo(() => params, [params]); - const { - chatDuration: { avg, longest }, - } = data ?? { - chatDuration: { - avg: 0, - longest: 0, - }, - }; + const getChartData = useEndpoint('GET', '/v1/livechat/analytics/dashboards/charts/timings'); + + const { data, isLoading } = useQuery(['ChatDurationChart', memoizedParams], async () => getChartData(memoizedParams), { + refetchInterval: reloadFrequency * 1000, + }); + + const { avg, longest } = data?.chatDuration ?? defaultData.chatDuration; useEffect(() => { const initChart = async () => { if (canvas?.current) { context.current = await init(canvas.current, context.current, t); + setIsInitialized(true); } }; initChart(); }, [t]); useEffect(() => { - if (state === AsyncStatePhase.RESOLVED) { + if (!isLoading && isInitialized) { const label = getMomentCurrentLabel(); updateChartData(label, [avg, longest]); } - }, [avg, longest, state, t, updateChartData]); + }, [avg, data, isInitialized, isLoading, longest, t, updateChartData]); return ; }; diff --git a/apps/meteor/client/views/omnichannel/realTimeMonitoring/charts/ChatsChart.tsx b/apps/meteor/client/views/omnichannel/realTimeMonitoring/charts/ChatsChart.tsx index b84dfe6918a24..11972808c5b78 100644 --- a/apps/meteor/client/views/omnichannel/realTimeMonitoring/charts/ChatsChart.tsx +++ b/apps/meteor/client/views/omnichannel/realTimeMonitoring/charts/ChatsChart.tsx @@ -1,13 +1,13 @@ +import type { Box } from '@rocket.chat/fuselage'; import type { OperationParams } from '@rocket.chat/rest-typings'; import type { TranslationContextValue, TranslationKey } from '@rocket.chat/ui-contexts'; -import { useTranslation } from '@rocket.chat/ui-contexts'; +import { useEndpoint, useTranslation } from '@rocket.chat/ui-contexts'; +import { useQuery } from '@tanstack/react-query'; import type { Chart as ChartType } from 'chart.js'; -import type { MutableRefObject } from 'react'; -import React, { useRef, useEffect } from 'react'; +import type { ComponentProps, MutableRefObject } from 'react'; +import React, { useRef, useEffect, useState, useMemo } from 'react'; import { drawDoughnutChart } from '../../../../../app/livechat/client/lib/chartHandler'; -import { AsyncStatePhase } from '../../../../hooks/useAsyncState'; -import { useEndpointData } from '../../../../hooks/useEndpointData'; import Chart from './Chart'; import { useUpdateChartData } from './useUpdateChartData'; @@ -31,10 +31,10 @@ const init = (canvas: HTMLCanvasElement, context: ChartType | undefined, t: Tran type ChatsChartProps = { params: OperationParams<'GET', '/v1/livechat/analytics/dashboards/charts/chats'>; - reloadRef: MutableRefObject<{ [x: string]: () => void }>; -}; + reloadFrequency: number; +} & ComponentProps; -const ChatsChart = ({ params, reloadRef, ...props }: ChatsChartProps) => { +const ChatsChart = ({ params, reloadFrequency, ...props }: ChatsChartProps) => { const t = useTranslation(); const canvas: MutableRefObject = useRef(null); @@ -47,9 +47,15 @@ const ChatsChart = ({ params, reloadRef, ...props }: ChatsChartProps) => { init, }); - const { value: data, phase: state, reload } = useEndpointData('/v1/livechat/analytics/dashboards/charts/chats', { params }); + const [isInitialized, setIsInitialized] = useState(false); + + const memoizedParams = useMemo(() => params, [params]); - reloadRef.current.chatsChart = reload; + const getChartData = useEndpoint('GET', '/v1/livechat/analytics/dashboards/charts/chats'); + + const { data, isLoading } = useQuery(['ChatsChart', memoizedParams], async () => getChartData(memoizedParams), { + refetchInterval: reloadFrequency * 1000, + }); const { open, queued, closed, onhold } = data ?? initialData; @@ -57,19 +63,20 @@ const ChatsChart = ({ params, reloadRef, ...props }: ChatsChartProps) => { const initChart = async () => { if (canvas?.current) { context.current = await init(canvas.current, context.current, t); + setIsInitialized(true); } }; initChart(); }, [t]); useEffect(() => { - if (state === AsyncStatePhase.RESOLVED) { + if (!isLoading && isInitialized) { updateChartData(t('Open'), [open]); updateChartData(t('Closed'), [closed]); updateChartData(t('On_Hold_Chats'), [onhold]); updateChartData(t('Queued'), [queued]); } - }, [closed, open, queued, onhold, state, t, updateChartData]); + }, [closed, open, queued, onhold, t, updateChartData, isLoading, isInitialized]); return ; }; diff --git a/apps/meteor/client/views/omnichannel/realTimeMonitoring/charts/ChatsPerAgentChart.tsx b/apps/meteor/client/views/omnichannel/realTimeMonitoring/charts/ChatsPerAgentChart.tsx index 40f6489b2e5ac..6cbd45d327051 100644 --- a/apps/meteor/client/views/omnichannel/realTimeMonitoring/charts/ChatsPerAgentChart.tsx +++ b/apps/meteor/client/views/omnichannel/realTimeMonitoring/charts/ChatsPerAgentChart.tsx @@ -1,13 +1,13 @@ +import type { Box } from '@rocket.chat/fuselage'; import type { OperationParams } from '@rocket.chat/rest-typings'; import type { TranslationContextValue } from '@rocket.chat/ui-contexts'; -import { useTranslation } from '@rocket.chat/ui-contexts'; +import { useEndpoint, useTranslation } from '@rocket.chat/ui-contexts'; +import { useQuery } from '@tanstack/react-query'; import type { Chart as ChartType } from 'chart.js'; -import type { MutableRefObject } from 'react'; -import React, { useRef, useEffect } from 'react'; +import type { ComponentProps, MutableRefObject } from 'react'; +import React, { useRef, useEffect, useState, useMemo } from 'react'; import { drawLineChart } from '../../../../../app/livechat/client/lib/chartHandler'; -import { AsyncStatePhase } from '../../../../hooks/useAsyncState'; -import { useEndpointData } from '../../../../hooks/useEndpointData'; import Chart from './Chart'; import { useUpdateChartData } from './useUpdateChartData'; @@ -25,10 +25,10 @@ const init = (canvas: HTMLCanvasElement, context: ChartType | undefined, t: Tran type ChatsPerAgentChartProps = { params: OperationParams<'GET', '/v1/livechat/analytics/dashboards/charts/chats-per-agent'>; - reloadRef: MutableRefObject<{ [x: string]: () => void }>; -}; + reloadFrequency: number; +} & ComponentProps; -const ChatsPerAgentChart = ({ params, reloadRef, ...props }: ChatsPerAgentChartProps) => { +const ChatsPerAgentChart = ({ params, reloadFrequency, ...props }: ChatsPerAgentChartProps) => { const t = useTranslation(); const canvas: MutableRefObject = useRef(null); @@ -41,9 +41,15 @@ const ChatsPerAgentChart = ({ params, reloadRef, ...props }: ChatsPerAgentChartP init, }); - const { value: data, phase: state, reload } = useEndpointData('/v1/livechat/analytics/dashboards/charts/chats-per-agent', { params }); + const [isInitialized, setIsInitialized] = useState(false); + + const memoizedParams = useMemo(() => params, [params]); - reloadRef.current.chatsPerAgentChart = reload; + const getChartData = useEndpoint('GET', '/v1/livechat/analytics/dashboards/charts/chats-per-agent'); + + const { data, isLoading } = useQuery(['ChatsPerAgentChart', memoizedParams], async () => getChartData(memoizedParams), { + refetchInterval: reloadFrequency * 1000, + }); const chartData = data ?? initialData; @@ -51,13 +57,14 @@ const ChatsPerAgentChart = ({ params, reloadRef, ...props }: ChatsPerAgentChartP const initChart = async () => { if (canvas?.current) { context.current = await init(canvas.current, context.current, t); + setIsInitialized(true); } }; initChart(); }, [t]); useEffect(() => { - if (state === AsyncStatePhase.RESOLVED) { + if (!isLoading && isInitialized) { if (chartData?.success) { const { success, ...filteredChartData } = chartData; Object.entries(filteredChartData).forEach(([name, value]) => { @@ -71,7 +78,7 @@ const ChatsPerAgentChart = ({ params, reloadRef, ...props }: ChatsPerAgentChartP }); } } - }, [chartData, state, t, updateChartData]); + }, [chartData, isInitialized, isLoading, t, updateChartData]); return ; }; diff --git a/apps/meteor/client/views/omnichannel/realTimeMonitoring/charts/ChatsPerDepartmentChart.tsx b/apps/meteor/client/views/omnichannel/realTimeMonitoring/charts/ChatsPerDepartmentChart.tsx index 901565113fff7..d80c823e81c02 100644 --- a/apps/meteor/client/views/omnichannel/realTimeMonitoring/charts/ChatsPerDepartmentChart.tsx +++ b/apps/meteor/client/views/omnichannel/realTimeMonitoring/charts/ChatsPerDepartmentChart.tsx @@ -1,13 +1,13 @@ +import type { Box } from '@rocket.chat/fuselage'; import type { OperationParams } from '@rocket.chat/rest-typings'; import type { TranslationContextValue } from '@rocket.chat/ui-contexts'; -import { useTranslation } from '@rocket.chat/ui-contexts'; +import { useTranslation, useEndpoint } from '@rocket.chat/ui-contexts'; +import { useQuery } from '@tanstack/react-query'; import type { Chart as ChartType } from 'chart.js'; -import type { MutableRefObject } from 'react'; -import React, { useRef, useEffect } from 'react'; +import type { ComponentProps, MutableRefObject } from 'react'; +import React, { useRef, useEffect, useState, useMemo } from 'react'; import { drawLineChart } from '../../../../../app/livechat/client/lib/chartHandler'; -import { AsyncStatePhase } from '../../../../hooks/useAsyncState'; -import { useEndpointData } from '../../../../hooks/useEndpointData'; import Chart from './Chart'; import { useUpdateChartData } from './useUpdateChartData'; @@ -25,10 +25,10 @@ const init = (canvas: HTMLCanvasElement, context: ChartType | undefined, t: Tran type ChatsPerDepartmentChartProps = { params: OperationParams<'GET', '/v1/livechat/analytics/dashboards/charts/chats-per-department'>; - reloadRef: MutableRefObject<{ [x: string]: () => void }>; -}; + reloadFrequency: number; +} & ComponentProps; -const ChatsPerDepartmentChart = ({ params, reloadRef, ...props }: ChatsPerDepartmentChartProps) => { +const ChatsPerDepartmentChart = ({ params, reloadFrequency, ...props }: ChatsPerDepartmentChartProps) => { const t = useTranslation(); const canvas: MutableRefObject = useRef(null); @@ -41,13 +41,15 @@ const ChatsPerDepartmentChart = ({ params, reloadRef, ...props }: ChatsPerDepart init, }); - const { - value: data, - phase: state, - reload, - } = useEndpointData('/v1/livechat/analytics/dashboards/charts/chats-per-department', { params }); + const [isInitialized, setIsInitialized] = useState(false); + + const memoizedParams = useMemo(() => params, [params]); - reloadRef.current.chatsPerDepartmentChart = reload; + const getChartData = useEndpoint('GET', '/v1/livechat/analytics/dashboards/charts/chats-per-department'); + + const { data, isLoading } = useQuery(['ChatsPerDepartmentChart', memoizedParams], async () => getChartData(memoizedParams), { + refetchInterval: reloadFrequency * 1000, + }); const chartData = data ?? initialData; @@ -55,13 +57,14 @@ const ChatsPerDepartmentChart = ({ params, reloadRef, ...props }: ChatsPerDepart const initChart = async () => { if (canvas?.current) { context.current = await init(canvas.current, context.current, t); + setIsInitialized(true); } }; initChart(); }, [t]); useEffect(() => { - if (state === AsyncStatePhase.RESOLVED) { + if (!isLoading && isInitialized) { if (chartData?.success) { const { success, ...filteredChartData } = chartData; Object.entries(filteredChartData).forEach(([name, value]) => { @@ -74,7 +77,7 @@ const ChatsPerDepartmentChart = ({ params, reloadRef, ...props }: ChatsPerDepart }); } } - }, [chartData, state, t, updateChartData]); + }, [chartData, isInitialized, isLoading, t, updateChartData]); return ; }; diff --git a/apps/meteor/client/views/omnichannel/realTimeMonitoring/charts/ResponseTimesChart.tsx b/apps/meteor/client/views/omnichannel/realTimeMonitoring/charts/ResponseTimesChart.tsx index dc8d81fba2a34..20fcd4559a2d5 100644 --- a/apps/meteor/client/views/omnichannel/realTimeMonitoring/charts/ResponseTimesChart.tsx +++ b/apps/meteor/client/views/omnichannel/realTimeMonitoring/charts/ResponseTimesChart.tsx @@ -1,14 +1,14 @@ +import type { Box } from '@rocket.chat/fuselage'; import type { OperationParams } from '@rocket.chat/rest-typings'; import type { TranslationContextValue } from '@rocket.chat/ui-contexts'; -import { useTranslation } from '@rocket.chat/ui-contexts'; +import { useTranslation, useEndpoint } from '@rocket.chat/ui-contexts'; +import { useQuery } from '@tanstack/react-query'; import type { Chart as ChartType } from 'chart.js'; -import type { MutableRefObject } from 'react'; -import React, { useRef, useEffect } from 'react'; +import type { ComponentProps, MutableRefObject } from 'react'; +import React, { useRef, useEffect, useState, useMemo } from 'react'; import { drawLineChart } from '../../../../../app/livechat/client/lib/chartHandler'; import { secondsToHHMMSS } from '../../../../../lib/utils/secondsToHHMMSS'; -import { AsyncStatePhase } from '../../../../hooks/useAsyncState'; -import { useEndpointData } from '../../../../hooks/useEndpointData'; import Chart from './Chart'; import { getMomentChartLabelsAndData } from './getMomentChartLabelsAndData'; import { getMomentCurrentLabel } from './getMomentCurrentLabel'; @@ -37,12 +37,23 @@ const init = (canvas: HTMLCanvasElement, context: ChartType | undefined, t: Tran { legends: true, anim: true, smallTicks: true, displayColors: false, tooltipCallbacks }, ); +const defaultData = { + reaction: { + avg: 0, + longest: 0, + }, + response: { + avg: 0, + longest: 0, + }, +}; + type ResponseTimesChartProps = { params: OperationParams<'GET', '/v1/livechat/analytics/dashboards/charts/timings'>; - reloadRef: MutableRefObject<{ [x: string]: () => void }>; -}; + reloadFrequency: number; +} & ComponentProps; -const ResponseTimesChart = ({ params, reloadRef, ...props }: ResponseTimesChartProps) => { +const ResponseTimesChart = ({ params, reloadFrequency, ...props }: ResponseTimesChartProps) => { const t = useTranslation(); const canvas: MutableRefObject = useRef(null); @@ -55,39 +66,36 @@ const ResponseTimesChart = ({ params, reloadRef, ...props }: ResponseTimesChartP init, }); - const { value: data, phase: state, reload } = useEndpointData('/v1/livechat/analytics/dashboards/charts/timings', { params }); + const [isInitialized, setIsInitialized] = useState(false); - reloadRef.current.responseTimesChart = reload; + const memoizedParams = useMemo(() => params, [params]); - const { - reaction: { avg: reactionAvg, longest: reactionLongest }, - response: { avg: responseAvg, longest: responseLongest }, - } = data ?? { - reaction: { - avg: 0, - longest: 0, - }, - response: { - avg: 0, - longest: 0, - }, - }; + const getChartData = useEndpoint('GET', '/v1/livechat/analytics/dashboards/charts/timings'); + + const { data, isLoading } = useQuery(['ResponseTimesChart', memoizedParams], async () => getChartData(memoizedParams), { + refetchInterval: reloadFrequency * 1000, + }); + + const { avg: reactionAvg, longest: reactionLongest } = data?.reaction ?? defaultData.reaction; + + const { avg: responseAvg, longest: responseLongest } = data?.response ?? defaultData.response; useEffect(() => { const initChart = async () => { if (canvas?.current) { context.current = await init(canvas.current, context.current, t); + setIsInitialized(true); } }; initChart(); }, [t]); useEffect(() => { - if (state === AsyncStatePhase.RESOLVED) { + if (!isLoading && isInitialized) { const label = getMomentCurrentLabel(); updateChartData(label, [reactionAvg, reactionLongest, responseAvg, responseLongest]); } - }, [reactionAvg, reactionLongest, responseAvg, responseLongest, state, t, updateChartData]); + }, [data, isInitialized, isLoading, reactionAvg, reactionLongest, responseAvg, responseLongest, t, updateChartData]); return ; };