1
1
import {
2
2
getClientTransactions ,
3
3
getInAppWalletUsage ,
4
+ getUniversalBridgeUsage ,
4
5
getUserOpUsage ,
5
6
getWalletConnections ,
6
7
getWalletUsers ,
@@ -14,6 +15,7 @@ import { redirect } from "next/navigation";
14
15
import { type WalletId , getWalletInfo } from "thirdweb/wallets" ;
15
16
import type {
16
17
InAppWalletStats ,
18
+ UniversalBridgeStats ,
17
19
WalletStats ,
18
20
WalletUserStats ,
19
21
} from "types/analytics" ;
@@ -24,7 +26,10 @@ import { PieChartCard } from "../../../../components/Analytics/PieChartCard";
24
26
25
27
import { getTeamBySlug } from "@/api/team" ;
26
28
import { GenericLoadingPage } from "@/components/blocks/skeletons/GenericLoadingPage" ;
27
- import { EmptyStateCard } from "app/(app)/team/components/Analytics/EmptyStateCard" ;
29
+ import {
30
+ EmptyStateCard ,
31
+ EmptyStateContent ,
32
+ } from "app/(app)/team/components/Analytics/EmptyStateCard" ;
28
33
import { Suspense } from "react" ;
29
34
import { TotalSponsoredChartCardUI } from "../../_components/TotalSponsoredCard" ;
30
35
import { TransactionsChartCardUI } from "../../_components/TransactionsCard" ;
@@ -100,6 +105,7 @@ async function OverviewPageContent(props: {
100
105
userOpUsage ,
101
106
clientTransactionsTimeSeries ,
102
107
clientTransactions ,
108
+ universalBridgeUsage ,
103
109
] = await Promise . all ( [
104
110
// Aggregated wallet connections
105
111
getWalletConnections ( {
@@ -148,6 +154,13 @@ async function OverviewPageContent(props: {
148
154
to : range . to ,
149
155
period : "all" ,
150
156
} ) ,
157
+ // Universal Bridge
158
+ getUniversalBridgeUsage ( {
159
+ teamId : teamId ,
160
+ from : range . from ,
161
+ to : range . to ,
162
+ period : interval ,
163
+ } ) ,
151
164
] ) ;
152
165
153
166
const isEmpty =
@@ -164,8 +177,9 @@ async function OverviewPageContent(props: {
164
177
< div className = "flex grow flex-col gap-6" >
165
178
{ walletUserStatsTimeSeries . some ( ( w ) => w . totalUsers !== 0 ) ? (
166
179
< div className = "" >
167
- < UsersChartCard
180
+ < AppHighlightsCard
168
181
userStats = { walletUserStatsTimeSeries }
182
+ volumeStats = { universalBridgeUsage }
169
183
searchParams = { searchParams }
170
184
/>
171
185
</ div >
@@ -218,70 +232,96 @@ async function OverviewPageContent(props: {
218
232
) ;
219
233
}
220
234
221
- type UserMetrics = {
222
- totalUsers : number ;
235
+ type AggregatedMetrics = {
223
236
activeUsers : number ;
224
237
newUsers : number ;
225
- returningUsers : number ;
238
+ totalVolume : number ;
239
+ feesCollected : number ;
226
240
} ;
227
241
228
- type TimeSeriesMetrics = UserMetrics & {
242
+ type TimeSeriesMetrics = AggregatedMetrics & {
229
243
date : string ;
230
244
} ;
231
245
232
246
function processTimeSeriesData (
233
247
userStats : WalletUserStats [ ] ,
248
+ volumeStats : UniversalBridgeStats [ ] ,
234
249
) : TimeSeriesMetrics [ ] {
235
250
const metrics : TimeSeriesMetrics [ ] = [ ] ;
236
251
237
- let cumulativeUsers = 0 ;
238
252
for ( const stat of userStats ) {
239
- cumulativeUsers += stat . newUsers ?? 0 ;
253
+ const volume = volumeStats
254
+ . filter ( ( v ) => v . date === stat . date && v . status === "completed" )
255
+ . reduce ( ( acc , curr ) => acc + curr . amountUsdCents / 100 , 0 ) ;
256
+
257
+ const fees = volumeStats
258
+ . filter ( ( v ) => v . date === stat . date && v . status === "completed" )
259
+ . reduce ( ( acc , curr ) => acc + curr . developerFeeUsdCents / 100 , 0 ) ;
260
+
240
261
metrics . push ( {
241
262
date : stat . date ,
242
263
activeUsers : stat . totalUsers ?? 0 ,
243
- returningUsers : stat . returningUsers ?? 0 ,
244
264
newUsers : stat . newUsers ?? 0 ,
245
- totalUsers : cumulativeUsers ,
265
+ totalVolume : volume ,
266
+ feesCollected : fees ,
246
267
} ) ;
247
268
}
248
269
249
270
return metrics ;
250
271
}
251
272
252
- function UsersChartCard ( {
273
+ function AppHighlightsCard ( {
253
274
userStats,
275
+ volumeStats,
254
276
searchParams,
255
277
} : {
256
278
userStats : WalletUserStats [ ] ;
279
+ volumeStats : UniversalBridgeStats [ ] ;
257
280
searchParams ?: { [ key : string ] : string | string [ ] | undefined } ;
258
281
} ) {
259
- const timeSeriesData = processTimeSeriesData ( userStats ) ;
282
+ const timeSeriesData = processTimeSeriesData ( userStats , volumeStats ) ;
260
283
261
284
const chartConfig = {
262
- activeUsers : { label : "Active Users" , color : "hsl(var(--chart-1))" } ,
263
- totalUsers : { label : "Total Users" , color : "hsl(var(--chart-2))" } ,
264
- newUsers : { label : "New Users" , color : "hsl(var(--chart-3))" } ,
265
- returningUsers : {
266
- label : "Returning Users" ,
285
+ totalVolume : {
286
+ label : "Total Volume" ,
287
+ color : "hsl(var(--chart-2))" ,
288
+ isCurrency : true ,
289
+ emptyContent : (
290
+ < EmptyStateContent
291
+ metric = "Payments"
292
+ description = "Onramp, swap, and bridge with thirdweb's Universal Bridge."
293
+ link = "https://portal.thirdweb.com/connect/pay/overview"
294
+ />
295
+ ) ,
296
+ } ,
297
+ feesCollected : {
298
+ label : "Fee Revenue" ,
267
299
color : "hsl(var(--chart-4))" ,
300
+ isCurrency : true ,
301
+ emptyContent : (
302
+ < EmptyStateContent
303
+ metric = "Fees"
304
+ description = "Your apps haven't collected any fees yet."
305
+ link = { "https://portal.thirdweb.com/connect/pay/fees" }
306
+ />
307
+ ) ,
268
308
} ,
309
+ activeUsers : { label : "Active Users" , color : "hsl(var(--chart-1))" } ,
310
+ newUsers : { label : "New Users" , color : "hsl(var(--chart-3))" } ,
269
311
} as const ;
270
312
271
313
return (
272
314
< CombinedBarChartCard
273
315
className = "max-md:rounded-none max-md:border-r-0 max-md:border-l-0"
274
- title = "Users "
316
+ title = "App Highlights "
275
317
chartConfig = { chartConfig }
276
318
activeChart = {
277
- ( searchParams ?. usersChart as keyof UserMetrics ) ?? "activeUsers"
319
+ ( searchParams ?. appHighlights as keyof AggregatedMetrics ) ??
320
+ "totalVolume"
278
321
}
279
322
data = { timeSeriesData }
280
323
aggregateFn = { ( _data , key ) =>
281
- // If there is only one data point, use that one, otherwise use the previous
282
- timeSeriesData . filter ( ( d ) => ( d [ key ] as number ) > 0 ) . length >= 2
283
- ? timeSeriesData [ timeSeriesData . length - 2 ] ?. [ key ]
284
- : timeSeriesData [ timeSeriesData . length - 1 ] ?. [ key ]
324
+ timeSeriesData . reduce ( ( acc , curr ) => acc + curr [ key ] , 0 )
285
325
}
286
326
// Get the trend from the last two COMPLETE periods
287
327
trendFn = { ( data , key ) =>
@@ -291,7 +331,7 @@ function UsersChartCard({
291
331
1
292
332
: undefined
293
333
}
294
- queryKey = "usersChart "
334
+ queryKey = "appHighlights "
295
335
existingQueryParams = { searchParams }
296
336
/>
297
337
) ;
0 commit comments