Skip to content

fix(web): hide upgrade prompts from non-owner users in the sidebar - #1525

Open
Harsh23Kashyap wants to merge 2 commits into
sourcebot-dev:mainfrom
Harsh23Kashyap:fix/upgrade-prompts-owner-only
Open

fix(web): hide upgrade prompts from non-owner users in the sidebar#1525
Harsh23Kashyap wants to merge 2 commits into
sourcebot-dev:mainfrom
Harsh23Kashyap:fix/upgrade-prompts-owner-only

Conversation

@Harsh23Kashyap

@Harsh23Kashyap Harsh23Kashyap commented Jul 31, 2026

Copy link
Copy Markdown

Summary

The "Upgrade to Pro" button in the sidebar footer and the per-item "Upgrade" badges in the default and settings sidebars were rendered to any signed-in user, not just to the org's OWNER. A MEMBER (or a former owner who lost the role) would see prompts they cannot act on.

Fixes #1524.

What changes

Thread an isOwner prop from the sidebar index files through the Nav and SidebarBase components. Both UpgradeBadge rendering in the two Nav components and UpgradeButton rendering in SidebarBase are now gated on isOwner.

defaultSidebar/index.tsx already computed isOwner for the connection-stats notification dot — that value is now also passed to <Nav> and <SidebarBase>. settingsSidebar/index.tsx did not compute it at all; it now does (using the same getAuthContext + OrgRole.OWNER pattern) and threads it through.

Files

  • packages/web/src/app/(app)/@sidebar/components/sidebarBase.tsx — added isOwner?: boolean to SidebarBaseProps, gated the UpgradeButton on isOwner && !isValidLicenseActive.
  • packages/web/src/app/(app)/@sidebar/components/defaultSidebar/nav.tsx — added isOwner?: boolean to NavProps, gated showUpgradeBadge on isOwner && missingEntitlement.
  • packages/web/src/app/(app)/@sidebar/components/defaultSidebar/index.tsx — pass isOwner={isOwner} to <Nav> and <SidebarBase> (the value was already computed at line 43).
  • packages/web/src/app/(app)/@sidebar/components/settingsSidebar/nav.tsx — same pattern as defaultSidebar/nav.tsx.
  • packages/web/src/app/(app)/@sidebar/components/settingsSidebar/index.tsx — compute isOwner (same pattern as defaultSidebar/index.tsx) and pass to <Nav> and <SidebarBase>.
  • packages/web/src/app/(app)/@sidebar/components/defaultSidebar/nav.test.tsx — 3 vitest cases covering: (1) badge renders for an OWNER missing the entitlement, (2) badge is hidden for a non-owner MEMBER, (3) badge is hidden for an unauthenticated user.
  • CHANGELOG.md — one-sentence entry under [Unreleased] → Fixed.

Why this is in scope

The settings pages already gate on OWNER via authenticatedPage(..., { minRole: OrgRole.OWNER, ... }), so the upsell cards inside /settings/security and /settings/audit were never affected — only the shared sidebar was missing the check. The fix lives in the sidebar, not in the entitlement-gated pages, because the sidebar is shared across all users.

Why I didn't push the gate into UpgradeBadge itself

UpgradeBadge is a tiny presentational element. Gating belongs in the parent Nav so all callers (default and settings) get the same behaviour without each importing the auth context. The settings sidebar would otherwise need its own getAuthContext call just to render the badge.

Test coverage

3 vitest cases in nav.test.tsx:

  • OWNER + missing entitlement → badge renders. Locks in the positive case so a future change that drops the isOwner gate entirely is caught (the badge would still render for non-owners if the gate is removed, and the test suite would flip red).
  • MEMBER + missing entitlement → badge is hidden. This is the regression test for the bug. The entitlement check is unchanged, so the only thing that suppresses the badge in this case is the new isOwner gate.
  • Unauthenticated user → badge is hidden. The default isOwner = false means a missing prop is treated the same as a MEMBER, which matches the production fallback path.

The 7 pre-existing getEnv is not a function failures in ee/askmcp/..., ee/permissionSyncStatus/..., etc. are unchanged by this PR — they fail to load due to the OTel SDK version mismatch, before any of my code runs.

Backward compatibility

Pure UI behaviour change. No API, no schema, no migration. Existing users on the OWNER role see no change. Non-owner users stop seeing prompts they couldn't act on.

Risks

Minimal. The settings pages already gate on OWNER, so the upsell cards inside /settings/security and /settings/audit are unaffected. The only behaviour change is in the shared sidebar, which is what the issue called out.

