components/MenuItems/Admin/RoleModal.js

/* eslint-disable react/no-array-index-key */
import React, { Component } from 'react';
import SVGComponent from '../../SVG/SVGComponent';
import { SVG_ICONS } from '../../../constants';
import validateFields from '../../../utils/validation';
/**
 * @module MI-Admin/RoleModal
 */
/**
 * @typedef {object} props
 * @property {Array} permissions
 * @property {Array} rolePermissions
 * @property {number} selectedRoleId
 * @property {object} role
 * @property {boolean} isEditingRole
 * @property {boolean} isAddNewRole
 * @property {Function} saveNewRole
 * @property {Function} saveEditRole
 * @property {Function} getRolePermissions
 * @property {Function} closeRoleModal
 */
export default class RoleModal extends Component {
  constructor(props) {
    super(props);

    /**
     * @member {object}
     * @property {string} name
     * @property {string} description
     * @property {Array} assignedPermissions
     * @property {Array} availablePermissions
     * @property {number} selectedAssignedIndex
     * @property {number} selectedAvailableIndex
     * @property {string} assignedFilterText
     * @property {string} availableFilterText
     * @property {object} errorMgs
     */
    this.state = {
      name: props.role.name,
      description: props.role.description,
      assignedPermissions: [],
      availablePermissions: props.permissions,
      selectedAssignedIndex: 0,
      selectedAvailableIndex: 0,
      assignedFilterText: '',
      availableFilterText: '',
      errorMgs: {},
    };

    // COMPONENT WILL MOUNT
    // Get permissions for selected role
    if (props.isEditingRole) {
      props.getRolePermissions({ roleId: props.role.roleId });
    }
  }

  /**
   * Checks if role permissions is fetch when editing
   *
   * @function
   * @param {object} prevProps Previous props
   * @returns {void}
   */
  componentDidUpdate(prevProps) {
    if (
      this.props.isEditingRole &&
      prevProps.selectedRoleId === null &&
      this.props.selectedRoleId === this.props.role.roleId
    ) {
      this.setState({
        assignedPermissions: this.props.rolePermissions,
        availablePermissions: this.permissionDifference(this.props.permissions, this.props.rolePermissions),
      });
    }
  }

  /**
   * Calculate difference between all permissions and assigned
   *
   * @function
   * @param {Array} all All perrmisions
   * @param {Array} assigned perrmissions
   * @returns {Array} Difference of all and assigned permissions
   */
  permissionDifference = (all, assigned) => all.filter((permission) => !this.inArray(assigned, permission));

  /**
   *  Check if permission is in list
   *
   *  @function
   *  @param {list} permissionList list of permissions
   *  @param {object} permissison permissison object
   *  @returns {boolean}
   */
  inArray = (permissionList, permissison) =>
    permissionList.findIndex((assigned) => assigned.permissionId === permissison.permissionId) !== -1;

  /**
   *  Move permission to assigned or to available
   *
   * @function
   * @returns {void}
   */
  addPermissionToAssigned = () => {
    const permission = this.getAvailablePermissions()[this.state.selectedAvailableIndex];
    if (permission) {
      const newAssignedPermissions = [...this.state.assignedPermissions];

      newAssignedPermissions.push(permission);
      newAssignedPermissions.sort((p1, p2) => (p1.name > p2.name ? 1 : 0));

      const newAvailablePermissions = this.permissionDifference(this.props.permissions, newAssignedPermissions);
      let selectedAvailableIndex = this.state.selectedAvailableIndex;
      if (selectedAvailableIndex && !this.getAvailablePermissions(newAvailablePermissions)[selectedAvailableIndex]) {
        selectedAvailableIndex -= 1;
      }
      this.setState({
        assignedPermissions: newAssignedPermissions,
        availablePermissions: newAvailablePermissions,
        selectedAvailableIndex,
      });
    }
  };

