renderUsers()

in addons/addon-base-ui/packages/base-ui/src/parts/users/UsersList.js [126:325]


  renderUsers() {
    // Read "this.mapOfUsersBeingEdited" in the "render" method here
    // The usersBeingEditedMap is then used in the ReactTable
    // If we directly use this.mapOfUsersBeingEdited in the ReactTable's cell method, MobX does not
    // realize that it is being used in the outer component's "render" method's scope
    // Due to this, MobX does not re-render the component when observable state changes.
    // To make this work correctly, we need to access "this.mapOfUsersBeingEdited" out side of ReactTable once
    const usersBeingEditedMap = this.mapOfUsersBeingEdited;

    const store = this.getStore();
    const nonRootUsers = store.nonRootUsers;
    // const nonRootUsers = store.list;
    const pageSize = Math.min(nonRootUsers.length, 50);
    const showPagination = nonRootUsers.length > pageSize;

    const displayEditableInput = attributeName => row => {
      const user = row.original;
      const userBeingEdited = usersBeingEditedMap[user.id];
      const handleChange = action(event => {
        event.preventDefault();
        userBeingEdited[attributeName] = event.target.value;
      });
      return userBeingEdited ? (
        <div className="ui focus input">
          <input type="text" defaultValue={row.value} onChange={handleChange} />
        </div>
      ) : (
        user[attributeName]
      );
    };

    const handleCheckboxChange = (userBeingEdited, attributeName) =>
      action((event, { checked }) => {
        userBeingEdited[attributeName] = checked;
        // update this.mapOfUsersBeingEdited reference to force re-render
        this.mapOfUsersBeingEdited = _.clone(this.mapOfUsersBeingEdited);
        event.stopPropagation();
      });
    const handleRadioChange = (userBeingEdited, attributeName) =>
      action((event, { value }) => {
        userBeingEdited[attributeName] = value;
        // update this.mapOfUsersBeingEdited reference to force re-render
        this.mapOfUsersBeingEdited = _.clone(this.mapOfUsersBeingEdited);
        event.stopPropagation();
      });

    const booleanColumnValueFilter = (trueString = 'yes', falseString = 'no') => (filter, row) => {
      const columnValueBoolean = row[filter.id];
      const columnValueStr = columnValueBoolean ? trueString : falseString;
      const filterValue = filter.value.toLowerCase();
      // Allow filtering by typing "yes/no" or "true/false"
      return (
        columnValueStr.indexOf(filterValue) === 0 ||
        String(columnValueBoolean)
          .toLowerCase()
          .indexOf(filterValue) === 0
      );
    };

    const processing = this.formProcessing;

    if (!store.hasNonRootUsers) {
      return null;
    }

    return (
      // TODO: add api token stats and active flag here in the table
      <Segment basic className="p0">
        <Dimmer active={processing} inverted>
          <Loader inverted>Updating</Loader>
        </Dimmer>
        <ReactTable
          data={nonRootUsers}
          defaultSorted={[{ id: 'lastName', desc: true }]}
          showPagination={showPagination}
          defaultPageSize={pageSize}
          className="-striped -highlight"
          filterable
          defaultFilterMethod={(filter, row) => {
            const columnValue = String(row[filter.id]).toLowerCase();
            const filterValue = filter.value.toLowerCase();
            return columnValue.indexOf(filterValue) >= 0;
          }}
          columns={[
            {
              Header: 'User Name',
              accessor: 'username',
            },
            {
              Header: 'Email',
              accessor: 'email',
              Cell: displayEditableInput('email'),
            },
            {
              Header: 'First Name',
              accessor: 'firstName',
              Cell: displayEditableInput('firstName'),
            },
            {
              Header: 'Last Name',
              accessor: 'lastName',
              Cell: displayEditableInput('lastName'),
            },
            {
              Header: 'Admin',
              accessor: 'isAdmin',
              filterMethod: booleanColumnValueFilter(),
              Cell: row => {
                const user = row.original;
                const userBeingEdited = usersBeingEditedMap[user.id];
                return userBeingEdited ? (
                  <span>
                    <Checkbox
                      checked={userBeingEdited.isAdmin}
                      label={userBeingEdited.isAdmin ? 'Yes' : 'No'}
                      onChange={handleCheckboxChange(userBeingEdited, 'isAdmin')}
                    />
                  </span>
                ) : user.isAdmin ? (
                  <span>
                    <i className="check circle outline icon green" />
                    Yes
                  </span>
                ) : (
                  <span>No</span>
                );
              },
            },
            {
              Header: 'Status',
              accessor: 'isActive',
              filterMethod: booleanColumnValueFilter('active', 'inactive'),
              minWidth: 125,
              Cell: row => {
                const user = row.original;
                const userBeingEdited = usersBeingEditedMap[user.id];
                const isActive = userBeingEdited ? userBeingEdited.status.toLowerCase() === 'active' : row.value;
                return userBeingEdited ? (
                  <span>
                    <Radio
                      name={`status-${user.id}`}
                      checked={isActive}
                      value="active"
                      label="Active"
                      onChange={handleRadioChange(userBeingEdited, 'status')}
                    />
                    <Radio
                      className="ml1"
                      name={`status-${user.id}`}
                      checked={!isActive}
                      value="inactive"
                      label="Inactive"
                      onChange={handleRadioChange(userBeingEdited, 'status')}
                    />
                  </span>
                ) : user.isActive ? (
                  <span>
                    <Label color="green">
                      <i className="check circle outline icon" />
                      Active
                    </Label>
                  </span>
                ) : (
                  <span>
                    <Label color="red">Inactive</Label>
                  </span>
                );
              },
            },
            {
              Header: '',
              filterable: false,
              Cell: cell => {
                const user = cell.original;
                const userBeingEdited = usersBeingEditedMap[user.id];
                return userBeingEdited ? (
                  <span>
                    <Icon
                      name="checkmark"
                      className="ml1 cursor-pointer"
                      color="green"
                      onClick={this.handleSave(userBeingEdited)}
                    />
                    <Icon
                      name="close"
                      className="ml1 cursor-pointer"
                      color="red"
                      onClick={this.handleCancel(userBeingEdited)}
                    />
                  </span>
                ) : (
                  <Icon name="pencil" className="ml1 cursor-pointer" color="blue" onClick={this.handleEditorOn(user)} />
                );
              },
            },
          ]}
        />
      </Segment>
    );
  }