Source: components/userComponents/forms/AddMonitoredAreaForm.js

import React from "react";
import {
  createMonitoredArea, deleteMonitoredArea,
  fetchMonitoredArea, updateMonitoredArea,
} from '../../restmodules/MonitoredAreaRestModule';
import { fetchDeviceList, fetchUndeployedDeviceList } from '../../restmodules/DeviceRestModule';
import {
  ValidateLatitude, ValidateLongitude, ValidateName,
  ValidateParameters
} from '../../validationModules/MonitoredAreaFormValidationModule';
import { Redirect } from 'react-router-dom';
import MonitoredAreaMap from '../maps/MonitoredAreaMap';
import '../css/MonitoredAreaAnnotation.css';
import { Checkbox} from 'react-ui-icheck';
import { withNamespaces } from 'react-i18next';
import i18n from '../../../i18n';

/**
 * Form for creating new Monitored Area
 * @extends Component
 * @hideconstructor
 */
class AddMonitoredAreaForm extends React.Component {

  /**
   * @constructs
   * @param props
   */
  constructor(props) {
    super(props);
    this.state = {
      isValid : {
        name : false,
        deviceLatitude : false,
        deviceLongitude : false,
        device : false
      },
      monitoredArea : {
        "id":"",
        "device":
          {
            "id":""
          },
        "deviceLatitude":null,
        "deviceLongitude":null,
        "name":"",
        "active":true,
        "detectionParameters":
          {
            "id":"",
            "backgroundSubstractionHistory":"1",
            "detectorMinContourWidth":"5",
            "detectorMinContourHeight":"5",
            "counterPathSize":"1",
            "counterMaxDistance":"1"
          }
      },
      devices : [],
      toMonitoredAreaConfiguration: false,
      monitoredAreaFetched : false,
      devicesFetched : false
    };

    this.handleSubmitCreate = this.handleSubmitCreate.bind(this);
    this.handleSubmitUpdate = this.handleSubmitUpdate.bind(this);
    this.handleInputChange = this.handleInputChange.bind(this);
    this.handleParametersInputChange = this.handleParametersInputChange.bind(this);
    this.handleDeviceInputChange = this.handleDeviceInputChange.bind(this);
    this.saveMapLocation = this.saveMapLocation.bind(this);
    this.handleDelete = this.handleDelete.bind(this);

    if (this.props.match.params.id) {
      for (let key in this.state.isValid) {
        if (this.state.isValid.hasOwnProperty(key)) {
          this.state.isValid[key] = true;
        }
      }
    }
  }

  /**
   * Close form.
   */
  closeScreen = () => {
    this.setState({
      toMonitoredAreaConfiguration: true
    });
  };

  /**
   * Set Map Location
   * @param lat
   * @param lng
   */
  saveMapLocation(lat, lng){
    let isValid = this.state.isValid;
    let data = this.state.monitoredArea;

    data.deviceLongitude = lng;
    data.deviceLatitude = lat;

    isValid.deviceLatitude = ValidateLatitude(data.deviceLatitude);
    isValid.deviceLongitude = ValidateLongitude(data.deviceLongitude);

    this.setState({
      monitoredArea : data,
      isValid : isValid
    })
  }

  /**
   * Toggle monitoredArea active/archived
   */
  toggleActive = () => {
    let data = this.state.monitoredArea;
    data.active = !this.state.monitoredArea.active;

    this.setState({
      monitoredArea: data
    })
  };

  /**
   *
   */
  handleDelete() {
    deleteMonitoredArea(this.state.monitoredArea.id)
    .then(response => {
      if (response.status === 204) {
        this.closeScreen();
      }
    })
    .catch(error => console.log(error));
  }

  /**
   *
   * @param event
   */
  handleSubmitCreate(event) {
    let data = this.state.monitoredArea;

    // if (this.state.devices.length > 0 && !this.state.monitoredArea.device.id){
    //   data.device.id = this.state.devices[0].id;
    // }

    createMonitoredArea(data)
    .then(response => {
      // this.props.updateTableData();
      // this.props.hideMonitoredAreaForm();
      this.closeScreen();
    });
    event.preventDefault();
  }

