Mp 1691 credit account details (#67)
@ -1,220 +0,0 @@
|
||||
import BigNumber from 'bignumber.js'
|
||||
import classNames from 'classnames'
|
||||
import { useState } from 'react'
|
||||
|
||||
import Button from 'components/Button'
|
||||
import FormattedNumber from 'components/FormattedNumber'
|
||||
import ArrowRightLine from 'components/Icons/arrow-right-line.svg'
|
||||
import ChevronDown from 'components/Icons/chevron-down.svg'
|
||||
import ChevronLeft from 'components/Icons/chevron-left.svg'
|
||||
import Text from 'components/Text'
|
||||
import useAccountStats from 'hooks/useAccountStats'
|
||||
import useCreditAccountPositions from 'hooks/useCreditAccountPositions'
|
||||
import useMarkets from 'hooks/useMarkets'
|
||||
import useTokenPrices from 'hooks/useTokenPrices'
|
||||
import { useAccountDetailsStore } from 'stores'
|
||||
import { chain } from 'utils/chains'
|
||||
import { getTokenDecimals, getTokenSymbol } from 'utils/tokens'
|
||||
|
||||
import AccountManageOverlay from './AccountManageOverlay'
|
||||
|
||||
const AccountDetails = () => {
|
||||
const selectedAccount = useAccountDetailsStore((s) => s.selectedAccount)
|
||||
const isOpen = useAccountDetailsStore((s) => s.isOpen)
|
||||
|
||||
const { data: positionsData, isLoading: isLoadingPositions } = useCreditAccountPositions(
|
||||
selectedAccount ?? '',
|
||||
)
|
||||
|
||||
const { data: tokenPrices } = useTokenPrices()
|
||||
const { data: marketsData } = useMarkets()
|
||||
const accountStats = useAccountStats()
|
||||
|
||||
const [showManageMenu, setShowManageMenu] = useState(false)
|
||||
|
||||
const getTokenTotalUSDValue = (amount: string, denom: string) => {
|
||||
// early return if prices are not fetched yet
|
||||
if (!tokenPrices) return 0
|
||||
|
||||
return (
|
||||
BigNumber(amount)
|
||||
.div(10 ** getTokenDecimals(denom))
|
||||
.toNumber() * tokenPrices[denom]
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classNames(
|
||||
'relative flex w-[400px] basis-[400px] flex-wrap content-start border-white/20 bg-header placeholder:border-l',
|
||||
'transition-[margin] duration-1000 ease-in-out',
|
||||
isOpen ? 'mr-0' : '-mr-[400px]',
|
||||
)}
|
||||
>
|
||||
<Button
|
||||
onClick={() => {
|
||||
useAccountDetailsStore.setState({ isOpen: true })
|
||||
}}
|
||||
variant='text'
|
||||
className={classNames(
|
||||
'absolute top-1/2 -left-[20px] w-[21px] -translate-y-1/2 bg-header p-0',
|
||||
'rounded-none rounded-tl-sm rounded-bl-sm',
|
||||
'border border-white/20',
|
||||
'transition-[opacity] delay-1000 duration-500 ease-in-out',
|
||||
isOpen ? 'pointer-events-none opacity-0' : 'opacity-100',
|
||||
)}
|
||||
>
|
||||
<span className='flex h-20 px-1 py-6 text-white/40 transition-[color] hover:text-white'>
|
||||
<ChevronLeft />
|
||||
</span>
|
||||
</Button>
|
||||
<div className='relative flex w-full flex-wrap items-center border-b border-white/20'>
|
||||
<Button
|
||||
variant='text'
|
||||
className='flex flex-grow flex-nowrap items-center justify-center p-4 text-center text-white text-xl-caps'
|
||||
onClick={() => setShowManageMenu(!showManageMenu)}
|
||||
>
|
||||
Account {selectedAccount}
|
||||
<span className='ml-2 flex w-4'>
|
||||
<ChevronDown />
|
||||
</span>
|
||||
</Button>
|
||||
<div className='flex border-l border-white/20' onClick={() => {}}>
|
||||
<Button
|
||||
variant='text'
|
||||
className='w-14 p-4 text-white/40 transition-[color] hover:cursor-pointer hover:text-white'
|
||||
onClick={() => {
|
||||
useAccountDetailsStore.setState({ isOpen: false })
|
||||
}}
|
||||
>
|
||||
<ArrowRightLine />
|
||||
</Button>
|
||||
</div>
|
||||
<AccountManageOverlay
|
||||
className='top-[60px] left-[36px]'
|
||||
show={showManageMenu}
|
||||
setShow={setShowManageMenu}
|
||||
/>
|
||||
</div>
|
||||
<div className='flex w-full flex-wrap p-2'>
|
||||
<div className='mb-2 flex w-full'>
|
||||
<Text size='xs' className='flex-grow text-white/60'>
|
||||
Total Position:
|
||||
</Text>
|
||||
|
||||
<Text size='xs' className='text-white/60'>
|
||||
<FormattedNumber
|
||||
amount={BigNumber(accountStats?.totalPosition ?? 0)
|
||||
.dividedBy(10 ** chain.stakeCurrency.coinDecimals)
|
||||
.toNumber()}
|
||||
animate
|
||||
prefix='$'
|
||||
/>
|
||||
</Text>
|
||||
</div>
|
||||
<div className='flex w-full justify-between'>
|
||||
<Text size='xs' className='flex-grow text-white/60'>
|
||||
Total Liabilities:
|
||||
</Text>
|
||||
<Text size='xs' className=' text-white/60'>
|
||||
<FormattedNumber
|
||||
amount={BigNumber(accountStats?.totalDebt ?? 0)
|
||||
.dividedBy(10 ** chain.stakeCurrency.coinDecimals)
|
||||
.toNumber()}
|
||||
animate
|
||||
prefix='$'
|
||||
/>
|
||||
</Text>
|
||||
</div>
|
||||
</div>
|
||||
<div className='flex w-full flex-wrap'>
|
||||
<Text uppercase className='w-full bg-black/20 px-4 py-2 text-white/40'>
|
||||
Balances
|
||||
</Text>
|
||||
{isLoadingPositions ? (
|
||||
<div>Loading...</div>
|
||||
) : (
|
||||
<div className='flex w-full flex-wrap'>
|
||||
<div className='mb-2 flex w-full border-b border-white/20 bg-black/20 px-4 py-2'>
|
||||
<Text size='xs' uppercase className='flex-1 text-white'>
|
||||
Asset
|
||||
</Text>
|
||||
<Text size='xs' uppercase className='flex-1 text-white'>
|
||||
Value
|
||||
</Text>
|
||||
<Text size='xs' uppercase className='flex-1 text-white'>
|
||||
Size
|
||||
</Text>
|
||||
<Text size='xs' uppercase className='flex-1 text-white'>
|
||||
APY
|
||||
</Text>
|
||||
</div>
|
||||
{positionsData?.coins.map((coin) => (
|
||||
<div key={coin.denom} className='flex w-full px-4 py-2'>
|
||||
<Text size='xs' className='flex-1 border-l-4 border-profit pl-2 text-white/60'>
|
||||
{getTokenSymbol(coin.denom)}
|
||||
</Text>
|
||||
<Text size='xs' className='flex-1 text-white/60'>
|
||||
<FormattedNumber
|
||||
amount={getTokenTotalUSDValue(coin.amount, coin.denom)}
|
||||
animate
|
||||
prefix='$'
|
||||
/>
|
||||
</Text>
|
||||
<Text size='xs' className='flex-1 text-white/60'>
|
||||
<FormattedNumber
|
||||
amount={BigNumber(coin.amount)
|
||||
.div(10 ** getTokenDecimals(coin.denom))
|
||||
.toNumber()}
|
||||
animate
|
||||
minDecimals={0}
|
||||
maxDecimals={4}
|
||||
/>
|
||||
</Text>
|
||||
<Text size='xs' className='flex-1 text-white/60'>
|
||||
-
|
||||
</Text>
|
||||
</div>
|
||||
))}
|
||||
{positionsData?.debts.map((coin) => (
|
||||
<div key={coin.denom} className='flex w-full px-4 py-2'>
|
||||
<Text size='xs' className='flex-1 border-l-4 border-loss pl-2 text-white/60'>
|
||||
{getTokenSymbol(coin.denom)}
|
||||
</Text>
|
||||
<Text size='xs' className='flex-1 text-white/60'>
|
||||
<FormattedNumber
|
||||
amount={getTokenTotalUSDValue(coin.amount, coin.denom)}
|
||||
prefix='-$'
|
||||
animate
|
||||
/>
|
||||
</Text>
|
||||
<Text size='xs' className='flex-1 text-white/60'>
|
||||
<FormattedNumber
|
||||
amount={BigNumber(coin.amount)
|
||||
.div(10 ** getTokenDecimals(coin.denom))
|
||||
.toNumber()}
|
||||
minDecimals={0}
|
||||
maxDecimals={4}
|
||||
animate
|
||||
/>
|
||||
</Text>
|
||||
<Text size='xs' className='flex-1 text-white/60'>
|
||||
<FormattedNumber
|
||||
amount={Number(marketsData?.[coin.denom].borrow_rate) * 100}
|
||||
minDecimals={0}
|
||||
maxDecimals={2}
|
||||
prefix='-'
|
||||
suffix='%'
|
||||
animate
|
||||
/>
|
||||
</Text>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default AccountDetails
|
@ -1,24 +0,0 @@
|
||||
import CircularProgress from 'components/CircularProgress'
|
||||
import Modal from 'components/Modal'
|
||||
import Text from 'components/Text'
|
||||
import { useModalStore } from 'stores'
|
||||
|
||||
const ConfirmModal = () => {
|
||||
const createOpen = useModalStore((s) => s.createAccountModal)
|
||||
const deleteOpen = useModalStore((s) => s.deleteAccountModal)
|
||||
|
||||
return (
|
||||
<Modal open={createOpen || deleteOpen}>
|
||||
<div className='w-full p-6'>
|
||||
<Text size='2xl' uppercase={true} className='mb-6 w-full text-center'>
|
||||
Confirm Transaction
|
||||
</Text>
|
||||
<div className='flex w-full justify-center pb-6'>
|
||||
<CircularProgress size={40} />
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
|
||||
export default ConfirmModal
|
@ -1,7 +0,0 @@
|
||||
export { default as AccountDetails } from './AccountDetails'
|
||||
export { default as AccountManageOverlay } from './AccountManageOverlay'
|
||||
export { default as AccountNavigation } from './AccountNavigation'
|
||||
export { default as AccountStatus } from './AccountStatus'
|
||||
export { default as ConfirmModal } from './ConfirmModal'
|
||||
export { default as FundAccountModal } from './FundAccountModal'
|
||||
export { default as WithdrawModal } from './WithdrawModal'
|
@ -1,2 +0,0 @@
|
||||
export { default as AssetRow } from './AssetRow'
|
||||
export { default as BorrowTable } from './BorrowTable'
|
@ -1,75 +0,0 @@
|
||||
import classNames from 'classnames'
|
||||
import React, { useEffect, useRef } from 'react'
|
||||
import { animated, useSpring } from 'react-spring'
|
||||
|
||||
import { formatValue } from 'utils/formatters'
|
||||
|
||||
interface Props {
|
||||
amount: number
|
||||
animate?: boolean
|
||||
className?: string
|
||||
minDecimals?: number
|
||||
maxDecimals?: number
|
||||
thousandSeparator?: boolean
|
||||
prefix?: boolean | string
|
||||
suffix?: boolean | string
|
||||
rounded?: boolean
|
||||
abbreviated?: boolean
|
||||
}
|
||||
|
||||
const FormattedNumber = ({
|
||||
amount,
|
||||
animate = false,
|
||||
className,
|
||||
minDecimals = 2,
|
||||
maxDecimals = 2,
|
||||
thousandSeparator = true,
|
||||
prefix = false,
|
||||
suffix = false,
|
||||
rounded = false,
|
||||
abbreviated = false,
|
||||
}: Props) => {
|
||||
const prevAmountRef = useRef<number>(0)
|
||||
|
||||
useEffect(() => {
|
||||
if (prevAmountRef.current !== amount) prevAmountRef.current = amount
|
||||
}, [amount])
|
||||
|
||||
const springAmount = useSpring({
|
||||
number: amount,
|
||||
from: { number: prevAmountRef.current },
|
||||
config: { duration: 1000 },
|
||||
})
|
||||
|
||||
return (prevAmountRef.current === amount && amount === 0) || !animate ? (
|
||||
<span className={classNames('number', className)}>
|
||||
{formatValue(
|
||||
amount,
|
||||
minDecimals,
|
||||
maxDecimals,
|
||||
thousandSeparator,
|
||||
prefix,
|
||||
suffix,
|
||||
rounded,
|
||||
abbreviated,
|
||||
)}
|
||||
</span>
|
||||
) : (
|
||||
<animated.span className={classNames('number', className)}>
|
||||
{springAmount.number.to((num) =>
|
||||
formatValue(
|
||||
num,
|
||||
minDecimals,
|
||||
maxDecimals,
|
||||
thousandSeparator,
|
||||
prefix,
|
||||
suffix,
|
||||
rounded,
|
||||
abbreviated,
|
||||
),
|
||||
)}
|
||||
</animated.span>
|
||||
)
|
||||
}
|
||||
|
||||
export default React.memo(FormattedNumber)
|
@ -1,3 +0,0 @@
|
||||
export { default as DesktopNavigation } from './DesktopNavigation'
|
||||
export { default as menuTree } from './menuTree'
|
||||
export { default as NavLink } from './NavLink'
|
@ -1,2 +0,0 @@
|
||||
export { default as Overlay } from './Overlay'
|
||||
export { default as OverlayLink } from './OverlayLink'
|
@ -1,3 +0,0 @@
|
||||
export { default as ConnectButton } from './ConnectButton'
|
||||
export { default as ConnectedButton } from './ConnectedButton'
|
||||
export { default as Wallet } from './Wallet'
|
@ -1,16 +1,9 @@
|
||||
const { withSentryConfig } = require('@sentry/nextjs')
|
||||
|
||||
// This file sets a custom webpack configuration to use your Next.js app
|
||||
// with Sentry.
|
||||
// https://nextjs.org/docs/api-reference/next.config.js/introduction
|
||||
// https://docs.sentry.io/platforms/javascript/guides/nextjs/
|
||||
|
||||
/** @type {import('next').NextConfig} */
|
||||
|
||||
const nextConfig = {
|
||||
reactStrictMode: true,
|
||||
// swcMinify: true,
|
||||
sentry: {
|
||||
// https://docs.sentry.io/platforms/javascript/guides/nextjs/manual-setup/#use-hidden-source-map
|
||||
hideSourceMaps: true,
|
||||
},
|
||||
async redirects() {
|
||||
|
15
package.json
@ -5,9 +5,11 @@
|
||||
"scripts": {
|
||||
"build": "next build",
|
||||
"dev": "next dev",
|
||||
"format": "eslint . --ext=ts,tsx --fix && prettier --write ./**/*.ts ./**/*.tsx",
|
||||
"lint": "next lint",
|
||||
"start": "next start"
|
||||
"lint": "eslint ./src/ && yarn prettier-check",
|
||||
"format": "yarn index && eslint ./src/ --fix && prettier --write ./src/",
|
||||
"prettier-check": "prettier --ignore-path .gitignore --check ./src/",
|
||||
"start": "next start",
|
||||
"index": "vscode-generate-index-standalone src/"
|
||||
},
|
||||
"dependencies": {
|
||||
"@cosmjs/cosmwasm-stargate": "^0.29.4",
|
||||
@ -27,6 +29,7 @@
|
||||
"ethereumjs-util": "^7.1.5",
|
||||
"graphql": "^16.6.0",
|
||||
"graphql-request": "^5.0.0",
|
||||
"moment": "^2.29.4",
|
||||
"next": "12.3.1",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0",
|
||||
@ -34,13 +37,14 @@
|
||||
"react-spring": "^9.5.5",
|
||||
"react-toastify": "^9.0.8",
|
||||
"react-use-clipboard": "^1.0.9",
|
||||
"recharts": "^2.2.0",
|
||||
"tailwindcss-border-gradient-radius": "^3.0.1",
|
||||
"use-local-storage-state": "^18.1.1",
|
||||
"zustand": "^4.1.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@svgr/webpack": "^6.4.0",
|
||||
"@types/node": "18.11.11",
|
||||
"@types/node": "^18.11.13",
|
||||
"@types/react": "18.0.26",
|
||||
"@types/react-dom": "18.0.9",
|
||||
"autoprefixer": "^10.4.13",
|
||||
@ -51,6 +55,7 @@
|
||||
"prettier": "^2.7.1",
|
||||
"prettier-plugin-tailwindcss": "^0.1.13",
|
||||
"tailwindcss": "^3.2.1",
|
||||
"typescript": "4.8.2"
|
||||
"typescript": "4.8.2",
|
||||
"vscode-generate-index-standalone": "^1.6.0"
|
||||
}
|
||||
}
|
||||
|
BIN
public/images/create-account-bg.png
Normal file
After Width: | Height: | Size: 150 KiB |
BIN
public/images/create-account-bg.webp
Normal file
After Width: | Height: | Size: 3.4 KiB |
BIN
public/images/delete-account-bg.png
Normal file
After Width: | Height: | Size: 164 KiB |
BIN
public/images/delete-account-bg.webp
Normal file
After Width: | Height: | Size: 6.4 KiB |
BIN
public/images/fund-bg.png
Normal file
After Width: | Height: | Size: 76 KiB |
BIN
public/images/fund-bg.webp
Normal file
After Width: | Height: | Size: 4.5 KiB |
Before Width: | Height: | Size: 210 KiB |
111
src/components/Account/AccountDetails.tsx
Normal file
@ -0,0 +1,111 @@
|
||||
import classNames from 'classnames'
|
||||
import { useEffect, useState } from 'react'
|
||||
|
||||
import { Button, LabelValuePair, PositionsList } from 'components'
|
||||
import { AccountManageOverlay, RiskChart } from 'components/Account'
|
||||
import { ArrowRightLine, ChevronDown, ChevronLeft } from 'components/Icons'
|
||||
import { useAccountStats, useBalances } from 'hooks/data'
|
||||
import { useAccountDetailsStore, useSettings } from 'stores'
|
||||
import { chain } from 'utils/chains'
|
||||
import { lookup } from 'utils/formatters'
|
||||
import { createRiskData } from 'utils/risk'
|
||||
|
||||
export const AccountDetails = () => {
|
||||
const enableAnimations = useSettings((s) => s.enableAnimations)
|
||||
const selectedAccount = useAccountDetailsStore((s) => s.selectedAccount)
|
||||
const isOpen = useAccountDetailsStore((s) => s.isOpen)
|
||||
const balances = useBalances()
|
||||
const accountStats = useAccountStats()
|
||||
|
||||
const [showManageMenu, setShowManageMenu] = useState(false)
|
||||
const [riskData, setRiskData] = useState<RiskTimePair[]>()
|
||||
|
||||
useEffect(() => {
|
||||
setRiskData(createRiskData(accountStats?.risk ?? 0))
|
||||
}, [accountStats?.risk, selectedAccount])
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classNames(
|
||||
'relative flex w-[400px] basis-[400px] flex-wrap content-start border-white/20 bg-header',
|
||||
enableAnimations && 'transition-[margin] duration-1000 ease-in-out',
|
||||
isOpen ? 'mr-0' : '-mr-[400px]',
|
||||
)}
|
||||
>
|
||||
<Button
|
||||
onClick={() => {
|
||||
useAccountDetailsStore.setState({ isOpen: true })
|
||||
}}
|
||||
variant='text'
|
||||
className={classNames(
|
||||
'absolute top-1/2 -left-[20px] w-[21px] -translate-y-1/2 bg-header p-0',
|
||||
'rounded-none rounded-tl-sm rounded-bl-sm',
|
||||
'border border-white/20',
|
||||
enableAnimations && 'transition-[opacity] delay-1000 duration-500 ease-in-out',
|
||||
isOpen ? 'pointer-events-none opacity-0' : 'opacity-100',
|
||||
)}
|
||||
>
|
||||
<span
|
||||
className={classNames(
|
||||
'flex h-20 px-1 py-6 text-white/40 hover:text-white',
|
||||
enableAnimations && 'transition-[color]',
|
||||
)}
|
||||
>
|
||||
<ChevronLeft />
|
||||
</span>
|
||||
</Button>
|
||||
<div className='relative flex w-full flex-wrap items-center border-b border-white/20'>
|
||||
<Button
|
||||
variant='text'
|
||||
className='flex flex-grow flex-nowrap items-center justify-center p-4 text-center text-white text-xl-caps'
|
||||
onClick={() => setShowManageMenu(!showManageMenu)}
|
||||
>
|
||||
Account {selectedAccount}
|
||||
<span className='ml-2 flex w-4'>
|
||||
<ChevronDown />
|
||||
</span>
|
||||
</Button>
|
||||
<div className='flex border-l border-white/20' onClick={() => {}}>
|
||||
<Button
|
||||
variant='text'
|
||||
className={classNames(
|
||||
'w-14 p-4 text-white/40 hover:cursor-pointer hover:text-white',
|
||||
enableAnimations && 'transition-[color]',
|
||||
)}
|
||||
onClick={() => {
|
||||
useAccountDetailsStore.setState({ isOpen: false })
|
||||
}}
|
||||
>
|
||||
<ArrowRightLine />
|
||||
</Button>
|
||||
</div>
|
||||
<AccountManageOverlay
|
||||
className='top-[60px] left-[36px]'
|
||||
show={showManageMenu}
|
||||
setShow={setShowManageMenu}
|
||||
/>
|
||||
</div>
|
||||
<div className='flex w-full flex-wrap p-3'>
|
||||
<LabelValuePair
|
||||
className='mb-2'
|
||||
label='Total Position:'
|
||||
value={{
|
||||
format: 'number',
|
||||
amount: lookup(accountStats?.totalPosition ?? 0, chain.stakeCurrency.coinDenom),
|
||||
prefix: '$',
|
||||
}}
|
||||
/>
|
||||
<LabelValuePair
|
||||
label='Total Liabilities:'
|
||||
value={{
|
||||
format: 'number',
|
||||
amount: lookup(accountStats?.totalDebt ?? 0, chain.stakeCurrency.coinDenom),
|
||||
prefix: '$',
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
{riskData && <RiskChart data={riskData} />}
|
||||
<PositionsList title='Balances' data={balances} />
|
||||
</div>
|
||||
)
|
||||
}
|
@ -1,16 +1,9 @@
|
||||
import { useEffect } from 'react'
|
||||
|
||||
import Button from 'components/Button'
|
||||
import PlusIcon from 'components/Icons/add.svg'
|
||||
import ArrowDown from 'components/Icons/arrow-down.svg'
|
||||
import ArrowUp from 'components/Icons/arrow-up.svg'
|
||||
import ArrowsLeftRight from 'components/Icons/arrows-left-right.svg'
|
||||
import DeleteIcon from 'components/Icons/rubbish.svg'
|
||||
import Overlay from 'components/Overlay/Overlay'
|
||||
import OverlayAction from 'components/Overlay/OverlayLink'
|
||||
import Text from 'components/Text'
|
||||
import useCreateCreditAccount from 'hooks/mutations/useCreateCreditAccount'
|
||||
import useDeleteCreditAccount from 'hooks/mutations/useDeleteCreditAccount'
|
||||
import { Button, Text } from 'components'
|
||||
import { Add, ArrowDown, ArrowsLeftRight, ArrowUp, Rubbish } from 'components/Icons'
|
||||
import { Overlay, OverlayAction } from 'components/Overlay'
|
||||
import { useCreateCreditAccount, useDeleteCreditAccount } from 'hooks/mutations'
|
||||
import { useAccountDetailsStore, useModalStore } from 'stores'
|
||||
|
||||
interface Props {
|
||||
@ -19,7 +12,7 @@ interface Props {
|
||||
show: boolean
|
||||
}
|
||||
|
||||
const AccountManageOverlay = ({ className, setShow, show }: Props) => {
|
||||
export const AccountManageOverlay = ({ className, setShow, show }: Props) => {
|
||||
const selectedAccount = useAccountDetailsStore((s) => s.selectedAccount)
|
||||
|
||||
const { mutate: createCreditAccount, isLoading: isLoadingCreate } = useCreateCreditAccount()
|
||||
@ -73,13 +66,13 @@ const AccountManageOverlay = ({ className, setShow, show }: Props) => {
|
||||
setShow={setShow}
|
||||
text='Create New Account'
|
||||
onClick={createCreditAccount}
|
||||
icon={<PlusIcon />}
|
||||
icon={<Add />}
|
||||
/>
|
||||
<OverlayAction
|
||||
setShow={setShow}
|
||||
text='Close Account'
|
||||
onClick={deleteCreditAccount}
|
||||
icon={<DeleteIcon />}
|
||||
icon={<Rubbish />}
|
||||
/>
|
||||
<OverlayAction
|
||||
setShow={setShow}
|
||||
@ -92,5 +85,3 @@ const AccountManageOverlay = ({ className, setShow, show }: Props) => {
|
||||
</Overlay>
|
||||
)
|
||||
}
|
||||
|
||||
export default AccountManageOverlay
|
@ -1,12 +1,11 @@
|
||||
import classNames from 'classnames'
|
||||
import { useMemo, useState } from 'react'
|
||||
|
||||
import Button from 'components/Button'
|
||||
import ChevronDownIcon from 'components/Icons/chevron-down.svg'
|
||||
import Overlay from 'components/Overlay/Overlay'
|
||||
import { Button } from 'components'
|
||||
import { ChevronDown } from 'components/Icons'
|
||||
import { Overlay } from 'components/Overlay'
|
||||
import { useAccountDetailsStore } from 'stores'
|
||||
|
||||
import AccountManageOverlay from './AccountManageOverlay'
|
||||
import { AccountManageOverlay } from 'components/Account'
|
||||
|
||||
interface Props {
|
||||
creditAccountsList: string[]
|
||||
@ -15,7 +14,7 @@ interface Props {
|
||||
|
||||
const MAX_VISIBLE_CREDIT_ACCOUNTS = 5
|
||||
|
||||
const AccountNavigation = ({ creditAccountsList, selectedAccount }: Props) => {
|
||||
export const AccountNavigation = ({ creditAccountsList, selectedAccount }: Props) => {
|
||||
const { firstCreditAccounts, restCreditAccounts } = useMemo(() => {
|
||||
return {
|
||||
firstCreditAccounts: creditAccountsList?.slice(0, MAX_VISIBLE_CREDIT_ACCOUNTS) ?? [],
|
||||
@ -53,7 +52,7 @@ const AccountNavigation = ({ creditAccountsList, selectedAccount }: Props) => {
|
||||
>
|
||||
More
|
||||
<span className='ml-1 inline-block w-3'>
|
||||
<ChevronDownIcon />
|
||||
<ChevronDown />
|
||||
</span>
|
||||
</Button>
|
||||
<Overlay show={showMoreMenu} setShow={setShowMoreMenu}>
|
||||
@ -92,7 +91,7 @@ const AccountNavigation = ({ creditAccountsList, selectedAccount }: Props) => {
|
||||
>
|
||||
Manage
|
||||
<span className='ml-1 inline-block w-3'>
|
||||
<ChevronDownIcon />
|
||||
<ChevronDown />
|
||||
</span>
|
||||
</Button>
|
||||
|
||||
@ -105,5 +104,3 @@ const AccountNavigation = ({ creditAccountsList, selectedAccount }: Props) => {
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default AccountNavigation
|
@ -1,19 +1,16 @@
|
||||
import BigNumber from 'bignumber.js'
|
||||
import { useEffect } from 'react'
|
||||
|
||||
import { Button, FormattedNumber, Gauge, Text } from 'components'
|
||||
import { BorrowCapacity } from 'components/BorrowCapacity'
|
||||
import Button from 'components/Button'
|
||||
import FormattedNumber from 'components/FormattedNumber'
|
||||
import Gauge from 'components/Gauge'
|
||||
import Text from 'components/Text'
|
||||
import useCreateCreditAccount from 'hooks/mutations/useCreateCreditAccount'
|
||||
import useAccountStats from 'hooks/useAccountStats'
|
||||
import useCreditAccounts from 'hooks/useCreditAccounts'
|
||||
import { useAccountStats } from 'hooks/data'
|
||||
import { useCreateCreditAccount } from 'hooks/mutations'
|
||||
import { useCreditAccounts } from 'hooks/queries'
|
||||
import { useModalStore } from 'stores'
|
||||
import { chain } from 'utils/chains'
|
||||
import { formatValue } from 'utils/formatters'
|
||||
|
||||
const AccountStatus = () => {
|
||||
export const AccountStatus = () => {
|
||||
const accountStats = useAccountStats()
|
||||
const { data: creditAccountsList } = useCreditAccounts()
|
||||
const { mutate: createCreditAccount, isLoading: isLoadingCreate } = useCreateCreditAccount()
|
||||
@ -81,4 +78,3 @@ const AccountStatus = () => {
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default AccountStatus
|
36
src/components/Account/ConfirmModal.tsx
Normal file
@ -0,0 +1,36 @@
|
||||
import classNames from 'classnames'
|
||||
|
||||
import { CircularProgress, Modal, Text } from 'components'
|
||||
import { MarsProtocol } from 'components/Icons'
|
||||
import { useModalStore } from 'stores'
|
||||
|
||||
export const ConfirmModal = () => {
|
||||
const createOpen = useModalStore((s) => s.createAccountModal)
|
||||
const deleteOpen = useModalStore((s) => s.deleteAccountModal)
|
||||
|
||||
return (
|
||||
<Modal open={createOpen || deleteOpen}>
|
||||
<div
|
||||
className={classNames(
|
||||
'relative flex h-[630px] w-full flex-wrap items-center justify-center p-6',
|
||||
createOpen && 'bg-create-modal',
|
||||
deleteOpen && 'bg-delete-modal',
|
||||
)}
|
||||
>
|
||||
<div className='w-full flex-wrap'>
|
||||
<div className='flex w-full justify-center pb-6'>
|
||||
<CircularProgress size={40} />
|
||||
</div>
|
||||
<Text size='2xl' uppercase={true} className='w-full text-center'>
|
||||
{createOpen &&
|
||||
'A small step for a Smart Contracts but a big leap for your financial freedom.'}
|
||||
{deleteOpen && 'Some rovers have to be recycled once in a while...'}
|
||||
</Text>
|
||||
</div>
|
||||
<div className='absolute bottom-8 left-8 flex w-[150px]'>
|
||||
<MarsProtocol />
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
)
|
||||
}
|
@ -4,19 +4,14 @@ import { useEffect, useMemo, useState } from 'react'
|
||||
import { toast } from 'react-toastify'
|
||||
import useLocalStorageState from 'use-local-storage-state'
|
||||
|
||||
import Button from 'components/Button'
|
||||
import CircularProgress from 'components/CircularProgress'
|
||||
import MarsProtocolLogo from 'components/Icons/mars-protocol.svg'
|
||||
import Modal from 'components/Modal'
|
||||
import Slider from 'components/Slider'
|
||||
import Text from 'components/Text'
|
||||
import useDepositCreditAccount from 'hooks/mutations/useDepositCreditAccount'
|
||||
import useAllBalances from 'hooks/useAllBalances'
|
||||
import useAllowedCoins from 'hooks/useAllowedCoins'
|
||||
import { Button, CircularProgress, Modal, Slider, Text } from 'components'
|
||||
import { MarsProtocol } from 'components/Icons'
|
||||
import { useDepositCreditAccount } from 'hooks/mutations'
|
||||
import { useAllBalances, useAllowedCoins } from 'hooks/queries'
|
||||
import { useAccountDetailsStore, useModalStore } from 'stores'
|
||||
import { getTokenDecimals, getTokenSymbol } from 'utils/tokens'
|
||||
|
||||
const FundAccountModal = () => {
|
||||
export const FundAccountModal = () => {
|
||||
// ---------------
|
||||
// STORE
|
||||
// ---------------
|
||||
@ -105,7 +100,7 @@ const FundAccountModal = () => {
|
||||
</Text>
|
||||
</div>
|
||||
<div className='w-[153px] text-white'>
|
||||
<MarsProtocolLogo />
|
||||
<MarsProtocol />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -202,7 +197,7 @@ const FundAccountModal = () => {
|
||||
<span
|
||||
className={`${
|
||||
lendAssets ? 'translate-x-6' : 'translate-x-1'
|
||||
} inline-block h-4 w-4 transform rounded-full bg-white transition`}
|
||||
} inline-block h-4 w-4 transform rounded-full bg-white`}
|
||||
/>
|
||||
</Switch>
|
||||
</div>
|
||||
@ -218,5 +213,3 @@ const FundAccountModal = () => {
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
|
||||
export default FundAccountModal
|
97
src/components/Account/RiskChart.tsx
Normal file
@ -0,0 +1,97 @@
|
||||
import moment from 'moment'
|
||||
import {
|
||||
Area,
|
||||
AreaChart,
|
||||
CartesianGrid,
|
||||
ResponsiveContainer,
|
||||
Tooltip,
|
||||
XAxis,
|
||||
YAxis,
|
||||
} from 'recharts'
|
||||
|
||||
import { FormattedNumber, Text } from 'components'
|
||||
import { useAccountStats } from 'hooks/data'
|
||||
import { useSettings } from 'stores'
|
||||
import { formatValue } from 'utils/formatters'
|
||||
|
||||
export const RiskChart = ({ data }: RiskChartProps) => {
|
||||
const enableAnimations = useSettings((s) => s.enableAnimations)
|
||||
const accountStats = useAccountStats()
|
||||
const currentRisk = accountStats?.risk ?? 0
|
||||
|
||||
return (
|
||||
<div className='flex w-full flex-wrap overflow-hidden py-2'>
|
||||
<FormattedNumber
|
||||
className='px-3 pb-2 text-lg'
|
||||
amount={currentRisk * 100}
|
||||
maxDecimals={0}
|
||||
minDecimals={0}
|
||||
animate
|
||||
prefix='Risk Score: '
|
||||
suffix='/100'
|
||||
/>
|
||||
<div className='-ml-6 h-[100px] w-[412px]'>
|
||||
<ResponsiveContainer width='100%' height='100%'>
|
||||
<AreaChart
|
||||
data={data}
|
||||
margin={{
|
||||
top: 0,
|
||||
right: 0,
|
||||
left: 0,
|
||||
bottom: 0,
|
||||
}}
|
||||
>
|
||||
<defs>
|
||||
<linearGradient id='chartGradient' x1='0' y1='0' x2='0' y2='1'>
|
||||
<stop offset='0%' stopColor={'#FFFFFF'} stopOpacity={0.2} />
|
||||
<stop offset='100%' stopColor={'#FFFFFF'} stopOpacity={0.02} />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<CartesianGrid
|
||||
strokeDasharray='0'
|
||||
horizontalPoints={[0, 20, 40, 60, 80, 100]}
|
||||
vertical={false}
|
||||
stroke='rgba(255,255,255,0.1)'
|
||||
/>
|
||||
<XAxis
|
||||
stroke='rgba(255, 255, 255, 0.6)'
|
||||
tickLine={false}
|
||||
tickFormatter={(value) => {
|
||||
return moment(value).format('DD')
|
||||
}}
|
||||
fontSize={10.53}
|
||||
dataKey='date'
|
||||
/>
|
||||
<YAxis
|
||||
ticks={[0, 20, 40, 60, 80, 100]}
|
||||
tickLine={false}
|
||||
fontSize={10.53}
|
||||
stroke='rgba(255, 255, 255, 0.6)'
|
||||
/>
|
||||
<Tooltip
|
||||
wrapperStyle={{ outline: 'none' }}
|
||||
content={({ payload, label }) => {
|
||||
if (payload && payload.length) {
|
||||
const risk = Number(payload[0].value) ?? 0
|
||||
return (
|
||||
<div className='max-w-[320px] rounded-lg px-4 py-2 shadow-tooltip gradient-tooltip '>
|
||||
<Text size='sm'>{moment(label).format('MM-DD-YYYY')}</Text>
|
||||
<Text size='sm'>Risk: {formatValue(risk, 0, 0, true, false, '%')}</Text>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<Area
|
||||
type='monotone'
|
||||
dataKey='risk'
|
||||
stroke='#FFFFFF'
|
||||
fill='url(#chartGradient)'
|
||||
isAnimationActive={enableAnimations}
|
||||
/>
|
||||
</AreaChart>
|
||||
</ResponsiveContainer>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
@ -4,27 +4,27 @@ import classNames from 'classnames'
|
||||
import React, { useEffect, useMemo, useState } from 'react'
|
||||
import { toast } from 'react-toastify'
|
||||
|
||||
import {
|
||||
Button,
|
||||
CircularProgress,
|
||||
FormattedNumber,
|
||||
Gauge,
|
||||
LabelValuePair,
|
||||
Modal,
|
||||
PositionsList,
|
||||
Slider,
|
||||
Text,
|
||||
} from 'components'
|
||||
import { BorrowCapacity } from 'components/BorrowCapacity'
|
||||
import Button from 'components/Button'
|
||||
import CircularProgress from 'components/CircularProgress'
|
||||
import FormattedNumber from 'components/FormattedNumber'
|
||||
import Gauge from 'components/Gauge'
|
||||
import Modal from 'components/Modal'
|
||||
import Slider from 'components/Slider'
|
||||
import Text from 'components/Text'
|
||||
import useWithdrawFunds from 'hooks/mutations/useWithdrawFunds'
|
||||
import useAccountStats, { AccountStatsAction } from 'hooks/useAccountStats'
|
||||
import useAllBalances from 'hooks/useAllBalances'
|
||||
import useCalculateMaxWithdrawAmount from 'hooks/useCalculateMaxWithdrawAmount'
|
||||
import useCreditAccountPositions from 'hooks/useCreditAccountPositions'
|
||||
import useMarkets from 'hooks/useMarkets'
|
||||
import useTokenPrices from 'hooks/useTokenPrices'
|
||||
import { useAccountStats, useBalances, useCalculateMaxWithdrawAmount } from 'hooks/data'
|
||||
import { useWithdrawFunds } from 'hooks/mutations'
|
||||
import { useCreditAccountPositions, useTokenPrices } from 'hooks/queries'
|
||||
import { useAccountDetailsStore, useModalStore } from 'stores'
|
||||
import { chain } from 'utils/chains'
|
||||
import { formatValue } from 'utils/formatters'
|
||||
import { formatValue, lookup } from 'utils/formatters'
|
||||
import { getTokenDecimals, getTokenSymbol } from 'utils/tokens'
|
||||
|
||||
const WithdrawModal = () => {
|
||||
export const WithdrawModal = () => {
|
||||
// ---------------
|
||||
// STORE
|
||||
// ---------------
|
||||
@ -45,9 +45,8 @@ const WithdrawModal = () => {
|
||||
// ---------------
|
||||
// EXTERNAL HOOKS
|
||||
// ---------------
|
||||
const { data: balancesData } = useAllBalances()
|
||||
const { data: tokenPrices } = useTokenPrices()
|
||||
const { data: marketsData } = useMarkets()
|
||||
const balances = useBalances()
|
||||
|
||||
const selectedTokenSymbol = getTokenSymbol(selectedToken)
|
||||
const selectedTokenDecimals = getTokenDecimals(selectedToken)
|
||||
@ -100,14 +99,6 @@ const WithdrawModal = () => {
|
||||
|
||||
const maxWithdrawAmount = useCalculateMaxWithdrawAmount(selectedToken, isBorrowEnabled)
|
||||
|
||||
const walletAmount = useMemo(() => {
|
||||
if (!selectedToken) return 0
|
||||
|
||||
return BigNumber(balancesData?.find((balance) => balance.denom === selectedToken)?.amount ?? 0)
|
||||
.div(10 ** selectedTokenDecimals)
|
||||
.toNumber()
|
||||
}, [balancesData, selectedToken, selectedTokenDecimals])
|
||||
|
||||
useEffect(() => {
|
||||
if (positionsData && positionsData.coins.length > 0) {
|
||||
// initialize selected token when allowedCoins fetch data is available
|
||||
@ -248,7 +239,7 @@ const WithdrawModal = () => {
|
||||
<span
|
||||
className={`${
|
||||
isBorrowEnabled ? 'translate-x-6' : 'translate-x-1'
|
||||
} inline-block h-4 w-4 transform rounded-full bg-white transition`}
|
||||
} inline-block h-4 w-4 transform rounded-full bg-white`}
|
||||
/>
|
||||
</Switch>
|
||||
</div>
|
||||
@ -310,130 +301,28 @@ const WithdrawModal = () => {
|
||||
)}
|
||||
</div>
|
||||
<div className='flex w-full flex-wrap border-b border-white/20 p-6'>
|
||||
<div className='mb-2 flex w-full'>
|
||||
<Text size='xs' className='flex-grow text-white/60'>
|
||||
Total Position:
|
||||
</Text>
|
||||
|
||||
<Text size='xs' className='text-white/60'>
|
||||
<FormattedNumber
|
||||
amount={BigNumber(accountStats?.totalPosition ?? 0)
|
||||
.dividedBy(10 ** chain.stakeCurrency.coinDecimals)
|
||||
.toNumber()}
|
||||
prefix='$'
|
||||
animate
|
||||
/>
|
||||
</Text>
|
||||
</div>
|
||||
<div className='flex w-full justify-between'>
|
||||
<Text size='xs' className='flex-grow text-white/60'>
|
||||
Total Liabilities:
|
||||
</Text>
|
||||
<Text size='xs' className=' text-white/60'>
|
||||
<FormattedNumber
|
||||
amount={BigNumber(accountStats?.totalDebt ?? 0)
|
||||
.dividedBy(10 ** chain.stakeCurrency.coinDecimals)
|
||||
.toNumber()}
|
||||
prefix='$'
|
||||
animate
|
||||
/>
|
||||
</Text>
|
||||
</div>
|
||||
</div>
|
||||
<div className='flex w-full flex-wrap'>
|
||||
<Text uppercase className='w-full bg-black/20 px-6 py-2 text-white/40'>
|
||||
Balances
|
||||
</Text>
|
||||
{isLoadingPositions ? (
|
||||
<div>Loading...</div>
|
||||
) : (
|
||||
<div className='flex w-full flex-wrap'>
|
||||
<div className='mb-2 flex w-full border-b border-white/20 bg-black/20 px-6 py-2 '>
|
||||
<Text size='xs' uppercase className='flex-1 text-white'>
|
||||
Asset
|
||||
</Text>
|
||||
<Text size='xs' uppercase className='flex-1 text-white'>
|
||||
Value
|
||||
</Text>
|
||||
<Text size='xs' uppercase className='flex-1 text-white'>
|
||||
Size
|
||||
</Text>
|
||||
<Text size='xs' uppercase className='flex-1 text-white'>
|
||||
APY
|
||||
</Text>
|
||||
</div>
|
||||
{positionsData?.coins.map((coin) => (
|
||||
<div key={coin.denom} className='flex w-full px-4 py-2'>
|
||||
<Text
|
||||
size='xs'
|
||||
className='flex-1 border-l-4 border-profit pl-2 text-white/60'
|
||||
>
|
||||
{getTokenSymbol(coin.denom)}
|
||||
</Text>
|
||||
<Text size='xs' className='flex-1 text-white/60'>
|
||||
<FormattedNumber
|
||||
amount={getTokenTotalUSDValue(coin.amount, coin.denom)}
|
||||
prefix='$'
|
||||
animate
|
||||
/>
|
||||
</Text>
|
||||
<Text size='xs' className='flex-1 text-white/60'>
|
||||
<FormattedNumber
|
||||
amount={BigNumber(coin.amount)
|
||||
.div(10 ** getTokenDecimals(coin.denom))
|
||||
.toNumber()}
|
||||
minDecimals={0}
|
||||
maxDecimals={4}
|
||||
animate
|
||||
/>
|
||||
</Text>
|
||||
<Text size='xs' className='flex-1 text-white/60'>
|
||||
-
|
||||
</Text>
|
||||
</div>
|
||||
))}
|
||||
{positionsData?.debts.map((coin) => (
|
||||
<div key={coin.denom} className='flex w-full px-4 py-2'>
|
||||
<Text size='xs' className='flex-1 border-l-4 border-loss pl-2 text-white/60'>
|
||||
{getTokenSymbol(coin.denom)}
|
||||
</Text>
|
||||
<Text size='xs' className='flex-1 text-white/60'>
|
||||
<FormattedNumber
|
||||
amount={getTokenTotalUSDValue(coin.amount, coin.denom)}
|
||||
prefix='-$'
|
||||
animate
|
||||
/>
|
||||
</Text>
|
||||
<Text size='xs' className='flex-1 text-white/60'>
|
||||
<FormattedNumber
|
||||
amount={BigNumber(coin.amount)
|
||||
.div(10 ** getTokenDecimals(coin.denom))
|
||||
.toNumber()}
|
||||
minDecimals={0}
|
||||
maxDecimals={4}
|
||||
animate
|
||||
/>
|
||||
</Text>
|
||||
<Text size='xs' className='flex-1 text-white/60'>
|
||||
<FormattedNumber
|
||||
amount={Number(marketsData?.[coin.denom].borrow_rate) * 100}
|
||||
minDecimals={0}
|
||||
maxDecimals={2}
|
||||
prefix='-'
|
||||
suffix='%'
|
||||
animate
|
||||
/>
|
||||
</Text>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
<LabelValuePair
|
||||
className='mb-2'
|
||||
label='Total Position:'
|
||||
value={{
|
||||
format: 'number',
|
||||
amount: lookup(accountStats?.totalPosition ?? 0, chain.stakeCurrency.coinDenom),
|
||||
prefix: '$',
|
||||
}}
|
||||
/>
|
||||
<LabelValuePair
|
||||
label='Total Liabilities:'
|
||||
value={{
|
||||
format: 'number',
|
||||
amount: lookup(accountStats?.totalDebt ?? 0, chain.stakeCurrency.coinDenom),
|
||||
prefix: '$',
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<PositionsList title='Balances' data={balances} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
|
||||
export default WithdrawModal
|
10
src/components/Account/index.ts
Normal file
@ -0,0 +1,10 @@
|
||||
// @index(['./*.tsx'], f => `export { ${f.name} } from '${f.path}'`)
|
||||
export { AccountDetails } from './AccountDetails'
|
||||
export { AccountManageOverlay } from './AccountManageOverlay'
|
||||
export { AccountNavigation } from './AccountNavigation'
|
||||
export { AccountStatus } from './AccountStatus'
|
||||
export { ConfirmModal } from './ConfirmModal'
|
||||
export { FundAccountModal } from './FundAccountModal'
|
||||
export { RiskChart } from './RiskChart'
|
||||
export { WithdrawModal } from './WithdrawModal'
|
||||
// @endindex
|
@ -1,9 +1,8 @@
|
||||
import Image from 'next/image'
|
||||
import React, { useState } from 'react'
|
||||
|
||||
import ChevronUpIcon from 'components/Icons/chevron-up.svg'
|
||||
import ChevronDownIcon from 'components/Icons/chevron-down.svg'
|
||||
import Button from 'components/Button'
|
||||
import { Button } from 'components'
|
||||
import { ChevronDown, ChevronUp } from 'components/Icons'
|
||||
import { formatCurrency } from 'utils/formatters'
|
||||
|
||||
type AssetRowProps = {
|
||||
@ -23,7 +22,7 @@ type AssetRowProps = {
|
||||
onRepayClick: () => void
|
||||
}
|
||||
|
||||
const AssetRow = ({ data, onBorrowClick, onRepayClick }: AssetRowProps) => {
|
||||
export const AssetRow = ({ data, onBorrowClick, onRepayClick }: AssetRowProps) => {
|
||||
const [isExpanded, setIsExpanded] = useState(false)
|
||||
|
||||
return (
|
||||
@ -54,7 +53,7 @@ const AssetRow = ({ data, onBorrowClick, onRepayClick }: AssetRowProps) => {
|
||||
</div>
|
||||
<div className='flex flex-1 items-center text-xs'>{data.marketLiquidity}</div>
|
||||
<div className='flex w-[50px] items-center justify-end'>
|
||||
<div className='w-5'>{isExpanded ? <ChevronUpIcon /> : <ChevronDownIcon />}</div>
|
||||
<div className='w-5'>{isExpanded ? <ChevronUp /> : <ChevronDown />}</div>
|
||||
</div>
|
||||
</div>
|
||||
{isExpanded && (
|
||||
@ -84,5 +83,3 @@ const AssetRow = ({ data, onBorrowClick, onRepayClick }: AssetRowProps) => {
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default AssetRow
|
@ -9,11 +9,9 @@ import {
|
||||
import Image from 'next/image'
|
||||
import React from 'react'
|
||||
|
||||
import ChevronUpIcon from 'components/Icons/chevron-up.svg'
|
||||
import ChevronDownIcon from 'components/Icons/chevron-down.svg'
|
||||
import { ChevronDown, ChevronUp } from 'components/Icons'
|
||||
import { formatCurrency } from 'utils/formatters'
|
||||
|
||||
import AssetRow from './AssetRow'
|
||||
import { AssetRow } from 'components/Borrow'
|
||||
|
||||
interface Market {
|
||||
denom: string
|
||||
@ -34,7 +32,7 @@ type Props = {
|
||||
onRepayClick: (denom: string) => void
|
||||
}
|
||||
|
||||
const BorrowTable = ({ data, onBorrowClick, onRepayClick }: Props) => {
|
||||
export const BorrowTable = ({ data, onBorrowClick, onRepayClick }: Props) => {
|
||||
const [sorting, setSorting] = React.useState<SortingState>([])
|
||||
|
||||
const columns = React.useMemo<ColumnDef<Market>[]>(
|
||||
@ -91,9 +89,7 @@ const BorrowTable = ({ data, onBorrowClick, onRepayClick }: Props) => {
|
||||
width: 150,
|
||||
cell: ({ row }) => (
|
||||
<div className='flex items-center justify-end'>
|
||||
<div className='w-5'>
|
||||
{row.getIsExpanded() ? <ChevronUpIcon /> : <ChevronDownIcon />}
|
||||
</div>
|
||||
<div className='w-5'>{row.getIsExpanded() ? <ChevronUp /> : <ChevronDown />}</div>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
@ -158,5 +154,3 @@ const BorrowTable = ({ data, onBorrowClick, onRepayClick }: Props) => {
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default BorrowTable
|
4
src/components/Borrow/index.ts
Normal file
@ -0,0 +1,4 @@
|
||||
// @index(['./**/*.tsx'], f => `export { ${f.name} } from '${f.path}'`)
|
||||
export { AssetRow } from './AssetRow'
|
||||
export { BorrowTable } from './BorrowTable'
|
||||
// @endindex
|
@ -1,9 +1,8 @@
|
||||
import classNames from 'classnames'
|
||||
import { useEffect, useState } from 'react'
|
||||
|
||||
import FormattedNumber from 'components/FormattedNumber'
|
||||
import Text from 'components/Text'
|
||||
import Tooltip from 'components/Tooltip'
|
||||
import { FormattedNumber, Text, Tooltip } from 'components'
|
||||
import { useSettings } from 'stores'
|
||||
|
||||
interface Props {
|
||||
balance: number
|
||||
@ -28,26 +27,25 @@ export const BorrowCapacity = ({
|
||||
hideValues,
|
||||
decimals = 2,
|
||||
}: Props) => {
|
||||
const enableAnimations = useSettings((s) => s.enableAnimations)
|
||||
|
||||
const [percentOfMaxRound, setPercentOfMaxRound] = useState(0)
|
||||
const [percentOfMaxRange, setPercentOfMaxRange] = useState(0)
|
||||
const [limitPercentOfMax, setLimitPercentOfMax] = useState(0)
|
||||
|
||||
useEffect(
|
||||
() => {
|
||||
if (max === 0) {
|
||||
setPercentOfMaxRound(0)
|
||||
setPercentOfMaxRange(0)
|
||||
setLimitPercentOfMax(0)
|
||||
return
|
||||
}
|
||||
useEffect(() => {
|
||||
if (max === 0) {
|
||||
setPercentOfMaxRound(0)
|
||||
setPercentOfMaxRange(0)
|
||||
setLimitPercentOfMax(0)
|
||||
return
|
||||
}
|
||||
|
||||
const pOfMax = +((balance / max) * 100)
|
||||
setPercentOfMaxRound(+(Math.round(pOfMax * 100) / 100))
|
||||
setPercentOfMaxRange(Math.min(Math.max(pOfMax, 0), 100))
|
||||
setLimitPercentOfMax((limit / max) * 100)
|
||||
}, // eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
[balance, max],
|
||||
)
|
||||
const pOfMax = +((balance / max) * 100)
|
||||
setPercentOfMaxRound(+(Math.round(pOfMax * 100) / 100))
|
||||
setPercentOfMaxRange(Math.min(Math.max(pOfMax, 0), 100))
|
||||
setLimitPercentOfMax((limit / max) * 100)
|
||||
}, [balance, max, limit])
|
||||
|
||||
return (
|
||||
<div className={classNames('flex items-center justify-center', className)}>
|
||||
@ -63,7 +61,8 @@ export const BorrowCapacity = ({
|
||||
{!hideValues && (
|
||||
<div
|
||||
className={classNames(
|
||||
'duration-800 transition-[opcity] delay-[1600ms] text-3xs-caps',
|
||||
enableAnimations && 'duration-800 transition-[opcity] delay-[1600ms]',
|
||||
'text-3xs-caps',
|
||||
limitPercentOfMax ? 'opacity-60' : 'opacity-0',
|
||||
)}
|
||||
>
|
||||
@ -79,7 +78,10 @@ export const BorrowCapacity = ({
|
||||
>
|
||||
<div className='absolute h-full w-full rounded-lg shadow-inset gradient-hatched '>
|
||||
<div
|
||||
className='ease-loss absolute left-0 h-full max-w-full rounded-l-3xl bg-body-dark transition-[right] duration-1000'
|
||||
className={classNames(
|
||||
'absolute left-0 h-full max-w-full rounded-l-3xl bg-body-dark',
|
||||
enableAnimations && 'transition-[right] duration-1000 ease-linear',
|
||||
)}
|
||||
style={{
|
||||
right: `${limitPercentOfMax ? 100 - limitPercentOfMax : 100}%`,
|
||||
}}
|
||||
@ -87,7 +89,10 @@ export const BorrowCapacity = ({
|
||||
|
||||
<div className='absolute top-0 h-full w-full'>
|
||||
<div
|
||||
className='h-full rounded-lg transition-[width] duration-1000 ease-linear'
|
||||
className={classNames(
|
||||
'h-full rounded-lg',
|
||||
enableAnimations && 'transition-[width] duration-1000 ease-linear',
|
||||
)}
|
||||
style={{
|
||||
width: `${percentOfMaxRange || 0.02}%`,
|
||||
WebkitMask: 'linear-gradient(#fff 0 0)',
|
||||
@ -97,11 +102,19 @@ export const BorrowCapacity = ({
|
||||
</div>
|
||||
|
||||
<div
|
||||
className='absolute bottom-0 h-[120%] w-[1px] bg-white transition-[left] duration-1000 ease-linear'
|
||||
className={classNames(
|
||||
'absolute bottom-0 h-[120%] w-[1px] bg-white',
|
||||
enableAnimations && 'transition-[left] duration-1000 ease-linear',
|
||||
)}
|
||||
style={{ left: `${limitPercentOfMax || 0}%` }}
|
||||
/>
|
||||
{showPercentageText ? (
|
||||
<span className='absolute top-1/2 mt-[1px] w-full -translate-y-1/2 animate-fadein text-center opacity-0 text-2xs-caps'>
|
||||
<span
|
||||
className={classNames(
|
||||
'absolute top-1/2 mt-[1px] w-full -translate-y-1/2 text-center text-2xs-caps',
|
||||
enableAnimations && 'animate-fadein opacity-0',
|
||||
)}
|
||||
>
|
||||
{max !== 0 && (
|
||||
<FormattedNumber
|
||||
className='text-white'
|
@ -4,21 +4,20 @@ import React, { useMemo, useState } from 'react'
|
||||
import { NumericFormat } from 'react-number-format'
|
||||
import { toast } from 'react-toastify'
|
||||
|
||||
import Button from 'components/Button'
|
||||
import CircularProgress from 'components/CircularProgress'
|
||||
import ContainerSecondary from 'components/ContainerSecondary'
|
||||
import Gauge from 'components/Gauge'
|
||||
import ProgressBar from 'components/ProgressBar'
|
||||
import Slider from 'components/Slider'
|
||||
import Text from 'components/Text'
|
||||
import Tooltip from 'components/Tooltip'
|
||||
import useBorrowFunds from 'hooks/mutations/useBorrowFunds'
|
||||
import useAccountStats, { AccountStatsAction } from 'hooks/useAccountStats'
|
||||
import useAllBalances from 'hooks/useAllBalances'
|
||||
import useCalculateMaxBorrowAmount from 'hooks/useCalculateMaxBorrowAmount'
|
||||
import useCreditAccountPositions from 'hooks/useCreditAccountPositions'
|
||||
import useMarkets from 'hooks/useMarkets'
|
||||
import useTokenPrices from 'hooks/useTokenPrices'
|
||||
import {
|
||||
Button,
|
||||
CircularProgress,
|
||||
ContainerSecondary,
|
||||
Gauge,
|
||||
PositionsList,
|
||||
ProgressBar,
|
||||
Slider,
|
||||
Text,
|
||||
Tooltip,
|
||||
} from 'components'
|
||||
import { useAccountStats, useBalances, useCalculateMaxBorrowAmount } from 'hooks/data'
|
||||
import { useBorrowFunds } from 'hooks/mutations'
|
||||
import { useAllBalances, useMarkets, useTokenPrices } from 'hooks/queries'
|
||||
import { useAccountDetailsStore } from 'stores'
|
||||
import { chain } from 'utils/chains'
|
||||
import { formatCurrency, formatValue } from 'utils/formatters'
|
||||
@ -30,14 +29,13 @@ type Props = {
|
||||
tokenDenom: string
|
||||
}
|
||||
|
||||
const BorrowModal = ({ show, onClose, tokenDenom }: Props) => {
|
||||
export const BorrowModal = ({ show, onClose, tokenDenom }: Props) => {
|
||||
const [amount, setAmount] = useState(0)
|
||||
const [isBorrowToCreditAccount, setIsBorrowToCreditAccount] = useState(false)
|
||||
|
||||
const selectedAccount = useAccountDetailsStore((s) => s.selectedAccount)
|
||||
const { data: positionsData, isLoading: isLoadingPositions } = useCreditAccountPositions(
|
||||
selectedAccount ?? '',
|
||||
)
|
||||
|
||||
const balances = useBalances()
|
||||
|
||||
const { actions, borrowAmount } = useMemo(() => {
|
||||
const borrowAmount = BigNumber(amount)
|
||||
@ -124,17 +122,6 @@ const BorrowModal = ({ show, onClose, tokenDenom }: Props) => {
|
||||
setAmount(0)
|
||||
}
|
||||
|
||||
const getTokenTotalUSDValue = (amount: string, denom: string) => {
|
||||
// early return if prices are not fetched yet
|
||||
if (!tokenPrices) return 0
|
||||
|
||||
return (
|
||||
BigNumber(amount)
|
||||
.div(10 ** getTokenDecimals(denom))
|
||||
.toNumber() * tokenPrices[denom]
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<Transition appear show={show} as={React.Fragment}>
|
||||
<Dialog as='div' className='relative z-10' onClose={onClose}>
|
||||
@ -310,62 +297,7 @@ const BorrowModal = ({ show, onClose, tokenDenom }: Props) => {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className='rounded-md border border-white/20 p-3'>
|
||||
<h4 className='mb-2 font-bold'>Balances</h4>
|
||||
{isLoadingPositions ? (
|
||||
<div>Loading...</div>
|
||||
) : (
|
||||
<table className='w-full border-separate border-spacing-1'>
|
||||
<thead className='text-left text-xs font-semibold'>
|
||||
<tr>
|
||||
<th>Asset</th>
|
||||
<th>Value</th>
|
||||
<th>Size</th>
|
||||
<th className='text-right'>APY</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{accountStats?.assets.map((coin) => (
|
||||
<tr key={coin.denom} className='text-xs text-white/50'>
|
||||
<td>{getTokenSymbol(coin.denom)}</td>
|
||||
<td>
|
||||
{formatCurrency(getTokenTotalUSDValue(coin.amount, coin.denom))}
|
||||
</td>
|
||||
<td>
|
||||
{BigNumber(coin.amount)
|
||||
.div(10 ** getTokenDecimals(coin.denom))
|
||||
.toNumber()
|
||||
.toLocaleString(undefined, {
|
||||
maximumFractionDigits: getTokenDecimals(coin.denom),
|
||||
})}
|
||||
</td>
|
||||
<td className='text-right'>-</td>
|
||||
</tr>
|
||||
))}
|
||||
{accountStats?.debts.map((coin) => (
|
||||
<tr key={coin.denom} className='text-xs text-red-500'>
|
||||
<td className='text-white/50'>{getTokenSymbol(coin.denom)}</td>
|
||||
<td>
|
||||
-{formatCurrency(getTokenTotalUSDValue(coin.amount, coin.denom))}
|
||||
</td>
|
||||
<td>
|
||||
-
|
||||
{BigNumber(coin.amount)
|
||||
.div(10 ** getTokenDecimals(coin.denom))
|
||||
.toNumber()
|
||||
.toLocaleString(undefined, {
|
||||
maximumFractionDigits: 6,
|
||||
})}
|
||||
</td>
|
||||
<td className='text-right'>
|
||||
-{(Number(marketsData?.[coin.denom].borrow_rate) * 100).toFixed(1)}%
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
)}
|
||||
</div>
|
||||
<PositionsList title='Balances' data={balances} />
|
||||
</div>
|
||||
</Dialog.Panel>
|
||||
</Transition.Child>
|
||||
@ -375,5 +307,3 @@ const BorrowModal = ({ show, onClose, tokenDenom }: Props) => {
|
||||
</Transition>
|
||||
)
|
||||
}
|
||||
|
||||
export default BorrowModal
|
@ -1,7 +1,8 @@
|
||||
import classNames from 'classnames'
|
||||
import React, { LegacyRef, ReactNode } from 'react'
|
||||
|
||||
import CircularProgress from 'components/CircularProgress'
|
||||
import { CircularProgress } from 'components'
|
||||
import { useSettings } from 'stores'
|
||||
|
||||
interface Props {
|
||||
children?: string | ReactNode
|
||||
@ -57,7 +58,7 @@ export const buttonVariantClasses = {
|
||||
text: 'border-none bg-transparent',
|
||||
}
|
||||
|
||||
const Button = React.forwardRef(function Button(
|
||||
export const Button = React.forwardRef(function Button(
|
||||
{
|
||||
children,
|
||||
className = '',
|
||||
@ -73,6 +74,7 @@ const Button = React.forwardRef(function Button(
|
||||
ref,
|
||||
) {
|
||||
const buttonClasses = []
|
||||
const enableAnimations = useSettings((s) => s.enableAnimations)
|
||||
|
||||
switch (variant) {
|
||||
case 'round':
|
||||
@ -96,7 +98,8 @@ const Button = React.forwardRef(function Button(
|
||||
return (
|
||||
<button
|
||||
className={classNames(
|
||||
'cursor-pointer appearance-none break-normal rounded-3xl outline-none transition-colors',
|
||||
'outline-nones cursor-pointer appearance-none break-normal rounded-3xl',
|
||||
enableAnimations && 'transition-color',
|
||||
buttonClasses,
|
||||
buttonVariantClasses[variant],
|
||||
disabled && 'pointer-events-none opacity-50',
|
||||
@ -114,5 +117,3 @@ const Button = React.forwardRef(function Button(
|
||||
</button>
|
||||
)
|
||||
})
|
||||
|
||||
export default Button
|
@ -6,7 +6,7 @@ interface Props {
|
||||
className?: string
|
||||
}
|
||||
|
||||
const Card = ({ children, className }: Props) => {
|
||||
export const Card = ({ children, className }: Props) => {
|
||||
return (
|
||||
<div
|
||||
className={classNames(
|
||||
@ -18,5 +18,3 @@ const Card = ({ children, className }: Props) => {
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Card
|
@ -1,18 +1,32 @@
|
||||
import classNames from 'classnames'
|
||||
|
||||
import { Text } from 'components'
|
||||
import { useSettings } from 'stores'
|
||||
|
||||
interface Props {
|
||||
color?: string
|
||||
size?: number
|
||||
className?: string
|
||||
}
|
||||
|
||||
const CircularProgress = ({ color = '#FFFFFF', size = 20, className }: Props) => {
|
||||
export const CircularProgress = ({ color = '#FFFFFF', size = 20, className }: Props) => {
|
||||
const enableAnimations = useSettings((s) => s.enableAnimations)
|
||||
|
||||
const borderWidth = `${size / 10}px`
|
||||
const borderColor = `${color} transparent transparent transparent`
|
||||
const loaderClasses = classNames('inline-block relative', className)
|
||||
const elementClasses =
|
||||
'block absolute w-4/5 h-4/5 m-[10%] rounded-full animate-progress border-solid'
|
||||
|
||||
if (!enableAnimations)
|
||||
return (
|
||||
<div className={loaderClasses} style={{ width: `${size}px`, height: `${size}px` }}>
|
||||
<Text className='text-center' uppercase size='lg'>
|
||||
...
|
||||
</Text>
|
||||
</div>
|
||||
)
|
||||
|
||||
return (
|
||||
<div className={loaderClasses} style={{ width: `${size}px`, height: `${size}px` }}>
|
||||
<div
|
||||
@ -49,5 +63,3 @@ const CircularProgress = ({ color = '#FFFFFF', size = 20, className }: Props) =>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default CircularProgress
|
@ -1,6 +1,6 @@
|
||||
import React from 'react'
|
||||
|
||||
const ContainerSecondary = ({
|
||||
export const ContainerSecondary = ({
|
||||
children,
|
||||
className,
|
||||
}: {
|
||||
@ -13,5 +13,3 @@ const ContainerSecondary = ({
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default ContainerSecondary
|
66
src/components/FormattedNumber.tsx
Normal file
@ -0,0 +1,66 @@
|
||||
import classNames from 'classnames'
|
||||
import React, { useEffect, useRef } from 'react'
|
||||
import { animated, useSpring } from 'react-spring'
|
||||
|
||||
import { useSettings } from 'stores'
|
||||
import { formatValue } from 'utils/formatters'
|
||||
|
||||
export const FormattedNumber = React.memo(
|
||||
({
|
||||
amount,
|
||||
animate = false,
|
||||
className,
|
||||
minDecimals = 2,
|
||||
maxDecimals = 2,
|
||||
thousandSeparator = true,
|
||||
prefix = false,
|
||||
suffix = false,
|
||||
rounded = false,
|
||||
abbreviated = false,
|
||||
}: FormattedNumberProps) => {
|
||||
const enableAnimations = useSettings((s) => s.enableAnimations)
|
||||
const prevAmountRef = useRef<number>(0)
|
||||
|
||||
useEffect(() => {
|
||||
if (prevAmountRef.current !== Number(amount)) prevAmountRef.current = Number(amount)
|
||||
}, [amount])
|
||||
|
||||
const springAmount = useSpring({
|
||||
number: Number(amount),
|
||||
from: { number: prevAmountRef.current },
|
||||
config: { duration: 1000 },
|
||||
})
|
||||
|
||||
return (prevAmountRef.current === amount && amount === 0) || !animate || !enableAnimations ? (
|
||||
<span className={classNames('number', className)}>
|
||||
{formatValue(
|
||||
amount,
|
||||
minDecimals,
|
||||
maxDecimals,
|
||||
thousandSeparator,
|
||||
prefix,
|
||||
suffix,
|
||||
rounded,
|
||||
abbreviated,
|
||||
)}
|
||||
</span>
|
||||
) : (
|
||||
<animated.span className={classNames('number', className)}>
|
||||
{springAmount.number.to((num) =>
|
||||
formatValue(
|
||||
num,
|
||||
minDecimals,
|
||||
maxDecimals,
|
||||
thousandSeparator,
|
||||
prefix,
|
||||
suffix,
|
||||
rounded,
|
||||
abbreviated,
|
||||
),
|
||||
)}
|
||||
</animated.span>
|
||||
)
|
||||
},
|
||||
)
|
||||
|
||||
FormattedNumber.displayName = 'FormattedNumber'
|
@ -1,9 +1,10 @@
|
||||
import classNames from 'classnames'
|
||||
import { ReactNode } from 'react'
|
||||
|
||||
import Tooltip from './Tooltip'
|
||||
import { Tooltip } from 'components'
|
||||
import { useSettings } from 'stores'
|
||||
|
||||
type Props = {
|
||||
interface Props {
|
||||
tooltip: string | ReactNode
|
||||
strokeWidth?: number
|
||||
background?: string
|
||||
@ -12,7 +13,15 @@ type Props = {
|
||||
label?: string
|
||||
}
|
||||
|
||||
const Gauge = ({ background = '#15161A', diameter = 40, value = 0, label, tooltip }: Props) => {
|
||||
export const Gauge = ({
|
||||
background = '#15161A',
|
||||
diameter = 40,
|
||||
value = 0,
|
||||
label,
|
||||
tooltip,
|
||||
}: Props) => {
|
||||
const enableAnimations = useSettings((s) => s.enableAnimations)
|
||||
|
||||
const percentage = value * 100
|
||||
const percentageValue = percentage > 100 ? 100 : percentage < 0 ? 0 : percentage
|
||||
const semiCirclePercentage = Math.abs(percentageValue / 2 - 50)
|
||||
@ -59,7 +68,7 @@ const Gauge = ({ background = '#15161A', diameter = 40, value = 0, label, toolti
|
||||
strokeWidth={5}
|
||||
style={{
|
||||
strokeDashoffset: semiCirclePercentage,
|
||||
transition: 'stroke-dashoffset 1s ease',
|
||||
transition: enableAnimations ? 'stroke-dashoffset 1s ease' : 'none',
|
||||
}}
|
||||
shapeRendering='geometricPrecision'
|
||||
/>
|
||||
@ -82,5 +91,3 @@ const Gauge = ({ background = '#15161A', diameter = 40, value = 0, label, toolti
|
||||
</Tooltip>
|
||||
)
|
||||
}
|
||||
|
||||
export default Gauge
|
Before Width: | Height: | Size: 964 B After Width: | Height: | Size: 964 B |
Before Width: | Height: | Size: 672 B After Width: | Height: | Size: 672 B |
Before Width: | Height: | Size: 382 B After Width: | Height: | Size: 382 B |
Before Width: | Height: | Size: 537 B After Width: | Height: | Size: 537 B |
Before Width: | Height: | Size: 933 B After Width: | Height: | Size: 933 B |
Before Width: | Height: | Size: 397 B After Width: | Height: | Size: 397 B |
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 451 B After Width: | Height: | Size: 451 B |
Before Width: | Height: | Size: 272 B After Width: | Height: | Size: 272 B |
Before Width: | Height: | Size: 166 B After Width: | Height: | Size: 166 B |
Before Width: | Height: | Size: 324 B After Width: | Height: | Size: 324 B |
Before Width: | Height: | Size: 325 B After Width: | Height: | Size: 325 B |
Before Width: | Height: | Size: 328 B After Width: | Height: | Size: 328 B |
Before Width: | Height: | Size: 329 B After Width: | Height: | Size: 329 B |
Before Width: | Height: | Size: 96 KiB After Width: | Height: | Size: 96 KiB |
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.8 KiB |
@ -1,3 +1,3 @@
|
||||
;<svg fill='currentColor' viewBox='0 0 12 14' xmlns='http://www.w3.org/2000/svg'>
|
||||
<svg fill='currentColor' viewBox='0 0 12 14' xmlns='http://www.w3.org/2000/svg'>
|
||||
<path d='M6.47317 3.19325C6.40977 3.13256 6.335 3.08498 6.25317 3.05325C6.09086 2.98657 5.90881 2.98657 5.7465 3.05325C5.66467 3.08498 5.58991 3.13256 5.5265 3.19325L2.85984 5.85992C2.79768 5.92208 2.74837 5.99587 2.71473 6.07709C2.68109 6.1583 2.66378 6.24535 2.66378 6.33325C2.66378 6.42116 2.68109 6.5082 2.71473 6.58942C2.74837 6.67063 2.79768 6.74443 2.85984 6.80659C2.922 6.86874 2.99579 6.91805 3.077 6.95169C3.15822 6.98533 3.24526 7.00265 3.33317 7.00265C3.42108 7.00265 3.50812 6.98533 3.58934 6.95169C3.67055 6.91805 3.74434 6.86874 3.8065 6.80659L5.33317 5.27325V12.9999C5.33317 13.1767 5.40341 13.3463 5.52843 13.4713C5.65346 13.5963 5.82303 13.6666 5.99984 13.6666C6.17665 13.6666 6.34622 13.5963 6.47124 13.4713C6.59627 13.3463 6.6665 13.1767 6.6665 12.9999V5.27325L8.19317 6.80659C8.25515 6.86907 8.32888 6.91867 8.41012 6.95251C8.49136 6.98636 8.5785 7.00378 8.6665 7.00378C8.75451 7.00378 8.84165 6.98636 8.92289 6.95251C9.00413 6.91867 9.07786 6.86907 9.13984 6.80659C9.20232 6.74461 9.25192 6.67088 9.28576 6.58964C9.31961 6.5084 9.33704 6.42126 9.33704 6.33325C9.33704 6.24524 9.31961 6.15811 9.28576 6.07687C9.25192 5.99563 9.20232 5.92189 9.13984 5.85992L6.47317 3.19325ZM10.6665 0.333252H1.33317C1.15636 0.333252 0.98679 0.40349 0.861766 0.528514C0.736742 0.653538 0.666504 0.823108 0.666504 0.999919C0.666504 1.17673 0.736742 1.3463 0.861766 1.47132C0.98679 1.59635 1.15636 1.66659 1.33317 1.66659H10.6665C10.8433 1.66659 11.0129 1.59635 11.1379 1.47132C11.2629 1.3463 11.3332 1.17673 11.3332 0.999919C11.3332 0.823108 11.2629 0.653538 11.1379 0.528514C11.0129 0.40349 10.8433 0.333252 10.6665 0.333252Z' />
|
||||
</svg>
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 225 B After Width: | Height: | Size: 225 B |
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.1 KiB |
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.2 KiB |
Before Width: | Height: | Size: 944 B After Width: | Height: | Size: 944 B |
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 3.8 KiB |
Before Width: | Height: | Size: 9.7 KiB After Width: | Height: | Size: 9.7 KiB |
Before Width: | Height: | Size: 664 B After Width: | Height: | Size: 664 B |
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 388 B After Width: | Height: | Size: 388 B |
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.2 KiB |
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 4.2 KiB |
Before Width: | Height: | Size: 191 B After Width: | Height: | Size: 191 B |
Before Width: | Height: | Size: 746 B After Width: | Height: | Size: 746 B |
Before Width: | Height: | Size: 442 B After Width: | Height: | Size: 442 B |
Before Width: | Height: | Size: 443 B After Width: | Height: | Size: 443 B |
Before Width: | Height: | Size: 547 B After Width: | Height: | Size: 547 B |
Before Width: | Height: | Size: 485 B After Width: | Height: | Size: 485 B |
Before Width: | Height: | Size: 371 B After Width: | Height: | Size: 371 B |
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 165 B After Width: | Height: | Size: 165 B |
Before Width: | Height: | Size: 615 B After Width: | Height: | Size: 615 B |
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.1 KiB |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 498 B After Width: | Height: | Size: 498 B |
48
src/components/Icons/index.ts
Normal file
@ -0,0 +1,48 @@
|
||||
// @index(['./*.svg'], f => `export { default as ${f.name} } from '${f.path}.svg'`)
|
||||
export { default as Add } from './Add.svg'
|
||||
export { default as ArrowBack } from './ArrowBack.svg'
|
||||
export { default as ArrowDown } from './ArrowDown.svg'
|
||||
export { default as ArrowLeftLine } from './ArrowLeftLine.svg'
|
||||
export { default as ArrowRightLine } from './ArrowRightLine.svg'
|
||||
export { default as ArrowsLeftRight } from './ArrowsLeftRight.svg'
|
||||
export { default as ArrowsUpDown } from './ArrowsUpDown.svg'
|
||||
export { default as ArrowUp } from './ArrowUp.svg'
|
||||
export { default as BurgerMenu } from './BurgerMenu.svg'
|
||||
export { default as Check } from './Check.svg'
|
||||
export { default as ChevronDown } from './ChevronDown.svg'
|
||||
export { default as ChevronLeft } from './ChevronLeft.svg'
|
||||
export { default as ChevronRight } from './ChevronRight.svg'
|
||||
export { default as ChevronUp } from './ChevronUp.svg'
|
||||
export { default as Close } from './Close.svg'
|
||||
export { default as Copy } from './Copy.svg'
|
||||
export { default as Deposit } from './Deposit.svg'
|
||||
export { default as Discord } from './Discord.svg'
|
||||
export { default as Edit } from './Edit.svg'
|
||||
export { default as Ellipsis } from './Ellipsis.svg'
|
||||
export { default as ExternalLink } from './ExternalLink.svg'
|
||||
export { default as Failed } from './Failed.svg'
|
||||
export { default as Github } from './Github.svg'
|
||||
export { default as Info } from './Info.svg'
|
||||
export { default as Logo } from './Logo.svg'
|
||||
export { default as MarsProtocol } from './MarsProtocol.svg'
|
||||
export { default as Medium } from './Medium.svg'
|
||||
export { default as Osmo } from './Osmo.svg'
|
||||
export { default as Questionmark } from './Questionmark.svg'
|
||||
export { default as Reddit } from './Reddit.svg'
|
||||
export { default as Rubbish } from './Rubbish.svg'
|
||||
export { default as Search } from './Search.svg'
|
||||
export { default as SmallClose } from './SmallClose.svg'
|
||||
export { default as SortAsc } from './SortAsc.svg'
|
||||
export { default as SortDesc } from './SortDesc.svg'
|
||||
export { default as SortNone } from './SortNone.svg'
|
||||
export { default as Subtract } from './Subtract.svg'
|
||||
export { default as Success } from './Success.svg'
|
||||
export { default as Telegram } from './Telegram.svg'
|
||||
export { default as TriangleDown } from './TriangleDown.svg'
|
||||
export { default as Twitter } from './Twitter.svg'
|
||||
export { default as Wallet } from './Wallet.svg'
|
||||
export { default as WalletConnect } from './WalletConnect.svg'
|
||||
export { default as Warning } from './Warning.svg'
|
||||
export { default as Withdraw } from './Withdraw.svg'
|
||||
export { default as YouTube } from './YouTube.svg'
|
||||
// @endindex
|
24
src/components/LabelValuePair.tsx
Normal file
@ -0,0 +1,24 @@
|
||||
import classNames from 'classnames'
|
||||
|
||||
import { FormattedNumber, Text } from 'components'
|
||||
|
||||
interface ValueData extends FormattedNumberProps {
|
||||
format?: 'number' | 'string'
|
||||
}
|
||||
|
||||
interface Props {
|
||||
label: string
|
||||
value: ValueData
|
||||
className?: string
|
||||
}
|
||||
|
||||
export const LabelValuePair = ({ label, value, className }: Props) => (
|
||||
<div className={classNames('flex w-full', className)}>
|
||||
<Text size='xs' className='flex-grow text-white/60'>
|
||||
{label}
|
||||
</Text>
|
||||
<Text size='xs' className='text-white/60'>
|
||||
{value.format === 'number' ? <FormattedNumber animate {...value} /> : value.amount || ''}
|
||||
</Text>
|
||||
</div>
|
||||
)
|
@ -1,19 +1,21 @@
|
||||
import { useWallet, WalletConnectionStatus } from '@marsprotocol/wallet-connector'
|
||||
import classNames from 'classnames'
|
||||
import React, { useEffect } from 'react'
|
||||
import { useWallet, WalletConnectionStatus } from '@marsprotocol/wallet-connector'
|
||||
|
||||
import AccountManager from 'components/Account/AccountDetails'
|
||||
import DesktopNavigation from 'components/Navigation/DesktopNavigation'
|
||||
import useCreditAccounts from 'hooks/useCreditAccounts'
|
||||
import { useWalletStore } from 'stores'
|
||||
import { AccountDetails } from 'components/Account'
|
||||
import { DesktopNavigation } from 'components/Navigation'
|
||||
import { useCreditAccounts } from 'hooks/queries'
|
||||
import { useSettings, useWalletStore } from 'stores'
|
||||
|
||||
const filter = {
|
||||
day: 'brightness-100 hue-rotate-0',
|
||||
night: '-hue-rotate-82 brightness-30',
|
||||
}
|
||||
|
||||
const Layout = ({ children }: { children: React.ReactNode }) => {
|
||||
const { data: creditAccountsList, isLoading: isLoadingCreditAccounts } = useCreditAccounts()
|
||||
export const Layout = ({ children }: { children: React.ReactNode }) => {
|
||||
const enableAnimations = useSettings((s) => s.enableAnimations)
|
||||
|
||||
const { data: creditAccountsList } = useCreditAccounts()
|
||||
const hasCreditAccounts = creditAccountsList && creditAccountsList.length > 0
|
||||
|
||||
const { status, signingCosmWasmClient, chainInfo, address, name } = useWallet()
|
||||
@ -27,19 +29,18 @@ const Layout = ({ children }: { children: React.ReactNode }) => {
|
||||
|
||||
const backgroundClasses = classNames(
|
||||
isConnected ? filter.day : filter.night,
|
||||
'top-0 left-0 absolute block h-full w-full flex-col bg-body bg-mars bg-desktop bg-top bg-no-repeat filter transition-background duration-3000 ease-linear',
|
||||
'top-0 left-0 absolute block h-full w-full flex-col bg-body bg-mars bg-desktop bg-top bg-no-repeat filter',
|
||||
enableAnimations && 'transition-background duration-3000 ease-linear',
|
||||
)
|
||||
|
||||
return (
|
||||
<div className='relative min-h-screen w-full'>
|
||||
<div className={backgroundClasses} />
|
||||
<DesktopNavigation />
|
||||
<main className='relative flex lg:h-[calc(100vh-120px)]'>
|
||||
<main className='relative flex lg:min-h-[calc(100vh-120px)]'>
|
||||
<div className='flex flex-grow flex-wrap p-6'>{children}</div>
|
||||
{hasCreditAccounts && <AccountManager />}
|
||||
{hasCreditAccounts && <AccountDetails />}
|
||||
</main>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Layout
|
@ -1,8 +1,8 @@
|
||||
import classNames from 'classnames'
|
||||
import { ReactNode } from 'react'
|
||||
|
||||
import Card from 'components/Card'
|
||||
import CloseIcon from 'components/Icons/close.svg'
|
||||
import { Card } from 'components'
|
||||
import { Close } from 'components/Icons'
|
||||
|
||||
interface Props {
|
||||
children?: ReactNode | string
|
||||
@ -12,7 +12,7 @@ interface Props {
|
||||
setOpen?: (open: boolean) => void
|
||||
}
|
||||
|
||||
const Modal = ({ children, content, className, open, setOpen }: Props) => {
|
||||
export const Modal = ({ children, content, className, open, setOpen }: Props) => {
|
||||
const onClickAway = () => {
|
||||
if (setOpen) setOpen(false)
|
||||
}
|
||||
@ -27,7 +27,7 @@ const Modal = ({ children, content, className, open, setOpen }: Props) => {
|
||||
onClick={onClickAway}
|
||||
role='button'
|
||||
>
|
||||
<CloseIcon />
|
||||
<Close />
|
||||
</span>
|
||||
)}
|
||||
{children ? children : content}
|
||||
@ -41,5 +41,3 @@ const Modal = ({ children, content, className, open, setOpen }: Props) => {
|
||||
</div>
|
||||
) : null
|
||||
}
|
||||
|
||||
export default Modal
|
@ -1,11 +1,9 @@
|
||||
import { ConfirmModal, FundAccountModal, WithdrawModal } from './Account'
|
||||
|
||||
const Modals = () => (
|
||||
export const Modals = () => (
|
||||
<>
|
||||
<FundAccountModal />
|
||||
<WithdrawModal />
|
||||
<ConfirmModal />
|
||||
</>
|
||||
)
|
||||
|
||||
export default Modals
|
@ -1,14 +1,13 @@
|
||||
import Link from 'next/link'
|
||||
|
||||
import { AccountNavigation, AccountStatus } from 'components/Account'
|
||||
import Logo from 'components/Icons/logo.svg'
|
||||
import { menuTree, NavLink } from 'components/Navigation'
|
||||
import SearchInput from 'components/Navigation/SearchInput'
|
||||
import Wallet from 'components/Wallet/Wallet'
|
||||
import useCreditAccounts from 'hooks/useCreditAccounts'
|
||||
import { Logo } from 'components/Icons'
|
||||
import { menuTree, NavLink, SearchInput } from 'components/Navigation'
|
||||
import { Wallet } from 'components/Wallet'
|
||||
import { useCreditAccounts } from 'hooks/queries'
|
||||
import { useAccountDetailsStore, useWalletStore } from 'stores'
|
||||
|
||||
const Navigation = () => {
|
||||
export const DesktopNavigation = () => {
|
||||
const address = useWalletStore((s) => s.address)
|
||||
const selectedAccount = useAccountDetailsStore((s) => s.selectedAccount)
|
||||
|
||||
@ -52,5 +51,3 @@ const Navigation = () => {
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Navigation
|
@ -8,7 +8,7 @@ interface Props {
|
||||
children: string | ReactNode
|
||||
}
|
||||
|
||||
const NavLink = ({ href, children }: Props) => {
|
||||
export const NavLink = ({ href, children }: Props) => {
|
||||
const router = useRouter()
|
||||
const isActive = router.pathname === href
|
||||
|
||||
@ -25,5 +25,3 @@ const NavLink = ({ href, children }: Props) => {
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
|
||||
export default NavLink
|
@ -1,8 +1,8 @@
|
||||
import SearchIcon from 'components/Icons/search.svg'
|
||||
const SearchInput = () => (
|
||||
import { Search } from 'components/Icons'
|
||||
export const SearchInput = () => (
|
||||
<div className='relative mt-[1px] py-2 text-white'>
|
||||
<span className='absolute top-1/2 left-0 flex h-6 w-8 -translate-y-1/2 items-center pl-2'>
|
||||
<SearchIcon />
|
||||
<Search />
|
||||
</span>
|
||||
<input
|
||||
className='w-[280px] rounded-md border border-white/20 bg-black/30 py-2 pl-10 text-sm text-white placeholder:text-white/40 focus:border-white/60 focus:outline-none'
|
||||
@ -10,5 +10,3 @@ const SearchInput = () => (
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
|
||||
export default SearchInput
|
6
src/components/Navigation/index.ts
Normal file
@ -0,0 +1,6 @@
|
||||
// @index(['./*.ts*'], f => `export { ${f.name} } from '${f.path}'`)
|
||||
export { DesktopNavigation } from './DesktopNavigation'
|
||||
export { menuTree } from './menuTree'
|
||||
export { NavLink } from './NavLink'
|
||||
export { SearchInput } from './SearchInput'
|
||||
// @endindex
|
@ -1,9 +1,7 @@
|
||||
const navItems = [
|
||||
export const menuTree = [
|
||||
{ href: '/trade', label: 'Trade' },
|
||||
{ href: '/earn', label: 'Earn' },
|
||||
{ href: '/borrow', label: 'Borrow' },
|
||||
{ href: '/portfolio', label: 'Portfolio' },
|
||||
{ href: '/council', label: 'Council' },
|
||||
]
|
||||
|
||||
export default navItems
|
@ -9,7 +9,7 @@ interface Props {
|
||||
setShow: (show: boolean) => void
|
||||
}
|
||||
|
||||
const Overlay = ({ children, content, className, show, setShow }: Props) => {
|
||||
export const Overlay = ({ children, content, className, show, setShow }: Props) => {
|
||||
const onClickAway = () => {
|
||||
setShow(false)
|
||||
}
|
||||
@ -32,5 +32,3 @@ const Overlay = ({ children, content, className, show, setShow }: Props) => {
|
||||
</>
|
||||
) : null
|
||||
}
|
||||
|
||||
export default Overlay
|
@ -1,7 +1,7 @@
|
||||
import classNames from 'classnames'
|
||||
import { ReactNode } from 'react'
|
||||
|
||||
import Button from 'components/Button'
|
||||
import { Button } from 'components'
|
||||
|
||||
interface Props {
|
||||
className?: string
|
||||
@ -11,7 +11,7 @@ interface Props {
|
||||
text: string | ReactNode
|
||||
}
|
||||
|
||||
const OverlayAction = ({ className, icon, onClick, setShow, text }: Props) => {
|
||||
export const OverlayAction = ({ className, icon, onClick, setShow, text }: Props) => {
|
||||
return (
|
||||
<Button
|
||||
className={classNames(
|
||||
@ -29,5 +29,3 @@ const OverlayAction = ({ className, icon, onClick, setShow, text }: Props) => {
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
|
||||
export default OverlayAction
|
4
src/components/Overlay/index.ts
Normal file
@ -0,0 +1,4 @@
|
||||
// @index(['./*.tsx'], f => `export { ${f.name} } from '${f.path}'`)
|
||||
export { Overlay } from './Overlay'
|
||||
export { OverlayAction } from './OverlayAction'
|
||||
// @endindex
|
61
src/components/PositionsList.tsx
Normal file
@ -0,0 +1,61 @@
|
||||
import classNames from 'classnames'
|
||||
|
||||
import { FormattedNumber, Text } from 'components'
|
||||
|
||||
interface Props {
|
||||
title: string
|
||||
data?: PositionsData[]
|
||||
}
|
||||
|
||||
export const PositionsList = (props: Props) => {
|
||||
if (!props?.data || props.data?.length === 0) return null
|
||||
const arrayKeys = Object.keys(props.data[0])
|
||||
|
||||
return (
|
||||
<div className='flex w-full flex-wrap'>
|
||||
<Text uppercase className='w-full bg-black/20 px-4 py-2 text-white/40'>
|
||||
{props.title}
|
||||
</Text>
|
||||
<div className='flex w-full flex-wrap'>
|
||||
<>
|
||||
<div className='mb-2 flex w-full border-b border-white/20 bg-black/20 px-4 py-2'>
|
||||
{arrayKeys.map((label, index) => (
|
||||
<Text key={index} size='xs' uppercase className='flex-1 text-white'>
|
||||
{label}
|
||||
</Text>
|
||||
))}
|
||||
</div>
|
||||
{props.data &&
|
||||
props.data.map((positionsData: PositionsData, index) => (
|
||||
<div key={index} className='align-center flex w-full px-4 py-2'>
|
||||
{arrayKeys.map((key, index) => {
|
||||
if (index === 0)
|
||||
return (
|
||||
<Text
|
||||
size='xs'
|
||||
key={index}
|
||||
className={classNames(
|
||||
'flex-1 border-l-4 pl-2 text-white/60',
|
||||
positionsData[key].type === 'debt' ? 'border-loss' : 'border-profit',
|
||||
)}
|
||||
>
|
||||
{positionsData[key].amount}
|
||||
</Text>
|
||||
)
|
||||
return (
|
||||
<Text size='xs' key={index} className='flex-1 text-white/60'>
|
||||
{positionsData[key].format && positionsData[key].format === 'number' ? (
|
||||
<FormattedNumber animate {...positionsData[key]} />
|
||||
) : (
|
||||
positionsData[key]?.amount || ''
|
||||
)}
|
||||
</Text>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
))}
|
||||
</>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
@ -4,7 +4,7 @@ type Props = {
|
||||
value: number
|
||||
}
|
||||
|
||||
const ProgressBar = ({ value }: Props) => {
|
||||
export const ProgressBar = ({ value }: Props) => {
|
||||
const percentageValue = `${(value * 100).toFixed(0)}%`
|
||||
|
||||
let bgColorClass = 'bg-green-500'
|
||||
@ -26,5 +26,3 @@ const ProgressBar = ({ value }: Props) => {
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default ProgressBar
|
@ -5,17 +5,12 @@ import React, { useMemo, useState } from 'react'
|
||||
import { NumericFormat } from 'react-number-format'
|
||||
import { toast } from 'react-toastify'
|
||||
|
||||
import Button from 'components/Button'
|
||||
import CircularProgress from 'components/CircularProgress'
|
||||
import ContainerSecondary from 'components/ContainerSecondary'
|
||||
import Slider from 'components/Slider'
|
||||
import useRepayFunds from 'hooks/mutations/useRepayFunds'
|
||||
import useAllBalances from 'hooks/useAllBalances'
|
||||
import useCreditAccountPositions from 'hooks/useCreditAccountPositions'
|
||||
import useTokenPrices from 'hooks/useTokenPrices'
|
||||
import { Button, CircularProgress, ContainerSecondary, Slider } from 'components'
|
||||
import { useRepayFunds } from 'hooks/mutations'
|
||||
import { useAccountDetailsStore } from 'stores'
|
||||
import { formatCurrency } from 'utils/formatters'
|
||||
import { getTokenDecimals, getTokenSymbol } from 'utils/tokens'
|
||||
import { useAllBalances, useCreditAccountPositions, useTokenPrices } from 'hooks/queries'
|
||||
|
||||
// 0.001% buffer / slippage to avoid repay action from not fully repaying the debt amount
|
||||
const REPAY_BUFFER = 1.00001
|
||||
@ -26,7 +21,7 @@ type Props = {
|
||||
tokenDenom: string
|
||||
}
|
||||
|
||||
const RepayModal = ({ show, onClose, tokenDenom }: Props) => {
|
||||
export const RepayModal = ({ show, onClose, tokenDenom }: Props) => {
|
||||
const [amount, setAmount] = useState(0)
|
||||
|
||||
const selectedAccount = useAccountDetailsStore((s) => s.selectedAccount)
|
||||
@ -190,5 +185,3 @@ const RepayModal = ({ show, onClose, tokenDenom }: Props) => {
|
||||
</Transition>
|
||||
)
|
||||
}
|
||||
|
||||
export default RepayModal
|
@ -8,7 +8,7 @@ type Props = {
|
||||
onMaxClick: () => void
|
||||
}
|
||||
|
||||
const Slider = ({ className, value, onChange, onMaxClick }: Props) => {
|
||||
export const Slider = ({ className, value, onChange, onMaxClick }: Props) => {
|
||||
return (
|
||||
<div className={`relative flex flex-1 items-center ${className || ''}`}>
|
||||
<RadixSlider.Root
|
||||
@ -35,5 +35,3 @@ const Slider = ({ className, value, onChange, onMaxClick }: Props) => {
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Slider
|