From 1721f8c3d62be3eedd38cd8ec84141baaa0a795b Mon Sep 17 00:00:00 2001 From: Christian Escalante Date: Thu, 5 Sep 2024 02:53:39 -0300 Subject: [PATCH 01/18] Money Market Mockups (#990) * aave page static components, mobile first * responsive panels * functional static borrow asset list * cleanup * renaming * lend assets list * Lend positions list * fixes and cleanup * cleanup * cleanup * cleanup * static ui done. missing validations * add validations * add validations * move errors down * borrow form modal * pr comments * pr comments * fix typo * Update apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/components/LendPositionAction/LendPositionAction.tsx Co-authored-by: Juan Dahl * repay modals * lend modal * use simple table row * asset amount input * add missing translations * fix translation * units and prices mock * setup layout for pool details screen * wallet overview panel * emode card mock * max with for emode category * top bar mock * network switch * cleanup * ui fixes * ui fixes, usd prices * Efficiency card * ordering * fix filter 0 balances checkbox * pr comment * token * Mocked table cards plus refactor of statistics card (#14) * mocked table cards plus refactor of statistics card * ui fixes * Saf 41 charts (#16) * mocked table cards plus refactor of statistics card * ui fixes * [SAF-41] give each graph it's own chart component * [SAF-41] add htmlLegend plugin in charts; remove comments and refactor some colors moving them to constants --------- Co-authored-by: matzapata * removed view transactions button (out of scope) * fix breakpoints * [SAF-41] fix color constant and background for mobile vs desktop views (#20) * [SAF-41] fix color constant and background for mobile vs desktop views * [SAF-41] use theme variables for colors and breakpoint usage for better consistency * Apply suggestions from code review Co-authored-by: Juan Dahl --------- Co-authored-by: Juan Dahl * Update apps/frontend/package.json Co-authored-by: Pietro <74987028+pietro-maximoff@users.noreply.github.com> * Update apps/frontend/src/app/2_molecules/AmountTransition/AmountTransition.tsx Co-authored-by: Pietro <74987028+pietro-maximoff@users.noreply.github.com> * Update apps/frontend/src/app/2_molecules/AssetAmountPriceRenderer/AssetAmountPriceRenderer.tsx Co-authored-by: Pietro <74987028+pietro-maximoff@users.noreply.github.com> * Update apps/frontend/src/app/2_molecules/NetworkSwitch/NetworkSwitch.tsx Co-authored-by: Pietro <74987028+pietro-maximoff@users.noreply.github.com> * Update apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/EModeDetails/EModeDetails.tsx Co-authored-by: Pietro <74987028+pietro-maximoff@users.noreply.github.com> * Update apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/SupplyDetailsGraph/SupplyDetailsGraph.tsx Co-authored-by: Pietro <74987028+pietro-maximoff@users.noreply.github.com> * Update apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/SupplyDetailsGraph/SupplyDetailsGraph.tsx Co-authored-by: Pietro <74987028+pietro-maximoff@users.noreply.github.com> * Apply suggestions from code review Co-authored-by: Pietro <74987028+pietro-maximoff@users.noreply.github.com> * remove LinkIcon * Apply suggestions from code review Co-authored-by: Pietro <74987028+pietro-maximoff@users.noreply.github.com> * Apply suggestions from code review Co-authored-by: Pietro <74987028+pietro-maximoff@users.noreply.github.com> * Apply suggestions from code review Co-authored-by: Pietro <74987028+pietro-maximoff@users.noreply.github.com> * pr comments * fixes --------- Co-authored-by: matzapata Co-authored-by: Matias Zapata <42716817+matzapata@users.noreply.github.com> Co-authored-by: Juan Dahl Co-authored-by: Luciano Perez Cerra Co-authored-by: Pietro <74987028+pietro-maximoff@users.noreply.github.com> --- apps/frontend/package.json | 1 + apps/frontend/src/app/1_atoms/Icons/Icons.tsx | 33 +++ .../AavePoolRowTitle/AavePoolRowTitle.tsx | 28 +++ .../AmountRenderer/AmountRenderer.tsx | 44 ++-- .../AmountTransition/AmountTransition.tsx | 28 +++ .../AssetAmountInput/AssetAmountInput.tsx | 93 +++++++ .../AssetAmountPriceRenderer.tsx | 40 +++ .../app/2_molecules/MaxButton/MaxButton.tsx | 1 + .../NetworkSwitch/NetworkSwitch.tsx | 24 ++ .../StatisticsCard/StatisticsCard.tsx | 53 ++++ .../src/app/5_pages/AavePage/AavePage.tsx | 83 +++++++ .../BorrowAssetsList.constants.tsx | 76 ++++++ .../BorrowAssetsList/BorrowAssetsList.tsx | 74 ++++++ .../BorrowAssetsList.types.tsx | 6 + .../BorrowAssetAction/BorrowAssetAction.tsx | 47 ++++ .../BorrowAssetDetails/BorrowAssetDetails.tsx | 44 ++++ .../components/BorrowModal/BorrowForm.tsx | 167 +++++++++++++ .../BorrowModal/BorrowForm.utils.ts | 14 ++ .../BorrowModal/BorrowModalContainer.tsx | 28 +++ .../BorrowPositionsList.constants.tsx | 83 +++++++ .../BorrowPositionsList.tsx | 113 +++++++++ .../BorrowPositionsList.types.tsx | 6 + .../BorrowPositionAction.tsx | 37 +++ .../BorrowPositionDetails.tsx | 50 ++++ .../EfficiencyModeCard/EfficiencyModeCard.tsx | 66 +++++ .../RepayModal/RepayModalContainer.tsx | 77 ++++++ .../RepayModal/RepayWithCollateralForm.tsx | 189 ++++++++++++++ .../RepayModal/RepayWithWalletBalanceForm.tsx | 133 ++++++++++ .../CollateralRatioHealthBar.tsx | 49 ++++ .../CollateralRatioHealthBar.utils.tsx | 14 ++ .../LendAssetsList.constants.tsx | 89 +++++++ .../LendAssetsList/LendAssetsList.tsx | 81 ++++++ .../LendAssetsList/LendAssetsList.types.tsx | 6 + .../LendAssetAction/LendAssetAction.tsx | 44 ++++ .../LendAssetDetails/LendAssetDetails.tsx | 58 +++++ .../components/LendModal/LendForm.tsx | 99 ++++++++ .../LendModal/LendModalContainer.tsx | 28 +++ .../LendPositionsList.constants.tsx | 89 +++++++ .../LendPositionsList/LendPositionsList.tsx | 116 +++++++++ .../LendPositionsList.types.tsx | 6 + .../LendPositionAction/LendPositionAction.tsx | 36 +++ .../LendPositionDetails.tsx | 54 ++++ .../ToggleCollateralAction.tsx | 19 ++ .../components/WithdrawModal/WithdrawForm.tsx | 126 ++++++++++ .../WithdrawModal/WithdrawModalContainer.tsx | 28 +++ .../PoolPositionStat/PoolPositionStat.tsx | 47 ++++ .../AavePage/components/TopPanel/TopPanel.tsx | 79 ++++++ .../AaveReserveOverviewPage.tsx | 91 +++++++ .../BorrowDetailsGraph/BorrowDetailsGraph.tsx | 108 ++++++++ .../components/Chart/Chart.constants.ts | 72 ++++++ .../components/Chart/Chart.tsx | 119 +++++++++ .../components/Chart/Chart.types.ts | 6 + .../components/Chart/Chart.utils.ts | 89 +++++++ .../components/EModeDetails/EModeDetails.tsx | 81 ++++++ .../InterestRateModelGraph.tsx | 76 ++++++ .../components/Chart/Chart.constants.ts | 45 ++++ .../components/Chart/Chart.tsx | 136 ++++++++++ .../components/Chart/Chart.types.ts | 8 + .../components/Chart/Chart.utils.ts | 89 +++++++ .../SupplyDetailsGraph/SupplyDetailsGraph.tsx | 120 +++++++++ .../components/Chart/Chart.constants.ts | 73 ++++++ .../components/Chart/Chart.tsx | 121 +++++++++ .../components/Chart/Chart.types.ts | 6 + .../components/Chart/Chart.utils.ts | 89 +++++++ .../components/TopPanel/TopPanel.tsx | 116 +++++++++ .../WalletOverview/WalletOverview.tsx | 125 ++++++++++ .../components/BorrowAction/BorrowAction.tsx | 51 ++++ .../components/SupplyAction/SupplyAction.tsx | 50 ++++ .../src/app/5_pages/BorrowPage/BorrowPage.tsx | 48 ++-- .../src/app/5_pages/LendPage/LendPage.tsx | 44 ++-- apps/frontend/src/constants/currencies.ts | 1 + .../frontend/src/locales/en/translations.json | 232 +++++++++++++++--- apps/frontend/src/router.tsx | 5 + .../ui/src/2_molecules/Table/Table.types.ts | 1 + .../OrderDirectionIcon.module.css | 2 +- .../components/TableMobile/TableMobile.tsx | 2 + .../TableMobile/components/TableMobileRow.tsx | 5 +- 77 files changed, 4625 insertions(+), 92 deletions(-) create mode 100644 apps/frontend/src/app/1_atoms/Icons/Icons.tsx create mode 100644 apps/frontend/src/app/2_molecules/AavePoolRowTitle/AavePoolRowTitle.tsx create mode 100644 apps/frontend/src/app/2_molecules/AmountTransition/AmountTransition.tsx create mode 100644 apps/frontend/src/app/2_molecules/AssetAmountInput/AssetAmountInput.tsx create mode 100644 apps/frontend/src/app/2_molecules/AssetAmountPriceRenderer/AssetAmountPriceRenderer.tsx create mode 100644 apps/frontend/src/app/2_molecules/NetworkSwitch/NetworkSwitch.tsx create mode 100644 apps/frontend/src/app/2_molecules/StatisticsCard/StatisticsCard.tsx create mode 100644 apps/frontend/src/app/5_pages/AavePage/AavePage.tsx create mode 100644 apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/BorrowAssetsList.constants.tsx create mode 100644 apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/BorrowAssetsList.tsx create mode 100644 apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/BorrowAssetsList.types.tsx create mode 100644 apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/components/BorrowAssetAction/BorrowAssetAction.tsx create mode 100644 apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/components/BorrowAssetDetails/BorrowAssetDetails.tsx create mode 100644 apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/components/BorrowModal/BorrowForm.tsx create mode 100644 apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/components/BorrowModal/BorrowForm.utils.ts create mode 100644 apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/components/BorrowModal/BorrowModalContainer.tsx create mode 100644 apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/BorrowPositionsList.constants.tsx create mode 100644 apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/BorrowPositionsList.tsx create mode 100644 apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/BorrowPositionsList.types.tsx create mode 100644 apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/BorrowPositionAction/BorrowPositionAction.tsx create mode 100644 apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/BorrowPositionDetails/BorrowPositionDetails.tsx create mode 100644 apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/EfficiencyModeCard/EfficiencyModeCard.tsx create mode 100644 apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/RepayModal/RepayModalContainer.tsx create mode 100644 apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/RepayModal/RepayWithCollateralForm.tsx create mode 100644 apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/RepayModal/RepayWithWalletBalanceForm.tsx create mode 100644 apps/frontend/src/app/5_pages/AavePage/components/CollateralRatioHealthBar/CollateralRatioHealthBar.tsx create mode 100644 apps/frontend/src/app/5_pages/AavePage/components/CollateralRatioHealthBar/CollateralRatioHealthBar.utils.tsx create mode 100644 apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/LendAssetsList.constants.tsx create mode 100644 apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/LendAssetsList.tsx create mode 100644 apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/LendAssetsList.types.tsx create mode 100644 apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/components/LendAssetAction/LendAssetAction.tsx create mode 100644 apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/components/LendAssetDetails/LendAssetDetails.tsx create mode 100644 apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/components/LendModal/LendForm.tsx create mode 100644 apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/components/LendModal/LendModalContainer.tsx create mode 100644 apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/LendPositionsList.constants.tsx create mode 100644 apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/LendPositionsList.tsx create mode 100644 apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/LendPositionsList.types.tsx create mode 100644 apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/components/LendPositionAction/LendPositionAction.tsx create mode 100644 apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/components/LendPositionDetails/LendPositionDetails.tsx create mode 100644 apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/components/ToggleCollateralAction/ToggleCollateralAction.tsx create mode 100644 apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/components/WithdrawModal/WithdrawForm.tsx create mode 100644 apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/components/WithdrawModal/WithdrawModalContainer.tsx create mode 100644 apps/frontend/src/app/5_pages/AavePage/components/PoolPositionStat/PoolPositionStat.tsx create mode 100644 apps/frontend/src/app/5_pages/AavePage/components/TopPanel/TopPanel.tsx create mode 100644 apps/frontend/src/app/5_pages/AaveReserveOverviewPage/AaveReserveOverviewPage.tsx create mode 100644 apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/BorrowDetailsGraph/BorrowDetailsGraph.tsx create mode 100644 apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/BorrowDetailsGraph/components/Chart/Chart.constants.ts create mode 100644 apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/BorrowDetailsGraph/components/Chart/Chart.tsx create mode 100644 apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/BorrowDetailsGraph/components/Chart/Chart.types.ts create mode 100644 apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/BorrowDetailsGraph/components/Chart/Chart.utils.ts create mode 100644 apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/EModeDetails/EModeDetails.tsx create mode 100644 apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/InterestRateModelGraph/InterestRateModelGraph.tsx create mode 100644 apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/InterestRateModelGraph/components/Chart/Chart.constants.ts create mode 100644 apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/InterestRateModelGraph/components/Chart/Chart.tsx create mode 100644 apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/InterestRateModelGraph/components/Chart/Chart.types.ts create mode 100644 apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/InterestRateModelGraph/components/Chart/Chart.utils.ts create mode 100644 apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/SupplyDetailsGraph/SupplyDetailsGraph.tsx create mode 100644 apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/SupplyDetailsGraph/components/Chart/Chart.constants.ts create mode 100644 apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/SupplyDetailsGraph/components/Chart/Chart.tsx create mode 100644 apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/SupplyDetailsGraph/components/Chart/Chart.types.ts create mode 100644 apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/SupplyDetailsGraph/components/Chart/Chart.utils.ts create mode 100644 apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/TopPanel/TopPanel.tsx create mode 100644 apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/WalletOverview/WalletOverview.tsx create mode 100644 apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/WalletOverview/components/BorrowAction/BorrowAction.tsx create mode 100644 apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/WalletOverview/components/SupplyAction/SupplyAction.tsx diff --git a/apps/frontend/package.json b/apps/frontend/package.json index f2d618805..ffc00fea1 100644 --- a/apps/frontend/package.json +++ b/apps/frontend/package.json @@ -28,6 +28,7 @@ "@uniswap/permit2-sdk": "1.2.0", "bitcoin-address-validation": "2.2.1", "chart.js": "4.1.1", + "chartjs-adapter-date-fns": "3.0.0", "classnames": "2.3.2", "date-fns": "2.30.0", "dayjs": "1.11.7", diff --git a/apps/frontend/src/app/1_atoms/Icons/Icons.tsx b/apps/frontend/src/app/1_atoms/Icons/Icons.tsx new file mode 100644 index 000000000..fae2b2880 --- /dev/null +++ b/apps/frontend/src/app/1_atoms/Icons/Icons.tsx @@ -0,0 +1,33 @@ +import React from 'react'; + +import { IconType } from '@sovryn/ui'; + +export const WalletIcon: IconType = ( + + + + +); + +export const EModeIcon: IconType = ( + + + +); diff --git a/apps/frontend/src/app/2_molecules/AavePoolRowTitle/AavePoolRowTitle.tsx b/apps/frontend/src/app/2_molecules/AavePoolRowTitle/AavePoolRowTitle.tsx new file mode 100644 index 000000000..99466a757 --- /dev/null +++ b/apps/frontend/src/app/2_molecules/AavePoolRowTitle/AavePoolRowTitle.tsx @@ -0,0 +1,28 @@ +import React, { FC } from 'react'; + +import { + AmountRenderer, + AmountRendererProps, +} from '../AmountRenderer/AmountRenderer'; +import { AssetRenderer } from '../AssetRenderer/AssetRenderer'; + +type AaveRowTitleProps = AmountRendererProps & { + asset: string; + label?: string; +}; + +export const AaveRowTitle: FC = ({ + asset, + label, + ...props +}) => ( +
+ +
+ + {label && ( + {label} + )} +
+
+); diff --git a/apps/frontend/src/app/2_molecules/AmountRenderer/AmountRenderer.tsx b/apps/frontend/src/app/2_molecules/AmountRenderer/AmountRenderer.tsx index 6350188fd..0994b2fcd 100644 --- a/apps/frontend/src/app/2_molecules/AmountRenderer/AmountRenderer.tsx +++ b/apps/frontend/src/app/2_molecules/AmountRenderer/AmountRenderer.tsx @@ -29,7 +29,7 @@ import { const { decimal, thousand } = getLocaleSeparators(); -type AmountRendererProps = { +export type AmountRendererProps = { value: Decimalish; precision?: number; className?: string; @@ -142,33 +142,29 @@ export const AmountRenderer: FC = ({ } - className={classNames({ - 'cursor-pointer': shouldShowTooltip, - })} + className={classNames({ 'cursor-pointer': shouldShowTooltip }, className)} disabled={!shouldShowTooltip} trigger={trigger} dataAttribute={dataAttribute} > - - {isAnimated ? ( - - ) : ( - <> - {shouldShowRoundingPrefix ? '~ ' : ''} - {prefix} - {localeFormattedValue} {suffix} - - )} - + {isAnimated ? ( + + ) : ( + + {shouldShowRoundingPrefix ? '~ ' : ''} + {prefix} + {localeFormattedValue} {suffix} + + )} ); }; diff --git a/apps/frontend/src/app/2_molecules/AmountTransition/AmountTransition.tsx b/apps/frontend/src/app/2_molecules/AmountTransition/AmountTransition.tsx new file mode 100644 index 000000000..643c56722 --- /dev/null +++ b/apps/frontend/src/app/2_molecules/AmountTransition/AmountTransition.tsx @@ -0,0 +1,28 @@ +import React, { FC } from 'react'; + +import classNames from 'classnames'; + +import { Icon, IconNames } from '@sovryn/ui'; + +import { + AmountRenderer, + AmountRendererProps, +} from '../AmountRenderer/AmountRenderer'; + +type AmountTransitionProps = { + className?: string; + to: AmountRendererProps; + from: AmountRendererProps; +}; + +export const AmountTransition: FC = ({ + from, + to, + className, +}) => ( +
+ + + +
+); diff --git a/apps/frontend/src/app/2_molecules/AssetAmountInput/AssetAmountInput.tsx b/apps/frontend/src/app/2_molecules/AssetAmountInput/AssetAmountInput.tsx new file mode 100644 index 000000000..ed2f76be9 --- /dev/null +++ b/apps/frontend/src/app/2_molecules/AssetAmountInput/AssetAmountInput.tsx @@ -0,0 +1,93 @@ +import React, { FC, useCallback, useState } from 'react'; + +import { AmountInput, Paragraph, Select, SelectOption } from '@sovryn/ui'; +import { Decimal, Decimalish } from '@sovryn/utils'; + +import { AmountRenderer } from '../AmountRenderer/AmountRenderer'; +import { AssetRenderer } from '../AssetRenderer/AssetRenderer'; +import { MaxButton } from '../MaxButton/MaxButton'; + +type AssetAmountInputProps = { + label?: string; + maxAmount?: Decimalish; + invalid?: boolean; + amountLabel?: string; + amountValue?: string | number; + onAmountChange?: (value: string) => void; + assetValue: string; + assetOptions: SelectOption[]; + onAssetChange: (asset: string) => void; +}; + +export const AssetAmountInput: FC = ({ + label, + maxAmount, + amountLabel, + amountValue, + onAmountChange, + invalid, + assetValue, + assetOptions, + onAssetChange, +}) => { + const [assetUsdValue] = useState(0); // TODO: mock + + const assetOptionRenderer = useCallback( + ({ value }) => ( + + ), + [], + ); + + return ( +
+ {label && ( + + {label} + + )} + +
+ {maxAmount !== undefined && ( + + + onAmountChange && + onAmountChange(Decimal.from(maxAmount).toString()) + } + /> + + )} + +
+
+ + +
+ + + ); +}; diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/EfficiencyModeCard/EfficencyModeCard.utils.ts b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/EfficiencyModeCard/EfficencyModeCard.utils.ts new file mode 100644 index 000000000..bb79bd4f5 --- /dev/null +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/EfficiencyModeCard/EfficencyModeCard.utils.ts @@ -0,0 +1,59 @@ +import { ReservesDataHumanized } from '@aave/contract-helpers'; +import { formatReserves, formatUserSummary } from '@aave/math-utils'; + +import { Decimal } from '@sovryn/utils'; + +import { UserReservesData } from '../../../../../../../types/aave'; +import { AaveCalculations } from '../../../../../../../utils/aave/AaveCalculations'; + +export const normalizeEModeSummary = ( + eModeCategoryId: number, + reservesData?: ReservesDataHumanized, + userReservesData?: UserReservesData, + timestamp?: number, +): { + ltv: Decimal; + collateralRatio: Decimal; + liquidationRisk: boolean; +} => { + if (!reservesData || !userReservesData || !timestamp) { + return { + ltv: Decimal.from(0), + collateralRatio: Decimal.from(0), + liquidationRisk: false, + }; + } + + const { + marketReferenceCurrencyDecimals, + marketReferenceCurrencyPriceInUsd: marketReferencePriceInUsd, + } = reservesData.baseCurrencyData; + const summary = formatUserSummary({ + userEmodeCategoryId: eModeCategoryId, + currentTimestamp: timestamp, + marketReferencePriceInUsd, + marketReferenceCurrencyDecimals, + userReserves: userReservesData.userReserves, + formattedReserves: formatReserves({ + currentTimestamp: timestamp, + marketReferencePriceInUsd, + marketReferenceCurrencyDecimals, + reserves: reservesData.reservesData, + }), + }); + + // if health factor is below 1 we're at risk. Negative means doesn't apply + const healthFactor = Decimal.from(summary.healthFactor); + const liquidationRisk = healthFactor.lte(1) && healthFactor.gt(0); + + const collateralRatio = AaveCalculations.computeCollateralRatio( + Decimal.from(summary.totalCollateralUSD), + Decimal.from(summary.totalBorrowsUSD), + ); + + return { + ltv: Decimal.from(summary.currentLoanToValue).div(100), + collateralRatio, + liquidationRisk, + }; +}; diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/EfficiencyModeCard/EfficiencyModeCard.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/EfficiencyModeCard/EfficiencyModeCard.tsx index 8e12f107e..954b4bfcb 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/EfficiencyModeCard/EfficiencyModeCard.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/EfficiencyModeCard/EfficiencyModeCard.tsx @@ -1,10 +1,14 @@ -import React, { FC, useState } from 'react'; +import React, { FC, useCallback, useMemo, useState } from 'react'; import classNames from 'classnames'; import { t } from 'i18next'; import { Button, + ButtonStyle, + Dialog, + DialogBody, + DialogHeader, Icon, IconNames, Link, @@ -14,53 +18,158 @@ import { } from '@sovryn/ui'; import { EModeIcon } from '../../../../../../1_atoms/Icons/Icons'; +import { useAaveEModeCategories } from '../../../../../../../hooks/aave/useAaveEModeCategories'; import { translations } from '../../../../../../../locales/i18n'; +import { DisableEModeForm } from './components/DisableEModeForm/DisableEModeForm'; +import { EnableEModeForm } from './components/EnableEModeForm/EnableEModeForm'; +import { SwitchEModeForm } from './components/SwitchEModeForm/SwitchEModeForm'; type EfficiencyModeCardProps = { className?: string; + eModeCategoryId: Number; }; export const EfficiencyModeCard: FC = ({ className, + eModeCategoryId, }) => { - const [enabled] = useState(false); + const eModeCategories = useAaveEModeCategories(); + const [enableEModeOpen, setEnableEModeOpen] = useState(false); + const [disableEModeOpen, setDisableEModeOpen] = useState(false); + const [switchEModeOpen, setSwitchEModeOpen] = useState(false); + + const onEnableEModeClose = useCallback(() => setEnableEModeOpen(false), []); + + const onDisableEModeClose = useCallback(() => setDisableEModeOpen(false), []); + + const onSwitchEModeClose = useCallback(() => setSwitchEModeOpen(false), []); + + const currentCategory = useMemo( + () => eModeCategories.find(c => c.id === eModeCategoryId), + [eModeCategories, eModeCategoryId], + ); return ( - -
- - {t(translations.aavePage.eModeCard.title)} - - - {t(translations.aavePage.eModeCard.description)}{' '} - - + <> + +
+ + {t(translations.aavePage.eModeCard.title)} + + + {eModeCategoryId !== 0 && ( +
+ + {t(translations.aavePage.eMode.assetCategory)} + + +
+ + {currentCategory?.label} + +
+ + + {t(translations.common.enabled)} + +
+
+
+ )} + + + {t(translations.aavePage.eModeCard.description)}{' '} + + +
+ {eModeCategoryId !== 0 ? ( +
+
+ ) : ( +
-
+ } + /> + + + + +
+ } + /> + + +
+ ); +}; diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/EfficiencyModeCard/components/EnableEModeForm/EnableEModeForm.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/EfficiencyModeCard/components/EnableEModeForm/EnableEModeForm.tsx new file mode 100644 index 000000000..9ab52b45b --- /dev/null +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/EfficiencyModeCard/components/EnableEModeForm/EnableEModeForm.tsx @@ -0,0 +1,119 @@ +import React, { FC, useCallback, useMemo, useState } from 'react'; + +import { t } from 'i18next'; + +import { + Button, + ErrorBadge, + ErrorLevel, + Paragraph, + Select, + SimpleTable, + SimpleTableRow, +} from '@sovryn/ui'; + +import { AmountRenderer } from '../../../../../../../../2_molecules/AmountRenderer/AmountRenderer'; +import { useAaveSetUserEMode } from '../../../../../../../../../hooks/aave/useAaveSetUserEMode'; +import { useAaveUserReservesData } from '../../../../../../../../../hooks/aave/useAaveUserReservesData'; +import { translations } from '../../../../../../../../../locales/i18n'; +import { EModeCategory } from '../../../../../../../../../types/aave'; + +type EnableEModeFormProps = { + categories: EModeCategory[]; + onComplete: () => void; +}; + +export const EnableEModeForm: FC = ({ + categories, + onComplete, +}) => { + const [category, setCategory] = useState(String(categories[0]?.id)); + const { summary } = useAaveUserReservesData(); + const { handleSetUserEMode } = useAaveSetUserEMode(); + + const categoriesOptions = useMemo( + () => + categories.map(category => ({ + label: category.label, + value: String(category?.id), + })), + [categories], + ); + + const selectedCategory = useMemo( + () => categories.find(c => c?.id === Number(category)), + [category, categories], + ); + + const positionsInOtherCategories = useMemo( + () => + summary.reserves.some( + r => r.reserve.eModeCategoryId !== Number(category) && r.borrowed.gt(0), + ), + [summary, category], + ); + + const onConfirmClick = useCallback(() => { + if (!selectedCategory) { + return; + } + handleSetUserEMode(selectedCategory, { onComplete }); + }, [handleSetUserEMode, selectedCategory, onComplete]); + + return ( +
+ + +
+ + {t(translations.aavePage.eMode.selectCategory)} + + + +
+ + + + + + {current?.label} + + + {selectedCategory?.label ?? '-'} + +
+ } + /> + + + + + + + } + /> + + + {positionsInOtherCategories && ( + + )} + +