various ui adjustments
This commit is contained in:
@@ -53,18 +53,18 @@ export const getNodeStatus = ({ status }: { status: MixnodeStatus }) => {
|
||||
|
||||
const BondedNodeCard = (props: Props) => {
|
||||
const { title: rawTitle, identityKey, status: rawStatus, action, children } = props;
|
||||
let title: string | React.ReactNode = (
|
||||
let Title: string | React.ReactNode = (
|
||||
<Typography fontSize={20} fontWeight={600}>
|
||||
{rawTitle}
|
||||
</Typography>
|
||||
);
|
||||
if (rawStatus) {
|
||||
title = (
|
||||
<Stack direction="row" spacing={1.2} alignItems="center">
|
||||
Title = (
|
||||
<Stack direction="column" spacing={1.2}>
|
||||
{getNodeStatus({ status: rawStatus })}
|
||||
<Typography fontSize={20} fontWeight={600}>
|
||||
{rawTitle}
|
||||
</Typography>
|
||||
{getNodeStatus({ status: rawStatus })}
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
@@ -72,7 +72,7 @@ const BondedNodeCard = (props: Props) => {
|
||||
return (
|
||||
<Card variant="outlined" sx={{ overflow: 'auto', border: 'none', dropShadow: 'none' }}>
|
||||
<CardHeader
|
||||
title={title}
|
||||
title={Title}
|
||||
subheader={<IdentityKey identityKey={identityKey} />}
|
||||
action={action}
|
||||
disableTypography
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
import * as React from 'react';
|
||||
import { IconButton, Menu, MenuItem, Stack } from '@mui/material';
|
||||
import { MoreVert } from '@mui/icons-material';
|
||||
import { useEffect } from 'react';
|
||||
import { MixnodeFlow } from '../mixnode/types';
|
||||
import { GatewayFlow } from '../gateway/types';
|
||||
|
||||
interface Props {
|
||||
onFlowChange: (flow: MixnodeFlow | GatewayFlow) => void;
|
||||
items: { label: string; flow: MixnodeFlow | GatewayFlow; icon: React.ReactNode }[];
|
||||
onOpen: (open: boolean) => void;
|
||||
}
|
||||
|
||||
const NodeMenu = ({ onFlowChange, items, onOpen }: Props) => {
|
||||
const [menuAnchorEl, setMenuAnchorEl] = React.useState<null | HTMLElement>(null);
|
||||
const menuOpen = Boolean(menuAnchorEl);
|
||||
|
||||
useEffect(() => {
|
||||
onOpen(menuOpen);
|
||||
}, [menuOpen]);
|
||||
|
||||
const handleMenuClose = () => {
|
||||
setMenuAnchorEl(null);
|
||||
};
|
||||
|
||||
const onClick = (flow: MixnodeFlow | GatewayFlow) => {
|
||||
onFlowChange(flow);
|
||||
handleMenuClose();
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<IconButton
|
||||
sx={{ fontSize: '1rem', padding: 0 }}
|
||||
id="menu-button"
|
||||
onClick={(event) => setMenuAnchorEl(event.currentTarget)}
|
||||
aria-controls={menuOpen ? 'node-menu' : undefined}
|
||||
aria-haspopup="true"
|
||||
aria-expanded={menuOpen ? 'true' : undefined}
|
||||
disableTouchRipple
|
||||
disableFocusRipple
|
||||
disableRipple
|
||||
>
|
||||
<MoreVert fontSize="inherit" sx={{ color: menuOpen ? 'primary.main' : 'text.primary' }} />
|
||||
</IconButton>
|
||||
<Menu
|
||||
open={menuOpen}
|
||||
anchorEl={menuAnchorEl}
|
||||
onClose={handleMenuClose}
|
||||
id="node-menu"
|
||||
sx={{
|
||||
'& .MuiPaper-root': {
|
||||
borderRadius: '4px',
|
||||
},
|
||||
}}
|
||||
>
|
||||
{items.map(({ label, flow, icon }) => (
|
||||
<MenuItem onClick={() => onClick(flow)} key={flow} disableRipple>
|
||||
<Stack direction="row" spacing={2} gap={1}>
|
||||
{icon}
|
||||
{label}
|
||||
</Stack>
|
||||
</MenuItem>
|
||||
))}
|
||||
</Menu>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default NodeMenu;
|
||||
@@ -39,12 +39,6 @@ const CellHeader = ({ children, tooltip, sx, size, align, color }: TableHeader)
|
||||
</MUITableCell>
|
||||
);
|
||||
|
||||
const CellValue = ({ children, align, size, color, sx }: TableCell) => (
|
||||
<MUITableCell component="th" scope="row" sx={{ py: 1, color, ...sx }} align={align} size={size}>
|
||||
{children}
|
||||
</MUITableCell>
|
||||
);
|
||||
|
||||
export type Header = Omit<TableHeader, 'children'> & { header?: React.ReactNode; id: string };
|
||||
export type Cell = Omit<TableCell, 'children'> & { cell: React.ReactNode; id: string };
|
||||
|
||||
@@ -68,9 +62,9 @@ const NodeTable = ({ headers, cells }: TableProps) => (
|
||||
<TableBody>
|
||||
<TableRow key="node-data">
|
||||
{cells.map(({ cell, id, align, size, color, sx }) => (
|
||||
<CellValue align={align} size={size} key={id} sx={sx} color={color}>
|
||||
<MUITableCell component="th" scope="row" sx={{ py: 1, color, ...sx }} align={align} size={size} key={id}>
|
||||
{cell}
|
||||
</CellValue>
|
||||
</MUITableCell>
|
||||
))}
|
||||
</TableRow>
|
||||
</TableBody>
|
||||
|
||||
@@ -4,5 +4,6 @@ export { default as CheckboxInput } from './CheckboxInput';
|
||||
export { default as RadioInput } from './RadioInput';
|
||||
export { default as TextFieldInput } from './TextFieldInput';
|
||||
export { default as CurrencyInput } from './CurrencyInput';
|
||||
export { default as NodeMenu } from './NodeMenu';
|
||||
|
||||
export * from './NodeTable';
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
import React, { useMemo } from 'react';
|
||||
import { IconButton } from '@mui/material';
|
||||
import { MoreVert } from '@mui/icons-material';
|
||||
import React, { useMemo, useState } from 'react';
|
||||
import { useTheme } from '@mui/material/styles';
|
||||
import EditIcon from '@mui/icons-material/Edit';
|
||||
import { BondedGateway } from '../../../context';
|
||||
import { NodeTable, BondedNodeCard, Cell, Header } from '../components';
|
||||
import { NodeTable, BondedNodeCard, Cell, Header, NodeMenu } from '../components';
|
||||
import { GatewayFlow } from './types';
|
||||
import Unbond from '../unbond';
|
||||
|
||||
const headers: Header[] = [
|
||||
{
|
||||
header: 'IP',
|
||||
id: 'ip-header',
|
||||
sx: { pl: 0 },
|
||||
sx: { pl: 0, width: 100 },
|
||||
},
|
||||
{
|
||||
header: 'Bond',
|
||||
@@ -18,13 +19,16 @@ const headers: Header[] = [
|
||||
{
|
||||
id: 'menu-button',
|
||||
size: 'small',
|
||||
sx: { pr: 0 },
|
||||
sx: { width: 34, maxWidth: 34 },
|
||||
},
|
||||
];
|
||||
|
||||
const GatewayCard = ({ gateway }: { gateway: BondedGateway }) => {
|
||||
const { ip, bond } = gateway;
|
||||
const [flow, setFlow] = useState<GatewayFlow>(null);
|
||||
const [nodeMenuOpen, setNodeMenuOpen] = useState(false);
|
||||
const theme = useTheme();
|
||||
|
||||
const cells: Cell[] = useMemo(
|
||||
() => [
|
||||
{
|
||||
@@ -38,21 +42,24 @@ const GatewayCard = ({ gateway }: { gateway: BondedGateway }) => {
|
||||
},
|
||||
{
|
||||
cell: (
|
||||
<IconButton sx={{ fontSize: '1rem', padding: 0 }}>
|
||||
<MoreVert fontSize="inherit" sx={{ color: 'text.primary' }} />
|
||||
</IconButton>
|
||||
<NodeMenu
|
||||
onFlowChange={(newFlow) => setFlow(newFlow as GatewayFlow)}
|
||||
onOpen={(open) => setNodeMenuOpen(open)}
|
||||
items={[{ label: 'Unbond', flow: 'unbond', icon: <EditIcon fontSize="inherit" /> }]}
|
||||
/>
|
||||
),
|
||||
id: 'menu-button-cell',
|
||||
align: 'center',
|
||||
size: 'small',
|
||||
sx: { pr: 0 },
|
||||
sx: { backgroundColor: nodeMenuOpen ? '#FB6E4E0D' : undefined, px: 0 },
|
||||
},
|
||||
],
|
||||
[gateway, theme],
|
||||
[gateway, theme, nodeMenuOpen],
|
||||
);
|
||||
return (
|
||||
<BondedNodeCard title="Valhalla gateway" identityKey={gateway.key}>
|
||||
<NodeTable headers={headers} cells={cells} />
|
||||
<Unbond node={gateway} show={flow === 'unbond'} onClose={() => setFlow(null)} />
|
||||
</BondedNodeCard>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
export type GatewayFlow = 'unbond' | null;
|
||||
@@ -2,15 +2,15 @@ import React, { useMemo, useState } from 'react';
|
||||
import { Button, Typography } from '@mui/material';
|
||||
import { useTheme } from '@mui/material/styles';
|
||||
import { Link } from '@nymproject/react/link/Link';
|
||||
import EditIcon from '@mui/icons-material/Edit';
|
||||
import { BondedMixnode } from '../../../context';
|
||||
import { Node as NodeIcon } from '../../../svg-icons/node';
|
||||
import { NodeTable, BondedNodeCard, Cell, Header } from '../components';
|
||||
import { NodeTable, BondedNodeCard, Cell, Header, NodeMenu } from '../components';
|
||||
import NodeSettings from './node-settings';
|
||||
import BondMore from './bond-more';
|
||||
import NodeMenu from './NodeMenu';
|
||||
import { MixnodeFlow } from './types';
|
||||
import RedeemRewards from './redeem';
|
||||
import Unbond from './unbond';
|
||||
import Unbond from '../unbond';
|
||||
import CompoundRewards from './compound';
|
||||
|
||||
const headers: Header[] = [
|
||||
@@ -52,13 +52,14 @@ const headers: Header[] = [
|
||||
{
|
||||
id: 'menu-button',
|
||||
size: 'small',
|
||||
sx: { pr: 0 },
|
||||
sx: { width: 34, maxWidth: 34 },
|
||||
},
|
||||
];
|
||||
|
||||
const MixnodeCard = ({ mixnode }: { mixnode: BondedMixnode }) => {
|
||||
const { stake, bond, stakeSaturation, profitMargin, nodeRewards, operatorRewards, delegators } = mixnode;
|
||||
const [flow, setFlow] = useState<MixnodeFlow>(null);
|
||||
const [nodeMenuOpen, setNodeMenuOpen] = useState(false);
|
||||
const theme = useTheme();
|
||||
|
||||
const cells: Cell[] = useMemo(
|
||||
@@ -94,14 +95,25 @@ const MixnodeCard = ({ mixnode }: { mixnode: BondedMixnode }) => {
|
||||
id: 'delegators-cell',
|
||||
},
|
||||
{
|
||||
cell: <NodeMenu onFlowChange={(newFlow) => setFlow(newFlow)} />,
|
||||
cell: (
|
||||
<NodeMenu
|
||||
onFlowChange={(newFlow) => setFlow(newFlow as MixnodeFlow)}
|
||||
onOpen={(open) => setNodeMenuOpen(open)}
|
||||
items={[
|
||||
{ label: 'Bond more', flow: 'bondMore', icon: <EditIcon fontSize="inherit" /> },
|
||||
{ label: 'Unbond', flow: 'unbond', icon: <EditIcon fontSize="inherit" /> },
|
||||
{ label: 'Compound rewards', flow: 'compound', icon: <EditIcon fontSize="inherit" /> },
|
||||
{ label: 'Redeem rewards', flow: 'redeem', icon: <EditIcon fontSize="inherit" /> },
|
||||
]}
|
||||
/>
|
||||
),
|
||||
id: 'menu-button-cell',
|
||||
align: 'center',
|
||||
size: 'small',
|
||||
sx: { pr: 0 },
|
||||
sx: { backgroundColor: nodeMenuOpen ? '#FB6E4E0D' : undefined, px: 0 },
|
||||
},
|
||||
],
|
||||
[mixnode, theme],
|
||||
[mixnode, theme, nodeMenuOpen],
|
||||
);
|
||||
return (
|
||||
<BondedNodeCard
|
||||
@@ -135,7 +147,7 @@ const MixnodeCard = ({ mixnode }: { mixnode: BondedMixnode }) => {
|
||||
<NodeSettings mixnode={mixnode} show={flow === 'nodeSettings'} onClose={() => setFlow(null)} />
|
||||
<BondMore mixnode={mixnode} show={flow === 'bondMore'} onClose={() => setFlow(null)} />
|
||||
<RedeemRewards mixnode={mixnode} show={flow === 'redeem'} onClose={() => setFlow(null)} />
|
||||
<Unbond mixnode={mixnode} show={flow === 'unbound'} onClose={() => setFlow(null)} />
|
||||
<Unbond node={mixnode} show={flow === 'unbond'} onClose={() => setFlow(null)} />
|
||||
<CompoundRewards mixnode={mixnode} show={flow === 'compound'} onClose={() => setFlow(null)} />
|
||||
</BondedNodeCard>
|
||||
);
|
||||
|
||||
@@ -1,64 +0,0 @@
|
||||
import * as React from 'react';
|
||||
import { IconButton, Menu, MenuItem } from '@mui/material';
|
||||
import { MoreVert } from '@mui/icons-material';
|
||||
import EditIcon from '@mui/icons-material/Edit';
|
||||
import { MixnodeFlow } from './types';
|
||||
|
||||
const NodeMenu = ({ onFlowChange }: { onFlowChange: (flow: MixnodeFlow) => void }) => {
|
||||
const [menuAnchorEl, setMenuAnchorEl] = React.useState<null | HTMLElement>(null);
|
||||
const menuOpen = Boolean(menuAnchorEl);
|
||||
|
||||
const handleMenuClose = () => {
|
||||
setMenuAnchorEl(null);
|
||||
};
|
||||
|
||||
const onClick = (flow: MixnodeFlow) => {
|
||||
onFlowChange(flow);
|
||||
handleMenuClose();
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<IconButton
|
||||
sx={{ fontSize: '1rem', padding: 0 }}
|
||||
id="menu-button"
|
||||
onClick={(event) => setMenuAnchorEl(event.currentTarget)}
|
||||
aria-controls={menuOpen ? 'node-menu' : undefined}
|
||||
aria-haspopup="true"
|
||||
aria-expanded={menuOpen ? 'true' : undefined}
|
||||
>
|
||||
<MoreVert fontSize="inherit" sx={{ color: 'text.primary' }} />
|
||||
</IconButton>
|
||||
<Menu
|
||||
open={menuOpen}
|
||||
anchorEl={menuAnchorEl}
|
||||
onClose={handleMenuClose}
|
||||
id="node-menu"
|
||||
sx={{
|
||||
'& .MuiPaper-root': {
|
||||
borderRadius: '4px',
|
||||
},
|
||||
}}
|
||||
>
|
||||
<MenuItem onClick={() => onClick('bondMore')} disableRipple>
|
||||
<EditIcon fontSize="inherit" />
|
||||
Bond more
|
||||
</MenuItem>
|
||||
<MenuItem onClick={() => onClick('unbound')} disableRipple>
|
||||
<EditIcon fontSize="inherit" />
|
||||
Unbond
|
||||
</MenuItem>
|
||||
<MenuItem onClick={() => onClick('compound')} disableRipple>
|
||||
<EditIcon fontSize="inherit" />
|
||||
Compound rewards
|
||||
</MenuItem>
|
||||
<MenuItem onClick={() => onClick('redeem')} disableRipple>
|
||||
<EditIcon fontSize="inherit" />
|
||||
Redeem rewards
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default NodeMenu;
|
||||
@@ -1 +1 @@
|
||||
export type MixnodeFlow = 'nodeSettings' | 'bondMore' | 'unbound' | 'compound' | 'redeem' | null;
|
||||
export type MixnodeFlow = 'nodeSettings' | 'bondMore' | 'unbond' | 'compound' | 'redeem' | null;
|
||||
|
||||
+11
-7
@@ -1,7 +1,7 @@
|
||||
import * as React from 'react';
|
||||
import { Divider, Stack, Typography } from '@mui/material';
|
||||
import { MajorCurrencyAmount } from '@nymproject/types';
|
||||
import { SimpleDialog } from '../../../../components';
|
||||
import { SimpleDialog } from '../../../components';
|
||||
|
||||
export interface Props {
|
||||
open: boolean;
|
||||
@@ -9,7 +9,7 @@ export interface Props {
|
||||
onConfirm: () => void;
|
||||
onCancel: () => void;
|
||||
bond: MajorCurrencyAmount;
|
||||
rewards: MajorCurrencyAmount;
|
||||
rewards?: MajorCurrencyAmount;
|
||||
fee: MajorCurrencyAmount;
|
||||
}
|
||||
|
||||
@@ -31,11 +31,15 @@ const SummaryModal = ({ open, onClose, onConfirm, onCancel, bond, rewards, fee }
|
||||
<Typography fontWeight={400}>{`${bond.amount} ${bond.denom}`}</Typography>
|
||||
</Stack>
|
||||
<Divider sx={{ my: 1 }} />
|
||||
<Stack direction="row" justifyContent="space-between">
|
||||
<Typography fontWeight={400}>Operator rewards</Typography>
|
||||
<Typography fontWeight={400}>{`${rewards.amount} ${rewards.denom}`}</Typography>
|
||||
</Stack>
|
||||
<Divider sx={{ my: 1 }} />
|
||||
{rewards?.amount && (
|
||||
<>
|
||||
<Stack direction="row" justifyContent="space-between">
|
||||
<Typography fontWeight={400}>Operator rewards</Typography>
|
||||
<Typography fontWeight={400}>{`${rewards.amount} ${rewards.denom}`}</Typography>
|
||||
</Stack>
|
||||
<Divider sx={{ my: 1 }} />
|
||||
</>
|
||||
)}
|
||||
<Stack direction="row" justifyContent="space-between">
|
||||
<Typography fontWeight={400}>Fee for this operation</Typography>
|
||||
<Typography fontWeight={400}>{`${fee.amount} ${fee.denom}`}</Typography>
|
||||
+6
-6
@@ -3,17 +3,17 @@ import { useContext, useEffect, useState } from 'react';
|
||||
import { MajorCurrencyAmount, TransactionExecuteResult } from '@nymproject/types';
|
||||
import { Link } from '@nymproject/react/link/Link';
|
||||
import { Typography } from '@mui/material';
|
||||
import { AppContext, BondedMixnode, urls } from '../../../../context';
|
||||
import { AppContext, BondedGateway, BondedMixnode, urls } from '../../../context';
|
||||
import SummaryModal from './SummaryModal';
|
||||
import { ConfirmationModal } from '../../../../components';
|
||||
import { ConfirmationModal } from '../../../components';
|
||||
|
||||
interface Props {
|
||||
mixnode: BondedMixnode;
|
||||
node: BondedMixnode | BondedGateway;
|
||||
show: boolean;
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
const Unbond = ({ mixnode, show, onClose }: Props) => {
|
||||
const Unbond = ({ node, show, onClose }: Props) => {
|
||||
const [fee, setFee] = useState<MajorCurrencyAmount>({ amount: '0', denom: 'NYM' });
|
||||
const [step, setStep] = useState<1 | 2>(1);
|
||||
const [tx, setTx] = useState<TransactionExecuteResult>();
|
||||
@@ -42,8 +42,8 @@ const Unbond = ({ mixnode, show, onClose }: Props) => {
|
||||
onClose={reset}
|
||||
onConfirm={submit}
|
||||
onCancel={reset}
|
||||
bond={mixnode.bond}
|
||||
rewards={mixnode.operatorRewards}
|
||||
bond={node.bond}
|
||||
rewards={(node as BondedMixnode).operatorRewards}
|
||||
fee={fee as MajorCurrencyAmount}
|
||||
/>
|
||||
<ConfirmationModal
|
||||
Reference in New Issue
Block a user