diff --git a/centrifuge-app/src/components/Charts/PoolPerformanceChart.tsx b/centrifuge-app/src/components/Charts/PoolPerformanceChart.tsx
index ce2bc15c37..5beb7254fb 100644
--- a/centrifuge-app/src/components/Charts/PoolPerformanceChart.tsx
+++ b/centrifuge-app/src/components/Charts/PoolPerformanceChart.tsx
@@ -1,11 +1,12 @@
-import { AnchorButton, Box, Grid, IconDownload, Shelf, Stack, Text } from '@centrifuge/fabric'
+import { AnchorButton, Box, IconDownload, Select, Shelf, Stack, Tabs, TabsItem, Text } from '@centrifuge/fabric'
+import Decimal from 'decimal.js-light'
import * as React from 'react'
import { useParams } from 'react-router'
import { Bar, CartesianGrid, ComposedChart, Line, ResponsiveContainer, Tooltip, XAxis, YAxis } from 'recharts'
-import styled, { useTheme } from 'styled-components'
+import { useTheme } from 'styled-components'
import { getCSVDownloadUrl } from '../../../src/utils/getCSVDownloadUrl'
import { daysBetween, formatDate } from '../../utils/date'
-import { formatBalance, formatBalanceAbbreviated } from '../../utils/formatting'
+import { formatBalance, formatBalanceAbbreviated, formatPercentage } from '../../utils/formatting'
import { useLoans } from '../../utils/useLoans'
import { useDailyPoolStates, usePool } from '../../utils/usePools'
import { Tooltips } from '../Tooltips'
@@ -15,25 +16,57 @@ import { getRangeNumber } from './utils'
type ChartData = {
day: Date
nav: number
- price: number | null
+ juniorTokenPrice: number
+ seniorTokenPrice?: number | null
+ currency?: string
+ seniorAPY: Decimal | null
+ juniorAPY: Decimal
+ isToday: boolean
}
-const RangeFilterButton = styled(Stack)`
- &:hover {
- cursor: pointer;
- }
-`
+type Tranche = {
+ seniority: number
+ tokenPrice: number
+}
const rangeFilters = [
+ { value: 'all', label: 'All' },
{ value: '30d', label: '30 days' },
{ value: '90d', label: '90 days' },
{ value: 'ytd', label: 'Year to date' },
- { value: 'all', label: 'All' },
-] as const
+]
+
+function calculateTranchePrices(pool: any) {
+ if (!pool?.tranches) return { juniorTokenPrice: 0, seniorTokenPrice: null }
+
+ const juniorTranche = pool.tranches.find((t: Tranche) => t.seniority === 0)
+ const seniorTranche = pool.tranches.length > 1 ? pool.tranches.find((t: Tranche) => t.seniority === 1) : null
+
+ const juniorTokenPrice = juniorTranche ? Number(formatBalance(juniorTranche.tokenPrice, undefined, 5, 5)) : 0
+ const seniorTokenPrice = seniorTranche ? Number(formatBalance(seniorTranche.tokenPrice, undefined, 5, 5)) : null
+
+ return { juniorTokenPrice, seniorTokenPrice }
+}
+
+function getYieldFieldForFilter(tranche: any, filter: string) {
+ switch (filter) {
+ case '30d':
+ return tranche.yield30DaysAnnualized || 0
+ case '90d':
+ return tranche.yield90DaysAnnualized || 0
+ case 'ytd':
+ return tranche.yieldYTD || 0
+ case 'all':
+ return tranche.yieldSinceInception || 0
+ default:
+ return 0
+ }
+}
function PoolPerformanceChart() {
const theme = useTheme()
- const chartColor = theme.colors.accentPrimary
+ const [selectedTabIndex, setSelectedTabIndex] = React.useState(0)
+ const chartColor = theme.colors.textGold
const { pid: poolId } = useParams<{ pid: string }>()
if (!poolId) throw new Error('Pool not found')
@@ -69,22 +102,61 @@ function PoolPerformanceChart() {
? formatBalance(pool?.tranches[pool.tranches.length - 1].tokenPrice || 0, undefined, 5, 5)
: null
+ const trancheTodayPrice = calculateTranchePrices(pool)
+
const data: ChartData[] = React.useMemo(
() =>
- truncatedPoolStates?.map((day) => {
+ truncatedPoolStates?.map((day: any) => {
const nav = day.poolState.netAssetValue.toDecimal().toNumber()
- const price = (isSingleTranche && Object.values(day.tranches)[0].price?.toFloat()) || null
+
+ const trancheKeys = Object.keys(day.tranches)
+ const juniorTrancheKey = trancheKeys[0]
+ const seniorTrancheKey = trancheKeys[1] || null
+
+ const juniorTokenPrice = day.tranches[juniorTrancheKey]?.price?.toFloat() ?? 0
+ const seniorTokenPrice = seniorTrancheKey ? day.tranches[seniorTrancheKey]?.price?.toFloat() ?? null : null
+
+ const juniorAPY = getYieldFieldForFilter(day.tranches[juniorTrancheKey], range.value).toPercent().toNumber()
+ const seniorAPY = seniorTrancheKey
+ ? getYieldFieldForFilter(day.tranches[seniorTrancheKey], range.value).toPercent().toNumber()
+ : null
+
if (day.timestamp && new Date(day.timestamp).toDateString() === new Date().toDateString()) {
- return { day: new Date(day.timestamp), nav: todayAssetValue, price: Number(todayPrice) }
+ const tranchePrices = calculateTranchePrices(pool)
+
+ return {
+ day: new Date(day.timestamp),
+ nav: todayAssetValue,
+ juniorTokenPrice: tranchePrices.juniorTokenPrice ?? 0,
+ seniorTokenPrice: tranchePrices.seniorTokenPrice ?? null,
+ juniorAPY,
+ seniorAPY,
+ isToday: true,
+ }
+ }
+
+ return {
+ day: new Date(day.timestamp),
+ nav: Number(nav),
+ juniorTokenPrice: juniorTokenPrice !== 0 ? juniorTokenPrice : null,
+ seniorTokenPrice: seniorTokenPrice !== 0 ? seniorTokenPrice : null,
+ juniorAPY,
+ seniorAPY,
+ isToday: false,
}
- return { day: new Date(day.timestamp), nav: Number(nav), price: Number(price) }
}) || [],
- [isSingleTranche, truncatedPoolStates, todayAssetValue, todayPrice]
+ [isSingleTranche, truncatedPoolStates, todayAssetValue, todayPrice, pool, range]
)
+ const todayData = data.find((day) => day.isToday)
+
const today = {
nav: todayAssetValue,
price: todayPrice,
+ currency: pool.currency.symbol,
+ juniorAPY: todayData?.juniorAPY,
+ seniorAPY: todayData?.seniorAPY,
+ ...trancheTodayPrice,
}
const chartData = data.slice(-rangeNumber)
@@ -94,28 +166,24 @@ function PoolPerformanceChart() {
return undefined
}
- const filteredData = chartData.map((data) => ({
- day: data.day,
- tokenPrice: data.price,
- }))
+ const filteredData = chartData.map((data) => {
+ const base = {
+ day: data.day,
+ nav: data.nav,
+ juniorTokenPrice: data.juniorTokenPrice ?? 0,
+ juniorAPY: data.juniorAPY,
+ }
+ if (data.seniorTokenPrice && data.seniorAPY) {
+ return {
+ ...base,
+ seniorTokenPrice: data.seniorTokenPrice,
+ seniorAPY: data.seniorAPY,
+ }
+ } else return { ...base }
+ })
return getCSVDownloadUrl(filteredData as any)
- }, [chartData])
-
- const priceRange = React.useMemo(() => {
- if (!chartData) return [0, 100]
-
- const min =
- chartData?.reduce((prev, curr) => {
- return prev.price! < curr.price! ? prev : curr
- }, chartData[0])?.price || 0
-
- const max =
- chartData?.reduce((prev, curr) => {
- return prev.price! > curr.price! ? prev : curr
- }, chartData[0])?.price || 1
- return [min, max]
- }, [chartData])
+ }, [chartData, selectedTabIndex])
if (truncatedPoolStates && truncatedPoolStates?.length < 1 && poolAge > 0)
return No data available
@@ -138,46 +206,31 @@ function PoolPerformanceChart() {
}
return (
-
-
-
+
+
+
Pool performance
+ setSelectedTabIndex(index)}>
+
+ Price
+
+
+ APY
+
+
Download
-
-
-
- {chartData.length > 0 &&
- rangeFilters.map((rangeFilter, index) => (
-
- setRange(rangeFilter)}>
-
- {rangeFilter.label}
-
-
-
- {index !== rangeFilters.length - 1 && (
-
- )}
-
- ))}
-
-
-
-
+
+
{chartData?.length ? (
@@ -200,7 +253,7 @@ function PoolPerformanceChart() {
formatBalanceAbbreviated(tick, '', 0)}
yAxisId="left"
width={80}
@@ -208,11 +261,11 @@ function PoolPerformanceChart() {
formatBalanceAbbreviated(tick, '', 6)}
+ style={{ fontSize: '10px', fill: theme.colors.textPrimary }}
+ tickFormatter={(tick: number) => formatBalanceAbbreviated(tick, '', 2)}
yAxisId="right"
orientation="right"
- domain={priceRange}
+ domain={selectedTabIndex === 0 ? ['dataMin - 0.25', 'dataMax + 0.25'] : [0, 'dataMax + 0.25']}
/>
{formatDate(payload[0].payload.day)}
- {payload.map(({ name, value }, index) => (
-
-
- {name === 'nav' ? 'NAV' : name === 'price' ? 'Token price' : 'Cash'}
-
-
- {name === 'nav' && typeof value === 'number'
- ? formatBalance(value, 'USD')
- : typeof value === 'number'
- ? formatBalance(value, 'USD', 6)
- : '-'}
-
-
- ))}
+ {payload.map(({ name, value }, index) => {
+ const labelMap: Record = {
+ nav: 'NAV',
+ juniorTokenPrice: 'Junior Token Price',
+ seniorTokenPrice: 'Senior Token Price',
+ juniorAPY: 'Junior APY',
+ seniorAPY: 'Senior APY',
+ default: 'Cash',
+ }
+
+ const label = typeof name === 'string' ? labelMap[name] ?? labelMap.default : labelMap.default
+
+ const formattedValue = (() => {
+ if (typeof value === 'undefined' || Array.isArray(value)) {
+ return '-'
+ }
+
+ if (name === 'juniorAPY' || name === 'seniorAPY') {
+ return formatPercentage(value)
+ }
+
+ return formatBalance(
+ Number(value),
+ name === 'nav' ? pool.currency.symbol ?? 'USD' : '',
+ name === 'juniorTokenPrice' || name === 'seniorTokenPrice' ? 6 : 0
+ )
+ })()
+
+ return (
+
+
+ {label}
+
+
+ {formattedValue}
+
+
+ )
+ })}
)
}
return null
}}
/>
-
-
+
+
+ {chartData.some((d) => d.seniorTokenPrice !== null) && (
+
+ )}
+
+
) : (
@@ -255,35 +382,96 @@ function PoolPerformanceChart() {
function CustomLegend({
data,
+ setRange,
+ selectedTabIndex,
}: {
data: {
+ currency: string
nav: number
- price: number | null
+ juniorTokenPrice: number
+ seniorTokenPrice?: number | null
+ juniorAPY: number
+ seniorAPY: number
}
+ setRange: (value: { value: string; label: string }) => void
+ selectedTabIndex: number
}) {
- const theme = useTheme()
+ const Dot = ({ color }: { color: string }) => (
+
+ )
+
+ const navObj = {
+ color: 'backgroundTertiary',
+ label: `NAV ${data.currency}`,
+ value: formatBalance(data.nav),
+ type: 'nav',
+ show: true,
+ }
+
+ const tokenData = [
+ navObj,
+ {
+ color: 'textGold',
+ label: 'Junior token price',
+ value: data.juniorTokenPrice ?? 0,
+ type: 'singleTrancheTokenPrice',
+ show: true,
+ },
+ {
+ color: 'textPrimary',
+ label: 'Senior token price',
+ value: data.seniorTokenPrice ?? 0,
+ type: 'singleTrancheTokenPrice',
+ show: !!data.seniorTokenPrice,
+ },
+ ]
+
+ const apyData = [
+ navObj,
+ {
+ color: 'textGold',
+ label: 'Junior APY',
+ value: formatPercentage(data.juniorAPY ?? 0),
+ type: 'singleTrancheTokenPrice',
+ show: true,
+ },
+ {
+ color: 'textPrimary',
+ label: 'Senior APY',
+ value: formatPercentage(data.seniorAPY ?? 0),
+ type: 'singleTrancheTokenPrice',
+ show: !!data.seniorAPY,
+ },
+ ]
+
+ const graphData = selectedTabIndex === 0 ? tokenData : apyData
+
+ const toggleRange = (e: any) => {
+ const value = e.target.value
+ const range = rangeFilters.find((range) => range.value === value)
+ setRange(range ?? rangeFilters[0])
+ }
return (
-
-
-
-
- {formatBalance(data.nav, 'USD')}
-
- {data.price && (
-
-
- {data.price ? formatBalance(data.price, 'USD', 6) : '-'}
-
- )}
-
-
+
+
+ {graphData.map((item: any, index: any) => {
+ if (!item.show) return
+ return (
+
+
+
+
+
+ {item.value}
+
+ )
+ })}
+
+
+
+
+
)
}
@@ -292,7 +480,7 @@ const CustomTick = ({ x, y, payload }: any) => {
return (
{children}
@@ -44,7 +44,7 @@ export function TooltipContainer({ children }: { children: React.ReactNode }) {
export function TooltipTitle({ children }: { children: React.ReactNode }) {
return (
-
+
{children}
)
diff --git a/centrifuge-app/src/components/DataTable.tsx b/centrifuge-app/src/components/DataTable.tsx
index 45088eeec4..0ae6b99f03 100644
--- a/centrifuge-app/src/components/DataTable.tsx
+++ b/centrifuge-app/src/components/DataTable.tsx
@@ -49,6 +49,7 @@ export type DataTableProps = {
footer?: React.ReactNode
pageSize?: number
page?: number
+ headerStyles?: React.CSSProperties
} & GroupedProps
export type OrderBy = 'asc' | 'desc'
@@ -96,6 +97,7 @@ export const DataTable = >({
defaultSortOrder = 'desc',
pageSize = Infinity,
page = 1,
+ headerStyles,
}: DataTableProps) => {
const [orderBy, setOrderBy] = React.useState>(
defaultSortKey ? { [defaultSortKey]: defaultSortOrder } : {}
@@ -122,7 +124,7 @@ export const DataTable = >({
return (
{showHeader && (
-
+
{columns.map((col, i) => (
@@ -203,12 +205,13 @@ const Row = styled('div')`
box-shadow: ${({ theme }) => `-1px 0 0 0 ${theme.colors.borderPrimary}, 1px 0 0 0 ${theme.colors.borderPrimary}`};
`
-const HeaderRow = styled(Row)(
+const HeaderRow = styled(Row)<{ styles?: any }>(({ styles }) =>
css({
backgroundColor: 'backgroundSecondary',
borderStyle: 'solid',
borderWidth: '1px 0',
borderColor: 'borderPrimary',
+ ...styles,
})
)
diff --git a/centrifuge-app/src/components/IssuerSection.tsx b/centrifuge-app/src/components/IssuerSection.tsx
index 5798a89b04..3065559866 100644
--- a/centrifuge-app/src/components/IssuerSection.tsx
+++ b/centrifuge-app/src/components/IssuerSection.tsx
@@ -1,61 +1,121 @@
import { PoolMetadata } from '@centrifuge/centrifuge-js'
import { useCentrifuge } from '@centrifuge/centrifuge-react'
-import { Accordion, AnchorButton, Box, IconExternalLink, Shelf, Stack, Text } from '@centrifuge/fabric'
+import {
+ AnchorButton,
+ Box,
+ IconBalanceSheet,
+ IconCashflow,
+ IconChevronRight,
+ IconExternalLink,
+ IconProfitAndLoss,
+ Shelf,
+ Stack,
+ Text,
+} from '@centrifuge/fabric'
import * as React from 'react'
+import { useLocation } from 'react-router'
+import styled from 'styled-components'
import { ExecutiveSummaryDialog } from './Dialogs/ExecutiveSummaryDialog'
import { LabelValueStack } from './LabelValueStack'
-import { PillButton } from './PillButton'
-import { AnchorTextLink } from './TextLink'
+import { AnchorPillButton, PillButton } from './PillButton'
+import { RouterTextLink } from './TextLink'
+
+const SUBTLE_GRAY = '#91969b21'
type IssuerSectionProps = {
metadata: Partial | undefined
}
+const reportLinks = [
+ { label: 'Balance sheet', href: '/balance-sheet', icon: },
+ { label: 'Profit & loss', href: '/profit-and-loss', icon: },
+ { label: 'Cashflow statement', href: '/cash-flow-statement', icon: },
+]
+
+const StyledRouterTextLink = styled(RouterTextLink)`
+ color: white;
+ text-decoration: unset;
+ font-size: 14px;
+ :active {
+ color: white;
+ }
+ :visited {
+ color: white;
+ }
+`
+
export function ReportDetails({ metadata }: IssuerSectionProps) {
- const cent = useCentrifuge()
+ const pathname = useLocation().pathname
const report = metadata?.pool?.reports?.[0]
-
return (
-
- Pool analysis
-
- {report && (
- <>
-
- {report.author.avatar?.uri && (
-
- )}
- {report.author.name && (
- {report.author.name}} />
- )}
- {report.author.title && (
- {report.author.title}} />
- )}
-
-
-
- View full analysis
-
-
- >
- )}
-
-
+ <>
+
+
+ Reports
+
+
+ View all
+
+
+
+
+ {reportLinks.map((link, i) => (
+
+
+ {link.icon}
+
+ {link.label}
+
+
+
+
+ ))}
+
+
+ {report && }
+ >
)
}
export function IssuerDetails({ metadata }: IssuerSectionProps) {
const cent = useCentrifuge()
const [isDialogOpen, setIsDialogOpen] = React.useState(false)
+
+ const links = [
+ {
+ label: 'Website',
+ href: metadata?.pool?.links.website,
+ show: !!metadata?.pool?.links.website,
+ },
+ {
+ label: 'Forum',
+ href: metadata?.pool?.links.forum,
+ show: !!metadata?.pool?.links.forum,
+ },
+ {
+ label: 'Email',
+ href: `mailto:${metadata?.pool?.issuer.email}`,
+ show: !!metadata?.pool?.issuer.email,
+ },
+ {
+ label: 'Executive Summary',
+ show: !!metadata?.pool?.links.executiveSummary,
+ onClick: () => setIsDialogOpen(true),
+ },
+ ]
return (
-
-
+
+
{metadata?.pool?.issuer.logo && (
)}
- {metadata?.pool?.issuer.name}} />
- {metadata?.pool?.issuer.repName}}
- />
- {metadata?.pool?.issuer.shortDescription}}
- />
- {metadata?.pool?.issuer.description}}
- />
- {metadata?.pool?.links.executiveSummary && (
-
- setIsDialogOpen(true)}>
- Executive summary
-
- setIsDialogOpen(false)}
- />
- >
- }
- />
- )}
-
-
- {(metadata?.pool?.links.website || metadata?.pool?.links.forum || metadata?.pool?.issuer.email) && (
-
-
- {metadata?.pool?.links.website && (
- Website
- )}
- {metadata?.pool?.links.forum && (
- Forum
- )}
- {metadata?.pool?.issuer.email && (
- Email
- )}
-
-
- }
- />
- )}
- {!!metadata?.pool?.details?.length && (
- } />
- )}
+
+
+
+ {metadata?.pool?.name}
+
+ {metadata?.pool?.issuer.description}
+
+
+ setIsDialogOpen(false)}
+ />
)
}
+const Links = ({ links }: { links: { label: string; href?: string; show: boolean; onClick?: () => void }[] }) => {
+ return (
+
+ {links.map((link, index) => {
+ if (!link.show) return null
+
+ if (link.onClick) {
+ return (
+
+ {link.label}
+
+ )
+ }
+
+ return (
+
+ {link.label}
+
+ )
+ })}
+
+ )
+}
+
export function RatingDetails({ metadata }: IssuerSectionProps) {
const rating = metadata?.pool?.rating
- return (
+ return rating?.ratingAgency || rating?.ratingValue || rating?.ratingReportUrl ? (
Pool rating
- {rating && (
-
- {rating.ratingAgency && (
- {rating.ratingAgency}} />
- )}
- {rating.ratingValue && (
- {rating.ratingValue}} />
- )}
-
- )}
+
+ {rating.ratingAgency && (
+ {rating.ratingAgency}} />
+ )}
+ {rating.ratingValue && (
+ {rating.ratingValue}} />
+ )}
+
{rating?.ratingReportUrl && (
@@ -151,5 +191,24 @@ export function RatingDetails({ metadata }: IssuerSectionProps) {
)}
- )
+ ) : null
+}
+
+export const PoolAnalysis = ({ metadata, inverted }: IssuerSectionProps & { inverted?: boolean }) => {
+ const report = metadata?.pool?.reports?.[0]
+ return report?.author?.name || report?.author?.title ? (
+
+
+ Pool analysis
+
+
+
+ Reviewer: {report?.author?.name || 'N/A'}
+
+
+ Title: {report?.author?.title || 'N/A'}
+
+
+
+ ) : null
}
diff --git a/centrifuge-app/src/components/PillButton.tsx b/centrifuge-app/src/components/PillButton.tsx
index 48bf81d31b..cea7e31c8e 100644
--- a/centrifuge-app/src/components/PillButton.tsx
+++ b/centrifuge-app/src/components/PillButton.tsx
@@ -8,24 +8,25 @@ const Pill = styled.button<{ variant?: 'small' | 'regular' }>(
css({
display: 'inline-block',
appearance: 'none',
- border: 0,
color: 'textPrimary',
whiteSpace: 'nowrap',
cursor: 'pointer',
backgroundColor: 'backgroundSecondary',
textDecoration: 'none',
- '&:visited,&:active': {
+ borderRadius: 20,
+ '&:visited, &:active': {
color: 'textPrimary',
},
'&:hover': {
- color: 'textSelected',
+ color: 'textGold',
},
}),
- {
+ ({ theme }) => ({
+ border: `1px solid ${theme.colors.textPrimary}`,
'&:focus-visible': {
boxShadow: '3px 3px 0 var(--fabric-focus)',
},
- },
+ }),
({ variant }) =>
variant === 'regular'
? css({
diff --git a/centrifuge-app/src/components/PoolList.tsx b/centrifuge-app/src/components/PoolList.tsx
index 1b17fcc629..6d9b8d96e5 100644
--- a/centrifuge-app/src/components/PoolList.tsx
+++ b/centrifuge-app/src/components/PoolList.tsx
@@ -136,7 +136,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 {
@@ -145,14 +144,7 @@ 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,
metaData: metaData as MetaData,
@@ -160,6 +152,24 @@ export function poolsToPoolCardProps(
})
}
+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 = {}
diff --git a/centrifuge-app/src/components/PoolOverview/KeyMetrics.tsx b/centrifuge-app/src/components/PoolOverview/KeyMetrics.tsx
index e12ecb5628..ca3b9ba963 100644
--- a/centrifuge-app/src/components/PoolOverview/KeyMetrics.tsx
+++ b/centrifuge-app/src/components/PoolOverview/KeyMetrics.tsx
@@ -1,52 +1,114 @@
-import { ActiveLoan, Loan, TinlakeLoan } from '@centrifuge/centrifuge-js'
-import { NetworkIcon } from '@centrifuge/centrifuge-react'
-import { Box, Card, Grid, IconExternalLink, Shelf, Stack, Text, Tooltip } from '@centrifuge/fabric'
+import { CurrencyBalance, Perquintill } from '@centrifuge/centrifuge-js'
+import { NetworkIcon, formatBalanceAbbreviated } from '@centrifuge/centrifuge-react'
+import { Box, Card, IconArrowRightWhite, IconMoody, IconSp, Shelf, Stack, Text, Tooltip } from '@centrifuge/fabric'
+import { BN } from 'bn.js'
import capitalize from 'lodash/capitalize'
import startCase from 'lodash/startCase'
+import { useMemo } from 'react'
+import styled, { useTheme } from 'styled-components'
import { evmChains } from '../../config'
+import { formatBalance, formatPercentage } from '../../utils/formatting'
+import { useAverageMaturity } from '../../utils/useAverageMaturity'
import { useActiveDomains } from '../../utils/useLiquidityPools'
-import { usePool } from '../../utils/usePools'
+import { useDailyTranchesStates, usePool, usePoolFees, usePoolMetadata } from '../../utils/usePools'
+import { PillButton } from '../PillButton'
+import { PoolStatus } from '../PoolCard/PoolStatus'
+import { getPoolStatus } from '../PoolList'
import { Spinner } from '../Spinner'
+import { Tooltips } from '../Tooltips'
+
+const StyledPillButton = styled(PillButton)`
+ background-color: white;
+ cursor: default;
+ :hover {
+ color: black;
+ }
+`
type Props = {
assetType?: { class: string; subClass: string }
- averageMaturity: string
- loans: TinlakeLoan[] | Loan[] | null | undefined
poolId: string
}
-export const KeyMetrics = ({ assetType, averageMaturity, loans, poolId }: Props) => {
- const isTinlakePool = poolId.startsWith('0x')
+interface DailyTrancheState {
+ yield30DaysAnnualized: Perquintill
+ timestamp: string
+}
- function hasValuationMethod(pricing: any): pricing is { valuationMethod: string } {
- return pricing && typeof pricing.valuationMethod === 'string'
+type DailyTrancheStateArr = Record
+
+type Tranche = {
+ currency: {
+ name: string
}
+ id: string
+}
+
+const getTodayValue = (data: DailyTrancheStateArr | null | undefined): DailyTrancheStateArr | undefined => {
+ if (!data) return
+ if (!Object.keys(data).length) return
+
+ const today = new Date()
- 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)
+ const filteredData: DailyTrancheStateArr = Object.keys(data).reduce((result, key) => {
+ const filteredValues = data[key].filter((obj) => {
+ const objDate = new Date(obj.timestamp)
return (
- loan.status === 'Active' &&
- loan.pricing.maturityDate &&
- new Date(loan.pricing.maturityDate).getTime() < Date.now() &&
- !loan.outstandingDebt.isZero()
+ objDate.getDate() === today.getDate() &&
+ objDate.getMonth() === today.getMonth() &&
+ objDate.getFullYear() === today.getFullYear()
)
- }).length
+ })
+
+ if (filteredValues.length > 0) {
+ result[key] = filteredValues
+ }
+
+ return result
+ }, {} as DailyTrancheStateArr)
+
+ return filteredData
+}
+
+export const KeyMetrics = ({ poolId }: Props) => {
+ const isTinlakePool = poolId.startsWith('0x')
+ const pool = usePool(poolId)
+ const { data: metadata } = usePoolMetadata(pool)
+ const poolFees = usePoolFees(poolId)
+ const tranchesIds = pool.tranches.map((tranche) => tranche.id)
+ const dailyTranches = useDailyTranchesStates(tranchesIds)
+ const totalNav = pool.nav.total.toFloat()
+ const theme = useTheme()
+ const averageMaturity = useAverageMaturity(poolId)
+
+ const pendingFees = useMemo(() => {
+ return new CurrencyBalance(
+ poolFees?.map((f) => f.amounts.pending).reduce((acc, f) => acc.add(f), new BN(0)) ?? new BN(0),
+ pool.currency.decimals
+ )
+ }, [poolFees, pool.currency.decimals])
+
+ const expenseRatio = (pendingFees.toFloat() / totalNav) * 100
+
+ const tranchesAPY = useMemo(() => {
+ const thirtyDayAPY = getTodayValue(dailyTranches)
+ if (!thirtyDayAPY) return null
+
+ return Object.keys(thirtyDayAPY).map((key) => {
+ return thirtyDayAPY[key][0].yield30DaysAnnualized
+ ? formatPercentage(thirtyDayAPY[key][0].yield30DaysAnnualized)
+ : null
+ })
+ }, [dailyTranches])
+
+ const minInvestmentPerTranche = useMemo(() => {
+ if (!metadata?.tranches) return null
+
+ return Object.values(metadata.tranches).map((item) => {
+ const minInv = new CurrencyBalance(item.minInitialInvestment ?? 0, pool.currency.decimals).toDecimal()
+ return item.minInitialInvestment ? formatBalanceAbbreviated(minInv, '', 0) : null
+ })
+ }, [metadata?.tranches])
const isBT3BT4 =
poolId.toLowerCase() === '0x90040f96ab8f291b6d43a8972806e977631affde' ||
@@ -54,8 +116,16 @@ export const KeyMetrics = ({ assetType, averageMaturity, loans, poolId }: Props)
const metrics = [
{
- metric: 'Asset class',
- value: `${capitalize(startCase(assetType?.class))} - ${assetType?.subClass}`,
+ metric: 'Asset type',
+ value: `${capitalize(startCase(metadata?.pool?.asset?.class))} - ${metadata?.pool?.asset?.subClass}`,
+ },
+ {
+ metric: '30-day APY',
+ value: tranchesAPY?.length
+ ? tranchesAPY.map((tranche, index) => {
+ return tranche && `${tranche} ${index !== tranchesAPY?.length - 1 ? '-' : ''} `
+ })
+ : '-',
},
...(isBT3BT4
? []
@@ -66,37 +136,76 @@ 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: minInvestmentPerTranche?.length
+ ? minInvestmentPerTranche.map((tranche, index) => {
+ return tranche && `${tranche} ${index !== minInvestmentPerTranche?.length - 1 ? '-' : ''} `
+ })
+ : '-',
},
- {
- metric: 'Ongoing assets',
- value: ongoingAssetCount,
- },
- ...(writtenOffAssetCount
+ ...(metadata?.pool?.investorType
? [
{
- metric: 'Written off assets',
- value: writtenOffAssetCount,
+ metric: 'Investor type',
+ value: metadata?.pool.investorType,
},
]
: []),
- ...(overdueAssetCount
+ ...(!isTinlakePool
? [
{
- metric: 'Overdue assets',
- value: overdueAssetCount,
+ metric: 'Available networks',
+ value: ,
},
]
: []),
-
- ...(!isTinlakePool
+ ...(metadata?.pool?.poolStructure
? [
{
- metric: 'Available networks',
- value: ,
+ metric: 'Pool structure',
+ value: metadata?.pool?.poolStructure,
+ },
+ ]
+ : []),
+ ...(metadata?.pool?.rating?.ratingValue
+ ? [
+ {
+ metric: 'Rating',
+ value: (
+
+ }
+ >
+
+ {metadata?.pool?.rating?.ratingAgency?.includes('moody') ? (
+
+ ) : (
+
+ )}
+ {metadata?.pool?.rating?.ratingValue}
+
+
+ ),
+ },
+ ]
+ : []),
+ ...(!!expenseRatio
+ ? [
+ {
+ metric: ,
+ value: `${formatBalance(expenseRatio * 100, '', 2)}%`,
},
]
: []),
@@ -104,31 +213,23 @@ export const KeyMetrics = ({ assetType, averageMaturity, loans, poolId }: Props)
return (
-
-
- Key metrics
-
-
+
+
+
+ Overview
+
+
+
+
{metrics.map(({ metric, value }, index) => (
-
-
+
+
{metric}
{value}
-
+
))}
@@ -136,49 +237,67 @@ export const KeyMetrics = ({ assetType, averageMaturity, loans, poolId }: Props)
)
}
+const TooltipBody = ({
+ title,
+ subtitle = 'View transactions',
+ url,
+ links,
+}: {
+ title: string
+ subtitle?: string
+ url?: string
+ links?: { text: string; url: string }[]
+}) => {
+ return (
+
+
+
+ {title}
+
+ {links ? (
+ links.map((link, index) => (
+
+
+ {subtitle}
+
+
+ ))
+ ) : (
+
+
+ {subtitle}
+
+
+ )}
+
+
+
+ )
+}
+
const AvailableNetworks = ({ poolId }: { poolId: string }) => {
const activeDomains = useActiveDomains(poolId)
const pool = usePool(poolId)
+
+ const renderTooltipBody = (networkName: string, tranches: Tranche[], baseUrl: string) => {
+ const links = tranches.map((tranche) => ({
+ text: `View ${tranche.currency.name.split(' ').at(-1)}`,
+ url: `${baseUrl}/token/${tranche.id}`,
+ }))
+
+ return
+ }
+
return (
-
+
{activeDomains.data?.length || import.meta.env.REACT_APP_COLLATOR_WSS_URL.includes('development') ? (
- Centrifuge
- {pool.tranches.length > 1 ? (
- pool.tranches.map((tranche) => (
-
-
-
- View {tranche.currency.name.split(' ').at(-1)}
- {' '}
-
-
-
- ))
- ) : (
-
-
-
- View transactions
- {' '}
-
-
-
- )}
-
- }
+ body={}
>
-
+
) : (
@@ -192,41 +311,8 @@ const AvailableNetworks = ({ poolId }: { poolId: string }) => {
key={domain.poolManager}
delay={300}
bodyWidth="maxContent"
- bodyPadding={0}
- body={
-
- {chain.name}
- {pool.tranches.length > 1 ? (
- pool.tranches.map((tranche) => (
-
-
-
- View {tranche.currency.name.split(' ').at(-1)}
- {' '}
-
-
-
- ))
- ) : (
-
-
-
- View transactions
- {' '}
-
-
-
- )}
-
- }
+ bodyPadding={1}
+ body={renderTooltipBody(chain.name, pool.tranches, chain.blockExplorerUrl)}
>
diff --git a/centrifuge-app/src/components/PoolOverview/PoolPerfomance.tsx b/centrifuge-app/src/components/PoolOverview/PoolPerfomance.tsx
index a34f086a0b..4d51641f83 100644
--- a/centrifuge-app/src/components/PoolOverview/PoolPerfomance.tsx
+++ b/centrifuge-app/src/components/PoolOverview/PoolPerfomance.tsx
@@ -5,7 +5,7 @@ import { Spinner } from '../Spinner'
export const PoolPerformance = () => (
}>
-
+
diff --git a/centrifuge-app/src/components/PoolOverview/PoolStructure.tsx b/centrifuge-app/src/components/PoolOverview/PoolStructure.tsx
deleted file mode 100644
index cb79062122..0000000000
--- a/centrifuge-app/src/components/PoolOverview/PoolStructure.tsx
+++ /dev/null
@@ -1,102 +0,0 @@
-import { Rate } from '@centrifuge/centrifuge-js'
-import { Box, Card, Grid, Stack, Text, Tooltip } from '@centrifuge/fabric'
-import capitalize from 'lodash/capitalize'
-import { formatPercentage } from '../../utils/formatting'
-
-type Props = {
- numOfTranches: number
- poolId: string
- poolStatus?: string
- poolFees: {
- fee: Rate
- name: string
- id: number
- }[]
-}
-
-export const PoolStructure = ({ numOfTranches, poolStatus, poolFees }: Props) => {
- const metrics = [
- {
- metric: 'Pool type',
- value: capitalize(poolStatus),
- },
- {
- metric: 'Pool structure',
- value: 'Revolving pool',
- },
- {
- metric: 'Tranche structure',
- value: numOfTranches === 1 ? 'Unitranche' : `${numOfTranches} tranches`,
- },
- ...poolFees.map((fee) => {
- return {
- metric: fee.name,
- value: formatPercentage(fee.fee.toPercent(), true, {}, 3),
- }
- }),
- ]
-
- const getValue = (metric: string, value: string) => {
- if (metric === 'Pool structure')
- return (
-
-
- {value}
-
-
- )
- if (metric === 'Tranche structure')
- return (
-
-
- {value}
-
-
- )
-
- if (metric === 'Pool type' && value === 'Open')
- return (
-
-
- Open
-
-
- )
- return (
-
- {value}
-
- )
- }
-
- return (
-
-
-
- Structure
-
-
- {metrics.map(({ metric, value }, index) => (
-
-
- {metric}
-
- {getValue(metric, value)}
-
- ))}
-
-
-
- )
-}
diff --git a/centrifuge-app/src/components/PoolOverview/TrancheTokenCards.tsx b/centrifuge-app/src/components/PoolOverview/TrancheTokenCards.tsx
index 9867c68f45..0b78abe657 100644
--- a/centrifuge-app/src/components/PoolOverview/TrancheTokenCards.tsx
+++ b/centrifuge-app/src/components/PoolOverview/TrancheTokenCards.tsx
@@ -1,125 +1,108 @@
-import { Box, Shelf, Stack, Text } from '@centrifuge/fabric'
+import { Perquintill } from '@centrifuge/centrifuge-js'
+import { Box, Shelf, Text } from '@centrifuge/fabric'
+import { useMemo } from 'react'
+import { useTheme } from 'styled-components'
import { InvestButton, Token } from '../../pages/Pool/Overview'
import { daysBetween } from '../../utils/date'
import { formatBalance, formatPercentage } from '../../utils/formatting'
-import { useIsAboveBreakpoint } from '../../utils/useIsAboveBreakpoint'
-import { Tooltips } from '../Tooltips'
+import { usePool } from '../../utils/usePools'
+import { DataTable } from '../DataTable'
+
+export const TrancheTokenCards = ({ trancheTokens, poolId }: { trancheTokens: Token[]; poolId: string }) => {
+ const pool = usePool(poolId)
+ const theme = useTheme()
+ const isTinlakePool = poolId.startsWith('0x')
+ const daysSinceCreation = pool?.createdAt ? daysBetween(new Date(pool.createdAt), new Date()) : 0
-export const TrancheTokenCards = ({
- trancheTokens,
- poolId,
- createdAt,
- poolCurrencySymbol,
-}: {
- trancheTokens: Token[]
- poolId: string
- createdAt: string | null
- poolCurrencySymbol: string
-}) => {
- const seniorTranche = Math.max(...trancheTokens.map((trancheToken) => trancheToken.seniority))
const getTrancheText = (trancheToken: Token) => {
- if (seniorTranche === trancheToken.seniority) return 'senior'
if (trancheToken.seniority === 0) return 'junior'
+ if (trancheToken.seniority === 1) return 'senior'
return 'mezzanine'
}
- return (
-
- {trancheTokens?.map((trancheToken) => (
-
- ))}
-
- )
-}
+ const calculateApy = (trancheToken: Token) => {
+ if (isTinlakePool && getTrancheText(trancheToken) === 'senior') return formatPercentage(trancheToken.apy)
+ if (daysSinceCreation < 30) return 'N/A'
+ return trancheToken.yield30DaysAnnualized
+ ? formatPercentage(new Perquintill(trancheToken.yield30DaysAnnualized))
+ : '-'
+ }
-const TrancheTokenCard = ({
- trancheToken,
- poolId,
- createdAt,
- numOfTrancheTokens,
- poolCurrencySymbol,
- trancheText,
-}: {
- trancheToken: Token
- poolId: string
- createdAt: string | null
- numOfTrancheTokens: number
- poolCurrencySymbol: string
- trancheText: 'senior' | 'junior' | 'mezzanine'
-}) => {
- const isMedium = useIsAboveBreakpoint('M')
+ const columnConfig = [
+ {
+ header: 'Token',
+ align: 'left',
+ formatter: (v: any) => v,
+ },
+ {
+ header: 'APY',
+ align: 'left',
+ formatter: (v: any) => (v ? calculateApy(v) : '-'),
+ },
+ {
+ header: `TVL (${pool?.currency.symbol})`,
+ align: 'left',
+ formatter: (v: any) => (v ? formatBalance(v) : '-'),
+ },
+ {
+ header: 'Token price',
+ align: 'left',
+ formatter: (v: any) => (v ? formatBalance(v, pool?.currency.symbol, pool?.currency.decimals) : '-'),
+ },
+ ...(pool.tranches.length > 1
+ ? [
+ {
+ header: 'Subordination',
+ align: 'left',
+ formatter: (_: any, row: any) => {
+ if (row.value[1].seniority === 0) return '-'
+ return formatPercentage(row.value[1].protection)
+ },
+ },
+ ]
+ : []),
+ {
+ header: '',
+ align: 'left',
+ formatter: (_: any, row: any) => {
+ return
+ },
+ },
+ ]
- const isTinlakePool = poolId.startsWith('0x')
- const daysSinceCreation = createdAt ? daysBetween(new Date(createdAt), new Date()) : 0
- const apyTooltipBody =
- poolId === '4139607887'
- ? 'Based on 3-month to 6-month T-Bills returns.'
- : poolId === '1655476167'
- ? 'Based on the return of the underlying funds'
- : `The 30d ${trancheText} yield is the effective annualized return of the pool's ${trancheText} token over the last 30 days.${
- daysSinceCreation < 30 && !isTinlakePool ? ' APY displayed after 30 days following token launch.' : ''
- }`
+ const columns = useMemo(() => {
+ return columnConfig.map((col, index) => {
+ return {
+ align: col.align,
+ header: col.header,
+ cell: (row: any) => (
+
+ {col.formatter(row.value[index], row)}
+
+ ),
+ }
+ })
+ }, [columnConfig])
- const calculateApy = () => {
- if (poolId === '4139607887') return formatPercentage(5)
- if (poolId === '1655476167') return formatPercentage(15)
- if (isTinlakePool && trancheText === 'senior') return formatPercentage(trancheToken.apy)
- if (daysSinceCreation < 30 || !trancheToken.yield30DaysAnnualized) return 'N/A'
- return formatPercentage(trancheToken.yield30DaysAnnualized)
- }
+ const dataTable = useMemo(() => {
+ return trancheTokens.map((tranche) => ({
+ value: [`${tranche.name} ${getTrancheText(tranche)}`, tranche, tranche.valueLocked, tranche.tokenPrice],
+ }))
+ }, [trancheTokens])
return (
-
-
-
- {trancheToken.name} ({trancheToken.symbol})
-
-
-
-
-
-
- {calculateApy()}
-
-
- {isMedium && (
-
-
- {formatPercentage(trancheToken.protection)}
-
- )}
-
-
-
- Token price
-
-
- {formatBalance(trancheToken.tokenPrice || 0, poolCurrencySymbol, 5, 5)}
-
-
-
- {formatBalance(trancheToken.valueLocked, poolCurrencySymbol)}
-
-
-
-
-
-
+
+
+
+
+
)
}
diff --git a/centrifuge-app/src/components/PoolOverview/TransactionHistory.tsx b/centrifuge-app/src/components/PoolOverview/TransactionHistory.tsx
index 666a1bd8dd..4265488617 100644
--- a/centrifuge-app/src/components/PoolOverview/TransactionHistory.tsx
+++ b/centrifuge-app/src/components/PoolOverview/TransactionHistory.tsx
@@ -1,5 +1,5 @@
import { AssetTransaction, CurrencyBalance } from '@centrifuge/centrifuge-js'
-import { AnchorButton, IconDownload, IconExternalLink, Shelf, Stack, StatusChip, Text } from '@centrifuge/fabric'
+import { AnchorButton, IconDownload, IconExternalLink, Shelf, Stack, Text } from '@centrifuge/fabric'
import BN from 'bn.js'
import { formatDate } from '../../utils/date'
import { formatBalance } from '../../utils/formatting'
@@ -202,7 +202,6 @@ export const TransactionHistoryTable = ({
return {
activeAssetId,
netFlow,
- type: label,
transactionDate: transaction.timestamp,
assetId: transaction.asset.id,
assetName: transaction.asset.name,
@@ -216,11 +215,6 @@ export const TransactionHistoryTable = ({
}) || []
const columns = [
- {
- align: 'left',
- header: 'Type',
- cell: ({ type }: Row) => {type},
- },
{
align: 'left',
header: ,
@@ -233,7 +227,7 @@ export const TransactionHistoryTable = ({
},
{
align: 'left',
- header: 'Asset',
+ header: ,
cell: ({ activeAssetId, assetId, assetName, fromAssetId, fromAssetName, toAssetId, toAssetName }: Row) => {
const base = `${basePath}/${poolId}/assets/`
return fromAssetId && toAssetId && activeAssetId === fromAssetId.split('-')[1] ? (
@@ -263,6 +257,7 @@ export const TransactionHistoryTable = ({
)
},
+ sortKey: 'transaction',
},
{
align: 'right',
@@ -299,25 +294,28 @@ export const TransactionHistoryTable = ({
Transaction history
- {transactions?.length && (
-
- Download
-
- )}
+
+ {transactions?.length! > 8 && preview && (
+
+ View all
+
+ )}
+ {transactions?.length && (
+
+ Download
+
+ )}
+
- {transactions?.length! > 8 && preview && (
-
- View all
-
- )}
)
}
diff --git a/centrifuge-app/src/components/Tooltips.tsx b/centrifuge-app/src/components/Tooltips.tsx
index 96ced10a00..e73ea81d6c 100644
--- a/centrifuge-app/src/components/Tooltips.tsx
+++ b/centrifuge-app/src/components/Tooltips.tsx
@@ -338,6 +338,10 @@ export const tooltipText = {
label: 'Target APY',
body: 'The target APY for the tranche.',
},
+ expenseRatio: {
+ label: 'Expense Ratio',
+ body: 'The operating expenses of the fund as a percentage of the total NAV',
+ },
}
export type TooltipsProps = {
@@ -345,15 +349,28 @@ export type TooltipsProps = {
variant?: 'primary' | 'secondary'
label?: string | React.ReactNode
props?: any
+ size?: 'med' | 'sm'
} & Partial>
-export function Tooltips({ type, label: labelOverride, variant = 'primary', props, ...textProps }: TooltipsProps) {
+export function Tooltips({
+ type,
+ label: labelOverride,
+ variant = 'primary',
+ size = 'sm',
+ props,
+ ...textProps
+}: TooltipsProps) {
const { label, body } = type ? tooltipText[type] : { label: labelOverride, body: textProps.body }
const isPrimary = variant === 'primary'
return (
{typeof label === 'string' ? (
-
+
{labelOverride || label}
) : (
diff --git a/centrifuge-app/src/pages/IssuerCreatePool/CustomCategories.tsx b/centrifuge-app/src/pages/IssuerCreatePool/CustomCategories.tsx
new file mode 100644
index 0000000000..dc170f114b
--- /dev/null
+++ b/centrifuge-app/src/pages/IssuerCreatePool/CustomCategories.tsx
@@ -0,0 +1,132 @@
+import { PoolMetadataInput } from '@centrifuge/centrifuge-js/dist/modules/pools'
+import { Box, Button, NumberInput, Select, Shelf, Stack, Text, TextInput } from '@centrifuge/fabric'
+import { Field, FieldArray, FieldProps, useFormikContext } from 'formik'
+
+const OPTIONS = [
+ { label: 'Fund admin', value: 'fundAdmin' },
+ { label: 'Trustee', value: 'trustee' },
+ { label: 'Pricing oracle provider', value: 'pricingOracleProvider' },
+ { label: 'Auditor', value: 'auditor' },
+ { label: 'Custodian', value: 'custodian' },
+ { label: 'Investment manager', value: 'Investment manager' },
+ { label: 'Sub-advisor', value: 'subadvisor' },
+ { label: 'Historical default rate', value: 'historicalDefaultRate' },
+ { label: 'Other', value: 'other' },
+]
+
+const createCategory = () => ({
+ type: 'fundAdmin',
+ value: '',
+})
+
+export type IssuerDetail = {
+ type: string
+ value: string
+ customType?: string
+}
+
+export function CustomCategories() {
+ const fmk = useFormikContext()
+ const { values } = fmk
+
+ console.log('FORM', values)
+
+ return (
+
+ {(fldArr) => (
+
+
+
+ Issuer profile categories
+
+ Add additional information
+
+
+
+
+
+ {!!values?.issuerCategories?.length &&
+ values.issuerCategories.map(
+ (category: { type: string; value: string | number; customType?: string }, index: number) => {
+ return (
+
+ {({ field, meta }: FieldProps) => {
+ return (
+
+
+
+ {category.type === 'other' && (
+
+ ) => {
+ fmk.setFieldValue(`issuerCategories.${index}.customType`, event.target.value)
+ }}
+ />
+
+ )}
+
+ ) => {
+ fmk.setFieldValue(`issuerCategories.${index}.value`, event.target.value)
+ }}
+ onBlur={field.onBlur}
+ value={category.value}
+ label="Value"
+ />
+
+
+
+
+
+ )
+ }}
+
+ )
+ }
+ )}
+
+ )}
+
+ )
+}
diff --git a/centrifuge-app/src/pages/IssuerCreatePool/IssuerInput.tsx b/centrifuge-app/src/pages/IssuerCreatePool/IssuerInput.tsx
index 456a24e85e..69fbb682e1 100644
--- a/centrifuge-app/src/pages/IssuerCreatePool/IssuerInput.tsx
+++ b/centrifuge-app/src/pages/IssuerCreatePool/IssuerInput.tsx
@@ -3,6 +3,7 @@ import { Field, FieldProps } from 'formik'
import { FieldWithErrorMessage } from '../../components/FieldWithErrorMessage'
import { Tooltips } from '../../components/Tooltips'
import { isTestEnv } from '../../config'
+import { CustomCategories } from './CustomCategories'
import { CustomDetails } from './CustomDetails'
import { validate } from './validate'
@@ -132,6 +133,9 @@ export function IssuerInput({ waitingForStoredIssuer = false }: Props) {
+
+
+
)
}
diff --git a/centrifuge-app/src/pages/IssuerCreatePool/index.tsx b/centrifuge-app/src/pages/IssuerCreatePool/index.tsx
index 1d896377eb..33eddcef88 100644
--- a/centrifuge-app/src/pages/IssuerCreatePool/index.tsx
+++ b/centrifuge-app/src/pages/IssuerCreatePool/index.tsx
@@ -114,6 +114,7 @@ export type CreatePoolValues = Omit<
poolType: 'open' | 'closed'
investorType: string
issuerShortDescription: string
+ issuerCategories: { type: string; value: string }[]
ratingAgency: string
ratingValue: string
ratingReportUrl: string
@@ -137,6 +138,7 @@ const initialValues: CreatePoolValues = {
issuerLogo: null,
issuerDescription: '',
issuerShortDescription: '',
+ issuerCategories: [],
executiveSummary: null,
website: '',
diff --git a/centrifuge-app/src/pages/IssuerPool/Configuration/Issuer.tsx b/centrifuge-app/src/pages/IssuerPool/Configuration/Issuer.tsx
index 5f04058d27..e7edcf09d0 100644
--- a/centrifuge-app/src/pages/IssuerPool/Configuration/Issuer.tsx
+++ b/centrifuge-app/src/pages/IssuerPool/Configuration/Issuer.tsx
@@ -6,7 +6,7 @@ import * as React from 'react'
import { useParams } from 'react-router'
import { lastValueFrom } from 'rxjs'
import { ButtonGroup } from '../../../components/ButtonGroup'
-import { IssuerDetails, RatingDetails, ReportDetails } from '../../../components/IssuerSection'
+import { IssuerDetails, PoolAnalysis, RatingDetails } from '../../../components/IssuerSection'
import { PageSection } from '../../../components/PageSection'
import { getFileDataURI } from '../../../utils/getFileDataURI'
import { useFile } from '../../../utils/useFile'
@@ -25,6 +25,7 @@ type Values = Pick<
| 'issuerLogo'
| 'issuerDescription'
| 'issuerShortDescription'
+ | 'issuerCategories'
| 'executiveSummary'
| 'website'
| 'forum'
@@ -60,6 +61,7 @@ export function Issuer() {
issuerLogo: logoFile ?? null,
issuerDescription: metadata?.pool?.issuer?.description ?? '',
issuerShortDescription: metadata?.pool?.issuer?.shortDescription ?? '',
+ issuerCategories: metadata?.pool?.issuer?.categories ?? [],
executiveSummary: metadata?.pool?.links?.executiveSummary ? 'executiveSummary.pdf' : ('' as any),
website: metadata?.pool?.links?.website ?? '',
forum: metadata?.pool?.links?.forum ?? '',
@@ -120,6 +122,7 @@ export function Issuer() {
logo:
logoChanged && logoUri ? { uri: logoUri, mime: values.issuerLogo!.type } : oldMetadata.pool.issuer.logo,
shortDescription: values.issuerShortDescription,
+ categories: values.issuerCategories,
},
links: {
executiveSummary: execSummaryUri
@@ -216,7 +219,7 @@ export function Issuer() {
) : (
- {metadata?.pool?.reports?.[0] && }
+ {metadata?.pool?.reports?.[0] && }
{metadata?.pool?.rating && }
)}
diff --git a/centrifuge-app/src/pages/Pool/Header.tsx b/centrifuge-app/src/pages/Pool/Header.tsx
index 8c6eba054d..7a1b5bd9fe 100644
--- a/centrifuge-app/src/pages/Pool/Header.tsx
+++ b/centrifuge-app/src/pages/Pool/Header.tsx
@@ -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'
@@ -30,14 +29,11 @@ export function PoolDetailHeader({ actions }: Props) {
return (
{metadata?.pool?.name ?? 'Unnamed pool'}}
- subtitle={
- by {metadata?.pool?.issuer.name ?? 'Unknown'}
- }
parent={{ to: `/pools${state?.token ? '/tokens' : ''}`, label: state?.token ? 'Tokens' : 'Pools' }}
icon={
-
+ <>
{metadata?.pool?.icon ? (
-
+
) : (
{(isLoading ? '' : metadata?.pool?.name ?? 'U')[0]}
)}
-
+ >
}
border={false}
actions={actions}
diff --git a/centrifuge-app/src/pages/Pool/Overview/index.tsx b/centrifuge-app/src/pages/Pool/Overview/index.tsx
index 9ab15172a9..6a7b3fb4bd 100644
--- a/centrifuge-app/src/pages/Pool/Overview/index.tsx
+++ b/centrifuge-app/src/pages/Pool/Overview/index.tsx
@@ -1,5 +1,5 @@
-import { CurrencyBalance, Price, Rate } from '@centrifuge/centrifuge-js'
-import { Box, Button, Card, Grid, IconFileText, Stack, Text, TextWithPlaceholder } from '@centrifuge/fabric'
+import { CurrencyBalance, Price } from '@centrifuge/centrifuge-js'
+import { Box, Button, Card, Grid, TextWithPlaceholder } from '@centrifuge/fabric'
import Decimal from 'decimal.js-light'
import * as React from 'react'
import { useParams } from 'react-router'
@@ -9,10 +9,8 @@ import { InvestRedeemDrawer } from '../../../components/InvestRedeem/InvestRedee
import { IssuerDetails, ReportDetails } from '../../../components/IssuerSection'
import { LayoutSection } from '../../../components/LayoutBase/LayoutSection'
import { LoadBoundary } from '../../../components/LoadBoundary'
-import { Cashflows } from '../../../components/PoolOverview/Cashflows'
import { KeyMetrics } from '../../../components/PoolOverview/KeyMetrics'
import { PoolPerformance } from '../../../components/PoolOverview/PoolPerfomance'
-import { PoolStructure } from '../../../components/PoolOverview/PoolStructure'
import { TrancheTokenCards } from '../../../components/PoolOverview/TrancheTokenCards'
import { TransactionHistory } from '../../../components/PoolOverview/TransactionHistory'
import { Spinner } from '../../../components/Spinner'
@@ -23,8 +21,7 @@ import { getPoolValueLocked } from '../../../utils/getPoolValueLocked'
import { useAverageMaturity } from '../../../utils/useAverageMaturity'
import { useConnectBeforeAction } from '../../../utils/useConnectBeforeAction'
import { useIsAboveBreakpoint } from '../../../utils/useIsAboveBreakpoint'
-import { useLoans } from '../../../utils/useLoans'
-import { usePool, usePoolFees, usePoolMetadata } from '../../../utils/usePools'
+import { usePool, usePoolMetadata } from '../../../utils/usePools'
import { PoolDetailHeader } from '../Header'
export type Token = {
@@ -71,11 +68,7 @@ export function PoolDetailOverview() {
const isTinlakePool = poolId.startsWith('0x')
const pool = usePool(poolId)
- const poolFees = usePoolFees(poolId)
const { data: metadata, isLoading: metadataIsLoading } = usePoolMetadata(pool)
- const averageMaturity = useAverageMaturity(poolId)
- const loans = useLoans(poolId)
- const isMedium = useIsAboveBreakpoint('M')
const pageSummaryData = [
{
@@ -109,100 +102,43 @@ export function PoolDetailOverview() {
id: tranche.id,
capacity: tranche.capacity,
tokenPrice: tranche.tokenPrice,
- yield30DaysAnnualized: tranche?.yield30DaysAnnualized,
+ yield30DaysAnnualized: tranche?.yield30DaysAnnualized?.toString() || '',
}
})
.reverse()
return (
-
-
-
- }>
-
-
-
- {tokens.length > 0 && (
+
+
+
+
+ }>
+
+
+
+ {tokens.length > 0 && (
+ }>
+
+
+ )}
}>
-
-
- )}
- }>
- {metadata?.pool?.reports?.length || !isTinlakePool ? (
-
-
-
-
-
-
- Reports
-
-
-
-
-
- Issuer details
-
-
-
-
- ) : null}
- {isTinlakePool && (
-
-
- Issuer details
+
+
-
-
- )}
-
- {!isTinlakePool && (
- <>
-
- }>
- {
- return {
- fee: poolFees?.find((f) => f.id === fee.id)?.amounts.percentOfNav ?? Rate.fromFloat(0),
- name: fee.name,
- id: fee.id,
- }
- }) || []
- }
- />
-
- {/* }>
-
- */}
-
- {isMedium && (
- }>
-
-
+
+ {metadata?.pool?.reports?.length || !isTinlakePool ? (
+
+
-
- )}
+ ) : null}
+
+
+ {!isTinlakePool && (
}>
-
-
-
+
- >
- )}
+ )}
+
)
}
diff --git a/centrifuge-js/src/modules/pools.ts b/centrifuge-js/src/modules/pools.ts
index 2a07f549c4..7518ef8c2a 100644
--- a/centrifuge-js/src/modules/pools.ts
+++ b/centrifuge-js/src/modules/pools.ts
@@ -684,6 +684,7 @@ export interface PoolMetadataInput {
issuerLogo?: FileType | null
issuerDescription: string
issuerShortDescription: string
+ issuerCategories: { type: string; value: string; customType?: string }[]
poolReport?: {
authorName: string
@@ -748,6 +749,7 @@ export type PoolMetadata = {
email: string
logo?: FileType | null
shortDescription: string
+ categories: { type: string; value: string; customType?: string }[]
}
links: {
executiveSummary: FileType | null
@@ -1129,6 +1131,7 @@ export function getPoolsModule(inst: Centrifuge) {
email: metadata.email,
logo: metadata.issuerLogo,
shortDescription: metadata.issuerShortDescription,
+ categories: metadata.issuerCategories,
},
poolStructure: metadata.poolStructure,
investorType: metadata.investorType,
@@ -2469,13 +2472,19 @@ export function getPoolsModule(inst: Centrifuge) {
}),
takeLast(1),
map(({ trancheSnapshots }) => {
- const trancheStates: Record = {}
+ const trancheStates: Record<
+ string,
+ { timestamp: string; tokenPrice: Price; yield30DaysAnnualized: Perquintill }[]
+ > = {}
trancheSnapshots?.forEach((state) => {
const tid = state.tranche.trancheId
const entry = {
timestamp: state.timestamp,
tokenPrice: new Price(state.tokenPrice),
pool: state.tranche.poolId,
+ yield30DaysAnnualized: state.yield30DaysAnnualized
+ ? new Perquintill(state.yield30DaysAnnualized)
+ : new Perquintill(0),
}
if (trancheStates[tid]) {
trancheStates[tid].push(entry)
@@ -2665,26 +2674,26 @@ export function getPoolsModule(inst: Centrifuge) {
poolCurrency.decimals
),
yield7DaysAnnualized: tranche.yield7DaysAnnualized
- ? new Perquintill(hexToBN(tranche.yield7DaysAnnualized))
+ ? new Perquintill(tranche.yield7DaysAnnualized)
: new Perquintill(0),
yield30DaysAnnualized: tranche.yield30DaysAnnualized
- ? new Perquintill(hexToBN(tranche.yield30DaysAnnualized))
+ ? new Perquintill(tranche.yield30DaysAnnualized)
: new Perquintill(0),
yield90DaysAnnualized: tranche.yield90DaysAnnualized
- ? new Perquintill(hexToBN(tranche.yield90DaysAnnualized))
+ ? new Perquintill(tranche.yield90DaysAnnualized)
: new Perquintill(0),
yieldSinceInception: tranche.yieldSinceInception
- ? new Perquintill(hexToBN(tranche.yieldSinceInception))
+ ? new Perquintill(tranche.yieldSinceInception)
: new Perquintill(0),
- yieldMTD: tranche.yieldMTD ? new Perquintill(hexToBN(tranche.yieldMTD)) : new Perquintill(0),
- yieldQTD: tranche.yieldQTD ? new Perquintill(hexToBN(tranche.yieldQTD)) : new Perquintill(0),
- yieldYTD: tranche.yieldYTD ? new Perquintill(hexToBN(tranche.yieldYTD)) : new Perquintill(0),
+ yieldMTD: tranche.yieldMTD ? new Perquintill(tranche.yieldMTD) : new Perquintill(0),
+ yieldQTD: tranche.yieldQTD ? new Perquintill(tranche.yieldQTD) : new Perquintill(0),
+ yieldYTD: tranche.yieldYTD ? new Perquintill(tranche.yieldYTD) : new Perquintill(0),
yieldSinceLastPeriod: tranche.yieldSinceLastPeriod
- ? new Perquintill(hexToBN(tranche.yieldSinceLastPeriod))
+ ? new Perquintill(tranche.yieldSinceLastPeriod)
: new Perquintill(0),
}
})
-
+ console.log('🚀 ~ tranches:', tranches)
return { ...state, poolState, poolValue, tranches }
}) || [],
trancheStates,
@@ -4629,7 +4638,7 @@ export function getPoolsModule(inst: Centrifuge) {
}
}
-function hexToBN(value?: string | number | null) {
+export function hexToBN(value?: string | number | null) {
if (typeof value === 'number' || value == null) return new BN(value ?? 0)
return new BN(value.toString().substring(2), 'hex')
}
diff --git a/fabric/src/components/Select/index.tsx b/fabric/src/components/Select/index.tsx
index 38fa43e539..e6e1eb8afa 100644
--- a/fabric/src/components/Select/index.tsx
+++ b/fabric/src/components/Select/index.tsx
@@ -17,6 +17,7 @@ export type SelectProps = React.SelectHTMLAttributes & {
placeholder?: string
errorMessage?: string
small?: boolean
+ hideBorder?: boolean
}
const StyledSelect = styled.select`
@@ -31,14 +32,11 @@ const StyledSelect = styled.select`
cursor: pointer;
line-height: inherit;
text-overflow: ellipsis;
+ font-weight: 500;
&:disabled {
cursor: default;
}
-
- &:focus {
- color: ${({ theme }) => theme.colors.textSelected};
- }
`
export function SelectInner({
@@ -79,7 +77,7 @@ export function SelectInner({
)
}
-export function Select({ label, errorMessage, id, ...rest }: SelectProps) {
+export function Select({ label, errorMessage, id, hideBorder, ...rest }: SelectProps) {
const defaultId = React.useId()
id ??= defaultId
return (
@@ -89,7 +87,7 @@ export function Select({ label, errorMessage, id, ...rest }: SelectProps) {
disabled={rest.disabled}
errorMessage={errorMessage}
inputElement={
-
+
}
diff --git a/fabric/src/components/Tabs/index.tsx b/fabric/src/components/Tabs/index.tsx
index 0e7a40e828..1d5ff62d13 100644
--- a/fabric/src/components/Tabs/index.tsx
+++ b/fabric/src/components/Tabs/index.tsx
@@ -29,7 +29,7 @@ export function Tabs({ selectedIndex, onChange, children }: TabsProps) {
)
}
-const StyledTabsItem = styled.button<{ $active?: boolean }>(
+const StyledTabsItem = styled.button<{ $active?: boolean; styleOverrides?: React.CSSProperties, showBorder?: boolean }>(
{
display: 'flex',
alignItems: 'center',
@@ -43,34 +43,56 @@ const StyledTabsItem = styled.button<{ $active?: boolean }>(
appearance: 'none',
background: 'transparent',
},
- ({ $active, theme }) => {
+ ({ $active, theme, styleOverrides, showBorder }) => {
return css({
paddingTop: 1,
paddingLeft: 2,
paddingRight: 2,
paddingBottom: 2,
- color: 'textPrimary',
- boxShadow: $active ? `inset 0 -2px 0 ${theme.colors.textGold}` : 'none',
+ color: $active ? 'textPrimary' : 'textSecondary',
+ boxShadow: $active ? `inset 0 -2px 0 ${theme.colors.textGold}` : showBorder ? `inset 0 -2px 0 ${theme.colors.textDisabled}` : 'none',
+ fontWeight: 400,
'&:hover, &:active, &:focus-visible': {
color: 'textGold',
},
+ ...styleOverrides,
})
}
)
-export type TabsItemProps = Omit, '$active' | 'ariaLabel'>
-
+export type TabsItemProps = Omit, '$active' | 'ariaLabel'> & {
+ styleOverrides?: React.CSSProperties
+ showBorder?: boolean
+}
type TabsItemPrivateProps = TabsItemProps & {
active?: boolean
onClick?: () => void
ariaLabel?: string
+ styleOverrides?: React.CSSProperties
+ showBorder?: boolean
}
-export function TabsItem({ children, active, onClick, ariaLabel, ...rest }: TabsItemPrivateProps) {
+export function TabsItem({
+ children,
+ active,
+ onClick,
+ ariaLabel,
+ styleOverrides,
+ showBorder,
+ ...rest
+}: TabsItemPrivateProps) {
return (
-
-
+
+
{children}
diff --git a/fabric/src/components/TextInput/index.tsx b/fabric/src/components/TextInput/index.tsx
index e48312d350..5f5775b17a 100644
--- a/fabric/src/components/TextInput/index.tsx
+++ b/fabric/src/components/TextInput/index.tsx
@@ -49,12 +49,11 @@ export const StyledTextInput = styled.input`
margin: 0;
}
`
-
-export const StyledInputBox = styled(Shelf)`
+export const StyledInputBox = styled(Shelf)<{ hideBorder?: boolean }>`
width: 100%;
position: relative;
background: ${({ theme }) => theme.colors.backgroundPage};
- border: 1px solid ${({ theme }) => theme.colors.borderPrimary};
+ border: ${({ hideBorder, theme }) => (hideBorder ? 'none' : `1px solid ${theme.colors.borderPrimary}`)};
border-radius: ${({ theme }) => theme.radii.input}px;
&::before {
diff --git a/fabric/src/components/Tooltip/index.tsx b/fabric/src/components/Tooltip/index.tsx
index 9eb97c49a5..ae0b10090f 100644
--- a/fabric/src/components/Tooltip/index.tsx
+++ b/fabric/src/components/Tooltip/index.tsx
@@ -57,6 +57,7 @@ const placements: {
}
const Container = styled(Stack)<{ pointer: PlacementAxis }>`
+ background-color: ${({ theme }) => theme.colors.backgroundInverted};
filter: ${({ theme }) => `drop-shadow(${theme.shadows.cardInteractive})`};
&::before {
@@ -65,7 +66,7 @@ const Container = styled(Stack)<{ pointer: PlacementAxis }>`
content: '';
position: absolute;
${({ pointer }) => placements[pointer!]}
- border: ${({ theme }) => `var(--size) solid ${theme.colors.backgroundPrimary}`};
+ border: ${({ theme }) => `var(--size) solid ${theme.colors.backgroundInverted}`};
transform: rotate(-45deg);
}
`
@@ -117,7 +118,9 @@ export function Tooltip({
{title}
)}
- {body}
+
+ {body}
+
)}
/>
diff --git a/fabric/src/icon-svg/IconBalanceSheet.svg b/fabric/src/icon-svg/IconBalanceSheet.svg
new file mode 100644
index 0000000000..89a2407fc4
--- /dev/null
+++ b/fabric/src/icon-svg/IconBalanceSheet.svg
@@ -0,0 +1,3 @@
+
diff --git a/fabric/src/icon-svg/IconCashflow.svg b/fabric/src/icon-svg/IconCashflow.svg
new file mode 100644
index 0000000000..798a162bc8
--- /dev/null
+++ b/fabric/src/icon-svg/IconCashflow.svg
@@ -0,0 +1,3 @@
+
diff --git a/fabric/src/icon-svg/IconMoody.svg b/fabric/src/icon-svg/IconMoody.svg
new file mode 100644
index 0000000000..2499a357a6
--- /dev/null
+++ b/fabric/src/icon-svg/IconMoody.svg
@@ -0,0 +1,9 @@
+
diff --git a/fabric/src/icon-svg/IconProfitAndLoss.svg b/fabric/src/icon-svg/IconProfitAndLoss.svg
new file mode 100644
index 0000000000..5223d3810d
--- /dev/null
+++ b/fabric/src/icon-svg/IconProfitAndLoss.svg
@@ -0,0 +1,3 @@
+
diff --git a/fabric/src/icon-svg/IconSp.svg b/fabric/src/icon-svg/IconSp.svg
new file mode 100644
index 0000000000..fd64483382
--- /dev/null
+++ b/fabric/src/icon-svg/IconSp.svg
@@ -0,0 +1,10 @@
+
diff --git a/fabric/src/icon-svg/icon-arrow-right-white.svg b/fabric/src/icon-svg/icon-arrow-right-white.svg
new file mode 100644
index 0000000000..d56f3c1f14
--- /dev/null
+++ b/fabric/src/icon-svg/icon-arrow-right-white.svg
@@ -0,0 +1,10 @@
+
diff --git a/fabric/src/theme/tokens/theme.ts b/fabric/src/theme/tokens/theme.ts
index 87824b8bee..4062c1b4bd 100644
--- a/fabric/src/theme/tokens/theme.ts
+++ b/fabric/src/theme/tokens/theme.ts
@@ -1,4 +1,4 @@
-import { black, blackScale, blueScale, centrifugeBlue, gold, grayScale, yellowScale } from './colors'
+import { black, blackScale, blueScale, gold, grayScale, yellowScale } from './colors'
const statusDefault = grayScale[800]
const statusInfo = blueScale[500]
@@ -79,18 +79,18 @@ const colors = {
borderButtonSecondaryHover: gold,
borderButtonSecondaryPressed: gold,
borderButtonSecondaryDisabled: 'transparent',
- shadowButtonSecondary: '#A8BFFD35',
+ shadowButtonSecondary: 'transparent',
backgroundButtonTertiary: 'transparent',
backgroundButtonTertiaryFocus: 'transparent',
backgroundButtonTertiaryHover: 'tranparent',
backgroundButtonTertiaryPressed: 'transparent',
backgroundButtonTertiaryDisabled: 'transparent',
- textButtonTertiary: centrifugeBlue,
- textButtonTertiaryFocus: centrifugeBlue,
- textButtonTertiaryHover: grayScale[800],
- textButtonTertiaryPressed: centrifugeBlue,
- textButtonTertiaryDisabled: grayScale[500],
+ textButtonTertiary: grayScale[800],
+ textButtonTertiaryFocus: gold,
+ textButtonTertiaryHover: gold,
+ textButtonTertiaryPressed: gold,
+ textButtonTertiaryDisabled: gold,
borderButtonTertiary: 'transparent',
borderButtonTertiaryFocus: 'transparent',
borderButtonTertiaryHover: 'transparent',
@@ -102,17 +102,17 @@ const colors = {
backgroundButtonInvertedHover: grayScale[100],
backgroundButtonInvertedPressed: grayScale[100],
backgroundButtonInvertedDisabled: grayScale[100],
- textButtonInverted: centrifugeBlue,
- textButtonInvertedFocus: centrifugeBlue,
- textButtonInvertedHover: centrifugeBlue,
- textButtonInvertedPressed: centrifugeBlue,
+ textButtonInverted: black,
+ textButtonInvertedFocus: black,
+ textButtonInvertedHover: black,
+ textButtonInvertedPressed: black,
textButtonInvertedDisabled: grayScale[500],
borderButtonInverted: grayScale[100],
- borderButtonInvertedFocus: centrifugeBlue,
- borderButtonInvertedHover: centrifugeBlue,
- borderButtonInvertedPressed: centrifugeBlue,
+ borderButtonInvertedFocus: black,
+ borderButtonInvertedHover: black,
+ borderButtonInvertedPressed: black,
borderButtonInvertedDisabled: 'transparent',
- shadowButtonInverted: '#E0E7FF',
+ shadowButtonInverted: 'transparent',
}
export const colorTheme = {