Skip to content

Commit

Permalink
feat(explorer): account page (#2238)
Browse files Browse the repository at this point in the history
  • Loading branch information
sstraatemans committed Jun 6, 2024
1 parent e79c180 commit 872a0f1
Show file tree
Hide file tree
Showing 10 changed files with 317 additions and 42 deletions.
5 changes: 5 additions & 0 deletions .changeset/fluffy-kings-battle.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@kadena/explorer": minor
---

add the account page
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import {
Cell,
Column,
Row,
Table,
TableBody,
TableHeader,
} from '@kadena/react-ui';
import type { FC } from 'react';
import React from 'react';
import type { ICompactKeyTableProps } from '../compact-keys-table';
import { tableClass } from './styles.css';

const CompactTransactionsTableDesktop: FC<ICompactKeyTableProps> = ({
keys,
}) => {
return (
<Table aria-label="Keys table" isStriped className={tableClass}>
<TableHeader>
<Column width="10%">ChainId</Column>
<Column width="50%">Key</Column>
<Column width="40%">Predicate</Column>
</TableHeader>
<TableBody>
{keys.map((key) => (
<Row key={key.key + key.chainId}>
<Cell>
<span>{key.chainId}</span>
</Cell>
<Cell>
<span>{key.key}</span>
</Cell>
<Cell>
<span>{key.predicate}</span>
</Cell>
</Row>
))}
</TableBody>
</Table>
);
};
export default CompactTransactionsTableDesktop;
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { globalStyle, style } from '@vanilla-extract/css';

export const tableClass = style({
width: '100%',
});

globalStyle(`${tableClass} td span`, {
display: 'block',
alignItems: 'center',
contain: 'inline-size',
overflow: 'hidden',
textOverflow: 'ellipsis',
whiteSpace: 'nowrap',
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import {
dataFieldClass,
dataFieldLinkClass,
headerClass,
rowClass,
sectionClass,
} from '@/components/compact-transactions-table/compact-transactions-table-mobile/styles.css';
import { Text } from '@kadena/react-ui';
import type { FC } from 'react';
import React from 'react';
import type { ICompactKeyTableProps } from '../compact-keys-table';

const CompactTransactionsTableMobile: FC<ICompactKeyTableProps> = ({
keys,
}) => {
return keys.map((key, index) => (
<section key={index} className={sectionClass}>
<div className={rowClass}>
<span className={headerClass}>ChainId</span>
{key.chainId}
</div>
<div className={rowClass}>
<span className={headerClass}>Key</span>
<Text variant="code" className={dataFieldClass}>
{key.key}
</Text>
</div>
<div className={rowClass}>
<span className={headerClass}>Predicate</span>
<span className={dataFieldLinkClass}>
<Text variant="code" className={dataFieldClass}>
{key.predicate}
</Text>
</span>
</div>
</section>
));
};
export default CompactTransactionsTableMobile;
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { Media } from '@/components/layout/media';
import type { FC } from 'react';
import React from 'react';
import CompactKeysTableDesktop from './compact-keys-table-desktop/compact-keys-table-desktop';
import CompactKeysTableMobile from './compact-keys-table-mobile/compact-keys-table-mobile';

export interface IKeyProps {
chainId: string;
key: string;
predicate: string;
}

export interface ICompactKeyTableProps {
keys: IKeyProps[];
}

const CompactKeysTable: FC<ICompactKeyTableProps> = ({ keys }) => {
return (
<>
<Media lessThan="sm">
<CompactKeysTableMobile keys={keys} />
</Media>
<Media greaterThanOrEqual="sm">
<CompactKeysTableDesktop keys={keys} />
</Media>
</>
);
};
export default CompactKeysTable;
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,32 @@ import type {
Transaction,
TransactionResult,
} from '@/__generated__/sdk';
import { tableClass } from '@/components/compact-keys-table/compact-keys-table-desktop/styles.css';
import {
MonoArrowOutward,
MonoCheck,
MonoClear,
} from '@kadena/react-icons/system';
import { Badge, Text } from '@kadena/react-ui';
import React, { Fragment } from 'react';
import {
Badge,
Cell,
Column,
Row,
Stack,
Table,
TableBody,
TableHeader,
Text,
} from '@kadena/react-ui';
import Link from 'next/link';
import React from 'react';
import {
badgeClass,
dataFieldClass,
dataFieldLinkClass,
headerClass,
iconLinkClass,
linkClass,
linkIconClass,
sectionClass,
linkWrapperClass,
} from './styles.css';

interface ICompactTransactionsTableDesktopProps {
Expand All @@ -29,44 +39,61 @@ const CompactTransactionsTableDesktop: React.FC<
ICompactTransactionsTableDesktopProps
> = ({ transactions }) => {
return (
<section className={sectionClass}>
<span>
<Badge className={badgeClass} size="sm">
Status
</Badge>
</span>
<span className={headerClass}>Sender</span>
<span className={headerClass}>Request Key</span>
<span className={headerClass}>Code Preview</span>
{transactions.map((transaction, index) => (
<Fragment key={index}>
{(transaction.result as TransactionResult).goodResult ? (
<MonoCheck />
) : (
<MonoClear />
)}
<Text variant="code" className={dataFieldClass}>
{transaction.cmd.meta.sender}
</Text>
<span className={dataFieldLinkClass}>
<a href={`/transaction/${transaction.hash}`} className={linkClass}>
<Text variant="code" className={dataFieldClass}>
{transaction.hash}
<Table aria-label="Keys table" isStriped className={tableClass}>
<TableHeader>
<Column width="10%">
<Badge className={badgeClass} size="sm">
Status
</Badge>
</Column>
<Column width="25%">Sender</Column>
<Column width="25%">Request Key</Column>
<Column width="40%">Code Preview</Column>
</TableHeader>
<TableBody>
{transactions.map((transaction) => (
<Row key={transaction.id}>
<Cell>
{(transaction.result as TransactionResult).goodResult ? (
<MonoCheck />
) : (
<MonoClear />
)}
</Cell>
<Cell>
<Text as="span" variant="code" className={dataFieldClass}>
{transaction.cmd.meta.sender}
</Text>
</Cell>
<Cell>
<span>
<Stack alignItems="center" className={linkWrapperClass}>
<Link
href={`/transaction/${transaction.hash}`}
className={linkClass}
>
<Text variant="code" className={dataFieldClass}>
{transaction.hash}
</Text>
</Link>
<Link
href={`/transaction/${transaction.hash}`}
className={iconLinkClass}
>
<MonoArrowOutward className={linkIconClass} />
</Link>
</Stack>
</span>
</Cell>
<Cell>
<Text as="span" variant="code" className={dataFieldClass}>
{(transaction.cmd.payload as ExecutionPayload).code}
</Text>
</a>
<a
href={`/transaction/${transaction.hash}`}
className={iconLinkClass}
>
<MonoArrowOutward className={linkIconClass} />
</a>
</span>
<Text variant="code" className={dataFieldClass}>
{(transaction.cmd.payload as ExecutionPayload).code}
</Text>
</Fragment>
))}
</section>
</Cell>
</Row>
))}
</TableBody>
</Table>
);
};
export default CompactTransactionsTableDesktop;
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ export const linkClass = style({
textDecoration: 'none',
});

export const linkWrapperClass = style({
width: `calc(100% - 15px)`,
});
export const iconLinkClass = style([
atoms({
color: 'text.base.default',
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { maskValue } from '@kadena/react-ui';
import type { FC } from 'react';
import React from 'react';

interface IProps {
value?: string;
}

const MaskedAccountName: FC<IProps> = ({ value }) => {
if (!value) return null;

return <span>{maskValue(value)}</span>;
};

export default MaskedAccountName;
25 changes: 25 additions & 0 deletions packages/apps/explorer/src/graphql/queries/account.graph.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { gql } from '@apollo/client';
import type { DocumentNode } from 'graphql';

export const block: DocumentNode = gql`
query account($accountName: String!) {
fungibleAccount(accountName: $accountName) {
accountName
totalBalance
chainAccounts {
chainId
guard {
keys
predicate
}
}
transactions {
edges {
node {
...CoreTransactionFields
}
}
}
}
}
`;
76 changes: 76 additions & 0 deletions packages/apps/explorer/src/pages/account/[accountName].tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import type { Transaction } from '@/__generated__/sdk';
import { useAccountQuery } from '@/__generated__/sdk';
import type { IKeyProps } from '@/components/compact-keys-table/compact-keys-table';
import CompactKeysTable from '@/components/compact-keys-table/compact-keys-table';
import CompactTransactionsTable from '@/components/compact-transactions-table/compact-transactions-table';
import MaskedAccountName from '@/components/mask-accountname/mask-accountname';
import { Heading, Stack } from '@kadena/react-ui';
import { useRouter } from 'next/router';
import type { FC } from 'react';
import React, { useMemo } from 'react';

const Account: FC = () => {
const router = useRouter();

const { loading, data, error } = useAccountQuery({
variables: {
accountName: router.query.accountName as string,
},
skip: !router.query.accountName,
});

const { fungibleAccount } = data ?? {};

const keys: IKeyProps[] = useMemo(() => {
const innerKeys: IKeyProps[] =
fungibleAccount?.chainAccounts.reduce((acc: IKeyProps[], val) => {
const guardKeys: IKeyProps[] = val.guard.keys.map((key) => {
return {
key: key,
predicate: val.guard.predicate,
chainId: val.chainId,
};
});
return [...acc, ...guardKeys];
}, []) ?? [];

return innerKeys.sort((a: IKeyProps, b: IKeyProps) => {
if (parseInt(a.chainId, 10) < parseInt(b.chainId, 10)) return -1;
if (parseInt(a.chainId, 10) > parseInt(b.chainId, 10)) return 1;
return 0;
});
}, [fungibleAccount?.chainAccounts]);

return (
<>
{loading && <div>Loading...</div>}
{error && <div>Error: {error.message}</div>}

<Stack margin="md">
<Heading as="h5">
{parseFloat(fungibleAccount?.totalBalance).toFixed(2)} KDA spread
across {fungibleAccount?.chainAccounts.length} Chains for account{' '}
<MaskedAccountName value={fungibleAccount?.accountName ?? ''} />
</Heading>
</Stack>
{keys && <CompactKeysTable keys={keys} />}
<Stack
width="100%"
gap="md"
flexDirection={{ xs: 'column-reverse', md: 'row' }}
>
<Stack flex={1} flexDirection="column">
{fungibleAccount?.transactions && (
<CompactTransactionsTable
transactions={fungibleAccount?.transactions.edges.map(
(edge) => edge.node as Transaction,
)}
/>
)}
</Stack>
</Stack>
</>
);
};

export default Account;

0 comments on commit 872a0f1

Please sign in to comment.