Skip to content

Commit c1348e2

Browse files
committed
Merge remote-tracking branch 'origin/main' into ph/feat-webhooks
2 parents f054236 + 2111b8b commit c1348e2

File tree

47 files changed

+494
-236
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+494
-236
lines changed

apps/dashboard/src/@/analytics/report.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -362,7 +362,11 @@ export function reportAssetCreationFailed(
362362
properties: { contractType: AssetContractType; error: string } & (
363363
| {
364364
assetType: "nft";
365-
step: "deploy-contract" | "mint-nfts" | "set-claim-conditions";
365+
step:
366+
| "deploy-contract"
367+
| "mint-nfts"
368+
| "set-claim-conditions"
369+
| "set-admins";
366370
}
367371
| {
368372
assetType: "coin";

apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/analytics/page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -367,7 +367,7 @@ function AppHighlightsCard({
367367
color: "hsl(var(--chart-2))",
368368
emptyContent: (
369369
<EmptyStateContent
370-
description="Onramp, swap, and bridge with thirdweb's Universal Bridge."
370+
description="Onramp, swap, and bridge with thirdweb's Payments."
371371
link="https://portal.thirdweb.com/connect/pay/overview"
372372
metric="Payments"
373373
/>

apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/components/ProjectFTUX/ProjectFTUX.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -257,7 +257,7 @@ function ProductsSection(props: { teamSlug: string; projectSlug: string }) {
257257
"Bridge, swap, and purchase cryptocurrencies with any fiat options or tokens via cross-chain routing",
258258
href: `/team/${props.teamSlug}/${props.projectSlug}/universal-bridge`,
259259
icon: PayIcon,
260-
title: "Universal Bridge",
260+
title: "Payments",
261261
},
262262
];
263263

apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/components/ProjectSidebarLayout.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ export function ProjectSidebarLayout(props: {
7575
{
7676
href: `${layoutPath}/universal-bridge`,
7777
icon: PayIcon,
78-
label: "Universal Bridge",
78+
label: "Payments",
7979
},
8080
{
8181
href: `${layoutPath}/tokens`,

apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -435,7 +435,7 @@ function AppHighlightsCard({
435435
color: "hsl(var(--chart-2))",
436436
emptyContent: (
437437
<EmptyStateContent
438-
description="Onramp, swap, and bridge with thirdweb's Universal Bridge."
438+
description="Onramp, swap, and bridge with thirdweb's Payments."
439439
link="https://portal.thirdweb.com/connect/pay/overview"
440440
metric="Payments"
441441
/>

apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/tokens/create/_common/SocialUrls.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ export function SocialUrlsFieldset<T extends WithSocialUrls>(props: {
3434
<h2 className="mb-2 font-medium text-sm">Social URLs</h2>
3535

3636
{fields.length > 0 && (
37-
<div className="mb-5 space-y-4">
37+
<div className="mb-4 space-y-3">
3838
{fields.map((field, index) => (
3939
<div
4040
className="flex gap-3 max-sm:mb-6 max-sm:border-b max-sm:border-dashed max-sm:pb-6"
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
"use client";
2+
3+
import { PlusIcon, Trash2Icon } from "lucide-react";
4+
import { type UseFormReturn, useFieldArray } from "react-hook-form";
5+
import { useActiveAccount } from "thirdweb/react";
6+
import { Button } from "@/components/ui/button";
7+
import {
8+
FormControl,
9+
FormField,
10+
FormItem,
11+
FormMessage,
12+
} from "@/components/ui/form";
13+
import { Input } from "@/components/ui/input";
14+
15+
type WithAdmins = {
16+
admins: {
17+
address: string;
18+
}[];
19+
};
20+
21+
export function AdminAddressesFieldset<T extends WithAdmins>(props: {
22+
form: UseFormReturn<T>;
23+
}) {
24+
// T contains all properties of WithAdmins, so this is ok
25+
const form = props.form as unknown as UseFormReturn<WithAdmins>;
26+
const account = useActiveAccount();
27+
28+
const { fields, append, remove } = useFieldArray({
29+
control: form.control,
30+
name: "admins",
31+
});
32+
33+
const handleAddAddress = () => {
34+
append({ address: "" });
35+
};
36+
37+
const handleRemoveAddress = (index: number) => {
38+
const field = fields[index];
39+
if (field?.address === account?.address) {
40+
return; // Don't allow removing the connected address
41+
}
42+
remove(index);
43+
};
44+
45+
return (
46+
<div className="border-t border-dashed px-4 py-6 lg:px-6">
47+
<div className="mb-3">
48+
<h2 className="mb-1 font-medium text-sm">Admins</h2>
49+
<p className="text-sm text-muted-foreground">
50+
These wallets will have authority on the token
51+
</p>
52+
</div>
53+
54+
{fields.length > 0 && (
55+
<div className="mb-4 space-y-3">
56+
{fields.map((field, index) => (
57+
<div
58+
className="flex gap-3 max-sm:mb-6 max-sm:border-b max-sm:border-dashed max-sm:pb-6"
59+
key={field.id}
60+
>
61+
<div className="flex flex-1 flex-col gap-3 lg:flex-row">
62+
<FormField
63+
control={form.control}
64+
name={`admins.${index}.address`}
65+
render={({ field }) => (
66+
<FormItem className="flex-1">
67+
<FormControl>
68+
<Input
69+
{...field}
70+
aria-label="Admin Address"
71+
disabled={field.value === account?.address}
72+
placeholder="0x..."
73+
/>
74+
</FormControl>
75+
<FormMessage />
76+
</FormItem>
77+
)}
78+
/>
79+
</div>
80+
81+
<Button
82+
className="rounded-full"
83+
disabled={field.address === account?.address}
84+
onClick={() => handleRemoveAddress(index)}
85+
size="icon"
86+
type="button"
87+
variant="outline"
88+
>
89+
<Trash2Icon className="h-4 w-4" />
90+
<span className="sr-only">Remove</span>
91+
</Button>
92+
</div>
93+
))}
94+
</div>
95+
)}
96+
97+
<Button
98+
className="h-auto gap-1.5 rounded-full px-3 py-1.5 text-xs"
99+
onClick={handleAddAddress}
100+
size="sm"
101+
type="button"
102+
variant="outline"
103+
>
104+
<PlusIcon className="size-3.5" />
105+
Add Admin
106+
</Button>
107+
108+
{form.watch("admins").length === 0 && (
109+
<p className="text-sm text-destructive mt-2">
110+
At least one admin address is required
111+
</p>
112+
)}
113+
</div>
114+
);
115+
}

apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/tokens/create/_common/schema.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,22 @@ export const socialUrlsSchema = z.array(
2222
}),
2323
);
2424

25+
export const addressArraySchema = z.array(
26+
z.object({
27+
address: z.string().refine(
28+
(value) => {
29+
if (isAddress(value)) {
30+
return true;
31+
}
32+
return false;
33+
},
34+
{
35+
message: "Invalid address",
36+
},
37+
),
38+
}),
39+
);
40+
2541
export const addressSchema = z.string().refine(
2642
(value) => {
2743
if (isAddress(value)) {

apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/tokens/create/nft/_common/form.ts

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,15 @@
1-
import { isAddress } from "thirdweb";
21
import * as z from "zod";
3-
import { socialUrlsSchema } from "../../_common/schema";
2+
import {
3+
addressArraySchema,
4+
addressSchema,
5+
socialUrlsSchema,
6+
} from "../../_common/schema";
47
import type { NFTMetadataWithPrice } from "../upload-nfts/batch-upload/process-files";
58

69
export const nftCollectionInfoFormSchema = z.object({
10+
admins: addressArraySchema.refine((addresses) => addresses.length > 0, {
11+
message: "At least one admin is required",
12+
}),
713
chain: z.string().min(1, "Chain is required"),
814
description: z.string().optional(),
915
image: z.instanceof(File).optional(),
@@ -12,14 +18,6 @@ export const nftCollectionInfoFormSchema = z.object({
1218
symbol: z.string(),
1319
});
1420

15-
const addressSchema = z.string().refine((value) => {
16-
if (isAddress(value)) {
17-
return true;
18-
}
19-
20-
return false;
21-
});
22-
2321
export const nftSalesSettingsFormSchema = z.object({
2422
primarySaleRecipient: addressSchema,
2523
royaltyBps: z.coerce.number().min(0).max(10000),
@@ -37,6 +35,14 @@ export type CreateNFTCollectionAllValues = {
3735
};
3836

3937
export type CreateNFTCollectionFunctions = {
38+
setAdmins: (values: {
39+
contractAddress: string;
40+
contractType: "DropERC721" | "DropERC1155";
41+
admins: {
42+
address: string;
43+
}[];
44+
chain: string;
45+
}) => Promise<void>;
4046
erc721: {
4147
deployContract: (values: CreateNFTCollectionAllValues) => Promise<{
4248
contractAddress: string;

apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/tokens/create/nft/collection-info/nft-collection-info-fieldset.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { SingleNetworkSelector } from "@/components/blocks/NetworkSelectors";
1010
import { Form } from "@/components/ui/form";
1111
import { Input } from "@/components/ui/input";
1212
import { Textarea } from "@/components/ui/textarea";
13+
import { AdminAddressesFieldset } from "../../_common/admin-addresses-fieldset";
1314
import { SocialUrlsFieldset } from "../../_common/SocialUrls";
1415
import { StepCard } from "../../_common/step-card";
1516
import type { NFTCollectionInfoFormValues } from "../_common/form";
@@ -126,6 +127,8 @@ export function NFTCollectionInfoFieldset(props: {
126127
</div>
127128

128129
<SocialUrlsFieldset form={form} />
130+
131+
<AdminAddressesFieldset form={form} />
129132
</StepCard>
130133
</form>
131134
</Form>

0 commit comments

Comments
 (0)