feat: flow report

This commit is contained in:
AP 2019-12-03 21:33:45 +01:00
parent 86e30be98d
commit 34a335870b
6 changed files with 280 additions and 151 deletions

View file

@ -132,10 +132,15 @@ export const SimpleButton = styled.button`
white-space: nowrap; white-space: nowrap;
`; `;
interface DarkProps {
fontSize?: string;
bottom?: string;
}
export const DarkSubTitle = styled.div` export const DarkSubTitle = styled.div`
font-size: 14px; font-size: ${({ fontSize }: DarkProps) => (fontSize ? fontSize : '14px')};
color: ${unSelectedNavButton}; color: ${unSelectedNavButton};
margin-bottom: 10px; margin-bottom: ${({ bottom }: DarkProps) => (bottom ? bottom : '10px')};
`; `;
export const ColorButton = styled(SimpleButton)` export const ColorButton = styled(SimpleButton)`

View file

@ -0,0 +1,55 @@
import React, { useContext } from 'react';
import { DarkSubTitle } from '../../generic/Styled';
import { SettingsContext } from '../../../context/SettingsContext';
import { getValue } from '../../../helpers/Helpers';
import { VictoryPie } from 'victory';
import { chartAxisColor } from '../../../styles/Themes';
import { Row, Col, PieRow } from '.';
interface Props {
flowPie: { x: string; y: number }[];
isType: string;
}
export const FlowPie = ({ flowPie, isType }: Props) => {
const { theme, price, symbol, currency } = useContext(SettingsContext);
const priceProps = { price, symbol, currency };
const getFormat = (amount: number) =>
getValue({
amount,
...priceProps,
});
return (
<Row>
<Col>
<PieRow>
<DarkSubTitle bottom={'0px'}>{flowPie[0].x}</DarkSubTitle>
{isType === 'tokens'
? getFormat(flowPie[0].y)
: flowPie[0].y}
</PieRow>
<PieRow>
<DarkSubTitle bottom={'0px'}>{flowPie[1].x}</DarkSubTitle>
{isType === 'tokens'
? getFormat(flowPie[1].y)
: flowPie[1].y}
</PieRow>
</Col>
<VictoryPie
padding={0}
height={150}
colorScale={['#FD5F00', '#ffd300']}
labels={() => ''}
padAngle={3}
innerRadius={50}
labelRadius={55}
data={flowPie}
style={{
labels: { fontSize: 24, fill: chartAxisColor[theme] },
}}
/>
</Row>
);
};

View file

@ -1,19 +1,13 @@
import React, { useContext } from 'react'; import React, { useContext } from 'react';
import { DarkSubTitle } from '../../generic/Styled';
import { useQuery } from '@apollo/react-hooks';
import { GET_IN_OUT } from '../../../graphql/query';
import numeral from 'numeral'; import numeral from 'numeral';
import { SettingsContext } from '../../../context/SettingsContext'; import { SettingsContext } from '../../../context/SettingsContext';
import { getValue } from '../../../helpers/Helpers'; import { getValue } from '../../../helpers/Helpers';
import { AccountContext } from '../../../context/AccountContext';
import { getAuthString } from '../../../utils/auth';
import { import {
VictoryBar, VictoryBar,
VictoryChart, VictoryChart,
VictoryAxis, VictoryAxis,
VictoryVoronoiContainer, VictoryVoronoiContainer,
VictoryGroup, VictoryGroup,
VictoryPie,
} from 'victory'; } from 'victory';
import { import {
chartAxisColor, chartAxisColor,
@ -21,11 +15,12 @@ import {
flowBarColor, flowBarColor,
flowBarColor2, flowBarColor2,
} from '../../../styles/Themes'; } from '../../../styles/Themes';
import { ChannelRow, CardContent, Row } from '.';
interface Props { interface Props {
isTime: string; isTime: string;
isType: string; isType: string;
parsedData: {}[];
parsedData2: {}[];
} }
const getValueString = (amount: number): string => { const getValueString = (amount: number): string => {
@ -37,16 +32,14 @@ const getValueString = (amount: number): string => {
return `${amount}`; return `${amount}`;
}; };
export const FlowReport = ({ isTime, isType }: Props) => { export const FlowReport = ({
isTime,
isType,
parsedData,
parsedData2,
}: Props) => {
const { theme, price, symbol, currency } = useContext(SettingsContext); const { theme, price, symbol, currency } = useContext(SettingsContext);
const { host, read, cert } = useContext(AccountContext);
const auth = getAuthString(host, read, cert);
const { data, loading, error } = useQuery(GET_IN_OUT, {
variables: { time: isTime, auth },
});
const priceProps = { price, symbol, currency }; const priceProps = { price, symbol, currency };
const getFormat = (amount: number) => const getFormat = (amount: number) =>
getValue({ getValue({
@ -54,12 +47,8 @@ export const FlowReport = ({ isTime, isType }: Props) => {
...priceProps, ...priceProps,
}); });
if (!data || loading) {
return <div>Loading</div>;
}
let domain = 24; let domain = 24;
let barWidth = 10; let barWidth = 3;
if (isTime === 'week') { if (isTime === 'week') {
domain = 7; domain = 7;
barWidth = 15; barWidth = 15;
@ -68,13 +57,6 @@ export const FlowReport = ({ isTime, isType }: Props) => {
barWidth = 3; barWidth = 3;
} }
const parsedData: {}[] = JSON.parse(data.getInOut.invoices);
const parsedData2: {}[] = JSON.parse(data.getInOut.payments);
const invoicePie = [
{ x: 'Confirmed', y: data.getInOut.confirmedInvoices },
{ x: 'Unconfirmed', y: data.getInOut.unConfirmedInvoices },
];
const getLabelString = (value: number) => { const getLabelString = (value: number) => {
if (isType === 'amount') { if (isType === 'amount') {
return numeral(value).format('0,0'); return numeral(value).format('0,0');
@ -82,106 +64,67 @@ export const FlowReport = ({ isTime, isType }: Props) => {
return getFormat(value); return getFormat(value);
}; };
const total = getLabelString(
parsedData
.map((x: any) => x[isType])
.reduce((a: number, c: number) => a + c, 0),
);
if (parsedData.length <= 0) {
return <p>Your node has not forwarded any payments.</p>;
}
return ( return (
<Row> <VictoryChart
<CardContent> height={100}
<div> domainPadding={50}
<VictoryChart padding={{ top: 10, left: 50, right: 50, bottom: 10 }}
domainPadding={50} containerComponent={
padding={{ top: 10, left: 50, right: 50, bottom: 20 }} <VictoryVoronoiContainer
containerComponent={ voronoiDimension="x"
<VictoryVoronoiContainer labels={({ datum }) => getLabelString(datum[isType])}
voronoiDimension="x" />
labels={({ datum }) => }
getLabelString(datum[isType]) >
} <VictoryAxis
/> domain={[0, domain]}
} tickFormat={() => ''}
> style={{
<VictoryAxis axis: { stroke: chartGridColor[theme] },
domain={[0, domain]} }}
tickFormat={() => ''} />
style={{ <VictoryAxis
axis: { stroke: chartGridColor[theme] }, dependentAxis
}} style={{
/> tickLabels: {
<VictoryAxis fill: chartAxisColor[theme],
dependentAxis fontSize: 8,
style={{ },
tickLabels: { grid: { stroke: chartGridColor[theme] },
fill: chartAxisColor[theme], axis: { stroke: 'transparent' },
fontSize: 18, }}
}, tickFormat={a => (isType === 'tokens' ? getFormat(a) : a)}
grid: { stroke: chartGridColor[theme] }, />
axis: { stroke: 'transparent' }, <VictoryGroup offset={barWidth}>
}} <VictoryBar
tickFormat={a => data={parsedData}
isType === 'tokens' ? getValueString(a) : a x="period"
} y={isType}
/>
<VictoryGroup offset={barWidth}>
<VictoryBar
animate={{
duration: 100,
}}
cornerRadius={barWidth / 2}
data={parsedData}
x="period"
y={isType}
style={{
data: {
fill: flowBarColor[theme],
width: barWidth,
},
}}
/>
<VictoryBar
animate={{
duration: 100,
}}
cornerRadius={barWidth / 2}
data={parsedData2}
x="period"
y={isType}
style={{
data: {
fill: flowBarColor2[theme],
width: barWidth,
},
}}
/>
</VictoryGroup>
</VictoryChart>
</div>
<ChannelRow>
<DarkSubTitle>Total:</DarkSubTitle>
{total}
</ChannelRow>
</CardContent>
<CardContent>
<VictoryPie
height={300}
colorScale={['tomato', 'orange', 'gold', 'cyan', 'navy']}
labels={({ datum }: any) => `${datum.y}`}
padAngle={3}
innerRadius={50}
labelRadius={55}
data={invoicePie}
style={{ style={{
labels: { fontSize: 24, fill: chartAxisColor[theme] }, data: {
fill: flowBarColor[theme],
width: barWidth,
},
labels: {
fontSize: '12px',
},
}} }}
/> />
</CardContent> <VictoryBar
</Row> data={parsedData2}
x="period"
y={isType}
style={{
data: {
fill: flowBarColor2[theme],
width: barWidth,
},
labels: {
fontSize: '12px',
},
}}
/>
</VictoryGroup>
</VictoryChart>
); );
}; };

View file

@ -0,0 +1,42 @@
import React, { useContext } from 'react';
import { DarkSubTitle } from '../../generic/Styled';
import { SettingsContext } from '../../../context/SettingsContext';
import { VictoryPie } from 'victory';
import { chartAxisColor } from '../../../styles/Themes';
import { Row, Col, PieRow } from '.';
interface Props {
invoicePie: { x: string; y: string }[];
}
export const InvoicePie = ({ invoicePie }: Props) => {
const { theme } = useContext(SettingsContext);
return (
<Row>
<Col>
<PieRow>
<DarkSubTitle bottom={'0px'}>Confirmed:</DarkSubTitle>
{invoicePie[0].y}
</PieRow>
<PieRow>
<DarkSubTitle bottom={'0px'}>Unconfirmed:</DarkSubTitle>
{invoicePie[1].y}
</PieRow>
</Col>
<VictoryPie
padding={0}
height={150}
colorScale={['#FD5F00', '#ffd300']}
labels={() => ''}
padAngle={3}
innerRadius={50}
labelRadius={55}
data={invoicePie}
style={{
labels: { fontSize: 24, fill: chartAxisColor[theme] },
}}
/>
</Row>
);
};

View file

@ -1,16 +1,20 @@
import React, { useState } from 'react'; import React, { useState, useContext } from 'react';
import styled from 'styled-components'; import styled from 'styled-components';
import { CardWithTitle, SubTitle, Card, CardTitle } from '../../generic/Styled'; import {
CardWithTitle,
SubTitle,
Card,
CardTitle,
Sub4Title,
} from '../../generic/Styled';
import { ButtonRow } from '../forwardReport/Buttons'; import { ButtonRow } from '../forwardReport/Buttons';
import { FlowReport } from './FlowReport'; import { FlowReport } from './FlowReport';
import { getAuthString } from '../../../utils/auth';
export const CardContent = styled.div` import { AccountContext } from '../../../context/AccountContext';
height: 100%; import { GET_IN_OUT } from '../../../graphql/query';
display: flex; import { useQuery } from '@apollo/react-hooks';
flex-flow: column; import { FlowPie } from './FlowPie';
width: 50%; import { InvoicePie } from './InvoicePie';
padding: 0 20px;
`;
export const ChannelRow = styled.div` export const ChannelRow = styled.div`
font-size: 14px; font-size: 14px;
@ -23,6 +27,27 @@ export const Row = styled.div`
display: flex; display: flex;
`; `;
export const PieRow = styled(Row)`
justify-content: space-between;
`;
export const Col = styled.div`
display: flex;
flex-direction: column;
justify-content: space-around;
min-width: 200px;
`;
const HalfCardWithTitle = styled(CardWithTitle)`
width: 50%;
`;
interface PeriodProps {
period: number;
amount: number;
tokens: number;
}
const availableTimes = ['day', 'week', 'month']; const availableTimes = ['day', 'week', 'month'];
const mappedTimes = ['Day', 'Week', 'Month']; const mappedTimes = ['Day', 'Week', 'Month'];
const availableTypes = ['amount', 'tokens']; const availableTypes = ['amount', 'tokens'];
@ -30,10 +55,15 @@ const mappedTypes = ['Amount', 'Value'];
const buttonBorder = `#FD5F00`; const buttonBorder = `#FD5F00`;
export const FlowBox = () => { export const FlowBox = () => {
const [isTime, setIsTime] = useState<string>('week'); const [isTime, setIsTime] = useState<string>('month');
const [isType, setIsType] = useState<string>('amount'); const [isType, setIsType] = useState<string>('amount');
const props = { isTime, isType }; const { host, read, cert } = useContext(AccountContext);
const auth = getAuthString(host, read, cert);
const { data, loading, error } = useQuery(GET_IN_OUT, {
variables: { time: isTime, auth },
});
const buttonProps = { const buttonProps = {
isTime, isTime,
isType, isType,
@ -46,15 +76,73 @@ export const FlowBox = () => {
buttonBorder, buttonBorder,
}; };
if (!data || loading) {
return <div>Loading</div>;
}
const parsedData: PeriodProps[] = JSON.parse(data.getInOut.invoices);
const parsedData2: PeriodProps[] = JSON.parse(data.getInOut.payments);
if (parsedData.length <= 0) {
return <p>Your node has not forwarded any payments.</p>;
}
const reduce = (array: PeriodProps[]) =>
array.reduce((p, c) => {
return {
tokens: p.tokens + c.tokens,
period: 0,
amount: p.amount + c.amount,
};
});
const totalInvoices: any = reduce(parsedData);
const totalPayments: any = reduce(parsedData2);
const flowPie = [
{ x: 'Invoice', y: totalInvoices[isType] },
{ x: 'Payments', y: totalPayments[isType] },
];
const invoicePie = [
{ x: 'Confirmed', y: data.getInOut.confirmedInvoices },
{ x: 'Unconfirmed', y: data.getInOut.unConfirmedInvoices },
];
const props = { isTime, isType, parsedData, parsedData2 };
const pieProps = { invoicePie };
const flowProps = { flowPie, isType };
return ( return (
<CardWithTitle> <>
<CardTitle> <CardWithTitle>
<SubTitle>Invoices and Payments Report</SubTitle> <CardTitle>
<ButtonRow {...buttonProps} /> <SubTitle>Invoices and Payments Report</SubTitle>
</CardTitle> <ButtonRow {...buttonProps} />
<Card> </CardTitle>
<FlowReport {...props} /> <Card bottom={'10px'}>
</Card> <FlowReport {...props} />
</CardWithTitle> </Card>
</CardWithTitle>
<Row>
<HalfCardWithTitle>
<CardTitle>
<Sub4Title>Total</Sub4Title>
</CardTitle>
<Card>
<FlowPie {...flowProps} />
</Card>
</HalfCardWithTitle>
<div style={{ width: '20px' }} />
<HalfCardWithTitle>
<CardTitle>
<Sub4Title>Invoices</Sub4Title>
</CardTitle>
<Card>
<InvoicePie {...pieProps} />
</Card>
</HalfCardWithTitle>
</Row>
</>
); );
}; };

View file

@ -124,10 +124,6 @@ export const ForwardReport = ({ isTime, isType }: Props) => {
} }
/> />
<VictoryBar <VictoryBar
animate={{
duration: 100,
}}
cornerRadius={barWidth / 2}
data={parsedData} data={parsedData}
x="period" x="period"
y={isType} y={isType}