  /**
   *  Remove permission from assigned
   *
   * @function
   * @returns {void}
   */
  removeFromAssingned = () => {
    const permission = this.getAssignedPermissions()[this.state.selectedAssignedIndex];
    if (permission) {
      let newAssignedPermissions = [...this.state.assignedPermissions];

      newAssignedPermissions = newAssignedPermissions.filter((p) => p.permissionId !== permission.permissionId);

      const newAvailablePermissions = this.permissionDifference(this.props.permissions, newAssignedPermissions);
      let selectedAssignedIndex = this.state.selectedAssignedIndex;
      if (selectedAssignedIndex && !this.getAssignedPermissions(newAssignedPermissions)[selectedAssignedIndex]) {
        selectedAssignedIndex -= 1;
      }
      this.setState({
        assignedPermissions: newAssignedPermissions,
        availablePermissions: newAvailablePermissions,
        selectedAssignedIndex,
      });
    }
  };

  /**
   * Handle form change
   *
   * @param {string} key
   * @param {Event} e
   * @function
   * @returns {void}
   */
  handleFormChange = (key, e) => {
    const value = e.target.value;
    const newStateObj = {
      selectedAssignedIndex: 0,
      selectedAvailableIndex: 0,
    };
    newStateObj[key] = value;

    this.setState(newStateObj);
  };

  /**
   * Save changes
   *
   * @function
   * @returns {void}
   */
  saveProcess = () => {
    if (this.props.isAddNewRole === true) {
      this.props.saveNewRole({
        name: this.state.name,
        description: this.state.description,
        permissionIds: this.state.assignedPermissions.map((item) => item.permissionId),
      });
    } else {
      this.props.saveEditRole({
        roleId: this.props.role.roleId,
        name: this.state.name,
        description: this.state.description,
        permissionIds: this.state.assignedPermissions.map((item) => item.permissionId),
      });
    }
    this.closeDialog();
  };

  /**
   * Validate fields before save
   *
   * @function
   * @returns {void}
   */
  validateFields = () => {
    const validationParams = [
      {
        label: 'Name',
        fieldName: 'name',
        value: this.state.name,
        rules: ['mandatory'],
      },
      {
        label: 'Description',
        fieldName: 'description',
        value: this.state.description,
        rules: ['mandatory'],
      },
      {
        label: 'Assigned permission',
        fieldName: 'permissionIds',
        value: this.state.assignedPermissions.length,
        rules: ['mandatory'],
      },
    ];
    validateFields(validationParams)
      .then(() => {
        this.saveProcess();
      })
      .catch((errorMgs) => {
        this.setState({ errorMgs });
      });
  };

  /**
   * Close dialog
   *
   * @function
   * @returns {void}
   */
  closeDialog = () => {
    this.props.closeRoleModal();
  };

  /**
   * Select row in assigned list
   *
   * @function
   * @param {number} index Permision id
   * @returns {void}
   */
  setAssignedSelected = (index) => {
    this.setState({ selectedAssignedIndex: index });
  };

  /**
   * Select row in available list
   *
   * @function
   * @param {number} index Permision id
   * @returns {void}
   */
  setAvailableSelected = (index) => {
    this.setState({ selectedAvailableIndex: index });
  };

  /**
   * Filter permission
   *
   * @function
   * @param {Array} roles roles
   * @param {string} serchText Search text
   * @returns {void}
   */
  getFilteredPermissions = (roles, serchText) => roles.filter((perm) => this.checkForMatch(perm.name, serchText));

  /**
   * Get assigned permission
   *
   * @function
   * @param {object} permission roles
   * @returns {void}
   */
  getAssignedPermissions = (permission) =>
    this.getFilteredPermissions(permission || this.state.assignedPermissions, this.state.assignedFilterText);

  /**
   * Get available permission
   *
   * @function
   * @param {object} permission roles
   * @returns {void}
   */
  getAvailablePermissions = (permission) =>
    this.getFilteredPermissions(permission || this.state.availablePermissions, this.state.availableFilterText);

  /**
   * Check for string in string (str2 in str1)
   *
   * @function
   * @param {string} str1 First string
   * @param {string} str2 Second string
   * @returns {boolean}
   */
  checkForMatch = (str1, str2) => str1.toLowerCase().indexOf(str2.toLowerCase()) !== -1;

