Skip to content

Commit aedaa10

Browse files
add overivew, polish all graphs
1 parent 8df3f12 commit aedaa10

File tree

13 files changed

+320
-57
lines changed

13 files changed

+320
-57
lines changed

apps/dashboard/src/@3rdweb-sdk/react/hooks/useApi.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -442,6 +442,43 @@ async function getUserOpUsage(args: {
442442
return json.data;
443443
}
444444

445+
export async function getInAppWalletUsage(args: {
446+
clientId: string;
447+
from?: Date;
448+
to?: Date;
449+
period?: "day" | "week" | "month" | "year" | "all";
450+
}) {
451+
const { clientId, from, to, period } = args;
452+
453+
const searchParams = new URLSearchParams();
454+
searchParams.append("clientId", clientId);
455+
if (from) {
456+
searchParams.append("from", from.toISOString());
457+
}
458+
if (to) {
459+
searchParams.append("to", to.toISOString());
460+
}
461+
if (period) {
462+
searchParams.append("period", period);
463+
}
464+
const res = await fetch(
465+
`${THIRDWEB_ANALYTICS_API_HOST}/v1/wallets/in-app?${searchParams.toString()}`,
466+
{
467+
method: "GET",
468+
headers: {
469+
"Content-Type": "application/json",
470+
},
471+
},
472+
);
473+
const json = await res?.json();
474+
475+
if (!res || res.status !== 200) {
476+
throw new Error(json.message);
477+
}
478+
479+
return json.data;
480+
}
481+
445482
export function useUserOpUsageAggregate(args: {
446483
clientId: string;
447484
from?: Date;

apps/dashboard/src/app/team/[team_slug]/[project_slug]/connect/analytics/ConnectAnalyticsDashboard.tsx

Lines changed: 28 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
"use client";
22
import {
3+
getInAppWalletUsage,
34
useUserOpUsageAggregate,
45
useWalletUsageAggregate,
56
useWalletUsagePeriod,
67
} from "@3rdweb-sdk/react/hooks/useApi";
8+
import { useQuery } from "@tanstack/react-query";
79
import {
8-
DateRangeSelector,
910
type Range,
1011
getLastNDaysRange,
1112
} from "components/analytics/date-range-selector";
12-
import { IntervalSelector } from "components/analytics/interval-selector";
1313
import { differenceInDays } from "date-fns";
1414
import { useState } from "react";
1515
import { ConnectAnalyticsDashboardUI } from "./ConnectAnalyticsDashboardUI";
@@ -45,31 +45,42 @@ export function ConnectAnalyticsDashboard(props: {
4545
clientId: props.clientId,
4646
});
4747

48+
const inAppAggregateQuery = useQuery({
49+
queryKey: ["in-app-usage-aggregate", props.clientId],
50+
queryFn: async () => {
51+
const [allTimeStats, monthlyStats] = await Promise.all([
52+
getInAppWalletUsage({
53+
clientId: props.clientId,
54+
from: new Date(2022, 0, 1),
55+
to: new Date(),
56+
period: "all",
57+
}),
58+
getInAppWalletUsage({
59+
clientId: props.clientId,
60+
from: new Date(new Date().getFullYear(), new Date().getMonth(), 1),
61+
to: new Date(),
62+
period: "month",
63+
}),
64+
]);
65+
return { allTimeStats, monthlyStats };
66+
},
67+
});
68+
4869
return (
4970
<div>
50-
<div className="flex gap-3">
51-
<DateRangeSelector
52-
range={range}
53-
setRange={(newRange) => {
54-
setRange(newRange);
55-
const days = differenceInDays(newRange.to, newRange.from);
56-
setIntervalType(days > 30 ? "week" : "day");
57-
}}
58-
/>
59-
<IntervalSelector
60-
intervalType={intervalType}
61-
setIntervalType={setIntervalType}
62-
/>
63-
</div>
64-
<div className="h-4" />
6571
<ConnectAnalyticsDashboardUI
6672
walletUsage={walletUsageQuery.data || []}
6773
aggregateWalletUsage={walletUsageAggregateQuery.data || []}
6874
aggregateUserOpUsageQuery={userOpAggregateQuery.data}
6975
connectLayoutSlug={props.connectLayoutSlug}
76+
inAppAggregateQuery={inAppAggregateQuery.data}
7077
isPending={
7178
walletUsageQuery.isPending || walletUsageAggregateQuery.isPending
7279
}
80+
range={range}
81+
setRange={setRange}
82+
intervalType={intervalType}
83+
setIntervalType={setIntervalType}
7384
/>
7485
</div>
7586
);

apps/dashboard/src/app/team/[team_slug]/[project_slug]/connect/analytics/ConnectAnalyticsDashboardUI.tsx

Lines changed: 89 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,19 @@
11
import { Button } from "@/components/ui/button";
2-
import type { UserOpStats, WalletStats } from "@3rdweb-sdk/react/hooks/useApi";
2+
import type {
3+
InAppWalletStats,
4+
UserOpStats,
5+
WalletStats,
6+
} from "@3rdweb-sdk/react/hooks/useApi";
7+
import type { Range } from "components/analytics/date-range-selector";
38
import { Stat } from "components/analytics/stat";
9+
import { InAppWalletsSummary } from "components/embedded-wallets/Analytics/Summary";
410
import { AccountAbstractionSummary } from "components/smart-wallets/AccountAbstractionAnalytics/AccountAbstractionSummary";
5-
import { CableIcon, WalletCardsIcon } from "lucide-react";
11+
import { differenceInDays } from "date-fns";
12+
import { ArrowRightIcon, CableIcon, WalletCardsIcon } from "lucide-react";
613
import Link from "next/link";
714
import { useMemo } from "react";
15+
import { DateRangeSelector } from "../../../../../../components/analytics/date-range-selector";
16+
import { IntervalSelector } from "../../../../../../components/analytics/interval-selector";
817
import { DailyConnectionsChartCard } from "./_components/DailyConnectionsChartCard";
918
import { WalletConnectorsChartCard } from "./_components/WalletConnectorsChartCard";
1019
import { WalletDistributionChartCard } from "./_components/WalletDistributionChartCard";
@@ -15,6 +24,14 @@ export function ConnectAnalyticsDashboardUI(props: {
1524
aggregateUserOpUsageQuery?: UserOpStats;
1625
connectLayoutSlug: string;
1726
isPending: boolean;
27+
inAppAggregateQuery?: {
28+
allTimeStats: InAppWalletStats[];
29+
monthlyStats: InAppWalletStats[];
30+
};
31+
setRange: (range: Range) => void;
32+
range: Range;
33+
intervalType: "day" | "week";
34+
setIntervalType: (intervalType: "day" | "week") => void;
1835
}) {
1936
const { totalWallets, uniqueWallets } = useMemo(() => {
2037
return props.aggregateWalletUsage.reduce(
@@ -29,35 +46,77 @@ export function ConnectAnalyticsDashboardUI(props: {
2946

3047
return (
3148
<div className="flex flex-col gap-4 lg:gap-6">
32-
{/* Connections */}
33-
<div className="grid grid-cols-2 gap-4 lg:gap-6">
34-
<Stat label="Connections" value={totalWallets} icon={CableIcon} />
35-
<Stat
36-
label="Unique Wallets"
37-
value={uniqueWallets}
38-
icon={WalletCardsIcon}
49+
<div>
50+
<div className="mb-4 flex flex-col gap-4 lg:flex-row lg:items-center lg:justify-between">
51+
<div>
52+
<h3 className="mb-1 font-semibold text-xl tracking-tight md:text-2xl">
53+
Connections
54+
</h3>
55+
<p className="text-muted-foreground">
56+
View how many users are connecting to your app.
57+
</p>
58+
</div>
59+
</div>
60+
<div className="grid grid-cols-2 gap-4 lg:gap-6">
61+
<Stat label="Connections" value={totalWallets} icon={CableIcon} />
62+
<Stat
63+
label="Unique Wallets"
64+
value={uniqueWallets}
65+
icon={WalletCardsIcon}
66+
/>
67+
</div>
68+
</div>
69+
70+
<div className="h-4">
71+
<div className="w-full border-separate border-b" />
72+
</div>
73+
74+
<div>
75+
<div className="mb-4 flex flex-col gap-4 lg:flex-row lg:items-center lg:justify-between">
76+
<div>
77+
<h3 className="mb-1 font-semibold text-xl tracking-tight md:text-2xl">
78+
In-App Wallets
79+
</h3>
80+
<p className="text-muted-foreground">
81+
View metrics for your in-app wallets.
82+
</p>
83+
</div>
84+
85+
<div>
86+
<Button asChild variant="primary" size="sm" className="w-full">
87+
<Link href={`${props.connectLayoutSlug}/in-app-wallets`}>
88+
In-App Wallet Metrics
89+
<ArrowRightIcon className="ml-2 h-4 w-4" />
90+
</Link>
91+
</Button>
92+
</div>
93+
</div>
94+
<InAppWalletsSummary
95+
allTimeStats={props.inAppAggregateQuery?.allTimeStats}
96+
monthlyStats={props.inAppAggregateQuery?.monthlyStats}
3997
/>
4098
</div>
4199

42100
<div className="h-4">
43-
<div className="w-full border-muted border-b" />
101+
<div className="w-full border-separate border-b" />
44102
</div>
45103

46104
<div>
47105
<div className="mb-4 flex flex-col gap-4 lg:flex-row lg:items-center lg:justify-between">
48106
<div>
49-
<h1 className="font-medium text-xl tracking-tight">
107+
<h3 className="mb-1 font-semibold text-xl tracking-tight md:text-2xl">
50108
Account Abstraction
51-
</h1>
109+
</h3>
52110
<p className="text-muted-foreground">
53-
Sponsor and batch transactions for your users.
111+
View how smart wallets are used in your app.
54112
</p>
55113
</div>
56114

57115
<div>
58116
<Button asChild variant="primary" size="sm" className="w-full">
59117
<Link href={`${props.connectLayoutSlug}/account-abstraction`}>
60-
View More Stats
118+
Account Abstraction Metrics
119+
<ArrowRightIcon className="ml-2 h-4 w-4" />
61120
</Link>
62121
</Button>
63122
</div>
@@ -68,7 +127,22 @@ export function ConnectAnalyticsDashboardUI(props: {
68127
</div>
69128

70129
<div className="h-4">
71-
<div className="w-full border-muted border-b" />
130+
<div className="w-full border-separate border-b" />
131+
</div>
132+
133+
<div className="flex justify-end gap-3">
134+
<DateRangeSelector
135+
range={props.range}
136+
setRange={(newRange) => {
137+
props.setRange(newRange);
138+
const days = differenceInDays(newRange.to, newRange.from);
139+
props.setIntervalType(days > 30 ? "week" : "day");
140+
}}
141+
/>
142+
<IntervalSelector
143+
intervalType={props.intervalType}
144+
setIntervalType={props.setIntervalType}
145+
/>
72146
</div>
73147

74148
<DailyConnectionsChartCard

apps/dashboard/src/app/team/[team_slug]/[project_slug]/connect/analytics/_components/ConnectAnalyticsDashboard.stories.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import type { Meta, StoryObj } from "@storybook/react";
22
import { createUserOpStatsStub } from "components/smart-wallets/AccountAbstractionAnalytics/storyUtils";
3+
import { getLastNDaysRange } from "../../../../../../../components/analytics/date-range-selector";
34
import { mobileViewport } from "../../../../../../../stories/utils";
45
import { ConnectAnalyticsDashboardUI } from "../ConnectAnalyticsDashboardUI";
56
import { createWalletStatsStub } from "./storyUtils";
@@ -30,6 +31,10 @@ function Component() {
3031
return (
3132
<div className="container max-w-[1150px] py-8">
3233
<ConnectAnalyticsDashboardUI
34+
setRange={() => {}}
35+
range={getLastNDaysRange("last-120")}
36+
intervalType="day"
37+
setIntervalType={() => {}}
3338
walletUsage={createWalletStatsStub(30)}
3439
aggregateWalletUsage={createWalletStatsStub(30)}
3540
aggregateUserOpUsageQuery={createUserOpStatsStub(1)?.[0]}

apps/dashboard/src/app/team/[team_slug]/[project_slug]/connect/analytics/_components/DailyConnectionsChartCard.tsx

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,15 @@ import { UnrealIcon } from "components/icons/brand-icons/UnrealIcon";
2727
import { DocLink } from "components/shared/DocLink";
2828
import { format } from "date-fns";
2929
import { useMemo, useState } from "react";
30-
import { Bar, BarChart, CartesianGrid, LabelList, XAxis } from "recharts";
30+
import {
31+
Bar,
32+
BarChart,
33+
CartesianGrid,
34+
LabelList,
35+
XAxis,
36+
YAxis,
37+
} from "recharts";
38+
import { formatTickerNumber } from "../../../../../../../lib/format-utils";
3139

3240
type ChartToShow = "uniqueWallets" | "totalWallets";
3341

@@ -194,7 +202,21 @@ export function DailyConnectionsChartCard(props: {
194202
axisLine={false}
195203
/>
196204

197-
<ChartTooltip cursor={true} content={<ChartTooltipContent />} />
205+
<YAxis
206+
dataKey={(data) => data[chartToShow]}
207+
tickLine={false}
208+
axisLine={false}
209+
tickFormatter={(value) => formatTickerNumber(value)}
210+
/>
211+
212+
<ChartTooltip
213+
cursor={true}
214+
content={
215+
<ChartTooltipContent
216+
valueFormatter={(value) => formatTickerNumber(Number(value))}
217+
/>
218+
}
219+
/>
198220

199221
<Bar
200222
dataKey={chartToShow}
@@ -207,6 +229,7 @@ export function DailyConnectionsChartCard(props: {
207229
offset={12}
208230
className="invisible fill-foreground sm:visible"
209231
fontSize={12}
232+
formatter={(value: number) => formatTickerNumber(value)}
210233
/>
211234
)}
212235
</Bar>

apps/dashboard/src/app/team/[team_slug]/[project_slug]/connect/analytics/_components/WalletConnectorsChartCard.tsx

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,11 @@ import { TypeScriptIcon } from "components/icons/brand-icons/TypeScriptIcon";
2626
import { DocLink } from "components/shared/DocLink";
2727
import { format } from "date-fns";
2828
import { useMemo, useState } from "react";
29-
import { Bar, BarChart, CartesianGrid, XAxis } from "recharts";
29+
import { Bar, BarChart, CartesianGrid, XAxis, YAxis } from "recharts";
30+
import {
31+
formatTickerNumber,
32+
formatWalletType,
33+
} from "../../../../../../../lib/format-utils";
3034

3135
type ChartToShow = "uniqueWalletsConnected" | "totalConnections";
3236

@@ -60,7 +64,8 @@ export function WalletConnectorsChartCard(props: {
6064
// for each stat, add it in _chartDataMap
6165
for (const stat of walletStats) {
6266
const chartData = _chartDataMap.get(stat.date);
63-
const { walletType } = stat;
67+
const { walletType: rawWalletType } = stat;
68+
const walletType = formatWalletType(rawWalletType);
6469

6570
// if no data for current day - create new entry
6671
if (!chartData) {
@@ -213,7 +218,26 @@ export function WalletConnectorsChartCard(props: {
213218
axisLine={false}
214219
/>
215220

216-
<ChartTooltip cursor={true} content={<ChartTooltipContent />} />
221+
<YAxis
222+
dataKey={(data) =>
223+
Object.entries(data)
224+
.filter(([key]) => key !== "time")
225+
.map(([, value]) => value)
226+
.reduce((acc, current) => Number(acc) + Number(current), 0)
227+
}
228+
tickLine={false}
229+
axisLine={false}
230+
tickFormatter={(value) => formatTickerNumber(value)}
231+
/>
232+
233+
<ChartTooltip
234+
cursor={true}
235+
content={
236+
<ChartTooltipContent
237+
valueFormatter={(value) => formatTickerNumber(Number(value))}
238+
/>
239+
}
240+
/>
217241
<ChartLegend content={<ChartLegendContent />} />
218242
{uniqueWalletTypes.map((walletType) => {
219243
return (

0 commit comments

Comments
 (0)