diff --git a/src/RouteLinks.js b/src/RouteLinks.js index e2960ca..ffc0263 100644 --- a/src/RouteLinks.js +++ b/src/RouteLinks.js @@ -5,6 +5,7 @@ const RouteLinks = { transactionsPage: '/transactions', repaymentsPage: '/repayments', loanChargesPage: '/loan-charges', + eligibility: '/eligibility', offers: '/offers', transaction_details_page: '/transaction/details', errorPage: '*', diff --git a/src/SiteRoutes.jsx b/src/SiteRoutes.jsx index 120568b..d3a84a2 100644 --- a/src/SiteRoutes.jsx +++ b/src/SiteRoutes.jsx @@ -1,5 +1,5 @@ -import { lazy, Suspense } from 'react' -import { Routes, Route } from 'react-router-dom' +import {lazy, Suspense} from 'react' +import {Routes, Route} from 'react-router-dom' import RouteLinks from './RouteLinks' import UserExist from './authorization/UserExist' @@ -13,35 +13,38 @@ import RepaymentsPage from './pages/RepaymentsPage' // REPAYMENTS PAGE import LoanChargesPage from './pages/LoanChargesPage' // LOAN CHARGES PAGE import TransactionDetailsPage from './pages/TransactionDetailsPage' // TRANSACTION DETAILS PAGE import OffersPage from './pages/OffersPage' // LOAN OFFERS PAGE -import ErrorPage from './pages/ErrorPage' // ERROR PAGE +import ErrorPage from './pages/ErrorPage' +import EligibilityPage from "./pages/EligibilityPage"; // ERROR PAGE // const Home = lazy(() => import('./pages/Home')); export default function SiteRoutes() { - return ( - - } /> {`*/LOGIN PAGE*/`} + return ( + + }/> {`*/LOGIN PAGE*/`} - }> - } /> {`*/HOME PAGE*/`} - } /> {`*/LOANS PAGE*/`} - } /> {`*/Transactions PAGE*/`} - } /> {`*/REPAYMENTS PAGE*/`} - } /> {`*/LOAN CHARGES PAGE*/`} - } /> {`*/TRANSACTION PAGE*/`} - } /> {`*/LOAN OFFERS PAGE*/`} - + }> + }/> {`*/HOME PAGE*/`} + }/> {`*/LOANS PAGE*/`} + }/> {`*/Transactions PAGE*/`} + }/> {`*/REPAYMENTS PAGE*/`} + }/> {`*/LOAN CHARGES PAGE*/`} + }/> {`*/ELIGIBILITY PAGE*/`} + }/> {`*/TRANSACTION PAGE*/`} + }/> {`*/LOAN OFFERS PAGE*/`} + - {/* ERROR PAGE */} - }> - - - } - /> - - ) + {/* ERROR PAGE */} + }> + + + } + /> + + ) } diff --git a/src/components/eligibility/EligibilityCom.jsx b/src/components/eligibility/EligibilityCom.jsx new file mode 100644 index 0000000..f7b33ea --- /dev/null +++ b/src/components/eligibility/EligibilityCom.jsx @@ -0,0 +1,169 @@ +import {useEffect, useState} from 'react' + +import BreadcrumbCom from '../breadcrumb/BreadcrumbCom' +import TablePaginatedWrapper from '../tableWrapper/TablePaginatedWrapper' +import Icons from '../Icons' +import {getLoanCharges} from '../../services/siteServices' +import getDateFromDateString from '../../helpers/GetDateFromDateString'; +import formatNumber from '../../helpers/formatNumber' +import Avatar from '../../assets/repay.png' + + +export default function EligibilityCom() { + + const [page, setPage] = useState(1) + const [allLoanCharges, setAllLoanCharges] = useState({loading: true, error: '', data: {}}) + + const [willFilter, setWillFilter] = useState(false) + const [filter, setFilter] = useState({type: '', id: ''}) + const handleFilter = ({target: {name, value}}) => { + setFilter(prev => ({...prev, [name]: value})) + } + + const handleFilterByParams = () => { + if (filter.type && !filter.id) { + return + } else if (!filter.type) { + setPage(1) + setWillFilter(prev => !prev) + setFilter({type: '', id: ''}) + } else { + setPage(1) + setWillFilter(prev => !prev) + } + } + + const loanCharges = allLoanCharges?.data?.loan_charges // LOAN CHARGES LIST + const pagination = allLoanCharges?.data?.pagination + const isFetching = allLoanCharges?.loading + const isError = allLoanCharges?.error + + useEffect(() => { + setAllLoanCharges(prev => ({...prev, loading: true})) + const payload = filter?.type ? {[filter?.type]: filter.id} : {} + getLoanCharges({...payload, page}).then(res => { + if (res?.status != 200) { + setAllLoanCharges(prev => ({...prev, loading: false})) + return + } + setAllLoanCharges({loading: false, error: '', data: res?.data}) + }).catch(err => { + setAllLoanCharges({loading: false, error: 'error occurred', data: {}}) + console.log('ERR', err) + }) + }, [page, willFilter]) + + return ( +
+ + +
+ {isError ? +

{allLoanCharges?.error}

+ : + <> + {/* filter section */} +
+ +
+ +
+
+ +
+ +
+ {/* end of filter section */} + + + {({data}) => ( + <> + + + + + {/* */} + + + + + + + {(data && data.length > 0) ? data?.map((item, index) => ( + + + + + + + )) + : + + + + } + +
+ TransactionID/Fee Type + + Loan + + Amount + + Added + + Action +
+
+ Jese image +
+
{item?.transaction_id || ''}
+
{item?.code}
+
+
+
+
+ {/*
{formatNumber(item?.initial_loan_amount)}
*/} +
#{formatNumber(item?.amount)}
+
+
+
+
{getDateFromDateString(item?.created_at)}
+
+
+
+
+ +
+
+
+
+ No Record Found +
+
+ + )} +
+ + } +
+
+ ) +} \ No newline at end of file diff --git a/src/components/eligibility/_LoanChargesCom.jsx b/src/components/eligibility/_LoanChargesCom.jsx new file mode 100644 index 0000000..26a4ab3 --- /dev/null +++ b/src/components/eligibility/_LoanChargesCom.jsx @@ -0,0 +1,119 @@ +import React, {useState} from 'react' +import {useQuery} from "@tanstack/react-query"; + +import BreadcrumbCom from '../breadcrumb/BreadcrumbCom' +import TablePaginatedWrapper from '../tableWrapper/TablePaginatedWrapper' +import Icons from '../Icons' + +import Avatar from '../../assets/user_avatar.jpg' +import queryKeys from '../../services/queryKeys' +import {getLoanCharges} from '../../services/siteServices' +import getDateFromDateString from '../../helpers/GetDateFromDateString'; +import formatNumber from '../../helpers/formatNumber'; + +export default function LoanChargesCom() { + + const [page, setPage] = useState(1) + + const {data, isFetching, isError, error} = useQuery({ + queryKey: [...queryKeys.loan_charges, page], + queryFn: () => getLoanCharges({page}), + staleTime: 0, + // placeholderData: keepPreviousData, + }) + + const loanCharges = data?.data?.loan_charges // LOAN CHARGES LIST + const pagination = data?.data?.pagination + + return ( +
+ + +
+ {isFetching ? + <> +

Loading...

+ + : isError ? +

{error.message}

+ : + + {({data}) => ( + <> + + + + + {/* */} + + + + + + + {(data && data.length > 0) ? data?.map((item, index) => ( + + + + + + + )) + : + + + + } + +
+ Name + + Loan + + Amount + + Added + + Action +
+
+
+
{item?.transaction_id || ''}
+
{item?.code}
+
+
+
+
+ {/*
{formatNumber(item?.initial_loan_amount)}
*/} +
{formatNumber(item?.amount)}
+
+
+
+
{getDateFromDateString(item?.created_at)}
+
+
+
+
+ +
+
+
+
+ No Record Found +
+
+ + )} +
+ } +
+
+ ) +} \ No newline at end of file diff --git a/src/components/layouts/aside/DashboardAside.jsx b/src/components/layouts/aside/DashboardAside.jsx index 3756ae6..d04e631 100644 --- a/src/components/layouts/aside/DashboardAside.jsx +++ b/src/components/layouts/aside/DashboardAside.jsx @@ -4,9 +4,9 @@ import DummyLogo from "../../DummyLogo"; import MainBtn from "../../MainBtn"; import AsideLink from "./AsideLink"; import AsideLinkWithSubLinks from "./AsideLinkWithSubLinks"; -import { useSelector } from "react-redux"; -import { generalLayoutContext } from "../../../context/GeneralLayoutContext"; -import { TbLogout2 } from "react-icons/tb"; +import {useSelector} from "react-redux"; +import {generalLayoutContext} from "../../../context/GeneralLayoutContext"; +import {TbLogout2} from "react-icons/tb"; import UserAvatar from '../../../assets/user_avatar.jpg' import Icons from "../../Icons"; import localImgLoader from '../../../helpers/localImageLoader'; @@ -18,136 +18,147 @@ export default function DashboardAside() { const {setLogoutModal, activeMenu, handleActiveMenu} = generalLayoutContext() const {userDetails} = useSelector((state) => state.userDetails) // GETS LOGGED IN USER ROLE DETAILS - const {role}= userDetails + const {role} = userDetails - return ( -
-
- {/* */} - -
- {/*
*/} + return ( +
+
+ {/* */} + +
+ {/*
*/} -
- {asideNavLinks.map((link, index) => { - let active = link.status == 1 ? true : false - let hasSubLinks = (link.subLinks && link.subLinks.length > 0) ? true : false - if(active && !hasSubLinks){ - return ( -
- -
- ) - } - if(active && hasSubLinks){ - let subLinkList = [] - link.subLinks.forEach(item =>{ - if(item.to){ - subLinkList.push(item.to) - }else if(item.subLinks?.length > 0){ - item.subLinks.forEach(item => { +
+ {asideNavLinks.map((link, index) => { + let active = link.status == 1 ? true : false + let hasSubLinks = (link.subLinks && link.subLinks.length > 0) ? true : false + if (active && !hasSubLinks) { + return ( +
+ +
+ ) + } + if (active && hasSubLinks) { + let subLinkList = [] + link.subLinks.forEach(item => { + if (item.to) { subLinkList.push(item.to) - }) - } - }) - return ( -
- {link.title && -

{link.title}

- } - - <> - {link.subLinks.map((subItem, index)=>{ - let active = subItem.status == 1 ? true : false - let hasSubLinks = (subItem.subLinks && subItem.subLinks.length > 0) ? true : false - if(active && !hasSubLinks){ - return ( -
- -
- ) - }else if(active && hasSubLinks){ - let subLinkList = subItem.subLinks.filter(value => value.to).map(item => { // specific open - if(item.to){ - return item.to - } - }) - return( - - <> - {subItem.subLinks.map((item, index)=>{ - let active = item.status == 1 ? true : false - if(active){ - return ( -
- + } else if (item.subLinks?.length > 0) { + item.subLinks.forEach(item => { + subLinkList.push(item.to) + }) + } + }) + return ( +
+ {link.title && +

{link.title}

+ } + + <> + {link.subLinks.map((subItem, index) => { + let active = subItem.status == 1 ? true : false + let hasSubLinks = (subItem.subLinks && subItem.subLinks.length > 0) ? true : false + if (active && !hasSubLinks) { + return ( +
+
) - } - })} - -
- ) - }else{ - return null - } - })} - - -
- ) - } - })} -
- -
-
-
- user avatar -
-

Username

-

username@gmail.com

-
-
- -
-
-
-
-

Username

-

username@gmail.com

+ } else if (active && hasSubLinks) { + let subLinkList = subItem.subLinks.filter(value => value.to).map(item => { // specific open + if (item.to) { + return item.to + } + }) + return ( + + <> + {subItem.subLinks.map((item, index) => { + let active = item.status == 1 ? true : false + if (active) { + return ( +
+ +
+ ) + } + })} + +
+ ) + } else { + return null + } + })} + +
+ ) + } + })} +
+ +
+
+
+ user avatar +
+

