feat: separate info and status page (#183)

* feat: separate info and status page

* chore: remove the detection of step to expand
This commit is contained in:
Vojtech Simetka
2021-08-25 17:41:17 +02:00
committed by GitHub
parent 766fe96d1c
commit 02a7bff733
9 changed files with 306 additions and 353 deletions
-170
View File
@@ -1,170 +0,0 @@
import { ReactElement, useEffect, useState, useContext } from 'react'
import { makeStyles, Theme, createStyles } from '@material-ui/core/styles'
import { Typography, Paper, Button, Step, StepLabel, StepContent, Stepper, StepButton } from '@material-ui/core/'
import { CheckCircle, Error, ExpandLessSharp, ExpandMoreSharp, Autorenew } from '@material-ui/icons/'
import DebugConnectionCheck from './SetupSteps/DebugConnectionCheck'
import NodeConnectionCheck from './SetupSteps/NodeConnectionCheck'
import VersionCheck from './SetupSteps/VersionCheck'
import EthereumConnectionCheck from './SetupSteps/EthereumConnectionCheck'
import ChequebookDeployFund from './SetupSteps/ChequebookDeployFund'
import PeerConnection from './SetupSteps/PeerConnection'
import { Context } from '../../providers/Bee'
const useStyles = makeStyles((theme: Theme) =>
createStyles({
root: {
padding: theme.spacing(2),
width: '100%',
},
button: {
marginTop: theme.spacing(1),
marginRight: theme.spacing(1),
},
actionsContainer: {
margin: theme.spacing(2),
},
}),
)
interface Step {
label: string
isOk: boolean
component: ReactElement
}
export default function NodeSetupWorkflow(): ReactElement {
const classes = useStyles()
const [activeStep, setActiveStep] = useState(-1)
const {
status,
isLoading,
latestUserVersion,
latestPublishedVersion,
isLatestBeeVersion,
latestBeeVersionUrl,
topology,
nodeAddresses,
chequebookAddress,
} = useContext(Context)
const steps: Step[] = [
{
label: 'Connected to Node DebugAPI',
isOk: status.debugApiConnection,
component: <DebugConnectionCheck isOk={status.debugApiConnection} />,
},
{
label: 'Running latest Bee version',
isOk: status.version,
component: (
<VersionCheck
isOk={status.version}
isLatestBeeVersion={isLatestBeeVersion}
userVersion={latestUserVersion}
latestVersion={latestPublishedVersion}
latestUrl={latestBeeVersionUrl}
/>
),
},
{
label: 'Connected to xDai Blockchain',
isOk: status.blockchainConnection,
component: <EthereumConnectionCheck isOk={status.blockchainConnection} nodeAddresses={nodeAddresses} />,
},
{
label: 'Deployed and Funded Chequebook',
isOk: status.chequebook,
component: (
<ChequebookDeployFund chequebookAddress={chequebookAddress?.chequebookAddress} isOk={status.chequebook} />
),
},
{
label: 'Connected to Node API',
isOk: status.apiConnection,
component: <NodeConnectionCheck isOk={status.apiConnection} />,
},
{
label: 'Connected to Peers',
isOk: status.topology,
component: <PeerConnection isOk={status.topology} topology={topology} />,
},
]
useEffect(() => {
// If the user already changed the active step we don't want to overwrite it
if (activeStep >= 0 && activeStep < steps.length) return
// If any step is not fully loaded yet return
if (!isLoading) return
// Select first step that is not OK
// This is deliberately a for loop (and not forEach) so that we can terminate the useEffect from within the cycle
for (let i = 0; i < steps.length; ++i) {
if (!steps[i].isOk) {
setActiveStep(i)
return
}
}
}, [steps])
const handleNext = () => {
setActiveStep(prevActiveStep => prevActiveStep + 1)
}
const handleBack = () => {
setActiveStep(prevActiveStep => prevActiveStep - 1)
}
return (
<Paper className={classes.root}>
<Typography variant="h5" gutterBottom>
Node Setup
</Typography>
<Stepper nonLinear activeStep={activeStep} orientation="vertical">
{steps.map(({ label, isOk, component }, index) => (
<Step key={label}>
<StepLabel
onClick={() => setActiveStep(index === activeStep ? steps.length : index)}
StepIconComponent={() => {
if (isLoading) return <Autorenew style={{ height: '25px', cursor: 'pointer' }} />
if (isOk) return <CheckCircle style={{ color: '#32c48d', height: '25px', cursor: 'pointer' }} />
return <Error style={{ color: '#c9201f', height: '25px', cursor: 'pointer' }} />
}}
>
<StepButton
disabled={isLoading}
onClick={() => setActiveStep(index === activeStep ? steps.length : index)}
style={{ justifyContent: 'space-between' }}
>
<div style={{ display: 'flex' }}>
<div style={{ marginTop: '5px' }}>{label}</div>
<div style={{ marginLeft: '12px' }}>
{index === activeStep ? <ExpandLessSharp /> : <ExpandMoreSharp />}
</div>
</div>
</StepButton>
</StepLabel>
<StepContent>
<Typography component="div">{component}</Typography>
<div className={classes.actionsContainer}>
<div>
<Button disabled={activeStep === 0} onClick={handleBack} className={classes.button}>
Back
</Button>
<Button variant="contained" color="primary" onClick={handleNext} className={classes.button}>
{index < steps.length - 1 ? 'Next' : 'Finish'}
</Button>
</div>
</div>
</StepContent>
</Step>
))}
</Stepper>
</Paper>
)
}
-129
View File
@@ -1,129 +0,0 @@
import { ReactElement, useState } from 'react'
import { Link } from 'react-router-dom'
import { createStyles, makeStyles } from '@material-ui/core/styles'
import { Card, CardContent, Typography, Chip, Button } from '@material-ui/core/'
import { CheckCircle, Error, ArrowRight, ArrowDropUp } from '@material-ui/icons/'
import { NodeAddresses, Topology } from '@ethersphere/bee-js'
const useStyles = makeStyles(() =>
createStyles({
root: {
display: 'flex',
flex: '1 1 auto',
flexDirection: 'column',
},
status: {
color: '#2145a0',
backgroundColor: '#e1effe',
},
}),
)
interface Props {
nodeAddresses: NodeAddresses | null
nodeTopology: Topology | null
userBeeVersion?: string
isLatestBeeVersion: boolean
isOk: boolean
latestUrl: string
}
function StatusCard({
userBeeVersion,
nodeAddresses,
nodeTopology,
isOk,
isLatestBeeVersion,
latestUrl,
}: Props): ReactElement | null {
const classes = useStyles()
const [underlayAddressesVisible, setUnderlayAddresessVisible] = useState<boolean>(false)
return (
<Card>
<CardContent className={classes.root}>
<Typography component="h5" variant="h5" style={{ display: 'flex', justifyContent: 'space-between' }}>
{isOk && (
<div>
<CheckCircle style={{ color: '#32c48d', marginRight: '7px' }} />
<span>Your Bee node is running as expected</span>
</div>
)}
{!isOk && (
<div>
<Error style={{ color: '#c9201f', marginRight: '7px' }} />
<span>Could not connect to Bee Node</span>
</div>
)}
</Typography>
{isOk && (
<>
<div style={{ marginBottom: '20px' }}>
<span style={{ marginRight: '20px' }}>Discovered Nodes: {nodeTopology?.population}</span>
<span style={{ marginRight: '20px' }}>
<span>Connected Peers: </span>
<Link to="/peers/">{nodeTopology?.connected}</Link>
</span>
</div>
<div>
<Typography component="div" variant="subtitle2" gutterBottom>
<span>AGENT: </span>
<a href="https://github.com/ethersphere/bee" rel="noreferrer" target="_blank">
Bee
</a>{' '}
<span>{userBeeVersion || '-'}</span>
{isLatestBeeVersion ? (
<Chip
style={{ marginLeft: '7px', color: '#2145a0' }}
size="small"
label="latest"
className={classes.status}
/>
) : (
<Button size="small" variant="outlined" href={latestUrl}>
update
</Button>
)}
</Typography>
<Typography component="div" variant="subtitle2" gutterBottom>
<span>PUBLIC KEY: </span>
<span>{nodeAddresses?.publicKey ? nodeAddresses.publicKey : '-'}</span>
</Typography>
<Typography component="div" variant="subtitle2" gutterBottom>
<span>PSS PUBLIC KEY: </span>
<span>{nodeAddresses?.pssPublicKey ? nodeAddresses.pssPublicKey : '-'}</span>
</Typography>
<Typography component="div" variant="subtitle2" gutterBottom>
<span>OVERLAY ADDRESS (PEER ID): </span>
<span>{nodeAddresses?.overlay ? nodeAddresses.overlay : '-'}</span>
</Typography>
<Typography component="div" variant="subtitle2" gutterBottom>
<Typography component="div" onClick={() => setUnderlayAddresessVisible(!underlayAddressesVisible)}>
<Button color="primary" style={{ padding: 0, marginTop: '6px' }}>
{underlayAddressesVisible ? (
<ArrowDropUp style={{ fontSize: '12px' }} />
) : (
<ArrowRight style={{ fontSize: '12px' }} />
)}
<span>Underlay Addresses</span>
</Button>
</Typography>
{underlayAddressesVisible && (
<div>
{nodeAddresses?.underlay.map(item => (
<li key={item}>{item}</li>
))}
</div>
)}
</Typography>
</div>
</>
)}
</CardContent>
</Card>
)
}
export default StatusCard
+127 -23
View File
@@ -1,48 +1,152 @@
import { ReactElement, useContext } from 'react'
import { ReactElement, useState, useContext } from 'react'
import { makeStyles, Theme, createStyles } from '@material-ui/core/styles'
import { Typography, Paper, Button, Step, StepLabel, StepContent, Stepper, StepButton } from '@material-ui/core/'
import { CheckCircle, Error, ExpandLessSharp, ExpandMoreSharp, Autorenew } from '@material-ui/icons/'
import NodeSetupWorkflow from './NodeSetupWorkflow'
import StatusCard from './StatusCard'
import EthereumAddressCard from '../../components/EthereumAddressCard'
import { Context as BeeContext } from '../../providers/Bee'
import DebugConnectionCheck from './SetupSteps/DebugConnectionCheck'
import NodeConnectionCheck from './SetupSteps/NodeConnectionCheck'
import VersionCheck from './SetupSteps/VersionCheck'
import EthereumConnectionCheck from './SetupSteps/EthereumConnectionCheck'
import ChequebookDeployFund from './SetupSteps/ChequebookDeployFund'
import PeerConnection from './SetupSteps/PeerConnection'
import { Context } from '../../providers/Bee'
const useStyles = makeStyles((theme: Theme) =>
createStyles({
root: {
padding: theme.spacing(2),
width: '100%',
display: 'grid',
rowGap: theme.spacing(3),
},
button: {
marginTop: theme.spacing(1),
marginRight: theme.spacing(1),
},
actionsContainer: {
margin: theme.spacing(2),
},
}),
)
export default function Status(): ReactElement {
interface Step {
label: string
isOk: boolean
component: ReactElement
}
export default function NodeSetupWorkflow(): ReactElement {
const classes = useStyles()
const [activeStep, setActiveStep] = useState(-1)
const {
status,
isLoading,
latestUserVersion,
latestPublishedVersion,
isLatestBeeVersion,
latestBeeVersionUrl,
topology,
nodeAddresses,
chequebookAddress,
} = useContext(BeeContext)
} = useContext(Context)
const steps: Step[] = [
{
label: 'Connected to Node DebugAPI',
isOk: status.debugApiConnection,
component: <DebugConnectionCheck isOk={status.debugApiConnection} />,
},
{
label: 'Running latest Bee version',
isOk: status.version,
component: (
<VersionCheck
isOk={status.version}
isLatestBeeVersion={isLatestBeeVersion}
userVersion={latestUserVersion}
latestVersion={latestPublishedVersion}
latestUrl={latestBeeVersionUrl}
/>
),
},
{
label: 'Connected to xDai Blockchain',
isOk: status.blockchainConnection,
component: <EthereumConnectionCheck isOk={status.blockchainConnection} nodeAddresses={nodeAddresses} />,
},
{
label: 'Deployed and Funded Chequebook',
isOk: status.chequebook,
component: (
<ChequebookDeployFund chequebookAddress={chequebookAddress?.chequebookAddress} isOk={status.chequebook} />
),
},
{
label: 'Connected to Node API',
isOk: status.apiConnection,
component: <NodeConnectionCheck isOk={status.apiConnection} />,
},
{
label: 'Connected to Peers',
isOk: status.topology,
component: <PeerConnection isOk={status.topology} topology={topology} />,
},
]
const handleNext = () => {
setActiveStep(prevActiveStep => prevActiveStep + 1)
}
const handleBack = () => {
setActiveStep(prevActiveStep => prevActiveStep - 1)
}
return (
<div className={classes.root}>
<StatusCard
userBeeVersion={latestUserVersion}
isLatestBeeVersion={isLatestBeeVersion}
isOk={status.all}
nodeTopology={topology}
latestUrl={latestBeeVersionUrl}
nodeAddresses={nodeAddresses}
/>
{nodeAddresses && chequebookAddress && (
<EthereumAddressCard nodeAddresses={nodeAddresses} chequebookAddress={chequebookAddress} />
)}
<NodeSetupWorkflow />
</div>
<Paper className={classes.root}>
<Typography variant="h5" gutterBottom>
Node Setup
</Typography>
<Stepper nonLinear activeStep={activeStep} orientation="vertical">
{steps.map(({ label, isOk, component }, index) => (
<Step key={label}>
<StepLabel
onClick={() => setActiveStep(index === activeStep ? steps.length : index)}
StepIconComponent={() => {
if (isLoading) return <Autorenew style={{ height: '25px', cursor: 'pointer' }} />
if (isOk) return <CheckCircle style={{ color: '#32c48d', height: '25px', cursor: 'pointer' }} />
return <Error style={{ color: '#c9201f', height: '25px', cursor: 'pointer' }} />
}}
>
<StepButton
disabled={isLoading}
onClick={() => setActiveStep(index === activeStep ? steps.length : index)}
style={{ justifyContent: 'space-between' }}
>
<div style={{ display: 'flex' }}>
<div style={{ marginTop: '5px' }}>{label}</div>
<div style={{ marginLeft: '12px' }}>
{index === activeStep ? <ExpandLessSharp /> : <ExpandMoreSharp />}
</div>
</div>
</StepButton>
</StepLabel>
<StepContent>
<Typography component="div">{component}</Typography>
<div className={classes.actionsContainer}>
<div>
<Button disabled={activeStep === 0} onClick={handleBack} className={classes.button}>
Back
</Button>
<Button variant="contained" color="primary" onClick={handleNext} className={classes.button}>
{index < steps.length - 1 ? 'Next' : 'Finish'}
</Button>
</div>
</div>
</StepContent>
</Step>
))}
</Stepper>
</Paper>
)
}