Skip to content

Commit d8f55ef

Browse files
committed
Add Connect analytics page in Teams layout (#4384)
## Problem solved Short description of the bug fixed or feature added <!-- start pr-codex --> --- ## PR-Codex overview This PR focuses on updating the analytics feature for team projects. ### Detailed summary - Updated links and routes related to analytics feature - Renamed functions for clarity - Refactored components for analytics visualization - Added new UI components for analytics dashboard > The following files were skipped due to too many changes: `apps/dashboard/src/app/team/[team_slug]/[project_slug]/connect/analytics/_components/ConnectSDKCard.tsx`, `apps/dashboard/src/app/team/[team_slug]/[project_slug]/connect/analytics/ConnectAnalyticsDashboard.tsx`, `apps/dashboard/src/pages/dashboard/connect/analytics.tsx` > ✨ Ask PR-Codex anything about this PR by commenting with `/codex {your question}` <!-- end pr-codex -->
1 parent d254505 commit d8f55ef

17 files changed

+243
-308
lines changed

apps/dashboard/src/app/team/[team_slug]/(team)/TeamOverviewPage.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ function ProjectCard(props: {
110110
<Link
111111
className="static group before:absolute before:top-0 before:bottom-0 before:left-0 before:right-0 before:z-0"
112112
// remove /connect when we have overview page
113-
href={`/team/${team_slug}/${project.slug}/connect/in-app-wallets`}
113+
href={`/team/${team_slug}/${project.slug}/connect/analytics`}
114114
>
115115
<h2 className="text-base font-medium">{project.name}</h2>
116116
</Link>
Lines changed: 14 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -1,72 +1,21 @@
1-
import type { WalletStats } from "@3rdweb-sdk/react/hooks/useApi";
2-
import { CableIcon, WalletCardsIcon } from "lucide-react";
3-
import type React from "react";
4-
import { useMemo } from "react";
5-
import { DailyConnectionsChartCard } from "./_components/DailyConnectionsChartCard";
6-
import { WalletConnectorsChartCard } from "./_components/WalletConnectorsChartCard";
7-
import { WalletDistributionChartCard } from "./_components/WalletDistributionChartCard";
1+
"use client";
2+
3+
import { useWalletStats } from "@3rdweb-sdk/react/hooks/useApi";
4+
import { ConnectAnalyticsDashboardUI } from "./ConnectAnalyticsDashboardUI";
85

96
export function ConnectAnalyticsDashboard(props: {
10-
walletStats: WalletStats;
11-
isLoading: boolean;
7+
clientId: string;
128
}) {
13-
const { totalWallets, uniqueWallets } = useMemo(() => {
14-
return props.walletStats.timeSeries.reduce(
15-
(acc, curr) => {
16-
acc.totalWallets += curr.totalWallets;
17-
acc.uniqueWallets += curr.uniqueWallets;
18-
return acc;
19-
},
20-
{ uniqueWallets: 0, totalWallets: 0 },
21-
);
22-
}, [props.walletStats]);
9+
const statsQuery = useWalletStats(props.clientId);
2310

2411
return (
25-
<div className="flex flex-col gap-4 lg:gap-6">
26-
{/* Summary Stat Cards */}
27-
<div className="grid grid-cols-2 gap-4 lg:gap-6">
28-
<Stat label="Connections" value={totalWallets} icon={CableIcon} />
29-
<Stat
30-
label="Unique Wallets"
31-
value={uniqueWallets}
32-
icon={WalletCardsIcon}
33-
/>
34-
</div>
35-
36-
<DailyConnectionsChartCard
37-
walletStats={props.walletStats}
38-
isLoading={props.isLoading}
39-
/>
40-
41-
<WalletConnectorsChartCard
42-
walletStats={props.walletStats}
43-
isLoading={props.isLoading}
44-
/>
45-
46-
<WalletDistributionChartCard
47-
walletStats={props.walletStats}
48-
isLoading={props.isLoading}
49-
/>
50-
</div>
12+
<ConnectAnalyticsDashboardUI
13+
walletStats={
14+
statsQuery.data || {
15+
timeSeries: [],
16+
}
17+
}
18+
isLoading={statsQuery.isLoading}
19+
/>
5120
);
5221
}
53-
54-
const Stat: React.FC<{
55-
label: string;
56-
value?: number;
57-
icon: React.FC<{ className?: string }>;
58-
}> = ({ label, value, icon: Icon }) => {
59-
return (
60-
<dl className="bg-muted/50 rounded-lg border border-border p-4 lg:p-6 flex items-center gap-4 justify-between">
61-
<div>
62-
<dd className="text-3xl lg:text-5xl font-semibold tracking-tight">
63-
{value}
64-
</dd>
65-
<dt className="text-sm lg:text-lg font-medium text-muted-foreground tracking-tight">
66-
{label}
67-
</dt>
68-
</div>
69-
<Icon className="text-muted-foreground size-12 opacity-50 hidden lg:block" />
70-
</dl>
71-
);
72-
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import type { WalletStats } from "@3rdweb-sdk/react/hooks/useApi";
2+
import { CableIcon, WalletCardsIcon } from "lucide-react";
3+
import type React from "react";
4+
import { useMemo } from "react";
5+
import { DailyConnectionsChartCard } from "./_components/DailyConnectionsChartCard";
6+
import { WalletConnectorsChartCard } from "./_components/WalletConnectorsChartCard";
7+
import { WalletDistributionChartCard } from "./_components/WalletDistributionChartCard";
8+
9+
export function ConnectAnalyticsDashboardUI(props: {
10+
walletStats: WalletStats;
11+
isLoading: boolean;
12+
}) {
13+
const { totalWallets, uniqueWallets } = useMemo(() => {
14+
return props.walletStats.timeSeries.reduce(
15+
(acc, curr) => {
16+
acc.totalWallets += curr.totalWallets;
17+
acc.uniqueWallets += curr.uniqueWallets;
18+
return acc;
19+
},
20+
{ uniqueWallets: 0, totalWallets: 0 },
21+
);
22+
}, [props.walletStats]);
23+
24+
return (
25+
<div className="flex flex-col gap-4 lg:gap-6">
26+
{/* Summary Stat Cards */}
27+
<div className="grid grid-cols-2 gap-4 lg:gap-6">
28+
<Stat label="Connections" value={totalWallets} icon={CableIcon} />
29+
<Stat
30+
label="Unique Wallets"
31+
value={uniqueWallets}
32+
icon={WalletCardsIcon}
33+
/>
34+
</div>
35+
36+
<DailyConnectionsChartCard
37+
walletStats={props.walletStats}
38+
isLoading={props.isLoading}
39+
/>
40+
41+
<WalletConnectorsChartCard
42+
walletStats={props.walletStats}
43+
isLoading={props.isLoading}
44+
/>
45+
46+
<WalletDistributionChartCard
47+
walletStats={props.walletStats}
48+
isLoading={props.isLoading}
49+
/>
50+
</div>
51+
);
52+
}
53+
54+
const Stat: React.FC<{
55+
label: string;
56+
value?: number;
57+
icon: React.FC<{ className?: string }>;
58+
}> = ({ label, value, icon: Icon }) => {
59+
return (
60+
<dl className="bg-muted/50 rounded-lg border border-border p-4 lg:p-6 flex items-center gap-4 justify-between">
61+
<div>
62+
<dd className="text-3xl lg:text-5xl font-semibold tracking-tight">
63+
{value}
64+
</dd>
65+
<dt className="text-sm lg:text-lg font-medium text-muted-foreground tracking-tight">
66+
{label}
67+
</dt>
68+
</div>
69+
<Icon className="text-muted-foreground size-12 opacity-50 hidden lg:block" />
70+
</dl>
71+
);
72+
};

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import type { Meta, StoryObj } from "@storybook/react";
22
import { mobileViewport } from "../../../../../../../stories/utils";
3-
import { ConnectAnalyticsDashboard } from "../ConnectAnalyticsDashboard";
4-
import { createWalletStatusStub } from "./storyUtils";
3+
import { ConnectAnalyticsDashboardUI } from "../ConnectAnalyticsDashboardUI";
4+
import { createWalletStatsStub } from "./storyUtils";
55

66
const meta = {
77
title: "Charts/Connect/Analytics Dashboard",
@@ -28,8 +28,8 @@ export const Mobile: Story = {
2828
function Component() {
2929
return (
3030
<div className="min-h-screen bg-background p-4 text-foreground gap-6 max-w-[1000px] flex flex-col mx-auto py-10">
31-
<ConnectAnalyticsDashboard
32-
walletStats={createWalletStatusStub(30)}
31+
<ConnectAnalyticsDashboardUI
32+
walletStats={createWalletStatsStub(30)}
3333
isLoading={false}
3434
/>
3535
</div>
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
import { SiReact } from "@react-icons/all-files/si/SiReact";
2+
import { SiTypescript } from "@react-icons/all-files/si/SiTypescript";
3+
import { SiUnity } from "@react-icons/all-files/si/SiUnity";
4+
import Link from "next/link";
5+
import { SiUnrealengine } from "react-icons/si";
6+
import { SiDotnet } from "react-icons/si";
7+
8+
export function ConnectSDKCard() {
9+
return (
10+
<div className="border border-border bg-muted/50 rounded-lg p-6 relative">
11+
<h3 className="text-2xl font-semibold tracking-tight mb-1">
12+
Connect SDK
13+
</h3>
14+
<p className="mb-8 text-muted-foreground text-sm">
15+
Add the Connect SDK to your app to start collecting analytics.
16+
</p>
17+
18+
<div className="grid gap-6 md:gap-4 grid-cols-1 md:grid-cols-2 lg:grid-cols-3 max-w-[500px]">
19+
<DocLink
20+
link="https://portal.thirdweb.com/typescript/v5/getting-started"
21+
icon={SiTypescript}
22+
label="TypeScript SDK"
23+
/>
24+
<DocLink
25+
link="https://portal.thirdweb.com/react/v5/getting-started"
26+
icon={SiReact}
27+
label="React SDK"
28+
/>
29+
<DocLink
30+
link="https://portal.thirdweb.com/react-native/v5/getting-started"
31+
icon={SiReact}
32+
label="React Native SDK"
33+
/>
34+
<DocLink
35+
link="https://portal.thirdweb.com/unity/v4/getting-started"
36+
icon={SiUnity}
37+
label="Unity SDK"
38+
/>
39+
<DocLink
40+
link="https://portal.thirdweb.com/unreal/getting-started"
41+
icon={SiUnrealengine}
42+
label="Unreal SDK"
43+
/>
44+
<DocLink
45+
link="https://portal.thirdweb.com/dotnet/getting-started"
46+
icon={SiDotnet}
47+
label=".NET SDK"
48+
/>
49+
</div>
50+
51+
<BackgroundPattern />
52+
</div>
53+
);
54+
}
55+
56+
function BackgroundPattern() {
57+
const color = "hsl(var(--foreground)/50%)";
58+
return (
59+
<div
60+
className="hidden xl:block absolute w-[50%] right-2 top-4 bottom-4 z-[1]"
61+
style={{
62+
backgroundImage: `radial-gradient(${color} 1px, transparent 1px)`,
63+
backgroundSize: "20px 20px",
64+
maskImage: "linear-gradient(to left, black, transparent)",
65+
}}
66+
/>
67+
);
68+
}
69+
70+
function DocLink(props: {
71+
link: string;
72+
label: string;
73+
icon: React.FC<{ className?: string }>;
74+
}) {
75+
return (
76+
<Link
77+
href={props.link}
78+
target="_blank"
79+
className="text-muted-foreground hover:text-foreground flex items-center gap-2 text-sm"
80+
>
81+
<props.icon className="size-4" />
82+
{props.label}
83+
</Link>
84+
);
85+
}

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

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import {
44
mobileViewport,
55
} from "../../../../../../../stories/utils";
66
import { DailyConnectionsChartCard } from "./DailyConnectionsChartCard";
7-
import { createWalletStatusStub } from "./storyUtils";
7+
import { createWalletStatsStub } from "./storyUtils";
88

99
const meta = {
1010
title: "Charts/Connect/DailyConnections",
@@ -33,35 +33,35 @@ function Component() {
3333
<div className="min-h-screen bg-background p-4 text-foreground gap-6 max-w-[1000px] flex flex-col mx-auto">
3434
<BadgeContainer label="30 days">
3535
<DailyConnectionsChartCard
36-
walletStats={createWalletStatusStub(30)}
36+
walletStats={createWalletStatsStub(30)}
3737
isLoading={false}
3838
/>
3939
</BadgeContainer>
4040

4141
<BadgeContainer label="60 days">
4242
<DailyConnectionsChartCard
43-
walletStats={createWalletStatusStub(60)}
43+
walletStats={createWalletStatsStub(60)}
4444
isLoading={false}
4545
/>
4646
</BadgeContainer>
4747

4848
<BadgeContainer label="10 days">
4949
<DailyConnectionsChartCard
50-
walletStats={createWalletStatusStub(10)}
50+
walletStats={createWalletStatsStub(10)}
5151
isLoading={false}
5252
/>
5353
</BadgeContainer>
5454

5555
<BadgeContainer label="0 days">
5656
<DailyConnectionsChartCard
57-
walletStats={createWalletStatusStub(0)}
57+
walletStats={createWalletStatsStub(0)}
5858
isLoading={false}
5959
/>
6060
</BadgeContainer>
6161

6262
<BadgeContainer label="Loading">
6363
<DailyConnectionsChartCard
64-
walletStats={createWalletStatusStub(0)}
64+
walletStats={createWalletStatsStub(0)}
6565
isLoading={true}
6666
/>
6767
</BadgeContainer>

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
"use client";
2+
13
import {
24
type ChartConfig,
35
ChartContainer,

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
"use client";
2+
13
import { Spinner } from "@/components/ui/Spinner/Spinner";
24
import { type ChartConfig, ChartContainer } from "@/components/ui/chart";
35
import { useMemo } from "react";

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

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import {
44
mobileViewport,
55
} from "../../../../../../../stories/utils";
66
import { WalletConnectorsChartCard } from "./WalletConnectorsChartCard";
7-
import { createWalletStatusStub } from "./storyUtils";
7+
import { createWalletStatsStub } from "./storyUtils";
88

99
const meta = {
1010
title: "Charts/Connect/Wallet Connectors",
@@ -33,35 +33,35 @@ function Component() {
3333
<div className="min-h-screen bg-background p-4 text-foreground gap-6 max-w-[1000px] flex flex-col mx-auto">
3434
<BadgeContainer label="30 days">
3535
<WalletConnectorsChartCard
36-
walletStats={createWalletStatusStub(30)}
36+
walletStats={createWalletStatsStub(30)}
3737
isLoading={false}
3838
/>
3939
</BadgeContainer>
4040

4141
<BadgeContainer label="60 days">
4242
<WalletConnectorsChartCard
43-
walletStats={createWalletStatusStub(60)}
43+
walletStats={createWalletStatsStub(60)}
4444
isLoading={false}
4545
/>
4646
</BadgeContainer>
4747

4848
<BadgeContainer label="10 days">
4949
<WalletConnectorsChartCard
50-
walletStats={createWalletStatusStub(10)}
50+
walletStats={createWalletStatsStub(10)}
5151
isLoading={false}
5252
/>
5353
</BadgeContainer>
5454

5555
<BadgeContainer label="0 days">
5656
<WalletConnectorsChartCard
57-
walletStats={createWalletStatusStub(0)}
57+
walletStats={createWalletStatsStub(0)}
5858
isLoading={false}
5959
/>
6060
</BadgeContainer>
6161

6262
<BadgeContainer label="Loading">
6363
<WalletConnectorsChartCard
64-
walletStats={createWalletStatusStub(0)}
64+
walletStats={createWalletStatsStub(0)}
6565
isLoading={true}
6666
/>
6767
</BadgeContainer>

0 commit comments

Comments
 (0)