Merge branch 'transaction-details-page' of DigiFi/digifi-FirstOffice into master

This commit is contained in:
2025-04-29 08:30:59 +00:00
committed by Gogs
13 changed files with 531 additions and 18 deletions
+1
View File
@@ -5,6 +5,7 @@ const RouteLinks = {
transactionsPage: '/transactions',
repaymentsPage: '/repayments',
loanChargesPage: '/loan-charges',
transaction_details_page: '/transaction/details',
errorPage: '*',
}
+2
View File
@@ -11,6 +11,7 @@ import LoansPage from './pages/LoansPage' // SELECTED LOANS PAGE
import TransactionsPage from './pages/TransactionsPage' // TRANSACTIONS PAGE
import RepaymentsPage from './pages/RepaymentsPage' // REPAYMENTS PAGE
import LoanChargesPage from './pages/LoanChargesPage' // LOAN CHARGES PAGE
import TransactionDetailsPage from './pages/TransactionDetailsPage'
import ErrorPage from './pages/ErrorPage'
@@ -27,6 +28,7 @@ export default function SiteRoutes() {
<Route path={RouteLinks.transactionsPage} element={<TransactionsPage />} /> {`*/Transactions PAGE*/`}
<Route path={RouteLinks.repaymentsPage} element={<RepaymentsPage />} /> {`*/REPAYMENTS PAGE*/`}
<Route path={RouteLinks.loanChargesPage} element={<LoanChargesPage />} /> {`*/LOAN CHARGES PAGE*/`}
<Route path={RouteLinks.transaction_details_page} element={<TransactionDetailsPage />} /> {`*/TRANSACTION PAGE*/`}
</Route>
{/* ERROR PAGE */}
+3 -3
View File
@@ -4,8 +4,6 @@ import { useQuery } from "@tanstack/react-query";
import BreadcrumbCom from '../../components/breadcrumb/BreadcrumbCom'
import CustomCounter from '../../components/CustomCounter'
import Icons from '../../components/Icons'
import TableWrapper from '../../components/tableWrapper/TableWrapper'
import Avatar from '../../assets/user_avatar.jpg'
import formatNumber from '../../helpers/formatNumber'
import queryKeys from '../../services/queryKeys'
@@ -137,7 +135,9 @@ export default function HomeCom() {
<Icons name='edit' />
</div> */}
<div className='p-2 flex justify-center items-center text-slate-500 bg-white-body dark:text-white-body dark:bg-black-body rounded-md'>
<Icons name='eye' />
<Link to={RouteLinks.transaction_details_page} state={{transactionID: item?.transaction_id}}>
<Icons name='eye' />
</Link>
</div>
{/* <div className='p-2 flex justify-center items-center text-slate-500 bg-white-body dark:text-white-body dark:bg-black-body rounded-md'>
<Icons name='trash' />
+17 -13
View File
@@ -9,19 +9,20 @@ 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.transactions, page],
queryKey: [...queryKeys.loan_charges, page],
queryFn: () => getLoanCharges({page}),
staleTime: 0,
// placeholderData: keepPreviousData,
})
const loanCharges = data?.data?.transactions // LOAN CHARGES LIST
const loanCharges = data?.data?.loan_charges // LOAN CHARGES LIST
const pagination = data?.data?.pagination
return (
@@ -48,7 +49,10 @@ export default function LoanChargesCom() {
{/* <th scope="col" className="px-2">
Loan
</th> */}
<th scope="col" className="px-2">
<th scope="col" className="px-2 text-right">
Amount
</th>
<th scope="col" className="px-2 text-right">
Added
</th>
<th scope="col" className="px-2 text-right">
@@ -63,19 +67,19 @@ export default function LoanChargesCom() {
<div className='w-full min-w-48 flex items-center gap-2 whitespace-nowrap'>
<img className="w-10 h-10 rounded-md" src={Avatar} alt="Jese image" />
<div className="text-left">
<div className="text-base font-semibold">{item?.account_id || ''}</div>
<div className="font-normal text-gray-500">{item?.transaction_id}</div>
<div className="text-base font-semibold">{item?.transaction_id || ''}</div>
<div className="font-normal text-gray-500 line-clamp-1">{item?.description}</div>
</div>
</div>
</td>
{/* <td className="px-2">
<div className="text-left">
<div className="text-base font-semibold">{item?.loan}</div>
<div className="font-normal text-gray-500">{item?.description}</div>
</div>
</td> */}
<td className="px-2">
<div className="text-left">
<div className="text-right">
{/* <div className="text-base font-semibold">{formatNumber(item?.initial_loan_amount)}</div> */}
<div className="font-normal text-gray-500">{formatNumber(item?.amount)}</div>
</div>
</td>
<td className="px-2">
<div className="text-right">
<div className="font-normal text-gray-500">{getDateFromDateString(item?.created_at)}</div>
</div>
</td>
@@ -90,7 +94,7 @@ export default function LoanChargesCom() {
))
:
<tr className="py-2 border-t border-dashed border-slate-300">
<td className="px-3 py-2" colSpan={3}>
<td className="px-3 py-2" colSpan={4}>
<div className="flex justify-center items-center">
No Record Found
</div>
@@ -0,0 +1,109 @@
import React, { useState } from 'react'
import { useQuery } from "@tanstack/react-query";
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 LoanChargeDetails({transactionID}) {
const [page, setPage] = useState(1)
const {data, isFetching, isError, error} = useQuery({
queryKey: [...queryKeys.loan_charges, page],
queryFn: () => getLoanCharges({transaction_id: transactionID, page}),
staleTime: 0,
// placeholderData: keepPreviousData,
})
const loanCharges = data?.data?.loan_charges // LOAN CHARGES LIST
const pagination = data?.data?.pagination
return (
<div className='box bg-white dark:bg-black-box text-black-body dark:text-white-body'>
<p className='font-bold text-base'>Loan Charges</p>
{isFetching ?
<>
<p className='text-slate-800'>Loading...</p>
</>
: isError ?
<p className='text-red-500'>{error.message}</p>
:
<TablePaginatedWrapper data={loanCharges} isFetching={isFetching} setPage={setPage} itemsPerPage={pagination?.limit} pagination={pagination}>
{({ data }) => (
<>
<table className="py-2 w-full text-sm">
<thead className="py-2 text-sm text-slate-500 text-left">
<tr>
<th scope="col" className="px-2 py-2">
Name
</th>
{/* <th scope="col" className="px-2">
Loan
</th> */}
<th scope="col" className="px-2 text-right">
Amount
</th>
<th scope="col" className="px-2 text-right">
Added
</th>
<th scope="col" className="px-2 text-right">
Action
</th>
</tr>
</thead>
<tbody>
{(data && data.length > 0) ? data?.map((item, index) => (
<tr key={index} className="py-2 border-t border-dashed border-slate-300">
<td className="px-2 py-2">
<div className='w-full min-w-48 flex items-center gap-2 whitespace-nowrap'>
<img className="w-10 h-10 rounded-md" src={Avatar} alt="Jese image" />
<div className="text-left">
<div className="text-base font-semibold">{item?.transaction_id || ''}</div>
<div className="font-normal text-gray-500 line-clamp-1">{item?.description}</div>
</div>
</div>
</td>
<td className="px-2">
<div className="text-right">
{/* <div className="text-base font-semibold">{formatNumber(item?.initial_loan_amount)}</div> */}
<div className="font-normal text-gray-500">{formatNumber(item?.amount)}</div>
</div>
</td>
<td className="px-2">
<div className="text-right">
<div className="font-normal text-gray-500">{getDateFromDateString(item?.created_at)}</div>
</div>
</td>
<td className="px-2 text-right">
<div className='flex items-center justify-end gap-3 md:gap-4'>
<div className='p-2 flex justify-center items-center text-slate-500 bg-white-body dark:text-white-body dark:bg-black-body rounded-md'>
<Icons name='eye' />
</div>
</div>
</td>
</tr>
))
:
<tr className="py-2 border-t border-dashed border-slate-300">
<td className="px-3 py-2" colSpan={4}>
<div className="flex justify-center items-center">
No Record Found
</div>
</td>
</tr>
}
</tbody>
</table>
</>
)}
</TablePaginatedWrapper>
}
</div>
)
}
@@ -0,0 +1,119 @@
import React from 'react'
import { useQuery } from "@tanstack/react-query";
import TableWrapper from '../tableWrapper/TableWrapper'
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'
export default function LoanDetails({transactionID}) {
const {data:allLoans, isFetching, isError, error} = useQuery({
queryKey: queryKeys.loans,
queryFn: () => getLoans({transaction_id: transactionID})
})
const loans = allLoans?.data?.loans // LOANS LIST
const loansCount = allLoans?.data?.count // LOANS LIST COUNT
// console.log('LOANS', loans)
return (
<div className='box bg-white dark:bg-black-box text-black-body dark:text-white-body'>
<p className='font-bold text-base'>Loans</p>
{isFetching ?
<>
<p className='text-slate-800'>Loading...</p>
</>
: isError ?
<p className='text-red-500'>{error.message}</p>
:
<TableWrapper data={loans} itemsPerPage={15}>
{({ data }) => (
<>
<table className="table-auto py-2 w-full text-sm">
<thead className="py-2 text-sm text-slate-500 text-left">
<tr>
<th scope="col" className="px-2 py-2">
Name
</th>
<th scope="col" className="px-2 text-right">
Loan Amount
</th>
<th scope="col" className="px-2 text-right">
Repay Amount
</th>
<th scope="col" className="px-2 text-right">
Install Amount
</th>
<th scope="col" className="px-2 text-right">
Added
</th>
<th scope="col" className="px-2 text-right">
Action
</th>
</tr>
</thead>
<tbody>
{(data && data.length > 0) ? data?.map((item, index) => (
<tr key={index} className="py-2 border-t border-dashed border-slate-300">
<td className="px-2 py-2">
<div className='w-full min-w-48 flex items-center gap-2 whitespace-nowrap'>
<img className="w-10 h-10 rounded-md" src={Avatar} alt="Jese image" />
<div className="text-left">
<div className="text-base font-semibold">{item?.account_id || ''}</div>
<div className="font-normal text-gray-500">{item?.customer_id}</div>
</div>
</div>
</td>
<td className="px-2">
<div className="text-right">
{/* <div className="text-base font-semibold">{formatNumber(item?.initial_loan_amount)}</div> */}
<div className="font-normal text-gray-500">{formatNumber(item?.initial_loan_amount)}</div>
</div>
</td>
<td className="px-2">
<div className="text-right">
<div className="font-normal text-gray-500">{formatNumber(item?.repayment_amount)}</div>
</div>
</td>
<td className="px-2">
<div className="text-right">
<div className="font-normal text-gray-500">{formatNumber(item?.installment_amount)}</div>
</div>
</td>
<td className="px-2">
<div className="text-right">
<div className="font-normal text-gray-500">{getDateFromDateString(item?.created_at)}</div>
</div>
</td>
<td className="px-2 text-right">
<div className='flex items-center justify-end gap-3 md:gap-4'>
<div className='p-2 flex justify-center items-center text-slate-500 bg-white-body dark:text-white-body dark:bg-black-body rounded-md'>
<Icons name='eye' />
</div>
</div>
</td>
</tr>
))
:
<tr className="py-2 border-t border-dashed border-slate-300">
<td className="px-3 py-2" colSpan={4}>
<div className="flex justify-center items-center">
No Record Found
</div>
</td>
</tr>
}
</tbody>
</table>
</>
)}
</TableWrapper>
}
</div>
)
}
@@ -0,0 +1,118 @@
import React, { useState } from 'react'
import { useQuery } from "@tanstack/react-query";
import TablePaginatedWrapper from '../tableWrapper/TablePaginatedWrapper'
import Icons from '../Icons'
import Avatar from '../../assets/user_avatar.jpg'
import queryKeys from '../../services/queryKeys'
import { getRepaymentSchedule } from '../../services/siteServices'
import getDateFromDateString from '../../helpers/GetDateFromDateString';
import formatNumber from '../../helpers/formatNumber';
export default function RepaymentScheduleDetails({transactionID}) {
const [page, setPage] = useState(1)
const {data, isFetching, isError, error} = useQuery({
queryKey: [...queryKeys.repayment_schedule, page],
queryFn: () => getRepaymentSchedule({transaction_id: transactionID, page}),
staleTime: 0,
// placeholderData: keepPreviousData,
})
console.log('data', data)
const loanCharges = data?.data?.repayment_schedules // LOAN CHARGES LIST
const pagination = data?.data?.pagination
return (
<div className='box bg-white dark:bg-black-box text-black-body dark:text-white-body'>
<p className='font-bold text-base'>Repayment Schedule</p>
{isFetching ?
<>
<p className='text-slate-800'>Loading...</p>
</>
: isError ?
<p className='text-red-500'>{error.message}</p>
:
<TablePaginatedWrapper data={loanCharges} isFetching={isFetching} setPage={setPage} itemsPerPage={pagination?.limit} pagination={pagination}>
{({ data }) => (
<>
<table className="py-2 w-full text-sm">
<thead className="py-2 text-sm text-slate-500 text-left">
<tr>
<th scope="col" className="px-2 py-2">
Name
</th>
{/* <th scope="col" className="px-2">
Loan
</th> */}
<th scope="col" className="px-2 text-right">
Repay Amount
</th>
<th scope="col" className="px-2 text-right">
Install Amount
</th>
<th scope="col" className="px-2 text-right">
Due Date
</th>
<th scope="col" className="px-2 text-right">
Action
</th>
</tr>
</thead>
<tbody>
{(data && data.length > 0) ? data?.map((item, index) => (
<tr key={index} className="py-2 border-t border-dashed border-slate-300">
<td className="px-2 py-2">
<div className='w-full min-w-48 flex items-center gap-2 whitespace-nowrap'>
<img className="w-10 h-10 rounded-md" src={Avatar} alt="Jese image" />
<div className="text-left">
<div className="text-base font-semibold">{item?.transaction_id || ''}</div>
<div className="font-normal text-gray-500 line-clamp-1">{item?.description}</div>
</div>
</div>
</td>
<td className="px-2">
<div className="text-right">
<div className="font-normal text-gray-500">{formatNumber(item?.total_repayment_amount)}</div>
</div>
</td>
<td className="px-2">
<div className="text-right">
<div className="font-normal text-gray-500">{formatNumber(item?.installment_amount)}</div>
</div>
</td>
<td className="px-2">
<div className="text-right">
<div className="font-normal text-gray-500">{getDateFromDateString(item?.due_date)}</div>
</div>
</td>
<td className="px-2 text-right">
<div className='flex items-center justify-end gap-3 md:gap-4'>
<div className='p-2 flex justify-center items-center text-slate-500 bg-white-body dark:text-white-body dark:bg-black-body rounded-md'>
<Icons name='eye' />
</div>
</div>
</td>
</tr>
))
:
<tr className="py-2 border-t border-dashed border-slate-300">
<td className="px-3 py-2" colSpan={5}>
<div className="flex justify-center items-center">
No Record Found
</div>
</td>
</tr>
}
</tbody>
</table>
</>
)}
</TablePaginatedWrapper>
}
</div>
)
}
@@ -0,0 +1,112 @@
import React, { useState } from 'react'
import { useQuery } from "@tanstack/react-query";
import {Link} from 'react-router-dom'
import TablePaginatedWrapper from '../tableWrapper/TablePaginatedWrapper'
import Icons from '../Icons'
import queryKeys from '../../services/queryKeys'
import { getTransactions } from '../../services/siteServices'
import getDateFromDateString from '../../helpers/GetDateFromDateString';
import localImgLoader from '../../helpers/localImageLoader';
import RouteLinks from '../../RouteLinks';
export default function TransactionDetails({transactionID}) {
const [page, setPage] = useState(1)
const {data, isFetching, isError, error} = useQuery({
queryKey: [...queryKeys.transactions, page],
queryFn: () => getTransactions({transaction_id: transactionID, page}),
staleTime: 0,
// placeholderData: keepPreviousData,
})
const transactions = data?.data?.transactions // TRANSACTIONS LIST
const pagination = data?.data?.pagination
return (
<div className='box bg-white dark:bg-black-box text-black-body dark:text-white-body'>
<p className='font-bold text-base'>Transactions</p>
{isFetching ?
<>
<p className='text-slate-800'>Loading...</p>
</>
: isError ?
<p className='text-red-500'>{error.message}</p>
:
<TablePaginatedWrapper data={transactions} isFetching={isFetching} setPage={setPage} itemsPerPage={pagination?.limit} pagination={pagination}>
{({ data }) => (
<>
<table className="py-2 w-full text-sm">
<thead className="py-2 text-sm text-slate-500 text-left">
<tr>
<th scope="col" className="px-2 py-2">
Request
</th>
<th scope="col" className="px-2">
Account
</th>
<th scope="col" className="px-2">
Activity
</th>
<th scope="col" className="px-2 text-right">
Action
</th>
</tr>
</thead>
<tbody>
{(data && data.length > 0) ? data?.map((item, index) => (
<tr key={index} className="py-2 border-t border-dashed border-slate-300">
<td className="px-2 py-2">
<div className='w-full min-w-48 flex items-center gap-2 whitespace-nowrap'>
<img className="w-10 h-10 rounded-md" src={localImgLoader(`loan_icons/${item?.type}.png`)} alt="Icon" />
<div className="text-left">
<div className="text-base font-semibold">{item?.transaction_id}</div>
<div className="font-normal text-gray-500">{getDateFromDateString(item?.created_at)}</div>
</div>
</div>
</td>
<td className="px-2">
<div className="text-left">
<div className="text-base font-semibold">{item?.account_id}</div>
<div className="font-normal text-gray-500">{item?.type}</div>
</div>
</td>
<td className="px-2">
<div className="text-left">
<div className="font-normal text-gray-500">50%</div>
<div className="relative h-[6px] w-full bg-white-body dark:bg-black-body rounded-full overflow-hidden">
<div className={`absolute left-0 h-full w-1/2 bg-emerald-600`}></div>
</div>
</div>
</td>
<td className="px-2 text-right">
<div className='flex items-center justify-end gap-3 md:gap-4'>
<div className='p-2 flex justify-center items-center text-slate-500 bg-white-body dark:text-white-body dark:bg-black-body rounded-md'>
<Link to={RouteLinks.transaction_details_page} state={{transactionID: item?.transaction_id}}>
<Icons name='eye' />
</Link>
</div>
</div>
</td>
</tr>
))
:
<tr className="py-2 border-t border-dashed border-slate-300">
<td className="px-3 py-2" colSpan={4}>
<div className="flex justify-center items-center">
No Record Found
</div>
</td>
</tr>
}
</tbody>
</table>
</>
)}
</TablePaginatedWrapper>
}
</div>
)
}
@@ -0,0 +1,18 @@
import React from 'react'
import BreadcrumbCom from '../breadcrumb/BreadcrumbCom'
import TransactionDetails from './TransactionDetails'
import LoanDetails from './LoanDetails'
import LoanChargeDetails from './LoanChargeDetails'
import RepaymentScheduleDetails from './RepaymentScheduleDetails'
export default function TransactionDetailsCom({id}) {
return (
<div className='w-full flex flex-col gap-8'>
<BreadcrumbCom title='Transaction Details' paths={['Dashboard', 'Transactions Details']} />
<TransactionDetails transactionID={id} />
<LoanDetails transactionID={id} />
<RepaymentScheduleDetails transactionID={id} />
<LoanChargeDetails transactionID={id} />
</div>
)
}
@@ -1,5 +1,6 @@
import React, { useState } from 'react'
import { useQuery } from "@tanstack/react-query";
import {Link} from 'react-router-dom'
import BreadcrumbCom from '../breadcrumb/BreadcrumbCom'
import TablePaginatedWrapper from '../tableWrapper/TablePaginatedWrapper'
@@ -10,6 +11,7 @@ import queryKeys from '../../services/queryKeys'
import { getTransactions } from '../../services/siteServices'
import getDateFromDateString from '../../helpers/GetDateFromDateString';
import localImgLoader from '../../helpers/localImageLoader';
import RouteLinks from '../../RouteLinks';
export default function TransactionsCom() {
@@ -86,7 +88,9 @@ export default function TransactionsCom() {
<td className="px-2 text-right">
<div className='flex items-center justify-end gap-3 md:gap-4'>
<div className='p-2 flex justify-center items-center text-slate-500 bg-white-body dark:text-white-body dark:bg-black-body rounded-md'>
<Icons name='eye' />
<Link to={RouteLinks.transaction_details_page} state={{transactionID: item?.transaction_id}}>
<Icons name='eye' />
</Link>
</div>
</div>
</td>
+18
View File
@@ -0,0 +1,18 @@
import React, { useEffect } from 'react'
import {useLocation, useNavigate} from 'react-router-dom'
import TransactionDetailsCom from '../components/transactionDetails/TransactionDetailsCom'
export default function TransactionDetailsPage() {
const {state} = useLocation()
const navigate = useNavigate()
useEffect(()=>{
if(!state?.transactionID){
navigate('/', {replace: true})
}
},[])
return (
<TransactionDetailsCom id={state?.transactionID} />
)
}
+2
View File
@@ -2,6 +2,8 @@ const queryKeys = {
dashboard: ['dashboard'],
loans: ['loans'],
transactions: ['transactions'],
repayment_schedule: ['repayment-schedule'],
loan_charges: ['loan-charges'],
apply_loan: ['apply'],
select_loan: ['select-loan'],
approved_loan: ['approved-loan'],
+7 -1
View File
@@ -77,5 +77,11 @@ export const getRepayments = (reqData) => {
// FUNCTION TO GET LOAN CHARGES TABLE
export const getLoanCharges = (reqData) => {
const postData = { ...reqData }
return getAuxEnd(`/transactions`, postData)
return getAuxEnd(`/loan-charges`, postData)
}
// FUNCTION TO GET REPAYMENT SCHEDULE TABLE
export const getRepaymentSchedule = (reqData) => {
const postData = { ...reqData }
return getAuxEnd(`/repayment-schedules`, postData)
}