Skip to content

Commit

Permalink
Add new card design to pools (#2413)
Browse files Browse the repository at this point in the history
* Add new design to pools landing page

* Add new pool fields

* Fix logo

* Update theme

* Bug Fix

* Add feedback review
  • Loading branch information
kattylucy authored Sep 10, 2024
1 parent a199cab commit 15ac610
Show file tree
Hide file tree
Showing 19 changed files with 348 additions and 230 deletions.
2 changes: 1 addition & 1 deletion centrifuge-app/src/components/LayoutBase/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export function LayoutBase(): JSX.Element {
</WalletContainer>
<Inner>
<LogoContainer>
<LogoLink></LogoLink>
<LogoLink />
</LogoContainer>
<ToolbarContainer as="aside">
<Menu />
Expand Down
14 changes: 10 additions & 4 deletions centrifuge-app/src/components/LayoutBase/styles.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ export const Inner = styled(Grid)`
padding-bottom: 1rem;
@media (min-width: ${({ theme }) => theme.breakpoints['M']}) and (max-width: ${({ theme }) =>
theme.breakpoints['L']}) {
width: 7vw;
width: 6vw;
background-color: ${({ theme }) => theme.colors.backgroundInverted};
overflow: visible;
}
Expand Down Expand Up @@ -77,13 +77,19 @@ export const LogoContainer = styled(Stack)`
z-index: ${({ theme }) => theme.zIndices.header};
position: sticky;
top: 0;
width: 100%;
grid-area: logo;
height: ${HEADER_HEIGHT}px;
justify-content: center;
padding-left: 12px;
@media (min-width: ${({ theme }) => theme.breakpoints[BREAK_POINT_COLUMNS]}) {
@media (min-width: ${({ theme }) => theme.breakpoints['M']}) and (max-width: ${({ theme }) =>
theme.breakpoints['L']}) {
justify-content: start;
padding-top: ${({ theme }) => theme.space[2]}px;
padding-left: 12px;
}
@media (min-width: ${({ theme }) => theme.breakpoints['L']}) {
justify-content: start;
padding-top: ${({ theme }) => theme.space[2]}px;
}
Expand Down
4 changes: 4 additions & 0 deletions centrifuge-app/src/components/ListItemCardStyles.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,7 @@ export const Ellipsis = styled.span`
overflow: hidden;
text-overflow: ellipsis;
`
export const CardHeader = styled(Box)`
display: flex;
justify-content: space-between;
`
6 changes: 3 additions & 3 deletions centrifuge-app/src/components/LogoLink-deprecated.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@ const [LogoMark, WordMark] = config.logo

export function LogoLink() {
const isMedium = useIsAboveBreakpoint('M')
const isXLarge = useIsAboveBreakpoint('XL')
const isLarge = useIsAboveBreakpoint('L')

return (
<Link to="/">
<Box color="textPrimary" width={[80, 80, 36, 36, 120]}>
{isMedium && !isXLarge ? <LogoMark /> : <WordMark />}
<Box color="textPrimary" width={[80, 80, 36, 120, 120]}>
{!isMedium || isLarge ? <WordMark /> : <LogoMark />}
</Box>
</Link>
)
Expand Down
2 changes: 1 addition & 1 deletion centrifuge-app/src/components/PoolCard/PoolStatus.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { StatusChip, StatusChipProps } from '@centrifuge/fabric'
export type PoolStatusKey = 'Open for investments' | 'Closed' | 'Upcoming' | 'Archived'

const statusColor: { [key in PoolStatusKey]: StatusChipProps['status'] } = {
'Open for investments': 'ok',
'Open for investments': 'warning',
Closed: 'default',
Upcoming: 'default',
Archived: 'default',
Expand Down
245 changes: 146 additions & 99 deletions centrifuge-app/src/components/PoolCard/index.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,52 @@
import { useBasePath } from '@centrifuge/centrifuge-app/src/utils/useBasePath'
import { Rate } from '@centrifuge/centrifuge-js'
import { Box, Grid, Text, TextWithPlaceholder, Thumbnail } from '@centrifuge/fabric'
import { CurrencyBalance, Rate } from '@centrifuge/centrifuge-js'
import { Box, Card, Divider, Stack, Text, Thumbnail } from '@centrifuge/fabric'
import Decimal from 'decimal.js-light'
import { useTheme } from 'styled-components'
import { formatBalance, formatPercentage } from '../../utils/formatting'
import { useIsAboveBreakpoint } from '../../utils/useIsAboveBreakpoint'
import { Eththumbnail } from '../EthThumbnail'
import { Anchor, Ellipsis, Root } from '../ListItemCardStyles'
import { Tooltips } from '../Tooltips'
import styled from 'styled-components'
import { formatBalance, formatBalanceAbbreviated, formatPercentage } from '../../utils/formatting'
import { CardHeader } from '../ListItemCardStyles'
import { RouterTextLink } from '../TextLink'
import { PoolStatus, PoolStatusKey } from './PoolStatus'
const columns_base = 'minmax(150px, 2fr) minmax(100px, 1fr) 140px 70px 150px'
const columns_extended = 'minmax(200px, 2fr) minmax(100px, 1fr) 140px 100px 150px'
export const COLUMNS = ['minmax(100px, 1fr) 1fr', 'minmax(100px, 1fr) 1fr', columns_base, columns_extended]
export const COLUMN_GAPS = [3, 3, 6, 8]

type TrancheData = {
name: string
apr: string
minInvestment: string
}

export type InnerMetadata = {
minInitialInvestment?: CurrencyBalance
}

export type MetaData = {
tranches: {
[key: string]: InnerMetadata
}
pool: {
issuer: {
shortDescription?: string
}
investorType?: string
}
}

export type Tranche = {
id: string
currency: {
name: string
decimals: number
}
interestRatePerSec: {
toAprPercent: () => Decimal
} | null
capacity?: CurrencyBalance | number
metadata?: MetaData
}

const StyledRouterTextLink = styled(RouterTextLink)`
font-size: 12px;
margin-top: 8px;
text-decoration: none;
`

export type PoolCardProps = {
poolId?: string
Expand All @@ -23,7 +57,8 @@ export type PoolCardProps = {
apr?: Rate | null | undefined
status?: PoolStatusKey
iconUri?: string
isLoading?: boolean
tranches?: Tranche[]
metaData?: MetaData
}

export function PoolCard({
Expand All @@ -32,100 +67,112 @@ export function PoolCard({
assetClass,
valueLocked,
currencySymbol,
apr,
status,
iconUri,
isLoading,
tranches,
metaData,
}: PoolCardProps) {
const isMedium = useIsAboveBreakpoint('M')
const basePath = useBasePath('/pools')
const { sizes, zIndices } = useTheme()

return (
<Root as="article" bg={status === 'Archived' ? 'backgroundSecondary' : 'transparent'}>
<Grid gridTemplateColumns={COLUMNS} gap={COLUMN_GAPS} p={2} alignItems="center">
<Grid as="header" gridTemplateColumns={`${sizes.iconMedium}px 1fr`} alignItems="center" gap={2}>
<Eththumbnail show={poolId?.startsWith('0x')}>
{iconUri ? (
<Box as="img" src={iconUri} alt="" height="iconMedium" width="iconMedium" />
) : (
<Thumbnail type="pool" label="LP" size="small" />
)}
</Eththumbnail>
const isOneTranche = tranches && tranches?.length === 1
const renderText = (text: string) => (
<Text fontWeight={500} as="h2" variant="body1">
{text}
</Text>
)

<TextWithPlaceholder as="h2" variant="body2" color="textPrimary" isLoading={isLoading}>
<Ellipsis>{name}</Ellipsis>
</TextWithPlaceholder>
</Grid>
const tranchesData: TrancheData[] = tranches?.map((tranche: Tranche) => {
const words = tranche.currency.name.trim().split(' ')
const metadata = metaData?.tranches[tranche.id] ?? null
const trancheName = words[words.length - 1]
const investmentBalance = new CurrencyBalance(
metadata?.minInitialInvestment ?? 0,
tranche.currency.decimals
).toDecimal()

{isMedium && (
<TextWithPlaceholder as="span" variant="body2" color="textSecondary" isLoading={isLoading}>
<Ellipsis>{assetClass}</Ellipsis>
</TextWithPlaceholder>
)}
return {
name: trancheName,
apr: tranche.interestRatePerSec
? formatPercentage(tranche.interestRatePerSec.toAprPercent(), true, {
minimumFractionDigits: 1,
maximumFractionDigits: 1,
})
: '-',
minInvestment:
metadata && metadata.minInitialInvestment ? formatBalanceAbbreviated(investmentBalance, '', 0) : '-',
}
}) as TrancheData[]

<TextWithPlaceholder as="span" variant="body1" color="textPrimary" textAlign="right" isLoading={isLoading}>
<Ellipsis>{valueLocked ? formatBalance(valueLocked, currencySymbol) : '-'}</Ellipsis>
</TextWithPlaceholder>

{isMedium && (
<TextWithPlaceholder
as="span"
variant="body1"
color="textPrimary"
fontWeight={500}
textAlign="left"
isLoading={isLoading}
maxLines={1}
>
<Ellipsis>
{apr ? (
formatPercentage(apr.toAprPercent(), true, {
minimumFractionDigits: 1,
maximumFractionDigits: 1,
})
) : poolId === '4139607887' ? (
<Tooltips
style={{ zIndex: zIndices.overlay }}
type="tbillApr"
label={
<>
<Text fontWeight={500} variant="body1">
5.0%
</Text>
<Text variant="body3"> target</Text>{' '}
</>
}
/>
) : poolId === '1655476167' ? (
<Tooltips
style={{ zIndex: zIndices.overlay }}
type="dyfApr"
label={
<>
<Text fontWeight={500} variant="body1">
15.0%
</Text>
<Text variant="body3"> target</Text>{' '}
</>
}
/>
) : (
'—'
)}
</Ellipsis>
{status === 'Upcoming' && apr ? <Text variant="body3"> target</Text> : ''}
</TextWithPlaceholder>
)}

{isMedium && (
return (
<Card marginRight={20} marginBottom={20} padding={18} height={320}>
<RouterTextLink to={`${poolId}`} style={{ textDecoration: 'none' }}>
<CardHeader marginBottom={12}>
<Box>
<PoolStatus status={status} />
<Text as="h2" fontWeight={500} style={{ marginTop: 4 }} variant="body1">
{name}
</Text>
</Box>
{iconUri ? (
<Box as="img" src={iconUri} alt="" height={38} width={38} borderRadius="4px" />
) : (
<Thumbnail type="pool" label="LP" size="small" />
)}
</CardHeader>
<Divider />
<Box display="flex" justifyContent="space-between" alignItems="center" marginY="8px">
<Text as="span" variant="body3" color="textButtonPrimaryDisabled">
TVL ({currencySymbol})
</Text>
<Text variant="heading1">{valueLocked ? formatBalance(valueLocked, '') : '-'}</Text>
</Box>
<Box
bg={isOneTranche ? 'white' : 'backgroundSecondary'}
marginY="8px"
borderRadius={4}
padding={isOneTranche ? 0 : '8px'}
display="flex"
justifyContent="space-between"
width={isOneTranche ? '50%' : '100%'}
>
{!isOneTranche && (
<Stack>
<Text as="span" variant="body3" color="textButtonPrimaryDisabled">
Tranches
</Text>
{tranchesData?.map((tranche) => renderText(tranche.name))}
{tranches && tranches.length > 2 ? (
<StyledRouterTextLink to={`/pools/${poolId}`}>View all</StyledRouterTextLink>
) : null}
</Stack>
)}
<Stack>
<Text as="span" variant="body3" color="textButtonPrimaryDisabled">
APY
</Text>
{tranchesData?.map((tranche) => renderText(`${tranche.apr}`))}
</Stack>
<Stack>
<Text as="span" variant="body3" color="textButtonPrimaryDisabled">
Min Investment
</Text>
{tranchesData?.map((tranche) => renderText(`${tranche.minInvestment}`))}
</Stack>
</Box>
{metaData?.pool?.issuer?.shortDescription && (
<Box marginY={12}>
<Text as="p" variant="body2" color="textButtonPrimaryDisabled">
{metaData?.pool?.issuer?.shortDescription}
</Text>
</Box>
)}
</Grid>

{status === 'Upcoming' ? null : <Anchor to={`${basePath}/${poolId}`} aria-label={`Go to ${name} details`} />}
</Root>
<Box display="flex" justifyContent="space-between">
<Text variant="body2">Asset Type</Text>
<Text variant="body2">{assetClass ?? '-'}</Text>
</Box>
<Box display="flex" justifyContent="space-between">
<Text variant="body2">Investor Type</Text>
<Text variant="body2"> {metaData?.pool?.investorType || '-'}</Text>
</Box>
</RouterTextLink>
</Card>
)
}
Loading

0 comments on commit 15ac610

Please sign in to comment.