Skip to content

Commit fe0ef7e

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

File tree

9 files changed

+295
-334
lines changed

9 files changed

+295
-334
lines changed
Lines changed: 81 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,95 @@
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 engineTxSummaryQuery = useQuery({
28+
queryKey: [
29+
"engine-tx-analytics-summary",
30+
props.teamId,
31+
props.project.publishableKey,
32+
props.authToken,
33+
],
34+
queryFn: async () => {
35+
const data = await getTransactionAnalyticsSummary({
36+
clientId: props.project.publishableKey,
37+
teamId: props.teamId,
38+
authToken: props.authToken,
39+
});
40+
return data;
41+
},
42+
refetchOnWindowFocus: false,
43+
refetchOnMount: false,
44+
});
45+
46+
if (engineTxSummaryQuery.isPending) {
47+
return (
48+
<div className="flex h-[642px] grow items-center justify-center bg-card rounded-xl border">
49+
<Spinner className="size-10" />
4250
</div>
43-
</ResponsiveSearchParamsProvider>
51+
);
52+
}
53+
54+
const hasTransactions = engineTxSummaryQuery.data
55+
? engineTxSummaryQuery.data.totalCount > 0
56+
: false;
57+
58+
return (
59+
<div className="flex grow flex-col gap-10">
60+
<EngineChecklist
61+
isManagedVault={props.isManagedVault}
62+
client={props.client}
63+
hasTransactions={hasTransactions}
64+
project={props.project}
65+
teamSlug={props.teamSlug}
66+
testTxWithWallet={props.testTxWithWallet}
67+
testSolanaTxWithWallet={props.testSolanaTxWithWallet}
68+
wallets={props.wallets}
69+
solanaWallets={props.solanaWallets}
70+
/>
71+
72+
{props.showAnalytics && hasTransactions && (
73+
<div className="flex flex-col gap-6">
74+
<TransactionAnalyticsSummary
75+
clientId={props.project.publishableKey}
76+
teamId={props.project.teamId}
77+
initialData={engineTxSummaryQuery.data}
78+
/>
79+
<TransactionsAnalytics
80+
project={props.project}
81+
authToken={props.authToken}
82+
teamSlug={props.teamSlug}
83+
wallets={props.wallets ?? []}
84+
/>
85+
</div>
86+
)}
87+
88+
<UnifiedTransactionsTable
89+
client={props.client}
90+
project={props.project}
91+
teamSlug={props.teamSlug}
92+
/>
93+
</div>
4494
);
4595
}

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: 48 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1,73 +1,66 @@
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 engineTxAnalytics = useQuery({
25+
queryKey: ["engine-tx-analytics", 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+
},
37+
refetchOnWindowFocus: false,
38+
refetchOnMount: false,
2239
});
2340

2441
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-
);
42+
<div className="flex flex-col gap-6">
43+
<div className="flex justify-end">
44+
<div className="no-scrollbar flex items-center gap-3 max-sm:overflow-auto">
45+
<DateRangeSelector
46+
popoverAlign="end"
47+
range={range}
48+
setRange={setRange}
49+
/>
4850

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}
51+
<IntervalSelector
52+
intervalType={interval}
53+
setIntervalType={setInterval}
54+
/>
55+
</div>
56+
</div>
57+
<TransactionsChartCardUI
58+
isPending={engineTxAnalytics.isPending}
6659
project={props.project}
6760
teamSlug={props.teamSlug}
68-
to={range.to.toISOString()}
61+
userOpStats={engineTxAnalytics.data ?? []}
6962
wallets={props.wallets}
7063
/>
71-
</ResponsiveSuspense>
64+
</div>
7265
);
7366
}

0 commit comments

Comments
 (0)