import { useEffect, useMemo, useState } from "react"; import useSWR from "swr"; import { Button, Tabs, Tab } from "@mui/material"; import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'; import { DatePicker } from '@mui/x-date-pickers/DatePicker'; import { DemoContainer } from '@mui/x-date-pickers/internals/demo'; import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider'; import dayjs, { Dayjs } from "dayjs"; import isBetween from 'dayjs/plugin/isBetween'; import { useAppContext, Message } from "../../contexts/AppContext"; import useFetch from "../../hooks/useFetch"; import { ChatHistory } from "./ChatHistory"; import { Statistics } from "./Statistics"; dayjs.extend(isBetween); interface AnalyticsMessage extends Omit { chat_id: string; user_id: string; flow_id: string; timestamp: string; msg_type: 'user' | 'system' | 'feedback' | 'error'; } export interface DialogItem { msg_id: string; chat_id: string; user_id: string; flow_id?: string; timestamp: string; user_message: AnalyticsMessage; system_message?: AnalyticsMessage; has_error: boolean; feedback_type?: 'up' | 'down'; } interface CustomTabPanelProps { children: React.ReactNode; value: number; index: number; } export const CustomTabPanel: React.FC = ({ children, value, index }) => { return ( ); }; export default function Analytics() { const { flows, setAppSection, setHeaderToolbar } = useAppContext(); const { fetcher } = useFetch(); const [page, setPage] = useState(1); const [startDate, setStartDate] = useState(dayjs().subtract(7, 'day')); const [endDate, setEndDate] = useState(null); const itemsPerPage = 20; const [tabValue, setTabValue] = useState(() => { const savedTab = sessionStorage.getItem('analyticsTab'); return savedTab ? parseInt(savedTab) : 0; }); useEffect(() => { sessionStorage.setItem('analyticsTab', tabValue.toString()); }, [tabValue]); const { data: rawMessages, isLoading } = useSWR( ["analytics_chat_history", startDate, endDate], async ([_, start, end]) => { const params: { start?: string; end?: string } = {}; if (start) params.start = (start as Dayjs).format('YYYY-MM-DD'); if (end) params.end = (end as Dayjs).format('YYYY-MM-DD'); const queryString = new URLSearchParams(params).toString(); const data = await fetcher(`/api/analytics/chat-dialogs?${queryString}`); return data; } ); const dialogs: DialogItem[] = useMemo(() => { if (!rawMessages) return []; const dialogsMap: Record = {}; rawMessages.forEach((message: AnalyticsMessage) => { const msgId = message.msg_id; if (!msgId) return; if (!dialogsMap[msgId]) { dialogsMap[msgId] = {}; } if (message.msg_type === 'user') { dialogsMap[msgId].user = message; } else if (message.msg_type === 'system') { dialogsMap[msgId].system = message; } }); const result: DialogItem[] = []; Object.entries(dialogsMap).forEach(([msgId, pair]) => { if (pair.user) { const dialog: DialogItem = { msg_id: msgId, chat_id: pair.user.chat_id, user_id: pair.user.user_id, flow_id: pair.user.flow_id, timestamp: pair.user.timestamp, user_message: pair.user, system_message: pair.system, has_error: pair.system?.error || false, feedback_type: pair.system?.feedback === 'thumbs_up' ? 'up' : pair.system?.feedback === 'thumbs_down' ? 'down' : undefined }; result.push(dialog); } }); return result.sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime()); }, [rawMessages]); useEffect(() => { setAppSection?.('Analytics'); const FilterToolbar = () => (
); setHeaderToolbar?.(); return () => setHeaderToolbar?.(null); }, [setAppSection, setHeaderToolbar]); useEffect(() => { setPage(1); }, [startDate, endDate]); const paginatedDialogs = dialogs.slice((page - 1) * itemsPerPage, page * itemsPerPage); const pagesNum = Math.ceil(dialogs.length / itemsPerPage); return (
setTabValue(newValue)} sx={{ '.dark & .MuiTab-root': { color: 'white', transition: 'color 0.3s ease', '&:hover': { color: 'white' }, '&.Mui-selected': { color: 'white' }, '&.Mui-disabled': { color: 'white' } }, '.dark & .MuiTabs-indicator': { backgroundColor: 'white', height: 'white' } }} >
, newPage: number) => setPage(newPage)} />
); }