From a9e60d2a875e05e7f0939d99b7e75f750d00f277 Mon Sep 17 00:00:00 2001
From: Larbi Gharib <larbi.gharib@savoirfairelinux.com>
Date: Fri, 23 Oct 2020 15:02:33 -0400
Subject: [PATCH] Add user to group

Change-Id: I640daaa58d820e572919817fec70ea3f380f4d9b
---
 .../public/locales/en/translation.json        |   5 +-
 .../public/locales/fr/translation.json        |   5 +-
 jams-react-client/src/api.js                  |  44 +--
 .../components/devicesStyle.js                |   4 +-
 jams-react-client/src/auth.js                 |  19 +-
 .../CustomPopupState/CustomPopupState.js      |   3 +-
 .../CustomizedSteppers/CustomizedSteppers.js  |  20 +-
 .../src/components/Drawer/Drawer.js           | 312 +++---------------
 .../LanguagePicker/LanguagePicker.js          |  13 -
 .../components/Navbars/AdminNavbarLinks.js    |  21 +-
 .../src/components/Navbars/Navbar.js          |  12 -
 .../PasswordDialog/PasswordDialog.js          |   3 +-
 .../ServerParameters/ServerParameters.js      |   2 +-
 jams-react-client/src/configured.route.js     |   9 +-
 jams-react-client/src/layouts/Admin.js        |  28 +-
 jams-react-client/src/layouts/SignIn.js       |   3 -
 jams-react-client/src/layouts/SignUp.js       |  12 +-
 .../src/views/Blueprint/Blueprint.js          |   6 -
 .../Blueprint/EditBlueprintConfiguration.js   |  23 +-
 .../Blueprint/EditBlueprintPermissions.js     |   8 +-
 .../src/views/Blueprints/Blueprints.js        |  11 +-
 .../src/views/Contacts/Contacts.js            |  93 +++++-
 .../src/views/Groups/EditGroup.js             | 138 ++++++--
 jams-react-client/src/views/Groups/Groups.js  |  70 ++--
 .../src/views/Settings/General.js             |   4 -
 .../src/views/Settings/Settings.js            |   5 -
 .../src/views/Settings/Subscription.js        |  13 +-
 .../views/UserProfile/DisplayUserProfile.js   | 257 ++++++++-------
 .../UserProfile/EditCreateUserProfile.js      |  68 ++--
 .../src/views/UserProfile/UserProfile.js      |  29 +-
 jams-react-client/src/views/Users/Users.js    |  33 +-
 31 files changed, 536 insertions(+), 737 deletions(-)

diff --git a/jams-react-client/public/locales/en/translation.json b/jams-react-client/public/locales/en/translation.json
index 43510d7e..970cc1a4 100644
--- a/jams-react-client/public/locales/en/translation.json
+++ b/jams-react-client/public/locales/en/translation.json
@@ -231,5 +231,8 @@
     "validate": "Validate",
     "change_language": "Change language",
     "general": "General",
-    "device_id": "Device Id"
+    "device_id": "Device Id",
+    "add_user_to_group": "Add user to group ...",
+    "add_user_to_a_group": "Add user to a group",
+    "remove_from_group": "Remove from group"
 }
diff --git a/jams-react-client/public/locales/fr/translation.json b/jams-react-client/public/locales/fr/translation.json
index c2a4e407..29ccf3e9 100644
--- a/jams-react-client/public/locales/fr/translation.json
+++ b/jams-react-client/public/locales/fr/translation.json
@@ -231,5 +231,8 @@
     "validate": "Valider",
     "change_language": "Change language",
     "general": "General",
-    "device_id": "Identifiant de l'appareil"
+    "device_id": "Identifiant de l'appareil",
+    "add_user_to_group": "Add user to group ...",
+    "add_user_to_a_group": "Add user to a group",
+    "remove_from_group": "Remove from group"
 }
diff --git a/jams-react-client/src/api.js b/jams-react-client/src/api.js
index 74d30367..d4325371 100644
--- a/jams-react-client/src/api.js
+++ b/jams-react-client/src/api.js
@@ -19,41 +19,15 @@
  */
 
 import {
-  uri,
-  current_uri,
-  backend_address,
   url_path,
   url_port,
-  api_path_post_install_admin,
-  api_path_post_auth_login,
-  api_path_post_install_ca,
-  api_path_post_install_auth,
-  api_path_post_install_server,
-  api_path_get_install_lastKnownStep,
   api_path_get_auth_user_search,
   api_path_get_auth_devices,
   api_path_get_admin_devices,
-  api_path_delete_admin_user_revoke,
-  api_path_delete_auth_user_revoke,
-  api_path_delete_auth_device_revoke,
-  api_path_rename_device,
-  api_path_get_server_status,
-  api_path_get_post_configuration_auth_service,
-  api_path_get_post_configuration_global_settings,
   api_path_post_configuration_change_password,
-  api_path_post_configuration_register_license,
-  api_path_get_subscription_status,
-  api_path_get_directories,
-  api_path_get_needs_update,
-  api_path_get_start_update,
   api_path_post_create_user,
-  api_path_get_auth_user,
   api_path_post_update_user,
-  api_path_get_exists_user,
   api_path_get_user_directory_search,
-  api_path_post_create_user_profile,
-  api_path_put_update_user_profile,
-  api_path_get_user_search,
   api_path_blueprints,
 } from "globalUrls";
 
