Skip to content

feat: cluster disabled admin sidebar tabs at the bottom#9867

Open
raunakab wants to merge 8 commits intomainfrom
feat/admin-sidebar-disabled-clustering
Open

feat: cluster disabled admin sidebar tabs at the bottom#9867
raunakab wants to merge 8 commits intomainfrom
feat/admin-sidebar-disabled-clustering

Conversation

@raunakab
Copy link
Copy Markdown
Contributor

@raunakab raunakab commented Apr 2, 2026

Description

  • Partition admin sidebar items into enabled and disabled groups
  • Enabled items render in their original sections; disabled items cluster at the bottom under their section headers
  • Separator between enabled and disabled groups
  • SidebarSection gains a disabled prop (wraps title in <Disabled>) and uses Hoverable for action visibility
  • Remove <Disabled> wrapper — use SidebarTab's native disabled prop
  • Gate Spending Limits behind EE
  • Move "Upgrade Plan" to an unlabeled group at the very bottom (only when no active subscription)

Addresses: https://linear.app/onyx-app/issue/ENG-3929/admin-sidebar-free-version-should-have-all-the-greyed-out-buttons-at.

Screenshots + Videos

image

Summary by cubic

Clusters disabled admin sidebar tabs at the bottom under their section headers, keeping enabled tabs in place and separated by a clear divider. Also gates Spending Limits behind EE and moves “Upgrade Plan” to an unlabeled bottom group when there’s no active subscription.

  • New Features
    • Partitions items into enabled/disabled; enabled render in original sections, disabled render at the bottom under their section headers.
    • Adds a separator between enabled and disabled groups.
    • Gates Spending Limits behind EE; disables the tab when EE is off.
    • Moves “Upgrade Plan” to an unlabeled group at the very end (only without an active subscription).
    • Adds a disabled prop to SidebarSection and uses Hoverable from @opal/core for action visibility; removes the <Disabled> wrapper in favor of SidebarTab’s native disabled prop from @opal/components.

Written for commit 5f64583. Summary will update on new commits.

raunakab added 8 commits April 2, 2026 11:40
Partition sidebar items into enabled and disabled groups. Enabled items
render in their original sections, disabled items cluster at the bottom
under their section headers. Remove <Disabled> wrapper in favor of
SidebarTab's native disabled prop.
- Spending Limits (TOKEN_RATE_LIMITS) is now disabled when EE is off
- Upgrade Plan moves to an unlabeled group at the very end of the list
  (only shown when there's no active subscription)
@raunakab raunakab requested a review from a team as a code owner April 2, 2026 18:47
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 2, 2026

Preview Deployment

Status Preview Commit Updated
https://onyx-preview-eh0vqibw6-danswer.vercel.app 5f64583 2026-04-02 18:50:34 UTC

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Apr 2, 2026

Greptile Summary

This PR refactors the admin sidebar to cluster disabled items (Enterprise-gated features) at the bottom of the nav under their section headers, separated from enabled items by a divider. It also moves "Upgrade Plan" to an unlabeled group at the very bottom (only for non-subscribers), gates Token Rate Limits behind EE, and migrates SidebarSection from CSS group-hover to Hoverable.Root/Hoverable.Item.

Key changes:

  • AdminSidebar now splits filtered into enabled and disabled arrays, groups each separately, and renders them in two blocks with a Separator between them.
  • SidebarSection gains a disabled prop that wraps the title in Disabled; the className prop is removed (no callers used it).
  • "Upgrade Plan" is pushed into SECTIONS.UNLABELED at the end of buildItems, so it always appears as an unlabeled item at the very bottom.
  • P1 bug: the disabled-groups renderer does not include the !group.section guard that the enabled-groups renderer uses. A disabled item in SECTIONS.UNLABELED (e.g. CUSTOM_ANALYTICS on a non-Enterprise self-hosted instance) causes a SidebarSection to be rendered with title="", producing a phantom sticky section header in the UI.

Confidence Score: 4/5

  • Safe to merge after fixing the empty-section-header bug for UNLABELED disabled items.
  • One P1 logic bug: the disabled-groups renderer is missing the !group.section guard, which causes a phantom sticky header for non-Enterprise self-hosted instances with custom analytics enabled. All other changes (SidebarSection refactor, Upgrade Plan relocation, EE gating of Token Rate Limits) are clean and consistent. No security, data-loss, or multi-tenancy concerns.
  • web/src/sections/sidebar/AdminSidebar.tsx — the disabledGroups rendering block (lines 324-336) needs the !group.section guard.

Important Files Changed

Filename Overview
web/src/sections/sidebar/AdminSidebar.tsx Splits items into enabled/disabled groups and renders them separately with a separator; contains a P1 bug where UNLABELED disabled items are wrapped in SidebarSection with an empty title, producing a phantom sticky header.
web/src/sections/sidebar/SidebarSection.tsx Adds disabled prop (wraps title in <Disabled>), migrates action visibility from CSS group-hover to Hoverable.Root/Hoverable.Item, and removes the className prop (no existing callers pass it).

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[buildItems] --> B[allItems array]
    B --> C[useFilter → filtered]
    C --> D[enabled = filtered where !disabled]
    C --> E[disabled = filtered where disabled]
    D --> F[groupBySection → enabledGroups]
    E --> G[groupBySection → disabledGroups]

    F --> H{group.section?}
    H -- No --> I[plain div wrapper]
    H -- Yes --> J[SidebarSection]

    K[disabledGroups.length > 0] --> L[Separator]

    G --> M[SidebarSection with disabled prop]
    M -- Bug: no section check --> N["SidebarSection title='' if UNLABELED"]

    style N fill:#ffcccc,stroke:#ff0000
Loading
Prompt To Fix All With AI
This is a comment left during a code review.
Path: web/src/sections/sidebar/AdminSidebar.tsx
Line: 324-336

Comment:
**Missing section guard for UNLABELED disabled items**

The enabled-groups renderer at lines 299–320 checks `if (!group.section)` and wraps unlabeled items in a plain `div` to avoid rendering an empty section header. The disabled-groups renderer here is missing this guard — it unconditionally wraps every group in `SidebarSection` regardless of whether `group.section` is `""` (i.e. `SECTIONS.UNLABELED`).

There is a real code path that hits this today: `ADMIN_ROUTES.CUSTOM_ANALYTICS` is pushed with `SECTIONS.UNLABELED` and `disabled: !enableEnterprise` at lines 91–96. On any self-hosted instance without Enterprise, this item ends up in a `disabledGroup` with `section = ""`. Rendering `SidebarSection` with an empty `title` produces a sticky layout-consuming phantom header above the disabled tab.

The fix mirrors the pattern already used for enabled groups: add an `if (!group.section)` branch that returns a plain `div` (without a `SidebarSection` wrapper) for unlabeled disabled items.

How can I resolve this? If you propose a fix, please make it concise.

Reviews (1): Last reviewed commit: "feat: gate Spending Limits behind EE, mo..." | Re-trigger Greptile

Comment on lines +324 to +336
{disabledGroups.map((group, groupIndex) => (
<SidebarSection
key={`disabled-${groupIndex}`}
title={group.section}
disabled
>
{group.items.map(({ link, icon, name }) => (
<SidebarTab key={link} disabled icon={icon}>
{name}
</SidebarTab>
))}
</SidebarSection>
))}
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.

P1 Missing section guard for UNLABELED disabled items

The enabled-groups renderer at lines 299–320 checks if (!group.section) and wraps unlabeled items in a plain div to avoid rendering an empty section header. The disabled-groups renderer here is missing this guard — it unconditionally wraps every group in SidebarSection regardless of whether group.section is "" (i.e. SECTIONS.UNLABELED).

There is a real code path that hits this today: ADMIN_ROUTES.CUSTOM_ANALYTICS is pushed with SECTIONS.UNLABELED and disabled: !enableEnterprise at lines 91–96. On any self-hosted instance without Enterprise, this item ends up in a disabledGroup with section = "". Rendering SidebarSection with an empty title produces a sticky layout-consuming phantom header above the disabled tab.

The fix mirrors the pattern already used for enabled groups: add an if (!group.section) branch that returns a plain div (without a SidebarSection wrapper) for unlabeled disabled items.

Prompt To Fix With AI
This is a comment left during a code review.
Path: web/src/sections/sidebar/AdminSidebar.tsx
Line: 324-336

Comment:
**Missing section guard for UNLABELED disabled items**

The enabled-groups renderer at lines 299–320 checks `if (!group.section)` and wraps unlabeled items in a plain `div` to avoid rendering an empty section header. The disabled-groups renderer here is missing this guard — it unconditionally wraps every group in `SidebarSection` regardless of whether `group.section` is `""` (i.e. `SECTIONS.UNLABELED`).

There is a real code path that hits this today: `ADMIN_ROUTES.CUSTOM_ANALYTICS` is pushed with `SECTIONS.UNLABELED` and `disabled: !enableEnterprise` at lines 91–96. On any self-hosted instance without Enterprise, this item ends up in a `disabledGroup` with `section = ""`. Rendering `SidebarSection` with an empty `title` produces a sticky layout-consuming phantom header above the disabled tab.

The fix mirrors the pattern already used for enabled groups: add an `if (!group.section)` branch that returns a plain `div` (without a `SidebarSection` wrapper) for unlabeled disabled items.

How can I resolve this? If you propose a fix, please make it concise.

Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

1 issue found across 2 files

Confidence score: 4/5

  • This PR looks safe to merge overall, with a minor UI-ordering issue rather than a functional break.
  • In web/src/sections/sidebar/AdminSidebar.tsx, "Upgrade Plan" is still handled as enabled, so when disabled tabs are present it may not stay pinned to the bottom as intended.
  • Given the moderate confidence and low-to-mild severity (4/10), the risk is mainly layout/UX consistency instead of regression in core behavior.
  • Pay close attention to web/src/sections/sidebar/AdminSidebar.tsx - ensure the "Upgrade Plan" entry is rendered after disabled items so it consistently appears at the bottom.
Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="web/src/sections/sidebar/AdminSidebar.tsx">

<violation number="1" location="web/src/sections/sidebar/AdminSidebar.tsx:167">
P2: "Upgrade Plan" is still treated as an enabled item, so it renders before the disabled cluster. When any disabled tabs exist, it won’t appear at the very bottom as intended. Consider rendering this entry after the disabled groups (or in its own trailing group) when there’s no subscription.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.


// 8. Upgrade Plan (admin only, no subscription)
if (!isCurator && !hasSubscription) {
items.push({
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot Apr 2, 2026

Choose a reason for hiding this comment

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

P2: "Upgrade Plan" is still treated as an enabled item, so it renders before the disabled cluster. When any disabled tabs exist, it won’t appear at the very bottom as intended. Consider rendering this entry after the disabled groups (or in its own trailing group) when there’s no subscription.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At web/src/sections/sidebar/AdminSidebar.tsx, line 167:

<comment>"Upgrade Plan" is still treated as an enabled item, so it renders before the disabled cluster. When any disabled tabs exist, it won’t appear at the very bottom as intended. Consider rendering this entry after the disabled groups (or in its own trailing group) when there’s no subscription.</comment>

<file context>
@@ -165,6 +162,16 @@ function buildItems(
 
+  // 8. Upgrade Plan (admin only, no subscription)
+  if (!isCurator && !hasSubscription) {
+    items.push({
+      section: SECTIONS.UNLABELED,
+      name: "Upgrade Plan",
</file context>
Fix with Cubic

Base automatically changed from refactor/sidebar-tab-opal-v2 to main April 2, 2026 19:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants