@@ -21,15 +21,14 @@ import type {
2121} from "types/analytics" ;
2222import { AnalyticsHeader } from "../../../../components/Analytics/AnalyticsHeader" ;
2323import { CombinedBarChartCard } from "../../../../components/Analytics/CombinedBarChartCard" ;
24- import { EmptyState } from "../../../../components/Analytics/EmptyState" ;
2524import { PieChartCard } from "../../../../components/Analytics/PieChartCard" ;
2625
2726import { getTeamBySlug } from "@/api/team" ;
28- import { GenericLoadingPage } from "@/components/blocks/skeletons/GenericLoadingPage" ;
2927import {
3028 EmptyStateCard ,
3129 EmptyStateContent ,
3230} from "app/(app)/team/components/Analytics/EmptyStateCard" ;
31+ import { LoadingChartState } from "components/analytics/empty-chart-state" ;
3332import { Suspense } from "react" ;
3433import { TotalSponsoredChartCardUI } from "../../_components/TotalSponsoredCard" ;
3534import { TransactionsChartCardUI } from "../../_components/TransactionsCard" ;
@@ -76,159 +75,221 @@ export default async function TeamOverviewPage(props: {
7675 />
7776 </ div >
7877 < div className = "flex grow flex-col justify-between gap-10 md:container md:pt-8 md:pb-16" >
79- < Suspense fallback = { < GenericLoadingPage /> } >
80- < OverviewPageContent
81- teamId = { team . id }
82- range = { range }
83- interval = { interval }
84- searchParams = { searchParams }
85- />
86- </ Suspense >
78+ < div className = "flex grow flex-col gap-6" >
79+ < Suspense
80+ fallback = { < LoadingChartState className = "h-[458px] border" /> }
81+ >
82+ < AsyncAppHighlightsCard
83+ teamId = { team . id }
84+ range = { range }
85+ interval = { interval }
86+ searchParams = { searchParams }
87+ />
88+ </ Suspense >
89+
90+ < div className = "grid gap-6 max-md:px-6 md:grid-cols-2" >
91+ < Suspense
92+ fallback = { < LoadingChartState className = "h-[431px] border" /> }
93+ >
94+ < AsyncWalletDistributionCard teamId = { team . id } range = { range } />
95+ </ Suspense >
96+
97+ < Suspense
98+ fallback = { < LoadingChartState className = "h-[431px] border" /> }
99+ >
100+ < AsyncAuthMethodDistributionCard teamId = { team . id } range = { range } />
101+ </ Suspense >
102+ </ div >
103+
104+ < Suspense
105+ fallback = { < LoadingChartState className = "h-[458px] border" /> }
106+ >
107+ < AsyncTransactionsChartCard
108+ teamId = { team . id }
109+ range = { range }
110+ interval = { interval }
111+ searchParams = { searchParams }
112+ />
113+ </ Suspense >
114+
115+ < Suspense
116+ fallback = { < LoadingChartState className = "h-[458px] border" /> }
117+ >
118+ < AsyncTotalSponsoredCard
119+ teamId = { team . id }
120+ range = { range }
121+ interval = { interval }
122+ searchParams = { searchParams }
123+ />
124+ </ Suspense >
125+ </ div >
87126 </ div >
88127 </ div >
89128 ) ;
90129}
91130
92- async function OverviewPageContent ( props : {
131+ async function AsyncAppHighlightsCard ( props : {
93132 teamId : string ;
94133 range : Range ;
95134 interval : "day" | "week" ;
96135 searchParams : SearchParams ;
97136} ) {
98- const { teamId, range, interval, searchParams } = props ;
99-
100- const [
101- walletConnections ,
102- walletUserStatsTimeSeries ,
103- inAppWalletUsage ,
104- userOpUsageTimeSeries ,
105- userOpUsage ,
106- clientTransactionsTimeSeries ,
107- clientTransactions ,
108- universalBridgeUsage ,
109- ] = await Promise . all ( [
110- // Aggregated wallet connections
111- getWalletConnections ( {
112- teamId : teamId ,
113- from : range . from ,
114- to : range . to ,
115- period : "all" ,
116- } ) ,
117- // Time series data for wallet users
118- getWalletUsers ( {
119- teamId : teamId ,
120- from : range . from ,
121- to : range . to ,
122- period : interval ,
123- } ) ,
124- // In-app wallet usage
125- getInAppWalletUsage ( {
126- teamId : teamId ,
127- from : range . from ,
128- to : range . to ,
129- period : "all" ,
130- } ) ,
131- // User operations usage
137+ const [ walletUserStatsTimeSeries , universalBridgeUsage ] =
138+ await Promise . allSettled ( [
139+ getWalletUsers ( {
140+ teamId : props . teamId ,
141+ from : props . range . from ,
142+ to : props . range . to ,
143+ period : props . interval ,
144+ } ) ,
145+ getUniversalBridgeUsage ( {
146+ teamId : props . teamId ,
147+ from : props . range . from ,
148+ to : props . range . to ,
149+ period : props . interval ,
150+ } ) ,
151+ ] ) ;
152+
153+ if (
154+ walletUserStatsTimeSeries . status === "fulfilled" &&
155+ universalBridgeUsage . status === "fulfilled" &&
156+ walletUserStatsTimeSeries . value . some ( ( w ) => w . totalUsers !== 0 )
157+ ) {
158+ return (
159+ < div className = "" >
160+ < AppHighlightsCard
161+ userStats = { walletUserStatsTimeSeries . value }
162+ volumeStats = { universalBridgeUsage . value }
163+ searchParams = { props . searchParams }
164+ />
165+ </ div >
166+ ) ;
167+ }
168+
169+ return (
170+ < EmptyStateCard
171+ metric = "Connect"
172+ link = "https://portal.thirdweb.com/connect/quickstart"
173+ />
174+ ) ;
175+ }
176+
177+ async function AsyncWalletDistributionCard ( props : {
178+ teamId : string ;
179+ range : Range ;
180+ } ) {
181+ const walletConnections = await getWalletConnections ( {
182+ teamId : props . teamId ,
183+ from : props . range . from ,
184+ to : props . range . to ,
185+ period : "all" ,
186+ } ) . catch ( ( ) => undefined ) ;
187+
188+ return walletConnections && walletConnections . length > 0 ? (
189+ < WalletDistributionCard data = { walletConnections } />
190+ ) : (
191+ < EmptyStateCard
192+ metric = "Connect"
193+ link = "https://portal.thirdweb.com/connect/quickstart"
194+ />
195+ ) ;
196+ }
197+
198+ async function AsyncAuthMethodDistributionCard ( props : {
199+ teamId : string ;
200+ range : Range ;
201+ } ) {
202+ const inAppWalletUsage = await getInAppWalletUsage ( {
203+ teamId : props . teamId ,
204+ from : props . range . from ,
205+ to : props . range . to ,
206+ period : "all" ,
207+ } ) . catch ( ( ) => undefined ) ;
208+
209+ return inAppWalletUsage && inAppWalletUsage . length > 0 ? (
210+ < AuthMethodDistributionCard data = { inAppWalletUsage } />
211+ ) : (
212+ < EmptyStateCard
213+ metric = "In-App Wallets"
214+ link = "https://portal.thirdweb.com/typescript/v5/inAppWallet"
215+ />
216+ ) ;
217+ }
218+
219+ async function AsyncTransactionsChartCard ( props : {
220+ teamId : string ;
221+ range : Range ;
222+ interval : "day" | "week" ;
223+ searchParams : SearchParams ;
224+ } ) {
225+ const [ clientTransactionsTimeSeries , clientTransactions ] =
226+ await Promise . allSettled ( [
227+ getClientTransactions ( {
228+ teamId : props . teamId ,
229+ from : props . range . from ,
230+ to : props . range . to ,
231+ period : props . interval ,
232+ } ) ,
233+ getClientTransactions ( {
234+ teamId : props . teamId ,
235+ from : props . range . from ,
236+ to : props . range . to ,
237+ period : "all" ,
238+ } ) ,
239+ ] ) ;
240+
241+ return clientTransactionsTimeSeries . status === "fulfilled" &&
242+ clientTransactions . status === "fulfilled" &&
243+ clientTransactions . value . length > 0 ? (
244+ < TransactionsChartCardUI
245+ searchParams = { props . searchParams }
246+ data = { clientTransactionsTimeSeries . value }
247+ aggregatedData = { clientTransactions . value }
248+ className = "max-md:rounded-none max-md:border-r-0 max-md:border-l-0"
249+ />
250+ ) : (
251+ < EmptyStateCard
252+ metric = "Transactions"
253+ link = "https://portal.thirdweb.com/connect/quickstart"
254+ />
255+ ) ;
256+ }
257+
258+ async function AsyncTotalSponsoredCard ( props : {
259+ teamId : string ;
260+ range : Range ;
261+ interval : "day" | "week" ;
262+ searchParams : SearchParams ;
263+ } ) {
264+ const [ userOpUsageTimeSeries , userOpUsage ] = await Promise . allSettled ( [
132265 getUserOpUsage ( {
133- teamId,
134- from : range . from ,
135- to : range . to ,
136- period : interval ,
266+ teamId : props . teamId ,
267+ from : props . range . from ,
268+ to : props . range . to ,
269+ period : props . interval ,
137270 } ) ,
138271 getUserOpUsage ( {
139- teamId,
140- from : range . from ,
141- to : range . to ,
272+ teamId : props . teamId ,
273+ from : props . range . from ,
274+ to : props . range . to ,
142275 period : "all" ,
143276 } ) ,
144- // Client transactions
145- getClientTransactions ( {
146- teamId : teamId ,
147- from : range . from ,
148- to : range . to ,
149- period : interval ,
150- } ) ,
151- getClientTransactions ( {
152- teamId : teamId ,
153- from : range . from ,
154- to : range . to ,
155- period : "all" ,
156- } ) ,
157- // Universal Bridge
158- getUniversalBridgeUsage ( {
159- teamId : teamId ,
160- from : range . from ,
161- to : range . to ,
162- period : interval ,
163- } ) ,
164277 ] ) ;
165278
166- const isEmpty =
167- ! walletUserStatsTimeSeries . some ( ( w ) => w . totalUsers !== 0 ) &&
168- walletConnections . length === 0 &&
169- inAppWalletUsage . length === 0 &&
170- userOpUsage . length === 0 ;
171-
172- if ( isEmpty ) {
173- return < EmptyState /> ;
174- }
175-
176- return (
177- < div className = "flex grow flex-col gap-6" >
178- { walletUserStatsTimeSeries . some ( ( w ) => w . totalUsers !== 0 ) ? (
179- < div className = "" >
180- < AppHighlightsCard
181- userStats = { walletUserStatsTimeSeries }
182- volumeStats = { universalBridgeUsage }
183- searchParams = { searchParams }
184- />
185- </ div >
186- ) : (
187- < EmptyStateCard
188- metric = "Connect"
189- link = "https://portal.thirdweb.com/connect/quickstart"
190- />
191- ) }
192- < div className = "grid gap-6 max-md:px-6 md:grid-cols-2" >
193- { walletConnections . length > 0 ? (
194- < WalletDistributionCard data = { walletConnections } />
195- ) : (
196- < EmptyStateCard
197- metric = "Connect"
198- link = "https://portal.thirdweb.com/connect/quickstart"
199- />
200- ) }
201- { inAppWalletUsage . length > 0 ? (
202- < AuthMethodDistributionCard data = { inAppWalletUsage } />
203- ) : (
204- < EmptyStateCard
205- metric = "In-App Wallets"
206- link = "https://portal.thirdweb.com/typescript/v5/inAppWallet"
207- />
208- ) }
209- </ div >
210- { clientTransactions . length > 0 && (
211- < TransactionsChartCardUI
212- searchParams = { searchParams }
213- data = { clientTransactionsTimeSeries }
214- aggregatedData = { clientTransactions }
215- className = "max-md:rounded-none max-md:border-r-0 max-md:border-l-0"
216- />
217- ) }
218- { userOpUsage . length > 0 ? (
219- < TotalSponsoredChartCardUI
220- searchParams = { searchParams }
221- data = { userOpUsageTimeSeries }
222- aggregatedData = { userOpUsage }
223- className = "max-md:rounded-none max-md:border-r-0 max-md:border-l-0"
224- />
225- ) : (
226- < EmptyStateCard
227- metric = "Gas Sponsored"
228- link = "https://portal.thirdweb.com/typescript/v5/account-abstraction/get-started"
229- />
230- ) }
231- </ div >
279+ return userOpUsageTimeSeries . status === "fulfilled" &&
280+ userOpUsage . status === "fulfilled" &&
281+ userOpUsage . value . length > 0 ? (
282+ < TotalSponsoredChartCardUI
283+ searchParams = { props . searchParams }
284+ data = { userOpUsageTimeSeries . value }
285+ aggregatedData = { userOpUsage . value }
286+ className = "max-md:rounded-none max-md:border-r-0 max-md:border-l-0"
287+ />
288+ ) : (
289+ < EmptyStateCard
290+ metric = "Gas Sponsored"
291+ link = "https://portal.thirdweb.com/typescript/v5/account-abstraction/get-started"
292+ />
232293 ) ;
233294}
234295
@@ -251,11 +312,19 @@ function processTimeSeriesData(
251312
252313 for ( const stat of userStats ) {
253314 const volume = volumeStats
254- . filter ( ( v ) => v . date === stat . date && v . status === "completed" )
315+ . filter (
316+ ( v ) =>
317+ new Date ( v . date ) . toISOString ( ) ===
318+ new Date ( stat . date ) . toISOString ( ) && v . status === "completed" ,
319+ )
255320 . reduce ( ( acc , curr ) => acc + curr . amountUsdCents / 100 , 0 ) ;
256321
257322 const fees = volumeStats
258- . filter ( ( v ) => v . date === stat . date && v . status === "completed" )
323+ . filter (
324+ ( v ) =>
325+ new Date ( v . date ) . toISOString ( ) ===
326+ new Date ( stat . date ) . toISOString ( ) && v . status === "completed" ,
327+ )
259328 . reduce ( ( acc , curr ) => acc + curr . developerFeeUsdCents / 100 , 0 ) ;
260329
261330 metrics . push ( {
0 commit comments