Skip to content

Commit

Permalink
chore: fix issue with collection creation
Browse files Browse the repository at this point in the history
  • Loading branch information
Joshua-Ogbonna committed Jan 12, 2025
1 parent b37bd38 commit 9127af4
Show file tree
Hide file tree
Showing 3 changed files with 138 additions and 98 deletions.
43 changes: 30 additions & 13 deletions src/app/create-rare-item/page.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
"use client";
import React, { useState, useRef, ChangeEvent } from "react";
import React, { useState, useRef, ChangeEvent, useEffect } from "react";
import {
Button,
Checkbox,
Expand Down Expand Up @@ -45,16 +45,18 @@ export default function CreateRareItem() {
defaultValues: {
name: "",
category: [],
description: "",
price: "",
royality: "",
product_info: "",
image: "",
brand_name: "",
tags: [],
},
});

useEffect(() => {
const subscription = form.watch((value, { name, type }) =>
console.log("Form updated:", { field: name, type, value, errors: form.formState.errors })
);
return () => subscription.unsubscribe();
}, [form]);

const uploadFile = async (fileToUpload: File) => {
try {
setUploading(true);
Expand Down Expand Up @@ -97,32 +99,41 @@ export default function CreateRareItem() {
};

async function onSubmit(values: z.infer<typeof baseFormSchema>) {
console.log("Form values:", values);
console.log("Form errors:", form.formState.errors);
console.log("CIDs:", cids);

if (cids.length === 0) {
setImageError(true);
toast.error("Please upload at least one image");
return;
}

try {
const brand_name = localStorage.getItem("brand_name");
const phygitalData: PhygitalData = {

if (!brand_name) {
toast.error("Brand name not found. Please create a brand first.");
return;
}

const phygitalData: Partial<PhygitalData> = {
type: "rare",
name: values.name,
brand_name: brand_name || "",
brand_name: brand_name,
category: values.category,
description: values.description,
price: values.price,
royality: values.royality,
product_info: values.product_info,
images: cids.map((cid) => "ipfs://" + cid),
tags: tags,
};

console.log("Saving phygital data:", phygitalData);
localStorage.setItem("phygitalData", JSON.stringify(phygitalData));
setLoading(true);
router.push("/create-phygital-detail");
} catch (error) {
console.error("Error:", error);
toast.error("Failed to save data");
toast.error("Failed to save data: " + (error as Error).message);
} finally {
setLoading(false);
}
Expand All @@ -140,7 +151,12 @@ export default function CreateRareItem() {
</div>

<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)}>
<form
onSubmit={form.handleSubmit(onSubmit, (errors) => {
console.log("Form validation failed:", errors);
toast.error("Please fill in all required fields");
})}
>
<div className="py-4 px-32 flex flex-col gap-12">
<FormField
name="name"
Expand Down Expand Up @@ -318,6 +334,7 @@ export default function CreateRareItem() {
<Button
type="submit"
className="w-fit bg-[#30D8FF] rounded-full hover:text-white text-black"
disabled={loading || form.formState.isSubmitting}
>
{loading ? "Loading..." : "Next"}
</Button>
Expand Down
155 changes: 94 additions & 61 deletions src/components/BrandForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,18 @@ import { useRouter } from "next/navigation";
import { useAccount } from "wagmi";
import axios from "axios";
import { v4 as uuidv4 } from "uuid";
import { clusterApiUrl, Connection, PublicKey, LAMPORTS_PER_SOL, Keypair } from "@solana/web3.js";
import {
clusterApiUrl,
Connection,
PublicKey,
LAMPORTS_PER_SOL,
Keypair,
SystemProgram,
Transaction,
sendAndConfirmTransaction,
} from "@solana/web3.js";
import { useWallet } from "@solana/wallet-adapter-react";
import { Metaplex, walletAdapterIdentity } from "@metaplex-foundation/js";
import { Metaplex, walletAdapterIdentity, toBigNumber } from "@metaplex-foundation/js";
import { WalletMultiButton } from "@solana/wallet-adapter-react-ui";

interface BrandFormProps {
Expand Down Expand Up @@ -245,71 +254,86 @@ export default function CreateBrand({

const deployContract = async () => {
if (!publicKey || !metaplex) {
toast.error("Please connect your wallet first");
return;
toast.error("Please connect your wallet first");
return;
}

try {
// Check SOL balance
const balance = await connection.getBalance(publicKey);
if (balance < LAMPORTS_PER_SOL * 0.05) {
toast.error("Insufficient SOL balance. Need at least 0.05 SOL");
return;
}

// Generate a new keypair for the mint
const mintKeypair = Keypair.generate();
// Check SOL balance
const balance = await connection.getBalance(publicKey);
const MINIMUM_BALANCE = LAMPORTS_PER_SOL * 0.05; // 0.05 SOL
if (balance < MINIMUM_BALANCE) {
toast.error("Insufficient SOL balance. Need at least 0.05 SOL");
return;
}

// Create metadata
const metadata = {
name: form.getValues("name"),
symbol: "BRAND",
description: form.getValues("description"),
image: `ipfs://${cid}`,
external_url: getWebsiteUrl(),
attributes: [
{
trait_type: "Brand Representative",
value: form.getValues("representative"),
},
{
trait_type: "Region",
value: elevateRegion || "Global",
},
],
};
// Create metadata
const metadata = {
name: form.getValues("name"),
symbol: "BRAND",
description: form.getValues("description"),
image: `ipfs://${cid}`,
external_url: getWebsiteUrl(),
attributes: [
{
trait_type: "Brand Representative",
value: form.getValues("representative"),
},
{
trait_type: "Region",
value: elevateRegion || "Global",
},
],
};

// Upload to IPFS
const { IpfsHash } = await uploadToIPFS(metadata);
const metadataUri = `ipfs://${IpfsHash}`;
// Upload to IPFS
const { IpfsHash } = await uploadToIPFS(metadata);
const metadataUri = `ipfs://${IpfsHash}`;

// Create Brand Collection
const { nft: collectionNft } = await metaplex.nfts().create({
// Create Brand Collection with retries
let retries = 3;
let lastError;

while (retries > 0) {
try {
const { nft: collectionNft } = await metaplex.nfts().create({
uri: metadataUri,
name: form.getValues("name"),
sellerFeeBasisPoints: 500, // 5% royalty
symbol: "BRAND",
isCollection: true,
useNewMint: mintKeypair,
creators: [
{
address: publicKey,
share: 100,
authority: metaplex.identity(),
}
{
address: publicKey,
share: 100,
authority: metaplex.identity(),
},
],
isMutable: true,
});
maxSupply: toBigNumber(0),
});

// Store collection address for future reference
setIsDeployed(true);
return collectionNft.address.toString();
// Store collection address for future reference
setIsDeployed(true);
return collectionNft.address.toString();
} catch (error) {
lastError = error;
retries--;
if (retries > 0) {
console.warn(`NFT creation failed, retrying... (${retries} attempts left)`);
await new Promise(resolve => setTimeout(resolve, 1000)); // Wait 1 second before retry
}
}
}

// If we get here, all retries failed
throw lastError;
} catch (error) {
console.error("Deployment error:", error);
toast.error(getErrorMessage(error));
throw error;
console.error("Deployment error:", error);
toast.error(getErrorMessage(error));
throw error;
}
};
};

const deployTradehubContract = async (
platformFee: number,
Expand Down Expand Up @@ -471,12 +495,15 @@ export default function CreateBrand({
}

try {
const socialLinksObject = socialLinks.reduce((acc, link) => {
if (link.platform && link.url) {
acc[link.platform] = link.url;
}
return acc;
}, {} as Record<string, string>);
const socialLinksObject = socialLinks.reduce(
(acc, link) => {
if (link.platform && link.url) {
acc[link.platform] = link.url;
}
return acc;
},
{} as Record<string, string>
);

if (cid) {
values.logo_image = "ipfs://" + cid;
Expand Down Expand Up @@ -532,7 +559,10 @@ export default function CreateBrand({
toast.warning("Deploying brand collection...");

const collectionAddress = await deployContract();
localStorage.setItem("AccessMasterAddress", collectionAddress as string);
localStorage.setItem(
"AccessMasterAddress",
collectionAddress as string
);
console.log("Brand collection deployed at:", collectionAddress);

toast.warning("Deploying trading collection...");
Expand Down Expand Up @@ -585,7 +615,10 @@ export default function CreateBrand({

const brand = await response.json();
localStorage.setItem("BrandId", brand.id);
localStorage.setItem('webxr-experience-with-ai-avatar', values.webxr_experience_with_ai_avatar.toString())
localStorage.setItem(
"webxr-experience-with-ai-avatar",
values.webxr_experience_with_ai_avatar.toString()
);

const users = await fetch(`${apiUrl}/users`, {
method: "POST",
Expand Down Expand Up @@ -1173,8 +1206,8 @@ export default function CreateBrand({
{loading
? "Processing..."
: isEdit
? "Update brand"
: "Continue"}
? "Update brand"
: "Continue"}
</Button>
</div>
</form>
Expand All @@ -1183,4 +1216,4 @@ export default function CreateBrand({
</main>
</>
);
}
}
38 changes: 14 additions & 24 deletions src/utils/phygitals.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { z } from "zod";

// Base schema for both phygital and rare items
// Initial form schema for rare items (first page)
export const baseFormSchema = z.object({
name: z
.string()
Expand All @@ -10,7 +10,15 @@ export const baseFormSchema = z.object({
.regex(/^[a-zA-Z0-9\s]*$/, {
message: "Name must only contain letters and numbers",
}),
category: z.array(z.string()) || z.string(),
category: z.array(z.string()),
price: z.string().min(1, { message: "Price must be provided" }),
image: z.string().optional(),
brand_name: z.string().optional(),
tags: z.array(z.string()).optional(),
});

// Details form schema (second page)
export const detailsFormSchema = z.object({
description: z
.string()
.min(2, {
Expand All @@ -19,17 +27,7 @@ export const baseFormSchema = z.object({
.max(1000, {
message: "Description should be less than 1000 words",
}),
price: z.string().min(1, { message: "Price must be provided" }),
royality: z.string(),
product_info: z.string().min(2, {
message: "Product Information must be at least 2 characters",
}),
image: z.string(),
brand_name: z.string(),
tags: z.array(z.string()).optional(),
quantity: z.number(),
color: z.string().min(1, "Color is required"),
size_option: z.number(),
size_details: z
.array(
z.object({
Expand All @@ -39,24 +37,16 @@ export const baseFormSchema = z.object({
})
)
.optional(),
});

// Details form schema
export const detailsFormSchema = z.object({
color: z.string().min(1, "Color is required"),
size_details: z.array(
z.object({
size: z.string(),
quantity: z.number(),
additional_details: z.string().optional(),
})
),
weight: z.string().min(1, "Weight is required"),
material: z.string().min(1, "Material is required"),
usage: z.string().optional(),
care_instructions: z.string().optional(),
manufacturer: z.string().min(1, "Manufacturer is required"),
origin_country: z.string().min(1, "Country of origin is required"),
product_info: z.string(),
royality: z.string(),
quantity: z.number(),
size_option: z.number(),
});

export const categories = [
Expand Down

0 comments on commit 9127af4

Please sign in to comment.