Skip to content

Commit 05f65ec

Browse files
committed
[TOOL-4686] Dashboard: Add supply remaining progress in asset page claim tokens card (#7283)
<!-- ## 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 enhances the `ClaimTokenCardUI` component by adding a new `SupplyRemaining` feature, which displays the supply of tokens claimed and remaining. It also improves the UI with a progress bar and formatting adjustments. ### Detailed summary - Added `Progress` component for visual representation of supply sold. - Introduced `SupplyRemaining` function to show claimed and remaining supply. - Formatted supply numbers using `compactNumberFormatter`. - Updated headings and styling for better UI consistency. > ✨ Ask PR-Codex anything about this PR by commenting with `/codex {your question}` <!-- end pr-codex --> <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Added a visual display showing claimed versus total claimable token supply, including a progress bar and percentage sold. - **Style** - Improved typography and spacing for headings and token symbol labels for better visual consistency. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
1 parent 6610633 commit 05f65ec

File tree

1 file changed

+78
-5
lines changed
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/erc20/_components/claim-tokens

1 file changed

+78
-5
lines changed

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

Lines changed: 78 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,19 @@ import { Spinner } from "@/components/ui/Spinner/Spinner";
33
import { Button } from "@/components/ui/button";
44
import { DecimalInput } from "@/components/ui/decimal-input";
55
import { Label } from "@/components/ui/label";
6+
import { Progress } from "@/components/ui/progress";
67
import { SkeletonContainer } from "@/components/ui/skeleton";
78
import { cn } from "@/lib/utils";
89
import { useMutation, useQuery } from "@tanstack/react-query";
910
import { TransactionButton } from "components/buttons/TransactionButton";
1011
import { useTrack } from "hooks/analytics/useTrack";
11-
import { CheckIcon, CircleIcon, ExternalLinkIcon, XIcon } from "lucide-react";
12+
import {
13+
CheckIcon,
14+
CircleIcon,
15+
ExternalLinkIcon,
16+
InfinityIcon,
17+
XIcon,
18+
} from "lucide-react";
1219
import { useTheme } from "next-themes";
1320
import Link from "next/link";
1421
import { useState } from "react";
@@ -31,7 +38,7 @@ import {
3138
useActiveWallet,
3239
useSendTransaction,
3340
} from "thirdweb/react";
34-
import { getClaimParams } from "thirdweb/utils";
41+
import { getClaimParams, maxUint256 } from "thirdweb/utils";
3542
import { tryCatch } from "utils/try-catch";
3643
import { getSDKTheme } from "../../../../../../../../components/sdk-component-theme";
3744
import { PublicPageConnectButton } from "../../../_components/PublicPageConnectButton";
@@ -41,6 +48,11 @@ type ActiveClaimCondition = Awaited<ReturnType<typeof getActiveClaimCondition>>;
4148

4249
// TODO UI improvements - show how many tokens connected wallet can claim at max
4350

51+
const compactNumberFormatter = new Intl.NumberFormat("en-US", {
52+
notation: "compact",
53+
maximumFractionDigits: 10,
54+
});
55+
4456
export function ClaimTokenCardUI(props: {
4557
contract: ThirdwebContract;
4658
name: string;
@@ -304,7 +316,9 @@ export function ClaimTokenCardUI(props: {
304316
return (
305317
<div className="rounded-xl border bg-card ">
306318
<div className="border-b px-4 py-5 lg:px-5">
307-
<h2 className="font-bold text-lg">Buy {props.symbol}</h2>
319+
<h2 className="font-semibold text-lg tracking-tight">
320+
Buy {props.symbol}
321+
</h2>
308322
<p className="text-muted-foreground text-sm">
309323
Buy tokens from the primary sale
310324
</p>
@@ -320,11 +334,18 @@ export function ClaimTokenCardUI(props: {
320334
id="token-amount"
321335
symbol={props.symbol}
322336
/>
323-
{/* <p className="text-xs text-muted-foreground">Maximum purchasable: {tokenData.maxPurchasable} tokens</p> */}
324337
</div>
325338

326339
<div className="h-4" />
327340

341+
<SupplyRemaining
342+
supplyClaimed={props.claimCondition.supplyClaimed}
343+
maxClaimableSupply={props.claimCondition.maxClaimableSupply}
344+
decimals={props.decimals}
345+
/>
346+
347+
<div className="h-4" />
348+
328349
<div className="space-y-3 rounded-lg bg-muted/50 p-3">
329350
{/* Price per token */}
330351
<div className="flex justify-between font-medium text-sm">
@@ -426,6 +447,58 @@ export function ClaimTokenCardUI(props: {
426447
);
427448
}
428449

450+
function SupplyRemaining(props: {
451+
supplyClaimed: bigint;
452+
maxClaimableSupply: bigint;
453+
decimals: number;
454+
}) {
455+
const isMaxClaimableSupplyUnlimited = props.maxClaimableSupply === maxUint256;
456+
const supplyClaimedTokenNumber = Number(
457+
toTokens(props.supplyClaimed, props.decimals),
458+
);
459+
460+
// if there is unlimited supply - show many are claimed
461+
if (isMaxClaimableSupplyUnlimited) {
462+
return (
463+
<p className="flex items-center justify-between gap-2">
464+
<span className="font-medium text-sm">Supply Claimed</span>
465+
<span className="flex items-center gap-1 font-bold text-sm">
466+
{compactNumberFormatter.format(supplyClaimedTokenNumber)} /{" "}
467+
<InfinityIcon className="size-4" aria-label="Unlimited" />
468+
</span>
469+
</p>
470+
);
471+
}
472+
473+
const maxClaimableSupplyTokenNumber = Number(
474+
toTokens(props.maxClaimableSupply, props.decimals),
475+
);
476+
477+
const soldPercentage = isMaxClaimableSupplyUnlimited
478+
? 0
479+
: (supplyClaimedTokenNumber / maxClaimableSupplyTokenNumber) * 100;
480+
481+
const supplyRemainingTokenNumber =
482+
maxClaimableSupplyTokenNumber - supplyClaimedTokenNumber;
483+
484+
// else - show supply remaining
485+
return (
486+
<div className="space-y-2">
487+
<div className="flex items-center justify-between">
488+
<span className="font-medium text-sm">Supply Remaining</span>
489+
<span className="font-bold text-sm">
490+
{compactNumberFormatter.format(supplyRemainingTokenNumber)} /{" "}
491+
{compactNumberFormatter.format(maxClaimableSupplyTokenNumber)}
492+
</span>
493+
</div>
494+
<Progress value={soldPercentage} className="h-2.5" />
495+
<p className="font-medium text-muted-foreground text-xs">
496+
{soldPercentage.toFixed(1)}% Sold
497+
</p>
498+
</div>
499+
);
500+
}
501+
429502
type Status = "idle" | "pending" | "success" | "error";
430503

431504
const statusToIcon: Record<Status, React.FC<{ className: string }>> = {
@@ -472,7 +545,7 @@ function PriceInput(props: {
472545
className="!text-2xl h-auto truncate bg-muted/50 pr-14 font-bold"
473546
/>
474547
{props.symbol && (
475-
<div className="-translate-y-1/2 absolute top-1/2 right-4 font-semibold text-base text-muted-foreground">
548+
<div className="-translate-y-1/2 absolute top-1/2 right-3 font-medium text-muted-foreground text-sm">
476549
{props.symbol}
477550
</div>
478551
)}

0 commit comments

Comments
 (0)