Username

+

username@gmail.com

-
- setLogoutModal(true)} - > - - +
+ +
+
+
+
+

Username

+

username@gmail.com

+
+
+
+ setLogoutModal(true)} + > + + +
-
- ) + ) } const asideNavLinks = [ - {name:'Dashboard', status:1, icon: 'dashboard', to: RouteLinks.homePage}, - {name:'First Advance', title:'Loan', status:1, icon: 'arrow-right', subLinks: [ - {name: 'Transactions', status:1, icon: 'dot', to: RouteLinks.transactionsPage}, - {name: 'Loans', status:1, icon: 'dot', to: RouteLinks.loansPage}, - {name: 'Repayments', status:1, icon: 'dot', to: RouteLinks.repaymentsPage}, - {name: 'Loan Charges', status:1, icon: 'dot', to: RouteLinks.loanChargesPage}, - {name: 'Configurations', status:1, icon: 'arrow-right', subLinks: [ - {name: 'Loan Offers', status:1, icon: 'dot', to: RouteLinks.offers }, - ] - }, + {name: 'Dashboard', status: 1, icon: 'dashboard', to: RouteLinks.homePage}, + { + name: 'First Advance', title: 'Loan', status: 1, icon: 'arrow-right', subLinks: [ + {name: 'Transactions', status: 1, icon: 'dot', to: RouteLinks.transactionsPage}, + {name: 'Loans', status: 1, icon: 'dot', to: RouteLinks.loansPage}, + {name: 'Repayments', status: 1, icon: 'dot', to: RouteLinks.repaymentsPage}, + {name: 'Loan Charges', status: 1, icon: 'dot', to: RouteLinks.loanChargesPage}, + {name: 'Eligibility', status: 1, icon: 'dot', to: RouteLinks.eligibility}, + { + name: 'Configurations', status: 1, icon: 'arrow-right', subLinks: [ + {name: 'Loan Offers', status: 1, icon: 'dot', to: RouteLinks.offers}, + ] + }, ], }, // {name:'Product 2', title:'Product 2', status:1, icon: 'product', subLinks: [ diff --git a/src/context/GeneralLayoutContext.jsx b/src/context/GeneralLayoutContext.jsx index 31d05b8..5fdfb31 100644 --- a/src/context/GeneralLayoutContext.jsx +++ b/src/context/GeneralLayoutContext.jsx @@ -1,4 +1,4 @@ -import { createContext, useContext, useEffect, useState } from 'react' +import {createContext, useContext, useEffect, useState} from 'react' const GeneralContextProvider = createContext({}) @@ -10,7 +10,7 @@ export default function GeneralLayoutContext({children}) { const [booking, setBooking] = useState('') - const [alertBox, setAlertBox] = useState({status:false, msg:''}) // USE TO SHOW SUcCESS OR FAILED ALERT MESSAGE + const [alertBox, setAlertBox] = useState({status: false, msg: ''}) // USE TO SHOW SUcCESS OR FAILED ALERT MESSAGE const [logoutModal, setLogoutModal] = useState(false) // USE TO SHOW LOGOUT MODAL BOX @@ -21,93 +21,93 @@ export default function GeneralLayoutContext({children}) { const [showAsideDrawer, setShowAsideDrawer] = useState('') const handleActiveMenu = (name) => { - if(activeMenu == name){ + if (activeMenu == name) { setActiveMenu('') - }else{ + } else { setActiveMenu(name) } } const handleDrawer = (drawerToOpen) => { // FUNCTION TO DETERMINE WHICH ASIDE DRAWER TO SHOW - setDrawer((prev)=>{ - if(!prev){ - return drawerToOpen - }else if(drawerToOpen == prev){ - return '' - }else{ - return drawerToOpen - } - }) + setDrawer((prev) => { + if (!prev) { + return drawerToOpen + } else if (drawerToOpen == prev) { + return '' + } else { + return drawerToOpen + } + }) } const handleBooking = (bookingToOpen) => { // FUNCTION TO DETERMINE WHICH ASIDE DRAWER TO SHOW - setBooking((prev)=>{ - if(!prev){ - return bookingToOpen - }else if(bookingToOpen == prev){ - return '' - }else{ - return bookingToOpen - } - }) + setBooking((prev) => { + if (!prev) { + return bookingToOpen + } else if (bookingToOpen == prev) { + return '' + } else { + return bookingToOpen + } + }) } const handleAlertBox = (valObj) => { - setAlertBox(valObj) + setAlertBox(valObj) } const handleTheme = () => { - setTheme(theme === "dark" ? "light" : "dark"); - } + setTheme(theme === "dark" ? "light" : "dark"); + } useEffect(() => { if (window.matchMedia("(prefers-color-scheme: dark)").matches) { - setTheme("dark"); + setTheme("dark"); } else { - setTheme("light"); + setTheme("light"); } - }, []); - - useEffect(() => { - if (theme === "dark") { - document.documentElement.classList.add("dark"); - } else { - document.documentElement.classList.remove("dark"); - } - }, [theme]); + }, []); - useEffect(()=>{ - window.addEventListener('resize', ()=>{ - setShrinkAside(false) - setShowAsideDrawer('') - setActiveMenu('') - }) - return () => window.removeEventListener('resize', window.addEventListener('resize', ()=>{ - setShrinkAside(false) - setShowAsideDrawer('') - setActiveMenu('') - })) - },[]) + useEffect(() => { + if (theme === "dark") { + document.documentElement.classList.add("dark"); + } else { + document.documentElement.classList.remove("dark"); + } + }, [theme]); + + useEffect(() => { + window.addEventListener('resize', () => { + setShrinkAside(false) + setShowAsideDrawer('') + setActiveMenu('') + }) + return () => window.removeEventListener('resize', window.addEventListener('resize', () => { + setShrinkAside(false) + setShowAsideDrawer('') + setActiveMenu('') + })) + }, []) let value = { - theme, handleTheme, - activeMenu, handleActiveMenu, - drawer, handleDrawer, - booking, handleBooking, - alertBox, handleAlertBox, + theme, handleTheme, + activeMenu, handleActiveMenu, + drawer, handleDrawer, + booking, handleBooking, + alertBox, handleAlertBox, logoutModal, setLogoutModal, shrinkAside, setShrinkAside, showAsideDrawer, setShowAsideDrawer - } - - return ( - - {children} - - ) + } + + return ( + + {children} + + ) } export const generalLayoutContext = () => { - return useContext(GeneralContextProvider) + return useContext(GeneralContextProvider) } diff --git a/src/pages/EligibilityPage.jsx b/src/pages/EligibilityPage.jsx new file mode 100644 index 0000000..e4f44af --- /dev/null +++ b/src/pages/EligibilityPage.jsx @@ -0,0 +1,9 @@ +import React from 'react' +import EligibilityCom from '../components/eligibility/EligibilityCom'; + + +export default function EligibilityPage() { + return ( + + ) +}