Skip to content

[TOOL-4686] Dashboard: Add supply remaining progress in asset page claim tokens card #7283

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,19 @@ import { Spinner } from "@/components/ui/Spinner/Spinner";
import { Button } from "@/components/ui/button";
import { DecimalInput } from "@/components/ui/decimal-input";
import { Label } from "@/components/ui/label";
import { Progress } from "@/components/ui/progress";
import { SkeletonContainer } from "@/components/ui/skeleton";
import { cn } from "@/lib/utils";
import { useMutation, useQuery } from "@tanstack/react-query";
import { TransactionButton } from "components/buttons/TransactionButton";
import { useTrack } from "hooks/analytics/useTrack";
import { CheckIcon, CircleIcon, ExternalLinkIcon, XIcon } from "lucide-react";
import {
CheckIcon,
CircleIcon,
ExternalLinkIcon,
InfinityIcon,
XIcon,
} from "lucide-react";
import { useTheme } from "next-themes";
import Link from "next/link";
import { useState } from "react";
Expand All @@ -31,7 +38,7 @@ import {
useActiveWallet,
useSendTransaction,
} from "thirdweb/react";
import { getClaimParams } from "thirdweb/utils";
import { getClaimParams, maxUint256 } from "thirdweb/utils";
import { tryCatch } from "utils/try-catch";
import { getSDKTheme } from "../../../../../../../../components/sdk-component-theme";
import { PublicPageConnectButton } from "../../../_components/PublicPageConnectButton";
Expand All @@ -41,6 +48,11 @@ type ActiveClaimCondition = Awaited<ReturnType<typeof getActiveClaimCondition>>;

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

const compactNumberFormatter = new Intl.NumberFormat("en-US", {
notation: "compact",
maximumFractionDigits: 10,
});

export function ClaimTokenCardUI(props: {
contract: ThirdwebContract;
name: string;
Expand Down Expand Up @@ -304,7 +316,9 @@ export function ClaimTokenCardUI(props: {
return (
<div className="rounded-xl border bg-card ">
<div className="border-b px-4 py-5 lg:px-5">
<h2 className="font-bold text-lg">Buy {props.symbol}</h2>
<h2 className="font-semibold text-lg tracking-tight">
Buy {props.symbol}
</h2>
<p className="text-muted-foreground text-sm">
Buy tokens from the primary sale
</p>
Expand All @@ -320,11 +334,18 @@ export function ClaimTokenCardUI(props: {
id="token-amount"
symbol={props.symbol}
/>
{/* <p className="text-xs text-muted-foreground">Maximum purchasable: {tokenData.maxPurchasable} tokens</p> */}
</div>

<div className="h-4" />

<SupplyRemaining
supplyClaimed={props.claimCondition.supplyClaimed}
maxClaimableSupply={props.claimCondition.maxClaimableSupply}
decimals={props.decimals}
/>

<div className="h-4" />

<div className="space-y-3 rounded-lg bg-muted/50 p-3">
{/* Price per token */}
<div className="flex justify-between font-medium text-sm">
Expand Down Expand Up @@ -426,6 +447,58 @@ export function ClaimTokenCardUI(props: {
);
}

function SupplyRemaining(props: {
supplyClaimed: bigint;
maxClaimableSupply: bigint;
decimals: number;
}) {
const isMaxClaimableSupplyUnlimited = props.maxClaimableSupply === maxUint256;
const supplyClaimedTokenNumber = Number(
toTokens(props.supplyClaimed, props.decimals),
);

// if there is unlimited supply - show many are claimed
if (isMaxClaimableSupplyUnlimited) {
return (
<p className="flex items-center justify-between gap-2">
<span className="font-medium text-sm">Supply Claimed</span>
<span className="flex items-center gap-1 font-bold text-sm">
{compactNumberFormatter.format(supplyClaimedTokenNumber)} /{" "}
<InfinityIcon className="size-4" aria-label="Unlimited" />
</span>
</p>
);
}

const maxClaimableSupplyTokenNumber = Number(
toTokens(props.maxClaimableSupply, props.decimals),
);

const soldPercentage = isMaxClaimableSupplyUnlimited
? 0
: (supplyClaimedTokenNumber / maxClaimableSupplyTokenNumber) * 100;

const supplyRemainingTokenNumber =
maxClaimableSupplyTokenNumber - supplyClaimedTokenNumber;

// else - show supply remaining
return (
<div className="space-y-2">
<div className="flex items-center justify-between">
<span className="font-medium text-sm">Supply Remaining</span>
<span className="font-bold text-sm">
{compactNumberFormatter.format(supplyRemainingTokenNumber)} /{" "}
{compactNumberFormatter.format(maxClaimableSupplyTokenNumber)}
</span>
</div>
<Progress value={soldPercentage} className="h-2.5" />
<p className="font-medium text-muted-foreground text-xs">
{soldPercentage.toFixed(1)}% Sold
</p>
</div>
);
}

type Status = "idle" | "pending" | "success" | "error";

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