Skip to content

Commit b8a59da

Browse files
committed
Initial delete user functionality
1 parent 32af683 commit b8a59da

File tree

9 files changed

+299
-31
lines changed

9 files changed

+299
-31
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: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
import { FC, useCallback, useEffect, useState } from 'react'
2+
import { BaseModal, Button, InputText } from '~/libs/ui'
3+
import classNames from 'classnames'
4+
5+
import { UserInfo } from '../../models'
6+
7+
import styles from './DialogDeleteUser.module.scss'
8+
9+
interface Props {
10+
className?: string
11+
open: boolean
12+
setOpen: (isOpen: boolean) => void
13+
userInfo: UserInfo
14+
isLoading?: boolean
15+
onDelete: (ticketUrl: string) => void
16+
}
17+
18+
export const DialogDeleteUser: FC<Props> = (props: Props) => {
19+
const [ticketUrl, setTicketUrl] = useState('')
20+
const [error, setError] = useState('')
21+
22+
useEffect(() => {
23+
if (props.open) {
24+
setTicketUrl('')
25+
setError('')
26+
}
27+
}, [props.open])
28+
29+
const handleClose = useCallback(() => {
30+
if (!props.isLoading) {
31+
props.setOpen(false)
32+
}
33+
}, [props.isLoading, props.setOpen])
34+
35+
const handleConfirm = useCallback(() => {
36+
if (!ticketUrl.trim()) {
37+
setError('Delete ticket URL is required')
38+
return
39+
}
40+
setError('')
41+
props.onDelete(ticketUrl.trim())
42+
}, [props, ticketUrl])
43+
44+
const description = `Are you sure you want to DELETE user ${props.userInfo.handle} with email address ${props.userInfo.email}. If you are sure, please enter the associated delete request ticket URL below`
45+
46+
return (
47+
<BaseModal
48+
allowBodyScroll
49+
blockScroll
50+
title={`Delete ${props.userInfo.handle}`}
51+
onClose={handleClose}
52+
open={props.open}
53+
focusTrapped={false}
54+
>
55+
<div className={classNames(styles.container, props.className)}>
56+
<p className={styles.description}>{description}</p>
57+
<InputText
58+
name='ticketUrl'
59+
label='Delete request ticket URL'
60+
placeholder='https://'
61+
value={ticketUrl}
62+
error={error}
63+
onChange={event => {
64+
setTicketUrl(event.target.value)
65+
if (error) {
66+
setError('')
67+
}
68+
}}
69+
disabled={props.isLoading}
70+
/>
71+
72+
<div className={styles.actions}>
73+
<Button
74+
secondary
75+
size='lg'
76+
onClick={handleClose}
77+
disabled={props.isLoading}
78+
>
79+
Cancel
80+
</Button>
81+
<Button
82+
primary
83+
variant='danger'
84+
size='lg'
85+
onClick={handleConfirm}
86+
disabled={props.isLoading}
87+
>
88+
DELETE
89+
</Button>
90+
</div>
91+
</div>
92+
</BaseModal>
93+
)
94+
}
95+
96+
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: 70 additions & 29 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,27 +333,29 @@ export const UsersTable: FC<Props> = props => {
321333
data,
322334
message: confirmation,
323335
})
336+
} else if (item === 'Delete') {
337+
setShowDialogDeleteUser(data)
324338
}
325339
}
326340

327341
return (
328342
<div className={styles.blockBtns}>
329343
{isTablet ? (
330-
<DropdownMenuButton
331-
options={[
332-
'Primary Email',
333-
'Roles',
334-
'Groups',
335-
'Terms',
336-
'SSO Logins',
337-
...(data.active
338-
? ['Deactivate']
339-
: ['Activate']),
340-
]}
341-
onSelectOption={onSelectOption}
342-
>
343-
<button type='button'>
344-
<IconOutline.DotsHorizontalIcon
344+
<DropdownMenuButton
345+
options={[
346+
'Primary Email',
347+
'Roles',
348+
'Groups',
349+
'Terms',
350+
'SSO Logins',
351+
...(data.active
352+
? ['Deactivate', 'Delete']
353+
: ['Activate', 'Delete']),
354+
]}
355+
onSelectOption={onSelectOption}
356+
>
357+
<button type='button'>
358+
<IconOutline.DotsHorizontalIcon
345359
width={15}
346360
/>
347361
</button>
@@ -367,24 +381,34 @@ export const UsersTable: FC<Props> = props => {
367381
</Button>
368382
</DropdownMenuButton>
369383
{data.active ? (
370-
<Button
371-
primary
372-
variant='danger'
373-
label='Deactivate'
374-
onClick={function onClick() {
375-
onSelectOption('Deactivate')
376-
}}
377-
/>
378-
) : (
379-
<Button
380-
primary
381-
variant='linkblue'
382-
label='Activate'
384+
<Button
385+
primary
386+
variant='danger'
387+
label='Deactivate'
388+
onClick={function onClick() {
389+
onSelectOption('Deactivate')
390+
}}
391+
disabled={isDeleting}
392+
/>
393+
) : (
394+
<Button
395+
primary
396+
variant='linkblue'
397+
label='Activate'
383398
onClick={function onClick() {
384399
onSelectOption('Activate')
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)