feat: spdv-995 (#225)

This commit is contained in:
rolandlor
2026-03-13 13:15:26 +01:00
committed by Bálint Ujvári
parent 3c4d618cc8
commit fa8a26e80d
3 changed files with 173 additions and 44 deletions
+92 -20
View File
@@ -1,6 +1,6 @@
import { BeeModes } from '@ethersphere/bee-js'
import { Box, Divider, Drawer, Grid, Link as MUILink, List, Typography } from '@mui/material'
import { ReactElement, useContext } from 'react'
import { Box, Divider, Drawer, Grid, IconButton, Link as MUILink, List, Tooltip, Typography } from '@mui/material'
import { ReactElement, useContext, useState } from 'react'
import { Link } from 'react-router-dom'
import FilesIcon from 'remixicon-react/ArrowUpDownLineIcon'
import DocsIcon from 'remixicon-react/BookOpenLineIcon'
@@ -8,6 +8,8 @@ import ExternalLinkIcon from 'remixicon-react/ExternalLinkLineIcon'
import FileManagerIcon from 'remixicon-react/FolderOpenLineIcon'
import GithubIcon from 'remixicon-react/GithubFillIcon'
import HomeIcon from 'remixicon-react/Home3LineIcon'
import MenuFoldIcon from 'remixicon-react/MenuFoldLineIcon'
import MenuUnfoldIcon from 'remixicon-react/MenuUnfoldLineIcon'
import SettingsIcon from 'remixicon-react/Settings2LineIcon'
import AccountIcon from 'remixicon-react/Wallet3LineIcon'
import { makeStyles } from 'tss-react/mui'
@@ -23,26 +25,75 @@ import SideBarItem from './SideBarItem'
import SideBarStatus from './SideBarStatus'
const drawerWidth = 300
const drawerWidthCollapsed = 72
const drawerHeaderHeight = 56
const useStyles = makeStyles()(theme => ({
root: {
flexWrap: 'nowrap',
minHeight: '100vh',
paddingTop: theme.spacing(8),
minHeight: `calc(100vh - ${drawerHeaderHeight}px)`,
paddingBottom: theme.spacing(8),
},
drawer: {
width: drawerWidth,
flexShrink: 0,
transition: theme.transitions.create('width', {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.enteringScreen,
}),
},
drawerCollapsed: {
width: drawerWidthCollapsed,
transition: theme.transitions.create('width', {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.leavingScreen,
}),
},
drawerPaper: {
width: drawerWidth,
backgroundColor: '#212121',
zIndex: 988,
overflowX: 'hidden',
transition: theme.transitions.create('width', {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.enteringScreen,
}),
},
drawerPaperCollapsed: {
width: drawerWidthCollapsed,
transition: theme.transitions.create('width', {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.leavingScreen,
}),
},
header: {
display: 'flex',
alignItems: 'center',
height: drawerHeaderHeight,
borderBottom: '1px solid #2c2c2c',
paddingLeft: theme.spacing(2),
paddingRight: theme.spacing(2),
flexShrink: 0,
},
logo: {
marginLeft: theme.spacing(8),
marginRight: theme.spacing(8),
flex: 1,
display: 'flex',
alignItems: 'center',
overflow: 'hidden',
},
logoImg: {
maxWidth: '100%',
display: 'block',
},
toggleButton: {
color: '#9f9f9f',
'&:hover': {
color: '#f9f9f9',
backgroundColor: '#2c2c2c',
},
},
toggleButtonCollapsed: {
marginLeft: 'auto',
},
icon: {
height: theme.spacing(4),
@@ -70,6 +121,7 @@ export default function SideBar(): ReactElement {
const { classes } = useStyles()
const { isDesktop } = useContext(SettingsContext)
const { nodeInfo } = useContext(BeeContext)
const [isCollapsed, setIsCollapsed] = useState(false)
const navBarItems = [
{
@@ -103,13 +155,28 @@ export default function SideBar(): ReactElement {
]
return (
<Drawer className={classes.drawer} variant="permanent" anchor="left" classes={{ paper: classes.drawerPaper }}>
<Grid container direction="column" justifyContent="space-between" className={classes.root}>
<Grid className={classes.logo}>
<Link to={ROUTES.INFO}>
<img alt="swarm" src={isDesktop ? DesktopLogo : DashboardLogo} />
<Drawer
className={`${classes.drawer} ${isCollapsed ? classes.drawerCollapsed : ''}`}
variant="permanent"
anchor="left"
classes={{ paper: `${classes.drawerPaper} ${isCollapsed ? classes.drawerPaperCollapsed : ''}` }}
>
<div className={classes.header}>
{!isCollapsed && (
<Link to={ROUTES.INFO} className={classes.logo}>
<img alt="swarm" className={classes.logoImg} src={isDesktop ? DesktopLogo : DashboardLogo} />
</Link>
</Grid>
)}
<Tooltip title={isCollapsed ? 'Expand sidebar' : 'Collapse sidebar'} placement="right">
<IconButton
onClick={() => setIsCollapsed(!isCollapsed)}
className={`${classes.toggleButton} ${isCollapsed ? classes.toggleButtonCollapsed : ''}`}
>
{isCollapsed ? <MenuUnfoldIcon /> : <MenuFoldIcon />}
</IconButton>
</Tooltip>
</div>
<Grid container direction="column" justifyContent="space-between" className={classes.root}>
<Grid>
<List>
{navBarItems.map(p => (
@@ -120,6 +187,7 @@ export default function SideBar(): ReactElement {
path={p.path}
pathMatcherSubstring={p.pathMatcherSubstring}
label={p.label}
isCollapsed={isCollapsed}
/>
</Link>
))}
@@ -129,8 +197,9 @@ export default function SideBar(): ReactElement {
<MUILink href={BEE_DOCS_HOST} target="_blank" className={classes.link}>
<SideBarItem
iconStart={<DocsIcon className={classes.icon} />}
iconEnd={<ExternalLinkIcon className={classes.icon} color="#595959" />}
iconEnd={!isCollapsed ? <ExternalLinkIcon className={classes.icon} color="#595959" /> : undefined}
label={<span>Docs</span>}
isCollapsed={isCollapsed}
/>
</MUILink>
</List>
@@ -143,22 +212,25 @@ export default function SideBar(): ReactElement {
>
<SideBarItem
iconStart={<GithubIcon className={classes.icon} />}
iconEnd={<ExternalLinkIcon className={classes.icon} color="#595959" />}
iconEnd={!isCollapsed ? <ExternalLinkIcon className={classes.icon} color="#595959" /> : undefined}
label={<span>GitHub</span>}
isCollapsed={isCollapsed}
/>
</MUILink>
</List>
<Divider className={classes.divider} />
<Box mt={4}>
<Link to={ROUTES.TOP_UP_GIFT_CODE}>
<Typography align="center">Redeem gift code</Typography>
</Link>
</Box>
{!isCollapsed && (
<Box mt={4}>
<Link to={ROUTES.TOP_UP_GIFT_CODE}>
<Typography align="center">Redeem gift code</Typography>
</Link>
</Box>
)}
</Grid>
<Grid>
<List>
<Link to={ROUTES.STATUS} className={classes.link}>
<SideBarStatus path={ROUTES.STATUS} />
<SideBarStatus path={ROUTES.STATUS} isCollapsed={isCollapsed} />
</Link>
</List>
</Grid>
+33 -7
View File
@@ -1,4 +1,4 @@
import { ListItemButton, ListItemIcon, ListItemText } from '@mui/material'
import { ListItemButton, ListItemIcon, ListItemText, Tooltip } from '@mui/material'
import type { ReactElement, ReactNode } from 'react'
import { matchPath, useLocation } from 'react-router-dom'
import { makeStyles } from 'tss-react/mui'
@@ -24,14 +24,24 @@ const useItemStyles = makeStyles()(theme => ({
},
},
},
rootCollapsed: {
justifyContent: 'center',
paddingLeft: theme.spacing(1),
paddingRight: theme.spacing(1),
},
}))
const useStyles = makeStyles()(theme => ({
icon: {
color: 'inherit',
minWidth: 0,
},
activeIcon: {
color: theme.palette.primary.main,
minWidth: 0,
},
label: {
marginLeft: theme.spacing(2),
},
}))
@@ -41,9 +51,17 @@ interface Props {
path?: string
label: ReactNode
pathMatcherSubstring?: string
isCollapsed?: boolean
}
export default function SideBarItem({ iconStart, iconEnd, path, label, pathMatcherSubstring }: Props): ReactElement {
export default function SideBarItem({
iconStart,
iconEnd,
path,
label,
pathMatcherSubstring,
isCollapsed,
}: Props): ReactElement {
const { classes } = useStyles()
const { classes: itemClasses } = useItemStyles()
const location = useLocation()
@@ -52,10 +70,18 @@ export default function SideBarItem({ iconStart, iconEnd, path, label, pathMatch
: Boolean(path && matchPath(location.pathname, path))
return (
<ListItemButton className={itemClasses.root} selected={isSelected} disableRipple>
<ListItemIcon className={isSelected ? classes.activeIcon : classes.icon}>{iconStart}</ListItemIcon>
<ListItemText primary={label} />
<ListItemIcon className={isSelected ? classes.activeIcon : classes.icon}>{iconEnd}</ListItemIcon>
</ListItemButton>
<Tooltip title={isCollapsed ? label : ''} placement="right">
<ListItemButton
className={`${itemClasses.root} ${isCollapsed ? itemClasses.rootCollapsed : ''}`}
selected={isSelected}
disableRipple
>
<ListItemIcon className={isSelected ? classes.activeIcon : classes.icon}>{iconStart}</ListItemIcon>
{!isCollapsed && <ListItemText primary={label} className={classes.label} />}
{!isCollapsed && iconEnd && (
<ListItemIcon className={isSelected ? classes.activeIcon : classes.icon}>{iconEnd}</ListItemIcon>
)}
</ListItemButton>
</Tooltip>
)
}
+48 -17
View File
@@ -1,4 +1,4 @@
import { ListItemButton, ListItemIcon, ListItemText, Typography } from '@mui/material'
import { ListItemButton, ListItemIcon, ListItemText, Tooltip, Typography } from '@mui/material'
import { ReactElement, useContext } from 'react'
import { matchPath, useLocation } from 'react-router-dom'
import ArrowRight from 'remixicon-react/ArrowRightLineIcon'
@@ -18,8 +18,8 @@ const useStyles = makeStyles()(theme => ({
root: {
height: theme.spacing(4),
paddingLeft: theme.spacing(1),
paddingRight: theme.spacing(4),
paddingLeft: theme.spacing(0),
paddingRight: theme.spacing(0),
color: '#f9f9f9',
borderLeft: '0px solid rgba(0,0,0,0)',
'&.Mui-selected, &.Mui-selected:hover': {
@@ -46,31 +46,62 @@ const useStyles = makeStyles()(theme => ({
fontSize: '0.9rem',
whiteSpace: 'nowrap',
},
rootCollapsed: {
justifyContent: 'center',
paddingLeft: theme.spacing(1),
paddingRight: theme.spacing(1),
},
statusIcon: {
marginLeft: '30px',
minWidth: 0,
},
statusIconCollapsed: {
marginLeft: 0,
minWidth: 0,
},
statusText: {
marginLeft: theme.spacing(2),
},
}))
interface Props {
path?: string
isCollapsed?: boolean
}
export default function SideBarItem({ path }: Props): ReactElement {
export default function SideBarItem({ path, isCollapsed }: Props): ReactElement {
const { status, isLoading } = useContext(Context)
const { classes } = useStyles()
const location = useLocation()
const isSelected = Boolean(path && matchPath(location.pathname, path))
return (
<ListItemButton
classes={{ root: `${classes.root} ${status.all ? '' : classes.rootError}` }}
selected={isSelected}
disableRipple
>
<ListItemIcon style={{ marginLeft: '30px' }}>
<StatusIcon checkState={status.all} isLoading={isLoading} />
</ListItemIcon>
<ListItemText primary={<Typography className={classes.smallerText}>{`Node ${status.all}`}</Typography>} />
<ListItemIcon className={classes.icon}>
{status.all ? null : <ArrowRight className={classes.iconSmall} />}
</ListItemIcon>
</ListItemButton>
<Tooltip title={isCollapsed ? `Node ${status.all}` : ''} placement="right">
<ListItemButton
classes={{
root: `${classes.root} ${status.all ? '' : classes.rootError} ${isCollapsed ? classes.rootCollapsed : ''}`,
}}
selected={isSelected}
disableRipple
>
<ListItemIcon className={isCollapsed ? classes.statusIconCollapsed : classes.statusIcon}>
<StatusIcon checkState={status.all} isLoading={isLoading} />
</ListItemIcon>
{!isCollapsed && (
<>
<ListItemText
primary={
<Typography
className={`${classes.smallerText} ${classes.statusText}`}
>{`Node ${status.all}`}</Typography>
}
/>
<ListItemIcon className={classes.icon}>
{status.all ? null : <ArrowRight className={classes.iconSmall} />}
</ListItemIcon>
</>
)}
</ListItemButton>
</Tooltip>
)
}