  /**
   * Render
   *
   * @returns {view}
   */
  render() {
    return (
      <div className="dialog dialog--md open">
        <div className="p-20">
          <div className="dialog__heading">
            <span className="dialog-title">{this.props.isEditingRole === true ? 'Edit Role' : 'Add Role'}</span>
            <SVGComponent
              className="icon-xxs dialog__close"
              onClick={this.closeDialog}
              src={`${SVG_ICONS.utility}#close`}
            />
          </div>
        </div>
        <div className="dialog__row align-items-start">
          <div className="dialog__fields mr-20">
            <label className="style-2">Name</label>
            <input
              id="name_roleModal"
              type="text"
              className="form-control"
              placeholder="-"
              value={this.state.name}
              onChange={this.handleFormChange.bind(this, 'name')}
            />
            {this.state.errorMgs.name}
          </div>
          <div className="dialog__fields">
            <label className="style-2">Description</label>
            <input
              id="description_roleModal"
              type="text"
              className="form-control"
              placeholder="-"
              value={this.state.description}
              onChange={this.handleFormChange.bind(this, 'description')}
            />
            {this.state.errorMgs.description}
          </div>
        </div>
        <div className="p-20">
          <div className="dialog__roles">
            <div className="dialog__roles-wrapper">
              <h3>Assigned Permissions</h3>
              <div className="dialog__fields">
                <input
                  id="assignedFilterText_roleModal"
                  type="text"
                  className="form-control"
                  value={this.state.assignedFilterText}
                  onChange={this.handleFormChange.bind(this, 'assignedFilterText')}
                />
                <i className="icon-search" />
              </div>
              {this.state.errorMgs.permissionIds}
              <ul className="dialog__list" id="assign-list">
                {this.getAssignedPermissions().map((permission, index) => (
                  <li
                    key={`asigned-${index}`}
                    className={`dialog__list-item ${this.state.selectedAssignedIndex === index ? 'active' : ''}`}
                    onClick={this.setAssignedSelected.bind(this, index)}
                  >
                    {permission.name}
                  </li>
                ))}
              </ul>
            </div>
            <div className="dialog__switch-wrap">
              <div className="dialog__switch">
                <div className="remove-arrow" onClick={this.removeFromAssingned}>
                  <SVGComponent className="icon-xs" src={`${SVG_ICONS.switchArrowIcons}#f-arrow`} />
                </div>
                <div className="assign-arrow mt-20" onClick={this.addPermissionToAssigned}>
                  <SVGComponent className="icon-xs" src={`${SVG_ICONS.switchArrowIcons}#b-arrow`} />
                </div>
              </div>
            </div>
            <div className="dialog__roles-wrapper">
              <h3>Available Permissions</h3>
              <div className="dialog__fields">
                <input
                  id="availableFilterText_roleModal"
                  type="text"
                  className="form-control"
                  value={this.state.availableFilterText}
                  onChange={this.handleFormChange.bind(this, 'availableFilterText')}
                />
                <i className="icon-search" />
              </div>
              <ul className="dialog__list" id="available-list">
                {this.getAvailablePermissions().map((permission, index) => (
                  <li
                    key={`avail-${index}`}
                    className={`dialog__list-item ${this.state.selectedAvailableIndex === index ? 'active' : ''}`}
                    onClick={this.setAvailableSelected.bind(this, index)}
                  >
                    {permission.name}
                  </li>
                ))}
              </ul>
            </div>
          </div>
        </div>
        <div className="p-20">
          <div className="dialog__button-wrapper">
            <button id="cancelBtn_roleModal" className="btn btn-secondary" onClick={this.closeDialog} type="button">
              Cancel
            </button>
            <button
              id="saveBtn_roleModal"
              className="btn btn-primary ml-10"
              onClick={this.validateFields}
              type="submit"
            >
              Save
            </button>
          </div>
        </div>
      </div>
    );
  }
}