@@ -6,81 +6,109 @@ import {
66 ChartTooltip ,
77 ChartTooltipContent ,
88} from "@/components/ui/chart" ;
9- import { formatTickerNumber } from "lib/format-utils " ;
9+ import { formatDate } from "date-fns " ;
1010import { useMemo } from "react" ;
1111import {
1212 Bar ,
1313 CartesianGrid ,
1414 BarChart as RechartsBarChart ,
1515 XAxis ,
16- YAxis ,
1716} from "recharts" ;
1817import type { RpcMethodStats } from "types/analytics" ;
1918import { EmptyStateCard } from "../../../../components/Analytics/EmptyStateCard" ;
2019
2120export function RpcMethodBarChartCardUI ( {
2221 rawData,
2322} : { rawData : RpcMethodStats [ ] } ) {
24- const uniqueMethods = useMemo (
25- ( ) => Array . from ( new Set ( rawData . map ( ( d ) => d . evmMethod ) ) ) ,
26- [ rawData ] ,
27- ) ;
28- const uniqueDates = useMemo (
29- ( ) => Array . from ( new Set ( rawData . map ( ( d ) => d . date ) ) ) ,
30- [ rawData ] ,
31- ) ;
23+ const maxMethodsToDisplay = 10 ;
24+
25+ const { data, methodsToDisplay, chartConfig } = useMemo ( ( ) => {
26+ const dateToValueMap : Map < string , Record < string , number > > = new Map ( ) ;
27+ const methodNameToCountMap : Map < string , number > = new Map ( ) ;
28+
29+ for ( const dataItem of rawData ) {
30+ const { date, evmMethod, count } = dataItem ;
31+ let dateRecord = dateToValueMap . get ( date ) ;
3232
33- const data = useMemo ( ( ) => {
34- return uniqueDates . map ( ( date ) => {
35- const dateData : { [ key : string ] : string | number } = { date } ;
36- for ( const method of uniqueMethods ) {
37- const methodData = rawData . find (
38- ( d ) => d . date === date && d . evmMethod === method ,
39- ) ;
40- dateData [ method ] = methodData ?. count ?? 0 ;
33+ if ( ! dateRecord ) {
34+ dateRecord = { } ;
35+ dateToValueMap . set ( date , dateRecord ) ;
4136 }
4237
43- // If we have too many methods to display well, add "other" and group the lowest keys for each time period
44- if ( uniqueMethods . length > 5 ) {
45- // If we haven't added "other" as a key yet, add it
46- if ( ! uniqueMethods . includes ( "Other" ) ) {
47- uniqueMethods . push ( "Other" ) ;
48- }
38+ dateRecord [ evmMethod ] = ( dateRecord [ evmMethod ] || 0 ) + count ;
39+ methodNameToCountMap . set (
40+ evmMethod ,
41+ ( methodNameToCountMap . get ( evmMethod ) || 0 ) + count ,
42+ ) ;
43+ }
44+
45+ // remove dates without with no data ( all methods have zero count )
46+ for ( const [ date , value ] of dateToValueMap . entries ( ) ) {
47+ const isAllZero = Object . values ( value ) . every ( ( v ) => v === 0 ) ;
48+ if ( isAllZero ) {
49+ dateToValueMap . delete ( date ) ;
50+ }
51+ }
52+
53+ // sort methods by count (highest count first) - remove the ones with 0 count
54+ const sortedMethodsByCount = Array . from ( methodNameToCountMap . entries ( ) )
55+ . sort ( ( a , b ) => b [ 1 ] - a [ 1 ] )
56+ . filter ( ( x ) => x [ 1 ] > 0 ) ;
57+
58+ const methodsToDisplayArray = sortedMethodsByCount
59+ . slice ( 0 , maxMethodsToDisplay )
60+ . map ( ( [ method ] ) => method ) ;
61+ const methodsToDisplay = new Set ( methodsToDisplayArray ) ;
4962
50- // Sort the methods by their count for the time period
51- const sortedMethods = uniqueMethods
52- . filter ( ( m ) => m !== "Other" )
53- . sort (
54- ( a , b ) =>
55- ( ( dateData [ b ] as number ) ?? 0 ) - ( ( dateData [ a ] as number ) ?? 0 ) ,
56- ) ;
57-
58- dateData . Other = 0 ;
59- for ( const method of sortedMethods . slice ( 5 , sortedMethods . length ) ) {
60- dateData . Other += ( dateData [ method ] as number ) ?? 0 ;
61- delete dateData [ method ] ;
63+ // loop over each entry in dateToValueMap
64+ // replace the method that is not in methodsToDisplay with "Other"
65+ // add total key that is the sum of all methods
66+ for ( const dateRecord of dateToValueMap . values ( ) ) {
67+ // calculate total
68+ let totalCountOfDay = 0 ;
69+ for ( const count of Object . values ( dateRecord ) ) {
70+ totalCountOfDay += count ;
71+ }
72+
73+ for ( const method of Object . keys ( dateRecord ) ) {
74+ if ( ! methodsToDisplay . has ( method ) ) {
75+ dateRecord . Other =
76+ ( dateRecord . Other || 0 ) + ( dateRecord [ method ] || 0 ) ;
77+ delete dateRecord [ method ] ;
6278 }
6379 }
64- return dateData ;
65- } ) ;
66- } , [ uniqueDates , uniqueMethods , rawData ] ) ;
67-
68- const config : ChartConfig = useMemo ( ( ) => {
69- const config : ChartConfig = { } ;
70- for ( const method of uniqueMethods ) {
71- config [ method ] = {
80+
81+ dateRecord . total = totalCountOfDay ;
82+ }
83+
84+ const returnValue : Array < Record < string , string | number > > = [ ] ;
85+ for ( const [ date , value ] of dateToValueMap . entries ( ) ) {
86+ returnValue . push ( { date, ...value } ) ;
87+ }
88+
89+ const chartConfig : ChartConfig = { } ;
90+ for ( const method of methodsToDisplayArray ) {
91+ chartConfig [ method ] = {
7292 label : method ,
7393 } ;
7494 }
75- return config ;
76- } , [ uniqueMethods ] ) ;
77-
78- if (
79- data . length === 0 ||
80- data . every ( ( date ) =>
81- Object . keys ( date ) . every ( ( k ) => k === "date" || date [ k ] === 0 ) ,
82- )
83- ) {
95+
96+ // if we need to display "Other" methods
97+ if ( sortedMethodsByCount . length > maxMethodsToDisplay ) {
98+ chartConfig . Other = {
99+ label : "Other" ,
100+ } ;
101+ methodsToDisplayArray . push ( "Other" ) ;
102+ }
103+
104+ return {
105+ data : returnValue ,
106+ methodsToDisplay : methodsToDisplayArray ,
107+ chartConfig,
108+ } ;
109+ } , [ rawData ] ) ;
110+
111+ if ( data . length === 0 ) {
84112 return < EmptyStateCard metric = "RPC" link = "https://portal.thirdweb.com/" /> ;
85113 }
86114
@@ -93,7 +121,7 @@ export function RpcMethodBarChartCardUI({
93121 </ CardHeader >
94122 < CardContent className = "px-2 sm:p-6 sm:pl-0" >
95123 < ChartContainer
96- config = { config }
124+ config = { chartConfig }
97125 className = "aspect-auto h-[250px] w-full pt-6"
98126 >
99127 < RechartsBarChart
@@ -105,6 +133,7 @@ export function RpcMethodBarChartCardUI({
105133 } }
106134 >
107135 < CartesianGrid vertical = { false } />
136+
108137 < XAxis
109138 dataKey = "date"
110139 tickLine = { false }
@@ -119,36 +148,36 @@ export function RpcMethodBarChartCardUI({
119148 } ) ;
120149 } }
121150 />
122- < YAxis
123- width = { 48 }
124- tickLine = { false }
125- axisLine = { false }
126- tickFormatter = { ( value : number ) => formatTickerNumber ( value ) }
127- />
151+
128152 < ChartTooltip
129153 content = {
130154 < ChartTooltipContent
131- labelFormatter = { ( value ) => {
132- return new Date ( value ) . toLocaleDateString ( "en-US" , {
133- month : "short" ,
134- day : "numeric" ,
135- year : "numeric" ,
136- } ) ;
155+ labelFormatter = { ( d ) => formatDate ( new Date ( d ) , "MMM d" ) }
156+ valueFormatter = { ( _value , _item ) => {
157+ const value = _value as number ;
158+ const payload = _item as {
159+ payload : {
160+ total : number ;
161+ } ;
162+ } ;
163+ return (
164+ < span className = "inline-flex gap-1.5" >
165+ { `${ ( ( value / payload . payload . total ) * 100 ) . toFixed ( 2 ) } ` }
166+ < span className = "text-muted-foreground" > %</ span >
167+ </ span >
168+ ) ;
137169 } }
138- valueFormatter = { ( v : unknown ) =>
139- formatTickerNumber ( v as number )
140- }
141170 />
142171 }
143172 />
144- { uniqueMethods . map ( ( method , idx ) => (
173+ { methodsToDisplay . map ( ( method , idx ) => (
145174 < Bar
146175 key = { method }
147176 stackId = "a"
148177 dataKey = { method }
149178 radius = { [
150- idx === uniqueMethods . length - 1 ? 4 : 0 ,
151- idx === uniqueMethods . length - 1 ? 4 : 0 ,
179+ idx === methodsToDisplay . length - 1 ? 4 : 0 ,
180+ idx === methodsToDisplay . length - 1 ? 4 : 0 ,
152181 idx === 0 ? 4 : 0 ,
153182 idx === 0 ? 4 : 0 ,
154183 ] }
0 commit comments