RB
Rrahim Bajraktari

Mock Products - Copy this React, Tailwind Component to your project

I want you to base on this create product page and create an edit product page because they have the same field only other tasks. import React, { useState, useEffect } from "react"; import { useDropzone } from "react dropzone"; import { useFormik } from "formik"; import * as Yup from "yup"; import { useDispatch } from "react redux"; import { ref, uploadBytes, getDownloadURL } from "firebase/storage"; import { ChevronDownIcon } from "@heroicons/react/24/outline"; import { storage } from "../../../config/firebaseConfig"; import { addProduct } from "../../../State/Product/productSlice"; const navigation = { categories: [ { id: "products", name: "Shop By", sections: [ { id: "collections", name: "Collections", items: [ { name: "Bridal", href: "bridal" }, { name: "Winter", href: "winter" }, ], }, { id: "category", name: "Categories", items: [ { name: "Evening Dresses", href: "evening dresses" }, { name: "Cocktail & Party Dresses", href: "party dresses" }, { name: "Prom Dresses", href: "prom dresses" }, { name: "Homecoming Gowns", href: "homecoming gowns" }, ], }, ], }, ], }; const CreateProductsPage = () => { const [selectedTab, setSelectedTab] = useState("productInfo"); const [productState, setProductState] = useState({ selectedColor: {}, colors: {}, currentImages: [], }); const [selectedCategory, setSelectedCategory] = useState(null); const [expandedSectionId, setExpandedSectionId] = useState(null); const dispatch = useDispatch(); useEffect(() => { return () => { productState.currentImages.forEach((file) => URL.revokeObjectURL(file.preview) ); }; }, [productState.currentImages, productState.selectedColor]); const { getRootProps } = useDropzone({ accept: "image/*", onDrop: (acceptedFiles) => { setProductState((prevState) => ({ ...prevState, currentImages: acceptedFiles.map((file) => Object.assign(file, { preview: URL.createObjectURL(file), }) ), })); }, }); const handleAddColor = async () => { const { selectedColor, currentImages } = productState; if (!selectedColor || currentImages.length === 0) return; try { const uploadTasks = currentImages.map((file) => { const storageRef = ref( storage, `products/${selectedColor.name.replace(/\s+/g, " ").toLowerCase()}/${file.name}` ); return uploadBytes(storageRef, file).then(() => getDownloadURL(storageRef) ); }); const urls = await Promise.all(uploadTasks); setProductState((prevState) => ({ ...prevState, colors: { ...prevState.colors, [selectedColor.name]: { images: urls.map((url) => ({ imageUrl: url })), colorAdded: true, }, }, currentImages: [], selectedColor: {}, })); } catch (error) { console.error("Error uploading color images:", error); } }; const formik = useFormik({ initialValues: { title: "", description: "", price: 0.0, category: "", }, validationSchema: Yup.object({ title: Yup.string().required("Title is required"), description: Yup.string().required("Description is required"), price: Yup.number() .required("Price is required") .min(0.01, "Price must be greater than 0"), category: Yup.string().required("Category is required"), }), onSubmit: async (values) => { const productData = { title: values.title, price: values.price, description: values.description, colors: Object.keys(productState.colors).map((color) => ({ name: color, images: productState.colors[color].images, })), categoryName: selectedCategory, }; dispatch(addProduct(productData)); }, }); const handleColorChange = (color) => { setProductState((prev) => ({ ...prev, selectedColor: prev.colors[color.name] || color, })); }; const handleRemoveImage = (file) => { setProductState((prevState) => ({ ...prevState, currentImages: prevState.currentImages.filter( (img) => img.name !== file.name ), })); }; const { values, errors, touched, handleChange, handleBlur } = formik; return ( <div className="max w 4xl mx auto py 12 sm:px 4"> <h1 className="text 3xl font semibold text center text gray 800 mb 6"> Add New Product </h1> {/* Tab Navigation */} <div className="flex space x 4 mb 8"> <button className={`px 4 py 2 rounded md ${ selectedTab === "productInfo" ? "bg blue 500 text white" : "bg gray 100" }`} onClick={() => setSelectedTab("productInfo")} > Product Information </button> <button className={`px 4 py 2 rounded md ${ selectedTab === "productColors" ? "bg blue 500 text white" : "bg gray 100" }`} onClick={() => setSelectedTab("productColors")} > Product Colors </button> </div> {/* Conditional Rendering for Tabs */} {selectedTab === "productInfo" && ( <form onSubmit={formik.handleSubmit} className="space y 6"> {/* Product Title */} <div> <label className="block text gray 700 font medium mb 2">Title</label> <input type="text" name="title" value={values.title} onChange={handleChange} onBlur={handleBlur} className="w full px 4 py 2 border rounded lg focus:outline none focus:ring 2 focus:ring blue 500" /> {touched.title && errors.title && ( <div className="text red 500">{errors.title}</div> )} </div> {/* Product Description */} <div> <label className="block text gray 700 font medium mb 2"> Description </label> <textarea name="description" value={values.description} onChange={handleChange} onBlur={handleBlur} className="w full px 4 py 2 border rounded lg focus:outline none focus:ring 2 focus:ring blue 500" /> {touched.description && errors.description && ( <div className="text red 500">{errors.description}</div> )} </div> {/* Price */} <div> <label className="block text gray 700 font medium mb 2">Price</label> <input type="number" name="price" value={values.price} onChange={handleChange} onBlur={handleBlur} className="w full px 4 py 2 border rounded lg focus:outline none focus:ring 2 focus:ring blue 500" /> {touched.price && errors.price && ( <div className="text red 500">{errors.price}</div> )} </div> {/* Category */} <div className="relative z 50"> <button onClick={() => setExpandedSectionId((prev) => (prev ? null : "categories")) } className="w full px 4 py 2 text left bg white border rounded lg" > {selectedCategory ? selectedCategory : "Select Category"} <ChevronDownIcon className="inline w 5 h 5 ml 2" /> </button> {expandedSectionId === "categories" && ( <div className="absolute mt 2 bg white shadow md rounded lg"> {navigation.categories[0].sections.map((section) => ( <div key={section.id}> <h3 className="px 2 py 2 font bold font moneta"> {section.name} </h3> {section.items.map((item) => ( <button key={item.href} onClick={() => { setSelectedCategory(item.name); setExpandedSectionId(null); }} className="block w full px 8 py 2 text left font medium font moneta hover:bg gray 100" > {item.name} </button> ))} </div> ))} </div> )} </div> </form> )} {selectedTab === "productColors" && ( <div className="space y 2"> <label className="block text gray 700 font medium">Select Color</label> <div className="flex space x 2"> {[ { name: "Light Red", className: "#ff0000" }, { name: "Dark Red", className: "#300000" }, { name: "Light Green", className: "#0eff00" }, { name: "Dark Green", className: "#063b00" }, { name: "Light Blue", className: "#15D5D6" }, { name: "Dark Blue", className: "#0000ff" }, { name: "White", className: "#D2CECE" }, { name: "Black", className: "#000000" }, ].map((preset) => ( <button key={preset.name} onClick={() => handleColorChange(preset)} className={`w 8 h 8 rounded full border ${ preset.name === productState.selectedColor.name ? "ring 2 ring black" : "" }`} style={{ backgroundColor: preset.className }} /> ))} </div> <div className="flex items center space x 4 mt 4"> {productState.currentImages.map((file) => ( <div key={file.name} className="relative"> <img src={file.preview} alt={file.name} className="w 20 h 20 object cover rounded md" /> <button type="button" onClick={() => handleRemoveImage(file)} className="absolute top 0 right 0 bg red 500 text white rounded full w 6 h 6 text xs flex items center justify center" > X </button> </div> ))} </div> <div {...getRootProps()}> <button type="button" className="w full py 2 bg blue 500 text white rounded md" > Upload Image </button> </div> <button type="button" onClick={handleAddColor} className="w full py 2 mt 6 bg green 500 text white rounded md" > Add Color </button> </div> )} {/* Form Submit */} <div className="mt 8"> <button type="submit" className="w full py 3 bg indigo 500 text white rounded lg" > Add Product </button> </div> </div> ); }; export default CreateProductsPage;

Prompt

About

mockProducts - Create and edit product pages with image uploads, color selection, and category navigation, built with React and Tailw. Free code available!

Share

Last updated 1 month ago