-
Notifications
You must be signed in to change notification settings - Fork 22
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1005 from bancorprotocol/simulator-summary
Simulator summary
- Loading branch information
Showing
17 changed files
with
596 additions
and
43 deletions.
There are no files selected for viewing
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
import { ReactComponent as CalendarIcon } from 'assets/icons/calendar.svg'; | ||
import { SimulatorPageTabs } from './SimulatorPageTabs'; | ||
import { SimulatorDownloadMenu } from './SimulatorDownload'; | ||
import { SimulatorReturn } from 'libs/queries'; | ||
|
||
interface Props extends Pick<SimulatorReturn, 'data'> { | ||
showSummary: boolean; | ||
setShowSummary: React.Dispatch<React.SetStateAction<boolean>>; | ||
} | ||
|
||
export const SimulatorChartHeader = ({ | ||
showSummary, | ||
setShowSummary, | ||
data, | ||
}: Props) => { | ||
const startTimestamp = data![0].date * 1e3; // ms | ||
const endTimestamp = data![data.length - 1].date * 1e3; // ms | ||
|
||
const dateFormatOptions: Intl.DateTimeFormatOptions = { | ||
year: 'numeric', | ||
month: 'long', | ||
day: '2-digit', | ||
}; | ||
|
||
const startDate = new Intl.DateTimeFormat('en-US', dateFormatOptions).format( | ||
startTimestamp | ||
); | ||
const endDate = new Intl.DateTimeFormat('en-US', dateFormatOptions).format( | ||
endTimestamp | ||
); | ||
|
||
return ( | ||
<section className="flex flex-wrap items-center justify-evenly gap-8 py-8 px-24 md:justify-between"> | ||
<article className="flex items-center gap-8"> | ||
<span className="h-24 w-24 items-center justify-center rounded-[12px] bg-white/10"> | ||
<CalendarIcon className="h-12 w-12" /> | ||
</span> | ||
<span className="justify-self-end text-14 text-white/80"> | ||
{startDate} – {endDate} | ||
</span> | ||
</article> | ||
<article className="flex items-center gap-8"> | ||
<SimulatorPageTabs | ||
setShowSummary={setShowSummary} | ||
showSummary={showSummary} | ||
/> | ||
<SimulatorDownloadMenu data={data} /> | ||
</article> | ||
</section> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
import { cn } from 'utils/helpers'; | ||
import { ReactComponent as IconLog } from 'assets/icons/page.svg'; | ||
import { ReactComponent as IconAnimation } from 'assets/icons/movie.svg'; | ||
import { ReactComponent as IconSummary } from 'assets/icons/image.svg'; | ||
import { ReactComponent as IconDownload } from 'assets/icons/download.svg'; | ||
import { DropdownMenu } from 'components/common/dropdownMenu'; | ||
import { useState } from 'react'; | ||
import { buttonStyles } from 'components/common/button/buttonStyles'; | ||
import { SimulatorReturn } from 'libs/queries'; | ||
import { CsvDataService } from 'libs/csv'; | ||
|
||
interface Props extends Pick<SimulatorReturn, 'data'> {} | ||
|
||
export const SimulatorDownloadMenu = ({ data }: Props) => { | ||
const [isOpen, setIsOpen] = useState(false); | ||
|
||
const items = [ | ||
{ | ||
id: 'summaryView', | ||
title: 'Summary View', | ||
subTitle: 'JPEG', | ||
action: () => console.log('Summary View'), | ||
icon: <IconSummary className="h-20 w-20" />, | ||
}, | ||
{ | ||
id: 'animation' as const, | ||
title: 'Animation', | ||
subTitle: 'GIF', | ||
action: () => console.log('Animation'), | ||
icon: <IconAnimation className="h-20 w-20" />, | ||
}, | ||
{ | ||
id: 'simulationLog' as const, | ||
title: 'Simulation Log', | ||
subTitle: 'CSV', | ||
action: () => { | ||
const csvOutput = data.map((item) => { | ||
return { | ||
...item, | ||
date: new Date(item.date * 1e3), | ||
}; | ||
}); | ||
CsvDataService.exportToCsv('data.csv', csvOutput); | ||
}, | ||
icon: <IconLog className="h-20 w-20" />, | ||
}, | ||
]; | ||
|
||
return ( | ||
<DropdownMenu | ||
isOpen={isOpen} | ||
setIsOpen={setIsOpen} | ||
placement="bottom-end" | ||
className="rounded-[10px] !border-0 p-8 text-white" | ||
aria-expanded={isOpen} | ||
button={(attr) => ( | ||
<button | ||
{...attr} | ||
className={cn( | ||
buttonStyles({ variant: 'black' }), | ||
'relative h-40 w-40 border-silver !p-0' | ||
)} | ||
onClick={(e) => { | ||
setIsOpen(true); | ||
attr.onClick(e); | ||
}} | ||
aria-label="Download Simulation" | ||
> | ||
<span className="flex h-36 w-36 items-center justify-center"> | ||
<IconDownload className="h-18 w-18" /> | ||
</span> | ||
</button> | ||
)} | ||
> | ||
{items?.map(({ id, action, title, subTitle, icon }, index) => { | ||
return ( | ||
<div key={`${index}_${id}`} className="border-grey5"> | ||
<button | ||
role="menuitem" | ||
aria-labelledby="optionTitle" | ||
className="hover:bg-body w-full rounded-6 p-8 text-left" | ||
onClick={() => { | ||
action(); | ||
setIsOpen(false); | ||
}} | ||
> | ||
<div className="flex items-center gap-y-8"> | ||
{icon} | ||
<span id="optionTitle" className="mx-8 text-14 font-weight-500"> | ||
{title} | ||
</span> | ||
<span className="text-14 font-weight-400 text-white/40"> | ||
{subTitle} | ||
</span> | ||
</div> | ||
</button> | ||
</div> | ||
); | ||
})} | ||
</DropdownMenu> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
import { Pathnames, PathParams } from 'libs/routing'; | ||
import { cn } from 'utils/helpers'; | ||
import { ReactComponent as IconAnimation } from 'assets/icons/movie.svg'; | ||
import { ReactComponent as IconSummary } from 'assets/icons/image.svg'; | ||
|
||
export interface StrategyTab { | ||
label: string; | ||
href: Pathnames; | ||
params?: PathParams; | ||
icon: JSX.Element; | ||
badge?: number; | ||
} | ||
|
||
interface Props { | ||
showSummary: boolean; | ||
setShowSummary: React.Dispatch<React.SetStateAction<boolean>>; | ||
} | ||
|
||
export const SimulatorPageTabs = ({ showSummary, setShowSummary }: Props) => { | ||
const tabs = [ | ||
{ | ||
label: 'Animation', | ||
icon: <IconAnimation className="h-18 w-18" />, | ||
isActive: () => !showSummary, | ||
click: () => setShowSummary(false), | ||
}, | ||
{ | ||
label: 'Summary', | ||
icon: <IconSummary className="h-18 w-18" />, | ||
isActive: () => showSummary, | ||
click: () => setShowSummary(true), | ||
}, | ||
]; | ||
|
||
return ( | ||
<nav | ||
aria-label="Simulation Tabs" | ||
className="max-w-40 flex h-40 w-full gap-2 rounded-full border-2 border-silver p-4 text-14 md:w-auto" | ||
> | ||
{tabs.map(({ label, icon, isActive, click }) => { | ||
const active = isActive(); | ||
return ( | ||
<button | ||
key={label} | ||
onClick={click} | ||
className={cn( | ||
'flex w-full items-center justify-center gap-4 rounded-full py-5 px-16', | ||
active ? 'bg-white/10' : 'bg-transparent text-white/60' | ||
)} | ||
> | ||
{icon} | ||
<span className="text-14 font-weight-500">{label}</span> | ||
</button> | ||
); | ||
})} | ||
</nav> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
import { Link, useSearch } from 'libs/routing'; | ||
import { buttonStyles } from 'components/common/button/buttonStyles'; | ||
import { SimulatorSummaryGains } from './SimulatorSummaryGains'; | ||
import { SimulatorSummaryRoi } from './SimulatorSummaryRoi'; | ||
import { SimulatorSummaryTokens } from './SimulatorTokens'; | ||
import { SimulatorSummaryTable } from './SimulatorSummaryTable'; | ||
import { useTokens } from 'hooks/useTokens'; | ||
|
||
interface Props { | ||
roi: number; | ||
gains: number; | ||
isLoading: boolean; | ||
} | ||
|
||
export const SimulatorSummary = ({ roi, gains, isLoading }: Props) => { | ||
const search = useSearch({ from: '/simulator/result' }); | ||
|
||
const portfolioGains = isLoading ? 0.0 : gains; | ||
const portfolioRoi = isLoading ? 0.0 : roi; | ||
|
||
const { getTokenById } = useTokens(); | ||
const baseToken = getTokenById(search.baseToken); | ||
const quoteToken = getTokenById(search.quoteToken); | ||
|
||
const summaryData = { | ||
buyMin: search.buyMin, | ||
buyMax: search.buyMax, | ||
sellMin: search.sellMin, | ||
sellMax: search.sellMax, | ||
baseBudget: search.baseBudget, | ||
quoteBudget: search.quoteBudget, | ||
}; | ||
|
||
const strategyType = 'recurring'; | ||
|
||
return ( | ||
<header className="my-8 flex flex-wrap gap-8"> | ||
<section className="flex flex-grow flex-wrap items-center justify-evenly gap-8 rounded-10 bg-black p-16 md:justify-between"> | ||
<SimulatorSummaryTokens | ||
baseToken={baseToken!} | ||
quoteToken={quoteToken!} | ||
strategyType={strategyType} | ||
/> | ||
<SimulatorSummaryTable | ||
summaryData={summaryData} | ||
baseToken={baseToken!} | ||
quoteToken={quoteToken!} | ||
/> | ||
</section> | ||
<section className="flex flex-grow flex-wrap items-center justify-evenly gap-8 rounded-10 bg-black p-16 md:justify-between"> | ||
<SimulatorSummaryGains | ||
portfolioGains={portfolioGains} | ||
quoteToken={quoteToken!} | ||
/> | ||
<SimulatorSummaryRoi portfolioRoi={portfolioRoi} /> | ||
<Link | ||
to="/strategies/create" | ||
search={{ | ||
base: baseToken!.address, | ||
quote: quoteToken!.address, | ||
strategyType: | ||
strategyType === 'recurring' ? 'recurring' : undefined, | ||
strategySettings: | ||
strategyType === 'recurring' ? 'range' : 'overlapping', | ||
}} | ||
className={buttonStyles({ variant: 'success', size: 'md' })} | ||
> | ||
Create strategy | ||
</Link> | ||
</section> | ||
</header> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
import { Tooltip } from 'components/common/tooltip/Tooltip'; | ||
import { ReactComponent as IconTooltip } from 'assets/icons/tooltip.svg'; | ||
import { cn, prettifyNumber } from 'utils/helpers'; | ||
import { FC } from 'react'; | ||
import { Token } from 'libs/tokens'; | ||
import { useFiatCurrency } from 'hooks/useFiatCurrency'; | ||
|
||
interface Props { | ||
quoteToken: Token; | ||
portfolioGains: number; | ||
} | ||
|
||
export const SimulatorSummaryGains = ({ | ||
portfolioGains, | ||
quoteToken, | ||
}: Props) => { | ||
const quoteFiat = useFiatCurrency(quoteToken); | ||
const budgetFormatted = prettifyNumber(portfolioGains, { | ||
currentCurrency: quoteFiat.selectedFiatCurrency, | ||
}); | ||
|
||
return ( | ||
<article className={cn('flex flex-col rounded-8')}> | ||
<Tooltip element={<TooltipContent />}> | ||
<h4 className="text-secondary flex items-center gap-4 font-mono !text-12"> | ||
Estimated Gains | ||
<IconTooltip className="h-10 w-10" /> | ||
</h4> | ||
</Tooltip> | ||
<p className={`text-24 font-weight-500`}>{budgetFormatted}</p> | ||
</article> | ||
); | ||
}; | ||
|
||
const TooltipContent: FC<{}> = () => ( | ||
<> | ||
<span className="align-middle">TBD. </span> | ||
</> | ||
); |
Oops, something went wrong.