Feat/1501 improve party details (#1785)
* feat(explorer): change styling party details page * feat(explorer): use panel component in txs-stats-info.tsx * feat(explorer): change order for party details sections
This commit is contained in:
parent
70eeeeb1e2
commit
1964d42e5b
@ -1,4 +1,4 @@
|
||||
import React from 'react';
|
||||
import React, { useMemo } from 'react';
|
||||
import type { ReactNode } from 'react';
|
||||
import { Panel } from '../panel';
|
||||
import {
|
||||
@ -8,6 +8,8 @@ import {
|
||||
Lozenge,
|
||||
Tooltip,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import { TruncateInline } from '../truncate/truncate';
|
||||
import { useScreenDimensions } from '@vegaprotocol/react-helpers';
|
||||
|
||||
interface InfoPanelProps {
|
||||
children?: ReactNode | ReactNode[];
|
||||
@ -24,32 +26,56 @@ export const InfoPanel = ({
|
||||
type,
|
||||
copy = true,
|
||||
}: InfoPanelProps) => {
|
||||
const { screenSize } = useScreenDimensions();
|
||||
const isTruncated = useMemo(
|
||||
() => ['xs', 'sm', 'md', 'lg'].includes(screenSize),
|
||||
[screenSize]
|
||||
);
|
||||
const isDesktop = useMemo(() => ['lg'].includes(screenSize), [screenSize]);
|
||||
const visibleChars = useMemo(() => (isDesktop ? 20 : 10), [isDesktop]);
|
||||
return (
|
||||
<Panel>
|
||||
<section className="flex gap-3 mb-1 items-center">
|
||||
<h3 className="text-lg font-bold">{title}</h3>
|
||||
<p title={id} className="truncate ...">
|
||||
{id}
|
||||
</p>
|
||||
{type && (
|
||||
<Lozenge
|
||||
className="text-xs leading-relaxed cursor-auto"
|
||||
variant={Intent.None}
|
||||
>
|
||||
<Tooltip side="top" description={type} align="center">
|
||||
<span>{type}</span>
|
||||
</Tooltip>
|
||||
</Lozenge>
|
||||
)}
|
||||
<section className="flex justify-between items-center items-center">
|
||||
<div>
|
||||
<div className="flex gap-3 items-center">
|
||||
<h3 className="text-lg font-medium">{title}</h3>
|
||||
{isTruncated ? (
|
||||
<TruncateInline
|
||||
text={id}
|
||||
startChars={visibleChars}
|
||||
endChars={visibleChars}
|
||||
className="text-black dark:text-zinc-200"
|
||||
/>
|
||||
) : (
|
||||
<p
|
||||
title={id}
|
||||
className="text-black dark:text-zinc-200 truncate ..."
|
||||
>
|
||||
{id}
|
||||
</p>
|
||||
)}
|
||||
|
||||
{type && (
|
||||
<Lozenge
|
||||
className="text-xs leading-relaxed cursor-auto"
|
||||
variant={Intent.None}
|
||||
>
|
||||
<Tooltip side="top" description={type} align="center">
|
||||
<span>{type}</span>
|
||||
</Tooltip>
|
||||
</Lozenge>
|
||||
)}
|
||||
</div>
|
||||
<div>{children}</div>
|
||||
</div>
|
||||
{copy && (
|
||||
<CopyWithTooltip text={id}>
|
||||
<button className="underline">
|
||||
<Icon name="duplicate" className="ml-2" />
|
||||
<button className="bg-zinc-100 dark:bg-zinc-900 rounded-sm py-2 px-3">
|
||||
<Icon name="duplicate" />
|
||||
</button>
|
||||
</CopyWithTooltip>
|
||||
)}
|
||||
</section>
|
||||
{children}
|
||||
</Panel>
|
||||
);
|
||||
};
|
||||
|
@ -6,5 +6,12 @@ interface PanelProps {
|
||||
className?: string;
|
||||
}
|
||||
export const Panel = ({ children, className }: PanelProps) => (
|
||||
<div className={classNames('border p-5 mb-5', className)}>{children}</div>
|
||||
<div
|
||||
className={classNames(
|
||||
'border border-zinc-200 dark:border-zinc-800 rounded-md p-5 mb-5',
|
||||
className
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
|
@ -2,8 +2,8 @@ import { gql, useQuery } from '@apollo/client';
|
||||
import { t } from '@vegaprotocol/react-helpers';
|
||||
import { useEffect } from 'react';
|
||||
import { InfoBlock } from '../../components/info-block';
|
||||
import { Panel } from '../../components/panel';
|
||||
import type { TxsStats, TxsStats_statistics } from './__generated__/TxsStats';
|
||||
import classNames from 'classnames';
|
||||
|
||||
const STATS_QUERY = gql`
|
||||
query TxsStats {
|
||||
@ -63,17 +63,18 @@ export const TxsStatsInfo = ({ className }: TxsStatsInfoProps) => {
|
||||
|
||||
const gridStyles =
|
||||
'grid grid-rows-2 gap-4 grid-cols-2 xl:gap-8 xl:grid-rows-1 xl:grid-cols-4';
|
||||
const containerStyles = 'p-5 border rounded-lg border-zinc-500';
|
||||
|
||||
return (
|
||||
<aside className={classNames(gridStyles, containerStyles, className)}>
|
||||
{TXS_STATS_MAP.map((field) => (
|
||||
<InfoBlock
|
||||
subtitle={field.label}
|
||||
tooltipInfo={field.info}
|
||||
title={data?.statistics[field.field] || ''}
|
||||
/>
|
||||
))}
|
||||
</aside>
|
||||
<Panel className={className}>
|
||||
<section className={gridStyles}>
|
||||
{TXS_STATS_MAP.map((field) => (
|
||||
<InfoBlock
|
||||
subtitle={field.label}
|
||||
tooltipInfo={field.info}
|
||||
title={data?.statistics[field.field] || ''}
|
||||
/>
|
||||
))}
|
||||
</section>
|
||||
</Panel>
|
||||
);
|
||||
};
|
||||
|
@ -4,16 +4,21 @@ import {
|
||||
t,
|
||||
useFetch,
|
||||
addDecimalsFormatNumber,
|
||||
useScreenDimensions,
|
||||
} from '@vegaprotocol/react-helpers';
|
||||
import { AccountTypeMapping } from '@vegaprotocol/types';
|
||||
import React from 'react';
|
||||
import React, { useMemo } from 'react';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { RouteTitle } from '../../../components/route-title';
|
||||
import { SubHeading } from '../../../components/sub-heading';
|
||||
import { AsyncRenderer, SyntaxHighlighter } from '@vegaprotocol/ui-toolkit';
|
||||
import {
|
||||
CopyWithTooltip,
|
||||
Icon,
|
||||
SyntaxHighlighter,
|
||||
AsyncRenderer,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import { Panel } from '../../../components/panel';
|
||||
import { InfoPanel } from '../../../components/info-panel';
|
||||
import { toNonHex } from '../../../components/search/detect-search';
|
||||
import { TruncateInline } from '../../../components/truncate/truncate';
|
||||
import { DATA_SOURCES } from '../../../config';
|
||||
import type {
|
||||
PartyAssetsQuery,
|
||||
@ -61,6 +66,8 @@ const PARTY_ASSETS_QUERY = gql`
|
||||
const Party = () => {
|
||||
const { party } = useParams<{ party: string }>();
|
||||
const partyId = party ? toNonHex(party) : '';
|
||||
const { isMobile } = useScreenDimensions();
|
||||
const visibleChars = useMemo(() => (isMobile ? 10 : 14), [isMobile]);
|
||||
const filters = `filters[tx.submitter]=${partyId}`;
|
||||
const { hasMoreTxs, loadTxs, error, txsData, loading } = useTxsData({
|
||||
limit: 10,
|
||||
@ -84,11 +91,19 @@ const Party = () => {
|
||||
);
|
||||
|
||||
const header = data?.party?.id ? (
|
||||
<InfoPanel
|
||||
title={t('Address')}
|
||||
id={data.party.id}
|
||||
type={data.party.__typename}
|
||||
/>
|
||||
<header className="flex items-center gap-x-4">
|
||||
<TruncateInline
|
||||
text={data.party.id}
|
||||
startChars={visibleChars}
|
||||
endChars={visibleChars}
|
||||
className="text-4xl xl:text-5xl uppercase font-alpha"
|
||||
/>
|
||||
<CopyWithTooltip text={data.party.id}>
|
||||
<button className="bg-zinc-100 dark:bg-zinc-900 rounded-sm py-2 px-3">
|
||||
<Icon name="duplicate" className="" />
|
||||
</button>
|
||||
</CopyWithTooltip>
|
||||
</header>
|
||||
) : (
|
||||
<Panel>
|
||||
<p>No party found for key {party}</p>
|
||||
@ -100,17 +115,13 @@ const Party = () => {
|
||||
{data?.party?.accounts?.length ? (
|
||||
data.party.accounts.map((account) => {
|
||||
return (
|
||||
<InfoPanel
|
||||
title={account.asset.name}
|
||||
id={account.asset.id}
|
||||
type={`Account Type - ${AccountTypeMapping[account.type]}`}
|
||||
>
|
||||
<InfoPanel title={account.asset.name} id={account.asset.id}>
|
||||
<section>
|
||||
<dl className="flex gap-2 font-mono">
|
||||
<dt className="font-bold">
|
||||
<dl className="flex gap-2">
|
||||
<dt className="text-zinc-500 dark:text-zinc-400 text-md">
|
||||
{t('Balance')} ({account.asset.symbol})
|
||||
</dt>
|
||||
<dd>
|
||||
<dd className="text-md">
|
||||
{addDecimalsFormatNumber(
|
||||
account.balance,
|
||||
account.asset.decimals
|
||||
@ -135,7 +146,6 @@ const Party = () => {
|
||||
<InfoPanel
|
||||
title={t('Current Stake Available')}
|
||||
id={data?.party?.stake?.currentStakeAvailable}
|
||||
type={data?.party?.stake.__typename}
|
||||
copy={false}
|
||||
/>
|
||||
) : (
|
||||
@ -148,7 +158,12 @@ const Party = () => {
|
||||
|
||||
return (
|
||||
<section>
|
||||
<RouteTitle data-testid="parties-header">{t('Party')}</RouteTitle>
|
||||
<h1
|
||||
className="font-alpha uppercase font-xl mb-4 text-zinc-800 dark:text-zinc-200"
|
||||
data-testid="parties-header"
|
||||
>
|
||||
{t('Party')}
|
||||
</h1>
|
||||
{data ? (
|
||||
<>
|
||||
{header}
|
||||
@ -156,12 +171,16 @@ const Party = () => {
|
||||
{accounts}
|
||||
<SubHeading>{t('Staking')}</SubHeading>
|
||||
{staking}
|
||||
<SubHeading>{t('Transactions')}</SubHeading>
|
||||
<SubHeading>{t('JSON')}</SubHeading>
|
||||
<section data-testid="parties-json">
|
||||
<SyntaxHighlighter data={data} />
|
||||
</section>
|
||||
<AsyncRenderer
|
||||
loading={loading as boolean}
|
||||
error={error}
|
||||
data={txsData}
|
||||
>
|
||||
<SubHeading>{t('Transactions')}</SubHeading>
|
||||
<TxsInfiniteList
|
||||
hasMoreTxs={hasMoreTxs}
|
||||
areTxsLoading={loading}
|
||||
@ -171,10 +190,6 @@ const Party = () => {
|
||||
className="mb-28"
|
||||
/>
|
||||
</AsyncRenderer>
|
||||
<SubHeading>{t('JSON')}</SubHeading>
|
||||
<section data-testid="parties-json">
|
||||
<SyntaxHighlighter data={data} />
|
||||
</section>
|
||||
</>
|
||||
) : null}
|
||||
|
||||
|
@ -13,7 +13,7 @@ export const TxsList = () => {
|
||||
<section className="md:p-2 lg:p-4 xl:p-6">
|
||||
<RouteTitle>{t('Transactions')}</RouteTitle>
|
||||
<BlocksRefetch refetch={refreshTxs} />
|
||||
<TxsStatsInfo className="my-8 py-8" />
|
||||
<TxsStatsInfo className="!my-12 py-8" />
|
||||
<TxsInfiniteList
|
||||
hasMoreTxs={hasMoreTxs}
|
||||
areTxsLoading={loading}
|
||||
|
Loading…
Reference in New Issue
Block a user