brand image integrate

This commit is contained in:
Sibunnayak 2025-02-06 09:45:54 +05:30
parent 4e461f1c9e
commit 1d5de2e4a4
7 changed files with 400 additions and 77 deletions

View File

@ -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 (
<Modal
open={open}
onClose={handleClose}
aria-labelledby="modal-modal-title"
aria-describedby="modal-modal-description"
>
<Box sx={style}>
<Box p={2} display={"flex"}>
<Typography
id="modal-modal-title"
variant="body"
component="h2"
flex={1}
>
Brand Name
</Typography>
<IconButton
onClick={() => {
handleClose();
setSelectedImage(null);
}}
>
<CloseIcon />
</IconButton>
</Box>
<hr />
<TextField
placeholder="brand name"
value={brandName}
fullWidth
inputProps={{
maxLength: 25,
}}
style={{
padding: "1rem",
}}
onChange={(e) =>
setBrandName(
e.target.value.charAt(0).toUpperCase() + e.target.value.slice(1)
)
}
/>
{brandName ? (
<>
<small className="charLeft mt-2 ml-3 fst-italic">
{25 - brandName.length} characters left
</small>
</>
) : (
<></>
)}
{/* Image Upload Section */}
<Box mt={2} className="d-flex ml-3">
{existingImage ? (
<Box>
<img
src={existingImage.url}
alt="Brand"
width={200}
height={200}
style={{
objectFit: "contain",
borderRadius: 8,
border: "1px solid #ccc",
}}
/>
<button
onClick={handleRemoveImage}
style={{ background: "red", color: "white", marginTop: 8, marginLeft: 8 }}
>
Delete Image
</button>
</Box>
) : selectedImage ? (
<Box>
<img
src={URL.createObjectURL(selectedImage)}
alt="Preview"
width={200}
height={200}
style={{
objectFit: "contain",
borderRadius: 8,
border: "1px solid #ccc", // Optional border for clarity
}}
/>
<button
onClick={handleRemoveImage}
style={{ background: "red", color: "white", marginTop: 8, marginLeft: 8 }}
>
Remove Image
</button>
</Box>
) : (
<input type="file" accept="image/*" onChange={handleFileChange} />
)}
</Box>
<Box
p={2}
display={"flex"}
justifyContent={"right"}
// width={"500px"}
>
{!edit && (
<button
style={{
color: "white",
marginRight: "1rem",
}}
onClick={() =>
handleSaveBrand(brandName, selectedImage, () =>
setSelectedImage(null)
)
}
type="button"
className="
btn btn-primary btn-sm
waves-effect waves-light
btn-table
mx-1
mt-1
"
>
<ClipLoader loading={saveLoading} size={18} />
{saveLoading || "Save"}
</button>
)}
{edit && (
<button
style={{
color: "white",
marginRight: "1rem",
}}
onClick={() =>
handleUpdate(brandName, selectedImage, () =>
setSelectedImage(null)
)
}
type="button"
className="
btn btn-primary btn-sm
waves-effect waves-light
btn-table
mx-1
mt-1
"
>
<ClipLoader loading={updating} size={18} />
{updating || "update"}
</button>
)}
<button
style={{
color: "black",
marginRight: "1rem",
background: "grey",
}}
onClick={() => {
setOpen(false);
setSelectedImage(null);
}}
type="button"
className="
btn btn-sm
waves-effect waves-light
btn-table
mx-1
mt-1
"
>
Close
</button>
</Box>
</Box>
</Modal>
);
};
export default BrandModal;

View File

