5dadd73dfd
* fixed styled component * dynamic colour for isSelected * corrected type names * wrapped nav for routing and positioning of main section and dynamic colour for selected section * overview info panes added * quick refactor break out components separately * WorldMap implemented but not data * map changed and updated to shades correctly * live data in cards * added any types for react simple map * nested routing added but needs tidying and types refactored * added tooltip to worldmap * worldmap killed unused props * updated MUI version to stable v5 * dark mode and ContentCard refactor complete * refactor of DarkMode context and API into class and context setup * context refactor for multiple APIs at top level * mui typography used for error msging instead of jsx/html * added typeDefs for node api types * small changes to sx styling * added types for api responses and main context * promiseAll for better error handling of individual async calls * switch out to live API for country nodes * removal of unnecessary type any and shortening sx style block * routing and basic mixnodes table and linking * fixed TS error handling and ts exclude files * refactor of class API fetch reqs * renaming to more appropriate explorer-api * broken - passing to Fouad * fix for types in context main * mixnode detail page * rebasing back before fetch mixnode by ID was implemented * added basic cache for huge dump of mixnode data * broken mixnodes context * fixed mixnode detail fetch * added hardcoded BondBreakdown section * added 2 col table for detail page and small refactor of ApiState type for consistent use throughout app * basic chart with basic dark/theme implemented - no live data * added scrollToTop useRef for Detail page * tidied grid items * media qry for smaller screens * small changes * added live data to bonds breakdown 1/2 * small changes/tweaks * Bondbreakdown retrieves live data * mixnode stats using live data * added node status live data * uptime story added with live data * date formatting added * mixnode map * error handling for mixnode stats * error handling for port stats * improved error handling for table - unfinished * error handling for mixnode table * handle Loading state for 2colSmallTable * Uptime story loading handling * set up data grid component * remove mixnode value check as handled inside MixnodesDataGrid component * use loading prop in data grid component * undo unintentional code formatting * map blur and linkable data-grid added * getting ready for gateways and removing con logs * quickfix for map blur * PR comment changes * refactored data grid for reuseability * Link to open Big Dipper for Blocks * passing element to title instead of string for routing to Big Dipper * quick fix for element passed as title for contentCard * fix for colour coding nodes * nuts and bolts of search and results per page are working * media query for responsive search and no-per-page toolbar * broke out search and pagesize to separate toolbar * fix for going back to mixnodes datagrid and refetching * corrected typings for WorldMap * removed API for topojson * Cleaner implementation of formatting inline for datagrid * added Type to Datagrid Rows for mixnode * removed optional from type for Datagrid * added page listing the Gateway nodes * adding clickable location to handleSearch * tidying util functions and removing dead useEffect * Add missing constant * Validators link to Big Dipper * added validators link to side nav as per Issues card * SVG icons * PR tweak to move logic to routes * removed dead code post rebase * fixed light dark mode for DataGrid * light dark mode works on SVGs in Nav * moving logic back to Nav to avoid window object issues * neater ternary for SVG icons dark mode * Better Linking/Styling for cells * corrected prop/attr name in svg to Reactify it * moved api url to constants * SVGs dark now governed by context not props so reverting renderIcon method back to key value setup * percentage for bond total added * SVGs for Overview cards Mixnodes Gateways and Validators * decimalised formatted punks and % of bond for BondBreakdown card * number formatting via validator module * adding cossmjs math pkg * unfinished refactor BondBreakdown * first few ui tweaks * Adding google font Open Sans as per designs * DataGrid unstylable in theme so nuking in css * adding theming to Block Height card not hardcoded colours * DataGrid styling * Nav styling colours but without hover fix * theme for bond breakdown * killing con logs * Datagrid styling * Nav bar working * added lines to nav * removed cursive from fallback fonts * trimming and refactoring * removed dead isActive code from nav options * Color correction for theme on 2col table * Moved cell styles out to UniversalDatagrid for reuseability * Nav colors moved to theme * Removing comments and dead code * DataGrid UI improvements * theming for Overview content card * Bonds updated from UPUNK to PUNK * corrected SVG warning on stroke-width * added Boolean class instead of ternary * fixing up svg attr to jsx props * merging UPUNK changes into ui-tweaks * corrected SVG warning on stroke-width * added Boolean class instead of ternary * last instance of Boolean * BondBreakdown handles 0 delegations * formatting for webpack config and svgs * Add `npm run lint` and `npm run lint:fix` targets to `package.json` * Allow `.vscode` directories - exclude them individually like has been done already in the `.gitignore` directory * Add `vscode` action to run `eslint` on save for the `/explorer/**` sub-directory * eslint auto fix * Fix some easy eslint issues * removing grid pipes and pastel map colors * Grid xl lg values to align with Search Toolbar * GitHub Actions: do not trigger Rust actions when the paths are only `/explorer/**` See https://docs.github.com/en/actions/learn-github-actions/workflow-syntax-for-github-actions#onpushpull_requestpaths for details. * GitHub Actions: run eslint and annotate pull requests * socials added to Appbar and Footer * smaller darkmode icon for mobile * cleaner code for nav dark light selector * almost all lint fixes * post linting Nav fix * killed con log and removed unused dep * ref type and removed 1x ts ignore from worldMap * disabl nested tern w/ nav is refactored on diff br * icons smaller put into mui List format * Added hover effect to match DarkLight switch * ts ignore for worldMap vs no ts decl * parking changes * Flipped to MUI SVGs * re-added external links to Socials * nav functionality working * spacing on Mixnode detail page * datagrid alignment & detail page spacing * map but no datagrid yet * killed old SVGs now using mui icons * added palette instead of strings * Mixnode Map page working still needs tidying * better lg xl responsive on Overview and sanitized Page Titles * removed typography from imports as unused now * search, sort working & added LG XL responsiveness * Routing root reqs direct to Overview * basic 404 page and btn back to overview * killed fragments and comments * updated Bond total in column * Change bond col to type number not text * Added field to DataGrid and updated MixnodeToGridrow logic * Added type number so sorting works properly. * added %self to Detail page * basic scroll working desktop * delegations now popout and scroll according to designs * added stickyHeader and killed dead code * ExpandMore only renders if delegations exist * killed old svg icons * added theme to Overview SVGs * bringing Title into other pages * linting fix * pagination and spacing of gateways cols * linting fix * style override for pagination * added hamburger and changed appbar to fixed * bringing in other lint fix to pass linting * PR feedback changes * Add README.md for theme customisation * Add hook to get app state context * Add Nym theme typings to MUI `Theme` types * Use new theme provider * Fixing up components to use theme typings Updated Overview Footer and ContentCard Footer and Nav socials Title, Nav chevron and Nav SVGs Overview SVGs Light Dark switch BondBreakdown and 2 col table DataGrid and 2col Tables WorldMap UptimeChart and theme changes WorldMap colors merge changes added StatsCard for overview * Bug fix: do no close drawer when clicking on mixnodes or gateways * Theme primary colour set to orange highlight, so that default/primary actions are clear to the user. This fixes colours on the pagination page list. * Fixing up map projection * Map view uses stroke colour from theme * added useTheme from correct pkg * react types upgrade to kill SXProps issue * SXProps fix removing dead mixins from fixed AppBar * Scale of Map changed to see more countries * return type for main Context required * Fixed map so more countries show * type for useMainContext hook added * Remove unused file * Tidy up imports * Remove use of `any` by using strongly typed hook to get the app state * Remove module declaration so that @types/react-simple-maps is used * API map response changed to indexed object * Map view uses correct typings from `@types/react-simple-maps` and `d3-scale` * Make content responsive and fills the view when screen widest * Link network explorer in title to overview page * Increase size of card headers to differentiate * Fix column widths * Fixed icons showing incorrectly the stats card in mixnode detail view * Set default sort on mixnodes and gateways to be `bond` descending. There is an error in MUI data-grid that does not adjust the sort caret based on initial `sortModel` value. Needs investigation. * GitHub Action for deployment: prefix with network explorer to stop collisions with other deployment projects * Mixnode list: fix up header title for `host` * Fix up notification URLs and tidy up readme * Fix up license information Co-authored-by: Adrian Thompson <adrian@nymtech.net> Co-authored-by: Adrian Thompson <adrianthompson@Adrians-MacBook-Air.local> Co-authored-by: fmtabbara <fmtabbara@hotmail.co.uk> Co-authored-by: Aid19801 <adrianThompson19801@gmail.com>
398 lines
10 KiB
TypeScript
398 lines
10 KiB
TypeScript
/* eslint-disable no-nested-ternary */
|
|
import * as React from 'react';
|
|
import { Link } from 'react-router-dom';
|
|
import { ChevronLeft, ExpandLess, ExpandMore, Menu } from '@mui/icons-material';
|
|
import { CSSObject, styled, Theme, useTheme } from '@mui/material/styles';
|
|
import MuiLink from '@mui/material/Link';
|
|
import Box from '@mui/material/Box';
|
|
import ListItem from '@mui/material/ListItem';
|
|
import MuiDrawer from '@mui/material/Drawer';
|
|
import AppBar from '@mui/material/AppBar';
|
|
import Toolbar from '@mui/material/Toolbar';
|
|
import List from '@mui/material/List';
|
|
import Typography from '@mui/material/Typography';
|
|
import IconButton from '@mui/material/IconButton';
|
|
import ListItemButton from '@mui/material/ListItemButton';
|
|
import ListItemIcon from '@mui/material/ListItemIcon';
|
|
import ListItemText from '@mui/material/ListItemText';
|
|
import { NymLogoSVG } from 'src/icons/NymLogoSVG';
|
|
import { BIG_DIPPER, NYM_WEBSITE } from 'src/api/constants';
|
|
import { useMediaQuery } from '@mui/material';
|
|
import { OverviewSVG } from '../icons/OverviewSVG';
|
|
import { NetworkComponentsSVG } from '../icons/NetworksSVG';
|
|
import { NodemapSVG } from '../icons/NodemapSVG';
|
|
import { Socials } from './Socials';
|
|
import { Footer } from './Footer';
|
|
import { DarkLightSwitchDesktop, DarkLightSwitchMobile } from './Switch';
|
|
|
|
const drawerWidth = 300;
|
|
|
|
const openedMixin = (theme: Theme): CSSObject => ({
|
|
width: drawerWidth,
|
|
transition: theme.transitions.create('width', {
|
|
easing: theme.transitions.easing.sharp,
|
|
duration: theme.transitions.duration.enteringScreen,
|
|
}),
|
|
overflowX: 'hidden',
|
|
});
|
|
|
|
const closedMixin = (theme: Theme): CSSObject => ({
|
|
transition: theme.transitions.create('width', {
|
|
easing: theme.transitions.easing.sharp,
|
|
duration: theme.transitions.duration.leavingScreen,
|
|
}),
|
|
overflowX: 'hidden',
|
|
width: `calc(${theme.spacing(7)} + 1px)`,
|
|
});
|
|
|
|
const DrawerHeader = styled('div')(({ theme }) => ({
|
|
display: 'flex',
|
|
alignItems: 'center',
|
|
justifyContent: 'flex-end',
|
|
padding: theme.spacing(0, 1),
|
|
height: 64,
|
|
}));
|
|
|
|
const Drawer = styled(MuiDrawer, {
|
|
shouldForwardProp: (prop) => prop !== 'open',
|
|
})(({ theme, open }) => ({
|
|
width: drawerWidth,
|
|
flexShrink: 0,
|
|
whiteSpace: 'nowrap',
|
|
boxSizing: 'border-box',
|
|
...(open && {
|
|
...openedMixin(theme),
|
|
'& .MuiDrawer-paper': openedMixin(theme),
|
|
}),
|
|
...(!open && {
|
|
...closedMixin(theme),
|
|
'& .MuiDrawer-paper': closedMixin(theme),
|
|
}),
|
|
}));
|
|
|
|
type navOptionType = {
|
|
id: number;
|
|
isActive?: boolean;
|
|
url: string;
|
|
title: string;
|
|
Icon?: React.ReactNode;
|
|
nested?: navOptionType[];
|
|
isExpandedChild?: boolean;
|
|
};
|
|
|
|
const originalNavOptions: navOptionType[] = [
|
|
{
|
|
id: 0,
|
|
isActive: false,
|
|
url: '/overview',
|
|
title: 'Overview',
|
|
Icon: <OverviewSVG />,
|
|
},
|
|
{
|
|
id: 1,
|
|
isActive: false,
|
|
url: '/network-components',
|
|
title: 'Network Components',
|
|
Icon: <NetworkComponentsSVG />,
|
|
nested: [
|
|
{
|
|
id: 3,
|
|
url: '/network-components/mixnodes',
|
|
title: 'Mixnodes',
|
|
},
|
|
{
|
|
id: 4,
|
|
url: '/network-components/gateways',
|
|
title: 'Gateways',
|
|
},
|
|
{
|
|
id: 5,
|
|
url: `${BIG_DIPPER}/validators`,
|
|
title: 'Validators',
|
|
},
|
|
],
|
|
},
|
|
{
|
|
id: 2,
|
|
isActive: false,
|
|
url: '/nodemap',
|
|
title: 'Nodemap',
|
|
Icon: <NodemapSVG />,
|
|
},
|
|
];
|
|
|
|
type ExpandableButtonType = {
|
|
id: number;
|
|
title: string;
|
|
url: string;
|
|
isActive?: boolean;
|
|
Icon?: React.ReactNode;
|
|
nested?: navOptionType[];
|
|
isChild?: boolean;
|
|
openDrawer: () => void;
|
|
closeDrawer: () => void;
|
|
drawIsOpen: boolean;
|
|
setToActive: (num: number) => void;
|
|
};
|
|
|
|
const ExpandableButton: React.FC<ExpandableButtonType> = ({
|
|
id,
|
|
url,
|
|
setToActive,
|
|
isActive,
|
|
openDrawer,
|
|
closeDrawer,
|
|
drawIsOpen,
|
|
Icon,
|
|
title,
|
|
nested,
|
|
isChild,
|
|
}) => {
|
|
const [dynamicStyle, setDynamicStyle] = React.useState({});
|
|
const [nestedOptions, toggleNestedOptions] = React.useState(false);
|
|
const [isExternal, setIsExternal] = React.useState<boolean>(false);
|
|
const { palette } = useTheme();
|
|
|
|
const handleClick = () => {
|
|
openDrawer();
|
|
if (title === 'Network Components' && nested) {
|
|
toggleNestedOptions(!nestedOptions);
|
|
}
|
|
setToActive(id);
|
|
};
|
|
|
|
React.useEffect(() => {
|
|
if (url) {
|
|
setIsExternal(url.includes('http'));
|
|
}
|
|
if (nested) {
|
|
setDynamicStyle({
|
|
background: '#242C3D',
|
|
borderRight: `3px solid ${palette.nym.highlight}`,
|
|
});
|
|
}
|
|
if (isChild) {
|
|
setDynamicStyle({
|
|
background: '#3C4558',
|
|
fontWeight: 800,
|
|
});
|
|
}
|
|
if (!nested && !isChild) {
|
|
setDynamicStyle({
|
|
background: '#242C3D',
|
|
borderRight: `3px solid ${palette.nym.highlight}`,
|
|
});
|
|
}
|
|
}, [url]);
|
|
|
|
React.useEffect(() => {
|
|
if (!drawIsOpen && nestedOptions) {
|
|
toggleNestedOptions(false);
|
|
}
|
|
}, [drawIsOpen]);
|
|
|
|
return (
|
|
<>
|
|
<ListItem
|
|
disablePadding
|
|
disableGutters
|
|
component={!nested ? Link : 'div'}
|
|
to={isExternal ? { pathname: url } : url}
|
|
target={isExternal ? '_blank' : ''}
|
|
sx={
|
|
isActive
|
|
? dynamicStyle
|
|
: { background: '#111826', borderRight: 'none' }
|
|
}
|
|
>
|
|
<ListItemButton
|
|
onClick={handleClick}
|
|
sx={{
|
|
pt: 2,
|
|
pb: 2,
|
|
background: isChild ? '#3C4558' : 'none',
|
|
}}
|
|
>
|
|
<ListItemIcon>{Icon}</ListItemIcon>
|
|
<ListItemText
|
|
primary={title}
|
|
sx={{
|
|
color: palette.nym.networkExplorer.nav.text,
|
|
}}
|
|
primaryTypographyProps={{
|
|
style: {
|
|
fontWeight: isActive ? 800 : 300,
|
|
},
|
|
}}
|
|
/>
|
|
{nested && nestedOptions && <ExpandLess />}
|
|
{nested && !nestedOptions && <ExpandMore />}
|
|
</ListItemButton>
|
|
</ListItem>
|
|
{nestedOptions &&
|
|
nested?.map((each) => (
|
|
<ExpandableButton
|
|
id={each.id}
|
|
url={each.url}
|
|
key={each.title}
|
|
title={each.title}
|
|
openDrawer={openDrawer}
|
|
drawIsOpen={drawIsOpen}
|
|
closeDrawer={closeDrawer}
|
|
setToActive={setToActive}
|
|
isChild
|
|
/>
|
|
))}
|
|
</>
|
|
);
|
|
};
|
|
|
|
ExpandableButton.defaultProps = {
|
|
Icon: null,
|
|
nested: undefined,
|
|
isChild: false,
|
|
isActive: false,
|
|
};
|
|
|
|
export const Nav: React.FC = ({ children }) => {
|
|
const [navOptionsState, updateNavOptionsState] =
|
|
React.useState(originalNavOptions);
|
|
const [open, setOpen] = React.useState(false);
|
|
const theme = useTheme();
|
|
const isMobile = useMediaQuery(theme.breakpoints.down('md'));
|
|
|
|
const setToActive = (id: number) => {
|
|
const updated = navOptionsState.map((option) => ({
|
|
...option,
|
|
isActive: option.id === id,
|
|
}));
|
|
updateNavOptionsState(updated);
|
|
};
|
|
|
|
const handleDrawerOpen = () => {
|
|
setOpen(true);
|
|
};
|
|
|
|
const handleDrawerClose = () => {
|
|
setOpen(false);
|
|
};
|
|
|
|
return (
|
|
<>
|
|
<Box sx={{ display: 'flex' }}>
|
|
<AppBar
|
|
sx={{
|
|
background: theme.palette.nym.networkExplorer.nav.background,
|
|
}}
|
|
>
|
|
<Toolbar
|
|
disableGutters
|
|
sx={{
|
|
display: 'flex',
|
|
justifyContent: 'space-between',
|
|
}}
|
|
>
|
|
<Box
|
|
sx={{
|
|
display: 'flex',
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
justifyContent: 'space-between',
|
|
width: 205,
|
|
}}
|
|
>
|
|
<IconButton component="a" href={NYM_WEBSITE} target="_blank">
|
|
<NymLogoSVG />
|
|
</IconButton>
|
|
<Typography
|
|
variant="h6"
|
|
noWrap
|
|
sx={{
|
|
color: theme.palette.nym.networkExplorer.nav.text,
|
|
fontSize: '18px',
|
|
}}
|
|
>
|
|
<MuiLink
|
|
component={Link}
|
|
to="/overview"
|
|
underline="none"
|
|
color="inherit"
|
|
>
|
|
Network Explorer
|
|
</MuiLink>
|
|
</Typography>
|
|
</Box>
|
|
<Box
|
|
sx={{
|
|
mr: 2,
|
|
alignItems: 'center',
|
|
display: 'flex',
|
|
}}
|
|
>
|
|
{!isMobile && (
|
|
<Box
|
|
sx={{
|
|
display: 'flex',
|
|
flexDirection: 'row',
|
|
width: 'auto',
|
|
pr: 0,
|
|
pl: 2,
|
|
justifyContent: 'flex-end',
|
|
alignItems: 'center',
|
|
}}
|
|
>
|
|
<Socials />
|
|
<DarkLightSwitchDesktop defaultChecked />
|
|
</Box>
|
|
)}
|
|
{isMobile && <DarkLightSwitchMobile />}
|
|
</Box>
|
|
</Toolbar>
|
|
</AppBar>
|
|
<Drawer
|
|
variant="permanent"
|
|
open={open}
|
|
sx={{
|
|
background: theme.palette.nym.networkExplorer.nav.background,
|
|
}}
|
|
>
|
|
<DrawerHeader
|
|
sx={{
|
|
justifyContent: open ? 'flex-end' : 'center',
|
|
paddingLeft: 0,
|
|
}}
|
|
>
|
|
<IconButton
|
|
onClick={open ? handleDrawerClose : handleDrawerOpen}
|
|
sx={{
|
|
padding: 0,
|
|
ml: '7px',
|
|
color: theme.palette.nym.networkExplorer.nav.text,
|
|
}}
|
|
>
|
|
{open ? <ChevronLeft /> : <Menu />}
|
|
</IconButton>
|
|
</DrawerHeader>
|
|
|
|
<List sx={{ pt: 0, pb: 0 }}>
|
|
{navOptionsState.map((props) => (
|
|
<ExpandableButton
|
|
setToActive={setToActive}
|
|
key={props.url}
|
|
openDrawer={handleDrawerOpen}
|
|
drawIsOpen={open}
|
|
closeDrawer={handleDrawerClose}
|
|
{...props}
|
|
/>
|
|
))}
|
|
</List>
|
|
</Drawer>
|
|
<Box sx={{ width: '100%', p: 4, mt: 7 }}>
|
|
{children}
|
|
<Footer />
|
|
</Box>
|
|
</Box>
|
|
</>
|
|
);
|
|
};
|