Skip to content

fix: let users without an organization open public projects#3816

Open
Anty0 wants to merge 1 commit into
mainfrom
jirikuchynka/org-less-public-project-access
Open

fix: let users without an organization open public projects#3816
Anty0 wants to merge 1 commit into
mainfrom
jirikuchynka/org-less-public-project-access

Conversation

@Anty0

@Anty0 Anty0 commented Jul 22, 2026

Copy link
Copy Markdown
Member

Follow-up to #3808 (merged). That PR fixed organization permissions for public projects on the backend; this one fixes the frontend bootstrap that still left the target user stuck.

Problem

A signed-in user with no organization — organization creation disabled on the server, or an SSO-provisioned account — was stuck on the "No permissions" page and could not open a public project, even from a direct link.

Root cause

RequirePreferredOrganization wrapped the whole authenticated route tree, so ProjectsRouter never mounted. ProjectContext — the only thing that adopts the project's owning organization as the preferred one — therefore never ran.

Fix

Move the /projects routes above the terminal gate and re-apply the gate to the /projects/add leaf inside ProjectsRouter: viewing a project is reachable without a preferred organization, creating one still requires it.

Supporting changes that fall out of the route move:

  • DashboardRouter extracted from RootRouter so the gated and ungated arms are declared in one place, and HelpMenu has a single structural mount.
  • HelpMenu guard changed from !preferredOrganization to !user — it must render for a signed-in user who has no organization, and must not render for anonymous visitors.
  • OrganizationSwitch / BaseProjectView handle the no-preferred-organization state instead of rendering an empty switch or a dangling breadcrumb chevron. On /community-projects the surface label still renders; the dropdown (which would open onto an empty list) does not.
  • Support-chat entitlement consolidated behind one useHasSupportChat, shared by useChatwoot and useIntercom, and made viewer-scoped: a community contributor parked on someone else's organization no longer inherits that organization's paid support widget. Previously setChatwootAttributes would have pushed the host organization's plan, subscription status and enabled features to Chatwoot on behalf of a non-member.
  • useIntercom now shuts the messenger down when a booted session becomes de-entitled mid-navigation — reachable now that the preferred organization changes as a side effect of ordinary navigation.

Preferred organization stays synchronized with the selected project; that is deliberate, since organization settings and glossaries need a selected organization.

Tests

  • Backend: two new cases in PreferredOrganizationCommunityTest covering an organization-less user adopting an organization via a public project, and failing to adopt one without public projects (7/7 pass).
  • Unit: organizationEntitlement pins the support-tier and limited-view rules (mutation-checked — dropping either support tier or the limited-view conjunct turns the suite red).
  • E2E: communityPreferredOrganization.cy.ts extended with an organization-less persona (server configured with userCanCreateOrganizations=false) covering cold deep-link adoption, reload persistence, switch-failure resilience, community project listing, and the create-project/create-organization walls.

