Skip to content

Commit

Permalink
Key metrics redesign
Browse files Browse the repository at this point in the history
  • Loading branch information
kattylucy committed Sep 10, 2024
1 parent f758e87 commit a285056
Show file tree
Hide file tree
Showing 4 changed files with 69 additions and 84 deletions.
28 changes: 19 additions & 9 deletions centrifuge-app/src/components/PoolList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,6 @@ export function poolsToPoolCardProps(
cent: Centrifuge
): PoolCardProps[] {
return pools.map((pool) => {
const tinlakePool = pool.id?.startsWith('0x') && (pool as TinlakePool)
const metaData = typeof pool.metadata === 'string' ? metaDataById[pool.id] : pool.metadata

return {
Expand All @@ -144,21 +143,32 @@ export function poolsToPoolCardProps(
assetClass: metaData?.pool?.asset.subClass,
valueLocked: getPoolValueLocked(pool),
currencySymbol: pool.currency.symbol,
status:
tinlakePool && tinlakePool.tinlakeMetadata.isArchived
? 'Archived'
: tinlakePool && tinlakePool.addresses.CLERK !== undefined && tinlakePool.tinlakeMetadata.maker?.ilk
? 'Closed'
: pool.tranches.at(0)?.capacity?.toFloat() // pool is displayed as "open for investments" if the most junior tranche has a capacity
? 'Open for investments'
: ('Closed' as PoolStatusKey),
status: getPoolStatus(pool),
iconUri: metaData?.pool?.icon?.uri ? cent.metadata.parseMetadataUrl(metaData?.pool?.icon?.uri) : undefined,
tranches: pool.tranches as Tranche[],
metaData: metaData as MetaData,
}
})
}

export function getPoolStatus(pool: Pool | TinlakePool): PoolStatusKey {
const tinlakePool = pool.id?.startsWith('0x') && (pool as TinlakePool)

if (tinlakePool && tinlakePool.tinlakeMetadata.isArchived) {
return 'Archived'
}

if (tinlakePool && tinlakePool.addresses.CLERK !== undefined && tinlakePool.tinlakeMetadata.maker?.ilk) {
return 'Closed'
}

if (pool.tranches.at(0)?.capacity?.toFloat()) {
return 'Open for investments'
}

return 'Closed'
}

function getMetasById(pools: Pool[], poolMetas: PoolMetaDataPartial[]) {
const result: MetaDataById = {}

Expand Down
119 changes: 47 additions & 72 deletions centrifuge-app/src/components/PoolOverview/KeyMetrics.tsx
Original file line number Diff line number Diff line change
@@ -1,62 +1,44 @@
import { ActiveLoan, Loan, TinlakeLoan } from '@centrifuge/centrifuge-js'
import { Loan, Pool, TinlakeLoan } from '@centrifuge/centrifuge-js'
import { NetworkIcon } from '@centrifuge/centrifuge-react'
import { Box, Card, Grid, IconExternalLink, Shelf, Stack, Text, Tooltip } from '@centrifuge/fabric'
import { Box, Card, IconExternalLink, Shelf, Stack, Text, Tooltip } from '@centrifuge/fabric'
import capitalize from 'lodash/capitalize'
import startCase from 'lodash/startCase'
import { evmChains } from '../../config'
import { TinlakePool } from '../../utils/tinlake/useTinlakePools'
import { useActiveDomains } from '../../utils/useLiquidityPools'
import { usePool } from '../../utils/usePools'
import { PoolStatus } from '../PoolCard/PoolStatus'
import { getPoolStatus } from '../PoolList'
import { Spinner } from '../Spinner'

type Props = {
assetType?: { class: string; subClass: string }
averageMaturity: string
loans: TinlakeLoan[] | Loan[] | null | undefined
poolId: string
pool: Pool | TinlakePool
}

export const KeyMetrics = ({ assetType, averageMaturity, loans, poolId }: Props) => {
export const KeyMetrics = ({ assetType, averageMaturity, loans, poolId, pool }: Props) => {
const isTinlakePool = poolId.startsWith('0x')

function hasValuationMethod(pricing: any): pricing is { valuationMethod: string } {
return pricing && typeof pricing.valuationMethod === 'string'
}

const ongoingAssetCount =
loans &&
[...loans].filter(
(loan) =>
loan.status === 'Active' &&
hasValuationMethod(loan.pricing) &&
loan.pricing.valuationMethod !== 'cash' &&
!loan.outstandingDebt.isZero()
).length

const writtenOffAssetCount =
loans && [...loans].filter((loan) => loan.status === 'Active' && (loan as ActiveLoan).writeOffStatus).length

const overdueAssetCount =
loans &&
[...loans].filter((loan) => {
const today = new Date()
today.setUTCHours(0, 0, 0, 0)
return (
loan.status === 'Active' &&
loan.pricing.maturityDate &&
new Date(loan.pricing.maturityDate).getTime() < Date.now() &&
!loan.outstandingDebt.isZero()
)
}).length

const isBT3BT4 =
poolId.toLowerCase() === '0x90040f96ab8f291b6d43a8972806e977631affde' ||
poolId.toLowerCase() === '0x55d86d51ac3bcab7ab7d2124931fba106c8b60c7'

const metrics = [
{
metric: 'Asset class',
metric: 'Asset type',
value: `${capitalize(startCase(assetType?.class))} - ${assetType?.subClass}`,
},
{
metric: '30-day APY',
value: averageMaturity,
},
...(isBT3BT4
? []
: [
Expand All @@ -66,32 +48,13 @@ export const KeyMetrics = ({ assetType, averageMaturity, loans, poolId }: Props)
},
]),
{
metric: 'Total assets',
value:
loans?.filter((loan) => hasValuationMethod(loan.pricing) && loan.pricing.valuationMethod !== 'cash').length ||
0,
metric: 'Min. investment',
value: averageMaturity,
},
{
metric: 'Ongoing assets',
value: ongoingAssetCount,
metric: 'Investor type',
value: averageMaturity,
},
...(writtenOffAssetCount
? [
{
metric: 'Written off assets',
value: writtenOffAssetCount,
},
]
: []),
...(overdueAssetCount
? [
{
metric: 'Overdue assets',
value: overdueAssetCount,
},
]
: []),

...(!isTinlakePool
? [
{
Expand All @@ -100,35 +63,47 @@ export const KeyMetrics = ({ assetType, averageMaturity, loans, poolId }: Props)
},
]
: []),
{
metric: 'Pool structure',
value:
loans?.filter((loan) => hasValuationMethod(loan.pricing) && loan.pricing.valuationMethod !== 'cash').length ||
0,
},
{
metric: 'Rating',
value:
loans?.filter((loan) => hasValuationMethod(loan.pricing) && loan.pricing.valuationMethod !== 'cash').length ||
0,
},
{
metric: 'Expense ratio',
value:
loans?.filter((loan) => hasValuationMethod(loan.pricing) && loan.pricing.valuationMethod !== 'cash').length ||
0,
},
]

console.log(pool)

return (
<Card p={3}>
<Stack gap={2}>
<Text fontSize="18px" fontWeight="500">
Key metrics
</Text>
<Box borderStyle="solid" borderWidth="1px" borderColor="borderPrimary">
<Stack gap={1}>
<Box display="flex" justifyContent="space-between">
<Text variant="body2" fontWeight="500">
Overview
</Text>
<PoolStatus status={getPoolStatus(pool)} />
</Box>
<Box marginTop={2}>
{metrics.map(({ metric, value }, index) => (
<Grid
borderBottomStyle={index === metrics.length - 1 ? 'none' : 'solid'}
borderBottomWidth={index === metrics.length - 1 ? '0' : '1px'}
borderBottomColor={index === metrics.length - 1 ? 'none' : 'borderPrimary'}
height={32}
key={index}
px={1}
gridTemplateColumns="1fr 1fr"
width="100%"
alignItems="center"
gap={2}
>
<Text variant="body3" textOverflow="ellipsis" whiteSpace="nowrap">
<Box key={index} display="flex" justifyContent="space-between" mt="6px">
<Text color="textSecondary" variant="body2" textOverflow="ellipsis" whiteSpace="nowrap">
{metric}
</Text>
<Text variant="body3" textOverflow="ellipsis" whiteSpace="nowrap">
{value}
</Text>
</Grid>
</Box>
))}
</Box>
</Stack>
Expand Down
5 changes: 2 additions & 3 deletions centrifuge-app/src/pages/Pool/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { Box, Shelf, Text, TextWithPlaceholder } from '@centrifuge/fabric'
import * as React from 'react'
import { useLocation, useParams } from 'react-router'
import { useTheme } from 'styled-components'
import { Eththumbnail } from '../../components/EthThumbnail'
import { BASE_PADDING } from '../../components/LayoutBase/BasePadding'
import { NavigationTabs, NavigationTabsItem } from '../../components/NavigationTabs'
import { PageHeader } from '../../components/PageHeader'
Expand Down Expand Up @@ -32,7 +31,7 @@ export function PoolDetailHeader({ actions }: Props) {
title={<TextWithPlaceholder isLoading={isLoading}>{metadata?.pool?.name ?? 'Unnamed pool'}</TextWithPlaceholder>}
parent={{ to: `/pools${state?.token ? '/tokens' : ''}`, label: state?.token ? 'Tokens' : 'Pools' }}
icon={
<Eththumbnail show={isTinlakePool}>
<>
{metadata?.pool?.icon ? (
<Box as="img" width="iconLarge" height="iconLarge" src={iconUri} borderRadius={4} />
) : (
Expand All @@ -46,7 +45,7 @@ export function PoolDetailHeader({ actions }: Props) {
<Text variant="body1">{(isLoading ? '' : metadata?.pool?.name ?? 'U')[0]}</Text>
</Shelf>
)}
</Eththumbnail>
</>
}
border={false}
actions={actions}
Expand Down
1 change: 1 addition & 0 deletions centrifuge-app/src/pages/Pool/Overview/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ export function PoolDetailOverview() {
averageMaturity={averageMaturity}
loans={loans}
poolId={poolId}
pool={pool}
/>
</React.Suspense>
</Grid>
Expand Down

0 comments on commit a285056

Please sign in to comment.