mirror of
https://github.com/apotdevin/thunderhub.git
synced 2025-02-22 22:25:21 +01:00
feat: flow report
This commit is contained in:
parent
86e30be98d
commit
34a335870b
6 changed files with 280 additions and 151 deletions
|
@ -132,10 +132,15 @@ export const SimpleButton = styled.button`
|
|||
white-space: nowrap;
|
||||
`;
|
||||
|
||||
interface DarkProps {
|
||||
fontSize?: string;
|
||||
bottom?: string;
|
||||
}
|
||||
|
||||
export const DarkSubTitle = styled.div`
|
||||
font-size: 14px;
|
||||
font-size: ${({ fontSize }: DarkProps) => (fontSize ? fontSize : '14px')};
|
||||
color: ${unSelectedNavButton};
|
||||
margin-bottom: 10px;
|
||||
margin-bottom: ${({ bottom }: DarkProps) => (bottom ? bottom : '10px')};
|
||||
`;
|
||||
|
||||
export const ColorButton = styled(SimpleButton)`
|
||||
|
|
55
src/components/reports/flow/FlowPie.tsx
Normal file
55
src/components/reports/flow/FlowPie.tsx
Normal 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>
|
||||
);
|
||||
};
|
|
@ -1,19 +1,13 @@
|
|||
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 { SettingsContext } from '../../../context/SettingsContext';
|
||||
import { getValue } from '../../../helpers/Helpers';
|
||||
import { AccountContext } from '../../../context/AccountContext';
|
||||
import { getAuthString } from '../../../utils/auth';
|
||||
import {
|
||||
VictoryBar,
|
||||
VictoryChart,
|
||||
VictoryAxis,
|
||||
VictoryVoronoiContainer,
|
||||
VictoryGroup,
|
||||
VictoryPie,
|
||||
} from 'victory';
|
||||
import {
|
||||
chartAxisColor,
|
||||
|
@ -21,11 +15,12 @@ import {
|
|||
flowBarColor,
|
||||
flowBarColor2,
|
||||
} from '../../../styles/Themes';
|
||||
import { ChannelRow, CardContent, Row } from '.';
|
||||
|
||||
interface Props {
|
||||
isTime: string;
|
||||
isType: string;
|
||||
parsedData: {}[];
|
||||
parsedData2: {}[];
|
||||
}
|
||||
|
||||
const getValueString = (amount: number): string => {
|
||||
|
@ -37,16 +32,14 @@ const getValueString = (amount: number): string => {
|
|||
return `${amount}`;
|
||||
};
|
||||
|
||||
export const FlowReport = ({ isTime, isType }: Props) => {
|
||||
export const FlowReport = ({
|
||||
isTime,
|
||||
isType,
|
||||
parsedData,
|
||||
parsedData2,
|
||||
}: Props) => {
|
||||
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 getFormat = (amount: number) =>
|
||||
getValue({
|
||||
|
@ -54,12 +47,8 @@ export const FlowReport = ({ isTime, isType }: Props) => {
|
|||
...priceProps,
|
||||
});
|
||||
|
||||
if (!data || loading) {
|
||||
return <div>Loading</div>;
|
||||
}
|
||||
|
||||
let domain = 24;
|
||||
let barWidth = 10;
|
||||
let barWidth = 3;
|
||||
if (isTime === 'week') {
|
||||
domain = 7;
|
||||
barWidth = 15;
|
||||
|
@ -68,13 +57,6 @@ export const FlowReport = ({ isTime, isType }: Props) => {
|
|||
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) => {
|
||||
if (isType === 'amount') {
|
||||
return numeral(value).format('0,0');
|
||||
|
@ -82,106 +64,67 @@ export const FlowReport = ({ isTime, isType }: Props) => {
|
|||
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 (
|
||||
<Row>
|
||||
<CardContent>
|
||||
<div>
|
||||
<VictoryChart
|
||||
domainPadding={50}
|
||||
padding={{ top: 10, left: 50, right: 50, bottom: 20 }}
|
||||
containerComponent={
|
||||
<VictoryVoronoiContainer
|
||||
voronoiDimension="x"
|
||||
labels={({ datum }) =>
|
||||
getLabelString(datum[isType])
|
||||
}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<VictoryAxis
|
||||
domain={[0, domain]}
|
||||
tickFormat={() => ''}
|
||||
style={{
|
||||
axis: { stroke: chartGridColor[theme] },
|
||||
}}
|
||||
/>
|
||||
<VictoryAxis
|
||||
dependentAxis
|
||||
style={{
|
||||
tickLabels: {
|
||||
fill: chartAxisColor[theme],
|
||||
fontSize: 18,
|
||||
},
|
||||
grid: { stroke: chartGridColor[theme] },
|
||||
axis: { stroke: 'transparent' },
|
||||
}}
|
||||
tickFormat={a =>
|
||||
isType === 'tokens' ? getValueString(a) : a
|
||||
}
|
||||
/>
|
||||
<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}
|
||||
<VictoryChart
|
||||
height={100}
|
||||
domainPadding={50}
|
||||
padding={{ top: 10, left: 50, right: 50, bottom: 10 }}
|
||||
containerComponent={
|
||||
<VictoryVoronoiContainer
|
||||
voronoiDimension="x"
|
||||
labels={({ datum }) => getLabelString(datum[isType])}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<VictoryAxis
|
||||
domain={[0, domain]}
|
||||
tickFormat={() => ''}
|
||||
style={{
|
||||
axis: { stroke: chartGridColor[theme] },
|
||||
}}
|
||||
/>
|
||||
<VictoryAxis
|
||||
dependentAxis
|
||||
style={{
|
||||
tickLabels: {
|
||||
fill: chartAxisColor[theme],
|
||||
fontSize: 8,
|
||||
},
|
||||
grid: { stroke: chartGridColor[theme] },
|
||||
axis: { stroke: 'transparent' },
|
||||
}}
|
||||
tickFormat={a => (isType === 'tokens' ? getFormat(a) : a)}
|
||||
/>
|
||||
<VictoryGroup offset={barWidth}>
|
||||
<VictoryBar
|
||||
data={parsedData}
|
||||
x="period"
|
||||
y={isType}
|
||||
style={{
|
||||
labels: { fontSize: 24, fill: chartAxisColor[theme] },
|
||||
data: {
|
||||
fill: flowBarColor[theme],
|
||||
width: barWidth,
|
||||
},
|
||||
labels: {
|
||||
fontSize: '12px',
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</CardContent>
|
||||
</Row>
|
||||
<VictoryBar
|
||||
data={parsedData2}
|
||||
x="period"
|
||||
y={isType}
|
||||
style={{
|
||||
data: {
|
||||
fill: flowBarColor2[theme],
|
||||
width: barWidth,
|
||||
},
|
||||
labels: {
|
||||
fontSize: '12px',
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</VictoryGroup>
|
||||
</VictoryChart>
|
||||
);
|
||||
};
|
||||
|
|
42
src/components/reports/flow/InvoicePie.tsx
Normal file
42
src/components/reports/flow/InvoicePie.tsx
Normal 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>
|
||||
);
|
||||
};
|
|
@ -1,16 +1,20 @@
|
|||
import React, { useState } from 'react';
|
||||
import React, { useState, useContext } from 'react';
|
||||
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 { FlowReport } from './FlowReport';
|
||||
|
||||
export const CardContent = styled.div`
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
width: 50%;
|
||||
padding: 0 20px;
|
||||
`;
|
||||
import { getAuthString } from '../../../utils/auth';
|
||||
import { AccountContext } from '../../../context/AccountContext';
|
||||
import { GET_IN_OUT } from '../../../graphql/query';
|
||||
import { useQuery } from '@apollo/react-hooks';
|
||||
import { FlowPie } from './FlowPie';
|
||||
import { InvoicePie } from './InvoicePie';
|
||||
|
||||
export const ChannelRow = styled.div`
|
||||
font-size: 14px;
|
||||
|
@ -23,6 +27,27 @@ export const Row = styled.div`
|
|||
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 mappedTimes = ['Day', 'Week', 'Month'];
|
||||
const availableTypes = ['amount', 'tokens'];
|
||||
|
@ -30,10 +55,15 @@ const mappedTypes = ['Amount', 'Value'];
|
|||
const buttonBorder = `#FD5F00`;
|
||||
|
||||
export const FlowBox = () => {
|
||||
const [isTime, setIsTime] = useState<string>('week');
|
||||
const [isTime, setIsTime] = useState<string>('month');
|
||||
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 = {
|
||||
isTime,
|
||||
isType,
|
||||
|
@ -46,15 +76,73 @@ export const FlowBox = () => {
|
|||
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 (
|
||||
<CardWithTitle>
|
||||
<CardTitle>
|
||||
<SubTitle>Invoices and Payments Report</SubTitle>
|
||||
<ButtonRow {...buttonProps} />
|
||||
</CardTitle>
|
||||
<Card>
|
||||
<FlowReport {...props} />
|
||||
</Card>
|
||||
</CardWithTitle>
|
||||
<>
|
||||
<CardWithTitle>
|
||||
<CardTitle>
|
||||
<SubTitle>Invoices and Payments Report</SubTitle>
|
||||
<ButtonRow {...buttonProps} />
|
||||
</CardTitle>
|
||||
<Card bottom={'10px'}>
|
||||
<FlowReport {...props} />
|
||||
</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>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -124,10 +124,6 @@ export const ForwardReport = ({ isTime, isType }: Props) => {
|
|||
}
|
||||
/>
|
||||
<VictoryBar
|
||||
animate={{
|
||||
duration: 100,
|
||||
}}
|
||||
cornerRadius={barWidth / 2}
|
||||
data={parsedData}
|
||||
x="period"
|
||||
y={isType}
|
||||
|
|
Loading…
Add table
Reference in a new issue