diff options
Diffstat (limited to 'app')
120 files changed, 1241 insertions, 463 deletions
diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index 4c5aff22f..6a79f7e8e 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -22,3 +22,6 @@ //= require_directory . // require('whatwg-fetch') // require('babel-polyfill') +//= require "i18n" +//= require "i18n/extended" +//= require "i18n/translations" diff --git a/app/assets/javascripts/i18n/extended.coffee b/app/assets/javascripts/i18n/extended.coffee new file mode 100644 index 000000000..aeb67bd09 --- /dev/null +++ b/app/assets/javascripts/i18n/extended.coffee @@ -0,0 +1,24 @@ +#= require i18n + +decorateI18n = (_i18n)-> + _i18n.tc = (key, opts={}) -> + out = _i18n.t(key, opts) + out += " " if _i18n.locale == "fr" + out + ":" + + _i18n.model_name = (model, opts={}) -> + last_key = if opts.plural then "other" else "one" + _i18n.t("activerecord.models.#{model}.#{last_key}") + + _i18n.attribute_name = (model, attribute, opts={}) -> + _i18n.t("activerecord.attributes.#{model}.#{attribute}") + + _i18n.enumerize = (enumerize, key, opts={}) -> + I18n.t("enumerize.#{enumerize}.#{key}") + + _i18n + +module?.exports = decorateI18n + +if I18n? + decorateI18n(I18n) diff --git a/app/assets/stylesheets/components/_tables.sass b/app/assets/stylesheets/components/_tables.sass index 35e1122f3..ef19bd538 100644 --- a/app/assets/stylesheets/components/_tables.sass +++ b/app/assets/stylesheets/components/_tables.sass @@ -9,7 +9,6 @@ font-weight: 700 border-bottom: 2px solid $darkgrey vertical-align: middle - > a position: relative display: block @@ -326,6 +325,26 @@ padding: 6px 8px border-bottom: 2px solid rgba($grey, 0.5) border-top: 1px solid rgba($grey, 0.5) + text-transform: capitalize + + .info-button + position: absolute + width: 20px + height: 20px + top: 0 + right: 0 + margin: 6px 8px + button + border: none + background: $blue + border-radius: 20px + width: 100% + height: 100% + font-size: 12px + line-height: 14px + color: white + outline: none + .td position: relative padding: 6px 8px @@ -355,6 +374,8 @@ .th text-align: right border-top-color: transparent + > div:not(.btn-group) + min-height: 20px .td > .headlined &:before @@ -389,7 +410,7 @@ .th > div:not(.btn-group) - min-height: 19px + min-height: 20px > *:first-child padding-right: 30px diff --git a/app/assets/stylesheets/modules/_vj_collection.sass b/app/assets/stylesheets/modules/_vj_collection.sass index d99c67bd7..d9079daa2 100644 --- a/app/assets/stylesheets/modules/_vj_collection.sass +++ b/app/assets/stylesheets/modules/_vj_collection.sass @@ -86,6 +86,20 @@ &:after bottom: 50% + + .table-2entries .t2e-head + .detailed-timetables + .fa + margin-right: 5px + .detailed-timetables-bt + text-decoration: none + .fa + margin-right: 5px + color: $red + transition: transform 0.1s + &.active .fa + transform: rotate(180deg) + .table-2entries .t2e-head > .td:nth-child(2) > div, .table-2entries .t2e-head > .td:last-child > div, .table-2entries.no_result .t2e-head > .td:last-child > div @@ -103,6 +117,54 @@ top: 50% margin-top: -8px + .detailed-timetables + padding-top: 10px + text-align: left + margin-bottom: -5px + + & > div + position: relative + border-left: 1px solid $lightgrey + padding-left: 10px + a + text-decoration: none + border: none + &:before + position: absolute + left: 0px + top: 0 + right: -8px + content: "" + border-top: 1px solid $lightgrey + font-size: 0.8em + height: 44px + position: relative + padding-bottom: 5px + + p + margin: 0 + p:first-child + padding-top: 8px + font-weight: bold + + .t2e-item-list .detailed-timetables > div + border-left: none + &:after + top: 50% + left: 50% + content: "" + border: 1px solid black + width: 20px + height: 20px + margin-left: -10px + margin-top: -10px + position: absolute + border-radius: 20px + &.active:after + background: black + &:before + left: -8px + // Errors .table-2entries .t2e-item-list .t2e-item diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index c5ecc505a..c4961123d 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -36,16 +36,6 @@ class ApplicationController < ActionController::Base end helper_method :current_organisation - def current_offer_workbench - current_organisation.workbenches.find_by_name("Gestion de l'offre") - end - helper_method :current_offer_workbench - - def current_workgroup - current_offer_workbench.workgroup - end - helper_method :current_workgroup - def current_functional_scope functional_scope = current_organisation.sso_attributes.try(:[], "functional_scope") if current_organisation JSON.parse(functional_scope) if functional_scope diff --git a/app/controllers/autocomplete_stop_areas_controller.rb b/app/controllers/autocomplete_stop_areas_controller.rb index d82fa316a..79154a6e0 100644 --- a/app/controllers/autocomplete_stop_areas_controller.rb +++ b/app/controllers/autocomplete_stop_areas_controller.rb @@ -21,7 +21,7 @@ class AutocompleteStopAreasController < ChouetteController scope = StopAreaPolicy::Scope.new(current_user, scope).search_scope(search_scope) end 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 = scope.where("unaccent(stop_areas.name) ILIKE unaccent(?) OR unaccent(stop_areas.city_name) ILIKE unaccent(?) OR stop_areas.registration_number ILIKE ? OR stop_areas.objectid ILIKE ?", *args).limit(50) @stop_areas end diff --git a/app/controllers/calendars_controller.rb b/app/controllers/calendars_controller.rb index 5cc7912b7..75d4cbd09 100644 --- a/app/controllers/calendars_controller.rb +++ b/app/controllers/calendars_controller.rb @@ -8,14 +8,21 @@ class CalendarsController < ChouetteController respond_to :json, only: :show respond_to :js, only: :index + belongs_to :workgroup + def index index! do - @calendars = CalendarDecorator.decorate(@calendars) + @calendars = decorate_calendars(@calendars) end end def show - @year = params[:year] ? params[:year].to_i : Date.today.cwyear + show! do + @year = params[:year] ? params[:year].to_i : Date.today.cwyear + @calendar = @calendar.decorate(context: { + workgroup: workgroup + }) + end end def month @@ -45,6 +52,16 @@ class CalendarsController < ChouetteController end private + + def decorate_calendars(calendars) + CalendarDecorator.decorate( + calendars, + context: { + workgroup: workgroup + } + ) + end + def calendar_params 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? @@ -60,25 +77,30 @@ class CalendarsController < ChouetteController end protected + + alias_method :workgroup, :parent + helper_method :workgroup + def resource - @calendar = Calendar.where('organisation_id = ? OR shared = true', current_organisation.id).find_by_id(params[:id]).decorate + @calendar ||= workgroup.calendars.where('(organisation_id = ? OR shared = ?)', current_organisation.id, true).find_by_id(params[:id]) end def build_resource super.tap do |calendar| + calendar.workgroup = workgroup calendar.organisation = current_organisation end end def collection - return @calendars if @calendars - scope = Calendar.where('organisation_id = ? OR shared = ?', current_organisation.id, true) - scope = shared_scope(scope) - @q = scope.ransack(params[:q]) - - calendars = @q.result - calendars = calendars.order(sort_column + ' ' + sort_direction) if sort_column && sort_direction - @calendars = calendars.paginate(page: params[:page]) + @calendars ||= begin + scope = workgroup.calendars.where('(organisation_id = ? OR shared = ?)', current_organisation.id, true) + scope = shared_scope(scope) + @q = scope.ransack(params[:q]) + calendars = @q.result + calendars = calendars.order(sort_column + ' ' + sort_direction) if sort_column && sort_direction + calendars = calendars.paginate(page: params[:page]) + end end def ransack_contains_date @@ -103,4 +125,4 @@ class CalendarsController < ChouetteController scope end -end +end
\ No newline at end of file diff --git a/app/controllers/compliance_control_sets_controller.rb b/app/controllers/compliance_control_sets_controller.rb index ae1d01feb..8f9251155 100644 --- a/app/controllers/compliance_control_sets_controller.rb +++ b/app/controllers/compliance_control_sets_controller.rb @@ -7,10 +7,8 @@ class ComplianceControlSetsController < ChouetteController def index index! do |format| - scope = self.ransack_period_range(scope: @compliance_control_sets, error_message: t('imports.filters.error_period_filter'), query: :where_updated_at_between) - @q_for_form = scope.ransack(params[:q]) format.html { - @compliance_control_sets = decorate_compliance_control_sets(@q_for_form.result.paginate(page: params[:page], per_page: 30)) + @compliance_control_sets = decorate_compliance_control_sets(@compliance_control_sets) } end end @@ -37,6 +35,14 @@ class ComplianceControlSetsController < ChouetteController private + def collection + scope = self.ransack_period_range(scope: ComplianceControlSet.all, error_message: t('imports.filters.error_period_filter'), query: :where_updated_at_between) + @q_for_form = scope.ransack(params[:q]) + compliance_control_sets = @q_for_form.result + compliance_control_sets = joins_with_associated_objects(compliance_control_sets).order(sort_column + ' ' + sort_direction) if sort_column && sort_direction + @compliance_control_sets = compliance_control_sets.paginate(page: params[:page], per_page: 30) + end + def decorate_compliance_control_sets(compliance_control_sets) ComplianceControlSetDecorator.decorate(compliance_control_sets) end @@ -58,4 +64,32 @@ class ComplianceControlSetsController < ChouetteController @direct_compliance_controls = compliance_controls.delete nil @blocks_to_compliance_controls_map = compliance_controls end + + def sort_column + case params[:sort] + when 'name' then 'lower(compliance_control_sets.name)' + when 'owner_jdc' then 'lower(organisations.name)' + when 'control_numbers' then 'COUNT(compliance_controls.id)' + else + ComplianceControlSet.column_names.include?(params[:sort]) ? params[:sort] : 'lower(compliance_control_sets.name)' + end + end + + def joins_with_associated_objects(collection) + + # dont know if this is the right way to do it but since we need to join table deoending of the params + # it was to avoid loading associated objects if we don't need them + case params[:sort] + when 'owner_jdc' + collection.joins("LEFT JOIN organisations ON compliance_control_sets.organisation_id = organisations.id") + when 'control_numbers' + collection.joins("LEFT JOIN compliance_controls ON compliance_controls.compliance_control_set_id = compliance_control_sets.id").group(:id) + else + collection + end + end + + def sort_direction + %w[asc desc].include?(params[:direction]) ? params[:direction] : 'asc' + end end diff --git a/app/controllers/referential_vehicle_journeys_controller.rb b/app/controllers/referential_vehicle_journeys_controller.rb index 2ce28a5cc..e36ef8153 100644 --- a/app/controllers/referential_vehicle_journeys_controller.rb +++ b/app/controllers/referential_vehicle_journeys_controller.rb @@ -20,11 +20,30 @@ class ReferentialVehicleJourneysController < ChouetteController @q = ransack_period_range(scope: @q, error_message: t('vehicle_journeys.errors.purchase_window'), query: :in_purchase_window, prefix: :purchase_window) @q = ransack_period_range(scope: @q, error_message: t('vehicle_journeys.errors.time_table'), query: :with_matching_timetable, prefix: :time_table) @q = @q.ransack(params[:q]) - @vehicle_journeys ||= @q.result.order(:published_journey_name).includes(:vehicle_journey_at_stops).paginate page: params[:page], per_page: params[:per_page] || 10 + @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 @all_stop_areas = Chouette::StopArea.where("id IN (#{@referential.vehicle_journeys.joins(:stop_areas).select("stop_areas.id").to_sql})").distinct stop_area_ids = params[:q].try(:[], :stop_area_ids).try(:select, &:present?) @filters_stop_areas = Chouette::StopArea.find(stop_area_ids) if stop_area_ids.present? && stop_area_ids.size <= 2 end + def parse_order scope + return scope.order(:published_journey_name) unless params[:sort].present? + direction = params[:direction] || "asc" + attributes = Chouette::VehicleJourney.column_names.map{|n| "vehicle_journeys.#{n}"}.join(',') + case params[:sort] + when "line" + scope.order("lines.name #{direction}").joins(route: :line) + when "route" + scope.order("routes.name #{direction}").joins(:route) + when "departure_time" + scope.joins(:vehicle_journey_at_stops).group(attributes).select(attributes).order("MIN(vehicle_journey_at_stops.departure_time) #{direction}") + when "arrival_time" + scope.joins(:vehicle_journey_at_stops).group(attributes).select(attributes).order("MAX(vehicle_journey_at_stops.departure_time) #{direction}") + else + scope.order "#{params[:sort]} #{direction}" + end + end end diff --git a/app/controllers/referentials_controller.rb b/app/controllers/referentials_controller.rb index 09ea35c26..6e3694547 100644 --- a/app/controllers/referentials_controller.rb +++ b/app/controllers/referentials_controller.rb @@ -1,5 +1,6 @@ class ReferentialsController < ChouetteController defaults :resource_class => Referential + before_action :load_workbench include PolicyChecker respond_to :html @@ -32,7 +33,7 @@ class ReferentialsController < ChouetteController def show resource.switch show! do |format| - @referential = @referential.decorate(context: { current_workbench_id: params[:current_workbench_id] } ) + @referential = @referential.decorate() @reflines = lines_collection.paginate(page: params[:page], per_page: 10) @reflines = ReferentialLineDecorator.decorate( @reflines, @@ -143,7 +144,6 @@ class ReferentialsController < ChouetteController if params[:from] source_referential = Referential.find(params[:from]) @referential = Referential.new_from(source_referential, current_functional_scope) - @referential.workbench_id = params[:current_workbench_id] end @referential.data_format = current_organisation.data_format @@ -183,4 +183,12 @@ class ReferentialsController < ChouetteController return user_not_authorized unless current_user.organisation.workgroups.include?(source.workbench.workgroup) end + def load_workbench + @workbench ||= Workbench.find(params[:workbench_id]) if params[:workbench_id] + @workbench ||= resource&.workbench if params[:id] + @workbench + end + + alias_method :current_workbench, :load_workbench + helper_method :current_workbench end diff --git a/app/controllers/snapshots_controller.rb b/app/controllers/snapshots_controller.rb new file mode 100644 index 000000000..e453b4965 --- /dev/null +++ b/app/controllers/snapshots_controller.rb @@ -0,0 +1,14 @@ +class SnapshotsController < ApplicationController + if Rails.env.development? || Rails.env.test? + layout :which_layout + def show + tpl = params[:snap] + tpl = tpl.gsub Rails.root.to_s, '' + render file: tpl + end + + def which_layout + "snapshots/#{params[:layout] || "default"}" + end + end +end diff --git a/app/controllers/statuses_controller.rb b/app/controllers/statuses_controller.rb new file mode 100644 index 000000000..e38a92982 --- /dev/null +++ b/app/controllers/statuses_controller.rb @@ -0,0 +1,20 @@ +class StatusesController < ChouetteController + respond_to :json + + def index + + status = { + referentials_blocked: Referential.blocked.count, + imports_blocked: Import.blocked.count, + compliance_check_sets_blocked: ComplianceCheckSet.blocked.count + } + status[:status] = global_status status + render json: status.to_json + end + + private + + def global_status status + status.values.all?(&:zero?) ? 'ok' : 'ko' + end +end diff --git a/app/controllers/vehicle_journeys_controller.rb b/app/controllers/vehicle_journeys_controller.rb index e031e4952..14795227c 100644 --- a/app/controllers/vehicle_journeys_controller.rb +++ b/app/controllers/vehicle_journeys_controller.rb @@ -154,7 +154,7 @@ class VehicleJourneysController < ChouetteController private def load_custom_fields - @custom_fields = current_workgroup.custom_fields_definitions + @custom_fields = referential.workgroup&.custom_fields_definitions || {} end def map_stop_points points @@ -185,7 +185,9 @@ class VehicleJourneysController < ChouetteController :long_lat_type => sp.stop_area.try(:long_lat_type), :country_code => sp.stop_area.try(:country_code), :country_name => sp.stop_area.try(:country_name), - :street_name => sp.stop_area.try(:street_name) + :street_name => sp.stop_area.try(:street_name), + :waiting_time => sp.stop_area.try(:waiting_time), + :waiting_time_text => sp.stop_area.decorate.try(:waiting_time_text), } end end diff --git a/app/decorators/calendar_decorator.rb b/app/decorators/calendar_decorator.rb index be1f9e3bf..4c6088e8e 100644 --- a/app/decorators/calendar_decorator.rb +++ b/app/decorators/calendar_decorator.rb @@ -1,6 +1,6 @@ class CalendarDecorator < AF83::Decorator decorates Calendar - + set_scope { context[:workgroup] } create_action_link with_instance_decorator do |instance_decorator| diff --git a/app/decorators/company_decorator.rb b/app/decorators/company_decorator.rb index aadce68bb..5580e0d4a 100644 --- a/app/decorators/company_decorator.rb +++ b/app/decorators/company_decorator.rb @@ -1,34 +1,21 @@ class CompanyDecorator < AF83::Decorator decorates Chouette::Company + set_scope { context[:referential] } + create_action_link do |l| l.content { h.t('companies.actions.new') } - l.href { [:new, context[:referential], :company] } end with_instance_decorator do |instance_decorator| - instance_decorator.show_action_link do |l| - l.href { [context[:referential], object] } - end + instance_decorator.show_action_link instance_decorator.edit_action_link do |l| l.content {|l| l.action == "show" ? h.t('actions.edit') : h.t('companies.actions.edit') } - l.href { - h.edit_line_referential_company_path( - context[:referential], - object - ) - } end instance_decorator.destroy_action_link do |l| l.content { h.destroy_link_content('companies.actions.destroy') } - l.href { - h.edit_line_referential_company_path( - context[:referential], - object - ) - } l.data {{ confirm: h.t('companies.actions.destroy_confirm') }} end end diff --git a/app/decorators/compliance_control_decorator.rb b/app/decorators/compliance_control_decorator.rb index c57a7ccc7..fd2dbd9ce 100644 --- a/app/decorators/compliance_control_decorator.rb +++ b/app/decorators/compliance_control_decorator.rb @@ -1,6 +1,8 @@ class ComplianceControlDecorator < AF83::Decorator decorates ComplianceControl + set_scope { object.compliance_control_set } + with_instance_decorator do |instance_decorator| instance_decorator.show_action_link do |l| l.content h.t('compliance_control_sets.actions.show') @@ -12,23 +14,9 @@ class ComplianceControlDecorator < AF83::Decorator end end - instance_decorator.edit_action_link do |l| - l.href do - h.edit_compliance_control_set_compliance_control_path( - object.compliance_control_set_id, - object.id - ) - end - end + instance_decorator.edit_action_link instance_decorator.destroy_action_link do |l| - l.content h.destroy_link_content - l.href do - h.compliance_control_set_compliance_control_path( - object.compliance_control_set.id, - object.id - ) - end l.data confirm: h.t('compliance_controls.actions.destroy_confirm') end end diff --git a/app/decorators/compliance_control_set_decorator.rb b/app/decorators/compliance_control_set_decorator.rb index 387822c67..b16a06886 100644 --- a/app/decorators/compliance_control_set_decorator.rb +++ b/app/decorators/compliance_control_set_decorator.rb @@ -6,6 +6,8 @@ class ComplianceControlSetDecorator < AF83::Decorator end with_instance_decorator do |instance_decorator| + instance_decorator.show_action_link + instance_decorator.edit_action_link do |l| l.content t('compliance_control_sets.actions.edit') end diff --git a/app/decorators/import_decorator.rb b/app/decorators/import_decorator.rb index c6b1f2349..1964365ae 100644 --- a/app/decorators/import_decorator.rb +++ b/app/decorators/import_decorator.rb @@ -1,6 +1,8 @@ class ImportDecorator < AF83::Decorator decorates Import + set_scope { context[:workbench] } + define_instance_method :import_status_css_class do cls ='' cls = 'overheaded-success' if object.status == 'successful' @@ -11,13 +13,10 @@ class ImportDecorator < AF83::Decorator create_action_link do |l| l.content t('imports.actions.new') - l.href { h.new_workbench_import_path(workbench_id: context[:workbench]) } end with_instance_decorator do |instance_decorator| - instance_decorator.show_action_link do |l| - l.href { h.workbench_import_path(context[:workbench], object) } - end + instance_decorator.show_action_link instance_decorator.action_link secondary: :show do |l| l.content t('imports.actions.download') diff --git a/app/decorators/import_resources_decorator.rb b/app/decorators/import_resources_decorator.rb index 2b1a25ef9..88a8057cf 100644 --- a/app/decorators/import_resources_decorator.rb +++ b/app/decorators/import_resources_decorator.rb @@ -1,4 +1,4 @@ -class ImportResourcesDecorator < ModelDecorator +class ImportResourcesDecorator < AF83::Decorator delegate :where def lines_imported diff --git a/app/decorators/line_decorator.rb b/app/decorators/line_decorator.rb index adeb89f70..0e7b6b9ae 100644 --- a/app/decorators/line_decorator.rb +++ b/app/decorators/line_decorator.rb @@ -1,9 +1,10 @@ class LineDecorator < AF83::Decorator decorates Chouette::Line + set_scope { context[:line_referential] } + create_action_link do |l| l.content t('lines.actions.new') - l.href { h.new_line_referential_line_path(context[:line_referential]) } end with_instance_decorator do |instance_decorator| @@ -14,18 +15,17 @@ class LineDecorator < AF83::Decorator instance_decorator.show_action_link do |l| l.content t('lines.actions.show') - l.href { [context[:line_referential], object] } end instance_decorator.action_link secondary: :show do |l| l.content t('lines.actions.show_network') - l.href { [context[:line_referential], object.network] } + l.href { [scope, object.network] } l.disabled { object.network.nil? } end instance_decorator.action_link secondary: :show do |l| l.content t('lines.actions.show_company') - l.href { [context[:line_referential], object.company] } + l.href { [scope, object.company] } l.disabled { object.company.nil? } end @@ -34,7 +34,6 @@ class LineDecorator < AF83::Decorator instance_decorator.with_condition can_edit_line do edit_action_link do |l| l.content {|l| l.primary? ? h.t('actions.edit') : h.t('lines.actions.edit') } - l.href { h.edit_line_referential_line_path(context[:line_referential], object.id) } end action_link on: :index, secondary: :index do |l| @@ -64,7 +63,6 @@ class LineDecorator < AF83::Decorator instance_decorator.destroy_action_link do |l| l.content { h.destroy_link_content('lines.actions.destroy') } - l.href { h.line_referential_line_path(context[:line_referential], object) } l.data confirm: h.t('lines.actions.destroy_confirm') l.add_class "delete-action" end diff --git a/app/decorators/network_decorator.rb b/app/decorators/network_decorator.rb index 90f0d0e82..ea0f73dc2 100644 --- a/app/decorators/network_decorator.rb +++ b/app/decorators/network_decorator.rb @@ -1,6 +1,7 @@ class NetworkDecorator < AF83::Decorator decorates Chouette::Network + set_scope { context[:line_referential] } # Action links require: # context: { # line_referential: , @@ -8,15 +9,10 @@ class NetworkDecorator < AF83::Decorator create_action_link do |l| l.content t('networks.actions.new') - l.href { h.new_line_referential_network_path(context[:line_referential]) } end with_instance_decorator do |instance_decorator| - instance_decorator.show_action_link do |l| - l.href do - h.line_referential_network_path(context[:line_referential], object) - end - end + instance_decorator.show_action_link instance_decorator.action_link secondary: true, policy: :edit do |l| l.content t('networks.actions.edit') @@ -30,12 +26,6 @@ class NetworkDecorator < AF83::Decorator instance_decorator.destroy_action_link do |l| l.content h.destroy_link_content('networks.actions.destroy') - l.href do - h.line_referential_network_path( - context[:line_referential], - object - ) - end l.data confirm: h.t('networks.actions.destroy_confirm') end end diff --git a/app/decorators/purchase_window_decorator.rb b/app/decorators/purchase_window_decorator.rb index 54b241173..9b58577b2 100644 --- a/app/decorators/purchase_window_decorator.rb +++ b/app/decorators/purchase_window_decorator.rb @@ -1,32 +1,20 @@ class PurchaseWindowDecorator < AF83::Decorator decorates Chouette::PurchaseWindow + set_scope { context[:referential] } + create_action_link do |l| l.content t('purchase_windows.actions.new') - l.href { h.new_referential_purchase_window_path(context[:referential]) } end with_instance_decorator do |instance_decorator| instance_decorator.show_action_link do |l| l.content t('purchase_windows.actions.show') - l.href do - h.referential_purchase_window_path( - context[:referential], - object - ) - end end - instance_decorator.edit_action_link do |l| - l.href do - h.edit_referential_purchase_window_path(context[:referential].id, object) - end - end + instance_decorator.edit_action_link instance_decorator.destroy_action_link do |l| - l.href do - h.referential_purchase_window_path(context[:referential].id, object) - end l.data confirm: h.t('purchase_windows.actions.destroy_confirm') end end diff --git a/app/decorators/referential_decorator.rb b/app/decorators/referential_decorator.rb index 3132cbf92..41cad237d 100644 --- a/app/decorators/referential_decorator.rb +++ b/app/decorators/referential_decorator.rb @@ -22,12 +22,12 @@ class ReferentialDecorator < AF83::Decorator instance_decorator.action_link policy: :clone, secondary: :show do |l| l.content t('actions.clone') - l.href { h.new_referential_path(from: object.id, current_workbench_id: context[:current_workbench_id]) } + l.href { h.duplicate_workbench_referential_path(object) } end instance_decorator.action_link policy: :validate, secondary: :show do |l| l.content t('actions.validate') - l.href { h.referential_select_compliance_control_set_path(object.id) } + l.href { h.select_compliance_control_set_referential_path(object.id) } end instance_decorator.action_link policy: :archive, secondary: :show do |l| diff --git a/app/decorators/referential_line_decorator.rb b/app/decorators/referential_line_decorator.rb index 8f884a8e0..3ac846d76 100644 --- a/app/decorators/referential_line_decorator.rb +++ b/app/decorators/referential_line_decorator.rb @@ -1,6 +1,8 @@ class ReferentialLineDecorator < AF83::Decorator decorates Chouette::Line + set_scope { context[:referential] } + # Action links require: # context: { # referential: , @@ -8,9 +10,7 @@ class ReferentialLineDecorator < AF83::Decorator # } with_instance_decorator do |instance_decorator| - instance_decorator.show_action_link do |l| - l.href { h.referential_line_path(context[:referential], object) } - end + instance_decorator.show_action_link instance_decorator.action_link secondary: true do |l| l.content Chouette::Line.human_attribute_name(:footnotes) @@ -21,7 +21,7 @@ class ReferentialLineDecorator < AF83::Decorator l.content h.t('routing_constraint_zones.index.title') l.href do h.referential_line_routing_constraint_zones_path( - context[:referential], + scope, object ) end @@ -37,7 +37,7 @@ class ReferentialLineDecorator < AF83::Decorator secondary: true ) do |l| l.content h.t('routes.actions.new') - l.href { h.new_referential_line_route_path(context[:referential], object) } + l.href { h.new_referential_line_route_path(scope, object) } end end end diff --git a/app/decorators/referential_network_decorator.rb b/app/decorators/referential_network_decorator.rb index ff3467188..c508452c0 100644 --- a/app/decorators/referential_network_decorator.rb +++ b/app/decorators/referential_network_decorator.rb @@ -1,6 +1,8 @@ class ReferentialNetworkDecorator < AF83::Decorator decorates Chouette::Network + set_scope { context[:referential] } + # Action links require: # context: { # referential: , @@ -8,33 +10,18 @@ class ReferentialNetworkDecorator < AF83::Decorator create_action_link do |l| l.content t('networks.actions.new') - l.href { h.new_referential_network_path(context[:referential]) } end with_instance_decorator do |instance_decorator| - instance_decorator.show_action_link do |l| - l.href { h.referential_network_path(context[:referential], object) } - end + instance_decorator.show_action_link instance_decorator.edit_action_link do |l| l.content t('networks.actions.edit') - l.href do - h.edit_referential_network_path( - context[:referential], - object - ) - end end instance_decorator.destroy_action_link do |l| l.content h.destroy_link_content('networks.actions.destroy') - l.href do - h.referential_network_path( - context[:referential], - object - ) - end l.data confirm: h.t('networks.actions.destroy_confirm') end end -end
\ No newline at end of file +end diff --git a/app/decorators/route_decorator.rb b/app/decorators/route_decorator.rb index f9870fbbe..fa6367924 100644 --- a/app/decorators/route_decorator.rb +++ b/app/decorators/route_decorator.rb @@ -7,26 +7,12 @@ class RouteDecorator < AF83::Decorator # line: # } + set_scope { [context[:referential], context[:line]] } + with_instance_decorator do |instance_decorator| - instance_decorator.show_action_link do |l| - l.href do - h.referential_line_route_path( - context[:referential], - context[:line], - object - ) - end - end + instance_decorator.show_action_link - instance_decorator.edit_action_link do |l| - l.href do - h.edit_referential_line_route_path( - context[:referential], - context[:line], - object - ) - end - end + instance_decorator.edit_action_link instance_decorator.action_link( if: ->() { object.stop_points.any? }, @@ -86,13 +72,6 @@ class RouteDecorator < AF83::Decorator end instance_decorator.destroy_action_link do |l| - l.href do - h.referential_line_route_path( - context[:referential], - context[:line], - object - ) - end l.data confirm: h.t('routes.actions.destroy_confirm') end end diff --git a/app/decorators/routing_constraint_zone_decorator.rb b/app/decorators/routing_constraint_zone_decorator.rb index 962625fa7..de73068be 100644 --- a/app/decorators/routing_constraint_zone_decorator.rb +++ b/app/decorators/routing_constraint_zone_decorator.rb @@ -1,6 +1,8 @@ class RoutingConstraintZoneDecorator < AF83::Decorator decorates Chouette::RoutingConstraintZone + set_scope { [context[:referential], context[:line]] } + # Action links require: # context: { # referential: , @@ -12,44 +14,13 @@ class RoutingConstraintZoneDecorator < AF83::Decorator h.policy(Chouette::RoutingConstraintZone).create? && context[:referential].organisation == h.current_organisation } - ) do |l| - l.href do - h.new_referential_line_routing_constraint_zone_path( - context[:referential], - context[:line] - ) - end - end + ) with_instance_decorator do |instance_decorator| - instance_decorator.show_action_link do |l| - l.href do - h.referential_line_routing_constraint_zone_path( - context[:referential], - context[:line], - object - ) - end - end - - instance_decorator.edit_action_link do |l| - l.href do - h.edit_referential_line_routing_constraint_zone_path( - context[:referential], - context[:line], - object - ) - end - end + instance_decorator.show_action_link + instance_decorator.edit_action_link instance_decorator.destroy_action_link do |l| - l.href do - h.referential_line_routing_constraint_zone_path( - context[:referential], - context[:line], - object - ) - end l.data confirm: h.t('routing_constraint_zones.actions.destroy_confirm') end end diff --git a/app/decorators/stop_area_decorator.rb b/app/decorators/stop_area_decorator.rb index 2e57da0e4..525681971 100644 --- a/app/decorators/stop_area_decorator.rb +++ b/app/decorators/stop_area_decorator.rb @@ -7,23 +7,11 @@ class StopAreaDecorator < AF83::Decorator end with_instance_decorator do |instance_decorator| - instance_decorator.show_action_link do |l| - l.href do - h.stop_area_referential_stop_area_path( - object.stop_area_referential, - object - ) - end - end + set_scope { object.stop_area_referential } + instance_decorator.show_action_link instance_decorator.edit_action_link do |l| l.content h.t('stop_areas.actions.edit') - l.href do - h.edit_stop_area_referential_stop_area_path( - object.stop_area_referential, - object - ) - end end instance_decorator.action_link policy: :deactivate, secondary: true do |l| @@ -54,12 +42,6 @@ class StopAreaDecorator < AF83::Decorator instance_decorator.destroy_action_link do |l| l.content h.destroy_link_content('stop_areas.actions.destroy') - l.href do - h.stop_area_referential_stop_area_path( - object.stop_area_referential, - object - ) - end l.data confirm: h.t('stop_areas.actions.destroy_confirm') end end diff --git a/app/decorators/time_table_decorator.rb b/app/decorators/time_table_decorator.rb index 95b1fd959..e4f9d7dbc 100644 --- a/app/decorators/time_table_decorator.rb +++ b/app/decorators/time_table_decorator.rb @@ -14,7 +14,7 @@ class TimeTableDecorator < AF83::Decorator l.href { [:edit, context[:referential], object] } end - instance_decorator.action_link if: ->{ object.calendar }, secondary: true do |l| + instance_decorator.action_link policy: :actualize, if: ->{ object.calendar }, secondary: true do |l| l.content t('actions.actualize') l.href do h.actualize_referential_time_table_path( diff --git a/app/helpers/referentials_helper.rb b/app/helpers/referentials_helper.rb index 8251377aa..e464ec8a5 100644 --- a/app/helpers/referentials_helper.rb +++ b/app/helpers/referentials_helper.rb @@ -15,4 +15,14 @@ module ReferentialsHelper service = ReferentialOverview.new referential, self render partial: "referentials/overview", locals: {referential: referential, overview: service} end + + def mutual_workbench workbench + current_user.organisation.workbenches.where(workgroup_id: workbench.workgroup_id).last + end + + def duplicate_workbench_referential_path referential + workbench = mutual_workbench referential.workbench + raise "Missing workbench for referential #{referential.name}" unless workbench.present? + new_workbench_referential_path(workbench, from: referential.id) + end end diff --git a/app/helpers/table_builder_helper.rb b/app/helpers/table_builder_helper.rb index 9a255e757..2068dd23c 100644 --- a/app/helpers/table_builder_helper.rb +++ b/app/helpers/table_builder_helper.rb @@ -330,7 +330,7 @@ module TableBuilderHelper else menu = content_tag :ul, class: 'dropdown-menu' do ( - CustomLinks.new(item, pundit_user, links, referential).links + + CustomLinks.new(item, pundit_user, links, referential, workgroup).links + action_links.select { |link| link.is_a?(Link) } ).map do |link| gear_menu_link(link) @@ -395,7 +395,6 @@ module TableBuilderHelper 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( @@ -414,4 +413,10 @@ module TableBuilderHelper # cases, avoid a `NoMethodError`. @__referential__ ||= try(:current_referential) end + + def workgroup + # Certain controllers don't define a `#current_referential`. In these + # cases, avoid a `NoMethodError`. + @__workgroup__ ||= try(:current_workgroup) + end end diff --git a/app/helpers/table_builder_helper/custom_links.rb b/app/helpers/table_builder_helper/custom_links.rb index b1bb11f10..e09078be0 100644 --- a/app/helpers/table_builder_helper/custom_links.rb +++ b/app/helpers/table_builder_helper/custom_links.rb @@ -8,13 +8,14 @@ module TableBuilderHelper unarchive: :put } - attr_reader :actions, :object, :user_context, :referential + attr_reader :actions, :object, :user_context, :referential, :workgroup - def initialize(object, user_context, actions, referential = nil) + def initialize(object, user_context, actions, referential = nil, workgroup = nil) @object = object @user_context = user_context @actions = actions @referential = referential + @workgroup = workgroup end def links @@ -34,7 +35,7 @@ module TableBuilderHelper polymorph_url << action end - polymorph_url += URL.polymorphic_url_parts(object, referential) + polymorph_url += URL.polymorphic_url_parts(object, referential, workgroup) end def method_for_action(action) diff --git a/app/helpers/table_builder_helper/url.rb b/app/helpers/table_builder_helper/url.rb index 28f1ade76..0e3dce0aa 100644 --- a/app/helpers/table_builder_helper/url.rb +++ b/app/helpers/table_builder_helper/url.rb @@ -1,6 +1,6 @@ module TableBuilderHelper class URL - def self.polymorphic_url_parts(item, referential) + def self.polymorphic_url_parts(item, referential, workgroup) polymorph_url = [] unless item.is_a?(Calendar) || item.is_a?(Referential) || item.is_a?(ComplianceControlSet) @@ -20,6 +20,7 @@ module TableBuilderHelper end end else + polymorph_url << item.workgroup if item.respond_to? :workgroup polymorph_url << item end diff --git a/app/javascript/helpers/stop_area_header_manager.js b/app/javascript/helpers/stop_area_header_manager.js index 5b18e2f63..1003b2cf6 100644 --- a/app/javascript/helpers/stop_area_header_manager.js +++ b/app/javascript/helpers/stop_area_header_manager.js @@ -15,11 +15,15 @@ export default class StopAreaHeaderManager { let index = this.ids_list.indexOf(object_id) let sp = this.stopPointsList[index] let showHeadline = this.showHeader(object_id) + let title = sp.city_name ? sp.city_name + ' (' + sp.zip_code +')' : "" + if(sp.waiting_time > 0){ + title += " | " + sp.waiting_time_text + } return ( <div className={(showHeadline) ? 'headlined' : ''} data-headline={showHeadline} - title={sp.city_name ? sp.city_name + ' (' + sp.zip_code +')' : ""} + title={title} > <span> <span> diff --git a/app/javascript/routes/components/BSelect2.js b/app/javascript/routes/components/BSelect2.js index 158deaa17..035bce155 100644 --- a/app/javascript/routes/components/BSelect2.js +++ b/app/javascript/routes/components/BSelect2.js @@ -85,7 +85,7 @@ class BSelect2 extends Component{ onSelect={ this.props.onSelect } ref='newSelect' options={{ - placeholder: this.context.I18n.routes.edit.select2.placeholder, + placeholder: this.context.I18n.t("routes.edit.select2.placeholder"), allowClear: true, language: 'fr', /* Doesn't seem to work... :( */ theme: 'bootstrap', diff --git a/app/javascript/routes/components/OlMap.js b/app/javascript/routes/components/OlMap.js index 056bddbcb..4beb02872 100644 --- a/app/javascript/routes/components/OlMap.js +++ b/app/javascript/routes/components/OlMap.js @@ -115,40 +115,40 @@ export default class OlMap extends Component{ <strong>{this.props.value.olMap.json.name}</strong> </p> <p> - <strong>{this.context.I18n.routes.edit.stop_point_type} : </strong> + <strong>{this.context.I18n.t('routes.edit.map.stop_point_type')} : </strong> {this.props.value.olMap.json.area_type} </p> <p> - <strong>{this.context.I18n.routes.edit.short_name} : </strong> + <strong>{this.context.I18n.t('routes.edit.map.short_name')} : </strong> {this.props.value.olMap.json.short_name} </p> <p> - <strong>{this.context.I18n.id_reflex} : </strong> + <strong>{this.context.I18n.t('id_reflex')} : </strong> {this.props.value.olMap.json.user_objectid} </p> - <p><strong>{this.context.I18n.routes.edit.map.coordinates} : </strong></p> + <p><strong>{this.context.I18n.t('routes.edit.map.coordinates')} : </strong></p> <p style={{paddingLeft: 10, marginTop: 0}}> - <em>{this.context.I18n.routes.edit.map.proj}.: </em>WSG84<br/> - <em>{this.context.I18n.routes.edit.map.lat}.: </em>{this.props.value.olMap.json.latitude} <br/> - <em>{this.context.I18n.routes.edit.map.lon}.: </em>{this.props.value.olMap.json.longitude} + <em>{this.context.I18n.t('routes.edit.map.proj')}.: </em>WSG84<br/> + <em>{this.context.I18n.t('routes.edit.map.lat')}.: </em>{this.props.value.olMap.json.latitude} <br/> + <em>{this.context.I18n.t('routes.edit.map.lon')}.: </em>{this.props.value.olMap.json.longitude} </p> <p> - <strong>{this.context.I18n.routes.edit.map.postal_code} : </strong> + <strong>{this.context.I18n.t('routes.edit.map.postal_code')} : </strong> {this.props.value.olMap.json.zip_code} </p> <p> - <strong>{this.context.I18n.routes.edit.map.city} : </strong> + <strong>{this.context.I18n.t('routes.edit.map.city')} : </strong> {this.props.value.olMap.json.city_name} </p> <p> - <strong>{this.context.I18n.routes.edit.map.comment} : </strong> + <strong>{this.context.I18n.t('routes.edit.map.comment')} : </strong> {this.props.value.olMap.json.comment} </p> {(this.props.value.stoparea_id != this.props.value.olMap.json.stoparea_id) &&( <div className='btn btn-outline-primary btn-sm' onClick= {() => {this.props.onUpdateViaOlMap(this.props.index, this.props.value.olMap.json)}} - >{this.context.I18n.actions.select}</div> + >{this.context.I18n.t('actions.select')}</div> )} </div> <div className='map_content'> @@ -162,7 +162,7 @@ export default class OlMap extends Component{ } } -OlMap.PropTypes = { +OlMap.propTypes = { } OlMap.contextTypes = { diff --git a/app/javascript/routes/components/StopPoint.js b/app/javascript/routes/components/StopPoint.js index 2d47e802b..af51a6bb4 100644 --- a/app/javascript/routes/components/StopPoint.js +++ b/app/javascript/routes/components/StopPoint.js @@ -18,15 +18,15 @@ export default function StopPoint(props, {I18n}) { <div> <select className='form-control' value={props.value.for_boarding} id="for_boarding" onChange={props.onSelectChange}> - <option value="normal">{I18n.routes.edit.stop_point.boarding.normal}</option> - <option value="forbidden">{I18n.routes.edit.stop_point.boarding.forbidden}</option> + <option value="normal">{I18n.t('routes.edit.stop_point.boarding.normal')}</option> + <option value="forbidden">{I18n.t('routes.edit.stop_point.boarding.forbidden')}</option> </select> </div> <div> <select className='form-control' value={props.value.for_alighting} id="for_alighting" onChange={props.onSelectChange}> - <option value="normal">{I18n.routes.edit.stop_point.alighting.normal}</option> - <option value="forbidden">{I18n.routes.edit.stop_point.alighting.forbidden}</option> + <option value="normal">{I18n.t('routes.edit.stop_point.alighting.normal')}</option> + <option value="forbidden">{I18n.t('routes.edit.stop_point.alighting.forbidden')}</option> </select> </div> @@ -77,7 +77,7 @@ export default function StopPoint(props, {I18n}) { ) } -StopPoint.PropTypes = { +StopPoint.propTypes = { onToggleMap: PropTypes.func.isRequired, onToggleEdit: PropTypes.func.isRequired, onDeleteClick: PropTypes.func.isRequired, @@ -93,4 +93,4 @@ StopPoint.PropTypes = { StopPoint.contextTypes = { I18n: PropTypes.object -}
\ No newline at end of file +} diff --git a/app/javascript/routes/components/StopPointList.js b/app/javascript/routes/components/StopPointList.js index b39fa0c9c..b227abdea 100644 --- a/app/javascript/routes/components/StopPointList.js +++ b/app/javascript/routes/components/StopPointList.js @@ -10,22 +10,22 @@ export default function StopPointList({ stopPoints, onDeleteClick, onMoveUpClick <div className="wrapper"> <div style={{width: 100}}> <div className="form-group"> - <label className="control-label">{I18n.reflex_id}</label> + <label className="control-label">{I18n.t('simple_form.labels.stop_point.reflex_id')}</label> </div> </div> <div> <div className="form-group"> - <label className="control-label">{I18n.simple_form.labels.stop_point.name}</label> + <label className="control-label">{I18n.t('simple_form.labels.stop_point.name')}</label> </div> </div> <div> <div className="form-group"> - <label className="control-label">{I18n.simple_form.labels.stop_point.for_boarding}</label> + <label className="control-label">{I18n.t('simple_form.labels.stop_point.for_boarding')}</label> </div> </div> <div> <div className="form-group"> - <label className="control-label">{I18n.simple_form.labels.stop_point.for_alighting}</label> + <label className="control-label">{I18n.t('simple_form.labels.stop_point.for_alighting')}</label> </div> </div> <div className='actions-5'></div> diff --git a/app/javascript/time_tables/actions/index.js b/app/javascript/time_tables/actions/index.js index 4a36ec4e1..98b9eab4b 100644 --- a/app/javascript/time_tables/actions/index.js +++ b/app/javascript/time_tables/actions/index.js @@ -8,7 +8,7 @@ const I18n = clone(window, "I18n") const actions = { weekDays: (index) => { - return range(1, 8).map(n => I18n.time_tables.edit.metas.days[n]) + return range(1, 8).map(n => I18n.t('time_tables.edit.metas.days')[n]) }, strToArrayDayTypes: (str) =>{ return actions.weekDays().map(day => str.indexOf(day) !== -1) @@ -155,7 +155,7 @@ const actions = { type : 'CLOSE_MODAL' }), monthName(strDate) { - let monthList = range(1,13).map(n => I18n.calendars.months[n]) + let monthList = range(1,13).map(n => I18n.t('calendars.months.'+ n )) let date = new Date(strDate) return monthList[date.getUTCMonth()] }, @@ -225,7 +225,7 @@ const actions = { let period = periods[i] if (index !== i && !period.deleted) { if (new Date(period.period_start) <= end && new Date(period.period_end) >= start) { - error = I18n.time_tables.edit.error_submit.periods_overlaps + error = I18n.t('time_tables.edit.error_submit.periods_overlaps') break } } @@ -239,7 +239,7 @@ const actions = { for (let day of in_days) { if (start <= new Date(day.date) && end >= new Date(day.date)) { - error = I18n.time_tables.edit.error_submit.dates_overlaps + error = I18n.t('time_tables.edit.error_submit.dates_overlaps') break } } @@ -316,9 +316,9 @@ const actions = { errorModalMessage: (errorKey) => { switch (errorKey) { case "withoutPeriodsWithDaysTypes": - return I18n.time_tables.edit.error_modal.withoutPeriodsWithDaysTypes + return I18n.t('time_tables.edit.error_modal.withoutPeriodsWithDaysTypes') case "withPeriodsWithoutDayTypes": - return I18n.time_tables.edit.error_modal.withPeriodsWithoutDayTypes + return I18n.t('time_tables.edit.error_modal.withPeriodsWithoutDayTypes') default: return errorKey diff --git a/app/javascript/time_tables/components/ConfirmModal.js b/app/javascript/time_tables/components/ConfirmModal.js index 845e7ed1b..4e8583bc0 100644 --- a/app/javascript/time_tables/components/ConfirmModal.js +++ b/app/javascript/time_tables/components/ConfirmModal.js @@ -9,11 +9,11 @@ export default function ConfirmModal({dispatch, modal, onModalAccept, onModalCan <div className='modal-dialog'> <div className='modal-content'> <div className='modal-header'> - <h4 className='modal-title'>{I18n.time_tables.edit.confirm_modal.title}</h4> + <h4 className='modal-title'>{I18n.t('time_tables.edit.confirm_modal.title')}</h4> </div> <div className='modal-body'> <div className='mt-md mb-md'> - <p>{I18n.time_tables.edit.confirm_modal.message}</p> + <p>{I18n.t('time_tables.edit.confirm_modal.message')}</p> </div> </div> <div className='modal-footer'> @@ -23,7 +23,7 @@ export default function ConfirmModal({dispatch, modal, onModalAccept, onModalCan type='button' onClick={() => { onModalCancel(modal.confirmModal.callback) }} > - {I18n.cancel} + {I18n.t('cancel')} </button> <button className='btn btn-primary' @@ -31,7 +31,7 @@ export default function ConfirmModal({dispatch, modal, onModalAccept, onModalCan type='button' onClick={() => { onModalAccept(modal.confirmModal.callback, timetable, metas) }} > - {I18n.actions.submit} + {I18n.t('actions.submit')} </button> </div> </div> @@ -49,4 +49,4 @@ ConfirmModal.propTypes = { ConfirmModal.contextTypes = { I18n: PropTypes.object -}
\ No newline at end of file +} diff --git a/app/javascript/time_tables/components/ErrorModal.js b/app/javascript/time_tables/components/ErrorModal.js index 543177e54..8af12f1d1 100644 --- a/app/javascript/time_tables/components/ErrorModal.js +++ b/app/javascript/time_tables/components/ErrorModal.js @@ -10,7 +10,7 @@ export default function ErrorModal({dispatch, modal, onModalClose}, {I18n}) { <div className='modal-dialog'> <div className='modal-content'> <div className='modal-header'> - <h4 className='modal-title'>{I18n.time_tables.edit.error_modal.title}</h4> + <h4 className='modal-title'>{I18n.t('time_tables.edit.error_modal.title')}</h4> </div> <div className='modal-body'> <div className='mt-md mb-md'> @@ -24,7 +24,7 @@ export default function ErrorModal({dispatch, modal, onModalClose}, {I18n}) { type='button' onClick={() => { onModalClose() }} > - {I18n.back} + {I18n.t('back')} </button> </div> </div> @@ -41,4 +41,4 @@ ErrorModal.propTypes = { ErrorModal.contextTypes = { I18n: PropTypes.object -}
\ No newline at end of file +} diff --git a/app/javascript/time_tables/components/Metas.js b/app/javascript/time_tables/components/Metas.js index 3c6848d27..08a6e26fe 100644 --- a/app/javascript/time_tables/components/Metas.js +++ b/app/javascript/time_tables/components/Metas.js @@ -13,7 +13,7 @@ export default function Metas({metas, onUpdateDayTypes, onUpdateComment, onUpdat {/* comment (name) */} <div className="form-group"> <label htmlFor="" className="control-label col-sm-4 required"> - {I18n.time_tables.edit.metas.name} <abbr title="">*</abbr> + {I18n.t('time_tables.edit.metas.name')} <abbr title="">*</abbr> </label> <div className="col-sm-8"> <input @@ -28,7 +28,7 @@ export default function Metas({metas, onUpdateDayTypes, onUpdateComment, onUpdat {/* color */} {metas.color !== undefined && <div className="form-group"> - <label htmlFor="" className="control-label col-sm-4">{I18n.activerecord.attributes.time_table.color}</label> + <label htmlFor="" className="control-label col-sm-4">{I18n.attribute_name('time_table', 'color')}</label> <div className="col-sm-8"> <div className="dropdown color_selector"> <button @@ -73,7 +73,7 @@ export default function Metas({metas, onUpdateDayTypes, onUpdateComment, onUpdat {/* tags */} {metas.tags !== undefined && <div className="form-group"> - <label htmlFor="" className="control-label col-sm-4">{I18n.activerecord.attributes.time_table.tag_list}</label> + <label htmlFor="" className="control-label col-sm-4">{I18n.attribute_name('time_table', 'tag_list')}</label> <div className="col-sm-8"> <TagsSelect2 initialTags={metas.initial_tags} @@ -86,16 +86,16 @@ export default function Metas({metas, onUpdateDayTypes, onUpdateComment, onUpdat {/* calendar */} {metas.calendar !== null && <div className="form-group"> - <label htmlFor="" className="control-label col-sm-4">{I18n.activerecord.attributes.time_table.calendar}</label> + <label htmlFor="" className="control-label col-sm-4">{I18n.attribute_name('time_table', 'calendar')}</label> <div className="col-sm-8"> - <span>{metas.calendar ? metas.calendar.name : I18n.time_tables.edit.metas.no_calendar}</span> + <span>{metas.calendar ? metas.calendar.name : I18n.t('time_tables.edit.metas.no_calendar')}</span> </div> </div>} {/* day_types */} <div className="form-group"> <label htmlFor="" className="control-label col-sm-4"> - {I18n.time_tables.edit.metas.day_types} + {I18n.t('time_tables.edit.metas.day_types')} </label> <div className="col-sm-8"> <div className="form-group labelled-checkbox-group"> diff --git a/app/javascript/time_tables/components/PeriodForm.js b/app/javascript/time_tables/components/PeriodForm.js index 085654a88..d17a246f7 100644 --- a/app/javascript/time_tables/components/PeriodForm.js +++ b/app/javascript/time_tables/components/PeriodForm.js @@ -46,7 +46,7 @@ export default function PeriodForm({modal, timetable, metas, onOpenAddPeriodForm <div> <div className="form-group"> <label htmlFor="" className="control-label required"> - {I18n.time_tables.edit.period_form.begin} + {I18n.t('time_tables.edit.period_form.begin')} <abbr title="requis">*</abbr> </label> </div> @@ -54,7 +54,7 @@ export default function PeriodForm({modal, timetable, metas, onOpenAddPeriodForm <div> <div className="form-group"> <label htmlFor="" className="control-label required"> - {I18n.time_tables.edit.period_form.end} + {I18n.t('time_tables.edit.period_form.end')} <abbr title="requis">*</abbr> </label> </div> @@ -105,14 +105,14 @@ export default function PeriodForm({modal, timetable, metas, onOpenAddPeriodForm className='btn btn-link' onClick={onClosePeriodForm} > - {I18n.cancel} + {I18n.t('cancel')} </button> <button type='button' className='btn btn-outline-primary mr-sm' onClick={() => onValidatePeriodForm(modal.modalProps, timetable.time_table_periods, metas, filter(timetable.time_table_dates, ['in_out', true]))} > - {I18n.actions.submit} + {I18n.t('actions.submit')} </button> </div> </div> @@ -124,7 +124,7 @@ export default function PeriodForm({modal, timetable, metas, onOpenAddPeriodForm className='btn btn-outline-primary' onClick={onOpenAddPeriodForm} > - {I18n.time_tables.actions.add_period} + {I18n.t('time_tables.actions.add_period')} </button> </div> } @@ -132,7 +132,7 @@ export default function PeriodForm({modal, timetable, metas, onOpenAddPeriodForm </div> </div> </div> - ) + ) } PeriodForm.propTypes = { @@ -147,4 +147,4 @@ PeriodForm.propTypes = { PeriodForm.contextTypes = { I18n: PropTypes.object -}
\ No newline at end of file +} diff --git a/app/javascript/time_tables/components/TagsSelect2.js b/app/javascript/time_tables/components/TagsSelect2.js index dc3739d58..43cf59fdf 100644 --- a/app/javascript/time_tables/components/TagsSelect2.js +++ b/app/javascript/time_tables/components/TagsSelect2.js @@ -40,7 +40,7 @@ export default class TagsSelect2 extends Component { allowClear: true, theme: 'bootstrap', width: '100%', - placeholder: this.context.I18n.time_tables.edit.select2.tag.placeholder, + placeholder: this.context.I18n.t('time_tables.edit.select2.tag.placeholder'), ajax: { url: origin + path + '/tags.json', dataType: 'json', diff --git a/app/javascript/time_tables/components/Timetable.js b/app/javascript/time_tables/components/Timetable.js index c44f2a134..991f31435 100644 --- a/app/javascript/time_tables/components/Timetable.js +++ b/app/javascript/time_tables/components/Timetable.js @@ -31,11 +31,11 @@ export default class Timetable extends Component { <div className="table table-2entries mb-sm"> <div className="t2e-head w20"> <div className="th"> - <div className="strong">{this.context.I18n.time_tables.synthesis}</div> + <div className="strong">{this.context.I18n.t('time_tables.edit.synthesis')}</div> </div> - <div className="td"><span>{this.context.I18n.time_tables.edit.day_types}</span></div> - <div className="td"><span>{this.context.I18n.time_tables.edit.periods}</span></div> - <div className="td"><span>{this.context.I18n.time_tables.edit.exceptions}</span></div> + <div className="td"><span>{this.context.I18n.t('time_tables.edit.day_types')}</span></div> + <div className="td"><span>{this.context.I18n.t('time_tables.edit.periods')}</span></div> + <div className="td"><span>{this.context.I18n.t('time_tables.edit.exceptions')}</span></div> </div> <div className="t2e-item-list w80"> <div> diff --git a/app/javascript/vehicle_journeys/actions/index.js b/app/javascript/vehicle_journeys/actions/index.js index 4ca8bd73b..b398d78fa 100644 --- a/app/javascript/vehicle_journeys/actions/index.js +++ b/app/javascript/vehicle_journeys/actions/index.js @@ -348,21 +348,11 @@ const actions = { var purchaseWindows = [] let tt for (tt of val.time_tables){ - timeTables.push({ - objectid: tt.objectid, - comment: tt.comment, - id: tt.id, - color: tt.color - }) + timeTables.push(tt) } if(val.purchase_windows){ for (tt of val.purchase_windows){ - purchaseWindows.push({ - objectid: tt.objectid, - name: tt.name, - id: tt.id, - color: tt.color - }) + purchaseWindows.push(tt) } } let vjasWithDelta = val.vehicle_journey_at_stops.map((vjas, i) => { @@ -527,6 +517,22 @@ const actions = { minute: actions.simplePad(newArrivalDT.getUTCMinutes()) } } + }, + addMinutesToTime: (time, minutes) => { + let res = { + hour: time.hour, + minute: time.minute + } + let delta_hour = parseInt(minutes/60) + let delta_minute = minutes - 60*delta_hour + res.hour += delta_hour + res.minute += delta_minute + let extra_hours = parseInt(res.minute/60) + res.hour += extra_hours + res.minute -= extra_hours*60 + res.hour = res.hour % 24 + + return res } } diff --git a/app/javascript/vehicle_journeys/components/ConfirmModal.js b/app/javascript/vehicle_journeys/components/ConfirmModal.js index 3bfc852fb..75e8a3932 100644 --- a/app/javascript/vehicle_journeys/components/ConfirmModal.js +++ b/app/javascript/vehicle_journeys/components/ConfirmModal.js @@ -7,7 +7,7 @@ export default function ConfirmModal({dispatch, modal, onModalAccept, onModalCan <div className='modal-dialog'> <div className='modal-content'> <div className='modal-body'> - <p> Voulez-vous valider vos modifications avant de changer de page? </p> + <p> {I18n.t('vehicle_journeys.vehicle_journeys_matrix.modal_confirm')} </p> </div> <div className='modal-footer'> <button @@ -31,11 +31,11 @@ export default function ConfirmModal({dispatch, modal, onModalAccept, onModalCan </div> </div> ) -} +} ConfirmModal.propTypes = { vehicleJourneys: PropTypes.array.isRequired, modal: PropTypes.object.isRequired, onModalAccept: PropTypes.func.isRequired, onModalCancel: PropTypes.func.isRequired -}
\ No newline at end of file +} diff --git a/app/javascript/vehicle_journeys/components/Filters.js b/app/javascript/vehicle_journeys/components/Filters.js index 2bd912e3e..f8697c930 100644 --- a/app/javascript/vehicle_journeys/components/Filters.js +++ b/app/javascript/vehicle_journeys/components/Filters.js @@ -46,10 +46,10 @@ export default function Filters({filters, pagination, missions, onFilter, onRese <div className='ffg-row'> {/* Plage horaire */} <div className='form-group togglable'> - <label className='control-label'>Plage horaire au départ de la course</label> + <label className='control-label'>{I18n.t("vehicle_journeys.form.departure_range.label")}</label> <div className='filter_menu'> <div className='form-group time filter_menu-item'> - <label className='control-label time'>Début</label> + <label className='control-label time'>{I18n.t("vehicle_journeys.form.departure_range.start")}</label> <div className='form-inline'> <div className='input-group time'> <input @@ -73,7 +73,7 @@ export default function Filters({filters, pagination, missions, onFilter, onRese </div> </div> <div className='form-group time filter_menu-item'> - <label className='control-label time'>Fin</label> + <label className='control-label time'>{I18n.t("vehicle_journeys.form.departure_range.end")}</label> <div className='form-inline'> <div className='input-group time'> <input @@ -101,7 +101,7 @@ export default function Filters({filters, pagination, missions, onFilter, onRese {/* Switch avec/sans horaires */} <div className='form-group has_switch'> - <label className='control-label pull-left'>Afficher les courses sans horaires</label> + <label className='control-label pull-left'>{I18n.t("vehicle_journeys.form.show_journeys_without_schedule")}</label> <div className='form-group pull-left' style={{padding: 0}}> <div className='checkbox'> <label> @@ -110,8 +110,8 @@ export default function Filters({filters, pagination, missions, onFilter, onRese onChange={onToggleWithoutSchedule} checked={filters.query.withoutSchedule} ></input> - <span className='switch-label' data-checkedvalue='Non' data-uncheckedvalue='Oui'> - {filters.query.withoutSchedule ? 'Oui' : 'Non'} + <span className='switch-label' data-checkedvalue={I18n.t("no")} data-uncheckedvalue={I18n.t("yes")}> + {filters.query.withoutSchedule ? I18n.t("yes") : I18n.t("no")} </span> </label> </div> @@ -122,7 +122,7 @@ export default function Filters({filters, pagination, missions, onFilter, onRese <div className="ffg-row"> {/* Switch avec/sans calendrier */} <div className='form-group has_switch'> - <label className='control-label pull-left'>Afficher les courses avec calendrier</label> + <label className='control-label pull-left'>{I18n.t("vehicle_journeys.form.show_journeys_with_calendar")}</label> <div className='form-group pull-left' style={{padding: 0}}> <div className='checkbox'> <label> @@ -131,8 +131,8 @@ export default function Filters({filters, pagination, missions, onFilter, onRese onChange={onToggleWithoutTimeTable} checked={filters.query.withoutTimeTable} ></input> - <span className='switch-label' data-checkedvalue='Non' data-uncheckedvalue='Oui'> - {filters.query.withoutTimeTable ? 'Oui' : 'Non'} + <span className='switch-label' data-checkedvalue={I18n.t("no")} data-uncheckedvalue={I18n.t("yes")}> + {filters.query.withoutTimeTable ? I18n.t("yes") : I18n.t("no")} </span> </label> </div> diff --git a/app/javascript/vehicle_journeys/components/Navigate.js b/app/javascript/vehicle_journeys/components/Navigate.js index 0158b8392..24843babc 100644 --- a/app/javascript/vehicle_journeys/components/Navigate.js +++ b/app/javascript/vehicle_journeys/components/Navigate.js @@ -17,8 +17,7 @@ export default function Navigate({ dispatch, vehicleJourneys, pagination, status if(status.fetchSuccess == true) { return ( <div className="pagination"> - Liste des horaires {minVJ} à {maxVJ} sur {pagination.totalCount} - + {I18n.t("vehicle_journeys.vehicle_journeys_matrix.pagination", {minVJ, maxVJ, total:pagination.totalCount})} <form className='page_links' onSubmit={e => {e.preventDefault()}}> <button onClick={e => { @@ -53,4 +52,4 @@ Navigate.propTypes = { status: PropTypes.object.isRequired, pagination: PropTypes.object.isRequired, dispatch: PropTypes.func.isRequired -}
\ No newline at end of file +} diff --git a/app/javascript/vehicle_journeys/components/ToggleArrivals.js b/app/javascript/vehicle_journeys/components/ToggleArrivals.js index 9e7089be5..9a2b0097f 100644 --- a/app/javascript/vehicle_journeys/components/ToggleArrivals.js +++ b/app/javascript/vehicle_journeys/components/ToggleArrivals.js @@ -5,7 +5,7 @@ import PropTypes from 'prop-types' export default function ToggleArrivals({filters, onToggleArrivals}) { return ( <div className='has_switch form-group inline'> - <label htmlFor='toggleArrivals' className='control-label'>Afficher et éditer les horaires d'arrivée</label> + <label htmlFor='toggleArrivals' className='control-label'>{I18n.t('vehicle_journeys.form.show_arrival_time')}</label> <div className='form-group'> <div className='checkbox'> <label> diff --git a/app/javascript/vehicle_journeys/components/Tools.js b/app/javascript/vehicle_journeys/components/Tools.js index ee02e5a68..22ea44283 100644 --- a/app/javascript/vehicle_journeys/components/Tools.js +++ b/app/javascript/vehicle_journeys/components/Tools.js @@ -44,8 +44,8 @@ export default class Tools extends Component { <DeleteVehicleJourneys disabled={!this.hasPolicy("destroy") || !editMode}/> </ul> - <span className='info-msg'>{actions.getSelected(vehicleJourneys).length} course(s) sélectionnée(s)</span> - <button className='btn btn-xs btn-link pull-right' onClick={onCancelSelection}>Annuler la sélection</button> + <span className='info-msg'>{I18n.t('vehicle_journeys.vehicle_journeys_matrix.selected_journeys', {count: actions.getSelected(vehicleJourneys).length})}</span> + <button className='btn btn-xs btn-link pull-right' onClick={onCancelSelection}>{I18n.t('vehicle_journeys.vehicle_journeys_matrix.cancel_selection')}</button> </div> ) } diff --git a/app/javascript/vehicle_journeys/components/VehicleJourney.js b/app/javascript/vehicle_journeys/components/VehicleJourney.js index 99a458f50..e11e91497 100644 --- a/app/javascript/vehicle_journeys/components/VehicleJourney.js +++ b/app/javascript/vehicle_journeys/components/VehicleJourney.js @@ -23,7 +23,7 @@ export default class VehicleJourney extends Component { 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 : '#4B4B4B')}}></span></a> + <a href={ttURL} title={I18n.t('vehicle_journeys.vehicle_journeys_matrix.show_timetable')}><span className='fa fa-calendar' style={{ color: (tt.color ? tt.color : '#4B4B4B')}}></span></a> ) } @@ -32,7 +32,7 @@ export default class VehicleJourney extends Component { 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> + <a href={ttURL} title={I18n.t('vehicle_journeys.vehicle_journeys_matrix.show_purchase_window')}><span className='fa fa-calendar' style={{color: (tt.color ? tt.color : '')}}></span></a> ) } @@ -48,12 +48,24 @@ export default class VehicleJourney extends Component { } } + hasTimeTable(time_tables, tt) { + let found = false + time_tables.map((t, index) => { + if(t.id == tt.id){ + found = true + return + } + }) + return found + } + isDisabled(bool1, bool2) { return (bool1 || bool2) } render() { this.previousCity = undefined + let detailed_calendars = this.hasFeature('detailed_calendars') && !this.disabled let {time_tables, purchase_windows} = this.props.value return ( @@ -65,23 +77,23 @@ export default class VehicleJourney extends Component { } > <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.published_journey_name && this.props.value.published_journey_name != I18n.t('undefined') ? this.props.value.published_journey_name : '-'}</div> <div>{this.props.value.journey_pattern.short_id || '-'}</div> <div>{this.props.value.company ? this.props.value.company.name : '-'}</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> {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> - } {!this.props.disabled && <div className={(this.props.value.deletable ? 'disabled ' : '') + 'checkbox'}> <input id={this.props.index} @@ -94,13 +106,22 @@ export default class VehicleJourney extends Component { ></input> <label htmlFor={this.props.index}></label> </div>} + {this.props.disabled && <VehicleJourneyInfoButton vehicleJourney={this.props.value} />} + + { detailed_calendars && + <div className="detailed-timetables hidden"> + {this.props.allTimeTables.map((tt, i) => + <div key={i} className={(this.hasTimeTable(time_tables, tt) ? "active" : "inactive")}></div> + )} + </div> + } </div> {this.props.value.vehicle_journey_at_stops.map((vj, i) => <div key={i} className='td text-center'> <div className={'cellwrap' + (this.cityNameChecker(vj) ? ' headlined' : '')}> {this.props.filters.toggleArrivals && - <div data-headline='Arrivée à'> + <div data-headline={I18n.t("vehicle_journeys.form.arrival_at")}> <span className={((this.isDisabled(this.props.value.deletable, vj.dummy) || this.props.filters.policy['vehicle_journeys.update'] == false) ? 'disabled ' : '') + 'input-group time'}> <input type='number' @@ -131,7 +152,7 @@ export default class VehicleJourney extends Component { <span className='sb sb-chrono sb-lg text-warning' data-textinside={vj.delta}></span> } </div> - <div data-headline='Départ à'> + <div data-headline={I18n.t("vehicle_journeys.form.departure_at")}> <span className={((this.isDisabled(this.props.value.deletable, vj.dummy) || this.props.filters.policy['vehicle_journeys.update'] == false) ? 'disabled ' : '') + 'input-group time'}> <input type='number' @@ -174,4 +195,5 @@ VehicleJourney.propTypes = { onUpdateTime: PropTypes.func.isRequired, onSelectVehicleJourney: PropTypes.func.isRequired, vehicleJourneys: PropTypes.object.isRequired, + allTimeTables: PropTypes.array.isRequired, } diff --git a/app/javascript/vehicle_journeys/components/VehicleJourneys.js b/app/javascript/vehicle_journeys/components/VehicleJourneys.js index ae852b35a..843aec1a8 100644 --- a/app/javascript/vehicle_journeys/components/VehicleJourneys.js +++ b/app/javascript/vehicle_journeys/components/VehicleJourneys.js @@ -12,6 +12,7 @@ export default class VehicleJourneys extends Component { this.stopPoints(), this.props.filters.features ) + this.toggleTimetables = this.toggleTimetables.bind(this) } isReturn() { @@ -48,9 +49,35 @@ export default class VehicleJourneys extends Component { return this.headerManager.showHeader(object_id) } + allTimeTables() { + if(this._allTimeTables){ + return this._allTimeTables + } + let keys = [] + this._allTimeTables = [] + this.vehicleJourneysList().map((vj, index) => { + vj.time_tables.map((tt, _) => { + if(keys.indexOf(tt.id) < 0){ + keys.push(tt.id) + this._allTimeTables.push(tt) + } + }) + }) + return this._allTimeTables + } + + toggleTimetables(e) { + $('.table-2entries .detailed-timetables').toggleClass('hidden') + $('.table-2entries .detailed-timetables-bt').toggleClass('active') + this.componentDidUpdate() + e.preventDefault() + false + } + componentDidUpdate(prevProps, prevState) { if(this.props.status.isFetching == false){ $('.table-2entries').each(function() { + $(this).find('.th').css('height', 'auto') var refH = [] var refCol = [] @@ -91,9 +118,19 @@ export default class VehicleJourneys extends Component { } } + 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 : '#4B4B4B')}}></span>{tt.days || tt.comment}</a> + ) + } + render() { this.previousBreakpoint = undefined - + this._allTimeTables = null + let detailed_calendars = this.hasFeature('detailed_calendars') && !this.isReturn() && (this.allTimeTables().length > 0) if(this.props.status.isFetching == true) { return ( <div className="isLoading" style={{marginTop: 80, marginBottom: 80}}> @@ -106,14 +143,14 @@ export default class VehicleJourneys extends Component { <div className='col-lg-12'> {(this.props.status.fetchSuccess == false) && ( <div className='alert alert-danger mt-sm'> - <strong>Erreur : </strong> - la récupération des missions a rencontré un problème. Rechargez la page pour tenter de corriger le problème. + <strong>{I18n.tc("error")}</strong> + {I18n.t("vehicle_journeys.vehicle_journeys_matrix.fetching_error")} </div> )} { this.vehicleJourneysList().errors && this.vehicleJourneysList().errors.length && _.some(this.vehicleJourneysList(), 'errors') && ( <div className="alert alert-danger mt-sm"> - <strong>Erreur : </strong> + <strong>{I18n.tc("error")}</strong> {this.vehicleJourneysList().map((vj, index) => vj.errors && vj.errors.map((err, i) => { return ( @@ -129,12 +166,32 @@ export default class VehicleJourneys extends Component { <div className={'table table-2entries mt-sm mb-sm' + ((this.vehicleJourneysList().length > 0) ? '' : ' no_result')}> <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>Transporteur</div> - <div>Calendriers</div> - { this.hasFeature('purchase_windows') && <div>Calendriers Commerciaux</div> } + <div className='strong mb-xs'>{I18n.attribute_name("vehicle_journey", "id")}</div> + <div>{I18n.attribute_name("vehicle_journey", "name")}</div> + <div>{I18n.attribute_name("vehicle_journey", "journey_pattern_id")}</div> + <div>{I18n.model_name("company")}</div> + { this.hasFeature('purchase_windows') && <div>{I18n.model_name("purchase_window", "plural": true)}</div> } + <div> + { detailed_calendars && + <a href='#' onClick={this.toggleTimetables} className='detailed-timetables-bt'> + <span className='fa fa-angle-up'></span> + {I18n.model_name("time_table", "plural": true)} + </a> + } + { !detailed_calendars && I18n.model_name("time_table", "plural": true)} + </div> + { !this.isReturn() && + <div className="detailed-timetables hidden"> + {this.allTimeTables().map((tt, i)=> + <div key={i}> + <p> + {this.timeTableURL(tt)} + </p> + <p>{tt.bounding_dates}</p> + </div> + )} + </div> + } </div> {this.stopPoints().map((sp, i) =>{ return ( @@ -159,6 +216,7 @@ export default class VehicleJourneys extends Component { onSelectVehicleJourney={this.props.onSelectVehicleJourney} vehicleJourneys={this} disabled={this.isReturn()} + allTimeTables={this.allTimeTables()} /> )} </div> diff --git a/app/javascript/vehicle_journeys/components/tools/EditVehicleJourney.js b/app/javascript/vehicle_journeys/components/tools/EditVehicleJourney.js index f6a0e3c61..d3c01f154 100644 --- a/app/javascript/vehicle_journeys/components/tools/EditVehicleJourney.js +++ b/app/javascript/vehicle_journeys/components/tools/EditVehicleJourney.js @@ -59,7 +59,7 @@ export default class EditVehicleJourney extends Component { <div className='modal-dialog'> <div className='modal-content'> <div className='modal-header'> - <h4 className='modal-title'>Informations</h4> + <h4 className='modal-title'>{I18n.t('vehicle_journeys.form.infos')}</h4> <span type="button" className="close modal-close" data-dismiss="modal">×</span> </div> @@ -69,7 +69,7 @@ export default class EditVehicleJourney extends Component { <div className='row'> <div className='col-lg-6 col-md-6 col-sm-6 col-xs-12'> <div className='form-group'> - <label className='control-label'>Nom de la course</label> + <label className='control-label'>{I18n.attribute_name('vehicle_journey', 'journey_name')}</label> <input type='text' ref='published_journey_name' @@ -82,7 +82,7 @@ export default class EditVehicleJourney extends Component { </div> <div className='col-lg-6 col-md-6 col-sm-6 col-xs-12'> <div className='form-group'> - <label className='control-label'>Mission</label> + <label className='control-label'>{I18n.attribute_name('vehicle_journey', 'journey_pattern')}</label> <input type='text' className='form-control' @@ -96,7 +96,7 @@ export default class EditVehicleJourney extends Component { <div className='row'> <div className='col-lg-6 col-md-6 col-sm-6 col-xs-12'> <div className='form-group'> - <label className='control-label'>Numéro de train</label> + <label className='control-label'>{I18n.attribute_name('vehicle_journey', 'company')}</label> <input type='text' ref='published_journey_identifier' @@ -109,7 +109,7 @@ export default class EditVehicleJourney extends Component { </div> <div className='col-lg-6 col-md-6 col-sm-6 col-xs-12'> <div className='form-group'> - <label className='control-label'>Transporteur</label> + <label className='control-label'>{I18n.attribute_name('vehicle_journey', 'company')}</label> <CompanySelect2 editModal={this.props.modal.type == "edit"} editMode={this.editMode()} @@ -124,29 +124,29 @@ export default class EditVehicleJourney extends Component { <div className='row'> <div className='col-lg-6 col-md-6 col-sm-6 col-xs-12'> <div className='form-group'> - <label className='control-label'>Mode de transport</label> + <label className='control-label'>{I18n.attribute_name('vehicle_journey', 'transport_mode')}</label> <input type='text' className='form-control' - value={window.I18n.fr.enumerize.transport_mode[this.props.modal.modalProps.vehicleJourney.transport_mode]} + value={I18n.enumerize('transport_mode', this.props.modal.modalProps.vehicleJourney.transport_mode)} disabled={true} /> </div> </div> <div className='col-lg-6 col-md-6 col-sm-6 col-xs-12'> <div className='form-group'> - <label className='control-label'>Sous mode de transport</label> + <label className='control-label'>{I18n.attribute_name('vehicle_journey', 'transport_submode')}</label> <input type='text' className='form-control' - value={window.I18n.fr.enumerize.transport_submode[this.props.modal.modalProps.vehicleJourney.transport_submode]} + value={I18n.enumerize('transport_submode', this.props.modal.modalProps.vehicleJourney.transport_submode)} disabled={true} /> </div> </div> </div> <div className='form-group'> - <label className='control-label'>Signature métier</label> + <label className='control-label'>{I18n.attribute_name('vehicle_journey', 'checksum')}</label> <input type='text' ref='checksum' diff --git a/app/javascript/vehicle_journeys/components/tools/VehicleJourneyInfoButton.js b/app/javascript/vehicle_journeys/components/tools/VehicleJourneyInfoButton.js index a63a1d701..538bbdbd6 100644 --- a/app/javascript/vehicle_journeys/components/tools/VehicleJourneyInfoButton.js +++ b/app/javascript/vehicle_journeys/components/tools/VehicleJourneyInfoButton.js @@ -10,7 +10,7 @@ export default class VehicleJourneyInfoButton extends Component { render() { return ( - <li className='st_action'> + <div className='info-button'> <button type='button' data-toggle='modal' @@ -19,7 +19,7 @@ export default class VehicleJourneyInfoButton extends Component { > <span className='fa fa-info'></span> </button> - </li> + </div> ) } } diff --git a/app/javascript/vehicle_journeys/components/tools/select2s/MissionSelect2.js b/app/javascript/vehicle_journeys/components/tools/select2s/MissionSelect2.js index 7ab85a1ea..72dbd0152 100644 --- a/app/javascript/vehicle_journeys/components/tools/select2s/MissionSelect2.js +++ b/app/javascript/vehicle_journeys/components/tools/select2s/MissionSelect2.js @@ -29,11 +29,11 @@ export default class BSelect4 extends Component { val = this.props.selection.selectedJPModal } } - if(this.useAjax()){ - val = val.published_name - } - else{ - if(val){ + if(val){ + if(this.useAjax()){ + val = val.published_name + } + else{ val = val.id } } diff --git a/app/javascript/vehicle_journeys/reducers/vehicleJourneys.js b/app/javascript/vehicle_journeys/reducers/vehicleJourneys.js index 1a15ec46d..383dea4a0 100644 --- a/app/javascript/vehicle_journeys/reducers/vehicleJourneys.js +++ b/app/javascript/vehicle_journeys/reducers/vehicleJourneys.js @@ -19,26 +19,17 @@ const vehicleJourney= (state = {}, action, keep) => { current_time.minute = parseInt(action.data["start_time.minute"].value) || 0 } _.each(action.stopPointsList, (sp) =>{ + let inJourney = false if(action.selectedJourneyPattern.full_schedule && action.selectedJourneyPattern.costs && action.selectedJourneyPattern.costs[prevSp.stop_area_id + "-" + sp.stop_area_id]){ let delta = parseInt(action.selectedJourneyPattern.costs[prevSp.stop_area_id + "-" + sp.stop_area_id].time) - let delta_hour = parseInt(delta/60) - let delta_minute = delta - 60*delta_hour - current_time.hour += delta_hour - current_time.minute += delta_minute - let extra_hours = parseInt(current_time.minute/60) - current_time.hour += extra_hours - current_time.minute -= extra_hours*60 - current_time.hour = current_time.hour % 24 + current_time = actions.addMinutesToTime(current_time, delta) prevSp = sp + inJourney = true } let offsetHours = sp.time_zone_offset / 3600 let offsetminutes = sp.time_zone_offset/60 - 60*offsetHours let newVjas = { delta: 0, - departure_time:{ - hour: (24 + current_time.hour + offsetHours) % 24, - minute: current_time.minute + offsetminutes - }, arrival_time:{ hour: (24 + current_time.hour + offsetHours) % 24, minute: current_time.minute + offsetminutes @@ -47,6 +38,16 @@ const vehicleJourney= (state = {}, action, keep) => { stop_area_cityname: sp.city_name, dummy: true } + + if(sp.waiting_time && inJourney){ + current_time = actions.addMinutesToTime(current_time, parseInt(sp.waiting_time)) + } + + newVjas.departure_time = { + hour: (24 + current_time.hour + offsetHours) % 24, + minute: current_time.minute + offsetminutes + } + if(current_time.hour + offsetHours > 24){ newVjas.departure_day_offset = 1 newVjas.arrival_day_offset = 1 diff --git a/app/models/calendar.rb b/app/models/calendar.rb index 561a2e3f7..84b569ab4 100644 --- a/app/models/calendar.rb +++ b/app/models/calendar.rb @@ -10,8 +10,9 @@ class Calendar < ActiveRecord::Base has_paper_trail class_name: 'PublicVersion' belongs_to :organisation + belongs_to :workgroup - validates_presence_of :name, :short_name, :organisation + validates_presence_of :name, :short_name, :organisation, :workgroup validates_uniqueness_of :short_name has_many :time_tables diff --git a/app/models/chouette/company.rb b/app/models/chouette/company.rb index b3d40ab96..53e412600 100644 --- a/app/models/chouette/company.rb +++ b/app/models/chouette/company.rb @@ -15,6 +15,5 @@ module Chouette [:organizational_unit, :operating_department_name, :code, :phone, :fax, :email, :url, :time_zone] end - end end diff --git a/app/models/chouette/journey_pattern.rb b/app/models/chouette/journey_pattern.rb index aa9fdb810..830e985d9 100644 --- a/app/models/chouette/journey_pattern.rb +++ b/app/models/chouette/journey_pattern.rb @@ -170,5 +170,21 @@ module Chouette end full end + + def set_distances distances + raise "inconsistent data: #{distances.count} values for #{stop_points.count} stops" unless distances.count == stop_points.count + prev = distances[0].to_i + _costs = self.costs + distances[1..-1].each_with_index do |distance, i| + distance = distance.to_i + relative = distance - prev + prev = distance + start, stop = stop_points[i..i+1] + key = "#{start.stop_area_id}-#{stop.stop_area_id}" + _costs[key] ||= {} + _costs[key]["distance"] = relative + end + self.costs = _costs + end end end diff --git a/app/models/chouette/line.rb b/app/models/chouette/line.rb index ba2e2755d..d077d5c9d 100644 --- a/app/models/chouette/line.rb +++ b/app/models/chouette/line.rb @@ -41,6 +41,7 @@ module Chouette validates_presence_of :name + scope :by_text, ->(text) { where('lower(name) LIKE :t or lower(published_name) LIKE :t or lower(objectid) LIKE :t or lower(comment) LIKE :t or lower(number) LIKE :t', t: "%#{text.downcase}%") } @@ -80,6 +81,14 @@ module Chouette line_referential.companies.where(id: ([company_id] + Array(secondary_company_ids)).compact) end + def deactivate + self.deactivated = true + end + + def activate + self.deactivated = false + end + def deactivate! update_attribute :deactivated, true end diff --git a/app/models/chouette/purchase_window.rb b/app/models/chouette/purchase_window.rb index 334493015..157390a21 100644 --- a/app/models/chouette/purchase_window.rb +++ b/app/models/chouette/purchase_window.rb @@ -19,6 +19,7 @@ module Chouette scope :contains_date, ->(date) { where('date ? <@ any (date_ranges)', date) } scope :overlap_dates, ->(date_range) { where('daterange(?, ?) && any (date_ranges)', date_range.first, date_range.last + 1.day) } + scope :matching_dates, ->(date_range) { where('ARRAY[daterange(?, ?)] = date_ranges', date_range.first, date_range.last + 1.day) } def self.ransackable_scopes(auth_object = nil) [:contains_date] diff --git a/app/models/chouette/route.rb b/app/models/chouette/route.rb index 47c18af09..3729deb7d 100644 --- a/app/models/chouette/route.rb +++ b/app/models/chouette/route.rb @@ -133,7 +133,7 @@ module Chouette def checksum_attributes values = self.slice(*['name', 'published_name', 'wayback']).values values.tap do |attrs| - attrs << self.stop_points.sort_by(&:position).map{|sp| "#{sp.stop_area.user_objectid}#{sp.for_boarding}#{sp.for_alighting}" }.join + attrs << self.stop_points.sort_by(&:position).map{|sp| [sp.stop_area.user_objectid, sp.for_boarding, sp.for_alighting]} attrs << self.routing_constraint_zones.map(&:checksum) end end @@ -185,6 +185,12 @@ module Chouette return true end + def full_journey_pattern + journey_pattern = journey_patterns.find_or_create_by registration_number: self.number, name: self.name + journey_pattern.stop_points = self.stop_points + journey_pattern + end + protected def self.vehicle_journeys_timeless(stop_point_id) diff --git a/app/models/chouette/routing_constraint_zone.rb b/app/models/chouette/routing_constraint_zone.rb index 903922241..58703598e 100644 --- a/app/models/chouette/routing_constraint_zone.rb +++ b/app/models/chouette/routing_constraint_zone.rb @@ -25,7 +25,9 @@ module Chouette end def checksum_attributes - self.stop_points.map(&:stop_area).map(&:user_objectid) + [ + self.stop_points.map(&:stop_area).map(&:user_objectid) + ] end def stop_points_belong_to_route diff --git a/app/models/chouette/stop_area.rb b/app/models/chouette/stop_area.rb index bb8747faa..7170dd217 100644 --- a/app/models/chouette/stop_area.rb +++ b/app/models/chouette/stop_area.rb @@ -369,6 +369,14 @@ module Chouette !activated? end + def activate + self.deleted_at = nil + end + + def deactivate + self.deleted_at = Time.now + end + def activate! update_attribute :deleted_at, nil end @@ -384,8 +392,8 @@ module Chouette def country_name return unless country_code - country = ISO3166::Country[country_code] + return unless country country.translations[I18n.locale.to_s] || country.name end diff --git a/app/models/chouette/time_table.rb b/app/models/chouette/time_table.rb index 15b22b671..b76de852a 100644 --- a/app/models/chouette/time_table.rb +++ b/app/models/chouette/time_table.rb @@ -44,10 +44,10 @@ module Chouette attrs << self.int_day_types dates = self.dates dates += TimeTableDate.where(time_table_id: self.id) - attrs << dates.map(&:checksum).map(&:to_s).sort + attrs << dates.map(&:checksum).map(&:to_s).uniq.sort periods = self.periods periods += TimeTablePeriod.where(time_table_id: self.id) - attrs << periods.map(&:checksum).map(&:to_s).sort + attrs << periods.map(&:checksum).map(&:to_s).uniq.sort end end diff --git a/app/models/chouette/vehicle_journey.rb b/app/models/chouette/vehicle_journey.rb index 4a6ba3f75..1a79db823 100644 --- a/app/models/chouette/vehicle_journey.rb +++ b/app/models/chouette/vehicle_journey.rb @@ -105,7 +105,7 @@ module Chouette attrs << self.try(:company).try(:get_objectid).try(:local_id) attrs << self.footnotes.map(&:checksum).sort vjas = self.vehicle_journey_at_stops - vjas += VehicleJourneyAtStop.where(vehicle_journey_id: self.id) + vjas += VehicleJourneyAtStop.where(vehicle_journey_id: self.id) unless self.new_record? attrs << vjas.uniq.sort_by { |s| s.stop_point&.position }.map(&:checksum).sort end end @@ -381,8 +381,8 @@ module Chouette end def self.lines - lines_query = joins(:route).select("routes.line_id").to_sql - Chouette::Line.where("id IN (#{lines_query})") + lines_query = joins(:route).select("routes.line_id").reorder(nil).except(:group).pluck(:'routes.line_id') + Chouette::Line.where(id: lines_query) end end end diff --git a/app/models/chouette/vehicle_journey_at_stop.rb b/app/models/chouette/vehicle_journey_at_stop.rb index eda711ade..3b4f35f13 100644 --- a/app/models/chouette/vehicle_journey_at_stop.rb +++ b/app/models/chouette/vehicle_journey_at_stop.rb @@ -41,7 +41,7 @@ module Chouette :arrival_day_offset, I18n.t( 'vehicle_journey_at_stops.errors.day_offset_must_not_exceed_max', - short_id: vehicle_journey.get_objectid.short_id, + short_id: vehicle_journey&.get_objectid&.short_id, max: DAY_OFFSET_MAX + 1 ) ) @@ -52,7 +52,7 @@ module Chouette :departure_day_offset, I18n.t( 'vehicle_journey_at_stops.errors.day_offset_must_not_exceed_max', - short_id: vehicle_journey.get_objectid.short_id, + short_id: vehicle_journey&.get_objectid&.short_id, max: DAY_OFFSET_MAX + 1 ) ) @@ -69,8 +69,8 @@ module Chouette def checksum_attributes [].tap do |attrs| - attrs << self.departure_time.try(:to_s, :time) - attrs << self.arrival_time.try(:to_s, :time) + attrs << self.departure_time&.utc.try(:to_s, :time) + attrs << self.arrival_time&.utc.try(:to_s, :time) attrs << self.departure_day_offset.to_s attrs << self.arrival_day_offset.to_s end diff --git a/app/models/chouette/vehicle_journey_at_stops_day_offset.rb b/app/models/chouette/vehicle_journey_at_stops_day_offset.rb index b2cb90d11..7497cd72c 100644 --- a/app/models/chouette/vehicle_journey_at_stops_day_offset.rb +++ b/app/models/chouette/vehicle_journey_at_stops_day_offset.rb @@ -11,13 +11,19 @@ module Chouette @at_stops.inject(nil) do |prior_stop, stop| next stop if prior_stop.nil? - if stop.arrival_time < prior_stop.departure_time || - stop.arrival_time < prior_stop.arrival_time + # we only compare time of the day, not actual times + stop_arrival_time = stop.arrival_time - stop.arrival_time.to_date.to_time + stop_departure_time = stop.departure_time - stop.departure_time.to_date.to_time + prior_stop_arrival_time = prior_stop.arrival_time - prior_stop.arrival_time.to_date.to_time + prior_stop_departure_time = prior_stop.departure_time - prior_stop.departure_time.to_date.to_time + + if stop_arrival_time < prior_stop_departure_time || + stop_arrival_time < prior_stop_arrival_time arrival_offset += 1 end - if stop.departure_time < stop.arrival_time || - stop.departure_time < prior_stop.departure_time + if stop_departure_time < stop_arrival_time || + stop_departure_time < prior_stop_departure_time departure_offset += 1 end @@ -39,4 +45,4 @@ module Chouette save end end -end
\ No newline at end of file +end diff --git a/app/models/compliance_check_set.rb b/app/models/compliance_check_set.rb index f4c44d26d..289fc134f 100644 --- a/app/models/compliance_check_set.rb +++ b/app/models/compliance_check_set.rb @@ -19,6 +19,20 @@ class ComplianceCheckSet < ActiveRecord::Base where('created_at BETWEEN :begin AND :end', begin: period_range.begin, end: period_range.end) end + scope :blocked, -> { where('created_at < ? AND status = ?', 4.hours.ago, 'running') } + + def self.finished_statuses + %w(successful failed warning aborted canceled) + end + + def self.abort_old + where( + 'created_at < ? AND status NOT IN (?)', + 4.hours.ago, + finished_statuses + ).update_all(status: 'aborted') + end + def notify_parent if parent # parent.child_change diff --git a/app/models/compliance_control.rb b/app/models/compliance_control.rb index 2bde5b95a..298a63ab9 100644 --- a/app/models/compliance_control.rb +++ b/app/models/compliance_control.rb @@ -3,8 +3,6 @@ class ComplianceControl < ActiveRecord::Base class << self def criticities; %i(warning error) end def default_code; "" end - def prerequisite; I18n.t('compliance_controls.metas.no_prerequisite'); end - def predicate; I18n.t("compliance_controls.#{self.name.underscore}.description") end def dynamic_attributes stored_attributes[:control_attributes] || [] end @@ -65,6 +63,9 @@ def initialize(attributes = {}) self.origin_code ||= self.class.default_code end +def predicate; I18n.t("compliance_controls.#{self.class.name.underscore}.description") end +def prerequisite; I18n.t('compliance_controls.metas.no_prerequisite'); end + end # Ensure STI subclasses are loaded diff --git a/app/models/concerns/checksum_support.rb b/app/models/concerns/checksum_support.rb index a76995b0f..92103798e 100644 --- a/app/models/concerns/checksum_support.rb +++ b/app/models/concerns/checksum_support.rb @@ -24,10 +24,29 @@ module ChecksumSupport self.attributes.values end + def checksum_replace_nil_or_empty_values values + # Replace empty array by nil & nil by VALUE_FOR_NIL_ATTRIBUTE + values + .map { |x| x.present? && x || VALUE_FOR_NIL_ATTRIBUTE } + .map do |item| + item = + if item.kind_of?(Array) + checksum_replace_nil_or_empty_values(item) + else + item + end + end + end + def current_checksum_source - source = self.checksum_attributes.map{ |x| x unless x.try(:empty?) } - source = source.map{ |x| x || VALUE_FOR_NIL_ATTRIBUTE } - source.map(&:to_s).join(SEPARATOR) + source = checksum_replace_nil_or_empty_values(self.checksum_attributes) + source.map{ |item| + if item.kind_of?(Array) + item.map{ |x| x.kind_of?(Array) ? "(#{x.join(',')})" : x }.join(',') + else + item + end + }.join(SEPARATOR) end def set_current_checksum_source diff --git a/app/models/concerns/min_max_values_validation.rb b/app/models/concerns/min_max_values_validation.rb index 9b2e0d548..eff779d81 100644 --- a/app/models/concerns/min_max_values_validation.rb +++ b/app/models/concerns/min_max_values_validation.rb @@ -2,6 +2,7 @@ module MinMaxValuesValidation extend ActiveSupport::Concern included do + validates_presence_of :minimum, :maximum validate :min_max_values_validation end diff --git a/app/models/concerns/timetable_support.rb b/app/models/concerns/timetable_support.rb index d2bc99d51..5242abc33 100644 --- a/app/models/concerns/timetable_support.rb +++ b/app/models/concerns/timetable_support.rb @@ -100,6 +100,7 @@ module TimetableSupport period.period_start = Date.parse(item['period_start']) period.period_end = Date.parse(item['period_end']) + period.save if period.is_a?(ActiveRecord::Base) && period.changed? item['id'] = period.id diff --git a/app/models/import.rb b/app/models/import.rb index 049a65f40..29aadcd56 100644 --- a/app/models/import.rb +++ b/app/models/import.rb @@ -13,6 +13,8 @@ class Import < ActiveRecord::Base where('started_at BETWEEN :begin AND :end', begin: period_range.begin, end: period_range.end) end + scope :blocked, -> { where('created_at < ? AND status = ?', 4.hours.ago, 'running') } + extend Enumerize enumerize :status, in: %w(new pending successful warning failed running aborted canceled), scope: true, default: :new @@ -42,6 +44,14 @@ class Import < ActiveRecord::Base %w(successful failed warning aborted canceled) end + def self.abort_old + where( + 'created_at < ? AND status NOT IN (?)', + 4.hours.ago, + finished_statuses + ).update_all(status: 'aborted') + end + def notify_parent parent.child_change update(notified_parent_at: DateTime.now) diff --git a/app/models/line_control/route.rb b/app/models/line_control/route.rb index b4b2bd9d8..b6c1f3630 100644 --- a/app/models/line_control/route.rb +++ b/app/models/line_control/route.rb @@ -3,6 +3,6 @@ module LineControl def self.default_code; "3-Line-1" end - def self.prerequisite; I18n.t("compliance_controls.#{self.name.underscore}.prerequisite") end + def prerequisite; I18n.t("compliance_controls.#{self.class.name.underscore}.prerequisite") end end end diff --git a/app/models/organisation.rb b/app/models/organisation.rb index 0598bfecf..745bc0d22 100644 --- a/app/models/organisation.rb +++ b/app/models/organisation.rb @@ -82,4 +82,8 @@ class Organisation < ActiveRecord::Base features && features.include?(feature.to_s) end + def default_workbench + workbenches.default + end + end diff --git a/app/models/referential.rb b/app/models/referential.rb index baaa354da..09c2e7d34 100644 --- a/app/models/referential.rb +++ b/app/models/referential.rb @@ -51,7 +51,9 @@ class Referential < ActiveRecord::Base belongs_to :stop_area_referential validates_presence_of :stop_area_referential has_many :stop_areas, through: :stop_area_referential + belongs_to :workbench + delegate :workgroup, to: :workbench, allow_nil: true belongs_to :referential_suite @@ -62,6 +64,7 @@ class Referential < ActiveRecord::Base scope :order_by_validity_period, ->(dir) { joins(:metadatas).order("unnest(periodes) #{dir}") } scope :order_by_lines, ->(dir) { joins(:metadatas).group("referentials.id").order("sum(array_length(referential_metadata.line_ids,1)) #{dir}") } scope :not_in_referential_suite, -> { where referential_suite_id: nil } + scope :blocked, -> { where('ready = ? AND created_at < ?', false, 4.hours.ago) } def save_with_table_lock_timeout(options = {}) save_without_table_lock_timeout(options) @@ -153,10 +156,6 @@ class Referential < ActiveRecord::Base end end - def stop_areas - Chouette::StopArea.all - end - def access_points Chouette::AccessPoint.all end diff --git a/app/models/route_control/opposite_route.rb b/app/models/route_control/opposite_route.rb index d5616ca6f..e0e9572ce 100644 --- a/app/models/route_control/opposite_route.rb +++ b/app/models/route_control/opposite_route.rb @@ -4,6 +4,6 @@ module RouteControl def self.default_code; "3-Route-2" end - def self.prerequisite; I18n.t("compliance_controls.#{self.name.underscore}.prerequisite") end + def prerequisite; I18n.t("compliance_controls.#{self.class.name.underscore}.prerequisite") end end end diff --git a/app/models/route_control/opposite_route_terminus.rb b/app/models/route_control/opposite_route_terminus.rb index 24c557734..e70d2c702 100644 --- a/app/models/route_control/opposite_route_terminus.rb +++ b/app/models/route_control/opposite_route_terminus.rb @@ -3,6 +3,6 @@ module RouteControl def self.default_code; "3-Route-5" end - def self.prerequisite; I18n.t("compliance_controls.#{self.name.underscore}.prerequisite") end + def prerequisite; I18n.t("compliance_controls.#{self.class.name.underscore}.prerequisite") end end end diff --git a/app/models/simple_importer.rb b/app/models/simple_importer.rb new file mode 100644 index 000000000..b824d596d --- /dev/null +++ b/app/models/simple_importer.rb @@ -0,0 +1,422 @@ +class SimpleImporter < ActiveRecord::Base + attr_accessor :configuration + + def self.define name + @importers ||= {} + configuration = Configuration.new name + yield configuration + configuration.validate! + @importers[name.to_sym] = configuration + end + + def self.find_configuration name + @importers ||= {} + configuration = @importers[name.to_sym] + raise "Importer not found: #{name}" unless configuration + configuration + end + + def initialize *args + super *args + self.configuration = self.class.find_configuration self.configuration_name + self.journal ||= [] + end + + def configure + new_config = configuration.duplicate + yield new_config + new_config.validate! + self.configuration = new_config + end + + def context + self.configuration.context + end + + def resolve col_name, value, &block + val = block.call(value) + return val if val.present? + @resolution_queue[[col_name.to_s, value]].push({record: @current_record, attribute: @current_attribute, block: block}) + nil + end + + def import opts={} + @verbose = opts.delete :verbose + + + @resolution_queue = Hash.new{|h,k| h[k] = []} + @errors = [] + @messages = [] + @number_of_lines = 0 + @padding = 1 + @current_line = 0 + fail_with_error "File not found: #{self.filepath}" do + @number_of_lines = CSV.read(self.filepath, self.configuration.csv_options).length + @padding = [1, Math.log(@number_of_lines, 10).ceil()].max + end + + + self.configuration.before_actions(:parsing).each do |action| action.call self end + + @statuses = "" + + if ENV["NO_TRANSACTION"] + process_csv_file + else + ActiveRecord::Base.transaction do + process_csv_file + end + end + self.status ||= :success + rescue FailedImport + self.status = :failed + ensure + self.save! + end + + def fail_with_error msg=nil, opts={} + begin + yield + rescue => e + msg = msg.call if msg.is_a?(Proc) + custom_print "\nFAILED: \n errors: #{msg}\n exception: #{e.message}\n#{e.backtrace.join("\n")}", color: :red unless self.configuration.ignore_failures + push_in_journal({message: msg, error: e.message, event: :error, kind: :error}) + @new_status = colorize("x", :red) + if self.configuration.ignore_failures + raise FailedRow if opts[:abort_row] + else + raise FailedImport + end + end + end + + def encode_string s + s.encode("utf-8").force_encoding("utf-8") + end + + def dump_csv_from_context + filepath = "./#{self.configuration_name}_#{Time.now.strftime "%y%m%d%H%M"}.csv" + # for some reason, context[:csv].to_csv does not work + CSV.open(filepath, 'w') do |csv| + header = true + context[:csv].each do |row| + csv << row.headers if header + csv << row.fields + header = false + end + end + log "CSV file dumped in #{filepath}" + end + + def log msg, opts={} + msg = colorize msg, opts[:color] if opts[:color] + if opts[:append] + @messages[-1] = (@messages[-1] || "") + msg + else + @messages << msg + end + print_state + end + + protected + + def process_csv_file + self.configuration.before_actions(:all).each do |action| action.call self end + log "Starting import ...", color: :green + + (context[:csv] || CSV.read(filepath, self.configuration.csv_options)).each do |row| + @current_row = row + @new_status = nil + begin + handle_row row + fail_with_error ->(){ @current_record.errors.messages } do + new_record = @current_record&.new_record? + @new_status ||= new_record ? colorize("✓", :green) : colorize("-", :orange) + @event = new_record ? :creation : :update + self.configuration.before_actions(:each_save).each do |action| + action.call self, @current_record + end + ### This could fail if the record has a mandatory relation which is not yet resolved + ### TODO: do not attempt to save if the current record if waiting for resolution + ### and fail at the end if there remains unresolved relations + if @current_record + if self.configuration.ignore_failures + unless @current_record.save + @new_status = colorize("x", :red) + push_in_journal({message: "errors: #{@current_record.errors.messages}", error: "invalid record", event: :error, kind: :error}) + end + else + @current_record.save! + end + end + self.configuration.after_actions(:each_save).each do |action| + action.call self, @current_record + end + end + rescue FailedRow + @new_status = colorize("x", :red) + end + push_in_journal({event: @event, kind: :log}) if @current_record&.valid? + @statuses += @new_status + self.configuration.columns.each do |col| + if @current_record && col.name && @resolution_queue.any? + val = @current_record.send col[:attribute] + (@resolution_queue.delete([col.name, val]) || []).each do |res| + record = res[:record] + attribute = res[:attribute] + value = res[:block].call(val, record) + record.send "#{attribute}=", value + record.save! + end + end + end + print_state + @current_line += 1 + end + + begin + self.configuration.after_actions(:all).each do |action| + action.call self + end + rescue FailedRow + end + end + + def handle_row row + if self.configuration.get_custom_handler + instance_exec(row, &self.configuration.get_custom_handler) + else + fail_with_error "", abort_row: true do + @current_record = self.configuration.find_record row + self.configuration.columns.each do |col| + @current_attribute = col[:attribute] + val = col[:value] + if val.nil? || val.is_a?(Proc) + if row.has_key? col.name + if val.is_a?(Proc) + val = instance_exec(row[col.name], &val) + else + val = row[col.name] + end + else + push_in_journal({event: :column_not_found, message: "Column not found: #{col.name}", kind: :warning}) + self.status ||= :success_with_warnings + end + end + + if val.nil? && col.required? + raise "MISSING VALUE FOR COLUMN #{col.name}" + end + val = encode_string(val) if val.is_a?(String) + @current_record.send "#{@current_attribute}=", val if val + end + end + end + end + + def push_in_journal data + line = @current_line + 1 + line += 1 if configuration.headers + self.journal.push data.update(line: line, row: @current_row) + if data[:kind] == :error || data[:kind] == :warning + @errors.push data + end + end + + def colorize txt, color + color = { + red: "31", + green: "32", + orange: "33", + }[color] || "33" + "\e[#{color}m#{txt}\e[0m" + end + + def print_state + return unless @verbose + + @status_width ||= begin + term_width = %x(tput cols).to_i + term_width - @padding - 10 + rescue + 100 + end + + @status_height ||= begin + term_height = %x(tput lines).to_i + term_height - 3 + rescue + 50 + end + + full_status = @statuses || "" + full_status = full_status.last(@status_width*10) || "" + padding_size = [(@number_of_lines - @current_line - 1), (@status_width - full_status.size/10)].min + full_status = "#{full_status}#{"."*[padding_size, 0].max}" + + msg = "#{"%#{@padding}d" % (@current_line + 1)}/#{@number_of_lines}: #{full_status}" + + lines_count = [(@status_height / 2) - 3, 1].max + + if @messages.any? + msg += "\n\n" + msg += colorize "=== MESSAGES (#{@messages.count}) ===\n", :green + msg += "[...]\n" if @messages.count > lines_count + msg += @messages.last(lines_count).join("\n") + msg += "\n"*[lines_count-@messages.count, 0].max + end + + if @errors.any? + msg += "\n\n" + msg += colorize "=== ERRORS (#{@errors.count}) ===\n", :red + msg += "[...]\n" if @errors.count > lines_count + msg += @errors.last(lines_count).map do |j| + kind = j[:kind] + kind = colorize(kind, kind == :error ? :red : :orange) + encode_string "[#{kind}]\t\tL#{j[:line]}\t#{j[:error]}\t\t#{j[:message]}" + end.join("\n") + end + custom_print msg, clear: true + end + + def custom_print msg, opts={} + return unless @verbose + out = "" + msg = colorize(msg, opts[:color]) if opts[:color] + puts "\e[H\e[2J" if opts[:clear] + out += msg + print out + end + + class FailedImport < RuntimeError + end + + class FailedRow < RuntimeError + end + + class Configuration + attr_accessor :model, :headers, :separator, :key, :context, :encoding, :ignore_failures, :scope + attr_reader :columns + + def initialize import_name, opts={} + @import_name = import_name + @key = opts[:key] || "id" + @headers = opts.has_key?(:headers) ? opts[:headers] : true + @separator = opts[:separator] || "," + @encoding = opts[:encoding] + @columns = opts[:columns] || [] + @model = opts[:model] + @custom_handler = opts[:custom_handler] + @before = opts[:before] + @after = opts[:after] + @ignore_failures = opts[:ignore_failures] + @context = opts[:context] || {} + @scope = opts[:scope] + end + + def duplicate + Configuration.new @import_name, self.options + end + + def options + { + key: @key, + headers: @headers, + separator: @separator, + encoding: @encoding, + columns: @columns.map(&:duplicate), + model: model, + custom_handler: @custom_handler, + before: @before, + after: @after, + ignore_failures: @ignore_failures, + context: @context, + scope: @scope + } + end + + def validate! + raise "Incomplete configuration, missing model for #{@import_name}" unless model.present? + end + + def attribute_for_col col_name + column = self.columns.find{|c| c.name == col_name} + column && column[:attribute] || col_name + end + + def record_scope + _scope = @scope + _scope = instance_exec(&_scope) if _scope.is_a?(Proc) + _scope || model + end + + def find_record attrs + record_scope.find_or_initialize_by(attribute_for_col(@key) => attrs[@key.to_s]) + end + + def csv_options + { + headers: self.headers, + col_sep: self.separator, + encoding: self.encoding + } + end + + def add_column name, opts={} + @columns.push Column.new({name: name.to_s}.update(opts)) + end + + def add_value attribute, value + @columns.push Column.new({attribute: attribute, value: value}) + end + + def before group=:all, &block + @before ||= Hash.new{|h, k| h[k] = []} + @before[group].push block + end + + def after group=:all, &block + @after ||= Hash.new{|h, k| h[k] = []} + @after[group].push block + end + + def before_actions group=:all + @before ||= Hash.new{|h, k| h[k] = []} + @before[group] + end + + def after_actions group=:all + @after ||= Hash.new{|h, k| h[k] = []} + @after[group] + end + + def custom_handler &block + @custom_handler = block + end + + def get_custom_handler + @custom_handler + end + + class Column + attr_accessor :name + def initialize opts={} + @name = opts[:name] + @options = opts + @options[:attribute] ||= @name + end + + def duplicate + Column.new @options.dup + end + + def required? + !!@options[:required] + end + + def [](key) + @options[key] + end + end + end +end diff --git a/app/models/user.rb b/app/models/user.rb index 1342f60ed..31e634415 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -15,6 +15,7 @@ class User < ActiveRecord::Base # Setup accessible (or protected) attributes for your model # attr_accessible :email, :password, :current_password, :password_confirmation, :remember_me, :name, :organisation_attributes belongs_to :organisation + has_many :workbenches, through: :organisation accepts_nested_attributes_for :organisation validates :organisation, :presence => true diff --git a/app/models/vehicle_journey_control/delta.rb b/app/models/vehicle_journey_control/delta.rb index f061b9fdd..737b7d78c 100644 --- a/app/models/vehicle_journey_control/delta.rb +++ b/app/models/vehicle_journey_control/delta.rb @@ -4,6 +4,7 @@ module VehicleJourneyControl store_accessor :control_attributes, :maximum validates_numericality_of :maximum, allow_nil: true, greater_than_or_equal_to: 0 + validates_presence_of :maximum def self.default_code; "3-VehicleJourney-3" end end diff --git a/app/models/vehicle_journey_control/waiting_time.rb b/app/models/vehicle_journey_control/waiting_time.rb index f2666cb72..89a18a5d9 100644 --- a/app/models/vehicle_journey_control/waiting_time.rb +++ b/app/models/vehicle_journey_control/waiting_time.rb @@ -3,6 +3,7 @@ module VehicleJourneyControl store_accessor :control_attributes, :maximum validates_numericality_of :maximum, allow_nil: true, greater_than_or_equal_to: 0 + validates_presence_of :maximum def self.default_code; "3-VehicleJourney-1" end end diff --git a/app/models/workbench.rb b/app/models/workbench.rb index b80fa64ac..eb53af7aa 100644 --- a/app/models/workbench.rb +++ b/app/models/workbench.rb @@ -1,4 +1,6 @@ class Workbench < ActiveRecord::Base + DEFAULT_WORKBENCH_NAME = "Gestion de l'offre" + include ObjectidFormatterSupport belongs_to :organisation belongs_to :line_referential @@ -40,6 +42,11 @@ class Workbench < ActiveRecord::Base end end + def self.default + self.last if self.count == 1 + where(name: DEFAULT_WORKBENCH_NAME).last + end + private def initialize_output diff --git a/app/models/workgroup.rb b/app/models/workgroup.rb index 3d761e81f..3af20ae23 100644 --- a/app/models/workgroup.rb +++ b/app/models/workgroup.rb @@ -3,6 +3,7 @@ class Workgroup < ActiveRecord::Base belongs_to :stop_area_referential has_many :workbenches + has_many :calendars has_many :organisations, through: :workbenches has_many :referentials, through: :workbenches diff --git a/app/services/parent_import_notifier.rb b/app/services/parent_import_notifier.rb deleted file mode 100644 index 47e6755e4..000000000 --- a/app/services/parent_import_notifier.rb +++ /dev/null @@ -1,15 +0,0 @@ -class ParentImportNotifier - def self.notify_when_finished(imports = nil) - imports ||= imports_pending_notification - imports.each(&:notify_parent) - end - - def self.imports_pending_notification - Import - .where( - notified_parent_at: nil, - status: Import.finished_statuses - ) - .where.not(parent: nil) - end -end diff --git a/app/services/parent_notifier.rb b/app/services/parent_notifier.rb new file mode 100644 index 000000000..653c98aff --- /dev/null +++ b/app/services/parent_notifier.rb @@ -0,0 +1,19 @@ +class ParentNotifier + def initialize(klass) + @klass = klass + end + + def notify_when_finished(collection = nil) + collection ||= objects_pending_notification + collection.each(&:notify_parent) + end + + def objects_pending_notification + @klass + .where( + notified_parent_at: nil, + status: @klass.finished_statuses + ) + .where.not(parent: nil) + end +end diff --git a/app/views/calendar_mailer/created.html.slim b/app/views/calendar_mailer/created.html.slim index 37b2a86ea..bee071150 100644 --- a/app/views/calendar_mailer/created.html.slim +++ b/app/views/calendar_mailer/created.html.slim @@ -1,4 +1,4 @@ -div = t('mailers.calendar_mailer.created.body', cal_name: @calendar.name, cal_index_url: calendars_url) +div = t('mailers.calendar_mailer.created.body', cal_name: @calendar.name, cal_index_url: workgroup_calendars_url(@calendar.workgroup)) table style="border-collapse:collapse;font-family:'Open Sans', Arial, sans serif;width:550px;margin:0px auto;color:#333333;" @@ -16,7 +16,7 @@ table style="border-collapse:collapse;font-family:'Open Sans', Arial, sans serif = t('mailers.calendar_mailer.updated.subject') p style="font-size:14px;margin:0px 0px 10px 0px;" - = t('mailers.calendar_mailer.created.body', cal_name: @calendar.name, cal_index_url: calendars_url).html_safe + = t('mailers.calendar_mailer.created.body', cal_name: @calendar.name, cal_index_url: workgroup_calendars_url(@calendar.workgroup)).html_safe tr td style="text-align:center;padding:20px 0 0 0;border-top:1px solid #007fbb;" diff --git a/app/views/calendar_mailer/updated.html.slim b/app/views/calendar_mailer/updated.html.slim index bf128439a..0bdc2e7db 100644 --- a/app/views/calendar_mailer/updated.html.slim +++ b/app/views/calendar_mailer/updated.html.slim @@ -14,7 +14,7 @@ table style="border-collapse:collapse;font-family:'Open Sans', Arial, sans serif = t('mailers.calendar_mailer.updated.subject') p style="font-size:14px;margin:0px 0px 10px 0px;" - = t('mailers.calendar_mailer.updated.body', cal_name: @calendar.name, cal_index_url: calendars_url).html_safe + = t('mailers.calendar_mailer.updated.body', cal_name: @calendar.name, cal_index_url: workgroup_calendars_url(@calendar.workgroup)).html_safe tr td style="text-align:center;padding:20px 0 0 0;border-top:1px solid #007fbb;" diff --git a/app/views/calendars/_filters.html.slim b/app/views/calendars/_filters.html.slim index d9c936b64..8bfe1974e 100644 --- a/app/views/calendars/_filters.html.slim +++ b/app/views/calendars/_filters.html.slim @@ -1,4 +1,4 @@ -= search_form_for @q, url: calendars_path, builder: SimpleForm::FormBuilder, html: { method: :get, class: 'form form-filter' } do |f| += search_form_for @q, url: workgroup_calendars_path(@workgroup), builder: SimpleForm::FormBuilder, html: { method: :get, class: 'form form-filter' } do |f| .ffg-row .input-group.search_bar class=filter_item_class(params[:q], :name_or_short_name_cont) = f.search_field :name_or_short_name_cont, class: 'form-control', placeholder: 'Indiquez un nom/nom court de calendrier...' @@ -18,5 +18,5 @@ = f.input :contains_date, as: :date, label: false, wrapper_html: { class: 'date smart_date' }, class: 'form-control', include_blank: true .actions - = link_to 'Effacer', calendars_path, class: 'btn btn-link' + = link_to 'Effacer', workgroup_calendars_path(@workgroup), class: 'btn btn-link' = f.submit 'Filtrer', id: 'calendar_filter_btn', class: 'btn btn-default' diff --git a/app/views/calendars/_form_advanced.html.slim b/app/views/calendars/_form_advanced.html.slim index b4154166b..e796e2e36 100644 --- a/app/views/calendars/_form_advanced.html.slim +++ b/app/views/calendars/_form_advanced.html.slim @@ -2,7 +2,7 @@ = javascript_tag do | window.actionType = "#{raw params[:action]}"; - | window.I18n = #{(I18n.backend.send(:translations)[I18n.locale].to_json).html_safe}; + // | window.I18n = #{(I18n.backend.send(:translations)[I18n.locale].to_json).html_safe}; | window.timetablesUrl = "#{calendar_url(@calendar).html_safe}"; = javascript_pack_tag 'calendars/edit.js' diff --git a/app/views/calendars/_form_simple.html.slim b/app/views/calendars/_form_simple.html.slim index 2f469ada7..ba18c765b 100644 --- a/app/views/calendars/_form_simple.html.slim +++ b/app/views/calendars/_form_simple.html.slim @@ -1,6 +1,6 @@ .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 @calendar, html: { class: 'form-horizontal', id: 'calendar_form' }, wrapper: :horizontal_form do |f| + = simple_form_for [@workgroup, @calendar], html: { class: 'form-horizontal', id: 'calendar_form' }, wrapper: :horizontal_form do |f| .row .col-lg-12 = f.input :name diff --git a/app/views/calendars/edit.html.slim b/app/views/calendars/edit.html.slim index e64790daf..79ab1f5d0 100644 --- a/app/views/calendars/edit.html.slim +++ b/app/views/calendars/edit.html.slim @@ -1,4 +1,4 @@ -- breadcrumb :calendar, @calendar +- breadcrumb :calendar, @workgroup, @calendar - page_header_content_for @calendar .page_content .container-fluid diff --git a/app/views/calendars/index.html.slim b/app/views/calendars/index.html.slim index 92c24be5b..0b58c0c72 100644 --- a/app/views/calendars/index.html.slim +++ b/app/views/calendars/index.html.slim @@ -1,4 +1,4 @@ -- breadcrumb :calendars +- breadcrumb :calendars, workgroup .page_content .container-fluid @@ -16,7 +16,7 @@ key: :name, \ attribute: 'name', \ link_to: lambda do |calendar| \ - calendar_path(calendar) \ + workgroup_calendar_path(workgroup, calendar) \ end \ ), \ TableBuilderHelper::Column.new( \ diff --git a/app/views/calendars/new.html.slim b/app/views/calendars/new.html.slim index b3840b705..5657a0c55 100644 --- a/app/views/calendars/new.html.slim +++ b/app/views/calendars/new.html.slim @@ -1,4 +1,4 @@ -- breadcrumb :calendars +- breadcrumb :calendars, @workgroup .page_content .container-fluid = render 'form_simple' diff --git a/app/views/calendars/show.html.slim b/app/views/calendars/show.html.slim index ec53be0ef..cec4f66a5 100644 --- a/app/views/calendars/show.html.slim +++ b/app/views/calendars/show.html.slim @@ -1,4 +1,4 @@ -- breadcrumb :calendar, @calendar +- breadcrumb :calendar, @workgroup, @calendar - page_header_content_for @calendar .page_content diff --git a/app/views/compliance_check_sets/index.html.slim b/app/views/compliance_check_sets/index.html.slim index ead467174..31ad31e5b 100644 --- a/app/views/compliance_check_sets/index.html.slim +++ b/app/views/compliance_check_sets/index.html.slim @@ -23,9 +23,9 @@ ), \ TableBuilderHelper::Column.new( \ key: :associated_object, \ - attribute: Proc.new{|n| n.referential.name}, \ + attribute: Proc.new{|n| n.referential.present? ? n.referential.name : ''}, \ link_to: lambda do |compliance_check_set| \ - referential_path(compliance_check_set.referential_id) \ + compliance_check_set.referential.present? ? referential_path(compliance_check_set.referential_id) : '#' \ end \ ), \ TableBuilderHelper::Column.new( \ diff --git a/app/views/compliance_controls/new.html.slim b/app/views/compliance_controls/new.html.slim index f7f47fba3..c0abc522f 100644 --- a/app/views/compliance_controls/new.html.slim +++ b/app/views/compliance_controls/new.html.slim @@ -6,5 +6,5 @@ = render 'form' = definition_list t('metadatas'), - I18n.t('activerecord.attributes.compliance_control.predicate') => @compliance_control.class.predicate, - I18n.t('activerecord.attributes.compliance_control.prerequisite') => @compliance_control.class.prerequisite + I18n.t('activerecord.attributes.compliance_control.predicate') => @compliance_control.predicate, + I18n.t('activerecord.attributes.compliance_control.prerequisite') => @compliance_control.prerequisite diff --git a/app/views/compliance_controls/show.html.slim b/app/views/compliance_controls/show.html.slim index 8a65bb864..ab25747a9 100644 --- a/app/views/compliance_controls/show.html.slim +++ b/app/views/compliance_controls/show.html.slim @@ -13,8 +13,8 @@ ComplianceControl.human_attribute_name(:code) => @compliance_control.code, ComplianceControl.human_attribute_name(:criticity) => @compliance_control.criticity, ComplianceControl.human_attribute_name(:comment) => @compliance_control.comment, - I18n.t('activerecord.attributes.compliance_control.predicate') => @compliance_control.class.predicate, - I18n.t('activerecord.attributes.compliance_control.prerequisite') => @compliance_control.class.prerequisite, + I18n.t('activerecord.attributes.compliance_control.predicate') => @compliance_control.predicate, + I18n.t('activerecord.attributes.compliance_control.prerequisite') => @compliance_control.prerequisite, }.merge( \ {}.tap do |hash| \ @compliance_control.class.dynamic_attributes.each do |attribute| \ diff --git a/app/views/dashboards/_dashboard.html.slim b/app/views/dashboards/_dashboard.html.slim index 075b94ddc..7f78934a6 100644 --- a/app/views/dashboards/_dashboard.html.slim +++ b/app/views/dashboards/_dashboard.html.slim @@ -9,30 +9,30 @@ span.badge.ml-xs = workbench.referentials.count if workbench.referentials.present? div - = link_to '', workbench_path(workbench), class: ' fa fa-chevron-right pull-right', title: t('.offers.see') + = link_to '', workbench_path(workbench), class: ' fa fa-chevron-right pull-right', title: t('workbenches.index.offers.see') - if workbench.referentials.present? .list-group - workbench.referentials.limit(5).each do |referential| - = link_to referential.name, referential_path(referential, workbench_id: referential.workbench_id, current_workbench_id: workbench.id), class: 'list-group-item' + = link_to referential.name, referential_path(referential), class: 'list-group-item' - else .panel-body - em.small.text-muted = t('.offers.no_content') + em.small.text-muted = t('workbenches.index.offers.no_content') - .panel.panel-default - .panel-heading - h3.panel-title.with_actions - = 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? - .list-group - - @dashboard.current_organisation.calendars.order("updated_at desc").limit(5).each do |calendar| - = link_to calendar.name, calendar_path(calendar), class: 'list-group-item' - - else - .panel-body - em.small.text-muted - = t('dasboard.calendars.none') + .panel.panel-default + .panel-heading + h3.panel-title.with_actions + = link_to I18n.t("activerecord.models.calendar", count: @dashboard.current_organisation.calendars.size), workgroup_calendars_path(workbench.workgroup) + div + = link_to '', workgroup_calendars_path(workbench.workgroup), class: ' fa fa-chevron-right pull-right' + - if @dashboard.current_organisation.calendars.present? + .list-group + - @dashboard.current_organisation.calendars.order("updated_at desc").limit(5).each do |calendar| + = link_to calendar.name, workgroup_calendars_path(workbench.workgroup, calendar), class: 'list-group-item' + - else + .panel-body + em.small.text-muted + = t('dasboard.calendars.none') .col-lg-6.col-md-6.col-sm-6.col-xs-12 .panel.panel-default @@ -51,4 +51,4 @@ .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' + = link_to Chouette::Network.model_name.human.pluralize.capitalize, line_referential_networks_path(referential), class: 'list-group-item' diff --git a/app/views/errors/forbidden.html.slim b/app/views/errors/forbidden.html.slim index 23ea67eff..8c35b46a8 100644 --- a/app/views/errors/forbidden.html.slim +++ b/app/views/errors/forbidden.html.slim @@ -9,7 +9,7 @@ p strong = "Désolé, la page demandée la page n'est pas accessible avec votre profil utilisateur." - p = "Vous pouvez néanmoins continuer à utiliser l'application IBOO." + p = "Vous pouvez néanmoins continuer à utiliser l'application." - else p diff --git a/app/views/errors/server_error.html.slim b/app/views/errors/server_error.html.slim index 189a48760..529ad73e8 100644 --- a/app/views/errors/server_error.html.slim +++ b/app/views/errors/server_error.html.slim @@ -9,7 +9,7 @@ p strong = "Désolé, une erreur est survenue." - p = "Vous pouvez néanmoins continuer à utiliser l'application IBOO." + p = "Vous pouvez néanmoins continuer à utiliser l'application." - else p diff --git a/app/views/layouts/application.html.slim b/app/views/layouts/application.html.slim index 34b373295..3921c8701 100644 --- a/app/views/layouts/application.html.slim +++ b/app/views/layouts/application.html.slim @@ -13,6 +13,8 @@ html lang=I18n.locale = javascript_pack_tag 'application' = javascript_include_tag 'application' + = javascript_tag do + | I18n.locale = '#{I18n.locale}' body = render 'layouts/navigation/main_nav' diff --git a/app/views/layouts/navigation/_main_nav_left_content_stif.html.slim b/app/views/layouts/navigation/_main_nav_left_content_stif.html.slim index 3963d4cd4..cb0698cf8 100644 --- a/app/views/layouts/navigation/_main_nav_left_content_stif.html.slim +++ b/app/views/layouts/navigation/_main_nav_left_content_stif.html.slim @@ -24,14 +24,14 @@ #miTwo.panel-collapse.collapse .list-group - - if current_user - = link_to workbench_path(current_offer_workbench), class: "list-group-item #{params[:controller] == 'workbenches' ? 'active' : ''}" do + - current_user.workbenches.each do |current_workbench| + = link_to workbench_path(current_workbench), class: "list-group-item #{params[:controller] == 'workbenches' ? 'active' : ''}" do span Jeux de données - = link_to workbench_imports_path(current_offer_workbench), class: "list-group-item #{(params[:controller] == 'imports') ? 'active' : ''}" do + = link_to workbench_imports_path(current_workbench), class: "list-group-item #{(params[:controller] == 'imports') ? 'active' : ''}" do span Import - = link_to calendars_path, class: 'list-group-item' do + = link_to workgroup_calendars_path(current_workbench.workgroup), class: 'list-group-item' do span Modèles de calendrier - = link_to workbench_compliance_check_sets_path(current_offer_workbench), class: 'list-group-item' do + = link_to workbench_compliance_check_sets_path(current_workbench), class: 'list-group-item' do span Rapport de contrôle = link_to compliance_control_sets_path, class: 'list-group-item' do span Jeux de contrôle diff --git a/app/views/layouts/snapshots/actions_links.html.slim b/app/views/layouts/snapshots/actions_links.html.slim new file mode 100644 index 000000000..f1fa55e87 --- /dev/null +++ b/app/views/layouts/snapshots/actions_links.html.slim @@ -0,0 +1,21 @@ +doctype html +html lang=I18n.locale + head + meta charset="utf-8" + meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" + + = csrf_meta_tag + + title = t('brandname') + + = stylesheet_link_tag 'base' + = stylesheet_link_tag 'application' + + = javascript_pack_tag 'application' + = javascript_include_tag 'application' + + body + = render 'layouts/navigation/main_nav' + = render 'layouts/flash_messages', flash: flash + div.page_header + = yield diff --git a/app/views/layouts/snapshots/default.html.slim b/app/views/layouts/snapshots/default.html.slim new file mode 100644 index 000000000..9e4565dcb --- /dev/null +++ b/app/views/layouts/snapshots/default.html.slim @@ -0,0 +1,19 @@ +doctype html +html lang=I18n.locale + head + meta charset="utf-8" + meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" + + = csrf_meta_tag + + title = t('brandname') + + = stylesheet_link_tag 'base' + = stylesheet_link_tag 'application' + + = javascript_pack_tag 'application' + = javascript_include_tag 'application' + + body + = yield + diff --git a/app/views/purchase_windows/show.html.slim b/app/views/purchase_windows/show.html.slim index 4e836f424..8ed9e393f 100644 --- a/app/views/purchase_windows/show.html.slim +++ b/app/views/purchase_windows/show.html.slim @@ -7,6 +7,5 @@ .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, Chouette::PurchaseWindow.human_attribute_name(:checksum) => @purchase_window.checksum } diff --git a/app/views/referential_vehicle_journeys/index.html.slim b/app/views/referential_vehicle_journeys/index.html.slim index 69e29597c..ca1b1ecd9 100644 --- a/app/views/referential_vehicle_journeys/index.html.slim +++ b/app/views/referential_vehicle_journeys/index.html.slim @@ -25,28 +25,28 @@ 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: false \ + sortable: true \ ), \ TableBuilderHelper::Column.new( \ key: :line, \ attribute: Proc.new {|v| v.route.line.name}, \ - sortable: false \ + sortable: true \ ), \ TableBuilderHelper::Column.new( \ key: :route, \ attribute: Proc.new {|v| v.route.name}, \ - sortable: false \ + sortable: true \ ), \ TableBuilderHelper::Column.new( \ key: :departure_time, \ attribute: Proc.new {|v| v.vehicle_journey_at_stops.first&.departure }, \ - sortable: false \ + sortable: true \ ), \ @filters_stop_areas&.map{|s| table_builder_column_for_stop_area(s)}, TableBuilderHelper::Column.new( \ key: :arrival_time, \ attribute: Proc.new {|v| v.vehicle_journey_at_stops.last&.arrival }, \ - sortable: false \ + sortable: true \ ), \ ].flatten.compact, cls: 'table has-filter has-search' diff --git a/app/views/referentials/_form.html.slim b/app/views/referentials/_form.html.slim index 9927f05bd..1e59ab566 100644 --- a/app/views/referentials/_form.html.slim +++ b/app/views/referentials/_form.html.slim @@ -1,4 +1,6 @@ -= simple_form_for @referential, html: {class: 'form-horizontal', id: 'referential_form'}, wrapper: :horizontal_form do |form| +- url = @referential.new_record? ? [@workbench, @referential] : [@referential] + += simple_form_for @referential, url: url, html: {class: 'form-horizontal', id: 'referential_form'}, wrapper: :horizontal_form do |form| .row .col-lg-12 diff --git a/app/views/referentials/_overview.html.slim b/app/views/referentials/_overview.html.slim index 143784800..539c25fd4 100644 --- a/app/views/referentials/_overview.html.slim +++ b/app/views/referentials/_overview.html.slim @@ -9,7 +9,7 @@ span.fa.fa-search .form-group.togglable = f.label Chouette::Line.human_attribute_name(:company_id), required: false, class: 'control-label' - = f.input :company_id_eq_any, collection: overview.referential_lines.map(&:company).uniq.sort_by(&:name), as: :check_boxes, label: false, label_method: lambda{|l| ("<span>" + l.name + "</span>").html_safe}, required: false, wrapper_html: { class: 'checkbox_list'} + = f.input :company_id_eq_any, collection: overview.referential_lines.map(&:company).compact.uniq.sort_by(&:name), as: :check_boxes, label: false, label_method: lambda{|l| ("<span>" + l.name + "</span>").html_safe}, required: false, wrapper_html: { class: 'checkbox_list'} .form-group.togglable = f.label Chouette::Line.human_attribute_name(:transport_mode), required: false, class: 'control-label' diff --git a/app/views/referentials/select_compliance_control_set.html.slim b/app/views/referentials/select_compliance_control_set.html.slim index 87a888c0a..69c87aab2 100644 --- a/app/views/referentials/select_compliance_control_set.html.slim +++ b/app/views/referentials/select_compliance_control_set.html.slim @@ -2,7 +2,7 @@ .container-fluid .row .col-lg-8.col-lg-offset-2.col-md-8.col-md-offset-2.col-sm-10.col-sm-offset-1 - = form_tag validate_referential_path(params[:referential_id]), {class: 'form-horizontal', id: 'select_compliance_control_set', wrapper: :horizontal_form} do + = form_tag validate_referential_path(@referential), {class: 'form-horizontal', id: 'select_compliance_control_set', wrapper: :horizontal_form} do .row .col-lg-12 .form-group diff --git a/app/views/routes/_form.html.slim b/app/views/routes/_form.html.slim index 29e5be3d2..81f719437 100644 --- a/app/views/routes/_form.html.slim +++ b/app/views/routes/_form.html.slim @@ -27,7 +27,7 @@ // Get JSON data for route stop points = javascript_tag do | window.itinerary_stop = "#{URI.escape(route_json_for_edit(@route))}"; - | window.I18n = #{(I18n.backend.send(:translations)[I18n.locale].to_json).html_safe}; + // | window.I18n = #{(I18n.backend.send(:translations)[I18n.locale].to_json).html_safe}; / StopPoints Reactux component = javascript_pack_tag 'routes/edit.js' diff --git a/app/views/stif/dashboards/_dashboard.html.slim b/app/views/stif/dashboards/_dashboard.html.slim index 64e7d4f96..83a2106bb 100644 --- a/app/views/stif/dashboards/_dashboard.html.slim +++ b/app/views/stif/dashboards/_dashboard.html.slim @@ -47,7 +47,7 @@ - if @dashboard.referentials.present? .list-group - @dashboard.referentials.first(5).each_with_index do |referential, i| - = link_to referential.name, referential_path(referential, workbench_id: referential.workbench_id, current_workbench_id: @dashboard.workbench.id), class: 'list-group-item' if i < 6 + = link_to referential.name, referential_path(referential), class: 'list-group-item' if i < 6 - else .panel-body @@ -60,12 +60,12 @@ span.badge.ml-xs = @dashboard.calendars.count if @dashboard.calendars.present? div - = link_to '', calendars_path, class: ' fa fa-chevron-right pull-right', title: t('.see') + = link_to '', workgroup_calendars_path(@dashboard.workbench.workgroup), class: ' fa fa-chevron-right pull-right', title: t('.see') - if @dashboard.calendars.present? .list-group - @dashboard.calendars.first(5).each_with_index do |calendar, i| - = link_to calendar.name, calendar_path(calendar), class: 'list-group-item' if i < 6 + = link_to calendar.name, workgroup_calendar_path(@dashboard.workbench.workgroup, calendar), class: 'list-group-item' if i < 6 - else .panel-body diff --git a/app/views/time_tables/_form.html.slim b/app/views/time_tables/_form.html.slim index d06fdf444..007044e65 100644 --- a/app/views/time_tables/_form.html.slim +++ b/app/views/time_tables/_form.html.slim @@ -5,7 +5,7 @@ = form.input :comment, :input_html => { :title => t("formtastic.titles#{format_restriction_for_locales(@referential)}.time_table.comment")} - if @time_table.new_record? && !@time_table.created_from - = form.input :calendar_id, as: :select, input_html: { class: 'tt_target', style: "width: 100%", data: { 'select2-ajax': 'true', 'select2ed-placeholder': 'Indiquez un modèle de calendrier...', term: 'name_cont', url: autocomplete_calendars_path}} + = form.input :calendar_id, as: :select, input_html: { class: 'tt_target', style: "width: 100%", data: { 'select2-ajax': 'true', 'select2ed-placeholder': 'Indiquez un modèle de calendrier...', term: 'name_cont', url: autocomplete_workgroup_calendars_path(current_workgroup)}} - if @time_table.created_from = form.input :created_from, disabled: true, input_html: { value: @time_table.created_from.comment } diff --git a/app/views/time_tables/edit.html.slim b/app/views/time_tables/edit.html.slim index e1c566ff4..d8cffb1b0 100644 --- a/app/views/time_tables/edit.html.slim +++ b/app/views/time_tables/edit.html.slim @@ -8,6 +8,6 @@ = javascript_tag do | window.actionType = "#{raw params[:action]}"; - | window.I18n = #{(I18n.backend.send(:translations)[I18n.locale].to_json).html_safe}; + // | window.I18n = #{(I18n.backend.send(:translations)[I18n.locale].to_json).html_safe}; = javascript_pack_tag 'time_tables/edit.js' diff --git a/app/views/time_tables/index.html.slim b/app/views/time_tables/index.html.slim index f58fbb5ea..6913712a0 100644 --- a/app/views/time_tables/index.html.slim +++ b/app/views/time_tables/index.html.slim @@ -61,6 +61,6 @@ = replacement_msg t('time_tables.search_no_results') = javascript_tag do - | window.I18n = #{(I18n.backend.send(:translations).to_json).html_safe}; + // | window.I18n = #{(I18n.backend.send(:translations).to_json).html_safe}; = javascript_pack_tag 'date_filters' diff --git a/app/views/vehicle_journeys/index.html.slim b/app/views/vehicle_journeys/index.html.slim index caa8450a0..d53d8b50c 100644 --- a/app/views/vehicle_journeys/index.html.slim +++ b/app/views/vehicle_journeys/index.html.slim @@ -29,7 +29,7 @@ | window.features = #{raw @features}; | window.all_missions = #{(@all_missions.to_json).html_safe}; | window.custom_fields = #{(@custom_fields.to_json).html_safe}; - | window.I18n = #{(I18n.backend.send(:translations).to_json).html_safe}; + // | window.I18n = #{(I18n.backend.send(:translations).to_json).html_safe}; - if has_feature?(:vehicle_journeys_return_route) = javascript_tag do diff --git a/app/views/vehicle_journeys/show.rabl b/app/views/vehicle_journeys/show.rabl index dca0866b3..bb26ce797 100644 --- a/app/views/vehicle_journeys/show.rabl +++ b/app/views/vehicle_journeys/show.rabl @@ -23,6 +23,12 @@ end child(:time_tables, :object_root => false) do |time_tables| attributes :id, :objectid, :comment, :color + node(:days) do |tt| + tt.display_day_types + end + node(:bounding_dates) do |tt| + tt.presenter.time_table_bounding + end child(:calendar) do attributes :id, :name, :date_ranges, :dates, :shared end @@ -39,10 +45,8 @@ child :footnotes, :object_root => false do |footnotes| end child(:vehicle_journey_at_stops_matrix, :object_root => false) do |vehicle_stops| + attributes :id, :connecting_service_id, :boarding_alighting_possibility 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(:area_kind) { vehicle_stop.stop_point.stop_area.kind } diff --git a/app/views/workbenches/show.html.slim b/app/views/workbenches/show.html.slim index a162ca334..159aa8ea2 100644 --- a/app/views/workbenches/show.html.slim +++ b/app/views/workbenches/show.html.slim @@ -5,7 +5,7 @@ .col-lg-12.text-right - if policy(Referential).create? = link_to t('actions.import'), workbench_imports_path(@workbench), class: 'btn btn-primary' - = link_to t('actions.add'), new_referential_path(workbench_id: @workbench), class: 'btn btn-primary' + = link_to t('actions.add'), new_workbench_referential_path(@workbench), class: 'btn btn-primary' = link_to t('workbenches.actions.show_output'), workbench_output_path(@workbench), class: 'btn btn-primary' .page_content @@ -25,7 +25,7 @@ key: :name, \ attribute: 'name', \ link_to: lambda do |referential| \ - referential_path(referential, current_workbench_id: params[:id]) \ + referential_path(referential) \ end \ ), \ TableBuilderHelper::Column.new( \ @@ -72,6 +72,6 @@ = replacement_msg t('referentials.search_no_results') = javascript_tag do - | window.I18n = #{(I18n.backend.send(:translations).to_json).html_safe}; + // | window.I18n = #{(I18n.backend.send(:translations).to_json).html_safe}; = javascript_pack_tag 'date_filters' |
