Skip to content

Commit 7619e6c

Browse files
committed
[MNY-304] Move engine tx summary request client side to fix page crash
1 parent 11c7bf0 commit 7619e6c

File tree

8 files changed

+290
-320
lines changed

8 files changed

+290
-320
lines changed
Lines changed: 78 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,92 @@
1-
import { ResponsiveSearchParamsProvider } from "responsive-rsc";
1+
"use client";
2+
import { useQuery } from "@tanstack/react-query";
3+
import { Spinner } from "@workspace/ui/components/spinner";
24
import type { ThirdwebClient } from "thirdweb";
35
import type { Project } from "@/api/project/projects";
46
import { UnifiedTransactionsTable } from "../components/transactions-table.client";
7+
import { getTransactionAnalyticsSummary } from "../lib/analytics-summary.client";
58
import type { Wallet } from "../server-wallets/wallet-table/types";
6-
import { TransactionAnalyticsFilter } from "./filter";
7-
import { TransactionsChartCard } from "./tx-chart/tx-chart";
9+
import type { SolanaWallet } from "../solana-wallets/wallet-table/types";
10+
import { EngineChecklist } from "./ftux.client";
11+
import { TransactionAnalyticsSummary } from "./summary";
12+
import { TransactionsAnalytics } from "./tx-chart/tx-chart";
813

