Skip to content

Commit 17a9fb9

Browse files
committed
WIP: claimable configuration UI
1 parent bea430c commit 17a9fb9

File tree

4 files changed

+468
-89
lines changed

4 files changed

+468
-89
lines changed

apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/modules/components/Mintable.tsx

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ import { CircleAlertIcon } from "lucide-react";
2929
import { useCallback } from "react";
3030
import { useForm } from "react-hook-form";
3131
import { toast } from "sonner";
32-
import { PreparedTransaction, sendAndConfirmTransaction } from "thirdweb";
32+
import { type PreparedTransaction, sendAndConfirmTransaction } from "thirdweb";
3333
import { isAddress } from "thirdweb";
3434
import { MintableERC721, MintableERC1155 } from "thirdweb/modules";
3535
import { useReadContract } from "thirdweb/react";
@@ -47,7 +47,7 @@ export type UpdateFormValues = {
4747

4848
// TODO - add form validation with zod schema for mint form
4949

50-
export type MintFormValues = NFTMetadat||aInputLimited & {
50+
export type MintFormValues = NFTMetadataInputLimited & {
5151
useNextTokenId: boolean;
5252
recipient: string;
5353
amount: number;
@@ -81,22 +81,22 @@ function MintableModule(props: ModuleInstanceProps) {
8181
if (!ownerAccount) {
8282
throw new Error("Not an owner account");
8383
}
84-
84+
8585
let mintTx: PreparedTransaction;
8686
if (isErc721) {
87-
mintTx = MintableERC721.mintWithRole({
88-
contract,
89-
to: values.recipient,
90-
nfts: [nft],
91-
})
87+
mintTx = MintableERC721.mintWithRole({
88+
contract,
89+
to: values.recipient,
90+
nfts: [nft],
91+
});
9292
} else if (values.useNextTokenId || values.tokenId) {
9393
mintTx = MintableERC1155.mintWithRole({
94-
contract,
95-
to: values.recipient,
96-
amount: BigInt(values.amount),
97-
tokenId: values.useNextTokenId ? undefined : BigInt(values.tokenId),
98-
nft,
99-
});
94+
contract,
95+
to: values.recipient,
96+
amount: BigInt(values.amount),
97+
tokenId: values.useNextTokenId ? undefined : BigInt(values.tokenId),
98+
nft,
99+
});
100100
} else {
101101
throw new Error("Invalid token ID");
102102
}
Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
import { ChakraProviderSetup } from "@/components/ChakraProviderSetup";
2+
import { Checkbox } from "@/components/ui/checkbox";
3+
import type { Meta, StoryObj } from "@storybook/react";
4+
import { useMutation } from "@tanstack/react-query";
5+
import { useState } from "react";
6+
import { Toaster, toast } from "sonner";
7+
import { BadgeContainer, mobileViewport } from "stories/utils";
8+
import {
9+
ClaimableModuleUI,
10+
type ConfigFormValues,
11+
type MintFormValues,
12+
} from "./Claimable";
13+
14+
const meta = {
15+
title: "Modules/Claimable",
16+
component: Component,
17+
parameters: {
18+
layout: "centered",
19+
},
20+
} satisfies Meta<typeof Component>;
21+
22+
export default meta;
23+
type Story = StoryObj<typeof meta>;
24+
25+
export const Desktop: Story = {
26+
args: {},
27+
};
28+
29+
export const Mobile: Story = {
30+
args: {},
31+
parameters: {
32+
viewport: mobileViewport("iphone14"),
33+
},
34+
};
35+
36+
const testAddress1 = "0x1F846F6DAE38E1C88D71EAA191760B15f38B7A37";
37+
38+
function Component() {
39+
const [isOwner, setIsOwner] = useState(true);
40+
const [isErc721, setIsErc721] = useState(false);
41+
const [isSequentialTokenIdInstalled, setIsSequentialTokenIdInstalled] =
42+
useState(false);
43+
async function updateStub(values: ConfigFormValues) {
44+
console.log("submitting", values);
45+
await new Promise((resolve) => setTimeout(resolve, 1000));
46+
}
47+
48+
async function mintStub(values: MintFormValues) {
49+
console.log("submitting", values);
50+
await new Promise((resolve) => setTimeout(resolve, 1000));
51+
}
52+
53+
const removeMutation = useMutation({
54+
mutationFn: async () => {
55+
await new Promise((resolve) => setTimeout(resolve, 1000));
56+
},
57+
onSuccess() {
58+
toast.success("Module removed successfully");
59+
},
60+
});
61+
62+
const contractInfo = {
63+
name: "Module Name",
64+
description:
65+
"lorem ipsum dolor sit amet consectetur adipisicing elit sed do eiusmod tempor incididunt ut labore ",
66+
publisher: "0xdd99b75f095d0c4d5112aCe938e4e6ed962fb024",
67+
version: "1.0.0",
68+
};
69+
70+
const claimCondition = {
71+
availableSupply: BigInt(100),
72+
maxMintPerWallet: BigInt(10),
73+
pricePerUnit: BigInt(10),
74+
currency: "0x0000000000000000000000000000000000000000",
75+
startTimestamp: 1689092800,
76+
endTimestamp: 1689092800,
77+
allowlistMerkleRoot:
78+
"0x0000000000000000000000000000000000000000000000000000000000000000" as `0x${string}`,
79+
auxData: "0x",
80+
};
81+
82+
// TODO - remove ChakraProviderSetup after converting the chakra components used in ClaimableModuleUI
83+
return (
84+
<ChakraProviderSetup>
85+
<div className="container flex max-w-[1150px] flex-col gap-10 py-10">
86+
<div className="flex items-center gap-5">
87+
<CheckboxWithLabel
88+
value={isOwner}
89+
onChange={setIsOwner}
90+
id="isOwner"
91+
label="Is Owner"
92+
/>
93+
94+
<CheckboxWithLabel
95+
value={isErc721}
96+
onChange={setIsErc721}
97+
id="isErc721"
98+
label="isErc721"
99+
/>
100+
101+
<CheckboxWithLabel
102+
value={isSequentialTokenIdInstalled}
103+
onChange={setIsSequentialTokenIdInstalled}
104+
id="isSequentialTokenIdInstalled"
105+
label="isSequentialTokenIdInstalled"
106+
/>
107+
</div>
108+
109+
<BadgeContainer label="Empty Primary Sale Recipient">
110+
<ClaimableModuleUI
111+
contractInfo={contractInfo}
112+
moduleAddress="0x0000000000000000000000000000000000000000"
113+
isPending={false}
114+
primarySaleRecipient={""}
115+
update={updateStub}
116+
mint={mintStub}
117+
uninstallButton={{
118+
onClick: async () => removeMutation.mutateAsync(),
119+
isPending: removeMutation.isPending,
120+
}}
121+
isOwnerAccount={isOwner}
122+
isErc721={isErc721}
123+
claimCondition={claimCondition}
124+
isSequentialTokenIdInstalled={isSequentialTokenIdInstalled}
125+
/>
126+
</BadgeContainer>
127+
128+
<BadgeContainer label="Filled Primary Sale Recipient">
129+
<ClaimableModuleUI
130+
contractInfo={contractInfo}
131+
moduleAddress="0x0000000000000000000000000000000000000000"
132+
isPending={false}
133+
primarySaleRecipient={testAddress1}
134+
update={updateStub}
135+
mint={mintStub}
136+
uninstallButton={{
137+
onClick: () => removeMutation.mutateAsync(),
138+
isPending: removeMutation.isPending,
139+
}}
140+
isOwnerAccount={isOwner}
141+
isErc721={isErc721}
142+
claimCondition={claimCondition}
143+
isSequentialTokenIdInstalled={isSequentialTokenIdInstalled}
144+
/>
145+
</BadgeContainer>
146+
147+
<BadgeContainer label="Pending">
148+
<ClaimableModuleUI
149+
contractInfo={contractInfo}
150+
moduleAddress="0x0000000000000000000000000000000000000000"
151+
isPending={true}
152+
primarySaleRecipient={testAddress1}
153+
update={updateStub}
154+
mint={mintStub}
155+
uninstallButton={{
156+
onClick: () => removeMutation.mutateAsync(),
157+
isPending: removeMutation.isPending,
158+
}}
159+
isOwnerAccount={isOwner}
160+
isErc721={isErc721}
161+
claimCondition={claimCondition}
162+
isSequentialTokenIdInstalled={isSequentialTokenIdInstalled}
163+
/>
164+
</BadgeContainer>
165+
166+
<Toaster richColors />
167+
</div>
168+
</ChakraProviderSetup>
169+
);
170+
}
171+
172+
function CheckboxWithLabel(props: {
173+
value: boolean;
174+
onChange: (value: boolean) => void;
175+
id: string;
176+
label: string;
177+
}) {
178+
return (
179+
<div className="items-top flex space-x-2">
180+
<Checkbox
181+
id={props.id}
182+
checked={props.value}
183+
onCheckedChange={(v) => props.onChange(!!v)}
184+
/>
185+
<div className="grid gap-1.5 leading-none">
186+
<label
187+
htmlFor={props.id}
188+
className="font-medium text-sm leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
189+
>
190+
{props.label}
191+
</label>
192+
</div>
193+
</div>
194+
);
195+
}

0 commit comments

Comments
 (0)