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 [logsByPage, setLogsByPage] = useState>({}); const [page, setPage] = useState(1); const [hasMore, setHasMore] = useState(true); const [loadingLogs, setLoadingLogs] = useLoaderState(true); const logs = Object.values(logsByPage).flat(); const fetchLogs = async () => { if (logsByPage[page] && logsByPage[page].length > 0) return; 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 data = await response.json(); setLogsByPage((prev) => ({ ...prev, [page]: data.logs, })); setHasMore(data.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 === logId) { setOpenLogId(null); } else { 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, isOpen, onToggle, }: { log: LogData; isOpen: boolean; 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 (
onToggle(log.id)} className={`flex cursor-pointer flex-row items-start gap-2 p-2 px-4 py-3 text-gray-900 ${ isOpen ? 'rounded-t-xl bg-[#F1F1F1] dark:bg-[#1B1B1B]' : '' }`} > Expand log entry

{`${log.timestamp}`}

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

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

{isOpen && (
              {JSON.stringify(filteredLog, null, 2)}
            
)}
); }