Skip to content

Commit

Permalink
feat(clerk-js): Use Gate in OrganizationMembers
Browse files Browse the repository at this point in the history
  • Loading branch information
panteliselef committed Oct 5, 2023
1 parent afefff1 commit 6e1dc58
Show file tree
Hide file tree
Showing 6 changed files with 127 additions and 105 deletions.
10 changes: 10 additions & 0 deletions packages/clerk-js/src/core/test/fixtures.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,16 @@ export const createOrganization = (params: OrgParams): OrganizationMembershipJSO
} as OrganizationJSON,
public_metadata: {},
role: role || 'admin',
permissions: [
'org:domains:delete',
'org:domains:manage',
'org:domains:read',
'org:memberships:delete',
'org:memberships:manage',
'org:memberships:read',
'org:profile:delete',
'org:profile:manage',
],
updated_at: new Date().getTime(),
} as OrganizationMembershipJSON;
};
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { MembershipRole, OrganizationMembershipResource } from '@clerk/types';

import { Gate } from '../../common/Gate';
import { useCoreOrganization, useCoreUser } from '../../contexts';
import { Badge, localizationKeys, Td, Text } from '../../customizables';
import { ThreeDotsMenu, useCardState, UserPreview } from '../../elements';
Expand All @@ -8,23 +9,17 @@ import { DataTable, RoleSelect, RowContainer } from './MemberListTable';

