diff --git a/package.json b/package.json index 8a700fc..2f909ca 100644 --- a/package.json +++ b/package.json @@ -41,19 +41,22 @@ "@emotion/styled": "^11.11.0", "@material-ui/core": "^4.12.4", "@material-ui/data-grid": "^4.0.0-alpha.37", - "@mui/icons-material": "^5.14.13", + "@mui/icons-material": "^5.14.14", "@mui/material": "^5.11.12", "@reduxjs/toolkit": "^1.9.2", "axios": "^0.25.0", "bootstrap": "^5.1.3", "country-state-city": "^3.1.2", + "md5": "^2.3.0", "prop-types": "^15.7.2", + "quill": "^1.3.7", "react": "18.0.0", "react-bootstrap": "^2.7.0", "react-datepicker": "^4.8.0", "react-dom": "^18.0.0", "react-hot-toast": "^2.4.0", "react-qr-code": "^2.0.11", + "react-quill": "^2.0.0", "react-redux": "^7.2.9", "react-router-dom": "^6.7.0", "react-spinners": "^0.11.0", @@ -67,6 +70,7 @@ }, "devDependencies": { "auto-changelog": "~2.3.0", + "fuse.js": "^6.6.2", "react-scripts": "^4.0.3", "sass": "^1.43.5" }, diff --git a/public/index.html b/public/index.html index 057fb7e..292776e 100644 --- a/public/index.html +++ b/public/index.html @@ -7,26 +7,37 @@ * License MIT --> - - - - - - - - - SOLAR Sign Admin - - - - - - - - + - + - - -
- - - - - - - \ No newline at end of file + + + diff --git a/src/_nav.js b/src/_nav.js index c386d73..55acafa 100644 --- a/src/_nav.js +++ b/src/_nav.js @@ -107,6 +107,12 @@ const _nav = [ icon: , to: "/contact/request", }, + { + component: CNavItem, + name: "Content ", + icon: , + to: "/content", + }, ], }, { diff --git a/src/components/AppSidebar.js b/src/components/AppSidebar.js index 105f60f..5391b3b 100644 --- a/src/components/AppSidebar.js +++ b/src/components/AppSidebar.js @@ -1,38 +1,43 @@ -import React, { useEffect, useState } from 'react' -import { useSelector, useDispatch } from 'react-redux' +import React, { useEffect, useState } from "react"; +import { useSelector, useDispatch } from "react-redux"; -import { CSidebar, CSidebarBrand, CSidebarNav, CSidebarToggler } from '@coreui/react' -import CIcon from '@coreui/icons-react' +import { + CSidebar, + CSidebarBrand, + CSidebarNav, + CSidebarToggler, +} from "@coreui/react"; +import CIcon from "@coreui/icons-react"; -import { AppSidebarNav } from './AppSidebarNav' +import { AppSidebarNav } from "./AppSidebarNav"; -import { logoNegative } from 'src/assets/brand/logo-negative' -import { sygnet } from 'src/assets/brand/sygnet' +import { logoNegative } from "src/assets/brand/logo-negative"; +import { sygnet } from "src/assets/brand/sygnet"; -import SimpleBar from 'simplebar-react' -import 'simplebar/dist/simplebar.min.css' +import SimpleBar from "simplebar-react"; +import "simplebar/dist/simplebar.min.css"; // sidebar nav config -import navigation from '../_nav' -import { isAutheticated } from 'src/auth' -import axios from 'axios' -import { Link } from 'react-router-dom' +import navigation from "../_nav"; +import { isAutheticated } from "src/auth"; +import axios from "axios"; +import { Link } from "react-router-dom"; const AppSidebar = () => { - const dispatch = useDispatch() - const unfoldable = useSelector((state) => state.sidebarUnfoldable) - const sidebarShow = useSelector((state) => state.sidebarShow) + const dispatch = useDispatch(); + const unfoldable = useSelector((state) => state.sidebarUnfoldable); + const sidebarShow = useSelector((state) => state.sidebarShow); ///----------------------// - const [loading, setLoading] = useState(false) + const [loading, setLoading] = useState(false); - const token = isAutheticated() + const token = isAutheticated(); // urlcreated images - const [AppName, setAppName] = useState('') - const [HeaderlogoUrl, setHeaderlogoUrl] = useState('') - const [FooterlogoUrl, setFooterlogoUrl] = useState('') - const [AdminlogoUrl, setAdminlogoUrl] = useState('') + const [AppName, setAppName] = useState(""); + const [HeaderlogoUrl, setHeaderlogoUrl] = useState(""); + const [FooterlogoUrl, setFooterlogoUrl] = useState(""); + const [AdminlogoUrl, setAdminlogoUrl] = useState(""); useEffect(() => { async function getConfiguration() { @@ -40,16 +45,16 @@ const AppSidebar = () => { headers: { Authorization: `Bearer ${token}`, }, - }) - setAppName(configDetails.data.result[0]?.appName) + }); + setAppName(configDetails.data.result[0]?.appName); configDetails.data.result.map((item) => { - setHeaderlogoUrl(item?.logo[0]?.Headerlogo) - setFooterlogoUrl(item?.logo[0]?.Footerlogo) - setAdminlogoUrl(item?.logo[0]?.Adminlogo) - }) + setHeaderlogoUrl(item?.logo[0]?.Headerlogo); + setFooterlogoUrl(item?.logo[0]?.Footerlogo); + setAdminlogoUrl(item?.logo[0]?.Adminlogo); + }); } - getConfiguration() - }, []) + getConfiguration(); + }, []); //---------------------------// return ( @@ -58,13 +63,25 @@ const AppSidebar = () => { unfoldable={unfoldable} visible={sidebarShow} onVisibleChange={(visible) => { - dispatch({ type: 'set', sidebarShow: visible }) + dispatch({ type: "set", sidebarShow: visible }); }} > - + {/* */} - {HeaderlogoUrl ? : { AppName } ?

Solar Sign

: ''} + {HeaderlogoUrl ? ( + + + + ) : { AppName } ? ( +

The Solar Sign

+ ) : ( + "" + )} {/* */}
@@ -75,10 +92,12 @@ const AppSidebar = () => { dispatch({ type: 'set', sidebarUnfoldable: !unfoldable })} + onClick={() => + dispatch({ type: "set", sidebarUnfoldable: !unfoldable }) + } /> - ) -} + ); +}; -export default React.memo(AppSidebar) +export default React.memo(AppSidebar); diff --git a/src/routes.js b/src/routes.js index 82e182a..1f9d7e0 100644 --- a/src/routes.js +++ b/src/routes.js @@ -83,6 +83,11 @@ import ViewHealthCareProvider from "./views/Business/ViewHealthCareProvider"; import Campaign from "./views/Campaigns/Campaign.js"; import AddCampaign from "./views/Campaigns/AddCampaign.js"; import Categories from "./views/Categories/categories"; +import Content from "./views/Content/content"; + +import EditPrivacyPolicy from "./views/Content/editPrivacyPolicy"; +import EditTermsConditions from "./views/Content/editTermsConditions"; +import EditShippingPolicy from "./views/Content/editShippingPolicy"; const routes = [ { path: "/", exact: true, name: "Home" }, { @@ -183,6 +188,30 @@ const routes = [ name: "AddContact Request", element: AddContactRequest, }, + + // Content ---- > + + { + path: "/content", + name: "Content", + element: Content, + }, + { + path: "/content/terms-and-conditions", + name: "Content", + element: EditTermsConditions, + }, + { + path: "/content/privacy-policy", + name: "Content", + element: EditPrivacyPolicy, + }, + { + path: "/content/shipping-policy", + name: "Content", + element: EditShippingPolicy, + }, + // { path: '/complaint/view/:id', name: 'view Complain', element: ViewComplaint }, //Complaints { path: "/testimonials", name: "Testimonials", element: Testimonials }, diff --git a/src/scss/style.scss b/src/scss/style.scss index 7e07c71..f02ccd6 100644 --- a/src/scss/style.scss +++ b/src/scss/style.scss @@ -3,7 +3,6 @@ $enable-ltr: true; $enable-rtl: true; - // Import CoreUI for React components library //@import "@coreui/coreui/scss/coreui"; // Import Chart.js custom tooltips styles @@ -14,4 +13,10 @@ $enable-rtl: true; @import "example"; // If you want to add custom CSS you can put it here. -@import "custom"; \ No newline at end of file +@import "custom"; + +.container .ql-editor { + min-height: 5in; + + background-color: white; +} diff --git a/src/views/Categories/categories.js b/src/views/Categories/categories.js index 5e16b04..a9342e0 100644 --- a/src/views/Categories/categories.js +++ b/src/views/Categories/categories.js @@ -277,11 +277,23 @@ const Categories = () => { placeholder="category name" value={categoryName} fullWidth + inputProps={{ + maxLength: 25, + }} style={{ padding: "1rem", }} onChange={(e) => setCategoryName(e.target.value)} /> + {categoryName ? ( + <> + + {25 - categoryName.length} characters left + + + ) : ( + <> + )} + + Content + + + + + + Page + + Action + + + + + {pages.map((row) => ( + + + {row.name} + + + {" "} + + + + + + ))} + +
+
+ + ); +} diff --git a/src/views/Content/editPrivacyPolicy.js b/src/views/Content/editPrivacyPolicy.js new file mode 100644 index 0000000..61d8dc2 --- /dev/null +++ b/src/views/Content/editPrivacyPolicy.js @@ -0,0 +1,160 @@ +import { Typography } from "@material-ui/core"; +import { Box, Button, TextField } from "@mui/material"; +import React, { useEffect, useState } from "react"; +import ReactrichTextEditor from "./reactrichTextEditor"; +import ReactQuill from "react-quill"; +import "react-quill/dist/quill.snow.css"; +import axios from "axios"; +import { isAutheticated } from "src/auth"; +import { useNavigate, useNavigation } from "react-router-dom"; + +const TOOLBAR_OPTIONS = [ + [{ header: [1, 2, 3, 4, 5, 6, false] }], + [{ font: [] }], + [{ list: "ordered" }, { list: "bullet" }], + ["bold", "italic", "underline"], + [{ color: [] }, { background: [] }], + [{ align: [] }], +]; +export default function EditPrivacyPolicy() { + const [title, setTitle] = useState("Privacy Policy"); + const [content, setContent] = useState(""); + const [added, setAdded] = useState(false); + const [olderContent, setOlderContent] = useState(""); + const navigation = useNavigate(); + + const token = isAutheticated(); + const handleContentChange = (content, delta, source, editor) => { + setContent(editor.getHTML()); + }; + const getPrivacyPolicy = async () => { + const response = await axios.get("/api/content/privacy-and-policy", { + headers: { + Authorization: `Bearer ${token}`, + }, + }); + if (response.status === 200) { + // console.log(response); + + setContent(response?.data?.privacyAndPolicy[0]?.privacyAndPolicyContent); + setOlderContent( + response?.data?.privacyAndPolicy[0]?.privacyAndPolicyContent + ); + } + }; + + const addPrivacyPolicy = async () => { + const response = await axios.post( + "/api/content/privacy-and-policy", + { content }, + { + headers: { + Authorization: `Bearer ${token}`, + }, + } + ); + if (response.status == 201) { + swal({ + title: "Congratulations!!", + text: "Terms and condition added successfully!", + icon: "success", + button: "OK", + }); + } + }; + const handleCancelClick = () => { + setContent(olderContent); + }; + const updateContent = async () => { + const response = await axios.patch( + "/api/content/privacy-and-policy-update", + { content }, + { + headers: { + Authorization: `Bearer ${token}`, + }, + } + ); + if (response.status === 200) { + swal({ + title: "Congratulations!!", + text: "Terms and condition updated successfully!", + icon: "success", + button: "OK", + }); + } else { + swal({ + title: "Sorry, please try again", + text: "Something went wrong!", + icon: "error", + button: "Retry", + dangerMode: true, + }); + } + }; + const handleSaveClick = async () => { + if (olderContent === undefined) { + await addPrivacyPolicy(); + setAdded(true); + } else { + await updateContent(); + setAdded(false); + } + + // Reload terms and conditions + await getPrivacyPolicy(); + }; + useEffect(() => { + // addTermsandConditions(); + getPrivacyPolicy(); + }, [added]); + return ( +
+
+ + +
+ + + + {" "} + Privacy and policy:{" "} + + Body + + +
+ ); +} diff --git a/src/views/Content/editShippingPolicy.js b/src/views/Content/editShippingPolicy.js new file mode 100644 index 0000000..5c26fe6 --- /dev/null +++ b/src/views/Content/editShippingPolicy.js @@ -0,0 +1,166 @@ +import { Typography } from "@material-ui/core"; +import { Box, Button, TextField } from "@mui/material"; +import React, { useEffect, useState } from "react"; +import ReactrichTextEditor from "./reactrichTextEditor"; +import ReactQuill from "react-quill"; +import "react-quill/dist/quill.snow.css"; +import axios from "axios"; +import { isAutheticated } from "src/auth"; +import { useNavigate, useNavigation } from "react-router-dom"; + +const TOOLBAR_OPTIONS = [ + [{ header: [1, 2, 3, 4, 5, 6, false] }], + [{ font: [] }], + [{ list: "ordered" }, { list: "bullet" }], + ["bold", "italic", "underline"], + [{ color: [] }, { background: [] }], + [{ align: [] }], +]; +export default function EditShippingPolicy() { + const [title, setTitle] = useState("Shipping Policy"); + const [content, setContent] = useState(""); + const [added, setAdded] = useState(false); + const [olderContent, setOlderContent] = useState(""); + const navigation = useNavigate(); + + const token = isAutheticated(); + const handleContentChange = (content, delta, source, editor) => { + setContent(editor.getHTML()); + }; + const getShipping = async () => { + const response = await axios.get("/api/content/shipping-and-policy", { + headers: { + Authorization: `Bearer ${token}`, + }, + }); + if (response.status === 200) { + // console.log(response); + + setContent(response?.data?.shipping[0]?.shippingContent); + setOlderContent(response?.data?.shipping[0]?.shippingContent); + } + }; + + const addShipping = async () => { + const response = await axios.post( + "/api/content/shipping-and-policy", + { content }, + { + headers: { + Authorization: `Bearer ${token}`, + }, + } + ); + if (response.status == 201) { + swal({ + title: "Congratulations!!", + text: "Terms and condition added successfully!", + icon: "success", + button: "OK", + }); + } + }; + const handleCancelClick = () => { + setContent(olderContent); + }; + const updateContent = async () => { + const response = await axios.patch( + "/api/content/shipping-and-policy-update", + { content }, + { + headers: { + Authorization: `Bearer ${token}`, + }, + } + ); + if (response.status === 200) { + swal({ + title: "Congratulations!!", + text: "Terms and condition updated successfully!", + icon: "success", + button: "OK", + }); + } else { + swal({ + title: "Sorry, please try again", + text: "Something went wrong!", + icon: "error", + button: "Retry", + dangerMode: true, + }); + } + }; + const handleSaveClick = async () => { + if (olderContent === undefined) { + await addShipping(); + setAdded(true); + } else { + await updateContent(); + setAdded(false); + } + + // Reload terms and conditions + await getShipping(); + }; + useEffect(() => { + // addTermsandConditions(); + getShipping(); + }, [added]); + return ( +
+
+ + +
+ + + {/* setTitle(e.target.value)} + variant="outlined" + size="small" + fullWidth + /> */} + + {" "} + Shipping and policy:{" "} + + Body + + +
+ ); +} diff --git a/src/views/Content/editTermsConditions.js b/src/views/Content/editTermsConditions.js new file mode 100644 index 0000000..0aa7c02 --- /dev/null +++ b/src/views/Content/editTermsConditions.js @@ -0,0 +1,167 @@ +import { Typography } from "@material-ui/core"; +import { Box, Button, TextField } from "@mui/material"; +import React, { useEffect, useState } from "react"; +import ReactrichTextEditor from "./reactrichTextEditor"; +import ReactQuill from "react-quill"; +import "react-quill/dist/quill.snow.css"; +import axios from "axios"; +import { isAutheticated } from "src/auth"; +import { useNavigate, useNavigation } from "react-router-dom"; + +const TOOLBAR_OPTIONS = [ + [{ header: [1, 2, 3, 4, 5, 6, false] }], + [{ font: [] }], + [{ list: "ordered" }, { list: "bullet" }], + ["bold", "italic", "underline"], + [{ color: [] }, { background: [] }], + [{ align: [] }], +]; +export default function EditTermsConditions() { + const [title, setTitle] = useState("Terms and conditions"); + const [content, setContent] = useState(""); + const [added, setAdded] = useState(false); + const [olderContent, setOlderContent] = useState(""); + const navigation = useNavigate(); + + const token = isAutheticated(); + const handleContentChange = (content, delta, source, editor) => { + setContent(editor.getHTML()); + }; + const getTermsAndConditions = async () => { + const response = await axios.get("/api/content/terms-and-conditions", { + headers: { + Authorization: `Bearer ${token}`, + }, + }); + if (response.status === 200) { + // console.log(response); + setContent(response?.data?.termsAndCondition[0]?.termsAndContionContent); + setOlderContent( + response?.data?.termsAndCondition[0]?.termsAndContionContent + ); + } + }; + + const addTermsandConditions = async () => { + const response = await axios.post( + "/api/content/terms-and-conditions", + { content }, + { + headers: { + Authorization: `Bearer ${token}`, + }, + } + ); + if (response.status == 201) { + swal({ + title: "Congratulations!!", + text: "Terms and condition added successfully!", + icon: "success", + button: "OK", + }); + } + }; + const handleCancelClick = () => { + setContent(olderContent); + }; + const updateContent = async () => { + const response = await axios.patch( + "/api/content/terms-and-condition-update", + { content }, + { + headers: { + Authorization: `Bearer ${token}`, + }, + } + ); + if (response.status === 200) { + swal({ + title: "Congratulations!!", + text: "Terms and condition updated successfully!", + icon: "success", + button: "OK", + }); + } else { + swal({ + title: "Sorry, please try again", + text: "Something went wrong!", + icon: "error", + button: "Retry", + dangerMode: true, + }); + } + }; + const handleSaveClick = async () => { + if (olderContent === undefined) { + await addTermsandConditions(); + setAdded(true); + } else { + await updateContent(); + setAdded(false); + } + + // Reload terms and conditions + await getTermsAndConditions(); + }; + useEffect(() => { + // addTermsandConditions(); + getTermsAndConditions(); + }, [added]); + return ( +
+
+ + +
+ + + {/* setTitle(e.target.value)} + variant="outlined" + size="small" + fullWidth + /> */} + + {" "} + Terms and policy:{" "} + + Body + + +
+ ); +} diff --git a/src/views/Content/reactrichTextEditor.js b/src/views/Content/reactrichTextEditor.js new file mode 100644 index 0000000..a3f05f6 --- /dev/null +++ b/src/views/Content/reactrichTextEditor.js @@ -0,0 +1,33 @@ +import React, { useCallback, useEffect, useRef, useState } from "react"; +import ReactQuill from "react-quill"; + +const TOOLBAR_OPTIONS = [ + [{ header: [1, 2, 3, 4, 5, 6, false] }], + [{ font: [] }], + [{ list: "ordered" }, { list: "bullet" }], + ["bold", "italic", "underline"], + [{ color: [] }, { background: [] }], + [{ align: [] }], +]; +export default function ReactrichTextEditor() { + const [content, setContent] = useState(""); + const handleContentChange = (content, delta, source, editor) => { + setContent(editor.getHTML()); + }; + // const wrapperRef = useCallback((wrapper) => { + // if (wrapper == null) return; + // wrapper.innerHTML = ""; + // const editor = document.createElement("div"); + // wrapper.append(editor); + // new Quill(editor, { theme: "snow", modules: { toolbar: TOOLBAR_OPTIONS } }); + // }, []); + // return
; + return ( + + ); +} diff --git a/src/views/Content/texteditorstyle.css b/src/views/Content/texteditorstyle.css new file mode 100644 index 0000000..e69de29 diff --git a/src/views/Products/AddProduct.js b/src/views/Products/AddProduct.js index ed2d730..2726161 100644 --- a/src/views/Products/AddProduct.js +++ b/src/views/Products/AddProduct.js @@ -4,6 +4,16 @@ import { Link, useNavigate } from "react-router-dom"; import swal from "sweetalert"; import axios from "axios"; import { isAutheticated } from "src/auth"; +import CloudUploadIcon from "@mui/icons-material/CloudUpload"; +import DeleteSharpIcon from "@mui/icons-material/DeleteSharp"; +import { + Box, + FormControl, + IconButton, + MenuItem, + Select, + TextField, +} from "@mui/material"; // import { WebsiteURL } from '../WebsiteURL' const AddProduct = () => { @@ -15,33 +25,50 @@ const AddProduct = () => { const [categories, setCategoies] = useState([]); const [imagesPreview, setImagesPreview] = useState([]); - const [allimage, setAllImage] = useState([]); + // const [allimage, setAllImage] = useState([]); + const [name, setName] = useState(""); + const [description, setDescription] = useState(""); + const [productImages, setProductImages] = useState([]); + const [price, setPrice] = useState(""); + const [categoryName, setCategoryName] = useState(""); + const [error, setError] = useState(""); - useEffect(() => { - 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); + const handleFileChange = (e) => { + const files = e.target.files; + + // Check the total number of selected files + if (productImages.length + files.length > 4) { + setError("You can only upload up to 4 images."); + return; + } + + // Check file types and append to selectedFiles + const allowedTypes = ["image/jpeg", "image/png", "image/jpg"]; + const selected = []; + + for (let i = 0; i < files.length; i++) { + if (productImages.length + selected.length >= 4) { + break; // Don't allow more than 4 images } - }; - getAllTax(); - getCategories(); - }, [token]); - const [data, setData] = useState({ - image: [], - imageURL: [], - name: "", - description: "", - price: "", - category: "", - }); + if (allowedTypes.includes(files[i].type)) { + selected.push(files[i]); + } + } + 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", { @@ -63,108 +90,118 @@ const AddProduct = () => { }); } }; - - 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; - } + // 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); } - setData((prev) => ({ ...prev, [e.target.id]: e.target.value })); }; + useEffect(() => { + getAllTax(); + getCategories(); + }, [token]); - const TaxRatechange = async (e) => { - let taxDetails = { - name: e.target.value.slice(12, 16), - rate: Number(e.target.value.slice(4, 6)), + // 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(); - taxId: e.target.value.slice(24), - }; + // reader.onload = () => { + // if (reader.readyState === 2) { + // setImagesPreview((old) => [...old, reader.result]); + // } + // }; - let trRate = taxDetails.rate / 100; - let PriceWithT = Number(data.price); - PriceWithT += +(PriceWithT * trRate).toFixed(); + // reader.readAsDataURL(file); + // }); + // // ----------------------------------------------------------------------------- - //price_Level_2_With_Tax - let price_Level_2_With_Tax = Number(data.price_Level_2); - price_Level_2_With_Tax += +(price_Level_2_With_Tax * trRate).toFixed(); - // - //price_Level_3_With_Tax - let price_Level_3_With_Tax = Number(data.price_Level_3); - price_Level_3_With_Tax += +(price_Level_3_With_Tax * trRate).toFixed(); - setData((prev) => ({ - ...prev, - price_With_Tax: PriceWithT, + // setData((prev) => ({ + // ...prev, - price_Level_2_With_Tax: price_Level_2_With_Tax, + // 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 })); + // }; - price_Level_3_With_Tax: price_Level_3_With_Tax, - taxId: taxDetails.taxId, - })); - }; + // const TaxRatechange = async (e) => { + // let taxDetails = { + // name: e.target.value.slice(12, 16), + // rate: Number(e.target.value.slice(4, 6)), + + // taxId: e.target.value.slice(24), + // }; + + // let trRate = taxDetails.rate / 100; + // let PriceWithT = Number(data.price); + // PriceWithT += +(PriceWithT * trRate).toFixed(); + + // //price_Level_2_With_Tax + // let price_Level_2_With_Tax = Number(data.price_Level_2); + // price_Level_2_With_Tax += +(price_Level_2_With_Tax * trRate).toFixed(); + // // + // //price_Level_3_With_Tax + // let price_Level_3_With_Tax = Number(data.price_Level_3); + // price_Level_3_With_Tax += +(price_Level_3_With_Tax * trRate).toFixed(); + // setData((prev) => ({ + // ...prev, + // price_With_Tax: PriceWithT, + + // price_Level_2_With_Tax: price_Level_2_With_Tax, + + // price_Level_3_With_Tax: price_Level_3_With_Tax, + // taxId: taxDetails.taxId, + // })); + // }; // console.log(data.image.length) const handleSubmit = () => { if ( - data.name.trim() === "" || - data.description.trim() === "" || - data.price === "" || - data.image === "" - // data.price_With_Tax === '' || - // data.price_Level_2 === '' || - // data.price_Level_2_With_Tax === '' || - // data.price_Level_3 === '' || - // data.price_Level_3_With_Tax === '' || - // data.imageURL.trim() === '' + name == "" || + description == "" || + productImages.length == 0 || + price == "" ) { swal({ title: "Warning", @@ -177,13 +214,13 @@ const AddProduct = () => { } setLoading(true); const formData = new FormData(); - formData.append("name", data.name); + formData.append("name", name); - formData.append("description", data.description); - formData.append("price", data.price); - formData.append("category", data.category); + formData.append("description", description); + formData.append("price", price); + formData.append("category", categoryName); - data.image.forEach((Singleimage) => { + productImages.forEach((Singleimage) => { // console.log(Singleimage) formData.append("image", Singleimage); }); @@ -220,8 +257,8 @@ const AddProduct = () => { }); }); }; - console.log(data); - + // console.log(data); + console.log(productImages); return (
@@ -285,14 +322,14 @@ const AddProduct = () => { type="text" className="form-control" id="name" - value={data.name} + value={name} maxLength={25} - onChange={(e) => handleChange(e)} + onChange={(e) => setName(e.target.value)} /> - {data.name ? ( + {name ? ( <> - {25 - data.name.length} characters left + {25 - name.length} characters left ) : ( @@ -308,14 +345,14 @@ const AddProduct = () => { type="text" className="form-control" id="description" - value={data.description} + value={description} maxLength="100" - onChange={(e) => handleChange(e)} + onChange={(e) => setDescription(e.target.value)} /> - {data.description ? ( + {description ? ( <> - {100 - data.description.length} characters left + {100 - description.length} characters left ) : ( @@ -327,21 +364,100 @@ const AddProduct = () => { - handleChange(e)} - /> + + + + + {error &&

{error}

}

Upload jpg, jpeg and png only*

+ + {productImages && + productImages.map((image, i) => ( + + profileImage + {/* handelDelete(image)} + sx={{ + position: "absolute", + fontSize: "small", + "&:hover": { + background: "black", + }, + background: "black", + }} + > */} + handelDelete(image)} + fontSize="small" + sx={{ + color: "white", + position: "absolute", + cursor: "pointer", + padding: "0.2rem", + background: "black", + borderRadius: "50%", + }} + /> + {/* */} + + ))} +
- *Please Upload maximum four images + *You cannot upload more than 4 images !!
@@ -363,23 +479,23 @@ const AddProduct = () => {
handleChange(e)} + value={price} + onChange={(e) => setPrice(e.target.value)} />
- + */} + + +
{allTax.length > 0 && ( diff --git a/src/views/Products/EditProduct.js b/src/views/Products/EditProduct.js index 2a1e94d..98cd1de 100644 --- a/src/views/Products/EditProduct.js +++ b/src/views/Products/EditProduct.js @@ -4,6 +4,17 @@ import { Link, useNavigate, useParams } from "react-router-dom"; import swal from "sweetalert"; import axios from "axios"; import { isAutheticated } from "src/auth"; + +import CloudUploadIcon from "@mui/icons-material/CloudUpload"; +import DeleteSharpIcon from "@mui/icons-material/DeleteSharp"; +import { + Box, + FormControl, + IconButton, + MenuItem, + Select, + TextField, +} from "@mui/material"; // import { WebsiteURL } from '../WebsiteURL' const EditProduct = () => { @@ -11,20 +22,18 @@ const EditProduct = () => { const token = isAutheticated(); const navigate = useNavigate(); - const [data, setData] = useState({ - image: [], - imageURL: [], - name: "", - description: "", - category: "", - - price: "", - }); const [loading, setLoading] = useState(false); const [allTax, setAllTax] = useState([]); const [categories, setCatgories] = useState([]); const [imagesPreview, setImagesPreview] = useState([]); + const [name, setName] = useState(""); + const [description, setDescription] = useState(""); + const [productImages, setProductImages] = useState([]); + const [price, setPrice] = useState(""); + const [categoryName, setCategoryName] = useState(""); + const [error, setError] = useState(""); + const [newUpdatedImages, setNewUpdatedImages] = useState([]); //get Productdata const getProduct = async () => { @@ -36,23 +45,22 @@ const EditProduct = () => { }, }) .then((res) => { - // console.log(res.data?.product?.image) - // if (res.data?.product?.image) { - // res.data?.product?.image.map(item => { - // }) - - // } - - // setImagesPreview(res.data?.product?.image) - setData((prev) => ({ - ...prev, - ...res.data?.product, - imageURL: res.data?.product?.image?.url, - })); + setName(res?.data?.product.name); + setDescription(res.data.product.description); + setProductImages(res.data.product.image); + setPrice(res.data.product.price); + setCategoryName(res.data.product.category); }) - .catch((err) => {}); + .catch((err) => { + swal({ + title: error, + text: " Can not fetch the product ", + icon: "error", + button: "Retry", + dangerMode: true, + }); + }); }; - // console.log(imagesPreview) const getCategories = async () => { try { @@ -95,101 +103,12 @@ const EditProduct = () => { getAllTax(); }, [token]); - 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 TaxRatechange = async (e) => { - let taxDetails = { - name: e.target.value.slice(12, 16), - rate: Number(e.target.value.slice(4, 6)), - - taxId: e.target.value.slice(24), - }; - - let trRate = taxDetails.rate / 100; - let PriceWithT = Number(data.price); - PriceWithT += +(PriceWithT * trRate).toFixed(); - - //price_Level_2_With_Tax - let price_Level_2_With_Tax = Number(data.price_Level_2); - price_Level_2_With_Tax += +(price_Level_2_With_Tax * trRate).toFixed(); - // - //price_Level_3_With_Tax - let price_Level_3_With_Tax = Number(data.price_Level_3); - price_Level_3_With_Tax += +(price_Level_3_With_Tax * trRate).toFixed(); - setData((prev) => ({ - ...prev, - price_With_Tax: PriceWithT, - - price_Level_2_With_Tax: price_Level_2_With_Tax, - - price_Level_3_With_Tax: price_Level_3_With_Tax, - taxId: taxDetails.taxId, - })); - }; - - // console.log(data.image.length) const handleSubmit = () => { if ( - data.name.trim() === "" || - data.description.trim() === "" || - data.price === "" || - data.image === "" + name == "" || + description == "" || + price == "" || + (productImages.length == 0 && newUpdatedImages.length == 0) // data.price_With_Tax === '' || // data.price_Level_2 === '' || // data.price_Level_2_With_Tax === '' || @@ -208,21 +127,25 @@ const EditProduct = () => { } setLoading(true); const formData = new FormData(); - formData.append("name", data.name); + formData.append("name", name); - formData.append("description", data.description); - formData.append("price", data.price); - formData.append("category", data.category); + formData.append("description", description); + formData.append("price", price); + formData.append("category", categoryName); - data.image.forEach((Singleimage) => { - formData.append("image", Singleimage); - }); + newUpdatedImages.length > 0 && + newUpdatedImages.forEach((Singleimage) => { + formData.append("newImages", Singleimage); + }); + + formData.append("image", JSON.stringify(productImages)); axios - .put(`/api/product/update/${id}`, formData, { + .patch(`/api/product/update/${id}`, formData, { headers: { Authorization: `Bearer ${token}`, - "Content-Type": "multipart/formdata", + "Content-Type": "multipart/form-data", + "Access-Control-Allow-Origin": "*", }, }) @@ -238,6 +161,7 @@ const EditProduct = () => { }) .catch((err) => { setLoading(false); + const message = err.response?.data?.message ? err.response?.data?.message : "Something went wrong!"; @@ -250,7 +174,63 @@ const EditProduct = () => { }); }); }; + const handleFileChange = (e) => { + const files = e.target.files; + // Check the total number of selected files + if (newUpdatedImages.length + files.length > 4 - productImages.length) { + setError("You can only upload up to 4 images."); + return; + } + + // Check file types and append to selectedFiles + const allowedTypes = ["image/jpeg", "image/png", "image/jpg"]; + const selected = []; + + for (let i = 0; i < files.length; i++) { + if ( + newUpdatedImages.length + selected.length >= + 4 - productImages.length + ) { + break; // Don't allow more than 4 images + } + + if (allowedTypes.includes(files[i].type)) { + selected.push(files[i]); + } + } + + if (selected.length === 0) { + setError("Please upload only PNG, JPEG, or JPG files."); + } else { + setError(""); + setNewUpdatedImages([...newUpdatedImages, ...selected]); + } + }; + + const handelDelete = async (public_id) => { + const ary = public_id.split("/"); + + const res = await axios.delete( + `/api/product/deleteImage/jatinMor/product/${ary[2]}`, + { + headers: { + "Access-Control-Allow-Origin": "*", + Authorization: `Bearer ${token}`, + }, + } + ); + if (res) { + const filtered = productImages.filter( + (item) => item.public_id !== public_id + ); + setProductImages(filtered); + } + }; + const handellocalDelete = (image) => { + const filtered = productImages.filter((item) => item !== image); + setProductImages(filtered); + }; return (
@@ -314,14 +294,14 @@ const EditProduct = () => { type="text" className="form-control" id="name" - value={data.name} + value={name} maxLength={25} - onChange={(e) => handleChange(e)} + onChange={(e) => setName(e.target.value)} /> - {data.name ? ( + {name ? ( <> - {25 - data.name.length} characters left + {25 - name.length} characters left ) : ( @@ -337,14 +317,14 @@ const EditProduct = () => { type="text" className="form-control" id="description" - value={data.description} + value={description} maxLength="100" - onChange={(e) => handleChange(e)} + onChange={(e) => setDescription(e.target.value)} /> - {data.description ? ( + {description ? ( <> - {100 - data.description.length} characters left + {100 - description.length} characters left ) : ( @@ -352,40 +332,119 @@ const EditProduct = () => { )}
-
-
+ + {error &&

{error}

}
- *Please Upload maximum four images + *You cannot upload more than 4 images
- {imagesPreview.length > 0 && ( -
- {imagesPreview.map((image, index) => ( - Product Preview + + {productImages && + productImages.map((image, i) => ( + + profileImage + {productImages.length + newUpdatedImages.length > 1 && ( + handelDelete(image.public_id)} + fontSize="small" + sx={{ + color: "white", + position: "absolute", + cursor: "pointer", + padding: "0.2rem", + background: "black", + borderRadius: "50%", + }} + /> + )} + {/* */} + ))} -
- )} + {newUpdatedImages && + newUpdatedImages.map((image, i) => ( + + profileImage + {productImages.length + newUpdatedImages.length > 1 && ( + handellocalDelete(image)} + fontSize="small" + sx={{ + color: "white", + position: "absolute", + cursor: "pointer", + padding: "0.2rem", + background: "black", + borderRadius: "50%", + }} + /> + )} + {/* */} + + ))} +
@@ -400,17 +459,17 @@ const EditProduct = () => { type="number" className="form-control" id="price" - value={data.price} - onChange={(e) => handleChange(e)} + value={price} + onChange={(e) => setPrice(e.target.value)} />
- + */} + + +
{allTax.length > 0 && ( diff --git a/src/views/Products/Products.js b/src/views/Products/Products.js index 04fbe1b..ba189bc 100644 --- a/src/views/Products/Products.js +++ b/src/views/Products/Products.js @@ -1,257 +1,453 @@ -import React, { useState, useEffect } from 'react' -import { Link } from 'react-router-dom' -import Button from '@material-ui/core/Button' -import { useNavigate } from 'react-router-dom' -import axios from 'axios' -import { isAutheticated } from 'src/auth' - +import React, { useState, useEffect } from "react"; +import { Link } from "react-router-dom"; +import Button from "@material-ui/core/Button"; +import { useNavigate } from "react-router-dom"; +import axios from "axios"; +import { isAutheticated } from "src/auth"; +import swal from "sweetalert"; +import { + Box, + FormControl, + IconButton, + InputLabel, + MenuItem, + Select, + TextField, +} from "@mui/material"; +import SearchIcon from "@mui/icons-material/Search"; +import Fuse from "fuse.js"; +import { Typography } from "@material-ui/core"; const Products = () => { - const token = isAutheticated() - const navigate = useNavigate() - const [loading, setLoading] = useState(true) - const [success, setSuccess] = useState(true) - const [productsData, setProductsData] = useState([]) + const token = isAutheticated(); + const [query, setQuery] = useState(""); + const navigate = useNavigate(); + const [loading, setLoading] = useState(true); + const [success, setSuccess] = useState(true); + const [productsData, setProductsData] = useState([]); + const [filterData, setFilterData] = useState([]); + const [queryData, setQueryData] = useState([]); - const [currentPage, setCurrentPage] = useState(1) - const [itemPerPage, setItemPerPage] = useState(10) - const [showData, setShowData] = useState(productsData) + const [currentPage, setCurrentPage] = useState(1); + const [itemPerPage, setItemPerPage] = useState(10); + const [showData, setShowData] = useState(productsData); - const handleShowEntries = (e) => { - setCurrentPage(1) - setItemPerPage(e.target.value) - } + const handleShowEntries = (e) => { + setCurrentPage(1); + setItemPerPage(e.target.value); + }; - - - const getProductsData = async () => { - axios - .get(`/api/product/getAll/`, { - headers: { - Authorization: `Bearer ${token}`, - }, - }) - .then((res) => { - setProductsData(res.data?.product) - setLoading(false) - }) - .catch((err) => { - setLoading(false) - }) - } - - useEffect(() => { - getProductsData() - }, [success]) - - useEffect(() => { - const loadData = () => { - const indexOfLastPost = currentPage * itemPerPage - const indexOfFirstPost = indexOfLastPost - itemPerPage - setShowData(productsData.slice(indexOfFirstPost, indexOfLastPost)) - } - loadData() - }, [currentPage, itemPerPage, productsData]) - - const handleDelete = (id) => { + const getProductsData = async () => { + axios + .get(`/api/product/getAll/`, { + headers: { + Authorization: `Bearer ${token}`, + }, + }) + .then((res) => { + setProductsData(res.data?.product); + setLoading(false); + }) + .catch((error) => { swal({ - title: 'Are you sure?', - icon: 'error', - buttons: { Yes: { text: 'Yes', value: true }, Cancel: { text: 'Cancel', value: 'cancel' } }, - }).then((value) => { - if (value === true) { - axios - .delete(`/api/product/delete/${id}`, { - headers: { - 'Access-Control-Allow-Origin': '*', - Authorization: `Bearer ${token}`, - }, - }) - .then((res) => { - swal({ - title: 'Deleted', - text: 'Product Deleted successfully!', - icon: 'success', - button: 'ok', - }) - setSuccess((prev) => !prev) - }) - .catch((err) => { - swal({ - title: 'Warning', - text: 'Something went wrong!', - icon: 'error', - button: 'Retry', - dangerMode: true, - }) - }) - } - }) - } + title: error, + text: "please login to access the resource or refresh the page ", + icon: "error", + button: "Retry", + dangerMode: true, + }); + setLoading(false); + }); + }; - return ( -
-
-
-
-
-
{ + if (value === true) { + axios + .delete(`/api/product/delete/${id}`, { + headers: { + "Access-Control-Allow-Origin": "*", + Authorization: `Bearer ${token}`, + }, + }) + .then((res) => { + swal({ + title: "Deleted", + text: "Product Deleted successfully!", + icon: "success", + button: "ok", + }); + setSuccess((prev) => !prev); + }) + .catch((err) => { + swal({ + title: "Warning", + text: "Something went wrong!", + icon: "error", + button: "Retry", + dangerMode: true, + }); + }); + } + }); + }; + const [filterCategory, setFilterCategory] = useState(""); + + const handleSearchClick = (query) => { + const option = { + isCaseSensitive: true, + includeScore: false, + shouldSort: true, + includeMatches: false, + findAllMatches: false, + minMatchCharLength: 1, + location: 0, + threshold: 0.6, + distance: 100, + useExtendedSearch: true, + ignoreLocation: false, + ignoreFieldNorm: false, + fieldNormWeight: 1, + keys: ["name"], + }; + + const fuse = new Fuse(productsData, option); + const result = fuse.search(query); + + const searchedResult = result.map((result) => result.item); + + setQueryData(searchedResult); + }; + useEffect(() => { + if (query !== "") { + setFilterCategory(""); + } + setTimeout(() => handleSearchClick(query), 100); + }, [query]); + useEffect(() => { + setTimeout(() => { + if (filterCategory !== "") { + const filteredProducts = productsData.filter( + (product) => product.category === filterCategory + ); + setFilterData(filteredProducts); + } else { + // If no category is selected, show all products + setShowData(productsData); + // setFilterData(filteredProducts); + } + }, 100); + }, [filterCategory, productsData]); + return ( +
+
+
+
+
+
-
- Products -
+ > +
+ Products +
-
- -
-
-
-
+
+ +
+
+
+
-
-
-
-
-
-
-
- -
-
-
+ > + + + + + + entries + +
+
+ {/* setQuery(e.target.value)} + placeholder="Type your keywords..." + /> + {" "} */} + + Search by product name : + + setQuery(e.target.value)} + InputProps={{ + endAdornment: ( + handleSearchClick(query)} + > + + + ), + disableUnderline: true, + }} + /> +
+
+ + Filter by Category name : + -
- - - + + + + + + + + - - - - - - - - - {!loading && showData.length === 0 && ( - - - - )} - {loading ? ( - - - - ) : ( - showData.map((product, i) => { - return ( - - - + ); + }) + ) : query !== "" ? ( + queryData.map((product, i) => { + return ( + + + + + + + + + ); + }) + ) : ( + query == "" && + filterData.map((product, i) => { + return ( + + + + + + + + + ); + }) + )} + +
Product NameImagePriceAdded OnActions
-
No Data Available
-
- Loading... -
{product.name} - {product.image && - product.image.map(i => - - )} +
+ + + + + + - - - - - + + + + + {!loading && showData.length === 0 && ( + + + + )} + {loading ? ( + + + + ) : query === "" && filterCategory == "" ? ( + showData.map((product, i) => { + return ( + + + + + + + - - ) - }) - )} - -
ImageProduct NameCategory Name₹{product.price} - {new Date(product.createdAt).toLocaleString('en-IN', { - weekday: 'short', - month: 'short', - day: 'numeric', - year: 'numeric', - hour: 'numeric', - minute: 'numeric', - hour12: true, - })} - - - - Added OnActions
+
No Data Available
+
+ Loading... +
+ {product.image && + product.image.map((i, j) => ( + + ))} + {product.name} + {product.category !== "" + ? product.category + : "Category Not selected "} + ${product.price} + {new Date(product.createdAt).toLocaleString( + "en-IN", + { + weekday: "short", + month: "short", + day: "numeric", + year: "numeric", + hour: "numeric", + minute: "numeric", + hour12: true, + } + )} + + + - - - - - - - -
-
+ onClick={() => { + handleDelete(product._id); + }} + > + Delete + + + +
+ {product.image && + product.image.map((i, j) => ( + + ))} + {product.name} + {product.category !== "" + ? product.category + : "Category Not selected "} + ₹{product.price} + {new Date(product.createdAt).toLocaleString( + "en-IN", + { + weekday: "short", + month: "short", + day: "numeric", + year: "numeric", + hour: "numeric", + minute: "numeric", + hour12: true, + } + )} + + + + + + + + + + +
+ {product.image && + product.image.map((i, j) => ( + + ))} + {product.name} + {product.category} + ₹{product.price} + {new Date(product.createdAt).toLocaleString( + "en-IN", + { + weekday: "short", + month: "short", + day: "numeric", + year: "numeric", + hour: "numeric", + minute: "numeric", + hour12: true, + } + )} + + + + + + + + + + +
+
-
-
-
- Showing {currentPage * itemPerPage - itemPerPage + 1} to{' '} - {Math.min(currentPage * itemPerPage, productsData.length)} of{' '} - {productsData.length} entries -
-
- -
-
-
    -
  • - setCurrentPage((prev) => prev - 1)} - > - Previous - -
  • - - {!(currentPage - 1 < 1) && ( -
  • - setCurrentPage((prev) => prev - 1)} - > - {currentPage - 1} - -
  • - )} - -
  • - - {currentPage} - -
  • - - {!( - (currentPage + 1) * itemPerPage - itemPerPage > - productsData.length - 1 - ) && ( -
  • - { - setCurrentPage((prev) => prev + 1) - }} - > - {currentPage + 1} - -
  • - )} - -
  • - productsData.length - 1 - ) - ? 'paginate_button page-item next' - : 'paginate_button page-item next disabled' - } - > - setCurrentPage((prev) => prev + 1)} - > - Next - -
  • -
-
-
-
-
-
-
+
+
+
+ Showing {currentPage * itemPerPage - itemPerPage + 1} to{" "} + {Math.min( + currentPage * itemPerPage, + productsData.length + )}{" "} + of {productsData.length} entries +
-
-
-
- ) -} -export default Products +
+
+
    +
  • + setCurrentPage((prev) => prev - 1)} + > + Previous + +
  • + + {!(currentPage - 1 < 1) && ( +
  • + + setCurrentPage((prev) => prev - 1) + } + > + {currentPage - 1} + +
  • + )} + +
  • + + {currentPage} + +
  • + + {!( + (currentPage + 1) * itemPerPage - itemPerPage > + productsData.length - 1 + ) && ( +
  • + { + setCurrentPage((prev) => prev + 1); + }} + > + {currentPage + 1} + +
  • + )} + +
  • + productsData.length - 1 + ) + ? "paginate_button page-item next" + : "paginate_button page-item next disabled" + } + > + setCurrentPage((prev) => prev + 1)} + > + Next + +
  • +
+
+
+
+
+
+
+
+
+
+ + ); +}; + +export default Products; diff --git a/src/views/Products/ViewProduct.js b/src/views/Products/ViewProduct.js index 4ea6287..092d97f 100644 --- a/src/views/Products/ViewProduct.js +++ b/src/views/Products/ViewProduct.js @@ -36,6 +36,7 @@ function ViewProduct() { return strTime; } + // console.log(product); return (
@@ -45,13 +46,13 @@ function ViewProduct() {

Product

- + {/*
@@ -78,7 +79,7 @@ function ViewProduct() { Id{" "} -
{product?._id}
+
{product.uniqueId}
@@ -106,7 +107,7 @@ function ViewProduct() { Base Price - ₹{product?.price} + ${product?.price} {/* Product Time{product?.time} diff --git a/src/views/pages/login/Login.js b/src/views/pages/login/Login.js index 34462ba..af93fd0 100644 --- a/src/views/pages/login/Login.js +++ b/src/views/pages/login/Login.js @@ -1,5 +1,5 @@ -import React, { useEffect } from 'react' -import { Link, useNavigate } from 'react-router-dom' +import React, { useEffect } from "react"; +import { Link, useNavigate } from "react-router-dom"; import { CButton, CCard, @@ -12,129 +12,122 @@ import { CInputGroup, CInputGroupText, CRow, -} from '@coreui/react' -import CIcon from '@coreui/icons-react' -import { cilLockLocked, cilUser } from '@coreui/icons' +} from "@coreui/react"; +import CIcon from "@coreui/icons-react"; +import { cilLockLocked, cilUser } from "@coreui/icons"; import ClipLoader from "react-spinners/ClipLoader"; -import { useState } from 'react' -import axios from 'axios' -import { useHistory } from 'react-router-dom' +import { useState } from "react"; +import axios from "axios"; +import { useHistory } from "react-router-dom"; const Login = () => { const [loading, setLoading] = useState(false); - const [validForm, setValidForm] = useState(false) + const [validForm, setValidForm] = useState(false); const [auth, setAuth] = useState({ email: "", - password: "" + password: "", }); const [errors, setErrors] = useState({ - emailError: '', - passwordError: '', - - }) + emailError: "", + passwordError: "", + }); const validEmailRegex = RegExp( - /^(([^<>()\[\]\.,;:\s@\"]+(\.[^<>()\[\]\.,;:\s@\"]+)*)|(\".+\"))@(([^<>()[\]\.,;:\s@\"]+\.)+[^<>()[\]\.,;:\s@\"]{2,})$/i, - ) - const validPasswordRegex = RegExp(/^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9])(?=.*?[^\w\s]).{7,}$/) + /^(([^<>()\[\]\.,;:\s@\"]+(\.[^<>()\[\]\.,;:\s@\"]+)*)|(\".+\"))@(([^<>()[\]\.,;:\s@\"]+\.)+[^<>()[\]\.,;:\s@\"]{2,})$/i + ); + const validPasswordRegex = RegExp( + /^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9])(?=.*?[^\w\s]).{7,}$/ + ); const history = useNavigate(); // const handleChange = (e) => (event) => { // setAuth({ ...auth, [e]: event.target.value }); // }; const validateForm = () => { - let valid = true + let valid = true; Object.values(errors).forEach((val) => { if (val.length > 0) { - valid = false - return false + valid = false; + return false; } - }) + }); Object.values(auth).forEach((val) => { if (val.length <= 0) { - valid = false - return false + valid = false; + return false; } - }) - return valid - } + }); + return valid; + }; //cheking email and password useEffect(() => { if (validateForm()) { - setValidForm(true) + setValidForm(true); } else { - setValidForm(false) + setValidForm(false); } - }, [errors]) + }, [errors]); const handleChange = (e) => { - const { name, value } = e.target + const { name, value } = e.target; switch (name) { - case 'email': + case "email": setErrors({ ...errors, - emailError: validEmailRegex.test(value) ? '' : 'Email is not valid!', - }) + emailError: validEmailRegex.test(value) ? "" : "Email is not valid!", + }); - break - case 'password': + break; + case "password": setErrors((errors) => ({ ...errors, passwordError: validPasswordRegex.test(value) - ? '' - : 'Password Shoud Be 8 Characters Long, Atleast One Uppercase, Atleast One Lowercase,Atleast One Digit, Atleast One Special Character', - })) - break + ? "" + : "Password Shoud Be 8 Characters Long, Atleast One Uppercase, Atleast One Lowercase,Atleast One Digit, Atleast One Special Character", + })); + break; default: - break + break; } - setAuth({ ...auth, [name]: value }) - } + setAuth({ ...auth, [name]: value }); + }; const Login = async () => { if (!(auth.email && auth.password)) { - - return swal('Error!', 'All fields are required', 'error') + return swal("Error!", "All fields are required", "error"); } - setLoading({ loading: true }) + setLoading({ loading: true }); try { const res = await axios.post("/api/v1/user/login/", auth); if (res.data.success == true) { - localStorage.setItem("authToken", res.data.token) + localStorage.setItem("authToken", res.data.token); let response = await axios.get(`/api/v1/user/details`, { headers: { Authorization: `Bearer ${res.data.token}`, }, - }) + }); // console.log(response.data) - const data = response.data - if (data.user.role === 'admin') { - history('/dashboard') + const data = response.data; + if (data.user.role === "admin") { + history("/dashboard"); setLoading(false); - window.location.reload() - } - else { - swal('Error!', 'please try with admin credential!!', 'error') + window.location.reload(); + } else { + swal("Error!", "please try with admin credential!!", "error"); setLoading(false); } - - - - } - else { + } else { setLoading(false); - swal('Error!', 'Invalid Credentials', 'error') - + swal("Error!", "Invalid Credentials", "error"); } } catch (error) { setLoading(false); - swal('Error!', 'Invalid Credentials', 'error') - + swal("Error!", "Invalid Credentials", "error"); } - } + }; return (
@@ -145,16 +138,27 @@ const Login = () => { -

Login

-

Sign In to Your SOLAR Sign Admin Dashboard Account.

+

The Solar Sign

+

+ Sign In to Your SOLAR Sign Admin Dashboard Account. +

- + {errors.emailError && ( -

{errors.emailError}

+

+ {errors.emailError} +

)} @@ -171,15 +175,20 @@ const Login = () => { {errors.passwordError && ( -

{errors.passwordError}

+

+ {errors.passwordError} +

)} - + {!loading && "Login"} - - Cancel @@ -188,12 +197,8 @@ const Login = () => {
- - Forgot password.? - + Forgot password.? - -
{/* @@ -202,18 +207,17 @@ const Login = () => { */}
-
- ) -} + ); +}; -export default Login +export default Login; - // < Route path = "/" name = "Home" render = {(props) => ( - // userdata && userdata.role === 'admin' ? : - // <> - // )} /> \ No newline at end of file +// < Route path = "/" name = "Home" render = {(props) => ( +// userdata && userdata.role === 'admin' ? : +// <> +// )} />