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

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

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

@Harsh23KashyapHarsh23Kashyap 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 #1524.
@coderabbitai

coderabbitaiBot 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 nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring 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 nameStatusExplanation
Linked Issues check✅ PassedThe changes satisfy issue #1524 by gating all sidebar upgrade buttons and badges on owner status with focused tests.
Out of Scope Changes check✅ PassedThe changelog, prop plumbing, implementation changes, and tests are directly related to issue #1524.
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.
Title check✅ PassedThe 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.

@coderabbitaicoderabbitaiBot 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 #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 freeto 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

2 participants

@Harsh23Kashyap@brendan-kellam
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

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

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

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

@Harsh23KashyapHarsh23Kashyap 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 #1524.
@coderabbitai

coderabbitaiBot 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 nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring 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 nameStatusExplanation
Linked Issues check✅ PassedThe changes satisfy issue #1524 by gating all sidebar upgrade buttons and badges on owner status with focused tests.
Out of Scope Changes check✅ PassedThe changelog, prop plumbing, implementation changes, and tests are directly related to issue #1524.
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.
Title check✅ PassedThe 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.

@coderabbitaicoderabbitaiBot 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 #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 freeto 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

2 participants

@Harsh23Kashyap@brendan-kellam
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

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

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

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

@Harsh23KashyapHarsh23Kashyap 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 #1524.
@coderabbitai

coderabbitaiBot 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 nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring 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 nameStatusExplanation
Linked Issues check✅ PassedThe changes satisfy issue #1524 by gating all sidebar upgrade buttons and badges on owner status with focused tests.
Out of Scope Changes check✅ PassedThe changelog, prop plumbing, implementation changes, and tests are directly related to issue #1524.
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.
Title check✅ PassedThe 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.

@coderabbitaicoderabbitaiBot 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 #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 freeto 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

2 participants

@Harsh23Kashyap@brendan-kellam
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

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

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

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

@Harsh23KashyapHarsh23Kashyap 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 #1524.
@coderabbitai

coderabbitaiBot 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 nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring 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 nameStatusExplanation
Linked Issues check✅ PassedThe changes satisfy issue #1524 by gating all sidebar upgrade buttons and badges on owner status with focused tests.
Out of Scope Changes check✅ PassedThe changelog, prop plumbing, implementation changes, and tests are directly related to issue #1524.
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.
Title check✅ PassedThe 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.

@coderabbitaicoderabbitaiBot 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 #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 freeto 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

2 participants

@Harsh23Kashyap@brendan-kellam
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

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

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

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

@Harsh23KashyapHarsh23Kashyap 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 #1524.
@coderabbitai

coderabbitaiBot 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 nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring 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 nameStatusExplanation
Linked Issues check✅ PassedThe changes satisfy issue #1524 by gating all sidebar upgrade buttons and badges on owner status with focused tests.
Out of Scope Changes check✅ PassedThe changelog, prop plumbing, implementation changes, and tests are directly related to issue #1524.
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.
Title check✅ PassedThe 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.

@coderabbitaicoderabbitaiBot 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 #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 freeto 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

2 participants

@Harsh23Kashyap@brendan-kellam
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

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

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

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

@Harsh23KashyapHarsh23Kashyap 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 #1524.
@coderabbitai

coderabbitaiBot 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 nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring 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 nameStatusExplanation
Linked Issues check✅ PassedThe changes satisfy issue #1524 by gating all sidebar upgrade buttons and badges on owner status with focused tests.
Out of Scope Changes check✅ PassedThe changelog, prop plumbing, implementation changes, and tests are directly related to issue #1524.
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.
Title check✅ PassedThe 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.

@coderabbitaicoderabbitaiBot 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 #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 freeto 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

2 participants

@Harsh23Kashyap@brendan-kellam
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

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

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

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

@Harsh23KashyapHarsh23Kashyap 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 #1524.
@coderabbitai

coderabbitaiBot 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 nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring 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 nameStatusExplanation
Linked Issues check✅ PassedThe changes satisfy issue #1524 by gating all sidebar upgrade buttons and badges on owner status with focused tests.
Out of Scope Changes check✅ PassedThe changelog, prop plumbing, implementation changes, and tests are directly related to issue #1524.
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.
Title check✅ PassedThe 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.

@coderabbitaicoderabbitaiBot 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 #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 freeto 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

2 participants

@Harsh23Kashyap@brendan-kellam
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

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

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

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

@Harsh23KashyapHarsh23Kashyap 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 #1524.
@coderabbitai

coderabbitaiBot 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 nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring 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 nameStatusExplanation
Linked Issues check✅ PassedThe changes satisfy issue #1524 by gating all sidebar upgrade buttons and badges on owner status with focused tests.
Out of Scope Changes check✅ PassedThe changelog, prop plumbing, implementation changes, and tests are directly related to issue #1524.
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.
Title check✅ PassedThe 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.

@coderabbitaicoderabbitaiBot 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 #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 freeto 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

2 participants

@Harsh23Kashyap@brendan-kellam