  /**
   *
   * @param event
   */
  handleSubmitUpdate(event) {
    updateMonitoredArea(this.state.monitoredArea)
    .then(response => {
      this.closeScreen();
    });
    event.preventDefault();
  }

  /**
   * Handles name, deviceLatitude, deviceLongitude change.
   * @param event
   */
  handleInputChange(event) {
    const target = event.target;
    const value = target.value;
    const name = target.name;

    let data = this.state.monitoredArea;
    data[name] = value;

    let isValid = this.state.isValid;
    let result;

    switch(name) {
      case "name":
        result = ValidateName(value);
        break;
      case "deviceLatitude":
        result = ValidateLatitude(value);
        break;
      case "deviceLongitude":
        result = ValidateLongitude(value);
        this.longitudeDisplayValue = parseFloat(value).toFixed(3);
        break;

    }

    isValid[name] = result;

    this.setState({
      monitoredArea : data,
      isValid : isValid
    });
  }

  /**
   * Handles device input change.
   * @param event
   */
  handleDeviceInputChange(event) {
    let data = this.state.monitoredArea;
    let isValid = this.state.isValid;
    data.device.id = event.target.value;

    for (let i = 0; i < this.state.devices.length; i++){
      if (this.state.devices[i].id === data.device.id){
        data.device.name = this.state.devices[i].name;
        break;
      }
    }

      isValid.device = this.isDeviceValid(data.device);

    this.setState({
      monitoredArea : data,
      isValid : isValid
    });
  }

  /**
   * Check if device selection is valid
   */
  checkDeviceValidity(){
    let isValid = this.state.isValid;

    isValid.device = this.isDeviceValid(this.state.monitoredArea.device);

    this.setState({
      isValid : isValid
    });
  }

  /**
   * Check if device object is valid
   * @param device
   * @returns {boolean}
   */
  isDeviceValid(device){
    return device !== null && device !== undefined && device.id !== undefined && device.id !== null && device.id !== ""
  }

  /**
   * Handle parameters input change
   * @param event
   */
  handleParametersInputChange(event) {
    const target = event.target;
    const value = target.value;
    const name = target.name;

    let data = this.state.monitoredArea;
    data.detectionParameters[name] = value;

    let isValid = this.state.isValid;
    let result;

    switch(name) {
      case "backgroundSubstractionHistory":
        result = ValidateParameters(value, 1, 1000);
        break;
      case "detectorMinContourHeight":
        result = ValidateParameters(value, 1,50);
        break;
      case "detectorMinContourWidth":
        result = ValidateParameters(value, 1,50);
        break;
      case "counterPathSize":
        result = ValidateParameters(value, 1,1000);
        break;
      case "counterMaxDistance":
        result = ValidateParameters(value, 1,100);
        break;
      default:
    }

    isValid[name] = result;

    this.setState({
      monitoredArea : data,
      isValid : isValid
    }, this.checkDeviceValidity);
  }

  /**
   *
   */
  componentDidMount() {
    const monitoredAreaID = this.props.match.params.id;
    if (monitoredAreaID) {
      fetchMonitoredArea(monitoredAreaID)
      .then(data => this.setState({ monitoredArea: data }, () => this.setState({ monitoredAreaFetched : true})))
      .catch(error => this.setState({ monitoredArea: [{ name: error.toString() }] }));
    }
    fetchUndeployedDeviceList().then(data => {
      data.push({
            "id": "",
            "name": i18n.t('forms.noCameraSelected')
      });
      this.setState({ devices: data }, () => this.setState({ devicesFetched: true }))
    })
    .then()
    .catch(error => this.setState({ devices: [{ name: error.toString() }] }));
  }

  /**
   *
   * @param prevProps
   * @param prevState
   * @param snapshot
   */
  componentDidUpdate(prevProps, prevState, snapshot) {

    if (this.state.monitoredAreaFetched && this.state.devicesFetched){
      let devices = this.state.devices;
      let contains = false;
      for (let i=0; i<devices.length; i++){
        if (devices[i].id === this.state.monitoredArea.device.id) {
          contains = true;
          break;
        }
      }
      if (contains === false) {
        devices.push({
          "id" : this.state.monitoredArea.device.id,
          "name" : this.state.monitoredArea.device.name
        });
      }
      this.setState({
        devices: devices,
        monitoredAreaFetched : false
      });
    }
  }

