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
- [#221](https://github.com/alleslabs/celatone-frontend/pull/221) Add codes of an account
- [#223](https://github.com/alleslabs/celatone-frontend/pull/223) Newer version of token card and format mechanism
- [#214](https://github.com/alleslabs/celatone-frontend/pull/214) Show code permission helper text in save new code modal
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,109 @@
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.toUpperCase()}
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="column" gap={1}>
{transaction.created ? (
<Box>
<Text variant="body3">{formatUTC(transaction.created)}</Text>
<Text variant="body3" color="text.dark">
{`(${dateFromNow(transaction.created)})`}
</Text>
</Box>
) : (
<Text variant="body3">N/A</Text>
)}
bkioshn marked this conversation as resolved.
Show resolved Hide resolved
</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>
);
};
182 changes: 182 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,182 @@
import { Box, Flex, Grid } from "@chakra-ui/react";
import type { ChangeEvent } from "react";
import { 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 TransactionsTableProps {
walletAddress: HumanAddr;
scrollComponentId: string;
totalData: Option<number>;
refetchCount: () => void;
onViewMore?: () => void;
}

interface TransactionsTableBodyProps extends TransactionsTableProps {
filters: TxFilters;
filterSelected: string[];
}

const TransactionsTableBody = ({
walletAddress,
scrollComponentId,
totalData,
refetchCount,
onViewMore,
filters,
filterSelected,
}: TransactionsTableBodyProps) => {
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 TransactionsTable = (
transactionsTableProps: TransactionsTableProps
) => {
const [filters, setFilters] = useState<TxFilters>(DEFAULT_FILTERS);

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

const filterSelected = Object.keys(filters).filter(
(key) => filters[key as keyof typeof filters]
);

const { totalData, onViewMore } = transactionsTableProps;
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>
<TransactionsTableBody
{...transactionsTableProps}
filters={filters}
filterSelected={filterSelected}
/>
</Box>
);
};
Loading