Adjust all table layouts with headers (#2480)
* Some small CSS adjustments * Adjust all table layouts with headers * Adjust... somewhat * Adjust * Ok, enough for now... * Adjust extrinsic from display * header adjustments
This commit is contained in:
parent
5b592c11bf
commit
c9cf0581a0
@ -123,13 +123,13 @@ export default React.memo(styled(Apps)`
|
||||
}
|
||||
|
||||
a.apps--SideBar-Item-NavLink-active {
|
||||
background: #fafafa;
|
||||
background: #f2f2f2;
|
||||
border-radius: 0.28571429rem 0 0 0.28571429rem;
|
||||
// border-bottom: 2px solid transparent;
|
||||
color: #3f3f3f;
|
||||
|
||||
&:hover {
|
||||
background: #fafafa;
|
||||
background: #f2f2f2;
|
||||
color: #3f3f3f;
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
@ -79,7 +79,7 @@ function Content ({ className }: Props): React.ReactElement<Props> {
|
||||
}
|
||||
|
||||
export default React.memo(styled(Content)`
|
||||
background: #fafafa;
|
||||
background: #f2f2f2;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-grow: 1;
|
||||
|
||||
@ -38,7 +38,7 @@ async function queryLedger (): Promise<void> {
|
||||
|
||||
function Overview ({ className, onStatusChange }: Props): React.ReactElement<Props> {
|
||||
const { t } = useTranslation();
|
||||
const { allAccounts, hasAccounts } = useAccounts();
|
||||
const { allAccounts } = useAccounts();
|
||||
const [isCreateOpen, toggleCreate] = useToggle();
|
||||
const [isImportOpen, toggleImport] = useToggle();
|
||||
const [isQrOpen, toggleQr] = useToggle();
|
||||
@ -116,42 +116,36 @@ function Overview ({ className, onStatusChange }: Props): React.ReactElement<Pro
|
||||
</>
|
||||
)}
|
||||
</Button.Group>
|
||||
{hasAccounts
|
||||
? (
|
||||
<>
|
||||
<div className='filter--tags'>
|
||||
<Input
|
||||
autoFocus
|
||||
isFull
|
||||
label={t('filter by name or tags')}
|
||||
onChange={setFilter}
|
||||
value={filter}
|
||||
/>
|
||||
</div>
|
||||
<Table>
|
||||
<Table.Head>
|
||||
<th colSpan={4}> </th>
|
||||
<th>{t('transactions')}</th>
|
||||
<th>{t('balances')}</th>
|
||||
<th>{t('type')}</th>
|
||||
<th colSpan={2}> </th>
|
||||
</Table.Head>
|
||||
<Table.Body>
|
||||
{sortedAccounts.map(({ address, isFavorite }): React.ReactNode => (
|
||||
<Account
|
||||
address={address}
|
||||
filter={filter}
|
||||
isFavorite={isFavorite}
|
||||
key={address}
|
||||
toggleFavorite={toggleFavorite}
|
||||
/>
|
||||
))}
|
||||
</Table.Body>
|
||||
</Table>
|
||||
</>
|
||||
)
|
||||
: t('no accounts yet, create or import an existing')
|
||||
}
|
||||
<Table>
|
||||
<Table.Head filter={
|
||||
<div className='filter--tags'>
|
||||
<Input
|
||||
autoFocus
|
||||
isFull
|
||||
label={t('filter by name or tags')}
|
||||
onChange={setFilter}
|
||||
value={filter}
|
||||
/>
|
||||
</div>
|
||||
}>
|
||||
<th className='start' colSpan={4}><h1>{t('accounts')}</h1></th>
|
||||
<th>{t('transactions')}</th>
|
||||
<th>{t('balances')}</th>
|
||||
<th>{t('type')}</th>
|
||||
<th colSpan={2}> </th>
|
||||
</Table.Head>
|
||||
<Table.Body empty={t('no accounts yet, create or import an existing')}>
|
||||
{sortedAccounts.map(({ address, isFavorite }): React.ReactNode => (
|
||||
<Account
|
||||
address={address}
|
||||
filter={filter}
|
||||
isFavorite={isFavorite}
|
||||
key={address}
|
||||
toggleFavorite={toggleFavorite}
|
||||
/>
|
||||
))}
|
||||
</Table.Body>
|
||||
</Table>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -19,7 +19,7 @@ const STORE_FAVS = 'accounts:favorites';
|
||||
|
||||
function Overview ({ className, onStatusChange }: Props): React.ReactElement<Props> {
|
||||
const { t } = useTranslation();
|
||||
const { hasAddresses, allAddresses } = useAddresses();
|
||||
const { allAddresses } = useAddresses();
|
||||
const [isCreateOpen, toggleCreate] = useToggle(false);
|
||||
const [favorites, toggleFavorite] = useFavorites(STORE_FAVS);
|
||||
const [sortedAddresses, setSortedAddresses] = useState<SortedAddress[]>([]);
|
||||
@ -54,41 +54,35 @@ function Overview ({ className, onStatusChange }: Props): React.ReactElement<Pro
|
||||
onStatusChange={onStatusChange}
|
||||
/>
|
||||
)}
|
||||
{hasAddresses
|
||||
? (
|
||||
<>
|
||||
<div className='filter--tags'>
|
||||
<Input
|
||||
autoFocus
|
||||
isFull
|
||||
label={t('filter by name or tags')}
|
||||
onChange={setFilter}
|
||||
value={filter}
|
||||
/>
|
||||
</div>
|
||||
<Table>
|
||||
<Table.Head>
|
||||
<th colSpan={3}> </th>
|
||||
<th>{t('transactions')}</th>
|
||||
<th>{t('balances')}</th>
|
||||
<th colSpan={2}> </th>
|
||||
</Table.Head>
|
||||
<Table.Body>
|
||||
{sortedAddresses.map(({ address, isFavorite }): React.ReactNode => (
|
||||
<Address
|
||||
address={address}
|
||||
filter={filter}
|
||||
isFavorite={isFavorite}
|
||||
key={address}
|
||||
toggleFavorite={toggleFavorite}
|
||||
/>
|
||||
))}
|
||||
</Table.Body>
|
||||
</Table>
|
||||
</>
|
||||
)
|
||||
: t('no addresses saved yet, add any existing address')
|
||||
}
|
||||
<Table>
|
||||
<Table.Head filter={
|
||||
<div className='filter--tags'>
|
||||
<Input
|
||||
autoFocus
|
||||
isFull
|
||||
label={t('filter by name or tags')}
|
||||
onChange={setFilter}
|
||||
value={filter}
|
||||
/>
|
||||
</div>
|
||||
}>
|
||||
<th className='start' colSpan={3}><h1>{t('contacts')}</h1></th>
|
||||
<th>{t('transactions')}</th>
|
||||
<th>{t('balances')}</th>
|
||||
<th colSpan={2}> </th>
|
||||
</Table.Head>
|
||||
<Table.Body empty={t('no addresses saved yet, add any existing address')}>
|
||||
{sortedAddresses.map(({ address, isFavorite }): React.ReactNode => (
|
||||
<Address
|
||||
address={address}
|
||||
filter={filter}
|
||||
isFavorite={isFavorite}
|
||||
key={address}
|
||||
toggleFavorite={toggleFavorite}
|
||||
/>
|
||||
))}
|
||||
</Table.Body>
|
||||
</Table>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -43,7 +43,7 @@ const Payload = styled.pre`
|
||||
cursor: copy;
|
||||
font-family: monospace;
|
||||
border: 1px dashed #c2c2c2;
|
||||
background: #fafafa;
|
||||
background: #f2f2f2;
|
||||
padding: 1rem;
|
||||
width: 100%;
|
||||
margin: 1rem 0;
|
||||
|
||||
@ -44,32 +44,27 @@ function Proposals ({ className, motions, prime }: Props): React.ReactElement<Pr
|
||||
members={members}
|
||||
/>
|
||||
</Button.Group>
|
||||
{motions?.length
|
||||
? (
|
||||
<Table>
|
||||
<Table.Head>
|
||||
<th colSpan={2}> </th>
|
||||
<th>{t('threshold')}</th>
|
||||
<th>{t('voting end')}</th>
|
||||
<th className='address'>{t('aye')}</th>
|
||||
<th className='address'>{t('nay')}</th>
|
||||
<th colSpan={2}> </th>
|
||||
</Table.Head>
|
||||
<Table.Body>
|
||||
{motions?.map((motion: DeriveCollectiveProposal): React.ReactNode => (
|
||||
<Motion
|
||||
isMember={isMember}
|
||||
key={motion.hash.toHex()}
|
||||
members={members}
|
||||
motion={motion}
|
||||
prime={prime}
|
||||
/>
|
||||
))}
|
||||
</Table.Body>
|
||||
</Table>
|
||||
)
|
||||
: <div>{t('No council motions')}</div>
|
||||
}
|
||||
<Table>
|
||||
<Table.Head>
|
||||
<th colSpan={2}> </th>
|
||||
<th>{t('threshold')}</th>
|
||||
<th>{t('voting end')}</th>
|
||||
<th className='address'>{t('aye')}</th>
|
||||
<th className='address'>{t('nay')}</th>
|
||||
<th colSpan={2}> </th>
|
||||
</Table.Head>
|
||||
<Table.Body empty={motions && t('No council motions')}>
|
||||
{motions?.map((motion: DeriveCollectiveProposal): React.ReactNode => (
|
||||
<Motion
|
||||
isMember={isMember}
|
||||
key={motion.hash.toHex()}
|
||||
members={members}
|
||||
motion={motion}
|
||||
prime={prime}
|
||||
/>
|
||||
))}
|
||||
</Table.Body>
|
||||
</Table>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -7,7 +7,7 @@ import { AccountId } from '@polkadot/types/interfaces';
|
||||
import { ComponentProps } from './types';
|
||||
|
||||
import React from 'react';
|
||||
import { Spinner, Table } from '@polkadot/react-components';
|
||||
import { Table } from '@polkadot/react-components';
|
||||
|
||||
import { useTranslation } from '../translate';
|
||||
import Candidate from './Candidate';
|
||||
@ -17,59 +17,43 @@ interface Props extends ComponentProps {
|
||||
className?: string;
|
||||
}
|
||||
|
||||
function Candidates ({ allVotes = {}, className, electionsInfo }: Props): React.ReactElement<Props> {
|
||||
function Candidates ({ allVotes = {}, electionsInfo }: Props): React.ReactElement<Props> {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<div className={className}>
|
||||
<h1>{t('runners up')}</h1>
|
||||
{electionsInfo
|
||||
? electionsInfo.runnersUp.length
|
||||
? (
|
||||
<Table>
|
||||
<Table.Head>
|
||||
<th colSpan={2}> </th>
|
||||
<th>{t('backing')}</th>
|
||||
</Table.Head>
|
||||
<Table.Body>
|
||||
{electionsInfo.runnersUp.map(([accountId, balance]): React.ReactNode => (
|
||||
<Candidate
|
||||
address={accountId}
|
||||
balance={balance}
|
||||
key={accountId.toString()}
|
||||
voters={allVotes[accountId.toString()]}
|
||||
/>
|
||||
))}
|
||||
</Table.Body>
|
||||
</Table>
|
||||
)
|
||||
: <div>{t('No runners up found')}</div>
|
||||
: <Spinner />
|
||||
}
|
||||
<h1>{t('candidates')}</h1>
|
||||
{electionsInfo
|
||||
? electionsInfo.candidates.length
|
||||
? (
|
||||
<Table>
|
||||
<Table.Head>
|
||||
<th colSpan={2}> </th>
|
||||
<th>{t('backing')}</th>
|
||||
</Table.Head>
|
||||
<Table.Body>
|
||||
{electionsInfo.candidates.map((accountId): React.ReactNode => (
|
||||
<Candidate
|
||||
address={accountId}
|
||||
key={accountId.toString()}
|
||||
voters={allVotes[accountId.toString()]}
|
||||
/>
|
||||
))}
|
||||
</Table.Body>
|
||||
</Table>
|
||||
)
|
||||
: <div>{t('No candidates found')}</div>
|
||||
: <Spinner />
|
||||
}
|
||||
</div>
|
||||
<>
|
||||
<Table>
|
||||
<Table.Head>
|
||||
<th className='start' colSpan={2}><h1>{t('runners up')}</h1></th>
|
||||
<th>{t('backing')}</th>
|
||||
</Table.Head>
|
||||
<Table.Body empty={electionsInfo && t('No runners up found')}>
|
||||
{electionsInfo?.runnersUp.map(([accountId, balance]): React.ReactNode => (
|
||||
<Candidate
|
||||
address={accountId}
|
||||
balance={balance}
|
||||
key={accountId.toString()}
|
||||
voters={allVotes[accountId.toString()]}
|
||||
/>
|
||||
))}
|
||||
</Table.Body>
|
||||
</Table>
|
||||
<Table>
|
||||
<Table.Head>
|
||||
<th className='start' colSpan={2}><h1>{t('candidates')}</h1></th>
|
||||
<th>{t('backing')}</th>
|
||||
</Table.Head>
|
||||
<Table.Body empty={electionsInfo && t('No candidates found')}>
|
||||
{electionsInfo?.candidates.map((accountId): React.ReactNode => (
|
||||
<Candidate
|
||||
address={accountId}
|
||||
key={accountId.toString()}
|
||||
voters={allVotes[accountId.toString()]}
|
||||
/>
|
||||
))}
|
||||
</Table.Body>
|
||||
</Table>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -6,7 +6,7 @@ import { AccountId } from '@polkadot/types/interfaces';
|
||||
import { ComponentProps } from './types';
|
||||
|
||||
import React from 'react';
|
||||
import { Spinner, Table } from '@polkadot/react-components';
|
||||
import { Table } from '@polkadot/react-components';
|
||||
|
||||
import { useTranslation } from '../translate';
|
||||
import Candidate from './Candidate';
|
||||
@ -21,33 +21,23 @@ function Members ({ allVotes = {}, className, electionsInfo, prime }: Props): Re
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<div className={className}>
|
||||
<h1>{t('members')}</h1>
|
||||
{electionsInfo
|
||||
? electionsInfo.members.length
|
||||
? (
|
||||
<Table>
|
||||
<Table.Head>
|
||||
<th colSpan={2}> </th>
|
||||
<th>{t('backing')}</th>
|
||||
</Table.Head>
|
||||
<Table.Body>
|
||||
{electionsInfo.members.map(([accountId, balance]): React.ReactNode => (
|
||||
<Candidate
|
||||
address={accountId}
|
||||
balance={balance}
|
||||
isPrime={prime?.eq(accountId)}
|
||||
key={accountId.toString()}
|
||||
voters={allVotes[accountId.toString()]}
|
||||
/>
|
||||
))}
|
||||
</Table.Body>
|
||||
</Table>
|
||||
)
|
||||
: <div>{t('No members found')}</div>
|
||||
: <Spinner />
|
||||
}
|
||||
</div>
|
||||
<Table className={className}>
|
||||
<Table.Head>
|
||||
<th className='start' colSpan={2}><h1>{t('members')}</h1></th>
|
||||
<th>{t('backing')}</th>
|
||||
</Table.Head>
|
||||
<Table.Body empty={electionsInfo && t('No members found')}>
|
||||
{electionsInfo?.members.map(([accountId, balance]): React.ReactNode => (
|
||||
<Candidate
|
||||
address={accountId}
|
||||
balance={balance}
|
||||
isPrime={prime?.eq(accountId)}
|
||||
key={accountId.toString()}
|
||||
voters={allVotes[accountId.toString()]}
|
||||
/>
|
||||
))}
|
||||
</Table.Body>
|
||||
</Table>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
import { DeriveDispatch } from '@polkadot/api-derive/types';
|
||||
|
||||
import React from 'react';
|
||||
import { Spinner, Table } from '@polkadot/react-components';
|
||||
import { Table } from '@polkadot/react-components';
|
||||
import { useApi, useCall } from '@polkadot/react-hooks';
|
||||
|
||||
import { useTranslation } from '../translate';
|
||||
@ -21,31 +21,21 @@ function DispatchQueue ({ className }: Props): React.ReactElement<Props> | null
|
||||
const queued = useCall<DeriveDispatch[]>(api.derive.democracy.dispatchQueue as any, []);
|
||||
|
||||
return (
|
||||
<div className={`proposalSection ${className}`}>
|
||||
<h1>{t('dispatch queue')}</h1>
|
||||
{queued
|
||||
? queued.length
|
||||
? (
|
||||
<Table>
|
||||
<Table.Head>
|
||||
<th colSpan={2}> </th>
|
||||
<th>{t('enact')}</th>
|
||||
<th colSpan={2}> </th>
|
||||
</Table.Head>
|
||||
<Table.Body>
|
||||
{queued.map((entry): React.ReactNode => (
|
||||
<DispatchEntry
|
||||
key={entry.index.toString()}
|
||||
value={entry}
|
||||
/>
|
||||
))}
|
||||
</Table.Body>
|
||||
</Table>
|
||||
)
|
||||
: <div>{t('Nothing queued for execution')}</div>
|
||||
: <Spinner />
|
||||
}
|
||||
</div>
|
||||
<Table className={className}>
|
||||
<Table.Head>
|
||||
<th className='start' colSpan={2}><h1>{t('dispatch queue')}</h1></th>
|
||||
<th>{t('enact')}</th>
|
||||
<th colSpan={2}> </th>
|
||||
</Table.Head>
|
||||
<Table.Body empty={queued && t('Nothing queued for execution')}>
|
||||
{queued?.map((entry): React.ReactNode => (
|
||||
<DispatchEntry
|
||||
key={entry.index.toString()}
|
||||
value={entry}
|
||||
/>
|
||||
))}
|
||||
</Table.Body>
|
||||
</Table>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
import { DeriveProposalExternal } from '@polkadot/api-derive/types';
|
||||
|
||||
import React from 'react';
|
||||
import { Spinner, Table } from '@polkadot/react-components';
|
||||
import { Table } from '@polkadot/react-components';
|
||||
import { useApi, useCall } from '@polkadot/react-hooks';
|
||||
|
||||
import { useTranslation } from '../translate';
|
||||
@ -22,25 +22,17 @@ function Externals ({ className }: Props): React.ReactElement<Props> | null {
|
||||
|
||||
return (
|
||||
<div className={`proposalSection ${className}`}>
|
||||
<h1>{t('external')}</h1>
|
||||
{external
|
||||
? (
|
||||
<Table>
|
||||
<Table.Head>
|
||||
<th> </th>
|
||||
<th className='address'>{t('proposer')}</th>
|
||||
<th>{t('locked')}</th>
|
||||
<th> </th>
|
||||
</Table.Head>
|
||||
<Table.Body>
|
||||
<External value={external} />
|
||||
</Table.Body>
|
||||
</Table>
|
||||
)
|
||||
: external === null
|
||||
? <div>{t('No external proposal')}</div>
|
||||
: <Spinner />
|
||||
}
|
||||
<Table>
|
||||
<Table.Head>
|
||||
<th className='start'><h1>{t('external')}</h1></th>
|
||||
<th className='address'>{t('proposer')}</th>
|
||||
<th>{t('locked')}</th>
|
||||
<th> </th>
|
||||
</Table.Head>
|
||||
<Table.Body empty={external === null && t('No external proposal')}>
|
||||
{external && <External value={external} />}
|
||||
</Table.Body>
|
||||
</Table>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
import { DeriveProposal } from '@polkadot/api-derive/types';
|
||||
|
||||
import React from 'react';
|
||||
import { Spinner, Table } from '@polkadot/react-components';
|
||||
import { Table } from '@polkadot/react-components';
|
||||
import { useApi, useCall } from '@polkadot/react-hooks';
|
||||
|
||||
import ProposalDisplay from './Proposal';
|
||||
@ -21,32 +21,22 @@ function Proposals ({ className }: Props): React.ReactElement<Props> {
|
||||
const proposals = useCall<DeriveProposal[]>(api.derive.democracy.proposals, []);
|
||||
|
||||
return (
|
||||
<div className={`proposalSection ${className}`}>
|
||||
<h1>{t('proposals')}</h1>
|
||||
{proposals
|
||||
? proposals.length
|
||||
? (
|
||||
<Table>
|
||||
<Table.Head>
|
||||
<th colSpan={2}> </th>
|
||||
<th className='address'>{t('proposer')}</th>
|
||||
<th>{t('locked')}</th>
|
||||
<th colSpan={3}> </th>
|
||||
</Table.Head>
|
||||
<Table.Body>
|
||||
{proposals.map((proposal): React.ReactNode => (
|
||||
<ProposalDisplay
|
||||
key={proposal.index.toString()}
|
||||
value={proposal}
|
||||
/>
|
||||
))}
|
||||
</Table.Body>
|
||||
</Table>
|
||||
)
|
||||
: <div>{t('No active proposals')}</div>
|
||||
: <Spinner />
|
||||
}
|
||||
</div>
|
||||
<Table className={className}>
|
||||
<Table.Head>
|
||||
<th className='start' colSpan={2}><h1>{t('proposals')}</h1></th>
|
||||
<th className='address'>{t('proposer')}</th>
|
||||
<th>{t('locked')}</th>
|
||||
<th colSpan={3}> </th>
|
||||
</Table.Head>
|
||||
<Table.Body empty={proposals && t('No active proposals')}>
|
||||
{proposals?.map((proposal): React.ReactNode => (
|
||||
<ProposalDisplay
|
||||
key={proposal.index.toString()}
|
||||
value={proposal}
|
||||
/>
|
||||
))}
|
||||
</Table.Body>
|
||||
</Table>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
import { DeriveReferendumExt } from '@polkadot/api-derive/types';
|
||||
|
||||
import React from 'react';
|
||||
import { Spinner, Table } from '@polkadot/react-components';
|
||||
import { Table } from '@polkadot/react-components';
|
||||
import { useApi, useCall } from '@polkadot/react-hooks';
|
||||
|
||||
import Referendum from './Referendum';
|
||||
@ -21,34 +21,24 @@ function Referendums ({ className }: Props): React.ReactElement<Props> {
|
||||
const referendums = useCall<DeriveReferendumExt[]>(api.derive.democracy.referendums, []);
|
||||
|
||||
return (
|
||||
<div className={`proposalSection ${className}`}>
|
||||
<h1>{t('referenda')}</h1>
|
||||
{referendums
|
||||
? referendums.length
|
||||
? (
|
||||
<Table>
|
||||
<Table.Head>
|
||||
<th colSpan={2}> </th>
|
||||
<th>{t('remaining')}</th>
|
||||
<th>{t('activate')}</th>
|
||||
<th>{t('aye')}</th>
|
||||
<th>{t('nay')}</th>
|
||||
<th colSpan={3}> </th>
|
||||
</Table.Head>
|
||||
<Table.Body>
|
||||
{referendums.map((referendum): React.ReactNode => (
|
||||
<Referendum
|
||||
key={referendum.index.toString()}
|
||||
value={referendum}
|
||||
/>
|
||||
))}
|
||||
</Table.Body>
|
||||
</Table>
|
||||
)
|
||||
: <div>{t('No active referendums')}</div>
|
||||
: <Spinner />
|
||||
}
|
||||
</div>
|
||||
<Table className={className}>
|
||||
<Table.Head>
|
||||
<th className='start' colSpan={2}><h1>{t('referenda')}</h1></th>
|
||||
<th>{t('remaining')}</th>
|
||||
<th>{t('activate')}</th>
|
||||
<th>{t('aye')}</th>
|
||||
<th>{t('nay')}</th>
|
||||
<th colSpan={3}> </th>
|
||||
</Table.Head>
|
||||
<Table.Body empty={referendums && t('No active referendums')}>
|
||||
{referendums?.map((referendum): React.ReactNode => (
|
||||
<Referendum
|
||||
key={referendum.index.toString()}
|
||||
value={referendum}
|
||||
/>
|
||||
))}
|
||||
</Table.Body>
|
||||
</Table>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -6,44 +6,15 @@ import { BareProps } from '@polkadot/react-components/types';
|
||||
|
||||
import React from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import styled from 'styled-components';
|
||||
import { HeaderExtended } from '@polkadot/api-derive';
|
||||
import { AddressMini, LinkExternal } from '@polkadot/react-components';
|
||||
import { AddressMini } from '@polkadot/react-components';
|
||||
import { formatNumber } from '@polkadot/util';
|
||||
|
||||
interface Props extends BareProps {
|
||||
isSummary?: boolean;
|
||||
value?: HeaderExtended;
|
||||
withExplorer?: boolean;
|
||||
withLink?: boolean;
|
||||
value: HeaderExtended;
|
||||
}
|
||||
|
||||
const renderDetails = ({ number: blockNumber, extrinsicsRoot, parentHash, stateRoot }: HeaderExtended): React.ReactNode => {
|
||||
const parentHex = parentHash.toHex();
|
||||
|
||||
return (
|
||||
<div className='contains'>
|
||||
<div className='info'>
|
||||
<label>parentHash</label>
|
||||
<span className='hash'>{
|
||||
blockNumber.unwrap().gtn(1)
|
||||
? <Link to={`/explorer/query/${parentHex}`}>{parentHex}</Link>
|
||||
: parentHex
|
||||
}</span>
|
||||
</div>
|
||||
<div className='info'>
|
||||
<label>extrinsicsRoot</label>
|
||||
<span className='hash'>{extrinsicsRoot.toHex()}</span>
|
||||
</div>
|
||||
<div className='info'>
|
||||
<label>stateRoot</label>
|
||||
<span className='hash'>{stateRoot.toHex()}</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
function BlockHeader ({ className, isSummary, value, withExplorer, withLink }: Props): React.ReactElement<Props> | null {
|
||||
function BlockHeader ({ value }: Props): React.ReactElement<Props> | null {
|
||||
if (!value) {
|
||||
return null;
|
||||
}
|
||||
@ -52,114 +23,16 @@ function BlockHeader ({ className, isSummary, value, withExplorer, withLink }: P
|
||||
const textNumber = formatNumber(value.number);
|
||||
|
||||
return (
|
||||
<article className={`explorer--BlockHeader ${className}`}>
|
||||
<div className='header-outer'>
|
||||
<div className='header'>
|
||||
<div className='number'>{
|
||||
withLink
|
||||
? <Link to={`/explorer/query/${hashHex}`}>{textNumber}</Link>
|
||||
: textNumber
|
||||
} </div>
|
||||
<div className='hash'>{hashHex}</div>
|
||||
<div className='author ui--media-small'>{
|
||||
value.author
|
||||
? <AddressMini value={value.author} />
|
||||
: undefined
|
||||
}</div>
|
||||
</div>
|
||||
</div>
|
||||
{
|
||||
isSummary
|
||||
? undefined
|
||||
: renderDetails(value)
|
||||
}
|
||||
{
|
||||
withExplorer
|
||||
? <LinkExternal data={hashHex} type='block' />
|
||||
: undefined
|
||||
}
|
||||
</article>
|
||||
<tr>
|
||||
<td className='number'>
|
||||
<h2><Link to={`/explorer/query/${hashHex}`}>{textNumber}</Link></h2>
|
||||
</td>
|
||||
<td className='all hash overflow'>{hashHex}</td>
|
||||
<td className='address'>
|
||||
{value.author && <AddressMini value={value.author} />}
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
}
|
||||
|
||||
export default React.memo(styled(BlockHeader)`
|
||||
.author {
|
||||
font-size: 1rem;
|
||||
text-align: right;
|
||||
vertical-align: middle;
|
||||
|
||||
> .ui--AddressMini.padded {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.header {
|
||||
color: rgba(0, 0, 0, 0.6);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
text-align: left;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
vertical-align: middle;
|
||||
|
||||
> div {
|
||||
display: inline-block;
|
||||
font-weight: 100;
|
||||
vertical-align: middle;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
> .number {
|
||||
font-size: 2.25rem;
|
||||
}
|
||||
|
||||
.hash {
|
||||
font-size: 1.5rem;
|
||||
font-family: sans-serif;
|
||||
}
|
||||
}
|
||||
|
||||
.hash {
|
||||
font-family: monospace;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.number {
|
||||
align-items: center;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.contains {
|
||||
border: 0;
|
||||
margin-top: 0.5rem;
|
||||
text-align: center;
|
||||
|
||||
> .info {
|
||||
margin-bottom: 0.125em;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
|
||||
.ui--Labelled {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
> label {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
> span,
|
||||
> label {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
label {
|
||||
margin-right: 0.5rem;
|
||||
min-width: 10rem;
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
}
|
||||
`);
|
||||
export default React.memo(BlockHeader);
|
||||
|
||||
@ -4,25 +4,34 @@
|
||||
|
||||
import React from 'react';
|
||||
import { HeaderExtended } from '@polkadot/api-derive';
|
||||
import { Table } from '@polkadot/react-components';
|
||||
|
||||
import BlockHeader from './BlockHeader';
|
||||
import { useTranslation } from './translate';
|
||||
|
||||
interface Props {
|
||||
headers: HeaderExtended[];
|
||||
}
|
||||
|
||||
function BlockHeaders ({ headers }: Props): React.ReactElement<Props> {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<>
|
||||
{headers.map((header, index): React.ReactNode => (
|
||||
<BlockHeader
|
||||
isSummary={!!index}
|
||||
key={header.number.toString()}
|
||||
value={header}
|
||||
withLink={!header.number.isEmpty}
|
||||
/>
|
||||
))}
|
||||
</>
|
||||
<Table>
|
||||
<Table.Head>
|
||||
<th className='start' colSpan={3}><h1>{t('recent blocks')}</h1></th>
|
||||
</Table.Head>
|
||||
<Table.Body empty={t('No blocks available')}>
|
||||
{headers
|
||||
.filter((header) => !!header)
|
||||
.map((header): React.ReactNode => (
|
||||
<BlockHeader
|
||||
key={header.number.toString()}
|
||||
value={header}
|
||||
/>
|
||||
))}
|
||||
</Table.Body>
|
||||
</Table>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
/* eslint-disable @typescript-eslint/camelcase */
|
||||
// Copyright 2017-2020 @polkadot/app-explorer authors & contributors
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
@ -6,12 +5,13 @@
|
||||
import { EventRecord, SignedBlock } from '@polkadot/types/interfaces';
|
||||
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { HeaderExtended } from '@polkadot/api-derive';
|
||||
import { Columar, Spinner } from '@polkadot/react-components';
|
||||
import { AddressMini, Columar, LinkExternal, Table } from '@polkadot/react-components';
|
||||
import { useApi, useCall } from '@polkadot/react-hooks';
|
||||
import { formatNumber } from '@polkadot/util';
|
||||
|
||||
import BlockHeader from '../BlockHeader';
|
||||
import { useTranslation } from '../translate';
|
||||
import Events from './Events';
|
||||
import Extrinsics from './Extrinsics';
|
||||
import Logs from './Logs';
|
||||
@ -22,41 +22,52 @@ interface Props {
|
||||
}
|
||||
|
||||
function BlockByHash ({ className, value }: Props): React.ReactElement<Props> {
|
||||
const { t } = useTranslation();
|
||||
const { api } = useApi();
|
||||
const events = useCall<EventRecord[]>(api.query.system.events.at as any, [value], { isSingle: true });
|
||||
const getBlock = useCall<SignedBlock>(api.rpc.chain.getBlock as any, [value], { isSingle: true });
|
||||
const getHeader = useCall<HeaderExtended>(api.derive.chain.getHeader as any, [value]);
|
||||
|
||||
if (!getBlock || getBlock.isEmpty || !getHeader || getHeader.isEmpty) {
|
||||
return <Spinner />;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={className}>
|
||||
<header>
|
||||
<BlockHeader
|
||||
className='exporer--BlockByHash-BlockHeader'
|
||||
value={getHeader}
|
||||
withExplorer
|
||||
/>
|
||||
</header>
|
||||
<Columar>
|
||||
<Extrinsics
|
||||
blockNumber={getHeader.number.unwrap()}
|
||||
value={getBlock.block.extrinsics}
|
||||
/>
|
||||
<Events value={events} />
|
||||
<Logs value={getHeader.digest.logs} />
|
||||
</Columar>
|
||||
<Table isFixed>
|
||||
<Table.Head>
|
||||
<th className='start'><h1>{getHeader ? formatNumber(getHeader.number.unwrap()) : '-'}</h1></th>
|
||||
{getHeader && (
|
||||
<>
|
||||
<th className='start'>{t('hash')}</th>
|
||||
<th className='start'>{t('parent')}</th>
|
||||
<th className='start'>{t('extrinsics')}</th>
|
||||
<th className='start'>{t('state')}</th>
|
||||
<th> </th>
|
||||
</>
|
||||
)}
|
||||
</Table.Head>
|
||||
<Table.Body>
|
||||
{getBlock && !getBlock.isEmpty && getHeader && !getHeader.isEmpty && (
|
||||
<tr>
|
||||
<td className='address'>{getHeader.author && <AddressMini value={getHeader.author} />}</td>
|
||||
<td className='hash overflow'>{getHeader.hash.toHex()}</td>
|
||||
<td className='hash overflow'><Link to={`/explorer/query/${getHeader.parentHash.toHex()}`}>{getHeader.parentHash.toHex()}</Link></td>
|
||||
<td className='hash overflow'>{getHeader.extrinsicsRoot.toHex()}</td>
|
||||
<td className='hash overflow'>{getHeader.stateRoot.toHex()}</td>
|
||||
<td><LinkExternal data={value} type='block' /></td>
|
||||
</tr>
|
||||
)}
|
||||
</Table.Body>
|
||||
</Table>
|
||||
{getBlock && getHeader && (
|
||||
<Columar>
|
||||
<Extrinsics
|
||||
blockNumber={getHeader.number.unwrap()}
|
||||
value={getBlock.block.extrinsics}
|
||||
/>
|
||||
<Events value={events} />
|
||||
<Logs value={getHeader.digest.logs} />
|
||||
</Columar>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default React.memo(
|
||||
styled(BlockByHash)`
|
||||
.exporer--BlockByHash-BlockHeader {
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
`
|
||||
);
|
||||
export default React.memo(BlockByHash);
|
||||
|
||||
@ -22,7 +22,7 @@ function Events ({ value }: Props): React.ReactElement<Props> | null {
|
||||
}
|
||||
|
||||
return (
|
||||
<Column headerText={t('events')}>
|
||||
<Column>
|
||||
<EventsDisplay
|
||||
eventClassName='explorer--BlockByHash-block'
|
||||
events={
|
||||
@ -30,6 +30,7 @@ function Events ({ value }: Props): React.ReactElement<Props> | null {
|
||||
key: `${index}`, record
|
||||
}))
|
||||
}
|
||||
label={t('events')}
|
||||
/>
|
||||
</Column>
|
||||
);
|
||||
|
||||
@ -7,7 +7,7 @@ import { BlockNumber, Extrinsic } from '@polkadot/types/interfaces';
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { registry } from '@polkadot/react-api';
|
||||
import { AddressMini, Call, Column, Expander, LinkExternal } from '@polkadot/react-components';
|
||||
import { AddressMini, Call, Column, Expander, LinkExternal, Table } from '@polkadot/react-components';
|
||||
import { formatNumber } from '@polkadot/util';
|
||||
|
||||
import { useTranslation } from '../translate';
|
||||
@ -19,85 +19,75 @@ interface Props {
|
||||
value?: Extrinsic[] | null;
|
||||
}
|
||||
|
||||
function renderExtrinsic (props: Props, extrinsic: Extrinsic, index: number, t: (s: string, opt?: any) => string): React.ReactNode {
|
||||
const { blockNumber } = props;
|
||||
const { meta, method, section } = registry.findMetaCall(extrinsic.callIndex);
|
||||
const isMortal = extrinsic.era.isMortalEra;
|
||||
let eraEnd;
|
||||
let eraStart;
|
||||
function getEra ({ era }: Extrinsic, blockNumber?: BlockNumber): [number, number] | null {
|
||||
if (blockNumber && era.isMortalEra) {
|
||||
const mortalEra = era.asMortalEra;
|
||||
|
||||
if (blockNumber && isMortal) {
|
||||
const mortalEra = extrinsic.era.asMortalEra;
|
||||
|
||||
eraEnd = mortalEra.death(blockNumber.toNumber());
|
||||
eraStart = mortalEra.birth(blockNumber.toNumber());
|
||||
return [mortalEra.birth(blockNumber.toNumber()), mortalEra.death(blockNumber.toNumber())];
|
||||
}
|
||||
|
||||
return (
|
||||
<article key={`extrinsic:${index}`}>
|
||||
<div className='header'>
|
||||
<h3>
|
||||
{section}.{method} (#{formatNumber(index)})
|
||||
</h3>
|
||||
{extrinsic.isSigned && (
|
||||
<div className='explorer--BlockByHash-header'>
|
||||
<div>
|
||||
<AddressMini value={extrinsic.signer} />
|
||||
</div>
|
||||
<div className='explorer--BlockByHash-nonce'>
|
||||
{t('index')} {formatNumber(extrinsic.nonce)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<Expander summaryMeta={meta}>
|
||||
<Call
|
||||
className='details'
|
||||
mortality={
|
||||
isMortal
|
||||
? blockNumber
|
||||
? t('mortal, valid from #{{startAt}} to #{{endsAt}}', {
|
||||
replace: {
|
||||
endsAt: formatNumber(eraEnd),
|
||||
startAt: formatNumber(eraStart)
|
||||
}
|
||||
})
|
||||
: t('mortal')
|
||||
: t('immortal')
|
||||
}
|
||||
tip={extrinsic.tip?.toBn()}
|
||||
value={extrinsic}
|
||||
withHash
|
||||
/>
|
||||
</Expander>
|
||||
{
|
||||
extrinsic.isSigned
|
||||
? <LinkExternal data={extrinsic.hash.toHex()} type='extrinsic' />
|
||||
: null
|
||||
}
|
||||
</article>
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
function Extrinsics (props: Props): React.ReactElement<Props> {
|
||||
function Extrinsics ({ className, blockNumber, label, value }: Props): React.ReactElement<Props> {
|
||||
const { t } = useTranslation();
|
||||
const { className, label, value } = props;
|
||||
|
||||
return (
|
||||
<Column
|
||||
className={className}
|
||||
emptyText={t('No pending extrinsics are in the queue')}
|
||||
headerText={label || t('extrinsics')}
|
||||
>
|
||||
{value?.map((extrinsic, index): React.ReactNode => {
|
||||
try {
|
||||
return renderExtrinsic(props, extrinsic, index, t);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
<Column className={className}>
|
||||
<Table>
|
||||
<Table.Head>
|
||||
<th className='start'><h1>{label || t('extrinsics')}</h1></th>
|
||||
</Table.Head>
|
||||
<Table.Body empty={t('No pending extrinsics are in the queue')}>
|
||||
{value?.map((extrinsic, index): React.ReactNode => {
|
||||
const { meta, method, section } = registry.findMetaCall(extrinsic.callIndex);
|
||||
const era = getEra(extrinsic, blockNumber);
|
||||
|
||||
return t('Unable to render extrinsic');
|
||||
}
|
||||
})}
|
||||
return (
|
||||
<tr key={`extrinsic:${index}`}>
|
||||
<td className='relative overflow'>
|
||||
<div>{section}.{method} (#{formatNumber(index)})</div>
|
||||
{extrinsic.isSigned && (
|
||||
<div className='explorer--BlockByHash-header'>
|
||||
<div>
|
||||
<AddressMini value={extrinsic.signer} />
|
||||
</div>
|
||||
<div className='explorer--BlockByHash-nonce'>
|
||||
{t('index')} {formatNumber(extrinsic.nonce)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<Expander summaryMeta={meta}>
|
||||
<Call
|
||||
className='details'
|
||||
mortality={
|
||||
era
|
||||
? blockNumber
|
||||
? t('mortal, valid from #{{startAt}} to #{{endsAt}}', {
|
||||
replace: {
|
||||
endsAt: formatNumber(era[1]),
|
||||
startAt: formatNumber(era[0])
|
||||
}
|
||||
})
|
||||
: t('mortal')
|
||||
: t('immortal')
|
||||
}
|
||||
tip={extrinsic.tip?.toBn()}
|
||||
value={extrinsic}
|
||||
withHash
|
||||
/>
|
||||
</Expander>
|
||||
{
|
||||
extrinsic.isSigned
|
||||
? <LinkExternal data={extrinsic.hash.toHex()} type='extrinsic' />
|
||||
: null
|
||||
}
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
})}
|
||||
</Table.Body>
|
||||
</Table>
|
||||
</Column>
|
||||
);
|
||||
}
|
||||
@ -105,7 +95,7 @@ function Extrinsics (props: Props): React.ReactElement<Props> {
|
||||
export default React.memo(styled(Extrinsics)`
|
||||
.explorer--BlockByHash-header {
|
||||
position: absolute;
|
||||
top: 0.25rem;
|
||||
top: 0.75rem;
|
||||
right: 0.75rem;
|
||||
}
|
||||
|
||||
|
||||
@ -7,7 +7,7 @@ import { Codec, TypeDef } from '@polkadot/types/types';
|
||||
|
||||
import React from 'react';
|
||||
import { Struct, Tuple, Raw, Vec, getTypeDef } from '@polkadot/types';
|
||||
import { Column, Expander } from '@polkadot/react-components';
|
||||
import { Column, Expander, Table } from '@polkadot/react-components';
|
||||
import Params from '@polkadot/react-params';
|
||||
|
||||
import { useTranslation } from '../translate';
|
||||
@ -85,38 +85,18 @@ function formatVector (vector: Vec<any>): React.ReactNode {
|
||||
);
|
||||
}
|
||||
|
||||
function renderItem (t: (s: string, opt?: any) => string): (item: DigestItem, index: number) => React.ReactNode {
|
||||
return function LogItem (item: DigestItem, index: number): React.ReactNode {
|
||||
let content: React.ReactNode;
|
||||
function formatItem (item: DigestItem): React.ReactNode {
|
||||
if (item.value instanceof Struct) {
|
||||
return formatStruct(item.value);
|
||||
} else if (item.value instanceof Tuple) {
|
||||
return formatTuple(item.value);
|
||||
} else if (item.value instanceof Vec) {
|
||||
return formatVector(item.value);
|
||||
} else if (item.value instanceof Raw) {
|
||||
return formatU8a(item.value);
|
||||
}
|
||||
|
||||
if (item.value instanceof Struct) {
|
||||
content = formatStruct(item.value);
|
||||
} else if (item.value instanceof Tuple) {
|
||||
content = formatTuple(item.value);
|
||||
} else if (item.value instanceof Vec) {
|
||||
content = formatVector(item.value);
|
||||
} else if (item.value instanceof Raw) {
|
||||
content = formatU8a(item.value);
|
||||
} else {
|
||||
content = <div>{item.value.toString().split(',').join(', ')}</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className='explorer--BlockByHash-block'
|
||||
key={index}
|
||||
>
|
||||
<article className='explorer--Container'>
|
||||
<div className='header'>
|
||||
<h3>{item.type.toString()}</h3>
|
||||
</div>
|
||||
<Expander summary={t('Details')}>
|
||||
{content}
|
||||
</Expander>
|
||||
</article>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
return <div>{item.value.toString().split(',').join(', ')}</div>;
|
||||
}
|
||||
|
||||
function Logs (props: Props): React.ReactElement<Props> | null {
|
||||
@ -128,8 +108,24 @@ function Logs (props: Props): React.ReactElement<Props> | null {
|
||||
}
|
||||
|
||||
return (
|
||||
<Column headerText={t('logs')}>
|
||||
{value.map(renderItem(t))}
|
||||
<Column>
|
||||
<Table>
|
||||
<Table.Head>
|
||||
<th className='start'><h1>{t('logs')}</h1></th>
|
||||
</Table.Head>
|
||||
<Table.Body>
|
||||
{value.map((log, index) => (
|
||||
<tr key={`log:${index}`}>
|
||||
<td className='overflow'>
|
||||
<div>{log.type.toString()}</div>
|
||||
<Expander summary={t('Details')}>
|
||||
{formatItem(log)}
|
||||
</Expander>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</Table.Body>
|
||||
</Table>
|
||||
</Column>
|
||||
);
|
||||
}
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
import { KeyedEvent } from './types';
|
||||
|
||||
import React from 'react';
|
||||
import { Event as EventDisplay, Expander } from '@polkadot/react-components';
|
||||
import { Event as EventDisplay, Expander, Table } from '@polkadot/react-components';
|
||||
import { formatNumber } from '@polkadot/util';
|
||||
|
||||
import { useTranslation } from './translate';
|
||||
@ -14,55 +14,44 @@ interface Props {
|
||||
emptyLabel?: React.ReactNode;
|
||||
events: KeyedEvent[];
|
||||
eventClassName?: string;
|
||||
label?: React.ReactNode;
|
||||
withoutIndex?: boolean;
|
||||
}
|
||||
|
||||
function Events ({ emptyLabel, eventClassName, events, withoutIndex }: Props): React.ReactElement<Props> {
|
||||
function Events ({ emptyLabel, eventClassName, events, label, withoutIndex }: Props): React.ReactElement<Props> {
|
||||
const { t } = useTranslation();
|
||||
|
||||
if (!events || events.length === 0) {
|
||||
return (
|
||||
<article>
|
||||
{emptyLabel || t('no events available')}
|
||||
</article>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{events.map(({ key, record: { event, phase } }: KeyedEvent): React.ReactNode => {
|
||||
const extIndex = !withoutIndex && phase.isApplyExtrinsic
|
||||
? phase.asApplyExtrinsic
|
||||
: -1;
|
||||
|
||||
if (!event.method || !event.section) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<article
|
||||
className={`explorer--Container ${eventClassName}`}
|
||||
key={key}
|
||||
>
|
||||
<div className='header'>
|
||||
<h3>
|
||||
{event.section}.{event.method} {
|
||||
extIndex !== -1
|
||||
? `(#${formatNumber(extIndex)})`
|
||||
<Table>
|
||||
<Table.Head>
|
||||
<th className='start'><h1>{label || t('recent events')}</h1></th>
|
||||
</Table.Head>
|
||||
<Table.Body empty={emptyLabel || t('No events available')}>
|
||||
{events
|
||||
.filter(({ record: { event: { method, section } } }): boolean => !!method && !!section)
|
||||
.map(({ key, record: { event, phase } }: KeyedEvent): React.ReactNode => (
|
||||
<tr
|
||||
className={eventClassName}
|
||||
key={key}
|
||||
>
|
||||
<td className='overflow'>
|
||||
<div>{event.section}.{event.method} {
|
||||
!withoutIndex && phase.isApplyExtrinsic
|
||||
? `(#${formatNumber(phase.asApplyExtrinsic)})`
|
||||
: ''
|
||||
}
|
||||
</h3>
|
||||
</div>
|
||||
<Expander summaryMeta={event.meta}>
|
||||
<EventDisplay
|
||||
className='details'
|
||||
value={event}
|
||||
/>
|
||||
</Expander>
|
||||
</article>
|
||||
);
|
||||
})}
|
||||
</>
|
||||
}</div>
|
||||
<Expander summaryMeta={event.meta}>
|
||||
<EventDisplay
|
||||
className='details'
|
||||
value={event}
|
||||
/>
|
||||
</Expander>
|
||||
</td>
|
||||
</tr>
|
||||
))
|
||||
}
|
||||
</Table.Body>
|
||||
</Table>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -12,7 +12,6 @@ import BlockHeaders from './BlockHeaders';
|
||||
import Events from './Events';
|
||||
import Query from './Query';
|
||||
import Summary from './Summary';
|
||||
import { useTranslation } from './translate';
|
||||
|
||||
interface Props {
|
||||
events: KeyedEvent[];
|
||||
@ -20,17 +19,15 @@ interface Props {
|
||||
}
|
||||
|
||||
function Main ({ events, headers }: Props): React.ReactElement<Props> {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<>
|
||||
<Query />
|
||||
<Summary />
|
||||
<Columar>
|
||||
<Column headerText={t('recent blocks')}>
|
||||
<Column>
|
||||
<BlockHeaders headers={headers} />
|
||||
</Column>
|
||||
<Column headerText={t('recent events')}>
|
||||
<Column>
|
||||
<Events events={events} />
|
||||
</Column>
|
||||
</Columar>
|
||||
|
||||
@ -5,107 +5,38 @@
|
||||
import { PeerInfo } from '@polkadot/types/interfaces';
|
||||
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { formatNumber } from '@polkadot/util';
|
||||
|
||||
import { useTranslation } from '../translate';
|
||||
import { Table } from '@polkadot/react-components';
|
||||
|
||||
interface Props {
|
||||
className?: string;
|
||||
peers?: PeerInfo[] | null;
|
||||
}
|
||||
|
||||
const renderPeer = (peer: PeerInfo): React.ReactNode => {
|
||||
const peerId = peer.peerId.toString();
|
||||
|
||||
return (
|
||||
<tr key={peerId}>
|
||||
<td className='roles'>{peer.roles.toString().toLowerCase()}</td>
|
||||
<td className='peerid ui--media-medium'>{peerId}</td>
|
||||
<td className='number'>{formatNumber(peer.bestNumber)}</td>
|
||||
<td className='hash'>{peer.bestHash.toHex()}</td>
|
||||
</tr>
|
||||
);
|
||||
};
|
||||
|
||||
function Peers ({ className, peers }: Props): React.ReactElement<Props> {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<section className={`status--Peers ${className}`}>
|
||||
<h1>{t('connected peers')}</h1>
|
||||
{peers && peers.length
|
||||
? (
|
||||
<article>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th className='roles'>{t('role')}</th>
|
||||
<th className='peerid ui--media-medium'>{t('peer id')}</th>
|
||||
<th>{t('best #')}</th>
|
||||
<th className='hash'>{t('best hash')}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{peers
|
||||
.sort((a, b): number => b.bestNumber.cmp(a.bestNumber))
|
||||
.map(renderPeer)
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</article>
|
||||
)
|
||||
: (
|
||||
<div className='ui disabled'>
|
||||
{t('no peers connected')}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</section>
|
||||
<Table className={className}>
|
||||
<Table.Head>
|
||||
<th className='start' colSpan={2}><h1>{t('connected peers')}</h1></th>
|
||||
<th className='number'>{t('best #')}</th>
|
||||
<th className='start'>{t('best hash')}</th>
|
||||
</Table.Head>
|
||||
<Table.Body empty={t('no peers connected')}>
|
||||
{peers?.sort((a, b): number => b.bestNumber.cmp(a.bestNumber)).map((peer) => (
|
||||
<tr key={peer.peerId.toString()}>
|
||||
<td>{peer.roles.toString().toLowerCase()}</td>
|
||||
<td className='hash'>{peer.peerId.toString()}</td>
|
||||
<td className='number'>{formatNumber(peer.bestNumber)}</td>
|
||||
<td className='hash'>{peer.bestHash.toHex()}</td>
|
||||
</tr>
|
||||
))}
|
||||
</Table.Body>
|
||||
</Table>
|
||||
);
|
||||
}
|
||||
|
||||
export default React.memo(
|
||||
styled(Peers)`
|
||||
table {
|
||||
width: 100%;
|
||||
|
||||
td, th {
|
||||
padding: 0.25rem 0.5rem;
|
||||
text-align: left;
|
||||
white-space: nowrap;
|
||||
|
||||
&.hash {
|
||||
font-family: monospace;
|
||||
max-width: 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
&.number {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
&.peerid {
|
||||
font-style: italic;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
&.roles {
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tbody {
|
||||
tr {
|
||||
width: 100%;
|
||||
|
||||
&:nth-child(odd) {
|
||||
background-color: #f2f2f2;
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
);
|
||||
export default React.memo(Peers);
|
||||
|
||||
@ -67,12 +67,12 @@ function ExplorerApp ({ basePath, className }: Props): React.ReactElement<Props>
|
||||
<Route path={`${basePath}/query/:value`} component={BlockInfo} />
|
||||
<Route path={`${basePath}/query`} component={BlockInfo} />
|
||||
<Route path={`${basePath}/node`} component={NodeInfo} />
|
||||
<Route render={(): React.ReactElement<{}> => (
|
||||
<Route>
|
||||
<Main
|
||||
events={events}
|
||||
headers={lastHeaders}
|
||||
/>
|
||||
)} />
|
||||
</Route>
|
||||
</Switch>
|
||||
</main>
|
||||
);
|
||||
|
||||
@ -306,7 +306,7 @@ export default React.memo(styled(Playground)`
|
||||
}
|
||||
|
||||
.codeflask__lines {
|
||||
background: #fafafa;
|
||||
background: #f2f2f2;
|
||||
line-height: 18px;
|
||||
padding-top: 50px;
|
||||
z-index: 100;
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
import { Bid } from '@polkadot/types/interfaces';
|
||||
|
||||
import React from 'react';
|
||||
import { Spinner, Table } from '@polkadot/react-components';
|
||||
import { Table } from '@polkadot/react-components';
|
||||
import { useApi, useCall } from '@polkadot/react-hooks';
|
||||
|
||||
import { useTranslation } from '../translate';
|
||||
@ -21,31 +21,21 @@ function Bids ({ className }: Props): React.ReactElement<Props> {
|
||||
const bids = useCall<Bid[]>(api.query.society.bids, []);
|
||||
|
||||
return (
|
||||
<div className={`overviewSection ${className}`}>
|
||||
<h1>{t('bids')}</h1>
|
||||
{bids
|
||||
? bids.length
|
||||
? (
|
||||
<Table>
|
||||
<Table.Head>
|
||||
<th> </th>
|
||||
<th>{t('kind')}</th>
|
||||
<th>{t('value')}</th>
|
||||
</Table.Head>
|
||||
<Table.Body>
|
||||
{bids.map((bid): React.ReactNode => (
|
||||
<BidRow
|
||||
key={bid.who.toString()}
|
||||
value={bid}
|
||||
/>
|
||||
))}
|
||||
</Table.Body>
|
||||
</Table>
|
||||
)
|
||||
: <div>{t('No bids')}</div>
|
||||
: <Spinner />
|
||||
}
|
||||
</div>
|
||||
<Table className={className}>
|
||||
<Table.Head>
|
||||
<th className='start'><h1>{t('bids')}</h1></th>
|
||||
<th>{t('kind')}</th>
|
||||
<th>{t('value')}</th>
|
||||
</Table.Head>
|
||||
<Table.Body empty={bids && t('No bids')}>
|
||||
{bids?.map((bid): React.ReactNode => (
|
||||
<BidRow
|
||||
key={bid.who.toString()}
|
||||
value={bid}
|
||||
/>
|
||||
))}
|
||||
</Table.Body>
|
||||
</Table>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -6,7 +6,7 @@ import { DeriveSocietyCandidate } from '@polkadot/api-derive/types';
|
||||
import { OwnMembers } from '../types';
|
||||
|
||||
import React from 'react';
|
||||
import { Spinner, Table } from '@polkadot/react-components';
|
||||
import { Table } from '@polkadot/react-components';
|
||||
import { useApi, useCall } from '@polkadot/react-hooks';
|
||||
|
||||
import { useTranslation } from '../translate';
|
||||
@ -22,36 +22,26 @@ function Candidates ({ allMembers, className, isMember, ownMembers }: Props): Re
|
||||
const candidates = useCall<DeriveSocietyCandidate[]>(api.derive.society.candidates, []);
|
||||
|
||||
return (
|
||||
<div className={`overviewSection ${className}`}>
|
||||
<h1>{t('candidates')}</h1>
|
||||
{candidates
|
||||
? candidates.length
|
||||
? (
|
||||
<Table>
|
||||
<Table.Head>
|
||||
<th> </th>
|
||||
<th>{t('kind')}</th>
|
||||
<th>{t('value')}</th>
|
||||
<th colSpan={3}>{t('votes')}</th>
|
||||
<th> </th>
|
||||
</Table.Head>
|
||||
<Table.Body>
|
||||
{candidates.map((candidate): React.ReactNode => (
|
||||
<Candidate
|
||||
allMembers={allMembers}
|
||||
isMember={isMember}
|
||||
key={candidate.accountId.toString()}
|
||||
ownMembers={ownMembers}
|
||||
value={candidate}
|
||||
/>
|
||||
))}
|
||||
</Table.Body>
|
||||
</Table>
|
||||
)
|
||||
: <div>{t('No candidates')}</div>
|
||||
: <Spinner />
|
||||
}
|
||||
</div>
|
||||
<Table className={className}>
|
||||
<Table.Head>
|
||||
<th className='start'><h1>{t('candidates')}</h1></th>
|
||||
<th>{t('kind')}</th>
|
||||
<th>{t('value')}</th>
|
||||
<th className='start'>{t('votes')}</th>
|
||||
<th> </th>
|
||||
</Table.Head>
|
||||
<Table.Body empty={candidates && t('No candidates')}>
|
||||
{candidates?.map((candidate): React.ReactNode => (
|
||||
<Candidate
|
||||
allMembers={allMembers}
|
||||
isMember={isMember}
|
||||
key={candidate.accountId.toString()}
|
||||
ownMembers={ownMembers}
|
||||
value={candidate}
|
||||
/>
|
||||
))}
|
||||
</Table.Body>
|
||||
</Table>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -34,30 +34,27 @@ function Defender ({ className, info, isMember, ownMembers }: Props): React.Reac
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={`overviewSection ${className}`}>
|
||||
<h1>{t('defender')}</h1>
|
||||
<Table>
|
||||
<Table.Head>
|
||||
<th> </th>
|
||||
<th colSpan={3}>{t('votes')}</th>
|
||||
<th> </th>
|
||||
</Table.Head>
|
||||
<Table.Body>
|
||||
<tr>
|
||||
<td className='address all'>
|
||||
<AddressSmall value={info.defender} />
|
||||
</td>
|
||||
<VoteDisplay votes={votes} />
|
||||
<td className='button'>
|
||||
<DefenderVoting
|
||||
isMember={isMember}
|
||||
ownMembers={ownMembers}
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
</Table.Body>
|
||||
</Table>
|
||||
</div>
|
||||
<Table className={className}>
|
||||
<Table.Head>
|
||||
<th className='start'><h1>{t('defender')}</h1></th>
|
||||
<th className='start'>{t('votes')}</th>
|
||||
<th> </th>
|
||||
</Table.Head>
|
||||
<Table.Body>
|
||||
<tr>
|
||||
<td className='address all'>
|
||||
<AddressSmall value={info.defender} />
|
||||
</td>
|
||||
<VoteDisplay votes={votes} />
|
||||
<td className='button'>
|
||||
<DefenderVoting
|
||||
isMember={isMember}
|
||||
ownMembers={ownMembers}
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
</Table.Body>
|
||||
</Table>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
import { DeriveSociety, DeriveSocietyMember } from '@polkadot/api-derive/types';
|
||||
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { Spinner, Table } from '@polkadot/react-components';
|
||||
import { Table } from '@polkadot/react-components';
|
||||
import { useApi, useCall } from '@polkadot/react-hooks';
|
||||
|
||||
import { useTranslation } from '../translate';
|
||||
@ -29,31 +29,21 @@ function Members ({ className, info }: Props): React.ReactElement<Props> {
|
||||
}, [info, members]);
|
||||
|
||||
return (
|
||||
<div className={`overviewSection ${className}`}>
|
||||
<h1>{t('members')}</h1>
|
||||
{info
|
||||
? filtered.length
|
||||
? (
|
||||
<Table>
|
||||
<Table.Head>
|
||||
<th colSpan={3}> </th>
|
||||
<th>{t('strikes')}</th>
|
||||
</Table.Head>
|
||||
<Table.Body>
|
||||
{filtered.map((member): React.ReactNode => (
|
||||
<Member
|
||||
isHead={info?.head?.eq(member.accountId)}
|
||||
key={member.accountId.toString()}
|
||||
value={member}
|
||||
/>
|
||||
))}
|
||||
</Table.Body>
|
||||
</Table>
|
||||
)
|
||||
: <div>{t('No active members')}</div>
|
||||
: <Spinner />
|
||||
}
|
||||
</div>
|
||||
<Table className={className}>
|
||||
<Table.Head>
|
||||
<th colSpan={3} className='start'><h1>{t('members')}</h1></th>
|
||||
<th>{t('strikes')}</th>
|
||||
</Table.Head>
|
||||
<Table.Body empty={info && t('No active members')}>
|
||||
{filtered.map((member): React.ReactNode => (
|
||||
<Member
|
||||
isHead={info?.head?.eq(member.accountId)}
|
||||
key={member.accountId.toString()}
|
||||
value={member}
|
||||
/>
|
||||
))}
|
||||
</Table.Body>
|
||||
</Table>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -5,15 +5,17 @@
|
||||
import { VoteSplit, VoteType } from '../types';
|
||||
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { AddressMini, Expander } from '@polkadot/react-components';
|
||||
|
||||
import { useTranslation } from '../translate';
|
||||
|
||||
interface Props {
|
||||
className?: string;
|
||||
votes?: VoteType[];
|
||||
}
|
||||
|
||||
function VoteDisplay ({ votes }: Props): React.ReactElement<Props> {
|
||||
function VoteDisplay ({ className, votes }: Props): React.ReactElement<Props> {
|
||||
const { t } = useTranslation();
|
||||
const [{ allAye, allNay, allSkeptic }, setVoteSplit] = useState<VoteSplit>({ allAye: [], allNay: [], allSkeptic: [] });
|
||||
|
||||
@ -26,45 +28,43 @@ function VoteDisplay ({ votes }: Props): React.ReactElement<Props> {
|
||||
}, [votes]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<td>
|
||||
{allSkeptic.length !== 0 && (
|
||||
<Expander summary={t('Skeptics ({{count}})', { replace: { count: allSkeptic.length } })}>
|
||||
{allSkeptic.map(([who]): React.ReactNode =>
|
||||
<AddressMini
|
||||
key={who.toString()}
|
||||
value={who}
|
||||
/>
|
||||
)}
|
||||
</Expander>
|
||||
)}
|
||||
</td>
|
||||
<td>
|
||||
{allAye.length !== 0 && (
|
||||
<Expander summary={t('Approvals ({{count}})', { replace: { count: allAye.length } })}>
|
||||
{allAye.map(([who]): React.ReactNode =>
|
||||
<AddressMini
|
||||
key={who.toString()}
|
||||
value={who}
|
||||
/>
|
||||
)}
|
||||
</Expander>
|
||||
)}
|
||||
</td>
|
||||
<td>
|
||||
{allNay.length !== 0 && (
|
||||
<Expander summary={t('Rejections ({{count}})', { replace: { count: allNay.length } })}>
|
||||
{allNay.map(([who]): React.ReactNode =>
|
||||
<AddressMini
|
||||
key={who.toString()}
|
||||
value={who}
|
||||
/>
|
||||
)}
|
||||
</Expander>
|
||||
)}
|
||||
</td>
|
||||
</>
|
||||
<td className={className}>
|
||||
{allSkeptic.length !== 0 && (
|
||||
<Expander summary={t('Skeptics ({{count}})', { replace: { count: allSkeptic.length } })}>
|
||||
{allSkeptic.map(([who]): React.ReactNode =>
|
||||
<AddressMini
|
||||
key={who.toString()}
|
||||
value={who}
|
||||
/>
|
||||
)}
|
||||
</Expander>
|
||||
)}
|
||||
{allAye.length !== 0 && (
|
||||
<Expander summary={t('Approvals ({{count}})', { replace: { count: allAye.length } })}>
|
||||
{allAye.map(([who]): React.ReactNode =>
|
||||
<AddressMini
|
||||
key={who.toString()}
|
||||
value={who}
|
||||
/>
|
||||
)}
|
||||
</Expander>
|
||||
)}
|
||||
{allNay.length !== 0 && (
|
||||
<Expander summary={t('Rejections ({{count}})', { replace: { count: allNay.length } })}>
|
||||
{allNay.map(([who]): React.ReactNode =>
|
||||
<AddressMini
|
||||
key={who.toString()}
|
||||
value={who}
|
||||
/>
|
||||
)}
|
||||
</Expander>
|
||||
)}
|
||||
</td>
|
||||
);
|
||||
}
|
||||
|
||||
export default React.memo(VoteDisplay);
|
||||
export default React.memo(styled(VoteDisplay)`
|
||||
.ui--Expander+ui--Expander {
|
||||
margin-left: 0.5rem;
|
||||
}
|
||||
`);
|
||||
|
||||
@ -77,34 +77,29 @@ function Actions ({ allRewards, allStashes, className, isVisible, next, stakingO
|
||||
{isNewStakeOpen && (
|
||||
<StartStaking onClose={_toggleNewStake} />
|
||||
)}
|
||||
{foundStashes?.length
|
||||
? (
|
||||
<Table>
|
||||
<Table.Head>
|
||||
<th colSpan={2}> </th>
|
||||
<th className='address'>{t('controller')}</th>
|
||||
<th colSpan={3}> </th>
|
||||
</Table.Head>
|
||||
<Table.Body>
|
||||
{foundStashes.map(([stashId, isOwnStash]): React.ReactNode => (
|
||||
<Account
|
||||
activeEra={activeEra}
|
||||
allStashes={allStashes}
|
||||
isOwnStash={isOwnStash}
|
||||
isVisible={isVisible}
|
||||
key={stashId}
|
||||
next={next}
|
||||
onUpdateType={_onUpdateType}
|
||||
rewards={allRewards && allRewards[stashId]}
|
||||
stakingOverview={stakingOverview}
|
||||
stashId={stashId}
|
||||
/>
|
||||
))}
|
||||
</Table.Body>
|
||||
</Table>
|
||||
)
|
||||
: <div>{t('No funds staked yet. Bond funds to validate or nominate a validator.')}</div>
|
||||
}
|
||||
<Table>
|
||||
<Table.Head>
|
||||
<th className='start' colSpan={2}><h1>{t('stashes')}</h1></th>
|
||||
<th className='address'>{t('controller')}</th>
|
||||
<th colSpan={3}> </th>
|
||||
</Table.Head>
|
||||
<Table.Body empty={t('No funds staked yet. Bond funds to validate or nominate a validator.')}>
|
||||
{foundStashes?.map(([stashId, isOwnStash]): React.ReactNode => (
|
||||
<Account
|
||||
activeEra={activeEra}
|
||||
allStashes={allStashes}
|
||||
isOwnStash={isOwnStash}
|
||||
isVisible={isVisible}
|
||||
key={stashId}
|
||||
next={next}
|
||||
onUpdateType={_onUpdateType}
|
||||
rewards={allRewards && allRewards[stashId]}
|
||||
stakingOverview={stakingOverview}
|
||||
stashId={stashId}
|
||||
/>
|
||||
))}
|
||||
</Table.Body>
|
||||
</Table>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -7,7 +7,7 @@ import { AccountId } from '@polkadot/types/interfaces';
|
||||
import { AddressDetails } from './types';
|
||||
|
||||
import React, { useEffect, useReducer, useState } from 'react';
|
||||
import { Input, Spinner, Table } from '@polkadot/react-components';
|
||||
import { Input, Table } from '@polkadot/react-components';
|
||||
import { useFavorites } from '@polkadot/react-hooks';
|
||||
|
||||
import { STORE_FAVS_BASE } from '../constants';
|
||||
@ -116,8 +116,8 @@ function CurrentList ({ authorsMap, hasQueries, isIntentions, isVisible, lastAut
|
||||
);
|
||||
}, [stakingOverview, validators]);
|
||||
|
||||
const _renderRows = (addresses: AccountExtend[], isMain: boolean): React.ReactNode =>
|
||||
addresses.map(([address, isElected, isFavorite]): React.ReactNode => (
|
||||
const _renderRows = (addresses?: AccountExtend[], isMain?: boolean): React.ReactNode =>
|
||||
addresses?.map(([address, isElected, isFavorite]): React.ReactNode => (
|
||||
<Address
|
||||
address={address}
|
||||
filterName={nameFilter}
|
||||
@ -136,22 +136,19 @@ function CurrentList ({ authorsMap, hasQueries, isIntentions, isVisible, lastAut
|
||||
/>
|
||||
));
|
||||
|
||||
if (!stakingOverview) {
|
||||
return <Spinner />;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={`${!isVisible && 'staking--hidden'}`}>
|
||||
<Input
|
||||
autoFocus
|
||||
isFull
|
||||
label={t('filter by name, address or index')}
|
||||
onChange={setNameFilter}
|
||||
value={nameFilter}
|
||||
/>
|
||||
<Table className={isIntentions ? 'staking--hidden' : ''}>
|
||||
<Table.Head>
|
||||
<th colSpan={3}> </th>
|
||||
<Table.Head filter={
|
||||
<Input
|
||||
autoFocus
|
||||
isFull
|
||||
label={t('filter by name, address or index')}
|
||||
onChange={setNameFilter}
|
||||
value={nameFilter}
|
||||
/>
|
||||
}>
|
||||
<th colSpan={3} className='start'><h1>{t('validators')}</h1></th>
|
||||
<th>{t('other stake')}</th>
|
||||
<th>{t('own stake')}</th>
|
||||
<th>{t('commission')}</th>
|
||||
@ -159,24 +156,20 @@ function CurrentList ({ authorsMap, hasQueries, isIntentions, isVisible, lastAut
|
||||
<th>{t('last #')}</th>
|
||||
<th> </th>
|
||||
</Table.Head>
|
||||
<Table.Body>
|
||||
<Table.Body empty={stakingOverview && t('No active validators found')}>
|
||||
{_renderRows(validators, true)}
|
||||
</Table.Body>
|
||||
</Table>
|
||||
{isIntentions && (
|
||||
waiting
|
||||
? (
|
||||
<Table>
|
||||
<Table.Head>
|
||||
<th colSpan={9}> </th>
|
||||
</Table.Head>
|
||||
<Table.Body>
|
||||
{_renderRows(elected, false)}
|
||||
{_renderRows(waiting, false)}
|
||||
</Table.Body>
|
||||
</Table>
|
||||
)
|
||||
: <Spinner />
|
||||
<Table>
|
||||
<Table.Head>
|
||||
<th colSpan={9}className='start'><h1>{t('intentions')}</h1></th>
|
||||
</Table.Head>
|
||||
<Table.Body empty={waiting && t('No waiting validators found')}>
|
||||
{_renderRows(elected, false)}
|
||||
{_renderRows(waiting, false)}
|
||||
</Table.Body>
|
||||
</Table>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
@ -10,7 +10,7 @@ import BN from 'bn.js';
|
||||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { registry } from '@polkadot/react-api';
|
||||
import { Icon, InputBalance, Spinner, Table } from '@polkadot/react-components';
|
||||
import { Icon, InputBalance, Table } from '@polkadot/react-components';
|
||||
import { useAccounts, useApi, useCall, useDebounce, useFavorites } from '@polkadot/react-hooks';
|
||||
import { createType, Option } from '@polkadot/types';
|
||||
|
||||
@ -211,10 +211,6 @@ function Targets ({ className }: Props): React.ReactElement<Props> {
|
||||
}
|
||||
}, [allAccounts, amount, electedInfo, favorites, lastReward, sortBy, sortFromMax]);
|
||||
|
||||
if (!sorted) {
|
||||
return <Spinner />;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={className}>
|
||||
<Summary
|
||||
@ -223,43 +219,37 @@ function Targets ({ className }: Props): React.ReactElement<Props> {
|
||||
numValidators={validators.length}
|
||||
totalStaked={totalStaked}
|
||||
/>
|
||||
{sorted.length
|
||||
? (
|
||||
<>
|
||||
<InputBalance
|
||||
className='balanceInput'
|
||||
help={t('The amount that will be used on a per-validator basis to calculate rewards for that validator.')}
|
||||
isFull
|
||||
label={t('amount to use for estimation')}
|
||||
onChange={setAmount}
|
||||
value={_amount}
|
||||
<Table>
|
||||
<Table.Head filter={
|
||||
<InputBalance
|
||||
className='balanceInput'
|
||||
help={t('The amount that will be used on a per-validator basis to calculate rewards for that validator.')}
|
||||
isFull
|
||||
label={t('amount to use for estimation')}
|
||||
onChange={setAmount}
|
||||
value={_amount}
|
||||
/>
|
||||
}>
|
||||
<th className='start' colSpan={3}><h1>{t('validators')}</h1></th>
|
||||
{['rankComm', 'rankBondTotal', 'rankBondOwn', 'rankBondOther', 'rankOverall'].map((header): React.ReactNode => (
|
||||
<th
|
||||
className={`isClickable ${sortBy === header && 'ui--highlight--border'} number`}
|
||||
key={header}
|
||||
onClick={(): void => _sort(header as 'rankComm')}
|
||||
>{labels[header]}<Icon name={sortBy === header ? (sortFromMax ? 'chevron down' : 'chevron up') : 'minus'} /></th>
|
||||
))}
|
||||
<th> </th>
|
||||
</Table.Head>
|
||||
<Table.Body empty={sorted && t('No active validators to check for rewards available')}>
|
||||
{sorted?.map((info): React.ReactNode =>
|
||||
<Validator
|
||||
info={info}
|
||||
key={info.key}
|
||||
toggleFavorite={toggleFavorite}
|
||||
/>
|
||||
<Table>
|
||||
<Table.Head>
|
||||
<th colSpan={3}> </th>
|
||||
{['rankComm', 'rankBondTotal', 'rankBondOwn', 'rankBondOther', 'rankOverall'].map((header): React.ReactNode => (
|
||||
<th
|
||||
className={`isClickable ${sortBy === header && 'ui--highlight--border'} number`}
|
||||
key={header}
|
||||
onClick={(): void => _sort(header as 'rankComm')}
|
||||
>{labels[header]}<Icon name={sortBy === header ? (sortFromMax ? 'chevron down' : 'chevron up') : 'minus'} /></th>
|
||||
))}
|
||||
<th> </th>
|
||||
</Table.Head>
|
||||
<Table.Body>
|
||||
{sorted.map((info): React.ReactNode =>
|
||||
<Validator
|
||||
info={info}
|
||||
key={info.key}
|
||||
toggleFavorite={toggleFavorite}
|
||||
/>
|
||||
)}
|
||||
</Table.Body>
|
||||
</Table>
|
||||
</>
|
||||
)
|
||||
: <Spinner />
|
||||
}
|
||||
)}
|
||||
</Table.Body>
|
||||
</Table>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -11,7 +11,7 @@ import { useTranslation } from '../translate';
|
||||
|
||||
interface Props {
|
||||
className?: string;
|
||||
members: string[];
|
||||
members?: string[];
|
||||
prime?: AccountId | null;
|
||||
}
|
||||
|
||||
@ -19,37 +19,30 @@ function Members ({ className, members, prime }: Props): React.ReactElement<Prop
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<div className={className}>
|
||||
{members.length
|
||||
? (
|
||||
<Table>
|
||||
<Table.Head>
|
||||
<th colSpan={3}> </th>
|
||||
</Table.Head>
|
||||
<Table.Body>
|
||||
{members.map((accountId): React.ReactNode => (
|
||||
<tr key={accountId.toString()}>
|
||||
<td className='address'>
|
||||
<AddressSmall value={accountId} />
|
||||
</td>
|
||||
<td>
|
||||
{prime?.eq(accountId) && (
|
||||
<Tag
|
||||
color='green'
|
||||
hover={t('Committee prime member, default voting')}
|
||||
label={t('prime member')}
|
||||
/>
|
||||
)}
|
||||
</td>
|
||||
<td className='all'> </td>
|
||||
</tr>
|
||||
))}
|
||||
</Table.Body>
|
||||
</Table>
|
||||
)
|
||||
: <div>{t('No members found')}</div>
|
||||
}
|
||||
</div>
|
||||
<Table className={className}>
|
||||
<Table.Head>
|
||||
<th className='start' colSpan={3}><h1>{t('members')}</h1></th>
|
||||
</Table.Head>
|
||||
<Table.Body empty={members && t('No members found')}>
|
||||
{members?.map((accountId): React.ReactNode => (
|
||||
<tr key={accountId.toString()}>
|
||||
<td className='address'>
|
||||
<AddressSmall value={accountId} />
|
||||
</td>
|
||||
<td>
|
||||
{prime?.eq(accountId) && (
|
||||
<Tag
|
||||
color='green'
|
||||
hover={t('Committee prime member, default voting')}
|
||||
label={t('prime member')}
|
||||
/>
|
||||
)}
|
||||
</td>
|
||||
<td className='all'> </td>
|
||||
</tr>
|
||||
))}
|
||||
</Table.Body>
|
||||
</Table>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -6,7 +6,7 @@ import { Hash } from '@polkadot/types/interfaces';
|
||||
import { ComponentProps as Props } from '../types';
|
||||
|
||||
import React from 'react';
|
||||
import { Button, Spinner, Table } from '@polkadot/react-components';
|
||||
import { Button, Table } from '@polkadot/react-components';
|
||||
|
||||
import { useTranslation } from '../translate';
|
||||
import Proposal from './Proposal';
|
||||
@ -23,31 +23,24 @@ function Proposals ({ className, isMember, members, prime, proposals }: Props):
|
||||
members={members}
|
||||
/>
|
||||
</Button.Group>
|
||||
{proposals
|
||||
? proposals.length
|
||||
? (
|
||||
<Table>
|
||||
<Table.Head>
|
||||
<th colSpan={2}> </th>
|
||||
<th>{t('threshold')}</th>
|
||||
<th className='address'>{t('aye')}</th>
|
||||
<th className='address'>{t('nay')}</th>
|
||||
<th> </th>
|
||||
</Table.Head>
|
||||
<Table.Body>
|
||||
{proposals.map((hash: Hash): React.ReactNode => (
|
||||
<Proposal
|
||||
imageHash={hash.toHex()}
|
||||
key={hash.toHex()}
|
||||
prime={prime}
|
||||
/>
|
||||
))}
|
||||
</Table.Body>
|
||||
</Table>
|
||||
)
|
||||
: <div>{t('No committee proposals')}</div>
|
||||
: <Spinner />
|
||||
}
|
||||
<Table>
|
||||
<Table.Head>
|
||||
<th className='start' colSpan={2}><h1>{t('proposals')}</h1></th>
|
||||
<th>{t('threshold')}</th>
|
||||
<th className='address'>{t('aye')}</th>
|
||||
<th className='address'>{t('nay')}</th>
|
||||
<th> </th>
|
||||
</Table.Head>
|
||||
<Table.Body empty={proposals && t('No committee proposals')}>
|
||||
{proposals?.map((hash: Hash): React.ReactNode => (
|
||||
<Proposal
|
||||
imageHash={hash.toHex()}
|
||||
key={hash.toHex()}
|
||||
prime={prime}
|
||||
/>
|
||||
))}
|
||||
</Table.Body>
|
||||
</Table>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -7,7 +7,7 @@ import { AccountId, Balance } from '@polkadot/types/interfaces';
|
||||
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { Spinner, Table } from '@polkadot/react-components';
|
||||
import { Table } from '@polkadot/react-components';
|
||||
import { useApi, useAccounts, useCall } from '@polkadot/react-hooks';
|
||||
|
||||
import Proposal from './Proposal';
|
||||
@ -40,38 +40,26 @@ function ProposalsBase ({ className, isApprovals, proposals }: Props): React.Rea
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={className}>
|
||||
<h1>{isApprovals ? t('Approved') : t('Proposals')}</h1>
|
||||
{proposals
|
||||
? proposals.length
|
||||
? (
|
||||
<Table>
|
||||
<Table.Head>
|
||||
<th colSpan={2}> </th>
|
||||
<th className='address'>{t('beneficiary')}</th>
|
||||
<th>{t('payment')}</th>
|
||||
<th>{t('bond')}</th>
|
||||
<th colSpan={2}> </th>
|
||||
</Table.Head>
|
||||
<Table.Body>
|
||||
{proposals.map((proposal): React.ReactNode => (
|
||||
<Proposal
|
||||
isMember={isMember}
|
||||
onRespond={_onRespond}
|
||||
proposal={proposal}
|
||||
key={proposal.id.toString()}
|
||||
withSend={!isApprovals}
|
||||
/>
|
||||
))}
|
||||
</Table.Body>
|
||||
</Table>
|
||||
)
|
||||
: isApprovals
|
||||
? <div>{t('No approved proposals')}</div>
|
||||
: <div>{t('No pending proposals')}</div>
|
||||
: <Spinner />
|
||||
}
|
||||
</div>
|
||||
<Table className={className}>
|
||||
<Table.Head>
|
||||
<th className='start' colSpan={2}><h1>{isApprovals ? t('Approved') : t('Proposals')}</h1></th>
|
||||
<th className='address'>{t('beneficiary')}</th>
|
||||
<th>{t('payment')}</th>
|
||||
<th>{t('bond')}</th>
|
||||
<th colSpan={2}> </th>
|
||||
</Table.Head>
|
||||
<Table.Body empty={proposals && (isApprovals ? t('No approved proposals') : t('No pending proposals'))}>
|
||||
{proposals?.map((proposal): React.ReactNode => (
|
||||
<Proposal
|
||||
isMember={isMember}
|
||||
onRespond={_onRespond}
|
||||
proposal={proposal}
|
||||
key={proposal.id.toString()}
|
||||
withSend={!isApprovals}
|
||||
/>
|
||||
))}
|
||||
</Table.Body>
|
||||
</Table>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -100,7 +100,7 @@ export default React.memo(styled(AddressMini)`
|
||||
|
||||
&.padded {
|
||||
display: inline-block;
|
||||
padding: 0.25rem 1rem 0 0;
|
||||
padding: 0 1rem 0 0;
|
||||
}
|
||||
|
||||
&.summary {
|
||||
|
||||
@ -43,6 +43,10 @@ const Memo = React.memo(styled(ButtonGroup)`
|
||||
margin-bottom: 0.5rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
&+.ui--Table {
|
||||
margin-top: 1.5rem;
|
||||
}
|
||||
`) as unknown as GroupType;
|
||||
|
||||
Memo.Divider = Divider;
|
||||
|
||||
@ -28,7 +28,7 @@ class CardGrid extends Collection<Props, State> {
|
||||
|
||||
return (
|
||||
<div className='ui--CardGrid-empty'>
|
||||
<h2>{emptyText || t('No items')}</h2>
|
||||
<h1>{emptyText || t('No items')}</h1>
|
||||
{buttons && (
|
||||
<div className='ui--CardGrid-buttons'>
|
||||
{buttons}
|
||||
|
||||
@ -104,10 +104,10 @@ export default React.memo(styled(CardSummary)`
|
||||
}
|
||||
|
||||
> div {
|
||||
font-size: 1.85rem;
|
||||
font-size: 1.75rem;
|
||||
font-weight: 100;
|
||||
position: relative;
|
||||
line-height: 1.85rem;
|
||||
line-height: 1.75rem;
|
||||
text-align: right;
|
||||
|
||||
> * {
|
||||
|
||||
@ -4,26 +4,40 @@
|
||||
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { isString } from '@polkadot/util';
|
||||
|
||||
import Spinner from './Spinner';
|
||||
|
||||
interface BaseProps {
|
||||
children: React.ReactNode;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
type BodyProps = BaseProps;
|
||||
interface BodyProps extends BaseProps {
|
||||
empty?: React.ReactNode;
|
||||
}
|
||||
|
||||
type HeadProps = BaseProps;
|
||||
interface HeadProps extends BaseProps {
|
||||
filter?: React.ReactNode;
|
||||
}
|
||||
|
||||
type TableProps = BaseProps;
|
||||
interface TableProps extends BaseProps {
|
||||
isFixed?: boolean;
|
||||
}
|
||||
|
||||
type TableImpl = React.FC<TableProps> & {
|
||||
Body: React.FC<BodyProps>;
|
||||
Head: React.FC<HeadProps>;
|
||||
}
|
||||
|
||||
function Head ({ children, className }: HeadProps): React.ReactElement<HeadProps> {
|
||||
function Head ({ children, className, filter }: HeadProps): React.ReactElement<HeadProps> {
|
||||
return (
|
||||
<thead className={className}>
|
||||
{filter && (
|
||||
<tr className='filter'>
|
||||
<th colSpan={100}>{filter}</th>
|
||||
</tr>
|
||||
)}
|
||||
<tr>
|
||||
{children}
|
||||
</tr>
|
||||
@ -31,17 +45,28 @@ function Head ({ children, className }: HeadProps): React.ReactElement<HeadProps
|
||||
);
|
||||
}
|
||||
|
||||
function Body ({ children, className }: BodyProps): React.ReactElement<BodyProps> {
|
||||
function Body ({ children, className, empty }: BodyProps): React.ReactElement<BodyProps> {
|
||||
const isEmpty = !children || (Array.isArray(children) && children.length === 0);
|
||||
|
||||
return (
|
||||
<tbody className={className}>
|
||||
{children}
|
||||
{isEmpty
|
||||
? (
|
||||
<tr><td colSpan={100}>{
|
||||
isString(empty)
|
||||
? <div className='empty'>{empty}</div>
|
||||
: empty || <Spinner />
|
||||
}</td></tr>
|
||||
)
|
||||
: children
|
||||
}
|
||||
</tbody>
|
||||
);
|
||||
}
|
||||
|
||||
function Table ({ children, className }: TableProps): React.ReactElement<TableProps> {
|
||||
function Table ({ children, className, isFixed }: TableProps): React.ReactElement<TableProps> {
|
||||
return (
|
||||
<div className={`ui--Table ${className}`}>
|
||||
<div className={`ui--Table ${isFixed && 'isFixed'} ${className}`}>
|
||||
<table>
|
||||
{children}
|
||||
</table>
|
||||
@ -51,21 +76,33 @@ function Table ({ children, className }: TableProps): React.ReactElement<TablePr
|
||||
|
||||
const Memo = React.memo(styled(Table)`
|
||||
margin-bottom: 1.5rem;
|
||||
margin-top: 0.25rem;
|
||||
|
||||
&.isFixed table {
|
||||
table-layout: fixed;
|
||||
}
|
||||
|
||||
table {
|
||||
border: 1px solid #f2f2f2;
|
||||
border: 1px solid #eee;
|
||||
border-radius: 0.25rem;
|
||||
border-spacing: 0;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
|
||||
thead tr {
|
||||
background: #f5f5f5;
|
||||
background: #f9f9f9;
|
||||
text-transform: lowercase;
|
||||
|
||||
&.filter th {
|
||||
padding: 0.25rem 0.5rem 0;
|
||||
}
|
||||
}
|
||||
|
||||
tbody tr {
|
||||
background: white;
|
||||
|
||||
td {
|
||||
border-top: 1px solid #f2f2f2;
|
||||
}
|
||||
}
|
||||
|
||||
tr {
|
||||
@ -104,8 +141,13 @@ const Memo = React.memo(styled(Table)`
|
||||
}
|
||||
|
||||
td, th {
|
||||
padding: 0.5rem 0.75rem;
|
||||
vertical-align: middle;
|
||||
&:first-child {
|
||||
padding-left: 1.5rem;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
padding-right: 1.5rem;
|
||||
}
|
||||
|
||||
&.all {
|
||||
width: 100%;
|
||||
@ -117,8 +159,10 @@ const Memo = React.memo(styled(Table)`
|
||||
}
|
||||
|
||||
td {
|
||||
border-top: 1px solid #f2f2f2;
|
||||
// border-top: 1px solid #f2f2f2;
|
||||
padding: 0.75rem 1rem;
|
||||
text-align: left;
|
||||
vertical-align: middle;
|
||||
|
||||
label {
|
||||
display: block !important;
|
||||
@ -129,6 +173,19 @@ const Memo = React.memo(styled(Table)`
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
div.empty {
|
||||
opacity: 0.6;
|
||||
padding: 0.25rem;
|
||||
}
|
||||
|
||||
.ui--Spinner {
|
||||
margin: auto 0.5rem;
|
||||
|
||||
.text {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&.address {
|
||||
min-width: 11rem;
|
||||
padding: 0.85rem 1rem;
|
||||
@ -147,14 +204,40 @@ const Memo = React.memo(styled(Table)`
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
&.combined {
|
||||
border-top-width: 0;
|
||||
}
|
||||
|
||||
&.empty {
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
&.hash {
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
&.number {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
&.relative {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
&.overflow {
|
||||
max-width: 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
&.together {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
&.middle {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
&.mini {
|
||||
padding: 0 0.75rem 0 0;
|
||||
white-space: nowrap;
|
||||
@ -166,12 +249,19 @@ const Memo = React.memo(styled(Table)`
|
||||
}
|
||||
|
||||
th {
|
||||
color: #888;
|
||||
color: rgba(0, 0, 0, 0.6);
|
||||
font-family: sans-serif;
|
||||
font-weight: 400;
|
||||
text-align: right;
|
||||
padding: 0.75rem 1rem 0.25rem;
|
||||
vertical-align: baseline;
|
||||
white-space: nowrap;
|
||||
|
||||
h1, h2 {
|
||||
color: inherit;
|
||||
font-size: 1.75rem;
|
||||
}
|
||||
|
||||
&:first-child {
|
||||
border-top-left-radius: 0.25rem;
|
||||
}
|
||||
|
||||
@ -99,7 +99,7 @@ export default css`
|
||||
}
|
||||
|
||||
.ui.modal {
|
||||
background: #fafafa;
|
||||
background: #f9f9f9;
|
||||
color: #4e4e4e;
|
||||
font-family: sans-serif;
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user