diff options
| author | Luc Donnet | 2018-01-31 12:20:25 +0100 | 
|---|---|---|
| committer | GitHub | 2018-01-31 12:20:25 +0100 | 
| commit | 4fa30ef61bd334de0627267b7b2eda4e09c8b728 (patch) | |
| tree | fa73d3d20cfa67a818b6f1d5f3a0a03898659cad | |
| parent | e54ee3a4379afb763906ab2ba2fd980349c48334 (diff) | |
| parent | c463c3a950246c4c2660ce7df1c1ea8f2acbe578 (diff) | |
| download | chouette-core-4fa30ef61bd334de0627267b7b2eda4e09c8b728.tar.bz2 | |
Merge pull request #263 from af83/5750-non-commercial-stop-areas
5750 non commercial stop areas
39 files changed, 431 insertions, 161 deletions
| diff --git a/Gemfile.lock b/Gemfile.lock index ba1a90a5a..ba86a911f 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -156,6 +156,7 @@ GEM        sort_alphabetical (~> 1.0)      crack (0.4.3)        safe_yaml (~> 1.0.0) +    crass (1.0.3)      cucumber (2.4.0)        builder (>= 2.1.2)        cucumber-core (~> 1.5.0) @@ -265,7 +266,7 @@ GEM      htmlbeautifier (1.3.1)      httparty (0.14.0)        multi_xml (>= 0.5.2) -    i18n (0.9.0) +    i18n (0.9.3)        concurrent-ruby (~> 1.0)      i18n-tasks (0.9.15)        activesupport (>= 4.0.2) @@ -302,7 +303,8 @@ GEM        thor        with_env (> 1.0)        xml-simple -    loofah (2.0.3) +    loofah (2.1.1) +      crass (~> 1.0.2)        nokogiri (>= 1.5.9)      mail (2.6.4)        mime-types (>= 1.16, < 4) @@ -313,7 +315,7 @@ GEM      mime-types-data (3.2016.0521)      mimemagic (0.3.2)      mini_portile2 (2.3.0) -    minitest (5.10.3) +    minitest (5.11.2)      money (6.10.1)        i18n (>= 0.6.4, < 1.0)      multi_json (1.12.1) @@ -368,7 +370,7 @@ GEM        rack      rack-protection (1.5.3)        rack -    rack-proxy (0.6.2) +    rack-proxy (0.6.3)        rack      rack-test (0.6.3)        rack (>= 1.0) @@ -396,8 +398,8 @@ GEM        rails-assets-jquery (>= 1.0.0)      rails-deprecated_sanitizer (1.0.3)        activesupport (>= 4.2.0.alpha) -    rails-dom-testing (1.0.8) -      activesupport (>= 4.2.0.beta, < 5.0) +    rails-dom-testing (1.0.9) +      activesupport (>= 4.2.0, < 5.0)        nokogiri (~> 1.6)        rails-deprecated_sanitizer (>= 1.0.1)      rails-erd (1.5.2) @@ -419,7 +421,7 @@ GEM        thor (>= 0.18.1, < 2.0)      rainbow (2.2.2)        rake -    rake (12.0.0) +    rake (12.3.0)      ransack (1.8.3)        actionpack (>= 3.0)        activerecord (>= 3.0) @@ -535,7 +537,7 @@ GEM      therubyracer (0.12.3)        libv8 (~> 3.16.14.15)        ref -    thor (0.19.4) +    thor (0.20.0)      thread (0.2.2)      thread_safe (0.3.6)      tilt (1.4.1) @@ -547,7 +549,7 @@ GEM        json (>= 1.8, < 3.0)        parser (>= 2.3.0.7)        rainbow (>= 1.99.1, < 3.0) -    tzinfo (1.2.3) +    tzinfo (1.2.4)        thread_safe (~> 0.1)      uglifier (2.7.2)        execjs (>= 0.3.0) @@ -560,7 +562,7 @@ GEM        addressable (>= 2.3.6)        crack (>= 0.3.2)        hashdiff -    webpacker (3.0.2) +    webpacker (3.2.1)        activesupport (>= 4.2)        rack-proxy (>= 0.6.1)        railties (>= 4.2) diff --git a/app/assets/stylesheets/OpenLayers/custom.sass b/app/assets/stylesheets/OpenLayers/custom.sass index 7a5b4baf1..5a3612f99 100644 --- a/app/assets/stylesheets/OpenLayers/custom.sass +++ b/app/assets/stylesheets/OpenLayers/custom.sass @@ -2,6 +2,27 @@    .list-group-item &      margin-top: 15px +  .routes-labels +    padding: 0 +    display: flex +    justify-content: space-between +    flex-wrap: wrap +    margin: 5px -5px +    li +      list-style-type: none +      flex: 1 0 25% +      border: 1px solid $lightgrey +      padding: 5px +      margin: 5px +      border-radius: 5px +      cursor: pointer +      color: $blue +      white-space: nowrap +      max-width: 33% + +      &:hover +        background: $orange +        color: white    .ol-scale-line      background-color: transparent diff --git a/app/assets/stylesheets/components/_forms.sass b/app/assets/stylesheets/components/_forms.sass index b7f720963..214795b8b 100644 --- a/app/assets/stylesheets/components/_forms.sass +++ b/app/assets/stylesheets/components/_forms.sass @@ -85,9 +85,15 @@ input  // BS horizontal form label positionning fix  .form-horizontal +  input[type="radio"].form-control +    height: auto +    width: auto    .form-group      position: relative - +    .radio-inline +      padding-top: 4px +      &:first-child +        padding-left: 0      > .control-label        &[class*='col-sm-']          float: none diff --git a/app/assets/stylesheets/modules/_vj_collection.sass b/app/assets/stylesheets/modules/_vj_collection.sass index 56769e52b..d99c67bd7 100644 --- a/app/assets/stylesheets/modules/_vj_collection.sass +++ b/app/assets/stylesheets/modules/_vj_collection.sass @@ -9,6 +9,9 @@          position: relative          padding-left: 25px +        .fa +          margin-left: 5px +          > .headlined            &:before              margin-left: -25px @@ -113,6 +116,9 @@            margin-left: 5px        &.has-error +        .errors +          color: $red +          font-size: 0.8em          &:before            content: ''            position: absolute diff --git a/app/controllers/stop_areas_controller.rb b/app/controllers/stop_areas_controller.rb index 79ffea72e..8d424b8d1 100644 --- a/app/controllers/stop_areas_controller.rb +++ b/app/controllers/stop_areas_controller.rb @@ -97,7 +97,7 @@ class StopAreasController < ChouetteController      edit! do        stop_area.position ||= stop_area.default_position        map.editable = true -   end +    end    end    def destroy @@ -203,6 +203,7 @@ class StopAreasController < ChouetteController        :url,        :waiting_time,        :zip_code, +      :kind,      )    end diff --git a/app/controllers/vehicle_journeys_controller.rb b/app/controllers/vehicle_journeys_controller.rb index c1762c13e..ed6ba6ed1 100644 --- a/app/controllers/vehicle_journeys_controller.rb +++ b/app/controllers/vehicle_journeys_controller.rb @@ -66,6 +66,8 @@ class VehicleJourneysController < ChouetteController              :city_name => sp.stop_area.try(:city_name),              :comment => sp.stop_area.try(:comment),              :area_type => sp.stop_area.try(:area_type), +            :area_type_i18n => I18n.t(sp.stop_area.try(:area_type), scope: 'area_types.label'), +            :area_kind => sp.stop_area.try(:kind),              :stop_area_id => sp.stop_area_id,              :registration_number => sp.stop_area.try(:registration_number),              :nearest_topic_name => sp.stop_area.try(:nearest_topic_name), diff --git a/app/helpers/routes_helper.rb b/app/helpers/routes_helper.rb index 4bffa99d4..61714a066 100644 --- a/app/helpers/routes_helper.rb +++ b/app/helpers/routes_helper.rb @@ -19,13 +19,15 @@ module RoutesHelper      css    end -  def route_json_for_edit(route) -    route.stop_points.includes(:stop_area).order(:position).map do |stop_point| +  def route_json_for_edit(route, serialize: true) +    data = route.stop_points.includes(:stop_area).order(:position).map do |stop_point|        stop_area_attributes = stop_point.stop_area.attributes.slice("name","city_name", "zip_code", "registration_number", "longitude", "latitude", "area_type", "comment")        stop_area_attributes["short_name"] = truncate(stop_area_attributes["name"], :length => 30) || ""        stop_point_attributes = stop_point.attributes.slice("for_boarding","for_alighting")        stop_area_attributes.merge(stop_point_attributes).merge(stoppoint_id: stop_point.id, stoparea_id: stop_point.stop_area.id).merge(user_objectid: stop_point.stop_area.user_objectid) -    end.to_json +    end +    data = data.to_json if serialize +    data    end  end diff --git a/app/javascript/helpers/master_slave.coffee b/app/javascript/helpers/master_slave.coffee new file mode 100644 index 000000000..4866a55e3 --- /dev/null +++ b/app/javascript/helpers/master_slave.coffee @@ -0,0 +1,18 @@ +class MasterSlave +  constructor: (selector)-> +    $(selector).find('[data-master]').each (i, slave)-> +      $slave = $(slave) +      master = $($slave.data().master) +      console.log $slave +      console.log $slave.find("input:disabled, select:disabled") +      $slave.find("input:disabled, select:disabled").attr "data-slave-force-disabled", "true" +      toggle = -> +        val = master.filter(":checked").val() if master.filter("[type=radio]").length > 0 +        val ||= master.val() +        selected = val == $slave.data().value +        $slave.toggle selected +        $slave.find("input, select").filter(":not([data-slave-force-disabled])").attr "disabled", !selected +      master.change toggle +      toggle() + +export default MasterSlave diff --git a/app/javascript/helpers/routes_map.coffee b/app/javascript/helpers/routes_map.coffee new file mode 100644 index 000000000..85def1390 --- /dev/null +++ b/app/javascript/helpers/routes_map.coffee @@ -0,0 +1,157 @@ +class RoutesMap +  constructor: (@target)-> +    @initMap() +    @area = [] +    @seenStopIds = [] +    @routes = {} + +  initMap: -> +    @map = new ol.Map +      target: @target, +      layers:   [ new ol.layer.Tile(source: new ol.source.OSM()) ] +      controls: [ new ol.control.ScaleLine(), new ol.control.Zoom(), new ol.control.ZoomSlider() ], +      interactions: ol.interaction.defaults(zoom: true) +      view: new ol.View() + +  addRoutes: (routes)-> +    for route in routes +      @addRoute route + +  addRoute: (route)-> +    geoColPts = [] +    geoColLns = [] +    @routes[route.id] = route if route.id +    stops = route.stops || route +    geoColEdges = [ +      new ol.Feature({ +        geometry: new ol.geom.Point(ol.proj.fromLonLat([parseFloat(stops[0].longitude), parseFloat(stops[0].latitude)])) +      }), +      new ol.Feature({ +        geometry: new ol.geom.Point(ol.proj.fromLonLat([parseFloat(stops[stops.length - 1].longitude), parseFloat(stops[stops.length - 1].latitude)])) +      }) +    ] +    stops.forEach (stop, i) => +      if i < stops.length - 1 +        geoColLns.push new ol.Feature +          geometry: new ol.geom.LineString([ +            ol.proj.fromLonLat([parseFloat(stops[i].longitude), parseFloat(stops[i].latitude)]), +            ol.proj.fromLonLat([parseFloat(stops[i + 1].longitude), parseFloat(stops[i + 1].latitude)]) +          ]) + +      geoColPts.push(new ol.Feature({ +        geometry: new ol.geom.Point(ol.proj.fromLonLat([parseFloat(stop.longitude), parseFloat(stop.latitude)])) +      })) +      unless @seenStopIds.indexOf(stop.stoparea_id) > 0 +        @area.push [parseFloat(stop.longitude), parseFloat(stop.latitude)] +        @seenStopIds.push stop.stoparea_id + +    vectorPtsLayer = new ol.layer.Vector({ +      source: new ol.source.Vector({ +        features: geoColPts +      }), +      style: @defaultStyles(), +      zIndex: 2 +    }) +    route.vectorPtsLayer = vectorPtsLayer if route.id +    vectorEdgesLayer = new ol.layer.Vector({ +      source: new ol.source.Vector({ +        features: geoColEdges +      }), +      style: @edgeStyles(), +      zIndex: 3 +    }) +    route.vectorEdgesLayer = vectorEdgesLayer if route.id +    vectorLnsLayer = new ol.layer.Vector({ +      source: new ol.source.Vector({ +        features: geoColLns +      }), +      style: [@lineStyle()], +      zIndex: 1 +    }) +    route.vectorLnsLayer = vectorLnsLayer if route.id +    @map.addLayer vectorPtsLayer +    @map.addLayer vectorEdgesLayer +    @map.addLayer vectorLnsLayer + +  lineStyle: (highlighted=false)-> +    new ol.style.Style +      stroke: new ol.style.Stroke +        color: if highlighted then "#ed7f00" else '#007fbb' +        width: 3 + +  edgeStyles: (highlighted=false)-> +    new ol.style.Style +      image: new ol.style.Circle +        radius: 5 +        stroke: new ol.style.Stroke +          color: if highlighted then "#ed7f00" else '#007fbb' +          width: 2 +        fill: new ol.style.Fill +          color: if highlighted then "#ed7f00" else '#007fbb' +          width: 2 + +  defaultStyles: (highlighted=false)-> +    new ol.style.Style +      image: new ol.style.Circle +        radius: 4 +        stroke: new ol.style.Stroke +          color: if highlighted then "#ed7f00" else '#007fbb' +          width: 2 +        fill: new ol.style.Fill +          color: '#ffffff' +          width: 2 + +  addRoutesLabels: -> +    labelsContainer = $("<ul class='routes-labels'></ul>") +    labelsContainer.appendTo $("##{@target}") +    @vectorPtsLayer = null +    @vectorEdgesLayer = null +    @vectorLnsLayer = null +    Object.keys(@routes).forEach (id)=> +      route = @routes[id] +      label = $("<li>#{route.name}</ul>") +      label.appendTo labelsContainer +      label.mouseleave => +        route.vectorPtsLayer.setStyle @defaultStyles(false) +        route.vectorEdgesLayer.setStyle @edgeStyles(false) +        route.vectorLnsLayer.setStyle @lineStyle(false) +        route.vectorPtsLayer.setZIndex 2 +        route.vectorEdgesLayer.setZIndex 3 +        route.vectorLnsLayer.setZIndex 1 +        @fitZoom() +      label.mouseenter => +        route.vectorPtsLayer.setStyle @defaultStyles(true) +        route.vectorEdgesLayer.setStyle @edgeStyles(true) +        route.vectorLnsLayer.setStyle @lineStyle(true) +        route.vectorPtsLayer.setZIndex 11 +        route.vectorEdgesLayer.setZIndex 12 +        route.vectorLnsLayer.setZIndex 10 +        @fitZoom(route) + +  fitZoom: (route)-> +    if route +      area = [] +      route.stops.forEach (stop, i) => +        area.push [parseFloat(stop.longitude), parseFloat(stop.latitude)] +    else +      area = @area +    boundaries = ol.extent.applyTransform( +      ol.extent.boundingExtent(area), ol.proj.getTransform('EPSG:4326', 'EPSG:3857') +    ) +    @map.getView().fit boundaries, @map.getSize() +    tooCloseToBounds = false +    mapBoundaries = @map.getView().calculateExtent @map.getSize() +    mapWidth = mapBoundaries[2] - mapBoundaries[0] +    mapHeight = mapBoundaries[3] - mapBoundaries[1] +    marginSize = 0.1 +    heightMargin = marginSize * mapHeight +    widthMargin = marginSize * mapWidth +    tooCloseToBounds = tooCloseToBounds || (boundaries[0] - mapBoundaries[0]) < widthMargin +    tooCloseToBounds = tooCloseToBounds || (mapBoundaries[2] - boundaries[2]) < widthMargin +    tooCloseToBounds = tooCloseToBounds || (boundaries[1] - mapBoundaries[1]) < heightMargin +    tooCloseToBounds = tooCloseToBounds || (mapBoundaries[3] - boundaries[3]) < heightMargin +    if tooCloseToBounds +      @map.getView().setZoom(@map.getView().getZoom() - 1) + + +export default RoutesMap diff --git a/app/javascript/helpers/stop_area_header_manager.js b/app/javascript/helpers/stop_area_header_manager.js index c9f397dee..2c820caf9 100644 --- a/app/javascript/helpers/stop_area_header_manager.js +++ b/app/javascript/helpers/stop_area_header_manager.js @@ -19,7 +19,7 @@ export default class StopAreaHeaderManager {        <div          className={(showHeadline) ? 'headlined' : ''}          data-headline={showHeadline} -        title={sp.city_name + ' (' + sp.zip_code +')'} +        title={sp.city_name ? sp.city_name + ' (' + sp.zip_code +')' : ""}        >          <span>            <span> @@ -27,6 +27,8 @@ export default class StopAreaHeaderManager {              {sp.time_zone_formatted_offset && <span className="small">                 ({sp.time_zone_formatted_offset})              </span>} +            {sp.area_kind == 'non_commercial' && <span className="fa fa-question-circle" title={sp.area_type_i18n}> +            </span>}            </span>          </span>        </div> diff --git a/app/javascript/packs/referential_lines/show.js b/app/javascript/packs/referential_lines/show.js new file mode 100644 index 000000000..542188018 --- /dev/null +++ b/app/javascript/packs/referential_lines/show.js @@ -0,0 +1,10 @@ +import clone from '../../helpers/clone' +import RoutesMap from '../../helpers/routes_map' + +let routes = clone(window, "routes", true) +routes = JSON.parse(decodeURIComponent(routes)) + +var map = new RoutesMap('routes_map') +map.addRoutes(routes) +// map.addRoutesLabels() +map.fitZoom() diff --git a/app/javascript/packs/routes/show.js b/app/javascript/packs/routes/show.js index 71777c379..c20de0800 100644 --- a/app/javascript/packs/routes/show.js +++ b/app/javascript/packs/routes/show.js @@ -1,121 +1,8 @@  import clone from '../../helpers/clone' +import RoutesMap from '../../helpers/routes_map' +  let route = clone(window, "route", true)  route = JSON.parse(decodeURIComponent(route)) - -const geoColPts = [] -const geoColLns = [] -const area = [] -const geoColEdges = [ -  new ol.Feature({ -    geometry: new ol.geom.Point(ol.proj.fromLonLat([parseFloat(route[0].longitude), parseFloat(route[0].latitude)])) -  }), -  new ol.Feature({ -    geometry: new ol.geom.Point(ol.proj.fromLonLat([parseFloat(route[route.length - 1].longitude), parseFloat(route[route.length - 1].latitude)])) -  }) -] -route.forEach(function (stop, i) { -  if (i < route.length - 1) { -    geoColLns.push(new ol.Feature({ -      geometry: new ol.geom.LineString([ -        ol.proj.fromLonLat([parseFloat(route[i].longitude), parseFloat(route[i].latitude)]), -        ol.proj.fromLonLat([parseFloat(route[i + 1].longitude), parseFloat(route[i + 1].latitude)]) -      ]) -    })) -  } -  geoColPts.push(new ol.Feature({ -    geometry: new ol.geom.Point(ol.proj.fromLonLat([parseFloat(stop.longitude), parseFloat(stop.latitude)])) -  })) -  area.push([parseFloat(stop.longitude), parseFloat(stop.latitude)]) -}) -var edgeStyles = new ol.style.Style({ -  image: new ol.style.Circle(({ -    radius: 5, -    stroke: new ol.style.Stroke({ -      color: '#007fbb', -      width: 2 -    }), -    fill: new ol.style.Fill({ -      color: '#007fbb', -      width: 2 -    }) -  })) -}) -var defaultStyles = new ol.style.Style({ -  image: new ol.style.Circle(({ -    radius: 4, -    stroke: new ol.style.Stroke({ -      color: '#007fbb', -      width: 2 -    }), -    fill: new ol.style.Fill({ -      color: '#ffffff', -      width: 2 -    }) -  })) -}) -var lineStyle = new ol.style.Style({ -  stroke: new ol.style.Stroke({ -    color: '#007fbb', -    width: 3 -  }) -}) - -var vectorPtsLayer = new ol.layer.Vector({ -  source: new ol.source.Vector({ -    features: geoColPts -  }), -  style: defaultStyles, -  zIndex: 2 -}) -var vectorEdgesLayer = new ol.layer.Vector({ -  source: new ol.source.Vector({ -    features: geoColEdges -  }), -  style: edgeStyles, -  zIndex: 3 -}) -var vectorLnsLayer = new ol.layer.Vector({ -  source: new ol.source.Vector({ -    features: geoColLns -  }), -  style: [lineStyle], -  zIndex: 1 -}) - -var map = new ol.Map({ -  target: 'route_map', -  layers: [ -    new ol.layer.Tile({ -      source: new ol.source.OSM() -    }), -    vectorPtsLayer, -    vectorEdgesLayer, -    vectorLnsLayer -  ], -  controls: [new ol.control.ScaleLine(), new ol.control.Zoom(), new ol.control.ZoomSlider()], -  interactions: ol.interaction.defaults({ -    zoom: true -  }), -  view: new ol.View({ -    center: ol.proj.fromLonLat([parseFloat(route[0].longitude), parseFloat(route[0].latitude)]), -    zoom: 13 -  }) -}); -const boundaries = ol.extent.applyTransform( -  ol.extent.boundingExtent(area), ol.proj.getTransform('EPSG:4326', 'EPSG:3857') -) -map.getView().fit(boundaries, map.getSize()); -let tooCloseToBounds = false -const mapBoundaries = map.getView().calculateExtent(map.getSize()) -const mapWidth = mapBoundaries[2] - mapBoundaries[0] -const mapHeight = mapBoundaries[3] - mapBoundaries[1] -const marginSize = 0.1 -const heightMargin = marginSize * mapHeight -const widthMargin = marginSize * mapWidth -tooCloseToBounds = tooCloseToBounds || (boundaries[0] - mapBoundaries[0]) < widthMargin -tooCloseToBounds = tooCloseToBounds || (mapBoundaries[2] - boundaries[2]) < widthMargin -tooCloseToBounds = tooCloseToBounds || (boundaries[1] - mapBoundaries[1]) < heightMargin -tooCloseToBounds = tooCloseToBounds || (mapBoundaries[3] - boundaries[3]) < heightMargin -if(tooCloseToBounds){ -  map.getView().setZoom(map.getView().getZoom() - 1) -} +var map = new RoutesMap('route_map') +map.addRoute(route) +map.fitZoom() diff --git a/app/javascript/packs/stop_areas/new.js b/app/javascript/packs/stop_areas/new.js new file mode 100644 index 000000000..ffe702cdb --- /dev/null +++ b/app/javascript/packs/stop_areas/new.js @@ -0,0 +1,3 @@ +import MasterSlave from "../../helpers/master_slave" + +new MasterSlave("form") diff --git a/app/javascript/routes/components/StopPointList.js b/app/javascript/routes/components/StopPointList.js index 43a027084..b39fa0c9c 100644 --- a/app/javascript/routes/components/StopPointList.js +++ b/app/javascript/routes/components/StopPointList.js @@ -56,7 +56,7 @@ export default function StopPointList({ stopPoints, onDeleteClick, onMoveUpClick    )  } -StopPointList.PropTypes = { +StopPointList.propTypes = {    stopPoints: PropTypes.array.isRequired,    onDeleteClick: PropTypes.func.isRequired,    onMoveUpClick: PropTypes.func.isRequired, @@ -68,4 +68,4 @@ StopPointList.PropTypes = {  StopPointList.contextTypes = {    I18n: PropTypes.object -}
\ No newline at end of file +} diff --git a/app/javascript/vehicle_journeys/actions/index.js b/app/javascript/vehicle_journeys/actions/index.js index 2675328e3..4a4ec371d 100644 --- a/app/javascript/vehicle_journeys/actions/index.js +++ b/app/javascript/vehicle_journeys/actions/index.js @@ -380,6 +380,17 @@ const actions = {          }        })    }, + +  validate : (dispatch, vehicleJourneys, next) => { +    dispatch(actions.didValidateVehicleJourneys(vehicleJourneys)) +    actions.submitVehicleJourneys(dispatch, vehicleJourneys, next) +  }, + +  didValidateVehicleJourneys : (vehicleJourneys) => ({ +    type: 'DID_VALIDATE_VEHICLE_JOURNEYS', +    vehicleJourneys +  }), +    submitVehicleJourneys : (dispatch, state, next) => {      dispatch(actions.fetchingApi())      let urlJSON = window.location.pathname + "_collection.json" diff --git a/app/javascript/vehicle_journeys/components/SaveVehicleJourneys.js b/app/javascript/vehicle_journeys/components/SaveVehicleJourneys.js index 6e94b04a3..fb921df9c 100644 --- a/app/javascript/vehicle_journeys/components/SaveVehicleJourneys.js +++ b/app/javascript/vehicle_journeys/components/SaveVehicleJourneys.js @@ -13,7 +13,7 @@ export default class SaveVehicleJourneys extends SaveButton{    }    submitForm(){ -    this.props.onSubmitVehicleJourneys(this.props.dispatch, this.props.vehicleJourneys) +    this.props.validate(this.props.vehicleJourneys, this.props.dispatch)    }  } diff --git a/app/javascript/vehicle_journeys/components/VehicleJourney.js b/app/javascript/vehicle_journeys/components/VehicleJourney.js index d240757a3..2b5783dda 100644 --- a/app/javascript/vehicle_journeys/components/VehicleJourney.js +++ b/app/javascript/vehicle_journeys/components/VehicleJourney.js @@ -153,6 +153,9 @@ export default class VehicleJourney extends Component {                        />                    </span>                  </div> +                {vj.errors && <div className="errors"> +                  {vj.errors} +                </div>}              </div>            </div>          )} diff --git a/app/javascript/vehicle_journeys/components/VehicleJourneys.js b/app/javascript/vehicle_journeys/components/VehicleJourneys.js index b188962c2..256ca81f9 100644 --- a/app/javascript/vehicle_journeys/components/VehicleJourneys.js +++ b/app/javascript/vehicle_journeys/components/VehicleJourneys.js @@ -89,7 +89,7 @@ export default class VehicleJourneys extends Component {                </div>              )} -            { _.some(this.props.vehicleJourneys, 'errors') && ( +            { this.props.vehicleJourneys.errors && this.props.vehicleJourneys.errors.length && _.some(this.props.vehicleJourneys, 'errors') && (                <div className="alert alert-danger mt-sm">                  <strong>Erreur : </strong>                  {this.props.vehicleJourneys.map((vj, index) => diff --git a/app/javascript/vehicle_journeys/containers/SaveVehicleJourneys.js b/app/javascript/vehicle_journeys/containers/SaveVehicleJourneys.js index f5f879ed8..3daf831f8 100644 --- a/app/javascript/vehicle_journeys/containers/SaveVehicleJourneys.js +++ b/app/javascript/vehicle_journeys/containers/SaveVehicleJourneys.js @@ -23,6 +23,9 @@ const mapDispatchToProps = (dispatch) => {      },      onSubmitVehicleJourneys: (next, state) => {        actions.submitVehicleJourneys(dispatch, state, next) +    }, +    validate: (state) =>{ +      actions.validate(dispatch, state)      }    }  } diff --git a/app/javascript/vehicle_journeys/reducers/vehicleJourneys.js b/app/javascript/vehicle_journeys/reducers/vehicleJourneys.js index ae45993a8..1a15ec46d 100644 --- a/app/javascript/vehicle_journeys/reducers/vehicleJourneys.js +++ b/app/javascript/vehicle_journeys/reducers/vehicleJourneys.js @@ -273,6 +273,8 @@ export default function vehicleJourneys(state = [], action) {            return vj          }        }) +    case 'DID_VALIDATE_VEHICLE_JOURNEYS': +      return [...action.vehicleJourneys]      default:        return state    } diff --git a/app/models/chouette/area_type.rb b/app/models/chouette/area_type.rb index 4703ea646..e17d2ee8d 100644 --- a/app/models/chouette/area_type.rb +++ b/app/models/chouette/area_type.rb @@ -1,13 +1,22 @@  class Chouette::AreaType    include Comparable -  ALL = %i(zdep zder zdlp zdlr lda gdl).freeze +  COMMERCIAL = %i(zdep zder zdlp zdlr lda gdl).freeze +  NON_COMMERCIAL = %i(deposit border service_area relief other).freeze +  ALL = COMMERCIAL + NON_COMMERCIAL +  @@commercial = COMMERCIAL +  @@non_commercial = NON_COMMERCIAL    @@all = ALL -  mattr_accessor :all +  mattr_accessor :all, :commercial, :non_commercial -  def self.all=(values) -    @@all = ALL & values +  def self.commercial=(values) +    @@commercial = COMMERCIAL & values +    reset_caches! +  end + +  def self.non_commercial=(values) +    @@non_commercial = NON_COMMERCIAL & values      reset_caches!    end @@ -20,12 +29,14 @@ class Chouette::AreaType    end    def self.reset_caches! +    @@all = @@commercial + @@non_commercial      @@instances = {} -    @@options = nil +    @@options = {}    end -  def self.options -    @@options ||= all.map { |c| find(c) }.map { |t| [ t.label, t.code ] } +  def self.options(kind=:all) +    @@options ||= {} +    @@options[kind] ||= self.send(kind).map { |c| find(c) }.map { |t| [ t.label, t.code ] }    end    attr_reader :code diff --git a/app/models/chouette/stop_area.rb b/app/models/chouette/stop_area.rb index ea1855ea8..ad42d54ae 100644 --- a/app/models/chouette/stop_area.rb +++ b/app/models/chouette/stop_area.rb @@ -10,6 +10,7 @@ module Chouette      extend Enumerize      enumerize :area_type, in: Chouette::AreaType::ALL +    enumerize :kind, in: %i(commercial non_commercial)      with_options dependent: :destroy do |assoc|        assoc.has_many :stop_points @@ -31,6 +32,7 @@ module Chouette      validates_format_of :registration_number, :with => %r{\A[\d\w_\-]+\Z}, :allow_blank => true      validates_presence_of :name +    validates_presence_of :kind      validates_presence_of :latitude, :if => :longitude      validates_presence_of :longitude, :if => :latitude      validates_numericality_of :latitude, :less_than_or_equal_to => 90, :greater_than_or_equal_to => -90, :allow_nil => true @@ -41,6 +43,7 @@ module Chouette      validates_numericality_of :waiting_time, greater_than_or_equal_to: 0, only_integer: true, if: :waiting_time      validate :parent_area_type_must_be_greater +    validate :area_type_of_right_kind      def self.nullable_attributes        [:registration_number, :street_name, :country_code, :fare_code, @@ -56,6 +59,13 @@ module Chouette        end      end +    def area_type_of_right_kind + +      unless Chouette::AreaType.send(self.kind).map(&:to_s).include?(self.area_type) +        errors.add(:area_type, I18n.t('stop_areas.errors.incorrect_kind_area_type')) +      end +    end +      after_update :clean_invalid_access_links      before_save :coordinates_to_lat_lng @@ -96,6 +106,10 @@ module Chouette        end      end +    def local_id +      id.to_s +    end +      def children_in_depth        return [] if self.children.empty? @@ -374,5 +388,9 @@ module Chouette        return nil unless time_zone.present?        ActiveSupport::TimeZone[time_zone]&.formatted_offset      end + +    def commercial? +      kind == "commercial" +    end    end  end diff --git a/app/policies/stop_area_policy.rb b/app/policies/stop_area_policy.rb index 6db48b702..fd73b7092 100644 --- a/app/policies/stop_area_policy.rb +++ b/app/policies/stop_area_policy.rb @@ -3,7 +3,7 @@ class StopAreaPolicy < ApplicationPolicy      def search_scope scope_name        scope = resolve        if scope_name&.to_s == "route_editor" -        scope = scope.where(area_type: 'zdep') unless user.organisation.has_feature?("route_stop_areas_all_types") +        scope = scope.where("kind = ? OR area_type = ?", :non_commercial, 'zdep') unless user.organisation.has_feature?("route_stop_areas_all_types")        end        scope      end diff --git a/app/views/referential_lines/show.html.slim b/app/views/referential_lines/show.html.slim index 02d605d8c..5ea0e31bb 100644 --- a/app/views/referential_lines/show.html.slim +++ b/app/views/referential_lines/show.html.slim @@ -17,7 +17,8 @@               @line.human_attribute_name(:transport_submode) => (@line.transport_submode.present? ? t("enumerize.transport_submode.#{@line.transport_submode}") : '-'),               @line.human_attribute_name(:url) => (@line.url ? @line.url : '-'),               @line.human_attribute_name(:seasonal) => (@line.seasonal? ? t('true') : t('false')),} - +      .col-lg-6.col-md-6.col-sm-12.col-xs-12 +        #routes_map.map.mb-lg      .row        .col-lg-12          .h3 = t('lines.show.routes.title') @@ -79,3 +80,8 @@            .row.mt-xs              .col-lg-12                = replacement_msg t('routes.search_no_results') + += javascript_tag do +  | window.routes = "#{URI.escape(@routes.map{|r| {name: r.name, id: r.id, stops: route_json_for_edit(r, serialize: false)}}.to_json)}" + += javascript_pack_tag 'referential_lines/show.js' diff --git a/app/views/stop_areas/_form.html.slim b/app/views/stop_areas/_form.html.slim index b2322f73a..aa156f7bd 100644 --- a/app/views/stop_areas/_form.html.slim +++ b/app/views/stop_areas/_form.html.slim @@ -6,10 +6,13 @@          /= @map.to_html          = f.input :id, as: :hidden          = f.input :name, :input_html => {:title => t("formtastic.titles#{format_restriction_for_locales(@referential)}.stop_area.name")} +        = f.input :kind, as: :radio_buttons, checked: @stop_area.kind, :input_html => {:disabled => !@stop_area.new_record?}, :include_blank => false, item_wrapper_class: 'radio-inline', wrapper: :horizontal_form, disabled: !@stop_area.new_record? -        = f.input :parent_id, as: :select, :collection => [f.object.parent_id], input_html: { data: { select2_ajax: 'true', url: autocomplete_stop_area_referential_stop_areas_path(@stop_area_referential), initvalue: {id: f.object.parent_id, text: f.object.parent.try(:full_name)}}} - -        = f.input :area_type, as: :select, :input_html => {:disabled => !@stop_area.new_record?}, :collection => Chouette::AreaType.options, :include_blank => false +        .slave data-master="[name='stop_area[kind]']" data-value="commercial" +          = f.input :parent_id, as: :select, :collection => [f.object.parent_id], input_html: { data: { select2_ajax: 'true', url: autocomplete_stop_area_referential_stop_areas_path(@stop_area_referential), initvalue: {id: f.object.parent_id, text: f.object.parent.try(:full_name)}}} +        - %i(non_commercial commercial).each do |kind| +          .slave data-master="[name='stop_area[kind]']" data-value=kind +            = f.input :area_type, as: :select, :input_html => {id: kind, :disabled => !@stop_area.new_record?}, :collection => Chouette::AreaType.options(kind), :include_blank => false, disabled: !@stop_area.new_record?          .location_info            h3 = t("stop_areas.stop_area.localisation") @@ -49,3 +52,5 @@    .separator    = f.button :submit, t('actions.submit'), class: 'btn btn-default formSubmitr', form: 'stop_area_form' + += javascript_pack_tag "stop_areas/new" diff --git a/app/views/stop_areas/show.html.slim b/app/views/stop_areas/show.html.slim index b5ec8ac00..b0896c1e0 100644 --- a/app/views/stop_areas/show.html.slim +++ b/app/views/stop_areas/show.html.slim @@ -6,11 +6,11 @@    .container-fluid      .row        .col-lg-6.col-md-6.col-sm-12.col-xs-12 -        - attributes = { t('id_reflex') => @stop_area.get_objectid.short_id, -            @stop_area.human_attribute_name(:parent) => @stop_area.parent ? link_to(@stop_area.parent.name, stop_area_referential_stop_area_path(@stop_area_referential, @stop_area.parent)) : "-", -            @stop_area.human_attribute_name(:stop_area_type) => Chouette::AreaType.find(@stop_area.area_type).try(:label), +        - attributes = { t('id_reflex') => @stop_area.get_objectid.short_id } +        - attributes.merge!({ @stop_area.human_attribute_name(:parent) => @stop_area.parent ? link_to(@stop_area.parent.name, stop_area_referential_stop_area_path(@stop_area_referential, @stop_area.parent)) : "-" }) if @stop_area.commercial? +        - attributes.merge!({ @stop_area.human_attribute_name(:stop_area_type) => Chouette::AreaType.find(@stop_area.area_type).try(:label),              @stop_area.human_attribute_name(:registration_number) => @stop_area.registration_number, -            } +            })          - attributes.merge!(@stop_area.human_attribute_name(:waiting_time) => @stop_area.waiting_time_text) if has_feature?(:stop_area_waiting_time)          - attributes.merge!({ "Coordonnées" => geo_data(@stop_area, @stop_area_referential),              @stop_area.human_attribute_name(:zip_code) => @stop_area.zip_code, diff --git a/app/views/vehicle_journeys/show.rabl b/app/views/vehicle_journeys/show.rabl index fc65e6cb6..dca0866b3 100644 --- a/app/views/vehicle_journeys/show.rabl +++ b/app/views/vehicle_journeys/show.rabl @@ -45,6 +45,7 @@ child(:vehicle_journey_at_stops_matrix, :object_root => false) do |vehicle_stops      end      node(:dummy) { vehicle_stop.dummy } +    node(:area_kind) { vehicle_stop.stop_point.stop_area.kind }      node(:stop_area_object_id) do        vehicle_stop.stop_point.stop_area.objectid diff --git a/config/locales/area_types.en.yml b/config/locales/area_types.en.yml index 34ec3243d..5d23a6665 100644 --- a/config/locales/area_types.en.yml +++ b/config/locales/area_types.en.yml @@ -6,3 +6,9 @@ en:        zdlp: ZDLp        zdlr: ZDLr        lda: LDA +      gdl: GDL +      deposit: Deposit +      border: Border +      service_area: Service Area +      relief: Relief point +      other: Other diff --git a/config/locales/area_types.fr.yml b/config/locales/area_types.fr.yml index fd4e1e741..bb249c235 100644 --- a/config/locales/area_types.fr.yml +++ b/config/locales/area_types.fr.yml @@ -6,3 +6,9 @@ fr:        zdlp: ZDLp        zdlr: ZDLr        lda: LDA +      gdl: GDL +      deposit: Dépôt +      border: Frontière +      service_area: Aire de service / Pause +      relief: Point de releve +      other: Autre diff --git a/config/locales/stop_areas.fr.yml b/config/locales/stop_areas.fr.yml index 0095bbe6d..283000960 100644 --- a/config/locales/stop_areas.fr.yml +++ b/config/locales/stop_areas.fr.yml @@ -5,6 +5,7 @@ fr:      errors:        empty: Aucun stop_area_id        parent_area_type: ne peut être de type %{area_type} +      incorrect_kind_area_type: Ce type d'arrêt est invalide pour cette catégorie      default_geometry_success: "%{count} arrêts édités"      stop_area:        no_position: "Pas de position" @@ -97,6 +98,7 @@ fr:      attributes:        stop_area:          name: "Nom" +        kind: "Catégorie"          registration_number: "Numéro d'enregistrement"          published_name: "Nom public"          deleted: "Supprimé" diff --git a/config/webpack/environment.js b/config/webpack/environment.js index e7c879fb9..688bcbe8e 100644 --- a/config/webpack/environment.js +++ b/config/webpack/environment.js @@ -1,4 +1,5 @@  const { environment } = require('@rails/webpacker') +const coffee =  require('./loaders/coffee')  const CleanWebpackPlugin = require('clean-webpack-plugin')  let pathsToClean = [ @@ -24,4 +25,5 @@ environment.plugins.set(  //   jquery: "jquery/src/jquery",  // } +environment.loaders.append('coffee', coffee)  module.exports = environment diff --git a/config/webpack/loaders/coffee.js b/config/webpack/loaders/coffee.js new file mode 100644 index 000000000..4666716dc --- /dev/null +++ b/config/webpack/loaders/coffee.js @@ -0,0 +1,6 @@ +module.exports = { +  test: /\.coffee(\.erb)?$/, +  use: [{ +    loader: 'coffee-loader' +  }] +} diff --git a/db/migrate/20180126134944_add_kind_to_stop_areas.rb b/db/migrate/20180126134944_add_kind_to_stop_areas.rb new file mode 100644 index 000000000..08f54a6c5 --- /dev/null +++ b/db/migrate/20180126134944_add_kind_to_stop_areas.rb @@ -0,0 +1,6 @@ +class AddKindToStopAreas < ActiveRecord::Migration +  def change +    add_column :stop_areas, :kind, :string +    Chouette::StopArea.where.not(kind: :non_commercial).update_all kind: :commercial +  end +end diff --git a/db/schema.rb b/db/schema.rb index 2c5520110..b2063539b 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,13 +11,14 @@  #  # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20180111200406) do +ActiveRecord::Schema.define(version: 20180126134944) do    # These are extensions that must be enabled in order to support this database    enable_extension "plpgsql" -  enable_extension "postgis"    enable_extension "hstore" +  enable_extension "postgis"    enable_extension "unaccent" +  enable_extension "objectid"    create_table "access_links", id: :bigserial, force: :cascade do |t|      t.integer  "access_point_id",                        limit: 8 @@ -90,6 +91,8 @@ ActiveRecord::Schema.define(version: 20180111200406) do      t.integer   "organisation_id", limit: 8      t.datetime  "created_at"      t.datetime  "updated_at" +    t.integer   "int_day_types" +    t.date      "excluded_dates",                            array: true    end    add_index "calendars", ["organisation_id"], name: "index_calendars_on_organisation_id", using: :btree @@ -115,6 +118,7 @@ ActiveRecord::Schema.define(version: 20180111200406) do      t.datetime "updated_at"      t.date     "end_date"      t.string   "date_type" +    t.string   "mode"    end    add_index "clean_ups", ["referential_id"], name: "index_clean_ups_on_referential_id", using: :btree @@ -786,6 +790,7 @@ ActiveRecord::Schema.define(version: 20180111200406) do      t.datetime "updated_at"      t.string   "stif_type"      t.integer  "waiting_time" +    t.string   "kind"    end    add_index "stop_areas", ["name"], name: "index_stop_areas_on_name", using: :btree diff --git a/package.json b/package.json index 80ca22f83..802a2eef7 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,8 @@      "babel-preset-react": "6.24.1",      "babelify": "8.0.0",      "bootstrap": "3", -    "coffeescript": "2.1.0", +    "coffee-loader": "^0.9.0", +    "coffeescript": "1.12.7",      "jquery": "3.2.1",      "lodash": "4.17.4",      "promise-polyfill": "7.0.0", diff --git a/spec/javascript/vehicle_journeys/actions_spec.js b/spec/javascript/vehicle_journeys/actions_spec.js index 9515b57f2..d486c9af8 100644 --- a/spec/javascript/vehicle_journeys/actions_spec.js +++ b/spec/javascript/vehicle_journeys/actions_spec.js @@ -37,6 +37,47 @@ describe('when clicking on add button', () => {      expect(actions.openCreateModal()).toEqual(expectedAction)    })  }) +describe('when validating the form', () => { +  it('should check that non-commercial stops have passing time', () => { +    let state = [{ +      vehicle_journey_at_stops: [{ +        area_kind: "non_commercial", +        departure_time: { +          hour: "00", +          minute: "00" +        } +      }] +    }] + +    expect(actions.validate(dispatch, state)).toEqual(false) + +    state = [{ +      vehicle_journey_at_stops: [{ +        area_kind: "non_commercial", +        departure_time: { +          hour: "00", +          minute: "01" +        } +      }] +    }] + +    expect(actions.validate(dispatch, state)).toEqual(true) +  }) + +  it('should not check that commercial stops', () => { +    let state = [{ +      vehicle_journey_at_stops: [{ +        area_kind: "commercial", +        departure_time: { +          hour: "00", +          minute: "00" +        } +      }] +    }] + +    expect(actions.validate(dispatch, state)).toEqual(true) +  }) +})  describe('when using select2 to pick a journey pattern', () => {    it('should create an action to select a journey pattern inside modal', () => {      let selectedJP = { diff --git a/spec/models/chouette/area_type_spec.rb b/spec/models/chouette/area_type_spec.rb index 67d218df8..28325dd0a 100644 --- a/spec/models/chouette/area_type_spec.rb +++ b/spec/models/chouette/area_type_spec.rb @@ -4,7 +4,9 @@ RSpec.describe Chouette::AreaType do    describe "::ALL" do      it "includes all supported types" do -      expect(Chouette::AreaType::ALL).to match_array( %i(zdep zder zdlp zdlr lda gdl) ) +      expect(Chouette::AreaType::ALL).to match_array( %i(zdep zder zdlp zdlr lda gdl deposit border service_area relief other) ) +      expect(Chouette::AreaType::COMMERCIAL).to match_array( %i(zdep zder zdlp zdlr lda gdl) ) +      expect(Chouette::AreaType::NON_COMMERCIAL).to match_array( %i( deposit border service_area relief other) )      end    end diff --git a/spec/models/chouette/stop_area_spec.rb b/spec/models/chouette/stop_area_spec.rb index a90e5d816..32ee5a3a6 100644 --- a/spec/models/chouette/stop_area_spec.rb +++ b/spec/models/chouette/stop_area_spec.rb @@ -10,10 +10,20 @@ describe Chouette::StopArea, :type => :model do    it { should belong_to(:stop_area_referential) }    it { should validate_presence_of :name } +  it { should validate_presence_of :kind }    it { should validate_numericality_of :latitude }    it { should validate_numericality_of :longitude }    it { is_expected.to be_versioned } +  describe "#area_type" do +    it "should validate the value is correct regarding to the kind" do +      expect(build(:stop_area, kind: :commercial, area_type: :gdl)).to be_valid +      expect(build(:stop_area, kind: :non_commercial, area_type: :relief)).to be_valid +      expect(build(:stop_area, kind: :commercial, area_type: :relief)).to_not be_valid +      expect(build(:stop_area, kind: :non_commercial, area_type: :gdl)).to_not be_valid +    end +  end +    # describe ".latitude" do    #   it "should accept -90 value" do    #     subject = create :stop_area, :area_type => "BoardingPosition" @@ -1404,13 +1404,19 @@ code-point-at@^1.0.0:    version "1.1.0"    resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" +coffee-loader@^0.9.0: +  version "0.9.0" +  resolved "https://registry.yarnpkg.com/coffee-loader/-/coffee-loader-0.9.0.tgz#6deabd336062ddc6d773da4dfd16367fc7107bd6" +  dependencies: +    loader-utils "^1.0.2" +  coffee-script@~1.10.0:    version "1.10.0"    resolved "https://registry.yarnpkg.com/coffee-script/-/coffee-script-1.10.0.tgz#12938bcf9be1948fa006f92e0c4c9e81705108c0" -coffeescript@2.1.0: -  version "2.1.0" -  resolved "https://registry.yarnpkg.com/coffeescript/-/coffeescript-2.1.0.tgz#8cb7ce12021ab9f84d8c524f54edbd6141374606" +coffeescript@1.12.7: +  version "1.12.7" +  resolved "https://registry.yarnpkg.com/coffeescript/-/coffeescript-1.12.7.tgz#e57ee4c4867cf7f606bfc4a0f2d550c0981ddd27"  color-convert@^1.3.0, color-convert@^1.8.2, color-convert@^1.9.0:    version "1.9.0" | 
