fix: change admin questions and answers operate

This commit is contained in:
shuai 2023-09-14 12:27:13 +08:00
parent a4adf7f40d
commit 0269c30516
10 changed files with 213 additions and 503 deletions

View File

@ -1097,12 +1097,15 @@ ui:
Are you sure you wish to delete? Are you sure you wish to delete?
other: Are you sure you wish to delete? other: Are you sure you wish to delete?
tip_answer_deleted: This answer has been deleted tip_answer_deleted: This answer has been deleted
undelete_title: Undelete this post
undelete_desc: Are you sure you wish to undelete?
btns: btns:
confirm: Confirm confirm: Confirm
cancel: Cancel cancel: Cancel
edit: Edit edit: Edit
save: Save save: Save
delete: Delete delete: Delete
undelete: Undelete
login: Log in login: Log in
signup: Sign up signup: Sign up
logout: Log out logout: Log out
@ -1123,6 +1126,8 @@ ui:
active: Active active: Active
suspend: Suspend suspend: Suspend
unsuspend: Unsuspend unsuspend: Unsuspend
close: Close
reopen: Reopen
search: search:
title: Search Results title: Search Results
keywords: Keywords keywords: Keywords
@ -1411,34 +1416,6 @@ ui:
created: Created created: Created
action: Action action: Action
review: Review review: Review
change_modal:
title: Change user status to...
btn_cancel: Cancel
btn_submit: Submit
normal_name: normal
normal_desc: A normal user can ask and answer questions.
suspended_name: suspended
suspended_desc: A suspended user can't log in.
deleted_name: deleted
deleted_desc: "Delete profile, authentication associations."
inactive_name: inactive
inactive_desc: An inactive user must re-validate their email.
confirm_title: Delete this user
confirm_content: Are you sure you want to delete this user? This is permanent!
confirm_btn: Delete
msg:
empty: Please select a reason.
status_modal:
title: "Change {{ type }} status to..."
normal_name: normal
normal_desc: A normal post available to everyone.
closed_name: closed
closed_desc: "A closed question can't answer, but still can edit, vote and comment."
deleted_name: deleted
deleted_desc: This post will be deleted.
btn_cancel: Cancel
btn_submit: Submit
btn_next: Next
user_role_modal: user_role_modal:
title: Change user role to... title: Change user role to...
btn_cancel: Cancel btn_cancel: Cancel

View File

