React tables migration (#540)

* feat: migration ClosedChannels and ChannelTable to tanstack react-table

* feat: create ColumnConfigurations to encapsulate functionality

* feat: hook up local storage to channel configurations

* feat: finish migrating all tables in codebase to tanstack

* feat: update package.json, remove dependency, and delete react-table

* fix: migrate missing table

* fix: remove default pagination

---------

Co-authored-by: Anthony Potdevin <31413433+apotdevin@users.noreply.github.com>
This commit is contained in:
AmbossKeegan 2023-07-14 00:03:30 -03:00 committed by GitHub
parent 8a55db4cb4
commit ec2857b11b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 692 additions and 760 deletions

38
package-lock.json generated
View file

@ -74,7 +74,6 @@
"react-select": "^5.7.3",
"react-slider": "^2.0.4",
"react-spinners": "^0.13.8",
"react-table": "^7.8.0",
"react-toastify": "^9.1.3",
"react-tooltip": "^5.13.1",
"reflect-metadata": "^0.1.13",
@ -122,7 +121,6 @@
"@types/react-grid-layout": "^1.3.2",
"@types/react-qr-reader": "^2.1.4",
"@types/react-slider": "^1.3.1",
"@types/react-table": "^7.7.14",
"@types/secp256k1": "^4.0.3",
"@types/styled-components": "^5.1.26",
"@types/styled-react-modal": "^1.2.2",
@ -6911,15 +6909,6 @@
"@types/react": "*"
}
},
"node_modules/@types/react-table": {
"version": "7.7.14",
"resolved": "https://registry.npmjs.org/@types/react-table/-/react-table-7.7.14.tgz",
"integrity": "sha512-TYrv7onCiakaG1uAu/UpQ9FojNEt/4/ht87EgJQaEGFoWV606ZLWUZAcUHzMxgc3v1mywP1cDyz3qB4ho3hWOw==",
"dev": true,
"dependencies": {
"@types/react": "*"
}
},
"node_modules/@types/react-transition-group": {
"version": "4.4.4",
"resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.4.tgz",
@ -19186,18 +19175,6 @@
"react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0"
}
},
"node_modules/react-table": {
"version": "7.8.0",
"resolved": "https://registry.npmjs.org/react-table/-/react-table-7.8.0.tgz",
"integrity": "sha512-hNaz4ygkZO4bESeFfnfOft73iBUj8K5oKi1EcSHPAibEydfsX2MyU6Z8KCr3mv3C9Kqqh71U+DhZkFvibbnPbA==",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/tannerlinsley"
},
"peerDependencies": {
"react": "^16.8.3 || ^17.0.0-0 || ^18.0.0"
}
},
"node_modules/react-toastify": {
"version": "9.1.3",
"resolved": "https://registry.npmjs.org/react-toastify/-/react-toastify-9.1.3.tgz",
@ -27798,15 +27775,6 @@
"@types/react": "*"
}
},
"@types/react-table": {
"version": "7.7.14",
"resolved": "https://registry.npmjs.org/@types/react-table/-/react-table-7.7.14.tgz",
"integrity": "sha512-TYrv7onCiakaG1uAu/UpQ9FojNEt/4/ht87EgJQaEGFoWV606ZLWUZAcUHzMxgc3v1mywP1cDyz3qB4ho3hWOw==",
"dev": true,
"requires": {
"@types/react": "*"
}
},
"@types/react-transition-group": {
"version": "4.4.4",
"resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.4.tgz",
@ -37264,12 +37232,6 @@
"integrity": "sha512-3e+k56lUkPj0vb5NDXPVFAOkPC//XyhKPJjvcGjyMNPWsBKpplfeyialP74G7H7+It7KzhtET+MvGqbKgAqpZA==",
"requires": {}
},
"react-table": {
"version": "7.8.0",
"resolved": "https://registry.npmjs.org/react-table/-/react-table-7.8.0.tgz",
"integrity": "sha512-hNaz4ygkZO4bESeFfnfOft73iBUj8K5oKi1EcSHPAibEydfsX2MyU6Z8KCr3mv3C9Kqqh71U+DhZkFvibbnPbA==",
"requires": {}
},
"react-toastify": {
"version": "9.1.3",
"resolved": "https://registry.npmjs.org/react-toastify/-/react-toastify-9.1.3.tgz",

View file

@ -99,7 +99,6 @@
"react-select": "^5.7.3",
"react-slider": "^2.0.4",
"react-spinners": "^0.13.8",
"react-table": "^7.8.0",
"react-toastify": "^9.1.3",
"react-tooltip": "^5.13.1",
"reflect-metadata": "^0.1.13",
@ -147,7 +146,6 @@
"@types/react-grid-layout": "^1.3.2",
"@types/react-qr-reader": "^2.1.4",
"@types/react-slider": "^1.3.1",
"@types/react-table": "^7.7.14",
"@types/secp256k1": "^4.0.3",
"@types/styled-components": "^5.1.26",
"@types/styled-react-modal": "^1.2.2",

View file

@ -12,8 +12,8 @@ import {
import { LoadingCard } from '../src/components/loading/LoadingCard';
import { AddPeer } from '../src/views/peers/AddPeer';
import { copyLink, getNodeLink } from '../src/components/generic/helpers';
import { Table } from '../src/components/table';
import { Price } from '../src/components/price/Price';
import Table from '../src/components/table';
const PeersView = () => {
const { loading, data } = useGetPeersQuery();
@ -32,27 +32,27 @@ const PeersView = () => {
const columns = useMemo(
() => [
{
Header: 'Peer',
accessor: 'alias',
Cell: ({ row }: any) => (
header: 'Peer',
accessorKey: 'alias',
cell: ({ row }: any) => (
<div style={{ whiteSpace: 'nowrap' }}>
{getNodeLink(row.original.public_key, row.original.alias)}
</div>
),
},
{
Header: 'Sync Peer',
accessor: 'is_sync_peer',
Cell: ({ row }: any) => (
header: 'Sync Peer',
accessorKey: 'is_sync_peer',
cell: ({ row }: any) => (
<div style={{ whiteSpace: 'nowrap' }}>
{row.original.is_sync_peer ? 'Yes' : '-'}
</div>
),
},
{
Header: 'Socket',
accessor: 'socket',
Cell: ({ row }: any) => (
header: 'Socket',
accessorKey: 'socket',
cell: ({ row }: any) => (
<div style={{ whiteSpace: 'nowrap' }}>
{row.original.socket.includes('.onion') ? 'Tor' : 'Clearnet'}
{copyLink(row.original.socket)}
@ -60,45 +60,45 @@ const PeersView = () => {
),
},
{
Header: 'Ping',
accessor: 'ping_time',
Cell: ({ row }: any) => (
header: 'Ping',
accessorKey: 'ping_time',
cell: ({ row }: any) => (
<div style={{ whiteSpace: 'nowrap' }}>
{`${row.original.ping_time} ms`}
</div>
),
},
{
Header: 'Sats Received',
accessor: 'tokens_received',
Cell: ({ row }: any) => (
header: 'Sats Received',
accessorKey: 'tokens_received',
cell: ({ row }: any) => (
<div style={{ whiteSpace: 'nowrap' }}>
<Price amount={row.original.tokens_received} />
</div>
),
},
{
Header: 'Sats Sent',
accessor: 'tokens_sent',
Cell: ({ row }: any) => (
header: 'Sats Sent',
accessorKey: 'tokens_sent',
cell: ({ row }: any) => (
<div style={{ whiteSpace: 'nowrap' }}>
<Price amount={row.original.tokens_sent} />
</div>
),
},
{
Header: 'Bytes Received',
accessor: 'bytes_received',
Cell: ({ row }: any) => (
header: 'Bytes Received',
accessorKey: 'bytes_received',
cell: ({ row }: any) => (
<div style={{ whiteSpace: 'nowrap' }}>
{`${Math.round(row.original.bytes_received * 0.0001) / 100} MB`}
</div>
),
},
{
Header: 'Bytes Sent',
accessor: 'bytes_sent',
Cell: ({ row }: any) => (
header: 'Bytes Sent',
accessorKey: 'bytes_sent',
cell: ({ row }: any) => (
<div style={{ whiteSpace: 'nowrap' }}>
{`${Math.round(row.original.bytes_sent * 0.0001) / 100} MB`}
</div>
@ -130,8 +130,10 @@ const PeersView = () => {
<Card mobileNoBackground={true}>
<Table
withBorder={true}
tableColumns={columns}
tableData={tableData}
columns={columns}
data={tableData}
withSorting={true}
withGlobalSort={true}
filterPlaceholder="peers"
/>
</Card>

View file

@ -1,215 +0,0 @@
import { useEffect, useState } from 'react';
import styled, { css } from 'styled-components';
import {
useReactTable,
getCoreRowModel,
getFilteredRowModel,
getPaginationRowModel,
getSortedRowModel,
flexRender,
ColumnDef,
SortingState,
} from '@tanstack/react-table';
import { Input } from '../input';
import { separationColor } from '../../../src/styles/Themes';
interface TableV2Props {
columns: ColumnDef<any, any>[];
data: any;
filterPlaceholder: string;
withBorder?: boolean;
alignCenter?: boolean;
fontSize?: string;
}
const FilterLine = styled.div`
margin-bottom: 24px;
`;
type StyledTableProps = {
withBorder?: boolean;
alignCenter?: boolean;
fontSize?: string;
};
const Styles = styled.div`
overflow-x: auto;
table {
border-spacing: 0;
tr {
:last-child {
td {
border-bottom: 0;
}
}
}
.cursor {
cursor: pointer;
}
,
th,
td {
font-size: ${({ fontSize }: StyledTableProps) => fontSize || '14px'};
text-align: left;
margin: 0;
padding: 8px;
${({ withBorder }: StyledTableProps) =>
withBorder &&
css`
border-bottom: 1px solid ${separationColor};
`}
${({ alignCenter }: StyledTableProps) =>
alignCenter &&
css`
text-align: center;
padding: 8px;
`}
:last-child {
border-right: 0;
}
}
}
`;
export default function TableV2({
columns,
data,
filterPlaceholder,
withBorder,
alignCenter,
fontSize,
}: TableV2Props) {
const [globalFilter, setGlobalFilter] = useState('');
const [sorting, setSorting] = useState<SortingState>([]);
const table = useReactTable({
data,
columns,
state: {
globalFilter,
sorting,
},
enableSorting: true,
onGlobalFilterChange: setGlobalFilter,
onSortingChange: setSorting,
getCoreRowModel: getCoreRowModel(),
getSortedRowModel: getSortedRowModel(),
getFilteredRowModel: getFilteredRowModel(),
getPaginationRowModel: getPaginationRowModel(),
});
return (
<>
<FilterLine>
<DebouncedInput
value={globalFilter ?? ''}
onChange={value => setGlobalFilter(String(value))}
placeholder={filterPlaceholder}
count={table.getFilteredRowModel().rows.length}
/>
</FilterLine>
<Styles
withBorder={withBorder}
fontSize={fontSize}
alignCenter={alignCenter}
>
<table>
<thead>
{table.getHeaderGroups().map(headerGroup => (
<tr key={headerGroup.id}>
{headerGroup.headers.map(header => {
return (
<th
key={header.id}
colSpan={header.colSpan}
style={{ whiteSpace: 'nowrap' }}
>
{header.isPlaceholder ? null : (
<>
<div
{...{
className: header.column.getCanSort()
? 'cursor'
: '',
onClick: header.column.getToggleSortingHandler(),
}}
>
{flexRender(
header.column.columnDef.header,
header.getContext()
)}
{{
asc: ' ⬆',
desc: ' ⬇',
}[header.column.getIsSorted() as string] ?? null}
</div>
</>
)}
</th>
);
})}
</tr>
))}
</thead>
<tbody>
{table.getRowModel().rows.map(row => {
return (
<tr key={row.id}>
{row.getVisibleCells().map(cell => {
return (
<td key={cell.id}>
{flexRender(
cell.column.columnDef.cell,
cell.getContext()
)}
</td>
);
})}
</tr>
);
})}
</tbody>
</table>
</Styles>
</>
);
}
// A debounced input react component
function DebouncedInput({
value: initialValue,
onChange,
debounce = 500,
placeholder,
count,
}: {
value: string | number;
onChange: (value: string | number) => void;
count: number;
debounce?: number;
placeholder?: string;
} & Omit<React.InputHTMLAttributes<HTMLInputElement>, 'onChange'>) {
const [value, setValue] = useState(initialValue);
useEffect(() => {
setValue(initialValue);
}, [initialValue]);
useEffect(() => {
const timeout = setTimeout(() => {
onChange(value);
}, debounce);
return () => clearTimeout(timeout);
}, [value]);
return (
<Input
maxWidth={'300px'}
value={value || ''}
onChange={e => setValue(e.target.value)}
placeholder={`Search ${count} ${placeholder || ''}`}
/>
);
}

View file

@ -0,0 +1,95 @@
import { Table } from '@tanstack/react-table';
import { FC, useMemo } from 'react';
import styled from 'styled-components';
import { mediaWidths } from '../../styles/Themes';
import { groupBy } from 'lodash';
import { DarkSubTitle, SubCard } from '../generic/Styled';
interface ColumnConfigurationsProps {
table: Table<any>;
toggleConfiguration: (hide: boolean, id: string) => void;
}
const S = {
row: styled.div`
display: flex;
flex-direction: row;
justify-content: space-between;
margin-bottom: 24px;
`,
optionRow: styled.div`
display: flex;
justify-content: flex-start;
align-items: stretch;
flex-wrap: wrap;
@media (${mediaWidths.mobile}) {
display: block;
}
`,
option: styled.label`
margin: 4px 8px;
`,
options: styled.div`
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: flex-start;
flex-wrap: wrap;
@media (${mediaWidths.mobile}) {
flex-direction: row;
}
`,
};
export const ColumnConfigurations: FC<ColumnConfigurationsProps> = ({
table,
toggleConfiguration,
}: ColumnConfigurationsProps) => {
// The columns that are hideable in configurations need to be grouped by their parents id in order for display purposes, see enableHiding to toggle viewability of each column
const groupedHideableColumns = useMemo(() => {
const allLeafColumns = table
.getAllLeafColumns()
.filter(c => c.getCanHide());
const grouped = groupBy(allLeafColumns, (c: any) => c?.parent?.id);
return grouped;
}, [table]);
return (
<S.optionRow>
{Object.keys(groupedHideableColumns).map(
(group: string, index: number) => {
return (
<SubCard key={`${group}-${index}`} style={{ height: 'auto' }}>
<DarkSubTitle fontSize="16px">
{group === 'undefined' ? 'General' : group}
</DarkSubTitle>
<S.options>
{groupedHideableColumns[group].map((column: any) => {
return (
<S.option key={column.id} className="px-1">
<label>
<input
{...{
type: 'checkbox',
checked: column.getIsVisible(),
onChange: column.getToggleVisibilityHandler(),
}}
onClick={(e: any) =>
toggleConfiguration(!e.target.checked, column.id)
}
/>{' '}
{column.columnDef.header}
</label>
</S.option>
);
})}
</S.options>
</SubCard>
);
}
)}
</S.optionRow>
);
};

View file

@ -0,0 +1,40 @@
import { useState, useEffect } from 'react';
import { Input } from '../input';
// A debounced input react component
export function DebouncedInput({
value: initialValue,
onChange,
debounce = 500,
placeholder,
count,
}: {
value: string | number;
onChange: (value: string | number) => void;
count: number;
debounce?: number;
placeholder?: string;
} & Omit<React.InputHTMLAttributes<HTMLInputElement>, 'onChange'>) {
const [value, setValue] = useState(initialValue);
useEffect(() => {
setValue(initialValue);
}, [initialValue]);
useEffect(() => {
const timeout = setTimeout(() => {
onChange(value);
}, debounce);
return () => clearTimeout(timeout);
}, [value]);
return (
<Input
maxWidth={'300px'}
value={value || ''}
onChange={e => setValue(e.target.value)}
placeholder={`Search ${count} ${placeholder || ''}`}
/>
);
}

View file

@ -1,21 +1,33 @@
import { useMemo, useState } from 'react';
import { useState } from 'react';
import styled, { css } from 'styled-components';
import {
useTable,
useSortBy,
useAsyncDebounce,
useGlobalFilter,
TableInstance,
useFilters,
ColumnInstance,
} from 'react-table';
import { mediaWidths, separationColor } from '../../../src/styles/Themes';
import { Input } from '../input';
useReactTable,
getCoreRowModel,
getFilteredRowModel,
getSortedRowModel,
flexRender,
ColumnDef,
SortingState,
VisibilityState,
} from '@tanstack/react-table';
import { Settings, X } from 'react-feather';
import { separationColor } from '../../styles/Themes';
import { ColorButton } from '../buttons/colorButton/ColorButton';
import { DarkSubTitle, SubCard } from '../generic/Styled';
import { groupBy } from 'lodash';
import 'regenerator-runtime/runtime';
import { ColumnConfigurations } from './ColumnConfigurations';
import { DebouncedInput } from './DebouncedInput';
interface TableProps {
columns: ColumnDef<any, any>[];
data: any;
filterPlaceholder?: string;
withGlobalSort?: boolean; // enables the global search box
withSorting?: boolean; // enables columns to be sorted
withBorder?: boolean;
alignCenter?: boolean;
fontSize?: string;
defaultHiddenColumns?: VisibilityState;
toggleConfiguration?: (hide: boolean, id: string) => void;
}
type StyledTableProps = {
withBorder?: boolean;
@ -23,290 +35,185 @@ type StyledTableProps = {
fontSize?: string;
};
const Styles = styled.div`
overflow-x: auto;
table {
border-spacing: 0;
tr {
:last-child {
td {
border-bottom: 0;
const S = {
row: styled.div`
display: flex;
flex-direction: row;
justify-content: space-between;
margin-bottom: 24px;
`,
wrapper: styled.div`
overflow-x: auto;
table {
width: 100%;
border-spacing: 0;
tr {
:last-child {
td {
border-bottom: 0;
}
}
}
.cursor {
cursor: pointer;
}
,
th,
td {
font-size: ${({ fontSize }: StyledTableProps) => fontSize || '14px'};
text-align: left;
margin: 0;
padding: 8px;
${({ withBorder }: StyledTableProps) =>
withBorder &&
css`
border-bottom: 1px solid ${separationColor};
`}
${({ alignCenter }: StyledTableProps) =>
alignCenter &&
css`
text-align: center;
padding: 8px;
`}
:last-child {
border-right: 0;
}
}
}
th,
td {
font-size: ${({ fontSize }: StyledTableProps) => fontSize || '14px'};
text-align: left;
margin: 0;
padding: 8px;
${({ withBorder }: StyledTableProps) =>
withBorder &&
css`
border-bottom: 1px solid ${separationColor};
`}
${({ alignCenter }: StyledTableProps) =>
alignCenter &&
css`
text-align: center;
padding: 8px;
`}
:last-child {
border-right: 0;
}
}
}
`;
const S = {
options: styled.div`
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: flex-start;
flex-wrap: wrap;
@media (${mediaWidths.mobile}) {
flex-direction: row;
}
`,
option: styled.label`
margin: 4px 8px;
`,
row: styled.div`
display: flex;
justify-content: space-between;
align-items: flex-start;
`,
optionRow: styled.div`
display: flex;
justify-content: flex-start;
align-items: stretch;
flex-wrap: wrap;
@media (${mediaWidths.mobile}) {
display: block;
}
`,
};
const FilterLine = styled.div`
margin-bottom: 24px;
`;
const GlobalFilter = ({
preGlobalFilteredRows,
globalFilter,
setGlobalFilter,
export default function Table({
columns,
data,
filterPlaceholder,
}: any) => {
const count = preGlobalFilteredRows.length;
const [value, setValue] = useState(globalFilter);
const onChange = useAsyncDebounce(value => {
setGlobalFilter(value || undefined);
}, 200);
return (
<FilterLine>
<Input
maxWidth={'300px'}
value={value || ''}
onChange={e => {
setValue(e.target.value);
onChange(e.target.value);
}}
placeholder={`Search ${count} ${filterPlaceholder || ''}`}
/>
</FilterLine>
);
};
type TableColumn = {
Header: string | JSX.Element;
accessor: string;
};
type TableProps = {
tableData: any[];
tableColumns: (
| TableColumn
| {
Header: string | JSX.Element;
columns: TableColumn[];
}
)[];
withBorder?: boolean;
fontSize?: string;
filterPlaceholder?: string;
notSortable?: boolean;
alignCenter?: boolean;
defaultHiddenColumns?: string[];
onHideToggle?: (hide: boolean, id: string) => void;
};
export const Table: React.FC<TableProps> = ({
onHideToggle,
defaultHiddenColumns = [],
tableData,
tableColumns,
withBorder,
fontSize,
filterPlaceholder,
notSortable,
alignCenter,
}) => {
fontSize,
defaultHiddenColumns,
toggleConfiguration,
withGlobalSort = false,
withSorting = false,
}: TableProps) {
const [globalFilter, setGlobalFilter] = useState('');
const [sorting, setSorting] = useState<SortingState>([]);
const [isOpen, setIsOpen] = useState<boolean>(false);
const data = useMemo(() => tableData, [tableData]);
const columns = useMemo(() => tableColumns, [tableColumns]);
const hiddenColumns = useMemo(
() => defaultHiddenColumns,
[defaultHiddenColumns]
const [columnVisibility, setColumnVisibility] = useState<VisibilityState>(
defaultHiddenColumns || {}
);
const instance = useTable(
{
autoResetSortBy: false,
columns,
data,
initialState: {
hiddenColumns,
},
} as any,
useFilters,
useGlobalFilter,
useSortBy
);
const {
allColumns,
getTableProps,
getTableBodyProps,
headerGroups,
rows,
prepareRow,
state,
preGlobalFilteredRows,
setGlobalFilter,
} = instance as TableInstance & {
preGlobalFilteredRows: any;
setGlobalFilter: any;
const tableConfigs = {
data,
columns,
state: {
columnVisibility,
globalFilter,
sorting,
},
enableSorting: withSorting,
onSortingChange: setSorting,
onGlobalFilterChange: setGlobalFilter,
onColumnVisibilityChange: setColumnVisibility,
getCoreRowModel: getCoreRowModel(),
getSortedRowModel: getSortedRowModel(),
getFilteredRowModel: getFilteredRowModel(),
};
const orderedColumns = useMemo(() => {
const hidableColumns = allColumns.reduce((p, c) => {
if (c.isVisible && (c as any).forceVisible) {
return p;
}
if (withGlobalSort) {
tableConfigs.enableSorting = true;
tableConfigs.state.globalFilter = globalFilter;
tableConfigs.onGlobalFilterChange = setGlobalFilter;
}
return [...p, c];
}, [] as ColumnInstance[]);
if (withSorting) {
tableConfigs.state.sorting = sorting;
tableConfigs.onSortingChange = setSorting;
}
const grouped = groupBy(
hidableColumns,
(c: any) => c?.parent?.Header || ''
);
const final = [];
for (const key in grouped) {
if (Object.prototype.hasOwnProperty.call(grouped, key)) {
const group = grouped[key];
final.push({ name: key, items: group });
}
}
return final;
}, [allColumns]);
const table = useReactTable(tableConfigs);
return (
<>
{filterPlaceholder || onHideToggle ? (
<S.row>
{filterPlaceholder ? (
<GlobalFilter
preGlobalFilteredRows={preGlobalFilteredRows}
globalFilter={(state as any).globalFilter}
setGlobalFilter={setGlobalFilter}
filterPlaceholder={filterPlaceholder}
/>
) : null}
{onHideToggle ? (
<S.row>
{withGlobalSort ? (
<DebouncedInput
value={globalFilter ?? ''}
onChange={value => setGlobalFilter(String(value))}
placeholder={filterPlaceholder || ''}
count={table.getFilteredRowModel().rows.length}
/>
) : null}
{toggleConfiguration ? (
<>
<ColorButton onClick={() => setIsOpen(p => !p)}>
{isOpen ? <X size={18} /> : <Settings size={18} />}
</ColorButton>
) : null}
</S.row>
</>
) : null}
</S.row>
{isOpen && toggleConfiguration ? (
<ColumnConfigurations
table={table}
toggleConfiguration={toggleConfiguration}
/>
) : null}
{isOpen && (
<S.optionRow>
{orderedColumns.map((column, index) => {
const { name, items } = column;
return (
<SubCard key={`${name}${index}`} style={{ height: 'auto' }}>
<DarkSubTitle fontSize="16px">{name || 'General'}</DarkSubTitle>
<S.options>
{items.map(item => {
const { checked } = item.getToggleHiddenProps();
return (
<S.option key={item.id}>
<label>
<input
onClick={() =>
onHideToggle && onHideToggle(checked, item.id)
}
type={'checkbox'}
{...item.getToggleHiddenProps()}
/>
{item.Header as any}
</label>
</S.option>
);
})}
</S.options>
</SubCard>
);
})}
</S.optionRow>
)}
<Styles
<S.wrapper
withBorder={withBorder}
fontSize={fontSize}
alignCenter={alignCenter}
>
<table {...getTableProps()} style={{ width: '100%' }}>
<table>
<thead>
{headerGroups.map((headerGroup, index) => (
<tr {...headerGroup.getHeaderGroupProps()} key={index}>
{headerGroup.headers.map((column: any, index) => (
<th
{...column.getHeaderProps(
notSortable ? undefined : column.getSortByToggleProps()
)}
key={index}
>
<span style={{ whiteSpace: 'nowrap' }}>
{column.render('Header')}
</span>
<span>
{column.isSorted ? (column.isSortedDesc ? '⬇' : '⬆') : ''}
</span>
</th>
))}
{table.getHeaderGroups().map(headerGroup => (
<tr key={headerGroup.id}>
{headerGroup.headers.map(header => {
return (
<th
key={header.id}
colSpan={header.colSpan}
style={{ whiteSpace: 'nowrap' }}
>
{header.isPlaceholder ? null : (
<>
<div
{...{
className: header.column.getCanSort()
? 'cursor'
: '',
onClick: header.column.getToggleSortingHandler(),
}}
>
{flexRender(
header.column.columnDef.header,
header.getContext()
)}
{{
asc: ' ⬆',
desc: ' ⬇',
}[header.column.getIsSorted() as string] ?? null}
</div>
</>
)}
</th>
);
})}
</tr>
))}
</thead>
<tbody {...getTableBodyProps()}>
{rows.map((row, index) => {
prepareRow(row);
<tbody>
{table.getRowModel().rows.map(row => {
return (
<tr {...row.getRowProps()} key={index}>
{row.cells.map((cell, index) => {
<tr key={row.id}>
{row.getVisibleCells().map(cell => {
return (
<td {...cell.getCellProps()} key={index}>
{cell.render('Cell')}
<td key={cell.id}>
{flexRender(
cell.column.columnDef.cell,
cell.getContext()
)}
</td>
);
})}
@ -315,7 +222,7 @@ export const Table: React.FC<TableProps> = ({
})}
</tbody>
</table>
</Styles>
</S.wrapper>
</>
);
};
}

View file

@ -4,7 +4,7 @@ import { useGetChainTransactionsQuery } from '../../../graphql/queries/__generat
import { DarkSubTitle } from '../../../components/generic/Styled';
import { getErrorContent } from '../../../utils/error';
import { LoadingCard } from '../../../components/loading/LoadingCard';
import { Table } from '../../../components/table';
import Table from '../../../components/table';
import {
getAddressLink,
getDateDif,
@ -33,9 +33,9 @@ export const ChainTransactions = () => {
const columns = useMemo(
() => [
{
Header: 'Type',
accessor: 'transaction_type',
Cell: ({ row }: any) => (
header: 'Type',
accessorKey: 'transaction_type',
cell: ({ row }: any) => (
<div style={{ whiteSpace: 'nowrap' }}>
{row.original.transaction_type === 'Sent' ? (
<ArrowUp color={chartColors.red} size={16} />
@ -46,38 +46,39 @@ export const ChainTransactions = () => {
),
},
{
Header: 'Date',
accessor: 'created_at',
Cell: ({ row }: any) => (
header: 'Date',
accessorKey: 'created_at',
cell: ({ row }: any) => (
<div style={{ whiteSpace: 'nowrap' }}>
{`${getDateDif(row.original.created_at)} ago`}
</div>
),
},
{
Header: 'Sats',
accessor: 'tokens',
Cell: ({ row }: any) => (
header: 'Sats',
accessorKey: 'tokens',
cell: ({ row }: any) => (
<div style={{ whiteSpace: 'nowrap' }}>
<Price amount={row.original.tokens} />
</div>
),
},
{
Header: 'Fee',
accessor: 'fee',
Cell: ({ row }: any) => (
header: 'Fee',
accessorKey: 'fee',
cell: ({ row }: any) => (
<div style={{ whiteSpace: 'nowrap' }}>
<Price amount={row.original.fee} />
</div>
),
},
{ Header: 'Confirmations', accessor: 'confirmation_count' },
{ Header: 'Confirmation Block', accessor: 'confirmation_height' },
{ header: 'Confirmations', accessorKey: 'confirmation_count' },
{ header: 'Confirmation Block', accessorKey: 'confirmation_height' },
{
Header: 'Output Addresses',
accessor: 'output_addresses',
Cell: ({ row }: any) =>
header: 'Output Addresses',
accessorKey: 'output_addresses',
enableSorting: false,
cell: ({ row }: any) =>
row.original.output_addresses.map((a: string) => (
<div key={a} style={{ whiteSpace: 'nowrap' }}>
{getAddressLink(a)}
@ -85,9 +86,10 @@ export const ChainTransactions = () => {
)),
},
{
Header: 'Transaction',
accessor: 'id',
Cell: ({ row }: any) => (
header: 'Transaction',
accessorKey: 'id',
enableSorting: false,
cell: ({ row }: any) => (
<div style={{ whiteSpace: 'nowrap' }}>
{getTransactionLink(row.original.id)}
</div>
@ -106,6 +108,11 @@ export const ChainTransactions = () => {
}
return (
<Table withBorder={true} tableColumns={columns} tableData={tableData} />
<Table
withBorder={true}
columns={columns}
data={tableData}
withSorting={true}
/>
);
};

View file

@ -4,7 +4,7 @@ import { useGetUtxosQuery } from '../../../graphql/queries/__generated__/getUtxo
import { DarkSubTitle } from '../../../components/generic/Styled';
import { getErrorContent } from '../../../utils/error';
import { LoadingCard } from '../../../components/loading/LoadingCard';
import { Table } from '../../../components/table';
import Table from '../../../components/table';
import { getAddressLink } from '../../../components/generic/helpers';
import { Price } from '../../../components/price/Price';
import { blockToTime } from '../../../utils/helpers';
@ -29,43 +29,46 @@ export const ChainUtxos = () => {
const columns = useMemo(
() => [
{
Header: '',
accessor: 'index',
header: '',
accessorKey: 'index',
},
{
Header: 'Sats',
accessor: 'tokens',
Cell: ({ row }: any) => (
header: 'Sats',
accessorKey: 'tokens',
cell: ({ row }: any) => (
<div style={{ whiteSpace: 'nowrap' }}>
<Price amount={row.original.tokens} />
</div>
),
},
{
Header: 'Confirmations',
header: 'Confirmations',
columns: [
{ Header: 'Blocks', accessor: 'confirmation_count' },
{ header: 'Blocks', accessorKey: 'confirmation_count' },
{
Header: 'Since',
accessor: 'time',
header: 'Since',
accessorKey: 'time',
},
],
},
{
Header: 'Address',
header: 'Address',
enableSorting: false,
columns: [
{
Header: 'Link',
accessor: 'output_addresses',
Cell: ({ row }: any) => (
header: 'Link',
enableSorting: false,
accessorKey: 'output_addresses',
cell: ({ row }: any) => (
<div style={{ whiteSpace: 'nowrap' }}>
{getAddressLink(row.original.address)}
</div>
),
},
{
Header: 'Format',
accessor: 'address_format',
header: 'Format',
enableSorting: false,
accessorKey: 'address_format',
},
],
},
@ -82,6 +85,11 @@ export const ChainUtxos = () => {
}
return (
<Table withBorder={true} tableColumns={columns} tableData={tableData} />
<Table
withBorder={true}
columns={columns}
data={tableData}
withSorting={true}
/>
);
};

View file

@ -21,7 +21,7 @@ import { LoadingCard } from '../../../components/loading/LoadingCard';
import { CloseChannel } from '../../../components/modal/closeChannel/CloseChannel';
import Modal from '../../../components/modal/ReactModal';
import { Price } from '../../../components/price/Price';
import { Table } from '../../../components/table';
import Table from '../../../components/table';
import { useGetChannelsQuery } from '../../../graphql/queries/__generated__/getChannels.generated';
import { useLocalStorage } from '../../../hooks/UseLocalStorage';
import { chartColors } from '../../../styles/Themes';
@ -29,6 +29,7 @@ import { getErrorContent } from '../../../utils/error';
import { blockToTime, formatSeconds, getPercent } from '../../../utils/helpers';
import { ChannelDetails } from './ChannelDetails';
import { defaultHiddenColumns } from './helpers';
import { VisibilityState } from '@tanstack/react-table';
const S = {
link: styled.span`
@ -276,47 +277,52 @@ export const ChannelTable = () => {
const columns = useMemo(
() => [
{
Header: 'Status',
header: 'Status',
columns: [
{
Header: 'Active',
accessor: 'channelActiveLogo',
header: 'Active',
accessorKey: 'channelActiveLogo',
sortType: numberStringSorting('channelActive'),
cell: ({ cell }: any) => cell.renderValue(),
},
{
Header: 'Private',
accessor: 'channelPrivateLogo',
header: 'Private',
accessorKey: 'channelPrivateLogo',
sortType: numberStringSorting('channelPrivate'),
cell: ({ cell }: any) => cell.renderValue(),
},
{
Header: 'Initiated',
accessor: 'channelOpenerLogo',
header: 'Initiated',
accessorKey: 'channelOpenerLogo',
sortType: numberStringSorting('channelOpener'),
cell: ({ cell }: any) => cell.renderValue(),
},
],
},
{
Header: 'Actions',
header: 'Actions',
columns: [
{
Header: <Edit size={14} />,
accessor: 'editAction',
disableSortBy: true,
header: <Edit size={14} />,
accessorKey: 'editAction',
enableSorting: false,
cell: ({ cell }: any) => cell.renderValue(),
},
{
Header: <X size={14} />,
accessor: 'closeAction',
disableSortBy: true,
header: <X size={14} />,
accessorKey: 'closeAction',
enableSorting: false,
cell: ({ cell }: any) => cell.renderValue(),
},
],
},
{
Header: 'Info',
header: 'Info',
columns: [
{
Header: 'Peer',
accessor: 'undercaseAlias',
Cell: ({ row }: any) => (
header: 'Peer',
accessorKey: 'undercaseAlias',
cell: ({ row }: any) => (
<div style={{ whiteSpace: 'nowrap' }}>
{getNodeLink(
row.original.partner_public_key,
@ -326,86 +332,88 @@ export const ChannelTable = () => {
),
},
{
Header: 'Channel Id',
accessor: 'id',
forceVisible: true,
Cell: ({ row }: any) => (
header: 'Channel Id',
accessorKey: 'id',
enableHiding: false,
cell: ({ row }: any) => (
<div style={{ whiteSpace: 'nowrap' }}>
{getChannelLink(row.original.id)}
</div>
),
},
{
Header: 'Capacity',
accessor: 'capacity',
Cell: ({ row }: any) => (
header: 'Capacity',
accessorKey: 'capacity',
cell: ({ row }: any) => (
<div style={{ whiteSpace: 'nowrap' }}>
<Price amount={row.original.capacity} />
</div>
),
},
{ Header: 'Block Age', accessor: 'channel_age' },
{ header: 'Block Age', accessorKey: 'channel_age' },
{
Header: 'Channel Age',
accessor: 'channel_age_duplicate',
Cell: ({ row }: any) => (
header: 'Channel Age',
accessorKey: 'channel_age_duplicate',
cell: ({ row }: any) => (
<div style={{ whiteSpace: 'nowrap' }}>
{blockToTime(row.original.channel_age)}
</div>
),
},
{
Header: 'Past States',
accessor: 'past_states',
header: 'Past States',
accessorKey: 'past_states',
},
],
},
{
Header: 'Balance',
header: 'Balance',
columns: [
{
Header: 'Local',
accessor: 'local_balance',
Cell: ({ row }: any) => (
<div style={{ whiteSpace: 'nowrap' }}>
<Price amount={row.original.local_balance} />
</div>
),
header: 'Local',
accessorKey: 'local_balance',
cell: ({ row }: any) => {
return (
<div style={{ whiteSpace: 'nowrap' }}>
<Price amount={row.original.local_balance} />
</div>
);
},
},
{
Header: 'Remote',
accessor: 'remote_balance',
Cell: ({ row }: any) => (
header: 'Remote',
accessorKey: 'remote_balance',
cell: ({ row }: any) => (
<div style={{ whiteSpace: 'nowrap' }}>
<Price amount={row.original.remote_balance} />
</div>
),
},
{
Header: 'Percent',
accessor: 'balancePercentText',
header: 'Percent',
accessorKey: 'balancePercentText',
sortType: numberStringSorting('balancePercent'),
},
],
},
{
Header: 'Pending HTLC',
header: 'Pending HTLC',
columns: [
{ Header: 'Total HTLC', accessor: 'pending_total_amount' },
{ Header: 'Total Sats', accessor: 'pending_total_tokens' },
{ Header: 'Incoming HTLC', accessor: 'pending_incoming_amount' },
{ Header: 'Incoming Sats', accessor: 'pending_incoming_tokens' },
{ Header: 'Outgoing HTLC', accessor: 'pending_outgoing_amount' },
{ Header: 'Outgoing Sats', accessor: 'pending_outgoing_tokens' },
{ header: 'Total HTLC', accessorKey: 'pending_total_amount' },
{ header: 'Total Sats', accessorKey: 'pending_total_tokens' },
{ header: 'Incoming HTLC', accessorKey: 'pending_incoming_amount' },
{ header: 'Incoming Sats', accessorKey: 'pending_incoming_tokens' },
{ header: 'Outgoing HTLC', accessorKey: 'pending_outgoing_amount' },
{ header: 'Outgoing Sats', accessorKey: 'pending_outgoing_tokens' },
],
},
{
Header: 'Monitoring',
header: 'Monitoring',
columns: [
{
Header: 'Online',
accessor: 'time_online',
Cell: ({ row }: any) => (
header: 'Online',
accessorKey: 'time_online',
cell: ({ row }: any) => (
<div style={{ whiteSpace: 'nowrap' }}>
{formatSeconds(
Math.round((row.original.time_online || 0) / 1000)
@ -414,9 +422,9 @@ export const ChannelTable = () => {
),
},
{
Header: 'Offline',
accessor: 'time_offline',
Cell: ({ row }: any) => (
header: 'Offline',
accessorKey: 'time_offline',
cell: ({ row }: any) => (
<div style={{ whiteSpace: 'nowrap' }}>
{formatSeconds(
Math.round((row.original.time_offline || 0) / 1000)
@ -425,89 +433,97 @@ export const ChannelTable = () => {
),
},
{
Header: 'Percent',
accessor: 'percentOnlineText',
header: 'Percent',
accessorKey: 'percentOnlineText',
sortType: numberStringSorting('percentOnline'),
},
],
},
{
Header: 'Activity',
header: 'Activity',
columns: [
{
Header: 'Sent',
accessor: 'sent',
Cell: ({ row }: any) => (
header: 'Sent',
accessorKey: 'sent',
cell: ({ row }: any) => (
<div style={{ whiteSpace: 'nowrap' }}>
<Price amount={row.original.sent} />
</div>
),
},
{
Header: 'Received',
accessor: 'received',
Cell: ({ row }: any) => (
header: 'Received',
accessorKey: 'received',
cell: ({ row }: any) => (
<div style={{ whiteSpace: 'nowrap' }}>
<Price amount={row.original.received} />
</div>
),
},
{
Header: 'Percent',
accessor: 'activityPercentText',
header: 'Percent',
accessorKey: 'activityPercentText',
sortType: numberStringSorting('activityPercent'),
},
],
},
{
Header: 'My Fees',
header: 'My Fees',
columns: [
{ Header: 'Rate', accessor: 'myRate' },
{ Header: 'Base', accessor: 'myBase' },
{ header: 'Rate', accessorKey: 'myRate' },
{ header: 'Base', accessorKey: 'myBase' },
],
},
{
Header: 'Partner Fees',
header: 'Partner Fees',
columns: [
{ Header: 'Rate', accessor: 'partnerRate' },
{ Header: 'Base', accessor: 'partnerBase' },
{ header: 'Rate', accessorKey: 'partnerRate' },
{ header: 'Base', accessorKey: 'partnerBase' },
],
},
{
Header: 'My HTLC',
header: 'My HTLC',
columns: [
{ Header: 'Max', accessor: 'myMaxHtlc' },
{ Header: 'Min', accessor: 'myMinHtlc' },
{ header: 'Max', accessorKey: 'myMaxHtlc' },
{ header: 'Min', accessorKey: 'myMinHtlc' },
],
},
{
Header: 'Partner HTLC',
header: 'Partner HTLC',
columns: [
{ Header: 'Max', accessor: 'partnerMaxHtlc' },
{ Header: 'Min', accessor: 'partnerMinHtlc' },
{ header: 'Max', accessorKey: 'partnerMaxHtlc' },
{ header: 'Min', accessorKey: 'partnerMinHtlc' },
],
},
{
Header: 'Bars',
header: 'Bars',
columns: [
{
Header: 'Balance',
accessor: 'balanceBars',
header: 'Balance',
accessorKey: 'balanceBars',
sortType: numberStringSorting('balancePercent'),
cell: ({ cell }: any) => cell.renderValue(),
},
{
Header: 'Proportional',
accessor: 'proportionalBars',
header: 'Proportional',
accessorKey: 'proportionalBars',
sortType: numberStringSorting('balancePercent'),
cell: ({ cell }: any) => cell.renderValue(),
},
{
Header: 'Activity',
accessor: 'activityBars',
header: 'Activity',
accessorKey: 'activityBars',
sortType: numberStringSorting('activityPercent'),
cell: ({ cell }: any) => cell.renderValue(),
},
],
},
{ Header: 'Details', accessor: 'viewAction' },
{
header: 'Details',
accessorKey: 'viewAction',
enableSorting: false,
cell: ({ cell }: any) => cell.renderValue(),
},
],
[numberStringSorting]
);
@ -521,6 +537,14 @@ export const ChannelTable = () => {
}
};
const hiddenColumnState: VisibilityState = useMemo(() => {
const defaultColumns: VisibilityState = {};
hiddenColumns.forEach(c => {
defaultColumns[c] = false;
});
return defaultColumns;
}, [hiddenColumns]);
if (loading) {
return <LoadingCard noCard={true} />;
}
@ -552,10 +576,12 @@ export const ChannelTable = () => {
<>
<Table
withBorder={true}
tableColumns={columns}
tableData={tableData}
onHideToggle={handleToggle}
defaultHiddenColumns={hiddenColumns}
columns={columns}
data={tableData}
withGlobalSort={true}
withSorting={true}
toggleConfiguration={handleToggle}
defaultHiddenColumns={hiddenColumnState}
filterPlaceholder="channels"
/>
<Modal isOpen={!!channel} closeCallback={() => setChannel(null)}>

View file

@ -4,7 +4,7 @@ import { useGetClosedChannelsQuery } from '../../../graphql/queries/__generated_
import { DarkSubTitle } from '../../../components/generic/Styled';
import { getErrorContent } from '../../../utils/error';
import { LoadingCard } from '../../../components/loading/LoadingCard';
import { Table } from '../../../components/table';
import Table from '../../../components/table';
import { Price } from '../../../components/price/Price';
import { blockToTime } from '../../../utils/helpers';
import { orderBy } from 'lodash';
@ -56,49 +56,54 @@ export const ClosedChannels = () => {
const columns = useMemo(
() => [
{
Header: 'Peer',
accessor: 'alias',
Cell: ({ row }: any) => (
header: 'Peer',
accessorKey: 'alias',
enableSorting: true,
cell: ({ row }: any) => (
<div style={{ whiteSpace: 'nowrap' }}>
{getNodeLink(row.original.partner_public_key, row.original.alias)}
</div>
),
},
{
Header: 'Closed Since',
accessor: 'closed_for_blocks',
Cell: ({ row }: any) => (
header: 'Closed Since',
accessorKey: 'closed_for_blocks',
enableSorting: true,
cell: ({ row }: any) => (
<div style={{ whiteSpace: 'nowrap' }}>
{blockToTime(row.original.closed_for_blocks)}
</div>
),
},
{
Header: 'Channel Age',
accessor: 'channel_age',
Cell: ({ row }: any) => (
header: 'Channel Age',
accessorKey: 'channel_age',
enableSorting: true,
cell: ({ row }: any) => (
<div style={{ whiteSpace: 'nowrap' }}>
{blockToTime(row.original.channel_age)}
</div>
),
},
{
Header: 'Capacity',
accessor: 'capacity',
Cell: ({ row }: any) => (
header: 'Capacity',
accessorKey: 'capacity',
enableSorting: true,
cell: ({ row }: any) => (
<div style={{ whiteSpace: 'nowrap' }}>
<Price amount={row.original.capacity} />
</div>
),
},
{
Header: 'Close Type',
accessor: 'closeType',
header: 'Close Type',
accessorKey: 'closeType',
},
{
Header: 'Transaction Id',
accessor: 'transaction_id',
Cell: ({ row }: any) => getTransactionLink(row.original.transaction_id),
header: 'Transaction Id',
accessorKey: 'transaction_id',
enableSorting: true,
cell: ({ row }: any) => getTransactionLink(row.original.transaction_id),
},
],
[]
@ -114,9 +119,11 @@ export const ClosedChannels = () => {
return (
<Table
columns={columns}
data={tableData}
withBorder={true}
tableColumns={columns}
tableData={tableData}
withGlobalSort={true}
withSorting={true}
filterPlaceholder="channels"
/>
);

View file

@ -8,7 +8,7 @@ import {
getNodeLink,
getTransactionLink,
} from '../../../components/generic/helpers';
import TableV2 from '../../../components/table-v2';
import Table from '../../../components/table';
import { Price } from '../../../components/price/Price';
import { ColumnDef } from '@tanstack/react-table';
import { PendingChannel } from '../../../graphql/types';
@ -180,10 +180,12 @@ export const PendingChannels = () => {
}
return (
<TableV2
<Table
columns={columns}
data={tableData}
withBorder={true}
withGlobalSort={true}
withSorting={true}
filterPlaceholder="channels"
/>
);

View file

@ -1,4 +1,4 @@
import { Table } from '../../../../components/table';
import Table from '../../../../components/table';
import { useBitcoinFees } from '../../../../hooks/UseBitcoinFees';
import styled from 'styled-components';
@ -17,10 +17,10 @@ export const MempoolWidget = () => {
}
const columns = [
{ Header: 'Fastest', accessor: 'fast' },
{ Header: 'Half Hour', accessor: 'halfHour' },
{ Header: 'Hour', accessor: 'hour' },
{ Header: 'Minimum', accessor: 'minimum' },
{ header: 'Fastest', accessorKey: 'fast' },
{ header: 'Half Hour', accessorKey: 'halfHour' },
{ header: 'Hour', accessorKey: 'hour' },
{ header: 'Minimum', accessorKey: 'minimum' },
];
const data = [
@ -34,7 +34,7 @@ export const MempoolWidget = () => {
return (
<S.wrapper>
<Table alignCenter={true} tableColumns={columns} tableData={data} />
<Table alignCenter={true} columns={columns} data={data} />
</S.wrapper>
);
};

View file

@ -1,6 +1,6 @@
import { getDateDif } from '../../../../components/generic/helpers';
import { Price } from '../../../../components/price/Price';
import { Table } from '../../../../components/table';
import Table from '../../../../components/table';
import { useGetForwardsQuery } from '../../../../graphql/queries/__generated__/getForwards.generated';
import { ChannelAlias } from '../../../../views/home/reports/forwardReport/ChannelAlias';
import styled from 'styled-components';
@ -32,11 +32,31 @@ export const ForwardListWidget = () => {
const forwards = data?.getForwards || [];
const columns = [
{ Header: 'Date', accessor: 'date' },
{ Header: 'Amount', accessor: 'amount' },
{ Header: 'Fee', accessor: 'fee' },
{ Header: 'Incoming', accessor: 'incoming' },
{ Header: 'Outgoing', accessor: 'outgoing' },
{
header: 'Date',
accessorKey: 'date',
cell: ({ cell }: any) => cell.renderValue(),
},
{
header: 'Amount',
accessorKey: 'amount',
cell: ({ cell }: any) => cell.renderValue(),
},
{
header: 'Fee',
accessorKey: 'fee',
cell: ({ cell }: any) => cell.renderValue(),
},
{
header: 'Incoming',
accessorKey: 'incoming',
cell: ({ cell }: any) => cell.renderValue(),
},
{
header: 'Outgoing',
accessorKey: 'outgoing',
cell: ({ cell }: any) => cell.renderValue(),
},
];
const tableData = forwards.reduce((p, f) => {
@ -65,7 +85,7 @@ export const ForwardListWidget = () => {
<S.wrapper>
<S.title>Forwards</S.title>
<S.table>
<Table tableColumns={columns} tableData={tableData} />
<Table columns={columns} data={tableData} withSorting={true} />
</S.table>
</S.wrapper>
);

View file

@ -3,7 +3,6 @@ import { toast } from 'react-toastify';
import { ProgressBar } from '../../components/generic/CardGeneric';
import { SingleLine } from '../../components/generic/Styled';
import { getPrice } from '../../components/price/Price';
import { Table } from '../../components/table';
import { useConfigState } from '../../context/ConfigContext';
import { usePriceState } from '../../context/PriceContext';
import { useGetForwardsQuery } from '../../graphql/queries/__generated__/getForwards.generated';
@ -11,6 +10,7 @@ import { Forward } from '../../graphql/types';
import { getErrorContent } from '../../utils/error';
import { ChannelAlias } from '../home/reports/forwardReport/ChannelAlias';
import { sortByNode } from './helpers';
import Table from '../../components/table';
const getBar = (top: number, bottom: number) => {
const percent = (top / bottom) * 100;
@ -52,12 +52,36 @@ export const ForwardTable: FC<{ days: number; order: string }> = ({
);
const columns = [
{ Header: 'Alias', accessor: 'alias' },
{ Header: 'Channel', accessor: 'channel' },
{ Header: 'Incoming', accessor: 'incoming' },
{ Header: 'Outgoing', accessor: 'outgoing' },
{ Header: 'Incoming', accessor: 'incomingBar' },
{ Header: 'Outgoing', accessor: 'outgoingBar' },
{
header: 'Alias',
accessorKey: 'alias',
cell: ({ cell }: any) => cell.renderValue(),
},
{
header: 'Channel',
accessorKey: 'channel',
cell: ({ cell }: any) => cell.renderValue(),
},
{
header: 'Incoming',
accessorKey: 'incoming',
cell: ({ cell }: any) => cell.renderValue(),
},
{
header: 'Outgoing',
accessorKey: 'outgoing',
cell: ({ cell }: any) => cell.renderValue(),
},
{
header: 'Incoming',
accessorKey: 'incomingBar',
cell: ({ cell }: any) => cell.renderValue(),
},
{
header: 'Outgoing',
accessorKey: 'outgoingBar',
cell: ({ cell }: any) => cell.renderValue(),
},
];
const tableData = final.map(f => ({
@ -69,5 +93,5 @@ export const ForwardTable: FC<{ days: number; order: string }> = ({
outgoingBar: <SingleBar value={getBar(f.outgoing, maxOut)} height={16} />,
}));
return <Table tableData={tableData} tableColumns={columns} />;
return <Table data={tableData} columns={columns} withSorting={true} />;
};

View file

@ -4,10 +4,10 @@ import { getDateDif } from '../../components/generic/helpers';
import { DarkSubTitle } from '../../components/generic/Styled';
import { LoadingCard } from '../../components/loading/LoadingCard';
import { Price } from '../../components/price/Price';
import { Table } from '../../components/table';
import { useGetForwardsQuery } from '../../graphql/queries/__generated__/getForwards.generated';
import { getErrorContent } from '../../utils/error';
import { ChannelAlias } from '../home/reports/forwardReport/ChannelAlias';
import Table from '../../components/table';
type ForwardProps = {
days: number;
@ -34,30 +34,30 @@ export const ForwardsList: FC<ForwardProps> = ({ days }) => {
const columns = useMemo(
() => [
{
Header: 'Date',
accessor: 'created_at',
Cell: ({ row }: any) => (
header: 'Date',
accessorKey: 'created_at',
cell: ({ row }: any) => (
<div style={{ whiteSpace: 'nowrap' }}>
{`${getDateDif(row.original.created_at)} ago`}
</div>
),
},
{
Header: 'Sats',
header: 'Sats',
columns: [
{
Header: 'Forwarded',
accessor: 'tokens',
Cell: ({ row }: any) => (
header: 'Forwarded',
accessorKey: 'tokens',
cell: ({ row }: any) => (
<div style={{ whiteSpace: 'nowrap' }}>
<Price amount={row.original.tokens} />
</div>
),
},
{
Header: 'Earned',
accessor: 'fee',
Cell: ({ row }: any) => (
header: 'Earned',
accessorKey: 'fee',
cell: ({ row }: any) => (
<div style={{ whiteSpace: 'nowrap' }}>
<Price amount={row.original.fee} />
</div>
@ -66,28 +66,32 @@ export const ForwardsList: FC<ForwardProps> = ({ days }) => {
],
},
{
Header: 'Peer',
header: 'Peer',
columns: [
{
Header: 'Incoming',
accessor: 'incoming_name',
header: 'Incoming',
accessorKey: 'incoming_name',
cell: ({ cell }: any) => cell.renderValue(),
},
{
Header: 'Outgoing',
accessor: 'outgoing_name',
header: 'Outgoing',
accessorKey: 'outgoing_name',
cell: ({ cell }: any) => cell.renderValue(),
},
],
},
{
Header: 'Channel',
header: 'Channel',
columns: [
{
Header: 'Incoming',
accessor: 'incoming_channel',
header: 'Incoming',
accessorKey: 'incoming_channel',
cell: ({ cell }: any) => cell.renderValue(),
},
{
Header: 'Outgoing',
accessor: 'outgoing_channel',
header: 'Outgoing',
accessorKeyKey: 'outgoing_channel',
cell: ({ cell }: any) => cell.renderValue(),
},
],
},
@ -104,6 +108,11 @@ export const ForwardsList: FC<ForwardProps> = ({ days }) => {
}
return (
<Table withBorder={true} tableColumns={columns} tableData={tableData} />
<Table
withBorder={true}
columns={columns}
data={tableData}
withSorting={true}
/>
);
};

View file

@ -1,6 +1,6 @@
import { FC } from 'react';
import { Table } from '../../../../components/table';
import { ChannelAlias } from './ChannelAlias';
import Table from '../../../../components/table';
export type RouteType = {
route: string;
@ -53,9 +53,21 @@ export const RouteTable: FC<RouteTableProps> = ({ order, forwardArray }) => {
};
const columns = [
{ Header: 'In', accessor: 'aliasIn' },
{ Header: 'Out', accessor: 'aliasOut' },
{ Header: getTitle(), accessor: getAccesor() },
{
header: 'In',
accessorKey: 'aliasIn',
cell: ({ cell }: any) => cell.renderValue(),
},
{
header: 'Out',
accessorKey: 'aliasOut',
cell: ({ cell }: any) => cell.renderValue(),
},
{
header: getTitle(),
accessorKey: getAccesor(),
cell: ({ cell }: any) => cell.renderValue(),
},
];
const tableData = forwardArray.map(f => ({
@ -64,7 +76,7 @@ export const RouteTable: FC<RouteTableProps> = ({ order, forwardArray }) => {
aliasOut: <ChannelAlias id={f.outgoing_channel} />,
}));
return <Table tableData={tableData} tableColumns={columns} />;
return <Table data={tableData} columns={columns} withSorting={true} />;
};
export const ChannelTable: FC<ChannelTableProps> = ({
@ -94,9 +106,21 @@ export const ChannelTable: FC<ChannelTableProps> = ({
};
const columns = [
{ Header: 'Alias', accessor: 'alias' },
{ Header: 'Id', accessor: 'name' },
{ Header: getTitle(), accessor: getAccesor() },
{
header: 'Alias',
accessorKey: 'alias',
cell: ({ cell }: any) => cell.renderValue(),
},
{
header: 'Id',
accessorKey: 'name',
cell: ({ cell }: any) => cell.renderValue(),
},
{
header: getTitle(),
accessorKey: getAccesor(),
cell: ({ cell }: any) => cell.renderValue(),
},
];
const tableData = forwardArray.map(f => ({
@ -104,5 +128,5 @@ export const ChannelTable: FC<ChannelTableProps> = ({
alias: <ChannelAlias id={f.channelId} />,
}));
return <Table tableData={tableData} tableColumns={columns} />;
return <Table data={tableData} columns={columns} withSorting={true} />;
};

View file

@ -3,7 +3,7 @@ import {
CardWithTitle,
SubTitle,
} from '../../../../components/generic/Styled';
import { Table } from '../../../../components/table';
import Table from '../../../../components/table';
import { useBitcoinFees } from '../../../../hooks/UseBitcoinFees';
export const MempoolReport = () => {
@ -14,10 +14,26 @@ export const MempoolReport = () => {
}
const columns = [
{ Header: 'Fastest', accessor: 'fast' },
{ Header: 'Half Hour', accessor: 'halfHour' },
{ Header: 'Hour', accessor: 'hour' },
{ Header: 'Minimum', accessor: 'minimum' },
{
header: 'Fastest',
accessorKey: 'fast',
cell: ({ cell }: any) => cell.renderValue(),
},
{
header: 'Half Hour',
accessorKey: 'halfHour',
cell: ({ cell }: any) => cell.renderValue(),
},
{
header: 'Hour',
accessorKey: 'hour',
cell: ({ cell }: any) => cell.renderValue(),
},
{
header: 'Minimum',
accessorKey: 'minimum',
cell: ({ cell }: any) => cell.renderValue(),
},
];
const data = [
@ -33,7 +49,7 @@ export const MempoolReport = () => {
<CardWithTitle>
<SubTitle>Mempool Fees</SubTitle>
<Card>
<Table alignCenter={true} tableColumns={columns} tableData={data} />
<Table alignCenter={true} columns={columns} data={data} />
</Card>
</CardWithTitle>
);