From 0a4c95727402488123780808926020f5557f47ac Mon Sep 17 00:00:00 2001 From: Sibunnayak Date: Fri, 30 Aug 2024 16:35:50 +0530 Subject: [PATCH] PD mapped with TM and SC mapped with TM and product manual --- src/_nav.js | 7 + src/components/ProtectedRoute.js | 6 +- src/routes.js | 30 +- src/views/ProductManual/ProductManual.js | 652 ++++++++++++++++++ .../ProductManual/SingleProductManual.js | 107 +++ .../TerritoryManager/TerritoryManager.js | 28 + .../ViewPrincipalDistributorTM.js | 548 +++++++++++++++ .../ViewSalesCoOrdinatorTM.js | 546 +++++++++++++++ 8 files changed, 1922 insertions(+), 2 deletions(-) create mode 100644 src/views/ProductManual/ProductManual.js create mode 100644 src/views/ProductManual/SingleProductManual.js create mode 100644 src/views/TerritoryManager/ViewPrincipalDistributorTM.js create mode 100644 src/views/TerritoryManager/ViewSalesCoOrdinatorTM.js diff --git a/src/_nav.js b/src/_nav.js index bae00b7..472839f 100644 --- a/src/_nav.js +++ b/src/_nav.js @@ -68,6 +68,13 @@ const _nav = [ }, ], }, + { + component: CNavItem, + name: "ProductManual", + icon: , + to: "/product-manual", + group: "Product Management", + }, { component: CNavItem, name: "Principal Distributor", diff --git a/src/components/ProtectedRoute.js b/src/components/ProtectedRoute.js index 421c151..63a755c 100644 --- a/src/components/ProtectedRoute.js +++ b/src/components/ProtectedRoute.js @@ -69,7 +69,11 @@ const ProtectedRoute = ({ element: Element }) => { } } - checkToken() + checkToken(); + const intervalId = setInterval(checkToken, 1 * 60 * 1000); + + // Clear interval on component unmount + return () => clearInterval(intervalId); }, [navigate]) return diff --git a/src/routes.js b/src/routes.js index 7d36ad9..0bd0f4f 100644 --- a/src/routes.js +++ b/src/routes.js @@ -33,7 +33,8 @@ import Products from "./views/Products/Products"; import AddProduct from "./views/Products/AddProduct"; import EditProduct from "./views/Products/EditProduct"; import ViewProduct from "./views/Products/ViewProduct"; - +//product manual +import ProductManual from "./views/ProductManual/ProductManual"; //Order Management import NewOrders from "./views/orders/NewOrders.js"; import ProcessingOrders from "./views/orders/ProcessingOrders.js"; @@ -142,6 +143,9 @@ import AddMultipleProduct from "./views/Products/AddMultipleProducts"; import AddMultiplePd from "./views/PrincipalDistributors/AddMultiplePD"; import Inventory from "./views/Inventory/Inventory"; import SingleInventory from "./views/Inventory/SingleInventory"; +import ViewProductManual from "./views/ProductManual/SingleProductManual"; +import ViewSalesCoOrdinatorTM from "./views/TerritoryManager/ViewSalesCoOrdinatorTM"; +import ViewPrincipalDistributorTM from "./views/TerritoryManager/ViewPrincipalDistributorTM"; const routes = [ //dashboard @@ -202,6 +206,18 @@ const routes = [ element: Brands, navName: "Product Management", }, + { + path: "/product-manual", + name: "Product Manual", + element: ProductManual, + navName: "Product Management", + }, + { + path: "/product-manual/view/:id", + name: "Product Manual", + element: ViewProductManual, + navName: "Product Management", + }, //SalesCoOrdinator { path: "/salescoordinator/edit/:id", @@ -240,6 +256,18 @@ const routes = [ element: AddTerritoryManager, navName: "TerritoryManagers", }, + { + path: "/view/salescoordinator/:id", + name: "View SalesCoOrdinator", + element: ViewSalesCoOrdinatorTM, + navName: "TerritoryManagers", + }, + { + path: "/view/principaldistributor/:id", + name: "View Principal Distributor", + element: ViewPrincipalDistributorTM, + navName: "TerritoryManagers", + }, // Attendence { path: "/attendance/today", diff --git a/src/views/ProductManual/ProductManual.js b/src/views/ProductManual/ProductManual.js new file mode 100644 index 0000000..d4c226d --- /dev/null +++ b/src/views/ProductManual/ProductManual.js @@ -0,0 +1,652 @@ +import React, { useState, useEffect, useCallback, useRef } from "react"; +import axios from "axios"; +import { Link } from "react-router-dom"; +import { isAutheticated } from "src/auth"; +import { + Button, + Box, + IconButton, + Modal, + TextField, + Typography, +} from "@mui/material"; +import CloseIcon from "@mui/icons-material/Close"; +import { ClipLoader } from "react-spinners"; +import swal from "sweetalert"; +import { useNavigate } from "react-router-dom"; +import debounce from "lodash/debounce"; + +const style = { + position: "absolute", + top: "50%", + left: "50%", + transform: "translate(-50%, -50%)", + width: 500, + bgcolor: "background.paper", + borderRadius: "0.5rem", + boxShadow: 24, +}; + +const ProductManual = () => { + const token = isAutheticated(); + const navigate = useNavigate(); + const [loading, setLoading] = useState(true); + const [updating, setUpdating] = useState(true); + const [saveLoding, setSaveLoading] = useState(true); + const [edit, setEdit] = useState(false); + const [title, setTitle] = useState(""); + const [pdfFile, setPdfFile] = useState(null); + const [itemPerPage, setItemPerPage] = useState(10); + const [currentPage, setCurrentPage] = useState(1); + const [open, setOpen] = useState(false); + const [currentManual, setCurrentManual] = useState(null); + const [productManuals, setProductManuals] = useState([]); + const [fileError, setFileError] = useState(""); + const [totalData, setTotalData] = useState(0); + const [success, setSuccess] = useState(true); + const titleRef = useRef(); + // const handleOpen = (manual = null) => { + // setOpen(true); + // setCurrentManual(manual); + // if (manual) { + // setTitle(manual.title); + // setPdfFile(null); + // setEdit(true); + // } else { + // setTitle(""); + // setPdfFile(null); + // setEdit(false); + // } + // }; + const handleOpen = () => setOpen(true); + const handleClose = () => { + setOpen(false); + setEdit(false); + setTitle(""); + setPdfFile(null); + setFileError(""); + }; + const handleEditClick = (manual) => { + setOpen(true); + setCurrentManual(manual); + setTitle(manual.title); + setPdfFile(null); + setEdit(true); + }; + const getProductManuals = async () => { + try { + setLoading(true); + const response = await axios.get(`/api/productmanual`, { + headers: { Authorization: `Bearer ${token}` }, + params: { + page: currentPage, + show: itemPerPage, + title: titleRef.current?.value || "", + }, + }); + if (response.status === 200) { + const { productManuals, total } = response.data; + setProductManuals(productManuals); + setTotalData(total); + setLoading(false); + } + } catch (error) { + console.error("Failed to fetch product manuals:", error); + setLoading(false); + } + }; + + useEffect(() => { + getProductManuals(); + }, [itemPerPage, currentPage, success]); + + const validateFile = (file) => { + return file && file.type === "application/pdf"; + }; + + const handleSave = async () => { + if (!title || !pdfFile || !validateFile(pdfFile)) { + setFileError("Please upload a valid PDF file!"); + return; + } + setFileError(""); + + setSaveLoading(false); + const formData = new FormData(); + formData.append("title", title); + formData.append("pdfFile", pdfFile); + + try { + await axios.post("/api/productmanual/create", formData, { + headers: { + Authorization: `Bearer ${token}`, + "Content-Type": "multipart/form-data", + }, + }); + handleClose(); + swal("Success", "New product manual added successfully!", "success"); + getProductManuals(); + } catch (error) { + swal("Error", "Failed to add product manual", "error"); + } finally { + setSaveLoading(true); + } + }; + + const handleUpdate = async () => { + if (!title) { + setFileError("Please upload a valid PDF file!"); + return; + } + setFileError(""); + + setUpdating(false); + const formData = new FormData(); + formData.append("title", title); + formData.append("pdfFile", pdfFile); + + try { + await axios.put( + `/api/productmanual/update/${currentManual._id}`, + formData, + { + headers: { Authorization: `Bearer ${token}` }, + } + ); + handleClose(); + setSuccess((prev) => !prev); + swal( + "Success", + "The product manual was updated successfully!", + "success" + ); + getProductManuals(); + } catch (err) { + swal("Error", "Failed to update product manual", "error"); + } finally { + setUpdating(true); + } + }; + + const handleDelete = async (_id) => { + swal({ + title: "Are you sure?", + icon: "warning", + buttons: { + Yes: { text: "Yes", value: true }, + Cancel: { text: "Cancel", value: "cancel" }, + }, + }).then(async (value) => { + if (value === true) { + try { + await axios.delete(`/api/productmanual/delete/${_id}`, { + headers: { Authorization: `Bearer ${token}` }, + }); + setSuccess((prev) => !prev); + swal( + "Success", + "The product manual was deleted successfully!", + "success" + ); + getProductManuals(); + } catch (err) { + swal("Error", "Failed to delete product manual", "error"); + } + } + }); + }; + + const debouncedSearch = useCallback( + debounce(() => { + setCurrentPage(1); + getProductManuals(); + }, 500), + [] + ); + + const handleSearchChange = (e) => { + debouncedSearch(); + }; + + return ( +
+
+
+
+
+
+
+ Product Manual +
+
+ +
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+ + +
+
+ {/* Modal for Add/Edit Product Manual */} + + + + + {edit + ? "Edit Product Manual" + : "Add New Product Manual"} + + + + + + + setTitle(e.target.value)} + /> + setPdfFile(e.target.files[0])} + /> + + + + {!edit && ( + + )} + {edit && ( + + )} + + + + +
+ + + + + + + + + + + + {loading ? ( + + + + ) : productManuals?.length > 0 ? ( + productManuals?.map((manual, i) => { + return ( + + + + + + + ); + }) + ) : ( + !loading && + productManuals?.length === 0 && ( + + + + ) + )} + +
TitleFile NameUpdated DateActions
+ Loading... +
{manual.title} + {manual.product_manual.filename} + + {new Date(manual.updatedAt).toLocaleString( + "en-IN", + { + weekday: "short", + month: "short", + day: "numeric", + year: "numeric", + hour: "numeric", + minute: "numeric", + hour12: true, + } + )} + + + + + + + + + + +
+
No Product manual Available...
+
+
+ +
+
+
+ Showing {currentPage * itemPerPage - itemPerPage + 1} to{" "} + {Math.min(currentPage * itemPerPage, totalData)} of{" "} + {totalData} entries +
+
+ +
+
+
    +
  • + setCurrentPage((prev) => prev - 1)} + disabled={loading} + > + Previous + +
  • + + {!(currentPage - 1 < 1) && ( +
  • + + setCurrentPage((prev) => prev - 1) + } + disabled={loading} + > + {currentPage - 1} + +
  • + )} + +
  • + + {currentPage} + +
  • + + {!( + (currentPage + 1) * itemPerPage - itemPerPage > + totalData - 1 + ) && ( +
  • + { + setCurrentPage((prev) => prev + 1); + }} + disabled={loading} + > + {currentPage + 1} + +
  • + )} + +
  • + totalData - 1 + ) + ? "paginate_button page-item next" + : "paginate_button page-item next disabled" + } + > + setCurrentPage((prev) => prev + 1)} + disabled={loading} + > + Next + +
  • +
