diff --git a/explorer/src/components/DetailTable.tsx b/explorer/src/components/DetailTable.tsx index d333f0eb59..39355d0607 100644 --- a/explorer/src/components/DetailTable.tsx +++ b/explorer/src/components/DetailTable.tsx @@ -14,7 +14,6 @@ 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 { cellStyles } from './Universal-DataGrid'; import { unymToNym } from '../utils/currency'; import { GatewayEnrichedRowType } from './Gateways'; import { MixnodeRowType } from './MixNodes'; @@ -110,7 +109,6 @@ export const DetailTable: FCWithChildren<{ scope="row" variant="body" sx={{ - ...cellStyles, padding: 2, width: 200, fontSize: 14, diff --git a/explorer/src/components/MixNodes/Status.tsx b/explorer/src/components/MixNodes/Status.tsx index a2da302c87..9bcea7697f 100644 --- a/explorer/src/components/MixNodes/Status.tsx +++ b/explorer/src/components/MixNodes/Status.tsx @@ -1,29 +1,12 @@ import { Typography } from '@mui/material'; import * as React from 'react'; -import { Theme, useTheme } from '@mui/material/styles'; -import { getMixNodeIcon } from '../Icons'; -import { MixnodeStatus } from '../../typeDefs/explorer-api'; +import { getMixNodeIcon } from '@src/components/Icons'; +import { MixnodeStatus } from '@src/typeDefs/explorer-api'; +import { useGetMixNodeStatusColor } from '@src/hooks/useGetMixnodeStatusColor'; interface MixNodeStatusProps { status: MixnodeStatus; } - -export const getMixNodeStatusColor = (theme: Theme, status: MixnodeStatus) => { - let color; - switch (status) { - case MixnodeStatus.active: - color = theme.palette.nym.networkExplorer.mixnodes.status.active; - break; - case MixnodeStatus.standby: - color = theme.palette.nym.networkExplorer.mixnodes.status.standby; - break; - default: - color = theme.palette.nym.networkExplorer.mixnodes.status.inactive; - break; - } - return color; -}; - // TODO: should be done with i18n export const getMixNodeStatusText = (status: MixnodeStatus) => { switch (status) { @@ -37,9 +20,8 @@ export const getMixNodeStatusText = (status: MixnodeStatus) => { }; export const MixNodeStatus: FCWithChildren = ({ status }) => { - const theme = useTheme(); const Icon = React.useMemo(() => getMixNodeIcon(status), [status]); - const color = React.useMemo(() => getMixNodeStatusColor(theme, status), [status, theme]); + const color = React.useMemo(() => useGetMixNodeStatusColor(status), [status]); return ( diff --git a/explorer/src/components/StyledLink.tsx b/explorer/src/components/StyledLink.tsx new file mode 100644 index 0000000000..6db211d849 --- /dev/null +++ b/explorer/src/components/StyledLink.tsx @@ -0,0 +1,19 @@ +import React from 'react'; +import { Link as MuiLink } from '@mui/material'; +import { Link as RRDL } from 'react-router-dom'; + +type StyledLinkProps = { + to: string; + children: string; + target?: React.HTMLAttributeAnchorTarget; + dataTestId?: string; + color?: string; +}; + +const StyledLink = ({ to, children, dataTestId, target, color = 'inherit' }: StyledLinkProps) => ( + + {children} + +); + +export default StyledLink; diff --git a/explorer/src/components/Universal-DataGrid.tsx b/explorer/src/components/Universal-DataGrid.tsx index 4a53901f12..0dc19edda8 100644 --- a/explorer/src/components/Universal-DataGrid.tsx +++ b/explorer/src/components/Universal-DataGrid.tsx @@ -2,7 +2,6 @@ import * as React from 'react'; import { makeStyles } from '@mui/styles'; import { DataGrid, GridColDef, useGridApiContext, useGridState } from '@mui/x-data-grid'; import Pagination from '@mui/material/Pagination'; -import { SxProps } from '@mui/system'; import { LinearProgress } from '@mui/material'; import { GridInitialStateCommunity } from '@mui/x-data-grid/models/gridStateCommunity'; @@ -12,18 +11,6 @@ const useStyles = makeStyles({ }, }); -export const cellStyles: SxProps = { - padding: 0, - maxHeight: 100, - color: 'inherit', - textDecoration: 'none', - fontWeight: 400, - lineHeight: 2, - fontSize: '12px', - wordBreak: 'break-word', - whiteSpace: 'break-spaces', -}; - const CustomPagination = () => { const apiRef = useGridApiContext(); const [state] = useGridState(apiRef); diff --git a/explorer/src/pages/Gateways/index.tsx b/explorer/src/pages/Gateways/index.tsx index 028b90d8b8..78da55d1ab 100644 --- a/explorer/src/pages/Gateways/index.tsx +++ b/explorer/src/pages/Gateways/index.tsx @@ -1,6 +1,5 @@ import * as React from 'react'; -import { Link as RRDLink } from 'react-router-dom'; -import { Box, Card, Grid, Link as MuiLink } from '@mui/material'; +import { Box, Card, Grid, Stack } from '@mui/material'; import { useTheme } from '@mui/material/styles'; import { CopyToClipboard } from '@nymproject/react/clipboard/CopyToClipboard'; import { GridColDef, GridRenderCellParams } from '@mui/x-data-grid'; @@ -13,12 +12,13 @@ import { GatewayResponse } from '../../typeDefs/explorer-api'; import { TableToolbar } from '../../components/TableToolbar'; import { CustomColumnHeading } from '../../components/CustomColumnHeading'; import { Title } from '../../components/Title'; -import { cellStyles, UniversalDataGrid } from '../../components/Universal-DataGrid'; +import { UniversalDataGrid } from '../../components/Universal-DataGrid'; import { unymToNym } from '../../utils/currency'; import { Tooltip } from '../../components/Tooltip'; import { NYM_BIG_DIPPER } from '../../api/constants'; import { splice } from '../../utils'; import { VersionDisplaySelector, VersionSelectOptions } from '../../components/Gateways/VersionDisplaySelector'; +import StyledLink from '../../components/StyledLink'; export const PageGateways: FCWithChildren = () => { const { gateways } = useMainContext(); @@ -100,28 +100,19 @@ export const PageGateways: FCWithChildren = () => { field: 'identity_key', renderHeader: () => , headerClassName: 'MuiDataGrid-header-override', - width: 380, + width: 400, disableColumnMenu: true, - headerAlign: 'left', + headerAlign: 'center', renderCell: (params: GridRenderCellParams) => ( - <> - - - {params.value} - - + + + {params.value} + ), }, { field: 'node_performance', + align: 'center', renderHeader: () => ( <> { ), - width: 175, + width: 120, disableColumnMenu: true, - headerAlign: 'left', + headerAlign: 'center', headerClassName: 'MuiDataGrid-header-override', renderCell: (params: GridRenderCellParams) => ( - + {`${params.value}%`} - + ), }, { field: 'version', + align: 'center', renderHeader: () => , width: 150, disableColumnMenu: true, - headerAlign: 'left', + headerAlign: 'center', headerClassName: 'MuiDataGrid-header-override', renderCell: (params: GridRenderCellParams) => ( - + {params.value} - + ), sortComparator: (a, b) => { if (gte(a, b)) return 1; @@ -183,7 +165,7 @@ export const PageGateways: FCWithChildren = () => { renderCell: (params: GridRenderCellParams) => ( handleSearch(params.value as string)} - sx={{ ...cellStyles, justifyContent: 'flex-start', cursor: 'pointer' }} + sx={{ justifyContent: 'flex-start', cursor: 'pointer' }} data-testid="location-button" > @@ -208,14 +190,9 @@ export const PageGateways: FCWithChildren = () => { headerAlign: 'left', headerClassName: 'MuiDataGrid-header-override', renderCell: (params: GridRenderCellParams) => ( - + {params.value} - + ), }, { @@ -227,14 +204,9 @@ export const PageGateways: FCWithChildren = () => { headerAlign: 'left', headerClassName: 'MuiDataGrid-header-override', renderCell: (params: GridRenderCellParams) => ( - + {splice(7, 29, params.value)} - + ), }, { @@ -246,14 +218,9 @@ export const PageGateways: FCWithChildren = () => { headerClassName: 'MuiDataGrid-header-override', headerAlign: 'left', renderCell: (params: GridRenderCellParams) => ( - - {unymToNym(params.value, 6)} - + + {`${unymToNym(params.value, 6)}`} + ), }, ]; diff --git a/explorer/src/pages/Mixnodes/index.tsx b/explorer/src/pages/Mixnodes/index.tsx index cf5cc789c3..6f3cc0b1b8 100644 --- a/explorer/src/pages/Mixnodes/index.tsx +++ b/explorer/src/pages/Mixnodes/index.tsx @@ -1,38 +1,30 @@ import * as React from 'react'; import { GridColDef, GridRenderCellParams } from '@mui/x-data-grid'; -import { Button, Card, Grid, Link as MuiLink, Box } from '@mui/material'; -import { Link as RRDLink, useParams, useNavigate } from 'react-router-dom'; +import { Stack, Card, Grid, Box } from '@mui/material'; +import { useParams, useNavigate } from 'react-router-dom'; import { SelectChangeEvent } from '@mui/material/Select'; -import { Stack, SxProps } from '@mui/system'; -import { Theme, useTheme } from '@mui/material/styles'; import { CopyToClipboard } from '@nymproject/react/clipboard/CopyToClipboard'; import { useWallet } from '@cosmos-kit/react'; -import { useMainContext } from '../../context/main'; -import { MixnodeRowType, mixnodeToGridRow } from '../../components/MixNodes'; -import { TableToolbar } from '../../components/TableToolbar'; -import { MixNodeResponse, MixnodeStatusWithAll, toMixnodeStatus } from '../../typeDefs/explorer-api'; -import { NYM_BIG_DIPPER } from '../../api/constants'; -import { CustomColumnHeading } from '../../components/CustomColumnHeading'; -import { Title } from '../../components/Title'; -import { cellStyles, UniversalDataGrid } from '../../components/Universal-DataGrid'; -import { currencyToString } from '../../utils/currency'; -import { splice } from '../../utils'; -import { getMixNodeStatusColor } from '../../components/MixNodes/Status'; -import { MixNodeStatusDropdown } from '../../components/MixNodes/StatusDropdown'; -import { Tooltip } from '../../components/Tooltip'; -import { DelegateIconButton } from '../../components/Delegations/components/DelegateIconButton'; -import { DelegationModal, DelegationModalProps } from '../../components/Delegations/components/DelegationModal'; -import { DelegateModal } from '../../components/Delegations/components/DelegateModal'; - -const getCellFontStyle = (theme: Theme, row: MixnodeRowType, textColor?: string) => ({ - color: textColor || getMixNodeStatusColor(theme, row.status), -}); - -const getCellStyles = (theme: Theme, row: MixnodeRowType, textColor?: string): SxProps => ({ - ...cellStyles, - // TODO: should these be here, or change in `cellStyles`?? - ...getCellFontStyle(theme, row, textColor), -}); +import { useMainContext } from '@src/context/main'; +import { + DelegateIconButton, + DelegationModal, + DelegationModalProps, + DelegateModal, + CustomColumnHeading, + StyledLink, + Title, + UniversalDataGrid, + TableToolbar, + Tooltip, + MixNodeStatusDropdown, + mixnodeToGridRow, +} from '@src/components'; +import { MixNodeResponse, MixnodeStatusWithAll, toMixnodeStatus } from '@src/typeDefs/explorer-api'; +import { NYM_BIG_DIPPER } from '@src/api/constants'; +import { currencyToString } from '@src/utils/currency'; +import { splice } from '@src/utils'; +import { useGetMixNodeStatusColor } from '@src/hooks'; export const PageMixnodes: FCWithChildren = () => { const { mixnodes, fetchMixnodes } = useMainContext(); @@ -46,7 +38,6 @@ export const PageMixnodes: FCWithChildren = () => { const [confirmationModalProps, setConfirmationModalProps] = React.useState(); const { status } = useParams<{ status: MixnodeStatusWithAll | undefined }>(); - const theme = useTheme(); const navigate = useNavigate(); const wallet = useWallet(); @@ -92,86 +83,90 @@ export const PageMixnodes: FCWithChildren = () => { ); }; + const handleOnDelegate = ({ identityKey, mixId }: { identityKey: string; mixId: number }) => { + if (wallet.status !== 'Connected') { + setConfirmationModalProps({ + status: 'info', + message: 'Please connect your wallet to delegate', + }); + } else { + setItemSelectedForDelegation({ identityKey, mixId }); + } + }; + const columns: GridColDef[] = [ { field: 'identity_key', + width: 215, headerName: 'Identity Key', + headerAlign: 'left', + headerClassName: 'MuiDataGrid-header-override', disableColumnMenu: true, renderHeader: () => , - headerClassName: 'MuiDataGrid-header-override', - width: 215, - headerAlign: 'left', renderCell: (params: GridRenderCellParams) => ( { - if (wallet.status !== 'Connected') { - setConfirmationModalProps({ - status: 'info', - message: 'Please connect your wallet to delegate', - }); - } else { - setItemSelectedForDelegation({ identityKey: params.row.identity_key, mixId: params.row.mix_id }); - } - }} + onDelegate={() => handleOnDelegate({ identityKey: params.value, mixId: params.row.mix_id })} /> - {splice(7, 29, params.value)} - + ), }, { field: 'mix_id', - headerName: 'Mix ID', - disableColumnMenu: true, - renderHeader: () => , - headerClassName: 'MuiDataGrid-header-override', width: 85, align: 'center', + headerName: 'Mix ID', headerAlign: 'center', + headerClassName: 'MuiDataGrid-header-override', + disableColumnMenu: true, + renderHeader: () => , renderCell: (params: GridRenderCellParams) => ( - {params.value} - + ), }, { field: 'bond', - headerName: 'Stake', - disableColumnMenu: true, - renderHeader: () => , - type: 'number', - headerClassName: 'MuiDataGrid-header-override', width: 150, align: 'left', + type: 'number', + disableColumnMenu: true, + headerName: 'Stake', headerAlign: 'left', + headerClassName: 'MuiDataGrid-header-override', + renderHeader: () => , renderCell: (params: GridRenderCellParams) => ( - - {currencyToString({ amount: params.value, dp: 2 })} - + {currencyToString(params.value)} + ), }, { field: 'stake_saturation', + width: 185, + align: 'center', headerName: 'Stake Saturation', + headerAlign: 'center', + headerClassName: 'MuiDataGrid-header-override', disableColumnMenu: true, renderHeader: () => ( { 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." /> ), - headerClassName: 'MuiDataGrid-header-override', - width: 185, - align: 'center', - headerAlign: 'center', renderCell: (params: GridRenderCellParams) => ( - 100 ? 'theme.palette.warning.main' : undefined), - }} - component={RRDLink} + - {`${params.value} %`} - + color={useGetMixNodeStatusColor(params.row.status)} + >{`${params.value} %`} ), }, { field: 'pledge_amount', - headerName: 'Bond', - disableColumnMenu: true, width: 150, - headerClassName: 'MuiDataGrid-header-override', - renderHeader: () => , - type: 'number', - headerAlign: 'left', align: 'left', + type: 'number', + headerName: 'Bond', + headerAlign: 'left', + headerClassName: 'MuiDataGrid-header-override', + disableColumnMenu: true, + renderHeader: () => , renderCell: (params: GridRenderCellParams) => ( - - {currencyToString({ amount: params.value, dp: 2 })} - + {currencyToString(params.value)} + ), }, { field: 'profit_percentage', + width: 145, + align: 'center', headerName: 'Profit Margin', + headerAlign: 'center', + headerClassName: 'MuiDataGrid-header-override', disableColumnMenu: true, renderHeader: () => ( { tooltipInfo="Percentage of the delegators rewards that the operator takes as fee before rewards are distributed to the delegators." /> ), - headerClassName: 'MuiDataGrid-header-override', - width: 145, - headerAlign: 'center', - align: 'center', renderCell: (params: GridRenderCellParams) => ( - - {params.value}% - + color={useGetMixNodeStatusColor(params.row.status)} + >{`${params.value}%`} ), }, { field: 'operating_cost', + width: 170, + align: 'center', headerName: 'Operating Cost', + headerAlign: 'center', + headerClassName: 'MuiDataGrid-header-override', + disableColumnMenu: true, renderHeader: () => ( ), - headerClassName: 'MuiDataGrid-header-override', - width: 170, - headerAlign: 'center', - align: 'center', - disableColumnMenu: true, renderCell: (params: GridRenderCellParams) => ( - - {params.value} NYM - + color={useGetMixNodeStatusColor(params.row.status)} + >{`${params.value} NYM`} ), }, { field: 'node_performance', + width: 165, + align: 'center', headerName: 'Routing Score', + headerAlign: 'center', + headerClassName: 'MuiDataGrid-header-override', disableColumnMenu: true, renderHeader: () => ( { tooltipInfo="Mixnode's most recent score (measured in the last 15 minutes). Routing score is relative to that of the network. Each time a gateway is tested, the test packets have to go through the full path of the network (gateway + 3 nodes). If a node in the path drop packets it will affect the score of the gateway and other nodes in the test." /> ), - headerClassName: 'MuiDataGrid-header-override', - width: 165, - headerAlign: 'center', - align: 'center', renderCell: (params: GridRenderCellParams) => ( - - {params.value}% - + color={useGetMixNodeStatusColor(params.row.status)} + >{`${params.value}%`} ), }, { field: 'owner', - headerName: 'Owner', - disableColumnMenu: true, - renderHeader: () => , width: 120, + headerName: 'Owner', headerAlign: 'left', headerClassName: 'MuiDataGrid-header-override', + disableColumnMenu: true, + renderHeader: () => , renderCell: (params: GridRenderCellParams) => ( - {splice(7, 29, params.value)} - + ), }, { field: 'location', + width: 150, headerName: 'Location', - renderHeader: () => , - disableColumnMenu: true, - width: 120, headerAlign: 'left', headerClassName: 'MuiDataGrid-header-override', + disableColumnMenu: true, + renderHeader: () => , renderCell: (params: GridRenderCellParams) => ( - + + handleSearch(params.value)} + > + {params.value} + + ), }, { field: 'host', - headerName: 'Host', - renderHeader: () => , - disableColumnMenu: true, - headerClassName: 'MuiDataGrid-header-override', width: 130, + headerName: 'Host', headerAlign: 'left', + headerClassName: 'MuiDataGrid-header-override', + disableColumnMenu: true, + renderHeader: () => , renderCell: (params: GridRenderCellParams) => ( - {params.value} - + ), }, ]; @@ -359,6 +329,7 @@ export const PageMixnodes: FCWithChildren = () => { const handlePageSize = (event: SelectChangeEvent) => { setPageSize(event.target.value); }; + return ( <> diff --git a/explorer/src/pages/MixnodesMap/index.tsx b/explorer/src/pages/MixnodesMap/index.tsx index c6148cb894..a7c0048a11 100644 --- a/explorer/src/pages/MixnodesMap/index.tsx +++ b/explorer/src/pages/MixnodesMap/index.tsx @@ -5,7 +5,7 @@ import { ContentCard } from '../../components/ContentCard'; import { CustomColumnHeading } from '../../components/CustomColumnHeading'; import { TableToolbar } from '../../components/TableToolbar'; import { Title } from '../../components/Title'; -import { UniversalDataGrid, cellStyles } from '../../components/Universal-DataGrid'; +import { UniversalDataGrid } from '../../components/Universal-DataGrid'; import { WorldMap } from '../../components/WorldMap'; import { useMainContext } from '../../context/main'; import { CountryDataRowType, countryDataToGridRow } from '../../utils'; @@ -31,11 +31,7 @@ export const PageMixnodesMap: FCWithChildren = () => { flex: 1, headerAlign: 'left', headerClassName: 'MuiDataGrid-header-override', - renderCell: (params: GridRenderCellParams) => ( - <Typography sx={cellStyles} data-testid="country-name"> - {params.value} - </Typography> - ), + renderCell: (params: GridRenderCellParams) => <Typography data-testid="country-name">{params.value}</Typography>, }, { field: 'nodes', @@ -44,9 +40,7 @@ export const PageMixnodesMap: FCWithChildren = () => { headerAlign: 'left', headerClassName: 'MuiDataGrid-header-override', renderCell: (params: GridRenderCellParams) => ( - <Typography sx={cellStyles} data-testid="number-of-nodes"> - {params.value} - </Typography> + <Typography data-testid="number-of-nodes">{params.value}</Typography> ), }, { @@ -55,11 +49,7 @@ export const PageMixnodesMap: FCWithChildren = () => { flex: 1, headerAlign: 'left', headerClassName: 'MuiDataGrid-header-override', - renderCell: (params: GridRenderCellParams) => ( - <Typography sx={cellStyles} data-testid="percentage"> - {params.value} - </Typography> - ), + renderCell: (params: GridRenderCellParams) => <Typography data-testid="percentage">{params.value}</Typography>, }, ];