diff --git a/package.json b/package.json index 8024cb9..17d997b 100644 --- a/package.json +++ b/package.json @@ -40,6 +40,7 @@ "classnames": "^2.5.1", "core-js": "^3.37.1", "date-fns": "^3.6.0", + "jwt-decode": "^4.0.0", "prop-types": "^15.8.1", "react": "^18.3.1", "react-dom": "^18.3.1", diff --git a/src/protectedRoute.js b/src/protectedRoute.js index 1382107..87cf6dc 100644 --- a/src/protectedRoute.js +++ b/src/protectedRoute.js @@ -2,15 +2,38 @@ /* eslint-disable react/prop-types */ import { useEffect } from 'react' import { useNavigate } from 'react-router-dom' +import { jwtDecode } from 'jwt-decode' + +const isTokenExpired = (token) => { + try { + const decodedToken = jwtDecode(token) + console.log('Decoded Token:', decodedToken) // Debugging + const currentTime = Date.now() / 1000 + console.log('Current Time:', currentTime) // Debugging + console.log('Token Expiration Time:', decodedToken.exp) // Debugging + return decodedToken.exp < currentTime + } catch (error) { + console.error('Error decoding token:', error) // Debugging + return true // If there's an error decoding the token, consider it expired + } +} const ProtectedRoute = ({ element: Element }) => { const navigate = useNavigate() - console.log('req came here ') useEffect(() => { - if (!localStorage.getItem('authToken')) { - navigate('/login') + const checkToken = () => { + const token = localStorage.getItem('authToken') + console.log('Token:', token) // Debugging + if (!token || isTokenExpired(token)) { + console.log('Token is expired or not present, redirecting to login') + navigate('/login') + } else { + console.log('Token is valid') + } } + + checkToken() }, [navigate]) return diff --git a/src/routes.js b/src/routes.js index 4f2d010..6976e7f 100644 --- a/src/routes.js +++ b/src/routes.js @@ -14,7 +14,7 @@ const routes = [ { path: '/shop', name: 'Shop', element: Shop }, { path: '/order', name: 'Order', element: Order }, // KYC - { path: '/kyc', name: 'Kyc', element: Kyc }, + { path: '/kyc', name: 'KYC', element: Kyc }, { path: '/kyc/details/:id', name: 'Kyc details', element: KycDetails }, { path: '/my-profile', name: 'Profile', element: MyProfile }, diff --git a/src/views/pages/Kyc/MessageList.js b/src/views/pages/Kyc/MessageList.js new file mode 100644 index 0000000..26b4a7d --- /dev/null +++ b/src/views/pages/Kyc/MessageList.js @@ -0,0 +1,29 @@ +/* eslint-disable react/prop-types */ +import React from 'react' + +const MessageList = ({ messages }) => { + return ( + <> + {messages.map((msg, index) => ( +
+
{msg.user}
+
+ {msg.replyDate} +
+
{msg.message}
+
+ ))} + + ) +} + +export default MessageList diff --git a/src/views/pages/Kyc/kyc.js b/src/views/pages/Kyc/kyc.js index 0623936..eab8ecc 100644 --- a/src/views/pages/Kyc/kyc.js +++ b/src/views/pages/Kyc/kyc.js @@ -1,4 +1,4 @@ -import React, { useState } from 'react' +import React, { useState, useEffect } from 'react' import { Box, Table, @@ -10,34 +10,51 @@ import { Paper, TablePagination, Button, - IconButton, Tooltip, + Tabs, + Tab, + Modal, + TextField, + DialogActions, + Typography, } 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 -} +import Axios from '../../../axios' +import { isAutheticated } from '../../../auth' +import Swal from 'sweetalert2' const Kyc = () => { - const [rows, setRows] = useState(generateRandomData(50)) + const [rows, setRows] = useState([]) const [page, setPage] = useState(0) - const [rowsPerPage, setRowsPerPage] = useState(5) + const [rowsPerPage, setRowsPerPage] = useState(10) + const [tabIndex, setTabIndex] = useState(0) + const [loading, setLoading] = useState(false) + const [openModal, setOpenModal] = useState(false) + const [openModal2, setOpenModal2] = useState(false) + const [rejectionReason, setRejectionReason] = useState('') + const [selectedRowId, setSelectedRowId] = useState(null) + const [selectedRowId2, setSelectedRowId2] = useState(null) const navigate = useNavigate() + const token = isAutheticated() + + useEffect(() => { + const fetchData = async () => { + try { + const response = await Axios.get('/api/kyc/getAll/', { + headers: { + Authorization: `Bearer ${token}`, + 'Content-Type': 'application/json', + }, + }) + console.log(response) + setRows(response.data) + } catch (error) { + console.error('Error fetching data: ', error) + } + } + + fetchData() + }, []) const handleChangePage = (event, newPage) => { setPage(newPage) @@ -48,13 +65,124 @@ const Kyc = () => { setPage(0) } - // const handleViewClick = (id) => { - // navigate(`/kyc/details/${id}`) - // } + const handleViewClick = (id) => { + navigate(`/kyc/details/${id}`) + } + + const handleTabChange = (event, newValue) => { + setTabIndex(newValue) + } + + const filterRowsByStatus = (status) => { + return rows.filter((row) => row.status === status) + } + + const filteredRows = filterRowsByStatus( + tabIndex === 0 ? 'new' : tabIndex === 1 ? 'approved' : 'reject', + ) + + const handleRejectClick = async (id) => { + setSelectedRowId(id) + setOpenModal(true) + } + + const handleApproveClick = async (id) => { + setSelectedRowId2(id) + setOpenModal2(true) + } + + const handleApproveConfirm = async () => { + try { + setLoading(true) + console.log(selectedRowId2) + await Axios.patch( + `/api/kyc/update/${selectedRowId2}`, + { + status: 'approved', + }, + { + headers: { + Authorization: `Bearer ${token}`, + 'Content-Type': 'application/json', + }, + }, + ) + setRows((prevRows) => + prevRows.map((row) => (row._id === selectedRowId2 ? { ...row, status: 'approved' } : row)), + ) + setLoading(true) + Swal.fire('Success', 'Approved', 'success') + handleModalClose2() + } catch (error) { + setLoading(false) + console.error('Error approving KYC: ', error) + Swal.fire('Error!', error.message, 'error') + handleModalClose2() + } + } + + const handleModalClose = () => { + setOpenModal(false) + setRejectionReason('') + } + + const handleModalClose2 = () => { + setOpenModal2(false) + } + + const handleRejectionReasonChange = (event) => { + setRejectionReason(event.target.value) + } + + const handleRejectConfirm = async () => { + try { + setLoading(true) + await Axios.patch( + `/api/kyc/update/${selectedRowId}`, + { + status: 'reject', + rejectionReason, + }, + { + headers: { + Authorization: `Bearer ${token}`, + 'Content-Type': 'application/json', + }, + }, + ) + setRows((prevRows) => + prevRows.map((row) => (row._id === selectedRowId ? { ...row, status: 'reject' } : row)), + ) + setLoading(true) + Swal.fire('Success', 'Rejected', 'success') + handleModalClose() + } catch (error) { + setLoading(false) + console.error('Error rejecting KYC: ', error) + Swal.fire('Error!', error.message, 'error') + handleModalClose() + } + } + + const formatAMPM = (date) => { + var hours = new Date(date).getHours() + var minutes = new Date(date).getMinutes() + var ampm = hours >= 12 ? 'PM' : 'AM' + hours = hours % 12 + hours = hours ? hours : 12 // the hour '0' should be '12' + minutes = minutes < 10 ? '0' + minutes : minutes + var strTime = hours + ':' + minutes + ' ' + ampm + return strTime + } return ( + + + + + @@ -67,60 +195,143 @@ const Kyc = () => { - {rows.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage).map((row) => ( - - {row.id} - {row.tradeName} - {format(row.createdOn, 'yyyy-MM-dd HH:mm:ss')} - {row.status} - - - {/* - - */} - - - - {/* - - */} - - - - {/* - - */} - - - - - ))} + {filteredRows + .slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage) + .map((row) => ( + + {row._id} + {row.trade_name} + + {new Date(`${row?.createdAt}`).toDateString()} + , {`${formatAMPM(row?.createdAt)}`} + + {row.status} + + + + + {tabIndex === 0 && ( + <> + + + + + + + + )} + + + ))}
+ + + + + + + + + + + + + + + Are you sure you want to approve this KYC? + + + + + + +
) } +const modalStyle = { + position: 'absolute', + top: '50%', + left: '50%', + transform: 'translate(-50%, -50%)', + bgcolor: 'background.paper', + boxShadow: 24, + p: 4, +} + export default Kyc diff --git a/src/views/pages/Kyc/kycDetails.js b/src/views/pages/Kyc/kycDetails.js index 8da92e0..c28c04a 100644 --- a/src/views/pages/Kyc/kycDetails.js +++ b/src/views/pages/Kyc/kycDetails.js @@ -1,51 +1,180 @@ -import React from 'react' -import { Box, Typography, Grid, Paper, Avatar } from '@mui/material' -import { useParams } from 'react-router-dom' +import React, { useState, useEffect } from 'react' +import axios from 'axios' +import { + Box, + Typography, + Grid, + Paper, + Avatar, + Button, + TextField, + Modal, + DialogActions, +} from '@mui/material' +import { useNavigate, 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'], -}) +import Axios from '../../../axios' +import { isAutheticated } from '../../../auth' +import MessageList from './MessageList' +import Swal from 'sweetalert2' const KycDetails = () => { const { id } = useParams() - console.log(id) - const retailerDetails = generateRandomRetailerDetails(id) + const [openRejectModal, setOpenRejectModal] = useState(false) + const [openApproveModal, setOpenApproveModal] = useState(false) + const [rejectionReason, setRejectionReason] = useState('') + const [selectedRowId, setSelectedRowId] = useState(null) + const [retailerDetails, setRetailerDetails] = useState(null) + const token = isAutheticated() + + const navigate = useNavigate() + + useEffect(() => { + const fetchData = async () => { + try { + const response = await Axios.get(`/api/kyc/get-single-kyc/${id}`, { + headers: { + 'Access-Control-Allow-Origin': '*', + Authorization: `Bearer ${token}`, + 'Content-Type': 'multipart/formdata', + }, + }) + console.log(response.data) + setRetailerDetails(response.data) + } catch (error) { + console.error('Error fetching data: ', error) + } + } + + fetchData() + }, [id]) + + const [msg, setMsg] = useState('') + const handelSend = async () => { + try { + const resp = await Axios.patch( + `/api/kyc/update/${id}`, + { + rejectionReason: msg, + + user: 'Principal Distributer', // Replace with actual user type + }, + { + headers: { + Authorization: `Bearer ${token}`, + 'Content-Type': 'application/json', + }, + }, + ) + setRetailerDetails(resp.data.kyc) + setMsg('') + Swal.fire('Success', 'Message sent', 'success') + } catch (error) { + console.error('Error sending message: ', error) + Swal.fire('Error!', error.message, 'error') + } + } + + const handleModalClose = () => { + setOpenRejectModal(false) + setOpenApproveModal(false) + setRejectionReason('') + } + + const handleRejectionReasonChange = (event) => { + setRejectionReason(event.target.value) + } + + const handleRejectConfirm = async () => { + try { + const res = await Axios.patch( + `/api/kyc/update/${id}`, + { + status: 'reject', + rejectionReason, + + user: 'Principal Distributer', // Replace with actual user type + }, + { + headers: { + Authorization: `Bearer ${token}`, + 'Content-Type': 'application/json', + }, + }, + ) + setRetailerDetails(res.data.kyc) + Swal.fire('Success', 'Rejected', 'success') + handleModalClose() + } catch (error) { + console.error('Error rejecting KYC: ', error) + Swal.fire('Error!', error.message, 'error') + handleModalClose() + } + } + + const handleApproveConfirm = async () => { + try { + const res = await Axios.patch( + `/api/kyc/update/${id}`, + { + status: 'approved', + user: 'Principal Distributer', // Replace with actual user type + }, + { + headers: { + Authorization: `Bearer ${token}`, + 'Content-Type': 'application/json', + }, + }, + ) + setRetailerDetails(res.data.kyc) + Swal.fire('Success', 'Approved', 'success') + handleModalClose() + } catch (error) { + console.error('Error approving KYC: ', error) + Swal.fire('Error!', error.message, 'error') + handleModalClose() + } + } + + const formatAMPM = (date) => { + var hours = new Date(date).getHours() + var minutes = new Date(date).getMinutes() + var ampm = hours >= 12 ? 'PM' : 'AM' + hours = hours % 12 + hours = hours ? hours : 12 // the hour '0' should be '12' + minutes = minutes < 10 ? '0' + minutes : minutes + var strTime = hours + ':' + minutes + ' ' + ampm + return strTime + } + + const handleRejectClick = () => { + setOpenRejectModal(true) + } + + const handleApproveClick = () => { + setOpenApproveModal(true) + } + + if (!retailerDetails) { + return Loading... + } return ( - - Retailer Details - + + + Retailer Details + + + @@ -54,7 +183,7 @@ const KycDetails = () => { - Trade Name: {retailerDetails.tradeName} + Trade Name: {retailerDetails.trade_name} Name: {retailerDetails.name} @@ -63,7 +192,7 @@ const KycDetails = () => { Address: {retailerDetails.address} - Town/City: {retailerDetails.townCity} + Town/City: {retailerDetails.city} @@ -77,10 +206,11 @@ const KycDetails = () => { Pincode: {retailerDetails.pincode} - Mobile Number: {retailerDetails.mobileNumber} + Mobile Number: {retailerDetails.mobile_number} - Mapped Principal Distributor: {retailerDetails.mappedDistributor} + Mapped Principal Distributor:{' '} + {retailerDetails.principal_distributer.name} @@ -93,27 +223,27 @@ const KycDetails = () => { - PAN Number: {retailerDetails.documents.panNumber} + PAN Number: {retailerDetails.pan_number} - Aadhar Number: {retailerDetails.documents.aadharNumber} + Aadhar Number: {retailerDetails.aadhar_number} - GST Number: {retailerDetails.documents.gstNumber} + GST Number: {retailerDetails.gst_number} @@ -123,23 +253,27 @@ const KycDetails = () => { Fertilizer License (optional): - + {retailerDetails.fertilizer_license_img ? ( + + ) : ( + Img not available + )} Selfie of Entrance Board: @@ -148,43 +282,158 @@ const KycDetails = () => { - Block 3: Sales Coordinators/Territory Manager Details + Sales Coordinators/Territory Manager Details - Designation: {retailerDetails.salesCoordinator.designation} + Designation:{' '} + {retailerDetails.userType === 'SalesCoOrdinator' + ? 'Sales Coordinator' + : 'Territory Manager'} - Name: {retailerDetails.salesCoordinator.name} + Name: {retailerDetails.addedBy.name} - Employee ID: {retailerDetails.salesCoordinator.employeeId} + Employee ID: {retailerDetails.addedBy.uniqueId} Uploaded on:{' '} - {format(retailerDetails.salesCoordinator.uploadedOn, 'yyyy-MM-dd HH:mm:ss')} + {new Date(`${retailerDetails?.createdAt}`).toDateString()} + , {`${formatAMPM(retailerDetails?.createdAt)}`} Resubmitted on:{' '} - {format(retailerDetails.salesCoordinator.resubmittedOn, 'yyyy-MM-dd HH:mm:ss')} + {new Date(`${retailerDetails?.createdAt}`).toDateString()} + , {`${formatAMPM(retailerDetails?.createdAt)}`} - - - Block 4: Notes - - {retailerDetails.notes.map((note, index) => ( - {note} - ))} - + {retailerDetails.notes.length > 0 && retailerDetails.status === 'reject' && ( + + + Notes: + + + + + setMsg(e.target.value)} + variant="outlined" + label="send message" + /> + + + )} + {retailerDetails.status === 'new' && ( + <> + + + + + + + + + + + + + + + + Are you sure you want to approve this KYC? + + + + + + + + + + + + + + )} ) } +const modalStyle = { + position: 'absolute', + top: '50%', + left: '50%', + transform: 'translate(-50%, -50%)', + bgcolor: 'background.paper', + boxShadow: 24, + p: 4, +} + export default KycDetails diff --git a/src/views/pages/login/Login.js b/src/views/pages/login/Login.js index 6031786..f9d0ad3 100644 --- a/src/views/pages/login/Login.js +++ b/src/views/pages/login/Login.js @@ -95,27 +95,25 @@ const Login = () => { if (!(auth.email && auth.password)) { return Swal.fire('Error!', 'All fields are required', 'error') } - setLoading({ loading: true }) + setLoading(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') { + 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') { + Swal.fire('success', 'logged in successfully ', 'success') + } 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 (