0
Fork 0
mirror of https://github.com/TryGhost/Ghost.git synced 2025-02-03 23:00:14 -05:00

Wired suspend user action on user detail modal in adminX

refs https://github.com/TryGhost/Team/issues/3351

- wires Suspend/Un-suspend user action on user detail modal
- adds running state for task buttons in modal
- adds api to delete and suspend/un-suspend users
This commit is contained in:
Rishabh 2023-06-05 10:08:50 +05:30
parent 2b0a6bc454
commit c2371b4841
3 changed files with 74 additions and 29 deletions

View file

@ -1,39 +1,49 @@
import Modal from './Modal';
import NiceModal from '@ebay/nice-modal-react';
import React from 'react';
import NiceModal, {useModal} from '@ebay/nice-modal-react';
import React, {useState} from 'react';
export interface ConfirmationModalProps {
title?: string;
prompt?: React.ReactNode;
cancelLabel?: string;
okLabel?: string;
okRunningLabel?: string;
okColor?: string;
onCancel?: () => void;
onOk?: () => void;
onOk?: (modal?: {
remove: () => void;
}) => void;
customFooter?: React.ReactNode;
}
const ConfirmationModal: React.FC<ConfirmationModalProps> = ({
title = 'Are you sure?',
title = 'Are you sure?',
prompt,
cancelLabel = 'Cancel',
okLabel = 'OK',
cancelLabel = 'Cancel',
okLabel = 'OK',
okRunningLabel = '...',
okColor = 'black',
onCancel,
onOk,
onCancel,
onOk,
customFooter
}) => {
const modal = useModal();
const [taskState, setTaskState] = useState<'running' | ''>('');
return (
<Modal
<Modal
backDrop={false}
cancelLabel={cancelLabel}
customFooter={customFooter}
okColor={okColor}
okLabel={okLabel}
okLabel={taskState === 'running' ? okRunningLabel : okLabel}
size={540}
title={title}
onCancel={onCancel}
onOk={onOk}
onOk={async () => {
setTaskState('running');
await onOk?.(modal);
setTaskState('');
}}
>
<div className='py-4'>
{prompt}

View file

@ -416,23 +416,38 @@ const confirmDelete = () => {
});
};
const confirmSuspend = () => {
NiceModal.show(ConfirmationModal, {
title: 'Are you sure you want to suspend this user?',
prompt: (
<>
<strong>WARNING:</strong> This user will no longer be able to log in but their posts will be kept.
</>
),
okLabel: 'Suspend',
okColor: 'red'
});
};
const UserDetailModal:React.FC<UserDetailModalProps> = ({user, updateUser}) => {
const {api} = useContext(ServicesContext);
const [userData, setUserData] = useState(user);
const [saveState, setSaveState] = useState('');
const confirmSuspend = (_user: User) => {
let warningText = 'This user will no longer be able to log in but their posts will be kept.';
if (_user.status === 'inactive') {
warningText = 'This user will be able to log in again and will have the same permissions they had previously.';
}
NiceModal.show(ConfirmationModal, {
title: 'Are you sure you want to suspend this user?',
prompt: (
<>
<strong>WARNING:</strong> {warningText}
</>
),
okLabel: _user.status === 'inactive' ? 'Un-suspend' : 'Suspend',
okRunningLabel: _user.status === 'inactive' ? 'Un-suspending...' : 'Suspending...',
okColor: 'red',
onOk: async (modal) => {
await api.users.edit({
..._user,
status: _user.status === 'inactive' ? 'active' : 'inactive'
});
modal?.remove();
}
});
};
let suspendUserLabel = user?.status === 'inactive' ? 'Un-suspend user' : 'Suspend user';
const menuItems = [
{
id: 'make-owner',
@ -442,12 +457,16 @@ const UserDetailModal:React.FC<UserDetailModalProps> = ({user, updateUser}) => {
{
id: 'delete-user',
label: 'Delete user',
onClick: confirmDelete
onClick: () => {
confirmDelete();
}
},
{
id: 'suspend-user',
label: 'Suspend user',
onClick: confirmSuspend
label: suspendUserLabel,
onClick: () => {
confirmSuspend(user);
}
},
{
id: 'view-user-activity',
@ -471,6 +490,8 @@ const UserDetailModal:React.FC<UserDetailModalProps> = ({user, updateUser}) => {
const fileUploadButtonClasses = 'absolute right-[104px] bottom-12 bg-[rgba(0,0,0,0.75)] rounded text-sm text-white flex items-center justify-center px-3 h-8 opacity-80 hover:opacity-100 transition cursor-pointer font-medium z-10';
const suspendedText = user.status === 'inactive' ? ' (Suspended)' : '';
return (
<Modal
okColor='green'
@ -522,11 +543,11 @@ const UserDetailModal:React.FC<UserDetailModalProps> = ({user, updateUser}) => {
<Icon color='white' name='user-add' size='lg' />
</ImageUpload>
<div>
<Heading styles='text-white'>{user.name}</Heading>
<Heading styles='text-white'>{user.name}{suspendedText}</Heading>
<span className='text-md font-semibold text-white'>Administrator</span>
</div>
</div>
</div>
</div>
<div className='mt-10 grid grid-cols-2 gap-x-12 gap-y-20 pb-10'>
<Basic setUserData={setUserData} user={userData} />
<Details setUserData={setUserData} user={userData} />

View file

@ -22,6 +22,12 @@ export interface UsersResponseType {
users: User[];
}
export interface DeleteUserResponse {
meta: {
filename: string;
}
}
export interface RolesResponseType {
meta?: Meta;
roles: UserRole[];
@ -90,6 +96,7 @@ interface API {
browse: () => Promise<UsersResponseType>;
currentUser: () => Promise<User>;
edit: (editedUser: User) => Promise<UsersResponseType>;
delete: (userId: string) => Promise<DeleteUserResponse>;
updatePassword: (options: UpdatePasswordOptions) => Promise<PasswordUpdateResponseType>;
};
roles: {
@ -205,6 +212,13 @@ function setupGhostApi({ghostVersion}: GhostApiOptions): API {
});
const data: PasswordUpdateResponseType = await response.json();
return data;
},
delete: async (userId: string) => {
const response = await fetcher(`/users/${userId}/`, {
method: 'DELETE'
});
const data: DeleteUserResponse = await response.json();
return data;
}
},
roles: {