mirror of https://gitee.com/answerdev/answer.git
commit
d02c0a9a20
|
@ -842,6 +842,9 @@ ui:
|
|||
change_btn_name: Change email
|
||||
msg:
|
||||
empty: Cannot be empty.
|
||||
resend_email:
|
||||
url_label: Are you sure you want to resend the activation email?
|
||||
url_text: You can also give the activation link above to the user.
|
||||
login:
|
||||
login_to_continue: Log in to continue
|
||||
info_sign: Don't have an account? <1>Sign up</1>
|
||||
|
@ -1000,6 +1003,7 @@ ui:
|
|||
flag_success: Thanks for flagging.
|
||||
forbidden_operate_self: Forbidden to operate on yourself
|
||||
review: Your revision will show after review.
|
||||
sent_success: Sent successfully
|
||||
related_question:
|
||||
title: Related Questions
|
||||
answers: answers
|
||||
|
@ -1094,6 +1098,7 @@ ui:
|
|||
answer: Answer
|
||||
comment: Comment
|
||||
refresh: Refresh
|
||||
resend: Resend
|
||||
search:
|
||||
title: Search Results
|
||||
keywords: Keywords
|
||||
|
@ -1405,6 +1410,31 @@ ui:
|
|||
title: Change user role to...
|
||||
btn_cancel: Cancel
|
||||
btn_submit: Submit
|
||||
new_password_modal:
|
||||
title: Set new password
|
||||
form:
|
||||
fields:
|
||||
password:
|
||||
label: Password
|
||||
text: The user will be logged out and need to login again.
|
||||
msg: Password must be at 8-32 characters in length.
|
||||
btn_cancel: Cancel
|
||||
btn_submit: Submit
|
||||
user_modal:
|
||||
title: Add new user
|
||||
form:
|
||||
fields:
|
||||
display_name:
|
||||
label: Display Name
|
||||
msg: Display Name must be at 3-30 characters in length.
|
||||
email:
|
||||
label: Email
|
||||
msg: Email is not valid.
|
||||
password:
|
||||
label: Password
|
||||
msg: Password must be at 8-32 characters in length.
|
||||
btn_cancel: Cancel
|
||||
btn_submit: Submit
|
||||
users:
|
||||
title: Users
|
||||
name: Name
|
||||
|
@ -1434,33 +1464,6 @@ ui:
|
|||
change_role: Change role
|
||||
show_logs: Show logs
|
||||
add_user: Add user
|
||||
new_password_modal:
|
||||
title: Set new password
|
||||
form:
|
||||
fields:
|
||||
password:
|
||||
label: Password
|
||||
text: The user will be logged out and need to login again.
|
||||
msg: Password must be at 8-32 characters in length.
|
||||
btn_cancel: Cancel
|
||||
btn_submit: Submit
|
||||
user_modal:
|
||||
title: Add new user
|
||||
form:
|
||||
fields:
|
||||
display_name:
|
||||
label: Display Name
|
||||
msg: Display Name must be at 3-30 characters in length.
|
||||
email:
|
||||
label: Email
|
||||
msg: Email is not valid.
|
||||
password:
|
||||
label: Password
|
||||
msg: Password must be at 8-32 characters in length.
|
||||
|
||||
btn_cancel: Cancel
|
||||
btn_submit: Submit
|
||||
|
||||
questions:
|
||||
page_title: Questions
|
||||
normal: Normal
|
||||
|
|
|
@ -10,6 +10,7 @@ import useChangePasswordModal from './useChangePasswordModal';
|
|||
import usePageTags from './usePageTags';
|
||||
import useLoginRedirect from './useLoginRedirect';
|
||||
import usePromptWithUnload from './usePrompt';
|
||||
import useActivationEmailModal from './useActivationEmailModal';
|
||||
|
||||
export {
|
||||
useTagModal,
|
||||
|
@ -24,4 +25,5 @@ export {
|
|||
usePageTags,
|
||||
useLoginRedirect,
|
||||
usePromptWithUnload,
|
||||
useActivationEmailModal,
|
||||
};
|
||||
|
|
|
@ -0,0 +1,149 @@
|
|||
import { useLayoutEffect, useState, useRef } from 'react';
|
||||
import { Modal, Button } from 'react-bootstrap';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import ReactDOM from 'react-dom/client';
|
||||
|
||||
import type * as Type from '@/common/interface';
|
||||
import { SchemaForm, JSONSchema, UISchema, initFormData } from '@/components';
|
||||
import { handleFormError } from '@/utils';
|
||||
import { getUserActivation, postUserActivation } from '@/services';
|
||||
import { useToast } from '@/hooks';
|
||||
|
||||
const div = document.createElement('div');
|
||||
const root = ReactDOM.createRoot(div);
|
||||
|
||||
interface IProps {
|
||||
title?: string;
|
||||
onConfirm?: (formData: any) => Promise<any>;
|
||||
}
|
||||
const useChangePasswordModal = (props: IProps = {}) => {
|
||||
const { t } = useTranslation('translation', {
|
||||
keyPrefix: 'inactive',
|
||||
});
|
||||
|
||||
const { title = t('btn_name') } = props;
|
||||
const [visible, setVisibleState] = useState(false);
|
||||
const userId = useRef('');
|
||||
const isLoading = useRef(false);
|
||||
const Toast = useToast();
|
||||
|
||||
const schema: JSONSchema = {
|
||||
title: t('btn_name'),
|
||||
properties: {
|
||||
activationUrl: {
|
||||
type: 'string',
|
||||
title: t('resend_email.url_label'),
|
||||
description: t('resend_email.url_text'),
|
||||
},
|
||||
},
|
||||
};
|
||||
const uiSchema: UISchema = {
|
||||
activationUrl: {
|
||||
'ui:options': {
|
||||
readOnly: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
const [formData, setFormData] = useState<Type.FormDataType>(
|
||||
initFormData(schema),
|
||||
);
|
||||
|
||||
const formRef = useRef<{
|
||||
validator: () => Promise<boolean>;
|
||||
}>(null);
|
||||
|
||||
const getActivationUrl = () => {
|
||||
return getUserActivation(userId.current).then((resp) => {
|
||||
if (resp?.activation_url) {
|
||||
setFormData({
|
||||
...formData,
|
||||
activationUrl: {
|
||||
value: resp.activation_url,
|
||||
isInvalid: false,
|
||||
errorMsg: '',
|
||||
},
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const onClose = () => {
|
||||
setVisibleState(false);
|
||||
userId.current = '';
|
||||
setFormData(initFormData(schema));
|
||||
};
|
||||
|
||||
const onShow = async (user_id: string) => {
|
||||
if (!user_id) {
|
||||
return;
|
||||
}
|
||||
userId.current = user_id;
|
||||
await getActivationUrl();
|
||||
setVisibleState(true);
|
||||
};
|
||||
|
||||
const handleSubmit = async (event) => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
isLoading.current = true;
|
||||
postUserActivation(userId.current)
|
||||
.then(() => {
|
||||
Toast.onShow({
|
||||
msg: t('sent_success', { keyPrefix: 'toast' }),
|
||||
variant: 'success',
|
||||
});
|
||||
onClose();
|
||||
})
|
||||
.catch((err) => {
|
||||
if (err.isError) {
|
||||
const data = handleFormError(err, formData);
|
||||
setFormData({ ...data });
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
isLoading.current = false;
|
||||
});
|
||||
};
|
||||
|
||||
const handleOnChange = (data) => {
|
||||
setFormData(data);
|
||||
};
|
||||
|
||||
useLayoutEffect(() => {
|
||||
root.render(
|
||||
<Modal show={visible} title={title} onHide={onClose}>
|
||||
<Modal.Header closeButton>
|
||||
<Modal.Title as="h5">{title}</Modal.Title>
|
||||
</Modal.Header>
|
||||
<Modal.Body>
|
||||
<SchemaForm
|
||||
ref={formRef}
|
||||
schema={schema}
|
||||
uiSchema={uiSchema}
|
||||
formData={formData}
|
||||
onChange={handleOnChange}
|
||||
hiddenSubmit
|
||||
/>
|
||||
</Modal.Body>
|
||||
<Modal.Footer>
|
||||
<Button variant="link" onClick={() => onClose()}>
|
||||
{t('cancel', { keyPrefix: 'btns' })}
|
||||
</Button>
|
||||
<Button
|
||||
disabled={isLoading.current}
|
||||
variant="primary"
|
||||
onClick={handleSubmit}>
|
||||
{t('resend', { keyPrefix: 'btns' })}
|
||||
</Button>
|
||||
</Modal.Footer>
|
||||
</Modal>,
|
||||
);
|
||||
});
|
||||
return {
|
||||
onClose,
|
||||
onShow,
|
||||
};
|
||||
};
|
||||
|
||||
export default useChangePasswordModal;
|
|
@ -17,7 +17,7 @@ interface IProps {
|
|||
}
|
||||
const useChangePasswordModal = (props: IProps = {}) => {
|
||||
const { t } = useTranslation('translation', {
|
||||
keyPrefix: 'admin.users.new_password_modal',
|
||||
keyPrefix: 'admin.new_password_modal',
|
||||
});
|
||||
|
||||
const { title = t('title'), onConfirm } = props;
|
||||
|
|
|
@ -18,7 +18,7 @@ interface IProps {
|
|||
}
|
||||
const useAddUserModal = (props: IProps = {}) => {
|
||||
const { t } = useTranslation('translation', {
|
||||
keyPrefix: 'admin.users.user_modal',
|
||||
keyPrefix: 'admin.user_modal',
|
||||
});
|
||||
|
||||
const { title = t('title'), onConfirm } = props;
|
||||
|
|
|
@ -19,6 +19,7 @@ import {
|
|||
useChangeModal,
|
||||
useChangeUserRoleModal,
|
||||
useChangePasswordModal,
|
||||
useActivationEmailModal,
|
||||
useToast,
|
||||
} from '@/hooks';
|
||||
import {
|
||||
|
@ -119,6 +120,8 @@ const Users: FC = () => {
|
|||
},
|
||||
});
|
||||
|
||||
const activationEmailModal = useActivationEmailModal();
|
||||
|
||||
const handleAction = (type, user) => {
|
||||
const { user_id, status, role_id, username } = user;
|
||||
if (username === currentUser.username) {
|
||||
|
@ -128,6 +131,7 @@ const Users: FC = () => {
|
|||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (type === 'status') {
|
||||
changeModal.onShow({
|
||||
id: user_id,
|
||||
|
@ -141,9 +145,14 @@ const Users: FC = () => {
|
|||
role_id,
|
||||
});
|
||||
}
|
||||
|
||||
if (type === 'password') {
|
||||
changePasswordModal.onShow(user_id);
|
||||
}
|
||||
|
||||
if (type === 'activation') {
|
||||
activationEmailModal.onShow(user_id);
|
||||
}
|
||||
};
|
||||
|
||||
const handleFilter = (e) => {
|
||||
|
@ -160,16 +169,21 @@ const Users: FC = () => {
|
|||
}, [ucAgent]);
|
||||
const showAddUser =
|
||||
!ucAgent?.enabled || (ucAgent?.enabled && adminUcAgent?.allow_create_user);
|
||||
|
||||
const showActionPassword =
|
||||
!ucAgent?.enabled ||
|
||||
(ucAgent?.enabled && adminUcAgent?.allow_update_user_password);
|
||||
|
||||
const showActionRole =
|
||||
!ucAgent?.enabled ||
|
||||
(ucAgent?.enabled && adminUcAgent?.allow_update_user_role);
|
||||
|
||||
const showActionStatus =
|
||||
!ucAgent?.enabled ||
|
||||
(ucAgent?.enabled && adminUcAgent?.allow_update_user_status);
|
||||
|
||||
const showAction = showActionPassword || showActionRole || showActionStatus;
|
||||
|
||||
return (
|
||||
<>
|
||||
<h3 className="mb-4">{t('title')}</h3>
|
||||
|
@ -228,6 +242,8 @@ const Users: FC = () => {
|
|||
</thead>
|
||||
<tbody className="align-middle">
|
||||
{data?.list.map((user) => {
|
||||
const showActionActivation = user.status === 'inactive';
|
||||
|
||||
return (
|
||||
<tr key={user.user_id}>
|
||||
<td>
|
||||
|
@ -267,7 +283,8 @@ const Users: FC = () => {
|
|||
</span>
|
||||
</td>
|
||||
)}
|
||||
{curFilter !== 'deleted' && showAction ? (
|
||||
{curFilter !== 'deleted' &&
|
||||
(showAction || showActionActivation) ? (
|
||||
<td className="text-end">
|
||||
<Dropdown>
|
||||
<Dropdown.Toggle variant="link" className="no-toggle">
|
||||
|
@ -292,6 +309,12 @@ const Users: FC = () => {
|
|||
{t('change_role')}
|
||||
</Dropdown.Item>
|
||||
) : null}
|
||||
{showActionActivation ? (
|
||||
<Dropdown.Item
|
||||
onClick={() => handleAction('activation', user)}>
|
||||
{t('btn_name', { keyPrefix: 'inactive' })}
|
||||
</Dropdown.Item>
|
||||
) : null}
|
||||
</Dropdown.Menu>
|
||||
</Dropdown>
|
||||
</td>
|
||||
|
|
|
@ -44,3 +44,21 @@ export const updateUserPassword = (params: {
|
|||
}) => {
|
||||
return request.put('/answer/admin/api/user/password', params);
|
||||
};
|
||||
|
||||
export const getUserActivation = (userId: string) => {
|
||||
const apiUrl = `/answer/admin/api/user/activation`;
|
||||
return request.get<{
|
||||
activation_url: string;
|
||||
}>(apiUrl, {
|
||||
params: {
|
||||
user_id: userId,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export const postUserActivation = (userId: string) => {
|
||||
const apiUrl = `/answer/admin/api/user/activation`;
|
||||
return request.post(apiUrl, {
|
||||
user_id: userId,
|
||||
});
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue