aboutsummaryrefslogtreecommitdiffstats
path: root/app
diff options
context:
space:
mode:
Diffstat (limited to 'app')
-rw-r--r--app/assets/javascripts/main_menu.coffee45
-rw-r--r--app/assets/stylesheets/components/_referentials.sass4
-rw-r--r--app/assets/stylesheets/typography/_sboiv.sass2
-rw-r--r--app/controllers/autocomplete_purchase_windows_controller.rb12
-rw-r--r--app/controllers/journey_patterns_collections_controller.rb1
-rw-r--r--app/controllers/referentials_controller.rb12
-rw-r--r--app/controllers/vehicle_journeys_controller.rb86
-rw-r--r--app/javascript/date_filters/index.js2
-rw-r--r--app/javascript/date_filters/purchase_window.js5
-rw-r--r--app/javascript/packs/vehicle_journeys/index.js5
-rw-r--r--app/javascript/routes/form_helper.js17
-rw-r--r--app/javascript/routes/reducers/stopPoints.js10
-rw-r--r--app/javascript/vehicle_journeys/actions/index.js52
-rw-r--r--app/javascript/vehicle_journeys/components/Filters.js3
-rw-r--r--app/javascript/vehicle_journeys/components/SaveVehicleJourneys.js34
-rw-r--r--app/javascript/vehicle_journeys/components/Tools.js17
-rw-r--r--app/javascript/vehicle_journeys/components/VehicleJourney.js38
-rw-r--r--app/javascript/vehicle_journeys/components/VehicleJourneys.js8
-rw-r--r--app/javascript/vehicle_journeys/components/tools/PurchaseWindowsEditVehicleJourney.js152
-rw-r--r--app/javascript/vehicle_journeys/components/tools/TimetablesEditVehicleJourney.js10
-rw-r--r--app/javascript/vehicle_journeys/components/tools/select2s/TimetableSelect2.js14
-rw-r--r--app/javascript/vehicle_journeys/containers/SaveVehicleJourneys.js4
-rw-r--r--app/javascript/vehicle_journeys/containers/tools/PurchaseWindowsEditVehicleJourney.js38
-rw-r--r--app/javascript/vehicle_journeys/reducers/modal.js51
-rw-r--r--app/javascript/vehicle_journeys/reducers/vehicleJourneys.js30
-rw-r--r--app/models/calendar/period.rb2
-rw-r--r--app/models/chouette/purchase_window.rb3
-rw-r--r--app/models/chouette/vehicle_journey.rb12
-rw-r--r--app/models/concerns/period_support.rb4
-rw-r--r--app/policies/line_policy.rb1
-rw-r--r--app/views/autocomplete_purchase_windows/index.rabl12
-rw-r--r--app/views/journey_patterns_collections/show.html.slim3
-rw-r--r--app/views/referentials/_form.html.slim2
-rw-r--r--app/views/vehicle_journeys/index.html.slim3
-rw-r--r--app/views/vehicle_journeys/show.rabl16
35 files changed, 567 insertions, 143 deletions
diff --git a/app/assets/javascripts/main_menu.coffee b/app/assets/javascripts/main_menu.coffee
index 1c39be736..e943f448a 100644
--- a/app/assets/javascripts/main_menu.coffee
+++ b/app/assets/javascripts/main_menu.coffee
@@ -1,12 +1,11 @@
$ ->
- link = []
+ stickyActions = []
ptitleCont = ""
$(document).on 'page:before-change', ->
- link = []
+ stickyActions = []
ptitleCont = ""
-
$el = $('#main_nav')
# Opening/closing left-side menu
$el.find('.openMenu').on 'click', (e) ->
@@ -24,35 +23,45 @@ $ ->
limit = 51
if $(window).scrollTop() >= limit
- data = ""
- if ($('.page-action .small').length > 0)
- data = $('.page-action .small')[0].innerHTML
+ if stickyActions.length == 0
+ if ($('.page-action .small').length > 0)
+ stickyActions.push
+ content: [
+ $('.page-action .small'),
+ $('.page-action .small').first().next()
+ ]
+ originalParent: $('.page-action .small').parent()
+
+ for action in $(".sticky-action, .sticky-actions")
+ stickyActions.push
+ class: "small",
+ content: [$(action)]
+ originalParent: $(action).parent()
- if ($(".page-title").length > 0)
+ if $(".page-title").length > 0
ptitleCont = $(".page-title").html()
- stickyContent = '<div class="sticky-content">'
- stickyContent += '<div class="sticky-ptitle">' + ptitleCont + '</div>'
- stickyContent += '<div class="sticky-paction"><div class="small">' + data + '</div></div>'
- stickyContent += '</div>'
+ stickyContent = $('<div class="sticky-content"></div>')
+ stickyContent.append $("<div class='sticky-ptitle'>#{ptitleCont}</div>")
+ stickyContent.append $('<div class="sticky-paction"></div>')
$('#main_nav').addClass 'sticky'
if $('#menu_top').find('.sticky-content').length == 0
if ptitleCont.length > 0
$('#menu_top').children('.menu-content').after(stickyContent)
- if link.length == 0
- link = $('.page-action .small').next()
-
- $('.sticky-paction .small').after(link)
+ for item in stickyActions
+ for child in item.content
+ child.appendTo $('.sticky-paction')
else
$('#main_nav').removeClass 'sticky'
if $('#menu_top').find('.sticky-content').length > 0
- if !$('.page-action').find('.formSubmitr').length
- $('.page-action .small').after(link)
+ for item in stickyActions
+ for child in item.content
+ child.appendTo item.originalParent
$('.sticky-content').remove()
- sticker();
+ sticker()
# Sticky behavior
$(document).on 'scroll', sticker
diff --git a/app/assets/stylesheets/components/_referentials.sass b/app/assets/stylesheets/components/_referentials.sass
new file mode 100644
index 000000000..0bbb18f2b
--- /dev/null
+++ b/app/assets/stylesheets/components/_referentials.sass
@@ -0,0 +1,4 @@
+#referential_form
+ .metadatas-errors
+ margin-top: -20px
+ margin-bottom: 20px
diff --git a/app/assets/stylesheets/typography/_sboiv.sass b/app/assets/stylesheets/typography/_sboiv.sass
index 0ed2890fa..f0943f843 100644
--- a/app/assets/stylesheets/typography/_sboiv.sass
+++ b/app/assets/stylesheets/typography/_sboiv.sass
@@ -52,6 +52,8 @@
&.sb-5x
font-size: 5em
+ &.sb-strong
+ font-weight: bold
.sb-ZDLR:before
content: '\e904'
diff --git a/app/controllers/autocomplete_purchase_windows_controller.rb b/app/controllers/autocomplete_purchase_windows_controller.rb
new file mode 100644
index 000000000..70dc5a346
--- /dev/null
+++ b/app/controllers/autocomplete_purchase_windows_controller.rb
@@ -0,0 +1,12 @@
+class AutocompletePurchaseWindowsController < ChouetteController
+ respond_to :json, :only => [:index]
+
+ requires_feature :purchase_windows
+
+ include ReferentialSupport
+
+ protected
+ def collection
+ @purchase_windows = referential.purchase_windows.search(params[:q]).result.paginate(page: params[:page])
+ end
+end
diff --git a/app/controllers/journey_patterns_collections_controller.rb b/app/controllers/journey_patterns_collections_controller.rb
index 736fb1441..5fe78766c 100644
--- a/app/controllers/journey_patterns_collections_controller.rb
+++ b/app/controllers/journey_patterns_collections_controller.rb
@@ -49,6 +49,7 @@ class JourneyPatternsCollectionsController < ChouetteController
end
def user_permissions
+ @features = Hash[*current_organisation.features.map{|f| [f, true]}.flatten].to_json
policy = policy(:journey_pattern)
@perms =
%w{create destroy update}.inject({}) do | permissions, action |
diff --git a/app/controllers/referentials_controller.rb b/app/controllers/referentials_controller.rb
index 40e8264ce..9dbd659da 100644
--- a/app/controllers/referentials_controller.rb
+++ b/app/controllers/referentials_controller.rb
@@ -13,14 +13,16 @@ class ReferentialsController < ChouetteController
end
def create
- create! do |format|
+ create! do |success, failure|
build_referenial
- if !!@referential.created_from_id
- flash[:notice] = t('notice.referentials.duplicate')
-
- format.html { redirect_to workbench_path(@referential.workbench) }
+ success.html do
+ if @referential.created_from_id.present?
+ flash[:notice] = t('notice.referentials.duplicate')
+ redirect_to workbench_path(@referential.workbench)
+ end
end
+ failure.html { render :new }
end
end
diff --git a/app/controllers/vehicle_journeys_controller.rb b/app/controllers/vehicle_journeys_controller.rb
index c941aeae4..7434c777f 100644
--- a/app/controllers/vehicle_journeys_controller.rb
+++ b/app/controllers/vehicle_journeys_controller.rb
@@ -40,43 +40,46 @@ class VehicleJourneysController < ChouetteController
end
def index
- @stop_points_list = []
- route.stop_points.each do |sp|
- @stop_points_list << {
- :id => sp.stop_area.id,
- :route_id => sp.try(:route_id),
- :object_id => sp.try(:objectid),
- :position => sp.try(:position),
- :for_boarding => sp.try(:for_boarding),
- :for_alighting => sp.try(:for_alighting),
- :name => sp.stop_area.try(:name),
- :zip_code => sp.stop_area.try(:zip_code),
- :city_name => sp.stop_area.try(:city_name),
- :comment => sp.stop_area.try(:comment),
- :area_type => sp.stop_area.try(:area_type),
- :registration_number => sp.stop_area.try(:registration_number),
- :nearest_topic_name => sp.stop_area.try(:nearest_topic_name),
- :fare_code => sp.stop_area.try(:fare_code),
- :longitude => sp.stop_area.try(:longitude),
- :latitude => sp.stop_area.try(:latitude),
- :long_lat_type => sp.stop_area.try(:long_lat_type),
- :country_code => sp.stop_area.try(:country_code),
- :street_name => sp.stop_area.try(:street_name)
- }
- end
-
- @transport_mode = route.line['transport_mode']
- @transport_submode = route.line['transport_submode']
-
- if params[:jp]
- @jp_origin = Chouette::JourneyPattern.find_by(objectid: params[:jp])
- @jp_origin_stop_points = @jp_origin.stop_points
- end
-
- index! do
+ index! do |format|
if collection.out_of_bounds?
redirect_to params.merge(:page => 1)
end
+ format.json do
+ @vehicle_journeys = @vehicle_journeys.includes({stop_points: :stop_area})
+ end
+ format.html do
+ @stop_points_list = []
+ @stop_points_list = route.stop_points.joins(:stop_area).map do |sp|
+ {
+ :id => sp.stop_area.id,
+ :route_id => sp.try(:route_id),
+ :object_id => sp.try(:objectid),
+ :position => sp.try(:position),
+ :for_boarding => sp.try(:for_boarding),
+ :for_alighting => sp.try(:for_alighting),
+ :name => sp.stop_area.try(:name),
+ :zip_code => sp.stop_area.try(:zip_code),
+ :city_name => sp.stop_area.try(:city_name),
+ :comment => sp.stop_area.try(:comment),
+ :area_type => sp.stop_area.try(:area_type),
+ :registration_number => sp.stop_area.try(:registration_number),
+ :nearest_topic_name => sp.stop_area.try(:nearest_topic_name),
+ :fare_code => sp.stop_area.try(:fare_code),
+ :longitude => sp.stop_area.try(:longitude),
+ :latitude => sp.stop_area.try(:latitude),
+ :long_lat_type => sp.stop_area.try(:long_lat_type),
+ :country_code => sp.stop_area.try(:country_code),
+ :street_name => sp.stop_area.try(:street_name)
+ }
+ end
+ @transport_mode = route.line['transport_mode']
+ @transport_submode = route.line['transport_submode']
+
+ if params[:jp]
+ @jp_origin = Chouette::JourneyPattern.find_by(objectid: params[:jp])
+ @jp_origin_stop_points = @jp_origin.stop_points
+ end
+ end
end
end
@@ -92,13 +95,15 @@ class VehicleJourneysController < ChouetteController
scope = maybe_filter_by_departure_time(scope)
scope = maybe_filter_out_journeys_with_time_tables(scope)
- @q = scope.search filtered_ransack_params
+ @vehicle_journeys ||= begin
+ @q = scope.search filtered_ransack_params
- @ppage = 20
- @vehicle_journeys = @q.result.paginate(:page => params[:page], :per_page => @ppage)
- @footnotes = route.line.footnotes.to_json
- @matrix = resource_class.matrix(@vehicle_journeys)
- @vehicle_journeys
+ @ppage = 20
+ vehicle_journeys = @q.result.paginate(:page => params[:page], :per_page => @ppage)
+ @footnotes = route.line.footnotes.to_json
+ @matrix = resource_class.matrix(vehicle_journeys)
+ vehicle_journeys
+ end
end
def maybe_filter_by_departure_time(scope)
@@ -159,6 +164,7 @@ class VehicleJourneysController < ChouetteController
end
def user_permissions
+ @features = Hash[*current_organisation.features.map{|f| [f, true]}.flatten].to_json
policy = policy(:vehicle_journey)
@perms =
%w{create destroy update}.inject({}) do | permissions, action |
diff --git a/app/javascript/date_filters/index.js b/app/javascript/date_filters/index.js
index ee892a7fe..432166008 100644
--- a/app/javascript/date_filters/index.js
+++ b/app/javascript/date_filters/index.js
@@ -3,6 +3,7 @@ import complianceControlSetDF from './compliance_control_set'
import complianceCheckSetDF from './compliance_check_set'
import timetableDF from './time_table'
import importDF from './import'
+import purchaseWindowDF from './purchase_window'
import workbenchDF from './workbench'
const DateFilters = {
@@ -11,6 +12,7 @@ const DateFilters = {
complianceControlSetDF,
importDF,
timetableDF,
+ purchaseWindowDF,
workbenchDF
}
diff --git a/app/javascript/date_filters/purchase_window.js b/app/javascript/date_filters/purchase_window.js
new file mode 100644
index 000000000..2c46b6d52
--- /dev/null
+++ b/app/javascript/date_filters/purchase_window.js
@@ -0,0 +1,5 @@
+import DateFilter from '../helpers/date_filters'
+
+const purchaseWindowDF = new DateFilter("purchase_window_filter_btn", "Tous les champs du filtre de date doivent être remplis", "q_contains_date_NUMi")
+
+export default purchaseWindowDF \ No newline at end of file
diff --git a/app/javascript/packs/vehicle_journeys/index.js b/app/javascript/packs/vehicle_journeys/index.js
index 38431af1d..53c5d5417 100644
--- a/app/javascript/packs/vehicle_journeys/index.js
+++ b/app/javascript/packs/vehicle_journeys/index.js
@@ -23,6 +23,7 @@ var initialState = {
filters: {
selectedJourneyPatterns : selectedJP,
policy: window.perms,
+ features: window.features,
toggleArrivals: false,
queryString: '',
query: {
@@ -54,7 +55,7 @@ var initialState = {
},
status: {
- fetchSuccess: true,
+ fetchSuccess: false,
isFetching: false
},
vehicleJourneys: [],
@@ -99,4 +100,4 @@ render(
<App />
</Provider>,
document.getElementById('vehicle_journeys_wip')
-) \ No newline at end of file
+)
diff --git a/app/javascript/routes/form_helper.js b/app/javascript/routes/form_helper.js
index 8a3277234..865722fb6 100644
--- a/app/javascript/routes/form_helper.js
+++ b/app/javascript/routes/form_helper.js
@@ -7,14 +7,14 @@ const formHelper = {
input.setAttribute('name', formatedName)
input.setAttribute('value', value)
form.appendChild(input)
- },
+ },
addError: (ids) => {
ids.forEach((id) => {
if (!$(id).parents('.form-group').hasClass('has-error')) {
$(id).parents('.form-group').addClass('has-error')
$(id).parent().append(`<span class='help-block small'>${'doit être rempli(e)'}</span>`)
}
- })
+ })
},
cleanInputs: (ids) => {
ids.forEach((id) =>{
@@ -28,21 +28,22 @@ const formHelper = {
ids.forEach(id => {
$(id).val() == "" ? blankInputs.push(id) : filledInputs.push(id)
})
-
+
if (filledInputs.length > 0) formHelper.cleanInputs(filledInputs)
- if (blankInputs.length > 0) formHelper.addError(blankInputs)
+ if (blankInputs.length > 0) formHelper.addError(blankInputs)
},
handleStopPoints: (event, state) => {
if (state.stopPoints.length >= 2) {
state.stopPoints.map((stopPoint, i) => {
formHelper.addInput('id', stopPoint.stoppoint_id ? stopPoint.stoppoint_id : '', i)
formHelper.addInput('stop_area_id', stopPoint.stoparea_id, i)
- formHelper.addInput('position', i, i)
+ formHelper.addInput('position', stopPoint.index, i)
formHelper.addInput('for_boarding', stopPoint.for_boarding, i)
formHelper.addInput('for_alighting', stopPoint.for_alighting, i)
})
- if ($('.alert.alert-danger').length > 0) $('.alert.alert-danger').remove()
- } else {
+ if ($('.alert.alert-danger').length > 0) $('.alert.alert-danger').remove()
+ }
+ else {
event.preventDefault()
let msg = "L'itinéraire doit comporter au moins deux arrêts"
if ($('.alert.alert-danger').length == 0) {
@@ -52,4 +53,4 @@ const formHelper = {
}
}
-export default formHelper \ No newline at end of file
+export default formHelper
diff --git a/app/javascript/routes/reducers/stopPoints.js b/app/javascript/routes/reducers/stopPoints.js
index eeec06327..0b42b504f 100644
--- a/app/javascript/routes/reducers/stopPoints.js
+++ b/app/javascript/routes/reducers/stopPoints.js
@@ -38,15 +38,15 @@ const stopPoints = (state = [], action) => {
case 'MOVE_STOP_UP':
return [
...state.slice(0, action.index - 1),
- _.assign({}, state[action.index], { stoppoint_id: state[action.index - 1].stoppoint_id }),
- _.assign({}, state[action.index - 1], { stoppoint_id: state[action.index].stoppoint_id }),
+ _.assign({}, state[action.index], { index: action.index - 1 }),
+ _.assign({}, state[action.index - 1], { index: action.index }),
...state.slice(action.index + 1)
]
case 'MOVE_STOP_DOWN':
return [
...state.slice(0, action.index),
- _.assign({}, state[action.index + 1], { stoppoint_id: state[action.index].stoppoint_id }),
- _.assign({}, state[action.index], { stoppoint_id: state[action.index + 1].stoppoint_id }),
+ _.assign({}, state[action.index + 1], { index: action.index }),
+ _.assign({}, state[action.index], { index: action.index + 1 }),
...state.slice(action.index + 2)
]
case 'DELETE_STOP':
@@ -141,4 +141,4 @@ const stopPoints = (state = [], action) => {
}
}
-export default stopPoints \ No newline at end of file
+export default stopPoints
diff --git a/app/javascript/vehicle_journeys/actions/index.js b/app/javascript/vehicle_journeys/actions/index.js
index ce4b9209d..202c09440 100644
--- a/app/javascript/vehicle_journeys/actions/index.js
+++ b/app/javascript/vehicle_journeys/actions/index.js
@@ -84,7 +84,8 @@ const actions = {
selectedItem:{
id: selectedTT.id,
comment: selectedTT.comment,
- objectid: selectedTT.objectid
+ objectid: selectedTT.objectid,
+ color: selectedTT.color
}
}),
addSelectedTimetable: () => ({
@@ -99,6 +100,31 @@ const actions = {
vehicleJourneys,
timetables
}),
+ openPurchaseWindowsEditModal : (vehicleJourneys) => ({
+ type : 'EDIT_PURCHASE_WINDOWS_VEHICLEJOURNEY_MODAL',
+ vehicleJourneys
+ }),
+ selectPurchaseWindowsModal: (selectedWindow) =>({
+ type: 'SELECT_PURCHASE_WINDOW_MODAL',
+ selectedItem:{
+ id: selectedWindow.id,
+ name: selectedWindow.name,
+ color: selectedWindow.color,
+ objectid: selectedWindow.objectid
+ }
+ }),
+ addSelectedPurchaseWindow: () => ({
+ type: 'ADD_SELECTED_PURCHASE_WINDOW'
+ }),
+ deletePurchaseWindowsModal : (purchaseWindow) => ({
+ type : 'DELETE_PURCHASE_WINDOW_MODAL',
+ purchaseWindow
+ }),
+ editVehicleJourneyPurchaseWindows : (vehicleJourneys, purchase_windows) => ({
+ type: 'EDIT_VEHICLEJOURNEYS_PURCHASE_WINDOWS',
+ vehicleJourneys,
+ purchase_windows
+ }),
openShiftModal : () => ({
type : 'SHIFT_VEHICLEJOURNEY_MODAL'
}),
@@ -313,6 +339,7 @@ const actions = {
let val
for (val of json.vehicle_journeys){
var timeTables = []
+ var purchaseWindows = []
let tt
for (tt of val.time_tables){
timeTables.push({
@@ -322,6 +349,14 @@ const actions = {
color: tt.color
})
}
+ for (tt of val.purchase_windows){
+ purchaseWindows.push({
+ objectid: tt.objectid,
+ name: tt.name,
+ id: tt.id,
+ color: tt.color
+ })
+ }
let vjasWithDelta = val.vehicle_journey_at_stops.map((vjas, i) => {
actions.fillEmptyFields(vjas)
return actions.getDelta(vjas)
@@ -333,6 +368,7 @@ const actions = {
short_id: val.short_id,
footnotes: val.footnotes,
time_tables: timeTables,
+ purchase_windows: purchaseWindows,
vehicle_journey_at_stops: vjasWithDelta,
deletable: false,
selected: false,
@@ -439,6 +475,20 @@ const actions = {
vjas.delta = delta
return vjas
},
+ adjustSchedule: (action, schedule) => {
+ // we enforce that the departure time remains after the arrival time
+ actions.getDelta(schedule)
+ if(schedule.delta < 0){
+ if(action.isDeparture){
+ schedule.arrival_time = schedule.departure_time
+ }
+ else{
+ schedule.departure_time = schedule.arrival_time
+ }
+ actions.getDelta(schedule)
+ }
+ return schedule
+ },
getShiftedSchedule: ({departure_time, arrival_time}, additional_time) => {
// We create dummy dates objects to manipulate time more easily
let departureDT = new Date (Date.UTC(2017, 2, 1, parseInt(departure_time.hour), parseInt(departure_time.minute)))
diff --git a/app/javascript/vehicle_journeys/components/Filters.js b/app/javascript/vehicle_journeys/components/Filters.js
index db6707520..3bc4f7ff7 100644
--- a/app/javascript/vehicle_journeys/components/Filters.js
+++ b/app/javascript/vehicle_journeys/components/Filters.js
@@ -33,6 +33,7 @@ export default function Filters({filters, pagination, onFilter, onResetFilters,
onSelect2Timetable={onSelect2Timetable}
hasRoute={true}
chunkURL={("/autocomplete_time_tables.json?route_id=" + String(window.route_id))}
+ searchKey={"comment_or_objectid_cont_any"}
filters={filters}
isFilter={true}
/>
@@ -165,4 +166,4 @@ Filters.propTypes = {
onSelect2Timetable: PropTypes.func.isRequired,
onSelect2JourneyPattern: PropTypes.func.isRequired,
onSelect2VehicleJourney: PropTypes.func.isRequired
-} \ No newline at end of file
+}
diff --git a/app/javascript/vehicle_journeys/components/SaveVehicleJourneys.js b/app/javascript/vehicle_journeys/components/SaveVehicleJourneys.js
index e8c27f92e..8bab5baa9 100644
--- a/app/javascript/vehicle_journeys/components/SaveVehicleJourneys.js
+++ b/app/javascript/vehicle_journeys/components/SaveVehicleJourneys.js
@@ -14,16 +14,27 @@ export default class SaveVehicleJourneys extends Component{
<div className='row mt-md'>
<div className='col-lg-12 text-right'>
<form className='vehicle_journeys formSubmitr ml-xs' onSubmit={e => {e.preventDefault()}}>
- <button
- className='btn btn-default'
- type='button'
- onClick={e => {
- e.preventDefault()
- this.props.editMode ? this.props.onSubmitVehicleJourneys(this.props.dispatch, this.props.vehicleJourneys) : this.props.onEnterEditMode()
- }}
- >
- {this.props.editMode ? "Valider" : "Editer"}
- </button>
+ <div className="btn-group sticky-actions">
+ <button
+ className={'btn ' + (this.props.editMode ? 'btn-success' : 'btn-default') + (this.props.status.fetchSuccess ? '' : ' disabled')}
+ type='button'
+ onClick={e => {
+ e.preventDefault()
+ this.props.editMode ? this.props.onSubmitVehicleJourneys(this.props.dispatch, this.props.vehicleJourneys) : this.props.onEnterEditMode()
+ }}
+ >
+ {this.props.editMode ? "Valider" : "Editer"}
+ </button>
+ {this.props.editMode && <button
+ className='btn btn-default'
+ type='button'
+ onClick={e => {
+ e.preventDefault()
+ this.props.onExitEditMode()
+ }}
+ > Annuler </button>
+ }
+ </div>
</form>
</div>
</div>
@@ -38,5 +49,6 @@ SaveVehicleJourneys.propTypes = {
status: PropTypes.object.isRequired,
filters: PropTypes.object.isRequired,
onEnterEditMode: PropTypes.func.isRequired,
+ onExitEditMode: PropTypes.func.isRequired,
onSubmitVehicleJourneys: PropTypes.func.isRequired
-} \ No newline at end of file
+}
diff --git a/app/javascript/vehicle_journeys/components/Tools.js b/app/javascript/vehicle_journeys/components/Tools.js
index 1ef576529..d6e04f00e 100644
--- a/app/javascript/vehicle_journeys/components/Tools.js
+++ b/app/javascript/vehicle_journeys/components/Tools.js
@@ -7,6 +7,7 @@ import DuplicateVehicleJourney from '../containers/tools/DuplicateVehicleJourney
import EditVehicleJourney from '../containers/tools/EditVehicleJourney'
import NotesEditVehicleJourney from '../containers/tools/NotesEditVehicleJourney'
import TimetablesEditVehicleJourney from '../containers/tools/TimetablesEditVehicleJourney'
+import PurchaseWindowsEditVehicleJourney from '../containers/tools/PurchaseWindowsEditVehicleJourney'
export default class Tools extends Component {
@@ -20,18 +21,26 @@ export default class Tools extends Component {
return this.props.filters.policy[`vehicle_journeys.${key}`]
}
+ hasFeature(key) {
+ // Check if the organisation has the given feature
+ return this.props.filters.features[key]
+ }
+
render() {
let { vehicleJourneys, onCancelSelection, editMode } = this.props
return (
<div className='select_toolbox'>
<ul>
- <AddVehicleJourney disabled={this.hasPolicy("create") && !editMode} />
- <DuplicateVehicleJourney disabled={this.hasPolicy("create") && this.hasPolicy("update") && !editMode}/>
- <ShiftVehicleJourney disabled={this.hasPolicy("update") && !editMode}/>
+ <AddVehicleJourney disabled={!this.hasPolicy("create") || !editMode} />
+ <DuplicateVehicleJourney disabled={!this.hasPolicy("create") || !this.hasPolicy("update") || !editMode}/>
+ <ShiftVehicleJourney disabled={!this.hasPolicy("update") || !editMode}/>
<EditVehicleJourney disabled={!this.hasPolicy("update")}/>
<TimetablesEditVehicleJourney disabled={!this.hasPolicy("update")}/>
+ { this.hasFeature('purchase_windows') &&
+ <PurchaseWindowsEditVehicleJourney disabled={!this.hasPolicy("update")}/>
+ }
<NotesEditVehicleJourney disabled={!this.hasPolicy("update")}/>
- <DeleteVehicleJourneys disabled={this.hasPolicy("destroy") && !editMode}/>
+ <DeleteVehicleJourneys disabled={!this.hasPolicy("destroy") || !editMode}/>
</ul>
<span className='info-msg'>{actions.getSelected(vehicleJourneys).length} course(s) sélectionnée(s)</span>
diff --git a/app/javascript/vehicle_journeys/components/VehicleJourney.js b/app/javascript/vehicle_journeys/components/VehicleJourney.js
index 929cbc5c4..1e62444ef 100644
--- a/app/javascript/vehicle_journeys/components/VehicleJourney.js
+++ b/app/javascript/vehicle_journeys/components/VehicleJourney.js
@@ -17,12 +17,25 @@ export default class VehicleJourney extends Component {
return bool
}
+ hasFeature(key) {
+ return this.props.filters.features[key]
+ }
+
timeTableURL(tt) {
let refURL = window.location.pathname.split('/', 3).join('/')
let ttURL = refURL + '/time_tables/' + tt.id
return (
- <a href={ttURL} title='Voir le calendrier'><span className='fa fa-calendar' style={{color: (tt.color ? tt.color : '')}}></span></a>
+ <a href={ttURL} title='Voir le calendrier'><span className='fa fa-calendar' style={{ color: (tt.color ? tt.color : '#4B4B4B')}}></span></a>
+ )
+ }
+
+ purchaseWindowURL(tt) {
+ let refURL = window.location.pathname.split('/', 3).join('/')
+ let ttURL = refURL + '/purchase_windows/' + tt.id
+
+ return (
+ <a href={ttURL} title='Voir le calendrier commercial'><span className='fa fa-calendar' style={{color: (tt.color ? tt.color : '')}}></span></a>
)
}
@@ -44,11 +57,16 @@ export default class VehicleJourney extends Component {
render() {
this.previousCity = undefined
- let {time_tables} = this.props.value
+ let {time_tables, purchase_windows} = this.props.value
return (
<div className={'t2e-item' + (this.props.value.deletable ? ' disabled' : '') + (this.props.value.errors ? ' has-error': '')}>
- <div className='th'>
+ <div
+ className='th'
+ onClick={(e) =>
+ ($(e.target).parents("a").length == 0) && this.props.editMode && this.props.onSelectVehicleJourney(this.props.index)
+ }
+ >
<div className='strong mb-xs'>{this.props.value.short_id || '-'}</div>
<div>{this.props.value.journey_pattern.short_id || '-'}</div>
<div>
@@ -57,6 +75,14 @@ export default class VehicleJourney extends Component {
)}
{time_tables.length > 3 && <span className='vj_tt'> + {time_tables.length - 3}</span>}
</div>
+ { this.hasFeature('purchase_windows') &&
+ <div>
+ {purchase_windows.slice(0,3).map((tt, i)=>
+ <span key={i} className='vj_tt'>{this.purchaseWindowURL(tt)}</span>
+ )}
+ {purchase_windows.length > 3 && <span className='vj_tt'> + {purchase_windows.length - 3}</span>}
+ </div>
+ }
<div className={(this.props.value.deletable ? 'disabled ' : '') + 'checkbox'}>
<input
id={this.props.index}
@@ -83,6 +109,7 @@ export default class VehicleJourney extends Component {
className='form-control'
disabled={this.isDisabled(this.props.value.deletable, vj.dummy) || this.props.filters.policy['vehicle_journeys.update'] == false}
readOnly={!this.props.editMode && !vj.dummy}
+ disabled={!this.props.editMode && !vj.dummy}
onChange={(e) => {this.props.onUpdateTime(e, i, this.props.index, 'hour', false, false)}}
value={vj.arrival_time['hour']}
/>
@@ -94,6 +121,7 @@ export default class VehicleJourney extends Component {
className='form-control'
disabled={this.isDisabled(this.props.value.deletable, vj.dummy) || this.props.filters.policy['vehicle_journeys.update'] == false}
readOnly={!this.props.editMode && !vj.dummy}
+ disabled={!this.props.editMode && !vj.dummy}
onChange={(e) => {this.props.onUpdateTime(e, i, this.props.index, 'minute', false, false)}}
value={vj.arrival_time['minute']}
/>
@@ -114,6 +142,7 @@ export default class VehicleJourney extends Component {
className='form-control'
disabled={this.isDisabled(this.props.value.deletable, vj.dummy) || this.props.filters.policy['vehicle_journeys.update'] == false}
readOnly={!this.props.editMode && !vj.dummy}
+ disabled={!this.props.editMode && !vj.dummy}
onChange={(e) => {this.props.onUpdateTime(e, i, this.props.index, 'hour', true, this.props.filters.toggleArrivals)}}
value={vj.departure_time['hour']}
/>
@@ -125,6 +154,7 @@ export default class VehicleJourney extends Component {
className='form-control'
disabled={this.isDisabled(this.props.value.deletable, vj.dummy) || this.props.filters.policy['vehicle_journeys.update'] == false}
readOnly={!this.props.editMode && !vj.dummy}
+ disabled={!this.props.editMode && !vj.dummy}
onChange={(e) => {this.props.onUpdateTime(e, i, this.props.index, "minute", true, this.props.filters.toggleArrivals)}}
value={vj.departure_time['minute']}
/>
@@ -144,4 +174,4 @@ VehicleJourney.propTypes = {
index: PropTypes.number.isRequired,
onUpdateTime: PropTypes.func.isRequired,
onSelectVehicleJourney: PropTypes.func.isRequired
-} \ No newline at end of file
+}
diff --git a/app/javascript/vehicle_journeys/components/VehicleJourneys.js b/app/javascript/vehicle_journeys/components/VehicleJourneys.js
index 6bce9766b..e16d03f90 100644
--- a/app/javascript/vehicle_journeys/components/VehicleJourneys.js
+++ b/app/javascript/vehicle_journeys/components/VehicleJourneys.js
@@ -12,6 +12,10 @@ export default class VehicleJourneys extends Component {
this.props.onLoadFirstPage(this.props.filters)
}
+ hasFeature(key) {
+ return this.props.filters.features[key]
+ }
+
componentDidUpdate(prevProps, prevState) {
if(this.props.status.isFetching == false){
$('.table-2entries').each(function() {
@@ -113,6 +117,7 @@ export default class VehicleJourneys extends Component {
<div className='strong mb-xs'>ID course</div>
<div>ID mission</div>
<div>Calendriers</div>
+ { this.hasFeature('purchase_windows') && <div>Calendriers Commerciaux</div> }
</div>
{this.props.stopPointsList.map((sp, i) =>{
return (
@@ -132,6 +137,7 @@ export default class VehicleJourneys extends Component {
index={index}
editMode={this.props.editMode}
filters={this.props.filters}
+ features={this.props.features}
onUpdateTime={this.props.onUpdateTime}
onSelectVehicleJourney={this.props.onSelectVehicleJourney}
/>
@@ -153,4 +159,4 @@ VehicleJourneys.propTypes = {
onLoadFirstPage: PropTypes.func.isRequired,
onUpdateTime: PropTypes.func.isRequired,
onSelectVehicleJourney: PropTypes.func.isRequired
-} \ No newline at end of file
+}
diff --git a/app/javascript/vehicle_journeys/components/tools/PurchaseWindowsEditVehicleJourney.js b/app/javascript/vehicle_journeys/components/tools/PurchaseWindowsEditVehicleJourney.js
new file mode 100644
index 000000000..d61c7a34b
--- /dev/null
+++ b/app/javascript/vehicle_journeys/components/tools/PurchaseWindowsEditVehicleJourney.js
@@ -0,0 +1,152 @@
+import React, { PropTypes, Component } from 'react'
+import actions from '../../actions'
+import TimetableSelect2 from './select2s/TimetableSelect2'
+
+export default class PurchaseWindowsEditVehicleJourney extends Component {
+ constructor(props) {
+ super(props)
+ this.handleSubmit = this.handleSubmit.bind(this)
+ this.purchaseWindowURL = this.purchaseWindowURL.bind(this)
+ }
+
+ handleSubmit() {
+ this.props.onShoppingWindowsEditVehicleJourney(this.props.modal.modalProps.vehicleJourneys, this.props.modal.modalProps.purchase_windows)
+ this.props.onModalClose()
+ $('#PurchaseWindowsEditVehicleJourneyModal').modal('hide')
+ }
+
+ purchaseWindowURL(tt) {
+ let refURL = window.location.pathname.split('/', 3).join('/')
+ return refURL + '/purchase_windows/' + tt.id
+ }
+
+ render() {
+ if(this.props.status.isFetching == true) {
+ return false
+ }
+ if(this.props.status.fetchSuccess == true) {
+ return (
+ <li className='st_action'>
+ <button
+ type='button'
+ disabled={(actions.getSelected(this.props.vehicleJourneys).length < 1 || this.props.disabled)}
+ data-toggle='modal'
+ data-target='#PurchaseWindowsEditVehicleJourneyModal'
+ onClick={() => this.props.onOpenCalendarsEditModal(actions.getSelected(this.props.vehicleJourneys))}
+ title='Calendriers commerciaux'
+ >
+ <span className='sb sb-purchase_window sb-strong'></span>
+ </button>
+
+ <div className={ 'modal fade ' + ((this.props.modal.type == 'duplicate') ? 'in' : '') } id='PurchaseWindowsEditVehicleJourneyModal'>
+ <div className='modal-container'>
+ <div className='modal-dialog'>
+ <div className='modal-content'>
+ <div className='modal-header'>
+ <h4 className='modal-title'>Calendriers commerciaux associés</h4>
+ <span type="button" className="close modal-close" data-dismiss="modal">&times;</span>
+ </div>
+
+ {(this.props.modal.type == 'purchase_windows_edit') && (
+ <form>
+ <div className='modal-body'>
+ <div className='row'>
+ <div className='col-lg-12'>
+ <div className='subform'>
+ <div className='nested-head'>
+ <div className='wrapper'>
+ <div>
+ <div className='form-group'>
+ <label className='control-label'>{this.props.modal.modalProps.purchase_windows.length == 0 ? "Aucun calendrier commercial associé" : "Calendriers commerciaux associés"}</label>
+ </div>
+ </div>
+ <div></div>
+ </div>
+ </div>
+ {this.props.modal.modalProps.purchase_windows.map((tt, i) =>
+ <div className='nested-fields' key={i}>
+ <div className='wrapper'>
+ <div> <a href={this.purchaseWindowURL(tt)} target="_blank">
+ <span className="fa fa-circle mr-xs" style={{color: tt.color}}></span>
+ {tt.name}
+ </a> </div>
+ {
+ this.props.editMode &&
+ <div>
+ <a
+ href='#'
+ title='Supprimer'
+ className='fa fa-trash remove_fields'
+ style={{ height: 'auto', lineHeight: 'normal' }}
+ onClick={(e) => {
+ e.preventDefault()
+ this.props.onDeleteCalendarModal(tt)
+ }}
+ ></a>
+ </div>
+ }
+ </div>
+ </div>
+ )}
+ {
+ this.props.editMode &&
+ <div className='nested-fields'>
+ <div className='wrapper'>
+ <div>
+ <TimetableSelect2
+ onSelect2Timetable={this.props.onSelect2Timetable}
+ chunkURL={'/autocomplete_purchase_windows.json'}
+ searchKey={"name_or_objectid_cont_any"}
+ isFilter={false}
+ />
+ </div>
+ </div>
+ </div>
+ }
+ </div>
+ </div>
+ </div>
+ </div>
+ {
+ this.props.editMode &&
+ <div className='modal-footer'>
+ <button
+ className='btn btn-link'
+ data-dismiss='modal'
+ type='button'
+ onClick={this.props.onModalClose}
+ >
+ Annuler
+ </button>
+ <button
+ className='btn btn-primary'
+ type='button'
+ onClick={this.handleSubmit}
+ >
+ Valider
+ </button>
+ </div>
+ }
+ </form>
+ )}
+
+ </div>
+ </div>
+ </div>
+ </div>
+ </li>
+ )
+ } else {
+ return false
+ }
+ }
+}
+
+PurchaseWindowsEditVehicleJourney.propTypes = {
+ onOpenCalendarsEditModal: PropTypes.func.isRequired,
+ onModalClose: PropTypes.func.isRequired,
+ onShoppingWindowsEditVehicleJourney: PropTypes.func.isRequired,
+ onDeleteCalendarModal: PropTypes.func.isRequired,
+ onSelect2Timetable: PropTypes.func.isRequired,
+ disabled: PropTypes.bool.isRequired
+}
diff --git a/app/javascript/vehicle_journeys/components/tools/TimetablesEditVehicleJourney.js b/app/javascript/vehicle_journeys/components/tools/TimetablesEditVehicleJourney.js
index 6629135dd..56f80ebb5 100644
--- a/app/javascript/vehicle_journeys/components/tools/TimetablesEditVehicleJourney.js
+++ b/app/javascript/vehicle_journeys/components/tools/TimetablesEditVehicleJourney.js
@@ -33,6 +33,7 @@ export default class TimetablesEditVehicleJourney extends Component {
data-toggle='modal'
data-target='#CalendarsEditVehicleJourneyModal'
onClick={() => this.props.onOpenCalendarsEditModal(actions.getSelected(this.props.vehicleJourneys))}
+ title='Calendriers'
>
<span className='fa fa-calendar'></span>
</button>
@@ -67,7 +68,7 @@ export default class TimetablesEditVehicleJourney extends Component {
<div className='wrapper'>
<div> <a href={this.timeTableURL(tt)} target="_blank">{tt.comment}</a> </div>
{
- this.props.editMode &&
+ this.props.editMode &&
<div>
<a
href='#'
@@ -85,13 +86,14 @@ export default class TimetablesEditVehicleJourney extends Component {
</div>
)}
{
- this.props.editMode &&
+ this.props.editMode &&
<div className='nested-fields'>
<div className='wrapper'>
<div>
<TimetableSelect2
onSelect2Timetable={this.props.onSelect2Timetable}
chunkURL={'/autocomplete_time_tables.json'}
+ searchKey={"comment_or_objectid_cont_any"}
isFilter={false}
/>
</div>
@@ -103,7 +105,7 @@ export default class TimetablesEditVehicleJourney extends Component {
</div>
</div>
{
- this.props.editMode &&
+ this.props.editMode &&
<div className='modal-footer'>
<button
className='btn btn-link'
@@ -144,4 +146,4 @@ TimetablesEditVehicleJourney.propTypes = {
onDeleteCalendarModal: PropTypes.func.isRequired,
onSelect2Timetable: PropTypes.func.isRequired,
disabled: PropTypes.bool.isRequired
-} \ No newline at end of file
+}
diff --git a/app/javascript/vehicle_journeys/components/tools/select2s/TimetableSelect2.js b/app/javascript/vehicle_journeys/components/tools/select2s/TimetableSelect2.js
index 19c183839..eb8651be2 100644
--- a/app/javascript/vehicle_journeys/components/tools/select2s/TimetableSelect2.js
+++ b/app/javascript/vehicle_journeys/components/tools/select2s/TimetableSelect2.js
@@ -31,12 +31,10 @@ export default class BSelect4 extends Component {
url: origin + path + this.props.chunkURL,
dataType: 'json',
delay: '500',
- data: function(params) {
- return {
- q: {
- comment_or_objectid_cont_any: params.term
- }
- };
+ data: (params) => {
+ let q = {}
+ q[this.props.searchKey] = params.term
+ return {q}
},
processResults: function(data, params) {
return {
@@ -44,7 +42,7 @@ export default class BSelect4 extends Component {
item => _.assign(
{},
item,
- {text: '<strong>' + "<span class='fa fa-circle' style='color:" + (item.color ? item.color : '#4B4B4B') + "'></span> " + item.comment + ' - ' + item.short_id + '</strong><br/><small>' + (item.day_types ? item.day_types.match(/[A-Z]?[a-z]+/g).join(', ') : "") + '</small>'}
+ {text: '<strong>' + "<span class='fa fa-circle' style='color:" + (item.color ? item.color : '#4B4B4B') + "'></span> " + (item.comment || item.name) + ' - ' + item.short_id + '</strong><br/><small>' + (item.day_types ? item.day_types.match(/[A-Z]?[a-z]+/g).join(', ') : "") + '</small>'}
)
)
};
@@ -62,4 +60,4 @@ export default class BSelect4 extends Component {
const formatRepo = (props) => {
if(props.text) return props.text
-} \ No newline at end of file
+}
diff --git a/app/javascript/vehicle_journeys/containers/SaveVehicleJourneys.js b/app/javascript/vehicle_journeys/containers/SaveVehicleJourneys.js
index 18f9e994e..f5f879ed8 100644
--- a/app/javascript/vehicle_journeys/containers/SaveVehicleJourneys.js
+++ b/app/javascript/vehicle_journeys/containers/SaveVehicleJourneys.js
@@ -17,6 +17,10 @@ const mapDispatchToProps = (dispatch) => {
onEnterEditMode: () => {
dispatch(actions.enterEditMode())
},
+ onExitEditMode: () => {
+ dispatch(actions.cancelSelection())
+ dispatch(actions.exitEditMode())
+ },
onSubmitVehicleJourneys: (next, state) => {
actions.submitVehicleJourneys(dispatch, state, next)
}
diff --git a/app/javascript/vehicle_journeys/containers/tools/PurchaseWindowsEditVehicleJourney.js b/app/javascript/vehicle_journeys/containers/tools/PurchaseWindowsEditVehicleJourney.js
new file mode 100644
index 000000000..3fef44489
--- /dev/null
+++ b/app/javascript/vehicle_journeys/containers/tools/PurchaseWindowsEditVehicleJourney.js
@@ -0,0 +1,38 @@
+import actions from '../../actions'
+import { connect } from 'react-redux'
+import PurchaseWindowsEditVehicleJourneyComponent from '../../components/tools/PurchaseWindowsEditVehicleJourney'
+
+const mapStateToProps = (state, ownProps) => {
+ return {
+ editMode: state.editMode,
+ modal: state.modal,
+ vehicleJourneys: state.vehicleJourneys,
+ status: state.status,
+ disabled: ownProps.disabled
+ }
+}
+
+const mapDispatchToProps = (dispatch) => {
+ return {
+ onModalClose: () =>{
+ dispatch(actions.closeModal())
+ },
+ onOpenCalendarsEditModal: (vehicleJourneys) =>{
+ dispatch(actions.openPurchaseWindowsEditModal(vehicleJourneys))
+ },
+ onDeleteCalendarModal: (timetable) => {
+ dispatch(actions.deletePurchaseWindowsModal(timetable))
+ },
+ onShoppingWindowsEditVehicleJourney: (vehicleJourneys, timetables) =>{
+ dispatch(actions.editVehicleJourneyPurchaseWindows(vehicleJourneys, timetables))
+ },
+ onSelect2Timetable: (e) =>{
+ dispatch(actions.selectPurchaseWindowsModal(e.params.data))
+ dispatch(actions.addSelectedPurchaseWindow())
+ }
+ }
+}
+
+const PurchaseWindowsEditVehicleJourney = connect(mapStateToProps, mapDispatchToProps)(PurchaseWindowsEditVehicleJourneyComponent)
+
+export default PurchaseWindowsEditVehicleJourney
diff --git a/app/javascript/vehicle_journeys/reducers/modal.js b/app/javascript/vehicle_journeys/reducers/modal.js
index 57f54a144..862e27e1b 100644
--- a/app/javascript/vehicle_journeys/reducers/modal.js
+++ b/app/javascript/vehicle_journeys/reducers/modal.js
@@ -40,7 +40,6 @@ export default function modal(state = {}, action) {
case 'EDIT_CALENDARS_VEHICLEJOURNEY_MODAL':
vehicleJourneysModal = JSON.parse(JSON.stringify(action.vehicleJourneys))
let uniqTimetables = []
- let timetable = {}
vehicleJourneysModal.map((vj, i) => {
vj.time_tables.map((tt, j) =>{
if(!(_.find(uniqTimetables, tt))){
@@ -56,6 +55,24 @@ export default function modal(state = {}, action) {
},
confirmModal: {}
}
+ case 'EDIT_PURCHASE_WINDOWS_VEHICLEJOURNEY_MODAL':
+ var vehicleJourneys = JSON.parse(JSON.stringify(action.vehicleJourneys))
+ let uniqPurchaseWindows = []
+ vehicleJourneys.map((vj, i) => {
+ vj.purchase_windows.map((pw, j) =>{
+ if(!(_.find(uniqPurchaseWindows, pw))){
+ uniqPurchaseWindows.push(pw)
+ }
+ })
+ })
+ return {
+ type: 'purchase_windows_edit',
+ modalProps: {
+ vehicleJourneys: vehicleJourneys,
+ purchase_windows: uniqPurchaseWindows
+ },
+ confirmModal: {}
+ }
case 'SELECT_CP_EDIT_MODAL':
newModalProps = _.assign({}, state.modalProps, {selectedCompany : action.selectedItem})
return _.assign({}, state, {modalProps: newModalProps})
@@ -65,6 +82,9 @@ export default function modal(state = {}, action) {
case 'SELECT_TT_CALENDAR_MODAL':
newModalProps = _.assign({}, state.modalProps, {selectedTimetable : action.selectedItem})
return _.assign({}, state, {modalProps: newModalProps})
+ case 'SELECT_PURCHASE_WINDOW_MODAL':
+ newModalProps = _.assign({}, state.modalProps, {selectedPurchaseWindow : action.selectedItem})
+ return _.assign({}, state, {modalProps: newModalProps})
case 'ADD_SELECTED_TIMETABLE':
if(state.modalProps.selectedTimetable){
newModalProps = JSON.parse(JSON.stringify(state.modalProps))
@@ -73,6 +93,14 @@ export default function modal(state = {}, action) {
}
return _.assign({}, state, {modalProps: newModalProps})
}
+ case 'ADD_SELECTED_PURCHASE_WINDOW':
+ if(state.modalProps.selectedPurchaseWindow){
+ newModalProps = JSON.parse(JSON.stringify(state.modalProps))
+ if (!_.find(newModalProps.purchase_windows, newModalProps.selectedPurchaseWindow)){
+ newModalProps.purchase_windows.push(newModalProps.selectedPurchaseWindow)
+ }
+ return _.assign({}, state, {modalProps: newModalProps})
+ }
case 'DELETE_CALENDAR_MODAL':
newModalProps = JSON.parse(JSON.stringify(state.modalProps))
let timetablesModal = state.modalProps.timetables.slice(0)
@@ -92,6 +120,25 @@ export default function modal(state = {}, action) {
newModalProps.vehicleJourneys = vehicleJourneysModal
newModalProps.timetables = timetablesModal
return _.assign({}, state, {modalProps: newModalProps})
+ case 'DELETE_PURCHASE_WINDOW_MODAL':
+ newModalProps = JSON.parse(JSON.stringify(state.modalProps))
+ let purchase_windows = state.modalProps.purchase_windows.slice(0)
+ purchase_windows.map((tt, i) =>{
+ if(tt == action.purchaseWindow){
+ purchase_windows.splice(i, 1)
+ }
+ })
+ vehicleJourneysModal = state.modalProps.vehicleJourneys.slice(0)
+ vehicleJourneysModal.map((vj) =>{
+ vj.purchase_windows.map((tt, i) =>{
+ if (_.isEqual(tt, action.purchaseWindow)){
+ vj.purchase_windows.splice(i, 1)
+ }
+ })
+ })
+ newModalProps.vehicleJourneys = vehicleJourneysModal
+ newModalProps.purchase_windows = purchase_windows
+ return _.assign({}, state, {modalProps: newModalProps})
case 'CREATE_VEHICLEJOURNEY_MODAL':
let selectedJP = {}
if (window.jpOrigin){
@@ -135,4 +182,4 @@ export default function modal(state = {}, action) {
default:
return state
}
-} \ No newline at end of file
+}
diff --git a/app/javascript/vehicle_journeys/reducers/vehicleJourneys.js b/app/javascript/vehicle_journeys/reducers/vehicleJourneys.js
index 136e1b41a..d057bf704 100644
--- a/app/javascript/vehicle_journeys/reducers/vehicleJourneys.js
+++ b/app/javascript/vehicle_journeys/reducers/vehicleJourneys.js
@@ -41,6 +41,7 @@ const vehicleJourney= (state = {}, action, keep) => {
short_id: '',
footnotes: [],
time_tables: [],
+ purchase_windows: [],
vehicle_journey_at_stops: pristineVjasList,
selected: false,
deletable: false,
@@ -79,18 +80,12 @@ const vehicleJourney= (state = {}, action, keep) => {
if (action.isDeparture){
newSchedule.departure_time[action.timeUnit] = actions.pad(action.val, action.timeUnit)
if(!action.isArrivalsToggled)
- newSchedule.arrival_time[action.timeUnit] = actions.pad(action.val, action.timeUnit)
- newSchedule = actions.getDelta(newSchedule)
- if(newSchedule.delta < 0){
- return vjas
- }
+ newSchedule.arrival_time[action.timeUnit] = newSchedule.departure_time[action.timeUnit]
+ newSchedule = actions.adjustSchedule(action, newSchedule)
return _.assign({}, state.vehicle_journey_at_stops[action.subIndex], {arrival_time: newSchedule.arrival_time, departure_time: newSchedule.departure_time, delta: newSchedule.delta})
}else{
newSchedule.arrival_time[action.timeUnit] = actions.pad(action.val, action.timeUnit)
- newSchedule = actions.getDelta(newSchedule)
- if(newSchedule.delta < 0){
- return vjas
- }
+ newSchedule = actions.adjustSchedule(action, newSchedule)
return _.assign({}, state.vehicle_journey_at_stops[action.subIndex], {arrival_time: newSchedule.arrival_time, departure_time: newSchedule.departure_time, delta: newSchedule.delta})
}
}else{
@@ -161,6 +156,21 @@ export default function vehicleJourneys(state = [], action) {
return vj
}
})
+ case 'EDIT_VEHICLEJOURNEYS_PURCHASE_WINDOWS':
+ let newWindows = JSON.parse(JSON.stringify(action.purchase_windows))
+ return state.map((vj,i) =>{
+ if(vj.selected){
+ let updatedVJ = _.assign({}, vj)
+ action.vehicleJourneys.map((vjm, j) =>{
+ if(vj.objectid == vjm.objectid){
+ updatedVJ.purchase_windows = newWindows
+ }
+ })
+ return updatedVJ
+ }else{
+ return vj
+ }
+ })
case 'SHIFT_VEHICLEJOURNEY':
return state.map((vj, i) => {
if (vj.selected){
@@ -225,4 +235,4 @@ export default function vehicleJourneys(state = [], action) {
default:
return state
}
-} \ No newline at end of file
+}
diff --git a/app/models/calendar/period.rb b/app/models/calendar/period.rb
index 1c423dfcc..56ab722fe 100644
--- a/app/models/calendar/period.rb
+++ b/app/models/calendar/period.rb
@@ -1,5 +1,5 @@
class Calendar < ActiveRecord::Base
-
+
class Period
include ActiveAttr::Model
diff --git a/app/models/chouette/purchase_window.rb b/app/models/chouette/purchase_window.rb
index 9f68d4408..e89a0ec7f 100644
--- a/app/models/chouette/purchase_window.rb
+++ b/app/models/chouette/purchase_window.rb
@@ -11,11 +11,12 @@ module Chouette
has_paper_trail
belongs_to :referential
+ has_and_belongs_to_many :vehicle_journeys, :class_name => 'Chouette::VehicleJourney'
validates_presence_of :name, :referential
scope :contains_date, ->(date) { where('date ? <@ any (date_ranges)', date) }
-
+
def self.ransackable_scopes(auth_object = nil)
[:contains_date]
end
diff --git a/app/models/chouette/vehicle_journey.rb b/app/models/chouette/vehicle_journey.rb
index 247c30668..983bf5363 100644
--- a/app/models/chouette/vehicle_journey.rb
+++ b/app/models/chouette/vehicle_journey.rb
@@ -1,3 +1,4 @@
+# coding: utf-8
module Chouette
class VehicleJourney < Chouette::TridentActiveRecord
has_paper_trail
@@ -23,6 +24,7 @@ module Chouette
belongs_to :journey_pattern
has_and_belongs_to_many :footnotes, :class_name => 'Chouette::Footnote'
+ has_and_belongs_to_many :purchase_windows, :class_name => 'Chouette::PurchaseWindow'
validates_presence_of :route
validates_presence_of :journey_pattern
@@ -139,7 +141,7 @@ module Chouette
end
def update_has_and_belongs_to_many_from_state item
- ['time_tables', 'footnotes'].each do |assos|
+ ['time_tables', 'footnotes', 'purchase_windows'].each do |assos|
saved = self.send(assos).map(&:id)
(saved - item[assos].map{|t| t['id']}).each do |id|
@@ -241,11 +243,9 @@ module Chouette
end
def self.matrix(vehicle_journeys)
- {}.tap do |hash|
- vehicle_journeys.map{ |vj|
- vj.vehicle_journey_at_stops.map{ |vjas |hash[ "#{vj.id}-#{vjas.stop_point_id}"] = vjas }
- }
- end
+ Hash[*VehicleJourneyAtStop.where(vehicle_journey_id: vehicle_journeys.pluck(:id)).map do |vjas|
+ [ "#{vjas.vehicle_journey_id}-#{vjas.stop_point_id}", vjas]
+ end.flatten]
end
def self.with_stops
diff --git a/app/models/concerns/period_support.rb b/app/models/concerns/period_support.rb
index f512c4e89..e17451fe4 100644
--- a/app/models/concerns/period_support.rb
+++ b/app/models/concerns/period_support.rb
@@ -7,7 +7,7 @@ module PeriodSupport
def init_date_ranges
self.date_ranges ||= []
end
-
+
### Calendar::Period
# Required by coocon
def build_period
@@ -77,4 +77,4 @@ module PeriodSupport
private :clear_periods
end
-end \ No newline at end of file
+end
diff --git a/app/policies/line_policy.rb b/app/policies/line_policy.rb
index e5674fbe2..e7263cc3b 100644
--- a/app/policies/line_policy.rb
+++ b/app/policies/line_policy.rb
@@ -6,7 +6,6 @@ class LinePolicy < ApplicationPolicy
end
def create?
- Rails.logger.debug "LinePolicy.create?"
user.has_permission?('lines.create')
end
diff --git a/app/views/autocomplete_purchase_windows/index.rabl b/app/views/autocomplete_purchase_windows/index.rabl
new file mode 100644
index 000000000..1d0287602
--- /dev/null
+++ b/app/views/autocomplete_purchase_windows/index.rabl
@@ -0,0 +1,12 @@
+collection @purchase_windows, :object_root => false
+
+node do |window|
+ {
+ :id => window.id,
+ :name => window.name,
+ :objectid => window.objectid,
+ :color => window.color,
+ :short_id => window.get_objectid.short_id,
+ :text => "<strong><span class='fa fa-circle' style='color:" + (window.color ? window.color : '#4b4b4b') + "'></span> " + window.name + " - " + window.get_objectid.short_id + "</strong>"
+ }
+end
diff --git a/app/views/journey_patterns_collections/show.html.slim b/app/views/journey_patterns_collections/show.html.slim
index 834501da3..8a7b0c47c 100644
--- a/app/views/journey_patterns_collections/show.html.slim
+++ b/app/views/journey_patterns_collections/show.html.slim
@@ -17,6 +17,7 @@
| window.stopPoints = #{(@stop_points_list.to_json).html_safe};
| window.journeyPatternLength = #{@journey_patterns.total_entries()};
| window.journeyPatternsPerPage = #{@ppage};
- | window.perms = #{raw @perms}
+ | window.perms = #{raw @perms};
+ | window.features = #{raw @features};
= javascript_pack_tag 'journey_patterns/index.js'
diff --git a/app/views/referentials/_form.html.slim b/app/views/referentials/_form.html.slim
index 6f7da84c7..1611ee6dd 100644
--- a/app/views/referentials/_form.html.slim
+++ b/app/views/referentials/_form.html.slim
@@ -17,7 +17,7 @@
.row
.col-lg-12
- if @referential.errors.has_key? :metadatas
- .row
+ .row.metadatas-errors
.col-lg-12
.alert.alert-danger
- @referential.errors[:metadatas].each do |msg|
diff --git a/app/views/vehicle_journeys/index.html.slim b/app/views/vehicle_journeys/index.html.slim
index 4ad9d524d..ebcac8197 100644
--- a/app/views/vehicle_journeys/index.html.slim
+++ b/app/views/vehicle_journeys/index.html.slim
@@ -4,7 +4,7 @@
- content_for :page_header_content do
.row.mb-sm
.col-lg-12.text-right
- = link_to(t('routes.actions.opposite_route_timetable'), [@referential, @route.line, @route.opposite_route, :vehicle_journeys], class: 'btn btn-primary')
+ = link_to(t('routes.actions.opposite_route_timetable'), [@referential, @route.line, @route.opposite_route, :vehicle_journeys], class: 'btn btn-primary sticky-action')
.page_content
@@ -25,6 +25,7 @@
| window.vehicleJourneysPerPage = #{@ppage};
| window.line_footnotes = #{raw @footnotes};
| window.perms = #{raw @perms};
+ | window.features = #{raw @features};
| window.I18n = #{(I18n.backend.send(:translations).to_json).html_safe};
= javascript_pack_tag 'vehicle_journeys/index.js'
diff --git a/app/views/vehicle_journeys/show.rabl b/app/views/vehicle_journeys/show.rabl
index 830dee8bd..a07f09309 100644
--- a/app/views/vehicle_journeys/show.rabl
+++ b/app/views/vehicle_journeys/show.rabl
@@ -28,12 +28,22 @@ child(:time_tables, :object_root => false) do |time_tables|
end
end
+if has_feature? :purchase_windows
+ child(:purchase_windows, :object_root => false) do |purchase_windows|
+ attributes :id, :objectid, :name, :color
+ end
+end
+
child :footnotes, :object_root => false do |footnotes|
attributes :id, :code, :label
end
child(:vehicle_journey_at_stops_matrix, :object_root => false) do |vehicle_stops|
node do |vehicle_stop|
+ [:id, :connecting_service_id, :boarding_alighting_possibility].map do |att|
+ node(att) { vehicle_stop.send(att) ? vehicle_stop.send(att) : nil }
+ end
+
node(:dummy) { vehicle_stop.dummy }
node(:stop_area_object_id) do
@@ -49,11 +59,7 @@ child(:vehicle_journey_at_stops_matrix, :object_root => false) do |vehicle_stops
vehicle_stop.stop_point.stop_area.city_name
end
- [:id, :connecting_service_id, :boarding_alighting_possibility].map do |att|
- node(att) { vehicle_stop.send(att) ? vehicle_stop.send(att) : nil }
- end
-
- [:arrival_time, :departure_time].map do |att|
+ [:arrival_time, :departure_time].each do |att|
node(att) do |vs|
{
hour: vs.send(att).try(:strftime, '%H'),