Skip to content

Commit c6849a8

Browse files
committed
[MNY-61] Dashboard: Tokens and Payments tracking updates (#7781)
<!-- ## title your PR with this format: "[SDK/Dashboard/Portal] Feature/Fix: Concise title for the changes" If you did not copy the branch name from Linear, paste the issue tag here (format is TEAM-0000): ## Notes for the reviewer Anything important to call out? Be sure to also clarify these in your comments. ## How to test Unit tests, playground, etc. --> <!-- start pr-codex --> --- ## PR-Codex overview This PR focuses on enhancing the payment link functionality and analytics reporting within the application. It introduces new hooks, modifies existing components, and refines analytics events to improve tracking and user experience. ### Detailed summary - Added `useEffectOnce` hook for single execution of effects. - Implemented `AssetPageView` component to report asset page views. - Updated `CreatePaymentLinkButton` to report link creation. - Enhanced payment link reporting with success and failure events. - Removed unused analytics functions related to asset creation. > ✨ Ask PR-Codex anything about this PR by commenting with `/codex {your question}` <!-- end pr-codex -->
1 parent 7617cc8 commit c6849a8

File tree

12 files changed

+105
-49
lines changed

12 files changed

+105
-49
lines changed

apps/dashboard/src/@/analytics/report.ts

Lines changed: 38 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
"use client";
12
import posthog from "posthog-js";
23

34
import type { Team } from "@/api/team";
@@ -269,22 +270,6 @@ export function reportAssetBuyFailed(properties: {
269270

270271
// Assets Landing Page ----------------------------
271272

272-
/**
273-
* ### Why do we need to report this event?
274-
* - To track number of asset creation started from the assets page
275-
* - To track which asset types are being created the most
276-
*
277-
* ### Who is responsible for this event?
278-
* @MananTank
279-
*/
280-
export function reportAssetCreationStarted(properties: {
281-
assetType: "nft" | "coin";
282-
}) {
283-
posthog.capture("asset creation started", {
284-
assetType: properties.assetType,
285-
});
286-
}
287-
288273
/**
289274
* ### Why do we need to report this event?
290275
* - To track number of assets imported successfully from the assets page
@@ -431,28 +416,59 @@ export function reportPaymentCardClick(properties: { id: string }) {
431416

432417
/**
433418
* ### Why do we need to report this event?
434-
* - To track payment link usage
419+
* - To create a funnel "create payment link pageview" -> "payment link created" to understand the conversion rate
420+
*
421+
* ### Who is responsible for this event?
422+
* @greg
423+
*/
424+
export function reportPaymentLinkCreated(properties: {
425+
linkId: string;
426+
clientId: string;
427+
}) {
428+
posthog.capture("payment link created", properties);
429+
}
430+
431+
/**
432+
* ### Why do we need to report this event?
433+
* - To track funnel "payment link pageview" -> "payment link buy successful" to understand the conversion rate
435434
*
436435
* ### Who is responsible for this event?
437436
* @greg
438437
*/
439-
export function reportPaymentLinkVisited(properties: {
438+
export function reportPaymentLinkBuySuccessful(properties: {
440439
linkId: string;
441440
clientId: string;
442441
}) {
443-
posthog.capture("payment link visited", properties);
442+
posthog.capture("payment link buy successful", properties);
444443
}
445444

446445
/**
447446
* ### Why do we need to report this event?
448-
* - To track payment link usage
447+
* - To track the number of failed payment link buys
448+
* - To track what errors users encounter when trying to buy from a payment link
449449
*
450450
* ### Who is responsible for this event?
451451
* @greg
452452
*/
453-
export function reportPaymentLinkCompleted(properties: {
453+
export function reportPaymentLinkBuyFailed(properties: {
454454
linkId: string;
455455
clientId: string;
456+
errorMessage: string;
457+
}) {
458+
posthog.capture("payment link buy failed", properties);
459+
}
460+
461+
/**
462+
* ### Why do we need to report this event?
463+
* - To create a funnel for "asset pageview" -> "asset purchase successful" to understand the conversion rate
464+
* - To understand which asset types are being viewed the most
465+
*
466+
* ### Who is responsible for this event?
467+
* @MananTank
468+
*/
469+
export function reportAssetPageview(properties: {
470+
assetType: "nft" | "coin";
471+
chainId: number;
456472
}) {
457-
posthog.capture("payment link completed", properties);
473+
posthog.capture("asset pageview", properties);
458474
}

apps/dashboard/src/@/api/universal-bridge/developer.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,11 @@ export async function createPaymentLink(props: {
200200
throw new Error(text);
201201
}
202202

203-
return;
203+
const response = (await res.json()) as {
204+
data: PaymentLink;
205+
};
206+
207+
return response.data;
204208
}
205209

206210
export async function deletePaymentLink(props: {
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
/** biome-ignore-all lint/correctness/useExhaustiveDependencies: we only want to run effect once */
2+
"use client";
3+
import { useEffect, useRef } from "react";
4+
5+
export function useEffectOnce(effect: () => void) {
6+
const hasCalledEffect = useRef(false);
7+
8+
// eslint-disable-next-line no-restricted-syntax
9+
useEffect(() => {
10+
if (hasCalledEffect.current) return;
11+
hasCalledEffect.current = true;
12+
effect();
13+
}, []);
14+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
"use client";
2+
import { reportAssetPageview } from "@/analytics/report";
3+
import { useEffectOnce } from "@/hooks/useEffectOnce";
4+
5+
export function AssetPageView(props: {
6+
assetType: "nft" | "coin";
7+
chainId: number;
8+
}) {
9+
useEffectOnce(() => {
10+
reportAssetPageview({
11+
assetType: props.assetType,
12+
chainId: props.chainId,
13+
});
14+
});
15+
16+
return null;
17+
}

apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/erc20/erc20.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { getContractMetadata } from "thirdweb/extensions/common";
44
import { decimals, getActiveClaimCondition } from "thirdweb/extensions/erc20";
55
import { GridPattern } from "@/components/ui/background-patterns";
66
import { resolveFunctionSelectors } from "@/lib/selectors";
7+
import { AssetPageView } from "../_components/asset-page-view";
78
import { getContractCreator } from "../_components/getContractCreator";
89
import { PageHeader } from "../_components/PageHeader";
910
import { getTokenPriceData } from "./_apis/token-price-data";
@@ -85,6 +86,7 @@ export async function ERC20PublicPage(props: {
8586

8687
return (
8788
<div className="flex grow flex-col">
89+
<AssetPageView assetType="coin" chainId={props.chainMetadata.chainId} />
8890
<PageHeader containerClassName="max-w-5xl" />
8991

9092
<div className="border-b">

apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/nft-page-layout.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import type { ThirdwebContract } from "thirdweb";
22
import type { ChainMetadata } from "thirdweb/chains";
3+
import { AssetPageView } from "../_components/asset-page-view";
34
import { PageHeader } from "../_components/PageHeader";
45
import { ContractHeaderUI } from "../erc20/_components/ContractHeader";
56

@@ -16,6 +17,7 @@ export function NFTPublicPageLayout(props: {
1617
}) {
1718
return (
1819
<div className="flex grow flex-col">
20+
<AssetPageView assetType="nft" chainId={props.chainMetadata.chainId} />
1921
<PageHeader containerClassName="max-w-8xl" />
2022
<div className="border-b">
2123
<div className="container max-w-8xl">

apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/components/RecentPaymentsSection.client.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,6 @@ export function RecentPaymentsSection(props: {
9999
>
100100
<Link
101101
href={`/team/${props.teamSlug}/${props.projectSlug}/payments/links`}
102-
target="_blank"
103102
>
104103
Create Payment Link
105104
<ArrowRightIcon className="size-4" />

apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/links/components/CreatePaymentLinkButton.client.tsx

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { toast } from "sonner";
1010
import { Bridge, toUnits } from "thirdweb";
1111
import { checksumAddress } from "thirdweb/utils";
1212
import z from "zod";
13+
import { reportPaymentLinkCreated } from "@/analytics/report";
1314
import { createPaymentLink } from "@/api/universal-bridge/developer";
1415
import { getUniversalBridgeTokens } from "@/api/universal-bridge/tokens";
1516
import { SingleNetworkSelector } from "@/components/blocks/NetworkSelectors";
@@ -91,7 +92,7 @@ export function CreatePaymentLinkButton(
9192
throw new Error("Invalid recipient address.");
9293
}
9394

94-
await createPaymentLink({
95+
const result = await createPaymentLink({
9596
clientId: props.clientId,
9697
teamId: props.teamId,
9798
intent: {
@@ -102,7 +103,8 @@ export function CreatePaymentLinkButton(
102103
},
103104
title: values.title,
104105
});
105-
return null;
106+
107+
return result;
106108
},
107109
onSuccess: () => {
108110
toast.success("Payment link created successfully.");
@@ -153,7 +155,12 @@ export function CreatePaymentLinkButton(
153155
className="flex flex-col gap-6"
154156
onSubmit={form.handleSubmit((values) =>
155157
createMutation.mutateAsync(values, {
156-
onSuccess: () => {
158+
onSuccess: (result) => {
159+
reportPaymentLinkCreated({
160+
linkId: result.id,
161+
clientId: props.clientId,
162+
});
163+
157164
setOpen(false);
158165
form.reset();
159166
form.clearErrors();

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

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import Link from "next/link";
55
import { useState } from "react";
66
import type { ThirdwebClient } from "thirdweb";
77
import {
8-
reportAssetCreationStarted,
98
reportAssetImportStarted,
109
reportAssetImportSuccessful,
1110
} from "@/analytics/report";
@@ -43,23 +42,13 @@ export function Cards(props: {
4342
description="Launch your own ERC-20 coin"
4443
href={`/team/${props.teamSlug}/${props.projectSlug}/tokens/create/token`}
4544
icon={CoinsIcon}
46-
onClick={() => {
47-
reportAssetCreationStarted({
48-
assetType: "coin",
49-
});
50-
}}
5145
title="Create Coin"
5246
/>
5347

5448
<CardLink
5549
description="Launch your own NFT collection"
5650
href={`/team/${props.teamSlug}/${props.projectSlug}/tokens/create/nft`}
5751
icon={ImagesIcon}
58-
onClick={() => {
59-
reportAssetCreationStarted({
60-
assetType: "nft",
61-
});
62-
}}
6352
title="Create NFT Collection"
6453
/>
6554

apps/dashboard/src/app/login/onboarding/team-onboarding/team-onboarding.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
"use client";
2-
import { useEffect } from "react";
32
import { toast } from "sonner";
43
import type { ThirdwebClient } from "thirdweb";
54
import { upload } from "thirdweb/storage";
@@ -10,6 +9,7 @@ import {
109
reportOnboardingStarted,
1110
} from "@/analytics/report";
1211
import type { Team } from "@/api/team";
12+
import { useEffectOnce } from "@/hooks/useEffectOnce";
1313
import { useDashboardRouter } from "@/lib/DashboardRouter";
1414
import { updateTeam } from "../../../(app)/team/[team_slug]/(team)/~/settings/general/updateTeam";
1515
import { InviteTeamMembersUI } from "./InviteTeamMembers";
@@ -23,9 +23,9 @@ export function TeamInfoForm(props: {
2323
const router = useDashboardRouter();
2424

2525
// eslint-disable-next-line no-restricted-syntax
26-
useEffect(() => {
26+
useEffectOnce(() => {
2727
reportOnboardingStarted();
28-
}, []);
28+
});
2929

3030
return (
3131
<TeamInfoFormUI

0 commit comments

Comments
 (0)