PD auth and protected routes done
This commit is contained in:
parent
823fbd7d19
commit
d5754487ad
@ -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",
|
||||
|
14
src/App.js
14
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 = () => {
|
||||
}
|
||||
>
|
||||
<Routes>
|
||||
<Route exact path="/my-profile" name="My profile" element={<MyProfile />} />
|
||||
<Route exact path="/change-password" name="My profile" element={<ChangePassword />} />
|
||||
{/* <Route exact path="/change-password" name="My profile" element={<ChangePassword />} /> */}
|
||||
<Route exact path="/login" name="Login Page" element={<Login />} />
|
||||
<Route exact path="/register" name="Register Page" element={<Register />} />
|
||||
<Route exact path="/404" name="Page 404" element={<Page404 />} />
|
||||
<Route exact path="/500" name="Page 500" element={<Page500 />} />
|
||||
<Route
|
||||
exact
|
||||
path="/forget-password"
|
||||
name="Forget password "
|
||||
element={<ForgetPassword />}
|
||||
/>
|
||||
<Route path="/*" element={<ProtectedRoute element={DefaultLayout} />} />
|
||||
<Route path="*" name="Home" element={<DefaultLayout />} />
|
||||
</Routes>
|
||||
</Suspense>
|
||||
|
17
src/_nav.js
17
src/_nav.js
@ -23,10 +23,6 @@ const _nav = [
|
||||
name: 'Dashboard',
|
||||
to: '/dashboard',
|
||||
icon: <CIcon icon={cilSpeedometer} customClassName="nav-icon" />,
|
||||
badge: {
|
||||
color: 'info',
|
||||
text: 'NEW',
|
||||
},
|
||||
},
|
||||
{
|
||||
component: CNavItem,
|
||||
@ -34,12 +30,19 @@ const _nav = [
|
||||
to: '/shop',
|
||||
icon: <CIcon icon={cilShare} customClassName="nav-icon" />,
|
||||
},
|
||||
// {
|
||||
// component: CNavItem,
|
||||
// name: 'Orders',
|
||||
// to: '/order',
|
||||
// icon: <CIcon icon={cilBorderAll} customClassName="nav-icon" />,
|
||||
// },
|
||||
{
|
||||
component: CNavItem,
|
||||
name: 'Orders',
|
||||
to: '/order',
|
||||
icon: <CIcon icon={cilBorderAll} customClassName="nav-icon" />,
|
||||
name: 'KYC',
|
||||
to: '/kyc',
|
||||
icon: <CIcon icon={cilDescription} customClassName="nav-icon" />,
|
||||
},
|
||||
|
||||
// {
|
||||
// component: CNavTitle,
|
||||
// name: 'Theme',
|
||||
|
26
src/assets/data/cardjson.json
Normal file
26
src/assets/data/cardjson.json
Normal file
@ -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"
|
||||
}
|
||||
]
|
10
src/auth.js
Normal file
10
src/auth.js
Normal file
@ -0,0 +1,10 @@
|
||||
export const isAutheticated = () => {
|
||||
if (typeof window == 'undefined') {
|
||||
return true
|
||||
}
|
||||
if (localStorage.getItem('authToken')) {
|
||||
return localStorage.getItem('authToken')
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
@ -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
|
||||
|
@ -4,7 +4,7 @@ import { CFooter } from '@coreui/react'
|
||||
const AppFooter = () => {
|
||||
return (
|
||||
<CFooter className="px-4">
|
||||
<div>
|
||||
{/* <div>
|
||||
<a href="https://coreui.io" target="_blank" rel="noopener noreferrer">
|
||||
CoreUI
|
||||
</a>
|
||||
@ -15,7 +15,7 @@ const AppFooter = () => {
|
||||
<a href="https://coreui.io/react" target="_blank" rel="noopener noreferrer">
|
||||
CoreUI React Admin & Dashboard Template
|
||||
</a>
|
||||
</div>
|
||||
</div> */}
|
||||
</CFooter>
|
||||
)
|
||||
}
|
||||
|
@ -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 (
|
||||
<CDropdown variant="nav-item">
|
||||
<CDropdownToggle placement="bottom-end" className="py-0 pe-0" caret={false}>
|
||||
<div className="media d-flex align-items-center">
|
||||
{/* <Image src={"Profile3"} className="user-avatar md-avatar rounded-circle" /> */}
|
||||
<FontAwesomeIcon
|
||||
style={{ fontSize: '2rem' }}
|
||||
className="user-avatar md-avatar rounded-circle "
|
||||
icon={faUserCircle}
|
||||
/>
|
||||
<div className="media-body ms-2 text-dark align-items-center d-none d-lg-block">
|
||||
<span className="mb-0 font-small fw-bold"> Garg</span>
|
||||
<span className="mb-0 font-small fw-bold"> {user?.name}</span>
|
||||
</div>
|
||||
</div>
|
||||
</CDropdownToggle>
|
||||
<CDropdownMenu className="pt-0" placement="bottom-end">
|
||||
<CDropdownHeader className="bg-body-secondary fw-semibold mb-2">Account</CDropdownHeader>
|
||||
{/* <CDropdownItem href="#">
|
||||
<CIcon icon={cilBell} className="me-2" />
|
||||
Update
|
||||
<CBadge color="info" className="ms-2">
|
||||
42
|
||||
</CBadge>
|
||||
</CDropdownItem>
|
||||
<CDropdownItem href="#">
|
||||
<CIcon icon={cilEnvelopeOpen} className="me-2" />
|
||||
Messages
|
||||
<CBadge color="success" className="ms-2">
|
||||
42
|
||||
</CBadge>
|
||||
</CDropdownItem>
|
||||
<CDropdownItem href="#">
|
||||
<CIcon icon={cilTask} className="me-2" />
|
||||
Tasks
|
||||
<CBadge color="danger" className="ms-2">
|
||||
42
|
||||
</CBadge>
|
||||
</CDropdownItem>
|
||||
<CDropdownItem href="#">
|
||||
<CIcon icon={cilCommentSquare} className="me-2" />
|
||||
Comments
|
||||
<CBadge color="warning" className="ms-2">
|
||||
42
|
||||
</CBadge>
|
||||
</CDropdownItem>
|
||||
<CDropdownHeader className="bg-body-secondary fw-semibold my-2">Settings</CDropdownHeader> */}
|
||||
|
||||
<CDropdownItem onClick={() => navigate('/my-profile')}>
|
||||
<CIcon icon={cilUser} className="me-2" />
|
||||
Profile
|
||||
@ -83,22 +78,9 @@ const AppHeaderDropdown = () => {
|
||||
<CIcon icon={cilSettings} className="me-2" />
|
||||
Change Password
|
||||
</CDropdownItem>
|
||||
{/* <CDropdownItem href="#">
|
||||
<CIcon icon={cilCreditCard} className="me-2" />
|
||||
Payments
|
||||
<CBadge color="secondary" className="ms-2">
|
||||
42
|
||||
</CBadge>
|
||||
</CDropdownItem>
|
||||
<CDropdownItem href="#">
|
||||
<CIcon icon={cilFile} className="me-2" />
|
||||
Projects
|
||||
<CBadge color="primary" className="ms-2">
|
||||
42
|
||||
</CBadge>
|
||||
</CDropdownItem> */}
|
||||
|
||||
<CDropdownDivider />
|
||||
<CDropdownItem href="#">
|
||||
<CDropdownItem onClick={signout}>
|
||||
<CIcon icon={cilLockLocked} className="me-2" />
|
||||
Logout
|
||||
</CDropdownItem>
|
||||
|
19
src/protectedRoute.js
Normal file
19
src/protectedRoute.js
Normal file
@ -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 <Element />
|
||||
}
|
||||
|
||||
export default ProtectedRoute
|
@ -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
|
||||
|
126
src/views/pages/Kyc/kyc.js
Normal file
126
src/views/pages/Kyc/kyc.js
Normal file
@ -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 (
|
||||
<Box sx={{ width: '100%' }}>
|
||||
<Paper sx={{ width: '100%', mb: 2 }}>
|
||||
<TableContainer>
|
||||
<Table>
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableCell>ID</TableCell>
|
||||
<TableCell>Trade Name</TableCell>
|
||||
<TableCell>Created On</TableCell>
|
||||
<TableCell>Status</TableCell>
|
||||
<TableCell>Action</TableCell>
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{rows.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage).map((row) => (
|
||||
<TableRow key={row.id}>
|
||||
<TableCell>{row.id}</TableCell>
|
||||
<TableCell>{row.tradeName}</TableCell>
|
||||
<TableCell>{format(row.createdOn, 'yyyy-MM-dd HH:mm:ss')}</TableCell>
|
||||
<TableCell>{row.status}</TableCell>
|
||||
<TableCell>
|
||||
<Tooltip title="View">
|
||||
{/* <IconButton color="primary">
|
||||
<Visibility />
|
||||
</IconButton> */}
|
||||
<Button
|
||||
sx={{ mr: '1rem' }}
|
||||
color="primary"
|
||||
variant="contained"
|
||||
onClick={() => handleViewClick(row.id)}
|
||||
>
|
||||
View
|
||||
</Button>
|
||||
</Tooltip>
|
||||
<Tooltip title="Approve">
|
||||
{/* <IconButton color="success">
|
||||
<ThumbUp />
|
||||
</IconButton> */}
|
||||
<Button sx={{ mr: '1rem' }} color="success" variant="contained">
|
||||
Approve
|
||||
</Button>
|
||||
</Tooltip>
|
||||
<Tooltip title="Reject">
|
||||
{/* <IconButton color="error">
|
||||
<ThumbDown />
|
||||
</IconButton> */}
|
||||
<Button sx={{ mr: '1rem' }} color="error" variant="contained">
|
||||
Reject
|
||||
</Button>
|
||||
</Tooltip>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
<TablePagination
|
||||
rowsPerPageOptions={[5, 10, 25]}
|
||||
component="div"
|
||||
count={rows.length}
|
||||
rowsPerPage={rowsPerPage}
|
||||
page={page}
|
||||
onPageChange={handleChangePage}
|
||||
onRowsPerPageChange={handleChangeRowsPerPage}
|
||||
/>
|
||||
</Paper>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
export default Kyc
|
190
src/views/pages/Kyc/kycDetails.js
Normal file
190
src/views/pages/Kyc/kycDetails.js
Normal file
@ -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 (
|
||||
<Box sx={{ p: 3 }}>
|
||||
<Typography variant="h4" gutterBottom>
|
||||
Retailer Details
|
||||
</Typography>
|
||||
|
||||
<Paper sx={{ p: 2, mb: 3 }}>
|
||||
<Typography variant="h5" gutterBottom>
|
||||
Retailer Details
|
||||
</Typography>
|
||||
<Grid container spacing={2}>
|
||||
<Grid item xs={6}>
|
||||
<Typography>
|
||||
<strong>Trade Name:</strong> {retailerDetails.tradeName}
|
||||
</Typography>
|
||||
<Typography>
|
||||
<strong>Name:</strong> {retailerDetails.name}
|
||||
</Typography>
|
||||
<Typography>
|
||||
<strong>Address:</strong> {retailerDetails.address}
|
||||
</Typography>
|
||||
<Typography>
|
||||
<strong>Town/City:</strong> {retailerDetails.townCity}
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={6}>
|
||||
<Typography>
|
||||
<strong>District:</strong> {retailerDetails.district}
|
||||
</Typography>
|
||||
<Typography>
|
||||
<strong>State:</strong> {retailerDetails.state}
|
||||
</Typography>
|
||||
<Typography>
|
||||
<strong>Pincode:</strong> {retailerDetails.pincode}
|
||||
</Typography>
|
||||
<Typography>
|
||||
<strong>Mobile Number:</strong> {retailerDetails.mobileNumber}
|
||||
</Typography>
|
||||
<Typography>
|
||||
<strong>Mapped Principal Distributor:</strong> {retailerDetails.mappedDistributor}
|
||||
</Typography>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Paper>
|
||||
|
||||
<Paper sx={{ p: 2, mb: 3 }}>
|
||||
<Typography variant="h6" gutterBottom>
|
||||
Documents
|
||||
</Typography>
|
||||
<Grid container spacing={2}>
|
||||
<Grid item xs={6}>
|
||||
<Typography>
|
||||
<strong>PAN Number:</strong> {retailerDetails.documents.panNumber}
|
||||
</Typography>
|
||||
<Avatar
|
||||
variant="square"
|
||||
src={retailerDetails.documents.panCard}
|
||||
sx={{ width: 100, height: 100, mb: 2 }}
|
||||
/>
|
||||
<Typography>
|
||||
<strong>Aadhar Number:</strong> {retailerDetails.documents.aadharNumber}
|
||||
</Typography>
|
||||
<Avatar
|
||||
variant="square"
|
||||
src={retailerDetails.documents.aadharCard}
|
||||
sx={{ width: 100, height: 100, mb: 2 }}
|
||||
/>
|
||||
<Typography>
|
||||
<strong>GST Number:</strong> {retailerDetails.documents.gstNumber}
|
||||
</Typography>
|
||||
<Avatar
|
||||
variant="square"
|
||||
src={retailerDetails.documents.gstRegistration}
|
||||
sx={{ width: 100, height: 100, mb: 2 }}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={6}>
|
||||
<Typography>
|
||||
<strong>Pesticide License:</strong>
|
||||
</Typography>
|
||||
<Avatar
|
||||
variant="square"
|
||||
src={retailerDetails.documents.pesticideLicense}
|
||||
sx={{ width: 100, height: 100, mb: 2 }}
|
||||
/>
|
||||
<Typography>
|
||||
<strong>Fertilizer License (optional):</strong>
|
||||
</Typography>
|
||||
<Avatar
|
||||
variant="square"
|
||||
src={retailerDetails.documents.fertilizerLicense}
|
||||
sx={{ width: 100, height: 100, mb: 2 }}
|
||||
/>
|
||||
<Typography>
|
||||
<strong>Selfie of Entrance Board:</strong>
|
||||
</Typography>
|
||||
<Avatar
|
||||
variant="square"
|
||||
src={retailerDetails.documents.entranceBoardSelfie}
|
||||
sx={{ width: 100, height: 100, mb: 2 }}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Paper>
|
||||
|
||||
<Paper sx={{ p: 2, mb: 3 }}>
|
||||
<Typography variant="h6" gutterBottom>
|
||||
Block 3: Sales Coordinators/Territory Manager Details
|
||||
</Typography>
|
||||
<Grid container spacing={2}>
|
||||
<Grid item xs={6}>
|
||||
<Typography>
|
||||
<strong>Designation:</strong> {retailerDetails.salesCoordinator.designation}
|
||||
</Typography>
|
||||
<Typography>
|
||||
<strong>Name:</strong> {retailerDetails.salesCoordinator.name}
|
||||
</Typography>
|
||||
<Typography>
|
||||
<strong>Employee ID:</strong> {retailerDetails.salesCoordinator.employeeId}
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={6}>
|
||||
<Typography>
|
||||
<strong>Uploaded on:</strong>{' '}
|
||||
{format(retailerDetails.salesCoordinator.uploadedOn, 'yyyy-MM-dd HH:mm:ss')}
|
||||
</Typography>
|
||||
<Typography>
|
||||
<strong>Resubmitted on:</strong>{' '}
|
||||
{format(retailerDetails.salesCoordinator.resubmittedOn, 'yyyy-MM-dd HH:mm:ss')}
|
||||
</Typography>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Paper>
|
||||
|
||||
<Paper sx={{ p: 2, mb: 3 }}>
|
||||
<Typography variant="h6" gutterBottom>
|
||||
Block 4: Notes
|
||||
</Typography>
|
||||
{retailerDetails.notes.map((note, index) => (
|
||||
<Typography key={index}>{note}</Typography>
|
||||
))}
|
||||
</Paper>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
export default KycDetails
|
106
src/views/pages/forgetPassword.js
Normal file
106
src/views/pages/forgetPassword.js
Normal file
@ -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 (
|
||||
<>
|
||||
<div className="bg-body-tertiary min-vh-100 d-flex flex-row align-items-center">
|
||||
<CContainer>
|
||||
<CRow className="justify-content-center">
|
||||
<CCol md={8}>
|
||||
<CCardGroup>
|
||||
<CCard className="p-4">
|
||||
<CCardBody>
|
||||
<CForm onSubmit={handleSubmit}>
|
||||
<h1>Forget Password</h1>
|
||||
<p className="text-body-secondary">
|
||||
Enter your email we will send you the password
|
||||
</p>
|
||||
<CInputGroup className="mb-3">
|
||||
<CInputGroupText>
|
||||
<FontAwesomeIcon icon={faEnvelope} />
|
||||
</CInputGroupText>
|
||||
<CFormInput
|
||||
type="email"
|
||||
required
|
||||
placeholder="abc@gmail.com"
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
value={email}
|
||||
name="email"
|
||||
autoComplete="email"
|
||||
/>
|
||||
</CInputGroup>
|
||||
|
||||
<CRow>
|
||||
<CCol xs={12}>
|
||||
<CButton
|
||||
color="primary"
|
||||
type="submit"
|
||||
style={{ width: '100%' }}
|
||||
className="px-4"
|
||||
disabled={loading}
|
||||
>
|
||||
{loading ? <CSpinner variant="grow" /> : 'Generate password '}
|
||||
</CButton>
|
||||
</CCol>
|
||||
<CCol xs={6} className="text-right">
|
||||
<span className="text-body-secondary">
|
||||
If you know you password? Continue to
|
||||
</span>
|
||||
<CButton onClick={() => navigate('/login')} color="link" className="px-2">
|
||||
Sign in?
|
||||
</CButton>
|
||||
</CCol>
|
||||
</CRow>
|
||||
</CForm>
|
||||
</CCardBody>
|
||||
</CCard>
|
||||
</CCardGroup>
|
||||
</CCol>
|
||||
</CRow>
|
||||
</CContainer>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default ForgetPassword
|
@ -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 (
|
||||
<div className="bg-body-tertiary min-vh-100 d-flex flex-row align-items-center">
|
||||
<CContainer>
|
||||
@ -32,26 +131,53 @@ const Login = () => {
|
||||
<CInputGroupText>
|
||||
<CIcon icon={cilUser} />
|
||||
</CInputGroupText>
|
||||
<CFormInput placeholder="Username" autoComplete="username" />
|
||||
<CFormInput
|
||||
type="email"
|
||||
placeholder="Email"
|
||||
onChange={handleChange}
|
||||
value={auth.email}
|
||||
name="email"
|
||||
autoComplete="email"
|
||||
/>
|
||||
</CInputGroup>
|
||||
{errors.emailError && (
|
||||
<p className="text-center py-2 text-danger">{errors.emailError}</p>
|
||||
)}
|
||||
<CInputGroup className="mb-4">
|
||||
<CInputGroupText>
|
||||
<CIcon icon={cilLockLocked} />
|
||||
</CInputGroupText>
|
||||
<CFormInput
|
||||
type="password"
|
||||
name="password"
|
||||
value={auth.password}
|
||||
onChange={handleChange}
|
||||
placeholder="Password"
|
||||
autoComplete="current-password"
|
||||
/>
|
||||
</CInputGroup>
|
||||
|
||||
{errors.passwordError && (
|
||||
<p className="text-center py-2 text-danger">{errors.passwordError}</p>
|
||||
)}
|
||||
<CRow>
|
||||
<CCol xs={6}>
|
||||
<CButton color="primary" className="px-4">
|
||||
Login
|
||||
<CButton
|
||||
color="primary"
|
||||
type="submit"
|
||||
className="px-4"
|
||||
onClick={handleSubmit}
|
||||
disabled={loading}
|
||||
>
|
||||
{loading ? <CSpinner variant="grow" /> : 'Login'}
|
||||
</CButton>
|
||||
</CCol>
|
||||
<CCol xs={6} className="text-right">
|
||||
<CButton color="link" className="px-0">
|
||||
<CButton
|
||||
onClick={() => navigate('/forget-password')}
|
||||
color="link"
|
||||
className="px-0"
|
||||
>
|
||||
Forgot password?
|
||||
</CButton>
|
||||
</CCol>
|
||||
@ -59,22 +185,6 @@ const Login = () => {
|
||||
</CForm>
|
||||
</CCardBody>
|
||||
</CCard>
|
||||
<CCard className="text-white bg-primary py-5" style={{ width: '44%' }}>
|
||||
<CCardBody className="text-center">
|
||||
<div>
|
||||
<h2>Sign up</h2>
|
||||
<p>
|
||||
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
|
||||
tempor incididunt ut labore et dolore magna aliqua.
|
||||
</p>
|
||||
<Link to="/register">
|
||||
<CButton color="primary" className="mt-3" active tabIndex={-1}>
|
||||
Register Now!
|
||||
</CButton>
|
||||
</Link>
|
||||
</div>
|
||||
</CCardBody>
|
||||
</CCard>
|
||||
</CCardGroup>
|
||||
</CCol>
|
||||
</CRow>
|
||||
|
@ -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 (
|
||||
<main>
|
||||
@ -93,7 +131,7 @@ const ChangePassword = () => {
|
||||
>
|
||||
<div className="bg-white shadow-soft border rounded border-light p-4 p-lg-5 w-100 fmxw-500">
|
||||
<h3 className="mb-4">Change Password</h3>
|
||||
<Form onSubmit={formHandler}>
|
||||
<Form onSubmit={handleSubmit}>
|
||||
<Form.Group id="password" className="mb-4">
|
||||
<Form.Label>Old Password</Form.Label>
|
||||
<InputGroup>
|
||||
@ -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."
|
||||
/>
|
||||
</InputGroup>
|
||||
</Form.Group>
|
||||
{errors.oldPasswordError && (
|
||||
<p className="text-center py-2 text-danger">{errors.oldPasswordError}</p>
|
||||
)}
|
||||
<Form.Group id="password" className="mb-4">
|
||||
<Form.Label>New Password</Form.Label>
|
||||
<InputGroup>
|
||||
@ -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."
|
||||
/>
|
||||
</InputGroup>
|
||||
</Form.Group>
|
||||
{errors.newPasswordError && (
|
||||
<p className="text-center py-2 text-danger">{errors.newPasswordError}</p>
|
||||
)}
|
||||
<Form.Group id="password" className="mb-4">
|
||||
<Form.Label>Confirm Password</Form.Label>
|
||||
<InputGroup>
|
||||
<InputGroup.Text>
|
||||
<FontAwesomeIcon icon={faLockOpen} />
|
||||
</InputGroup.Text>
|
||||
<Form.Control
|
||||
required
|
||||
type="password"
|
||||
placeholder="Confirm password"
|
||||
name="confirmPassword"
|
||||
value={user.confirmPassword}
|
||||
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."
|
||||
/>
|
||||
</InputGroup>
|
||||
</Form.Group>
|
||||
{errors.confirmPasswordError && (
|
||||
<p className="text-center py-2 text-danger">{errors.confirmPasswordError}</p>
|
||||
)}
|
||||
|
||||
<Button variant="primary" type="submit" className="w-100">
|
||||
{Loading ? <>Loading...</> : <> Change Password </>}
|
||||
{loading ? <>Loading...</> : <> Change Password </>}
|
||||
</Button>
|
||||
</Form>
|
||||
</div>
|
||||
|
@ -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 (
|
||||
<main>
|
||||
<section className="bg-soft d-flex align-items-center my-5 mt-lg-6 mb-lg-5">
|
||||
@ -122,7 +118,7 @@ const MyProfile = () => {
|
||||
<Col xs={12} className="d-flex align-items-center justify-content-center">
|
||||
<div className="bg-white shadow-soft border rounded border-light p-4 p-lg-5 w-100 fmxw-500">
|
||||
<h3 className="mb-4">Profile</h3>
|
||||
<Form onSubmit={formHandler}>
|
||||
<Form onSubmit={handleSubmit}>
|
||||
<Form.Group id="name" className="mb-4">
|
||||
<Form.Label>Name</Form.Label>
|
||||
<InputGroup>
|
||||
@ -134,8 +130,8 @@ const MyProfile = () => {
|
||||
type="text"
|
||||
placeholder="Your Name"
|
||||
name="name"
|
||||
value={formData.name}
|
||||
onChange={handleInputChange}
|
||||
value={ownerDetails.name}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
</InputGroup>
|
||||
</Form.Group>
|
||||
@ -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"
|
||||
/>
|
||||
</InputGroup>
|
||||
@ -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 = () => {
|
||||
</Form.Group>
|
||||
|
||||
<Button variant="primary" type="submit" className="w-100">
|
||||
{Loading ? <>Loading...</> : <>Update</>}
|
||||
{loading ? <>Loading...</> : <>Update</>}
|
||||
</Button>
|
||||
</Form>
|
||||
</div>
|
||||
|
@ -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 (
|
||||
<>
|
||||
<div>Shop</div>
|
||||
<Container>
|
||||
<Typography sx={{ fontWeight: 'bold' }} variant="h4">
|
||||
Categories
|
||||
</Typography>
|
||||
<Select
|
||||
sx={{ width: '400px', mt: '1rem' }}
|
||||
labelId="demo-simple-select-label"
|
||||
id="demo-simple-select"
|
||||
value={option}
|
||||
label=""
|
||||
onChange={handleChange}
|
||||
>
|
||||
<MenuItem value="all">All</MenuItem>
|
||||
<MenuItem value="camera">Camera</MenuItem>
|
||||
<MenuItem value="phone">Phone</MenuItem>
|
||||
</Select>
|
||||
|
||||
<Box mt={3}>
|
||||
{/* Cards */}
|
||||
<Grid container spacing={2}>
|
||||
{filteredData.map((item, i) => (
|
||||
<Grid key={i} item xs={12} sm={6} md={4} lg={4}>
|
||||
<ShopCard item={item} />
|
||||
</Grid>
|
||||
))}
|
||||
</Grid>
|
||||
</Box>
|
||||
</Container>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
33
src/views/shops/shopCard.js
Normal file
33
src/views/shops/shopCard.js
Normal file
@ -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 (
|
||||
<div>
|
||||
<Card>
|
||||
<CardMedia component="img" height="150" image={item?.image} alt={item?.name} />
|
||||
<CardContent>
|
||||
<Typography gutterBottom variant="h5" component="div">
|
||||
{item?.name}
|
||||
</Typography>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
${item?.price}
|
||||
</Typography>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
fullWidth
|
||||
// onClick={handleAddToCart}
|
||||
sx={{ marginTop: '10px' }}
|
||||
>
|
||||
Add to Cart
|
||||
</Button>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default ShopCard
|
Loading…
Reference in New Issue
Block a user