diff options
| author | Zog | 2018-04-25 09:12:26 +0200 | 
|---|---|---|
| committer | Zog | 2018-04-25 09:12:26 +0200 | 
| commit | d0c9be18498c1179a9341f51c5705a736d21ea71 (patch) | |
| tree | bf540e06a33d5be29663a30dd75d62d0e1fedb7e | |
| parent | eb51cf0aa62a96c552c1a32778aa84e64df458a6 (diff) | |
| download | chouette-core-6472-itls-exclusions.tar.bz2 | |
Refs #6472; Add Constraint Zone exclusions to journeys6472-itls-exclusions
along with the React interface
14 files changed, 367 insertions, 12 deletions
| diff --git a/app/controllers/routing_constraint_zones_controller.rb b/app/controllers/routing_constraint_zones_controller.rb index 47df211d0..886247e79 100644 --- a/app/controllers/routing_constraint_zones_controller.rb +++ b/app/controllers/routing_constraint_zones_controller.rb @@ -13,13 +13,16 @@ class RoutingConstraintZonesController < ChouetteController    def index      index! do |format| -      @routing_constraint_zones = RoutingConstraintZoneDecorator.decorate( -        @routing_constraint_zones, -        context: { -          referential: referential, -          line: parent -        } -      ) +      format.html do +        @routing_constraint_zones = RoutingConstraintZoneDecorator.decorate( +          @routing_constraint_zones, +          context: { +            referential: referential, +            line: parent +          } +        ) +      end +      format.json      end    end diff --git a/app/javascript/vehicle_journeys/actions/index.js b/app/javascript/vehicle_journeys/actions/index.js index 70d6e953a..c67f9f0cf 100644 --- a/app/javascript/vehicle_journeys/actions/index.js +++ b/app/javascript/vehicle_journeys/actions/index.js @@ -97,10 +97,30 @@ const actions = {      vehicleJourneys,      timetables    }), +  editVehicleJourneyConstraintZones : (vehicleJourneys, zones) => ({ +    type: 'EDIT_VEHICLEJOURNEYS_CONSTRAINT_ZONES', +    vehicleJourneys, +    zones +  }),    openPurchaseWindowsEditModal : (vehicleJourneys) => ({      type : 'EDIT_PURCHASE_WINDOWS_VEHICLEJOURNEY_MODAL',      vehicleJourneys    }), +  openConstraintExclusionEditModal : (vehicleJourneys) => ({ +    type : 'EDIT_CONSTRAINT_EXCLUSIONS_VEHICLEJOURNEY_MODAL', +    vehicleJourneys +  }), +  selectConstraintZone: (selectedZone) =>({ +    type: 'SELECT_CONSTRAINT_ZONE_MODAL', +    selectedZone: { +      id: selectedZone.id, +      name: selectedZone.text +    } +  }), +  deleteConstraintZone : (constraintZone) => ({ +    type : 'DELETE_CONSTRAINT_ZONE_MODAL', +    constraintZone +  }),    selectPurchaseWindowsModal: (selectedItem) =>({      type: 'SELECT_PURCHASE_WINDOW_MODAL',      selectedItem diff --git a/app/javascript/vehicle_journeys/components/Tools.js b/app/javascript/vehicle_journeys/components/Tools.js index 22ea44283..efae6b2b5 100644 --- a/app/javascript/vehicle_journeys/components/Tools.js +++ b/app/javascript/vehicle_journeys/components/Tools.js @@ -9,6 +9,7 @@ import EditVehicleJourney from '../containers/tools/EditVehicleJourney'  import NotesEditVehicleJourney from '../containers/tools/NotesEditVehicleJourney'  import TimetablesEditVehicleJourney from '../containers/tools/TimetablesEditVehicleJourney'  import PurchaseWindowsEditVehicleJourney from '../containers/tools/PurchaseWindowsEditVehicleJourney' +import ConstraintExclusionEditVehicleJourney from '../containers/tools/ConstraintExclusionEditVehicleJourney'  export default class Tools extends Component { @@ -36,10 +37,12 @@ export default class Tools extends Component {            <DuplicateVehicleJourney disabled={!this.hasPolicy("create") || !this.hasPolicy("update") || !editMode}/>            <ShiftVehicleJourney disabled={!this.hasPolicy("update") || !editMode}/>            <EditVehicleJourney disabled={!this.hasPolicy("update")}/> +            <TimetablesEditVehicleJourney disabled={!this.hasPolicy("update")}/>            { this.hasFeature('purchase_windows') &&              <PurchaseWindowsEditVehicleJourney disabled={!this.hasPolicy("update")}/>            } +          <ConstraintExclusionEditVehicleJourney disabled={!this.hasPolicy("update")}/>            <NotesEditVehicleJourney disabled={!this.hasPolicy("update")}/>            <DeleteVehicleJourneys disabled={!this.hasPolicy("destroy") || !editMode}/>          </ul> diff --git a/app/javascript/vehicle_journeys/components/tools/ConstraintExclusionEditVehicleJourney.js b/app/javascript/vehicle_journeys/components/tools/ConstraintExclusionEditVehicleJourney.js new file mode 100644 index 000000000..bab77926f --- /dev/null +++ b/app/javascript/vehicle_journeys/components/tools/ConstraintExclusionEditVehicleJourney.js @@ -0,0 +1,182 @@ +import _ from 'lodash' +import React, { Component } from 'react' +import PropTypes from 'prop-types' +import actions from '../../actions' +import ConstraintZoneSelect2 from './select2s/ConstraintZoneSelect2' + + +export default class ConstraintExclusionEditVehicleJourney extends Component { +  constructor(props) { +    super(props) +    this.handleSubmit = this.handleSubmit.bind(this) +    this.constraintZoneUrl = this.constraintZoneUrl.bind(this) +    this.excluded_constraint_zones = this.excluded_constraint_zones.bind(this) +    this.constraint_zones = null +  } + +  handleSubmit() { +    this.props.onConstraintZonesEditVehicleJourney(this.props.modal.modalProps.vehicleJourneys, this.props.modal.modalProps.selectedConstraintZones) +    this.props.onModalClose() +    $('#ConstraintExclusionEditVehicleJourney').modal('hide') +  } + +  constraintZoneUrl(contraint_zone) { +    return window.constraint_zones_routes + "/" + contraint_zone.id +  } + +  excluded_constraint_zones() { +    let out = [] +    this.props.modal.modalProps.selectedConstraintZones.map((id, _)=>{ +      this.constraint_zones.map((zone, _)=>{ +        if(zone.id == id){ +          out.push(zone) +        } +      }) +    }) +    return out +  } + +  fetch_constraint_zones() { +    let url = window.constraint_zones_routes + ".json" +    fetch(url, { +      credentials: 'same-origin', +    }).then(response => { +      return response.json() +    }).then((json) => { +      this.constraint_zones = [] +      json.map((item, i)=>{ +        this.constraint_zones.push( +          _.assign({}, item, {text: item.name}) +        ) +      }) +      this.forceUpdate() +    }) +  } + +  render() { +    if(this.constraint_zones === null) { +      this.fetch_constraint_zones() +      return false +    } +    if(this.props.status.fetchSuccess == true) { +      return ( +        <li className='st_action'> +          <button +            type='button' +            disabled={(actions.getSelected(this.props.vehicleJourneys).length < 1 || this.props.disabled)} +            data-toggle='modal' +            data-target='#ConstraintExclusionEditVehicleJourney' +            onClick={() => this.props.onOpenCalendarsEditModal(actions.getSelected(this.props.vehicleJourneys))} +            title={I18n.t('activerecord.attributes.vehicle_journey.constraint_exclusions')} +          > +            <span className='fa fa-ban fa-strong'></span> +          </button> + +          <div className={ 'modal fade ' + ((this.props.modal.type == 'duplicate') ? 'in' : '') } id='ConstraintExclusionEditVehicleJourney'> +            <div className='modal-container'> +              <div className='modal-dialog'> +                <div className='modal-content'> +                  <div className='modal-header'> +                    <h4 className='modal-title'>{I18n.t('activerecord.attributes.vehicle_journey.constraint_exclusions')}</h4> +                    <span type="button" className="close modal-close" data-dismiss="modal">×</span> +                  </div> + +                  {(this.props.modal.type == 'constraint_exclusions_edit') && ( +                    <form> +                      <div className='modal-body'> +                        <div className='row'> +                          <div className='col-lg-12'> +                            <div className='subform'> +                              <div className='nested-head'> +                                <div className='wrapper'> +                                  <div> +                                    <div className='form-group'> +                                      <label className='control-label'>{this.excluded_constraint_zones().length == 0 ? I18n.t('vehicle_journeys.vehicle_journeys_matrix.no_excluded_constraint_zones') : I18n.t('vehicle_journeys.form.excluded_constraint_zones')}</label> +                                    </div> +                                  </div> +                                  <div></div> +                                </div> +                              </div> +                              {this.excluded_constraint_zones().map((contraint_zone, i) => +                                <div className='nested-fields' key={i}> +                                  <div className='wrapper'> +                                    <div> <a href={this.constraintZoneUrl(contraint_zone)} target="_blank"> +                                      {contraint_zone.name} +                                    </a> </div> +                                    { +                                      this.props.editMode && +                                      <div> +                                        <a +                                          href='#' +                                          title='Supprimer' +                                          className='fa fa-trash remove_fields' +                                          style={{ height: 'auto', lineHeight: 'normal' }} +                                          onClick={(e) => { +                                            e.preventDefault() +                                            this.props.onDeleteConstraintZone(contraint_zone) +                                          }} +                                        ></a> +                                      </div> +                                    } +                                  </div> +                                </div> +                              )} +                              { +                                this.props.editMode && +                                <div className='nested-fields'> +                                  <div className='wrapper'> +                                    <div> +                                      <ConstraintZoneSelect2 +                                        data={this.constraint_zones} +                                        values={this.props.modal.modalProps.constraint_exclusions} +                                        placeholder={I18n.t('vehicle_journeys.vehicle_journeys_matrix.filters.constraint_zone')} +                                        onSelectConstraintZone={this.props.onSelectConstraintZone} +                                      /> +                                    </div> +                                  </div> +                                </div> +                              } +                            </div> +                          </div> +                        </div> +                      </div> +                      { +                        this.props.editMode && +                        <div className='modal-footer'> +                          <button +                            className='btn btn-link' +                            data-dismiss='modal' +                            type='button' +                            onClick={this.props.onModalClose} +                          > +                            {I18n.t('cancel')} +                          </button> +                          <button +                            className='btn btn-primary' +                            type='button' +                            onClick={this.handleSubmit} +                          > +                            {I18n.t('actions.submit')} +                          </button> +                        </div> +                      } +                    </form> +                  )} + +                </div> +              </div> +            </div> +          </div> +        </li> +      ) +    } else { +      return false +    } +  } +} + +ConstraintExclusionEditVehicleJourney.propTypes = { +  onOpenCalendarsEditModal: PropTypes.func.isRequired, +  onModalClose: PropTypes.func.isRequired, +  disabled: PropTypes.bool.isRequired +} diff --git a/app/javascript/vehicle_journeys/components/tools/select2s/ConstraintZoneSelect2.js b/app/javascript/vehicle_journeys/components/tools/select2s/ConstraintZoneSelect2.js new file mode 100644 index 000000000..1caaf2401 --- /dev/null +++ b/app/javascript/vehicle_journeys/components/tools/select2s/ConstraintZoneSelect2.js @@ -0,0 +1,37 @@ +import _ from 'lodash' +import React, { Component } from 'react' +import PropTypes from 'prop-types' +import Select2 from 'react-select2-wrapper' +import actions from '../../../actions' + +export default class BSelect4 extends Component { +  constructor(props) { +    super(props) +  } + +  render() { +    return ( +      <Select2 +        data={this.props.data} +        value={this.props.value} +        onSelect={(e) => this.props.onSelectConstraintZone(e) } +        multiple={false} +        ref='constraint_zone_id' +        options={{ +          allowClear: false, +          theme: 'bootstrap', +          width: '100%', +          placeholder: this.props.placeholder, +          language: require('./language'), +          minimumInputLength: 1, +          escapeMarkup: function (markup) { return markup; }, +          templateResult: formatRepo +        }} +      /> +    ) +  } +} + +const formatRepo = (props) => { +  if(props.text) return props.text +} diff --git a/app/javascript/vehicle_journeys/containers/tools/ConstraintExclusionEditVehicleJourney.js b/app/javascript/vehicle_journeys/containers/tools/ConstraintExclusionEditVehicleJourney.js new file mode 100644 index 000000000..76d85dfe6 --- /dev/null +++ b/app/javascript/vehicle_journeys/containers/tools/ConstraintExclusionEditVehicleJourney.js @@ -0,0 +1,37 @@ +import actions from '../../actions' +import { connect } from 'react-redux' +import ConstraintExclusionEditVehicleJourneyComponent from '../../components/tools/ConstraintExclusionEditVehicleJourney' + +const mapStateToProps = (state, ownProps) => { +  return { +    editMode: state.editMode, +    modal: state.modal, +    vehicleJourneys: state.vehicleJourneys, +    status: state.status, +    disabled: ownProps.disabled +  } +} + +const mapDispatchToProps = (dispatch) => { +  return { +    onModalClose: () =>{ +      dispatch(actions.closeModal()) +    }, +    onOpenCalendarsEditModal: (vehicleJourneys) =>{ +      dispatch(actions.openConstraintExclusionEditModal(vehicleJourneys)) +    }, +    onDeleteConstraintZone: (constraint_zone) => { +      dispatch(actions.deleteConstraintZone(constraint_zone)) +    }, +    onConstraintZonesEditVehicleJourney: (vehicleJourneys, constraint_zones) => { +      dispatch(actions.editVehicleJourneyConstraintZones(vehicleJourneys, constraint_zones)) +    }, +    onSelectConstraintZone: (e) => { +      dispatch(actions.selectConstraintZone(e.params.data)) +    }, +  } +} + +const ConstraintExclusionEditVehicleJourney = connect(mapStateToProps, mapDispatchToProps)(ConstraintExclusionEditVehicleJourneyComponent) + +export default ConstraintExclusionEditVehicleJourney diff --git a/app/javascript/vehicle_journeys/reducers/modal.js b/app/javascript/vehicle_journeys/reducers/modal.js index 84567ec0d..75ab2f4ca 100644 --- a/app/javascript/vehicle_journeys/reducers/modal.js +++ b/app/javascript/vehicle_journeys/reducers/modal.js @@ -82,6 +82,30 @@ export default function modal(state = {}, action) {          },          confirmModal: {}        } +    case 'EDIT_CONSTRAINT_EXCLUSIONS_VEHICLEJOURNEY_MODAL': +      var vehicleJourneys = JSON.parse(JSON.stringify(action.vehicleJourneys)) +      let uniqExclusions = [] +      vehicleJourneys.map((vj, i) => { +        vj.ignored_routing_contraint_zone_ids.map((exclusion, j) =>{ +          let found = false +          uniqExclusions.map((id, i)=>{ +            if(id == parseInt(exclusion)){ +              found = true +            } +          }) +          if(!found){ +            uniqExclusions.push(parseInt(exclusion)) +          } +        }) +      }) +      return { +        type: 'constraint_exclusions_edit', +        modalProps: { +          vehicleJourneys: vehicleJourneys, +          selectedConstraintZones: uniqExclusions +        }, +        confirmModal: {} +      }      case 'SELECT_CP_EDIT_MODAL':        vehicleJourney =  _.assign({}, state.modalProps.vehicleJourney, {company: action.selectedItem})        newModalProps = _.assign({}, state.modalProps, {vehicleJourney}) @@ -93,9 +117,28 @@ export default function modal(state = {}, action) {      case 'SELECT_TT_CALENDAR_MODAL':        newModalProps = _.assign({}, state.modalProps, {selectedTimetable : action.selectedItem})        return _.assign({}, state, {modalProps: newModalProps}) -    case 'SELECT_PURCHASE_WINDOW_MODAL': -      newModalProps = _.assign({}, state.modalProps, {selectedPurchaseWindow : action.selectedItem}) +    case 'SELECT_CONSTRAINT_ZONE_MODAL': +      let selectedConstraintZones = state.modalProps.selectedConstraintZones +      let already_present = false +      selectedConstraintZones.map((zone_id, i)=>{ +        if(zone_id == parseInt(action.selectedZone.id)){ +          already_present = true +        } +      }) +      if(already_present){ return state } +      selectedConstraintZones.push(parseInt(action.selectedZone.id)) +      newModalProps = _.assign({}, state.modalProps, {selectedConstraintZones})        return _.assign({}, state, {modalProps: newModalProps}) +    case 'DELETE_CONSTRAINT_ZONE_MODAL': +        newModalProps = JSON.parse(JSON.stringify(state.modalProps)) +        selectedConstraintZones = state.modalProps.selectedConstraintZones.slice(0) +        selectedConstraintZones.map((zone_id, i) =>{ +          if(zone_id == parseInt(action.constraintZone.id)){ +            selectedConstraintZones.splice(i, 1) +          } +        }) +        newModalProps.selectedConstraintZones = selectedConstraintZones +        return _.assign({}, state, {modalProps: newModalProps})      case 'ADD_SELECTED_TIMETABLE':        if(state.modalProps.selectedTimetable){          newModalProps = JSON.parse(JSON.stringify(state.modalProps)) diff --git a/app/javascript/vehicle_journeys/reducers/vehicleJourneys.js b/app/javascript/vehicle_journeys/reducers/vehicleJourneys.js index b02c19a69..8432c5abe 100644 --- a/app/javascript/vehicle_journeys/reducers/vehicleJourneys.js +++ b/app/javascript/vehicle_journeys/reducers/vehicleJourneys.js @@ -116,6 +116,7 @@ const vehicleJourney= (state = {}, action, keep) => {          footnotes: [],          time_tables: [],          purchase_windows: [], +        ignored_routing_contraint_zone_ids: [],          vehicle_journey_at_stops: pristineVjasList,          selected: false,          deletable: false, @@ -232,6 +233,21 @@ export default function vehicleJourneys(state = [], action) {            return vj          }        }) +    case 'EDIT_VEHICLEJOURNEYS_CONSTRAINT_ZONES': +      let newExclusions = JSON.parse(JSON.stringify(action.zones)) +      return state.map((vj,i) =>{ +        if(vj.selected){ +          let updatedVJ = _.assign({}, vj) +          action.vehicleJourneys.map((vjm, j) =>{ +            if(vj.objectid == vjm.objectid){ +              updatedVJ.ignored_routing_contraint_zone_ids =  newExclusions +            } +          }) +          return updatedVJ +        }else{ +          return vj +        } +      })        case 'EDIT_VEHICLEJOURNEYS_PURCHASE_WINDOWS':          let newWindows = JSON.parse(JSON.stringify(action.purchase_windows))          return state.map((vj,i) =>{ diff --git a/app/models/chouette/vehicle_journey.rb b/app/models/chouette/vehicle_journey.rb index 814eeb388..b5476c210 100644 --- a/app/models/chouette/vehicle_journey.rb +++ b/app/models/chouette/vehicle_journey.rb @@ -299,7 +299,8 @@ module Chouette          'published_journey_identifier',          'published_journey_name',          'journey_pattern_id', -        'company_id' +        'company_id', +        'ignored_routing_contraint_zone_ids'        ).to_hash        if item['journey_pattern'] diff --git a/app/views/vehicle_journeys/index.html.slim b/app/views/vehicle_journeys/index.html.slim index 7fcee545f..2af0e5345 100644 --- a/app/views/vehicle_journeys/index.html.slim +++ b/app/views/vehicle_journeys/index.html.slim @@ -33,6 +33,7 @@    | window.all_missions = #{(@all_missions.to_json).html_safe};    | window.custom_fields = #{(@custom_fields.to_json).html_safe};    | window.extra_headers = #{(@extra_headers.to_json).html_safe}; +  | window.constraint_zones_routes = "#{url_for([@referential, @route.line, :routing_constraint_zones]).html_safe}";  - if has_feature?(:vehicle_journeys_return_route)    = javascript_tag do diff --git a/app/views/vehicle_journeys/show.rabl b/app/views/vehicle_journeys/show.rabl index 6c588416c..3a551f237 100644 --- a/app/views/vehicle_journeys/show.rabl +++ b/app/views/vehicle_journeys/show.rabl @@ -1,6 +1,6 @@  object @vehicle_journey -[:objectid, :published_journey_name, :published_journey_identifier, :company_id, :comment, :checksum, :custom_fields].each do |attr| +[:objectid, :published_journey_name, :published_journey_identifier, :company_id, :comment, :checksum, :custom_fields, :ignored_routing_contraint_zone_ids].each do |attr|    attributes attr, :unless => lambda { |m| m.send( attr).nil?}  end diff --git a/config/locales/vehicle_journeys.en.yml b/config/locales/vehicle_journeys.en.yml index c0d6f74d4..c073fd1e7 100644 --- a/config/locales/vehicle_journeys.en.yml +++ b/config/locales/vehicle_journeys.en.yml @@ -5,7 +5,7 @@ en:          id: Filter by ID...          journey_pattern: Filter by journey pattern...          timetable: Filter by timetable... - +        constraint_zone: Select a routing constraint zone          purchase_window: Filter by purchase window        cancel_selection: "Cancel Selection"        fetching_error: "There has been a problem fetching the data. Please reload the page to try again." @@ -17,6 +17,7 @@ en:        no_associated_timetables: No associated timetables        no_associated_purchase_windows: No associated purchase windows        no_associated_footnotes: No associated footnotes +      no_excluded_constraint_zones: No exluded constraint zone        duplicate:          one: Clone %{count} vehicle journey          other: Clone %{count} vehicle journeys @@ -58,6 +59,7 @@ en:          start: Start          end: End        ending_stop: "Arrival" +      excluded_constraint_zones: Excluded constraint zones        infos: Informations        set: "Set"        show_arrival_time: "Show and edit arrival times" @@ -161,6 +163,7 @@ en:          vehicle_journey_at_stop_ids: "Time list"          vehicle_type_identifier: "Vehicle Type Identifier"          start_time: Start time +        constraint_exclusions: Constraint Zones exclusions      errors:        models:          vehicle_journey: diff --git a/config/locales/vehicle_journeys.fr.yml b/config/locales/vehicle_journeys.fr.yml index b17e102a8..e9d97c8cc 100644 --- a/config/locales/vehicle_journeys.fr.yml +++ b/config/locales/vehicle_journeys.fr.yml @@ -6,6 +6,7 @@ fr:          journey_pattern: 'Filtrer par code, nom ou OID de mission...'          timetable: Filtrer par calendrier...          purchase_windows: Filtrer par calendrier commercial... +        constraint_zone: Choisir une ITL        cancel_selection: "Annuler la sélection"        fetching_error: "La récupération des missions a rencontré un problème. Rechargez la page pour tenter de corriger le problème."        line_routes: "Séquences d'arrêts de la ligne" @@ -16,6 +17,7 @@ fr:        no_associated_timetables: Aucun calendrier associé        no_associated_purchase_windows: Aucun calendrier commercial associé        no_associated_footnotes: Aucune note associée +      no_excluded_constraint_zones: Aucune ITL exclue        duplicate:          one: Dupliquer %{count} course          other: Dupliquer %{count} courses @@ -57,6 +59,7 @@ fr:          start: Début          end: Fin        ending_stop: "Arrivée" +      excluded_constraint_zones: Exclusions d'ITL        infos: Informations        set: "Fixer"        show_arrival_time: "Afficher et éditer les horaires d'arrivée" @@ -161,6 +164,7 @@ fr:          vehicle_journey_at_stop_ids: "Liste des horaires"          vehicle_type_identifier: "Type d'identifiant du véhicule"          start_time: Heure de départ +        constraint_exclusions: Exclusions d'ITL      errors:        models:          vehicle_journey: diff --git a/db/migrate/20180424125207_add_ignored_routing_contraint_zone_ids_to_vehicle_journeys.rb b/db/migrate/20180424125207_add_ignored_routing_contraint_zone_ids_to_vehicle_journeys.rb new file mode 100644 index 000000000..8886f6595 --- /dev/null +++ b/db/migrate/20180424125207_add_ignored_routing_contraint_zone_ids_to_vehicle_journeys.rb @@ -0,0 +1,5 @@ +class AddIgnoredRoutingContraintZoneIdsToVehicleJourneys < ActiveRecord::Migration +  def change +    add_column :vehicle_journeys, :ignored_routing_contraint_zone_ids, :integer, array: true, default: [] +  end +end | 
