From a89efab5539e785c1c7b82a71ac5791371f47bf1 Mon Sep 17 00:00:00 2001 From: Manan Tank Date: Wed, 15 Jan 2025 02:37:29 +0530 Subject: [PATCH] [TOOL-2473] Dashboard: Update RPC method analytics chart to show % values --- apps/dashboard/src/@/components/ui/chart.tsx | 11 +- .../RpcMethodBarChartCard.stories.tsx | 17 +- .../RpcMethodBarChartCardUI.tsx | 173 ++++++++++-------- 3 files changed, 124 insertions(+), 77 deletions(-) diff --git a/apps/dashboard/src/@/components/ui/chart.tsx b/apps/dashboard/src/@/components/ui/chart.tsx index 1f70c0babc6..16aacc0eb2d 100644 --- a/apps/dashboard/src/@/components/ui/chart.tsx +++ b/apps/dashboard/src/@/components/ui/chart.tsx @@ -113,7 +113,10 @@ const ChartTooltipContent = React.forwardRef< indicator?: "line" | "dot" | "dashed"; nameKey?: string; labelKey?: string; - valueFormatter?: (v: unknown) => string | undefined; + valueFormatter?: ( + v: unknown, + payLoad: unknown, + ) => React.ReactNode | undefined; } >( ( @@ -233,7 +236,7 @@ const ChartTooltipContent = React.forwardRef< )}
@@ -243,10 +246,10 @@ const ChartTooltipContent = React.forwardRef< {itemConfig?.label || item.name}
- {item.value && ( + {item.value !== undefined && ( {valueFormatter - ? valueFormatter(item.value) || item.value + ? valueFormatter(item.value, item) || item.value : item.value} )} diff --git a/apps/dashboard/src/app/team/[team_slug]/[project_slug]/components/RpcMethodBarChartCard/RpcMethodBarChartCard.stories.tsx b/apps/dashboard/src/app/team/[team_slug]/[project_slug]/components/RpcMethodBarChartCard/RpcMethodBarChartCard.stories.tsx index 1bacf70f93d..5dfadfc3615 100644 --- a/apps/dashboard/src/app/team/[team_slug]/[project_slug]/components/RpcMethodBarChartCard/RpcMethodBarChartCard.stories.tsx +++ b/apps/dashboard/src/app/team/[team_slug]/[project_slug]/components/RpcMethodBarChartCard/RpcMethodBarChartCard.stories.tsx @@ -55,17 +55,32 @@ const commonMethods = [ "eth_getBalance", "eth_getTransactionReceipt", "eth_blockNumber", + "eth_getLogs", + "eth_getTransactionByHash", + "eth_getCode", + "eth_getTransactionCount", + "eth_getStorageAt", + "eth_gasPrice", + "eth_getBlockByHash", + "eth_getProof", + "net_version", ]; function Component() { return (
- + + + + + diff --git a/apps/dashboard/src/app/team/[team_slug]/[project_slug]/components/RpcMethodBarChartCard/RpcMethodBarChartCardUI.tsx b/apps/dashboard/src/app/team/[team_slug]/[project_slug]/components/RpcMethodBarChartCard/RpcMethodBarChartCardUI.tsx index 6f174a29ab6..81fcaf4ba63 100644 --- a/apps/dashboard/src/app/team/[team_slug]/[project_slug]/components/RpcMethodBarChartCard/RpcMethodBarChartCardUI.tsx +++ b/apps/dashboard/src/app/team/[team_slug]/[project_slug]/components/RpcMethodBarChartCard/RpcMethodBarChartCardUI.tsx @@ -6,14 +6,13 @@ import { ChartTooltip, ChartTooltipContent, } from "@/components/ui/chart"; -import { formatTickerNumber } from "lib/format-utils"; +import { formatDate } from "date-fns"; import { useMemo } from "react"; import { Bar, CartesianGrid, BarChart as RechartsBarChart, XAxis, - YAxis, } from "recharts"; import type { RpcMethodStats } from "types/analytics"; import { EmptyStateCard } from "../../../../components/Analytics/EmptyStateCard"; @@ -21,66 +20,95 @@ import { EmptyStateCard } from "../../../../components/Analytics/EmptyStateCard" export function RpcMethodBarChartCardUI({ rawData, }: { rawData: RpcMethodStats[] }) { - const uniqueMethods = useMemo( - () => Array.from(new Set(rawData.map((d) => d.evmMethod))), - [rawData], - ); - const uniqueDates = useMemo( - () => Array.from(new Set(rawData.map((d) => d.date))), - [rawData], - ); + const maxMethodsToDisplay = 10; + + const { data, methodsToDisplay, chartConfig } = useMemo(() => { + const dateToValueMap: Map> = new Map(); + const methodNameToCountMap: Map = new Map(); + + for (const dataItem of rawData) { + const { date, evmMethod, count } = dataItem; + let dateRecord = dateToValueMap.get(date); - const data = useMemo(() => { - return uniqueDates.map((date) => { - const dateData: { [key: string]: string | number } = { date }; - for (const method of uniqueMethods) { - const methodData = rawData.find( - (d) => d.date === date && d.evmMethod === method, - ); - dateData[method] = methodData?.count ?? 0; + if (!dateRecord) { + dateRecord = {}; + dateToValueMap.set(date, dateRecord); } - // If we have too many methods to display well, add "other" and group the lowest keys for each time period - if (uniqueMethods.length > 5) { - // If we haven't added "other" as a key yet, add it - if (!uniqueMethods.includes("Other")) { - uniqueMethods.push("Other"); - } + dateRecord[evmMethod] = (dateRecord[evmMethod] || 0) + count; + methodNameToCountMap.set( + evmMethod, + (methodNameToCountMap.get(evmMethod) || 0) + count, + ); + } + + // remove dates without with no data ( all methods have zero count ) + for (const [date, value] of dateToValueMap.entries()) { + const isAllZero = Object.values(value).every((v) => v === 0); + if (isAllZero) { + dateToValueMap.delete(date); + } + } + + // sort methods by count (highest count first) - remove the ones with 0 count + const sortedMethodsByCount = Array.from(methodNameToCountMap.entries()) + .sort((a, b) => b[1] - a[1]) + .filter((x) => x[1] > 0); + + const methodsToDisplayArray = sortedMethodsByCount + .slice(0, maxMethodsToDisplay) + .map(([method]) => method); + const methodsToDisplay = new Set(methodsToDisplayArray); - // Sort the methods by their count for the time period - const sortedMethods = uniqueMethods - .filter((m) => m !== "Other") - .sort( - (a, b) => - ((dateData[b] as number) ?? 0) - ((dateData[a] as number) ?? 0), - ); - - dateData.Other = 0; - for (const method of sortedMethods.slice(5, sortedMethods.length)) { - dateData.Other += (dateData[method] as number) ?? 0; - delete dateData[method]; + // loop over each entry in dateToValueMap + // replace the method that is not in methodsToDisplay with "Other" + // add total key that is the sum of all methods + for (const dateRecord of dateToValueMap.values()) { + // calculate total + let totalCountOfDay = 0; + for (const count of Object.values(dateRecord)) { + totalCountOfDay += count; + } + + for (const method of Object.keys(dateRecord)) { + if (!methodsToDisplay.has(method)) { + dateRecord.Other = + (dateRecord.Other || 0) + (dateRecord[method] || 0); + delete dateRecord[method]; } } - return dateData; - }); - }, [uniqueDates, uniqueMethods, rawData]); - - const config: ChartConfig = useMemo(() => { - const config: ChartConfig = {}; - for (const method of uniqueMethods) { - config[method] = { + + dateRecord.total = totalCountOfDay; + } + + const returnValue: Array> = []; + for (const [date, value] of dateToValueMap.entries()) { + returnValue.push({ date, ...value }); + } + + const chartConfig: ChartConfig = {}; + for (const method of methodsToDisplayArray) { + chartConfig[method] = { label: method, }; } - return config; - }, [uniqueMethods]); - - if ( - data.length === 0 || - data.every((date) => - Object.keys(date).every((k) => k === "date" || date[k] === 0), - ) - ) { + + // if we need to display "Other" methods + if (sortedMethodsByCount.length > maxMethodsToDisplay) { + chartConfig.Other = { + label: "Other", + }; + methodsToDisplayArray.push("Other"); + } + + return { + data: returnValue, + methodsToDisplay: methodsToDisplayArray, + chartConfig, + }; + }, [rawData]); + + if (data.length === 0) { return ; } @@ -93,7 +121,7 @@ export function RpcMethodBarChartCardUI({ + - formatTickerNumber(value)} - /> + { - return new Date(value).toLocaleDateString("en-US", { - month: "short", - day: "numeric", - year: "numeric", - }); + labelFormatter={(d) => formatDate(new Date(d), "MMM d")} + valueFormatter={(_value, _item) => { + const value = _value as number; + const payload = _item as { + payload: { + total: number; + }; + }; + return ( + + {`${((value / payload.payload.total) * 100).toFixed(2)}`} + % + + ); }} - valueFormatter={(v: unknown) => - formatTickerNumber(v as number) - } /> } /> - {uniqueMethods.map((method, idx) => ( + {methodsToDisplay.map((method, idx) => (