{}}
+ range={getLastNDaysRange("last-120")}
+ intervalType="day"
+ setIntervalType={() => {}}
walletUsage={createWalletStatsStub(30)}
aggregateWalletUsage={createWalletStatsStub(30)}
aggregateUserOpUsageQuery={createUserOpStatsStub(1)?.[0]}
diff --git a/apps/dashboard/src/app/team/[team_slug]/[project_slug]/connect/analytics/_components/DailyConnectionsChartCard.tsx b/apps/dashboard/src/app/team/[team_slug]/[project_slug]/connect/analytics/_components/DailyConnectionsChartCard.tsx
index b8957cf9eec..2d116e87b5c 100644
--- a/apps/dashboard/src/app/team/[team_slug]/[project_slug]/connect/analytics/_components/DailyConnectionsChartCard.tsx
+++ b/apps/dashboard/src/app/team/[team_slug]/[project_slug]/connect/analytics/_components/DailyConnectionsChartCard.tsx
@@ -27,7 +27,15 @@ import { UnrealIcon } from "components/icons/brand-icons/UnrealIcon";
import { DocLink } from "components/shared/DocLink";
import { format } from "date-fns";
import { useMemo, useState } from "react";
-import { Bar, BarChart, CartesianGrid, LabelList, XAxis } from "recharts";
+import {
+ Bar,
+ BarChart,
+ CartesianGrid,
+ LabelList,
+ XAxis,
+ YAxis,
+} from "recharts";
+import { formatTickerNumber } from "../../../../../../../lib/format-utils";
type ChartToShow = "uniqueWallets" | "totalWallets";
@@ -194,7 +202,21 @@ export function DailyConnectionsChartCard(props: {
axisLine={false}
/>
- } />
+ data[chartToShow]}
+ tickLine={false}
+ axisLine={false}
+ tickFormatter={(value) => formatTickerNumber(value)}
+ />
+
+ formatTickerNumber(Number(value))}
+ />
+ }
+ />
formatTickerNumber(value)}
/>
)}
diff --git a/apps/dashboard/src/app/team/[team_slug]/[project_slug]/connect/analytics/_components/WalletConnectorsChartCard.tsx b/apps/dashboard/src/app/team/[team_slug]/[project_slug]/connect/analytics/_components/WalletConnectorsChartCard.tsx
index eeaa8be0380..259a89b9343 100644
--- a/apps/dashboard/src/app/team/[team_slug]/[project_slug]/connect/analytics/_components/WalletConnectorsChartCard.tsx
+++ b/apps/dashboard/src/app/team/[team_slug]/[project_slug]/connect/analytics/_components/WalletConnectorsChartCard.tsx
@@ -26,7 +26,11 @@ import { TypeScriptIcon } from "components/icons/brand-icons/TypeScriptIcon";
import { DocLink } from "components/shared/DocLink";
import { format } from "date-fns";
import { useMemo, useState } from "react";
-import { Bar, BarChart, CartesianGrid, XAxis } from "recharts";
+import { Bar, BarChart, CartesianGrid, XAxis, YAxis } from "recharts";
+import {
+ formatTickerNumber,
+ formatWalletType,
+} from "../../../../../../../lib/format-utils";
type ChartToShow = "uniqueWalletsConnected" | "totalConnections";
@@ -60,7 +64,8 @@ export function WalletConnectorsChartCard(props: {
// for each stat, add it in _chartDataMap
for (const stat of walletStats) {
const chartData = _chartDataMap.get(stat.date);
- const { walletType } = stat;
+ const { walletType: rawWalletType } = stat;
+ const walletType = formatWalletType(rawWalletType);
// if no data for current day - create new entry
if (!chartData) {
@@ -213,7 +218,26 @@ export function WalletConnectorsChartCard(props: {
axisLine={false}
/>
- } />
+
+ Object.entries(data)
+ .filter(([key]) => key !== "time")
+ .map(([, value]) => value)
+ .reduce((acc, current) => Number(acc) + Number(current), 0)
+ }
+ tickLine={false}
+ axisLine={false}
+ tickFormatter={(value) => formatTickerNumber(value)}
+ />
+
+ formatTickerNumber(Number(value))}
+ />
+ }
+ />
} />
{uniqueWalletTypes.map((walletType) => {
return (
diff --git a/apps/dashboard/src/app/team/[team_slug]/[project_slug]/connect/analytics/_components/WalletDistributionChartCard.tsx b/apps/dashboard/src/app/team/[team_slug]/[project_slug]/connect/analytics/_components/WalletDistributionChartCard.tsx
index 53ea056ad4f..700adfbced1 100644
--- a/apps/dashboard/src/app/team/[team_slug]/[project_slug]/connect/analytics/_components/WalletDistributionChartCard.tsx
+++ b/apps/dashboard/src/app/team/[team_slug]/[project_slug]/connect/analytics/_components/WalletDistributionChartCard.tsx
@@ -26,6 +26,10 @@ import { TypeScriptIcon } from "components/icons/brand-icons/TypeScriptIcon";
import { DocLink } from "components/shared/DocLink";
import { useMemo, useState } from "react";
import { Pie, PieChart } from "recharts";
+import {
+ formatTickerNumber,
+ formatWalletType,
+} from "../../../../../../../lib/format-utils";
type ChartToShow = "totalConnections" | "uniqueWalletsConnected";
@@ -63,7 +67,8 @@ export function WalletDistributionChartCard(props: {
let _uniqueConnections = 0;
for (const stat of walletStats) {
- const { walletType } = stat;
+ const { walletType: rawWalletType } = stat;
+ const walletType = formatWalletType(rawWalletType);
const chartData = _chartDataMap.get(walletType);
_totalConnections += stat.totalConnections;
@@ -235,7 +240,7 @@ export function WalletDistributionChartCard(props: {
? uniqueConnections
: totalConnections;
const percentageValue = ((v / sumValue) * 100).toFixed(2);
- return `${percentageValue}% - ${v}`;
+ return `${percentageValue}% - ${formatTickerNumber(v)}`;
}
}}
/>
diff --git a/apps/dashboard/src/app/team/[team_slug]/[project_slug]/connect/in-app-wallets/_components/AnalyticsCallout.tsx b/apps/dashboard/src/app/team/[team_slug]/[project_slug]/connect/in-app-wallets/_components/AnalyticsCallout.tsx
deleted file mode 100644
index 2022c9f0c51..00000000000
--- a/apps/dashboard/src/app/team/[team_slug]/[project_slug]/connect/in-app-wallets/_components/AnalyticsCallout.tsx
+++ /dev/null
@@ -1,79 +0,0 @@
-"use client";
-
-import { Button } from "@/components/ui/button";
-import { TrackedLinkTW } from "@/components/ui/tracked-link";
-import { useAccountUsage } from "@3rdweb-sdk/react/hooks/useApi";
-import { UsageCard } from "components/settings/Account/UsageCard";
-import { ArrowRightIcon } from "lucide-react";
-import { useMemo } from "react";
-import { toNumber, toPercent } from "utils/number";
-
-type AnalyticsCalloutProps = {
- trackingCategory: string;
-};
-
-export const AnalyticsCallout: React.FC = ({
- trackingCategory,
-}) => {
- const usageQuery = useAccountUsage();
-
- const walletsMetrics = useMemo(() => {
- if (!usageQuery?.data) {
- return undefined;
- }
-
- const usageData = usageQuery.data;
-
- const numOfWallets = usageData.usage.embeddedWallets.countWalletAddresses;
- const limitWallets = usageData.limits.embeddedWallets;
- const percent = toPercent(numOfWallets, limitWallets);
-
- return {
- total: `${toNumber(numOfWallets)} / ${toNumber(
- limitWallets,
- )} (${percent}%)`,
- progress: percent,
- ...(usageData.billableUsd.embeddedWallets > 0
- ? {
- overage: usageData.billableUsd.embeddedWallets,
- }
- : {}),
- };
- }, [usageQuery]);
-
- return (
-
- {/* Left */}
-
-
Analytics
-
- View more insights about how users are interacting with your
- application
-
-
-
-
-
- {/* Right */}
-
- {walletsMetrics && (
-
- )}
-
-
- );
-};
diff --git a/apps/dashboard/src/app/team/[team_slug]/[project_slug]/connect/in-app-wallets/_components/header.tsx b/apps/dashboard/src/app/team/[team_slug]/[project_slug]/connect/in-app-wallets/_components/header.tsx
new file mode 100644
index 00000000000..e3887007cdb
--- /dev/null
+++ b/apps/dashboard/src/app/team/[team_slug]/[project_slug]/connect/in-app-wallets/_components/header.tsx
@@ -0,0 +1,46 @@
+import { TrackedLinkTW } from "@/components/ui/tracked-link";
+import { InAppWalletsSummary } from "components/embedded-wallets/Analytics/Summary";
+import { getInAppWalletUsage } from "data/analytics/wallets/in-app";
+import { TRACKING_CATEGORY } from "../_constants";
+
+export async function InAppWalletsHeader({ clientId }: { clientId: string }) {
+ const allTimeStats = await getInAppWalletUsage({
+ clientId,
+ from: new Date(2022, 0, 1),
+ to: new Date(),
+ period: "all",
+ });
+
+ const monthlyStats = await getInAppWalletUsage({
+ clientId,
+ from: new Date(new Date().getFullYear(), new Date().getMonth(), 1),
+ to: new Date(),
+ period: "month",
+ });
+
+ return (
+
+
+ In-App Wallets
+
+
+ A wallet infrastructure that enables apps to create, manage, and control
+ their users wallets. Email login, social login, and bring-your-own auth
+ supported.{" "}
+
+ Learn more
+
+
- A wallet infrastructure that enables apps to create, manage, and control
- their users wallets. Email login, social login, and bring-your-own auth
- supported.{" "}
-
- Learn more
-
-
-
-
-
-
-
-
-
-
-
+ // Default to the users tab
+ redirect(
+ `/team/${props.params.team_slug}/${props.params.project_slug}/connect/in-app-wallets/analytics`,
);
}
diff --git a/apps/dashboard/src/app/team/[team_slug]/[project_slug]/connect/in-app-wallets/users/page.tsx b/apps/dashboard/src/app/team/[team_slug]/[project_slug]/connect/in-app-wallets/users/page.tsx
new file mode 100644
index 00000000000..8b3683ae657
--- /dev/null
+++ b/apps/dashboard/src/app/team/[team_slug]/[project_slug]/connect/in-app-wallets/users/page.tsx
@@ -0,0 +1,15 @@
+import { InAppWalletUsersPageContent } from "components/embedded-wallets/Users";
+import { TRACKING_CATEGORY } from "../_constants";
+
+export default function Page({
+ params,
+}: { params: { team_slug: string; project_slug: string } }) {
+ return (
+ <>
+
+ >
+ );
+}
diff --git a/apps/dashboard/src/components/embedded-wallets/Analytics/InAppWalletUsersChartCard.tsx b/apps/dashboard/src/components/embedded-wallets/Analytics/InAppWalletUsersChartCard.tsx
new file mode 100644
index 00000000000..79809049f6f
--- /dev/null
+++ b/apps/dashboard/src/components/embedded-wallets/Analytics/InAppWalletUsersChartCard.tsx
@@ -0,0 +1,238 @@
+"use client";
+import { ExportToCSVButton } from "@/components/blocks/ExportToCSVButton";
+import {
+ type ChartConfig,
+ ChartContainer,
+ ChartLegend,
+ ChartLegendContent,
+ ChartTooltip,
+ ChartTooltipContent,
+} from "@/components/ui/chart";
+import type { InAppWalletStats } from "@3rdweb-sdk/react/hooks/useApi";
+import {
+ EmptyChartState,
+ LoadingChartState,
+} from "components/analytics/empty-chart-state";
+import { ReactIcon } from "components/icons/brand-icons/ReactIcon";
+import { TypeScriptIcon } from "components/icons/brand-icons/TypeScriptIcon";
+import { UnityIcon } from "components/icons/brand-icons/UnityIcon";
+import { UnrealIcon } from "components/icons/brand-icons/UnrealIcon";
+import { DocLink } from "components/shared/DocLink";
+import { format } from "date-fns";
+import { useMemo } from "react";
+import { Bar, BarChart, CartesianGrid, XAxis, YAxis } from "recharts";
+import { formatTickerNumber } from "../../../lib/format-utils";
+
+type ChartData = Record & {
+ time: string; // human readable date
+};
+
+export function InAppWalletUsersChartCard(props: {
+ inAppWalletStats: InAppWalletStats[];
+ isPending: boolean;
+}) {
+ const { inAppWalletStats } = props;
+ const topChainsToShow = 10;
+
+ const { chartConfig, chartData } = useMemo(() => {
+ const _chartConfig: ChartConfig = {};
+ const _chartDataMap: Map = new Map();
+ const authMethodToVolumeMap: Map = new Map();
+ // for each stat, add it in _chartDataMap
+ for (const stat of inAppWalletStats) {
+ const chartData = _chartDataMap.get(stat.date);
+ const { authenticationMethod } = stat;
+
+ // if no data for current day - create new entry
+ if (!chartData && stat.uniqueWalletsConnected > 0) {
+ _chartDataMap.set(stat.date, {
+ time: format(new Date(stat.date), "MMM dd"),
+ [authenticationMethod || "Unknown"]: stat.uniqueWalletsConnected,
+ } as ChartData);
+ } else if (chartData) {
+ chartData[authenticationMethod || "Unknown"] =
+ (chartData[authenticationMethod || "Unknown"] || 0) +
+ stat.uniqueWalletsConnected;
+ }
+
+ authMethodToVolumeMap.set(
+ authenticationMethod || "Unknown",
+ stat.uniqueWalletsConnected +
+ (authMethodToVolumeMap.get(authenticationMethod || "Unknown") || 0),
+ );
+ }
+
+ const authMethodsSorted = Array.from(authMethodToVolumeMap.entries())
+ .sort((a, b) => b[1] - a[1])
+ .map((w) => w[0]);
+
+ const authMethodsToShow = authMethodsSorted.slice(0, topChainsToShow);
+ const authMethodsAsOther = authMethodsSorted.slice(topChainsToShow);
+
+ // replace chainIdsToTagAsOther chainId with "other"
+ for (const data of _chartDataMap.values()) {
+ for (const authMethod in data) {
+ if (authMethodsAsOther.includes(authMethod)) {
+ data.others = (data.others || 0) + (data[authMethod] || 0);
+ delete data[authMethod];
+ }
+ }
+ }
+
+ authMethodsToShow.forEach((walletType, i) => {
+ _chartConfig[walletType] = {
+ label: authMethodsToShow[i],
+ color: `hsl(var(--chart-${(i % 10) + 1}))`,
+ };
+ });
+
+ // Add Other
+ authMethodsToShow.push("others");
+ _chartConfig.others = {
+ label: "Others",
+ color: "hsl(var(--muted-foreground))",
+ };
+
+ return {
+ chartData: Array.from(_chartDataMap.values()).sort(
+ (a, b) => new Date(a.time).getTime() - new Date(b.time).getTime(),
+ ),
+ chartConfig: _chartConfig,
+ };
+ }, [inAppWalletStats]);
+
+ const uniqueAuthMethods = Object.keys(chartConfig);
+ const disableActions =
+ props.isPending ||
+ chartData.length === 0 ||
+ chartData.every((data) => data.sponsoredUsd === 0);
+
+ return (
+
+
+ Unique Users
+
+
+ The total number of active in-app wallet users on your project.
+
+
+
+ {
+ // Shows the number of each type of wallet connected on all dates
+ const header = ["Date", ...uniqueAuthMethods];
+ const rows = chartData.map((data) => {
+ const { time, ...rest } = data;
+ return [
+ time,
+ ...uniqueAuthMethods.map((w) => (rest[w] || 0).toString()),
+ ];
+ });
+ return { header, rows };
+ }}
+ />
+