export const ActiveMembersList = () => {
const card = useCardState();
const {
organization,
membership: currentUserMembership,
memberships,
...rest
} = useCoreOrganization({
const { organization, memberships, ...rest } = useCoreOrganization({
memberships: true,
});

// TODO: Update this to support filter by permissions
const { memberships: adminMembers } = useCoreOrganization({
memberships: {
role: ['admin'],
},
});

const isAdmin = currentUserMembership?.role === 'admin';

const mutateSwrState = () => {
const unstable__mutate = (rest as any).unstable__mutate;
if (unstable__mutate && typeof unstable__mutate === 'function') {
Expand All @@ -37,9 +32,6 @@ export const ActiveMembersList = () => {
}

const handleRoleChange = (membership: OrganizationMembershipResource) => (newRole: MembershipRole) => {
if (!isAdmin) {
return;
}
return card
.runAsync(async () => {
await membership.update({ role: newRole });
Expand All @@ -49,9 +41,6 @@ export const ActiveMembersList = () => {
};

const handleRemove = (membership: OrganizationMembershipResource) => () => {
if (!isAdmin) {
return;
}
return card
.runAsync(async () => {
const destroyedMembership = membership.destroy();
Expand Down Expand Up @@ -97,10 +86,8 @@ const MemberRow = (props: {
}) => {
const { membership, onRemove, onRoleChange, adminCount } = props;
const card = useCardState();
const { membership: currentUserMembership } = useCoreOrganization();
const user = useCoreUser();

const isAdmin = currentUserMembership?.role === 'admin';
const isCurrentUser = user.id === membership.publicUserData.userId;
const isLastAdmin = adminCount <= 1 && membership.role === 'admin';

Expand All @@ -123,21 +110,24 @@ const MemberRow = (props: {
</Td>
<Td>{membership.createdAt.toLocaleDateString()}</Td>
<Td>
{isAdmin ? (
<Gate
permission={'org:memberships:manage'}
fallback={
<Text
sx={t => ({ opacity: t.opacity.$inactive })}
localizationKey={roleLocalizationKey(membership.role)}
/>
}
>
<RoleSelect
isDisabled={card.isLoading || !onRoleChange || isLastAdmin}
value={membership.role}
onChange={onRoleChange}
/>
) : (
<Text
sx={t => ({ opacity: t.opacity.$inactive })}
localizationKey={roleLocalizationKey(membership.role)}
/>
)}
</Gate>
</Td>
<Td>
{isAdmin && (
<Gate permission={'org:memberships:delete'}>
<ThreeDotsMenu
actions={[
{
Expand All @@ -149,7 +139,7 @@ const MemberRow = (props: {
]}
elementId={'member'}
/>
)}
</Gate>
</Td>
</RowContainer>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { NotificationCountBadge } from '../../common';
import { NotificationCountBadge, useGate } from '../../common';
import { useCoreOrganization, useEnvironment, useOrganizationProfileContext } from '../../contexts';
import { Col, descriptors, Flex, localizationKeys } from '../../customizables';
import {
Expand All @@ -21,15 +21,19 @@ import { OrganizationMembersTabRequests } from './OrganizationMembersTabRequests
export const OrganizationMembers = withCardStateProvider(() => {
const { organizationSettings } = useEnvironment();
const card = useCardState();
const { membership } = useCoreOrganization();
const isAdmin = membership?.role === 'admin';
const allowRequests = organizationSettings?.domains?.enabled && isAdmin;
const { isAuthorizedUser: canManageMemberships } = useGate({ permission: 'org:memberships:manage' });
const isDomainsEnabled = organizationSettings?.domains?.enabled;
const { membershipRequests } = useCoreOrganization({
membershipRequests: allowRequests || undefined,
membershipRequests: isDomainsEnabled || undefined,
});
//@ts-expect-error

// @ts-expect-error
const { __unstable_manageBillingUrl } = useOrganizationProfileContext();

if (canManageMemberships === null) {
return null;
}

return (
<Col
elementDescriptor={descriptors.page}
Expand All @@ -52,12 +56,12 @@ export const OrganizationMembers = withCardStateProvider(() => {
<Tabs>
<TabsList>
<Tab localizationKey={localizationKeys('organizationProfile.membersPage.start.headerTitle__members')} />
{isAdmin && (
{canManageMemberships && (
<Tab
localizationKey={localizationKeys('organizationProfile.membersPage.start.headerTitle__invitations')}
/>
)}
{allowRequests && (
{canManageMemberships && isDomainsEnabled && (
<Tab localizationKey={localizationKeys('organizationProfile.membersPage.start.headerTitle__requests')}>
<NotificationCountBadge notificationCount={membershipRequests?.count || 0} />
</Tab>
Expand All @@ -72,16 +76,16 @@ export const OrganizationMembers = withCardStateProvider(() => {
width: '100%',
}}
>
{isAdmin && __unstable_manageBillingUrl && <MembershipWidget />}
{canManageMemberships && __unstable_manageBillingUrl && <MembershipWidget />}
<ActiveMembersList />
</Flex>
</TabPanel>
{isAdmin && (
{canManageMemberships && (
<TabPanel sx={{ width: '100%' }}>
<OrganizationMembersTabInvitations />
</TabPanel>
)}
{allowRequests && (
{canManageMemberships && isDomainsEnabled && (
<TabPanel sx={{ width: '100%' }}>
<OrganizationMembersTabRequests />
</TabPanel>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { BlockButton } from '../../common';
import { useCoreOrganization, useEnvironment, useOrganizationProfileContext } from '../../contexts';
import { BlockButton, Gate } from '../../common';
import { useEnvironment, useOrganizationProfileContext } from '../../contexts';
import { Col, descriptors, Flex, Icon, localizationKeys } from '../../customizables';
import { Header, IconButton } from '../../elements';
import { UserAdd } from '../../icons';
Expand All @@ -11,17 +11,10 @@ import { MembershipWidget } from './MembershipWidget';
export const OrganizationMembersTabInvitations = () => {
const { organizationSettings } = useEnvironment();
const { navigate } = useRouter();
const { membership } = useCoreOrganization();
//@ts-expect-error
const { __unstable_manageBillingUrl } = useOrganizationProfileContext();

const isAdmin = membership?.role === 'admin';

const allowDomains = organizationSettings?.domains?.enabled;

if (!isAdmin) {
return null;
}
const isDomainsEnabled = organizationSettings?.domains?.enabled;

return (
<Col
Expand All @@ -32,43 +25,45 @@ export const OrganizationMembersTabInvitations = () => {
>
{__unstable_manageBillingUrl && <MembershipWidget />}

{allowDomains && (
<Col
gap={2}
sx={{
width: '100%',
}}
>
<Header.Root>
<Header.Title
localizationKey={localizationKeys(
'organizationProfile.membersPage.invitationsTab.autoInvitations.headerTitle',
)}
textVariant='largeMedium'
/>
<Header.Subtitle
localizationKey={localizationKeys(
'organizationProfile.membersPage.invitationsTab.autoInvitations.headerSubtitle',
)}
variant='regularRegular'
/>
</Header.Root>
<DomainList
fallback={
<BlockButton
colorScheme='primary'
textLocalizationKey={localizationKeys(
'organizationProfile.membersPage.invitationsTab.autoInvitations.primaryButton',
{isDomainsEnabled && (
<Gate permission={'org:domains:manage'}>
<Col
gap={2}
sx={{
width: '100%',
}}
>
<Header.Root>
<Header.Title
localizationKey={localizationKeys(
'organizationProfile.membersPage.invitationsTab.autoInvitations.headerTitle',
)}
id='manageVerifiedDomains'
onClick={() => navigate('organization-settings/domain')}
textVariant='largeMedium'
/>
}
redirectSubPath={'organization-settings/domain/'}
verificationStatus={'verified'}
enrollmentMode={'automatic_invitation'}
/>
</Col>
<Header.Subtitle
localizationKey={localizationKeys(
'organizationProfile.membersPage.invitationsTab.autoInvitations.headerSubtitle',
)}
variant='regularRegular'
/>
</Header.Root>
<DomainList
fallback={
<BlockButton
colorScheme='primary'
textLocalizationKey={localizationKeys(
'organizationProfile.membersPage.invitationsTab.autoInvitations.primaryButton',
)}
id='manageVerifiedDomains'
onClick={() => navigate('organization-settings/domain')}
/>
}
redirectSubPath={'organization-settings/domain/'}
verificationStatus={'verified'}
enrollmentMode={'automatic_invitation'}
/>
</Col>
</Gate>
)}

<Flex
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { BlockButton } from '../../common';
import { useCoreOrganization, useOrganizationProfileContext } from '../../contexts';
import { useOrganizationProfileContext } from '../../contexts';
import { Col, Flex, localizationKeys } from '../../customizables';
import { Header } from '../../elements';
import { useRouter } from '../../router';
Expand All @@ -9,15 +9,9 @@ import { RequestToJoinList } from './RequestToJoinList';

export const OrganizationMembersTabRequests = () => {
const { navigate } = useRouter();
const { membership } = useCoreOrganization();
//@ts-expect-error
const { __unstable_manageBillingUrl } = useOrganizationProfileContext();

const isAdmin = membership?.role === 'admin';

if (!isAdmin) {
return null;
}
return (
<Col
gap={8}
Expand Down
Loading

0 comments on commit 6e1dc58

Please sign in to comment.