Skip to content

Commit

Permalink
Pool Overview redesign (#2435)
Browse files Browse the repository at this point in the history
* Pool overview redesign

* Fix types

* Fix linter warnings
  • Loading branch information
kattylucy authored Oct 9, 2024
1 parent 2a2ea42 commit 180f9ec
Show file tree
Hide file tree
Showing 64 changed files with 2,215 additions and 1,420 deletions.
456 changes: 343 additions & 113 deletions centrifuge-app/src/components/Charts/PoolPerformanceChart.tsx

Large diffs are not rendered by default.

66 changes: 42 additions & 24 deletions centrifuge-app/src/components/Charts/PriceChart.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Box, Select, Shelf, Stack, Text } from '@centrifuge/fabric'
import { Box, Select, Shelf, Stack, StatusChip, Text } from '@centrifuge/fabric'
import React from 'react'
import { Area, AreaChart, CartesianGrid, ResponsiveContainer, Tooltip, XAxis, YAxis } from 'recharts'
import { useTheme } from 'styled-components'
Expand All @@ -8,13 +8,14 @@ import { CustomizedTooltip } from './Tooltip'
export type FilterOptions = 'YTD' | '30days' | '90days'

type PriceChartProps = {
data: { day: Date; price: number }[]
data: { day: Date; price: number; apy: number }[]
currency: string
filter?: FilterOptions
setFilter?: React.Dispatch<React.SetStateAction<FilterOptions>>
isPrice: boolean
}