@ -2,8 +2,6 @@ import useTagModal from './useTagModal';
import useToast from './useToast'; import useToast from './useToast';
import useReportModal from './useReportModal'; import useReportModal from './useReportModal';
import usePageUsers from './usePageUsers'; import usePageUsers from './usePageUsers';
import useChangeModal from './useChangeModal';
import useEditStatusModal from './useEditStatusModal';
import useChangeUserRoleModal from './useChangeUserRoleModal'; import useChangeUserRoleModal from './useChangeUserRoleModal';
import useUserModal from './useUserModal'; import useUserModal from './useUserModal';
import useChangePasswordModal from './useChangePasswordModal'; import useChangePasswordModal from './useChangePasswordModal';
@ -18,8 +16,6 @@ export {
useToast, useToast,
useReportModal, useReportModal,
usePageUsers, usePageUsers,
useChangeModal,
useEditStatusModal,
useChangeUserRoleModal, useChangeUserRoleModal,
useUserModal, useUserModal,
useChangePasswordModal, useChangePasswordModal,

View File

@ -1,215 +0,0 @@
import { useLayoutEffect, useState } from 'react';
import { Modal, Form, Button, FormCheck } from 'react-bootstrap';
import { useTranslation } from 'react-i18next';
import ReactDOM from 'react-dom/client';
import { Modal as AnswerModal } from '@/components';
import { changeUserStatus } from '@/services';
const div = document.createElement('div');
const root = ReactDOM.createRoot(div);
interface Props {
callback?: () => void;
}
const useChangeModal = ({ callback }: Props) => {
const { t } = useTranslation('translation', {
keyPrefix: 'admin.change_modal',
});
const [id, setId] = useState('');
const [defaultType, setDefaultType] = useState('');
const [isInvalid, setInvalidState] = useState(false);
const [changeType, setChangeType] = useState({
type: '',
haveContent: false,
});
const [content, setContent] = useState({
value: '',
isInvalid: false,
errorMsg: '',
});
const [show, setShow] = useState(false);
const [list] = useState<any[]>([
{
type: 'normal',
name: t('normal_name'),
description: t('normal_desc'),
},
{
type: 'suspended',
name: t('suspended_name'),
description: t('suspended_desc'),
},
{
type: 'deleted',
name: t('deleted_name'),
description: t('deleted_desc'),
},
{
type: 'inactive',
name: t('inactive_name'),
description: t('inactive_desc'),
},
]);
const handleRadio = (val) => {
setInvalidState(false);
setContent({
value: '',
isInvalid: false,
errorMsg: '',
});
setChangeType({
type: val.type,
haveContent: val.have_content,
});
};
const onClose = () => {
setChangeType({
type: '',
haveContent: false,
});
setContent({
value: '',
isInvalid: false,
errorMsg: '',
});
setContent({
value: '',
isInvalid: false,
errorMsg: '',
});
setShow(false);
};
const handleSubmit = () => {
if (changeType.type === '') {
setInvalidState(true);
return;
}
if (changeType.haveContent && !content.value) {
setContent({
value: content.value,
isInvalid: true,
errorMsg: t('remark.empty'),
});
return;
}
if (defaultType === changeType.type) {
onClose();
return;
}
if (changeType.type === 'deleted') {
onClose();
AnswerModal.confirm({
title: t('confirm_title'),
content: t('confirm_content'),
confirmText: t('confirm_btn'),
confirmBtnVariant: 'danger',
onConfirm: () => {
changeUserStatus({
user_id: id,
status: changeType.type,
}).then(() => {
callback?.();
onClose();
});
},
});
return;
}
changeUserStatus({
user_id: id,
status: changeType.type,
}).then(() => {
callback?.();
onClose();
});
};
const onShow = (params) => {
setId(params.id);
setChangeType({
...changeType,
type: params.type,
});
setDefaultType(params.type);
setShow(true);
};
useLayoutEffect(() => {
root.render(
<Modal show={show} onHide={onClose}>
<Modal.Header closeButton>
<Modal.Title as="h5">{t('title')}</Modal.Title>
</Modal.Header>
<Modal.Body>
<Form>
{list.map((item) => {
if (
defaultType === 'inactive' &&
(item.type === 'suspended' || item.type === 'deleted')
) {
return null;
}
if (defaultType === 'suspended' && item.type === 'inactive') {
return null;
}
return (
<div key={item?.type}>
<Form.Group
controlId={item.type}
className={`${
item.have_content && changeType === item.type
? 'mb-2'
: 'mb-3'
}`}>
<FormCheck>
<FormCheck.Input
id={item.type}
type="radio"
checked={changeType.type === item.type}
onChange={() => handleRadio(item)}
isInvalid={isInvalid}
/>
<FormCheck.Label htmlFor={item.type}>
<span className="fw-bold">{item?.name}</span>
<br />
<span className="text-secondary">
{item?.description}
</span>
</FormCheck.Label>
<Form.Control.Feedback type="invalid">
{t('msg.empty')}
</Form.Control.Feedback>
</FormCheck>
</Form.Group>
</div>
);
})}
</Form>
</Modal.Body>
<Modal.Footer>
<Button variant="link" onClick={() => onClose()}>
{t('btn_cancel')}
</Button>
<Button variant="primary" onClick={handleSubmit}>
{t('btn_submit')}
</Button>
</Modal.Footer>
</Modal>,
);
});
return {
onClose,
onShow,
};
};
export default useChangeModal;

View File

@ -1,134 +0,0 @@
import { useLayoutEffect, useState } from 'react';
import { Modal, Form, Button, FormCheck } from 'react-bootstrap';
import { useTranslation } from 'react-i18next';
import ReactDOM from 'react-dom/client';
const div = document.createElement('div');
const root = ReactDOM.createRoot(div);
const useEditStatusModal = ({
editType = '',
callback,
}: {
editType: string;
callback: (id, type) => void;
}) => {
const { t } = useTranslation('translation', {
keyPrefix: 'admin.status_modal',
});
const [id, setId] = useState('');
const [defaultType, setDefaultType] = useState('');
const [isInvalid, setInvalidState] = useState(false);
const [changeType, setChangeType] = useState('normal');
const [show, setShow] = useState(false);
const [list] = useState<any[]>([
{
type: 'normal',
name: t('normal_name'),
description: t('normal_desc'),
},
{
type: 'closed',
name: t('closed_name'),
description: t('closed_desc'),
},
{
type: 'deleted',
name: t('deleted_name'),
description: t('deleted_desc'),
},
]);
const handleRadio = (val) => {
setInvalidState(false);
setChangeType(val.type);
};
const onClose = () => {
setChangeType('');
setShow(false);
};
const handleSubmit = () => {
if (changeType === '') {
setInvalidState(true);
return;
}
if (defaultType === changeType) {
onClose();
return;
}
onClose();
callback?.(id, changeType);
};
const onShow = (params) => {
setId(params.id);
setChangeType(params.type);
setDefaultType(params.type);
setShow(true);
};
useLayoutEffect(() => {
root.render(
<Modal show={show} onHide={onClose}>
<Modal.Header closeButton>
<Modal.Title as="h5">{t('title', { type: editType })}</Modal.Title>
</Modal.Header>
<Modal.Body>
<Form>
{list.map((item) => {
if (editType === 'answer' && item.type === 'closed') {
return null;
}
return (
<div key={item?.type}>
<Form.Group controlId={item.type} className="mb-3">
<FormCheck>
<FormCheck.Input
id={item.type}
type="radio"
checked={changeType === item.type}
onChange={() => handleRadio(item)}
isInvalid={isInvalid}
/>
<FormCheck.Label htmlFor={item.type}>
<span className="fw-bold">{item.name}</span>
<br />
<span className="small text-secondary">
{item.description}
</span>
</FormCheck.Label>
<Form.Control.Feedback type="invalid">
{t('msg.empty')}
</Form.Control.Feedback>
</FormCheck>
</Form.Group>
</div>
);
})}
</Form>
</Modal.Body>
<Modal.Footer>
<Button variant="link" onClick={() => onClose()}>
{t('btn_cancel')}
</Button>
<Button variant="primary" onClick={handleSubmit}>
{changeType !== 'normal' ? t('btn_next') : t('btn_submit')}
</Button>
</Modal.Footer>
</Modal>,
);
});
return {
onClose,
onShow,
};
};
export default useEditStatusModal;

View File

@ -0,0 +1,66 @@
import { Dropdown } from 'react-bootstrap';
import { useTranslation } from 'react-i18next';
import { Icon, Modal } from '@/components';
import { changeAnswerStatus } from '@/services';
const AnswerActions = ({ itemData, curFilter, refreshList }) => {
const { t } = useTranslation('translation', { keyPrefix: 'delete' });
const handleAction = (type) => {
console.log(type);
if (type === 'delete') {
Modal.confirm({
title: t('title'),
content: itemData.accepted === 2 ? t('answer_accepted') : t('other'),
cancelBtnVariant: 'link',
confirmBtnVariant: 'danger',
confirmText: t('delete', { keyPrefix: 'btns' }),
onConfirm: () => {
changeAnswerStatus(itemData.id, 'deleted').then(() => {
refreshList();
});
},
});
}
if (type === 'undelete') {
Modal.confirm({
title: t('undelete_title'),
content: t('undelete_desc'),
cancelBtnVariant: 'link',
confirmBtnVariant: 'danger',
confirmText: t('undelete', { keyPrefix: 'btns' }),
onConfirm: () => {
changeAnswerStatus(itemData.id, 'available').then(() => {
refreshList();
});
},
});
}
};
return (
<Dropdown>
<Dropdown.Toggle variant="link" className="no-toggle p-0">
<Icon
name="three-dots-vertical"
title={t('action', { keyPrefix: 'admin.answers' })}
/>
</Dropdown.Toggle>
<Dropdown.Menu>
{curFilter === 'deleted' ? (
<Dropdown.Item onClick={() => handleAction('undelete')}>
{t('undelete', { keyPrefix: 'btns' })}
</Dropdown.Item>
) : (
<Dropdown.Item onClick={() => handleAction('delete')}>
{t('delete', { keyPrefix: 'btns' })}
</Dropdown.Item>
)}
</Dropdown.Menu>
</Dropdown>
);
};
export default AnswerActions;

View File

@ -1,5 +1,5 @@
import { FC } from 'react'; import { FC } from 'react';
import { Button, Form, Table, Stack } from 'react-bootstrap'; import { Form, Table, Stack } from 'react-bootstrap';
import { useSearchParams } from 'react-router-dom'; import { useSearchParams } from 'react-router-dom';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
@ -9,18 +9,18 @@ import {
FormatTime, FormatTime,
Icon, Icon,
Pagination, Pagination,
Modal,
BaseUserCard, BaseUserCard,
Empty, Empty,
QueryGroup, QueryGroup,
} from '@/components'; } from '@/components';
import { ADMIN_LIST_STATUS } from '@/common/constants'; import { ADMIN_LIST_STATUS } from '@/common/constants';
import { useEditStatusModal } from '@/hooks';
import * as Type from '@/common/interface'; import * as Type from '@/common/interface';
import { useAnswerSearch, changeAnswerStatus } from '@/services'; import { useAnswerSearch } from '@/services';
import { escapeRemove } from '@/utils'; import { escapeRemove } from '@/utils';
import { pathFactory } from '@/router/pathFactory'; import { pathFactory } from '@/router/pathFactory';
import AnswerAction from './components/Action';
const answerFilterItems: Type.AdminContentsFilterBy[] = ['normal', 'deleted']; const answerFilterItems: Type.AdminContentsFilterBy[] = ['normal', 'deleted'];
const Answers: FC = () => { const Answers: FC = () => {
@ -45,44 +45,6 @@ const Answers: FC = () => {
}); });
const count = listData?.count || 0; const count = listData?.count || 0;
const handleCallback = (id, type) => {
if (type === 'normal') {
changeAnswerStatus(id, 'available').then(() => {
refreshList();
});
}
if (type === 'deleted') {
const item = listData?.list?.filter((v) => v.id === id)?.[0];
Modal.confirm({
title: t('title', { keyPrefix: 'delete' }),
content:
item.accepted === 2
? t('answer_accepted', { keyPrefix: 'delete' })
: t('other', { keyPrefix: 'delete' }),
cancelBtnVariant: 'link',
confirmBtnVariant: 'danger',
confirmText: t('delete', { keyPrefix: 'btns' }),
onConfirm: () => {
changeAnswerStatus(id, 'deleted').then(() => {
refreshList();
});
},
});
}
};
const changeModal = useEditStatusModal({
editType: 'answer',
callback: handleCallback,
});
const handleChange = (itemId) => {
changeModal.onShow({
id: itemId,
type: curFilter,
});
};
const handleFilter = (e) => { const handleFilter = (e) => {
urlSearchParams.set('query', e.target.value); urlSearchParams.set('query', e.target.value);
urlSearchParams.delete('page'); urlSearchParams.delete('page');
@ -115,9 +77,9 @@ const Answers: FC = () => {
<th style={{ width: '11%' }}>{t('votes')}</th> <th style={{ width: '11%' }}>{t('votes')}</th>
<th style={{ width: '14%' }}>{t('created')}</th> <th style={{ width: '14%' }}>{t('created')}</th>
<th style={{ width: '11%' }}>{t('status')}</th> <th style={{ width: '11%' }}>{t('status')}</th>
{curFilter !== 'deleted' && ( <th style={{ width: '11%' }} className="text-end">
<th style={{ width: '11%' }}>{t('action')}</th> {t('action')}
)} </th>
</tr> </tr>
</thead> </thead>
<tbody className="align-middle"> <tbody className="align-middle">
@ -172,16 +134,13 @@ const Answers: FC = () => {
{t(ADMIN_LIST_STATUS[curFilter]?.name)} {t(ADMIN_LIST_STATUS[curFilter]?.name)}
</span> </span>
</td> </td>
{curFilter !== 'deleted' && ( <td className="text-end">
<td> <AnswerAction
<Button itemData={{ id: li.id, accepted: li.accepted }}
variant="link" curFilter={curFilter}
className="p-0 btn-no-border" refreshList={refreshList}
onClick={() => handleChange(li.id)}> />
{t('change')} </td>
</Button>
</td>
)}
</tr> </tr>
); );
})} })}