  /**
   *
   * @returns {*}
   */
  render() {
    const { t } = this.props;
    if (this.state.toMonitoredAreaConfiguration === true) {
      return <Redirect to="/config"/>
    }

    let submitEnabled = true;
    const submitHandle = this.props.match.params.id ? this.handleSubmitUpdate : this.handleSubmitCreate;

    for (let key in this.state.isValid) {
      if (this.state.isValid.hasOwnProperty(key) && !this.state.isValid[key]) {
        submitEnabled = false;
        break;
      }
    }

    const title = this.props.match.params.id ? t('forms.update') + ' ' + this.state.monitoredArea.name : t('forms.createNewMonitoredArea');
    const bigTitle = this.props.match.params.id ? <h1>
                                                    {t('forms.updatingMonitoredArea')}
                                                    <small> {t('forms.editMonitoredArea')}</small>
                                                  </h1> :
                                                  <h1>
                                                    {t('forms.additionOfNewMonitoredArea')}
                                                    <small> {t('forms.createNewMonitoredArea')}</small>
                                                  </h1>
    const link = this.props.match.params.id ?
                      <li><a href={"/config/edit/" + this.state.monitoredArea.id}>{t('forms.updateMonitoredArea')}</a></li> :
                      <li><a href={"/config/new"}>{t('forms.createMonitoredArea')}</a></li>
    return (
        <section className="content-header">

          {bigTitle}

          <ol className="breadcrumb">
            <li><a href="/config"><i className="fa fa-gear"></i>{t('sidebar.adminConfiguration')}</a></li>
            {link}
          </ol>

          <br/>

          <div className="row">
            <div className="col-lg-12">
              <div className="box">

                {this.renderConfirmDeleteModal(t('forms.deleteMonitoredArea') + "?")}

          <div className="box-header with-border">
            <h3 className="box-title">{title}</h3>
          </div>

          <div className="box-header with-border">

            <div className=" col-xs-1">
              <label>{t('forms.active')}</label>
              <label className="control-label fa fa-info-circle info-icon"
                     data-toggle="tooltip"
                     title={t('forms.tooltip.active')}/>
            </div>
                <div className="col-xs-1">
                  <Checkbox
                    checkboxClass="icheckbox_square-blue"
                    checked={this.state.monitoredArea.active}
                    label=" "
                    onChange={() => {this.toggleActive()}}
                  />
            </div>
          </div>
          <div className="box-body">
            <div className={this.state.isValid.device ? "form-group" : "form-group has-error"}>
              <label>{t('forms.device')}</label>
              <label className="control-label fa fa-info-circle info-icon"
                     data-toggle="tooltip"
                     title={t('forms.tooltip.device')}/>
              <select className="form-control" name="id" value={this.state.monitoredArea.device.id} onChange={this.handleDeviceInputChange}>
                {this.state.devices.map((device, i) =>
                  <option
                    value={device.id}
                    label={device.name}
                    key={i}
                  >
                    {device.name}
                  </option>
                )}
              </select>
              {this.state.isValid.device ? null : <span className="help-block">{t('forms.help.invalidDeviceSelected')}</span>}
            </div>
          </div>

          <div className="box-body">
            <form role="form"
                  onSubmit={submitHandle}>
                <div className={this.state.isValid.name ? "form-group" : "form-group has-error"}>
                  <label>{t('forms.monitoredAreaName')}</label>
                  <label className="control-label fa fa-info-circle info-icon"
                         data-toggle="tooltip"
                         title={t('forms.tooltip.monitoredAreaName')}/>
                  <input
                    type="text"
                    className="form-control"
                    placeholder={t("forms.name")}
                    name="name"
                    value={this.state.monitoredArea.name}
                    onChange={this.handleInputChange}
                  />
                  {this.state.isValid.name ? null : <span className="help-block">{t("forms.help.nameCannotBeEmpty")}</span>}
                </div>

                <div>
                  <div className="row-xs-flex-center" style={{'margin-right': 'inherit'}}>
                    <div className="col-sm-2">
                      <label>{t("forms.coordinates")}</label>
                      <label className="control-label fa fa-info-circle info-icon"
                             data-toggle="tooltip"
                             title={t("forms.tooltip.coordinates")}/>
                      <div className={this.state.isValid.deviceLatitude ? "form-group" : "form-group has-error"}>
                        <input
                          type="text"
                          className="form-control"
                          placeholder={t('forms.latitude')}
                          name="deviceLatitude"
                          value={this.state.monitoredArea.deviceLatitude}
                          onChange={this.handleInputChange}
                        />
                        {this.state.isValid.deviceLatitude ? null : <span className="help-block">{t('forms.help.latitude')}</span>}
                      </div>
                      <div className={this.state.isValid.deviceLongitude ? "form-group" : "form-group has-error"}>
                        <input
                          type="text"
                          className="form-control"
                          placeholder={t('forms.longitude')}
                          name="deviceLongitude"
                          value={this.state.monitoredArea.deviceLongitude}
                          onChange={this.handleInputChange}
                        />
                        {this.state.isValid.deviceLongitude ? null : <span className="help-block">{t('forms.help.longitude')}</span>}
                      </div>
                    </div>
                    <div className="col-sm-10">
                      <MonitoredAreaMap
                        interactive={true}
                        saveChanges={(lat, lng) => this.saveMapLocation(lat, lng)}
                        deviceLatitude={this.state.monitoredArea.deviceLatitude}
                        deviceLongitude={this.state.monitoredArea.deviceLongitude}
                      />
                    </div>
                  </div>
                </div>
                {this.renderSubmitButton(!submitEnabled)}
            </form>
          </div>
              </div>
            </div>
        </div>
      </section>
    )
  }

