Skip to content

feat(sub-org): sub-organization menu selector UI/UX improvements#5599

Merged
IgorHorta merged 52 commits intomainfrom
igor/platfrm-216-address-sub-org-menu-selector-uiux
Mar 7, 2026
Merged

feat(sub-org): sub-organization menu selector UI/UX improvements#5599
IgorHorta merged 52 commits intomainfrom
igor/platfrm-216-address-sub-org-menu-selector-uiux

Conversation

@IgorHorta
Copy link
Contributor

@IgorHorta IgorHorta commented Mar 5, 2026

Context

This PR introduces several UI/UX improvements to the sub-organization experience:

  • Adds a Sub Orgs tab to the organization overview page with a searchable, paginated grid/list view
  • Adds a searchable filter to the sub-org dropdown menus in the navbar (org picker + sub-org breadcrumb)
  • Persists the selected tab (Projects / Sub Orgs) in URL search params across navigation
  • Exposes Edit/Delete actions in the sub-org role permissions UI
  • Adds a join sub-organization API endpoint and frontend hook
  • Fixes: hides "New Sub-Organization" and "Add Sub Org" buttons for users without sub-organization:create permission
  • Fixes: throws a clear 400 error when joining a sub-org the user is already a member of
  • Fixes: handles MFA flow when logging into a sub-organization from the Sub Orgs view

Screenshots

