diff --git a/app/controllers/client_api/users/invitations_controller.rb b/app/controllers/client_api/users/invitations_controller.rb index 27a5fa6a3..87789769e 100644 --- a/app/controllers/client_api/users/invitations_controller.rb +++ b/app/controllers/client_api/users/invitations_controller.rb @@ -1,34 +1,34 @@ module ClientApi -module Users - class InvitationsController < Devise::InvitationsController + module Users + class InvitationsController < Devise::InvitationsController + before_action :check_invite_users_permission, only: :invite_users - before_action :check_invite_users_permission, only: :invite_users + def invite_users + invite_service = + ClientApi::InvitationsService.new(user: current_user, + team: @team, + role: params['user_role'], + emails: params[:emails]) + invite_results = invite_service.invitation + success_response(invite_results) + end - def invite_users - invite_service = ClientApi::InvitationsService.new(user: current_user, - team: @team, - role: params['user_role'], - emails: params[:emails]) - invite_results = invite_service.invitation - success_response(invite_results) - end - - def success_response(invite_results) - respond_to do |format| - format.json do - render template: '/client_api/users/invite_users', - status: :ok, - locals: {invite_results: invite_results, team: @team} + def success_response(invite_results) + respond_to do |format| + format.json do + render template: '/client_api/users/invite_users', + status: :ok, + locals: { invite_results: invite_results, team: @team } + end end end - end - private + private - def check_invite_users_permission - @team = Team.find_by_id(params[:team_id]) - render_403 if @team && !is_admin_of_team(@team) + def check_invite_users_permission + @team = Team.find_by_id(params[:team_id]) + render_403 if @team && !is_admin_of_team(@team) + end end end end -end \ No newline at end of file diff --git a/app/javascript/src/components/InviteUsersModal/components/InviteUsersButton.jsx b/app/javascript/src/components/InviteUsersModal/components/InviteUsersButton.jsx index 0ddd97ccf..4b38f0092 100644 --- a/app/javascript/src/components/InviteUsersModal/components/InviteUsersButton.jsx +++ b/app/javascript/src/components/InviteUsersModal/components/InviteUsersButton.jsx @@ -1,27 +1,28 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import { FormattedMessage } from 'react-intl'; -import { DropdownButton, MenuItem } from 'react-bootstrap'; +import React from "react"; +import { func } from "prop-types"; +import { FormattedMessage } from "react-intl"; +import { DropdownButton, MenuItem } from "react-bootstrap"; -const InviteUsersButton = props => - } - id="invite_users.submit_button" - > - props.handleClick('guest')}> - - - props.handleClick('normal_user')}> - - - props.handleClick('admin')}> - - - ; +const InviteUsersButton = props => ( + } + id="invite_users.submit_button" + > + props.handleClick("guest")}> + + + props.handleClick("normal_user")}> + + + props.handleClick("admin")}> + + + +); InviteUsersButton.propTypes = { - handleClick: PropTypes.func.isRequired + handleClick: func.isRequired }; export default InviteUsersButton; diff --git a/app/javascript/src/components/InviteUsersModal/components/InviteUsersForm.jsx b/app/javascript/src/components/InviteUsersModal/components/InviteUsersForm.jsx index 6e8576360..3f0805b24 100644 --- a/app/javascript/src/components/InviteUsersModal/components/InviteUsersForm.jsx +++ b/app/javascript/src/components/InviteUsersModal/components/InviteUsersForm.jsx @@ -1,38 +1,42 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import { FormattedMessage } from 'react-intl'; -import { FormGroup, HelpBlock } from 'react-bootstrap'; -import TagsInput from 'react-tagsinput'; +import React from "react"; +import { string, func, arrayOf } from "prop-types"; +import { FormattedMessage } from "react-intl"; +import { FormGroup, HelpBlock } from "react-bootstrap"; +import TagsInput from "react-tagsinput"; -import { INVITE_USERS_LIMIT } from '../../../config/constants/numeric'; +import { INVITE_USERS_LIMIT } from "../../../config/constants/numeric"; -const InviteUsersForm = props => - -

