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" |
