Skip to content
Merged
Show file tree
Hide file tree
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
@@ -1,4 +1,4 @@
import { Link } from "tw-components";
import Link from "next/link";
import { QuantityInputWithUnlimited } from "../../quantity-input-with-unlimited";
import { CustomFormControl } from "../common";
import { useClaimConditionsFormContext } from "../index";
Expand Down Expand Up @@ -41,8 +41,8 @@ export const MaxClaimablePerWalletInput: React.FC = () => {
Limits are set per wallets and not per user, sophisticated actors
could get around wallet restrictions.{" "}
<Link
isExternal
color="blue.500"
className="text-blue-500"
target="_blank"
href="https://portal.thirdweb.com/contracts/design/Drop#sybil-attacks"
>
Learn more
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,6 @@
import { Flex, FormControl } from "@chakra-ui/react";
import type { FieldError } from "react-hook-form";
import {
FormErrorMessage,
FormHelperText,
FormLabel,
Heading,
} from "tw-components";
import { FormErrorMessage, FormHelperText, FormLabel } from "tw-components";
import type { ComponentWithChildren } from "types/component-with-children";
import { useClaimConditionsFormContext } from ".";

Expand All @@ -22,9 +17,7 @@ export const CustomFormControl: ComponentWithChildren<
return (
<FormControl isDisabled={props.disabled} isInvalid={!!props.error}>
{/* label */}
<Heading as={FormLabel} size="label.md">
{props.label}
</Heading>
<FormLabel className="font-bold">{props.label}</FormLabel>

{/* input */}
{props.children}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { Card } from "@/components/ui/card";
import { AdminOnly } from "@3rdweb-sdk/react/components/roles/admin-only";
import { Flex, SimpleGrid } from "@chakra-ui/react";
import { ChevronDownIcon, ChevronUpIcon, XIcon } from "lucide-react";
import type { ThirdwebContract } from "thirdweb";
import { Badge, Button, Card, Heading, Text } from "tw-components";
import { ClaimConditionTypeData, useClaimConditionsFormContext } from ".";
import { PricePreview } from "../price-preview";
import { ClaimPriceInput } from "./Inputs/ClaimPriceInput";
Expand Down Expand Up @@ -41,117 +42,114 @@ export const ClaimConditionsPhase: React.FC<ClaimConditionsPhaseProps> = ({
};

return (
<Card position="relative" p={8}>
<Flex direction="column" gap={8}>
<Flex
align="flex-start"
justify="space-between"
position="absolute"
top="10px"
right="10px"
gap={1}
<Card className="relative flex flex-col gap-8 p-8">
<div className="absolute top-3 right-3 flex flex-row items-start justify-between gap-1">
<Button
variant="ghost"
onClick={toggleEditing}
size="sm"
className="gap-2"
>
{field.isEditing ? "Collapse" : isAdmin ? "Edit" : "See Phase"}
{field.isEditing ? (
<ChevronUpIcon className="size-4" />
) : (
<ChevronDownIcon className="size-4" />
)}
</Button>
<AdminOnly contract={contract}>
<Button
variant="ghost"
onClick={toggleEditing}
onClick={onRemove}
disabled={isPending}
size="sm"
rightIcon={
field.isEditing ? (
<ChevronUpIcon className="size-4" />
) : (
<ChevronDownIcon className="size-4" />
)
}
className="gap-2 text-red-300"
>
{field.isEditing ? "Collapse" : isAdmin ? "Edit" : "See Phase"}
Remove <XIcon className="size-4" />
</Button>
<AdminOnly contract={contract}>
<Button
variant="ghost"
onClick={onRemove}
isDisabled={isPending}
colorScheme="red"
size="sm"
rightIcon={<XIcon className="size-4" />}
>
Remove
</Button>
</AdminOnly>
</Flex>
</AdminOnly>
</div>

<Flex flexDir="column" gap={2} mt={{ base: 4, md: 0 }}>
<Flex gap={3} alignItems="center">
<Heading>{ClaimConditionTypeData[claimConditionType].name}</Heading>
{isActive && (
<Badge colorScheme="green" borderRadius="lg" p={1.5}>
Currently active
</Badge>
)}
</Flex>
<div className="mt-4 flex flex-col gap-2 md:mt-0">
<div className="flex flex-row items-center gap-3">
<p className="font-bold text-lg text-muted-foreground">
{ClaimConditionTypeData[claimConditionType].name}
</p>
{isActive && (
<Badge variant="success" className="rounded-lg p-1.5">
Currently active
</Badge>
)}
</div>

<Text>{ClaimConditionTypeData[claimConditionType].description}</Text>
</Flex>
<p className="text-muted-foreground">
{ClaimConditionTypeData[claimConditionType].description}
</p>
</div>

{!field.isEditing ? (
<SimpleGrid columns={{ base: 2, md: 4 }} gap={2}>
<div className="flex flex-col">
<Text fontWeight="bold">Phase start</Text>
<Text>{field.startTime?.toLocaleString()}</Text>
</div>
<div className="flex flex-col">
<Text fontWeight="bold">
{isErc20 ? "Tokens" : "NFTs"} to drop
</Text>
<Text textTransform="capitalize">{field.maxClaimableSupply}</Text>
</div>
<PricePreview
price={field.price}
currencyAddress={field.currencyAddress}
contractChainId={contract.chain.id}
/>
<div className="flex flex-col">
<Text fontWeight="bold">Limit per wallet</Text>
{claimConditionType === "specific" ? (
<Text>Set in the snapshot</Text>
) : claimConditionType === "creator" ? (
<Text>Unlimited</Text>
) : (
<Text textTransform="capitalize">
{field.maxClaimablePerWallet}
</Text>
)}
</div>
</SimpleGrid>
) : (
<>
<CustomFormGroup>
{/* Phase Name Input / Form Title */}
{isMultiPhase ? <PhaseNameInput /> : null}
<PhaseStartTimeInput />
</CustomFormGroup>
{!field.isEditing ? (
<div className="grid grid-cols-2 gap-2 md:grid-cols-4">
<div className="flex flex-col">
<p className="font-bold text-muted-foreground">Phase start</p>
<p className="text-muted-foreground">
{field.startTime?.toLocaleString()}
</p>
</div>
<div className="flex flex-col">
<p className="font-bold text-muted-foreground">
{isErc20 ? "Tokens" : "NFTs"} to drop
</p>
<p className="text-muted-foreground capitalize">
{field.maxClaimableSupply}
</p>
</div>
<PricePreview
price={field.price}
currencyAddress={field.currencyAddress}
contractChainId={contract.chain.id}
/>
<div className="flex flex-col">
<p className="font-bold text-muted-foreground">Limit per wallet</p>
{claimConditionType === "specific" ? (
<p>Set in the snapshot</p>
) : claimConditionType === "creator" ? (
<p>Unlimited</p>
) : (
<p className="text-muted-foreground capitalize">
{field.maxClaimablePerWallet}
</p>
)}
</div>
</div>
) : (
<>
<CustomFormGroup>
{/* Phase Name Input / Form Title */}
{isMultiPhase ? <PhaseNameInput /> : null}
<PhaseStartTimeInput />
</CustomFormGroup>

<CreatorInput
creatorAddress={
(field.snapshot?.[0] as { address: string })?.address
}
/>
<CreatorInput
creatorAddress={
(field.snapshot?.[0] as { address: string })?.address
}
/>

<CustomFormGroup>
<MaxClaimableSupplyInput />
<ClaimPriceInput contractChainId={contract.chain.id} />
</CustomFormGroup>

{claimConditionType === "specific" ||
claimConditionType === "creator" ? null : (
<CustomFormGroup>
<MaxClaimableSupplyInput />
<ClaimPriceInput contractChainId={contract.chain.id} />
<MaxClaimablePerWalletInput />
</CustomFormGroup>
)}

{claimConditionType === "specific" ||
claimConditionType === "creator" ? null : (
<CustomFormGroup>
<MaxClaimablePerWalletInput />
</CustomFormGroup>
)}

<ClaimerSelection />
</>
)}
</Flex>
<ClaimerSelection />
</>
)}
</Card>
);
};
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { CURRENCIES } from "constants/currencies";
import { useAllChainsData } from "hooks/chains/allChains";
import { Text } from "tw-components";
import { shortenIfAddress } from "utils/usedapp-external";
import { isAddressZero } from "utils/zeroAddress";

Expand All @@ -25,19 +24,19 @@ export const PricePreview: React.FC<PricePreviewProps> = ({
);

return (
<div className="flex flex-col">
<Text fontWeight="bold">Default price</Text>
<div className="flex flex-col text-muted-foreground">
<p className="font-bold">Default price</p>
{Number(price) === 0 ? (
<Text>Free</Text>
<p>Free</p>
) : (
<Text>
<p>
{price}{" "}
{foundCurrency
? foundCurrency.symbol
: isAddressZero(currencyAddress || "")
? chain?.nativeCurrency.symbol || "(Native Token)"
: `(${shortenIfAddress(currencyAddress)})`}
</Text>
</p>
)}
</div>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,14 @@
import {
Input,
InputGroup,
type InputProps,
InputRightElement,
} from "@chakra-ui/react";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { useEffect, useState } from "react";
import { Button } from "tw-components";

interface QuantityInputWithUnlimitedProps
extends Omit<InputProps, "onChange" | "value" | "onBlur" | "max" | "min"> {
interface QuantityInputWithUnlimitedProps {
value: string;
onChange: (value: string) => void;
hideMaxButton?: true;
decimals?: number;
isDisabled: boolean;
isRequired: boolean;
}

export const QuantityInputWithUnlimited: React.FC<
Expand All @@ -24,7 +20,6 @@ export const QuantityInputWithUnlimited: React.FC<
isDisabled,
isRequired,
decimals,
...restInputProps
}) => {
const [stringValue, setStringValue] = useState<string>(
Number.isNaN(Number(value)) ? "0" : value.toString(),
Expand All @@ -50,10 +45,10 @@ export const QuantityInputWithUnlimited: React.FC<
};

return (
<InputGroup {...restInputProps}>
<div className="flex flex-row items-center rounded-md border border-border">
<Input
isRequired={isRequired}
isDisabled={isDisabled}
required={isRequired}
disabled={isDisabled}
value={stringValue === "unlimited" ? "Unlimited" : stringValue}
onChange={(e) => updateValue(e.currentTarget.value)}
onBlur={() => {
Expand All @@ -65,23 +60,21 @@ export const QuantityInputWithUnlimited: React.FC<
setStringValue("0");
}
}}
className="border-none"
/>
{hideMaxButton ? null : (
<InputRightElement w="auto">
<Button
isDisabled={isDisabled}
colorScheme="primary"
variant="ghost"
size="sm"
mr={1}
onClick={() => {
updateValue("unlimited");
}}
>
Unlimited
</Button>
</InputRightElement>
<Button
disabled={isDisabled}
variant="ghost"
size="sm"
className="mr-1 text-primary"
onClick={() => {
updateValue("unlimited");
}}
>
Unlimited
</Button>
)}
</InputGroup>
</div>
);
};
Loading