diff --git a/apps/dashboard/src/@3rdweb-sdk/react/hooks/useApi.ts b/apps/dashboard/src/@3rdweb-sdk/react/hooks/useApi.ts index b6461cf3cca..06376c7e75b 100644 --- a/apps/dashboard/src/@3rdweb-sdk/react/hooks/useApi.ts +++ b/apps/dashboard/src/@3rdweb-sdk/react/hooks/useApi.ts @@ -235,12 +235,6 @@ export interface WalletStats { walletType: string; } -export interface InAppWalletStats { - date: string; - authenticationMethod: string; - uniqueWalletsConnected: number; -} - export interface UserOpStats { date: string; successful: number; diff --git a/apps/dashboard/src/app/(dashboard)/dashboard/connect/in-app-wallets/[clientId]/_components/tabs.tsx b/apps/dashboard/src/app/(dashboard)/dashboard/connect/in-app-wallets/[clientId]/_components/tabs.tsx deleted file mode 100644 index 234bf1fe346..00000000000 --- a/apps/dashboard/src/app/(dashboard)/dashboard/connect/in-app-wallets/[clientId]/_components/tabs.tsx +++ /dev/null @@ -1,32 +0,0 @@ -"use client"; -import { TabLinks } from "@/components/ui/tabs"; -import { usePathname } from "next/navigation"; - -export function Tabs({ clientId }: { clientId: string }) { - const path = usePathname(); - - return ( - - ); -} diff --git a/apps/dashboard/src/app/(dashboard)/dashboard/connect/in-app-wallets/[clientId]/_constants.tsx b/apps/dashboard/src/app/(dashboard)/dashboard/connect/in-app-wallets/[clientId]/_constants.tsx deleted file mode 100644 index 37103397a9c..00000000000 --- a/apps/dashboard/src/app/(dashboard)/dashboard/connect/in-app-wallets/[clientId]/_constants.tsx +++ /dev/null @@ -1 +0,0 @@ -export const TRACKING_CATEGORY = "embedded-wallet"; diff --git a/apps/dashboard/src/app/(dashboard)/dashboard/connect/in-app-wallets/[clientId]/analytics/page.tsx b/apps/dashboard/src/app/(dashboard)/dashboard/connect/in-app-wallets/[clientId]/analytics/page.tsx deleted file mode 100644 index cca2aa5301d..00000000000 --- a/apps/dashboard/src/app/(dashboard)/dashboard/connect/in-app-wallets/[clientId]/analytics/page.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import type { Range } from "components/analytics/date-range-selector"; -import { InAppWalletAnalytics } from "components/embedded-wallets/Analytics"; - -export default function Page({ - params, - searchParams, -}: { - params: { team_slug: string; project_slug: string }; - searchParams: { - from?: string; - to?: string; - type?: string; - interval?: string; - }; -}) { - const range = - searchParams.from && searchParams.to - ? { - type: searchParams.type ?? "last-120", - from: new Date(searchParams.from), - to: new Date(searchParams.to), - } - : undefined; - - const interval: "day" | "week" = ["day", "week"].includes( - searchParams.interval ?? "", - ) - ? (searchParams.interval as "day" | "week") - : "week"; - return ( - - ); -} diff --git a/apps/dashboard/src/app/(dashboard)/dashboard/connect/in-app-wallets/[clientId]/config/page.tsx b/apps/dashboard/src/app/(dashboard)/dashboard/connect/in-app-wallets/[clientId]/config/page.tsx deleted file mode 100644 index 46637440e6d..00000000000 --- a/apps/dashboard/src/app/(dashboard)/dashboard/connect/in-app-wallets/[clientId]/config/page.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import { InAppWalletSettingsPage } from "components/embedded-wallets/Configure"; -import { redirect } from "next/navigation"; -import { getInAppWalletSupportedAPIKeys } from "../../getInAppWalletSupportedAPIKeys"; -import { TRACKING_CATEGORY } from "../_constants"; - -export default async function Page({ - params: { clientId }, -}: { params: { clientId: string } }) { - const apiKeys = await getInAppWalletSupportedAPIKeys(); - const apiKey = apiKeys.find((key) => key.key === clientId); - - if (!apiKey) { - redirect("/dashboard/connect/in-app-wallets"); - } - return ( - - ); -} diff --git a/apps/dashboard/src/app/(dashboard)/dashboard/connect/in-app-wallets/[clientId]/layout.tsx b/apps/dashboard/src/app/(dashboard)/dashboard/connect/in-app-wallets/[clientId]/layout.tsx deleted file mode 100644 index 272d12ba9f4..00000000000 --- a/apps/dashboard/src/app/(dashboard)/dashboard/connect/in-app-wallets/[clientId]/layout.tsx +++ /dev/null @@ -1,78 +0,0 @@ -import { InAppWalletsSummary } from "components/embedded-wallets/Analytics/Summary"; -import { getInAppWalletUsage } from "data/analytics/wallets/in-app"; -import { redirect } from "next/navigation"; -import { getAuthToken } from "../../../../../api/lib/getAuthToken"; -import { PageHeader } from "../PageHeader"; -import { getInAppWalletSupportedAPIKeys } from "../getInAppWalletSupportedAPIKeys"; -import { InAppWalletsAPIKeysMenu } from "../inAppWalletsAPIKeysMenu"; -import { Tabs } from "./_components/tabs"; - -export default async function Page(props: { - params: { - clientId: string; - }; - searchParams: { - tab?: string; - }; - children: React.ReactNode; -}) { - const authToken = getAuthToken(); - const { clientId } = props.params; - - if (!authToken) { - redirect( - `/login?next=${encodeURIComponent(`/dashboard/connect/in-app-wallets/${clientId}`)}`, - ); - } - - const apiKeys = await getInAppWalletSupportedAPIKeys(); - const apiKey = apiKeys.find((key) => key.key === clientId); - - if (!apiKey) { - redirect("/dashboard/connect/in-app-wallets"); - } - - 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 ( -
- {/* header */} -
- -
- ({ - name: x.name, - key: x.key, - }))} - selectedAPIKey={apiKey} - /> -
-
- -
- - - -
- -
- {props.children} -
- ); -} diff --git a/apps/dashboard/src/app/(dashboard)/dashboard/connect/in-app-wallets/[clientId]/page.tsx b/apps/dashboard/src/app/(dashboard)/dashboard/connect/in-app-wallets/[clientId]/page.tsx index c6bf1a1a74a..8e0bb1cacb1 100644 --- a/apps/dashboard/src/app/(dashboard)/dashboard/connect/in-app-wallets/[clientId]/page.tsx +++ b/apps/dashboard/src/app/(dashboard)/dashboard/connect/in-app-wallets/[clientId]/page.tsx @@ -1,4 +1,9 @@ import { redirect } from "next/navigation"; +import { EmbeddedWallets } from "../../../../../../components/embedded-wallets"; +import { getAuthToken } from "../../../../../api/lib/getAuthToken"; +import { PageHeader } from "../PageHeader"; +import { getInAppWalletSupportedAPIKeys } from "../getInAppWalletSupportedAPIKeys"; +import { InAppWalletsAPIKeysMenu } from "../inAppWalletsAPIKeysMenu"; export default async function Page(props: { params: { @@ -8,7 +13,44 @@ export default async function Page(props: { tab?: string; }; }) { + const authToken = getAuthToken(); const { clientId } = props.params; - redirect(`/dashboard/connect/in-app-wallets/${clientId}/analytics`); + if (!authToken) { + redirect( + `/login?next=${encodeURIComponent(`/dashboard/connect/in-app-wallets/${clientId}`)}`, + ); + } + + const apiKeys = await getInAppWalletSupportedAPIKeys(); + const apiKey = apiKeys.find((key) => key.key === clientId); + + if (!apiKey) { + redirect("/dashboard/connect/in-app-wallets"); + } + + return ( +
+ {/* header */} +
+ +
+ ({ + name: x.name, + key: x.key, + }))} + selectedAPIKey={apiKey} + /> +
+
+ +
+ +
+ ); } diff --git a/apps/dashboard/src/app/(dashboard)/dashboard/connect/in-app-wallets/[clientId]/users/page.tsx b/apps/dashboard/src/app/(dashboard)/dashboard/connect/in-app-wallets/[clientId]/users/page.tsx deleted file mode 100644 index d174bb901fe..00000000000 --- a/apps/dashboard/src/app/(dashboard)/dashboard/connect/in-app-wallets/[clientId]/users/page.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import { InAppWalletUsersPageContent } from "components/embedded-wallets/Users"; -import { redirect } from "next/navigation"; -import { getInAppWalletSupportedAPIKeys } from "../../getInAppWalletSupportedAPIKeys"; -import { TRACKING_CATEGORY } from "../_constants"; - -export default async function Page(props: { - params: { - clientId: string; - }; -}) { - const { clientId } = props.params; - const apiKeys = await getInAppWalletSupportedAPIKeys(); - const apiKey = apiKeys.find((key) => key.key === clientId); - - if (!apiKey) { - redirect("/dashboard/connect/in-app-wallets"); - } - return ( - - ); -} diff --git a/apps/dashboard/src/app/(dashboard)/dashboard/connect/in-app-wallets/layout.tsx b/apps/dashboard/src/app/(dashboard)/dashboard/connect/in-app-wallets/layout.tsx index 2c760b46800..2c9758c30e2 100644 --- a/apps/dashboard/src/app/(dashboard)/dashboard/connect/in-app-wallets/layout.tsx +++ b/apps/dashboard/src/app/(dashboard)/dashboard/connect/in-app-wallets/layout.tsx @@ -1,3 +1,4 @@ +import { AnalyticsCallout } from "../../../../team/[team_slug]/[project_slug]/connect/in-app-wallets/_components/AnalyticsCallout"; import { InAppWaletFooterSection } from "../../../../team/[team_slug]/[project_slug]/connect/in-app-wallets/_components/footer"; export default function Layout(props: { @@ -8,6 +9,7 @@ export default function Layout(props: { {props.children}
{/* Footer */} +
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 new file mode 100644 index 00000000000..2022c9f0c51 --- /dev/null +++ b/apps/dashboard/src/app/team/[team_slug]/[project_slug]/connect/in-app-wallets/_components/AnalyticsCallout.tsx @@ -0,0 +1,79 @@ +"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 deleted file mode 100644 index e3887007cdb..00000000000 --- a/apps/dashboard/src/app/team/[team_slug]/[project_slug]/connect/in-app-wallets/_components/header.tsx +++ /dev/null @@ -1,46 +0,0 @@ -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 - -

- -
- ); -} diff --git a/apps/dashboard/src/app/team/[team_slug]/[project_slug]/connect/in-app-wallets/_components/tabs.tsx b/apps/dashboard/src/app/team/[team_slug]/[project_slug]/connect/in-app-wallets/_components/tabs.tsx deleted file mode 100644 index 07de192b944..00000000000 --- a/apps/dashboard/src/app/team/[team_slug]/[project_slug]/connect/in-app-wallets/_components/tabs.tsx +++ /dev/null @@ -1,35 +0,0 @@ -"use client"; -import { TabLinks } from "@/components/ui/tabs"; -import { usePathname } from "next/navigation"; - -export function Tabs({ - team_slug, - project_slug, -}: { team_slug: string; project_slug: string }) { - const path = usePathname(); - - return ( - - ); -} diff --git a/apps/dashboard/src/app/team/[team_slug]/[project_slug]/connect/in-app-wallets/_constants.ts b/apps/dashboard/src/app/team/[team_slug]/[project_slug]/connect/in-app-wallets/_constants.ts deleted file mode 100644 index f50b20dd636..00000000000 --- a/apps/dashboard/src/app/team/[team_slug]/[project_slug]/connect/in-app-wallets/_constants.ts +++ /dev/null @@ -1 +0,0 @@ -export const TRACKING_CATEGORY = "team/in-app-wallets"; diff --git a/apps/dashboard/src/app/team/[team_slug]/[project_slug]/connect/in-app-wallets/analytics/page.tsx b/apps/dashboard/src/app/team/[team_slug]/[project_slug]/connect/in-app-wallets/analytics/page.tsx deleted file mode 100644 index a97022b14fd..00000000000 --- a/apps/dashboard/src/app/team/[team_slug]/[project_slug]/connect/in-app-wallets/analytics/page.tsx +++ /dev/null @@ -1,38 +0,0 @@ -import type { Range } from "components/analytics/date-range-selector"; -import { InAppWalletAnalytics } from "components/embedded-wallets/Analytics"; - -export default function Page({ - params, - searchParams, -}: { - params: { team_slug: string; project_slug: string }; - searchParams: { - from?: string; - to?: string; - type?: string; - interval?: string; - }; -}) { - const range = - searchParams.from && searchParams.to - ? { - type: searchParams.type ?? "last-120", - from: new Date(searchParams.from), - to: new Date(searchParams.to), - } - : undefined; - - const interval: "day" | "week" = ["day", "week"].includes( - searchParams.interval ?? "", - ) - ? (searchParams.interval as "day" | "week") - : "week"; - - return ( - - ); -} diff --git a/apps/dashboard/src/app/team/[team_slug]/[project_slug]/connect/in-app-wallets/config/page.tsx b/apps/dashboard/src/app/team/[team_slug]/[project_slug]/connect/in-app-wallets/config/page.tsx deleted file mode 100644 index 5b273c4fde9..00000000000 --- a/apps/dashboard/src/app/team/[team_slug]/[project_slug]/connect/in-app-wallets/config/page.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import { getProject } from "@/api/projects"; -import { getAPIKeyForProjectId } from "app/api/lib/getAPIKeys"; -import { notFound } from "next/navigation"; -import { InAppWalletSettingsPage } from "../../../../../../../components/embedded-wallets/Configure"; -import { TRACKING_CATEGORY } from "../_constants"; - -export default async function Page({ - params, -}: { params: { team_slug: string; project_slug: string } }) { - const project = await getProject(params.team_slug, params.project_slug); - - if (!project) { - notFound(); - } - - const apiKey = await getAPIKeyForProjectId(project.id); - - if (!apiKey) { - notFound(); - } - - return ( - <> - - - ); -} diff --git a/apps/dashboard/src/app/team/[team_slug]/[project_slug]/connect/in-app-wallets/layout.tsx b/apps/dashboard/src/app/team/[team_slug]/[project_slug]/connect/in-app-wallets/layout.tsx deleted file mode 100644 index ff42ad03171..00000000000 --- a/apps/dashboard/src/app/team/[team_slug]/[project_slug]/connect/in-app-wallets/layout.tsx +++ /dev/null @@ -1,49 +0,0 @@ -import { getProject } from "@/api/projects"; -import { getAPIKeyForProjectId } from "app/api/lib/getAPIKeys"; -import { notFound } from "next/navigation"; -import { InAppWaletFooterSection } from "./_components/footer"; -import { InAppWalletsHeader } from "./_components/header"; -import { Tabs } from "./_components/tabs"; -import { TRACKING_CATEGORY } from "./_constants"; - -export default async function Layout(props: { - params: { - team_slug: string; - project_slug: string; - }; - children: React.ReactNode; -}) { - const project = await getProject( - props.params.team_slug, - props.params.project_slug, - ); - if (!project) { - notFound(); - } - - const apiKey = await getAPIKeyForProjectId(project.id); - if (!apiKey) { - notFound(); - } - - return ( -
- - -
- - - -
- - {props.children} - -
- - -
- ); -} diff --git a/apps/dashboard/src/app/team/[team_slug]/[project_slug]/connect/in-app-wallets/page.tsx b/apps/dashboard/src/app/team/[team_slug]/[project_slug]/connect/in-app-wallets/page.tsx index 811e88fcf48..1b79776584b 100644 --- a/apps/dashboard/src/app/team/[team_slug]/[project_slug]/connect/in-app-wallets/page.tsx +++ b/apps/dashboard/src/app/team/[team_slug]/[project_slug]/connect/in-app-wallets/page.tsx @@ -1,4 +1,9 @@ -import { redirect } from "next/navigation"; +import { getProject } from "@/api/projects"; +import { TrackedLinkTW } from "@/components/ui/tracked-link"; +import { notFound } from "next/navigation"; +import { InAppWalletUsersPageContent } from "../../../../../../components/embedded-wallets/Users"; +import { AnalyticsCallout } from "./_components/AnalyticsCallout"; +import { InAppWaletFooterSection } from "./_components/footer"; export default async function Page(props: { params: { @@ -6,8 +11,48 @@ export default async function Page(props: { project_slug: string; }; }) { - // Default to the users tab - redirect( - `/team/${props.params.team_slug}/${props.params.project_slug}/connect/in-app-wallets/analytics`, + const project = await getProject( + props.params.team_slug, + props.params.project_slug, + ); + + if (!project) { + notFound(); + } + + const TRACKING_CATEGORY = "team/in-app-wallets"; + + 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 + +

+ + + +
+ +
+ + +
); } 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 deleted file mode 100644 index 8b3683ae657..00000000000 --- a/apps/dashboard/src/app/team/[team_slug]/[project_slug]/connect/in-app-wallets/users/page.tsx +++ /dev/null @@ -1,15 +0,0 @@ -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 deleted file mode 100644 index 67ec0be8bea..00000000000 --- a/apps/dashboard/src/components/embedded-wallets/Analytics/InAppWalletUsersChartCard.tsx +++ /dev/null @@ -1,216 +0,0 @@ -"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 } from "recharts"; - -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()), - 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 }; - }} - /> -
- - {/* Chart */} - - {props.isPending ? ( - - ) : chartData.length === 0 || - chartData.every((data) => data.sponsoredUsd === 0) ? ( - -
- - Connect users to your app with social logins - -
- - - - - -
-
-
- ) : ( - - - - - - } /> - } /> - {uniqueAuthMethods.map((authMethod) => { - return ( - - ); - })} - - )} -
-
- ); -} diff --git a/apps/dashboard/src/components/embedded-wallets/Analytics/RangeSelector.tsx b/apps/dashboard/src/components/embedded-wallets/Analytics/RangeSelector.tsx deleted file mode 100644 index d929a5939fc..00000000000 --- a/apps/dashboard/src/components/embedded-wallets/Analytics/RangeSelector.tsx +++ /dev/null @@ -1,42 +0,0 @@ -"use client"; -import { useDashboardRouter } from "@/lib/DashboardRouter"; -import { DateRangeSelector } from "components/analytics/date-range-selector"; -import type { Range } from "components/analytics/date-range-selector"; -import { IntervalSelector } from "components/analytics/interval-selector"; -import { differenceInDays } from "date-fns"; -import { usePathname, useSearchParams } from "next/navigation"; - -export function RangeSelector({ - range, - interval, -}: { range: Range; interval: "day" | "week" }) { - const pathname = usePathname(); - const searchParams = useSearchParams(); - const router = useDashboardRouter(); - - return ( -
- { - const days = differenceInDays(newRange.to, newRange.from); - const interval = days > 30 ? "week" : "day"; - const newSearchParams = new URLSearchParams(searchParams || {}); - newSearchParams.set("from", newRange.from.toISOString()); - newSearchParams.set("to", newRange.to.toISOString()); - newSearchParams.set("type", newRange.type); - newSearchParams.set("interval", interval); - router.push(`${pathname}?${newSearchParams.toString()}`); - }} - /> - { - const newSearchParams = new URLSearchParams(searchParams || {}); - newSearchParams.set("interval", newInterval); - router.push(`${pathname}?${newSearchParams.toString()}`); - }} - /> -
- ); -} diff --git a/apps/dashboard/src/components/embedded-wallets/Analytics/Summary.tsx b/apps/dashboard/src/components/embedded-wallets/Analytics/Summary.tsx deleted file mode 100644 index d3438d293ff..00000000000 --- a/apps/dashboard/src/components/embedded-wallets/Analytics/Summary.tsx +++ /dev/null @@ -1,43 +0,0 @@ -import type { InAppWalletStats } from "@3rdweb-sdk/react/hooks/useApi"; -import { Stat } from "components/analytics/stat"; -import { ActivityIcon, UserIcon } from "lucide-react"; - -export function InAppWalletsSummary(props: { - allTimeStats: InAppWalletStats[]; - monthlyStats: InAppWalletStats[]; -}) { - const allTimeStats = props.allTimeStats.reduce( - (acc, curr) => { - acc.uniqueWalletsConnected += curr.uniqueWalletsConnected; - return acc; - }, - { - uniqueWalletsConnected: 0, - }, - ); - - const monthlyStats = props.monthlyStats.reduce( - (acc, curr) => { - acc.uniqueWalletsConnected += curr.uniqueWalletsConnected; - return acc; - }, - { - uniqueWalletsConnected: 0, - }, - ); - - return ( -
- - -
- ); -} diff --git a/apps/dashboard/src/components/embedded-wallets/Analytics/index.tsx b/apps/dashboard/src/components/embedded-wallets/Analytics/index.tsx deleted file mode 100644 index 6203c9d62b0..00000000000 --- a/apps/dashboard/src/components/embedded-wallets/Analytics/index.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import { - type Range, - getLastNDaysRange, -} from "components/analytics/date-range-selector"; -import { getInAppWalletUsage } from "data/analytics/wallets/in-app"; -import { InAppWalletUsersChartCard } from "./InAppWalletUsersChartCard"; -import { RangeSelector } from "./RangeSelector"; - -export async function InAppWalletAnalytics({ - clientId, - interval, - range, -}: { clientId: string; interval: "day" | "week"; range?: Range }) { - if (!range) { - range = getLastNDaysRange("last-120"); - } - - const stats = await getInAppWalletUsage({ - clientId, - from: range.from, - to: range.to, - period: interval, - }); - - return ( -
- - -
- -
- -
-
- ); -} diff --git a/apps/dashboard/src/components/embedded-wallets/Configure/index.tsx b/apps/dashboard/src/components/embedded-wallets/Configure/index.tsx index 3ba63154756..3873d8106a3 100644 --- a/apps/dashboard/src/components/embedded-wallets/Configure/index.tsx +++ b/apps/dashboard/src/components/embedded-wallets/Configure/index.tsx @@ -14,6 +14,7 @@ import { } from "@/components/ui/form"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; +import { Separator } from "@/components/ui/separator"; import { Textarea } from "@/components/ui/textarea"; import { TrackedLinkTW } from "@/components/ui/tracked-link"; import { cn } from "@/lib/utils"; @@ -229,7 +230,7 @@ export const InAppWalletSettingsUI: React.FC< canEditAdvancedFeatures={canEditAdvancedFeatures} /> -
+ ; + trackingCategory: string; + defaultTab: 0 | 1; +} + +export const EmbeddedWallets: React.FC = ({ + apiKey, + trackingCategory, + defaultTab, +}) => { + const [selectedTab, setSelectedTab] = useState<"users" | "config">( + defaultTab === 0 ? "users" : "config", + ); + + function updateSearchParams(value: string) { + const url = new URL(window.location.href); + url.searchParams.set("tab", value); + window.history.pushState(null, "", url.toString()); + } + + return ( +
+ { + setSelectedTab("users"); + updateSearchParams("0"); + }, + isActive: selectedTab === "users", + isEnabled: true, + }, + { + name: "Configuration", + onClick: () => { + setSelectedTab("config"); + updateSearchParams("1"); + }, + isActive: selectedTab === "config", + isEnabled: true, + }, + ]} + /> + +
+ + {selectedTab === "users" && ( + + )} + + {selectedTab === "config" && ( + + )} +
+ ); +}; diff --git a/apps/dashboard/src/data/analytics/fetch-analytics.ts b/apps/dashboard/src/data/analytics/fetch-analytics.ts deleted file mode 100644 index 03f78832617..00000000000 --- a/apps/dashboard/src/data/analytics/fetch-analytics.ts +++ /dev/null @@ -1,33 +0,0 @@ -import "server-only"; - -export async function fetchAnalytics(input: string | URL, init?: RequestInit) { - const [pathname, searchParams] = input.toString().split("?"); - if (!pathname) { - throw new Error("Invalid input, no pathname provided"); - } - - // create a new URL object for the analytics server - const API_SERVER_URL = new URL( - process.env.ANALYTICS_SERVICE_URL || "https://analytics.thirdweb.com", - ); - API_SERVER_URL.pathname = pathname; - for (const param of searchParams?.split("&") || []) { - const [key, value] = param.split("="); - if (!key || !value) { - return; - } - API_SERVER_URL.searchParams.append( - decodeURIComponent(key), - decodeURIComponent(value), - ); - } - - return await fetch(API_SERVER_URL, { - ...init, - headers: { - "content-type": "application/json", - authorization: `Bearer ${process.env.ANALYTICS_SERVICE_API_KEY}`, - ...init?.headers, - }, - }); -} diff --git a/apps/dashboard/src/data/analytics/wallets/in-app.ts b/apps/dashboard/src/data/analytics/wallets/in-app.ts deleted file mode 100644 index 0ebfb54585f..00000000000 --- a/apps/dashboard/src/data/analytics/wallets/in-app.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { fetchAnalytics } from "../fetch-analytics"; - -export async function getInAppWalletUsage(args: { - clientId: string; - from?: Date; - to?: Date; - period?: "day" | "week" | "month" | "year" | "all"; -}) { - const { clientId, from, to, period } = args; - - const searchParams = new URLSearchParams(); - searchParams.append("clientId", clientId); - if (from) { - searchParams.append("from", from.toISOString()); - } - if (to) { - searchParams.append("to", to.toISOString()); - } - if (period) { - searchParams.append("period", period); - } - const res = await fetchAnalytics( - `v1/wallets/in-app?${searchParams.toString()}`, - { - method: "GET", - headers: { - "Content-Type": "application/json", - }, - }, - ); - const json = await res?.json(); - - if (!res || res.status !== 200) { - throw new Error(json.message); - } - - return json.data; -}