diff --git a/apps/main/src/lend/components/AlertNoLoanFound.tsx b/apps/main/src/lend/components/AlertNoLoanFound.tsx index d37cb33e5d..246931bbfa 100644 --- a/apps/main/src/lend/components/AlertNoLoanFound.tsx +++ b/apps/main/src/lend/components/AlertNoLoanFound.tsx @@ -25,7 +25,7 @@ const AlertNoLoanFound = ({ alertType, owmId }: { alertType?: AlertType; owmId: size="large" onClick={() => { setStateByKeyMarkets('marketDetailsView', 'market') - push(getLoanCreatePathname(params, owmId)) + push(getLoanCreatePathname(params, owmId, 'create')) }} > Create loan diff --git a/apps/main/src/lend/components/PageLoanCreate/LoanCreateTabs.tsx b/apps/main/src/lend/components/PageLoanCreate/LoanCreateTabs.tsx deleted file mode 100644 index c9bd29fe8a..0000000000 --- a/apps/main/src/lend/components/PageLoanCreate/LoanCreateTabs.tsx +++ /dev/null @@ -1,66 +0,0 @@ -import { useCallback } from 'react' -import LoanFormCreate from '@/lend/components/PageLoanCreate/LoanFormCreate' -import type { FormValues } from '@/lend/components/PageLoanCreate/types' -import { DEFAULT_FORM_VALUES } from '@/lend/components/PageLoanCreate/utils' -import networks from '@/lend/networks' -import useStore from '@/lend/store/useStore' -import { type MarketUrlParams, type PageContentProps } from '@/lend/types/lend.types' -import { CreateLoanForm } from '@/llamalend/features/borrow/components/CreateLoanForm' -import type { OnBorrowFormUpdate } from '@/llamalend/features/borrow/types' -import { hasLeverage } from '@/llamalend/llama.utils' -import { useCreateLoanMuiForm } from '@ui-kit/hooks/useFeatureFlags' -import { t } from '@ui-kit/lib/i18n' -import { type FormTab, FormTabs } from '@ui-kit/shared/ui/FormTabs/FormTabs' - -type CreateLoanProps = PageContentProps - -/** - * Callback that synchronizes the `ChartOhlc` component with the `RangeSlider` component in the new `BorrowTabContents`. - */ -const useOnFormUpdate = ({ api, market }: Pick): OnBorrowFormUpdate => - useCallback( - async ({ debt, userCollateral, range, slippage, leverageEnabled }) => { - const { setFormValues, setStateByKeys } = useStore.getState().loanCreate - const formValues: FormValues = { - ...DEFAULT_FORM_VALUES, - n: range, - debt: `${debt ?? ''}`, - userCollateral: `${userCollateral ?? ''}`, - } - await setFormValues(api, market, formValues, `${slippage}`, leverageEnabled) - setStateByKeys({ isEditLiqRange: true }) - }, - [api, market], - ) - -function CreateLoanTab({ market, api, rChainId }: CreateLoanProps) { - const onLoanCreated = useStore((state) => state.loanCreate.onLoanCreated) - const onCreated = useCallback( - async () => api && market && (await onLoanCreated(api, market)), - [api, market, onLoanCreated], - ) - const onUpdate = useOnFormUpdate({ market, api }) - return ( - - ) -} - -const LendCreateTabsNewMenu = [ - { value: 'create', label: t`Borrow`, component: CreateLoanTab }, -] satisfies FormTab[] - -const LendCreateTabsOldMenu = [ - { value: 'create', label: t`Create Loan`, component: LoanFormCreate }, - { - value: 'leverage', - label: t`Leverage`, - component: (p) => , - visible: ({ market }) => market && hasLeverage(market), - }, -] satisfies FormTab[] - -export const LoanCreateTabs = (pageProps: CreateLoanProps) => { - const menu = useCreateLoanMuiForm() ? LendCreateTabsNewMenu : LendCreateTabsOldMenu - const shouldWrap = menu === LendCreateTabsOldMenu - return -} diff --git a/apps/main/src/lend/components/PageLoanCreate/LoanFormCreate/index.tsx b/apps/main/src/lend/components/PageLoanCreate/LoanFormCreate/index.tsx index f9f4426a09..05549d74ad 100644 --- a/apps/main/src/lend/components/PageLoanCreate/LoanFormCreate/index.tsx +++ b/apps/main/src/lend/components/PageLoanCreate/LoanFormCreate/index.tsx @@ -412,7 +412,7 @@ const LoanCreate = ({ size="large" onClick={() => { setStateByKeyMarkets('marketDetailsView', 'user') - push(getLoanManagePathname(params, rOwmId)) + push(getLoanManagePathname(params, rOwmId, 'loan')) }} > Manage loan @@ -431,7 +431,7 @@ const LoanCreate = ({ )} {steps && } {formStatus.isComplete && market && ( - + Manage loan )} diff --git a/apps/main/src/lend/components/PageLoanCreate/Page.tsx b/apps/main/src/lend/components/PageLoanCreate/Page.tsx index 64d881c17b..3841891282 100644 --- a/apps/main/src/lend/components/PageLoanCreate/Page.tsx +++ b/apps/main/src/lend/components/PageLoanCreate/Page.tsx @@ -3,7 +3,7 @@ import type { Address } from 'viem' import CampaignRewardsBanner from '@/lend/components/CampaignRewardsBanner' import { MarketInformationComp } from '@/lend/components/MarketInformationComp' import { MarketInformationTabs } from '@/lend/components/MarketInformationTabs' -import { LoanCreateTabs } from '@/lend/components/PageLoanCreate/LoanCreateTabs' +import LoanCreate from '@/lend/components/PageLoanCreate/index' import { useOneWayMarket } from '@/lend/entities/chain' import { useLendPageTitle } from '@/lend/hooks/useLendPageTitle' import { useMarketDetails } from '@/lend/hooks/useMarketDetails' @@ -35,7 +35,7 @@ const { Spacing } = SizesAndSpaces const Page = () => { const params = useParams() - const { rMarket, rChainId } = parseMarketParams(params) + const { rMarket, rChainId, rFormType } = parseMarketParams(params) const { data: market, isSuccess } = useOneWayMarket(rChainId, rMarket) const { llamaApi: api = null, connectState } = useConnection() @@ -88,6 +88,7 @@ const Page = () => { params, rChainId, rOwmId, + rFormType, api, market, titleMapper, @@ -96,7 +97,7 @@ const Page = () => { } const positionDetailsHrefs = { borrow: '', - supply: getVaultPathname(params, rOwmId), + supply: getVaultPathname(params, rOwmId, 'deposit'), } return isSuccess && !market ? ( @@ -105,7 +106,7 @@ const Page = () => { <> - {rChainId && rOwmId && } + {rChainId && rOwmId && } + useCallback( + async ({ debt, userCollateral, range, slippage, leverageEnabled }) => { + const { setFormValues, setStateByKeys } = useStore.getState().loanCreate + const formValues: FormValues = { + ...DEFAULT_FORM_VALUES, + n: range, + debt: `${debt ?? ''}`, + userCollateral: `${userCollateral ?? ''}`, + } + await setFormValues(api, market, formValues, `${slippage}`, leverageEnabled) + setStateByKeys({ isEditLiqRange: true }) + }, + [api, market], + ) + +const LoanCreate = (pageProps: PageContentProps & { params: MarketUrlParams }) => { + const { rChainId, rOwmId, rFormType, market, params, api } = pageProps + const push = useNavigate() + const shouldUseBorrowUnifiedForm = useCreateLoanMuiForm() + const onUpdate = useOnFormUpdate(pageProps) + + const onLoanCreated = useStore((state) => state.loanCreate.onLoanCreated) + const resetState = useStore((state) => state.loanCreate.resetState) + + type Tab = 'create' | 'leverage' + const tabs: TabOption[] = useMemo( + () => + shouldUseBorrowUnifiedForm + ? // the new borrow form contains both create and leverage functionality + [{ value: 'create' as const, label: t`Borrow` }] + : [ + { value: 'create' as const, label: t`Create Loan` }, + ...(market?.leverage.hasLeverage() ? [{ value: 'leverage' as const, label: t`Leverage` }] : []), + ], + [market?.leverage, shouldUseBorrowUnifiedForm], + ) + + const onCreated = useCallback( + async () => api && market && (await onLoanCreated(api, market)), + [api, market, onLoanCreated], + ) + + return ( + + { + resetState({ rChainId, rOwmId, key }) + push(getLoanCreatePathname(params, rOwmId, key)) + }} + options={tabs} + /> + {shouldUseBorrowUnifiedForm ? ( + + ) : ( + t.design.Layer[1].Fill }}> + + + + + )} + + ) +} + +export default LoanCreate diff --git a/apps/main/src/lend/components/PageLoanCreate/types.ts b/apps/main/src/lend/components/PageLoanCreate/types.ts index 4293951e40..b49d280ce8 100644 --- a/apps/main/src/lend/components/PageLoanCreate/types.ts +++ b/apps/main/src/lend/components/PageLoanCreate/types.ts @@ -10,6 +10,7 @@ import { import type { HealthMode } from '@/llamalend/llamalend.types' import type { Step } from '@ui/Stepper/types' +export type FormType = 'create' | 'vault' | 'leverage' export type StepKey = 'APPROVAL' | 'CREATE' | '' export type InpError = 'too-much' | 'too-much-max' | '' diff --git a/apps/main/src/lend/components/PageLoanManage/LoanBorrowMore/index.tsx b/apps/main/src/lend/components/PageLoanManage/LoanBorrowMore/index.tsx index 0c655bf6f7..91ea35d726 100644 --- a/apps/main/src/lend/components/PageLoanManage/LoanBorrowMore/index.tsx +++ b/apps/main/src/lend/components/PageLoanManage/LoanBorrowMore/index.tsx @@ -21,13 +21,11 @@ import useStore from '@/lend/store/useStore' import { Api, OneWayMarketTemplate, PageContentProps } from '@/lend/types/lend.types' import { _showNoLoanFound } from '@/lend/utils/helpers' import { DEFAULT_HEALTH_MODE } from '@/llamalend/constants' -import { hasLeverage } from '@/llamalend/llama.utils' import type { HealthMode } from '@/llamalend/llamalend.types' import { useLoanExists } from '@/llamalend/queries/loan-exists' import Stack from '@mui/material/Stack' import Typography from '@mui/material/Typography' import AlertBox from '@ui/AlertBox' -import { AppFormContentWrapper } from '@ui/AppForm' import { getActiveStep } from '@ui/Stepper/helpers' import Stepper from '@ui/Stepper/Stepper' import type { Step } from '@ui/Stepper/types' @@ -42,9 +40,17 @@ import { SizesAndSpaces } from '@ui-kit/themes/design/1_sizes_spaces' const { Spacing } = SizesAndSpaces -const LoanBorrowMore = ({ rChainId, rOwmId, isLoaded, api, market, userActiveKey }: PageContentProps) => { +const LoanBorrowMore = ({ + rChainId, + rOwmId, + isLeverage = false, + isLoaded, + api, + market, + userActiveKey, +}: PageContentProps & { isLeverage?: boolean }) => { const isSubscribed = useRef(false) - const isLeverage = !!market && hasLeverage(market) + const activeKey = useStore((state) => state.loanBorrowMore.activeKey) const activeKeyMax = useStore((state) => state.loanBorrowMore.activeKeyMax) const detailInfoLeverage = useStore((state) => state.loanBorrowMore.detailInfoLeverage[activeKey]) @@ -430,14 +436,3 @@ const LoanBorrowMore = ({ rChainId, rOwmId, isLoaded, api, market, userActiveKey } export default LoanBorrowMore - -/** - * The new implementation of LoanBorrowMore with mui isn't ready yet. For now, we wrap the old one for styling. - */ -export const LoanBorrowMoreWrapped = (props: PageContentProps) => ( - t.design.Layer[1].Fill }}> - - - - -) diff --git a/apps/main/src/lend/components/PageLoanManage/LoanCollateralAdd/index.tsx b/apps/main/src/lend/components/PageLoanManage/LoanCollateralAdd/index.tsx index eea70678a7..7fb4485c0b 100644 --- a/apps/main/src/lend/components/PageLoanManage/LoanCollateralAdd/index.tsx +++ b/apps/main/src/lend/components/PageLoanManage/LoanCollateralAdd/index.tsx @@ -19,7 +19,6 @@ import useStore from '@/lend/store/useStore' import { Api, OneWayMarketTemplate, PageContentProps } from '@/lend/types/lend.types' import { _showNoLoanFound } from '@/lend/utils/helpers' import { DEFAULT_HEALTH_MODE } from '@/llamalend/constants' -import { AddCollateralForm } from '@/llamalend/features/manage-loan/components/AddCollateralForm' import { useLoanExists } from '@/llamalend/queries/loan-exists' import AlertBox from '@ui/AlertBox' import { getActiveStep } from '@ui/Stepper/helpers' @@ -259,7 +258,3 @@ const LoanCollateralAdd = ({ rChainId, rOwmId, api, isLoaded, market, userActive } export default LoanCollateralAdd - -export const LoanAddCollateralTab = ({ rChainId, market, isLoaded }: PageContentProps) => ( - -) diff --git a/apps/main/src/lend/components/PageLoanManage/LoanCollateralRemove/index.tsx b/apps/main/src/lend/components/PageLoanManage/LoanCollateralRemove/index.tsx index 0a2e54f410..aa959653e8 100644 --- a/apps/main/src/lend/components/PageLoanManage/LoanCollateralRemove/index.tsx +++ b/apps/main/src/lend/components/PageLoanManage/LoanCollateralRemove/index.tsx @@ -21,7 +21,6 @@ import useStore from '@/lend/store/useStore' import { Api, OneWayMarketTemplate, PageContentProps } from '@/lend/types/lend.types' import { _showNoLoanFound } from '@/lend/utils/helpers' import { DEFAULT_HEALTH_MODE } from '@/llamalend/constants' -import { RemoveCollateralForm } from '@/llamalend/features/manage-loan/components/RemoveCollateralForm' import type { HealthMode } from '@/llamalend/llamalend.types' import { useLoanExists } from '@/llamalend/queries/loan-exists' import AlertBox from '@ui/AlertBox' @@ -297,7 +296,3 @@ const LoanCollateralRemove = ({ rChainId, rOwmId, isLoaded, api, market, userAct } export default LoanCollateralRemove - -export const LoanRemoveCollateralTab = ({ rChainId, market, isLoaded }: PageContentProps) => ( - -) diff --git a/apps/main/src/lend/components/PageLoanManage/LoanRepay/index.tsx b/apps/main/src/lend/components/PageLoanManage/LoanRepay/index.tsx index 8c4c51015e..aa58edd63f 100644 --- a/apps/main/src/lend/components/PageLoanManage/LoanRepay/index.tsx +++ b/apps/main/src/lend/components/PageLoanManage/LoanRepay/index.tsx @@ -20,7 +20,6 @@ import { Api, FormError, type MarketUrlParams, OneWayMarketTemplate, PageContent import { _showNoLoanFound } from '@/lend/utils/helpers' import { getCollateralListPathname } from '@/lend/utils/utilsRouter' import { DEFAULT_HEALTH_MODE } from '@/llamalend/constants' -import { RepayForm } from '@/llamalend/features/manage-loan/components/RepayForm' import type { HealthMode } from '@/llamalend/llamalend.types' import { useLoanExists } from '@/llamalend/queries/loan-exists' import Stack from '@mui/material/Stack' @@ -538,11 +537,3 @@ const LoanRepay = ({ } export default LoanRepay - -export const LoanRepayFromWalletTab = ({ rChainId, market, isLoaded }: PageContentProps) => ( - -) - -export const LoanRepayFromCollateralTab = ({ rChainId, market, isLoaded }: PageContentProps) => ( - -) diff --git a/apps/main/src/lend/components/PageLoanManage/LoanSelfLiquidation/index.tsx b/apps/main/src/lend/components/PageLoanManage/LoanSelfLiquidation/index.tsx index 15efbecd39..b1647b2693 100644 --- a/apps/main/src/lend/components/PageLoanManage/LoanSelfLiquidation/index.tsx +++ b/apps/main/src/lend/components/PageLoanManage/LoanSelfLiquidation/index.tsx @@ -45,7 +45,7 @@ const LoanSelfLiquidation = ({ market, userActiveKey, params, -}: PageContentProps) => { +}: PageContentProps & { params: MarketUrlParams }) => { const isSubscribed = useRef(false) const formEstGas = useStore((state) => state.loanSelfLiquidation.formEstGas) const formStatus = useStore((state) => state.loanSelfLiquidation.formStatus) diff --git a/apps/main/src/lend/components/PageLoanManage/ManageLoanTabs.tsx b/apps/main/src/lend/components/PageLoanManage/ManageLoanTabs.tsx deleted file mode 100644 index ee4e2ad5a4..0000000000 --- a/apps/main/src/lend/components/PageLoanManage/ManageLoanTabs.tsx +++ /dev/null @@ -1,103 +0,0 @@ -import LoanBorrowMore, { LoanBorrowMoreWrapped } from '@/lend/components/PageLoanManage/LoanBorrowMore' -import LoanCollateralAdd, { LoanAddCollateralTab } from '@/lend/components/PageLoanManage/LoanCollateralAdd' -import LoanCollateralRemove, { LoanRemoveCollateralTab } from '@/lend/components/PageLoanManage/LoanCollateralRemove' -import LoanRepay, { - LoanRepayFromCollateralTab, - LoanRepayFromWalletTab, -} from '@/lend/components/PageLoanManage/LoanRepay' -import LoanSelfLiquidation from '@/lend/components/PageLoanManage/LoanSelfLiquidation' -import networks from '@/lend/networks' -import { type MarketUrlParams, PageContentProps } from '@/lend/types/lend.types' -import { useClosePositionTab } from '@/llamalend/features/manage-soft-liquidation/hooks/useClosePositionTab' -import { useImproveHealthTab } from '@/llamalend/features/manage-soft-liquidation/hooks/useImproveHealthTab' -import { ClosePosition } from '@/llamalend/features/manage-soft-liquidation/ui/tabs/ClosePosition' -import { ImproveHealth } from '@/llamalend/features/manage-soft-liquidation/ui/tabs/ImproveHealth' -import { useManageLoanMuiForm, useManageSoftLiquidation } from '@ui-kit/hooks/useFeatureFlags' -import { t } from '@ui-kit/lib/i18n' -import { type FormTab, FormTabs } from '@ui-kit/shared/ui/FormTabs/FormTabs' - -type ManageLoanProps = PageContentProps - -const ImproveHealthTab = ({ rChainId, rOwmId }: ManageLoanProps) => ( - -) - -const ClosePositionTab = ({ rChainId, rOwmId }: ManageLoanProps) => ( - -) - -const LendManageLegacyMenu = [ - { - value: 'loan', - label: t`Borrow`, - subTabs: [ - { value: 'loan-increase', label: t`Borrow more`, component: LoanBorrowMore }, - { value: 'loan-decrease', label: t`Repay`, component: LoanRepay }, - { value: 'loan-liquidate', label: t`Self-liquidate`, component: LoanSelfLiquidation }, - ], - }, - { - value: 'collateral', - label: t`Borrow`, - subTabs: [ - { value: 'collateral-increase', label: t`Add collateral`, component: LoanCollateralAdd }, - { value: 'collateral-decrease', label: t`Remove collateral`, component: LoanCollateralRemove }, - ], - }, - { - value: 'leverage', - label: t`Leverage`, - visible: ({ market }) => market?.leverage?.hasLeverage(), - component: LoanBorrowMore, - }, -] satisfies FormTab[] - -const LendManageNewMenu = [ - { - value: 'borrow', - label: ({ market }) => (market?.leverage?.hasLeverage() ? t`Leverage` : t`Borrow`), - component: LoanBorrowMoreWrapped, - }, - { - value: 'repay', - label: t`Repay`, - subTabs: [ - { value: 'from-wallet', label: t`From wallet`, component: LoanRepayFromWalletTab }, - { value: 'from-collateral', label: t`From collateral`, component: LoanRepayFromCollateralTab }, - ], - }, - { - value: 'collateral', - label: t`Collateral`, - subTabs: [ - { value: 'add', label: t`Add`, component: LoanAddCollateralTab }, - { value: 'remove', label: t`Remove`, component: LoanRemoveCollateralTab }, - ], - }, -] satisfies FormTab[] - -const LendManageSoftLiquidationMenu = [ - { - value: 'soft-liquidation', - label: t`Manage soft liquidation`, - subTabs: [ - { value: 'improve-health', label: t`Improve health`, component: ImproveHealthTab }, - { value: 'close-position', label: t`Close position`, component: ClosePositionTab }, - ], - }, -] satisfies FormTab[] - -export const ManageLoanTabs = ({ - isInSoftLiquidation, - ...pageProps -}: ManageLoanProps & { isInSoftLiquidation: boolean | undefined }) => { - const shouldUseSoftLiquidation = useManageSoftLiquidation() && isInSoftLiquidation - const shouldUseManageLoanMuiForm = useManageLoanMuiForm() - const menu = shouldUseSoftLiquidation - ? LendManageSoftLiquidationMenu - : shouldUseManageLoanMuiForm - ? LendManageNewMenu - : LendManageLegacyMenu - const shouldWrap = menu === LendManageLegacyMenu - return -} diff --git a/apps/main/src/lend/components/PageLoanManage/Page.tsx b/apps/main/src/lend/components/PageLoanManage/Page.tsx index 0ca3e148c9..4613d40239 100644 --- a/apps/main/src/lend/components/PageLoanManage/Page.tsx +++ b/apps/main/src/lend/components/PageLoanManage/Page.tsx @@ -3,7 +3,8 @@ import type { Address } from 'viem' import CampaignRewardsBanner from '@/lend/components/CampaignRewardsBanner' import { MarketInformationComp } from '@/lend/components/MarketInformationComp' import { MarketInformationTabs } from '@/lend/components/MarketInformationTabs' -import { ManageLoanTabs } from '@/lend/components/PageLoanManage/ManageLoanTabs' +import LoanMange from '@/lend/components/PageLoanManage/index' +import type { DetailInfoTypes } from '@/lend/components/PageLoanManage/types' import { useOneWayMarket } from '@/lend/entities/chain' import { useBorrowPositionDetails } from '@/lend/hooks/useBorrowPositionDetails' import { useLendPageTitle } from '@/lend/hooks/useLendPageTitle' @@ -15,6 +16,7 @@ import useStore from '@/lend/store/useStore' import { type MarketUrlParams } from '@/lend/types/lend.types' import { getVaultPathname, parseMarketParams } from '@/lend/utils/helpers' import { getCollateralListPathname } from '@/lend/utils/utilsRouter' +import { ManageSoftLiquidation } from '@/llamalend/features/manage-soft-liquidation' import { MarketDetails } from '@/llamalend/features/market-details' import { BorrowPositionDetails, NoPosition } from '@/llamalend/features/market-position-details' import { UserPositionHistory } from '@/llamalend/features/user-position-history' @@ -28,6 +30,7 @@ import Box from '@ui/Box' import { ConnectWalletPrompt, isLoading, useConnection, useWallet } from '@ui-kit/features/connect-wallet' import { useLayoutStore } from '@ui-kit/features/layout' import { useParams } from '@ui-kit/hooks/router' +import { useManageSoftLiquidation } from '@ui-kit/hooks/useFeatureFlags' import { t } from '@ui-kit/lib/i18n' import { REFRESH_INTERVAL } from '@ui-kit/lib/model' import { ErrorPage } from '@ui-kit/pages/ErrorPage' @@ -37,12 +40,13 @@ const { Spacing } = SizesAndSpaces const Page = () => { const params = useParams() - const { rMarket, rChainId } = parseMarketParams(params) + const { rMarket, rChainId, rFormType } = parseMarketParams(params) const { llamaApi: api = null, connectState } = useConnection() const titleMapper = useTitleMapper() const { data: market, isSuccess } = useOneWayMarket(rChainId, rMarket) const rOwmId = market?.id ?? '' const userActiveKey = helpers.getUserActiveKey(api, market!) + const isMdUp = useLayoutStore((state) => state.isMdUp) const isPageVisible = useLayoutStore((state) => state.isPageVisible) const fetchAllMarketDetails = useStore((state) => state.markets.fetchAll) const fetchAllUserMarketDetails = useStore((state) => state.user.fetchAll) @@ -84,8 +88,15 @@ const Page = () => { network, }) - const isInSoftLiquidation = - borrowPositionDetails.liquidationAlert.softLiquidation || borrowPositionDetails.liquidationAlert.hardLiquidation + const isManageSoftLiq = + useManageSoftLiquidation() && + (borrowPositionDetails.liquidationAlert.softLiquidation || borrowPositionDetails.liquidationAlert.hardLiquidation) + + // set tabs + const DETAIL_INFO_TYPES: { key: DetailInfoTypes; label: string }[] = [{ label: t`Market Details`, key: 'market' }] + if (signerAddress) { + DETAIL_INFO_TYPES.push({ label: t`Your Details`, key: 'user' }) + } useEffect(() => { if (api && market && isPageVisible) { @@ -112,6 +123,7 @@ const Page = () => { params, rChainId, rOwmId, + rFormType, isLoaded, api, market, @@ -121,7 +133,7 @@ const Page = () => { const positionDetailsHrefs = { borrow: '', - supply: getVaultPathname(params, rOwmId), + supply: getVaultPathname(params, rOwmId, 'deposit'), } return isSuccess && !market ? ( @@ -130,7 +142,13 @@ const Page = () => { <> - {rChainId && rOwmId && } + {rChainId && + rOwmId && + (isManageSoftLiq ? ( + + ) : ( + + ))} [] = [ + { value: 'loan-increase', label: t`Borrow more` }, + { value: 'loan-decrease', label: t`Repay` }, + { value: 'loan-liquidate', label: t`Self-liquidate` }, +] + +const tabsCollateral: TabOption[] = [ + { value: 'collateral-increase', label: t`Add collateral` }, + { value: 'collateral-decrease', label: t`Remove collateral` }, +] + +const ManageLoan = (pageProps: PageContentProps & { params: MarketUrlParams }) => { + const { rChainId, rOwmId, rFormType, market, params, isLoaded } = pageProps + const push = useNavigate() + const shouldUseManageLoanMuiForm = useManageLoanMuiForm() + const useMuiForm = shouldUseManageLoanMuiForm && !!market + + type Tab = 'loan' | 'collateral' | 'leverage' + const tabs: TabOption[] = useMemo( + () => [ + { value: 'loan' as const, label: t`Loan` }, + { value: 'collateral' as const, label: t`Collateral` }, + ...(market?.leverage?.hasLeverage() ? [{ value: 'leverage' as const, label: t`Leverage` }] : []), + ], + [market?.leverage], + ) + + type SubTab = LoanFormType | CollateralFormType | LeverageFormType + const [subTab, setSubTab] = useState('loan-increase') + + const subTabs = useMemo( + () => (!rFormType || rFormType === 'loan' ? tabsLoan : rFormType === 'collateral' ? tabsCollateral : []), + [rFormType], + ) + + useEffect(() => setSubTab(subTabs[0]?.value), [subTabs]) + + return ( + + push(getLoanManagePathname(params, rOwmId, key))} + options={tabs} + /> + {useMuiForm ? ( + <> + + + {subTab === 'loan-increase' && ( + t.design.Layer[1].Fill }}> + + + + + )} + {subTab === 'loan-decrease' && market && ( + {}} + /> + )} + {subTab === 'loan-liquidate' && } + {subTab === 'collateral-increase' && market && ( + {}} + /> + )} + {subTab === 'collateral-decrease' && market && ( + {}} + /> + )} + {/** Leverage has no subtabs */} + {rFormType === 'leverage' && ( + t.design.Layer[1].Fill }}> + + + + + )} + + ) : ( + t.design.Layer[1].Fill }}> + + + + {subTab === 'loan-increase' && } + {subTab === 'loan-decrease' && } + {subTab === 'loan-liquidate' && } + {subTab === 'collateral-increase' && } + {subTab === 'collateral-decrease' && } + {/** Leverage has no subtabs */} + {rFormType === 'leverage' && } + + + )} + + ) +} + +export default ManageLoan diff --git a/apps/main/src/lend/components/PageLoanManage/types.ts b/apps/main/src/lend/components/PageLoanManage/types.ts index a1a7aef085..b57bb5ee69 100644 --- a/apps/main/src/lend/components/PageLoanManage/types.ts +++ b/apps/main/src/lend/components/PageLoanManage/types.ts @@ -1,6 +1,10 @@ import { FutureRates } from '@/lend/types/lend.types' export type DetailInfoTypes = 'user' | 'market' +export type FormType = 'loan' | 'collateral' | 'leverage' +export type LoanFormType = 'loan-increase' | 'loan-decrease' | 'loan-liquidate' +export type CollateralFormType = 'collateral-increase' | 'collateral-decrease' +export type LeverageFormType = 'leverage-borrow-more' export type FormStatus = { isApproved: boolean diff --git a/apps/main/src/lend/components/PageVault/Page.tsx b/apps/main/src/lend/components/PageVault/Page.tsx index 8aa4162fa3..c93412ea6b 100644 --- a/apps/main/src/lend/components/PageVault/Page.tsx +++ b/apps/main/src/lend/components/PageVault/Page.tsx @@ -2,7 +2,7 @@ import { useEffect, useState } from 'react' import CampaignRewardsBanner from '@/lend/components/CampaignRewardsBanner' import { MarketInformationComp } from '@/lend/components/MarketInformationComp' import { MarketInformationTabs } from '@/lend/components/MarketInformationTabs' -import { VaultTabs } from '@/lend/components/PageVault/VaultTabs' +import Vault from '@/lend/components/PageVault/index' import { useOneWayMarket } from '@/lend/entities/chain' import { useLendPageTitle } from '@/lend/hooks/useLendPageTitle' import { useMarketDetails } from '@/lend/hooks/useMarketDetails' @@ -36,7 +36,7 @@ const { Spacing } = SizesAndSpaces const Page = () => { const params = useParams() - const { rMarket, rChainId } = parseMarketParams(params) + const { rMarket, rChainId, rFormType } = parseMarketParams(params) const { connect, provider } = useWallet() const { llamaApi: api = null, connectState } = useConnection() const titleMapper = useTitleMapper() @@ -99,6 +99,7 @@ const Page = () => { params, rChainId, rOwmId, + rFormType, isLoaded, api, market, @@ -107,7 +108,10 @@ const Page = () => { } const borrowPathnameFn = loanExists ? getLoanManagePathname : getLoanCreatePathname - const positionDetailsHrefs = { borrow: borrowPathnameFn(params, rOwmId), supply: '' } + const positionDetailsHrefs = { + borrow: borrowPathnameFn(params, rOwmId, ''), + supply: '', + } const hasSupplyPosition = (supplyPositionDetails.shares.value ?? 0) > 0 return isSuccess && !market ? ( @@ -115,7 +119,7 @@ const Page = () => { ) : provider ? ( <> - {rChainId && rOwmId && } + {rChainId && rOwmId && } { - const rFormType = 'deposit' +const VaultDepositMint = ({ rChainId, rOwmId, rFormType, isLoaded, api, market, userActiveKey }: PageContentProps) => { const isSubscribed = useRef(false) const marketAlert = useMarketAlert(rChainId, rOwmId) @@ -177,7 +176,7 @@ const VaultDepositMint = ({ rChainId, rOwmId, isLoaded, api, market, userActiveK // steps useEffect(() => { - if (isLoaded && api && market) { + if (isLoaded && api && market && rFormType) { const updatedSteps = getSteps(activeKey, rFormType, api, market, formStatus, formValues, steps) setSteps(updatedSteps) } diff --git a/apps/main/src/lend/components/PageVault/VaultStake/index.tsx b/apps/main/src/lend/components/PageVault/VaultStake/index.tsx index c88b03bf1c..7a20024fdd 100644 --- a/apps/main/src/lend/components/PageVault/VaultStake/index.tsx +++ b/apps/main/src/lend/components/PageVault/VaultStake/index.tsx @@ -24,8 +24,7 @@ import { t } from '@ui-kit/lib/i18n' import { LargeTokenInput } from '@ui-kit/shared/ui/LargeTokenInput' import { decimal, type Decimal } from '@ui-kit/utils' -const VaultStake = ({ rChainId, rOwmId, isLoaded, api, market, userActiveKey }: PageContentProps) => { - const rFormType = 'stake' +const VaultStake = ({ rChainId, rOwmId, rFormType, isLoaded, api, market, userActiveKey }: PageContentProps) => { const isSubscribed = useRef(false) const activeKey = useStore((state) => state.vaultStake.activeKey) diff --git a/apps/main/src/lend/components/PageVault/VaultTabs.tsx b/apps/main/src/lend/components/PageVault/VaultTabs.tsx deleted file mode 100644 index 49e8e0ba1f..0000000000 --- a/apps/main/src/lend/components/PageVault/VaultTabs.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import VaultClaim from '@/lend/components/PageVault/VaultClaim' -import VaultDepositMint from '@/lend/components/PageVault/VaultDepositMint' -import VaultStake from '@/lend/components/PageVault/VaultStake' -import VaultUnstake from '@/lend/components/PageVault/VaultUnstake' -import VaultWithdrawRedeem from '@/lend/components/PageVault/VaultWithdrawRedeem' -import { type MarketUrlParams, PageContentProps } from '@/lend/types/lend.types' -import { t } from '@ui-kit/lib/i18n' -import { FormTab, FormTabs } from '@ui-kit/shared/ui/FormTabs/FormTabs' - -type VaultProps = PageContentProps - -const menu = [ - { - value: 'deposit', - label: t`Deposit`, - subTabs: [ - { value: 'deposit', label: t`Deposit`, component: VaultDepositMint }, - { value: 'stake', label: t`Stake`, component: VaultStake }, - ], - }, - { - value: 'withdraw', - label: t`Withdraw`, - subTabs: [ - { value: 'withdraw', label: t`Withdraw`, component: VaultWithdrawRedeem }, - { value: 'unstake', label: t`Unstake`, component: VaultUnstake }, - { value: 'claim', label: t`Claim Rewards`, component: VaultClaim }, - ], - }, -] satisfies FormTab[] - -export const VaultTabs = (pageProps: VaultProps) => diff --git a/apps/main/src/lend/components/PageVault/VaultUnstake/index.tsx b/apps/main/src/lend/components/PageVault/VaultUnstake/index.tsx index 4949ab340f..94278d61f5 100644 --- a/apps/main/src/lend/components/PageVault/VaultUnstake/index.tsx +++ b/apps/main/src/lend/components/PageVault/VaultUnstake/index.tsx @@ -23,8 +23,7 @@ import { t } from '@ui-kit/lib/i18n' import { LargeTokenInput } from '@ui-kit/shared/ui/LargeTokenInput' import { decimal, type Decimal } from '@ui-kit/utils' -const VaultUnstake = ({ rChainId, rOwmId, isLoaded, api, market, userActiveKey }: PageContentProps) => { - const rFormType = 'unstake' +const VaultUnstake = ({ rChainId, rOwmId, rFormType, isLoaded, api, market, userActiveKey }: PageContentProps) => { const isSubscribed = useRef(false) const activeKey = useStore((state) => state.vaultUnstake.activeKey) diff --git a/apps/main/src/lend/components/PageVault/VaultWithdrawRedeem/index.tsx b/apps/main/src/lend/components/PageVault/VaultWithdrawRedeem/index.tsx index e4a2d01044..b44ea416ba 100644 --- a/apps/main/src/lend/components/PageVault/VaultWithdrawRedeem/index.tsx +++ b/apps/main/src/lend/components/PageVault/VaultWithdrawRedeem/index.tsx @@ -27,8 +27,15 @@ import { t } from '@ui-kit/lib/i18n' import { LargeTokenInput } from '@ui-kit/shared/ui/LargeTokenInput' import { decimal, type Decimal } from '@ui-kit/utils' -const VaultWithdrawRedeem = ({ rChainId, rOwmId, isLoaded, api, market, userActiveKey }: PageContentProps) => { - const rFormType = 'withdraw' +const VaultWithdrawRedeem = ({ + rChainId, + rOwmId, + rFormType, + isLoaded, + api, + market, + userActiveKey, +}: PageContentProps) => { const isSubscribed = useRef(false) const activeKey = useStore((state) => state.vaultWithdrawRedeem.activeKey) diff --git a/apps/main/src/lend/components/PageVault/index.tsx b/apps/main/src/lend/components/PageVault/index.tsx new file mode 100644 index 0000000000..a9e44c57c5 --- /dev/null +++ b/apps/main/src/lend/components/PageVault/index.tsx @@ -0,0 +1,72 @@ +import { useEffect, useMemo, useState } from 'react' +import VaultClaim from '@/lend/components/PageVault/VaultClaim' +import VaultDepositMint from '@/lend/components/PageVault/VaultDepositMint' +import VaultStake from '@/lend/components/PageVault/VaultStake' +import VaultUnstake from '@/lend/components/PageVault/VaultUnstake' +import VaultWithdrawRedeem from '@/lend/components/PageVault/VaultWithdrawRedeem' +import { + type MarketUrlParams, + PageContentProps, + type VaultDepositFormType, + type VaultWithdrawFormType, +} from '@/lend/types/lend.types' +import { getVaultPathname } from '@/lend/utils/utilsRouter' +import Stack from '@mui/material/Stack' +import { AppFormContentWrapper } from '@ui/AppForm' +import { useNavigate } from '@ui-kit/hooks/router' +import { t } from '@ui-kit/lib/i18n' +import { type TabOption, TabsSwitcher } from '@ui-kit/shared/ui/TabsSwitcher' + +type FormType = 'deposit' | 'withdraw' + +const tabs: TabOption[] = [ + { value: 'deposit', label: t`Deposit` }, + { value: 'withdraw', label: t`Withdraw` }, +] + +const tabsDeposit: TabOption[] = [ + { value: 'deposit', label: t`Deposit` }, + { value: 'stake', label: t`Stake` }, +] + +const tabsWithdraw: TabOption[] = [ + { value: 'withdraw', label: t`Withdraw` }, + { value: 'unstake', label: t`Unstake` }, + { value: 'claim', label: t`Claim Rewards` }, +] + +const Vault = (pageProps: PageContentProps & { params: MarketUrlParams }) => { + const { rOwmId, rFormType, params } = pageProps + const push = useNavigate() + + type SubTab = VaultDepositFormType | VaultWithdrawFormType + const [subTab, setSubTab] = useState('deposit') + + const subTabs = useMemo(() => (!rFormType || rFormType === 'deposit' ? tabsDeposit : tabsWithdraw), [rFormType]) + useEffect(() => setSubTab(subTabs[0]?.value), [subTabs]) + + return ( + t.design.Layer[1].Fill }}> + push(getVaultPathname(params, rOwmId, key))} + options={tabs} + fullWidth + /> + + + + + {subTab === 'deposit' && } + {subTab === 'stake' && } + {subTab === 'withdraw' && } + {subTab === 'unstake' && } + {subTab === 'claim' && } + + + ) +} + +export default Vault diff --git a/apps/main/src/lend/constants.ts b/apps/main/src/lend/constants.ts index 06c99b8b98..cac97898b8 100644 --- a/apps/main/src/lend/constants.ts +++ b/apps/main/src/lend/constants.ts @@ -1,19 +1,13 @@ import { LEND_ROUTES } from '@ui-kit/shared/routes' -export const MARKET_ROUTES = { - PAGE_CREATE: '/create', - PAGE_MANAGE: '/manage', - PAGE_VAULT: '/vault', -} as const - -export type LendMarketRoute = (typeof MARKET_ROUTES)[keyof typeof MARKET_ROUTES] - export const ROUTE = { ...LEND_ROUTES, PAGE_INTEGRATIONS: '/integrations', - ...MARKET_ROUTES, + PAGE_CREATE: '/create', + PAGE_MANAGE: '/manage', + PAGE_VAULT: '/vault', PAGE_404: '/404', -} as const +} // TODO: translation export const NOFITY_MESSAGE = { diff --git a/apps/main/src/lend/hooks/useBorrowPositionDetails.ts b/apps/main/src/lend/hooks/useBorrowPositionDetails.ts index 653c9dff10..b465193a93 100644 --- a/apps/main/src/lend/hooks/useBorrowPositionDetails.ts +++ b/apps/main/src/lend/hooks/useBorrowPositionDetails.ts @@ -34,19 +34,11 @@ export const useBorrowPositionDetails = ({ }: UseBorrowPositionDetailsProps): BorrowPositionDetailsProps => { const { controller } = market?.addresses ?? {} const { address: userAddress } = useAccount() - const { data: loanExists } = useLoanExists({ + const { data: userLoanDetails, isLoading: isUserLoanDetailsLoading } = useUserLoanDetails({ chainId, marketId, userAddress, }) - const { data: userLoanDetails, isLoading: isUserLoanDetailsLoading } = useUserLoanDetails( - { - chainId, - marketId, - userAddress, - }, - !!loanExists, - ) const { bands, health, @@ -57,6 +49,11 @@ export const useBorrowPositionDetails = ({ state: { collateral, borrowed, debt } = {}, } = userLoanDetails ?? {} const prices = useStore((state) => state.markets.pricesMapper[chainId]?.[marketId]) + const { data: loanExists } = useLoanExists({ + chainId, + marketId, + userAddress, + }) const { data: userPnl, isLoading: isUserPnlLoading } = useUserPnl({ chainId, marketId, diff --git a/apps/main/src/lend/types/lend.types.ts b/apps/main/src/lend/types/lend.types.ts index 9f3639f2af..3da350ce59 100644 --- a/apps/main/src/lend/types/lend.types.ts +++ b/apps/main/src/lend/types/lend.types.ts @@ -19,7 +19,7 @@ export type EstimatedGas = number | number[] | null export type OneWayMarketTemplate = LendMarketTemplate // todo: use LendMarketTemplate consistently export type NetworkUrlParams = { network: NetworkEnum } -export type MarketUrlParams = NetworkUrlParams & { market: string } +export type MarketUrlParams = NetworkUrlParams & { market: string; formType?: RFormType } export type UrlParams = NetworkUrlParams & Partial export interface NetworkConfig extends BaseConfig< @@ -80,10 +80,17 @@ export type ExpectedBorrowed = { avgPrice: string } -export type PageContentProps = { - params: T +export type VaultCreateFormType = 'create' | 'leverage' +export type VaultDepositFormType = 'deposit' | 'stake' +export type VaultWithdrawFormType = 'withdraw' | 'unstake' | 'claim' +export type VaultManageFormType = 'loan' | 'collateral' +export type RFormType = VaultCreateFormType | VaultDepositFormType | VaultWithdrawFormType | VaultManageFormType | '' + +export type PageContentProps = { + params: UrlParams rChainId: ChainId rOwmId: string + rFormType: RFormType userActiveKey: string isLoaded: boolean api: LlamaApi | null diff --git a/apps/main/src/lend/utils/helpers.ts b/apps/main/src/lend/utils/helpers.ts index 141a72dd62..72f14081fe 100644 --- a/apps/main/src/lend/utils/helpers.ts +++ b/apps/main/src/lend/utils/helpers.ts @@ -30,6 +30,14 @@ export function getErrorMessage(error: CustomError, defaultErrorMessage: string) return errorMessage } +export function scrollToTop() { + window.scroll({ + top: 0, + left: 0, + behavior: 'smooth', + }) +} + export function fulfilledValue(result: PromiseSettledResult) { if (result.status === 'fulfilled') { return result.value diff --git a/apps/main/src/lend/utils/utilsRouter.ts b/apps/main/src/lend/utils/utilsRouter.ts index ac2bbde7c8..2c490a76f1 100644 --- a/apps/main/src/lend/utils/utilsRouter.ts +++ b/apps/main/src/lend/utils/utilsRouter.ts @@ -1,4 +1,4 @@ -import { type LendMarketRoute, ROUTE } from '@/lend/constants' +import { ROUTE } from '@/lend/constants' import { networksIdMapper } from '@/lend/networks' import { type MarketUrlParams, NetworkUrlParams, type UrlParams } from '@/lend/types/lend.types' import { getInternalUrl, LLAMALEND_ROUTES } from '@ui-kit/shared/routes' @@ -9,19 +9,17 @@ export const getPath = ({ network }: UrlParams, route: string) => getInternalUrl export const getCollateralListPathname = ({ network }: NetworkUrlParams) => getInternalUrl('llamalend', network, LLAMALEND_ROUTES.PAGE_MARKETS) -const getMarketPathname = ({ network }: UrlParams, marketId: string, page: LendMarketRoute) => - `${getInternalUrl('lend', network, ROUTE.PAGE_MARKETS)}/${marketId}${page}` +export const getLoanCreatePathname = (params: UrlParams, owmId: string, formType: string) => + getPath(params, `${ROUTE.PAGE_MARKETS}/${owmId}${ROUTE.PAGE_CREATE}${formType === 'create' ? '' : `/${formType}`}`) -export const getLoanCreatePathname = (params: UrlParams, marketId: string) => - getMarketPathname(params, marketId, ROUTE.PAGE_CREATE) +export const getLoanManagePathname = (params: UrlParams, owmId: string, formType: string) => + getPath(params, `${ROUTE.PAGE_MARKETS}/${owmId}${ROUTE.PAGE_MANAGE}/${formType}`) -export const getLoanManagePathname = (params: UrlParams, marketId: string) => - getMarketPathname(params, marketId, ROUTE.PAGE_MANAGE) +export const getVaultPathname = (params: UrlParams, owmId: string, formType: string) => + getPath(params, `${ROUTE.PAGE_MARKETS}/${owmId}${ROUTE.PAGE_VAULT}${formType === 'vault' ? '' : `/${formType}`}`) -export const getVaultPathname = (params: UrlParams, marketId: string) => - getMarketPathname(params, marketId, ROUTE.PAGE_VAULT) - -export const parseMarketParams = ({ market, network }: MarketUrlParams) => ({ +export const parseMarketParams = ({ formType, market, network }: MarketUrlParams) => ({ rMarket: market.toLowerCase(), rChainId: networksIdMapper[network], + rFormType: formType ?? '', }) diff --git a/apps/main/src/llamalend/features/manage-loan/components/AddCollateralForm.tsx b/apps/main/src/llamalend/features/manage-loan/components/AddCollateralForm.tsx index 2b7996d65b..e220cdcd39 100644 --- a/apps/main/src/llamalend/features/manage-loan/components/AddCollateralForm.tsx +++ b/apps/main/src/llamalend/features/manage-loan/components/AddCollateralForm.tsx @@ -25,7 +25,7 @@ export const AddCollateralForm = ({ networks: NetworkDict chainId: ChainId enabled?: boolean - onAdded?: NonNullable + onAdded: NonNullable }) => { const network = networks[chainId] const [isOpen, , , toggle] = useSwitch(false) diff --git a/apps/main/src/llamalend/features/manage-loan/components/RemoveCollateralForm.tsx b/apps/main/src/llamalend/features/manage-loan/components/RemoveCollateralForm.tsx index 9041ad73ab..627257bccf 100644 --- a/apps/main/src/llamalend/features/manage-loan/components/RemoveCollateralForm.tsx +++ b/apps/main/src/llamalend/features/manage-loan/components/RemoveCollateralForm.tsx @@ -27,7 +27,7 @@ export const RemoveCollateralForm = ({ networks: NetworkDict chainId: ChainId enabled?: boolean - onRemoved?: NonNullable + onRemoved: NonNullable }) => { const network = networks[chainId] const [isOpen, , , toggle] = useSwitch(false) diff --git a/apps/main/src/llamalend/features/manage-loan/components/RepayForm.tsx b/apps/main/src/llamalend/features/manage-loan/components/RepayForm.tsx index 2a3eead762..ea5a326864 100644 --- a/apps/main/src/llamalend/features/manage-loan/components/RepayForm.tsx +++ b/apps/main/src/llamalend/features/manage-loan/components/RepayForm.tsx @@ -20,18 +20,12 @@ export const RepayForm = ({ chainId, enabled, onRepaid, - fromCollateral, - fromWallet, - fromBorrowed, }: { market: LlamaMarketTemplate | undefined networks: NetworkDict chainId: ChainId enabled?: boolean - onRepaid?: RepayOptions['onRepaid'] - fromCollateral?: boolean - fromWallet?: boolean - fromBorrowed?: boolean + onRepaid: NonNullable }) => { const network = networks[chainId] const [isOpen, , , toggle] = useSwitch(false) @@ -92,39 +86,33 @@ export const RepayForm = ({ } > }> - {fromCollateral && ( - - )} - {fromWallet && ( - - )} - {fromBorrowed && ( - - )} + + +