From e5e5697a612a71315d2b9cae917befb863232cea Mon Sep 17 00:00:00 2001
From: pkupczyk <piotr.kupczyk@id.ethz.ch>
Date: Wed, 2 Sep 2020 17:28:02 +0200
Subject: [PATCH] NG_UI : user management : SSDM-10062 - add initial version of
 'Roles' section

---
 openbis_ng_ui/src/js/common/consts/ids.js     |   2 +
 .../src/js/components/users/form/UserForm.jsx | 105 ++++++++--
 .../components/users/form/UserFormButtons.jsx |  16 +-
 .../users/form/UserFormController.js          |   5 +
 .../users/form/UserFormControllerAddGroup.js  |   7 +-
 .../users/form/UserFormControllerAddRole.js   |  42 ++++
 .../users/form/UserFormControllerChange.js    |  16 +-
 .../users/form/UserFormControllerLoad.js      |  61 +++++-
 .../users/form/UserFormControllerRemove.js    |  19 ++
 .../components/users/form/UserFormFacade.js   |   6 +-
 .../users/form/UserFormParameters.jsx         |  56 ------
 .../users/form/UserFormParametersRole.jsx     | 186 ++++++++++++++++++
 12 files changed, 424 insertions(+), 97 deletions(-)
 create mode 100644 openbis_ng_ui/src/js/components/users/form/UserFormControllerAddRole.js
 delete mode 100644 openbis_ng_ui/src/js/components/users/form/UserFormParameters.jsx
 create mode 100644 openbis_ng_ui/src/js/components/users/form/UserFormParametersRole.jsx

diff --git a/openbis_ng_ui/src/js/common/consts/ids.js b/openbis_ng_ui/src/js/common/consts/ids.js
index 2dc98bd93bb..e37e8f47a69 100644
--- a/openbis_ng_ui/src/js/common/consts/ids.js
+++ b/openbis_ng_ui/src/js/common/consts/ids.js
@@ -2,6 +2,7 @@ const WEB_APP_ID = 'openbis_ng_ui'
 const TYPES_GRID_ID = 'types_grid'
 const USERS_GRID_ID = 'users_grid'
 const USER_GROUPS_GRID_ID = 'user_groups_grid'