@@ -92,20 +66,20 @@ export default function configApiCall(
   // pass data in the header
   if (data) {
     if (
-      api_path == api_path_get_user_directory_search ||
-      api_path == api_path_get_auth_user_search ||
-      (api_path == api_path_post_create_user && request_type == "POST") ||
-      (api_path.includes(api_path_blueprints) && request_type == "POST") ||
-      api_path == api_path_post_update_user ||
-      api_path == api_path_get_auth_devices ||
-      api_path == api_path_get_admin_devices ||
-      api_path == api_path_post_configuration_change_password
+      api_path === api_path_get_user_directory_search ||
+      api_path === api_path_get_auth_user_search ||
+      (api_path === api_path_post_create_user && request_type === "POST") ||
+      (api_path.includes(api_path_blueprints) && request_type === "POST") ||
+      api_path === api_path_post_update_user ||
+      api_path === api_path_get_auth_devices ||
+      api_path === api_path_get_admin_devices ||
+      api_path === api_path_post_configuration_change_password
     )
       isSearch = true;
 
     // search dataType
     if (isSearch) {
-      if (request_type == "GET" || request_type == "DELETE") {
+      if (request_type === "GET" || request_type === "DELETE") {
         config["params"] = data;
       } else {
         config["data"] = data;
diff --git a/jams-react-client/src/assets/jss/material-dashboard-react/components/devicesStyle.js b/jams-react-client/src/assets/jss/material-dashboard-react/components/devicesStyle.js
index a5699e82..e3505a06 100644
--- a/jams-react-client/src/assets/jss/material-dashboard-react/components/devicesStyle.js
+++ b/jams-react-client/src/assets/jss/material-dashboard-react/components/devicesStyle.js
@@ -26,11 +26,11 @@ const tasksStyle = {
   },
   tableCell: {
     ...defaultFont,
-    padding: "8px",
     verticalAlign: "middle",
     border: "none",
     lineHeight: "1.42857143",
-    fontSize: "14px"
+    fontSize: "14px",
+    textAlign: "left"
   },
   tableCellRTL: {
     textAlign: "right"
diff --git a/jams-react-client/src/auth.js b/jams-react-client/src/auth.js
index 217e576b..b7164001 100644
--- a/jams-react-client/src/auth.js
+++ b/jams-react-client/src/auth.js
@@ -8,8 +8,7 @@ import {
     api_path_get_install_lastKnownStep,
     api_path_get_directories,
     api_path_get_subscription_status,
-    api_path_get_needs_update,
-    api_path_get_start_update
+    api_path_get_needs_update
 } from "globalUrls";
 
 class Auth {
@@ -56,9 +55,9 @@ class Auth {
     login(jsonData, cb) {
         this.deleteJWT()
         axios(configApiCall(api_path_post_auth_login, "POST", jsonData, null)).then((response) => {
-            if(response.status == 200){
+            if(response.status === 200){
                 this.setJWT(response.data['access_token']);
-                this.setScope(response.data["scope"] == "ADMIN" ? true : false);
+                this.setScope(response.data["scope"] === "ADMIN" ? true : false);
                 this.setUsername(jsonData.username);
                 this.authenticated = true;
             }
@@ -85,10 +84,10 @@ class Auth {
 
     checkDirectoryType(cb){
         axios(configApiCall(api_path_get_directories, "GET", null, null)).then((response) => {
-            if (response.data.length == 1) {
+            if (response.data.length === 1) {
                 this.setLocalDirectory(true);
             }
-            else if (response.data.length == 2){
+            else if (response.data.length === 2){
                 this.setLocalDirectory(false);
             }else{
                 console.log("Error getting on checkDirectoryType: Size of directory types is " + response.data.length);
@@ -102,12 +101,12 @@ class Auth {
 
     checkAdminAccountStatus(cb) {
         axios(configApiCall(api_path_post_install_admin, "GET", null, null)).then((response) => {
-            if (response['headers']['showlogin'] == "true") {
+            if (response['headers']['showlogin'] === "true") {
                 this.admin = true;
             }
             cb()
         }).catch((error) => {
-            if(error.response.status == 404){
+            if(error.response.status === 404){
                 this.admin = true;
             }
             else{
@@ -119,7 +118,7 @@ class Auth {
 
     isServerInstalled(cb) {
         axios(configApiCall(api_path_get_server_status, "GET", null, null)).then((response) => {                       
-            if (response.data['installed'] == 'true') {
+            if (response.data['installed'] === 'true') {
                 this.installed = true
                 console.log("Server is installed")
             } else {
@@ -143,7 +142,7 @@ class Auth {
                 this.authenticated = true;
                 cb()
             }).catch((error) => {
-                if(error.response.status == 401){
+                if(error.response.status === 401){
                     this.authenticated = false;
                     console.log("Error during API request on checkLastKnowStep not authenticated!");
                 }
diff --git a/jams-react-client/src/components/CustomPopupState/CustomPopupState.js b/jams-react-client/src/components/CustomPopupState/CustomPopupState.js
index 09d69ee7..b58a3814 100644
--- a/jams-react-client/src/components/CustomPopupState/CustomPopupState.js
+++ b/jams-react-client/src/components/CustomPopupState/CustomPopupState.js
@@ -9,7 +9,6 @@ import Typography from '@material-ui/core/Typography';
 
 export default function CustomPopupState(props) {
 
-    const [message, setMessage] = React.useState(props.message);
     return(
 <IconButton>
 
@@ -29,7 +28,7 @@ export default function CustomPopupState(props) {
             }}
         >
             <Box p={1}>
-        <Typography>{message}</Typography>
+        <Typography>{props.message}</Typography>
             </Box>
         </Popover>
         </div>
diff --git a/jams-react-client/src/components/CustomizedSteppers/CustomizedSteppers.js b/jams-react-client/src/components/CustomizedSteppers/CustomizedSteppers.js
index 914f6c76..71518121 100644
--- a/jams-react-client/src/components/CustomizedSteppers/CustomizedSteppers.js
+++ b/jams-react-client/src/components/CustomizedSteppers/CustomizedSteppers.js
@@ -1,6 +1,6 @@
 import React from 'react';
 import PropTypes from 'prop-types';
-import { makeStyles, withStyles } from '@material-ui/core/styles';
+import { makeStyles } from '@material-ui/core/styles';
 import clsx from 'clsx';
 import Stepper from '@material-ui/core/Stepper';
 import Step from '@material-ui/core/Step';
@@ -9,9 +9,6 @@ import Check from '@material-ui/icons/Check';
 import SettingsIcon from '@material-ui/icons/Settings';
 import GroupAddIcon from '@material-ui/icons/GroupAdd';
 import VideoLabelIcon from '@material-ui/icons/VideoLabel';
-import StepConnector from '@material-ui/core/StepConnector';
-import Button from '@material-ui/core/Button';
-import Typography from '@material-ui/core/Typography';
 
 import i18next from 'i18next';
 
@@ -143,24 +140,11 @@ function getSteps() {
 
 export default function CustomizedSteppers(props) {
   const classes = useStyles();
-  const [activeStep, setActiveStep] = React.useState(props.step);
   const steps = getSteps();
 
-  const handleNext = () => {
-    setActiveStep((prevActiveStep) => prevActiveStep + 1);
-  };
-
-  const handleBack = () => {
-    setActiveStep((prevActiveStep) => prevActiveStep - 1);
-  };
-
-  const handleReset = () => {
-    setActiveStep(0);
-  };
-
   return (
     <div className={classes.root}>
-      <Stepper alternativeLabel activeStep={activeStep}>
+      <Stepper alternativeLabel activeStep={props.step}>
         {steps.map((label) => (
           <Step key={label}>
             <StepLabel>{label}</StepLabel>
diff --git a/jams-react-client/src/components/Drawer/Drawer.js b/jams-react-client/src/components/Drawer/Drawer.js
index 32e22b9b..293bc95b 100644
--- a/jams-react-client/src/components/Drawer/Drawer.js
+++ b/jams-react-client/src/components/Drawer/Drawer.js
@@ -7,32 +7,18 @@ import List from "@material-ui/core/List";
 import Divider from "@material-ui/core/Divider";
 import ListItem from "@material-ui/core/ListItem";
 import ListItemText from "@material-ui/core/ListItemText";
-import AddCircleOutlineIcon from "@material-ui/icons/AddCircleOutline";
 import Avatar from "@material-ui/core/Avatar";
 
 import noProfilePicture from "assets/img/faces/no-profile-picture.png";
 
-import {
-  api_path_get_user_directory_search,
-  api_path_get_auth_contacts,
-  api_path_get_admin_contacts,
-  api_path_put_update_group,
-} from "globalUrls";
-import { useHistory } from "react-router-dom";
-import axios from "axios";
-import configApiCall from "api.js";
-import auth from "auth.js";
-
 import { debounce } from "lodash";
 
-import i18next from "i18next";
-
 const useStyles = makeStyles({
   list: {
-    width: 500,
+    width: "100%",
   },
   fullList: {
-    width: "auto",
+    width: "100%",
   },
   search: {
     width: "100%",
@@ -45,259 +31,67 @@ const useStyles = makeStyles({
 
 export default function TemporaryDrawer(props) {
   const classes = useStyles();
-  const history = useHistory();
-  const [direction, setDirection] = React.useState(props.direction);
-  const [users, setUsers] = React.useState([]);
-  const [existingContacts, setExistingContacts] = React.useState([]);
-  const [userAdded, setUserAdded] = React.useState(false);
-  let addingToGroup = props.addingToGroup;
 
   useEffect(() => {
-    /**
-     * Get users list to pass to the drawer to add contacts to user or users to group
-     */
-
-    axios(
-      configApiCall(
-        api_path_get_admin_contacts + "?username=" + props.username,
-        "GET",
-        null,
-        null
-      )
-    )
-      .then((response) => {
-        /*
-                TODO: Include the username of the user of witch we want to display contacts
-                at the moment the admin sees his contacts in each user profile he visits
-            */
-        let originalContacts = response.data;
-        originalContacts.map((contact) => {
-          contact.display = "";
-        });
-        setExistingContacts(originalContacts);
-      })
-      .catch((error) => {
-        console.log(error);
-        if (error.response.status == 401) {
-          auth.authenticated = false;
-          history.push("/");
-        }
-      });
-
-    axios(
-      configApiCall(
-        api_path_get_user_directory_search,
-        "GET",
-        { queryString: "*", page: "1" },
-        null
-      )
-    )
-      .then((response) => {
-        let resp = response.data.profiles;
-        if (!addingToGroup) {
-          let contacts = resp.filter((data) => {
-            if (data.username !== props.username) return data;
-          });
-          setUsers(contacts);
-        } else setUsers(resp);
-      })
-      .catch((error) => {
-        console.log(error);
-        if (error.response.status == 401) {
-          auth.authenticated = false;
-          history.push("/");
-        }
-      });
-  }, [userAdded]);
-
-  const addContactToUser = (firstName, lastName, jamiId) => {
-    var displayName = firstName + " " + lastName;
-    var owner = props.username;
-    const data = {
-      owner: owner,
-      uri: jamiId,
-      displayName: displayName,
-      timestamp: "",
-      status: "",
-      banned: false,
-      confirmed: false,
-    };
-    if (props.isAdmin) {
-      axios(
-        configApiCall(
-          api_path_get_admin_contacts + "?username=" + props.username,
-          "PUT",
-          data,
-          null
-        )
-      )
-        .then((response) => {
-          setUserAdded(true);
-          props.setOpenDrawer(false);
-        })
-        .catch((error) => {
-          console.log("Error adding user: " + error);
-          props.setOpenDrawer(false);
-        });
-    } else {
-      axios(configApiCall(api_path_get_auth_contacts, "PUT", data, null))
-        .then((response) => {
-          setUserAdded(true);
-          props.setOpenDrawer(false);
-        })
-        .catch((error) => {
-          console.log("Error adding user: " + error);
-          props.setOpenDrawer(false);
-        });
-    }
-  };
-
-  const addUserToGroup = (username) => {
-    let url = "";
-    console.log(props.blueprintLabel);
-
-    if (props.blueprintLabel == null) {
-      url =
-        api_path_put_update_group +
-        "?groupName=" +
-        props.groupName +
-        "&newName=" +
-        props.groupName +
-        "&blueprintName=&groupMembers=" +
-        username;
-    } else {
-      url =
-        api_path_put_update_group +
-        "?groupName=" +
-        props.groupName +
-        "&newName=" +
-        props.groupName +
-        "&blueprintName=" +
-        props.blueprintLabel +
-        "&groupMembers=" +
-        username;
-    }
-
-    axios(configApiCall(url, "PUT", null, null))
-      .then((response) => {
-        setUserAdded(true);
-        props.getGroup();
-        props.setOpenDrawer(false);
-      })
-      .catch((error) => {
-        console.log("Error adding user: " + error);
-        props.setOpenDrawer(false);
-      });
-  };
+      console.log(props.targets)
+  }, []);
 
-  const searchUsers = (value) => {
-    axios(
-      configApiCall(
-        api_path_get_user_directory_search,
-        "GET",
-        { queryString: value ? value : "*", page: "1" },
-        null
-      )
-    )
-      .then((response) => {
-        let resp = response.data.profiles;
-        if (!addingToGroup) {
-          let contacts = resp.filter((data) => {
-            if (data.username !== props.username) return data;
-          });
-          setUsers(contacts);
-        } else setUsers(resp);
-      })
-      .catch((error) => {
-        console.log(error);
-        setUsers([]);
-        if (error.response.status == 401) {
-          auth.authenticated = false;
-          history.push("/");
-        }
-      });
-  };
 
   const listUsers = () => (
     <List>
-      {addingToGroup
-        ? users.map((user) => (
-            <ListItem
-              button
-              key={user.username}
-              onClick={() => {
-                addUserToGroup(user.username);
-              }}
-            >
-              {/* <AddCircleOutlineIcon style={{ marginRight: "10px" }} /> */}
-              <Avatar
-                style={{ marginRight: "10px" }}
-                alt={user.username}
-                src={
-                  user.profilePicture
-                    ? "data:image/png;base64, " + user.profilePicture
-                    : noProfilePicture
-                }
-              />
-              <ListItemText
-                primary={
-                  user.username === ""
-                    ? user.id
-                    : user.firstName === "" || user.lastName === ""
-                    ? user.username
-                    : user.firstName + " " + user.lastName
-                }
-              />
-            </ListItem>
-          ))
-        : users
-            .filter((data) => {
-              var added = false;
-              existingContacts.forEach((contact) => {
-                if (contact.uri === data.id) added = true;
-              });
-              if (!added) return data;
-            })
-            .map((user) => (
-              <ListItem
-                button
-                key={user.username}
-                onClick={() => {
-                  addContactToUser(user.firstName, user.lastName, user.id);
-                }}
-              >
-                {/* <AddCircleOutlineIcon style={{ marginRight: "10px" }} /> */}
-                <Avatar
-                  style={{ marginRight: "10px" }}
-                  alt={user.username}
-                  src={
-                    user.profilePicture
-                      ? "data:image/png;base64, " + user.profilePicture
-                      : noProfilePicture
-                  }
-                />
-                <ListItemText
-                  primary={
-                    user.username === ""
-                      ? user.id
-                      : user.firstName === "" || user.lastName === ""
-                      ? user.username
-                      : user.firstName + " " + user.lastName
-                  }
-                />
-              </ListItem>
-            ))}
+      {props.type === "user" ? props.targets.map((target) => (
+        <ListItem
+          button
+          key={target.username}
+          onClick={() => {
+            props.addElementToTarget(target);
+            props.setOpenDrawer(false);
+          }}
+        >
+          <Avatar
+            style={{ marginRight: "10px" }}
+            alt={target.username}
+            src={
+              target.profilePicture
+                ? "data:image/png;base64, " + target.profilePicture
+                : noProfilePicture
+            }
+          />
+          <ListItemText
+            primary={
+              target.username === ""
+                ? target.id
+                : target.firstName === "" || target.lastName === ""
+                ? target.username
+                : target.firstName + " " + target.lastName
+            }
+          />
+        </ListItem>
+      )) : props.targets.map((target) => (      
+        <ListItem
+          button
+          key={target.name}
+          onClick={() => {
+            props.addElementToTarget(target.name);
+            props.setOpenDrawer(false);
+          }}
+        >
+          <ListItemText
+            primary={target.name}
+          />
+        </ListItem>
+      ))}
     </List>
   );
 
-  const initSearchUsers = useCallback(
-    debounce((searchValue) => searchUsers(searchValue), 500),
+  const initSearchTargets = useCallback(
+    debounce((searchValue) => props.searchTargets(searchValue), 500),
     []
   );
 
-  const handleSearchUsers = (e) => {
+  const handleSearchTargets = (e) => {
     const searchValue = e.target.value;
-    initSearchUsers(searchValue);
+    initSearchTargets(searchValue);
   };
 
   return (
@@ -312,7 +106,7 @@ export default function TemporaryDrawer(props) {
         >
           <div
             className={clsx(classes.list, {
-              [classes.fullList]: direction === "top" || direction === "bottom",
+              [classes.fullList]: props.direction === "top" || props.direction === "bottom",
             })}
             role="presentation"
           >
@@ -324,9 +118,9 @@ export default function TemporaryDrawer(props) {
                 inputProps={{
                   placeholder: props.placeholder,
                   inputProps: {
-                    "aria-label": i18next.t("add_a_contact", "Add contact"),
+                    "aria-label": props.placeholder,
                   },
-                  onKeyUp: handleSearchUsers,
+                  onKeyUp: handleSearchTargets,
                 }}
               />
             </div>
diff --git a/jams-react-client/src/components/LanguagePicker/LanguagePicker.js b/jams-react-client/src/components/LanguagePicker/LanguagePicker.js
index 1eafd230..a87d64fa 100644
--- a/jams-react-client/src/components/LanguagePicker/LanguagePicker.js
+++ b/jams-react-client/src/components/LanguagePicker/LanguagePicker.js
@@ -1,28 +1,15 @@
 import React, { useEffect } from "react";
 import { useHistory } from "react-router-dom";
-import { makeStyles } from "@material-ui/core/styles";
 
 import Button from "@material-ui/core/Button";
 import Menu from "@material-ui/core/Menu";
 import MenuItem from "@material-ui/core/MenuItem";
 import PopupState, { bindTrigger, bindMenu } from "material-ui-popup-state";
 
-import TranslateIcon from "@material-ui/icons/Translate";
-
 import i18next from "i18next";
 
 import { useTranslation } from "react-i18next";
 
-const useStyles = makeStyles((theme) => ({
-  root: {
-    "& .MuiTextField-root": {
-      margin: theme.spacing(1),
-      width: "25ch",
-      backgrounColor: "white",
-    },
-  },
-}));
-
 export default function LanguagePicker(props) {
   const history = useHistory();
   const [language, setLanguage] = React.useState(i18next.language || window.localStorage.i18nextLng || "en");
diff --git a/jams-react-client/src/components/Navbars/AdminNavbarLinks.js b/jams-react-client/src/components/Navbars/AdminNavbarLinks.js
index 7a158056..1b9ff7d3 100644
--- a/jams-react-client/src/components/Navbars/AdminNavbarLinks.js
+++ b/jams-react-client/src/components/Navbars/AdminNavbarLinks.js
@@ -1,5 +1,4 @@
 import React from "react";
-import { useHistory } from 'react-router-dom'
 import classNames from "classnames";
 // @material-ui/core components
 import { makeStyles } from "@material-ui/core/styles";
@@ -13,32 +12,18 @@ import Poppers from "@material-ui/core/Popper";
 import Divider from "@material-ui/core/Divider";
 // @material-ui/icons
 import Person from "@material-ui/icons/Person";
-import Notifications from "@material-ui/icons/Notifications";
-import Dashboard from "@material-ui/icons/Dashboard";
-import Search from "@material-ui/icons/Search";
+
 // core components
-import CustomInput from "components/CustomInput/CustomInput.js";
 import Button from "components/CustomButtons/Button.js";
 
 import styles from "assets/jss/material-dashboard-react/components/headerLinksStyle.js";
-import auth from "../../auth"
+
 const useStyles = makeStyles(styles);
 
 export default function AdminNavbarLinks(props) {
   const classes = useStyles();
-  const history = useHistory();
-  const [openNotification, setOpenNotification] = React.useState(null);
   const [openProfile, setOpenProfile] = React.useState(null);
-  const handleClickNotification = event => {
-    if (openNotification && openNotification.contains(event.target)) {
-      setOpenNotification(null);
-    } else {
-      setOpenNotification(event.currentTarget);
-    }
-  };
-  const handleCloseNotification = () => {
-    setOpenNotification(null);
-  };
+
   const handleClickProfile = event => {
     if (openProfile && openProfile.contains(event.target)) {
       setOpenProfile(null);
diff --git a/jams-react-client/src/components/Navbars/Navbar.js b/jams-react-client/src/components/Navbars/Navbar.js
index 2b6a4356..370ac08b 100755
--- a/jams-react-client/src/components/Navbars/Navbar.js
+++ b/jams-react-client/src/components/Navbars/Navbar.js
@@ -10,8 +10,6 @@ import Hidden from "@material-ui/core/Hidden";
 // @material-ui/icons
 import Menu from "@material-ui/icons/Menu";
 // core components
-import AdminNavbarLinks from "./AdminNavbarLinks.js";
-import RTLNavbarLinks from "./RTLNavbarLinks.js";
 import Button from "components/CustomButtons/Button.js";
 
 import styles from "assets/jss/material-dashboard-react/components/headerStyle.js";
@@ -20,16 +18,6 @@ const useStyles = makeStyles(styles);
 
 export default function Header(props) {
   const classes = useStyles();
-  function makeBrand() {
-    var name;
-    props.routes.map(prop => {
-      if (window.location.href.indexOf(prop.layout + prop.path) !== -1) {
-        name = props.rtlActive ? prop.rtlName : prop.name;
-      }
-      return null;
-    });
-    return name;
-  }
   const { color } = props;
   const appBarClasses = classNames({
     [" " + classes[color]]: color
diff --git a/jams-react-client/src/components/PasswordDialog/PasswordDialog.js b/jams-react-client/src/components/PasswordDialog/PasswordDialog.js
index 5fc9da56..2d70194b 100644
--- a/jams-react-client/src/components/PasswordDialog/PasswordDialog.js
+++ b/jams-react-client/src/components/PasswordDialog/PasswordDialog.js
@@ -1,4 +1,4 @@
-import React, { useEffect } from "react";
+import React from "react";
 // @material-ui/core components
 import { makeStyles } from "@material-ui/core/styles";
 // core components
@@ -9,7 +9,6 @@ import Dialog from "@material-ui/core/Dialog";
 import DialogActions from "@material-ui/core/DialogActions";
 import DialogContent from "@material-ui/core/DialogContent";
 import DialogContentText from "@material-ui/core/DialogContentText";
-import DialogTitle from "@material-ui/core/DialogTitle";
 
 import Grid from "@material-ui/core/Grid";
 
diff --git a/jams-react-client/src/components/ServerParameters/ServerParameters.js b/jams-react-client/src/components/ServerParameters/ServerParameters.js
index 69bcee65..6c8c6e30 100644
--- a/jams-react-client/src/components/ServerParameters/ServerParameters.js
+++ b/jams-react-client/src/components/ServerParameters/ServerParameters.js
@@ -4,7 +4,7 @@ import Button from "@material-ui/core/Button";
 import TextField from "@material-ui/core/TextField";
 import Typography from "@material-ui/core/Typography";
 import { makeStyles } from "@material-ui/core/styles";
-import { Formik, Field } from "formik";
+import { Formik } from "formik";
 
 import CustomPopupState from "../CustomPopupState/CustomPopupState";
 import Select from "@material-ui/core/Select";
diff --git a/jams-react-client/src/configured.route.js b/jams-react-client/src/configured.route.js
index d0434b94..32200089 100644
--- a/jams-react-client/src/configured.route.js
+++ b/jams-react-client/src/configured.route.js
@@ -1,10 +1,9 @@
 
 import React from 'react'
-import { Route, Redirect } from 'react-router-dom'
+import { Route } from 'react-router-dom'
 import auth from './auth'
 import SignUp from "layouts/SignUp";
 import SignIn from "layouts/SignIn";
-import Admin from 'layouts/Admin';
 
 export const ConfiguredRoute = ({ component: Component, ...rest }) => {
     return (
@@ -15,12 +14,12 @@ export const ConfiguredRoute = ({ component: Component, ...rest }) => {
                     if(auth.isInstalled()){
                         return <Component {...props} />
                     }else if(!auth.isInstalled() && auth.isAuthenticated()){
-                        if(auth.uri == '/api/install/ca' || auth.uri == 'start'){
+                        if(auth.uri === '/api/install/ca' || auth.uri === 'start'){
                             return <SignUp step={1}/>
                         }
-                        else if (auth.uri == '/api/install/auth'){
+                        else if (auth.uri === '/api/install/auth'){
                             return <SignUp step={2}/>
-                        } else if (auth.uri == '/api/install/settings'){
+                        } else if (auth.uri === '/api/install/settings'){
                             return <SignUp step={3}/>
                         } else {
                             console.log('Error no matching path for configuration')
diff --git a/jams-react-client/src/layouts/Admin.js b/jams-react-client/src/layouts/Admin.js
index 9d92b5a6..df779bcc 100644
--- a/jams-react-client/src/layouts/Admin.js
+++ b/jams-react-client/src/layouts/Admin.js
@@ -9,7 +9,6 @@ import { makeStyles } from "@material-ui/core/styles";
 import Navbar from "components/Navbars/Navbar.js";
 import Footer from "components/Footer/Footer.js";
 import Sidebar from "components/Sidebar/Sidebar.js";
-import Snackbar from "@material-ui/core/Snackbar";
 
 // @material-ui/icons
 import Person from "@material-ui/icons/Person";
@@ -31,21 +30,15 @@ import logo from "assets/img/logo-jams.svg";
 import auth from "auth";
 import configApiCall from "api.js";
 import {
-  api_path_get_start_update,
-  api_path_get_subscription_status,
+  api_path_get_start_update
 } from "globalUrls";
 
 import axios from "axios";
 
-import ReactDOM from "react-dom";
-import { ConfiguredRoute } from "../configured.route";
-import SignIn from "./SignIn";
-import { ProtectedRoute } from "../protected.route";
+
 import Dialog from "@material-ui/core/Dialog/Dialog";
 import DialogTitle from "@material-ui/core/DialogTitle/DialogTitle";
-import { Field, Formik } from "formik";
 import DialogContent from "@material-ui/core/DialogContent/DialogContent";
-import TextField from "@material-ui/core/TextField/TextField";
 import DialogActions from "@material-ui/core/DialogActions/DialogActions";
 import DialogContentText from "@material-ui/core/DialogContentText/DialogContentText";
 import Button from "@material-ui/core/Button";
@@ -68,10 +61,6 @@ export default function Admin({ ...rest }) {
   const [mobileOpen, setMobileOpen] = React.useState(false);
   const [open, setOpen] = React.useState(false);
   const [message, setMessage] = React.useState(false);
-  const [signedUp, setSignedUp] = React.useState(false);
-  const [signedIn, setSignedIn] = React.useState(false);
-  const [token, setToken] = React.useState(false);
-  const [severity, setSeverity] = React.useState("success");
   const [openUpdate, setOpenUpdate] = React.useState(false);
   const [dialogMessage, setDialogMessage] = React.useState("");
   const [messageYes, setMessageYes] = React.useState("");
@@ -137,19 +126,6 @@ export default function Admin({ ...rest }) {
     </Switch>
   );
 
-  const handleImageClick = (image) => {
-    setImage(image);
-  };
-  const handleColorClick = (color) => {
-    setColor(color);
-  };
-  const handleFixedClick = () => {
-    if (fixedClasses === "dropdown") {
-      setFixedClasses("dropdown show");
-    } else {
-      setFixedClasses("dropdown");
-    }
-  };
   const handleDrawerToggle = () => {
     setMobileOpen(!mobileOpen);
   };
diff --git a/jams-react-client/src/layouts/SignIn.js b/jams-react-client/src/layouts/SignIn.js
index ecc8d71c..094d9a65 100644
--- a/jams-react-client/src/layouts/SignIn.js
+++ b/jams-react-client/src/layouts/SignIn.js
@@ -4,10 +4,7 @@ import { Formik } from "formik";
 import Button from "@material-ui/core/Button";
 import CssBaseline from "@material-ui/core/CssBaseline";
 import TextField from "@material-ui/core/TextField";
-import FormControlLabel from "@material-ui/core/FormControlLabel";
-import Checkbox from "@material-ui/core/Checkbox";
 import Link from "@material-ui/core/Link";
-import Grid from "@material-ui/core/Grid";
 import Box from "@material-ui/core/Box";
 import Typography from "@material-ui/core/Typography";
 import { makeStyles } from "@material-ui/core/styles";
diff --git a/jams-react-client/src/layouts/SignUp.js b/jams-react-client/src/layouts/SignUp.js
index aa5aeb34..901dd534 100644
--- a/jams-react-client/src/layouts/SignUp.js
+++ b/jams-react-client/src/layouts/SignUp.js
@@ -7,7 +7,6 @@ import { makeStyles } from "@material-ui/core/styles";
 import Container from "@material-ui/core/Container";
 import MuiAlert from "@material-ui/lab/Alert";
 import Paper from "@material-ui/core/Paper";
-import Button from "@material-ui/core/Button";
 
 import logo from "assets/img/logo-jams-blue.svg";
 
@@ -69,27 +68,26 @@ export default function SignUp(props) {
   const classes = useStyles();
   const [error, setError] = React.useState(false);
   const [errorMessage, setErrorMessage] = React.useState("Test");
-  const [step, setStep] = React.useState(props.step);
 
   useEffect(() => {
     //request the server to check for the step to return
   });
 
   function returnStep() {
-    if (step === 0) {
+    if (props.step === 0) {
       return (
         <CreateAdmin setError={setError} setErrorMessage={setErrorMessage} />
       );
-    } else if (step === 1) {
+    } else if (props.step === 1) {
       return <CaSetup setError={setError} setErrorMessage={setErrorMessage} />;
-    } else if (step === 2) {
+    } else if (props.step === 2) {
       return (
         <IdentityManagement
           setError={setError}
           setErrorMessage={setErrorMessage}
         />
       );
-    } else if (step === 3) {
+    } else if (props.step === 3) {
       return (
         <ServerParameters
           setError={setError}
@@ -103,7 +101,7 @@ export default function SignUp(props) {
       <Paper className={classes.paper}>
         <CssBaseline />
         <img src={logo} className={classes.logo} alt="Logo Jami" />
-        <CustomizedSteppers step={step} />
+        <CustomizedSteppers step={props.step} />
         {error && errorMessage && (
           <Alert severity="error">{errorMessage}</Alert>
         )}
diff --git a/jams-react-client/src/views/Blueprint/Blueprint.js b/jams-react-client/src/views/Blueprint/Blueprint.js
index eb47e88d..7ea53ff7 100644
--- a/jams-react-client/src/views/Blueprint/Blueprint.js
+++ b/jams-react-client/src/views/Blueprint/Blueprint.js
@@ -1,7 +1,4 @@
 import React from "react";
-// @material-ui/core components
-import { makeStyles } from "@material-ui/core/styles";
-
 
 import PropTypes from 'prop-types';
 import AppBar from '@material-ui/core/AppBar';
@@ -71,10 +68,7 @@ const styles = {
   }
 };
 
-const useStyles = makeStyles(styles);
-
 export default function Blueprint(props) {
-  const classes = useStyles();
 
   const [value, setValue] = React.useState(0);
 
diff --git a/jams-react-client/src/views/Blueprint/EditBlueprintConfiguration.js b/jams-react-client/src/views/Blueprint/EditBlueprintConfiguration.js
index a64d2ac6..5b08391a 100644
--- a/jams-react-client/src/views/Blueprint/EditBlueprintConfiguration.js
+++ b/jams-react-client/src/views/Blueprint/EditBlueprintConfiguration.js
@@ -1,6 +1,4 @@
 import React from "react";
-import { useHistory } from "react-router-dom";
-
 import clsx from "clsx";
 
 // @material-ui/core components
@@ -171,7 +169,6 @@ const StyledRadio = (props) => {
 
 export default function EditBlueprintConfiguration(props) {
   const classes = useStyles();
-  const history = useHistory();
 
   const [videoEnabled, setVideoEnabled] = React.useState(true);
   const [publicInCalls, setPublicInCalls] = React.useState(true);
@@ -250,7 +247,7 @@ export default function EditBlueprintConfiguration(props) {
             error
         );
       });
-  }, []);
+  }, [props.blueprintName, props.username]);
 
   const handleUpdateConfiguration = (field, value, selectedOptions = []) => {
     let data = {
@@ -287,17 +284,17 @@ export default function EditBlueprintConfiguration(props) {
         delete data.dhtProxyListUrl;
       }
 
-      if (selectedOption == "customTurn") {
+      if (selectedOption === "customTurn") {
         data.turnEnabled = true;
       }
-      if (selectedOption == "customDHTProxy") {
+      if (selectedOption === "customDHTProxy") {
         data.proxyEnabled = true;
       }
 
-      if (selectedOption == "disabledTurn") {
+      if (selectedOption === "disabledTurn") {
         data.turnEnabled = false;
       }
-      if (selectedOption == "disabledDHTProxy") {
+      if (selectedOption === "disabledDHTProxy") {
         data.proxyEnabled = false;
       }
     });
@@ -441,7 +438,7 @@ export default function EditBlueprintConfiguration(props) {
                           row
                           style={{
                             display:
-                              selectedTurnOption == "customTurn"
+                              selectedTurnOption === "customTurn"
                                 ? "block"
                                 : "none",
                           }}
@@ -473,7 +470,7 @@ export default function EditBlueprintConfiguration(props) {
                           row
                           style={{
                             display:
-                              selectedTurnOption == "customTurn"
+                              selectedTurnOption === "customTurn"
                                 ? "block"
                                 : "none",
                           }}
@@ -505,7 +502,7 @@ export default function EditBlueprintConfiguration(props) {
                           row
                           style={{
                             display:
-                              selectedTurnOption == "customTurn"
+                              selectedTurnOption === "customTurn"
                                 ? "block"
                                 : "none",
                           }}
@@ -582,7 +579,7 @@ export default function EditBlueprintConfiguration(props) {
                             size="large"
                             style={{
                               display:
-                                selectedDHTProxyOption == "customDHTProxy"
+                                selectedDHTProxyOption === "customDHTProxy"
                                   ? "block"
                                   : "none",
                             }}
@@ -615,7 +612,7 @@ export default function EditBlueprintConfiguration(props) {
                             size="large"
                             style={{
                               display:
-                                selectedDHTProxyOption == "customDHTProxy"
+                                selectedDHTProxyOption === "customDHTProxy"
                                   ? "block"
                                   : "none",
                             }}
diff --git a/jams-react-client/src/views/Blueprint/EditBlueprintPermissions.js b/jams-react-client/src/views/Blueprint/EditBlueprintPermissions.js
index cb08a58b..8e25bee7 100644
--- a/jams-react-client/src/views/Blueprint/EditBlueprintPermissions.js
+++ b/jams-react-client/src/views/Blueprint/EditBlueprintPermissions.js
@@ -1,5 +1,4 @@
 import React from "react";
-import { useHistory } from "react-router-dom";
 // @material-ui/core components
 import { makeStyles } from "@material-ui/core/styles";
 import Checkbox from "@material-ui/core/Checkbox";
@@ -93,7 +92,6 @@ function Alert(props) {
 
 export default function EditBlueprintPermissions(props) {
   const classes = useStyles();
-  const history = useHistory();
 
   const [videoEnabled, setVideoEnabled] = React.useState(true);
   const [publicInCalls, setPublicInCalls] = React.useState(true);
@@ -148,7 +146,7 @@ export default function EditBlueprintPermissions(props) {
             error
         );
       });
-  }, []);
+  }, [props.blueprintName]);
 
   const handleUpdatePermissions = (field, value) => {
     let data = {
@@ -177,14 +175,14 @@ export default function EditBlueprintPermissions(props) {
       data[field] = value;
     }
 
-    if (turnEnabled == undefined) {
+    if (turnEnabled === undefined) {
       delete data.turnEnabled;
       delete data.turnServer;
       delete data.turnServerUserName;
       delete data.turnServerPassword;
     }
 
-    if (proxyEnabled == undefined) {
+    if (proxyEnabled === undefined) {
       delete data.proxyEnabled;
       delete data.proxyServer;
       delete data.dhtProxyListUrl;
diff --git a/jams-react-client/src/views/Blueprints/Blueprints.js b/jams-react-client/src/views/Blueprints/Blueprints.js
index 9cb58bcc..b7333a9f 100644
--- a/jams-react-client/src/views/Blueprints/Blueprints.js
+++ b/jams-react-client/src/views/Blueprints/Blueprints.js
@@ -29,7 +29,6 @@ import auth from "auth.js";
 import { api_path_blueprints } from "globalUrls";
 
 import AddCircleOutlineIcon from "@material-ui/icons/AddCircleOutline";
-import KeyboardReturnIcon from "@material-ui/icons/KeyboardReturn";
 
 import LinearProgress from "@material-ui/core/LinearProgress";
 
@@ -290,7 +289,7 @@ export default function Blueprints() {
                 {disableCreate && blueprintName.length > 0 && (
                   <p>{i18next.t("blueprint_name_already_exists", "Blueprint name already exists!")}</p>
                 )}
-                {disableCreate && blueprintName.length == 0 && (
+                {disableCreate && blueprintName.length === 0 && (
                   <p>{i18next.t("blueprint_name_is_empty", "Blueprint name is empty")}</p>
                 )}
               </DialogContentText>
@@ -376,7 +375,13 @@ export default function Blueprints() {
               </div>
             ) : (
               blueprints.map((blueprint) => (
-                <GridItem xs={12} sm={12} md={2} key={blueprint.name}>
+                <GridItem
+                xs={12}
+                sm={6}
+                md={3}
+                lg={2}
+                xl={2}
+                key={blueprint.name}>
                   <Card profile>
                       <a
                         href="#"
diff --git a/jams-react-client/src/views/Contacts/Contacts.js b/jams-react-client/src/views/Contacts/Contacts.js
index 7506403f..cbce942d 100644
--- a/jams-react-client/src/views/Contacts/Contacts.js
+++ b/jams-react-client/src/views/Contacts/Contacts.js
@@ -1,4 +1,4 @@
-import React, { useState, useEffect } from "react";
+import React, { useEffect } from "react";
 import { useHistory } from "react-router-dom";
 // @material-ui/core components
 import { makeStyles } from "@material-ui/core/styles";
@@ -26,6 +26,7 @@ import {
   api_path_delete_admin_contacts,
   api_path_get_ns_name_from_addr,
   api_path_get_user_profile,
+  api_path_get_user_directory_search
 } from "globalUrls";
 
 import AddCircleOutlineIcon from "@material-ui/icons/AddCircleOutline";
@@ -79,6 +80,7 @@ export default function Users(props) {
   const classes = useStyles();
   const history = useHistory();
   const [contacts, setContacts] = React.useState([]);
+  const [users, setUsers] = React.useState([]);
   const [searchValue, setSearchValue] = React.useState(null);
   const [loading, setLoading] = React.useState(false);
   const [progress, setProgress] = React.useState(0);
@@ -87,7 +89,30 @@ export default function Users(props) {
   const [removedContactName, setRemovedContactName] = React.useState();
   const [open, setOpen] = React.useState(false);
 
+  const searchContacts = (value) => {
+    axios(
+      configApiCall(
+        api_path_get_user_directory_search,
+        "GET",
+        { queryString: value ? value : "*", page: "1" },
+        null
+      )
+    )
+      .then((response) => {
+        setUsers(response.data.profiles);
+      })
+      .catch((error) => {
+        console.log(error);
+        setUsers([]);
+        if (error.response.status === 401) {
+          auth.authenticated = false;
+          history.push("/");
+        }
+      });
+  }
+
   const getAllContacts = () => {
+    
     if (auth.hasAdminScope()) {
       axios(
         configApiCall(
@@ -176,7 +201,7 @@ export default function Users(props) {
         })
         .catch((error) => {
           console.log(error);
-          if (error.response.status == 401) {
+          if (error.response.status === 401) {
             auth.authenticated = false;
             history.push("/");
           }
@@ -184,6 +209,47 @@ export default function Users(props) {
     }
   };
 
+  const addContactToUser = (value) => {
+
+    const data = {
+      owner: props.username,
+      uri: value.id,
+      displayName: `${value.firstName} ${value.lastName}`,
+      timestamp: "",
+      status: "",
+      banned: false,
+      confirmed: false,
+    };
+    if (auth.hasAdmin()) {
+      axios(
+        configApiCall(
+          api_path_get_admin_contacts + "?username=" + props.username,
+          "PUT",
+          data,
+          null
+        )
+      )
+        .then((response) => {
+          getAllContacts();
+          setOpenDrawer(false);
+        })
+        .catch((error) => {
+          console.log("Error adding user: " + error);
+          setOpenDrawer(false);
+        });
+    } else {
+      axios(configApiCall(api_path_get_auth_contacts, "PUT", data, null))
+        .then((response) => {
+          getAllContacts();
+          setOpenDrawer(false);
+        })
+        .catch((error) => {
+          console.log("Error adding user: " + error);
+          setOpenDrawer(false);
+        });
+    }
+  };
+
   useEffect(() => {
     setLoading(true);
     const timer = setInterval(() => {
@@ -195,6 +261,7 @@ export default function Users(props) {
         return Math.min(oldProgress + diff, 100);
       });
     }, 500);
+    searchContacts();
     getAllContacts();
     return () => {
       clearInterval(timer);
@@ -251,14 +318,16 @@ export default function Users(props) {
 
   return (
     <div>
-      <TemporaryDrawer
-        isAdmin={auth.hasAdminScope()}
-        username={props.username}
-        openDrawer={openDrawer}
-        setOpenDrawer={setOpenDrawer}
-        direction="right"
-        addingToGroup={false}
+      <TemporaryDrawer 
+        openDrawer={openDrawer} 
+        setOpenDrawer={setOpenDrawer} 
+        direction="right" 
         placeholder={i18next.t("add_contact", "Add contact…")}
+        searchTargets={searchContacts} 
+        targets={users}
+        addElementToTarget={addContactToUser}
+        targetName={props.username}
+        type="user"
       />
       <Dialog
         open={open}
@@ -348,10 +417,10 @@ export default function Users(props) {
                     />
                   </CardAvatar>
                   <h4 className={classes.cardTitle}>
-                    {contact.firstName != ""
+                    {contact.firstName !== ""
                       ? contact.firstName
                       : "No first name"}{" "}
-                    {contact.lastName != "" ? contact.lastName : "No last name"}
+                    {contact.lastName !== "" ? contact.lastName : "No last name"}
                   </h4>
                   <ul>
                     <li>
@@ -385,7 +454,7 @@ export default function Users(props) {
               </Card>
             </GridItem>
           ))}
-        {contacts == [] && props.username + i18next.t("has_no_contacts", " has no contacts")}
+        {contacts === [] && props.username + i18next.t("has_no_contacts", " has no contacts")}
       </GridContainer>
     </div>
   );
diff --git a/jams-react-client/src/views/Groups/EditGroup.js b/jams-react-client/src/views/Groups/EditGroup.js
index 163bdf0b..22a1d5d2 100644
--- a/jams-react-client/src/views/Groups/EditGroup.js
+++ b/jams-react-client/src/views/Groups/EditGroup.js
@@ -1,5 +1,5 @@
 import React from "react";
-import { useHistory } from 'react-router-dom';
+import { useHistory } from "react-router-dom";
 import classnames from "classnames";
 
 // @material-ui/core components
@@ -26,8 +26,6 @@ import TableCell from "@material-ui/core/TableCell";
 
 import Select from "@material-ui/core/Select";
 
-import { hexToRgb, blackColor } from "assets/jss/material-dashboard-react.js";
-
 import AddCircleOutlineIcon from '@material-ui/icons/AddCircleOutline';
 import EditIcon from '@material-ui/icons/Edit';
 import PeopleOutlineIcon from '@material-ui/icons/PeopleOutline';
@@ -40,19 +38,23 @@ import configApiCall from "../../api"
 import {
     api_path_get_list_group,
     api_path_put_update_group,
-    api_path_blueprints,
-    api_path_get_auth_contacts
+    api_path_get_user_directory_search,
+    api_path_get_user_profile
 } from "../../globalUrls"
 
 import dashboardStyle from "assets/jss/material-dashboard-react/views/dashboardStyle.js";
 import devicesStyle from "assets/jss/material-dashboard-react/components/devicesStyle.js";
+import Avatar from "@material-ui/core/Avatar";
+
+import noProfilePicture from "assets/img/faces/no-profile-picture.png";
 
 import TemporaryDrawer from "components/Drawer/Drawer"
 import * as tool from "../../tools";
-import auth from 'auth.js'
 
 import i18next from "i18next";
 
+import auth from "auth.js";
+
 const useStyles = makeStyles((theme) => ({
     ...devicesStyle,
     ...dashboardStyle,
@@ -98,37 +100,74 @@ const useStyles = makeStyles((theme) => ({
 
 export default function EditGroup(props) {
     const classes = useStyles();
+    const history = useHistory();
 
     const [name, setName] = React.useState(props.groupName);
     const [newName, setNewName] = React.useState(props.groupName);
     const [blueprint, setBlueprint] = React.useState(null);
     const [groupMembers, setGroupMembers] = React.useState([]);
     const [openDrawer, setOpenDrawer] = React.useState(false);
+    const [users, setUsers] = React.useState([]);
+
+    const getUserInfo = username => new Promise((resolve, reject) => {
+        axios(
+            configApiCall(
+              api_path_get_user_profile + username,
+              "GET",
+              null,
+              null
+            )
+          )
+            .then((response) => {
+                resolve(response.data);
+            })
+            .catch((error) => {
+                reject(error);
+            });
+    })
+
+    const getUsersInformation = userNames => {
+        userNames.forEach((username)=> {
+            getUserInfo(username).then((userInfo) => setGroupMembers(groupMembers.push(userInfo)))
+        })
+    }
 
     const getGroup = () => {
         axios(configApiCall(api_path_get_list_group+"?groupName="+props.groupName, 'GET', null, null)).then((response) => {
             let groups=response.data;
             if(groups.length > 1){
                 groups.map((group) => {
-                    if(group.name == props.groupName){
+                    if(group.name === props.groupName){
                         props.getBlueprintsOptions().forEach((blueprintOption) => {
                             if(blueprintOption.label === group["blueprint"]){
                                 console.log("Group option value : " + blueprintOption.value);
                                 setBlueprint(blueprintOption.value);
                             }
                         })
-                        setGroupMembers(group["groupMembers"]);
+                        
+                            getUserInfo(group["groupMembers"]).then((userInfo) => {
+                                let newGroupMembers = groupMembers;
+                                newGroupMembers.push(userInfo);
+                                setGroupMembers(newGroupMembers);
+                            })
                     }
                 })
             }
             else{
-                if(groups.name == props.groupName){
+                if(groups.name === props.groupName){
                     props.getBlueprintsOptions().forEach((blueprintOption) => {
                         if(blueprintOption.label === groups["blueprint"]){
                             setBlueprint(blueprintOption.value)
                         }
                     })
-                    setGroupMembers(groups.groupMembers)
+                    groups["groupMembers"].forEach((username)=> {
+                        getUserInfo(username).then((userInfo) => {
+                            let newGroupMembers = groupMembers;
+                            newGroupMembers.push(userInfo);
+                            setGroupMembers(newGroupMembers);
+                        })
+                    })
+ 
                 }
             }
 
@@ -155,6 +194,7 @@ export default function EditGroup(props) {
 
     React.useEffect(()=>{
         getGroup();
+        searchUsers();
     }, [])
 
     const handleUpdateGroup = (blueprintValue) => {
@@ -168,23 +208,47 @@ export default function EditGroup(props) {
 
         axios(configApiCall(url, 'PUT', null, null)).then((response) => {
             setNewName(name);
-            getGroup();
+            setBlueprint(blueprintValue);
         }).catch((error) => {
             console.log("Error updating group: " + error)
         })
     }
 
-    const removeUserFromGroup = (user) => {
+    const searchUsers = (value) => {
+        axios(
+          configApiCall(
+            api_path_get_user_directory_search,
+            "GET",
+            { queryString: value ? value : "*", page: "1" },
+            null
+          )
+        )
+          .then((response) => {
+            setUsers(response.data.profiles);
+          })
+          .catch((error) => {
+            console.log(error);
+            setUsers([]);
+            if (error.response.status === 401) {
+              auth.authenticated = false;
+              history.push("/");
+            }
+          });
+      };
 
+    const updateUserInGroup = (user) => {
         let url = '';
         if(blueprint == null){
-            url = api_path_put_update_group+"?groupName="+props.groupName+"&newName="+props.groupName+"&blueprintName=&groupMembers="+[user,];
+            url = api_path_put_update_group+"?groupName="+props.groupName+"&newName="+props.groupName+"&blueprintName=&groupMembers="+[user.username,];
         }else{
-            url = api_path_put_update_group+"?groupName="+props.groupName+"&newName="+props.groupName+"&blueprintName="+props.getBlueprintsOptions()[blueprint].label+"&groupMembers="+[user,];
+            url = api_path_put_update_group+"?groupName="+props.groupName+"&newName="+props.groupName+"&blueprintName="+props.getBlueprintsOptions()[blueprint].label+"&groupMembers="+[user.username,];
         }
 
         axios(configApiCall(url, 'PUT', null, null)).then((response) => {
-            getGroup()
+            let newGroupMembers = groupMembers;
+            if(newGroupMembers.includes(user)) newGroupMembers.splice(newGroupMembers.indexOf(user), 1);
+            else newGroupMembers.push(user);
+            setGroupMembers(newGroupMembers);
         }).catch((error) => {
             console.log("Error updating group: " + error)
         })
@@ -199,7 +263,17 @@ export default function EditGroup(props) {
 
     return(
         <div>
-            <TemporaryDrawer openDrawer={openDrawer} setOpenDrawer={setOpenDrawer} direction="right" addingToGroup={true} placeholder="Add user ..." groupName={name === ''?props.groupName:name} blueprintLabel={ blueprint == null ? null : props.getBlueprintsOptions()[blueprint].label} getGroup={getGroup}/>
+            <TemporaryDrawer 
+                openDrawer={openDrawer} 
+                setOpenDrawer={setOpenDrawer} 
+                direction="right" 
+                placeholder={i18next.t("add_user_to_group", "Add user to group ...")} 
+                searchTargets={searchUsers} 
+                targets={users}
+                addElementToTarget={updateUserInGroup}
+                targetName={name}
+                type="user"
+            />
             <GridContainer>
                 <GridItem xs={12} sm={12} md={6}>
                     <Card profile>
@@ -263,21 +337,41 @@ export default function EditGroup(props) {
                     <Table className={classes.table}>
                         <TableHead>
                             <TableRow>
-                                <TableCell>{i18next.t("username", "Username")}</TableCell>
+                                <TableCell align="left"></TableCell>
+                                <TableCell align="left">{i18next.t("username", "Username")}</TableCell>
+                                <TableCell align="left">{i18next.t("first_name", "First name")}</TableCell>
+                                <TableCell align="left">{i18next.t("last_name", "Last name")}</TableCell>
                                 <TableCell align="right">{i18next.t("action", "Action")}</TableCell>
                             </TableRow>
                         </TableHead>
                         <TableBody>
-                            {groupMembers.map(user =>
-                                <TableRow key={user} className={classes.tableRow}>
+                            {groupMembers.map(user =>                       
+                                <TableRow key={user.username} className={classes.tableRow}>
+                                    <TableCell  className={tableCellClasses}>
+                                        <Avatar
+                                            style={{ marginRight: "10px" }}
+                                            alt={user.username}
+                                            src={
+                                                user.profilePicture
+                                                ? "data:image/png;base64, " + user.profilePicture
+                                                : noProfilePicture
+                                            }
+                                        />
+                                    </TableCell>
+                                    <TableCell className={tableCellClasses}>
+                                        {user.username}
+                                    </TableCell>
+                                    <TableCell className={tableCellClasses}>
+                                        {user.firstName}
+                                    </TableCell>
                                     <TableCell className={tableCellClasses}>
-                                        {user}
+                                        {user.lastName}
                                     </TableCell>
                                     <TableCell align="right" className={classes.tableActions}>
-                                        <Button color="primary" onClick={() => removeUserFromGroup(user)}>{i18next.t("remove_user", "Remove user")}</Button>
+                                        <Button color="primary" onClick={() => updateUserInGroup(user)}>{i18next.t("remove_user", "Remove user")}</Button>
                                     </TableCell>
                                 </TableRow>
-                            ) }
+                            )}
                         </TableBody>
                     </Table>
                 </GridItem>
diff --git a/jams-react-client/src/views/Groups/Groups.js b/jams-react-client/src/views/Groups/Groups.js
index 9679db81..be38fe72 100644
--- a/jams-react-client/src/views/Groups/Groups.js
+++ b/jams-react-client/src/views/Groups/Groups.js
@@ -15,7 +15,6 @@ import CustomInput from "components/CustomInput/CustomInput.js";
 
 import IconButton from "@material-ui/core/IconButton";
 import DeleteOutlineIcon from "@material-ui/icons/DeleteOutline";
-import EditOutlinedIcon from "@material-ui/icons/EditOutlined";
 import Search from "@material-ui/icons/Search";
 import PeopleOutlineIcon from "@material-ui/icons/PeopleOutline";
 import MailOutlineIcon from "@material-ui/icons/MailOutline";
@@ -36,7 +35,6 @@ import {
 } from "globalUrls";
 
 import AddCircleOutlineIcon from "@material-ui/icons/AddCircleOutline";
-import KeyboardReturnIcon from "@material-ui/icons/KeyboardReturn";
 import EditGroup from "views/Groups/EditGroup";
 
 import FormControl from "@material-ui/core/FormControl";
@@ -137,22 +135,18 @@ export default function Groups() {
     history.push("/admin/groups");
   };
 
-  const loadBlueprints = () => {
-    axios(configApiCall(api_path_blueprints + "?name=*", "GET", null, null))
-      .then((response) => {
-        setBlueprints(response.data);
-        setSelectedBlueprint(getBlueprintsOptions()[0]);
-      })
-      .catch((error) => {
-        console.log(error);
-        if (error.response.status === 401) {
-          auth.authenticated = false;
-          history.push("/");
-        }
-        if (error.response.status === 500) {
-          setBlueprints([]);
-        }
+  const getBlueprintsOptions = () => {
+    let blueprintsOptions = [];
+    let index = 0;
+    if (blueprints.length === 0)
+      blueprintsOptions.push({ value: index, label: "No blueprint found" });
+    else {
+      blueprints.map((blueprint) => {
+        blueprintsOptions.push({ value: index, label: blueprint.name });
+        index += 1;
       });
+    }
+    return blueprintsOptions;
   };
 
   useEffect(() => {
@@ -177,6 +171,21 @@ export default function Groups() {
           setZeroGroup(false);
         }
         setGroups(allGroups);
+        axios(configApiCall(api_path_blueprints + "?name=*", "GET", null, null))
+        .then((response) => {
+          setBlueprints(response.data);
+          setSelectedBlueprint(getBlueprintsOptions()[0]);
+        })
+        .catch((error) => {
+          console.log(error);
+          if (error.response.status === 401) {
+            auth.authenticated = false;
+            history.push("/");
+          }
+          if (error.response.status === 500) {
+            setBlueprints([]);
+          }
+        });
         setLoading(false);
       })
       .catch((error) => {
@@ -186,11 +195,10 @@ export default function Groups() {
           history.push("/");
         }
       });
-    loadBlueprints();
     return () => {
       clearInterval(timer);
     };
-  }, [openCreate, openRemoveDialog]);
+  }, [openCreate, openRemoveDialog, history]);
 
   const [selectedGroup, setSelectedGroup] = useState(false);
 
@@ -262,20 +270,6 @@ export default function Groups() {
     history.push("/admin/groups");
   };
 
-  const getBlueprintsOptions = () => {
-    let blueprintsOptions = [];
-    let index = 0;
-    if (blueprints.length === 0)
-      blueprintsOptions.push({ value: index, label: "No blueprint found" });
-    else {
-      blueprints.map((blueprint) => {
-        blueprintsOptions.push({ value: index, label: blueprint.name });
-        index += 1;
-      });
-    }
-    return blueprintsOptions;
-  };
-
   const blueprintsOptionsItems = tool.buildSelectMenuItems(
     getBlueprintsOptions()
   );
@@ -349,7 +343,7 @@ export default function Groups() {
                   {disableCreate && groupName.length > 0 && (
                     <p>{i18next.t("group_name_already_exists", "Group name already exists!")}</p>
                   )}
-                  {disableCreate && groupName.length == 0 && (
+                  {disableCreate && groupName.length === 0 && (
                     <p>{i18next.t("group_name_is_empty", "Group name is empty")}</p>
                   )}
                 </Grid>
@@ -453,7 +447,13 @@ export default function Groups() {
                 }
               })
               .map((group) => (
-                <GridItem xs={12} sm={12} md={2} key={group.name}>
+                <GridItem                  
+                  xs={12}
+                  sm={6}
+                  md={3}
+                  lg={2}
+                  xl={2} 
+                  key={group.name}>
                   <Card profile>
                       <a
                         href="#"
diff --git a/jams-react-client/src/views/Settings/General.js b/jams-react-client/src/views/Settings/General.js
index 43ae3166..0f715201 100644
--- a/jams-react-client/src/views/Settings/General.js
+++ b/jams-react-client/src/views/Settings/General.js
@@ -1,10 +1,8 @@
 import React from "react";
-import { useHistory } from "react-router-dom";
 import { Formik } from "formik";
 import FormikField from "components/FormikField/FormikField";
 import * as Yup from "yup";
 import Button from "@material-ui/core/Button";
-import TextField from "@material-ui/core/TextField";
 import Grid from "@material-ui/core/Grid";
 import Typography from "@material-ui/core/Typography";
 import { makeStyles } from "@material-ui/core/styles";
@@ -20,7 +18,6 @@ import InputAdornment from "@material-ui/core/InputAdornment";
 import GridContainer from "components/Grid/GridContainer.js";
 import Card from "components/Card/Card.js";
 import CardHeader from "components/Card/CardHeader.js";
-import CardIcon from "components/Card/CardIcon.js";
 import CardBody from "components/Card/CardBody.js";
 
 import axios from "axios";
@@ -58,7 +55,6 @@ const useStyles = makeStyles((theme) => ({
 
 export default function General(props) {
   const classes = useStyles();
-  const history = useHistory();
 
   const [copied, setCopied] = React.useState(false);
   const [generated, setGenerated] = React.useState(false);
diff --git a/jams-react-client/src/views/Settings/Settings.js b/jams-react-client/src/views/Settings/Settings.js
index 0736d9e4..e8dde972 100644
--- a/jams-react-client/src/views/Settings/Settings.js
+++ b/jams-react-client/src/views/Settings/Settings.js
@@ -1,6 +1,4 @@
 import React from "react";
-// @material-ui/core components
-import { makeStyles } from "@material-ui/core/styles";
 // core components
 import General from "./General";
 import Subscription from "./Subscription";
@@ -75,10 +73,7 @@ const styles = {
   },
 };
 
-const useStyles = makeStyles(styles);
-
 export default function Settings(props) {
-  const classes = useStyles();
 
   const [value, setValue] = React.useState(0);
   const [error, setError] = React.useState(false);
diff --git a/jams-react-client/src/views/Settings/Subscription.js b/jams-react-client/src/views/Settings/Subscription.js
index 6509844e..a5ab8a6b 100644
--- a/jams-react-client/src/views/Settings/Subscription.js
+++ b/jams-react-client/src/views/Settings/Subscription.js
@@ -1,5 +1,4 @@
-import React, {useEffect, useState} from "react";
-import { useHistory } from "react-router-dom";
+import React, {useEffect} from "react";
 import { useFormik } from 'formik';
 import * as Yup from 'yup';
 import Button from '@material-ui/core/Button';
@@ -11,15 +10,12 @@ import { makeStyles } from '@material-ui/core/styles';
 import GridContainer from "components/Grid/GridContainer.js";
 import Card from "components/Card/Card.js";
 import CardHeader from "components/Card/CardHeader.js";
-import CardIcon from "components/Card/CardIcon.js";
 import CardBody from "components/Card/CardBody.js";
 
 import axios from 'axios';
 import configApiCall from '../../api'
 import { api_path_post_configuration_register_license } from '../../globalUrls'
 
-import auth from '../../auth'
-
 import i18next from "i18next";
 
 const useStyles = makeStyles((theme) => ({
@@ -44,14 +40,13 @@ const useStyles = makeStyles((theme) => ({
 
 export default function Subscription(props) {
     const classes = useStyles();
-    const history = useHistory();
     const [activated, setActivated] = React.useState(false);
 
     useEffect(()=>{
         axios(configApiCall(api_path_post_configuration_register_license, "GET", null, null)).then((response)=>{
-            if(response.data["activated"] == true) setActivated(true);
+            if(response.data["activated"] === true) setActivated(true);
         }).catch((error)=>{
-            if(error.response.status == 500){
+            if(error.response.status === 500){
                 props.setErrorMessage(i18next.t("an_error_occured_while_getting_license_information", "An error occurred while getting subscription information!"));
                 props.setSeverity("error");
                 props.setError(true);
@@ -86,7 +81,7 @@ export default function Subscription(props) {
             props.setSeverity("success");
             props.setError(true);
         }).catch((error)=>{
-            if(error.response.status == 500){
+            if(error.response.status === 500){
                 props.setErrorMessage(i18next.t("a_generic_occured_while_trying_to_load_license_or_license_could_not_be_found", "A generic occurred while trying to load your subscription or your subscription could not be found!"));
                 props.setSeverity("error");
                 props.setError(true);
diff --git a/jams-react-client/src/views/UserProfile/DisplayUserProfile.js b/jams-react-client/src/views/UserProfile/DisplayUserProfile.js
index 95672189..8b416f4c 100644
--- a/jams-react-client/src/views/UserProfile/DisplayUserProfile.js
+++ b/jams-react-client/src/views/UserProfile/DisplayUserProfile.js
@@ -1,5 +1,7 @@
 import React, { useEffect } from "react";
 import { useHistory } from "react-router-dom";
+import classnames from "classnames";
+
 // @material-ui/core components
 import { makeStyles } from "@material-ui/core/styles";
 // core components
@@ -17,22 +19,29 @@ import DialogContentText from "@material-ui/core/DialogContentText";
 import DialogTitle from "@material-ui/core/DialogTitle";
 import EditIcon from "@material-ui/icons/Edit";
 import DeleteIcon from "@material-ui/icons/Delete";
-import GroupIcon from "@material-ui/icons/Group";
+import AddCircleOutlineIcon from '@material-ui/icons/AddCircleOutline';
+
+import Table from "@material-ui/core/Table";
+import TableHead from '@material-ui/core/TableHead';
+import TableRow from "@material-ui/core/TableRow";
+import TableBody from "@material-ui/core/TableBody";
+import TableCell from "@material-ui/core/TableCell";
 
 import Grid from "@material-ui/core/Grid";
+import GridItem from "components/Grid/GridItem.js";
 import BusinessCenterOutlinedIcon from "@material-ui/icons/BusinessCenterOutlined";
 import AlternateEmailOutlinedIcon from "@material-ui/icons/AlternateEmailOutlined";
 import PhoneInTalkOutlinedIcon from "@material-ui/icons/PhoneInTalkOutlined";
 import SmartphoneOutlinedIcon from "@material-ui/icons/SmartphoneOutlined";
 import LocalPrintshopOutlinedIcon from "@material-ui/icons/LocalPrintshopOutlined";
 import PhoneForwardedOutlinedIcon from "@material-ui/icons/PhoneForwardedOutlined";
-import PersonOutlinedIcon from "@material-ui/icons/PersonOutlined";
 
 
 import Avatar from "@material-ui/core/Avatar";
 import Chip from "@material-ui/core/Chip";
-import DoneIcon from "@material-ui/icons/Done";
 import CardAvatar from "components/Card/CardAvatar";
+import PersonIcon from "../../../node_modules/@material-ui/icons/Person";
+import VpnKeyIcon from "@material-ui/icons/VpnKey";
 
 import List from "@material-ui/core/List";
 import ListItem from "@material-ui/core/ListItem";
@@ -56,12 +65,12 @@ import dashboardStyle from "assets/jss/material-dashboard-react/views/dashboardS
 
 import { hexToRgb, blackColor } from "assets/jss/material-dashboard-react.js";
 import axios from "axios";
-import PersonIcon from "../../../node_modules/@material-ui/icons/Person";
 
-import VpnKeyIcon from "@material-ui/icons/VpnKey";
 
 import PasswordDialog from "components/PasswordDialog/PasswordDialog";
 
+import TemporaryDrawer from "components/Drawer/Drawer"
+
 import i18next from "i18next";
 
 const styles = (theme) => ({
@@ -161,6 +170,7 @@ export default function DisplayUserProfile(props) {
   const classes = useStyles();
   const history = useHistory();
   const [user, setUser] = React.useState([]);
+  const [groupMemberships, setGroupMemberships] = React.useState([]);
   const [revoked, setRevoked] = React.useState(false);
   const [open, setOpen] = React.useState(false);
   const [revokedUser, setRevokedUser] = React.useState("");
@@ -168,7 +178,78 @@ export default function DisplayUserProfile(props) {
   const [loading, setLoading] = React.useState(false);
   const [progress, setProgress] = React.useState(0);
   const [groups, setGroups] = React.useState([]);
-  const [zeroGroup, setZeroGroup] = React.useState(false);
+  const [openDrawer, setOpenDrawer] = React.useState(false);
+
+  const searchGroups = (value) => {
+    if(value === "") value = "*";
+    axios(
+      configApiCall(
+        api_path_get_list_group + "?groupName=" + value,
+        "GET",
+        null,
+        null
+      )
+    )
+      .then((response) => {
+        if(response.data.length > 1) setGroups(response.data);
+        else setGroups([response.data])
+      })
+      .catch((error) => {
+        console.log(error);
+        if (error.response.status === 401) {
+          auth.authenticated = false;
+          history.push("/");
+        }
+      });
+  }
+
+  const updateUserInGroup = (group) => {
+    axios(
+      configApiCall(
+        api_path_get_list_group + "?groupName=" + group,
+        "GET",
+        null,
+        null
+      )
+    ).then((response)=>{
+      
+      let url = "";
+      let oldGroup = response.data.name;
+      if (response.blueprint == null) {
+        url =
+          api_path_put_update_group +
+          "?groupName=" +
+          response.data.name +
+          "&newName=" +
+          response.data.name +
+          "&blueprintName=&groupMembers=" +
+          [props.username];
+      } else {
+        url =
+          api_path_put_update_group +
+          "?groupName=" +
+          response.data.name +
+          "&newName=" +
+          response.data.name +
+          "&blueprintName=" +
+          group.data.blueprint +
+          "&groupMembers=" +
+          [props.username];
+      }
+  
+      axios(configApiCall(url, "PUT", null, null))
+        .then(() => {
+          let newGroupMemberships = groupMemberships;
+          if(newGroupMemberships.includes(oldGroup)) newGroupMemberships.splice(newGroupMemberships.indexOf(oldGroup), 1);
+          else newGroupMemberships.push(oldGroup);
+          setGroupMemberships(newGroupMemberships);
+        })
+        .catch((error) => {
+          console.log("Error updating group: " + error);
+        });
+    }).catch((error)=>{console.log(error)})
+
+  };
 
   useEffect(() => {
     setLoading(true);
@@ -202,43 +283,10 @@ export default function DisplayUserProfile(props) {
             )
               .then((response) => {
                 setUser(response.data);
-                const groupMemberships = response.data.groupMemberships;
-                axios(
-                  configApiCall(
-                    api_path_get_list_group + "?groupName=*",
-                    "GET",
-                    null,
-                    null
-                  )
-                )
-                  .then((response) => {
-                    let allGroups = response.data;
-                    if (allGroups.length === 0) setZeroGroup(true);
-                    else {
-                      setZeroGroup(false);
-
-                      allGroups.forEach((group) => {
-                        group.actif = false;
-                      });
-
-                      allGroups.forEach((group) => {
-                        groupMemberships.forEach((userGroup) => {
-                          if (group.name === userGroup) {
-                            group.actif = true;
-                          }
-                        });
-                      });
-                    }
-                    setGroups(allGroups);
-                    setLoading(false);
-                  })
-                  .catch((error) => {
-                    console.log(error);
-                    if (error.response.status === 401) {
-                      auth.authenticated = false;
-                      history.push("/");
-                    }
-                  });
+                if(response.data.groupMemberships == [""]) setGroupMemberships([])
+                else setGroupMemberships(response.data.groupMemberships)
+                searchGroups("*")
+                setLoading(false);
               })
               .catch((error) => {
                 console.log(error);
@@ -271,6 +319,7 @@ export default function DisplayUserProfile(props) {
             )
               .then((response) => {
                 setUser(response.data);
+                setGroupMemberships(response.data.groupMemberships)
                 setLoading(false);
               })
               .catch((error) => {
@@ -342,46 +391,7 @@ export default function DisplayUserProfile(props) {
     setChangePasswordOpen(false);
   };
 
-  const updateUserGroup = (group) => {
-    const newGroups = groups;
-    newGroups.forEach((g) => {
-      if (g === group) {
-        if (group.actif) g.actif = false;
-        else g.actif = true;
-      }
-    });
-    let url = "";
-    if (group.blueprint == null) {
-      url =
-        api_path_put_update_group +
-        "?groupName=" +
-        group.name +
-        "&newName=" +
-        group.name +
-        "&blueprintName=&groupMembers=" +
-        [props.username];
-    } else {
-      url =
-        api_path_put_update_group +
-        "?groupName=" +
-        group.name +
-        "&newName=" +
-        group.name +
-        "&blueprintName=" +
-        group.blueprint +
-        "&groupMembers=" +
-        [props.username];
-    }
-
-    axios(configApiCall(url, "PUT", null, null))
-      .then(() => {
-        setGroups(newGroups);
-      })
-      .catch((error) => {
-        console.log("Error updating group: " + error);
-      });
-  };
-
+  const tableCellClasses = classnames(classes.tableCell);
   
   return (
     <div>
@@ -419,8 +429,19 @@ export default function DisplayUserProfile(props) {
           <LinearProgress variant="determinate" value={progress} />
         )}
       </div>
+      <TemporaryDrawer 
+          openDrawer={openDrawer} 
+          setOpenDrawer={setOpenDrawer} 
+          direction="right" 
+          placeholder={i18next.t("add_user_to_group", "Add user to group ...")} 
+          searchTargets={searchGroups} 
+          targets={groups}
+          addElementToTarget={updateUserInGroup}
+          targetName={props.username}
+          type="group"
+      />
       {!loading && (<GridContainer>
-        <Grid item xs={12} sm={12} md={8}>
+        <Grid item xs={12} sm={12} md={6}>
           <Card profile>
             <CardBody profile>
               <div className={classes.root}>
@@ -473,31 +494,29 @@ export default function DisplayUserProfile(props) {
                           <ListItemText primary={user.phoneNumber} />
                         </ListItem>
                       )}
-                      {user.email && (
+                      {user.organization && (
                         <ListItem>
                           <ListItemAvatar>
                             <Avatar>
-                              <AlternateEmailOutlinedIcon />
+                              <BusinessCenterOutlinedIcon />
                             </Avatar>
                           </ListItemAvatar>
-                          <ListItemText primary={user.email} />
+                          <ListItemText primary={user.organization} />
                         </ListItem>
                       )}
-                      {user.organization && (
+                    </List>
+
+                    <List dense={false}>
+                      {user.email && (
                         <ListItem>
                           <ListItemAvatar>
                             <Avatar>
-                              <BusinessCenterOutlinedIcon />
+                              <AlternateEmailOutlinedIcon />
                             </Avatar>
                           </ListItemAvatar>
-                          <ListItemText primary={user.organization} />
+                          <ListItemText primary={user.email} />
                         </ListItem>
                       )}
-                    </List>
-                  </Grid>
-
-                  <Grid item xs={12} sm={12} md={6}>
-                    <List dense={false}>
                       {user.phoneNumberExtension && (
                         <ListItem>
                           <ListItemAvatar>
@@ -528,29 +547,6 @@ export default function DisplayUserProfile(props) {
                           <ListItemText primary={user.faxNumber} />
                         </ListItem>
                       )}
-                      {auth.hasAdminScope() && !zeroGroup && (
-                        <ListItem>
-                          <ListItemAvatar>
-                            <Avatar>
-                              <GroupIcon />
-                            </Avatar>
-                          </ListItemAvatar>
-                          <div className={classes.groups}>
-                            {groups.map((group) => (
-                              <Chip
-                                style={{ flex: 1 }}
-                                label={group.name}
-                                variant="default"
-                                clickable
-                                size="medium"
-                                onClick={() => updateUserGroup(group)}
-                                color={group.actif ? "primary" : "default"}
-                                icon={group.actif ? <DoneIcon /> : ""}
-                              />
-                            ))}
-                          </div>
-                        </ListItem>
-                      )}
                     </List>
                   </Grid>
                 </Grid>
@@ -597,6 +593,31 @@ export default function DisplayUserProfile(props) {
             </CardFooter>
           </Card>
         </Grid>
+          {auth.hasAdminScope() && (
+            <GridItem xs={12} sm={12} md={12}>
+                <Button color="primary" onClick={() => {setOpenDrawer(true)}}><AddCircleOutlineIcon /> {i18next.t("add_user_to_a_group", "Add user to a group")}</Button>
+                <Table className={classes.table}>
+                    <TableHead>
+                        <TableRow>
+                            <TableCell align="left">{i18next.t("group_name", "Group name")}</TableCell>
+                            <TableCell align="right">{i18next.t("action", "Action")}</TableCell>
+                        </TableRow>
+                    </TableHead>
+                    <TableBody>
+                        {groupMemberships.map(group =>
+                            <TableRow key={group} className={classes.tableRow}>
+                                <TableCell className={tableCellClasses}>
+                                    {group}
+                                </TableCell>
+                                <TableCell align="right" className={classes.tableActions}>
+                                    <Button color="primary" onClick={() => updateUserInGroup(group)}>{i18next.t("remove_from_group", "Remove from group")}</Button>
+                                </TableCell>
+                            </TableRow>
+                        ) }
+                    </TableBody>
+                </Table>
+            </GridItem>
+          )}
       </GridContainer>
       )}
     </div>
diff --git a/jams-react-client/src/views/UserProfile/EditCreateUserProfile.js b/jams-react-client/src/views/UserProfile/EditCreateUserProfile.js
index 6cf7c40d..3830d690 100644
--- a/jams-react-client/src/views/UserProfile/EditCreateUserProfile.js
+++ b/jams-react-client/src/views/UserProfile/EditCreateUserProfile.js
@@ -3,7 +3,6 @@ import { useHistory } from "react-router-dom";
 
 // @material-ui/core components
 import { makeStyles } from "@material-ui/core/styles";
-import InputLabel from "@material-ui/core/InputLabel";
 
 // core components
 import Grid from "@material-ui/core/Grid";
@@ -13,11 +12,8 @@ import Button from "components/CustomButtons/Button.js";
 import Card from "components/Card/Card.js";
 import CardAvatar from "components/Card/CardAvatar.js";
 import CardHeader from "components/Card/CardHeader.js";
-import CardIcon from "components/Card/CardIcon.js";
 import CardBody from "components/Card/CardBody.js";
 import CardFooter from "components/Card/CardFooter.js";
-import FormControl from "@material-ui/core/FormControl";
-import Input from "@material-ui/core/Input";
 import InputAdornment from "@material-ui/core/InputAdornment";
 import Slider from "@material-ui/core/Slider";
 import Typography from "@material-ui/core/Typography";
@@ -25,7 +21,6 @@ import Typography from "@material-ui/core/Typography";
 import Dialog from "@material-ui/core/Dialog";
 import DialogActions from "@material-ui/core/DialogActions";
 import DialogContent from "@material-ui/core/DialogContent";
-import DialogContentText from "@material-ui/core/DialogContentText";
 import DialogTitle from "@material-ui/core/DialogTitle";
 import Cropper from "react-easy-crop";
 import getCroppedImg from "./cropImage";
@@ -225,7 +220,6 @@ const useStyles = makeStyles(styles);
 export default function EditCreateUserProfile(props) {
   const classes = useStyles();
   const history = useHistory();
-  const [createUser, setCreateUser] = React.useState(props.createUser);
   const [copied, setCopied] = React.useState(false);
   const [generated, setGenerated] = React.useState(true);
   const [userExists, setUserExists] = React.useState(false);
@@ -242,9 +236,20 @@ export default function EditCreateUserProfile(props) {
   const [open, setOpen] = React.useState(false);
   const [crop, setCrop] = React.useState({ x: 0, y: 0 });
   const [zoom, setZoom] = React.useState(1);
-  const [aspect, setAspect] = React.useState(1);
   const [rotation, setRotation] = React.useState(0);
   const [passwordVisible, setPasswordVisible] = React.useState(false);
+
+  const passwordGenerator = () => {
+    return generator.generate({
+      length: 10,
+      uppercase: false,
+      numbers: true,
+      symbols: true,
+    });
+  };
+
+  const intialyGeneratedPassword = passwordGenerator();
+
   const [initialValues, setInitialValues] = React.useState({
     username: "",
     password: intialyGeneratedPassword,
@@ -261,20 +266,9 @@ export default function EditCreateUserProfile(props) {
     jamiId: "",
   });
 
-  const passwordGenerator = () => {
-    return generator.generate({
-      length: 10,
-      uppercase: false,
-      numbers: true,
-      symbols: true,
-    });
-  };
-
-  const intialyGeneratedPassword = passwordGenerator();
-
   React.useEffect(() => {
     
-    if (!createUser) {
+    if (!props.createUser) {
       setLoading(true);
       const timer = setInterval(() => {
         setProgress((oldProgress) => {
@@ -310,7 +304,7 @@ export default function EditCreateUserProfile(props) {
           }
           setInitialValues(values);
           setProfilePicture(user.profilePicture);
-          if (user.profilePicture != "") {
+          if (user.profilePicture !== "") {
             setProfilePicturePreview(
               "data:image/png;base64, " + user.profilePicture
             );
@@ -327,7 +321,7 @@ export default function EditCreateUserProfile(props) {
         clearInterval(timer);
       };
     }
-  }, []);
+  }, [props.createUser, props.username]);
 
   const resizeFile = (file, outputFormat) =>
     new Promise((resolve) => {
@@ -386,9 +380,7 @@ export default function EditCreateUserProfile(props) {
       })
       .catch((error) => {
         console.log(
-          "Failed to create new user. This is either because the username is already in use" +
-            " on the public nameserver, or another unknown error has occurred. " +
-            "Please choose another one."
+          "Failed to create new user. This is either because the username is already in use on the public nameserver, or another unknown error has occurred. Please choose another one."
         );
       });
   };
@@ -429,7 +421,7 @@ export default function EditCreateUserProfile(props) {
 
   const handleFormikSubmit = (values) => {
     values.profilePicture = profilePicture;
-    if (createUser) {
+    if (props.createUser) {
       handleCreateUser(values);
     } else {
       handleUpdateUser(values);
@@ -476,10 +468,10 @@ export default function EditCreateUserProfile(props) {
       .max(32, i18next.t("maximum_32_characters", "Maximum 32 characters!"))
       .required(i18next.t("username_is_required", "Username is required!"))
       .matches(/^[A-Za-z_][A-Za-z0-9_]*$/, i18next.t("only_alphanumeric_characters", "Only alphanumeric characters!")),
-    password: createUser
+    password: props.createUser
       ? Yup.string().required(i18next.t("only_alphanumeric_characters", "Password is required!"))
       : null,
-    confirmPassword: createUser
+    confirmPassword: props.createUser
       ? Yup.string().oneOf([Yup.ref("password"), null], i18next.t("password_must_match", "Passwords must match"))
       : null,
     firstName: Yup.string().min(2, i18next.t("first_name_is_too_short", "First name is too short!")),
@@ -520,7 +512,7 @@ export default function EditCreateUserProfile(props) {
               crop={crop}
               rotation={rotation}
               zoom={zoom}
-              aspect={aspect}
+              aspect={1}
               onCropChange={setCrop}
               onRotationChange={setRotation}
               onCropComplete={onCropComplete}
@@ -587,9 +579,9 @@ export default function EditCreateUserProfile(props) {
                 <Card profile>
                   <CardHeader color="info" stats icon className={classes.profileEditHeaderMobile}>
                     <p className={classes.cardCategory}>
-                      {createUser ? i18next.t("create_new_profile", "Create new profile") : i18next.t("edit_profile", "Edit profile")}
+                      {props.createUser ? i18next.t("create_new_profile", "Create new profile") : i18next.t("edit_profile", "Edit profile")}
                     </p>
-                    {createUser ? (
+                    {props.createUser ? (
                       ""
                     ) : (
                       <h3 className={classes.cardTitle}>{userName}</h3>
@@ -631,7 +623,7 @@ export default function EditCreateUserProfile(props) {
                             {i18next.t("change_profile_image", "Change profile image")}
                           </label>
                         </Grid>
-                        {createUser && (
+                        {props.createUser && (
                           <Grid item xs={12} sm={12} md={6}>
                             <FormikField
                               name="username"
@@ -661,10 +653,10 @@ export default function EditCreateUserProfile(props) {
                             />
                           </Grid>
                         )}
-                        {createUser && (
+                        {props.createUser && (
                           <Grid item xs={12} sm={12} md={6}></Grid>
                         )}
-                        {createUser && (
+                        {props.createUser && (
                           <Grid item xs={12} sm={12} md={6}>
                             <FormikField
                               type={passwordVisible ? "text" : "password"}
@@ -697,7 +689,7 @@ export default function EditCreateUserProfile(props) {
                             />
                           </Grid>
                         )}
-                        {createUser && (
+                        {props.createUser && (
                           <Grid item xs={12} sm={12} md={6}>
                             <FormikField
                               type={passwordVisible ? "text" : "password"}
@@ -730,10 +722,10 @@ export default function EditCreateUserProfile(props) {
                             />
                           </Grid>
                         )}
-                        {createUser && (
+                        {props.createUser && (
                           <Grid item align="left" xs={12} sm={12} md={6}></Grid>
                         )}
-                        {createUser && (
+                        {props.createUser && (
                           <Grid item align="left" xs={12} sm={12} md={6}>
                             <Button
                               variant="contained"
@@ -910,12 +902,12 @@ export default function EditCreateUserProfile(props) {
                     </div>
                   </CardBody>
                   <CardFooter className={classes.alignRight}>
-                    {!createUser && (
+                    {!props.createUser && (
                       <Button color="info" onClick={handleCancelUpdate}>
                         {i18next.t("cancel", "Cancel")}
                       </Button>
                     )}
-                    {createUser ? (
+                    {props.createUser ? (
                       <Button
                         type="submit"
                         disabled={!isValid || !dirty || userExists}
diff --git a/jams-react-client/src/views/UserProfile/UserProfile.js b/jams-react-client/src/views/UserProfile/UserProfile.js
index 472a6a3a..cf6ee46a 100755
--- a/jams-react-client/src/views/UserProfile/UserProfile.js
+++ b/jams-react-client/src/views/UserProfile/UserProfile.js
@@ -1,6 +1,4 @@
 import React from "react";
-// @material-ui/core components
-import { makeStyles } from "@material-ui/core/styles";
 // core components
 import Devices from "components/Devices/Devices.js";
 
@@ -53,32 +51,9 @@ function a11yProps(index) {
   };
 }
 
-const styles = {
-  cardCategoryWhite: {
-    color: "rgba(255,255,255,.62)",
-    margin: "0",
-    fontSize: "14px",
-    marginTop: "0",
-    marginBottom: "0",
-  },
-  cardTitleWhite: {
-    color: "#FFFFFF",
-    marginTop: "0px",
-    minHeight: "auto",
-    fontWeight: "300",
-    fontFamily: "'Roboto', 'Helvetica', 'Arial', sans-serif",
-    marginBottom: "3px",
-    textDecoration: "none",
-  },
-};
-
-const useStyles = makeStyles(styles);
-
 export default function UserProfile(props) {
-  const classes = useStyles();
 
   const [value, setValue] = React.useState(0);
-  const [username, setUsername] = React.useState(props.username);
   const [displayUser, setDisplayUser] = React.useState(true);
 
   const handleChange = (event, newValue) => {
@@ -103,12 +78,12 @@ export default function UserProfile(props) {
       <TabPanel value={value} index={0}>
         {displayUser ? (
           <DisplayUserProfile
-            username={username}
+            username={props.username}
             setDisplayUser={setDisplayUser}
           />
         ) : (
           <EditCreateUserProfile
-            username={username}
+            username={props.username}
             createUser={false}
             setDisplayUser={setDisplayUser}
           />
diff --git a/jams-react-client/src/views/Users/Users.js b/jams-react-client/src/views/Users/Users.js
index 402b7eb4..715c9867 100644
--- a/jams-react-client/src/views/Users/Users.js
+++ b/jams-react-client/src/views/Users/Users.js
@@ -1,9 +1,7 @@
 import React, { useState, useEffect, useCallback } from "react";
 import { useHistory } from "react-router-dom";
-import { Switch, Route, Redirect } from "react-router-dom";
 // @material-ui/core components
 import { makeStyles } from "@material-ui/core/styles";
-import InputLabel from "@material-ui/core/InputLabel";
 import Pagination from "@material-ui/lab/Pagination";
 // core components
 import GridItem from "components/Grid/GridItem.js";
@@ -11,25 +9,19 @@ import GridContainer from "components/Grid/GridContainer.js";
 import CustomInput from "components/CustomInput/CustomInput.js";
 import Button from "components/CustomButtons/Button.js";
 import Card from "components/Card/Card.js";
-import CardHeader from "components/Card/CardHeader.js";
+
 import CardAvatar from "components/Card/CardAvatar.js";
 import CardBody from "components/Card/CardBody.js";
-import CardFooter from "components/Card/CardFooter.js";
+
 import UserProfile from "views/UserProfile/UserProfile.js";
-import Divider from "@material-ui/core/Divider";
-import PersonIcon from "@material-ui/icons/Person";
-import PermIdentityIcon from "@material-ui/icons/PermIdentity";
-import PhoneOutlinedIcon from "@material-ui/icons/PhoneOutlined";
 import InfoIcon from "@material-ui/icons/Info";
 import BusinessOutlinedIcon from "@material-ui/icons/BusinessOutlined";
 import Search from "@material-ui/icons/Search";
-import MailOutlineIcon from "@material-ui/icons/MailOutline";
 import axios from "axios";
 import configApiCall from "api.js";
 import auth from "auth.js";
 import { api_path_get_user_directory_search } from "globalUrls";
 import AddCircleOutlineIcon from "@material-ui/icons/AddCircleOutline";
-import KeyboardReturnIcon from "@material-ui/icons/KeyboardReturn";
 import jami from "assets/img/faces/jami.png";
 import noProfilePicture from "assets/img/faces/no-profile-picture.png";
 import EditCreateUserProfile from "views/UserProfile/EditCreateUserProfile";
@@ -113,7 +105,7 @@ export default function Users(props) {
       )
     )
       .then((response) => {
-        if (response.status != 204) {
+        if (response.status !== 204) {
           setUsers(response.data.profiles);
 
           setNumberPages(response.data.numPages);
@@ -124,7 +116,7 @@ export default function Users(props) {
       })
       .catch((error) => {
         console.log(error);
-        if (error.response.status == 401) {
+        if (error.response.status === 401) {
           auth.authenticated = false;
           history.push("/");
         }
@@ -144,15 +136,6 @@ export default function Users(props) {
     setLoading(true);
     setNoMatchFound(false);
     setUsers([]);
-    const timer = setInterval(() => {
-      setProgress((oldProgress) => {
-        if (oldProgress === 100) {
-          return 0;
-        }
-        const diff = Math.random() * 10;
-        return Math.min(oldProgress + diff, 100);
-      });
-    }, 500);
     axios(
       configApiCall(
         api_path_get_user_directory_search,
@@ -163,7 +146,7 @@ export default function Users(props) {
     )
       .then((response) => {
         setLoading(false);
-        if (response.status != 204) {
+        if (response.status !== 204) {
           setUsers(response.data.profiles);
           setNumberPages(response.data.numPages);
         } else {
@@ -175,7 +158,7 @@ export default function Users(props) {
         setUsers([]);
 
         setNoMatchFound(true);
-        if (error.response.status == 401) {
+        if (error.response.status === 401) {
           auth.authenticated = false;
           history.push("/");
         }
@@ -193,13 +176,13 @@ export default function Users(props) {
   const handleChangePage = (e, page) => {
     searchUsers(searchValue, page);
   };
-  if (!auth.hasAdminScope() && auth.getUsername() != "") {
+  if (!auth.hasAdminScope() && auth.getUsername() !== "") {
     return (
       <div>
         <UserProfile username={auth.getUsername()} />
       </div>
     );
-  } else if (selectedProfile && auth.hasAdminScope() && selectedUsername != "") {
+  } else if (selectedProfile && auth.hasAdminScope() && selectedUsername !== "") {
     return (
       <div>
         <UserProfile username={selectedUsername} />
-- 
GitLab