Skip to content

Commit f638967

Browse files
committed
[NEB-95] Dashboard: Nebula Analytics UI updates
1 parent b7158c2 commit f638967

File tree

9 files changed

+316
-234
lines changed

9 files changed

+316
-234
lines changed
Lines changed: 96 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
"use client";
22

3+
import {
4+
Card,
5+
CardContent,
6+
CardDescription,
7+
CardHeader,
8+
CardTitle,
9+
} from "@/components/ui/card";
310
import {
411
type ChartConfig,
512
ChartContainer,
@@ -15,8 +22,14 @@ import {
1522
EmptyChartState,
1623
LoadingChartState,
1724
} from "../../../../components/analytics/empty-chart-state";
25+
import { cn } from "../../../lib/utils";
1826

1927
type ThirdwebAreaChartProps<TConfig extends ChartConfig> = {
28+
header?: {
29+
title: string;
30+
description?: string;
31+
titleClassName?: string;
32+
};
2033
// chart config
2134
config: TConfig;
2235
data: Array<Record<keyof TConfig, number> & { time: number | string | Date }>;
@@ -25,78 +38,99 @@ type ThirdwebAreaChartProps<TConfig extends ChartConfig> = {
2538
// chart className
2639
chartClassName?: string;
2740
isPending: boolean;
41+
hideLabel?: boolean;
42+
toolTipLabelFormatter?: (label: string, payload: unknown) => React.ReactNode;
2843
};
2944

3045
export function ThirdwebAreaChart<TConfig extends ChartConfig>(
3146
props: ThirdwebAreaChartProps<TConfig>,
3247
) {
3348
const configKeys = useMemo(() => Object.keys(props.config), [props.config]);
49+
3450
return (
35-
<div className="rounded-lg border border-border px-4 pt-10 pb-4">
36-
<ChartContainer config={props.config} className={props.chartClassName}>
37-
{props.isPending ? (
38-
<LoadingChartState />
39-
) : props.data.length === 0 ? (
40-
<EmptyChartState />
41-
) : (
42-
<AreaChart
43-
accessibilityLayer
44-
data={props.data}
45-
margin={{
46-
left: 12,
47-
right: 12,
48-
}}
49-
>
50-
<CartesianGrid vertical={false} />
51-
<XAxis
52-
dataKey="time"
53-
tickLine={false}
54-
axisLine={false}
55-
tickMargin={20}
56-
tickFormatter={(value) => formatDate(new Date(value), "MMM dd")}
57-
/>
58-
<ChartTooltip cursor={false} content={<ChartTooltipContent />} />
59-
<defs>
51+
<Card>
52+
{props.header && (
53+
<CardHeader>
54+
<CardTitle className={cn("mb-2", props.header.titleClassName)}>
55+
{props.header.title}
56+
</CardTitle>
57+
{props.header.description && (
58+
<CardDescription>{props.header.description}</CardDescription>
59+
)}
60+
</CardHeader>
61+
)}
62+
63+
<CardContent className={cn(!props.header && "pt-6")}>
64+
<ChartContainer config={props.config} className={props.chartClassName}>
65+
{props.isPending ? (
66+
<LoadingChartState />
67+
) : props.data.length === 0 ? (
68+
<EmptyChartState />
69+
) : (
70+
<AreaChart accessibilityLayer data={props.data}>
71+
<CartesianGrid vertical={false} />
72+
<XAxis
73+
dataKey="time"
74+
tickLine={false}
75+
axisLine={false}
76+
tickMargin={20}
77+
tickFormatter={(value) => formatDate(new Date(value), "MMM dd")}
78+
/>
79+
<ChartTooltip
80+
cursor={false}
81+
content={
82+
<ChartTooltipContent
83+
hideLabel={
84+
props.hideLabel !== undefined ? props.hideLabel : true
85+
}
86+
labelFormatter={props.toolTipLabelFormatter}
87+
/>
88+
}
89+
/>
90+
<defs>
91+
{configKeys.map((key) => (
92+
<linearGradient
93+
key={key}
94+
id={`fill_${key}`}
95+
x1="0"
96+
y1="0"
97+
x2="0"
98+
y2="1"
99+
>
100+
<stop
101+
offset="5%"
102+
stopColor={`var(--color-${key})`}
103+
stopOpacity={0.8}
104+
/>
105+
<stop
106+
offset="95%"
107+
stopColor={`var(--color-${key})`}
108+
stopOpacity={0.1}
109+
/>
110+
</linearGradient>
111+
))}
112+
</defs>
60113
{configKeys.map((key) => (
61-
<linearGradient
114+
<Area
62115
key={key}
63-
id={`fill_${key}`}
64-
x1="0"
65-
y1="0"
66-
x2="0"
67-
y2="1"
68-
>
69-
<stop
70-
offset="5%"
71-
stopColor={`var(--color-${key})`}
72-
stopOpacity={0.8}
73-
/>
74-
<stop
75-
offset="95%"
76-
stopColor={`var(--color-${key})`}
77-
stopOpacity={0.1}
78-
/>
79-
</linearGradient>
116+
dataKey={key}
117+
type="natural"
118+
fill={`url(#fill_${key})`}
119+
fillOpacity={0.4}
120+
stroke={`var(--color-${key})`}
121+
stackId="a"
122+
/>
80123
))}
81-
</defs>
82-
{configKeys.map((key) => (
83-
<Area
84-
key={key}
85-
dataKey={key}
86-
type="natural"
87-
fill={`url(#fill_${key})`}
88-
fillOpacity={0.4}
89-
stroke={`var(--color-${key})`}
90-
stackId="a"
91-
/>
92-
))}
93124

94-
{props.showLegend && (
95-
<ChartLegend content={<ChartLegendContent />} className="pt-8" />
96-
)}
97-
</AreaChart>
98-
)}
99-
</ChartContainer>
100-
</div>
125+
{props.showLegend && (
126+
<ChartLegend
127+
content={<ChartLegendContent className="pt-8" />}
128+
/>
129+
)}
130+
</AreaChart>
131+
)}
132+
</ChartContainer>
133+
</CardContent>
134+
</Card>
101135
);
102136
}

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

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,11 @@ import { cn } from "../../../lib/utils";
2727

2828
type ThirdwebBarChartProps<TConfig extends ChartConfig> = {
2929
// metadata
30-
title: string;
31-
description?: string;
30+
header?: {
31+
title: string;
32+
description?: string;
33+
titleClassName?: string;
34+
};
3235
// chart config
3336
config: TConfig;
3437
data: Array<Record<keyof TConfig, number> & { time: number | string | Date }>;
@@ -39,7 +42,6 @@ type ThirdwebBarChartProps<TConfig extends ChartConfig> = {
3942
isPending: boolean;
4043
toolTipLabelFormatter?: (label: string, payload: unknown) => React.ReactNode;
4144
hideLabel?: boolean;
42-
titleClassName?: string;
4345
};
4446

4547
export function ThirdwebBarChart<TConfig extends ChartConfig>(
@@ -52,15 +54,18 @@ export function ThirdwebBarChart<TConfig extends ChartConfig>(
5254

5355
return (
5456
<Card>
55-
<CardHeader>
56-
<CardTitle className={cn("mb-2", props.titleClassName)}>
57-
{props.title}
58-
</CardTitle>
59-
{props.description && (
60-
<CardDescription>{props.description}</CardDescription>
61-
)}
62-
</CardHeader>
63-
<CardContent>
57+
{props.header && (
58+
<CardHeader>
59+
<CardTitle className={cn("mb-2", props.header.titleClassName)}>
60+
{props.header.title}
61+
</CardTitle>
62+
{props.header.description && (
63+
<CardDescription>{props.header.description}</CardDescription>
64+
)}
65+
</CardHeader>
66+
)}
67+
68+
<CardContent className={cn(!props.header && "pt-6")}>
6469
<ChartContainer config={props.config} className={props.chartClassName}>
6570
{props.isPending ? (
6671
<LoadingChartState />
@@ -87,7 +92,9 @@ export function ThirdwebBarChart<TConfig extends ChartConfig>(
8792
}
8893
/>
8994
{props.showLegend && (
90-
<ChartLegend content={<ChartLegendContent />} />
95+
<ChartLegend
96+
content={<ChartLegendContent className="pt-5" />}
97+
/>
9198
)}
9299
{configKeys.map((key, idx) => (
93100
<Bar

apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/analytics/ContractAnalyticsPage.tsx

Lines changed: 26 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -126,8 +126,11 @@ function UniqueWalletsChart(props: ChartProps) {
126126

127127
return (
128128
<ThirdwebBarChart
129-
title="Unique Wallets"
130-
description="The number of unique wallet addresses that have sent a transaction to this contract."
129+
header={{
130+
title: "Unique Wallets",
131+
description:
132+
"The number of unique wallet addresses that have sent a transaction to this contract.",
133+
}}
131134
data={analyticsQuery.data || []}
132135
isPending={analyticsQuery.isPending}
133136
config={{
@@ -151,8 +154,11 @@ function TotalTransactionsChart(props: ChartProps) {
151154

152155
return (
153156
<ThirdwebBarChart
154-
title="Total Transactions"
155-
description="The number of transactions that have been sent to this contract."
157+
header={{
158+
title: "Total Transactions",
159+
description:
160+
"The number of transactions that have been sent to this contract.",
161+
}}
156162
data={analyticsQuery.data || []}
157163
isPending={analyticsQuery.isPending}
158164
config={{
@@ -171,8 +177,11 @@ function TotalEventsChart(props: ChartProps) {
171177

172178
return (
173179
<ThirdwebBarChart
174-
title="Total Events"
175-
description="The number of on-chain events that have been emitted from this contract."
180+
header={{
181+
title: "Total Events",
182+
description:
183+
"The number of on-chain events that have been emitted from this contract.",
184+
}}
176185
data={analyticsQuery.data || []}
177186
isPending={analyticsQuery.isPending}
178187
config={{
@@ -216,8 +225,11 @@ function FunctionBreakdownChart(
216225

217226
return (
218227
<ThirdwebBarChart
219-
title="Function Breakdown"
220-
description="The breakdown of calls to each write function from transactions."
228+
header={{
229+
title: "Function Breakdown",
230+
description:
231+
"The breakdown of calls to each write function from transactions.",
232+
}}
221233
data={mappedQueryData || []}
222234
isPending={analyticsQuery.isPending}
223235
config={Object.keys(mappedQueryData?.[0] || {}).reduce(
@@ -269,8 +281,10 @@ function EventBreakdownChart(
269281

270282
return (
271283
<ThirdwebBarChart
272-
title="Event Breakdown"
273-
description="The breakdown of events emitted by this contract."
284+
header={{
285+
title: "Event Breakdown",
286+
description: "The breakdown of events emitted by this contract.",
287+
}}
274288
data={mappedQueryData || []}
275289
isPending={analyticsQuery.isPending}
276290
config={Object.keys(mappedQueryData?.[0] || {}).reduce(
@@ -319,7 +333,7 @@ const AnalyticsStat: React.FC<AnalyticsStatProps> = ({
319333

320334
const AnalyticsSkeleton: React.FC<{ label: string }> = ({ label }) => {
321335
return (
322-
<Card as={Stat}>
336+
<Card as={Stat} className="bg-card">
323337
<StatLabel mb={{ base: 1, md: 0 }}>{label}</StatLabel>
324338
<Skeleton isLoaded={false}>
325339
<StatNumber>{0}</StatNumber>
@@ -350,7 +364,7 @@ const AnalyticsData: React.FC<AnalyticsStatProps> = ({
350364
}, [totalQuery.data]);
351365

352366
return (
353-
<Card as={Stat}>
367+
<Card as={Stat} className="bg-card">
354368
<StatLabel mb={{ base: 1, md: 0 }}>{label}</StatLabel>
355369
<Skeleton isLoaded={totalQuery.isFetched}>
356370
<StatNumber>{data.toLocaleString()}</StatNumber>

apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/overview/components/Analytics.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ function OverviewAnalytics(props: ChartProps) {
135135
data={mergedData || []}
136136
isPending={isPending}
137137
showLegend
138-
chartClassName="aspect-[1.5] lg:aspect-[4.5]"
138+
chartClassName="aspect-[1.5] lg:aspect-[3.5]"
139139
/>
140140
);
141141
}

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

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -532,13 +532,15 @@ export function TransactionCharts(props: {
532532

533533
return (
534534
<ThirdwebBarChart
535-
title="Transactions Breakdown"
536-
description="Transactions sent from your backend wallets per day"
535+
header={{
536+
title: "Transactions Breakdown",
537+
description: "Transactions sent from your backend wallets per day",
538+
titleClassName: "text-xl mb-0",
539+
}}
537540
config={chartConfig}
538541
data={analyticsData}
539542
isPending={transactionsQuery.isPending}
540543
chartClassName="aspect-[1.5] lg:aspect-[4.5]"
541-
titleClassName="text-xl mb-0"
542544
hideLabel={false}
543545
variant="stacked"
544546
showLegend

apps/dashboard/src/app/team/[team_slug]/(team)/~/nebula/page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { getTeamBySlug } from "@/api/team";
22
import { getValidAccount } from "../../../../../account/settings/getAccount";
33
import { getAuthToken } from "../../../../../api/lib/getAuthToken";
44
import { loginRedirect } from "../../../../../login/loginRedirect";
5-
import { NebulaAnalyticsPage } from "../../../[project_slug]/nebula/components/analytics/nebula-analytics-ui";
5+
import { NebulaAnalyticsPage } from "../../../[project_slug]/nebula/components/analytics/nebula-analytics-page";
66
import { NebulaWaitListPage } from "../../../[project_slug]/nebula/components/nebula-waitlist-page";
77

88
export default async function Page(props: {

0 commit comments

Comments
 (0)