Skip to content

Commit

Permalink
[Dashboard] Feature: RPC Chart (#5504)
Browse files Browse the repository at this point in the history
CNCT-2395

<img width="1161" alt="Screenshot 2024-11-22 at 9 52 09 PM" src="https://github.com/user-attachments/assets/e46f1645-11bb-42d5-abcf-147852a3e8ce">

<!-- start pr-codex -->

---

## PR-Codex overview
This PR primarily focuses on refactoring the analytics data handling by shifting the source of types from the `@/api/analytics` module to a new `types/analytics` module. This change enhances type definitions and improves code organization.

### Detailed summary
- Changed import paths for various analytics types to `types/analytics`.
- Added new interfaces in `apps/dashboard/src/types/analytics.ts` for `WalletStats`, `WalletUserStats`, `InAppWalletStats`, `EcosystemWalletStats`, `UserOpStats`, `RpcMethodStats`, and `AnalyticsQueryParams`.
- Updated multiple components and utilities to utilize the new type definitions.
- Implemented a new RPC method usage function in `apps/dashboard/src/@/api/analytics.ts`.
- Added `RpcMethodBarChartCard` component to visualize RPC method usage in `apps/dashboard/src/app/team/[team_slug]/[project_slug]/page.tsx`.

> ✨ Ask PR-Codex anything about this PR by commenting with `/codex {your question}`

<!-- end pr-codex -->
  • Loading branch information
gregfromstl committed Nov 24, 2024
1 parent 7dd4298 commit dfc824d
Show file tree
Hide file tree
Showing 26 changed files with 406 additions and 75 deletions.
85 changes: 47 additions & 38 deletions apps/dashboard/src/@/api/analytics.ts
Original file line number Diff line number Diff line change
@@ -1,42 +1,12 @@
import { fetchAnalytics } from "data/analytics/fetch-analytics";

export interface WalletStats {
date: string;
uniqueWalletsConnected: number;
totalConnections: number;
walletType: string;
}

export interface WalletUserStats {
date: string;
newUsers: number;
returningUsers: number;
totalUsers: number;
}

export interface InAppWalletStats {
date: string;
authenticationMethod: string;
uniqueWalletsConnected: number;
}

export interface EcosystemWalletStats extends InAppWalletStats {}

export interface UserOpStats {
date: string;
successful: number;
failed: number;
sponsoredUsd: number;
chainId?: string;
}

interface AnalyticsQueryParams {
clientId?: string;
accountId?: string;
from?: Date;
to?: Date;
period?: "day" | "week" | "month" | "year" | "all";
}
import type {
AnalyticsQueryParams,
InAppWalletStats,
RpcMethodStats,
UserOpStats,
WalletStats,
WalletUserStats,
} from "types/analytics";

function buildSearchParams(params: AnalyticsQueryParams): URLSearchParams {
const searchParams = new URLSearchParams();
Expand Down Expand Up @@ -115,6 +85,27 @@ export async function getUserOpUsage(
return json.data as UserOpStats[];
}

export async function getRpcMethodUsage(
params: AnalyticsQueryParams,
): Promise<RpcMethodStats[]> {
const searchParams = buildSearchParams(params);
const res = await fetchAnalytics(
`v1/rpc/evm-methods?${searchParams.toString()}`,
{
method: "GET",
headers: { "Content-Type": "application/json" },
},
);

if (res?.status !== 200) {
console.error("Failed to fetch RPC method usage");
return [];
}

const json = await res.json();
return json.data as RpcMethodStats[];
}

export async function getWalletUsers(
params: AnalyticsQueryParams,
): Promise<WalletUserStats[]> {
Expand All @@ -135,3 +126,21 @@ export async function getWalletUsers(
const json = await res.json();
return json.data as WalletUserStats[];
}

export async function isProjectActive(
params: AnalyticsQueryParams,
): Promise<boolean> {
const searchParams = buildSearchParams(params);
const res = await fetchAnalytics(`v1/active?${searchParams.toString()}`, {
method: "GET",
headers: { "Content-Type": "application/json" },
});

if (res?.status !== 200) {
console.error("Failed to fetch project active status");
return false;
}

const json = await res.json();
return json.data.isActive as boolean;
}
2 changes: 1 addition & 1 deletion apps/dashboard/src/@3rdweb-sdk/react/hooks/useApi.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import type { UserOpStats } from "@/api/analytics";
import type { Team } from "@/api/team";
import {
type Query,
Expand All @@ -9,6 +8,7 @@ import {
import { THIRDWEB_ANALYTICS_API_HOST, THIRDWEB_API_HOST } from "constants/urls";
import { useAllChainsData } from "hooks/chains/allChains";
import invariant from "tiny-invariant";
import type { UserOpStats } from "types/analytics";
import { accountKeys, apiKeys, authorizedWallets } from "../cache-keys";
import { useLoggedInUser } from "./useLoggedInUser";

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { UserOpStats } from "@/api/analytics";
import { cn } from "@/lib/utils";
import { defineChain } from "thirdweb";
import { type ChainMetadata, getChainMetadata } from "thirdweb/chains";
import type { UserOpStats } from "types/analytics";
import { EmptyAccountAbstractionChartContent } from "../../../../../components/smart-wallets/AccountAbstractionAnalytics/SponsoredTransactionsChartCard";
import { BarChart } from "../../../components/Analytics/BarChart";
import { CombinedBarChartCard } from "../../../components/Analytics/CombinedBarChartCard";
Expand Down
2 changes: 1 addition & 1 deletion apps/dashboard/src/app/team/[team_slug]/(team)/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import type {
InAppWalletStats,
WalletStats,
WalletUserStats,
} from "@/api/analytics";
} from "types/analytics";

import {
type DurationId,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
"use client";
import type { EcosystemWalletStats } from "@/api/analytics";
import { ExportToCSVButton } from "@/components/blocks/ExportToCSVButton";
import {
type ChartConfig,
Expand All @@ -21,6 +20,7 @@ import { format } from "date-fns";
import { formatTickerNumber } from "lib/format-utils";
import { useMemo } from "react";
import { Bar, BarChart, CartesianGrid, XAxis, YAxis } from "recharts";
import type { EcosystemWalletStats } from "types/analytics";

type ChartData = Record<string, number> & {
time: string; // human readable date
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { EcosystemWalletStats } from "@/api/analytics";
import { Stat } from "components/analytics/stat";
import { ActivityIcon, UserIcon } from "lucide-react";
import type { EcosystemWalletStats } from "types/analytics";

export function EcosystemWalletsSummary(props: {
allTimeStats: EcosystemWalletStats[] | undefined;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
"use client";
import type { EcosystemWalletStats } from "@/api/analytics";
import { CopyButton } from "@/components/ui/CopyButton";
import { Spinner } from "@/components/ui/Spinner/Spinner";
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
Expand All @@ -24,6 +23,7 @@ import {
} from "lucide-react";
import Image from "next/image";
import Link from "next/link";
import type { EcosystemWalletStats } from "types/analytics";
import { useEcosystemList } from "../../../hooks/use-ecosystem-list";
import type { Ecosystem } from "../../../types";
import { EcosystemWalletsSummary } from "../analytics/components/Summary";
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import type { Meta, StoryObj } from "@storybook/react";
import { BadgeContainer, mobileViewport } from "stories/utils";
import type { RpcMethodStats } from "types/analytics";
import { RpcMethodBarChartCardUI } from "./RpcMethodBarChartCardUI";

const meta = {
title: "Analytics/RpcMethodBarChartCard",
component: Component,
parameters: {
layout: "centered",
},
} satisfies Meta<typeof Component>;

export default meta;
type Story = StoryObj<typeof meta>;

export const Desktop: Story = {
parameters: {
viewport: { defaultViewport: "desktop" },
},
};

export const Mobile: Story = {
parameters: {
viewport: mobileViewport("iphone14"),
},
};

const generateTimeSeriesData = (
days: number,
methods: string[],
emptyData = false,
) => {
const data: RpcMethodStats[] = [];
const today = new Date();

for (let i = days - 1; i >= 0; i--) {
const date = new Date(today);
date.setDate(date.getDate() - i);

for (const method of methods) {
data.push({
date: date.toISOString(),
evmMethod: method,
count: emptyData ? 0 : Math.floor(Math.random() * 1000) + 100,
});
}
}

return data;
};

const commonMethods = [
"eth_call",
"eth_getBalance",
"eth_getTransactionReceipt",
"eth_blockNumber",
];

function Component() {
return (
<div className="container space-y-8 py-8">
<BadgeContainer label="Normal Usage">
<RpcMethodBarChartCardUI
rawData={generateTimeSeriesData(30, commonMethods)}
/>
</BadgeContainer>

<BadgeContainer label="Empty Data">
<RpcMethodBarChartCardUI rawData={[]} />
</BadgeContainer>

<BadgeContainer label="Zero Values">
<RpcMethodBarChartCardUI
rawData={generateTimeSeriesData(30, commonMethods, true)}
/>
</BadgeContainer>

<BadgeContainer label="Single Method">
<RpcMethodBarChartCardUI
rawData={generateTimeSeriesData(30, ["eth_call"])}
/>
</BadgeContainer>
</div>
);
}
Loading

0 comments on commit dfc824d

Please sign in to comment.