From af00e3e191bbd03fd124b72f1c2ee401719e1b5f Mon Sep 17 00:00:00 2001 From: Maksim Eltyshev Date: Fri, 3 Apr 2020 00:35:25 +0500 Subject: [PATCH] Add username to user --- client/src/actions/entry/user.js | 12 ++ client/src/actions/user.js | 31 +++ client/src/api/users.js | 4 + .../components/AddUserPopup/AddUserPopup.jsx | 67 +++++-- client/src/components/Header/Header.jsx | 8 + client/src/components/Login/Login.jsx | 33 ++-- .../components/UserPopup/EditEmailStep.jsx | 10 +- .../components/UserPopup/EditPasswordStep.jsx | 4 +- .../components/UserPopup/EditUsernameStep.jsx | 182 ++++++++++++++++++ .../UserPopup/EditUsernameStep.module.css | 10 + client/src/components/UserPopup/UserPopup.jsx | 39 +++- client/src/components/UsersModal/Item.jsx | 8 +- .../src/components/UsersModal/UsersModal.jsx | 6 +- client/src/constants/ActionTypes.js | 4 + client/src/constants/EntryActionTypes.js | 2 + client/src/containers/HeaderContainer.js | 4 + client/src/locales/en-US/app.js | 10 +- client/src/locales/en-US/embed.js | 4 +- client/src/locales/ru-RU/app.js | 10 +- client/src/locales/ru-RU/embed.js | 4 +- client/src/models/User.js | 58 ++++++ client/src/reducers/forms/authenticate.js | 2 +- client/src/reducers/forms/user-create.js | 1 + client/src/sagas/app/requests/user.js | 27 +++ client/src/sagas/app/services/user.js | 22 +++ client/src/sagas/app/watchers/user.js | 8 + client/src/utils/validator.js | 6 + .../api/controllers/access-tokens/create.js | 29 +-- server/api/controllers/actions/index.js | 6 +- server/api/controllers/boards/create.js | 4 +- server/api/controllers/boards/delete.js | 4 +- server/api/controllers/boards/show.js | 8 +- server/api/controllers/boards/update.js | 4 +- server/api/controllers/card-labels/create.js | 19 +- server/api/controllers/card-labels/delete.js | 17 +- .../controllers/card-memberships/create.js | 19 +- .../controllers/card-memberships/delete.js | 17 +- server/api/controllers/cards/create.js | 10 +- server/api/controllers/cards/delete.js | 6 +- server/api/controllers/cards/show.js | 6 +- server/api/controllers/cards/update.js | 15 +- .../api/controllers/comment-actions/create.js | 6 +- .../api/controllers/comment-actions/delete.js | 6 +- .../api/controllers/comment-actions/update.js | 6 +- server/api/controllers/labels/create.js | 6 +- server/api/controllers/labels/delete.js | 6 +- server/api/controllers/labels/update.js | 6 +- server/api/controllers/lists/create.js | 6 +- server/api/controllers/lists/delete.js | 6 +- server/api/controllers/lists/update.js | 6 +- .../api/controllers/notifications/update.js | 6 - .../controllers/project-memberships/create.js | 17 +- .../controllers/project-memberships/delete.js | 4 +- server/api/controllers/projects/delete.js | 4 +- server/api/controllers/projects/update.js | 4 +- server/api/controllers/tasks/create.js | 6 +- server/api/controllers/tasks/delete.js | 6 +- server/api/controllers/tasks/update.js | 6 +- server/api/controllers/users/create.js | 25 ++- server/api/controllers/users/delete.js | 4 +- server/api/controllers/users/update-email.js | 22 +-- .../api/controllers/users/update-password.js | 14 +- .../api/controllers/users/update-username.js | 85 ++++++++ server/api/controllers/users/update.js | 6 +- server/api/controllers/users/upload-avatar.js | 14 +- server/api/helpers/create-action.js | 2 +- server/api/helpers/create-board.js | 6 +- server/api/helpers/create-card-label.js | 6 +- server/api/helpers/create-card-membership.js | 8 +- server/api/helpers/create-card.js | 2 +- server/api/helpers/create-list.js | 2 +- .../api/helpers/create-project-membership.js | 6 +- server/api/helpers/create-user.js | 26 ++- server/api/helpers/delete-board.js | 2 +- .../api/helpers/delete-project-membership.js | 4 +- server/api/helpers/delete-project.js | 4 +- server/api/helpers/delete-user.js | 2 +- .../api/helpers/get-action-to-project-path.js | 8 +- server/api/helpers/get-actions-for-card.js | 2 +- server/api/helpers/get-actions.js | 6 +- .../api/helpers/get-board-to-project-path.js | 6 +- server/api/helpers/get-boards-for-project.js | 4 +- server/api/helpers/get-boards.js | 2 +- .../api/helpers/get-card-labels-for-card.js | 2 +- server/api/helpers/get-card-labels.js | 2 +- server/api/helpers/get-card-memberships.js | 2 +- server/api/helpers/get-card-subscriptions.js | 2 +- .../api/helpers/get-card-to-project-path.js | 8 +- server/api/helpers/get-cards-for-board.js | 2 +- server/api/helpers/get-cards-for-list.js | 4 +- server/api/helpers/get-cards.js | 2 +- .../api/helpers/get-label-to-project-path.js | 8 +- server/api/helpers/get-labels-for-board.js | 2 +- .../api/helpers/get-list-to-project-path.js | 8 +- server/api/helpers/get-lists-for-board.js | 4 +- .../get-membership-project-ids-for-user.js | 2 +- .../get-membership-user-ids-for-project.js | 2 +- .../api/helpers/get-memberships-for-card.js | 4 +- .../helpers/get-memberships-for-project.js | 2 +- .../api/helpers/get-notifications-for-user.js | 2 +- server/api/helpers/get-notifications.js | 2 +- .../get-project-memberships-for-user.js | 2 +- server/api/helpers/get-project-memberships.js | 2 +- server/api/helpers/get-projects.js | 2 +- .../get-subscription-user-ids-for-card.js | 4 +- .../get-subscriptions-by-user-for-card.js | 2 +- .../api/helpers/get-subscriptions-for-card.js | 4 +- .../api/helpers/get-task-to-project-path.js | 8 +- server/api/helpers/get-tasks-for-card.js | 2 +- server/api/helpers/get-tasks.js | 2 +- .../helpers/get-user-by-email-or-username.js | 18 ++ server/api/helpers/get-user.js | 2 +- server/api/helpers/get-users.js | 2 +- server/api/helpers/insert-to-positionables.js | 8 +- server/api/helpers/map-records.js | 2 +- server/api/helpers/update-board.js | 6 +- server/api/helpers/update-card.js | 2 +- server/api/helpers/update-list.js | 2 +- .../helpers/update-notifications-for-user.js | 4 +- server/api/helpers/update-project.js | 2 +- server/api/helpers/update-user.js | 24 ++- server/api/helpers/verify-token.js | 4 +- server/api/hooks/current-user/index.js | 2 +- server/api/models/User.js | 8 + server/config/routes.js | 1 + .../20180721020022_create_next_id_function.js | 4 +- .../20180721021044_create_archive_table.js | 11 +- ...0180721220409_create_user_account_table.js | 15 +- .../20180721233450_create_project_table.js | 11 +- ...1234154_create_project_membership_table.js | 11 +- .../20180722000627_create_board_table.js | 11 +- .../20180722003437_create_list_table.js | 11 +- .../20180722003502_create_label_table.js | 11 +- .../20180722003614_create_card_table.js | 11 +- ...22005122_create_card_subscription_table.js | 11 +- ...0722005359_create_card_membership_table.js | 11 +- .../20180722005928_create_card_label_table.js | 11 +- .../20180722006570_create_task_table.js | 11 +- .../20181024220134_create_action_table.js | 11 +- ...0181112104653_create_notification_table.js | 11 +- server/db/seeds/default.js | 3 +- server/package-lock.json | 13 +- server/package.json | 3 +- 143 files changed, 1051 insertions(+), 420 deletions(-) create mode 100644 client/src/components/UserPopup/EditUsernameStep.jsx create mode 100644 client/src/components/UserPopup/EditUsernameStep.module.css create mode 100644 client/src/utils/validator.js create mode 100644 server/api/controllers/users/update-username.js create mode 100644 server/api/helpers/get-user-by-email-or-username.js 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', + })} + ) +
+