initial ui for test my node

use svg for node path

adjust layout for overiew page

add stories for test my node

remove placeholder nav item

add top margin to app bar

add print to pdf functionality for node test results
This commit is contained in:
fmtabbara
2022-09-07 22:43:04 +01:00
parent 6c4f0bc2f4
commit ecdf192b47
17 changed files with 562 additions and 1 deletions
+1
View File
@@ -48,6 +48,7 @@
"react-hook-form": "^7.14.2",
"react-router-dom": "6",
"recharts": "^2.1.13",
"react-to-print": "^2.14.7",
"semver": "^6.3.0",
"string-to-color": "^2.2.2",
"use-clipboard-copy": "^0.2.0",
Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

+1 -1
View File
@@ -15,7 +15,7 @@ export const AppBar = () => {
const navigate = useNavigate();
return (
<MuiAppBar position="sticky" sx={{ boxShadow: 'none', bgcolor: 'transparent', backgroundImage: 'none', pt: 3 }}>
<MuiAppBar position="sticky" sx={{ boxShadow: 'none', bgcolor: 'transparent', backgroundImage: 'none', mt: 3 }}>
<Toolbar disableGutters>
<Grid container justifyContent="space-between" alignItems="center" flexWrap="nowrap">
<Grid item container alignItems="center" spacing={1}>
@@ -0,0 +1,40 @@
import React from 'react';
import { Button, Grid, Stack } from '@mui/material';
import testNode from 'src/assets/test-node-illustration.jpg';
import { OverviewDescription } from '../components/OverviewDescription';
const content = [
{
title: 'How is works',
description:
'This is your APY playground - play with the parameters on left to see estimated rewards on the right side',
},
{
title: 'Test path',
description:
'This is your APY playground - play with the parameters on left to see estimated rewards on the right side',
},
{
title: 'Results',
description:
'This is your APY playground - play with the parameters on left to see estimated rewards on the right side',
},
];
export const Overview = ({ onStartTest }: { onStartTest: () => void }) => (
<Grid container spacing={2} justifyContent="center">
<Grid item xs={12} md={6}>
<img src={testNode} style={{ borderRadius: 8 }} />
</Grid>
<Grid item container direction="column" xs={12} xl={6}>
<Grid item>
<Stack>{content.map(OverviewDescription)}</Stack>
</Grid>
<Grid item>
<Button variant="contained" fullWidth disableElevation onClick={onStartTest}>
Start test
</Button>
</Grid>
</Grid>
</Grid>
);
@@ -0,0 +1,27 @@
import * as React from 'react';
import { ComponentMeta, ComponentStory } from '@storybook/react';
import { Box } from '@mui/material';
import { Packets } from '.';
export default {
title: 'Test my node / Packets',
component: Packets,
} as ComponentMeta<typeof Packets>;
const Transfer: ComponentStory<typeof Packets> = (args) => (
<Box width="500px">
<Packets {...args} />
</Box>
);
export const HighTransfer = Transfer.bind({});
HighTransfer.args = {
sent: '100',
received: '80',
};
export const LowTransfer = Transfer.bind({});
LowTransfer.args = {
sent: '100',
received: '50',
};
@@ -0,0 +1,35 @@
import * as React from 'react';
import { ComponentMeta, ComponentStory } from '@storybook/react';
import { Box } from '@mui/material';
import { Path } from '.';
export default {
title: 'Test my node / Node path',
component: Path,
} as ComponentMeta<typeof Path>;
const Template: ComponentStory<typeof Path> = (args) => (
<Box display="flex">
<Path {...args} />
</Box>
);
export const Gateway = Template.bind({});
Gateway.args = {
layer: 'gateway',
};
export const LayerOne = Template.bind({});
LayerOne.args = {
layer: '1',
};
export const LayerTwo = Template.bind({});
LayerTwo.args = {
layer: '2',
};
export const LayerThree = Template.bind({});
LayerThree.args = {
layer: '3',
};
@@ -0,0 +1,18 @@
import * as React from 'react';
import { ComponentMeta, ComponentStory } from '@storybook/react';
import { Box } from '@mui/material';
import { NodeSpeed, Results } from '.';
export default {
title: 'Test my node / Results',
component: Results,
} as ComponentMeta<typeof Results>;
const Template: ComponentStory<typeof Results> = (args) => <Results {...args} />;
export const Default = Template.bind({});
Default.args = {
layer: '1',
packetsSent: '1000',
packetsReceived: '5000',
};
@@ -0,0 +1,33 @@
import * as React from 'react';
import { ComponentMeta, ComponentStory } from '@storybook/react';
import { Box } from '@mui/material';
import { NodeSpeed } from '.';
export default {
title: 'Test my node / Node speed',
component: NodeSpeed,
} as ComponentMeta<typeof NodeSpeed>;
const Template: ComponentStory<typeof NodeSpeed> = (args) => (
<Box display="flex" alignContent="center">
<NodeSpeed {...args} />
</Box>
);
export const FastNode = Template.bind({});
FastNode.args = {
Mbps: 500,
performance: 'good',
};
export const FairNode = Template.bind({});
FairNode.args = {
Mbps: 100,
performance: 'fair',
};
export const SlowNode = Template.bind({});
SlowNode.args = {
Mbps: 10,
performance: 'poor',
};
@@ -0,0 +1,109 @@
import React, { useRef } from 'react';
import { ArrowForward, CheckCircleOutline, Description, Download } from '@mui/icons-material';
import { Box, Button, Card, Chip, CircularProgress, Divider, Grid, Stack, Typography } from '@mui/material';
import format from 'date-fns/format';
import { ResultsCard } from '../components/ResultsCard';
import { ResultsCardDetail } from '../components/ResultsCardDetail';
import { NodePath } from 'src/svg-icons/node-path';
import { useReactToPrint } from 'react-to-print';
export type Layer = '1' | '2' | '3' | 'gateway';
const getLayerDescription = (layer: Layer) => {
if (layer === 'gateway') return 'Your node was in the Gateway layer';
return `Your node was in layer ${layer}`;
};
export const NodeSpeed = ({ Mbps, performance }: { Mbps: number; performance: 'poor' | 'fair' | 'good' }) => (
<ResultsCard
label="Node speed"
detail={`${performance === 'good' ? 'Fast' : performance === 'poor' ? 'Slow' : 'Fair'} node`}
isOk={performance === 'good'}
>
<Box
sx={{
display: 'flex',
position: 'relative',
width: 250,
height: 250,
justifyContent: 'center',
alignItems: 'center',
mx: 'auto',
mt: 4,
}}
>
<CircularProgress
variant="determinate"
value={performance === 'poor' ? 12.5 : performance === 'good' ? 85 : 65}
size={250}
sx={{ position: 'absolute', top: 0, left: 0 }}
color={performance === 'poor' ? 'error' : performance === 'good' ? 'success' : 'warning'}
/>
<Stack alignItems="center" gap={1}>
<Typography fontWeight="bold" variant="h4">
{Mbps}
</Typography>
<Typography>Mbps</Typography>
</Stack>
</Box>
</ResultsCard>
);
export const Packets = ({ sent, received }: { sent: string; received: string }) => {
const percentage = Math.round((+received / +sent) * 100);
return (
<ResultsCard label="Packets" detail={`${percentage}% packets`} isOk={percentage > 75}>
<Divider sx={{ my: 2 }} />
<ResultsCardDetail label="Packets sent" detail={sent} />
<Divider sx={{ my: 2 }} />
<ResultsCardDetail label="Packets received" detail={received} />
</ResultsCard>
);
};
export const Path = ({ layer }: { layer: Layer }) => (
<ResultsCard label="Path" detail={getLayerDescription(layer)} isOk>
<Box sx={{ mt: 3 }}>
<NodePath layer={layer} />
</Box>
</ResultsCard>
);
export const Results = ({
packetsSent,
packetsReceived,
layer,
}: {
packetsSent: string;
packetsReceived: string;
layer: '1' | '2' | '3' | 'gateway';
}) => {
const ref = useRef(null);
const handleSaveToPdf = useReactToPrint({ documentTitle: 'Test results', content: () => ref.current });
return (
<>
<Stack direction="row" justifyContent="space-between" alignItems="center" sx={{ mb: 1 }}>
<Box display="flex" gap={1}>
<Typography fontWeight="bold" component="span">
Test date
</Typography>
<Typography>{format(new Date(), 'dd/MM/yyyy HH:mm')}</Typography>
</Box>
<Button onClick={handleSaveToPdf} startIcon={<Download />}>
Save to PDF
</Button>
</Stack>
<Grid container spacing={2} ref={ref}>
<Grid item md={5}>
<NodeSpeed Mbps={150.01} performance="good" />
</Grid>
<Grid item container direction="column" md={7}>
<Stack spacing={2}>
<Packets sent={packetsSent} received={packetsReceived} />
<Path layer={layer} />
</Stack>
</Grid>
</Grid>
</>
);
};
@@ -0,0 +1,38 @@
import * as React from 'react';
import { ComponentMeta, ComponentStory } from '@storybook/react';
import { Box } from '@mui/material';
import { TestProgress } from '.';
export default {
title: 'Test my node / Test progress',
component: TestProgress,
} as ComponentMeta<typeof TestProgress>;
const Template: ComponentStory<typeof TestProgress> = ({ packetsSent, totalPackets }) => {
const [sent, setSent] = React.useState(packetsSent);
const mockPacketTransfer = (sent: number) => {
if (sent - 1 < totalPackets) {
setSent(sent);
setTimeout(() => {
mockPacketTransfer(sent + 1);
}, 25);
}
};
React.useEffect(() => {
mockPacketTransfer(0);
}, []);
return (
<Box display="flex" alignContent="center">
<TestProgress packetsSent={sent} totalPackets={totalPackets} />
</Box>
);
};
export const Default = Template.bind({});
Default.args = {
packetsSent: 0,
totalPackets: 100,
};
@@ -0,0 +1,34 @@
import React from 'react';
import { Box, CircularProgress, Stack, Typography } from '@mui/material';
export const TestProgress = ({ totalPackets, packetsSent }: { totalPackets: number; packetsSent: number }) => {
const percentage = Math.round((packetsSent / totalPackets) * 100);
return (
<Stack alignItems="center" gap={3}>
<Typography sx={{ textTransform: 'uppercase' }}>Test in progress</Typography>
<Box
sx={{
display: 'flex',
position: 'relative',
width: 250,
height: 250,
justifyContent: 'center',
alignItems: 'center',
}}
>
<CircularProgress
variant="determinate"
value={percentage}
size={250}
sx={{ position: 'absolute', top: 0, left: 0 }}
/>
<Typography fontWeight="bold" variant="h4">
{percentage}%
</Typography>
</Box>
<Typography>Sending packets...</Typography>
<Typography>{`${packetsSent} / ${totalPackets}`}</Typography>
</Stack>
);
};
@@ -0,0 +1,13 @@
import React from 'react';
import { Box, Typography } from '@mui/material';
export const OverviewDescription = ({ title, description }: { title: string; description: string }) => (
<Box>
<Typography fontWeight="bold" sx={{ mb: 1 }}>
{title}
</Typography>
<Typography fontSize="small" sx={{ color: 'grey.700', mb: 2 }}>
{description}
</Typography>
</Box>
);
@@ -0,0 +1,6 @@
import React from 'react';
import { Chip } from '@mui/material';
export const PathChip = ({ label, highlight }: { label: string; highlight: boolean }) => (
<Chip label={label} size="medium" color={highlight ? 'primary' : 'default'} />
);
@@ -0,0 +1,21 @@
import React from 'react';
import { CheckCircleOutline } from '@mui/icons-material';
import { Card } from '@mui/material';
import { ResultsCardDetail } from './ResultsCardDetail';
export const ResultsCard: React.FC<{ label: string; detail: string; isOk: boolean }> = ({
label,
detail,
isOk,
children,
}) => (
<Card variant="outlined" sx={{ p: 3 }}>
<ResultsCardDetail
label={label}
detail={detail}
boldLabel
DescriptionIcon={isOk && <CheckCircleOutline sx={{ color: 'success.light' }} />}
/>
{children}
</Card>
);
@@ -0,0 +1,22 @@
import React from 'react';
import { Box, Stack, Typography } from '@mui/material';
export const ResultsCardDetail = ({
label,
detail,
DescriptionIcon,
boldLabel,
}: {
label: string;
detail: string;
DescriptionIcon?: React.ReactNode;
boldLabel?: boolean;
}) => (
<Stack direction="row" justifyContent="space-between">
<Typography fontWeight={boldLabel ? 'bold' : 'regular'}>{label}</Typography>
<Box display="flex" gap={1} alignItems="center">
<Typography>{detail}</Typography>
{DescriptionIcon}
</Box>
</Stack>
);
@@ -0,0 +1,36 @@
import React, { useState } from 'react';
import { NymCard } from 'src/components';
import { Overview } from 'src/components/TestMyNode/Overview';
import { Results } from 'src/components/TestMyNode/Results';
import { TestProgress } from 'src/components/TestMyNode/TestProgress';
export const TestNode = () => {
const [view, setView] = useState('overview');
const [packetsSent, setPacketsSent] = useState(0);
const totalPackets = 500;
const mockPacketTransfer = (sent: number) => {
if (sent - 1 < totalPackets) {
setPacketsSent(sent);
setTimeout(() => {
mockPacketTransfer(sent + 1);
}, 12.5);
}
if (sent === totalPackets) {
setView('results');
}
};
const startTest = () => {
setView('start-test');
mockPacketTransfer(0);
};
return (
<NymCard title="Test Node">
{view === 'overview' && <Overview onStartTest={startTest} />}
{view === 'start-test' && <TestProgress totalPackets={totalPackets} packetsSent={packetsSent} />}
{view === 'results' && <Results layer="1" packetsSent="5000" packetsReceived="1000" />}
</NymCard>
);
};
File diff suppressed because one or more lines are too long