PD auth and protected routes done

This commit is contained in:
ROSHAN GARG 2024-07-26 18:21:32 +05:30
parent 823fbd7d19
commit d5754487ad
18 changed files with 978 additions and 241 deletions

View File

@ -26,14 +26,20 @@
"@coreui/react": "^5.1.0", "@coreui/react": "^5.1.0",
"@coreui/react-chartjs": "^3.0.0", "@coreui/react-chartjs": "^3.0.0",
"@coreui/utils": "^2.0.2", "@coreui/utils": "^2.0.2",
"@emotion/react": "^11.13.0",
"@emotion/styled": "^11.13.0",
"@fortawesome/fontawesome-svg-core": "^6.6.0", "@fortawesome/fontawesome-svg-core": "^6.6.0",
"@fortawesome/free-solid-svg-icons": "^6.6.0", "@fortawesome/free-solid-svg-icons": "^6.6.0",
"@fortawesome/react-fontawesome": "^0.2.2", "@fortawesome/react-fontawesome": "^0.2.2",
"@mui/icons-material": "^5.16.4",
"@mui/material": "^5.16.4",
"@popperjs/core": "^2.11.8", "@popperjs/core": "^2.11.8",
"@themesberg/react-bootstrap": "^1.4.1", "@themesberg/react-bootstrap": "^1.4.1",
"axios": "^1.7.2",
"chart.js": "^4.4.3", "chart.js": "^4.4.3",
"classnames": "^2.5.1", "classnames": "^2.5.1",
"core-js": "^3.37.1", "core-js": "^3.37.1",
"date-fns": "^3.6.0",
"prop-types": "^15.8.1", "prop-types": "^15.8.1",
"react": "^18.3.1", "react": "^18.3.1",
"react-dom": "^18.3.1", "react-dom": "^18.3.1",
@ -41,7 +47,7 @@
"react-router-dom": "^6.23.1", "react-router-dom": "^6.23.1",
"redux": "5.0.1", "redux": "5.0.1",
"simplebar-react": "^3.2.5", "simplebar-react": "^3.2.5",
"sweetalert2": "^11.12.2" "sweetalert2": "^11.12.3"
}, },
"devDependencies": { "devDependencies": {
"@vitejs/plugin-react": "^4.3.1", "@vitejs/plugin-react": "^4.3.1",

View File

@ -4,6 +4,7 @@ import { useSelector } from 'react-redux'
import { CSpinner, useColorModes } from '@coreui/react' import { CSpinner, useColorModes } from '@coreui/react'
import './scss/style.scss' import './scss/style.scss'
import ProtectedRoute from './protectedRoute'
// Containers // Containers
const DefaultLayout = React.lazy(() => import('./layout/DefaultLayout')) 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 Register = React.lazy(() => import('./views/pages/register/Register'))
const Page404 = React.lazy(() => import('./views/pages/page404/Page404')) const Page404 = React.lazy(() => import('./views/pages/page404/Page404'))
const Page500 = React.lazy(() => import('./views/pages/page500/Page500')) const Page500 = React.lazy(() => import('./views/pages/page500/Page500'))
const MyProfile = React.lazy(() => import('./views/pages/profile/MyProfile')) const ForgetPassword = React.lazy(() => import('./views/pages/forgetPassword'))
const ChangePassword = React.lazy(() => import('./views/pages/profile/ChangePassword'))
const App = () => { const App = () => {
const { isColorModeSet, setColorMode } = useColorModes('coreui-free-react-admin-template-theme') const { isColorModeSet, setColorMode } = useColorModes('coreui-free-react-admin-template-theme')
@ -46,12 +46,18 @@ const App = () => {
} }
> >
<Routes> <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="/login" name="Login Page" element={<Login />} />
<Route exact path="/register" name="Register Page" element={<Register />} /> <Route exact path="/register" name="Register Page" element={<Register />} />
<Route exact path="/404" name="Page 404" element={<Page404 />} /> <Route exact path="/404" name="Page 404" element={<Page404 />} />
<Route exact path="/500" name="Page 500" element={<Page500 />} /> <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 />} /> <Route path="*" name="Home" element={<DefaultLayout />} />
</Routes> </Routes>
</Suspense> </Suspense>

View File

@ -23,10 +23,6 @@ const _nav = [
name: 'Dashboard', name: 'Dashboard',
to: '/dashboard', to: '/dashboard',
icon: <CIcon icon={cilSpeedometer} customClassName="nav-icon" />, icon: <CIcon icon={cilSpeedometer} customClassName="nav-icon" />,
badge: {
color: 'info',
text: 'NEW',
},
}, },
{ {
component: CNavItem, component: CNavItem,
@ -34,12 +30,19 @@ const _nav = [
to: '/shop', to: '/shop',
icon: <CIcon icon={cilShare} customClassName="nav-icon" />, icon: <CIcon icon={cilShare} customClassName="nav-icon" />,
}, },
// {
// component: CNavItem,
// name: 'Orders',
// to: '/order',
// icon: <CIcon icon={cilBorderAll} customClassName="nav-icon" />,
// },
{ {
component: CNavItem, component: CNavItem,
name: 'Orders', name: 'KYC',
to: '/order', to: '/kyc',
icon: <CIcon icon={cilBorderAll} customClassName="nav-icon" />, icon: <CIcon icon={cilDescription} customClassName="nav-icon" />,
}, },
// { // {
// component: CNavTitle, // component: CNavTitle,
// name: 'Theme', // name: 'Theme',

View 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
View 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
}
}

