Skip to content

Commit 8c1fa1b

Browse files
committed
[Dashboard] Remove tw-components from claim condition UI (#5368)
## Problem solved Short description of the bug fixed or feature added <!-- start pr-codex --> --- ## PR-Codex overview This PR focuses on improving the user interface and functionality of the `claim-conditions` components in the dashboard application. It enhances accessibility, updates styling, and refines the input handling for claim conditions. ### Detailed summary - Updated `MaxClaimablePerWalletInput` to use `className` for styling and added a `target` attribute to the `Link`. - Simplified `CustomFormControl` by replacing `Heading` with `FormLabel`. - Changed `Text` components to `p` tags in `PricePreview` for better semantic structure. - Refactored `QuantityInputWithUnlimited` to use new UI components and improved props handling. - Enhanced `ClaimConditionsPhase` layout with Tailwind CSS classes, replacing Chakra UI components. - Updated button styles and functionalities, including the removal button and editing toggle. - Improved readability and structure of the phase details display. > ✨ Ask PR-Codex anything about this PR by commenting with `/codex {your question}` <!-- end pr-codex -->
1 parent 453e45b commit 8c1fa1b

File tree

5 files changed

+128
-145
lines changed

5 files changed

+128
-145
lines changed

apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/_components/claim-conditions/claim-conditions-form/Inputs/MaxClaimablePerWalletInput.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Link } from "tw-components";
1+
import Link from "next/link";
22
import { QuantityInputWithUnlimited } from "../../quantity-input-with-unlimited";
33
import { CustomFormControl } from "../common";
44
import { useClaimConditionsFormContext } from "../index";
@@ -41,8 +41,8 @@ export const MaxClaimablePerWalletInput: React.FC = () => {
4141
Limits are set per wallets and not per user, sophisticated actors
4242
could get around wallet restrictions.{" "}
4343
<Link
44-
isExternal
45-
color="blue.500"
44+
className="text-blue-500"
45+
target="_blank"
4646
href="https://portal.thirdweb.com/contracts/design/Drop#sybil-attacks"
4747
>
4848
Learn more

apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/_components/claim-conditions/claim-conditions-form/common.tsx

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,6 @@
11
import { Flex, FormControl } from "@chakra-ui/react";
22
import type { FieldError } from "react-hook-form";
3-
import {
4-
FormErrorMessage,
5-
FormHelperText,
6-
FormLabel,
7-
Heading,
8-
} from "tw-components";
3+
import { FormErrorMessage, FormHelperText, FormLabel } from "tw-components";
94
import type { ComponentWithChildren } from "types/component-with-children";
105
import { useClaimConditionsFormContext } from ".";
116

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

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

4344
return (
44-
<Card position="relative" p={8}>
45-
<Flex direction="column" gap={8}>
46-
<Flex
47-
align="flex-start"
48-
justify="space-between"
49-
position="absolute"
50-
top="10px"
51-
right="10px"
52-
gap={1}
45+
<Card className="relative flex flex-col gap-8 p-8">
46+
<div className="absolute top-3 right-3 flex flex-row items-start justify-between gap-1">
47+
<Button
48+
variant="ghost"
49+
onClick={toggleEditing}
50+
size="sm"
51+
className="gap-2"
5352
>
53+
{field.isEditing ? "Collapse" : isAdmin ? "Edit" : "See Phase"}
54+
{field.isEditing ? (
55+
<ChevronUpIcon className="size-4" />
56+
) : (
57+
<ChevronDownIcon className="size-4" />
58+
)}
59+
</Button>
60+
<AdminOnly contract={contract}>
5461
<Button
5562
variant="ghost"
56-
onClick={toggleEditing}
63+
onClick={onRemove}
64+
disabled={isPending}
5765
size="sm"
58-
rightIcon={
59-
field.isEditing ? (
60-
<ChevronUpIcon className="size-4" />
61-
) : (
62-
<ChevronDownIcon className="size-4" />
63-
)
64-
}
66+
className="gap-2 text-red-300"
6567
>
66-
{field.isEditing ? "Collapse" : isAdmin ? "Edit" : "See Phase"}
68+
Remove <XIcon className="size-4" />
6769
</Button>
68-
<AdminOnly contract={contract}>
69-
<Button
70-
variant="ghost"
71-
onClick={onRemove}
72-
isDisabled={isPending}
73-
colorScheme="red"
74-
size="sm"
75-
rightIcon={<XIcon className="size-4" />}
76-
>
77-
Remove
78-
</Button>
79-
</AdminOnly>
80-
</Flex>
70+
</AdminOnly>
71+
</div>
8172

82-
<Flex flexDir="column" gap={2} mt={{ base: 4, md: 0 }}>
83-
<Flex gap={3} alignItems="center">
84-
<Heading>{ClaimConditionTypeData[claimConditionType].name}</Heading>
85-
{isActive && (
86-
<Badge colorScheme="green" borderRadius="lg" p={1.5}>
87-
Currently active
88-
</Badge>
89-
)}
90-
</Flex>
73+
<div className="mt-4 flex flex-col gap-2 md:mt-0">
74+
<div className="flex flex-row items-center gap-3">
75+
<p className="font-bold text-lg text-muted-foreground">
76+
{ClaimConditionTypeData[claimConditionType].name}
77+
</p>
78+
{isActive && (
79+
<Badge variant="success" className="rounded-lg p-1.5">
80+
Currently active
81+
</Badge>
82+
)}
83+
</div>
9184

92-
<Text>{ClaimConditionTypeData[claimConditionType].description}</Text>
93-
</Flex>
85+
<p className="text-muted-foreground">
86+
{ClaimConditionTypeData[claimConditionType].description}
87+
</p>
88+
</div>
9489

95-
{!field.isEditing ? (
96-
<SimpleGrid columns={{ base: 2, md: 4 }} gap={2}>
97-
<div className="flex flex-col">
98-
<Text fontWeight="bold">Phase start</Text>
99-
<Text>{field.startTime?.toLocaleString()}</Text>
100-
</div>
101-
<div className="flex flex-col">
102-
<Text fontWeight="bold">
103-
{isErc20 ? "Tokens" : "NFTs"} to drop
104-
</Text>
105-
<Text textTransform="capitalize">{field.maxClaimableSupply}</Text>
106-
</div>
107-
<PricePreview
108-
price={field.price}
109-
currencyAddress={field.currencyAddress}
110-
contractChainId={contract.chain.id}
111-
/>
112-
<div className="flex flex-col">
113-
<Text fontWeight="bold">Limit per wallet</Text>
114-
{claimConditionType === "specific" ? (
115-
<Text>Set in the snapshot</Text>
116-
) : claimConditionType === "creator" ? (
117-
<Text>Unlimited</Text>
118-
) : (
119-
<Text textTransform="capitalize">
120-
{field.maxClaimablePerWallet}
121-
</Text>
122-
)}
123-
</div>
124-
</SimpleGrid>
125-
) : (
126-
<>
127-
<CustomFormGroup>
128-
{/* Phase Name Input / Form Title */}
129-
{isMultiPhase ? <PhaseNameInput /> : null}
130-
<PhaseStartTimeInput />
131-
</CustomFormGroup>
90+
{!field.isEditing ? (
91+
<div className="grid grid-cols-2 gap-2 md:grid-cols-4">
92+
<div className="flex flex-col">
93+
<p className="font-bold text-muted-foreground">Phase start</p>
94+
<p className="text-muted-foreground">
95+
{field.startTime?.toLocaleString()}
96+
</p>
97+
</div>
98+
<div className="flex flex-col">
99+
<p className="font-bold text-muted-foreground">
100+
{isErc20 ? "Tokens" : "NFTs"} to drop
101+
</p>
102+
<p className="text-muted-foreground capitalize">
103+
{field.maxClaimableSupply}
104+
</p>
105+
</div>
106+
<PricePreview
107+
price={field.price}
108+
currencyAddress={field.currencyAddress}
109+
contractChainId={contract.chain.id}
110+
/>
111+
<div className="flex flex-col">
112+
<p className="font-bold text-muted-foreground">Limit per wallet</p>
113+
{claimConditionType === "specific" ? (
114+
<p>Set in the snapshot</p>
115+
) : claimConditionType === "creator" ? (
116+
<p>Unlimited</p>
117+
) : (
118+
<p className="text-muted-foreground capitalize">
119+
{field.maxClaimablePerWallet}
120+
</p>
121+
)}
122+
</div>
123+
</div>
124+
) : (
125+
<>
126+
<CustomFormGroup>
127+
{/* Phase Name Input / Form Title */}
128+
{isMultiPhase ? <PhaseNameInput /> : null}
129+
<PhaseStartTimeInput />
130+
</CustomFormGroup>
132131

133-
<CreatorInput
134-
creatorAddress={
135-
(field.snapshot?.[0] as { address: string })?.address
136-
}
137-
/>
132+
<CreatorInput
133+
creatorAddress={
134+
(field.snapshot?.[0] as { address: string })?.address
135+
}
136+
/>
137+
138+
<CustomFormGroup>
139+
<MaxClaimableSupplyInput />
140+
<ClaimPriceInput contractChainId={contract.chain.id} />
141+
</CustomFormGroup>
138142

143+
{claimConditionType === "specific" ||
144+
claimConditionType === "creator" ? null : (
139145
<CustomFormGroup>
140-
<MaxClaimableSupplyInput />
141-
<ClaimPriceInput contractChainId={contract.chain.id} />
146+
<MaxClaimablePerWalletInput />
142147
</CustomFormGroup>
148+
)}
143149

144-
{claimConditionType === "specific" ||
145-
claimConditionType === "creator" ? null : (
146-
<CustomFormGroup>
147-
<MaxClaimablePerWalletInput />
148-
</CustomFormGroup>
149-
)}
150-
151-
<ClaimerSelection />
152-
</>
153-
)}
154-
</Flex>
150+
<ClaimerSelection />
151+
</>
152+
)}
155153
</Card>
156154
);
157155
};

apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/_components/claim-conditions/price-preview.tsx

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import { CURRENCIES } from "constants/currencies";
22
import { useAllChainsData } from "hooks/chains/allChains";
3-
import { Text } from "tw-components";
43
import { shortenIfAddress } from "utils/usedapp-external";
54
import { isAddressZero } from "utils/zeroAddress";
65

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

2726
return (
28-
<div className="flex flex-col">
29-
<Text fontWeight="bold">Default price</Text>
27+
<div className="flex flex-col text-muted-foreground">
28+
<p className="font-bold">Default price</p>
3029
{Number(price) === 0 ? (
31-
<Text>Free</Text>
30+
<p>Free</p>
3231
) : (
33-
<Text>
32+
<p>
3433
{price}{" "}
3534
{foundCurrency
3635
? foundCurrency.symbol
3736
: isAddressZero(currencyAddress || "")
3837
? chain?.nativeCurrency.symbol || "(Native Token)"
3938
: `(${shortenIfAddress(currencyAddress)})`}
40-
</Text>
39+
</p>
4140
)}
4241
</div>
4342
);
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,14 @@
1-
import {
2-
Input,
3-
InputGroup,
4-
type InputProps,
5-
InputRightElement,
6-
} from "@chakra-ui/react";
1+
import { Button } from "@/components/ui/button";
2+
import { Input } from "@/components/ui/input";
73
import { useEffect, useState } from "react";
8-
import { Button } from "tw-components";
94

