Skip to content

Commit ce26b47

Browse files
authored
Merge pull request #1363 from topcoder-platform/dev
Add delete user functionality to system-admin app / fix checkpoint screener
2 parents 12c1e9c + d7c673b commit ce26b47

File tree

10 files changed

+290
-9
lines changed

10 files changed

+290
-9
lines changed

.circleci/config.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,7 @@ workflows:
233233
- feat/v6
234234
- pm-2074_1
235235
- feat/ai-workflows
236+
- delete_user
236237

237238
- deployQa:
238239
context: org-global
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
@import '@libs/ui/styles/includes';
2+
3+
.container {
4+
display: flex;
5+
flex-direction: column;
6+
gap: $sp-4;
7+
}
8+
9+
.description {
10+
white-space: pre-line;
11+
}
12+
13+
.actions {
14+
display: flex;
15+
justify-content: flex-end;
16+
gap: $sp-3;
17+
margin-top: $sp-4;
18+
}
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
import { ChangeEvent, FC, useCallback, useEffect, useState } from 'react'
2+
import classNames from 'classnames'
3+
4+
import { BaseModal, Button, InputText } from '~/libs/ui'
5+
6+
import { UserInfo } from '../../models'
7+
8+
import styles from './DialogDeleteUser.module.scss'
9+
10+
interface Props {
11+
className?: string
12+
open: boolean
13+
setOpen: (isOpen: boolean) => void
14+
userInfo: UserInfo
15+
isLoading?: boolean
16+
onDelete: (ticketUrl: string) => void
17+
}
18+
19+
export const DialogDeleteUser: FC<Props> = (props: Props) => {
20+
const [ticketUrl, setTicketUrl] = useState('')
21+
const [error, setError] = useState('')
22+
23+
useEffect(() => {
24+
if (props.open) {
25+
setTicketUrl('')
26+
setError('')
27+
}
28+
}, [props.open])
29+
30+
const handleClose = useCallback(() => {
31+
if (!props.isLoading) {
32+
props.setOpen(false)
33+
}
34+
}, [props.isLoading, props.setOpen])
35+
36+
const handleConfirm = useCallback(() => {
37+
if (!ticketUrl.trim()) {
38+
setError('Delete ticket URL is required')
39+
return
40+
}
41+
42+
setError('')
43+
props.onDelete(ticketUrl.trim())
44+
}, [props, ticketUrl])
45+
46+
const handleTicketUrlChange = useCallback(
47+
(event: ChangeEvent<HTMLInputElement>) => {
48+
if (error) {
49+
setError('')
50+
}
51+
52+
setTicketUrl(event.target.value)
53+
},
54+
[error],
55+
)
56+
57+
const description
58+
= `Are you sure you want to DELETE user ${props.userInfo.handle} with email address ${props.userInfo.email}. `
59+
+ 'If you are sure, please enter the associated delete request ticket URL below'
60+
61+
return (
62+
<BaseModal
63+
allowBodyScroll
64+
blockScroll
65+
title={`Delete ${props.userInfo.handle}`}
66+
onClose={handleClose}
67+
open={props.open}
68+
focusTrapped={false}
69+
>
70+
<div className={classNames(styles.container, props.className)}>
71+
<p className={styles.description}>{description}</p>
72+
<InputText
73+
name='ticketUrl'
74+
label='Delete request ticket URL'
75+
placeholder='https://'
76+
type='text'
77+
value={ticketUrl}
78+
error={error}
79+
onChange={handleTicketUrlChange}
80+
disabled={props.isLoading}
81+
/>
82+
83+
<div className={styles.actions}>
84+
<Button
85+
secondary
86+
size='lg'
87+
onClick={handleClose}
88+
disabled={props.isLoading}
89+
>
90+
Cancel
91+
</Button>
92+
<Button
93+
primary
94+
variant='danger'
95+
size='lg'
96+
onClick={handleConfirm}
97+
disabled={props.isLoading}
98+
>
99+
DELETE
100+
</Button>
101+
</div>
102+
</div>
103+
</BaseModal>
104+
)
105+
}
106+
107+
export default DialogDeleteUser
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { DialogDeleteUser } from './DialogDeleteUser'

src/apps/admin/src/lib/components/UsersTable/UsersTable.module.scss

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@
5757
}
5858

