Skip to content

Commit f206f59

Browse files
committed
feat: empty wallet states
1 parent 0b48b76 commit f206f59

File tree

9 files changed

+308
-126
lines changed

9 files changed

+308
-126
lines changed
Lines changed: 98 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import type { UserOpStats, WalletStats } from "@3rdweb-sdk/react/hooks/useApi";
22
import {
3-
ActivityIcon,
4-
CableIcon,
5-
CoinsIcon,
6-
WalletCardsIcon,
3+
ActivityIcon,
4+
CableIcon,
5+
CoinsIcon,
6+
WalletCardsIcon,
77
} from "lucide-react";
88
import type React from "react";
99
import { useMemo } from "react";
@@ -14,112 +14,111 @@ import { WalletConnectorsChartCard } from "./_components/WalletConnectorsChartCa
1414
import { WalletDistributionChartCard } from "./_components/WalletDistributionChartCard";
1515

1616
export function ConnectAnalyticsDashboardUI(props: {
17-
walletUsage: WalletStats[];
18-
aggregateWalletUsage: WalletStats[];
19-
userOpUsage: UserOpStats[];
20-
aggregateUserOpUsage: UserOpStats[];
21-
isPending: boolean;
17+
walletUsage: WalletStats[];
18+
aggregateWalletUsage: WalletStats[];
19+
userOpUsage: UserOpStats[];
20+
aggregateUserOpUsage: UserOpStats[];
21+
isPending: boolean;
2222
}) {
23-
// This hook aggregates wallets across wallet types, only one date is returned for the aggregate query
24-
const { totalWallets, uniqueWallets } = useMemo(() => {
25-
return props.aggregateWalletUsage.reduce(
26-
(acc, curr) => {
27-
acc.totalWallets += curr.totalConnections;
28-
acc.uniqueWallets += curr.uniqueWalletsConnected;
29-
return acc;
30-
},
31-
{ uniqueWallets: 0, totalWallets: 0 },
32-
);
33-
}, [props.aggregateWalletUsage]);
23+
const { totalWallets, uniqueWallets } = useMemo(() => {
24+
return props.aggregateWalletUsage.reduce(
25+
(acc, curr) => {
26+
acc.totalWallets += curr.totalConnections;
27+
acc.uniqueWallets += curr.uniqueWalletsConnected;
28+
return acc;
29+
},
30+
{ uniqueWallets: 0, totalWallets: 0 },
31+
);
32+
}, [props.aggregateWalletUsage]);
3433

35-
const { totalSponsoredTransactions, totalSponsoredUsd } = useMemo(() => {
36-
return props.aggregateUserOpUsage.reduce(
37-
(acc, curr) => {
38-
acc.totalSponsoredTransactions += curr.successful;
39-
acc.totalSponsoredUsd += curr.sponsoredUsd;
40-
return acc;
41-
},
42-
{ totalSponsoredTransactions: 0, totalSponsoredUsd: 0 },
43-
);
44-
}, [props.aggregateUserOpUsage]);
34+
const { totalSponsoredTransactions, totalSponsoredUsd } = useMemo(() => {
35+
return props.aggregateUserOpUsage.reduce(
36+
(acc, curr) => {
37+
acc.totalSponsoredTransactions += curr.successful;
38+
acc.totalSponsoredUsd += curr.sponsoredUsd;
39+
return acc;
40+
},
41+
{ totalSponsoredTransactions: 0, totalSponsoredUsd: 0 },
42+
);
43+
}, [props.aggregateUserOpUsage]);
4544

46-
return (
47-
<div className="flex flex-col gap-4 lg:gap-6">
48-
{/* Connections */}
49-
<div className="grid grid-cols-2 gap-4 lg:gap-6">
50-
<Stat label="Connections" value={totalWallets} icon={CableIcon} />
51-
<Stat
52-
label="Unique Wallets"
53-
value={uniqueWallets}
54-
icon={WalletCardsIcon}
55-
/>
56-
</div>
45+
return (
46+
<div className="flex flex-col gap-4 lg:gap-6">
47+
{/* Connections */}
48+
<div className="grid grid-cols-2 gap-4 lg:gap-6">
49+
<Stat label="Connections" value={totalWallets} icon={CableIcon} />
50+
<Stat
51+
label="Unique Wallets"
52+
value={uniqueWallets}
53+
icon={WalletCardsIcon}
54+
/>
55+
</div>
5756

58-
<DailyConnectionsChartCard
59-
walletStats={props.walletUsage}
60-
isPending={props.isPending}
61-
/>
57+
<DailyConnectionsChartCard
58+
walletStats={props.walletUsage}
59+
isPending={props.isPending}
60+
/>
6261

63-
<WalletConnectorsChartCard
64-
walletStats={props.walletUsage}
65-
isPending={props.isPending}
66-
/>
62+
<WalletConnectorsChartCard
63+
walletStats={props.walletUsage}
64+
isPending={props.isPending}
65+
/>
6766

68-
<WalletDistributionChartCard
69-
walletStats={props.walletUsage}
70-
isPending={props.isPending}
71-
/>
67+
<WalletDistributionChartCard
68+
walletStats={props.walletUsage}
69+
isPending={props.isPending}
70+
/>
7271

73-
{/* Connections */}
74-
<div className="grid grid-cols-2 gap-4 lg:gap-6">
75-
<Stat
76-
label="Sponsored Transactions"
77-
value={totalSponsoredTransactions}
78-
icon={ActivityIcon}
79-
/>
80-
<Stat
81-
label="Total Sponsored"
82-
value={totalSponsoredUsd}
83-
formatter={(value) =>
84-
new Intl.NumberFormat("en-US", {
85-
style: "currency",
86-
currency: "USD",
87-
}).format(value)
88-
}
89-
icon={CoinsIcon}
90-
/>
91-
</div>
72+
{/* Connections */}
73+
<div className="grid grid-cols-2 gap-4 lg:gap-6">
74+
<Stat
75+
label="Sponsored Transactions"
76+
value={totalSponsoredTransactions}
77+
icon={ActivityIcon}
78+
/>
79+
<Stat
80+
label="Total Sponsored"
81+
value={totalSponsoredUsd}
82+
formatter={(value) =>
83+
new Intl.NumberFormat("en-US", {
84+
style: "currency",
85+
currency: "USD",
86+
}).format(value)
87+
}
88+
icon={CoinsIcon}
89+
/>
90+
</div>
9291

93-
<TotalSponsoredChartCard
94-
userOpStats={props.userOpUsage}
95-
isPending={props.isPending}
96-
/>
92+
<TotalSponsoredChartCard
93+
userOpStats={props.userOpUsage}
94+
isPending={props.isPending}
95+
/>
9796

98-
<SponsoredTransactionsChartCard
99-
userOpStats={props.userOpUsage}
100-
isPending={props.isPending}
101-
/>
102-
</div>
103-
);
97+
<SponsoredTransactionsChartCard
98+
userOpStats={props.userOpUsage}
99+
isPending={props.isPending}
100+
/>
101+
</div>
102+
);
104103
}
105104

