Просмотр исходного кода

team members, bug in fetching team

Peter Holmberg 7 лет назад
Родитель
Сommit
59b3bfd342

+ 44 - 24
public/app/features/teams/TeamMembers.tsx

@@ -1,12 +1,21 @@
 import React, { PureComponent } from 'react';
+import { connect } from 'react-redux';
 import { hot } from 'react-hot-loader';
 import SlideDown from 'app/core/components/Animations/SlideDown';
 import { UserPicker, User } from 'app/core/components/Picker/UserPicker';
 import DeleteButton from 'app/core/components/DeleteButton/DeleteButton';
 import { Team, TeamMember } from '../../types';
+import { loadTeamMembers, addTeamMember, removeTeamMember, setSearchMemberQuery } from './state/actions';
+import { getSearchMemberQuery, getTeam } from './state/selectors';
+import { getRouteParamsId } from '../../core/selectors/location';
 
 interface Props {
   team: Team;
+  searchMemberQuery: string;
+  loadTeamMembers: typeof loadTeamMembers;
+  addTeamMember: typeof addTeamMember;
+  removeTeamMember: typeof removeTeamMember;
+  setSearchMemberQuery: typeof setSearchMemberQuery;
 }
 
 interface State {
@@ -21,20 +30,29 @@ export class TeamMembers extends PureComponent<Props, State> {
   }
 
   componentDidMount() {
-    // this.props.team.loadMembers();
+    this.props.loadTeamMembers();
   }
 
-  onSearchQueryChange = evt => {
-    // this.props.team.setSearchQuery(evt.target.value);
+  onSearchQueryChange = event => {
+    this.props.setSearchMemberQuery(event.target.value);
   };
 
   removeMember(member: TeamMember) {
-    // this.props.team.removeMember(member);
+    this.props.removeTeamMember(member.userId);
   }
 
-  removeMemberConfirmed(member: TeamMember) {
-    // this.props.team.removeMember(member);
-  }
+  onToggleAdding = () => {
+    this.setState({ isAdding: !this.state.isAdding });
+  };
+
+  onUserSelected = (user: User) => {
+    this.setState({ newTeamMember: user });
+  };
+
+  onAddUserToTeam = async () => {
+    this.props.addTeamMember(this.state.newTeamMember.id);
+    this.setState({ newTeamMember: null });
+  };
 
   renderMember(member: TeamMember) {
     return (
@@ -51,23 +69,9 @@ export class TeamMembers extends PureComponent<Props, State> {
     );
   }
 
-  onToggleAdding = () => {
-    this.setState({ isAdding: !this.state.isAdding });
-  };
-
-  onUserSelected = (user: User) => {
-    this.setState({ newTeamMember: user });
-  };
-
-  onAddUserToTeam = async () => {
-    // await this.props.team.addMember(this.state.newTeamMember.id);
-    // await this.props.team.loadMembers();
-    // this.setState({ newTeamMember: null });
-  };
-
   render() {
     const { newTeamMember, isAdding } = this.state;
-    const { team } = this.props;
+    const { team, searchMemberQuery } = this.props;
     const newTeamMemberValue = newTeamMember && newTeamMember.id.toString();
 
     return (
@@ -79,7 +83,7 @@ export class TeamMembers extends PureComponent<Props, State> {
                 type="text"
                 className="gf-form-input"
                 placeholder="Search members"
-                value={team.search}
+                value={searchMemberQuery}
                 onChange={this.onSearchQueryChange}
               />
               <i className="gf-form-input-icon fa fa-search" />
@@ -129,4 +133,20 @@ export class TeamMembers extends PureComponent<Props, State> {
   }
 }
 
-export default hot(module)(TeamMembers);
+function mapStateToProps(state) {
+  const teamId = getRouteParamsId(state.location);
+
+  return {
+    team: getTeam(state.team, teamId),
+    searchMemberQuery: getSearchMemberQuery(state.team),
+  };
+}
+
+const mapDispatchToProps = {
+  loadTeamMembers,
+  addTeamMember,
+  removeTeamMember,
+  setSearchMemberQuery,
+};
+
+export default hot(module)(connect(mapStateToProps, mapDispatchToProps)(TeamMembers));

+ 2 - 2
public/app/features/teams/TeamPages.tsx

@@ -63,7 +63,7 @@ export class TeamPages extends PureComponent<Props, State> {
 
     switch (currentPage) {
       case PageTypes.Members:
-        return <TeamMembers team={team} />;
+        return <TeamMembers />;
 
       case PageTypes.Settings:
         return <TeamSettings team={team} />;
@@ -95,7 +95,7 @@ function mapStateToProps(state) {
     navModel: getNavModel(state.navIndex, `team-${pageName}-${teamId}`),
     teamId: teamId,
     pageName: pageName,
-    team: getTeam(state.team),
+    team: getTeam(state.team, teamId),
   };
 }
 

+ 10 - 0
public/app/features/teams/__mocks__/teamMocks.ts

@@ -30,3 +30,13 @@ export const getMockTeam = (): Team => {
     groups: [],
   };
 };
+
+export const getMockTeamMember = () => {
+  return {
+    userId: 1,
+    teamId: 1,
+    avatarUrl: 'some/url/',
+    email: 'test@test.com',
+    login: 'testUser',
+  };
+};

+ 65 - 2
public/app/features/teams/state/actions.ts

@@ -1,6 +1,6 @@
 import { ThunkAction } from 'redux-thunk';
 import { getBackendSrv } from 'app/core/services/backend_srv';
-import { NavModelItem, StoreState, Team } from '../../../types';
+import { NavModelItem, StoreState, Team, TeamMember } from '../../../types';
 import { updateNavIndex } from '../../../core/actions';
 import { UpdateNavIndexAction } from '../../../core/actions/navModel';
 
@@ -8,6 +8,8 @@ export enum ActionTypes {
   LoadTeams = 'LOAD_TEAMS',
   LoadTeam = 'LOAD_TEAM',
   SetSearchQuery = 'SET_SEARCH_QUERY',
+  SetSearchMemberQuery = 'SET_SEARCH_MEMBER_QUERY',
+  LoadTeamMembers = 'TEAM_MEMBERS_LOADED',
 }
 
 export interface LoadTeamsAction {
@@ -20,12 +22,27 @@ export interface LoadTeamAction {
   payload: Team;
 }
 
+export interface LoadTeamMembersAction {
+  type: ActionTypes.LoadTeamMembers;
+  payload: TeamMember[];
+}
+
 export interface SetSearchQueryAction {
   type: ActionTypes.SetSearchQuery;
   payload: string;
 }
 
-export type Action = LoadTeamsAction | SetSearchQueryAction | LoadTeamAction;
+export interface SetSearchMemberQueryAction {
+  type: ActionTypes.SetSearchMemberQuery;
+  payload: string;
+}
+
+export type Action =
+  | LoadTeamsAction
+  | SetSearchQueryAction
+  | LoadTeamAction
+  | LoadTeamMembersAction
+  | SetSearchMemberQueryAction;
 
 type ThunkResult<R> = ThunkAction<R, StoreState, undefined, Action | UpdateNavIndexAction>;
 
@@ -39,6 +56,16 @@ const teamLoaded = (team: Team): LoadTeamAction => ({
   payload: team,
 });
 
+const teamMembersLoaded = (teamMembers: TeamMember[]): LoadTeamMembersAction => ({
+  type: ActionTypes.LoadTeamMembers,
+  payload: teamMembers,
+});
+
+export const setSearchMemberQuery = (searchQuery: string): SetSearchMemberQueryAction => ({
+  type: ActionTypes.SetSearchMemberQuery,
+  payload: searchQuery,
+});
+
 export const setSearchQuery = (searchQuery: string): SetSearchQueryAction => ({
   type: ActionTypes.SetSearchQuery,
   payload: searchQuery,
@@ -89,6 +116,42 @@ export function loadTeam(id: number): ThunkResult<void> {
   };
 }
 
+export function loadTeamMembers(): ThunkResult<void> {
+  return async (dispatch, getStore) => {
+    const team = getStore().team.team;
+
+    await getBackendSrv()
+      .get(`/api/teams/${team.id}/members`)
+      .then(response => {
+        dispatch(teamMembersLoaded(response));
+      });
+  };
+}
+
+export function addTeamMember(id: number): ThunkResult<void> {
+  return async (dispatch, getStore) => {
+    const team = getStore().team.team;
+
+    await getBackendSrv()
+      .post(`/api/teams/${team.id}/members`, { userId: id })
+      .then(() => {
+        dispatch(loadTeamMembers());
+      });
+  };
+}
+
+export function removeTeamMember(id: number): ThunkResult<void> {
+  return async (dispatch, getStore) => {
+    const team = getStore().team.team;
+
+    await getBackendSrv()
+      .delete(`/api/teams/${team.id}/members/${id}`)
+      .then(() => {
+        dispatch(loadTeamMembers());
+      });
+  };
+}
+
 export function deleteTeam(id: number): ThunkResult<void> {
   return async dispatch => {
     await getBackendSrv()

+ 24 - 13
public/app/features/teams/state/reducers.test.ts

@@ -1,20 +1,10 @@
 import { Action, ActionTypes } from './actions';
-import { initialTeamsState, teamsReducer } from './reducers';
+import { initialTeamsState, initialTeamState, teamReducer, teamsReducer } from './reducers';
+import { getMockTeam, getMockTeamMember } from '../__mocks__/teamMocks';
 
 describe('teams reducer', () => {
   it('should set teams', () => {
-    const payload = [
-      {
-        id: 1,
-        name: 'test',
-        avatarUrl: 'some/url/',
-        email: 'test@test.com',
-        memberCount: 1,
-        search: '',
-        members: [],
-        groups: [],
-      },
-    ];
+    const payload = [getMockTeam()];
 
     const action: Action = {
       type: ActionTypes.LoadTeams,
@@ -39,3 +29,24 @@ describe('teams reducer', () => {
     expect(result.searchQuery).toEqual('test');
   });
 });
+
+describe('team reducer', () => {
+  it('should set team members', () => {
+    const mockTeamMember = getMockTeamMember();
+    const mockTeam = getMockTeam();
+    const state = {
+      ...initialTeamState,
+      team: mockTeam,
+    };
+
+    const action: Action = {
+      type: ActionTypes.LoadTeamMembers,
+      payload: [mockTeamMember],
+    };
+
+    const result = teamReducer(state, action);
+    const expectedState = { team: { ...mockTeam, members: [mockTeamMember] }, searchQuery: '' };
+
+    expect(result).toEqual(expectedState);
+  });
+});

+ 7 - 1
public/app/features/teams/state/reducers.ts

@@ -2,7 +2,7 @@ import { Team, TeamsState, TeamState } from '../../../types';
 import { Action, ActionTypes } from './actions';
 
 export const initialTeamsState: TeamsState = { teams: [], searchQuery: '' };
-export const initialTeamState: TeamState = { team: {} as Team, searchQuery: '' };
+export const initialTeamState: TeamState = { team: {} as Team, searchMemberQuery: '' };
 
 export const teamsReducer = (state = initialTeamsState, action: Action): TeamsState => {
   switch (action.type) {
@@ -19,6 +19,12 @@ export const teamReducer = (state = initialTeamState, action: Action): TeamState
   switch (action.type) {
     case ActionTypes.LoadTeam:
       return { ...state, team: action.payload };
+
+    case ActionTypes.LoadTeamMembers:
+      return { ...state, team: { ...state.team, members: action.payload } };
+
+    case ActionTypes.SetSearchMemberQuery:
+      return { ...state, searchMemberQuery: action.payload };
   }
 
   return state;

+ 7 - 1
public/app/features/teams/state/selectors.ts

@@ -1,6 +1,12 @@
 export const getSearchQuery = state => state.searchQuery;
+export const getSearchMemberQuery = state => state.searchMemberQuery;
 
-export const getTeam = state => state.team;
+export const getTeam = (state, currentTeamId) => {
+  if (state.team.id === currentTeamId) {
+    console.log('yes');
+    return state.team;
+  }
+};
 
 export const getTeams = state => {
   const regex = RegExp(state.searchQuery, 'i');

+ 2 - 1
public/app/types/index.ts

@@ -124,7 +124,7 @@ export interface TeamsState {
 
 export interface TeamState {
   team: Team;
-  searchQuery: string;
+  searchMemberQuery: string;
 }
 
 export interface StoreState {
@@ -132,4 +132,5 @@ export interface StoreState {
   location: LocationState;
   alertRules: AlertRulesState;
   teams: TeamsState;
+  team: TeamState;
 }