Skip to content

Commit 28ab053

Browse files
committed
Dashboard: Various Asset page updates
1 parent 3b1e16c commit 28ab053

File tree

6 files changed

+233
-49
lines changed

6 files changed

+233
-49
lines changed

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

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -107,9 +107,16 @@ function RecentTransfersUI(props: {
107107
</div>
108108
</TableCell>
109109
<TableCell className="text-sm">
110-
{formatDistanceToNow(new Date(transfer.block_timestamp), {
111-
addSuffix: true,
112-
})}
110+
{formatDistanceToNow(
111+
new Date(
112+
transfer.block_timestamp.endsWith("Z")
113+
? transfer.block_timestamp
114+
: `${transfer.block_timestamp}Z`,
115+
),
116+
{
117+
addSuffix: true,
118+
},
119+
)}
113120
</TableCell>
114121
<TableCell>
115122
<Button

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

Lines changed: 131 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
11
"use client";
22
import { Spinner } from "@/components/ui/Spinner/Spinner";
3+
import { Button } from "@/components/ui/button";
4+
import { DecimalInput } from "@/components/ui/decimal-input";
35
import { Label } from "@/components/ui/label";
46
import { SkeletonContainer } from "@/components/ui/skeleton";
57
import { cn } from "@/lib/utils";
68
import { useMutation, useQuery } from "@tanstack/react-query";
79
import { TransactionButton } from "components/buttons/TransactionButton";
8-
import { CheckIcon, CircleIcon, XIcon } from "lucide-react";
10+
import { useTrack } from "hooks/analytics/useTrack";
11+
import { CheckIcon, CircleIcon, ExternalLinkIcon, XIcon } from "lucide-react";
912
import { useTheme } from "next-themes";
13+
import Link from "next/link";
1014
import { useState } from "react";
1115
import { toast } from "sonner";
1216
import {
@@ -22,10 +26,13 @@ import {
2226
type getActiveClaimCondition,
2327
getApprovalForTransaction,
2428
} from "thirdweb/extensions/erc20";
25-
import { useActiveAccount, useSendTransaction } from "thirdweb/react";
29+
import {
30+
useActiveAccount,
31+
useActiveWallet,
32+
useSendTransaction,
33+
} from "thirdweb/react";
2634
import { getClaimParams } from "thirdweb/utils";
2735
import { tryCatch } from "utils/try-catch";
28-
import { DecimalInput } from "../../../../../../../../../../@/components/ui/decimal-input";
2936
import { getSDKTheme } from "../../../../../../../../components/sdk-component-theme";
3037
import { PublicPageConnectButton } from "../../../_components/PublicPageConnectButton";
3138
import { getCurrencyMeta } from "../../_utils/getCurrencyMeta";
@@ -48,12 +55,47 @@ export function ClaimTokenCardUI(props: {
4855
}) {
4956
const [quantity, setQuantity] = useState("1");
5057
const account = useActiveAccount();
58+
const activeWallet = useActiveWallet();
5159
const { theme } = useTheme();
60+
const trackEvent = useTrack();
5261
const sendClaimTx = useSendTransaction({
5362
payModal: {
5463
theme: getSDKTheme(theme === "light" ? "light" : "dark"),
5564
},
5665
});
66+
const [successScreen, setSuccessScreen] = useState<
67+
| undefined
68+
| {
69+
txHash: string;
70+
}
71+
>(undefined);
72+
73+
function trackAssetBuy(
74+
params:
75+
| {
76+
type: "attempt" | "success";
77+
}
78+
| {
79+
type: "error";
80+
errorMessage: string;
81+
},
82+
) {
83+
trackEvent({
84+
category: "asset",
85+
action: "buy",
86+
label: params.type,
87+
contractType: "DropERC20",
88+
accountAddress: account?.address,
89+
walletId: activeWallet?.id,
90+
chainId: props.contract.chain.id,
91+
...(params.type === "error"
92+
? {
93+
errorMessage: params.errorMessage,
94+
}
95+
: {}),
96+
});
97+
}
98+
5799
const [stepsUI, setStepsUI] = useState<
58100
| undefined
59101
| {
@@ -69,6 +111,10 @@ export function ClaimTokenCardUI(props: {
69111
return;
70112
}
71113

114+
trackAssetBuy({
115+
type: "attempt",
116+
});
117+
72118
setStepsUI(undefined);
73119

74120
const transaction = claimTo({
@@ -101,6 +147,12 @@ export function ClaimTokenCardUI(props: {
101147
approve: "error",
102148
claim: "idle",
103149
});
150+
151+
trackAssetBuy({
152+
type: "error",
153+
errorMessage: approveTxResult.error.message,
154+
});
155+
104156
console.error(approveTxResult.error);
105157
toast.error("Failed to approve spending", {
106158
description: approveTxResult.error.message,
@@ -116,7 +168,7 @@ export function ClaimTokenCardUI(props: {
116168

117169
async function sendAndConfirm() {
118170
const result = await sendClaimTx.mutateAsync(transaction);
119-
await waitForReceipt(result);
171+
return await waitForReceipt(result);
120172
}
121173

122174
setStepsUI({
@@ -130,8 +182,14 @@ export function ClaimTokenCardUI(props: {
130182
approve: approveTx ? "success" : undefined,
131183
claim: "error",
132184
});
185+
186+
trackAssetBuy({
187+
type: "error",
188+
errorMessage: claimTxResult.error.message,
189+
});
190+
133191
console.error(claimTxResult.error);
134-
toast.error("Failed to claim tokens", {
192+
toast.error("Failed to buy tokens", {
135193
description: claimTxResult.error.message,
136194
});
137195
return;
@@ -142,7 +200,13 @@ export function ClaimTokenCardUI(props: {
142200
claim: "success",
143201
});
144202

145-
toast.success("Tokens claimed successfully");
203+
trackAssetBuy({
204+
type: "success",
205+
});
206+
207+
setSuccessScreen({
208+
txHash: claimTxResult.data.transactionHash,
209+
});
146210
},
147211
});
148212

@@ -192,6 +256,51 @@ export function ClaimTokenCardUI(props: {
192256

193257
const claimParamsData = claimParamsQuery.data;
194258

259+
if (successScreen) {
260+
const explorerUrl =
261+
props.chainMetadata.explorers?.[0]?.url ??
262+
`https://thirdweb.com/${props.chainMetadata.slug}`;
263+
264+
return (
265+
<div className="rounded-xl border bg-card p-6">
266+
{/* icon */}
267+
<div className="flex justify-center py-8">
268+
<div className="rounded-full border bg-background p-3">
269+
<CheckIcon className="size-8" />
270+
</div>
271+
</div>
272+
273+
<div className="mb-12">
274+
<h2 className="mb-1 text-center font-bold text-xl">
275+
Purchase Successful
276+
</h2>
277+
<p className="text-center text-muted-foreground text-sm">
278+
You have successfully purchased {quantity}{" "}
279+
{props.symbol || "tokens"}
280+
</p>
281+
</div>
282+
283+
<Button className="w-full bg-muted/50" variant="outline" asChild>
284+
<Link
285+
href={`${explorerUrl}/tx/${successScreen.txHash}`}
286+
target="_blank"
287+
className="gap-1.5"
288+
>
289+
View Transaction{" "}
290+
<ExternalLinkIcon className="size-3.5 text-muted-foreground" />
291+
</Link>
292+
</Button>
293+
294+
<Button
295+
onClick={() => setSuccessScreen(undefined)}
296+
className="mt-3 w-full"
297+
>
298+
Buy More
299+
</Button>
300+
</div>
301+
);
302+
}
303+
195304
return (
196305
<div className="rounded-xl border bg-card ">
197306
<div className="border-b px-4 py-5 lg:px-5">
@@ -225,10 +334,12 @@ export function ClaimTokenCardUI(props: {
225334
skeletonData={`0.00 ${props.claimConditionCurrency.symbol}`}
226335
loadedData={
227336
claimParamsData
228-
? `${toTokens(
229-
claimParamsData.pricePerTokenWei,
230-
claimParamsData.decimals,
231-
)} ${claimParamsData.symbol}`
337+
? claimParamsData.pricePerTokenWei === 0n
338+
? "FREE"
339+
: `${toTokens(
340+
claimParamsData.pricePerTokenWei,
341+
claimParamsData.decimals,
342+
)} ${claimParamsData.symbol}`
232343
: undefined
233344
}
234345
render={(v) => {
@@ -251,14 +362,16 @@ export function ClaimTokenCardUI(props: {
251362
skeletonData={"0.00 ETH"}
252363
loadedData={
253364
claimParamsData
254-
? `${
255-
Number(
256-
toTokens(
257-
claimParamsData.pricePerTokenWei,
258-
claimParamsData.decimals,
259-
),
260-
) * Number(quantity)
261-
} ${claimParamsData.symbol}`
365+
? claimParamsData.pricePerTokenWei === 0n
366+
? "FREE"
367+
: `${
368+
Number(
369+
toTokens(
370+
claimParamsData.pricePerTokenWei,
371+
claimParamsData.decimals,
372+
),
373+
) * Number(quantity)
374+
} ${claimParamsData.symbol}`
262375
: undefined
263376
}
264377
render={(v) => {

apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/create/create-token-page-impl.tsx

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,10 @@ export function CreateTokenAssetPage(props: {
6262
);
6363

6464
trackEvent(
65-
getTokenDeploymentTrackingData("attempt", Number(formValues.chain)),
65+
getTokenDeploymentTrackingData({
66+
type: "attempt",
67+
chainId: Number(formValues.chain),
68+
}),
6669
);
6770

6871
const socialUrls = formValues.socialUrls.reduce(
@@ -109,7 +112,10 @@ export function CreateTokenAssetPage(props: {
109112
);
110113

111114
trackEvent(
112-
getTokenDeploymentTrackingData("success", Number(formValues.chain)),
115+
getTokenDeploymentTrackingData({
116+
type: "success",
117+
chainId: Number(formValues.chain),
118+
}),
113119
);
114120

115121
// add contract to project in background
@@ -133,11 +139,16 @@ export function CreateTokenAssetPage(props: {
133139
action: "deploy",
134140
chainId: Number(formValues.chain),
135141
status: "error",
142+
errorMessage: e instanceof Error ? e.message : "Unknown error",
136143
}),
137144
);
138145

139146
trackEvent(
140-
getTokenDeploymentTrackingData("error", Number(formValues.chain)),
147+
getTokenDeploymentTrackingData({
148+
type: "error",
149+
chainId: Number(formValues.chain),
150+
errorMessage: e instanceof Error ? e.message : "Unknown error",
151+
}),
141152
);
142153
throw e;
143154
}
@@ -201,6 +212,7 @@ export function CreateTokenAssetPage(props: {
201212
action: "airdrop",
202213
chainId: Number(formValues.chain),
203214
status: "error",
215+
errorMessage: e instanceof Error ? e.message : "Unknown error",
204216
}),
205217
);
206218
throw e;
@@ -270,6 +282,7 @@ export function CreateTokenAssetPage(props: {
270282
action: "mint",
271283
chainId: Number(formValues.chain),
272284
status: "error",
285+
errorMessage: e instanceof Error ? e.message : "Unknown error",
273286
}),
274287
);
275288
throw e;
@@ -368,6 +381,7 @@ export function CreateTokenAssetPage(props: {
368381
action: "claim-conditions",
369382
chainId: Number(formValues.chain),
370383
status: "error",
384+
errorMessage: e instanceof Error ? e.message : "Unknown error",
371385
}),
372386
);
373387
throw e;

apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/create/launch/launch-token.tsx

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -46,18 +46,29 @@ export function LaunchTokenStatus(props: {
4646
const trackEvent = useTrack();
4747

4848
async function handleSubmitClick() {
49-
function launchTracking(type: "attempt" | "success" | "error") {
49+
function launchTracking(
50+
params:
51+
| {
52+
type: "attempt" | "success";
53+
}
54+
| {
55+
type: "error";
56+
errorMessage: string;
57+
},
58+
) {
5059
trackEvent(
5160
getLaunchTrackingData({
5261
chainId: Number(formValues.chain),
5362
airdropEnabled: formValues.airdropEnabled,
5463
saleEnabled: formValues.saleEnabled,
55-
type,
64+
...params,
5665
}),
5766
);
5867
}
5968

60-
launchTracking("attempt");
69+
launchTracking({
70+
type: "attempt",
71+
});
6172

6273
function updateStatus(index: number, newStatus: MultiStepState["status"]) {
6374
setSteps((prev) => {
@@ -84,12 +95,18 @@ export function LaunchTokenStatus(props: {
8495
// do not use await next step
8596
nextStep.execute();
8697
} else {
87-
launchTracking("success");
98+
launchTracking({
99+
type: "success",
100+
});
88101
props.onLaunchSuccess();
89102
}
90103
} catch (error) {
91104
updateStatus(index, "error");
92-
launchTracking("error");
105+
launchTracking({
106+
type: "error",
107+
errorMessage:
108+
error instanceof Error ? error.message : "Unknown error",
109+
});
93110
console.error(error);
94111
}
95112
};

0 commit comments

Comments
 (0)