Skip to content

Commit

Permalink
Add easy borrow form (#421)
Browse files Browse the repository at this point in the history
  • Loading branch information
yivlad authored Nov 7, 2024
1 parent 7ac2cd9 commit 63fce42
Show file tree
Hide file tree
Showing 20 changed files with 569 additions and 520 deletions.
6 changes: 4 additions & 2 deletions packages/app/.storybook/decorators.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,10 @@ export function WithTooltipProvider() {
export function WithClassname(classname: string) {
return function WithClassname(Story: StoryFn) {
return (
<div className={classname} data-sb="decorator">
<Story />
<div data-sb="decorator">
<div className={classname}>
<Story />
</div>
</div>
)
}
Expand Down
115 changes: 71 additions & 44 deletions packages/app/src/features/easy-borrow/components/EasyBorrowPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@ import { UserPositionSummary } from '@/domain/market-info/marketInfo'
import { Percentage } from '@/domain/types/NumericValues'
import { ActionsContainer } from '@/features/actions/ActionsContainer'
import { InjectedActionsContext, Objective } from '@/features/actions/logic/types'
import { Button } from '@/ui/atoms/button/Button'
import { Panel } from '@/ui/atoms/panel/Panel'
import { Form } from '@/ui/atoms/form/Form'
import { Button } from '@/ui/atoms/new/button/Button'
import { IconButton } from '@/ui/atoms/new/icon-button/IconButton'
import { Panel } from '@/ui/atoms/new/panel/Panel'
import { Typography } from '@/ui/atoms/typography/Typography'
import { ConnectOrSandboxCTAButtonGroup } from '@/ui/molecules/connect-or-sandbox-cta-button-group/ConnectOrSandboxCTAButtonGroup'
import { HealthFactorPanel } from '@/ui/organisms/health-factor-panel/HealthFactorPanel'
import { RiskAcknowledgement } from '@/ui/organisms/risk-acknowledgement/RiskAcknowledgement'
import { Trans } from '@lingui/macro'
import { X } from 'lucide-react'
import { XIcon } from 'lucide-react'
import { UseFormReturn } from 'react-hook-form'
import { FormFieldsForAssetClass } from '../logic/form/form'
import { EasyBorrowFormSchema } from '../logic/form/validation'
Expand Down Expand Up @@ -40,55 +42,80 @@ export interface EasyBorrowPanelProps {
}

export function EasyBorrowPanel(props: EasyBorrowPanelProps) {
const { pageStatus, updatedPositionSummary, objectives, liquidationDetails, healthFactorPanelRef, actionsContext } =
props
const {
pageStatus,
updatedPositionSummary,
objectives,
liquidationDetails,
healthFactorPanelRef,
actionsContext,
form,
guestMode,
openConnectModal,
openSandboxModal,
} = props

const disabled = pageStatus.state !== 'form'

return (
<Panel.Wrapper className="flex min-w-full max-w-3xl flex-col self-center p-4 md:p-8">
<Panel className="flex min-w-full max-w-3xl flex-col self-center p-4 md:p-8">
<div className="mb-6 flex h-10 flex-row items-center justify-between">
<Typography variant="h3">
<Trans>Borrow</Trans>
</Typography>
<Typography variant="h3">Borrow</Typography>
{pageStatus.state === 'confirmation' && (
<Button onClick={pageStatus.onProceedToForm} variant="icon" className="-mr-4">
<X size={28} />
</Button>
<IconButton onClick={pageStatus.onProceedToForm} variant="transparent" size="l" icon={XIcon} />
)}
</div>

<EasyBorrowForm
{...props}
borrowRate={props.borrowDetails.borrowRate}
onSubmit={pageStatus.submitForm}
disabled={pageStatus.state !== 'form'}
/>
<Form {...form}>
<form onSubmit={form.handleSubmit(pageStatus.submitForm)} className="flex flex-col gap-4">
<Panel className="bg-tertiary p-1.5" spacing="none">
<EasyBorrowForm {...props} disabled={disabled} />

{pageStatus.state === 'confirmation' && (
<div className="mt-6 flex flex-col gap-6">
<HealthFactorPanel
hf={updatedPositionSummary.healthFactor}
liquidationDetails={liquidationDetails}
variant="full-details"
ref={healthFactorPanelRef}
/>
{props.riskAcknowledgement.warning && (
<RiskAcknowledgement
onStatusChange={props.riskAcknowledgement.onStatusChange}
warning={props.riskAcknowledgement.warning}
/>
)}
{props.borrowDetails.isUpgradingToUsds && (
<UsdsUpgradeAlert borrowDetails={props.borrowDetails} variant="borrow" />
)}
<Panel>
<ActionsContainer
objectives={objectives}
context={actionsContext}
onFinish={pageStatus.goToSuccessScreen}
enabled={pageStatus.actionsEnabled}
actionsGridLayout="extended"
/>
</Panel>
</div>
)}
</Panel>

{pageStatus.state === 'confirmation' && (
<div className="mt-6 flex flex-col gap-6">
<HealthFactorPanel
hf={updatedPositionSummary.healthFactor}
liquidationDetails={liquidationDetails}
variant="full-details"
ref={healthFactorPanelRef}
/>
{props.riskAcknowledgement.warning && (
<RiskAcknowledgement
onStatusChange={props.riskAcknowledgement.onStatusChange}
warning={props.riskAcknowledgement.warning}
{guestMode ? (
<ConnectOrSandboxCTAButtonGroup
buttonText="Connect wallet"
action={openConnectModal}
openSandboxModal={openSandboxModal}
/>
) : (
!disabled && (
<Button type="submit" size="m" variant="primary" disabled={!form.formState.isValid}>
Borrow
</Button>
)
)}
{props.borrowDetails.isUpgradingToUsds && (
<UsdsUpgradeAlert borrowDetails={props.borrowDetails} variant="borrow" />
)}
<ActionsContainer
objectives={objectives}
context={actionsContext}
onFinish={pageStatus.goToSuccessScreen}
enabled={pageStatus.actionsEnabled}
actionsGridLayout="extended"
/>
</div>
)}
</Panel.Wrapper>
</form>
</Form>
</Panel>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import type { Meta, StoryObj } from '@storybook/react'

import { NormalizedUnitNumber } from '@/domain/types/NumericValues'
import { Form } from '@/ui/atoms/form/Form'
import { WithClassname, WithTooltipProvider } from '@sb/decorators'
import { tokens } from '@sb/tokens'
import { useForm } from 'react-hook-form'
import { withRouter } from 'storybook-addon-remix-react-router'
import { Borrow } from './Borrow'

function BorrowWrapper() {
const form = useForm() as any

return (
<Form {...form}>
<Borrow
control={form.control}
selectedAssets={[
{
token: tokens.DAI,
balance: NormalizedUnitNumber(1),
},
]}
changeAsset={() => {}}
allAssets={[
{
token: tokens.DAI,
balance: NormalizedUnitNumber(1),
},
{
token: tokens.USDS,
balance: NormalizedUnitNumber(2),
},
]}
alreadyBorrowed={{
tokens: [tokens.DAI],
totalValueUSD: NormalizedUnitNumber(3000),
}}
/>
</Form>
)
}

const meta: Meta<typeof BorrowWrapper> = {
title: 'Features/EasyBorrow/Components/Form/Borrow',
decorators: [withRouter, WithTooltipProvider(), WithClassname('w-[425px]')],
component: BorrowWrapper,
}

export default meta
type Story = StoryObj<typeof BorrowWrapper>

export const Default: Story = {}
44 changes: 21 additions & 23 deletions packages/app/src/features/easy-borrow/components/form/Borrow.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { TokenWithBalance } from '@/domain/common/types'
import { TokenSymbol } from '@/domain/types/TokenSymbol'
import { Typography } from '@/ui/atoms/typography/Typography'
import { AssetSelector } from '@/ui/molecules/asset-selector/AssetSelector'
import { ControlledMultiSelectorAssetInput } from '@/ui/organisms/multi-selector/MultiSelector'
import { Panel } from '@/ui/atoms/new/panel/Panel'
import { AssetInput } from '@/ui/organisms/new/asset-input/AssetInput'
import { cn } from '@/ui/utils/style'
import { testIds } from '@/ui/utils/testIds'
import { raise } from '@/utils/assert'
import { Control } from 'react-hook-form'
Expand All @@ -16,34 +16,32 @@ interface BorrowProps {
changeAsset: (index: number, newSymbol: TokenSymbol) => void
alreadyBorrowed: ExistingPosition
control: Control<EasyBorrowFormSchema>
disabled: boolean
disabled?: boolean
}

export function Borrow({ selectedAssets, allAssets, changeAsset, alreadyBorrowed, control, disabled }: BorrowProps) {
const { token } = selectedAssets[0] ?? raise('No borrow token selected')
const selectedAsset = selectedAssets[0] ?? raise('No borrow token selected')
const showTokenSummary = alreadyBorrowed.tokens.length > 0

return (
<div data-testid={testIds.easyBorrow.form.borrow} className="flex flex-1 flex-col">
<Typography variant="h4" className="flex h-10 items-center">
Borrow
</Typography>
<Panel className="flex flex-col" data-testid={testIds.easyBorrow.form.borrow} spacing="none">
<Panel className={cn('flex flex-1 flex-col gap-4 bg-primary', showTokenSummary && 'rounded-b-none')}>
<h4 className="typography-label-2 h-8 text-primary">Borrow</h4>

{alreadyBorrowed.tokens.length > 0 && <TokenSummary position={alreadyBorrowed} type="borrow" />}

<div className="mt-2 flex flex-row items-start gap-2">
<AssetSelector
assets={allAssets}
selectedAsset={token}
setSelectedAsset={(newAsset) => changeAsset(0, newAsset)}
disabled={disabled}
/>
<ControlledMultiSelectorAssetInput
fieldName="assetsToBorrow.0.value"
<AssetInput
fieldName={'assetsToBorrow.0.value'}
control={control}
selectorAssets={allAssets.filter((s) => !selectedAssets.some((a) => a.token.symbol === s.token.symbol))}
selectedAsset={selectedAsset}
setSelectedAsset={(newAsset) => changeAsset(0, newAsset)}
disabled={disabled}
token={token}
/>
</div>
</div>
</Panel>
{showTokenSummary && (
<Panel className="rounded-t-none bg-secondary px-8 py-3.5" spacing="none">
<TokenSummary position={alreadyBorrowed} type="borrow" />
</Panel>
)}
</Panel>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import type { Meta, StoryObj } from '@storybook/react'

import { NormalizedUnitNumber } from '@/domain/types/NumericValues'
import { Form } from '@/ui/atoms/form/Form'
import { WithClassname, WithTooltipProvider } from '@sb/decorators'
import { tokens } from '@sb/tokens'
import { useForm } from 'react-hook-form'
import { withRouter } from 'storybook-addon-remix-react-router'
import { Deposits } from './Deposits'

function DepositsWrapper() {
const form = useForm() as any

return (
<Form {...form}>
<Deposits
control={form.control}
selectedAssets={[
{
token: tokens.ETH,
balance: NormalizedUnitNumber(1),
},
{
token: tokens.wstETH,
balance: NormalizedUnitNumber(1.5),
},
]}
changeAsset={() => {}}
addAsset={() => {}}
removeAsset={() => {}}
allAssets={[
{
token: tokens.ETH,
balance: NormalizedUnitNumber(1),
},
{
token: tokens.wstETH,
balance: NormalizedUnitNumber(2),
},
{
token: tokens.rETH,
balance: NormalizedUnitNumber(3),
},
]}
alreadyDeposited={{
tokens: [tokens.ETH, tokens.wstETH],
totalValueUSD: NormalizedUnitNumber(5000),
}}
assetToMaxValue={{
[tokens.ETH.symbol]: NormalizedUnitNumber(1),
[tokens.wstETH.symbol]: NormalizedUnitNumber(2),
[tokens.rETH.symbol]: NormalizedUnitNumber(3),
}}
/>
</Form>
)
}

const meta: Meta<typeof DepositsWrapper> = {
title: 'Features/EasyBorrow/Components/Form/Deposits',
decorators: [withRouter, WithTooltipProvider(), WithClassname('w-[425px]')],
component: DepositsWrapper,
}

export default meta
type Story = StoryObj<typeof DepositsWrapper>

export const Default: Story = {}
Loading

0 comments on commit 63fce42

Please sign in to comment.