export const PriceChart = ({ data, currency, filter, setFilter }: PriceChartProps) => {
export const PriceChart = ({ data, currency, filter, setFilter, isPrice }: PriceChartProps) => {
const theme = useTheme()
const currentPrice = data.at(-1)?.price

Expand All @@ -34,10 +35,12 @@ export const PriceChart = ({ data, currency, filter, setFilter }: PriceChartProp
</Text>
)}
{priceDifference && (
<Text variant="body3" color={priceDifference.gte(0) ? 'statusOk' : 'statusCritical'}>
{' '}
{priceDifference.gte(0) ? '+' : ''} {priceDifference.mul(100).toFixed(2)}%
</Text>
<StatusChip status={priceDifference.gte(0) ? 'ok' : 'critical'}>
<Text variant="body3" color={priceDifference.gte(0) ? 'statusOk' : 'statusCritical'}>
{' '}
{priceDifference.gte(0) ? '+' : ''} {priceDifference.mul(100).toFixed(2)}%
</Text>
</StatusChip>
)}
</Shelf>
{filter && setFilter && (
Expand All @@ -50,16 +53,17 @@ export const PriceChart = ({ data, currency, filter, setFilter }: PriceChartProp
]}
onChange={(option) => setFilter(option.target.value as FilterOptions)}
defaultValue={filter}
hideBorder
/>
</Box>
)}
</Shelf>
<ResponsiveContainer width="100%" height="100%" minHeight="200px">
<AreaChart data={data || []} margin={{ top: 18, left: -10 }}>
<defs>
<linearGradient id="colorPrice" x1="0" y1="0" x2="0" y2="1">
<stop offset="5%" stopColor={'#626262'} stopOpacity={0.4} />
<stop offset="95%" stopColor={'#908f8f'} stopOpacity={0} />
<linearGradient id="colorPoolValue" x1="0" y1="0" x2="0" y2="1">
<stop offset="5%" stopColor={theme.colors.textGold} stopOpacity={0.4} />
<stop offset="95%" stopColor={theme.colors.textGold} stopOpacity={0.2} />
</linearGradient>
</defs>
<XAxis
Expand All @@ -81,28 +85,42 @@ export const PriceChart = ({ data, currency, filter, setFilter }: PriceChartProp
tickLine={false}
allowDuplicatedCategory={false}
/>
<YAxis
tickCount={6}
dataKey="price"
tickLine={false}
style={{ fontSize: '10px', fill: theme.colors.textSecondary, letterSpacing: '-0.5px' }}
tickFormatter={(tick: number) => {
return tick.toFixed(6)
}}
domain={['dataMin - 0.001', 'dataMax + 0.001']}
interval={'preserveStartEnd'}
/>
{isPrice ? (
<YAxis
tickCount={6}
dataKey="price"
tickLine={false}
style={{ fontSize: '10px', fill: theme.colors.textSecondary, letterSpacing: '-0.5px' }}
tickFormatter={(tick: number) => {
return tick.toFixed(6)
}}
domain={['dataMin - 0.001', 'dataMax + 0.001']}
interval="preserveStartEnd"
/>
) : (
<YAxis
tickCount={6}
dataKey="apy"
tickLine={false}
style={{ fontSize: '10px', fill: theme.colors.textSecondary, letterSpacing: '-0.5px' }}
tickFormatter={(tick: number) => {
return tick.toFixed(6)
}}
domain={['dataMin - 0.001', 'dataMax + 0.001']}
interval="preserveStartEnd"
/>
)}
<CartesianGrid stroke={theme.colors.borderPrimary} />
<Tooltip content={<CustomizedTooltip currency={currency} precision={6} />} />
<Area
type="monotone"
dataKey="price"
dataKey={isPrice ? 'price' : 'apy'}
strokeWidth={1}
fillOpacity={1}
fill="url(#colorPrice)"
name="Price"
name={isPrice ? 'Price' : 'APY'}
activeDot={{ fill: '#908f8f' }}
stroke="#908f8f"
stroke={theme.colors.textGold}
/>
</AreaChart>
</ResponsiveContainer>
Expand Down
101 changes: 101 additions & 0 deletions centrifuge-app/src/components/Charts/SimpleBarChart.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import { CurrencyBalance, CurrencyMetadata } from '@centrifuge/centrifuge-js'
import { Shelf, Text } from '@centrifuge/fabric'
import { Bar, BarChart, CartesianGrid, ReferenceLine, 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 (
<Shelf justifyContent="center">
<Text>No data available</Text>
</Shelf>
)

return (
<LoadBoundary>
<ResponsiveContainer width="100%" height={200}>
<BarChart width={500} height={300} data={data} barSize={10} barGap={10}>
<CartesianGrid stroke={theme.colors.borderPrimary} vertical={false} />
<XAxis
dy={4}
interval={0}
tickLine={false}
axisLine={false}
tickMargin={10}
type="category"
dataKey="name"
ticks={getOneDayPerMonth()}
tick={<CustomTick />}
angle={45}
/>
<YAxis
tickFormatter={(tick: number) => {
const balance = new CurrencyBalance(tick, currency?.decimals || 0)
return formatBalanceAbbreviated(balance, '', 0)
}}
tick={{ fontSize: 10, color: theme.colors.textPrimary }}
tickLine={false}
axisLine={false}
dataKey="yAxis"
/>
<ReferenceLine y={0} stroke={theme.colors.textSecondary} />

<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"
name="yAxis"
fill={theme.colors.backgroundTertiary}
strokeWidth={0}
fillOpacity={1}
maxBarSize={20}
/>
</BarChart>
</ResponsiveContainer>
</LoadBoundary>
)
}
6 changes: 3 additions & 3 deletions centrifuge-app/src/components/Charts/Tooltip.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,9 @@ export function TooltipContainer({ children }: { children: React.ReactNode }) {
bg="backgroundPage"
p={1}
style={{
boxShadow: '1px 3px 6px rgba(0, 0, 0, .15)',
boxShadow: '1px 3px 6px 0px rgba(0, 0, 0, 0.15)',
}}
minWidth="180px"
minWidth="250px"
gap="4px"
>
{children}
Expand All @@ -44,7 +44,7 @@ export function TooltipContainer({ children }: { children: React.ReactNode }) {

export function TooltipTitle({ children }: { children: React.ReactNode }) {
return (
<Text variant="label2" fontWeight="500">
<Text color="textPrimary" variant="label2" fontWeight="500">
{children}
</Text>
)
Expand Down
59 changes: 33 additions & 26 deletions centrifuge-app/src/components/DataTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ export type DataTableProps<T = any> = {
footer?: React.ReactNode
pageSize?: number
page?: number
headerStyles?: React.CSSProperties
} & GroupedProps

export type OrderBy = 'asc' | 'desc'
Expand All @@ -59,6 +60,7 @@ export type Column = {
align?: string
sortKey?: string
width?: string
isLabel?: boolean
}
const sorter = <T extends Record<string, any>>(data: Array<T>, order: OrderBy, sortKey?: string) => {
if (!sortKey) return data
Expand Down Expand Up @@ -96,6 +98,7 @@ export const DataTable = <T extends Record<string, any>>({
defaultSortOrder = 'desc',
pageSize = Infinity,
page = 1,
headerStyles,
}: DataTableProps<T>) => {
const [orderBy, setOrderBy] = React.useState<Record<string, OrderBy>>(
defaultSortKey ? { [defaultSortKey]: defaultSortOrder } : {}
Expand All @@ -122,7 +125,7 @@ export const DataTable = <T extends Record<string, any>>({
return (
<TableGrid gridTemplateColumns={templateColumns} gridAutoRows="auto" gap={0} rowGap={0}>
{showHeader && (
<HeaderRow>
<HeaderRow styles={headerStyles}>
{columns.map((col, i) => (
<HeaderCol key={i} align={col?.align}>
<Text variant="body3">
Expand All @@ -147,33 +150,36 @@ export const DataTable = <T extends Record<string, any>>({
tabIndex={onRowClicked ? 0 : undefined}
>
{columns.map((col, index) => (
<DataCol variant="body2" align={col?.align} key={index}>
{col.cell(row, i)}
</DataCol>
))}
</DataRow>
))}
{sortedAndPaginatedData?.map((row, i) => (
<DataRow
data-testId={`data-table-row-${i}-${groupIndex ?? 0}`}
hoverable={hoverable}
as={onRowClicked ? Link : 'div'}
to={onRowClicked ? onRowClicked(row) : undefined}
key={keyField ? row[keyField] : i}
tabIndex={onRowClicked ? 0 : undefined}
>
{columns.map((col, index) => (
<DataCol
data-testId={`data-table-col-${i}-${groupIndex ?? 0}-${col.header}`}
variant="body2"
align={col?.align}
key={index}
>
<DataCol variant="body2" align={col?.align} key={index} isLabel={col.isLabel}>
{col.cell(row, i)}
</DataCol>
))}
</DataRow>
))}
{sortedAndPaginatedData?.map((row, i) => {
return (
<DataRow
data-testId={`data-table-row-${i}-${groupIndex ?? 0}`}
hoverable={hoverable}
as={onRowClicked ? Link : 'div'}
to={onRowClicked ? onRowClicked(row) : undefined}
key={keyField ? row[keyField] : i}
tabIndex={onRowClicked ? 0 : undefined}
>
{columns.map((col, index) => (
<DataCol
data-testId={`data-table-col-${i}-${groupIndex ?? 0}-${col.header}`}
variant="body2"
align={col?.align}
key={index}
isLabel={col.isLabel}
>
{col.cell(row, i)}
</DataCol>
))}
</DataRow>
)
})}
{/* summary row is not included in sorting */}
{summary && (
<DataRow data-testId={`row-summary-${groupIndex ?? 0}`}>
Expand Down Expand Up @@ -203,12 +209,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)<any>(
const HeaderRow = styled(Row)<{ styles?: any }>(({ styles }) =>
css({
backgroundColor: 'backgroundSecondary',
borderStyle: 'solid',
borderWidth: '1px 0',
borderColor: 'borderPrimary',
...styles,
})
)

Expand Down Expand Up @@ -238,8 +245,8 @@ export const DataRow = styled(Row)<any>`
})}
`

export const DataCol = styled(Text)<{ align: Column['align'] }>`
background: initial;
export const DataCol = styled(Text)<{ align: Column['align']; isLabel?: boolean }>`
background: ${({ isLabel, theme }) => (isLabel ? theme.colors.backgroundSecondary : 'initial')};
border: none;
padding: 8px 16px;
display: flex;
Expand Down
1 change: 1 addition & 0 deletions centrifuge-app/src/components/InvestRedeem/InvestForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,7 @@ export function InvestForm({ autoFocus, investLabel = 'Invest' }: InvestFormProp
type="submit"
loading={isInvesting}
loadingMessage={loadingMessage}
variant="secondary"
disabled={
state.isPoolBusy || (state.poolCurrency?.symbol.toLowerCase().includes('lp') && hasPendingOrder)
}
Expand Down
Loading

0 comments on commit 180f9ec

Please sign in to comment.