From 1b714a7b15a38c9131890efc146924a88d518b6d Mon Sep 17 00:00:00 2001 From: Keiran Flanigan Date: Mon, 14 Jul 2025 20:38:57 -0700 Subject: [PATCH 1/2] update Billing nav settings --- .changeset/public-bags-stay.md | 6 +++ packages/clerk-js/src/core/clerk.ts | 8 ++-- .../src/core/resources/CommerceSettings.ts | 20 ++++++++++ .../OrganizationProfileRoutes.tsx | 16 ++++---- .../Subscriptions/SubscriptionsList.tsx | 37 +++++++++++-------- .../UserProfile/UserProfileRoutes.tsx | 16 ++++---- .../src/ui/utils/createCustomPages.tsx | 11 +++--- .../clerk-js/src/utils/componentGuards.ts | 16 ++++++-- packages/types/src/commerceSettings.ts | 16 ++++++++ 9 files changed, 102 insertions(+), 44 deletions(-) create mode 100644 .changeset/public-bags-stay.md diff --git a/.changeset/public-bags-stay.md b/.changeset/public-bags-stay.md new file mode 100644 index 00000000000..8ead721e2f4 --- /dev/null +++ b/.changeset/public-bags-stay.md @@ -0,0 +1,6 @@ +--- +'@clerk/clerk-js': patch +'@clerk/types': patch +--- + +Adjust the cases in which the Billing item shows within the `UserProfile` and `OrgProfile` components diff --git a/packages/clerk-js/src/core/clerk.ts b/packages/clerk-js/src/core/clerk.ts index f4cf76950df..859e820ff68 100644 --- a/packages/clerk-js/src/core/clerk.ts +++ b/packages/clerk-js/src/core/clerk.ts @@ -94,8 +94,8 @@ import { createAllowedRedirectOrigins, createBeforeUnloadTracker, createPageLifecycle, + disabledAllBillingFeatures, disabledAPIKeysFeature, - disabledBillingFeature, disabledOrganizationsFeature, errorThrower, generateSignatureWithCoinbaseWallet, @@ -576,7 +576,7 @@ export class Clerk implements ClerkInterface { public __internal_openCheckout = (props?: __internal_CheckoutProps): void => { this.assertComponentsReady(this.#componentControls); - if (disabledBillingFeature(this, this.environment)) { + if (disabledAllBillingFeatures(this, this.environment)) { if (this.#instanceType === 'development') { throw new ClerkRuntimeError(warnings.cannotRenderAnyCommerceComponent('Checkout'), { code: CANNOT_RENDER_BILLING_DISABLED_ERROR_CODE, @@ -605,7 +605,7 @@ export class Clerk implements ClerkInterface { public __internal_openPlanDetails = (props: __internal_PlanDetailsProps): void => { this.assertComponentsReady(this.#componentControls); - if (disabledBillingFeature(this, this.environment)) { + if (disabledAllBillingFeatures(this, this.environment)) { if (this.#instanceType === 'development') { throw new ClerkRuntimeError(warnings.cannotRenderAnyCommerceComponent('PlanDetails'), { code: CANNOT_RENDER_BILLING_DISABLED_ERROR_CODE, @@ -1060,7 +1060,7 @@ export class Clerk implements ClerkInterface { public mountPricingTable = (node: HTMLDivElement, props?: PricingTableProps): void => { this.assertComponentsReady(this.#componentControls); - if (disabledBillingFeature(this, this.environment)) { + if (disabledAllBillingFeatures(this, this.environment)) { if (this.#instanceType === 'development') { throw new ClerkRuntimeError(warnings.cannotRenderAnyCommerceComponent('PricingTable'), { code: CANNOT_RENDER_BILLING_DISABLED_ERROR_CODE, diff --git a/packages/clerk-js/src/core/resources/CommerceSettings.ts b/packages/clerk-js/src/core/resources/CommerceSettings.ts index 104ce856c43..26909c9b756 100644 --- a/packages/clerk-js/src/core/resources/CommerceSettings.ts +++ b/packages/clerk-js/src/core/resources/CommerceSettings.ts @@ -11,6 +11,14 @@ export class CommerceSettings extends BaseResource implements CommerceSettingsRe enabled: false, hasPaidUserPlans: false, hasPaidOrgPlans: false, + organization: { + enabled: false, + hasPaidPlans: false, + }, + user: { + enabled: false, + hasPaidPlans: false, + }, }; public constructor(data: CommerceSettingsJSON | CommerceSettingsJSONSnapshot | null = null) { @@ -27,6 +35,10 @@ export class CommerceSettings extends BaseResource implements CommerceSettingsRe this.billing.enabled = data.billing.enabled || false; this.billing.hasPaidUserPlans = data.billing.has_paid_user_plans || false; this.billing.hasPaidOrgPlans = data.billing.has_paid_org_plans || false; + this.billing.organization.enabled = data.billing.organization.enabled || false; + this.billing.organization.hasPaidPlans = data.billing.organization.has_paid_plans || false; + this.billing.user.enabled = data.billing.user.enabled || false; + this.billing.user.hasPaidPlans = data.billing.user.has_paid_plans || false; return this; } @@ -38,6 +50,14 @@ export class CommerceSettings extends BaseResource implements CommerceSettingsRe enabled: this.billing.enabled, has_paid_user_plans: this.billing.hasPaidUserPlans, has_paid_org_plans: this.billing.hasPaidOrgPlans, + organization: { + enabled: this.billing.organization.enabled, + has_paid_plans: this.billing.organization.hasPaidPlans, + }, + user: { + enabled: this.billing.user.enabled, + has_paid_plans: this.billing.user.hasPaidPlans, + }, }, } as unknown as CommerceSettingsJSONSnapshot; } diff --git a/packages/clerk-js/src/ui/components/OrganizationProfile/OrganizationProfileRoutes.tsx b/packages/clerk-js/src/ui/components/OrganizationProfile/OrganizationProfileRoutes.tsx index 863a173afd3..f153a991b93 100644 --- a/packages/clerk-js/src/ui/components/OrganizationProfile/OrganizationProfileRoutes.tsx +++ b/packages/clerk-js/src/ui/components/OrganizationProfile/OrganizationProfileRoutes.tsx @@ -83,7 +83,7 @@ export const OrganizationProfileRoutes = () => { - {commerceSettings.billing.enabled && commerceSettings.billing.hasPaidOrgPlans && ( + {commerceSettings.billing.organization.enabled ? ( has({ permission: 'org:sys_billing:read' }) || has({ permission: 'org:sys_billing:manage' }) @@ -96,11 +96,13 @@ export const OrganizationProfileRoutes = () => { - - - - - + {commerceSettings.billing.organization.hasPaidPlans ? ( + + + + + + ) : null} @@ -114,7 +116,7 @@ export const OrganizationProfileRoutes = () => { - )} + ) : null} {apiKeysSettings.enabled && ( diff --git a/packages/clerk-js/src/ui/components/Subscriptions/SubscriptionsList.tsx b/packages/clerk-js/src/ui/components/Subscriptions/SubscriptionsList.tsx index effb90e695b..0d3ef9dafad 100644 --- a/packages/clerk-js/src/ui/components/Subscriptions/SubscriptionsList.tsx +++ b/packages/clerk-js/src/ui/components/Subscriptions/SubscriptionsList.tsx @@ -2,6 +2,7 @@ import { ProfileSection } from '@/ui/elements/Section'; import { useProtect } from '../../common'; import { + useEnvironment, usePlansContext, useSubscriberTypeContext, useSubscriberTypeLocalizationRoot, @@ -44,6 +45,7 @@ export function SubscriptionsList({ has => has({ permission: 'org:sys_billing:manage' }) || subscriberType === 'user', ); const { navigate } = useRouter(); + const { commerceSettings } = useEnvironment(); const sortedSubscriptions = subscriptions.sort((a, b) => { // alway put active subscriptions first @@ -188,22 +190,25 @@ export function SubscriptionsList({ )} - 0 ? arrowButtonText : arrowButtonEmptyText} - sx={[ - t => ({ - justifyContent: 'start', - height: t.sizes.$8, - }), - ]} - leftIcon={subscriptions.length > 0 ? ArrowsUpDown : Plus} - leftIconSx={t => ({ - width: t.sizes.$4, - height: t.sizes.$4, - })} - onClick={() => void navigate('plans')} - /> + {(commerceSettings.billing.user.hasPaidPlans && subscriberType === 'user') || + (commerceSettings.billing.organization.hasPaidPlans && subscriberType === 'org') ? ( + 0 ? arrowButtonText : arrowButtonEmptyText} + sx={[ + t => ({ + justifyContent: 'start', + height: t.sizes.$8, + }), + ]} + leftIcon={subscriptions.length > 0 ? ArrowsUpDown : Plus} + leftIconSx={t => ({ + width: t.sizes.$4, + height: t.sizes.$4, + })} + onClick={() => void navigate('plans')} + /> + ) : null} ); } diff --git a/packages/clerk-js/src/ui/components/UserProfile/UserProfileRoutes.tsx b/packages/clerk-js/src/ui/components/UserProfile/UserProfileRoutes.tsx index 8455fb688d1..76151e5b809 100644 --- a/packages/clerk-js/src/ui/components/UserProfile/UserProfileRoutes.tsx +++ b/packages/clerk-js/src/ui/components/UserProfile/UserProfileRoutes.tsx @@ -80,7 +80,7 @@ export const UserProfileRoutes = () => { - {commerceSettings.billing.enabled && commerceSettings.billing.hasPaidUserPlans && ( + {commerceSettings.billing.user.enabled ? ( @@ -88,11 +88,13 @@ export const UserProfileRoutes = () => { - - - - - + {commerceSettings.billing.user.hasPaidPlans ? ( + + + + + + ) : null} @@ -105,7 +107,7 @@ export const UserProfileRoutes = () => { - )} + ) : null} {apiKeysSettings.enabled && ( diff --git a/packages/clerk-js/src/ui/utils/createCustomPages.tsx b/packages/clerk-js/src/ui/utils/createCustomPages.tsx index a856048aeb7..ce9a45d0061 100644 --- a/packages/clerk-js/src/ui/utils/createCustomPages.tsx +++ b/packages/clerk-js/src/ui/utils/createCustomPages.tsx @@ -3,9 +3,8 @@ import type { CustomPage, EnvironmentResource, LoadedClerk } from '@clerk/types' import { canViewOrManageAPIKeys, disabledAPIKeysFeature, - disabledBillingFeature, - hasPaidOrgPlans, - hasPaidUserPlans, + disabledOrganizationBillingFeature, + disabledUserBillingFeature, isValidUrl, } from '../../utils'; import { ORGANIZATION_PROFILE_NAVBAR_ROUTE_ID, USER_PROFILE_NAVBAR_ROUTE_ID } from '../constants'; @@ -97,9 +96,9 @@ const createCustomPages = ( organization?: boolean, ) => { const { INITIAL_ROUTES, pageToRootNavbarRouteMap, validReorderItemLabels } = getDefaultRoutes({ - commerce: - !disabledBillingFeature(clerk, environment) && - (organization ? hasPaidOrgPlans(clerk, environment) : hasPaidUserPlans(clerk, environment)), + commerce: organization + ? !disabledOrganizationBillingFeature(clerk, environment) + : !disabledUserBillingFeature(clerk, environment), apiKeys: !disabledAPIKeysFeature(clerk, environment) && (organization ? canViewOrManageAPIKeys(clerk) : true), }); diff --git a/packages/clerk-js/src/utils/componentGuards.ts b/packages/clerk-js/src/utils/componentGuards.ts index 03770b41936..baf23904b17 100644 --- a/packages/clerk-js/src/utils/componentGuards.ts +++ b/packages/clerk-js/src/utils/componentGuards.ts @@ -22,16 +22,24 @@ export const disabledOrganizationsFeature: ComponentGuard = (_, environment) => return !environment?.organizationSettings.enabled; }; -export const disabledBillingFeature: ComponentGuard = (_, environment) => { - return !environment?.commerceSettings.billing.enabled; +export const disabledUserBillingFeature: ComponentGuard = (_, environment) => { + return !environment?.commerceSettings.billing.user.enabled; +}; + +export const disabledOrganizationBillingFeature: ComponentGuard = (_, environment) => { + return !environment?.commerceSettings.billing.organization.enabled; +}; + +export const disabledAllBillingFeatures: ComponentGuard = (_, environment) => { + return disabledUserBillingFeature(_, environment) && disabledOrganizationBillingFeature(_, environment); }; export const hasPaidOrgPlans: ComponentGuard = (_, environment) => { - return environment?.commerceSettings.billing.hasPaidOrgPlans || false; + return environment?.commerceSettings.billing.organization.hasPaidPlans || false; }; export const hasPaidUserPlans: ComponentGuard = (_, environment) => { - return environment?.commerceSettings.billing.hasPaidUserPlans || false; + return environment?.commerceSettings.billing.user.hasPaidPlans || false; }; export const disabledAPIKeysFeature: ComponentGuard = (_, environment) => { diff --git a/packages/types/src/commerceSettings.ts b/packages/types/src/commerceSettings.ts index 3be9ede7370..d41bf633ff9 100644 --- a/packages/types/src/commerceSettings.ts +++ b/packages/types/src/commerceSettings.ts @@ -9,6 +9,14 @@ export interface CommerceSettingsJSON extends ClerkResourceJSON { stripe_publishable_key: string; has_paid_user_plans: boolean; has_paid_org_plans: boolean; + organization: { + enabled: boolean; + has_paid_plans: boolean; + }; + user: { + enabled: boolean; + has_paid_plans: boolean; + }; }; } @@ -18,6 +26,14 @@ export interface CommerceSettingsResource extends ClerkResource { stripePublishableKey: string; hasPaidUserPlans: boolean; hasPaidOrgPlans: boolean; + organization: { + enabled: boolean; + hasPaidPlans: boolean; + }; + user: { + enabled: boolean; + hasPaidPlans: boolean; + }; }; __internal_toSnapshot: () => CommerceSettingsJSONSnapshot; From 1bf510b550f0d734fc0113001f93d3f5a87cc2db Mon Sep 17 00:00:00 2001 From: Keiran Flanigan Date: Wed, 16 Jul 2025 09:44:25 -0700 Subject: [PATCH 2/2] bundle bump --- packages/clerk-js/bundlewatch.config.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/clerk-js/bundlewatch.config.json b/packages/clerk-js/bundlewatch.config.json index e823a807a56..2f4e6a1d8b3 100644 --- a/packages/clerk-js/bundlewatch.config.json +++ b/packages/clerk-js/bundlewatch.config.json @@ -1,6 +1,6 @@ { "files": [ - { "path": "./dist/clerk.js", "maxSize": "616.27KB" }, + { "path": "./dist/clerk.js", "maxSize": "616.37KB" }, { "path": "./dist/clerk.browser.js", "maxSize": "72.2KB" }, { "path": "./dist/clerk.legacy.browser.js", "maxSize": "115.08KB" }, { "path": "./dist/clerk.headless*.js", "maxSize": "55KB" },