From 1d5de2e4a473a21bc6f7681af641c013bf6d3907 Mon Sep 17 00:00:00 2001 From: Sibunnayak Date: Thu, 6 Feb 2025 09:45:54 +0530 Subject: [PATCH] brand image integrate --- src/views/Brands/BrandModal.js | 252 ++++++++++++++++++ src/views/Brands/Brands.js | 180 ++++++++----- src/views/Products/AddProduct.js | 4 +- src/views/Products/EditProduct.js | 4 +- src/views/Products/Products.js | 15 +- src/views/Products/ViewProduct.js | 13 +- .../RetailDistributors/DistributorStock.js | 9 +- 7 files changed, 400 insertions(+), 77 deletions(-) create mode 100644 src/views/Brands/BrandModal.js diff --git a/src/views/Brands/BrandModal.js b/src/views/Brands/BrandModal.js new file mode 100644 index 0000000..91460fa --- /dev/null +++ b/src/views/Brands/BrandModal.js @@ -0,0 +1,252 @@ +import React, { useState } from "react"; +import { Modal, Box, Typography, IconButton, TextField } from "@mui/material"; +import CloseIcon from "@mui/icons-material/Close"; +import { ClipLoader } from "react-spinners"; +import { isAutheticated } from "src/auth"; +import { toast } from "react-hot-toast"; +import axios from "axios"; +const style = { + position: "absolute", + top: "50%", + left: "50%", + transform: "translate(-50%, -50%)", + bgcolor: "background.paper", + borderRadius: "0.5rem", + boxShadow: 24, + width: "500px", +}; +const BrandModal = ({ + open, + setOpen, + brandName, + setBrandName, + edit, + handleSaveBrand, + handleUpdate, + saveLoading, + updating, + existingImage, + setExistingImage, + handleClose, + getBrands, +}) => { + const [selectedImage, setSelectedImage] = useState(null); + + const handleFileChange = (event) => { + const file = event.target.files[0]; + if (file) { + setSelectedImage(file); + } + }; + const handleDeleteImage = (url, public_id) => { + if (public_id) { + // Retrieve the authentication token + const token = isAutheticated(); // Ensure this function + const encodedPublicId = encodeURIComponent(public_id); + // console.log("public_id:", public_id); // Log to debug + // console.log("encodedPublicId:", encodedPublicId); // Log to debug + // Delete image from Cloudinary + axios + .delete(`/api/brand/deleteImage/${encodedPublicId}`, { + headers: { + Authorization: `Bearer ${token}`, // Include the token in the headers + }, + }) + .then(() => { + setExistingImage(null); + getBrands(); + toast.success("Image deleted successfully"); + }) + .catch((err) => { + console.error(err.response?.data); + }); + } + }; + const handleRemoveImage = () => { + if (existingImage) { + handleDeleteImage(existingImage.url, existingImage.public_id); + } else { + setSelectedImage(null); + } + }; + return ( + + + + + Brand Name + + { + handleClose(); + setSelectedImage(null); + }} + > + + + +
+ + setBrandName( + e.target.value.charAt(0).toUpperCase() + e.target.value.slice(1) + ) + } + /> + {brandName ? ( + <> + + {25 - brandName.length} characters left + + + ) : ( + <> + )} + {/* Image Upload Section */} + + {existingImage ? ( + + Brand + + + ) : selectedImage ? ( + + Preview + + + ) : ( + + )} + + + + {!edit && ( + + )} + {edit && ( + + )} + + +
+
+ ); +}; + +export default BrandModal; diff --git a/src/views/Brands/Brands.js b/src/views/Brands/Brands.js index dcfd0f3..8d8ff9e 100644 --- a/src/views/Brands/Brands.js +++ b/src/views/Brands/Brands.js @@ -15,24 +15,14 @@ import { ClipLoader } from "react-spinners"; import swal from "sweetalert"; import { toast } from "react-hot-toast"; import debounce from "lodash.debounce"; -const style = { - position: "absolute", - top: "50%", - left: "50%", - transform: "translate(-50%, -50%)", - width: 400, - bgcolor: "background.paper", - borderRadius: "0.5rem", - boxShadow: 24, - width: "500px", -}; +import BrandModal from "./BrandModal"; const Brands = () => { const token = isAutheticated(); const nameRef = useRef(); const [loading, setLoading] = useState(true); - const [updating, setUpdating] = useState(true); - const [saveLoading, setSaveLoading] = useState(true); + const [updating, setUpdating] = useState(false); + const [saveLoading, setSaveLoading] = useState(false); const [edit, setEdit] = useState(false); const [brandName, setBrandName] = useState(""); const [brandId, setBrandId] = useState(""); @@ -41,13 +31,19 @@ const Brands = () => { const [page, setPage] = useState(1); const [open, setOpen] = useState(false); const [olderBrandName, setOlderBrandName] = useState(""); + const [existingImage, setExistingImage] = useState(null); + + const handleOpen = () => { + setOpen(true); + setBrandName(""); + }; - const handleOpen = () => setOpen(true); const handleClose = () => { setOpen(false); setEdit(false); setBrandName(""); setBrandId(""); + setExistingImage(null); }; const getBrands = async () => { @@ -72,17 +68,22 @@ const Brands = () => { useEffect(() => { getBrands(); - }, []); + }, []); - const handleEditClick = (_id, brandName) => { + const handleEditClick = (_id, brandName, image) => { setOpen(true); setBrandName(brandName); setBrandId(_id); setOlderBrandName(brandName); setEdit(true); + if (image?.length > 0) { + setExistingImage({ url: image[0].url, public_id: image[0].public_id }); + } }; - const handleUpdate = async () => { + const handleUpdate = async (brandName, image, callback) => { + setUpdating(true); + const filteredBrandNames = brands .filter( (brand) => @@ -92,29 +93,39 @@ const Brands = () => { if (filteredBrandNames.includes(brandName.toLowerCase())) { swal("Warning", "Brand already exists", "error"); + setUpdating(false); return; } if (!brandName) { swal("Warning", "Please fill all the required fields!", "error"); + setUpdating(false); return; } - setUpdating(false); const formData = new FormData(); formData.append("brandName", brandName); + if (image) { + formData.append("image", image); // Append image only if provided + } + try { await axios.patch(`/api/brand/update/${brandId}`, formData, { - headers: { Authorization: `Bearer ${token}` }, + headers: { + Authorization: `Bearer ${token}`, + "Content-Type": "multipart/form-data", // Ensure proper form data handling + }, }); + handleClose(); toast.success("Brand updated successfully"); getBrands(); } catch (err) { swal("Error", "Failed to update brand", "error"); } finally { - setUpdating(true); + setUpdating(false); + callback(); } }; @@ -141,7 +152,7 @@ const Brands = () => { }); }; - const handleSaveBrand = async () => { + const handleSaveBrand = async (brandName, image, callback) => { if ( brands.some( (brand) => brand.brandName.toLowerCase() === brandName.toLowerCase() @@ -156,10 +167,15 @@ const Brands = () => { return; } - setSaveLoading(false); + setSaveLoading(true); // Set loading state before API call + const formData = new FormData(); formData.append("brandName", brandName); + if (image) { + formData.append("image", image); // Append image if provided + } + try { await axios.post("/api/brand/add", formData, { headers: { @@ -167,17 +183,20 @@ const Brands = () => { "Content-Type": "multipart/form-data", }, }); + handleClose(); swal("Success", "New brand added successfully!", "success"); getBrands(); } catch (error) { swal("Error", "Failed to add brand", "error"); } finally { - setSaveLoading(true); + setSaveLoading(false); // Reset loading state after API call + callback(); } }; - const getPageCount = () => Math.max(1, Math.ceil(brands?.length / itemPerPage)); + const getPageCount = () => + Math.max(1, Math.ceil(brands?.length / itemPerPage)); const debouncedSearch = useCallback( debounce(() => { setPage(1); @@ -228,7 +247,7 @@ const Brands = () => { > Add New brand - { - + */} + @@ -397,8 +431,8 @@ const Brands = () => { style={{ background: "rgb(140, 213, 213)" }} > + Brand Image Brand Name - Action @@ -419,56 +453,72 @@ const Brands = () => { ) : ( brands && brands - .slice( - (`${page}` - 1) * itemPerPage, - `${page}` * itemPerPage - ) - .map((item, i) => ( - - -
{item.brandName}
- - - - - - - )) + onClick={() => handleDelete(item._id)} + > + Delete + + + + )) )} diff --git a/src/views/Products/AddProduct.js b/src/views/Products/AddProduct.js index 87569bc..49c0193 100644 --- a/src/views/Products/AddProduct.js +++ b/src/views/Products/AddProduct.js @@ -119,7 +119,7 @@ const AddProduct = () => { - + {/* @@ -142,7 +142,7 @@ const AddProduct = () => { - + */} ); diff --git a/src/views/Products/EditProduct.js b/src/views/Products/EditProduct.js index ab56f36..6cd5d78 100644 --- a/src/views/Products/EditProduct.js +++ b/src/views/Products/EditProduct.js @@ -166,7 +166,7 @@ const EditProduct = () => { - + {/* @@ -197,7 +197,7 @@ const EditProduct = () => { - + */} ); diff --git a/src/views/Products/Products.js b/src/views/Products/Products.js index 43ff905..4014b60 100644 --- a/src/views/Products/Products.js +++ b/src/views/Products/Products.js @@ -379,7 +379,7 @@ const Products = () => { productsData?.map((product, i) => ( - {product?.image && + {/* {product?.image && product?.image?.length !== 0 ? ( { alt="preview" style={{ borderRadius: "5px" }} /> - ) : ( + ) */} + {product?.brand && + product?.brand?.image?.length !== 0 ? ( + preview + ) + : (

No

image

diff --git a/src/views/Products/ViewProduct.js b/src/views/Products/ViewProduct.js index 74c01dd..02052d1 100644 --- a/src/views/Products/ViewProduct.js +++ b/src/views/Products/ViewProduct.js @@ -85,7 +85,7 @@ const ViewProduct = () => { Images - {productData?.image && + {/* {productData?.image && productData?.image?.length !== 0 ? productData?.image.map((e, i) => ( { alt="preview" key={i} /> - )) + )) */} + {productData?.brand?.image && productData?.brand?.image?.length !==0 ? ( + preview + ) : "No Images Uploaded!"} diff --git a/src/views/RetailDistributors/DistributorStock.js b/src/views/RetailDistributors/DistributorStock.js index d6c60fa..11c9813 100644 --- a/src/views/RetailDistributors/DistributorStock.js +++ b/src/views/RetailDistributors/DistributorStock.js @@ -326,12 +326,13 @@ const DistributorStocks = () => { return ( - {product?.image && - product?.image?.length !== 0 ? ( + {product?.brandImage && + product?.brandImage?.length !== 0 ? ( <> preview