View File

@ -0,0 +1,110 @@
import { Dropdown } from 'react-bootstrap';
import { useTranslation } from 'react-i18next';
import { Icon, Modal } from '@/components';
import { changeQuestionStatus, reopenQuestion } from '@/services';
import { useReportModal, useToast } from '@/hooks';
const AnswerActions = ({ itemData, refreshList, curFilter }) => {
const { t } = useTranslation('translation', { keyPrefix: 'delete' });
const closeModal = useReportModal(refreshList);
const toast = useToast();
const handleAction = (type) => {
console.log(type);
if (type === 'delete') {
Modal.confirm({
title: t('title', { keyPrefix: 'delete' }),
content:
itemData.answer_count > 0
? t('question', { keyPrefix: 'delete' })
: t('other', { keyPrefix: 'delete' }),
cancelBtnVariant: 'link',
confirmBtnVariant: 'danger',
confirmText: t('delete', { keyPrefix: 'btns' }),
onConfirm: () => {
changeQuestionStatus(itemData.id, 'deleted').then(() => {
refreshList();
});
},
});
}
if (type === 'undelete') {
Modal.confirm({
title: t('undelete_title'),
content: t('undelete_desc'),
cancelBtnVariant: 'link',
confirmBtnVariant: 'danger',
confirmText: t('undelete', { keyPrefix: 'btns' }),
onConfirm: () => {
changeQuestionStatus(itemData.id, 'available').then(() => {
refreshList();
});
},
});
}
if (type === 'close') {
closeModal.onShow({
type: 'question',
id: itemData.id,
action: 'close',
});
}
if (type === 'reopen') {
Modal.confirm({
title: t('title', { keyPrefix: 'question_detail.reopen' }),
content: t('content', { keyPrefix: 'question_detail.reopen' }),
cancelBtnVariant: 'link',
confirmText: t('confirm_btn', { keyPrefix: 'question_detail.reopen' }),
onConfirm: () => {
reopenQuestion({
question_id: itemData.id,
}).then(() => {
toast.onShow({
msg: t('post_reopen', { keyPrefix: 'messages' }),
variant: 'success',
});
refreshList();
});
},
});
}
};
return (
<Dropdown>
<Dropdown.Toggle variant="link" className="no-toggle p-0">
<Icon
name="three-dots-vertical"
title={t('action', { keyPrefix: 'admin.answers' })}
/>
</Dropdown.Toggle>
<Dropdown.Menu>
{curFilter === 'normal' && (
<Dropdown.Item onClick={() => handleAction('close')}>
{t('close', { keyPrefix: 'btns' })}
</Dropdown.Item>
)}
{curFilter === 'closed' && (
<Dropdown.Item onClick={() => handleAction('reopen')}>
{t('reopen', { keyPrefix: 'btns' })}
</Dropdown.Item>
)}
{curFilter !== 'deleted' ? (
<Dropdown.Item onClick={() => handleAction('delete')}>
{t('delete', { keyPrefix: 'btns' })}
</Dropdown.Item>
) : (
<Dropdown.Item onClick={() => handleAction('undelete')}>
{t('undelete', { keyPrefix: 'btns' })}
</Dropdown.Item>
)}
</Dropdown.Menu>
</Dropdown>
);
};
export default AnswerActions;

