TeamMembers.tsx 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  1. import React, { PureComponent } from 'react';
  2. import { connect } from 'react-redux';
  3. import SlideDown from 'app/core/components/Animations/SlideDown';
  4. import { UserPicker, User } from 'app/core/components/Picker/UserPicker';
  5. import DeleteButton from 'app/core/components/DeleteButton/DeleteButton';
  6. import { TeamMember } from '../../types';
  7. import { loadTeamMembers, addTeamMember, removeTeamMember, setSearchMemberQuery } from './state/actions';
  8. import { getSearchMemberQuery, getTeamMembers } from './state/selectors';
  9. export interface Props {
  10. members: TeamMember[];
  11. searchMemberQuery: string;
  12. loadTeamMembers: typeof loadTeamMembers;
  13. addTeamMember: typeof addTeamMember;
  14. removeTeamMember: typeof removeTeamMember;
  15. setSearchMemberQuery: typeof setSearchMemberQuery;
  16. }
  17. interface State {
  18. isAdding: boolean;
  19. newTeamMember?: User;
  20. }
  21. export class TeamMembers extends PureComponent<Props, State> {
  22. constructor(props) {
  23. super(props);
  24. this.state = { isAdding: false, newTeamMember: null };
  25. }
  26. componentDidMount() {
  27. this.props.loadTeamMembers();
  28. }
  29. onSearchQueryChange = event => {
  30. this.props.setSearchMemberQuery(event.target.value);
  31. };
  32. onRemoveMember(member: TeamMember) {
  33. this.props.removeTeamMember(member.userId);
  34. }
  35. onToggleAdding = () => {
  36. this.setState({ isAdding: !this.state.isAdding });
  37. };
  38. onUserSelected = (user: User) => {
  39. this.setState({ newTeamMember: user });
  40. };
  41. onAddUserToTeam = async () => {
  42. this.props.addTeamMember(this.state.newTeamMember.id);
  43. this.setState({ newTeamMember: null });
  44. };
  45. renderMember(member: TeamMember) {
  46. return (
  47. <tr key={member.userId}>
  48. <td className="width-4 text-center">
  49. <img className="filter-table__avatar" src={member.avatarUrl} />
  50. </td>
  51. <td>{member.login}</td>
  52. <td>{member.email}</td>
  53. <td className="text-right">
  54. <DeleteButton onConfirmDelete={() => this.onRemoveMember(member)} />
  55. </td>
  56. </tr>
  57. );
  58. }
  59. render() {
  60. const { newTeamMember, isAdding } = this.state;
  61. const { searchMemberQuery, members } = this.props;
  62. const newTeamMemberValue = newTeamMember && newTeamMember.id.toString();
  63. return (
  64. <div>
  65. <div className="page-action-bar">
  66. <div className="gf-form gf-form--grow">
  67. <label className="gf-form--has-input-icon gf-form--grow">
  68. <input
  69. type="text"
  70. className="gf-form-input"
  71. placeholder="Search members"
  72. value={searchMemberQuery}
  73. onChange={this.onSearchQueryChange}
  74. />
  75. <i className="gf-form-input-icon fa fa-search" />
  76. </label>
  77. </div>
  78. <div className="page-action-bar__spacer" />
  79. <button className="btn btn-success pull-right" onClick={this.onToggleAdding} disabled={isAdding}>
  80. <i className="fa fa-plus" /> Add a member
  81. </button>
  82. </div>
  83. <SlideDown in={isAdding}>
  84. <div className="cta-form">
  85. <button className="cta-form__close btn btn-transparent" onClick={this.onToggleAdding}>
  86. <i className="fa fa-close" />
  87. </button>
  88. <h5>Add Team Member</h5>
  89. <div className="gf-form-inline">
  90. <UserPicker onSelected={this.onUserSelected} className="width-30" value={newTeamMemberValue} />
  91. {this.state.newTeamMember && (
  92. <button className="btn btn-success gf-form-btn" type="submit" onClick={this.onAddUserToTeam}>
  93. Add to team
  94. </button>
  95. )}
  96. </div>
  97. </div>
  98. </SlideDown>
  99. <div className="admin-list-table">
  100. <table className="filter-table filter-table--hover form-inline">
  101. <thead>
  102. <tr>
  103. <th />
  104. <th>Name</th>
  105. <th>Email</th>
  106. <th style={{ width: '1%' }} />
  107. </tr>
  108. </thead>
  109. <tbody>{members && members.map(member => this.renderMember(member))}</tbody>
  110. </table>
  111. </div>
  112. </div>
  113. );
  114. }
  115. }
  116. function mapStateToProps(state) {
  117. return {
  118. members: getTeamMembers(state.team),
  119. searchMemberQuery: getSearchMemberQuery(state.team),
  120. };
  121. }
  122. const mapDispatchToProps = {
  123. loadTeamMembers,
  124. addTeamMember,
  125. removeTeamMember,
  126. setSearchMemberQuery,
  127. };
  128. export default connect(mapStateToProps, mapDispatchToProps)(TeamMembers);