Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Explorer transfer table1 #3101

Merged
merged 16 commits into from
Jul 11, 2022
7 changes: 4 additions & 3 deletions explorer/client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,16 @@
},
"dependencies": {
"@mysten/sui.js": "file:../../sdk/typescript",
"@tanstack/react-table": "^8.1.4",
"bn.js": "^5.2.0",
"classnames": "^2.3.1",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-ga4": "^1.4.1",
"react-json-view": "^1.21.3",
"react-router-dom": "^6.2.1",
"vanilla-cookieconsent": "^2.8.0",
"web-vitals": "^2.1.4",
"react-json-view": "^1.21.3",
"bn.js": "^5.2.0"
"web-vitals": "^2.1.4"
},
"scripts": {
"start": "react-scripts start",
Expand Down
3 changes: 3 additions & 0 deletions explorer/client/src/assets/SVGIcons/failed.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions explorer/client/src/assets/SVGIcons/forward-arrow-dark.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions explorer/client/src/assets/SVGIcons/forward-arrow.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions explorer/client/src/assets/SVGIcons/success.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
10 changes: 9 additions & 1 deletion explorer/client/src/components/longtext/Longtext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import { useCallback, useState, useContext } from 'react';
import { Link, useNavigate } from 'react-router-dom';

import { ReactComponent as ContentForwardArrowDark } from '../../assets/SVGIcons/forward-arrow-dark.svg';
import { ReactComponent as ContentCopyIcon } from '../../assets/content_copy_black_18dp.svg';
import { NetworkContext } from '../../context';
import { navigateWithUnknown } from '../../utils/searchUtil';
Expand All @@ -17,6 +18,7 @@ function Longtext({
isLink = true,
alttext = '',
isCopyButton = true,
showIconButton = false,
}: {
text: string;
category:
Expand All @@ -28,6 +30,7 @@ function Longtext({
isLink?: boolean;
alttext?: string;
isCopyButton?: boolean;
showIconButton?: boolean;
}) {
const [isCopyIcon, setCopyIcon] = useState(true);

Expand All @@ -42,6 +45,7 @@ function Longtext({
}, [setCopyIcon, text]);

let icon;
let iconButton = <></>;

if (isCopyButton) {
if (pleaseWait) {
Expand All @@ -59,6 +63,10 @@ function Longtext({
icon = <></>;
}

if (showIconButton) {
iconButton = <ContentForwardArrowDark />;
}

const navigateUnknown = useCallback(() => {
setPleaseWait(true);
navigateWithUnknown(text, navigate, network).then(() =>
Expand Down Expand Up @@ -97,7 +105,7 @@ function Longtext({
className={styles.longtext}
to={`/${category}/${encodeURIComponent(text)}`}
>
{alttext ? alttext : text}
{alttext ? alttext : text} {iconButton}
</Link>
);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
.networkstats {
@apply bg-[#F7F8F8] p-5 font-[500] text-[#767A81] text-[13px] leading-4 uppercase grid md:grid-cols-2 grid-cols-1 gap-10;
}

.statsitem {
@apply flex justify-between items-center leading-6 text-left;
}

.stats {
@apply text-black text-[16px];
}
77 changes: 77 additions & 0 deletions explorer/client/src/components/network-stats/SuiNetworkStats.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
// Copyright (c) 2022, Mysten Labs, Inc.
// SPDX-License-Identifier: Apache-2.0

import { IS_STATIC_ENV } from '../../utils/envUtil';

import styles from './SuiNetworkStats.module.css';

//TODO add the backend service to get all Network stats data
function SuiNetworkCard({ count }: { count: number | string }) {
const totalStatsData = [
{
title: 'TOTAL Objects',
value: '372.5M',
},
{
title: 'TOTAL MODULES',
value: '153,510',
},
{
title: 'TOTAL BYTES STORED',
value: '2.591B',
},
{
title: 'TOTAL TRANSACTIONS',
value: count,
},
];

const currentStatsData = [
{
title: 'CURRENT SUI PRICE',
value: '$26.45',
},
{
title: 'Current Epoch',
value: '142,215',
},
{
title: 'CURRENT VALIDATORS',
value: '15,482',
},
{
title: 'CURRENT TPS',
value: '2,125',
},
];

return (
<div className={styles.networkstats}>
<div className={styles.statsitems}>
{totalStatsData.map((item, idx) => (
<div className={styles.statsitem} key={idx}>
{item.title}
<span className={styles.stats}>{item.value}</span>
</div>
))}
</div>
<div className={styles.statsitems}>
{currentStatsData.map((item, idx) => (
<div className={styles.statsitem} key={idx}>
{item.title}
<span className={styles.stats}>{item.value}</span>
</div>
))}
</div>
</div>
);
}

function SuiNetworkCardStatic() {
return <SuiNetworkCard count={3030} />;
}

const SuiNetworkStats = ({ count }: { count: number }) =>
IS_STATIC_ENV ? <SuiNetworkCardStatic /> : <SuiNetworkCard count={count} />;

export default SuiNetworkStats;
33 changes: 33 additions & 0 deletions explorer/client/src/components/table/TableCard.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
.content {
@apply container overflow-x-auto text-left text-[#4E555D];
}

.table {
@apply w-full text-sm text-left text-[#4E555D] overflow-x-auto min-w-max;

border-bottom: 1px solid #f0f1f2;
}

.table th {
@apply text-left font-normal uppercase text-[#767A81] text-xs;
}

.table td {
@apply pr-5 text-[#4E555D];
}

.table tbody tr {
@apply space-y-10 mt-10;
}

.addresses {
@apply flex flex-wrap;
}

.addresses svg {
@apply mr-1;
}

.tablespacing {
@apply pb-1 pt-1;
}
183 changes: 183 additions & 0 deletions explorer/client/src/components/table/TableCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
// Copyright (c) 2022, Mysten Labs, Inc.
// SPDX-License-Identifier: Apache-2.0
import {
flexRender,
getCoreRowModel,
useReactTable,
} from '@tanstack/react-table';
import { useMemo } from 'react';

import { ReactComponent as ContentFailedStatus } from '../../assets/SVGIcons/failed.svg';
import { ReactComponent as ContentForwardArrow } from '../../assets/SVGIcons/forward-arrow.svg';
import { ReactComponent as ContentSuccessStatus } from '../../assets/SVGIcons/success.svg';
import Longtext from '../../components/longtext/Longtext';

import type { ExecutionStatusType, TransactionKindName } from '@mysten/sui.js';

import styles from './TableCard.module.css';

type Category =
| 'objects'
| 'transactions'
| 'addresses'
| 'ethAddress'
| 'unknown';

type Link = {
url: string;
name?: string;
copy?: boolean;
category?: string;
isLink?: boolean;
};

type TableColumn = {
headerLabel: string;
accessorKey: string;
};
// TODO: update Link to use Tuple type
// type Links = [Link, Link?];
type Links = Link[];

type TxStatus = {
txTypeName: TransactionKindName | undefined;
status: ExecutionStatusType;
};

// support multiple types with special handling for 'addresses'/links and status
// TODO: Not sure to allow HTML elements in the table
type TxType = {
[key: string]:
| string
| number
| boolean
| Links
| React.ReactElement
| TxStatus;
};

function TxAddresses({ content }: { content: Link[] }) {
return (
<section className={styles.addresses}>
{content.map((itm, idx) => (
<div key={idx + itm.url}>
<Longtext
text={itm.url}
alttext={itm.name}
category={(itm.category as Category) || 'unknown'}
isLink={itm?.isLink}
isCopyButton={itm?.copy}
/>
{idx !== content.length - 1 && <ContentForwardArrow />}
</div>
))}
</section>
);
}

function TxStatusType({ content }: { content: TxStatus }) {
return (
<>
{content.status === 'success' ? (
<ContentSuccessStatus />
) : (
<ContentFailedStatus />
)}{' '}
{content.txTypeName}
</>
);
}

function columnsContent(columns: TableColumn[]) {
return columns.map((column) => ({
accessorKey: column.accessorKey,
id: column.accessorKey,
header: column.headerLabel,
// cell renderer for each column from react-table
cell: (info: any) => {
const content = info.getValue();

// handle multiple links in one cell
if (Array.isArray(content)) {
return <TxAddresses content={content} />;
}
// Special case for txTypes and status
if (
typeof content === 'object' &&
content !== null &&
content.txTypeName
) {
return <TxStatusType content={content} />;
}
// handle most common types including
return content;
},
}));
}

function TableCard({
Jibz1 marked this conversation as resolved.
Show resolved Hide resolved
tabledata,
}: {
tabledata: {
data: TxType[];
columns: TableColumn[];
};
}) {
const data = useMemo(() => tabledata.data, [tabledata.data]);
// Use Columns to create a table
const columns = useMemo(
() => columnsContent(tabledata.columns),
[tabledata.columns]
);
const table = useReactTable({
data,
columns,
getCoreRowModel: getCoreRowModel(),
});

return (
<div className={styles.content}>
<table className={styles.table}>
<thead>
{table.getHeaderGroups().map((headerGroup) => (
<tr key={headerGroup.id}>
{headerGroup.headers.map((header) => (
<th
key={header.id}
colSpan={header.colSpan}
scope="col"
>
{header.isPlaceholder
? null
: flexRender(
header.column.columnDef.header,
header.getContext()
)}
</th>
))}
</tr>
))}
</thead>
<tbody>
{table.getRowModel().rows.map((row) => (
<tr key={row.id}>
{row.getVisibleCells().map((cell) => (
<td
key={cell.id}
className={styles.tablespacing}
>
{flexRender(
cell.column.columnDef.cell,
cell.getContext()
)}
</td>
))}
</tr>
))}
</tbody>
</table>
</div>
);
}

export default TableCard;
Loading