From 7149c7cf4f20b92cf7c7319c86f8cde1c28d567a Mon Sep 17 00:00:00 2001 From: Sibunnayak Date: Thu, 8 Aug 2024 16:08:28 +0530 Subject: [PATCH] multiple data for product and principal distributor successfully --- src/App.js | 2 + src/_nav.js | 2 +- src/routes.js | 14 ++ .../PrincipalDistributors/AddMultiplePD.js | 141 +++++++++++++++ .../addPrincipalDistributor.js | 58 ++---- .../PrincipalDistributors/orderDetails.js | 2 +- .../principalDistributor.js | 16 +- src/views/Products/AddMultipleProducts.js | 136 ++++++++++++++ src/views/Products/Products.js | 168 +++++------------- 9 files changed, 364 insertions(+), 175 deletions(-) create mode 100644 src/views/PrincipalDistributors/AddMultiplePD.js create mode 100644 src/views/Products/AddMultipleProducts.js diff --git a/src/App.js b/src/App.js index 475cd7a..e33722b 100644 --- a/src/App.js +++ b/src/App.js @@ -194,6 +194,7 @@ import React, { Suspense, useEffect, useState } from "react"; import { HashRouter, Route, Routes } from "react-router-dom"; import { useSelector } from "react-redux"; +import { Toaster } from "react-hot-toast"; import { isAutheticated } from "./auth"; import "./scss/style.scss"; import ProtectedRoute from "./components/ProtectedRoute"; @@ -261,6 +262,7 @@ const App = () => { /> } /> + ); diff --git a/src/_nav.js b/src/_nav.js index 88cd6bc..ffdfe39 100644 --- a/src/_nav.js +++ b/src/_nav.js @@ -91,7 +91,7 @@ const _nav = [ }, { component: CNavItem, - name: "retail Distributor", + name: "Retail Distributor", icon: , to: "/retail-distributor", group: "RetailDistributor", diff --git a/src/routes.js b/src/routes.js index 98c55f8..6f56c0a 100644 --- a/src/routes.js +++ b/src/routes.js @@ -141,6 +141,8 @@ import SingleUserleave from "./views/Leaves/SingleUserLeave"; import LeaveTerritoryManager from "./views/Leaves/LeaveTerritoryManager"; import RetailDistributor from "./views/RetailDistributors/RetailDistributor"; import SingleRetailDistributor from "./views/RetailDistributors/SingleRetailDistributor"; +import AddMultipleProduct from "./views/Products/AddMultipleProducts"; +import AddMultiplePd from "./views/PrincipalDistributors/AddMultiplePD"; const routes = [ //dashboard @@ -171,6 +173,12 @@ const routes = [ element: AddProduct, navName: "Product Management", }, + { + path: "/product/add/multiple", + name: "Add products", + element: AddMultipleProduct, + navName: "Product Management", + }, { path: "/product/edit/:id", name: "Edit products", @@ -353,6 +361,12 @@ const routes = [ element: addPrincipalDistributor, navName: "PrincipalDistributor", }, + { + path: "/add-principal-distributor/multiple", + name: "PrincipalDistributor", + element: AddMultiplePd, + navName: "PrincipalDistributor", + }, //------------------ End customers Route------------------------- // { diff --git a/src/views/PrincipalDistributors/AddMultiplePD.js b/src/views/PrincipalDistributors/AddMultiplePD.js new file mode 100644 index 0000000..0b31083 --- /dev/null +++ b/src/views/PrincipalDistributors/AddMultiplePD.js @@ -0,0 +1,141 @@ +import React, { useState } from "react"; +import axios from "axios"; +import swal from "sweetalert"; +import { isAutheticated } from "src/auth.js"; +import { useNavigate } from "react-router-dom"; // Import useNavigate +import { toast } from "react-hot-toast"; +const AddMultiplePd = () => { + const [file, setFile] = useState(null); + const [loading, setLoading] = useState(false); + const [errors, setErrors] = useState([]); + const navigate = useNavigate(); // Initialize useNavigate + + const handleFileChange = (e) => { + const selectedFile = e.target.files[0]; + if ( + selectedFile && + selectedFile.type === + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" + ) { + setFile(selectedFile); + } else { + swal("Error", "Please upload a valid Excel file", "error"); + setFile(null); + } + }; + + const handleSubmit = async () => { + if (!file) { + swal("Error", "No file selected", "error"); + return; + } + + setLoading(true); + setErrors([]); + + const formData = new FormData(); + formData.append('file', file); + + try { + const token = isAutheticated(); + const response = await axios.post('/api/v1/principaldistributor/upload', formData, { + headers: { + 'Content-Type': 'multipart/form-data', + Authorization: `Bearer ${token}`, + }, + }); + + const { data } = response; + + if (data.errors && data.errors.length > 0) { + setErrors(data.errors); + swal({ + title: "SpreadSheet Upload Successful", + text: "A few Principal Distributor have errors. Please fix them and upload again.", + icon: "warning", + button: "OK", + }); + } else { + toast.success("Principal Distributor and Address Added Successfully"); + navigate("/principal-distributor"); + } + + setFile(null); // Clear the file input + document.querySelector('input[type="file"]').value = ""; // Reset file input value + } catch (error) { + console.error("Upload error:", error); + swal("Error", `Failed to upload Principal Distributor: ${error.response?.data?.message || 'An unexpected error occurred'}`, "error"); + } finally { + setLoading(false); + } + }; + + return ( +
+
+ +
+
Add Multiple Principal Distributor
+
+
+
+ +
+
+ +
+
+

Upload only .xlsx files*

+
+ + {errors.length > 0 && ( +
+
Finding errors while adding the Principal Distributor.
+ + + + + + + + + + + + + {errors.map((error, index) => ( + + + + + + + + + ))} + +
Principal Distributor NameEmailPhonePanGSTMessage
{error.name || "N/A"}{error.email || "N/A"}{error.phone || "N/A"}{error.panNumber || "N/A"}{error.gstNumber || "N/A"}{error.message}
+
+ )} +
+ ); +}; + +export default AddMultiplePd; diff --git a/src/views/PrincipalDistributors/addPrincipalDistributor.js b/src/views/PrincipalDistributors/addPrincipalDistributor.js index 1dfee25..c8dd404 100644 --- a/src/views/PrincipalDistributors/addPrincipalDistributor.js +++ b/src/views/PrincipalDistributors/addPrincipalDistributor.js @@ -10,7 +10,7 @@ import { CircularProgress, } from "@mui/material"; import { useNavigate } from "react-router-dom"; -import toast from "react-hot-toast"; +import { toast } from "react-hot-toast"; import axios from "axios"; import { isAutheticated } from "src/auth"; import { City, State } from "country-state-city"; @@ -84,40 +84,6 @@ const AddPrincipalDistributor = () => { setSelectedCity(newValue); }; - const generatePassword = (name, email) => { - // Combine name and email, and convert to lowercase - const combinedStr = (name + email).toLowerCase(); - - // Define character pools - const specialChars = "@#*"; - const numbers = "0123456789"; - const alphaLower = combinedStr.match(/[a-z]/g) || []; - const alphaUpper = combinedStr.match(/[A-Z]/g) || []; - - // Ensure at least one character from each category - const specialChar = specialChars.charAt(Math.floor(Math.random() * specialChars.length)); - const numberChar = numbers.charAt(Math.floor(Math.random() * numbers.length)); - const lowerChar = alphaLower.length > 0 ? alphaLower[Math.floor(Math.random() * alphaLower.length)] : String.fromCharCode(Math.floor(Math.random() * 26) + 97); - const upperChar = alphaUpper.length > 0 ? alphaUpper[Math.floor(Math.random() * alphaUpper.length)] : String.fromCharCode(Math.floor(Math.random() * 26) + 65); - - // Combine required characters - let passwordChars = [specialChar, numberChar, lowerChar, upperChar]; - - // Fill remaining positions with random characters from the combined string - const allChars = combinedStr + specialChars + numbers; - while (passwordChars.length < 8) { - passwordChars.push(allChars.charAt(Math.floor(Math.random() * allChars.length))); - } - - // Shuffle characters to ensure randomness - passwordChars = passwordChars.sort(() => Math.random() - 0.5); - - // Generate password of length 8 - const password = passwordChars.slice(0, 8).join(""); - - return password; - }; - const handleFormSubmit = async (e) => { e.preventDefault(); try { @@ -133,20 +99,18 @@ const AddPrincipalDistributor = () => { ) { throw new Error("Fill all fields!"); } - + setLoading(true); - const generatedPassword = generatePassword(user.name, user.email); - + // Attempt to register user const userResponse = await axios.post("/api/v1/user/register", { ...user, - password: generatedPassword, - role:"principal-Distributor", + role: "principal-Distributor", }); - + if (userResponse.status === 201 || userResponse.status === 200) { const userId = userResponse.data.userId; - + // console.log(userId); // Add address details for the user const addressResponse = await axios.post( `/api/shipping/address/admin/new/${userId}`, @@ -161,7 +125,7 @@ const AddPrincipalDistributor = () => { }, } ); - + setLoading(false); if (addressResponse.status === 201) { toast.success("Principal Distributor and Address Added Successfully"); @@ -170,11 +134,13 @@ const AddPrincipalDistributor = () => { } } catch (error) { setLoading(false); - console.error("Error adding principal distributor and address:", error); - toast.error(error.response?.data?.message || "Something went wrong!"); + if (error.response && error.response?.data) { + toast.error(error.response?.data.message || "Something went wrong!"); + } else { + toast.error("Something went wrong!"); + } } }; - const handleCancel = () => { navigate("/principal-distributor"); diff --git a/src/views/PrincipalDistributors/orderDetails.js b/src/views/PrincipalDistributors/orderDetails.js index 3d7d50d..294fb40 100644 --- a/src/views/PrincipalDistributors/orderDetails.js +++ b/src/views/PrincipalDistributors/orderDetails.js @@ -33,7 +33,7 @@ const OrderDetails = ({ _id, setLoading1 }) => { useEffect(() => { getOrders(); }, [_id]); - console.log(userOrder, "userOrder"); + // console.log(userOrder, "userOrder"); // if (loading) { // return
Loading...
; diff --git a/src/views/PrincipalDistributors/principalDistributor.js b/src/views/PrincipalDistributors/principalDistributor.js index 11493ea..006aceb 100644 --- a/src/views/PrincipalDistributors/principalDistributor.js +++ b/src/views/PrincipalDistributors/principalDistributor.js @@ -136,17 +136,23 @@ const principalDistributor = () => { + diff --git a/src/views/Products/AddMultipleProducts.js b/src/views/Products/AddMultipleProducts.js new file mode 100644 index 0000000..1986e53 --- /dev/null +++ b/src/views/Products/AddMultipleProducts.js @@ -0,0 +1,136 @@ +import React, { useState } from "react"; +import axios from "axios"; +import swal from "sweetalert"; +import { isAutheticated } from "src/auth.js"; +import { useNavigate } from "react-router-dom"; // Import useNavigate + +const AddMultipleProducts = () => { + const [file, setFile] = useState(null); + const [loading, setLoading] = useState(false); + const [errors, setErrors] = useState([]); + const navigate = useNavigate(); // Initialize useNavigate + + const handleFileChange = (e) => { + const selectedFile = e.target.files[0]; + if ( + selectedFile && + selectedFile.type === + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" + ) { + setFile(selectedFile); + } else { + swal("Error", "Please upload a valid Excel file", "error"); + setFile(null); + } + }; + + const handleSubmit = async () => { + if (!file) { + swal("Error", "No file selected", "error"); + return; + } + + setLoading(true); + setErrors([]); + + const formData = new FormData(); + formData.append('file', file); + + try { + const token = isAutheticated(); + const response = await axios.post('/api/products/upload', formData, { + headers: { + 'Content-Type': 'multipart/form-data', + Authorization: `Bearer ${token}`, + }, + }); + + const { data } = response; + + if (data.errors && data.errors.length > 0) { + setErrors(data.errors); + swal({ + title: "SpreadSheet Upload Successful", + text: "A few products have errors. Please fix them and upload again.", + icon: "warning", + button: "OK", + }); + } else { + swal("Success", "Products added successfully", "success"); + } + + setFile(null); // Clear the file input + document.querySelector('input[type="file"]').value = ""; // Reset file input value + } catch (error) { + console.error("Upload error:", error); + swal("Error", `Failed to upload products: ${error.response?.data?.message || 'An unexpected error occurred'}`, "error"); + } finally { + setLoading(false); + } + }; + + return ( +
+
+ +
+
Add Multiple Products
+
+
+
+ +
+
+ +
+
+

Upload only .xlsx files*

+
+ + {errors.length > 0 && ( +
+
Finding errors while adding the products.
+ + + + + + + + + + + {errors.map((error, index) => ( + + + + + + + ))} + +
Product NameCategoryGSTMessage
{error.productName || "N/A"}{error.category || "N/A"}{error.GST || "N/A"}{error.message}
+
+ )} +
+ ); +}; + +export default AddMultipleProducts; diff --git a/src/views/Products/Products.js b/src/views/Products/Products.js index 8fee7cc..40f5962 100644 --- a/src/views/Products/Products.js +++ b/src/views/Products/Products.js @@ -1,10 +1,11 @@ -import React, { useState, useEffect, useRef } from "react"; +import React, { useState, useEffect, useRef, useCallback } from "react"; import { Link } from "react-router-dom"; import axios from "axios"; import Button from "@material-ui/core/Button"; import { useNavigate } from "react-router-dom"; import { isAutheticated } from "src/auth"; import swal from "sweetalert"; +import debounce from "lodash.debounce"; const Products = () => { const token = isAutheticated(); const navigate = useNavigate(); @@ -15,51 +16,39 @@ const Products = () => { const nameRef = useRef(); const categoryRef = useRef(); - const FeatureProductRef = useRef(); const [currentPage, setCurrentPage] = useState(1); const [itemPerPage, setItemPerPage] = useState(10); const [totalData, setTotalData] = useState(0); - // const { - // edit, - // add, - // delete: deletepermission, - // } = checkPermission("Product Master"); const getProductsData = async () => { setLoading(true); - await axios - .get(`/api/product/getAll/admin/`, { + try { + const response = await axios.get(`/api/product/getAll/admin/`, { headers: { Authorization: `Bearer ${token}`, }, params: { page: currentPage, show: itemPerPage, - name: nameRef.current.value, - category: categoryRef.current.value, - FeatureProduct: FeatureProductRef.current.value, + name: nameRef.current?.value || "", + category: categoryRef.current?.value || "", }, - }) - .then((res) => { - // console.log(res.data); - setProductsData(res.data?.products); - setTotalData(res.data?.total_data); - setLoading(false); - }) - .catch((err) => { - const msg = err?.response?.data?.message || "Something went wrong!"; - swal({ - title: err, - text: msg, - icon: "error", - button: "Retry", - dangerMode: true, - }); - setLoading(false); }); - - setLoading(false); + setProductsData(response.data?.products || []); + setTotalData(response.data?.total_data || 0); + } catch (err) { + const msg = err?.response?.data?.msg || "Something went wrong!"; + swal({ + title: "Error", + text: msg, + icon: "error", + button: "Retry", + dangerMode: true, + }); + } finally { + setLoading(false); + } }; const getCatagories = () => { @@ -101,6 +90,18 @@ const Products = () => { getProductsData(); }, [success, itemPerPage, currentPage]); + const debouncedSearch = useCallback( + debounce(() => { + setCurrentPage(1); + getProductsData(); + }, 500), + [] + ); + + const handleSearchChange = () => { + debouncedSearch(); + }; + const handleDelete = (id) => { swal({ title: "Are you sure?", @@ -142,47 +143,6 @@ const Products = () => { } }); }; - const handleFeaturedProduct = (id) => { - swal({ - title: "Are you sure?", - icon: "warning", - buttons: { - Yes: { text: "Yes", value: true }, - Cancel: { text: "Cancel", value: "cancel" }, - }, - }).then((value) => { - if (value === true) { - axios - .patch(`/api/product/admin/feature_product/status/${id}`, { - headers: { - "Access-Control-Allow-Origin": "*", - Authorization: `Bearer ${token}`, - }, - }) - .then((res) => { - swal({ - title: "Changed", - text: " Feature Product status changed successfully!", - icon: "success", - button: "ok", - }); - setSuccess((prev) => !prev); - }) - .catch((err) => { - let msg = err?.response?.data?.msg - ? err?.response?.data?.msg - : "Something went wrong!"; - swal({ - title: "Warning", - text: msg, - icon: "warning", - button: "ok", - dangerMode: true, - }); - }); - } - }); - }; const handleStatus = (id) => { swal({ title: "Are you sure?", @@ -243,17 +203,21 @@ const Products = () => { + @@ -292,6 +256,7 @@ const Products = () => { placeholder="product name" className="form-control" ref={nameRef} + onChange={handleSearchChange} disabled={loading} /> @@ -300,6 +265,7 @@ const Products = () => { -
- - -
-
- -
@@ -349,7 +291,6 @@ const Products = () => { Image Product Category - Feature Product Price Status @@ -397,23 +338,6 @@ const Products = () => { ? product.category?.categoryName : "Category Not selected "} - - - - - {currencyDetails?.CurrencySymbol} {product?.price}