From d5754487adc9853c0de35339927f8c46c25d12e2 Mon Sep 17 00:00:00 2001 From: ROSHAN GARG Date: Fri, 26 Jul 2024 18:21:32 +0530 Subject: [PATCH] PD auth and protected routes done --- package.json | 8 +- src/App.js | 14 +- src/_nav.js | 17 +- src/assets/data/cardjson.json | 26 +++ src/auth.js | 10 ++ src/axios.js | 4 +- src/components/AppFooter.js | 4 +- src/components/header/AppHeaderDropdown.js | 76 ++++---- src/protectedRoute.js | 19 ++ src/routes.js | 10 ++ src/views/pages/Kyc/kyc.js | 126 ++++++++++++++ src/views/pages/Kyc/kycDetails.js | 190 ++++++++++++++++++++ src/views/pages/forgetPassword.js | 106 +++++++++++ src/views/pages/login/Login.js | 154 +++++++++++++--- src/views/pages/profile/ChangePassword.js | 193 ++++++++++++++------- src/views/pages/profile/MyProfile.js | 176 +++++++++---------- src/views/shops/Shop.js | 53 +++++- src/views/shops/shopCard.js | 33 ++++ 18 files changed, 978 insertions(+), 241 deletions(-) create mode 100644 src/assets/data/cardjson.json create mode 100644 src/auth.js create mode 100644 src/protectedRoute.js create mode 100644 src/views/pages/Kyc/kyc.js create mode 100644 src/views/pages/Kyc/kycDetails.js create mode 100644 src/views/pages/forgetPassword.js create mode 100644 src/views/shops/shopCard.js diff --git a/package.json b/package.json index c963eca..8024cb9 100644 --- a/package.json +++ b/package.json @@ -26,14 +26,20 @@ "@coreui/react": "^5.1.0", "@coreui/react-chartjs": "^3.0.0", "@coreui/utils": "^2.0.2", + "@emotion/react": "^11.13.0", + "@emotion/styled": "^11.13.0", "@fortawesome/fontawesome-svg-core": "^6.6.0", "@fortawesome/free-solid-svg-icons": "^6.6.0", "@fortawesome/react-fontawesome": "^0.2.2", + "@mui/icons-material": "^5.16.4", + "@mui/material": "^5.16.4", "@popperjs/core": "^2.11.8", "@themesberg/react-bootstrap": "^1.4.1", + "axios": "^1.7.2", "chart.js": "^4.4.3", "classnames": "^2.5.1", "core-js": "^3.37.1", + "date-fns": "^3.6.0", "prop-types": "^15.8.1", "react": "^18.3.1", "react-dom": "^18.3.1", @@ -41,7 +47,7 @@ "react-router-dom": "^6.23.1", "redux": "5.0.1", "simplebar-react": "^3.2.5", - "sweetalert2": "^11.12.2" + "sweetalert2": "^11.12.3" }, "devDependencies": { "@vitejs/plugin-react": "^4.3.1", diff --git a/src/App.js b/src/App.js index 0518884..340eb53 100644 --- a/src/App.js +++ b/src/App.js @@ -4,6 +4,7 @@ import { useSelector } from 'react-redux' import { CSpinner, useColorModes } from '@coreui/react' import './scss/style.scss' +import ProtectedRoute from './protectedRoute' // Containers const DefaultLayout = React.lazy(() => import('./layout/DefaultLayout')) @@ -13,8 +14,7 @@ const Login = React.lazy(() => import('./views/pages/login/Login')) const Register = React.lazy(() => import('./views/pages/register/Register')) const Page404 = React.lazy(() => import('./views/pages/page404/Page404')) const Page500 = React.lazy(() => import('./views/pages/page500/Page500')) -const MyProfile = React.lazy(() => import('./views/pages/profile/MyProfile')) -const ChangePassword = React.lazy(() => import('./views/pages/profile/ChangePassword')) +const ForgetPassword = React.lazy(() => import('./views/pages/forgetPassword')) const App = () => { const { isColorModeSet, setColorMode } = useColorModes('coreui-free-react-admin-template-theme') @@ -46,12 +46,18 @@ const App = () => { } > - } /> - } /> + {/* } /> */} } /> } /> } /> } /> + } + /> + } /> } /> diff --git a/src/_nav.js b/src/_nav.js index abcd1ec..72cc65f 100644 --- a/src/_nav.js +++ b/src/_nav.js @@ -23,10 +23,6 @@ const _nav = [ name: 'Dashboard', to: '/dashboard', icon: , - badge: { - color: 'info', - text: 'NEW', - }, }, { component: CNavItem, @@ -34,12 +30,19 @@ const _nav = [ to: '/shop', icon: , }, + // { + // component: CNavItem, + // name: 'Orders', + // to: '/order', + // icon: , + // }, { component: CNavItem, - name: 'Orders', - to: '/order', - icon: , + name: 'KYC', + to: '/kyc', + icon: , }, + // { // component: CNavTitle, // name: 'Theme', diff --git a/src/assets/data/cardjson.json b/src/assets/data/cardjson.json new file mode 100644 index 0000000..d192b2b --- /dev/null +++ b/src/assets/data/cardjson.json @@ -0,0 +1,26 @@ +[ + { + "name": "Item 1", + "image": "https://images.pexels.com/photos/341523/pexels-photo-341523.jpeg?auto=compress&cs=tinysrgb&w=600", + "price": 10.99, + "categoryName": "phone" + }, + { + "name": "Item 2", + "image": "https://images.pexels.com/photos/90946/pexels-photo-90946.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1", + "price": 20.99, + "categoryName": "phone" + }, + { + "name": "Item 3", + "image": "https://images.pexels.com/photos/341523/pexels-photo-341523.jpeg?auto=compress&cs=tinysrgb&w=600", + "price": 30.99, + "categoryName": "camera" + }, + { + "name": "Item 4", + "image": "https://images.pexels.com/photos/90946/pexels-photo-90946.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1", + "price": 40.99, + "categoryName": "camera" + } +] diff --git a/src/auth.js b/src/auth.js new file mode 100644 index 0000000..f26833f --- /dev/null +++ b/src/auth.js @@ -0,0 +1,10 @@ +export const isAutheticated = () => { + if (typeof window == 'undefined') { + return true + } + if (localStorage.getItem('authToken')) { + return localStorage.getItem('authToken') + } else { + return false + } +} diff --git a/src/axios.js b/src/axios.js index fbfab56..9c9c43a 100644 --- a/src/axios.js +++ b/src/axios.js @@ -1,10 +1,10 @@ import axios from 'axios' const Axios = axios.create({ - // baseURL: "http://localhost:5000/", + // baseURL: 'http://localhost:5000/', // baseURL: 'https://leadesh-whatsapp.onrender.com', // baseURL: "https://api.leadesh.com/", - baseURL: 'https://leadesh-api-github.onrender.com/', // latest is this one + baseURL: 'https://cheminova-api-2.onrender.com', // latest is this one }) export default Axios diff --git a/src/components/AppFooter.js b/src/components/AppFooter.js index fd126f4..a058e3d 100644 --- a/src/components/AppFooter.js +++ b/src/components/AppFooter.js @@ -4,7 +4,7 @@ import { CFooter } from '@coreui/react' const AppFooter = () => { return ( -
+ {/*
CoreUI @@ -15,7 +15,7 @@ const AppFooter = () => { CoreUI React Admin & Dashboard Template -
+
*/}
) } diff --git a/src/components/header/AppHeaderDropdown.js b/src/components/header/AppHeaderDropdown.js index 3d56f2c..6c7d71c 100644 --- a/src/components/header/AppHeaderDropdown.js +++ b/src/components/header/AppHeaderDropdown.js @@ -1,4 +1,4 @@ -import React, { useState } from 'react' +import React, { useEffect, useState } from 'react' import { CAvatar, CBadge, @@ -26,55 +26,50 @@ import avatar8 from './../../assets/images/avatars/8.jpg' import { useNavigate } from 'react-router-dom' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import { faUserCircle } from '@fortawesome/free-solid-svg-icons' +import Swal from 'sweetalert2' +import Axios from '../../axios' +import { isAutheticated } from '../../auth' const AppHeaderDropdown = () => { const navigate = useNavigate() + const signout = async () => { + localStorage.removeItem('authToken') + Swal.fire('success!', 'Logged Out', 'success') + navigate('/login') + } + const [user, setUser] = useState(null) + const token = isAutheticated() + const getData = async () => { + let res = await Axios.get(`/api/v1/user/details`, { + headers: { + Authorization: `Bearer ${token}`, + }, + }) + if (res.data.success) { + setUser({ ...res.data.user }) + } + } + useEffect(() => { + getData() + }, []) + return (
- {/* */}
- Garg + {user?.name}
Account - {/* - - Update - - 42 - - - - - Messages - - 42 - - - - - Tasks - - 42 - - - - - Comments - - 42 - - - Settings */} + navigate('/my-profile')}> Profile @@ -83,22 +78,9 @@ const AppHeaderDropdown = () => { Change Password - {/* - - Payments - - 42 - - - - - Projects - - 42 - - */} + - + Logout diff --git a/src/protectedRoute.js b/src/protectedRoute.js new file mode 100644 index 0000000..1382107 --- /dev/null +++ b/src/protectedRoute.js @@ -0,0 +1,19 @@ +/* eslint-disable react/react-in-jsx-scope */ +/* eslint-disable react/prop-types */ +import { useEffect } from 'react' +import { useNavigate } from 'react-router-dom' + +const ProtectedRoute = ({ element: Element }) => { + const navigate = useNavigate() + console.log('req came here ') + + useEffect(() => { + if (!localStorage.getItem('authToken')) { + navigate('/login') + } + }, [navigate]) + + return +} + +export default ProtectedRoute diff --git a/src/routes.js b/src/routes.js index 6e43fdb..4f2d010 100644 --- a/src/routes.js +++ b/src/routes.js @@ -3,12 +3,22 @@ import React from 'react' const Dashboard = React.lazy(() => import('./views/dashboard/Dashboard')) const Shop = React.lazy(() => import('./views/shops/Shop')) const Order = React.lazy(() => import('./views/orders/Order')) +const MyProfile = React.lazy(() => import('./views/pages/profile/MyProfile')) +const ChangePassword = React.lazy(() => import('./views/pages/profile/ChangePassword')) +const Kyc = React.lazy(() => import('./views/pages/Kyc/kyc')) +const KycDetails = React.lazy(() => import('./views/pages/Kyc/kycDetails')) const routes = [ { path: '/', exact: true, name: 'Home' }, { path: '/dashboard', name: 'Dashboard', element: Dashboard }, { path: '/shop', name: 'Shop', element: Shop }, { path: '/order', name: 'Order', element: Order }, + // KYC + { path: '/kyc', name: 'Kyc', element: Kyc }, + { path: '/kyc/details/:id', name: 'Kyc details', element: KycDetails }, + + { path: '/my-profile', name: 'Profile', element: MyProfile }, + { path: '/change-password', name: 'Change password', element: ChangePassword }, ] export default routes diff --git a/src/views/pages/Kyc/kyc.js b/src/views/pages/Kyc/kyc.js new file mode 100644 index 0000000..ae3ebad --- /dev/null +++ b/src/views/pages/Kyc/kyc.js @@ -0,0 +1,126 @@ +import React, { useState } from 'react' +import { + Box, + Table, + TableBody, + TableCell, + TableContainer, + TableHead, + TableRow, + Paper, + TablePagination, + Button, + IconButton, + Tooltip, +} from '@mui/material' +import { Visibility, ThumbUp, ThumbDown } from '@mui/icons-material' +import { format } from 'date-fns' +import { useNavigate } from 'react-router-dom' + +const generateRandomData = (numRows) => { + const statuses = ['New', 'Pending', 'Rejected', 'Approved'] + const data = [] + + for (let i = 0; i < numRows; i++) { + data.push({ + id: i + 1, + tradeName: `Trade ${i + 1}`, + createdOn: new Date(), + status: statuses[Math.floor(Math.random() * statuses.length)], + }) + } + + return data +} + +const Kyc = () => { + const [rows, setRows] = useState(generateRandomData(50)) + const [page, setPage] = useState(0) + const [rowsPerPage, setRowsPerPage] = useState(5) + const navigate = useNavigate() + + const handleChangePage = (event, newPage) => { + setPage(newPage) + } + + const handleChangeRowsPerPage = (event) => { + setRowsPerPage(parseInt(event.target.value, 10)) + setPage(0) + } + + const handleViewClick = (id) => { + navigate(`/kyc/details/${id}`) + } + + return ( + + + + + + + ID + Trade Name + Created On + Status + Action + + + + {rows.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage).map((row) => ( + + {row.id} + {row.tradeName} + {format(row.createdOn, 'yyyy-MM-dd HH:mm:ss')} + {row.status} + + + {/* + + */} + + + + {/* + + */} + + + + {/* + + */} + + + + + ))} + +
+
+ +
+
+ ) +} + +export default Kyc diff --git a/src/views/pages/Kyc/kycDetails.js b/src/views/pages/Kyc/kycDetails.js new file mode 100644 index 0000000..8da92e0 --- /dev/null +++ b/src/views/pages/Kyc/kycDetails.js @@ -0,0 +1,190 @@ +import React from 'react' +import { Box, Typography, Grid, Paper, Avatar } from '@mui/material' +import { useParams } from 'react-router-dom' +import { format } from 'date-fns' + +const generateRandomRetailerDetails = (id) => ({ + id, + tradeName: `Trade ${id}`, + name: `Retailer ${id}`, + address: '123 Main St', + townCity: 'Townsville', + district: 'District A', + state: 'State B', + pincode: '123456', + mobileNumber: '123-456-7890', + mappedDistributor: `Distributor ${id}`, + documents: { + panNumber: 'ABCDE1234F', + panCard: + 'https://www.shutterstock.com/shutterstock/photos/2329691987/display_1500/stock-vector-blank-pan-card-vector-image-income-tax-card-personal-account-number-image-translation-income-2329691987.jpg', + aadharNumber: '1234-5678-9101', + aadharCard: 'https://via.placeholder.com/100', + gstNumber: '22AAAAA0000A1Z5', + gstRegistration: 'https://via.placeholder.com/100', + pesticideLicense: 'https://via.placeholder.com/100', + fertilizerLicense: 'https://via.placeholder.com/100', + entranceBoardSelfie: 'https://via.placeholder.com/100', + }, + salesCoordinator: { + designation: 'Sales Coordinator', + name: `Coordinator ${id}`, + employeeId: `EMP${id}`, + uploadedOn: new Date(), + resubmittedOn: new Date(), + }, + notes: ['Note 1', 'Note 2', 'Note 3'], +}) + +const KycDetails = () => { + const { id } = useParams() + console.log(id) + const retailerDetails = generateRandomRetailerDetails(id) + + return ( + + + Retailer Details + + + + + Retailer Details + + + + + Trade Name: {retailerDetails.tradeName} + + + Name: {retailerDetails.name} + + + Address: {retailerDetails.address} + + + Town/City: {retailerDetails.townCity} + + + + + District: {retailerDetails.district} + + + State: {retailerDetails.state} + + + Pincode: {retailerDetails.pincode} + + + Mobile Number: {retailerDetails.mobileNumber} + + + Mapped Principal Distributor: {retailerDetails.mappedDistributor} + + + + + + + + Documents + + + + + PAN Number: {retailerDetails.documents.panNumber} + + + + Aadhar Number: {retailerDetails.documents.aadharNumber} + + + + GST Number: {retailerDetails.documents.gstNumber} + + + + + + Pesticide License: + + + + Fertilizer License (optional): + + + + Selfie of Entrance Board: + + + + + + + + + Block 3: Sales Coordinators/Territory Manager Details + + + + + Designation: {retailerDetails.salesCoordinator.designation} + + + Name: {retailerDetails.salesCoordinator.name} + + + Employee ID: {retailerDetails.salesCoordinator.employeeId} + + + + + Uploaded on:{' '} + {format(retailerDetails.salesCoordinator.uploadedOn, 'yyyy-MM-dd HH:mm:ss')} + + + Resubmitted on:{' '} + {format(retailerDetails.salesCoordinator.resubmittedOn, 'yyyy-MM-dd HH:mm:ss')} + + + + + + + + Block 4: Notes + + {retailerDetails.notes.map((note, index) => ( + {note} + ))} + + + ) +} + +export default KycDetails diff --git a/src/views/pages/forgetPassword.js b/src/views/pages/forgetPassword.js new file mode 100644 index 0000000..8660380 --- /dev/null +++ b/src/views/pages/forgetPassword.js @@ -0,0 +1,106 @@ +import { cilUser } from '@coreui/icons' +import CIcon from '@coreui/icons-react' +import { + CButton, + CCard, + CCardBody, + CCardGroup, + CCol, + CContainer, + CForm, + CFormInput, + CInputGroup, + CInputGroupText, + CRow, + CSpinner, +} from '@coreui/react' + +import React, { useState } from 'react' +import { useNavigate } from 'react-router-dom' +import Swal from 'sweetalert2' +import Axios from '../../axios' +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' +import { faEnvelope } from '@fortawesome/free-solid-svg-icons' + +const ForgetPassword = () => { + const [email, setEmail] = useState('') + const [loading, setLoading] = useState(false) + const navigate = useNavigate() + const handleSubmit = async (e) => { + e.preventDefault() + try { + const res = await Axios.post('/api/v1/user/password/forgot', { email }) + console.log(res) + if (res?.status === 200) { + Swal.fire('success', res?.data?.message, 'success') + navigate('/login') + } else if (res?.status === 305) { + Swal.fire('error', res?.data?.message, 'error') + } + } catch (err) { + Swal.fire('error', 'User Not found with this email ', 'error') + } + } + return ( + <> +
+ + + + + + + +

Forget Password

+

+ Enter your email we will send you the password +

+ + + + + setEmail(e.target.value)} + value={email} + name="email" + autoComplete="email" + /> + + + + + + {loading ? : 'Generate password '} + + + + + If you know you password? Continue to + + navigate('/login')} color="link" className="px-2"> + Sign in? + + + +
+
+
+
+
+
+
+
+ + ) +} + +export default ForgetPassword diff --git a/src/views/pages/login/Login.js b/src/views/pages/login/Login.js index 1b2ee0b..6031786 100644 --- a/src/views/pages/login/Login.js +++ b/src/views/pages/login/Login.js @@ -1,5 +1,5 @@ -import React from 'react' -import { Link } from 'react-router-dom' +import React, { useEffect, useState } from 'react' +import { Link, useNavigate } from 'react-router-dom' import { CButton, CCard, @@ -12,11 +12,110 @@ import { CInputGroup, CInputGroupText, CRow, + CSpinner, } from '@coreui/react' import CIcon from '@coreui/icons-react' import { cilLockLocked, cilUser } from '@coreui/icons' +// import axios from 'axios' +import Axios from '../../../axios' +import Swal from 'sweetalert2' const Login = () => { + const [loading, setLoading] = useState(false) + const [validForm, setValidForm] = useState(false) + const navigate = useNavigate() + const [auth, setAuth] = useState({ + email: '', + password: '', + }) + const [errors, setErrors] = useState({ + emailError: '', + passwordError: '', + }) + const validEmailRegex = RegExp( + /^(([^<>()\[\]\.,;:\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 + Object.values(errors).forEach((val) => { + if (val.length > 0) { + valid = false + return false + } + }) + Object.values(auth).forEach((val) => { + if (val.length <= 0) { + valid = false + return false + } + }) + return valid + } + + //cheking email and password + useEffect(() => { + if (validateForm()) { + setValidForm(true) + } else { + setValidForm(false) + } + }, [errors]) + const handleChange = (e) => { + const { name, value } = e.target + + switch (name) { + case 'email': + setErrors({ + ...errors, + emailError: validEmailRegex.test(value) ? '' : 'Email is not valid!', + }) + + 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 + default: + break + } + + setAuth({ ...auth, [name]: value }) + } + const handleSubmit = async (e) => { + if (!(auth.email && auth.password)) { + return Swal.fire('Error!', 'All fields are required', 'error') + } + setLoading({ loading: true }) + try { + const res = await Axios.post('/api/v1/user/login/', auth) + console.log(res) + if (res.data.success == true && res.data.user.role === 'principal-Distributor') { + localStorage.setItem('authToken', res.data.token) + navigate('/dashboard') + setLoading(false) + Swal.fire('success', 'logged in successfuly ', 'success') + + // console.log(response.data) + } else if (res.data.success == true && res.data.user.role !== 'principal-Distributor') { + setLoading(false) + Swal.fire('error', 'Please login through the PD Credentials ', 'error') + } + } catch (error) { + setLoading(false) + + Swal.fire('Error!', 'Invalid Credentials', 'error') + } + } return (
@@ -32,26 +131,53 @@ const Login = () => { - + + {errors.emailError && ( +

{errors.emailError}

+ )} + + {errors.passwordError && ( +

{errors.passwordError}

+ )} - - Login + + {loading ? : 'Login'} - + navigate('/forget-password')} + color="link" + className="px-0" + > Forgot password? @@ -59,22 +185,6 @@ const Login = () => { - - -
-

Sign up

-

- Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod - tempor incididunt ut labore et dolore magna aliqua. -

- - - Register Now! - - -
-
-
diff --git a/src/views/pages/profile/ChangePassword.js b/src/views/pages/profile/ChangePassword.js index 4b3af36..5a1530f 100644 --- a/src/views/pages/profile/ChangePassword.js +++ b/src/views/pages/profile/ChangePassword.js @@ -2,79 +2,117 @@ import React, { useState } from 'react' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import { faAngleLeft, faEnvelope, faLockOpen, faUnlockAlt } from '@fortawesome/free-solid-svg-icons' import { Col, Row, Form, Card, Button, Container, InputGroup } from '@themesberg/react-bootstrap' -import { Link } from 'react-router-dom' +import { Link, useNavigate } from 'react-router-dom' // import axios from 'axios' // import { Routes } from '../../routes' import Swal from 'sweetalert2' +import { isAutheticated } from '../../../auth' +import Axios from '../../../axios' // import Axios from '../../axios' const ChangePassword = () => { - const [Loading, setLoading] = useState(false) - - const [formData, setFormData] = useState({ + const [loading, setLoading] = useState(false) + const navigate = useNavigate() + const token = isAutheticated() + const [user, setUser] = useState({ oldPassword: '', newPassword: '', + confirmPassword: '', }) - - // console.log(formData); - const handleInputChange = (e) => { + const [errors, setErrors] = useState({ + confirmPasswordError: '', + newPasswordError: '', + oldPasswordError: '', + passwordMismatchError: '', + }) + const validEmailRegex = RegExp( + /^(([^<>()\[\]\.,;:\s@\"]+(\.[^<>()\[\]\.,;:\s@\"]+)*)|(\".+\"))@(([^<>()[\]\.,;:\s@\"]+\.)+[^<>()[\]\.,;:\s@\"]{2,})$/i, + ) + const validPasswordRegex = RegExp(/^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9])(?=.*?[^\w\s]).{7,}$/) + const handleChange = (e) => { const { name, value } = e.target - setFormData((prevData) => ({ ...prevData, [name]: value })) + + switch (name) { + case 'oldPassword': + setErrors({ + ...errors, + oldPasswordError: validPasswordRegex.test(value) + ? '' + : 'Password Shoud Be 8 Characters Long, Atleast One Uppercase, Atleast One Lowercase,Atleast One Digit, Atleast One Special Character', + }) + + break + case 'newPassword': + setErrors({ + ...errors, + newPasswordError: validPasswordRegex.test(value) + ? '' + : 'Password Shoud Be 8 Characters Long, Atleast One Uppercase, Atleast One Lowercase,Atleast One Digit, Atleast One Special Character', + }) + + break + case 'confirmPassword': + setErrors({ + ...errors, + confirmPasswordError: validPasswordRegex.test(value) + ? '' + : 'Password should be 8 characters long, contain at least one uppercase letter, one lowercase letter, one digit, and one special character', + passwordMismatchError: value !== user.newPassword ? 'Passwords do not match' : '', + }) + break + default: + break + } + + setUser({ ...user, [name]: value }) } - const formHandler = async (e) => { + + const handleSubmit = async (e) => { e.preventDefault() + + if (!(user.oldPassword && user.newPassword && user.confirmPassword)) { + return Swal.fire('Error!', 'All fields are required', 'error') + } + + if (!(user.newPassword.length >= 8)) { + return Swal.fire('Error!', 'New password must be 8 characters long', 'error') + } + + if (user.newPassword !== user.confirmPassword) { + return Swal.fire('Error!', 'New Password and Confirm Password do not match!', 'error') + } + setLoading(true) - // const jwt = localStorage.getItem('jwt') - // console.log(jwt); - // try { - // const response = await Axios.post( - // '/api/password', - // { - // oldPassword: formData.oldPassword, - // newPassword: formData.newPassword, - // }, - // { - // headers: { - // jwt: `${jwt}`, - // 'Content-Type': 'application/json', - // }, - // }, - // ) - // // console.log("response ", response); - // if (response.status === 200) { - // // Display success alert - // Swal.fire({ - // icon: 'success', - // title: 'Password Changed Successfully', - // text: 'Your password has been changed successfully!', - // }).then((result) => { - // if (result.isConfirmed) { - // history.push(Routes.Presentation.path) - // // history.push(Routes.Dashboard.path); - // } - // }) - - // // Redirect to the dashboard - // } else { - // // Handle other scenarios if needed - // Swal.fire({ - // icon: 'error', - // title: 'Password Change Failed', - // text: 'There was an issue changing your password. Please try again.', - // }) - // } - // } catch (err) { - // console.log('error is ', err) - // Swal.fire({ - // icon: 'error', - // title: 'Password Change Failed', - // text: 'There was an error changing your password. Please try again.', - // }) - // } - - setLoading(false) + try { + const res = await Axios.put( + '/api/v1/user/password/update', + { ...user }, + { + headers: { + Authorization: `Bearer ${token}`, + }, + }, + ) + console.log(res) + if (res?.data.success) { + Swal.fire({ + title: 'Done', + text: 'Password Changed', + icon: 'success', + confirmButtonText: 'ok', + confirmButtonColor: '#303c54', + iconColor: '#303c54', + }).then(() => { + navigate('/dashboard') + }) + } + } catch (error) { + Swal.fire('Error!', error.response?.data.message || 'Something went wrong', 'error') + } finally { + setLoading(false) + } } return (
@@ -93,7 +131,7 @@ const ChangePassword = () => { >

Change Password

-
+ Old Password @@ -105,13 +143,16 @@ const ChangePassword = () => { type="password" placeholder="old password" name="oldPassword" - value={formData.oldPassword} - onChange={handleInputChange} + value={user.oldPassword} + onChange={handleChange} pattern="^(?=.*[a-z](){}[])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$" title="The password should have 1 upper-case letter, 1 lower-case letter, 1 number, 1 special character and at least 8 characters." /> + {errors.oldPasswordError && ( +

{errors.oldPasswordError}

+ )} New Password @@ -123,16 +164,40 @@ const ChangePassword = () => { type="password" placeholder="new password" name="newPassword" - value={formData.newPassword} - onChange={handleInputChange} + value={user.newPassword} + onChange={handleChange} pattern="^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$" title="The password should have 1 upper-case letter, 1 lower-case letter, 1 number, 1 special character and at least 8 characters." /> + {errors.newPasswordError && ( +

{errors.newPasswordError}

+ )} + + Confirm Password + + + + + + + + {errors.confirmPasswordError && ( +

{errors.confirmPasswordError}

+ )}
diff --git a/src/views/pages/profile/MyProfile.js b/src/views/pages/profile/MyProfile.js index a4f7106..ad0d07d 100644 --- a/src/views/pages/profile/MyProfile.js +++ b/src/views/pages/profile/MyProfile.js @@ -10,6 +10,9 @@ import { } from '@fortawesome/free-solid-svg-icons' import { Col, Row, Form, Card, Button, Container, InputGroup } from '@themesberg/react-bootstrap' import { Link, useNavigate } from 'react-router-dom' +import { isAutheticated } from '../../../auth' +import Axios from '../../../axios' +import Swal from 'sweetalert2' // import { Axios } from 'axios' // import Axios from '../../axios' // import { Routes } from '../../routes' @@ -17,98 +20,91 @@ import { Link, useNavigate } from 'react-router-dom' // import Swal from 'sweetalert2' const MyProfile = () => { - const navigate = useNavigate() - const [Loading, setLoading] = useState(false) + const [image, setImage] = useState('') + const [loading, setLoading] = useState(false) + const [imagesPreview, setImagesPreview] = useState() + const token = isAutheticated() - const [formData, setFormData] = useState({ - mobile: '', + const [ownerDetails, setOwnerDetails] = useState({ name: '', email: '', + phone: '', }) + const history = useNavigate() - console.log(formData) + const getData = async () => { + let res = await Axios.get(`/api/v1/user/details`, { + headers: { + Authorization: `Bearer ${token}`, + }, + }) + if (res.data.success) { + setOwnerDetails({ ...res.data.user }) - // console.log(formData); - const handleInputChange = (e) => { - const { name, value } = e.target - setFormData((prevData) => ({ ...prevData, [name]: value })) + if (res.data.user.avatar) { + setImagesPreview(res.data.user.avatar.url) + } + } } - const formHandler = async (e) => { + const handleChange = (event) => { + const { name, value } = event.target + setOwnerDetails({ ...ownerDetails, [name]: value }) + } + async function handleSubmit(e) { e.preventDefault() - // const jwt = localStorage.getItem('jwt') - // console.log(jwt); + if (ownerDetails.name === '' || ownerDetails.email === '' || ownerDetails.phone === '') { + Swal.fire({ + title: 'Warning', + text: 'Fill all mandatory fields', + icon: 'error', + button: 'Close', + dangerMode: true, + }) + return + } + const formData = new FormData() + formData.append('name', ownerDetails.name) + formData.append('email', ownerDetails.email) + formData.append('phone', ownerDetails.phone) - // try { - // const response = await Axios.post( - // '/api/user', - // { - // username: formData.name, - // newNumber: formData.mobile, - // email: formData.email, - // }, - // { - // headers: { - // jwt: `${jwt}`, - // 'Content-Type': 'application/json', - // }, - // }, - // ) - // // console.log("response ", response); - // if (response.status === 200) { - // // Display success alert - // Swal.fire({ - // icon: 'success', - // title: 'Profile Updated Successfully', - // text: 'Your Profile has been Updated successfully!', - // }).then((result) => { - // if (result.isConfirmed) { - // history.push(Routes.Presentation.path) - // // history.push(Routes.Dashboard.path); - // } - // }) - - // // Redirect to the dashboard - // } else { - // // Handle other scenarios if needed - // Swal.fire({ - // icon: 'error', - // title: 'Profile Update Failed', - // text: 'There was an issue updating your profile. Please try again.', - // }) - // } - // } catch (err) { - // console.log('error is ', err) - // Swal.fire({ - // icon: 'error', - // title: 'Profile Update Failed', - // text: 'There was an issue updating your profile. Please try again.', - // }) - // } + setLoading(true) + try { + const res = await Axios.put(`/api/v1/user/update/profile`, formData, { + headers: { + 'Access-Control-Allow-Origin': '*', + Authorization: `Bearer ${token}`, + 'Content-Type': 'multipart/formdata', + }, + }) + if (res.data.success === true) { + setLoading(false) + Swal.fire({ + title: 'Edited', + text: 'Profile Edited Successfully!', + icon: 'success', + button: 'Return', + }) + } + } catch (error) { + const message = error?.response?.data?.message || 'Something went wrong!' + setLoading(false) + Swal.fire({ + title: 'Warning', + text: message, + icon: 'error', + button: 'Retry', + dangerMode: true, + }) + } } - // const getMe = async () => { - // const jwt = localStorage.getItem('jwt') + console.log(ownerDetails) + const handleCancle = () => { + Navigate('/dashboard') + } + useEffect(() => { + getData() + }, []) - // try { - // const respose = await Axios.get('/api/getMe', { - // headers: { - // jwt: `${jwt}`, - // 'Content-Type': 'application/json', - // }, - // }) - - // // console.log(respose.data); - // setFormData({ - // mobile: respose?.data?.number, - // name: respose?.data?.name, - // email: respose?.data?.email, - // }) - // } catch (err) { - // console.log('error in get user', err) - // } - // } - // useEffect(() => { - // getMe() - // }, []) return (
@@ -122,7 +118,7 @@ const MyProfile = () => {

Profile

-
+ Name @@ -134,8 +130,8 @@ const MyProfile = () => { type="text" placeholder="Your Name" name="name" - value={formData.name} - onChange={handleInputChange} + value={ownerDetails.name} + onChange={handleChange} /> @@ -152,9 +148,9 @@ const MyProfile = () => { type="email" placeholder="example@gmail.com" name="email" - value={formData.email} - onChange={handleInputChange} - pattern="[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}" + value={ownerDetails.email} + onChange={handleChange} + // pattern="[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}" title="Please enter a valid email address" /> @@ -171,8 +167,8 @@ const MyProfile = () => { type="tel" placeholder="Your Mobile Number" name="mobile" - value={formData.mobile} - onChange={handleInputChange} + value={ownerDetails.phone} + onChange={handleChange} // pattern="[0-9]{10}" // Assuming a 10-digit mobile number, adjust as needed title="Please enter a valid 10-digit mobile number" /> @@ -180,7 +176,7 @@ const MyProfile = () => {
diff --git a/src/views/shops/Shop.js b/src/views/shops/Shop.js index 23c8d67..eb113d5 100644 --- a/src/views/shops/Shop.js +++ b/src/views/shops/Shop.js @@ -1,9 +1,58 @@ -import React from 'react' +import { + Box, + Card, + CircularProgress, + Container, + FormControl, + Grid, + MenuItem, + Select, + Typography, +} from '@mui/material' +import cardData from '../../assets/data/cardjson.json' +import React, { useState } from 'react' +import ShopCard from './shopCard' const Shop = () => { + const [option, setOption] = useState('all') + + const handleChange = (event) => { + setOption(event.target.value) + } + + const filteredData = + option === 'all' ? cardData : cardData.filter((item) => item.categoryName === option) + return ( <> -
Shop
+ + + Categories + + + + + {/* Cards */} + + {filteredData.map((item, i) => ( + + + + ))} + + + ) } diff --git a/src/views/shops/shopCard.js b/src/views/shops/shopCard.js new file mode 100644 index 0000000..a1a4a67 --- /dev/null +++ b/src/views/shops/shopCard.js @@ -0,0 +1,33 @@ +/* eslint-disable react/prop-types */ +import { Button, Card, CardContent, CardMedia, Typography } from '@mui/material' +import React from 'react' + +// eslint-disable-next-line react/prop-types +const ShopCard = ({ item }) => { + return ( +
+ + + + + {item?.name} + + + ${item?.price} + + + + +
+ ) +} + +export default ShopCard