Future work

  • A hasEntitlement('org-management') check on the settings pages' UpgradeBadge instances is a related but separate concern — those pages are already owner-only, so the badge is only seen by owners, but a future "view-only settings" feature would need to gate them. Not in scope for this PR.
  • A shared <RoleGate role="OWNER"> component would consolidate the isOwner && ... pattern across the sidebar and the settings pages. Punted; one-off gate is fine until we have three or more call sites.

Note

Low Risk
UI-only visibility change with no API or auth logic changes; owners keep prior behavior.

Overview
Fixes #1524 by limiting sidebar upgrade CTAs to org owners, who are the ones who can act on billing.

An isOwner flag (from getAuthContext() + OrgRole.OWNER) is passed from the default and settings sidebar entry points into SidebarBase and both Nav implementations. The footer Upgrade to Pro button now requires isOwner && !isValidLicenseActive, and per-nav Upgrade badges require isOwner in addition to the existing missing-entitlement check. The default sidebar reuses owner detection it already had for settings notifications; the settings sidebar adds the same lookup.

Vitest coverage was added for owner vs member vs unauthenticated badge/button visibility, plus a changelog entry under Unreleased → Fixed.

Reviewed by Cursor Bugbot for commit 680bfb8. Bugbot is set up for automated code reviews on this repo. Configure here.

Summary by CodeRabbit

  • Bug Fixes

    • Upgrade prompts in the sidebar are now shown only to organization owners.
    • Non-owner members and unauthenticated users no longer see the “Upgrade to Pro” button or upgrade badges.
    • Updated default and settings sidebars to apply the same role-based visibility consistently.
  • Documentation

    • Added an Unreleased changelog entry describing the sidebar upgrade prompt changes.

The "Upgrade to Pro" button in the sidebar footer and the per-item
"Upgrade" badges in the default and settings sidebars were rendered
to any signed-in user, not just to the org's OWNER. A MEMBER (or a
former owner who lost the role) would see prompts they cannot act on.

The settings pages already gate on OWNER via authenticatedPage, so
the upsell cards inside /settings/security and /settings/audit were
unaffected — only the shared sidebar was missing the check.

Fix: thread an isOwner prop from the sidebar index files through the
Nav and SidebarBase components. defaultSidebar/index.tsx already
computed isOwner for the connection-stats notification dot;
settingsSidebar/index.tsx now does the same. Both UpgradeBadge
rendering in the Nav components and UpgradeButton rendering in
SidebarBase are gated on isOwner.

Issue sourcebot-dev#1524.
@coderabbitai

coderabbitai Bot commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

This PR gates the sidebar footer "Upgrade to Pro" button and per-item "Upgrade" badges to organization owners only. It adds an isOwner prop to SidebarBase and to the Nav components in both default and settings sidebars. The prop is derived via getAuthContext and OrgRole checks and passed from each sidebar's index file. Test files validate badge and button visibility for owners, non-owners, and unauthenticated users. The changelog documents the fix.

Changes

Owner-gated upgrade UI

Layer / File(s) Summary
SidebarBase upgrade button gating and tests
packages/web/src/app/(app)/@sidebar/components/sidebarBase.tsx, packages/web/src/app/(app)/@sidebar/components/sidebarBase.test.tsx
Adds optional isOwner prop to SidebarBaseProps and function signature. UpgradeButton now renders only when isOwner && !isValidLicenseActive. Test suite verifies button visibility for owners with inactive license and hidden state for non-owners or owners with active license.
Default sidebar Nav upgrade badge gating, wiring, and tests
packages/web/src/app/(app)/@sidebar/components/defaultSidebar/nav.tsx, packages/web/src/app/(app)/@sidebar/components/defaultSidebar/index.tsx, packages/web/src/app/(app)/@sidebar/components/defaultSidebar/nav.test.tsx
Adds isOwner prop to NavProps. Gates showUpgradeBadge on isOwner and missing entitlements. Wires isOwner from index file to SidebarBase and Nav. Test suite with mocks validates badge renders for owners with missing entitlements and hides for non-owners or unauthenticated users.
Settings sidebar Nav upgrade badge gating, wiring, and tests
packages/web/src/app/(app)/@sidebar/components/settingsSidebar/index.tsx, packages/web/src/app/(app)/@sidebar/components/settingsSidebar/nav.tsx, packages/web/src/app/(app)/@sidebar/components/settingsSidebar/nav.test.tsx
Derives isOwner from authenticated context in index file using getAuthContext and OrgRole checks. Passes isOwner to SidebarBase and Nav. Adds isOwner prop to NavProps. Gates showUpgradeBadge on isOwner and missing entitlements. Test suite validates badge visibility for owners and absence for non-owners.
Changelog entry
CHANGELOG.md
Adds entry to Unreleased section documenting that upgrade UI elements ("Upgrade to Pro" button and badges) are now hidden for non-owner users in organizations.

