Skip to content

feat(workspace): make the Personal-workspace invite limit obvious - #2721

Open
carlosvirreira wants to merge 2 commits into
mainfrom
feat/workspace-invite-clarity
Open

feat(workspace): make the Personal-workspace invite limit obvious#2721
carlosvirreira wants to merge 2 commits into
mainfrom
feat/workspace-invite-clarity

Conversation

@carlosvirreira

@carlosvirreira carlosvirreira commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Why

Personal workspaces are created automatically at signup and cannot have members, so "how can someone join my workspace?" is a recurring support question. The limitation itself is fine. The problem is that the product never stated it, and Settings > Team removed the answer instead of showing it: Users and Pending invites were hidden from the nav, and visiting either URL silently redirected to Settings > General with no message.

Changes

Onboarding (/welcome)

  • Pre-selects Team when the onboarding team-size answer indicates more than one person.
  • Both plan cards now state the consequence, rather than only describing the option.
  • Replaces "you can always switch later" with the route that actually exists.
  • Choosing Personal against a stated team size shows a caution with a one-click switch.

Settings > Team

  • Explains the limit on the page people actually open, with a tier-aware upgrade CTA.
  • /settings/team/users and /settings/team/invites no longer redirect to General with no explanation.
  • Users and Pending invites render locked instead of being hidden entirely.
  • Non-registered members empty state no longer calls them "team members"; those records cannot log in.

Workspace switcher

  • Adds an "Invite your team" action on Personal workspaces, routing to the page that explains the limit.

Notes for review

  • Only one place makes an entitlement claim. resolveTeamUpgradeCta() (app/utils/team-upgrade-cta.ts) answers a single narrow question: can this user create a Team workspace? Team-entitled users go to workspace creation; users who already spent their free trial are offered an upgrade rather than a trial that account-details.subscription would reject with "You have already used your free trial".
  • No surface renders "the plan", because there isn't one. Tier lives on User.tierId while add-ons live on the organization (auditsEnabled, barcodesEnabled), and the personal-with-addons onboarding path enables an add-on on a Personal workspace without changing tierId. A free-tier customer paying for Barcodes cannot be honestly labelled by any single badge, so the sidebar states workspace facts only. Add-ons do not affect Team-workspace entitlement, which comes from TierLimit.maxOrganizations.
  • NestedRouteRenderer previously ignored a child nav item's disabled prop entirely. It now honours it, which is what makes locked sub-items possible.
  • UserBusinessIntel.teamSize is captured with SelectWithOther and is therefore free text (production data contains values like "1"), so signalsTeamIntent() matches against an allowlist derived from TEAM_SIZE_OPTIONS.

Testing

  • typecheck, eslint and prettier clean.
  • 3264 unit tests pass across 240 files, 9 added (team-size intent parsing and the upgrade-CTA tier matrix).
  • react-doctor reports no findings in any changed file.
  • Manually verified in the browser on a Personal workspace: onboarding pre-selection and caution, the Team page explanation and CTA, the locked nav items, the redirect fix and the switcher action.
  • The tier-dependent CTA was verified on screen for Free, Plus and Team tiers by temporarily moving a test account between tiers and restoring it. The one combination covered by unit tests alone is a free-tier user whose trial is already spent, which shares the code path with the Plus case that was captured.

Summary by CodeRabbit

  • New Features

    • Added Personal-workspace upgrade prompts in Team settings, including “Create a Team workspace” and trial/upgrade messaging.
    • Added an “Invite your team” button to the organization selector for Personal workspaces.
    • Enhanced onboarding plan selection to recommend Team when multiple people are expected, with a Personal-specific callout and “Switch to Team”.
    • Kept “Users”/“Pending invites” visible for Personal workspaces but disabled with an explanatory tooltip.
  • Bug Fixes

    • Updated Personal-workspace redirects for team invites and team user management to the appropriate non-registered members page.
    • Improved sidebar behavior for disabled nested entries (non-navigable, muted state with reason).
  • Tests

    • Added coverage for team-intent detection and upgrade CTA selection logic.

Personal workspaces are created automatically at signup and cannot have members, so
"how can someone join my workspace?" is a recurring support question. The rule was never
stated in the product, and Settings > Team removed the answer instead of showing it.

Onboarding:
- Pre-select Team when the onboarding team-size answer indicates more than one person.
- State the consequence on both plan cards instead of only describing each option.
- Replace "you can always switch later" with the route that actually exists.
- Add signalsTeamIntent() so free-text answers such as "1" are not read as a team.

Settings > Team:
- Explain the limit on the page people open, with a tier-aware upgrade CTA.
- Stop redirecting /settings/team/users and /invites to General with no message.
- Render Users and Pending invites locked rather than hiding them entirely.
- Correct the non-registered members empty state; those records cannot log in.

