import React, { useState, useEffect, useRef, useCallback } from 'react'; import { useTranslation } from 'react-i18next'; import userService from '../api/services/userService'; import ChevronRight from '../assets/chevron-right.svg'; import CopyButton from '../components/CopyButton'; import Dropdown from '../components/Dropdown'; import SkeletonLoader from '../components/SkeletonLoader'; import { useLoaderState } from '../hooks'; import { APIKeyData, LogData } from './types'; export default function Logs() { const { t } = useTranslation(); const [chatbots, setChatbots] = useState([]); const [selectedChatbot, setSelectedChatbot] = useState(); const [logs, setLogs] = useState([]); const [page, setPage] = useState(1); const [hasMore, setHasMore] = useState(true); const [loadingChatbots, setLoadingChatbots] = useLoaderState(true); const [loadingLogs, setLoadingLogs] = useLoaderState(true); const fetchChatbots = async () => { setLoadingChatbots(true); 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); } finally { setLoadingChatbots(false); } }; const fetchLogs = async () => { setLoadingLogs(true); try { const response = await userService.getLogs({ page: page, api_key_id: selectedChatbot?.id, page_size: 10, }); if (!response.ok) { throw new Error('Failed to fetch logs'); } const olderLogs = await response.json(); setLogs((prevLogs) => [...prevLogs, ...olderLogs.logs]); setHasMore(olderLogs.has_more); } catch (error) { console.error(error); } finally { setLoadingLogs(false); } }; useEffect(() => { fetchChatbots(); }, []); useEffect(() => { if (hasMore) fetchLogs(); }, [page, selectedChatbot]); return (
{loadingChatbots ? ( ) : (
({ label: chatbot.name, value: chatbot.id, })), { label: t('settings.logs.none'), value: '' }, ]} placeholder={t('settings.logs.selectChatbot')} onSelect={(chatbot: { label: string; value: string }) => { setSelectedChatbot( chatbots.find((item) => item.id === chatbot.value), ); setLogs([]); setPage(1); setHasMore(true); }} selectedValue={ (selectedChatbot && { label: selectedChatbot.name, value: selectedChatbot.id, }) || null } rounded="3xl" border="border" darkBorderColor="dim-gray" />
)}
); } type LogsTableProps = { logs: LogData[]; setPage: React.Dispatch>; loading: boolean; }; function LogsTable({ logs, setPage, loading }: LogsTableProps) { const { t } = useTranslation(); const observerRef = useRef(null); const [openLogId, setOpenLogId] = useState(null); const handleLogToggle = (logId: string) => { if (openLogId && openLogId !== logId) { // If a different log is being opened, close the current one const currentOpenLog = document.getElementById( openLogId, ) as HTMLDetailsElement; if (currentOpenLog) { currentOpenLog.open = false; } } setOpenLogId(logId); }; const firstObserver = useCallback((node: HTMLDivElement | null) => { if (observerRef.current) { observerRef.current.disconnect(); } if (!node) return; observerRef.current = new IntersectionObserver((entries) => { if (entries[0].isIntersecting) { setPage((prev) => prev + 1); } }); observerRef.current.observe(node); }, []); useEffect(() => { return () => { if (observerRef.current) { observerRef.current.disconnect(); } }; }, []); return (

{t('settings.logs.tableHeader')}

{logs?.map((log, index) => { if (index === logs.length - 1) { return (
); } else return ; })} {loading && }
); } function Log({ log, onToggle, }: { log: LogData; onToggle: (id: string) => void; }) { const { t } = useTranslation(); const logLevelColor = { info: 'text-green-500', error: 'text-red-500', warning: 'text-yellow-500', }; const { id, action, timestamp, ...filteredLog } = log; return (
{ if ((e.target as HTMLDetailsElement).open) { onToggle(log.id); } }} > Expand log entry

{`${log.timestamp}`}

{`[${log.action}]`}

{`${log.question}`.length > 250 ? `${log.question.substring(0, 250)}...` : log.question}

{JSON.stringify(filteredLog, null, 2)}

); }