diff options
| author | Alban Peignier | 2018-04-25 11:04:36 +0200 |
|---|---|---|
| committer | GitHub | 2018-04-25 11:04:36 +0200 |
| commit | 94406e685f252451b87fc7f8aa4445761858c9fd (patch) | |
| tree | 34e13f1855f92fd9e845443d71e2dddaa653100f | |
| parent | 45199ca5b142691467956bf610489ef16354f040 (diff) | |
| parent | d0c9be18498c1179a9341f51c5705a736d21ea71 (diff) | |
| download | chouette-core-94406e685f252451b87fc7f8aa4445761858c9fd.tar.bz2 | |
Merge pull request #519 from af83/6472-itls-exclusions
Add Constraint Zone exclusions to journeys. Refs #6472
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 |
