diff options
| author | Alban Peignier | 2018-05-10 22:17:03 +0200 |
|---|---|---|
| committer | GitHub | 2018-05-10 22:17:03 +0200 |
| commit | 764aa8a93bd3e25a83b3db70892ca06304edaa9c (patch) | |
| tree | b681730cec81ee6b721e8148f3fbcab691c3689b | |
| parent | 093599f1c2e75ba2a4e3e9e8a6aa2cf665919886 (diff) | |
| parent | 3364755e26c5a4a54a6de6080bf1177ee03c7183 (diff) | |
| download | chouette-core-764aa8a93bd3e25a83b3db70892ca06304edaa9c.tar.bz2 | |
Merge pull request #555 from af83/6923-consolidated-offer
Display consolidated offer. Fixes #6923
| -rw-r--r-- | app/assets/stylesheets/components/_forms.sass | 8 | ||||
| -rw-r--r-- | app/assets/stylesheets/modules/_vj_collection.sass | 88 | ||||
| -rw-r--r-- | app/controllers/referential_vehicle_journeys_controller.rb | 4 | ||||
| -rw-r--r-- | app/helpers/vehicle_journeys_helper.rb | 12 | ||||
| -rw-r--r-- | app/models/chouette/route.rb | 3 | ||||
| -rw-r--r-- | app/services/referential_consolidated.rb | 124 | ||||
| -rw-r--r-- | app/views/referential_vehicle_journeys/_consolidated.html.slim | 33 | ||||
| -rw-r--r-- | app/views/referential_vehicle_journeys/_consolidated_line.html.slim | 119 | ||||
| -rw-r--r-- | app/views/referential_vehicle_journeys/_filters.html.slim | 14 | ||||
| -rw-r--r-- | app/views/referential_vehicle_journeys/_list.html.slim | 49 | ||||
| -rw-r--r-- | app/views/referential_vehicle_journeys/index.html.slim | 52 |
11 files changed, 451 insertions, 55 deletions
diff --git a/app/assets/stylesheets/components/_forms.sass b/app/assets/stylesheets/components/_forms.sass index b13c5fc23..e7aa31fab 100644 --- a/app/assets/stylesheets/components/_forms.sass +++ b/app/assets/stylesheets/components/_forms.sass @@ -507,10 +507,10 @@ table, .table right: 15px top: 50% padding: 0 - margin-top: -13px z-index: 1 + transform: translateY(-50%) - .btn + *:not(.btn-group) > .btn color: $blue font-weight: 700 background-color: transparent @@ -718,6 +718,10 @@ table, .table > .form-group:last-child border-right: none + @for $i from 1 through 99 + &.w#{$i} + display: inline-block + // Form group date .form-group.date .form-inline diff --git a/app/assets/stylesheets/modules/_vj_collection.sass b/app/assets/stylesheets/modules/_vj_collection.sass index 3ff0828ea..0131201f0 100644 --- a/app/assets/stylesheets/modules/_vj_collection.sass +++ b/app/assets/stylesheets/modules/_vj_collection.sass @@ -2,7 +2,7 @@ // VJ Collection // //-----------------// -#vehicle_journeys_wip +#vehicle_journeys_wip, .consolidated-view .table-2entries .t2e-head > .td @@ -218,3 +218,89 @@ // Reset default behaviour .form-control border-color: #ccc + + +.consolidated-view + $highlighted: #d4ba32 + .line + & > .head + font-size: 2em + text-transform: capitalize + border-top: 3px solid black + border-bottom: 3px solid black + padding-left: 10px + margin-top: 10px + .routes + .route + background: #F9F9F9 + & > .head + &.highlighted + .pull-right, span.fa + color: $highlighted !important + a + padding: 15px + color: black + font-size: 1.2em + text-transform: capitalize + display: block + text-decoration: none + &:hover + background-color: #F0F0F0 + .fa + color: $red + transition: transform 0.1s + &.active .fa + transform: rotate(180deg) + .pull-right + font-size: 0.9em + text-transform: lowercase + .vehicle-journeys + display: block + overflow: hidden + transition: height 0.5s, margin-bottom 0.5s + margin-bottom: 0px + & > * + display: inline-block + & > * + overflow: hidden + min-width: 0 + &.open + margin-bottom: 40px + + .highlighted + background-color: lighten($highlighted, 20%) !important + .disabled + background-color: #dedede + color: #777 + .t2e-item-list .t2e-item + .th + min-width: 100px + max-width: 150px + & > div + text-overflow: ellipsis + overflow: hidden + white-space: nowrap + .td + text-align: center + &:hover:after + position: absolute + height: 100% + bottom: 0 + left: -10000px + right: -10000px + content: "" + background-color: $red + opacity: 0.1 + z-index: 10 + &.headlined:hover:after + height: 50% + + + .table-2entries > .t2e-head > .td > div > span::after + bottom: -6px !important + + .table.table-2entries .t2e-item-list .t2e-item + background-color: #F9F9F9 + + .table.table-2entries .td > div.headlined::before + border-right: none diff --git a/app/controllers/referential_vehicle_journeys_controller.rb b/app/controllers/referential_vehicle_journeys_controller.rb index 14f7909b9..111d39c2b 100644 --- a/app/controllers/referential_vehicle_journeys_controller.rb +++ b/app/controllers/referential_vehicle_journeys_controller.rb @@ -42,9 +42,9 @@ class ReferentialVehicleJourneysController < ChouetteController @q = @q.ransack(params[:q]) @vehicle_journeys ||= @q.result @vehicle_journeys = parse_order @vehicle_journeys - @vehicle_journeys = @vehicle_journeys.paginate page: params[:page], per_page: params[:per_page] || 10 @all_companies = Chouette::Company.where("id IN (#{@referential.vehicle_journeys.select(:company_id).to_sql})").distinct - + @consolidated = ReferentialConsolidated.new @vehicle_journeys, params + @vehicle_journeys = @vehicle_journeys.paginate page: params[:page], per_page: params[:per_page] || 10 end def parse_order scope diff --git a/app/helpers/vehicle_journeys_helper.rb b/app/helpers/vehicle_journeys_helper.rb index 1cc865c62..4d7eb7002 100644 --- a/app/helpers/vehicle_journeys_helper.rb +++ b/app/helpers/vehicle_journeys_helper.rb @@ -69,4 +69,16 @@ module VehicleJourneysHelper ) end + def vehicle_journey_stop_headline prev_sp, sp + if has_feature?(:long_distance_routes) + headline = prev_sp && prev_sp.stop_area.country_code + headline = sp.stop_area.country_code != headline + headline && sp.stop_area.country_name + else + headline = prev_sp && prev_sp.stop_area.city_name + headline = sp.stop_area.city_name != headline + headline && sp.stop_area.city_name + end + end + end diff --git a/app/models/chouette/route.rb b/app/models/chouette/route.rb index 7a8d043e0..949b18d6f 100644 --- a/app/models/chouette/route.rb +++ b/app/models/chouette/route.rb @@ -72,6 +72,9 @@ module Chouette end end + has_many :time_tables, :through => :vehicle_journeys + has_many :purchase_windows, :through => :vehicle_journeys + accepts_nested_attributes_for :stop_points, :allow_destroy => :true validates_presence_of :name diff --git a/app/services/referential_consolidated.rb b/app/services/referential_consolidated.rb new file mode 100644 index 000000000..465eab405 --- /dev/null +++ b/app/services/referential_consolidated.rb @@ -0,0 +1,124 @@ +class ReferentialConsolidated + attr_reader :params + + def initialize vehicle_journeys, params + @vehicle_journeys = vehicle_journeys + @params = params + end + + def paginated_lines + @paginated_lines ||= begin + line_ids = @vehicle_journeys.joins(route: :line).pluck('lines.id') + lines = Chouette::Line.where(id: line_ids).order(:name) + lines.paginate page: params[:page], per_page: params[:per_page] || 10 + end + end + + def lines + @lines ||= paginated_lines.to_a.map {|l| Line.new(self, l, @vehicle_journeys, params) } + end + + def _should_highlight? + return false unless params[:q].present? + keys = params[:q].keys - ["stop_areas"] + params[:q].values_at(*keys).each do |value| + if value.is_a?(Hash) + return true if value.values.any?(&:present?) + elsif value.is_a?(Array) + return true if value.any?(&:present?) + else + if value.present? + return true + end + end + end + false + end + + def should_highlight? + if @should_highlight.nil? + @should_highlight = _should_highlight? + end + @should_highlight + end + + class Base + extend Forwardable + attr_reader :params + attr_reader :parent + attr_reader :ar_model + + def initialize parent, ar_model, vehicle_journeys, params + @parent = parent + @ar_model = ar_model + @all_vehicle_journeys = vehicle_journeys + @params = params + end + + def should_highlight? + parent.should_highlight? + end + end + + class Line < Base + delegate name: :ar_model + delegate id: :ar_model + + def routes + @routes ||= begin + ar_model.routes.order(:name).map {|r| Route.new(self, r, @all_vehicle_journeys, params) } + end + end + end + + class Route < Base + def_delegators :ar_model, :name, :id, :time_tables, :purchase_windows, :stop_area_ids + + def vehicle_journeys + @vehicle_journeys ||= begin + ar_model.vehicle_journeys.map {|vj| VehicleJourney.new(self, vj, @all_vehicle_journeys, params) } + end + end + + def highlighted_journeys + @all_vehicle_journeys.joins(:journey_pattern).where(route_id: self.id) + end + + def highlighted_count + highlighted_journeys.count + end + + def highlighted? + matching_stop_areas = params[:q] && params[:q]["stop_areas"] && (params[:q]["stop_areas"].values & self.stop_area_ids.map(&:to_s)).present? + (should_highlight? || matching_stop_areas) && highlighted_journeys.exists? + end + + def stop_points + @stop_points ||= ar_model.stop_points.map {|sp| StopPoint.new(self, sp, @all_vehicle_journeys, params) } + end + end + + class VehicleJourney < Base + def_delegators :ar_model, :id, :published_journey_name, :journey_pattern, :time_tables, :purchase_windows, :vehicle_journey_at_stops, :time_table_ids, :purchase_window_ids, :route + + def highlighted? + should_highlight? && @all_vehicle_journeys.where(id: self.id).exists? + end + + def has_purchase_window? purchase_window + purchase_window_ids.include?(purchase_window.id) + end + + def has_time_table? time_table + time_table_ids.include?(time_table.id) + end + end + + class StopPoint < Base + def_delegators :ar_model, :id, :arrival_time, :departure_time, :name, :stop_area, :stop_area_id + + def highlighted? + params[:q] && params[:q]["stop_areas"] && params[:q]["stop_areas"].values.any?{|v| v.to_s == stop_area_id.to_s} + end + end +end diff --git a/app/views/referential_vehicle_journeys/_consolidated.html.slim b/app/views/referential_vehicle_journeys/_consolidated.html.slim new file mode 100644 index 000000000..e2de526bc --- /dev/null +++ b/app/views/referential_vehicle_journeys/_consolidated.html.slim @@ -0,0 +1,33 @@ +.row.consolidated-view + - @consolidated.lines.each do |line| + = render partial: "consolidated_line", object: line + .col-md-12 + = new_pagination @consolidated.paginated_lines, 'pull-right' + +coffee: + $('a.toggle-timetables').click (e)-> + e.preventDefault() + $(e.target).toggleClass 'active' + $(e.target).parents('.table').find('.detailed-timetables').toggleClass 'hidden' + + $('a.toggle-purchase-windows').click (e)-> + e.preventDefault() + $(e.target).toggleClass 'active' + $(e.target).parents('.table').find('.detailed-purchase-windows').toggleClass 'hidden' + + $('.route').find('.vehicle-journeys').each (i, e)-> + $e = $(e) + $e.removeClass 'hidden' + e.setAttribute 'data-original-height', $e.height() + $e.height 0 + + $('a.toggle-route').click (e)-> + e.preventDefault() + $(e.currentTarget).toggleClass 'active' + tab = $(e.currentTarget).parents('.route').find('.vehicle-journeys') + if tab.hasClass 'open' + tab.removeClass 'open' + tab.height 0 + else + tab.addClass 'open' + tab.height tab.attr('data-original-height') diff --git a/app/views/referential_vehicle_journeys/_consolidated_line.html.slim b/app/views/referential_vehicle_journeys/_consolidated_line.html.slim new file mode 100644 index 000000000..d4c756d38 --- /dev/null +++ b/app/views/referential_vehicle_journeys/_consolidated_line.html.slim @@ -0,0 +1,119 @@ +.line.col-md-12 + .head + span + = Chouette::Line.ts + | + strong= consolidated_line.name + .routes + - consolidated_line.routes.each do |route| + .route + .head class="#{route.highlighted? ? 'highlighted' : ''}" + = link_to '#', class: 'toggle-route' do + span.sb.sb-route + | + = Chouette::Route.ts + | + strong= route.name + .pull-right + = route.highlighted_count + | + = Chouette::VehicleJourney.t + | + span.fa.fa-angle-up + .table.table-2entries.vehicle-journeys.hidden + .t2e-head.w20 + .th + div + strong= Chouette::VehicleJourney.tmf(:id) + div= Chouette::VehicleJourney.tmf(:name) + div= Chouette::VehicleJourney.tmf(:journey_pattern_id) + div + = link_to '#', class: 'toggle-purchase-windows detailed-timetables-bt' do + span.fa.fa-angle-up + = Chouette::PurchaseWindow.t + .detailed-purchase-windows.hidden + - route.purchase_windows.uniq.each do |tt| + div + p + = link_to [@referential, tt] do + span.fa.fa-calendar style={color: (tt.color ? tt.color : '#4B4B4B')} + | + = tt.name + + p= tt.bounding_dates.split(' ').join(' > ') + div + = link_to '#', class: 'toggle-timetables detailed-timetables-bt' do + span.fa.fa-angle-up + = Chouette::TimeTable.t + + .detailed-timetables.hidden + - route.time_tables.uniq.each do |tt| + div + p + = link_to [@referential, tt] do + span.fa.fa-calendar style={color: (tt.color ? tt.color : '#4B4B4B')} + | + = tt.display_day_types + + p= tt.bounding_dates.split(' ').join(' > ') + - prev_sp = nil + - route.stop_points.each do |sp| + ruby: + headline = vehicle_journey_stop_headline prev_sp, sp + prev_sp = sp + .td class="#{sp.highlighted? ? 'highlighted' : ''}" + div title="#{sp.stop_area.city_name ? "#{sp.stop_area.city_name} (#{sp.stop_area.zip_code})" : ''}" data-headline=headline class=(headline.present? ? 'headlined' : '') + span + = sp.name + - if sp.stop_area.time_zone_formatted_offset + span.small + | + = "(#{sp.stop_area.time_zone_formatted_offset})" + .t2e-item-list.w80 + div + - route.vehicle_journeys.each do |journey| + .t2e-item class="#{journey.highlighted? ? 'highlighted' : ''}" + .th + div + strong= link_to journey.id, [@referential, journey.route.line, journey.route, :vehicle_journeys] + div + = link_to journey.published_journey_name, [@referential, journey.route.line, journey.route, :vehicle_journeys], title: journey.published_journey_name + div= journey.journey_pattern.get_objectid.short_id + div + - journey.purchase_windows[0..3].each do |tt| + span.vj_tt + = link_to [@referential, tt], target: :blank do + span.fa.fa-calendar style="color: #{tt.color ? tt.color : '#4B4B4B'}" + - if journey.purchase_windows.size > 3 + span.vj_tt = "+ #{journey.purchase_windows.size - 3}" + .detailed-purchase-windows.hidden + - route.purchase_windows.uniq.each do |tt| + div class=(journey.has_purchase_window?(tt) ? 'active' : 'inactive') + div + - journey.time_tables[0..3].each do |tt| + span.vj_tt + = link_to [@referential, tt], target: :blank do + span.fa.fa-calendar style="color: #{tt.color ? tt.color : '#4B4B4B'}" + - if journey.time_tables.size > 3 + span.vj_tt = "+ #{journey.time_tables.size - 3}" + .detailed-timetables.hidden + - route.time_tables.uniq.each do |tt| + div class=(journey.has_time_table?(tt) ? 'active' : 'inactive') + + - prev_sp = nil + - route.stop_points.each do |sp| + ruby: + headline = vehicle_journey_stop_headline prev_sp, sp + prev_sp = sp + vjas = journey.vehicle_journey_at_stops.where(stop_point_id: sp.id).last + .td class="#{vjas && sp.highlighted? ? 'highlighted' : ''} #{vjas.nil? ? 'disabled' : ''} #{headline.present? ? 'headlined' : ''}" + div title="#{sp.stop_area.city_name ? "#{sp.stop_area.city_name} (#{sp.stop_area.zip_code})" : ''}" data-headline=headline class=(headline.present? ? 'headlined' : '') + - if vjas.present? + - if vjas.departure_time == vjas.arrival_time + = vjas.departure_time.l(format: "%H:%M") + - else + = vjas.arrival_time.l(format: "%H:%M") + | - + = vjas.departure_time.l(format: "%H:%M") + - else + | 00:00 diff --git a/app/views/referential_vehicle_journeys/_filters.html.slim b/app/views/referential_vehicle_journeys/_filters.html.slim index a6e289b97..31053c5ba 100644 --- a/app/views/referential_vehicle_journeys/_filters.html.slim +++ b/app/views/referential_vehicle_journeys/_filters.html.slim @@ -1,10 +1,20 @@ = search_form_for @q, url: referential_vehicle_journeys_path(@referential), html: {method: :get}, class: 'form form-filter' do |f| - .ffg-row + input type="hidden" name="display" value=params[:display] + .ffg-row.w85 .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 + .ffg-row.w15 + - if has_feature?(:consolidated_offers) + .form-group + .btn-group + = link_to referential_vehicle_journeys_path(@referential, q: params[:q], display: :list), class: 'btn btn-default ' + (params[:display] != "consolidated" ? 'active' : '') do + span.fa.fa-align-justify + = link_to referential_vehicle_journeys_path(@referential, q: params[:q], display: :consolidated), class: 'btn btn-default ' + (params[:display] == "consolidated" ? 'active' : '') do + span.fa.fa-th-large + .ffg-row .form-group.per-page-select = I18n.t("simple_form.per_page") @@ -73,5 +83,5 @@ .actions - = link_to t('actions.erase'), referential_vehicle_journeys_path(@referential), class: 'btn btn-link' + = link_to t('actions.erase'), referential_vehicle_journeys_path(@referential, display: params[:display]), class: 'btn btn-link' = f.submit t('actions.filter'), class: 'btn btn-default' diff --git a/app/views/referential_vehicle_journeys/_list.html.slim b/app/views/referential_vehicle_journeys/_list.html.slim new file mode 100644 index 000000000..74f8238f8 --- /dev/null +++ b/app/views/referential_vehicle_journeys/_list.html.slim @@ -0,0 +1,49 @@ +.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| \ + vehicle_journey.published_journey_name ? referential_line_route_vehicle_journeys_path(@referential, vehicle_journey.route.line, vehicle_journey.route) : '' \ + end, \ + sortable: true \ + ), \ + TableBuilderHelper::Column.new( \ + key: :line, \ + attribute: Proc.new {|v| v.route.line.name}, \ + sortable: true \ + ), \ + TableBuilderHelper::Column.new( \ + key: :route, \ + attribute: Proc.new {|v| v.route.name}, \ + sortable: true \ + ), \ + TableBuilderHelper::Column.new( \ + key: :departure_time, \ + attribute: Proc.new {|v| v.vehicle_journey_at_stops.first&.departure_local }, \ + sortable: true \ + ), \ + [@starting_stop, @ending_stop].compact.map{|stop| \ + TableBuilderHelper::Column.new( \ + attribute: Proc.new {|v| v.vehicle_journey_at_stops.where("stop_points.stop_area_id" => stop.id).last&.arrival_local }, \ + sortable: false, \ + name: stop.name \ + )\ + }, \ + TableBuilderHelper::Column.new( \ + key: :arrival_time, \ + attribute: Proc.new {|v| v.vehicle_journey_at_stops.last&.arrival_local }, \ + sortable: true, \ + ), \ + ].flatten.compact, + cls: 'table has-filter has-search' + + = new_pagination @vehicle_journeys, 'pull-right' diff --git a/app/views/referential_vehicle_journeys/index.html.slim b/app/views/referential_vehicle_journeys/index.html.slim index 00f63cb65..d1d1dae07 100644 --- a/app/views/referential_vehicle_journeys/index.html.slim +++ b/app/views/referential_vehicle_journeys/index.html.slim @@ -9,55 +9,11 @@ = 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| \ - vehicle_journey.published_journey_name ? referential_line_route_vehicle_journeys_path(@referential, vehicle_journey.route.line, vehicle_journey.route) : '' \ - end, \ - sortable: true \ - ), \ - TableBuilderHelper::Column.new( \ - key: :line, \ - attribute: Proc.new {|v| v.route.line.name}, \ - sortable: true \ - ), \ - TableBuilderHelper::Column.new( \ - key: :route, \ - attribute: Proc.new {|v| v.route.name}, \ - sortable: true \ - ), \ - TableBuilderHelper::Column.new( \ - key: :departure_time, \ - attribute: Proc.new {|v| v.vehicle_journey_at_stops.first&.departure_local }, \ - sortable: true \ - ), \ - [@starting_stop, @ending_stop].compact.map{|stop| \ - TableBuilderHelper::Column.new( \ - attribute: Proc.new {|v| v.vehicle_journey_at_stops.where("stop_points.stop_area_id" => stop.id).last&.arrival_local }, \ - sortable: false, \ - name: stop.name \ - )\ - }, \ - TableBuilderHelper::Column.new( \ - key: :arrival_time, \ - attribute: Proc.new {|v| v.vehicle_journey_at_stops.last&.arrival_local }, \ - sortable: true, \ - ), \ - ].flatten.compact, - cls: 'table has-filter has-search' + - if params[:display] == "consolidated" && has_feature?(:consolidated_offers) + = render partial: "consolidated" + - else + = render partial: "list" - = new_pagination @vehicle_journeys, 'pull-right' - unless @vehicle_journeys.any? .row.mt-xs |
