-
Notifications
You must be signed in to change notification settings - Fork 21
Delete user functionality for system-admin (PM-3157) #1362
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| @import '@libs/ui/styles/includes'; | ||
|
|
||
| .container { | ||
| display: flex; | ||
| flex-direction: column; | ||
| gap: $sp-4; | ||
| } | ||
|
|
||
| .description { | ||
| white-space: pre-line; | ||
| } | ||
|
|
||
| .actions { | ||
| display: flex; | ||
| justify-content: flex-end; | ||
| gap: $sp-3; | ||
| margin-top: $sp-4; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,107 @@ | ||
| import { ChangeEvent, FC, useCallback, useEffect, useState } from 'react' | ||
| import classNames from 'classnames' | ||
|
|
||
| import { BaseModal, Button, InputText } from '~/libs/ui' | ||
|
|
||
| import { UserInfo } from '../../models' | ||
|
|
||
| import styles from './DialogDeleteUser.module.scss' | ||
|
|
||
| interface Props { | ||
| className?: string | ||
| open: boolean | ||
| setOpen: (isOpen: boolean) => void | ||
| userInfo: UserInfo | ||
| isLoading?: boolean | ||
| onDelete: (ticketUrl: string) => void | ||
| } | ||
|
|
||
| export const DialogDeleteUser: FC<Props> = (props: Props) => { | ||
| const [ticketUrl, setTicketUrl] = useState('') | ||
| const [error, setError] = useState('') | ||
|
|
||
| useEffect(() => { | ||
| if (props.open) { | ||
| setTicketUrl('') | ||
| setError('') | ||
| } | ||
| }, [props.open]) | ||
|
|
||
| const handleClose = useCallback(() => { | ||
| if (!props.isLoading) { | ||
| props.setOpen(false) | ||
| } | ||
| }, [props.isLoading, props.setOpen]) | ||
|
|
||
| const handleConfirm = useCallback(() => { | ||
| if (!ticketUrl.trim()) { | ||
| setError('Delete ticket URL is required') | ||
| return | ||
| } | ||
|
|
||
| setError('') | ||
| props.onDelete(ticketUrl.trim()) | ||
| }, [props, ticketUrl]) | ||
|
|
||
| const handleTicketUrlChange = useCallback( | ||
| (event: ChangeEvent<HTMLInputElement>) => { | ||
| if (error) { | ||
| setError('') | ||
| } | ||
|
|
||
| setTicketUrl(event.target.value) | ||
| }, | ||
| [error], | ||
| ) | ||
|
|
||
| const description | ||
| = `Are you sure you want to DELETE user ${props.userInfo.handle} with email address ${props.userInfo.email}. ` | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [💡 |
||
| + 'If you are sure, please enter the associated delete request ticket URL below' | ||
|
|
||
| return ( | ||
| <BaseModal | ||
| allowBodyScroll | ||
| blockScroll | ||
| title={`Delete ${props.userInfo.handle}`} | ||
| onClose={handleClose} | ||
| open={props.open} | ||
| focusTrapped={false} | ||
| > | ||
| <div className={classNames(styles.container, props.className)}> | ||
| <p className={styles.description}>{description}</p> | ||
| <InputText | ||
| name='ticketUrl' | ||
| label='Delete request ticket URL' | ||
| placeholder='https://' | ||
| type='text' | ||
| value={ticketUrl} | ||
| error={error} | ||
| onChange={handleTicketUrlChange} | ||
| disabled={props.isLoading} | ||
| /> | ||
|
|
||
| <div className={styles.actions}> | ||
| <Button | ||
| secondary | ||
| size='lg' | ||
| onClick={handleClose} | ||
| disabled={props.isLoading} | ||
| > | ||
| Cancel | ||
| </Button> | ||
| <Button | ||
| primary | ||
| variant='danger' | ||
| size='lg' | ||
| onClick={handleConfirm} | ||
| disabled={props.isLoading} | ||
| > | ||
| DELETE | ||
| </Button> | ||
| </div> | ||
| </div> | ||
| </BaseModal> | ||
| ) | ||
| } | ||
|
|
||
| export default DialogDeleteUser | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| export { DialogDeleteUser } from './DialogDeleteUser' |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -57,7 +57,7 @@ | |
| } | ||
|
|
||
| .blockColumnAction { | ||
| width: 240px; | ||
| width: 320px; | ||
|
|
||
| @include ltelg { | ||
| width: 60px; | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -26,6 +26,7 @@ import { DialogEditUserSSOLogin } from '../DialogEditUserSSOLogin' | |
| import { DialogEditUserTerms } from '../DialogEditUserTerms' | ||
| import { DialogEditUserStatus } from '../DialogEditUserStatus' | ||
| import { DialogUserStatusHistory } from '../DialogUserStatusHistory' | ||
| import { DialogDeleteUser } from '../DialogDeleteUser' | ||
| import { DropdownMenuButton } from '../common/DropdownMenuButton' | ||
| import { useTableFilterLocal, useTableFilterLocalProps } from '../../hooks' | ||
| import { TABLE_DATE_FORMAT } from '../../../config/index.config' | ||
|
|
@@ -43,12 +44,18 @@ interface Props { | |
| totalPages: number | ||
| onPageChange: (page: number) => void | ||
| updatingStatus: { [key: string]: boolean } | ||
| deletingUsers: { [key: string]: boolean } | ||
| doUpdateStatus: ( | ||
| userInfo: UserInfo, | ||
| newStatus: string, | ||
| comment: string, | ||
| onSuccess?: () => void, | ||
| ) => void | ||
| doDeleteUser: ( | ||
| userInfo: UserInfo, | ||
| ticketUrl: string, | ||
| onSuccess?: () => void, | ||
| ) => void | ||
| } | ||
|
|
||
| export const UsersTable: FC<Props> = props => { | ||
|
|
@@ -100,6 +107,9 @@ export const UsersTable: FC<Props> = props => { | |
| const [showDialogStatusHistory, setShowDialogStatusHistory] = useState< | ||
| UserInfo | undefined | ||
| >() | ||
| const [showDialogDeleteUser, setShowDialogDeleteUser] = useState< | ||
| UserInfo | undefined | ||
| >() | ||
| const { width: screenWidth }: WindowSize = useWindowSize() | ||
|
|
||
| const updatingStatusBool = useMemo( | ||
|
|
@@ -294,6 +304,8 @@ export const UsersTable: FC<Props> = props => { | |
| columnId: 'Action', | ||
| label: 'Action', | ||
| renderer: (data: UserInfo) => { | ||
| const isDeleting = props.deletingUsers?.[data.id] === true | ||
|
|
||
| function onSelectOption(item: string): void { | ||
| if (item === 'Primary Email') { | ||
| setShowDialogEditUserEmail(data) | ||
|
|
@@ -321,6 +333,8 @@ export const UsersTable: FC<Props> = props => { | |
| data, | ||
| message: confirmation, | ||
| }) | ||
| } else if (item === 'Delete') { | ||
| setShowDialogDeleteUser(data) | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -335,8 +349,8 @@ export const UsersTable: FC<Props> = props => { | |
| 'Terms', | ||
| 'SSO Logins', | ||
| ...(data.active | ||
| ? ['Deactivate'] | ||
| : ['Activate']), | ||
| ? ['Deactivate', 'Delete'] | ||
| : ['Activate', 'Delete']), | ||
| ]} | ||
| onSelectOption={onSelectOption} | ||
| > | ||
|
|
@@ -374,6 +388,7 @@ export const UsersTable: FC<Props> = props => { | |
| onClick={function onClick() { | ||
| onSelectOption('Deactivate') | ||
| }} | ||
| disabled={isDeleting} | ||
| /> | ||
| ) : ( | ||
| <Button | ||
|
|
@@ -385,6 +400,15 @@ export const UsersTable: FC<Props> = props => { | |
| }} | ||
| /> | ||
| )} | ||
| <Button | ||
| primary | ||
| variant='danger' | ||
| label='Delete' | ||
| onClick={function onClick() { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [💡 |
||
| onSelectOption('Delete') | ||
| }} | ||
| disabled={isDeleting} | ||
| /> | ||
| </> | ||
| )} | ||
| </div> | ||
|
|
@@ -393,7 +417,7 @@ export const UsersTable: FC<Props> = props => { | |
| type: 'action', | ||
| }, | ||
| ], | ||
| [isTablet, isMobile], | ||
| [isTablet, isMobile, props.deletingUsers, props.updatingStatus], | ||
| ) | ||
|
|
||
| return ( | ||
|
|
@@ -473,6 +497,23 @@ export const UsersTable: FC<Props> = props => { | |
| isLoading={updatingStatusBool} | ||
| /> | ||
| )} | ||
| {showDialogDeleteUser && ( | ||
| <DialogDeleteUser | ||
| open | ||
| setOpen={function setOpen() { | ||
| setShowDialogDeleteUser(undefined) | ||
| }} | ||
| userInfo={showDialogDeleteUser} | ||
| isLoading={props.deletingUsers?.[showDialogDeleteUser.id]} | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [ |
||
| onDelete={function onDelete(ticketUrl: string) { | ||
| props.doDeleteUser( | ||
| showDialogDeleteUser, | ||
| ticketUrl, | ||
| () => setShowDialogDeleteUser(undefined), | ||
| ) | ||
| }} | ||
| /> | ||
| )} | ||
| {showDialogStatusHistory && ( | ||
| <DialogUserStatusHistory | ||
| open | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[⚠️
correctness]Consider using a more specific validation for the
ticketUrlto ensure it is a valid URL format. This will prevent potential issues with invalid URLs being processed.