Skip to content

Commit ea6d205

Browse files
icecrasher321claude
andcommitted
fix(invitations): disclose the seat on a personal-workspace join
Both accept surfaces scoped the membership notice on `organizationId` or the preview's `organizationName`. A personal-workspace invite has neither until acceptance runs — it creates the organization by converting the billed owner's Pro to Team — so the seat and membership disclosure was suppressed for exactly the case that creates the membership. They now also scope on the preview's `willJoinOrganization`, which is the authoritative signal. Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 9d9eded commit ea6d205

3 files changed

Lines changed: 36 additions & 2 deletions

File tree

apps/sim/app/invite/[id]/invite.tsx

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -518,7 +518,18 @@ export default function Invite() {
518518
membershipIntent: invitation?.membershipIntent,
519519
isOrganizationAdminRole: Boolean(invitation?.role && isOrgAdminRole(invitation.role)),
520520
organizationLabel,
521-
isOrganizationScoped: Boolean(invitation?.organizationId || joinPreview?.organizationName),
521+
/**
522+
* A personal-workspace invite has no organization id and no organization
523+
* name yet — acceptance creates one by converting the billed owner's Pro to
524+
* Team — so `willJoinOrganization` is the authoritative signal that a
525+
* membership and seat are involved. Gating on the ids alone silenced the
526+
* disclosure for exactly the case that creates the membership.
527+
*/
528+
isOrganizationScoped: Boolean(
529+
invitation?.organizationId ||
530+
joinPreview?.organizationName ||
531+
joinPreview?.willJoinOrganization
532+
),
522533
})
523534

524535
return (

apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/workspace-header/components/pending-invitations/view-invitations-modal.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,13 @@ function invitationDisclosure(inv: MyInvitation): string {
5757
membershipIntent: inv.membershipIntent,
5858
isOrganizationAdminRole: isOrgAdminRole(inv.role),
5959
organizationLabel,
60-
isOrganizationScoped: Boolean(inv.organizationId || inv.joinPreview?.organizationName),
60+
/** `willJoinOrganization` also covers a personal-workspace invite, which has
61+
* no organization id or name until acceptance creates one. */
62+
isOrganizationScoped: Boolean(
63+
inv.organizationId ||
64+
inv.joinPreview?.organizationName ||
65+
inv.joinPreview?.willJoinOrganization
66+
),
6167
})
6268
const migration = buildWorkspaceMigrationNotice({
6369
joinPreview: inv.joinPreview,

apps/sim/lib/invitations/disclosure-copy.test.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,23 @@ describe('buildMembershipNotice', () => {
102102
).toContain('external collaborator')
103103
})
104104

105+
/**
106+
* A personal-workspace invite has no organization name until acceptance
107+
* creates one, so the caller must scope on `willJoinOrganization`. Pinning the
108+
* copy here so the seat is disclosed once it does.
109+
*/
110+
it('discloses the seat for a will-join preview with no organization name yet', () => {
111+
expect(
112+
buildMembershipNotice({
113+
joinPreview: preview({ organizationName: null }),
114+
membershipIntent: 'internal',
115+
isOrganizationAdminRole: false,
116+
organizationLabel: 'the organization',
117+
isOrganizationScoped: true,
118+
})
119+
).toContain('uses one of their seats')
120+
})
121+
105122
it('says nothing for an invitation with no organization standing', () => {
106123
expect(
107124
buildMembershipNotice({

0 commit comments

Comments
 (0)