diff --git a/nym-wallet/package.json b/nym-wallet/package.json index 26d58da9d1..edc9f3fabf 100644 --- a/nym-wallet/package.json +++ b/nym-wallet/package.json @@ -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", diff --git a/nym-wallet/src/assets/test-node-illustration.jpg b/nym-wallet/src/assets/test-node-illustration.jpg new file mode 100644 index 0000000000..edc79e8dbb Binary files /dev/null and b/nym-wallet/src/assets/test-node-illustration.jpg differ diff --git a/nym-wallet/src/components/AppBar.tsx b/nym-wallet/src/components/AppBar.tsx index 24b2f93942..3022646798 100644 --- a/nym-wallet/src/components/AppBar.tsx +++ b/nym-wallet/src/components/AppBar.tsx @@ -15,7 +15,7 @@ export const AppBar = () => { const navigate = useNavigate(); return ( - + diff --git a/nym-wallet/src/components/TestMyNode/Overview/index.tsx b/nym-wallet/src/components/TestMyNode/Overview/index.tsx new file mode 100644 index 0000000000..2ebcf7180b --- /dev/null +++ b/nym-wallet/src/components/TestMyNode/Overview/index.tsx @@ -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 }) => ( + + + + + + + {content.map(OverviewDescription)} + + + + + + +); diff --git a/nym-wallet/src/components/TestMyNode/Results/Packets.stories.tsx b/nym-wallet/src/components/TestMyNode/Results/Packets.stories.tsx new file mode 100644 index 0000000000..32901ea50c --- /dev/null +++ b/nym-wallet/src/components/TestMyNode/Results/Packets.stories.tsx @@ -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; + +const Transfer: ComponentStory = (args) => ( + + + +); + +export const HighTransfer = Transfer.bind({}); +HighTransfer.args = { + sent: '100', + received: '80', +}; + +export const LowTransfer = Transfer.bind({}); +LowTransfer.args = { + sent: '100', + received: '50', +}; diff --git a/nym-wallet/src/components/TestMyNode/Results/Path.stories.tsx b/nym-wallet/src/components/TestMyNode/Results/Path.stories.tsx new file mode 100644 index 0000000000..2c4601801a --- /dev/null +++ b/nym-wallet/src/components/TestMyNode/Results/Path.stories.tsx @@ -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; + +const Template: ComponentStory = (args) => ( + + + +); + +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', +}; diff --git a/nym-wallet/src/components/TestMyNode/Results/Results.stories.tsx b/nym-wallet/src/components/TestMyNode/Results/Results.stories.tsx new file mode 100644 index 0000000000..30dbc9f941 --- /dev/null +++ b/nym-wallet/src/components/TestMyNode/Results/Results.stories.tsx @@ -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; + +const Template: ComponentStory = (args) => ; + +export const Default = Template.bind({}); +Default.args = { + layer: '1', + packetsSent: '1000', + packetsReceived: '5000', +}; diff --git a/nym-wallet/src/components/TestMyNode/Results/Speed.stories.tsx b/nym-wallet/src/components/TestMyNode/Results/Speed.stories.tsx new file mode 100644 index 0000000000..e8e3d9d078 --- /dev/null +++ b/nym-wallet/src/components/TestMyNode/Results/Speed.stories.tsx @@ -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; + +const Template: ComponentStory = (args) => ( + + + +); + +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', +}; diff --git a/nym-wallet/src/components/TestMyNode/Results/index.tsx b/nym-wallet/src/components/TestMyNode/Results/index.tsx new file mode 100644 index 0000000000..a5b6e04bb9 --- /dev/null +++ b/nym-wallet/src/components/TestMyNode/Results/index.tsx @@ -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' }) => ( + + + + + + {Mbps} + + Mbps + + + +); + +export const Packets = ({ sent, received }: { sent: string; received: string }) => { + const percentage = Math.round((+received / +sent) * 100); + return ( + 75}> + + + + + + ); +}; + +export const Path = ({ layer }: { layer: Layer }) => ( + + + + + +); + +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 ( + <> + + + + Test date + + {format(new Date(), 'dd/MM/yyyy HH:mm')} + + + + + + + + + + + + + + + + ); +}; diff --git a/nym-wallet/src/components/TestMyNode/TestProgress/TestProgress.stories.tsx b/nym-wallet/src/components/TestMyNode/TestProgress/TestProgress.stories.tsx new file mode 100644 index 0000000000..47241e8f1e --- /dev/null +++ b/nym-wallet/src/components/TestMyNode/TestProgress/TestProgress.stories.tsx @@ -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; + +const Template: ComponentStory = ({ 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 ( + + + + ); +}; + +export const Default = Template.bind({}); +Default.args = { + packetsSent: 0, + totalPackets: 100, +}; diff --git a/nym-wallet/src/components/TestMyNode/TestProgress/index.tsx b/nym-wallet/src/components/TestMyNode/TestProgress/index.tsx new file mode 100644 index 0000000000..3e910855a3 --- /dev/null +++ b/nym-wallet/src/components/TestMyNode/TestProgress/index.tsx @@ -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 ( + + Test in progress + + + + {percentage}% + + + Sending packets... + {`${packetsSent} / ${totalPackets}`} + + ); +}; diff --git a/nym-wallet/src/components/TestMyNode/components/OverviewDescription.tsx b/nym-wallet/src/components/TestMyNode/components/OverviewDescription.tsx new file mode 100644 index 0000000000..19de926986 --- /dev/null +++ b/nym-wallet/src/components/TestMyNode/components/OverviewDescription.tsx @@ -0,0 +1,13 @@ +import React from 'react'; +import { Box, Typography } from '@mui/material'; + +export const OverviewDescription = ({ title, description }: { title: string; description: string }) => ( + + + {title} + + + {description} + + +); diff --git a/nym-wallet/src/components/TestMyNode/components/PathChip.tsx b/nym-wallet/src/components/TestMyNode/components/PathChip.tsx new file mode 100644 index 0000000000..f6154cfc4b --- /dev/null +++ b/nym-wallet/src/components/TestMyNode/components/PathChip.tsx @@ -0,0 +1,6 @@ +import React from 'react'; +import { Chip } from '@mui/material'; + +export const PathChip = ({ label, highlight }: { label: string; highlight: boolean }) => ( + +); diff --git a/nym-wallet/src/components/TestMyNode/components/ResultsCard.tsx b/nym-wallet/src/components/TestMyNode/components/ResultsCard.tsx new file mode 100644 index 0000000000..c6837fea0f --- /dev/null +++ b/nym-wallet/src/components/TestMyNode/components/ResultsCard.tsx @@ -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, +}) => ( + + } + /> + {children} + +); diff --git a/nym-wallet/src/components/TestMyNode/components/ResultsCardDetail.tsx b/nym-wallet/src/components/TestMyNode/components/ResultsCardDetail.tsx new file mode 100644 index 0000000000..8819baf335 --- /dev/null +++ b/nym-wallet/src/components/TestMyNode/components/ResultsCardDetail.tsx @@ -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; +}) => ( + + {label} + + {detail} + {DescriptionIcon} + + +); diff --git a/nym-wallet/src/pages/bonding/node-settings/test-my-node/index.tsx b/nym-wallet/src/pages/bonding/node-settings/test-my-node/index.tsx new file mode 100644 index 0000000000..521d0dd7a3 --- /dev/null +++ b/nym-wallet/src/pages/bonding/node-settings/test-my-node/index.tsx @@ -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 ( + + {view === 'overview' && } + {view === 'start-test' && } + {view === 'results' && } + + ); +}; diff --git a/nym-wallet/src/svg-icons/node-path.tsx b/nym-wallet/src/svg-icons/node-path.tsx new file mode 100644 index 0000000000..6d87aefcfe --- /dev/null +++ b/nym-wallet/src/svg-icons/node-path.tsx @@ -0,0 +1,128 @@ +import React from 'react'; + +// fill="#FB6E4E" fill-opacity="0.05" +// text #1D2125 +// white bg #FBFBFB +// orange bg #FB6E4E + +const withHighlight = (shouldHighlight: boolean) => ({ + background: shouldHighlight ? '#FB6E4E' : '#FBFBFB', + text: shouldHighlight ? '#FB6E4E' : '#1D2125', + bgOpacity: shouldHighlight ? '0.05' : '1', +}); + +export const NodePath = ({ layer }: { layer: '1' | '2' | '3' | 'gateway' }) => ( + + + + + + + + + + + + + + + + + + + + + + +);