use components from react-components lib

This commit is contained in:
fmtabbara
2024-07-08 12:37:17 +01:00
parent 3c1939953c
commit 1688eb9477
11 changed files with 543 additions and 542 deletions
@@ -1,13 +1,13 @@
import * as React from 'react'
import { Box, Typography } from '@mui/material'
import { useTheme } from '@mui/material/styles'
import { Tooltip } from '@nymproject/react/tooltip/Tooltip'
import * as React from "react";
import { Box, Typography } from "@mui/material";
import { useTheme } from "@mui/material/styles";
import { Tooltip } from "@nymproject/react";
export const CustomColumnHeading: FCWithChildren<{
headingTitle: string
tooltipInfo?: string
headingTitle: string;
tooltipInfo?: string;
}> = ({ headingTitle, tooltipInfo }) => {
const theme = useTheme()
const theme = useTheme();
return (
<Box alignItems="center" display="flex">
@@ -26,5 +26,5 @@ export const CustomColumnHeading: FCWithChildren<{
{headingTitle}
</Typography>
</Box>
)
}
);
};
@@ -1,35 +1,35 @@
'use client'
"use client";
import React, { useState } from 'react'
import { Box, SxProps } from '@mui/material'
import { IdentityKeyFormField } from '@nymproject/react/mixnodes/IdentityKeyFormField'
import { CurrencyFormField } from '@nymproject/react/currency/CurrencyFormField'
import { CurrencyDenom, DecCoin } from '@nymproject/types'
import { useWalletContext } from '@/app/context/wallet'
import { urls } from '@/app/utils'
import { useDelegationsContext } from '@/app/context/delegations'
import { validateAmount } from '@/app/utils/currency'
import { SimpleModal } from './SimpleModal'
import { ModalListItem } from './ModalListItem'
import { DelegationModalProps } from './DelegationModal'
import React, { useState } from "react";
import { Box, SxProps } from "@mui/material";
import { IdentityKeyFormField } from "@nymproject/react";
import { CurrencyFormField } from "@nymproject/react";
import { CurrencyDenom, DecCoin } from "@nymproject/types";
import { useWalletContext } from "@/app/context/wallet";
import { urls } from "@/app/utils";
import { useDelegationsContext } from "@/app/context/delegations";
import { validateAmount } from "@/app/utils/currency";
import { SimpleModal } from "./SimpleModal";
import { ModalListItem } from "./ModalListItem";
import { DelegationModalProps } from "./DelegationModal";
const MIN_AMOUNT_TO_DELEGATE = 10
const MIN_AMOUNT_TO_DELEGATE = 10;
type Props = {
mixId: number
identityKey: string
header?: string
buttonText?: string
rewardInterval?: string
estimatedReward?: number
profitMarginPercentage?: string | null
nodeUptimePercentage?: number | null
denom: CurrencyDenom
sx?: SxProps
backdropProps?: object
onClose: () => void
onOk?: (delegationModalProps: DelegationModalProps) => void
}
mixId: number;
identityKey: string;
header?: string;
buttonText?: string;
rewardInterval?: string;
estimatedReward?: number;
profitMarginPercentage?: string | null;
nodeUptimePercentage?: number | null;
denom: CurrencyDenom;
sx?: SxProps;
backdropProps?: object;
onClose: () => void;
onOk?: (delegationModalProps: DelegationModalProps) => void;
};
export const DelegateModal = ({
mixId,
@@ -40,106 +40,106 @@ export const DelegateModal = ({
sx,
}: Props) => {
const [amount, setAmount] = useState<DecCoin | undefined>({
amount: '10',
denom: 'nym',
})
const [isValidated, setValidated] = useState<boolean>(false)
const [errorAmount, setErrorAmount] = useState<string | undefined>()
amount: "10",
denom: "nym",
});
const [isValidated, setValidated] = useState<boolean>(false);
const [errorAmount, setErrorAmount] = useState<string | undefined>();
const { address, balance } = useWalletContext()
const { handleDelegate } = useDelegationsContext()
const { address, balance } = useWalletContext();
const { handleDelegate } = useDelegationsContext();
const validate = async () => {
let newValidatedValue = true
let errorAmountMessage
let newValidatedValue = true;
let errorAmountMessage;
if (amount && !(await validateAmount(amount.amount, '0'))) {
newValidatedValue = false
errorAmountMessage = 'Please enter a valid amount'
if (amount && !(await validateAmount(amount.amount, "0"))) {
newValidatedValue = false;
errorAmountMessage = "Please enter a valid amount";
}
if (amount && +amount.amount < MIN_AMOUNT_TO_DELEGATE) {
errorAmountMessage = `Min. delegation amount: ${MIN_AMOUNT_TO_DELEGATE} ${denom.toUpperCase()}`
newValidatedValue = false
errorAmountMessage = `Min. delegation amount: ${MIN_AMOUNT_TO_DELEGATE} ${denom.toUpperCase()}`;
newValidatedValue = false;
}
if (!amount?.amount.length) {
newValidatedValue = false
newValidatedValue = false;
}
if (amount && balance.data && +balance.data - +amount.amount <= 0) {
errorAmountMessage = 'Not enough funds'
newValidatedValue = false
errorAmountMessage = "Not enough funds";
newValidatedValue = false;
}
setErrorAmount(errorAmountMessage)
setValidated(newValidatedValue)
}
setErrorAmount(errorAmountMessage);
setValidated(newValidatedValue);
};
const delegateToMixnode = async ({
delegationMixId,
delegationAmount,
}: {
delegationMixId: number
delegationAmount: string
delegationMixId: number;
delegationAmount: string;
}) => {
try {
const tx = await handleDelegate(delegationMixId, delegationAmount)
return tx
const tx = await handleDelegate(delegationMixId, delegationAmount);
return tx;
} catch (e) {
console.error('Failed to delegate to mixnode', e)
throw e
console.error("Failed to delegate to mixnode", e);
throw e;
}
}
};
const handleConfirm = async () => {
if (mixId && amount && onOk) {
onOk({
status: 'loading',
})
status: "loading",
});
try {
if (!address) {
throw new Error('Please connect your wallet')
throw new Error("Please connect your wallet");
}
const tx = await delegateToMixnode({
delegationMixId: mixId,
delegationAmount: amount.amount,
})
});
if (!tx) {
throw new Error('Failed to delegate')
throw new Error("Failed to delegate");
}
onOk({
status: 'success',
message: 'Delegation can take up to one hour to process',
status: "success",
message: "Delegation can take up to one hour to process",
transactions: [
{
url: `${urls('MAINNET').blockExplorer}/transaction/${
url: `${urls("MAINNET").blockExplorer}/transaction/${
tx.transactionHash
}`,
hash: tx.transactionHash,
},
],
})
});
} catch (e) {
console.error('Failed to delegate', e)
console.error("Failed to delegate", e);
onOk({
status: 'error',
status: "error",
message: (e as Error).message,
})
});
}
}
}
};
const handleAmountChanged = (newAmount: DecCoin) => {
setAmount(newAmount)
}
setAmount(newAmount);
};
React.useEffect(() => {
validate()
}, [amount, identityKey, mixId])
validate();
}, [amount, identityKey, mixId]);
return (
<SimpleModal
@@ -170,7 +170,7 @@ export const DelegateModal = ({
fullWidth
autoFocus
label="Amount"
initialValue={amount?.amount || '10'}
initialValue={amount?.amount || "10"}
onChanged={handleAmountChanged}
denom={denom}
validationError={errorAmount}
@@ -187,5 +187,5 @@ export const DelegateModal = ({
<ModalListItem label="Est. fee for this transaction will be calculated in your connected wallet" />
</SimpleModal>
)
}
);
};
@@ -1,26 +1,26 @@
import React from 'react'
import { Typography, SxProps, Stack } from '@mui/material'
import { Link } from '@nymproject/react/link/Link'
import { LoadingModal } from './LoadingModal'
import { ConfirmationModal } from './ConfirmationModal'
import { ErrorModal } from './ErrorModal'
import React from "react";
import { Typography, SxProps, Stack } from "@mui/material";
import { Link } from "@nymproject/react";
import { LoadingModal } from "./LoadingModal";
import { ConfirmationModal } from "./ConfirmationModal";
import { ErrorModal } from "./ErrorModal";
export type DelegationModalProps = {
status: 'loading' | 'success' | 'error' | 'info'
message?: string
status: "loading" | "success" | "error" | "info";
message?: string;
transactions?: {
url: string
hash: string
}[]
}
url: string;
hash: string;
}[];
};
export const DelegationModal: FCWithChildren<
DelegationModalProps & {
open: boolean
onClose: () => void
sx?: SxProps
backdropProps?: object
children?: React.ReactNode
open: boolean;
onClose: () => void;
sx?: SxProps;
backdropProps?: object;
children?: React.ReactNode;
}
> = ({
status,
@@ -32,18 +32,18 @@ export const DelegationModal: FCWithChildren<
sx,
backdropProps,
}) => {
if (status === 'loading')
return <LoadingModal sx={sx} backdropProps={backdropProps} />
if (status === "loading")
return <LoadingModal sx={sx} backdropProps={backdropProps} />;
if (status === 'error') {
if (status === "error") {
return (
<ErrorModal message={message} sx={sx} open={open} onClose={onClose}>
{children}
</ErrorModal>
)
);
}
if (status === 'info') {
if (status === "info") {
return (
<ConfirmationModal
open={open}
@@ -53,7 +53,7 @@ export const DelegationModal: FCWithChildren<
>
<Typography>{message}</Typography>
</ConfirmationModal>
)
);
}
return (
@@ -91,5 +91,5 @@ export const DelegationModal: FCWithChildren<
)}
</Stack>
</ConfirmationModal>
)
}
);
};
+39 -39
View File
@@ -1,4 +1,4 @@
import * as React from 'react'
import * as React from "react";
import {
Link,
Paper,
@@ -9,49 +9,49 @@ import {
TableHead,
TableRow,
TableCellProps,
} from '@mui/material'
import { useTheme } from '@mui/material/styles'
import { Tooltip } from '@nymproject/react/tooltip/Tooltip'
import { CopyToClipboard } from '@nymproject/react/clipboard/CopyToClipboard'
import { Box } from '@mui/system'
import { unymToNym } from '@/app/utils/currency'
import { GatewayEnrichedRowType } from './Gateways/Gateways'
import { MixnodeRowType } from './MixNodes'
import { StakeSaturationProgressBar } from './MixNodes/Economics/StakeSaturationProgressBar'
} from "@mui/material";
import { useTheme } from "@mui/material/styles";
import { Tooltip } from "@nymproject/react";
import { CopyToClipboard } from "@nymproject/react";
import { Box } from "@mui/system";
import { unymToNym } from "@/app/utils/currency";
import { GatewayEnrichedRowType } from "./Gateways/Gateways";
import { MixnodeRowType } from "./MixNodes";
import { StakeSaturationProgressBar } from "./MixNodes/Economics/StakeSaturationProgressBar";
export type ColumnsType = {
field: string
title: string
headerAlign?: TableCellProps['align']
width?: string | number
tooltipInfo?: string
}
field: string;
title: string;
headerAlign?: TableCellProps["align"];
width?: string | number;
tooltipInfo?: string;
};
export interface UniversalTableProps<T = any> {
tableName: string
columnsData: ColumnsType[]
rows: T[]
tableName: string;
columnsData: ColumnsType[];
rows: T[];
}
function formatCellValues(val: string | number, field: string) {
if (field === 'identity_key' && typeof val === 'string') {
if (field === "identity_key" && typeof val === "string") {
return (
<Box display="flex" justifyContent="flex-end">
<CopyToClipboard
sx={{ mr: 1, mt: 0.5, fontSize: '18px' }}
sx={{ mr: 1, mt: 0.5, fontSize: "18px" }}
value={val}
tooltip={`Copy identity key ${val} to clipboard`}
/>
<span>{val}</span>
</Box>
)
);
}
if (field === 'bond') {
return unymToNym(val, 6)
if (field === "bond") {
return unymToNym(val, 6);
}
if (field === 'owner') {
if (field === "owner") {
return (
<Link
underline="none"
@@ -61,22 +61,22 @@ function formatCellValues(val: string | number, field: string) {
>
{val}
</Link>
)
);
}
if (field === 'stake_saturation') {
return <StakeSaturationProgressBar value={Number(val)} threshold={100} />
if (field === "stake_saturation") {
return <StakeSaturationProgressBar value={Number(val)} threshold={100} />;
}
return val
return val;
}
export const DetailTable: FCWithChildren<{
tableName: string
columnsData: ColumnsType[]
rows: MixnodeRowType[] | GatewayEnrichedRowType[]
tableName: string;
columnsData: ColumnsType[];
rows: MixnodeRowType[] | GatewayEnrichedRowType[];
}> = ({ tableName, columnsData, rows }: UniversalTableProps) => {
const theme = useTheme()
const theme = useTheme();
return (
<TableContainer component={Paper}>
<Table sx={{ minWidth: 1080 }} aria-label={tableName}>
@@ -87,9 +87,9 @@ export const DetailTable: FCWithChildren<{
key={field}
sx={{ fontSize: 14, fontWeight: 600, width }}
>
<Box sx={{ display: 'flex', alignItems: 'center' }}>
<Box sx={{ display: "flex", alignItems: "center" }}>
{tooltipInfo && (
<Box sx={{ display: 'flex', alignItems: 'center' }}>
<Box sx={{ display: "flex", alignItems: "center" }}>
<Tooltip
title={tooltipInfo}
id={field}
@@ -115,7 +115,7 @@ export const DetailTable: FCWithChildren<{
{rows.map((eachRow) => (
<TableRow
key={eachRow.id}
sx={{ '&:last-child td, &:last-child th': { border: 0 } }}
sx={{ "&:last-child td, &:last-child th": { border: 0 } }}
>
{columnsData?.map((data, index) => (
<TableCell
@@ -128,7 +128,7 @@ export const DetailTable: FCWithChildren<{
width: 200,
fontSize: 14,
}}
data-testid={`${data.title.replace(/ /g, '-')}-value`}
data-testid={`${data.title.replace(/ /g, "-")}-value`}
>
{formatCellValues(
eachRow[columnsData[index].field],
@@ -141,5 +141,5 @@ export const DetailTable: FCWithChildren<{
</TableBody>
</Table>
</TableContainer>
)
}
);
};
@@ -1,4 +1,4 @@
import * as React from 'react'
import * as React from "react";
import {
Paper,
Table,
@@ -8,26 +8,26 @@ import {
TableHead,
TableRow,
Typography,
} from '@mui/material'
import { Box } from '@mui/system'
import { useTheme } from '@mui/material/styles'
import { Tooltip } from '@nymproject/react/tooltip/Tooltip'
import { EconomicsRowsType, EconomicsInfoRowWithIndex } from './types'
import { UniversalTableProps } from '@/app/components/DetailTable'
import { textColour } from '@/app/utils'
} from "@mui/material";
import { Box } from "@mui/system";
import { useTheme } from "@mui/material/styles";
import { Tooltip } from "@nymproject/react";
import { EconomicsRowsType, EconomicsInfoRowWithIndex } from "./types";
import { UniversalTableProps } from "@/app/components/DetailTable";
import { textColour } from "@/app/utils";
const formatCellValues = (value: EconomicsRowsType, field: string) => (
<Box sx={{ display: 'flex', alignItems: 'center' }} id="field">
<Typography sx={{ mr: 1, fontWeight: '600', fontSize: '12px' }} id={field}>
<Box sx={{ display: "flex", alignItems: "center" }} id="field">
<Typography sx={{ mr: 1, fontWeight: "600", fontSize: "12px" }} id={field}>
{value.value}
</Typography>
</Box>
)
);
export const DelegatorsInfoTable: FCWithChildren<
UniversalTableProps<EconomicsInfoRowWithIndex>
> = ({ tableName, columnsData, rows }) => {
const theme = useTheme()
const theme = useTheme();
return (
<TableContainer component={Paper}>
@@ -39,7 +39,7 @@ export const DelegatorsInfoTable: FCWithChildren<
key={field}
sx={{ fontSize: 14, fontWeight: 600, width }}
>
<Box sx={{ display: 'flex', alignItems: 'center' }}>
<Box sx={{ display: "flex", alignItems: "center" }}>
{tooltipInfo && (
<Tooltip
title={tooltipInfo}
@@ -65,27 +65,27 @@ export const DelegatorsInfoTable: FCWithChildren<
{rows?.map((eachRow) => (
<TableRow
key={eachRow.id}
sx={{ '&:last-child td, &:last-child th': { border: 0 } }}
sx={{ "&:last-child td, &:last-child th": { border: 0 } }}
>
{columnsData?.map((_, index: number) => {
const { field } = columnsData[index]
const value: EconomicsRowsType = (eachRow as any)[field]
const { field } = columnsData[index];
const value: EconomicsRowsType = (eachRow as any)[field];
return (
<TableCell
key={_.title}
sx={{
color: textColour(value, field, theme),
}}
data-testid={`${_.title.replace(/ /g, '-')}-value`}
data-testid={`${_.title.replace(/ /g, "-")}-value`}
>
{formatCellValues(value, columnsData[index].field)}
</TableCell>
)
);
})}
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
)
}
);
};
+118 -117
View File
@@ -1,91 +1,92 @@
'use client'
"use client";
import * as React from 'react'
import { ExpandLess, ExpandMore, Menu } from '@mui/icons-material'
import { CSSObject, styled, Theme, useTheme } from '@mui/material/styles'
import { Link as MuiLink } from '@mui/material'
import Button from '@mui/material/Button'
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 Typography from '@mui/material/Typography'
import List from '@mui/material/List'
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 { NYM_WEBSITE } from '@/app/api/constants'
import { useMainContext } from '@/app/context/main'
import { MobileDrawerClose } from '@/app/icons/MobileDrawerClose'
import { NavOptionType, originalNavOptions } from '@/app/context/nav'
import { DarkLightSwitchDesktop } from '@/app/components/Switch'
import { Footer } from '@/app/components/Footer'
import { ConnectKeplrWallet } from '@/app/components/Wallet/ConnectKeplrWallet'
import { usePathname, useRouter } from 'next/navigation'
import * as React from "react";
import { Menu } from "@mui/icons-material";
import { CSSObject, styled, Theme, useTheme } from "@mui/material/styles";
import { Link as MuiLink } from "@mui/material";
import Button from "@mui/material/Button";
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 Typography from "@mui/material/Typography";
import List from "@mui/material/List";
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 { NYM_WEBSITE } from "@/app/api/constants";
import { useMainContext } from "@/app/context/main";
import { MobileDrawerClose } from "@/app/icons/MobileDrawerClose";
import { NavOptionType, originalNavOptions } from "@/app/context/nav";
import { DarkLightSwitchDesktop } from "@/app/components/Switch";
import { Footer } from "@/app/components/Footer";
import { ConnectKeplrWallet } from "@/app/components/Wallet/ConnectKeplrWallet";
import { usePathname, useRouter } from "next/navigation";
import { NymLogo } from "@nymproject/react";
const drawerWidth = 255
const bannerHeight = 80
const drawerWidth = 255;
const bannerHeight = 80;
const openedMixin = (theme: Theme): CSSObject => ({
width: drawerWidth,
transition: theme.transitions.create('width', {
transition: theme.transitions.create("width", {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.enteringScreen,
}),
overflowX: 'hidden',
})
overflowX: "hidden",
});
const closedMixin = (theme: Theme): CSSObject => ({
transition: theme.transitions.create('width', {
transition: theme.transitions.create("width", {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.leavingScreen,
}),
overflowX: 'hidden',
overflowX: "hidden",
width: `calc(${theme.spacing(7)} + 1px)`,
})
});
const DrawerHeader = styled('div')(({ theme }) => ({
display: 'flex',
alignItems: 'center',
justifyContent: 'flex-end',
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',
shouldForwardProp: (prop) => prop !== "open",
})(({ theme, open }) => ({
width: drawerWidth,
flexShrink: 0,
whiteSpace: 'nowrap',
boxSizing: 'border-box',
whiteSpace: "nowrap",
boxSizing: "border-box",
...(open && {
...openedMixin(theme),
'& .MuiDrawer-paper': openedMixin(theme),
"& .MuiDrawer-paper": openedMixin(theme),
}),
...(!open && {
...closedMixin(theme),
'& .MuiDrawer-paper': closedMixin(theme),
"& .MuiDrawer-paper": closedMixin(theme),
}),
}))
}));
type ExpandableButtonType = {
title: string
url: string
isActive?: boolean
Icon?: React.ReactNode
nested?: NavOptionType[]
isChild?: boolean
isMobile: boolean
drawIsTempOpen: boolean
drawIsFixed: boolean
isExternalLink?: boolean
openDrawer: () => void
closeDrawer?: () => void
fixDrawerClose?: () => void
}
title: string;
url: string;
isActive?: boolean;
Icon?: React.ReactNode;
nested?: NavOptionType[];
isChild?: boolean;
isMobile: boolean;
drawIsTempOpen: boolean;
drawIsFixed: boolean;
isExternalLink?: boolean;
openDrawer: () => void;
closeDrawer?: () => void;
fixDrawerClose?: () => void;
};
export const ExpandableButton: FCWithChildren<ExpandableButtonType> = ({
title,
@@ -101,33 +102,33 @@ export const ExpandableButton: FCWithChildren<ExpandableButtonType> = ({
closeDrawer,
fixDrawerClose,
}) => {
const { palette } = useTheme()
const pathname = usePathname()
const router = useRouter()
const { palette } = useTheme();
const pathname = usePathname();
const router = useRouter();
const handleClick = () => {
if (title === 'Network Components') {
return undefined
if (title === "Network Components") {
return undefined;
}
if (isExternalLink) {
window.open(url, '_blank')
window.open(url, "_blank");
return undefined
return undefined;
}
if (!isExternalLink) {
router.push(url, {})
router.push(url, {});
}
if (closeDrawer) {
closeDrawer()
closeDrawer();
}
}
};
const selectedStyle = {
background: palette.nym.networkExplorer.nav.selected.main,
borderRight: `3px solid ${palette.nym.highlight}`,
}
};
return (
<>
@@ -135,12 +136,12 @@ export const ExpandableButton: FCWithChildren<ExpandableButtonType> = ({
disablePadding
disableGutters
sx={{
borderBottom: isChild ? 'none' : '1px solid rgba(255, 255, 255, 0.1)',
borderBottom: isChild ? "none" : "1px solid rgba(255, 255, 255, 0.1)",
...(pathname === url
? selectedStyle
: {
background: palette.nym.networkExplorer.nav.background,
borderRight: 'none',
borderRight: "none",
}),
}}
>
@@ -151,10 +152,10 @@ export const ExpandableButton: FCWithChildren<ExpandableButtonType> = ({
pb: 2,
background: isChild
? palette.nym.networkExplorer.nav.selected.nested
: 'none',
: "none",
}}
>
<ListItemIcon sx={{ minWidth: '39px' }}>{Icon}</ListItemIcon>
<ListItemIcon sx={{ minWidth: "39px" }}>{Icon}</ListItemIcon>
<ListItemText
primary={title}
sx={{
@@ -179,52 +180,52 @@ export const ExpandableButton: FCWithChildren<ExpandableButtonType> = ({
/>
))}
</>
)
}
);
};
export const Nav: FCWithChildren = ({ children }) => {
const { environment } = useMainContext()
const [drawerIsOpen, setDrawerToOpen] = React.useState(false)
const [fixedOpen, setFixedOpen] = React.useState(false)
const { environment } = useMainContext();
const [drawerIsOpen, setDrawerToOpen] = React.useState(false);
const [fixedOpen, setFixedOpen] = React.useState(false);
// Set maintenance banner to false by default to don't display it
const [openMaintenance, setOpenMaintenance] = React.useState(false)
const theme = useTheme()
const [openMaintenance, setOpenMaintenance] = React.useState(false);
const theme = useTheme();
const explorerName = environment
? `${environment} Explorer`
: 'Mainnet Explorer'
: "Mainnet Explorer";
const switchNetworkText =
environment === 'mainnet' ? 'Switch to Testnet' : 'Switch to Mainnet'
environment === "mainnet" ? "Switch to Testnet" : "Switch to Mainnet";
const switchNetworkLink =
environment === 'mainnet'
? 'https://sandbox-explorer.nymtech.net'
: 'https://explorer.nymtech.net'
environment === "mainnet"
? "https://sandbox-explorer.nymtech.net"
: "https://explorer.nymtech.net";
const fixDrawerOpen = () => {
setFixedOpen(true)
setDrawerToOpen(true)
}
setFixedOpen(true);
setDrawerToOpen(true);
};
const fixDrawerClose = () => {
setFixedOpen(false)
setDrawerToOpen(false)
}
setFixedOpen(false);
setDrawerToOpen(false);
};
const tempDrawerOpen = () => {
if (!fixedOpen) {
setDrawerToOpen(true)
setDrawerToOpen(true);
}
}
};
const tempDrawerClose = () => {
if (!fixedOpen) {
setDrawerToOpen(false)
setDrawerToOpen(false);
}
}
};
return (
<Box sx={{ display: 'flex' }}>
<Box sx={{ display: "flex" }}>
<AppBar
sx={{
background: theme.palette.nym.networkExplorer.topNav.appBar,
@@ -234,28 +235,28 @@ export const Nav: FCWithChildren = ({ children }) => {
<Toolbar
disableGutters
sx={{
display: 'flex',
justifyContent: 'space-between',
display: "flex",
justifyContent: "space-between",
}}
>
<Box
sx={{
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
display: "flex",
flexDirection: "row",
alignItems: "center",
justifyContent: "space-between",
ml: 0.5,
}}
>
<IconButton component="a" href={NYM_WEBSITE} target="_blank">
{/* <NymLogo /> */}
<NymLogo width={25} />
</IconButton>
<Typography
variant="h6"
noWrap
sx={{
color: theme.palette.nym.networkExplorer.nav.text,
fontSize: '18px',
fontSize: "18px",
fontWeight: 600,
}}
>
@@ -274,7 +275,7 @@ export const Nav: FCWithChildren = ({ children }) => {
href={switchNetworkLink}
sx={{
borderRadius: 2,
textTransform: 'none',
textTransform: "none",
width: 150,
ml: 4,
fontSize: 14,
@@ -288,19 +289,19 @@ export const Nav: FCWithChildren = ({ children }) => {
<Box
sx={{
mr: 2,
alignItems: 'center',
display: 'flex',
alignItems: "center",
display: "flex",
}}
>
<Box
sx={{
display: 'flex',
flexDirection: 'row',
width: 'auto',
display: "flex",
flexDirection: "row",
width: "auto",
pr: 0,
pl: 2,
justifyContent: 'flex-end',
alignItems: 'center',
justifyContent: "flex-end",
alignItems: "center",
}}
>
<Box sx={{ mr: 1 }}>
@@ -324,10 +325,10 @@ export const Nav: FCWithChildren = ({ children }) => {
>
<DrawerHeader
sx={{
borderBottom: '1px solid rgba(255, 255, 255, 0.1)',
justifyContent: 'flex-start',
borderBottom: "1px solid rgba(255, 255, 255, 0.1)",
justifyContent: "flex-start",
paddingLeft: 0,
display: 'none',
display: "none",
}}
>
<IconButton
@@ -369,5 +370,5 @@ export const Nav: FCWithChildren = ({ children }) => {
<Footer />
</Box>
</Box>
)
}
);
};
@@ -1,7 +1,7 @@
'use client'
"use client";
import * as React from 'react'
import { useTheme } from '@mui/material/styles'
import * as React from "react";
import { useTheme } from "@mui/material/styles";
import {
AppBar,
Box,
@@ -12,34 +12,34 @@ import {
ListItemButton,
ListItemIcon,
Toolbar,
} from '@mui/material'
import { Menu } from '@mui/icons-material'
import { MaintenanceBanner } from '@nymproject/react/banners/MaintenanceBanner'
import { useIsMobile } from '@/app/hooks/useIsMobile'
import { MobileDrawerClose } from '@/app/icons/MobileDrawerClose'
import { Footer } from '../Footer'
import { ExpandableButton } from './DesktopNav'
import { ConnectKeplrWallet } from '../Wallet/ConnectKeplrWallet'
import { NetworkTitle } from '../NetworkTitle'
import { originalNavOptions } from '@/app/context/nav'
} from "@mui/material";
import { Menu } from "@mui/icons-material";
import { MaintenanceBanner } from "@nymproject/react";
import { useIsMobile } from "@/app/hooks/useIsMobile";
import { MobileDrawerClose } from "@/app/icons/MobileDrawerClose";
import { Footer } from "../Footer";
import { ExpandableButton } from "./DesktopNav";
import { ConnectKeplrWallet } from "../Wallet/ConnectKeplrWallet";
import { NetworkTitle } from "../NetworkTitle";
import { originalNavOptions } from "@/app/context/nav";
export const MobileNav: FCWithChildren = ({ children }) => {
const theme = useTheme()
const [drawerOpen, setDrawerOpen] = React.useState(false)
const theme = useTheme();
const [drawerOpen, setDrawerOpen] = React.useState(false);
// Set maintenance banner to false by default to don't display it
const [openMaintenance, setOpenMaintenance] = React.useState(false)
const isSmallMobile = useIsMobile(400)
const [openMaintenance, setOpenMaintenance] = React.useState(false);
const isSmallMobile = useIsMobile(400);
const toggleDrawer = () => {
setDrawerOpen(!drawerOpen)
}
setDrawerOpen(!drawerOpen);
};
const openDrawer = () => {
setDrawerOpen(true)
}
setDrawerOpen(true);
};
return (
<Box sx={{ display: 'flex', flexDirection: 'column' }}>
<Box sx={{ display: "flex", flexDirection: "column" }}>
<AppBar
sx={{
background: theme.palette.nym.networkExplorer.topNav.appBar,
@@ -52,21 +52,21 @@ export const MobileNav: FCWithChildren = ({ children }) => {
/>
<Toolbar
sx={{
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
width: '100%',
display: "flex",
justifyContent: "space-between",
alignItems: "center",
width: "100%",
}}
>
<Box
sx={{
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
display: "flex",
flexDirection: "row",
alignItems: "center",
}}
>
<IconButton onClick={toggleDrawer}>
<Menu sx={{ color: 'primary.contrastText' }} />
<Menu sx={{ color: "primary.contrastText" }} />
</IconButton>
{!isSmallMobile && <NetworkTitle />}
</Box>
@@ -91,7 +91,7 @@ export const MobileNav: FCWithChildren = ({ children }) => {
sx={{
height: 64,
background: theme.palette.nym.networkExplorer.nav.background,
borderBottom: '1px solid rgba(255, 255, 255, 0.1)',
borderBottom: "1px solid rgba(255, 255, 255, 0.1)",
}}
>
<ListItemButton
@@ -100,8 +100,8 @@ export const MobileNav: FCWithChildren = ({ children }) => {
pt: 2,
pb: 2,
background: theme.palette.nym.networkExplorer.nav.background,
display: 'flex',
justifyContent: 'flex-start',
display: "flex",
justifyContent: "flex-start",
}}
>
<ListItemIcon>
@@ -127,10 +127,10 @@ export const MobileNav: FCWithChildren = ({ children }) => {
</Box>
</Drawer>
<Box sx={{ width: '100%', p: 4, mt: 7 }}>
<Box sx={{ width: "100%", p: 4, mt: 7 }}>
{children}
<Footer />
</Box>
</Box>
)
}
);
};
@@ -1,8 +1,8 @@
import * as React from 'react'
import { FallbackProps } from 'react-error-boundary'
import { Alert, AlertTitle, Container } from '@mui/material'
import { NymThemeProvider } from '@nymproject/mui-theme'
import { NymLogo } from '@nymproject/react/logo/NymLogo'
import * as React from "react";
import { FallbackProps } from "react-error-boundary";
import { Alert, AlertTitle, Container } from "@mui/material";
import { NymThemeProvider } from "@nymproject/mui-theme";
import { NymLogo } from "@nymproject/react";
export const ErrorBoundaryContent: FCWithChildren<FallbackProps> = ({
error,
@@ -15,7 +15,7 @@ export const ErrorBoundaryContent: FCWithChildren<FallbackProps> = ({
<AlertTitle>{error.name}</AlertTitle>
{error.message}
</Alert>
{process.env.NODE_ENV === 'development' && (
{process.env.NODE_ENV === "development" && (
<Alert severity="info" sx={{ mt: 2 }} data-testid="stack-trace">
<AlertTitle>Stack trace</AlertTitle>
{error.stack}
@@ -23,4 +23,4 @@ export const ErrorBoundaryContent: FCWithChildren<FallbackProps> = ({
)}
</Container>
</NymThemeProvider>
)
);
@@ -1,106 +1,106 @@
'use client'
"use client";
import React, { useMemo } from 'react'
import { Box, Card, Grid, Stack } from '@mui/material'
import { useTheme } from '@mui/material/styles'
import React, { useMemo } from "react";
import { Box, Card, Grid, Stack } from "@mui/material";
import { useTheme } from "@mui/material/styles";
import {
MRT_ColumnDef,
MaterialReactTable,
useMaterialReactTable,
} from 'material-react-table'
import { GridColDef, GridRenderCellParams } from '@mui/x-data-grid'
import { CopyToClipboard } from '@nymproject/react/clipboard/CopyToClipboard'
import { Tooltip as InfoTooltip } from '@nymproject/react/tooltip/Tooltip'
import { diff, gte, rcompare } from 'semver'
import { useMainContext } from '@/app/context/main'
import { TableToolbar } from '@/app/components/TableToolbar'
import { CustomColumnHeading } from '@/app/components/CustomColumnHeading'
import { Title } from '@/app/components/Title'
import { unymToNym } from '@/app/utils/currency'
import { Tooltip } from '@/app/components/Tooltip'
import { NYM_BIG_DIPPER } from '@/app/api/constants'
import { splice } from '@/app/utils'
} from "material-react-table";
import { GridColDef, GridRenderCellParams } from "@mui/x-data-grid";
import { CopyToClipboard } from "@nymproject/react";
import { Tooltip as InfoTooltip } from "@nymproject/react";
import { diff, gte, rcompare } from "semver";
import { useMainContext } from "@/app/context/main";
import { TableToolbar } from "@/app/components/TableToolbar";
import { CustomColumnHeading } from "@/app/components/CustomColumnHeading";
import { Title } from "@/app/components/Title";
import { unymToNym } from "@/app/utils/currency";
import { Tooltip } from "@/app/components/Tooltip";
import { NYM_BIG_DIPPER } from "@/app/api/constants";
import { splice } from "@/app/utils";
import {
VersionDisplaySelector,
VersionSelectOptions,
} from '@/app/components/Gateways/VersionDisplaySelector'
import StyledLink from '@/app/components/StyledLink'
} from "@/app/components/Gateways/VersionDisplaySelector";
import StyledLink from "@/app/components/StyledLink";
import {
GatewayRowType,
gatewayToGridRow,
} from '@/app/components/Gateways/Gateways'
} from "@/app/components/Gateways/Gateways";
const PageGateways = () => {
const { gateways } = useMainContext()
const { gateways } = useMainContext();
const [versionFilter, setVersionFilter] =
React.useState<VersionSelectOptions>(VersionSelectOptions.all)
React.useState<VersionSelectOptions>(VersionSelectOptions.all);
const theme = useTheme()
const theme = useTheme();
const highestVersion = React.useMemo(() => {
if (gateways?.data) {
const versions = gateways.data.reduce(
(a: string[], b) => [...a, b.gateway.version],
[]
)
const [lastestVersion] = versions.sort(rcompare)
return lastestVersion
);
const [lastestVersion] = versions.sort(rcompare);
return lastestVersion;
}
// fallback value
return '2.0.0'
}, [gateways])
return "2.0.0";
}, [gateways]);
const filterByLatestVersions = React.useMemo(() => {
const filtered = gateways?.data?.filter((gw) => {
const versionDiff = diff(highestVersion, gw.gateway.version)
return versionDiff === 'patch' || versionDiff === null
})
if (filtered) return filtered
return []
}, [gateways])
const versionDiff = diff(highestVersion, gw.gateway.version);
return versionDiff === "patch" || versionDiff === null;
});
if (filtered) return filtered;
return [];
}, [gateways]);
const filterByOlderVersions = React.useMemo(() => {
const filtered = gateways?.data?.filter((gw) => {
const versionDiff = diff(highestVersion, gw.gateway.version)
return versionDiff === 'major' || versionDiff === 'minor'
})
if (filtered) return filtered
return []
}, [gateways])
const versionDiff = diff(highestVersion, gw.gateway.version);
return versionDiff === "major" || versionDiff === "minor";
});
if (filtered) return filtered;
return [];
}, [gateways]);
const filteredByVersion = React.useMemo(() => {
switch (versionFilter) {
case VersionSelectOptions.latestVersion:
return filterByLatestVersions
return filterByLatestVersions;
case VersionSelectOptions.olderVersions:
return filterByOlderVersions
return filterByOlderVersions;
case VersionSelectOptions.all:
return gateways?.data || []
return gateways?.data || [];
default:
return []
return [];
}
}, [versionFilter, gateways])
}, [versionFilter, gateways]);
const data = useMemo(() => {
return gatewayToGridRow(filteredByVersion || [])
}, [filteredByVersion])
return gatewayToGridRow(filteredByVersion || []);
}, [filteredByVersion]);
const columns = useMemo<MRT_ColumnDef<GatewayRowType>[]>(() => {
return [
{
id: 'gateway-data',
header: 'Gatewsay Data',
id: "gateway-data",
header: "Gatewsay Data",
columns: [
{
id: 'identity_key',
header: 'Identity Key',
accessorKey: 'identity_key',
id: "identity_key",
header: "Identity Key",
accessorKey: "identity_key",
size: 250,
Cell: ({ row }) => {
return (
<Stack direction="row" alignItems="center" gap={1}>
<CopyToClipboard
sx={{ mr: 0.5, color: 'grey.400' }}
sx={{ mr: 0.5, color: "grey.400" }}
smallIcons
value={row.original.identity_key}
tooltip={`Copy identity key ${row.original.identity_key} to clipboard`}
@@ -113,13 +113,13 @@ const PageGateways = () => {
{splice(7, 29, row.original.identity_key)}
</StyledLink>
</Stack>
)
);
},
},
{
id: 'node_performance',
header: 'Node Performance',
accessorKey: 'node_performance',
id: "node_performance",
header: "Node Performance",
accessorKey: "node_performance",
size: 200,
Header: () => {
return (
@@ -137,7 +137,7 @@ const PageGateways = () => {
/>
<CustomColumnHeading headingTitle="Routing Score" />
</Box>
)
);
},
Cell: ({ row }) => {
return (
@@ -148,13 +148,13 @@ const PageGateways = () => {
>
{`${row.original.node_performance}%`}
</StyledLink>
)
);
},
},
{
id: 'version',
header: 'Version',
accessorKey: 'version',
id: "version",
header: "Version",
accessorKey: "version",
size: 150,
Cell: ({ row }) => {
return (
@@ -165,18 +165,18 @@ const PageGateways = () => {
>
{row.original.version}
</StyledLink>
)
);
},
},
{
id: 'location',
header: 'Location',
accessorKey: 'location',
id: "location",
header: "Location",
accessorKey: "location",
size: 150,
Cell: ({ row }) => {
return (
<Box
sx={{ justifyContent: 'flex-start', cursor: 'pointer' }}
sx={{ justifyContent: "flex-start", cursor: "pointer" }}
data-testid="location-button"
>
<Tooltip
@@ -185,22 +185,22 @@ const PageGateways = () => {
>
<Box
sx={{
overflow: 'hidden',
whiteSpace: 'nowrap',
textOverflow: 'ellipsis',
overflow: "hidden",
whiteSpace: "nowrap",
textOverflow: "ellipsis",
}}
>
{row.original.location}
</Box>
</Tooltip>
</Box>
)
);
},
},
{
id: 'host',
header: 'IP:Port',
accessorKey: 'host',
id: "host",
header: "IP:Port",
accessorKey: "host",
size: 150,
Cell: ({ row }) => {
return (
@@ -211,13 +211,13 @@ const PageGateways = () => {
>
{row.original.host}
</StyledLink>
)
);
},
},
{
id: 'owner',
header: 'Owner',
accessorKey: 'owner',
id: "owner",
header: "Owner",
accessorKey: "owner",
size: 150,
Cell: ({ row }) => {
return (
@@ -229,18 +229,18 @@ const PageGateways = () => {
>
{splice(7, 29, row.original.owner)}
</StyledLink>
)
);
},
},
],
},
]
}, [])
];
}, []);
const _columns: GridColDef[] = [
{
field: 'node_performance',
align: 'center',
field: "node_performance",
align: "center",
renderHeader: () => (
<>
<InfoTooltip
@@ -257,8 +257,8 @@ const PageGateways = () => {
),
width: 120,
disableColumnMenu: true,
headerAlign: 'center',
headerClassName: 'MuiDataGrid-header-override',
headerAlign: "center",
headerClassName: "MuiDataGrid-header-override",
renderCell: (params: GridRenderCellParams) => (
<StyledLink
to={`/network-components/gateways/${params.row.identity_key}`}
@@ -269,13 +269,13 @@ const PageGateways = () => {
),
},
{
field: 'version',
align: 'center',
field: "version",
align: "center",
renderHeader: () => <CustomColumnHeading headingTitle="Version" />,
width: 150,
disableColumnMenu: true,
headerAlign: 'center',
headerClassName: 'MuiDataGrid-header-override',
headerAlign: "center",
headerClassName: "MuiDataGrid-header-override",
renderCell: (params: GridRenderCellParams) => (
<StyledLink
to={`/network-components/gateways/${params.row.identity_key}`}
@@ -285,28 +285,28 @@ const PageGateways = () => {
</StyledLink>
),
sortComparator: (a, b) => {
if (gte(a, b)) return 1
return -1
if (gte(a, b)) return 1;
return -1;
},
},
{
field: 'location',
field: "location",
renderHeader: () => <CustomColumnHeading headingTitle="Location" />,
width: 180,
disableColumnMenu: true,
headerAlign: 'left',
headerClassName: 'MuiDataGrid-header-override',
headerAlign: "left",
headerClassName: "MuiDataGrid-header-override",
renderCell: (params: GridRenderCellParams) => (
<Box
sx={{ justifyContent: 'flex-start', cursor: 'pointer' }}
sx={{ justifyContent: "flex-start", cursor: "pointer" }}
data-testid="location-button"
>
<Tooltip text={params.value} id="gateway-location-text">
<Box
sx={{
overflow: 'hidden',
whiteSpace: 'nowrap',
textOverflow: 'ellipsis',
overflow: "hidden",
whiteSpace: "nowrap",
textOverflow: "ellipsis",
}}
>
{params.value}
@@ -316,12 +316,12 @@ const PageGateways = () => {
),
},
{
field: 'host',
field: "host",
renderHeader: () => <CustomColumnHeading headingTitle="IP:Port" />,
width: 180,
disableColumnMenu: true,
headerAlign: 'left',
headerClassName: 'MuiDataGrid-header-override',
headerAlign: "left",
headerClassName: "MuiDataGrid-header-override",
renderCell: (params: GridRenderCellParams) => (
<StyledLink
to={`/network-components/gateways/${params.row.identity_key}`}
@@ -332,13 +332,13 @@ const PageGateways = () => {
),
},
{
field: 'owner',
headerName: 'Owner',
field: "owner",
headerName: "Owner",
renderHeader: () => <CustomColumnHeading headingTitle="Owner" />,
width: 180,
disableColumnMenu: true,
headerAlign: 'left',
headerClassName: 'MuiDataGrid-header-override',
headerAlign: "left",
headerClassName: "MuiDataGrid-header-override",
renderCell: (params: GridRenderCellParams) => (
<StyledLink
to={`${NYM_BIG_DIPPER}/account/${params.value}`}
@@ -350,13 +350,13 @@ const PageGateways = () => {
),
},
{
field: 'bond',
field: "bond",
width: 150,
disableColumnMenu: true,
type: 'number',
type: "number",
renderHeader: () => <CustomColumnHeading headingTitle="Bond" />,
headerClassName: 'MuiDataGrid-header-override',
headerAlign: 'left',
headerClassName: "MuiDataGrid-header-override",
headerAlign: "left",
renderCell: (params: GridRenderCellParams) => (
<StyledLink
to={`/network-components/gateways/${params.row.identity_key}`}
@@ -366,12 +366,12 @@ const PageGateways = () => {
</StyledLink>
),
},
]
];
const table = useMaterialReactTable({
columns,
data,
})
});
return (
<>
@@ -383,7 +383,7 @@ const PageGateways = () => {
<Card
sx={{
padding: 2,
height: '100%',
height: "100%",
}}
>
<TableToolbar
@@ -399,7 +399,7 @@ const PageGateways = () => {
</Grid>
</Grid>
</>
)
}
);
};
export default PageGateways
export default PageGateways;
@@ -1,13 +1,13 @@
'use client'
"use client";
import React, { useCallback, useMemo } from 'react'
import { useRouter, useSearchParams } from 'next/navigation'
import React, { useCallback, useMemo } from "react";
import { useRouter, useSearchParams } from "next/navigation";
import {
MaterialReactTable,
useMaterialReactTable,
type MRT_ColumnDef,
} from 'material-react-table'
import { Grid, Card, Button, Box, Stack } from '@mui/material'
} from "material-react-table";
import { Grid, Card, Button, Box, Stack } from "@mui/material";
import {
CustomColumnHeading,
DelegateIconButton,
@@ -21,81 +21,81 @@ import {
Title,
Tooltip,
mixnodeToGridRow,
} from '@/app/components'
import { DelegationsProvider } from '@/app/context/delegations'
import { useWalletContext } from '@/app/context/wallet'
import { useGetMixNodeStatusColor, useIsMobile } from '@/app/hooks'
import { useMainContext } from '@/app/context/main'
import { CopyToClipboard } from '@nymproject/react/clipboard/CopyToClipboard'
import { splice } from '@/app/utils'
import { currencyToString } from '@/app/utils/currency'
import { NYM_BIG_DIPPER } from '@/app/api/constants'
} from "@/app/components";
import { DelegationsProvider } from "@/app/context/delegations";
import { useWalletContext } from "@/app/context/wallet";
import { useGetMixNodeStatusColor, useIsMobile } from "@/app/hooks";
import { useMainContext } from "@/app/context/main";
import { CopyToClipboard } from "@nymproject/react";
import { splice } from "@/app/utils";
import { currencyToString } from "@/app/utils/currency";
import { NYM_BIG_DIPPER } from "@/app/api/constants";
import {
MixnodeStatusWithAll,
toMixnodeStatus,
} from '@/app/typeDefs/explorer-api'
} from "@/app/typeDefs/explorer-api";
export default function MixnodesPage() {
const isMobile = useIsMobile()
const { isWalletConnected } = useWalletContext()
const { mixnodes, fetchMixnodes } = useMainContext()
const router = useRouter()
const isMobile = useIsMobile();
const { isWalletConnected } = useWalletContext();
const { mixnodes, fetchMixnodes } = useMainContext();
const router = useRouter();
const [itemSelectedForDelegation, setItemSelectedForDelegation] =
React.useState<{
mixId: number
identityKey: string
}>()
mixId: number;
identityKey: string;
}>();
const [confirmationModalProps, setConfirmationModalProps] = React.useState<
DelegationModalProps | undefined
>()
>();
const search = useSearchParams()
const status = search.get('status') as MixnodeStatusWithAll
const search = useSearchParams();
const status = search.get("status") as MixnodeStatusWithAll;
React.useEffect(() => {
// when the status changes, get the mixnodes
fetchMixnodes(toMixnodeStatus(status))
}, [status])
fetchMixnodes(toMixnodeStatus(status));
}, [status]);
const handleMixnodeStatusChanged = (newStatus?: MixnodeStatusWithAll) => {
router.push(
newStatus && newStatus !== 'all'
newStatus && newStatus !== "all"
? `/network-components/mixnodes?status=${newStatus}`
: '/network-components/mixnodes'
)
}
: "/network-components/mixnodes"
);
};
const handleOnDelegate = useCallback(
({ identityKey, mixId }: { identityKey: string; mixId: number }) => {
if (!isWalletConnected) {
setConfirmationModalProps({
status: 'info',
message: 'Please connect your wallet to delegate',
})
status: "info",
message: "Please connect your wallet to delegate",
});
} else {
setItemSelectedForDelegation({ identityKey, mixId })
setItemSelectedForDelegation({ identityKey, mixId });
}
},
[isWalletConnected]
)
);
const handleNewDelegation = (delegationModalProps: DelegationModalProps) => {
setItemSelectedForDelegation(undefined)
setConfirmationModalProps(delegationModalProps)
}
setItemSelectedForDelegation(undefined);
setConfirmationModalProps(delegationModalProps);
};
const columns = useMemo<MRT_ColumnDef<MixnodeRowType>[]>(() => {
return [
{
id: 'mixnode-data',
header: 'Mixnode Data',
id: "mixnode-data",
header: "Mixnode Data",
columns: [
{
id: 'delegate',
accessorKey: 'delegate',
id: "delegate",
accessorKey: "delegate",
size: isMobile ? 50 : 150,
header: '',
header: "",
grow: false,
Cell: ({ row }) => (
<DelegateIconButton
@@ -113,15 +113,15 @@ export default function MixnodesPage() {
Filter: () => null,
},
{
id: 'identity_key',
header: 'Identity Key',
accessorKey: 'identity_key',
id: "identity_key",
header: "Identity Key",
accessorKey: "identity_key",
size: 250,
Cell: ({ row }) => {
return (
<Stack direction="row" alignItems="center" gap={1}>
<CopyToClipboard
sx={{ mr: 0.5, color: 'grey.400' }}
sx={{ mr: 0.5, color: "grey.400" }}
smallIcons
value={row.original.identity_key}
tooltip={`Copy identity key ${row.original.identity_key} to clipboard`}
@@ -134,13 +134,13 @@ export default function MixnodesPage() {
{splice(7, 29, row.original.identity_key)}
</StyledLink>
</Stack>
)
);
},
},
{
id: 'bond',
header: 'Stake',
accessorKey: 'bond',
id: "bond",
header: "Stake",
accessorKey: "bond",
Cell: ({ row }) => (
<StyledLink
to={`/network-components/mixnodes/${row.original.mix_id}`}
@@ -151,9 +151,9 @@ export default function MixnodesPage() {
),
},
{
id: 'stake_saturation',
header: 'Stake Saturation',
accessorKey: 'stake_saturation',
id: "stake_saturation",
header: "Stake Saturation",
accessorKey: "stake_saturation",
size: 225,
Header() {
return (
@@ -161,7 +161,7 @@ export default function MixnodesPage() {
headingTitle="Stake Saturation"
tooltipInfo="Level of stake saturation for this node. Nodes receive more rewards the higher their saturation level, up to 100%. Beyond 100% no additional rewards are granted. The current stake saturation level is 940k NYMs, computed as S/K where S is target amount of tokens staked in the network and K is the number of nodes in the reward set."
/>
)
);
},
Cell: ({ row }) => (
<StyledLink
@@ -171,9 +171,9 @@ export default function MixnodesPage() {
),
},
{
id: 'pledge_amount',
header: 'Bond',
accessorKey: 'pledge_amount',
id: "pledge_amount",
header: "Bond",
accessorKey: "pledge_amount",
size: 185,
Header: () => (
<CustomColumnHeading
@@ -193,9 +193,9 @@ export default function MixnodesPage() {
),
},
{
id: 'profit_percentage',
accessorKey: 'profit_percentage',
header: 'Profit Margin',
id: "profit_percentage",
accessorKey: "profit_percentage",
header: "Profit Margin",
size: 145,
Header: () => (
<CustomColumnHeading
@@ -211,10 +211,10 @@ export default function MixnodesPage() {
),
},
{
id: 'operating_cost',
accessorKey: 'operating_cost',
id: "operating_cost",
accessorKey: "operating_cost",
size: 220,
header: 'Operating Cost',
header: "Operating Cost",
disableColumnMenu: true,
Header: () => (
<CustomColumnHeading
@@ -230,10 +230,10 @@ export default function MixnodesPage() {
),
},
{
id: 'node_performance',
accessorKey: 'node_performance',
id: "node_performance",
accessorKey: "node_performance",
size: 200,
header: 'Routing Score',
header: "Routing Score",
Header: () => (
<CustomColumnHeading
headingTitle="Routing Score"
@@ -248,10 +248,10 @@ export default function MixnodesPage() {
),
},
{
id: 'owner',
accessorKey: 'owner',
id: "owner",
accessorKey: "owner",
size: 150,
header: 'Owner',
header: "Owner",
Header: () => <CustomColumnHeading headingTitle="Owner" />,
Cell: ({ row }) => (
<StyledLink
@@ -265,19 +265,19 @@ export default function MixnodesPage() {
),
},
{
id: 'location',
accessorKey: 'location',
header: 'Location',
id: "location",
accessorKey: "location",
header: "Location",
maxSize: 150,
Header: () => <CustomColumnHeading headingTitle="Location" />,
Cell: ({ row }) => (
<Tooltip text={row.original.location} id="mixnode-location-text">
<Box
sx={{
overflow: 'hidden',
whiteSpace: 'nowrap',
textOverflow: 'ellipsis',
cursor: 'pointer',
overflow: "hidden",
whiteSpace: "nowrap",
textOverflow: "ellipsis",
cursor: "pointer",
color: useGetMixNodeStatusColor(row.original.status),
}}
>
@@ -287,9 +287,9 @@ export default function MixnodesPage() {
),
},
{
id: 'host',
accessorKey: 'host',
header: 'Host',
id: "host",
accessorKey: "host",
header: "Host",
size: 130,
Header: () => <CustomColumnHeading headingTitle="Host" />,
Cell: ({ row }) => (
@@ -303,12 +303,12 @@ export default function MixnodesPage() {
},
],
},
]
}, [handleOnDelegate, isMobile])
];
}, [handleOnDelegate, isMobile]);
const data = useMemo(() => {
return mixnodeToGridRow(mixnodes?.data)
}, [mixnodes?.data])
return mixnodeToGridRow(mixnodes?.data);
}, [mixnodes?.data]);
const table = useMaterialReactTable({
columns,
@@ -317,11 +317,11 @@ export default function MixnodesPage() {
state: {
isLoading: mixnodes?.isLoading,
},
layoutMode: 'grid-no-grow',
layoutMode: "grid-no-grow",
initialState: {
columnPinning: { left: ['delegate'] },
columnPinning: { left: ["delegate"] },
},
})
});
return (
<DelegationsProvider>
@@ -333,7 +333,7 @@ export default function MixnodesPage() {
<Card
sx={{
padding: 2,
height: '100%',
height: "100%",
}}
>
<TableToolbar
@@ -350,7 +350,7 @@ export default function MixnodesPage() {
fullWidth
size="large"
variant="outlined"
onClick={() => router.push('/delegations')}
onClick={() => router.push("/delegations")}
>
Delegations
</Button>
@@ -364,7 +364,7 @@ export default function MixnodesPage() {
{itemSelectedForDelegation && (
<DelegateModal
onClose={() => {
setItemSelectedForDelegation(undefined)
setItemSelectedForDelegation(undefined);
}}
header="Delegate"
buttonText="Delegate stake"
@@ -382,19 +382,19 @@ export default function MixnodesPage() {
{...confirmationModalProps}
open={Boolean(confirmationModalProps)}
onClose={async () => {
setConfirmationModalProps(undefined)
if (confirmationModalProps.status === 'success') {
router.push('/delegations')
setConfirmationModalProps(undefined);
if (confirmationModalProps.status === "success") {
router.push("/delegations");
}
}}
sx={{
width: {
xs: '90%',
xs: "90%",
sm: 600,
},
}}
/>
)}
</DelegationsProvider>
)
);
}
+3 -3
View File
@@ -22,10 +22,10 @@
"@mui/x-data-grid": "7.1.1",
"@mui/x-date-pickers": "7.1.1",
"@nymproject/contract-clients": "1.2.4-rc.1",
"@nymproject/mui-theme": "workspace:^",
"@nymproject/nym-validator-client": "0.18.0",
"@nymproject/mui-theme": "workspace:^1.0.0",
"@nymproject/nym-validator-client": "^0.18.0",
"@nymproject/react": "workspace:^1.0.0",
"@nymproject/types": "workspace:^",
"@nymproject/types": "workspace:^1.0.0",
"@storybook/react": "^6.5.15",
"@types/d3-scale": "^4.0.8",
"big.js": "^6.2.1",