@ -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 () => {
@ -74,15 +70,20 @@ const Brands = () => {
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
</Button>
<Modal
{/* <Modal
open={open}
onClose={handleClose}
aria-labelledby="modal-modal-title"
@ -342,7 +361,22 @@ const Brands = () => {
</button>
</Box>
</Box>
</Modal>
</Modal> */}
<BrandModal
open={open}
handleClose={handleClose}
setOpen={setOpen}
brandName={brandName}
setBrandName={setBrandName}
edit={edit}
handleSaveBrand={handleSaveBrand}
handleUpdate={handleUpdate}
saveLoading={saveLoading}
updating={updating}
existingImage={existingImage}
setExistingImage={setExistingImage}
getBrands={getBrands}
/>
</div>
</div>
</div>
@ -397,8 +431,8 @@ const Brands = () => {
style={{ background: "rgb(140, 213, 213)" }}
>
<tr>
<th> Brand Image</th>
<th> Brand Name</th>
<th>Action</th>
</tr>
</thead>
@ -419,56 +453,72 @@ const Brands = () => {
) : (
brands &&
brands
.slice(
(`${page}` - 1) * itemPerPage,
`${page}` * itemPerPage
)
.map((item, i) => (
<tr key={i}>
<td>
<h5>{item.brandName} </h5>
</td>
<td className="text-start">
<button
style={{
color: "white",
marginRight: "1rem",
}}
type="button"
className="
.slice(
(`${page}` - 1) * itemPerPage,
`${page}` * itemPerPage
)
.map((item, i) => (
<tr key={i}>
<td>
{item?.image && item?.image.length !== 0 ? (
<img
src={item?.image[0]?.url}
width={50}
height={100}
alt="preview"
/>
) : (
"No Image Uploaded!"
)}
</td>
<td>
<h5>{item.brandName} </h5>
</td>
<td className="text-start">
<button
style={{
color: "white",
marginRight: "1rem",
}}
type="button"
className="
btn btn-primary btn-sm
waves-effect waves-light
btn-table
mx-1
mt-1
"
onClick={() =>
handleEditClick(item._id, item.brandName)
}
>
Edit
</button>
<button
style={{
color: "white",
marginRight: "1rem",
background: "red",
}}
type="button"
className="
onClick={() =>
handleEditClick(
item._id,
item.brandName,
item?.image
)
}
>
Edit
</button>
<button
style={{
color: "white",
marginRight: "1rem",
background: "red",
}}
type="button"
className="
btn btn-sm
waves-effect waves-light
btn-table
mx-1
mt-1
"
onClick={() => handleDelete(item._id)}
>
Delete
</button>
</td>
</tr>
))
onClick={() => handleDelete(item._id)}
>
Delete
</button>
</td>
</tr>
))
)}
</tbody>
</table>

View File

@ -119,7 +119,7 @@ const AddProduct = () => {
</CCard>
</CCardGroup>
</CCol>
<CCol md={3} className="mt-1">
{/* <CCol md={3} className="mt-1">
<CCardGroup>
<CCard>
<CCardBody>
@ -142,7 +142,7 @@ const AddProduct = () => {
</CCardBody>
</CCard>
</CCardGroup>
</CCol>
</CCol> */}
</CRow>
</CContainer>
);

View File

@ -166,7 +166,7 @@ const EditProduct = () => {
</CCard>
</CCardGroup>
</CCol>
<CCol md={3} className="mt-1">
{/* <CCol md={3} className="mt-1">
<CCardGroup>
<CCard>
<CCardBody>
@ -197,7 +197,7 @@ const EditProduct = () => {
</CCardBody>
</CCard>
</CCardGroup>
</CCol>
</CCol> */}
</CRow>
</CContainer>
);

View File

@ -379,7 +379,7 @@ const Products = () => {
productsData?.map((product, i) => (
<tr key={i}>
<td>
{product?.image &&
{/* {product?.image &&
product?.image?.length !== 0 ? (
<img
src={product?.image[0]?.url}
@ -387,7 +387,18 @@ const Products = () => {
alt="preview"
style={{ borderRadius: "5px" }}
/>
) : (
) */}
{product?.brand &&
product?.brand?.image?.length !== 0 ? (
<img
src={product?.brand?.image[0]?.url}
width={50}
height={100}
alt="preview"
style={{ borderRadius: "5px" }}
/>
)
: (
<div style={{ fontSize: "13px" }}>
<p className="m-0">No</p>
<p className="m-0">image</p>

View File

@ -85,7 +85,7 @@ const ViewProduct = () => {
<tr>
<th>Images</th>
<td>
{productData?.image &&
{/* {productData?.image &&
productData?.image?.length !== 0
? productData?.image.map((e, i) => (
<img
@ -95,7 +95,16 @@ const ViewProduct = () => {
alt="preview"
key={i}
/>
))
)) */}
{productData?.brand?.image && productData?.brand?.image?.length !==0 ? (
<img
className="p-1"
src={productData?.brand?.image[0]?.url}
width={50}
height={100}
alt="preview"
/>
)
: "No Images Uploaded!"}
</td>
</tr>

View File

@ -326,12 +326,13 @@ const DistributorStocks = () => {
return (
<tr key={i}>
<td>
{product?.image &&
product?.image?.length !== 0 ? (
{product?.brandImage &&
product?.brandImage?.length !== 0 ? (
<>
<img
src={product?.image[0]?.url}
width="50"
src={product?.brandImage[0]?.url}
width={50}
height={100}
alt="preview"
/>
</>