diff --git a/centrifuge-app/src/components/Charts/PoolPerformanceChart.tsx b/centrifuge-app/src/components/Charts/PoolPerformanceChart.tsx index 2e3a912f90..333ef7ea79 100644 --- a/centrifuge-app/src/components/Charts/PoolPerformanceChart.tsx +++ b/centrifuge-app/src/components/Charts/PoolPerformanceChart.tsx @@ -22,7 +22,6 @@ type ChartData = { currency?: string seniorAPY: number | null | undefined juniorAPY: number | null - isToday: boolean } type GraphDataItemWithType = { @@ -115,7 +114,7 @@ function PoolPerformanceChart() { return true }) - const [range, setRange] = React.useState<(typeof rangeFilters)[number]>({ value: 'all', label: 'All' }) + const [range, setRange] = React.useState<(typeof rangeFilters)[number]>(rangeFilters[0]) const rangeNumber = getRangeNumber(range.value, poolAge) ?? 100 // querying chain for more accurate data, since data for today from subquery is not necessarily up to date @@ -124,6 +123,16 @@ function PoolPerformanceChart() { ? formatBalance(pool?.tranches[pool.tranches.length - 1].tokenPrice || 0, undefined, 5, 5) : null + const todayJuniorApy = pool?.tranches + ?.find((pool) => pool.seniority === 0) + ?.yield30DaysAnnualized?.toPercent() + .toNumber() + + const todaySeniorApy = pool?.tranches + ?.find((pool) => pool.seniority === 1) + ?.yield30DaysAnnualized?.toPercent() + .toNumber() + const trancheTodayPrice = calculateTranchePrices(pool as Pool) const data: ChartData[] = React.useMemo( @@ -151,9 +160,8 @@ function PoolPerformanceChart() { nav: todayAssetValue, juniorTokenPrice: tranchePrices.juniorTokenPrice ?? 0, seniorTokenPrice: tranchePrices.seniorTokenPrice ?? null, - juniorAPY: formattedJuniorAPY, - seniorAPY: formattedSeniorAPY, - isToday: true, + juniorAPY: pool.id === '1655476167' ? 15 : todayJuniorApy, + seniorAPY: todaySeniorApy, } } @@ -164,20 +172,17 @@ function PoolPerformanceChart() { seniorTokenPrice: seniorTokenPrice !== 0 ? seniorTokenPrice : null, juniorAPY: formattedJuniorAPY, seniorAPY: formattedSeniorAPY, - isToday: false, } }) || [], - [truncatedPoolStates, todayAssetValue, pool, range] + [truncatedPoolStates, todayAssetValue, pool, range, todayJuniorApy, todaySeniorApy] ) - const todayData = data.find((day) => day.isToday) - const today = { nav: todayAssetValue, price: todayPrice, currency: pool.currency.symbol, - juniorAPY: todayData?.juniorAPY, - seniorAPY: todayData?.seniorAPY, + juniorAPY: pool.id === '1655476167' ? 15 : todayJuniorApy, + seniorAPY: todaySeniorApy, ...trancheTodayPrice, } @@ -217,10 +222,10 @@ function PoolPerformanceChart() { Pool performance setSelectedTabIndex(index)}> - + Price - + APY @@ -258,7 +263,7 @@ function PoolPerformanceChart() { formatBalanceAbbreviated(tick, '', 0)} yAxisId="left" width={80} @@ -266,7 +271,7 @@ function PoolPerformanceChart() { formatBalanceAbbreviated(tick, '', 2)} yAxisId="right" orientation="right" @@ -421,7 +426,7 @@ function CustomLegend({ navData, { color: 'textGold', - label: 'Junior token price', + label: data.seniorTokenPrice ? 'Junior token price' : 'Token price', value: formatBalance(data.juniorTokenPrice ?? 0, '', 3), type: 'singleTrancheTokenPrice', show: true, @@ -439,7 +444,7 @@ function CustomLegend({ navData, { color: 'textGold', - label: 'Junior APY', + label: data.seniorAPY ? 'Junior APY' : 'APY', value: formatPercentage(data.juniorAPY ?? 0), show: !!data.juniorAPY, }, @@ -470,13 +475,15 @@ function CustomLegend({ } return ( - + {hasType(item) ? ( - - ) : ( + + + ) : ( + {item.label} )} @@ -507,7 +514,7 @@ export const CustomTick = ({ x, y, payload }: CustomTickProps) => { return ( )} - } /> + } /> { +export const SimpleBarChart = ({ currency, data, groupBy }: SimpleBarChartProps) => { const theme = useTheme() + const isSmallerBar = groupBy === 'daily' || false const getOneDayPerMonth = () => { const seenMonths = new Set() @@ -56,7 +58,7 @@ export const SimpleBarChart = ({ currency, data }: SimpleBarChartProps) => { type="category" dataKey="name" ticks={getOneDayPerMonth()} - tick={} + tick={(props) => } angle={45} /> { fill={theme.colors.backgroundTertiary} strokeWidth={0} fillOpacity={1} - maxBarSize={20} + barSize={isSmallerBar ? 20 : 80} /> diff --git a/centrifuge-app/src/components/Charts/Tooltip.tsx b/centrifuge-app/src/components/Charts/Tooltip.tsx index fb179d23bb..bde12533b2 100644 --- a/centrifuge-app/src/components/Charts/Tooltip.tsx +++ b/centrifuge-app/src/components/Charts/Tooltip.tsx @@ -4,9 +4,9 @@ import { TooltipProps } from 'recharts' import { formatDate } from '../../utils/date' import { formatBalance, formatPercentage } from '../../utils/formatting' -type CustomizedTooltipProps = TooltipProps & { currency: string; precision?: number } +type CustomizedTooltipProps = TooltipProps & { currency: string; precision?: number; isRate?: boolean } -export function CustomizedTooltip({ payload, currency, precision }: CustomizedTooltipProps) { +export function CustomizedTooltip({ payload, currency, precision, isRate }: CustomizedTooltipProps) { if (payload && payload?.length > 0) { return ( @@ -15,7 +15,7 @@ export function CustomizedTooltip({ payload, currency, precision }: CustomizedTo {typeof value !== 'number' ? formatBalance(value[1] - value[0], currency, precision) - : unit === 'percent' + : unit === 'percent' || isRate ? formatPercentage(value) : formatBalance(value, currency, precision)} diff --git a/centrifuge-app/src/components/DataTable.tsx b/centrifuge-app/src/components/DataTable.tsx index c852234ddd..73fc0ac58e 100644 --- a/centrifuge-app/src/components/DataTable.tsx +++ b/centrifuge-app/src/components/DataTable.tsx @@ -42,6 +42,7 @@ export type DataTableProps = { defaultSortKey?: string defaultSortOrder?: OrderBy hoverable?: boolean + scrollable?: boolean /** * summary row is not included in sorting */ @@ -99,6 +100,7 @@ export const DataTable = >({ pageSize = Infinity, page = 1, headerStyles, + scrollable = false, }: DataTableProps) => { const [orderBy, setOrderBy] = React.useState>( defaultSortKey ? { [defaultSortKey]: defaultSortOrder } : {} @@ -123,9 +125,9 @@ export const DataTable = >({ const templateColumns = `[start] ${columns.map((col) => col.width ?? 'minmax(min-content, 1fr)').join(' ')} [end]` return ( - + {showHeader && ( - + {columns.map((col, i) => ( @@ -200,7 +202,15 @@ export const DataTable = >({ ) } -const TableGrid = styled(Grid)`` +const TableGrid = styled(Grid)<{ scrollable?: boolean }>` + ${({ scrollable }) => + scrollable && + css({ + maxHeight: 'calc(100vh - 180px)', + overflowY: 'auto', + overflowX: 'auto', + })} +` const Row = styled('div')` display: grid; @@ -209,12 +219,17 @@ 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)<{ styles?: any }>(({ styles }) => +const HeaderRow = styled(Row)<{ styles?: any; scrollable?: boolean }>(({ styles, scrollable }) => css({ backgroundColor: 'backgroundSecondary', borderStyle: 'solid', borderWidth: '1px 0', borderColor: 'borderPrimary', + position: scrollable ? 'sticky' : 'static', + top: scrollable ? 0 : 'auto', + zIndex: scrollable ? 10 : 'auto', + borderTopLeftRadius: '8px', + borderTopRightRadius: '8px', ...styles, }) ) @@ -242,6 +257,10 @@ export const DataRow = styled(Row)` '&:focus-visible': { boxShadow: 'inset 0 0 0 3px var(--fabric-focus)', }, + '&:last-child': { + borderBottomLeftRadius: '8px', + borderBottomRightRadius: '8px', + }, })} ` diff --git a/centrifuge-app/src/components/InvestRedeem/InvestRedeem.tsx b/centrifuge-app/src/components/InvestRedeem/InvestRedeem.tsx index d0c86a1b73..66ec5fc07c 100644 --- a/centrifuge-app/src/components/InvestRedeem/InvestRedeem.tsx +++ b/centrifuge-app/src/components/InvestRedeem/InvestRedeem.tsx @@ -1,3 +1,4 @@ +import { CurrencyBalance } from '@centrifuge/centrifuge-js' import { ConnectionGuard, useGetNetworkName, useWallet } from '@centrifuge/centrifuge-react' import { Network } from '@centrifuge/centrifuge-react/dist/components/WalletProvider/types' import { useGetExplorerUrl } from '@centrifuge/centrifuge-react/dist/components/WalletProvider/utils' @@ -25,6 +26,7 @@ import { usePool, usePoolMetadata } from '../../utils/usePools' import { LiquidityRewardsContainer } from '../LiquidityRewards/LiquidityRewardsContainer' import { LiquidityRewardsProvider } from '../LiquidityRewards/LiquidityRewardsProvider' import { LoadBoundary } from '../LoadBoundary' +import { PoolMetaDataPartial } from '../PoolList' import { Transactions } from '../Portfolio/Transactions' import { Spinner } from '../Spinner' import { AnchorTextLink } from '../TextLink' @@ -35,8 +37,18 @@ import { RedeemForm } from './RedeemForm' export type InvestRedeemProps = { poolId: string trancheId: string + metadata?: PoolMetaDataPartial } & InputProps +type HeaderProps = { + sumUnrealizedProfitAtMarketPrice?: CurrencyBalance + sumRealizedProfitFifoByPeriod?: CurrencyBalance +} & InputProps + +type InputProps = { + defaultView?: 'invest' | 'redeem' +} + // @ts-ignore const listFormatter = new Intl.ListFormat('en') @@ -71,7 +83,7 @@ export function InvestRedeem({ poolId, trancheId, ...rest }: InvestRedeemProps) > -
+
{!isTinlakePool && (connectedType === 'substrate' || isEvmOnSubstrate) && }