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:
+12
-27
@@ -4,19 +4,18 @@ import { Link, RouteComponentProps } from 'react-router-dom'
|
|||||||
import { createStyles, Theme, makeStyles } from '@material-ui/core/styles'
|
import { createStyles, Theme, makeStyles } from '@material-ui/core/styles'
|
||||||
import { ListItemText, ListItemIcon, ListItem, Divider, List, Drawer, Link as MUILink } from '@material-ui/core'
|
import { ListItemText, ListItemIcon, ListItem, Divider, List, Drawer, Link as MUILink } from '@material-ui/core'
|
||||||
import { OpenInNewSharp } from '@material-ui/icons'
|
import { OpenInNewSharp } from '@material-ui/icons'
|
||||||
import { Activity, FileText, DollarSign, Share2, Settings, Layers } from 'react-feather'
|
import { Home, FileText, DollarSign, Share2, Settings, Layers } from 'react-feather'
|
||||||
|
|
||||||
import SwarmLogoOrange from '../assets/swarm-logo-orange.svg'
|
import SwarmLogoOrange from '../assets/swarm-logo-orange.svg'
|
||||||
import { Health } from '@ethersphere/bee-js'
|
|
||||||
|
|
||||||
const drawerWidth = 240
|
const drawerWidth = 240
|
||||||
|
|
||||||
const navBarItems = [
|
const navBarItems = [
|
||||||
{
|
{
|
||||||
label: 'Status',
|
label: 'Info',
|
||||||
id: 'status',
|
id: 'info',
|
||||||
path: '/',
|
path: '/',
|
||||||
icon: Activity,
|
icon: Home,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Files',
|
label: 'Files',
|
||||||
@@ -83,8 +82,7 @@ const useStyles = makeStyles((theme: Theme) =>
|
|||||||
|
|
||||||
interface Props extends RouteComponentProps {
|
interface Props extends RouteComponentProps {
|
||||||
themeMode: string
|
themeMode: string
|
||||||
health: boolean
|
isOk: boolean
|
||||||
nodeHealth: Health | null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function SideBar(props: Props): ReactElement {
|
export default function SideBar(props: Props): ReactElement {
|
||||||
@@ -139,11 +137,11 @@ export default function SideBar(props: Props): ReactElement {
|
|||||||
</MUILink>
|
</MUILink>
|
||||||
</List>
|
</List>
|
||||||
<div style={{ position: 'fixed', bottom: 0, width: 'inherit', padding: '10px' }}>
|
<div style={{ position: 'fixed', bottom: 0, width: 'inherit', padding: '10px' }}>
|
||||||
<ListItem>
|
<Link to="/status" style={{ marginRight: '30px', color: 'inherit', textDecoration: 'none' }}>
|
||||||
<div style={{ marginRight: '30px' }}>
|
<ListItem>
|
||||||
<div
|
<span
|
||||||
style={{
|
style={{
|
||||||
backgroundColor: props.health ? '#32c48d' : '#c9201f',
|
backgroundColor: props.isOk ? '#32c48d' : '#c9201f',
|
||||||
marginRight: '7px',
|
marginRight: '7px',
|
||||||
height: '10px',
|
height: '10px',
|
||||||
width: '10px',
|
width: '10px',
|
||||||
@@ -151,22 +149,9 @@ export default function SideBar(props: Props): ReactElement {
|
|||||||
display: 'inline-block',
|
display: 'inline-block',
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<span>API</span>
|
<span>Node {props.isOk ? 'OK' : 'Error'}</span>
|
||||||
</div>
|
</ListItem>
|
||||||
<div>
|
</Link>
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
backgroundColor: props.nodeHealth?.status === 'ok' ? '#32c48d' : '#c9201f',
|
|
||||||
marginRight: '7px',
|
|
||||||
height: '10px',
|
|
||||||
width: '10px',
|
|
||||||
borderRadius: '50%',
|
|
||||||
display: 'inline-block',
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<span>Debug API</span>
|
|
||||||
</div>
|
|
||||||
</ListItem>
|
|
||||||
</div>
|
</div>
|
||||||
</Drawer>
|
</Drawer>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ export default function TroubleshootConnectionCard(): ReactElement {
|
|||||||
</Typography>
|
</Typography>
|
||||||
<div style={{ marginBottom: '20px', textAlign: 'center' }}>
|
<div style={{ marginBottom: '20px', textAlign: 'center' }}>
|
||||||
<strong>
|
<strong>
|
||||||
<Link to="/">Click to run status checks</Link> on your nodes connection or check out the{' '}
|
<Link to="/status">Click to run status checks</Link> on your nodes connection or check out the{' '}
|
||||||
<a href={process.env.REACT_APP_BEE_DOCS_HOST} target="_blank" rel="noreferrer">
|
<a href={process.env.REACT_APP_BEE_DOCS_HOST} target="_blank" rel="noreferrer">
|
||||||
Swarm Bee Docs
|
Swarm Bee Docs
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ const Dashboard = (props: Props): ReactElement => {
|
|||||||
|
|
||||||
const [themeMode, toggleThemeMode] = useState('light')
|
const [themeMode, toggleThemeMode] = useState('light')
|
||||||
|
|
||||||
const { isLoading, apiHealth, debugApiHealth } = useContext(Context)
|
const { isLoading, status } = useContext(Context)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const theme = localStorage.getItem('theme')
|
const theme = localStorage.getItem('theme')
|
||||||
@@ -56,7 +56,7 @@ const Dashboard = (props: Props): ReactElement => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<SideBar {...props} themeMode={themeMode} health={apiHealth} nodeHealth={debugApiHealth} />
|
<SideBar {...props} themeMode={themeMode} isOk={status.all} />
|
||||||
<NavBar themeMode={themeMode} />
|
<NavBar themeMode={themeMode} />
|
||||||
<ErrorBoundary>
|
<ErrorBoundary>
|
||||||
<main className={classes.content}>
|
<main className={classes.content}>
|
||||||
|
|||||||
@@ -0,0 +1,112 @@
|
|||||||
|
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 { 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,
|
||||||
|
isLatestBeeVersion,
|
||||||
|
latestUrl,
|
||||||
|
}: Props): ReactElement | null {
|
||||||
|
const classes = useStyles()
|
||||||
|
|
||||||
|
const [underlayAddressesVisible, setUnderlayAddresessVisible] = useState<boolean>(false)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card>
|
||||||
|
<CardContent className={classes.root}>
|
||||||
|
<>
|
||||||
|
<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
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
import { ReactElement, useContext } from 'react'
|
||||||
|
import { makeStyles, Theme, createStyles } from '@material-ui/core/styles'
|
||||||
|
|
||||||
|
import StatusCard from './StatusCard'
|
||||||
|
import EthereumAddressCard from '../../components/EthereumAddressCard'
|
||||||
|
import TroubleshootConnectionCard from '../../components/TroubleshootConnectionCard'
|
||||||
|
import { Context as BeeContext } from '../../providers/Bee'
|
||||||
|
|
||||||
|
const useStyles = makeStyles((theme: Theme) =>
|
||||||
|
createStyles({
|
||||||
|
root: {
|
||||||
|
width: '100%',
|
||||||
|
display: 'grid',
|
||||||
|
rowGap: theme.spacing(3),
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
|
export default function Status(): ReactElement {
|
||||||
|
const classes = useStyles()
|
||||||
|
|
||||||
|
const {
|
||||||
|
status,
|
||||||
|
latestUserVersion,
|
||||||
|
isLatestBeeVersion,
|
||||||
|
latestBeeVersionUrl,
|
||||||
|
topology,
|
||||||
|
nodeAddresses,
|
||||||
|
chequebookAddress,
|
||||||
|
} = useContext(BeeContext)
|
||||||
|
|
||||||
|
if (!status.all) return <TroubleshootConnectionCard />
|
||||||
|
|
||||||
|
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} />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -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>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -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
@@ -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 { 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 DebugConnectionCheck from './SetupSteps/DebugConnectionCheck'
|
||||||
import StatusCard from './StatusCard'
|
import NodeConnectionCheck from './SetupSteps/NodeConnectionCheck'
|
||||||
import EthereumAddressCard from '../../components/EthereumAddressCard'
|
import VersionCheck from './SetupSteps/VersionCheck'
|
||||||
import { Context as BeeContext } from '../../providers/Bee'
|
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) =>
|
const useStyles = makeStyles((theme: Theme) =>
|
||||||
createStyles({
|
createStyles({
|
||||||
root: {
|
root: {
|
||||||
|
padding: theme.spacing(2),
|
||||||
width: '100%',
|
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 classes = useStyles()
|
||||||
|
const [activeStep, setActiveStep] = useState(-1)
|
||||||
|
|
||||||
const {
|
const {
|
||||||
status,
|
status,
|
||||||
|
isLoading,
|
||||||
latestUserVersion,
|
latestUserVersion,
|
||||||
|
latestPublishedVersion,
|
||||||
isLatestBeeVersion,
|
isLatestBeeVersion,
|
||||||
latestBeeVersionUrl,
|
latestBeeVersionUrl,
|
||||||
topology,
|
topology,
|
||||||
nodeAddresses,
|
nodeAddresses,
|
||||||
chequebookAddress,
|
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 (
|
return (
|
||||||
<div className={classes.root}>
|
<Paper className={classes.root}>
|
||||||
<StatusCard
|
<Typography variant="h5" gutterBottom>
|
||||||
userBeeVersion={latestUserVersion}
|
Node Setup
|
||||||
isLatestBeeVersion={isLatestBeeVersion}
|
</Typography>
|
||||||
isOk={status.all}
|
<Stepper nonLinear activeStep={activeStep} orientation="vertical">
|
||||||
nodeTopology={topology}
|
{steps.map(({ label, isOk, component }, index) => (
|
||||||
latestUrl={latestBeeVersionUrl}
|
<Step key={label}>
|
||||||
nodeAddresses={nodeAddresses}
|
<StepLabel
|
||||||
/>
|
onClick={() => setActiveStep(index === activeStep ? steps.length : index)}
|
||||||
{nodeAddresses && chequebookAddress && (
|
StepIconComponent={() => {
|
||||||
<EthereumAddressCard nodeAddresses={nodeAddresses} chequebookAddress={chequebookAddress} />
|
if (isLoading) return <Autorenew style={{ height: '25px', cursor: 'pointer' }} />
|
||||||
)}
|
|
||||||
<NodeSetupWorkflow />
|
if (isOk) return <CheckCircle style={{ color: '#32c48d', height: '25px', cursor: 'pointer' }} />
|
||||||
</div>
|
|
||||||
|
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>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import AppRoute from './AppRoute'
|
|||||||
import Dashboard from '../layout/Dashboard'
|
import Dashboard from '../layout/Dashboard'
|
||||||
|
|
||||||
// pages
|
// pages
|
||||||
|
import Info from '../pages/info'
|
||||||
import Status from '../pages/status'
|
import Status from '../pages/status'
|
||||||
import Files from '../pages/files'
|
import Files from '../pages/files'
|
||||||
import Peers from '../pages/peers'
|
import Peers from '../pages/peers'
|
||||||
@@ -16,12 +17,13 @@ import Stamps from '../pages/stamps'
|
|||||||
|
|
||||||
const BaseRouter = (): ReactElement => (
|
const BaseRouter = (): ReactElement => (
|
||||||
<Switch>
|
<Switch>
|
||||||
<AppRoute exact path="/" layout={Dashboard} component={Status} />
|
<AppRoute exact path="/" layout={Dashboard} component={Info} />
|
||||||
<AppRoute exact path="/files/" layout={Dashboard} component={Files} />
|
<AppRoute exact path="/files/" layout={Dashboard} component={Files} />
|
||||||
<AppRoute exact path="/peers/" layout={Dashboard} component={Peers} />
|
<AppRoute exact path="/peers/" layout={Dashboard} component={Peers} />
|
||||||
<AppRoute exact path="/accounting/" layout={Dashboard} component={Accounting} />
|
<AppRoute exact path="/accounting/" layout={Dashboard} component={Accounting} />
|
||||||
<AppRoute exact path="/settings/" layout={Dashboard} component={Settings} />
|
<AppRoute exact path="/settings/" layout={Dashboard} component={Settings} />
|
||||||
<AppRoute exact path="/stamps/" layout={Dashboard} component={Stamps} />
|
<AppRoute exact path="/stamps/" layout={Dashboard} component={Stamps} />
|
||||||
|
<AppRoute exact path="/status" layout={Dashboard} component={Status} />
|
||||||
</Switch>
|
</Switch>
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user