diff --git a/components/project/traces/trace-filter.tsx b/components/project/traces/trace-filter.tsx index 5fd04298..68173b48 100644 --- a/components/project/traces/trace-filter.tsx +++ b/components/project/traces/trace-filter.tsx @@ -36,7 +36,7 @@ import { SpanAttributes } from "@/lib/ts_sdk_constants"; import ClearIcon from "@mui/icons-material/Clear"; import { Check, ChevronsUpDown, MinusCircle, PlusCircle } from "lucide-react"; import Link from "next/link"; -import { useEffect, useState } from "react"; +import { useState } from "react"; import VendorDropdown from "./vendor-dropdown"; export default function TraceFilter({ @@ -62,18 +62,19 @@ export default function TraceFilter({ model: string; setModel: (model: string) => void; }) { - useEffect(() => { - if (!open) { - setFilters((prev: PropertyFilter[]) => - prev.filter( - (filter) => - filter.value !== null && - filter.value !== undefined && - filter.value !== "" - ) - ); - } - }, [open, setFilters]); + // TODO(Karthik): Commenting for now. Unsure if this is needed. + // useEffect(() => { + // if (!open) { + // setFilters((prev: PropertyFilter[]) => + // prev.filter( + // (filter) => + // filter.value !== null && + // filter.value !== undefined && + // filter.value !== "" + // ) + // ); + // } + // }, [open, setFilters]); const handleFilterChange = ( filter: PropertyFilter, diff --git a/components/project/traces/traces-table.tsx b/components/project/traces/traces-table.tsx index 68f1b08e..dd55f18b 100644 --- a/components/project/traces/traces-table.tsx +++ b/components/project/traces/traces-table.tsx @@ -1,5 +1,4 @@ import { SetupInstructions } from "@/components/shared/setup-instructions"; -import { Spinner } from "@/components/shared/spinner"; import { Button } from "@/components/ui/button"; import { DropdownMenu, @@ -7,6 +6,7 @@ import { DropdownMenuContent, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; +import { Separator } from "@/components/ui/separator"; import { Table, TableBody, @@ -15,7 +15,9 @@ import { TableHeader, TableRow, } from "@/components/ui/table"; +import { HOW_TO_GROUP_RELATED_OPERATIONS } from "@/lib/constants"; import { Trace } from "@/lib/trace_util"; +import { cn } from "@/lib/utils"; import { ResetIcon } from "@radix-ui/react-icons"; import { ColumnDef, @@ -25,7 +27,9 @@ import { VisibilityState, } from "@tanstack/react-table"; import { ChevronDown } from "lucide-react"; +import Link from "next/link"; import { useEffect, useMemo, useState } from "react"; +import TraceRowSkeleton from "./trace-row-skeleton"; import { TraceSheet } from "./trace-sheet"; import { TracesPageSkeleton } from "./traces"; @@ -34,6 +38,7 @@ interface TracesTableProps { columns: ColumnDef[]; data: TData[]; loading?: boolean; + fetching?: boolean; paginationLoading?: boolean; scrollableDivRef?: React.RefObject; } @@ -43,30 +48,44 @@ export function TracesTable({ columns, data, loading, + fetching, paginationLoading, scrollableDivRef, }: TracesTableProps) { - const [tableState, setTableState] = useState({}); + const [tableState, setTableState] = useState({ + pagination: { + pageIndex: 0, + pageSize: 100, + }, + }); const [columnVisibility, setColumnVisibility] = useState({}); const [openDropdown, setOpenDropdown] = useState(false); const [openSheet, setOpenSheet] = useState(false); const [selectedTrace, setSelectedTrace] = useState(null); useEffect(() => { - // fetch preferences from local storage if (typeof window !== "undefined") { - const initState = window.localStorage.getItem( - "preferences.traces.table-view" - ); - const parsedInitState = JSON.parse(initState || "{}"); - const pagination = { - pageIndex: 0, //initial page index - pageSize: 100, //default page size - }; - parsedInitState.pagination = pagination; - setTableState(parsedInitState); - if (parsedInitState.columnVisibility) - setColumnVisibility(parsedInitState.columnVisibility); + try { + const storedState = window.localStorage.getItem( + "preferences.traces.table-view" + ); + if (storedState) { + const parsedState = JSON.parse(storedState); + setTableState((prevState: any) => ({ + ...prevState, + ...parsedState, + pagination: { + ...prevState.pagination, + ...parsedState.pagination, + }, + })); + if (parsedState.columnVisibility) { + setColumnVisibility(parsedState.columnVisibility); + } + } + } catch (error) { + console.error("Error parsing stored table state:", error); + } } }, []); @@ -76,14 +95,19 @@ export function TracesTable({ getCoreRowModel: getCoreRowModel(), initialState: { ...tableState, + pagination: tableState.pagination, columnVisibility, }, state: { ...tableState, + pagination: tableState.pagination, columnVisibility, }, - onStateChange: (newState) => { - setTableState(newState); + onStateChange: (newState: any) => { + setTableState((prevState: any) => ({ + ...newState, + pagination: newState.pagination || prevState.pagination, + })); const currState = table.getState(); localStorage.setItem( "preferences.traces.table-view", @@ -100,6 +124,7 @@ export function TracesTable({ }, enableColumnResizing: true, columnResizeMode: "onChange", + manualPagination: true, // Add this if you're handling pagination yourself }); const columnSizeVars = useMemo(() => { @@ -116,46 +141,70 @@ export function TracesTable({ return ( <> {!loading && data && data.length > 0 && ( -
- - - - - - {table - .getAllColumns() - .filter((column) => column.getCanHide()) - .map((column) => { - return ( - setOpenDropdown(true)} - onCheckedChange={(value) => - column.toggleVisibility(!!value) - } - > - {column.columnDef.header?.toString()} - - ); - })} - - - +
+
+

+ {fetching + ? "Fetching traces..." + : `Fetched the last ${data.length} traces`} +

+
+ Seeing related spans as separate rows?{" "} + + Learn how to group spans + +
+
+
+ + + + + + {table + .getAllColumns() + .filter((column) => column.getCanHide()) + .map((column) => { + return ( + setOpenDropdown(true)} + onCheckedChange={(value) => + column.toggleVisibility(!!value) + } + > + {column.columnDef.header?.toString()} + + ); + })} + + + +
)}
({ /> )} {paginationLoading && ( -
- +
+ + {Array.from({ length: 2 }).map((_, index) => ( + + ))}
)}
diff --git a/components/project/traces/traces.tsx b/components/project/traces/traces.tsx index 5de46f10..045d0228 100644 --- a/components/project/traces/traces.tsx +++ b/components/project/traces/traces.tsx @@ -4,7 +4,7 @@ import { HoverCell } from "@/components/shared/hover-cell"; import { Info } from "@/components/shared/info"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; -import { HOW_TO_GROUP_RELATED_OPERATIONS, PAGE_SIZE } from "@/lib/constants"; +import { PAGE_SIZE } from "@/lib/constants"; import { PropertyFilter } from "@/lib/services/query_builder_service"; import { processTrace, Trace } from "@/lib/trace_util"; import { correctTimestampFormat } from "@/lib/trace_utils"; @@ -12,9 +12,8 @@ import { cn, formatDateTime } from "@/lib/utils"; import FilterListIcon from "@mui/icons-material/FilterList"; import { ColumnDef } from "@tanstack/react-table"; import { XIcon } from "lucide-react"; -import Link from "next/link"; import { useParams } from "next/navigation"; -import { useEffect, useState } from "react"; +import { useCallback, useEffect, useState } from "react"; import { useBottomScrollListener } from "react-bottom-scroll-listener"; import { useQuery } from "react-query"; import { toast } from "sonner"; @@ -29,22 +28,22 @@ export default function Traces({ email }: { email: string }) { const [page, setPage] = useState(1); const [totalPages, setTotalPages] = useState(1); const [currentData, setCurrentData] = useState([]); - const [showLoader, setShowLoader] = useState(false); + const [showBottomLoader, setShowBottomLoader] = useState(false); + const [showFreshLoading, setShowFreshLoading] = useState(false); const [filters, setFilters] = useState([]); const [enableFetch, setEnableFetch] = useState(false); const [utcTime, setUtcTime] = useState(false); const [isTraceFilterOpen, setIsTraceFilterOpen] = useState(false); - const [groupSpans, setGroupSpans] = useState(true); const [userId, setUserId] = useState(""); const [promptId, setPromptId] = useState(""); const [model, setModel] = useState(""); const [expandedView, setExpandedView] = useState(false); useEffect(() => { - // setShowLoader(true); setCurrentData([]); setPage(1); setTotalPages(1); + setShowFreshLoading(true); setEnableFetch(true); // fetch preferences from local storage @@ -52,14 +51,24 @@ export default function Traces({ email }: { email: string }) { const utc = window.localStorage.getItem("preferences.timestamp.utc"); setUtcTime(utc === "true"); - const group = window.localStorage.getItem("preferences.group"); - setGroupSpans(group === "true"); - const expanded = window.localStorage.getItem("preferences.expanded"); setExpandedView(expanded === "true"); } }, [filters]); + useEffect(() => { + const handleFocusChange = () => { + setPage(1); + setEnableFetch(true); + }; + + window.addEventListener("focus", handleFocusChange); + + return () => { + window.removeEventListener("focus", handleFocusChange); + }; + }, []); + const columns: ColumnDef[] = [ { accessorKey: "start_time", @@ -316,25 +325,24 @@ export default function Traces({ email }: { email: string }) { return; } if (page <= totalPages) { - setShowLoader(true); + setShowBottomLoader(true); fetchTraces.refetch(); } }); - const fetchTraces = useQuery({ - queryKey: ["fetch-traces-query"], - queryFn: async () => { + const fetchTracesCall = useCallback( + async (pageNum: number) => { const apiEndpoint = "/api/traces"; const body = { - page, + page: pageNum, pageSize: PAGE_SIZE, projectId: project_id, filters: { filters: filters, operation: "OR", }, - group: groupSpans, + group: true, }; const response = await fetch(apiEndpoint, { @@ -348,39 +356,41 @@ export default function Traces({ email }: { email: string }) { const error = await response.json(); throw new Error(error?.message || "Failed to fetch traces"); } - const result = await response.json(); - return result; + return await response.json(); }, + [project_id, filters] + ); + + const fetchTraces = useQuery({ + queryKey: ["fetch-traces-query", page], + queryFn: () => fetchTracesCall(page), onSuccess: (data) => { - // Get the newly fetched data and metadata const newData = data?.traces?.result || []; const metadata = data?.traces?.metadata || {}; - // Update the total pages and current page number setTotalPages(parseInt(metadata?.total_pages) || 1); if (parseInt(metadata?.page) <= parseInt(metadata?.total_pages)) { setPage(parseInt(metadata?.page) + 1); } - // transform the data const transformedNewData = newData.map((trace: any) => processTrace(trace) ); - // Merge the new data with the existing data - if (currentData.length > 0) { - const updatedData = [...currentData, ...transformedNewData]; - setCurrentData(updatedData); - } else { + if (page === 1) { setCurrentData(transformedNewData); + } else { + setCurrentData((prevData: any) => [...prevData, ...transformedNewData]); } setEnableFetch(false); - setShowLoader(false); + setShowFreshLoading(false); + setShowBottomLoader(false); }, onError: (error) => { setEnableFetch(false); - setShowLoader(false); + setShowFreshLoading(false); + setShowBottomLoader(false); toast.error("Failed to fetch traces", { description: error instanceof Error ? error.message : String(error), }); @@ -414,7 +424,7 @@ export default function Traces({ email }: { email: string }) { {FILTERS.map((item, i) => (
filter.value === item.key)} onCheckedChange={(checked) => { @@ -495,37 +505,6 @@ export default function Traces({ email }: { email: string }) {
-
-

Don't Group

- { - setGroupSpans(check); - - // Save the preference in local storage - if (typeof window !== "undefined") { - window.localStorage.setItem( - "preferences.group", - check ? "true" : "false" - ); - toast.success("Preferences updated."); - } - }} - /> -
-

Group Spans by Trace ID

- - - Learn More - -
-

Compress

, @@ -11,7 +11,7 @@ const Switch = React.forwardRef< >(({ className, ...props }, ref) => ( -)) -Switch.displayName = SwitchPrimitives.Root.displayName +)); +Switch.displayName = SwitchPrimitives.Root.displayName; -export { Switch } +export { Switch }; diff --git a/lib/trace_util.ts b/lib/trace_util.ts index 862d36af..502fc4dd 100644 --- a/lib/trace_util.ts +++ b/lib/trace_util.ts @@ -2,6 +2,7 @@ import { calculateTotalTime, convertTracesToHierarchy } from "./trace_utils"; import { calculatePriceFromUsage } from "./utils"; export interface Trace { + id: string; status: string; namespace: string; user_ids: string[]; @@ -247,6 +248,7 @@ export function processTrace(trace: any): Trace { // construct the response object const result: Trace = { + id: trace[0]?.trace_id, status: status, namespace: traceHierarchy[0].name, user_ids: userIds,