Notes for review

  • Intercom availability now conjoins companyInfo, matching what the boot effect already required before this PR. This keeps self-hosted behaviour identical to today (no activeCloudSubscription there, so Intercom never booted and still doesn't) while fixing a pre-existing mismatch where the menu entry rendered but the messenger was never initialised. Chatwoot is unaffected and degrades gracefully. Flagging it because it is a deliberate privacy-conservative choice rather than an obvious one — booting Intercom for self-hosted EE licence holders would be a separate, intentional change.
  • /projects/add after adopting a foreign organization currently renders the wall. Whether it should instead be reachable is an open product question, deliberately not decided here.
  • limitedView is applied to support chat only. Whether QA checks / tasks / TM should also respect it for community viewers is a separate question — arguably they should keep following the content owner's entitlement.

A signed-in user with no organization — organization creation disabled on
the server, or an SSO-provisioned account — was stuck on the "No permissions"
page and could not open a public project even via a direct link.

RequirePreferredOrganization wrapped the whole authenticated route tree, so
ProjectsRouter never mounted. ProjectContext, the only thing that adopts the
project's owning organization as the preferred one, therefore never ran: the
user needed an organization to reach the page that would have given them one.

Move the /projects routes above the terminal gate and re-apply the gate to the
/projects/add leaf inside ProjectsRouter, so viewing a project is reachable
without a preferred organization while creating one still requires it.
@coderabbitai

coderabbitai Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Changes

The PR expands preferred-organization behavior for organization-less users, restructures dashboard routing and access guards, adds community Cypress coverage, and centralizes organization support entitlements used by Chatwoot and Intercom.

Preferred organization access

Layer / File(s) Summary
Org-less test data and backend coverage
backend/data/..., backend/app/src/test/...
Adds organization-less test users and verifies preferred-organization adoption and rejection behavior.
Dashboard routing and access guards
webapp/src/component/..., webapp/src/views/projects/...
Extracts dashboard routing, applies preferred-organization guards, and adjusts navigation and no-permission rendering.
Community interaction validation
e2e/cypress/e2e/projects/..., e2e/cypress/support/dataCyType.d.ts
Covers signed-out help visibility, adoption flows, failed switches, reloads, deep links, project access, and community listings.

Support entitlement integrations

Layer / File(s) Summary
Entitlement helpers and tests
webapp/src/fixtures/organizationEntitlement.ts, webapp/src/fixtures/__tests__/*
Adds support-chat entitlement and subscription company-information helpers with focused tests.
Chat provider availability and lifecycle
webapp/src/globalContext/helpers.tsx, webapp/src/hooks/useChatwoot.ts, webapp/src/hooks/useIntercom.ts
Uses shared support eligibility to control Chatwoot loading and Intercom boot, theme updates, and shutdown.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant DashboardRouter
  participant RequirePreferredOrganization
  participant OrganizationSwitch
  User->>DashboardRouter: open community project
  DashboardRouter->>RequirePreferredOrganization: check preferred organization
  RequirePreferredOrganization->>OrganizationSwitch: render community organization state
  OrganizationSwitch-->>User: show project or no-permissions state
Loading

Possibly related PRs

Suggested reviewers: jancizmar, bdshadow

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely summarizes the main change: allowing org-less users to open public projects.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch jirikuchynka/org-less-public-project-access

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
webapp/src/fixtures/__tests__/organizationEntitlement.test.ts (1)

2-5: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use the Tolgee fixture alias.

Replace the relative import with tg.fixtures/organizationEntitlement.

Proposed fix
 import {
   organizationCompanyInfo,
   organizationHasSupportChat,
-} from '../organizationEntitlement';
+} from 'tg.fixtures/organizationEntitlement';
🤖 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 `@webapp/src/fixtures/__tests__/organizationEntitlement.test.ts` around lines 2
- 5, Update the imports in organizationEntitlement.test.ts to use the
tg.fixtures/organizationEntitlement alias instead of the relative
../organizationEntitlement path, preserving the existing imported symbols.

Source: Coding guidelines

🤖 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 `@webapp/src/fixtures/__tests__/organizationEntitlement.test.ts`:
- Around line 2-5: Update the imports in organizationEntitlement.test.ts to use
the tg.fixtures/organizationEntitlement alias instead of the relative
../organizationEntitlement path, preserving the existing imported symbols.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: fb349e77-8d1a-4964-a9fb-772dcd6e6ca7

📥 Commits

Reviewing files that changed from the base of the PR and between 0d09306 and 3ea8e20.

📒 Files selected for processing (17)
  • backend/app/src/test/kotlin/io/tolgee/api/v2/controllers/PreferredOrganizationCommunityTest.kt
  • backend/data/src/main/kotlin/io/tolgee/development/testDataBuilder/data/PublicProjectsControllerTestData.kt
  • backend/data/src/main/kotlin/io/tolgee/development/testDataBuilder/data/PublicProjectsE2eData.kt
  • e2e/cypress/e2e/projects/communityPreferredOrganization.cy.ts
  • e2e/cypress/support/dataCyType.d.ts
  • webapp/src/component/DashboardRouter.tsx
  • webapp/src/component/HelpMenu.tsx
  • webapp/src/component/RootRouter.tsx
  • webapp/src/component/common/RequirePreferredOrganization.tsx
  • webapp/src/component/organizationSwitch/OrganizationSwitch.tsx
  • webapp/src/fixtures/__tests__/organizationEntitlement.test.ts
  • webapp/src/fixtures/organizationEntitlement.ts
  • webapp/src/globalContext/helpers.tsx
  • webapp/src/hooks/useChatwoot.ts
  • webapp/src/hooks/useIntercom.ts
  • webapp/src/views/projects/BaseProjectView.tsx
  • webapp/src/views/projects/ProjectsRouter.tsx

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.

1 participant