huridocs/uwazi

View on GitHub
app/react/Templates/components/FormConfigRelationship.js

Summary

Maintainability
A
3 hrs
Test Coverage
A
93%
import { Field, actions as formActions } from 'react-redux-form';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import Immutable from 'immutable';
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import { createSelector } from 'reselect';

import { Select } from 'app/ReactReduxForms';
import { Translate, t } from 'app/I18N';
import { Icon } from 'app/UI';
import PropertyConfigOptions from './PropertyConfigOptions';
import Tip from '../../Layout/Tip';

export class FormConfigRelationship extends Component {
  static contentValidation() {
    return { required: val => val && val.trim() !== '' };
  }

  constructor(props) {
    super(props);
    this.state = { inherit: props.showInheritSelect };
    this.onInheritChange = this.onInheritChange.bind(this);
    this.onInheritTypeChange = this.onInheritTypeChange.bind(this);
  }

  onInheritChange() {
    const { index } = this.props;
    if (this.state.inherit) {
      this.props.resetFormValue(`template.data.properties[${index}].inherit.property`);
    }
    this.props.resetFormValue(`template.data.properties[${index}].filter`);
    this.setState(prevState => ({ inherit: !prevState.inherit }));
  }

  onInheritTypeChange() {
    const { index } = this.props;
    this.props.resetFormValue(`template.data.properties[${index}].filter`);
  }

  render() {
    const {
      index,
      type,
      labelError,
      relationTypeError,
      templates,
      relationTypes,
      templateId,
      showInheritOption,
      templateProperties,
      inheritSelectPropertyType,
    } = this.props;

    const options = templates.toJS().filter(template => template._id !== templateId);

    const labelClass = labelError ? 'form-group has-error' : 'form-group';
    const canBeFilter =
      !this.state.inherit ||
      (this.state.inherit &&
        !['image', 'geolocation', 'preview', 'media', 'link'].includes(inheritSelectPropertyType));
    return (
      <div>
        <div className={labelClass}>
          <label htmlFor="label">
            <Translate>Label</Translate>
          </label>
          <Field model={`template.data.properties[${index}].label`}>
            <input id="label" className="form-control" />
          </Field>
        </div>
        <div className={relationTypeError ? 'form-group has-error' : 'form-group'}>
          <label>
            <Translate>Relationship</Translate>
            <span className="required">*</span>
          </label>
          <Select
            model={`template.data.properties[${index}].relationType`}
            options={relationTypes.toJS()}
            optionsLabel="name"
            validators={FormConfigRelationship.contentValidation()}
            optionsValue="_id"
          />
        </div>
        <div className="form-group">
          <label>
            <Translate>Entities</Translate>
          </label>
          <Select
            model={`template.data.properties[${index}].content`}
            options={options}
            optionsLabel="name"
            placeholder={t('System', 'Any entity or document', null, false)}
            optionsValue="_id"
          />
        </div>
        {showInheritOption && (
          <div>
            <label className="property-label" htmlFor={`inherit${index}`}>
              <input
                id={`inherit${index}`}
                type="checkbox"
                checked={this.state.inherit}
                onChange={this.onInheritChange}
              />{' '}
              <Translate>Inherit property</Translate>
            </label>
            <Tip>
              <Translate>
                This property will be inherited from the related entities and shown as metadata of
                this type of entities.
              </Translate>
            </Tip>
          </div>
        )}
        {this.state.inherit && (
          <div className="form-group">
            <Select
              model={`template.data.properties[${index}].inherit.property`}
              options={templateProperties}
              onChange={this.onInheritTypeChange}
              optionsLabel="label"
              optionsValue="_id"
            />
          </div>
        )}
        {this.state.inherit && inheritSelectPropertyType === 'geolocation' && (
          <div className="geolocation-grouping-alert">
            <Icon icon="info-circle" />
            <p>
              <Translate>Adjacent geolocation properties will render on the same map</Translate>.
            </p>
          </div>
        )}
        <PropertyConfigOptions canBeFilter={canBeFilter} index={index} type={type} />
      </div>
    );
  }
}

FormConfigRelationship.defaultProps = {
  labelError: false,
  relationTypeError: false,
  showInheritOption: false,
  showInheritSelect: false,
  templateId: null,
  inheritSelectPropertyType: null,
};

FormConfigRelationship.propTypes = {
  templates: PropTypes.instanceOf(Immutable.List).isRequired,
  relationTypes: PropTypes.instanceOf(Immutable.List).isRequired,
  templateProperties: PropTypes.array.isRequired,
  index: PropTypes.number.isRequired,
  type: PropTypes.string.isRequired,
  labelError: PropTypes.bool,
  relationTypeError: PropTypes.bool,
  showInheritOption: PropTypes.bool,
  showInheritSelect: PropTypes.bool,
  templateId: PropTypes.string,
  inheritSelectPropertyType: PropTypes.string,
  resetFormValue: PropTypes.func.isRequired,
};

const getTemplateProperties = createSelector(
  state => state.templates,
  (state, props) =>
    state.template.formState.properties[props.index].content
      ? state.template.formState.properties[props.index].content.value
      : null,
  (templates, content) => {
    const targetTemplate = templates.find(template => template.get('_id') === content);
    return targetTemplate ? targetTemplate.get('properties').toJS() : [];
  }
);

const getInheritSelectPropertyType = createSelector(
  getTemplateProperties,
  (state, props) => state.template.data.properties[props.index].inherit?.property,
  (templateProperties, inheritedPropertyId) => {
    const inheritedProperty = templateProperties.find(p => p._id === inheritedPropertyId);
    return inheritedProperty && inheritedProperty.type;
  }
);

export function mapStateToProps(state, props) {
  const { template, templates, relationTypes } = state;

  const templateProperties = getTemplateProperties(state, props);

  return {
    labelError:
      template.formState.$form.errors[`properties.${props.index}.label.required`] ||
      template.formState.$form.errors[`properties.${props.index}.label.duplicated`],

    relationTypeError:
      template.formState.$form.errors[`properties.${props.index}.relationType.required`] &&
      template.formState.$form.submitFailed,

    showInheritOption: Boolean(
      template.formState.properties[props.index].content && templateProperties.length
    ),

    showInheritSelect: Boolean(
      template.formState.properties[props.index].inherit?.property?.value &&
        templateProperties.length
    ),
    inheritSelectPropertyType: getInheritSelectPropertyType(state, props),
    templateProperties,
    templateId: template.data._id,
    templates,
    relationTypes,
  };
}

function mapDispatchToProps(dispatch) {
  return bindActionCreators(
    {
      resetFormValue: model => formActions.reset(model),
    },
    dispatch
  );
}

export default connect(mapStateToProps, mapDispatchToProps)(FormConfigRelationship);