diff --git a/apps/dashboard/src/@/analytics/report.ts b/apps/dashboard/src/@/analytics/report.ts index dcc55ad4fb1..1f3352e90ea 100644 --- a/apps/dashboard/src/@/analytics/report.ts +++ b/apps/dashboard/src/@/analytics/report.ts @@ -1,3 +1,4 @@ +"use client"; import posthog from "posthog-js"; import type { Team } from "@/api/team"; @@ -269,22 +270,6 @@ export function reportAssetBuyFailed(properties: { // Assets Landing Page ---------------------------- -/** - * ### Why do we need to report this event? - * - To track number of asset creation started from the assets page - * - To track which asset types are being created the most - * - * ### Who is responsible for this event? - * @MananTank - */ -export function reportAssetCreationStarted(properties: { - assetType: "nft" | "coin"; -}) { - posthog.capture("asset creation started", { - assetType: properties.assetType, - }); -} - /** * ### Why do we need to report this event? * - To track number of assets imported successfully from the assets page @@ -431,28 +416,59 @@ export function reportPaymentCardClick(properties: { id: string }) { /** * ### Why do we need to report this event? - * - To track payment link usage + * - To create a funnel "create payment link pageview" -> "payment link created" to understand the conversion rate + * + * ### Who is responsible for this event? + * @greg + */ +export function reportPaymentLinkCreated(properties: { + linkId: string; + clientId: string; +}) { + posthog.capture("payment link created", properties); +} + +/** + * ### Why do we need to report this event? + * - To track funnel "payment link pageview" -> "payment link buy successful" to understand the conversion rate * * ### Who is responsible for this event? * @greg */ -export function reportPaymentLinkVisited(properties: { +export function reportPaymentLinkBuySuccessful(properties: { linkId: string; clientId: string; }) { - posthog.capture("payment link visited", properties); + posthog.capture("payment link buy successful", properties); } /** * ### Why do we need to report this event? - * - To track payment link usage + * - To track the number of failed payment link buys + * - To track what errors users encounter when trying to buy from a payment link * * ### Who is responsible for this event? * @greg */ -export function reportPaymentLinkCompleted(properties: { +export function reportPaymentLinkBuyFailed(properties: { linkId: string; clientId: string; + errorMessage: string; +}) { + posthog.capture("payment link buy failed", properties); +} + +/** + * ### Why do we need to report this event? + * - To create a funnel for "asset pageview" -> "asset purchase successful" to understand the conversion rate + * - To understand which asset types are being viewed the most + * + * ### Who is responsible for this event? + * @MananTank + */ +export function reportAssetPageview(properties: { + assetType: "nft" | "coin"; + chainId: number; }) { - posthog.capture("payment link completed", properties); + posthog.capture("asset pageview", properties); } diff --git a/apps/dashboard/src/@/api/universal-bridge/developer.ts b/apps/dashboard/src/@/api/universal-bridge/developer.ts index 05a4c9709be..f5987f3ef8e 100644 --- a/apps/dashboard/src/@/api/universal-bridge/developer.ts +++ b/apps/dashboard/src/@/api/universal-bridge/developer.ts @@ -200,7 +200,11 @@ export async function createPaymentLink(props: { throw new Error(text); } - return; + const response = (await res.json()) as { + data: PaymentLink; + }; + + return response.data; } export async function deletePaymentLink(props: { diff --git a/apps/dashboard/src/@/hooks/useEffectOnce.ts b/apps/dashboard/src/@/hooks/useEffectOnce.ts new file mode 100644 index 00000000000..6e7d37629b4 --- /dev/null +++ b/apps/dashboard/src/@/hooks/useEffectOnce.ts @@ -0,0 +1,14 @@ +/** biome-ignore-all lint/correctness/useExhaustiveDependencies: we only want to run effect once */ +"use client"; +import { useEffect, useRef } from "react"; + +export function useEffectOnce(effect: () => void) { + const hasCalledEffect = useRef(false); + + // eslint-disable-next-line no-restricted-syntax + useEffect(() => { + if (hasCalledEffect.current) return; + hasCalledEffect.current = true; + effect(); + }, []); +} diff --git a/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/_components/asset-page-view.tsx b/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/_components/asset-page-view.tsx new file mode 100644 index 00000000000..f08dfcf594e --- /dev/null +++ b/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/_components/asset-page-view.tsx @@ -0,0 +1,17 @@ +"use client"; +import { reportAssetPageview } from "@/analytics/report"; +import { useEffectOnce } from "@/hooks/useEffectOnce"; + +export function AssetPageView(props: { + assetType: "nft" | "coin"; + chainId: number; +}) { + useEffectOnce(() => { + reportAssetPageview({ + assetType: props.assetType, + chainId: props.chainId, + }); + }); + + return null; +} diff --git a/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/erc20/erc20.tsx b/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/erc20/erc20.tsx index e7534a4dd56..2fbd519762e 100644 --- a/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/erc20/erc20.tsx +++ b/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/erc20/erc20.tsx @@ -4,6 +4,7 @@ import { getContractMetadata } from "thirdweb/extensions/common"; import { decimals, getActiveClaimCondition } from "thirdweb/extensions/erc20"; import { GridPattern } from "@/components/ui/background-patterns"; import { resolveFunctionSelectors } from "@/lib/selectors"; +import { AssetPageView } from "../_components/asset-page-view"; import { getContractCreator } from "../_components/getContractCreator"; import { PageHeader } from "../_components/PageHeader"; import { getTokenPriceData } from "./_apis/token-price-data"; @@ -85,6 +86,7 @@ export async function ERC20PublicPage(props: { return (
+
diff --git a/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/nft-page-layout.tsx b/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/nft-page-layout.tsx index 19270782bb8..17fd4760a33 100644 --- a/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/nft-page-layout.tsx +++ b/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/nft-page-layout.tsx @@ -1,5 +1,6 @@ import type { ThirdwebContract } from "thirdweb"; import type { ChainMetadata } from "thirdweb/chains"; +import { AssetPageView } from "../_components/asset-page-view"; import { PageHeader } from "../_components/PageHeader"; import { ContractHeaderUI } from "../erc20/_components/ContractHeader"; @@ -16,6 +17,7 @@ export function NFTPublicPageLayout(props: { }) { return (
+
diff --git a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/components/RecentPaymentsSection.client.tsx b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/components/RecentPaymentsSection.client.tsx index eb2ac17bb6f..42604d1b1b3 100644 --- a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/components/RecentPaymentsSection.client.tsx +++ b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/components/RecentPaymentsSection.client.tsx @@ -99,7 +99,6 @@ export function RecentPaymentsSection(props: { > Create Payment Link diff --git a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/links/components/CreatePaymentLinkButton.client.tsx b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/links/components/CreatePaymentLinkButton.client.tsx index 76db422e868..490e4640c91 100644 --- a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/links/components/CreatePaymentLinkButton.client.tsx +++ b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/links/components/CreatePaymentLinkButton.client.tsx @@ -10,6 +10,7 @@ import { toast } from "sonner"; import { Bridge, toUnits } from "thirdweb"; import { checksumAddress } from "thirdweb/utils"; import z from "zod"; +import { reportPaymentLinkCreated } from "@/analytics/report"; import { createPaymentLink } from "@/api/universal-bridge/developer"; import { getUniversalBridgeTokens } from "@/api/universal-bridge/tokens"; import { SingleNetworkSelector } from "@/components/blocks/NetworkSelectors"; @@ -91,7 +92,7 @@ export function CreatePaymentLinkButton( throw new Error("Invalid recipient address."); } - await createPaymentLink({ + const result = await createPaymentLink({ clientId: props.clientId, teamId: props.teamId, intent: { @@ -102,7 +103,8 @@ export function CreatePaymentLinkButton( }, title: values.title, }); - return null; + + return result; }, onSuccess: () => { toast.success("Payment link created successfully."); @@ -153,7 +155,12 @@ export function CreatePaymentLinkButton( className="flex flex-col gap-6" onSubmit={form.handleSubmit((values) => createMutation.mutateAsync(values, { - onSuccess: () => { + onSuccess: (result) => { + reportPaymentLinkCreated({ + linkId: result.id, + clientId: props.clientId, + }); + setOpen(false); form.reset(); form.clearErrors(); diff --git a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/tokens/cards.tsx b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/tokens/cards.tsx index a9d221899fd..c878006dc85 100644 --- a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/tokens/cards.tsx +++ b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/tokens/cards.tsx @@ -5,7 +5,6 @@ import Link from "next/link"; import { useState } from "react"; import type { ThirdwebClient } from "thirdweb"; import { - reportAssetCreationStarted, reportAssetImportStarted, reportAssetImportSuccessful, } from "@/analytics/report"; @@ -43,11 +42,6 @@ export function Cards(props: { description="Launch your own ERC-20 coin" href={`/team/${props.teamSlug}/${props.projectSlug}/tokens/create/token`} icon={CoinsIcon} - onClick={() => { - reportAssetCreationStarted({ - assetType: "coin", - }); - }} title="Create Coin" /> @@ -55,11 +49,6 @@ export function Cards(props: { description="Launch your own NFT collection" href={`/team/${props.teamSlug}/${props.projectSlug}/tokens/create/nft`} icon={ImagesIcon} - onClick={() => { - reportAssetCreationStarted({ - assetType: "nft", - }); - }} title="Create NFT Collection" /> diff --git a/apps/dashboard/src/app/login/onboarding/team-onboarding/team-onboarding.tsx b/apps/dashboard/src/app/login/onboarding/team-onboarding/team-onboarding.tsx index ceaf42e8a4c..287ab4a2009 100644 --- a/apps/dashboard/src/app/login/onboarding/team-onboarding/team-onboarding.tsx +++ b/apps/dashboard/src/app/login/onboarding/team-onboarding/team-onboarding.tsx @@ -1,5 +1,4 @@ "use client"; -import { useEffect } from "react"; import { toast } from "sonner"; import type { ThirdwebClient } from "thirdweb"; import { upload } from "thirdweb/storage"; @@ -10,6 +9,7 @@ import { reportOnboardingStarted, } from "@/analytics/report"; import type { Team } from "@/api/team"; +import { useEffectOnce } from "@/hooks/useEffectOnce"; import { useDashboardRouter } from "@/lib/DashboardRouter"; import { updateTeam } from "../../../(app)/team/[team_slug]/(team)/~/settings/general/updateTeam"; import { InviteTeamMembersUI } from "./InviteTeamMembers"; @@ -23,9 +23,9 @@ export function TeamInfoForm(props: { const router = useDashboardRouter(); // eslint-disable-next-line no-restricted-syntax - useEffect(() => { + useEffectOnce(() => { reportOnboardingStarted(); - }, []); + }); return ( { + if (paymentLinkId && clientId) { + reportPaymentLinkBuyFailed({ + linkId: paymentLinkId, + clientId: clientId, + errorMessage: error.message, + }); + } + }} paymentLinkId={paymentLinkId} purchaseData={purchaseData} seller={checksumAddress(recipientAddress)}