Skip to content

Commit dcd6d07

Browse files
authored
chore: migrate invite users to rtk (#5196)
1 parent 42590ae commit dcd6d07

File tree

8 files changed

+206
-65
lines changed

8 files changed

+206
-65
lines changed

frontend/common/dispatcher/action-constants.js

-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@ const Actions = Object.assign({}, require('./base/_action-constants'), {
3333
'GET_ORGANISATION': 'GET_ORGANISATION',
3434
'GET_PROJECT': 'GET_PROJECT',
3535
'INVALIDATE_INVITE_LINK': 'INVALIDATE_INVITE_LINK',
36-
'INVITE_USERS': 'INVITE_USERS',
3736
'MIGRATE_PROJECT': 'MIGRATE_PROJECT',
3837
'OAUTH': 'OAUTH',
3938
'REFRESH_FEATURES': 'REFRESH_FEATURES',

frontend/common/dispatcher/app-actions.js

-6
Original file line numberDiff line numberDiff line change
@@ -264,12 +264,6 @@ const AppActions = Object.assign({}, require('./base/_app-actions'), {
264264
link,
265265
})
266266
},
267-
inviteUsers(invites) {
268-
Dispatcher.handleViewAction({
269-
actionType: Actions.INVITE_USERS,
270-
invites,
271-
})
272-
},
273267
migrateProject(projectId) {
274268
Dispatcher.handleViewAction({
275269
actionType: Actions.MIGRATE_PROJECT,
+110
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
import { Res } from 'common/types/responses'
2+
import { Req } from 'common/types/requests'
3+
import { service } from 'common/service'
4+
import Constants from 'common/constants'
5+
import API from 'project/api'
6+
7+
export const invitesService = service
8+
.enhanceEndpoints({ addTagTypes: ['Invite'] })
9+
.injectEndpoints({
10+
endpoints: (builder) => ({
11+
createUserInvite: builder.mutation<
12+
Res['createdUserInvite'],
13+
Req['createUserInvite']
14+
>({
15+
invalidatesTags: [{ id: 'LIST', type: 'Invite' }],
16+
query: (query: Req['createUserInvite']) => ({
17+
body: {
18+
frontend_base_url: `${document.location.origin}/email-invite/`,
19+
invites: query.invites.map((invite) => {
20+
API.trackEvent(Constants.events.INVITE)
21+
return invite
22+
}),
23+
},
24+
method: 'POST',
25+
url: `organisations/${query.organisationId}/invite/`,
26+
}),
27+
}),
28+
deleteUserInvite: builder.mutation<{}, Req['deleteUserInvite']>({
29+
invalidatesTags: [{ id: 'LIST', type: 'Invite' }],
30+
query: (query: Req['deleteUserInvite']) => {
31+
API.trackEvent(Constants.events.DELETE_INVITE)
32+
return {
33+
method: 'DELETE',
34+
url: `organisations/${query.organisationId}/invites/${query.inviteId}/`,
35+
}
36+
},
37+
}),
38+
getUserInvites: builder.query<Res['userInvites'], Req['getUserInvites']>({
39+
providesTags: [{ id: 'LIST', type: 'Invite' }],
40+
query: (query: Req['getUserInvites']) => ({
41+
url: `organisations/${query.organisationId}/invites/`,
42+
}),
43+
}),
44+
resendUserInvite: builder.mutation<{}, Req['resendUserInvite']>({
45+
invalidatesTags: [{ id: 'LIST', type: 'Invite' }],
46+
query: (query: Req['resendUserInvite']) => {
47+
API.trackEvent(Constants.events.RESEND_INVITE)
48+
return {
49+
method: 'POST',
50+
url: `organisations/${query.organisationId}/invites/${query.inviteId}/resend/`,
51+
}
52+
},
53+
}),
54+
}),
55+
})
56+
57+
export async function createUserInvite(
58+
store: any,
59+
data: Req['createUserInvite'],
60+
options?: Parameters<
61+
typeof invitesService.endpoints.createUserInvite.initiate
62+
>[1],
63+
) {
64+
return store.dispatch(
65+
invitesService.endpoints.createUserInvite.initiate(data, options),
66+
)
67+
}
68+
69+
export async function deleteUserInvite(
70+
store: any,
71+
data: Req['deleteUserInvite'],
72+
options?: Parameters<
73+
typeof invitesService.endpoints.deleteUserInvite.initiate
74+
>[1],
75+
) {
76+
return store.dispatch(
77+
invitesService.endpoints.deleteUserInvite.initiate(data, options),
78+
)
79+
}
80+
81+
export async function getUserInvites(
82+
store: any,
83+
data: Req['getUserInvites'],
84+
options?: Parameters<
85+
typeof invitesService.endpoints.getUserInvites.initiate
86+
>[1],
87+
) {
88+
return store.dispatch(
89+
invitesService.endpoints.getUserInvites.initiate(data, options),
90+
)
91+
}
92+
93+
export async function resendUserInvite(
94+
store: any,
95+
data: Req['resendUserInvite'],
96+
options?: Parameters<
97+
typeof invitesService.endpoints.resendUserInvite.initiate
98+
>[1],
99+
) {
100+
return store.dispatch(
101+
invitesService.endpoints.resendUserInvite.initiate(data, options),
102+
)
103+
}
104+
105+
export const {
106+
useCreateUserInviteMutation,
107+
useDeleteUserInviteMutation,
108+
useGetUserInvitesQuery,
109+
useResendUserInviteMutation,
110+
} = invitesService

frontend/common/stores/organisation-store.js

-33
Original file line numberDiff line numberDiff line change
@@ -254,36 +254,6 @@ const controller = {
254254
}),
255255
)
256256
},
257-
inviteUsers: (invites) => {
258-
store.saving()
259-
data
260-
.post(`${Project.api}organisations/${store.id}/invite/`, {
261-
frontend_base_url: `${document.location.origin}/email-invite/`,
262-
invites: invites.map((invite) => {
263-
API.trackEvent(Constants.events.INVITE)
264-
return {
265-
email: invite.emailAddress,
266-
permission_groups: invite.groups,
267-
role: invite.role.value,
268-
}
269-
}),
270-
})
271-
.then((res) => {
272-
store.model.invites = store.model.invites || []
273-
store.model.invites = store.model.invites.concat(res)
274-
store.saved()
275-
toast('Invite(s) sent successfully')
276-
})
277-
.catch((e) => {
278-
store.saved()
279-
toast(
280-
`Failed to send invite(s). ${
281-
e && e.error ? e.error : 'Please try again later'
282-
}`,
283-
'danger',
284-
)
285-
})
286-
},
287257
resendInvite: (id) => {
288258
data
289259
.post(`${Project.api}organisations/${store.id}/invites/${id}/resend/`)
@@ -359,9 +329,6 @@ store.dispatcherIndex = Dispatcher.register(store, (payload) => {
359329
case Actions.DELETE_PROJECT:
360330
controller.deleteProject(action.id)
361331
break
362-
case Actions.INVITE_USERS:
363-
controller.inviteUsers(action.invites)
364-
break
365332
case Actions.DELETE_INVITE:
366333
controller.deleteInvite(action.id)
367334
break

frontend/common/types/requests.ts

+19
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,25 @@ export type Req = {
190190
description: string | null
191191
name: string
192192
}
193+
getUserInvites: {
194+
organisationId: number
195+
}
196+
createUserInvite: {
197+
organisationId: number
198+
invites: {
199+
email: string
200+
role: string
201+
permission_groups: number[]
202+
}[]
203+
}
204+
deleteUserInvite: {
205+
organisationId: number
206+
inviteId: number
207+
}
208+
resendUserInvite: {
209+
organisationId: number
210+
inviteId: number
211+
}
193212
getRole: { organisation_id: string; role_id: number }
194213
updateRole: {
195214
organisation_id: number

frontend/common/types/responses.ts

+5-4
Original file line numberDiff line numberDiff line change
@@ -235,11 +235,10 @@ export type githubIntegration = {
235235
export type User = {
236236
id: number
237237
email: string
238-
last_login?: string
239238
first_name: string
240239
last_name: string
241-
role: 'ADMIN' | 'USER'
242-
date_joined: string
240+
last_login: string
241+
uuid: string
243242
}
244243
export type GroupUser = Omit<User, 'role'> & {
245244
group_admin: boolean
@@ -486,7 +485,7 @@ export type Invite = {
486485
email: string
487486
date_created: string
488487
invited_by: User
489-
link: string
488+
link?: string
490489
permission_groups: number[]
491490
}
492491

@@ -787,6 +786,8 @@ export type Res = {
787786
groupAdmin: { id: string }
788787
groups: PagedResponse<UserGroup>
789788
group: UserGroup
789+
userInvites: PagedResponse<Invite>
790+
createdUserInvite: Invite[]
790791
myGroups: PagedResponse<UserGroupSummary>
791792
createSegmentOverride: {
792793
id: number

frontend/web/components/modals/InviteUsers.tsx

+37-15
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,22 @@ import InputGroup from 'components/base/forms/InputGroup'
1010
import OrganisationProvider from 'common/providers/OrganisationProvider'
1111
import Utils from 'common/utils/utils'
1212
import _ from 'lodash'
13-
import AppActions from 'common/dispatcher/app-actions'
1413
import ErrorMessage from 'components/ErrorMessage'
1514
import AccountStore from 'common/stores/account-store'
1615
import { close as closeIcon } from 'ionicons/icons'
1716
import { MultiValueProps } from 'react-select/lib/components/MultiValue'
1817
import { useGetGroupsQuery } from 'common/services/useGroup'
18+
import { useCreateUserInviteMutation } from 'common/services/useInvites'
19+
import { Req } from 'common/types/requests'
1920

2021
interface Invite {
2122
temporaryId: string
22-
emailAddress?: string
23-
role?: string
23+
emailAddress: string
24+
role?: {
25+
isDisabled: boolean
26+
label: React.ReactNode
27+
value: string
28+
}
2429
groups?: number[]
2530
}
2631

@@ -49,8 +54,10 @@ const CustomMultiValue = (props: MultiValueProps<GroupOption>) => {
4954
type GroupOption = { label: string; value: number }
5055

5156
const InviteUsers: FC = () => {
57+
const [createUserInvite, { isLoading }] = useCreateUserInviteMutation()
58+
5259
const [invites, setInvites] = useState<Invite[]>([
53-
{ temporaryId: Utils.GUID() },
60+
{ emailAddress: '', temporaryId: Utils.GUID() },
5461
])
5562
const inputRef = useRef<HTMLInputElement>(null)
5663

@@ -102,9 +109,18 @@ const InviteUsers: FC = () => {
102109
setInvites(invites.filter((invite) => invite.temporaryId !== id))
103110
}
104111

105-
const close = (invites: Omit<Invite, 'temporaryId'>[]): void => {
106-
AppActions.inviteUsers(invites)
107-
closeModal()
112+
const getPayload = (
113+
invites: Omit<Invite, 'temporaryId'>[],
114+
orgId: number,
115+
): Req['createUserInvite'] => {
116+
return {
117+
invites: invites.map((invite) => ({
118+
email: invite.emailAddress,
119+
permission_groups: invite.groups ?? [],
120+
role: invite.role?.value ?? '',
121+
})),
122+
organisationId: orgId,
123+
}
108124
}
109125

110126
return (
@@ -114,7 +130,14 @@ const InviteUsers: FC = () => {
114130
<form
115131
onSubmit={(e) => {
116132
e.preventDefault()
117-
AppActions.inviteUsers(invites)
133+
createUserInvite(getPayload(invites, orgId))
134+
.then(() => {
135+
toast('Invite sent successfully')
136+
})
137+
.catch((error) => {
138+
toast('Error sending invite', 'error')
139+
console.error(error)
140+
})
118141
}}
119142
>
120143
<div
@@ -249,10 +272,13 @@ const InviteUsers: FC = () => {
249272
theme='outline'
250273
size='small'
251274
id='btn-add-invite'
252-
disabled={isSaving || !isValid()}
275+
disabled={isSaving || isLoading || !isValid()}
253276
type='button'
254277
onClick={() =>
255-
setInvites([...invites, { temporaryId: Utils.GUID() }])
278+
setInvites([
279+
...invites,
280+
{ emailAddress: '', temporaryId: Utils.GUID() },
281+
])
256282
}
257283
>
258284
<Row>
@@ -286,11 +312,7 @@ const InviteUsers: FC = () => {
286312
<Button
287313
id='btn-send-invite'
288314
disabled={isSaving || !isValid()}
289-
onClick={() =>
290-
close(
291-
invites.map(({ temporaryId, ...rest }) => ({ ...rest })),
292-
)
293-
}
315+
onClick={() => closeModal()}
294316
type='submit'
295317
>
296318
{isSaving ? 'Sending' : 'Send Invitation'}

0 commit comments

Comments
 (0)