Skip to content

Commit 4d93210

Browse files
committed
[TOOL-3056]: Add Engine transactions bar chart, Make shadcn table header sticky
1 parent fdc528d commit 4d93210

File tree

6 files changed

+92
-10
lines changed

6 files changed

+92
-10
lines changed

apps/dashboard/src/@/components/blocks/charts/bar-chart.tsx

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import {
2323
EmptyChartState,
2424
LoadingChartState,
2525
} from "../../../../components/analytics/empty-chart-state";
26+
import { cn } from "../../../lib/utils";
2627

2728
type ThirdwebBarChartProps<TConfig extends ChartConfig> = {
2829
// metadata
@@ -36,6 +37,9 @@ type ThirdwebBarChartProps<TConfig extends ChartConfig> = {
3637
// chart className
3738
chartClassName?: string;
3839
isPending: boolean;
40+
toolTipLabelFormatter?: (label: string, payload: unknown) => React.ReactNode;
41+
hideLabel?: boolean;
42+
titleClassName?: string;
3943
};
4044

4145
export function ThirdwebBarChart<TConfig extends ChartConfig>(
@@ -45,10 +49,13 @@ export function ThirdwebBarChart<TConfig extends ChartConfig>(
4549
// if there are more than 4 keys then we should stack them by default
4650
const variant =
4751
props.variant || configKeys.length > 4 ? "stacked" : "grouped";
52+
4853
return (
4954
<Card>
5055
<CardHeader>
51-
<CardTitle className="mb-2">{props.title}</CardTitle>
56+
<CardTitle className={cn("mb-2", props.titleClassName)}>
57+
{props.title}
58+
</CardTitle>
5259
{props.description && (
5360
<CardDescription>{props.description}</CardDescription>
5461
)}
@@ -69,7 +76,16 @@ export function ThirdwebBarChart<TConfig extends ChartConfig>(
6976
tickMargin={10}
7077
tickFormatter={(value) => formatDate(new Date(value), "MMM d")}
7178
/>
72-
<ChartTooltip content={<ChartTooltipContent hideLabel />} />
79+
<ChartTooltip
80+
content={
81+
<ChartTooltipContent
82+
hideLabel={
83+
props.hideLabel !== undefined ? props.hideLabel : true
84+
}
85+
labelFormatter={props.toolTipLabelFormatter}
86+
/>
87+
}
88+
/>
7389
{props.showLegend && (
7490
<ChartLegend content={<ChartLegendContent />} />
7591
)}

apps/dashboard/src/@/components/ui/table.tsx

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@ const TableHeader = React.forwardRef<
2525
<thead
2626
ref={ref}
2727
className={cn(
28-
"border-border border-b bg-background [&_tr]:border-b",
28+
"border-border border-b bg-background",
29+
"sticky top-0 z-20 border-none before:absolute before:inset-0 before:border-border before:border-b",
2930
className,
3031
)}
3132
{...props}
@@ -125,10 +126,20 @@ const TableCaption = React.forwardRef<
125126
));
126127
TableCaption.displayName = "TableCaption";
127128

128-
function TableContainer(props: { children: React.ReactNode }) {
129+
function TableContainer(props: {
130+
children: React.ReactNode;
131+
className?: string;
132+
scrollableContainerClassName?: string;
133+
}) {
129134
return (
130135
<ScrollShadow
131-
className="relative whitespace-nowrap rounded-lg border border-border bg-card"
136+
shadowClassName="z-30"
137+
disableTopShadow
138+
className={cn(
139+
"relative whitespace-nowrap rounded-lg border border-border bg-card",
140+
props.className,
141+
)}
142+
scrollableClassName={props.scrollableContainerClassName}
132143
shadowColor="hsl(var(--muted))"
133144
>
134145
{props.children}

apps/dashboard/src/app/team/[team_slug]/(team)/~/engine/(instance)/[engineId]/overview/components/backend-wallets-table.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,7 @@ export const BackendWalletsTable: React.FC<BackendWalletsTableProps> = ({
181181
columns={columns as ColumnDef<BackendWallet, string>[]}
182182
isPending={isPending}
183183
isFetched={isFetched}
184+
tableScrollableClassName="max-h-[1000px]"
184185
onMenuClick={[
185186
{
186187
icon: <PencilIcon className="size-4" />,

apps/dashboard/src/app/team/[team_slug]/(team)/~/engine/(instance)/[engineId]/overview/components/engine-overview.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -149,8 +149,7 @@ function TransactionsSection(props: {
149149
Transactions
150150
</h2>
151151
<p className="text-muted-foreground">
152-
View transactions sent from your backend wallets in the past 24
153-
hours.
152+
View transactions sent from your backend wallets
154153
</p>
155154
</div>
156155

apps/dashboard/src/app/team/[team_slug]/(team)/~/engine/(instance)/[engineId]/overview/components/transactions-table.tsx

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { ThirdwebBarChart } from "@/components/blocks/charts/bar-chart";
12
import { WalletAddress } from "@/components/blocks/wallet-address";
23
import { CopyAddressButton } from "@/components/ui/CopyAddressButton";
34
import { CopyTextButton } from "@/components/ui/CopyTextButton";
@@ -20,10 +21,10 @@ import {
2021
import { createColumnHelper } from "@tanstack/react-table";
2122
import { ChainIcon } from "components/icons/ChainIcon";
2223
import { formatDistanceToNowStrict } from "date-fns";
23-
import { format } from "date-fns/format";
24+
import { format, formatDate } from "date-fns/format";
2425
import { useAllChainsData } from "hooks/chains/allChains";
2526
import { InfoIcon, MoveLeftIcon, MoveRightIcon } from "lucide-react";
26-
import { type Dispatch, type SetStateAction, useState } from "react";
27+
import { type Dispatch, type SetStateAction, useMemo, useState } from "react";
2728
import { toTokens } from "thirdweb";
2829
import { Button, Card, FormLabel, LinkButton, Text } from "tw-components";
2930
import { TWTable } from "../../../../../../../../../../components/shared/TWTable";
@@ -256,14 +257,63 @@ export const TransactionsTable: React.FC<TransactionsTableProps> = ({
256257
? transactions.indexOf(selectedTransaction)
257258
: 0;
258259

260+
const transactionAnalytics = useMemo(() => {
261+
const dayToTxCountMap: Map<number, number> = new Map();
262+
263+
for (const tx of transactions) {
264+
if (!tx.queuedAt) {
265+
continue;
266+
}
267+
const normalizedDate = new Date(tx.queuedAt);
268+
normalizedDate.setHours(0, 0, 0, 0); // normalize time
269+
const time = normalizedDate.getTime();
270+
const count = dayToTxCountMap.get(time) ?? 0;
271+
dayToTxCountMap.set(time, count + 1);
272+
}
273+
274+
return Array.from(dayToTxCountMap.entries())
275+
.map(([day, count]) => ({
276+
time: new Date(day).getTime(),
277+
count,
278+
}))
279+
.sort((a, b) => a.time - b.time);
280+
}, [transactions]);
281+
259282
return (
260283
<>
284+
<ThirdwebBarChart
285+
title="Daily Transactions"
286+
description="The number of transactions that have been sent from your backend wallets in the past 24 hours."
287+
config={{
288+
count: {
289+
label: "Transactions",
290+
color: "hsl(var(--chart-1))",
291+
},
292+
}}
293+
data={transactionAnalytics}
294+
isPending={isPending}
295+
chartClassName="aspect[2] lg:aspect-[4.5]"
296+
titleClassName="text-xl mb-0"
297+
hideLabel={false}
298+
toolTipLabelFormatter={(_v, item) => {
299+
if (Array.isArray(item)) {
300+
const time = item[0].payload.time as number;
301+
return formatDate(new Date(time), "MMM d, yyyy");
302+
}
303+
return undefined;
304+
}}
305+
/>
306+
307+
<div className="h-8" />
308+
261309
<TWTable
262310
title="transactions"
263311
data={transactions}
264312
columns={columns}
265313
isPending={isPending}
266314
isFetched={isFetched}
315+
bodyRowClassName="hover:bg-accent/50"
316+
tableScrollableClassName="max-h-[1000px]"
267317
onRowClick={(row) => {
268318
setSelectedTransaction(row);
269319
transactionDisclosure.onOpen();

apps/dashboard/src/components/shared/TWTable.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@ type TWTableProps<TRowData> = {
5656
title: string;
5757
bodyRowClassName?: string;
5858
bodyRowLinkBox?: boolean;
59+
tableContainerClassName?: string;
60+
tableScrollableClassName?: string;
5961
};
6062

6163
export function TWTable<TRowData>(tableProps: TWTableProps<TRowData>) {
@@ -124,7 +126,10 @@ export function TWTable<TRowData>(tableProps: TWTableProps<TRowData>) {
124126
});
125127

126128
return (
127-
<TableContainer>
129+
<TableContainer
130+
className={tableProps.tableContainerClassName}
131+
scrollableContainerClassName={tableProps.tableScrollableClassName}
132+
>
128133
<Table>
129134
<TableHeader>
130135
{table.getHeaderGroups().map((headerGroup) => (

0 commit comments

Comments
 (0)