From cf409cbac2d22d83ee5a761edbc97c0489e4dbfa Mon Sep 17 00:00:00 2001 From: victorAnumudu Date: Wed, 28 May 2025 13:27:33 +0100 Subject: [PATCH] added filter component --- .env | 5 +- .env.development | 5 +- .env.production | 5 +- src/authorization/UserExist.jsx | 41 ++++++ src/components/home/HomeCom.jsx | 12 +- .../loan_charges/LoanChargesCom.jsx | 87 +++++++++--- .../loan_charges/_LoanChargesCom.jsx | 113 +++++++++++++++ src/components/loanscom/LoansCom.jsx | 82 ++++++++--- src/components/loanscom/_LoansCom.jsx | 132 ++++++++++++++++++ src/components/repayments/RepaymentsCom.jsx | 83 ++++++++--- src/components/repayments/_RepaymentsCom.jsx | 109 +++++++++++++++ src/helpers/debounceFunction.js | 15 ++ 12 files changed, 621 insertions(+), 68 deletions(-) create mode 100644 src/components/loan_charges/_LoanChargesCom.jsx create mode 100644 src/components/loanscom/_LoansCom.jsx create mode 100644 src/components/repayments/_RepaymentsCom.jsx create mode 100644 src/helpers/debounceFunction.js diff --git a/.env b/.env index 0db3d6e..3a6cd48 100644 --- a/.env +++ b/.env @@ -14,4 +14,7 @@ VITE_EMAIL_ENDPOINT='fcmbloan@support.com' #BANK NAME VITE_BANK_NAME='First City Monument Bank' -VITE_BANK_NAME_SHORT='FCMB' \ No newline at end of file +VITE_BANK_NAME_SHORT='FCMB' + +# Inactivity timeout/logout AT 10MINS +REACT_APP_TIMEOUT=600000 \ No newline at end of file diff --git a/.env.development b/.env.development index 4c21013..0915dd9 100644 --- a/.env.development +++ b/.env.development @@ -14,4 +14,7 @@ VITE_EMAIL_ENDPOINT='fcmbloan@support.com' #BANK NAME VITE_BANK_NAME='First City Monument Bank' -VITE_BANK_NAME_SHORT='FCMB' \ No newline at end of file +VITE_BANK_NAME_SHORT='FCMB' + +# Inactivity timeout/logout AT 10MINS +REACT_APP_TIMEOUT=600000 \ No newline at end of file diff --git a/.env.production b/.env.production index 0db3d6e..3a6cd48 100644 --- a/.env.production +++ b/.env.production @@ -14,4 +14,7 @@ VITE_EMAIL_ENDPOINT='fcmbloan@support.com' #BANK NAME VITE_BANK_NAME='First City Monument Bank' -VITE_BANK_NAME_SHORT='FCMB' \ No newline at end of file +VITE_BANK_NAME_SHORT='FCMB' + +# Inactivity timeout/logout AT 10MINS +REACT_APP_TIMEOUT=600000 \ No newline at end of file diff --git a/src/authorization/UserExist.jsx b/src/authorization/UserExist.jsx index 4acb13d..1ec879d 100644 --- a/src/authorization/UserExist.jsx +++ b/src/authorization/UserExist.jsx @@ -6,15 +6,56 @@ import DashboardLayout from "../components/layouts/DashboardLayout" import PageLoader from "../components/PageLoader" import { updateUserDetails } from "../store/UserDetails" import RouteLinks from "../RouteLinks" +import debounceFunction from '../helpers/debounceFunction' + export default function UserExist() { const dispatch = useDispatch() const navigate = useNavigate() + const [lastActivityTime, setLastActivityTime] = useState(Date.now()); // HOLDS THE INITIAL TIME USER LOGS IN + const [pageIsLoading, setPageIsLoading] = useState(true) const {userDetails} = useSelector((state) => state.userDetails) + + // Function to log the user out + const logoutUser = () => { + localStorage.clear() + navigate(RouteLinks.login, {replace:true}) + window.location.reload() + }; + + // Function to reset the activity time + const resetTimer = () => { + debounceFunction(setLastActivityTime(Date.now()), 1000) + }; + + useEffect(()=>{ + const timer = setTimeout(()=>{ + if(Date.now() - Number(lastActivityTime) >= Number(process.env.REACT_APP_TIMEOUT)){ + logoutUser() + } + }, Number(process.env.REACT_APP_TIMEOUT)) + + // Listen for activity events + const events = ['mousemove', 'keydown', 'click', 'scroll', 'touchstart']; + + // Adding event listeners + events.forEach(event => { + window.addEventListener(event, resetTimer); + }); + + return () => { + clearTimeout(timer) + events.forEach(event => { + window.removeEventListener(event, resetTimer); + }) + } + },[lastActivityTime]) + + useEffect(()=>{ const loadUser = (token) =>{ const userExist = [{name:'dummy'}] diff --git a/src/components/home/HomeCom.jsx b/src/components/home/HomeCom.jsx index 91c198f..288f8f2 100644 --- a/src/components/home/HomeCom.jsx +++ b/src/components/home/HomeCom.jsx @@ -40,14 +40,20 @@ export default function HomeCom() {