  renderSubmitButton (disabled){
    const { t } = this.props;
    if (this.props.match.params.id) {
      return (
        <div className="box-footer no-border">
          <div className="pull-left">
            <a
              className="btn-link-red"
              data-toggle="modal"
              data-target="#confirmDeleteModal"
            >{t("forms.deleteMonitoredArea")}</a>
          </div>

          <div className="pull-right">
            <button className="btn btn-outline-primary"
                    onClick={this.closeScreen}
            >{t("forms.cancel")}</button>
            <button type="submit"
                    className="btn btn-primary"
                    disabled={disabled}
            >{t("forms.update")}</button>
          </div>
        </div>
      )
    }else {
      return (
        <div className="box-footer no-border">
          <div className="pull-right">
            <button className="btn btn-default"
                    onClick={this.closeScreen}
            >{t("forms.cancel")}</button>
            <button type="submit"
                    className="btn btn-primary"
                    disabled={disabled}
            >{t("forms.create")}</button>
          </div>
        </div>
      )
    }
  }

  renderConfirmDeleteModal(text) {
    const { t } = this.props;
    return (
      <div className="modal fade"
           id="confirmDeleteModal"
           tabIndex="-1"
           role="dialog"
           aria-labelledby="myModalLabel">
        <div className="modal-dialog" role="document">
          <div className="modal-content">
            <div className="modal-header">
              <button type="button" className="close" data-dismiss="modal" aria-label="Close">
                <span aria-hidden="true">&times;</span></button>
              <h4 className="modal-title" id="myModalLabel">{t("forms.deleteMonitoredArea")}?</h4>
            </div>
            <div className="modal-body">
              {text}
            </div>
            <div className="modal-footer">
              <button type="button"
                      className="btn btn-default"
                      data-dismiss="modal">
                {t("forms.cancel")}
              </button>
              <button type="button"
                      className="btn btn-primary"
                      data-dismiss="modal"
                      onClick={this.handleDelete}
              >{t("forms.delete")}
              </button>
            </div>
          </div>
        </div>
      </div>
    )
  }
}

export default withNamespaces()(AddMonitoredAreaForm);