Fix: make percentage chart work properly
Fix: code review Fix: rework components according to PR requested changes Fix: fix repeated code buildRatesNoCommission Also renames id to deviceId in transactions quer Fix: pr requested changes Chore: move inline styles to classes Chore: remove comment Fix: bad equality !process.env.NODE_ENV === 'production'
This commit is contained in:
parent
5572fb0eb1
commit
ae7eaca10c
43 changed files with 818 additions and 1578 deletions
|
|
@ -1,10 +1,11 @@
|
|||
import { useQuery } from '@apollo/react-hooks'
|
||||
import Grid from '@material-ui/core/Grid'
|
||||
import { makeStyles } from '@material-ui/core/styles'
|
||||
import BigNumber from 'bignumber.js'
|
||||
import gql from 'graphql-tag'
|
||||
import moment from 'moment'
|
||||
import * as R from 'ramda'
|
||||
import React, { useState, useEffect } from 'react'
|
||||
import React, { useState } from 'react'
|
||||
|
||||
import { Label2 } from 'src/components/typography/index'
|
||||
import { ReactComponent as TriangleDown } from 'src/styling/icons/arrow/triangle_down.svg'
|
||||
|
|
@ -18,18 +19,31 @@ import InfoWithLabel from './InfoWithLabel'
|
|||
import Nav from './Nav'
|
||||
import styles from './SystemPerformance.styles'
|
||||
|
||||
const isNotProp = R.curry(R.compose(R.isNil, R.prop))
|
||||
BigNumber.config({ ROUNDING_MODE: BigNumber.ROUND_HALF_UP })
|
||||
|
||||
const getFiats = R.map(R.prop('fiat'))
|
||||
const getProps = propName => R.map(R.prop(propName))
|
||||
const useStyles = makeStyles(styles)
|
||||
const mapToFee = R.map(R.prop('cashInFee'))
|
||||
|
||||
const getDateSecondsAgo = (seconds = 0, startDate = null) => {
|
||||
if (startDate) {
|
||||
return moment(startDate).subtract(seconds, 'second')
|
||||
}
|
||||
return moment().subtract(seconds, 'second')
|
||||
const date = startDate ? moment(startDate) : moment()
|
||||
return date.subtract(seconds, 'second')
|
||||
}
|
||||
|
||||
// const now = moment()
|
||||
const ranges = {
|
||||
Day: {
|
||||
left: getDateSecondsAgo(2 * 24 * 3600, moment()),
|
||||
right: getDateSecondsAgo(24 * 3600, moment())
|
||||
},
|
||||
Week: {
|
||||
left: getDateSecondsAgo(14 * 24 * 3600, moment()),
|
||||
right: getDateSecondsAgo(7 * 24 * 3600, moment())
|
||||
},
|
||||
Month: {
|
||||
left: getDateSecondsAgo(60 * 24 * 3600, moment()),
|
||||
right: getDateSecondsAgo(30 * 24 * 3600, moment())
|
||||
}
|
||||
}
|
||||
|
||||
const GET_DATA = gql`
|
||||
query getData {
|
||||
|
|
@ -42,7 +56,7 @@ const GET_DATA = gql`
|
|||
txClass
|
||||
error
|
||||
}
|
||||
btcRates {
|
||||
fiatRates {
|
||||
code
|
||||
name
|
||||
rate
|
||||
|
|
@ -51,134 +65,72 @@ const GET_DATA = gql`
|
|||
}
|
||||
`
|
||||
|
||||
const reducer = (acc, it) =>
|
||||
(acc +=
|
||||
Number.parseFloat(it.commissionPercentage) * Number.parseFloat(it.fiat))
|
||||
|
||||
const SystemPerformance = () => {
|
||||
const classes = useStyles()
|
||||
|
||||
const [selectedRange, setSelectedRange] = useState('Day')
|
||||
const [transactionsToShow, setTransactionsToShow] = useState([])
|
||||
const [transactionsLastTimePeriod, setTransactionsLastTimePeriod] = useState(
|
||||
[]
|
||||
)
|
||||
|
||||
const { data, loading } = useQuery(GET_DATA)
|
||||
|
||||
const fiatLocale = fromNamespace('locale')(data?.config).fiatCurrency
|
||||
|
||||
useEffect(() => {
|
||||
const isInRange = (getLastTimePeriod = false) => t => {
|
||||
const now = moment()
|
||||
switch (selectedRange) {
|
||||
case 'Day':
|
||||
if (getLastTimePeriod) {
|
||||
return (
|
||||
t.error === null &&
|
||||
moment(t.created).isBetween(
|
||||
getDateSecondsAgo(2 * 24 * 3600, now),
|
||||
getDateSecondsAgo(24 * 3600, now)
|
||||
)
|
||||
)
|
||||
}
|
||||
return (
|
||||
t.error === null &&
|
||||
moment(t.created).isBetween(getDateSecondsAgo(24 * 3600, now), now)
|
||||
)
|
||||
case 'Week':
|
||||
if (getLastTimePeriod) {
|
||||
return (
|
||||
t.error === null &&
|
||||
moment(t.created).isBetween(
|
||||
getDateSecondsAgo(14 * 24 * 3600, now),
|
||||
getDateSecondsAgo(7 * 24 * 3600, now)
|
||||
)
|
||||
)
|
||||
}
|
||||
return (
|
||||
t.error === null &&
|
||||
moment(t.created).isBetween(
|
||||
getDateSecondsAgo(7 * 24 * 3600, now),
|
||||
now
|
||||
)
|
||||
)
|
||||
case 'Month':
|
||||
if (getLastTimePeriod) {
|
||||
return (
|
||||
t.error === null &&
|
||||
moment(t.created).isBetween(
|
||||
getDateSecondsAgo(60 * 24 * 3600, now),
|
||||
getDateSecondsAgo(30 * 24 * 3600, now)
|
||||
)
|
||||
)
|
||||
}
|
||||
return (
|
||||
t.error === null &&
|
||||
moment(t.created).isBetween(
|
||||
getDateSecondsAgo(30 * 24 * 3600, now),
|
||||
now
|
||||
)
|
||||
)
|
||||
default:
|
||||
return t.error === null && true
|
||||
}
|
||||
const isInRangeAndNoError = getLastTimePeriod => t => {
|
||||
if (t.error !== null) return false
|
||||
if (!getLastTimePeriod) {
|
||||
return (
|
||||
t.error === null &&
|
||||
moment(t.created).isBetween(ranges[selectedRange].right, moment())
|
||||
)
|
||||
}
|
||||
|
||||
const convertFiatToLocale = item => {
|
||||
if (item.fiatCode === fiatLocale) return item
|
||||
const itemRate = R.find(R.propEq('code', item.fiatCode))(data.btcRates)
|
||||
const localeRate = R.find(R.propEq('code', fiatLocale))(data.btcRates)
|
||||
const multiplier = localeRate.rate / itemRate.rate
|
||||
return { ...item, fiat: parseFloat(item.fiat) * multiplier }
|
||||
}
|
||||
|
||||
setTransactionsToShow(
|
||||
R.map(convertFiatToLocale)(
|
||||
R.filter(isInRange(false), data?.transactions ?? [])
|
||||
return (
|
||||
t.error === null &&
|
||||
moment(t.created).isBetween(
|
||||
ranges[selectedRange].left,
|
||||
ranges[selectedRange].right
|
||||
)
|
||||
)
|
||||
setTransactionsLastTimePeriod(
|
||||
R.map(convertFiatToLocale)(
|
||||
R.filter(isInRange(true), data?.transactions ?? [])
|
||||
)
|
||||
)
|
||||
}, [data, fiatLocale, selectedRange])
|
||||
|
||||
const handleSetRange = range => {
|
||||
setSelectedRange(range)
|
||||
}
|
||||
|
||||
const convertFiatToLocale = item => {
|
||||
if (item.fiatCode === fiatLocale) return item
|
||||
const itemRate = R.find(R.propEq('code', item.fiatCode))(data.fiatRates)
|
||||
const localeRate = R.find(R.propEq('code', fiatLocale))(data.fiatRates)
|
||||
const multiplier = localeRate.rate / itemRate.rate
|
||||
return { ...item, fiat: parseFloat(item.fiat) * multiplier }
|
||||
}
|
||||
|
||||
const transactionsToShow = R.map(convertFiatToLocale)(
|
||||
R.filter(isInRangeAndNoError(false), data?.transactions ?? [])
|
||||
)
|
||||
const transactionsLastTimePeriod = R.map(convertFiatToLocale)(
|
||||
R.filter(isInRangeAndNoError(true), data?.transactions ?? [])
|
||||
)
|
||||
|
||||
const getNumTransactions = () => {
|
||||
return R.length(R.filter(isNotProp('error'), transactionsToShow))
|
||||
return R.length(transactionsToShow)
|
||||
}
|
||||
|
||||
const getFiatVolume = () => {
|
||||
// for explanation check https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/round
|
||||
return +(
|
||||
Math.round(
|
||||
R.sum(getFiats(R.filter(isNotProp('error'), transactionsToShow))) +
|
||||
'e+2'
|
||||
) + 'e-2'
|
||||
)
|
||||
}
|
||||
const getFiatVolume = () =>
|
||||
new BigNumber(R.sum(getFiats(transactionsToShow)))
|
||||
.decimalPlaces(2)
|
||||
.toNumber()
|
||||
|
||||
const getProfit = (transactions = transactionsToShow) => {
|
||||
const cashInFees = R.sum(
|
||||
getProps('cashInFee')(R.filter(isNotProp('error'), transactions))
|
||||
)
|
||||
let commissionFees = 0
|
||||
transactions.forEach(t => {
|
||||
if (t.error === null) {
|
||||
commissionFees +=
|
||||
Number.parseFloat(t.commissionPercentage) * Number.parseFloat(t.fiat)
|
||||
}
|
||||
})
|
||||
return +(Math.round(commissionFees + cashInFees + 'e+2') + 'e-2')
|
||||
const getProfit = transactions => {
|
||||
const cashInFees = R.sum(mapToFee(transactions))
|
||||
const commissionFees = R.reduce(reducer, 0, transactions)
|
||||
|
||||
return new BigNumber(commissionFees + cashInFees)
|
||||
.decimalPlaces(2)
|
||||
.toNumber()
|
||||
}
|
||||
|
||||
const getPercentChange = () => {
|
||||
const thisTimePeriodProfit = getProfit(transactionsToShow)
|
||||
const previousTimePeriodProfit = getProfit(transactionsLastTimePeriod)
|
||||
if (previousTimePeriodProfit === 0) {
|
||||
return 100
|
||||
}
|
||||
|
||||
if (previousTimePeriodProfit === 0) return 100
|
||||
|
||||
return Math.round(
|
||||
(100 * (thisTimePeriodProfit - previousTimePeriodProfit)) /
|
||||
Math.abs(previousTimePeriodProfit)
|
||||
|
|
@ -186,36 +138,17 @@ const SystemPerformance = () => {
|
|||
}
|
||||
|
||||
const getDirectionPercent = () => {
|
||||
const directions = {
|
||||
cashIn: 0,
|
||||
cashOut: 0,
|
||||
length: 0
|
||||
const [cashIn, cashOut] = R.partition(R.propEq('txClass', 'cashIn'))(
|
||||
transactionsToShow
|
||||
)
|
||||
const totalLength = cashIn.length + cashOut.length
|
||||
if (totalLength === 0) {
|
||||
return { cashIn: 0, cashOut: 0 }
|
||||
}
|
||||
transactionsToShow.forEach(t => {
|
||||
if (t.error === null) {
|
||||
switch (t.txClass) {
|
||||
case 'cashIn':
|
||||
directions.cashIn += 1
|
||||
directions.length += 1
|
||||
break
|
||||
case 'cashOut':
|
||||
directions.cashOut += 1
|
||||
directions.length += 1
|
||||
break
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
return {
|
||||
cashIn:
|
||||
directions.length > 0
|
||||
? Math.round((directions.cashIn / directions.length) * 100)
|
||||
: 0,
|
||||
cashOut:
|
||||
directions.length > 0
|
||||
? Math.round((directions.cashOut / directions.length) * 100)
|
||||
: 0
|
||||
cashIn: Math.round((cashIn.length / totalLength) * 100),
|
||||
cashOut: Math.round((cashOut.length / totalLength) * 100)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -223,7 +156,7 @@ const SystemPerformance = () => {
|
|||
|
||||
return (
|
||||
<>
|
||||
<Nav handleSetRange={handleSetRange} />
|
||||
<Nav handleSetRange={setSelectedRange} />
|
||||
{!loading && (
|
||||
<>
|
||||
<Grid container spacing={2}>
|
||||
|
|
@ -241,7 +174,7 @@ const SystemPerformance = () => {
|
|||
</Grid>
|
||||
{/* todo new customers */}
|
||||
</Grid>
|
||||
<Grid container style={{ marginTop: 30 }}>
|
||||
<Grid container className={classes.gridContainer}>
|
||||
<Grid item xs={12}>
|
||||
<Label2>Transactions</Label2>
|
||||
<Scatterplot
|
||||
|
|
@ -250,27 +183,23 @@ const SystemPerformance = () => {
|
|||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Grid container style={{ marginTop: 30 }}>
|
||||
<Grid container className={classes.gridContainer}>
|
||||
<Grid item xs={8}>
|
||||
<Label2>Profit from commissions</Label2>
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
margin: '0 26px -30px 16px',
|
||||
position: 'relative'
|
||||
}}>
|
||||
<div className={classes.profitContainer}>
|
||||
<div className={classes.profitLabel}>
|
||||
{`${getProfit()} ${data?.config.locale_fiatCurrency}`}
|
||||
{`${getProfit(transactionsToShow)} ${
|
||||
data?.config.locale_fiatCurrency
|
||||
}`}
|
||||
</div>
|
||||
<div
|
||||
className={
|
||||
percentChange <= 0 ? classes.percentDown : classes.percentUp
|
||||
}>
|
||||
{percentChange <= 0 ? (
|
||||
<TriangleDown style={{ height: 13 }} />
|
||||
<TriangleDown className={classes.percentDown} />
|
||||
) : (
|
||||
<TriangleUp style={{ height: 10 }} />
|
||||
<TriangleUp className={classes.percentUp} />
|
||||
)}{' '}
|
||||
{`${percentChange}%`}
|
||||
</div>
|
||||
|
|
@ -281,7 +210,10 @@ const SystemPerformance = () => {
|
|||
<Label2>Direction</Label2>
|
||||
<Grid container>
|
||||
<Grid item xs>
|
||||
<PercentageChart data={getDirectionPercent()} />
|
||||
<PercentageChart
|
||||
cashIn={getDirectionPercent().cashIn}
|
||||
cashOut={getDirectionPercent().cashOut}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue