Skip to content

Commit 7573dd6

Browse files
committed
[TOOL-4645] Dashboard: Improve Project overview page loading performance
1 parent 6cce31d commit 7573dd6

File tree

5 files changed

+187
-134
lines changed

5 files changed

+187
-134
lines changed

apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/components/EngineCloudChartCard/index.tsx

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,7 @@ import { EngineCloudBarChartCardUI } from "./EngineCloudBarChartCardUI";
66

77
export function EngineCloudChartCard(props: AnalyticsQueryParams) {
88
return (
9-
<Suspense
10-
fallback={
11-
<div className="h-[400px]">
12-
<LoadingChartState />
13-
</div>
14-
}
15-
>
9+
<Suspense fallback={<LoadingChartState className="h-[377px] border" />}>
1610
<EngineCloudChartCardAsync {...props} />
1711
</Suspense>
1812
);

apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/components/RpcMethodBarChartCard/index.tsx

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,7 @@ import { RpcMethodBarChartCardUI } from "./RpcMethodBarChartCardUI";
77
export function RpcMethodBarChartCard(props: AnalyticsQueryParams) {
88
return (
99
// TODO: Add better LoadingChartState
10-
<Suspense
11-
fallback={
12-
<div className="h-[400px]">
13-
<LoadingChartState />
14-
</div>
15-
}
16-
>
10+
<Suspense fallback={<LoadingChartState className="h-[377px] border" />}>
1711
<RpcMethodBarChartCardAsync {...props} />
1812
</Suspense>
1913
);

apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/components/Transactions/index.tsx

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,7 @@ export function TransactionsCharts(
1313
) {
1414
return (
1515
// TODO: Add better LoadingChartState
16-
<Suspense
17-
fallback={
18-
<div className="h-[400px]">
19-
<LoadingChartState />
20-
</div>
21-
}
22-
>
16+
<Suspense fallback={<LoadingChartState className="h-[458px] border" />}>
2317
<TransactionsChartCardAsync {...props} />
2418
</Suspense>
2519
);

apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/page.tsx

Lines changed: 176 additions & 111 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ import {
3737
getChainMetadata,
3838
} from "thirdweb/chains";
3939
import { type WalletId, getWalletInfo } from "thirdweb/wallets";
40+
import { LoadingChartState } from "../../../../../../components/analytics/empty-chart-state";
4041
import { getAuthToken } from "../../../../api/lib/getAuthToken";
4142
import { loginRedirect } from "../../../../login/loginRedirect";
4243
import { CombinedBarChartCard } from "../../../components/Analytics/CombinedBarChartCard";
@@ -148,107 +149,29 @@ async function ProjectAnalytics(props: {
148149
}) {
149150
const { project, params, range, interval, searchParams, client } = props;
150151

151-
// Fetch all analytics data in parallel
152-
const [
153-
walletConnections,
154-
walletUserStatsTimeSeries,
155-
inAppWalletUsage,
156-
userOpUsageTimeSeries,
157-
userOpUsage,
158-
universalBridgeUsage,
159-
] = await Promise.allSettled([
160-
// Aggregated wallet connections
161-
getWalletConnections({
162-
teamId: project.teamId,
163-
projectId: project.id,
164-
from: range.from,
165-
to: range.to,
166-
period: "all",
167-
}),
168-
// Time series data for wallet users
169-
getWalletUsers({
170-
teamId: project.teamId,
171-
projectId: project.id,
172-
from: range.from,
173-
to: range.to,
174-
period: interval,
175-
}),
176-
// In-app wallet usage
177-
getInAppWalletUsage({
178-
teamId: project.teamId,
179-
projectId: project.id,
180-
from: range.from,
181-
to: range.to,
182-
period: "all",
183-
}),
184-
// User operations usage
185-
getUserOpUsage({
186-
teamId: project.teamId,
187-
projectId: project.id,
188-
from: range.from,
189-
to: range.to,
190-
period: interval,
191-
}),
192-
getUserOpUsage({
193-
teamId: project.teamId,
194-
projectId: project.id,
195-
from: range.from,
196-
to: range.to,
197-
period: "all",
198-
}),
199-
// Universal Bridge
200-
getUniversalBridgeUsage({
201-
teamId: project.teamId,
202-
projectId: project.id,
203-
from: range.from,
204-
to: range.to,
205-
period: interval,
206-
}),
207-
]);
208-
209152
return (
210153
<div className="flex grow flex-col gap-6">
211-
{walletUserStatsTimeSeries.status === "fulfilled" &&
212-
universalBridgeUsage.status === "fulfilled" &&
213-
walletUserStatsTimeSeries.value.some((w) => w.totalUsers !== 0) ? (
214-
<div className="">
215-
<AppHighlightsCard
216-
chartKey={
217-
(searchParams.appHighlights as keyof AggregatedMetrics) ??
218-
"totalVolume"
219-
}
220-
params={params}
221-
userStats={walletUserStatsTimeSeries.value}
222-
volumeStats={universalBridgeUsage.value}
223-
searchParams={searchParams}
224-
/>
225-
</div>
226-
) : (
227-
<EmptyStateCard
228-
metric="Connect"
229-
link="https://portal.thirdweb.com/connect/quickstart"
154+
<Suspense fallback={<LoadingChartState className="h-[458px] border" />}>
155+
<AsyncAppHighlightsCard
156+
project={project}
157+
range={range}
158+
interval={interval}
159+
searchParams={searchParams}
160+
client={client}
161+
params={params}
230162
/>
231-
)}
163+
</Suspense>
164+
232165
<div className="grid gap-6 md:grid-cols-2">
233-
{walletConnections.status === "fulfilled" &&
234-
walletConnections.value.length > 0 ? (
235-
<WalletDistributionCard data={walletConnections.value} />
236-
) : (
237-
<EmptyStateCard
238-
metric="Connect"
239-
link="https://portal.thirdweb.com/connect/quickstart"
240-
/>
241-
)}
242-
{inAppWalletUsage.status === "fulfilled" &&
243-
inAppWalletUsage.value.length > 0 ? (
244-
<AuthMethodDistributionCard data={inAppWalletUsage.value} />
245-
) : (
246-
<EmptyStateCard
247-
metric="In-App Wallets"
248-
link="https://portal.thirdweb.com/typescript/v5/inAppWallet"
249-
/>
250-
)}
166+
<Suspense fallback={<LoadingChartState className="h-[431px] border" />}>
167+
<AsyncWalletDistributionCard project={project} range={range} />
168+
</Suspense>
169+
170+
<Suspense fallback={<LoadingChartState className="h-[431px] border" />}>
171+
<AsyncAuthMethodDistributionCard project={project} range={range} />
172+
</Suspense>
251173
</div>
174+
252175
<TransactionsCharts
253176
searchParams={searchParams}
254177
from={range.from}
@@ -257,22 +180,16 @@ async function ProjectAnalytics(props: {
257180
teamId={project.teamId}
258181
client={client}
259182
/>
260-
{userOpUsageTimeSeries.status === "fulfilled" &&
261-
userOpUsage.status === "fulfilled" &&
262-
userOpUsage.value.length > 0 ? (
263-
<div className="">
264-
<TotalSponsoredCard
265-
searchParams={searchParams}
266-
data={userOpUsageTimeSeries.value}
267-
aggregatedData={userOpUsage.value}
268-
/>
269-
</div>
270-
) : (
271-
<EmptyStateCard
272-
metric="Sponsored Transactions"
273-
link="https://portal.thirdweb.com/typescript/v5/account-abstraction/get-started"
183+
184+
<Suspense fallback={<LoadingChartState className="h-[458px] border" />}>
185+
<AsyncTotalSponsoredCard
186+
project={project}
187+
range={range}
188+
interval={interval}
189+
searchParams={searchParams}
274190
/>
275-
)}
191+
</Suspense>
192+
276193
<RpcMethodBarChartCard
277194
from={range.from}
278195
to={range.to}
@@ -332,6 +249,154 @@ function processTimeSeriesData(
332249
return metrics;
333250
}
334251

252+
async function AsyncTotalSponsoredCard(props: {
253+
project: Project;
254+
range: Range;
255+
interval: "day" | "week";
256+
searchParams: PageSearchParams;
257+
}) {
258+
const [userOpUsageTimeSeries, userOpUsage] = await Promise.allSettled([
259+
getUserOpUsage({
260+
teamId: props.project.teamId,
261+
projectId: props.project.id,
262+
from: props.range.from,
263+
to: props.range.to,
264+
period: props.interval,
265+
}),
266+
getUserOpUsage({
267+
teamId: props.project.teamId,
268+
projectId: props.project.id,
269+
from: props.range.from,
270+
to: props.range.to,
271+
period: "all",
272+
}),
273+
]);
274+
275+
return userOpUsageTimeSeries.status === "fulfilled" &&
276+
userOpUsage.status === "fulfilled" &&
277+
userOpUsage.value.length > 0 ? (
278+
<div className="">
279+
<TotalSponsoredCard
280+
searchParams={props.searchParams}
281+
data={userOpUsageTimeSeries.value}
282+
aggregatedData={userOpUsage.value}
283+
/>
284+
</div>
285+
) : (
286+
<EmptyStateCard
287+
metric="Sponsored Transactions"
288+
link="https://portal.thirdweb.com/typescript/v5/account-abstraction/get-started"
289+
/>
290+
);
291+
}
292+
293+
async function AsyncAuthMethodDistributionCard(props: {
294+
project: Project;
295+
range: Range;
296+
}) {
297+
const inAppWalletUsage = await getInAppWalletUsage({
298+
teamId: props.project.teamId,
299+
projectId: props.project.id,
300+
from: props.range.from,
301+
to: props.range.to,
302+
period: "all",
303+
}).catch(() => undefined);
304+
305+
return inAppWalletUsage && inAppWalletUsage.length > 0 ? (
306+
<AuthMethodDistributionCard data={inAppWalletUsage} />
307+
) : (
308+
<EmptyStateCard
309+
metric="In-App Wallets"
310+
link="https://portal.thirdweb.com/typescript/v5/inAppWallet"
311+
/>
312+
);
313+
}
314+
315+
async function AsyncAppHighlightsCard(props: {
316+
project: Project;
317+
range: Range;
318+
interval: "day" | "week";
319+
searchParams: PageSearchParams;
320+
client: ThirdwebClient;
321+
params: PageParams;
322+
}) {
323+
const [walletUserStatsTimeSeries, universalBridgeUsage] =
324+
await Promise.allSettled([
325+
getWalletUsers({
326+
teamId: props.project.teamId,
327+
projectId: props.project.id,
328+
from: props.range.from,
329+
to: props.range.to,
330+
period: props.interval,
331+
}),
332+
getUniversalBridgeUsage({
333+
teamId: props.project.teamId,
334+
projectId: props.project.id,
335+
from: props.range.from,
336+
to: props.range.to,
337+
period: props.interval,
338+
}),
339+
]);
340+
341+
if (
342+
walletUserStatsTimeSeries.status === "fulfilled" &&
343+
universalBridgeUsage.status === "fulfilled" &&
344+
walletUserStatsTimeSeries.value.some((w) => w.totalUsers !== 0)
345+
) {
346+
return (
347+
<div>
348+
<AppHighlightsCard
349+
chartKey={
350+
(props.searchParams.appHighlights as keyof AggregatedMetrics) ??
351+
"totalVolume"
352+
}
353+
params={props.params}
354+
userStats={
355+
walletUserStatsTimeSeries.status === "fulfilled"
356+
? walletUserStatsTimeSeries.value
357+
: []
358+
}
359+
volumeStats={
360+
universalBridgeUsage.status === "fulfilled"
361+
? universalBridgeUsage.value
362+
: []
363+
}
364+
searchParams={props.searchParams}
365+
/>
366+
</div>
367+
);
368+
}
369+
370+
return (
371+
<EmptyStateCard
372+
metric="Connect"
373+
link="https://portal.thirdweb.com/connect/quickstart"
374+
/>
375+
);
376+
}
377+
378+
async function AsyncWalletDistributionCard(props: {
379+
project: Project;
380+
range: Range;
381+
}) {
382+
const walletConnections = await getWalletConnections({
383+
teamId: props.project.teamId,
384+
projectId: props.project.id,
385+
from: props.range.from,
386+
to: props.range.to,
387+
period: "all",
388+
}).catch(() => undefined);
389+
390+
return walletConnections && walletConnections.length > 0 ? (
391+
<WalletDistributionCard data={walletConnections} />
392+
) : (
393+
<EmptyStateCard
394+
metric="Connect"
395+
link="https://portal.thirdweb.com/connect/quickstart"
396+
/>
397+
);
398+
}
399+
335400
function AppHighlightsCard({
336401
chartKey,
337402
userStats,

apps/dashboard/src/components/analytics/empty-chart-state.tsx

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { LoadingDots } from "@/components/ui/LoadingDots";
33
import { type ChartConfig, ChartContainer } from "@/components/ui/chart";
44
import { useMemo } from "react";
55
import { Area, AreaChart, Bar, BarChart } from "recharts";
6+
import { cn } from "../../@/lib/utils";
67

78
type FakeCartData = {
89
value: number;
@@ -37,9 +38,14 @@ export function EmptyChartState({
3738
);
3839
}
3940

40-
export function LoadingChartState() {
41+
export function LoadingChartState({ className }: { className?: string }) {
4142
return (
42-
<div className="pointer-events-none flex h-full w-full items-center justify-center rounded-lg bg-muted/30">
43+
<div
44+
className={cn(
45+
"pointer-events-none flex h-full w-full items-center justify-center rounded-lg bg-muted/30",
46+
className,
47+
)}
48+
>
4349
<LoadingDots />
4450
</div>
4551
);

0 commit comments

Comments
 (0)