aboutsummaryrefslogtreecommitdiffstats
path: root/app
diff options
context:
space:
mode:
Diffstat (limited to 'app')
-rw-r--r--app/assets/javascripts/forms.coffee19
-rw-r--r--app/assets/javascripts/main_menu.coffee44
-rw-r--r--app/assets/javascripts/select2.coffee17
-rw-r--r--app/assets/stylesheets/_layout.sass1
-rw-r--r--app/assets/stylesheets/base/_config.sass1
-rw-r--r--app/assets/stylesheets/components/_breadcrumb.sass5
-rw-r--r--app/assets/stylesheets/components/_buttons.sass7
-rw-r--r--app/assets/stylesheets/components/_color_selector.sass21
-rw-r--r--app/assets/stylesheets/components/_forms.sass7
-rw-r--r--app/assets/stylesheets/components/_lists.sass5
-rw-r--r--app/assets/stylesheets/components/_main_nav.sass19
-rw-r--r--app/assets/stylesheets/components/_panels.sass1
-rw-r--r--app/assets/stylesheets/components/_referentials.sass4
-rw-r--r--app/assets/stylesheets/components/_tables.sass12
-rw-r--r--app/assets/stylesheets/modules/import_messages.sass40
-rw-r--r--app/assets/stylesheets/typography/_sboiv.sass8
-rw-r--r--app/controllers/application_controller.rb1
-rw-r--r--app/controllers/autocomplete_purchase_windows_controller.rb12
-rw-r--r--app/controllers/autocomplete_stop_areas_controller.rb11
-rw-r--r--app/controllers/calendars_controller.rb2
-rw-r--r--app/controllers/companies_controller.rb4
-rw-r--r--app/controllers/concerns/activatable.rb11
-rw-r--r--app/controllers/concerns/feature_checker.rb42
-rw-r--r--app/controllers/group_of_lines_controller.rb5
-rw-r--r--app/controllers/journey_patterns_collections_controller.rb1
-rw-r--r--app/controllers/line_referentials_controller.rb1
-rw-r--r--app/controllers/lines_controller.rb8
-rw-r--r--app/controllers/networks_controller.rb4
-rw-r--r--app/controllers/purchase_windows_controller.rb75
-rw-r--r--app/controllers/referential_companies_controller.rb3
-rw-r--r--app/controllers/referential_vehicle_journeys_controller.rb17
-rw-r--r--app/controllers/referentials_controller.rb17
-rw-r--r--app/controllers/stop_area_referentials_controller.rb1
-rw-r--r--app/controllers/stop_areas_controller.rb45
-rw-r--r--app/controllers/vehicle_journeys_controller.rb86
-rw-r--r--app/decorators/company_decorator.rb9
-rw-r--r--app/decorators/compliance_check_set_decorator.rb9
-rw-r--r--app/decorators/line_decorator.rb23
-rw-r--r--app/decorators/purchase_window_decorator.rb34
-rw-r--r--app/decorators/referential_decorator.rb32
-rw-r--r--app/decorators/referential_line_decorator.rb24
-rw-r--r--app/decorators/route_decorator.rb2
-rw-r--r--app/decorators/stop_area_decorator.rb53
-rw-r--r--app/decorators/stop_point_decorator.rb2
-rw-r--r--app/helpers/application_helper.rb26
-rw-r--r--app/helpers/common_helpers.rb26
-rw-r--r--app/helpers/compliance_control_sets_helper.rb91
-rw-r--r--app/helpers/links_helper.rb15
-rw-r--r--app/helpers/multiple_selection_toolbox_helper.rb11
-rw-r--r--app/helpers/table_builder_helper.rb148
-rw-r--r--app/helpers/table_builder_helper/url.rb2
-rw-r--r--app/inputs/color_select_input.rb44
-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/components/BSelect2.js23
-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.js116
-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.js21
-rw-r--r--app/javascript/vehicle_journeys/components/VehicleJourney.js43
-rw-r--r--app/javascript/vehicle_journeys/components/VehicleJourneys.js9
-rw-r--r--app/javascript/vehicle_journeys/components/tools/CreateModal.js6
-rw-r--r--app/javascript/vehicle_journeys/components/tools/EditVehicleJourney.js1
-rw-r--r--app/javascript/vehicle_journeys/components/tools/PurchaseWindowsEditVehicleJourney.js152
-rw-r--r--app/javascript/vehicle_journeys/components/tools/TimetablesEditVehicleJourney.js21
-rw-r--r--app/javascript/vehicle_journeys/components/tools/select2s/CompanySelect2.js4
-rw-r--r--app/javascript/vehicle_journeys/components/tools/select2s/MissionSelect2.js4
-rw-r--r--app/javascript/vehicle_journeys/components/tools/select2s/TimetableSelect2.js14
-rw-r--r--app/javascript/vehicle_journeys/components/tools/select2s/VJSelect2.js2
-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.js59
-rw-r--r--app/javascript/vehicle_journeys/reducers/vehicleJourneys.js31
-rw-r--r--app/models/calendar.rb151
-rw-r--r--app/models/calendar/period.rb2
-rw-r--r--app/models/chouette/area_type.rb42
-rw-r--r--app/models/chouette/line.rb13
-rw-r--r--app/models/chouette/purchase_window.rb36
-rw-r--r--app/models/chouette/stop_area.rb33
-rw-r--r--app/models/chouette/vehicle_journey.rb12
-rw-r--r--app/models/chouette/vehicle_journey_at_stop.rb11
-rw-r--r--app/models/compliance_check_block.rb6
-rw-r--r--app/models/compliance_control.rb3
-rw-r--r--app/models/compliance_control_block.rb10
-rw-r--r--app/models/concerns/date_support.rb80
-rw-r--r--app/models/concerns/period_support.rb80
-rw-r--r--app/models/generic_attribute_control/min_max.rb2
-rw-r--r--app/models/generic_attribute_control/pattern.rb2
-rw-r--r--app/models/generic_attribute_control/uniqueness.rb2
-rw-r--r--app/models/import.rb48
-rw-r--r--app/models/organisation.rb66
-rw-r--r--app/models/referential.rb14
-rw-r--r--app/models/referential_cloning.rb21
-rw-r--r--app/models/user.rb2
-rw-r--r--app/models/vehicle_journey_control/delta.rb2
-rw-r--r--app/models/vehicle_journey_control/speed.rb2
-rw-r--r--app/models/vehicle_journey_control/waiting_time.rb4
-rw-r--r--app/policies/calendar_policy.rb19
-rw-r--r--app/policies/line_policy.rb9
-rw-r--r--app/policies/line_referential_policy.rb14
-rw-r--r--app/policies/purchase_window_policy.rb20
-rw-r--r--app/policies/referential_policy.rb2
-rw-r--r--app/policies/stop_area_policy.rb16
-rw-r--r--app/policies/stop_area_referential_policy.rb14
-rw-r--r--app/services/zip_service.rb31
-rw-r--r--app/uploaders/import_uploader.rb6
-rw-r--r--app/views/autocomplete_purchase_windows/index.rabl12
-rw-r--r--app/views/autocomplete_stop_areas/index.rabl3
-rw-r--r--app/views/companies/index.html.slim2
-rw-r--r--app/views/compliance_check_sets/index.html.slim4
-rw-r--r--app/views/compliance_check_sets/show.html.slim25
-rw-r--r--app/views/compliance_control_sets/index.html.slim2
-rw-r--r--app/views/compliance_control_sets/show.html.slim91
-rw-r--r--app/views/dashboards/_dashboard.html.slim33
-rw-r--r--app/views/devise/invitations/edit.html.slim33
-rw-r--r--app/views/devise/mailer/invitation_instructions.fr.html.slim4
-rw-r--r--app/views/devise/passwords/edit.html.slim28
-rw-r--r--app/views/devise/passwords/new.html.slim24
-rw-r--r--app/views/devise/sessions/new.html.slim2
-rw-r--r--app/views/import_resources/index.html.slim2
-rw-r--r--app/views/imports/_import_messages.html.slim11
-rw-r--r--app/views/imports/show.html.slim92
-rw-r--r--app/views/journey_patterns_collections/show.html.slim3
-rw-r--r--app/views/layouts/mailer.html.erb22
-rw-r--r--app/views/layouts/mailer.html.slim7
-rw-r--r--app/views/layouts/navigation/_main_nav_top.html.slim4
-rw-r--r--app/views/line_referentials/show.html.slim5
-rw-r--r--app/views/lines/_form.html.slim4
-rw-r--r--app/views/lines/index.html.slim4
-rw-r--r--app/views/lines/show.html.slim4
-rw-r--r--app/views/purchase_windows/_date_value_fields.html.slim13
-rw-r--r--app/views/purchase_windows/_filters.html.slim15
-rw-r--r--app/views/purchase_windows/_form.html.slim29
-rw-r--r--app/views/purchase_windows/_period_fields.html.slim15
-rw-r--r--app/views/purchase_windows/edit.html.slim7
-rw-r--r--app/views/purchase_windows/index.html.slim45
-rw-r--r--app/views/purchase_windows/new.html.slim6
-rw-r--r--app/views/purchase_windows/show.html.slim20
-rw-r--r--app/views/referential_companies/index.html.slim2
-rw-r--r--app/views/referential_vehicle_journeys/_filters.html.slim11
-rw-r--r--app/views/referential_vehicle_journeys/index.html.slim58
-rw-r--r--app/views/referentials/_filters.html.slim4
-rw-r--r--app/views/referentials/_form.html.slim2
-rw-r--r--app/views/referentials/new.html.slim2
-rw-r--r--app/views/referentials/show.html.slim2
-rw-r--r--app/views/routes/show.html.slim2
-rw-r--r--app/views/stop_area_referentials/show.html.slim7
-rw-r--r--app/views/stop_areas/_filters.html.slim2
-rw-r--r--app/views/stop_areas/_form.html.slim10
-rw-r--r--app/views/stop_areas/autocomplete.rabl24
-rw-r--r--app/views/stop_areas/index.html.slim6
-rw-r--r--app/views/stop_areas/show.html.slim15
-rw-r--r--app/views/stop_points/_stop_point.html.slim4
-rw-r--r--app/views/vehicle_journeys/index.html.slim7
-rw-r--r--app/views/vehicle_journeys/show.rabl18
-rw-r--r--app/views/workbenches/show.html.slim4
-rw-r--r--app/workers/referential_cloning_worker.rb27
-rw-r--r--app/workers/workbench_import_worker.rb92
-rw-r--r--app/workers/workbench_import_worker/object_state_updater.rb36
162 files changed, 2466 insertions, 947 deletions
diff --git a/app/assets/javascripts/forms.coffee b/app/assets/javascripts/forms.coffee
index 12d82fef1..b7ae3c6ca 100644
--- a/app/assets/javascripts/forms.coffee
+++ b/app/assets/javascripts/forms.coffee
@@ -32,14 +32,25 @@ isEdge = !isIE && !!window.StyleMedia
@colorSelector = ->
$('.form-group .dropdown.color_selector').each ->
- selectedStatus = $(this).children('.dropdown-toggle').children('.fa-circle')
-
+ selectedStatusColor = $(this).children('.dropdown-toggle').children('.fa-circle')
+ selectedStatusLabel = $(this).children('.dropdown-toggle')
+ self = this
$(this).on 'click', "input[type='radio']", (e) ->
selectedValue = e.currentTarget.value
+ selectedText = $(e.currentTarget).parent()[0].textContent
+ if e.currentTarget.getAttribute("data-for")
+ hidden = $("[name=\"#{e.currentTarget.getAttribute("data-for")}\"]")
+
if selectedValue == ''
- $(selectedStatus).css('color', 'transparent')
+ $(selectedStatusColor).css('color', 'transparent')
+ $(selectedStatusLabel).contents().filter( -> this.nodeType == 3 ).filter(':first').text = ""
+ hidden?.val ""
else
- $(selectedStatus).css('color', selectedValue)
+ $(selectedStatusColor).css('color', selectedValue)
+ $(selectedStatusLabel).contents().filter( -> this.nodeType == 3 ).first().replaceWith selectedText
+ hidden?.val selectedValue
+
+ $(self).find('.dropdown-toggle').click()
$ ->
togglableFilter()
diff --git a/app/assets/javascripts/main_menu.coffee b/app/assets/javascripts/main_menu.coffee
index a12c47576..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,34 +23,45 @@ $ ->
limit = 51
if $(window).scrollTop() >= limit
- 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/javascripts/select2.coffee b/app/assets/javascripts/select2.coffee
index 2e3884d7f..4cf5f42d0 100644
--- a/app/assets/javascripts/select2.coffee
+++ b/app/assets/javascripts/select2.coffee
@@ -9,24 +9,27 @@ bind_select2 = (el, cfg = {}) ->
target.select2 $.extend({}, default_cfg, cfg)
bind_select2_ajax = (el, cfg = {}) ->
- target = $(el)
+ _this = $(el)
cfg =
ajax:
data: (params) ->
- q:
- "#{target.data('term')}": params.term
- url: target.data('url'),
+ if _this.data('term')
+ { q: "#{_this.data('term')}": params.term }
+ else
+ { q: params.term }
+ url: _this.data('url'),
dataType: 'json',
delay: 125,
processResults: (data, params) -> results: data
- minimumInputLength: 1
- placeholder: target.data('select2ed-placeholder')
templateResult: (item) ->
item.text
templateSelection: (item) ->
item.text
escapeMarkup: (markup) ->
markup
+ initSelection : (item, callback) ->
+ if _this.data('initvalue')
+ callback(_this.data('initvalue'))
bind_select2(el, cfg)
@@ -40,7 +43,5 @@ bind_select2_ajax = (el, cfg = {}) ->
$('select.form-control.tags').each ->
bind_select2(this, {tags: true})
-
-
$ ->
select_2()
diff --git a/app/assets/stylesheets/_layout.sass b/app/assets/stylesheets/_layout.sass
index b6b91b2a5..340467e77 100644
--- a/app/assets/stylesheets/_layout.sass
+++ b/app/assets/stylesheets/_layout.sass
@@ -28,6 +28,7 @@ body
// width: 75%
border-bottom: 1px solid rgba($blue, 0.5)
margin: 30px auto 45px auto
+ clear: both
.content_header
font-size: 2.2rem
diff --git a/app/assets/stylesheets/base/_config.sass b/app/assets/stylesheets/base/_config.sass
index 65444479f..ec1c43e7f 100644
--- a/app/assets/stylesheets/base/_config.sass
+++ b/app/assets/stylesheets/base/_config.sass
@@ -16,6 +16,7 @@ $blue: #007fbb
$darkgrey: #4b4b4b
$grey: #a4a4a4
+$lightgrey: rgba($grey, 0.15)
$green: #70b12b
$red: #da2f36
diff --git a/app/assets/stylesheets/components/_breadcrumb.sass b/app/assets/stylesheets/components/_breadcrumb.sass
index 62f167eb4..1b30ca42b 100644
--- a/app/assets/stylesheets/components/_breadcrumb.sass
+++ b/app/assets/stylesheets/components/_breadcrumb.sass
@@ -1,3 +1,6 @@
.breadcrumbs
+ white-space: nowrap
+ text-overflow: ellipsis
+ overflow: hidden
a
- color: white \ No newline at end of file
+ color: white
diff --git a/app/assets/stylesheets/components/_buttons.sass b/app/assets/stylesheets/components/_buttons.sass
index a59699383..a649a07ef 100644
--- a/app/assets/stylesheets/components/_buttons.sass
+++ b/app/assets/stylesheets/components/_buttons.sass
@@ -165,6 +165,13 @@ table, .table
.fa:first-child
margin-right: 0.5em
+ & + li.delete-action
+ > a, > button
+ margin-top: 0
+ &:before
+ display: none
+
+
&.table-2entries .t2e-item
> .th
position: relative
diff --git a/app/assets/stylesheets/components/_color_selector.sass b/app/assets/stylesheets/components/_color_selector.sass
new file mode 100644
index 000000000..07bfa0c80
--- /dev/null
+++ b/app/assets/stylesheets/components/_color_selector.sass
@@ -0,0 +1,21 @@
+select.color_selector
+ option[value='#9B9B9B']
+ background-color: #9B9B9B
+ option[value='#FFA070']
+ background-color: #FFA070
+ option[value='#C67300']
+ background-color: #C67300
+ option[value='#7F551B']
+ background-color: #7F551B
+ option[value='#41CCE3']
+ background-color: #41CCE3
+ option[value='#09B09C']
+ background-color: #09B09C
+ option[value='#3655D7']
+ background-color: #3655D7
+ option[value='#6321A0']
+ background-color: #6321A0
+ option[value='#E796C6']
+ background-color: #E796C6
+ option[value='#DD2DAA']
+ background-color: #DD2DAA \ No newline at end of file
diff --git a/app/assets/stylesheets/components/_forms.sass b/app/assets/stylesheets/components/_forms.sass
index 9a363ab97..47faf19b1 100644
--- a/app/assets/stylesheets/components/_forms.sass
+++ b/app/assets/stylesheets/components/_forms.sass
@@ -229,6 +229,13 @@ $cbx-size-xs: 15px
&[type='checkbox']:checked + label:before
background-color: $blue
+ &[type='checkbox']:disabled + label
+ &:before
+ border-color: $grey
+ background-color: $lightgrey
+ cursor: not-allowed
+ &:after
+ display: none
// Table adjustments
table, .table
.td, td, .th, th
diff --git a/app/assets/stylesheets/components/_lists.sass b/app/assets/stylesheets/components/_lists.sass
index d8f83d72b..3cce20021 100644
--- a/app/assets/stylesheets/components/_lists.sass
+++ b/app/assets/stylesheets/components/_lists.sass
@@ -54,3 +54,8 @@ $dlWidth: 40%
// Definition
.dl-def
width: 100% - $dlWidth
+
+ ul
+ list-style: none
+ padding-left: 0
+ margin-bottom: 0 \ No newline at end of file
diff --git a/app/assets/stylesheets/components/_main_nav.sass b/app/assets/stylesheets/components/_main_nav.sass
index fdbf5836a..f102c4617 100644
--- a/app/assets/stylesheets/components/_main_nav.sass
+++ b/app/assets/stylesheets/components/_main_nav.sass
@@ -17,6 +17,9 @@ $menuW: 300px
line-height: $menuH
padding-left: 10px
opacity: 0.6
+ > a
+ color: rgba(#fff, 0.9)
+ text-decoration: none
#menu_left
position: absolute
@@ -240,6 +243,9 @@ $menuW: 300px
left: 0
top: 13px
+ & > .menu-item
+ max-width: 75%
+
.menu-item
padding: 0 10px
@@ -321,8 +327,11 @@ $menuW: 300px
height: $menuH * 2
transition: 0.1s
- #menu_top > .menu-content > .menu-item-group
- display: none
+ #menu_top > .menu-content
+ & > .menu-item
+ max-width: 90%
+ & > .menu-item-group
+ display: none
.sticky-content
height: $menuH
@@ -342,16 +351,16 @@ $menuW: 300px
white-space: nowrap
max-height: 1.1em
margin: 0 -1.4em 0 0
- padding: 0 1.4em 0 0
+ padding: 0 1.4em 5px 0
overflow: hidden
&:before
content: '[...]'
font-size: 0.65em
position: absolute
- z-index: 5
+ z-index: 6
right: 0
- bottom: 4px
+ bottom: 9px
&:after
content: ''
diff --git a/app/assets/stylesheets/components/_panels.sass b/app/assets/stylesheets/components/_panels.sass
index e9f615081..ab25d8184 100644
--- a/app/assets/stylesheets/components/_panels.sass
+++ b/app/assets/stylesheets/components/_panels.sass
@@ -34,6 +34,7 @@
a
text-decoration: none
color: $blue
+ text-transform: capitalize
&:hover, &:focus
color: $darkblue
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/components/_tables.sass b/app/assets/stylesheets/components/_tables.sass
index 178ec2f36..119a38c07 100644
--- a/app/assets/stylesheets/components/_tables.sass
+++ b/app/assets/stylesheets/components/_tables.sass
@@ -211,6 +211,18 @@
top: 50%
margin-top: -8px
+ .zdlp
+ background: url( image-path('map/zdlp.png') ) no-repeat left 50%
+ padding-left: 30px
+
+ .lda
+ background: url( image-path('map/lda.png') ) no-repeat left 50%
+ padding-left: 30px
+
+ .gdl
+ background: url( image-path('map/lda.png') ) no-repeat left 50%
+ padding-left: 30px
+
// select_toolbox
.select_toolbox
diff --git a/app/assets/stylesheets/modules/import_messages.sass b/app/assets/stylesheets/modules/import_messages.sass
index e5666cbcd..6a088dc06 100644
--- a/app/assets/stylesheets/modules/import_messages.sass
+++ b/app/assets/stylesheets/modules/import_messages.sass
@@ -2,4 +2,42 @@
.status_icon
padding-right: 20px
h1
- padding-bottom: 20px
+ padding-bottom: 20px
+
+
+.import_message-list
+ padding-bottom: 20px
+
+ .import_messages-head
+ display: block
+ font-size: 1.8rem
+ font-weight: 700
+ border-bottom: 2px solid #4b4b4b
+ padding: 5px 15px 6px 15px
+
+ dl
+ dd, dt
+ display: inline-block
+ letter-spacing: normal
+ word-spacing: normal
+ text-rendering: auto
+ vertical-align: top
+ padding: 5px 15px 6px 15px
+
+ dt
+ position: relative
+ width: 7%
+ font-weight: 700
+
+ &:before
+ content: ""
+ display: block
+ position: absolute
+ z-index: 1
+ top: 0
+ left: 0
+ width: 250%
+ border-bottom: 1px solid rgba(164, 164, 164, 0.5)
+
+ dd
+ width: 93% \ No newline at end of file
diff --git a/app/assets/stylesheets/typography/_sboiv.sass b/app/assets/stylesheets/typography/_sboiv.sass
index 6c82a5739..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'
@@ -80,7 +82,7 @@
.sb-network:before
content: '\e90c'
-.sb-compliance-check-set:before
+.sb-compliance_check_set:before, .sb-import_resource:before
content: '\e90d'
.sb-OAT:before
@@ -89,7 +91,7 @@
.sb-OAS:before
content: '\e90f'
-.sb-calendar:before
+.sb-calendar:before, .sb-purchase_window:before
content: '\e910'
.sb-journey_pattern:before
@@ -104,7 +106,7 @@
.sb-LDA:before
content: '\e914'
-.sb-referential:before
+.sb-referential:before, .sb-workbench:before
content: '\e915'
.sb-compliance_control_set:before
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index 97f5548ae..474277da1 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -1,6 +1,7 @@
class ApplicationController < ActionController::Base
include PaperTrailSupport
include Pundit
+ include FeatureChecker
rescue_from Pundit::NotAuthorizedError, with: :user_not_authorized
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/autocomplete_stop_areas_controller.rb b/app/controllers/autocomplete_stop_areas_controller.rb
index 233012028..d82fa316a 100644
--- a/app/controllers/autocomplete_stop_areas_controller.rb
+++ b/app/controllers/autocomplete_stop_areas_controller.rb
@@ -17,13 +17,20 @@ class AutocompleteStopAreasController < ChouetteController
scope = scope.possible_parents if relation_parent?
scope = scope.possible_parents if relation_children?
end
+ if search_scope.present?
+ scope = StopAreaPolicy::Scope.new(current_user, scope).search_scope(search_scope)
+ end
args = [].tap{|arg| 4.times{arg << "%#{params[:q]}%"}}
- @stop_areas = scope.where("name ILIKE ? OR city_name ILIKE ? OR registration_number ILIKE ? OR objectid ILIKE ?", *args).limit(50)
+ @stop_areas = scope.where("unaccent(name) ILIKE unaccent(?) OR unaccent(city_name) ILIKE unaccent(?) OR registration_number ILIKE ? OR objectid ILIKE ?", *args).limit(50)
@stop_areas
end
def target_type?
- params.has_key?( :target_type)
+ params.has_key?(:target_type)
+ end
+
+ def search_scope
+ params[:scope]
end
def relation_parent?
diff --git a/app/controllers/calendars_controller.rb b/app/controllers/calendars_controller.rb
index 2ed10a111..4a752f2b9 100644
--- a/app/controllers/calendars_controller.rb
+++ b/app/controllers/calendars_controller.rb
@@ -19,7 +19,7 @@ class CalendarsController < ChouetteController
private
def calendar_params
- permitted_params = [:id, :name, :short_name, periods_attributes: [:id, :begin, :end, :_destroy], date_values_attributes: [:id, :value, :_destroy]]
+ permitted_params = [:id, :name, :short_name, :shared, periods_attributes: [:id, :begin, :end, :_destroy], date_values_attributes: [:id, :value, :_destroy]]
permitted_params << :shared if policy(Calendar).share?
params.require(:calendar).permit(*permitted_params)
end
diff --git a/app/controllers/companies_controller.rb b/app/controllers/companies_controller.rb
index 931d846c5..f84252920 100644
--- a/app/controllers/companies_controller.rb
+++ b/app/controllers/companies_controller.rb
@@ -61,6 +61,10 @@ class CompaniesController < ChouetteController
alias_method :current_referential, :line_referential
helper_method :current_referential
+ def begin_of_association_chain
+ current_organisation
+ end
+
def company_params
params.require(:company).permit( :objectid, :object_version, :name, :short_name, :organizational_unit, :operating_department_name, :code, :phone, :fax, :email, :registration_number, :url, :time_zone )
end
diff --git a/app/controllers/concerns/activatable.rb b/app/controllers/concerns/activatable.rb
new file mode 100644
index 000000000..1a34551a9
--- /dev/null
+++ b/app/controllers/concerns/activatable.rb
@@ -0,0 +1,11 @@
+module Activatable
+ extend ActiveSupport::Concern
+
+ %w(activate deactivate).each do |action|
+ define_method action do
+ authorize resource, "#{action}?"
+ resource.send "#{action}!"
+ redirect_to request.referer || [current_referential, resource]
+ end
+ end
+end
diff --git a/app/controllers/concerns/feature_checker.rb b/app/controllers/concerns/feature_checker.rb
new file mode 100644
index 000000000..9ca5ed0a7
--- /dev/null
+++ b/app/controllers/concerns/feature_checker.rb
@@ -0,0 +1,42 @@
+# Check availability of optional features
+#
+# In your controller, use :
+#
+# requires_feature :test
+# requires_feature :test, only: [:show]
+#
+# In your view, use :
+#
+# has_feature? :test
+#
+module FeatureChecker
+ extend ActiveSupport::Concern
+
+ module ClassMethods
+ def requires_feature(feature, options = {})
+ before_action options do
+ check_feature! feature
+ end
+ end
+ end
+
+ included do
+ helper_method :has_feature?
+ end
+
+ protected
+
+ def has_feature?(*features)
+ features.all? do |feature|
+ current_organisation.has_feature? feature
+ end
+ end
+
+ def check_feature!(*features)
+ unless has_feature?(*features)
+ raise NotAuthorizedError, "Feature not autorized"
+ end
+ end
+
+ class NotAuthorizedError < StandardError; end
+end
diff --git a/app/controllers/group_of_lines_controller.rb b/app/controllers/group_of_lines_controller.rb
index 5762108dc..46d9d077f 100644
--- a/app/controllers/group_of_lines_controller.rb
+++ b/app/controllers/group_of_lines_controller.rb
@@ -42,7 +42,6 @@ class GroupOfLinesController < ChouetteController
end
end
-
protected
def filtered_group_of_lines_maps
@@ -70,6 +69,10 @@ class GroupOfLinesController < ChouetteController
alias_method :line_referential, :parent
+ def begin_of_association_chain
+ current_organisation
+ end
+
private
def group_of_line_params
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/line_referentials_controller.rb b/app/controllers/line_referentials_controller.rb
index 39c2cdb89..03dab3f8f 100644
--- a/app/controllers/line_referentials_controller.rb
+++ b/app/controllers/line_referentials_controller.rb
@@ -3,6 +3,7 @@ class LineReferentialsController < ChouetteController
defaults :resource_class => LineReferential
def sync
+ authorize resource, :synchronize?
@sync = resource.line_referential_syncs.build
if @sync.save
flash[:notice] = t('notice.line_referential_sync.created')
diff --git a/app/controllers/lines_controller.rb b/app/controllers/lines_controller.rb
index 2f0ef1542..7041a3a26 100644
--- a/app/controllers/lines_controller.rb
+++ b/app/controllers/lines_controller.rb
@@ -1,6 +1,8 @@
class LinesController < ChouetteController
include ApplicationHelper
+ include Activatable
include PolicyChecker
+
defaults :resource_class => Chouette::Line
respond_to :html
respond_to :xml
@@ -112,6 +114,10 @@ class LinesController < ChouetteController
alias_method :current_referential, :line_referential
helper_method :current_referential
+ def begin_of_association_chain
+ current_organisation
+ end
+
def line_params
params.require(:line).permit(
:transport_mode,
@@ -135,6 +141,8 @@ class LinesController < ChouetteController
:color,
:text_color,
:stable_id,
+ :transport_submode,
+ :secondary_company_ids => [],
footnotes_attributes: [:code, :label, :_destroy, :id]
)
end
diff --git a/app/controllers/networks_controller.rb b/app/controllers/networks_controller.rb
index 494d1e69f..79a5eb97b 100644
--- a/app/controllers/networks_controller.rb
+++ b/app/controllers/networks_controller.rb
@@ -71,6 +71,10 @@ class NetworksController < ChouetteController
alias_method :current_referential, :line_referential
helper_method :current_referential
+ def begin_of_association_chain
+ current_organisation
+ end
+
def network_params
params.require(:network).permit(:objectid, :object_version, :version_date, :description, :name, :registration_number, :source_name, :source_type_name, :source_identifier, :comment )
end
diff --git a/app/controllers/purchase_windows_controller.rb b/app/controllers/purchase_windows_controller.rb
new file mode 100644
index 000000000..04b5736bb
--- /dev/null
+++ b/app/controllers/purchase_windows_controller.rb
@@ -0,0 +1,75 @@
+class PurchaseWindowsController < ChouetteController
+ include ReferentialSupport
+ include RansackDateFilter
+ include PolicyChecker
+ before_action :ransack_contains_date, only: [:index]
+ defaults :resource_class => Chouette::PurchaseWindow, collection_name: 'purchase_windows', instance_name: 'purchase_window'
+ belongs_to :referential
+
+ requires_feature :purchase_windows
+
+ def index
+ index! do
+ @purchase_windows = decorate_purchase_windows(@purchase_windows)
+ end
+ end
+
+ def show
+ show! do
+ @purchase_window = @purchase_window.decorate(context: {
+ referential: @referential
+ })
+ end
+ end
+
+ protected
+
+ def create_resource(purchase_window)
+ purchase_window.referential = @referential
+ super
+ end
+
+ private
+
+ def purchase_window_params
+ params.require(:purchase_window).permit(:id, :name, :color, :referential_id, periods_attributes: [:id, :begin, :end, :_destroy])
+ end
+
+ def decorate_purchase_windows(purchase_windows)
+ ModelDecorator.decorate(
+ purchase_windows,
+ with: PurchaseWindowDecorator,
+ context: {
+ referential: @referential
+ }
+ )
+ end
+
+ def sort_column
+ Chouette::PurchaseWindow.column_names.include?(params[:sort]) ? params[:sort] : 'name'
+ end
+
+ def sort_direction
+ %w[asc desc].include?(params[:direction]) ? params[:direction] : 'asc'
+ end
+
+ def collection
+ return @purchase_windows if @purchase_windows
+ @q = Chouette::PurchaseWindow.ransack(params[:q])
+
+ purchase_windows = @q.result
+ purchase_windows = purchase_windows.order(sort_column + ' ' + sort_direction) if sort_column && sort_direction
+ @purchase_windows = purchase_windows.paginate(page: params[:page])
+ end
+
+ def ransack_contains_date
+ date =[]
+ if params[:q] && params[:q]['contains_date(1i)'].present?
+ ['contains_date(1i)', 'contains_date(2i)', 'contains_date(3i)'].each do |key|
+ date << params[:q][key].to_i
+ params[:q].delete(key)
+ end
+ params[:q]['contains_date'] = @date = Date.new(*date) rescue nil
+ end
+ end
+end
diff --git a/app/controllers/referential_companies_controller.rb b/app/controllers/referential_companies_controller.rb
index ca1ff67db..7e65a72cf 100644
--- a/app/controllers/referential_companies_controller.rb
+++ b/app/controllers/referential_companies_controller.rb
@@ -35,7 +35,8 @@ class ReferentialCompaniesController < ChouetteController
def collection
scope = referential.line_referential.companies
if params[:line_id]
- scope = referential.line_referential.lines.find(params[:line_id]).companies
+ line_scope = referential.line_referential.lines.find(params[:line_id]).companies
+ scope = line_scope if line_scope.exists?
end
@q = scope.search(params[:q])
diff --git a/app/controllers/referential_vehicle_journeys_controller.rb b/app/controllers/referential_vehicle_journeys_controller.rb
new file mode 100644
index 000000000..ad08699a5
--- /dev/null
+++ b/app/controllers/referential_vehicle_journeys_controller.rb
@@ -0,0 +1,17 @@
+#
+# Browse all VehicleJourneys of the Referential
+#
+class ReferentialVehicleJourneysController < ChouetteController
+ include ReferentialSupport
+ defaults :resource_class => Chouette::VehicleJourney, collection_name: :vehicle_journeys
+
+ requires_feature :referential_vehicle_journeys
+
+ private
+
+ def collection
+ @q ||= end_of_association_chain.ransack(params[:q])
+ @vehicle_journeys ||= @q.result.includes(:vehicle_journey_at_stops).paginate page: params[:page], per_page: 10
+ end
+
+end
diff --git a/app/controllers/referentials_controller.rb b/app/controllers/referentials_controller.rb
index 227651a59..83e3bc56a 100644
--- a/app/controllers/referentials_controller.rb
+++ b/app/controllers/referentials_controller.rb
@@ -13,11 +13,16 @@ class ReferentialsController < ChouetteController
end
def create
- create! do |format|
- build_referential
-
- if !!@referential.created_from_id
- format.html { redirect_to workbench_path(@referential.workbench) }
+ create! do |success, failure|
+ success.html do
+ if @referential.created_from_id.present?
+ flash[:notice] = t('notice.referentials.duplicate')
+ end
+ redirect_to workbench_path(@referential.workbench)
+ end
+ failure.html do
+ Rails.logger.info "Can't create Referential : #{@referential.errors.inspect}"
+ render :new
end
end
end
@@ -60,7 +65,7 @@ class ReferentialsController < ChouetteController
def validate
ComplianceControlSetCopyWorker.perform_async(params[:compliance_control_set], params[:id])
- flash[:notice] = I18n.t("referentials.operation_in_progress")
+ flash[:notice] = t('notice.referentials.validate')
redirect_to(referential_path)
end
diff --git a/app/controllers/stop_area_referentials_controller.rb b/app/controllers/stop_area_referentials_controller.rb
index 85541230d..f2d375e49 100644
--- a/app/controllers/stop_area_referentials_controller.rb
+++ b/app/controllers/stop_area_referentials_controller.rb
@@ -2,6 +2,7 @@ class StopAreaReferentialsController < ChouetteController
defaults :resource_class => StopAreaReferential
def sync
+ authorize resource, :synchronize?
@sync = resource.stop_area_referential_syncs.build
if @sync.save
flash[:notice] = t('notice.stop_area_referential_sync.created')
diff --git a/app/controllers/stop_areas_controller.rb b/app/controllers/stop_areas_controller.rb
index 133518324..5243ce56c 100644
--- a/app/controllers/stop_areas_controller.rb
+++ b/app/controllers/stop_areas_controller.rb
@@ -1,5 +1,6 @@
class StopAreasController < ChouetteController
include ApplicationHelper
+ include Activatable
defaults :resource_class => Chouette::StopArea
@@ -13,10 +14,12 @@ class StopAreasController < ChouetteController
respond_to :html, :kml, :xml, :json
respond_to :js, :only => :index
- # def complete
- # @stop_areas = line.stop_areas
- # render :layout => false
- # end
+ def autocomplete
+ scope = stop_area_referential.stop_areas.where(deleted_at: nil)
+ args = [].tap{|arg| 4.times{arg << "%#{params[:q]}%"}}
+ @stop_areas = scope.where("unaccent(name) ILIKE unaccent(?) OR unaccent(city_name) ILIKE unaccent(?) OR registration_number ILIKE ? OR objectid ILIKE ?", *args).limit(50)
+ @stop_areas
+ end
def select_parent
@stop_area = stop_area
@@ -154,6 +157,10 @@ class StopAreasController < ChouetteController
end
end
+ def begin_of_association_chain
+ current_organisation
+ end
+
private
def sort_column
@@ -171,7 +178,35 @@ class StopAreasController < ChouetteController
helper_method :current_referential
def stop_area_params
- params.require(:stop_area).permit( :routing_stop_ids, :routing_line_ids, :children_ids, :stop_area_type, :parent_id, :objectid, :object_version, :name, :comment, :area_type, :registration_number, :nearest_topic_name, :fare_code, :longitude, :latitude, :long_lat_type, :country_code, :street_name, :zip_code, :city_name, :mobility_restricted_suitability, :stairs_availability, :lift_availability, :int_user_needs, :coordinates, :url, :time_zone )
+ params.require(:stop_area).permit(
+ :area_type,
+ :children_ids,
+ :city_name,
+ :comment,
+ :coordinates,
+ :country_code,
+ :fare_code,
+ :int_user_needs,
+ :latitude,
+ :lift_availability,
+ :long_lat_type,
+ :longitude,
+ :mobility_restricted_suitability,
+ :name,
+ :nearest_topic_name,
+ :object_version,
+ :objectid,
+ :parent_id,
+ :registration_number,
+ :routing_line_ids,
+ :routing_stop_ids,
+ :stairs_availability,
+ :street_name,
+ :time_zone,
+ :url,
+ :waiting_time,
+ :zip_code,
+ )
end
end
diff --git a/app/controllers/vehicle_journeys_controller.rb b/app/controllers/vehicle_journeys_controller.rb
index c941aeae4..c03db0c7f 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.includes(: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/decorators/company_decorator.rb b/app/decorators/company_decorator.rb
index 9416c73ae..50b82d276 100644
--- a/app/decorators/company_decorator.rb
+++ b/app/decorators/company_decorator.rb
@@ -18,13 +18,6 @@ class CompanyDecorator < Draper::Decorator
def action_links
links = []
- if h.policy(Chouette::Company).create?
- links << Link.new(
- content: h.t('companies.actions.new'),
- href: h.new_line_referential_company_path(context[:referential])
- )
- end
-
if h.policy(object).update?
links << Link.new(
content: h.t('companies.actions.edit'),
@@ -37,7 +30,7 @@ class CompanyDecorator < Draper::Decorator
if h.policy(object).destroy?
links << Link.new(
- content: t('companies.actions.destroy'),
+ content: h.destroy_link_content('companies.actions.destroy'),
href: h.line_referential_company_path(
context[:referential],
object
diff --git a/app/decorators/compliance_check_set_decorator.rb b/app/decorators/compliance_check_set_decorator.rb
index 096596b19..c58e14d88 100644
--- a/app/decorators/compliance_check_set_decorator.rb
+++ b/app/decorators/compliance_check_set_decorator.rb
@@ -3,15 +3,6 @@ class ComplianceCheckSetDecorator < Draper::Decorator
def action_links
links = []
-
- links << Link.new(
- content: h.destroy_link_content,
- href: h.workbench_compliance_check_sets_path(object.id),
- method: :delete,
- data: {confirm: h.t('imports.actions.destroy_confirm')}
- )
-
- links
end
def lines_status
diff --git a/app/decorators/line_decorator.rb b/app/decorators/line_decorator.rb
index ede670cbd..9c0cf7292 100644
--- a/app/decorators/line_decorator.rb
+++ b/app/decorators/line_decorator.rb
@@ -18,7 +18,8 @@ class LineDecorator < Draper::Decorator
links << Link.new(
content: h.t('lines.actions.show_company'),
- href: [context[:line_referential], object.company]
+ href: [context[:line_referential], object.company],
+ disabled: object.company.nil?
)
if h.policy(Chouette::Line).create? &&
@@ -41,6 +42,26 @@ class LineDecorator < Draper::Decorator
)
end
+ if h.policy(object).deactivate?
+ links << Link.new(
+ content: h.deactivate_link_content('lines.actions.deactivate'),
+ href: h.deactivate_line_referential_line_path(context[:line_referential], object),
+ method: :put,
+ data: {confirm: h.t('lines.actions.deactivate_confirm')},
+ extra_class: "delete-action"
+ )
+ end
+
+ if h.policy(object).activate?
+ links << Link.new(
+ content: h.activate_link_content('lines.actions.activate'),
+ href: h.activate_line_referential_line_path(context[:line_referential], object),
+ method: :put,
+ data: {confirm: h.t('lines.actions.activate_confirm')},
+ extra_class: "delete-action"
+ )
+ end
+
if h.policy(object).destroy?
links << Link.new(
content: h.destroy_link_content('lines.actions.destroy'),
diff --git a/app/decorators/purchase_window_decorator.rb b/app/decorators/purchase_window_decorator.rb
new file mode 100644
index 000000000..646fdea0d
--- /dev/null
+++ b/app/decorators/purchase_window_decorator.rb
@@ -0,0 +1,34 @@
+class PurchaseWindowDecorator < Draper::Decorator
+ decorates Chouette::PurchaseWindow
+ delegate_all
+
+ def action_links
+ policy = h.policy(object)
+ links = []
+
+ if policy.update?
+ links << Link.new(
+ content: I18n.t('actions.edit'),
+ href: h.edit_referential_purchase_window_path(context[:referential].id, object)
+ )
+ end
+
+ if policy.destroy?
+ links << Link.new(
+ content: I18n.t('actions.destroy'),
+ href: h.referential_purchase_window_path(context[:referential].id, object),
+ method: :delete,
+ data: { confirm: h.t('purchase_windows.actions.destroy_confirm') }
+ )
+ end
+
+ links
+ end
+
+ def bounding_dates
+ unless object.date_ranges.empty?
+ object.date_ranges.map(&:min).min..object.date_ranges.map(&:max).max
+ end
+ end
+
+end
diff --git a/app/decorators/referential_decorator.rb b/app/decorators/referential_decorator.rb
index 4103790aa..d75ad1050 100644
--- a/app/decorators/referential_decorator.rb
+++ b/app/decorators/referential_decorator.rb
@@ -3,12 +3,26 @@ class ReferentialDecorator < Draper::Decorator
def action_links
policy = h.policy(object)
- links = [
- Link.new(
- content: h.t('time_tables.index.title'),
- href: h.referential_time_tables_path(object)
+ links = []
+
+ if has_feature?(:referential_vehicle_journeys)
+ links << Link.new(
+ content: h.t('referential_vehicle_journeys.index.title'),
+ href: h.referential_vehicle_journeys_path(object)
+ )
+ end
+
+ if has_feature?(:purchase_windows)
+ links << Link.new(
+ content: h.t('purchase_windows.index.title'),
+ href: h.referential_purchase_windows_path(object)
)
- ]
+ end
+
+ links << Link.new(
+ content: h.t('time_tables.index.title'),
+ href: h.referential_time_tables_path(object)
+ )
if policy.clone?
links << Link.new(
@@ -63,4 +77,12 @@ class ReferentialDecorator < Draper::Decorator
links
end
+
+ private
+
+ # TODO move to a base Decorator (ApplicationDecorator)
+ def has_feature?(*features)
+ h.has_feature?(*features) rescue false
+ end
+
end
diff --git a/app/decorators/referential_line_decorator.rb b/app/decorators/referential_line_decorator.rb
index 55acf7ed9..dceb3e2a9 100644
--- a/app/decorators/referential_line_decorator.rb
+++ b/app/decorators/referential_line_decorator.rb
@@ -24,30 +24,6 @@ class ReferentialLineDecorator < Draper::Decorator
)
)
- if h.policy(Chouette::Line).create? &&
- context[:referential].organisation == context[:current_organisation]
- links << Link.new(
- content: h.t('actions.new'),
- href: h.new_referential_line_path(context[:referential])
- )
- end
-
- if h.policy(object).update?
- links << Link.new(
- content: h.t('actions.edit'),
- href: h.edit_referential_line_path(context[:referential], object)
- )
- end
-
- if h.policy(object).destroy?
- links << Link.new(
- content: h.destroy_link_content('actions.destroy'),
- href: h.referential_line_path(context[:referential], object),
- method: :delete,
- data: { confirm: t('lines.actions.destroy_confirm') }
- )
- end
-
if !object.hub_restricted? ||
(object.hub_restricted? && object.routes.size < 2)
if h.policy(Chouette::Route).create? &&
diff --git a/app/decorators/route_decorator.rb b/app/decorators/route_decorator.rb
index 510c941a3..ec7f0d6aa 100644
--- a/app/decorators/route_decorator.rb
+++ b/app/decorators/route_decorator.rb
@@ -13,7 +13,7 @@ class RouteDecorator < Draper::Decorator
if object.stop_points.any?
links << Link.new(
- content: h.t('journey_patterns.index.title'),
+ content: h.t('journey_patterns.actions.index'),
href: [
context[:referential],
context[:line],
diff --git a/app/decorators/stop_area_decorator.rb b/app/decorators/stop_area_decorator.rb
index 4e777292d..32f6e1d2b 100644
--- a/app/decorators/stop_area_decorator.rb
+++ b/app/decorators/stop_area_decorator.rb
@@ -3,21 +3,12 @@ class StopAreaDecorator < Draper::Decorator
delegate_all
- def action_links(stop_area = nil)
- links = []
+ def common_action_links(stop_area = nil)
+ top_links, bottom_links = [], []
stop_area ||= object
- if h.policy(Chouette::StopArea).new?
- links << Link.new(
- content: h.t('stop_areas.actions.new'),
- href: h.new_stop_area_referential_stop_area_path(
- stop_area.stop_area_referential
- )
- )
- end
-
if h.policy(stop_area).update?
- links << Link.new(
+ top_links << Link.new(
content: h.t('stop_areas.actions.edit'),
href: h.edit_stop_area_referential_stop_area_path(
stop_area.stop_area_referential,
@@ -27,7 +18,7 @@ class StopAreaDecorator < Draper::Decorator
end
if h.policy(stop_area).destroy?
- links << Link.new(
+ bottom_links << Link.new(
content: h.destroy_link_content('stop_areas.actions.destroy'),
href: h.stop_area_referential_stop_area_path(
stop_area.stop_area_referential,
@@ -38,6 +29,40 @@ class StopAreaDecorator < Draper::Decorator
)
end
- links
+ [top_links, bottom_links]
+ end
+
+ def action_links(stop_area = nil)
+ stop_area ||= object
+ top_links, bottom_links = common_action_links(stop_area)
+ links = []
+
+ if h.policy(object).deactivate?
+ links << Link.new(
+ content: h.deactivate_link_content('stop_areas.actions.deactivate'),
+ href: h.deactivate_stop_area_referential_stop_area_path(stop_area.stop_area_referential, object),
+ method: :put,
+ data: {confirm: h.t('stop_areas.actions.deactivate_confirm')},
+ extra_class: "delete-action"
+ )
+ end
+
+ if h.policy(object).activate?
+ links << Link.new(
+ content: h.activate_link_content('stop_areas.actions.activate'),
+ href: h.activate_stop_area_referential_stop_area_path(stop_area.stop_area_referential, object),
+ method: :put,
+ data: {confirm: h.t('stop_areas.actions.activate_confirm')},
+ extra_class: "delete-action"
+ )
+ end
+
+ top_links + links + bottom_links
end
+
+ def waiting_time_text
+ return '-' if [nil, 0].include? waiting_time
+ h.t('stop_areas.waiting_time_format', value: waiting_time)
+ end
+
end
diff --git a/app/decorators/stop_point_decorator.rb b/app/decorators/stop_point_decorator.rb
index 196d6d490..27e1a7058 100644
--- a/app/decorators/stop_point_decorator.rb
+++ b/app/decorators/stop_point_decorator.rb
@@ -4,6 +4,6 @@ class StopPointDecorator < StopAreaDecorator
delegate_all
def action_links
- super(object.stop_area)
+ common_action_links(object.stop_area).flatten
end
end
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
index d2cdaaa20..713542ff4 100644
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -3,24 +3,38 @@ module ApplicationHelper
include NewapplicationHelper
+ def array_to_html_list items
+ content_tag :ul do
+ items.each do |item|
+ concat content_tag :li, item
+ end
+ end
+ end
+
def page_header_title(object)
# Unwrap from decorator, we want to know the object model name
object = object.object if object.try(:object)
local = "#{object.model_name.name.underscore.pluralize}.#{params[:action]}.title"
if object.try(:name)
- t(local, name: object.name)
+ t(local, name: object.name || object.id)
else
t(local)
end
end
def page_header_meta(object)
- info = t('last_update', time: l(object.updated_at, format: :short))
- if object.try(:versions)
- author = object.versions.try(:last).try(:whodunnit) || t('default_whodunnit')
- info = "#{info} <br/> #{t('whodunnit', author: author)}"
+ out = ""
+ display = true
+ display = policy(object).synchronize? if policy(object).respond_to?(:synchronize?) rescue false
+ if display
+ info = t('last_update', time: l(object.updated_at, format: :short))
+ if object.try(:versions)
+ author = object.versions.try(:last).try(:whodunnit) || t('default_whodunnit')
+ info = "#{info} <br/> #{t('whodunnit', author: author)}"
+ end
+ out += content_tag :div, info.html_safe, class: 'small last-update'
end
- content_tag :div, info.html_safe, class: 'small'
+ out.html_safe
end
def page_header_content_for(object)
diff --git a/app/helpers/common_helpers.rb b/app/helpers/common_helpers.rb
deleted file mode 100644
index 29cabddac..000000000
--- a/app/helpers/common_helpers.rb
+++ /dev/null
@@ -1,26 +0,0 @@
-
-module CommonHelpers
- # TODO: Needs refactoring, but does not seem to be under test
- # so let us refactor this **after** test coverage.
- def access_links_pairs(access_links)
- hpairs = Hash.new
- pairs = Array.new
- access_links.each do |link|
- key = pair_key(link)
- pair = nil
- if (hpairs.has_key? key)
- pair = hpairs[key]
- else
- pair = AccessLinkPair.new
- pairs << pair
- hpairs[key] = pair
- end
- if (link.link_orientation_type == "access_point_to_stop_area")
- pair.from_access_point = link
- else
- pair.to_access_point = link
- end
- end
- pairs
- end
-end
diff --git a/app/helpers/compliance_control_sets_helper.rb b/app/helpers/compliance_control_sets_helper.rb
index bb2a72623..57e6d9608 100644
--- a/app/helpers/compliance_control_sets_helper.rb
+++ b/app/helpers/compliance_control_sets_helper.rb
@@ -4,12 +4,12 @@ module ComplianceControlSetsHelper
[current_organisation, Organisation.find_by_name("STIF")].uniq
end
- def flotted_links ccs_id = @compliance_control_set
+ def floating_links ccs_id
links = [new_control(ccs_id), new_block(ccs_id)]
- unless links.all? &:nil?
- content_tag :div, class: 'select_toolbox' do
+ if links.any?
+ content_tag :div, class: 'select_toolbox', id: 'floating-links' do
content_tag :ul do
- links.collect {|link| concat content_tag(:li, link, class: 'st_action with_text') unless link.nil?}
+ links.collect {|link| concat content_tag(:li, link, class: 'st_action with_text') if link}
end
end
end
@@ -21,8 +21,6 @@ module ComplianceControlSetsHelper
concat content_tag :span, nil, class: 'fa fa-plus'
concat content_tag :span, t('compliance_control_sets.actions.add_compliance_control')
end
- else
- nil
end
end
@@ -32,8 +30,83 @@ module ComplianceControlSetsHelper
concat content_tag :span, nil, class: 'fa fa-plus'
concat content_tag :span,t('compliance_control_sets.actions.add_compliance_control_block')
end
- else
- nil
end
end
-end \ No newline at end of file
+
+ def render_compliance_control_block(block=nil)
+ content_tag :div, class: 'row' do
+ content_tag :div, class: 'col-lg-12' do
+ content_tag :h2 do
+ concat transport_mode_text(block)
+ concat dropdown(block) if block
+ end
+ end
+ end
+ end
+
+ def dropdown(block)
+ dropdown_button = content_tag :div, class: 'btn dropdown-toggle', "data-toggle": "dropdown" do
+ content_tag :div, nil, class: 'span fa fa-cog'
+ end
+
+ dropdown_menu = content_tag :ul, class: 'dropdown-menu' do
+ link_1 = content_tag :li do
+ link_to t('compliance_control_sets.actions.edit'), edit_compliance_control_set_compliance_control_block_path(@compliance_control_set.id, block.id)
+ end
+ link_2 = content_tag :li do
+ link_to t('compliance_control_sets.actions.destroy'), compliance_control_set_compliance_control_block_path(@compliance_control_set.id, block.id), :method => :delete, :data => {:confirm => t('compliance_control_sets.actions.destroy_confirm')}
+ end
+ link_1 + link_2
+ end
+
+ content_tag :div, class: 'btn-group' do
+ dropdown_button + dropdown_menu
+ end
+
+ end
+
+ def render_compliance_controls(compliance_controls)
+ content_tag :div, class: 'row' do
+ content_tag :div, class: 'col-lg-12' do
+ compliance_controls.try(:any?) ? render_table_builder(compliance_controls) : render_no_controls
+ end
+ end
+
+ end
+
+ def render_table_builder(compliance_controls)
+ table = content_tag :div, class: 'select_table' do
+ table_builder_2 compliance_controls,
+ [
+ TableBuilderHelper::Column.new(
+ key: :code,
+ attribute: 'code'
+ ),
+ TableBuilderHelper::Column.new(
+ key: :name,
+ attribute: 'name',
+ link_to: lambda do |compliance_control|
+ compliance_control_set_compliance_control_path(@compliance_control_set, compliance_control)
+ end
+ ),
+ TableBuilderHelper::Column.new(
+ key: :criticity,
+ attribute: 'criticity'
+ ),
+ TableBuilderHelper::Column.new(
+ key: :comment,
+ attribute: 'comment'
+ ),
+ ],
+ sortable: true,
+ cls: 'table has-filter has-search',
+ model: ComplianceControl
+ end
+ metas = content_tag :div, I18n.t('compliance_control_blocks.metas.control', count: compliance_controls.count), class: 'pull-right'
+ table + metas
+ end
+
+ def render_no_controls
+ content_tag :div, I18n.t('compliance_control_blocks.metas.control.zero'), class: 'alert alert-warning'
+ end
+end
diff --git a/app/helpers/links_helper.rb b/app/helpers/links_helper.rb
index 4fb7a797d..088415dc3 100644
--- a/app/helpers/links_helper.rb
+++ b/app/helpers/links_helper.rb
@@ -1,5 +1,18 @@
module LinksHelper
+ def custom_link_content(translation_key, klass, extra_class: nil)
+ klass = ["fa", "fa-#{klass}", "mr-xs", extra_class].compact.join(" ")
+ content_tag(:span, nil, class: klass) + t(translation_key)
+ end
+
def destroy_link_content(translation_key = 'actions.destroy')
- content_tag(:span, nil, class: 'fa fa-trash mr-xs') + t(translation_key)
+ custom_link_content translation_key, 'trash'
+ end
+
+ def deactivate_link_content(translation_key = 'actions.deactivate')
+ custom_link_content translation_key, 'power-off', extra_class: "text-danger"
+ end
+
+ def activate_link_content(translation_key = 'actions.activate')
+ custom_link_content translation_key, 'power-off', extra_class: "text-success"
end
end
diff --git a/app/helpers/multiple_selection_toolbox_helper.rb b/app/helpers/multiple_selection_toolbox_helper.rb
index 85294af6d..e0a1d2dd4 100644
--- a/app/helpers/multiple_selection_toolbox_helper.rb
+++ b/app/helpers/multiple_selection_toolbox_helper.rb
@@ -1,7 +1,11 @@
module MultipleSelectionToolboxHelper
# Box of links that floats at the bottom right of the page
- def multiple_selection_toolbox(actions)
+ # c.f. https://projects.af83.io/issues/5206
+ # #5206 method too long
+ def multiple_selection_toolbox(actions, collection_name:)
links = content_tag :ul do
+
+ # #5206 `if params[:controller]` mieux passer comme parametre si besoin
delete_path = nil
if params[:controller] = 'workbenches'
@@ -15,6 +19,7 @@ module MultipleSelectionToolboxHelper
method: :delete,
data: {
path: delete_path,
+ # #5206 Missing Translations
confirm: 'Etes-vous sûr(e) de vouloir effectuer cette action ?'
},
title: t("actions.#{action}")
@@ -33,7 +38,9 @@ module MultipleSelectionToolboxHelper
class: 'info-msg'
)
- content_tag :div, '', class: 'select_toolbox noselect' do
+ content_tag :div, '',
+ class: 'select_toolbox noselect',
+ id: "selected-#{collection_name}-action-box" do
links + label
end
end
diff --git a/app/helpers/table_builder_helper.rb b/app/helpers/table_builder_helper.rb
index 37f01ce0d..dede51920 100644
--- a/app/helpers/table_builder_helper.rb
+++ b/app/helpers/table_builder_helper.rb
@@ -95,6 +95,18 @@ module TableBuilderHelper
class: cls
end
+ def self.item_row_class_name collection
+ if collection.respond_to?(:model)
+ model_name = collection.model.name
+ elsif collection.respond_to?(:first)
+ model_name = collection.first.class.name
+ else
+ model_name = "item"
+ end
+
+ model_name.split("::").last.parameterize
+ end
+
private
def thead(collection, columns, sortable, selectable, has_links, overhead, model )
@@ -187,86 +199,92 @@ module TableBuilderHelper
end
end
- def tbody(collection, columns, selectable, links, overhead)
- content_tag :tbody do
- collection.map do |item|
-
- content_tag :tr do
- bcont = []
-
- if selectable
- bcont << content_tag(
- :td,
- checkbox(id_name: item.try(:id), value: item.try(:id))
- )
- end
-
- columns.each do |column|
- value = column.value(item)
+ def tr item, columns, selectable, links, overhead, model_name
+ klass = "#{model_name}-#{item.id}"
+ content_tag :tr, class: klass do
+ bcont = []
+ if selectable
+ disabled = selectable.respond_to?(:call) && !selectable.call(item)
+ bcont << content_tag(
+ :td,
+ checkbox(id_name: item.try(:id), value: item.try(:id), disabled: disabled)
+ )
+ end
- if column.linkable?
- path = column.link_to(item)
- link = link_to(value, path)
+ columns.each do |column|
+ value = column.value(item)
- if overhead.empty?
- bcont << content_tag(:td, link, title: 'Voir')
+ if column.linkable?
+ path = column.link_to(item)
+ link = link_to(value, path)
- else
- i = columns.index(column)
-
- if overhead[i].blank?
- if (i > 0) && (overhead[i - 1][:width] > 1)
- clsArrayAlt = overhead[i - 1][:cls].split
+ if overhead.empty?
+ bcont << content_tag(:td, link, title: 'Voir')
- bcont << content_tag(:td, link, title: 'Voir', class: td_cls(clsArrayAlt))
+ else
+ i = columns.index(column)
- else
- bcont << content_tag(:td, link, title: 'Voir')
- end
+ if overhead[i].blank?
+ if (i > 0) && (overhead[i - 1][:width] > 1)
+ clsArrayAlt = overhead[i - 1][:cls].split
- else
- clsArray = overhead[columns.index(column)][:cls].split
+ bcont << content_tag(:td, link, title: 'Voir', class: td_cls(clsArrayAlt))
- bcont << content_tag(:td, link, title: 'Voir', class: td_cls(clsArray))
- end
+ else
+ bcont << content_tag(:td, link, title: 'Voir')
end
else
- if overhead.empty?
- bcont << content_tag(:td, value)
+ clsArray = overhead[columns.index(column)][:cls].split
- else
- i = columns.index(column)
+ bcont << content_tag(:td, link, title: 'Voir', class: td_cls(clsArray))
+ end
+ end
- if overhead[i].blank?
- if (i > 0) && (overhead[i - 1][:width] > 1)
- clsArrayAlt = overhead[i - 1][:cls].split
+ else
+ if overhead.empty?
+ bcont << content_tag(:td, value)
- bcont << content_tag(:td, value, class: td_cls(clsArrayAlt))
+ else
+ i = columns.index(column)
- else
- bcont << content_tag(:td, value)
- end
+ if overhead[i].blank?
+ if (i > 0) && (overhead[i - 1][:width] > 1)
+ clsArrayAlt = overhead[i - 1][:cls].split
- else
- clsArray = overhead[i][:cls].split
+ bcont << content_tag(:td, value, class: td_cls(clsArrayAlt))
- bcont << content_tag(:td, value, class: td_cls(clsArray))
- end
+ else
+ bcont << content_tag(:td, value)
end
+
+ else
+ clsArray = overhead[i][:cls].split
+
+ bcont << content_tag(:td, value, class: td_cls(clsArray))
end
end
+ end
+ end
- if links.any? || item.try(:action_links).try(:any?)
- bcont << content_tag(
- :td,
- build_links(item, links),
- class: 'actions'
- )
- end
+ if links.any? || item.try(:action_links).try(:any?)
+ bcont << content_tag(
+ :td,
+ build_links(item, links),
+ class: 'actions'
+ )
+ end
- bcont.join.html_safe
- end
+ bcont.join.html_safe
+ end
+ end
+
+ def tbody(collection, columns, selectable, links, overhead)
+ model_name = TableBuilderHelper.item_row_class_name collection
+
+ content_tag :tbody do
+ collection.map do |item|
+ tr item, columns, selectable, links, overhead, model_name
end.join.html_safe
end
end
@@ -341,14 +359,20 @@ module TableBuilderHelper
end
end
- def checkbox(id_name:, value:)
+ def checkbox(id_name:, value:, disabled: false)
content_tag :div, '', class: 'checkbox' do
- check_box_tag(id_name, value).concat(
+ check_box_tag(id_name, value, nil, disabled: disabled).concat(
content_tag(:label, '', for: id_name)
)
end
end
+
def gear_menu_link(link)
+ klass = []
+ klass << link.extra_class if link.extra_class
+ klass << 'delete-action' if link.method == :delete
+ klass << 'disabled' if link.disabled
+
content_tag(
:li,
link_to(
@@ -358,7 +382,7 @@ module TableBuilderHelper
) do
link.content
end,
- class: ('delete-action' if link.method == :delete)
+ class: (klass.join(' ') if klass.present?)
)
end
diff --git a/app/helpers/table_builder_helper/url.rb b/app/helpers/table_builder_helper/url.rb
index a53ac5620..28f1ade76 100644
--- a/app/helpers/table_builder_helper/url.rb
+++ b/app/helpers/table_builder_helper/url.rb
@@ -10,7 +10,7 @@ module TableBuilderHelper
polymorph_url << item.route.line if item.is_a?(Chouette::RoutingConstraintZone)
polymorph_url << item if item.respond_to? :line_referential
polymorph_url << item.stop_area if item.respond_to? :stop_area
- polymorph_url << item if item.respond_to?(:stop_points) || item.is_a?(Chouette::TimeTable)
+ polymorph_url << item if item.respond_to?(:stop_points) || item.is_a?(Chouette::TimeTable) || item.is_a?(Chouette::PurchaseWindow)
elsif item.respond_to? :referential
if item.respond_to? :workbench
polymorph_url << item.workbench
diff --git a/app/inputs/color_select_input.rb b/app/inputs/color_select_input.rb
new file mode 100644
index 000000000..f92c80a22
--- /dev/null
+++ b/app/inputs/color_select_input.rb
@@ -0,0 +1,44 @@
+class ColorSelectInput < SimpleForm::Inputs::CollectionInput
+ enable :placeholder
+
+ def input(wrapper_options = {})
+ selected_color = object.send(attribute_name)
+ label = if selected_color
+ collection.find{|i| i.is_a?(Enumerable) && i.last == selected_color}.try(:first)
+ end
+
+ out = @builder.hidden_field attribute_name, value: selected_color
+ tag_name = ActionView::Helpers::Tags::Base.new( ActiveModel::Naming.param_key(object), attribute_name, :dummy ).send(:tag_name)
+ select = <<-eos
+ <div class="dropdown color_selector">
+ <button type='button' class="btn btn-default dropdown-toggle" data-toggle='dropdown' aria-haspopup='true' aria-expanded='true'
+ ><span
+ class='fa fa-circle mr-xs'
+ style='color: #{selected_color == nil ? 'transparent' : selected_color}'
+ >
+ </span>
+ #{label}
+ <span class='caret'></span>
+ </button>
+
+ <div class="form-group dropdown-menu" aria-labelledby='dpdwn_color'>
+ eos
+
+ collection.each do |color|
+ name = nil
+ name, color = color if color.is_a?(Enumerable)
+ select += <<-eos
+ <span class="radio" key=#{color} >
+ <label>
+ <input type='radio' class='color_selector' value='#{color}' data-for='#{tag_name}'/>
+ <span class='fa fa-circle mr-xs' style='color: #{color == nil ? 'transparent' : color}'></span>
+ #{name}
+ </label>
+ </span>
+ eos
+ end
+ select += "</div></div>"
+
+ out + select.html_safe
+ end
+end
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/components/BSelect2.js b/app/javascript/routes/components/BSelect2.js
index 340d9df95..0d8d7787f 100644
--- a/app/javascript/routes/components/BSelect2.js
+++ b/app/javascript/routes/components/BSelect2.js
@@ -96,17 +96,26 @@ class BSelect2 extends Component{
data: function(params) {
return {
q: params.term,
- target_type: 'zdep'
+ scope: 'route_editor'
};
},
processResults: function(data, params) {
return {
- results: data.map(
- item => _.assign(
- {},
- item,
- { text: item.name + ", " + item.zip_code + " " + item.short_city_name + " <small><em>(" + item.user_objectid + ")</em></small>" }
- )
+ results: data.map(
+ function(item) {
+ var text = item.name;
+ if (item.zip_code || item.short_city_name) {
+ text += ","
+ }
+ if (item.zip_code) {
+ text += ` ${item.zip_code}`
+ }
+ if (item.short_city_name) {
+ text += ` ${item.short_city_name}`
+ }
+ text += ` <small><em>(${item.area_type.toUpperCase()}, ${item.user_objectid})</em></small>`;
+ return _.assign({}, item, { text: text });
+ }
)
};
},
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 ddb54d615..40c8006f1 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'
}),
@@ -163,25 +189,17 @@ const actions = {
$(target).parent().removeClass('has-error').children('.help-block').remove()
},
validateFields : (fields) => {
- const test = []
-
- Object.keys(fields).map(function(key) {
- test.push(fields[key].validity.valid)
+ let valid = true
+ Object.keys(fields).forEach((key) => {
+ let field = fields[key]
+ if(field.validity && !field.validity.valid){
+ valid = false
+ $(field).parent().addClass('has-error').children('.help-block').remove()
+ $(field).parent().append("<span class='small help-block'>" + field.validationMessage + "</span>")
+ }
})
- if(test.indexOf(false) >= 0) {
- // Form is invalid
- test.map(function(item, i) {
- if(item == false) {
- const k = Object.keys(fields)[i]
- $(fields[k]).parent().addClass('has-error').children('.help-block').remove()
- $(fields[k]).parent().append("<span class='small help-block'>" + fields[k].validationMessage + "</span>")
- }
- })
- return false
- } else {
- // Form is valid
- return true
- }
+
+ return valid
},
toggleArrivals : () => ({
type: 'TOGGLE_ARRIVALS',
@@ -313,6 +331,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,26 +341,35 @@ const actions = {
color: tt.color
})
}
+ if(val.purchase_windows){
+ 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)
})
- vehicleJourneys.push({
- journey_pattern: val.journey_pattern,
- published_journey_name: val.published_journey_name,
- objectid: val.objectid,
- short_id: val.short_id,
- footnotes: val.footnotes,
- time_tables: timeTables,
- vehicle_journey_at_stops: vjasWithDelta,
- deletable: false,
- selected: false,
- published_journey_name: val.published_journey_name || 'non renseigné',
- published_journey_identifier: val.published_journey_identifier || 'non renseigné',
- company: val.company || 'non renseigné',
- transport_mode: val.route.line.transport_mode || 'undefined',
- transport_submode: val.route.line.transport_submode || 'undefined'
- })
+
+ vehicleJourneys.push(
+ _.assign({}, val, {
+ time_tables: timeTables,
+ purchase_windows: purchaseWindows,
+ vehicle_journey_at_stops: vjasWithDelta,
+ deletable: false,
+ selected: false,
+ published_journey_name: val.published_journey_name || 'non renseigné',
+ published_journey_identifier: val.published_journey_identifier || 'non renseigné',
+ company: val.company || {name: 'non renseigné'},
+ transport_mode: val.route.line.transport_mode || 'undefined',
+ transport_submode: val.route.line.transport_submode || 'undefined'
+ })
+ )
}
window.currentItemsLength = vehicleJourneys.length
dispatch(actions.receiveVehicleJourneys(vehicleJourneys))
@@ -439,6 +467,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)))
@@ -457,10 +499,6 @@ const actions = {
minute: actions.simplePad(newArrivalDT.getUTCMinutes())
}
}
- },
- escapeWildcardCharacters(search) {
- let newSearch = search.replace(/^_/, "\\_")
- return newSearch.replace(/^%/, "\\%")
}
}
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 7621dfc10..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 {
@@ -17,7 +18,12 @@ export default class Tools extends Component {
hasPolicy(key) {
// Check if the user has the policy to disable or not the action
- return this.props.filters.policy[`vehicle_journeys.${key}`]
+ 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() {
@@ -25,13 +31,16 @@ export default class Tools extends Component {
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>
@@ -45,4 +54,4 @@ Tools.propTypes = {
vehicleJourneys : PropTypes.array.isRequired,
onCancelSelection: PropTypes.func.isRequired,
filters: PropTypes.object.isRequired
-} \ No newline at end of file
+}
diff --git a/app/javascript/vehicle_journeys/components/VehicleJourney.js b/app/javascript/vehicle_journeys/components/VehicleJourney.js
index 8fb4b8a7e..5f6281487 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,19 +57,33 @@ 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='strong mb-xs'>{this.props.value.objectid ? this.props.value.short_id : '-'}</div>
- <div>{this.props.value.journey_pattern.short_id}</div>
+ <div
+ className='th'
+ onClick={(e) =>
+ ($(e.target).parents("a").length == 0) && this.props.onSelectVehicleJourney(this.props.index)
+ }
+ >
+ <div className='strong mb-xs'>{this.props.value.short_id || '-'}</div>
+ <div>{this.props.value.published_journey_name && this.props.value.published_journey_name != "non renseigné" ? this.props.value.published_journey_name : '-'}</div>
+ <div>{this.props.value.journey_pattern.short_id || '-'}</div>
<div>
{time_tables.slice(0,3).map((tt, i)=>
<span key={i} className='vj_tt'>{this.timeTableURL(tt)}</span>
)}
{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 +110,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 +122,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 +143,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 +155,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 +175,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..dc480d6b4 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() {
@@ -111,8 +115,10 @@ export default class VehicleJourneys extends Component {
<div className='t2e-head w20'>
<div className='th'>
<div className='strong mb-xs'>ID course</div>
+ <div>Nom 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 +138,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 +160,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/CreateModal.js b/app/javascript/vehicle_journeys/components/tools/CreateModal.js
index 2bffebdf6..cd593cdff 100644
--- a/app/javascript/vehicle_journeys/components/tools/CreateModal.js
+++ b/app/javascript/vehicle_journeys/components/tools/CreateModal.js
@@ -9,7 +9,7 @@ export default class CreateModal extends Component {
}
handleSubmit() {
- if(actions.validateFields(this.refs) == true && this.props.modal.modalProps.selectedJPModal) {
+ if (actions.validateFields(...this.refs, $('.vjCreateSelectJP')[0]) && this.props.modal.modalProps.selectedJPModal) {
this.props.onAddVehicleJourney(this.refs, this.props.modal.modalProps.selectedJPModal, this.props.stopPointsList, this.props.modal.modalProps.selectedCompany)
this.props.onModalClose()
$('#NewVehicleJourneyModal').modal('hide')
@@ -61,7 +61,7 @@ export default class CreateModal extends Component {
<div className='form-group'>
<label className='control-label'>Nom du transporteur</label>
<CompanySelect2
- company = {undefined}
+ company = {this.props.modal.modalProps.vehicleJourney && this.props.modal.modalProps.vehicleJourney.company || undefined}
onSelect2Company = {(e) => this.props.onSelect2Company(e)}
/>
</div>
@@ -130,4 +130,4 @@ CreateModal.propTypes = {
onAddVehicleJourney: PropTypes.func.isRequired,
onSelect2JourneyPattern: PropTypes.func.isRequired,
disabled: PropTypes.bool.isRequired
-} \ No newline at end of file
+}
diff --git a/app/javascript/vehicle_journeys/components/tools/EditVehicleJourney.js b/app/javascript/vehicle_journeys/components/tools/EditVehicleJourney.js
index 7d91896eb..f8d6add03 100644
--- a/app/javascript/vehicle_journeys/components/tools/EditVehicleJourney.js
+++ b/app/javascript/vehicle_journeys/components/tools/EditVehicleJourney.js
@@ -97,6 +97,7 @@ export default class EditVehicleJourney extends Component {
<div className='form-group'>
<label className='control-label'>Transporteur</label>
<CompanySelect2
+ editModal={this.props.modal.type == "edit"}
editMode={this.props.editMode}
company = {this.props.modal.modalProps.vehicleJourney.company}
onSelect2Company = {(e) => this.props.onSelect2Company(e)}
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 fef3cdcc9..fdaa5aeed 100644
--- a/app/javascript/vehicle_journeys/components/tools/TimetablesEditVehicleJourney.js
+++ b/app/javascript/vehicle_journeys/components/tools/TimetablesEditVehicleJourney.js
@@ -29,10 +29,11 @@ export default class TimetablesEditVehicleJourney extends Component {
<li className='st_action'>
<button
type='button'
- disabled={(actions.getSelected(this.props.vehicleJourneys).length != 1 || this.props.disabled)}
+ disabled={(actions.getSelected(this.props.vehicleJourneys).length < 1 || this.props.disabled)}
data-toggle='modal'
data-target='#CalendarsEditVehicleJourneyModal'
onClick={() => this.props.onOpenCalendarsEditModal(actions.getSelected(this.props.vehicleJourneys))}
+ title='Calendriers'
>
<span className='fa fa-calendar'></span>
</button>
@@ -56,7 +57,7 @@ export default class TimetablesEditVehicleJourney extends Component {
<div className='wrapper'>
<div>
<div className='form-group'>
- <label className='control-label'>Calendriers associés</label>
+ <label className='control-label'>{this.props.modal.modalProps.timetables.length == 0 ? "Aucun calendrier associé" : "Calendriers associés"}</label>
</div>
</div>
<div></div>
@@ -65,9 +66,14 @@ export default class TimetablesEditVehicleJourney extends Component {
{this.props.modal.modalProps.timetables.map((tt, i) =>
<div className='nested-fields' key={i}>
<div className='wrapper'>
- <div> <a href={this.timeTableURL(tt)} target="_blank">{tt.comment}</a> </div>
+ <div>
+ <a href={this.timeTableURL(tt)} target="_blank">
+ <span className="fa fa-circle mr-xs" style={{color: tt.color || 'black'}}></span>
+ {tt.comment}
+ </a>
+ </div>
{
- this.props.editMode &&
+ this.props.editMode &&
<div>
<a
href='#'
@@ -85,13 +91,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 +110,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 +151,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/CompanySelect2.js b/app/javascript/vehicle_journeys/components/tools/select2s/CompanySelect2.js
index 0697e9141..79ba8f094 100644
--- a/app/javascript/vehicle_journeys/components/tools/select2s/CompanySelect2.js
+++ b/app/javascript/vehicle_journeys/components/tools/select2s/CompanySelect2.js
@@ -21,7 +21,7 @@ export default class BSelect4 extends Component {
value={(this.props.company) ? this.props.company.name : undefined}
onSelect={(e) => this.props.onSelect2Company(e) }
onUnselect={() => this.props.onUnselect2Company()}
- disabled={!this.props.editMode}
+ disabled={!this.props.editMode && this.props.editModal}
multiple={false}
ref='company_id'
options={{
@@ -36,7 +36,7 @@ export default class BSelect4 extends Component {
delay: '500',
data: function(params) {
return {
- q: { name_cont: actions.escapeWildcardCharacters(params.term)},
+ q: { name_cont: params.term},
};
},
processResults: function(data, params) {
diff --git a/app/javascript/vehicle_journeys/components/tools/select2s/MissionSelect2.js b/app/javascript/vehicle_journeys/components/tools/select2s/MissionSelect2.js
index 5b4ae564c..fa847886c 100644
--- a/app/javascript/vehicle_journeys/components/tools/select2s/MissionSelect2.js
+++ b/app/javascript/vehicle_journeys/components/tools/select2s/MissionSelect2.js
@@ -21,6 +21,8 @@ export default class BSelect4 extends Component {
onSelect={(e) => this.props.onSelect2JourneyPattern(e)}
multiple={false}
ref='journey_pattern_id'
+ className={!this.props.isFilter ? "vjCreateSelectJP" : null}
+ required={!this.props.isFilter}
options={{
allowClear: false,
theme: 'bootstrap',
@@ -33,7 +35,7 @@ export default class BSelect4 extends Component {
delay: '500',
data: function(params) {
return {
- q: { published_name_or_objectid_or_registration_number_cont: actions.escapeWildcardCharacters(params.term)},
+ q: { published_name_or_objectid_or_registration_number_cont: params.term},
};
},
processResults: function(data, params) {
diff --git a/app/javascript/vehicle_journeys/components/tools/select2s/TimetableSelect2.js b/app/javascript/vehicle_journeys/components/tools/select2s/TimetableSelect2.js
index a90a9f7b8..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: actions.escapeWildcardCharacters(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/components/tools/select2s/VJSelect2.js b/app/javascript/vehicle_journeys/components/tools/select2s/VJSelect2.js
index 37628fce0..b063abeca 100644
--- a/app/javascript/vehicle_journeys/components/tools/select2s/VJSelect2.js
+++ b/app/javascript/vehicle_journeys/components/tools/select2s/VJSelect2.js
@@ -33,7 +33,7 @@ export default class BSelect4b extends Component {
delay: '500',
data: function(params) {
return {
- q: { objectid_cont: actions.escapeWildcardCharacters(params.term)},
+ q: { objectid_cont: params.term},
};
},
processResults: function(data, params) {
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..eae3314e8 100644
--- a/app/javascript/vehicle_journeys/reducers/modal.js
+++ b/app/javascript/vehicle_journeys/reducers/modal.js
@@ -1,6 +1,6 @@
import _ from 'lodash'
-let vehicleJourneysModal, newModalProps
+let vehicleJourneysModal, newModalProps, vehicleJourney
export default function modal(state = {}, action) {
switch (action.type) {
@@ -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,15 +55,38 @@ 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})
+ vehicleJourney = _.assign({}, state.modalProps.vehicleJourney, {company: action.selectedItem})
+ newModalProps = _.assign({}, state.modalProps, {vehicleJourney})
return _.assign({}, state, {modalProps: newModalProps})
case 'UNSELECT_CP_EDIT_MODAL':
- newModalProps = _.assign({}, state.modalProps, {selectedCompany : undefined})
+ vehicleJourney = _.assign({}, state.modalProps.vehicleJourney, {company: undefined})
+ newModalProps = _.assign({}, state.modalProps, {vehicleJourney})
return _.assign({}, state, {modalProps: newModalProps})
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 +95,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 +122,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 +184,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 775fefdca..d057bf704 100644
--- a/app/javascript/vehicle_journeys/reducers/vehicleJourneys.js
+++ b/app/javascript/vehicle_journeys/reducers/vehicleJourneys.js
@@ -38,8 +38,10 @@ const vehicleJourney= (state = {}, action, keep) => {
published_journey_name: action.data.published_journey_name.value,
published_journey_identifier: action.data.published_journey_identifier.value,
objectid: '',
+ short_id: '',
footnotes: [],
time_tables: [],
+ purchase_windows: [],
vehicle_journey_at_stops: pristineVjasList,
selected: false,
deletable: false,
@@ -78,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{
@@ -160,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){
@@ -224,4 +235,4 @@ export default function vehicleJourneys(state = [], action) {
default:
return state
}
-} \ No newline at end of file
+}
diff --git a/app/models/calendar.rb b/app/models/calendar.rb
index b2e73929f..34ed51374 100644
--- a/app/models/calendar.rb
+++ b/app/models/calendar.rb
@@ -3,22 +3,19 @@ require_relative 'calendar/date_value'
require_relative 'calendar/period'
class Calendar < ActiveRecord::Base
+ include DateSupport
+ include PeriodSupport
+
has_paper_trail
belongs_to :organisation
- has_many :time_tables
validates_presence_of :name, :short_name, :organisation
validates_uniqueness_of :short_name
- after_initialize :init_dates_and_date_ranges
+ has_many :time_tables
scope :contains_date, ->(date) { where('date ? = any (dates) OR date ? <@ any (date_ranges)', date, date) }
- def init_dates_and_date_ranges
- self.dates ||= []
- self.date_ranges ||= []
- end
-
def self.ransackable_scopes(auth_object = nil)
[:contains_date]
end
@@ -35,144 +32,4 @@ class Calendar < ActiveRecord::Base
end
end
-
- ### Calendar::Period
- # Required by coocon
- def build_period
- Calendar::Period.new
- end
-
- def periods
- @periods ||= init_periods
- end
-
- def init_periods
- (date_ranges || [])
- .each_with_index
- .map( &Calendar::Period.method(:from_range) )
- end
- private :init_periods
-
- validate :validate_periods
-
- def validate_periods
- periods_are_valid = periods.all?(&:valid?)
-
- periods.each do |period|
- if period.intersect?(periods)
- period.errors.add(:base, I18n.t('calendars.errors.overlapped_periods'))
- periods_are_valid = false
- end
- end
-
- unless periods_are_valid
- errors.add(:periods, :invalid)
- end
- end
-
- def flatten_date_array attributes, key
- date_int = %w(1 2 3).map {|e| attributes["#{key}(#{e}i)"].to_i }
- Date.new(*date_int)
- end
-
- def periods_attributes=(attributes = {})
- @periods = []
- attributes.each do |index, period_attribute|
- # Convert date_select to date
- ['begin', 'end'].map do |attr|
- period_attribute[attr] = flatten_date_array(period_attribute, attr)
- end
- period = Calendar::Period.new(period_attribute.merge(id: index))
- @periods << period unless period.marked_for_destruction?
- end
-
- date_ranges_will_change!
- end
-
- before_validation :fill_date_ranges
-
- def fill_date_ranges
- if @periods
- self.date_ranges = @periods.map(&:range).compact.sort_by(&:begin)
- end
- end
-
- after_save :clear_periods
-
- def clear_periods
- @periods = nil
- end
-
- private :clear_periods
-
- ### Calendar::DateValue
-
- # Required by coocon
- def build_date_value
- Calendar::DateValue.new
- end
-
- def date_values
- @date_values ||= init_date_values
- end
-
- def init_date_values
- if dates
- dates.each_with_index.map { |d, index| Calendar::DateValue.from_date(index, d) }
- else
- []
- end
- end
- private :init_date_values
-
- validate :validate_date_values
-
- def validate_date_values
- date_values_are_valid = date_values.all?(&:valid?)
-
- date_values.each do |date_value|
- if date_values.count { |d| d.value == date_value.value } > 1
- date_value.errors.add(:base, I18n.t('activerecord.errors.models.calendar.attributes.dates.date_in_dates'))
- date_values_are_valid = false
- end
- date_ranges.each do |date_range|
- if date_range.cover? date_value.value
- date_value.errors.add(:base, I18n.t('activerecord.errors.models.calendar.attributes.dates.date_in_date_ranges'))
- date_values_are_valid = false
- end
- end
- end
-
- unless date_values_are_valid
- errors.add(:date_values, :invalid)
- end
- end
-
- def date_values_attributes=(attributes = {})
- @date_values = []
- attributes.each do |index, date_value_attribute|
- date_value_attribute['value'] = flatten_date_array(date_value_attribute, 'value')
- date_value = Calendar::DateValue.new(date_value_attribute.merge(id: index))
- @date_values << date_value unless date_value.marked_for_destruction?
- end
-
- dates_will_change!
- end
-
- before_validation :fill_dates
-
- def fill_dates
- if @date_values
- self.dates = @date_values.map(&:value).compact.sort
- end
- end
-
- after_save :clear_date_values
-
- def clear_date_values
- @date_values = nil
- end
-
- private :clear_date_values
-
end
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/area_type.rb b/app/models/chouette/area_type.rb
new file mode 100644
index 000000000..43d96b391
--- /dev/null
+++ b/app/models/chouette/area_type.rb
@@ -0,0 +1,42 @@
+class Chouette::AreaType
+ include Comparable
+
+ ALL = %i(zdep zder zdlp zdlr lda gdl).freeze
+
+ @@all = ALL
+ mattr_accessor :all
+
+ def self.all=(values)
+ @@all = ALL & values
+ reset_caches!
+ end
+
+ @@instances = {}
+ def self.find(code)
+ code = code.to_sym
+ @@instances[code] ||= new(code) if ALL.include? code
+ end
+
+ def self.reset_caches!
+ @@instances = {}
+ @@options = nil
+ end
+
+ def self.options
+ @@options ||= all.map { |c| find(c) }.map { |t| [ t.label, t.code ] }
+ end
+
+ attr_reader :code
+ def initialize(code)
+ @code = code
+ end
+
+ def <=>(other)
+ all.index(code) <=> all.index(other.code)
+ end
+
+ def label
+ I18n.translate code, scope: 'area_types.label'
+ end
+
+end
diff --git a/app/models/chouette/line.rb b/app/models/chouette/line.rb
index 784e3f5b9..2d776e94b 100644
--- a/app/models/chouette/line.rb
+++ b/app/models/chouette/line.rb
@@ -72,12 +72,23 @@ module Chouette
end
def display_name
- [self.get_objectid.local_id, number, name, company.try(:name)].compact.join(' - ')
+ [self.get_objectid.short_id, number, name, company.try(:name)].compact.join(' - ')
end
def companies
line_referential.companies.where(id: ([company_id] + Array(secondary_company_ids)).compact)
end
+ def deactivate!
+ update_attribute :deactivated, true
+ end
+
+ def activate!
+ update_attribute :deactivated, false
+ end
+
+ def activated?
+ !deactivated
+ end
end
end
diff --git a/app/models/chouette/purchase_window.rb b/app/models/chouette/purchase_window.rb
new file mode 100644
index 000000000..e89a0ec7f
--- /dev/null
+++ b/app/models/chouette/purchase_window.rb
@@ -0,0 +1,36 @@
+require 'range_ext'
+require_relative '../calendar/period'
+
+module Chouette
+ class PurchaseWindow < Chouette::TridentActiveRecord
+ # include ChecksumSupport
+ include ObjectidSupport
+ include PeriodSupport
+ extend Enumerize
+ enumerize :color, in: %w(#9B9B9B #FFA070 #C67300 #7F551B #41CCE3 #09B09C #3655D7 #6321A0 #E796C6 #DD2DAA)
+
+ 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
+
+ def self.colors_i18n
+ Hash[*color.values.map{|c| [I18n.t("enumerize.purchase_window.color.#{c[1..-1]}"), c]}.flatten]
+ end
+
+ def local_id
+ "IBOO-#{self.referential.id}-#{self.id}"
+ end
+
+ # def checksum_attributes
+ # end
+
+ end
+end
diff --git a/app/models/chouette/stop_area.rb b/app/models/chouette/stop_area.rb
index cc7170728..4f1359ff8 100644
--- a/app/models/chouette/stop_area.rb
+++ b/app/models/chouette/stop_area.rb
@@ -9,7 +9,7 @@ module Chouette
include ObjectidSupport
extend Enumerize
- enumerize :area_type, in: %i(zdep zder zdlp zdlr lda)
+ enumerize :area_type, in: Chouette::AreaType::ALL
with_options dependent: :destroy do |assoc|
assoc.has_many :stop_points
@@ -39,11 +39,21 @@ module Chouette
validates_format_of :coordinates, :with => %r{\A *-?(0?[0-9](\.[0-9]*)?|[0-8][0-9](\.[0-9]*)?|90(\.[0]*)?) *\, *-?(0?[0-9]?[0-9](\.[0-9]*)?|1[0-7][0-9](\.[0-9]*)?|180(\.[0]*)?) *\Z}, :allow_nil => true, :allow_blank => true
validates_format_of :url, :with => %r{\Ahttps?:\/\/([\da-z\.-]+)\.([a-z\.]{2,6})([\/\w \.-]*)*\/?\Z}, :allow_nil => true, :allow_blank => true
+ validates_numericality_of :waiting_time, greater_than_or_equal_to: 0, only_integer: true, if: :waiting_time
+ validate :parent_area_type_must_be_greater
+
def self.nullable_attributes
[:registration_number, :street_name, :country_code, :fare_code,
:nearest_topic_name, :comment, :long_lat_type, :zip_code, :city_name, :url, :time_zone]
end
+ def parent_area_type_must_be_greater
+ return unless self.parent
+ if Chouette::AreaType.find(self.area_type) >= Chouette::AreaType.find(self.parent.area_type)
+ errors.add(:parent_id, I18n.t('stop_areas.errors.parent_area_type', area_type: self.parent.area_type))
+ end
+ end
+
after_update :clean_invalid_access_links
before_save :coordinates_to_lat_lng
@@ -72,6 +82,10 @@ module Chouette
end
end
+ def full_name
+ "#{name} #{zip_code} #{city_name} - #{user_objectid}"
+ end
+
def user_objectid
if objectid =~ /^.*:([0-9A-Za-z_-]+):STIF$/
$1
@@ -196,10 +210,12 @@ module Chouette
GeoRuby::SimpleFeatures::Envelope.from_coordinates coordinates
end
+ # DEPRECATED use StopArea#area_type
def stop_area_type
area_type ? area_type : " "
end
+ # DEPRECATED use StopArea#area_type
def stop_area_type=(stop_area_type)
self.area_type = (stop_area_type ? stop_area_type.camelcase : nil)
end
@@ -324,5 +340,20 @@ module Chouette
end
end
+ def activated?
+ deleted_at.nil?
+ end
+
+ def deactivated?
+ !activated?
+ end
+
+ def activate!
+ update_attribute :deleted_at, nil
+ end
+
+ def deactivate!
+ update_attribute :deleted_at, Time.now
+ end
end
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/chouette/vehicle_journey_at_stop.rb b/app/models/chouette/vehicle_journey_at_stop.rb
index 6f0119e74..6b3c1e7de 100644
--- a/app/models/chouette/vehicle_journey_at_stop.rb
+++ b/app/models/chouette/vehicle_journey_at_stop.rb
@@ -75,5 +75,14 @@ module Chouette
attrs << self.arrival_day_offset.to_s
end
end
+
+ def departure
+ departure_time.utc.strftime "%H:%M" if departure_time
+ end
+
+ def arrival
+ arrival_time.utc.strftime "%H:%M" if arrival_time
+ end
+
end
-end \ No newline at end of file
+end
diff --git a/app/models/compliance_check_block.rb b/app/models/compliance_check_block.rb
index 05240b428..059547e1b 100644
--- a/app/models/compliance_check_block.rb
+++ b/app/models/compliance_check_block.rb
@@ -6,8 +6,8 @@ class ComplianceCheckBlock < ActiveRecord::Base
has_many :compliance_checks
- hstore_accessor :condition_attributes,
- transport_mode: :string,
- transport_submode: :string
+ store_accessor :condition_attributes,
+ :transport_mode,
+ :transport_submode
end
diff --git a/app/models/compliance_control.rb b/app/models/compliance_control.rb
index 65e22643d..2bde5b95a 100644
--- a/app/models/compliance_control.rb
+++ b/app/models/compliance_control.rb
@@ -6,7 +6,7 @@ class ComplianceControl < ActiveRecord::Base
def prerequisite; I18n.t('compliance_controls.metas.no_prerequisite'); end
def predicate; I18n.t("compliance_controls.#{self.name.underscore}.description") end
def dynamic_attributes
- hstore_metadata_for_control_attributes.keys
+ stored_attributes[:control_attributes] || []
end
def policy_class
@@ -39,7 +39,6 @@ class ComplianceControl < ActiveRecord::Base
belongs_to :compliance_control_block
enumerize :criticity, in: criticities, scope: true, default: :warning
- hstore_accessor :control_attributes, {}
validates :criticity, presence: true
validates :name, presence: true
diff --git a/app/models/compliance_control_block.rb b/app/models/compliance_control_block.rb
index cfcdfd1a6..d7d84fd06 100644
--- a/app/models/compliance_control_block.rb
+++ b/app/models/compliance_control_block.rb
@@ -5,11 +5,15 @@ class ComplianceControlBlock < ActiveRecord::Base
belongs_to :compliance_control_set
has_many :compliance_controls, dependent: :destroy
- hstore_accessor :condition_attributes,
- transport_mode: :string,
- transport_submode: :string
+ store_accessor :condition_attributes,
+ :transport_mode,
+ :transport_submode
validates :transport_mode, presence: true
validates :compliance_control_set, presence: true
+ def name
+ ApplicationController.helpers.transport_mode_text(self)
+ end
+
end
diff --git a/app/models/concerns/date_support.rb b/app/models/concerns/date_support.rb
new file mode 100644
index 000000000..fbfe19af1
--- /dev/null
+++ b/app/models/concerns/date_support.rb
@@ -0,0 +1,80 @@
+module DateSupport
+ extend ActiveSupport::Concern
+
+ included do
+ after_initialize :init_dates
+
+ def init_dates
+ self.dates ||= []
+ end
+
+ ### Calendar::DateValue
+ # Required by coocon
+ def build_date_value
+ Calendar::DateValue.new
+ end
+
+ def date_values
+ @date_values ||= init_date_values
+ end
+
+ def init_date_values
+ if dates
+ dates.each_with_index.map { |d, index| Calendar::DateValue.from_date(index, d) }
+ else
+ []
+ end
+ end
+ private :init_date_values
+
+ validate :validate_date_values
+
+ def validate_date_values
+ date_values_are_valid = date_values.all?(&:valid?)
+
+ date_values.each do |date_value|
+ if date_values.count { |d| d.value == date_value.value } > 1
+ date_value.errors.add(:base, I18n.t('activerecord.errors.models.calendar.attributes.dates.date_in_dates'))
+ date_values_are_valid = false
+ end
+ date_ranges.each do |date_range|
+ if date_range.cover? date_value.value
+ date_value.errors.add(:base, I18n.t('activerecord.errors.models.calendar.attributes.dates.date_in_date_ranges'))
+ date_values_are_valid = false
+ end
+ end
+ end
+
+ unless date_values_are_valid
+ errors.add(:date_values, :invalid)
+ end
+ end
+
+ def date_values_attributes=(attributes = {})
+ @date_values = []
+ attributes.each do |index, date_value_attribute|
+ date_value_attribute['value'] = flatten_date_array(date_value_attribute, 'value')
+ date_value = Calendar::DateValue.new(date_value_attribute.merge(id: index))
+ @date_values << date_value unless date_value.marked_for_destruction?
+ end
+
+ dates_will_change!
+ end
+
+ before_validation :fill_dates
+
+ def fill_dates
+ if @date_values
+ self.dates = @date_values.map(&:value).compact.sort
+ end
+ end
+
+ after_save :clear_date_values
+
+ def clear_date_values
+ @date_values = nil
+ end
+
+ private :clear_date_values
+ end
+end \ No newline at end of file
diff --git a/app/models/concerns/period_support.rb b/app/models/concerns/period_support.rb
new file mode 100644
index 000000000..e17451fe4
--- /dev/null
+++ b/app/models/concerns/period_support.rb
@@ -0,0 +1,80 @@
+module PeriodSupport
+ extend ActiveSupport::Concern
+
+ included do
+ after_initialize :init_date_ranges
+
+ def init_date_ranges
+ self.date_ranges ||= []
+ end
+
+ ### Calendar::Period
+ # Required by coocon
+ def build_period
+ Calendar::Period.new
+ end
+
+ def periods
+ @periods ||= init_periods
+ end
+
+ def init_periods
+ (date_ranges || [])
+ .each_with_index
+ .map( &Calendar::Period.method(:from_range) )
+ end
+ private :init_periods
+
+ validate :validate_periods
+
+ def validate_periods
+ periods_are_valid = periods.all?(&:valid?)
+
+ periods.each do |period|
+ if period.intersect?(periods)
+ period.errors.add(:base, I18n.t('calendars.errors.overlapped_periods'))
+ periods_are_valid = false
+ end
+ end
+
+ unless periods_are_valid
+ errors.add(:periods, :invalid)
+ end
+ end
+
+ def flatten_date_array attributes, key
+ date_int = %w(1 2 3).map {|e| attributes["#{key}(#{e}i)"].to_i }
+ Date.new(*date_int)
+ end
+
+ def periods_attributes=(attributes = {})
+ @periods = []
+ attributes.each do |index, period_attribute|
+ # Convert date_select to date
+ ['begin', 'end'].map do |attr|
+ period_attribute[attr] = flatten_date_array(period_attribute, attr)
+ end
+ period = Calendar::Period.new(period_attribute.merge(id: index))
+ @periods << period unless period.marked_for_destruction?
+ end
+
+ date_ranges_will_change!
+ end
+
+ before_validation :fill_date_ranges
+
+ def fill_date_ranges
+ if @periods
+ self.date_ranges = @periods.map(&:range).compact.sort_by(&:begin)
+ end
+ end
+
+ after_save :clear_periods
+
+ def clear_periods
+ @periods = nil
+ end
+
+ private :clear_periods
+ end
+end
diff --git a/app/models/generic_attribute_control/min_max.rb b/app/models/generic_attribute_control/min_max.rb
index ab6f546a7..1c429b9a4 100644
--- a/app/models/generic_attribute_control/min_max.rb
+++ b/app/models/generic_attribute_control/min_max.rb
@@ -1,6 +1,6 @@
module GenericAttributeControl
class MinMax < ComplianceControl
- hstore_accessor :control_attributes, minimum: :integer, maximum: :integer, target: :string
+ store_accessor :control_attributes, :minimum, :maximum, :target
validates :minimum, numericality: true, allow_nil: true
validates :maximum, numericality: true, allow_nil: true
diff --git a/app/models/generic_attribute_control/pattern.rb b/app/models/generic_attribute_control/pattern.rb
index 3a4a55d5c..7fc008e28 100644
--- a/app/models/generic_attribute_control/pattern.rb
+++ b/app/models/generic_attribute_control/pattern.rb
@@ -1,6 +1,6 @@
module GenericAttributeControl
class Pattern < ComplianceControl
- hstore_accessor :control_attributes, pattern: :string, target: :string
+ store_accessor :control_attributes, :pattern, :target
validates :target, presence: true
validates :pattern, presence: true
diff --git a/app/models/generic_attribute_control/uniqueness.rb b/app/models/generic_attribute_control/uniqueness.rb
index f707c944b..82b5c0892 100644
--- a/app/models/generic_attribute_control/uniqueness.rb
+++ b/app/models/generic_attribute_control/uniqueness.rb
@@ -1,6 +1,6 @@
module GenericAttributeControl
class Uniqueness < ComplianceControl
- hstore_accessor :control_attributes, target: :string
+ store_accessor :control_attributes, :target
validates :target, presence: true
diff --git a/app/models/import.rb b/app/models/import.rb
index 20e7f2d8a..049a65f40 100644
--- a/app/models/import.rb
+++ b/app/models/import.rb
@@ -14,12 +14,11 @@ class Import < ActiveRecord::Base
end
extend Enumerize
- enumerize :status, in: %i(new pending successful warning failed running aborted canceled), scope: true, default: :new
+ enumerize :status, in: %w(new pending successful warning failed running aborted canceled), scope: true, default: :new
validates :name, presence: true
validates :file, presence: true
validates_presence_of :workbench, :creator
- validates_format_of :file, with: %r{\.zip\z}i, message: I18n.t('activerecord.errors.models.import.attributes.file.wrong_file_extension')
before_create :initialize_fields
@@ -28,15 +27,19 @@ class Import < ActiveRecord::Base
end
def children_succeedeed
- children.with_status(:successful).count
+ children.with_status(:successful, :warning).count
end
- def self.failing_statuses
- symbols_with_indifferent_access(%i(failed aborted canceled))
+ def self.launched_statuses
+ %w(new pending)
+ end
+
+ def self.failed_statuses
+ %w(failed aborted canceled)
end
def self.finished_statuses
- symbols_with_indifferent_access(%i(successful failed warning aborted canceled))
+ %w(successful failed warning aborted canceled)
end
def notify_parent
@@ -52,35 +55,25 @@ class Import < ActiveRecord::Base
end
def update_status
- status_count = children.group(:status).count
- children_finished_count = children_failed_count = children_count = 0
-
- status_count.each do |status, count|
- if self.class.failing_statuses.include?(status)
- children_failed_count += count
- end
- if self.class.finished_statuses.include?(status)
- children_finished_count += count
- end
- children_count += count
- end
-
- attributes = {
- current_step: children_finished_count
- }
-
status =
- if children_failed_count > 0
+ if children.where(status: self.class.failed_statuses).count > 0
'failed'
- elsif status_count['successful'] == children_count
+ elsif children.where(status: "warning").count > 0
+ 'warning'
+ elsif children.where(status: "successful").count == children.count
'successful'
end
+ attributes = {
+ current_step: children.count,
+ status: status
+ }
+
if self.class.finished_statuses.include?(status)
attributes[:ended_at] = Time.now
end
- update attributes.merge(status: status)
+ update attributes
end
def update_referentials
@@ -97,7 +90,4 @@ class Import < ActiveRecord::Base
self.token_download = SecureRandom.urlsafe_base64
end
- def self.symbols_with_indifferent_access(array)
- array.flat_map { |symbol| [symbol, symbol.to_s] }
- end
end
diff --git a/app/models/organisation.rb b/app/models/organisation.rb
index f6fba2d67..da7d1fcf3 100644
--- a/app/models/organisation.rb
+++ b/app/models/organisation.rb
@@ -1,3 +1,4 @@
+# coding: utf-8
class Organisation < ActiveRecord::Base
include DataFormatEnumerations
@@ -18,36 +19,39 @@ class Organisation < ActiveRecord::Base
validates_presence_of :name
validates_uniqueness_of :code
- def self.portail_api_request
- conf = Rails.application.config.try(:stif_portail_api)
- raise 'Rails.application.config.stif_portail_api configuration is not defined' unless conf
+ class << self
- HTTPService.get_json_resource(
- host: conf[:url],
- path: '/api/v1/organizations',
- token: conf[:key])
- end
+ def portail_api_request
+ conf = Rails.application.config.try(:stif_portail_api)
+ raise 'Rails.application.config.stif_portail_api configuration is not defined' unless conf
+
+ HTTPService.get_json_resource(
+ host: conf[:url],
+ path: '/api/v1/organizations',
+ token: conf[:key])
+ end
- def self.sync_update code, name, scope
- org = Organisation.find_or_initialize_by(code: code)
- if scope
- org.sso_attributes ||= {}
- if org.sso_attributes['functional_scope'] != scope
- org.sso_attributes['functional_scope'] = scope
- # FIXME see #1941
- org.sso_attributes_will_change!
+ def sync_update code, name, scope
+ org = Organisation.find_or_initialize_by(code: code)
+ if scope
+ org.sso_attributes ||= {}
+ if org.sso_attributes['functional_scope'] != scope
+ org.sso_attributes['functional_scope'] = scope
+ # FIXME see #1941
+ org.sso_attributes_will_change!
+ end
end
+ org.name = name
+ org.synced_at = Time.now
+ org.save
+ org
end
- org.name = name
- org.synced_at = Time.now
- org.save
- org
- end
- def self.portail_sync
- self.portail_api_request.each do |el|
- org = self.sync_update el['code'], el['name'], el['functional_scope']
- puts "✓ Organisation #{org.name} has been updated" unless Rails.env.test?
+ def portail_sync
+ portail_api_request.each do |el|
+ org = self.sync_update el['code'], el['name'], el['functional_scope']
+ puts "✓ Organisation #{org.name} has been updated" unless Rails.env.test?
+ end
end
end
@@ -64,4 +68,16 @@ class Organisation < ActiveRecord::Base
raise ActiveRecord::RecordNotFound
end
+ def functional_scope
+ JSON.parse( (sso_attributes || {}).fetch('functional_scope', '[]') )
+ end
+
+ def lines_set
+ STIF::CodifligneLineId.lines_set_from_functional_scope( functional_scope )
+ end
+
+ def has_feature?(feature)
+ features && features.include?(feature.to_s)
+ end
+
end
diff --git a/app/models/referential.rb b/app/models/referential.rb
index 3a9ef2027..1cdda9e6a 100644
--- a/app/models/referential.rb
+++ b/app/models/referential.rb
@@ -1,3 +1,4 @@
+# coding: utf-8
class Referential < ActiveRecord::Base
include DataFormatEnumerations
include ObjectidFormatterSupport
@@ -34,7 +35,7 @@ class Referential < ActiveRecord::Base
I18n.t('referentials.errors.inconsistent_organisation',
indirect_name: workbench.organisation.name,
direct_name: organisation.name))
- end
+ end, if: :organisation
belongs_to :line_referential
validates_presence_of :line_referential
@@ -139,6 +140,10 @@ class Referential < ActiveRecord::Base
Chouette::RoutingConstraintZone.all
end
+ def purchase_windows
+ Chouette::PurchaseWindow.all
+ end
+
before_validation :define_default_attributes
def define_default_attributes
@@ -295,7 +300,8 @@ class Referential < ActiveRecord::Base
def detect_overlapped_referentials
self.class.where(id: overlapped_referential_ids).each do |referential|
- errors.add :metadatas, I18n.t("referentials.errors.overlapped_referential", referential: referential.name)
+ Rails.logger.info "Referential #{referential.id} #{referential.metadatas.inspect} overlaps #{metadatas.inspect}"
+ errors.add :metadatas, I18n.t("referentials.errors.overlapped_referential", :referential => referential.name)
end
end
@@ -315,11 +321,11 @@ class Referential < ActiveRecord::Base
end
def assign_slug
- self.slug ||= "#{self.name.parameterize.gsub('-', '_')}_#{Time.now.to_i}"
+ self.slug ||= "#{name.parameterize.gsub('-', '_')}_#{Time.now.to_i}" if name
end
def assign_prefix
- self.prefix = organisation.name.parameterize.gsub('-', '_')
+ self.prefix = organisation.name.parameterize.gsub('-', '_') if organisation
end
def assign_line_and_stop_area_referential
diff --git a/app/models/referential_cloning.rb b/app/models/referential_cloning.rb
index 5bf283814..24117e6c8 100644
--- a/app/models/referential_cloning.rb
+++ b/app/models/referential_cloning.rb
@@ -2,14 +2,27 @@ class ReferentialCloning < ActiveRecord::Base
include AASM
belongs_to :source_referential, class_name: 'Referential'
belongs_to :target_referential, class_name: 'Referential'
- after_commit :perform_clone, :on => :create
+ after_commit :clone, on: :create
- private
- def perform_clone
+ def clone
ReferentialCloningWorker.perform_async(id)
- # ReferentialCloningWorker.new.perform(id)
end
+ def clone!
+ run!
+
+ AF83::SchemaCloner
+ .new(source_referential.slug, target_referential.slug)
+ .clone_schema
+
+ successful!
+ rescue Exception => e
+ Rails.logger.error "Clone failed : #{e}"
+ failed!
+ end
+
+ private
+
aasm column: :status do
state :new, :initial => true
state :pending
diff --git a/app/models/user.rb b/app/models/user.rb
index 37d35209a..1342f60ed 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -36,7 +36,7 @@ class User < ActiveRecord::Base
self.name = extra[:full_name]
self.email = extra[:email]
self.organisation = Organisation.sync_update extra[:organisation_code], extra[:organisation_name], extra[:functional_scope]
- self.permissions = Stif::PermissionTranslator.translate(extra[:permissions])
+ self.permissions = Stif::PermissionTranslator.translate(extra[:permissions], self.organisation)
end
def self.portail_api_request
diff --git a/app/models/vehicle_journey_control/delta.rb b/app/models/vehicle_journey_control/delta.rb
index 1f3a4d492..077dd6c4a 100644
--- a/app/models/vehicle_journey_control/delta.rb
+++ b/app/models/vehicle_journey_control/delta.rb
@@ -1,7 +1,7 @@
module VehicleJourneyControl
class Delta < ComplianceControl
- hstore_accessor :control_attributes, maximum: :integer
+ store_accessor :control_attributes, :maximum
validates :maximum, numericality: true, allow_nil: true
diff --git a/app/models/vehicle_journey_control/speed.rb b/app/models/vehicle_journey_control/speed.rb
index be9f838e4..14fad9139 100644
--- a/app/models/vehicle_journey_control/speed.rb
+++ b/app/models/vehicle_journey_control/speed.rb
@@ -1,6 +1,6 @@
module VehicleJourneyControl
class Speed < ComplianceControl
- hstore_accessor :control_attributes, minimum: :integer, maximum: :integer
+ store_accessor :control_attributes, :minimum, :maximum
validates :minimum, numericality: true, allow_nil: true
validates :maximum, numericality: true, allow_nil: true
diff --git a/app/models/vehicle_journey_control/waiting_time.rb b/app/models/vehicle_journey_control/waiting_time.rb
index 68fccb5c1..f2666cb72 100644
--- a/app/models/vehicle_journey_control/waiting_time.rb
+++ b/app/models/vehicle_journey_control/waiting_time.rb
@@ -1,8 +1,8 @@
module VehicleJourneyControl
class WaitingTime < ComplianceControl
- hstore_accessor :control_attributes, maximum: :integer
+ store_accessor :control_attributes, :maximum
- validates :maximum, numericality: true, allow_nil: true
+ validates_numericality_of :maximum, allow_nil: true, greater_than_or_equal_to: 0
def self.default_code; "3-VehicleJourney-1" end
end
diff --git a/app/policies/calendar_policy.rb b/app/policies/calendar_policy.rb
index 074c41d8d..c2da8c924 100644
--- a/app/policies/calendar_policy.rb
+++ b/app/policies/calendar_policy.rb
@@ -5,18 +5,15 @@ class CalendarPolicy < ApplicationPolicy
end
end
- def create?
- !archived? && user.has_permission?('calendars.create')
- end
- def destroy?
- !archived? & organisation_match? && user.has_permission?('calendars.destroy')
- end
- def update?
- !archived? && organisation_match? && user.has_permission?('calendars.update')
+ def create?
+ user.has_permission?('calendars.create')
end
+ def destroy?; instance_permission("destroy") end
+ def update?; instance_permission("update") end
+ def share?; instance_permission("share") end
- def share?
- user.organisation.name == 'STIF' # FIXME
+ private
+ def instance_permission permission
+ organisation_match? && user.has_permission?("calendars.#{permission}")
end
-
end
diff --git a/app/policies/line_policy.rb b/app/policies/line_policy.rb
index 67ea0b611..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
@@ -14,6 +13,14 @@ class LinePolicy < ApplicationPolicy
user.has_permission?('lines.destroy')
end
+ def deactivate?
+ !record.deactivated? && user.has_permission?('lines.change_status')
+ end
+
+ def activate?
+ record.deactivated? && user.has_permission?('lines.change_status')
+ end
+
def update?
user.has_permission?('lines.update')
end
diff --git a/app/policies/line_referential_policy.rb b/app/policies/line_referential_policy.rb
new file mode 100644
index 000000000..ee742a083
--- /dev/null
+++ b/app/policies/line_referential_policy.rb
@@ -0,0 +1,14 @@
+class LineReferentialPolicy < ApplicationPolicy
+ class Scope < Scope
+ def resolve
+ scope
+ end
+ end
+
+ def synchronize?; instance_permission("synchronize") end
+
+ private
+ def instance_permission permission
+ user.has_permission?("line_referentials.#{permission}")
+ end
+end
diff --git a/app/policies/purchase_window_policy.rb b/app/policies/purchase_window_policy.rb
new file mode 100644
index 000000000..75143a8bd
--- /dev/null
+++ b/app/policies/purchase_window_policy.rb
@@ -0,0 +1,20 @@
+class PurchaseWindowPolicy < ApplicationPolicy
+ class Scope < Scope
+ def resolve
+ scope
+ end
+ end
+
+ def create?
+ !archived? && organisation_match? && user.has_permission?('purchase_windows.create')
+ end
+
+ def update?
+ !archived? && organisation_match? && user.has_permission?('purchase_windows.update')
+ end
+
+ def destroy?
+ !archived? && organisation_match? && user.has_permission?('purchase_windows.destroy')
+ end
+
+end \ No newline at end of file
diff --git a/app/policies/referential_policy.rb b/app/policies/referential_policy.rb
index bc8a3e24b..253917509 100644
--- a/app/policies/referential_policy.rb
+++ b/app/policies/referential_policy.rb
@@ -22,7 +22,7 @@ class ReferentialPolicy < ApplicationPolicy
end
def validate?
- !archived? && create?
+ !archived? && create? && organisation_match?
end
def archive?
diff --git a/app/policies/stop_area_policy.rb b/app/policies/stop_area_policy.rb
index faeebbc2a..6db48b702 100644
--- a/app/policies/stop_area_policy.rb
+++ b/app/policies/stop_area_policy.rb
@@ -1,5 +1,13 @@
class StopAreaPolicy < ApplicationPolicy
class Scope < Scope
+ def search_scope scope_name
+ scope = resolve
+ if scope_name&.to_s == "route_editor"
+ scope = scope.where(area_type: 'zdep') unless user.organisation.has_feature?("route_stop_areas_all_types")
+ end
+ scope
+ end
+
def resolve
scope
end
@@ -16,4 +24,12 @@ class StopAreaPolicy < ApplicationPolicy
def update?
user.has_permission?('stop_areas.update')
end
+
+ def deactivate?
+ !record.deactivated? && user.has_permission?('stop_areas.change_status')
+ end
+
+ def activate?
+ record.deactivated? && user.has_permission?('stop_areas.change_status')
+ end
end
diff --git a/app/policies/stop_area_referential_policy.rb b/app/policies/stop_area_referential_policy.rb
new file mode 100644
index 000000000..e370babf8
--- /dev/null
+++ b/app/policies/stop_area_referential_policy.rb
@@ -0,0 +1,14 @@
+class StopAreaReferentialPolicy < ApplicationPolicy
+ class Scope < Scope
+ def resolve
+ scope
+ end
+ end
+
+ def synchronize?; instance_permission("synchronize") end
+
+ private
+ def instance_permission permission
+ user.has_permission?("stop_area_referentials.#{permission}")
+ end
+end
diff --git a/app/services/zip_service.rb b/app/services/zip_service.rb
index 7a4bdad1b..7166e6448 100644
--- a/app/services/zip_service.rb
+++ b/app/services/zip_service.rb
@@ -1,12 +1,16 @@
class ZipService
- class Subdir < Struct.new(:name, :stream, :spurious)
+ class Subdir < Struct.new(:name, :stream, :spurious, :foreign_lines)
+ def ok?
+ foreign_lines.empty? && spurious.empty?
+ end
end
- attr_reader :current_key, :current_output, :current_spurious, :yielder
+ attr_reader :allowed_lines, :current_key, :foreign_lines, :current_output, :current_spurious, :yielder
- def initialize data
+ def initialize data, allowed_lines
@zip_data = StringIO.new(data)
+ @allowed_lines = allowed_lines
@current_key = nil
@current_output = nil
end
@@ -35,7 +39,8 @@ class ZipService
end
def add_to_current_output entry
- return if is_spurious! entry.name
+ return if is_spurious!(entry.name) || is_foreign_line!(entry.name)
+
current_output.put_next_entry entry.name
write_to_current_output entry.get_input_stream
end
@@ -52,20 +57,22 @@ class ZipService
current_key,
# Second part of the solution, yield the closed stream
current_output.close_buffer,
- current_spurious)
+ current_spurious.to_a,
+ foreign_lines)
end
end
def open_new_output entry_key
@current_key = entry_key
# First piece of the solution, use internal way to create a Zip::OutputStream
- @current_output = Zip::OutputStream.new(StringIO.new(''), true, nil)
- @current_spurious = []
+ @current_output = Zip::OutputStream.new(StringIO.new(''), true, nil)
+ @current_spurious = Set.new
+ @foreign_lines = []
end
def entry_key entry
# last dir name File.dirname.split("/").last
- entry.name.split('/', -1)[-2]
+ entry.name.split('/').first
end
def is_spurious! entry_name
@@ -75,4 +82,12 @@ class ZipService
current_spurious << segments.second
return true
end
+
+ def is_foreign_line! entry_name
+ STIF::NetexFile::Frame.get_short_id(entry_name).tap do | line_object_id |
+ return nil unless line_object_id
+ return nil if line_object_id.in? allowed_lines
+ foreign_lines << line_object_id
+ end
+ end
end
diff --git a/app/uploaders/import_uploader.rb b/app/uploaders/import_uploader.rb
index 2740393ca..60e17ca0f 100644
--- a/app/uploaders/import_uploader.rb
+++ b/app/uploaders/import_uploader.rb
@@ -36,9 +36,9 @@ class ImportUploader < CarrierWave::Uploader::Base
# Add a white list of extensions which are allowed to be uploaded.
# For images you might use something like this:
- # def extension_whitelist
- # %w(jpg jpeg gif png)
- # end
+ def extension_whitelist
+ %w(zip)
+ end
# Override the filename of the uploaded files:
# Avoid using model.id or version_name here, see uploader/store.rb for details.
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/autocomplete_stop_areas/index.rabl b/app/views/autocomplete_stop_areas/index.rabl
index 5a9f76a47..d20051ad5 100644
--- a/app/views/autocomplete_stop_areas/index.rabl
+++ b/app/views/autocomplete_stop_areas/index.rabl
@@ -14,7 +14,8 @@ node do |stop_area|
:longitude => stop_area.longitude,
:latitude => stop_area.latitude,
:area_type => stop_area.area_type,
- :comment => stop_area.comment
+ :comment => stop_area.comment,
+ :text => stop_area.full_name
}
end
diff --git a/app/views/companies/index.html.slim b/app/views/companies/index.html.slim
index 5d746642f..e031f3776 100644
--- a/app/views/companies/index.html.slim
+++ b/app/views/companies/index.html.slim
@@ -34,7 +34,7 @@
end \
) \
],
- links: [:show, :edit],
+ links: [:show],
cls: 'table has-search'
= new_pagination @companies, 'pull-right'
diff --git a/app/views/compliance_check_sets/index.html.slim b/app/views/compliance_check_sets/index.html.slim
index f5d1bd777..f109845b4 100644
--- a/app/views/compliance_check_sets/index.html.slim
+++ b/app/views/compliance_check_sets/index.html.slim
@@ -12,7 +12,7 @@
[ \
TableBuilderHelper::Column.new( \
key: :ref, \
- attribute: 'compliance_check_set_id' \
+ attribute: 'id' \
), \
TableBuilderHelper::Column.new( \
key: :creation_date, \
@@ -41,5 +41,3 @@
.row.mt-xs
.col-lg-12
= replacement_msg t('compliance_check_sets.search_no_results')
-
-
diff --git a/app/views/compliance_check_sets/show.html.slim b/app/views/compliance_check_sets/show.html.slim
index c9d0583a5..5d8e3fa15 100644
--- a/app/views/compliance_check_sets/show.html.slim
+++ b/app/views/compliance_check_sets/show.html.slim
@@ -1,11 +1,6 @@
- breadcrumb :compliance_check_sets, @workbench, @compliance_check_set
/ PageHeader
-= pageheader 'jeux-de-donnees',
- @compliance_check_set.name,
- '',
- t('last_update', time: l(@compliance_check_set.updated_at, format: :short)) do
-
- / Below is secundary actions & optional contents (filters, ...)
+- content_for :page_header_content do
.row
.col-lg-12.text-right.mb-sm
- @compliance_check_set.action_links.each do |link|
@@ -13,7 +8,23 @@
method: link.method,
data: link.data,
class: 'btn btn-primary' do
- = link.content
+ = link.content
+
+- page_header_content_for @compliance_check_set
+/ = pageheader 'jeux-de-donnees',
+/ @compliance_check_set.name,
+/ '',
+/ t('last_update', time: l(@compliance_check_set.updated_at, format: :short)) do
+
+ / Below is secundary actions & optional contents (filters, ...)
+ / .row
+ / .col-lg-12.text-right.mb-sm
+ / - @compliance_check_set.action_links.each do |link|
+ / = link_to link.href,
+ / method: link.method,
+ / data: link.data,
+ / class: 'btn btn-primary' do
+ / = link.content
/ PageContent
.page_content.import_messages
diff --git a/app/views/compliance_control_sets/index.html.slim b/app/views/compliance_control_sets/index.html.slim
index 2a5651280..28d2254bf 100644
--- a/app/views/compliance_control_sets/index.html.slim
+++ b/app/views/compliance_control_sets/index.html.slim
@@ -30,7 +30,7 @@
), \
TableBuilderHelper::Column.new( \
key: :control_numbers, \
- attribute: 'control_numbers' \
+ attribute: Proc.new {|n| n.compliance_controls.count }\
), \
TableBuilderHelper::Column.new( \
key: :updated_at, \
diff --git a/app/views/compliance_control_sets/show.html.slim b/app/views/compliance_control_sets/show.html.slim
index e6416fda4..d915bbdaf 100644
--- a/app/views/compliance_control_sets/show.html.slim
+++ b/app/views/compliance_control_sets/show.html.slim
@@ -26,82 +26,23 @@
.col-lg-12
= render '/compliance_controls/filters'
+ / compliance controls without block
+ = render_compliance_control_block
+ = render_compliance_controls(@direct_compliance_controls)
- - if @direct_compliance_controls.try(:any?)
- .row
- .col-lg-12
- h2
- = transport_mode_text()
- .row
- .col-lg-12
- .select_table
- = table_builder_2 @direct_compliance_controls,
- [ \
- TableBuilderHelper::Column.new( \
- key: :code, \
- attribute: 'code' \
- ), \
- TableBuilderHelper::Column.new( \
- key: :name, \
- attribute: 'name', \
- link_to: lambda do |compliance_control| \
- compliance_control_set_compliance_control_path(@compliance_control_set, compliance_control) \
- end \
- ), \
- TableBuilderHelper::Column.new( \
- key: :criticity, \
- attribute: 'criticity' \
- ), \
- TableBuilderHelper::Column.new( \
- key: :comment, \
- attribute: 'comment' \
- ), \
- ],
- sortable: true,
- cls: 'table has-filter has-search',
- model: ComplianceControl
-
+ / compliance controls with block
+ - if params[:q] && params[:q][:compliance_control_block_id_eq_any].try(:present?)
- @blocks_to_compliance_controls_map.each do |block, compliance_controls|
+ = render_compliance_control_block(block)
+ = render_compliance_controls(compliance_controls)
+ - else
+ - @compliance_control_set.compliance_control_blocks.each do |block|
+ = render_compliance_control_block(block)
+ = render_compliance_controls(@blocks_to_compliance_controls_map[block])
- - if compliance_controls.try(:any?)
- .row
- .col-lg-12
- h2
- = transport_mode_text(block)
- .btn-group
- .btn.dropdown-toggle{ data-toggle="dropdown" }
- .span.fa.fa-cog
- ul.dropdown-menu
- li
- = link_to t('compliance_control_sets.actions.edit'), edit_compliance_control_set_compliance_control_block_path(@compliance_control_set.id, block.id)
- = link_to t('compliance_control_sets.actions.destroy'), compliance_control_set_compliance_control_block_path(@compliance_control_set.id, block.id), :method => :delete, :data => {:confirm => t('compliance_control_sets.actions.destroy_confirm')}
- .row
- .col-lg-12
- .select_table
- = table_builder_2 compliance_controls,
- [ \
- TableBuilderHelper::Column.new( \
- key: :code, \
- attribute: 'code' \
- ), \
- TableBuilderHelper::Column.new( \
- key: :name, \
- attribute: 'name', \
- link_to: lambda do |compliance_control| \
- compliance_control_set_compliance_control_path(@compliance_control_set, compliance_control) \
- end \
- ), \
- TableBuilderHelper::Column.new( \
- key: :criticity, \
- attribute: 'criticity' \
- ), \
- TableBuilderHelper::Column.new( \
- key: :comment, \
- attribute: 'comment' \
- ), \
- ],
- sortable: true,
- cls: 'table has-filter has-search',
- model: ComplianceControl
+ - if params[:q].present? && !@blocks_to_compliance_controls_map.try(:any?) && @direct_compliance_controls.nil?
+ .row.mt-xs
+ .col-lg-12
+ = replacement_msg t('compliance_controls.search_no_results')
- = flotted_links @compliance_control_set.id
+ = floating_links @compliance_control_set.id
diff --git a/app/views/dashboards/_dashboard.html.slim b/app/views/dashboards/_dashboard.html.slim
index f03301e23..075b94ddc 100644
--- a/app/views/dashboards/_dashboard.html.slim
+++ b/app/views/dashboards/_dashboard.html.slim
@@ -5,7 +5,7 @@
.panel-heading
h3.panel-title.with_actions
div
- = workbench.name
+ = link_to workbench.name, workbench_path(workbench)
span.badge.ml-xs = workbench.referentials.count if workbench.referentials.present?
div
@@ -22,7 +22,7 @@
.panel.panel-default
.panel-heading
h3.panel-title.with_actions
- = "Modèles de calendrier"
+ = link_to I18n.t("activerecord.models.calendar", count: @dashboard.current_organisation.calendars.size), calendars_path
div
= link_to '', calendars_path, class: ' fa fa-chevron-right pull-right'
- if @dashboard.current_organisation.calendars.present?
@@ -31,21 +31,24 @@
= link_to calendar.name, calendar_path(calendar), class: 'list-group-item'
- else
.panel-body
- em.small.text-muted Aucun modèle de calendrier défini
+ em.small.text-muted
+ = t('dasboard.calendars.none')
.col-lg-6.col-md-6.col-sm-6.col-xs-12
.panel.panel-default
- .panel-heading
- h3.panel-title
- = "Référentiels d'arrêts"
- .list-group
- - @dashboard.current_organisation.stop_area_referentials.each do |referential|
- = link_to referential.name, stop_area_referential_stop_areas_path(referential), class: 'list-group-item'
+ - @dashboard.current_organisation.stop_area_referentials.each do |referential|
+ .panel-heading
+ h3.panel-title
+ = referential.name
+ .list-group
+ = link_to Chouette::StopArea.model_name.human.pluralize.capitalize, stop_area_referential_stop_areas_path(referential), class: 'list-group-item'
.panel.panel-default
- .panel-heading
- h3.panel-title
- = "Référentiels de lignes"
- .list-group
- - @dashboard.current_organisation.line_referentials.all.each do |referential|
- = link_to referential.name, line_referential_lines_path(referential), class: 'list-group-item'
+ - @dashboard.current_organisation.line_referentials.all.each do |referential|
+ .panel-heading
+ h3.panel-title
+ = referential.name
+ .list-group
+ = link_to Chouette::Line.model_name.human.pluralize.capitalize, line_referential_lines_path(referential), class: 'list-group-item'
+ = link_to Chouette::Company.model_name.human.pluralize.capitalize, line_referential_companies_path(referential), class: 'list-group-item'
+ = link_to "Réseaux", line_referential_networks_path(referential), class: 'list-group-item'
diff --git a/app/views/devise/invitations/edit.html.slim b/app/views/devise/invitations/edit.html.slim
index 6c9a6f436..7a22146c0 100644
--- a/app/views/devise/invitations/edit.html.slim
+++ b/app/views/devise/invitations/edit.html.slim
@@ -1,14 +1,19 @@
-.col-md-offset-2.col-md-8
- .panel.panel-default
- .panel-heading = t('devise.invitations.edit.header')
-
- .panel-body
- = simple_form_for resource, as: resource_name, :url => invitation_path(resource_name), :html => { :method => :put, class: "form-horizontal" } do |form|
- = form.hidden_field :invitation_token
-
- = form.input :name
- = form.input :password, as: :password
- = form.input :password_confirmation, as: :password
-
- .submit
- = form.button :submit, value: t('devise.invitations.edit.submit_button'), class: 'btn-info' \ No newline at end of file
+/ PageHeader
+
+- content_for :page_header_title, t('.title')
+
+/ 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
+ = simple_form_for resource, as: resource_name, :url => invitation_path(resource_name), :html => { :method => :put, class: "form-horizontal", id: 'invitation_form' } do |form|
+ .row
+ .col-lg-12
+ = form.hidden_field :invitation_token
+
+ = form.input :name
+ = form.input :password, as: :password
+ = form.input :password_confirmation, as: :password
+
+ = form.button :submit, value: t('devise.invitations.edit.submit_button'), class: 'btn-info btn-default formSubmitr', form: 'invitation_form'
diff --git a/app/views/devise/mailer/invitation_instructions.fr.html.slim b/app/views/devise/mailer/invitation_instructions.fr.html.slim
index 484e0d68d..e263e188a 100644
--- a/app/views/devise/mailer/invitation_instructions.fr.html.slim
+++ b/app/views/devise/mailer/invitation_instructions.fr.html.slim
@@ -1,7 +1,7 @@
-p = t("devise.mailer.invitation_instructions.hello", email: @resource.email)
+p = t("devise.mailer.invitation_instructions.hello", email: @resource.name)
p = t("devise.mailer.invitation_instructions.someone_invited_you", url: unauthenticated_root_url)
p = link_to t("devise.mailer.invitation_instructions.accept"), accept_invitation_url(@resource, :invitation_token => @token)
-p = t("devise.mailer.invitation_instructions.ignore").html_safe \ No newline at end of file
+p = t("devise.mailer.invitation_instructions.ignore").html_safe
diff --git a/app/views/devise/passwords/edit.html.slim b/app/views/devise/passwords/edit.html.slim
index 864a44499..0d18f657c 100644
--- a/app/views/devise/passwords/edit.html.slim
+++ b/app/views/devise/passwords/edit.html.slim
@@ -1,13 +1,17 @@
-.col-md-offset-2.col-md-8
- .panel.panel-default
- .panel-heading = t('.title')
- .panel-body
- = simple_form_for(resource, :as => resource_name, :url => password_path(resource_name), :html => { :method => :put, class: "form-horizontal" }) do |f|
+/ PageHeader
- = f.input :reset_password_token, as: :hidden
- = f.input :password, as: :password
- = f.input :password_confirmation, as: :password
-
- .form-actions
- = link_to t("cancel"), unauthenticated_root_path, class: 'btn btn-default'
- = f.button :submit, :value => t("devise.passwords.edit.commit"), class: 'btn-info' \ No newline at end of file
+- content_for :page_header_title, t('.title')
+
+/ 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
+ = simple_form_for(resource, :as => resource_name, :url => password_path(resource_name), :html => { :method => :put, class: "form-horizontal", id: 'password_form' }) do |f|
+ .row
+ .col-lg-12
+ = f.input :reset_password_token, as: :hidden
+ = f.input :password, as: :password
+ = f.input :password_confirmation, as: :password
+
+ = f.button :submit, :value => t("devise.passwords.edit.commit"), class: 'btn btn-default formSubmitr', form: 'password_form'
diff --git a/app/views/devise/passwords/new.html.slim b/app/views/devise/passwords/new.html.slim
index 8a739ecc1..303f78f0e 100644
--- a/app/views/devise/passwords/new.html.slim
+++ b/app/views/devise/passwords/new.html.slim
@@ -1,11 +1,15 @@
-.col-md-offset-2.col-md-8
- .panel.panel-default
- .panel-heading = t('.title')
-
- .panel-body
- = simple_form_for(resource, :as => resource_name, :url => password_path(resource_name), html: {class: 'form-horizontal' } ) do |form|
- = form.input :email, :as => :email, placeholder: 'user@domain.com'
+/ PageHeader
- .form-actions
- = link_to t("cancel"), unauthenticated_root_path, class: 'btn btn-default'
- = form.button :submit, :value => t("devise.passwords.new.commit"), class: 'btn-info' \ No newline at end of file
+- content_for :page_header_title, t('.title')
+
+/ 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
+ = simple_form_for(resource, :as => resource_name, :url => password_path(resource_name), html: {class: 'form-horizontal', id: 'password_form' } ) do |form|
+ .row
+ .col-lg-12
+ = form.input :email, :as => :email, placeholder: 'user@domain.com'
+
+ = form.button :submit, :value => t("devise.passwords.new.commit"), class: 'btn btn-default formSubmitr', form: 'password_form'
diff --git a/app/views/devise/sessions/new.html.slim b/app/views/devise/sessions/new.html.slim
index 0ed17e24a..a812726e5 100644
--- a/app/views/devise/sessions/new.html.slim
+++ b/app/views/devise/sessions/new.html.slim
@@ -1,3 +1,5 @@
+- content_for :page_header_title, t('.title')
+
.page_content#devise
.container-fluid
#sessions_new.row
diff --git a/app/views/import_resources/index.html.slim b/app/views/import_resources/index.html.slim
index 1c4a5a765..728d9f4a8 100644
--- a/app/views/import_resources/index.html.slim
+++ b/app/views/import_resources/index.html.slim
@@ -1,4 +1,4 @@
-
+- breadcrumb :import_resources, @import, @import_resources
.page_content.import_messages
.container-fluid
.row
diff --git a/app/views/imports/_import_messages.html.slim b/app/views/imports/_import_messages.html.slim
new file mode 100644
index 000000000..a10dce065
--- /dev/null
+++ b/app/views/imports/_import_messages.html.slim
@@ -0,0 +1,11 @@
+- if import_messages.any?
+ .import_message-list
+ .import_messages-head Messages
+ dl
+ - import_messages.each do | import_message |
+ dt.criticity
+ = import_message.criticity
+ dd
+ = t( ['import_messages',
+ 'compliance_check_messages',
+ import_message.message_key].join('.'), import_message.message_attributes.symbolize_keys)
diff --git a/app/views/imports/show.html.slim b/app/views/imports/show.html.slim
index 5e22e03e0..cf137867b 100644
--- a/app/views/imports/show.html.slim
+++ b/app/views/imports/show.html.slim
@@ -19,46 +19,52 @@
.row
.col-lg-12
- = table_builder_2 @import.children,
- [ \
- TableBuilderHelper::Column.new( \
- name: 'Nom du jeu de données', \
- attribute: 'name', \
- sortable: false, \
- link_to: lambda do |import| \
- referential_path(import.referential) if import.referential.present? \
- end \
- ), \
- TableBuilderHelper::Column.new( \
- key: :status, \
- attribute: Proc.new { |n| import_status(n.status) }, \
- sortable: false, \
- link_to: lambda do |import| \
- workbench_import_import_resources_path(import.workbench_id, import) \
- end \
- ), \
- TableBuilderHelper::Column.new( \
- name: 'Contrôle STIF', \
- attribute: '', \
- sortable: false, \
- ), \
- TableBuilderHelper::Column.new( \
- name: 'Contrôle organisation', \
- attribute: '', \
- sortable: false, \
- ) \
- ],
- links: [],
- cls: 'table',
- overhead: [ \
- {}, \
- { \
- title: "#{@import.children_succeedeed} jeu de données validé sur #{@import.children.count} présent(s) dans l'archive", \
- width: 1, \
- cls: "#{@import.import_status_css_class} full-border" \
- }, { \
- title: 'Bilan des jeux de contrôles d\'import <span title="Lorem ipsum..." class="fa fa-lg fa-info-circle text-info"></span>', \
- width: 2, \
- cls: 'overheaded-default colspan="2"' \
- } \
- ]
+ .error_messages
+ = render 'import_messages', import_messages: @import.messages
+
+ - if @import.children.any?
+ .row
+ .col-lg-12
+ = table_builder_2 @import.children,
+ [ \
+ TableBuilderHelper::Column.new( \
+ name: 'Nom du jeu de données', \
+ attribute: 'name', \
+ sortable: false, \
+ link_to: lambda do |import| \
+ referential_path(import.referential) if import.referential.present? \
+ end \
+ ), \
+ TableBuilderHelper::Column.new( \
+ key: :status, \
+ attribute: Proc.new { |n| import_status(n.status) }, \
+ sortable: false, \
+ link_to: lambda do |import| \
+ workbench_import_import_resources_path(import.workbench_id, import) \
+ end \
+ ), \
+ TableBuilderHelper::Column.new( \
+ name: 'Contrôle STIF', \
+ attribute: '', \
+ sortable: false, \
+ ), \
+ TableBuilderHelper::Column.new( \
+ name: 'Contrôle organisation', \
+ attribute: '', \
+ sortable: false, \
+ ) \
+ ],
+ links: [],
+ cls: 'table',
+ overhead: [ \
+ {}, \
+ { \
+ title: "#{@import.children_succeedeed} jeu de données validé sur #{@import.children.count} présent(s) dans l'archive", \
+ width: 1, \
+ cls: "#{@import.import_status_css_class} full-border" \
+ }, { \
+ title: 'Bilan des jeux de contrôles d\'import <span title="Lorem ipsum..." class="fa fa-lg fa-info-circle text-info"></span>', \
+ width: 2, \
+ cls: 'overheaded-default colspan="2"' \
+ } \
+ ]
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/layouts/mailer.html.erb b/app/views/layouts/mailer.html.erb
new file mode 100644
index 000000000..977cbfe5b
--- /dev/null
+++ b/app/views/layouts/mailer.html.erb
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+ <style>
+ /* Email styles need to be inline */
+ body {
+ font-family: "Open Sans", sans-serif;
+ font-size: 14px;
+ line-height: 1.4;
+ color: #4b4b4b;
+ width: 800px;
+ }
+ </style>
+ </head>
+
+ <body>
+ <%= image_tag 'mail-header.png' %>
+
+ <%= yield %>
+ </body>
+</html>
diff --git a/app/views/layouts/mailer.html.slim b/app/views/layouts/mailer.html.slim
deleted file mode 100644
index 5db6917b3..000000000
--- a/app/views/layouts/mailer.html.slim
+++ /dev/null
@@ -1,7 +0,0 @@
-doctype html
-html
- head
- meta charset="utf-8"
- /!* Email styles need to be inline */
- body
- = yield
diff --git a/app/views/layouts/navigation/_main_nav_top.html.slim b/app/views/layouts/navigation/_main_nav_top.html.slim
index d6c849d3f..278249e09 100644
--- a/app/views/layouts/navigation/_main_nav_top.html.slim
+++ b/app/views/layouts/navigation/_main_nav_top.html.slim
@@ -1,5 +1,5 @@
.nav-menu#menu_top
- .brandname = t('brandname')
+ .brandname = link_to t('brandname'), root_path
.menu-content
.menu-item
@@ -11,7 +11,7 @@
span.fa.fa-lg.fa-tasks
= link_to '#', class: 'menu-item', data: { panel: 'toggle', target: '#profile_panel' }, title: 'Profil' do
- span = current_user.username
+ span = current_user.name
span.fa.fa-lg.fa-user
diff --git a/app/views/line_referentials/show.html.slim b/app/views/line_referentials/show.html.slim
index b4b32bc52..763eb076e 100644
--- a/app/views/line_referentials/show.html.slim
+++ b/app/views/line_referentials/show.html.slim
@@ -1,7 +1,8 @@
- breadcrumb :line_referential, @line_referential
- page_header_content_for @line_referential
-- content_for :page_header_actions do
- = link_to(t('actions.sync'), sync_line_referential_path(@line_referential), method: :post, class: 'btn btn-default')
+- if policy(@line_referential).synchronize?
+ - content_for :page_header_actions do
+ = link_to(t('actions.sync'), sync_line_referential_path(@line_referential), method: :post, class: 'btn btn-default')
- content_for :page_header_content do
.row.mb-md
diff --git a/app/views/lines/_form.html.slim b/app/views/lines/_form.html.slim
index 4952b72ff..909d6512e 100644
--- a/app/views/lines/_form.html.slim
+++ b/app/views/lines/_form.html.slim
@@ -3,7 +3,8 @@
.col-lg-12
= f.input :name
= f.input :network_id, as: :select, :collection => @line_referential.networks, include_blank: false
- = f.input :company_id, as: :select, :collection => @line_referential.companies, include_blank: false
+ = f.input :company_id, as: :select, :collection => @line_referential.companies, include_blank: true
+ = f.input :secondary_company_ids, :collection => @line_referential.companies, include_blank: false, input_html: { multiple: true, 'data-select2ed': true }, label: t('activerecord.attributes.line.secondary_company')
= f.input :published_name
= f.input :registration_number
= f.input :number
@@ -19,4 +20,3 @@
.separator
= f.button :submit, t('actions.submit'), class: 'btn btn-default formSubmitr', form: 'lines_form'
-
diff --git a/app/views/lines/index.html.slim b/app/views/lines/index.html.slim
index 8b035b477..b62263263 100644
--- a/app/views/lines/index.html.slim
+++ b/app/views/lines/index.html.slim
@@ -1,6 +1,6 @@
- breadcrumb :lines, @line_referential
- content_for :page_header_actions do
- - if (policy(Chouette::Line).create? && @line_referential.organisations.include?(current_organisation))
+ - if policy(Chouette::Line).create?
= link_to(t('lines.actions.new'), new_line_referential_line_path(@line_referential), class: 'btn btn-primary')
.page_content
@@ -41,7 +41,7 @@
), \
TableBuilderHelper::Column.new( \
key: 'companies.name', \
- attribute: Proc.new { |n| n.try(:company).try(:name) } \
+ attribute: Proc.new { |n| n&.company&.name || "-" } \
), \
TableBuilderHelper::Column.new( \
key: :transport_mode, \
diff --git a/app/views/lines/show.html.slim b/app/views/lines/show.html.slim
index 4969ca3cd..83244f739 100644
--- a/app/views/lines/show.html.slim
+++ b/app/views/lines/show.html.slim
@@ -6,7 +6,7 @@
= link_to link.href,
method: link.method,
data: link.data,
- class: 'btn btn-primary' do
+ class: "btn btn-primary #{link.disabled ? "disabled" : ""}" do
= link.content
- page_header_content_for @line
@@ -20,7 +20,7 @@
'Activé' => (@line.deactivated? ? t('false') : t('true')),
@line.human_attribute_name(:network_id) => (@line.network.nil? ? t('lines.index.unset') : @line.network.name),
@line.human_attribute_name(:company_id) => (@line.company.nil? ? t('lines.index.unset') : @line.company.name),
- 'Transporteur(s) secondaire(s)' => (@line.secondary_companies.nil? ? t('lines.index.unset') : @line.secondary_companies.collect(&:name).join(', ')),
+ 'Transporteur(s) secondaire(s)' => (@line.secondary_companies.nil? ? t('lines.index.unset') : array_to_html_list(@line.secondary_companies.collect(&:name))),
'Nom court' => @line.number,
'Code public' => (@line.registration_number ? @line.registration_number : '-'),
@line.human_attribute_name(:transport_mode) => (@line.transport_mode.present? ? t("enumerize.transport_mode.#{@line.transport_mode}") : '-'),
diff --git a/app/views/purchase_windows/_date_value_fields.html.slim b/app/views/purchase_windows/_date_value_fields.html.slim
new file mode 100644
index 000000000..7bde06a94
--- /dev/null
+++ b/app/views/purchase_windows/_date_value_fields.html.slim
@@ -0,0 +1,13 @@
+.nested-fields
+ - if f.object.errors.has_key? :base
+ .row
+ .col-lg-12
+ .alert.alert-danger
+ - f.object.errors[:base].each do |message|
+ p.small = message
+
+ .wrapper
+ div
+ = f.input :value, as: :date, label: false, wrapper_html: { class: 'date smart_date' }
+ div
+ = link_to_remove_association '', f, class: 'fa fa-trash', data: { confirm: 'Etes-vous sûr(e) ?' }, title: t('actions.delete')
diff --git a/app/views/purchase_windows/_filters.html.slim b/app/views/purchase_windows/_filters.html.slim
new file mode 100644
index 000000000..4d7c8ce26
--- /dev/null
+++ b/app/views/purchase_windows/_filters.html.slim
@@ -0,0 +1,15 @@
+= search_form_for @q, url: referential_purchase_windows_path, builder: SimpleForm::FormBuilder, html: { method: :get, class: 'form form-filter' } do |f|
+ .ffg-row
+ .input-group.search_bar
+ = f.search_field :name_cont, class: 'form-control', placeholder: t('purchase_windows.index.filter_placeholder')
+ span.input-group-btn
+ button.btn.btn-default#search_btn type='submit'
+ span.fa.fa-search
+
+ .form-group
+ = f.label Chouette::PurchaseWindow.human_attribute_name(:date), class: 'control-label'
+ = f.input :contains_date, as: :date, label: false, wrapper_html: { class: 'date smart_date' }, class: 'form-control', default: @date, include_blank: @date ? false : true
+
+ .actions
+ = link_to t('actions.erase'), referential_purchase_windows_path, class: 'btn btn-link'
+ = f.submit t('actions.filter'), id: 'purchase_window_filter_btn', class: 'btn btn-default'
diff --git a/app/views/purchase_windows/_form.html.slim b/app/views/purchase_windows/_form.html.slim
new file mode 100644
index 000000000..2101ae6db
--- /dev/null
+++ b/app/views/purchase_windows/_form.html.slim
@@ -0,0 +1,29 @@
+= simple_form_for [@referential, @purchase_window], html: { class: 'form-horizontal', id: 'purchase_window_form' }, wrapper: :horizontal_form do |f|
+ .row
+ .col-lg-12
+ = f.input :name
+ = f.input :color, as: :color_select, collection: Chouette::PurchaseWindow.colors_i18n
+
+ .separator
+
+ .row
+ .col-lg-12
+ .subform
+ .nested-head
+ .wrapper
+ div
+ .form-group
+ label.control-label
+ = t('simple_form.labels.purchase_window.ranges.begin')
+ div
+ .form-group
+ label.control-label
+ = t('simple_form.labels.purchase_window.ranges.end')
+ div
+
+ = f.simple_fields_for :periods do |period|
+ = render 'period_fields', f: period
+ .links.nested-linker
+ = link_to_add_association t('simple_form.labels.purchase_window.add_a_date_range'), f, :periods, class: 'btn btn-outline-primary'
+
+ = f.button :submit, t('actions.submit'), class: 'btn btn-default formSubmitr', form: 'purchase_window_form'
diff --git a/app/views/purchase_windows/_period_fields.html.slim b/app/views/purchase_windows/_period_fields.html.slim
new file mode 100644
index 000000000..95e204554
--- /dev/null
+++ b/app/views/purchase_windows/_period_fields.html.slim
@@ -0,0 +1,15 @@
+.nested-fields
+ - if f.object.errors.has_key? :base
+ .row
+ .col-lg-12
+ .alert.alert-danger
+ - f.object.errors[:base].each do |message|
+ p.small = message
+
+ .wrapper
+ div
+ = f.input :begin, as: :date, label: false, wrapper_html: { class: 'date smart_date' }
+ div
+ = f.input :end, as: :date, label: false, wrapper_html: { class: 'date smart_date' }
+ div
+ = link_to_remove_association '', f, class: 'fa fa-trash', data: { confirm: 'Etes-vous sûr(e) ?' }, title: t('actions.delete')
diff --git a/app/views/purchase_windows/edit.html.slim b/app/views/purchase_windows/edit.html.slim
new file mode 100644
index 000000000..6354db853
--- /dev/null
+++ b/app/views/purchase_windows/edit.html.slim
@@ -0,0 +1,7 @@
+- breadcrumb :purchase_window, @referential, @purchase_window
+- page_header_content_for @purchase_window
+.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/purchase_windows/index.html.slim b/app/views/purchase_windows/index.html.slim
new file mode 100644
index 000000000..04f9fb0a8
--- /dev/null
+++ b/app/views/purchase_windows/index.html.slim
@@ -0,0 +1,45 @@
+- breadcrumb :purchase_windows, @referential
+- content_for :page_header_actions do
+ - if policy(Chouette::PurchaseWindow).create?
+ = link_to(t('actions.add'), new_referential_purchase_window_path(@referential), class: 'btn btn-default')
+
+.page_content
+ .container-fluid
+ - if params[:q].present? or @purchase_windows.any?
+ .row
+ .col-lg-12
+ = render 'filters'
+
+ - if @purchase_windows.any?
+ .row
+ .col-lg-12
+ = table_builder_2 @purchase_windows,
+ [ \
+ TableBuilderHelper::Column.new( \
+ key: :name, \
+ attribute: 'name', \
+ link_to: lambda do |purchase_window| \
+ referential_purchase_window_path(purchase_window.referential, purchase_window) \
+ end \
+ ), \
+ TableBuilderHelper::Column.new( \
+ key: :color, \
+ attribute: Proc.new { |tt| tt.color ? content_tag(:span, '', class: 'fa fa-circle', style: "color:#{tt.color}") : '-' }\
+ ), \
+ TableBuilderHelper::Column.new( \
+ key: :bounding_dates, \
+ attribute: Proc.new {|w| w.bounding_dates.nil? ? '-' : t('validity_range', debut: l(w.bounding_dates.begin, format: :short), end: l(w.bounding_dates.end, format: :short))}, \
+ sortable: false \
+ ) \
+ ],
+ links: [:show],
+ cls: 'table has-filter'
+
+ = new_pagination @purchase_windows, 'pull-right'
+
+ - unless @purchase_windows.any?
+ .row.mt-xs
+ .col-lg-12
+ = replacement_msg t('purchase_windows.search_no_results')
+
+= javascript_pack_tag 'date_filters'
diff --git a/app/views/purchase_windows/new.html.slim b/app/views/purchase_windows/new.html.slim
new file mode 100644
index 000000000..402084167
--- /dev/null
+++ b/app/views/purchase_windows/new.html.slim
@@ -0,0 +1,6 @@
+- breadcrumb :purchase_windows, @referential
+.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/purchase_windows/show.html.slim b/app/views/purchase_windows/show.html.slim
new file mode 100644
index 000000000..9f3abb267
--- /dev/null
+++ b/app/views/purchase_windows/show.html.slim
@@ -0,0 +1,20 @@
+- breadcrumb :purchase_window, @referential, @purchase_window
+- page_header_content_for @purchase_window
+- content_for :page_header_content do
+ .row.mb-sm
+ .col-lg-12.text-right
+ - @purchase_window.action_links.each do |link|
+ = link_to link.href,
+ method: link.method,
+ data: link.data,
+ class: 'btn btn-primary' do
+ = link.content
+
+.page_content
+ .container-fluid
+ .row
+ .col-lg-6.col-md-6.col-sm-12.col-xs-12
+ = definition_list t('metadatas'),
+ { Chouette::PurchaseWindow.human_attribute_name(:name) => @purchase_window.try(:name),
+ 'Organisation' => @purchase_window.referential.organisation.name,
+ Chouette::PurchaseWindow.human_attribute_name(:date_ranges) => @purchase_window.periods.map{|d| t('validity_range', debut: l(d.begin, format: :short), end: l(d.end, format: :short))}.join('<br>').html_safe }
diff --git a/app/views/referential_companies/index.html.slim b/app/views/referential_companies/index.html.slim
index de0f7de69..07de2bc9d 100644
--- a/app/views/referential_companies/index.html.slim
+++ b/app/views/referential_companies/index.html.slim
@@ -46,7 +46,7 @@
attribute: 'url' \
) \
],
- links: [:show, :edit],
+ links: [:show],
cls: 'table has-search'
= new_pagination @companies, 'pull-right'
diff --git a/app/views/referential_vehicle_journeys/_filters.html.slim b/app/views/referential_vehicle_journeys/_filters.html.slim
new file mode 100644
index 000000000..963da8cea
--- /dev/null
+++ b/app/views/referential_vehicle_journeys/_filters.html.slim
@@ -0,0 +1,11 @@
+= search_form_for @q, url: referential_vehicle_journeys_path(@referential), html: {method: :get}, class: 'form form-filter' do |f|
+ .ffg-row
+ .input-group.search_bar
+ = f.search_field :published_journey_name_or_objectid_cont, placeholder: t('.published_journey_name_or_objectid'), class: 'form-control'
+ span.input-group-btn
+ button.btn.btn-default#search-btn type='submit'
+ span.fa.fa-search
+
+ .actions
+ = link_to 'Effacer', referential_vehicle_journeys_path(@referential), class: 'btn btn-link'
+ = f.submit 'Filtrer', class: 'btn btn-default'
diff --git a/app/views/referential_vehicle_journeys/index.html.slim b/app/views/referential_vehicle_journeys/index.html.slim
new file mode 100644
index 000000000..394f4a3f7
--- /dev/null
+++ b/app/views/referential_vehicle_journeys/index.html.slim
@@ -0,0 +1,58 @@
+- breadcrumb :referential_vehicle_journeys, @referential
+- content_for :page_header_title, t('.title')
+
+.page_content
+ .container-fluid
+ - if params[:q].present? or @vehicle_journeys.present?
+ .row
+ .col-lg-12
+ = render 'filters'
+
+ - if @vehicle_journeys.present?
+ .row
+ .col-lg-12
+ .select_table
+ = table_builder_2 @vehicle_journeys,
+ [ \
+ TableBuilderHelper::Column.new( \
+ name: t('objectid'), \
+ attribute: Proc.new { |n| n.get_objectid.short_id }, \
+ sortable: false \
+ ), \
+ TableBuilderHelper::Column.new( \
+ key: :published_journey_name, \
+ attribute: 'published_journey_name', \
+ link_to: lambda do |vehicle_journey| \
+ referential_line_route_vehicle_journeys_path(@referential, vehicle_journey.route.line, vehicle_journey.route) \
+ end, \
+ sortable: false \
+ ), \
+ TableBuilderHelper::Column.new( \
+ key: :line, \
+ attribute: Proc.new {|v| v.route.line.name}, \
+ sortable: false \
+ ), \
+ TableBuilderHelper::Column.new( \
+ key: :route, \
+ attribute: Proc.new {|v| v.route.name}, \
+ sortable: false \
+ ), \
+ TableBuilderHelper::Column.new( \
+ key: :departure_time, \
+ attribute: Proc.new {|v| v.vehicle_journey_at_stops.first&.departure }, \
+ sortable: false \
+ ), \
+ TableBuilderHelper::Column.new( \
+ key: :arrival_time, \
+ attribute: Proc.new {|v| v.vehicle_journey_at_stops.last&.arrival }, \
+ sortable: false \
+ ), \
+ ],
+ cls: 'table has-filter has-search'
+
+ = new_pagination @vehicle_journeys, 'pull-right'
+
+ - unless @vehicle_journeys.any?
+ .row.mt-xs
+ .col-lg-12
+ = replacement_msg t('.search_no_results')
diff --git a/app/views/referentials/_filters.html.slim b/app/views/referentials/_filters.html.slim
index c5b6042f0..93fa679df 100644
--- a/app/views/referentials/_filters.html.slim
+++ b/app/views/referentials/_filters.html.slim
@@ -12,11 +12,11 @@
= f.input :transport_mode_eq_any, collection: @referential.lines.pluck(:transport_mode).uniq.compact, as: :check_boxes, label: false, label_method: lambda{|l| ("<span>" + t("enumerize.transport_mode.#{l}") + "</span>").html_safe}, required: false, wrapper_html: { class: 'checkbox_list' }
.form-group.togglable
- = f.label Chouette::Line.human_attribute_name(:network), required: false, class: 'control-label'
+ = f.label t('activerecord.attributes.referential.networks'), required: false, class: 'control-label'
= f.input :network_id_eq_any, collection: LineReferential.first.networks.order('name').pluck(:id), as: :check_boxes, label: false, label_method: lambda{|l| ("<span>#{LineReferential.first.networks.find(l).name}</span>").html_safe}, required: false, wrapper_html: { class: 'checkbox_list' }
.form-group.togglable
- = f.label Chouette::Line.human_attribute_name(:company), required: false, class: 'control-label'
+ = f.label t('activerecord.attributes.referential.companies'), required: false, class: 'control-label'
= f.input :company_id_eq_any, collection: LineReferential.first.companies.order('name').pluck(:id), as: :check_boxes, label: false, label_method: lambda{|l| ("<span>#{LineReferential.first.companies.find(l).name}</span>").html_safe}, required: false, wrapper_html: { class: 'checkbox_list' }
.actions
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/referentials/new.html.slim b/app/views/referentials/new.html.slim
index 2bed9f912..13d58ee71 100644
--- a/app/views/referentials/new.html.slim
+++ b/app/views/referentials/new.html.slim
@@ -1,3 +1,5 @@
+- breadcrumb :referentials
+
.page_content
.container-fluid
.row
diff --git a/app/views/referentials/show.html.slim b/app/views/referentials/show.html.slim
index 9852fb0a3..96755359c 100644
--- a/app/views/referentials/show.html.slim
+++ b/app/views/referentials/show.html.slim
@@ -68,7 +68,7 @@
), \
TableBuilderHelper::Column.new( \
key: 'companies.name', \
- attribute: Proc.new { |n| n.try(:company).try(:name) } \
+ attribute: Proc.new { |n| n&.company&.name || "-" } \
) \
],
links: [:show],
diff --git a/app/views/routes/show.html.slim b/app/views/routes/show.html.slim
index 3adf3e2f6..2b4ebf159 100644
--- a/app/views/routes/show.html.slim
+++ b/app/views/routes/show.html.slim
@@ -40,7 +40,7 @@
), \
TableBuilderHelper::Column.new( \
key: :name, \
- attribute: Proc.new {|s| s.try(:stop_area).try(:name)}, \
+ attribute: Proc.new { |s| content_tag :span, s.stop_area&.name, class: s.stop_area&.area_type }, \
link_to: lambda do |stop_point| \
referential_stop_area_path(@referential, stop_point.stop_area) \
end \
diff --git a/app/views/stop_area_referentials/show.html.slim b/app/views/stop_area_referentials/show.html.slim
index d43333fd9..911006c39 100644
--- a/app/views/stop_area_referentials/show.html.slim
+++ b/app/views/stop_area_referentials/show.html.slim
@@ -1,13 +1,14 @@
- breadcrumb :stop_area_referential, @stop_area_referential
-- content_for :page_header_actions do
- = link_to(t('actions.sync'), sync_stop_area_referential_path(@stop_area_referential), method: :post, class: 'btn btn-default')
+- if policy(@stop_area_referential).synchronize?
+ - content_for :page_header_actions do
+ = link_to(t('actions.sync'), sync_stop_area_referential_path(@stop_area_referential), method: :post, class: 'btn btn-default')
- content_for :page_header_content do
.row.mb-md
.col-lg-12.text-right
= link_to stop_area_referential_stop_areas_path(@stop_area_referential), class: 'btn btn-primary' do
= Referential.human_attribute_name(:stop_areas)
- em.small = " (#{@stop_area_referential.stop_areas.size})"
+ em.small = " (#{@stop_area_referential.stop_areas.count})"
- page_header_content_for @stop_area_referential
.page_content
diff --git a/app/views/stop_areas/_filters.html.slim b/app/views/stop_areas/_filters.html.slim
index 3b99f377c..90368dff4 100644
--- a/app/views/stop_areas/_filters.html.slim
+++ b/app/views/stop_areas/_filters.html.slim
@@ -12,7 +12,7 @@
.form-group.togglable
= f.label Chouette::StopArea.human_attribute_name(:area_type), required: false, class: 'control-label'
- = f.input :area_type_eq_any, collection: Chouette::StopArea.area_type.options.sort, as: :check_boxes, label: false, label_method: lambda{|w| ("<span>" + t("enumerize.stop_area.area_type.#{w[1]}") + "</span>").html_safe}, required: false, wrapper_html: { class: 'checkbox_list' }
+ = f.input :area_type_eq_any, collection: Chouette::AreaType.options, as: :check_boxes, label: false, label_method: lambda{|w| ("<span>" + w[0] + "</span>").html_safe}, required: false, wrapper_html: { class: 'checkbox_list' }
.actions
= link_to 'Effacer', @workbench, class: 'btn btn-link'
diff --git a/app/views/stop_areas/_form.html.slim b/app/views/stop_areas/_form.html.slim
index 20c7c0468..af8b9d889 100644
--- a/app/views/stop_areas/_form.html.slim
+++ b/app/views/stop_areas/_form.html.slim
@@ -6,7 +6,10 @@
/= @map.to_html
= f.input :id, as: :hidden
= f.input :name, :input_html => {:title => t("formtastic.titles#{format_restriction_for_locales(@referential)}.stop_area.name")}
- = f.input :area_type, as: :select, :input_html => {:disabled => !@stop_area.new_record?}, :collection => Chouette::StopArea.area_type.options, :include_blank => false
+
+ = f.input :parent_id, as: :select, :collection => [f.object.parent_id], input_html: { data: { select2_ajax: 'true', url: autocomplete_stop_area_referential_stop_areas_path(@stop_area_referential), initvalue: {id: f.object.parent_id, text: f.object.parent.try(:full_name)}}}
+
+ = f.input :area_type, as: :select, :input_html => {:disabled => !@stop_area.new_record?}, :collection => Chouette::AreaType.options, :include_blank => false
.location_info
h3 = t("stop_areas.stop_area.localisation")
@@ -19,13 +22,16 @@
= f.input :coordinates, :input_html => {:title => t("formtastic.titles#{format_restriction_for_locales(@referential)}.stop_area.coordinates")}, required: true
= f.input :street_name
- /= f.input :country_code, required: format_restriction_for_locales(@referential) == '.hub'
= f.input :zip_code, :input_html => {:title => t("formtastic.titles#{format_restriction_for_locales(@referential)}.stop_area.zip_code")}
= f.input :city_name, required: format_restriction_for_locales(@referential) == '.hub', :input_html => {:title => t("formtastic.titles#{format_restriction_for_locales(@referential)}.stop_area.city_name")}
+ = f.input :country_code, as: :country, priority: ['FR', 'GB', 'DE', 'ES'], :include_blank => true
.stop_areas.stop_area.general_info
h3 = t("stop_areas.stop_area.general")
+ - if has_feature?(:stop_area_waiting_time)
+ = f.input :waiting_time
+
= f.input :registration_number, required: format_restriction_for_locales(@referential) == '.hub', :input_html => {:title => t("formtastic.titles#{format_restriction_for_locales(@referential)}.stop_area.registration_number")}
= f.input :fare_code
= f.input :nearest_topic_name, :input_html => {:title => t("formtastic.titles#{format_restriction_for_locales(@referential)}.stop_area.nearest_topic_name")}
diff --git a/app/views/stop_areas/autocomplete.rabl b/app/views/stop_areas/autocomplete.rabl
new file mode 100644
index 000000000..3208289b5
--- /dev/null
+++ b/app/views/stop_areas/autocomplete.rabl
@@ -0,0 +1,24 @@
+collection @stop_areas
+
+node do |stop_area|
+ {
+ :id => stop_area.id,
+ :registration_number => stop_area.registration_number || "",
+ :short_registration_number => truncate(stop_area.registration_number, :length => 10) || "",
+ :name => stop_area.name || "",
+ :short_name => truncate(stop_area.name, :length => 30) || "",
+ :zip_code => stop_area.zip_code || "",
+ :city_name => stop_area.city_name || "",
+ :short_city_name => truncate(stop_area.city_name, :length => 15) || "",
+ :user_objectid => stop_area.user_objectid,
+ :longitude => stop_area.longitude,
+ :latitude => stop_area.latitude,
+ :area_type => stop_area.area_type,
+ :comment => stop_area.comment,
+ :text => stop_area.full_name
+ }
+end
+
+node(:stop_area_path) { |stop_area|
+ stop_area_picture_url(stop_area) || ""
+}
diff --git a/app/views/stop_areas/index.html.slim b/app/views/stop_areas/index.html.slim
index c4d880081..63e99fd75 100644
--- a/app/views/stop_areas/index.html.slim
+++ b/app/views/stop_areas/index.html.slim
@@ -24,7 +24,7 @@
key: :name, \
attribute: 'name', \
link_to: lambda do |stop_area| \
- referential_stop_area_path( \
+ stop_area_referential_stop_area_path( \
@stop_area_referential, \
stop_area \
) \
@@ -48,10 +48,10 @@
), \
TableBuilderHelper::Column.new( \
key: :area_type, \
- attribute: Proc.new { |s| (s.area_type.nil? ? '-' : t("enumerize.stop_area.area_type.#{s.try(:area_type)}")) } \
+ attribute: Proc.new { |s| Chouette::AreaType.find(s.area_type).try :label } \
), \
],
- links: [:show, :edit, :delete],
+ links: [:show],
cls: 'table has-filter has-search'
= new_pagination @stop_areas, 'pull-right'
diff --git a/app/views/stop_areas/show.html.slim b/app/views/stop_areas/show.html.slim
index af673bb25..f9de34a98 100644
--- a/app/views/stop_areas/show.html.slim
+++ b/app/views/stop_areas/show.html.slim
@@ -15,12 +15,17 @@
.container-fluid
.row
.col-lg-6.col-md-6.col-sm-12.col-xs-12
- = definition_list t('metadatas'),
- { t('id_reflex') => @stop_area.get_objectid.short_id,
- @stop_area.human_attribute_name(:stop_area_type) => t("area_types.label.#{@stop_area.stop_area_type}"),
+ - attributes = { t('id_reflex') => @stop_area.get_objectid.short_id,
+ @stop_area.human_attribute_name(:parent) => @stop_area.parent ? link_to(@stop_area.parent.name, stop_area_referential_stop_area_path(@stop_area_referential, @stop_area.parent)) : "-",
+ @stop_area.human_attribute_name(:stop_area_type) => Chouette::AreaType.find(@stop_area.area_type).try(:label),
@stop_area.human_attribute_name(:registration_number) => @stop_area.registration_number,
- 'Coordonnées' => geo_data(@stop_area, @stop_area_referential),
+ }
+ - attributes.merge!(@stop_area.human_attribute_name(:waiting_time) => @stop_area.waiting_time_text) if has_feature?(:stop_area_waiting_time)
+ - attributes.merge!({ "Coordonnées" => geo_data(@stop_area, @stop_area_referential),
@stop_area.human_attribute_name(:zip_code) => @stop_area.zip_code,
@stop_area.human_attribute_name(:city_name) => @stop_area.city_name,
+ @stop_area.human_attribute_name(:country_code) => @stop_area.country_code.presence || '-',
'Etat' => (@stop_area.deleted_at ? 'Supprimé' : 'Actif'),
- @stop_area.human_attribute_name(:comment) => @stop_area.try(:comment) }
+ @stop_area.human_attribute_name(:comment) => @stop_area.try(:comment),
+ })
+ = definition_list t('metadatas'), attributes
diff --git a/app/views/stop_points/_stop_point.html.slim b/app/views/stop_points/_stop_point.html.slim
index ca86e339a..e54158cef 100644
--- a/app/views/stop_points/_stop_point.html.slim
+++ b/app/views/stop_points/_stop_point.html.slim
@@ -5,7 +5,7 @@
= link_to [@referential, stop_point.stop_area], class: "preview", title: "#{Chouette::StopArea.model_name.human.capitalize} #{stop_point.stop_area.name}" do
span.name
span.label.label-primary = stop_point.position + 1
- = image_tag "map/" + stop_point.stop_area.stop_area_type + ".png"
+ = image_tag "map/" + stop_point.stop_area.area_type + ".png"
= truncate(stop_point.stop_area.name, length: 20)
.panel-body
@@ -27,4 +27,4 @@
= t(".no_object")
- else
- stop_point.stop_area.lines.each do |line|
- span.label.label-default.line = line.number || truncate( line.name, length: 4 ) \ No newline at end of file
+ span.label.label-default.line = line.number || truncate( line.name, length: 4 )
diff --git a/app/views/vehicle_journeys/index.html.slim b/app/views/vehicle_journeys/index.html.slim
index 52c1a9728..ebcac8197 100644
--- a/app/views/vehicle_journeys/index.html.slim
+++ b/app/views/vehicle_journeys/index.html.slim
@@ -1,5 +1,11 @@
- breadcrumb :vehicle_journeys, @referential, @route
- content_for :page_header_title, t('vehicle_journeys.index.title', route: @route.name)
+- if @route.opposite_route.present?
+ - 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 sticky-action')
+
.page_content
.container-fluid
@@ -19,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..01175a85d 100644
--- a/app/views/vehicle_journeys/show.rabl
+++ b/app/views/vehicle_journeys/show.rabl
@@ -1,6 +1,6 @@
object @vehicle_journey
-[:objectid, :published_journey_name, :published_journey_identifier, :company_id].each do |attr|
+[:objectid, :published_journey_name, :published_journey_identifier, :company_id, :comment].each do |attr|
attributes attr, :unless => lambda { |m| m.send( attr).nil?}
end
@@ -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'),
diff --git a/app/views/workbenches/show.html.slim b/app/views/workbenches/show.html.slim
index 22869b2d7..1c82c34b7 100644
--- a/app/views/workbenches/show.html.slim
+++ b/app/views/workbenches/show.html.slim
@@ -57,11 +57,11 @@
attribute: '' \
) \
],
- selectable: true,
+ selectable: ->(ref){ @workbench.referentials.include?(ref) },
links: [:show, :edit],
cls: 'table has-filter has-search'
- = multiple_selection_toolbox([:delete])
+ = multiple_selection_toolbox([:delete], collection_name: 'referentials')
= new_pagination @wbench_refs, 'pull-right'
diff --git a/app/workers/referential_cloning_worker.rb b/app/workers/referential_cloning_worker.rb
index 6592160ec..e20148055 100644
--- a/app/workers/referential_cloning_worker.rb
+++ b/app/workers/referential_cloning_worker.rb
@@ -1,32 +1,7 @@
class ReferentialCloningWorker
include Sidekiq::Worker
- # Replace default apartment created schema with clone schema from source referential
def perform(id)
- ref_cloning = ReferentialCloning.find id
-
- source_schema = ref_cloning.source_referential.slug
- target_schema = ref_cloning.target_referential.slug
-
- clone_schema ref_cloning, source_schema, target_schema
- end
-
- private
-
- def clone_schema ref_cloning, source_schema, target_schema
- ref_cloning.run!
-
- AF83::SchemaCloner
- .new(source_schema, target_schema)
- .clone_schema
-
- ref_cloning.successful!
- rescue Exception => e
- Rails.logger.error "ReferentialCloningWorker : #{e}"
- ref_cloning.failed!
- end
-
- def execute_sql sql
- ActiveRecord::Base.connection.execute sql
+ ReferentialCloning.find(id).clone!
end
end
diff --git a/app/workers/workbench_import_worker.rb b/app/workers/workbench_import_worker.rb
index de51efded..6420be835 100644
--- a/app/workers/workbench_import_worker.rb
+++ b/app/workers/workbench_import_worker.rb
@@ -3,29 +3,25 @@ class WorkbenchImportWorker
include Rails.application.routes.url_helpers
include Configurable
+ include ObjectStateUpdater
+
+ attr_reader :entries, :workbench_import
+
# Workers
# =======
def perform(import_id)
- @workbench_import = WorkbenchImport.find(import_id)
- @response = nil
- @workbench_import.update(status: 'running', started_at: Time.now)
- downloaded = download
- zip_service = ZipService.new(downloaded)
+ @entries = 0
+ @workbench_import ||= WorkbenchImport.find(import_id)
+
+ workbench_import.update(status: 'running', started_at: Time.now)
+ zip_service = ZipService.new(downloaded, allowed_lines)
upload zip_service
- @workbench_import.update(ended_at: Time.now)
+ workbench_import.update(ended_at: Time.now)
rescue Zip::Error
handle_corrupt_zip_file
end
- def download
- logger.info "HTTP GET #{import_url}"
- HTTPService.get_resource(
- host: import_host,
- path: import_path,
- params: {token: @workbench_import.token_download}).body
- end
-
def execute_post eg_name, eg_file
logger.info "HTTP POST #{export_url} (for #{complete_entry_group_name(eg_name)})"
HTTPService.post_resource(
@@ -35,48 +31,43 @@ class WorkbenchImportWorker
end
def handle_corrupt_zip_file
- @workbench_import.messages.create(criticity: :error, message_key: 'corrupt_zip_file', message_attributes: {source_filename: @workbench_import.file.file.file})
+ workbench_import.messages.create(criticity: :error, message_key: 'corrupt_zip_file', message_attributes: {source_filename: workbench_import.file.file.file})
end
def upload zip_service
entry_group_streams = zip_service.subdirs
- @workbench_import.update total_steps: entry_group_streams.size
entry_group_streams.each_with_index(&method(:upload_entry_group))
+ workbench_import.update total_steps: @entries
rescue Exception => e
logger.error e.message
- @workbench_import.update( current_step: entry_group_streams.size, status: 'failed' )
+ workbench_import.update( current_step: @entries, status: 'failed' )
raise
end
- def update_object_state entry, count
- @workbench_import.update( current_step: count )
- unless entry.spurious.empty?
- @workbench_import.messages.create(
- criticity: :warning,
- message_key: 'inconsistent_zip_file',
- message_attributes: {
- 'source_filename' => @workbench_import.file.file.file,
- 'spurious_dirs' => entry.spurious.join(', ')
- })
- end
- end
def upload_entry_group entry, element_count
update_object_state entry, element_count.succ
+ return unless entry.ok?
# status = retry_service.execute(&upload_entry_group_proc(entry))
- eg_name = entry.name
- eg_stream = entry.stream
+ upload_entry_group_stream entry.name, entry.stream
+ end
+ def upload_entry_group_stream eg_name, eg_stream
FileUtils.mkdir_p(Rails.root.join('tmp', 'imports'))
- eg_file = File.new(Rails.root.join('tmp', 'imports', "WorkbenchImport_#{eg_name}_#{$$}.zip"), 'wb').tap do |file|
+ File.open(Rails.root.join('tmp', 'imports', "WorkbenchImport_#{eg_name}_#{$$}.zip"), 'wb') do |file|
eg_stream.rewind
file.write eg_stream.read
end
- eg_file.close
- eg_file = File.new(Rails.root.join('tmp', 'imports', "WorkbenchImport_#{eg_name}_#{$$}.zip"))
+
+ upload_entry_group_tmpfile eg_name, File.new(Rails.root.join('tmp', 'imports', "WorkbenchImport_#{eg_name}_#{$$}.zip"))
+ end
+
+ def upload_entry_group_tmpfile eg_name, eg_file
result = execute_post eg_name, eg_file
if result && result.status < 400
+ @entries += 1
+ workbench_import.update( current_step: @entries )
result
else
raise StopIteration, result.body
@@ -91,7 +82,7 @@ class WorkbenchImportWorker
# =======
def complete_entry_group_name entry_group_name
- [@workbench_import.name, entry_group_name].join("--")
+ [workbench_import.name, entry_group_name].join("--")
end
# Constants
@@ -111,7 +102,7 @@ class WorkbenchImportWorker
Rails.application.config.rails_host
end
def import_path
- @__import_path__ ||= download_workbench_import_path(@workbench_import.workbench, @workbench_import)
+ @__import_path__ ||= download_workbench_import_path(workbench_import.workbench, workbench_import)
end
def import_url
@__import_url__ ||= File.join(import_host, import_path)
@@ -119,10 +110,29 @@ class WorkbenchImportWorker
def params file, name
{ netex_import:
- { parent_id: @workbench_import.id,
- parent_type: @workbench_import.class.name,
- workbench_id: @workbench_import.workbench_id,
- name: name,
- file: HTTPService.upload(file, 'application/zip', "#{name}.zip") } }
+ { parent_id: workbench_import.id,
+ parent_type: workbench_import.class.name,
+ workbench_id: workbench_import.workbench_id,
+ name: name,
+ file: HTTPService.upload(file, 'application/zip', "#{name}.zip") } }
+ end
+
+ # Lazy Values
+ # ===========
+
+ def allowed_lines
+ @__allowed_lines__ ||= workbench_import.workbench.organisation.lines_set
end
+ def downloaded
+ @__downloaded__ ||= download_response.body
+ end
+ def download_response
+ @__download_response__ ||= HTTPService.get_resource(
+ host: import_host,
+ path: import_path,
+ params: {token: workbench_import.token_download}).tap do
+ logger.info "HTTP GET #{import_url}"
+ end
+ end
+
end
diff --git a/app/workers/workbench_import_worker/object_state_updater.rb b/app/workers/workbench_import_worker/object_state_updater.rb
new file mode 100644
index 000000000..67bdc0654
--- /dev/null
+++ b/app/workers/workbench_import_worker/object_state_updater.rb
@@ -0,0 +1,36 @@
+
+class WorkbenchImportWorker
+ module ObjectStateUpdater
+
+ def update_object_state entry, count
+ workbench_import.update( total_steps: count )
+ update_spurious entry
+ update_foreign_lines entry
+ end
+
+
+ private
+
+ def update_foreign_lines entry
+ return if entry.foreign_lines.empty?
+ workbench_import.messages.create(
+ criticity: :error,
+ message_key: 'foreign_lines_in_referential',
+ message_attributes: {
+ 'source_filename' => workbench_import.file.file.file,
+ 'foreign_lines' => entry.foreign_lines.join(', ')
+ })
+ end
+
+ def update_spurious entry
+ return if entry.spurious.empty?
+ workbench_import.messages.create(
+ criticity: :error,
+ message_key: 'inconsistent_zip_file',
+ message_attributes: {
+ 'source_filename' => workbench_import.file.file.file,
+ 'spurious_dirs' => entry.spurious.join(', ')
+ })
+ end
+ end
+end