Skip to content

Commit 84ea908

Browse files
authored
fix(ui): Hide "must belong to org" screen when org resources exist (#7553)
1 parent bf80963 commit 84ea908

File tree

3 files changed

+67
-31
lines changed

3 files changed

+67
-31
lines changed

.changeset/tasty-animals-grab.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@clerk/ui': patch
3+
---
4+
5+
Fix "You must belong to an organization" screen showing when user has existing memberships, invitations or suggestions

packages/ui/src/components/SessionTasks/tasks/TaskChooseOrganization/__tests__/TaskChooseOrganization.test.tsx

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,10 @@ import { describe, expect, it } from 'vitest';
33

44
import { bindCreateFixtures } from '@/test/create-fixtures';
55
import { render } from '@/test/utils';
6-
import { createFakeUserOrganizationMembership } from '@/ui/components/OrganizationSwitcher/__tests__/test-utils';
6+
import {
7+
createFakeUserOrganizationMembership,
8+
createFakeUserOrganizationSuggestion,
9+
} from '@/ui/components/OrganizationSwitcher/__tests__/test-utils';
710

811
import { TaskChooseOrganization } from '..';
912

@@ -296,5 +299,56 @@ describe('TaskChooseOrganization', () => {
296299
expect(queryByText(/you must belong to an organization/i)).toBeInTheDocument();
297300
expect(queryByText(/contact your organization admin for an invitation/i)).toBeInTheDocument();
298301
});
302+
303+
it('with existing memberships or suggestions, displays create organization screen', async () => {
304+
const { wrapper, fixtures } = await createFixtures(f => {
305+
f.withOrganizations();
306+
f.withForceOrganizationSelection();
307+
f.withUser({
308+
create_organization_enabled: false,
309+
});
310+
});
311+
312+
fixtures.clerk.user?.getOrganizationMemberships.mockReturnValueOnce(
313+
Promise.resolve({
314+
data: [
315+
createFakeUserOrganizationMembership({
316+
id: '1',
317+
organization: {
318+
id: '1',
319+
name: 'Existing Org',
320+
slug: 'org1',
321+
membersCount: 1,
322+
adminDeleteEnabled: false,
323+
maxAllowedMemberships: 1,
324+
pendingInvitationsCount: 1,
325+
},
326+
}),
327+
],
328+
total_count: 1,
329+
}),
330+
);
331+
332+
fixtures.clerk.user?.getOrganizationSuggestions.mockReturnValueOnce(
333+
Promise.resolve({
334+
data: [
335+
createFakeUserOrganizationSuggestion({
336+
id: '2',
337+
emailAddress: '[email protected]',
338+
publicOrganizationData: {
339+
name: 'OrgTwoSuggestion',
340+
},
341+
}),
342+
],
343+
total_count: 1,
344+
}),
345+
);
346+
347+
const { findByText, queryByRole } = render(<TaskChooseOrganization />, { wrapper });
348+
349+
expect(await findByText('Existing Org')).toBeInTheDocument();
350+
expect(await findByText('Create new organization')).toBeInTheDocument();
351+
expect(queryByRole('textbox', { name: /name/i })).not.toBeInTheDocument();
352+
});
299353
});
300354
});

packages/ui/src/components/SessionTasks/tasks/TaskChooseOrganization/index.tsx

Lines changed: 7 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { useClerk, useSession, useUser } from '@clerk/shared/react';
2-
import { useState, type ComponentType } from 'react';
2+
import { useState } from 'react';
33

44
import { useSignOutContext, withCoreSessionSwitchGuard } from '@/ui/contexts';
55
import { descriptors, Flex, Flow, localizationKeys, Spinner } from '@/ui/customizables';
@@ -14,24 +14,16 @@ import { ChooseOrganizationScreen } from './ChooseOrganizationScreen';
1414
import { CreateOrganizationScreen } from './CreateOrganizationScreen';
1515

1616
const TaskChooseOrganizationInternal = () => {
17-
const { signOut } = useClerk();
1817
const { user } = useUser();
19-
const { session } = useSession();
2018
const { userMemberships, userSuggestions, userInvitations } = useOrganizationListInView();
21-
const { otherSessions } = useMultipleSessions({ user });
22-
const { navigateAfterSignOut, navigateAfterMultiSessionSingleSignOutUrl } = useSignOutContext();
23-
24-
const handleSignOut = () => {
25-
if (otherSessions.length === 0) {
26-
return signOut(navigateAfterSignOut);
27-
}
28-
29-
return signOut(navigateAfterMultiSessionSingleSignOutUrl, { sessionId: session?.id });
30-
};
3119

3220
const isLoading = userMemberships?.isLoading || userInvitations?.isLoading || userSuggestions?.isLoading;
3321
const hasExistingResources = !!(userMemberships?.count || userInvitations?.count || userSuggestions?.count);
34-
const identifier = user?.primaryEmailAddress?.emailAddress ?? user?.username;
22+
const isOrganizationCreationDisabled = !isLoading && !user?.createOrganizationEnabled && !hasExistingResources;
23+
24+
if (isOrganizationCreationDisabled) {
25+
return <OrganizationCreationDisabledScreen />;
26+
}
3527

3628
return (
3729
<Flow.Root flow='taskChooseOrganization'>
@@ -127,18 +119,6 @@ const TaskChooseOrganizationFlows = withCardStateProvider((props: TaskChooseOrga
127119
return <ChooseOrganizationScreen onCreateOrganizationClick={() => setCurrentFlow('create')} />;
128120
});
129121

130-
export const withOrganizationCreationEnabledGuard = <T,>(Component: ComponentType<T>) => {
131-
return (props: T) => {
132-
const { user } = useUser();
133-
134-
if (!user?.createOrganizationEnabled) {
135-
return <OrganizationCreationDisabledScreen />;
136-
}
137-
138-
return <Component {...props} />;
139-
};
140-
};
141-
142122
function OrganizationCreationDisabledScreen() {
143123
return (
144124
<Flow.Root flow='taskChooseOrganization'>
@@ -163,8 +143,5 @@ function OrganizationCreationDisabledScreen() {
163143
}
164144

165145
export const TaskChooseOrganization = withCoreSessionSwitchGuard(
166-
withTaskGuard(
167-
withCardStateProvider(withOrganizationCreationEnabledGuard(TaskChooseOrganizationInternal)),
168-
'choose-organization',
169-
),
146+
withTaskGuard(withCardStateProvider(TaskChooseOrganizationInternal), 'choose-organization'),
170147
);

0 commit comments

Comments
 (0)