diff --git a/lib/editor/actions/trip.js b/lib/editor/actions/trip.js index 7cedd4120..9d12e16bc 100644 --- a/lib/editor/actions/trip.js +++ b/lib/editor/actions/trip.js @@ -35,6 +35,12 @@ export function fetchTripsForCalendar (feedId, pattern, calendarId) { id: pattern_id trips (service_id: $service_id, limit: -1) { id + frequencies { + startTime: start_time + endTime: end_time + headwaySecs: headway_secs + exactTimes: exact_times + } tripId: trip_id tripHeadsign: trip_headsign tripShortName: trip_short_name @@ -160,6 +166,10 @@ export function saveMultipleTripsForCalendar (feedId, pattern, calendarId, trips } } +/** + * Delete multiple trips. This method takes the provided trips and maps the trips' + * IDs to a comma-separated query parameter, indicating which trips to delete. + */ export function deleteTripsForCalendar (feedId, pattern, calendarId, trips) { return function (dispatch, getState) { let errorCount = 0 diff --git a/lib/editor/actions/tripPattern.js b/lib/editor/actions/tripPattern.js index 74f4e1f38..ce7cdc884 100644 --- a/lib/editor/actions/tripPattern.js +++ b/lib/editor/actions/tripPattern.js @@ -74,3 +74,16 @@ export function saveTripPattern (feedId, tripPattern) { }) } } + +/** + * Deletes all trips for a given pattern ID. This is used to clear the trips for + * a pattern if/when a pattern is changed from frequency-based to timetable-baed. + */ +export function deleteAllTripsForPattern (feedId, patternId) { + return function (dispatch, getState) { + const url = `/api/editor/secure/pattern/${patternId}/trips?feedId=${feedId}` + return dispatch(secureFetch(url, 'delete')) + .then(res => res.json()) + .then(json => json && json.result === 'OK') + } +} diff --git a/lib/editor/components/pattern/EditSchedulePanel.js b/lib/editor/components/pattern/EditSchedulePanel.js index 604868899..dca040079 100644 --- a/lib/editor/components/pattern/EditSchedulePanel.js +++ b/lib/editor/components/pattern/EditSchedulePanel.js @@ -29,20 +29,24 @@ export default class EditSchedulePanel extends Component { } _onChangeUseFrequency = key => { + const {activePattern, deleteAllTripsForPattern, feedSource, saveActiveEntity, showConfirmModal, updateActiveEntity} = this.props const useFrequency = key !== 'timetables' ? 1 : 0 const unselectedOption = key === 'timetables' ? 'frequencies' : 'timetables' - this.props.showConfirmModal({ - title: `Use ${key} for ${this.props.activePattern.name}?`, + showConfirmModal({ + title: `Use ${key} for ${activePattern.name}?`, body: `Are you sure you want to use ${key} for this trip pattern? Any trips created using ${unselectedOption} will be lost.`, onConfirm: () => { - this.props.updateActiveEntity(this.props.activePattern, 'trippattern', {useFrequency}) - this.props.saveActiveEntity('trippattern') + // Update and save useFrequency field + updateActiveEntity(activePattern, 'trippattern', {useFrequency}) + saveActiveEntity('trippattern') + // Then, delete all trips for the pattern. + .then(() => deleteAllTripsForPattern(feedSource.id, activePattern.patternId)) } }) } render () { - const { activePattern, activePatternId } = this.props + const {activePattern, activePatternId} = this.props const timetableOptions = [ Use timetables, Use frequencies diff --git a/lib/editor/components/timetable/Timetable.js b/lib/editor/components/timetable/Timetable.js index 283ecc4a1..68e0e1c28 100644 --- a/lib/editor/components/timetable/Timetable.js +++ b/lib/editor/components/timetable/Timetable.js @@ -78,6 +78,7 @@ export default class Timetable extends Component { updateCellValue={updateCellValue} columns={columns} hideDepartureTimes={hideDepartureTimes} + saveEditedTrips={this.saveEditedTrips} setActiveCell={this.setActiveCell} {...this.props} {...stepperProps} diff --git a/lib/editor/components/timetable/TimetableEditor.js b/lib/editor/components/timetable/TimetableEditor.js index f3bf41059..3af32a91c 100644 --- a/lib/editor/components/timetable/TimetableEditor.js +++ b/lib/editor/components/timetable/TimetableEditor.js @@ -105,6 +105,11 @@ export default class TimetableEditor extends Component { objectPath.set(newRow, 'id', ENTITY.NEW_ID) objectPath.set(newRow, 'tripId', null) objectPath.set(newRow, 'useFrequency', activePattern.useFrequency) + if (activePattern.useFrequency) { + // If a frequency-based trip, never use exact times. NOTE: there is no + // column to edit this field in the timetable grid. + objectPath.set(newRow, 'frequencies.0.exactTimes', 0) + } objectPath.set(newRow, 'feedId', this.props.feedSource.id) objectPath.set(newRow, 'patternId', activePattern.patternId) objectPath.set(newRow, 'routeId', activePattern.routeId) @@ -249,6 +254,7 @@ export default class TimetableEditor extends Component { toggleRowSelection={toggleRowSelection} toggleAllRows={toggleAllRows} scrollToRow={scrollToRow} + saveEditedTrips={this.saveEditedTrips} scrollToColumn={scrollToColumn} {...this.props} /> :

diff --git a/lib/editor/components/timetable/TimetableGrid.js b/lib/editor/components/timetable/TimetableGrid.js index 962961181..9fdcbae79 100644 --- a/lib/editor/components/timetable/TimetableGrid.js +++ b/lib/editor/components/timetable/TimetableGrid.js @@ -30,6 +30,7 @@ export default class TimetableGrid extends Component { static propTypes = { updateCellValue: PropTypes.func, toggleAllRows: PropTypes.func, + saveEditedTrips: PropTypes.func, selected: PropTypes.array, toggleRowSelection: PropTypes.func } @@ -50,7 +51,10 @@ export default class TimetableGrid extends Component { handleKeyPress = (evt) => { const { activeCell, + activePattern, + activeScheduleId, data, + saveEditedTrips, setActiveCell, scrollToColumn, scrollToRow, @@ -67,7 +71,14 @@ export default class TimetableGrid extends Component { break case 13: // Enter if (!activeCell) { - return setActiveCell(`${scrollToRow}-${scrollToColumn}`) + if (evt.metaKey || evt.ctrlKey) { + // If Enter is pressed with CTRL or CMD and no cell is active, save + // any unsaved trips. + return saveEditedTrips(activePattern, activeScheduleId) + } else { + // Otherwise, set active cell + return setActiveCell(`${scrollToRow}-${scrollToColumn}`) + } } else { return setActiveCell(null) } @@ -242,6 +253,7 @@ export default class TimetableGrid extends Component { (selected[0] !== '*' && selected.indexOf(rowIndex) !== -1) let val = objectPath.get(data[rowIndex], col.key) + // console.log(val, data[rowIndex], col.key) if (col.key === 'tripId' && val === null) { val = objectPath.get(data[rowIndex], 'id') !== ENTITY.NEW_ID ? objectPath.get(data[rowIndex], 'id') : null } diff --git a/lib/editor/containers/ActiveTripPatternList.js b/lib/editor/containers/ActiveTripPatternList.js index d40555db3..0cedeba87 100644 --- a/lib/editor/containers/ActiveTripPatternList.js +++ b/lib/editor/containers/ActiveTripPatternList.js @@ -16,6 +16,7 @@ import {addStopToPattern, removeStopFromPattern} from '../actions/map/stopStrate import {updatePatternGeometry} from '../actions/map' import {setErrorMessage} from '../../manager/actions/status' import { + deleteAllTripsForPattern, resnapStops, setActiveStop, setActivePatternSegment, @@ -79,6 +80,7 @@ const mapDispatchToProps = { newGtfsEntity, // Trip pattern specific actions addStopToPattern, + deleteAllTripsForPattern, removeStopFromPattern, resnapStops, setActivePatternSegment, diff --git a/lib/editor/selectors/timetable.js b/lib/editor/selectors/timetable.js index 00c526a9b..08e133fa3 100644 --- a/lib/editor/selectors/timetable.js +++ b/lib/editor/selectors/timetable.js @@ -70,24 +70,31 @@ export const getTimetableColumns = createSelector( columns.push({ name: 'Start time', width: 100, - key: 'startTime', + key: 'frequencies.0.startTime', type: 'TIME', placeholder: 'HH:MM:SS' }) columns.push({ name: 'End time', width: 100, - key: 'endTime', + key: 'frequencies.0.endTime', type: 'TIME', placeholder: 'HH:MM:SS' }) columns.push({ name: 'Headway', width: 60, - key: 'headway', + key: 'frequencies.0.headwaySecs', type: 'MINUTES', placeholder: '15 (min)' }) + // columns.push({ + // name: 'Exact times', + // width: 60, + // key: 'frequencies.0.exactTimes', + // type: 'MINUTES', + // placeholder: '15 (min)' + // }) } } return columns