Estimated code review effort: 2 (Simple) | ~14 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The changes satisfy issue #1524 by gating all sidebar upgrade buttons and badges on owner status with focused tests.
Out of Scope Changes check ✅ Passed The changelog, prop plumbing, implementation changes, and tests are directly related to issue #1524.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes hiding sidebar upgrade prompts from non-owner users, matching the main change.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
packages/web/src/app/(app)/@sidebar/components/defaultSidebar/nav.test.tsx (1)

73-98: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add regression tests for the settings nav and footer CTA.

This file renders only defaultSidebar/Nav. It does not exercise settingsSidebar/Nav, SidebarBase, or the owner-state wiring in either index component. A regression in those gates could pass this suite. Add owner, member, and unauthenticated cases for the settings navigation, plus owner and non-owner cases for SidebarBase when isValidLicenseActive is false.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/web/src/app/`(app)/@sidebar/components/defaultSidebar/nav.test.tsx
around lines 73 - 98, The current tests cover only defaultSidebar/Nav; extend
the suite to exercise settingsSidebar/Nav with owner, member, and
unauthenticated cases, verifying the upgrade badge appears only for owners. Also
add SidebarBase coverage with isValidLicenseActive=false for both owner and
non-owner states, asserting the owner-state gate and footer CTA behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@packages/web/src/app/`(app)/@sidebar/components/defaultSidebar/nav.test.tsx:
- Around line 73-98: The current tests cover only defaultSidebar/Nav; extend the
suite to exercise settingsSidebar/Nav with owner, member, and unauthenticated
cases, verifying the upgrade badge appears only for owners. Also add SidebarBase
coverage with isValidLicenseActive=false for both owner and non-owner states,
asserting the owner-state gate and footer CTA behavior.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: f74121a2-6550-44f0-9c40-38b9b0520532

📥 Commits

Reviewing files that changed from the base of the PR and between 8184cf4 and 1bce4cc.

📒 Files selected for processing (7)
  • CHANGELOG.md
  • packages/web/src/app/(app)/@sidebar/components/defaultSidebar/index.tsx
  • packages/web/src/app/(app)/@sidebar/components/defaultSidebar/nav.test.tsx
  • packages/web/src/app/(app)/@sidebar/components/defaultSidebar/nav.tsx
  • packages/web/src/app/(app)/@sidebar/components/settingsSidebar/index.tsx
  • packages/web/src/app/(app)/@sidebar/components/settingsSidebar/nav.tsx
  • packages/web/src/app/(app)/@sidebar/components/sidebarBase.tsx

CodeRabbit nitpick: the original test only covered the defaultSidebar
Nav. Extend the suite to cover the other two owner-gated surfaces
that the fix touches:

- settingsSidebar/nav.test.tsx (2 cases): badge renders for an OWNER
  missing the entitlement, hidden for a non-owner. Uses two mock nav
  groups (one gated, one ungated) so the test also verifies that the
  ungated item is not affected.
- sidebarBase.test.tsx (3 cases): UpgradeButton renders for an OWNER
  with no valid license, hidden for a non-owner with no valid
  license, hidden for an OWNER with a valid license. The last case
  locks in the pre-existing !isValidLicenseActive gate so the
  isOwner refactor doesn't accidentally drop it.

The SidebarBase test mocks the heavy client-only dependencies
(theme, keymap, PostHog, signOut, dropdown menu primitives) so the
test only exercises the upgrade CTA gate, which is the only behaviour
this PR changes.

8/8 sidebar tests pass; full suite 1005/1005 (5 new tests added since
the original 1000 baseline; the 7 pre-existing OTel-setup failures
in ee/askmcp and ee/permissionSyncStatus are unchanged by this PR).

Refs sourcebot-dev#1524.
@Harsh23Kashyap

Copy link
Copy Markdown
Author

Extended the test coverage in 680bfb82:

  • settingsSidebar/nav.test.tsx (2 cases): badge renders for an OWNER missing the required entitlement, hidden for a non-owner. Uses two mock nav groups (one gated, one ungated) so the test also verifies that the ungated item is unaffected by the new isOwner gate.
  • sidebarBase.test.tsx (3 cases): UpgradeButton renders for an OWNER with no valid license, hidden for a non-owner with no valid license, hidden for an OWNER with a valid license. The last case locks in the pre-existing !isValidLicenseActive gate so the isOwner refactor doesn't accidentally drop it.

8/8 sidebar tests pass; full suite 1005/1005 (5 new tests since the original 1000 baseline; the 7 pre-existing OTel-setup failures in ee/askmcp and ee/permissionSyncStatus are unchanged).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] UpgradeBadge and UpgradeButton render to non-owner users in the sidebar

1 participant