10-
interface QuantityInputWithUnlimitedProps
11-
extends Omit<InputProps, "onChange" | "value" | "onBlur" | "max" | "min"> {
5+
interface QuantityInputWithUnlimitedProps {
126
value: string;
137
onChange: (value: string) => void;
148
hideMaxButton?: true;
159
decimals?: number;
10+
isDisabled: boolean;
11+
isRequired: boolean;
1612
}
1713

1814
export const QuantityInputWithUnlimited: React.FC<
@@ -24,7 +20,6 @@ export const QuantityInputWithUnlimited: React.FC<
2420
isDisabled,
2521
isRequired,
2622
decimals,
27-
...restInputProps
2823
}) => {
2924
const [stringValue, setStringValue] = useState<string>(
3025
Number.isNaN(Number(value)) ? "0" : value.toString(),
@@ -50,10 +45,10 @@ export const QuantityInputWithUnlimited: React.FC<
5045
};
5146

5247
return (
53-
<InputGroup {...restInputProps}>
48+
<div className="flex flex-row items-center rounded-md border border-border">
5449
<Input
55-
isRequired={isRequired}
56-
isDisabled={isDisabled}
50+
required={isRequired}
51+
disabled={isDisabled}
5752
value={stringValue === "unlimited" ? "Unlimited" : stringValue}
5853
onChange={(e) => updateValue(e.currentTarget.value)}
5954
onBlur={() => {
@@ -65,23 +60,21 @@ export const QuantityInputWithUnlimited: React.FC<
6560
setStringValue("0");
6661
}
6762
}}
63+
className="border-none"
6864
/>
6965
{hideMaxButton ? null : (
70-
<InputRightElement w="auto">
71-
<Button
72-
isDisabled={isDisabled}
73-
colorScheme="primary"
74-
variant="ghost"
75-
size="sm"
76-
mr={1}
77-
onClick={() => {
78-
updateValue("unlimited");
79-
}}
80-
>
81-
Unlimited
82-
</Button>
83-
</InputRightElement>
66+
<Button
67+
disabled={isDisabled}
68+
variant="ghost"
69+
size="sm"
70+
className="mr-1 text-primary"
71+
onClick={() => {
72+
updateValue("unlimited");
73+
}}
74+
>
75+
Unlimited
76+
</Button>
8477
)}
85-
</InputGroup>
78+
</div>
8679
);
8780
};

0 commit comments

Comments
 (0)