-
Notifications
You must be signed in to change notification settings - Fork 24
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Added usersWithOpenTasks to the admin api. Added a button to the projects-view that displays all users with open tasks of the selected project in a modal * added method to transfer many tasks at once to admin_rest_api, created a modified copy of the task_transfer_modal to control a bulk task transfer * adding a fetch function, that fetches all users with open tasks -> onhold, waiting for backend to provide the api request * creating mock data, ongoing * added mock data, the modal is now working and displayable but has no real functionallity * fixed flow errors * fixed flow errors and added some error handling if no project was selected * [WIP] transfer annotations to different user for api #2862 * [WIP] route for transferring active tasks of a project to a user #2862 * routes for showing active tasks and transfering them #2862 * added changelog, moved error message to messages, fixed flow error * added the request for active users to the admin_rest_api and used it to display the table in the modal * added a type for activeUsers, added an api request to transfer all active tasks of a project, transfer modal only renders if set to visible * reverted changelog -> entry for this issue still missing * added changelog entry * added message for successful transfer, added transfer functionality, and change api call to post-type * corrected changelog entry * added error handling to the transfer request, added an error message to messages * refeshed snapshots * refactored requested changes * updated snapshots and resolved auto merging errors * fixed flow type problems * fixed linter issues * added a spinner while fetching data * disallow transferring tasks to users who cannot access the dataset
- Loading branch information
1 parent
ea0856f
commit d099fd7
Showing
17 changed files
with
554 additions
and
81 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
162 changes: 162 additions & 0 deletions
162
app/assets/javascripts/admin/project/transfer_all_tasks_modal.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,162 @@ | ||
// @flow | ||
|
||
import _ from "lodash"; | ||
import * as React from "react"; | ||
import { Modal, Button, Table, Spin } from "antd"; | ||
import { | ||
getUsers, | ||
getUsersWithActiveTasks, | ||
transferActiveTasksOfProject, | ||
} from "admin/admin_rest_api"; | ||
import type { APIUserType, APIProjectType, APIActiveUserType } from "admin/api_flow_types"; | ||
import Toast from "libs/toast"; | ||
import messages from "messages"; | ||
import { handleGenericError } from "libs/error_handling"; | ||
import UserSelectionComponent from "admin/user/user_selection_component"; | ||
|
||
type Props = { | ||
project: ?APIProjectType, | ||
onCancel: () => void, | ||
onComplete: () => void, | ||
}; | ||
|
||
type State = { | ||
users: Array<APIUserType>, | ||
selectedUser: ?APIUserType, | ||
usersWithActiveTasks: Array<APIActiveUserType>, | ||
isLoading: boolean, | ||
}; | ||
|
||
class TransferAllTasksModal extends React.PureComponent<Props, State> { | ||
state = { | ||
users: [], | ||
selectedUser: null, | ||
usersWithActiveTasks: [], | ||
isLoading: false, | ||
}; | ||
|
||
componentDidMount() { | ||
this.fetchData(); | ||
} | ||
|
||
async fetchData() { | ||
try { | ||
this.setState({ isLoading: true }); | ||
const users = await getUsers(); | ||
const activeUsers = users.filter(u => u.isActive); | ||
const usersWithActiveTasks = this.props.project | ||
? await getUsersWithActiveTasks(this.props.project.name) | ||
: []; | ||
const sortedUsers = _.sortBy(activeUsers, "lastName"); | ||
this.setState({ | ||
users: sortedUsers, | ||
usersWithActiveTasks, | ||
}); | ||
} catch (error) { | ||
handleGenericError(error); | ||
} finally { | ||
this.setState({ isLoading: false }); | ||
} | ||
} | ||
|
||
transferAllActiveTasks = async () => { | ||
if (!this.state.selectedUser || !this.props.project) { | ||
return; | ||
} | ||
try { | ||
const selectedUser = this.state.selectedUser; | ||
await transferActiveTasksOfProject(this.props.project.name, selectedUser.id); | ||
if (selectedUser) { | ||
Toast.success( | ||
`${messages["project.successful_active_tasks_transfer"]} ${selectedUser.lastName}, ${ | ||
selectedUser.firstName | ||
}`, | ||
); | ||
} | ||
this.props.onComplete(); | ||
} catch (e) { | ||
Toast.error(messages["project.unsuccessful_active_tasks_transfer"]); | ||
} | ||
}; | ||
|
||
renderTableContent() { | ||
const activeUsersWithKey = this.state.usersWithActiveTasks.map(activeUser => ({ | ||
email: activeUser.email, | ||
activeTasks: activeUser.activeTasks, | ||
key: activeUser.email, | ||
})); | ||
const columns = [ | ||
{ | ||
title: "User Email", | ||
dataIndex: "email", | ||
key: "email", | ||
}, | ||
{ | ||
title: "Number of Active Tasks", | ||
dataIndex: "activeTasks", | ||
key: "activeTasks", | ||
}, | ||
]; | ||
return ( | ||
<Table | ||
columns={columns} | ||
dataSource={activeUsersWithKey} | ||
rowKey="email" | ||
pagination={false} | ||
size="small" | ||
/> | ||
); | ||
} | ||
|
||
handleSelectChange = (userId: string) => { | ||
const selectedUser = this.state.users.find(user => user.id === userId); | ||
this.setState({ selectedUser }); | ||
}; | ||
|
||
render() { | ||
const project = this.props.project; | ||
if (!project) { | ||
return ( | ||
<Modal title="Error" visible onOk={this.props.onCancel} onCancel={this.props.onCancel}> | ||
<p>{messages["project.none_selected"]}</p> | ||
</Modal> | ||
); | ||
} else { | ||
const title = `All users with open tasks of ${project.name}`; | ||
return ( | ||
<Modal | ||
title={title} | ||
visible | ||
onCancel={this.props.onCancel} | ||
pagination="false" | ||
footer={ | ||
<div> | ||
<Button | ||
type="primary" | ||
disabled={!this.state.selectedUser} | ||
onClick={this.transferAllActiveTasks} | ||
> | ||
Transfer all tasks | ||
</Button> | ||
<Button onClick={this.props.onCancel}>Close</Button> | ||
</div> | ||
} | ||
> | ||
<div> | ||
{this.state.isLoading ? <Spin size="large" /> : this.renderTableContent()} | ||
<br /> | ||
<br /> | ||
</div> | ||
Select a user to transfer the tasks to: | ||
<div className="control-group"> | ||
<div className="form-group"> | ||
<UserSelectionComponent handleSelection={this.handleSelectChange} /> | ||
</div> | ||
</div> | ||
</Modal> | ||
); | ||
} | ||
} | ||
} | ||
|
||
export default TransferAllTasksModal; |
81 changes: 81 additions & 0 deletions
81
app/assets/javascripts/admin/user/user_selection_component.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
// @flow | ||
|
||
import _ from "lodash"; | ||
import * as React from "react"; | ||
import { Spin, Select } from "antd"; | ||
import { getUsers } from "admin/admin_rest_api"; | ||
import type { APIUserType } from "admin/api_flow_types"; | ||
import { handleGenericError } from "libs/error_handling"; | ||
|
||
const { Option } = Select; | ||
|
||
type Props = { | ||
handleSelection: string => void, | ||
}; | ||
|
||
type State = { | ||
isLoading: boolean, | ||
users: Array<APIUserType>, | ||
currentUserIdValue: string, | ||
}; | ||
|
||
class UserSelectionComponent extends React.PureComponent<Props, State> { | ||
state = { | ||
isLoading: false, | ||
users: [], | ||
currentUserIdValue: "", | ||
}; | ||
|
||
componentDidMount() { | ||
this.fetchData(); | ||
} | ||
|
||
async fetchData() { | ||
try { | ||
this.setState({ isLoading: true }); | ||
const users = await getUsers(); | ||
const activeUsers = users.filter(u => u.isActive); | ||
const sortedUsers = _.sortBy(activeUsers, "lastName"); | ||
this.setState({ | ||
users: sortedUsers, | ||
}); | ||
} catch (error) { | ||
handleGenericError(error); | ||
} finally { | ||
this.setState({ isLoading: false }); | ||
} | ||
} | ||
|
||
handleSelectChange = (userId: string) => { | ||
this.setState({ currentUserIdValue: userId }); | ||
this.props.handleSelection(userId); | ||
}; | ||
|
||
render() { | ||
return this.state.isLoading ? ( | ||
<div className="text-center"> | ||
<Spin size="large" /> | ||
</div> | ||
) : ( | ||
<Select | ||
showSearch | ||
placeholder="Select a New User" | ||
value={this.state.currentUserIdValue} | ||
onChange={this.handleSelectChange} | ||
optionFilterProp="children" | ||
style={{ width: "100%" }} | ||
filterOption={(input, option) => | ||
option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0 | ||
} | ||
> | ||
{this.state.users.map(user => ( | ||
<Option key={user.id} value={user.id}> | ||
{`${user.lastName}, ${user.firstName} ${user.email}`} | ||
</Option> | ||
))} | ||
</Select> | ||
); | ||
} | ||
} | ||
|
||
export default UserSelectionComponent; |
Oops, something went wrong.