Loans

{/*

{dashData?.loans?.currency_text}

*/} -

{dashData?.loans?.currency_text}

+

+ {/* {dashData?.loans?.currency_text} */} + +

{dashData?.loans?.text}

Payments

-

{dashData?.payments?.currency_text}

+

+ {/* {dashData?.payments?.currency_text} */} + +

{dashData?.payments?.text}

@@ -62,7 +68,7 @@ export default function HomeCom() {
-

$K

+

{Object.keys(item)[0]}

diff --git a/src/components/loan_charges/LoanChargesCom.jsx b/src/components/loan_charges/LoanChargesCom.jsx index 2ea06ae..b989dcc 100644 --- a/src/components/loan_charges/LoanChargesCom.jsx +++ b/src/components/loan_charges/LoanChargesCom.jsx @@ -1,42 +1,84 @@ -import React, { useState } from 'react' -import { useQuery } from "@tanstack/react-query"; +import { useEffect, useState } from 'react' 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'; +import formatNumber from '../../helpers/formatNumber' +import Avatar from '../../assets/user_avatar.jpg' -export default function LoanChargesCom() { + +export default function LoansChargesCom() { const [page, setPage] = useState(1) + const [allLoanCharges, setAllLoanCharges] = useState({loading:true, error:'', data:{}}) - const {data, isFetching, isError, error} = useQuery({ - queryKey: [...queryKeys.loan_charges, page], - queryFn: () => getLoanCharges({page}), - staleTime: 0, - // placeholderData: keepPreviousData, - }) + const [willFilter, setWillFilter] = useState(false) + const [filter, setFilter] = useState({type: '', id: ''}) + const handleFilter = ({target:{name, value}}) => { + setFilter(prev => ({...prev, [name]:value})) + } - const loanCharges = data?.data?.loan_charges // LOAN CHARGES LIST - const pagination = data?.data?.pagination + 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 (
- +
- {isFetching ? - <> -

Loading...

- - : isError ? -

{error.message}

+ { isError ? +

{allLoanCharges?.error}

: + <> + {/* filter section */} +
+ +
+ +
+
+ +
+ +
+ {/* end of filter section */} + {({ data }) => ( <> @@ -106,6 +148,7 @@ export default function LoanChargesCom() { )} + }
diff --git a/src/components/loan_charges/_LoanChargesCom.jsx b/src/components/loan_charges/_LoanChargesCom.jsx new file mode 100644 index 0000000..2ea06ae --- /dev/null +++ b/src/components/loan_charges/_LoanChargesCom.jsx @@ -0,0 +1,113 @@ +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 +
+
+ 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/loanscom/LoansCom.jsx b/src/components/loanscom/LoansCom.jsx index fd50ac8..1d0b061 100644 --- a/src/components/loanscom/LoansCom.jsx +++ b/src/components/loanscom/LoansCom.jsx @@ -1,44 +1,85 @@ -import React, { useState } from 'react' -import { useQuery } from "@tanstack/react-query"; +import { useEffect, useState } from 'react' import {Link} from 'react-router-dom' import BreadcrumbCom from '../breadcrumb/BreadcrumbCom' -// import TableWrapper from '../tableWrapper/TableWrapper' -import TablePaginatedWrapper from '../tableWrapper/TablePaginatedWrapper'; +import TablePaginatedWrapper from '../tableWrapper/TablePaginatedWrapper' import Icons from '../Icons' - -import Avatar from '../../assets/user_avatar.jpg' -import queryKeys from '../../services/queryKeys' import { getLoans } from '../../services/siteServices' import getDateFromDateString from '../../helpers/GetDateFromDateString'; -import formatNumber from '../../helpers/formatNumber' +import Avatar from '../../assets/user_avatar.jpg' import RouteLinks from '../../RouteLinks'; +import formatNumber from '../../helpers/formatNumber' export default function LoansCom() { const [page, setPage] = useState(1) + const [allLoans, setAllLoans] = useState({loading:true, error:'', data:{}}) - const {data:allLoans, isFetching, isError, error} = useQuery({ - queryKey: [...queryKeys.loans, page], - queryFn: () => getLoans({page}), - staleTime: 0, - }) + 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 loans = allLoans?.data?.loans // LOANS LIST const pagination = allLoans?.data?.pagination + const isFetching = allLoans?.loading + const isError = allLoans?.error + + useEffect(()=>{ + setAllLoans(prev => ({...prev, loading:true})) + const payload = filter?.type ? {[filter?.type]: filter.id} : {} + getLoans({...payload, page}).then(res => { + if(res?.status != 200){ + setAllLoans(prev => ({...prev, loading:false})) + return + } + setAllLoans({loading:false, error:'', data:res?.data}) + }).catch(err => { + setAllLoans({loading:false, error:'error occurred', data:{}}) + console.log('ERR', err) + }) + },[page, willFilter]) return (
- +
- {isFetching ? - <> -

Loading...

- - : isError ? -

{error.message}

+ { isError ? +

{allLoans?.error}

: + <> + {/* filter section */} +
+ +
+ +
+
+ +
+ +
+ {/* end of filter section */} + {({ data }) => ( <> @@ -125,6 +166,7 @@ export default function LoansCom() { )} + }
diff --git a/src/components/loanscom/_LoansCom.jsx b/src/components/loanscom/_LoansCom.jsx new file mode 100644 index 0000000..fd50ac8 --- /dev/null +++ b/src/components/loanscom/_LoansCom.jsx @@ -0,0 +1,132 @@ +import React, { useState } from 'react' +import { useQuery } from "@tanstack/react-query"; +import {Link} from 'react-router-dom' + +import BreadcrumbCom from '../breadcrumb/BreadcrumbCom' +// import TableWrapper from '../tableWrapper/TableWrapper' +import TablePaginatedWrapper from '../tableWrapper/TablePaginatedWrapper'; +import Icons from '../Icons' + +import Avatar from '../../assets/user_avatar.jpg' +import queryKeys from '../../services/queryKeys' +import { getLoans } from '../../services/siteServices' +import getDateFromDateString from '../../helpers/GetDateFromDateString'; +import formatNumber from '../../helpers/formatNumber' +import RouteLinks from '../../RouteLinks'; + +export default function LoansCom() { + + const [page, setPage] = useState(1) + + const {data:allLoans, isFetching, isError, error} = useQuery({ + queryKey: [...queryKeys.loans, page], + queryFn: () => getLoans({page}), + staleTime: 0, + }) + + const loans = allLoans?.data?.loans // LOANS LIST + const pagination = allLoans?.data?.pagination + + return ( +
+ + +
+ {isFetching ? + <> +

Loading...

+ + : isError ? +

{error.message}

+ : + + {({ data }) => ( + <> + + + + + + + + + + + + + {(data && data.length > 0) ? data?.map((item, index) => ( + + + + + + + + + )) + : + + + + } + +
+ Name + + Loan Amount + + Product/Tenor + + Repay/Install Amount + + Added + + Action +
+
+ Jese image +
+
{item?.account_id || ''}
+
{item?.id} : {item?.transaction_id}
+
+
+
+
+ {/*
{formatNumber(item?.initial_loan_amount)}
*/} +
{formatNumber(item?.initial_loan_amount)}
+
+
+
+
{formatNumber(item?.product_id)}
+
{item?.tenor} days
+
+
+
+
{formatNumber(item?.repayment_amount)}
+
{formatNumber(item?.installment_amount)}
+
+
+
+
{getDateFromDateString(item?.created_at)}
+
+
+
+
+ + + +
+
+
+
+ No Record Found +
+
+ + )} +
+ } +
+
+ ) +} \ No newline at end of file diff --git a/src/components/repayments/RepaymentsCom.jsx b/src/components/repayments/RepaymentsCom.jsx index 0c046bc..3fd1749 100644 --- a/src/components/repayments/RepaymentsCom.jsx +++ b/src/components/repayments/RepaymentsCom.jsx @@ -1,41 +1,83 @@ -import React, { useState } from 'react' -import { useQuery } from "@tanstack/react-query"; +import { useEffect, useState } from 'react' 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 { getRepayments } from '../../services/siteServices' import getDateFromDateString from '../../helpers/GetDateFromDateString'; +import Avatar from '../../assets/user_avatar.jpg' + export default function RepaymentsCom() { const [page, setPage] = useState(1) + const [allRepayments, setAllRepayments] = useState({loading:true, error:'', data:{}}) - const {data, isFetching, isError, error} = useQuery({ - queryKey: [...queryKeys.transactions, page], - queryFn: () => getRepayments({page}), - staleTime: 0, - // placeholderData: keepPreviousData, - }) + const [willFilter, setWillFilter] = useState(false) + const [filter, setFilter] = useState({type: '', id: ''}) + const handleFilter = ({target:{name, value}}) => { + setFilter(prev => ({...prev, [name]:value})) + } - const repayments = data?.data?.repayments // REPAYMENTS LIST - const pagination = data?.data?.pagination + 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 repayments = allRepayments?.data?.repayments // LOAN REPAYMENTS LIST + const pagination = allRepayments?.data?.pagination + const isFetching = allRepayments?.loading + const isError = allRepayments?.error + + useEffect(()=>{ + setAllRepayments(prev => ({...prev, loading:true})) + const payload = filter?.type ? {[filter?.type]: filter.id} : {} + getRepayments({...payload, page}).then(res => { + if(res?.status != 200){ + setAllRepayments(prev => ({...prev, loading:false})) + return + } + setAllRepayments({loading:false, error:'', data:res?.data}) + }).catch(err => { + setAllRepayments({loading:false, error:'error occurred', data:{}}) + console.log('ERR', err) + }) + },[page, willFilter]) return (
- +
- {isFetching ? - <> -

Loading...

- - : isError ? -

{error.message}

+ { isError ? +

{allRepayments?.error}

: + <> + {/* filter section */} +
+ +
+ +
+
+ +
+ +
+ {/* end of filter section */} + {({ data }) => ( <> @@ -102,6 +144,7 @@ export default function RepaymentsCom() { )} + }
diff --git a/src/components/repayments/_RepaymentsCom.jsx b/src/components/repayments/_RepaymentsCom.jsx new file mode 100644 index 0000000..0c046bc --- /dev/null +++ b/src/components/repayments/_RepaymentsCom.jsx @@ -0,0 +1,109 @@ +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 { getRepayments } from '../../services/siteServices' +import getDateFromDateString from '../../helpers/GetDateFromDateString'; + +export default function RepaymentsCom() { + + const [page, setPage] = useState(1) + + const {data, isFetching, isError, error} = useQuery({ + queryKey: [...queryKeys.transactions, page], + queryFn: () => getRepayments({page}), + staleTime: 0, + // placeholderData: keepPreviousData, + }) + + const repayments = data?.data?.repayments // REPAYMENTS LIST + const pagination = data?.data?.pagination + + return ( +
+ + +
+ {isFetching ? + <> +

Loading...

+ + : isError ? +

{error.message}

+ : + + {({ data }) => ( + <> + + + + + {/* */} + + + + + + {(data && data.length > 0) ? data?.map((item, index) => ( + + + {/* */} + + + + )) + : + + + + } + +
+ Name + + Loan + + Added + + Action +
+
+ Jese image +
+
{item?.customer_id || ''}
+
{item?.loan_id} : {item?.transaction_id}
+
+
+
+
+
{item?.loan}
+
{item?.description}
+
+
+
+
{getDateFromDateString(item?.created_at)}
+
+
+
+
+ +
+
+
+
+ No Record Found +
+
+ + )} +
+ } +
+
+ ) +} \ No newline at end of file diff --git a/src/helpers/debounceFunction.js b/src/helpers/debounceFunction.js new file mode 100644 index 0000000..fca5d5e --- /dev/null +++ b/src/helpers/debounceFunction.js @@ -0,0 +1,15 @@ +function debounceFunction(func, delay) { + let timer; + + return function(...args) { + // Clear the previous timer if the function is called before the delay + clearTimeout(timer); + + // Set a new timer to execute the function after the specified delay + timer = setTimeout(() => { + func(...args); + }, delay); + }; +} + + export default debounceFunction \ No newline at end of file -- 2.34.1