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

feat: implement transaction table for account details page #220

Merged
merged 10 commits into from
Feb 24, 2023
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Features

- [#220](https://github.com/alleslabs/celatone-frontend/pull/220) Add transactions table for account details page
- [#218](https://github.com/alleslabs/celatone-frontend/pull/218) Add instantiated and admin contracts of an account
- [#192](https://github.com/alleslabs/celatone-frontend/pull/192) Add alternative sidebar with only icons
- [#210](https://github.com/alleslabs/celatone-frontend/pull/210) New design for token card, currently support price
Expand Down
10 changes: 8 additions & 2 deletions src/lib/components/table/TableTitle.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,16 @@ interface TableTitleProps {
title: string;
count: number;
helperText?: string;
mb?: number | string;
}

export const TableTitle = ({ title, count, helperText }: TableTitleProps) => (
<Box mb={6}>
export const TableTitle = ({
title,
count,
helperText,
mb = "6",
}: TableTitleProps) => (
<Box mb={mb}>
<Flex gap={2} h="29px" alignItems="center">
<Heading as="h6" variant="h6">
{title}
Expand Down
11 changes: 11 additions & 0 deletions src/lib/data/constant.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,3 +77,14 @@ export const typeUrlDict = {
};

export const DEFAULT_RPC_ERROR = "Invalid format, or Something went wrong";

export const DEFAULT_FILTERS = {
bkioshn marked this conversation as resolved.
Show resolved Hide resolved
isExecute: false,
isInstantiate: false,
isUpload: false,
isIbc: false,
isSend: false,
isMigrate: false,
isUpdateAdmin: false,
isClearAdmin: false,
};
12 changes: 2 additions & 10 deletions src/lib/model/account.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// TODO - Refactor Past txs query
import { DEFAULT_FILTERS } from "lib/data";
import { useTxQueryCount } from "lib/pages/past-txs/query/useTxQuery";
import { useCodeListCountByWalletAddress } from "lib/services/codeService";
import {
Expand All @@ -24,16 +25,7 @@ export const useAccountDetailsTableCounts = (walletAddress: HumanAddr) => {
const { data: countTxs, refetch: refetchCountTxs } = useTxQueryCount(
walletAddress,
"",
{
isExecute: false,
isInstantiate: false,
isUpload: false,
isIbc: false,
isSend: false,
isMigrate: false,
isUpdateAdmin: false,
isClearAdmin: false,
}
DEFAULT_FILTERS
);

return {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import {
Box,
Flex,
Grid,
Icon,
Tag,
Text,
useDisclosure,
} from "@chakra-ui/react";
import { useState } from "react";
import { MdCheck, MdClose, MdKeyboardArrowDown } from "react-icons/md";

import { RenderActionMessages } from "lib/components/action-msg/ActionMessages";
import { ExplorerLink } from "lib/components/ExplorerLink";
import { TableRow } from "lib/components/table";
import { AccordionTx } from "lib/components/table/AccordionTx";
import type { PastTransaction } from "lib/types";
import { dateFromNow, formatUTC } from "lib/utils";

interface TxsTableRowProps {
transaction: PastTransaction;
templateColumnsStyle: string;
bkioshn marked this conversation as resolved.
Show resolved Hide resolved
}

export const TxsTableRow = ({
bkioshn marked this conversation as resolved.
Show resolved Hide resolved
transaction,
templateColumnsStyle,
}: TxsTableRowProps) => {
const { isOpen, onToggle } = useDisclosure();
const isAccordion = transaction.messages.length > 1;
const [showCopyButton, setShowCopyButton] = useState(false);

return (
<Box w="full" minW="min-content">
<Grid
templateColumns={templateColumnsStyle}
onClick={isAccordion ? onToggle : undefined}
_hover={{ background: "pebble.900" }}
onMouseEnter={() => setShowCopyButton(true)}
onMouseLeave={() => setShowCopyButton(false)}
transition="all .25s ease-in-out"
cursor={isAccordion ? "pointer" : "default"}
>
<TableRow pl="16px">
<ExplorerLink
value={transaction.hash.toLocaleUpperCase()}
bkioshn marked this conversation as resolved.
Show resolved Hide resolved
type="tx_hash"
canCopyWithHover
/>
</TableRow>
<TableRow>
<Icon
as={transaction.success ? MdCheck : MdClose}
fontSize="24px"
color={transaction.success ? "success.main" : "error.main"}
/>
</TableRow>
<TableRow>
<Flex gap={1} flexWrap="wrap">
<RenderActionMessages
transaction={transaction}
showCopyButton={showCopyButton}
/>
{transaction.isIbc && (
<Tag borderRadius="full" bg="honeydew.dark" color="pebble.900">
IBC
</Tag>
)}
</Flex>
</TableRow>
<TableRow>
<Flex direction="row" justify="space-between" align="center" w="full">
bkioshn marked this conversation as resolved.
Show resolved Hide resolved
<Flex direction="column" gap={1}>
{transaction.created ? (
<>
bkioshn marked this conversation as resolved.
Show resolved Hide resolved
<Text variant="body3">{formatUTC(transaction.created)}</Text>
<Text variant="body3" color="text.dark">
{`(${dateFromNow(transaction.created)})`}
</Text>
</>
) : (
<Text variant="body3">N/A</Text>
)}
</Flex>
</Flex>
</TableRow>
<TableRow>
{isAccordion && (
<Icon
as={MdKeyboardArrowDown}
transform={isOpen ? "rotate(180deg)" : "rotate(0deg)"}
boxSize="24px"
color="pebble.600"
/>
)}
</TableRow>
</Grid>
{isAccordion && (
<Grid w="full" py={2} bg="pebble.900" hidden={!isOpen}>
{transaction.messages.map((msg, index) => (
<AccordionTx
key={index.toString() + msg.type}
allowFurtherAction={false}
message={msg}
/>
))}
</Grid>
)}
</Box>
);
};
187 changes: 187 additions & 0 deletions src/lib/pages/account-details/components/tables/transactions/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
import { Box, Flex, Grid } from "@chakra-ui/react";
import type { ChangeEvent } from "react";
import { useMemo, useState } from "react";

import { Loading } from "lib/components/Loading";
import { Pagination } from "lib/components/pagination";
import { usePaginator } from "lib/components/pagination/usePaginator";
import { EmptyState } from "lib/components/state/EmptyState";
import { TableContainer, TableHeader } from "lib/components/table";
import { TableTitle } from "lib/components/table/TableTitle";
import { ViewMore } from "lib/components/table/ViewMore";
import { DEFAULT_FILTERS } from "lib/data";
import { FilterSelection } from "lib/pages/past-txs/components/FilterSelection";
import { useTxQuery } from "lib/pages/past-txs/query/useTxQuery";
bkioshn marked this conversation as resolved.
Show resolved Hide resolved
import type { HumanAddr, Option, TxFilters } from "lib/types";

import { TxsTableRow } from "./TxsTableRow";

interface TransactionTableProps {
walletAddress: HumanAddr;
scrollComponentId: string;
totalData: Option<number>;
refetchCount: () => void;
onViewMore?: () => void;
}

interface TransactionTableBodyProps extends TransactionTableProps {
filters: TxFilters;
filterSelected: string[];
}

const TransactionTableBody = ({
walletAddress,
scrollComponentId,
totalData,
refetchCount,
onViewMore,
filters,
filterSelected,
}: TransactionTableBodyProps) => {
const {
pagesQuantity,
currentPage,
setCurrentPage,
pageSize,
setPageSize,
offset,
} = usePaginator({
total: totalData,
initialState: {
pageSize: 10,
currentPage: 1,
isDisabled: false,
},
});

const { data: transactions, isLoading } = useTxQuery(
walletAddress,
"",
filters,
onViewMore ? 5 : pageSize,
offset
);

const onPageChange = (nextPage: number) => {
refetchCount();
setCurrentPage(nextPage);
};

const onPageSizeChange = (e: ChangeEvent<HTMLSelectElement>) => {
const size = Number(e.target.value);
refetchCount();
setPageSize(size);
setCurrentPage(1);
};

const templateColumnsStyle =
bkioshn marked this conversation as resolved.
Show resolved Hide resolved
"180px 70px minmax(360px, 1fr) max(250px) max(70px)";

if (isLoading) return <Loading />;

if (!transactions?.length && filterSelected.length > 0)
return (
<Flex
mt="20px"
py="64px"
direction="column"
borderY="1px solid"
borderColor="pebble.700"
>
<EmptyState
image="https://assets.alleslabs.dev/illustration/search-empty.svg"
message="No past transaction matches found with your input."
/>
</Flex>
);

if (!transactions?.length)
return (
<Flex
mt="20px"
py="64px"
direction="column"
borderY="1px solid"
borderColor="pebble.700"
>
<EmptyState message="This account did not submit any transactions before." />
</Flex>
);

return (
<>
<TableContainer>
<Grid templateColumns={templateColumnsStyle}>
<TableHeader>Transaction Hash</TableHeader>
<TableHeader />
<TableHeader>Messages</TableHeader>
<TableHeader>Timestamp</TableHeader>
<TableHeader />
</Grid>
{transactions.map((transaction) => (
<TxsTableRow
key={transaction.hash}
transaction={transaction}
templateColumnsStyle={templateColumnsStyle}
/>
))}
</TableContainer>
{totalData &&
(onViewMore
? totalData > 5 && <ViewMore onClick={onViewMore} />
: totalData > 10 && (
<Pagination
currentPage={currentPage}
pagesQuantity={pagesQuantity}
offset={offset}
totalData={totalData}
scrollComponentId={scrollComponentId}
pageSize={pageSize}
onPageChange={onPageChange}
onPageSizeChange={onPageSizeChange}
/>
))}
</>
);
};

export const TransactionTable = (
transactionTableProps: TransactionTableProps
) => {
const [filters, setFilters] = useState<TxFilters>(DEFAULT_FILTERS);

const handleSetFilters = (filter: string, bool: boolean) => {
setFilters((prevFilters) => ({ ...prevFilters, [filter]: bool }));
};

const filterSelected = useMemo(() => {
return Object.keys(filters).reduce((acc: string[], key: string) => {
if (filters[key as keyof typeof filters]) {
acc.push(key);
}
return acc;
}, []);
}, [filters]);
bkioshn marked this conversation as resolved.
Show resolved Hide resolved

const { totalData, onViewMore } = transactionTableProps;
return (
<Box mt={12} mb={4}>
<Flex direction="row" justify="space-between" alignItems="center">
<TableTitle title="Transactions" count={totalData ?? 0} mb={0} />
{!onViewMore && (
<FilterSelection
result={filterSelected}
setResult={handleSetFilters}
boxWidth="400px"
placeholder="All"
/>
)}
</Flex>
<TransactionTableBody
{...transactionTableProps}
filters={filters}
filterSelected={filterSelected}
/>
</Box>
);
};
Loading