diff --git a/apps/dashboard/src/@/api/usage/billing-preview.ts b/apps/dashboard/src/@/api/usage/billing-preview.ts new file mode 100644 index 00000000000..70e5e1a0eea --- /dev/null +++ b/apps/dashboard/src/@/api/usage/billing-preview.ts @@ -0,0 +1,66 @@ +import "server-only"; + +import { NEXT_PUBLIC_THIRDWEB_API_HOST } from "@/constants/public-envs"; +import { getAuthToken } from "../../../app/(app)/api/lib/getAuthToken"; + +type LineItem = { + quantity: number; + amountUsdCents: number; + unitAmountUsdCents: string; + description: string; +}; + +export type UsageCategory = { + category: string; + unitName: string; + lineItems: LineItem[]; +}; + +type UsageApiResponse = { + result: UsageCategory[]; + periodStart: string; + periodEnd: string; + planVersion: number; +}; + +export async function getBilledUsage(teamSlug: string) { + const authToken = await getAuthToken(); + if (!authToken) { + throw new Error("No auth token found"); + } + const response = await fetch( + new URL( + `/v1/teams/${teamSlug}/billing/billed-usage`, + NEXT_PUBLIC_THIRDWEB_API_HOST, + ), + { + next: { + // revalidate this query once per minute (does not need to be more granular than that) + revalidate: 60, + }, + headers: { + Authorization: `Bearer ${authToken}`, + }, + }, + ); + if (!response.ok) { + // if the status is 404, the most likely reason is that the team is on a free plan + if (response.status === 404) { + return { + status: "error", + reason: "free_plan", + } as const; + } + const body = await response.text(); + return { + status: "error", + reason: "unknown", + body, + } as const; + } + const data = (await response.json()) as UsageApiResponse; + return { + status: "success", + data, + } as const; +} diff --git a/apps/dashboard/src/@/api/usage/rpc.ts b/apps/dashboard/src/@/api/usage/rpc.ts index 2a20222a388..4d9439170ff 100644 --- a/apps/dashboard/src/@/api/usage/rpc.ts +++ b/apps/dashboard/src/@/api/usage/rpc.ts @@ -2,58 +2,6 @@ import "server-only"; import { unstable_cache } from "next/cache"; import { ANALYTICS_SERVICE_URL } from "../../constants/server-envs"; -export type RPCUsageDataItem = { - date: string; - usageType: "included" | "overage" | "rate-limit"; - count: string; -}; - -export const fetchRPCUsage = unstable_cache( - async (params: { - teamId: string; - projectId?: string; - authToken: string; - from: string; - to: string; - period: "day" | "week" | "month" | "year" | "all"; - }) => { - const analyticsEndpoint = ANALYTICS_SERVICE_URL; - const url = new URL(`${analyticsEndpoint}/v2/rpc/usage-types`); - url.searchParams.set("teamId", params.teamId); - if (params.projectId) { - url.searchParams.set("projectId", params.projectId); - } - url.searchParams.set("from", params.from); - url.searchParams.set("to", params.to); - url.searchParams.set("period", params.period); - - const res = await fetch(url, { - headers: { - Authorization: `Bearer ${params.authToken}`, - }, - }); - - if (!res.ok) { - const error = await res.text(); - return { - ok: false as const, - error: error, - }; - } - - const resData = await res.json(); - - return { - ok: true as const, - data: resData.data as RPCUsageDataItem[], - }; - }, - ["rpc-usage"], - { - revalidate: 60 * 60, // 1 hour - }, -); - type Last24HoursRPCUsageApiResponse = { peakRate: { date: string; diff --git a/apps/dashboard/src/@/components/blocks/charts/area-chart.tsx b/apps/dashboard/src/@/components/blocks/charts/area-chart.tsx index 5ea5ddb4cd1..dc7fe351bf0 100644 --- a/apps/dashboard/src/@/components/blocks/charts/area-chart.tsx +++ b/apps/dashboard/src/@/components/blocks/charts/area-chart.tsx @@ -20,7 +20,7 @@ import { EmptyChartState, LoadingChartState, } from "components/analytics/empty-chart-state"; -import { formatDate } from "date-fns"; +import { format } from "date-fns"; import { useMemo } from "react"; import { Area, AreaChart, CartesianGrid, XAxis, YAxis } from "recharts"; @@ -94,7 +94,7 @@ export function ThirdwebAreaChart( axisLine={false} tickMargin={20} tickFormatter={(value) => - formatDate( + format( new Date(value), props.xAxis?.sameDay ? "MMM dd, HH:mm" : "MMM dd", ) diff --git a/apps/dashboard/src/@/components/blocks/charts/bar-chart.tsx b/apps/dashboard/src/@/components/blocks/charts/bar-chart.tsx index 26270091c10..be4b16bb58c 100644 --- a/apps/dashboard/src/@/components/blocks/charts/bar-chart.tsx +++ b/apps/dashboard/src/@/components/blocks/charts/bar-chart.tsx @@ -22,7 +22,7 @@ import { EmptyChartState, LoadingChartState, } from "components/analytics/empty-chart-state"; -import { formatDate } from "date-fns"; +import { format } from "date-fns"; import { useMemo } from "react"; type ThirdwebBarChartProps = { @@ -86,7 +86,7 @@ export function ThirdwebBarChart( tickLine={false} axisLine={false} tickMargin={10} - tickFormatter={(value) => formatDate(new Date(value), "MMM d")} + tickFormatter={(value) => format(new Date(value), "MMM d")} /> { if (Array.isArray(item)) { const time = item[0].payload.time as number; - return formatDate( + return format( new Date(time), includeTimeOfDay ? "MMM d, yyyy hh:mm a" : "MMM d, yyyy", ); diff --git a/apps/dashboard/src/app/(app)/(dashboard)/published-contract/[publisher]/[contract_id]/[version]/opengraph-image.tsx b/apps/dashboard/src/app/(app)/(dashboard)/published-contract/[publisher]/[contract_id]/[version]/opengraph-image.tsx index a6525296104..f95305e0cf7 100644 --- a/apps/dashboard/src/app/(app)/(dashboard)/published-contract/[publisher]/[contract_id]/[version]/opengraph-image.tsx +++ b/apps/dashboard/src/app/(app)/(dashboard)/published-contract/[publisher]/[contract_id]/[version]/opengraph-image.tsx @@ -1,5 +1,5 @@ import { serverThirdwebClient } from "@/constants/thirdweb-client.server"; -import { format } from "date-fns/format"; +import { format } from "date-fns"; import { resolveEns } from "lib/ens"; import { correctAndUniqueLicenses } from "lib/licenses"; import { getSocialProfiles } from "thirdweb/social"; diff --git a/apps/dashboard/src/app/(app)/(dashboard)/published-contract/[publisher]/[contract_id]/opengraph-image.tsx b/apps/dashboard/src/app/(app)/(dashboard)/published-contract/[publisher]/[contract_id]/opengraph-image.tsx index 937c1a1af1f..449cece382a 100644 --- a/apps/dashboard/src/app/(app)/(dashboard)/published-contract/[publisher]/[contract_id]/opengraph-image.tsx +++ b/apps/dashboard/src/app/(app)/(dashboard)/published-contract/[publisher]/[contract_id]/opengraph-image.tsx @@ -1,5 +1,5 @@ import { serverThirdwebClient } from "@/constants/thirdweb-client.server"; -import { format } from "date-fns/format"; +import { format } from "date-fns"; import { resolveEns } from "lib/ens"; import { correctAndUniqueLicenses } from "lib/licenses"; import { getSocialProfiles } from "thirdweb/social"; diff --git a/apps/dashboard/src/app/(app)/account/wallets/LinkWalletUI.tsx b/apps/dashboard/src/app/(app)/account/wallets/LinkWalletUI.tsx index 1a8f7f02f42..244694902ab 100644 --- a/apps/dashboard/src/app/(app)/account/wallets/LinkWalletUI.tsx +++ b/apps/dashboard/src/app/(app)/account/wallets/LinkWalletUI.tsx @@ -23,7 +23,7 @@ import { } from "@/components/ui/table"; import { useDashboardRouter } from "@/lib/DashboardRouter"; import { useMutation } from "@tanstack/react-query"; -import { formatDate } from "date-fns"; +import { format } from "date-fns"; import { MinusIcon } from "lucide-react"; import { useState } from "react"; import { toast } from "sonner"; @@ -106,7 +106,7 @@ export function LinkWalletUI(props: { /> - {formatDate(wallet.createdAt, "MMM d, yyyy")} + {format(wallet.createdAt, "MMM d, yyyy")} sum + item.amountUsdCents, + 0, + ); + + // filter out any lines with 0 quantity + const filteredLineItems = category.lineItems.filter( + (item) => item.quantity > 0, + ); + + return ( + + + {category.category} + + + + + + Description + Quantity + Unit Price + Amount + + + + {filteredLineItems.length > 0 ? ( + filteredLineItems.map((item, index) => ( + + + {item.description} + + + {item.quantity.toLocaleString()} + + + {formatPrice(item.unitAmountUsdCents, { + isUnitPrice: true, + inCents: true, + })} + + + {formatPrice( + item.quantity * + Number.parseFloat(item.unitAmountUsdCents), + { inCents: true }, + )} + + + )) + ) : ( + + + No usage during this period. + + + )} + +
+
+ {categoryTotalCents > 0 && ( + +
+ Subtotal: {formatPrice(categoryTotalCents, { inCents: true })} +
+
+ )} +
+ ); +} + +// Currency Formatting Helper +export function formatPrice( + value: number | string, + options?: { isUnitPrice?: boolean; inCents?: boolean }, +) { + const { isUnitPrice = false, inCents = true } = options || {}; + const numericValue = + typeof value === "string" ? Number.parseFloat(value) : value; + + if (Number.isNaN(numericValue)) { + return "N/A"; + } + + const amountInDollars = inCents ? numericValue / 100 : numericValue; + + return amountInDollars.toLocaleString("en-US", { + style: "currency", + currency: "USD", + minimumFractionDigits: 2, + maximumFractionDigits: isUnitPrice ? 10 : 2, // Allow more precision for unit prices + }); +} diff --git a/apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/ecosystem/[slug]/(active)/analytics/components/EcosystemWalletUsersChartCard.tsx b/apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/ecosystem/[slug]/(active)/analytics/components/EcosystemWalletUsersChartCard.tsx index 84f9e1c9eb9..21c4d022639 100644 --- a/apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/ecosystem/[slug]/(active)/analytics/components/EcosystemWalletUsersChartCard.tsx +++ b/apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/ecosystem/[slug]/(active)/analytics/components/EcosystemWalletUsersChartCard.tsx @@ -6,7 +6,7 @@ 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 { DocLink } from "components/shared/DocLink"; -import { formatDate } from "date-fns"; +import { format } from "date-fns"; import { formatTickerNumber } from "lib/format-utils"; import { useMemo } from "react"; import type { EcosystemWalletStats } from "types/analytics"; @@ -169,7 +169,7 @@ export function EcosystemWalletUsersChartCard(props: { toolTipLabelFormatter={(_v, item) => { if (Array.isArray(item)) { const time = item[0].payload.time as number; - return formatDate(new Date(time), "MMM d, yyyy"); + return format(new Date(time), "MMM d, yyyy"); } return undefined; }} diff --git a/apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/projects/TeamProjectsPage.tsx b/apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/projects/TeamProjectsPage.tsx index 801df3ba9f9..acad52f0104 100644 --- a/apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/projects/TeamProjectsPage.tsx +++ b/apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/projects/TeamProjectsPage.tsx @@ -25,7 +25,7 @@ import { import { useDashboardRouter } from "@/lib/DashboardRouter"; import { cn } from "@/lib/utils"; import { LazyCreateProjectDialog } from "components/settings/ApiKeys/Create/LazyCreateAPIKeyDialog"; -import { formatDate } from "date-fns"; +import { format } from "date-fns"; import { ArrowDownNarrowWideIcon, PlusIcon, SearchIcon } from "lucide-react"; import Link from "next/link"; import { useMemo, useState } from "react"; @@ -207,7 +207,7 @@ export function TeamProjectsPage(props: { />
- {formatDate(new Date(project.createdAt), "MMM d, yyyy")} + {format(new Date(project.createdAt), "MMM d, yyyy")} ))} diff --git a/apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/settings/billing/components/PlanInfoCard.tsx b/apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/settings/billing/components/PlanInfoCard.tsx index 244d5272bc2..d359b8f9e3d 100644 --- a/apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/settings/billing/components/PlanInfoCard.tsx +++ b/apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/settings/billing/components/PlanInfoCard.tsx @@ -16,8 +16,7 @@ import { import { ToolTipLabel } from "@/components/ui/tooltip"; import { CancelPlanButton } from "components/settings/Account/Billing/CancelPlanModal/CancelPlanModal"; import { BillingPricing } from "components/settings/Account/Billing/Pricing"; -import { differenceInDays, isAfter } from "date-fns"; -import { format } from "date-fns/format"; +import { differenceInDays, format, isAfter } from "date-fns"; import { CreditCardIcon, FileTextIcon, SquarePenIcon } from "lucide-react"; import { CircleAlertIcon } from "lucide-react"; import Link from "next/link"; @@ -200,7 +199,10 @@ export function PlanInfoCardUI(props: { ) : ( - + )} @@ -208,8 +210,8 @@ export function PlanInfoCardUI(props: {

- Adjust your plan here to avoid unnecessary charges.{" "} -
For more details, See{" "} + Adjust your plan to avoid unnecessary charges.{" "} +
For more details, see{" "}
subscription.type === "PLAN", @@ -282,7 +286,7 @@ function BillingInfo({ return (

{planSubscription && ( - + )} {usageSubscription && ( @@ -290,7 +294,20 @@ function BillingInfo({ +
Usage
{" "} + + -{" "} + + View Breakdown + + +
+ } /> )} @@ -300,7 +317,7 @@ function BillingInfo({ function SubscriptionOverview(props: { subscription: TeamSubscription; - title?: string; + title?: string | React.ReactNode; }) { const { subscription } = props; @@ -308,9 +325,12 @@ function SubscriptionOverview(props: {
- {props.title && ( -
{props.title}
- )} + {props.title && + (typeof props.title === "string" ? ( +
{props.title}
+ ) : ( + props.title + ))}

{format( new Date(props.subscription.currentPeriodStart), diff --git a/apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/settings/members/ManageInvitesSection.tsx b/apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/settings/members/ManageInvitesSection.tsx index e50d434897a..c122e6e313a 100644 --- a/apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/settings/members/ManageInvitesSection.tsx +++ b/apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/settings/members/ManageInvitesSection.tsx @@ -20,7 +20,7 @@ import { } from "@/components/ui/dropdown-menu"; import { BASE_URL } from "@/constants/env-utils"; import { useMutation } from "@tanstack/react-query"; -import { formatDate } from "date-fns"; +import { format } from "date-fns"; import { EllipsisIcon, MailIcon } from "lucide-react"; import { useMemo, useState } from "react"; import { toast } from "sonner"; @@ -167,12 +167,12 @@ function InviteRow(props: {

- Invited on {formatDate(props.invite.createdAt, "MMM d, yyyy")} + Invited on {format(props.invite.createdAt, "MMM d, yyyy")}
Invitation expires on{" "} - {formatDate(props.invite.expiresAt, "MMM d, yyyy")} + {format(props.invite.expiresAt, "MMM d, yyyy")}
diff --git a/apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/usage/account-abstraction/page.tsx b/apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/usage/account-abstraction/page.tsx new file mode 100644 index 00000000000..4c5aad76489 --- /dev/null +++ b/apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/usage/account-abstraction/page.tsx @@ -0,0 +1,65 @@ +import { getTeamBySlug } from "@/api/team"; +import { getClientThirdwebClient } from "@/constants/thirdweb-client.client"; +import { redirect } from "next/navigation"; +import { getProjects } from "../../../../../../../../@/api/projects"; +import { getTeamSubscriptions } from "../../../../../../../../@/api/team-subscription"; +import { getAuthToken } from "../../../../../../api/lib/getAuthToken"; +import { loginRedirect } from "../../../../../../login/loginRedirect"; +import { SponsoredTransactionsTable } from "../overview/components/SponsoredTransactionsTable"; + +export default async function Page(props: { + params: Promise<{ + team_slug: string; + }>; +}) { + const params = await props.params; + const [team, authToken] = await Promise.all([ + getTeamBySlug(params.team_slug), + getAuthToken(), + ]); + + if (!authToken) { + loginRedirect(`/team/${params.team_slug}/~/usage/account-abstraction`); + } + + if (!team) { + redirect("/team"); + } + + const [subscriptions, projects] = await Promise.all([ + getTeamSubscriptions(team.slug), + getProjects(team.slug), + ]); + + const usageSubscription = subscriptions?.find( + (subscription) => subscription.type === "USAGE", + ); + + if (!usageSubscription) { + return ( +
+ You are on a free plan. Please upgrade to a paid plan to view your + usage. +
+ ); + } + + const client = getClientThirdwebClient({ + jwt: authToken, + teamId: team.id, + }); + + return ( +
+ +
+ ); +} diff --git a/apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/usage/layout.tsx b/apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/usage/layout.tsx index a9b013f3908..e92c94071ce 100644 --- a/apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/usage/layout.tsx +++ b/apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/usage/layout.tsx @@ -1,4 +1,6 @@ import { SidebarLayout } from "@/components/blocks/SidebarLayout"; +import { Button } from "@/components/ui/button"; +import Link from "next/link"; export default async function Layout(props: { children: React.ReactNode; @@ -10,10 +12,22 @@ export default async function Layout(props: { return (
-
+

Usage

+
+ + +
{props.children} diff --git a/apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/usage/overview/components/Usage.tsx b/apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/usage/overview/components/Usage.tsx deleted file mode 100644 index 7a0432b8f62..00000000000 --- a/apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/usage/overview/components/Usage.tsx +++ /dev/null @@ -1,327 +0,0 @@ -import { getInAppWalletUsage, getUserOpUsage } from "@/api/analytics"; -import type { Team } from "@/api/team"; -import type { TeamSubscription } from "@/api/team-subscription"; -import type { RPCUsageDataItem } from "@/api/usage/rpc"; -import { ThirdwebBarChart } from "@/components/blocks/charts/bar-chart"; -import { Button } from "@/components/ui/button"; -import type { Account } from "@3rdweb-sdk/react/hooks/useApi"; -import { InAppWalletUsersChartCardUI } from "components/embedded-wallets/Analytics/InAppWalletUsersChartCard"; -import { subMonths } from "date-fns"; -import Link from "next/link"; -import { Suspense, useMemo } from "react"; -import type { ThirdwebClient } from "thirdweb"; -import { TotalSponsoredChartCardUI } from "../../../../_components/TotalSponsoredCard"; -import { SponsoredTransactionsTable } from "./SponsoredTransactionsTable"; -import { UsageCard } from "./UsageCard"; - -type UsageProps = { - subscriptions: TeamSubscription[]; - account: Account; - team: Team; - client: ThirdwebClient; - projects: { - id: string; - name: string; - image: string | null; - slug: string; - }[]; - rpcUsage: RPCUsageDataItem[]; -}; - -const compactNumberFormatter = new Intl.NumberFormat("en-US", { - minimumFractionDigits: 0, - notation: "compact", -}); - -export const Usage: React.FC = ({ - subscriptions, - account, - team, - client, - projects, - rpcUsage, -}) => { - const gatewayMetrics = useMemo(() => { - return { - title: "Unlimited Requests", - total: ( - - {compactNumberFormatter.format( - team.capabilities.storage.upload.rateLimit, - )}{" "} - Requests Per Second - - ), - }; - }, [team.capabilities.storage.upload.rateLimit]); - - const usageSub = subscriptions.find((sub) => sub.type === "USAGE"); - - // we don't have `usageSub` for free plan - so we use "1 month ago" as the period - // even free plan can have usage data - - const currentPeriodStart = usageSub?.currentPeriodStart - ? new Date(usageSub.currentPeriodStart) - : subMonths(new Date(), 1); - - const currentPeriodEnd = usageSub?.currentPeriodEnd - ? new Date(usageSub.currentPeriodEnd) - : new Date(); - - const storageUsage = team.capabilities.storage.upload; - - const rpcUsageData = useMemo(() => { - const mappedRPCUsage = rpcUsage.reduce( - (acc, curr) => { - switch (curr.usageType) { - case "rate-limit": - acc[curr.date] = { - ...(acc[curr.date] || {}), - "rate-limit": - (acc[curr.date]?.["rate-limit"] || 0) + Number(curr.count), - included: acc[curr.date]?.included || 0, - }; - break; - default: - acc[curr.date] = { - ...(acc[curr.date] || {}), - "rate-limit": acc[curr.date]?.["rate-limit"] || 0, - included: (acc[curr.date]?.included || 0) + Number(curr.count), - }; - break; - } - return acc; - }, - {} as Record, - ); - - return Object.entries(mappedRPCUsage).map(([date, usage]) => ({ - time: new Date(date).getTime(), - ...usage, - })); - }, [rpcUsage]); - - return ( -
- - - - - - - - - - - 10_000 ? undefined : ( -

- {compactNumberFormatter.format(storageUsage.rateLimit)} Requests - Per Second -

- ) - } - name="Storage Pinning" - description="Amount of IPFS Storage pinning allowed in your plan" - > - -
-
- ); -}; - -type ChartCardProps = { - from: Date; - to: Date; - teamId: string; - accountId: string; -}; - -function InAppWalletUsersChartCard(props: ChartCardProps) { - const title = "In-App Wallets"; - const description = - "Number of unique users interacting with your apps using in-app wallets every day"; - - return ( - - } - > - - - ); -} - -async function AsyncInAppWalletUsersChartCard( - props: ChartCardProps & { - title: string; - description: string; - }, -) { - const inAppWalletUsage = await getInAppWalletUsage({ - period: "day", - from: props.from, - to: props.to, - teamId: props.teamId, - }).catch(() => null); - - return ( - - ); -} - -function TotalSponsoredCard(props: ChartCardProps) { - const title = "Total Sponsored"; - const description = - "Total amount of USD sponsored across all mainnets with account abstraction"; - - return ( - - } - > - - - ); -} - -async function AsyncTotalSponsoredChartCard( - props: ChartCardProps & { - description: string; - title: string; - }, -) { - const { teamId, from, to } = props; - const [userOpUsageTimeSeries, userOpUsage] = await Promise.all([ - // User operations usage - getUserOpUsage({ - teamId, - from: from, - to: to, - period: "week", - }), - getUserOpUsage({ - teamId, - from: from, - to: to, - period: "all", - }), - ]); - - return ( - - ); -} - -function formatStorageBytes(bytes: number) { - const ONE_KB = 1024; - const ONE_MB = ONE_KB * 1024; - const ONE_GB = ONE_MB * 1024; - - if (bytes < ONE_KB) { - return `${bytes} bytes`; - } - - if (bytes < ONE_MB) { - return `${compactNumberFormatter.format(bytes / ONE_KB)} KB`; - } - - if (bytes < ONE_GB) { - return `${compactNumberFormatter.format(bytes / ONE_MB)} MB`; - } - - const gbs = bytes / ONE_GB; - - if (gbs > 1000) { - return "Unlimited"; - } - - return `${compactNumberFormatter.format(gbs)} GB`; -} diff --git a/apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/usage/overview/components/UsageCard.tsx b/apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/usage/overview/components/UsageCard.tsx deleted file mode 100644 index 94c53be64b0..00000000000 --- a/apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/usage/overview/components/UsageCard.tsx +++ /dev/null @@ -1,70 +0,0 @@ -import type { JSX } from "react"; -import { toUSD } from "utils/number"; - -interface UsageCardProps { - name: string; - overage?: number; - title?: string; - total?: string | number | JSX.Element; - progress?: number; - description: string; - children?: JSX.Element; - totalUsage?: string; -} - -export const UsageCard: React.FC = ({ - name, - title, - total, - overage, - progress, - description, - children, - totalUsage, -}) => { - return ( -
-

{name}

-

{description}

- -
- -
- {title &&

{title}

} - - {total !== undefined && ( -

- {typeof total === "number" ? toUSD(total) : total} -

- )} - - {progress !== undefined && } - {totalUsage &&

{totalUsage}

} - - {overage && ( -

- Additional overage fees to your next invoice will be - {toUSD(overage)} -

- )} -
- - {children} -
- ); -}; - -function Progress(props: { - value: number; -}) { - return ( -
-
-
- ); -} diff --git a/apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/usage/page.tsx b/apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/usage/page.tsx index 81df228a121..bdd5822c8fb 100644 --- a/apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/usage/page.tsx +++ b/apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/usage/page.tsx @@ -1,14 +1,22 @@ -import { getProjects } from "@/api/projects"; import { getTeamBySlug } from "@/api/team"; -import { getTeamSubscriptions } from "@/api/team-subscription"; -import { fetchRPCUsage } from "@/api/usage/rpc"; -import { getClientThirdwebClient } from "@/constants/thirdweb-client.client"; -import { normalizeTimeISOString } from "@/lib/time"; +import { getBilledUsage } from "@/api/usage/billing-preview"; +import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"; +import { + Card, + CardContent, + CardDescription, + CardFooter, + CardHeader, + CardTitle, +} from "@/components/ui/card"; +import { format, parseISO } from "date-fns"; +import { InfoIcon } from "lucide-react"; import { redirect } from "next/navigation"; import { getValidAccount } from "../../../../../account/settings/getAccount"; -import { getAuthToken } from "../../../../../api/lib/getAuthToken"; -import { loginRedirect } from "../../../../../login/loginRedirect"; -import { Usage } from "./overview/components/Usage"; +import { + UsageCategoryDetails, + formatPrice, +} from "../../_components/usage-category-details"; export default async function Page(props: { params: Promise<{ @@ -16,62 +24,101 @@ export default async function Page(props: { }>; }) { const params = await props.params; - const [account, team, authToken] = await Promise.all([ + const [, team] = await Promise.all([ getValidAccount(`/team/${params.team_slug}/~/usage`), getTeamBySlug(params.team_slug), - getAuthToken(), ]); if (!team) { redirect("/team"); } - if (!authToken) { - loginRedirect(`/team/${params.team_slug}/~/usage`); - } - - const [subscriptions, projects, rpcUsage] = await Promise.all([ - getTeamSubscriptions(team.slug), - getProjects(team.slug), - fetchRPCUsage({ - authToken, - period: "day", - // 7 days ago - from: normalizeTimeISOString( - new Date(Date.now() - 1000 * 60 * 60 * 24 * 7), - ), - // now - to: normalizeTimeISOString(new Date()), - teamId: team.id, - }), - ]); + const usagePreview = await getBilledUsage(team.slug); - if (!subscriptions) { - return ( -
- Something went wrong. Please try again later. -
- ); + if (usagePreview.status === "error") { + switch (usagePreview.reason) { + case "free_plan": + return ( +
+ You are on a free plan. Please upgrade to a paid plan to view your + usage. +
+ ); + default: + return ( +
+ Something went wrong. Please try again later. +
+ ); + } } - const client = getClientThirdwebClient({ - jwt: authToken, - teamId: team.id, - }); + const grandTotalCents = usagePreview.data.result.reduce((total, category) => { + const categoryTotal = category.lineItems.reduce( + (sum, item) => sum + item.amountUsdCents, + 0, + ); + return total + categoryTotal; + }, 0); return ( - ({ - id: project.id, - name: project.name, - image: project.image, - slug: project.slug, - }))} - /> +
+ {usagePreview.data.planVersion < 5 && ( + + + Outdated Plan + +

+ You are on an outdated plan. Some of this information may be + inaccurate. +

+
+
+ )} + + + + Usage Summary + + + {format(parseISO(usagePreview.data.periodStart), "MMM d, yyyy")} -{" "} + {format(parseISO(usagePreview.data.periodEnd), "MMM d, yyyy")} + + + +
+ {formatPrice(grandTotalCents, { inCents: true })} +
+

+ Total estimated cost based on usage in the current billing period so + far. +

+
+ + + + Billing Thresholds and Discounts + +

+ Partial payments due to billing thresholds as well as discounts + are not reflected in this estimate. +
+ This means the invoice you receive at the end of the billing + period may be lower. +

+
+
+
+
+ +
+ {usagePreview.data.result.map((category, index) => ( + + ))} +
+
); } diff --git a/apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/usage/rpc/components/count-graph.tsx b/apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/usage/rpc/components/count-graph.tsx index e46ed74aa2e..e720cfb4c8f 100644 --- a/apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/usage/rpc/components/count-graph.tsx +++ b/apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/usage/rpc/components/count-graph.tsx @@ -1,7 +1,7 @@ "use client"; import { ThirdwebAreaChart } from "@/components/blocks/charts/area-chart"; -import { formatDate } from "date-fns"; +import { format } from "date-fns"; export function CountGraph(props: { peakPercentage: number; @@ -49,7 +49,7 @@ export function CountGraph(props: { }} hideLabel={false} toolTipLabelFormatter={(label) => { - return formatDate(label, "MMM dd, HH:mm"); + return format(label, "MMM dd, HH:mm"); }} // @ts-expect-error - sending MORE data than expected is ok data={props.data diff --git a/apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/usage/rpc/components/rate-graph.tsx b/apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/usage/rpc/components/rate-graph.tsx index f2075ad0752..6a2fb0be2c3 100644 --- a/apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/usage/rpc/components/rate-graph.tsx +++ b/apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/usage/rpc/components/rate-graph.tsx @@ -1,7 +1,7 @@ "use client"; import { ThirdwebAreaChart } from "@/components/blocks/charts/area-chart"; -import { formatDate } from "date-fns"; +import { format } from "date-fns"; export function RateGraph(props: { peakPercentage: number; @@ -57,7 +57,7 @@ export function RateGraph(props: { showLegend hideLabel={false} toolTipLabelFormatter={(label) => { - return formatDate(label, "MMM dd, HH:mm"); + return format(label, "MMM dd, HH:mm"); }} isPending={false} /> diff --git a/apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/usage/storage/your-files.tsx b/apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/usage/storage/your-files.tsx index 2a191589f3c..936872b3efb 100644 --- a/apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/usage/storage/your-files.tsx +++ b/apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/usage/storage/your-files.tsx @@ -16,8 +16,7 @@ import { import { ToolTipLabel } from "@/components/ui/tooltip"; import { NEXT_PUBLIC_DASHBOARD_UPLOAD_SERVER } from "@/constants/public-envs"; import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; -import { format } from "date-fns"; -import { formatDistance } from "date-fns/formatDistance"; +import { format, formatDistance } from "date-fns"; import { PinOffIcon } from "lucide-react"; import { useState } from "react"; import { useActiveAccount } from "thirdweb/react"; diff --git a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/components/EngineCloudChartCard/EngineCloudBarChartCardUI.tsx b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/components/EngineCloudChartCard/EngineCloudBarChartCardUI.tsx index a9fd7a0ae6a..95a7d58ecf1 100644 --- a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/components/EngineCloudChartCard/EngineCloudBarChartCardUI.tsx +++ b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/components/EngineCloudChartCard/EngineCloudBarChartCardUI.tsx @@ -6,7 +6,7 @@ import { ChartTooltip, ChartTooltipContent, } from "@/components/ui/chart"; -import { formatDate } from "date-fns"; +import { format } from "date-fns"; import { useMemo } from "react"; import { Bar, @@ -107,7 +107,7 @@ export function EngineCloudBarChartCardUI({ formatDate(new Date(d), "MMM d")} + labelFormatter={(d) => format(new Date(d), "MMM d")} valueFormatter={(_value) => { const value = typeof _value === "number" ? _value : 0; return {value}; diff --git a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/components/RpcMethodBarChartCard/RpcMethodBarChartCardUI.tsx b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/components/RpcMethodBarChartCard/RpcMethodBarChartCardUI.tsx index 6bbd43b80c3..d517f8cab0a 100644 --- a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/components/RpcMethodBarChartCard/RpcMethodBarChartCardUI.tsx +++ b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/components/RpcMethodBarChartCard/RpcMethodBarChartCardUI.tsx @@ -6,7 +6,7 @@ import { ChartTooltip, ChartTooltipContent, } from "@/components/ui/chart"; -import { formatDate } from "date-fns"; +import { format } from "date-fns"; import { useMemo } from "react"; import { Bar, @@ -145,7 +145,7 @@ export function RpcMethodBarChartCardUI({ formatDate(new Date(d), "MMM d")} + labelFormatter={(d) => format(new Date(d), "MMM d")} valueFormatter={(_value, _item) => { const value = typeof _value === "number" ? _value : 0; const payload = _item as { diff --git a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/cloud/analytics/tx-chart/tx-chart-ui.tsx b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/cloud/analytics/tx-chart/tx-chart-ui.tsx index 1a18269b723..04ac2f60a70 100644 --- a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/cloud/analytics/tx-chart/tx-chart-ui.tsx +++ b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/cloud/analytics/tx-chart/tx-chart-ui.tsx @@ -6,7 +6,7 @@ import { ThirdwebBarChart } from "@/components/blocks/charts/bar-chart"; import { Button } from "@/components/ui/button"; import type { ChartConfig } from "@/components/ui/chart"; import { useDashboardRouter } from "@/lib/DashboardRouter"; -import { formatDate } from "date-fns"; +import { format } from "date-fns"; import { useAllChainsData } from "hooks/chains/allChains"; import { formatTickerNumber } from "lib/format-utils"; import Link from "next/link"; @@ -141,7 +141,7 @@ export function TransactionsChartCardUI(props: { toolTipLabelFormatter={(_v, item) => { if (Array.isArray(item)) { const time = item[0].payload.time as number; - return formatDate(new Date(time), "MMM d, yyyy"); + return format(new Date(time), "MMM d, yyyy"); } return undefined; }} diff --git a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/cloud/analytics/tx-table/tx-table-ui.tsx b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/cloud/analytics/tx-table/tx-table-ui.tsx index 4847bed4365..20b9cf33321 100644 --- a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/cloud/analytics/tx-table/tx-table-ui.tsx +++ b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/cloud/analytics/tx-table/tx-table-ui.tsx @@ -31,8 +31,7 @@ import { ToolTipLabel } from "@/components/ui/tooltip"; import { useDashboardRouter } from "@/lib/DashboardRouter"; import { keepPreviousData, useQuery } from "@tanstack/react-query"; import { ChainIconClient } from "components/icons/ChainIcon"; -import { formatDistanceToNowStrict } from "date-fns"; -import { format } from "date-fns/format"; +import { format, formatDistanceToNowStrict } from "date-fns"; import { useAllChainsData } from "hooks/chains/allChains"; import { ExternalLinkIcon, InfoIcon } from "lucide-react"; import Link from "next/link"; diff --git a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/cloud/server-wallets/wallet-table/wallet-table-ui.client.tsx b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/cloud/server-wallets/wallet-table/wallet-table-ui.client.tsx index 38aecaf4c6b..ad2b0320436 100644 --- a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/cloud/server-wallets/wallet-table/wallet-table-ui.client.tsx +++ b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/cloud/server-wallets/wallet-table/wallet-table-ui.client.tsx @@ -26,8 +26,7 @@ import { import { ToolTipLabel } from "@/components/ui/tooltip"; import { useDashboardRouter } from "@/lib/DashboardRouter"; import { useQuery } from "@tanstack/react-query"; -import { formatDistanceToNowStrict } from "date-fns"; -import { format } from "date-fns/format"; +import { format, formatDistanceToNowStrict } from "date-fns"; import { useV5DashboardChain } from "lib/v5-adapter"; import { SendIcon } from "lucide-react"; import Link from "next/link"; diff --git a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/dedicated/(instance)/[engineId]/alerts/components/ManageEngineAlerts.tsx b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/dedicated/(instance)/[engineId]/alerts/components/ManageEngineAlerts.tsx index 413dd0eb7c2..cb79333a27a 100644 --- a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/dedicated/(instance)/[engineId]/alerts/components/ManageEngineAlerts.tsx +++ b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/dedicated/(instance)/[engineId]/alerts/components/ManageEngineAlerts.tsx @@ -28,7 +28,7 @@ import { useEngineNotificationChannels, } from "@3rdweb-sdk/react/hooks/useEngine"; import { type UseMutationResult, useMutation } from "@tanstack/react-query"; -import { formatDate } from "date-fns"; +import { format } from "date-fns"; import { EllipsisVerticalIcon, PlusIcon } from "lucide-react"; import { useState } from "react"; import { toast } from "sonner"; @@ -201,7 +201,7 @@ function EngineAlertsTableUI(props: { - {formatDate( + {format( new Date(notificationChannel.createdAt), "MMM dd, yyyy", )} diff --git a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/dedicated/(instance)/[engineId]/overview/components/transactions-table.tsx b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/dedicated/(instance)/[engineId]/overview/components/transactions-table.tsx index a935ecd765b..07a0bc453d6 100644 --- a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/dedicated/(instance)/[engineId]/overview/components/transactions-table.tsx +++ b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/dedicated/(instance)/[engineId]/overview/components/transactions-table.tsx @@ -40,8 +40,7 @@ import { } from "@3rdweb-sdk/react/hooks/useEngine"; import { Collapse, Divider, useDisclosure } from "@chakra-ui/react"; import { ChainIconClient } from "components/icons/ChainIcon"; -import { formatDistanceToNowStrict } from "date-fns"; -import { format, formatDate } from "date-fns/format"; +import { format, formatDistanceToNowStrict } from "date-fns"; import { useAllChainsData } from "hooks/chains/allChains"; import { ExternalLinkIcon, @@ -562,7 +561,7 @@ export function TransactionCharts(props: { toolTipLabelFormatter={(_v, item) => { if (Array.isArray(item)) { const time = item[0].payload.time as number; - return formatDate(new Date(time), "MMM d, yyyy"); + return format(new Date(time), "MMM d, yyyy"); } return undefined; }} diff --git a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/nebula/components/analytics/nebula-analytics-ui.tsx b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/nebula/components/analytics/nebula-analytics-ui.tsx index dcdefb19a9a..24120f9165f 100644 --- a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/nebula/components/analytics/nebula-analytics-ui.tsx +++ b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/nebula/components/analytics/nebula-analytics-ui.tsx @@ -2,7 +2,7 @@ import { ThirdwebAreaChart } from "@/components/blocks/charts/area-chart"; import { SkeletonContainer } from "@/components/ui/skeleton"; -import { formatDate } from "date-fns"; +import { format } from "date-fns"; import { ActivityIcon, MessageCircleQuestionIcon, @@ -173,7 +173,7 @@ export function NebulaAnalyticsDashboardUI(props: { function toolTipLabelFormatter(_v: string, item: unknown) { if (Array.isArray(item)) { const time = item[0].payload.time as number; - return formatDate(new Date(time), "MMM d, yyyy"); + return format(new Date(time), "MMM d, yyyy"); } return undefined; } diff --git a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/page.tsx b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/page.tsx index 563e77e018b..75854523105 100644 --- a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/page.tsx +++ b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/page.tsx @@ -257,7 +257,7 @@ function processTimeSeriesData( return metrics; } -async function AsyncTotalSponsoredCard(props: { +export async function AsyncTotalSponsoredCard(props: { project: Project; range: Range; interval: "day" | "week"; diff --git a/apps/dashboard/src/components/contract-components/published-contract/index.tsx b/apps/dashboard/src/components/contract-components/published-contract/index.tsx index 3e9f67fb767..a389a70fa9e 100644 --- a/apps/dashboard/src/components/contract-components/published-contract/index.tsx +++ b/apps/dashboard/src/components/contract-components/published-contract/index.tsx @@ -5,7 +5,7 @@ import { Separator } from "@/components/ui/separator"; import { Divider, Flex, GridItem, List, ListItem } from "@chakra-ui/react"; import { useQuery } from "@tanstack/react-query"; import { ContractFunctionsOverview } from "components/contract-functions/contract-functions"; -import { format } from "date-fns/format"; +import { format } from "date-fns"; import { correctAndUniqueLicenses } from "lib/licenses"; import { replaceIpfsUrl } from "lib/sdk"; import { diff --git a/apps/dashboard/src/components/embedded-wallets/Analytics/InAppWalletUsersChartCard.tsx b/apps/dashboard/src/components/embedded-wallets/Analytics/InAppWalletUsersChartCard.tsx index 6d47315c400..894c13e7c58 100644 --- a/apps/dashboard/src/components/embedded-wallets/Analytics/InAppWalletUsersChartCard.tsx +++ b/apps/dashboard/src/components/embedded-wallets/Analytics/InAppWalletUsersChartCard.tsx @@ -7,7 +7,7 @@ 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 { formatDate } from "date-fns"; +import { format } from "date-fns"; import { useMemo } from "react"; import type { InAppWalletStats } from "types/analytics"; @@ -142,7 +142,7 @@ export function InAppWalletUsersChartCardUI(props: { toolTipLabelFormatter={(_v, item) => { if (Array.isArray(item)) { const time = item[0].payload.time as number; - return formatDate(new Date(time), "MMM d, yyyy"); + return format(new Date(time), "MMM d, yyyy"); } return undefined; }} diff --git a/apps/dashboard/src/components/embedded-wallets/Users/index.tsx b/apps/dashboard/src/components/embedded-wallets/Users/index.tsx index 1ecdbc5f54e..f0dba99f450 100644 --- a/apps/dashboard/src/components/embedded-wallets/Users/index.tsx +++ b/apps/dashboard/src/components/embedded-wallets/Users/index.tsx @@ -15,7 +15,7 @@ import { } from "@3rdweb-sdk/react/hooks/useEmbeddedWallets"; import { createColumnHelper } from "@tanstack/react-table"; import { TWTable } from "components/shared/TWTable"; -import { format } from "date-fns/format"; +import { format } from "date-fns"; import { ArrowLeftIcon, ArrowRightIcon } from "lucide-react"; import Papa from "papaparse"; import { useCallback, useMemo, useState } from "react"; diff --git a/apps/dashboard/src/components/settings/AuthorizedWallets/AuthorizedWalletsTable.tsx b/apps/dashboard/src/components/settings/AuthorizedWallets/AuthorizedWalletsTable.tsx index 82f32cf75a6..256b2efc6b9 100644 --- a/apps/dashboard/src/components/settings/AuthorizedWallets/AuthorizedWalletsTable.tsx +++ b/apps/dashboard/src/components/settings/AuthorizedWallets/AuthorizedWalletsTable.tsx @@ -7,7 +7,7 @@ import { } from "@3rdweb-sdk/react/hooks/useApi"; import { createColumnHelper } from "@tanstack/react-table"; import { TWTable } from "components/shared/TWTable"; -import { format } from "date-fns/format"; +import { format } from "date-fns"; import { useTrack } from "hooks/analytics/useTrack"; import { useState } from "react"; import { toast } from "sonner"; diff --git a/apps/dashboard/src/components/smart-wallets/AccountAbstractionAnalytics/SponsoredTransactionsChartCard.tsx b/apps/dashboard/src/components/smart-wallets/AccountAbstractionAnalytics/SponsoredTransactionsChartCard.tsx index 491c1a2eafd..a6f21e11bc8 100644 --- a/apps/dashboard/src/components/smart-wallets/AccountAbstractionAnalytics/SponsoredTransactionsChartCard.tsx +++ b/apps/dashboard/src/components/smart-wallets/AccountAbstractionAnalytics/SponsoredTransactionsChartCard.tsx @@ -9,7 +9,7 @@ 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 { formatDate } from "date-fns"; +import { format } from "date-fns"; import { useAllChainsData } from "hooks/chains/allChains"; import { useMemo } from "react"; import type { UserOpStats } from "types/analytics"; @@ -137,7 +137,7 @@ export function SponsoredTransactionsChartCard(props: { toolTipLabelFormatter={(_v, item) => { if (Array.isArray(item)) { const time = item[0].payload.time as number; - return formatDate(new Date(time), "MMM d, yyyy"); + return format(new Date(time), "MMM d, yyyy"); } return undefined; }} diff --git a/apps/dashboard/src/components/smart-wallets/AccountAbstractionAnalytics/TotalSponsoredChartCard.tsx b/apps/dashboard/src/components/smart-wallets/AccountAbstractionAnalytics/TotalSponsoredChartCard.tsx index dd4d944bc1b..9779f6801d0 100644 --- a/apps/dashboard/src/components/smart-wallets/AccountAbstractionAnalytics/TotalSponsoredChartCard.tsx +++ b/apps/dashboard/src/components/smart-wallets/AccountAbstractionAnalytics/TotalSponsoredChartCard.tsx @@ -9,7 +9,7 @@ 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 { formatDate } from "date-fns"; +import { format } from "date-fns"; import { useAllChainsData } from "hooks/chains/allChains"; import { useMemo } from "react"; import type { UserOpStats } from "types/analytics"; @@ -142,7 +142,7 @@ export function TotalSponsoredChartCard(props: { toolTipLabelFormatter={(_v, item) => { if (Array.isArray(item)) { const time = item[0].payload.time as number; - return formatDate(new Date(time), "MMM d, yyyy"); + return format(new Date(time), "MMM d, yyyy"); } return undefined; }} diff --git a/apps/dashboard/src/utils/date-utils.ts b/apps/dashboard/src/utils/date-utils.ts index faba4cf11fc..e41ba62e325 100644 --- a/apps/dashboard/src/utils/date-utils.ts +++ b/apps/dashboard/src/utils/date-utils.ts @@ -1,5 +1,4 @@ -import { format } from "date-fns/format"; -import { isValid } from "date-fns/isValid"; +import { format, isValid } from "date-fns"; const DATE_TIME_LOCAL_FORMAT = "yyyy-MM-dd HH:mm";