Skip to content

Commit be60100

Browse files
joaquim-vergesMananTank
authored andcommitted
[Dashboard] Fix asset page link and improve token claim UX (#7193)
1 parent 51eda91 commit be60100

File tree

8 files changed

+169
-64
lines changed

8 files changed

+169
-64
lines changed

apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/overview/ContractOverviewPage.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ export const ContractOverviewPage: React.FC<ContractOverviewPageProps> = ({
5353
text: "View asset page",
5454
icon: <ExternalLinkIcon className="size-4" />,
5555
target: "_blank",
56-
link: `https://thirdweb.com/${chainSlug}/${contract.address}`,
56+
link: `/${chainSlug}/${contract.address}`,
5757
}}
5858
trackingCategory="erc20-contract"
5959
trackingLabel="view-asset-page"

apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/overview/components/Analytics.tsx

Lines changed: 69 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,22 @@ import {
77
useContractTransactionAnalytics,
88
useContractUniqueWalletAnalytics,
99
} from "data/analytics/hooks";
10+
import { differenceInCalendarDays, formatDate } from "date-fns";
1011
import { useTrack } from "hooks/analytics/useTrack";
1112
import { ArrowRightIcon } from "lucide-react";
1213
import Link from "next/link";
1314
import { useMemo, useState } from "react";
1415
import type { ProjectMeta } from "../../../../../../team/[team_slug]/[project_slug]/contract/[chainIdOrSlug]/[contractAddress]/types";
1516
import { buildContractPagePath } from "../../_utils/contract-page-path";
1617

17-
function getDayKey(date: Date) {
18-
return date.toISOString().split("T")[0];
18+
function getDateKey(date: Date, precision: "day" | "hour") {
19+
const dayKey = date.toISOString().split("T")[0];
20+
if (precision === "day") {
21+
return dayKey;
22+
}
23+
24+
const hourKey = date.getHours();
25+
return `${dayKey}-${hourKey}`;
1926
}
2027

2128
export function ContractAnalyticsOverviewCard(props: {
@@ -59,33 +66,59 @@ export function ContractAnalyticsOverviewCard(props: {
5966
const isPending =
6067
wallets.isPending || transactions.isPending || events.isPending;
6168

62-
const mergedData = useMemo(() => {
69+
const { data, precision } = useMemo(() => {
6370
if (isPending) {
64-
return undefined;
71+
return {
72+
data: undefined,
73+
precision: "day" as const,
74+
};
6575
}
6676

6777
const time = (wallets.data || transactions.data || events.data || []).map(
6878
(wallet) => wallet.time,
6979
);
7080

71-
return time.map((time) => {
72-
const wallet = wallets.data?.find(
73-
(wallet) => getDayKey(wallet.time) === getDayKey(time),
74-
);
75-
const transaction = transactions.data?.find(
76-
(transaction) => getDayKey(transaction.time) === getDayKey(time),
77-
);
78-
const event = events.data?.find((event) => {
79-
return getDayKey(event.time) === getDayKey(time);
80-
});
81+
// if the time difference between the first and last time is less than 3 days - use hour precision
82+
const firstTime = time[0];
83+
const lastTime = time[time.length - 1];
84+
const timeDiff =
85+
firstTime && lastTime
86+
? differenceInCalendarDays(lastTime, firstTime)
87+
: undefined;
8188

82-
return {
83-
time,
84-
wallets: wallet?.count || 0,
85-
transactions: transaction?.count || 0,
86-
events: event?.count || 0,
87-
};
88-
});
89+
const precision: "day" | "hour" = !timeDiff
90+
? "hour"
91+
: timeDiff < 3
92+
? "hour"
93+
: "day";
94+
95+
return {
96+
data: time.map((time) => {
97+
const wallet = wallets.data?.find(
98+
(wallet) =>
99+
getDateKey(wallet.time, precision) === getDateKey(time, precision),
100+
);
101+
const transaction = transactions.data?.find(
102+
(transaction) =>
103+
getDateKey(transaction.time, precision) ===
104+
getDateKey(time, precision),
105+
);
106+
107+
const event = events.data?.find((event) => {
108+
return (
109+
getDateKey(event.time, precision) === getDateKey(time, precision)
110+
);
111+
});
112+
113+
return {
114+
time,
115+
wallets: wallet?.count || 0,
116+
transactions: transaction?.count || 0,
117+
events: event?.count || 0,
118+
};
119+
}),
120+
precision,
121+
};
89122
}, [wallets.data, transactions.data, events.data, isPending]);
90123

91124
const analyticsPath = buildContractPagePath({
@@ -111,10 +144,11 @@ export function ContractAnalyticsOverviewCard(props: {
111144
color: "hsl(var(--chart-3))",
112145
},
113146
}}
114-
data={mergedData || []}
147+
data={data || []}
115148
isPending={isPending}
116149
showLegend
117150
chartClassName="aspect-[1.5] lg:aspect-[3]"
151+
toolTipLabelFormatter={toolTipLabelFormatterWithPrecision(precision)}
118152
customHeader={
119153
<div className="flex items-center justify-between gap-4 border-b p-6 py-4">
120154
<h2 className="font-semibold text-xl tracking-tight">Analytics</h2>
@@ -141,3 +175,16 @@ export function ContractAnalyticsOverviewCard(props: {
141175
/>
142176
);
143177
}
178+
179+
function toolTipLabelFormatterWithPrecision(precision: "day" | "hour") {
180+
return function toolTipLabelFormatter(_v: string, item: unknown) {
181+
if (Array.isArray(item)) {
182+
const time = item[0].payload.time as number;
183+
return formatDate(
184+
new Date(time),
185+
precision === "day" ? "MMM d, yyyy" : "MMM d, yyyy hh:mm a",
186+
);
187+
}
188+
return undefined;
189+
};
190+
}

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

Lines changed: 14 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -122,8 +122,8 @@ export function ClaimTokenCardUI(props: {
122122
}
123123

124124
async function sendAndConfirm() {
125-
const receipt = await sendClaimTx.mutateAsync(transaction);
126-
await waitForReceipt(receipt);
125+
const result = await sendClaimTx.mutateAsync(transaction);
126+
await waitForReceipt(result);
127127
}
128128

129129
setStepsUI({
@@ -154,12 +154,7 @@ export function ClaimTokenCardUI(props: {
154154
});
155155

156156
const claimParamsQuery = useQuery({
157-
queryKey: [
158-
"claim-params",
159-
props.contract.address,
160-
quantity,
161-
account?.address,
162-
],
157+
queryKey: ["claim-params", props.contract.address, account?.address],
163158
queryFn: async () => {
164159
const defaultPricing = {
165160
pricePerTokenWei: props.claimCondition.pricePerToken,
@@ -180,7 +175,7 @@ export function ClaimTokenCardUI(props: {
180175
const claimParams = await getClaimParams({
181176
contract: props.contract,
182177
to: account.address,
183-
quantity: BigInt(quantity),
178+
quantity: 1n, // not relevant
184179
type: "erc20",
185180
tokenDecimals: props.decimals,
186181
from: account.address,
@@ -204,12 +199,6 @@ export function ClaimTokenCardUI(props: {
204199

205200
const claimParamsData = claimParamsQuery.data;
206201

207-
const totalPriceInTokens = claimParamsData
208-
? Number(
209-
toTokens(claimParamsData.pricePerTokenWei, claimParamsData.decimals),
210-
) * quantity
211-
: undefined;
212-
213202
return (
214203
<div className="rounded-xl border bg-card ">
215204
<div className="border-b px-4 py-5 lg:px-5">
@@ -269,8 +258,15 @@ export function ClaimTokenCardUI(props: {
269258
<SkeletonContainer
270259
skeletonData={"0.00 ETH"}
271260
loadedData={
272-
totalPriceInTokens && claimParamsData
273-
? `${totalPriceInTokens} ${claimParamsData.symbol}`
261+
claimParamsData
262+
? `${
263+
Number(
264+
toTokens(
265+
claimParamsData.pricePerTokenWei,
266+
claimParamsData.decimals,
267+
),
268+
) * quantity
269+
} ${claimParamsData.symbol}`
274270
: undefined
275271
}
276272
render={(v) => {
@@ -286,6 +282,7 @@ export function ClaimTokenCardUI(props: {
286282
{account ? (
287283
<TransactionButton
288284
transactionCount={undefined}
285+
checkBalance={false}
289286
isLoggedIn={true}
290287
isPending={approveAndClaim.isPending}
291288
onClick={async () => {

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

Lines changed: 70 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,17 @@ import {
66
useContractTransactionAnalytics,
77
useContractUniqueWalletAnalytics,
88
} from "data/analytics/hooks";
9+
import { differenceInCalendarDays, formatDate } from "date-fns";
910
import { useMemo, useState } from "react";
1011

11-
function getDayKey(date: Date) {
12-
return date.toISOString().split("T")[0];
12+
function getDateKey(date: Date, precision: "day" | "hour") {
13+
const dayKey = date.toISOString().split("T")[0];
14+
if (precision === "day") {
15+
return dayKey;
16+
}
17+
18+
const hourKey = date.getHours();
19+
return `${dayKey}-${hourKey}`;
1320
}
1421

1522
export function ContractAnalyticsOverview(props: {
@@ -50,33 +57,59 @@ export function ContractAnalyticsOverview(props: {
5057
const isPending =
5158
wallets.isPending || transactions.isPending || events.isPending;
5259

53-
const mergedData = useMemo(() => {
60+
const { data, precision } = useMemo(() => {
5461
if (isPending) {
55-
return undefined;
62+
return {
63+
data: undefined,
64+
precision: "day" as const,
65+
};
5666
}
5767

5868
const time = (wallets.data || transactions.data || events.data || []).map(
5969
(wallet) => wallet.time,
6070
);
6171

62-
return time.map((time) => {
63-
const wallet = wallets.data?.find(
64-
(wallet) => getDayKey(wallet.time) === getDayKey(time),
65-
);
66-
const transaction = transactions.data?.find(
67-
(transaction) => getDayKey(transaction.time) === getDayKey(time),
68-
);
69-
const event = events.data?.find((event) => {
70-
return getDayKey(event.time) === getDayKey(time);
71-
});
72+
// if the time difference between the first and last time is less than 3 days - use hour precision
73+
const firstTime = time[0];
74+
const lastTime = time[time.length - 1];
75+
const timeDiff =
76+
firstTime && lastTime
77+
? differenceInCalendarDays(lastTime, firstTime)
78+
: undefined;
7279

73-
return {
74-
time,
75-
wallets: wallet?.count || 0,
76-
transactions: transaction?.count || 0,
77-
events: event?.count || 0,
78-
};
79-
});
80+
const precision: "day" | "hour" = !timeDiff
81+
? "hour"
82+
: timeDiff < 3
83+
? "hour"
84+
: "day";
85+
86+
return {
87+
data: time.map((time) => {
88+
const wallet = wallets.data?.find(
89+
(wallet) =>
90+
getDateKey(wallet.time, precision) === getDateKey(time, precision),
91+
);
92+
const transaction = transactions.data?.find(
93+
(transaction) =>
94+
getDateKey(transaction.time, precision) ===
95+
getDateKey(time, precision),
96+
);
97+
98+
const event = events.data?.find((event) => {
99+
return (
100+
getDateKey(event.time, precision) === getDateKey(time, precision)
101+
);
102+
});
103+
104+
return {
105+
time,
106+
wallets: wallet?.count || 0,
107+
transactions: transaction?.count || 0,
108+
events: event?.count || 0,
109+
};
110+
}),
111+
precision,
112+
};
80113
}, [wallets.data, transactions.data, events.data, isPending]);
81114

82115
return (
@@ -105,11 +138,26 @@ export function ContractAnalyticsOverview(props: {
105138
color: "hsl(var(--chart-3))",
106139
},
107140
}}
108-
data={mergedData || []}
141+
data={data || []}
109142
isPending={isPending}
110143
showLegend
144+
hideLabel={false}
111145
chartClassName="aspect-[1.5] lg:aspect-[3]"
146+
toolTipLabelFormatter={toolTipLabelFormatterWithPrecision(precision)}
112147
/>
113148
</div>
114149
);
115150
}
151+
152+
function toolTipLabelFormatterWithPrecision(precision: "day" | "hour") {
153+
return function toolTipLabelFormatter(_v: string, item: unknown) {
154+
if (Array.isArray(item)) {
155+
const time = item[0].payload.time as number;
156+
return formatDate(
157+
new Date(time),
158+
precision === "day" ? "MMM d, yyyy" : "MMM d, yyyy hh:mm a",
159+
);
160+
}
161+
return undefined;
162+
};
163+
}

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,5 +51,6 @@ export function useTokenPriceData(params: {
5151
}
5252
: { type: "no-data" as const };
5353
},
54+
refetchInterval: 5000,
5455
});
5556
}

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,5 +49,6 @@ export function useTokenTransfers(params: {
4949
const data = json.data as TokenTransfersData[];
5050
return data;
5151
},
52+
refetchInterval: 5000,
5253
});
5354
}

apps/dashboard/src/components/buttons/MismatchButton.tsx

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,13 +80,20 @@ type MistmatchButtonProps = React.ComponentProps<typeof Button> & {
8080
txChainId: number;
8181
isLoggedIn: boolean;
8282
isPending: boolean;
83+
checkBalance?: boolean;
8384
};
8485

8586
export const MismatchButton = forwardRef<
8687
HTMLButtonElement,
8788
MistmatchButtonProps
8889
>((props, ref) => {
89-
const { txChainId, isLoggedIn, isPending, ...buttonProps } = props;
90+
const {
91+
txChainId,
92+
isLoggedIn,
93+
isPending,
94+
checkBalance = true,
95+
...buttonProps
96+
} = props;
9097
const account = useActiveAccount();
9198
const wallet = useActiveWallet();
9299
const activeWalletChain = useActiveWalletChain();
@@ -150,7 +157,8 @@ export const MismatchButton = forwardRef<
150157
}
151158

152159
const isBalanceRequired =
153-
wallet.id === "smart" ? false : !GAS_FREE_CHAINS.includes(txChainId);
160+
checkBalance &&
161+
(wallet.id === "smart" ? false : !GAS_FREE_CHAINS.includes(txChainId));
154162

155163
const notEnoughBalance =
156164
(txChainBalance.data?.value || 0n) === 0n && isBalanceRequired;

0 commit comments

Comments
 (0)