+const USER_ROLES_GRID_ID = 'user_roles_grid'
 const VOCABULARY_TERMS_GRID_ID = 'vocabulary_terms_grid'
 
 export default {
@@ -9,5 +10,6 @@ export default {
   TYPES_GRID_ID,
   USERS_GRID_ID,
   USER_GROUPS_GRID_ID,
+  USER_ROLES_GRID_ID,
   VOCABULARY_TERMS_GRID_ID
 }
diff --git a/openbis_ng_ui/src/js/components/users/form/UserForm.jsx b/openbis_ng_ui/src/js/components/users/form/UserForm.jsx
index dee72a3bad0..fb2dd9089c1 100644
--- a/openbis_ng_ui/src/js/components/users/form/UserForm.jsx
+++ b/openbis_ng_ui/src/js/components/users/form/UserForm.jsx
@@ -13,12 +13,14 @@ import logger from '@src/js/common/logger.js'
 
 import UserFormController from './UserFormController.js'
 import UserFormFacade from './UserFormFacade.js'
-import UserFormParameters from './UserFormParameters.jsx'
+import UserFormParametersUser from './UserFormParametersUser.jsx'
+import UserFormParametersGroup from './UserFormParametersGroup.jsx'
+import UserFormParametersRole from './UserFormParametersRole.jsx'
 import UserFormButtons from './UserFormButtons.jsx'
 
 const styles = () => ({})
 
-const columns = [
+const USER_GROUPS_GRID_COLUMNS = [
   {
     field: 'code.value',
     label: 'Code',
@@ -30,6 +32,22 @@ const columns = [
   }
 ]
 
+const USER_ROLES_GRID_COLUMNS = [
+  {
+    field: 'space.value',
+    label: 'Space',
+    sort: 'asc'
+  },
+  {
+    field: 'project.value',
+    label: 'Project'
+  },
+  {
+    field: 'role.value',
+    label: 'Role'
+  }
+]
+
 class UserForm extends React.PureComponent {
   constructor(props) {
     super(props)
@@ -54,7 +72,7 @@ class UserForm extends React.PureComponent {
     this.controller.handleSelectionChange()
   }
 
-  handleSelectedRowChange(row) {
+  handleSelectedGroupRowChange(row) {
     const { controller } = this
     if (row) {
       controller.handleSelectionChange('group', { id: row.id })
@@ -63,8 +81,21 @@ class UserForm extends React.PureComponent {
     }
   }
 
-  handleGridControllerRef(gridController) {
-    this.controller.gridController = gridController
+  handleSelectedRoleRowChange(row) {
+    const { controller } = this
+    if (row) {
+      controller.handleSelectionChange('role', { id: row.id })
+    } else {
+      controller.handleSelectionChange()
+    }
+  }
+
+  handleGroupsGridControllerRef(gridController) {
+    this.controller.groupsGridController = gridController
+  }
+
+  handleRolesGridControllerRef(gridController) {
+    this.controller.rolesGridController = gridController
   }
 
   render() {
@@ -85,20 +116,31 @@ class UserForm extends React.PureComponent {
   }
 
   renderMainPanel() {
-    const { groups, selection } = this.state
+    const { groups, roles, selection } = this.state
 
     return (
       <GridContainer onClick={this.handleClickContainer}>
         <Header>Groups</Header>
         <Grid
           id={ids.USER_GROUPS_GRID_ID}
-          controllerRef={this.handleGridControllerRef}
-          columns={columns}
+          controllerRef={this.handleGroupsGridControllerRef}
+          columns={USER_GROUPS_GRID_COLUMNS}
           rows={groups}
           selectedRowId={
             selection && selection.type === 'group' ? selection.params.id : null
           }
-          onSelectedRowChange={this.handleSelectedRowChange}
+          onSelectedRowChange={this.handleSelectedGroupRowChange}
+        />
+        <Header>Roles</Header>
+        <Grid
+          id={ids.USER_ROLES_GRID_ID}
+          controllerRef={this.handleRolesGridControllerRef}
+          columns={USER_ROLES_GRID_COLUMNS}
+          rows={roles}
+          selectedRowId={
+            selection && selection.type === 'role' ? selection.params.id : null
+          }
+          onSelectedRowChange={this.handleSelectedRoleRowChange}
         />
       </GridContainer>
     )
@@ -106,24 +148,44 @@ class UserForm extends React.PureComponent {
 
   renderAdditionalPanel() {
     const { controller } = this
-    const { user, groups, selection, mode } = this.state
+    const { user, groups, roles, selection, mode } = this.state
 
     const selectedRow = controller.gridController
       ? controller.gridController.getSelectedRow()
       : null
 
     return (
-      <UserFormParameters
-        controller={controller}
-        user={user}
-        groups={groups}
-        selection={selection}
-        selectedRow={selectedRow}
-        mode={mode}
-        onChange={controller.handleChange}
-        onSelectionChange={controller.handleSelectionChange}
-        onBlur={controller.handleBlur}
-      />
+      <div>
+        <UserFormParametersUser
+          controller={controller}
+          user={user}
+          selection={selection}
+          mode={mode}
+          onChange={controller.handleChange}
+          onSelectionChange={controller.handleSelectionChange}
+          onBlur={controller.handleBlur}
+        />
+        <UserFormParametersGroup
+          controller={controller}
+          groups={groups}
+          selection={selection}
+          selectedRow={selectedRow}
+          mode={mode}
+          onChange={controller.handleChange}
+          onSelectionChange={controller.handleSelectionChange}
+          onBlur={controller.handleBlur}
+        />
+        <UserFormParametersRole
+          controller={controller}
+          roles={roles}
+          selection={selection}
+          selectedRow={selectedRow}
+          mode={mode}
+          onChange={controller.handleChange}
+          onSelectionChange={controller.handleSelectionChange}
+          onBlur={controller.handleBlur}
+        />
+      </div>
     )
   }
 
@@ -137,6 +199,7 @@ class UserForm extends React.PureComponent {
         onSave={controller.handleSave}
         onCancel={controller.handleCancel}
         onAddGroup={controller.handleAddGroup}
+        onAddRole={controller.handleAddRole}
         onRemove={controller.handleRemove}
         user={user}
         selection={selection}
diff --git a/openbis_ng_ui/src/js/components/users/form/UserFormButtons.jsx b/openbis_ng_ui/src/js/components/users/form/UserFormButtons.jsx
index c1a3593de65..e39689eca60 100644
--- a/openbis_ng_ui/src/js/components/users/form/UserFormButtons.jsx
+++ b/openbis_ng_ui/src/js/components/users/form/UserFormButtons.jsx
@@ -28,7 +28,7 @@ class UserFormButtons extends React.PureComponent {
   }
 
   renderAdditionalButtons(classes) {
-    const { onAddGroup, onRemove } = this.props
+    const { onAddGroup, onAddRole, onRemove } = this.props
 
     return (
       <React.Fragment>
@@ -38,20 +38,28 @@ class UserFormButtons extends React.PureComponent {
           styles={{ root: classes.button }}
           onClick={onAddGroup}
         />
+        <Button
+          name='addRole'
+          label='Add Role'
+          styles={{ root: classes.button }}
+          onClick={onAddRole}
+        />
         <Button
           name='remove'
           label='Remove'
           styles={{ root: classes.button }}
-          disabled={!this.isGroupSelected()}
+          disabled={!this.isGroupOrRoleSelected()}
           onClick={onRemove}
         />
       </React.Fragment>
     )
   }
 
-  isGroupSelected() {
+  isGroupOrRoleSelected() {
     const { selection } = this.props
-    return selection && selection.type === 'group'
+    return (
+      selection && (selection.type === 'group' || selection.type === 'role')
+    )
   }
 }
 
diff --git a/openbis_ng_ui/src/js/components/users/form/UserFormController.js b/openbis_ng_ui/src/js/components/users/form/UserFormController.js
index ed8df64537a..f103ba44f18 100644
--- a/openbis_ng_ui/src/js/components/users/form/UserFormController.js
+++ b/openbis_ng_ui/src/js/components/users/form/UserFormController.js
@@ -1,6 +1,7 @@
 import PageController from '@src/js/components/common/page/PageController.js'
 import UserFormControllerLoad from './UserFormControllerLoad.js'
 import UserFormControllerAddGroup from './UserFormControllerAddGroup.js'
+import UserFormControllerAddRole from './UserFormControllerAddRole.js'
 import UserFormControllerRemove from './UserFormControllerRemove.js'
 import UserFormControllerValidate from './UserFormControllerValidate.js'
 import UserFormControllerChange from './UserFormControllerChange.js'
@@ -37,6 +38,10 @@ export default class UserFormController extends PageController {
     return new UserFormControllerAddGroup(this).execute()
   }
 
+  handleAddRole() {
+    return new UserFormControllerAddRole(this).execute()
+  }
+
   handleRemove() {
     return new UserFormControllerRemove(this).execute()
   }
diff --git a/openbis_ng_ui/src/js/components/users/form/UserFormControllerAddGroup.js b/openbis_ng_ui/src/js/components/users/form/UserFormControllerAddGroup.js
index bccefc4823a..c9a4e0d1a07 100644
--- a/openbis_ng_ui/src/js/components/users/form/UserFormControllerAddGroup.js
+++ b/openbis_ng_ui/src/js/components/users/form/UserFormControllerAddGroup.js
@@ -1,10 +1,9 @@
 import FormUtil from '@src/js/components/common/form/FormUtil.js'
 
-export default class UserFormControllerAdd {
+export default class UserFormControllerAddGroup {
   constructor(controller) {
     this.controller = controller
     this.context = controller.context
-    this.gridController = controller.gridController
   }
 
   async execute() {
@@ -35,8 +34,8 @@ export default class UserFormControllerAdd {
 
     await this.controller.changed(true)
 
-    if (this.gridController) {
-      await this.gridController.showSelectedRow()
+    if (this.controller.groupsGridController) {
+      await this.controller.groupsGridController.showSelectedRow()
     }
   }
 }
diff --git a/openbis_ng_ui/src/js/components/users/form/UserFormControllerAddRole.js b/openbis_ng_ui/src/js/components/users/form/UserFormControllerAddRole.js
new file mode 100644
index 00000000000..324589b2008
--- /dev/null
+++ b/openbis_ng_ui/src/js/components/users/form/UserFormControllerAddRole.js
@@ -0,0 +1,42 @@
+import FormUtil from '@src/js/components/common/form/FormUtil.js'
+
+export default class UserFormControllerAddRole {
+  constructor(controller) {
+    this.controller = controller
+    this.context = controller.context
+  }
+
+  async execute() {
+    let { roles, rolesCounter } = this.context.getState()
+
+    const newRole = {
+      id: 'role-' + rolesCounter++,
+      space: FormUtil.createField({}),
+      project: FormUtil.createField({}),
+      role: FormUtil.createField({}),
+      original: null
+    }
+
+    const newRoles = Array.from(roles)
+    newRoles.push(newRole)
+
+    await this.context.setState(state => ({
+      ...state,
+      roles: newRoles,
+      rolesCounter,
+      selection: {
+        type: 'role',
+        params: {
+          id: newRole.id,
+          part: 'space'
+        }
+      }
+    }))
+
+    await this.controller.changed(true)
+
+    if (this.controller.rolesGridController) {
+      await this.controller.rolesGridController.showSelectedRow()
+    }
+  }
+}
diff --git a/openbis_ng_ui/src/js/components/users/form/UserFormControllerChange.js b/openbis_ng_ui/src/js/components/users/form/UserFormControllerChange.js
index 3c8ebf6927f..c34ab33b69c 100644
--- a/openbis_ng_ui/src/js/components/users/form/UserFormControllerChange.js
+++ b/openbis_ng_ui/src/js/components/users/form/UserFormControllerChange.js
@@ -1,11 +1,6 @@
 import PageControllerChange from '@src/js/components/common/page/PageControllerChange.js'
 
 export default class UserFormControllerChange extends PageControllerChange {
-  constructor(controller) {
-    super(controller)
-    this.gridController = controller.gridController
-  }
-
   async execute(type, params) {
     if (type === 'user') {
       const { field, value } = params
@@ -14,8 +9,15 @@ export default class UserFormControllerChange extends PageControllerChange {
       const { id, field, value } = params
       await this.changeCollectionItemField('groups', id, field, value)
 
-      if (this.gridController) {
-        this.gridController.showSelectedRow()
+      if (this.controller.groupsGridController) {
+        await this.controller.groupsGridController.showSelectedRow()
+      }
+    } else if (type === 'role') {
+      const { id, field, value } = params
+      await this.changeCollectionItemField('roles', id, field, value)
+
+      if (this.controller.rolesGridController) {
+        await this.controller.rolesGridController.showSelectedRow()
       }
     }
   }
diff --git a/openbis_ng_ui/src/js/components/users/form/UserFormControllerLoad.js b/openbis_ng_ui/src/js/components/users/form/UserFormControllerLoad.js
index f84c97374df..c2bf5ef3f71 100644
--- a/openbis_ng_ui/src/js/components/users/form/UserFormControllerLoad.js
+++ b/openbis_ng_ui/src/js/components/users/form/UserFormControllerLoad.js
@@ -28,12 +28,25 @@ export default class UserFormControllerLoad extends PageControllerLoad {
       )
     }
 
-    const selection = this._createSelection(groups)
+    let rolesCounter = 0
+    let roles = []
+
+    if (loadedUser && loadedUser.getRoleAssignments()) {
+      roles = loadedUser
+        .getRoleAssignments()
+        .map(loadedRole =>
+          this._createRole('role-' + rolesCounter++, loadedRole)
+        )
+    }
+
+    const selection = this._createSelection(groups, roles)
 
     return this.context.setState({
       user,
       groups,
       groupsCounter,
+      roles,
+      rolesCounter,
       selection,
       original: {
         user: user.original,
@@ -79,6 +92,26 @@ export default class UserFormControllerLoad extends PageControllerLoad {
     return user
   }
 
+  _createRole(id, loadedRole) {
+    const role = {
+      id: id,
+      space: FormUtil.createField({
+        value: _.get(loadedRole, 'space.code', null),
+        enabled: false
+      }),
+      project: FormUtil.createField({
+        value: _.get(loadedRole, 'project.permId.permId', null),
+        enabled: false
+      }),
+      role: FormUtil.createField({
+        value: _.get(loadedRole, 'role', null),
+        enabled: false
+      })
+    }
+    role.original = _.cloneDeep(role)
+    return role
+  }
+
   _createGroup(id, loadedGroup) {
     const group = {
       id: id,
@@ -95,10 +128,11 @@ export default class UserFormControllerLoad extends PageControllerLoad {
     return group
   }
 
-  _createSelection(newGroups) {
+  _createSelection(newGroups, newRoles) {
     const {
       selection: oldSelection,
-      groups: oldGroups
+      groups: oldGroups,
+      roles: oldRoles
     } = this.context.getState()
 
     if (!oldSelection) {
@@ -121,6 +155,27 @@ export default class UserFormControllerLoad extends PageControllerLoad {
           }
         }
       }
+    } else if (oldSelection.type === 'role') {
+      const oldRole = _.find(
+        oldRoles,
+        oldRole => oldRole.id === oldSelection.params.id
+      )
+      const newRole = _.find(
+        newRoles,
+        newRole =>
+          newRole.space.value === oldRole.space.value &&
+          newRole.project.value === oldRole.project.value &&
+          newRole.role.value === oldRole.role.value
+      )
+
+      if (newRole) {
+        return {
+          type: 'role',
+          params: {
+            id: newRole.id
+          }
+        }
+      }
     } else {
       return null
     }
diff --git a/openbis_ng_ui/src/js/components/users/form/UserFormControllerRemove.js b/openbis_ng_ui/src/js/components/users/form/UserFormControllerRemove.js
index 45c3217abfb..065258f986d 100644
--- a/openbis_ng_ui/src/js/components/users/form/UserFormControllerRemove.js
+++ b/openbis_ng_ui/src/js/components/users/form/UserFormControllerRemove.js
@@ -8,6 +8,8 @@ export default class UserFormControllerRemove {
     const { selection } = this.context.getState()
     if (selection.type === 'group') {
       this._handleRemoveGroup(selection.params.id)
+    } else if (selection.type === 'role') {
+      this._handleRemoveRole(selection.params.id)
     }
   }
 
@@ -27,4 +29,21 @@ export default class UserFormControllerRemove {
 
     this.controller.changed(true)
   }
+
+  _handleRemoveRole(roleId) {
+    const { roles } = this.context.getState()
+
+    const roleIndex = roles.findIndex(role => role.id === roleId)
+
+    const newRoles = Array.from(roles)
+    newRoles.splice(roleIndex, 1)
+
+    this.context.setState(state => ({
+      ...state,
+      roles: newRoles,
+      selection: null
+    }))
+
+    this.controller.changed(true)
+  }
 }
diff --git a/openbis_ng_ui/src/js/components/users/form/UserFormFacade.js b/openbis_ng_ui/src/js/components/users/form/UserFormFacade.js
index 2d5c565c394..44489b8df47 100644
--- a/openbis_ng_ui/src/js/components/users/form/UserFormFacade.js
+++ b/openbis_ng_ui/src/js/components/users/form/UserFormFacade.js
@@ -5,7 +5,8 @@ export default class UserFormFacade {
     const id = new openbis.PersonPermId(userId)
     const fo = new openbis.PersonFetchOptions()
     fo.withSpace()
-    fo.withRoleAssignments()
+    fo.withRoleAssignments().withSpace()
+    fo.withRoleAssignments().withProject()
     return openbis.getPersons([id], fo).then(map => {
       return map[userId]
     })
@@ -15,7 +16,8 @@ export default class UserFormFacade {
     const criteria = new openbis.AuthorizationGroupSearchCriteria()
     const fo = new openbis.AuthorizationGroupFetchOptions()
     fo.withUsers()
-    fo.withRoleAssignments()
+    fo.withRoleAssignments().withSpace()
+    fo.withRoleAssignments().withProject()
     return openbis.searchAuthorizationGroups(criteria, fo).then(result => {
       return result.getObjects().filter(group => {
         return group.getUsers().some(user => {
diff --git a/openbis_ng_ui/src/js/components/users/form/UserFormParameters.jsx b/openbis_ng_ui/src/js/components/users/form/UserFormParameters.jsx
deleted file mode 100644
index d2e6b752845..00000000000
--- a/openbis_ng_ui/src/js/components/users/form/UserFormParameters.jsx
+++ /dev/null
@@ -1,56 +0,0 @@
-import React from 'react'
-import { withStyles } from '@material-ui/core/styles'
-import logger from '@src/js/common/logger.js'
-
-import UserFormParametersUser from './UserFormParametersUser.jsx'
-import UserFormParametersGroup from './UserFormParametersGroup.jsx'
-
-const styles = () => ({})
-
-class UserFormParameters extends React.PureComponent {
-  constructor(props) {
-    super(props)
-  }
-
-  render() {
-    logger.log(logger.DEBUG, 'UserFormParameters.render')
-
-    const {
-      controller,
-      user,
-      groups,
-      selection,
-      selectedRow,
-      mode,
-      onChange,
-      onSelectionChange,
-      onBlur
-    } = this.props
-
-    return (
-      <div>
-        <UserFormParametersUser
-          controller={controller}
-          user={user}
-          selection={selection}
-          mode={mode}
-          onChange={onChange}
-          onSelectionChange={onSelectionChange}
-          onBlur={onBlur}
-        />
-        <UserFormParametersGroup
-          controller={controller}
-          groups={groups}
-          selection={selection}
-          selectedRow={selectedRow}
-          mode={mode}
-          onChange={onChange}
-          onSelectionChange={onSelectionChange}
-          onBlur={onBlur}
-        />
-      </div>
-    )
-  }
-}
-
-export default withStyles(styles)(UserFormParameters)
diff --git a/openbis_ng_ui/src/js/components/users/form/UserFormParametersRole.jsx b/openbis_ng_ui/src/js/components/users/form/UserFormParametersRole.jsx
new file mode 100644
index 00000000000..4032b45c0aa
--- /dev/null
+++ b/openbis_ng_ui/src/js/components/users/form/UserFormParametersRole.jsx
@@ -0,0 +1,186 @@
+import React from 'react'
+import { withStyles } from '@material-ui/core/styles'
+import Container from '@src/js/components/common/form/Container.jsx'
+import Header from '@src/js/components/common/form/Header.jsx'
+import TextField from '@src/js/components/common/form/TextField.jsx'
+import logger from '@src/js/common/logger.js'
+
+const styles = theme => ({
+  field: {
+    paddingBottom: theme.spacing(1)
+  }
+})
+
+class UserFormParametersRole extends React.PureComponent {
+  constructor(props) {
+    super(props)
+    this.state = {}
+    this.references = {
+      space: React.createRef(),
+      project: React.createRef(),
+      role: React.createRef()
+    }
+    this.handleChange = this.handleChange.bind(this)
+    this.handleFocus = this.handleFocus.bind(this)
+    this.handleBlur = this.handleBlur.bind(this)
+  }
+
+  componentDidMount() {
+    this.focus()
+  }
+
+  componentDidUpdate(prevProps) {
+    const prevSelection = prevProps.selection
+    const selection = this.props.selection
+
+    if (prevSelection !== selection) {
+      this.focus()
+    }
+  }
+
+  focus() {
+    const role = this.getRole(this.props)
+    if (role && this.props.selection) {
+      const { part } = this.props.selection.params
+      if (part) {
+        const reference = this.references[part]
+        if (reference && reference.current) {
+          reference.current.focus()
+        }
+      }
+    }
+  }
+
+  handleChange(event) {
+    const role = this.getRole(this.props)
+    this.props.onChange('role', {
+      id: role.id,
+      field: event.target.name,
+      value: event.target.value
+    })
+  }
+
+  handleFocus(event) {
+    const role = this.getRole(this.props)
+    this.props.onSelectionChange('role', {
+      id: role.id,
+      part: event.target.name
+    })
+  }
+
+  handleBlur() {
+    this.props.onBlur()
+  }
+
+  render() {
+    logger.log(logger.DEBUG, 'UserFormParametersRole.render')
+
+    const role = this.getRole(this.props)
+    if (!role) {
+      return null
+    }
+
+    return (
+      <Container>
+        <Header>Role</Header>
+        {this.renderSpace(role)}
+        {this.renderProject(role)}
+        {this.renderRole(role)}
+      </Container>
+    )
+  }
+
+  renderSpace(role) {
+    const { visible, enabled, error, value } = { ...role.space }
+
+    if (!visible) {
+      return null
+    }
+
+    const { mode, classes } = this.props
+    return (
+      <div className={classes.field}>
+        <TextField
+          reference={this.references.space}
+          label='Space'
+          name='space'
+          mandatory={true}
+          error={error}
+          disabled={!enabled}
+          value={value}
+          mode={mode}
+          onChange={this.handleChange}
+          onFocus={this.handleFocus}
+          onBlur={this.handleBlur}
+        />
+      </div>
+    )
+  }
+
+  renderProject(role) {
+    const { visible, enabled, error, value } = { ...role.project }
+
+    if (!visible) {
+      return null
+    }
+
+    const { mode, classes } = this.props
+    return (
+      <div className={classes.field}>
+        <TextField
+          reference={this.references.project}
+          label='Project'
+          name='project'
+          mandatory={true}
+          error={error}
+          disabled={!enabled}
+          value={value}
+          mode={mode}
+          onChange={this.handleChange}
+          onFocus={this.handleFocus}
+          onBlur={this.handleBlur}
+        />
+      </div>
+    )
+  }
+
+  renderRole(role) {
+    const { visible, enabled, error, value } = { ...role.role }
+
+    if (!visible) {
+      return null
+    }
+
+    const { mode, classes } = this.props
+    return (
+      <div className={classes.field}>
+        <TextField
+          reference={this.references.role}
+          label='Role'
+          name='role'
+          mandatory={true}
+          error={error}
+          disabled={!enabled}
+          value={value}
+          mode={mode}
+          onChange={this.handleChange}
+          onFocus={this.handleFocus}
+          onBlur={this.handleBlur}
+        />
+      </div>
+    )
+  }
+
+  getRole(props) {
+    let { roles, selection } = props
+
+    if (selection && selection.type === 'role') {
+      let [role] = roles.filter(role => role.id === selection.params.id)
+      return role
+    } else {
+      return null
+    }
+  }
+}
+
+export default withStyles(styles)(UserFormParametersRole)
-- 
GitLab