Skip to content

Commit a4e6932

Browse files
authored
chore(clerk-js,shared,localizations): Display role sets migration status in OrganizationProfile (#7541)
1 parent 2eaa829 commit a4e6932

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

61 files changed

+631
-13
lines changed

.changeset/wicked-wings-lie.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
'@clerk/localizations': minor
3+
'@clerk/clerk-js': minor
4+
'@clerk/shared': minor
5+
---
6+
7+
Disable role selection in `OrganizationProfile` during role set migration

packages/clerk-js/bundlewatch.config.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
{ "path": "./dist/clerk.browser.js", "maxSize": "87KB" },
55
{ "path": "./dist/clerk.legacy.browser.js", "maxSize": "129KB" },
66
{ "path": "./dist/clerk.headless*.js", "maxSize": "66KB" },
7-
{ "path": "./dist/ui-common*.js", "maxSize": "119.1KB" },
7+
{ "path": "./dist/ui-common*.js", "maxSize": "123KB" },
88
{ "path": "./dist/ui-common*.legacy.*.js", "maxSize": "123KB" },
99
{ "path": "./dist/vendors*.js", "maxSize": "50KB" },
1010
{ "path": "./dist/coinbase*.js", "maxSize": "38KB" },

packages/clerk-js/src/core/resources/Organization.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,11 +95,18 @@ export class Organization extends BaseResource implements OrganizationResource {
9595
forceUpdateClient: true,
9696
},
9797
).then(res => {
98-
const { data: roles, total_count } = res?.response as unknown as ClerkPaginatedResponse<RoleJSON>;
98+
const {
99+
data: roles,
100+
total_count,
101+
has_role_set_migration,
102+
} = res?.response as unknown as ClerkPaginatedResponse<RoleJSON> & {
103+
has_role_set_migration?: boolean;
104+
};
99105

100106
return {
101107
total_count,
102108
data: roles.map(role => new Role(role)),
109+
has_role_set_migration,
103110
};
104111
});
105112
};

