Skip to content

Commit

Permalink
feat(organizations): add functions for managing members TASK-985 (#5281)
Browse files Browse the repository at this point in the history
### 💭 Notes
Not testable per se until further PR is built atop this one and uses it
:)
  • Loading branch information
magicznyleszek authored Dec 5, 2024
1 parent f304269 commit f93524c
Show file tree
Hide file tree
Showing 3 changed files with 79 additions and 4 deletions.
78 changes: 74 additions & 4 deletions jsapp/js/account/organization/membersQuery.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,22 @@
import {keepPreviousData, useQuery} from '@tanstack/react-query';
// Libraries
import {
useQuery,
useQueryClient,
useMutation,
keepPreviousData,
} from '@tanstack/react-query';

// Stores, hooks and utilities
import {fetchGet, fetchPatch, fetchDelete} from 'js/api';
import {
useOrganizationQuery,
type OrganizationUserRole
} from './organizationQuery';

// Constants and types
import {endpoints} from 'js/api.endpoints';
import type {PaginatedResponse} from 'js/dataInterface';
import {fetchGet} from 'js/api';
import {QueryKeys} from 'js/query/queryKeys';
import {useOrganizationQuery, type OrganizationUserRole} from './organizationQuery';

export interface OrganizationMember {
/**
Expand Down Expand Up @@ -34,6 +47,62 @@ export interface OrganizationMember {
};
}

function getMemberEndpoint(orgId: string, username: string) {
return endpoints.ORGANIZATION_MEMBER_URL
.replace(':organization_id', orgId)
.replace(':username', username);
}

/**
* Mutation hook for updating organization member. It ensures that all related
* queries refetch data (are invalidated).
*/
export function usePatchOrganizationMember(username: string) {
const queryClient = useQueryClient();

const orgQuery = useOrganizationQuery();
const orgId = orgQuery.data?.id;

return useMutation({
mutationFn: async (data: Partial<OrganizationMember>) => (
// We're asserting the `orgId` is not `undefined` here, because the parent
// query (`useOrganizationMembersQuery`) wouldn't be enabled without it.
// Plus all the organization-related UI (that would use this hook) is
// accessible only to logged in users.
fetchPatch<OrganizationMember>(getMemberEndpoint(orgId!, username), data)
),
onSettled: () => {
// We invalidate query, so it will refetch (instead of refetching it
// directly, see: https://github.com/TanStack/query/discussions/2468)
queryClient.invalidateQueries({queryKey: [QueryKeys.organizationMembers]});
},
});
}

/**
* Mutation hook for removing member from organiztion. It ensures that all
* related queries refetch data (are invalidated).
*/
export function useRemoveOrganizationMember() {
const queryClient = useQueryClient();

const orgQuery = useOrganizationQuery();
const orgId = orgQuery.data?.id;

return useMutation({
mutationFn: async (username: string) => (
// We're asserting the `orgId` is not `undefined` here, because the parent
// query (`useOrganizationMembersQuery`) wouldn't be enabled without it.
// Plus all the organization-related UI (that would use this hook) is
// accessible only to logged in users.
fetchDelete(getMemberEndpoint(orgId!, username))
),
onSettled: () => {
queryClient.invalidateQueries({queryKey: [QueryKeys.organizationMembers]});
},
});
}

/**
* Fetches paginated list of members for given organization.
* This is mainly needed for `useOrganizationMembersQuery`, so you most probably
Expand All @@ -49,7 +118,8 @@ async function getOrganizationMembers(
offset: offset.toString(),
});

const apiUrl = endpoints.ORGANIZATION_MEMBERS_URL.replace(':organization_id', orgId);
const apiUrl = endpoints.ORGANIZATION_MEMBERS_URL
.replace(':organization_id', orgId);

return fetchGet<PaginatedResponse<OrganizationMember>>(
apiUrl + '?' + params,
Expand Down
4 changes: 4 additions & 0 deletions jsapp/js/account/organization/organizationQuery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ export interface Organization {
request_user_role: OrganizationUserRole;
}

/**
* Note that it's only possible to update the role via API to either `admin` or
* `member`.
*/
export enum OrganizationUserRole {
member = 'member',
admin = 'admin',
Expand Down
1 change: 1 addition & 0 deletions jsapp/js/api.endpoints.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export const endpoints = {
SUBSCRIPTION_URL: '/api/v2/stripe/subscriptions/',
ADD_ONS_URL: '/api/v2/stripe/addons/',
ORGANIZATION_MEMBERS_URL: '/api/v2/organizations/:organization_id/members/',
ORGANIZATION_MEMBER_URL: '/api/v2/organizations/:organization_id/members/:username/',
/** Expected parameters: price_id and organization_id **/
CHECKOUT_URL: '/api/v2/stripe/checkout-link',
/** Expected parameter: organization_id **/
Expand Down

0 comments on commit f93524c

Please sign in to comment.