Untitled
import React, { useState, useEffect } from "react"; import axios from "axios"; import { useRouter } from "next/router"; import NavBar from "@/components/navbar"; import Sidebar from "@/components/sidebar"; import { ToastContainer, toast } from "react-toastify"; import routes from "@/routes"; const AddCategory = () => { const [categoryName, setCategoryName] = useState(""); const [description, setDescription] = useState(""); const [photos, setPhotos] = useState([]); // Changed to array for multiple images const [errors, setErrors] = useState({}); const router = useRouter(); const [componentName, setComponentName] = useState(""); const [sectionID, setSectionID] = useState(""); const [AllSections, setAllSection] = useState([]); const [isLoading, setIsLoading] = useState(true); const [prevSectionID, setPrevSectionID] = useState(""); const [discountType, setDiscountType] = useState("percentage"); const [discountValue, setDiscountValue] = useState(0); const getComponentNameBySectionID = async (sectionID) => { try { const res = await axios.get( `${process.env.NEXT_PUBLIC_BASE_URL}/section/${sectionID}` ); return res.data.componentName; } catch (error) { console.error("Error fetching componentName:", error); return null; } }; useEffect(() => { const fetchData = async () => { if (router.query.data) { const { _id, name, photo, sectionID: querySectionID, description: queryDescription, discount, } = JSON.parse(router.query.data); setCategoryName(name); // Initialize photos array with existing photo if it exists setPhotos(photo ? [photo] : []); setSectionID(querySectionID); setPrevSectionID(querySectionID); setDescription(queryDescription); if (discount) { setDiscountType(discount.type); setDiscountValue(discount.value); } const fetchedComponentName = await getComponentNameBySectionID( querySectionID ); if (fetchedComponentName) { setComponentName(fetchedComponentName); await getAllSectionWithComponent(fetchedComponentName); } } setIsLoading(false); }; fetchData(); }, [router.query.data]); const validateForm = () => { const newErrors = {}; if (!categoryName) newErrors.categoryName = "Category name is required"; if (photos.length === 0) newErrors.photo = "At least one photo is required"; if (!componentName) newErrors.componentName = "Component name is required"; if (!sectionID) newErrors.sectionID = "Please select a Section"; if (!discountValue) newErrors.discountValue = "Discount value is required."; if (discountValue < 0) newErrors.discountValue = "Discount value cannot be negative."; if (!description) newErrors.description = "Description is required."; setErrors(newErrors); return Object.keys(newErrors).length === 0; }; const handleCategoryNameChange = (e) => { setCategoryName(e.target.value); setErrors((prev) => ({ ...prev, categoryName: "" })); }; const handleDescriptionChange = (e) => { setDescription(e.target.value); setErrors((prev) => ({ ...prev, description: "" })); }; const handlePhotoChange = async (e) => { const files = Array.from(e.target.files); for (const file of files) { if (file && file.type.startsWith("image/")) { try { setErrors((prev) => ({ ...prev, photo: "" })); const token = localStorage.getItem("token"); const formDataToSend = new FormData(); // Add all necessary category data to formData formDataToSend.append("image", file); formDataToSend.append("name", categoryName); formDataToSend.append("componentName", componentName); formDataToSend.append("sectionID", sectionID); formDataToSend.append("description", description); formDataToSend.append("discountType", discountType); formDataToSend.append("discountValue", discountValue); let response; if (router.query.data) { // If editing existing category const { _id } = JSON.parse(router.query.data); response = await axios.put( `${process.env.NEXT_PUBLIC_BASE_URL}${routes.category.edit(_id)}`, formDataToSend, { headers: { Authorization: `Bearer ${token}`, "Content-Type": "multipart/form-data", }, } ); } else { // If creating new category response = await axios.post( `${process.env.NEXT_PUBLIC_BASE_URL}${routes.s3.create}`, formDataToSend, { headers: { Authorization: `Bearer ${token}`, "Content-Type": "multipart/form-data", }, } ); } // Assuming the response includes the image URL const imageUrl = response.data.photo || response.data.imageUrl; setPhotos((prevPhotos) => [...prevPhotos, imageUrl]); toast.success("Image uploaded successfully"); } catch (error) { console.error("Error uploading image:", error); toast.error( "There was an error uploading the image. Please try again." ); } } else { toast.error("Please select a valid image file"); } } }; const handleRemovePhoto = (indexToRemove) => { setPhotos((prevPhotos) => prevPhotos.filter((_, index) => index !== indexToRemove) ); }; const handleSubmit = async (e) => { e.preventDefault(); if (!validateForm()) { return; } const token = localStorage.getItem("token"); const formData = new FormData(); // Add all form data formData.append("name", categoryName); formData.append("componentName", componentName); formData.append("sectionID", sectionID); formData.append("description", description); formData.append("discountType", discountType); formData.append("discountValue", discountValue); // Add all photos photos.forEach((photo, index) => { formData.append(`photos[${index}]`, photo); }); try { let response; if (router.query.data) { const { _id } = JSON.parse(router.query.data); response = await axios.put( `${process.env.NEXT_PUBLIC_BASE_URL}${routes.category.edit(_id)}`, formData, { headers: { Authorization: `Bearer ${token}`, "Content-Type": "multipart/form-data", }, } ); } else { response = await axios.post( `${process.env.NEXT_PUBLIC_BASE_URL}${routes.s3.create}`, formData, { headers: { Authorization: `Bearer ${token}`, "Content-Type": "multipart/form-data", }, } ); } toast.success(response.data.message || "Category saved successfully"); setTimeout(() => { router.push("/categories"); }, 1000); } catch (error) { toast.error("Error saving category"); console.error("Error saving category:", error); } }; const getAllSectionWithComponent = async (compName) => { try { const res = await axios.get( `${process.env.NEXT_PUBLIC_BASE_URL}/section?componentName=${compName}` ); setAllSection(res.data); } catch (error) { console.error("Error fetching sections:", error); toast.error("Error loading sections"); } }; useEffect(() => { if (componentName) { getAllSectionWithComponent(componentName); } }, [componentName]); const handleComponentChange = (e) => { const newComponentName = e.target.value; setComponentName(newComponentName); setSectionID(""); setErrors((prev) => ({ ...prev, componentName: "", sectionID: "" })); }; const handleSectionChange = (e) => { setSectionID(e.target.value); setErrors((prev) => ({ ...prev, sectionID: "" })); }; if (isLoading) { return <div>Loading...</div>; } return ( <div className="flex flex-1 h-screen overflow-y-scroll custom-scrollbar"> <ToastContainer /> <Sidebar /> <div className="flex flex-1 flex-col"> <NavBar /> <div className="flex-1 px-4 bg-[#f6f6f6] py-4"> <div className="p-4 mt-24 flex flex-col"> <p className="text-black text-xl font-semibold"> {router.query.data ? "Edit" : "Add"} Category Form </p> <p className="text-neutral-400 text-xs font-normal mt-2"> {router.query.data ? "Edit" : "Add"} form for categories </p> <div className="w-full h-fit bg-white rounded-lg mt-5"> <form onSubmit={handleSubmit} className="w-[92%] mx-auto flex flex-col mt-6" > <div> <span className="text-neutral-800 text-sm font-medium"> Category Name </span> <span className="text-red-400 text-sm font-medium">*</span> </div> <div className="mt-2"> <input className="outline-none w-[464px] h-12 bg-white rounded-lg border border-zinc-300 px-2" placeholder="Enter category name" required={true} value={categoryName} onChange={handleCategoryNameChange} /> {errors.categoryName && ( <p className="text-red-500 text-sm mt-1"> {errors.categoryName} </p> )} </div> <div> <span className="text-neutral-800 text-sm font-medium"> Category Description </span> <span className="text-red-400 text-sm font-medium">*</span> </div> <div className="mt-2"> <input className="outline-none w-[464px] h-12 bg-white rounded-lg border border-zinc-300 px-2" placeholder="Enter category description" required={true} value={description} onChange={handleDescriptionChange} /> {errors.description && ( <p className="text-red-500 text-sm mt-1"> {errors.description} </p> )} </div> <div className="mt-5"> <span className="text-neutral-800 text-sm font-medium"> Component Name </span> <span className="text-red-400 text-sm font-medium">*</span> </div> <div className="mt-2"> <select className="outline-none w-[464px] h-12 bg-white rounded-lg border border-zinc-300 px-2" value={componentName} onChange={handleComponentChange} required > <option value="">Select a component</option> <option value="categorylist1">Category List 1</option> <option value="categorylist2">Category List 2</option> <option value="categorylist3">Category List 3</option> </select> {errors.componentName && ( <p className="text-red-500 text-sm mt-1"> {errors.componentName} </p> )} </div> <div className="mt-5"> <span className="text-neutral-800 text-sm font-medium"> Select Section ID </span> <span className="text-red-400 text-sm font-medium">*</span> </div> <div className="mt-2"> <select className="outline-none w-[464px] h-12 bg-white rounded-lg border border-zinc-300 px-2" value={sectionID} onChange={handleSectionChange} required > <option value="">Select a Section</option> {AllSections.map((section) => ( <option key={section._id} value={section._id}> {section.title} </option> ))} </select> {errors.sectionID && ( <p className="text-red-500 text-sm mt-1"> {errors.sectionID} </p> )} <div className="flex flex-col w-[45.5%] gap-1 mt-2"> <p className="text-neutral-800 text-sm font-medium"> Discount Type </p> <select value={discountType} onChange={(e) => setDiscountType(e.target.value)} className="outline-none border rounded-md h-[50px] w-[90%] px-2" > <option value="percentage">Percentage</option> <option value="flat">Flat</option> </select> <p className="text-neutral-800 text-sm font-medium mt-2"> Discount Value </p> <input className="outline-none border rounded-md h-[50px] w-[90%] px-2" placeholder="Enter discount value" type="number" value={discountValue} onChange={(e) => setDiscountValue(e.target.value)} /> {errors.discountValue && ( <p className="text-red-500 text-sm mt-1"> {errors.discountValue} </p> )} </div> </div> <div className="mt-5"> <span className="text-neutral-800 text-sm font-medium"> Upload photos </span> <span className="text-red-400 text-sm font-medium">*</span> <div className="w-[465px] h-[125px] bg-neutral-50 rounded-lg custom-dotted-border mt-2 border-pink-700 flex justify-center items-center"> <label htmlFor="photoInput"> <img src="/images/upload.svg" alt="Upload" /> </label> <input id="photoInput" type="file" className="hidden" onChange={handlePhotoChange} multiple /> </div> {errors.photo && ( <p className="text-red-500 text-sm mt-1">{errors.photo}</p> )} <div className="flex flex-wrap gap-3 mt-3"> {photos.map((photo, index) => ( <div key={index} className="relative border border-pink-700 rounded-lg" > <img src={photo} alt={`Uploaded Photo ${index + 1}`} className="h-12 w-18 rounded-lg" /> <button type="button" className="absolute -top-1 -right-1 bg-white rounded-full w-5 h-5 flex items-center justify-center text-red-500 text-sm border border-red-500" onClick={() => handleRemovePhoto(index)} > × </button> {typeof photo === "string" && ( <span className="ml-2 text-xs"> {photo.split("/").pop()} </span> )} </div> ))} </div> </div> <div className="flex justify-end my-5 gap-5 mx-5"> <div className="w-[184px] h-[43px] pl-[63px] pr-16 pt-2.5 pb-[9px] bg-pink-700 rounded-md border justify-center items-center inline-flex"> <button type="submit" className="text-white text-base font-medium" > {router.query.data ? "Update" : "Submit"} </button> </div> <button type="button" className="text-pink-700 font-medium underline" onClick={() => { setCategoryName(""); setPhotos([]); setComponentName(""); setSectionID(""); setErrors({}); }} > Clear </button> </div> </form> </div> </div> </div> </div> </div> ); }; export default AddCategory;
Leave a Comment