106105
const Stat: React.FC<{
107-
label: string;
108-
value?: number;
109-
icon: React.FC<{ className?: string }>;
110-
formatter?: (value: number) => string;
106+
label: string;
107+
value?: number;
108+
icon: React.FC<{ className?: string }>;
109+
formatter?: (value: number) => string;
111110
}> = ({ label, value, formatter, icon: Icon }) => {
112-
return (
113-
<dl className="flex items-center justify-between gap-4 rounded-lg border border-border bg-muted/50 p-4 lg:p-6">
114-
<div>
115-
<dd className="font-semibold text-3xl tracking-tight lg:text-5xl">
116-
{value && formatter ? formatter(value) : value?.toLocaleString()}
117-
</dd>
118-
<dt className="font-medium text-muted-foreground text-sm tracking-tight lg:text-lg">
119-
{label}
120-
</dt>
121-
</div>
122-
<Icon className="hidden size-12 text-muted-foreground opacity-50 lg:block" />
123-
</dl>
124-
);
111+
return (
112+
<dl className="flex items-center justify-between gap-4 rounded-lg border border-border bg-muted/50 p-4 lg:p-6">
113+
<div>
114+
<dd className="font-semibold text-3xl tracking-tight lg:text-5xl">
115+
{value && formatter ? formatter(value) : value?.toLocaleString()}
116+
</dd>
117+
<dt className="font-medium text-muted-foreground text-sm tracking-tight lg:text-lg">
118+
{label}
119+
</dt>
120+
</div>
121+
<Icon className="hidden size-12 text-muted-foreground opacity-50 lg:block" />
122+
</dl>
123+
);
125124
};

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

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,12 @@ import {
1515
SelectValue,
1616
} from "@/components/ui/select";
1717
import type { WalletStats } from "@3rdweb-sdk/react/hooks/useApi";
18+
import { DotNetIcon } from "components/icons/brand-icons/DotNetIcon";
19+
import { ReactIcon } from "components/icons/brand-icons/ReactIcon";
20+
import { TypeScriptIcon } from "components/icons/brand-icons/TypeScriptIcon";
21+
import { UnityIcon } from "components/icons/brand-icons/UnityIcon";
22+
import { UnrealIcon } from "components/icons/brand-icons/UnrealIcon";
23+
import { DocLink } from "components/shared/DocLink";
1824
import { format } from "date-fns";
1925
import { useMemo, useState } from "react";
2026
import { Bar, BarChart, CartesianGrid, LabelList, XAxis } from "recharts";
@@ -129,7 +135,45 @@ export function DailyConnectionsChartCard(props: {
129135
{props.isPending ? (
130136
<LoadingChartState />
131137
) : barChartData.length === 0 ? (
132-
<EmptyChartState />
138+
<EmptyChartState>
139+
<div className="flex flex-col items-center justify-center">
140+
<span className="mb-6 text-lg">
141+
Send your first connect event
142+
</span>
143+
<div className="flex max-w-md flex-wrap items-center justify-center gap-x-6 gap-y-4">
144+
<DocLink
145+
link="https://portal.thirdweb.com/typescript/v5/getting-started"
146+
label="TypeScript"
147+
icon={TypeScriptIcon}
148+
/>
149+
<DocLink
150+
link="https://portal.thirdweb.com/react/v5"
151+
label="React"
152+
icon={ReactIcon}
153+
/>
154+
<DocLink
155+
link="https://portal.thirdweb.com/react-native/v5"
156+
label="React Native"
157+
icon={ReactIcon}
158+
/>
159+
<DocLink
160+
link="https://portal.thirdweb.com/dotnet/getting-started"
161+
label="Unity"
162+
icon={UnityIcon}
163+
/>
164+
<DocLink
165+
link="https://portal.thirdweb.com/unreal-engine/getting-started"
166+
label="Unreal Engine"
167+
icon={UnrealIcon}
168+
/>
169+
<DocLink
170+
link="https://portal.thirdweb.com/dotnet/getting-started"
171+
label=".NET"
172+
icon={DotNetIcon}
173+
/>
174+
</div>
175+
</div>
176+
</EmptyChartState>
133177
) : (
134178
<BarChart
135179
accessibilityLayer

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,14 @@ const skeletonChartConfig = {
2222
},
2323
} satisfies ChartConfig;
2424

25-
export function EmptyChartState() {
25+
export function EmptyChartState({ children }: { children?: React.ReactNode }) {
2626
const barChartData = useMemo(() => generateRandomData(), []);
2727

2828
return (
2929
<div className="relative z-0 h-full w-full">
30-
<span className="absolute inset-0 z-[1] flex items-center justify-center font-semibold text-base text-muted-foreground">
31-
No data available
32-
</span>
30+
<div className="absolute inset-0 z-[1] flex flex-col items-center justify-center font-semibold text-base text-muted-foreground">
31+
{children ?? "No data available"}
32+
</div>
3333
<SkeletonBarChart data={barChartData} />
3434
</div>
3535
);

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

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,12 @@ import {
99
ChartTooltipContent,
1010
} from "@/components/ui/chart";
1111
import type { UserOpStats } from "@3rdweb-sdk/react/hooks/useApi";
12+
import { DotNetIcon } from "components/icons/brand-icons/DotNetIcon";
13+
import { ReactIcon } from "components/icons/brand-icons/ReactIcon";
14+
import { TypeScriptIcon } from "components/icons/brand-icons/TypeScriptIcon";
15+
import { UnityIcon } from "components/icons/brand-icons/UnityIcon";
16+
import { UnrealIcon } from "components/icons/brand-icons/UnrealIcon";
17+
import { DocLink } from "components/shared/DocLink";
1218
import { format } from "date-fns";
1319
import { useMemo } from "react";
1420
import { Bar, BarChart, CartesianGrid, XAxis } from "recharts";
@@ -85,7 +91,45 @@ export function SponsoredTransactionsChartCard(props: {
8591
{props.isPending ? (
8692
<LoadingChartState />
8793
) : barChartData.length === 0 ? (
88-
<EmptyChartState />
94+
<EmptyChartState>
95+
<div className="flex flex-col items-center justify-center">
96+
<span className="mb-6 text-lg">
97+
Send your first sponsored transaction
98+
</span>
99+
<div className="flex max-w-md flex-wrap items-center justify-center gap-x-6 gap-y-4">
100+
<DocLink
101+
link="https://portal.thirdweb.com/typescript/v5/account-abstraction/batching-transactions"
102+
label="TypeScript"
103+
icon={TypeScriptIcon}
104+
/>
105+
<DocLink
106+
link="https://portal.thirdweb.com/react/v5/account-abstraction/batching-transactions"
107+
label="React"
108+
icon={ReactIcon}
109+
/>
110+
<DocLink
111+
link="https://portal.thirdweb.com/react/v5/account-abstraction/get-started"
112+
label="React Native"
113+
icon={ReactIcon}
114+
/>
115+
<DocLink
116+
link="https://portal.thirdweb.com/unity/v5/wallets/account-abstraction"
117+
label="Unity"
118+
icon={UnityIcon}
119+
/>
120+
<DocLink
121+
link="https://portal.thirdweb.com/unreal-engine/blueprints/smart-wallet"
122+
label="Unreal Engine"
123+
icon={UnrealIcon}
124+
/>
125+
<DocLink
126+
link="https://portal.thirdweb.com/dotnet/wallets/providers/account-abstraction"
127+
label=".NET"
128+
icon={DotNetIcon}
129+
/>
130+
</div>
131+
</div>
132+
</EmptyChartState>
89133
) : (
90134
<BarChart
91135
accessibilityLayer

0 commit comments

Comments
 (0)