Skip to content

Commit ad46ee0

Browse files
fix: Various usability tweaks on the members page (#3805)
1 parent 359ba95 commit ad46ee0

File tree

7 files changed

+330
-303
lines changed

7 files changed

+330
-303
lines changed

src/pages/MembersPage/MembersActivation/AutoActivate/AutoActivate.jsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,19 @@ import Toggle from 'ui/Toggle'
66

77
function AutoActivate({ planAutoActivate }) {
88
const { owner, provider } = useParams()
9-
const { mutate: autoActivate } = useAutoActivate({ owner, provider })
9+
const { mutate: autoActivate, isLoading } = useAutoActivate({
10+
owner,
11+
provider,
12+
})
1013

1114
return (
12-
<div className="flex flex-col gap-2 p-4">
15+
<div className="flex flex-col items-start gap-2 p-4">
1316
<div className="font-semibold">
1417
<Toggle
1518
dataMarketing="auto-activate-members"
1619
onClick={() => autoActivate(!planAutoActivate)}
1720
value={planAutoActivate}
21+
isLoading={isLoading}
1822
label="Auto-activate members"
1923
/>
2024
</div>

src/pages/MembersPage/MembersList/MembersList.jsx

Lines changed: 2 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,13 @@
11
import { Suspense, useState } from 'react'
2-
import { useParams } from 'react-router-dom'
32

4-
import { usePlanData } from 'services/account/usePlanData'
53
import { ApiFilterEnum } from 'services/navigation/normalize'
64
import { useLocationParams } from 'services/navigation/useLocationParams'
7-
import { useUpdateUser } from 'services/users'
85
import SearchField from 'ui/SearchField'
96
import Select from 'ui/Select'
107
import Spinner from 'ui/Spinner'
118

129
import { ActivatedItems, AdminItems } from './enums'
13-
import MembersTable from './MembersTable'
10+
import MembersTable from './MembersTable/MembersTable'
1411
import UpgradeModal from './UpgradeModal/UpgradeModal'
1512

1613
const UserManagementClasses = {
@@ -26,33 +23,7 @@ const UserManagementClasses = {
2623
cta: 'w-full truncate',
2724
}
2825

29-
function useActivateUser({ provider, owner }) {
30-
const { mutate, ...rest } = useUpdateUser({
31-
provider,
32-
owner,
33-
})
34-
35-
function activate(ownerid, activated) {
36-
mutate({ targetUserOwnerid: ownerid, activated })
37-
}
38-
39-
return { activate, ...rest }
40-
}
41-
42-
const handleActivate = (planData, activate, setIsOpen) => (user) => {
43-
if (
44-
!planData?.plan?.hasSeatsLeft &&
45-
!user.activated &&
46-
planData?.plan?.isFreePlan
47-
) {
48-
setIsOpen(true)
49-
} else {
50-
activate(user.ownerid, !user.activated)
51-
}
52-
}
53-
5426
function MembersList() {
55-
const { owner, provider } = useParams()
5627
const { params, updateParams } = useLocationParams({
5728
activated: ApiFilterEnum.none, // Default to no filter on activated
5829
isAdmin: ApiFilterEnum.none, // Default to no filter on isAdmin
@@ -61,8 +32,6 @@ function MembersList() {
6132
pageSize: 50, // Default page size
6233
})
6334

64-
const { activate } = useActivateUser({ owner, provider })
65-
const { data: planData } = usePlanData({ owner, provider })
6635
const [isOpen, setIsOpen] = useState(false)
6736

6837
return (
@@ -114,7 +83,7 @@ function MembersList() {
11483
}
11584
>
11685
<MembersTable
117-
handleActivate={handleActivate(planData, activate, setIsOpen)}
86+
openUpgradeModal={() => setIsOpen(true)}
11887
params={params}
11988
/>
12089
</Suspense>
Lines changed: 12 additions & 179 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,12 @@
11
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
22
import { render, screen, waitFor } from '@testing-library/react'
33
import userEvent from '@testing-library/user-event'
4-
import { graphql, http, HttpResponse } from 'msw'
4+
import { http, HttpResponse } from 'msw'
55
import { setupServer } from 'msw/node'
66
import { MemoryRouter, Route } from 'react-router-dom'
77

88
import 'react-intersection-observer/test-utils'
99

10-
import { TrialStatuses } from 'services/account/usePlanData'
11-
import { Plans } from 'shared/utils/billing'
12-
1310
import MembersList from './MembersList'
1411

1512
const mockNonActiveUserRequest = {
@@ -50,23 +47,6 @@ const mockActiveUserRequest = {
5047
total_pages: 1,
5148
}
5249

53-
const mockPlanData = {
54-
isEnterprisePlan: false,
55-
isProPlan: false,
56-
isSentryPlan: false,
57-
isTrialPlan: false,
58-
baseUnitPrice: 10,
59-
benefits: [],
60-
billingRate: 'monthly',
61-
marketingName: 'Users Developer',
62-
monthlyUploadLimit: 250,
63-
trialStatus: TrialStatuses.NOT_STARTED,
64-
trialStartDate: '',
65-
trialEndDate: '',
66-
trialTotalDays: 0,
67-
pretrialUsersCount: 0,
68-
}
69-
7050
const queryClient = new QueryClient({
7151
defaultOptions: { queries: { retry: false } },
7252
})
@@ -101,11 +81,7 @@ describe('MembersList', () => {
10181
</QueryClientProvider>
10282
)
10383

104-
function setup({
105-
planName = Plans.USERS_DEVELOPER,
106-
planUserCount = 0,
107-
hasSeatsLeft = true,
108-
}) {
84+
function setup() {
10985
const user = userEvent.setup()
11086
const mockActivateUser = vi.fn()
11187

@@ -118,38 +94,14 @@ describe('MembersList', () => {
11894
return HttpResponse.json(mockActiveUserRequest)
11995
}
12096
return HttpResponse.json(mockNonActiveUserRequest)
121-
}),
122-
http.patch('/internal/:provider/codecov/users/:ownerid', () => {
123-
sendActivatedUser = true
124-
mockActivateUser()
125-
return HttpResponse.json({})
126-
}),
127-
graphql.query('GetPlanData', () => {
128-
return HttpResponse.json({
129-
data: {
130-
owner: {
131-
hasPrivateRepos: false,
132-
plan: {
133-
...mockPlanData,
134-
value: planName,
135-
isFreePlan: planName === Plans.USERS_DEVELOPER,
136-
isTeamPlan:
137-
planName === Plans.USERS_TEAMM ||
138-
planName === Plans.USERS_TEAMY,
139-
planUserCount,
140-
hasSeatsLeft,
141-
},
142-
},
143-
},
144-
})
14597
})
14698
)
14799

148100
return { user, mockActivateUser }
149101
}
150102

151103
describe('rendering MembersList', () => {
152-
beforeEach(() => setup({}))
104+
beforeEach(() => setup())
153105

154106
it('does not render UpgradeModal', () => {
155107
render(<MembersList />, { wrapper })
@@ -197,7 +149,7 @@ describe('MembersList', () => {
197149
describe('interacting with the status selector', () => {
198150
describe('selecting Active Users', () => {
199151
it('updates select text', async () => {
200-
const { user } = setup({})
152+
const { user } = setup()
201153
render(<MembersList />, { wrapper })
202154

203155
const select = await screen.findByText('All users')
@@ -213,7 +165,7 @@ describe('MembersList', () => {
213165
})
214166

215167
it('updates query params', async () => {
216-
const { user } = setup({})
168+
const { user } = setup()
217169
render(<MembersList />, { wrapper })
218170

219171
const select = await screen.findByText('All users')
@@ -230,7 +182,7 @@ describe('MembersList', () => {
230182

231183
describe('selecting Inactive Users', () => {
232184
it('updates select text', async () => {
233-
const { user } = setup({})
185+
const { user } = setup()
234186
render(<MembersList />, { wrapper })
235187

236188
const select = await screen.findByText('All users')
@@ -246,7 +198,7 @@ describe('MembersList', () => {
246198
})
247199

248200
it('updates query params', async () => {
249-
const { user } = setup({})
201+
const { user } = setup()
250202
render(<MembersList />, { wrapper })
251203

252204
const select = await screen.findByText('All users')
@@ -267,7 +219,7 @@ describe('MembersList', () => {
267219
describe('interacting with the search field', () => {
268220
describe('user types into search field', () => {
269221
it('updates url params', async () => {
270-
const { user } = setup({})
222+
const { user } = setup()
271223
render(<MembersList />, { wrapper })
272224

273225
const searchField = await screen.findByTestId('search-input-members')
@@ -281,7 +233,7 @@ describe('MembersList', () => {
281233
describe('interacting with the role selector', () => {
282234
describe('selecting Admins Users', () => {
283235
it('updates select text', async () => {
284-
const { user } = setup({})
236+
const { user } = setup()
285237
render(<MembersList />, { wrapper })
286238

287239
const select = await screen.findByText('Everyone')
@@ -297,7 +249,7 @@ describe('MembersList', () => {
297249
})
298250

299251
it('updates query params', async () => {
300-
const { user } = setup({})
252+
const { user } = setup()
301253
render(<MembersList />, { wrapper })
302254

303255
const select = screen.getByText('Everyone')
@@ -312,7 +264,7 @@ describe('MembersList', () => {
312264

313265
describe('selecting Developers', () => {
314266
it('updates select text', async () => {
315-
const { user } = setup({})
267+
const { user } = setup()
316268
render(<MembersList />, { wrapper })
317269

318270
const select = await screen.findByText('Everyone')
@@ -326,7 +278,7 @@ describe('MembersList', () => {
326278
})
327279

328280
it('updates query params', async () => {
329-
const { user } = setup({})
281+
const { user } = setup()
330282
render(<MembersList />, { wrapper })
331283

332284
const select = screen.getByText('Everyone')
@@ -339,123 +291,4 @@ describe('MembersList', () => {
339291
})
340292
})
341293
})
342-
343-
describe('interacting with user toggles', () => {
344-
describe('user is on a free plan', () => {
345-
describe('activated seats is greater then or equal to plan quantity', () => {
346-
it('opens up upgrade modal', async () => {
347-
const { user } = setup({
348-
hasSeatsLeft: false,
349-
planUserCount: 1,
350-
planName: Plans.USERS_DEVELOPER,
351-
})
352-
353-
render(<MembersList />, { wrapper })
354-
355-
await waitFor(() =>
356-
expect(screen.queryByTestId('spinner')).not.toBeInTheDocument()
357-
)
358-
359-
const tableHeader = await screen.findByText('Username')
360-
expect(tableHeader).toBeInTheDocument()
361-
362-
const toggle = await screen.findByLabelText('Non-Active')
363-
expect(toggle).toBeInTheDocument()
364-
await user.click(toggle)
365-
366-
const modalHeader = await screen.findByText('Upgrade to Pro')
367-
expect(modalHeader).toBeInTheDocument()
368-
})
369-
})
370-
371-
describe('activated seats is less then or equal to plan quantity', () => {
372-
it('does not open upgrade modal', async () => {
373-
const { user } = setup({
374-
hasSeatsLeft: true,
375-
planUserCount: 1,
376-
planName: Plans.USERS_DEVELOPER,
377-
})
378-
render(<MembersList />, { wrapper })
379-
380-
await waitFor(() =>
381-
expect(screen.queryByTestId('spinner')).not.toBeInTheDocument()
382-
)
383-
384-
const tableHeader = await screen.findByText('Username')
385-
expect(tableHeader).toBeInTheDocument()
386-
387-
const toggle = await screen.findByLabelText('Non-Active')
388-
expect(toggle).toBeInTheDocument()
389-
await user.click(toggle)
390-
391-
await waitFor(() =>
392-
expect(
393-
screen.queryByLabelText('Non-Active')
394-
).not.toBeInTheDocument()
395-
)
396-
397-
const activeToggle = await screen.findByText('Activated')
398-
expect(activeToggle).toBeInTheDocument()
399-
})
400-
})
401-
})
402-
403-
describe('user is not on a free plan', () => {
404-
describe('activated seats is greater then or equal to plan quantity', () => {
405-
it('renders disabled toggle', async () => {
406-
setup({
407-
hasSeatsLeft: false,
408-
planUserCount: 1,
409-
planName: Plans.USERS_PR_INAPPY,
410-
})
411-
412-
render(<MembersList />, { wrapper })
413-
414-
await waitFor(() =>
415-
expect(screen.queryByTestId('spinner')).not.toBeInTheDocument()
416-
)
417-
418-
const tableHeader = await screen.findByText('Username')
419-
expect(tableHeader).toBeInTheDocument()
420-
421-
const toggle = await screen.findByLabelText('Non-Active')
422-
expect(toggle).toBeInTheDocument()
423-
expect(toggle).toBeDisabled()
424-
})
425-
})
426-
427-
describe('activated seats is less then or equal to plan quantity', () => {
428-
it('calls activate user', async () => {
429-
const { user, mockActivateUser } = setup({
430-
hasSeatsLeft: true,
431-
planUserCount: 1,
432-
planName: Plans.USERS_PR_INAPPY,
433-
})
434-
render(<MembersList />, { wrapper })
435-
436-
await waitFor(() =>
437-
expect(screen.queryByTestId('spinner')).not.toBeInTheDocument()
438-
)
439-
440-
const tableHeader = await screen.findByText('Username')
441-
expect(tableHeader).toBeInTheDocument()
442-
443-
const toggle = await screen.findByLabelText('Non-Active')
444-
expect(toggle).toBeInTheDocument()
445-
await user.click(toggle)
446-
447-
await waitFor(() =>
448-
expect(
449-
screen.queryByLabelText('Non-Active')
450-
).not.toBeInTheDocument()
451-
)
452-
453-
const activeToggle = await screen.findByText('Activated')
454-
expect(activeToggle).toBeInTheDocument()
455-
456-
await waitFor(() => expect(mockActivateUser).toHaveBeenCalled())
457-
})
458-
})
459-
})
460-
})
461294
})

0 commit comments

Comments
 (0)