MANAGING A TEAM WITH NODE.JS (PART 2)
Tags: javascript, node, webdev, database
Published: 2023-06-03 10:02:07

In case you missed the first part of this article, [read here to catch up](https://dev.to/candie_code/managing-a-team-with-nodejs-kfm)

In the last article, we were able to 

- Create a team
- Send team invitation,
- Accept team invitation

In this article, we will be talking about

- Rejecting Invitation
- Leaving a team
- Removing member from a team
- Managing roles in a team

Quickly, 

- let us create two new users, 
- Send invitation from one user to another, 
- then we continue from there

PAYLOAD

```js
// user 1
{
    "name": "john doe",
    "email": "johndoe@gmail.com",
    "password": "test1234"
}
// user 2
{
    "name": "jane doe",
    "email": "janedoe@gmail.com",
    "password": "test1234"
}
```

## STEP 1
Let us send invitation from john doe to jane doe

PAYLOAD:

```js
{
    "email": "janedoe@gmail.com"
}
```

RESPONSE:

```js
{
    "status": "success",
    "data": "invitation sent to janedoe@gmail.com"
}
```

## STEP 2
Let us take a look at the user object for john and jane

```js
// john
 "data": {
        "user": {
            "_id": "647afd1914c2f48dfc18c846",
            "name": "john doe",
            "email": "johndoe@gmail.com",
            "teams": [
                {
                    "_id": "647aff306dcdb9d9546c7b1b",
                    "name": "nodejs team",
                    "members": [
                        {
                            "name": "john doe",
                            "email": "johndoe@gmail.com",
                            "id": "647afd1914c2f48dfc18c846",
                            "role": "admin"
                        }
                    ],
                    "userId": "647afd1914c2f48dfc18c846",
                    "createdAt": "2023-06-03T08:51:44.822Z",
                    "__v": 1,
                    "id": "647aff306dcdb9d9546c7b1b"
                }
            ],
            "invitations": [],
            "createdAt": "2023-06-03T08:41:06.544Z",
            "__v": 5,
            "id": "647afd1914c2f48dfc18c846"
        }
    }
```
Inside john's object, the team array contains a team with only one member, which is john, and he is the admin

```js
{
    "status": "success",
    "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjY0N2FmZDQwMTRjMmY0OGRmYzE4Yzg0OSIsImlhdCI6MTY4NTc4MjUwMiwiZXhwIjoxNjkzNTU4NTAyfQ.NABFYJzvEvOfWUwy5cBnecIrHgDflrF3QVaAsYw_rcc",
    "data": {
        "user": {
            "_id": "647afd4014c2f48dfc18c849",
            "name": "jane doe",
            "email": "janedoe@gmail.com",
            "teams": [],
            "invitations": [
                {
                    "_id": "647aff306dcdb9d9546c7b1b",
                    "name": "nodejs team",
                    "id": "647aff306dcdb9d9546c7b1b"
                }
            ],
            "createdAt": "2023-06-03T08:41:06.544Z",
            "__v": 2,
            "id": "647afd4014c2f48dfc18c849"
        }
    }
}
```
In the jane user object, the team array is empty, because obviously, jane is not a member of any team yet,

And the invitation array contains one team, `nodejs team`, the invitation that was sent by john.

## STEP 3

### REJECTING INVITATION
Now let us compute how to reject the invitation sent by john.

Back to our team `controller file`

NOTE: Basically the concept is finding the index of the team in the invitation array and deleting it with the `Array.splice()` method.

```js
// reject team invitation
exports.rejectInvitation = async (req, res) => {
  // find user in db

  const user = await User.findById(req.user.id);

  // find team in db
  // teamID will be gotted as a parameter when we run the request
  const team = await Team.findById(req.params.teamId);

  // check if team exists
  if (!team) {
    return res.status(404).json({
      status: "failed",
      message: "team not found",
    });
  }

  // if team exists, let us find the index of the team in the invitations array
  const teamIndex = user.invitations.indexOf(team.id);

  // remove team id from the invitations array

  user.invitations.splice(teamIndex);

  // save changes
  await user.save();

  return res.status(200).json({
    status: "success",
    message: "invitation rejected",
  });
};
```

## STEP 4
Let us create a route for this request and run it on post man,
NOTE: Pass the team ID as a parameter for this request

```js
router.post(
  "/reject-invitation/:teamId",
  middleware.protect,
  teamController.rejectInvitation
);
```

RESPONSE:

```js
{
    "status": "success",
    "message": "invitation rejected"
}
```

To confirm the invitation was rejected, let us take a look at the user object for jane again.

```js
"data": {
        "user": {
            "_id": "647afd4014c2f48dfc18c849",
            "name": "jane doe",
            "email": "janedoe@gmail.com",
            "teams": [],
            "invitations": [],
            "createdAt": "2023-06-03T08:41:06.544Z",
            "__v": 3,
            "id": "647afd4014c2f48dfc18c849"
        }
    }
```

Now the invitation array is empty, because we have successfully deleted the team from the invitation array.

## STEP 5

### LEAVING A TEAM
To compute this, let us send a new invitation to jane, then login to jane account to accept the invitation, then we can proceed to leave a team.

Because you have to be a member of a team to leave a team.

RESPONSE: 

```js
{
    "status": "success",
    "data": "invitation sent to janedoe@gmail.com"
}
```

RESPONSE:

```js
{
    "status": "success",
    "message": "invitation to nodejs team accepted"
}
```

Let us take a look at jane user object to be sure she's now a member of nodejs team

```js
"data": {
        "user": {
            "_id": "647afd4014c2f48dfc18c849",
            "name": "jane doe",
            "email": "janedoe@gmail.com",
            "teams": [
                {
                    "_id": "647aff306dcdb9d9546c7b1b",
                    "name": "nodejs team",
                    "members": [
                        {
                            "name": "john doe",
                            "email": "johndoe@gmail.com",
                            "id": "647afd1914c2f48dfc18c846",
                            "role": "admin"
                        },
                        {
                            "name": "jane doe",
                            "email": "janedoe@gmail.com",
                            "id": "647afd4014c2f48dfc18c849",
                            "role": "member"
                        }
                    ],
                    "userId": "647afd1914c2f48dfc18c846",
                    "createdAt": "2023-06-03T08:51:44.822Z",
                    "__v": 2,
                    "id": "647aff306dcdb9d9546c7b1b"
                }
            ],
            "invitations": [],
            "createdAt": "2023-06-03T08:41:06.544Z",
            "__v": 5,
            "id": "647afd4014c2f48dfc18c849"
        }
    }
```
As you can see, the node.js team has two members, the admin which is john doe, and a jane doe, a member.

## STEP 6
Let us proceed to our controller and compute leaving a team

```js
exports.leaveTeam = async (req, res) => {
  // find user in db
  const user = await User.findById(req.user.id);

  // find team in db
  const team = await Team.findById(req.params.teamId);

  // check if team exists
  if (!team) {
    return res.status(404).json({
      status: "failed",
      message: "team not found",
    });
  }

  // if team exist, check if user is a member of the team
  const member = team.members.find((item) => item.id === user.id);

  // if user is not a member, throw an error and terminate
  if (!member) {
    return res.status(400).json({
      status: "failed",
      message: `you are not a member of ${team.name.toUpperCase()}`,
    });
  }

  // if user is a member, find index of team in the team array on the user object
  const teamIndex = user.teams.indexOf(team.id);

  // delete team from team array
  user.teams.splice(teamIndex);

  // remove user from member array on the team
  const newMembers = team.members.filter((item) => item.id !== member.id);
  team.members = newMembers;

  // save changes
  await team.save();
  await user.save();

  return res.status(200).json({
    status: "success",
    message: `successfully exited ${team.name}`,
  });
};
```

## STEP 7
Let us create a route for this request and run it on postman

```js
router.post(
  "/leave-team/:teamId",
  middleware.protect,
  teamController.leaveTeam
);
```

RESPONSE: 

```js
{
    "status": "success",
    "message": "successfully exited nodejs team"
}
```
To confirm it was successful, let us take a look at the jane user object once again,

```js
    "data": {
        "user": {
            "_id": "647afd4014c2f48dfc18c849",
            "name": "jane doe",
            "email": "janedoe@gmail.com",
            "teams": [],
            "invitations": [],
            "createdAt": "2023-06-03T08:41:06.544Z",
            "__v": 6,
            "id": "647afd4014c2f48dfc18c849"
        }
    }
```
As expected, the teams array is empty, which means the request was successful.

## STEP 8

### REMOVING MEMBER FROM A TEAM
Let us repeat the process of sending an invitation to jane and accepting the invitation, then logging in back to john account to remove jane from the team

```js
    "data": {
        "user": {
            "_id": "647afd4014c2f48dfc18c849",
            "name": "jane doe",
            "email": "janedoe@gmail.com",
            "teams": [
                {
                    "_id": "647aff306dcdb9d9546c7b1b",
                    "name": "nodejs team",
                    "members": [
                        {
                            "name": "john doe",
                            "email": "johndoe@gmail.com",
                            "id": "647afd1914c2f48dfc18c846",
                            "role": "admin"
                        },
                        {
                            "name": "jane doe",
                            "email": "janedoe@gmail.com",
                            "id": "647afd4014c2f48dfc18c849",
                            "role": "member"
                        }
                    ],
                    "userId": "647afd1914c2f48dfc18c846",
                    "createdAt": "2023-06-03T08:51:44.822Z",
                    "__v": 4,
                    "id": "647aff306dcdb9d9546c7b1b"
                }
            ],
            "invitations": [],
            "createdAt": "2023-06-03T08:41:06.544Z",
            "__v": 8,
            "id": "647afd4014c2f48dfc18c849"
        }
    }
```

Jane is already a member of the nodejs team, now let us log back in to john's account to compute removing jane from the nodejs team.

```js
exports.removeMemberFromTeam = async (req, res) => {
  // extract userId and teamId from the request query
  const { userId, teamId } = req.query;

  // find team in db
  const team = await Team.findById(teamId);
  const user = await User.findById(userId);

  // check if team exist
  if (!team) {
    return res.status(404).json({
      status: "failed",
      message: "team not found",
    });
  }

  // if team exist, check if user is a member of the team
  const member = team.members.find((item) => item.id === userId);

  // if user is not a member, throw an error and terminate
  if (!member) {
    return res.status(400).json({
      status: "failed",
      message: `you are not a member of ${team.name.toUpperCase()}`,
    });
  }

  // if user is a member, filter user out of the team
  const newTeamMember = team.members.filter((item) => item.id !== userId);
  team.members = newTeamMember;

  // remove teamId from teams array on user object
  const teamIndex = user.teams.indexOf(team.id);
  user.teams.splice(teamIndex);

  // save changes
  await team.save();
  await user.save();

  return res.status(200).json({
    status: "success",
    message: `${user.name} successfully removed from ${team.name}`,
  });
};
```

## STEP 9
Let us create a route for this request and run it in postman

```js
router.post(
  "/remove-member",
  middleware.protect,
  teamController.removeMemberFromTeam
);
```

```js
// request url
http://localhost:8000/api/v1/team/remove-member?teamId=647aff306dcdb9d9546c7b1b&userId=647afd4014c2f48dfc18c849
```

RESPONSE: 

```js
{
    "status": "success",
    "message": "jane doe successfully removed from nodejs team"
}
```
To confirm, let us take a look at the nodejs team again

```js
"teams": [
                {
                    "_id": "647aff306dcdb9d9546c7b1b",
                    "name": "nodejs team",
                    "members": [
                        {
                            "name": "john doe",
                            "email": "johndoe@gmail.com",
                            "id": "647afd1914c2f48dfc18c846",
                            "role": "admin"
                        }
                    ],
```
Nodejs team only contains john, who is the admin, meaning our logic works too.

So far we have been able to

- Create a team
- Send team invitation
- Accept team invitation
- Reject team invitation
- Leave team
- Remove member from team

In the final part of this article, we will be talking about **ROLE MANAGEMENT IN A TEAM**

I hope you are excited.

Meanwhile Let me know your thoughts on this in the comments section.

[Get my source code here](https://github.com/MrCandie/team-management)
[View my API documentation here](https://documenter.getpostman.com/view/23843313/2s93sW8b4R)