mirror of https://gitee.com/answerdev/answer.git
fix: change admin questions and answers operate
This commit is contained in:
parent
a4adf7f40d
commit
0269c30516
|
@ -1097,12 +1097,15 @@ ui:
|
|||
Are you sure you wish to delete?
|
||||
other: Are you sure you wish to delete?
|
||||
tip_answer_deleted: This answer has been deleted
|
||||
undelete_title: Undelete this post
|
||||
undelete_desc: Are you sure you wish to undelete?
|
||||
btns:
|
||||
confirm: Confirm
|
||||
cancel: Cancel
|
||||
edit: Edit
|
||||
save: Save
|
||||
delete: Delete
|
||||
undelete: Undelete
|
||||
login: Log in
|
||||
signup: Sign up
|
||||
logout: Log out
|
||||
|
@ -1123,6 +1126,8 @@ ui:
|
|||
active: Active
|
||||
suspend: Suspend
|
||||
unsuspend: Unsuspend
|
||||
close: Close
|
||||
reopen: Reopen
|
||||
search:
|
||||
title: Search Results
|
||||
keywords: Keywords
|
||||
|
@ -1411,34 +1416,6 @@ ui:
|
|||
created: Created
|
||||
action: Action
|
||||
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:
|
||||
title: Change user role to...
|
||||
btn_cancel: Cancel
|
||||
|
|
|
@ -2,8 +2,6 @@ import useTagModal from './useTagModal';
|
|||
import useToast from './useToast';
|
||||
import useReportModal from './useReportModal';
|
||||
import usePageUsers from './usePageUsers';
|
||||
import useChangeModal from './useChangeModal';
|
||||
import useEditStatusModal from './useEditStatusModal';
|
||||
import useChangeUserRoleModal from './useChangeUserRoleModal';
|
||||
import useUserModal from './useUserModal';
|
||||
import useChangePasswordModal from './useChangePasswordModal';
|
||||
|
@ -18,8 +16,6 @@ export {
|
|||
useToast,
|
||||
useReportModal,
|
||||
usePageUsers,
|
||||
useChangeModal,
|
||||
useEditStatusModal,
|
||||
useChangeUserRoleModal,
|
||||
useUserModal,
|
||||
useChangePasswordModal,
|
||||
|
|
|
@ -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;
|
|
@ -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;
|
|
@ -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;
|
|
@ -1,5 +1,5 @@
|
|||
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 { useTranslation } from 'react-i18next';
|
||||
|
||||
|
@ -9,18 +9,18 @@ import {
|
|||
FormatTime,
|
||||
Icon,
|
||||
Pagination,
|
||||
Modal,
|
||||
BaseUserCard,
|
||||
Empty,
|
||||
QueryGroup,
|
||||
} from '@/components';
|
||||
import { ADMIN_LIST_STATUS } from '@/common/constants';
|
||||
import { useEditStatusModal } from '@/hooks';
|
||||
import * as Type from '@/common/interface';
|
||||
import { useAnswerSearch, changeAnswerStatus } from '@/services';
|
||||
import { useAnswerSearch } from '@/services';
|
||||
import { escapeRemove } from '@/utils';
|
||||
import { pathFactory } from '@/router/pathFactory';
|
||||
|
||||
import AnswerAction from './components/Action';
|
||||
|
||||
const answerFilterItems: Type.AdminContentsFilterBy[] = ['normal', 'deleted'];
|
||||
|
||||
const Answers: FC = () => {
|
||||
|
@ -45,44 +45,6 @@ const Answers: FC = () => {
|
|||
});
|
||||
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) => {
|
||||
urlSearchParams.set('query', e.target.value);
|
||||
urlSearchParams.delete('page');
|
||||
|
@ -115,9 +77,9 @@ const Answers: FC = () => {
|
|||
<th style={{ width: '11%' }}>{t('votes')}</th>
|
||||
<th style={{ width: '14%' }}>{t('created')}</th>
|
||||
<th style={{ width: '11%' }}>{t('status')}</th>
|
||||
{curFilter !== 'deleted' && (
|
||||
<th style={{ width: '11%' }}>{t('action')}</th>
|
||||
)}
|
||||
<th style={{ width: '11%' }} className="text-end">
|
||||
{t('action')}
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="align-middle">
|
||||
|
@ -172,16 +134,13 @@ const Answers: FC = () => {
|
|||
{t(ADMIN_LIST_STATUS[curFilter]?.name)}
|
||||
</span>
|
||||
</td>
|
||||
{curFilter !== 'deleted' && (
|
||||
<td>
|
||||
<Button
|
||||
variant="link"
|
||||
className="p-0 btn-no-border"
|
||||
onClick={() => handleChange(li.id)}>
|
||||
{t('change')}
|
||||
</Button>
|
||||
</td>
|
||||
)}
|
||||
<td className="text-end">
|
||||
<AnswerAction
|
||||
itemData={{ id: li.id, accepted: li.accepted }}
|
||||
curFilter={curFilter}
|
||||
refreshList={refreshList}
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
})}
|
||||
|
|
|
@ -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;
|
|
@ -1,5 +1,5 @@
|
|||
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 { useTranslation } from 'react-i18next';
|
||||
|
||||
|
@ -9,17 +9,17 @@ import {
|
|||
FormatTime,
|
||||
Icon,
|
||||
Pagination,
|
||||
Modal,
|
||||
BaseUserCard,
|
||||
Empty,
|
||||
QueryGroup,
|
||||
} from '@/components';
|
||||
import { ADMIN_LIST_STATUS } from '@/common/constants';
|
||||
import { useEditStatusModal, useReportModal } from '@/hooks';
|
||||
import * as Type from '@/common/interface';
|
||||
import { useQuestionSearch, changeQuestionStatus } from '@/services';
|
||||
import { useQuestionSearch } from '@/services';
|
||||
import { pathFactory } from '@/router/pathFactory';
|
||||
|
||||
import Action from './components/Action';
|
||||
|
||||
const questionFilterItems: Type.AdminContentsFilterBy[] = [
|
||||
'normal',
|
||||
'closed',
|
||||
|
@ -46,53 +46,6 @@ const Questions: FC = () => {
|
|||
});
|
||||
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) => {
|
||||
urlSearchParams.set('query', e.target.value);
|
||||
urlSearchParams.delete('page');
|
||||
|
@ -126,9 +79,9 @@ const Questions: FC = () => {
|
|||
<th style={{ width: '8%' }}>{t('answers')}</th>
|
||||
<th style={{ width: '20%' }}>{t('created')}</th>
|
||||
<th style={{ width: '9%' }}>{t('status')}</th>
|
||||
{curFilter !== 'deleted' && (
|
||||
<th style={{ width: '10%' }}>{t('action')}</th>
|
||||
)}
|
||||
<th style={{ width: '10%' }} className="text-end">
|
||||
{t('action')}
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="align-middle">
|
||||
|
@ -176,16 +129,14 @@ const Questions: FC = () => {
|
|||
{t(ADMIN_LIST_STATUS[curFilter]?.name)}
|
||||
</span>
|
||||
</td>
|
||||
{curFilter !== 'deleted' && (
|
||||
<td>
|
||||
<Button
|
||||
variant="link"
|
||||
className="p-0 btn-no-border"
|
||||
onClick={() => handleChange(li.id)}>
|
||||
{t('change')}
|
||||
</Button>
|
||||
</td>
|
||||
)}
|
||||
|
||||
<td className="text-end">
|
||||
<Action
|
||||
itemData={{ id: li.id, answer_count: li.answer_count }}
|
||||
refreshList={refreshList}
|
||||
curFilter={curFilter}
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
})}
|
||||
|
|
|
@ -138,7 +138,7 @@ const UserOperation = ({
|
|||
return (
|
||||
<td className="text-end">
|
||||
<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')} />
|
||||
</Dropdown.Toggle>
|
||||
<Dropdown.Menu>
|
|
@ -25,7 +25,7 @@ import { loggedUserInfoStore, userCenterStore } from '@/stores';
|
|||
import { formatCount } from '@/utils';
|
||||
|
||||
import DeleteUserModal from './components/DeleteUserModal';
|
||||
import UserOperate from './components/UserOperate';
|
||||
import Action from './components/Action';
|
||||
|
||||
const UserFilterKeys: Type.UserFilterBy[] = [
|
||||
'all',
|
||||
|
@ -243,7 +243,7 @@ const Users: FC = () => {
|
|||
)}
|
||||
{curFilter !== 'deleted' &&
|
||||
(showAction || user.status === 'inactive') ? (
|
||||
<UserOperate
|
||||
<Action
|
||||
userData={user}
|
||||
showActionPassword={showActionPassword}
|
||||
showActionRole={showActionRole}
|
||||
|
|
Loading…
Reference in New Issue