product section change

This commit is contained in:
pawan-dot 2024-04-15 14:55:54 +05:30
parent b5b8cbaf0c
commit 75b5bc7c0e
7 changed files with 2183 additions and 1151 deletions

View File

@ -1,214 +1,182 @@
import React, { useEffect, useState } from "react";
import Button from "@material-ui/core/Button";
import { Link, useNavigate } from "react-router-dom";
import swal from "sweetalert";
import React, { useState, useEffect } from "react";
import { Button } from "@mui/material";
import axios from "axios";
import { isAutheticated } from "src/auth";
import CloudUploadIcon from "@mui/icons-material/CloudUpload";
import DeleteSharpIcon from "@mui/icons-material/DeleteSharp";
// import { API } from "src/API";
// import { isAutheticated } from "../../components/auth/authhelper";
import { Link, useNavigate } from "react-router-dom";
import {
Box,
FormControl,
IconButton,
MenuItem,
Select,
TextField,
} from "@mui/material";
// import { WebsiteURL } from '../WebsiteURL'
CCard,
CCardBody,
CCardGroup,
CCol,
CContainer,
CRow,
} from "@coreui/react";
import ProductDetails from "./Productcomponents/ProductDetails.js";
import ProductVarients from "./Productcomponents/ProductVarients.js";
import ProductsImages from "./Productcomponents/ProductImages.js";
import { isAutheticated } from "src/auth.js";
// import ReleventProduct from "./Productcomponents/ReleventProduct";
// import ProductFabric from "./Productcomponents/ProductFabric.js";
const AddProduct = () => {
const token = isAutheticated();
const [productId, setProductId] = useState("");
const [viewState, setViewState] = useState(1);
const [images, setImages] = useState([]);
const [categories, setCategories] = useState([]);
const [taxes, setTaxes] = useState([]);
const [sizes, setSizes] = useState([]);
const [relevantProduct, setRelevantProduct] = useState([]);
const [allreleventSelectedProduct, setallReleventSelectedProduct] = useState(
[]
);
const navigate = useNavigate();
const [loading, setLoading] = useState(false);
const [allTax, setAllTax] = useState([]);
const [categories, setCategoies] = useState([]);
const [imagesPreview, setImagesPreview] = useState([]);
// const [allimage, setAllImage] = useState([]);
const [name, setName] = useState("");
const [description, setDescription] = useState("");
const [productImages, setProductImages] = useState([]);
const [price, setPrice] = useState(0);
const [category, setCategoryName] = useState("");
const [error, setError] = useState("");
const [selectedTax, setselectedTax] = useState();
const [product_Status, setproduct_Status] = useState("");
const [totalAmt, setTotalAmt] = useState(0);
const [gst_amount, setGst_amount] = useState(0);
const [data, setData] = useState({
name: "",
category: "",
// sku: "",
description: "",
master_price: "",
// discontinue_on: "",
// hsn_code: "",
product_Status: "",
const handleFileChange = (e) => {
const files = e.target.files;
special_instructions: "",
// productImages.length == 0 ||
// gst_amount === "" ||
// price === "" ||
// totalAmt === "" ||
// gst_amount === "" ||
});
// Check the total number of selected files
if (productImages.length + files.length > 4) {
setError("You can only upload up to 4 images.");
return;
}
const [varients, setVarients] = useState([
{
variant_Name: "",
weight: "",
volume: "",
price: "",
// Check file types and append to selectedFiles
const allowedTypes = ["image/jpeg", "image/png", "image/jpg"];
const selected = [];
gst_Id: "",
},
{
variant_Name: "",
weight: "",
volume: "",
price: "",
for (let i = 0; i < files.length; i++) {
if (productImages.length + selected.length >= 4) {
break; // Don't allow more than 4 images
}
gst_Id: "",
},
if (allowedTypes.includes(files[i].type)) {
selected.push(files[i]);
}
}
{
variant_Name: "",
weight: "",
volume: "",
price: "",
if (selected.length === 0) {
setError("Please upload only PNG, JPEG, or JPG files.");
} else {
setError("");
setProductImages([...productImages, ...selected]);
}
};
const handelDelete = (image) => {
const filtered = productImages.filter((item) => item !== image);
setProductImages(filtered);
};
// get All categories
const getCategories = async () => {
try {
const response = await axios.get("/api/category/getCategories", {
headers: {
Authorization: `Bearer ${token}`,
},
});
if (response.status === 200) {
setCategoies(response?.data?.categories);
}
} catch (error) {
swal({
title: error,
text: " please login to access the resource ",
icon: "error",
button: "Retry",
dangerMode: true,
});
}
};
// Get all tax
const getAllTax = async () => {
const res = await axios.get(`/api/tax/view_tax`, {
headers: {
"Access-Control-Allow-Origin": "*",
Authorization: `Bearer ${token}`,
},
});
if (res.data) {
setAllTax(res.data);
}
};
useEffect(() => {
getAllTax();
getCategories();
}, [token]);
const TaxRatechange = async (e) => {
let m = JSON.parse(e.target.value);
if (m?.tax) {
let totalprice = Number(price) + Number((price * m?.tax) / 100);
setGst_amount(Number((price * m?.tax) / 100)?.toFixed(2));
setTotalAmt(totalprice?.toFixed(2));
setselectedTax(m?._id);
}
};
const handleSubmit = () => {
if (
name === "" ||
description === "" ||
productImages.length == 0 ||
category === "" ||
selectedTax === "" ||
gst_amount === "" ||
product_Status === "" ||
price === ""
) {
swal({
title: "Warning",
text: "Fill all mandatory fields",
icon: "error",
button: "Close",
dangerMode: true,
});
return;
}
setLoading(true);
const formData = new FormData();
formData.append("name", name);
formData.append("description", description);
formData.append("price", price);
formData.append("category", category);
formData.append("total_amount", totalAmt);
formData.append("gst_amount", gst_amount);
formData.append("product_Status", product_Status);
formData.append("gst", selectedTax);
productImages.forEach((Singleimage) => {
// console.log(Singleimage)
formData.append("image", Singleimage);
});
gst_Id: "",
},
]);
const [allFabrics, setAllFabrics] = useState([]);
const [fabrics, setFabrics] = useState([
{ _id: "", fabric_Name: "", for_Part: "" },
{ _id: "", fabric_Name: "", for_Part: "" },
{ _id: "", fabric_Name: "", for_Part: "" },
]);
const getCategories = () => {
axios
.post(`/api/product/create/`, formData, {
.get(`/api/category/getCategories`, {
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "multipart/formdata",
"Access-Control-Allow-Origin": "*",
Authorization: `Bearer ${token}`,
},
})
.then((res) => {
swal({
title: "Added",
text: "Product added successfully!",
icon: "success",
button: "ok",
});
setCategories(res?.data?.categories);
setLoading(false);
navigate("/products", { replace: true });
})
.catch((err) => {
setLoading(false);
const message = err.response?.data?.message
? err.response?.data?.message
: "Something went wrong!";
swal({
title: "Warning",
text: message,
icon: "error",
button: "Retry",
dangerMode: true,
});
});
};
const handlePriceChange = (e) => {
const newPrice = e.target.value;
setPrice(newPrice);
const selectedTaxObj = allTax.find((t) => t._id === selectedTax);
if (selectedTaxObj && !isNaN(newPrice)) {
const gstAmount = (newPrice * selectedTaxObj.tax) / 100;
const totalAmount = Number(newPrice) + gstAmount;
setGst_amount(gstAmount.toFixed(2));
setTotalAmt(totalAmount.toFixed(2));
}
const getTaxes = () => {
axios
.get(`/api/tax/view_tax`, {
headers: {
"Access-Control-Allow-Origin": "*",
Authorization: `Bearer ${token}`,
},
})
.then((res) => {
setTaxes(res.data);
});
};
// console.log(data);
// console.log(productImages);
// const getSizes = () => {
// axios
// .get(`/api/erp/sizemaster/size`, {
// headers: {
// "Access-Control-Allow-Origin": "*",
// Authorization: `Bearer ${token}`,
// },
// })
// .then((res) => {
// setSizes(res.data?.data);
// });
// };
// const getItemWhichcontaiNameFabric = () => {
// axios
// .get(`/api/erp/item/name_contain_fabric`, {
// headers: {
// "Access-Control-Allow-Origin": "*",
// Authorization: `Bearer ${token}`,
// },
// })
// .then((res) => {
// console.log(res?.data);
// // setSizes(res.data?.data)
// setAllFabrics(res?.data?.data);
// })
// .catch((err) => {
// console.log(err);
// });
// };
// const getProductsData = async () => {
// axios
// .get(`/api/product`, {
// headers: {
// Authorization: `Bearer ${token}`,
// },
// })
// .then((res) => {
// setRelevantProduct(res.data?.data);
// })
// .catch((err) => {
// console.log(err);
// });
// };
useEffect(() => {
getCategories();
getTaxes();
// getSizes();
// getProductsData();
// getItemWhichcontaiNameFabric();
}, []);
const handleView = (n) => {
if (viewState === n) return;
setViewState(n);
};
return (
<div className="container">
<div className="row">
<div className="col-12">
<CContainer>
<CRow className="mt-3">
<CCol md={12}>
<div
className="
page-title-box
@ -218,27 +186,9 @@ const AddProduct = () => {
"
>
<div style={{ fontSize: "22px" }} className="fw-bold">
Add Product
Add Product : {data?.name && data?.name}
</div>
<div style={{ display: "flex", gap: "1rem" }}>
<h4 className="mb-0"></h4>
</div>
<div className="page-title-right">
<Button
variant="contained"
color="primary"
style={{
fontWeight: "bold",
marginBottom: "1rem",
textTransform: "capitalize",
marginRight: "5px",
}}
onClick={() => handleSubmit()}
disabled={loading}
>
{loading ? "Loading" : "Save"}
</Button>
<Link to="/products">
<Button
variant="contained"
@ -254,302 +204,140 @@ const AddProduct = () => {
</Link>
</div>
</div>
</div>
</div>
<div className="row">
<div className="col-lg-6 col-md-6 col-sm-12 my-1">
<div className="card h-100">
<div className="card-body px-5">
<div className="mb-3">
<label htmlFor="title" className="form-label">
Product Name*
</label>
<input
type="text"
className="form-control"
id="name"
value={name}
maxLength={35}
onChange={(e) => setName(e.target.value)}
/>
{name ? (
<>
<small className="charLeft mt-4 fst-italic">
{35 - name.length} characters left
</small>
</>
) : (
<></>
)}{" "}
</div>
<div className="mb-3">
<label htmlFor="title" className="form-label">
Description*
</label>
<textarea
type="text"
className="form-control"
id="description"
value={description}
rows={8}
maxLength="400"
onChange={(e) => setDescription(e.target.value)}
/>
{description ? (
<>
<small className="charLeft mt-4 fst-italic">
{400 - description.length} characters left
</small>
</>
) : (
<></>
)}
</div>
<div className="mb-3">
<label htmlFor="image" className="form-label">
Product Image*
</label>
<Box>
<label htmlFor="upload-Image">
<TextField
style={{
display: "none",
width: "350px",
height: "350px",
borderRadius: "10%",
}}
fullWidth
id="upload-Image"
type="file"
accept=".jpg , .png ,.jpeg"
label="file"
multiple
variant="outlined"
onChange={(e) => handleFileChange(e)}
/>
<Box
style={{ borderRadius: "10%" }}
sx={{
margin: "1rem 0rem",
cursor: "pointer",
width: "140px",
height: "140px",
border: "2px solid grey",
// borderRadius: '50%',
"&:hover": {
background: "rgba(112,112,112,0.5)",
},
}}
>
<CloudUploadIcon
style={{
color: "grey",
margin: "auto",
fontSize: "5rem",
}}
fontSize="large"
/>
</Box>
</label>
</Box>
{error && <p style={{ color: "red" }}>{error}</p>}
<p className="pt-1 pl-2 text-secondary">
Upload jpg, jpeg and png only*
</p>
<Box style={{ display: "flex" }}>
{productImages &&
productImages.map((image, i) => (
<Box marginRight={"2rem"}>
<img
src={URL.createObjectURL(image)}
alt="profileImage"
style={{
width: 70,
height: 70,
marginBottom: "1rem",
}}
/>
<DeleteSharpIcon
onClick={() => handelDelete(image)}
fontSize="small"
sx={{
color: "white",
position: "absolute",
cursor: "pointer",
padding: "0.2rem",
background: "black",
borderRadius: "50%",
}}
/>
{/* </IconButton> */}
</Box>
))}
</Box>
</div>
<div>
<strong className="fs-6 fst-italic">
*You cannot upload more than 4 images !!
</strong>
</div>
<div id="createProductFormImage" className="w-25 d-flex">
{imagesPreview.map((image, index) => (
<img
className=" w-50 p-1 "
key={index}
src={image}
alt="Product Preview"
</CCol>
</CRow>
<CRow>
<CCol md={9} className="mt-1">
<CCardGroup>
<CCard className="p-4 mb-3">
<CCardBody>
{viewState === 1 && (
<ProductDetails
data={{ data, setData }}
categories={categories}
ProductId={{ productId, setProductId }}
loading={{ loading, setLoading }}
/>
))}
</div>
</div>
</div>
</div>
<div className="col-lg-6 col-md-6 col-sm-12 my-1">
<div className="card h-100">
<div className="card-body px-5">
<div className="mb-3 me-3">
<label htmlFor="title" className="form-label">
Price(Rs)*
</label>
<input
type="number"
className="form-control"
id="price"
value={price}
onChange={(e) => handlePriceChange(e)}
/>
</div>
<div className="">
<label htmlFor="categorySelect">Select a Category *:</label>
<select
id="category"
className="form-control"
style={{ width: "100%" }}
value={category}
onChange={(e) => setCategoryName(e.target.value)}
>
<option value={""}>None</option>
{categories.map((category, index) => (
<option key={index} value={category?._id}>
{category.categoryName}
</option>
))}
</select>
{/* <FormControl fullWidth>
<Select
labelId="demo-simple-select-label"
id="demo-simple-select"
value={categoryName}
onChange={(e) => setCategoryName(e.target.value)}
)}
{viewState === 2 && (
<ProductVarients
productId={productId}
data={{ varients, setVarients }}
taxes={taxes}
sizes={sizes}
loading={{ loading, setLoading }}
/>
)}
{viewState === 3 && (
<ProductsImages
productId={productId}
data={{ images, setImages }}
loading={{ loading, setLoading }}
/>
)}
{/* {viewState === 4 && (
<ProductFabric
productId={productId}
data={{ fabrics, setFabrics }}
allFabrics={allFabrics}
loading={{ loading, setLoading }}
/>
)}
{viewState === 5 && (
<ReleventProduct
data={{ data, setData }}
ProductId={productId}
AllreleventSelectedPro={{
allreleventSelectedProduct,
setallReleventSelectedProduct,
}}
ReleventProduct={relevantProduct}
loading={{ loading, setLoading }}
/>
)} */}
{/* {viewState === 5 && (
<ProductFabric
data={{ data, setData }}
ProductId={productId}
AllreleventSelectedPro={{
allreleventSelectedProduct,
setallReleventSelectedProduct,
}}
ReleventProduct={relevantProduct}
loading={{ loading, setLoading }}
/>
)} */}
</CCardBody>
</CCard>
</CCardGroup>
</CCol>
<CCol md={3} className="mt-1">
<CCardGroup>
<CCard>
<CCardBody>
<div className="d-grid gap-2">
<button
className={
viewState === 1
? "btn btn-light"
: "btn btn-info text-white"
}
type="button"
onClick={() => handleView(1)}
>
<MenuItem
style={{
width: "100%",
display: "flex",
justifyContent: "left",
textAlign: "left",
padding: "0.5rem",
}}
value={""}
>
None
</MenuItem>
{categories.map((category, i) => (
<MenuItem
style={{
width: "100%",
display: "flex",
justifyContent: "left",
textAlign: "left",
padding: "0.5rem",
}}
key={i}
value={category.categoryName}
>
{category.categoryName}
</MenuItem>
))}
</Select>
</FormControl> */}
</div>
{allTax.length > 0 && (
<div className=" mb-3">
<label htmlFor="title" className="form-label">
GST*
</label>{" "}
<select
className="form-control"
name="gst"
id="gst"
onChange={(e) => TaxRatechange(e)}
Product Details
</button>
<button
className={
viewState === 2
? "btn btn-light"
: "btn btn-info text-white"
}
type="button"
onClick={() => handleView(2)}
>
<option value="">--Select--</option>
{allTax.map((t, i) => (
<option key={i} value={JSON.stringify(t)}>
{t.tax}% {t.name}
</option>
))}
</select>
Variants
</button>
<button
className={
viewState === 3
? "btn btn-light"
: "btn btn-info text-white"
}
type="button"
onClick={() => handleView(3)}
>
Images
</button>
{/* <button
className={
viewState === 4
? "btn btn-light"
: "btn btn-info text-white"
}
type="button"
onClick={() => handleView(4)}
>
Fabric
</button> */}
{/* <button
className={
viewState === 5
? "btn btn-light"
: "btn btn-info text-white"
}
type="button"
onClick={() => handleView(5)}
>
+ Relevent Product
</button> */}
</div>
)}
<div className="mb-3 me-3">
<label htmlFor="title" className="form-label">
GST Amount (Rs) *
</label>
<input
disabled
type="number"
name="gst_amount"
className="form-control"
id="gst_amount"
value={gst_amount}
// onChange={(e) => setPrice(e.target.value)}
/>
</div>
<div className="mb-3 me-3">
<label htmlFor="title" className="form-label">
Total Amount(Rs)*
</label>
<input
disabled
type="number"
name="total_amount"
className="form-control"
id="total_amount"
value={totalAmt}
// onChange={(e) => setPrice(e.target.value)}
/>
</div>
<div className=" mb-3">
<label htmlFor="title" className="form-label">
Product Status *
</label>{" "}
<select
className="form-control"
name="product_Status"
id="product_Status"
value={product_Status}
onChange={(e) => setproduct_Status(e.target.value)}
>
<option value="">--Select--</option>
<option value="Active">Active</option>
<option value="Inactive">Inactive</option>
</select>
</div>
</div>
</div>
</div>
</div>
</div>
</CCardBody>
</CCard>
</CCardGroup>
</CCol>
</CRow>
</CContainer>
);
};

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,244 @@
import axios from "axios";
import React from "react";
import swal from "sweetalert";
import { isAutheticated } from "src/auth";
const ProductDetails = (props) => {
const token = isAutheticated();
const { data, setData } = props.data;
const { productId, setProductId } = props.ProductId;
const { loading, setLoading } = props.loading;
const categories = props?.categories || [];
const handleChange = (e) => {
if (e.target.id === "master_price" && /^\D+$/.test(e.target.value)) return;
if (e.target.id === "discontinue_on") {
if (new Date(e.target.value) < new Date()) {
return setData((prev) => ({
...prev,
[e.target.id]: new Date().toISOString().slice(0, 10),
}));
}
}
setData((prev) => ({ ...prev, [e.target.id]: e.target.value }));
};
const handleSubmit = () => {
if (
data.name.trim() === "" ||
// data.master_price.trim() === "" ||
data.category === "" ||
data.description === "" ||
data.product_Status === ""
// data.sku === "" ||
// data.hsn_code === ""
) {
swal({
title: "Warning",
text: "Fill all mandatory fields",
icon: "warning",
button: "Return",
});
return;
}
setLoading(true);
axios
.post(
`/api/product/create/`,
{ ...data, product_id: productId },
{
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${token}`,
},
}
)
.then((res) => {
swal({
title: "Saved",
text: "Product details saved successfully!",
icon: "success",
button: "Close",
});
setProductId(res.data.product_id);
setLoading(false);
})
.catch((err) => {
const msg = err?.response?.data?.message || "Something went wrong!";
swal({
title: "Warning",
text: msg,
icon: "warning",
button: "Close",
});
setLoading(false);
});
};
return (
<>
<div className="d-flex justify-content-between">
<h5>Product Details</h5>
<button
className="btn btn-primary btn-sm"
type="button"
onClick={() => handleSubmit()}
disabled={loading}
>
{loading ? "Loading" : "Save Details"}
</button>
</div>
<div className="mb-3">
<label htmlFor="product" className="form-label">
Product Name*
</label>
<input
type="text"
className="form-control"
id="name"
value={data.name}
maxLength="50"
onChange={(e) => handleChange(e)}
/>
</div>
<div className="mb-3 row">
{/* <div className="col-lg-6">
<label htmlFor="product" className="form-label">
SKU*
</label>
<input
type="text"
className="form-control"
id="sku"
value={data.sku}
maxLength="50"
onChange={(e) => handleChange(e)}
/>
</div> */}
<div className="col-lg-6 ">
<label htmlFor="product" className="form-label">
Category *
</label>
<select
id="category"
onChange={(e) => handleChange(e)}
className="form-control"
value={data.category}
>
<option value="">---select---</option>
{categories ? (
categories.map((item, index) => (
<option value={item._id} key={index}>
{item.categoryName}
</option>
))
) : (
<option value="" disabled>
Add Category to select
</option>
)}
</select>
</div>
<div className="col-lg-6">
{/* <label htmlFor="product" className="form-label">
Master Price*
</label>
<input
type="text"
className="form-control"
id="master_price"
value={data.master_price}
maxLength="6"
onChange={(e) => handleChange(e)}
/> */}
<label htmlFor="title" className="form-label">
Product Status *
</label>{" "}
<select
className="form-control"
name="product_Status"
id="product_Status"
value={data.product_Status}
onChange={(e) => handleChange(e)}
>
<option value="">--Select--</option>
<option value="Active">Active</option>
<option value="Inactive">Inactive</option>
</select>
</div>
</div>
<div className="mb-3">
<label htmlFor="product" className="form-label">
Description*
</label>
<textarea
className="form-control"
id="description"
value={data.description}
onChange={(e) => handleChange(e)}
/>
</div>
{/* <div className="mb-3 row">
<div className="col-6">
<label htmlFor="hsc_code" className="form-label">
HSN Code*
</label>
<input
type="text"
className="form-control"
id="hsn_code"
value={data.hsn_code}
maxLength="50"
onChange={(e) => handleChange(e)}
/>
</div>
</div> */}
<div className="mb-3">
<label htmlFor="product" className="form-label">
Special Instructions
</label>
<textarea
className="form-control"
style={{
whiteSpace: "pre-wrap",
minHeight: "100px",
overflowWrap: "break-word",
}}
value={data.special_instructions}
onChange={handleChange}
id="special_instructions"
/>
</div>
{/* <div className="mb-3">
<label>Discontinue On*</label>
<input
type="date"
value={data.discontinue_on}
id="discontinue_on"
onChange={(e) => handleChange(e)}
className="form-control"
min={new Date().toISOString().slice(0, 10)}
/>
</div> */}
{/* <div className=" mb-3">
<label htmlFor="title" className="form-label">
Product Status *
</label>{" "}
<select
className="form-control"
name="product_Status"
id="product_Status"
value={data.product_Status}
onChange={(e) => handleChange(e)}
>
<option value="">--Select--</option>
<option value="Active">Active</option>
<option value="Inactive">Inactive</option>
</select>
</div> */}
</>
);
};
export default ProductDetails;

View File

@ -0,0 +1,302 @@
import axios from "axios";
import React, { useEffect, useState } from "react";
import swal from "sweetalert";
import { isAutheticated } from "src/auth";
const ProductsImages = (props) => {
const token = isAutheticated();
const productId = props.productId;
const { images, setImages } = props.data;
const { loading, setLoading } = props.loading;
const [deleting, setDeleting] = useState(false);
const [imagePreview, setImagePreview] = useState({ preview: "", image: "" });
const [imagesPreview, setImagesPreview] = useState([]);
const [allimage, setAllImage] = useState([]);
//
const [data, setData] = useState({
image: [],
imageURL: [],
});
useEffect(() => {
if (images?.length > 0) {
setData((prev) => ({
...prev,
imageURL: images?.url,
}));
setImagesPreview(images);
}
}, []);
const handleChange = (e) => {
if (e.target.id === "image") {
if (
e.target.files[0]?.type === "image/jpeg" ||
e.target.files[0]?.type === "image/png" ||
e.target.files[0]?.type === "image/jpg"
) {
if (imagesPreview.length > 3) {
swal({
title: "Warning",
text: "maximum Four image Upload ",
icon: "error",
button: "Close",
dangerMode: true,
});
return;
}
// only for file preview------------------------------------
const files = Array.from(e.target.files);
files.forEach((file) => {
const reader = new FileReader();
reader.onload = () => {
if (reader.readyState === 2) {
setImagesPreview((old) => [...old, reader.result]);
}
};
reader.readAsDataURL(file);
});
// -----------------------------------------------------------------------------
setData((prev) => ({
...prev,
image: [...data.image, ...e.target.files],
}));
return;
} else {
swal({
title: "Warning",
text: "Upload jpg, jpeg, png only.",
icon: "error",
button: "Close",
dangerMode: true,
});
setData((prev) => ({
...prev,
imageURL: "",
image: "",
}));
e.target.value = null;
return;
}
}
setData((prev) => ({ ...prev, [e.target.id]: e.target.value }));
};
//
// const handleChange = (e) => {
// if (
// e.target.files[0]?.type === "image/jpeg" ||
// e.target.files[0]?.type === "image/png" ||
// e.target.files[0]?.type === "image/jpg"
// ) {
// setImagePreview((prev) => ({
// ...prev,
// preview: URL.createObjectURL(e.target.files[0]),
// image: e.target.files[0],
// }));
// return;
// } else {
// swal({
// title: "Warning",
// text: "Upload jpg, jpeg, png only.",
// icon: "error",
// button: "Close",
// dangerMode: true,
// });
// setImagePreview((prev) => ({
// ...prev,
// preview: "",
// image: "",
// }));
// e.target.value = null;
// return;
// }
// };
const handleDelete = (id) => {
swal({
title: "Are you sure?",
icon: "error",
buttons: {
Yes: { text: "Yes", value: true },
Cancel: { text: "Cancel", value: "cancel" },
},
}).then((value) => {
if (value === true) {
setDeleting(true);
axios
.delete(`/api/product/image/${productId}/${id}`, {
headers: {
"Access-Control-Allow-Origin": "*",
Authorization: `Bearer ${token}`,
},
})
.then((res) => {
setDeleting(false);
const filteredImages = images.filter((e) => e._id !== id);
setImages(filteredImages);
})
.catch((err) => {
setDeleting(false);
swal({
title: "Warning",
text: "Something went wrong!",
icon: "error",
button: "Retry",
dangerMode: true,
});
});
}
});
};
const handleSubmit = () => {
// if (!imagePreview.image) return;
if (data.image?.length < 1) {
swal({
title: "Warning",
text: "PLease select Product Image",
icon: "warning",
button: "ok",
dangerMode: true,
});
return;
}
setLoading(true);
const formData = new FormData();
data.image.forEach((Singleimage) => {
// console.log(Singleimage)
formData.append("image", Singleimage);
});
axios
.patch(`/api/product/update/${productId}`, formData, {
headers: {
"Content-Type": "multipart/form-data",
Authorization: `Bearer ${token}`,
"Access-Control-Allow-Origin": "*",
},
})
.then((res) => {
swal({
title: "Saved",
text: "Product image saved successfully!",
icon: "success",
button: "Close",
});
setImages((prev) => [...prev, res.data?.image]);
setImagePreview({ preview: "", image: "" });
setLoading(false);
})
.catch((err) => {
// console.log(err);
const msg = err?.response?.data?.message || "Something went wrong!";
swal({
title: "Warning",
text: msg,
icon: "warning",
button: "Close",
});
setLoading(false);
});
};
return (
<>
<div className="d-flex justify-content-between">
<h5>Product Images</h5>
</div>
<div className="my-3">
<div className="row">
<div className="col-lg-9">
<input
type="file"
className="form-control"
id="image"
accept="image/*"
multiple
onChange={(e) => handleChange(e)}
/>
</div>
<div className="col-lg-3">
<button
className="btn btn-primary"
onClick={() => handleSubmit()}
disabled={!productId || loading}
>
{productId
? loading
? "Loading"
: "Upload"
: "Product Details not saved"}
</button>
</div>
</div>
<p className="pt-1 pl-2 text-secondary">
Upload jpg, jpeg and png only*
</p>
<div>
<strong className="fs-6 fst-italic">
*Please Upload maximum four images
</strong>
</div>
<div className="mb-3" style={{ height: "100px", maxWdth: "100%" }}>
{/* <img
src={imagePreview.preview}
alt="Uploaded Image will be shown here"
style={{ maxHeight: "100px", maxWidth: "100%" }}
/> */}
{imagesPreview.map((image, index) => (
<img
className="p-1"
key={index}
src={image?.url ? image?.url : image}
alt="Uploaded Image will be shown here"
style={{ maxHeight: "100px", maxWidth: "100%" }}
/>
))}
</div>
</div>
{/* <table className="table">
<thead>
<tr>
<th scope="col">Preview</th>
<th scope="col">Actions</th>
</tr>
</thead>
<tbody>
{images.length !== 0 ? (
images.map((r, idx) => (
<tr key={idx}>
<td>
<img src={r.url} alt="preview" height="100px" />
</td>
<td>
<button
className="btn btn-danger btn-sm text-white"
onClick={() => handleDelete(r._id)}
disabled={deleting}
>
Delete
</button>
</td>
</tr>
))
) : (
<tr>
<td colSpan={6} className="text-center fw-bold">
No Image Added
</td>
</tr>
)}
</tbody>
</table> */}
</>
);
};
export default ProductsImages;

View File

@ -0,0 +1,249 @@
import axios from "axios";
import React from "react";
import swal from "sweetalert";
import { isAutheticated } from "src/auth";
const ProductVarients = (props) => {
const token = isAutheticated();
const productId = props.productId;
const taxes = props.taxes;
const sizes = props.sizes;
const { varients, setVarients } = props.data;
const { loading, setLoading } = props.loading;
const addVarientRow = () => {
setVarients((prev) => [
...prev,
{
variant_Name: "",
weight: "",
volume: "",
price: "",
gst_Id: "",
},
]);
};
const handleChange = (e, idx) => {
if (
e.target.name === "weight" &&
e.target.value !== "" &&
!/^[0-9.]+$/.test(e.target.value)
)
return;
if (
e.target.name === "price" &&
e.target.value !== "" &&
!/^[0-9.]+$/.test(e.target.value)
)
return;
let clone = [...varients];
let obj = clone[idx];
// if (e.target.name === "gst_Id") {
// }
obj[e.target.name] = e.target.value;
clone[idx] = obj;
setVarients([...clone]);
};
// variant_Name: "",
// weight: "",
// volume: "",
// price: "",
// gst_Name: "",
// gst_Rate: "",
// gst_Id: "",
const handleSubmit = () => {
const emptyVarients = varients.filter(
(e) =>
!(e.variant_Name === "" && e.price === "" && e.gst_Id === "") &&
(e.variant_Name === "" || e.price === "" || e.gst_Id === "")
);
if (emptyVarients.length !== 0) {
swal({
title: "Warning",
text: "Fill all fields of a row",
icon: "warning",
button: "Return",
});
return;
}
// const variant_Name = [];
// varients.map((e) => e.variant_Name && variant_Name.push(e.variant_Name));
// const duplicate = variant_Name.filter(
// (e) => sizes.indexOf(e) !== sizes.lastIndexOf(e)
// );
// if (duplicate.length !== 0) {
// swal({
// title: "Warning",
// text: "Duplicate sizes selected!",
// icon: "warning",
// button: "Return",
// });
// return;
// }
const varientData = varients.filter((e) => e.variant_Name !== "");
const emptyVariants = varients.reduce(
(arr, e) =>
e.gst_Id !== "" && e.variant_Name === "" && e.price === ""
? [...arr, e._id]
: arr,
[]
);
setLoading(true);
axios
.patch(
`/api/product/update/${productId}`,
{ variants: varientData, delete_variants: emptyVariants },
{
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${token}`,
},
}
)
.then((res) => {
swal({
title: "Saved",
text: "Product variants saved successfully!",
icon: "success",
button: "Close",
});
setVarients((prev) =>
res?.data?.variants?.length > 0 ? [...res?.data?.variants] : prev
);
setLoading(false);
})
.catch((err) => {
const msg = err?.response?.data?.message || "Something went wrong!";
swal({
title: "Warning",
text: msg,
icon: "warning",
button: "Close",
});
setLoading(false);
});
};
return (
<>
<div className="d-flex justify-content-between">
<h5>Product Variants</h5>
<button
className="btn btn-primary btn-sm"
type="button"
onClick={() => handleSubmit()}
disabled={!productId || loading}
>
{productId
? loading
? "Loading"
: "Save Varients"
: "First Save Product Details then Save Varients"}
</button>
</div>
<table className="table">
<thead>
<tr>
{/* <th scope="col">Size</th> */}
<th scope="col">Variant Name</th>
<th scope="col">Price</th>
{/* <th scope="col">Weight (in Grams)</th> */}
{/* <th scope="col">Show in E-store</th> */}
<th scope="col">Tax</th>
</tr>
</thead>
<tbody>
{varients.map((r, idx) => (
<tr key={idx}>
{/* <td>
<select
name="size"
value={r.size}
onChange={(e) => handleChange(e, idx)}
className="form-control"
>
<option value="">---select---</option>
{sizes &&
sizes.map((item, index) => (
<option value={item._id} key={index}>
{item.size}
</option>
))}
</select>
</td> */}
<td>
<input
type="text"
className="form-control"
name="variant_Name"
value={r.variant_Name}
onChange={(e) => handleChange(e, idx)}
/>
</td>
<td>
<input
type="number"
className="form-control"
name="price"
value={r.price}
onChange={(e) => handleChange(e, idx)}
/>
</td>
{/* <td>
<input
type="text"
className="form-control"
name="weight"
value={r.weight}
onChange={(e) => handleChange(e, idx)}
/>
</td> */}
{/* <td>
<select
name="show_in_estore"
value={r.show_in_estore}
onChange={(e) => handleChange(e, idx)}
className="form-control"
>
<option value={true}>Yes</option>
<option value={false}>No</option>
</select>
</td> */}
<td>
<select
name="gst_Id"
value={r.gst_Id?._id ? r.gst_Id?._id : r.gst_Id}
onChange={(e) => handleChange(e, idx)}
className="form-control"
>
<option value="">---select---</option>
{taxes &&
taxes.map((item, index) => (
<option value={item._id} key={index}>
{item.tax} %{item.name}
</option>
))}
</select>
</td>
</tr>
))}
</tbody>
</table>
<div className="text-center">
<button
className="btn btn-primary btn-sm"
onClick={() => addVarientRow()}
>
Add another variant
</button>
</div>
</>
);
};
export default ProductVarients;

View File

@ -48,9 +48,11 @@ const Products = () => {
setLoading(false);
})
.catch((error) => {
const msg = error?.response?.data?.message || "Something went wrong!";
swal({
title: error,
text: "please login to access the resource or refresh the page ",
text: msg,
icon: "error",
button: "Retry",
dangerMode: true,
@ -62,7 +64,6 @@ const Products = () => {
useEffect(() => {
getProductsData();
}, [success]);
console.log(productsData);
useEffect(() => {
const loadData = () => {
@ -100,9 +101,11 @@ const Products = () => {
setSuccess((prev) => !prev);
})
.catch((err) => {
const msg = err?.response?.data?.message || "Something went wrong!";
swal({
title: "Warning",
text: "Something went wrong!",
text: msg,
icon: "error",
button: "Retry",
dangerMode: true,
@ -384,7 +387,10 @@ const Products = () => {
: "Category Not selected "}
</td>
<th className="text-start">
{product?.total_amount}
{product?.total_amount
? product?.total_amount
: product?.variants[0]?.price}
</th>
<td className="text-start">
{new Date(product.createdAt).toLocaleString(

View File

@ -1,42 +1,47 @@
import React, { useState, useEffect } from "react";
import axios from "axios";
import React, { useEffect, useState, useCallback, useMemo } from "react";
import swal from "sweetalert";
import { Link, useParams } from "react-router-dom";
import { useNavigate, useParams } from "react-router-dom";
import { isAutheticated } from "src/auth";
function ViewProduct() {
const [product, setProduct] = useState([]);
const ViewProduct = () => {
// const id = useParams()?.id;
const { id } = useParams();
const token = isAutheticated();
const getProduct = useCallback(async () => {
let res = await axios.get(`/api/product/getOne/${id}`, {
headers: {
Authorization: `Bearer ${token}`,
},
});
setProduct(res.data.product);
}, [token]);
const [productData, setProductData] = useState({});
const [SAW, setSAW] = useState([
{ volume: "", weight: "" },
{ volume: "", weight: "" },
{ volume: "", weight: "" },
{ volume: "", weight: "" },
{ volume: "", weight: "" },
]);
const navigate = useNavigate();
const getProductData = async () => {
axios
.get(`/api/product/getOne/${id}`, {
headers: {
Authorization: `Bearer ${token}`,
},
})
.then((res) => {
console.log(res.data.data);
setProductData(res.data.data);
if (res.data.data?.variants) {
setSAW((prev) => [...res.data.data?.variants]);
}
})
.catch((err) => {});
};
useEffect(() => {
getProduct();
}, [getProduct]);
getProductData();
}, []);
//change time formate
function formatAMPM(date) {
var hours = new Date(date).getHours();
var minutes = new Date(date).getMinutes();
var ampm = hours >= 12 ? "PM" : "AM";
hours = hours % 12;
hours = hours ? hours : 12; // the hour '0' should be '12'
minutes = minutes < 10 ? "0" + minutes : minutes;
var strTime = hours + ":" + minutes + " " + ampm;
return strTime;
}
// console.log(product);
const onCancel = () => {
navigate("/products");
};
let count = 1;
return (
<div className=" main-content">
<div className=" my-3 page-content">
@ -45,24 +50,15 @@ function ViewProduct() {
<div className="row">
<div className="col-12">
<div className="page-title-box d-flex align-items-center justify-content-between">
<h4 className="mb-3">Product</h4>
<Link to="/products">
<button
type="button"
className="btn btn-info float-end mb-3 ml-4"
>
{" "}
Back
</button>
</Link>
{/* <div className="page-title-right">
<ol className="breadcrumb m-0">
<li className="breadcrumb-item">
<Link to="/dashboard">CMD-App</Link>
</li>
<li className="breadcrumb-item">CMD-Category</li>
</ol>
</div> */}
<h4 className="mb-3">Product Details</h4>
<button
onClick={onCancel}
type="button"
className="mb-2 ml-2 btn btn-warning btn-cancel waves-effect waves-light mr-3"
>
Back
</button>
</div>
</div>
</div>
@ -77,91 +73,166 @@ function ViewProduct() {
<table className="table table-centered table-nowrap mb-0">
<thead className="thead-light">
<tr>
<th>Id</th>{" "}
<td>
<h5>{product.uniqueId}</h5>
</td>
<th>Name</th>
<td>{productData?.name}</td>
</tr>
<tr>
<th>Name</th> <td>{product?.name}</td>
</tr>
{product.image && (
<tr>
<th>image</th>
<td>
{product.image.map((i) => (
<img
className="me-2"
src={`${i?.url}`}
width="70"
alt=""
/>
))}
</td>
</tr>
)}
<tr>
<th>Category </th>
<td>
{product.category?.categoryName !== ""
? product.category?.categoryName
: "Category not selected "}
</td>
</tr>
<tr>
<th>Description</th>
<td>{product?.description}</td>
</tr>
<tr>
<th>Price</th>
<td>{product?.price}</td>
</tr>
<tr>
<th>Gst</th>
<td>
{product?.gst?.tax}% {product?.gst?.name}
</td>
</tr>
{product?.gst_amount && (
<tr>
<th>Gst Amount</th>
<td>{product?.gst_amount}</td>
</tr>
)}
<tr>
<th>Total Amount(with Gst)</th>
<td>{product?.total_amount}</td>
</tr>
{/* <tr><th>Product Time</th><td>{product?.time}</td></tr>
<tr><th>Location</th><td>{product?.location}</td></tr> */}
<tr>
<th>Product Status</th>
<td>{product?.product_Status}</td>
<th>Product Group</th>
<td>{productData?.category?.categoryName}</td>
</tr>
<tr>
<th>Created On</th>
<th>Images</th>
<td>
{new Date(`${product?.createdAt}`).toDateString()}
<span>
{" "}
, {`${formatAMPM(product?.createdAt)}`}
</span>
{productData?.image &&
productData?.image?.length !== 0
? productData?.image.map((e, i) => (
<img
className="p-1"
src={e.url}
width="100"
alt="preview"
key={i}
/>
))
: "No Images Uploaded!"}
</td>
</tr>
<tr>
<th>Updated At</th>
<th>Description</th>
<td>{productData?.description}</td>
</tr>
<tr>
<th>Product Status</th>
<td>{productData?.product_Status}</td>
</tr>
<tr>
<th>Special Instructions</th>
<td>
{new Date(`${product?.updatedAt}`).toDateString()}
<span>
{" "}
, {`${formatAMPM(product?.updatedAt)}`}
</span>
<p
style={{ whiteSpace: "pre-line" }}
className="m-0 p-0"
>
{productData?.special_instructions
? productData?.special_instructions
: "---"}
</p>
</td>
</tr>
</thead>
<tbody></tbody>
</table>
<table className="table table-primary mt-3">
<caption
style={{ captionSide: "top" }}
className="text-dark fw-bold"
>
Varients:
</caption>
<thead>
<tr>
<th className="text-center">Variant Name</th>
<th className="text-center">Price</th>
<th className="text-center">Gst</th>
</tr>
</thead>
<tbody>
{SAW.map(
(r, i) =>
r.variant_Name !== "" && (
<tr key={i}>
<td className="text-center">
{r?.variant_Name}
</td>
<td className="text-center">{r?.price}</td>
<td className="text-center">
{r?.gst_Id?.name + " " + r?.gst_Id?.tax + "%"}
</td>
</tr>
)
)}
{SAW.filter((e) => e.variant_Name !== "").length ===
0 && (
<tr>
<td colSpan={"6"} className="text-center">
No data available
</td>
</tr>
)}
</tbody>
</table>
{/* <div className="mb-2">
<table className="table table-secondary mt-3">
<caption
style={{ captionSide: "top" }}
className="text-dark fw-bold"
>
Product Fabric:
</caption>
{productData?.product_Fabric?.length > 0 ? (
<>
<thead>
<tr>
<th className="text-center">S. No.</th>
<th className="text-center"> Name</th>
<th className="text-center"> Use For Part</th>
</tr>
</thead>
<tbody>
{productData?.product_Fabric?.map((r, i) => (
<tr key={i}>
<td className="text-center">{++i}</td>
<td className="text-center">
{r?.fabric_Name}
</td>
<td className="text-center">{r?.for_Part}</td>
</tr>
))}
</tbody>
</>
) : (
<>
<h5>No Fabric Allotted for this product!</h5>
</>
)}
</table>
</div> */}
{/* <div className="mb-2">
<table className="table table-info mt-3">
<caption
style={{ captionSide: "top" }}
className="text-dark fw-bold"
>
Relevent Product:
</caption>
{productData?.relevent_product?.length > 0 ? (
<>
<thead>
<tr>
<th className="text-center">S. No.</th>
<th className="text-center">Product Name</th>
</tr>
</thead>
<tbody>
{productData?.relevent_product?.map((r, i) => (
<tr key={i}>
<td className="text-center">{count++}</td>
<td className="text-center">{r?.name}</td>
</tr>
))}
</tbody>
</>
) : (
<>
<h5>No relevent item for this product !</h5>
</>
)}
</table>
</div> */}
</div>
{/* <!-- end table-responsive --> */}
@ -174,6 +245,6 @@ function ViewProduct() {
</div>
</div>
);
}
};
export default ViewProduct;