5959
.blockColumnAction {
60-
width: 240px;
60+
width: 320px;
6161

6262
@include ltelg {
6363
width: 60px;

src/apps/admin/src/lib/components/UsersTable/UsersTable.tsx

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import { DialogEditUserSSOLogin } from '../DialogEditUserSSOLogin'
2626
import { DialogEditUserTerms } from '../DialogEditUserTerms'
2727
import { DialogEditUserStatus } from '../DialogEditUserStatus'
2828
import { DialogUserStatusHistory } from '../DialogUserStatusHistory'
29+
import { DialogDeleteUser } from '../DialogDeleteUser'
2930
import { DropdownMenuButton } from '../common/DropdownMenuButton'
3031
import { useTableFilterLocal, useTableFilterLocalProps } from '../../hooks'
3132
import { TABLE_DATE_FORMAT } from '../../../config/index.config'
@@ -43,12 +44,18 @@ interface Props {
4344
totalPages: number
4445
onPageChange: (page: number) => void
4546
updatingStatus: { [key: string]: boolean }
47+
deletingUsers: { [key: string]: boolean }
4648
doUpdateStatus: (
4749
userInfo: UserInfo,
4850
newStatus: string,
4951
comment: string,
5052
onSuccess?: () => void,
5153
) => void
54+
doDeleteUser: (
55+
userInfo: UserInfo,
56+
ticketUrl: string,
57+
onSuccess?: () => void,
58+
) => void
5259
}
5360

5461
export const UsersTable: FC<Props> = props => {
@@ -100,6 +107,9 @@ export const UsersTable: FC<Props> = props => {
100107
const [showDialogStatusHistory, setShowDialogStatusHistory] = useState<
101108
UserInfo | undefined
102109
>()
110+
const [showDialogDeleteUser, setShowDialogDeleteUser] = useState<
111+
UserInfo | undefined
112+
>()
103113
const { width: screenWidth }: WindowSize = useWindowSize()
104114

105115
const updatingStatusBool = useMemo(
@@ -294,6 +304,8 @@ export const UsersTable: FC<Props> = props => {
294304
columnId: 'Action',
295305
label: 'Action',
296306
renderer: (data: UserInfo) => {
307+
const isDeleting = props.deletingUsers?.[data.id] === true
308+
297309
function onSelectOption(item: string): void {
298310
if (item === 'Primary Email') {
299311
setShowDialogEditUserEmail(data)
@@ -321,6 +333,8 @@ export const UsersTable: FC<Props> = props => {
321333
data,
322334
message: confirmation,
323335
})
336+
} else if (item === 'Delete') {
337+
setShowDialogDeleteUser(data)
324338
}
325339
}
326340

@@ -335,8 +349,8 @@ export const UsersTable: FC<Props> = props => {
335349
'Terms',
336350
'SSO Logins',
337351
...(data.active
338-
? ['Deactivate']
339-
: ['Activate']),
352+
? ['Deactivate', 'Delete']
353+
: ['Activate', 'Delete']),
340354
]}
341355
onSelectOption={onSelectOption}
342356
>
@@ -374,6 +388,7 @@ export const UsersTable: FC<Props> = props => {
374388
onClick={function onClick() {
375389
onSelectOption('Deactivate')
376390
}}
391+
disabled={isDeleting}
377392
/>
378393
) : (
379394
<Button
@@ -385,6 +400,15 @@ export const UsersTable: FC<Props> = props => {
385400
}}
386401
/>
387402
)}
403+
<Button
404+
primary
405+
variant='danger'
406+
label='Delete'
407+
onClick={function onClick() {
408+
onSelectOption('Delete')
409+
}}
410+
disabled={isDeleting}
411+
/>
388412
</>
389413
)}
390414
</div>
@@ -393,7 +417,7 @@ export const UsersTable: FC<Props> = props => {
393417
type: 'action',
394418
},
395419
],
396-
[isTablet, isMobile],
420+
[isTablet, isMobile, props.deletingUsers, props.updatingStatus],
397421
)
398422

399423
return (
@@ -473,6 +497,23 @@ export const UsersTable: FC<Props> = props => {
473497
isLoading={updatingStatusBool}
474498
/>
475499
)}
500+
{showDialogDeleteUser && (
501+
<DialogDeleteUser
502+
open
503+
setOpen={function setOpen() {
504+
setShowDialogDeleteUser(undefined)
505+
}}
506+
userInfo={showDialogDeleteUser}
507+
isLoading={props.deletingUsers?.[showDialogDeleteUser.id]}
508+
onDelete={function onDelete(ticketUrl: string) {
509+
props.doDeleteUser(
510+
showDialogDeleteUser,
511+
ticketUrl,
512+
() => setShowDialogDeleteUser(undefined),
513+
)
514+
}}
515+
/>
516+
)}
476517
{showDialogStatusHistory && (
477518
<DialogUserStatusHistory
478519
open

0 commit comments

Comments
 (0)