diff options
Diffstat (limited to 'app/javascript')
69 files changed, 704 insertions, 354 deletions
diff --git a/app/javascript/vehicle_journeys/components/tools/CustomFieldsInputs.js b/app/javascript/helpers/CustomFieldsInputs.js index 90d72a801..abc8097d5 100644 --- a/app/javascript/vehicle_journeys/components/tools/CustomFieldsInputs.js +++ b/app/javascript/helpers/CustomFieldsInputs.js @@ -27,6 +27,32 @@ export default class CustomFieldsInputs extends Component { ) } + stringInput(cf){ + return( + <input + type='text' + ref={'custom_fields.' + cf.code} + className='form-control' + disabled={this.props.disabled} + value={cf.value} + onChange={(e) => {this.props.onUpdate(cf.code, e.target.value); this.forceUpdate()} } + /> + ) + } + + integerInput(cf){ + return( + <input + type='number' + ref={'custom_fields.' + cf.code} + className='form-control' + disabled={this.props.disabled} + value={cf.value} + onChange={(e) => {this.props.onUpdate(cf.code, e.target.value); this.forceUpdate()} } + /> + ) + } + render() { return ( <div> diff --git a/app/javascript/helpers/polyfills.js b/app/javascript/helpers/polyfills.js new file mode 100644 index 000000000..93e3e9846 --- /dev/null +++ b/app/javascript/helpers/polyfills.js @@ -0,0 +1,4 @@ +import 'promise-polyfill/src/polyfill' +import 'es6-symbol/implement' +import 'polyfill-array-includes' +import 'whatwg-fetch' diff --git a/app/javascript/helpers/routes_map.coffee b/app/javascript/helpers/routes_map.coffee index 6834406fc..de6196372 100644 --- a/app/javascript/helpers/routes_map.coffee +++ b/app/javascript/helpers/routes_map.coffee @@ -1,3 +1,65 @@ +RoutesLayersButton = (options) -> + menu = options.menu + + toggleMenu = (e)=> + $(menu.element).toggleClass 'hidden' + button.innerHTML = if button.innerHTML == "+" then "-" else "+" + + button = document.createElement("button") + button.innerHTML = "+" + button.addEventListener('click', toggleMenu, false) + button.addEventListener('touchstart', toggleMenu, false) + button.className = "ol-routes-layers-button" + + element = document.createElement('div'); + element.className = 'ol-control ol-routes-layers-button-wrapper'; + + element.appendChild(button) + + ol.control.Control.call(this, { + element + target: options.target + }) + +ol.inherits RoutesLayersButton, ol.control.Control + +RoutesLayersControl = (routes, routes_map) -> + + element = document.createElement('div') + element.className = 'ol-unselectable ol-routes-layers hidden' + Object.keys(routes).forEach (id)=> + route = routes[id] + route.active = false + label = document.createElement('a') + label.title = route.name + label.className = '' + label.innerHTML = route.name + element.appendChild label + label.addEventListener "click", => + route.active = !route.active + $(label).toggleClass "active" + route.active + route.vectorPtsLayer.setStyle routes_map.defaultStyles(route.active) + route.vectorEdgesLayer.setStyle routes_map.edgeStyles(route.active) + route.vectorLnsLayer.setStyle routes_map.lineStyle(route.active) + routes_map.fitZoom() + label.addEventListener "mouseenter", => + route.vectorPtsLayer.setStyle routes_map.defaultStyles(true) + route.vectorEdgesLayer.setStyle routes_map.edgeStyles(true) + route.vectorLnsLayer.setStyle routes_map.lineStyle(true) + + label.addEventListener "mouseleave", => + route.vectorPtsLayer.setStyle routes_map.defaultStyles(route.active) + route.vectorEdgesLayer.setStyle routes_map.edgeStyles(route.active) + route.vectorLnsLayer.setStyle routes_map.lineStyle(route.active) + + + ol.control.Control.call(this, { + element + }) + +ol.inherits RoutesLayersControl, ol.control.Control + class RoutesMap constructor: (@target)-> @initMap() @@ -20,6 +82,7 @@ class RoutesMap addRoute: (route)-> geoColPts = [] geoColLns = [] + route.active = true @routes[route.id] = route if route.id stops = route.stops || route geoColEdges = [ @@ -77,68 +140,49 @@ class RoutesMap @map.addLayer vectorEdgesLayer @map.addLayer vectorLnsLayer - lineStyle: (highlighted=false)-> + lineStyle: (active=false)-> new ol.style.Style stroke: new ol.style.Stroke - color: if highlighted then "#ed7f00" else '#007fbb' - width: 3 + color: '#007fbb' + width: if active then 3 else 0 - edgeStyles: (highlighted=false)-> + edgeStyles: (active=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 + color: '#007fbb' + width: if active then 3 else 0 fill: new ol.style.Fill - color: if highlighted then "#ed7f00" else '#007fbb' - width: 2 + color: '#007fbb' + width: if active then 3 else 0 - defaultStyles: (highlighted=false)-> + defaultStyles: (active=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 + color: '#007fbb' + width: if active then 3 else 0 fill: new ol.style.Fill color: '#ffffff' - width: 2 + width: if active then 3 else 0 addRoutesLabels: -> - labelsContainer = $("<ul class='routes-labels'></ul>") - labelsContainer.appendTo $("##{@target}") - @vectorPtsLayer = null - @vectorEdgesLayer = null - @vectorLnsLayer = null + menu = new RoutesLayersControl(@routes, this) + @map.addControl menu + @map.addControl new RoutesLayersButton(menu: menu) + + fitZoom: ()-> + area = [] + found = false 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 + if route.active + found = true + route.stops.forEach (stop, i) => + area.push [parseFloat(stop.longitude), parseFloat(stop.latitude)] + area = @area unless found boundaries = ol.extent.applyTransform( ol.extent.boundingExtent(area), ol.proj.getTransform('EPSG:4326', 'EPSG:3857') ) diff --git a/app/javascript/helpers/save_button.js b/app/javascript/helpers/save_button.js index 7e0bd5bbe..af5ee54a8 100644 --- a/app/javascript/helpers/save_button.js +++ b/app/javascript/helpers/save_button.js @@ -35,7 +35,7 @@ export default class SaveButton extends Component{ this.props.editMode ? this.submitForm() : this.props.onEnterEditMode() }} > - {this.props.editMode ? "Valider" : "Editer"} + {this.props.editMode ? I18n.t('actions.submit') : I18n.t('actions.edit')} </button> </div> </form> diff --git a/app/javascript/journey_patterns/actions/index.js b/app/javascript/journey_patterns/actions/index.js index a70a2e6f2..a813f4b9e 100644 --- a/app/javascript/journey_patterns/actions/index.js +++ b/app/javascript/journey_patterns/actions/index.js @@ -1,10 +1,3 @@ -import Promise from 'promise-polyfill' - -// To add to window -if (!window.Promise) { - window.Promise = Promise; -} - const actions = { enterEditMode: () => ({ type: "ENTER_EDIT_MODE" @@ -20,6 +13,12 @@ const actions = { type: "RECEIVE_ERRORS", json }), + receiveRouteCosts: (costs, key, index) => ({ + type: "RECEIVE_ROUTE_COSTS", + costs, + key, + index + }), unavailableServer : () => ({ type: 'UNAVAILABLE_SERVER' }), @@ -37,7 +36,7 @@ const actions = { }), updateCheckboxValue : (e, index) => ({ type : 'UPDATE_CHECKBOX_VALUE', - id : e.currentTarget.id, + position : e.currentTarget.id, index }), checkConfirmModal : (event, callback, stateChanged,dispatch) => { @@ -189,15 +188,19 @@ const actions = { dispatch(actions.unavailableServer()) } else { if(json.length != 0){ - let val - for (val of json){ - for (let stop_point of val.route_short_description.stop_points){ + let j = 0 + while(j < json.length){ + let val = json[j] + let i = 0 + while(i < val.route_short_description.stop_points.length){ + let stop_point = val.route_short_description.stop_points[i] stop_point.checked = false val.stop_area_short_descriptions.map((element) => { if(element.stop_area_short_description.id === stop_point.id){ stop_point.checked = true } }) + i ++ } journeyPatterns.push( _.assign({}, val, { @@ -206,6 +209,7 @@ const actions = { deletable: false }) ) + j ++ } } window.currentItemsLength = journeyPatterns.length @@ -213,6 +217,46 @@ const actions = { } }) }, + fetchRouteCosts: (dispatch, key, index) => { + if(actions.fetchingRouteCosts){ + // A request is already sent, wait for it + if(!actions.waitingForRouteCosts){ + actions.waitingForRouteCosts = [] + } + actions.waitingForRouteCosts.push([key, index]) + return + } + + if(actions.routeCostsCache) { + // Dispatch asynchronously to prevent warning when + // this executes during `render()` + requestAnimationFrame(() => { + dispatch(actions.receiveRouteCosts( + actions.routeCostsCache, + key, + index + )) + }) + } else { + actions.fetchingRouteCosts = true + fetch(window.routeCostsUrl, { + credentials: 'same-origin', + }).then(response => { + return response.json() + }).then(json => { + let costs = json.costs ? json.costs : {} + actions.routeCostsCache = costs + + dispatch(actions.receiveRouteCosts(costs, key, index)) + if(actions.waitingForRouteCosts){ + actions.waitingForRouteCosts.map(function(item, i) { + dispatch(actions.receiveRouteCosts(costs, item[0], item[1])) + }) + } + actions.fetchingRouteCosts = false + }) + } + }, getChecked : (jp) => { return jp.filter((obj) => { return obj.checked diff --git a/app/javascript/journey_patterns/components/ConfirmModal.js b/app/javascript/journey_patterns/components/ConfirmModal.js index ccd0a9384..fdf32649f 100644 --- a/app/javascript/journey_patterns/components/ConfirmModal.js +++ b/app/javascript/journey_patterns/components/ConfirmModal.js @@ -9,11 +9,11 @@ export default function ConfirmModal({dispatch, modal, onModalAccept, onModalCan <div className='modal-dialog'> <div className='modal-content'> <div className='modal-header'> - <h4 className='modal-title'>Confirmation</h4> + <h4 className='modal-title'>{I18n.t('journey_patterns.show.confirmation')}</h4> </div> <div className='modal-body'> <div className='mt-md mb-md'> - <p>Vous vous apprêtez à changer de page. Voulez-vous valider vos modifications avant cela ?</p> + <p>{I18n.t('journey_patterns.show.confirm_page_change')}</p> </div> </div> <div className='modal-footer'> @@ -23,7 +23,7 @@ export default function ConfirmModal({dispatch, modal, onModalAccept, onModalCan type='button' onClick={() => { onModalCancel(modal.confirmModal.callback) }} > - Ne pas valider + {I18n.t('cancel')} </button> <button className='btn btn-primary' @@ -31,7 +31,7 @@ export default function ConfirmModal({dispatch, modal, onModalAccept, onModalCan type='button' onClick={() => { onModalAccept(modal.confirmModal.callback, journeyPatterns) }} > - Valider + {I18n.t('actions.submit')} </button> </div> </div> diff --git a/app/javascript/journey_patterns/components/CreateModal.js b/app/javascript/journey_patterns/components/CreateModal.js index a6c1b608a..51f6f6c1b 100644 --- a/app/javascript/journey_patterns/components/CreateModal.js +++ b/app/javascript/journey_patterns/components/CreateModal.js @@ -1,15 +1,17 @@ import React, { Component } from 'react' import PropTypes from 'prop-types' import actions from '../actions' +import CustomFieldsInputs from '../../helpers/CustomFieldsInputs' export default class CreateModal extends Component { constructor(props) { super(props) + this.custom_fields = _.assign({}, this.props.custom_fields) } handleSubmit() { if(actions.validateFields(this.refs) == true) { - this.props.onAddJourneyPattern(this.refs) + this.props.onAddJourneyPattern(_.assign({}, this.refs, {custom_fields: this.custom_fields})) this.props.onModalClose() $('#NewJourneyPatternModal').modal('hide') } @@ -38,14 +40,14 @@ export default class CreateModal extends Component { <div className='modal-dialog'> <div className='modal-content'> <div className='modal-header'> - <h4 className='modal-title'>Ajouter une mission</h4> + <h4 className='modal-title'>{I18n.t('journey_patterns.actions.new')}</h4> </div> {(this.props.modal.type == 'create') && ( <form> <div className='modal-body'> <div className='form-group'> - <label className='control-label is-required'>Nom</label> + <label className='control-label is-required'>{I18n.attribute_name('journey_pattern', 'name')}</label> <input type='text' ref='name' @@ -57,7 +59,7 @@ export default class CreateModal extends Component { <div className='row'> <div className='col-lg-6 col-md-6 col-sm-6 col-xs-6'> <div className='form-group'> - <label className='control-label is-required'>Nom public</label> + <label className='control-label is-required'>{I18n.attribute_name('journey_pattern', 'published_name')}</label> <input type='text' ref='published_name' @@ -69,7 +71,7 @@ export default class CreateModal extends Component { </div> <div className='col-lg-6 col-md-6 col-sm-6 col-xs-6'> <div className='form-group'> - <label className='control-label'>Code mission</label> + <label className='control-label'>{I18n.attribute_name('journey_pattern', 'registration_number')}</label> <input type='text' ref='registration_number' @@ -78,8 +80,14 @@ export default class CreateModal extends Component { /> </div> </div> + <CustomFieldsInputs + values={this.props.custom_fields} + onUpdate={(code, value) => this.custom_fields[code]["value"] = value} + disabled={false} + /> </div> </div> + <div className='modal-footer'> <button className='btn btn-link' @@ -87,14 +95,14 @@ export default class CreateModal extends Component { type='button' onClick={this.props.onModalClose} > - Annuler + {I18n.t('cancel')} </button> <button className='btn btn-primary' type='button' onClick={this.handleSubmit.bind(this)} > - Valider + {I18n.t('actions.submit')} </button> </div> </form> @@ -120,4 +128,4 @@ CreateModal.propTypes = { onOpenCreateModal: PropTypes.func.isRequired, onModalClose: PropTypes.func.isRequired, onAddJourneyPattern: PropTypes.func.isRequired -}
\ No newline at end of file +} diff --git a/app/javascript/journey_patterns/components/EditModal.js b/app/javascript/journey_patterns/components/EditModal.js index c960cb41c..a23a57f96 100644 --- a/app/javascript/journey_patterns/components/EditModal.js +++ b/app/javascript/journey_patterns/components/EditModal.js @@ -1,33 +1,44 @@ import React, { Component } from 'react' import PropTypes from 'prop-types' import actions from '../actions' +import CustomFieldsInputs from '../../helpers/CustomFieldsInputs' export default class EditModal extends Component { constructor(props) { super(props) + this.updateValue = this.updateValue.bind(this) } handleSubmit() { if(actions.validateFields(this.refs) == true) { - this.props.saveModal(this.props.modal.modalProps.index, this.refs) + this.props.saveModal(this.props.modal.modalProps.index, _.assign({}, this.refs, {custom_fields: this.custom_fields})) $('#JourneyPatternModal').modal('hide') } } + updateValue(attribute, e) { + actions.resetValidation(e.currentTarget) + this.props.modal.modalProps.journeyPattern[attribute] = e.target.value + this.forceUpdate() + } + renderModalTitle() { if (this.props.editMode) { return ( <h4 className='modal-title'> - Editer la mission + {I18n.t('journey_patterns.actions.edit')} {this.props.modal.type == 'edit' && <em> "{this.props.modal.modalProps.journeyPattern.name}"</em>} </h4> ) } else { - return <h4 className='modal-title'> Informations </h4> + return <h4 className='modal-title'> {I18n.t('journey_patterns.show.informations')} </h4> } } render() { + if(this.props.modal.modalProps.journeyPattern){ + this.custom_fields = _.assign({}, this.props.modal.modalProps.journeyPattern.custom_fields) + } return ( <div className={ 'modal fade ' + ((this.props.modal.type == 'edit') ? 'in' : '') } id='JourneyPatternModal'> <div className='modal-container'> @@ -41,15 +52,15 @@ export default class EditModal extends Component { <form> <div className='modal-body'> <div className='form-group'> - <label className='control-label is-required'>Nom</label> + <label className='control-label is-required'>{I18n.attribute_name('journey_pattern', 'name')}</label> <input type='text' ref='name' className='form-control' disabled={!this.props.editMode} id={this.props.modal.modalProps.index} - defaultValue={this.props.modal.modalProps.journeyPattern.name} - onKeyDown={(e) => actions.resetValidation(e.currentTarget)} + value={this.props.modal.modalProps.journeyPattern.name} + onChange={(e) => this.updateValue('name', e)} required /> </div> @@ -57,42 +68,49 @@ export default class EditModal extends Component { <div className='row'> <div className='col-lg-6 col-md-6 col-sm-6 col-xs-6'> <div className='form-group'> - <label className='control-label is-required'>Nom public</label> + <label className='control-label is-required'>{I18n.attribute_name('journey_pattern', 'published_name')}</label> <input type='text' ref='published_name' className='form-control' disabled={!this.props.editMode} id={this.props.modal.modalProps.index} - defaultValue={this.props.modal.modalProps.journeyPattern.published_name} - onKeyDown={(e) => actions.resetValidation(e.currentTarget)} + value={this.props.modal.modalProps.journeyPattern.published_name} + onChange={(e) => this.updateValue('published_name', e)} required /> </div> </div> <div className='col-lg-6 col-md-6 col-sm-6 col-xs-6'> <div className='form-group'> - <label className='control-label'>Code mission</label> + <label className='control-label'>{I18n.attribute_name('journey_pattern', 'registration_number')}</label> <input type='text' ref='registration_number' className='form-control' disabled={!this.props.editMode} id={this.props.modal.modalProps.index} - defaultValue={this.props.modal.modalProps.journeyPattern.registration_number} - onKeyDown={(e) => actions.resetValidation(e.currentTarget)} + value={this.props.modal.modalProps.journeyPattern.registration_number} + onChange={(e) => this.updateValue('registration_number', e)} /> </div> </div> </div> + <div className='row'> + <CustomFieldsInputs + values={this.props.modal.modalProps.journeyPattern.custom_fields} + onUpdate={(code, value) => this.custom_fields[code]["value"] = value} + disabled={!this.props.editMode} + /> + </div> <div> - <label className='control-label'>Signature métier</label> + <label className='control-label'>{I18n.attribute_name('journey_pattern', 'checksum')}</label> <input type='text' ref='checksum' className='form-control' disabled='disabled' - defaultValue={this.props.modal.modalProps.journeyPattern.checksum} + value={this.props.modal.modalProps.journeyPattern.checksum} /> </div> </div> @@ -105,14 +123,14 @@ export default class EditModal extends Component { type='button' onClick={this.props.onModalClose} > - Annuler + {I18n.t('cancel')} </button> <button className='btn btn-primary' type='button' onClick={this.handleSubmit.bind(this)} > - Valider + {I18n.t('actions.submit')} </button> </div> } diff --git a/app/javascript/journey_patterns/components/JourneyPattern.js b/app/javascript/journey_patterns/components/JourneyPattern.js index 35765b99a..4eba80030 100644 --- a/app/javascript/journey_patterns/components/JourneyPattern.js +++ b/app/javascript/journey_patterns/components/JourneyPattern.js @@ -23,7 +23,7 @@ export default class JourneyPattern extends Component{ let vjURL = routeURL + '/vehicle_journeys?jp=' + jpOid return ( - <a href={vjURL}>Horaires des courses</a> + <a href={vjURL}>{I18n.t('journey_patterns.journey_pattern.vehicle_journey_at_stops')}</a> ) } @@ -45,7 +45,7 @@ export default class JourneyPattern extends Component{ <input onChange = {(e) => this.props.onCheckboxChange(e)} type='checkbox' - id={sp.id} + id={sp.position} checked={sp.checked} disabled={(this.props.value.deletable || this.props.status.policy['journey_patterns.update'] == false || this.props.editMode == false) ? 'disabled' : ''} > @@ -74,17 +74,21 @@ export default class JourneyPattern extends Component{ return !this.props.status.policy[`journey_patterns.${action}`] } - totals(){ + totals(onlyCommercial=false){ let totalTime = 0 let totalDistance = 0 let from = null this.props.value.stop_points.map((stopPoint, i) =>{ - if(from && stopPoint.checked){ + let usePoint = stopPoint.checked + if(onlyCommercial && (i == 0 || i == this.props.value.stop_points.length - 1) && stopPoint.kind == "non_commercial"){ + usePoint = false + } + if(from && usePoint){ let [costsKey, costs, time, distance] = this.getTimeAndDistanceBetweenStops(from, stopPoint.id) totalTime += time totalDistance += distance } - if(stopPoint.checked){ + if(usePoint){ from = stopPoint.id } }) @@ -93,12 +97,24 @@ export default class JourneyPattern extends Component{ getTimeAndDistanceBetweenStops(from, to){ let costsKey = from + "-" + to - let costs = this.props.value.costs[costsKey] || {distance: 0, time: 0} + let costs = this.getCosts(costsKey) let time = costs['time'] || 0 let distance = costs['distance'] || 0 return [costsKey, costs, time, distance] } + getCosts(costsKey) { + let cost = this.props.value.costs[costsKey] + + if (cost) { + return cost + } + + this.props.fetchRouteCosts(costsKey) + + return { distance: 0, time: 0 } + } + formatDistance(distance){ return parseFloat(Math.round(distance * 100) / 100).toFixed(2) + " km" } @@ -109,25 +125,33 @@ export default class JourneyPattern extends Component{ } else{ let hours = parseInt(time/60) - return hours + " h " + (time - 60*hours) + let minutes = (time - 60*hours) + return hours + " h " + (minutes > 0 ? minutes : '') } } render() { this.previousSpId = undefined - let [totalTime, totalDistance] = this.totals() + let [totalTime, totalDistance] = this.totals(false) + let [commercialTotalTime, commercialTotalDistance] = this.totals(true) return ( <div className={'t2e-item' + (this.props.value.deletable ? ' disabled' : '') + (this.props.value.object_id ? '' : ' to_record') + (this.props.value.errors ? ' has-error': '') + (this.hasFeature('costs_in_journey_patterns') ? ' with-costs' : '')}> <div className='th'> <div className='strong mb-xs'>{this.props.value.object_id ? this.props.value.short_id : '-'}</div> <div>{this.props.value.registration_number}</div> - <div>{actions.getChecked(this.props.value.stop_points).length} arrêt(s)</div> + <div>{I18n.t('journey_patterns.show.stop_points_count', {count: actions.getChecked(this.props.value.stop_points).length})}</div> {this.hasFeature('costs_in_journey_patterns') && <div className="small row totals"> <span className="col-md-6"><i className="fa fa-arrows-h"></i>{totalDistance}</span> <span className="col-md-6"><i className="fa fa-clock-o"></i>{totalTime}</span> </div> } + {this.hasFeature('costs_in_journey_patterns') && + <div className="small row totals commercial"> + <span className="col-md-6"><i className="fa fa-arrows-h"></i>{commercialTotalDistance}</span> + <span className="col-md-6"><i className="fa fa-clock-o"></i>{commercialTotalTime}</span> + </div> + } <div className={this.props.value.deletable ? 'btn-group disabled' : 'btn-group'}> <div className={this.props.value.deletable ? 'btn dropdown-toggle disabled' : 'btn dropdown-toggle'} @@ -143,7 +167,7 @@ export default class JourneyPattern extends Component{ data-toggle='modal' data-target='#JourneyPatternModal' > - {this.props.editMode ? 'Editer' : 'Consulter'} + {this.props.editMode ? I18n.t('actions.edit') : I18n.t('actions.show')} </button> </li> <li className={this.props.value.object_id ? '' : 'disabled'}> @@ -159,7 +183,7 @@ export default class JourneyPattern extends Component{ this.props.onDeleteJourneyPattern(this.props.index)} } > - <span className='fa fa-trash'></span>Supprimer + <span className='fa fa-trash'></span>{I18n.t('actions.destroy')} </button> </li> </ul> @@ -215,5 +239,6 @@ JourneyPattern.propTypes = { onCheckboxChange: PropTypes.func.isRequired, onOpenEditModal: PropTypes.func.isRequired, onDeleteJourneyPattern: PropTypes.func.isRequired, - journeyPatterns: PropTypes.object.isRequired + journeyPatterns: PropTypes.object.isRequired, + fetchRouteCosts: PropTypes.func.isRequired } diff --git a/app/javascript/journey_patterns/components/JourneyPatterns.js b/app/javascript/journey_patterns/components/JourneyPatterns.js index 31727fefc..91c783189 100644 --- a/app/javascript/journey_patterns/components/JourneyPatterns.js +++ b/app/javascript/journey_patterns/components/JourneyPatterns.js @@ -84,14 +84,14 @@ export default class JourneyPatterns extends Component { <div className='col-lg-12'> {(this.props.status.fetchSuccess == false) && ( <div className="alert alert-danger mt-sm"> - <strong>Erreur : </strong> - la récupération des missions a rencontré un problème. Rechargez la page pour tenter de corriger le problème + <strong>{I18n.t('error')} : </strong> + {I18n.t('journeys_patterns.journey_pattern.fetching_error')} </div> )} { _.some(this.props.journeyPatterns, 'errors') && ( <div className="alert alert-danger mt-sm"> - <strong>Erreur : </strong> + <strong> {I18n.t('error')} : </strong> {this.props.journeyPatterns.map((jp, index) => jp.errors && jp.errors.map((err, i) => { return ( @@ -107,9 +107,14 @@ export default class JourneyPatterns extends Component { <div className={'table table-2entries mt-sm mb-sm' + ((this.props.journeyPatterns.length > 0) ? '' : ' no_result')}> <div className='t2e-head w20'> <div className='th'> - <div className='strong mb-xs'>ID Mission</div> - <div>Code mission</div> - <div>Nb arrêts</div> + <div className='strong mb-xs'>{I18n.t('objectid')}</div> + <div>{I18n.attribute_name('journey_pattern', 'registration_number')}</div> + <div>{I18n.attribute_name('journey_pattern', 'stop_points')}</div> + { this.hasFeature('costs_in_journey_patterns') && + <div> + <div>{I18n.attribute_name('journey_pattern', 'full_journey_time')}</div> + <div>{I18n.attribute_name('journey_pattern', 'commercial_journey_time')}</div> + </div> } </div> {this.props.stopPointsList.map((sp, i) =>{ return ( @@ -133,6 +138,7 @@ export default class JourneyPatterns extends Component { status= {this.props.status} editMode= {this.props.editMode} journeyPatterns= {this} + fetchRouteCosts={(costsKey) => this.props.fetchRouteCosts(costsKey, index)} /> )} </div> @@ -151,5 +157,6 @@ JourneyPatterns.propTypes = { status: PropTypes.object.isRequired, onCheckboxChange: PropTypes.func.isRequired, onLoadFirstPage: PropTypes.func.isRequired, - onOpenEditModal: PropTypes.func.isRequired + onOpenEditModal: PropTypes.func.isRequired, + fetchRouteCosts: PropTypes.func.isRequired } diff --git a/app/javascript/journey_patterns/components/Navigate.js b/app/javascript/journey_patterns/components/Navigate.js index 78f324a7d..9e454da5e 100644 --- a/app/javascript/journey_patterns/components/Navigate.js +++ b/app/javascript/journey_patterns/components/Navigate.js @@ -1,5 +1,6 @@ import React, { Component } from 'react' import PropTypes from 'prop-types' +import capitalize from 'lodash/capitalize' import actions from '../actions' export default function Navigate({ dispatch, journeyPatterns, pagination, status }) { @@ -17,7 +18,7 @@ export default function Navigate({ dispatch, journeyPatterns, pagination, status <div className='row'> <div className='col-lg-12 text-right'> <div className='pagination'> - Liste des missions {firstItemOnPage} à {(lastItemOnPage < pagination.totalCount) ? lastItemOnPage : pagination.totalCount} sur {pagination.totalCount} + {I18n.t('will_paginate.page_entries_info.multi_page', { model: capitalize(I18n.model_name('journey_pattern', { plural: true })), from: firstItemOnPage, to: lastItemOnPage, count: pagination.totalCount})} <form className='page_links' onSubmit={e => { e.preventDefault() }}> diff --git a/app/javascript/journey_patterns/containers/AddJourneyPattern.js b/app/javascript/journey_patterns/containers/AddJourneyPattern.js index b093fd111..9e85afd5e 100644 --- a/app/javascript/journey_patterns/containers/AddJourneyPattern.js +++ b/app/javascript/journey_patterns/containers/AddJourneyPattern.js @@ -7,7 +7,8 @@ const mapStateToProps = (state) => { modal: state.modal, journeyPatterns: state.journeyPatterns, editMode: state.editMode, - status: state.status + status: state.status, + custom_fields: state.custom_fields } } diff --git a/app/javascript/journey_patterns/containers/JourneyPatternList.js b/app/javascript/journey_patterns/containers/JourneyPatternList.js index d338345f2..539b6b54c 100644 --- a/app/javascript/journey_patterns/containers/JourneyPatternList.js +++ b/app/javascript/journey_patterns/containers/JourneyPatternList.js @@ -29,6 +29,9 @@ const mapDispatchToProps = (dispatch) => { onUpdateJourneyPatternCosts: (index, costs) =>{ dispatch(actions.updateJourneyPatternCosts(index, costs)) }, + fetchRouteCosts: (key, index) => { + actions.fetchRouteCosts(dispatch, key, index) + }, } } diff --git a/app/javascript/journey_patterns/containers/Modal.js b/app/javascript/journey_patterns/containers/Modal.js index 33ee8583c..fc04843e4 100644 --- a/app/javascript/journey_patterns/containers/Modal.js +++ b/app/javascript/journey_patterns/containers/Modal.js @@ -7,7 +7,8 @@ const mapStateToProps = (state) => { return { editMode: state.editMode, modal: state.modal, - journeyPattern: state.journeyPattern + journeyPattern: state.journeyPattern, + custom_fields: state.custom_fields, } } diff --git a/app/javascript/journey_patterns/reducers/index.js b/app/javascript/journey_patterns/reducers/index.js index 2ffaf86d4..d3a1d29a2 100644 --- a/app/javascript/journey_patterns/reducers/index.js +++ b/app/javascript/journey_patterns/reducers/index.js @@ -12,7 +12,8 @@ const journeyPatternsApp = combineReducers({ journeyPatterns, pagination, stopPointsList, - modal + modal, + custom_fields: (state = [], action) => state }) export default journeyPatternsApp diff --git a/app/javascript/journey_patterns/reducers/journeyPatterns.js b/app/javascript/journey_patterns/reducers/journeyPatterns.js index 1ce069522..1a6a27da6 100644 --- a/app/javascript/journey_patterns/reducers/journeyPatterns.js +++ b/app/javascript/journey_patterns/reducers/journeyPatterns.js @@ -22,7 +22,7 @@ const journeyPattern = (state = {}, action) =>{ } case 'UPDATE_CHECKBOX_VALUE': var updatedStopPoints = state.stop_points.map((s) => { - if (String(s.id) == action.id) { + if (String(s.position) == action.position) { return _.assign({}, s, {checked : !s.checked}) }else { return s @@ -40,6 +40,17 @@ export default function journeyPatterns (state = [], action) { return [...action.json] case 'RECEIVE_ERRORS': return [...action.json] + case 'RECEIVE_ROUTE_COSTS': + return state.map((j, i) =>{ + if(i == action.index) { + const new_costs = Object.assign({}, j.costs) + new_costs[action.key] = action.costs[action.key] || + {distance: 0, time: 0} + return _.assign({}, j, {costs: new_costs}) + } else { + return j + } + }) case 'GO_TO_PREVIOUS_PAGE': $('#ConfirmModal').modal('hide') if(action.pagination.page > 1){ @@ -92,7 +103,8 @@ export default function journeyPatterns (state = [], action) { return _.assign({}, j, { name: action.data.name.value, published_name: action.data.published_name.value, - registration_number: action.data.registration_number.value + registration_number: action.data.registration_number.value, + custom_fields: action.data.custom_fields, }) } else { return j diff --git a/app/javascript/packs/calendars/edit.js b/app/javascript/packs/calendars/edit.js index bd09657ec..0c46313b9 100644 --- a/app/javascript/packs/calendars/edit.js +++ b/app/javascript/packs/calendars/edit.js @@ -1,3 +1,5 @@ +import '../../helpers/polyfills' + import React from 'react' import { render } from 'react-dom' import { Provider } from 'react-redux' diff --git a/app/javascript/packs/exports/new.js b/app/javascript/packs/exports/new.js new file mode 100644 index 000000000..b113cc709 --- /dev/null +++ b/app/javascript/packs/exports/new.js @@ -0,0 +1,5 @@ +import '../../helpers/polyfills' + +import MasterSlave from "../../helpers/master_slave" + +new MasterSlave("form") diff --git a/app/javascript/packs/journey_patterns/index.js b/app/javascript/packs/journey_patterns/index.js index 367a8830f..bd7df2634 100644 --- a/app/javascript/packs/journey_patterns/index.js +++ b/app/javascript/packs/journey_patterns/index.js @@ -1,3 +1,5 @@ +import '../../helpers/polyfills' + import React from 'react' import { render } from 'react-dom' import { Provider } from 'react-redux' @@ -32,7 +34,8 @@ var initialState = { type: '', modalProps: {}, confirmModal: {} - } + }, + custom_fields: window.custom_fields } // const loggerMiddleware = createLogger() diff --git a/app/javascript/packs/referential_lines/show.js b/app/javascript/packs/referential_lines/show.js index 542188018..523f2040f 100644 --- a/app/javascript/packs/referential_lines/show.js +++ b/app/javascript/packs/referential_lines/show.js @@ -1,3 +1,5 @@ +import '../../helpers/polyfills' + import clone from '../../helpers/clone' import RoutesMap from '../../helpers/routes_map' @@ -6,5 +8,5 @@ routes = JSON.parse(decodeURIComponent(routes)) var map = new RoutesMap('routes_map') map.addRoutes(routes) -// map.addRoutesLabels() +map.addRoutesLabels() map.fitZoom() diff --git a/app/javascript/packs/referential_overview/overview.js b/app/javascript/packs/referential_overview/overview.js index 59c326e9a..03da12ef3 100644 --- a/app/javascript/packs/referential_overview/overview.js +++ b/app/javascript/packs/referential_overview/overview.js @@ -1 +1,3 @@ +import '../../helpers/polyfills' + import ReferentialOverview from '../../referential_overview' diff --git a/app/javascript/packs/routes/edit.js b/app/javascript/packs/routes/edit.js index b787bec97..0512b7aff 100644 --- a/app/javascript/packs/routes/edit.js +++ b/app/javascript/packs/routes/edit.js @@ -1,3 +1,5 @@ +import '../../helpers/polyfills' + import React from 'react' import PropTypes from 'prop-types' @@ -29,6 +31,7 @@ const getInitialState = () => { state.push({ stoppoint_id: v.stoppoint_id, stoparea_id: v.stoparea_id, + stoparea_kind: v.stoparea_kind, user_objectid: v.user_objectid, short_name: v.short_name ? v.short_name.replace("'", "\'") : '', area_type: v.area_type, @@ -39,8 +42,8 @@ const getInitialState = () => { name: v.name ? v.name.replace("'", "\'") : '', registration_number: v.registration_number, text: fancyText, - for_boarding: v.for_boarding || "normal", - for_alighting: v.for_alighting || "normal", + for_boarding: v.for_boarding, + for_alighting: v.for_alighting, longitude: v.longitude || 0, latitude: v.latitude || 0, comment: v.comment ? v.comment.replace("'", "\'") : '', @@ -55,12 +58,25 @@ const getInitialState = () => { } var initialState = { stopPoints: getInitialState() } -const loggerMiddleware = createLogger() -let store = createStore( - reducers, - initialState, - applyMiddleware(thunkMiddleware, promise, loggerMiddleware) -) +let store = null + +if(Object.assign){ + const loggerMiddleware = createLogger() + store = createStore( + reducers, + initialState, + applyMiddleware(thunkMiddleware, promise, loggerMiddleware) + ) +} +else{ + // IE + store = createStore( + reducers, + initialState, + applyMiddleware(thunkMiddleware, promise) + ) +} + render( <Provider store={store}> diff --git a/app/javascript/packs/routes/show.js b/app/javascript/packs/routes/show.js index c20de0800..e8e068ddd 100644 --- a/app/javascript/packs/routes/show.js +++ b/app/javascript/packs/routes/show.js @@ -1,3 +1,5 @@ +import '../../helpers/polyfills' + import clone from '../../helpers/clone' import RoutesMap from '../../helpers/routes_map' diff --git a/app/javascript/packs/stop_areas/new.js b/app/javascript/packs/stop_areas/new.js index ffe702cdb..b113cc709 100644 --- a/app/javascript/packs/stop_areas/new.js +++ b/app/javascript/packs/stop_areas/new.js @@ -1,3 +1,5 @@ +import '../../helpers/polyfills' + import MasterSlave from "../../helpers/master_slave" new MasterSlave("form") diff --git a/app/javascript/packs/time_tables/edit.js b/app/javascript/packs/time_tables/edit.js index cf058d501..873bdea50 100644 --- a/app/javascript/packs/time_tables/edit.js +++ b/app/javascript/packs/time_tables/edit.js @@ -1,3 +1,5 @@ +import '../../helpers/polyfills' + import React from 'react' import { render } from 'react-dom' import { Provider } from 'react-redux' diff --git a/app/javascript/packs/vehicle_journeys/index.js b/app/javascript/packs/vehicle_journeys/index.js index e6867cb17..0b4351d26 100644 --- a/app/javascript/packs/vehicle_journeys/index.js +++ b/app/javascript/packs/vehicle_journeys/index.js @@ -1,3 +1,5 @@ +import '../../helpers/polyfills' + import React from 'react' import { render } from 'react-dom' import { Provider } from 'react-redux' @@ -9,7 +11,7 @@ import { enableBatching } from '../../vehicle_journeys/batch' // logger, DO NOT REMOVE // var applyMiddleware = require('redux').applyMiddleware -// var createLogger = require('redux-logger') +// import { createLogger } from 'redux-logger'; // var thunkMiddleware = require('redux-thunk').default // var promise = require('redux-promise') diff --git a/app/javascript/routes/components/App.js b/app/javascript/routes/components/App.js index 26e69bf53..6f8cdf749 100644 --- a/app/javascript/routes/components/App.js +++ b/app/javascript/routes/components/App.js @@ -3,14 +3,9 @@ import PropTypes from 'prop-types' import AddStopPoint from '../containers/AddStopPoint' import VisibleStopPoints from'../containers/VisibleStopPoints' import clone from '../../helpers/clone' -const I18n = clone(window , "I18n", true) export default class App extends Component { - getChildContext() { - return { I18n } - } - render() { return ( <div> @@ -20,7 +15,3 @@ export default class App extends Component { ) } } - -App.childContextTypes = { - I18n: PropTypes.object -} diff --git a/app/javascript/routes/components/BSelect2.js b/app/javascript/routes/components/BSelect2.js index 035bce155..90f288944 100644 --- a/app/javascript/routes/components/BSelect2.js +++ b/app/javascript/routes/components/BSelect2.js @@ -10,13 +10,14 @@ var path = window.location.pathname.split('/', 3).join('/') export default class BSelect3 extends Component { - constructor(props, context) { - super(props, context) + constructor(props) { + super(props) } onChange(e) { this.props.onChange(this.props.index, { text: e.currentTarget.textContent, stoparea_id: e.currentTarget.value, + stoparea_kind: e.params.data.kind, user_objectid: e.params.data.user_objectid, longitude: e.params.data.longitude, latitude: e.params.data.latitude, @@ -85,7 +86,7 @@ class BSelect2 extends Component{ onSelect={ this.props.onSelect } ref='newSelect' options={{ - placeholder: this.context.I18n.t("routes.edit.select2.placeholder"), + placeholder: I18n.t("routes.edit.select2.placeholder"), allowClear: true, language: 'fr', /* Doesn't seem to work... :( */ theme: 'bootstrap', @@ -128,8 +129,4 @@ class BSelect2 extends Component{ /> ) } -} - -BSelect2.contextTypes = { - I18n: PropTypes.object -} +}
\ No newline at end of file diff --git a/app/javascript/routes/components/OlMap.js b/app/javascript/routes/components/OlMap.js index 4beb02872..16fec0e87 100644 --- a/app/javascript/routes/components/OlMap.js +++ b/app/javascript/routes/components/OlMap.js @@ -3,8 +3,8 @@ import React, { Component } from 'react' import PropTypes from 'prop-types' export default class OlMap extends Component{ - constructor(props, context){ - super(props, context) + constructor(props){ + super(props) } fetchApiURL(id){ @@ -115,40 +115,40 @@ export default class OlMap extends Component{ <strong>{this.props.value.olMap.json.name}</strong> </p> <p> - <strong>{this.context.I18n.t('routes.edit.map.stop_point_type')} : </strong> + <strong>{I18n.t('routes.edit.map.stop_point_type')} : </strong> {this.props.value.olMap.json.area_type} </p> <p> - <strong>{this.context.I18n.t('routes.edit.map.short_name')} : </strong> + <strong>{I18n.t('routes.edit.map.short_name')} : </strong> {this.props.value.olMap.json.short_name} </p> <p> - <strong>{this.context.I18n.t('id_reflex')} : </strong> + <strong>{I18n.t('id_reflex')} : </strong> {this.props.value.olMap.json.user_objectid} </p> - <p><strong>{this.context.I18n.t('routes.edit.map.coordinates')} : </strong></p> + <p><strong>{I18n.t('routes.edit.map.coordinates')} : </strong></p> <p style={{paddingLeft: 10, marginTop: 0}}> - <em>{this.context.I18n.t('routes.edit.map.proj')}.: </em>WSG84<br/> - <em>{this.context.I18n.t('routes.edit.map.lat')}.: </em>{this.props.value.olMap.json.latitude} <br/> - <em>{this.context.I18n.t('routes.edit.map.lon')}.: </em>{this.props.value.olMap.json.longitude} + <em>{I18n.t('routes.edit.map.proj')}.: </em>WSG84<br/> + <em>{I18n.t('routes.edit.map.lat')}.: </em>{this.props.value.olMap.json.latitude} <br/> + <em>{I18n.t('routes.edit.map.lon')}.: </em>{this.props.value.olMap.json.longitude} </p> <p> - <strong>{this.context.I18n.t('routes.edit.map.postal_code')} : </strong> + <strong>{I18n.t('routes.edit.map.postal_code')} : </strong> {this.props.value.olMap.json.zip_code} </p> <p> - <strong>{this.context.I18n.t('routes.edit.map.city')} : </strong> + <strong>{I18n.t('routes.edit.map.city')} : </strong> {this.props.value.olMap.json.city_name} </p> <p> - <strong>{this.context.I18n.t('routes.edit.map.comment')} : </strong> + <strong>{I18n.t('routes.edit.map.comment')} : </strong> {this.props.value.olMap.json.comment} </p> {(this.props.value.stoparea_id != this.props.value.olMap.json.stoparea_id) &&( <div className='btn btn-outline-primary btn-sm' onClick= {() => {this.props.onUpdateViaOlMap(this.props.index, this.props.value.olMap.json)}} - >{this.context.I18n.t('actions.select')}</div> + >{I18n.t('actions.select')}</div> )} </div> <div className='map_content'> @@ -164,7 +164,3 @@ export default class OlMap extends Component{ OlMap.propTypes = { } - -OlMap.contextTypes = { - I18n: PropTypes.object -} diff --git a/app/javascript/routes/components/StopPoint.js b/app/javascript/routes/components/StopPoint.js index af51a6bb4..916052b42 100644 --- a/app/javascript/routes/components/StopPoint.js +++ b/app/javascript/routes/components/StopPoint.js @@ -4,7 +4,9 @@ import PropTypes from 'prop-types' import BSelect2 from './BSelect2' import OlMap from './OlMap' -export default function StopPoint(props, {I18n}) { +import { defaultAttribute } from '../actions' + +export default function StopPoint(props) { return ( <div className='nested-fields'> <div className='wrapper'> @@ -40,13 +42,13 @@ export default function StopPoint(props, {I18n}) { <div className={'btn btn-link' + (props.first ? ' disabled' : '')} - onClick={props.onMoveUpClick} + onClick={props.first ? null : props.onMoveUpClick} > <span className='fa fa-arrow-up'></span> </div> <div className={'btn btn-link' + (props.last ? ' disabled' : '')} - onClick={props.onMoveDownClick} + onClick={props.last ? null : props.onMoveDownClick} > <span className='fa fa-arrow-down'></span> </div> @@ -90,7 +92,3 @@ StopPoint.propTypes = { index: PropTypes.number, value: PropTypes.object } - -StopPoint.contextTypes = { - I18n: PropTypes.object -} diff --git a/app/javascript/routes/components/StopPointList.js b/app/javascript/routes/components/StopPointList.js index b227abdea..9bc5e02d1 100644 --- a/app/javascript/routes/components/StopPointList.js +++ b/app/javascript/routes/components/StopPointList.js @@ -3,7 +3,7 @@ import PropTypes from 'prop-types' import StopPoint from './StopPoint' -export default function StopPointList({ stopPoints, onDeleteClick, onMoveUpClick, onMoveDownClick, onChange, onSelectChange, onToggleMap, onToggleEdit, onSelectMarker, onUnselectMarker, onUpdateViaOlMap }, {I18n}) { +export default function StopPointList({ stopPoints, onDeleteClick, onMoveUpClick, onMoveDownClick, onChange, onSelectChange, onToggleMap, onToggleEdit, onSelectMarker, onUnselectMarker, onUpdateViaOlMap }) { return ( <div className='subform'> <div className='nested-head'> diff --git a/app/javascript/routes/containers/AddStopPoint.js b/app/javascript/routes/containers/AddStopPoint.js index fd9227ff3..4b169807d 100644 --- a/app/javascript/routes/containers/AddStopPoint.js +++ b/app/javascript/routes/containers/AddStopPoint.js @@ -11,7 +11,7 @@ let AddStopPoint = ({ dispatch }) => { dispatch(actions.addStop()) }}> <button type="submit" className="btn btn-outline-primary"> - Ajouter un arrêt + {I18n.t('stop_areas.actions.new')} </button> </form> </div> diff --git a/app/javascript/routes/index.js b/app/javascript/routes/index.js index febae7d54..fc99a3086 100644 --- a/app/javascript/routes/index.js +++ b/app/javascript/routes/index.js @@ -22,10 +22,12 @@ const getInitialState = () => { let fancyText = v.name.replace("'", "\'") if(v.zip_code && v.city_name) fancyText += ", " + v.zip_code + " " + v.city_name.replace("'", "\'") + forAlightingAndBoarding = v.stoparea_kind === 'commercial' ? 'normal' : 'forbidden' state.push({ stoppoint_id: v.stoppoint_id, stoparea_id: v.stoparea_id, + stoparea_kind: v.stoparea_kind, user_objectid: v.user_objectid, short_name: v.short_name ? v.short_name.replace("'", "\'") : '', area_type: v.area_type, @@ -36,8 +38,8 @@ const getInitialState = () => { name: v.name ? v.name.replace("'", "\'") : '', registration_number: v.registration_number, text: fancyText, - for_boarding: v.for_boarding || "normal", - for_alighting: v.for_alighting || "normal", + for_boarding: v.for_boarding || forAlightingAndBoarding, + for_alighting: v.for_alighting || forAlightingAndBoarding, longitude: v.longitude || 0, latitude: v.latitude || 0, comment: v.comment ? v.comment.replace("'", "\'") : '', diff --git a/app/javascript/routes/reducers/stopPoints.js b/app/javascript/routes/reducers/stopPoints.js index 0b42b504f..54142338d 100644 --- a/app/javascript/routes/reducers/stopPoints.js +++ b/app/javascript/routes/reducers/stopPoints.js @@ -61,6 +61,7 @@ const stopPoints = (state = [], action) => { case 'UPDATE_INPUT_VALUE': return state.map( (t, i) => { if (i === action.index) { + let forAlightingAndBoarding = action.text.stoparea_kind === 'commercial' ? 'normal' : 'forbidden' return _.assign( {}, t, @@ -68,6 +69,7 @@ const stopPoints = (state = [], action) => { stoppoint_id: t.stoppoint_id, text: action.text.text, stoparea_id: action.text.stoparea_id, + stoparea_kind: action.text.stoparea_kind, user_objectid: action.text.user_objectid, latitude: action.text.latitude, longitude: action.text.longitude, @@ -76,7 +78,9 @@ const stopPoints = (state = [], action) => { area_type: action.text.area_type, city_name: action.text.city_name, comment: action.text.comment, - registration_number: action.text.registration_number + registration_number: action.text.registration_number, + for_alighting: forAlightingAndBoarding, + for_boarding: forAlightingAndBoarding } ) } else { diff --git a/app/javascript/time_tables/actions/index.js b/app/javascript/time_tables/actions/index.js index 98b9eab4b..a5c454a18 100644 --- a/app/javascript/time_tables/actions/index.js +++ b/app/javascript/time_tables/actions/index.js @@ -4,7 +4,6 @@ import reject from 'lodash/reject' import some from 'lodash/some' import every from 'lodash/every' import clone from '../../helpers/clone' -const I18n = clone(window, "I18n") const actions = { weekDays: (index) => { @@ -192,10 +191,13 @@ const actions = { isInPeriod: (periods, date) => { date = new Date(date) - for (let period of periods) { + let i = 0; + while(i < periods.length){ + let period = periods[i] let begin = new Date(period.period_start) let end = new Date(period.period_end) if (date >= begin && date <= end) return true + i++ } return false @@ -236,12 +238,14 @@ const actions = { let error = '' start = new Date(start) end = new Date(end) - - for (let day of in_days) { + let i = 0; + while(i < in_days){ + let day = in_days[i] if (start <= new Date(day.date) && end >= new Date(day.date)) { error = I18n.t('time_tables.edit.error_submit.dates_overlaps') break } + i ++ } return error }, @@ -307,10 +311,11 @@ const actions = { }) }, errorModalKey: (periods, dayTypes) => { - const withoutPeriodsWithDaysTypes = reject(periods, 'deleted').length == 0 && some(dayTypes) && "withoutPeriodsWithDaysTypes" + // const withoutPeriodsWithDaysTypes = reject(periods, 'deleted').length == 0 && some(dayTypes) && "withoutPeriodsWithDaysTypes" const withPeriodsWithoutDayTypes = reject(periods, 'deleted').length > 0 && every(dayTypes, dt => dt == false) && "withPeriodsWithoutDayTypes" - return (withoutPeriodsWithDaysTypes || withPeriodsWithoutDayTypes) && (withoutPeriodsWithDaysTypes ? "withoutPeriodsWithDaysTypes" : "withPeriodsWithoutDayTypes") + // return (withoutPeriodsWithDaysTypes || withPeriodsWithoutDayTypes) && (withoutPeriodsWithDaysTypes ? "withoutPeriodsWithDaysTypes" : "withPeriodsWithoutDayTypes") + return withPeriodsWithoutDayTypes }, errorModalMessage: (errorKey) => { diff --git a/app/javascript/time_tables/components/ConfirmModal.js b/app/javascript/time_tables/components/ConfirmModal.js index 4e8583bc0..e4219348d 100644 --- a/app/javascript/time_tables/components/ConfirmModal.js +++ b/app/javascript/time_tables/components/ConfirmModal.js @@ -2,7 +2,7 @@ import React from 'react' import PropTypes from 'prop-types' -export default function ConfirmModal({dispatch, modal, onModalAccept, onModalCancel, timetable, metas}, {I18n}) { +export default function ConfirmModal({dispatch, modal, onModalAccept, onModalCancel, timetable, metas}) { return ( <div className={'modal fade ' + ((modal.type == 'confirm') ? 'in' : '')} id='ConfirmModal'> <div className='modal-container'> @@ -45,8 +45,4 @@ ConfirmModal.propTypes = { modal: PropTypes.object.isRequired, onModalAccept: PropTypes.func.isRequired, onModalCancel: PropTypes.func.isRequired -} - -ConfirmModal.contextTypes = { - I18n: PropTypes.object -} +}
\ No newline at end of file diff --git a/app/javascript/time_tables/components/ErrorModal.js b/app/javascript/time_tables/components/ErrorModal.js index 8af12f1d1..a512d28fd 100644 --- a/app/javascript/time_tables/components/ErrorModal.js +++ b/app/javascript/time_tables/components/ErrorModal.js @@ -3,7 +3,7 @@ import PropTypes from 'prop-types' import actions from '../actions' -export default function ErrorModal({dispatch, modal, onModalClose}, {I18n}) { +export default function ErrorModal({dispatch, modal, onModalClose}) { return ( <div className={'modal fade ' + ((modal.type == 'error') ? 'in' : '')} id='ErrorModal'> <div className='modal-container'> @@ -37,8 +37,4 @@ export default function ErrorModal({dispatch, modal, onModalClose}, {I18n}) { ErrorModal.propTypes = { modal: PropTypes.object.isRequired, onModalClose: PropTypes.func.isRequired -} - -ErrorModal.contextTypes = { - I18n: PropTypes.object -} +}
\ No newline at end of file diff --git a/app/javascript/time_tables/components/Metas.js b/app/javascript/time_tables/components/Metas.js index 08a6e26fe..186af540a 100644 --- a/app/javascript/time_tables/components/Metas.js +++ b/app/javascript/time_tables/components/Metas.js @@ -4,7 +4,7 @@ import PropTypes from 'prop-types' import actions from '../actions' import TagsSelect2 from './TagsSelect2' -export default function Metas({metas, onUpdateDayTypes, onUpdateComment, onUpdateColor, onSelect2Tags, onUnselect2Tags}, {I18n}) { +export default function Metas({metas, onUpdateDayTypes, onUpdateComment, onUpdateColor, onSelect2Tags, onUnselect2Tags}) { let colorList = ["", "#9B9B9B", "#FFA070", "#C67300", "#7F551B", "#41CCE3", "#09B09C", "#3655D7", "#6321A0", "#E796C6", "#DD2DAA"] return ( <div className='form-horizontal'> @@ -76,7 +76,6 @@ export default function Metas({metas, onUpdateDayTypes, onUpdateComment, onUpdat <label htmlFor="" className="control-label col-sm-4">{I18n.attribute_name('time_table', 'tag_list')}</label> <div className="col-sm-8"> <TagsSelect2 - initialTags={metas.initial_tags} tags={metas.tags} onSelect2Tags={(e) => onSelect2Tags(e)} onUnselect2Tags={(e) => onUnselect2Tags(e)} @@ -134,8 +133,4 @@ Metas.propTypes = { onUpdateColor: PropTypes.func.isRequired, onSelect2Tags: PropTypes.func.isRequired, onUnselect2Tags: PropTypes.func.isRequired -} - -Metas.contextTypes = { - I18n: PropTypes.object -} +}
\ No newline at end of file diff --git a/app/javascript/time_tables/components/PeriodForm.js b/app/javascript/time_tables/components/PeriodForm.js index d17a246f7..36ed6cfdf 100644 --- a/app/javascript/time_tables/components/PeriodForm.js +++ b/app/javascript/time_tables/components/PeriodForm.js @@ -33,7 +33,7 @@ const makeYearsOptions = (yearSelected) => { return arr } -export default function PeriodForm({modal, timetable, metas, onOpenAddPeriodForm, onClosePeriodForm, onUpdatePeriodForm, onValidatePeriodForm}, {I18n}) { +export default function PeriodForm({modal, timetable, metas, onOpenAddPeriodForm, onClosePeriodForm, onUpdatePeriodForm, onValidatePeriodForm}) { return ( <div className="container-fluid"> <div className="row"> @@ -143,8 +143,4 @@ PeriodForm.propTypes = { onUpdatePeriodForm: PropTypes.func.isRequired, onValidatePeriodForm: PropTypes.func.isRequired, timetable: PropTypes.object.isRequired -} - -PeriodForm.contextTypes = { - I18n: PropTypes.object -} +}
\ No newline at end of file diff --git a/app/javascript/time_tables/components/PeriodManager.js b/app/javascript/time_tables/components/PeriodManager.js index 6b817fe73..6871d0b9b 100644 --- a/app/javascript/time_tables/components/PeriodManager.js +++ b/app/javascript/time_tables/components/PeriodManager.js @@ -55,7 +55,7 @@ export default class PeriodManager extends Component { type='button' onClick={() => this.props.onOpenEditPeriodForm(this.props.value, this.props.index)} > - Modifier + {I18n.t('actions.edit')} </button> </li> <li className='delete-action'> @@ -64,7 +64,7 @@ export default class PeriodManager extends Component { onClick={() => this.props.onDeletePeriod(this.props.index, this.props.metas.day_types)} > <span className='fa fa-trash'></span> - Supprimer + {I18n.t('actions.destroy')} </button> </li> </ul> @@ -79,8 +79,4 @@ PeriodManager.propTypes = { currentDate: PropTypes.object.isRequired, onDeletePeriod: PropTypes.func.isRequired, onOpenEditPeriodForm: PropTypes.func.isRequired -} - -PeriodManager.contextTypes = { - I18n: PropTypes.object }
\ No newline at end of file diff --git a/app/javascript/time_tables/components/SaveTimetable.js b/app/javascript/time_tables/components/SaveTimetable.js index 704590abd..468f1773b 100644 --- a/app/javascript/time_tables/components/SaveTimetable.js +++ b/app/javascript/time_tables/components/SaveTimetable.js @@ -26,7 +26,7 @@ export default class SaveTimetable extends Component{ } }} > - Valider + {I18n.t('actions.submit')} </button> </form> </div> diff --git a/app/javascript/time_tables/components/TagsSelect2.js b/app/javascript/time_tables/components/TagsSelect2.js index 43cf59fdf..fe610ed58 100644 --- a/app/javascript/time_tables/components/TagsSelect2.js +++ b/app/javascript/time_tables/components/TagsSelect2.js @@ -27,7 +27,7 @@ export default class TagsSelect2 extends Component { return ( <Select2 value={(this.props.tags.length) ? map(this.props.tags, 'id') : undefined} - data={(this.props.initialTags.length) ? this.mapKeys(this.props.initialTags) : undefined} + data={(this.props.tags.length) ? this.mapKeys(this.props.tags) : undefined} onSelect={(e) => this.props.onSelect2Tags(e)} onUnselect={(e) => setTimeout( () => this.props.onUnselect2Tags(e, 150))} multiple={true} @@ -40,7 +40,7 @@ export default class TagsSelect2 extends Component { allowClear: true, theme: 'bootstrap', width: '100%', - placeholder: this.context.I18n.t('time_tables.edit.select2.tag.placeholder'), + placeholder: I18n.t('time_tables.edit.select2.tag.placeholder'), ajax: { url: origin + path + '/tags.json', dataType: 'json', @@ -75,7 +75,3 @@ export default class TagsSelect2 extends Component { const formatRepo = (props) => { if(props.name) return props.name } - -TagsSelect2.contextTypes = { - I18n: PropTypes.object -}
\ No newline at end of file diff --git a/app/javascript/time_tables/components/Timetable.js b/app/javascript/time_tables/components/Timetable.js index 991f31435..3779fa2d0 100644 --- a/app/javascript/time_tables/components/Timetable.js +++ b/app/javascript/time_tables/components/Timetable.js @@ -31,11 +31,11 @@ export default class Timetable extends Component { <div className="table table-2entries mb-sm"> <div className="t2e-head w20"> <div className="th"> - <div className="strong">{this.context.I18n.t('time_tables.edit.synthesis')}</div> + <div className="strong">{I18n.t('time_tables.edit.synthesis')}</div> </div> - <div className="td"><span>{this.context.I18n.t('time_tables.edit.day_types')}</span></div> - <div className="td"><span>{this.context.I18n.t('time_tables.edit.periods')}</span></div> - <div className="td"><span>{this.context.I18n.t('time_tables.edit.exceptions')}</span></div> + <div className="td"><span>{I18n.t('time_tables.edit.day_types')}</span></div> + <div className="td"><span>{I18n.t('time_tables.edit.periods')}</span></div> + <div className="td"><span>{I18n.t('time_tables.edit.exceptions')}</span></div> </div> <div className="t2e-item-list w80"> <div> @@ -109,8 +109,4 @@ Timetable.propTypes = { onDeletePeriod: PropTypes.func.isRequired, onExcludeDateFromPeriod: PropTypes.func.isRequired, onIncludeDateInPeriod: PropTypes.func.isRequired -} - -Timetable.contextTypes = { - I18n: PropTypes.object -} +}
\ No newline at end of file diff --git a/app/javascript/time_tables/containers/App.js b/app/javascript/time_tables/containers/App.js index 5963f8f1d..c12e33ef5 100644 --- a/app/javascript/time_tables/containers/App.js +++ b/app/javascript/time_tables/containers/App.js @@ -10,7 +10,6 @@ import SaveTimetable from './SaveTimetable' import ConfirmModal from './ConfirmModal' import ErrorModal from './ErrorModal' import clone from '../../helpers/clone' -const I18n = clone(window, "I18n", true) class App extends Component { componentDidMount(){ diff --git a/app/javascript/time_tables/containers/Metas.js b/app/javascript/time_tables/containers/Metas.js index ebccf556e..7bc3ef4e1 100644 --- a/app/javascript/time_tables/containers/Metas.js +++ b/app/javascript/time_tables/containers/Metas.js @@ -24,6 +24,7 @@ const mapDispatchToProps = (dispatch) => { }, onSelect2Tags: (e) => { e.preventDefault() + $(e.target).find('[data-select2-tag]').remove() dispatch(actions.select2Tags(e.params.data)) }, onUnselect2Tags: (e) => { diff --git a/app/javascript/time_tables/reducers/metas.js b/app/javascript/time_tables/reducers/metas.js index 51e1ec149..012f29511 100644 --- a/app/javascript/time_tables/reducers/metas.js +++ b/app/javascript/time_tables/reducers/metas.js @@ -31,11 +31,13 @@ export default function metas(state = {}, action) { return assign({}, state, {color: action.color}) case 'UPDATE_SELECT_TAG': let tags = [...state.tags] - tags.push(action.selectedItem) + if(tags.length == 0 || tags[tags.length-1].name != action.selectedItem.name){ + tags.push(action.selectedItem) + } return assign({}, state, {tags: tags}) case 'UPDATE_UNSELECT_TAG': return assign({}, state, {tags: filter(state.tags, (t) => (t.id != action.selectedItem.id))}) default: return state } -}
\ 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 e67753e4b..70d6e953a 100644 --- a/app/javascript/vehicle_journeys/actions/index.js +++ b/app/javascript/vehicle_journeys/actions/index.js @@ -1,9 +1,3 @@ -import Promise from 'promise-polyfill' - -// To add to window -if (!window.Promise) { - window.Promise = Promise; -} import { batchActions } from '../batch' const actions = { @@ -54,16 +48,10 @@ const actions = { }), selectJPCreateModal : (selectedJP) => ({ type : 'SELECT_JP_CREATE_MODAL', - selectedItem: { - id: selectedJP.id, + selectedItem: _.assign({}, selectedJP, { objectid: selectedJP.object_id, - short_id: selectedJP.short_id, - name: selectedJP.name, - published_name: selectedJP.published_name, - stop_areas: selectedJP.stop_area_short_descriptions, - costs: selectedJP.costs, - full_schedule: selectedJP.full_schedule - } + stop_areas: selectedJP.stop_area_short_descriptions + }) }), openEditModal : (vehicleJourney) => ({ type : 'EDIT_VEHICLEJOURNEY_MODAL', @@ -113,14 +101,9 @@ const actions = { type : 'EDIT_PURCHASE_WINDOWS_VEHICLEJOURNEY_MODAL', vehicleJourneys }), - selectPurchaseWindowsModal: (selectedWindow) =>({ + selectPurchaseWindowsModal: (selectedItem) =>({ type: 'SELECT_PURCHASE_WINDOW_MODAL', - selectedItem:{ - id: selectedWindow.id, - name: selectedWindow.name, - color: selectedWindow.color, - objectid: selectedWindow.objectid - } + selectedItem }), addSelectedPurchaseWindow: () => ({ type: 'ADD_SELECTED_PURCHASE_WINDOW' @@ -203,24 +186,24 @@ const actions = { let field = fields[key] if(field.validity && !field.validity.valid){ valid = false - $(field).parent().addClass('has-error').children('.help-block').remove() + $(field).parent().parent().addClass('has-error').children('.help-block').remove() $(field).parent().append("<span class='small help-block'>" + field.validationMessage + "</span>") } }) - return valid }, toggleArrivals : () => ({ type: 'TOGGLE_ARRIVALS', }), - updateTime : (val, subIndex, index, timeUnit, isDeparture, isArrivalsToggled) => ({ + updateTime : (val, subIndex, index, timeUnit, isDeparture, isArrivalsToggled, enforceConsistency=false) => ({ type: 'UPDATE_TIME', val, subIndex, index, timeUnit, isDeparture, - isArrivalsToggled + isArrivalsToggled, + enforceConsistency }), resetStateFilters: () => ({ type: 'RESET_FILTERS' @@ -344,16 +327,23 @@ const actions = { if(hasError == true) { dispatch(actions.unavailableServer()) } else { - let val - for (val of json.vehicle_journeys){ + let i = 0 + while(i < json.vehicle_journeys.length){ + let val = json.vehicle_journeys[i] + i++ var timeTables = [] var purchaseWindows = [] - let tt - for (tt of val.time_tables){ + let k = 0 + while(k < val.time_tables.length){ + let tt = val.time_tables[k] + k++ timeTables.push(tt) } if(val.purchase_windows){ - for (tt of val.purchase_windows){ + k = 0 + while(k < val.purchase_windows.length){ + let tt = val.purchase_windows[k] + k++ purchaseWindows.push(tt) } } @@ -487,10 +477,10 @@ const actions = { vjas.delta = delta return vjas }, - adjustSchedule: (action, schedule) => { + adjustSchedule: (action, schedule, enforceConsistency=false) => { // we enforce that the departure time remains after the arrival time actions.getDelta(schedule) - if(schedule.delta < 0){ + if(enforceConsistency && schedule.delta < 0){ if(action.isDeparture){ schedule.arrival_time = schedule.departure_time } diff --git a/app/javascript/vehicle_journeys/components/ConfirmModal.js b/app/javascript/vehicle_journeys/components/ConfirmModal.js index 75e8a3932..330b4e02f 100644 --- a/app/javascript/vehicle_journeys/components/ConfirmModal.js +++ b/app/javascript/vehicle_journeys/components/ConfirmModal.js @@ -16,7 +16,7 @@ export default function ConfirmModal({dispatch, modal, onModalAccept, onModalCan type='button' onClick={() => { onModalCancel(modal.confirmModal.callback) }} > - Ne pas valider + {I18n.t('cancel')} </button> <button className='btn btn-danger' @@ -24,7 +24,7 @@ export default function ConfirmModal({dispatch, modal, onModalAccept, onModalCan type='button' onClick={() => { onModalAccept(modal.confirmModal.callback, vehicleJourneys) }} > - Valider + {I18n.t('actions.submit')} </button> </div> </div> diff --git a/app/javascript/vehicle_journeys/components/Filters.js b/app/javascript/vehicle_journeys/components/Filters.js index ae3ab3476..1e43a490e 100644 --- a/app/javascript/vehicle_journeys/components/Filters.js +++ b/app/javascript/vehicle_journeys/components/Filters.js @@ -33,6 +33,7 @@ export default function Filters({filters, pagination, missions, onFilter, onRese {/* Calendriers */} <div className='form-group w33'> <TimetableSelect2 + placeholder={I18n.t('vehicle_journeys.vehicle_journeys_matrix.filters.timetable')} onSelect2Timetable={onSelect2Timetable} hasRoute={true} chunkURL={("/autocomplete_time_tables.json?route_id=" + String(window.route_id))} @@ -145,12 +146,12 @@ export default function Filters({filters, pagination, missions, onFilter, onRese <span className='btn btn-link' onClick={(e) => onResetFilters(e, pagination)}> - Effacer + {I18n.t('actions.erase')} </span> <span className='btn btn-default' onClick={(e) => onFilter(e, pagination)}> - Filtrer + {I18n.t('actions.filter')} </span> </div> </div> diff --git a/app/javascript/vehicle_journeys/components/Navigate.js b/app/javascript/vehicle_journeys/components/Navigate.js index 24843babc..e79823e49 100644 --- a/app/javascript/vehicle_journeys/components/Navigate.js +++ b/app/javascript/vehicle_journeys/components/Navigate.js @@ -1,5 +1,6 @@ import React, { Component } from 'react' import PropTypes from 'prop-types' +import capitalize from 'lodash/capitalize' import actions from'../actions' export default function Navigate({ dispatch, vehicleJourneys, pagination, status, filters}) { @@ -17,7 +18,7 @@ export default function Navigate({ dispatch, vehicleJourneys, pagination, status if(status.fetchSuccess == true) { return ( <div className="pagination"> - {I18n.t("vehicle_journeys.vehicle_journeys_matrix.pagination", {minVJ, maxVJ, total:pagination.totalCount})} + {I18n.t('will_paginate.page_entries_info.multi_page', { model: capitalize(I18n.model_name('vehicle_journey', { plural: true })), from: minVJ, to: maxVJ, count: pagination.totalCount })} <form className='page_links' onSubmit={e => {e.preventDefault()}}> <button onClick={e => { diff --git a/app/javascript/vehicle_journeys/components/VehicleJourney.js b/app/javascript/vehicle_journeys/components/VehicleJourney.js index 7db0cee1c..5eb73de0e 100644 --- a/app/javascript/vehicle_journeys/components/VehicleJourney.js +++ b/app/javascript/vehicle_journeys/components/VehicleJourney.js @@ -10,6 +10,10 @@ export default class VehicleJourney extends Component { this.previousCity = undefined } + journey_length() { + return this.props.value.journey_pattern.journey_length + "km" + } + cityNameChecker(sp) { return this.props.vehicleJourneys.showHeader(sp.stop_point_objectid) } @@ -48,6 +52,14 @@ export default class VehicleJourney extends Component { } } + displayDelta(delta) { + if(delta > 99){ + return "+" + } + return delta + } + + hasTimeTable(time_tables, tt) { let found = false time_tables.map((t, index) => { @@ -59,14 +71,35 @@ export default class VehicleJourney extends Component { return found } + hasPurchaseWindow(purchase_windows, window) { + return this.hasTimeTable(purchase_windows, window) + } + isDisabled(bool1, bool2) { return (bool1 || bool2) } + extraHeaderValue(header) { + if(header.type == "custom_field"){ + let field = this.props.value.custom_fields[header["name"]] + if(field.field_type == "list"){ + return field.options.list_values[field.value] + } + else{ + return field.value + } + } + else{ + return this.props.value[header["name"]] + } + } + render() { this.previousCity = undefined let detailed_calendars = this.hasFeature('detailed_calendars') && !this.disabled let detailed_calendars_shown = $('.detailed-timetables-bt').hasClass('active') + let detailed_purchase_windows = this.hasFeature('detailed_purchase_windows') && !this.disabled + let detailed_purchase_windows_shown = $('.detailed-purchase-windows-bt').hasClass('active') let {time_tables, purchase_windows} = this.props.value return ( @@ -81,6 +114,16 @@ export default class VehicleJourney extends Component { <div>{this.props.value.published_journey_name && this.props.value.published_journey_name != I18n.t('undefined') ? this.props.value.published_journey_name : '-'}</div> <div>{this.props.value.journey_pattern.short_id || '-'}</div> <div>{this.props.value.company ? this.props.value.company.name : '-'}</div> + { + this.props.extraHeaders.map((header, i) => + <div key={i}>{this.extraHeaderValue(header)}</div> + ) + } + { this.hasFeature('journey_length_in_vehicle_journeys') && + <div> + {this.journey_length()} + </div> + } { this.hasFeature('purchase_windows') && <div> {purchase_windows.slice(0,3).map((tt, i)=> @@ -89,6 +132,13 @@ export default class VehicleJourney extends Component { {purchase_windows.length > 3 && <span className='vj_tt'> + {purchase_windows.length - 3}</span>} </div> } + { detailed_purchase_windows && + <div className={"detailed-purchase-windows" + (detailed_purchase_windows_shown ? "" : " hidden")}> + {this.props.allPurchaseWindows.map((w, i) => + <div key={i} className={(this.hasPurchaseWindow(purchase_windows, w) ? "active" : "inactive")}></div> + )} + </div> + } <div> {time_tables.slice(0,3).map((tt, i)=> <span key={i} className='vj_tt'>{this.timeTableURL(tt)}</span> @@ -117,6 +167,8 @@ export default class VehicleJourney extends Component { )} </div> } + + </div> {this.props.value.vehicle_journey_at_stops.map((vj, i) => <div key={i} className='td text-center'> @@ -132,6 +184,7 @@ export default class VehicleJourney extends Component { disabled={!this.props.editMode || this.isDisabled(this.props.value.deletable, vj.dummy) || this.props.filters.policy['vehicle_journeys.update'] == false} readOnly={!this.props.editMode && !vj.dummy} onChange={(e) => {this.props.onUpdateTime(e, i, this.props.index, 'hour', false, false)}} + onChange={(e) => {this.props.onUpdateTime(e, i, this.props.index, 'hour', false, false, true)}} value={vj.arrival_time['hour']} /> <span>:</span> @@ -143,6 +196,7 @@ export default class VehicleJourney extends Component { disabled={!this.props.editMode || this.isDisabled(this.props.value.deletable, vj.dummy) || this.props.filters.policy['vehicle_journeys.update'] == false} readOnly={!this.props.editMode && !vj.dummy} onChange={(e) => {this.props.onUpdateTime(e, i, this.props.index, 'minute', false, false)}} + onBlur={(e) => {this.props.onUpdateTime(e, i, this.props.index, 'minute', false, false, true)}} value={vj.arrival_time['minute']} /> </span> @@ -150,7 +204,7 @@ export default class VehicleJourney extends Component { } <div className={(this.columnHasDelta() ? '' : 'hidden')}> {(vj.delta != 0) && - <span className='sb sb-chrono sb-lg text-warning' data-textinside={vj.delta}></span> + <span className='sb sb-chrono sb-lg text-warning' data-textinside={this.displayDelta(vj.delta)}></span> } </div> <div data-headline={I18n.t("vehicle_journeys.form.departure_at")}> @@ -163,6 +217,7 @@ export default class VehicleJourney extends Component { disabled={!this.props.editMode || this.isDisabled(this.props.value.deletable, vj.dummy) || this.props.filters.policy['vehicle_journeys.update'] == false} readOnly={!this.props.editMode && !vj.dummy} onChange={(e) => {this.props.onUpdateTime(e, i, this.props.index, 'hour', true, this.props.filters.toggleArrivals)}} + onBlur={(e) => {this.props.onUpdateTime(e, i, this.props.index, 'hour', true, this.props.filters.toggleArrivals, true)}} value={vj.departure_time['hour']} /> <span>:</span> @@ -174,13 +229,11 @@ export default class VehicleJourney extends Component { disabled={!this.props.editMode || this.isDisabled(this.props.value.deletable, vj.dummy) || this.props.filters.policy['vehicle_journeys.update'] == false} readOnly={!this.props.editMode && !vj.dummy} onChange={(e) => {this.props.onUpdateTime(e, i, this.props.index, "minute", true, this.props.filters.toggleArrivals)}} + onBlur={(e) => {this.props.onUpdateTime(e, i, this.props.index, "minute", true, this.props.filters.toggleArrivals, true)}} value={vj.departure_time['minute']} /> - </span> - </div> - {vj.errors && <div className="errors"> - {vj.errors} - </div>} + </span> + </div> </div> </div> )} @@ -197,4 +250,6 @@ VehicleJourney.propTypes = { onSelectVehicleJourney: PropTypes.func.isRequired, vehicleJourneys: PropTypes.object.isRequired, allTimeTables: PropTypes.array.isRequired, + allPurchaseWindows: PropTypes.array.isRequired, + extraHeaders: PropTypes.array.isRequired, } diff --git a/app/javascript/vehicle_journeys/components/VehicleJourneys.js b/app/javascript/vehicle_journeys/components/VehicleJourneys.js index 384afba17..27e147b37 100644 --- a/app/javascript/vehicle_journeys/components/VehicleJourneys.js +++ b/app/javascript/vehicle_journeys/components/VehicleJourneys.js @@ -12,6 +12,7 @@ export default class VehicleJourneys extends Component { this.stopPoints(), this.props.filters.features ) + this.togglePurchaseWindows = this.togglePurchaseWindows.bind(this) this.toggleTimetables = this.toggleTimetables.bind(this) } @@ -49,26 +50,55 @@ export default class VehicleJourneys extends Component { return this.headerManager.showHeader(object_id) } - allTimeTables() { - if(this._allTimeTables){ - return this._allTimeTables - } - let keys = [] + getPurchaseWindowsAndTimeTables(){ + let timetables_keys = [] + let windows_keys = [] this._allTimeTables = [] + this._allPurchaseWindows = [] this.vehicleJourneysList().map((vj, index) => { vj.time_tables.map((tt, _) => { - if(keys.indexOf(tt.id) < 0){ - keys.push(tt.id) + if(timetables_keys.indexOf(tt.id) < 0){ + timetables_keys.push(tt.id) this._allTimeTables.push(tt) } }) + vj.purchase_windows.map((tt, _) => { + if(windows_keys.indexOf(tt.id) < 0){ + windows_keys.push(tt.id) + this._allPurchaseWindows.push(tt) + } + }) }) + } + + allTimeTables() { + if(! this._allTimeTables){ + this.getPurchaseWindowsAndTimeTables() + } return this._allTimeTables } + allPurchaseWindows() { + if(!this._allPurchaseWindows){ + this.getPurchaseWindowsAndTimeTables() + } + + return this._allPurchaseWindows + } + toggleTimetables(e) { - $('.table-2entries .detailed-timetables').toggleClass('hidden') - $('.table-2entries .detailed-timetables-bt').toggleClass('active') + let root = $(this.refs['vehicleJourneys']) + root.find('.table-2entries .detailed-timetables').toggleClass('hidden') + root.find('.table-2entries .detailed-timetables-bt').toggleClass('active') + this.componentDidUpdate() + e.preventDefault() + false + } + + togglePurchaseWindows(e) { + let root = $(this.refs['vehicleJourneys']) + root.find('.table-2entries .detailed-purchase-windows').toggleClass('hidden') + root.find('.table-2entries .detailed-purchase-windows-bt').toggleClass('active') this.componentDidUpdate() e.preventDefault() false @@ -127,10 +157,29 @@ export default class VehicleJourneys extends Component { ) } + purchaseWindowURL(tt) { + let refURL = window.location.pathname.split('/', 3).join('/') + let ttURL = refURL + '/purchase_windows/' + tt.id + return ( + <a href={ttURL} title='Voir le calendrier commercial'><span className='fa fa-calendar' style={{color: (tt.color ? tt.color : '#4B4B4B')}}></span>{tt.name}</a> + ) + } + + extraHeaderLabel(header) { + if(header["type"] == "custom_field"){ + return this.props.customFields[header["name"]]["name"] + } + else{ + return I18n.attribute_name("vehicle_journey", header) + } + } + render() { this.previousBreakpoint = undefined this._allTimeTables = null + this._allPurchaseWindows = null let detailed_calendars = this.hasFeature('detailed_calendars') && !this.isReturn() && (this.allTimeTables().length > 0) + let detailed_purchase_windows = this.hasFeature('detailed_purchase_windows') && !this.isReturn() && (this.allPurchaseWindows().length > 0) if(this.props.status.isFetching == true) { return ( <div className="isLoading" style={{marginTop: 80, marginBottom: 80}}> @@ -139,7 +188,7 @@ export default class VehicleJourneys extends Component { ) } else { return ( - <div className='row'> + <div className='row' ref='vehicleJourneys'> <div className='col-lg-12'> {(this.props.status.fetchSuccess == false) && ( <div className='alert alert-danger mt-sm'> @@ -148,7 +197,7 @@ export default class VehicleJourneys extends Component { </div> )} - { this.vehicleJourneysList().errors && this.vehicleJourneysList().errors.length && _.some(this.vehicleJourneysList(), 'errors') && ( + { _.some(this.vehicleJourneysList(), 'errors') && ( <div className="alert alert-danger mt-sm"> <strong>{I18n.tc("error")}</strong> {this.vehicleJourneysList().map((vj, index) => @@ -170,17 +219,49 @@ export default class VehicleJourneys extends Component { <div>{I18n.attribute_name("vehicle_journey", "name")}</div> <div>{I18n.attribute_name("vehicle_journey", "journey_pattern_id")}</div> <div>{I18n.model_name("company")}</div> - { this.hasFeature('purchase_windows') && <div>{I18n.model_name("purchase_window", "plural": true)}</div> } + { + this.props.extraHeaders.map((header, i) => + <div key={i}>{this.extraHeaderLabel(header)}</div> + ) + } + { this.hasFeature('journey_length_in_vehicle_journeys') && + <div> + {I18n.attribute_name("vehicle_journey", "journey_length")} + </div> + } + { this.hasFeature('purchase_windows') && + <div> + { detailed_purchase_windows && + <a href='#' onClick={this.togglePurchaseWindows} className='detailed-purchase-windows-bt'> + <span className='fa fa-angle-up'></span> + {I18n.model_name("purchase_window", {"plural": true})} + </a> + } + { !detailed_purchase_windows && I18n.model_name("purchase_window", {"plural": true})} + </div> + } + { detailed_purchase_windows && + <div className="detailed-purchase-windows hidden"> + {this.allPurchaseWindows().map((tt, i)=> + <div key={i}> + <p> + {this.purchaseWindowURL(tt)} + </p> + <p>{tt.bounding_dates.join(' > ')}</p> + </div> + )} + </div> + } <div> { detailed_calendars && <a href='#' onClick={this.toggleTimetables} className='detailed-timetables-bt'> <span className='fa fa-angle-up'></span> - {I18n.model_name("time_table", "plural": true)} + {I18n.model_name("time_table", {"plural": true})} </a> } - { !detailed_calendars && I18n.model_name("time_table", "plural": true)} + { !detailed_calendars && I18n.model_name("time_table", {"plural": true})} </div> - { !this.isReturn() && + { detailed_calendars && <div className="detailed-timetables hidden"> {this.allTimeTables().map((tt, i)=> <div key={i}> @@ -217,6 +298,8 @@ export default class VehicleJourneys extends Component { vehicleJourneys={this} disabled={this.isReturn()} allTimeTables={this.allTimeTables()} + allPurchaseWindows={this.allPurchaseWindows()} + extraHeaders={this.props.extraHeaders} /> )} </div> @@ -232,6 +315,8 @@ export default class VehicleJourneys extends Component { VehicleJourneys.propTypes = { status: PropTypes.object.isRequired, filters: PropTypes.object.isRequired, + extraHeaders: PropTypes.array.isRequired, + customFields: PropTypes.object.isRequired, stopPointsList: PropTypes.array.isRequired, onLoadFirstPage: PropTypes.func.isRequired, onUpdateTime: PropTypes.func.isRequired, diff --git a/app/javascript/vehicle_journeys/components/tools/CreateModal.js b/app/javascript/vehicle_journeys/components/tools/CreateModal.js index 24d9a23c2..1d470cd43 100644 --- a/app/javascript/vehicle_journeys/components/tools/CreateModal.js +++ b/app/javascript/vehicle_journeys/components/tools/CreateModal.js @@ -3,7 +3,7 @@ import PropTypes from 'prop-types' import actions from '../../actions' import MissionSelect2 from './select2s/MissionSelect2' import CompanySelect2 from './select2s/CompanySelect2' -import CustomFieldsInputs from './CustomFieldsInputs' +import CustomFieldsInputs from '../../../helpers/CustomFieldsInputs' export default class CreateModal extends Component { constructor(props) { @@ -12,7 +12,13 @@ export default class CreateModal extends Component { } handleSubmit() { - if (actions.validateFields(...this.refs, $('.vjCreateSelectJP')[0]) && this.props.modal.modalProps.selectedJPModal) { + if(!this.props.modal.modalProps.selectedJPModal){ + let field = $('#NewVehicleJourneyModal').find(".vjCreateSelectJP") + field.parent().parent().addClass('has-error').children('.help-block').remove() + field.parent().append("<span class='small help-block'>" + I18n.t("simple_form.required.text") + "</span>") + return + } + if (actions.validateFields(...this.refs, $('.vjCreateSelectJP')[0])) { this.props.onAddVehicleJourney(_.assign({}, this.refs, {custom_fields: this.custom_fields}), this.props.modal.modalProps.selectedJPModal, this.props.stopPointsList, this.props.modal.modalProps.vehicleJourney && this.props.modal.modalProps.vehicleJourney.company) this.props.onModalClose() $('#NewVehicleJourneyModal').modal('hide') @@ -41,7 +47,7 @@ export default class CreateModal extends Component { <div className='modal-dialog'> <div className='modal-content'> <div className='modal-header'> - <h4 className='modal-title'>Ajouter une course</h4> + <h4 className='modal-title'>{I18n.t('vehicle_journeys.actions.new')}</h4> <span type="button" className="close modal-close" data-dismiss="modal">×</span> </div> @@ -51,7 +57,7 @@ export default class CreateModal extends Component { <div className='row'> <div className='col-lg-6 col-md-6 col-sm-6 col-xs-12'> <div className='form-group'> - <label className='control-label'>Nom de la course</label> + <label className='control-label'>{I18n.attribute_name('vehicle_journey', 'journey_name')}</label> <input type='text' ref='published_journey_name' @@ -62,7 +68,7 @@ export default class CreateModal extends Component { </div> <div className='col-lg-6 col-md-6 col-sm-6 col-xs-12'> <div className='form-group'> - <label className='control-label'>Nom du transporteur</label> + <label className='control-label'>{I18n.attribute_name('vehicle_journey', 'company_name')}</label> <CompanySelect2 company = {this.props.modal.modalProps.vehicleJourney && this.props.modal.modalProps.vehicleJourney.company || undefined} onSelect2Company = {(e) => this.props.onSelect2Company(e)} @@ -72,7 +78,7 @@ export default class CreateModal extends Component { </div> <div className='col-lg-6 col-md-6 col-sm-6 col-xs-12'> <div className='form-group'> - <label className='control-label is-required'>Nom public de la mission</label> + <label className='control-label is-required'>{I18n.attribute_name('vehicle_journey', 'journey_pattern_published_name')}</label> <MissionSelect2 selection={this.props.modal.modalProps} onSelect2JourneyPattern={this.props.onSelect2JourneyPattern} @@ -83,7 +89,7 @@ export default class CreateModal extends Component { </div> <div className='col-lg-6 col-md-6 col-sm-6 col-xs-12'> <div className='form-group'> - <label className='control-label'>Numéro de train</label> + <label className='control-label'>{I18n.attribute_name('vehicle_journey', 'published_journey_identifier')}</label> <input type='text' ref='published_journey_identifier' @@ -99,7 +105,7 @@ export default class CreateModal extends Component { /> { this.props.modal.modalProps.selectedJPModal && this.props.modal.modalProps.selectedJPModal.full_schedule && <div className='col-lg-6 col-md-6 col-sm-6 col-xs-12'> <div className='form-group'> - <label className='control-label'>Heure de départ</label> + <label className='control-label'>{I18n.attribute_name('vehicle_journey', 'start_time')}</label> <div className='input-group time'> <input type='number' @@ -136,14 +142,14 @@ export default class CreateModal extends Component { type='button' onClick={this.props.onModalClose} > - Annuler + {I18n.t('cancel')} </button> <button className='btn btn-primary' type='button' onClick={this.handleSubmit.bind(this)} > - Valider + {I18n.t('actions.submit')} </button> </div> </form> diff --git a/app/javascript/vehicle_journeys/components/tools/DeleteVehicleJourneys.js b/app/javascript/vehicle_journeys/components/tools/DeleteVehicleJourneys.js index 4815003d3..b1ce3786b 100644 --- a/app/javascript/vehicle_journeys/components/tools/DeleteVehicleJourneys.js +++ b/app/javascript/vehicle_journeys/components/tools/DeleteVehicleJourneys.js @@ -13,7 +13,7 @@ export default function DeleteVehicleJourneys({onDeleteVehicleJourneys, vehicleJ e.preventDefault() onDeleteVehicleJourneys() }} - title='Supprimer' + title={ I18n.t('actions.delete') } > <span className='fa fa-trash'></span> </button> diff --git a/app/javascript/vehicle_journeys/components/tools/DuplicateVehicleJourney.js b/app/javascript/vehicle_journeys/components/tools/DuplicateVehicleJourney.js index 102a87d85..d7e48bf08 100644 --- a/app/javascript/vehicle_journeys/components/tools/DuplicateVehicleJourney.js +++ b/app/javascript/vehicle_journeys/components/tools/DuplicateVehicleJourney.js @@ -93,7 +93,7 @@ export default class DuplicateVehicleJourney extends Component { <div className='modal-content'> <div className='modal-header'> <h4 className='modal-title'> - Dupliquer { actions.getSelected(this.props.vehicleJourneys).length > 1 ? 'plusieurs courses' : 'une course' } + {I18n.t('vehicle_journeys.vehicle_journeys_matrix.duplicate', { count: actions.getSelected(this.props.vehicleJourneys).length })} </h4> <span type="button" className="close modal-close" data-dismiss="modal">×</span> </div> @@ -102,7 +102,7 @@ export default class DuplicateVehicleJourney extends Component { <form className='form-horizontal'> <div className='modal-body'> <div className={'form-group ' + (actions.getSelected(this.props.vehicleJourneys).length > 1 ? 'hidden' : '' )}> - <label className='control-label is-required col-sm-8'>Horaire de départ indicatif</label> + <label className='control-label is-required col-sm-8'>{I18n.t('vehicle_journeys.vehicle_journeys_matrix.duplicate.start_time')}</label> <span className="col-sm-4"> <span className={'input-group time' + (actions.getSelected(this.props.vehicleJourneys).length > 1 ? ' disabled' : '')}> <input @@ -133,7 +133,7 @@ export default class DuplicateVehicleJourney extends Component { </div> <div className='form-group'> - <label className='control-label is-required col-sm-8'>Nombre de courses à créer et dupliquer</label> + <label className='control-label is-required col-sm-8'>{I18n.t('vehicle_journeys.vehicle_journeys_matrix.duplicate.number')}</label> <div className="col-sm-4"> <input type='number' @@ -152,7 +152,7 @@ export default class DuplicateVehicleJourney extends Component { </div> <div className='form-group'> - <label className='control-label is-required col-sm-8'>Décalage à partir duquel on créé les courses</label> + <label className='control-label is-required col-sm-8'>{I18n.t('vehicle_journeys.vehicle_journeys_matrix.duplicate.delta')}</label> <span className="col-sm-4"> <input type='number' @@ -178,7 +178,7 @@ export default class DuplicateVehicleJourney extends Component { type='button' onClick={this.props.onModalClose} > - Annuler + {I18n.t('cancel')} </button> <button className={'btn btn-primary ' + (this.state.additional_time == 0 && this.state.originalDT.hour == this.state.duplicate_time_hh && this.state.originalDT.minute == this.state.duplicate_time_mm ? 'disabled' : '')} @@ -186,7 +186,7 @@ export default class DuplicateVehicleJourney extends Component { onClick={this.handleSubmit} disabled={this.disableValidateButton()} > - Valider + {I18n.t('actions.submit')} </button> </div> </form> diff --git a/app/javascript/vehicle_journeys/components/tools/EditVehicleJourney.js b/app/javascript/vehicle_journeys/components/tools/EditVehicleJourney.js index d3c01f154..60d982845 100644 --- a/app/javascript/vehicle_journeys/components/tools/EditVehicleJourney.js +++ b/app/javascript/vehicle_journeys/components/tools/EditVehicleJourney.js @@ -2,7 +2,7 @@ import React, { Component } from 'react' import PropTypes from 'prop-types' import actions from '../../actions' import CompanySelect2 from './select2s/CompanySelect2' -import CustomFieldsInputs from './CustomFieldsInputs' +import CustomFieldsInputs from '../../../helpers/CustomFieldsInputs' export default class EditVehicleJourney extends Component { constructor(props) { @@ -96,7 +96,7 @@ export default class EditVehicleJourney extends Component { <div className='row'> <div className='col-lg-6 col-md-6 col-sm-6 col-xs-12'> <div className='form-group'> - <label className='control-label'>{I18n.attribute_name('vehicle_journey', 'company')}</label> + <label className='control-label'>{I18n.attribute_name('vehicle_journey', 'published_journey_identifier')}</label> <input type='text' ref='published_journey_identifier' @@ -173,14 +173,14 @@ export default class EditVehicleJourney extends Component { type='button' onClick={this.props.onModalClose} > - Annuler + {I18n.t('cancel')} </button> <button className='btn btn-primary' type='button' onClick={this.handleSubmit.bind(this)} > - Valider + {I18n.t('actions.submit')} </button> </div> } diff --git a/app/javascript/vehicle_journeys/components/tools/NotesEditVehicleJourney.js b/app/javascript/vehicle_journeys/components/tools/NotesEditVehicleJourney.js index 880542216..ef58916f4 100644 --- a/app/javascript/vehicle_journeys/components/tools/NotesEditVehicleJourney.js +++ b/app/javascript/vehicle_journeys/components/tools/NotesEditVehicleJourney.js @@ -31,23 +31,23 @@ export default class NotesEditVehicleJourney extends Component { type='button' className='btn btn-outline-danger btn-xs' onClick={() => this.props.onToggleFootnoteModal(lf, false)} - ><span className="fa fa-trash"></span> Retirer</button> + ><span className="fa fa-trash"></span>{I18n.t('actions.remove')}</button> } else { return <button type='button' className='btn btn-outline-primary btn-xs' onClick={() => this.props.onToggleFootnoteModal(lf, true)} - ><span className="fa fa-plus"></span> Ajouter</button> + ><span className="fa fa-plus"></span>{I18n.t('actions.add')}</button> } } renderAssociatedFN() { if (this.footnotes().associated.length == 0) { - return <h3>Aucune note associée</h3> + return <h3>{I18n.t('vehicle_journeys.vehicle_journeys_matrix.no_associated_footnotes')}</h3> } else { return ( <div> - <h3>Notes associées :</h3> + <h3>{I18n.t('vehicle_journeys.form.purchase_windows')} :</h3> {this.footnotes().associated.map((lf, i) => <div key={i} @@ -68,13 +68,13 @@ export default class NotesEditVehicleJourney extends Component { } renderToAssociateFN() { - if (window.line_footnotes.length == 0) return <h3>La ligne ne possède pas de notes</h3> + if (window.line_footnotes.length == 0) return <h3>{I18n.t('vehicle_journeys.vehicle_journeys_matrix.no_line_footnotes')}</h3> if (this.footnotes().to_associate.length == 0) return false return ( <div> - <h3 className='mt-lg'>Sélectionnez les notes à associer à cette course :</h3> + <h3 className='mt-lg'>{I18n.t('vehicle_journeys.vehicle_journeys_matrix.select_footnotes')} :</h3> {this.footnotes().to_associate.map((lf, i) => <div key={i} className='panel panel-default'> <div className='panel-heading'> @@ -111,7 +111,7 @@ export default class NotesEditVehicleJourney extends Component { <div className='modal-dialog'> <div className='modal-content'> <div className='modal-header'> - <h4 className='modal-title'>Notes</h4> + <h4 className='modal-title'>{I18n.t('vehicle_journeys.form.footnotes')}</h4> <span type="button" className="close modal-close" data-dismiss="modal">×</span> </div> @@ -130,14 +130,14 @@ export default class NotesEditVehicleJourney extends Component { type='button' onClick={this.props.onModalClose} > - Annuler + {I18n.t('cancel')} </button> <button className='btn btn-primary' type='button' onClick={this.handleSubmit.bind(this)} > - Valider + {I18n.t('actions.submit')} </button> </div> } diff --git a/app/javascript/vehicle_journeys/components/tools/PurchaseWindowsEditVehicleJourney.js b/app/javascript/vehicle_journeys/components/tools/PurchaseWindowsEditVehicleJourney.js index ce9a4cde9..5fc925f4c 100644 --- a/app/javascript/vehicle_journeys/components/tools/PurchaseWindowsEditVehicleJourney.js +++ b/app/javascript/vehicle_journeys/components/tools/PurchaseWindowsEditVehicleJourney.js @@ -44,7 +44,7 @@ export default class PurchaseWindowsEditVehicleJourney extends Component { <div className='modal-dialog'> <div className='modal-content'> <div className='modal-header'> - <h4 className='modal-title'>Calendriers commerciaux associés</h4> + <h4 className='modal-title'>{I18n.t('vehicle_journeys.form.purchase_windows')}</h4> <span type="button" className="close modal-close" data-dismiss="modal">×</span> </div> @@ -58,7 +58,7 @@ export default class PurchaseWindowsEditVehicleJourney extends Component { <div className='wrapper'> <div> <div className='form-group'> - <label className='control-label'>{this.props.modal.modalProps.purchase_windows.length == 0 ? "Aucun calendrier commercial associé" : "Calendriers commerciaux associés"}</label> + <label className='control-label'>{this.props.modal.modalProps.purchase_windows.length == 0 ? I18n.t('vehicle_journeys.vehicle_journeys_matrix.no_associated_purchase_windows') : I18n.t('vehicle_journeys.form.purchase_windows')}</label> </div> </div> <div></div> @@ -95,6 +95,7 @@ export default class PurchaseWindowsEditVehicleJourney extends Component { <div className='wrapper'> <div> <TimetableSelect2 + placeholder={I18n.t('vehicle_journeys.vehicle_journeys_matrix.filters.purchase_window')} onSelect2Timetable={this.props.onSelect2Timetable} chunkURL={'/autocomplete_purchase_windows.json'} searchKey={"name_or_objectid_cont_any"} @@ -117,14 +118,14 @@ export default class PurchaseWindowsEditVehicleJourney extends Component { type='button' onClick={this.props.onModalClose} > - Annuler + {I18n.t('cancel')} </button> <button className='btn btn-primary' type='button' onClick={this.handleSubmit} > - Valider + {I18n.t('actions.submit')} </button> </div> } diff --git a/app/javascript/vehicle_journeys/components/tools/ShiftVehicleJourney.js b/app/javascript/vehicle_journeys/components/tools/ShiftVehicleJourney.js index 6574bfa2d..bc3d8db34 100644 --- a/app/javascript/vehicle_journeys/components/tools/ShiftVehicleJourney.js +++ b/app/javascript/vehicle_journeys/components/tools/ShiftVehicleJourney.js @@ -27,6 +27,7 @@ export default class ShiftVehicleJourney extends Component { } render() { + let id = this.props.modal.type == 'shift' && actions.getSelected(this.props.vehicleJourneys)[0].short_id if(this.props.status.isFetching == true) { return false } @@ -48,10 +49,7 @@ export default class ShiftVehicleJourney extends Component { <div className='modal-dialog'> <div className='modal-content'> <div className='modal-header'> - <h4 className='modal-title'>Mettre à jour une course</h4> - {(this.props.modal.type == 'shift') && ( - <em>Mettre à jour les horaires de la course {actions.getSelected(this.props.vehicleJourneys)[0].short_id}</em> - )} + <h4 className='modal-title'>{I18n.t('vehicle_journeys.form.slide_title', {id: id})}</h4> <span type="button" className="close modal-close" data-dismiss="modal">×</span> </div> @@ -61,7 +59,7 @@ export default class ShiftVehicleJourney extends Component { <div className='row'> <div className='col-lg-4 col-lg-offset-4 col-md-4 col-md-offset-4 col-sm-4 col-sm-offset-4 col-xs-12'> <div className='form-group'> - <label className='control-label is-required'>Avec un décalage de</label> + <label className='control-label is-required'>{I18n.t('vehicle_journeys.form.slide_delta')}</label> <input type='number' style={{'width': 104}} @@ -85,14 +83,14 @@ export default class ShiftVehicleJourney extends Component { type='button' onClick={this.props.onModalClose} > - Annuler + {I18n.t('cancel')} </button> <button className={'btn btn-primary ' + (this.state.additional_time == 0 ? 'disabled' : '')} type='button' onClick={this.handleSubmit.bind(this)} > - Valider + {I18n.t('actions.submit')} </button> </div> </form> diff --git a/app/javascript/vehicle_journeys/components/tools/TimetablesEditVehicleJourney.js b/app/javascript/vehicle_journeys/components/tools/TimetablesEditVehicleJourney.js index f21480563..594140c76 100644 --- a/app/javascript/vehicle_journeys/components/tools/TimetablesEditVehicleJourney.js +++ b/app/javascript/vehicle_journeys/components/tools/TimetablesEditVehicleJourney.js @@ -44,7 +44,7 @@ export default class TimetablesEditVehicleJourney extends Component { <div className='modal-dialog'> <div className='modal-content'> <div className='modal-header'> - <h4 className='modal-title'>Calendriers associés</h4> + <h4 className='modal-title'>{I18n.t('vehicle_journeys.form.time_tables')}</h4> <span type="button" className="close modal-close" data-dismiss="modal">×</span> </div> @@ -58,7 +58,7 @@ export default class TimetablesEditVehicleJourney extends Component { <div className='wrapper'> <div> <div className='form-group'> - <label className='control-label'>{this.props.modal.modalProps.timetables.length == 0 ? "Aucun calendrier associé" : "Calendriers associés"}</label> + <label className='control-label'>{this.props.modal.modalProps.timetables.length == 0 ? I18n.t('vehicle_journeys.vehicle_journeys_matrix.no_associated_timetables'): I18n.t('vehicle_journeys.form.time_tables')}</label> </div> </div> <div></div> @@ -97,6 +97,7 @@ export default class TimetablesEditVehicleJourney extends Component { <div className='wrapper'> <div> <TimetableSelect2 + placeholder={I18n.t('vehicle_journeys.vehicle_journeys_matrix.filters.timetable')} onSelect2Timetable={this.props.onSelect2Timetable} chunkURL={'/autocomplete_time_tables.json'} searchKey={"unaccented_comment_or_objectid_cont_any"} @@ -119,14 +120,14 @@ export default class TimetablesEditVehicleJourney extends Component { type='button' onClick={this.props.onModalClose} > - Annuler + {I18n.t('cancel')} </button> <button className='btn btn-primary' type='button' onClick={this.handleSubmit} > - Valider + {I18n.t('actions.submit')} </button> </div> } diff --git a/app/javascript/vehicle_journeys/components/tools/select2s/CompanySelect2.js b/app/javascript/vehicle_journeys/components/tools/select2s/CompanySelect2.js index 5c7f75d99..60ad439b8 100644 --- a/app/javascript/vehicle_journeys/components/tools/select2s/CompanySelect2.js +++ b/app/javascript/vehicle_journeys/components/tools/select2s/CompanySelect2.js @@ -16,6 +16,7 @@ export default class BSelect4 extends Component { } render() { + let placeHolder = I18n.t('') return ( <Select2 data={(this.props.company) ? [this.props.company.name] : undefined} @@ -29,8 +30,8 @@ export default class BSelect4 extends Component { allowClear: true, theme: 'bootstrap', width: '100%', - placeholder: 'Filtrer par transporteur...', - language: require('./fr'), + placeholder: I18n.t('vehicle_journeys.vehicle_journeys_matrix.affect_company'), + language: require('./language'), ajax: { url: origin + path + '/companies.json' + '?line_id=' + line, dataType: 'json', diff --git a/app/javascript/vehicle_journeys/components/tools/select2s/MissionSelect2.js b/app/javascript/vehicle_journeys/components/tools/select2s/MissionSelect2.js index 72dbd0152..cec39ab4e 100644 --- a/app/javascript/vehicle_journeys/components/tools/select2s/MissionSelect2.js +++ b/app/javascript/vehicle_journeys/components/tools/select2s/MissionSelect2.js @@ -74,8 +74,8 @@ export default class BSelect4 extends Component { width: '100%', escapeMarkup: function (markup) { return markup; }, templateResult: formatRepo, - placeholder: 'Filtrer par code, nom ou OID de mission...', - language: require('./fr'), + placeholder: I18n.t('vehicle_journeys.vehicle_journeys_matrix.filters.journey_pattern'), + language: require('./language'), allowClear: false, escapeMarkup: function (markup) { return markup; }, } diff --git a/app/javascript/vehicle_journeys/components/tools/select2s/TimetableSelect2.js b/app/javascript/vehicle_journeys/components/tools/select2s/TimetableSelect2.js index 0339455ca..919853a71 100644 --- a/app/javascript/vehicle_journeys/components/tools/select2s/TimetableSelect2.js +++ b/app/javascript/vehicle_journeys/components/tools/select2s/TimetableSelect2.js @@ -26,8 +26,8 @@ export default class BSelect4 extends Component { allowClear: false, theme: 'bootstrap', width: '100%', - placeholder: 'Filtrer par calendrier...', - language: require('./fr'), + placeholder: this.props.placeholder, + language: require('./language'), ajax: { url: origin + path + this.props.chunkURL, dataType: 'json', diff --git a/app/javascript/vehicle_journeys/components/tools/select2s/VJSelect2.js b/app/javascript/vehicle_journeys/components/tools/select2s/VJSelect2.js index ccb4c9595..50a941b6d 100644 --- a/app/javascript/vehicle_journeys/components/tools/select2s/VJSelect2.js +++ b/app/javascript/vehicle_journeys/components/tools/select2s/VJSelect2.js @@ -25,9 +25,9 @@ export default class BSelect4b extends Component { options={{ allowClear: false, theme: 'bootstrap', - placeholder: 'Filtrer par ID course...', + placeholder: I18n.t('vehicle_journeys.vehicle_journeys_matrix.filters.id'), width: '100%', - language: require('./fr'), + language: require('./language'), ajax: { url: origin + path + '/vehicle_journeys.json', dataType: 'json', diff --git a/app/javascript/vehicle_journeys/components/tools/select2s/fr.js b/app/javascript/vehicle_journeys/components/tools/select2s/fr.js deleted file mode 100644 index 20154d412..000000000 --- a/app/javascript/vehicle_journeys/components/tools/select2s/fr.js +++ /dev/null @@ -1,9 +0,0 @@ -module.exports = { - errorLoading:function(){return"Les résultats ne peuvent pas être chargés."}, - inputTooLong:function(e){var t=e.input.length-e.maximum,n="Supprimez "+t+" caractère";return t!==1&&(n+="s"),n}, - inputTooShort:function(e){var t=e.minimum-e.input.length,n="Saisissez "+t+" caractère";return t!==1&&(n+="s"),n}, - loadingMore:function(){return"Chargement de résultats supplémentaires…"}, - maximumSelected:function(e){var t="Vous pouvez seulement sélectionner "+e.maximum+" élément";return e.maximum!==1&&(t+="s"),t}, - noResults:function(){return"Aucun résultat trouvé"}, - searching:function(){return"Recherche en cours…"} -} diff --git a/app/javascript/vehicle_journeys/components/tools/select2s/language.js b/app/javascript/vehicle_journeys/components/tools/select2s/language.js new file mode 100644 index 000000000..9d587f96e --- /dev/null +++ b/app/javascript/vehicle_journeys/components/tools/select2s/language.js @@ -0,0 +1,9 @@ +module.exports = { + errorLoading: () => I18n.t('select2.error_loading'), + inputTooLong: (e) => I18n.t('select2.input_too_short', { count: e.input.length - e.maximum}), + inputTooShort: (e) => I18n.t('select2.input_too_long', { count: e.minimum - e.input.length }), + loadingMore: () => I18n.t('select2.loading_more'), + maximumSelected: (e) => I18n.t('select2.maximum_selected', {count: e.maximum}), + noResults: () => I18n.t('select2.no_results'), + searching: () => I18n.t('select2.searching') +} diff --git a/app/javascript/vehicle_journeys/containers/VehicleJourneysList.js b/app/javascript/vehicle_journeys/containers/VehicleJourneysList.js index 76d1c3a78..f21e2c87e 100644 --- a/app/javascript/vehicle_journeys/containers/VehicleJourneysList.js +++ b/app/javascript/vehicle_journeys/containers/VehicleJourneysList.js @@ -10,7 +10,9 @@ const mapStateToProps = (state) => { status: state.status, filters: state.filters, stopPointsList: state.stopPointsList, - returnStopPointsList: state.returnStopPointsList + returnStopPointsList: state.returnStopPointsList, + extraHeaders: window.extra_headers, + customFields: window.custom_fields, } } @@ -20,8 +22,8 @@ const mapDispatchToProps = (dispatch) => { dispatch(actions.fetchingApi()) actions.fetchVehicleJourneys(dispatch, undefined, undefined, filters.queryString, routeUrl) }, - onUpdateTime: (e, subIndex, index, timeUnit, isDeparture, isArrivalsToggled) => { - dispatch(actions.updateTime(e.target.value, subIndex, index, timeUnit, isDeparture, isArrivalsToggled)) + onUpdateTime: (e, subIndex, index, timeUnit, isDeparture, isArrivalsToggled, enforceConsistency=false) => { + dispatch(actions.updateTime(e.target.value, subIndex, index, timeUnit, isDeparture, isArrivalsToggled, enforceConsistency)) }, onSelectVehicleJourney: (index) => { dispatch(actions.selectVehicleJourney(index)) diff --git a/app/javascript/vehicle_journeys/reducers/modal.js b/app/javascript/vehicle_journeys/reducers/modal.js index bcfc6ea0b..84567ec0d 100644 --- a/app/javascript/vehicle_journeys/reducers/modal.js +++ b/app/javascript/vehicle_journeys/reducers/modal.js @@ -164,7 +164,9 @@ export default function modal(state = {}, action) { confirmModal: {} } case 'SELECT_JP_CREATE_MODAL': - newModalProps = _.assign({}, state.modalProps, {selectedJPModal : action.selectedItem}) + let selected = action.selectedItem + delete selected["element"] + newModalProps = _.assign({}, state.modalProps, {selectedJPModal : selected}) return _.assign({}, state, {modalProps: newModalProps}) case 'SHIFT_VEHICLEJOURNEY_MODAL': return { diff --git a/app/javascript/vehicle_journeys/reducers/vehicleJourneys.js b/app/javascript/vehicle_journeys/reducers/vehicleJourneys.js index 8705b3cf2..b02c19a69 100644 --- a/app/javascript/vehicle_journeys/reducers/vehicleJourneys.js +++ b/app/javascript/vehicle_journeys/reducers/vehicleJourneys.js @@ -9,32 +9,35 @@ const vehicleJourney= (state = {}, action, keep) => { return _.assign({}, state, {selected: false}) case 'ADD_VEHICLEJOURNEY': let pristineVjasList = [] - let prevSp = action.stopPointsList[0] + let prevSp let current_time = { hour: 0, minute: 0 } let computeSchedule = false - let userTZOffet = 0 + let initTZOffet = 0 if(action.data["start_time.hour"] && action.data["start_time.hour"].value && action.data["start_time.hour"].value.length > 0 && action.data["start_time.minute"] && action.selectedJourneyPattern.full_schedule && action.selectedJourneyPattern.costs){ computeSchedule = true - userTZOffet = action.data["tz_offset"] && parseInt(action.data["tz_offset"].value) || 0 - current_time.hour = parseInt(action.data["start_time.hour"].value) + parseInt(userTZOffet / 60) + initTZOffet = - action.stopPointsList[0].time_zone_offset / 60 || 0 + current_time.hour = parseInt(action.data["start_time.hour"].value) + parseInt(initTZOffet / 60) current_time.minute = 0 if(action.data["start_time.minute"].value){ - current_time.minute = parseInt(action.data["start_time.minute"].value) + (userTZOffet - 60 * parseInt(userTZOffet / 60)) + current_time.minute = parseInt(action.data["start_time.minute"].value) + (initTZOffet - 60 * parseInt(initTZOffet / 60)) } } _.each(action.stopPointsList, (sp) =>{ let inJourney = false let newVjas if(computeSchedule){ - if(action.selectedJourneyPattern.costs[prevSp.stop_area_id + "-" + sp.stop_area_id]){ + if(prevSp && action.selectedJourneyPattern.costs[prevSp.stop_area_id + "-" + sp.stop_area_id]){ let delta = parseInt(action.selectedJourneyPattern.costs[prevSp.stop_area_id + "-" + sp.stop_area_id].time) current_time = actions.addMinutesToTime(current_time, delta) prevSp = sp inJourney = true } + if(!prevSp){ + prevSp = sp + } let offsetHours = sp.time_zone_offset / 3600 let offsetminutes = sp.time_zone_offset/60 - 60*offsetHours newVjas = { @@ -90,6 +93,11 @@ const vehicleJourney= (state = {}, action, keep) => { } }) + let lastStop = action.selectedJourneyPattern.stop_areas && action.selectedJourneyPattern.stop_areas[action.selectedJourneyPattern.stop_areas.length - 1] + if(lastStop && lastStop.stop_area_short_description.id == sp.id){ + newVjas.departure_time = newVjas.arrival_time + } + if(newVjas.dummy){ newVjas.departure_time = {hour: "00", minute: "00"} newVjas.arrival_time = {hour: "00", minute: "00"} @@ -148,11 +156,11 @@ const vehicleJourney= (state = {}, action, keep) => { newSchedule.departure_time[action.timeUnit] = actions.pad(action.val, action.timeUnit) if(!action.isArrivalsToggled) newSchedule.arrival_time[action.timeUnit] = newSchedule.departure_time[action.timeUnit] - newSchedule = actions.adjustSchedule(action, newSchedule) + newSchedule = actions.adjustSchedule(action, newSchedule, action.enforceConsistency) return _.assign({}, state.vehicle_journey_at_stops[action.subIndex], {arrival_time: newSchedule.arrival_time, departure_time: newSchedule.departure_time, delta: newSchedule.delta}) }else{ newSchedule.arrival_time[action.timeUnit] = actions.pad(action.val, action.timeUnit) - newSchedule = actions.adjustSchedule(action, newSchedule) + newSchedule = actions.adjustSchedule(action, newSchedule, action.enforceConsistency) return _.assign({}, state.vehicle_journey_at_stops[action.subIndex], {arrival_time: newSchedule.arrival_time, departure_time: newSchedule.departure_time, delta: newSchedule.delta}) } }else{ |