View File

@ -1,10 +1,10 @@
import axios from 'axios' import axios from 'axios'
const Axios = axios.create({ const Axios = axios.create({
// baseURL: "http://localhost:5000/", // baseURL: 'http://localhost:5000/',
// baseURL: 'https://leadesh-whatsapp.onrender.com', // baseURL: 'https://leadesh-whatsapp.onrender.com',
// baseURL: "https://api.leadesh.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 export default Axios

View File

@ -4,7 +4,7 @@ import { CFooter } from '@coreui/react'
const AppFooter = () => { const AppFooter = () => {
return ( return (
<CFooter className="px-4"> <CFooter className="px-4">
<div> {/* <div>
<a href="https://coreui.io" target="_blank" rel="noopener noreferrer"> <a href="https://coreui.io" target="_blank" rel="noopener noreferrer">
CoreUI CoreUI
</a> </a>
@ -15,7 +15,7 @@ const AppFooter = () => {
<a href="https://coreui.io/react" target="_blank" rel="noopener noreferrer"> <a href="https://coreui.io/react" target="_blank" rel="noopener noreferrer">
CoreUI React Admin &amp; Dashboard Template CoreUI React Admin &amp; Dashboard Template
</a> </a>
</div> </div> */}
</CFooter> </CFooter>
) )
} }

View File

@ -1,4 +1,4 @@
import React, { useState } from 'react' import React, { useEffect, useState } from 'react'
import { import {
CAvatar, CAvatar,
CBadge, CBadge,
@ -26,55 +26,50 @@ import avatar8 from './../../assets/images/avatars/8.jpg'
import { useNavigate } from 'react-router-dom' import { useNavigate } from 'react-router-dom'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faUserCircle } from '@fortawesome/free-solid-svg-icons' import { faUserCircle } from '@fortawesome/free-solid-svg-icons'
import Swal from 'sweetalert2'
import Axios from '../../axios'
import { isAutheticated } from '../../auth'
const AppHeaderDropdown = () => { const AppHeaderDropdown = () => {
const navigate = useNavigate() 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 ( return (
<CDropdown variant="nav-item"> <CDropdown variant="nav-item">
<CDropdownToggle placement="bottom-end" className="py-0 pe-0" caret={false}> <CDropdownToggle placement="bottom-end" className="py-0 pe-0" caret={false}>
<div className="media d-flex align-items-center"> <div className="media d-flex align-items-center">
{/* <Image src={"Profile3"} className="user-avatar md-avatar rounded-circle" /> */}
<FontAwesomeIcon <FontAwesomeIcon
style={{ fontSize: '2rem' }} style={{ fontSize: '2rem' }}
className="user-avatar md-avatar rounded-circle " className="user-avatar md-avatar rounded-circle "
icon={faUserCircle} icon={faUserCircle}
/> />
<div className="media-body ms-2 text-dark align-items-center d-none d-lg-block"> <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>
</div> </div>
</CDropdownToggle> </CDropdownToggle>
<CDropdownMenu className="pt-0" placement="bottom-end"> <CDropdownMenu className="pt-0" placement="bottom-end">
<CDropdownHeader className="bg-body-secondary fw-semibold mb-2">Account</CDropdownHeader> <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')}> <CDropdownItem onClick={() => navigate('/my-profile')}>
<CIcon icon={cilUser} className="me-2" /> <CIcon icon={cilUser} className="me-2" />
Profile Profile
@ -83,22 +78,9 @@ const AppHeaderDropdown = () => {
<CIcon icon={cilSettings} className="me-2" /> <CIcon icon={cilSettings} className="me-2" />
Change Password Change Password
</CDropdownItem> </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 /> <CDropdownDivider />
<CDropdownItem href="#"> <CDropdownItem onClick={signout}>
<CIcon icon={cilLockLocked} className="me-2" /> <CIcon icon={cilLockLocked} className="me-2" />
Logout Logout
</CDropdownItem> </CDropdownItem>

19
src/protectedRoute.js Normal file
View 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

View File

@ -3,12 +3,22 @@ import React from 'react'
const Dashboard = React.lazy(() => import('./views/dashboard/Dashboard')) const Dashboard = React.lazy(() => import('./views/dashboard/Dashboard'))
const Shop = React.lazy(() => import('./views/shops/Shop')) const Shop = React.lazy(() => import('./views/shops/Shop'))
const Order = React.lazy(() => import('./views/orders/Order')) 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 = [ const routes = [
{ path: '/', exact: true, name: 'Home' }, { path: '/', exact: true, name: 'Home' },
{ path: '/dashboard', name: 'Dashboard', element: Dashboard }, { path: '/dashboard', name: 'Dashboard', element: Dashboard },
{ path: '/shop', name: 'Shop', element: Shop }, { path: '/shop', name: 'Shop', element: Shop },
{ path: '/order', name: 'Order', element: Order }, { 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 export default routes

126
src/views/pages/Kyc/kyc.js Normal file
View 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

View 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

View 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

View File

@ -1,5 +1,5 @@
import React from 'react' import React, { useEffect, useState } from 'react'
import { Link } from 'react-router-dom' import { Link, useNavigate } from 'react-router-dom'
import { import {
CButton, CButton,
CCard, CCard,
@ -12,11 +12,110 @@ import {
CInputGroup, CInputGroup,
CInputGroupText, CInputGroupText,
CRow, CRow,
CSpinner,
} from '@coreui/react' } from '@coreui/react'
import CIcon from '@coreui/icons-react' import CIcon from '@coreui/icons-react'
import { cilLockLocked, cilUser } from '@coreui/icons' import { cilLockLocked, cilUser } from '@coreui/icons'
// import axios from 'axios'
import Axios from '../../../axios'
import Swal from 'sweetalert2'
const Login = () => { 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 ( return (
<div className="bg-body-tertiary min-vh-100 d-flex flex-row align-items-center"> <div className="bg-body-tertiary min-vh-100 d-flex flex-row align-items-center">
<CContainer> <CContainer>
@ -32,26 +131,53 @@ const Login = () => {
<CInputGroupText> <CInputGroupText>
<CIcon icon={cilUser} /> <CIcon icon={cilUser} />
</CInputGroupText> </CInputGroupText>
<CFormInput placeholder="Username" autoComplete="username" /> <CFormInput
type="email"
placeholder="Email"
onChange={handleChange}
value={auth.email}
name="email"
autoComplete="email"
/>
</CInputGroup> </CInputGroup>
{errors.emailError && (
<p className="text-center py-2 text-danger">{errors.emailError}</p>
)}
<CInputGroup className="mb-4"> <CInputGroup className="mb-4">
<CInputGroupText> <CInputGroupText>
<CIcon icon={cilLockLocked} /> <CIcon icon={cilLockLocked} />
</CInputGroupText> </CInputGroupText>
<CFormInput <CFormInput
type="password" type="password"
name="password"
value={auth.password}
onChange={handleChange}
placeholder="Password" placeholder="Password"
autoComplete="current-password" autoComplete="current-password"
/> />
</CInputGroup> </CInputGroup>
{errors.passwordError && (
<p className="text-center py-2 text-danger">{errors.passwordError}</p>
)}
<CRow> <CRow>
<CCol xs={6}> <CCol xs={6}>
<CButton color="primary" className="px-4"> <CButton
Login color="primary"
type="submit"
className="px-4"
onClick={handleSubmit}
disabled={loading}
>
{loading ? <CSpinner variant="grow" /> : 'Login'}
</CButton> </CButton>
</CCol> </CCol>
<CCol xs={6} className="text-right"> <CCol xs={6} className="text-right">
<CButton color="link" className="px-0"> <CButton
onClick={() => navigate('/forget-password')}
color="link"
className="px-0"
>
Forgot password? Forgot password?
</CButton> </CButton>
</CCol> </CCol>
@ -59,22 +185,6 @@ const Login = () => {
</CForm> </CForm>
</CCardBody> </CCardBody>
</CCard> </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> </CCardGroup>
</CCol> </CCol>
</CRow> </CRow>

View File

@ -2,79 +2,117 @@ import React, { useState } from 'react'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faAngleLeft, faEnvelope, faLockOpen, faUnlockAlt } from '@fortawesome/free-solid-svg-icons' import { faAngleLeft, faEnvelope, faLockOpen, faUnlockAlt } from '@fortawesome/free-solid-svg-icons'
import { Col, Row, Form, Card, Button, Container, InputGroup } from '@themesberg/react-bootstrap' 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 axios from 'axios'
// import { Routes } from '../../routes' // import { Routes } from '../../routes'
import Swal from 'sweetalert2' import Swal from 'sweetalert2'
import { isAutheticated } from '../../../auth'
import Axios from '../../../axios'
// import Axios from '../../axios' // import Axios from '../../axios'
const ChangePassword = () => { const ChangePassword = () => {
const [Loading, setLoading] = useState(false) const [loading, setLoading] = useState(false)
const navigate = useNavigate()
const [formData, setFormData] = useState({ const token = isAutheticated()
const [user, setUser] = useState({
oldPassword: '', oldPassword: '',
newPassword: '', newPassword: '',
confirmPassword: '',
}) })
const [errors, setErrors] = useState({
// console.log(formData); confirmPasswordError: '',
const handleInputChange = (e) => { 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 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() 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) setLoading(true)
// const jwt = localStorage.getItem('jwt')
// console.log(jwt);
// try { try {
// const response = await Axios.post( const res = await Axios.put(
// '/api/password', '/api/v1/user/password/update',
// { { ...user },
// oldPassword: formData.oldPassword, {
// newPassword: formData.newPassword, headers: {
// }, Authorization: `Bearer ${token}`,
// { },
// headers: { },
// jwt: `${jwt}`, )
// 'Content-Type': 'application/json', console.log(res)
// }, if (res?.data.success) {
// }, Swal.fire({
// ) title: 'Done',
// // console.log("response ", response); text: 'Password Changed',
// if (response.status === 200) { icon: 'success',
// // Display success alert confirmButtonText: 'ok',
// Swal.fire({ confirmButtonColor: '#303c54',
// icon: 'success', iconColor: '#303c54',
// title: 'Password Changed Successfully', }).then(() => {
// text: 'Your password has been changed successfully!', navigate('/dashboard')
// }).then((result) => { })
// if (result.isConfirmed) { }
// history.push(Routes.Presentation.path) } catch (error) {
// // history.push(Routes.Dashboard.path); Swal.fire('Error!', error.response?.data.message || 'Something went wrong', 'error')
// } } finally {
// }) setLoading(false)
}
// // 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)
} }
return ( return (
<main> <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"> <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> <h3 className="mb-4">Change Password</h3>
<Form onSubmit={formHandler}> <Form onSubmit={handleSubmit}>
<Form.Group id="password" className="mb-4"> <Form.Group id="password" className="mb-4">
<Form.Label>Old Password</Form.Label> <Form.Label>Old Password</Form.Label>
<InputGroup> <InputGroup>
@ -105,13 +143,16 @@ const ChangePassword = () => {
type="password" type="password"
placeholder="old password" placeholder="old password"
name="oldPassword" name="oldPassword"
value={formData.oldPassword} value={user.oldPassword}
onChange={handleInputChange} onChange={handleChange}
pattern="^(?=.*[a-z](){}[])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$" 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." title="The password should have 1 upper-case letter, 1 lower-case letter, 1 number, 1 special character and at least 8 characters."
/> />
</InputGroup> </InputGroup>
</Form.Group> </Form.Group>
{errors.oldPasswordError && (
<p className="text-center py-2 text-danger">{errors.oldPasswordError}</p>
)}
<Form.Group id="password" className="mb-4"> <Form.Group id="password" className="mb-4">
<Form.Label>New Password</Form.Label> <Form.Label>New Password</Form.Label>
<InputGroup> <InputGroup>
@ -123,16 +164,40 @@ const ChangePassword = () => {
type="password" type="password"
placeholder="new password" placeholder="new password"
name="newPassword" name="newPassword"
value={formData.newPassword} value={user.newPassword}
onChange={handleInputChange} onChange={handleChange}
pattern="^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$" 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." title="The password should have 1 upper-case letter, 1 lower-case letter, 1 number, 1 special character and at least 8 characters."
/> />
</InputGroup> </InputGroup>
</Form.Group> </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"> <Button variant="primary" type="submit" className="w-100">
{Loading ? <>Loading...</> : <> Change Password </>} {loading ? <>Loading...</> : <> Change Password </>}
</Button> </Button>
</Form> </Form>
</div> </div>

View File

@ -10,6 +10,9 @@ import {
} from '@fortawesome/free-solid-svg-icons' } from '@fortawesome/free-solid-svg-icons'
import { Col, Row, Form, Card, Button, Container, InputGroup } from '@themesberg/react-bootstrap' import { Col, Row, Form, Card, Button, Container, InputGroup } from '@themesberg/react-bootstrap'
import { Link, useNavigate } from 'react-router-dom' 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 Axios from '../../axios' // import Axios from '../../axios'
// import { Routes } from '../../routes' // import { Routes } from '../../routes'
@ -17,98 +20,91 @@ import { Link, useNavigate } from 'react-router-dom'
// import Swal from 'sweetalert2' // import Swal from 'sweetalert2'
const MyProfile = () => { const MyProfile = () => {
const navigate = useNavigate() const [image, setImage] = useState('')
const [Loading, setLoading] = useState(false) const [loading, setLoading] = useState(false)
const [imagesPreview, setImagesPreview] = useState()
const token = isAutheticated()
const [formData, setFormData] = useState({ const [ownerDetails, setOwnerDetails] = useState({
mobile: '',
name: '', name: '',
email: '', 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); if (res.data.user.avatar) {
const handleInputChange = (e) => { setImagesPreview(res.data.user.avatar.url)
const { name, value } = e.target }
setFormData((prevData) => ({ ...prevData, [name]: value })) }
} }
const formHandler = async (e) => { const handleChange = (event) => {
const { name, value } = event.target
setOwnerDetails({ ...ownerDetails, [name]: value })
}
async function handleSubmit(e) {
e.preventDefault() e.preventDefault()
// const jwt = localStorage.getItem('jwt') if (ownerDetails.name === '' || ownerDetails.email === '' || ownerDetails.phone === '') {
// console.log(jwt); 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 { setLoading(true)
// const response = await Axios.post( try {
// '/api/user', const res = await Axios.put(`/api/v1/user/update/profile`, formData, {
// { headers: {
// username: formData.name, 'Access-Control-Allow-Origin': '*',
// newNumber: formData.mobile, Authorization: `Bearer ${token}`,
// email: formData.email, 'Content-Type': 'multipart/formdata',
// }, },
// { })
// headers: { if (res.data.success === true) {
// jwt: `${jwt}`, setLoading(false)
// 'Content-Type': 'application/json', Swal.fire({
// }, title: 'Edited',
// }, text: 'Profile Edited Successfully!',
// ) icon: 'success',
// // console.log("response ", response); button: 'Return',
// if (response.status === 200) { })
// // Display success alert }
// Swal.fire({ } catch (error) {
// icon: 'success', const message = error?.response?.data?.message || 'Something went wrong!'
// title: 'Profile Updated Successfully', setLoading(false)
// text: 'Your Profile has been Updated successfully!', Swal.fire({
// }).then((result) => { title: 'Warning',
// if (result.isConfirmed) { text: message,
// history.push(Routes.Presentation.path) icon: 'error',
// // history.push(Routes.Dashboard.path); button: 'Retry',
// } dangerMode: true,
// }) })
}
// // 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.',
// })
// }
} }
// const getMe = async () => { console.log(ownerDetails)
// const jwt = localStorage.getItem('jwt') 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 ( return (
<main> <main>
<section className="bg-soft d-flex align-items-center my-5 mt-lg-6 mb-lg-5"> <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"> <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"> <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> <h3 className="mb-4">Profile</h3>
<Form onSubmit={formHandler}> <Form onSubmit={handleSubmit}>
<Form.Group id="name" className="mb-4"> <Form.Group id="name" className="mb-4">
<Form.Label>Name</Form.Label> <Form.Label>Name</Form.Label>
<InputGroup> <InputGroup>
@ -134,8 +130,8 @@ const MyProfile = () => {
type="text" type="text"
placeholder="Your Name" placeholder="Your Name"
name="name" name="name"
value={formData.name} value={ownerDetails.name}
onChange={handleInputChange} onChange={handleChange}
/> />
</InputGroup> </InputGroup>
</Form.Group> </Form.Group>
@ -152,9 +148,9 @@ const MyProfile = () => {
type="email" type="email"
placeholder="example@gmail.com" placeholder="example@gmail.com"
name="email" name="email"
value={formData.email} value={ownerDetails.email}
onChange={handleInputChange} onChange={handleChange}
pattern="[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}" // pattern="[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}"
title="Please enter a valid email address" title="Please enter a valid email address"
/> />
</InputGroup> </InputGroup>
@ -171,8 +167,8 @@ const MyProfile = () => {
type="tel" type="tel"
placeholder="Your Mobile Number" placeholder="Your Mobile Number"
name="mobile" name="mobile"
value={formData.mobile} value={ownerDetails.phone}
onChange={handleInputChange} onChange={handleChange}
// pattern="[0-9]{10}" // Assuming a 10-digit mobile number, adjust as needed // pattern="[0-9]{10}" // Assuming a 10-digit mobile number, adjust as needed
title="Please enter a valid 10-digit mobile number" title="Please enter a valid 10-digit mobile number"
/> />
@ -180,7 +176,7 @@ const MyProfile = () => {
</Form.Group> </Form.Group>
<Button variant="primary" type="submit" className="w-100"> <Button variant="primary" type="submit" className="w-100">
{Loading ? <>Loading...</> : <>Update</>} {loading ? <>Loading...</> : <>Update</>}
</Button> </Button>
</Form> </Form>
</div> </div>

View File

@ -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 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 ( 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>
</> </>
) )
} }

View 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