Skip to content

Commit fa3c3e5

Browse files
committed
feat(webapp): hide self-serve billing UI for managed-billing orgs
Self-serve billing UI is now hidden for managed-billing organizations. Uses the new `showSelfServe` subscription flag, defaulting to `true` for existing self-serve organizations.
1 parent ef04cc3 commit fa3c3e5

11 files changed

Lines changed: 268 additions & 119 deletions

File tree

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
---
2+
area: webapp
3+
type: improvement
4+
---
5+
6+
Hide self-serve billing and upgrade UI for organizations that are billed
7+
directly. When self-serve is off, plan pickers, upgrade buttons, billing
8+
alerts, and seat/branch purchase flows are replaced with a "Contact us"
9+
option, and the billing page shows a managed-billing message instead.

apps/webapp/app/components/navigation/OrganizationSettingsSideMenu.tsx

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import { LinkButton } from "../primitives/Buttons";
3030
import { HelpAndFeedback } from "./HelpAndFeedbackPopover";
3131
import { SideMenuHeader } from "./SideMenuHeader";
3232
import { SideMenuItem } from "./SideMenuItem";
33+
import { useShowSelfServe } from "~/hooks/useShowSelfServe";
3334
import { useCurrentPlan } from "~/routes/_app.orgs.$organizationSlug/route";
3435
import { Paragraph } from "../primitives/Paragraph";
3536
import { Badge } from "../primitives/Badge";
@@ -56,6 +57,7 @@ export function OrganizationSettingsSideMenu({
5657
const { isManagedCloud } = useFeatures();
5758
const featureFlags = useFeatureFlags();
5859
const currentPlan = useCurrentPlan();
60+
const showSelfServe = useShowSelfServe();
5961
const isAdmin = useHasAdminAccess();
6062
const showBuildInfo = isAdmin || !isManagedCloud;
6163

@@ -104,14 +106,16 @@ export function OrganizationSettingsSideMenu({
104106
) : undefined
105107
}
106108
/>
107-
<SideMenuItem
108-
name="Billing alerts"
109-
icon={BellAlertIcon}
110-
activeIconColor="text-rose-500"
111-
inactiveIconColor="text-rose-500"
112-
to={v3BillingAlertsPath(organization)}
113-
data-action="billing-alerts"
114-
/>
109+
{showSelfServe ? (
110+
<SideMenuItem
111+
name="Billing alerts"
112+
icon={BellAlertIcon}
113+
activeIconColor="text-rose-500"
114+
inactiveIconColor="text-rose-500"
115+
to={v3BillingAlertsPath(organization)}
116+
data-action="billing-alerts"
117+
/>
118+
) : null}
115119
</>
116120
)}
117121
{featureFlags.hasPrivateConnections && (
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { useCurrentPlan } from "~/routes/_app.orgs.$organizationSlug/route";
2+
3+
/** Whether the org should see self-serve billing UI (plan picker, Stripe checkout, upgrades). */
4+
export function useShowSelfServe(): boolean {
5+
const plan = useCurrentPlan();
6+
return plan?.v3Subscription?.showSelfServe ?? true;
7+
}

apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.alerts/route.tsx

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import assertNever from "assert-never";
1818
import { typedjson, useTypedLoaderData } from "remix-typedjson";
1919
import { z } from "zod";
2020
import { AlertsNoneDev, AlertsNoneDeployed } from "~/components/BlankStatePanels";
21+
import { Feedback } from "~/components/Feedback";
2122
import { EnvironmentCombo } from "~/components/environments/EnvironmentLabel";
2223
import { MainCenteredContainer, PageBody, PageContainer } from "~/components/layout/AppLayout";
2324
import { Button, LinkButton } from "~/components/primitives/Buttons";
@@ -45,6 +46,7 @@ import {
4546
import { EnabledStatus } from "~/components/runs/v3/EnabledStatus";
4647
import { prisma } from "~/db.server";
4748
import { useEnvironment } from "~/hooks/useEnvironment";
49+
import { useShowSelfServe } from "~/hooks/useShowSelfServe";
4850
import { useOrganization } from "~/hooks/useOrganizations";
4951
import { useProject } from "~/hooks/useProject";
5052
import { redirectWithSuccessMessage } from "~/models/message.server";
@@ -182,6 +184,7 @@ export default function Page() {
182184
const organization = useOrganization();
183185
const project = useProject();
184186
const environment = useEnvironment();
187+
const showSelfServe = useShowSelfServe();
185188

186189
const requiresUpgrade = limits.used >= limits.limit;
187190

@@ -343,9 +346,16 @@ export default function Page() {
343346
</Header3>
344347
)}
345348

346-
<LinkButton to={v3BillingPath(organization)} variant="secondary/small">
347-
Upgrade
348-
</LinkButton>
349+
{showSelfServe ? (
350+
<LinkButton to={v3BillingPath(organization)} variant="secondary/small">
351+
Upgrade
352+
</LinkButton>
353+
) : (
354+
<Feedback
355+
defaultValue="enterprise"
356+
button={<Button variant="secondary/small">Request more</Button>}
357+
/>
358+
)}
349359
</div>
350360
</div>
351361
</div>

apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.branches/route.tsx

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { typedjson, useTypedLoaderData } from "remix-typedjson";
1212
import { z } from "zod";
1313
import { BranchEnvironmentIconSmall } from "~/assets/icons/EnvironmentIcons";
1414
import { BranchesNoBranchableEnvironment, BranchesNoBranches } from "~/components/BlankStatePanels";
15+
import { Feedback } from "~/components/Feedback";
1516
import { GitMetadata } from "~/components/GitMetadata";
1617
import { V4Title } from "~/components/V4Badge";
1718
import { AdminDebugTooltip } from "~/components/admin/debugTooltip";
@@ -56,6 +57,7 @@ import {
5657
} from "~/components/primitives/Table";
5758
import { InfoIconTooltip, SimpleTooltip } from "~/components/primitives/Tooltip";
5859
import { useEnvironment } from "~/hooks/useEnvironment";
60+
import { useShowSelfServe } from "~/hooks/useShowSelfServe";
5961
import { useOrganization } from "~/hooks/useOrganizations";
6062
import { useProject } from "~/hooks/useProject";
6163

@@ -632,6 +634,7 @@ function PurchaseBranchesModal({
632634
planBranchLimit: number;
633635
triggerButton?: React.ReactNode;
634636
}) {
637+
const showSelfServe = useShowSelfServe();
635638
const fetcher = useFetcher();
636639
const lastSubmission =
637640
fetcher.data && typeof fetcher.data === "object" && "intent" in fetcher.data
@@ -679,6 +682,15 @@ function PurchaseBranchesModal({
679682
const pricePerBranch = branchPricing.centsPerStep / branchPricing.stepSize / 100;
680683
const title = extraBranches === 0 ? "Purchase extra branches…" : "Add/remove extra branches…";
681684

685+
if (!showSelfServe) {
686+
return (
687+
<Feedback
688+
defaultValue="enterprise"
689+
button={<Button variant="secondary/small">Request more</Button>}
690+
/>
691+
);
692+
}
693+
682694
return (
683695
<Dialog open={open} onOpenChange={setOpen}>
684696
<DialogTrigger asChild>

0 commit comments

Comments
 (0)