packages/clerk-js/src/ui/components/OrganizationProfile/ActiveMembersList.tsx

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ export const ActiveMembersList = ({ memberships, pageSize }: ActiveMembersListPr
2020
const card = useCardState();
2121
const { organization } = useOrganization();
2222

23-
const { options, isLoading: loadingRoles } = useFetchRoles();
23+
const { options, isLoading: loadingRoles, hasRoleSetMigration } = useFetchRoles();
2424

2525
if (!organization) {
2626
return null;
@@ -61,6 +61,7 @@ export const ActiveMembersList = ({ memberships, pageSize }: ActiveMembersListPr
6161
options={options}
6262
onRoleChange={handleRoleChange(m)}
6363
onRemove={handleRemove(m)}
64+
hasRoleSetMigration={hasRoleSetMigration}
6465
/>
6566
))}
6667
/>
@@ -73,8 +74,9 @@ const MemberRow = (props: {
7374
onRemove: () => unknown;
7475
options: Parameters<typeof RoleSelect>[0]['roles'];
7576
onRoleChange: (role: string) => unknown;
77+
hasRoleSetMigration: boolean;
7678
}) => {
77-
const { membership, onRemove, onRoleChange, options } = props;
79+
const { membership, onRemove, onRoleChange, options, hasRoleSetMigration } = props;
7880
const { localizeCustomRole } = useLocalizeCustomRoles();
7981
const card = useCardState();
8082
const { user } = useUser();
@@ -112,7 +114,7 @@ const MemberRow = (props: {
112114
}
113115
>
114116
<RoleSelect
115-
isDisabled={card.isLoading || !onRoleChange}
117+
isDisabled={card.isLoading || !onRoleChange || hasRoleSetMigration}
116118
value={membership.role}
117119
fallbackLabel={membership.roleName}
118120
onChange={onRoleChange}

packages/clerk-js/src/ui/components/OrganizationProfile/InviteMembersForm.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,7 @@ export const InviteMembersForm = (props: InviteMembersFormProps) => {
199199
};
200200

201201
const AsyncRoleSelect = (field: ReturnType<typeof useFormControl<'role'>>) => {
202-
const { options, isLoading } = useFetchRoles();
202+
const { options, isLoading, hasRoleSetMigration } = useFetchRoles();
203203

204204
const { t } = useLocalizations();
205205

@@ -212,7 +212,7 @@ const AsyncRoleSelect = (field: ReturnType<typeof useFormControl<'role'>>) => {
212212
<RoleSelect
213213
{...field.props}
214214
roles={options}
215-
isDisabled={isLoading}
215+
isDisabled={isLoading || hasRoleSetMigration}
216216
onChange={value => field.setValue(value)}
217217
triggerSx={t => ({ minWidth: t.sizes.$40, justifyContent: 'space-between', display: 'flex' })}
218218
optionListSx={t => ({ minWidth: t.sizes.$48 })}

packages/clerk-js/src/ui/components/OrganizationProfile/OrganizationMembers.tsx

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
import { useOrganization } from '@clerk/shared/react';
22
import { useState } from 'react';
33

4+
import { Alert } from '@/ui/elements/Alert';
45
import { Animated } from '@/ui/elements/Animated';
56
import { Card } from '@/ui/elements/Card';
67
import { useCardState, withCardStateProvider } from '@/ui/elements/contexts';
78
import { Header } from '@/ui/elements/Header';
89
import { Tab, TabPanel, TabPanels, Tabs, TabsList } from '@/ui/elements/Tabs';
10+
import { useFetchRoles } from '@/ui/hooks/useFetchRoles';
911

1012
import { NotificationCountBadge, useProtect } from '../../common';
1113
import { useEnvironment, useOrganizationProfileContext } from '../../contexts';
@@ -24,6 +26,7 @@ export const ACTIVE_MEMBERS_PAGE_SIZE = 10;
2426
export const OrganizationMembers = withCardStateProvider(() => {
2527
const { organizationSettings } = useEnvironment();
2628
const card = useCardState();
29+
const { hasRoleSetMigration } = useFetchRoles();
2730
const canManageMemberships = useProtect({ permission: 'org:sys_memberships:manage' });
2831
const canReadMemberships = useProtect({ permission: 'org:sys_memberships:read' });
2932
const isDomainsEnabled = organizationSettings?.domains?.enabled && canManageMemberships;
@@ -142,6 +145,17 @@ export const OrganizationMembers = withCardStateProvider(() => {
142145
/>
143146
}
144147
/>
148+
{hasRoleSetMigration && (
149+
<Alert
150+
variant='warning'
151+
title={localizationKeys(
152+
'organizationProfile.membersPage.alerts.roleSetMigrationInProgress.title',
153+
)}
154+
subtitle={localizationKeys(
155+
'organizationProfile.membersPage.alerts.roleSetMigrationInProgress.subtitle',
156+
)}
157+
/>
158+
)}
145159
<ActiveMembersList
146160
pageSize={ACTIVE_MEMBERS_PAGE_SIZE}
147161
memberships={memberships}

packages/clerk-js/src/ui/components/OrganizationProfile/__tests__/InviteMembersPage.test.tsx

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import { ClerkAPIResponseError } from '@clerk/shared/error';
22
import type { OrganizationInvitationResource } from '@clerk/shared/types';
33
import { waitFor } from '@testing-library/react';
4-
import React from 'react';
54
import { beforeEach, describe, expect, it, vi } from 'vitest';
65

76
import { bindCreateFixtures } from '@/test/create-fixtures';
@@ -21,6 +20,57 @@ describe('InviteMembersPage', () => {
2120
clearFetchCache();
2221
});
2322

23+
it('disables the role select when role set migration is in progress', async () => {
24+
const { wrapper, fixtures } = await createFixtures(f => {
25+
f.withOrganizations();
26+
f.withUser({
27+
email_addresses: ['[email protected]'],
28+
organization_memberships: [{ name: 'Org1', role: 'admin' }],
29+
});
30+
});
31+
32+
fixtures.clerk.organization?.getInvitations.mockRejectedValue(null);
33+
fixtures.clerk.organization?.getRoles.mockResolvedValue({
34+
total_count: 2,
35+
has_role_set_migration: true,
36+
data: [
37+
{
38+
pathRoot: '',
39+
reload: vi.fn(),
40+
id: 'member',
41+
key: 'member',
42+
name: 'member',
43+
description: '',
44+
permissions: [],
45+
createdAt: new Date(),
46+
updatedAt: new Date(),
47+
},
48+
{
49+
pathRoot: '',
50+
reload: vi.fn(),
51+
id: 'admin',
52+
key: 'admin',
53+
name: 'Admin',
54+
description: '',
55+
permissions: [],
56+
createdAt: new Date(),
57+
updatedAt: new Date(),
58+
},
59+
],
60+
});
61+
62+
const { getByRole } = render(
63+
<Action.Root>
64+
<InviteMembersScreen />
65+
</Action.Root>,
66+
{ wrapper },
67+
);
68+
69+
await waitFor(() => {
70+
expect(getByRole('button', { name: /select role/i })).toBeDisabled();
71+
});
72+
});
73+
2474
it('renders the component', async () => {
2575
const { wrapper, fixtures } = await createFixtures(f => {
2676
f.withOrganizations();

0 commit comments

Comments
 (0)