Skip to content

Commit dd77c7f

Browse files
committed
Analytics UI Empty States (#5121)
<img width="968" alt="Screenshot 2024-10-21 at 6 23 23 PM" src="https://github.com/user-attachments/assets/86000f33-2041-4d3a-b084-b6367e61f032"> <!-- start pr-codex --> --- ## PR-Codex overview This PR focuses on enhancing the `DocLink` component for better link handling and integrating it into various chart components for improved user guidance on wallet connections and transactions. ### Detailed summary - Added `DocLink` component in `DocLink.tsx`. - Refactored `ConnectSDKCard` to use `DocLink` instead of its previous implementation. - Modified `EmptyChartState` to accept `children` for customizable content. - Integrated `DocLink` into multiple chart components, providing links for wallet integration guidance. > ✨ Ask PR-Codex anything about this PR by commenting with `/codex {your question}` <!-- end pr-codex -->
1 parent 9c3ff94 commit dd77c7f

File tree

8 files changed

+216
-33
lines changed

8 files changed

+216
-33
lines changed

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: 49 additions & 5 deletions
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";
@@ -41,13 +47,13 @@ export function SponsoredTransactionsChartCard(props: {
4147

4248
for (const data of userOpStats) {
4349
const chartData = chartDataMap.get(data.date);
44-
if (!chartData) {
50+
if (!chartData && data.sponsoredUsd > 0) {
4551
chartDataMap.set(data.date, {
4652
time: format(new Date(data.date), "MMM dd"),
4753
successful: data.successful,
4854
failed: data.failed,
4955
});
50-
} else {
56+
} else if (chartData && data.sponsoredUsd > 0) {
5157
chartData.successful += data.successful;
5258
chartData.failed += data.failed;
5359
}
@@ -60,11 +66,11 @@ export function SponsoredTransactionsChartCard(props: {
6066

6167
return (
6268
<div className="relative w-full rounded-lg border border-border bg-muted/50 p-4 md:p-6">
63-
<h3 className="mb-1 font-semibold text-xl tracking-tight md:text-2xl">
69+
<h3 className="mb-4 font-semibold text-xl tracking-tight md:text-2xl">
6470
Sponsored Transactions
6571
</h3>
6672

67-
<div className="top-6 right-6 mb-4 grid grid-cols-2 items-center gap-2 md:absolute md:mb-0 md:flex">
73+
<div className="top-6 right-6 mb-8 grid grid-cols-2 items-center gap-2 md:absolute md:mb-0 md:flex">
6874
<ExportToCSVButton
6975
className="bg-background"
7076
fileName="Sponsored Transactions"
@@ -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

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

Lines changed: 45 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,12 @@ import {
88
ChartTooltipContent,
99
} from "@/components/ui/chart";
1010
import type { UserOpStats } from "@3rdweb-sdk/react/hooks/useApi";
11+
import { DotNetIcon } from "components/icons/brand-icons/DotNetIcon";
12+
import { ReactIcon } from "components/icons/brand-icons/ReactIcon";
13+
import { TypeScriptIcon } from "components/icons/brand-icons/TypeScriptIcon";
14+
import { UnityIcon } from "components/icons/brand-icons/UnityIcon";
15+
import { UnrealIcon } from "components/icons/brand-icons/UnrealIcon";
16+
import { DocLink } from "components/shared/DocLink";
1117
import { format } from "date-fns";
1218
import { useMemo } from "react";
1319
import { Bar, BarChart, CartesianGrid, LabelList, XAxis } from "recharts";
@@ -35,12 +41,12 @@ export function TotalSponsoredChartCard(props: {
3541

3642
for (const data of userOpStats) {
3743
const chartData = chartDataMap.get(data.date);
38-
if (!chartData) {
44+
if (!chartData && data.sponsoredUsd > 0) {
3945
chartDataMap.set(data.date, {
4046
time: format(new Date(data.date), "MMM dd"),
4147
sponsoredUsd: data.sponsoredUsd,
4248
});
43-
} else {
49+
} else if (chartData && data.sponsoredUsd > 0) {
4450
chartData.sponsoredUsd += data.sponsoredUsd;
4551
}
4652
}
@@ -83,7 +89,43 @@ export function TotalSponsoredChartCard(props: {
8389
{props.isPending ? (
8490
<LoadingChartState />
8591
) : barChartData.length === 0 ? (
86-
<EmptyChartState />
92+
<EmptyChartState>
93+
<div className="flex flex-col items-center justify-center">
94+
<span className="mb-6 text-lg">Sponsor gas for your users</span>
95+
<div className="flex max-w-md flex-wrap items-center justify-center gap-x-6 gap-y-4">
96+
<DocLink
97+
link="https://portal.thirdweb.com/typescript/v5/account-abstraction/get-started"
98+
label="TypeScript"
99+
icon={TypeScriptIcon}
100+
/>
101+
<DocLink
102+
link="https://portal.thirdweb.com/react/v5/account-abstraction/get-started"
103+
label="React"
104+
icon={ReactIcon}
105+
/>
106+
<DocLink
107+
link="https://portal.thirdweb.com/react/v5/account-abstraction/get-started"
108+
label="React Native"
109+
icon={ReactIcon}
110+
/>
111+
<DocLink
112+
link="https://portal.thirdweb.com/unity/v5/wallets/account-abstraction"
113+
label="Unity"
114+
icon={UnityIcon}
115+
/>
116+
<DocLink
117+
link="https://portal.thirdweb.com/unreal-engine/blueprints/smart-wallet"
118+
label="Unreal Engine"
119+
icon={UnrealIcon}
120+
/>
121+
<DocLink
122+
link="https://portal.thirdweb.com/dotnet/wallets/providers/account-abstraction"
123+
label=".NET"
124+
icon={DotNetIcon}
125+
/>
126+
</div>
127+
</div>
128+
</EmptyChartState>
87129
) : (
88130
<BarChart
89131
accessibilityLayer

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

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ import {
1717
SelectValue,
1818
} from "@/components/ui/select";
1919
import type { WalletStats } from "@3rdweb-sdk/react/hooks/useApi";
20+
import { ReactIcon } from "components/icons/brand-icons/ReactIcon";
21+
import { TypeScriptIcon } from "components/icons/brand-icons/TypeScriptIcon";
22+
import { DocLink } from "components/shared/DocLink";
2023
import { format } from "date-fns";
2124
import { useMemo, useState } from "react";
2225
import { Bar, BarChart, CartesianGrid, XAxis } from "recharts";
@@ -166,7 +169,30 @@ export function WalletConnectorsChartCard(props: {
166169
{props.isPending ? (
167170
<LoadingChartState />
168171
) : chartData.length === 0 ? (
169-
<EmptyChartState />
172+
<EmptyChartState>
173+
<div className="flex flex-col items-center justify-center">
174+
<span className="mb-6 text-lg">
175+
Connect any wallet to your app
176+
</span>
177+
<div className="flex max-w-md flex-wrap items-center justify-center gap-x-6 gap-y-4">
178+
<DocLink
179+
link="https://portal.thirdweb.com/typescript/v5/supported-wallets"
180+
label="TypeScript"
181+
icon={TypeScriptIcon}
182+
/>
183+
<DocLink
184+
link="https://portal.thirdweb.com/typescript/v5/supported-wallets"
185+
label="React"
186+
icon={ReactIcon}
187+
/>
188+
<DocLink
189+
link="https://portal.thirdweb.com/typescript/v5/supported-wallets"
190+
label="React Native"
191+
icon={ReactIcon}
192+
/>
193+
</div>
194+
</div>
195+
</EmptyChartState>
170196
) : (
171197
<BarChart
172198
accessibilityLayer

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

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ import {
1717
SelectValue,
1818
} from "@/components/ui/select";
1919
import type { WalletStats } from "@3rdweb-sdk/react/hooks/useApi";
20+
import { ReactIcon } from "components/icons/brand-icons/ReactIcon";
21+
import { TypeScriptIcon } from "components/icons/brand-icons/TypeScriptIcon";
22+
import { DocLink } from "components/shared/DocLink";
2023
import { useMemo, useState } from "react";
2124
import { Pie, PieChart } from "recharts";
2225
import { EmptyChartState, LoadingChartState } from "./EmptyChartState";
@@ -192,7 +195,30 @@ export function WalletDistributionChartCard(props: {
192195
{props.isPending ? (
193196
<LoadingChartState />
194197
) : chartData.length === 0 ? (
195-
<EmptyChartState />
198+
<EmptyChartState>
199+
<div className="flex flex-col items-center justify-center">
200+
<span className="mb-6 text-lg">
201+
Connect any wallet to your app
202+
</span>
203+
<div className="flex max-w-md flex-wrap items-center justify-center gap-x-6 gap-y-4">
204+
<DocLink
205+
link="https://portal.thirdweb.com/typescript/v5/supported-wallets"
206+
label="TypeScript"
207+
icon={TypeScriptIcon}
208+
/>
209+
<DocLink
210+
link="https://portal.thirdweb.com/typescript/v5/supported-wallets"
211+
label="React"
212+
icon={ReactIcon}
213+
/>
214+
<DocLink
215+
link="https://portal.thirdweb.com/typescript/v5/supported-wallets"
216+
label="React Native"
217+
icon={ReactIcon}
218+
/>
219+
</div>
220+
</div>
221+
</EmptyChartState>
196222
) : (
197223
<PieChart>
198224
<ChartTooltip

apps/dashboard/src/components/shared/ConnectSDKCard.tsx

Lines changed: 1 addition & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { ReactIcon } from "components/icons/brand-icons/ReactIcon";
33
import { TypeScriptIcon } from "components/icons/brand-icons/TypeScriptIcon";
44
import { UnityIcon } from "components/icons/brand-icons/UnityIcon";
55
import { UnrealIcon } from "components/icons/brand-icons/UnrealIcon";
6-
import Link from "next/link";
6+
import { DocLink } from "./DocLink";
77

88
export function ConnectSDKCard({
99
title,
@@ -69,20 +69,3 @@ function BackgroundPattern() {
6969
/>
7070
);
7171
}
72-
73-
function DocLink(props: {
74-
link: string;
75-
label: string;
76-
icon: React.FC<{ className?: string }>;
77-
}) {
78-
return (
79-
<Link
80-
href={props.link}
81-
target="_blank"
82-
className="flex items-center gap-2 text-muted-foreground text-sm hover:text-foreground"
83-
>
84-
<props.icon className="size-4" />
85-
{props.label}
86-
</Link>
87-
);
88-
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import Link from "next/link";
2+
3+
export function DocLink(props: {
4+
link: string;
5+
label: string;
6+
icon: React.FC<{ className?: string }>;
7+
}) {
8+
return (
9+
<Link
10+
href={props.link}
11+
target="_blank"
12+
className="flex items-center gap-2 whitespace-nowrap text-muted-foreground text-sm hover:text-foreground"
13+
>
14+
<props.icon className="size-4" />
15+
{props.label}
16+
</Link>
17+
);
18+
}

0 commit comments

Comments
 (0)