diff --git a/apps/earn-protocol/app/earn/portfolio/[walletAddress]/page.tsx b/apps/earn-protocol/app/earn/portfolio/[walletAddress]/page.tsx index 4b7e67ac2..3a3289c90 100644 --- a/apps/earn-protocol/app/earn/portfolio/[walletAddress]/page.tsx +++ b/apps/earn-protocol/app/earn/portfolio/[walletAddress]/page.tsx @@ -1,3 +1,7 @@ +import { parseServerResponseToClient } from '@summerfi/app-utils' +import { type IArmadaPosition } from '@summerfi/sdk-client-react' + +import { portfolioPositionsHandler } from '@/app/server-handlers/portfolio/portfolio-positions-handler' import { portfolioRewardsHandler } from '@/app/server-handlers/portfolio/portfolio-rewards-handler' import { portfolioWalletAssetsHandler } from '@/app/server-handlers/portfolio/portfolio-wallet-assets-handler' import { getUserPositions } from '@/app/server-handlers/sdk/get-user-positions' @@ -20,9 +24,17 @@ const PortfolioPage = async ({ params }: PortfolioPageProps) => { ]) const rewardsData = portfolioRewardsHandler(walletAddress) + const positionsJsonSafe = positions + ? parseServerResponseToClient(positions) + : [] + + const positionsList = positionsJsonSafe.map((position) => + portfolioPositionsHandler({ position, vaultsList: vaults }), + ) + return ( { + const vaultData = vaultsList.find((vault) => vault.id === position.pool.id.fleetAddress.value) + + if (!vaultData) { + throw new Error(`Vault not found for position ${position.pool.id.fleetAddress.value}`) + } + + return { + positionData: position, + vaultData, + } +} + +export type PortfolioPositionsList = ReturnType diff --git a/apps/earn-protocol/components/layout/PortfolioPageView/PortfolioPageView.tsx b/apps/earn-protocol/components/layout/PortfolioPageView/PortfolioPageView.tsx index 366ec5486..0cc830694 100644 --- a/apps/earn-protocol/components/layout/PortfolioPageView/PortfolioPageView.tsx +++ b/apps/earn-protocol/components/layout/PortfolioPageView/PortfolioPageView.tsx @@ -3,8 +3,8 @@ import { type FC } from 'react' import { TabBar } from '@summerfi/app-earn-ui' import { type SDKVaultsListType } from '@summerfi/app-types' -import { type IArmadaPosition } from '@summerfi/sdk-client-react' +import { type PortfolioPositionsList } from '@/app/server-handlers/portfolio/portfolio-positions-handler' import { type PortfolioRewardsRawData } from '@/app/server-handlers/portfolio/portfolio-rewards-handler' import { type PortfolioAssetsResponse } from '@/app/server-handlers/portfolio/portfolio-wallet-assets-handler' import { PortfolioHeader } from '@/features/portfolio/components/PortfolioHeader/PortfolioHeader' @@ -18,7 +18,7 @@ interface PortfolioPageViewProps { walletData: PortfolioAssetsResponse rewardsData: PortfolioRewardsRawData[] vaultsList: SDKVaultsListType - positions: IArmadaPosition[] + positions: PortfolioPositionsList[] } export const PortfolioPageView: FC = ({ diff --git a/apps/earn-protocol/components/organisms/Charts/ComparisonChart.tsx b/apps/earn-protocol/components/organisms/Charts/DumbCharts/ComparisonChart.tsx similarity index 97% rename from apps/earn-protocol/components/organisms/Charts/ComparisonChart.tsx rename to apps/earn-protocol/components/organisms/Charts/DumbCharts/ComparisonChart.tsx index 1f747d811..13e5d5245 100644 --- a/apps/earn-protocol/components/organisms/Charts/ComparisonChart.tsx +++ b/apps/earn-protocol/components/organisms/Charts/DumbCharts/ComparisonChart.tsx @@ -55,7 +55,7 @@ export const ComparisonChart = ({ data, dataNames, colors, compare }: Comparison dataName === 'Summer Strategy' ? ( { + return ( +
+ + + + + + + + + + + `${label}%`} /> + `${Number(val).toFixed(2)}`} + useTranslate3d + contentStyle={{ + zIndex: 1000, + backgroundColor: 'var(--color-surface-subtler)', + borderRadius: '5px', + padding: '20px 30px', + border: 'none', + }} + /> + {dataNames.map((dataName, dataIndex) => + dataName === 'Summer Strategy' ? ( + + ) : ( + + ), + )} + + + +
+ ) +} diff --git a/apps/earn-protocol/components/organisms/Charts/HistoricalYieldChart.tsx b/apps/earn-protocol/components/organisms/Charts/HistoricalYieldChart.tsx index d1e0336aa..cfa1e3db9 100644 --- a/apps/earn-protocol/components/organisms/Charts/HistoricalYieldChart.tsx +++ b/apps/earn-protocol/components/organisms/Charts/HistoricalYieldChart.tsx @@ -1,14 +1,11 @@ 'use client' import { useMemo, useState } from 'react' -import { Card } from '@summerfi/app-earn-ui' -import { type CardVariant } from '@summerfi/app-earn-ui/dist/types/src/components/atoms/Card/Card' import { type TimeframesType } from '@summerfi/app-types' import BigNumber from 'bignumber.js' import dayjs from 'dayjs' -import { ChartHeader } from '@/components/organisms/Charts/ChartHeader' -import { ComparisonChart } from '@/components/organisms/Charts/ComparisonChart' +import { YieldsChart } from '@/components/organisms/Charts/DumbCharts/YieldsChart' const dataNames = ['Summer Strategy'] @@ -16,14 +13,7 @@ const colors = { 'Summer Strategy-color': '#FF49A4', } -export const HistoricalYieldChart = ({ - aprHourlyList, - cardVariant = 'cardSecondary', -}: { - cardVariant?: CardVariant - aprHourlyList: string[] -}) => { - const [compare, setCompare] = useState(true) +export const HistoricalYieldChart = ({ aprHourlyList }: { aprHourlyList: string[] }) => { const [timeframe, setTimeframe] = useState('90d') const _unused = setTimeframe @@ -40,23 +30,6 @@ export const HistoricalYieldChart = ({ }, [aprHourlyList]) return ( - - null} - /> - - + ) } diff --git a/apps/earn-protocol/components/organisms/Charts/MockedLineChart.tsx b/apps/earn-protocol/components/organisms/Charts/MockedLineChart.tsx index cbcf5b1b8..724b1d054 100644 --- a/apps/earn-protocol/components/organisms/Charts/MockedLineChart.tsx +++ b/apps/earn-protocol/components/organisms/Charts/MockedLineChart.tsx @@ -7,7 +7,7 @@ import { type TimeframesType } from '@summerfi/app-types' import { memoize, random } from 'lodash-es' import { ChartHeader } from '@/components/organisms/Charts/ChartHeader' -import { ComparisonChart } from '@/components/organisms/Charts/ComparisonChart' +import { ComparisonChart } from '@/components/organisms/Charts/DumbCharts/ComparisonChart' const dataNames = [ 'Median Defi Yield', diff --git a/apps/earn-protocol/features/portfolio/components/PortfolioOverview/PortfolioOverview.tsx b/apps/earn-protocol/features/portfolio/components/PortfolioOverview/PortfolioOverview.tsx index b68d699c6..3cc07171d 100644 --- a/apps/earn-protocol/features/portfolio/components/PortfolioOverview/PortfolioOverview.tsx +++ b/apps/earn-protocol/features/portfolio/components/PortfolioOverview/PortfolioOverview.tsx @@ -1,9 +1,9 @@ -import { Card, DataBlock, Text, WithArrow } from '@summerfi/app-earn-ui' +import { Card, DataBlock, PortfolioPosition, Text, WithArrow } from '@summerfi/app-earn-ui' import { type SDKVaultsListType } from '@summerfi/app-types' -import { type IArmadaPosition } from '@summerfi/sdk-client-react' import Link from 'next/link' -import { MockedLineChart } from '@/components/organisms/Charts/MockedLineChart' +import { type PortfolioPositionsList } from '@/app/server-handlers/portfolio/portfolio-positions-handler' +import { HistoricalYieldChart } from '@/components/organisms/Charts/HistoricalYieldChart' import { CryptoUtilities } from '@/features/crypto-utilities/components/CryptoUtilities/CryptoUtilities' import { NewsAndUpdates } from '@/features/news-and-updates/components/NewsAndUpdates/NewsAndUpdates' import { PortfolioVaultsCarousel } from '@/features/portfolio/components/PortfolioVaultsCarousel/PortfolioVaultsCarousel' @@ -57,7 +57,7 @@ const dataBlocks = [ type PortfolioOverviewProps = { vaultsList: SDKVaultsListType - positions: IArmadaPosition[] + positions: PortfolioPositionsList[] } export const PortfolioOverview = ({ vaultsList, positions }: PortfolioOverviewProps) => { @@ -86,7 +86,21 @@ export const PortfolioOverview = ({ vaultsList, positions }: PortfolioOverviewPr Positions - + {positions.length > 0 ? ( + positions.map((position) => ( + + } + /> + )) + ) : ( + + No positions + + )} -
{JSON.stringify(positions, null, 2)}
) diff --git a/apps/earn-protocol/features/rebalance-activity/components/RebalanceActivityTable/RebalanceActivityTable.tsx b/apps/earn-protocol/features/rebalance-activity/components/RebalanceActivityTable/RebalanceActivityTable.tsx index 1a409f9e9..8eb11f8d3 100644 --- a/apps/earn-protocol/features/rebalance-activity/components/RebalanceActivityTable/RebalanceActivityTable.tsx +++ b/apps/earn-protocol/features/rebalance-activity/components/RebalanceActivityTable/RebalanceActivityTable.tsx @@ -1,8 +1,11 @@ import { type FC, type ReactNode, useMemo, useState } from 'react' -import { Table, type TableSortedColumn } from '@summerfi/app-earn-ui' +import { Table, type TableSortedColumn, useMobileCheck } from '@summerfi/app-earn-ui' import { type SDKGlobalRebalancesType } from '@summerfi/app-types' -import { rebalancingActivityColumns } from '@/features/rebalance-activity/table/columns' +import { + rebalancingActivityColumns, + rebalancingActivityColumnsHiddenOnMobile, +} from '@/features/rebalance-activity/table/columns' import { rebalancingActivityMapper } from '@/features/rebalance-activity/table/mapper' interface RebalanceActivityTableProps { @@ -22,19 +25,22 @@ export const RebalanceActivityTable: FC = ({ rowsToDisplay, }) => { const [sortConfig, setSortConfig] = useState>() + const { isMobile } = useMobileCheck() const rows = useMemo( () => rebalancingActivityMapper(rebalancesList, sortConfig), [rebalancesList, sortConfig], ) + const resolvedHiddenColumns = isMobile ? rebalancingActivityColumnsHiddenOnMobile : hiddenColumns + return ( setSortConfig(_sortConfig)} - hiddenColumns={hiddenColumns} + hiddenColumns={resolvedHiddenColumns} /> ) } diff --git a/apps/earn-protocol/features/rebalance-activity/components/RebalanceActivityView/RebalanceActivityView.module.scss b/apps/earn-protocol/features/rebalance-activity/components/RebalanceActivityView/RebalanceActivityView.module.scss index e0a50f79a..a98791c06 100644 --- a/apps/earn-protocol/features/rebalance-activity/components/RebalanceActivityView/RebalanceActivityView.module.scss +++ b/apps/earn-protocol/features/rebalance-activity/components/RebalanceActivityView/RebalanceActivityView.module.scss @@ -10,8 +10,12 @@ .filtersWrapper { display: flex; - justify-content: flex-start; + flex-direction: column; gap: var(--general-space-16); - flex-wrap: wrap; margin-bottom: var(--general-space-24); + + @media screen and (min-width: 768px) { + flex-direction: row; + justify-content: flex-start; + } } diff --git a/apps/earn-protocol/features/rebalance-activity/components/RebalanceActivityView/RebalanceActivityView.tsx b/apps/earn-protocol/features/rebalance-activity/components/RebalanceActivityView/RebalanceActivityView.tsx index 6c645a689..45e63ef86 100644 --- a/apps/earn-protocol/features/rebalance-activity/components/RebalanceActivityView/RebalanceActivityView.tsx +++ b/apps/earn-protocol/features/rebalance-activity/components/RebalanceActivityView/RebalanceActivityView.tsx @@ -7,6 +7,7 @@ import { HeadingWithCards, TableCarousel, useCurrentUrl, + useMobileCheck, useQueryParams, } from '@summerfi/app-earn-ui' import { type SDKGlobalRebalancesType, type SDKVaultsListType } from '@summerfi/app-types' @@ -42,6 +43,7 @@ export const RebalanceActivityView: FC = ({ const [tokenFilter, setTokenFilter] = useState(searchParams?.tokens ?? []) const [protocolFilter, setProtocolFilter] = useState(searchParams?.protocols ?? []) const currentUrl = useCurrentUrl() + const { isMobile } = useMobileCheck() const [current, setCurrent] = useState(initialRows) @@ -134,6 +136,7 @@ export const RebalanceActivityView: FC = ({ label={filter.label} onChange={filter.onChange} initialValues={filter.initialValues} + style={{ width: isMobile ? '100%' : 'fit-content' }} /> ))} diff --git a/apps/earn-protocol/features/rebalance-activity/table/columns.ts b/apps/earn-protocol/features/rebalance-activity/table/columns.ts index d53e039f2..39d553e66 100644 --- a/apps/earn-protocol/features/rebalance-activity/table/columns.ts +++ b/apps/earn-protocol/features/rebalance-activity/table/columns.ts @@ -30,3 +30,10 @@ export const rebalancingActivityColumns = [ sortable: false, }, ] + +export const rebalancingActivityColumnsHiddenOnMobile = [ + 'amount', + 'strategy', + 'timestamp', + 'provider', +] diff --git a/apps/earn-protocol/features/user-activity/components/TopDepositorsTable/TopDepositorsTable.tsx b/apps/earn-protocol/features/user-activity/components/TopDepositorsTable/TopDepositorsTable.tsx index b4e07adf4..d30ab866a 100644 --- a/apps/earn-protocol/features/user-activity/components/TopDepositorsTable/TopDepositorsTable.tsx +++ b/apps/earn-protocol/features/user-activity/components/TopDepositorsTable/TopDepositorsTable.tsx @@ -1,8 +1,11 @@ import { type FC, type ReactNode, useMemo, useState } from 'react' -import { Table, type TableSortedColumn } from '@summerfi/app-earn-ui' +import { Table, type TableSortedColumn, useMobileCheck } from '@summerfi/app-earn-ui' import { type SDKUsersActivityType } from '@summerfi/app-types' -import { topDepositorsColumns } from '@/features/user-activity/table/top-depositors-columns' +import { + topDepositorsColumns, + topDepositorsColumnsHiddenOnMobile, +} from '@/features/user-activity/table/top-depositors-columns' import { topDepositorsMapper } from '@/features/user-activity/table/top-depositors-mapper' interface TopDepositorsTableProps { @@ -22,19 +25,22 @@ export const TopDepositorsTable: FC = ({ rowsToDisplay, }) => { const [sortConfig, setSortConfig] = useState>() + const { isMobile } = useMobileCheck() const rows = useMemo( () => topDepositorsMapper(topDepositorsList, sortConfig), [topDepositorsList, sortConfig], ) + const resolvedHiddenColumns = isMobile ? topDepositorsColumnsHiddenOnMobile : hiddenColumns + return (
setSortConfig(_sortConfig)} - hiddenColumns={hiddenColumns} + hiddenColumns={resolvedHiddenColumns} /> ) } diff --git a/apps/earn-protocol/features/user-activity/components/UserActivityTable/UserActivityTable.tsx b/apps/earn-protocol/features/user-activity/components/UserActivityTable/UserActivityTable.tsx index 4d42df97b..f27d920b2 100644 --- a/apps/earn-protocol/features/user-activity/components/UserActivityTable/UserActivityTable.tsx +++ b/apps/earn-protocol/features/user-activity/components/UserActivityTable/UserActivityTable.tsx @@ -1,8 +1,11 @@ import { type FC, type ReactNode, useMemo, useState } from 'react' -import { Table, type TableSortedColumn } from '@summerfi/app-earn-ui' +import { Table, type TableSortedColumn, useMobileCheck } from '@summerfi/app-earn-ui' import { type UsersActivity } from '@summerfi/app-types' -import { userActivityColumns } from '@/features/user-activity/table/user-activity-columns' +import { + userActivityColumns, + userActivityColumnsHiddenOnMobile, +} from '@/features/user-activity/table/user-activity-columns' import { userActivityMapper } from '@/features/user-activity/table/user-activity-mapper' interface UserActivityTableProps { @@ -22,19 +25,22 @@ export const UserActivityTable: FC = ({ rowsToDisplay, }) => { const [sortConfig, setSortConfig] = useState>() + const { isMobile } = useMobileCheck() const rows = useMemo( () => userActivityMapper(userActivityList, sortConfig), [userActivityList, sortConfig], ) + const resolvedHiddenColumns = isMobile ? userActivityColumnsHiddenOnMobile : hiddenColumns + return (
setSortConfig(_sortConfig)} - hiddenColumns={hiddenColumns} + hiddenColumns={resolvedHiddenColumns} /> ) } diff --git a/apps/earn-protocol/features/user-activity/components/UserActivityView/UserActivityView.module.scss b/apps/earn-protocol/features/user-activity/components/UserActivityView/UserActivityView.module.scss index e0a50f79a..2ce99a283 100644 --- a/apps/earn-protocol/features/user-activity/components/UserActivityView/UserActivityView.module.scss +++ b/apps/earn-protocol/features/user-activity/components/UserActivityView/UserActivityView.module.scss @@ -10,8 +10,14 @@ .filtersWrapper { display: flex; - justify-content: flex-start; + flex-direction: column; gap: var(--general-space-16); - flex-wrap: wrap; margin-bottom: var(--general-space-24); + margin-top: var(--general-space-12); + + @media screen and (min-width: 768px) { + margin-top: unset; + flex-direction: row; + justify-content: flex-start; + } } diff --git a/apps/earn-protocol/features/user-activity/components/UserActivityView/UserActivityView.tsx b/apps/earn-protocol/features/user-activity/components/UserActivityView/UserActivityView.tsx index 9651f6f99..0b2ffe29a 100644 --- a/apps/earn-protocol/features/user-activity/components/UserActivityView/UserActivityView.tsx +++ b/apps/earn-protocol/features/user-activity/components/UserActivityView/UserActivityView.tsx @@ -8,6 +8,7 @@ import { TabBar, TableCarousel, useCurrentUrl, + useMobileCheck, useQueryParams, } from '@summerfi/app-earn-ui' import { @@ -53,6 +54,7 @@ export const UserActivityView: FC = ({ const [strategyFilter, setStrategyFilter] = useState(searchParams?.strategies ?? []) const [tokenFilter, setTokenFilter] = useState(searchParams?.tokens ?? []) const currentUrl = useCurrentUrl() + const { isMobile } = useMobileCheck() const [currentUserActivityIdx, setCurrentUserActivityIdx] = useState(initialRows) const [currentTopDepositorsIdx, setCurrentTopDepositorsIdx] = useState(initialRows) @@ -152,6 +154,7 @@ export const UserActivityView: FC = ({ label={filter.label} onChange={filter.onChange} initialValues={filter.initialValues} + style={{ width: isMobile ? '100%' : 'fit-content' }} /> ))} diff --git a/apps/earn-protocol/features/user-activity/table/top-depositors-columns.ts b/apps/earn-protocol/features/user-activity/table/top-depositors-columns.ts index e3154f139..4a56a8269 100644 --- a/apps/earn-protocol/features/user-activity/table/top-depositors-columns.ts +++ b/apps/earn-protocol/features/user-activity/table/top-depositors-columns.ts @@ -40,3 +40,11 @@ export const topDepositorsColumns = [ sortable: false, }, ] + +export const topDepositorsColumnsHiddenOnMobile = [ + 'strategy', + 'change7d', + 'projected1yrEarnings', + 'numberOfDeposits', + 'earningsStreak', +] diff --git a/apps/earn-protocol/features/user-activity/table/user-activity-columns.ts b/apps/earn-protocol/features/user-activity/table/user-activity-columns.ts index 7cd5c230c..dea474353 100644 --- a/apps/earn-protocol/features/user-activity/table/user-activity-columns.ts +++ b/apps/earn-protocol/features/user-activity/table/user-activity-columns.ts @@ -30,3 +30,5 @@ export const userActivityColumns = [ sortable: false, }, ] + +export const userActivityColumnsHiddenOnMobile = ['strategy', 'timestamp', 'balance'] diff --git a/apps/earn-protocol/features/vault-exposure/components/VaultExposureTable/VaultExposureTable.tsx b/apps/earn-protocol/features/vault-exposure/components/VaultExposureTable/VaultExposureTable.tsx index 9477ce5b8..569be186e 100644 --- a/apps/earn-protocol/features/vault-exposure/components/VaultExposureTable/VaultExposureTable.tsx +++ b/apps/earn-protocol/features/vault-exposure/components/VaultExposureTable/VaultExposureTable.tsx @@ -1,8 +1,11 @@ import { type FC, type ReactNode, useMemo, useState } from 'react' -import { Table, type TableSortedColumn } from '@summerfi/app-earn-ui' +import { Table, type TableSortedColumn, useMobileCheck } from '@summerfi/app-earn-ui' import { type SDKVaultType } from '@summerfi/app-types' -import { vaultExposureColumns } from '@/features/vault-exposure/table/columns' +import { + vaultExposureColumns, + vaultExposureColumnsHiddenOnMobile, +} from '@/features/vault-exposure/table/columns' import { vaultExposureMapper } from '@/features/vault-exposure/table/mapper' interface VaultExposureTableProps { @@ -22,16 +25,19 @@ export const VaultExposureTable: FC = ({ rowsToDisplay, }) => { const [sortConfig, setSortConfig] = useState>() + const { isMobile } = useMobileCheck() const rows = useMemo(() => vaultExposureMapper(vault, sortConfig), [vault, sortConfig]) + const resolvedHiddenColumns = isMobile ? vaultExposureColumnsHiddenOnMobile : hiddenColumns + return (
setSortConfig(_sortConfig)} - hiddenColumns={hiddenColumns} + hiddenColumns={resolvedHiddenColumns} /> ) } diff --git a/apps/earn-protocol/features/vault-exposure/table/columns.tsx b/apps/earn-protocol/features/vault-exposure/table/columns.tsx index dc2909ef6..b2975c31a 100644 --- a/apps/earn-protocol/features/vault-exposure/table/columns.tsx +++ b/apps/earn-protocol/features/vault-exposure/table/columns.tsx @@ -71,3 +71,5 @@ export const vaultExposureColumns = [ sortable: false, }, ] + +export const vaultExposureColumnsHiddenOnMobile = ['liquidity', 'type'] diff --git a/packages/app-earn-ui/src/components/molecules/DataBlock/DataBlock.module.scss b/packages/app-earn-ui/src/components/molecules/DataBlock/DataBlock.module.scss index 46e05e147..07ef5bde8 100644 --- a/packages/app-earn-ui/src/components/molecules/DataBlock/DataBlock.module.scss +++ b/packages/app-earn-ui/src/components/molecules/DataBlock/DataBlock.module.scss @@ -1,7 +1,28 @@ +.hasAccent { + padding-left: calc(var(--general-space-8) * 2); +} + .dataBlockWrapper { display: flex; flex-direction: column; + &.centered { align-items: center; } + + .titleWrapper { + display: flex; + align-items: center; + flex-direction: row; + + .accent { + display: inline; + width: var(--general-space-8); + height: var(--general-space-8); + border-radius: var(--radius-circle); + margin-left: calc(var(--general-space-8) * -2); + margin-right: var(--general-space-8); + } + } + } \ No newline at end of file diff --git a/packages/app-earn-ui/src/components/molecules/DataBlock/DataBlock.module.scss.d.ts b/packages/app-earn-ui/src/components/molecules/DataBlock/DataBlock.module.scss.d.ts index f6c07f56f..8069b8c87 100644 --- a/packages/app-earn-ui/src/components/molecules/DataBlock/DataBlock.module.scss.d.ts +++ b/packages/app-earn-ui/src/components/molecules/DataBlock/DataBlock.module.scss.d.ts @@ -1,6 +1,9 @@ export type Styles = { + accent: string centered: string dataBlockWrapper: string + hasAccent: string + titleWrapper: string } export type ClassNames = keyof Styles diff --git a/packages/app-earn-ui/src/components/molecules/DataBlock/DataBlock.tsx b/packages/app-earn-ui/src/components/molecules/DataBlock/DataBlock.tsx index 8c55b856a..f194f9712 100644 --- a/packages/app-earn-ui/src/components/molecules/DataBlock/DataBlock.tsx +++ b/packages/app-earn-ui/src/components/molecules/DataBlock/DataBlock.tsx @@ -20,6 +20,7 @@ type DataBlockProps = { subValueType?: 'positive' | 'negative' | 'neutral' centered?: boolean subValueStyle?: CSSProperties + accent?: string } export const DataBlock = ({ @@ -36,6 +37,7 @@ export const DataBlock = ({ subValueType, subValueSize, subValueStyle, + accent, }: DataBlockProps) => { const titleVariant = { small: 'p3semi' as const, @@ -59,16 +61,20 @@ export const DataBlock = ({
- +
+ {accent &&
} + +
{value} diff --git a/packages/app-earn-ui/src/components/molecules/HeadingWithCards/HeadingWithCards.module.scss b/packages/app-earn-ui/src/components/molecules/HeadingWithCards/HeadingWithCards.module.scss index a24aec067..d74398bc9 100644 --- a/packages/app-earn-ui/src/components/molecules/HeadingWithCards/HeadingWithCards.module.scss +++ b/packages/app-earn-ui/src/components/molecules/HeadingWithCards/HeadingWithCards.module.scss @@ -9,6 +9,7 @@ align-items: center; margin-bottom: var(--general-space-16); gap: var(--general-space-16); + flex-wrap: wrap; .headingIcons { display: flex; @@ -38,8 +39,7 @@ .cardsWrapper { display: flex; - justify-content: space-between; - flex-wrap: wrap; + flex-direction: column; gap: var(--general-space-16); margin-bottom: var(--general-space-40); @@ -49,4 +49,9 @@ align-items: flex-start; flex: 1; } + + @media screen and (min-width: 768px) { + flex-direction: row; + justify-content: space-between; + } } diff --git a/packages/app-earn-ui/src/components/organisms/PortfolioPosition/PortfolioPosition.module.scss b/packages/app-earn-ui/src/components/organisms/PortfolioPosition/PortfolioPosition.module.scss new file mode 100644 index 000000000..8b86efdc6 --- /dev/null +++ b/packages/app-earn-ui/src/components/organisms/PortfolioPosition/PortfolioPosition.module.scss @@ -0,0 +1,24 @@ +.basicInfoWrapper { + display: flex; + align-items: center; + height: 100%; +} + +.strategyInfoTopWrapper { + display: flex; + flex-direction: column; + text-align: right; + + .header { + color: var(--color-text-secondary); + } + + .value { + color: var(--color-text-primary); + } + + .subValue { + color: var(--color-text-secondary); + } + +} \ No newline at end of file diff --git a/packages/app-earn-ui/src/components/organisms/PortfolioPosition/PortfolioPosition.module.scss.d.ts b/packages/app-earn-ui/src/components/organisms/PortfolioPosition/PortfolioPosition.module.scss.d.ts new file mode 100644 index 000000000..adb5cb64e --- /dev/null +++ b/packages/app-earn-ui/src/components/organisms/PortfolioPosition/PortfolioPosition.module.scss.d.ts @@ -0,0 +1,13 @@ +export type Styles = { + basicInfoWrapper: string + header: string + strategyInfoTopWrapper: string + subValue: string + value: string +} + +export type ClassNames = keyof Styles + +declare const styles: Styles + +export default styles diff --git a/packages/app-earn-ui/src/components/organisms/PortfolioPosition/PortfolioPosition.tsx b/packages/app-earn-ui/src/components/organisms/PortfolioPosition/PortfolioPosition.tsx new file mode 100644 index 000000000..4c339baa2 --- /dev/null +++ b/packages/app-earn-ui/src/components/organisms/PortfolioPosition/PortfolioPosition.tsx @@ -0,0 +1,131 @@ +import { type ReactNode } from 'react' +import { type IArmadaPosition, type SDKVaultsListType } from '@summerfi/app-types' +import { formatCryptoBalance, formatDecimalAsPercent } from '@summerfi/app-utils' +import BigNumber from 'bignumber.js' +import Link from 'next/link' + +import { Button } from '@/components/atoms/Button/Button' +import { Card } from '@/components/atoms/Card/Card' +import { Text } from '@/components/atoms/Text/Text' +import { BonusLabel } from '@/components/molecules/BonusLabel/BonusLabel' +import { DataBlock } from '@/components/molecules/DataBlock/DataBlock' +import { SimpleGrid } from '@/components/molecules/Grid/SimpleGrid' +import { VaultTitleWithRisk } from '@/components/molecules/VaultTitleWithRisk/VaultTitleWithRisk' +import { getVaultPositionUrl } from '@/helpers/get-vault-url' + +import portfolioPositionStyles from './PortfolioPosition.module.scss' + +type PortfolioPositionProps = { + position: { + positionData: IArmadaPosition + vaultData: SDKVaultsListType[number] + } + positionGraph: ReactNode +} + +export const PortfolioPosition = ({ position, positionGraph }: PortfolioPositionProps) => { + const { + inputToken, + protocol, + name: vaultName, + apr30d, + apr365d, + totalValueLockedUSD, + id: vaultId, + } = position.vaultData + const { + amount, + id: { + user: { + wallet: { + address: { value: walletAddress }, + }, + }, + }, + } = position.positionData + const apr30dParsed = formatDecimalAsPercent(new BigNumber(apr30d).div(100)) + // TODO: fill data + const aprAllTime = formatDecimalAsPercent(new BigNumber(apr365d).div(100)) + const marketValue = formatCryptoBalance(totalValueLockedUSD) + const netContribution = formatCryptoBalance(amount.amount) + + return ( + +
+ +
+ + + + +
+
+ + Strategy + + + {vaultName} + + + xxx + +
+
+ + Earnings + + + Earnings + + + Earnings + +
+
+ {positionGraph} + + + + + +
+ + + +
+
+
+
+ ) +} diff --git a/packages/app-earn-ui/src/components/organisms/Table/Table.module.scss b/packages/app-earn-ui/src/components/organisms/Table/Table.module.scss index 8c2bd3846..10cd7a607 100644 --- a/packages/app-earn-ui/src/components/organisms/Table/Table.module.scss +++ b/packages/app-earn-ui/src/components/organisms/Table/Table.module.scss @@ -80,43 +80,3 @@ } } } - -/* Mobile styles */ -@media (max-width: 768px) { - .table { - display: block; - - thead { - display: none; - } - - tbody { - display: block; - width: 100%; - - tr { - border-bottom: 1px solid var(--earn-protocol-neutral-70); - } - } - - tr { - display: block; - margin-bottom: 10px; - } - - td { - display: block; - padding: 8px; - text-align: left; - position: relative; - - &:before { - content: attr(data-label); - position: absolute; - left: 10px; - font-weight: bold; - text-align: left; - } - } - } -} diff --git a/packages/app-earn-ui/src/hooks/use-mobile-check.ts b/packages/app-earn-ui/src/hooks/use-mobile-check.ts new file mode 100644 index 000000000..0e47326f7 --- /dev/null +++ b/packages/app-earn-ui/src/hooks/use-mobile-check.ts @@ -0,0 +1,59 @@ +'use client' +import { useLayoutEffect, useState } from 'react' + +interface ScreenInfo { + isMobile: boolean + width: number + height: number +} + +/** + * Custom hook to check if the screen size is mobile and to track screen width and height. + * This hook includes support for SSR by initializing with default values. + * + * @returns An object containing: + * - `isMobile`: A boolean indicating if the screen width is 768px or less. + * - `width`: The current screen width in pixels, defaults to 0 in SSR. + * - `height`: The current screen height in pixels, defaults to 0 in SSR. + * + * @example + * const { isMobile, width, height } = useMobileCheck(); + * console.log(isMobile); // true if the screen width is 768px or less + * + * @remarks + * - Adds an event listener to `window.resize` to update the screen information on resize. + * - Automatically removes the event listener when the component using this hook unmounts. + * - Checks if `window` is defined before accessing properties, making it safe for SSR. + */ +export const useMobileCheck = (): ScreenInfo => { + // Initialize with default values that assume a non-mobile, zero-width/height screen + const [screenInfo, setScreenInfo] = useState({ + isMobile: false, + width: 0, + height: 0, + }) + + useLayoutEffect(() => { + // Check if window is defined (important for SSR) + if (typeof window === 'undefined') return + + const handleResize = () => { + setScreenInfo({ + isMobile: window.innerWidth <= 768, + width: window.innerWidth, + height: window.innerHeight, + }) + } + + // Set initial screen info when component mounts + handleResize() + + // Listen for resize events + window.addEventListener('resize', handleResize) + + // eslint-disable-next-line consistent-return + return () => window.removeEventListener('resize', handleResize) + }, []) + + return screenInfo +} diff --git a/packages/app-earn-ui/src/index.ts b/packages/app-earn-ui/src/index.ts index fbf2341d2..ce17ff3e1 100644 --- a/packages/app-earn-ui/src/index.ts +++ b/packages/app-earn-ui/src/index.ts @@ -73,12 +73,14 @@ export { TermsOfService } from './components/organisms/TermsOfService/TermsOfSer export { Sidebar, type SidebarProps } from './components/organisms/Sidebar/Sidebar' export { Table, type TableSortedColumn } from './components/organisms/Table/Table' export { VaultSimulationForm } from './components/organisms/VaultSimulationForm/VaultSimulationForm' +export { PortfolioPosition } from './components/organisms/PortfolioPosition/PortfolioPosition' export { useToggle } from './hooks/use-toggle' export { useHash } from './hooks/use-hash' export { useOutsideElementClickHandler } from './hooks/use-outside-element-click-handler' export { useCurrentUrl } from './hooks/use-current-url' export { useQueryParams } from './hooks/use-query-params' +export { useMobileCheck } from './hooks/use-mobile-check' export { sidebarFootnote } from './common/sidebar/footnote' export { getVaultUrl, getVaultDetailsUrl, getVaultPositionUrl } from './helpers/get-vault-url' diff --git a/packages/app-tos/vite.config.ts b/packages/app-tos/vite.config.ts index 69bc3a5d4..c79495fdb 100644 --- a/packages/app-tos/vite.config.ts +++ b/packages/app-tos/vite.config.ts @@ -64,6 +64,8 @@ export default defineConfig(({ mode }) => { 'kysely', 'viem', 'viem/chains', + 'graphql', + 'graphql-tag', '@safe-global/safe-apps-sdk', 'jsonwebtoken', '@summerfi/serverless-shared', diff --git a/packages/app-types/package.json b/packages/app-types/package.json index 721f27990..edbcee600 100644 --- a/packages/app-types/package.json +++ b/packages/app-types/package.json @@ -9,10 +9,13 @@ "get-config-types": "node scripts/get-config-types.js && node scripts/get-rays-config-types.js" }, "dependencies": { + "@summerfi/armada-protocol-service": "workspace:*", "@summerfi/app-db": "workspace:*", "@summerfi/rays-db": "workspace:*", + "@summerfi/sdk-client-react": "workspace:*", "@summerfi/sdk-common": "workspace:*", "@summerfi/subgraph-manager-common": "workspace:*", + "@summerfi/armada-protocol-common": "workspace:*", "@types/react": "^18.3.1" }, "devDependencies": { diff --git a/packages/app-types/types/src/earn-protocol/index.ts b/packages/app-types/types/src/earn-protocol/index.ts index 58d67bcad..32bc54fbd 100644 --- a/packages/app-types/types/src/earn-protocol/index.ts +++ b/packages/app-types/types/src/earn-protocol/index.ts @@ -7,9 +7,11 @@ import { } from '@summerfi/subgraph-manager-common' import { ChainId } from '@summerfi/serverless-shared' import { type TransactionInfo } from '@summerfi/sdk-common' +import { type IArmadaPosition } from '@summerfi/armada-protocol-common' export { Network as SDKNetwork } export { ChainId as SDKChainId } +export { IArmadaPosition } export type SDKVaultsListType = GetVaultsQuery['vaults'] export type SDKVaultType = Exclude diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1f2bedb33..dbecb1167 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1116,9 +1116,18 @@ importers: '@summerfi/app-db': specifier: workspace:* version: link:../app-db + '@summerfi/armada-protocol-common': + specifier: workspace:* + version: link:../../sdk/armada-protocol-common + '@summerfi/armada-protocol-service': + specifier: workspace:* + version: link:../../sdk/armada-protocol-service '@summerfi/rays-db': specifier: workspace:* version: link:../rays-db + '@summerfi/sdk-client-react': + specifier: workspace:* + version: link:../../sdk/sdk-client-react '@summerfi/sdk-common': specifier: workspace:* version: link:../../sdk/sdk-common @@ -2653,6 +2662,9 @@ importers: '@summerfi/armada-protocol-common': specifier: workspace:* version: link:../armada-protocol-common + '@summerfi/armada-protocol-service': + specifier: workspace:* + version: link:../armada-protocol-service '@summerfi/protocol-plugins': specifier: workspace:* version: link:../protocol-plugins diff --git a/sdk/sdk-client/package.json b/sdk/sdk-client/package.json index 3273fa6d2..72908eac7 100644 --- a/sdk/sdk-client/package.json +++ b/sdk/sdk-client/package.json @@ -23,6 +23,7 @@ "dependencies": { "@summerfi/protocol-plugins": "workspace:*", "@summerfi/armada-protocol-common": "workspace:*", + "@summerfi/armada-protocol-service": "workspace:*", "@summerfi/sdk-common": "workspace:*", "@summerfi/sdk-server": "workspace:*", "@trpc/client": "11.0.0-rc.502", diff --git a/sdk/sdk-client/src/protocol-plugins-reexport.ts b/sdk/sdk-client/src/protocol-plugins-reexport.ts index fbf0e7fbd..83209bd2b 100644 --- a/sdk/sdk-client/src/protocol-plugins-reexport.ts +++ b/sdk/sdk-client/src/protocol-plugins-reexport.ts @@ -40,3 +40,6 @@ export { SparkProtocol, type ISparkProtocol, } from '@summerfi/protocol-plugins/plugins/spark' + +// another workaround to re-export the protocol service - FE needs classes for superjson to be registered +export * from '@summerfi/armada-protocol-service'