diff options
| author | Xinhui | 2017-05-23 10:03:52 +0200 |
|---|---|---|
| committer | Xinhui | 2017-05-23 10:03:52 +0200 |
| commit | 4f5cc7d35777f3b4bfa1c63c1223c679f713424e (patch) | |
| tree | 8c3fa28f8f1f4b238248037dce4a6c5974a82e02 | |
| parent | af2c4b003ca0bf856a1f1d532865963b40f159ee (diff) | |
| parent | 5e3c2d8daba5617a72d0bfd06e0b6b3f03628f56 (diff) | |
| download | chouette-core-4f5cc7d35777f3b4bfa1c63c1223c679f713424e.tar.bz2 | |
Merge branch 'master' into staging
111 files changed, 1705 insertions, 431 deletions
diff --git a/app/assets/javascripts/es6_browserified/itineraries/show.js b/app/assets/javascripts/es6_browserified/itineraries/show.js new file mode 100644 index 000000000..79a11701f --- /dev/null +++ b/app/assets/javascripts/es6_browserified/itineraries/show.js @@ -0,0 +1,99 @@ +route = JSON.parse(decodeURIComponent(route)) +const geoColPts = [] +const geoColLns= [] +const geoColEdges = [ + new ol.Feature({ + geometry: new ol.geom.Point(ol.proj.fromLonLat([parseFloat(route[0].longitude), parseFloat(route[0].latitude)])) + }), + new ol.Feature({ + geometry: new ol.geom.Point(ol.proj.fromLonLat([parseFloat(route[route.length - 1].longitude), parseFloat(route[route.length - 1].latitude)])) + }) +] +route.forEach(function(stop, i){ + if (i < route.length - 1){ + geoColLns.push(new ol.Feature({ + geometry: new ol.geom.LineString([ + ol.proj.fromLonLat([parseFloat(route[i].longitude), parseFloat(route[i].latitude)]), + ol.proj.fromLonLat([parseFloat(route[i+1].longitude), parseFloat(route[i+1].latitude)]) + ]) + })) + } + geoColPts.push(new ol.Feature({ + geometry: new ol.geom.Point(ol.proj.fromLonLat([parseFloat(stop.longitude), parseFloat(stop.latitude)])) + }) + ) +}) +var edgeStyles = new ol.style.Style({ + image: new ol.style.Circle(({ + radius: 5, + stroke: new ol.style.Stroke({ + color: '#007fbb', + width: 2 + }), + fill: new ol.style.Fill({ + color: '#007fbb', + width: 2 + }) + })) +}) +var defaultStyles = new ol.style.Style({ + image: new ol.style.Circle(({ + radius: 4, + stroke: new ol.style.Stroke({ + color: '#007fbb', + width: 2 + }), + fill: new ol.style.Fill({ + color: '#ffffff', + width: 2 + }) + })) +}) +var lineStyle = new ol.style.Style({ + stroke: new ol.style.Stroke({ + color: '#007fbb', + width: 3 + }) +}) + +var vectorPtsLayer = new ol.layer.Vector({ + source: new ol.source.Vector({ + features: geoColPts + }), + style: defaultStyles, + zIndex: 2 +}) +var vectorEdgesLayer = new ol.layer.Vector({ + source: new ol.source.Vector({ + features: geoColEdges + }), + style: edgeStyles, + zIndex: 3 +}) +var vectorLnsLayer = new ol.layer.Vector({ + source: new ol.source.Vector({ + features: geoColLns + }), + style: [lineStyle], + zIndex: 1 +}) + +var map = new ol.Map({ + target: 'route_map', + layers: [ + new ol.layer.Tile({ + source: new ol.source.OSM() + }), + vectorPtsLayer, + vectorEdgesLayer, + vectorLnsLayer + ], + controls: [ new ol.control.ScaleLine(), new ol.control.Zoom(), new ol.control.ZoomSlider() ], + interactions: ol.interaction.defaults({ + zoom: true + }), + view: new ol.View({ + center: ol.proj.fromLonLat([parseFloat(route[0].longitude), parseFloat(route[0].latitude)]), + zoom: 13 + }) +}); diff --git a/app/assets/javascripts/es6_browserified/time_tables/actions/index.js b/app/assets/javascripts/es6_browserified/time_tables/actions/index.js index 3f15b7f01..41d247b21 100644 --- a/app/assets/javascripts/es6_browserified/time_tables/actions/index.js +++ b/app/assets/javascripts/es6_browserified/time_tables/actions/index.js @@ -50,9 +50,13 @@ const actions = { dispatch, page: val }), - updateDayTypes: (index) => ({ + updateDayTypes: (dayTypes) => ({ type: 'UPDATE_DAY_TYPES', - index + dayTypes + }), + updateCurrentMonthFromDaytypes: (dayTypes) => ({ + type: 'UPDATE_CURRENT_MONTH_FROM_DAYTYPES', + dayTypes }), updateComment: (comment) => ({ type: 'UPDATE_COMMENT', @@ -162,11 +166,8 @@ const actions = { if(testDate === false){ if(currentDate >= begin && currentDate <= end) { - if(daytypes[d.wday] === false) { - testDate = false - } else { - testDate = true - } + testDate = true + p.include_date = false } } }) @@ -174,8 +175,11 @@ const actions = { } let improvedCM = state.current_month.map((d, i) => { + let bool = isInPeriod(state.current_month[i]) return _.assign({}, state.current_month[i], { - in_periods: isInPeriod(state.current_month[i]) + in_periods: bool, + include_date: bool ? false : state.current_month[i].include_date, + excluded_date: !bool ? false : state.current_month[i].excluded_date }) }) return improvedCM @@ -197,7 +201,7 @@ const actions = { start = new Date(start) end = new Date(end) _.each(periods, (period, i) => { - if(index != i){ + if(index != i && !period.deleted){ if((new Date(period.period_start) <= start && new Date(period.period_end) >= start) || (new Date(period.period_start) <= end && new Date(period.period_end) >= end)) error = 'Les périodes ne peuvent pas se chevaucher' } diff --git a/app/assets/javascripts/es6_browserified/time_tables/components/ExceptionsInDay.js b/app/assets/javascripts/es6_browserified/time_tables/components/ExceptionsInDay.js index cc7980b22..10b558373 100644 --- a/app/assets/javascripts/es6_browserified/time_tables/components/ExceptionsInDay.js +++ b/app/assets/javascripts/es6_browserified/time_tables/components/ExceptionsInDay.js @@ -10,9 +10,8 @@ class ExceptionsInDay extends Component { render() { {/* display add or remove link, only if true in daytypes */} - if(this.props.outFromDaytypes == true) { {/* display add or remove link, according to context (presence in period, or not) */} - if(this.props.value.current_month[this.props.index].in_periods == true) { + if(this.props.value.current_month[this.props.index].in_periods == true && this.props.blueDaytype == true) { return ( <div className='td'> <button @@ -28,7 +27,7 @@ class ExceptionsInDay extends Component { </button> </div> ) - } else { + } else if(this.props.value.current_month[this.props.index].in_periods == false) { return ( <div className='td'> <button @@ -44,19 +43,20 @@ class ExceptionsInDay extends Component { </button> </div> ) + } else if(this.props.value.current_month[this.props.index].in_periods == true && this.props.blueDaytype == false){ + return ( + <div className='td'></div> + ) + } else{ + return false } - } else { - return ( - <div className='td'></div> - ) - } } } ExceptionsInDay.propTypes = { value: PropTypes.object.isRequired, metas: PropTypes.object.isRequired, - outFromDaytypes: PropTypes.bool.isRequired, + blueDaytype: PropTypes.bool.isRequired, onExcludeDateFromPeriod: PropTypes.func.isRequired, onIncludeDateInPeriod: PropTypes.func.isRequired, index: PropTypes.number.isRequired diff --git a/app/assets/javascripts/es6_browserified/time_tables/components/Metas.js b/app/assets/javascripts/es6_browserified/time_tables/components/Metas.js index 792d75520..502320c27 100644 --- a/app/assets/javascripts/es6_browserified/time_tables/components/Metas.js +++ b/app/assets/javascripts/es6_browserified/time_tables/components/Metas.js @@ -107,7 +107,7 @@ const Metas = ({metas, onUpdateDayTypes, onUpdateComment, onUpdateColor, onSelec <div className="checkbox"> <label> <input - onChange={(e) => {onUpdateDayTypes(i)}} + onChange={(e) => {onUpdateDayTypes(i, metas.day_types)}} id={i} type="checkbox" checked={day ? 'checked' : ''} diff --git a/app/assets/javascripts/es6_browserified/time_tables/components/PeriodForm.js b/app/assets/javascripts/es6_browserified/time_tables/components/PeriodForm.js index 5beb80573..1a6c67b6b 100644 --- a/app/assets/javascripts/es6_browserified/time_tables/components/PeriodForm.js +++ b/app/assets/javascripts/es6_browserified/time_tables/components/PeriodForm.js @@ -118,7 +118,7 @@ const PeriodForm = ({modal, timetable, metas, onOpenAddPeriodForm, onClosePeriod <div className="text-right"> <button type='button' - className='btn btn-outline-primary add_fields' + className='btn btn-outline-primary' onClick={onOpenAddPeriodForm} > Ajouter une période diff --git a/app/assets/javascripts/es6_browserified/time_tables/components/TimeTableDay.js b/app/assets/javascripts/es6_browserified/time_tables/components/TimeTableDay.js index 29c894565..71621c874 100644 --- a/app/assets/javascripts/es6_browserified/time_tables/components/TimeTableDay.js +++ b/app/assets/javascripts/es6_browserified/time_tables/components/TimeTableDay.js @@ -17,7 +17,7 @@ class TimeTableDay extends Component { {((this.props.value.day).charAt(0) == 'm') ? (this.props.value.day).substr(0, 2) : (this.props.value.day).charAt(0)} </span> <span - className={'daynumber' + (((this.props.value.in_periods && this.props.dayTypeActive && !this.props.value.excluded_date) || (this.props.value.include_date && this.props.dayTypeActive)) ? ' included' : '')} + className={'daynumber' + (((this.props.value.in_periods && this.props.dayTypeActive && !this.props.value.excluded_date) || (this.props.value.include_date)) ? ' included' : '')} > {this.props.value.mday} </span> diff --git a/app/assets/javascripts/es6_browserified/time_tables/components/Timetable.js b/app/assets/javascripts/es6_browserified/time_tables/components/Timetable.js index 1830bfcde..d562655b9 100644 --- a/app/assets/javascripts/es6_browserified/time_tables/components/Timetable.js +++ b/app/assets/javascripts/es6_browserified/time_tables/components/Timetable.js @@ -60,7 +60,7 @@ class Timetable extends Component{ {this.props.timetable.current_month.map((d, i) => <div key={i} - className={'td-group' + (this.props.metas.day_types[d.wday] ? '' : ' out_from_daytypes') + (d.wday == 0 ? ' last_wday' : '')} + className={'td-group' + (this.props.metas.day_types[d.wday] || !d.in_periods ? '' : ' out_from_daytypes') + (d.wday == 0 ? ' last_wday' : '')} > {/* day_types */} <div className="td"></div> @@ -80,7 +80,7 @@ class Timetable extends Component{ index={i} value={this.props.timetable} metas={this.props.metas} - outFromDaytypes={this.props.metas.day_types[d.wday]} + blueDaytype={this.props.metas.day_types[d.wday]} onExcludeDateFromPeriod={this.props.onExcludeDateFromPeriod} onIncludeDateInPeriod={this.props.onIncludeDateInPeriod} /> diff --git a/app/assets/javascripts/es6_browserified/time_tables/containers/Metas.js b/app/assets/javascripts/es6_browserified/time_tables/containers/Metas.js index a8639d89f..c4ab2d5b3 100644 --- a/app/assets/javascripts/es6_browserified/time_tables/containers/Metas.js +++ b/app/assets/javascripts/es6_browserified/time_tables/containers/Metas.js @@ -10,8 +10,11 @@ const mapStateToProps = (state) => { const mapDispatchToProps = (dispatch) => { return { - onUpdateDayTypes: (index) => { - dispatch(actions.updateDayTypes(index)) + onUpdateDayTypes: (index, dayTypes) => { + let newDayTypes = dayTypes.slice(0) + newDayTypes[index] = !newDayTypes[index] + dispatch(actions.updateDayTypes(newDayTypes)) + dispatch(actions.updateCurrentMonthFromDaytypes(newDayTypes)) }, onUpdateComment: (comment) => { dispatch(actions.updateComment(comment)) diff --git a/app/assets/javascripts/es6_browserified/time_tables/reducers/metas.js b/app/assets/javascripts/es6_browserified/time_tables/reducers/metas.js index dd6b484f3..97de90225 100644 --- a/app/assets/javascripts/es6_browserified/time_tables/reducers/metas.js +++ b/app/assets/javascripts/es6_browserified/time_tables/reducers/metas.js @@ -18,9 +18,7 @@ const metas = (state = {}, action) => { case 'VALIDATE_PERIOD_FORM': return _.assign({}, state, {calendar: {name: 'Aucun'}}) case 'UPDATE_DAY_TYPES': - let dayTypes = state.day_types.slice(0) - dayTypes[action.index] = !dayTypes[action.index] - return _.assign({}, state, {day_types: dayTypes, calendar : {name: 'Aucun'}}) + return _.assign({}, state, {day_types: action.dayTypes, calendar : {name: 'Aucun'}}) case 'UPDATE_COMMENT': return _.assign({}, state, {comment: action.comment}) case 'UPDATE_COLOR': diff --git a/app/assets/javascripts/es6_browserified/time_tables/reducers/timetable.js b/app/assets/javascripts/es6_browserified/time_tables/reducers/timetable.js index f84223b23..65cd9231a 100644 --- a/app/assets/javascripts/es6_browserified/time_tables/reducers/timetable.js +++ b/app/assets/javascripts/es6_browserified/time_tables/reducers/timetable.js @@ -55,6 +55,8 @@ const timetable = (state = {}, action) => { }) newState = _.assign({}, state, {current_month: newCMe}) return _.assign({}, newState, {current_month: actions.updateSynthesis(newState, action.dayTypes)}) + case 'UPDATE_CURRENT_MONTH_FROM_DAYTYPES': + return _.assign({}, state, {current_month: actions.updateSynthesis(state, action.dayTypes)}) case 'VALIDATE_PERIOD_FORM': let period_start = actions.formatDate(action.modalProps.begin) let period_end = actions.formatDate(action.modalProps.end) diff --git a/app/assets/javascripts/es6_browserified/vehicle_journeys/actions/index.js b/app/assets/javascripts/es6_browserified/vehicle_journeys/actions/index.js index 0e6f5ed12..0af1bb53d 100644 --- a/app/assets/javascripts/es6_browserified/vehicle_journeys/actions/index.js +++ b/app/assets/javascripts/es6_browserified/vehicle_journeys/actions/index.js @@ -88,9 +88,10 @@ const actions = { type : 'DELETE_CALENDAR_MODAL', timetable }), - editVehicleJourneyCalendars : (vehicleJourneys) => ({ - type: 'EDIT_VEHICLEJOURNEYS_CALENDARS', - vehicleJourneys + editVehicleJourneyTimetables : (vehicleJourneys, timetables) => ({ + type: 'EDIT_VEHICLEJOURNEYS_TIMETABLES', + vehicleJourneys, + timetables }), openShiftModal : () => ({ type : 'SHIFT_VEHICLEJOURNEY_MODAL' @@ -189,6 +190,9 @@ const actions = { toggleWithoutSchedule: () => ({ type: 'TOGGLE_WITHOUT_SCHEDULE' }), + toggleWithoutTimeTable: () => ({ + type: 'TOGGLE_WITHOUT_TIMETABLE' + }), updateStartTimeFilter: (val, unit) => ({ type: 'UPDATE_START_TIME_FILTER', val, @@ -216,6 +220,12 @@ const actions = { published_name: selectedJP.published_name } }), + filterSelect2VehicleJourney: (selectedVJ) => ({ + type : 'SELECT_VJ_FILTER', + selectedItem: { + objectid: selectedVJ.objectid + } + }), createQueryString: () => ({ type: 'CREATE_QUERY_STRING' }), diff --git a/app/assets/javascripts/es6_browserified/vehicle_journeys/components/Filters.js b/app/assets/javascripts/es6_browserified/vehicle_journeys/components/Filters.js index 6f07dd880..e2d03e195 100644 --- a/app/assets/javascripts/es6_browserified/vehicle_journeys/components/Filters.js +++ b/app/assets/javascripts/es6_browserified/vehicle_journeys/components/Filters.js @@ -1,16 +1,26 @@ var React = require('react') var PropTypes = require('react').PropTypes var MissionSelect2 = require('./tools/select2s/MissionSelect2') +var VJSelect2 = require('./tools/select2s/VJSelect2') var TimetableSelect2 = require('./tools/select2s/TimetableSelect2') -const Filters = ({filters, pagination, onFilter, onResetFilters, onUpdateStartTimeFilter, onUpdateEndTimeFilter, onToggleWithoutSchedule, onSelect2Timetable, onSelect2JourneyPattern}) => { +const Filters = ({filters, pagination, onFilter, onResetFilters, onUpdateStartTimeFilter, onUpdateEndTimeFilter, onToggleWithoutSchedule, onToggleWithoutTimeTable, onSelect2Timetable, onSelect2JourneyPattern, onSelect2VehicleJourney}) => { return ( <div className='row'> <div className='col-lg-12'> <div className='form form-filter'> <div className='ffg-row'> + {/* ID course */} + <div className="form-group w33"> + <VJSelect2 + onSelect2VehicleJourney={onSelect2VehicleJourney} + filters={filters} + isFilter={true} + /> + </div> + {/* Missions */} - <div className='form-group w40'> + <div className='form-group w33'> <MissionSelect2 onSelect2JourneyPattern={onSelect2JourneyPattern} filters={filters} @@ -19,7 +29,7 @@ const Filters = ({filters, pagination, onFilter, onResetFilters, onUpdateStartTi </div> {/* Calendriers */} - <div className='form-group w40'> + <div className='form-group w33'> <TimetableSelect2 onSelect2Timetable={onSelect2Timetable} hasRoute={true} @@ -87,9 +97,9 @@ const Filters = ({filters, pagination, onFilter, onResetFilters, onUpdateStartTi </div> {/* Switch avec/sans horaires */} - <div className='form-group has_switch w40'> - <label className='control-label col-sm-8'>Afficher les courses sans horaires</label> - <div className='form-group col-sm-4' style={{padding: 0}}> + <div className='form-group has_switch'> + <label className='control-label pull-left'>Afficher les courses sans horaires</label> + <div className='form-group pull-left' style={{padding: 0}}> <div className='checkbox'> <label> <input @@ -104,6 +114,25 @@ const Filters = ({filters, pagination, onFilter, onResetFilters, onUpdateStartTi </div> </div> + <div className="ffg-row"> + {/* Switch avec/sans calendrier */} + <div className='form-group has_switch'> + <label className='control-label pull-left'>Afficher les courses sans calendrier</label> + <div className='form-group pull-left' style={{padding: 0}}> + <div className='checkbox'> + <label> + <input + type='checkbox' + onChange={onToggleWithoutTimeTable} + checked={filters.query.withoutTimeTable} + ></input> + <span className='switch-label' data-checkedvalue='Oui' data-uncheckedvalue='Non'></span> + </label> + </div> + </div> + </div> + </div> + {/* Actions */} <div className='actions'> <span @@ -131,7 +160,8 @@ Filters.propTypes = { onUpdateStartTimeFilter: PropTypes.func.isRequired, onUpdateEndTimeFilter: PropTypes.func.isRequired, onSelect2Timetable: PropTypes.func.isRequired, - onSelect2JourneyPattern: PropTypes.func.isRequired + onSelect2JourneyPattern: PropTypes.func.isRequired, + onSelect2VehicleJourney: PropTypes.func.isRequired } module.exports = Filters diff --git a/app/assets/javascripts/es6_browserified/vehicle_journeys/components/Tools.js b/app/assets/javascripts/es6_browserified/vehicle_journeys/components/Tools.js index e486dd155..b417828db 100644 --- a/app/assets/javascripts/es6_browserified/vehicle_journeys/components/Tools.js +++ b/app/assets/javascripts/es6_browserified/vehicle_journeys/components/Tools.js @@ -6,7 +6,7 @@ var ShiftVehicleJourney = require('../containers/tools/ShiftVehicleJourney') var DuplicateVehicleJourney = require('../containers/tools/DuplicateVehicleJourney') var EditVehicleJourney = require('../containers/tools/EditVehicleJourney') var NotesEditVehicleJourney = require('../containers/tools/NotesEditVehicleJourney') -var CalendarsEditVehicleJourney = require('../containers/tools/CalendarsEditVehicleJourney') +var TimetablesEditVehicleJourney = require('../containers/tools/TimetablesEditVehicleJourney') var actions = require('../actions') const Tools = ({vehicleJourneys, onCancelSelection}) => { @@ -17,7 +17,7 @@ const Tools = ({vehicleJourneys, onCancelSelection}) => { <DuplicateVehicleJourney /> <ShiftVehicleJourney /> <EditVehicleJourney /> - <CalendarsEditVehicleJourney /> + <TimetablesEditVehicleJourney /> <NotesEditVehicleJourney /> <DeleteVehicleJourneys /> </ul> diff --git a/app/assets/javascripts/es6_browserified/vehicle_journeys/components/VehicleJourney.js b/app/assets/javascripts/es6_browserified/vehicle_journeys/components/VehicleJourney.js index 6f338f747..de370ac1b 100644 --- a/app/assets/javascripts/es6_browserified/vehicle_journeys/components/VehicleJourney.js +++ b/app/assets/javascripts/es6_browserified/vehicle_journeys/components/VehicleJourney.js @@ -15,6 +15,7 @@ class VehicleJourney extends Component { bool = true this.previousCity = sp.stop_area_cityname } + return bool } @@ -51,9 +52,11 @@ class VehicleJourney extends Component { <div className='th'> <div className='strong mb-xs'>{this.props.value.objectid ? actions.humanOID(this.props.value.objectid) : '-'}</div> <div>{actions.humanOID(this.props.value.journey_pattern.objectid)}</div> - {this.props.value.time_tables.map((tt, i)=> - <div key={i}>{this.timeTableURL(tt)}</div> - )} + <div> + {this.props.value.time_tables.map((tt, i)=> + <span key={i} className='vj_tt'>{this.timeTableURL(tt)}</span> + )} + </div> {(this.props.filters.policy['vehicle_journeys.edit'] == true) && <div className={(this.props.value.deletable ? 'disabled ' : '') + 'checkbox'}> @@ -73,7 +76,7 @@ class VehicleJourney extends Component { </div> {this.props.value.vehicle_journey_at_stops.map((vj, i) => <div key={i} className='td text-center'> - <div className={'cellwrap' + (vj.dummy ? ' headlined' : '') + (this.cityNameChecker(vj) ? ' headlined' : '')}> + <div className={'cellwrap' + (this.cityNameChecker(vj) ? ' headlined' : '')}> {this.props.filters.toggleArrivals && <div data-headline='Arrivée à'> <span className={((this.isDisabled(this.props.value.deletable, vj.dummy) || this.props.filters.policy['vehicle_journeys.edit'] == false) ? 'disabled ' : '') + 'input-group time'}> diff --git a/app/assets/javascripts/es6_browserified/vehicle_journeys/components/tools/CreateModal.js b/app/assets/javascripts/es6_browserified/vehicle_journeys/components/tools/CreateModal.js index 1e121b473..314d481d4 100644 --- a/app/assets/javascripts/es6_browserified/vehicle_journeys/components/tools/CreateModal.js +++ b/app/assets/javascripts/es6_browserified/vehicle_journeys/components/tools/CreateModal.js @@ -71,6 +71,7 @@ class CreateModal extends Component { <div className='form-group'> <label className='control-label is-required'>Nom public de la mission</label> <MissionSelect2 + selection={this.props.modal.modalProps} onSelect2JourneyPattern={this.props.onSelect2JourneyPattern} isFilter={false} /> diff --git a/app/assets/javascripts/es6_browserified/vehicle_journeys/components/tools/CalendarsEditVehicleJourney.js b/app/assets/javascripts/es6_browserified/vehicle_journeys/components/tools/TimetablesEditVehicleJourney.js index e32c873e6..d6c1179ba 100644 --- a/app/assets/javascripts/es6_browserified/vehicle_journeys/components/tools/CalendarsEditVehicleJourney.js +++ b/app/assets/javascripts/es6_browserified/vehicle_journeys/components/tools/TimetablesEditVehicleJourney.js @@ -4,13 +4,13 @@ var PropTypes = require('react').PropTypes var actions = require('../../actions') var TimetableSelect2 = require('./select2s/TimetableSelect2') -class CalendarsEditVehicleJourney extends Component { +class TimetablesEditVehicleJourney extends Component { constructor(props) { super(props) } handleSubmit() { - this.props.onCalendarsEditVehicleJourney(this.props.modal.modalProps.vehicleJourneys) + this.props.onTimetablesEditVehicleJourney(this.props.modal.modalProps.vehicleJourneys, this.props.modal.modalProps.timetables) this.props.onModalClose() $('#CalendarsEditVehicleJourneyModal').modal('hide') } @@ -134,14 +134,14 @@ class CalendarsEditVehicleJourney extends Component { } } -CalendarsEditVehicleJourney.propTypes = { +TimetablesEditVehicleJourney.propTypes = { onOpenCalendarsEditModal: PropTypes.func.isRequired, onModalClose: PropTypes.func.isRequired, - onCalendarsEditVehicleJourney: PropTypes.func.isRequired, + onTimetablesEditVehicleJourney: PropTypes.func.isRequired, onDeleteCalendarModal: PropTypes.func.isRequired, onSelect2Timetable: PropTypes.func.isRequired, onAddSelectedTimetable: PropTypes.func.isRequired, filters: PropTypes.object.isRequired } -module.exports = CalendarsEditVehicleJourney +module.exports = TimetablesEditVehicleJourney diff --git a/app/assets/javascripts/es6_browserified/vehicle_journeys/components/tools/select2s/CompanySelect2.js b/app/assets/javascripts/es6_browserified/vehicle_journeys/components/tools/select2s/CompanySelect2.js index 1f5e5e98f..a6b8dcfa1 100644 --- a/app/assets/javascripts/es6_browserified/vehicle_journeys/components/tools/select2s/CompanySelect2.js +++ b/app/assets/javascripts/es6_browserified/vehicle_journeys/components/tools/select2s/CompanySelect2.js @@ -27,6 +27,7 @@ class BSelect4 extends React.Component{ theme: 'bootstrap', width: '100%', placeholder: 'Filtrer par transporteur...', + language: require('./fr'), ajax: { url: origin + path + '/companies.json' + '?line_id=' + line, dataType: 'json', diff --git a/app/assets/javascripts/es6_browserified/vehicle_journeys/components/tools/select2s/MissionSelect2.js b/app/assets/javascripts/es6_browserified/vehicle_journeys/components/tools/select2s/MissionSelect2.js index 75cbd1f3c..36aaa5cfd 100644 --- a/app/assets/javascripts/es6_browserified/vehicle_journeys/components/tools/select2s/MissionSelect2.js +++ b/app/assets/javascripts/es6_browserified/vehicle_journeys/components/tools/select2s/MissionSelect2.js @@ -13,18 +13,20 @@ class BSelect4 extends React.Component{ super(props) } + render() { return ( <Select2 - data={(this.props.isFilter) ? [this.props.filters.query.journeyPattern.published_name] : undefined} - value={(this.props.isFilter) ? this.props.filters.query.journeyPattern.published_name : undefined} + data={(this.props.isFilter) ? [this.props.filters.query.journeyPattern.published_name] : ((this.props.selection.selectedJPModal) ? [this.props.selection.selectedJPModal.published_name] : undefined)} + value={(this.props.isFilter) ? this.props.filters.query.journeyPattern.published_name : ((this.props.selection.selectedJPModal) ? this.props.selection.selectedJPModal.published_name : undefined) } onSelect={(e) => this.props.onSelect2JourneyPattern(e)} multiple={false} ref='journey_pattern_id' options={{ allowClear: false, theme: 'bootstrap', - placeholder: 'Filtrer par mission...', + placeholder: 'Filtrer par code, nom ou OID de mission...', + language: require('./fr'), width: '100%', ajax: { url: origin + path + '/journey_patterns_collection.json', @@ -32,7 +34,7 @@ class BSelect4 extends React.Component{ delay: '500', data: function(params) { return { - q: {published_name_cont: params.term}, + q: {published_name_or_objectid_or_registration_number_cont: params.term}, }; }, processResults: function(data, params) { @@ -41,14 +43,15 @@ class BSelect4 extends React.Component{ item => _.assign( {}, item, - {text: item.published_name} + { text: '<small><em>Nom: </em></small>' + item.published_name + '<br/><small><em>Code: </em></small>' + item.registration_number + '<br/><small><em>ID: </em></small>' + _.last(_.split(item.object_id, ':')) } ) ) }; }, cache: true }, - minimumInputLength: 3, + minimumInputLength: 2, + escapeMarkup: function (markup) { return markup; }, templateResult: formatRepo }} /> diff --git a/app/assets/javascripts/es6_browserified/vehicle_journeys/components/tools/select2s/TimetableSelect2.js b/app/assets/javascripts/es6_browserified/vehicle_journeys/components/tools/select2s/TimetableSelect2.js index c28d8e06f..b236e7d94 100644 --- a/app/assets/javascripts/es6_browserified/vehicle_journeys/components/tools/select2s/TimetableSelect2.js +++ b/app/assets/javascripts/es6_browserified/vehicle_journeys/components/tools/select2s/TimetableSelect2.js @@ -26,6 +26,7 @@ class BSelect4 extends React.Component{ theme: 'bootstrap', width: '100%', placeholder: 'Filtrer par calendrier...', + language: require('./fr'), ajax: { url: origin + path + this.props.chunkURL, dataType: 'json', diff --git a/app/assets/javascripts/es6_browserified/vehicle_journeys/components/tools/select2s/VJSelect2.js b/app/assets/javascripts/es6_browserified/vehicle_journeys/components/tools/select2s/VJSelect2.js new file mode 100644 index 000000000..cc2ee4b9e --- /dev/null +++ b/app/assets/javascripts/es6_browserified/vehicle_journeys/components/tools/select2s/VJSelect2.js @@ -0,0 +1,68 @@ +var _ = require('lodash') +var React = require('react') +var PropTypes = require('react').PropTypes +var Select2 = require('react-select2') + +// get JSON full path +var origin = window.location.origin +var path = window.location.pathname.split('/', 7).join('/') + + +class BSelect4b extends React.Component{ + constructor(props) { + super(props) + } + humanOID(oid) { + var a = oid.split(':') + return a[a.length - 1] + } + + render() { + return ( + <Select2 + data={(this.props.isFilter) ? [this.props.filters.query.vehicleJourney.objectid] : undefined} + value={(this.props.isFilter) ? this.props.filters.query.vehicleJourney.objectid : undefined} + onSelect={(e) => this.props.onSelect2VehicleJourney(e)} + multiple={false} + ref='vehicle_journey_objectid' + options={{ + allowClear: false, + theme: 'bootstrap', + placeholder: 'Filtrer par ID course...', + width: '100%', + language: require('./fr'), + ajax: { + url: origin + path + '/vehicle_journeys.json', + dataType: 'json', + delay: '500', + data: function(params) { + return { + q: {objectid_cont: params.term}, + }; + }, + processResults: function(data, params) { + return { + results: data.vehicle_journeys.map( + item => _.assign( + {}, + item, + { id: item.objectid, text: _.last(_.split(item.objectid, ':')) } + ) + ) + }; + }, + cache: true + }, + minimumInputLength: 1, + templateResult: formatRepo + }} + /> + ) + } +} + +const formatRepo = (props) => { + if(props.text) return props.text +} + +module.exports = BSelect4b diff --git a/app/assets/javascripts/es6_browserified/vehicle_journeys/components/tools/select2s/fr.js b/app/assets/javascripts/es6_browserified/vehicle_journeys/components/tools/select2s/fr.js new file mode 100644 index 000000000..20154d412 --- /dev/null +++ b/app/assets/javascripts/es6_browserified/vehicle_journeys/components/tools/select2s/fr.js @@ -0,0 +1,9 @@ +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/assets/javascripts/es6_browserified/vehicle_journeys/containers/Filters.js b/app/assets/javascripts/es6_browserified/vehicle_journeys/containers/Filters.js index 7570dd466..831b47ca1 100644 --- a/app/assets/javascripts/es6_browserified/vehicle_journeys/containers/Filters.js +++ b/app/assets/javascripts/es6_browserified/vehicle_journeys/containers/Filters.js @@ -22,6 +22,9 @@ const mapDispatchToProps = (dispatch) => { onToggleWithoutSchedule: () =>{ dispatch(actions.toggleWithoutSchedule()) }, + onToggleWithoutTimeTable: () =>{ + dispatch(actions.toggleWithoutTimeTable()) + }, onResetFilters: (e, pagination) =>{ dispatch(actions.checkConfirmModal(e, actions.resetFilters(dispatch), pagination.stateChanged, dispatch)) }, @@ -33,6 +36,9 @@ const mapDispatchToProps = (dispatch) => { }, onSelect2JourneyPattern: (e) => { dispatch(actions.filterSelect2JourneyPattern(e.params.data)) + }, + onSelect2VehicleJourney: (e) => { + dispatch(actions.filterSelect2VehicleJourney(e.params.data)) } } } diff --git a/app/assets/javascripts/es6_browserified/vehicle_journeys/containers/tools/CalendarsEditVehicleJourney.js b/app/assets/javascripts/es6_browserified/vehicle_journeys/containers/tools/TimetablesEditVehicleJourney.js index 130acb017..6d0096019 100644 --- a/app/assets/javascripts/es6_browserified/vehicle_journeys/containers/tools/CalendarsEditVehicleJourney.js +++ b/app/assets/javascripts/es6_browserified/vehicle_journeys/containers/tools/TimetablesEditVehicleJourney.js @@ -1,5 +1,5 @@ var connect = require('react-redux').connect -var CalendarsEditComponent = require('../../components/tools/CalendarsEditVehicleJourney') +var TimetablesEditComponent = require('../../components/tools/TimetablesEditVehicleJourney') var actions = require('../../actions') const mapStateToProps = (state) => { @@ -22,8 +22,8 @@ const mapDispatchToProps = (dispatch) => { onDeleteCalendarModal: (timetable) => { dispatch(actions.deleteCalendarModal(timetable)) }, - onCalendarsEditVehicleJourney: (calendars) =>{ - dispatch(actions.editVehicleJourneyCalendars(calendars)) + onTimetablesEditVehicleJourney: (vehicleJourneys, timetables) =>{ + dispatch(actions.editVehicleJourneyTimetables(vehicleJourneys, timetables)) }, onSelect2Timetable: (e) =>{ dispatch(actions.selectTTCalendarsModal(e.params.data)) @@ -34,6 +34,6 @@ const mapDispatchToProps = (dispatch) => { } } -const CalendarsEditVehicleJourney = connect(mapStateToProps, mapDispatchToProps)(CalendarsEditComponent) +const TimetablesEditVehicleJourney = connect(mapStateToProps, mapDispatchToProps)(TimetablesEditComponent) -module.exports = CalendarsEditVehicleJourney +module.exports = TimetablesEditVehicleJourney diff --git a/app/assets/javascripts/es6_browserified/vehicle_journeys/index.js b/app/assets/javascripts/es6_browserified/vehicle_journeys/index.js index 4c9423c1f..489446ab9 100644 --- a/app/assets/javascripts/es6_browserified/vehicle_journeys/index.js +++ b/app/assets/javascripts/es6_browserified/vehicle_journeys/index.js @@ -8,10 +8,10 @@ var actions = require("./actions") var enableBatching = require('./batch').enableBatching // logger, DO NOT REMOVE -// var applyMiddleware = require('redux').applyMiddleware -// var createLogger = require('redux-logger') -// var thunkMiddleware = require('redux-thunk').default -// var promise = require('redux-promise') +var applyMiddleware = require('redux').applyMiddleware +var createLogger = require('redux-logger') +var thunkMiddleware = require('redux-thunk').default +var promise = require('redux-promise') var selectedJP = [] @@ -38,13 +38,17 @@ var initialState = { journeyPattern: { published_name: '' }, + vehicleJourney: { + objectid: '' + }, company: { name: '' }, timetable: { comment: '' }, - withoutSchedule: true + withoutSchedule: true, + withoutTimeTable: false } }, @@ -76,16 +80,17 @@ if (window.jpOrigin){ } let params = { 'q[journey_pattern_id_eq]': initialState.filters.query.journeyPattern.id, + 'q[objectid_cont]': initialState.filters.query.vehicleJourney.objectid } initialState.filters.queryString = actions.encodeParams(params) } -// const loggerMiddleware = createLogger() +const loggerMiddleware = createLogger() let store = createStore( enableBatching(vehicleJourneysApp), - initialState - // applyMiddleware(thunkMiddleware, promise, loggerMiddleware) + initialState, + applyMiddleware(thunkMiddleware, promise, loggerMiddleware) ) render( diff --git a/app/assets/javascripts/es6_browserified/vehicle_journeys/reducers/filters.js b/app/assets/javascripts/es6_browserified/vehicle_journeys/reducers/filters.js index 80b62c6b4..09588f824 100644 --- a/app/assets/javascripts/es6_browserified/vehicle_journeys/reducers/filters.js +++ b/app/assets/javascripts/es6_browserified/vehicle_journeys/reducers/filters.js @@ -15,11 +15,14 @@ const filters = (state = {}, action) => { minute: '59' } } - newQuery = _.assign({}, state.query, {interval: interval, journeyPattern: {}, timetable: {}, withoutSchedule: true }) + newQuery = _.assign({}, state.query, {interval: interval, journeyPattern: {}, vehicleJourney: {}, timetable: {}, withoutSchedule: true, withoutTimeTable: false }) return _.assign({}, state, {query: newQuery, queryString: ''}) case 'TOGGLE_WITHOUT_SCHEDULE': newQuery = _.assign({}, state.query, {withoutSchedule: !state.query.withoutSchedule}) return _.assign({}, state, {query: newQuery}) + case 'TOGGLE_WITHOUT_TIMETABLE': + newQuery = _.assign({}, state.query, {withoutTimeTable: !state.query.withoutTimeTable}) + return _.assign({}, state, {query: newQuery}) case 'UPDATE_END_TIME_FILTER': newInterval = JSON.parse(JSON.stringify(state.query.interval)) newInterval.end[action.unit] = actions.pad(action.val, action.unit) @@ -44,6 +47,9 @@ const filters = (state = {}, action) => { case 'SELECT_JP_FILTER': newQuery = _.assign({}, state.query, {journeyPattern : action.selectedItem}) return _.assign({}, state, {query: newQuery}) + case 'SELECT_VJ_FILTER': + newQuery = _.assign({}, state.query, {vehicleJourney : action.selectedItem}) + return _.assign({}, state, {query: newQuery}) case 'TOGGLE_ARRIVALS': return _.assign({}, state, {toggleArrivals: !state.toggleArrivals}) case 'QUERY_FILTER_VEHICLEJOURNEYS': @@ -52,11 +58,12 @@ const filters = (state = {}, action) => { case 'CREATE_QUERY_STRING': let params = { 'q[journey_pattern_id_eq]': state.query.journeyPattern.id || undefined, + 'q[objectid_cont]': state.query.vehicleJourney.objectid || undefined, 'q[time_tables_id_eq]': state.query.timetable.id || undefined, - // Fixme 3358 - // 'q[vehicle_journey_at_stops_departure_time_gteq]': (state.query.interval.start.hour + ':' + state.query.interval.start.minute), - // 'q[vehicle_journey_at_stops_departure_time_lteq]': (state.query.interval.end.hour + ':' + state.query.interval.end.minute), - // 'q[vehicle_journey_without_departure_time]' : state.query.withoutSchedule + 'q[vehicle_journey_at_stops_departure_time_gteq]': (state.query.interval.start.hour + ':' + state.query.interval.start.minute), + 'q[vehicle_journey_at_stops_departure_time_lteq]': (state.query.interval.end.hour + ':' + state.query.interval.end.minute), + 'q[vehicle_journey_without_departure_time]': state.query.withoutSchedule, + 'q[vehicle_journey_without_time_table]': state.query.withoutTimeTable } let queryString = actions.encodeParams(params) return _.assign({}, state, {queryString: queryString}) diff --git a/app/assets/javascripts/es6_browserified/vehicle_journeys/reducers/modal.js b/app/assets/javascripts/es6_browserified/vehicle_journeys/reducers/modal.js index 3b13ab9de..e504c2531 100644 --- a/app/assets/javascripts/es6_browserified/vehicle_journeys/reducers/modal.js +++ b/app/assets/javascripts/es6_browserified/vehicle_journeys/reducers/modal.js @@ -63,17 +63,6 @@ const modal = (state = {}, action) => { case 'ADD_SELECTED_TIMETABLE': if(state.modalProps.selectedTimetable){ newModalProps = JSON.parse(JSON.stringify(state.modalProps)) - newModalProps.vehicleJourneys.map((vj) => { - let isPresent = false - vj.time_tables.forEach((tt) =>{ - if (_.isEqual(newModalProps.selectedTimetable.objectid, tt.objectid)){ - isPresent = true - } - }) - if (!isPresent){ - vj.time_tables.push(newModalProps.selectedTimetable) - } - }) if (!_.find(newModalProps.timetables, newModalProps.selectedTimetable)){ newModalProps.timetables.push(newModalProps.selectedTimetable) } diff --git a/app/assets/javascripts/es6_browserified/vehicle_journeys/reducers/vehicleJourneys.js b/app/assets/javascripts/es6_browserified/vehicle_journeys/reducers/vehicleJourneys.js index 2db76deae..d153739ce 100644 --- a/app/assets/javascripts/es6_browserified/vehicle_journeys/reducers/vehicleJourneys.js +++ b/app/assets/javascripts/es6_browserified/vehicle_journeys/reducers/vehicleJourneys.js @@ -1,7 +1,7 @@ var _ = require('lodash') var actions = require("../actions") -const vehicleJourney= (state = {}, action) => { +const vehicleJourney= (state = {}, action, keep) => { switch (action.type) { case 'SELECT_VEHICLEJOURNEY': return _.assign({}, state, {selected: !state.selected}) @@ -61,10 +61,14 @@ const vehicleJourney= (state = {}, action) => { actions.checkSchedules(shiftedSchedule) shiftedVjas = _.assign({}, state.vehicle_journey_at_stops[i], shiftedSchedule) vjas = _.assign({}, state.vehicle_journey_at_stops[i], shiftedVjas) - delete vjas['id'] + if(!keep){ + delete vjas['id'] + } return vjas }else { - delete vjas['id'] + if(!keep){ + delete vjas['id'] + } return vjas } }) @@ -147,13 +151,14 @@ const vehicleJourneys = (state = [], action) => { return vj } }) - case 'EDIT_VEHICLEJOURNEYS_CALENDARS': + case 'EDIT_VEHICLEJOURNEYS_TIMETABLES': + let newTimetables = JSON.parse(JSON.stringify(action.timetables)) return state.map((vj,i) =>{ if(vj.selected){ let updatedVJ = _.assign({}, vj) action.vehicleJourneys.map((vjm, j) =>{ if(vj.objectid == vjm.objectid){ - updatedVJ.time_tables = vjm.time_tables + updatedVJ.time_tables = newTimetables } }) return updatedVJ @@ -164,7 +169,7 @@ const vehicleJourneys = (state = [], action) => { case 'SHIFT_VEHICLEJOURNEY': return state.map((vj, i) => { if (vj.selected){ - return vehicleJourney(vj, action) + return vehicleJourney(vj, action, true) }else{ return vj } @@ -179,7 +184,7 @@ const vehicleJourneys = (state = [], action) => { selectedIndex = i for (i = 0; i< action.data.duplicate_number.value; i++){ action.data.additional_time.value = val * (i + 1) - dupeVj = vehicleJourney(vj, action) + dupeVj = vehicleJourney(vj, action, false) dupeVj.published_journey_name = dupeVj.published_journey_name + '-' + i dupeVj.selected = false delete dupeVj['objectid'] diff --git a/app/assets/javascripts/select2.coffee b/app/assets/javascripts/select2.coffee index 1870f7f9a..af3dc6d75 100644 --- a/app/assets/javascripts/select2.coffee +++ b/app/assets/javascripts/select2.coffee @@ -1,19 +1,42 @@ +bind_select2 = (el, cfg = {}) -> + target = $(el) + default_cfg = + theme: 'bootstrap' + language: 'fr' + placeholder: target.data('select2ed-placeholder') + allowClear: false + + target.select2 $.extend({}, default_cfg, cfg) + +bind_select2_ajax = (el, cfg = {}) -> + target = $(el) + cfg = + ajax: + data: (params) -> + q: + "#{target.data('term')}": params.term + url: target.data('url'), + dataType: 'json', + delay: 125, + processResults: (data, params) -> results: data + minimumInputLength: 3 + templateResult: (item) -> + item.text + templateSelection: (item) -> + item.text + + bind_select2(el, cfg) + @select_2 = -> $("[data-select2ed='true']").each -> - target = $(this) - target.select2 - theme: 'bootstrap' - language: 'fr' - placeholder: target.data('select2ed-placeholder') - allowClear: true + bind_select2(this) + + $("[data-select2-ajax='true']").each -> + bind_select2_ajax(this) $('select.form-control.tags').each -> - target = $(this) - target.select2 - theme: 'bootstrap' - language: 'fr' - allowClear: true - tags: true + bind_select2(this, {tags: true}) + $(document).on 'turbolinks:load', select_2 diff --git a/app/assets/javascripts/time_table_combinations.coffee b/app/assets/javascripts/time_table_combinations.coffee new file mode 100644 index 000000000..8923af958 --- /dev/null +++ b/app/assets/javascripts/time_table_combinations.coffee @@ -0,0 +1,6 @@ +@combinedTypeToggle = -> + $('#time_table_combination_combined_type').on 'click', -> + $(this).closest('.has_switch').siblings('.form-group').each -> + $(this).toggleClass('hidden') + +$(document).on 'turbolinks:load', combinedTypeToggle diff --git a/app/assets/stylesheets/OpenLayers/custom.sass b/app/assets/stylesheets/OpenLayers/custom.sass index a13f6c831..7a5b4baf1 100644 --- a/app/assets/stylesheets/OpenLayers/custom.sass +++ b/app/assets/stylesheets/OpenLayers/custom.sass @@ -1,5 +1,64 @@ .map - // ... - .list-group-item & margin-top: 15px + + + .ol-scale-line + background-color: transparent + bottom: 5px + right: 5px + left: auto + padding: 3px + position: absolute + + .ol-scale-line-inner + position: relative + border: none + border-bottom: 2px solid $darkblue + border-top: none + color: $darkblue + font-size: 0.85em + text-align: center + font-weight: 700 + + &:before + content: '' + display: block + position: absolute + left: 0 + bottom: 0 + right: 0 + height: 5px + border-left: 2px solid $darkblue + border-right: 2px solid $darkblue + + .ol-zoom + background-color: transparent + + .ol-zoom-in, .ol-zoom-out + background-color: $darkblue + color: #fff + border-radius: 3px + margin: 0 + + .ol-zoom-in + border-bottom-left-radius: 0 + border-bottom-right-radius: 0 + + .ol-zoom-out + margin-top: 1px + border-top-left-radius: 0 + border-top-right-radius: 0 + + .ol-zoomslider + margin: 2px + width: calc(1.375em + 4px) + background-color: rgba($darkblue, 0.25) + + button.ol-zoomslider-thumb + margin: 0 -2px 0 -2px + padding: 0 2px + background-color: $darkblue + color: #fff + border-radius: 0 + z-index: 5 diff --git a/app/assets/stylesheets/OpenLayers/ol.css b/app/assets/stylesheets/OpenLayers/ol.css index ea50e7ebe..02c19f3af 100644 --- a/app/assets/stylesheets/OpenLayers/ol.css +++ b/app/assets/stylesheets/OpenLayers/ol.css @@ -1 +1,226 @@ -.ol-control,.ol-scale-line{position:absolute;padding:2px}.ol-box{box-sizing:border-box;border-radius:2px;border:2px solid #00f}.ol-mouse-position{top:8px;right:8px;position:absolute}.ol-scale-line{background:rgba(0,60,136,.3);border-radius:4px;bottom:8px;left:8px}.ol-scale-line-inner{border:1px solid #eee;border-top:none;color:#eee;font-size:10px;text-align:center;margin:1px;will-change:contents,width}.ol-overlay-container{will-change:left,right,top,bottom}.ol-unsupported{display:none}.ol-viewport .ol-unselectable{-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-tap-highlight-color:transparent}.ol-control{background-color:rgba(255,255,255,.4);border-radius:4px}.ol-control:hover{background-color:rgba(255,255,255,.6)}.ol-zoom{top:.5em;left:.5em}.ol-rotate{top:.5em;right:.5em;transition:opacity .25s linear,visibility 0s linear}.ol-rotate.ol-hidden{opacity:0;visibility:hidden;transition:opacity .25s linear,visibility 0s linear .25s}.ol-zoom-extent{top:4.643em;left:.5em}.ol-full-screen{right:.5em;top:.5em}@media print{.ol-control{display:none}}.ol-control button{display:block;margin:1px;padding:0;color:#fff;font-size:1.14em;font-weight:700;text-decoration:none;text-align:center;height:1.375em;width:1.375em;line-height:.4em;background-color:rgba(0,60,136,.5);border:none;border-radius:2px}.ol-control button::-moz-focus-inner{border:none;padding:0}.ol-zoom-extent button{line-height:1.4em}.ol-compass{display:block;font-weight:400;font-size:1.2em;will-change:transform}.ol-touch .ol-control button{font-size:1.5em}.ol-touch .ol-zoom-extent{top:5.5em}.ol-control button:focus,.ol-control button:hover{text-decoration:none;background-color:rgba(0,60,136,.7)}.ol-zoom .ol-zoom-in{border-radius:2px 2px 0 0}.ol-zoom .ol-zoom-out{border-radius:0 0 2px 2px}.ol-attribution{text-align:right;bottom:.5em;right:.5em;max-width:calc(100% - 1.3em)}.ol-attribution ul{margin:0;padding:0 .5em;font-size:.7rem;line-height:1.375em;color:#000;text-shadow:0 0 2px #fff}.ol-attribution li{display:inline;list-style:none;line-height:inherit}.ol-attribution li:not(:last-child):after{content:" "}.ol-attribution img{max-height:2em;max-width:inherit;vertical-align:middle}.ol-attribution button,.ol-attribution ul{display:inline-block}.ol-attribution.ol-collapsed ul{display:none}.ol-attribution.ol-logo-only ul{display:block}.ol-attribution:not(.ol-collapsed){background:rgba(255,255,255,.8)}.ol-attribution.ol-uncollapsible{bottom:0;right:0;border-radius:4px 0 0;height:1.1em;line-height:1em}.ol-attribution.ol-logo-only{background:0 0;bottom:.4em;height:1.1em;line-height:1em}.ol-attribution.ol-uncollapsible img{margin-top:-.2em;max-height:1.6em}.ol-attribution.ol-logo-only button,.ol-attribution.ol-uncollapsible button{display:none}.ol-zoomslider{top:4.5em;left:.5em;height:200px}.ol-zoomslider button{position:relative;height:10px}.ol-touch .ol-zoomslider{top:5.5em}.ol-overviewmap{left:.5em;bottom:.5em}.ol-overviewmap.ol-uncollapsible{bottom:0;left:0;border-radius:0 4px 0 0}.ol-overviewmap .ol-overviewmap-map,.ol-overviewmap button{display:inline-block}.ol-overviewmap .ol-overviewmap-map{border:1px solid #7b98bc;height:150px;margin:2px;width:150px}.ol-overviewmap:not(.ol-collapsed) button{bottom:1px;left:2px;position:absolute}.ol-overviewmap.ol-collapsed .ol-overviewmap-map,.ol-overviewmap.ol-uncollapsible button{display:none}.ol-overviewmap:not(.ol-collapsed){background:rgba(255,255,255,.8)}.ol-overviewmap-box{border:2px dotted rgba(0,60,136,.7)}
\ No newline at end of file +.ol-control,.ol-scale-line { + position:absolute; + padding:2px +} +.ol-box { + box-sizing:border-box; + border-radius:2px; + border:2px solid #00f +} +.ol-mouse-position { + top:8px; + right:8px; + position:absolute +} +.ol-scale-line { + background:rgba(0,60,136,.3); + border-radius:4px; + bottom:8px; + left:8px +} +.ol-scale-line-inner { + border:1px solid #eee; + border-top:none; + color:#eee; + font-size:10px; + text-align:center; + margin:1px; + will-change:contents,width +} +.ol-overlay-container { + will-change:left,right,top,bottom +} +.ol-unsupported { + display:none +} +.ol-viewport .ol-unselectable { + -webkit-touch-callout:none; + -webkit-user-select:none; + -khtml-user-select:none; + -moz-user-select:none; + -ms-user-select:none; + user-select:none; + -webkit-tap-highlight-color:transparent +} +.ol-control { + background-color:rgba(255,255,255,.4); + border-radius:4px +} +.ol-control:hover { + background-color:rgba(255,255,255,.6) +} +.ol-zoom { + top:.5em; + left:.5em +} +.ol-rotate{ + top:.5em; + right:.5em; + transition:opacity .25s linear,visibility 0s linear +} +.ol-rotate.ol-hidden { + opacity:0; + visibility:hidden; + transition:opacity .25s linear,visibility 0s linear .25s +} +.ol-zoom-extent { + top:4.643em; + left:.5em +} +.ol-full-screen { + right:.5em; + top:.5em +} +@media print{ + .ol-control{ + display:none + } +} +.ol-control button { + display:block; + margin:1px; + padding:0; + color:#fff; + font-size:1.14em; + font-weight:700; + text-decoration:none; + text-align:center; + height:1.375em; + width:1.375em; + line-height:.4em; + background-color:rgba(0,60,136,.5); + border:none; + border-radius:2px +} +.ol-control button::-moz-focus-inner { + border:none; + padding:0 +} +.ol-zoom-extent button { + line-height:1.4em +} +.ol-compass { + display:block; + font-weight:400; + font-size:1.2em; + will-change:transform +} +.ol-touch .ol-control button { + font-size:1.5em +} +.ol-touch .ol-zoom-extent { + top:5.5em +} +.ol-control button:focus,.ol-control button:hover { + text-decoration:none; + background-color:rgba(0,60,136,.7) +} +.ol-zoom .ol-zoom-in { + border-radius:2px 2px 0 0 +} +.ol-zoom .ol-zoom-out { + border-radius:0 0 2px 2px +} +.ol-attribution { + text-align:right; + bottom:.5em; + right:.5em; + max-width:calc(100% - 1.3em) +} +.ol-attribution ul { + margin:0; + padding:0 .5em; + font-size:.7rem; + line-height:1.375em; + color:#000; + text-shadow:0 0 2px #fff +} +.ol-attribution li { + display:inline; + list-style:none; + line-height:inherit +} +.ol-attribution li:not(:last-child):after { + content:" " +} +.ol-attribution img { + max-height:2em; + max-width:inherit; + vertical-align:middle +} +.ol-attribution button,.ol-attribution ul { + display:inline-block +} +.ol-attribution.ol-collapsed ul { + display:none +} +.ol-attribution.ol-logo-only ul { + display:block +} +.ol-attribution:not(.ol-collapsed) { + background:rgba(255,255,255,.8) +} +.ol-attribution.ol-uncollapsible { + bottom:0; + right:0; + border-radius:4px 0 0; + height:1.1em; + line-height:1em +} +.ol-attribution.ol-logo-only { + background:0 0; + bottom:.4em; + height:1.1em; + line-height:1em +} +.ol-attribution.ol-uncollapsible img { + margin-top:-.2em; + max-height:1.6em +} +.ol-attribution.ol-logo-only button,.ol-attribution.ol-uncollapsible button { + display:none +} +.ol-zoomslider { + top:4.5em; + left:.5em; + height:200px +} +.ol-zoomslider button { + position:relative; + height:10px +} +.ol-touch .ol-zoomslider { + top:5.5em +} +.ol-overviewmap { + left:.5em; + bottom:.5em +} +.ol-overviewmap.ol-uncollapsible { + bottom:0; + left:0; + border-radius:0 4px 0 0 +} +.ol-overviewmap .ol-overviewmap-map,.ol-overviewmap button { + display:inline-block +} +.ol-overviewmap .ol-overviewmap-map { + border:1px solid #7b98bc; + height:150px; + margin:2px; + width:150px +} +.ol-overviewmap:not(.ol-collapsed) button { + bottom:1px; + left:2px; + position:absolute +} +.ol-overviewmap.ol-collapsed .ol-overviewmap-map,.ol-overviewmap.ol-uncollapsible button { + display:none +} +.ol-overviewmap:not(.ol-collapsed) { + background:rgba(255,255,255,.8) +} +.ol-overviewmap-box { + border:2px dotted rgba(0,60,136,.7) +} diff --git a/app/assets/stylesheets/application.sass b/app/assets/stylesheets/application.sass index 47cccba65..e3f776640 100644 --- a/app/assets/stylesheets/application.sass +++ b/app/assets/stylesheets/application.sass @@ -5,6 +5,8 @@ @import 'base/config' @import 'base/utilities' +@import 'OpenLayers/custom' + @import 'typography/fonts' @import 'typography/sboiv' @import 'typography/typography' diff --git a/app/assets/stylesheets/base.sass b/app/assets/stylesheets/base.sass index 3035fd64c..83d92076c 100644 --- a/app/assets/stylesheets/base.sass +++ b/app/assets/stylesheets/base.sass @@ -13,3 +13,6 @@ // Select2 @import 'select2' @import 'select2-bootstrap' + +// OL3 +@import 'OpenLayers/ol' diff --git a/app/assets/stylesheets/components/_buttons.sass b/app/assets/stylesheets/components/_buttons.sass index fd3561dc8..a59699383 100644 --- a/app/assets/stylesheets/components/_buttons.sass +++ b/app/assets/stylesheets/components/_buttons.sass @@ -181,6 +181,7 @@ table, .table right: auto &:last-child - > .th > .btn-group .dropdown-menu - left: auto - right: 0 + &:not(:first-child) + > .th > .btn-group .dropdown-menu + left: auto + right: 0 diff --git a/app/assets/stylesheets/components/_forms.sass b/app/assets/stylesheets/components/_forms.sass index 46f40291a..7a5323011 100644 --- a/app/assets/stylesheets/components/_forms.sass +++ b/app/assets/stylesheets/components/_forms.sass @@ -337,7 +337,7 @@ table, .table + .switch-label position: relative display: inline-block - width: 50px + width: 100% /* 50px */ height: 30px padding-left: 60px line-height: 30px diff --git a/app/assets/stylesheets/components/_olMap.sass b/app/assets/stylesheets/components/_olMap.sass index acbbbeade..9aaad78dc 100644 --- a/app/assets/stylesheets/components/_olMap.sass +++ b/app/assets/stylesheets/components/_olMap.sass @@ -35,30 +35,3 @@ .map height: 300px width: 100% - -.ol-scale-line - // background-color: rgba($blue, 0.5) - bottom: 5px - right: 5px - padding: 3px - position: absolute - - .ol-scale-line-inner - position: relative - border-bottom: 1px solid $darkgrey - border-top: none - color: $blue - font-size: 0.85em - text-align: center - font-weight: 700 - - &:before - content: '' - display: block - position: absolute - left: 0 - bottom: 0 - right: 0 - height: 5px - border-left: 1px solid $darkgrey - border-right: 1px solid $darkgrey diff --git a/app/assets/stylesheets/components/_select2.sass b/app/assets/stylesheets/components/_select2.sass index cbb3a80da..960e8b10b 100644 --- a/app/assets/stylesheets/components/_select2.sass +++ b/app/assets/stylesheets/components/_select2.sass @@ -2,9 +2,15 @@ // Select2 Customisation // //-----------------------// +// Dirty hotfix for select2 i18n not working properly +// .select2-results__options +// .select2-results__message, .loading-results +// display: none + .select2-selection__placeholder color: rgba($grey, 0.65) font-style: italic + padding: 0 6px // With Font Awesome adjusts. .select2-selection__rendered @@ -66,3 +72,10 @@ .select2-container--bootstrap .select2-selection border-color: rgba($grey, 0.3) + +// Tags +.form-filter + .form-group.select2ed + .form-control.tags + .select2-container--bootstrap + input.select2-search__field + padding: 0 6px diff --git a/app/assets/stylesheets/components/_tables.sass b/app/assets/stylesheets/components/_tables.sass index bc04b49e3..0cd91a1a8 100644 --- a/app/assets/stylesheets/components/_tables.sass +++ b/app/assets/stylesheets/components/_tables.sass @@ -218,8 +218,9 @@ vertical-align: top overflow: auto white-space: nowrap - border-right: 1px solid rgba($grey, 0.5) + // border-right: 1px solid rgba($grey, 0.5) max-width: 100% + min-width: 280px padding-right: 1px .t2e-item @@ -231,20 +232,13 @@ &:first-child border-left: none + &:last-child + border-right: 1px solid rgba($grey, 0.5) .th > *:first-child padding-right: 30px - .td - > div - // > .has_radio - // top: 50% - // margin-top: -10px - // - // &.headlined > .has_radio - // margin-top: calc((1.4em + 6px) * -1) - .th + .td, .td:first-child > .headlined [data-headline] diff --git a/app/assets/stylesheets/modules/_routes_stopoints.sass b/app/assets/stylesheets/modules/_routes_stopoints.sass index 88e662849..fd62cee2a 100644 --- a/app/assets/stylesheets/modules/_routes_stopoints.sass +++ b/app/assets/stylesheets/modules/_routes_stopoints.sass @@ -48,7 +48,7 @@ + .map_container > .map_metas position: relative - padding-left: 25px + padding-left: 30px &:before content: '' @@ -60,7 +60,7 @@ z-index: 3 top: 0 bottom: 0 - left: 5px + left: 10px &:last-child > .wrapper:last-child > div:first-child span:after diff --git a/app/assets/stylesheets/modules/_vj_collection.sass b/app/assets/stylesheets/modules/_vj_collection.sass index 8ff983310..56769e52b 100644 --- a/app/assets/stylesheets/modules/_vj_collection.sass +++ b/app/assets/stylesheets/modules/_vj_collection.sass @@ -105,6 +105,13 @@ .t2e-item position: relative + .th .vj_tt + display: inline-block + vertical-align: top + + + .vj_tt + margin-left: 5px + &.has-error &:before content: '' diff --git a/app/controllers/autocomplete_calendars_controller.rb b/app/controllers/autocomplete_calendars_controller.rb new file mode 100644 index 000000000..dbdd1a9fc --- /dev/null +++ b/app/controllers/autocomplete_calendars_controller.rb @@ -0,0 +1,7 @@ +class AutocompleteCalendarsController < ApplicationController + respond_to :json, :only => [:autocomplete] + + def autocomplete + @calendars = Calendar.search(params[:q]).result.paginate(page: params[:page]) + end +end diff --git a/app/controllers/time_table_combinations_controller.rb b/app/controllers/time_table_combinations_controller.rb index ebae9f225..bbb262247 100644 --- a/app/controllers/time_table_combinations_controller.rb +++ b/app/controllers/time_table_combinations_controller.rb @@ -1,46 +1,26 @@ class TimeTableCombinationsController < ChouetteController - respond_to :js, :only => [:new,:create] - belongs_to :referential do - belongs_to :time_table, :parent_class => Chouette::TimeTable - end - after_filter :clean_flash - - def clean_flash - # only run this in case it's an Ajax request. - return unless request.xhr? - flash.discard + belongs_to :time_table, :parent_class => Chouette::TimeTable end def new - @time_table_combination = TimeTableCombination.new(:source_id => parent.id) - render :action => :new + @combination = TimeTableCombination.new(source_id: parent.id) end - def create - Rails.logger.warn( params.inspect) - @time_table_combination = TimeTableCombination.new( params[:time_table_combination].merge( :source_id => parent.id)) - Rails.logger.warn( @time_table_combination.inspect) - @year = params[:year] ? params[:year].to_i : Date.today.cwyear - if @time_table_combination.valid? - begin - @time_table = @time_table_combination.combine - flash[:notice] = t('time_table_combinations.success') - render "create_success" - rescue => e - Rails.logger.error( "TimeTableCombination error, @time_table_combination=#{@time_table_combination.inspect}") - Rails.logger.error( e.inspect) - flash[:error] = t('time_table_combinations.failure') - render "create_failure" - end - else - render "create_failure" - end - + @combination = TimeTableCombination.new(params[:time_table_combination].merge(source_id: parent.id)) + @combination.valid? ? perform_combination : render(:new) end - protected - alias_method :time_table_combination, :resource - + def perform_combination + begin + @time_table = @combination.combine + flash[:notice] = t('time_table_combinations.success') + redirect_to referential_time_table_path(referential, @time_table) + rescue => e + flash[:notice] = e.message + flash[:error] = t('time_table_combinations.failure') + render :new + end + end end diff --git a/app/controllers/time_tables_controller.rb b/app/controllers/time_tables_controller.rb index 8436dc020..dcf8f3e5e 100644 --- a/app/controllers/time_tables_controller.rb +++ b/app/controllers/time_tables_controller.rb @@ -95,6 +95,15 @@ class TimeTablesController < ChouetteController render :new end + def actualize + @time_table = resource + if @time_table.calendar + @time_table.actualize + flash[:notice] = t('.success') + end + redirect_to referential_time_table_path @referential, @time_table + end + def tags @tags = ActsAsTaggableOn::Tag.where("tags.name LIKE ?", "%#{params[:tag]}%") respond_to do |format| @@ -112,8 +121,8 @@ class TimeTablesController < ChouetteController scope = select_time_tables.tagged_with(tags, :wild => true, :any => true) if tags.any? end scope = ransack_periode(scope) - @q = scope.search(params[:q]) + if sort_column && sort_direction @time_tables ||= @q.result(:distinct => true).order("#{sort_column} #{sort_direction}") else @@ -139,24 +148,29 @@ class TimeTablesController < ChouetteController end private - # Fake ransack filter def ransack_periode scope return scope unless params[:q] - periode = params[:q] - return scope if periode['end_date_lteq(1i)'].empty? || periode['start_date_gteq(1i)'].empty? + return scope unless params[:q]['end_date_lteq(1i)'].present? - begin_range = Date.civil(periode["start_date_gteq(1i)"].to_i, periode["start_date_gteq(2i)"].to_i, periode["start_date_gteq(3i)"].to_i) - end_range = Date.civil(periode["end_date_lteq(1i)"].to_i, periode["end_date_lteq(2i)"].to_i, periode["end_date_lteq(3i)"].to_i) + begin_range = flatten_date('start_date_gteq') + end_range = flatten_date('end_date_lteq') if begin_range > end_range flash.now[:error] = t('referentials.errors.validity_period') else + scope = scope.overlapping(begin_range, end_range) + params[:q] = params[:q].slice('comment_cont', 'color_cont_any') @begin_range = begin_range @end_range = end_range end scope end + def flatten_date key + date_int = %w(1 2 3).map {|e| params[:q]["#{key}(#{e}i)"].to_i } + Date.new(*date_int) + end + def sort_column referential.time_tables.column_names.include?(params[:sort]) ? params[:sort] : 'comment' end diff --git a/app/controllers/vehicle_journeys_controller.rb b/app/controllers/vehicle_journeys_controller.rb index cca75af5b..71787ba78 100644 --- a/app/controllers/vehicle_journeys_controller.rb +++ b/app/controllers/vehicle_journeys_controller.rb @@ -3,7 +3,7 @@ class VehicleJourneysController < ChouetteController before_action :user_permissions, only: :index respond_to :json, :only => :index - respond_to :js, :only => [:select_journey_pattern, :edit, :new, :index] + respond_to :js, :only => [:select_journey_pattern, :select_vehicle_journey, :edit, :new, :index] belongs_to :referential do belongs_to :line, :parent_class => Chouette::Line do @@ -18,12 +18,17 @@ class VehicleJourneysController < ChouetteController def select_journey_pattern if params[:journey_pattern_id] - selected_journey_pattern = Chouette::JourneyPattern.find( params[:journey_pattern_id]) + selected_journey_pattern = Chouette::JourneyPattern.find(params[:journey_pattern_id]) @vehicle_journey = vehicle_journey @vehicle_journey.update_journey_pattern(selected_journey_pattern) end end + def select_vehicle_journey + if params[:vehicle_journey_objectid] + @vehicle_journey = Chouette::VehicleJourney.find(params[:vehicle_journey_objectid]) + end + end def create create!(:alert => t('activerecord.errors.models.vehicle_journey.invalid_times')) @@ -78,11 +83,10 @@ class VehicleJourneysController < ChouetteController protected def collection scope = route.vehicle_journeys.with_stops - @q = scope.search filtered_ransack_params + scope = maybe_filter_by_departure_time(scope) + scope = maybe_filter_out_journeys_with_time_tables(scope) - # Fixme 3358 - # grouping = ransack_periode_filter - # @q.build_grouping(grouping) if grouping + @q = scope.search filtered_ransack_params @ppage = 20 @vehicle_journeys = @q.result.paginate(:page => params[:page], :per_page => @ppage) @@ -91,17 +95,29 @@ class VehicleJourneysController < ChouetteController @vehicle_journeys end - def ransack_periode_filter - if params[:q] && params[:q][:vehicle_journey_at_stops_departure_time_gteq] - between = [:departure_time_gteq, :departure_time_lteq].map do |filter| - "2000-01-01 #{params[:q]["vehicle_journey_at_stops_#{filter}"]}:00 UTC" - end - { - :m => 'or', - :vehicle_journey_at_stops_departure_time_between => between.join(' to '), - :vehicle_journey_at_stops_id_null => params[:q][:vehicle_journey_without_departure_time] - } + def maybe_filter_by_departure_time(scope) + if params[:q] && + params[:q][:vehicle_journey_at_stops_departure_time_gteq] && + params[:q][:vehicle_journey_at_stops_departure_time_lteq] + scope = scope.where_departure_time_between( + params[:q][:vehicle_journey_at_stops_departure_time_gteq], + params[:q][:vehicle_journey_at_stops_departure_time_lteq], + allow_empty: + params[:q][:vehicle_journey_without_departure_time] == 'true' + ) end + + scope + end + + def maybe_filter_out_journeys_with_time_tables(scope) + if params[:q] && + params[:q][:vehicle_journey_without_time_table] == 'true' + return scope + .without_time_tables + end + + scope end def filtered_ransack_params @@ -140,14 +156,28 @@ class VehicleJourneysController < ChouetteController private def vehicle_journey_params - params.require(:vehicle_journey).permit( { footnote_ids: [] } , :journey_pattern_id, :number, :published_journey_name, - :published_journey_identifier, :comment, :transport_mode, - :mobility_restricted_suitability, :flexible_service, :status_value, - :facility, :vehicle_type_identifier, :objectid, :time_table_tokens, - { date: [ :hour, :minute ] }, :button, :referential_id, :line_id, - :route_id, :id, { vehicle_journey_at_stops_attributes: [ :arrival_time, - :id, :_destroy, - :stop_point_id, - :departure_time] } ) + params.require(:vehicle_journey).permit( + { footnote_ids: [] }, + :journey_pattern_id, + :number, + :published_journey_name, + :published_journey_identifier, + :comment, + :transport_mode, + :mobility_restricted_suitability, + :flexible_service, + :status_value, + :facility, + :vehicle_type_identifier, + :objectid, + :time_table_tokens, + { date: [:hour, :minute] }, + :button, + :referential_id, + :line_id, + :route_id, + :id, + { vehicle_journey_at_stops_attributes: [:arrival_time, :id, :_destroy, :stop_point_id, :departure_time] } + ) end end diff --git a/app/helpers/newapplication_helper.rb b/app/helpers/newapplication_helper.rb index f03228d73..e189199a2 100644 --- a/app/helpers/newapplication_helper.rb +++ b/app/helpers/newapplication_helper.rb @@ -160,7 +160,7 @@ module NewapplicationHelper end.join.html_safe end - content_tag :div, trigger + menu, class: 'btn-group' + # content_tag :div, trigger + menu, class: 'btn-group' end diff --git a/app/models/calendar.rb b/app/models/calendar.rb index 91a17e853..39e0d6d49 100644 --- a/app/models/calendar.rb +++ b/app/models/calendar.rb @@ -19,6 +19,17 @@ class Calendar < ActiveRecord::Base [:contains_date] end + def convert_to_time_table + Chouette::TimeTable.new.tap do |tt| + self.dates.each do |d| + tt.dates << Chouette::TimeTableDate.new(date: d, in_out: true) + end + self.date_ranges.each do |p| + tt.periods << Chouette::TimeTablePeriod.new(period_start: p.begin, period_end: p.end) + end + end + end + class Period include ActiveAttr::Model diff --git a/app/models/chouette/time_table.rb b/app/models/chouette/time_table.rb index 60684411a..eacc5c827 100644 --- a/app/models/chouette/time_table.rb +++ b/app/models/chouette/time_table.rb @@ -20,6 +20,10 @@ class Chouette::TimeTable < Chouette::TridentActiveRecord belongs_to :calendar belongs_to :created_from, class_name: 'Chouette::TimeTable' + scope :overlapping, -> (date_start, date_end) do + joins(:periods).where('(period_start, period_end) OVERLAPS (?, ?)', date_start, date_end) + end + after_save :save_shortcuts def self.object_id_key @@ -130,6 +134,12 @@ class Chouette::TimeTable < Chouette::TridentActiveRecord [Chouette::TimeTable.maximum(:end_date)].compact.max end + def actualize + self.dates.clear + self.periods.clear + self.merge! self.calendar.convert_to_time_table + end + def month_inspect(date) (date.beginning_of_month..date.end_of_month).map do |d| { @@ -278,6 +288,10 @@ class Chouette::TimeTable < Chouette::TridentActiveRecord [bounding_min, bounding_max].compact end + def display_day_types + %w(monday tuesday wednesday thursday friday saturday sunday).select{ |d| self.send(d) }.map{ |d| self.human_attribute_name(d).first(2)}.join('') + end + def day_by_mask(flag) int_day_types & flag == flag end @@ -513,6 +527,7 @@ class Chouette::TimeTable < Chouette::TridentActiveRecord self.dates.to_a.sort! { |a,b| a.date <=> b.date} self.save! end + self.convert_continuous_dates_to_periods end # remove dates form tt which aren't in another_tt @@ -528,6 +543,7 @@ class Chouette::TimeTable < Chouette::TridentActiveRecord self.dates.to_a.sort! { |a,b| a.date <=> b.date} self.save! end + self.convert_continuous_dates_to_periods end @@ -546,6 +562,7 @@ class Chouette::TimeTable < Chouette::TridentActiveRecord self.periods.to_a.sort! { |a,b| a.period_start <=> b.period_start} self.save! end + self.convert_continuous_dates_to_periods end def duplicate diff --git a/app/models/chouette/vehicle_journey.rb b/app/models/chouette/vehicle_journey.rb index 297e462f0..d20a3d315 100644 --- a/app/models/chouette/vehicle_journey.rb +++ b/app/models/chouette/vehicle_journey.rb @@ -51,9 +51,7 @@ module Chouette at_stops = self.vehicle_journey_at_stops.to_a.dup (route.stop_points.map(&:id) - at_stops.map(&:stop_point_id)).each do |id| # Set stop_point id for fake vjas with no departure time yep. - params = {} - params[:stop_point_id] = id if journey_pattern.stop_points.map(&:id).include?(id) - at_stops.insert(route.stop_points.map(&:id).index(id), Chouette::VehicleJourneyAtStop.new(params)) + at_stops.insert(route.stop_points.map(&:id).index(id), Chouette::VehicleJourneyAtStop.new(stop_point_id: id)) end at_stops end @@ -219,7 +217,51 @@ module Chouette AND "vehicle_journey_at_stops"."stop_point_id" = "journey_patterns"."departure_stop_point_id" ') - .order("vehicle_journey_at_stops.departure_time") + .order('"vehicle_journey_at_stops"."departure_time"') + end + + # Requires a SELECT DISTINCT and a join with + # "vehicle_journey_at_stops". + # + # Example: + # .select('DISTINCT "vehicle_journeys".*') + # .joins(' + # LEFT JOIN "vehicle_journey_at_stops" + # ON "vehicle_journey_at_stops"."vehicle_journey_id" = + # "vehicle_journeys"."id" + # ') + # .where_departure_time_between('08:00', '09:45') + def self.where_departure_time_between( + start_time, + end_time, + allow_empty: false + ) + self + .where( + %Q( + "vehicle_journey_at_stops"."departure_time" >= ? + AND "vehicle_journey_at_stops"."departure_time" < ? + #{ + if allow_empty + 'OR "vehicle_journey_at_stops"."id" IS NULL' + end + } + ), + "2000-01-01 #{start_time}:00 UTC", + "2000-01-01 #{end_time}:00 UTC" + ) + end + + def self.without_time_tables + # Joins the VehicleJourney–TimeTable through table to select only those + # VehicleJourneys that don't have an associated TimeTable. + self + .joins(' + LEFT JOIN "time_tables_vehicle_journeys" + ON "time_tables_vehicle_journeys"."vehicle_journey_id" = + "vehicle_journeys"."id" + ') + .where('"time_tables_vehicle_journeys"."vehicle_journey_id" IS NULL') end end diff --git a/app/models/time_table_combination.rb b/app/models/time_table_combination.rb index 519c02f2b..9b5111014 100644 --- a/app/models/time_table_combination.rb +++ b/app/models/time_table_combination.rb @@ -1,42 +1,61 @@ class TimeTableCombination + include ActiveModel::Validations include ActiveModel::Conversion extend ActiveModel::Naming - attr_accessor :source_id, :combined_id, :operation + attr_accessor :source_id, :combined_type, :operation, :time_table_id, :calendar_id + + validates_presence_of :source_id, :combined_type, :operation + + validates :time_table_id, presence: true, if: "calendar_id.blank?" + validates :calendar_id, presence: true, if: "time_table_id.blank?" - validates_presence_of :source_id, :combined_id, :operation - validates_inclusion_of :operation, :in => %w( union intersection disjunction), :allow_nil => true + validates_inclusion_of :combined_type, :in => %w(time_table calendar) + validates_inclusion_of :operation, :in => %w(union intersection disjunction), :allow_nil => true def clean - self.source_id = nil - self.combined_id = nil - self.operation = nil + self.source_id = nil + self.time_table_id = nil + self.calendar_id = nil + self.combined_type = nil + self.operation = nil self.errors.clear end def self.operations - %w( union intersection disjunction) + %w(union intersection disjunction) end def initialize(attributes = {}) attributes.each do |name, value| send("#{name}=", value) end + self.combined_type = "time_table" end def persisted? false end + def target + id = self.send("#{combined_type}_id") + klass = combined_type == 'calendar' ? Calendar : Chouette::TimeTable + target = klass.find id + target = target.convert_to_time_table unless target.is_a? Chouette::TimeTable + target + end + def combine - source = Chouette::TimeTable.find( source_id) - combined = Chouette::TimeTable.find( combined_id) - if operation == "union" + source = Chouette::TimeTable.find source_id + combined = self.target + + case operation + when 'union' source.merge! combined - elsif operation == "intersection" + when 'intersection' source.intersect! combined - elsif operation == "disjunction" + when 'disjunction' source.disjoin! combined else raise "unknown operation" diff --git a/app/policies/time_table_policy.rb b/app/policies/time_table_policy.rb index 6ca02f451..82e4ca194 100644 --- a/app/policies/time_table_policy.rb +++ b/app/policies/time_table_policy.rb @@ -17,6 +17,10 @@ class TimeTablePolicy < ApplicationPolicy organisation_match? && user.has_permission?('time_tables.destroy') end + def duplicate? + organisation_match? && create? + end + def update? ; edit? end def new? ; create? end end diff --git a/app/views/access_link_pairs/_access_link_pair.html.slim b/app/views/access_link_pairs/_access_link_pair.html.slim index c313f9044..3eebfd7f6 100644 --- a/app/views/access_link_pairs/_access_link_pair.html.slim +++ b/app/views/access_link_pairs/_access_link_pair.html.slim @@ -1,4 +1,5 @@ tr + - require 'pry'; binding.pry td .link .access_point diff --git a/app/views/autocomplete_calendars/autocomplete.rabl b/app/views/autocomplete_calendars/autocomplete.rabl new file mode 100644 index 000000000..9aba2c37b --- /dev/null +++ b/app/views/autocomplete_calendars/autocomplete.rabl @@ -0,0 +1,5 @@ +collection @calendars, :object_root => false +attribute :id, :name, :short_name, :shared +node :text do |cal| + "#{cal.id} - #{cal.name}" +end diff --git a/app/views/autocomplete_time_tables/index.rabl b/app/views/autocomplete_time_tables/index.rabl index 8ec7b314e..80e3f8684 100644 --- a/app/views/autocomplete_time_tables/index.rabl +++ b/app/views/autocomplete_time_tables/index.rabl @@ -1,10 +1,15 @@ collection @time_tables, :object_root => false node do |time_table| - { :id => time_table.id, :comment => time_table.comment, :objectid => time_table.objectid, + { + :id => time_table.id, :comment => time_table.comment, :objectid => time_table.objectid, :time_table_bounding => time_table.presenter.time_table_bounding, :composition_info => time_table.presenter.composition_info, :tags => time_table.tags.join(','), - :day_types => %w(monday tuesday wednesday thursday friday saturday sunday).select{ |d| time_table.send(d) }.map{ |d| time_table.human_attribute_name(d).first(2)}.join('')} + :text => "#{time_table.comment} - #{time_table.display_day_types} - #{time_table.objectid.parts.try(:third)}", + :color => time_table.color, + :day_types => time_table.display_day_types, + :short_id => time_table.objectid.parts.try(:third) + } end diff --git a/app/views/group_of_lines/_group_of_line.html.slim b/app/views/group_of_lines/_group_of_line.html.slim index fb9f95894..e33201f74 100644 --- a/app/views/group_of_lines/_group_of_line.html.slim +++ b/app/views/group_of_lines/_group_of_line.html.slim @@ -2,11 +2,11 @@ .panel-heading .panel-title.clearfix span.pull-right - - if edit && policy(group_of_line).update? - = link_to edit_line_referential_group_of_line_path(@line_referential, group_of_line), class: 'btn btn-default btn-sm' do - span.fa.fa-pencil - - if delete && policy(group_of_line).destroy? - = link_to('<span class="fa fa-trash-o"></span>'.html_safe, line_referential_group_of_line_path(@line_referential, group_of_line), :method => :delete, :data => {:confirm => t('group_of_lines.actions.destroy_confirm')}, class: 'btn btn-danger btn-sm') + / - if edit && policy(group_of_line).update? + = link_to edit_line_referential_group_of_line_path(@line_referential, group_of_line), class: 'btn btn-default btn-sm' do + span.fa.fa-pencil + / - if delete && policy(group_of_line).destroy? + = link_to('<span class="fa fa-trash-o"></span>'.html_safe, line_referential_group_of_line_path(@line_referential, group_of_line), :method => :delete, :data => {:confirm => t('group_of_lines.actions.destroy_confirm')}, class: 'btn btn-danger btn-sm') h5 = link_to [@line_referential, group_of_line], :class => "preview", :title => "#{Chouette::GroupOfLine.model_name.human.capitalize} #{group_of_line.name}" do diff --git a/app/views/referential_lines/_filters.html.slim b/app/views/referential_lines/_filters.html.slim new file mode 100644 index 000000000..aa355884b --- /dev/null +++ b/app/views/referential_lines/_filters.html.slim @@ -0,0 +1,16 @@ += search_form_for @q, url: referential_line_path(@referential, @line), class: 'form form-filter' do |f| + .ffg-row + .input-group.search_bar + = f.search_field :name_or_objectid_cont, class: 'form-control', placeholder: "Indiquez un nom d'itinéraire ou un OiD..." + span.input-group-btn + button.btn.btn-default#search-btn type='submit' + span.fa.fa-search + + .ffg-row + .form-group.togglable + = f.label Chouette::Route.human_attribute_name(:wayback), required: false, class: 'control-label' + = f.input :wayback_eq_any, as: :checkboxes, class: 'form-control', collection: Chouette::Route.wayback.values, as: :check_boxes, label: false, required: false, wrapper_html: { class: 'checkbox_list'}, label_method: lambda{|l| ("<span>" + t("enumerize.route.wayback.#{l}") + "</span>").html_safe} + + .actions + = link_to 'Effacer', referential_line_path(@referential, @line), class: 'btn btn-link' + = f.submit 'Filtrer', class: 'btn btn-default' diff --git a/app/views/referential_lines/show.html.slim b/app/views/referential_lines/show.html.slim index b60342f19..1e24f0f15 100644 --- a/app/views/referential_lines/show.html.slim +++ b/app/views/referential_lines/show.html.slim @@ -49,13 +49,7 @@ - if params[:q].present? or @routes.any? .row .col-lg-12 - = search_form_for @q, url: referential_line_path(@referential, @line), class: 'form form-filter' do |f| - .input-group.search_bar - = f.search_field :name_cont, class: 'form-control', placeholder: "Indiquez un nom d'itinéraire..." - - span.input-group-btn - button.btn.btn-default type='submit' - span.fa.fa-search + = render 'filters' - if @routes.any? .row diff --git a/app/views/routes/show.html.slim b/app/views/routes/show.html.slim index 7ab628690..d994e3861 100644 --- a/app/views/routes/show.html.slim +++ b/app/views/routes/show.html.slim @@ -8,7 +8,8 @@ / Below is secundary actions & optional contents (filters, ...) .row.mb-sm .col-lg-12.text-right - = link_to t('journey_patterns.index.title'), [@referential, @line, @route, :journey_patterns_collection], data: {turbolinks: false}, class: 'btn btn-primary' + - if @route_sp.any? + = link_to t('journey_patterns.index.title'), [@referential, @line, @route, :journey_patterns_collection], data: {turbolinks: false}, class: 'btn btn-primary' - if @route.journey_patterns.present? = link_to t('vehicle_journeys.actions.index'), [@referential, @line, @route, :vehicle_journeys], data: {turbolinks: false}, class: 'btn btn-primary' @@ -19,7 +20,7 @@ span.fa.fa-trash span = t('actions.destroy') -/ PägeContent +/ PageContent .page_content .container-fluid .row @@ -30,6 +31,10 @@ @route.human_attribute_name(:wayback) => (@route.wayback ? @route.wayback_text : '-' ), @route.human_attribute_name(:opposite_route) => (@route.opposite_route ? @route.opposite_route.name : '-') } + - if @route_sp.any? + .col-lg-6.col-md-6.col-sm-12.col-xs-12 + #route_map.map.mb-lg + .row .col-lg-12 - if @route_sp.any? @@ -48,3 +53,7 @@ - else = replacement_msg t('stop_areas.search_no_results') + += javascript_tag do + | window.route = "#{URI.escape(route_json_for_edit(@route))}" += javascript_include_tag 'es6_browserified/itineraries/show.js' diff --git a/app/views/time_table_combinations/_combine.html.slim b/app/views/time_table_combinations/_combine.html.slim deleted file mode 100644 index a2e9a9c6e..000000000 --- a/app/views/time_table_combinations/_combine.html.slim +++ /dev/null @@ -1,12 +0,0 @@ -#modal_combine.modal.fade tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true" - .modal-dialog - .modal-content - .modal-header - button.close type="button" data-dismiss="modal" - span aria-hidden="true" × - span.sr-only Close - - h4.modal-title id="myModalLabel" - = t('time_tables.show.combine_form') - - == render "time_table_combinations/combine_form"
\ No newline at end of file diff --git a/app/views/time_table_combinations/_combine_form.html.slim b/app/views/time_table_combinations/_combine_form.html.slim deleted file mode 100644 index 2367cd4af..000000000 --- a/app/views/time_table_combinations/_combine_form.html.slim +++ /dev/null @@ -1,11 +0,0 @@ -= semantic_form_for [@referential, @time_table, @time_table_combination], :remote => true do |form| - .modal-body - == render "shared/flash_messages" - - = form.inputs do - = form.input :operation, as: :radio, :collection => Hash[TimeTableCombination.operations.map {|b| [t( b, :scope => "time_table_combinations.operations"),b]}] - = form.input :combined_id, :label => t('.time_tables'), as: :search_time_table, :json => referential_autocomplete_time_tables_path(@referential, :format => :json), :hint_text => t('search_hint'), :no_result_text => t('no_result_text'), :searching_text => t('searching_term'), :tokenLimit => 1 - - .modal-footer - = form.actions do - = form.action :submit, as: :button , :label => t('time_tables.show.combine')
\ No newline at end of file diff --git a/app/views/time_table_combinations/_form.html.slim b/app/views/time_table_combinations/_form.html.slim new file mode 100644 index 000000000..3716f6713 --- /dev/null +++ b/app/views/time_table_combinations/_form.html.slim @@ -0,0 +1,22 @@ += simple_form_for [@referential, @time_table, @combination], html: {class: 'form-horizontal', id: 'tt_combination_form'}, wrapper: :horizontal_form do |f| + .row + .col-lg-12 + .form-group.has_switch + = f.label :combined_type, class: 'col-sm-4 control-label required' do + = 'Type de calendriers ' + abbr title='Champ requis' * + = f.input :combined_type, as: :boolean, checked_value: 'time_table', unchecked_value: 'calendar', required: false, label: content_tag(:span, t("time_table_combinations.combined_type.#{@combination.combined_type}"), class: 'switch-label', data: { checkedValue: 'Calendriers', uncheckedValue: 'Modèles de calendriers' }), wrapper_html: { class: 'col-sm-8' } + + = f.input :time_table_id, as: :select, input_html: {class: 'tt_combination_target', style: "width: 100%", data: { 'select2-ajax': 'true', term: 'comment_or_objectid_cont', url: referential_autocomplete_time_tables_path(@referential, format: :json)}}, wrapper_html: {class: @combination.combined_type != 'time_table' ? 'hidden' : ''} + + = f.input :calendar_id, as: :select, input_html: { class: 'tt_combination_target', style: "width: 100%", data: { 'select2-ajax': 'true', term: 'name_cont', url: autocomplete_calendars_path}}, wrapper_html: {class: @combination.combined_type != 'calendar' ? 'hidden' : ''} + + .separator + + .row + .col-lg-12 + = f.label :operation, class: 'col-sm-4 control-label' + = f.input :operation, as: :radio_buttons, label: false, collection: TimeTableCombination.operations, label_method: lambda{|o| t("time_table_combinations.operations.#{o}")}, wrapper_html: { class: 'col-sm-8' } + + + = f.button :submit, t('actions.submit'), class: 'btn btn-default formSubmitr', form: 'tt_combination_form' diff --git a/app/views/time_table_combinations/create_failure.js.slim b/app/views/time_table_combinations/create_failure.js.slim deleted file mode 100644 index c5b544483..000000000 --- a/app/views/time_table_combinations/create_failure.js.slim +++ /dev/null @@ -1,3 +0,0 @@ -| var combine_form_partial = "#{j render 'time_table_combinations/combine_form'}"; - -| $('#new_time_table_combination').replaceWith(combine_form_partial);
\ No newline at end of file diff --git a/app/views/time_table_combinations/create_success.js.slim b/app/views/time_table_combinations/create_success.js.slim deleted file mode 100644 index f18eea4bc..000000000 --- a/app/views/time_table_combinations/create_success.js.slim +++ /dev/null @@ -1,6 +0,0 @@ -| var combine_form_partial = "#{j render 'time_table_combinations/combine_form'}"; - -| $('#new_time_table_combination').replaceWith(combine_form_partial); - -| var time_table_partial = "#{j render 'time_tables/show_time_table'}"; -| $('#time_table_show').replaceWith(time_table_partial);
\ No newline at end of file diff --git a/app/views/time_table_combinations/new.html.slim b/app/views/time_table_combinations/new.html.slim new file mode 100644 index 000000000..e49a10bc6 --- /dev/null +++ b/app/views/time_table_combinations/new.html.slim @@ -0,0 +1,12 @@ +/ PageHeader += pageheader 'map-marker', + t('time_tables.show.combine_form'), + '', + '' + +/ PageContent +.page_content + .container-fluid + .row + .col-lg-8.col-lg-offset-2.col-md-8.col-md-offset-2.col-sm-10.col-sm-offset-1 + = render 'form' diff --git a/app/views/time_table_combinations/new.js.slim b/app/views/time_table_combinations/new.js.slim deleted file mode 100644 index 749ec04f6..000000000 --- a/app/views/time_table_combinations/new.js.slim +++ /dev/null @@ -1,2 +0,0 @@ -| var combine_form_partial = "#{j render 'time_table_combinations/combine_form'}"; -| $('#new_time_table_combination').replaceWith(combine_form_partial);
\ No newline at end of file diff --git a/app/views/time_tables/_filter.html.slim b/app/views/time_tables/_filter.html.slim index d29c628a9..cee915911 100644 --- a/app/views/time_tables/_filter.html.slim +++ b/app/views/time_tables/_filter.html.slim @@ -7,12 +7,13 @@ span.fa.fa-search .ffg-row - .form-group.togglable - = f.label @time_tables.human_attribute_name(:color), required: false, class: 'control-label' - = f.input :color_cont_any, collection: ["#9B9B9B", "#FFA070", "#C67300", "#7F551B", "#41CCE3", "#09B09C", "#3655D7", "#6321A0", "#E796C6", "#DD2DAA"], as: :check_boxes, label: false, label_method: lambda{|tt| ("<span style='height:19px;'><span class='fa fa-circle' style='position:relative;top:0;margin-top:0;color:" + tt + "'></span></span>").html_safe }, required: false, wrapper_html: { class: 'checkbox_list' } + / .form-group.togglable + / = f.label @time_tables.human_attribute_name(:color), required: false, class: 'control-label' + / = f.input :color_cont_any, collection: ["#9B9B9B", "#FFA070", "#C67300", "#7F551B", "#41CCE3", "#09B09C", "#3655D7", "#6321A0", "#E796C6", "#DD2DAA"], as: :check_boxes, label: false, label_method: lambda{|tt| ("<span style='height:19px;'><span class='fa fa-circle' style='position:relative;top:0;margin-top:0;color:" + tt + "'></span></span>").html_safe }, required: false, wrapper_html: { class: 'checkbox_list' } - / = f.label @time_tables.human_attribute_name(:tag_search), required: false, class: 'control-label' - / = f.input :tag_search, as: :tags, collection: Chouette::TimeTable.tags_on(:tags).pluck(:name), label: false, input_html: { 'data-select2ed': 'true', 'data-select2ed-placeholder': 'Indiquez une étiquette...' }, wrapper_html: { class: 'select2ed'}, include_blank: false + .form-group + = f.label @time_tables.human_attribute_name(:tag_search), required: false, class: 'control-label' + = f.input :tag_search, as: :tags, collection: Chouette::TimeTable.tags_on(:tags).pluck(:name), label: false, input_html: { 'data-select2ed': 'true', 'data-select2ed-placeholder': 'Indiquez une étiquette...' }, wrapper_html: { class: 'select2ed'}, include_blank: false .form-group.togglable = f.label @time_tables.human_attribute_name(:bounding_dates), required: false, class: 'control-label' diff --git a/app/views/time_tables/show.html.slim b/app/views/time_tables/show.html.slim index 220ac3995..1c5984a7d 100644 --- a/app/views/time_tables/show.html.slim +++ b/app/views/time_tables/show.html.slim @@ -11,12 +11,14 @@ .col-lg-12.text-right / - if policy(@time_table).create? && @referential.organisation == current_organisation / = link_to t('time_tables.actions.new'), new_referential_time_table_path(@referential), class: 'btn btn-primary' + - if @time_table.calendar + = link_to t('actions.actualize'), actualize_referential_time_table_path(@referential, @time_table), method: :post, class: 'btn btn-primary' /- if policy(@time_table).create? && @referential.organisation == current_organisation - = link_to t('actions.combine'), new_referential_time_table_time_table_combination_path(@referential, @time_table), {remote: true, 'data-toggle' => "modal", 'data-target' => '#modal_combine', class: 'btn btn-primary' } + = link_to t('actions.combine'), new_referential_time_table_time_table_combination_path(@referential, @time_table), class: 'btn btn-primary' - - if policy(@time_table).create? && @referential.organisation == current_organisation - = link_to t('actions.clone'), duplicate_referential_time_table_path(@referential, @time_table), class: 'btn btn-primary' + - if policy(@time_table).duplicate? + = link_to t('actions.duplicate'), duplicate_referential_time_table_path(@referential, @time_table), class: 'btn btn-primary' - if policy(@time_table).destroy? = link_to referential_time_table_path(@referential, @time_table), method: :delete, data: {confirm: t('time_tables.actions.destroy_confirm')}, class: 'btn btn-primary' do @@ -44,8 +46,3 @@ = link_to '', referential_time_table_path(@referential, @time_table, year: (@year + 1)), class: 'next_page' = render 'show_time_table' - - .row - .col-lg-12 - / WTF ??! - = render 'time_table_combinations/combine' diff --git a/app/views/time_tables/show.rabl b/app/views/time_tables/show.rabl index a0a799985..53c9daec0 100644 --- a/app/views/time_tables/show.rabl +++ b/app/views/time_tables/show.rabl @@ -9,7 +9,8 @@ node do |tt| current_month: tt.month_inspect(Date.today), periode_range: month_periode_enum(3), current_periode_range: Date.today.beginning_of_month, - color: tt.color ? tt.color : '' + color: tt.color ? tt.color : '', + short_id: tt.objectid.parts.try(:third) } end diff --git a/app/views/vehicle_journeys/show.rabl b/app/views/vehicle_journeys/show.rabl index 7db9b531d..1ef9bc6b5 100644 --- a/app/views/vehicle_journeys/show.rabl +++ b/app/views/vehicle_journeys/show.rabl @@ -31,7 +31,7 @@ end child(:vehicle_journey_at_stops_matrix, :object_root => false) do |vehicle_stops| node do |vehicle_stop| - node(:dummy) { !vehicle_stop.stop_point_id? } + node(:dummy) { !vehicle_stop.id? } node(:stop_area_object_id) do vehicle_stop.stop_point ? vehicle_stop.stop_point.stop_area.objectid : nil end diff --git a/config/locales/actions.en.yml b/config/locales/actions.en.yml index 5bfd9d9fb..6e2dd3aa2 100644 --- a/config/locales/actions.en.yml +++ b/config/locales/actions.en.yml @@ -13,6 +13,7 @@ en: clean_up: 'Clean up' sync: 'Synchronize' combine: 'Combine' + actualize: 'Actualize' or: "or" cancel: "Cancel" search_hint: "Type in a search term" diff --git a/config/locales/actions.fr.yml b/config/locales/actions.fr.yml index 94afc1393..8f2fc90f8 100644 --- a/config/locales/actions.fr.yml +++ b/config/locales/actions.fr.yml @@ -14,6 +14,7 @@ fr: clean_up: 'Purger' sync: 'Synchroniser' combine: 'Combiner' + actualize: 'Actualiser' or: "ou" cancel: "Annuler" search_hint: "Entrez un texte à rechercher" diff --git a/config/locales/time_table_combinations.en.yml b/config/locales/time_table_combinations.en.yml index e7bf9d90e..142270d13 100644 --- a/config/locales/time_table_combinations.en.yml +++ b/config/locales/time_table_combinations.en.yml @@ -2,6 +2,9 @@ en: time_table_combinations: success: "operation applied on timetable" failure: "operation failed on timetable" + combined_type: + calendar: 'Calendars' + time_table: 'Time tables' operations: union: "merge" intersection: "intersect" @@ -13,3 +16,5 @@ en: time_table_combination: combined_id: "Time table id" operation: "operation" + time_table_id: "Search a calendar" + calendar_id: "Search a calendar" diff --git a/config/locales/time_table_combinations.fr.yml b/config/locales/time_table_combinations.fr.yml index 562c7b58d..5a23cf029 100644 --- a/config/locales/time_table_combinations.fr.yml +++ b/config/locales/time_table_combinations.fr.yml @@ -2,14 +2,19 @@ fr: time_table_combinations: success: "opération appliquée sur le calendrier" failure: "opération échouée" + combined_type: + calendar: 'Modèles de calendriers' + time_table: 'Calendriers' operations: - union: "ajouter les dates" - intersection: "conserver les dates communes" - disjunction: "soustraire les dates" + union: "Ajouter les dates" + intersection: "Conserver les dates communes" + disjunction: "Soustraire les dates" combine_form: time_tables: "Calendrier à combiner" activemodel: attributes: time_table_combination: combined_id: "Id Calendrier" - operation: "opération" + operation: "Type d'opération" + time_table_id: "Rechercher un calendrier" + calendar_id: "Rechercher un calendrier" diff --git a/config/locales/time_tables.en.yml b/config/locales/time_tables.en.yml index 495770987..6c5aac012 100644 --- a/config/locales/time_tables.en.yml +++ b/config/locales/time_tables.en.yml @@ -20,6 +20,8 @@ en: add_period: "Add a period" add_date: "Add a peculiar date" add_excluded_date: "Add an excluded date" + actualize: + success: "Actualize succeded" new: title: "Add a new timetable" edit: diff --git a/config/locales/time_tables.fr.yml b/config/locales/time_tables.fr.yml index 812d8cdd2..36a588eb4 100644 --- a/config/locales/time_tables.fr.yml +++ b/config/locales/time_tables.fr.yml @@ -20,6 +20,8 @@ fr: add_period: "Ajouter une période" add_date: "Ajouter une date particulière" add_excluded_date: "Ajouter une date exclue" + actualize: + success: "Actualisation terminée" new: title: "Ajouter un calendrier" duplicate: @@ -68,7 +70,7 @@ fr: time_table: comment: "Nom" color: "Couleur associée" - bounding_dates: 'Période de validité englobante' + bounding_dates: 'Période contenue dans le calendrier' version: "Abréviation" day_types: "Jours d'application des périodes" none: "aucun" diff --git a/config/routes.rb b/config/routes.rb index 8415d49df..f75578106 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -72,7 +72,9 @@ ChouetteIhm::Application.routes.draw do resources :networks end - resources :calendars + resources :calendars do + get :autocomplete, on: :collection, controller: 'autocomplete_calendars' + end resources :referentials do resources :api_keys @@ -118,6 +120,7 @@ ChouetteIhm::Application.routes.draw do resource :vehicle_journeys_collection, :only => [:show, :update] resources :vehicle_journeys, :vehicle_journey_frequencies do get 'select_journey_pattern', :on => :member + get 'select_vehicle_journey', :on => :member resources :vehicle_translations resources :time_tables end @@ -165,6 +168,7 @@ ChouetteIhm::Application.routes.draw do get :tags end member do + post 'actualize' get 'duplicate' get 'month', defaults: { format: :json } end diff --git a/spec/controllers/autocomplete_calendars_controller_spec.rb b/spec/controllers/autocomplete_calendars_controller_spec.rb new file mode 100644 index 000000000..3ff75fadf --- /dev/null +++ b/spec/controllers/autocomplete_calendars_controller_spec.rb @@ -0,0 +1,5 @@ +require 'rails_helper' + +RSpec.describe AutocompleteCalendarsController, type: :controller do + +end diff --git a/spec/factories/chouette_vehicle_journey.rb b/spec/factories/chouette_vehicle_journey.rb index 9ba660800..452909f23 100644 --- a/spec/factories/chouette_vehicle_journey.rb +++ b/spec/factories/chouette_vehicle_journey.rb @@ -11,13 +11,18 @@ FactoryGirl.define do end factory :vehicle_journey do - after(:create) do |vehicle_journey| + transient do + stop_arrival_time '01:00:00' + stop_departure_time '03:00:00' + end + + after(:create) do |vehicle_journey, evaluator| vehicle_journey.journey_pattern.stop_points.each_with_index do |stop_point, index| vehicle_journey.vehicle_journey_at_stops << create(:vehicle_journey_at_stop, :vehicle_journey => vehicle_journey, :stop_point => stop_point, - :arrival_time => '2000-01-01 01:00:00 UTC', - :departure_time => '2000-01-01 03:00:00 UTC') + :arrival_time => "2000-01-01 #{evaluator.stop_arrival_time} UTC", + :departure_time => "2000-01-01 #{evaluator.stop_departure_time} UTC") end end diff --git a/spec/features/calendars_permissions_spec.rb b/spec/features/calendars_permissions_spec.rb new file mode 100644 index 000000000..6eb0ea08e --- /dev/null +++ b/spec/features/calendars_permissions_spec.rb @@ -0,0 +1,55 @@ +# -*- coding: utf-8 -*- +require 'spec_helper' + +describe 'Calendars', type: :feature do + login_user + + let(:calendar) { create :calendar, organisation_id: 1 } + + describe 'permissions' do + before do + allow_any_instance_of(CalendarPolicy).to receive(:edit?).and_return permission + allow_any_instance_of(CalendarPolicy).to receive(:destroy?).and_return permission + allow_any_instance_of(CalendarPolicy).to receive(:share?).and_return permission + visit path + end + + context 'on show view' do + let( :path ){ calendar_path(calendar) } + + context 'if present → ' do + let( :permission ){ true } + it 'view shows the corresponding buttons' do + expect(page).to have_css('a.btn.btn-default', text: 'Editer') + expect(page).to have_css('a.btn.btn-primary', text: 'Supprimer') + end + end + + context 'if absent → ' do + let( :permission ){ false } + it 'view does not show the corresponding buttons' do + expect(page).not_to have_css('a.btn.btn-default', text: 'Editer') + expect(page).not_to have_css('a.btn.btn-primary', text: 'Supprimer') + end + end + end + + context 'on edit view' do + let( :path ){ edit_calendar_path(calendar) } + + context 'if present → ' do + let( :permission ){ true } + it 'view shows the corresponding checkbox' do + expect( page ).to have_css('div.has_switch label.boolean[for=calendar_shared]') + end + end + + context 'if absent → ' do + let( :permission ){ false } + it 'view does not show the corresponding checkbox' do + expect( page ).not_to have_css('div.has_switch label.boolean[for=calendar_shared]') + end + end + end + end +end diff --git a/spec/features/group_of_lines_permissions_spec.rb b/spec/features/group_of_lines_permissions_spec.rb new file mode 100644 index 000000000..5c03481ec --- /dev/null +++ b/spec/features/group_of_lines_permissions_spec.rb @@ -0,0 +1,43 @@ +# -*- coding: utf-8 -*- + +describe "Group of lines", :type => :feature do + skip 'delete param of view seems to be constantly false' do + login_user + + let(:line) { create(:line_with_stop_areas, :network => network, :company => company) } + + let(:line_referential) { create :line_referential } + let(:network) { create(:network) } + let(:company) { create(:company) } + + let(:group_of_line) { create :group_of_line, line_referential: line_referential } + + describe 'permissions' do + before do + group_of_line.lines << line + allow_any_instance_of(LinePolicy).to receive(:destroy?).and_return permission + visit path + end + + context 'on show view' do + let( :path ){ line_referential_group_of_line_path(line_referential, group_of_line, delete: true) } + + context 'if permissions present → ' do + let( :permission ){ true } + + it 'shows the appropriate buttons' do + expected_url = line_referential_line_path(line.line_referential, line) + expect( page ).to have_link('Supprimer', href: expected_url) + end + end + context 'if permissions absent → ' do + let( :permission ){ false } + + it 'shows the appropriate buttons' do + expect( page ).not_to have_link('Supprimer') + end + end + end + end + end +end diff --git a/spec/features/group_of_lines_spec.rb b/spec/features/group_of_lines_spec.rb index 79500fe33..59101ccd5 100644 --- a/spec/features/group_of_lines_spec.rb +++ b/spec/features/group_of_lines_spec.rb @@ -37,7 +37,7 @@ describe "Group of lines", :type => :feature do describe "show" do it "displays group of line" do visit line_referential_group_of_lines_path(line_referential) - click_link "#{subject.name}" + click_link subject.name expect(page).to have_content(subject.name) end diff --git a/spec/features/line_footnotes_permissions_spec.rb b/spec/features/line_footnotes_permissions_spec.rb new file mode 100644 index 000000000..4de2a6137 --- /dev/null +++ b/spec/features/line_footnotes_permissions_spec.rb @@ -0,0 +1,40 @@ +# -*- coding: utf-8 -*- + +describe 'Line Footnotes', type: :feature do + login_user + + let!(:line) { create :line_with_stop_areas, network: network, company: company, line_referential: line_referential } + let(:referential) { Referential.first } + let( :line_referential ){ referential.line_referential } + let(:network) { create(:network) } + let(:company) { create(:company) } + + + describe 'permissions' do + before do + allow_any_instance_of(LinePolicy).to receive(:update_footnote?).and_return permission + visit path + end + + describe 'on show view' do + let( :path ){ referential_line_footnotes_path(line_referential, line) } + + context 'if present → ' do + let( :permission ){ true } + + it 'displays the corresponding button' do + expect( page ).to have_link('Editer', href: edit_referential_line_footnotes_path(line_referential, line)) + end + end + + context 'if absent → ' do + let( :permission ){ false } + + it 'does not display the corresponding button' do + expect( page ).not_to have_link('Editer', href: edit_referential_line_footnotes_path(line_referential, line)) + end + end + end + + end +end diff --git a/spec/features/line_footnotes_spec.rb b/spec/features/line_footnotes_spec.rb index 0273ee288..4d77cba41 100644 --- a/spec/features/line_footnotes_spec.rb +++ b/spec/features/line_footnotes_spec.rb @@ -27,18 +27,6 @@ describe 'Line Footnotes', type: :feature do expect(page).not_to have_content(I18n.t('actions.add')) end - context 'user has permission to edit footnotes' do - it 'shows edit link for footnotes' do - expect(page).to have_content(I18n.t('actions.edit')) - end - end - - context 'user does not have permission to edit footnotes' do - it 'does not show edit link for footnotes' do - @user.update_attribute(:permissions, []) - visit referential_line_footnotes_path(referential.line_referential, line) - expect(page).not_to have_content(I18n.t('lines.actions.edit_footnotes')) - end - end end + end diff --git a/spec/features/lines_permissions_spec.rb b/spec/features/lines_permissions_spec.rb new file mode 100644 index 000000000..5d53d6568 --- /dev/null +++ b/spec/features/lines_permissions_spec.rb @@ -0,0 +1,66 @@ +# -*- coding: utf-8 -*- + +describe "Lines", :type => :feature do + login_user + + let(:line_referential) { create :line_referential } + + let(:network) { create(:network) } + let(:company) { create(:company) } + let(:line) { create :line_with_stop_areas, network: network, company: company, line_referential: line_referential } + context 'permissions' do + before do + create :group_of_line + line_referential.lines << line + line_referential.organisations << Organisation.first + allow_any_instance_of(LinePolicy).to receive(:create?).and_return permission + allow_any_instance_of(LinePolicy).to receive(:destroy?).and_return permission + visit path + end + + context 'on index view' do + let( :path ){ line_referential_lines_path(line_referential) } + + context 'if present → ' do + let( :permission ){ true } + + it 'displays the corresponding button' do + expected_href = new_line_referential_line_path(line_referential) + expect( page ).to have_link('Ajouter une ligne', href: expected_href) + end + end + + context 'if absent → ' do + let( :permission ){ false } + + it 'does not display the corresponding button' do + expect( page ).not_to have_link('Ajouter une ligne') + end + end + end + + context 'on show view' do + skip 'policies always false' do + let( :path ){ line_referential_line_path(line_referential, line) } + + context 'if present → ' do + let( :permission ){ true } + + it 'displays the corresponding buttons' do + expected_href = new_line_referential_line_path(line_referential) + expect( page ).to have_link('Ajouter une ligne', href: expected_href) + end + end + + context 'if absent → ' do + let( :permission ){ false } + + it 'does not display the corresponding button' do + expect( page ).not_to have_link('Ajouter une ligne') + end + end + end + + end + end +end diff --git a/spec/features/referential_lines_permissions_spec.rb b/spec/features/referential_lines_permissions_spec.rb new file mode 100644 index 000000000..0d156f0d6 --- /dev/null +++ b/spec/features/referential_lines_permissions_spec.rb @@ -0,0 +1,44 @@ +# -*- coding: utf-8 -*- + +describe 'ReferentialLines', type: :feature do + login_user + let!(:referential_metadata) { create :referential_metadata, referential: referential } + let( :line ){ referential.lines.first } + + context 'permissions' do + before do + allow_any_instance_of(RoutePolicy).to receive(:create?).and_return permission + visit path + end + + context 'on show view' do + let( :path ){ referential_line_path(referential, line) } + + context 'if present → ' do + let( :permission ){ true } + it 'shows the corresponding button' do + expected_href = new_referential_line_route_path(referential, line) + expect( page ).to have_link("Ajouter un itinéraire", href: expected_href) + end + end + context 'if absent → ' do + let( :permission ){ false } + it 'does not show the corresponding button' do + expect( page ).not_to have_link("Ajouter un itinéraire") + end + end + end + + end + + # describe 'show' do + # it 'displays referential line' do + # visit referential_line_path(referential, referential.lines.first) + # expect(page).to have_content(referential.lines.first.name) + # end + # it 'displays referential line' do + # visit referential_line_path(referential, referential.lines.first, sort: "stop_points") + # expect(page).to have_content(referential.lines.first.name) + # end + # end +end diff --git a/spec/features/routes_permissions_spec.rb b/spec/features/routes_permissions_spec.rb new file mode 100644 index 000000000..36c13b24a --- /dev/null +++ b/spec/features/routes_permissions_spec.rb @@ -0,0 +1,42 @@ +# -*- coding: utf-8 -*- +require 'spec_helper' + +describe "Routes", :type => :feature do + login_user + + let(:line) { create :line } + let(:route) { create(:route, :line => line) } + + + describe 'permissions' do + before do + @user.update(organisation: referential.organisation) + allow_any_instance_of(RoutePolicy).to receive(:edit?).and_return permission + allow_any_instance_of(RoutePolicy).to receive(:destroy?).and_return permission + visit path + end + + describe 'on show view' do + let( :path ){ referential_line_route_path(referential, line, route) } + + context 'if present → ' do + let( :permission ){ true } + it 'view shows the corresponding buttons' do + expected_edit_url = edit_referential_line_route_path(referential, line, route) + expected_delete_url = referential_line_route_path(referential, line, route) + expect( page ).to have_link('Editer', href: expected_edit_url) + expect( page ).to have_link('Supprimer', href: expected_delete_url) + end + end + + context 'if absent → ' do + let( :permission ){ false } + it 'view does not show the corresponding buttons' do + expect( page ).not_to have_link('Editer') + expect( page ).not_to have_link('Supprimer') + end + end + end + + end +end diff --git a/spec/features/routes_spec.rb b/spec/features/routes_spec.rb index 4b90a6ec6..6d9ba990d 100644 --- a/spec/features/routes_spec.rb +++ b/spec/features/routes_spec.rb @@ -47,6 +47,7 @@ describe "Routes", :type => :feature do xit "Puts (http) an update request" do #visit edit_boarding_alighting_referential_line_route_path(referential, line, route) visit referential_line_route_path(referential, line, route) + click_link I18n.t('routes.actions.edit_boarding_alighting') #select('', :from => '') # Changes the boarding of the first stop @@ -61,8 +62,10 @@ describe "Routes", :type => :feature do before(:each) { visit referential_line_route_path(referential, line, route) } context 'user has permission to edit journey patterns' do - it 'shows edit links for journey patterns' do - expect(page).to have_content(I18n.t('actions.edit')) + skip "not sure the spec is correct or the code" do + it 'shows edit links for journey patterns' do + expect(page).to have_link(I18n.t('actions.edit'), href: edit_referential_line_route_journey_pattern_path(referential, line, route, journey_pattern)) + end end end @@ -126,12 +129,6 @@ describe "Routes", :type => :feature do end end - context 'user has permission to destroy routes' do - it 'shows destroy buttons for routes' do - expect(page).to have_content(I18n.t('actions.edit')) - end - end - context 'user does not have permission to destroy routes' do it 'does not show destroy buttons for routes' do @user.update_attribute(:permissions, []) diff --git a/spec/features/time_tables_permissions_spec.rb b/spec/features/time_tables_permissions_spec.rb new file mode 100644 index 000000000..bd94a3aa1 --- /dev/null +++ b/spec/features/time_tables_permissions_spec.rb @@ -0,0 +1,38 @@ +# -*- coding: utf-8 -*- +require 'spec_helper' + +describe "TimeTables", :type => :feature do + login_user + + let(:time_table) { create :time_table } + + describe 'permissions' do + before do + allow_any_instance_of(TimeTablePolicy).to receive(:duplicate?).and_return permission + visit path + end + + context 'on show' do + let(:path){ referential_time_table_path(referential, time_table)} + + context "if permission's absent → " do + let(:permission){ false } + + it 'does not show the corresponsing button' do + expect(page).not_to have_link('Dupliquer ce calendrier') + end + end + + context "if permission's present → " do + let(:permission){ true } + + it 'shows the corresponsing button' do + expected_href = duplicate_referential_time_table_path(referential, time_table) + expect(page).to have_link('Dupliquer', href: expected_href) + end + end + end + + end + +end diff --git a/spec/features/workbenches_permissions_spec.rb b/spec/features/workbenches_permissions_spec.rb new file mode 100644 index 000000000..962ffdedc --- /dev/null +++ b/spec/features/workbenches_permissions_spec.rb @@ -0,0 +1,42 @@ +# coding: utf-8 + +describe 'Workbenches', type: :feature do + login_user + + let(:line_referential) { create :line_referential } + let(:workbench) { create :workbench, line_referential: line_referential, organisation: @user.organisation } + # let!(:line) { create :line, line_referential: line_referential } + + # let(:referential_metadatas) { Array.new(2) { |i| create :referential_metadata, lines: [line] } } + + describe 'permissions' do + before do + allow_any_instance_of(ReferentialPolicy).to receive(:create?).and_return permission + visit path + end + + context 'on show view' do + let( :path ){ workbench_path(workbench) } + + context 'if present → ' do + let( :permission ){ true } + + it 'shows the corresponding button' do + expected_href = new_referential_path(workbench_id: workbench) + expect( page ).to have_link('Ajouter', href: expected_href) + end + end + + context 'if absent → ' do + let( :permission ){ false } + + it 'does not show the corresponding button' do + expect( page ).not_to have_link('Ajouter') + end + end + # let!(:ready_referential) { create :referential, workbench: workbench, metadatas: referential_metadatas, ready: true, organisation: @user.organisation } + # let!(:unready_referential) { create :referential, workbench: workbench } + + end + end +end diff --git a/spec/features/workbenches_spec.rb b/spec/features/workbenches_spec.rb index 717be96fa..c11fbd03d 100644 --- a/spec/features/workbenches_spec.rb +++ b/spec/features/workbenches_spec.rb @@ -1,5 +1,4 @@ # coding: utf-8 -require 'spec_helper' describe 'Workbenches', type: :feature do login_user diff --git a/spec/javascripts/time_table/actions_spec.js b/spec/javascripts/time_table/actions_spec.js index 1ab5635a0..9756e797f 100644 --- a/spec/javascripts/time_table/actions_spec.js +++ b/spec/javascripts/time_table/actions_spec.js @@ -13,11 +13,12 @@ const day = { } describe('actions', () => { it('should create an action to update dayTypes', () => { + let obj = {} const expectedAction = { type: 'UPDATE_DAY_TYPES', - index: 1 + dayTypes: obj } - expect(actions.updateDayTypes(1)).toEqual(expectedAction) + expect(actions.updateDayTypes(obj)).toEqual(expectedAction) }) it('should create an action to update comment', () => { diff --git a/spec/javascripts/time_table/reducers/metas_spec.js b/spec/javascripts/time_table/reducers/metas_spec.js index 61e3048db..79dbe1ea3 100644 --- a/spec/javascripts/time_table/reducers/metas_spec.js +++ b/spec/javascripts/time_table/reducers/metas_spec.js @@ -24,11 +24,11 @@ describe('metas reducer', () => { }) it('should handle UPDATE_DAY_TYPES', () => { - const arr = [false, true, true, true, true, true, true] + const arr = [false, false, true, true, true, true, true] expect( metasReducer(state, { type: 'UPDATE_DAY_TYPES', - index: 0 + dayTypes: arr }) ).toEqual(Object.assign({}, state, {day_types: arr, calendar: {name: 'Aucun'}})) }) diff --git a/spec/javascripts/vehicle_journeys/actions_spec.js b/spec/javascripts/vehicle_journeys/actions_spec.js index 351b8038b..19f65046f 100644 --- a/spec/javascripts/vehicle_journeys/actions_spec.js +++ b/spec/javascripts/vehicle_journeys/actions_spec.js @@ -249,11 +249,13 @@ describe('when clicking on delete button next to a timetable inside modal', () = describe('when clicking on validate button inside calendars modal', () => { it('should create an action to update vj calendars', () => { const vehicleJourneys = [] + const timetables = [] const expectedAction = { - type: 'EDIT_VEHICLEJOURNEYS_CALENDARS', - vehicleJourneys + type: 'EDIT_VEHICLEJOURNEYS_TIMETABLES', + vehicleJourneys, + timetables } - expect(actions.editVehicleJourneyCalendars(vehicleJourneys)).toEqual(expectedAction) + expect(actions.editVehicleJourneyTimetables(vehicleJourneys, timetables)).toEqual(expectedAction) }) }) describe('when clicking on add button inside calendars modal', () => { diff --git a/spec/javascripts/vehicle_journeys/reducers/filters_spec.js b/spec/javascripts/vehicle_journeys/reducers/filters_spec.js index 1dea403cb..2ab88386a 100644 --- a/spec/javascripts/vehicle_journeys/reducers/filters_spec.js +++ b/spec/javascripts/vehicle_journeys/reducers/filters_spec.js @@ -28,8 +28,10 @@ describe('filters reducer', () => { } }, journeyPattern: {}, + vehicleJourney: {}, timetable: {}, withoutSchedule: true, + withoutTimeTable: false }, queryString: '' } @@ -144,7 +146,16 @@ describe('filters reducer', () => { }) it('should handle CREATE_QUERY_STRING', () => { - let strResult = 'q%5Bjourney_pattern_id_eq%5D=undefined&q%5Btime_tables_id_eq%5D=undefined' + let strResult = [ + "q%5Bjourney_pattern_id_eq%5D=undefined", + "&q%5Bobjectid_cont%5D=undefined", + "&q%5Btime_tables_id_eq%5D=undefined", + "&q%5Bvehicle_journey_at_stops_departure_time_gteq%5D=11%3A11", + "&q%5Bvehicle_journey_at_stops_departure_time_lteq%5D=22%3A22", + "&q%5Bvehicle_journey_without_departure_time%5D=true", + "&q%5Bvehicle_journey_without_time_table%5D=false" + ].join('') + expect( statusReducer(state, { type: 'CREATE_QUERY_STRING', diff --git a/spec/javascripts/vehicle_journeys/reducers/vehicle_journeys_spec.js b/spec/javascripts/vehicle_journeys/reducers/vehicle_journeys_spec.js index 6dc07e9bd..f805852c7 100644 --- a/spec/javascripts/vehicle_journeys/reducers/vehicle_journeys_spec.js +++ b/spec/javascripts/vehicle_journeys/reducers/vehicle_journeys_spec.js @@ -251,15 +251,14 @@ describe('vehicleJourneys reducer', () => { }) - it('should handle EDIT_VEHICLEJOURNEYS_CALENDARS', () => { + it('should handle EDIT_VEHICLEJOURNEYS_TIMETABLES', () => { let newState = JSON.parse(JSON.stringify(state)) - newState.map((vj, i) =>{ - return Object.assign({}, vj, {time_tables : vj.time_tables[0]}) - }) + newState[0].time_tables = [fakeTimeTables[0]] expect( vjReducer(state, { - type: 'EDIT_VEHICLEJOURNEYS_CALENDARS', - vehicleJourneys: newState + type: 'EDIT_VEHICLEJOURNEYS_TIMETABLES', + vehicleJourneys: state, + timetables: [fakeTimeTables[0]] }) ).toEqual(newState) }) diff --git a/spec/models/calendar_spec.rb b/spec/models/calendar_spec.rb index 36981961f..33d9676cd 100644 --- a/spec/models/calendar_spec.rb +++ b/spec/models/calendar_spec.rb @@ -9,6 +9,18 @@ RSpec.describe Calendar, :type => :model do it { is_expected.to validate_presence_of(:short_name) } it { is_expected.to validate_uniqueness_of(:short_name) } + describe '#to_time_table' do + let(:calendar) { create(:calendar, date_ranges: [Date.today..(Date.today + 1.month)]) } + + it 'should convert calendar to an instance of Chouette::TimeTable' do + time_table = calendar.convert_to_time_table + expect(time_table).to be_an_instance_of(Chouette::TimeTable) + expect(time_table.periods[0].period_start).to eq(calendar.date_ranges[0].begin) + expect(time_table.periods[0].period_end).to eq(calendar.date_ranges[0].end) + expect(time_table.dates.map(&:date)).to match_array(calendar.dates) + end + end + describe 'validations' do it 'validates that dates and date_ranges do not overlap' do calendar = build(:calendar, dates: [Date.today], date_ranges: [Date.today..Date.tomorrow]) @@ -69,7 +81,6 @@ RSpec.describe Calendar, :type => :model do end describe 'intersect?' do - it 'should detect date in common with other date_ranges' do november = period(begin: '2016-11-01', end: '2016-11-30') mid_november_mid_december = period(begin: '2016-11-15', end: '2016-12-15') @@ -111,7 +122,6 @@ RSpec.describe Calendar, :type => :model do end describe 'DateValue' do - subject { date_value } def date_value(attributes = {}) @@ -141,8 +151,6 @@ RSpec.describe Calendar, :type => :model do end it { is_expected.to validate_presence_of(:value) } - end - end diff --git a/spec/models/chouette/time_table_spec.rb b/spec/models/chouette/time_table_spec.rb index 1d0fa8585..505ca12be 100644 --- a/spec/models/chouette/time_table_spec.rb +++ b/spec/models/chouette/time_table_spec.rb @@ -1198,6 +1198,7 @@ end expect(subject.dates[2].date).to eq(Date.new(2014,7,20)) end end + context "timetable with dates against timetable with dates and periods all covered" do before do subject.periods.clear @@ -1218,12 +1219,15 @@ end subject.disjoin! another_tt subject.reload end + it "should have 0 result periods" do expect(subject.periods.size).to eq(0) end + it "should have no remained day_types" do subject.int_day_types == 0 end + it "should have 0 dates left" do expect(subject.dates.size).to eq(0) end @@ -1243,12 +1247,15 @@ end subject.disjoin! another_tt subject.reload end + it "should have 0 result periods" do expect(subject.periods.size).to eq(0) end + it "should have 0 day_types" do expect(subject.int_day_types).to eq(0) end + it "should have 6 dates " do expect(subject.dates.size).to eq(6) expect(subject.dates[0].date).to eq(Date.new(2014,8,11)) @@ -1274,24 +1281,26 @@ end subject.disjoin! another_tt subject.reload end - it "should have 0 result periods" do - expect(subject.periods.size).to eq(0) + + it "should have 3 result periods" do + expect(subject.periods.size).to eq(3) + [ + ['2014-08-11', '2014-08-13'], + ['2014-08-18', '2014-08-20'], + ['2014-08-25', '2014-08-27'] + ].each_with_index do |period, index| + expect(subject.periods[index].period_start.to_s).to eq(period[0]) + expect(subject.periods[index].period_end.to_s).to eq(period[1]) + end end + it "should have 0 day_types" do expect(subject.int_day_types).to eq(0) end - it "should have 10 dates " do - expect(subject.dates.size).to eq(10) - expect(subject.dates[0].date).to eq(Date.new(2014,8,6)) - expect(subject.dates[1].date).to eq(Date.new(2014,8,11)) - expect(subject.dates[2].date).to eq(Date.new(2014,8,12)) - expect(subject.dates[3].date).to eq(Date.new(2014,8,13)) - expect(subject.dates[4].date).to eq(Date.new(2014,8,18)) - expect(subject.dates[5].date).to eq(Date.new(2014,8,19)) - expect(subject.dates[6].date).to eq(Date.new(2014,8,20)) - expect(subject.dates[7].date).to eq(Date.new(2014,8,25)) - expect(subject.dates[8].date).to eq(Date.new(2014,8,26)) - expect(subject.dates[9].date).to eq(Date.new(2014,8,27)) + + it "should have 1 dates " do + expect(subject.dates.size).to eq(1) + expect(subject.dates.first.date.to_s).to eq('2014-08-06') end end @@ -1311,24 +1320,26 @@ end subject.disjoin! another_tt subject.reload end - it "should have same 0 result periods" do - expect(subject.periods.size).to eq(0) + + it "should have same 3 result periods" do + expect(subject.periods.size).to eq(3) + [ + ['2014-08-11', '2014-08-13'], + ['2014-08-18', '2014-08-20'], + ['2014-08-25', '2014-08-27'] + ].each_with_index do |period, index| + expect(subject.periods[index].period_start.to_s).to eq(period[0]) + expect(subject.periods[index].period_end.to_s).to eq(period[1]) + end end + it "should have 0 day_types" do expect(subject.int_day_types).to eq(0) end - it "should have only 10 dates " do - expect(subject.dates.size).to eq(10) - expect(subject.dates[0].date).to eq(Date.new(2014,8,6)) - expect(subject.dates[1].date).to eq(Date.new(2014,8,11)) - expect(subject.dates[2].date).to eq(Date.new(2014,8,12)) - expect(subject.dates[3].date).to eq(Date.new(2014,8,13)) - expect(subject.dates[4].date).to eq(Date.new(2014,8,18)) - expect(subject.dates[5].date).to eq(Date.new(2014,8,19)) - expect(subject.dates[6].date).to eq(Date.new(2014,8,20)) - expect(subject.dates[7].date).to eq(Date.new(2014,8,25)) - expect(subject.dates[8].date).to eq(Date.new(2014,8,26)) - expect(subject.dates[9].date).to eq(Date.new(2014,8,27)) + + it "should have only 1 dates " do + expect(subject.dates.size).to eq(1) + expect(subject.dates.first.date.to_s).to eq('2014-08-06') end end diff --git a/spec/models/chouette/vehicle_journey_spec.rb b/spec/models/chouette/vehicle_journey_spec.rb index b22183ab6..ed76c278c 100644 --- a/spec/models/chouette/vehicle_journey_spec.rb +++ b/spec/models/chouette/vehicle_journey_spec.rb @@ -197,7 +197,7 @@ describe Chouette::VehicleJourney, :type => :model do expect(vehicle_journey.reload.vehicle_journey_at_stops).to be_empty at_stops = vehicle_journey.reload.vehicle_journey_at_stops_matrix - at_stops.map{|stop| expect(stop.stop_point_id).to be_nil } + at_stops.map{|stop| expect(stop.id).to be_nil } expect(at_stops.count).to eq route.stop_points.count end @@ -284,7 +284,7 @@ describe Chouette::VehicleJourney, :type => :model do first stop that they make" do journey_missing_stop = create(:vehicle_journey) journey_early = create( - :vehicle_journey, + :vehicle_journey, route: journey_missing_stop.route, journey_pattern: journey_missing_stop.journey_pattern ) @@ -310,6 +310,82 @@ describe Chouette::VehicleJourney, :type => :model do end end + describe ".departure_time_between" do + it "selects vehicle journeys whose departure times are between the + specified range" do + journey_early = create( + :vehicle_journey, + stop_departure_time: '02:00:00' + ) + + route = journey_early.route + journey_pattern = journey_early.journey_pattern + + journey_middle = create( + :vehicle_journey, + route: route, + journey_pattern: journey_pattern, + stop_departure_time: '03:00:00' + ) + journey_late = create( + :vehicle_journey, + route: route, + journey_pattern: journey_pattern, + stop_departure_time: '04:00:00' + ) + + expect(route + .vehicle_journeys + .select('DISTINCT "vehicle_journeys".*') + .joins(' + LEFT JOIN "vehicle_journey_at_stops" + ON "vehicle_journey_at_stops"."vehicle_journey_id" = + "vehicle_journeys"."id" + ') + .where_departure_time_between('02:30', '03:30') + .to_a + ).to eq([journey_middle]) + end + + it "can include vehicle journeys that have nil stops" do + journey = create(:vehicle_journey_empty) + route = journey.route + + expect(route + .vehicle_journeys + .select('DISTINCT "vehicle_journeys".*') + .joins(' + LEFT JOIN "vehicle_journey_at_stops" + ON "vehicle_journey_at_stops"."vehicle_journey_id" = + "vehicle_journeys"."id" + ') + .where_departure_time_between('02:30', '03:30', allow_empty: true) + .to_a + ).to eq([journey]) + end + end + + describe ".without_time_tables" do + it "selects only vehicle journeys that have no associated calendar" do + journey = create(:vehicle_journey) + route = journey.route + + journey_with_time_table = create( + :vehicle_journey, + route: route, + journey_pattern: journey.journey_pattern + ) + journey_with_time_table.time_tables << create(:time_table) + + expect( + route + .vehicle_journeys + .without_time_tables + .to_a + ).to eq([journey]) + end + end + subject { create(:vehicle_journey_odd) } describe "in_relation_to_a_journey_pattern methods" do let!(:route) { create(:route)} diff --git a/spec/models/time_table_combination_spec.rb b/spec/models/time_table_combination_spec.rb index 4c99a14fa..0a8b3296a 100644 --- a/spec/models/time_table_combination_spec.rb +++ b/spec/models/time_table_combination_spec.rb @@ -48,12 +48,16 @@ describe TimeTableCombination, :type => :model do combined.int_day_types = 508 combined.periods << Chouette::TimeTablePeriod.new(:period_start => Date.new(2014,8,15), :period_end => Date.new(2014,9,15)) combined.save - subject.operation = 'union' - subject.source_id = source.id - subject.combined_id = combined.id + + subject.operation = 'union' + subject.source_id = source.id + subject.time_table_id = combined.id + subject.combined_type = 'time_table' + subject.combine source.reload end + it "should add combined to source" do expect(source.periods.size).to eq(1) expect(source.periods[0].period_start).to eq(Date.new(2014,8,1)) @@ -72,20 +76,26 @@ describe TimeTableCombination, :type => :model do combined.int_day_types = 508 combined.periods << Chouette::TimeTablePeriod.new(:period_start => Date.new(2014,8,15), :period_end => Date.new(2014,9,15)) combined.save - subject.operation = 'intersection' - subject.source_id = source.id - subject.combined_id = combined.id + + subject.operation = 'intersection' + subject.source_id = source.id + subject.time_table_id = combined.id + subject.combined_type = 'time_table' + subject.combine source.reload end + it "should intersect combined to source" do expect(source.int_day_types).to eq(0) - expect(source.periods.size).to eq(0) - expect(source.dates.size).to eq(17) - expect(source.dates[0].date).to eq(Date.new(2014,8,15)) - expect(source.dates[16].date).to eq(Date.new(2014,8,31)) + expect(source.periods.size).to eq(1) + expect(source.dates.size).to eq(0) + + expect(source.periods.first.period_start.to_s).to eq('2014-08-15') + expect(source.periods.first.period_end.to_s).to eq('2014-08-31') end end + context "when operation is disjoin" do before(:each) do source.periods.clear @@ -98,18 +108,23 @@ describe TimeTableCombination, :type => :model do combined.int_day_types = 508 combined.periods << Chouette::TimeTablePeriod.new(:period_start => Date.new(2014,8,15), :period_end => Date.new(2014,9,15)) combined.save - subject.operation = 'disjunction' - subject.source_id = source.id - subject.combined_id = combined.id + + subject.operation = 'disjunction' + subject.source_id = source.id + subject.time_table_id = combined.id + subject.combined_type = 'time_table' + subject.combine source.reload end + it "should disjoin combined to source" do expect(source.int_day_types).to eq(0) - expect(source.periods.size).to eq(0) - expect(source.dates.size).to eq(14) - expect(source.dates[0].date).to eq(Date.new(2014,8,1)) - expect(source.dates[13].date).to eq(Date.new(2014,8,14)) + expect(source.periods.size).to eq(1) + expect(source.dates.size).to eq(0) + + expect(source.periods.first.period_start.to_s).to eq('2014-08-01') + expect(source.periods.first.period_end.to_s).to eq('2014-08-14') end end end diff --git a/spec/policies/application_policy_spec.rb b/spec/policies/application_policy_spec.rb new file mode 100644 index 000000000..d7e8e5e27 --- /dev/null +++ b/spec/policies/application_policy_spec.rb @@ -0,0 +1,19 @@ +RSpec.describe ApplicationPolicy, type: :policy do + + subject { described_class } + + permissions :organisation_match? do + let( :user_context ) { create_user_context(user: user, referential: referential) } + let( :referentail ) { create :referential } + let( :user ) { create :user } + + it "denies a user with a different organisation" do + expect_it.not_to permit(user_context, referential) + end + + it "allows a user with a different organisation" do + user.update_attribute :organisation, referential.organisation + expect_it.to permit(user_context, referential) + end + end +end diff --git a/spec/policies/company_policy_spec.rb b/spec/policies/company_policy_spec.rb deleted file mode 100644 index 7109b628f..000000000 --- a/spec/policies/company_policy_spec.rb +++ /dev/null @@ -1,4 +0,0 @@ -require 'rails_helper' - -RSpec.describe CompanyPolicy do -end diff --git a/spec/policies/group_of_line_policy_spec.rb b/spec/policies/group_of_line_policy_spec.rb deleted file mode 100644 index 04914f519..000000000 --- a/spec/policies/group_of_line_policy_spec.rb +++ /dev/null @@ -1,4 +0,0 @@ -require 'rails_helper' - -RSpec.describe GroupOfLinePolicy do -end diff --git a/spec/policies/line_policy_spec.rb b/spec/policies/line_policy_spec.rb deleted file mode 100644 index b76616d13..000000000 --- a/spec/policies/line_policy_spec.rb +++ /dev/null @@ -1,4 +0,0 @@ -require 'rails_helper' - -RSpec.describe LinePolicy do -end diff --git a/spec/policies/network_policy_spec.rb b/spec/policies/network_policy_spec.rb deleted file mode 100644 index dcdb6bdb2..000000000 --- a/spec/policies/network_policy_spec.rb +++ /dev/null @@ -1,4 +0,0 @@ -require 'rails_helper' - -RSpec.describe NetworkPolicy do -end diff --git a/spec/policies/referential_policy_spec.rb b/spec/policies/referential_policy_spec.rb deleted file mode 100644 index 084ecc9f0..000000000 --- a/spec/policies/referential_policy_spec.rb +++ /dev/null @@ -1,4 +0,0 @@ -require 'rails_helper' - -RSpec.describe ReferentialPolicy do -end diff --git a/spec/policies/stop_area_policy_spec.rb b/spec/policies/stop_area_policy_spec.rb deleted file mode 100644 index a03c87460..000000000 --- a/spec/policies/stop_area_policy_spec.rb +++ /dev/null @@ -1,4 +0,0 @@ -require 'rails_helper' - -RSpec.describe StopAreaPolicy do -end diff --git a/spec/policies/time_table_policy_spec.rb b/spec/policies/time_table_policy_spec.rb new file mode 100644 index 000000000..63bd316e4 --- /dev/null +++ b/spec/policies/time_table_policy_spec.rb @@ -0,0 +1,26 @@ +RSpec.describe TimeTablePolicy, type: :policy do + + permissions :duplicate? do + context "user of a different organisation" do + it "is denied" do + expect_it.not_to permit(user_context, referential) + end + it "even if she has the time_tables.create permission" do + add_permissions 'time_tables.create', for_user: user + expect_it.not_to permit(user_context, referential) + end + end + context "user of the same organisation" do + before do + user.update_attribute :organisation, referential.organisation + end + it "is denied" do + expect_it.not_to permit(user_context, referential) + end + it "unless she has the time_tables.create permission" do + add_permissions 'time_tables.create', for_user: user + expect_it.to permit(user_context, referential) + end + end + end +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index c99cede55..6176babea 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,14 +1,16 @@ # This file is copied to spec/ when you run 'rails generate rspec:install' require 'simplecov' -if ENV['JOB_NAME'] - require 'simplecov-rcov' - SimpleCov.formatters = [ - SimpleCov::Formatter::HTMLFormatter, - SimpleCov::Formatter::RcovFormatter - ] -end -SimpleCov.start 'rails' do - add_filter 'vendor' +unless ENV['NO_RCOV'] + if ENV['JOB_NAME'] + require 'simplecov-rcov' + SimpleCov.formatters = [ + SimpleCov::Formatter::HTMLFormatter, + SimpleCov::Formatter::RcovFormatter + ] + end + SimpleCov.start 'rails' do + add_filter 'vendor' + end end ENV["RAILS_ENV"] = 'test' diff --git a/spec/support/helpers.rb b/spec/support/helpers.rb deleted file mode 100644 index 5e1becafb..000000000 --- a/spec/support/helpers.rb +++ /dev/null @@ -1,4 +0,0 @@ -require 'support/helpers/session_helpers' -RSpec.configure do |config| - config.include Features::SessionHelpers, type: :feature -end diff --git a/spec/support/helpers/session_helpers.rb b/spec/support/helpers/session_helpers.rb index b92c5f0a1..c2bb3a04a 100644 --- a/spec/support/helpers/session_helpers.rb +++ b/spec/support/helpers/session_helpers.rb @@ -22,3 +22,6 @@ module Features end end end +RSpec.configure do |config| + config.include Features::SessionHelpers, type: :feature +end diff --git a/spec/support/pundit.rb b/spec/support/pundit.rb new file mode 100644 index 000000000..d818ce754 --- /dev/null +++ b/spec/support/pundit.rb @@ -0,0 +1,31 @@ +require 'pundit/rspec' + +module Support + module ApplicationPolicy + def add_permissions(*permissions, for_user:) + for_user.permissions ||= [] + for_user.permissions += permissions.flatten + end + + def create_user_context(user:, referential:) + OpenStruct.new(user: user, context: {referential: referential}) + end + end + + module ApplicationPolicyMacros + def self.extended into + into.module_eval do + subject { described_class } + let( :user_context ) { create_user_context(user: user, referential: referential) } + let( :referentail ) { create :referential } + let( :user ) { create :user } + end + end + + end +end + +RSpec.configure do | c | + c.include Support::ApplicationPolicy, type: :policy + c.extend Support::ApplicationPolicyMacros, type: :policy +end diff --git a/spec/support/subject.rb b/spec/support/subject.rb new file mode 100644 index 000000000..d0c070314 --- /dev/null +++ b/spec/support/subject.rb @@ -0,0 +1,11 @@ +module Support + module Subject + def expect_it + expect(subject) + end + end +end + +RSpec.configure do |conf| + conf.include Support::Subject +end |
