Skip to content
This repository was archived by the owner on Sep 30, 2024. It is now read-only.

Commit 18f42a7

Browse files
authored
feat(plg): require confirmation before removing user or revoking admin (#63388)
Closes https://github.com/sourcegraph/sourcegraph/issues/63240 Adds a confirmation modal before revoking admin or removing user. <!-- 💡 To write a useful PR description, make sure that your description covers: - WHAT this PR is changing: - How was it PREVIOUSLY. - How it will be from NOW on. - WHY this PR is needed. - CONTEXT, i.e. to which initiative, project or RFC it belongs. The structure of the description doesn't matter as much as covering these points, so use your best judgement based on your context. Learn how to write good pull request description: https://www.notion.so/sourcegraph/Write-a-good-pull-request-description-610a7fd3e613496eb76f450db5a49b6e?pvs=4 --> <img width="549" alt="Screenshot 2024-06-20 at 19 50 28" src="https://github.com/sourcegraph/sourcegraph/assets/25318659/361bce61-b2c3-42da-8d3c-5389a7ba9bd2"> <img width="549" alt="Screenshot 2024-06-20 at 19 50 36" src="https://github.com/sourcegraph/sourcegraph/assets/25318659/f0bbc9c7-2097-46af-a290-74551d99896e"> ## Test plan - Run Sourcegraph instance in dotcom mode - Sign in as Cody Pro team site admin - Navigate to "/cody/team/manage" page - Click on remove button next to the team member - Ensure the confirmation modal is visible - Ensure clicking the cancel button closes the confirmation modal and user is not removed - Ensure clicking the confirm button removes the user and closes the confirmation modal - Click on the revoke admin button next to the team admin - Repeat steps for the user removal <!-- All pull requests REQUIRE a test plan: https://docs-legacy.sourcegraph.com/dev/background-information/testing_principles --> ## Changelog <!-- 1. Ensure your pull request title is formatted as: $type($domain): $what 2. Add bullet list items for each additional detail you want to cover (see example below) 3. You can edit this after the pull request was merged, as long as release shipping it hasn't been promoted to the public. 4. For more information, please see this how-to https://www.notion.so/sourcegraph/Writing-a-changelog-entry-dd997f411d524caabf0d8d38a24a878c? Audience: TS/CSE > Customers > Teammates (in that order). Cheat sheet: $type = chore|fix|feat $domain: source|search|ci|release|plg|cody|local|... --> <!-- Example: Title: fix(search): parse quotes with the appropriate context Changelog section: ## Changelog - When a quote is used with regexp pattern type, then ... - Refactored underlying code. -->
1 parent f71789c commit 18f42a7

File tree

3 files changed

+92
-28
lines changed

3 files changed

+92
-28
lines changed

client/web/src/cody/management/subscription/manage/SubscriptionDetails.module.scss

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,3 @@
77
font-size: 1rem;
88
font-weight: 600;
99
}
10-
11-
.bold {
12-
font-weight: 700;
13-
}
14-
15-
.button-container {
16-
justify-content: end;
17-
gap: 0.5rem;
18-
}

client/web/src/cody/management/subscription/manage/SubscriptionDetails.tsx

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ export const SubscriptionDetails: React.FC<SubscriptionDetailsProps> = props =>
7373
{props.subscription.cancelAtPeriodEnd
7474
? 'Subscription canceled. Access to Cody Pro will end on'
7575
: 'Subscription renews on'}{' '}
76-
<Text as="span" className={styles.bold}>
76+
<Text as="span" className="font-bold">
7777
{humanizeDate(props.subscription.currentPeriodEnd)}
7878
</Text>
7979
.
@@ -111,15 +111,14 @@ export const SubscriptionDetails: React.FC<SubscriptionDetailsProps> = props =>
111111
Canceling your subscription now means that you won't be able to use Cody with
112112
Pro features after {humanizeDate(props.subscription.currentPeriodEnd)}.
113113
</Text>
114-
<Text className={classNames('mt-4 mb-0', styles.bold)}>
115-
Do you want to proceed?
116-
</Text>
114+
<Text className="mt-4 mb-0 font-bold">Do you want to proceed?</Text>
117115
</div>
118-
<div className={classNames('d-flex mt-4', styles.buttonContainer)}>
116+
<div className="d-flex mt-4 justify-content-end">
119117
<Button
120118
variant="secondary"
121119
outline={true}
122120
onClick={() => setIsConfirmationModalVisible(false)}
121+
className="mr-3"
123122
>
124123
No, I've changed my mind
125124
</Button>

client/web/src/cody/team/TeamMemberList.tsx

Lines changed: 88 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,18 @@
11
import { type FunctionComponent, useMemo, useCallback, useState } from 'react'
22

3+
import { mdiCheck } from '@mdi/js'
34
import classNames from 'classnames'
45
import { intlFormatDistance } from 'date-fns'
56

67
import type { TelemetryV2Props } from '@sourcegraph/shared/src/telemetry'
7-
import { H2, Text, Badge, Link, ButtonLink, Button } from '@sourcegraph/wildcard'
8+
import { H2, Text, Badge, Button, Modal, H3 } from '@sourcegraph/wildcard'
89

910
import { CodyAlert } from '../components/CodyAlert'
1011
import { CodyContainer } from '../components/CodyContainer'
1112
import { useCancelInvite, useResendInvite } from '../management/api/react-query/invites'
1213
import { useUpdateTeamMember } from '../management/api/react-query/teams'
1314
import type { TeamMember, TeamInvite } from '../management/api/types'
15+
import { LoadingIconButton } from '../management/subscription/manage/LoadingIconButton'
1416