- -

- - - - - - -
; +const InviteUsersForm = props => ( + +

+ +

+ + + + + + +
+); InviteUsersForm.propTypes = { - tags: PropTypes.arrayOf(PropTypes.string.isRequired).isRequired, - handleChange: PropTypes.func.isRequired, - teamName: PropTypes.string.isRequired + tags: arrayOf(string.isRequired).isRequired, + handleChange: func.isRequired, + teamName: string.isRequired }; export default InviteUsersForm; diff --git a/app/javascript/src/components/InviteUsersModal/components/InviteUsersResults.jsx b/app/javascript/src/components/InviteUsersModal/components/InviteUsersResults.jsx index 87d2ff153..6c27fb14c 100644 --- a/app/javascript/src/components/InviteUsersModal/components/InviteUsersResults.jsx +++ b/app/javascript/src/components/InviteUsersModal/components/InviteUsersResults.jsx @@ -1,30 +1,38 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import { Alert } from 'react-bootstrap'; -import { FormattedMessage } from 'react-intl'; +import React from "react"; +import { shape, arrayOf, string } from "prop-types"; +import { Alert } from "react-bootstrap"; +import { FormattedMessage } from "react-intl"; -const InviteUsersResults = props => -
-
- -
-
- {props.results.invite_results.map(result => - - {result.email} -  -  - , - nr: result.invite_limit - }} /> - - )} -
; +const InviteUsersResults = props => ( +
+
+ +
+
+ {props.results.invite_results.map(result => ( + + {result.email} +  -  + + ), + nr: result.invite_limit + }} + /> + + ))} +
+); InviteUsersResults.propTypes = { - results: PropTypes.object.isRequired + results: shape({ + invite_results: arrayOf.isRequired, + team_name: string.isRequired + }).isRequired }; export default InviteUsersResults; - diff --git a/app/javascript/src/components/InviteUsersModal/index.jsx b/app/javascript/src/components/InviteUsersModal/index.jsx index b4ab3b8be..8d702cfa2 100644 --- a/app/javascript/src/components/InviteUsersModal/index.jsx +++ b/app/javascript/src/components/InviteUsersModal/index.jsx @@ -1,107 +1,115 @@ -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; -import { FormattedMessage } from 'react-intl'; -import { Modal, ButtonToolbar, Button } from 'react-bootstrap'; -import styled from 'styled-components'; -import axios from '../../config/axios'; +import React, { Component } from "react"; +import { bool, func, shape, number, string } from "prop-types"; +import { FormattedMessage } from "react-intl"; +import { Modal, ButtonToolbar, Button } from "react-bootstrap"; +import styled from "styled-components"; +import axios from "../../config/axios"; -import { INVITE_USERS_PATH, TEAM_DETAILS_PATH } from '../../config/api_endpoints'; -import InviteUsersForm from './components/InviteUsersForm'; -import InviteUsersResults from './components/InviteUsersResults'; -import InviteUsersButton from './components/InviteUsersButton'; +import { + INVITE_USERS_PATH, + TEAM_DETAILS_PATH +} from "../../config/api_endpoints"; +import InviteUsersForm from "./components/InviteUsersForm"; +import InviteUsersResults from "./components/InviteUsersResults"; +import InviteUsersButton from "./components/InviteUsersButton"; -const StyledButtonToolbar = styled(ButtonToolbar)` - float: right; -`; +const StyledButtonToolbar = styled(ButtonToolbar)`float: right;`; class InviteUsersModal extends Component { - constructor() { - super(); - this.state = { - showInviteUsersResults: false, - inputTags: [], - inviteResults: [] - }; - this.handleInputChange = this.handleInputChange.bind(this); - this.inviteAs = this.inviteAs.bind(this); - this.handleCloseModal = this.handleCloseModal.bind(this) - } + constructor(props) { + super(props); + this.state = { + showInviteUsersResults: false, + inputTags: [], + inviteResults: [] + }; + this.handleInputChange = this.handleInputChange.bind(this); + this.inviteAs = this.inviteAs.bind(this); + this.handleCloseModal = this.handleCloseModal.bind(this); + } - handleCloseModal() { - const path = TEAM_DETAILS_PATH.replace(":team_id", this.props.team.id); - this.props.onCloseModal(); - this.setState({ - showInviteUsersResults: false, - inputTags: [], - inviteResults: [] - }); - // Update team members table - axios.get(path).then(response => { - const { users } = response.data.team_details; - this.props.updateUsersCallback(users); - }) - } + handleCloseModal() { + const path = TEAM_DETAILS_PATH.replace(":team_id", this.props.team.id); + this.props.onCloseModal(); + this.setState({ + showInviteUsersResults: false, + inputTags: [], + inviteResults: [] + }); + // Update team members table + axios.get(path).then(response => { + const { users } = response.data.team_details; + this.props.updateUsersCallback(users); + }); + } - handleInputChange(inputTags) { - this.setState({ inputTags }); - } + handleInputChange(inputTags) { + this.setState({ inputTags }); + } - inviteAs(role) { - axios - .put(INVITE_USERS_PATH, { - user_role: role, - emails: this.state.inputTags, - team_id: this.props.team.id - }) - .then(({ data }) => { - this.setState({ inviteResults: data }); - this.setState({ showInviteUsersResults: true }); - }) - .catch(error => {}); - } + inviteAs(role) { + axios + .put(INVITE_USERS_PATH, { + user_role: role, + emails: this.state.inputTags, + team_id: this.props.team.id + }) + .then(({ data }) => { + this.setState({ inviteResults: data }); + this.setState({ showInviteUsersResults: true }); + }) + .catch(error => {}); + } - render() { - let modalBody = null; - let inviteButton = null; - if (this.state.showInviteUsersResults) { - modalBody = ; - inviteButton = null; - } else { - modalBody = ; - inviteButton = ; - } + render() { + let modalBody = null; + let inviteButton = null; + if (this.state.showInviteUsersResults) { + modalBody = ; + inviteButton = null; + } else { + modalBody = ( + + ); + inviteButton = ; + } - return ( - - - - - - - - {modalBody} - - - - - {inviteButton} - - - - ); - } + return ( + + + + + + + {modalBody} + + + + {inviteButton} + + + + ); + } } InviteUsersModal.propTypes = { - showModal: PropTypes.bool.isRequired, - onCloseModal: PropTypes.func.isRequired, - team: PropTypes.shape({ - id: PropTypes.number.isRequired, - name: PropTypes.string.isRequired - }).isRequired, - updateUsersCallback: PropTypes.func.isRequired + showModal: bool.isRequired, + onCloseModal: func.isRequired, + team: shape({ + id: number.isRequired, + name: string.isRequired + }).isRequired, + updateUsersCallback: func.isRequired }; export default InviteUsersModal; diff --git a/app/javascript/src/config/api_endpoints.js b/app/javascript/src/config/api_endpoints.js index 9e1fa0172..9c066088b 100644 --- a/app/javascript/src/config/api_endpoints.js +++ b/app/javascript/src/config/api_endpoints.js @@ -39,7 +39,7 @@ export const CHANGE_USER_RECENT_NOTIFICATION_EMAIL_PATH = "/client_api/users/change_recent_notification_email"; export const CHANGE_USER_SYSTEM_MESSAGE_NOTIFICATION_EMAIL_PATH = "/client_api/users/change_system_notification_email"; -export const INVITE_USERS_PATH = '/client_api/users/invite_users'; +export const INVITE_USERS_PATH = "/client_api/users/invite_users"; // info dropdown_title export const CUSTOMER_SUPPORT_LINK = "http://scinote.net/support"; diff --git a/app/javascript/src/config/locales/messages.js b/app/javascript/src/config/locales/messages.js index 037c2fb8e..b1f64bd33 100644 --- a/app/javascript/src/config/locales/messages.js +++ b/app/javascript/src/config/locales/messages.js @@ -1,25 +1,25 @@ export default { - 'en-US': { + "en-US": { general: { - close: 'Close', - cancel: 'Cancel', - update: 'Update', - edit: 'Edit', - loading: 'Loading ...' + close: "Close", + cancel: "Cancel", + update: "Update", + edit: "Edit", + loading: "Loading ..." }, error_messages: { text_too_short: "is too short (minimum is {min_length} characters)", text_too_long: "is too long (maximum is {max_length} characters)" }, navbar: { - page_title: 'sciNote', - home_label: 'Home', - protocols_label: 'Protocols', - repositories_label: 'Repositories', - activities_label: 'Activities', - search_label: 'Search', - notifications_label: 'Notifications', - info_label: 'Info' + page_title: "sciNote", + home_label: "Home", + protocols_label: "Protocols", + repositories_label: "Repositories", + activities_label: "Activities", + search_label: "Search", + notifications_label: "Notifications", + info_label: "Info" }, settings_page: { all_teams: "All teams", @@ -54,8 +54,8 @@ export default { "Assignment notifications appear whenever you get assigned to a team, project, task.", recent_changes: "Recent changes", recent_changes_msg: - 'Recent changes notifications appear whenever there is a change on a task you are assigned to.', - system_message: 'System message', + "Recent changes notifications appear whenever there is a change on a task you are assigned to.", + system_message: "System message", system_message_msg: "System message notifications are specifically sent by site maintainers to notify all users about a system update.", show_in_scinote: "Show in sciNote", @@ -64,20 +64,28 @@ export default { yes: "Yes", leave_team_modal: { title: "Leave team {teamName}", - subtitle: "Are you sure you wish to leave team My projects? This action is irreversible.", + subtitle: + "Are you sure you wish to leave team My projects? This action is irreversible.", warnings: "Leaving team has following consequences:", - warning_message_one: "you will lose access to all content belonging to the team (including projects, tasks, protocols and activities);", - warning_message_two: "all projects in the team where you were the sole Owner will receive a new owner from the team administrators;", - warning_message_three: "all repository protocols in the team belonging to you will be reassigned onto a new owner from team administrators.", + warning_message_one: + "you will lose access to all content belonging to the team (including projects, tasks, protocols and activities);", + warning_message_two: + "all projects in the team where you were the sole Owner will receive a new owner from the team administrators;", + warning_message_three: + "all repository protocols in the team belonging to you will be reassigned onto a new owner from team administrators.", leave_team: "Leave" }, remove_user_modal: { title: "Remove user {user} from team {team}", - subtitle: "Are you sure you wish to remove user {user} from team {team}?", + subtitle: + "Are you sure you wish to remove user {user} from team {team}?", warnings: "Removing user from team has following consequences:", - warning_message_one: "user will lose access to all content belonging to the team (including projects, tasks, protocols and activities);", - warning_message_two: "all projects in the team where user was the sole Owner will be reassigned onto you as a new owner;", - warning_message_three: "all repository protocols in the team belonging to user will be reassigned onto you.", + warning_message_one: + "user will lose access to all content belonging to the team (including projects, tasks, protocols and activities);", + warning_message_two: + "all projects in the team where user was the sole Owner will be reassigned onto you as a new owner;", + warning_message_three: + "all repository protocols in the team belonging to user will be reassigned onto you.", remove_user: "Remove user" }, update_team_description_modal: { @@ -114,61 +122,65 @@ export default { } }, activities: { - modal_title: 'Activities', - no_data: 'No Data', - more_activities: 'More Activities' + modal_title: "Activities", + no_data: "No Data", + more_activities: "More Activities" }, global_team_switch: { - new_team: 'New team' + new_team: "New team" }, notifications: { - dropdown_title: 'Notifications', - dropdown_settings_link: 'Settings', - dropdown_show_all: 'Show all notifications' + dropdown_title: "Notifications", + dropdown_settings_link: "Settings", + dropdown_show_all: "Show all notifications" }, info_dropdown: { - customer_support: 'Customer support', - tutorials: 'Tutorials', - release_notes: 'Release notes', - premium: 'Premium', - contact_us: 'Contact us' + customer_support: "Customer support", + tutorials: "Tutorials", + release_notes: "Release notes", + premium: "Premium", + contact_us: "Contact us" }, user_account_dropdown: { - greeting: 'Hi, {name}', - settings: 'Settings', - log_out: 'Log out' + greeting: "Hi, {name}", + settings: "Settings", + log_out: "Log out" }, invite_users: { - modal_title: 'Invite users to team {team}', - input_text: 'Invite more people to team {team} and start using sciNote.', - input_help: 'Input one or multiple emails, confirm each email with ENTER key.', + modal_title: "Invite users to team {team}", + input_text: "Invite more people to team {team} and start using sciNote.", + input_help: + "Input one or multiple emails, confirm each email with ENTER key.", dropdown_button: { - invite: 'Invite user/s', - guest: 'as Guest/s', - normal_user: 'as Normal user/s', - admin: 'as Administrator/s' + invite: "Invite user/s", + guest: "as Guest/s", + normal_user: "as Normal user/s", + admin: "as Administrator/s" }, - results_title: 'Invitation results:', + results_title: "Invitation results:", roles: { - guest: 'Guest', - normal_user: 'Normal user', - admin: 'Administrator' + guest: "Guest", + normal_user: "Normal user", + admin: "Administrator" }, results_msg: { - user_exists: 'User is already a member of sciNote.', + user_exists: "User is already a member of sciNote.", user_exists_unconfirmed: - 'User is already a member of sciNote but is not confirmed yet.', + "User is already a member of sciNote but is not confirmed yet.", user_exists_and_in_team_unconfirmed: - 'User is already a member of sciNote and team {team} as {role} but is not confirmed yet.', + "User is already a member of sciNote and team {team} as {role} but is not confirmed yet.", user_exists_invited_to_team_unconfirmed: - 'User is already a member of sciNote but is not confirmed yet - successfully invited to team {team} as {role}.', - user_exists_and_in_team: 'User is already a member of sciNote and team {team} as {role}.', + "User is already a member of sciNote but is not confirmed yet - successfully invited to team {team} as {role}.", + user_exists_and_in_team: + "User is already a member of sciNote and team {team} as {role}.", user_exists_invited_to_team: - 'User was already a member of sciNote - successfully invited to team {team} as {role}.', - user_created: 'User succesfully invited to sciNote.', - user_created_invited_to_team: 'User successfully invited to sciNote and team {team} as {role}.', - user_invalid: 'Invalid email.', - too_many_emails: 'Only invited first {nr} emails. To invite more users, fill in another invitation form.' + "User was already a member of sciNote - successfully invited to team {team} as {role}.", + user_created: "User succesfully invited to sciNote.", + user_created_invited_to_team: + "User successfully invited to sciNote and team {team} as {role}.", + user_invalid: "Invalid email.", + too_many_emails: + "Only invited first {nr} emails. To invite more users, fill in another invitation form." } } } diff --git a/app/javascript/src/scenes/SettingsPage/scenes/team/components/TeamsMembers.jsx b/app/javascript/src/scenes/SettingsPage/scenes/team/components/TeamsMembers.jsx index bed7cf04b..8f703018b 100644 --- a/app/javascript/src/scenes/SettingsPage/scenes/team/components/TeamsMembers.jsx +++ b/app/javascript/src/scenes/SettingsPage/scenes/team/components/TeamsMembers.jsx @@ -10,7 +10,7 @@ import { import { FormattedMessage } from "react-intl"; import axios from "../../../../../config/axios"; -import InviteUsersModal from '../../../../../components/InviteUsersModal'; +import InviteUsersModal from "../../../../../components/InviteUsersModal"; import RemoveUserModal from "./RemoveUserModal"; import DataTable from "../../../../../components/data_table"; import { UPDATE_USER_TEAM_ROLE_PATH } from "../../../../../config/api_endpoints"; @@ -30,8 +30,12 @@ class TeamsMembers extends Component { userToRemove: initalUserToRemove }; this.memberAction = this.memberAction.bind(this); - this.showInviteUsersModalCallback = this.showInviteUsersModalCallback.bind(this); - this.closeInviteUsersModalCallback = this.closeInviteUsersModalCallback.bind(this); + this.showInviteUsersModalCallback = this.showInviteUsersModalCallback.bind( + this + ); + this.closeInviteUsersModalCallback = this.closeInviteUsersModalCallback.bind( + this + ); this.hideModal = this.hideModal.bind(this); } @@ -184,7 +188,7 @@ class TeamsMembers extends Component { } > - diff --git a/app/javascript/src/styles/main.scss b/app/javascript/src/styles/main.scss index 5c5a8f1fb..54cb15064 100644 --- a/app/javascript/src/styles/main.scss +++ b/app/javascript/src/styles/main.scss @@ -1,26 +1,26 @@ -@import 'constants'; -@import 'react-bootstrap-timezone-picker/dist/react-bootstrap-timezone-picker.min.css'; -@import '~react-bootstrap-table/dist/react-bootstrap-table.min'; -@import 'react-tagsinput/react-tagsinput.css'; +@import "constants"; +@import "react-bootstrap-timezone-picker/dist/react-bootstrap-timezone-picker.min.css"; +@import "~react-bootstrap-table/dist/react-bootstrap-table.min"; +@import "react-tagsinput/react-tagsinput.css"; body { - background-color: $color-concrete; - color: $color-emperor; - font-family: "Open Sans", Arial, Helvetica, sans-serif; - font-size: 13px; + background-color: $color-concrete; + color: $color-emperor; + font-family: "Open Sans", Arial, Helvetica, sans-serif; + font-size: 13px; } .label-primary { - background-color: $color-theme-primary; + background-color: $color-theme-primary; } .btn-primary { - background-color: $color-theme-secondary; - border-color: $primary-hover-color; - margin-right: 7px; - &:hover { - background-color: $primary-hover-color; - } + background-color: $color-theme-secondary; + border-color: $primary-hover-color; + margin-right: 7px; + &:hover { + background-color: $primary-hover-color; + } } // // fixes issue with dropdown in datatable diff --git a/app/services/client_api/invitations_service.rb b/app/services/client_api/invitations_service.rb index 9971eb275..4abb0e04f 100644 --- a/app/services/client_api/invitations_service.rb +++ b/app/services/client_api/invitations_service.rb @@ -11,12 +11,14 @@ module ClientApi raise ClientApi::CustomInvitationsError unless @emails && @team && @role @emails && @emails.empty? { raise ClientApi::CustomInvitationsError } - @role && !UserTeam.roles.keys.include?(@role) { raise ClientApi::CustomInvitationsError } + if @role && !UserTeam.roles.keys.include?(@role) + raise ClientApi::CustomInvitationsError + end end def invitation invite_results = [] - + @emails.each_with_index do |email, index| result = {} # Check invite users limit @@ -33,52 +35,13 @@ module ClientApi # Check if user already exists user = User.find_by_email(email) if User.exists?(email: email) - # User does not exist - if user.blank? - password = generate_user_password - # Validate the user data - error = !(Constants::BASIC_EMAIL_REGEX === email) - error = validate_user(email, email, password).count > 0 unless error - - if !error - user = invite_new_user(email) - - result[:status] = :user_created - result[:alert] = :success - result[:user] = user - - # Invitation to team - if @team.present? - user_team = create_user_team_relation_and_notification(user) - result[:status] = :user_created_invited_to_team - result[:user_team] = user_team - end - else - # Return invalid status - result[:status] = :user_invalid - result[:alert] = :danger - end - # User exists - else - result[:status] = :"#{:user_exists}#{:_unconfirmed if !user.confirmed?}" - result[:alert] = :info - result[:user] = user - - # Invitation to team - if @team.present? - user_team = - UserTeam.where(user: user, team: @team).first if UserTeam.exists?(user: user, team: @team) - - if user_team.present? - result[:status] = :"#{:user_exists_and_in_team}#{:_unconfirmed if !user.confirmed?}" - - else - user_team = create_user_team_relation_and_notification(user) - result[:status] = :"#{:user_exists_invited_to_team}#{:_unconfirmed if !user.confirmed?}" - end - result[:user_team] = user_team - end - end + result = if user.blank? + # User does not exist + handle_new_user(result, email, user) + else + # User exists + handle_existing_user(result, user) + end invite_results << result end invite_results @@ -86,6 +49,61 @@ module ClientApi private + def handle_new_user(result, email, user) + password = generate_user_password + # Validate the user data + error = (Constants::BASIC_EMAIL_REGEX !~ email) + error = validate_user(email, email, password).count > 0 unless error + + if !error + # Invite new user + user = invite_new_user(email) + + result[:status] = :user_created + result[:alert] = :success + result[:user] = user + + # Invitation to team + if @team.present? + user_team = create_user_team_relation_and_notification(user) + result[:status] = :user_created_invited_to_team + result[:user_team] = user_team + end + else + # Return invalid status + result[:status] = :user_invalid + result[:alert] = :danger + end + result + end + + def handle_existing_user(result, user) + result[:status] = + :"#{:user_exists}#{:_unconfirmed unless user.confirmed?}" + result[:alert] = :info + result[:user] = user + + # Invitation to team + if @team.present? + if UserTeam.exists?(user: user, team: @team) + user_team = UserTeam.where(user: user, team: @team).first + end + + if user_team.present? + result[:status] = + :"#{:user_exists_and_in_team}#{:_unconfirmed unless user + .confirmed?}" + else + user_team = create_user_team_relation_and_notification(user) + result[:status] = + :"#{:user_exists_invited_to_team}#{:_unconfirmed unless user + .confirmed?}" + end + result[:user_team] = user_team + end + result + end + def invite_new_user(email) user = User.invite!( full_name: email, @@ -140,4 +158,4 @@ module ClientApi end CustomInvitationsError = Class.new(StandardError) -end \ No newline at end of file +end diff --git a/app/views/client_api/users/invite_users.json.jbuilder b/app/views/client_api/users/invite_users.json.jbuilder index 95da57caf..d0c4d71c2 100644 --- a/app/views/client_api/users/invite_users.json.jbuilder +++ b/app/views/client_api/users/invite_users.json.jbuilder @@ -2,7 +2,11 @@ json.invite_results invite_results do |invite_result| json.status invite_result[:status] json.alert invite_result[:alert] json.email invite_result[:email] - json.user_role invite_result[:user_team].role if invite_result[:user_team].present? - json.invite_limit invite_result[:invite_user_limit] if invite_result[:invite_user_limit].present? + if invite_result[:user_team].present? + json.user_role invite_result[:user_team].role + end + if invite_result[:invite_user_limit].present? + json.invite_limit invite_result[:invite_user_limit] + end end -json.team_name team.name if team.present? \ No newline at end of file +json.team_name team.name if team.present? diff --git a/app/views/users/settings/teams/show.html.erb b/app/views/users/settings/teams/show.html.erb index 13b420746..a432bd8ec 100644 --- a/app/views/users/settings/teams/show.html.erb +++ b/app/views/users/settings/teams/show.html.erb @@ -83,7 +83,8 @@