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.
+
+
+
+ Principal Distributor Name |
+ Email |
+ Phone |
+ Pan |
+ GST |
+ Message |
+
+
+
+ {errors.map((error, index) => (
+
+ {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.
+
+
+
+ Product Name |
+ Category |
+ GST |
+ Message |
+
+
+
+ {errors.map((error, index) => (
+
+ {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}
|