From b511a695f9c62501a719dcad6f8bec195c581b75 Mon Sep 17 00:00:00 2001 From: MananTank Date: Thu, 16 Jan 2025 01:52:15 +0000 Subject: [PATCH] [TOOL-3056]: Add Engine transactions bar chart, Make shadcn table header sticky (#5963) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## PR-Codex overview This PR focuses on enhancing the user interface and functionality of various components in the dashboard application, particularly around data visualization and table displays. ### Detailed summary - Updated `chartClassName` for better aspect ratio in multiple chart components. - Added `tableScrollableClassName` and `tableContainerClassName` to improve table layouts. - Modified text in `engine-overview.tsx` for clarity. - Introduced new props in `ThirdwebBarChart` for customizable tooltips and titles. - Implemented data processing logic in `TransactionsTable` for analytics display. > ✨ Ask PR-Codex anything about this PR by commenting with `/codex {your question}` --- .../@/components/blocks/charts/bar-chart.tsx | 20 +++++- apps/dashboard/src/@/components/ui/table.tsx | 17 ++++- .../analytics/ContractAnalyticsPage.tsx | 10 +-- .../overview/components/Analytics.tsx | 2 +- .../components/backend-wallets-table.tsx | 1 + .../overview/components/engine-overview.tsx | 3 +- .../components/transactions-table.tsx | 71 ++++++++++++++++++- .../src/components/shared/TWTable.tsx | 7 +- 8 files changed, 115 insertions(+), 16 deletions(-) diff --git a/apps/dashboard/src/@/components/blocks/charts/bar-chart.tsx b/apps/dashboard/src/@/components/blocks/charts/bar-chart.tsx index 3adbe34988a..678b46f2c26 100644 --- a/apps/dashboard/src/@/components/blocks/charts/bar-chart.tsx +++ b/apps/dashboard/src/@/components/blocks/charts/bar-chart.tsx @@ -23,6 +23,7 @@ import { EmptyChartState, LoadingChartState, } from "../../../../components/analytics/empty-chart-state"; +import { cn } from "../../../lib/utils"; type ThirdwebBarChartProps = { // metadata @@ -36,6 +37,9 @@ type ThirdwebBarChartProps = { // chart className chartClassName?: string; isPending: boolean; + toolTipLabelFormatter?: (label: string, payload: unknown) => React.ReactNode; + hideLabel?: boolean; + titleClassName?: string; }; export function ThirdwebBarChart( @@ -45,10 +49,13 @@ export function ThirdwebBarChart( // if there are more than 4 keys then we should stack them by default const variant = props.variant || configKeys.length > 4 ? "stacked" : "grouped"; + return ( - {props.title} + + {props.title} + {props.description && ( {props.description} )} @@ -69,7 +76,16 @@ export function ThirdwebBarChart( tickMargin={10} tickFormatter={(value) => formatDate(new Date(value), "MMM d")} /> - } /> + + } + /> {props.showLegend && ( } /> )} diff --git a/apps/dashboard/src/@/components/ui/table.tsx b/apps/dashboard/src/@/components/ui/table.tsx index 13f4012d9ce..aad6ec54bb9 100644 --- a/apps/dashboard/src/@/components/ui/table.tsx +++ b/apps/dashboard/src/@/components/ui/table.tsx @@ -25,7 +25,8 @@ const TableHeader = React.forwardRef< {props.children} diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/analytics/ContractAnalyticsPage.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/analytics/ContractAnalyticsPage.tsx index a92870bf4cd..ffb3a3cf732 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/analytics/ContractAnalyticsPage.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/analytics/ContractAnalyticsPage.tsx @@ -136,7 +136,7 @@ function UniqueWalletsChart(props: ChartProps) { color: "hsl(var(--chart-1))", }, }} - chartClassName="aspect[2] lg:aspect-[4.5]" + chartClassName="aspect-[1.5] lg:aspect-[4.5]" /> ); } @@ -161,7 +161,7 @@ function TotalTransactionsChart(props: ChartProps) { color: "hsl(var(--chart-1))", }, }} - chartClassName="aspect[2] lg:aspect-[4.5]" + chartClassName="aspect-[1.5] lg:aspect-[4.5]" /> ); } @@ -181,7 +181,7 @@ function TotalEventsChart(props: ChartProps) { color: "hsl(var(--chart-1))", }, }} - chartClassName="aspect[2] lg:aspect-[4.5]" + chartClassName="aspect-[1.5] lg:aspect-[4.5]" /> ); } @@ -233,7 +233,7 @@ function FunctionBreakdownChart( }, {} as Record, )} - chartClassName="aspect[2] lg:aspect-[4.5]" + chartClassName="aspect-[1.5] lg:aspect-[4.5]" showLegend /> ); @@ -286,7 +286,7 @@ function EventBreakdownChart( }, {} as Record, )} - chartClassName="aspect[2] lg:aspect-[4.5]" + chartClassName="aspect-[1.5] lg:aspect-[4.5]" showLegend /> ); diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/overview/components/Analytics.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/overview/components/Analytics.tsx index 23b98786e3b..67a61bdc566 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/overview/components/Analytics.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/overview/components/Analytics.tsx @@ -135,7 +135,7 @@ function OverviewAnalytics(props: ChartProps) { data={mergedData || []} isPending={isPending} showLegend - chartClassName="aspect[2] lg:aspect-[4.5]" + chartClassName="aspect-[1.5] lg:aspect-[4.5]" /> ); } diff --git a/apps/dashboard/src/app/team/[team_slug]/(team)/~/engine/(instance)/[engineId]/overview/components/backend-wallets-table.tsx b/apps/dashboard/src/app/team/[team_slug]/(team)/~/engine/(instance)/[engineId]/overview/components/backend-wallets-table.tsx index a17ff3405e8..c2f96627af7 100644 --- a/apps/dashboard/src/app/team/[team_slug]/(team)/~/engine/(instance)/[engineId]/overview/components/backend-wallets-table.tsx +++ b/apps/dashboard/src/app/team/[team_slug]/(team)/~/engine/(instance)/[engineId]/overview/components/backend-wallets-table.tsx @@ -181,6 +181,7 @@ export const BackendWalletsTable: React.FC = ({ columns={columns as ColumnDef[]} isPending={isPending} isFetched={isFetched} + tableScrollableClassName="max-h-[1000px]" onMenuClick={[ { icon: , diff --git a/apps/dashboard/src/app/team/[team_slug]/(team)/~/engine/(instance)/[engineId]/overview/components/engine-overview.tsx b/apps/dashboard/src/app/team/[team_slug]/(team)/~/engine/(instance)/[engineId]/overview/components/engine-overview.tsx index ad81ba142d3..73616accc7d 100644 --- a/apps/dashboard/src/app/team/[team_slug]/(team)/~/engine/(instance)/[engineId]/overview/components/engine-overview.tsx +++ b/apps/dashboard/src/app/team/[team_slug]/(team)/~/engine/(instance)/[engineId]/overview/components/engine-overview.tsx @@ -149,8 +149,7 @@ function TransactionsSection(props: { Transactions

- View transactions sent from your backend wallets in the past 24 - hours. + View transactions sent from your backend wallets

diff --git a/apps/dashboard/src/app/team/[team_slug]/(team)/~/engine/(instance)/[engineId]/overview/components/transactions-table.tsx b/apps/dashboard/src/app/team/[team_slug]/(team)/~/engine/(instance)/[engineId]/overview/components/transactions-table.tsx index 72df90c251c..2d232c3cd8f 100644 --- a/apps/dashboard/src/app/team/[team_slug]/(team)/~/engine/(instance)/[engineId]/overview/components/transactions-table.tsx +++ b/apps/dashboard/src/app/team/[team_slug]/(team)/~/engine/(instance)/[engineId]/overview/components/transactions-table.tsx @@ -1,7 +1,9 @@ +import { ThirdwebBarChart } from "@/components/blocks/charts/bar-chart"; import { WalletAddress } from "@/components/blocks/wallet-address"; import { CopyAddressButton } from "@/components/ui/CopyAddressButton"; import { CopyTextButton } from "@/components/ui/CopyTextButton"; import { Badge } from "@/components/ui/badge"; +import type { ChartConfig } from "@/components/ui/chart"; import { Sheet, SheetContent, @@ -20,10 +22,10 @@ import { import { createColumnHelper } from "@tanstack/react-table"; import { ChainIcon } from "components/icons/ChainIcon"; import { formatDistanceToNowStrict } from "date-fns"; -import { format } from "date-fns/format"; +import { format, formatDate } from "date-fns/format"; import { useAllChainsData } from "hooks/chains/allChains"; import { InfoIcon, MoveLeftIcon, MoveRightIcon } from "lucide-react"; -import { type Dispatch, type SetStateAction, useState } from "react"; +import { type Dispatch, type SetStateAction, useMemo, useState } from "react"; import { toTokens } from "thirdweb"; import { Button, Card, FormLabel, LinkButton, Text } from "tw-components"; import { TWTable } from "../../../../../../../../../../components/shared/TWTable"; @@ -256,14 +258,79 @@ export const TransactionsTable: React.FC = ({ ? transactions.indexOf(selectedTransaction) : 0; + const { analyticsData, chartConfig } = useMemo(() => { + const dayToTxCountMap: Map> = new Map(); + const uniqueStatuses = new Set(); + + for (const tx of transactions) { + if (!tx.queuedAt || !tx.status) { + continue; + } + const normalizedDate = new Date(tx.queuedAt); + normalizedDate.setHours(0, 0, 0, 0); // normalize time + const time = normalizedDate.getTime(); + const entry = dayToTxCountMap.get(time) ?? {}; + entry[tx.status] = (entry[tx.status] ?? 0) + 1; + uniqueStatuses.add(tx.status); + dayToTxCountMap.set(time, entry); + } + + const analyticsData = Array.from(dayToTxCountMap.entries()) + .map(([day, record]) => ({ + time: new Date(day).getTime(), + ...record, + })) + .sort((a, b) => a.time - b.time); + + const chartConfig: ChartConfig = {}; + Array.from(uniqueStatuses).forEach((status, i) => { + chartConfig[status] = { + label: status.charAt(0).toUpperCase() + status.slice(1), // first letter uppercase + color: + status === "errored" + ? "hsl(var(--destructive-text))" + : status === "mined" + ? "hsl(var(--success-text))" + : `hsl(var(--chart-${(i % 15) + 3}))`, + }; + }); + + return { + analyticsData, + chartConfig, + }; + }, [transactions]); + return ( <> + { + if (Array.isArray(item)) { + const time = item[0].payload.time as number; + return formatDate(new Date(time), "MMM d, yyyy"); + } + return undefined; + }} + /> + +
+ { setSelectedTransaction(row); transactionDisclosure.onOpen(); diff --git a/apps/dashboard/src/components/shared/TWTable.tsx b/apps/dashboard/src/components/shared/TWTable.tsx index e8305cf7208..d76bfd2e0f3 100644 --- a/apps/dashboard/src/components/shared/TWTable.tsx +++ b/apps/dashboard/src/components/shared/TWTable.tsx @@ -56,6 +56,8 @@ type TWTableProps = { title: string; bodyRowClassName?: string; bodyRowLinkBox?: boolean; + tableContainerClassName?: string; + tableScrollableClassName?: string; }; export function TWTable(tableProps: TWTableProps) { @@ -124,7 +126,10 @@ export function TWTable(tableProps: TWTableProps) { }); return ( - + {table.getHeaderGroups().map((headerGroup) => (