Skip to content

Commit a5cd664

Browse files
committed
[TOOL-4687] Dashboard: Show Special Price UI in claim tokens card (#7284)
<!-- ## title your PR with this format: "[SDK/Dashboard/Portal] Feature/Fix: Concise title for the changes" If you did not copy the branch name from Linear, paste the issue tag here (format is TEAM-0000): ## Notes for the reviewer Anything important to call out? Be sure to also clarify these in your comments. ## How to test Unit tests, playground, etc. --> <!-- start pr-codex --> --- ## PR-Codex overview This PR focuses on improving the `RecentTransfers` and `ClaimTokenCardUI` components in a React application by refining state management, simplifying logic, and enhancing UI elements. ### Detailed summary - Removed `hasFetchedOnce` state in `RecentTransfers`. - Changed condition for disabling the button in `RecentTransfersUI`. - Simplified logic for claim parameters in `ClaimTokenCardUI`. - Introduced `TokenPrice` component to handle price display. - Added tooltip for special pricing indication. - Adjusted UI elements for better clarity and organization. > ✨ Ask PR-Codex anything about this PR by commenting with `/codex {your question}` <!-- end pr-codex -->
1 parent 05f65ec commit a5cd664

File tree

2 files changed

+72
-39
lines changed

2 files changed

+72
-39
lines changed

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

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import {
1818
ChevronRightIcon,
1919
ExternalLinkIcon,
2020
} from "lucide-react";
21-
import { useEffect, useState } from "react";
21+
import { useState } from "react";
2222
import { type ThirdwebClient, type ThirdwebContract, toTokens } from "thirdweb";
2323
import type { ChainMetadata } from "thirdweb/chains";
2424
import {
@@ -171,7 +171,7 @@ function RecentTransfersUI(props: {
171171
<Button
172172
variant="outline"
173173
size="sm"
174-
disabled={props.isPending || props.data.length === 0}
174+
disabled={props.isPending || props.data.length < props.rowsPerPage}
175175
className="gap-1.5 bg-background"
176176
onClick={() => props.setPage(props.page + 1)}
177177
>
@@ -213,7 +213,6 @@ export function RecentTransfers(props: {
213213
}) {
214214
const rowsPerPage = 10;
215215
const [page, setPage] = useState(0);
216-
const [hasFetchedOnce, setHasFetchedOnce] = useState(false);
217216

218217
const tokenQuery = useTokenTransfers({
219218
chainId: props.clientContract.chain.id,
@@ -222,18 +221,11 @@ export function RecentTransfers(props: {
222221
limit: rowsPerPage,
223222
});
224223

225-
// eslint-disable-next-line no-restricted-syntax
226-
useEffect(() => {
227-
if (!tokenQuery.isPending) {
228-
setHasFetchedOnce(true);
229-
}
230-
}, [tokenQuery.isPending]);
231-
232224
return (
233225
<div>
234226
<RecentTransfersUI
235227
data={tokenQuery.data ?? []}
236-
isPending={tokenQuery.isPending && !hasFetchedOnce}
228+
isPending={tokenQuery.isPending}
237229
rowsPerPage={rowsPerPage}
238230
client={props.clientContract.client}
239231
tokenMetadata={{

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

Lines changed: 69 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { TransactionButton } from "components/buttons/TransactionButton";
1111
import { useTrack } from "hooks/analytics/useTrack";
1212
import {
1313
CheckIcon,
14+
CircleAlertIcon,
1415
CircleIcon,
1516
ExternalLinkIcon,
1617
InfinityIcon,
@@ -40,6 +41,7 @@ import {
4041
} from "thirdweb/react";
4142
import { getClaimParams, maxUint256 } from "thirdweb/utils";
4243
import { tryCatch } from "utils/try-catch";
44+
import { ToolTipLabel } from "../../../../../../../../../../@/components/ui/tooltip";
4345
import { getSDKTheme } from "../../../../../../../../components/sdk-component-theme";
4446
import { PublicPageConnectButton } from "../../../_components/PublicPageConnectButton";
4547
import { getCurrencyMeta } from "../../_utils/getCurrencyMeta";
@@ -222,23 +224,23 @@ export function ClaimTokenCardUI(props: {
222224
},
223225
});
224226

227+
const publicPrice = {
228+
pricePerTokenWei: props.claimCondition.pricePerToken,
229+
currencyAddress: props.claimCondition.currency,
230+
decimals: props.claimConditionCurrency.decimals,
231+
symbol: props.claimConditionCurrency.symbol,
232+
};
233+
225234
const claimParamsQuery = useQuery({
226235
queryKey: ["claim-params", props.contract.address, account?.address],
227236
queryFn: async () => {
228-
const defaultPricing = {
229-
pricePerTokenWei: props.claimCondition.pricePerToken,
230-
currencyAddress: props.claimCondition.currency,
231-
decimals: props.claimConditionCurrency.decimals,
232-
symbol: props.claimConditionCurrency.symbol,
233-
};
234-
235237
if (!account) {
236-
return defaultPricing;
238+
return publicPrice;
237239
}
238240

239241
const merkleRoot = props.claimCondition.merkleRoot;
240242
if (!merkleRoot || merkleRoot === padHex("0x", { size: 32 })) {
241-
return defaultPricing;
243+
return publicPrice;
242244
}
243245

244246
const claimParams = await getClaimParams({
@@ -313,6 +315,11 @@ export function ClaimTokenCardUI(props: {
313315
);
314316
}
315317

318+
const isShowingCustomPrice =
319+
claimParamsData &&
320+
(claimParamsData.pricePerTokenWei !== publicPrice.pricePerTokenWei ||
321+
claimParamsData.currencyAddress !== publicPrice.currencyAddress);
322+
316323
return (
317324
<div className="rounded-xl border bg-card ">
318325
<div className="border-b px-4 py-5 lg:px-5">
@@ -348,25 +355,22 @@ export function ClaimTokenCardUI(props: {
348355

349356
<div className="space-y-3 rounded-lg bg-muted/50 p-3">
350357
{/* Price per token */}
351-
<div className="flex justify-between font-medium text-sm">
352-
<span>Price per token</span>
353-
354-
<SkeletonContainer
355-
skeletonData={`0.00 ${props.claimConditionCurrency.symbol}`}
356-
loadedData={
357-
claimParamsData
358-
? claimParamsData.pricePerTokenWei === 0n
359-
? "FREE"
360-
: `${toTokens(
361-
claimParamsData.pricePerTokenWei,
362-
claimParamsData.decimals,
363-
)} ${claimParamsData.symbol}`
364-
: undefined
365-
}
366-
render={(v) => {
367-
return <span className="">{v}</span>;
368-
}}
369-
/>
358+
<div className="flex items-start justify-between font-medium text-sm">
359+
<span className="flex items-center gap-2">
360+
Price per token
361+
{isShowingCustomPrice && (
362+
<ToolTipLabel label="Your connected wallet address is added in the allowlist and is getting a special price">
363+
<CircleAlertIcon className="size-3.5 text-muted-foreground" />
364+
</ToolTipLabel>
365+
)}
366+
</span>
367+
368+
<div className="flex flex-col items-end gap-1">
369+
{isShowingCustomPrice && (
370+
<TokenPrice data={publicPrice} strikethrough={true} />
371+
)}
372+
<TokenPrice data={claimParamsData} strikethrough={false} />
373+
</div>
370374
</div>
371375

372376
{/* Quantity */}
@@ -447,6 +451,43 @@ export function ClaimTokenCardUI(props: {
447451
);
448452
}
449453

454+
function TokenPrice(props: {
455+
strikethrough: boolean;
456+
data:
457+
| {
458+
pricePerTokenWei: bigint;
459+
decimals: number;
460+
symbol: string;
461+
}
462+
| undefined;
463+
}) {
464+
return (
465+
<SkeletonContainer
466+
skeletonData={"0.00 ETH"}
467+
loadedData={
468+
props.data
469+
? props.data.pricePerTokenWei === 0n
470+
? "FREE"
471+
: `${toTokens(
472+
props.data.pricePerTokenWei,
473+
props.data.decimals,
474+
)} ${props.data.symbol}`
475+
: undefined
476+
}
477+
render={(v) => {
478+
if (props.strikethrough) {
479+
return (
480+
<s className="font-medium text-muted-foreground text-sm line-through decoration-muted-foreground/50">
481+
{v}
482+
</s>
483+
);
484+
}
485+
return <span className="font-medium text-foreground text-sm">{v}</span>;
486+
}}
487+
/>
488+
);
489+
}
490+
450491
function SupplyRemaining(props: {
451492
supplyClaimed: bigint;
452493
maxClaimableSupply: bigint;

0 commit comments

Comments
 (0)