Workspace switcher:
- Show the plan the user actually pays for, plus a route to upgrade.

resolveTeamUpgradeCta() keeps the CTA honest per tier: Team-entitled users are sent to
workspace creation, and users who already spent their free trial are offered an upgrade
rather than a trial the subscription action would reject.
@github-actions

Copy link
Copy Markdown

🩺 React Doctor — webapp

✅ No new findings on the files changed by this PR.

Run locally with pnpm webapp:doctor for a full scan, or cd apps/webapp && pnpm exec react-doctor . --diff for the same diff-only view.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d2ac521f1b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +52 to +54
return {
to: "/account-details/subscription",
label: usedFreeTrial ? "Upgrade to Team" : "Start a Team trial",

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Route non-premium users to workspace creation

When ENABLE_PREMIUM_FEATURES is false, free and Plus users still receive this subscription CTA, but the subscription loader immediately redirects them to /account-details/general (account-details.subscription.tsx:52-53). In the same environment, canCreateMoreOrganizations explicitly permits workspace creation (subscription.server.ts:289), so the new “Start a Team trial”/“Upgrade to Team” action is a dead end instead of taking these users to /account-details/workspace.

Useful? React with 👍 / 👎.

@coderabbitai

coderabbitai Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

Personal workspace onboarding now detects team intent, recommends Team plans, and supports switching plans. Team settings and sidebar navigation expose upgrade actions while personal-workspace team-management routes redirect to non-registered members.

Changes

Personal workspace team guidance

Layer / File(s) Summary
Onboarding team intent
apps/webapp/app/modules/onboarding/constants.ts, apps/webapp/app/modules/onboarding/constants.test.ts, apps/webapp/app/routes/_welcome+/welcome.tsx, apps/webapp/app/components/welcome/choose-purpose.tsx
Team-size answers are classified, passed through the welcome loader, used for initial plan selection, and displayed as a conditional Team recommendation with updated plan copy.
Team upgrade settings
apps/webapp/app/utils/team-upgrade-cta.ts, apps/webapp/app/utils/team-upgrade-cta.test.ts, apps/webapp/app/routes/_layout+/settings.team.tsx
Subscription tier and trial usage determine the personal-workspace upgrade destination and label, which are rendered in the team settings teaser.
Personal sidebar state
apps/webapp/app/hooks/use-sidebar-nav-items.tsx, apps/webapp/app/components/layout/sidebar/parent-nav-item.tsx, apps/webapp/app/components/layout/sidebar/organization-selector.tsx
Personal workspaces retain disabled Users and Pending invites entries, display disabled reasons, and show team-invitation affordances.
Team route handling
apps/webapp/app/routes/_layout+/settings.team.invites.tsx, apps/webapp/app/routes/_layout+/settings.team.users.tsx, apps/webapp/app/routes/_layout+/settings.team.nrm.tsx
Personal-workspace invite and user-management routes redirect to NRM settings, whose empty state explains non-registered member records.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant WelcomeLoader
  participant UserData
  participant ChoosePurpose
  WelcomeLoader->>UserData: read teamSize
  WelcomeLoader->>ChoosePurpose: pass teamIntent and default plan
  ChoosePurpose->>ChoosePurpose: show recommendation or select Team
Loading
sequenceDiagram
  participant TeamSettingsLoader
  participant UserSubscription
  participant TeamSettings
  TeamSettingsLoader->>UserSubscription: read tier and trial usage
  TeamSettingsLoader->>TeamSettings: provide upgrade CTA
  TeamSettings->>TeamSettings: render personal-workspace teaser
Loading

Suggested reviewers: donkoko

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% 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 matches the PR’s main theme: making Personal workspace invite limits visible across onboarding, settings, and the switcher.
✨ 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 feat/workspace-invite-clarity

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.

The plan marker read the workspace type, not what the user pays. Tier lives on the
user (User.tierId) while add-ons live on the organization (auditsEnabled,
barcodesEnabled), and the personal-with-addons onboarding path enables an add-on on a
Personal workspace without ever changing tierId. A free-tier customer paying for
Barcodes would therefore have been labelled "Free", and no single label can be correct
across every tier and add-on combination.

Remove the marker rather than try to compute one. The locked Users and Pending invites
items are already an always-accurate standing signal and make no billing claim.

Also neutralise the switcher action to "Invite your team". "Upgrade" was wrong for a
Team-tier user sitting in their Personal workspace, who needs to create a workspace
rather than change plan.

Entitlement is now claimed in exactly one place, resolveTeamUpgradeCta, which answers
only whether the user can create a Team workspace. That is driven by
TierLimit.maxOrganizations and is unaffected by add-ons.
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