Add UiLock toggle and themed the stats page
This commit is contained in:
parent
e0370f4b0f
commit
59f1156703
|
@ -1,14 +1,14 @@
|
|||
import { Listbox } from '@headlessui/react'
|
||||
import { ChevronDownIcon, ChevronUpIcon } from '@heroicons/react/solid'
|
||||
|
||||
const Select = ({ value, onChange, options, className = '' }) => {
|
||||
const Select = ({ value, onChange, children, className = '' }) => {
|
||||
return (
|
||||
<div className={`relative`}>
|
||||
<div className={`relative ${className}`}>
|
||||
<Listbox value={value} onChange={onChange}>
|
||||
{({ open }) => (
|
||||
<>
|
||||
<Listbox.Button
|
||||
className={`h-full w-full bg-th-bkg-1 border border-th-fgd-4 rounded focus:outline-none focus:ring-1 focus:ring-th-primary ${className}`}
|
||||
className={`h-full w-full bg-th-bkg-1 border border-th-fgd-4 rounded focus:outline-none focus:ring-1 focus:ring-th-primary`}
|
||||
>
|
||||
<div
|
||||
className={`flex items-center justify-between space-x-4 pl-2 pr-1`}
|
||||
|
@ -26,19 +26,7 @@ const Select = ({ value, onChange, options, className = '' }) => {
|
|||
static
|
||||
className={`z-20 w-full p-1 absolute left-0 mt-1 bg-th-bkg-1 origin-top-left divide-y divide-th-bkg-3 shadow-lg outline-none rounded-md`}
|
||||
>
|
||||
{options.map((option) => (
|
||||
<Listbox.Option key={option} value={option}>
|
||||
{({ selected }) => (
|
||||
<div
|
||||
className={`p-2 hover:bg-th-bkg-3 hover:cursor-pointer tracking-wider ${
|
||||
selected && `text-th-primary`
|
||||
}`}
|
||||
>
|
||||
{option}
|
||||
</div>
|
||||
)}
|
||||
</Listbox.Option>
|
||||
))}
|
||||
{children}
|
||||
</Listbox.Options>
|
||||
) : null}
|
||||
</>
|
||||
|
@ -48,4 +36,22 @@ const Select = ({ value, onChange, options, className = '' }) => {
|
|||
)
|
||||
}
|
||||
|
||||
const Option = ({ key, value, children }) => {
|
||||
return (
|
||||
<Listbox.Option key={key} value={value}>
|
||||
{({ selected }) => (
|
||||
<div
|
||||
className={`p-2 hover:bg-th-bkg-3 hover:cursor-pointer tracking-wider ${
|
||||
selected && `text-th-primary`
|
||||
}`}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
)}
|
||||
</Listbox.Option>
|
||||
)
|
||||
}
|
||||
|
||||
Select.Option = Option
|
||||
|
||||
export default Select
|
||||
|
|
|
@ -4,6 +4,8 @@ import { MenuIcon, XIcon } from '@heroicons/react/outline'
|
|||
import MenuItem from './MenuItem'
|
||||
import useWallet from '../hooks/useWallet'
|
||||
import ThemeSwitch from './ThemeSwitch'
|
||||
import UiLock from './UiLock'
|
||||
import { useRouter } from 'next/router'
|
||||
|
||||
const Code = styled.code`
|
||||
border: 1px solid hsla(0, 0%, 39.2%, 0.2);
|
||||
|
@ -12,6 +14,9 @@ const Code = styled.code`
|
|||
`
|
||||
|
||||
const TopBar = () => {
|
||||
const { asPath } = useRouter()
|
||||
console.log('asPath', asPath)
|
||||
|
||||
const { connected, wallet } = useWallet()
|
||||
const [showMenu, setShowMenu] = useState(false)
|
||||
|
||||
|
@ -39,18 +44,8 @@ const TopBar = () => {
|
|||
</div>
|
||||
<div className={`flex`}>
|
||||
<div className={`flex items-center pr-1`}>
|
||||
{asPath === '/' ? <UiLock className="mr-4" /> : null}
|
||||
<ThemeSwitch />
|
||||
{/*<button
|
||||
type="button"
|
||||
className={`inline-flex items-center justify-center p-2 rounded-md text-black dark:text-white hover:text-gray-400 focus:outline-none`}
|
||||
onClick={() => setTheme(theme === 'dark' ? 'light' : 'dark')}
|
||||
>
|
||||
{theme === 'light' ? (
|
||||
<MoonIcon className={`h-5 w-5`} />
|
||||
) : (
|
||||
<SunIcon className={`h-5 w-5`} />
|
||||
)}
|
||||
</button>*/}
|
||||
<div className={`hidden sm:ml-4 sm:flex sm:items-center`}>
|
||||
<button
|
||||
onClick={handleConnectDisconnect}
|
||||
|
|
|
@ -12,6 +12,7 @@ import MarginBalances from './MarginBalances'
|
|||
import TradeForm from './TradeForm'
|
||||
import UserInfo from './UserInfo'
|
||||
import RecentMarketTrades from './RecentMarketTrades'
|
||||
import useMangoStore from '../stores/useMangoStore'
|
||||
|
||||
const ResponsiveGridLayout = WidthProvider(Responsive)
|
||||
|
||||
|
@ -37,6 +38,8 @@ const layouts = {
|
|||
}
|
||||
|
||||
const TradePageGrid = () => {
|
||||
const { uiLocked } = useMangoStore((s) => s.settings)
|
||||
|
||||
return (
|
||||
<ResponsiveGridLayout
|
||||
className="layout"
|
||||
|
@ -44,7 +47,8 @@ const TradePageGrid = () => {
|
|||
breakpoints={{ xl: 1600, lg: 1200, md: 996, sm: 768, xs: 0 }}
|
||||
cols={{ xl: 5, lg: 3, md: 3, sm: 2, xs: 1 }}
|
||||
rowHeight={15}
|
||||
isDraggable={false}
|
||||
isDraggable={!uiLocked}
|
||||
isResizable={!uiLocked}
|
||||
>
|
||||
<div key="tvChart">
|
||||
<FloatingElement>
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
import { LockClosedIcon, LockOpenIcon } from '@heroicons/react/outline'
|
||||
import useMangoStore from '../stores/useMangoStore'
|
||||
|
||||
const UiLock = ({ className = '' }) => {
|
||||
const set = useMangoStore((s) => s.set)
|
||||
const uiLocked = useMangoStore((s) => s.settings.uiLocked)
|
||||
|
||||
const handleClick = () => {
|
||||
set((state) => {
|
||||
state.settings.uiLocked = !uiLocked
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={`flex relative ${className}`}>
|
||||
<button
|
||||
onClick={handleClick}
|
||||
className="bg-transparent rounded w-5 h-5 hover:text-th-primary focus:outline-none"
|
||||
>
|
||||
{uiLocked ? <LockClosedIcon /> : <LockOpenIcon />}
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default UiLock
|
|
@ -146,8 +146,11 @@ const useHydrateStore = () => {
|
|||
state.fills = loadedFills
|
||||
})
|
||||
}
|
||||
|
||||
fetchFills()
|
||||
try {
|
||||
fetchFills()
|
||||
} catch (err) {
|
||||
console.error('Error fetching fills:', err)
|
||||
}
|
||||
}, _SLOW_REFRESH_INTERVAL)
|
||||
}
|
||||
|
||||
|
|
|
@ -85,7 +85,6 @@ export const useTradeHistory = () => {
|
|||
}, [marginAccount])
|
||||
|
||||
useInterval(() => {
|
||||
console.log('interval', allTrades, tradeHistory)
|
||||
if (marginAccount && tradeHistory.length === 0) {
|
||||
fetchTradeHistory()
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
"@blockworks-foundation/mango-client": "^0.1.10",
|
||||
"@emotion/react": "^11.1.5",
|
||||
"@emotion/styled": "^11.1.5",
|
||||
"@headlessui/react": "^0.3.2",
|
||||
"@headlessui/react": "^0.3.2-d950146",
|
||||
"@heroicons/react": "^1.0.0",
|
||||
"@project-serum/serum": "^0.13.31",
|
||||
"@project-serum/sol-wallet-adapter": "^0.1.8",
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
|
||||
|
||||
import { NextApiRequest, NextApiResponse } from 'next'
|
||||
|
||||
const handler = (req: NextApiRequest, res: NextApiResponse) => {
|
||||
res.status(200).json({ name: 'John Doe' })
|
||||
}
|
||||
|
||||
export default handler
|
135
pages/stats.tsx
135
pages/stats.tsx
|
@ -1,6 +1,5 @@
|
|||
import { useEffect, useState } from 'react'
|
||||
import styled from '@emotion/styled'
|
||||
import { Select } from 'antd'
|
||||
import { LineChart, Line, ReferenceLine, XAxis, YAxis, Tooltip } from 'recharts'
|
||||
import useDimensions from 'react-cool-dimensions'
|
||||
import { IDS, MangoClient } from '@blockworks-foundation/mango-client'
|
||||
|
@ -9,6 +8,7 @@ import { DEFAULT_MANGO_GROUP } from '../utils/mango'
|
|||
import FloatingElement from '../components/FloatingElement'
|
||||
import useConnection from '../hooks/useConnection'
|
||||
import TopBar from '../components/TopBar'
|
||||
import Select from '../components/Select'
|
||||
|
||||
const DECIMALS = {
|
||||
BTC: 4,
|
||||
|
@ -26,13 +26,6 @@ const icons = {
|
|||
WUSDT: '/assets/icons/usdt.svg',
|
||||
}
|
||||
|
||||
const Wrapper = styled.div`
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 16px 16px;
|
||||
`
|
||||
|
||||
const ChartLayover = styled.div`
|
||||
position: absolute;
|
||||
top: 0;
|
||||
|
@ -195,50 +188,76 @@ export default function StatsPage() {
|
|||
)
|
||||
|
||||
return (
|
||||
<Wrapper>
|
||||
<div className={`bg-th-bkg-1 text-th-fgd-1 transition-all `}>
|
||||
<TopBar />
|
||||
<div className="w-2/3 mx-auto">
|
||||
<div className="min-h-screen w-full lg:w-2/3 mx-auto p-1 sm:px-2 sm:py-1 md:px-6 md:py-1">
|
||||
<FloatingElement>
|
||||
<>
|
||||
<div className="text-center">
|
||||
<h1 className={`text-white text-lg`}>Mango Stats</h1>
|
||||
</div>
|
||||
<div className="flex justify-between divide">
|
||||
<div>Asset</div>
|
||||
<div>Total Deposits</div>
|
||||
<div>Total Borrows</div>
|
||||
<div>Deposit Interest</div>
|
||||
<div>Borrow Interest</div>
|
||||
<div>Utilization</div>
|
||||
</div>
|
||||
<div className="divide-y divide-gray-600">
|
||||
{latestStats.map((stat) => (
|
||||
<div key={stat.symbol} className="flex justify-between py-4">
|
||||
<div className="flex items-center">
|
||||
<img src={icons[stat.symbol]} alt={icons[stat.symbol]} />
|
||||
<button
|
||||
onClick={() => setSelectedAsset(stat.symbol)}
|
||||
className="text-th-primary cursor-pointer ml-2"
|
||||
>
|
||||
<div style={{ width: '100%' }}>{stat.symbol}</div>
|
||||
</button>
|
||||
</div>
|
||||
<div>{stat.totalDeposits.toFixed(DECIMALS[stat.symbol])}</div>
|
||||
<div>{stat.totalBorrows.toFixed(DECIMALS[stat.symbol])}</div>
|
||||
<div>{stat.depositInterest.toFixed(2)}%</div>
|
||||
<div>{stat.borrowInterest.toFixed(2)}%</div>
|
||||
<div>{(parseFloat(stat.utilization) * 100).toFixed(2)}%</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</>
|
||||
<div className="text-center">
|
||||
<h1 className={`text-white text-3xl`}>Mango Stats</h1>
|
||||
</div>
|
||||
<div className="hidden md:flex md:flex-col min-w-full">
|
||||
<table className="min-w-full">
|
||||
<thead className="">
|
||||
<tr>
|
||||
<th scope="col" className="text-left py-4">
|
||||
Asset
|
||||
</th>
|
||||
<th scope="col" className="text-left py-4">
|
||||
Total Deposits
|
||||
</th>
|
||||
<th scope="col" className="text-left py-4">
|
||||
Total Borrows
|
||||
</th>
|
||||
<th scope="col" className="text-left py-4">
|
||||
Deposit Interest
|
||||
</th>
|
||||
<th scope="col" className="text-left py-4">
|
||||
Borrow Interest
|
||||
</th>
|
||||
<th scope="col" className="text-left py-4">
|
||||
Utilization
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="divide-y divide-gray-600">
|
||||
{latestStats.map((stat) => (
|
||||
<tr key={stat.symbol}>
|
||||
<td className="flex items-center text-left py-4">
|
||||
<img src={icons[stat.symbol]} alt={icons[stat.symbol]} />
|
||||
<button
|
||||
onClick={() => setSelectedAsset(stat.symbol)}
|
||||
className="text-th-primary cursor-pointer ml-2"
|
||||
>
|
||||
<div style={{ width: '100%' }}>{stat.symbol}</div>
|
||||
</button>
|
||||
</td>
|
||||
<td className="text-left py-4">
|
||||
{stat.totalDeposits.toFixed(DECIMALS[stat.symbol])}
|
||||
</td>
|
||||
<td className="text-left py-4">
|
||||
{stat.totalBorrows.toFixed(DECIMALS[stat.symbol])}
|
||||
</td>
|
||||
<td className="text-left py-4">
|
||||
{stat.depositInterest.toFixed(2)}%
|
||||
</td>
|
||||
<td className="text-left py-4">
|
||||
{stat.borrowInterest.toFixed(2)}%
|
||||
</td>
|
||||
<td className="text-left py-4">
|
||||
{(parseFloat(stat.utilization) * 100).toFixed(2)}%
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</FloatingElement>
|
||||
{selectedAsset ? (
|
||||
<FloatingElement shrink>
|
||||
<div className="flex justify-center text-lg">
|
||||
<div className="flex justify-center text-2xl">
|
||||
<span className={`text-white`}>Historical</span>
|
||||
<Select
|
||||
style={{ margin: '0px 8px', fontSize: 16 }}
|
||||
className="mx-4 text-lg"
|
||||
value={selectedAsset}
|
||||
onChange={(val) => setSelectedAsset(val)}
|
||||
>
|
||||
|
@ -251,8 +270,11 @@ export default function StatsPage() {
|
|||
<span className={`text-white`}>Stats</span>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-row mt-2">
|
||||
<div className="relative w-1/2" style={{ height: '300px' }}>
|
||||
<div className="flex flex-col md:flex-row mt-2">
|
||||
<div
|
||||
className="relative my-2 md:w-1/2"
|
||||
style={{ height: '300px' }}
|
||||
>
|
||||
<StatsChart
|
||||
title="Total Deposits"
|
||||
xAxis="time"
|
||||
|
@ -261,7 +283,10 @@ export default function StatsPage() {
|
|||
labelFormat={(x) => x.toFixed(DECIMALS[selectedAsset])}
|
||||
/>
|
||||
</div>
|
||||
<div className="relative w-1/2" style={{ height: '300px' }}>
|
||||
<div
|
||||
className="relative my-2 md:w-1/2"
|
||||
style={{ height: '300px' }}
|
||||
>
|
||||
<StatsChart
|
||||
title="Total Borrows"
|
||||
xAxis="time"
|
||||
|
@ -271,8 +296,11 @@ export default function StatsPage() {
|
|||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-row" style={{ margin: '50px 0' }}>
|
||||
<div className="relative w-1/2" style={{ height: '300px' }}>
|
||||
<div className="flex flex-col md:flex-row">
|
||||
<div
|
||||
className="relative my-2 md:w-1/2"
|
||||
style={{ height: '300px' }}
|
||||
>
|
||||
<StatsChart
|
||||
title="Deposit Interest"
|
||||
xAxis="time"
|
||||
|
@ -281,7 +309,10 @@ export default function StatsPage() {
|
|||
labelFormat={(x) => `${(x * 100).toFixed(5)}%`}
|
||||
/>
|
||||
</div>
|
||||
<div className="relative w-1/2" style={{ height: '300px' }}>
|
||||
<div
|
||||
className="relative my-2 md:w-1/2"
|
||||
style={{ height: '300px' }}
|
||||
>
|
||||
<StatsChart
|
||||
title="Borrow Interest"
|
||||
xAxis="time"
|
||||
|
@ -294,6 +325,6 @@ export default function StatsPage() {
|
|||
</FloatingElement>
|
||||
) : null}
|
||||
</div>
|
||||
</Wrapper>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -91,6 +91,9 @@ interface MangoStore extends State {
|
|||
current: Wallet
|
||||
balances: Array<{ account: any; publicKey: PublicKey }>
|
||||
}
|
||||
settings: {
|
||||
uiLocked: boolean
|
||||
}
|
||||
set: (x: any) => void
|
||||
actions: any
|
||||
}
|
||||
|
@ -145,6 +148,9 @@ const useMangoStore = create<MangoStore>((set, get) => ({
|
|||
current: null,
|
||||
balances: [],
|
||||
},
|
||||
settings: {
|
||||
uiLocked: true,
|
||||
},
|
||||
set: (fn) => set(produce(fn)),
|
||||
actions: {
|
||||
async fetchWalletBalances() {
|
||||
|
|
|
@ -12,8 +12,7 @@
|
|||
"moduleResolution": "node",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"jsx": "preserve",
|
||||
"jsxImportSource": "@emotion/react"
|
||||
"jsx": "preserve"
|
||||
},
|
||||
"exclude": ["node_modules", ".next", "out", "public/datafeeds"],
|
||||
"include": [
|
||||
|
|
|
@ -1024,9 +1024,10 @@
|
|||
version "9.1.1"
|
||||
resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-9.1.1.tgz#9daf5745156fd84b8e9889a2dc721f0c58e894aa"
|
||||
|
||||
"@headlessui/react@^0.3.2":
|
||||
version "0.3.2"
|
||||
resolved "https://registry.yarnpkg.com/@headlessui/react/-/react-0.3.2.tgz#fa8600fa669fe704b84e9256855fb39092b6e233"
|
||||
"@headlessui/react@^0.3.2-d950146":
|
||||
version "0.3.2-d950146"
|
||||
resolved "https://registry.yarnpkg.com/@headlessui/react/-/react-0.3.2-d950146.tgz#00cad543d2803654a9361be2add89f2b663b4c8d"
|
||||
integrity sha512-MblPc6jWSTdWiIe7VLBXbpGwhMOoA3ubUdDTqavQ978WNCI70lOBs2k1oi4mU5znRvmEPc9YNAiHIibyxq+4nQ==
|
||||
|
||||
"@heroicons/react@^1.0.0":
|
||||
version "1.0.0"
|
||||
|
|
Loading…
Reference in New Issue