diff --git a/.changeset/rotten-rules-argue.md b/.changeset/rotten-rules-argue.md new file mode 100644 index 00000000000..4b22bdb9027 --- /dev/null +++ b/.changeset/rotten-rules-argue.md @@ -0,0 +1,10 @@ +--- +'@clerk/localizations': patch +'@clerk/clerk-js': patch +'@clerk/types': patch +--- + +A OrganizationMembershipRequest can now be rejected + +- New `OrganizationMembershipRequest.reject` method alongside `accept` +- As an organization admin, navigate to `Organization Profile` > `Members` > `Requests`. You can now reject a request from the table. diff --git a/packages/clerk-js/src/core/resources/OrganizationMembershipRequest.ts b/packages/clerk-js/src/core/resources/OrganizationMembershipRequest.ts index 1beb70211e0..071315c426b 100644 --- a/packages/clerk-js/src/core/resources/OrganizationMembershipRequest.ts +++ b/packages/clerk-js/src/core/resources/OrganizationMembershipRequest.ts @@ -23,6 +23,12 @@ export class OrganizationMembershipRequest extends BaseResource implements Organ }); }; + reject = async (): Promise => { + return await this._basePost({ + path: `/organizations/${this.organizationId}/membership_requests/${this.id}/reject`, + }); + }; + protected fromJSON(data: OrganizationMembershipRequestJSON | null): this { if (data) { this.id = data.id; diff --git a/packages/clerk-js/src/core/resources/__snapshots__/OrganizationMembershipRequest.test.ts.snap b/packages/clerk-js/src/core/resources/__snapshots__/OrganizationMembershipRequest.test.ts.snap index b1d4f1c218d..9f0583a730f 100644 --- a/packages/clerk-js/src/core/resources/__snapshots__/OrganizationMembershipRequest.test.ts.snap +++ b/packages/clerk-js/src/core/resources/__snapshots__/OrganizationMembershipRequest.test.ts.snap @@ -16,6 +16,7 @@ OrganizationMembershipRequest { "profileImageUrl": "test_url", "userId": undefined, }, + "reject": [Function], "status": "pending", "updatedAt": 1970-01-01T00:00:05.678Z, } diff --git a/packages/clerk-js/src/ui/components/OrganizationProfile/InvitedMembersList.tsx b/packages/clerk-js/src/ui/components/OrganizationProfile/InvitedMembersList.tsx index 674d5b22e25..17e96f12bf8 100644 --- a/packages/clerk-js/src/ui/components/OrganizationProfile/InvitedMembersList.tsx +++ b/packages/clerk-js/src/ui/components/OrganizationProfile/InvitedMembersList.tsx @@ -66,7 +66,6 @@ const InvitationRow = (props: { invitation: OrganizationInvitationResource; onRe diff --git a/packages/clerk-js/src/ui/components/OrganizationProfile/RequestToJoinList.tsx b/packages/clerk-js/src/ui/components/OrganizationProfile/RequestToJoinList.tsx index 432562f86e6..d78a6e3d0f7 100644 --- a/packages/clerk-js/src/ui/components/OrganizationProfile/RequestToJoinList.tsx +++ b/packages/clerk-js/src/ui/components/OrganizationProfile/RequestToJoinList.tsx @@ -2,7 +2,7 @@ import type { OrganizationMembershipRequestResource } from '@clerk/types'; import { useCoreOrganization } from '../../contexts'; import { Button, Flex, localizationKeys, Td } from '../../customizables'; -import { useCardState, UserPreview } from '../../elements'; +import { useCardState, UserPreview, withCardStateProvider } from '../../elements'; import { handleError } from '../../utils'; import { DataTable, RowContainer } from './MemberListTable'; @@ -15,82 +15,101 @@ export const RequestToJoinList = () => { }, }); - const mutateSwrState = () => { - const unstable__mutate = (membershipRequests as any).unstable__mutate; - if (unstable__mutate && typeof unstable__mutate === 'function') { - unstable__mutate(); - } - }; - if (!organization) { return null; } - const approve = (request: OrganizationMembershipRequestResource) => () => { - return card - .runAsync(request.accept) - .then(mutateSwrState) - .catch(err => handleError(err, [], card.setError)); - }; - return ( null)} itemCount={membershipRequests?.count ?? 0} itemsPerPage={ITEMS_PER_PAGE} - isLoading={membershipRequests?.isFetching} + isLoading={membershipRequests?.isLoading} emptyStateLocalizationKey={localizationKeys('organizationProfile.membersPage.requestsTab.table__emptyRow')} headers={[ localizationKeys('organizationProfile.membersPage.activeMembersTab.tableHeader__user'), localizationKeys('organizationProfile.membersPage.requestsTab.tableHeader__requested'), localizationKeys('organizationProfile.membersPage.activeMembersTab.tableHeader__actions'), ]} - rows={(membershipRequests?.data || []).map(i => ( + rows={(membershipRequests?.data || []).map(request => ( ))} /> ); }; -const RequestRow = (props: { request: OrganizationMembershipRequestResource; onAccept: () => unknown }) => { - const { request, onAccept } = props; +const RequestRow = withCardStateProvider( + (props: { request: OrganizationMembershipRequestResource; onError: ReturnType['setError'] }) => { + const { request, onError } = props; + const card = useCardState(); + const { membershipRequests } = useCoreOrganization(); - return ( - - - - - {request.createdAt.toLocaleDateString()} + const mutateSwrState = () => { + const unstable__mutate = (membershipRequests as any).unstable__mutate; + if (unstable__mutate && typeof unstable__mutate === 'function') { + unstable__mutate(); + } + }; - - - - - - - ); -}; + const onAccept = () => { + return card + .runAsync(request.accept, 'accept') + .then(mutateSwrState) + .catch(err => handleError(err, [], onError)); + }; + + const onReject = () => { + return card + .runAsync(request.reject, 'reject') + .then(mutateSwrState) + .catch(err => handleError(err, [], onError)); + }; -const AcceptRejectRequestButtons = (props: { onAccept: () => unknown }) => { + return ( + + + + + {request.createdAt.toLocaleDateString()} + + + + + + ); + }, +); + +const AcceptRejectRequestButtons = (props: { onAccept: () => unknown; onReject: () => unknown }) => { const card = useCardState(); return ( - <> + +