feat(explorer): add epoch to block pages (#5817)
This commit is contained in:
parent
bc8a427788
commit
bf094288fd
@ -8,12 +8,14 @@ import EpochMissingOverview from './epoch-missing';
|
|||||||
import { Icon, Tooltip } from '@vegaprotocol/ui-toolkit';
|
import { Icon, Tooltip } from '@vegaprotocol/ui-toolkit';
|
||||||
import type { IconProps } from '@vegaprotocol/ui-toolkit';
|
import type { IconProps } from '@vegaprotocol/ui-toolkit';
|
||||||
import isPast from 'date-fns/isPast';
|
import isPast from 'date-fns/isPast';
|
||||||
|
import { EpochSymbol } from '../links/block-link/block-link';
|
||||||
|
|
||||||
const borderClass =
|
const borderClass =
|
||||||
'border-solid border-2 border-vega-dark-200 border-collapse';
|
'border-solid border-2 border-vega-dark-200 border-collapse';
|
||||||
|
|
||||||
export type EpochOverviewProps = {
|
export type EpochOverviewProps = {
|
||||||
id?: string;
|
id?: string;
|
||||||
|
icon?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -24,7 +26,7 @@ export type EpochOverviewProps = {
|
|||||||
*
|
*
|
||||||
* The details are hidden in a tooltip, behind the epoch number
|
* The details are hidden in a tooltip, behind the epoch number
|
||||||
*/
|
*/
|
||||||
const EpochOverview = ({ id }: EpochOverviewProps) => {
|
const EpochOverview = ({ id, icon = true }: EpochOverviewProps) => {
|
||||||
const { data, error, loading } = useExplorerEpochQuery({
|
const { data, error, loading } = useExplorerEpochQuery({
|
||||||
variables: { id: id || '' },
|
variables: { id: id || '' },
|
||||||
});
|
});
|
||||||
@ -38,7 +40,12 @@ const EpochOverview = ({ id }: EpochOverviewProps) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!ti || loading || error) {
|
if (!ti || loading || error) {
|
||||||
return <span>{id}</span>;
|
return (
|
||||||
|
<span>
|
||||||
|
<EpochSymbol />
|
||||||
|
{id}
|
||||||
|
</span>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const description = (
|
const description = (
|
||||||
@ -90,7 +97,11 @@ const EpochOverview = ({ id }: EpochOverviewProps) => {
|
|||||||
return (
|
return (
|
||||||
<Tooltip description={description}>
|
<Tooltip description={description}>
|
||||||
<p>
|
<p>
|
||||||
|
{icon ? (
|
||||||
<IconForEpoch start={ti.start} end={ti.end} />
|
<IconForEpoch start={ti.start} end={ti.end} />
|
||||||
|
) : (
|
||||||
|
<EpochSymbol />
|
||||||
|
)}
|
||||||
{id}
|
{id}
|
||||||
</p>
|
</p>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
|
@ -0,0 +1,10 @@
|
|||||||
|
query ExplorerEpochForBlock($block: String!) {
|
||||||
|
epoch(block: $block) {
|
||||||
|
id
|
||||||
|
timestamps {
|
||||||
|
start
|
||||||
|
end
|
||||||
|
lastBlock
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
53
apps/explorer/src/app/components/links/block-link/__generated__/EpochByBlock.ts
generated
Normal file
53
apps/explorer/src/app/components/links/block-link/__generated__/EpochByBlock.ts
generated
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
import * as Types from '@vegaprotocol/types';
|
||||||
|
|
||||||
|
import { gql } from '@apollo/client';
|
||||||
|
import * as Apollo from '@apollo/client';
|
||||||
|
const defaultOptions = {} as const;
|
||||||
|
export type ExplorerEpochForBlockQueryVariables = Types.Exact<{
|
||||||
|
block: Types.Scalars['String'];
|
||||||
|
}>;
|
||||||
|
|
||||||
|
|
||||||
|
export type ExplorerEpochForBlockQuery = { __typename?: 'Query', epoch: { __typename?: 'Epoch', id: string, timestamps: { __typename?: 'EpochTimestamps', start?: any | null, end?: any | null, lastBlock?: string | null } } };
|
||||||
|
|
||||||
|
|
||||||
|
export const ExplorerEpochForBlockDocument = gql`
|
||||||
|
query ExplorerEpochForBlock($block: String!) {
|
||||||
|
epoch(block: $block) {
|
||||||
|
id
|
||||||
|
timestamps {
|
||||||
|
start
|
||||||
|
end
|
||||||
|
lastBlock
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* __useExplorerEpochForBlockQuery__
|
||||||
|
*
|
||||||
|
* To run a query within a React component, call `useExplorerEpochForBlockQuery` and pass it any options that fit your needs.
|
||||||
|
* When your component renders, `useExplorerEpochForBlockQuery` returns an object from Apollo Client that contains loading, error, and data properties
|
||||||
|
* you can use to render your UI.
|
||||||
|
*
|
||||||
|
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* const { data, loading, error } = useExplorerEpochForBlockQuery({
|
||||||
|
* variables: {
|
||||||
|
* block: // value for 'block'
|
||||||
|
* },
|
||||||
|
* });
|
||||||
|
*/
|
||||||
|
export function useExplorerEpochForBlockQuery(baseOptions: Apollo.QueryHookOptions<ExplorerEpochForBlockQuery, ExplorerEpochForBlockQueryVariables>) {
|
||||||
|
const options = {...defaultOptions, ...baseOptions}
|
||||||
|
return Apollo.useQuery<ExplorerEpochForBlockQuery, ExplorerEpochForBlockQueryVariables>(ExplorerEpochForBlockDocument, options);
|
||||||
|
}
|
||||||
|
export function useExplorerEpochForBlockLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<ExplorerEpochForBlockQuery, ExplorerEpochForBlockQueryVariables>) {
|
||||||
|
const options = {...defaultOptions, ...baseOptions}
|
||||||
|
return Apollo.useLazyQuery<ExplorerEpochForBlockQuery, ExplorerEpochForBlockQueryVariables>(ExplorerEpochForBlockDocument, options);
|
||||||
|
}
|
||||||
|
export type ExplorerEpochForBlockQueryHookResult = ReturnType<typeof useExplorerEpochForBlockQuery>;
|
||||||
|
export type ExplorerEpochForBlockLazyQueryHookResult = ReturnType<typeof useExplorerEpochForBlockLazyQuery>;
|
||||||
|
export type ExplorerEpochForBlockQueryResult = Apollo.QueryResult<ExplorerEpochForBlockQuery, ExplorerEpochForBlockQueryVariables>;
|
@ -4,17 +4,56 @@ import { Link } from 'react-router-dom';
|
|||||||
|
|
||||||
import type { ComponentProps } from 'react';
|
import type { ComponentProps } from 'react';
|
||||||
import Hash from '../hash';
|
import Hash from '../hash';
|
||||||
|
import { useExplorerEpochForBlockQuery } from './__generated__/EpochByBlock';
|
||||||
|
import { t } from '@vegaprotocol/i18n';
|
||||||
|
|
||||||
export type BlockLinkProps = Partial<ComponentProps<typeof Link>> & {
|
export type BlockLinkProps = Partial<ComponentProps<typeof Link>> & {
|
||||||
height: string;
|
height: string;
|
||||||
|
showEpoch?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
const BlockLink = ({ height, ...props }: BlockLinkProps) => {
|
const BlockLink = ({ height, showEpoch = false, ...props }: BlockLinkProps) => {
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
<Link className="underline" {...props} to={`/${Routes.BLOCKS}/${height}`}>
|
<Link className="underline" {...props} to={`/${Routes.BLOCKS}/${height}`}>
|
||||||
<Hash text={height} />
|
<Hash text={height} />
|
||||||
</Link>
|
</Link>
|
||||||
|
{showEpoch && <EpochForBlock block={height} />}
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export function EpochForBlock(props: { block: string }) {
|
||||||
|
const { error, data, loading } = useExplorerEpochForBlockQuery({
|
||||||
|
errorPolicy: 'ignore',
|
||||||
|
variables: { block: props.block },
|
||||||
|
});
|
||||||
|
|
||||||
|
// NOTE: 0.73.x & <0.74.2 can error showing epoch, so for now we hide loading
|
||||||
|
// or error states and only display if we get usable data
|
||||||
|
if (error || loading || !data) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<span className="ml-2" title={t('Epoch')}>
|
||||||
|
<EpochSymbol />
|
||||||
|
{data.epoch.id}
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const EPOCH_SYMBOL = 'ⓔ';
|
||||||
|
|
||||||
|
export function EpochSymbol() {
|
||||||
|
return (
|
||||||
|
<em
|
||||||
|
title={t('Epoch')}
|
||||||
|
className="mr-1 cursor-default text-xl leading-none align-text-bottom not-italic"
|
||||||
|
>
|
||||||
|
{EPOCH_SYMBOL}
|
||||||
|
</em>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export default BlockLink;
|
export default BlockLink;
|
||||||
|
@ -0,0 +1,16 @@
|
|||||||
|
import { render, screen } from '@testing-library/react';
|
||||||
|
import GovernanceLink from './governance-link';
|
||||||
|
|
||||||
|
describe('GovernanceLink', () => {
|
||||||
|
it('renders the link with the correct text', () => {
|
||||||
|
render(<GovernanceLink text="Governance internet website" />);
|
||||||
|
const linkElement = screen.getByText('Governance internet website');
|
||||||
|
expect(linkElement).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders the link with the correct href and sensible default text', () => {
|
||||||
|
render(<GovernanceLink />);
|
||||||
|
const linkElement = screen.getByText('Governance');
|
||||||
|
expect(linkElement).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,18 @@
|
|||||||
|
import { ExternalLink } from '@vegaprotocol/ui-toolkit';
|
||||||
|
import { ENV } from '../../../config/env';
|
||||||
|
import { t } from '@vegaprotocol/i18n';
|
||||||
|
|
||||||
|
export type GovernanceLinkProps = {
|
||||||
|
text?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Just a link to the governance page, with optional text
|
||||||
|
*/
|
||||||
|
const GovernanceLink = ({ text = t('Governance') }: GovernanceLinkProps) => {
|
||||||
|
const base = ENV.dataSources.governanceUrl;
|
||||||
|
|
||||||
|
return <ExternalLink href={base}>{text}</ExternalLink>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default GovernanceLink;
|
@ -10,6 +10,8 @@ import { ChainResponseCode } from '../chain-response-code/chain-reponse.code';
|
|||||||
import { TxDataView } from '../../tx-data-view';
|
import { TxDataView } from '../../tx-data-view';
|
||||||
import Hash from '../../../links/hash';
|
import Hash from '../../../links/hash';
|
||||||
import { Signature } from '../../../signature/signature';
|
import { Signature } from '../../../signature/signature';
|
||||||
|
import { useExplorerEpochForBlockQuery } from '../../../links/block-link/__generated__/EpochByBlock';
|
||||||
|
import EpochOverview from '../../../epoch-overview/epoch';
|
||||||
|
|
||||||
interface TxDetailsSharedProps {
|
interface TxDetailsSharedProps {
|
||||||
txData: BlockExplorerTransactionResult | undefined;
|
txData: BlockExplorerTransactionResult | undefined;
|
||||||
@ -44,6 +46,11 @@ export const TxDetailsShared = ({
|
|||||||
blockData,
|
blockData,
|
||||||
hideTypeRow = false,
|
hideTypeRow = false,
|
||||||
}: TxDetailsSharedProps) => {
|
}: TxDetailsSharedProps) => {
|
||||||
|
const { data } = useExplorerEpochForBlockQuery({
|
||||||
|
errorPolicy: 'ignore',
|
||||||
|
variables: { block: txData?.block.toString() || '' },
|
||||||
|
});
|
||||||
|
|
||||||
if (!txData) {
|
if (!txData) {
|
||||||
return <>{t('Awaiting Block Explorer transaction details')}</>;
|
return <>{t('Awaiting Block Explorer transaction details')}</>;
|
||||||
}
|
}
|
||||||
@ -74,7 +81,7 @@ export const TxDetailsShared = ({
|
|||||||
<TableRow modifier="bordered">
|
<TableRow modifier="bordered">
|
||||||
<TableCell {...sharedHeaderProps}>{t('Block')}</TableCell>
|
<TableCell {...sharedHeaderProps}>{t('Block')}</TableCell>
|
||||||
<TableCell>
|
<TableCell>
|
||||||
<BlockLink height={height} />
|
<BlockLink height={height} showEpoch={false} />
|
||||||
</TableCell>
|
</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
<TableRow modifier="bordered">
|
<TableRow modifier="bordered">
|
||||||
@ -83,6 +90,7 @@ export const TxDetailsShared = ({
|
|||||||
<Signature signature={txData.signature} />
|
<Signature signature={txData.signature} />
|
||||||
</TableCell>
|
</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
|
|
||||||
<TableRow modifier="bordered">
|
<TableRow modifier="bordered">
|
||||||
<TableCell {...sharedHeaderProps}>{t('Time')}</TableCell>
|
<TableCell {...sharedHeaderProps}>{t('Time')}</TableCell>
|
||||||
<TableCell>
|
<TableCell>
|
||||||
@ -100,6 +108,14 @@ export const TxDetailsShared = ({
|
|||||||
)}
|
)}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
|
{data && data.epoch && (
|
||||||
|
<TableRow modifier="bordered">
|
||||||
|
<TableCell scope="row">{t('Epoch')}</TableCell>
|
||||||
|
<TableCell modifier="bordered">
|
||||||
|
<EpochOverview id={data.epoch.id} icon={false} />
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
)}
|
||||||
<TableRow modifier="bordered">
|
<TableRow modifier="bordered">
|
||||||
<TableCell {...sharedHeaderProps}>{t('Response code')}</TableCell>
|
<TableCell {...sharedHeaderProps}>{t('Response code')}</TableCell>
|
||||||
<TableCell>
|
<TableCell>
|
||||||
|
@ -113,13 +113,16 @@ export const TxDetailsTransfer = ({
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets a string description of this transfer
|
* Gets a string description of this transfer
|
||||||
* @param txData A full transfer
|
* @param tx A full transfer
|
||||||
* @returns string Transfer label
|
* @returns string Transfer label
|
||||||
*/
|
*/
|
||||||
export function getTypeLabelForTransfer(tx: Transfer) {
|
export function getTypeLabelForTransfer(tx: Transfer) {
|
||||||
if (tx.to === SPECIAL_CASE_NETWORK || tx.to === SPECIAL_CASE_NETWORK_ID) {
|
if (tx.to === SPECIAL_CASE_NETWORK || tx.to === SPECIAL_CASE_NETWORK_ID) {
|
||||||
|
if (tx.toAccountType === 'ACCOUNT_TYPE_NETWORK_TREASURY') {
|
||||||
|
return 'Treasury transfer';
|
||||||
|
}
|
||||||
if (tx.recurring && tx.recurring.dispatchStrategy) {
|
if (tx.recurring && tx.recurring.dispatchStrategy) {
|
||||||
return 'Reward top up transfer';
|
return 'Reward transfer';
|
||||||
}
|
}
|
||||||
// Else: we don't know that it's a reward transfer, so let's not guess
|
// Else: we don't know that it's a reward transfer, so let's not guess
|
||||||
} else if (tx.recurring) {
|
} else if (tx.recurring) {
|
||||||
|
@ -2,6 +2,7 @@ import { t } from '@vegaprotocol/i18n';
|
|||||||
import type { components } from '../../../types/explorer';
|
import type { components } from '../../../types/explorer';
|
||||||
import { VoteIcon } from '../vote-icon/vote-icon';
|
import { VoteIcon } from '../vote-icon/vote-icon';
|
||||||
import { ExternalChainIcon } from '../links/external-explorer-link/external-chain-icon';
|
import { ExternalChainIcon } from '../links/external-explorer-link/external-chain-icon';
|
||||||
|
import { getTypeLabelForTransfer } from './details/tx-transfer';
|
||||||
|
|
||||||
interface TxOrderTypeProps {
|
interface TxOrderTypeProps {
|
||||||
orderType: string;
|
orderType: string;
|
||||||
@ -95,7 +96,7 @@ export function getLabelForOrderType(
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Given a proposal, will return a specific label
|
* Given a proposal, will return a specific label
|
||||||
* @param chainEvent
|
* @param proposal
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export function getLabelForProposal(
|
export function getLabelForProposal(
|
||||||
@ -142,6 +143,36 @@ export function getLabelForProposal(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type label = {
|
||||||
|
type: string;
|
||||||
|
colours: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function getLabelForTransfer(
|
||||||
|
transfer: components['schemas']['commandsv1Transfer']
|
||||||
|
): label {
|
||||||
|
const type = getTypeLabelForTransfer(transfer);
|
||||||
|
|
||||||
|
if (transfer.toAccountType === 'ACCOUNT_TYPE_NETWORK_TREASURY') {
|
||||||
|
return {
|
||||||
|
type,
|
||||||
|
colours:
|
||||||
|
'text-vega-green dark:text-green bg-vega-dark-150 dark:bg-vega-dark-250',
|
||||||
|
};
|
||||||
|
} else if (transfer.recurring) {
|
||||||
|
return {
|
||||||
|
type,
|
||||||
|
colours:
|
||||||
|
'text-vega-yellow dark:text-yellow bg-vega-dark-150 dark:bg-vega-dark-250',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
type,
|
||||||
|
colours:
|
||||||
|
'text-white dark:text-white bg-vega-dark-150 dark:bg-vega-dark-250',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Given a chain event, will try to provide a more useful label
|
* Given a chain event, will try to provide a more useful label
|
||||||
* @param chainEvent
|
* @param chainEvent
|
||||||
@ -225,9 +256,10 @@ export const TxOrderType = ({ orderType, command }: TxOrderTypeProps) => {
|
|||||||
if (type === 'Chain Event' && !!command?.chainEvent) {
|
if (type === 'Chain Event' && !!command?.chainEvent) {
|
||||||
type = getLabelForChainEvent(command.chainEvent);
|
type = getLabelForChainEvent(command.chainEvent);
|
||||||
colours = 'text-white dark-text-white bg-vega-pink dark:bg-vega-pink';
|
colours = 'text-white dark-text-white bg-vega-pink dark:bg-vega-pink';
|
||||||
} else if (type === 'Validator Heartbeat') {
|
} else if (type === 'Transfer Funds' && command?.transfer) {
|
||||||
colours =
|
const res = getLabelForTransfer(command.transfer);
|
||||||
'text-white dark-text-white bg-vega-light-200 dark:bg-vega-dark-100';
|
type = res.type;
|
||||||
|
colours = res.colours;
|
||||||
} else if (type === 'Proposal' || type === 'Governance Proposal') {
|
} else if (type === 'Proposal' || type === 'Governance Proposal') {
|
||||||
if (command && !!command.proposalSubmission) {
|
if (command && !!command.proposalSubmission) {
|
||||||
type = getLabelForProposal(command.proposalSubmission);
|
type = getLabelForProposal(command.proposalSubmission);
|
||||||
|
@ -16,6 +16,8 @@ import { useBlockInfo } from '@vegaprotocol/tendermint';
|
|||||||
import { NodeLink } from '../../../components/links';
|
import { NodeLink } from '../../../components/links';
|
||||||
import { useDocumentTitle } from '../../../hooks/use-document-title';
|
import { useDocumentTitle } from '../../../hooks/use-document-title';
|
||||||
import EmptyList from '../../../components/empty-list/empty-list';
|
import EmptyList from '../../../components/empty-list/empty-list';
|
||||||
|
import { useExplorerEpochForBlockQuery } from '../../../components/links/block-link/__generated__/EpochByBlock';
|
||||||
|
import EpochOverview from '../../../components/epoch-overview/epoch';
|
||||||
|
|
||||||
type Params = { block: string };
|
type Params = { block: string };
|
||||||
|
|
||||||
@ -26,6 +28,11 @@ const Block = () => {
|
|||||||
state: { data: blockData, loading, error },
|
state: { data: blockData, loading, error },
|
||||||
} = useBlockInfo(Number(block));
|
} = useBlockInfo(Number(block));
|
||||||
|
|
||||||
|
const { data } = useExplorerEpochForBlockQuery({
|
||||||
|
errorPolicy: 'ignore',
|
||||||
|
variables: { block: block?.toString() || '' },
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section>
|
<section>
|
||||||
<RouteTitle data-testid="block-header">{t(`BLOCK ${block}`)}</RouteTitle>
|
<RouteTitle data-testid="block-header">{t(`BLOCK ${block}`)}</RouteTitle>
|
||||||
@ -75,6 +82,7 @@ const Block = () => {
|
|||||||
<code>{blockData.result.block.header.consensus_hash}</code>
|
<code>{blockData.result.block.header.consensus_hash}</code>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
|
|
||||||
<TableRow modifier="bordered">
|
<TableRow modifier="bordered">
|
||||||
<TableHeader scope="row">Mined by</TableHeader>
|
<TableHeader scope="row">Mined by</TableHeader>
|
||||||
<TableCell modifier="bordered">
|
<TableCell modifier="bordered">
|
||||||
@ -97,6 +105,14 @@ const Block = () => {
|
|||||||
)}
|
)}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
|
{data && data.epoch && (
|
||||||
|
<TableRow modifier="bordered">
|
||||||
|
<TableCell scope="row">{t('Epoch')}</TableCell>
|
||||||
|
<TableCell modifier="bordered">
|
||||||
|
<EpochOverview id={data.epoch.id} icon={false} />
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
)}
|
||||||
<TableRow modifier="bordered">
|
<TableRow modifier="bordered">
|
||||||
<TableHeader scope="row">Transactions</TableHeader>
|
<TableHeader scope="row">Transactions</TableHeader>
|
||||||
<TableCell modifier="bordered">
|
<TableCell modifier="bordered">
|
||||||
|
@ -33,7 +33,10 @@ export const NetworkAccountsTable = () => {
|
|||||||
return (
|
return (
|
||||||
<section className="md:flex md:flex-row flex-wrap">
|
<section className="md:flex md:flex-row flex-wrap">
|
||||||
{c.map((a) => (
|
{c.map((a) => (
|
||||||
<div className="basis-1/2 md:basis-1/4">
|
<div
|
||||||
|
className="basis-1/2 md:basis-1/4"
|
||||||
|
key={`${a.assetId}-${a.balance}`}
|
||||||
|
>
|
||||||
<div className="bg-white rounded overflow-hidden shadow-lg dark:bg-black dark:border-slate-500 dark:border">
|
<div className="bg-white rounded overflow-hidden shadow-lg dark:bg-black dark:border-slate-500 dark:border">
|
||||||
<div className="text-center p-6 bg-gray-100 dark:bg-slate-900 border-b dark:border-slate-500">
|
<div className="text-center p-6 bg-gray-100 dark:bg-slate-900 border-b dark:border-slate-500">
|
||||||
<p className="flex justify-center">
|
<p className="flex justify-center">
|
||||||
|
@ -16,19 +16,21 @@ import type { DeepPartial } from '@apollo/client/utilities';
|
|||||||
|
|
||||||
describe('typeLabel', () => {
|
describe('typeLabel', () => {
|
||||||
it('should return "Transfer" for "OneOffTransfer" kind', () => {
|
it('should return "Transfer" for "OneOffTransfer" kind', () => {
|
||||||
expect(typeLabel('OneOffTransfer')).toBe('Transfer');
|
expect(typeLabel('OneOffTransfer')).toBe('Transfer - one time');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return "Transfer" for "RecurringTransfer" kind', () => {
|
it('should return "Transfer" for "RecurringTransfer" kind', () => {
|
||||||
expect(typeLabel('RecurringTransfer')).toBe('Transfer');
|
expect(typeLabel('RecurringTransfer')).toBe('Transfer - repeating');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return "Governance" for "OneOffGovernanceTransfer" kind', () => {
|
it('should return "Governance" for "OneOffGovernanceTransfer" kind', () => {
|
||||||
expect(typeLabel('OneOffGovernanceTransfer')).toBe('Governance');
|
expect(typeLabel('OneOffGovernanceTransfer')).toBe('Governance - one time');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return "Governance" for "RecurringGovernanceTransfer" kind', () => {
|
it('should return "Governance" for "RecurringGovernanceTransfer" kind', () => {
|
||||||
expect(typeLabel('RecurringGovernanceTransfer')).toBe('Governance');
|
expect(typeLabel('RecurringGovernanceTransfer')).toBe(
|
||||||
|
'Governance - repeating'
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return "Unknown" for unknown kind', () => {
|
it('should return "Unknown" for unknown kind', () => {
|
||||||
@ -256,7 +258,7 @@ describe('NetworkTransfersTable', () => {
|
|||||||
expect(screen.getByTestId('from-account').textContent).toEqual('Treasury');
|
expect(screen.getByTestId('from-account').textContent).toEqual('Treasury');
|
||||||
expect(screen.getByTestId('to-account').textContent).toEqual('7100…97a0');
|
expect(screen.getByTestId('to-account').textContent).toEqual('7100…97a0');
|
||||||
expect(screen.getByTestId('transfer-kind').textContent).toEqual(
|
expect(screen.getByTestId('transfer-kind').textContent).toEqual(
|
||||||
'Governance'
|
'Governance - one time'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -12,6 +12,7 @@ import { t } from '@vegaprotocol/i18n';
|
|||||||
import { IconNames } from '@blueprintjs/icons';
|
import { IconNames } from '@blueprintjs/icons';
|
||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
import { useScreenDimensions } from '@vegaprotocol/react-helpers';
|
import { useScreenDimensions } from '@vegaprotocol/react-helpers';
|
||||||
|
import ProposalLink from '../../../components/links/proposal-link/proposal-link';
|
||||||
|
|
||||||
export const colours = {
|
export const colours = {
|
||||||
INCOMING: '!fill-vega-green-600 text-vega-green-600 mr-2',
|
INCOMING: '!fill-vega-green-600 text-vega-green-600 mr-2',
|
||||||
@ -50,14 +51,24 @@ export function getToAccountTypeLabel(type?: AccountType): string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function isGovernanceTransfer(kind?: string): boolean {
|
||||||
|
if (kind && kind.includes('Governance')) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
export function typeLabel(kind?: string): string {
|
export function typeLabel(kind?: string): string {
|
||||||
switch (kind) {
|
switch (kind) {
|
||||||
case 'OneOffTransfer':
|
case 'OneOffTransfer':
|
||||||
|
return t('Transfer - one time');
|
||||||
case 'RecurringTransfer':
|
case 'RecurringTransfer':
|
||||||
return t('Transfer');
|
return t('Transfer - repeating');
|
||||||
case 'OneOffGovernanceTransfer':
|
case 'OneOffGovernanceTransfer':
|
||||||
|
return t('Governance - one time');
|
||||||
case 'RecurringGovernanceTransfer':
|
case 'RecurringGovernanceTransfer':
|
||||||
return t('Governance');
|
return t('Governance - repeating');
|
||||||
default:
|
default:
|
||||||
return t('Unknown');
|
return t('Unknown');
|
||||||
}
|
}
|
||||||
@ -239,6 +250,11 @@ export const NetworkTransfersTable = () => {
|
|||||||
>
|
>
|
||||||
{a && typeLabel(a.kind.__typename)}
|
{a && typeLabel(a.kind.__typename)}
|
||||||
</span>
|
</span>
|
||||||
|
{isGovernanceTransfer(a?.kind.__typename) && a?.id && (
|
||||||
|
<span className="ml-4">
|
||||||
|
<ProposalLink id={a?.id} text="View" />
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
);
|
);
|
||||||
|
@ -4,6 +4,7 @@ import { t } from '@vegaprotocol/i18n';
|
|||||||
import { RouteTitle } from '../../components/route-title';
|
import { RouteTitle } from '../../components/route-title';
|
||||||
import { NetworkAccountsTable } from './components/network-accounts-table';
|
import { NetworkAccountsTable } from './components/network-accounts-table';
|
||||||
import { NetworkTransfersTable } from './components/network-transfers-table';
|
import { NetworkTransfersTable } from './components/network-transfers-table';
|
||||||
|
import GovernanceLink from '../../components/links/governance-link/governance-link';
|
||||||
|
|
||||||
export type NonZeroAccount = {
|
export type NonZeroAccount = {
|
||||||
assetId: string;
|
assetId: string;
|
||||||
@ -16,7 +17,33 @@ export const NetworkTreasury = () => {
|
|||||||
return (
|
return (
|
||||||
<section>
|
<section>
|
||||||
<RouteTitle data-testid="block-header">{t(`Treasury`)}</RouteTitle>
|
<RouteTitle data-testid="block-header">{t(`Treasury`)}</RouteTitle>
|
||||||
<div>
|
<details className="w-full md:w-3/5 cursor-pointer shadow-lg p-5 dark:border-l-2 dark:border-vega-green">
|
||||||
|
<summary>{t('About the Network Treasury')}</summary>
|
||||||
|
<section className="mt-4 b-1 border-grey">
|
||||||
|
<p className="mb-2">
|
||||||
|
The network treasury can hold funds from any active settlement asset
|
||||||
|
on the network. It is funded periodically by transfers from Gobalsky
|
||||||
|
as part of the Community Adoption Fund (CAF), but in future may
|
||||||
|
receive funds from any sources.
|
||||||
|
</p>
|
||||||
|
<p className="mb-2">
|
||||||
|
Funds in the network treasury can be used by creating governance
|
||||||
|
initiated transfers via{' '}
|
||||||
|
<GovernanceLink text={t('community governance')} />. These transfers
|
||||||
|
can be initiated by anyone and be used to fund reward pools, or can
|
||||||
|
be used to fund other activities the{' '}
|
||||||
|
<abbr className="decoration-dotted" title="Community Adoption Fund">
|
||||||
|
CAF
|
||||||
|
</abbr>{' '}
|
||||||
|
is exploring.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
This page shows details of the balances in the treasury, pending
|
||||||
|
transfers, and historic transfer movements to and from the treasury.
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
</details>
|
||||||
|
<div className="mt-6">
|
||||||
<NetworkAccountsTable />
|
<NetworkAccountsTable />
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-5">
|
<div className="mt-5">
|
||||||
|
Loading…
Reference in New Issue
Block a user