diff --git a/client/src/actions/entry/user.js b/client/src/actions/entry/user.js index 8d157958..32cf63cd 100755 --- a/client/src/actions/entry/user.js +++ b/client/src/actions/entry/user.js @@ -51,6 +51,18 @@ export const clearCurrentUserPasswordUpdateError = () => ({ payload: {}, }); +export const updateCurrentUserUsername = (data) => ({ + type: EntryActionTypes.CURRENT_USER_USERNAME_UPDATE, + payload: { + data, + }, +}); + +export const clearCurrentUserUsernameUpdateError = () => ({ + type: EntryActionTypes.CURRENT_USER_USERNAME_UPDATE_ERROR_CLEAR, + payload: {}, +}); + export const uploadCurrentUserAvatar = (file) => ({ type: EntryActionTypes.CURRENT_USER_AVATAR_UPLOAD, payload: { diff --git a/client/src/actions/user.js b/client/src/actions/user.js index c00ccb27..5464795c 100644 --- a/client/src/actions/user.js +++ b/client/src/actions/user.js @@ -36,6 +36,13 @@ export const clearUserPasswordUpdateError = (id) => ({ }, }); +export const clearUserUsernameUpdateError = (id) => ({ + type: ActionTypes.USER_USERNAME_UPDATE_ERROR_CLEAR, + payload: { + id, + }, +}); + export const deleteUser = (id) => ({ type: ActionTypes.USER_DELETE, payload: { @@ -202,6 +209,30 @@ export const updateUserPasswordFailed = (id, error) => ({ }, }); +export const updateUserUsernameRequested = (id, data) => ({ + type: ActionTypes.USER_USERNAME_UPDATE_REQUESTED, + payload: { + id, + data, + }, +}); + +export const updateUserUsernameSucceeded = (id, username) => ({ + type: ActionTypes.USER_USERNAME_UPDATE_SUCCEEDED, + payload: { + id, + username, + }, +}); + +export const updateUserUsernameFailed = (id, error) => ({ + type: ActionTypes.USER_USERNAME_UPDATE_FAILED, + payload: { + id, + error, + }, +}); + export const uploadUserAvatarRequested = (id) => ({ type: ActionTypes.USER_AVATAR_UPLOAD_REQUESTED, payload: { diff --git a/client/src/api/users.js b/client/src/api/users.js index 4851a004..2c28c976 100755 --- a/client/src/api/users.js +++ b/client/src/api/users.js @@ -16,6 +16,9 @@ const updateUserEmail = (id, data, headers) => socket.patch(`/users/${id}/email` const updateUserPassword = (id, data, headers) => socket.patch(`/users/${id}/password`, data, headers); +const updateUserUsername = (id, data, headers) => + socket.patch(`/users/${id}/username`, data, headers); + const uploadUserAvatar = (id, file, headers) => http.post( `/users/${id}/upload-avatar`, @@ -34,6 +37,7 @@ export default { updateUser, updateUserEmail, updateUserPassword, + updateUserUsername, uploadUserAvatar, deleteUser, }; diff --git a/client/src/components/AddUserPopup/AddUserPopup.jsx b/client/src/components/AddUserPopup/AddUserPopup.jsx index c6b202ca..2c3dc7f5 100755 --- a/client/src/components/AddUserPopup/AddUserPopup.jsx +++ b/client/src/components/AddUserPopup/AddUserPopup.jsx @@ -8,6 +8,7 @@ import { withPopup } from '../../lib/popup'; import { Input, Popup } from '../../lib/custom-ui'; import { useForm } from '../../hooks'; +import { isUsername } from '../../utils/validator'; import styles from './AddUserPopup.module.css'; @@ -16,17 +17,23 @@ const createMessage = (error) => { return error; } - if (error.message === 'User is already exist') { - return { - type: 'error', - content: 'common.userIsAlreadyExist', - }; + switch (error.message) { + case 'Email already in use': + return { + type: 'error', + content: 'common.emailAlreadyInUse', + }; + case 'Username already in use': + return { + type: 'error', + content: 'common.usernameAlreadyInUse', + }; + default: + return { + type: 'warning', + content: 'common.unknownError', + }; } - - return { - type: 'warning', - content: 'common.unknownError', - }; }; const AddUserPopup = React.memo( @@ -38,6 +45,7 @@ const AddUserPopup = React.memo( email: '', password: '', name: '', + username: '', ...defaultData, })); @@ -46,12 +54,14 @@ const AddUserPopup = React.memo( const emailField = useRef(null); const passwordField = useRef(null); const nameField = useRef(null); + const usernameField = useRef(null); const handleSubmit = useCallback(() => { const cleanData = { ...data, email: data.email.trim(), name: data.name.trim(), + username: data.username.trim() || null, }; if (!isEmail(cleanData.email)) { @@ -69,6 +79,11 @@ const AddUserPopup = React.memo( return; } + if (cleanData.username && !isUsername(cleanData.username)) { + usernameField.current.select(); + return; + } + onCreate(cleanData); }, [onCreate, data]); @@ -78,10 +93,20 @@ const AddUserPopup = React.memo( useEffect(() => { if (wasSubmitting && !isSubmitting) { - if (!error) { + if (error) { + switch (error.message) { + case 'Email already in use': + emailField.current.select(); + + break; + case 'Username already in use': + usernameField.current.select(); + + break; + default: + } + } else { onClose(); - } else if (error.message === 'User is already exist') { - emailField.current.select(); } } }, [isSubmitting, wasSubmitting, error, onClose]); @@ -136,6 +161,22 @@ const AddUserPopup = React.memo( className={styles.field} onChange={handleFieldChange} /> +
+ {t('common.username')} ( + {t('common.optional', { + context: 'inline', + })} + ) +
+