Last active
July 29, 2024 21:40
-
-
Save Oluwasetemi/bc61bbe862536e92e19d5078a6ebaa63 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Box from '@mui/material/Box' | |
import TableCell from '@mui/material/TableCell' | |
import TableRow from '@mui/material/TableRow' | |
import Typography from '@mui/material/Typography' | |
import React, { useCallback, useEffect, useState } from 'react' | |
import { Helmet } from 'react-helmet-async' | |
import Table from 'components/table/Table' | |
import { FilterState } from 'interfaces/filter' | |
import FilterDropdown from 'components/FilterDropdown' | |
import { activityColumns, headerStyle, cases } from 'data/loan-account' | |
import CustomPagination from 'components/table/customPagination' | |
import { isSameDay, isAfter, format, parseISO } from 'date-fns' | |
import { | |
LoanPayment, | |
preppedData as preppedDataType, | |
} from 'interfaces/loansAccount' | |
import { formatNumber } from 'helper/utils' | |
import { TableExportTypes } from 'interfaces/Table' | |
import { abbreviateLoanType } from '../loans/loansHome/loanTableHelper' | |
import DownloadButton from './downloadButton' | |
import generatePDF from 'helper/pdfUtils' | |
import LoanStatement from './loanStatement' | |
import { useAppSelector } from 'redux/hooks' | |
type ActivityTableType = { | |
data: LoanPayment[] | |
currency: string | |
} | |
const ActivityTable = ({ data, currency }: ActivityTableType) => { | |
const preppedData: preppedDataType[] = data | |
.map((paymentData) => { | |
const { | |
id, | |
dueDate, | |
actualPaymentDate, | |
paymentType, | |
currency, | |
amount, | |
isCredit, | |
description, | |
isConverted, | |
conversionAmount, | |
conversionRate, | |
conversionCurrency, | |
conversionTime, | |
companyLoans, | |
} = paymentData | |
const loanType = companyLoans?.loanType | |
const loanName = companyLoans?.creditStructureApproved?.loanName | |
const modifiedPayment = { | |
id, | |
date: dueDate, | |
actualPaymentDate, | |
paymentType, | |
currency, | |
amount, | |
credit: isCredit ? amount : 0, | |
debit: isCredit ? 0 : amount, | |
isCredit, | |
description, | |
loanType: loanType, | |
loanName: loanName, | |
isConverted, | |
conversionAmount, | |
conversionRate, | |
conversionCurrency, | |
conversionTime, | |
} | |
return modifiedPayment | |
}) | |
// .filter((payment) => !payment.paymentType?.includes('Penalty')) | |
.sort((a, b) => { | |
const timeA = | |
a.isCredit && a.actualPaymentDate ? a.actualPaymentDate : a.date | |
const timeB = | |
b.isCredit && b.actualPaymentDate ? b.actualPaymentDate : b.date | |
return isSameDay(new Date(timeA), new Date(timeB)) | |
? 0 | |
: isAfter(new Date(timeA), new Date(timeB)) | |
? 1 | |
: -1 | |
}) | |
const exportCustomization = { | |
description: (value: string | number, data: any) => { | |
const { loanType, loanName, isCredit } = data | |
const debitDescription = abbreviateLoanType(loanType) + ': ' + loanName | |
return isCredit ? data[value] : debitDescription | |
}, | |
} | |
// const latestActivity = preppedData[preppedData.length - 1]?.date | |
// Define the desired date format | |
const dateFormat = 'dd MMM, yyyy' | |
const endDate = preppedData[preppedData.length - 1]?.date | |
const startDate = preppedData[0]?.date | |
// Parse the original date string into a Date object | |
const originalStartDate = parseISO(startDate) | |
const originalEndDate = parseISO(endDate) | |
const [allData, setAllData] = useState<preppedDataType[]>(preppedData) | |
const [currentFilterState, setCurrentFilterState] = useState<FilterState>({ | |
date: { | |
values: [], | |
}, | |
}) | |
const [exportFunction, setExportFunction] = useState(false) | |
const [exportType, setExportType] = useState<TableExportTypes>('pdf') | |
const [filteredData, setFilteredData] = | |
useState<preppedDataType[]>(preppedData) | |
const [balancedData, setBalancedData] = | |
useState<preppedDataType[]>(preppedData) | |
const [tableData, setTableData] = useState<preppedDataType[]>(preppedData) | |
const [totals, setTotals] = useState<{ | |
credit: number | |
debit: number | |
balance: number | |
}>() | |
const tableStyles = { | |
overflow: 'auto', | |
'&::-webkit-scrollbar-track': { | |
backgroundColor: '#f5f5f5', | |
}, | |
'&::-webkit-scrollbar-thumb': { | |
boxShadow: 'inset 0 0 2px rgba(0, 0, 0, 0.3)', | |
backgroundColor: '#F3F6FF', | |
borderRadius: '3px', | |
}, | |
} | |
const [page, setPage] = useState<number>(0) // Pass page to table pagination prop e.g pagination={{ page }} | |
const [pagination, setPagination] = useState({ | |
rowsPerPageValue: 5, | |
rowsPerPageOptions: [5, 10, 15], | |
}) | |
const company = useAppSelector((state) => state.company.company) | |
const companyName = company?.legalName as string | |
const companyAddress = company.headquartersCity as string | |
const handleGeneratePDF = async () => { | |
await generatePDF( | |
<LoanStatement | |
data={tableData || []} | |
credit={formatNumber().format(totals?.credit as number)} | |
debit={formatNumber().format(totals?.debit as number)} | |
outstandingBalance={formatNumber().format(totals?.balance as number)} | |
getData={apiDataResource} | |
currency={currency} | |
companyName={companyName} | |
companyAddress={companyAddress} | |
endDate={format(originalEndDate, dateFormat)} | |
startDate={format(originalStartDate, dateFormat)} | |
/>, | |
`Loan account statement - ${companyName}` | |
) | |
} | |
const handleGenerateCSV = () => { | |
console.log(tableData) | |
const csvData = tableData.map((data) => { | |
const { | |
date, | |
paymentType, | |
description, | |
currency, | |
amount, | |
credit, | |
debit, | |
loanType, | |
loanName, | |
} = data | |
return { | |
Date: format(new Date(date), 'dd MMM yyyy'), | |
'Payment Type': paymentType, | |
Description: description, | |
Currency: currency, | |
Amount: amount, | |
Credit: credit, | |
Debit: debit, | |
'Loan Type': loanType, | |
'Loan Name': loanName, | |
} | |
}) | |
const csvHeaders = [ | |
'Date', | |
'Payment Type', | |
'Description', | |
'Currency', | |
'Amount', | |
'Credit', | |
'Debit', | |
'Loan Type', | |
'Loan Name', | |
] | |
const csvDataFormatted = csvData.map((data) => { | |
return csvHeaders.map((header) => data[header]) | |
}) | |
const generateSummary = () => { | |
// const credit = data.reduce((acc, curr) => acc + curr.Credit, 0) | |
// const debit = data.reduce((acc, curr) => acc + curr.Debit, 0) | |
// const balance = credit - debit | |
return { | |
'Total Inflows': totals?.credit || 0, | |
'Total Outflows': totals?.debit || 0, | |
'Ending Outstanding Balance': totals?.balance || 0, | |
} | |
} | |
const turnSummaryIntoCSV = (summary: any) => { | |
return Object.entries(summary).map(([key, value]) => { | |
return `${key},,${value}` | |
}) | |
} | |
const summary = generateSummary() | |
const summaryCSV = turnSummaryIntoCSV(summary) | |
const csvDataString = csvDataFormatted | |
.map((data) => data.join(',')) | |
.join('\n') | |
// join the summary data with the csv data | |
const csvDataStringWithSummary = | |
` | |
,,,,,,,,,,, | |
,,${companyName}'s Payment Schedule,,,,,,,,,07/08/2024 | |
,,(Updated as of ${format(new Date(), 'dd MMM yyyy')}),,,,,,,,, | |
Summary,,,,,"If you have any questions, please reach out to:",,,,,,,,,, | |
,,,,,,[email protected],,,,,,, | |
` + | |
summaryCSV.join('\n') + | |
'\n'.repeat(3) + | |
csvHeaders.join(',') + | |
'\n' + | |
csvDataString | |
console.log(csvDataStringWithSummary) | |
const blob = new Blob([csvDataStringWithSummary], { | |
type: 'text/csv;charset=utf-8;', | |
}) | |
saveAs(blob, `Loan account statement - ${companyName}.csv`) | |
} | |
function saveAs(blob: Blob, fileName: string) { | |
const url = window.URL.createObjectURL(blob) | |
const a = document.createElement('a') | |
a.href = url | |
a.download = fileName | |
a.click() | |
} | |
// This should be passed as part of the pagination prop of the table. e.g pagination={{ onPageChange }} | |
const onPageChange = (_: unknown, page: number) => { | |
setPage(page) | |
} | |
// This should be passed as part of the pagination prop of the table e.g pagination={{ onRowsPerPageChange }} | |
const onRowsPerPageChange = (value: number | string) => { | |
setPagination((prevValue) => ({ | |
...prevValue, | |
rowsPerPageValue: typeof value === 'string' ? parseInt(value, 10) : value, | |
})) | |
setPage(0) | |
} | |
const exportFunctionality = (label: TableExportTypes) => { | |
setExportType(label) | |
setExportFunction(true) | |
setTimeout(() => { | |
setExportFunction(false) | |
}, 200) | |
} | |
const apiDataResource = useCallback( | |
async (rowsPerPage = pagination.rowsPerPageValue, page = 0) => { | |
const statements = balancedData ? [...balancedData] : [] | |
const startIndex = statements.length - page * rowsPerPage | |
const endIndex = statements.length - (page + 1) * rowsPerPage | |
const displayData = statements.filter( | |
(e: any, i: any) => i < startIndex && i >= endIndex | |
) | |
setTableData([...displayData] || []) | |
}, | |
[balancedData] | |
) | |
const filterTableData = () => { | |
const filterDates = currentFilterState?.date?.values[0]?.value as string | |
if (filterDates && filterDates !== 'all') { | |
const startDate = filterDates.split(', ')[1] | |
const endDate = filterDates.split(', ')[0] | |
const modifiedTableData = allData.filter((loanPayment) => { | |
const { isCredit, actualPaymentDate, date } = loanPayment | |
const dateToFilter = | |
isCredit && actualPaymentDate ? actualPaymentDate : date | |
return ( | |
isAfter(new Date(dateToFilter), new Date(startDate)) && | |
isAfter(new Date(endDate), new Date(dateToFilter)) | |
) | |
}) | |
setFilteredData(modifiedTableData) | |
} else { | |
setFilteredData(allData) | |
} | |
} | |
const balanceTableData = () => { | |
let credit = 0 | |
let debit = 0 | |
let balance = 0 | |
const modifiedTableData = filteredData.reduce( | |
(accumulator, currentValue) => { | |
balance = | |
balance + | |
(currentValue.isCredit | |
? currentValue.amount * -1 | |
: currentValue.amount) | |
if (currentValue.isCredit) { | |
credit += currentValue.amount | |
} else { | |
debit += currentValue.amount | |
} | |
currentValue['outstandingBalance'] = balance | |
return [...accumulator, currentValue] | |
}, | |
[] as preppedDataType[] | |
) | |
const total = { credit, debit, balance } | |
setBalancedData(modifiedTableData) | |
setTotals(total) | |
} | |
useEffect(() => { | |
filterTableData() | |
setPage(0) | |
setPagination({ | |
rowsPerPageValue: 5, | |
rowsPerPageOptions: [5, 10, 15], | |
}) | |
}, [currentFilterState]) | |
useEffect(() => { | |
balanceTableData() | |
}, [filteredData]) | |
useEffect(() => { | |
apiDataResource() | |
}, [apiDataResource]) | |
useEffect(() => { | |
setAllData(preppedData) | |
}, [data.length]) | |
return ( | |
<Box> | |
<Helmet title="Fluna Inc. | Your Loan Home" /> | |
<Box | |
sx={{ | |
display: 'flex', | |
alignItems: 'center', | |
justifyContent: 'space-between', | |
}} | |
> | |
<Box | |
sx={{ | |
display: 'flex', | |
alignItems: 'center', | |
margin: '1rem 0 2rem 0', | |
}} | |
> | |
<Typography | |
sx={{ | |
fontWeight: 600, | |
fontSize: '1.5rem', | |
lineHeight: '2rem', | |
color: '#171721', | |
}} | |
> | |
Activity to Date | |
</Typography> | |
</Box> | |
<Box | |
sx={{ | |
gap: 2, | |
display: 'flex', | |
alignItems: 'center', | |
}} | |
> | |
<FilterDropdown | |
filterValues={{ | |
name: 'date', | |
selectType: 'date', | |
options: [ | |
{ name: 'Today', value: 'today' }, | |
{ name: 'This Month', value: 'this_month' }, | |
{ name: 'Last Month', value: 'last_month' }, | |
{ name: 'Custom Date', value: 'custom_date' }, | |
], | |
}} | |
filterState={currentFilterState} | |
setFilterState={setCurrentFilterState} | |
/> | |
<DownloadButton | |
downloadPdf={handleGeneratePDF} | |
downloadCsv={handleGenerateCSV} | |
exportFunctionality={exportFunctionality} | |
/> | |
</Box> | |
</Box> | |
<Box | |
sx={{ | |
border: '1px solid #E9EAEF', | |
padding: '18px 0 0 0px', | |
borderRadius: '8px', | |
}} | |
> | |
<Table<any> | |
data={tableData || []} | |
totalRows={balancedData?.length} | |
columns={activityColumns} | |
exporType={exportType} | |
exportFunction={exportFunction} | |
footerStyle={{ background: '#F3F6FF' }} | |
pagination={{ | |
show: false, | |
page, | |
onRowsPerPageChange, | |
onPageChange, | |
customPagination: false, | |
rowsPerPageValue: pagination.rowsPerPageValue, | |
rowsPerPageOptions: pagination.rowsPerPageOptions, | |
}} | |
footerInfo={{ | |
data: { | |
date: 'Total Balance', | |
paymentType: '---', | |
description: '---', | |
credit: formatNumber().format(totals?.credit as number), | |
debit: formatNumber().format(totals?.debit as number), | |
outstandingBalance: formatNumber().format( | |
totals?.balance as number | |
), | |
}, | |
Footer: (props: any) => { | |
const data = props?.data | |
return ( | |
<TableRow sx={{ background: '#F3F6FF' }}> | |
<TableCell | |
sx={{ | |
fontWeight: 400, | |
fontSize: '0.875rem', | |
lineHeight: '1rem', | |
color: '#171721', | |
}} | |
> | |
{data?.date} | |
</TableCell> | |
<TableCell | |
sx={{ | |
fontWeight: 400, | |
fontSize: '0.875rem', | |
lineHeight: '1rem', | |
color: '#8083A3', | |
}} | |
> | |
{data?.paymentType} | |
</TableCell> | |
<TableCell | |
sx={{ | |
fontWeight: 400, | |
fontSize: '0.875rem', | |
lineHeight: '1rem', | |
color: '#8083A3', | |
}} | |
> | |
{data?.description} | |
</TableCell> | |
<TableCell> | |
<Box | |
component={'span'} | |
sx={{ | |
width: '100%', | |
display: 'flex', | |
justifyContent: 'flex-end', | |
color: data?.credit === '0' ? '#8083A3' : '#171721', | |
}} | |
> | |
{data?.credit === '0' | |
? '---' | |
: `${currency}${data?.credit}`} | |
</Box> | |
</TableCell> | |
<TableCell> | |
<Box | |
component={'span'} | |
sx={{ | |
width: '100%', | |
display: 'flex', | |
justifyContent: 'flex-end', | |
fontWeight: data?.debit === '0' ? 400 : 600, | |
fontSize: '0.875rem', | |
lineHeight: '1.25rem', | |
color: data?.debit === '0' ? '#8083A3' : '#D14343', | |
}} | |
> | |
{data?.debit === '0' | |
? '---' | |
: `${currency}${data?.debit}`} | |
</Box> | |
</TableCell> | |
<TableCell> | |
<Box | |
component={'span'} | |
sx={{ | |
width: '100%', | |
display: 'flex', | |
justifyContent: 'flex-end', | |
fontWeight: 600, | |
fontSize: '0.875rem', | |
lineHeight: '1.25rem', | |
color: | |
data?.outstandingBalance <= 0 ? '#2952CC' : '#171721', | |
}} | |
> | |
{currency} | |
{data?.outstandingBalance} | |
</Box> | |
</TableCell> | |
</TableRow> | |
) | |
}, | |
}} | |
height={tableData.length < 7 ? '100%' : '20rem'} | |
sorting={true} | |
loadingData={false} | |
RenderedModal={() => null} | |
getData={apiDataResource} | |
customizations={{ cases: cases(currency) }} | |
headerStyle={headerStyle} | |
tableStyles={tableStyles} | |
showTooltip={true} | |
exportCustomization={exportCustomization} | |
modifiedExportName={`Loan account statement - ${company.name}`} | |
/> | |
<CustomPagination | |
page={page} | |
count={balancedData?.length} | |
rowsPerPage={pagination.rowsPerPageValue} | |
onRowsPerPageChange={onRowsPerPageChange} | |
rowsPerPageOptions={pagination.rowsPerPageOptions} | |
onPageChange={onPageChange} | |
/> | |
</Box> | |
</Box> | |
) | |
} | |
export default ActivityTable |
Author
Oluwasetemi
commented
Jul 29, 2024
•
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment