Repository: thirdweb-dev/js
Pull request #6610: [TOOL-3762] Dashboard: Update In-app wallet analytics page
Author: login: MananTank
Labels: name: Dashboard
description: Involves changes to the Dashboard.
Created: 2025-04-01T14:48:07Z | Merged: 2025-04-02T07:18:36Z

<!-- start pr-codex -->

## PR-Codex overview
This PR focuses on enhancing the `InAppWallets` and `AccountAbstraction` components by refactoring code for better handling of loading states and data fetching, while also replacing `Stat` components with `StatCard` for improved UI consistency.

### Detailed summary
- Removed `loading.tsx` files for `in-app-wallets` and `account-abstraction`.
- Updated `InAppWalletsHeader` to remove props and simplify data fetching.
- Introduced `StatCard` in place of `Stat` for displaying statistics.
- Added `isPending` prop to `StatCard` for loading states.
- Refactored `InAppWalletsSummary` and `AccountAbstractionSummary` to use asynchronous data fetching with suspense.
- Modified `AccountAbstractionAnalytics` to remove unnecessary summary component.
- Enhanced `InAppWalletAnalytics` to manage loading state and data fetching more effectively.

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

<!-- end pr-codex -->


Diff:
diff --git a/apps/dashboard/src/app/team/[team_slug]/(team)/~/ecosystem/[slug]/(active)/analytics/components/Summary.tsx b/apps/dashboard/src/app/team/[team_slug]/(team)/~/ecosystem/[slug]/(active)/analytics/components/Summary.tsx
index bbd93ee53c6..8b08969fa17 100644
--- a/apps/dashboard/src/app/team/[team_slug]/(team)/~/ecosystem/[slug]/(active)/analytics/components/Summary.tsx
+++ b/apps/dashboard/src/app/team/[team_slug]/(team)/~/ecosystem/[slug]/(active)/analytics/components/Summary.tsx
@@ -1,4 +1,4 @@
-import { Stat } from "components/analytics/stat";
+import { StatCard } from "components/analytics/stat";
 import { ActivityIcon, UserIcon } from "lucide-react";
 import type { EcosystemWalletStats } from "types/analytics";
 
@@ -28,15 +28,17 @@ export function EcosystemWalletsSummary(props: {
 
   return (
     <div className="grid grid-cols-2 gap-4 lg:gap-6">
-      <Stat
+      <StatCard
         label="Total Users"
         value={allTimeStats?.uniqueWalletsConnected || 0}
         icon={ActivityIcon}
+        isPending={false}
       />
-      <Stat
+      <StatCard
         label="Monthly Active Users"
         value={monthlyStats?.uniqueWalletsConnected || 0}
         icon={UserIcon}
+        isPending={false}
       />
     </div>
   );
diff --git a/apps/dashboard/src/app/team/[team_slug]/[project_slug]/connect/account-abstraction/loading.tsx b/apps/dashboard/src/app/team/[team_slug]/[project_slug]/connect/account-abstraction/loading.tsx
deleted file mode 100644
index 6c54ef15def..00000000000
--- a/apps/dashboard/src/app/team/[team_slug]/[project_slug]/connect/account-abstraction/loading.tsx
+++ /dev/null
@@ -1,3 +0,0 @@
-"use client";
-
-export { GenericLoadingPage as default } from "@/components/blocks/skeletons/GenericLoadingPage";
diff --git a/apps/dashboard/src/app/team/[team_slug]/[project_slug]/connect/account-abstraction/page.tsx b/apps/dashboard/src/app/team/[team_slug]/[project_slug]/connect/account-abstraction/page.tsx
index c1c124ffc28..bb660eddb46 100644
--- a/apps/dashboard/src/app/team/[team_slug]/[project_slug]/connect/account-abstraction/page.tsx
+++ b/apps/dashboard/src/app/team/[team_slug]/[project_slug]/connect/account-abstraction/page.tsx
@@ -1,4 +1,4 @@
-import { getAggregateUserOpUsage, getUserOpUsage } from "@/api/analytics";
+import { getUserOpUsage } from "@/api/analytics";
 import { getProject } from "@/api/projects";
 import { getTeamBySlug } from "@/api/team";
 import { getThirdwebClient } from "@/constants/thirdweb.server";
@@ -9,6 +9,7 @@ import {
 import { AccountAbstractionAnalytics } from "components/smart-wallets/AccountAbstractionAnalytics";
 import { notFound, redirect } from "next/navigation";
 import type { SearchParams } from "nuqs/server";
+import { AccountAbstractionSummary } from "../../../../../../components/smart-wallets/AccountAbstractionAnalytics/AccountAbstractionSummary";
 import { getAuthToken } from "../../../../../api/lib/getAuthToken";
 import { searchParamLoader } from "./search-params";
 
@@ -60,7 +61,7 @@ export default async function Page(props: {
     type: rangeType,
   };
 
-  const userOpStatsPromise = getUserOpUsage({
+  const userOpStats = await getUserOpUsage({
     teamId: project.teamId,
     projectId: project.id,
     from: range.from,
@@ -68,24 +69,21 @@ export default async function Page(props: {
     period: interval,
   });
 
-  const aggregateUserOpStatsPromise = getAggregateUserOpUsage({
-    teamId: team.id,
-    projectId: project.id,
-  });
-
-  const [userOpStats, aggregateUserOpStats] = await Promise.all([
-    userOpStatsPromise,
-    aggregateUserOpStatsPromise,
-  ]);
-
   return (
-    <AccountAbstractionAnalytics
-      userOpStats={userOpStats}
-      client={getThirdwebClient(authToken)}
-      teamId={project.teamId}
-      projectId={project.id}
-      teamSlug={params.team_slug}
-      aggregateUserOpStats={aggregateUserOpStats}
-    />
+    <div>
+      <AccountAbstractionSummary
+        teamId={project.teamId}
+        projectId={project.id}
+      />
+
+      <div className="h-10" />
+      <AccountAbstractionAnalytics
+        userOpStats={userOpStats}
+        client={getThirdwebClient(authToken)}
+        teamId={project.teamId}
+        projectId={project.id}
+        teamSlug={params.team_slug}
+      />
+    </div>
   );
 }
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
index 1fa77895e03..4630e251eb4 100644
--- 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
@@ -1,34 +1,7 @@
-import { getInAppWalletUsage } from "@/api/analytics";
 import { TrackedUnderlineLink } from "@/components/ui/tracked-link";
-import { InAppWalletsSummary } from "components/embedded-wallets/Analytics/Summary";
-import { subDays } from "date-fns";
 import { TRACKING_CATEGORY } from "../_constants";
 
-export async function InAppWalletsHeader({
-  teamId,
-  projectId,
-}: { teamId: string; projectId: string }) {
-  const allTimeStatsPromise = getInAppWalletUsage({
-    teamId,
-    projectId,
-    from: new Date(2022, 0, 1),
-    to: new Date(),
-    period: "all",
-  });
-
-  const monthlyStatsPromise = getInAppWalletUsage({
-    teamId,
-    projectId,
-    from: subDays(new Date(), 30),
-    to: new Date(),
-    period: "month",
-  });
-
-  const [allTimeStats, monthlyStats] = await Promise.all([
-    allTimeStatsPromise,
-    monthlyStatsPromise,
-  ]).catch(() => [null, null]);
-
+export async function InAppWalletsHeader() {
   return (
     <div>
       <h1 className="mb-1 font-semibold text-2xl tracking-tight lg:mb-2 lg:text-3xl">
@@ -47,10 +20,6 @@ export async function InAppWalletsHeader({
           Learn more
         </TrackedUnderlineLink>
       </p>
-      <InAppWalletsSummary
-        allTimeStats={allTimeStats || []}
-        monthlyStats={monthlyStats || []}
-      />
     </div>
   );
 }
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
index 17c338f4274..4c1cdce9a3b 100644
--- 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
@@ -23,9 +23,7 @@ export default async function Layout(props: {
 
   return (
     <div>
-      <InAppWalletsHeader teamId={project.teamId} projectId={project.id} />
-      <div className="h-8" />
-
+      <InAppWalletsHeader />
       <TabPathLinks
         links={[
           {
diff --git a/apps/dashboard/src/app/team/[team_slug]/[project_slug]/connect/in-app-wallets/loading.tsx b/apps/dashboard/src/app/team/[team_slug]/[project_slug]/connect/in-app-wallets/loading.tsx
deleted file mode 100644
index 6c54ef15def..00000000000
--- a/apps/dashboard/src/app/team/[team_slug]/[project_slug]/connect/in-app-wallets/loading.tsx
+++ /dev/null
@@ -1,3 +0,0 @@
-"use client";
-
-export { GenericLoadingPage as default } from "@/components/blocks/skeletons/GenericLoadingPage";
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 babdd2da822..fa7aa16fcd6 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,7 +1,8 @@
+import { getProject } from "@/api/projects";
 import type { Range } from "components/analytics/date-range-selector";
 import { InAppWalletAnalytics } from "components/embedded-wallets/Analytics";
 import { redirect } from "next/navigation";
-import { getProject } from "../../../../../../@/api/projects";
+import { InAppWalletsSummary } from "../../../../../../components/embedded-wallets/Analytics/Summary";
 
 export default async function Page(props: {
   params: Promise<{ team_slug: string; project_slug: string }>;
@@ -39,11 +40,15 @@ export default async function Page(props: {
   }
 
   return (
-    <InAppWalletAnalytics
-      teamId={project.teamId}
-      projectId={project.id}
-      interval={interval}
-      range={range as Range}
-    />
+    <div>
+      <InAppWalletsSummary teamId={project.teamId} projectId={project.id} />
+      <div className="h-10" />
+      <InAppWalletAnalytics
+        teamId={project.teamId}
+        projectId={project.id}
+        interval={interval}
+        range={range as Range}
+      />
+    </div>
   );
 }
diff --git a/apps/dashboard/src/components/analytics/stat.tsx b/apps/dashboard/src/components/analytics/stat.tsx
index 0e29ee0dda6..be7621a44dc 100644
--- a/apps/dashboard/src/components/analytics/stat.tsx
+++ b/apps/dashboard/src/components/analytics/stat.tsx
@@ -1,16 +1,23 @@
-export const Stat: React.FC<{
+import { Skeleton } from "../../@/components/ui/skeleton";
+
+export const StatCard: React.FC<{
   label: string;
   value?: number;
   icon: React.FC<{ className?: string }>;
   formatter?: (value: number) => string;
-}> = ({ label, value, formatter, icon: Icon }) => {
+  isPending: boolean;
+}> = ({ label, value, formatter, icon: Icon, isPending }) => {
   return (
     <dl className="flex items-center justify-between gap-4 rounded-lg border border-border bg-card p-4 pr-6">
       <div>
         <dd className="mb-0.5 font-semibold text-2xl tracking-tight">
-          {value !== undefined && formatter
-            ? formatter(value)
-            : value?.toLocaleString()}
+          {isPending ? (
+            <Skeleton className="h-8 w-20" />
+          ) : value !== undefined && formatter ? (
+            formatter(value)
+          ) : (
+            value?.toLocaleString()
+          )}
         </dd>
         <dt className="text-muted-foreground text-sm">{label}</dt>
       </div>
diff --git a/apps/dashboard/src/components/embedded-wallets/Analytics/Summary.tsx b/apps/dashboard/src/components/embedded-wallets/Analytics/Summary.tsx
index 5839fd59f53..405d52958a3 100644
--- a/apps/dashboard/src/components/embedded-wallets/Analytics/Summary.tsx
+++ b/apps/dashboard/src/components/embedded-wallets/Analytics/Summary.tsx
@@ -1,10 +1,14 @@
-import { Stat } from "components/analytics/stat";
+import { getInAppWalletUsage } from "@/api/analytics";
+import { StatCard } from "components/analytics/stat";
+import { subDays } from "date-fns";
 import { ActivityIcon, UserIcon } from "lucide-react";
+import { Suspense } from "react";
 import type { InAppWalletStats } from "types/analytics";
 
-export function InAppWalletsSummary(props: {
+function InAppWalletsSummaryInner(props: {
   allTimeStats: InAppWalletStats[] | undefined;
   monthlyStats: InAppWalletStats[] | undefined;
+  isPending: boolean;
 }) {
   const allTimeStats = props.allTimeStats?.reduce(
     (acc, curr) => {
@@ -28,16 +32,75 @@ export function InAppWalletsSummary(props: {
 
   return (
     <div className="grid grid-cols-2 gap-4">
-      <Stat
+      <StatCard
         label="Total Users"
         value={allTimeStats?.uniqueWalletsConnected || 0}
         icon={ActivityIcon}
+        isPending={props.isPending}
       />
-      <Stat
+      <StatCard
         label="Monthly Active Users"
         value={monthlyStats?.uniqueWalletsConnected || 0}
         icon={UserIcon}
+        isPending={props.isPending}
       />
     </div>
   );
 }
+
+async function AsyncInAppWalletsSummary(props: {
+  teamId: string;
+  projectId: string;
+}) {
+  const { teamId, projectId } = props;
+  const allTimeStatsPromise = getInAppWalletUsage({
+    teamId,
+    projectId,
+    from: new Date(2022, 0, 1),
+    to: new Date(),
+    period: "all",
+  });
+
+  const monthlyStatsPromise = getInAppWalletUsage({
+    teamId,
+    projectId,
+    from: subDays(new Date(), 30),
+    to: new Date(),
+    period: "month",
+  });
+
+  const [allTimeStats, monthlyStats] = await Promise.all([
+    allTimeStatsPromise,
+    monthlyStatsPromise,
+  ]).catch(() => [null, null]);
+
+  return (
+    <InAppWalletsSummaryInner
+      allTimeStats={allTimeStats || undefined}
+      monthlyStats={monthlyStats || undefined}
+      isPending={false}
+    />
+  );
+}
+
+export function InAppWalletsSummary(props: {
+  teamId: string;
+  projectId: string;
+}) {
+  return (
+    <Suspense
+      fallback={
+        <InAppWalletsSummaryInner
+          allTimeStats={undefined}
+          monthlyStats={undefined}
+          isPending={true}
+        />
+      }
+    >
+      <AsyncInAppWalletsSummary
+        teamId={props.teamId}
+        projectId={props.projectId}
+      />
+    </Suspense>
+  );
+}
diff --git a/apps/dashboard/src/components/embedded-wallets/Analytics/index.tsx b/apps/dashboard/src/components/embedded-wallets/Analytics/index.tsx
index 23f825b70ea..99f16d8b32c 100644
--- a/apps/dashboard/src/components/embedded-wallets/Analytics/index.tsx
+++ b/apps/dashboard/src/components/embedded-wallets/Analytics/index.tsx
@@ -1,43 +1,34 @@
+import { getInAppWalletUsage } from "@/api/analytics";
 import {
   type Range,
   getLastNDaysRange,
 } from "components/analytics/date-range-selector";
-import { getInAppWalletUsage } from "../../../@/api/analytics";
+import { Suspense } from "react";
+import type { InAppWalletStats } from "../../../types/analytics";
 import { RangeSelector } from "../../analytics/range-selector";
 import { InAppWalletUsersChartCardUI } from "./InAppWalletUsersChartCard";
 
-export async function InAppWalletAnalytics({
-  teamId,
-  projectId,
-  interval,
-  range = getLastNDaysRange("last-120"),
-}: {
-  teamId: string;
-  projectId: string;
+type InAppWalletAnalyticsProps = {
   interval: "day" | "week";
-  range?: Range;
-}) {
-  const stats = await getInAppWalletUsage({
-    teamId,
-    projectId,
-    from: range.from,
-    to: range.to,
-    period: interval,
-  }).catch((error) => {
-    console.error(error);
-    return [];
-  });
+  range: Range;
+  stats: InAppWalletStats[];
+  isPending: boolean;
+};
 
+function InAppWalletAnalyticsInner({
+  interval,
+  range,
+  stats,
+  isPending,
+}: InAppWalletAnalyticsProps) {
   return (
     <div>
       <RangeSelector range={range} interval={interval} />
-
       <div className="h-6" />
-
       <div className="flex flex-col gap-4 lg:gap-6">
         <InAppWalletUsersChartCardUI
           inAppWalletStats={stats || []}
-          isPending={false}
+          isPending={isPending}
           title="Unique Users"
           description="The total number of active in-app wallet users on your project."
         />
@@ -45,3 +36,48 @@ export async function InAppWalletAnalytics({
     </div>
   );
 }
+type AsyncInAppWalletAnalyticsProps = Omit<
+  InAppWalletAnalyticsProps,
+  "stats" | "isPending"
+> & {
+  teamId: string;
+  projectId: string;
+};
+
+async function AsyncInAppWalletAnalytics(
+  props: AsyncInAppWalletAnalyticsProps,
+) {
+  const range = props.range ?? getLastNDaysRange("last-120");
+
+  const stats = await getInAppWalletUsage({
+    teamId: props.teamId,
+    projectId: props.projectId,
+    from: range.from,
+    to: range.to,
+    period: props.interval,
+  }).catch((error) => {
+    console.error(error);
+    return [];
+  });
+
+  return (
+    <InAppWalletAnalyticsInner
+      {...props}
+      stats={stats}
+      range={range}
+      isPending={false}
+    />
+  );
+}
+
+export function InAppWalletAnalytics(props: AsyncInAppWalletAnalyticsProps) {
+  return (
+    <Suspense
+      fallback={
+        <InAppWalletAnalyticsInner {...props} stats={[]} isPending={true} />
+      }
+    >
+      <AsyncInAppWalletAnalytics {...props} />
+    </Suspense>
+  );
+}
diff --git a/apps/dashboard/src/components/smart-wallets/AccountAbstractionAnalytics/AccountAbstractionSummary.tsx b/apps/dashboard/src/components/smart-wallets/AccountAbstractionAnalytics/AccountAbstractionSummary.tsx
index e0de3dec7f7..871ae786b90 100644
--- a/apps/dashboard/src/components/smart-wallets/AccountAbstractionAnalytics/AccountAbstractionSummary.tsx
+++ b/apps/dashboard/src/components/smart-wallets/AccountAbstractionAnalytics/AccountAbstractionSummary.tsx
@@ -1,18 +1,22 @@
-import { Stat } from "components/analytics/stat";
+import { getAggregateUserOpUsage } from "@/api/analytics";
+import { StatCard } from "components/analytics/stat";
 import { ActivityIcon, CoinsIcon } from "lucide-react";
+import { Suspense } from "react";
 import type { UserOpStats } from "types/analytics";
 
-export function AccountAbstractionSummary(props: {
-  aggregateUserOpUsageQuery?: UserOpStats;
+function AccountAbstractionSummaryInner(props: {
+  aggregateUserOpUsageQuery: UserOpStats | undefined;
+  isPending: boolean;
 }) {
   return (
     <div className="grid grid-cols-2 gap-4">
-      <Stat
+      <StatCard
         label="Mainnet Sponsored Transactions"
         value={props.aggregateUserOpUsageQuery?.successful || 0}
         icon={ActivityIcon}
+        isPending={props.isPending}
       />
-      <Stat
+      <StatCard
         label="Mainnet Gas Sponsored"
         value={props.aggregateUserOpUsageQuery?.sponsoredUsd || 0}
         formatter={(value: number) =>
@@ -22,7 +26,46 @@ export function AccountAbstractionSummary(props: {
           }).format(value)
         }
         icon={CoinsIcon}
+        isPending={props.isPending}
       />
     </div>
   );
 }
+
+async function AsyncAccountAbstractionSummary(props: {
+  teamId: string;
+  projectId: string;
+}) {
+  const aggregateUserOpStats = await getAggregateUserOpUsage({
+    teamId: props.teamId,
+    projectId: props.projectId,
+  });
+
+  return (
+    <AccountAbstractionSummaryInner
+      aggregateUserOpUsageQuery={aggregateUserOpStats}
+      isPending={false}
+    />
+  );
+}
+
+export function AccountAbstractionSummary(props: {
+  teamId: string;
+  projectId: string;
+}) {
+  return (
+    <Suspense
+      fallback={
+        <AccountAbstractionSummaryInner
+          aggregateUserOpUsageQuery={undefined}
+          isPending={true}
+        />
+      }
+    >
+      <AsyncAccountAbstractionSummary
+        teamId={props.teamId}
+        projectId={props.projectId}
+      />
+    </Suspense>
+  );
+}
diff --git a/apps/dashboard/src/components/smart-wallets/AccountAbstractionAnalytics/index.tsx b/apps/dashboard/src/components/smart-wallets/AccountAbstractionAnalytics/index.tsx
index 8f1fac41f1c..df0b8a88a74 100644
--- a/apps/dashboard/src/components/smart-wallets/AccountAbstractionAnalytics/index.tsx
+++ b/apps/dashboard/src/components/smart-wallets/AccountAbstractionAnalytics/index.tsx
@@ -12,7 +12,6 @@ import type { ThirdwebClient } from "thirdweb";
 import type { UserOpStats } from "types/analytics";
 import { SponsoredTransactionsTable } from "../../../app/team/[team_slug]/(team)/~/usage/overview/components/SponsoredTransactionsTable";
 import { searchParams } from "../../../app/team/[team_slug]/[project_slug]/connect/account-abstraction/search-params";
-import { AccountAbstractionSummary } from "./AccountAbstractionSummary";
 import { SponsoredTransactionsChartCard } from "./SponsoredTransactionsChartCard";
 import { TotalSponsoredChartCard } from "./TotalSponsoredChartCard";
 
@@ -22,7 +21,6 @@ export function AccountAbstractionAnalytics(props: {
   teamSlug: string;
   client: ThirdwebClient;
   projectId: string;
-  aggregateUserOpStats: UserOpStats;
 }) {
   const [isLoading, startTransition] = useTransition();
 
@@ -67,12 +65,6 @@ export function AccountAbstractionAnalytics(props: {
 
   return (
     <div>
-      <AccountAbstractionSummary
-        aggregateUserOpUsageQuery={props.aggregateUserOpStats}
-      />
-
-      <div className="h-10" />
-
       <div className="flex justify-end gap-3">
         <DateRangeSelector
           range={range}


Reviews:
user: login: graphite-app[bot]
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-01T14:49:29Z
user: login: MananTank
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-01T15:43:33Z
user: login: joaquim-verges
state: APPROVED
author_association: MEMBER
submitted_at: 2025-04-01T23:10:31Z

Review comments:
diff_hunk: @@ -1,16 +1,23 @@
-export const Stat: React.FC<{
+import { Skeleton } from "../../@/components/ui/skeleton";
path: apps/dashboard/src/components/analytics/stat.tsx
user: login: graphite-app[bot]
body: The import path for the Skeleton component contains an error. The path `../../@/components/ui/skeleton` combines relative path navigation (`../../`) with an absolute path alias (`@/`), which is incorrect. 

Since `@/` is an absolute path alias, the correct import should be:

```typescript
import { Skeleton } from "@/components/ui/skeleton";
```

This ensures the component is properly imported using the configured path alias.
```suggestion
import { Skeleton } from "@/components/ui/skeleton";
```
  

*Spotted by [Diamond](https://app.graphite.dev/diamond/?org=thirdweb-dev&ref=ai-review-comment)*<i class='graphite__hidden'><br /><br />Is this helpful? React 👍 or 👎 to let us know.</i>
created_at: 2025-04-01T14:49:27Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1,16 +1,23 @@
-export const Stat: React.FC<{
+import { Skeleton } from "../../@/components/ui/skeleton";
path: apps/dashboard/src/components/analytics/stat.tsx
user: login: MananTank
body: Its OK
created_at: 2025-04-01T15:43:33Z
author_association: CONTRIBUTOR

PR comments:
user: login: linear[bot]
created_at: 2025-04-01T14:48:11Z
body: <p><a href="https://linear.app/thirdweb/issue/TOOL-3762/in-app-wallets-analytics-page">TOOL-3762 In-App Wallets Analytics Page</a></p>
author_association: NONE
user: login: changeset-bot[bot]
created_at: 2025-04-01T14:48:12Z
body: ###  ⚠️  No Changeset found

Latest commit: 7eaf86a607a976ff5c91287f336dc7ba952be726

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. **If these changes should result in a version bump, you need to add a changeset.**

<details><summary>This PR includes no changesets</summary>

  When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

</details>

[Click here to learn what changesets are, and how to add one](https://github.com/changesets/changesets/blob/main/docs/adding-a-changeset.md).

[Click here if you're a maintainer who wants to add a changeset to this PR](https://github.com/thirdweb-dev/js/new/tool-3762?filename=.changeset/lovely-seas-hug.md&value=---%0A%22thirdweb-dashboard%22%3A%20patch%0A---%0A%0A%5BTOOL-3762%5D%20Dashboard%3A%20Update%20In-app%20wallet%20analytics%20page%0A)


author_association: NONE
user: login: vercel[bot]
created_at: 2025-04-01T14:48:20Z
body: [vc]: #W45422D2u8xuIdu0vY/0NdVDDTN5ny+6VGfpUbsm7oI=:eyJpc01vbm9yZXBvIjp0cnVlLCJ0eXBlIjoiZ2l0aHViIiwicHJvamVjdHMiOlt7Im5hbWUiOiJkb2NzLXYyIiwicm9vdERpcmVjdG9yeSI6ImFwcHMvcG9ydGFsIiwibGl2ZUZlZWRiYWNrIjp7InJlc29sdmVkIjowLCJ1bnJlc29sdmVkIjowLCJ0b3RhbCI6MCwibGluayI6IiJ9LCJpbnNwZWN0b3JVcmwiOiJodHRwczovL3ZlcmNlbC5jb20vdGhpcmR3ZWIvZG9jcy12Mi9DaXVVcndFWFNveE1Ra1BmQXZLY0dzNXo1YmZTIiwicHJldmlld1VybCI6IiIsIm5leHRDb21taXRTdGF0dXMiOiJTS0lQUEVEIn0seyJuYW1lIjoibG9naW4iLCJyb290RGlyZWN0b3J5IjoiYXBwcy9sb2dpbiIsImxpdmVGZWVkYmFjayI6eyJyZXNvbHZlZCI6MCwidW5yZXNvbHZlZCI6MCwidG90YWwiOjAsImxpbmsiOiIifSwiaW5zcGVjdG9yVXJsIjoiaHR0cHM6Ly92ZXJjZWwuY29tL3RoaXJkd2ViL2xvZ2luLzVpcXQ2MmMzOGo5M3J2cjFoZHlYekZzV3lMRWMiLCJwcmV2aWV3VXJsIjoiIiwibmV4dENvbW1pdFN0YXR1cyI6IlNLSVBQRUQifSx7Im5hbWUiOiJ3YWxsZXQtdWkiLCJyb290RGlyZWN0b3J5IjoiYXBwcy93YWxsZXQtdWkiLCJsaXZlRmVlZGJhY2siOnsicmVzb2x2ZWQiOjAsInVucmVzb2x2ZWQiOjAsInRvdGFsIjowLCJsaW5rIjoiIn0sImluc3BlY3RvclVybCI6Imh0dHBzOi8vdmVyY2VsLmNvbS90aGlyZHdlYi93YWxsZXQtdWkvOFNWbUFXa0xiRUZBSFZUZFVERXlkZEtjakFWUSIsInByZXZpZXdVcmwiOiIiLCJuZXh0Q29tbWl0U3RhdHVzIjoiU0tJUFBFRCJ9LHsibmFtZSI6InRoaXJkd2ViX3BsYXlncm91bmQiLCJyb290RGlyZWN0b3J5IjoiYXBwcy9wbGF5Z3JvdW5kLXdlYiIsImxpdmVGZWVkYmFjayI6eyJyZXNvbHZlZCI6MCwidW5yZXNvbHZlZCI6MCwidG90YWwiOjAsImxpbmsiOiIifSwiaW5zcGVjdG9yVXJsIjoiaHR0cHM6Ly92ZXJjZWwuY29tL3RoaXJkd2ViL3RoaXJkd2ViX3BsYXlncm91bmQvMkZrVkU0TlFlUDNvTWpXVGptb21KeFYxZkZHQiIsInByZXZpZXdVcmwiOiIiLCJuZXh0Q29tbWl0U3RhdHVzIjoiU0tJUFBFRCJ9LHsibmFtZSI6InRoaXJkd2ViLXd3dyIsInJvb3REaXJlY3RvcnkiOiJhcHBzL2Rhc2hib2FyZCIsImluc3BlY3RvclVybCI6Imh0dHBzOi8vdmVyY2VsLmNvbS90aGlyZHdlYi90aGlyZHdlYi13d3cvNktkdmZ0cXd6MnJzVlc2cmR0TlZHMXRoVGI2VyIsInByZXZpZXdVcmwiOiJ0aGlyZHdlYi13d3ctZ2l0LXRvb2wtMzc2Mi50aGlyZHdlYi1wcmV2aWV3LmNvbSIsIm5leHRDb21taXRTdGF0dXMiOiJERVBMT1lFRCIsImxpdmVGZWVkYmFjayI6eyJyZXNvbHZlZCI6MCwidW5yZXNvbHZlZCI6MCwidG90YWwiOjAsImxpbmsiOiJ0aGlyZHdlYi13d3ctZ2l0LXRvb2wtMzc2Mi50aGlyZHdlYi1wcmV2aWV3LmNvbSJ9fV19
**The latest updates on your projects**. Learn more about [Vercel for Git ↗︎](https://vercel.link/github-learn-more)

| Name | Status | Preview | Comments | Updated (UTC) |
| :--- | :----- | :------ | :------- | :------ |
| **thirdweb-www** | ✅ Ready ([Inspect](https://vercel.com/thirdweb/thirdweb-www/6Kdvftqwz2rsVW6rdtNVG1thTb6W)) | [Visit Preview](https://thirdweb-www-git-tool-3762.thirdweb-preview.com) | 💬 [**Add feedback**](https://vercel.live/open-feedback/thirdweb-www-git-tool-3762.thirdweb-preview.com?via=pr-comment-feedback-link) | Apr 2, 2025 7:17am |

<details><summary>4 Skipped Deployments</summary>

| Name | Status | Preview | Comments | Updated (UTC) |
| :--- | :----- | :------ | :------- | :------ |
| **docs-v2** | ⬜️ Skipped ([Inspect](https://vercel.com/thirdweb/docs-v2/CiuUrwEXSoxMQkPfAvKcGs5z5bfS)) |  |  | Apr 2, 2025 7:17am |
| **login** | ⬜️ Skipped ([Inspect](https://vercel.com/thirdweb/login/5iqt62c38j93rvr1hdyXzFsWyLEc)) |  |  | Apr 2, 2025 7:17am |
| **thirdweb_playground** | ⬜️ Skipped ([Inspect](https://vercel.com/thirdweb/thirdweb_playground/2FkVE4NQeP3oMjWTjmomJxV1fFGB)) |  |  | Apr 2, 2025 7:17am |
| **wallet-ui** | ⬜️ Skipped ([Inspect](https://vercel.com/thirdweb/wallet-ui/8SVmAWkLbEFAHVTdUDEyddKcjAVQ)) |  |  | Apr 2, 2025 7:17am |
</details>






author_association: NONE
user: login: MananTank
created_at: 2025-04-01T14:48:25Z
body: * **#6616** <a href="https://app.graphite.dev/github/pr/thirdweb-dev/js/6616?utm_source=stack-comment-icon" target="_blank"><img src="https://static.graphite.dev/graphite-32x32-black.png" alt="Graphite" width="10px" height="10px"/></a>
* **#6614** <a href="https://app.graphite.dev/github/pr/thirdweb-dev/js/6614?utm_source=stack-comment-icon" target="_blank"><img src="https://static.graphite.dev/graphite-32x32-black.png" alt="Graphite" width="10px" height="10px"/></a>
* **#6611** <a href="https://app.graphite.dev/github/pr/thirdweb-dev/js/6611?utm_source=stack-comment-icon" target="_blank"><img src="https://static.graphite.dev/graphite-32x32-black.png" alt="Graphite" width="10px" height="10px"/></a>
* **#6610** <a href="https://app.graphite.dev/github/pr/thirdweb-dev/js/6610?utm_source=stack-comment-icon" target="_blank"><img src="https://static.graphite.dev/graphite-32x32-black.png" alt="Graphite" width="10px" height="10px"/></a> 👈 <a href="https://app.graphite.dev/github/pr/thirdweb-dev/js/6610?utm_source=stack-comment-view-in-graphite" target="_blank">(View in Graphite)</a>
* **#6609** <a href="https://app.graphite.dev/github/pr/thirdweb-dev/js/6609?utm_source=stack-comment-icon" target="_blank"><img src="https://static.graphite.dev/graphite-32x32-black.png" alt="Graphite" width="10px" height="10px"/></a>
* `main`
<hr />

## How to use the Graphite Merge Queue

Add either label to this PR to merge it via the merge queue:
- <kbd>merge-queue</kbd> - adds this PR to the back of the merge queue
- <kbd>hotfix</kbd> - for urgent hot fixes, skip the queue and merge this PR next

You must have a Graphite account in order to use the merge queue. Sign up using [this link](https://app.graphite.dev/invite/github/thirdweb-dev?ref=merge-queue-instructions-in-stack-comment&prId=6610).

<h2></h2>
<sub>

An organization admin has enabled the [Graphite Merge Queue](https://graphite.dev/docs/graphite-merge-queue) in this repository.

</sub>

<sub>

Please do not merge from GitHub as this will restart CI on PRs being processed by the merge queue.

</sub>

<h2></h2>

This stack of pull requests is managed by <a href="https://graphite.dev?utm-source=stack-comment"><b>Graphite</b></a>. Learn more about <a href="https://stacking.dev/?utm_source=stack-comment">stacking</a>.
<!-- Current dependencies on/for this PR: -->
author_association: CONTRIBUTOR
user: login: codecov[bot]
created_at: 2025-04-01T14:51:03Z
body: ## [Codecov](https://app.codecov.io/gh/thirdweb-dev/js/pull/6610?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=thirdweb-dev) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Project coverage is 54.97%. Comparing base [(`5788cbd`)](https://app.codecov.io/gh/thirdweb-dev/js/commit/5788cbdbad3a637eddeb2add692d751bfbb3197a?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=thirdweb-dev) to head [(`7eaf86a`)](https://app.codecov.io/gh/thirdweb-dev/js/commit/7eaf86a607a976ff5c91287f336dc7ba952be726?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=thirdweb-dev).
> Report is 1 commits behind head on main.

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##             main    #6610      +/-   ##
==========================================
- Coverage   54.97%   54.97%   -0.01%     
==========================================
  Files         885      885              
  Lines       55610    55610              
  Branches     3795     3793       -2     
==========================================
- Hits        30574    30570       -4     
- Misses      24941    24945       +4     
  Partials       95       95              
```

| [Flag](https://app.codecov.io/gh/thirdweb-dev/js/pull/6610/flags?src=pr&el=flags&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=thirdweb-dev) | Coverage Δ | |
|---|---|---|
| [packages](https://app.codecov.io/gh/thirdweb-dev/js/pull/6610/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=thirdweb-dev) | `54.97% <ø> (-0.01%)` | :arrow_down: |

[see 1 file with indirect coverage changes](https://app.codecov.io/gh/thirdweb-dev/js/pull/6610/indirect-changes?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=thirdweb-dev)

</details>
<details><summary> :rocket: New features to boost your workflow: </summary>

- :snowflake: [Test Analytics](https://docs.codecov.com/docs/test-analytics): Detect flaky tests, report on failures, and find test suite problems.
- :package: [JS Bundle Analysis](https://docs.codecov.com/docs/javascript-bundle-analysis): Save yourself from yourself by tracking and limiting bundle sizes in JS merges.
</details>
author_association: NONE
user: login: github-actions[bot]
created_at: 2025-04-01T14:53:27Z
body: ## size-limit report 📦 
| Path                                    | Size           | Loading time (3g) | Running time (snapdragon) | Total time |
| --------------------------------------- | -------------- | ----------------- | ------------------------- | ---------- |
| thirdweb (esm)                          | 47.55 KB (0%)  | 952 ms (0%)       | 132 ms (+93.13% 🔺)       | 1.1 s      |
| thirdweb (cjs)                          | 127.72 KB (0%) | 2.6 s (0%)        | 222 ms (+33.95% 🔺)       | 2.8 s      |
| thirdweb (minimal + tree-shaking)       | 5.62 KB (0%)   | 113 ms (0%)       | 71 ms (+1363.97% 🔺)      | 184 ms     |
| thirdweb/chains (tree-shaking)          | 514 B (0%)     | 11 ms (0%)        | 47 ms (+2125.56% 🔺)      | 57 ms      |
| thirdweb/react (minimal + tree-shaking) | 19.35 KB (0%)  | 388 ms (0%)       | 112 ms (+897.31% 🔺)      | 499 ms     |
author_association: CONTRIBUTOR
user: login: graphite-app[bot]
created_at: 2025-04-02T07:09:20Z
body: ### Merge activity

* **Apr 2, 3:09 AM EDT**: `joaquim-verges` added this pull request to the [Graphite merge queue](https://app.graphite.dev/merges?org=thirdweb-dev&repo=js).
* **Apr 2, 3:18 AM EDT**: Merged by the [Graphite merge queue](https://app.graphite.dev/merges?org=thirdweb-dev&repo=js).

author_association: CONTRIBUTOR