Skip to content

Commit 1659ae3

Browse files
committed
Dashboard: Migrate Various forms in contract pages from chakra to tailwind (#7690)
<!-- ## 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 refactoring the form components in the dashboard's NFT management system to improve structure and usability. It replaces Chakra UI components with custom UI components, enhancing consistency and maintainability. ### Detailed summary - Refactored `PropertiesFormControl` to utilize custom form components. - Removed unused imports and consolidated imports from `@chakra-ui/react`. - Updated form handling to use `FormField`, `FormItem`, and `FormDescription`. - Enhanced error handling by integrating `FormMessage` for displaying validation messages. - Replaced `Flex`, `FormControl`, and other Chakra UI components with custom equivalents. - Updated `LazyMintNftForm`, `NFTMintForm`, and `UpdateNftMetadata` forms to use new structure and components. - Improved user experience by refining button and input styles and behavior. > ✨ 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 * **Refactor** * Migrated all NFT-related form components from Chakra UI to a custom UI library, updating form controls, buttons, accordions, and input elements. * Updated error handling and helper text display to use new form components. * Simplified and modernized form structures for minting, lazy minting, metadata updates, and property controls. * Removed file upload support and reset functionality from the properties form. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
1 parent 88d44af commit 1659ae3

File tree

4 files changed

+868
-643
lines changed

4 files changed

+868
-643
lines changed
Lines changed: 97 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,4 @@
1-
import {
2-
Flex,
3-
FormControl,
4-
IconButton,
5-
Input,
6-
InputGroup,
7-
InputRightElement,
8-
Tooltip,
9-
} from "@chakra-ui/react";
10-
import { Button } from "chakra/button";
11-
import { FormErrorMessage, FormLabel } from "chakra/form";
12-
import { BanIcon, PlusIcon, TrashIcon, UploadIcon, XIcon } from "lucide-react";
1+
import { PlusIcon, TrashIcon, XIcon } from "lucide-react";
132
import { useEffect } from "react";
143
import {
154
type ArrayPath,
@@ -25,7 +14,16 @@ import {
2514
type WatchObserver,
2615
} from "react-hook-form";
2716
import type { ThirdwebClient } from "thirdweb";
28-
import { FileInput } from "@/components/blocks/FileInput";
17+
import { Button } from "@/components/ui/button";
18+
import {
19+
FormControl,
20+
FormField,
21+
FormItem,
22+
FormLabel,
23+
FormMessage,
24+
} from "@/components/ui/form";
25+
import { Input } from "@/components/ui/input";
26+
import { cn } from "@/lib/utils";
2927

3028
type OptionalPropertiesInput = {
3129
[key: string]: string | number;
@@ -46,17 +44,15 @@ interface IPropertiesFormControlProps<
4644
client: ThirdwebClient;
4745
}
4846

49-
export const PropertiesFormControl = <
47+
export function PropertiesFormControl<
5048
TFieldValues extends IPropertyFieldValues,
5149
>({
5250
control,
53-
register,
5451
watch,
5552
errors,
5653
setValue,
57-
client,
58-
}: React.PropsWithChildren<IPropertiesFormControlProps<TFieldValues>>) => {
59-
const { fields, append, remove, replace } = useFieldArray({
54+
}: React.PropsWithChildren<IPropertiesFormControlProps<TFieldValues>>) {
55+
const { fields, append, remove } = useFieldArray({
6056
control,
6157
name: "attributes" as ArrayPath<TFieldValues>,
6258
});
@@ -72,122 +68,116 @@ export const PropertiesFormControl = <
7268

7369
return (
7470
<div className="flex flex-col gap-4">
75-
<Flex align="center" direction="row" justify="space-between">
76-
<FormLabel m={0}>Attributes</FormLabel>
77-
<Button
78-
colorScheme="red"
79-
// biome-ignore lint/suspicious/noExplicitAny: FIXME
80-
onClick={() => replace([{ trait_type: "", value: "" } as any])}
81-
rightIcon={<BanIcon className="size-4" />}
82-
size="xs"
83-
variant="outline"
84-
>
85-
Reset
86-
</Button>
87-
</Flex>
71+
<FormLabel>Attributes</FormLabel>
8872
{fields.map((field, index) => {
8973
// biome-ignore lint/suspicious/noExplicitAny: FIXME
9074
const keyError = (errors as any)?.attributes?.[index]?.trait_type
9175
?.message as string | undefined;
9276
// biome-ignore lint/suspicious/noExplicitAny: FIXME
9377
const valueError = (errors as any)?.attributes?.[index]?.value
9478
?.message as string | undefined;
95-
const isInvalid = !!(keyError || valueError);
79+
const _isInvalid = !!(keyError || valueError);
9680

9781
return (
9882
<div className="flex flex-row items-center gap-2" key={field.id}>
99-
<FormControl
100-
className="flex flex-row items-start gap-3"
101-
isInvalid={isInvalid}
102-
>
103-
<FormControl isInvalid={!!keyError}>
104-
<Input
105-
{...register(
106-
`attributes.${index}.trait_type` as Path<TFieldValues>,
107-
)}
108-
placeholder="trait_type"
109-
/>
110-
<FormErrorMessage>{keyError}</FormErrorMessage>
111-
</FormControl>
112-
<FormControl isInvalid={!!valueError}>
113-
{watch(
114-
`attributes.${index}.value` as unknown as WatchObserver<TFieldValues>,
115-
) instanceof File ? (
116-
<InputGroup>
117-
<Input
118-
isDisabled
119-
value={
120-
watch(`attributes.${index}.value` as Path<TFieldValues>)
121-
.name
122-
}
123-
/>
124-
<InputRightElement>
125-
<TrashIcon
126-
className="size-4 cursor-pointer text-red-300 hover:text-red-200"
127-
onClick={() =>
128-
setValue(
129-
`attributes.${index}.value` as Path<TFieldValues>,
130-
"" as PathValue<TFieldValues, Path<TFieldValues>>,
131-
)
132-
}
83+
<div className="flex flex-row items-start gap-3 flex-1">
84+
<FormField
85+
control={control}
86+
name={`attributes.${index}.trait_type` as Path<TFieldValues>}
87+
render={({ field: traitField }) => (
88+
<FormItem className="flex-1">
89+
<FormControl>
90+
<Input
91+
{...traitField}
92+
placeholder="trait_type"
93+
className={cn(
94+
"bg-card",
95+
keyError && "border-destructive",
96+
)}
13397
/>
134-
</InputRightElement>
135-
</InputGroup>
136-
) : (
137-
<InputGroup>
138-
<Input
139-
{...register(
140-
`attributes.${index}.value` as Path<TFieldValues>,
98+
</FormControl>
99+
{keyError && <FormMessage>{keyError}</FormMessage>}
100+
</FormItem>
101+
)}
102+
/>
103+
104+
<FormField
105+
control={control}
106+
name={`attributes.${index}.value` as Path<TFieldValues>}
107+
render={({ field: valueField }) => (
108+
<FormItem className="flex-1">
109+
<FormControl>
110+
{watch(
111+
`attributes.${index}.value` as unknown as WatchObserver<TFieldValues>,
112+
) instanceof File ? (
113+
<div className="relative">
114+
<Input
115+
disabled
116+
value={
117+
watch(
118+
`attributes.${index}.value` as Path<TFieldValues>,
119+
).name
120+
}
121+
className="pr-10 bg-card"
122+
/>
123+
<Button
124+
type="button"
125+
variant="ghost"
126+
size="sm"
127+
className="absolute right-0 top-0 h-full px-3 hover:bg-transparent"
128+
onClick={() =>
129+
setValue(
130+
`attributes.${index}.value` as Path<TFieldValues>,
131+
"" as PathValue<
132+
TFieldValues,
133+
Path<TFieldValues>
134+
>,
135+
)
136+
}
137+
>
138+
<TrashIcon className="size-4 text-muted-foreground hover:text-destructive" />
139+
</Button>
140+
</div>
141+
) : (
142+
<div className="relative">
143+
<Input
144+
{...valueField}
145+
placeholder="value"
146+
className={`pr-10 bg-card ${valueError ? "border-destructive" : ""}`}
147+
/>
148+
</div>
141149
)}
142-
placeholder="value"
143-
/>
144-
<InputRightElement>
145-
<Tooltip label="Upload file" shouldWrapChildren>
146-
<FileInput
147-
client={client}
148-
setValue={(file) => {
149-
setValue(
150-
`attributes.${index}.value` as Path<TFieldValues>,
151-
file as PathValue<
152-
TFieldValues,
153-
Path<TFieldValues>
154-
>,
155-
);
156-
}}
157-
>
158-
<UploadIcon className="size-4 text-muted-foreground" />
159-
</FileInput>
160-
</Tooltip>
161-
</InputRightElement>
162-
</InputGroup>
150+
</FormControl>
151+
{valueError && <FormMessage>{valueError}</FormMessage>}
152+
</FormItem>
163153
)}
164-
<FormErrorMessage>{valueError}</FormErrorMessage>
165-
</FormControl>
166-
</FormControl>
167-
<IconButton
168-
aria-label="remove key value pair"
169-
colorScheme="red"
170-
icon={<XIcon />}
154+
/>
155+
</div>
156+
<Button
157+
type="button"
158+
variant="outline"
159+
size="sm"
160+
className="rounded-full p-0 size-10 bg-card"
171161
onClick={() => remove(index)}
172-
size="xs"
173-
variant="ghost"
174-
/>
162+
>
163+
<XIcon className="size-4" />
164+
</Button>
175165
</div>
176166
);
177167
})}
178168
<div className="flex flex-row gap-2">
179169
<Button
180-
colorScheme="purple"
181-
leftIcon={<PlusIcon className="size-5" />}
170+
className="rounded-full"
182171
onClick={() =>
183172
// biome-ignore lint/suspicious/noExplicitAny: FIXME
184173
append({ trait_type: undefined, value: undefined } as any)
185174
}
186175
size="sm"
187176
>
177+
<PlusIcon className="size-4 mr-2" />
188178
Add Row
189179
</Button>
190180
</div>
191181
</div>
192182
);
193-
};
183+
}

0 commit comments

Comments
 (0)