Skip to content

Commit

Permalink
Add graph for balance sheet report
Browse files Browse the repository at this point in the history
  • Loading branch information
kattylucy committed Sep 27, 2024
1 parent acff0d0 commit f33a900
Show file tree
Hide file tree
Showing 5 changed files with 191 additions and 85 deletions.
4 changes: 2 additions & 2 deletions centrifuge-app/src/components/Charts/PoolPerformanceChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,6 @@ function PoolPerformanceChart() {

return result
}

return (
<Stack gap={2} padding={20}>
<Stack flexDirection="row" justifyContent="space-between" alignItems="center" mb={12}>
Expand Down Expand Up @@ -479,8 +478,9 @@ function CustomLegend({
)
}

const CustomTick = ({ x, y, payload }: any) => {
export const CustomTick = ({ x, y, payload }: any) => {
const theme = useTheme()

return (
<g transform={`translate(${x},${y})`}>
<text
Expand Down
79 changes: 79 additions & 0 deletions centrifuge-app/src/components/Charts/SimpleBarChart.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import { CurrencyMetadata } from '@centrifuge/centrifuge-js'
import { Text } from '@centrifuge/fabric'
import { Bar, BarChart, CartesianGrid, ResponsiveContainer, Tooltip, XAxis, YAxis } from 'recharts'
import { useTheme } from 'styled-components'
import { formatDate } from '../../../src/utils/date'
import { formatBalance, formatBalanceAbbreviated } from '../../../src/utils/formatting'
import { LoadBoundary } from '../LoadBoundary'
import { CustomTick } from './PoolPerformanceChart'
import { TooltipContainer, TooltipTitle } from './Tooltip'

type SimpleBarChartProps = {
currency?: CurrencyMetadata
data: { name: string; yAxis: number }[]
}

export const SimpleBarChart = ({ currency, data }: SimpleBarChartProps) => {
const theme = useTheme()

const getOneDayPerMonth = () => {
const seenMonths = new Set<string>()
const result: string[] = []

data.forEach((item) => {
const date = new Date(item.name)
const month = date.getMonth() + 1
const year = date.getFullYear()
const monthYear = `${year}-${month}`

if (!seenMonths.has(monthYear)) {
seenMonths.add(monthYear)
result.push(item.name)
}
})

return result
}

if (!data.length) return
return (
<LoadBoundary>
<ResponsiveContainer width="100%" height={200}>
<BarChart width={500} height={300} data={data} barSize={16} barGap={16} barCategoryGap="20%">
<CartesianGrid stroke={theme.colors.borderPrimary} vertical={false} />
<XAxis
dy={4}
interval={0}
minTickGap={100000}
tickLine={false}
type="category"
dataKey="name"
ticks={getOneDayPerMonth()}
tick={<CustomTick />}
/>
<YAxis
tickFormatter={(tick: number) => formatBalanceAbbreviated(tick, '', 0)}
tick={{ fontSize: 10, color: theme.colors.textPrimary }}
tickLine={false}
/>
<Tooltip
cursor={false}
content={({ payload }) => {
if (payload && payload?.length > 0) {
return (
<TooltipContainer>
<TooltipTitle>{formatDate(payload[0].payload.name)}</TooltipTitle>
{payload.map((item) => (
<Text variant="body3">{formatBalance(item.value as number, currency)}</Text>
))}
</TooltipContainer>
)
}
}}
/>
<Bar dataKey="yAxis" fill={theme.colors.backgroundTertiary} strokeWidth={0} fillOpacity={1} />
</BarChart>
</ResponsiveContainer>
</LoadBoundary>
)
}
6 changes: 5 additions & 1 deletion centrifuge-app/src/components/Report/BalanceSheet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ type Row = TableDataRow & {
}

export function BalanceSheet({ pool }: { pool: Pool }) {
const { startDate, endDate, groupBy, setCsvData } = React.useContext(ReportContext)
const { startDate, endDate, groupBy, setCsvData, setReportData, reportData } = React.useContext(ReportContext)

const [adjustedStartDate, adjustedEndDate] = React.useMemo(() => {
const today = new Date()
Expand Down Expand Up @@ -239,6 +239,10 @@ export function BalanceSheet({ pool }: { pool: Pool }) {
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [assetValuationRecords, trancheRecords])

React.useEffect(() => {
setReportData(poolStates)
}, [assetValuationRecords, trancheRecords])

if (!poolStates) {
return <Spinner mt={2} />
}
Expand Down
6 changes: 6 additions & 0 deletions centrifuge-app/src/components/Report/ReportContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@ export type ReportContextType = {

loan: string
setLoan: (type: string) => void

reportData: any
setReportData: (type: any) => void
}

export type CsvDataProps = {
Expand Down Expand Up @@ -78,6 +81,7 @@ export function ReportContextProvider({ children }: { children: React.ReactNode
const [address, setAddress] = React.useState(searchParams.get('address') || '')
const [network, setNetwork] = React.useState<string | number>(searchParams.get('network') || 'all')
const [loan, setLoan] = React.useState(searchParams.get('loan') || '')
const [reportData, setReportData] = React.useState([])

React.useEffect(() => {
const startDate = searchParams.get('from')
Expand Down Expand Up @@ -170,6 +174,8 @@ export function ReportContextProvider({ children }: { children: React.ReactNode
setNetwork: (value: any) => updateParamValues('network', value),
loan,
setLoan: (value: string) => updateParamValues('asset', value),
reportData,
setReportData,
}}
>
{children}
Expand Down
181 changes: 99 additions & 82 deletions centrifuge-app/src/components/Report/ReportFilter.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Pool } from '@centrifuge/centrifuge-js'
import { CurrencyBalance, Pool } from '@centrifuge/centrifuge-js'
import {
AnchorButton,
Box,
Expand All @@ -15,6 +15,7 @@ import * as React from 'react'
import { useNavigate } from 'react-router'
import styled from 'styled-components'
import { useBasePath } from '../../utils/useBasePath'
import { SimpleBarChart } from '../Charts/SimpleBarChart'
import { GroupBy, ReportContext } from './ReportContext'

interface StyledButtonProps {
Expand All @@ -37,102 +38,118 @@ type ReportFilterProps = {
}

export function ReportFilter({ pool }: ReportFilterProps) {
const { csvData, setStartDate, startDate, endDate, setEndDate, groupBy, setGroupBy, report } =
const { csvData, setStartDate, startDate, endDate, setEndDate, groupBy, setGroupBy, report, reportData } =
React.useContext(ReportContext)
const navigate = useNavigate()
const basePath = useBasePath()

const transformDataChart = React.useMemo(() => {
if (report === 'balance-sheet') {
return reportData.map((data: { timestamp: string; netAssetValue: CurrencyBalance }) => ({
name: data.timestamp,
yAxis: new CurrencyBalance(data.netAssetValue, pool.currency.decimals).toDecimal().toNumber(),
}))
}
}, [report, reportData])

return (
<Shelf
alignItems="center"
flexWrap="wrap"
gap={2}
padding={2}
marginX={[1, 6]}
marginY={2}
borderWidth={[0, 1]}
borderRadius={6}
borderStyle="solid"
borderColor="borderPrimary"
justifyContent="space-between"
borderWidth={[0, 1]}
flexDirection="column"
>
<Box display="flex">
<StyledButton
selected={report === 'balance-sheet'}
variant={report === 'balance-sheet' ? 'secondary' : 'tertiary'}
icon={<IconBalanceSheet />}
onClick={() => navigate(`${basePath}/${pool.id}/reporting/balance-sheet`)}
>
Balance sheet
</StyledButton>
<StyledButton
selected={report === 'profit-and-loss'}
variant={report === 'profit-and-loss' ? 'secondary' : 'tertiary'}
icon={<IconProfitAndLoss />}
onClick={() => navigate(`${basePath}/${pool.id}/reporting/profit-and-loss`)}
>
Profit & loss
</StyledButton>
<StyledButton
selected={report === 'cash-flow-statement'}
variant={report === 'cash-flow-statement' ? 'secondary' : 'tertiary'}
icon={<IconCashflow />}
onClick={() => navigate(`${basePath}/${pool.id}/reporting/cash-flow-statement`)}
>
Cash flow
</StyledButton>
</Box>

<Box display="flex">
<Box marginRight={2}>
<Select
name="balanceSheetGroupBy"
onChange={(event) => {
setGroupBy(event.target.value as GroupBy)
}}
value={groupBy}
options={[
{ label: 'Day', value: 'day' },
{ label: 'Daily', value: 'daily' },
{ label: 'Monthly', value: 'month' },
{ label: 'Quarterly', value: 'quarter' },
{ label: 'Yearly', value: 'year' },
]}
hideBorder
/>
</Box>
<Box marginRight={2}>
{groupBy === 'day' && (
<DateInput row label="Day" value={startDate} onChange={(e) => setStartDate(e.target.value)} />
)}
<Shelf alignItems="center" flexWrap="wrap" justifyContent="space-between" width="100%">
<Box display="flex">
<StyledButton
selected={report === 'balance-sheet'}
variant={report === 'balance-sheet' ? 'secondary' : 'tertiary'}
icon={<IconBalanceSheet />}
onClick={() => navigate(`${basePath}/${pool.id}/reporting/balance-sheet`)}
>
Balance sheet
</StyledButton>
<StyledButton
selected={report === 'profit-and-loss'}
variant={report === 'profit-and-loss' ? 'secondary' : 'tertiary'}
icon={<IconProfitAndLoss />}
onClick={() => navigate(`${basePath}/${pool.id}/reporting/profit-and-loss`)}
>
Profit & loss
</StyledButton>
<StyledButton
selected={report === 'cash-flow-statement'}
variant={report === 'cash-flow-statement' ? 'secondary' : 'tertiary'}
icon={<IconCashflow />}
onClick={() => navigate(`${basePath}/${pool.id}/reporting/cash-flow-statement`)}
>
Cash flow
</StyledButton>
</Box>
{groupBy === 'month' || groupBy === 'daily' ? (
<>
<Box marginRight={2}>
<DateInput
row
label="From"
value={startDate}
max={endDate}
onChange={(e) => setStartDate(e.target.value)}
/>
</Box>
<DateInput row label="To" value={endDate} min={startDate} onChange={(e) => setEndDate(e.target.value)} />
</>
) : null}
</Box>

<Box>
<AnchorButton
disabled={!csvData}
download={csvData?.fileName}
href={csvData?.dataUrl}
icon={<IconDownload />}
small
variant="inverted"
>
CSV
</AnchorButton>
<Box display="flex">
<Box marginRight={2}>
<Select
name="balanceSheetGroupBy"
onChange={(event) => {
setGroupBy(event.target.value as GroupBy)
}}
value={groupBy}
options={[
{ label: 'Day', value: 'day' },
{ label: 'Daily', value: 'daily' },
{ label: 'Monthly', value: 'month' },
{ label: 'Quarterly', value: 'quarter' },
{ label: 'Yearly', value: 'year' },
]}
hideBorder
/>
</Box>
<Box marginRight={2}>
{groupBy === 'day' && (
<DateInput row label="Day" value={startDate} onChange={(e) => setStartDate(e.target.value)} />
)}
</Box>
{groupBy === 'month' || groupBy === 'daily' ? (
<>
<Box marginRight={2}>
<DateInput
row
label="From"
value={startDate}
max={endDate}
onChange={(e) => setStartDate(e.target.value)}
/>
</Box>
<Box marginRight={2}>
<DateInput
row
label="To"
value={endDate}
min={startDate}
onChange={(e) => setEndDate(e.target.value)}
/>
</Box>
</>
) : null}
<AnchorButton
disabled={!csvData}
download={csvData?.fileName}
href={csvData?.dataUrl}
icon={<IconDownload />}
small
variant="inverted"
>
CSV
</AnchorButton>
</Box>
</Shelf>
<Box mt={4} width="100%" height={200} marginLeft="-50px">
<SimpleBarChart data={transformDataChart} currency={pool.currency} />
</Box>
</Shelf>
)
Expand Down

0 comments on commit f33a900

Please sign in to comment.