(https://www.loom.com/share/79ca41c0bff742a9a3c9747808c142e0)

Steps to verify the change

  • Navigate to org overview — verify "Sub Orgs" tab is present and URL param persists on navigation
  • As org admin: confirm "Add Sub Org" button and "New Sub-Organization" dropdown item are visible
  • As org member without sub-organization:create: confirm both buttons are hidden
  • Open navbar org picker on an org with sub-orgs — verify search filter works
  • Log into a sub-org that requires MFA — verify MFA screen appears and completes correctly
  • Attempt to join a sub-org you're already a member of — verify 400 already a member error

Type

  • Fix
  • Feature
  • Improvement
  • Breaking
  • Docs
  • Chore

Checklist

  • Title follows the conventional commit format: type(scope): short description (scope is optional, e.g., fix: prevent crash on sync or fix(api): handle null response).
  • Tested locally
  • Updated docs (if needed)
  • Read the contributing guide

IgorHorta added 18 commits March 4, 2026 19:41
Adds direct membership support for sub-organizations: new TJoinSubOrgDTO
type, joinSubOrg service method, POST /:subOrgId/memberships route, and
JOIN_SUB_ORGANIZATION audit event.

Made-with: Cursor
Adds TJoinSubOrganizationDTO type and useJoinSubOrganization mutation
hook calling POST /api/v1/sub-organizations/:subOrgId/memberships.

Made-with: Cursor
Introduces a vertical tab layout in ProjectsPage with Projects and
Sub Orgs tabs (root orgs only). SubOrgsView supports listing,
searching, sorting, creating, editing, deleting, and joining
sub-organizations with proper permission and license checks.

Made-with: Cursor
Adds Mintlify endpoint pages for delete and join sub-organization
operations, and registers the Sub Organizations group in docs.json.

Made-with: Cursor
Adds a magnifying glass search input with thin-scrollbar list and
divider to both the org dropdown flyout and breadcrumb sub-org
switcher, matching the ProjectSelect pattern.

Made-with: Cursor
- Row click logs into sub-org; removed dedicated login icon button
- cursor-pointer on member rows; role/keyboard handlers follow a11y
- Hide My/All toggle when user lacks DirectAccess permission
- When DirectAccess is missing show only accessible sub-orgs (no badge)
- Joined badge only shown in All Sub Orgs view with DirectAccess

Made-with: Cursor
Match existing patterns (OrgRoleTable, AuditLogStreamRow) by using
colorSchema="secondary" and className="w-6" on the options IconButton.

Made-with: Cursor
Adds Edit and Delete actions to the SubOrganization permission subject,
replacing the incorrect Settings subject fallback. Updates backend
service, permission schema, default admin role, frontend types, role
modify utils, and SubOrgsView to use the dedicated sub-org actions.

Made-with: Cursor
Adds Edit and Delete checkboxes to OrgPermissionSubOrgRow so the new
actions appear in the Access Control role editor, alongside Full Access
and No Access presets.

Made-with: Cursor
Adds validateSearch to the ProjectsPage route and replaces useState
with useSearch/useNavigate so the active tab (Projects vs Sub Orgs)
is reflected in the URL and survives page refreshes.

Made-with: Cursor
@linear
Copy link

linear bot commented Mar 5, 2026

@maidul98
Copy link
Collaborator

maidul98 commented Mar 5, 2026

Snyk checks have passed. No issues have been found so far.

Status Scanner Critical High Medium Low Total (0)
Open Source Security 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

@greptile-apps
Copy link
Contributor

greptile-apps bot commented Mar 5, 2026

Greptile Summary

This PR delivers a solid set of UI/UX improvements to the sub-organization experience: a searchable/paginated Sub-Orgs table in the Settings page, client-side search filters in both navbar dropdowns, new Delete and Join API endpoints (with audit logging), permission-gated Edit/Delete actions in the role UI, and an MFA flow for switching into sub-orgs. The overall structure is well-organized and consistent with existing patterns.

Key concerns identified during review:

  • license-fns.ts unexpectedly enables six unrelated enterprise features (rbac, samlSSO, enforceGoogleSSO, oidcSSO, groups, enforceMfa) by default for all self-hosted/on-prem deployments. Only subOrganization: true is relevant to this PR; the rest silently unlock paid capabilities for every existing on-prem instance and should be explicitly justified or removed.
  • Navbar sub-org query regressed from limit 500 to the backend default of 25 — removing the explicit limit: 500 means the org-picker and breadcrumb dropdowns will silently omit accessible sub-orgs beyond the first 25, which is a functional regression for users with many sub-orgs.
  • subOrgMenuSearch is not cleared when the org-picker sub-menu closes, creating a minor UX inconsistency with the breadcrumb dropdown (which does reset on close).

Confidence Score: 2/5

  • Not safe to merge until the license-fns.ts change is reviewed — it silently enables multiple enterprise features (SSO, RBAC, groups, MFA enforcement) by default for all self-hosted deployments.
  • The frontend and backend sub-org logic is generally correct and well-structured. However, the change to license-fns.ts is a high-risk, broadly scoped modification that goes far beyond the stated UI/UX improvements and could unexpectedly unlock paid features for every on-prem deployment. Until that change is explicitly confirmed or scoped down, the PR should not be merged.
  • backend/src/ee/services/license/license-fns.ts requires immediate attention — the bulk feature flag changes are unrelated to sub-org UI/UX and may have licensing/security implications for self-hosted instances.

Important Files Changed

Filename Overview
backend/src/ee/services/license/license-fns.ts Enables 7 enterprise feature flags by default for on-prem (rbac, subOrganization, samlSSO, enforceGoogleSSO, oidcSSO, groups, enforceMfa) — only subOrganization is needed by this PR; the rest are unrelated and could silently unlock paid features for all self-hosted instances.
backend/src/ee/routes/v1/sub-org-router.ts Adds DELETE and POST /memberships (join) routes with proper auth, rate limiting, and audit logging; operationId annotations added to existing routes. Changes look correct and consistent with existing patterns.
backend/src/ee/services/sub-org/sub-org-service.ts Adds joinSubOrg (assigns Member role, checks for existing membership) and deleteSubOrg (permission-gated); refactors permissionActor → permission naming; logic is sound and the already-member 400 check is correctly implemented.
backend/src/services/org/org-dal.ts Refactors listSubOrganizations to support search/orderBy/orderDirection/pagination and returns totalCount; OR conditions in accessibility subquery are properly grouped inside a callback (safe); orderBy is typed as string at the DAL level which allows unchecked column interpolation if called outside the router.
frontend/src/layouts/OrganizationLayout/components/NavBar/Navbar.tsx Adds SubOrgFilterList with client-side search for both org-picker and breadcrumb dropdowns; removes explicit limit:500 causing a regression to the backend default of 25; breadcrumb search correctly resets on close but org-picker search does not.
frontend/src/pages/organization/SettingsPage/components/OrgSubOrgsTab/OrgSubOrgsTab.tsx New paginated, searchable sub-org management table with edit/delete/join actions, MFA flow support, and permission gating; login error is properly caught on row click; edit and delete have try/catch with notifications.
frontend/src/pages/organization/SettingsPage/components/OrgTabGroup/OrgTabGroup.tsx Adds Sub Organizations tab with requiresFeature gating that shows upgrade modal when enterprise plan is absent; correctly avoids rendering a TabPanel for gated tabs; tab state initialisation and upgrade flow look correct.
frontend/src/hooks/api/subOrganizations/mutations.tsx Adds useDeleteSubOrganization and useJoinSubOrganization mutations that invalidate the sub-org query cache on success; both lack onError handlers but this is handled by the consuming component (OrgSubOrgsTab).

Comments Outside Diff (3)

  1. backend/src/ee/services/license/license-fns.ts, line 271-294 (link)

    Unrelated enterprise features enabled by default for on-prem

    This PR enables seven separate enterprise feature flags for on-prem / self-hosted deployments — well beyond the single subOrganization: true that this PR actually requires:

    Feature Before After
    rbac false true
    subOrganization false true
    samlSSO false true
    enforceGoogleSSO false true
    oidcSSO false true
    groups false true
    enforceMfa false true

    Only subOrganization: true is plausibly needed for the sub-org features in this PR. Enabling SAML SSO, OIDC SSO, Google SSO enforcement, RBAC, groups, and MFA enforcement for all self-hosted instances as a side-effect of a UI/UX PR is a very broad and potentially breaking product/licensing change. Any self-hosted deployment that relies on feature-gating these capabilities (e.g., restricting SSO to paid tiers) will silently have those gates removed.

    Please confirm each change is intentional, and if so, call it out explicitly in the PR description and changelog so customers and the licensing team are aware.

  2. frontend/src/layouts/OrganizationLayout/components/NavBar/Navbar.tsx, line 1182 (link)

    Navbar sub-org query regressed from 500 to default 25

    The previous code fetched up to 500 accessible sub-orgs for the navbar dropdown:

    subOrganizationsQuery.list({ limit: 500, isAccessible: true })

    The new code omits limit, so the backend falls back to its default of 25 (the Zod schema in sub-org-router.ts has .default(25)). For any user who is a member of more than 25 sub-orgs, the overflow entries will never appear in the navbar dropdown, regardless of whether they use the filter.

    Consider passing an explicit higher limit (e.g., limit: 500) or, if the real goal is to use server-side search, remove the client-side SubOrgFilterList filtering and instead pass the search value to the API call so the server handles filtering across all records.

    const subOrgQuery = subOrganizationsQuery.list({ limit: 500, isAccessible: true });
  3. frontend/src/layouts/OrganizationLayout/components/NavBar/Navbar.tsx, line 1241-1246 (link)

    subOrgMenuSearch not cleared when the org-picker sub-menu closes

    The breadcrumb dropdown correctly resets its search state when closed:

    onOpenChange={(open) => {
      if (!open) setSubOrgBreadcrumbSearch("");
    }}

    The DropdownSubMenu that wraps the org-picker sub-menu has no equivalent handler for subOrgMenuSearch. After a user searches in the org-picker, closes the dropdown, and reopens it, the previous search term is still visible — inconsistent with the breadcrumb behavior.

    Consider adding an onOpenChange handler to the DropdownSubMenu (if the component exposes one) to clear subOrgMenuSearch on close, matching the breadcrumb pattern.

Last reviewed commit: 11e5ada

@IgorHorta
Copy link
Contributor Author

@greptile review this again plz

Move the OrgPermissionCan + "New Sub-Organization" button into SubOrgFilterList
to eliminate duplicated markup at both call sites.

Made-with: Cursor
…sion gates

Rewrite OrgSubOrgsTab using v3 design system (UnstableCard, UnstableTable,
UnstablePagination, UnstableDropdownMenu, UnstableEmpty, InputGroup) and
replace FontAwesome icons with lucide-react equivalents. Wrap edit, delete,
and join actions with OrgPermissionCan to hide UI when the user lacks the
corresponding permissions. Only show cursor-pointer on rows when the user
has DirectAccess permission.

Made-with: Cursor
@gitguardian
Copy link

gitguardian bot commented Mar 6, 2026

⚠️ GitGuardian has uncovered 1 secret following the scan of your pull request.

Please consider investigating the findings and remediating the incidents. Failure to do so may lead to compromising the associated services or software components.

🔎 Detected hardcoded secret in your pull request
GitGuardian id GitGuardian status Secret Commit Filename
27411356 Triggered Generic Database Assignment bc29752 backend/src/ee/services/pam-discovery/active-directory/active-directory-discovery-factory.ts View secret
🛠 Guidelines to remediate hardcoded secrets
  1. Understand the implications of revoking this secret by investigating where it is used in your code.
  2. Replace and store your secret safely. Learn here the best practices.
  3. Revoke and rotate this secret.
  4. If possible, rewrite git history. Rewriting git history is not a trivial act. You might completely break other contributing developers' workflow and you risk accidentally deleting legitimate data.

To avoid such incidents in the future consider


🦉 GitGuardian detects secrets in your source code to help developers and security teams secure the modern development process. You are seeing this because you or someone else with access to this repository has authorized GitGuardian to scan your pull request.

Switch from CASL_ACTION_SCHEMA_ENUM with explicit values to
CASL_ACTION_SCHEMA_NATIVE_ENUM(OrgPermissionSubOrgActions) to match the
pattern used by all other subjects and fix oasdiff breaking change detection.

Made-with: Cursor
Add a read-only Sub-Organization ID field to SubOrgNameChangeSection,
matching the existing Organization ID field in OrgNameChangeSection.

Made-with: Cursor
@IgorHorta IgorHorta requested a review from victorvhs017 March 6, 2026 18:22
@IgorHorta
Copy link
Contributor Author

@greptile check this pr again plz

The Edit/Delete dropdown was gated behind `subOrg.isMember`, but
the backend checks these permissions against the parent org, not
the sub-org. This meant admins had to join a sub-org before they
could manage it. Remove the membership guard so the actions menu
renders based solely on org-level permissions.

Made-with: Cursor
createSubOrg validates that the org plan includes the
subOrganization feature, but joinSubOrg did not. Add the
same license guard so users cannot join sub-orgs when the
feature is not available on their plan.

Made-with: Cursor
Remove the limit: 500 param so the query uses the server default,
avoiding silently incomplete results in the dropdown.

Made-with: Cursor
@IgorHorta
Copy link
Contributor Author

@greptile once more plz

The dev overrides enabling enterprise features (rbac, subOrganization,
samlSSO, oidcSSO, groups, enforceMfa, enforceGoogleSSO) were
accidentally included. Revert to the original values from main.

Made-with: Cursor
Show the Join button in the Status column for non-members, replacing the
empty space. Members see the "Joined" badge, non-members see the Join
button (permission-gated). The actions column now only contains the
edit/delete dropdown menu.

Made-with: Cursor
Remove manual error notifications from edit, delete, and login handlers
since the axios interceptor already displays error toasts for failed API
requests.

Made-with: Cursor
@IgorHorta IgorHorta requested a review from victorvhs017 March 6, 2026 23:48
victorvhs017
victorvhs017 previously approved these changes Mar 6, 2026
Copy link
Contributor

@victorvhs017 victorvhs017 left a comment

Choose a reason for hiding this comment

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

All tested! Nice work!

@IgorHorta IgorHorta merged commit e57871d into main Mar 7, 2026
11 of 13 checks passed
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.

3 participants