import React, { useState, useEffect, useCallback } from 'react'; import { useTranslation } from 'react-i18next'; import { BarElement, CategoryScale, Chart as ChartJS, Legend, LinearScale, Title, Tooltip, } from 'chart.js'; import { Bar } from 'react-chartjs-2'; import userService from '../api/services/userService'; import Dropdown from '../components/Dropdown'; import { htmlLegendPlugin } from '../utils/chartUtils'; import { formatDate } from '../utils/dateTimeUtils'; import { APIKeyData } from './types'; import type { ChartData } from 'chart.js'; import SkeletonLoader from '../components/SkeletonLoader'; ChartJS.register( CategoryScale, LinearScale, BarElement, Title, Tooltip, Legend, ); export default function Analytics() { const { t } = useTranslation(); const filterOptions = [ { label: t('settings.analytics.filterOptions.hour'), value: 'last_hour' }, { label: t('settings.analytics.filterOptions.last24Hours'), value: 'last_24_hour', }, { label: t('settings.analytics.filterOptions.last7Days'), value: 'last_7_days', }, { label: t('settings.analytics.filterOptions.last15Days'), value: 'last_15_days', }, { label: t('settings.analytics.filterOptions.last30Days'), value: 'last_30_days', }, ]; const [messagesData, setMessagesData] = useState | null>(null); const [tokenUsageData, setTokenUsageData] = useState | null>(null); const [feedbackData, setFeedbackData] = useState | null>(null); const [chatbots, setChatbots] = useState([]); const [selectedChatbot, setSelectedChatbot] = useState(); const [messagesFilter, setMessagesFilter] = useState<{ label: string; value: string; }>({ label: t('settings.analytics.filterOptions.last30Days'), value: 'last_30_days', }); const [tokenUsageFilter, setTokenUsageFilter] = useState<{ label: string; value: string; }>({ label: t('settings.analytics.filterOptions.last30Days'), value: 'last_30_days', }); const [feedbackFilter, setFeedbackFilter] = useState<{ label: string; value: string; }>({ label: t('settings.analytics.filterOptions.last30Days'), value: 'last_30_days', }); const [loadingMessages, setLoadingMessages] = useState(false); const [loadingTokens, setLoadingTokens] = useState(false); const [loadingFeedback, setLoadingFeedback] = useState(false); const setLoadingWithMinDuration = useCallback( ( setter: React.Dispatch>, isLoading: boolean, ) => { if (isLoading) { setter(true); } else { setTimeout(() => { setter(false); }, 2000); } }, [], ); const fetchChatbots = async () => { try { const response = await userService.getAPIKeys(); if (!response.ok) { throw new Error('Failed to fetch Chatbots'); } const chatbots = await response.json(); setChatbots(chatbots); } catch (error) { console.error(error); } }; const fetchMessagesData = useCallback(async () => { setLoadingWithMinDuration(setLoadingMessages, true); try { const response = await userService.getMessageAnalytics({ api_key_id: selectedChatbot?.id, filter_option: messagesFilter.value, }); if (!response.ok) { throw new Error('Failed to fetch analytics data'); } const data = await response.json(); setMessagesData(data.messages); setLoadingWithMinDuration(setLoadingMessages, false); } catch (error) { console.error(error); } finally { setLoadingWithMinDuration(setLoadingMessages, false); } }, [selectedChatbot, messagesFilter, setLoadingWithMinDuration]); const fetchTokenData = useCallback(async () => { setLoadingWithMinDuration(setLoadingTokens, true); try { const response = await userService.getTokenAnalytics({ api_key_id: selectedChatbot?.id, filter_option: tokenUsageFilter.value, }); if (!response.ok) { throw new Error('Failed to fetch analytics data'); } const data = await response.json(); setTokenUsageData(data.token_usage); setLoadingWithMinDuration(setLoadingTokens, false); } catch (error) { console.error(error); } finally { setLoadingWithMinDuration(setLoadingTokens, false); } }, [selectedChatbot, tokenUsageFilter, setLoadingWithMinDuration]); const fetchFeedbackData = useCallback(async () => { setLoadingWithMinDuration(setLoadingFeedback, true); try { const response = await userService.getFeedbackAnalytics({ api_key_id: selectedChatbot?.id, filter_option: feedbackFilter.value, }); if (!response.ok) { throw new Error('Failed to fetch analytics data'); } const data = await response.json(); setFeedbackData(data.feedback); setLoadingWithMinDuration(setLoadingFeedback, false); } catch (error) { console.error(error); } finally { setLoadingWithMinDuration(setLoadingFeedback, false); } }, [selectedChatbot, feedbackFilter, setLoadingWithMinDuration]); useEffect(() => { fetchChatbots(); }, []); useEffect(() => { fetchMessagesData(); fetchTokenData(); fetchFeedbackData(); }, [fetchMessagesData, fetchTokenData, fetchFeedbackData]); return (

{t('settings.analytics.filterByChatbot')}

({ label: chatbot.name, value: chatbot.id, })), { label: t('settings.analytics.none'), value: '' }, ]} placeholder={t('settings.analytics.selectChatbot')} onSelect={(chatbot: { label: string; value: string }) => { setSelectedChatbot( chatbots.find((item) => item.id === chatbot.value), ); }} selectedValue={ (selectedChatbot && { label: selectedChatbot.name, value: selectedChatbot.id, }) || null } rounded="3xl" border="border" borderColor="gray-700" />
{/* Messages Analytics */}

{t('settings.analytics.messages')}

{ setMessagesFilter(selectedOption); }} selectedValue={messagesFilter ?? null} rounded="3xl" border="border" contentSize="text-sm" />
{loadingMessages ? ( ) : ( formatDate(item), ), datasets: [ { label: t('settings.analytics.messages'), data: Object.values(messagesData || {}), backgroundColor: '#7D54D1', }, ], }} legendID="legend-container-1" maxTicksLimitInX={8} isStacked={false} /> )}
{/* Token Usage Analytics */}

{t('settings.analytics.tokenUsage')}

{ setTokenUsageFilter(selectedOption); }} selectedValue={tokenUsageFilter ?? null} rounded="3xl" border="border" contentSize="text-sm" />
{loadingTokens ? ( ) : ( formatDate(item), ), datasets: [ { label: t('settings.analytics.tokenUsage'), data: Object.values(tokenUsageData || {}), backgroundColor: '#7D54D1', }, ], }} legendID="legend-container-2" maxTicksLimitInX={8} isStacked={false} /> )}
{/* Feedback Analytics */}

{t('settings.analytics.feedback')}

{ setFeedbackFilter(selectedOption); }} selectedValue={feedbackFilter ?? null} rounded="3xl" border="border" contentSize="text-sm" />
{loadingFeedback ? ( ) : ( formatDate(item), ), datasets: [ { label: t('settings.analytics.positiveFeedback'), data: Object.values(feedbackData || {}).map( (item) => item.positive, ), backgroundColor: '#7D54D1', }, { label: t('settings.analytics.negativeFeedback'), data: Object.values(feedbackData || {}).map( (item) => item.negative, ), backgroundColor: '#FF6384', }, ], }} legendID="legend-container-3" maxTicksLimitInX={8} isStacked={false} /> )}
); } type AnalyticsChartProps = { data: ChartData<'bar'>; legendID: string; maxTicksLimitInX: number; isStacked: boolean; }; function AnalyticsChart({ data, legendID, maxTicksLimitInX, isStacked, }: AnalyticsChartProps) { const options = { responsive: true, maintainAspectRatio: false, plugins: { legend: { display: false, }, htmlLegend: { containerID: legendID, }, }, scales: { x: { grid: { lineWidth: 0.2, color: '#C4C4C4', }, border: { width: 0.2, color: '#C4C4C4', }, ticks: { maxTicksLimit: maxTicksLimitInX, }, stacked: isStacked, }, y: { grid: { lineWidth: 0.2, color: '#C4C4C4', }, border: { width: 0.2, color: '#C4C4C4', }, stacked: isStacked, }, }, }; return ; }