1517
import styles from './TeamMemberList.module.scss'
1618

@@ -44,6 +46,11 @@ export const TeamMemberList: FunctionComponent<TeamMemberListProps> = ({
4446
const updateTeamMemberMutation = useUpdateTeamMember()
4547
const cancelInviteMutation = useCancelInvite()
4648
const resendInviteMutation = useResendInvite()
49+
const [confirmationModal, setConfirmationModal] = useState<{
50+
action: 'removeMember' | 'revokeAdmin'
51+
member: TeamMember
52+
}>()
53+
4754
const isLoading =
4855
updateTeamMemberMutation.status === 'pending' ||
4956
cancelInviteMutation.status === 'pending' ||
@@ -142,6 +149,68 @@ export const TeamMemberList: FunctionComponent<TeamMemberListProps> = ({
142149
return null
143150
}
144151

152+
const renderConfirmActionModal = (): React.ReactNode => {
153+
if (!confirmationModal) {
154+
return null
155+
}
156+
157+
const { action, member } = confirmationModal
158+
159+
const dismissModal = (): void => setConfirmationModal(undefined)
160+
let title: string
161+
let comfirmationText: string
162+
let confirmButtonText: string
163+
let performAction: () => Promise<void>
164+
switch (action) {
165+
case 'revokeAdmin': {
166+
title = 'Revoke admin'
167+
comfirmationText = `Do you want to revoke admin rights for ${member.email}? The user will still have access to Cody Pro and remain on the team.`
168+
confirmButtonText = 'Revoke admin'
169+
performAction = () => updateRole(member.accountId, 'member')
170+
break
171+
}
172+
case 'removeMember': {
173+
title = 'Remove user'
174+
comfirmationText = `Do you want to remove ${member.email} from your Cody Pro team? The user will be downgraded to Cody Free.`
175+
confirmButtonText = 'Remove from team'
176+
performAction = () => removeMember(member.accountId)
177+
break
178+
}
179+
default: {
180+
return null
181+
}
182+
}
183+
184+
return (
185+
<Modal aria-label="Confirmation modal" isOpen={!!confirmationModal} onDismiss={dismissModal}>
186+
<div className="pb-3">
187+
<H3>{title}</H3>
188+
<Text className="mt-4 mb-0">{comfirmationText}</Text>
189+
</div>
190+
<div className="d-flex mt-4 justify-content-end">
191+
<Button
192+
variant="secondary"
193+
outline={true}
194+
disabled={updateTeamMemberMutation.isPending}
195+
onClick={dismissModal}
196+
className="mr-3"
197+
>
198+
Cancel
199+
</Button>
200+
<LoadingIconButton
201+
variant="danger"
202+
disabled={updateTeamMemberMutation.isPending}
203+
isLoading={updateTeamMemberMutation.isPending}
204+
onClick={() => performAction().finally(dismissModal)}
205+
iconSvgPath={mdiCheck}
206+
>
207+
{confirmButtonText}
208+
</LoadingIconButton>
209+
</div>
210+
</Modal>
211+
)
212+
}
213+
145214
return (
146215
<>
147216
{actionResult && (
@@ -187,7 +256,7 @@ export const TeamMemberList: FunctionComponent<TeamMemberListProps> = ({
187256
<div className="align-content-center text-center">
188257
<Button
189258
variant="link"
190-
onClick={() => updateRole(member.accountId, 'member')}
259+
onClick={() => setConfirmationModal({ action: 'revokeAdmin', member })}
191260
className="ml-2"
192261
disabled={adminCount < 2}
193262
>
@@ -198,22 +267,22 @@ export const TeamMemberList: FunctionComponent<TeamMemberListProps> = ({
198267
) : (
199268
<>
200269
<div className="align-content-center text-center">
201-
<Link
202-
to="#"
270+
<Button
271+
variant="link"
203272
onClick={() => updateRole(member.accountId, 'admin')}
204273
className="ml-2"
205274
>
206275
Make admin
207-
</Link>
276+
</Button>
208277
</div>
209278
<div className="align-content-center text-center">
210-
<Link
211-
to="#"
212-
onClick={() => removeMember(member.accountId)}
279+
<Button
280+
variant="link"
281+
onClick={() => setConfirmationModal({ action: 'removeMember', member })}
213282
className="ml-2"
214283
>
215284
Remove
216-
</Link>
285+
</Button>
217286
</div>
218287
</>
219288
)
@@ -253,26 +322,31 @@ export const TeamMemberList: FunctionComponent<TeamMemberListProps> = ({
253322
{isAdmin && (
254323
<>
255324
<div className="align-content-center text-center">
256-
<Link to="#" onClick={() => revokeInvite(invite.id)} className="ml-2">
325+
<Button
326+
variant="link"
327+
onClick={() => revokeInvite(invite.id)}
328+
className="ml-2"
329+
>
257330
Revoke
258-
</Link>
331+
</Button>
259332
</div>
260333
<div className="align-content-center text-center">
261-
<ButtonLink
262-
to="#"
334+
<Button
263335
variant="secondary"
264336
size="sm"
265337
onClick={() => resendInvite(invite.id)}
266338
className="ml-2"
267339
>
268340
Re-send invite
269-
</ButtonLink>
341+
</Button>
270342
</div>
271343
</>
272344
)}
273345
</li>
274346
))}
275347
</ul>
348+
349+
{renderConfirmActionModal()}
276350
</CodyContainer>
277351
</>
278352
)

0 commit comments

Comments
 (0)