import React, { useCallback, useEffect, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useSelector } from 'react-redux'; import userService from '../api/services/userService'; import ChevronRight from '../assets/chevron-right.svg'; import CopyButton from '../components/CopyButton'; import SkeletonLoader from '../components/SkeletonLoader'; import { useLoaderState } from '../hooks'; import { selectToken } from '../preferences/preferenceSlice'; import { LogData } from './types'; type LogsProps = { agentId?: string; tableHeader?: string; }; export default function Logs({ agentId, tableHeader }: LogsProps) { const token = useSelector(selectToken); const [logs, setLogs] = useState([]); const [page, setPage] = useState(1); const [hasMore, setHasMore] = useState(true); const [loadingLogs, setLoadingLogs] = useLoaderState(true); const fetchLogs = async () => { setLoadingLogs(true); try { const response = await userService.getLogs( { page: page, api_key_id: agentId, page_size: 10, }, token, ); 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(() => { if (hasMore) fetchLogs(); }, [page, agentId]); return (
); } type LogsTableProps = { logs: LogData[]; setPage: React.Dispatch>; loading: boolean; tableHeader?: string; }; function LogsTable({ logs, setPage, loading, tableHeader }: 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 (

{tableHeader ? tableHeader : 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)}

); }