From 3b077db5d818ae713da9f925c1f4acc0f0d2f68e Mon Sep 17 00:00:00 2001 From: Alexandru Emil Lupu Date: Wed, 31 May 2023 09:45:43 +0300 Subject: [PATCH] Refactor intiative wizard (#10727) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Refactor intiative wizard * Fix the initiative creation * Fixing failing specs * Apply review recommendations * Update decidim-initiatives/app/models/decidim/initiative.rb Co-authored-by: Andrés Pereira de Lucena * Apply review recommendations * Fix the user group creation * Small refactor on initiatives * Add spec for custom signature end date in update command * Update decidim-initiatives/app/controllers/decidim/initiatives/create_initiative_controller.rb Co-authored-by: Andrés Pereira de Lucena * Update decidim-initiatives/lib/decidim/initiatives/engine.rb Co-authored-by: Andrés Pereira de Lucena * Apply latest review recommendations * Add area spec * Apply suggestions from code review Co-authored-by: Antti Hukkanen --------- Co-authored-by: Andrés Pereira de Lucena Co-authored-by: Antti Hukkanen --- .../decidim/initiatives/create_initiative.rb | 25 +-- .../decidim/initiatives/update_initiative.rb | 3 +- .../create_initiative_controller.rb | 187 +++++++----------- .../decidim/initiatives/initiative_form.rb | 51 ++--- .../decidim/initiatives/previous_form.rb | 4 + .../app/models/decidim/initiative.rb | 12 +- .../src/decidim/initiatives/scoped_type.js | 2 +- .../initiatives/similar_initiatives.rb | 4 +- .../_share_committee_link.html.erb | 2 +- .../create_initiative/fill_data.html.erb | 10 +- .../create_initiative/previous_form.html.erb | 2 +- .../select_initiative_type.html.erb | 4 +- .../show_similar_initiatives.html.erb | 2 +- .../initiatives/initiatives/_form.html.erb | 6 +- .../initiatives_type_scopes/search.html.erb | 3 +- .../_initiative_creation_header.html.erb | 21 +- .../decidim/initiative_creation.html.erb | 2 +- decidim-initiatives/config/locales/en.yml | 1 - .../lib/decidim/initiatives/engine.rb | 17 +- .../initiatives/update_initiative_spec.rb | 51 +++++ .../spec/shared/create_initiative_example.rb | 81 -------- .../spec/system/create_initiative_spec.rb | 68 +++++-- 22 files changed, 257 insertions(+), 301 deletions(-) diff --git a/decidim-initiatives/app/commands/decidim/initiatives/create_initiative.rb b/decidim-initiatives/app/commands/decidim/initiatives/create_initiative.rb index 5ec2e78221b85..d6b07712042d9 100644 --- a/decidim-initiatives/app/commands/decidim/initiatives/create_initiative.rb +++ b/decidim-initiatives/app/commands/decidim/initiatives/create_initiative.rb @@ -5,7 +5,6 @@ module Initiatives # A command with all the business logic that creates a new initiative. class CreateInitiative < Decidim::Command include CurrentLocale - include ::Decidim::MultipleAttachmentsMethods # Public: Initializes the command. # @@ -19,17 +18,12 @@ def initialize(form, current_user) # Executes the command. Broadcasts these events: # # - :ok when everything is valid. - # - :invalid if the form wasn't valid and we couldn't proceed. + # - :invalid if the form was not valid and we could not proceed. # # Returns nothing. def call return broadcast(:invalid) if form.invalid? - if process_attachments? - build_attachments - return broadcast(:invalid) if attachments_invalid? - end - initiative = create_initiative if initiative.persisted? @@ -50,8 +44,6 @@ def create_initiative initiative.transaction do initiative.save! - @attached_to = initiative - create_attachments if process_attachments? create_components_for(initiative) send_notification(initiative) @@ -68,21 +60,14 @@ def build_initiative title: { current_locale => form.title }, description: { current_locale => form.description }, author: current_user, - decidim_user_group_id: form.decidim_user_group_id, scoped_type: scoped_type, - area: area, - signature_type: form.signature_type, - signature_end_date: signature_end_date, - state: "created", - hashtag: form.hashtag + signature_type: form.type.signature_type, + state: "created" ) end def scoped_type - InitiativesTypeScope.find_by( - type: form.initiative_type, - scope: form.scope - ) + InitiativesTypeScope.order(:id).find_by(type: form.type) end def signature_end_date @@ -112,7 +97,7 @@ def create_components_for(initiative) def initialize_pages(component) Decidim::Pages::CreatePage.call(component) do - on(:invalid) { raise "Can't create page" } + on(:invalid) { raise "Cannot create page" } end end diff --git a/decidim-initiatives/app/commands/decidim/initiatives/update_initiative.rb b/decidim-initiatives/app/commands/decidim/initiatives/update_initiative.rb index 4ed342eb686a1..05dc1632fa332 100644 --- a/decidim-initiatives/app/commands/decidim/initiatives/update_initiative.rb +++ b/decidim-initiatives/app/commands/decidim/initiatives/update_initiative.rb @@ -57,7 +57,8 @@ def attributes attrs = { title: { current_locale => form.title }, description: { current_locale => form.description }, - hashtag: form.hashtag + hashtag: form.hashtag, + decidim_user_group_id: form.decidim_user_group_id } if form.signature_type_updatable? diff --git a/decidim-initiatives/app/controllers/decidim/initiatives/create_initiative_controller.rb b/decidim-initiatives/app/controllers/decidim/initiatives/create_initiative_controller.rb index 809753fe5b3ef..8ab39b0f637ef 100644 --- a/decidim-initiatives/app/controllers/decidim/initiatives/create_initiative_controller.rb +++ b/decidim-initiatives/app/controllers/decidim/initiatives/create_initiative_controller.rb @@ -8,7 +8,6 @@ module Initiatives class CreateInitiativeController < Decidim::Initiatives::ApplicationController layout "layouts/decidim/initiative_creation" - include Wicked::Wizard include Decidim::FormFactory include InitiativeHelper include TypeSelectorOptions @@ -26,163 +25,127 @@ class CreateInitiativeController < Decidim::Initiatives::ApplicationController helper_method :promotal_committee_required? before_action :authenticate_user! + before_action :ensure_type_exists, + only: [:store_initiative_type, :previous_form, :store_initial_data, :fill_data, :store_data, :show_similar_initiatives, :promotal_committee, :finish] + before_action :ensure_user_can_create_initiative, + only: [:previous_form, :store_initial_data, :fill_data, :store_data, :show_similar_initiatives, :promotal_committee, :finish] + before_action :ensure_initiative_exists, only: [:fill_data, :store_data, :show_similar_initiatives, :promotal_committee, :finish] - steps :select_initiative_type, - :previous_form, - :show_similar_initiatives, - :fill_data, - :promotal_committee, - :finish + def select_initiative_type + @form = form(Decidim::Initiatives::SelectInitiativeTypeForm).from_params(params) - before_action :ensure_type_exists, only: :show - - def show - send("#{step}_step", initiative: session_initiative) - end - - def update - enforce_permission_to :create, :initiative, { initiative_type: initiative_type_from_params } - send("#{step}_step", params) - end - - private - - def ensure_type_exists - destination_step = single_initiative_type? ? :previous_form : :select_initiative_type - - return if step == destination_step - return if initiative_type_id.present? && initiative_type.present? - - redirect_to wizard_path(destination_step) + redirect_to previous_form_create_initiative_index_path if single_initiative_type? end - def select_initiative_type_step(_parameters) - @form = form(Decidim::Initiatives::SelectInitiativeTypeForm).instance - session[:initiative] = {} + def store_initiative_type + @form = form(Decidim::Initiatives::SelectInitiativeTypeForm).from_params(params) - if single_initiative_type? - redirect_to next_wizard_path - return + if @form.valid? + session[:type_id] = @form.type_id + redirect_to previous_form_create_initiative_index_path + else + render :select_initiative_type end - - @form = form(Decidim::Initiatives::SelectInitiativeTypeForm).instance - render_wizard unless performed? end - def previous_form_step(parameters) - @form = build_form(Decidim::Initiatives::PreviousForm, parameters) - - enforce_permission_to :create, :initiative, { initiative_type: initiative_type } - - render_wizard + def previous_form + @form = form(Decidim::Initiatives::PreviousForm).from_params({ type_id: initiative_type_id }) end - def show_similar_initiatives_step(parameters) - @form = build_form(Decidim::Initiatives::PreviousForm, parameters) - unless @form.valid? - redirect_to previous_wizard_path(validate_form: true) - return - end + def store_initial_data + @form = form(Decidim::Initiatives::PreviousForm).from_params(params, { initiative_type: initiative_type }) - if similar_initiatives.empty? - @form = build_form(Decidim::Initiatives::InitiativeForm, parameters) - redirect_to wizard_path(:fill_data) - end + CreateInitiative.call(@form, current_user) do + on(:ok) do |initiative| + session[:initiative_id] = initiative.id + redirect_to show_similar_initiatives_create_initiative_index_path + end - render_wizard unless performed? + on(:invalid) do + render :previous_form + end + end end - def fill_data_step(parameters) - @form = build_form(Decidim::Initiatives::InitiativeForm, parameters) - @form.attachment = form(AttachmentForm).from_params({}) + def show_similar_initiatives + @form = form(Decidim::Initiatives::PreviousForm).from_model(current_initiative) - render_wizard + redirect_to fill_data_create_initiative_index_path if similar_initiatives.empty? end - def promotal_committee_step(parameters) - @form = build_form(Decidim::Initiatives::InitiativeForm, parameters) - unless @form.valid? - redirect_to previous_wizard_path(validate_form: true) - return - end + def fill_data + @form = form(Decidim::Initiatives::InitiativeForm).from_model(current_initiative, { initiative_type: initiative_type }) + end - skip_step unless promotal_committee_required? + def store_data + @form = form(Decidim::Initiatives::InitiativeForm).from_params(params, { initiative_type: initiative_type }) - if session_initiative.has_key?(:id) - render_wizard - return - end + UpdateInitiative.call(current_initiative, @form, current_user) do + on(:ok) do + path = promotal_committee_required? ? "promotal_committee" : "finish" - CreateInitiative.call(@form, current_user) do - on(:ok) do |initiative| - session[:initiative][:id] = initiative.id - if current_initiative.created_by_individual? - render_wizard - else - redirect_to wizard_path(:finish) - end + redirect_to send("#{path}_create_initiative_index_path".to_sym) end - on(:invalid) do |initiative| - logger.fatal "Failed creating initiative: #{initiative.errors.full_messages.join(", ")}" if initiative - redirect_to previous_wizard_path(validate_form: true) + on(:invalid) do + render :fill_data end end end - def finish_step(_parameters) - render_wizard + def promotal_committee + redirect_to finish_create_initiative_index_path unless promotal_committee_required? end - def similar_initiatives - @similar_initiatives ||= Decidim::Initiatives::SimilarInitiatives - .for(current_organization, @form) - .all - end + def finish; end - def build_form(klass, parameters) - @form = if single_initiative_type? - form(klass).from_params(parameters.except(:id).merge(type_id: current_organization_initiatives_type.first.id), extra_context) - else - form(klass).from_params(parameters.except(:id), extra_context) - end + private - attributes = @form.attributes_with_values - session[:initiative] = session_initiative.merge(attributes) - @form.valid? if params[:validate_form] + def ensure_user_can_create_initiative + enforce_permission_to :create, :initiative, { initiative_type: initiative_type } + end - @form + def initiative_type_id + @initiative_type_id ||= fetch_initiative_type_id end - def extra_context - return {} unless initiative_type_id + def fetch_initiative_type_id + return current_organization_initiatives_type.first.id if single_initiative_type? + return params.dig(:initiative, :type_id) if params.dig(:initiative, :type_id).present? + return current_initiative&.type&.id if session[:initiative_id].present? - { initiative_type: initiative_type } + session[:type_id] end - def scopes - @scopes ||= @form.available_scopes + def ensure_initiative_exists + redirect_to previous_form_create_initiative_index_path if session[:initiative_id].blank? end - def current_initiative - Initiative.where(organization: current_organization).find_by(id: session_initiative[:id]) if session_initiative.has_key?(:id) + def ensure_type_exists + destination_step = single_initiative_type? ? "previous_form" : "select_initiative_type" + + return if action_name == destination_step + return if initiative_type_id.present? && initiative_type.present? + + redirect_to send("#{destination_step}_create_initiative_index_path".to_sym) end - def initiative_type - @initiative_type ||= InitiativesType.where(organization: current_organization).find_by(id: initiative_type_id) + def similar_initiatives + @similar_initiatives ||= Decidim::Initiatives::SimilarInitiatives + .for(current_organization, @form) + .all end - def initiative_type_from_params - Decidim::InitiativesType.find_by(id: params["initiative"]["type_id"]) + def scopes + @scopes ||= @form.available_scopes end - def initiative_type_id - session_initiative[:type_id] || @form&.type_id + def current_initiative + Initiative.find(session[:initiative_id] || nil) end - def session_initiative - session[:initiative] ||= {} - session[:initiative].with_indifferent_access + def initiative_type + @initiative_type ||= InitiativesType.find(initiative_type_id) end def promotal_committee_required? diff --git a/decidim-initiatives/app/forms/decidim/initiatives/initiative_form.rb b/decidim-initiatives/app/forms/decidim/initiatives/initiative_form.rb index bd1eddab546fa..35434274e1d22 100644 --- a/decidim-initiatives/app/forms/decidim/initiatives/initiative_form.rb +++ b/decidim-initiatives/app/forms/decidim/initiatives/initiative_form.rb @@ -3,16 +3,12 @@ module Decidim module Initiatives # A form object used to collect the data for a new initiative. - class InitiativeForm < Form + class InitiativeForm < PreviousForm include TranslatableAttributes include AttachmentAttributes mimic :initiative - attribute :title, String - attribute :description, String - attribute :type_id, Integer - attribute :scope_id, Integer attribute :area_id, Integer attribute :decidim_user_group_id, Integer attribute :signature_type, String @@ -20,18 +16,16 @@ class InitiativeForm < Form attribute :state, String attribute :attachment, AttachmentForm attribute :hashtag, String + attribute :scope_id, Integer attachments_attribute :photos attachments_attribute :documents - validates :title, :description, presence: true - validates :title, length: { maximum: 150 } validates :signature_type, presence: true - validates :type_id, presence: true validates :area, presence: true, if: ->(form) { form.area_id.present? } - validate :scope_exists validate :notify_missing_attachment_if_errored validate :trigger_attachment_errors + validate :scope_exists validates :signature_end_date, date: { after: Date.current }, if: lambda { |form| form.context.initiative_type.custom_signature_end_date_enabled? && form.signature_end_date.present? } @@ -39,6 +33,7 @@ class InitiativeForm < Form def map_model(model) self.type_id = model.type.id self.scope_id = model.scope&.id + self.decidim_user_group_id = model.decidim_user_group_id self.signature_type = model.signature_type self.title = translated_attribute(model.title) self.description = translated_attribute(model.description) @@ -56,12 +51,6 @@ def area_updatable? @area_updatable ||= current_user.admin? || context.initiative.created? end - def scope_id - return nil if initiative_type.only_global_scope_enabled? - - super.presence - end - def area @area ||= current_organization.areas.find_by(id: area_id) end @@ -70,37 +59,39 @@ def initiative_type @initiative_type ||= type_id ? InitiativesType.find(type_id) : context.initiative.type end - def available_scopes - @available_scopes ||= if initiative_type.only_global_scope_enabled? - initiative_type.scopes.where(scope: nil) - else - initiative_type.scopes - end + def scoped_type_id + return unless type && scope_id + + type.scopes.find_by(decidim_scopes_id: scope_id.presence).id end def scope @scope ||= Scope.find(scope_id) if scope_id.present? end - def scoped_type_id - return unless type && scope_id + def scope_id + return nil if type.only_global_scope_enabled? - type.scopes.find_by(decidim_scopes_id: scope_id.presence).id + super.presence end - private - - def type - @type ||= type_id ? Decidim::InitiativesType.find(type_id) : context.initiative.type + def available_scopes + @available_scopes ||= if type.only_global_scope_enabled? + type.scopes.where(scope: nil) + else + type.scopes + end end + private + def scope_exists return if scope_id.blank? - errors.add(:scope_id, :invalid) unless InitiativesTypeScope.exists?(type: initiative_type, scope: scope) + errors.add(:scope_id, :invalid) unless InitiativesTypeScope.exists?(type: type, scope: scope) end - # This method will add an error to the `attachment` field only if there's + # This method will add an error to the `attachment` field only if there is # any error in any other field. This is needed because when the form has # an error, the attachment is lost, so we need a way to inform the user of # this problem. diff --git a/decidim-initiatives/app/forms/decidim/initiatives/previous_form.rb b/decidim-initiatives/app/forms/decidim/initiatives/previous_form.rb index 1010d0e8505e2..86a007f2846fb 100644 --- a/decidim-initiatives/app/forms/decidim/initiatives/previous_form.rb +++ b/decidim-initiatives/app/forms/decidim/initiatives/previous_form.rb @@ -15,6 +15,10 @@ class PreviousForm < Form validates :title, :description, presence: true validates :title, length: { maximum: 150 } validates :type_id, presence: true + + def type + @type ||= type_id ? Decidim::InitiativesType.find(type_id) : context.initiative.type + end end end end diff --git a/decidim-initiatives/app/models/decidim/initiative.rb b/decidim-initiatives/app/models/decidim/initiative.rb index 5184c005f7902..f720c8edc7986 100644 --- a/decidim-initiatives/app/models/decidim/initiative.rb +++ b/decidim-initiatives/app/models/decidim/initiative.rb @@ -27,6 +27,11 @@ class Initiative < ApplicationRecord translatable_fields :title, :description, :answer + delegate :type, :scope, :scope_name, :supports_required, to: :scoped_type, allow_nil: true + delegate :document_number_authorization_handler, :promoting_committee_enabled?, :attachments_enabled?, + :promoting_committee_enabled?, :custom_signature_end_date_enabled?, :area_enabled?, to: :type + delegate :name, to: :area, prefix: true, allow_nil: true + belongs_to :organization, foreign_key: "decidim_organization_id", class_name: "Decidim::Organization" @@ -35,10 +40,6 @@ class Initiative < ApplicationRecord class_name: "Decidim::InitiativesTypeScope", inverse_of: :initiatives - delegate :type, :scope, :scope_name, :supports_required, to: :scoped_type, allow_nil: true - delegate :attachments_enabled?, :promoting_committee_enabled?, :custom_signature_end_date_enabled?, :area_enabled?, to: :type - delegate :name, to: :area, prefix: true, allow_nil: true - has_many :votes, foreign_key: "decidim_initiative_id", class_name: "Decidim::InitiativesVote", @@ -162,9 +163,6 @@ def self.ransackable_scopes(_auth_object = nil) [:with_any_state, :with_any_type, :with_any_scope, :with_any_area] end - delegate :document_number_authorization_handler, :promoting_committee_enabled?, to: :type - delegate :type, :scope, :scope_name, to: :scoped_type, allow_nil: true - # Public: Overrides participatory space's banner image with the banner image defined # for the initiative type. # diff --git a/decidim-initiatives/app/packs/src/decidim/initiatives/scoped_type.js b/decidim-initiatives/app/packs/src/decidim/initiatives/scoped_type.js index 5045322ef65e0..bc3969e21dce7 100644 --- a/decidim-initiatives/app/packs/src/decidim/initiatives/scoped_type.js +++ b/decidim-initiatives/app/packs/src/decidim/initiatives/scoped_type.js @@ -1,5 +1,5 @@ /* eslint-disable camelcase */ -const controlSelector = function(source, prefix, currentValueKey) { +const controlSelector = function (source, prefix, currentValueKey) { if (source.length) { let currentValue = source.data(currentValueKey), searchUrl = source.data(`${prefix}-search-url`), diff --git a/decidim-initiatives/app/queries/decidim/initiatives/similar_initiatives.rb b/decidim-initiatives/app/queries/decidim/initiatives/similar_initiatives.rb index b817f2fd26cae..bb94459beb3d3 100644 --- a/decidim-initiatives/app/queries/decidim/initiatives/similar_initiatives.rb +++ b/decidim-initiatives/app/queries/decidim/initiatives/similar_initiatives.rb @@ -31,8 +31,8 @@ def query .where(organization: @organization) .where( Arel.sql("GREATEST(#{title_similarity}, #{description_similarity}) >= ?").to_s, - form.title, - form.description, + translated_attribute(form.title), + translated_attribute(form.description), Decidim::Initiatives.similarity_threshold ) .limit(Decidim::Initiatives.similarity_limit) diff --git a/decidim-initiatives/app/views/decidim/initiatives/create_initiative/_share_committee_link.html.erb b/decidim-initiatives/app/views/decidim/initiatives/create_initiative/_share_committee_link.html.erb index 6caf0a7179623..0046f84482fd5 100644 --- a/decidim-initiatives/app/views/decidim/initiatives/create_initiative/_share_committee_link.html.erb +++ b/decidim-initiatives/app/views/decidim/initiatives/create_initiative/_share_committee_link.html.erb @@ -18,7 +18,7 @@
- <%= link_to t(".continue"), next_wizard_path, class: "button expanded" %> + <%= link_to t(".continue"), finish_create_initiative_index_path, class: "button expanded" %>
<%= javascript_pack_tag "decidim_initiatives_admin" %> diff --git a/decidim-initiatives/app/views/decidim/initiatives/create_initiative/fill_data.html.erb b/decidim-initiatives/app/views/decidim/initiatives/create_initiative/fill_data.html.erb index c228420d7897d..09da6d6a905ce 100644 --- a/decidim-initiatives/app/views/decidim/initiatives/create_initiative/fill_data.html.erb +++ b/decidim-initiatives/app/views/decidim/initiatives/create_initiative/fill_data.html.erb @@ -18,7 +18,7 @@
- <%= decidim_form_for(@form, url: next_wizard_path, method: :put, html: { class: "form new_initiative_form" }) do |f| %> + <%= decidim_form_for(@form, url: fill_data_create_initiative_index_path, method: :put, html: { class: "form new_initiative_form", novalidate: false }) do |f| %> <%= form_required_explanation %>
<% if single_initiative_type? %> @@ -30,22 +30,22 @@ {}, { disabled: !@form.signature_type_updatable?, - "data-scope-selector": "initiative_decidim_scope_id", + "data-scope-selector": "initiative_scope_id", "data-scope-id": f.object.scope_id.to_s, "data-scope-search-url": decidim_initiatives.initiative_type_scopes_search_url, "data-signature-types-selector": "initiative_signature_type", - "data-signature-type": current_initiative&.signature_type, + "data-signature-type": current_initiative.signature_type, "data-signature-types-search-url": decidim_initiatives.initiative_type_signature_types_search_url } %>
<% end %>
- <%= f.text_field :title, autofocus: true %> + <%= f.text_field :title, autofocus: true, value: translated_attribute(f.object.title) %>
- <%= text_editor_for(f, :description, lines: 8, toolbar: :content) %> + <%= text_editor_for(f, :description, lines: 8, toolbar: :content, value: translated_attribute(f.object.description)) %>
diff --git a/decidim-initiatives/app/views/decidim/initiatives/create_initiative/previous_form.html.erb b/decidim-initiatives/app/views/decidim/initiatives/create_initiative/previous_form.html.erb index daf3ff9ecbc0f..c7d3009843219 100644 --- a/decidim-initiatives/app/views/decidim/initiatives/create_initiative/previous_form.html.erb +++ b/decidim-initiatives/app/views/decidim/initiatives/create_initiative/previous_form.html.erb @@ -18,7 +18,7 @@
- <%= decidim_form_for(@form, url: next_wizard_path, method: :put, html: { class: "form new_initiative_previous_form" }) do |f| %> + <%= decidim_form_for(@form, url: previous_form_create_initiative_index_path, method: :put, html: { class: "form new_initiative_previous_form" }) do |f| %> <%= form_required_explanation %> <%= f.hidden_field :type_id %> diff --git a/decidim-initiatives/app/views/decidim/initiatives/create_initiative/select_initiative_type.html.erb b/decidim-initiatives/app/views/decidim/initiatives/create_initiative/select_initiative_type.html.erb index 28c47b0350a2c..b4869f03fc0e1 100644 --- a/decidim-initiatives/app/views/decidim/initiatives/create_initiative/select_initiative_type.html.erb +++ b/decidim-initiatives/app/views/decidim/initiatives/create_initiative/select_initiative_type.html.erb @@ -40,8 +40,8 @@

<% if allowed_to?(:create, :initiative, { initiative_type: type }) %> - <%= decidim_form_for(@form, url: next_wizard_path, method: :put, html: { id: "new_initiative_#{type.id}", class: "form select-initiative_type-form" }) do |f| %> - <%= f.hidden_field :type_id, value: type.id, id: "initiative_type_id_#{ type.id }" %> + <%= decidim_form_for(@form, url: select_initiative_type_create_initiative_index_path, method: :put, html: { id: "new_initiative_#{type.id}", class: "form select-initiative_type-form" }) do |f| %> + <%= f.hidden_field :type_id, value: type.id, id: "initiative_type_id_#{type.id}" %> <%= f.submit t(".select"), class: "button" %> <% end %> <% else %> diff --git a/decidim-initiatives/app/views/decidim/initiatives/create_initiative/show_similar_initiatives.html.erb b/decidim-initiatives/app/views/decidim/initiatives/create_initiative/show_similar_initiatives.html.erb index d1476186d06fd..1fcbe59a6ed80 100644 --- a/decidim-initiatives/app/views/decidim/initiatives/create_initiative/show_similar_initiatives.html.erb +++ b/decidim-initiatives/app/views/decidim/initiatives/create_initiative/show_similar_initiatives.html.erb @@ -18,6 +18,6 @@
- <%= link_to t(".continue"), next_wizard_path, class: "button expanded" %> + <%= link_to t(".continue"), fill_data_create_initiative_index_path, class: "button expanded" %>
diff --git a/decidim-initiatives/app/views/decidim/initiatives/initiatives/_form.html.erb b/decidim-initiatives/app/views/decidim/initiatives/initiatives/_form.html.erb index 2c361d1353a1a..53fe912361d4d 100644 --- a/decidim-initiatives/app/views/decidim/initiatives/initiatives/_form.html.erb +++ b/decidim-initiatives/app/views/decidim/initiatives/initiatives/_form.html.erb @@ -7,7 +7,7 @@ {}, { disabled: !@form.signature_type_updatable?, - "data-scope-selector": "initiative_decidim_scope_id", + "data-scope-selector": "initiative_scope_id", "data-scope-id": form.object.scope_id.to_s, "data-scope-search-url": decidim_initiatives.initiative_type_scopes_search_url, "data-signature-types-selector": "initiative_signature_type", @@ -114,7 +114,3 @@ <% if current_initiative.type.promoting_committee_enabled? %> <%= render partial: "committee_members" %> <% end %> - -<% content_for :js_content do %> - <%= javascript_pack_tag "decidim_initiatives" %> -<% end %> diff --git a/decidim-initiatives/app/views/decidim/initiatives/initiatives_type_scopes/search.html.erb b/decidim-initiatives/app/views/decidim/initiatives/initiatives_type_scopes/search.html.erb index e006ff8d9a34b..9feac8880456e 100644 --- a/decidim-initiatives/app/views/decidim/initiatives/initiatives_type_scopes/search.html.erb +++ b/decidim-initiatives/app/views/decidim/initiatives/initiatives_type_scopes/search.html.erb @@ -1 +1,2 @@ -<%= options_for_select scoped_types.map { |s| [translated_attribute(s.scope_name), s&.scope&.id] }, params[:selected] %> +<% blank = [t("select_scope", scope: "decidim.initiatives.create_initiative.fill_data"), ""] %> +<%= options_for_select scoped_types.map { |s| [translated_attribute(s.scope_name), s&.scope&.id] }.prepend(blank), params[:selected] %> diff --git a/decidim-initiatives/app/views/layouts/decidim/_initiative_creation_header.html.erb b/decidim-initiatives/app/views/layouts/decidim/_initiative_creation_header.html.erb index ca68128fb01ba..f1c24cb29dc85 100644 --- a/decidim-initiatives/app/views/layouts/decidim/_initiative_creation_header.html.erb +++ b/decidim-initiatives/app/views/layouts/decidim/_initiative_creation_header.html.erb @@ -11,19 +11,16 @@
    - <% wizard_steps.each do |wizard_step| %> - <% next if wizard_step.to_s == "promotal_committee" && !promotal_committee_required? %> - <% next if wizard_step.to_s == "select_initiative_type" && single_initiative_type? %> - <% if step == wizard_step %> -
  1. - <%= t(".#{wizard_step}") %> -
  2. - <% else %> -
  3. - <%= t(".#{wizard_step}") %> -
  4. - <% end %> + <% unless single_initiative_type? %> + <%= content_tag :li, t(".select_initiative_type"), class: action_name == "select_initiative_type" ? "step--active" : nil %> <% end %> + <%= content_tag :li, t(".previous_form"), class: action_name == "previous_form" ? "step--active" : nil %> + <%= content_tag :li, t(".show_similar_initiatives"), class: action_name == "show_similar_initiatives" ? "step--active" : nil %> + <%= content_tag :li, t(".fill_data"), class: action_name == "fill_data" ? "step--active" : nil %> + <% if promotal_committee_required? %> + <%= content_tag :li, t(".promotal_committee"), class: action_name == "promotal_committee" ? "step--active" : nil %> + <% end %> + <%= content_tag :li, t(".finish"), class: action_name == "finish" ? "step--active" : nil %>
diff --git a/decidim-initiatives/app/views/layouts/decidim/initiative_creation.html.erb b/decidim-initiatives/app/views/layouts/decidim/initiative_creation.html.erb index ca22981332549..b2972b1c7422a 100644 --- a/decidim-initiatives/app/views/layouts/decidim/initiative_creation.html.erb +++ b/decidim-initiatives/app/views/layouts/decidim/initiative_creation.html.erb @@ -1,5 +1,5 @@ <%= render "layouts/decidim/application" do %> - <% if wizard_steps.first == step %> + <% if action_name == "select_initiative_type" %>
<%= yield %>
diff --git a/decidim-initiatives/config/locales/en.yml b/decidim-initiatives/config/locales/en.yml index 10cc29a12ae5b..6f954afaa5bb0 100644 --- a/decidim-initiatives/config/locales/en.yml +++ b/decidim-initiatives/config/locales/en.yml @@ -634,7 +634,6 @@ en: promotal_committee: Promoter committee select_initiative_type: Choose show_similar_initiatives: Compare - step: Step %{current} of %{total} title: Create new initiative initiative_header: initiative_menu_item: Initiative diff --git a/decidim-initiatives/lib/decidim/initiatives/engine.rb b/decidim-initiatives/lib/decidim/initiatives/engine.rb index 2c77478413280..1bd50c74f5e5e 100644 --- a/decidim-initiatives/lib/decidim/initiatives/engine.rb +++ b/decidim-initiatives/lib/decidim/initiatives/engine.rb @@ -19,7 +19,22 @@ class Engine < ::Rails::Engine get "/initiative_type_scopes/search", to: "initiatives_type_scopes#search", as: :initiative_type_scopes_search get "/initiative_type_signature_types/search", to: "initiatives_type_signature_types#search", as: :initiative_type_signature_types_search - resources :create_initiative + resources :create_initiative do + collection do + get :select_initiative_type + put :select_initiative_type, to: "create_initiative#store_initiative_type" + + get :previous_form + put :previous_form, to: "create_initiative#store_initial_data" + + get :show_similar_initiatives + + get :fill_data + put :fill_data, to: "create_initiative#store_data" + get :promotal_committee + get :finish + end + end get "initiatives/:initiative_id", to: redirect { |params, _request| initiative = Decidim::Initiative.find(params[:initiative_id]) diff --git a/decidim-initiatives/spec/commands/decidim/initiatives/update_initiative_spec.rb b/decidim-initiatives/spec/commands/decidim/initiatives/update_initiative_spec.rb index cbf124e67b621..041b19c28ef13 100644 --- a/decidim-initiatives/spec/commands/decidim/initiatives/update_initiative_spec.rb +++ b/decidim-initiatives/spec/commands/decidim/initiatives/update_initiative_spec.rb @@ -72,6 +72,57 @@ module Initiatives expect(initiative.description["en"]).to eq description end + context "when the initiative type enables custom signature end date" do + let(:initiative_type) { create(:initiatives_type, :custom_signature_end_date_enabled, organization: organization) } + let(:scoped_type) { create(:initiatives_type_scope, type: initiative_type) } + let!(:initiative) { create(:initiative, :created, organization: organization, scoped_type: scoped_type) } + + let(:form_params) do + { + title: title, + description: description, + signature_type: signature_type, + type_id: initiative_type.id, + attachment: attachment, + add_documents: uploaded_files, + documents: current_files, + signature_end_date: Date.tomorrow + } + end + + it "sets the signature end date" do + command.call + initiative = Decidim::Initiative.last + + expect(initiative.signature_end_date).to eq(Date.tomorrow) + end + end + + context "when the initiative type enables area" do + let(:initiative_type) { create(:initiatives_type, :area_enabled, organization: organization) } + let(:scoped_type) { create(:initiatives_type_scope, type: initiative_type) } + let!(:initiative) { create(:initiative, :created, organization: organization, scoped_type: scoped_type) } + let(:area) { create(:area, organization: initiative_type.organization) } + + let(:form_params) do + { + title: "A reasonable initiative title", + description: "A reasonable initiative description", + type_id: initiative_type.id, + signature_type: "online", + decidim_user_group_id: nil, + area_id: area.id + } + end + + it "sets the area" do + command.call + initiative = Decidim::Initiative.last + + expect(initiative.decidim_area_id).to eq(area.id) + end + end + context "when attachments are allowed" do let(:uploaded_files) do [ diff --git a/decidim-initiatives/spec/shared/create_initiative_example.rb b/decidim-initiatives/spec/shared/create_initiative_example.rb index 77d9a2c32b386..adaeac7118190 100644 --- a/decidim-initiatives/spec/shared/create_initiative_example.rb +++ b/decidim-initiatives/spec/shared/create_initiative_example.rb @@ -58,40 +58,6 @@ end.to change(Decidim::Initiative, :count).by(1) end - context "when attachment is present" do - let(:uploaded_files) do - [ - upload_test_file(Decidim::Dev.test_file("Exampledocument.pdf", "application/pdf")) - ] - end - - it "creates an attachment for the proposal" do - expect { command.call }.to change(Decidim::Attachment, :count).by(1) - last_initiative = Decidim::Initiative.last - last_attachment = Decidim::Attachment.last - expect(last_attachment.attached_to).to eq(last_initiative) - end - - context "when attachment is left blank" do - it "broadcasts ok" do - expect { command.call }.to broadcast(:ok) - end - end - end - - context "when has multiple attachments" do - let(:uploaded_files) do - [ - upload_test_file(Decidim::Dev.test_file("city.jpeg", "image/jpeg")), - upload_test_file(Decidim::Dev.test_file("Exampledocument.pdf", "application/pdf")) - ] - end - - it "creates multiple attachments for the initiative" do - expect { command.call }.to change(Decidim::Attachment, :count).by(2) - end - end - it "sets the author" do command.call initiative = Decidim::Initiative.last @@ -144,53 +110,6 @@ expect(initiative.signature_end_date).to be_nil end end - - context "when the initiative type enables custom signature end date" do - let(:initiative_type) { create(:initiatives_type, :custom_signature_end_date_enabled) } - - let(:form_params) do - { - title: "A reasonable initiative title", - description: "A reasonable initiative description", - type_id: scoped_type.type.id, - signature_type: "online", - scope_id: scoped_type.scope.id, - decidim_user_group_id: nil, - signature_end_date: Date.tomorrow - } - end - - it "sets the signature end date" do - command.call - initiative = Decidim::Initiative.last - - expect(initiative.signature_end_date).to eq(Date.tomorrow) - end - end - - context "when the initiative type enables area" do - let(:initiative_type) { create(:initiatives_type, :area_enabled) } - let(:area) { create(:area, organization: initiative_type.organization) } - - let(:form_params) do - { - title: "A reasonable initiative title", - description: "A reasonable initiative description", - type_id: scoped_type.type.id, - signature_type: "online", - scope_id: scoped_type.scope.id, - decidim_user_group_id: nil, - area_id: area.id - } - end - - it "sets the area" do - command.call - initiative = Decidim::Initiative.last - - expect(initiative.decidim_area_id).to eq(area.id) - end - end end end end diff --git a/decidim-initiatives/spec/system/create_initiative_spec.rb b/decidim-initiatives/spec/system/create_initiative_spec.rb index 1499404c75283..87e7f68946de2 100644 --- a/decidim-initiatives/spec/system/create_initiative_spec.rb +++ b/decidim-initiatives/spec/system/create_initiative_spec.rb @@ -9,7 +9,6 @@ let!(:authorized_user) { create(:user, :confirmed, organization: organization) } let!(:authorization) { create(:authorization, user: authorized_user) } let(:login) { true } - let(:initiative_type_minimum_committee_members) { 2 } let(:signature_type) { "any" } let(:initiative_type_promoting_committee_enabled) { true } @@ -559,6 +558,27 @@ context "when create initiative" do let(:initiative) { build(:initiative) } + context "when only one signature collection and scope are available" do + let(:signature_type) { "offline" } + let!(:other_initiative_type) { nil } + let!(:other_initiative_type_scope) { nil } + let(:initiative_type_scope2) { nil } + let(:initiative_type) { create(:initiatives_type, organization: organization, minimum_committee_members: initiative_type_minimum_committee_members, signature_type: signature_type) } + + before do + fill_in "Title", with: translated(initiative.title, locale: :en) + fill_in "initiative_description", with: translated(initiative.description, locale: :en) + find_button("Continue").click + end + + it "hides and automatically selects the values" do + expect(page).not_to have_content("Signature collection type") + expect(page).not_to have_content("Scope") + expect(find(:xpath, "//input[@id='initiative_type_id']", visible: :all).value).to eq(initiative_type.id.to_s) + expect(find(:xpath, "//input[@id='initiative_signature_type']", visible: :all).value).to eq("offline") + end + end + context "when there is only one initiative type" do let!(:other_initiative_type) { nil } let!(:other_initiative_type_scope) { nil } @@ -612,7 +632,7 @@ it "shows input for signature collection type" do expect(page).to have_content("Signature collection type") - expect(find(:xpath, "//select[@id='initiative_signature_type']", visible: :all).value).to eq("online") + expect(find(:xpath, "//select[@id='initiative_signature_type']", visible: :all).value).to eq(signature_type) end it "shows input for hashtag" do @@ -632,14 +652,6 @@ end end - context "when the scope is not selected" do - it "shows an error" do - select("Online", from: "Signature collection type") - find_button("Continue").click - expect(page).to have_content("There's an error in this field") - end - end - context "when the initiative type does not enable custom signature end date" do it "does not show the signature end date" do expect(page).not_to have_content("End of signature collection period") @@ -647,7 +659,8 @@ end context "when the initiative type enables custom signature end date" do - let(:initiative_type) { create(:initiatives_type, :custom_signature_end_date_enabled, organization: organization, minimum_committee_members: initiative_type_minimum_committee_members, signature_type: "offline") } + let(:signature_type) { "offline" } + let(:initiative_type) { create(:initiatives_type, :custom_signature_end_date_enabled, organization: organization, minimum_committee_members: initiative_type_minimum_committee_members, signature_type: signature_type) } it "shows the signature end date" do expect(page).to have_content("End of signature collection period") @@ -661,7 +674,8 @@ end context "when the initiative type enables area" do - let(:initiative_type) { create(:initiatives_type, :area_enabled, organization: organization, minimum_committee_members: initiative_type_minimum_committee_members, signature_type: "offline") } + let(:signature_type) { "offline" } + let(:initiative_type) { create(:initiatives_type, :area_enabled, organization: organization, minimum_committee_members: initiative_type_minimum_committee_members, signature_type: signature_type) } it "shows the area" do expect(page).to have_content("Area") @@ -681,7 +695,7 @@ end end - context "when there's a promoter committee" do + context "when there is a promoter committee" do let(:initiative) { build(:initiative, organization: organization, scoped_type: initiative_type_scope) } before do @@ -693,7 +707,6 @@ find_button("Continue").click select("Online", from: "Signature collection type") - select(translated(initiative_type_scope.scope.name, locale: :en), from: "Scope") find_button("Continue").click end @@ -726,7 +739,7 @@ end end - context "and it's disabled at the type scope" do + context "and it is disabled at the type scope" do let(:initiative_type) { create(:initiatives_type, organization: organization, promoting_committee_enabled: false, signature_type: signature_type) } it "skips the promoting committee settings" do @@ -736,6 +749,30 @@ end end + context "when the initiative is created by an user group" do + let(:organization) { create(:organization, available_authorizations: authorizations, user_groups_enabled: true) } + let(:initiative) { build(:initiative) } + let!(:user_group) { create(:user_group, :verified, organization: organization, users: [authorized_user]) } + + before do + authorized_user.reload + find_button("I want to promote this initiative").click + + fill_in "Title", with: translated(initiative.title, locale: :en) + fill_in "initiative_description", with: translated(initiative.description, locale: :en) + find_button("Continue").click + + select("Online", from: "Signature collection type") + select(user_group.name, from: "Author") + end + + it "shows the user group as author" do + expect(Decidim::Initiative.where(decidim_user_group_id: user_group.id).count).to eq(0) + find_button("Continue").click + expect(Decidim::Initiative.where(decidim_user_group_id: user_group.id).count).to eq(1) + end + end + context "when finish" do let(:initiative) { build(:initiative) } @@ -746,7 +783,6 @@ fill_in "initiative_description", with: translated(initiative.description, locale: :en) find_button("Continue").click - select(translated(initiative_type_scope.scope.name, locale: :en), from: "Scope") select("Online", from: "Signature collection type") dynamically_attach_file(:initiative_documents, Decidim::Dev.asset("Exampledocument.pdf")) find_button("Continue").click