914
export function TransactionsAnalyticsPageContent(props: {
10-
searchParams: {
11-
from?: string | undefined | string[];
12-
to?: string | undefined | string[];
13-
interval?: string | undefined | string[];
14-
};
1515
project: Project;
1616
showAnalytics: boolean;
17-
wallets?: Wallet[];
17+
wallets: Wallet[];
1818
teamSlug: string;
1919
client: ThirdwebClient;
20+
authToken: string;
21+
teamId: string;
22+
isManagedVault: boolean;
23+
testTxWithWallet: string | undefined;
24+
testSolanaTxWithWallet: string | undefined;
25+
solanaWallets: SolanaWallet[];
2026
}) {
21-
return (
22-
<ResponsiveSearchParamsProvider value={props.searchParams}>
23-
<div className="flex grow flex-col gap-6">
24-
{props.showAnalytics && (
25-
<>
26-
<div className="flex justify-end">
27-
<TransactionAnalyticsFilter />
28-
</div>
29-
<TransactionsChartCard
30-
project={props.project}
31-
searchParams={props.searchParams}
32-
teamSlug={props.teamSlug}
33-
wallets={props.wallets ?? []}
34-
/>
35-
</>
36-
)}
37-
<UnifiedTransactionsTable
38-
client={props.client}
39-
project={props.project}
40-
teamSlug={props.teamSlug}
41-
/>
27+
const dataQuery = useQuery({
28+
queryKey: [
29+
"transaction-analytics-summary",
30+
props.teamId,
31+
props.project.publishableKey,
32+
],
33+
queryFn: async () => {
34+
const data = await getTransactionAnalyticsSummary({
35+
clientId: props.project.publishableKey,
36+
teamId: props.teamId,
37+
authToken: props.authToken,
38+
});
39+
return data;
40+
},
41+
});
42+
43+
if (dataQuery.isPending) {
44+
return (
45+
<div className="flex h-[642px] grow items-center justify-center bg-card rounded-xl border">
46+
<Spinner className="size-10" />
4247
</div>
43-
</ResponsiveSearchParamsProvider>
48+
);
49+
}
50+
51+
const hasTransactions = dataQuery.data
52+
? dataQuery.data.totalCount > 0
53+
: false;
54+
55+
return (
56+
<div className="flex grow flex-col gap-10">
57+
<EngineChecklist
58+
isManagedVault={props.isManagedVault}
59+
client={props.client}
60+
hasTransactions={hasTransactions}
61+
project={props.project}
62+
teamSlug={props.teamSlug}
63+
testTxWithWallet={props.testTxWithWallet}
64+
testSolanaTxWithWallet={props.testSolanaTxWithWallet}
65+
wallets={props.wallets}
66+
solanaWallets={props.solanaWallets}
67+
/>
68+
69+
{props.showAnalytics && hasTransactions && (
70+
<div className="flex flex-col gap-6">
71+
<TransactionAnalyticsSummary
72+
clientId={props.project.publishableKey}
73+
teamId={props.project.teamId}
74+
initialData={dataQuery.data}
75+
/>
76+
<TransactionsAnalytics
77+
project={props.project}
78+
authToken={props.authToken}
79+
teamSlug={props.teamSlug}
80+
wallets={props.wallets ?? []}
81+
/>
82+
</div>
83+
)}
84+
85+
<UnifiedTransactionsTable
86+
client={props.client}
87+
project={props.project}
88+
teamSlug={props.teamSlug}
89+
/>
90+
</div>
4491
);
4592
}

apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/transactions/analytics/filter.tsx

Lines changed: 0 additions & 51 deletions
This file was deleted.

apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/transactions/analytics/summary.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { ActivityIcon, CoinsIcon } from "lucide-react";
22
import { toEther } from "thirdweb/utils";
33
import { StatCard } from "@/components/analytics/stat"; // Assuming correct path
4-
import type { TransactionSummaryData } from "../lib/analytics";
4+
import type { TransactionSummaryData } from "../lib/analytics-summary.client";
55

66
// Renders the UI based on fetched data or pending state
77
function TransactionAnalyticsSummaryUI(props: {
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
"use client";
2+
import { NEXT_PUBLIC_ENGINE_CLOUD_URL } from "@/constants/public-envs";
3+
import type { TransactionStats } from "@/types/analytics";
4+
5+
export async function getTransactionsChartData({
6+
teamId,
7+
clientId,
8+
from,
9+
to,
10+
interval,
11+
authToken,
12+
}: {
13+
teamId: string;
14+
clientId: string;
15+
from: string;
16+
to: string;
17+
interval: "day" | "week";
18+
authToken: string;
19+
}): Promise<TransactionStats[]> {
20+
const filters = {
21+
endDate: to,
22+
resolution: interval,
23+
startDate: from,
24+
};
25+
26+
const response = await fetch(
27+
`${NEXT_PUBLIC_ENGINE_CLOUD_URL}/v1/transactions/analytics`,
28+
{
29+
body: JSON.stringify(filters),
30+
headers: {
31+
Authorization: `Bearer ${authToken}`,
32+
"Content-Type": "application/json",
33+
"x-client-id": clientId,
34+
"x-team-id": teamId,
35+
},
36+
method: "POST",
37+
},
38+
);
39+
40+
if (!response.ok) {
41+
if (response.status === 401 || response.status === 400) {
42+
return [];
43+
}
44+
45+
// TODO - need to handle this error state, like we do with the connect charts
46+
throw new Error(
47+
`Error fetching transactions chart data: ${response.status} ${
48+
response.statusText
49+
} - ${await response.text().catch(() => "Unknown error")}`,
50+
);
51+
}
52+
53+
type TransactionsChartResponse = {
54+
result: {
55+
analytics: Array<{
56+
timeBucket: string;
57+
chainId: string;
58+
count: number;
59+
}>;
60+
metadata: {
61+
resolution: string;
62+
startDate: string;
63+
endDate: string;
64+
};
65+
};
66+
};
67+
68+
const data = (await response.json()) as TransactionsChartResponse;
69+
70+
return data.result.analytics.map((stat) => ({
71+
chainId: Number(stat.chainId),
72+
count: stat.count,
73+
date: stat.timeBucket,
74+
}));
75+
}
Lines changed: 46 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1,73 +1,64 @@
1-
import { ResponsiveSuspense } from "responsive-rsc";
1+
"use client";
2+
import { useQuery } from "@tanstack/react-query";
3+
import { useState } from "react";
24
import type { Project } from "@/api/project/projects";
3-
import { getTransactionsChart } from "../../lib/analytics";
4-
import { getTxAnalyticsFiltersFromSearchParams } from "../../lib/utils";
5+
import {
6+
DateRangeSelector,
7+
getLastNDaysRange,
8+
} from "@/components/analytics/date-range-selector";
9+
import { IntervalSelector } from "@/components/analytics/interval-selector";
10+
import { normalizeTimeISOString } from "@/lib/time";
511
import type { Wallet } from "../../server-wallets/wallet-table/types";
12+
import { getTransactionsChartData } from "./data";
613
import { TransactionsChartCardUI } from "./tx-chart-ui";
714

8-
async function AsyncTransactionsChartCard(props: {
9-
from: string;
10-
to: string;
11-
interval: "day" | "week";
15+
export function TransactionsAnalytics(props: {
1216
project: Project;
1317
wallets: Wallet[];
18+
authToken: string;
1419
teamSlug: string;
1520
}) {
16-
const data = await getTransactionsChart({
17-
clientId: props.project.publishableKey,
18-
from: props.from,
19-
interval: props.interval,
20-
teamId: props.project.teamId,
21-
to: props.to,
21+
const [range, setRange] = useState(() => getLastNDaysRange("last-30"));
22+
const [interval, setInterval] = useState<"day" | "week">("day");
23+
24+
const dataQuery = useQuery({
25+
queryKey: ["transactions-chart", props],
26+
queryFn: async () => {
27+
const data = await getTransactionsChartData({
28+
clientId: props.project.publishableKey,
29+
from: normalizeTimeISOString(range.from),
30+
interval: interval,
31+
teamId: props.project.teamId,
32+
to: normalizeTimeISOString(range.to),
33+
authToken: props.authToken,
34+
});
35+
return data;
36+
},
2237
});
2338

2439
return (
25-
<TransactionsChartCardUI
26-
isPending={false}
27-
project={props.project}
28-
teamSlug={props.teamSlug}
29-
userOpStats={data}
30-
wallets={props.wallets}
31-
/>
32-
);
33-
}
34-
35-
export function TransactionsChartCard(props: {
36-
searchParams: {
37-
from?: string | undefined | string[];
38-
to?: string | undefined | string[];
39-
interval?: string | undefined | string[];
40-
};
41-
project: Project;
42-
wallets: Wallet[];
43-
teamSlug: string;
44-
}) {
45-
const { range, interval } = getTxAnalyticsFiltersFromSearchParams(
46-
props.searchParams,
47-
);
40+
<div className="flex flex-col gap-6">
41+
<div className="flex justify-end">
42+
<div className="no-scrollbar flex items-center gap-3 max-sm:overflow-auto">
43+
<DateRangeSelector
44+
popoverAlign="end"
45+
range={range}
46+
setRange={setRange}
47+
/>
4848

49-
return (
50-
<ResponsiveSuspense
51-
// TODO - change this if this component does not end up using these params
52-
fallback={
53-
<TransactionsChartCardUI
54-
isPending={true}
55-
project={props.project}
56-
teamSlug={props.teamSlug}
57-
userOpStats={[]}
58-
wallets={[]}
59-
/>
60-
}
61-
searchParamsUsed={["from", "to", "interval"]}
62-
>
63-
<AsyncTransactionsChartCard
64-
from={range.from.toISOString()}
65-
interval={interval}
49+
<IntervalSelector
50+
intervalType={interval}
51+
setIntervalType={setInterval}
52+
/>
53+
</div>
54+
</div>
55+
<TransactionsChartCardUI
56+
isPending={dataQuery.isPending}
6657
project={props.project}
6758
teamSlug={props.teamSlug}
68-
to={range.to.toISOString()}
59+
userOpStats={dataQuery.data ?? []}
6960
wallets={props.wallets}
7061
/>
71-
</ResponsiveSuspense>
62+
</div>
7263
);
7364
}

0 commit comments

Comments
 (0)