diff --git a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/TopBar/components/OrganizationDropdown/OrganizationDropdown.tsx b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/TopBar/components/OrganizationDropdown/OrganizationDropdown.tsx
index d918461f319..df88826a400 100644
--- a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/TopBar/components/OrganizationDropdown/OrganizationDropdown.tsx
+++ b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/TopBar/components/OrganizationDropdown/OrganizationDropdown.tsx
@@ -1,5 +1,6 @@
import { COMPANY } from "@superset/shared/constants";
import { Avatar } from "@superset/ui/atoms/Avatar";
+import { Badge } from "@superset/ui/badge";
import {
DropdownMenu,
DropdownMenuContent,
@@ -27,6 +28,7 @@ import {
} from "react-icons/hi2";
import { IoBugOutline } from "react-icons/io5";
import { LuKeyboard } from "react-icons/lu";
+import { useCurrentPlan } from "renderer/hooks/useCurrentPlan";
import { useHotkeyDisplay } from "renderer/hotkeys";
import { authClient } from "renderer/lib/auth-client";
import { electronTrpc } from "renderer/lib/electron-trpc";
@@ -69,6 +71,18 @@ export function OrganizationDropdown({
const userName = session?.user?.name;
const displayName = activeOrganization?.name ?? userName ?? "Organization";
+ const currentPlan = useCurrentPlan();
+ const isPaid = currentPlan !== "free";
+ const planLabel = currentPlan.charAt(0).toUpperCase() + currentPlan.slice(1);
+ const planBadge = isPaid ? (
+
+ {planLabel}
+
+ ) : null;
+
const triggerButton =
variant === "collapsed" ? (
{displayName}
+ {planBadge}
) : (
@@ -113,6 +128,7 @@ export function OrganizationDropdown({
{displayName}
+ {planBadge}
);
diff --git a/apps/web/src/app/(agents)/components/AgentsHeader/AgentsHeader.tsx b/apps/web/src/app/(agents)/components/AgentsHeader/AgentsHeader.tsx
index 62f852b53c2..243db056bb1 100644
--- a/apps/web/src/app/(agents)/components/AgentsHeader/AgentsHeader.tsx
+++ b/apps/web/src/app/(agents)/components/AgentsHeader/AgentsHeader.tsx
@@ -1,7 +1,9 @@
"use client";
import { authClient } from "@superset/auth/client";
+import { isPaidPlan } from "@superset/shared/billing";
import { Avatar, AvatarFallback, AvatarImage } from "@superset/ui/avatar";
+import { Badge } from "@superset/ui/badge";
import { Drawer, DrawerContent, DrawerTitle } from "@superset/ui/drawer";
import {
DropdownMenu,
@@ -44,6 +46,14 @@ export function AgentsHeader() {
trpc.user.myOrganizations.queryOptions(),
);
+ const { data: activePlan } = useQuery(trpc.billing.activePlan.queryOptions());
+
+ const isPro = isPaidPlan(activePlan?.plan);
+ const planLabel =
+ isPro && activePlan?.plan
+ ? activePlan.plan.charAt(0).toUpperCase() + activePlan.plan.slice(1)
+ : null;
+
const user = session?.user;
const activeOrganizationId = session?.session?.activeOrganizationId;
const activeOrganization = organizations?.find(
@@ -191,7 +201,14 @@ export function AgentsHeader() {
Account menu
-
{user?.name}
+
+
{user?.name}
+ {isPro && (
+
+ {planLabel}
+
+ )}
+
{user?.email}
@@ -251,7 +268,14 @@ export function AgentsHeader() {
-
{user?.name}
+
+
{user?.name}
+ {isPro && (
+
+ {planLabel}
+
+ )}
+
{user?.email}
diff --git a/packages/trpc/src/router/billing/billing.ts b/packages/trpc/src/router/billing/billing.ts
index 3616f241006..664f17daf29 100644
--- a/packages/trpc/src/router/billing/billing.ts
+++ b/packages/trpc/src/router/billing/billing.ts
@@ -1,8 +1,9 @@
import { stripeClient } from "@superset/auth/stripe";
import { db } from "@superset/db/client";
import { members, subscriptions } from "@superset/db/schema";
+import { ACTIVE_SUBSCRIPTION_STATUSES } from "@superset/shared/billing";
import { TRPCError, type TRPCRouterRecord } from "@trpc/server";
-import { and, eq } from "drizzle-orm";
+import { and, desc, eq, inArray } from "drizzle-orm";
import type Stripe from "stripe";
import { z } from "zod";
import { env } from "../../env";
@@ -65,6 +66,25 @@ async function requireOwnerWithCustomer(ctx: {
}
export const billingRouter = {
+ activePlan: protectedProcedure.query(async ({ ctx }) => {
+ const activeOrgId = ctx.activeOrganizationId;
+ if (!activeOrgId) return { plan: "free" as const, status: null };
+
+ const subscription = await db.query.subscriptions.findFirst({
+ where: and(
+ eq(subscriptions.referenceId, activeOrgId),
+ inArray(subscriptions.status, ACTIVE_SUBSCRIPTION_STATUSES),
+ ),
+ orderBy: desc(subscriptions.createdAt),
+ });
+
+ if (!subscription) {
+ return { plan: "free" as const, status: null };
+ }
+
+ return { plan: subscription.plan, status: subscription.status };
+ }),
+
invoices: protectedProcedure.query(async ({ ctx }) => {
const activeOrgId = ctx.activeOrganizationId;
if (!activeOrgId) {