getting color graphs
This commit is contained in:
parent
c15d5e9e91
commit
f916f2bd74
|
@ -15,20 +15,90 @@ export const PortfolioValueGraph = memo(function PortfolioValueGraph(props: {
|
||||||
const { portfolioHistory, height, includeTime, mode } = props
|
const { portfolioHistory, height, includeTime, mode } = props
|
||||||
const { width } = useWindowSize()
|
const { width } = useWindowSize()
|
||||||
|
|
||||||
const points = portfolioHistory.map((p) => {
|
function getPoints(line: 'value' | 'posProfit' | 'negProfit') {
|
||||||
|
let points = portfolioHistory.map((p) => {
|
||||||
const { timestamp, balance, investmentValue, totalDeposits } = p
|
const { timestamp, balance, investmentValue, totalDeposits } = p
|
||||||
const value = balance + investmentValue
|
const value = balance + investmentValue
|
||||||
const profit = value - totalDeposits
|
const profit = value - totalDeposits
|
||||||
|
let posProfit = null
|
||||||
|
let negProfit = null
|
||||||
|
|
||||||
|
// const profit = value - totalDeposits
|
||||||
|
if (profit < 0) {
|
||||||
|
negProfit = profit
|
||||||
|
} else {
|
||||||
|
posProfit = profit
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
x: new Date(timestamp),
|
x: new Date(timestamp),
|
||||||
y: mode === 'value' ? value : profit,
|
y:
|
||||||
|
line === 'value'
|
||||||
|
? value
|
||||||
|
: line === 'posProfit'
|
||||||
|
? posProfit
|
||||||
|
: negProfit,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
const data = [{ id: 'Value', data: points, color: '#11b981' }]
|
return points
|
||||||
|
}
|
||||||
|
|
||||||
|
let data
|
||||||
|
|
||||||
|
if (mode === 'value') {
|
||||||
|
data = [{ id: 'value', data: getPoints('value'), color: '#4f46e5' }]
|
||||||
|
} else {
|
||||||
|
data = [
|
||||||
|
{
|
||||||
|
id: 'negProfit',
|
||||||
|
data: getPoints('negProfit'),
|
||||||
|
color: '#dc2626',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'posProfit',
|
||||||
|
data: getPoints('posProfit'),
|
||||||
|
color: '#14b8a6',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
const firstPoints = data[0].data
|
||||||
const numXTickValues = !width || width < 800 ? 2 : 5
|
const numXTickValues = !width || width < 800 ? 2 : 5
|
||||||
const numYTickValues = 4
|
const numYTickValues = 4
|
||||||
const endDate = last(points)?.x
|
const endDate = last(firstPoints)?.x
|
||||||
|
|
||||||
|
const firstPointsY = firstPoints
|
||||||
|
.filter((p) => {
|
||||||
|
return p.y !== null
|
||||||
|
})
|
||||||
|
.map((p) => p.y)
|
||||||
|
// console.log(firstPointsY)
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
'MIN: ',
|
||||||
|
mode === 'value'
|
||||||
|
? Math.min(...firstPointsY)
|
||||||
|
: Math.min(
|
||||||
|
...firstPointsY,
|
||||||
|
...data[1].data
|
||||||
|
.filter((p) => {
|
||||||
|
return p.y !== null
|
||||||
|
})
|
||||||
|
.map((p) => p.y)
|
||||||
|
),
|
||||||
|
|
||||||
|
'MAX: ',
|
||||||
|
mode === 'value'
|
||||||
|
? Math.max(...firstPointsY)
|
||||||
|
: Math.max(
|
||||||
|
...firstPointsY,
|
||||||
|
...data[1].data
|
||||||
|
.filter((p) => {
|
||||||
|
return p.y !== null
|
||||||
|
})
|
||||||
|
.map((p) => p.y)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className="w-full overflow-hidden"
|
className="w-full overflow-hidden"
|
||||||
|
@ -39,16 +109,37 @@ export const PortfolioValueGraph = memo(function PortfolioValueGraph(props: {
|
||||||
margin={{ top: 20, right: 28, bottom: 22, left: 60 }}
|
margin={{ top: 20, right: 28, bottom: 22, left: 60 }}
|
||||||
xScale={{
|
xScale={{
|
||||||
type: 'time',
|
type: 'time',
|
||||||
min: points[0]?.x,
|
min: firstPoints[0]?.x,
|
||||||
max: endDate,
|
max: endDate,
|
||||||
}}
|
}}
|
||||||
yScale={{
|
yScale={{
|
||||||
type: 'linear',
|
type: 'linear',
|
||||||
stacked: false,
|
stacked: false,
|
||||||
min: Math.min(...points.map((p) => p.y)),
|
min:
|
||||||
|
mode === 'value'
|
||||||
|
? Math.min(...firstPointsY)
|
||||||
|
: Math.min(
|
||||||
|
...firstPointsY,
|
||||||
|
...data[1].data
|
||||||
|
.filter((p) => {
|
||||||
|
return p.y !== null
|
||||||
|
})
|
||||||
|
.map((p) => p.y)
|
||||||
|
),
|
||||||
|
max:
|
||||||
|
mode === 'value'
|
||||||
|
? Math.max(...firstPointsY)
|
||||||
|
: Math.max(
|
||||||
|
...firstPointsY,
|
||||||
|
...data[1].data
|
||||||
|
.filter((p) => {
|
||||||
|
return p.y !== null
|
||||||
|
})
|
||||||
|
.map((p) => p.y)
|
||||||
|
),
|
||||||
}}
|
}}
|
||||||
gridYValues={numYTickValues}
|
gridYValues={numYTickValues}
|
||||||
curve="stepAfter"
|
curve="linear"
|
||||||
enablePoints={false}
|
enablePoints={false}
|
||||||
colors={{ datum: 'color' }}
|
colors={{ datum: 'color' }}
|
||||||
axisBottom={{
|
axisBottom={{
|
||||||
|
@ -56,7 +147,7 @@ export const PortfolioValueGraph = memo(function PortfolioValueGraph(props: {
|
||||||
format: (time) => formatTime(+time, !!includeTime),
|
format: (time) => formatTime(+time, !!includeTime),
|
||||||
}}
|
}}
|
||||||
pointBorderColor="#fff"
|
pointBorderColor="#fff"
|
||||||
pointSize={points.length > 100 ? 0 : 6}
|
pointSize={firstPoints.length > 100 ? 0 : 6}
|
||||||
axisLeft={{
|
axisLeft={{
|
||||||
tickValues: numYTickValues,
|
tickValues: numYTickValues,
|
||||||
format: (value) => formatMoney(value),
|
format: (value) => formatMoney(value),
|
||||||
|
@ -66,6 +157,21 @@ export const PortfolioValueGraph = memo(function PortfolioValueGraph(props: {
|
||||||
enableSlices="x"
|
enableSlices="x"
|
||||||
animate={false}
|
animate={false}
|
||||||
yFormat={(value) => formatMoney(+value)}
|
yFormat={(value) => formatMoney(+value)}
|
||||||
|
// defs={[
|
||||||
|
// {
|
||||||
|
// id: 'purpleGradient',
|
||||||
|
// type: 'linearGradient',
|
||||||
|
// colors: [
|
||||||
|
// { offset: 0, color: '#7c3aed' },
|
||||||
|
// {
|
||||||
|
// offset: 100,
|
||||||
|
// color: '#inherit',
|
||||||
|
// opacity: 0,
|
||||||
|
// },
|
||||||
|
// ],
|
||||||
|
// },
|
||||||
|
// ]}
|
||||||
|
enableArea={true}
|
||||||
></ResponsiveLine>
|
></ResponsiveLine>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|
|
@ -4,6 +4,7 @@ import { last } from 'lodash'
|
||||||
import { memo, useRef, useState } from 'react'
|
import { memo, useRef, useState } from 'react'
|
||||||
import { usePortfolioHistory } from 'web/hooks/use-portfolio-history'
|
import { usePortfolioHistory } from 'web/hooks/use-portfolio-history'
|
||||||
import { Period } from 'web/lib/firebase/users'
|
import { Period } from 'web/lib/firebase/users'
|
||||||
|
import { PillButton } from '../buttons/pill-button'
|
||||||
import { Col } from '../layout/col'
|
import { Col } from '../layout/col'
|
||||||
import { Row } from '../layout/row'
|
import { Row } from '../layout/row'
|
||||||
import { Spacer } from '../layout/spacer'
|
import { Spacer } from '../layout/spacer'
|
||||||
|
@ -15,6 +16,7 @@ export const PortfolioValueSection = memo(
|
||||||
|
|
||||||
const [portfolioPeriod, setPortfolioPeriod] = useState<Period>('weekly')
|
const [portfolioPeriod, setPortfolioPeriod] = useState<Period>('weekly')
|
||||||
const portfolioHistory = usePortfolioHistory(userId, portfolioPeriod)
|
const portfolioHistory = usePortfolioHistory(userId, portfolioPeriod)
|
||||||
|
const [graphMode, setGraphMode] = useState<'profit' | 'value'>('profit')
|
||||||
|
|
||||||
// Remember the last defined portfolio history.
|
// Remember the last defined portfolio history.
|
||||||
const portfolioRef = useRef(portfolioHistory)
|
const portfolioRef = useRef(portfolioHistory)
|
||||||
|
@ -32,38 +34,34 @@ export const PortfolioValueSection = memo(
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Row className="gap-8">
|
<Row className="justify-between">
|
||||||
<Col className="flex-1 justify-center">
|
<Row className="gap-4 sm:gap-8 ">
|
||||||
<div className="text-sm text-gray-500">Profit</div>
|
<Col>
|
||||||
<div className="text-lg">{formatMoney(totalProfit)}</div>
|
<div className="text-greyscale-4 text-xs sm:text-sm">
|
||||||
|
Portfolio value
|
||||||
|
</div>
|
||||||
|
<div className="text-lg text-indigo-600 sm:text-xl">
|
||||||
|
{formatMoney(totalValue)}
|
||||||
|
</div>
|
||||||
</Col>
|
</Col>
|
||||||
{/* <select
|
<Col>
|
||||||
className="select select-bordered self-start"
|
<div className="text-greyscale-4 text-xs sm:text-sm">Profit</div>
|
||||||
value={portfolioPeriod}
|
<div
|
||||||
onChange={(e) => {
|
className={clsx(
|
||||||
setPortfolioPeriod(e.target.value as Period)
|
totalProfit > 0 ? 'text-green-500' : 'text-red-600',
|
||||||
}}
|
'text-lg sm:text-xl'
|
||||||
|
)}
|
||||||
>
|
>
|
||||||
<option value="allTime">All time</option>
|
{formatMoney(totalProfit)}
|
||||||
<option value="monthly">Last Month</option>
|
</div>
|
||||||
<option value="weekly">Last 7d</option>
|
</Col>
|
||||||
<option value="daily">Last 24h</option>
|
</Row>
|
||||||
</select> */}
|
<GraphToggle setGraphMode={setGraphMode} graphMode={graphMode} />
|
||||||
</Row>
|
</Row>
|
||||||
<PortfolioValueGraph
|
<PortfolioValueGraph
|
||||||
portfolioHistory={currPortfolioHistory}
|
portfolioHistory={currPortfolioHistory}
|
||||||
includeTime={portfolioPeriod == 'daily'}
|
includeTime={true}
|
||||||
mode="profit"
|
mode={graphMode}
|
||||||
/>
|
|
||||||
<Spacer h={8} />
|
|
||||||
<Col className="flex-1 justify-center">
|
|
||||||
<div className="text-sm text-gray-500">Portfolio value</div>
|
|
||||||
<div className="text-lg">{formatMoney(totalValue)}</div>
|
|
||||||
</Col>
|
|
||||||
<PortfolioValueGraph
|
|
||||||
portfolioHistory={currPortfolioHistory}
|
|
||||||
includeTime={portfolioPeriod == 'daily'}
|
|
||||||
mode="value"
|
|
||||||
/>
|
/>
|
||||||
<PortfolioPeriodSelection
|
<PortfolioPeriodSelection
|
||||||
portfolioPeriod={portfolioPeriod}
|
portfolioPeriod={portfolioPeriod}
|
||||||
|
@ -85,7 +83,7 @@ export function PortfolioPeriodSelection(props: {
|
||||||
const { setPortfolioPeriod, portfolioPeriod, className, selectClassName } =
|
const { setPortfolioPeriod, portfolioPeriod, className, selectClassName } =
|
||||||
props
|
props
|
||||||
return (
|
return (
|
||||||
<Row className={className}>
|
<Row className={clsx(className, 'text-greyscale-4')}>
|
||||||
<button
|
<button
|
||||||
className={clsx(portfolioPeriod === 'daily' ? selectClassName : '')}
|
className={clsx(portfolioPeriod === 'daily' ? selectClassName : '')}
|
||||||
onClick={() => setPortfolioPeriod('daily' as Period)}
|
onClick={() => setPortfolioPeriod('daily' as Period)}
|
||||||
|
@ -113,3 +111,34 @@ export function PortfolioPeriodSelection(props: {
|
||||||
</Row>
|
</Row>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function GraphToggle(props: {
|
||||||
|
setGraphMode: (mode: 'profit' | 'value') => void
|
||||||
|
graphMode: string
|
||||||
|
}) {
|
||||||
|
const { setGraphMode, graphMode } = props
|
||||||
|
return (
|
||||||
|
<Row className="relative mt-1 ml-1 items-center gap-1.5 sm:ml-0 sm:gap-2">
|
||||||
|
<PillButton
|
||||||
|
selected={graphMode === 'value'}
|
||||||
|
onSelect={() => {
|
||||||
|
setGraphMode('value')
|
||||||
|
}}
|
||||||
|
xs={true}
|
||||||
|
className="z-50"
|
||||||
|
>
|
||||||
|
Value
|
||||||
|
</PillButton>
|
||||||
|
<PillButton
|
||||||
|
selected={graphMode === 'profit'}
|
||||||
|
onSelect={() => {
|
||||||
|
setGraphMode('profit')
|
||||||
|
}}
|
||||||
|
xs={true}
|
||||||
|
className="z-50"
|
||||||
|
>
|
||||||
|
Profit
|
||||||
|
</PillButton>
|
||||||
|
</Row>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user