Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 38 additions & 22 deletions apps/dashboard/src/@/analytics/report.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
"use client";
import posthog from "posthog-js";

import type { Team } from "@/api/team";
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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);
}
6 changes: 5 additions & 1 deletion apps/dashboard/src/@/api/universal-bridge/developer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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: {
Expand Down
14 changes: 14 additions & 0 deletions apps/dashboard/src/@/hooks/useEffectOnce.ts
Original file line number Diff line number Diff line change
@@ -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();
}, []);
}
Original file line number Diff line number Diff line change
@@ -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;
}
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -85,6 +86,7 @@ export async function ERC20PublicPage(props: {

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

<div className="border-b">
Expand Down
Original file line number Diff line number Diff line change
@@ -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";

Expand All @@ -16,6 +17,7 @@ export function NFTPublicPageLayout(props: {
}) {
return (
<div className="flex grow flex-col">
<AssetPageView assetType="nft" chainId={props.chainMetadata.chainId} />
<PageHeader containerClassName="max-w-8xl" />
<div className="border-b">
<div className="container max-w-8xl">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,6 @@ export function RecentPaymentsSection(props: {
>
<Link
href={`/team/${props.teamSlug}/${props.projectSlug}/payments/links`}
target="_blank"
>
Create Payment Link
<ArrowRightIcon className="size-4" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -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: {
Expand All @@ -102,7 +103,8 @@ export function CreatePaymentLinkButton(
},
title: values.title,
});
return null;

return result;
},
onSuccess: () => {
toast.success("Payment link created successfully.");
Expand Down Expand Up @@ -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();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -43,23 +42,13 @@ 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"
/>

<CardLink
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"
/>

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
"use client";
import { useEffect } from "react";
import { toast } from "sonner";
import type { ThirdwebClient } from "thirdweb";
import { upload } from "thirdweb/storage";
Expand All @@ -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";
Expand All @@ -23,9 +23,9 @@ export function TeamInfoForm(props: {
const router = useDashboardRouter();

// eslint-disable-next-line no-restricted-syntax
useEffect(() => {
useEffectOnce(() => {
reportOnboardingStarted();
}, []);
});

return (
<TeamInfoFormUI
Expand Down
6 changes: 0 additions & 6 deletions apps/dashboard/src/app/pay/[id]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import type { Metadata } from "next";
import { defineChain, getContract } from "thirdweb";
import { getCurrencyMetadata } from "thirdweb/extensions/erc20";
import { checksumAddress } from "thirdweb/utils";
import { reportPaymentLinkVisited } from "@/analytics/report";
import { getPaymentLink } from "@/api/universal-bridge/links";
import { getClientThirdwebClient } from "@/constants/thirdweb-client.client";
import { PayPageWidget } from "../components/client/PayPageWidget.client";
Expand Down Expand Up @@ -33,11 +32,6 @@ export default async function PayPage({
paymentId: id,
});

reportPaymentLinkVisited({
linkId: id,
clientId: paymentLink.clientId,
});

const tokenContract = getContract({
address: paymentLink.destinationToken.address, // for this RPC call, use the dashboard client
// eslint-disable-next-line no-restricted-syntax
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@ import { useEffect } from "react";
import { createThirdwebClient, NATIVE_TOKEN_ADDRESS, toTokens } from "thirdweb";
import { AutoConnect, CheckoutWidget } from "thirdweb/react";
import { checksumAddress } from "thirdweb/utils";
import { reportPaymentLinkCompleted } from "@/analytics/report";
import {
reportPaymentLinkBuyFailed,
reportPaymentLinkBuySuccessful,
} from "@/analytics/report";
import { useV5DashboardChain } from "@/hooks/chains/v5-adapter";

export function PayPageWidget({
Expand Down Expand Up @@ -62,13 +65,22 @@ export function PayPageWidget({
if (!redirectUri) return;
const url = new URL(redirectUri);
if (paymentLinkId && clientId) {
reportPaymentLinkCompleted({
reportPaymentLinkBuySuccessful({
linkId: paymentLinkId,
clientId: clientId,
});
}
return window.open(url.toString());
}}
onError={(error) => {
if (paymentLinkId && clientId) {
reportPaymentLinkBuyFailed({
linkId: paymentLinkId,
clientId: clientId,
errorMessage: error.message,
});
}
}}
paymentLinkId={paymentLinkId}
purchaseData={purchaseData}
seller={checksumAddress(recipientAddress)}
Expand Down
Loading