View File

@ -1,5 +1,5 @@
import { FC } from 'react'; import { FC } from 'react';
import { Button, Form, Table, Stack } from 'react-bootstrap'; import { Form, Table, Stack } from 'react-bootstrap';
import { Link, useSearchParams } from 'react-router-dom'; import { Link, useSearchParams } from 'react-router-dom';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
@ -9,17 +9,17 @@ import {
FormatTime, FormatTime,
Icon, Icon,
Pagination, Pagination,
Modal,
BaseUserCard, BaseUserCard,
Empty, Empty,
QueryGroup, QueryGroup,
} from '@/components'; } from '@/components';
import { ADMIN_LIST_STATUS } from '@/common/constants'; import { ADMIN_LIST_STATUS } from '@/common/constants';
import { useEditStatusModal, useReportModal } from '@/hooks';
import * as Type from '@/common/interface'; import * as Type from '@/common/interface';
import { useQuestionSearch, changeQuestionStatus } from '@/services'; import { useQuestionSearch } from '@/services';
import { pathFactory } from '@/router/pathFactory'; import { pathFactory } from '@/router/pathFactory';
import Action from './components/Action';
const questionFilterItems: Type.AdminContentsFilterBy[] = [ const questionFilterItems: Type.AdminContentsFilterBy[] = [
'normal', 'normal',
'closed', 'closed',
@ -46,53 +46,6 @@ const Questions: FC = () => {
}); });
const count = listData?.count || 0; const count = listData?.count || 0;
const closeModal = useReportModal(refreshList);
const handleCallback = (id, type) => {
if (type === 'normal') {
changeQuestionStatus(id, 'available').then(() => {
refreshList();
});
}
if (type === 'closed') {
closeModal.onShow({
type: 'question',
id,
action: 'close',
});
}
if (type === 'deleted') {
const item = listData?.list?.filter((v) => v.id === id)?.[0];
Modal.confirm({
title: t('title', { keyPrefix: 'delete' }),
content:
item.answer_count > 0
? t('question', { keyPrefix: 'delete' })
: t('other', { keyPrefix: 'delete' }),
cancelBtnVariant: 'link',
confirmBtnVariant: 'danger',
confirmText: t('delete', { keyPrefix: 'btns' }),
onConfirm: () => {
changeQuestionStatus(id, 'deleted').then(() => {
refreshList();
});
},
});
}
};
const changeModal = useEditStatusModal({
editType: 'question',
callback: handleCallback,
});
const handleChange = (itemId) => {
changeModal.onShow({
id: itemId,
type: curFilter,
});
};
const handleFilter = (e) => { const handleFilter = (e) => {
urlSearchParams.set('query', e.target.value); urlSearchParams.set('query', e.target.value);
urlSearchParams.delete('page'); urlSearchParams.delete('page');
@ -126,9 +79,9 @@ const Questions: FC = () => {
<th style={{ width: '8%' }}>{t('answers')}</th> <th style={{ width: '8%' }}>{t('answers')}</th>
<th style={{ width: '20%' }}>{t('created')}</th> <th style={{ width: '20%' }}>{t('created')}</th>
<th style={{ width: '9%' }}>{t('status')}</th> <th style={{ width: '9%' }}>{t('status')}</th>
{curFilter !== 'deleted' && ( <th style={{ width: '10%' }} className="text-end">
<th style={{ width: '10%' }}>{t('action')}</th> {t('action')}
)} </th>
</tr> </tr>
</thead> </thead>
<tbody className="align-middle"> <tbody className="align-middle">
@ -176,16 +129,14 @@ const Questions: FC = () => {
{t(ADMIN_LIST_STATUS[curFilter]?.name)} {t(ADMIN_LIST_STATUS[curFilter]?.name)}
</span> </span>
</td> </td>
{curFilter !== 'deleted' && (
<td> <td className="text-end">
<Button <Action
variant="link" itemData={{ id: li.id, answer_count: li.answer_count }}
className="p-0 btn-no-border" refreshList={refreshList}
onClick={() => handleChange(li.id)}> curFilter={curFilter}
{t('change')} />
</Button> </td>
</td>
)}
</tr> </tr>
); );
})} })}

