use components from react-components lib
This commit is contained in:
@@ -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>
|
||||
)
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
@@ -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>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
|
||||
Reference in New Issue
Block a user