Untitled
unknown
plain_text
a year ago
18 kB
17
Indexable
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;
Editor is loading...
Leave a Comment