View File

@ -138,7 +138,7 @@ const UserOperation = ({
return ( return (
<td className="text-end"> <td className="text-end">
<Dropdown> <Dropdown>
<Dropdown.Toggle variant="link" className="no-toggle"> <Dropdown.Toggle variant="link" className="no-toggle p-0">
<Icon name="three-dots-vertical" title={t('action')} /> <Icon name="three-dots-vertical" title={t('action')} />
</Dropdown.Toggle> </Dropdown.Toggle>
<Dropdown.Menu> <Dropdown.Menu>

View File

@ -25,7 +25,7 @@ import { loggedUserInfoStore, userCenterStore } from '@/stores';
import { formatCount } from '@/utils'; import { formatCount } from '@/utils';
import DeleteUserModal from './components/DeleteUserModal'; import DeleteUserModal from './components/DeleteUserModal';
import UserOperate from './components/UserOperate'; import Action from './components/Action';
const UserFilterKeys: Type.UserFilterBy[] = [ const UserFilterKeys: Type.UserFilterBy[] = [
'all', 'all',
@ -243,7 +243,7 @@ const Users: FC = () => {
)} )}
{curFilter !== 'deleted' && {curFilter !== 'deleted' &&
(showAction || user.status === 'inactive') ? ( (showAction || user.status === 'inactive') ? (
<UserOperate <Action
userData={user} userData={user}
showActionPassword={showActionPassword} showActionPassword={showActionPassword}
showActionRole={showActionRole} showActionRole={showActionRole}