From 2c6eee08cd548fe9103679bc0a275a531db32aca Mon Sep 17 00:00:00 2001 From: JustJousting Date: Wed, 8 Jan 2025 11:27:20 +0200 Subject: [PATCH 01/18] styling: fix input wallet balance font color --- .../PageCrvUsdStaking/components/InputComp/index.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/apps/loan/src/components/PageCrvUsdStaking/components/InputComp/index.tsx b/apps/loan/src/components/PageCrvUsdStaking/components/InputComp/index.tsx index 0ffa756d2..3a4c01c73 100644 --- a/apps/loan/src/components/PageCrvUsdStaking/components/InputComp/index.tsx +++ b/apps/loan/src/components/PageCrvUsdStaking/components/InputComp/index.tsx @@ -94,6 +94,7 @@ const InputLoaderWrapper = styled.div` const StyledIcon = styled(Icon)` margin-left: var(--spacing-1); + color: var(--page--text-color); opacity: 0.5; ` @@ -101,7 +102,9 @@ const WalletBalanceWrapper = styled(Box)` display: flex; flex-direction: row; align-items: center; + color: var(--page--text-color); gap: var(--spacing-1); + opacity: 0.7; ` const BalancesWrapper = styled(Box)` From e7819d0be8242837af424b0613cdd448110faf30 Mon Sep 17 00:00:00 2001 From: JustJousting Date: Thu, 9 Jan 2025 21:33:20 +0200 Subject: [PATCH 02/18] set up statistics component --- .../PageCrvUsdStaking/Statistics/index.tsx | 27 +++++++++++++++++++ .../components/PageCrvUsdStaking/index.tsx | 6 +++-- .../curve-ui-kit/src/themes/components.ts | 5 +++- .../src/themes/design/1_sizes_spaces.ts | 1 + .../src/themes/generate-themes.ts | 3 ++- 5 files changed, 38 insertions(+), 4 deletions(-) create mode 100644 apps/loan/src/components/PageCrvUsdStaking/Statistics/index.tsx diff --git a/apps/loan/src/components/PageCrvUsdStaking/Statistics/index.tsx b/apps/loan/src/components/PageCrvUsdStaking/Statistics/index.tsx new file mode 100644 index 000000000..3b9f7a7fa --- /dev/null +++ b/apps/loan/src/components/PageCrvUsdStaking/Statistics/index.tsx @@ -0,0 +1,27 @@ +import { SizesAndSpaces } from '@ui-kit/themes/design/1_sizes_spaces' +import { Card, CardHeader } from '@mui/material' +import Typography from '@mui/material/Typography' +import { useScrvUsdYield } from '@/entities/scrvusdYield' + +const { ColumnWidth, Spacing, MinWidth, MaxWidth } = SizesAndSpaces + +const Statistics = () => { + const { data, error, isFetching } = useScrvUsdYield({ timeFrame: '1d' }) + + return ( + t.design.Layer[1].Fill, + width: '100%', + maxWidth: MaxWidth.statistics, + }} + > + + + ) +} + +export default Statistics diff --git a/apps/loan/src/components/PageCrvUsdStaking/index.tsx b/apps/loan/src/components/PageCrvUsdStaking/index.tsx index 775c5351e..6de0db2d0 100644 --- a/apps/loan/src/components/PageCrvUsdStaking/index.tsx +++ b/apps/loan/src/components/PageCrvUsdStaking/index.tsx @@ -5,6 +5,7 @@ import BigNumber from 'bignumber.js' import useStore from '@/store/useStore' import StatsBanner from '@/components/PageCrvUsdStaking/StatsBanner' +import Statistics from '@/components/PageCrvUsdStaking/Statistics' import DepositWithdraw from '@/components/PageCrvUsdStaking/DepositWithdraw' import UserInformation from '@/components/PageCrvUsdStaking/UserInformation' import UserPositionBanner from '@/components/PageCrvUsdStaking/UserPositionBanner' @@ -56,11 +57,12 @@ const CrvUsdStaking = ({ mobileBreakpoint }: { mobileBreakpoint: string }) => { - {isUserScrvUsdBalanceZero ? ( + {/* {isUserScrvUsdBalanceZero ? ( ) : ( - )} + )} */} + diff --git a/packages/curve-ui-kit/src/themes/components.ts b/packages/curve-ui-kit/src/themes/components.ts index 6e4b8df49..516eefda0 100644 --- a/packages/curve-ui-kit/src/themes/components.ts +++ b/packages/curve-ui-kit/src/themes/components.ts @@ -1,4 +1,5 @@ import { type ThemeOptions } from '@mui/material/styles' +import { type TypographyOptions } from '@mui/material/styles/createTypography' import { defineMuiButton, defineMuiIconButton, defineMuiToggleButton } from './button' import { defineMuiTypography } from './typography' import { defineMuiTab, defineMuiTabs } from './tabs' @@ -12,7 +13,7 @@ import { definedMuiMenuItem } from '@ui-kit/themes/mui-menu-item' export const DEFAULT_BAR_SIZE = SizesAndSpaces.ButtonSize.sm export const MOBILE_SIDEBAR_WIDTH = { width: '100%', minWidth: 320 } as const -export const createComponents = (design: DesignSystem): ThemeOptions['components'] => ({ +export const createComponents = (design: DesignSystem, typography: TypographyOptions): ThemeOptions['components'] => ({ MuiButton: defineMuiButton(design), MuiButtonBase: { defaultProps: { @@ -25,9 +26,11 @@ export const createComponents = (design: DesignSystem): ThemeOptions['components paddingBlock: SizesAndSpaces.Spacing.sm.desktop + ' 0', paddingInline: SizesAndSpaces.Spacing.md.desktop + ' 0', borderBottom: `1px solid ${design.Layer[3].Outline}`, + height: '4rem', minHeight: `calc(${SizesAndSpaces.ButtonSize.lg} + 1px)`, // 1px for the border }, action: { alignContent: 'center', margin: 0 }, + title: { ...typography.headingSBold }, }, }, MuiCardActions: { diff --git a/packages/curve-ui-kit/src/themes/design/1_sizes_spaces.ts b/packages/curve-ui-kit/src/themes/design/1_sizes_spaces.ts index 2e71ebde7..e8180cf99 100644 --- a/packages/curve-ui-kit/src/themes/design/1_sizes_spaces.ts +++ b/packages/curve-ui-kit/src/themes/design/1_sizes_spaces.ts @@ -274,6 +274,7 @@ export const SizesAndSpaces = { tableTitle: '67rem', // 1072px table: '96rem', // 1536px footer: '96rem', // 1536px + statistics: '59.5rem', // 952px - "statistics" might not be the best name }, MaxHeight: { popover: '17rem', // 272px diff --git a/packages/curve-ui-kit/src/themes/generate-themes.ts b/packages/curve-ui-kit/src/themes/generate-themes.ts index 1f95b661d..67db82a7b 100644 --- a/packages/curve-ui-kit/src/themes/generate-themes.ts +++ b/packages/curve-ui-kit/src/themes/generate-themes.ts @@ -10,12 +10,13 @@ const paletteMode = (theme: ThemeKey, options: DesignOptions) => const generateTheme = (theme: ThemeKey, options: DesignOptions = {}): Theme => { const design = DesignSystem[theme](options) + const typography = createTypography(design) return createMuiTheme({ ...basicMuiTheme, design: { ...design, options }, palette: createPalette(paletteMode(theme, options), design), typography: createTypography(design), - components: createComponents(design), + components: createComponents(design, typography), shape: { borderRadius: 0 }, cssVariables: true, shadows: Array(25).fill('none') as Shadows, From a5d796e2fab0cb7581facae81ffb18153eeb3ae4 Mon Sep 17 00:00:00 2001 From: JustJousting Date: Thu, 9 Jan 2025 21:33:36 +0200 Subject: [PATCH 03/18] set up historical scrvusd yield fetch --- .../Statistics/StatsStack.tsx | 5 ++ apps/loan/src/entities/scrvusdYield.ts | 46 +++++++++++++++++++ 2 files changed, 51 insertions(+) create mode 100644 apps/loan/src/components/PageCrvUsdStaking/Statistics/StatsStack.tsx create mode 100644 apps/loan/src/entities/scrvusdYield.ts diff --git a/apps/loan/src/components/PageCrvUsdStaking/Statistics/StatsStack.tsx b/apps/loan/src/components/PageCrvUsdStaking/Statistics/StatsStack.tsx new file mode 100644 index 000000000..be19ff91f --- /dev/null +++ b/apps/loan/src/components/PageCrvUsdStaking/Statistics/StatsStack.tsx @@ -0,0 +1,5 @@ +import { Stack } from '@mui/material' + +const StatsStack = () => StatsStack + +export default StatsStack diff --git a/apps/loan/src/entities/scrvusdYield.ts b/apps/loan/src/entities/scrvusdYield.ts new file mode 100644 index 000000000..c8cff0b70 --- /dev/null +++ b/apps/loan/src/entities/scrvusdYield.ts @@ -0,0 +1,46 @@ +import { queryFactory } from '@ui-kit/lib/model/query' +import { createValidationSuite } from '@ui-kit/lib/validation' + +type TimeFrame = '1d' | '1h' | '1m' + +type ScrvUsdYieldFromApi = { + timestamp: number + assets: number + supply: number + proj_apy: number + price: number +} + +type GetScrvUsdYieldResponse = { + data?: ScrvUsdYieldFromApi[] + detail?: string +} + +export const _getScrvUsdYield = async (params: { timeFrame: TimeFrame }) => { + const timeFrameCalc = { + '1d': 300 * 24 * 60 * 60 * 1000, + '1h': 300 * 60 * 60 * 1000, + '1m': 300 * 60 * 1000, + } + + const startTimestamp = Math.floor((Date.now() - timeFrameCalc[params.timeFrame]) / 1000) + const endTimestamp = Math.floor(Date.now() / 1000) + + const url = `https://prices.curve.fi/v1/crvusd/savings/yield?agg_number=1&agg_units=day&start=${startTimestamp}&end=${endTimestamp}` + const response = await fetch(url) + const { data, detail } = (await response.json()) as GetScrvUsdYieldResponse + + // detail is returned from API when there is an error + if (detail) { + throw new Error(`Failed to fetch scrvUSD historical yield data. ${detail}`) + } + + return data +} + +export const { useQuery: useScrvUsdYield } = queryFactory({ + queryKey: (params: { timeFrame: TimeFrame }) => ['scrvUsdYield', { timeFrame: params.timeFrame }] as const, + queryFn: _getScrvUsdYield, + staleTime: '5m', + validationSuite: createValidationSuite(() => {}), +}) From f101e8d571955a8e94465592a28234c62ee320fb Mon Sep 17 00:00:00 2001 From: JustJousting <91265399+OnlyJousting@users.noreply.github.com> Date: Mon, 13 Jan 2025 01:23:58 +0200 Subject: [PATCH 04/18] add scrvusd historical revenue query --- .../PageCrvUsdStaking/Statistics/index.tsx | 3 +- apps/loan/src/entities/scrvusdRevenue.ts | 67 +++++++++++++++++++ 2 files changed, 69 insertions(+), 1 deletion(-) create mode 100644 apps/loan/src/entities/scrvusdRevenue.ts diff --git a/apps/loan/src/components/PageCrvUsdStaking/Statistics/index.tsx b/apps/loan/src/components/PageCrvUsdStaking/Statistics/index.tsx index 3b9f7a7fa..c37a4c987 100644 --- a/apps/loan/src/components/PageCrvUsdStaking/Statistics/index.tsx +++ b/apps/loan/src/components/PageCrvUsdStaking/Statistics/index.tsx @@ -1,12 +1,13 @@ import { SizesAndSpaces } from '@ui-kit/themes/design/1_sizes_spaces' import { Card, CardHeader } from '@mui/material' -import Typography from '@mui/material/Typography' import { useScrvUsdYield } from '@/entities/scrvusdYield' +import { useScrvUsdRevenue } from '@/entities/scrvusdRevenue' const { ColumnWidth, Spacing, MinWidth, MaxWidth } = SizesAndSpaces const Statistics = () => { const { data, error, isFetching } = useScrvUsdYield({ timeFrame: '1d' }) + const { data: revenueData, error: revenueError, isFetching: revenueIsFetching } = useScrvUsdRevenue({}) return ( { + const pages = 1 + const dataPerPage = 300 + + const url = `https://prices.curve.fi/v1/crvusd/savings/revenue?page=${pages}&per_page=${dataPerPage}` + const response = await fetch(url) + + const data = (await response.json()) as GetScrvUsdYieldResponse + + // detail is returned from API when there is an error + if ('detail' in data) { + throw new Error(`Failed to fetch scrvUSD historical revenue data. ${data.detail[0].msg}`) + } + + const formattedData = { + ...data, + total_distributed: +data.total_distributed / 1e18, + history: data.history.map((item) => ({ + ...item, + gain: +item.gain / 1e18, + loss: +item.loss / 1e18, + current_debt: +item.current_debt / 1e18, + total_refunds: +item.total_refunds / 1e18, + total_fees: +item.total_fees / 1e18, + protocol_fees: +item.protocol_fees / 1e18, + })), + } + + return formattedData +} + +export const { useQuery: useScrvUsdRevenue } = queryFactory({ + queryKey: () => ['scrvUsdRevenue'] as const, + queryFn: _getScrvUsdRevenue, + staleTime: '5m', + validationSuite: createValidationSuite(() => {}), +}) From d41a150ccd7737af6bf95ede9cb062d1f2e27e5e Mon Sep 17 00:00:00 2001 From: JustJousting <91265399+OnlyJousting@users.noreply.github.com> Date: Mon, 13 Jan 2025 17:14:24 +0200 Subject: [PATCH 05/18] set up scrvusd metrics stack --- .../Statistics/StatsStack.tsx | 57 ++++++++++++++++++- .../PageCrvUsdStaking/Statistics/index.tsx | 43 +++++++------- 2 files changed, 76 insertions(+), 24 deletions(-) diff --git a/apps/loan/src/components/PageCrvUsdStaking/Statistics/StatsStack.tsx b/apps/loan/src/components/PageCrvUsdStaking/Statistics/StatsStack.tsx index be19ff91f..ea1018d10 100644 --- a/apps/loan/src/components/PageCrvUsdStaking/Statistics/StatsStack.tsx +++ b/apps/loan/src/components/PageCrvUsdStaking/Statistics/StatsStack.tsx @@ -1,5 +1,60 @@ import { Stack } from '@mui/material' +import Typography from '@mui/material/Typography' +import Skeleton from '@mui/material/Skeleton' +import { SizesAndSpaces } from '@ui-kit/themes/design/1_sizes_spaces' +import { formatNumber } from '@/ui/utils' -const StatsStack = () => StatsStack +import { useScrvUsdYield } from '@/entities/scrvusdYield' +import { useScrvUsdRevenue } from '@/entities/scrvusdRevenue' + +const { Spacing } = SizesAndSpaces +const metricSkeletonSize = { width: 90, height: 36 } + +const StatsColumn = ({ + title, + value, + loading, + error, +}: { + title: string + value: string + loading: boolean + error: boolean +}) => ( + + t.design.Text.TextColors.Tertiary }}> + {title} + + {loading ? ( + + ) : ( + {error ? '-' : value} + )} + +) + +const StatsStack = () => { + const { data: yieldData, error: yieldError, isFetching: yieldIsFetching } = useScrvUsdYield({ timeFrame: '1d' }) + const { data: revenueData, error: revenueError, isFetching: revenueIsFetching } = useScrvUsdRevenue({}) + + return ( + + + + {/* + */} + + ) +} export default StatsStack diff --git a/apps/loan/src/components/PageCrvUsdStaking/Statistics/index.tsx b/apps/loan/src/components/PageCrvUsdStaking/Statistics/index.tsx index c37a4c987..88ee65c56 100644 --- a/apps/loan/src/components/PageCrvUsdStaking/Statistics/index.tsx +++ b/apps/loan/src/components/PageCrvUsdStaking/Statistics/index.tsx @@ -1,28 +1,25 @@ import { SizesAndSpaces } from '@ui-kit/themes/design/1_sizes_spaces' -import { Card, CardHeader } from '@mui/material' -import { useScrvUsdYield } from '@/entities/scrvusdYield' -import { useScrvUsdRevenue } from '@/entities/scrvusdRevenue' +import { Card, CardHeader, Box } from '@mui/material' +import StatsStack from './StatsStack' -const { ColumnWidth, Spacing, MinWidth, MaxWidth } = SizesAndSpaces +const { Spacing, MaxWidth } = SizesAndSpaces -const Statistics = () => { - const { data, error, isFetching } = useScrvUsdYield({ timeFrame: '1d' }) - const { data: revenueData, error: revenueError, isFetching: revenueIsFetching } = useScrvUsdRevenue({}) - - return ( - t.design.Layer[1].Fill, - width: '100%', - maxWidth: MaxWidth.statistics, - }} - > - - - ) -} +const Statistics = () => ( + t.design.Layer[1].Fill, + width: '100%', + maxWidth: MaxWidth.statistics, + }} + > + + + + + +) export default Statistics From 577d7bd9c0b915e5531c1336c3a0bbade604b0b0 Mon Sep 17 00:00:00 2001 From: JustJousting <91265399+OnlyJousting@users.noreply.github.com> Date: Fri, 17 Jan 2025 01:52:46 +0200 Subject: [PATCH 06/18] feat: add chart --- .../Statistics/ChartTooltip.tsx | 52 ++++++++ .../Statistics/LineChart.tsx | 115 ++++++++++++++++++ .../Statistics/StatsStack.tsx | 45 ++----- .../PageCrvUsdStaking/Statistics/index.tsx | 48 +++++--- apps/loan/src/entities/scrvusdYield.ts | 28 ++++- .../src/shared/ui/ChartHeader.tsx | 52 ++++++++ 6 files changed, 287 insertions(+), 53 deletions(-) create mode 100644 apps/loan/src/components/PageCrvUsdStaking/Statistics/ChartTooltip.tsx create mode 100644 apps/loan/src/components/PageCrvUsdStaking/Statistics/LineChart.tsx create mode 100644 packages/curve-ui-kit/src/shared/ui/ChartHeader.tsx diff --git a/apps/loan/src/components/PageCrvUsdStaking/Statistics/ChartTooltip.tsx b/apps/loan/src/components/PageCrvUsdStaking/Statistics/ChartTooltip.tsx new file mode 100644 index 000000000..3ee2d1ada --- /dev/null +++ b/apps/loan/src/components/PageCrvUsdStaking/Statistics/ChartTooltip.tsx @@ -0,0 +1,52 @@ +import type { NameType, ValueType } from 'recharts/types/component/DefaultTooltipContent' +import { TooltipProps } from 'recharts' +import { t } from '@lingui/macro' +import { Paper, Stack, Typography } from '@mui/material' +import { SizesAndSpaces } from '@ui-kit/themes/design/1_sizes_spaces' +import { formatDateFromTimestamp, formatNumber } from '@/ui/utils/utilsFormat' + +const { Spacing } = SizesAndSpaces + +const DataSet = ({ label, value }: { label: string; value: string }) => ( + + {label} + {value} + +) + +const CustomTooltip = ({ active, payload }: TooltipProps) => { + if (active && payload && payload.length) { + const { timestamp, proj_apy, proj_apy_7d_avg, proj_apy_total_avg } = payload[0].payload + + return ( + theme.design.Layer[3].Fill, + padding: Spacing.md, + width: '20.5rem', + maxWidth: '100vw', + }} + elevation={2} + > + {formatDateFromTimestamp(timestamp)} + theme.design.Layer[2].Fill, + }} + > + + + + + + ) + } + + return null +} + +export default CustomTooltip diff --git a/apps/loan/src/components/PageCrvUsdStaking/Statistics/LineChart.tsx b/apps/loan/src/components/PageCrvUsdStaking/Statistics/LineChart.tsx new file mode 100644 index 000000000..1d104f838 --- /dev/null +++ b/apps/loan/src/components/PageCrvUsdStaking/Statistics/LineChart.tsx @@ -0,0 +1,115 @@ +import type { ScrvUsdYieldWithAverages } from '@/entities/scrvusdYield' +import { SizesAndSpaces } from '@ui-kit/themes/design/1_sizes_spaces' +import { useTheme } from '@mui/material/styles' +import { t } from '@lingui/macro' +import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer } from 'recharts' +import CustomTooltip from './ChartTooltip' + +import { formatDateFromTimestamp } from '@/ui/utils/utilsFormat' + +// import LineChartCustomTooltip from './LineChartCustomTooltip' +const { FontSize } = SizesAndSpaces + +type Props = { + data: ScrvUsdYieldWithAverages[] + height?: number +} + +const LineChartComponent = ({ data, height = 400 }: Props) => { + const yAxisWidth = 24 + const { + design: { Color, Text }, + } = useTheme() + const gridLineColor = Color.Neutral[300] + const gridTextColor = Text.TextColors.Tertiary + const averageLineColor = Color.Tertiary[400] + const sevenDayAverageLineColor = Color.Secondary[500] + const mainLineColor = Color.Primary[500] + + // const maxValue = Math.max(...data.map((item) => item.gauge_relative_weight)) + + console.log(data) + + const labels = { + proj_apy: { text: t`Historical APR`, dash: 'none' }, + proj_apy_7d_avg: { text: t`7-day Average APR`, dash: '2 2' }, + proj_apy_total_avg: { text: t`Average APR`, dash: '4 4' }, + } as const + + return ( + + + + formatDateFromTimestamp(unixTime)} + /> + `${value}%`} + tickLine={{ fill: gridLineColor, strokeWidth: 0.5 }} + axisLine={false} + width={yAxisWidth} + // domain={[0, Math.ceil(maxValue)]} + /> + + + + + labels[value].text} + wrapperStyle={{ + fontWeight: 'bold', + fontSize: 'var(--font-size-1)', + color: 'var(--page--text-color)', + }} + /> + + + ) +} + +export default LineChartComponent diff --git a/apps/loan/src/components/PageCrvUsdStaking/Statistics/StatsStack.tsx b/apps/loan/src/components/PageCrvUsdStaking/Statistics/StatsStack.tsx index ea1018d10..6c74f5d1e 100644 --- a/apps/loan/src/components/PageCrvUsdStaking/Statistics/StatsStack.tsx +++ b/apps/loan/src/components/PageCrvUsdStaking/Statistics/StatsStack.tsx @@ -1,37 +1,11 @@ import { Stack } from '@mui/material' -import Typography from '@mui/material/Typography' -import Skeleton from '@mui/material/Skeleton' +import { Metric } from '@ui-kit/shared/ui/Metric' import { SizesAndSpaces } from '@ui-kit/themes/design/1_sizes_spaces' -import { formatNumber } from '@/ui/utils' import { useScrvUsdYield } from '@/entities/scrvusdYield' import { useScrvUsdRevenue } from '@/entities/scrvusdRevenue' const { Spacing } = SizesAndSpaces -const metricSkeletonSize = { width: 90, height: 36 } - -const StatsColumn = ({ - title, - value, - loading, - error, -}: { - title: string - value: string - loading: boolean - error: boolean -}) => ( - - t.design.Text.TextColors.Tertiary }}> - {title} - - {loading ? ( - - ) : ( - {error ? '-' : value} - )} - -) const StatsStack = () => { const { data: yieldData, error: yieldError, isFetching: yieldIsFetching } = useScrvUsdYield({ timeFrame: '1d' }) @@ -39,17 +13,18 @@ const StatsStack = () => { return ( - - {/* */} diff --git a/apps/loan/src/components/PageCrvUsdStaking/Statistics/index.tsx b/apps/loan/src/components/PageCrvUsdStaking/Statistics/index.tsx index 88ee65c56..2ac04321f 100644 --- a/apps/loan/src/components/PageCrvUsdStaking/Statistics/index.tsx +++ b/apps/loan/src/components/PageCrvUsdStaking/Statistics/index.tsx @@ -1,25 +1,39 @@ import { SizesAndSpaces } from '@ui-kit/themes/design/1_sizes_spaces' import { Card, CardHeader, Box } from '@mui/material' import StatsStack from './StatsStack' +import ChartHeader, { ChartOption } from '@ui-kit/shared/ui/ChartHeader' +import { useScrvUsdYield } from '@/entities/scrvusdYield' +import LineChart from './LineChart' const { Spacing, MaxWidth } = SizesAndSpaces -const Statistics = () => ( - t.design.Layer[1].Fill, - width: '100%', - maxWidth: MaxWidth.statistics, - }} - > - - - - - -) +const chartOptions: ChartOption[] = [ + { activeTitle: 'Historical Rate', label: 'Interest Rate' }, + { activeTitle: 'Historical Rewards', label: 'Rewards' }, +] + +const Statistics = () => { + const { data } = useScrvUsdYield({ timeFrame: '1d' }) + + return ( + t.design.Layer[1].Fill, + width: '100%', + maxWidth: MaxWidth.statistics, + }} + > + + + + + + + + ) +} export default Statistics diff --git a/apps/loan/src/entities/scrvusdYield.ts b/apps/loan/src/entities/scrvusdYield.ts index c8cff0b70..cba0d96bd 100644 --- a/apps/loan/src/entities/scrvusdYield.ts +++ b/apps/loan/src/entities/scrvusdYield.ts @@ -11,6 +11,11 @@ type ScrvUsdYieldFromApi = { price: number } +export type ScrvUsdYieldWithAverages = ScrvUsdYieldFromApi & { + proj_apy_7d_avg: number + proj_apy_total_avg: number +} + type GetScrvUsdYieldResponse = { data?: ScrvUsdYieldFromApi[] detail?: string @@ -35,7 +40,28 @@ export const _getScrvUsdYield = async (params: { timeFrame: TimeFrame }) => { throw new Error(`Failed to fetch scrvUSD historical yield data. ${detail}`) } - return data + if (!data) { + return [] + } + + const dataWithAverages: ScrvUsdYieldWithAverages[] = data?.map((item, index, array) => { + // Calculate overall average across all data points + const totalAverage = array.reduce((sum, curr) => sum + curr.proj_apy, 0) / array.length + + // Calculate 7-day moving average (looking back 7 days) + const lookback = 7 * 24 * 12 // 7 days worth of 5-minute intervals + const startIdx = Math.max(0, index - lookback) + const relevantData = array.slice(startIdx, index + 1) + const movingAverage = relevantData.reduce((sum, curr) => sum + curr.proj_apy, 0) / relevantData.length + + return { + ...item, + proj_apy_7d_avg: movingAverage, + proj_apy_total_avg: totalAverage, + } + }) + + return dataWithAverages } export const { useQuery: useScrvUsdYield } = queryFactory({ diff --git a/packages/curve-ui-kit/src/shared/ui/ChartHeader.tsx b/packages/curve-ui-kit/src/shared/ui/ChartHeader.tsx new file mode 100644 index 000000000..d86531048 --- /dev/null +++ b/packages/curve-ui-kit/src/shared/ui/ChartHeader.tsx @@ -0,0 +1,52 @@ +import { useState } from 'react' +import { Stack, ToggleButtonGroup } from '@mui/material' +import { SizesAndSpaces } from '@ui-kit/themes/design/1_sizes_spaces' +import Typography from '@mui/material/Typography' +import ToggleButton from '@mui/material/ToggleButton' +import IconButton from '@mui/material/IconButton' +import Icon from 'ui/src/Icon' + +const { Spacing } = SizesAndSpaces + +export type ChartOption = { + label: string + activeTitle: string +} + +type TimeOption = { + label: '1d' | '7d' | '30d' +} + +type ChartHeaderProps = { + chartOptions: ChartOption[] + timeOptions?: TimeOption[] +} + +const ChartHeader = ({ chartOptions, timeOptions }: ChartHeaderProps) => { + const [activeChartOption, setActiveChartOption] = useState(chartOptions[0]) + + const handleChartOption = (event: React.MouseEvent, newChartOption: ChartOption) => { + // ensure that one option is always selected by checking null + if (newChartOption !== null) setActiveChartOption(newChartOption) + } + + return ( + + + {activeChartOption.activeTitle} + + + {chartOptions.map((option) => ( + + {option.label} + + ))} + + + + + + ) +} + +export default ChartHeader From 38da5161924d526809dbeff6d9978910b84707ae Mon Sep 17 00:00:00 2001 From: JustJousting <91265399+OnlyJousting@users.noreply.github.com> Date: Mon, 20 Jan 2025 00:23:40 +0200 Subject: [PATCH 07/18] styling: configure MuiSelect --- .../curve-ui-kit/src/themes/components.ts | 21 ++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/packages/curve-ui-kit/src/themes/components.ts b/packages/curve-ui-kit/src/themes/components.ts index 5ee8acc1b..4859c3d9b 100644 --- a/packages/curve-ui-kit/src/themes/components.ts +++ b/packages/curve-ui-kit/src/themes/components.ts @@ -1,5 +1,5 @@ import { type ThemeOptions } from '@mui/material/styles' -import { type TypographyOptions } from '@mui/material/styles/createTypography' +import type { TypographyOptions } from '@mui/material/styles/createTypography' import { defineMuiButton, defineMuiIconButton, defineMuiToggleButton } from './button' import { defineMuiTypography } from './typography' import { defineMuiTab, defineMuiTabs } from './tabs' @@ -8,10 +8,8 @@ import { SizesAndSpaces } from './design/1_sizes_spaces' import { defineMuiSwitch } from './mui-switch' import { basicMuiTheme } from './basic-theme' import { alpha } from '@mui/system' -import type { TypographyOptions } from '@mui/material/styles/createTypography' import { defineMuiMenuItem } from '@ui-kit/themes/mui-menu-item' import { TransitionFunction } from '@ui-kit/themes/design/0_primitives' -import { linearProgressClasses } from '@mui/material/LinearProgress' export const DEFAULT_BAR_SIZE = SizesAndSpaces.ButtonSize.sm export const MOBILE_SIDEBAR_WIDTH = { width: '100%', minWidth: 320 } as const @@ -105,6 +103,23 @@ export const createComponents = (design: DesignSystem, typography: TypographyOpt }, }, MuiMenuItem: defineMuiMenuItem(design), + MuiSelect: { + styleOverrides: { + root: { + border: 'none', + '& .MuiOutlinedInput-notchedOutline': { + border: 'none', + borderBottom: `1px solid ${design.Layer[3].Outline}`, + }, + '&.Mui-focused .MuiOutlinedInput-notchedOutline': { + border: `2px solid ${design.Inputs.Base.Default.Border.Active}`, + }, + }, + select: { + ...typography.bodyMBold, + }, + }, + }, MuiSlider: { styleOverrides: { thumb: { From 30e8347e0ad0fa83393645513034581f5fcfb43e Mon Sep 17 00:00:00 2001 From: JustJousting <91265399+OnlyJousting@users.noreply.github.com> Date: Mon, 20 Jan 2025 00:24:29 +0200 Subject: [PATCH 08/18] set up TimeOption --- .../Statistics/LineChart.tsx | 15 +++---- .../Statistics/StatsStack.tsx | 6 +-- .../PageCrvUsdStaking/Statistics/index.tsx | 18 ++++++-- apps/loan/src/entities/scrvusdYield.ts | 37 ++++++++++------ .../src/shared/ui/ChartHeader.tsx | 42 ++++++++++++++----- 5 files changed, 79 insertions(+), 39 deletions(-) diff --git a/apps/loan/src/components/PageCrvUsdStaking/Statistics/LineChart.tsx b/apps/loan/src/components/PageCrvUsdStaking/Statistics/LineChart.tsx index 1d104f838..0eb07faf6 100644 --- a/apps/loan/src/components/PageCrvUsdStaking/Statistics/LineChart.tsx +++ b/apps/loan/src/components/PageCrvUsdStaking/Statistics/LineChart.tsx @@ -26,13 +26,9 @@ const LineChartComponent = ({ data, height = 400 }: Props) => { const sevenDayAverageLineColor = Color.Secondary[500] const mainLineColor = Color.Primary[500] - // const maxValue = Math.max(...data.map((item) => item.gauge_relative_weight)) - - console.log(data) - const labels = { - proj_apy: { text: t`Historical APR`, dash: 'none' }, - proj_apy_7d_avg: { text: t`7-day Average APR`, dash: '2 2' }, + proj_apy: { text: t`APR`, dash: 'none' }, + proj_apy_7d_avg: { text: t`7-day MA APR`, dash: '2 2' }, proj_apy_total_avg: { text: t`Average APR`, dash: '4 4' }, } as const @@ -61,11 +57,11 @@ const LineChartComponent = ({ data, height = 400 }: Props) => { /> `${value}%`} + tickFormatter={(value) => `${value.toFixed(0)}%`} tickLine={{ fill: gridLineColor, strokeWidth: 0.5 }} axisLine={false} + dataKey={'proj_apy'} width={yAxisWidth} - // domain={[0, Math.ceil(maxValue)]} /> { labels[value].text} diff --git a/apps/loan/src/components/PageCrvUsdStaking/Statistics/StatsStack.tsx b/apps/loan/src/components/PageCrvUsdStaking/Statistics/StatsStack.tsx index 6c74f5d1e..190a96d2f 100644 --- a/apps/loan/src/components/PageCrvUsdStaking/Statistics/StatsStack.tsx +++ b/apps/loan/src/components/PageCrvUsdStaking/Statistics/StatsStack.tsx @@ -8,20 +8,20 @@ import { useScrvUsdRevenue } from '@/entities/scrvusdRevenue' const { Spacing } = SizesAndSpaces const StatsStack = () => { - const { data: yieldData, error: yieldError, isFetching: yieldIsFetching } = useScrvUsdYield({ timeFrame: '1d' }) + const { data: yieldData, error: yieldError, isFetching: yieldIsFetching } = useScrvUsdYield({ timeOption: '1d' }) const { data: revenueData, error: revenueError, isFetching: revenueIsFetching } = useScrvUsdRevenue({}) return ( { - const { data } = useScrvUsdYield({ timeFrame: '1d' }) + const [activeChartOption, setActiveChartOption] = useState(chartOptions[0]) + const [activeTimeOption, setActiveTimeOption] = useState(timeOptions[0]) + const { data } = useScrvUsdYield({ timeOption: activeTimeOption }) return ( { - + ) diff --git a/apps/loan/src/entities/scrvusdYield.ts b/apps/loan/src/entities/scrvusdYield.ts index cba0d96bd..e1df101db 100644 --- a/apps/loan/src/entities/scrvusdYield.ts +++ b/apps/loan/src/entities/scrvusdYield.ts @@ -1,8 +1,7 @@ +import type { TimeOption } from '@ui-kit/shared/ui/ChartHeader' import { queryFactory } from '@ui-kit/lib/model/query' import { createValidationSuite } from '@ui-kit/lib/validation' -type TimeFrame = '1d' | '1h' | '1m' - type ScrvUsdYieldFromApi = { timestamp: number assets: number @@ -21,17 +20,22 @@ type GetScrvUsdYieldResponse = { detail?: string } -export const _getScrvUsdYield = async (params: { timeFrame: TimeFrame }) => { - const timeFrameCalc = { +export const _getScrvUsdYield = async (params: { timeOption: TimeOption }) => { + const timeOptionCalc = { '1d': 300 * 24 * 60 * 60 * 1000, - '1h': 300 * 60 * 60 * 1000, - '1m': 300 * 60 * 1000, + '1w': 300 * 24 * 60 * 60 * 7 * 1000, + '1m': 300 * 24 * 60 * 60 * 30 * 1000, + } + const aggNumbers = { + '1d': 1, + '1w': 7, + '1m': 30, } - const startTimestamp = Math.floor((Date.now() - timeFrameCalc[params.timeFrame]) / 1000) + const startTimestamp = Math.floor((Date.now() - timeOptionCalc[params.timeOption]) / 1000) const endTimestamp = Math.floor(Date.now() / 1000) - const url = `https://prices.curve.fi/v1/crvusd/savings/yield?agg_number=1&agg_units=day&start=${startTimestamp}&end=${endTimestamp}` + const url = `https://prices.curve.fi/v1/crvusd/savings/yield?agg_number=${aggNumbers[params.timeOption]}&agg_units=day&start=${startTimestamp}&end=${endTimestamp}` const response = await fetch(url) const { data, detail } = (await response.json()) as GetScrvUsdYieldResponse @@ -48,10 +52,17 @@ export const _getScrvUsdYield = async (params: { timeFrame: TimeFrame }) => { // Calculate overall average across all data points const totalAverage = array.reduce((sum, curr) => sum + curr.proj_apy, 0) / array.length - // Calculate 7-day moving average (looking back 7 days) - const lookback = 7 * 24 * 12 // 7 days worth of 5-minute intervals - const startIdx = Math.max(0, index - lookback) - const relevantData = array.slice(startIdx, index + 1) + // Calculate 7-day moving average + const SEVEN_DAYS_IN_SECONDS = 7 * 24 * 60 * 60 + const currentTimestamp = item.timestamp + const sevenDaysAgoTimestamp = currentTimestamp - SEVEN_DAYS_IN_SECONDS + + const relevantData = array.filter( + (dataPoint) => + dataPoint.timestamp >= sevenDaysAgoTimestamp && + dataPoint.timestamp <= currentTimestamp && + array.indexOf(dataPoint) <= index, // Only include data points up to current index + ) const movingAverage = relevantData.reduce((sum, curr) => sum + curr.proj_apy, 0) / relevantData.length return { @@ -65,7 +76,7 @@ export const _getScrvUsdYield = async (params: { timeFrame: TimeFrame }) => { } export const { useQuery: useScrvUsdYield } = queryFactory({ - queryKey: (params: { timeFrame: TimeFrame }) => ['scrvUsdYield', { timeFrame: params.timeFrame }] as const, + queryKey: (params: { timeOption: TimeOption }) => ['scrvUsdYield', { timeOption: params.timeOption }] as const, queryFn: _getScrvUsdYield, staleTime: '5m', validationSuite: createValidationSuite(() => {}), diff --git a/packages/curve-ui-kit/src/shared/ui/ChartHeader.tsx b/packages/curve-ui-kit/src/shared/ui/ChartHeader.tsx index d86531048..7ac969f10 100644 --- a/packages/curve-ui-kit/src/shared/ui/ChartHeader.tsx +++ b/packages/curve-ui-kit/src/shared/ui/ChartHeader.tsx @@ -1,5 +1,7 @@ -import { useState } from 'react' -import { Stack, ToggleButtonGroup } from '@mui/material' +import Stack from '@mui/material/Stack' +import ToggleButtonGroup from '@mui/material/ToggleButtonGroup' +import Select, { SelectChangeEvent } from '@mui/material/Select' +import MenuItem from '@mui/material/MenuItem' import { SizesAndSpaces } from '@ui-kit/themes/design/1_sizes_spaces' import Typography from '@mui/material/Typography' import ToggleButton from '@mui/material/ToggleButton' @@ -13,29 +15,40 @@ export type ChartOption = { activeTitle: string } -type TimeOption = { - label: '1d' | '7d' | '30d' -} +export type TimeOption = '1d' | '1w' | '1m' type ChartHeaderProps = { chartOptions: ChartOption[] - timeOptions?: TimeOption[] + timeOptions: TimeOption[] + activeChartOption: ChartOption + setActiveChartOption: (newChartOption: ChartOption) => void + activeTimeOption: TimeOption + setActiveTimeOption: (newTimeOption: TimeOption) => void } -const ChartHeader = ({ chartOptions, timeOptions }: ChartHeaderProps) => { - const [activeChartOption, setActiveChartOption] = useState(chartOptions[0]) - +const ChartHeader = ({ + chartOptions, + timeOptions, + activeChartOption, + setActiveChartOption, + activeTimeOption, + setActiveTimeOption, +}: ChartHeaderProps) => { const handleChartOption = (event: React.MouseEvent, newChartOption: ChartOption) => { // ensure that one option is always selected by checking null if (newChartOption !== null) setActiveChartOption(newChartOption) } + const handleTimeOption = (event: SelectChangeEvent) => { + if (event.target.value !== null) setActiveTimeOption(event.target.value as TimeOption) + } + return ( - + {activeChartOption.activeTitle} - + {chartOptions.map((option) => ( {option.label} @@ -45,6 +58,13 @@ const ChartHeader = ({ chartOptions, timeOptions }: ChartHeaderProps) => { + ) } From 67dfd0bf96399a968796121fa907850e9ef82340 Mon Sep 17 00:00:00 2001 From: JustJousting <91265399+OnlyJousting@users.noreply.github.com> Date: Mon, 20 Jan 2025 00:25:03 +0200 Subject: [PATCH 09/18] Line chart tooltip --- .../Statistics/ChartTooltip.tsx | 46 +++++++++++++++---- 1 file changed, 38 insertions(+), 8 deletions(-) diff --git a/apps/loan/src/components/PageCrvUsdStaking/Statistics/ChartTooltip.tsx b/apps/loan/src/components/PageCrvUsdStaking/Statistics/ChartTooltip.tsx index 3ee2d1ada..cc0d91959 100644 --- a/apps/loan/src/components/PageCrvUsdStaking/Statistics/ChartTooltip.tsx +++ b/apps/loan/src/components/PageCrvUsdStaking/Statistics/ChartTooltip.tsx @@ -3,18 +3,38 @@ import { TooltipProps } from 'recharts' import { t } from '@lingui/macro' import { Paper, Stack, Typography } from '@mui/material' import { SizesAndSpaces } from '@ui-kit/themes/design/1_sizes_spaces' -import { formatDateFromTimestamp, formatNumber } from '@/ui/utils/utilsFormat' +import { formatDateFromTimestamp } from '@/ui/utils/utilsFormat' +import { useTheme } from '@mui/material/styles' const { Spacing } = SizesAndSpaces -const DataSet = ({ label, value }: { label: string; value: string }) => ( - - {label} +const DataSet = ({ + label, + value, + lineColor, + dash, +}: { + label: string + value: string + lineColor: string + dash?: string +}) => ( + + + + + + {label} + {value} ) const CustomTooltip = ({ active, payload }: TooltipProps) => { + const { + design: { Color }, + } = useTheme() + if (active && payload && payload.length) { const { timestamp, proj_apy, proj_apy_7d_avg, proj_apy_total_avg } = payload[0].payload @@ -34,13 +54,23 @@ const CustomTooltip = ({ active, payload }: TooltipProps) = sx={{ marginTop: Spacing.sm, padding: Spacing.sm, - gap: 0, + gap: 1, backgroundColor: (theme) => theme.design.Layer[2].Fill, }} > - - - + + + ) From 3f48ac6af60499de28e005b4783ff3835f4a98b0 Mon Sep 17 00:00:00 2001 From: JustJousting <91265399+OnlyJousting@users.noreply.github.com> Date: Mon, 20 Jan 2025 16:01:39 +0200 Subject: [PATCH 10/18] move types --- .../src/components/PageCrvUsdStaking/Statistics/index.tsx | 5 +++-- apps/loan/src/entities/scrvusdYield.ts | 2 +- packages/curve-ui-kit/src/lib/types/scrvusd.ts | 1 + packages/curve-ui-kit/src/shared/ui/ChartHeader.tsx | 3 +-- 4 files changed, 6 insertions(+), 5 deletions(-) create mode 100644 packages/curve-ui-kit/src/lib/types/scrvusd.ts diff --git a/apps/loan/src/components/PageCrvUsdStaking/Statistics/index.tsx b/apps/loan/src/components/PageCrvUsdStaking/Statistics/index.tsx index 39254cf87..8af46d782 100644 --- a/apps/loan/src/components/PageCrvUsdStaking/Statistics/index.tsx +++ b/apps/loan/src/components/PageCrvUsdStaking/Statistics/index.tsx @@ -1,15 +1,16 @@ +import type { TimeOption } from '@ui-kit/lib/types/scrvusd' import { SizesAndSpaces } from '@ui-kit/themes/design/1_sizes_spaces' import { Card, CardHeader, Box } from '@mui/material' import { useState } from 'react' import StatsStack from './StatsStack' -import ChartHeader, { ChartOption, TimeOption } from '@ui-kit/shared/ui/ChartHeader' +import ChartHeader, { ChartOption } from '@ui-kit/shared/ui/ChartHeader' import { useScrvUsdYield } from '@/entities/scrvusdYield' import LineChart from './LineChart' const { Spacing, MaxWidth } = SizesAndSpaces const chartOptions: ChartOption[] = [ - { activeTitle: 'Historical Rate', label: 'Interest Rate' }, + { activeTitle: 'Historical Rate', label: 'Savings Rate' }, { activeTitle: 'Historical Rewards', label: 'Rewards' }, ] diff --git a/apps/loan/src/entities/scrvusdYield.ts b/apps/loan/src/entities/scrvusdYield.ts index e1df101db..b34dde90d 100644 --- a/apps/loan/src/entities/scrvusdYield.ts +++ b/apps/loan/src/entities/scrvusdYield.ts @@ -1,4 +1,4 @@ -import type { TimeOption } from '@ui-kit/shared/ui/ChartHeader' +import type { TimeOption } from '@ui-kit/lib/types/scrvusd' import { queryFactory } from '@ui-kit/lib/model/query' import { createValidationSuite } from '@ui-kit/lib/validation' diff --git a/packages/curve-ui-kit/src/lib/types/scrvusd.ts b/packages/curve-ui-kit/src/lib/types/scrvusd.ts new file mode 100644 index 000000000..d7a4449d4 --- /dev/null +++ b/packages/curve-ui-kit/src/lib/types/scrvusd.ts @@ -0,0 +1 @@ +export type TimeOption = '1d' | '1w' | '1m' diff --git a/packages/curve-ui-kit/src/shared/ui/ChartHeader.tsx b/packages/curve-ui-kit/src/shared/ui/ChartHeader.tsx index 7ac969f10..99a462284 100644 --- a/packages/curve-ui-kit/src/shared/ui/ChartHeader.tsx +++ b/packages/curve-ui-kit/src/shared/ui/ChartHeader.tsx @@ -1,3 +1,4 @@ +import type { TimeOption } from '@ui-kit/lib/types/scrvusd' import Stack from '@mui/material/Stack' import ToggleButtonGroup from '@mui/material/ToggleButtonGroup' import Select, { SelectChangeEvent } from '@mui/material/Select' @@ -15,8 +16,6 @@ export type ChartOption = { activeTitle: string } -export type TimeOption = '1d' | '1w' | '1m' - type ChartHeaderProps = { chartOptions: ChartOption[] timeOptions: TimeOption[] From 9faab04ede4ec3b566798306b91a7a8c5ffb4d3b Mon Sep 17 00:00:00 2001 From: JustJousting Date: Mon, 20 Jan 2025 16:10:50 +0200 Subject: [PATCH 11/18] remove chart animation --- .../src/components/PageCrvUsdStaking/Statistics/LineChart.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/apps/loan/src/components/PageCrvUsdStaking/Statistics/LineChart.tsx b/apps/loan/src/components/PageCrvUsdStaking/Statistics/LineChart.tsx index 0eb07faf6..7c2ce203d 100644 --- a/apps/loan/src/components/PageCrvUsdStaking/Statistics/LineChart.tsx +++ b/apps/loan/src/components/PageCrvUsdStaking/Statistics/LineChart.tsx @@ -71,6 +71,7 @@ const LineChartComponent = ({ data, height = 400 }: Props) => { strokeWidth={2} activeDot={{ r: 4 }} dot={false} + isAnimationActive={false} /> { strokeDasharray={labels['proj_apy_7d_avg'].dash} activeDot={{ r: 4 }} dot={false} + isAnimationActive={false} /> { strokeDasharray={labels['proj_apy_total_avg'].dash} activeDot={{ r: 4 }} dot={false} + isAnimationActive={false} /> Date: Mon, 20 Jan 2025 17:39:37 +0200 Subject: [PATCH 12/18] update import paths --- .../PageCrvUsdStaking/Statistics/ChartTooltip.tsx | 2 +- .../PageCrvUsdStaking/Statistics/LineChart.tsx | 4 ++-- .../PageCrvUsdStaking/Statistics/StatsStack.tsx | 4 ++-- .../components/PageCrvUsdStaking/Statistics/index.tsx | 2 +- apps/loan/src/components/PageCrvUsdStaking/index.tsx | 10 +++++----- packages/curve-ui-kit/src/themes/components.ts | 1 - 6 files changed, 11 insertions(+), 12 deletions(-) diff --git a/apps/loan/src/components/PageCrvUsdStaking/Statistics/ChartTooltip.tsx b/apps/loan/src/components/PageCrvUsdStaking/Statistics/ChartTooltip.tsx index cc0d91959..ff8990307 100644 --- a/apps/loan/src/components/PageCrvUsdStaking/Statistics/ChartTooltip.tsx +++ b/apps/loan/src/components/PageCrvUsdStaking/Statistics/ChartTooltip.tsx @@ -3,7 +3,7 @@ import { TooltipProps } from 'recharts' import { t } from '@lingui/macro' import { Paper, Stack, Typography } from '@mui/material' import { SizesAndSpaces } from '@ui-kit/themes/design/1_sizes_spaces' -import { formatDateFromTimestamp } from '@/ui/utils/utilsFormat' +import { formatDateFromTimestamp } from '@ui/utils/utilsFormat' import { useTheme } from '@mui/material/styles' const { Spacing } = SizesAndSpaces diff --git a/apps/loan/src/components/PageCrvUsdStaking/Statistics/LineChart.tsx b/apps/loan/src/components/PageCrvUsdStaking/Statistics/LineChart.tsx index 7c2ce203d..12428856c 100644 --- a/apps/loan/src/components/PageCrvUsdStaking/Statistics/LineChart.tsx +++ b/apps/loan/src/components/PageCrvUsdStaking/Statistics/LineChart.tsx @@ -1,11 +1,11 @@ -import type { ScrvUsdYieldWithAverages } from '@/entities/scrvusdYield' +import type { ScrvUsdYieldWithAverages } from '@loan/entities/scrvusdYield' import { SizesAndSpaces } from '@ui-kit/themes/design/1_sizes_spaces' import { useTheme } from '@mui/material/styles' import { t } from '@lingui/macro' import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer } from 'recharts' import CustomTooltip from './ChartTooltip' -import { formatDateFromTimestamp } from '@/ui/utils/utilsFormat' +import { formatDateFromTimestamp } from '@ui/utils/utilsFormat' // import LineChartCustomTooltip from './LineChartCustomTooltip' const { FontSize } = SizesAndSpaces diff --git a/apps/loan/src/components/PageCrvUsdStaking/Statistics/StatsStack.tsx b/apps/loan/src/components/PageCrvUsdStaking/Statistics/StatsStack.tsx index 190a96d2f..63dfc8ded 100644 --- a/apps/loan/src/components/PageCrvUsdStaking/Statistics/StatsStack.tsx +++ b/apps/loan/src/components/PageCrvUsdStaking/Statistics/StatsStack.tsx @@ -2,8 +2,8 @@ import { Stack } from '@mui/material' import { Metric } from '@ui-kit/shared/ui/Metric' import { SizesAndSpaces } from '@ui-kit/themes/design/1_sizes_spaces' -import { useScrvUsdYield } from '@/entities/scrvusdYield' -import { useScrvUsdRevenue } from '@/entities/scrvusdRevenue' +import { useScrvUsdYield } from '@loan/entities/scrvusdYield' +import { useScrvUsdRevenue } from '@loan/entities/scrvusdRevenue' const { Spacing } = SizesAndSpaces diff --git a/apps/loan/src/components/PageCrvUsdStaking/Statistics/index.tsx b/apps/loan/src/components/PageCrvUsdStaking/Statistics/index.tsx index 8af46d782..184aa59e0 100644 --- a/apps/loan/src/components/PageCrvUsdStaking/Statistics/index.tsx +++ b/apps/loan/src/components/PageCrvUsdStaking/Statistics/index.tsx @@ -4,7 +4,7 @@ import { Card, CardHeader, Box } from '@mui/material' import { useState } from 'react' import StatsStack from './StatsStack' import ChartHeader, { ChartOption } from '@ui-kit/shared/ui/ChartHeader' -import { useScrvUsdYield } from '@/entities/scrvusdYield' +import { useScrvUsdYield } from '@loan/entities/scrvusdYield' import LineChart from './LineChart' const { Spacing, MaxWidth } = SizesAndSpaces diff --git a/apps/loan/src/components/PageCrvUsdStaking/index.tsx b/apps/loan/src/components/PageCrvUsdStaking/index.tsx index f8ced7b37..70d5fd19a 100644 --- a/apps/loan/src/components/PageCrvUsdStaking/index.tsx +++ b/apps/loan/src/components/PageCrvUsdStaking/index.tsx @@ -4,11 +4,11 @@ import BigNumber from 'bignumber.js' import useStore from '@loan/store/useStore' -import StatsBanner from '@/components/PageCrvUsdStaking/StatsBanner' -import Statistics from '@/components/PageCrvUsdStaking/Statistics' -import DepositWithdraw from '@/components/PageCrvUsdStaking/DepositWithdraw' -import UserInformation from '@/components/PageCrvUsdStaking/UserInformation' -import UserPositionBanner from '@/components/PageCrvUsdStaking/UserPositionBanner' +import StatsBanner from '@loan/components/PageCrvUsdStaking/StatsBanner' +import Statistics from '@loan/components/PageCrvUsdStaking/Statistics' +import DepositWithdraw from '@loan/components/PageCrvUsdStaking/DepositWithdraw' +import UserInformation from '@loan/components/PageCrvUsdStaking/UserInformation' +import UserPositionBanner from '@loan/components/PageCrvUsdStaking/UserPositionBanner' const CrvUsdStaking = ({ mobileBreakpoint }: { mobileBreakpoint: string }) => { const { diff --git a/packages/curve-ui-kit/src/themes/components.ts b/packages/curve-ui-kit/src/themes/components.ts index 6f2b67807..b002ed7f0 100644 --- a/packages/curve-ui-kit/src/themes/components.ts +++ b/packages/curve-ui-kit/src/themes/components.ts @@ -10,7 +10,6 @@ import { basicMuiTheme } from './basic-theme' import { alpha } from '@mui/system' import { defineMuiMenuItem } from '@ui-kit/themes/mui-menu-item' import { defineMuiAlert, defineMuiAlertTitle } from '@ui-kit/themes/mui-alert' -import type { TypographyOptions } from '@mui/material/styles/createTypography' import { TransitionFunction } from '@ui-kit/themes/design/0_primitives' export const DEFAULT_BAR_SIZE = SizesAndSpaces.ButtonSize.sm From e30ca2259542fb727d245b891d2fa26e73bf1254 Mon Sep 17 00:00:00 2001 From: JustJousting Date: Tue, 21 Jan 2025 01:10:04 +0200 Subject: [PATCH 13/18] calc revenue epochs --- .../Statistics/StatsStack.tsx | 11 +++-- .../PageCrvUsdStaking/Statistics/index.tsx | 10 ++-- apps/loan/src/entities/scrvusdRevenue.ts | 49 +++++++++++++++++++ 3 files changed, 64 insertions(+), 6 deletions(-) diff --git a/apps/loan/src/components/PageCrvUsdStaking/Statistics/StatsStack.tsx b/apps/loan/src/components/PageCrvUsdStaking/Statistics/StatsStack.tsx index 63dfc8ded..6e66e3293 100644 --- a/apps/loan/src/components/PageCrvUsdStaking/Statistics/StatsStack.tsx +++ b/apps/loan/src/components/PageCrvUsdStaking/Statistics/StatsStack.tsx @@ -20,14 +20,19 @@ const StatsStack = () => { unit="dollar" /> - {/* - */} + + {/* */} ) } diff --git a/apps/loan/src/components/PageCrvUsdStaking/Statistics/index.tsx b/apps/loan/src/components/PageCrvUsdStaking/Statistics/index.tsx index 184aa59e0..406df5a61 100644 --- a/apps/loan/src/components/PageCrvUsdStaking/Statistics/index.tsx +++ b/apps/loan/src/components/PageCrvUsdStaking/Statistics/index.tsx @@ -5,13 +5,14 @@ import { useState } from 'react' import StatsStack from './StatsStack' import ChartHeader, { ChartOption } from '@ui-kit/shared/ui/ChartHeader' import { useScrvUsdYield } from '@loan/entities/scrvusdYield' +import { useScrvUsdRevenue } from '@loan/entities/scrvusdRevenue' import LineChart from './LineChart' const { Spacing, MaxWidth } = SizesAndSpaces const chartOptions: ChartOption[] = [ { activeTitle: 'Historical Rate', label: 'Savings Rate' }, - { activeTitle: 'Historical Rewards', label: 'Rewards' }, + { activeTitle: 'Historical Distributions', label: 'Distributions' }, ] const timeOptions: TimeOption[] = ['1d', '1w', '1m'] @@ -19,7 +20,10 @@ const timeOptions: TimeOption[] = ['1d', '1w', '1m'] const Statistics = () => { const [activeChartOption, setActiveChartOption] = useState(chartOptions[0]) const [activeTimeOption, setActiveTimeOption] = useState(timeOptions[0]) - const { data } = useScrvUsdYield({ timeOption: activeTimeOption }) + const { data: yieldData } = useScrvUsdYield({ timeOption: activeTimeOption }) + const { data: revenueData } = useScrvUsdRevenue({}) + + console.log(revenueData) return ( { chartOptions={chartOptions} timeOptions={timeOptions} /> - + ) } diff --git a/apps/loan/src/entities/scrvusdRevenue.ts b/apps/loan/src/entities/scrvusdRevenue.ts index 1d89265c0..1ca0b4720 100644 --- a/apps/loan/src/entities/scrvusdRevenue.ts +++ b/apps/loan/src/entities/scrvusdRevenue.ts @@ -28,6 +28,54 @@ type GetScrvUsdYieldResponse = }[] } +type Epoch = { + startDate: string + endDate: string + weeklyRevenue: number + data: ScrvUsdRevenueHistory[] +} + +const organizeDataIntoEpochs = (history: ScrvUsdRevenueHistory[]): Epoch[] => { + // Sort history by date + const sortedHistory = [...history].sort((a, b) => new Date(a.dt).getTime() - new Date(b.dt).getTime()) + + const epochs: Epoch[] = [] + let currentEpoch: Epoch | null = null + + sortedHistory.forEach((item) => { + const itemDate = new Date(item.dt) + + // If we don't have a current epoch or the item doesn't belong to current epoch + if (!currentEpoch || itemDate > new Date(currentEpoch.endDate)) { + // Find the previous Thursday if item is not on Thursday + const startDate = new Date(itemDate) + while (startDate.getDay() !== 4) { + // 4 represents Thursday + startDate.setDate(startDate.getDate() - 1) + } + + // Set end date to next Thursday + const endDate = new Date(startDate) + endDate.setDate(startDate.getDate() + 7) + + currentEpoch = { + startDate: startDate.toISOString(), + endDate: endDate.toISOString(), + weeklyRevenue: 0, + data: [], + } + epochs.push(currentEpoch) + } + + // Add item to current epoch + currentEpoch.data.push(item) + // Add to weekly revenue (gain - loss) + currentEpoch.weeklyRevenue += (+item.gain - +item.loss) / 1e18 + }) + + return epochs +} + export const _getScrvUsdRevenue = async () => { const pages = 1 const dataPerPage = 300 @@ -45,6 +93,7 @@ export const _getScrvUsdRevenue = async () => { const formattedData = { ...data, total_distributed: +data.total_distributed / 1e18, + epochs: organizeDataIntoEpochs(data.history), history: data.history.map((item) => ({ ...item, gain: +item.gain / 1e18, From 6b07ac728d7664edb1a39df189b040113ce053df Mon Sep 17 00:00:00 2001 From: JustJousting Date: Wed, 22 Jan 2025 20:06:27 +0200 Subject: [PATCH 14/18] distributions bar chart --- .../PageCrvUsdStaking/Statistics/BarChart.tsx | 69 +++++++++++++++++++ .../Statistics/LineChart.tsx | 19 +++-- .../PageCrvUsdStaking/Statistics/index.tsx | 6 +- .../src/components/PageCrvUsdStaking/utils.ts | 18 +++++ apps/loan/src/entities/scrvusdRevenue.ts | 48 +++++++++---- 5 files changed, 136 insertions(+), 24 deletions(-) create mode 100644 apps/loan/src/components/PageCrvUsdStaking/Statistics/BarChart.tsx diff --git a/apps/loan/src/components/PageCrvUsdStaking/Statistics/BarChart.tsx b/apps/loan/src/components/PageCrvUsdStaking/Statistics/BarChart.tsx new file mode 100644 index 000000000..03e5ed0ef --- /dev/null +++ b/apps/loan/src/components/PageCrvUsdStaking/Statistics/BarChart.tsx @@ -0,0 +1,69 @@ +import type { ScrvUsdRevenue } from '@loan/entities/scrvusdRevenue' +import { BarChart, Bar, Cell, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer } from 'recharts' +import { formatNumber } from '@ui/utils/utilsFormat' +import { useTheme } from '@mui/material/styles' +import { SizesAndSpaces } from '@ui-kit/themes/design/1_sizes_spaces' +import { toUTC } from '../utils' + +const { FontSize } = SizesAndSpaces + +type RevenueDistributionsBarChartProps = { + data: ScrvUsdRevenue | null + height?: number +} + +const RevenueDistributionsBarChart: React.FC = ({ data, height = 400 }) => { + const { + design: { Color, Text }, + } = useTheme() + const gridLineColor = Color.Neutral[300] + const gridTextColor = Text.TextColors.Tertiary + const barColor = Color.Secondary[500] + + return ( + + + + { + const unix = toUTC(time as string | number) * 1000 + return new Intl.DateTimeFormat(undefined, { + day: '2-digit', + month: '2-digit', + year: 'numeric', + }).format(unix) + }} + /> + `$${formatNumber(value, { notation: 'compact' })}`} + /> + {/* */} + + {data?.epochs.map((entry, index) => )} + + + + ) +} + +export default RevenueDistributionsBarChart diff --git a/apps/loan/src/components/PageCrvUsdStaking/Statistics/LineChart.tsx b/apps/loan/src/components/PageCrvUsdStaking/Statistics/LineChart.tsx index 12428856c..c013e34a8 100644 --- a/apps/loan/src/components/PageCrvUsdStaking/Statistics/LineChart.tsx +++ b/apps/loan/src/components/PageCrvUsdStaking/Statistics/LineChart.tsx @@ -4,10 +4,9 @@ import { useTheme } from '@mui/material/styles' import { t } from '@lingui/macro' import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer } from 'recharts' import CustomTooltip from './ChartTooltip' - import { formatDateFromTimestamp } from '@ui/utils/utilsFormat' +import { toUTC } from '../utils' -// import LineChartCustomTooltip from './LineChartCustomTooltip' const { FontSize } = SizesAndSpaces type Props = { @@ -16,7 +15,6 @@ type Props = { } const LineChartComponent = ({ data, height = 400 }: Props) => { - const yAxisWidth = 24 const { design: { Color, Text }, } = useTheme() @@ -33,7 +31,7 @@ const LineChartComponent = ({ data, height = 400 }: Props) => { } as const return ( - + { margin={{ top: 16, right: 16, - left: 24, + left: undefined, bottom: 16, }} > @@ -53,7 +51,14 @@ const LineChartComponent = ({ data, height = 400 }: Props) => { axisLine={false} minTickGap={20} tickMargin={4} - tickFormatter={(unixTime) => formatDateFromTimestamp(unixTime)} + tickFormatter={(time) => { + const unix = toUTC(time as string | number) * 1000 + return new Intl.DateTimeFormat(undefined, { + day: '2-digit', + month: '2-digit', + year: 'numeric', + }).format(unix) + }} /> { tickLine={{ fill: gridLineColor, strokeWidth: 0.5 }} axisLine={false} dataKey={'proj_apy'} - width={yAxisWidth} /> { fontWeight: 'bold', fontSize: 'var(--font-size-1)', color: 'var(--page--text-color)', + paddingLeft: 24, }} /> diff --git a/apps/loan/src/components/PageCrvUsdStaking/Statistics/index.tsx b/apps/loan/src/components/PageCrvUsdStaking/Statistics/index.tsx index 406df5a61..9b5f3d1fb 100644 --- a/apps/loan/src/components/PageCrvUsdStaking/Statistics/index.tsx +++ b/apps/loan/src/components/PageCrvUsdStaking/Statistics/index.tsx @@ -7,6 +7,7 @@ import ChartHeader, { ChartOption } from '@ui-kit/shared/ui/ChartHeader' import { useScrvUsdYield } from '@loan/entities/scrvusdYield' import { useScrvUsdRevenue } from '@loan/entities/scrvusdRevenue' import LineChart from './LineChart' +import BarChart from './BarChart' const { Spacing, MaxWidth } = SizesAndSpaces @@ -23,8 +24,6 @@ const Statistics = () => { const { data: yieldData } = useScrvUsdYield({ timeOption: activeTimeOption }) const { data: revenueData } = useScrvUsdRevenue({}) - console.log(revenueData) - return ( { chartOptions={chartOptions} timeOptions={timeOptions} /> - + {activeChartOption.label === 'Savings Rate' && } + {activeChartOption.label === 'Distributions' && } ) } diff --git a/apps/loan/src/components/PageCrvUsdStaking/utils.ts b/apps/loan/src/components/PageCrvUsdStaking/utils.ts index 2a9e86ea7..42c992e42 100644 --- a/apps/loan/src/components/PageCrvUsdStaking/utils.ts +++ b/apps/loan/src/components/PageCrvUsdStaking/utils.ts @@ -9,3 +9,21 @@ export const txIsSuccess = (status: TransactionStatus) => status === 'success' export const txIsError = (status: TransactionStatus) => status === 'error' export const txIsLoading = (status: TransactionStatus) => status === 'loading' export const txIsIdle = (status: TransactionStatus) => status === '' + +export function toUTC(timestamp: string | number): number { + if (typeof timestamp === 'number') { + return timestamp + } + + const parsed = Number(timestamp) + if (!Number.isNaN(parsed)) { + return parsed + } + + const [date, time] = timestamp.split('T') + const [year, month, day] = date.split('-').map(Number) + const [timeWithoutZ] = time.split('.') // Handle milliseconds by splitting on dot + const [hour, minute, second] = timeWithoutZ.split(':').map(Number) + + return Date.UTC(year, month - 1, day, hour, minute, second) / 1000 +} diff --git a/apps/loan/src/entities/scrvusdRevenue.ts b/apps/loan/src/entities/scrvusdRevenue.ts index 1ca0b4720..fd1503e03 100644 --- a/apps/loan/src/entities/scrvusdRevenue.ts +++ b/apps/loan/src/entities/scrvusdRevenue.ts @@ -1,7 +1,7 @@ import { queryFactory } from '@ui-kit/lib/model/query' import { createValidationSuite } from '@ui-kit/lib/validation' -type ScrvUsdRevenueHistory = { +type ScrvUsdRevenueHistoryFromApi = { strategy: string gain: string loss: string @@ -13,11 +13,23 @@ type ScrvUsdRevenueHistory = { dt: string } +type ScrvUsdRevenueHistory = { + strategy: string + gain: number + loss: number + current_debt: number + total_refunds: number + total_fees: number + protocol_fees: number + tx_hash: string + dt: string +} + type GetScrvUsdYieldResponse = | { count: number total_distributed: string - history: ScrvUsdRevenueHistory[] + history: ScrvUsdRevenueHistoryFromApi[] } | { detail: { @@ -35,6 +47,13 @@ type Epoch = { data: ScrvUsdRevenueHistory[] } +export type ScrvUsdRevenue = { + count: number + total_distributed: number + epochs: Epoch[] + history: ScrvUsdRevenueHistory[] +} + const organizeDataIntoEpochs = (history: ScrvUsdRevenueHistory[]): Epoch[] => { // Sort history by date const sortedHistory = [...history].sort((a, b) => new Date(a.dt).getTime() - new Date(b.dt).getTime()) @@ -67,10 +86,9 @@ const organizeDataIntoEpochs = (history: ScrvUsdRevenueHistory[]): Epoch[] => { epochs.push(currentEpoch) } - // Add item to current epoch currentEpoch.data.push(item) // Add to weekly revenue (gain - loss) - currentEpoch.weeklyRevenue += (+item.gain - +item.loss) / 1e18 + currentEpoch.weeklyRevenue += +item.gain - +item.loss }) return epochs @@ -90,19 +108,21 @@ export const _getScrvUsdRevenue = async () => { throw new Error(`Failed to fetch scrvUSD historical revenue data. ${data.detail[0].msg}`) } + const history = data.history.map((item) => ({ + ...item, + gain: +item.gain / 1e18, + loss: +item.loss / 1e18, + current_debt: +item.current_debt / 1e18, + total_refunds: +item.total_refunds / 1e18, + total_fees: +item.total_fees / 1e18, + protocol_fees: +item.protocol_fees / 1e18, + })) + const formattedData = { ...data, total_distributed: +data.total_distributed / 1e18, - epochs: organizeDataIntoEpochs(data.history), - history: data.history.map((item) => ({ - ...item, - gain: +item.gain / 1e18, - loss: +item.loss / 1e18, - current_debt: +item.current_debt / 1e18, - total_refunds: +item.total_refunds / 1e18, - total_fees: +item.total_fees / 1e18, - protocol_fees: +item.protocol_fees / 1e18, - })), + epochs: organizeDataIntoEpochs(history), + history, } return formattedData From fad34b3174781d75ec5fa46fd479a6bf88641ed8 Mon Sep 17 00:00:00 2001 From: JustJousting Date: Wed, 22 Jan 2025 20:13:02 +0200 Subject: [PATCH 15/18] chore: resolve merge from main issues --- .../loan}/components/PageCrvUsdStaking/Statistics/BarChart.tsx | 2 +- .../components/PageCrvUsdStaking/Statistics/ChartTooltip.tsx | 0 .../loan}/components/PageCrvUsdStaking/Statistics/LineChart.tsx | 2 +- .../components/PageCrvUsdStaking/Statistics/StatsStack.tsx | 0 .../src/loan}/components/PageCrvUsdStaking/Statistics/index.tsx | 0 5 files changed, 2 insertions(+), 2 deletions(-) rename apps/{loan/src => main/src/loan}/components/PageCrvUsdStaking/Statistics/BarChart.tsx (97%) rename apps/{loan/src => main/src/loan}/components/PageCrvUsdStaking/Statistics/ChartTooltip.tsx (100%) rename apps/{loan/src => main/src/loan}/components/PageCrvUsdStaking/Statistics/LineChart.tsx (98%) rename apps/{loan/src => main/src/loan}/components/PageCrvUsdStaking/Statistics/StatsStack.tsx (100%) rename apps/{loan/src => main/src/loan}/components/PageCrvUsdStaking/Statistics/index.tsx (100%) diff --git a/apps/loan/src/components/PageCrvUsdStaking/Statistics/BarChart.tsx b/apps/main/src/loan/components/PageCrvUsdStaking/Statistics/BarChart.tsx similarity index 97% rename from apps/loan/src/components/PageCrvUsdStaking/Statistics/BarChart.tsx rename to apps/main/src/loan/components/PageCrvUsdStaking/Statistics/BarChart.tsx index 03e5ed0ef..012826f61 100644 --- a/apps/loan/src/components/PageCrvUsdStaking/Statistics/BarChart.tsx +++ b/apps/main/src/loan/components/PageCrvUsdStaking/Statistics/BarChart.tsx @@ -3,7 +3,7 @@ import { BarChart, Bar, Cell, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveCo import { formatNumber } from '@ui/utils/utilsFormat' import { useTheme } from '@mui/material/styles' import { SizesAndSpaces } from '@ui-kit/themes/design/1_sizes_spaces' -import { toUTC } from '../utils' +import { toUTC } from '@loan/components/PageCrvUsdStaking/utils' const { FontSize } = SizesAndSpaces diff --git a/apps/loan/src/components/PageCrvUsdStaking/Statistics/ChartTooltip.tsx b/apps/main/src/loan/components/PageCrvUsdStaking/Statistics/ChartTooltip.tsx similarity index 100% rename from apps/loan/src/components/PageCrvUsdStaking/Statistics/ChartTooltip.tsx rename to apps/main/src/loan/components/PageCrvUsdStaking/Statistics/ChartTooltip.tsx diff --git a/apps/loan/src/components/PageCrvUsdStaking/Statistics/LineChart.tsx b/apps/main/src/loan/components/PageCrvUsdStaking/Statistics/LineChart.tsx similarity index 98% rename from apps/loan/src/components/PageCrvUsdStaking/Statistics/LineChart.tsx rename to apps/main/src/loan/components/PageCrvUsdStaking/Statistics/LineChart.tsx index c013e34a8..777dd104e 100644 --- a/apps/loan/src/components/PageCrvUsdStaking/Statistics/LineChart.tsx +++ b/apps/main/src/loan/components/PageCrvUsdStaking/Statistics/LineChart.tsx @@ -5,7 +5,7 @@ import { t } from '@lingui/macro' import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer } from 'recharts' import CustomTooltip from './ChartTooltip' import { formatDateFromTimestamp } from '@ui/utils/utilsFormat' -import { toUTC } from '../utils' +import { toUTC } from '@loan/components/PageCrvUsdStaking/utils' const { FontSize } = SizesAndSpaces diff --git a/apps/loan/src/components/PageCrvUsdStaking/Statistics/StatsStack.tsx b/apps/main/src/loan/components/PageCrvUsdStaking/Statistics/StatsStack.tsx similarity index 100% rename from apps/loan/src/components/PageCrvUsdStaking/Statistics/StatsStack.tsx rename to apps/main/src/loan/components/PageCrvUsdStaking/Statistics/StatsStack.tsx diff --git a/apps/loan/src/components/PageCrvUsdStaking/Statistics/index.tsx b/apps/main/src/loan/components/PageCrvUsdStaking/Statistics/index.tsx similarity index 100% rename from apps/loan/src/components/PageCrvUsdStaking/Statistics/index.tsx rename to apps/main/src/loan/components/PageCrvUsdStaking/Statistics/index.tsx From e0cd0e81d604b4098b2b9070bc77141433128447 Mon Sep 17 00:00:00 2001 From: JustJousting Date: Wed, 22 Jan 2025 23:46:20 +0200 Subject: [PATCH 16/18] distributions bar chart tooltip --- ...BarChart.tsx => DistributionsBarChart.tsx} | 3 +- .../Statistics/DistributionsChartTooltip.tsx | 61 +++++++++++++++++++ ...artTooltip.tsx => RevenueChartTooltip.tsx} | 14 ++++- .../{LineChart.tsx => RevenueLineChart.tsx} | 13 ++-- .../PageCrvUsdStaking/Statistics/index.tsx | 8 +-- 5 files changed, 85 insertions(+), 14 deletions(-) rename apps/main/src/loan/components/PageCrvUsdStaking/Statistics/{BarChart.tsx => DistributionsBarChart.tsx} (94%) create mode 100644 apps/main/src/loan/components/PageCrvUsdStaking/Statistics/DistributionsChartTooltip.tsx rename apps/main/src/loan/components/PageCrvUsdStaking/Statistics/{ChartTooltip.tsx => RevenueChartTooltip.tsx} (84%) rename apps/main/src/loan/components/PageCrvUsdStaking/Statistics/{LineChart.tsx => RevenueLineChart.tsx} (91%) diff --git a/apps/main/src/loan/components/PageCrvUsdStaking/Statistics/BarChart.tsx b/apps/main/src/loan/components/PageCrvUsdStaking/Statistics/DistributionsBarChart.tsx similarity index 94% rename from apps/main/src/loan/components/PageCrvUsdStaking/Statistics/BarChart.tsx rename to apps/main/src/loan/components/PageCrvUsdStaking/Statistics/DistributionsBarChart.tsx index 012826f61..22290638e 100644 --- a/apps/main/src/loan/components/PageCrvUsdStaking/Statistics/BarChart.tsx +++ b/apps/main/src/loan/components/PageCrvUsdStaking/Statistics/DistributionsBarChart.tsx @@ -4,6 +4,7 @@ import { formatNumber } from '@ui/utils/utilsFormat' import { useTheme } from '@mui/material/styles' import { SizesAndSpaces } from '@ui-kit/themes/design/1_sizes_spaces' import { toUTC } from '@loan/components/PageCrvUsdStaking/utils' +import DistributionsChartTooltip from './DistributionsChartTooltip' const { FontSize } = SizesAndSpaces @@ -57,7 +58,7 @@ const RevenueDistributionsBarChart: React.FC axisLine={{ opacity: 0.3, strokeWidth: 0.3 }} tickFormatter={(value) => `$${formatNumber(value, { notation: 'compact' })}`} /> - {/* */} + {data?.epochs.map((entry, index) => )} diff --git a/apps/main/src/loan/components/PageCrvUsdStaking/Statistics/DistributionsChartTooltip.tsx b/apps/main/src/loan/components/PageCrvUsdStaking/Statistics/DistributionsChartTooltip.tsx new file mode 100644 index 000000000..7634f1f8d --- /dev/null +++ b/apps/main/src/loan/components/PageCrvUsdStaking/Statistics/DistributionsChartTooltip.tsx @@ -0,0 +1,61 @@ +import type { NameType, ValueType } from 'recharts/types/component/DefaultTooltipContent' +import { TooltipProps } from 'recharts' +import { t } from '@lingui/macro' +import { Paper, Stack, Typography } from '@mui/material' +import { SizesAndSpaces } from '@ui-kit/themes/design/1_sizes_spaces' +import { formatNumber } from '@ui/utils/utilsFormat' +import { toUTC } from '@loan/components/PageCrvUsdStaking/utils' + +const { Spacing } = SizesAndSpaces + +const DataSet = ({ label, value }: { label: string; value: string }) => ( + + {label} + {value} + +) + +const DistributionsChartTooltip = ({ active, payload }: TooltipProps) => { + if (active && payload && payload.length) { + const { endDate, weeklyRevenue } = payload[0].payload + + const unixTimestamp = toUTC(endDate) * 1000 + const formattedDate = new Intl.DateTimeFormat(undefined, { + month: 'short', + day: 'numeric', + year: 'numeric', + hour: 'numeric', + minute: '2-digit', + hour12: true, + }).format(unixTimestamp) + + return ( + theme.design.Layer[3].Fill, + padding: Spacing.md, + width: '20.5rem', + maxWidth: '100vw', + }} + elevation={2} + > + {formattedDate} + theme.design.Layer[2].Fill, + }} + > + + + + ) + } + + return null +} + +export default DistributionsChartTooltip diff --git a/apps/main/src/loan/components/PageCrvUsdStaking/Statistics/ChartTooltip.tsx b/apps/main/src/loan/components/PageCrvUsdStaking/Statistics/RevenueChartTooltip.tsx similarity index 84% rename from apps/main/src/loan/components/PageCrvUsdStaking/Statistics/ChartTooltip.tsx rename to apps/main/src/loan/components/PageCrvUsdStaking/Statistics/RevenueChartTooltip.tsx index ff8990307..a0ca241b1 100644 --- a/apps/main/src/loan/components/PageCrvUsdStaking/Statistics/ChartTooltip.tsx +++ b/apps/main/src/loan/components/PageCrvUsdStaking/Statistics/RevenueChartTooltip.tsx @@ -3,8 +3,8 @@ import { TooltipProps } from 'recharts' import { t } from '@lingui/macro' import { Paper, Stack, Typography } from '@mui/material' import { SizesAndSpaces } from '@ui-kit/themes/design/1_sizes_spaces' -import { formatDateFromTimestamp } from '@ui/utils/utilsFormat' import { useTheme } from '@mui/material/styles' +import { toUTC } from '@loan/components/PageCrvUsdStaking/utils' const { Spacing } = SizesAndSpaces @@ -38,6 +38,16 @@ const CustomTooltip = ({ active, payload }: TooltipProps) = if (active && payload && payload.length) { const { timestamp, proj_apy, proj_apy_7d_avg, proj_apy_total_avg } = payload[0].payload + const unixTimestamp = toUTC(timestamp) * 1000 + const formattedDate = new Intl.DateTimeFormat(undefined, { + month: 'short', + day: 'numeric', + year: 'numeric', + hour: 'numeric', + minute: '2-digit', + hour12: true, + }).format(unixTimestamp) + return ( ) = }} elevation={2} > - {formatDateFromTimestamp(timestamp)} + {formattedDate} { axisLine={false} dataKey={'proj_apy'} /> - + { height={32} formatter={(value: keyof typeof labels) => labels[value].text} wrapperStyle={{ - fontWeight: 'bold', - fontSize: 'var(--font-size-1)', - color: 'var(--page--text-color)', + fontWeight: FontWeight.Medium, + fontSize: FontSize.sm.desktop, + color: Text.TextColors.Secondary, paddingLeft: 24, }} /> diff --git a/apps/main/src/loan/components/PageCrvUsdStaking/Statistics/index.tsx b/apps/main/src/loan/components/PageCrvUsdStaking/Statistics/index.tsx index 9b5f3d1fb..027e4c3c8 100644 --- a/apps/main/src/loan/components/PageCrvUsdStaking/Statistics/index.tsx +++ b/apps/main/src/loan/components/PageCrvUsdStaking/Statistics/index.tsx @@ -6,8 +6,8 @@ import StatsStack from './StatsStack' import ChartHeader, { ChartOption } from '@ui-kit/shared/ui/ChartHeader' import { useScrvUsdYield } from '@loan/entities/scrvusdYield' import { useScrvUsdRevenue } from '@loan/entities/scrvusdRevenue' -import LineChart from './LineChart' -import BarChart from './BarChart' +import RevenueLineChart from './RevenueLineChart' +import DistributionsBarChart from './DistributionsBarChart' const { Spacing, MaxWidth } = SizesAndSpaces @@ -47,8 +47,8 @@ const Statistics = () => { chartOptions={chartOptions} timeOptions={timeOptions} /> - {activeChartOption.label === 'Savings Rate' && } - {activeChartOption.label === 'Distributions' && } + {activeChartOption.label === 'Savings Rate' && } + {activeChartOption.label === 'Distributions' && } ) } From e9fada331aef73fe2f466a573f82a77b8368127d Mon Sep 17 00:00:00 2001 From: JustJousting Date: Wed, 22 Jan 2025 23:46:32 +0200 Subject: [PATCH 17/18] weekly accum revenue stat --- .../components/PageCrvUsdStaking/Statistics/StatsStack.tsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/apps/main/src/loan/components/PageCrvUsdStaking/Statistics/StatsStack.tsx b/apps/main/src/loan/components/PageCrvUsdStaking/Statistics/StatsStack.tsx index 6e66e3293..702cdad96 100644 --- a/apps/main/src/loan/components/PageCrvUsdStaking/Statistics/StatsStack.tsx +++ b/apps/main/src/loan/components/PageCrvUsdStaking/Statistics/StatsStack.tsx @@ -32,7 +32,12 @@ const StatsStack = () => { loading={revenueIsFetching} unit="dollar" /> - {/* */} + ) } From f22db8cbe6345dd51c2e88b7b793ac41468ea01d Mon Sep 17 00:00:00 2001 From: JustJousting Date: Mon, 27 Jan 2025 18:14:27 +0200 Subject: [PATCH 18/18] set up advanced section --- .../Statistics/AdvancedDetails.tsx | 28 +++++++++ .../PageCrvUsdStaking/Statistics/index.tsx | 63 +++++++++++-------- .../src/shared/ui/ChartHeader.tsx | 9 ++- .../curve-ui-kit/src/themes/components.ts | 24 ++++++- .../src/themes/mui-overrides.d.ts | 6 ++ 5 files changed, 99 insertions(+), 31 deletions(-) create mode 100644 apps/main/src/loan/components/PageCrvUsdStaking/Statistics/AdvancedDetails.tsx diff --git a/apps/main/src/loan/components/PageCrvUsdStaking/Statistics/AdvancedDetails.tsx b/apps/main/src/loan/components/PageCrvUsdStaking/Statistics/AdvancedDetails.tsx new file mode 100644 index 000000000..ac48f6972 --- /dev/null +++ b/apps/main/src/loan/components/PageCrvUsdStaking/Statistics/AdvancedDetails.tsx @@ -0,0 +1,28 @@ +import { Card, CardHeader, Stack, Typography } from '@mui/material' +import { SizesAndSpaces } from '@ui-kit/themes/design/1_sizes_spaces' +import { t } from '@lingui/macro' + +const { Spacing } = SizesAndSpaces + +const AdvancedDetails = () => ( + + + + {t`Advanced Details`} + + +) + +export default AdvancedDetails diff --git a/apps/main/src/loan/components/PageCrvUsdStaking/Statistics/index.tsx b/apps/main/src/loan/components/PageCrvUsdStaking/Statistics/index.tsx index 027e4c3c8..ddcf50551 100644 --- a/apps/main/src/loan/components/PageCrvUsdStaking/Statistics/index.tsx +++ b/apps/main/src/loan/components/PageCrvUsdStaking/Statistics/index.tsx @@ -1,6 +1,6 @@ import type { TimeOption } from '@ui-kit/lib/types/scrvusd' import { SizesAndSpaces } from '@ui-kit/themes/design/1_sizes_spaces' -import { Card, CardHeader, Box } from '@mui/material' +import { Stack, Card, CardHeader, Box } from '@mui/material' import { useState } from 'react' import StatsStack from './StatsStack' import ChartHeader, { ChartOption } from '@ui-kit/shared/ui/ChartHeader' @@ -8,6 +8,7 @@ import { useScrvUsdYield } from '@loan/entities/scrvusdYield' import { useScrvUsdRevenue } from '@loan/entities/scrvusdRevenue' import RevenueLineChart from './RevenueLineChart' import DistributionsBarChart from './DistributionsBarChart' +import AdvancedDetails from './AdvancedDetails' const { Spacing, MaxWidth } = SizesAndSpaces @@ -25,31 +26,41 @@ const Statistics = () => { const { data: revenueData } = useScrvUsdRevenue({}) return ( - t.design.Layer[1].Fill, - width: '100%', - maxWidth: MaxWidth.statistics, - }} - > - - - - - - {activeChartOption.label === 'Savings Rate' && } - {activeChartOption.label === 'Distributions' && } - + + t.design.Layer[1].Fill, + width: '100%', + maxWidth: MaxWidth.statistics, + }} + > + + + + + + {activeChartOption.label === 'Savings Rate' && } + {activeChartOption.label === 'Distributions' && } + + + ) } diff --git a/packages/curve-ui-kit/src/shared/ui/ChartHeader.tsx b/packages/curve-ui-kit/src/shared/ui/ChartHeader.tsx index 99a462284..334a4509d 100644 --- a/packages/curve-ui-kit/src/shared/ui/ChartHeader.tsx +++ b/packages/curve-ui-kit/src/shared/ui/ChartHeader.tsx @@ -57,9 +57,14 @@ const ChartHeader = ({ - {timeOptions.map((option) => ( - + {option} ))} diff --git a/packages/curve-ui-kit/src/themes/components.ts b/packages/curve-ui-kit/src/themes/components.ts index b002ed7f0..f4a61e281 100644 --- a/packages/curve-ui-kit/src/themes/components.ts +++ b/packages/curve-ui-kit/src/themes/components.ts @@ -30,11 +30,29 @@ export const createComponents = (design: DesignSystem, typography: TypographyOpt paddingBlock: SizesAndSpaces.Spacing.sm.desktop + ' 0', paddingInline: SizesAndSpaces.Spacing.md.desktop + ' 0', borderBottom: `1px solid ${design.Layer[3].Outline}`, - height: '4rem', - minHeight: `calc(${SizesAndSpaces.ButtonSize.lg} + 1px)`, // 1px for the border + minHeight: `calc(${SizesAndSpaces.ButtonSize.lg} + 1px)`, + variants: [ + { + props: { size: 'small' }, // todo: currently not working, applying styles inline + style: { + minHeight: SizesAndSpaces.Sizing.md.desktop, + padding: `0 ${SizesAndSpaces.Spacing.md.desktop} ${SizesAndSpaces.Spacing.sm.desktop}`, + }, + }, + ], }, action: { alignContent: 'center', margin: 0 }, - title: { ...typography.headingSBold }, + title: { + ...typography.headingSBold, + variants: [ + { + props: { size: 'small' }, + style: { + ...typography.headingXsBold, + }, + }, + ], + }, }, }, MuiCardActions: { diff --git a/packages/curve-ui-kit/src/themes/mui-overrides.d.ts b/packages/curve-ui-kit/src/themes/mui-overrides.d.ts index 1dbdbc568..0e93ffe3e 100644 --- a/packages/curve-ui-kit/src/themes/mui-overrides.d.ts +++ b/packages/curve-ui-kit/src/themes/mui-overrides.d.ts @@ -14,3 +14,9 @@ declare module '@mui/material/styles' { highlight: string } } + +declare module '@mui/material/CardHeader' { + interface CardHeaderPropsVariantOverrides { + size?: true + } +}