various ui adjustments

This commit is contained in:
pierre
2022-07-05 17:53:58 +02:00
committed by fmtabbara
parent 508f8324f9
commit 2493abcdff
12 changed files with 135 additions and 110 deletions
@@ -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;
@@ -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>
@@ -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