Skip to content

Commit 3a7f307

Browse files
joaquim-vergesMananTank
authored andcommitted
[Dashboard] Fix asset page link and improve token claim UX (#7193)
1 parent 568d484 commit 3a7f307

File tree

23 files changed

+655
-240
lines changed

23 files changed

+655
-240
lines changed
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { Input } from "./input";
2+
export function DecimalInput(props: {
3+
value: string;
4+
onChange: (value: string) => void;
5+
maxValue?: number;
6+
id?: string;
7+
className?: string;
8+
}) {
9+
return (
10+
<Input
11+
id={props.id}
12+
type="text"
13+
value={props.value}
14+
className={props.className}
15+
inputMode="decimal"
16+
onChange={(e) => {
17+
const number = Number(e.target.value);
18+
// ignore if string becomes invalid number
19+
if (Number.isNaN(number)) {
20+
return;
21+
}
22+
if (props.maxValue && number > props.maxValue) {
23+
return;
24+
}
25+
// replace leading multiple zeros with single zero
26+
let cleanedValue = e.target.value.replace(/^0+/, "0");
27+
// replace leading zero before decimal point
28+
if (!cleanedValue.includes(".")) {
29+
cleanedValue = cleanedValue.replace(/^0+/, "");
30+
}
31+
props.onChange(cleanedValue || "0");
32+
}}
33+
/>
34+
);
35+
}

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: 118 additions & 51 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: {
@@ -34,60 +41,13 @@ export function ContractAnalyticsOverviewCard(props: {
3441
})(),
3542
);
3643
const [endDate] = useState(new Date());
37-
38-
const wallets = useContractUniqueWalletAnalytics({
39-
chainId: props.chainId,
40-
contractAddress: props.contractAddress,
41-
startDate,
42-
endDate,
43-
});
44-
45-
const transactions = useContractTransactionAnalytics({
44+
const { data, precision, isPending } = useContractAnalyticsOverview({
4645
chainId: props.chainId,
4746
contractAddress: props.contractAddress,
4847
startDate,
4948
endDate,
5049
});
5150

52-
const events = useContractEventAnalytics({
53-
chainId: props.chainId,
54-
contractAddress: props.contractAddress,
55-
startDate,
56-
endDate,
57-
});
58-
59-
const isPending =
60-
wallets.isPending || transactions.isPending || events.isPending;
61-
62-
const mergedData = useMemo(() => {
63-
if (isPending) {
64-
return undefined;
65-
}
66-
67-
const time = (wallets.data || transactions.data || events.data || []).map(
68-
(wallet) => wallet.time,
69-
);
70-
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-
82-
return {
83-
time,
84-
wallets: wallet?.count || 0,
85-
transactions: transaction?.count || 0,
86-
events: event?.count || 0,
87-
};
88-
});
89-
}, [wallets.data, transactions.data, events.data, isPending]);
90-
9151
const analyticsPath = buildContractPagePath({
9252
projectMeta: props.projectMeta,
9353
chainIdOrSlug: props.chainSlug,
@@ -111,10 +71,11 @@ export function ContractAnalyticsOverviewCard(props: {
11171
color: "hsl(var(--chart-3))",
11272
},
11373
}}
114-
data={mergedData || []}
74+
data={data || []}
11575
isPending={isPending}
11676
showLegend
11777
chartClassName="aspect-[1.5] lg:aspect-[3]"
78+
toolTipLabelFormatter={toolTipLabelFormatterWithPrecision(precision)}
11879
customHeader={
11980
<div className="flex items-center justify-between gap-4 border-b p-6 py-4">
12081
<h2 className="font-semibold text-xl tracking-tight">Analytics</h2>
@@ -141,3 +102,109 @@ export function ContractAnalyticsOverviewCard(props: {
141102
/>
142103
);
143104
}
105+
106+
export function useContractAnalyticsOverview(props: {
107+
chainId: number;
108+
contractAddress: string;
109+
startDate: Date;
110+
endDate: Date;
111+
}) {
112+
const { chainId, contractAddress, startDate, endDate } = props;
113+
const wallets = useContractUniqueWalletAnalytics({
114+
chainId: chainId,
115+
contractAddress: contractAddress,
116+
startDate,
117+
endDate,
118+
});
119+
120+
const transactions = useContractTransactionAnalytics({
121+
chainId: chainId,
122+
contractAddress: contractAddress,
123+
startDate,
124+
endDate,
125+
});
126+
127+
const events = useContractEventAnalytics({
128+
chainId: chainId,
129+
contractAddress: contractAddress,
130+
startDate,
131+
endDate,
132+
});
133+
134+
const isPending =
135+
wallets.isPending || transactions.isPending || events.isPending;
136+
137+
const { data, precision } = useMemo(() => {
138+
if (isPending) {
139+
return {
140+
data: undefined,
141+
precision: "day" as const,
142+
};
143+
}
144+
145+
const time = (wallets.data || transactions.data || events.data || []).map(
146+
(wallet) => wallet.time,
147+
);
148+
149+
// if the time difference between the first and last time is less than 3 days - use hour precision
150+
const firstTime = time[0];
151+
const lastTime = time[time.length - 1];
152+
const timeDiff =
153+
firstTime && lastTime
154+
? differenceInCalendarDays(lastTime, firstTime)
155+
: undefined;
156+
157+
const precision: "day" | "hour" = !timeDiff
158+
? "hour"
159+
: timeDiff < 3
160+
? "hour"
161+
: "day";
162+
163+
return {
164+
data: time.map((time) => {
165+
const wallet = wallets.data?.find(
166+
(wallet) =>
167+
getDateKey(wallet.time, precision) === getDateKey(time, precision),
168+
);
169+
const transaction = transactions.data?.find(
170+
(transaction) =>
171+
getDateKey(transaction.time, precision) ===
172+
getDateKey(time, precision),
173+
);
174+
175+
const event = events.data?.find((event) => {
176+
return (
177+
getDateKey(event.time, precision) === getDateKey(time, precision)
178+
);
179+
});
180+
181+
return {
182+
time,
183+
wallets: wallet?.count || 0,
184+
transactions: transaction?.count || 0,
185+
events: event?.count || 0,
186+
};
187+
}),
188+
precision,
189+
};
190+
}, [wallets.data, transactions.data, events.data, isPending]);
191+
192+
return {
193+
data,
194+
precision,
195+
isPending,
196+
};
197+
}
198+
199+
export function toolTipLabelFormatterWithPrecision(precision: "day" | "hour") {
200+
return function toolTipLabelFormatter(_v: string, item: unknown) {
201+
if (Array.isArray(item)) {
202+
const time = item[0].payload.time as number;
203+
return formatDate(
204+
new Date(time),
205+
precision === "day" ? "MMM d, yyyy" : "MMM d, yyyy hh:mm a",
206+
);
207+
}
208+
return undefined;
209+
};
210+
}

0 commit comments

Comments
 (0)