Skip to content

Commit

Permalink
feat: add tx page
Browse files Browse the repository at this point in the history
  • Loading branch information
pedronauck committed Sep 27, 2023
1 parent 185b2bc commit f9e6fb8
Show file tree
Hide file tree
Showing 20 changed files with 254 additions and 84 deletions.
2 changes: 1 addition & 1 deletion packages/app/.env.production
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
FUEL_PROVIDER_URL=http://beta-4.fuel.network/graphql
FUEL_PROVIDER_URL=http://beta-3.fuel.network/graphql

4 changes: 3 additions & 1 deletion packages/app/next.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,17 @@ const config = {
externalDir: true,
serverComponentsExternalPackages: [
'bcryptjs',
'ws',
'isomorphic-ws',
'@graphql-tools/delegate',
'@graphql-tools/load',
'@graphql-tools/load-files',
'@graphql-tools/schema',
'@graphql-tools/stitch',
'@graphql-tools/url-loader',
'@graphql-tools/utils',
],
serverActions: true,
esmExternals: true,
},
/** We run eslint as a separate task in CI */
eslint: {
Expand Down
1 change: 1 addition & 0 deletions packages/app/src/app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
h5,
h6 {
font-weight: 600;
letter-spacing: -0.025em;
}
kbd {
font-size: 0.875rem;
Expand Down
10 changes: 7 additions & 3 deletions packages/app/src/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,17 @@ import { getLastTxs } from '~/systems/Transaction/actions/get-last-txs';
import { TxList } from '~/systems/Transaction/component/TxList/TxList';

export default async function Home() {
const transactions = await getLastTxs({});
const transactions = await getLastTxs({ last: 30 });
return (
<Layout hero>
<Heading as="h2" size="2" className="mb-10">
<Heading
as="h2"
size="2"
className="flex justify-between items-center mb-10"
>
Recent Transactions
</Heading>
<TxList transactions={transactions} />
<TxList transactions={transactions.edges} />
</Layout>
);
}
Expand Down
25 changes: 25 additions & 0 deletions packages/app/src/app/tx/[id]/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { Layout } from '~/systems/Core/components/Layout/Layout';
import { getTx } from '~/systems/Transaction/actions/get-tx';
import { TxScreen } from '~/systems/Transaction/screens/TxScreen/TxScreen';

type TransactionProps = {
params: {
id: string;
};
};

export default async function Transaction({ params }: TransactionProps) {
const id = params.id;
const tx = await getTx({ id });
if (!tx) {
throw new Error('Transaction not found');
}
return (
<Layout>
<TxScreen transaction={tx} />
</Layout>
);
}

// Revalidate cache every 10 seconds
export const revalidate = 10;
5 changes: 0 additions & 5 deletions packages/app/src/systems/Core/utils/address.ts

This file was deleted.

2 changes: 1 addition & 1 deletion packages/app/src/systems/Core/utils/sdk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,5 @@ const getBaseUrl = () => {
};

const API_URL = resolve(getBaseUrl(), '/api/graphql');
const client = new GraphQLClient(API_URL);
const client = new GraphQLClient(API_URL, { fetch });
export const sdk = getSdk(client);
11 changes: 5 additions & 6 deletions packages/app/src/systems/Transaction/actions/get-last-txs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,11 @@ import { act } from '~/systems/Core/utils/act-server';
import { sdk } from '~/systems/Core/utils/sdk';

const schema = z.object({
last: z.number().default(12).optional(),
first: z.number().optional().nullable(),
last: z.number().optional().nullable(),
});

export const getLastTxs = act(schema, async ({ last = 12 }) => {
const { data } = await sdk.getLastTransactions({ last }).catch(() => ({
data: { transactions: { nodes: [] } },
}));
return data.transactions.nodes;
export const getLastTxs = act(schema, async (input) => {
const { data } = await sdk.getLastTransactions(input);
return data.transactions;
});
14 changes: 14 additions & 0 deletions packages/app/src/systems/Transaction/actions/get-tx.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
'use server';

import { z } from 'zod';
import { act } from '~/systems/Core/utils/act-server';
import { sdk } from '~/systems/Core/utils/sdk';

const schema = z.object({
id: z.string(),
});

export const getTx = act(schema, async (input) => {
const { data } = await sdk.getTransaction(input);
return data.transaction;
});
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { TxIcon } from '../TxIcon/TxIcon';
export type TxAccountItemProps = CardProps & {
type: TxAccountType;
id: string;
spent: BN;
spent?: BN;
};

const COLOR_MAP = {
Expand All @@ -33,9 +33,11 @@ export function TxAccountItem({
<TxIcon color={COLOR_MAP[type]} type={type} />
</EntityItem.Slot>
<EntityItem.Info id={id} title={type}>
<Text as="div" className="text-sm" leftIcon={IconCoins}>
Spent: {bn(spent).format({ units: 3 })}
</Text>
{spent && (
<Text as="div" className="text-sm" leftIcon={IconCoins}>
Spent: {bn(spent).format()}
</Text>
)}
</EntityItem.Info>
</EntityItem>
</Card.Body>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import type { BN } from 'fuels';
import Image from 'next/image';
import { useMemo } from 'react';

import { TxIcon } from '../TxIcon/TxIcon';

export type TxAssetItemProps = CardProps & {
assetId: string;
amountIn: BN;
Expand All @@ -27,36 +29,39 @@ export function TxAssetItem({
() => ASSET_LIST.find((i) => i.assetId === assetId),
[assetId],
);
if (!asset) {
throw new Error(`Asset not found: ${assetId}`);
}
const assetName = asset?.name ?? 'Unknown';
const assetSymbol = asset?.symbol ?? null;
return (
<Card {...props} className={cx('gap-2 pb-2', className)}>
<EntityItem className="px-4 pb-4 border-b border-border">
<EntityItem.Slot>
<Image
alt={asset.name}
height={ICON_SIZE}
src={asset.icon as string}
width={ICON_SIZE}
/>
{asset?.icon ? (
<Image
src={asset.icon as string}
width={ICON_SIZE}
height={ICON_SIZE}
alt={asset.name}
/>
) : (
<TxIcon type="Mint" status="Submitted" />
)}
</EntityItem.Slot>
<EntityItem.Info id={asset.assetId} title={asset.name} />
<EntityItem.Info id={assetId} title={assetName} />
</EntityItem>
<HStack className="px-4 justify-between">
<Text
className="text-sm"
iconColor="text-success"
leftIcon={IconArrowUp}
>
{bn(amountIn).format({ units: 4 })} {asset.symbol}
{bn(amountIn).format()} {assetSymbol}
</Text>
<Text
className="text-sm"
iconColor="text-error"
leftIcon={IconArrowDown}
>
{bn(amountOut).format({ units: 4 })} {asset.symbol}
{bn(amountOut).format()} {assetSymbol}
</Text>
</HStack>
</Card>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
'use client';

import type { BreadcrumbProps } from '@fuels/ui';
import {
Breadcrumb,
BreadcrumbItem,
BreadcrumbLink,
Copyable,
Icon,
shortAddress,
} from '@fuels/ui';
import { IconHome } from '@tabler/icons-react';
import Link from 'next/link';

import type { TransactionNode } from '../../types';

type TxBreadcrumbProps = BreadcrumbProps & {
transaction: TransactionNode;
};

export function TxBreadcrumb({ transaction: tx, ...props }: TxBreadcrumbProps) {
return (
<Breadcrumb {...props}>
<BreadcrumbLink asChild>
<Link href="/">
<Icon icon={IconHome} size={24} color="text-muted" />
</Link>
</BreadcrumbLink>
<BreadcrumbItem>
<Copyable value={tx.id}>{shortAddress(tx.id)}</Copyable>
</BreadcrumbItem>
</Breadcrumb>
);
}
61 changes: 32 additions & 29 deletions packages/app/src/systems/Transaction/component/TxCard/TxCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
IconTransfer,
IconUsers,
} from '@tabler/icons-react';
import Link from 'next/link';
import { tv } from 'tailwind-variants';

import type { TransactionNode, TxStatus } from '../../types';
Expand All @@ -22,35 +23,37 @@ export function TxCard({ transaction: tx, className, ...props }: TxCardProps) {
const classes = styles();
const title = tx.title as string;
return (
<Card {...props} className={classes.root({ className })}>
<Card.Header>
<EntityItem>
<EntityItem.Slot>
<TxIcon status={tx.statusType as TxStatus} type={title} />
</EntityItem.Slot>
<EntityItem.Info id={tx.id} title={title} />
</EntityItem>
</Card.Header>
<Card.Body className={classes.body()}>
<Flex className={classes.row()} justify="between">
<Text leftIcon={IconUsers}>{tx.totalAccounts} accounts</Text>
</Flex>
<Flex className={classes.row()} justify="between">
<Text leftIcon={IconTransfer}>{tx.totalOperations} operations</Text>
<Text className={classes.small()}>
<Badge color={TX_INTENT_MAP[tx.statusType as string]}>
{tx.statusType}
</Badge>
</Text>
</Flex>
<Flex className={classes.row()} justify="between">
<Text leftIcon={IconCoins}>{tx.totalAssets} assets</Text>
<Text className={classes.small()} leftIcon={IconGasStation}>
{bn(tx.gasUsed).format({ units: 3 })} ETH
</Text>
</Flex>
</Card.Body>
</Card>
<Link href={`/tx/${tx.id}`}>
<Card {...props} className={classes.root({ className })}>
<Card.Header>
<EntityItem>
<EntityItem.Slot>
<TxIcon status={tx.statusType as TxStatus} type={title} />
</EntityItem.Slot>
<EntityItem.Info id={tx.id} title={title} />
</EntityItem>
</Card.Header>
<Card.Body className={classes.body()}>
<Flex className={classes.row()} justify="between">
<Text leftIcon={IconUsers}>{tx.totalAccounts} accounts</Text>
</Flex>
<Flex className={classes.row()} justify="between">
<Text leftIcon={IconTransfer}>{tx.totalOperations} operations</Text>
<Text className={classes.small()}>
<Badge color={TX_INTENT_MAP[tx.statusType as string]}>
{tx.statusType}
</Badge>
</Text>
</Flex>
<Flex className={classes.row()} justify="between">
<Text leftIcon={IconCoins}>{tx.totalAssets} assets</Text>
<Text className={classes.small()} leftIcon={IconGasStation}>
{bn(tx.gasUsed).format({ precision: 5 })} ETH
</Text>
</Flex>
</Card.Body>
</Card>
</Link>
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ const TxInputCoin = createComponent<TxInputProps, typeof Card>({
<HStack align="center">
{amount && (
<Text className="text-secondary">
{bn(amount).format({ units: 3 })} {asset.symbol}
{bn(amount).format({ precision: 3 })} {asset.symbol}
</Text>
)}
<IconButton
Expand All @@ -104,9 +104,14 @@ const TxInputCoin = createComponent<TxInputProps, typeof Card>({
</Text>
{inputs?.map((input: InputCoin) => (
<HStack key={input.utxoId} align="center" justify="between">
<Text className="text-xs leading-relaxed">{input.utxoId}</Text>
<Copyable
className="text-xs leading-relaxed"
value={input.utxoId}
>
{shortAddress(input.utxoId, 14, 14)}
</Copyable>
<Text className="text-xs leading-relaxed text-muted">
{bn(input.amount).format({ units: 3 })}
{bn(input.amount).format({ precision: 3 })} {asset.symbol}
</Text>
</HStack>
))}
Expand Down Expand Up @@ -221,7 +226,7 @@ export function TxInput({ input, ...props }: TxInputProps) {
const styles = tv({
slots: {
header: 'group flex flex-row gap-4 justify-between items-center',
icon: 'transition-transform group-hover:rotate-180 group-data-[state=open]:rotate-180',
icon: 'transition-transform group-data-[state=closed]:hover:rotate-180 group-data-[state=open]:rotate-180',
utxos: 'bg-gray-2 mx-4 py-3 px-4 rounded',
},
});
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
import type { GetLastTransactionsQuery } from '@fuel-explorer/graphql';
import { Grid } from '@fuels/ui';

import type { TransactionNode } from '../../types';
import { TxCard } from '../TxCard/TxCard';

type TxListProps = {
transactions: TransactionNode[];
transactions: GetLastTransactionsQuery['transactions']['edges'];
};

export function TxList({ transactions = [] }: TxListProps) {
return (
<Grid columns="3" gap="6">
{transactions.map((transaction) => (
<TxCard key={transaction.id} transaction={transaction} />
<TxCard key={transaction.node.id} transaction={transaction.node} />
))}
</Grid>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
'use client';

import { bn } from '@fuel-ts/math';
import type { BaseProps, CardProps, BoxProps } from '@fuels/ui';
import {
Expand Down Expand Up @@ -101,7 +103,7 @@ export const TxSummaryDetails = createComponent<
iconSize={24}
leftIcon={IconGasStation}
>
{bn(tx.gasUsed).format({ units: 3 })} ETH
{bn(tx.gasUsed).format()} ETH
</Text>
</TxSummaryRow>
</Card.Body>
Expand Down Expand Up @@ -144,7 +146,7 @@ export const TxSummary = withNamespace(TxSummaryRoot, {

const styles = tv({
slots: {
root: 'grid grid-cols-[2fr,1fr]',
root: 'grid grid-cols-[2fr,1fr] gap-6',
details: 'p-6',
params: 'p-6 fuel-[Text]:text-lg',
row: 'grid grid-cols-[100px,1fr] gap-8 items-center',
Expand Down
Loading

0 comments on commit f9e6fb8

Please sign in to comment.