+
+
+
+
+
+
+
+
+
+
+ ); +}; + +export default ProductManual; diff --git a/src/views/ProductManual/SingleProductManual.js b/src/views/ProductManual/SingleProductManual.js new file mode 100644 index 0000000..0f0787e --- /dev/null +++ b/src/views/ProductManual/SingleProductManual.js @@ -0,0 +1,107 @@ +import React, { useEffect, useState } from "react"; +import Button from "@material-ui/core/Button"; +import { Link, useParams } from "react-router-dom"; +import swal from "sweetalert"; +import axios from "axios"; +import { isAutheticated } from "src/auth"; + +const ViewProductManual = () => { + const [title, setTitle] = useState(""); + const [image, setImage] = useState(""); + const token = isAutheticated(); + const { id } = useParams(); + + const getproductmanual = async () => { + try { + const res = await axios.get(`/api/productmanual/${id}`, { + headers: { Authorization: `Bearer ${token}` }, + }); + // console.log(res); + setTitle(res?.data?.productManual?.title); + setImage(res?.data?.productManual?.product_manual); + } catch (err) { + console.error(err); + swal({ + title: "Error", + text: "Unable to fetch the blog", + icon: "error", + button: "Retry", + dangerMode: true, + }); + } + }; + + useEffect(() => { + getproductmanual(); + }, []); + + return ( +
+
+
+
+
+ View Product Manual +
+
+

+
+
+ + + +
+
+
+
+
+
+
+
+

+ {title} +

+
+ {image && ( + image.url.endsWith('.pdf') ? ( +