11"use client" ;
2+
23import { FormFieldSetup } from "@/components/blocks/FormFieldSetup" ;
34import { DatePickerWithRange } from "@/components/ui/DatePickerWithRange" ;
45import {
78 AccordionItem ,
89 AccordionTrigger ,
910} from "@/components/ui/accordion" ;
10- import { Alert , AlertTitle } from "@/components/ui/alert" ;
11+ import { Alert , AlertDescription , AlertTitle } from "@/components/ui/alert" ;
1112import { Button } from "@/components/ui/button" ;
1213import {
1314 Form ,
@@ -18,19 +19,26 @@ import {
1819 FormMessage ,
1920} from "@/components/ui/form" ;
2021import { Input } from "@/components/ui/input" ;
22+ import { Label } from "@/components/ui/label" ;
2123import { Separator } from "@/components/ui/separator" ;
2224import { Skeleton } from "@/components/ui/skeleton" ;
2325import { ToolTipLabel } from "@/components/ui/tooltip" ;
2426import { zodResolver } from "@hookform/resolvers/zod" ;
25- import { useMutation , useQuery } from "@tanstack/react-query" ;
27+ import { useMutation } from "@tanstack/react-query" ;
2628import { TransactionButton } from "components/buttons/TransactionButton" ;
2729import { addDays , fromUnixTime } from "date-fns" ;
2830import { useAllChainsData } from "hooks/chains/allChains" ;
2931import { useTxNotifications } from "hooks/useTxNotifications" ;
3032import { CircleAlertIcon , PlusIcon , Trash2Icon } from "lucide-react" ;
31- import { useCallback } from "react" ;
33+ import {
34+ type Dispatch ,
35+ type SetStateAction ,
36+ useCallback ,
37+ useState ,
38+ } from "react" ;
3239import { useFieldArray , useForm } from "react-hook-form" ;
3340import {
41+ NATIVE_TOKEN_ADDRESS ,
3442 type PreparedTransaction ,
3543 ZERO_ADDRESS ,
3644 getContract ,
@@ -57,22 +65,15 @@ export type ClaimConditionValue = {
5765 auxData : string ;
5866} ;
5967
60- type ClaimCondition = {
61- availableSupply : bigint ;
62- allowlistMerkleRoot : `0x${string } `;
63- pricePerUnit : bigint ;
64- currency : string ;
65- maxMintPerWallet : bigint ;
66- startTimestamp : number ;
67- endTimestamp : number ;
68- auxData : string ;
69- } ;
68+ const positiveIntegerRegex = / ^ [ 0 - 9 ] \d * $ / ;
7069
7170function ClaimableModule ( props : ModuleInstanceProps ) {
7271 const { contract, ownerAccount } = props ;
7372 const account = useActiveAccount ( ) ;
73+ const [ tokenId , setTokenId ] = useState < string > ( "" ) ;
7474
7575 const isErc721 = props . contractInfo . name === "ClaimableERC721" ;
76+ const isValidTokenId = positiveIntegerRegex . test ( tokenId ) ;
7677
7778 const primarySaleRecipientQuery = useReadContract (
7879 isErc721 ? ClaimableERC721 . getSaleConfig : ClaimableERC1155 . getSaleConfig ,
@@ -82,20 +83,27 @@ function ClaimableModule(props: ModuleInstanceProps) {
8283 ) ;
8384
8485 const claimConditionQuery = useReadContract (
85- ClaimableERC721 . getClaimCondition ,
86+ isErc721
87+ ? ClaimableERC721 . getClaimCondition
88+ : ClaimableERC1155 . getClaimCondition ,
8689 {
90+ tokenId : positiveIntegerRegex . test ( tokenId ) ? BigInt ( tokenId ) : 0n ,
8791 contract : contract ,
8892 queryOptions : {
89- enabled : isErc721 ,
93+ enabled : isErc721 || ( ! ! tokenId && isValidTokenId ) ,
9094 } ,
9195 } ,
9296 ) ;
9397
94- const getClaimConditionErc1155 = ( tokenId : string ) =>
95- ClaimableERC1155 . getClaimCondition ( {
96- contract : contract ,
97- tokenId : BigInt ( tokenId ) ,
98- } ) ;
98+ const noClaimConditionSet =
99+ claimConditionQuery . data ?. availableSupply === 0n &&
100+ claimConditionQuery . data ?. allowlistMerkleRoot ===
101+ "0x0000000000000000000000000000000000000000000000000000000000000000" &&
102+ claimConditionQuery . data ?. pricePerUnit === 0n &&
103+ claimConditionQuery . data ?. currency === ZERO_ADDRESS &&
104+ claimConditionQuery . data ?. maxMintPerWallet === 0n &&
105+ claimConditionQuery . data ?. startTimestamp === 0 &&
106+ claimConditionQuery . data ?. endTimestamp === 0 ;
99107
100108 const currencyContract = getContract ( {
101109 address : claimConditionQuery . data ?. currency || "" ,
@@ -104,7 +112,6 @@ function ClaimableModule(props: ModuleInstanceProps) {
104112 } ) ;
105113
106114 const shouldFetchTokenDecimals =
107- isErc721 &&
108115 claimConditionQuery . data &&
109116 claimConditionQuery . data ?. currency !== ZERO_ADDRESS ;
110117
@@ -217,10 +224,8 @@ function ClaimableModule(props: ModuleInstanceProps) {
217224 } }
218225 claimConditionSection = { {
219226 data :
220- // claim condition is common for all tokens
221- isErc721 &&
222- // claim conditions is fetched
223- claimConditionQuery . isFetched &&
227+ // claim conditions data is present
228+ claimConditionQuery . data &&
224229 // token decimals is fetched if it should be fetched
225230 ( shouldFetchTokenDecimals ? tokenDecimalsQuery . isFetched : true )
226231 ? {
@@ -229,11 +234,17 @@ function ClaimableModule(props: ModuleInstanceProps) {
229234 }
230235 : undefined ,
231236 setClaimCondition,
232- getClaimConditionErc1155,
237+ tokenId,
238+ isLoading :
239+ claimConditionQuery . isLoading ||
240+ ( ! ! shouldFetchTokenDecimals && tokenDecimalsQuery . isLoading ) ,
233241 } }
234242 isOwnerAccount = { ! ! ownerAccount }
235243 isErc721 = { isErc721 }
236244 contractChainId = { props . contract . chain . id }
245+ setTokenId = { setTokenId }
246+ isValidTokenId = { isValidTokenId }
247+ noClaimConditionSet = { noClaimConditionSet }
237248 mintSection = { {
238249 mint,
239250 } }
@@ -246,6 +257,9 @@ export function ClaimableModuleUI(
246257 isOwnerAccount : boolean ;
247258 isErc721 : boolean ;
248259 contractChainId : number ;
260+ setTokenId : Dispatch < SetStateAction < string > > ;
261+ isValidTokenId : boolean ;
262+ noClaimConditionSet : boolean ;
249263 primarySaleRecipientSection : {
250264 setPrimarySaleRecipient : (
251265 values : PrimarySaleRecipientFormValues ,
@@ -260,14 +274,15 @@ export function ClaimableModuleUI(
260274 mint : ( values : MintFormValues ) => Promise < void > ;
261275 } ;
262276 claimConditionSection : {
277+ tokenId : string ;
263278 setClaimCondition : ( values : ClaimConditionFormValues ) => Promise < void > ;
264- getClaimConditionErc1155 : ( tokenId : string ) => Promise < ClaimCondition > ;
265279 data :
266280 | {
267- claimCondition : ClaimConditionValue | undefined ;
281+ claimCondition : ClaimConditionValue ;
268282 tokenDecimals : number | undefined ;
269283 }
270284 | undefined ;
285+ isLoading : boolean ;
271286 } ;
272287 } ,
273288) {
@@ -296,25 +311,44 @@ export function ClaimableModuleUI(
296311 Claim Conditions
297312 </ AccordionTrigger >
298313 < AccordionContent className = "px-1" >
299- { ! props . isErc721 || props . claimConditionSection . data ? (
300- < ClaimConditionSection
301- isOwnerAccount = { props . isOwnerAccount }
302- claimCondition = {
303- props . claimConditionSection . data ?. claimCondition
304- }
305- update = { props . claimConditionSection . setClaimCondition }
306- isErc721 = { props . isErc721 }
307- chainId = { props . contractChainId }
308- tokenDecimals = {
309- props . claimConditionSection . data ?. tokenDecimals
310- }
311- getClaimConditionErc1155 = {
312- props . claimConditionSection . getClaimConditionErc1155
313- }
314- />
315- ) : (
316- < Skeleton className = "h-[350px]" />
314+ { ! props . isErc721 && (
315+ < div className = "flex flex-col gap-6" >
316+ < div className = "flex-1 space-y-1" >
317+ < Label > Token ID</ Label >
318+ < p className = "text-muted-foreground text-sm" >
319+ { props . isOwnerAccount
320+ ? "View and Update claim conditions for given token ID"
321+ : "View claim conditions for given token ID" }
322+ </ p >
323+ < Input onChange = { ( e ) => props . setTokenId ( e . target . value ) } />
324+ </ div >
325+ </ div >
317326 ) }
327+
328+ < div className = "h-6" />
329+
330+ { props . isValidTokenId &&
331+ props . claimConditionSection . data &&
332+ ! props . claimConditionSection . isLoading && (
333+ < ClaimConditionSection
334+ isOwnerAccount = { props . isOwnerAccount }
335+ claimCondition = {
336+ props . claimConditionSection . data . claimCondition
337+ }
338+ update = { props . claimConditionSection . setClaimCondition }
339+ isErc721 = { props . isErc721 }
340+ chainId = { props . contractChainId }
341+ noClaimConditionSet = { props . noClaimConditionSet }
342+ tokenDecimals = {
343+ props . claimConditionSection . data ?. tokenDecimals
344+ }
345+ tokenId = { props . claimConditionSection . tokenId }
346+ />
347+ ) }
348+ { props . isValidTokenId &&
349+ props . claimConditionSection . isLoading && (
350+ < Skeleton className = "h-[350px]" />
351+ ) }
318352 </ AccordionContent >
319353 </ AccordionItem >
320354
@@ -383,63 +417,48 @@ const defaultStartDate = addDays(new Date(), 7);
383417const defaultEndDate = addDays ( new Date ( ) , 14 ) ;
384418
385419function ClaimConditionSection ( props : {
386- claimCondition ? : ClaimConditionValue ;
420+ claimCondition : ClaimConditionValue ;
387421 update : ( values : ClaimConditionFormValues ) => Promise < void > ;
388422 isOwnerAccount : boolean ;
389423 isErc721 : boolean ;
390424 chainId : number ;
391425 tokenDecimals ?: number ;
392- getClaimConditionErc1155 : ( tokenId : string ) => Promise < ClaimCondition > ;
426+ tokenId : string ;
427+ noClaimConditionSet : boolean ;
393428} ) {
394429 const { idToChain } = useAllChainsData ( ) ;
395430 const chain = idToChain . get ( props . chainId ) ;
396- const { claimCondition } = props ;
397-
398- const tokenIdForm = useForm < { tokenId : string } > ( {
399- defaultValues : {
400- tokenId : "" ,
401- } ,
402- } ) ;
403-
404- const tokenId = tokenIdForm . watch ( "tokenId" ) ;
405-
406- const claimConditionErc1155Query = useQuery ( {
407- queryKey : [ "claimConditionErc1155" , props . chainId , tokenId ] ,
408- queryFn : ( ) => props . getClaimConditionErc1155 ( tokenId ) ,
409- enabled : ! props . isErc721 && BigInt ( tokenId ) >= 0n ,
410- } ) ;
411-
412- const conditions = props . isErc721
413- ? claimCondition
414- : claimConditionErc1155Query . data ;
431+ const { tokenId, claimCondition } = props ;
432+ const [ addClaimConditionButtonClicked , setAddClaimConditionButtonClicked ] =
433+ useState ( false ) ;
415434
416435 const form = useForm < ClaimConditionFormValues > ( {
417436 resolver : zodResolver ( claimConditionFormSchema ) ,
418437 values : {
419438 tokenId,
420439 currencyAddress :
421- conditions ?. currency === ZERO_ADDRESS ? "" : conditions ?. currency ,
422- pricePerToken :
423- conditions ?. pricePerUnit &&
424- conditions ?. currency !== ZERO_ADDRESS &&
425- props . tokenDecimals
426- ? Number ( toTokens ( conditions ?. pricePerUnit , props . tokenDecimals ) )
427- : 0 ,
440+ claimCondition ?. currency === ZERO_ADDRESS
441+ ? NATIVE_TOKEN_ADDRESS // default to the native token address
442+ : claimCondition ?. currency ,
443+ // default case is zero state, so 0 // 10 ** 18 still results in 0
444+ pricePerToken : Number (
445+ toTokens ( claimCondition ?. pricePerUnit , props . tokenDecimals || 18 ) ,
446+ ) ,
428447 maxClaimableSupply :
429- conditions ?. availableSupply . toString ( ) === "0" ||
430- conditions ?. availableSupply . toString ( ) === MAX_UINT_256
448+ claimCondition ?. availableSupply . toString ( ) === "0" ||
449+ claimCondition ?. availableSupply . toString ( ) === MAX_UINT_256
431450 ? ""
432- : conditions ?. availableSupply . toString ( ) || "" ,
451+ : claimCondition ?. availableSupply . toString ( ) || "" ,
433452 maxClaimablePerWallet :
434- conditions ?. maxMintPerWallet . toString ( ) === "0" ||
435- conditions ?. maxMintPerWallet . toString ( ) === MAX_UINT_256
453+ claimCondition ?. maxMintPerWallet . toString ( ) === "0" ||
454+ claimCondition ?. maxMintPerWallet . toString ( ) === MAX_UINT_256
436455 ? ""
437- : conditions ?. maxMintPerWallet . toString ( ) || "" ,
438- startTime : conditions ?. startTimestamp
439- ? fromUnixTime ( conditions ?. startTimestamp )
456+ : claimCondition ?. maxMintPerWallet . toString ( ) || "" ,
457+ startTime : claimCondition ?. startTimestamp
458+ ? fromUnixTime ( claimCondition ?. startTimestamp )
440459 : defaultStartDate ,
441- endTime : conditions ?. endTimestamp
442- ? fromUnixTime ( conditions ?. endTimestamp )
460+ endTime : claimCondition ?. endTimestamp
461+ ? fromUnixTime ( claimCondition ?. endTimestamp )
443462 : defaultEndDate ,
444463 allowList : [ ] ,
445464 } ,
@@ -474,39 +493,30 @@ function ClaimConditionSection(props: {
474493
475494 return (
476495 < div className = "flex flex-col gap-6" >
477- { ! props . isErc721 && (
478- < Form { ...tokenIdForm } >
479- < form >
480- < FormField
481- control = { tokenIdForm . control }
482- name = "tokenId"
483- render = { ( { field } ) => (
484- < FormItem className = "flex-1" >
485- < FormLabel > Token ID</ FormLabel >
486- < FormControl >
487- < Input { ...field } disabled = { ! props . isOwnerAccount } />
488- </ FormControl >
489- < FormMessage />
490- </ FormItem >
491- ) }
492- />
493- </ form >
494- </ Form >
496+ { props . noClaimConditionSet && ! addClaimConditionButtonClicked && (
497+ < >
498+ < Alert variant = "warning" >
499+ < CircleAlertIcon className = "size-5 max-sm:hidden" />
500+ < AlertTitle > No Claim Condition Set</ AlertTitle >
501+ < AlertDescription >
502+ You have not set a claim condition for this token. You can set a
503+ claim condition by clicking the "Set Claim Condition" button.
504+ </ AlertDescription >
505+ </ Alert >
506+
507+ < Button
508+ onClick = { ( ) => setAddClaimConditionButtonClicked ( true ) }
509+ variant = "outline"
510+ className = "w-full"
511+ >
512+ Add Claim Condition
513+ </ Button >
514+ </ >
495515 ) }
496516
497- < Form { ...form } >
498- < form onSubmit = { form . handleSubmit ( onSubmit ) } >
499- { ! props . isErc721 &&
500- tokenId !== "" &&
501- BigInt ( tokenId ) >= 0n &&
502- claimConditionErc1155Query . isPending && (
503- < Skeleton className = "h-[350px]" />
504- ) }
505-
506- { ( props . isErc721 ||
507- ( tokenId !== "" &&
508- BigInt ( tokenId ) >= 0n &&
509- ! claimConditionErc1155Query . isPending ) ) && (
517+ { ( ! props . noClaimConditionSet || addClaimConditionButtonClicked ) && (
518+ < Form { ...form } >
519+ < form onSubmit = { form . handleSubmit ( onSubmit ) } >
510520 < div className = "flex flex-col gap-6" >
511521 < div className = "grid grid-cols-1 gap-4 md:grid-cols-2" >
512522 < FormField
@@ -668,9 +678,9 @@ function ClaimConditionSection(props: {
668678 </ TransactionButton >
669679 </ div >
670680 </ div >
671- ) }
672- </ form > { " " }
673- </ Form >
681+ </ form > { " " }
682+ </ Form >
683+ ) }
674684 </ div >
675685 ) ;
676686}
0 commit comments