mirror of
https://github.com/scinote-eln/scinote-web.git
synced 2025-11-13 09:53:08 +08:00
adds remove team action
This commit is contained in:
parent
1b3b3dfea9
commit
705d07f60e
11 changed files with 215 additions and 8 deletions
|
|
@ -31,7 +31,6 @@ module ClientApi
|
||||||
current_user: current_user,
|
current_user: current_user,
|
||||||
params: team_params)
|
params: team_params)
|
||||||
team_service.update_team!
|
team_service.update_team!
|
||||||
|
|
||||||
success_response('/client_api/teams/update_details',
|
success_response('/client_api/teams/update_details',
|
||||||
team_service.single_team_details_data)
|
team_service.single_team_details_data)
|
||||||
rescue ClientApi::CustomTeamError => error
|
rescue ClientApi::CustomTeamError => error
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,19 @@ module ClientApi
|
||||||
unsuccess_response(error.to_s)
|
unsuccess_response(error.to_s)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def remove_user
|
||||||
|
ut_service = ClientApi::UserTeamService.new(
|
||||||
|
user: current_user,
|
||||||
|
team_id: params[:team],
|
||||||
|
user_team_id: params[:user_team]
|
||||||
|
)
|
||||||
|
ut_service.destroy_user_team_and_assign_new_team_owner!
|
||||||
|
success_response('/client_api/teams/team_users',
|
||||||
|
ut_service.team_users_data)
|
||||||
|
rescue ClientApi::CustomUserTeamError => error
|
||||||
|
unsuccess_response(error.to_s)
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def success_response(template, locals)
|
def success_response(template, locals)
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,7 @@ export const CONTACT_US_LINK =
|
||||||
// user teams
|
// user teams
|
||||||
export const LEAVE_TEAM_PATH = "/client_api/users/leave_team";
|
export const LEAVE_TEAM_PATH = "/client_api/users/leave_team";
|
||||||
export const UPDATE_USER_TEAM_ROLE_PATH = "/client_api/users/update_role";
|
export const UPDATE_USER_TEAM_ROLE_PATH = "/client_api/users/update_role";
|
||||||
|
export const REMOVE_USER_FROM_TEAM_PATH = "/client_api/users/remove_user";
|
||||||
|
|
||||||
// settings
|
// settings
|
||||||
export const SETTINGS_ACCOUNT_PROFILE = "/settings/account/profile";
|
export const SETTINGS_ACCOUNT_PROFILE = "/settings/account/profile";
|
||||||
|
|
|
||||||
|
|
@ -70,6 +70,15 @@ export default {
|
||||||
warning_message_three: "all repository protocols in the team belonging to you will be reassigned onto a new owner from 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"
|
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}?",
|
||||||
|
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 <b>Owner</b> 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: {
|
update_team_description_modal: {
|
||||||
title: "Edit team description",
|
title: "Edit team description",
|
||||||
label: "Description"
|
label: "Description"
|
||||||
|
|
|
||||||
|
|
@ -163,7 +163,7 @@ class SettingsTeamPageContainer extends Component {
|
||||||
<TeamsMembers
|
<TeamsMembers
|
||||||
members={this.state.users}
|
members={this.state.users}
|
||||||
updateUsersCallback={this.updateUsersCallback}
|
updateUsersCallback={this.updateUsersCallback}
|
||||||
teamId={this.state.team.id}
|
team={this.state.team}
|
||||||
/>
|
/>
|
||||||
<UpdateTeamDescriptionModal
|
<UpdateTeamDescriptionModal
|
||||||
showModal={this.state.showModal}
|
showModal={this.state.showModal}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,99 @@
|
||||||
|
import React, { Component } from "react";
|
||||||
|
import PropTypes, { bool, number, string, func } from "prop-types";
|
||||||
|
import { Modal, Button, Alert, Glyphicon } from "react-bootstrap";
|
||||||
|
import { FormattedMessage, FormattedHTMLMessage } from "react-intl";
|
||||||
|
|
||||||
|
import axios from "../../../../../app/axios";
|
||||||
|
|
||||||
|
import { REMOVE_USER_FROM_TEAM_PATH } from "../../../../../app/routes";
|
||||||
|
|
||||||
|
class RemoveUserModal extends Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.onCloseModal = this.onCloseModal.bind(this);
|
||||||
|
this.removeUser = this.removeUser.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
onCloseModal() {
|
||||||
|
this.props.hideModal();
|
||||||
|
}
|
||||||
|
|
||||||
|
removeUser() {
|
||||||
|
const { team_id, team_user_id } = this.props.userToRemove;
|
||||||
|
axios({
|
||||||
|
method: "DELETE",
|
||||||
|
url: REMOVE_USER_FROM_TEAM_PATH,
|
||||||
|
withCredentials: true,
|
||||||
|
data: {
|
||||||
|
team: team_id,
|
||||||
|
user_team: team_user_id
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then(response => {
|
||||||
|
this.props.updateUsersCallback(response.data.team_users);
|
||||||
|
this.props.hideModal();
|
||||||
|
})
|
||||||
|
.catch(error => console.log(error));
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { teamName, userName } = this.props.userToRemove;
|
||||||
|
return (
|
||||||
|
<Modal show={this.props.showModal} onHide={this.onCloseModal}>
|
||||||
|
<Modal.Header closeButton>
|
||||||
|
<Modal.Title>
|
||||||
|
<FormattedMessage
|
||||||
|
id="settings_page.remove_user_modal.title"
|
||||||
|
values={{ user: userName, team: teamName }}
|
||||||
|
/>
|
||||||
|
</Modal.Title>
|
||||||
|
</Modal.Header>
|
||||||
|
<Modal.Body>
|
||||||
|
<p>
|
||||||
|
<FormattedMessage
|
||||||
|
id="settings_page.remove_user_modal.subtitle"
|
||||||
|
values={{ user: userName, team: teamName }}
|
||||||
|
/>
|
||||||
|
</p>
|
||||||
|
<Alert bsStyle="danger">
|
||||||
|
<Glyphicon glyph="exclamation-sign" />
|
||||||
|
<FormattedMessage id="settings_page.remove_user_modal.warnings" />
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<FormattedMessage id="settings_page.remove_user_modal.warning_message_one" />
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<FormattedHTMLMessage id="settings_page.remove_user_modal.warning_message_two" />
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<FormattedMessage id="settings_page.remove_user_modal.warning_message_three" />
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</Alert>
|
||||||
|
</Modal.Body>
|
||||||
|
<Modal.Footer>
|
||||||
|
<Button onClick={this.onCloseModal}>
|
||||||
|
<FormattedMessage id="general.close" />
|
||||||
|
</Button>
|
||||||
|
<Button bsStyle="success" onClick={this.removeUser}>
|
||||||
|
<FormattedMessage id="settings_page.remove_user_modal.remove_user" />
|
||||||
|
</Button>
|
||||||
|
</Modal.Footer>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RemoveUserModal.propTypes = {
|
||||||
|
showModal: bool.isRequired,
|
||||||
|
hideModal: func.isRequired,
|
||||||
|
userToRemove: PropTypes.shape({
|
||||||
|
userName: string.isRequired,
|
||||||
|
team_user_id: number.isRequired,
|
||||||
|
teamName: string.isRequired,
|
||||||
|
team_id: number.isRequired
|
||||||
|
}).isRequired,
|
||||||
|
updateUsersCallback: func.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
export default RemoveUserModal;
|
||||||
|
|
@ -10,13 +10,22 @@ import {
|
||||||
import { FormattedMessage } from "react-intl";
|
import { FormattedMessage } from "react-intl";
|
||||||
import axios from "../../../../../app/axios";
|
import axios from "../../../../../app/axios";
|
||||||
|
|
||||||
|
import RemoveUserModal from "./RemoveUserModal";
|
||||||
import DataTable from "../../../../../shared/data_table";
|
import DataTable from "../../../../../shared/data_table";
|
||||||
import { UPDATE_USER_TEAM_ROLE_PATH } from "../../../../../app/routes";
|
import { UPDATE_USER_TEAM_ROLE_PATH } from "../../../../../app/routes";
|
||||||
|
|
||||||
|
const initalUserToRemove = {
|
||||||
|
userName: "",
|
||||||
|
team_user_id: 0,
|
||||||
|
teamName: "",
|
||||||
|
team_id: 0
|
||||||
|
};
|
||||||
class TeamsMembers extends Component {
|
class TeamsMembers extends Component {
|
||||||
constructor(params) {
|
constructor(params) {
|
||||||
super(params);
|
super(params);
|
||||||
|
this.state = { showModal: false, userToRemove: initalUserToRemove };
|
||||||
this.memberAction = this.memberAction.bind(this);
|
this.memberAction = this.memberAction.bind(this);
|
||||||
|
this.hideModal = this.hideModal.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
currentRole(memberRole, role) {
|
currentRole(memberRole, role) {
|
||||||
|
|
@ -26,7 +35,7 @@ class TeamsMembers extends Component {
|
||||||
updateRole(userTeamId, role) {
|
updateRole(userTeamId, role) {
|
||||||
axios
|
axios
|
||||||
.put(UPDATE_USER_TEAM_ROLE_PATH, {
|
.put(UPDATE_USER_TEAM_ROLE_PATH, {
|
||||||
team: this.props.teamId,
|
team: this.props.team.id,
|
||||||
user_team: userTeamId,
|
user_team: userTeamId,
|
||||||
role
|
role
|
||||||
})
|
})
|
||||||
|
|
@ -36,7 +45,13 @@ class TeamsMembers extends Component {
|
||||||
.catch(error => console.log(error));
|
.catch(error => console.log(error));
|
||||||
}
|
}
|
||||||
|
|
||||||
removeUser(userTeamId) {}
|
hideModal() {
|
||||||
|
this.setState({ showModal: false, userToRemove: initalUserToRemove });
|
||||||
|
}
|
||||||
|
|
||||||
|
userToRemove(userToRemove) {
|
||||||
|
this.setState({ showModal: true, userToRemove });
|
||||||
|
}
|
||||||
|
|
||||||
memberAction(data, row) {
|
memberAction(data, row) {
|
||||||
return (
|
return (
|
||||||
|
|
@ -83,7 +98,12 @@ class TeamsMembers extends Component {
|
||||||
<MenuItem divider />
|
<MenuItem divider />
|
||||||
<MenuItem
|
<MenuItem
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
this.removeUser(data.team_user_id);
|
this.userToRemove({
|
||||||
|
userName: row.name,
|
||||||
|
team_user_id: data.team_user_id,
|
||||||
|
teamName: this.props.team.name,
|
||||||
|
team_id: this.props.team.id
|
||||||
|
});
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<FormattedMessage id="settings_page.single_team.actions.remove_user" />
|
<FormattedMessage id="settings_page.single_team.actions.remove_user" />
|
||||||
|
|
@ -155,6 +175,12 @@ class TeamsMembers extends Component {
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<DataTable data={this.props.members} columns={columns} />
|
<DataTable data={this.props.members} columns={columns} />
|
||||||
|
<RemoveUserModal
|
||||||
|
showModal={this.state.showModal}
|
||||||
|
hideModal={this.hideModal}
|
||||||
|
updateUsersCallback={this.props.updateUsersCallback}
|
||||||
|
userToRemove={this.state.userToRemove}
|
||||||
|
/>
|
||||||
</Panel>
|
</Panel>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -162,7 +188,10 @@ class TeamsMembers extends Component {
|
||||||
|
|
||||||
TeamsMembers.propTypes = {
|
TeamsMembers.propTypes = {
|
||||||
updateUsersCallback: func.isRequired,
|
updateUsersCallback: func.isRequired,
|
||||||
teamId: number.isRequired,
|
team: PropTypes.shape({
|
||||||
|
id: number.isRequired,
|
||||||
|
name: string.isRequired
|
||||||
|
}).isRequired,
|
||||||
members: PropTypes.arrayOf(
|
members: PropTypes.arrayOf(
|
||||||
PropTypes.shape({
|
PropTypes.shape({
|
||||||
id: number.isRequired,
|
id: number.isRequired,
|
||||||
|
|
|
||||||
|
|
@ -9,10 +9,10 @@ module ClientApi
|
||||||
@user = parsed_args.fetch(:user)
|
@user = parsed_args.fetch(:user)
|
||||||
@user_team = UserTeam.find_by_id(parsed_args.fetch(:user_team_id).to_i)
|
@user_team = UserTeam.find_by_id(parsed_args.fetch(:user_team_id).to_i)
|
||||||
@role = args.fetch(:role) { false }
|
@role = args.fetch(:role) { false }
|
||||||
raise ClientApi::CustomUserTeamError unless @user_team && @user && @team
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def destroy_user_team_and_assign_new_team_owner!
|
def destroy_user_team_and_assign_new_team_owner!
|
||||||
|
binding.pry
|
||||||
raise ClientApi::CustomUserTeamError unless user_cant_leave?
|
raise ClientApi::CustomUserTeamError unless user_cant_leave?
|
||||||
new_owner = @team.user_teams
|
new_owner = @team.user_teams
|
||||||
.where(role: 2)
|
.where(role: 2)
|
||||||
|
|
@ -59,7 +59,7 @@ module ClientApi
|
||||||
end
|
end
|
||||||
|
|
||||||
def user_cant_leave?
|
def user_cant_leave?
|
||||||
@user.teams.includes @team &&
|
@user.teams.includes(@team) &&
|
||||||
@user_team.admin? &&
|
@user_team.admin? &&
|
||||||
@team.user_teams.where(role: 2).count <= 1
|
@team.user_teams.where(role: 2).count <= 1
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,7 @@ Rails.application.routes.draw do
|
||||||
get '/current_user_info', to: 'users#current_user_info'
|
get '/current_user_info', to: 'users#current_user_info'
|
||||||
|
|
||||||
namespace :users do
|
namespace :users do
|
||||||
|
delete '/remove_user', to: 'user_teams#remove_user'
|
||||||
delete '/leave_team', to: 'user_teams#leave_team'
|
delete '/leave_team', to: 'user_teams#leave_team'
|
||||||
put '/update_role', to: 'user_teams#update_role'
|
put '/update_role', to: 'user_teams#update_role'
|
||||||
end
|
end
|
||||||
|
|
|
||||||
56
spec/services/client_api/user_team_service_spec.rb
Normal file
56
spec/services/client_api/user_team_service_spec.rb
Normal file
|
|
@ -0,0 +1,56 @@
|
||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
describe ClientApi::UserTeamService do
|
||||||
|
let(:team_one) { create :team }
|
||||||
|
let(:user_one) { create :user, email: Faker::Internet.email }
|
||||||
|
let(:user_team) { create :user_team, user: user_one, team: team_one }
|
||||||
|
|
||||||
|
it 'should raise ClientApi::CustomUserTeamError if user is not assigned' do
|
||||||
|
expect {
|
||||||
|
ClientApi::UserTeamService.new(
|
||||||
|
team_id: team_one.id,
|
||||||
|
user_team_id: user_team.id
|
||||||
|
)
|
||||||
|
}.to raise_error(ClientApi::CustomUserTeamError)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should raise ClientApi::CustomUserTeamError if team is not assigned' do
|
||||||
|
expect {
|
||||||
|
ClientApi::UserTeamService.new(user: user_one, user_team_id: user_team.id)
|
||||||
|
}.to raise_error(ClientApi::CustomUserTeamError)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should raise ClientApi::CustomUserTeamError if ' \
|
||||||
|
'user_team is not assigned' do
|
||||||
|
expect {
|
||||||
|
ClientApi::UserTeamService.new(user: user_one, team_id: team_one.id)
|
||||||
|
}.to raise_error(ClientApi::CustomUserTeamError)
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#destroy_user_team_and_assign_new_team_owner!' do
|
||||||
|
let(:user_two) { create :user, email: Faker::Internet.email }
|
||||||
|
|
||||||
|
it 'should raise ClientApi::CustomUserTeamError if user ' \
|
||||||
|
'can\'t leave the team' do
|
||||||
|
ut_service = ClientApi::UserTeamService.new(
|
||||||
|
team_id: team_one.id,
|
||||||
|
user_team_id: user_team.id,
|
||||||
|
user: user_one
|
||||||
|
)
|
||||||
|
expect {
|
||||||
|
ut_service.destroy_user_team_and_assign_new_team_owner!
|
||||||
|
}.to raise_error(ClientApi::CustomUserTeamError)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should destroy the user_team relation' do
|
||||||
|
new_user_team = create :user_team, team: team_one, user: user_two
|
||||||
|
ut_service = ClientApi::UserTeamService.new(
|
||||||
|
team_id: team_one.id,
|
||||||
|
user_team_id: new_user_team.id,
|
||||||
|
user: user_two
|
||||||
|
)
|
||||||
|
ut_service.destroy_user_team_and_assign_new_team_owner!
|
||||||
|
expect(new_user_team).to_not exist
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
Loading…
Add table
Reference in a new issue