From 82e8de094b5cde1d435059299b05e40a38594910 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javi=20Mart=C3=ADn?= Date: Fri, 4 Jun 2021 21:58:09 +0200 Subject: [PATCH 01/18] Extract budget form to a component This way it will be easier to change it and reuse it. --- .../admin/budgets/form_component.html.erb} | 36 +++++++++--------- .../admin/budgets/form_component.rb | 38 +++++++++++++++++++ app/controllers/admin/budgets_controller.rb | 6 --- app/helpers/budgets_helper.rb | 14 ------- app/views/admin/budgets/edit.html.erb | 2 +- app/views/admin/budgets/new.html.erb | 2 +- .../admin/budgets/form_component_spec.rb | 16 ++++++++ spec/helpers/budgets_helper_spec.rb | 14 ------- 8 files changed, 74 insertions(+), 54 deletions(-) rename app/{views/admin/budgets/_form.html.erb => components/admin/budgets/form_component.html.erb} (65%) create mode 100644 app/components/admin/budgets/form_component.rb create mode 100644 spec/components/admin/budgets/form_component_spec.rb delete mode 100644 spec/helpers/budgets_helper_spec.rb diff --git a/app/views/admin/budgets/_form.html.erb b/app/components/admin/budgets/form_component.html.erb similarity index 65% rename from app/views/admin/budgets/_form.html.erb rename to app/components/admin/budgets/form_component.html.erb index 295f1234a..3391bc6f7 100644 --- a/app/views/admin/budgets/_form.html.erb +++ b/app/components/admin/budgets/form_component.html.erb @@ -1,8 +1,8 @@ -<%= translatable_form_for [:admin, @budget], html: { class: "budgets-form" } do |f| %> +<%= translatable_form_for [:admin, budget], html: { class: "budgets-form" } do |f| %>
<%= t("admin.budgets.edit.info.budget_settings") %> - <%= render "shared/globalize_locales", resource: @budget %> - <%= render "shared/errors", resource: @budget %> + <%= render "shared/globalize_locales", resource: budget %> + <%= render "shared/errors", resource: budget %> <%= f.translatable_fields do |translations_form| %>
@@ -13,11 +13,11 @@ <% end %>
- <%= f.select :voting_style, budget_voting_styles_select_options %> + <%= f.select :voting_style, voting_styles_select_options %>
- <%= f.select :currency_symbol, budget_currency_symbol_select_options %> + <%= f.select :currency_symbol, currency_symbol_select_options %>
@@ -26,28 +26,28 @@ <% %w[administrators valuators].each do |staff| %>
- <%= link_to t("admin.budgets.edit.#{staff}", count: @budget.send(staff).count), + <%= link_to t("admin.budgets.edit.#{staff}", count: budget.send(staff).count), "#", class: "button expanded hollow js-budget-show-#{staff}-list js-budget-show-users-list", data: { toggle: "#{staff}_list", texts: t("admin.budgets.edit.#{staff}") } %>
<% end %> - <%= render "/admin/budgets/association", assignable_type: "administrators", assignables: @admins, form: f %> - <%= render "/admin/budgets/association", assignable_type: "valuators", assignables: @valuators, form: f %> + <%= render "/admin/budgets/association", assignable_type: "administrators", assignables: admins, form: f %> + <%= render "/admin/budgets/association", assignable_type: "valuators", assignables: valuators, form: f %>
<%= t("admin.budgets.edit.info.phases_settings") %>
- <%= f.select :phase, budget_phases_select_options %> + <%= f.select :phase, phases_select_options %>
<%= render Admin::Budgets::HelpComponent.new("budget_phases") %> - <%= render Admin::BudgetPhases::PhasesComponent.new(@budget) %> + <%= render Admin::BudgetPhases::PhasesComponent.new(budget) %>
- <% if @budget.persisted? %> + <% if budget.persisted? %> <%= render "admin/shared/show_results_fields", form: f %> <% end %> @@ -57,21 +57,21 @@
- <% if display_calculate_winners_button?(@budget) %> - <%= link_to calculate_winner_button_text(@budget), - calculate_winners_admin_budget_path(@budget), + <% if display_calculate_winners_button?(budget) %> + <%= link_to calculate_winner_button_text(budget), + calculate_winners_admin_budget_path(budget), method: :put, class: "button hollow" %> <% end %> - <% if @budget.has_winning_investments? %> + <% if budget.has_winning_investments? %> <%= link_to t("budgets.show.see_results"), - budget_results_path(@budget), + budget_results_path(budget), class: "button hollow margin-left" %> <% end %> - <% if @budget.persisted? %> + <% if budget.persisted? %> <%= link_to t("admin.budgets.edit.delete"), - admin_budget_path(@budget), + admin_budget_path(budget), method: :delete, class: "delete float-right margin-left" %> <% end %> diff --git a/app/components/admin/budgets/form_component.rb b/app/components/admin/budgets/form_component.rb new file mode 100644 index 000000000..93752cefc --- /dev/null +++ b/app/components/admin/budgets/form_component.rb @@ -0,0 +1,38 @@ +class Admin::Budgets::FormComponent < ApplicationComponent + include TranslatableFormHelper + include GlobalizeHelper + + attr_reader :budget + delegate :display_calculate_winners_button?, + :calculate_winner_button_text, + :calculate_winners_admin_budget_path, + to: :helpers + + def initialize(budget) + @budget = budget + end + + def voting_styles_select_options + Budget::VOTING_STYLES.map do |style| + [Budget.human_attribute_name("voting_style_#{style}"), style] + end + end + + def currency_symbol_select_options + Budget::CURRENCY_SYMBOLS.map { |cs| [cs, cs] } + end + + def phases_select_options + Budget::Phase::PHASE_KINDS.map { |ph| [t("budgets.phase.#{ph}"), ph] } + end + + private + + def admins + @admins ||= Administrator.includes(:user) + end + + def valuators + @valuators ||= Valuator.includes(:user).order(description: :asc).order("users.email ASC") + end +end diff --git a/app/controllers/admin/budgets_controller.rb b/app/controllers/admin/budgets_controller.rb index b6adc0a0a..a35396342 100644 --- a/app/controllers/admin/budgets_controller.rb +++ b/app/controllers/admin/budgets_controller.rb @@ -7,7 +7,6 @@ class Admin::BudgetsController < Admin::BaseController has_filters %w[all open finished], only: :index before_action :load_budget, except: [:index, :new, :create] - before_action :load_staff, only: [:new, :create, :edit, :update, :show] load_and_authorize_resource def index @@ -83,9 +82,4 @@ class Admin::BudgetsController < Admin::BaseController def load_budget @budget = Budget.find_by_slug_or_id! params[:id] end - - def load_staff - @admins = Administrator.includes(:user) - @valuators = Valuator.includes(:user).order(description: :asc).order("users.email ASC") - end end diff --git a/app/helpers/budgets_helper.rb b/app/helpers/budgets_helper.rb index 6d5751302..e07b73192 100644 --- a/app/helpers/budgets_helper.rb +++ b/app/helpers/budgets_helper.rb @@ -1,10 +1,4 @@ module BudgetsHelper - def budget_voting_styles_select_options - Budget::VOTING_STYLES.map do |style| - [Budget.human_attribute_name("voting_style_#{style}"), style] - end - end - def csv_params csv_params = params.clone.merge(format: :csv) csv_params = csv_params.to_unsafe_h.map { |k, v| [k.to_sym, v] }.to_h @@ -12,14 +6,6 @@ module BudgetsHelper csv_params end - def budget_phases_select_options - Budget::Phase::PHASE_KINDS.map { |ph| [t("budgets.phase.#{ph}"), ph] } - end - - def budget_currency_symbol_select_options - Budget::CURRENCY_SYMBOLS.map { |cs| [cs, cs] } - end - def namespaced_budget_investment_path(investment, options = {}) case namespace when "management" diff --git a/app/views/admin/budgets/edit.html.erb b/app/views/admin/budgets/edit.html.erb index 70cdcfd69..ad5d44f76 100644 --- a/app/views/admin/budgets/edit.html.erb +++ b/app/views/admin/budgets/edit.html.erb @@ -5,4 +5,4 @@ <%= render Admin::Budgets::DraftingComponent.new(@budget) %> -<%= render "/admin/budgets/form" %> +<%= render Admin::Budgets::FormComponent.new(@budget) %> diff --git a/app/views/admin/budgets/new.html.erb b/app/views/admin/budgets/new.html.erb index 12bdf42f8..90d802424 100644 --- a/app/views/admin/budgets/new.html.erb +++ b/app/views/admin/budgets/new.html.erb @@ -4,4 +4,4 @@

<%= t("admin.budgets.new.title") %>

-<%= render "/admin/budgets/form" %> +<%= render Admin::Budgets::FormComponent.new(@budget) %> diff --git a/spec/components/admin/budgets/form_component_spec.rb b/spec/components/admin/budgets/form_component_spec.rb new file mode 100644 index 000000000..1abacdeee --- /dev/null +++ b/spec/components/admin/budgets/form_component_spec.rb @@ -0,0 +1,16 @@ +require "rails_helper" + +describe Admin::Budgets::FormComponent, type: :component do + describe "#voting_styles_select_options" do + it "provides vote kinds" do + types = [ + ["Knapsack", "knapsack"], + ["Approval", "approval"] + ] + + component = Admin::Budgets::FormComponent.new(double) + + expect(component.voting_styles_select_options).to eq(types) + end + end +end diff --git a/spec/helpers/budgets_helper_spec.rb b/spec/helpers/budgets_helper_spec.rb deleted file mode 100644 index 832f510d8..000000000 --- a/spec/helpers/budgets_helper_spec.rb +++ /dev/null @@ -1,14 +0,0 @@ -require "rails_helper" - -describe BudgetsHelper do - describe "#budget_voting_styles_select_options" do - it "provides vote kinds" do - types = [ - ["Knapsack", "knapsack"], - ["Approval", "approval"] - ] - - expect(budget_voting_styles_select_options).to eq(types) - end - end -end From 2115eb5274c85fd589da6acc2404c1195ed1d21b Mon Sep 17 00:00:00 2001 From: Julian Herrero Date: Sun, 15 Mar 2020 06:51:52 +0100 Subject: [PATCH 02/18] Allow creating budgets step by step We introduce the first step (creating the budget). Co-Authored-By: decabeza --- .../budgets_wizard/creation_timeline.scss | 34 ++++++++ .../admin/budgets/form_component.html.erb | 21 +++-- .../admin/budgets/form_component.rb | 8 ++ .../admin/budgets/index_component.html.erb | 2 +- .../budgets/new_component.html.erb | 6 ++ .../budgets_wizard/budgets/new_component.rb | 12 +++ .../creation_timeline_component.html.erb | 5 ++ .../creation_timeline_component.rb | 2 + app/controllers/admin/budgets_controller.rb | 14 +--- .../budgets_wizard/budgets_controller.rb | 32 ++++++++ app/views/admin/budgets/new.html.erb | 7 -- .../admin/budgets_wizard/budgets/new.html.erb | 1 + config/locales/en/admin.yml | 3 + config/locales/es/admin.yml | 3 + config/routes/admin.rb | 6 +- spec/system/admin/budgets_spec.rb | 82 ------------------- .../admin/budgets_wizard/budgets_spec.rb | 82 +++++++++++++++++++ 17 files changed, 205 insertions(+), 115 deletions(-) create mode 100644 app/assets/stylesheets/admin/budgets_wizard/creation_timeline.scss create mode 100644 app/components/admin/budgets_wizard/budgets/new_component.html.erb create mode 100644 app/components/admin/budgets_wizard/budgets/new_component.rb create mode 100644 app/components/admin/budgets_wizard/creation_timeline_component.html.erb create mode 100644 app/components/admin/budgets_wizard/creation_timeline_component.rb create mode 100644 app/controllers/admin/budgets_wizard/budgets_controller.rb delete mode 100644 app/views/admin/budgets/new.html.erb create mode 100644 app/views/admin/budgets_wizard/budgets/new.html.erb create mode 100644 spec/system/admin/budgets_wizard/budgets_spec.rb diff --git a/app/assets/stylesheets/admin/budgets_wizard/creation_timeline.scss b/app/assets/stylesheets/admin/budgets_wizard/creation_timeline.scss new file mode 100644 index 000000000..539abe84c --- /dev/null +++ b/app/assets/stylesheets/admin/budgets_wizard/creation_timeline.scss @@ -0,0 +1,34 @@ +.creation-timeline { + display: flex; + list-style-type: none; + margin: $line-height * 2 0; + position: relative; + + li { + border-top: 4px solid $admin-border-color; + display: inline-block; + font-size: $small-font-size; + font-weight: bold; + padding: $line-height / 2 $line-height * 3 0; + text-transform: uppercase; + + &::before { + background: $admin-border-color; + border-radius: 50%; + content: ""; + height: 20px; + margin-left: $line-height / 2; + position: absolute; + top: -8px; + width: 20px; + } + + &[aria-current] { + border-color: $brand; + + &::before { + background: $brand; + } + } + } +} diff --git a/app/components/admin/budgets/form_component.html.erb b/app/components/admin/budgets/form_component.html.erb index 3391bc6f7..64159594f 100644 --- a/app/components/admin/budgets/form_component.html.erb +++ b/app/components/admin/budgets/form_component.html.erb @@ -1,4 +1,4 @@ -<%= translatable_form_for [:admin, budget], html: { class: "budgets-form" } do |f| %> +<%= translatable_form_for [namespace, budget], html: { class: "budgets-form" } do |f| %>
<%= t("admin.budgets.edit.info.budget_settings") %> <%= render "shared/globalize_locales", resource: budget %> @@ -37,17 +37,16 @@ <%= render "/admin/budgets/association", assignable_type: "valuators", assignables: valuators, form: f %>
-
- <%= t("admin.budgets.edit.info.phases_settings") %> -
- <%= f.select :phase, phases_select_options %> -
- - <%= render Admin::Budgets::HelpComponent.new("budget_phases") %> - <%= render Admin::BudgetPhases::PhasesComponent.new(budget) %> -
- <% if budget.persisted? %> +
+ <%= t("admin.budgets.edit.info.phases_settings") %> +
+ <%= f.select :phase, phases_select_options %> +
+ + <%= render Admin::BudgetPhases::PhasesComponent.new(budget) %> +
+ <%= render "admin/shared/show_results_fields", form: f %> <% end %> diff --git a/app/components/admin/budgets/form_component.rb b/app/components/admin/budgets/form_component.rb index 93752cefc..cb6c3344a 100644 --- a/app/components/admin/budgets/form_component.rb +++ b/app/components/admin/budgets/form_component.rb @@ -12,6 +12,14 @@ class Admin::Budgets::FormComponent < ApplicationComponent @budget = budget end + def namespace + if controller.class.name.starts_with?("Admin::BudgetsWizard") + :admin_budgets_wizard + else + helpers.namespace.to_sym + end + end + def voting_styles_select_options Budget::VOTING_STYLES.map do |style| [Budget.human_attribute_name("voting_style_#{style}"), style] diff --git a/app/components/admin/budgets/index_component.html.erb b/app/components/admin/budgets/index_component.html.erb index feb881352..35e6fce7f 100644 --- a/app/components/admin/budgets/index_component.html.erb +++ b/app/components/admin/budgets/index_component.html.erb @@ -1,5 +1,5 @@ <%= header do %> - <%= link_to t("admin.budgets.index.new_link"), new_admin_budget_path %> + <%= link_to t("admin.budgets.index.new_link"), new_admin_budgets_wizard_budget_path %> <% end %> <%= render Admin::Budgets::HelpComponent.new("budgets") %> diff --git a/app/components/admin/budgets_wizard/budgets/new_component.html.erb b/app/components/admin/budgets_wizard/budgets/new_component.html.erb new file mode 100644 index 000000000..e2e227928 --- /dev/null +++ b/app/components/admin/budgets_wizard/budgets/new_component.html.erb @@ -0,0 +1,6 @@ +<%= back_link_to admin_budgets_path %> + +<%= header %> + +<%= render Admin::BudgetsWizard::CreationTimelineComponent.new %> +<%= render Admin::Budgets::FormComponent.new(budget) %> diff --git a/app/components/admin/budgets_wizard/budgets/new_component.rb b/app/components/admin/budgets_wizard/budgets/new_component.rb new file mode 100644 index 000000000..09502de89 --- /dev/null +++ b/app/components/admin/budgets_wizard/budgets/new_component.rb @@ -0,0 +1,12 @@ +class Admin::BudgetsWizard::Budgets::NewComponent < ApplicationComponent + include Header + attr_reader :budget + + def initialize(budget) + @budget = budget + end + + def title + t("admin.budgets.new.title") + end +end diff --git a/app/components/admin/budgets_wizard/creation_timeline_component.html.erb b/app/components/admin/budgets_wizard/creation_timeline_component.html.erb new file mode 100644 index 000000000..3a566c277 --- /dev/null +++ b/app/components/admin/budgets_wizard/creation_timeline_component.html.erb @@ -0,0 +1,5 @@ +
    +
  1. + <%= t("admin.budgets_wizard.creation_timeline.budget") %> +
  2. +
diff --git a/app/components/admin/budgets_wizard/creation_timeline_component.rb b/app/components/admin/budgets_wizard/creation_timeline_component.rb new file mode 100644 index 000000000..47e5a4d2a --- /dev/null +++ b/app/components/admin/budgets_wizard/creation_timeline_component.rb @@ -0,0 +1,2 @@ +class Admin::BudgetsWizard::CreationTimelineComponent < ApplicationComponent +end diff --git a/app/controllers/admin/budgets_controller.rb b/app/controllers/admin/budgets_controller.rb index a35396342..572d55fc1 100644 --- a/app/controllers/admin/budgets_controller.rb +++ b/app/controllers/admin/budgets_controller.rb @@ -6,7 +6,7 @@ class Admin::BudgetsController < Admin::BaseController has_filters %w[all open finished], only: :index - before_action :load_budget, except: [:index, :new, :create] + before_action :load_budget, except: [:index] load_and_authorize_resource def index @@ -17,9 +17,6 @@ class Admin::BudgetsController < Admin::BaseController render :edit end - def new - end - def edit end @@ -46,15 +43,6 @@ class Admin::BudgetsController < Admin::BaseController end end - def create - @budget = Budget.new(budget_params.merge(published: false)) - if @budget.save - redirect_to edit_admin_budget_path(@budget), notice: t("admin.budgets.create.notice") - else - render :new - end - end - def destroy if @budget.investments.any? redirect_to admin_budgets_path, alert: t("admin.budgets.destroy.unable_notice") diff --git a/app/controllers/admin/budgets_wizard/budgets_controller.rb b/app/controllers/admin/budgets_wizard/budgets_controller.rb new file mode 100644 index 000000000..1efb41379 --- /dev/null +++ b/app/controllers/admin/budgets_wizard/budgets_controller.rb @@ -0,0 +1,32 @@ +class Admin::BudgetsWizard::BudgetsController < Admin::BaseController + include Translatable + include FeatureFlags + feature_flag :budgets + + load_and_authorize_resource + + def new + end + + def create + @budget.published = false + + if @budget.save + redirect_to edit_admin_budget_path(@budget), notice: t("admin.budgets.create.notice") + else + render :new + end + end + + private + + def budget_params + params.require(:budget).permit(*allowed_params) + end + + def allowed_params + valid_attributes = [:currency_symbol, :voting_style, administrator_ids: [], valuator_ids: []] + + valid_attributes + [translation_params(Budget)] + end +end diff --git a/app/views/admin/budgets/new.html.erb b/app/views/admin/budgets/new.html.erb deleted file mode 100644 index 90d802424..000000000 --- a/app/views/admin/budgets/new.html.erb +++ /dev/null @@ -1,7 +0,0 @@ -<%= back_link_to admin_budgets_path %> - -
-

<%= t("admin.budgets.new.title") %>

-
- -<%= render Admin::Budgets::FormComponent.new(@budget) %> diff --git a/app/views/admin/budgets_wizard/budgets/new.html.erb b/app/views/admin/budgets_wizard/budgets/new.html.erb new file mode 100644 index 000000000..6ebfd4521 --- /dev/null +++ b/app/views/admin/budgets_wizard/budgets/new.html.erb @@ -0,0 +1 @@ +<%= render Admin::BudgetsWizard::Budgets::NewComponent.new(@budget) %> diff --git a/config/locales/en/admin.yml b/config/locales/en/admin.yml index e0f3f20ca..4f459b453 100644 --- a/config/locales/en/admin.yml +++ b/config/locales/en/admin.yml @@ -284,6 +284,9 @@ en: tags_placeholder: "Write the tags you want separated by commas (,)" undefined: Undefined search_unfeasible: Search unfeasible + budgets_wizard: + creation_timeline: + budget: Budget milestones: index: table_id: "ID" diff --git a/config/locales/es/admin.yml b/config/locales/es/admin.yml index 3e6db5a74..89cd99119 100644 --- a/config/locales/es/admin.yml +++ b/config/locales/es/admin.yml @@ -284,6 +284,9 @@ es: tags_placeholder: "Escribe las etiquetas que desees separadas por comas (,)" undefined: Sin definir search_unfeasible: Buscar inviables + budgets_wizard: + creation_timeline: + budget: Presupuesto milestones: index: table_id: "ID" diff --git a/config/routes/admin.rb b/config/routes/admin.rb index 21712e04a..23cd9867a 100644 --- a/config/routes/admin.rb +++ b/config/routes/admin.rb @@ -51,7 +51,7 @@ namespace :admin do end end - resources :budgets do + resources :budgets, except: [:create, :new] do member do patch :publish put :calculate_winners @@ -72,6 +72,10 @@ namespace :admin do resources :budget_phases, only: [:edit, :update] end + namespace :budgets_wizard do + resources :budgets, only: [:create, :new] + end + resources :milestone_statuses, only: [:index, :new, :create, :update, :edit, :destroy] resources :signature_sheets, only: [:index, :new, :create, :show] diff --git a/spec/system/admin/budgets_spec.rb b/spec/system/admin/budgets_spec.rb index e6a5ab6a4..50f8c3de8 100644 --- a/spec/system/admin/budgets_spec.rb +++ b/spec/system/admin/budgets_spec.rb @@ -84,88 +84,6 @@ describe "Admin budgets", :admin do end end - context "New" do - scenario "Create budget - Knapsack voting (default)" do - visit admin_budgets_path - click_link "Create new budget" - - fill_in "Name", with: "M30 - Summer campaign" - select "Accepting projects", from: "budget[phase]" - - click_button "Create Budget" - - expect(page).to have_content "New participatory budget created successfully!" - expect(page).to have_field "Name", with: "M30 - Summer campaign" - expect(page).to have_select "Final voting style", selected: "Knapsack" - end - - scenario "Create budget - Approval voting" do - admin = Administrator.first - - visit admin_budgets_path - click_link "Create new budget" - - fill_in "Name", with: "M30 - Summer campaign" - select "Accepting projects", from: "budget[phase]" - select "Approval", from: "Final voting style" - click_button "Create Budget" - - expect(page).to have_content "New participatory budget created successfully!" - expect(page).to have_field "Name", with: "M30 - Summer campaign" - expect(page).to have_select "Final voting style", selected: "Approval" - - click_link "Select administrators" - - expect(page).to have_field admin.name - end - - scenario "Name is mandatory" do - visit new_admin_budget_path - click_button "Create Budget" - - expect(page).not_to have_content "New participatory budget created successfully!" - expect(page).to have_css(".is-invalid-label", text: "Name") - end - - scenario "Name should be unique" do - create(:budget, name: "Existing Name") - - visit new_admin_budget_path - fill_in "Name", with: "Existing Name" - click_button "Create Budget" - - expect(page).not_to have_content "New participatory budget created successfully!" - expect(page).to have_css(".is-invalid-label", text: "Name") - expect(page).to have_css("small.form-error", text: "has already been taken") - end - - scenario "Do not show results and stats settings on new budget" do - visit new_admin_budget_path - - expect(page).not_to have_content "Show results and stats" - expect(page).not_to have_field "Show results" - expect(page).not_to have_field "Show stats" - expect(page).not_to have_field "Show advanced stats" - end - end - - context "Create" do - scenario "A new budget is always created in draft mode" do - visit admin_budgets_path - click_link "Create new budget" - - fill_in "Name", with: "M30 - Summer campaign" - select "Accepting projects", from: "budget[phase]" - - click_button "Create Budget" - - expect(page).to have_content "New participatory budget created successfully!" - expect(page).to have_content "This participatory budget is in draft mode" - expect(page).to have_link "Preview budget" - expect(page).to have_link "Publish budget" - end - end - context "Publish" do let(:budget) { create(:budget, :drafting) } diff --git a/spec/system/admin/budgets_wizard/budgets_spec.rb b/spec/system/admin/budgets_wizard/budgets_spec.rb new file mode 100644 index 000000000..13cb9a75d --- /dev/null +++ b/spec/system/admin/budgets_wizard/budgets_spec.rb @@ -0,0 +1,82 @@ +require "rails_helper" + +describe "Budgets wizard, first step", :admin do + describe "New" do + scenario "Create budget - Knapsack voting (default)" do + visit admin_budgets_path + click_link "Create new budget" + + fill_in "Name", with: "M30 - Summer campaign" + click_button "Create Budget" + + expect(page).to have_content "New participatory budget created successfully!" + expect(page).to have_field "Name", with: "M30 - Summer campaign" + expect(page).to have_select "Final voting style", selected: "Knapsack" + end + + scenario "Create budget - Approval voting" do + admin = Administrator.first + + visit admin_budgets_path + click_link "Create new budget" + + fill_in "Name", with: "M30 - Summer campaign" + select "Approval", from: "Final voting style" + click_button "Create Budget" + + expect(page).to have_content "New participatory budget created successfully!" + expect(page).to have_field "Name", with: "M30 - Summer campaign" + expect(page).to have_select "Final voting style", selected: "Approval" + + click_link "Select administrators" + + expect(page).to have_field admin.name + end + + scenario "Submit the form with errors" do + visit new_admin_budgets_wizard_budget_path + click_button "Create Budget" + + expect(page).not_to have_content "New participatory budget created successfully!" + expect(page).to have_css ".is-invalid-label", text: "Name" + expect(page).to have_css ".creation-timeline" + end + + scenario "Name should be unique" do + create(:budget, name: "Existing Name") + + visit new_admin_budgets_wizard_budget_path + fill_in "Name", with: "Existing Name" + click_button "Create Budget" + + expect(page).not_to have_content "New participatory budget created successfully!" + expect(page).to have_css(".is-invalid-label", text: "Name") + expect(page).to have_css("small.form-error", text: "has already been taken") + end + + scenario "Do not show results and stats settings on new budget" do + visit new_admin_budgets_wizard_budget_path + + expect(page).not_to have_content "Show results and stats" + expect(page).not_to have_field "Show results" + expect(page).not_to have_field "Show stats" + expect(page).not_to have_field "Show advanced stats" + end + end + + describe "Create" do + scenario "A new budget is always created in draft mode" do + visit admin_budgets_path + click_link "Create new budget" + + fill_in "Name", with: "M30 - Summer campaign" + + click_button "Create Budget" + + expect(page).to have_content "New participatory budget created successfully!" + expect(page).to have_content "This participatory budget is in draft mode" + expect(page).to have_link "Preview budget" + expect(page).to have_link "Publish budget" + end + end +end From b16fe80ba5c89e8196f13fd3a26d0f6e4acc7a62 Mon Sep 17 00:00:00 2001 From: Julian Herrero Date: Tue, 17 Mar 2020 12:55:35 +0700 Subject: [PATCH 03/18] Extract groups and headings tables to components --- .../budget_groups/groups_component.html.erb | 33 +++++++++++++++ .../admin/budget_groups/groups_component.rb | 13 ++++++ .../headings_component.html.erb | 39 ++++++++++++++++++ .../budget_headings/headings_component.rb | 17 ++++++++ app/views/admin/budget_groups/index.html.erb | 35 +--------------- .../admin/budget_headings/index.html.erb | 41 +------------------ 6 files changed, 104 insertions(+), 74 deletions(-) create mode 100644 app/components/admin/budget_groups/groups_component.html.erb create mode 100644 app/components/admin/budget_groups/groups_component.rb create mode 100644 app/components/admin/budget_headings/headings_component.html.erb create mode 100644 app/components/admin/budget_headings/headings_component.rb diff --git a/app/components/admin/budget_groups/groups_component.html.erb b/app/components/admin/budget_groups/groups_component.html.erb new file mode 100644 index 000000000..00bcc18fc --- /dev/null +++ b/app/components/admin/budget_groups/groups_component.html.erb @@ -0,0 +1,33 @@ +<% if groups.any? %> +

<%= t("admin.budget_groups.amount", count: groups.count) %>

+ + + + + + + + + + + <% groups.each do |group| %> + + + + + + + <% end %> + +
<%= t("admin.budget_groups.name") %><%= Budget::Group.human_attribute_name(:max_votable_headings) %><%= t("admin.budget_groups.headings_name") %><%= t("admin.actions.actions") %>
<%= group.name %><%= group.max_votable_headings %><%= group.headings.count %> + <%= render Admin::TableActionsComponent.new(group) do |actions| %> + <%= actions.link_to t("admin.budget_groups.headings_manage"), + admin_budget_group_headings_path(budget, group), + class: "headings-link" %> + <% end %> +
+<% else %> +
+ <%= t("admin.budget_groups.no_groups") %> +
+<% end %> diff --git a/app/components/admin/budget_groups/groups_component.rb b/app/components/admin/budget_groups/groups_component.rb new file mode 100644 index 000000000..87012734d --- /dev/null +++ b/app/components/admin/budget_groups/groups_component.rb @@ -0,0 +1,13 @@ +class Admin::BudgetGroups::GroupsComponent < ApplicationComponent + attr_reader :groups + + def initialize(groups) + @groups = groups + end + + private + + def budget + @budget ||= groups.first.budget + end +end diff --git a/app/components/admin/budget_headings/headings_component.html.erb b/app/components/admin/budget_headings/headings_component.html.erb new file mode 100644 index 000000000..9e32dcbe5 --- /dev/null +++ b/app/components/admin/budget_headings/headings_component.html.erb @@ -0,0 +1,39 @@ +<% if headings.any? %> +

<%= t("admin.budget_headings.amount", count: headings.count) %>

+ + + + + + <% if budget.approval_voting? %> + + <% end %> + + + + + + + <% headings.each do |heading| %> + + + + <% if budget.approval_voting? %> + + <% end %> + + + + + <% end %> + +
<%= Budget::Heading.human_attribute_name(:name) %><%= Budget::Heading.human_attribute_name(:price) %><%= Budget::Heading.human_attribute_name(:max_ballot_lines) %><%= Budget::Heading.human_attribute_name(:population) %><%= Budget::Heading.human_attribute_name(:allow_custom_content) %><%= t("admin.actions.actions") %>
<%= heading.name %><%= budget.formatted_heading_price(heading) %><%= heading.max_ballot_lines %><%= heading.population %> + <%= heading.allow_custom_content ? t("admin.shared.true_value") : t("admin.shared.false_value") %> + + <%= render Admin::TableActionsComponent.new(heading) %> +
+<% else %> +
+ <%= t("admin.budget_headings.no_headings") %> +
+<% end %> diff --git a/app/components/admin/budget_headings/headings_component.rb b/app/components/admin/budget_headings/headings_component.rb new file mode 100644 index 000000000..3b6594f23 --- /dev/null +++ b/app/components/admin/budget_headings/headings_component.rb @@ -0,0 +1,17 @@ +class Admin::BudgetHeadings::HeadingsComponent < ApplicationComponent + attr_reader :headings + + def initialize(headings) + @headings = headings + end + + private + + def group + @group ||= headings.first.group + end + + def budget + @budget ||= headings.first.budget + end +end diff --git a/app/views/admin/budget_groups/index.html.erb b/app/views/admin/budget_groups/index.html.erb index 2e20ac1ac..f89d3ee20 100644 --- a/app/views/admin/budget_groups/index.html.erb +++ b/app/views/admin/budget_groups/index.html.erb @@ -8,37 +8,4 @@ <%= render Admin::Budgets::HelpComponent.new("budget_groups") %> - -<% if @groups.any? %> -

<%= t("admin.budget_groups.amount", count: @groups.count) %>

- - - - - - - - - - - <% @groups.each do |group| %> - - - - - - - <% end %> - -
<%= t("admin.budget_groups.name") %><%= Budget::Group.human_attribute_name(:max_votable_headings) %><%= t("admin.budget_groups.headings_name") %><%= t("admin.actions.actions") %>
<%= group.name %><%= group.max_votable_headings %><%= group.headings.count %> - <%= render Admin::TableActionsComponent.new(group) do |actions| %> - <%= actions.link_to t("admin.budget_groups.headings_manage"), - admin_budget_group_headings_path(@budget, group), - class: "headings-link" %> - <% end %> -
-<% else %> -
- <%= t("admin.budget_groups.no_groups") %> -
-<% end %> +<%= render Admin::BudgetGroups::GroupsComponent.new(@groups) %> diff --git a/app/views/admin/budget_headings/index.html.erb b/app/views/admin/budget_headings/index.html.erb index 4487a8f4b..f154b5ae9 100644 --- a/app/views/admin/budget_headings/index.html.erb +++ b/app/views/admin/budget_headings/index.html.erb @@ -6,43 +6,4 @@ <%= render Admin::Budgets::HelpComponent.new("budget_headings") %> - -<% if @headings.any? %> -

<%= t("admin.budget_headings.amount", count: @headings.count) %>

- - - - - - <% if @budget.approval_voting? %> - - <% end %> - - - - - - - <% @headings.each do |heading| %> - - - - <% if @budget.approval_voting? %> - - <% end %> - - - - - <% end %> - -
<%= Budget::Heading.human_attribute_name(:name) %><%= Budget::Heading.human_attribute_name(:price) %><%= Budget::Heading.human_attribute_name(:max_ballot_lines) %><%= Budget::Heading.human_attribute_name(:population) %><%= Budget::Heading.human_attribute_name(:allow_custom_content) %><%= t("admin.actions.actions") %>
<%= heading.name %><%= @budget.formatted_heading_price(heading) %><%= heading.max_ballot_lines %><%= heading.population %> - <%= heading.allow_custom_content ? t("admin.shared.true_value") : t("admin.shared.false_value") %> - - <%= render Admin::TableActionsComponent.new(heading) %> -
-<% else %> -
- <%= t("admin.budget_headings.no_headings") %> -
-<% end %> +<%= render Admin::BudgetHeadings::HeadingsComponent.new(@headings) %> From de27c7a56cc1e5e59b439268dd900258b67990b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javi=20Mart=C3=ADn?= Date: Wed, 2 Jun 2021 16:10:48 +0200 Subject: [PATCH 04/18] Use local variables in group and heading partials We usually prefer local variables over instance variables in partials. This way we'll be able to call the partial from views or components where the instance variable isn't available. And since we're using the `path` variable to configure the URL, we don't have to specify extra variables like `@budget` or the namespace `:admin` in `form_for`, since Rails only uses those variables to set the URL. --- app/views/admin/budget_groups/_form.html.erb | 10 +++++----- app/views/admin/budget_groups/edit.html.erb | 2 +- app/views/admin/budget_groups/new.html.erb | 2 +- app/views/admin/budget_headings/_form.html.erb | 8 ++++---- app/views/admin/budget_headings/edit.html.erb | 2 +- app/views/admin/budget_headings/new.html.erb | 2 +- 6 files changed, 13 insertions(+), 13 deletions(-) diff --git a/app/views/admin/budget_groups/_form.html.erb b/app/views/admin/budget_groups/_form.html.erb index ac89d033d..b50378063 100644 --- a/app/views/admin/budget_groups/_form.html.erb +++ b/app/views/admin/budget_groups/_form.html.erb @@ -1,8 +1,8 @@ -<%= render "shared/globalize_locales", resource: @group %> +<%= render "shared/globalize_locales", resource: group %> -<%= translatable_form_for [:admin, @budget, @group], url: path do |f| %> +<%= translatable_form_for group, url: path do |f| %> - <%= render "shared/errors", resource: @group %> + <%= render "shared/errors", resource: group %>
<%= f.translatable_fields do |translations_form| %> @@ -12,11 +12,11 @@ <% end %>
- <% if @group.persisted? %> + <% if group.persisted? %>
<%= f.select :max_votable_headings, - (1..@group.headings.count), + (1..group.headings.count), hint: t("admin.budget_groups.form.max_votable_headings_info") %>
diff --git a/app/views/admin/budget_groups/edit.html.erb b/app/views/admin/budget_groups/edit.html.erb index 0730a4206..c43b26d38 100644 --- a/app/views/admin/budget_groups/edit.html.erb +++ b/app/views/admin/budget_groups/edit.html.erb @@ -1,3 +1,3 @@ <%= render "header", action: "edit" %> -<%= render "form", path: admin_budget_group_path(@budget, @group), action: "submit" %> +<%= render "form", group: @group, path: admin_budget_group_path(@budget, @group), action: "submit" %> diff --git a/app/views/admin/budget_groups/new.html.erb b/app/views/admin/budget_groups/new.html.erb index 461427d44..098ac5446 100644 --- a/app/views/admin/budget_groups/new.html.erb +++ b/app/views/admin/budget_groups/new.html.erb @@ -1,3 +1,3 @@ <%= render "header", action: "create" %> -<%= render "form", path: admin_budget_groups_path(@budget), action: "create" %> +<%= render "form", group: @group, path: admin_budget_groups_path(@budget), action: "create" %> diff --git a/app/views/admin/budget_headings/_form.html.erb b/app/views/admin/budget_headings/_form.html.erb index 06eae3926..2ac0d2c66 100644 --- a/app/views/admin/budget_headings/_form.html.erb +++ b/app/views/admin/budget_headings/_form.html.erb @@ -1,8 +1,8 @@ -<%= render "shared/globalize_locales", resource: @heading %> +<%= render "shared/globalize_locales", resource: heading %> -<%= translatable_form_for [:admin, @budget, @group, @heading], url: path do |f| %> +<%= translatable_form_for heading, url: path do |f| %> - <%= render "shared/errors", resource: @heading %> + <%= render "shared/errors", resource: heading %>
<%= f.translatable_fields do |translations_form| %> @@ -16,7 +16,7 @@
<%= f.text_field :price, maxlength: 8 %> - <% if @heading.budget.approval_voting? %> + <% if heading.budget.approval_voting? %> <%= f.number_field :max_ballot_lines, hint: t("admin.budget_headings.form.max_ballot_lines_info") %> diff --git a/app/views/admin/budget_headings/edit.html.erb b/app/views/admin/budget_headings/edit.html.erb index 124d07510..2102d8e3c 100644 --- a/app/views/admin/budget_headings/edit.html.erb +++ b/app/views/admin/budget_headings/edit.html.erb @@ -1,3 +1,3 @@ <%= render "header", action: "edit" %> -<%= render "form", path: admin_budget_group_heading_path(@budget, @group, @heading), action: "submit" %> +<%= render "form", heading: @heading, path: admin_budget_group_heading_path(@budget, @group, @heading), action: "submit" %> diff --git a/app/views/admin/budget_headings/new.html.erb b/app/views/admin/budget_headings/new.html.erb index 0566a5a5e..3e1810fbc 100644 --- a/app/views/admin/budget_headings/new.html.erb +++ b/app/views/admin/budget_headings/new.html.erb @@ -1,3 +1,3 @@ <%= render "header", action: "create" %> -<%= render "form", path: admin_budget_group_headings_path(@budget, @group), action: "create" %> +<%= render "form", heading: @heading, path: admin_budget_group_headings_path(@budget, @group), action: "create" %> From 9d72aed37d63f866ef053dc2c93ce3a4c14d4519 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javi=20Mart=C3=ADn?= Date: Thu, 3 Jun 2021 01:57:51 +0200 Subject: [PATCH 05/18] Fix mixin for general button styles Using the placeholder selector, we weren't overwriting the rules in the mixin called with `@include` in some cases because in the generated CSS the placeholder selector appeared before the code generated by the calls to `@include`. --- app/assets/stylesheets/mixins/buttons.scss | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/app/assets/stylesheets/mixins/buttons.scss b/app/assets/stylesheets/mixins/buttons.scss index 9e6fea4a1..5ef6a960d 100644 --- a/app/assets/stylesheets/mixins/buttons.scss +++ b/app/assets/stylesheets/mixins/buttons.scss @@ -1,4 +1,4 @@ -%button { +@mixin base-button { font-size: $base-font-size; &:focus, @@ -7,16 +7,20 @@ } } +%button { + @include base-button; +} + @mixin regular-button($color: $brand) { @include button($background: $color); @include inverted-selection; - @extend %button; + @include base-button; } @mixin hollow-button($color: $link) { @include button($style: hollow, $background: $color); @include normal-selection; - @extend %button; + @include base-button; margin-bottom: 0; } From 01c21158c43827dd057fb95bb5eaea0b029140e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javi=20Mart=C3=ADn?= Date: Thu, 3 Jun 2021 02:48:27 +0200 Subject: [PATCH 06/18] Use :only in groups and headings before_action When we've got 6 actions and we use it in 3, IMHO the code is easier to follow if we write the actions where we do use it. --- app/controllers/admin/budget_groups_controller.rb | 2 +- app/controllers/admin/budget_headings_controller.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/controllers/admin/budget_groups_controller.rb b/app/controllers/admin/budget_groups_controller.rb index 1a19d5296..0e2b87719 100644 --- a/app/controllers/admin/budget_groups_controller.rb +++ b/app/controllers/admin/budget_groups_controller.rb @@ -4,7 +4,7 @@ class Admin::BudgetGroupsController < Admin::BaseController feature_flag :budgets before_action :load_budget - before_action :load_group, except: [:index, :new, :create] + before_action :load_group, only: [:edit, :update, :destroy] def index @groups = @budget.groups.order(:id) diff --git a/app/controllers/admin/budget_headings_controller.rb b/app/controllers/admin/budget_headings_controller.rb index da701cb7f..6c0f43174 100644 --- a/app/controllers/admin/budget_headings_controller.rb +++ b/app/controllers/admin/budget_headings_controller.rb @@ -5,7 +5,7 @@ class Admin::BudgetHeadingsController < Admin::BaseController before_action :load_budget before_action :load_group - before_action :load_heading, except: [:index, :new, :create] + before_action :load_heading, only: [:edit, :update, :destroy] def index @headings = @group.headings.order(:id) From 03e598a53ddae268379cec402a2e46e4d5cbfdf8 Mon Sep 17 00:00:00 2001 From: Alberto Date: Sat, 21 Mar 2020 03:45:59 +0100 Subject: [PATCH 07/18] Improve layout in budget groups form --- .../stylesheets/admin/budget_groups/form.scss | 3 ++ .../stylesheets/admin/budgets/form.scss | 5 +-- app/assets/stylesheets/mixins/layouts.scss | 6 ++++ app/views/admin/budget_groups/_form.html.erb | 35 ++++++++----------- 4 files changed, 24 insertions(+), 25 deletions(-) create mode 100644 app/assets/stylesheets/admin/budget_groups/form.scss diff --git a/app/assets/stylesheets/admin/budget_groups/form.scss b/app/assets/stylesheets/admin/budget_groups/form.scss new file mode 100644 index 000000000..a34dbaf8a --- /dev/null +++ b/app/assets/stylesheets/admin/budget_groups/form.scss @@ -0,0 +1,3 @@ +.admin .budget-groups-form { + @include full-width-form; +} diff --git a/app/assets/stylesheets/admin/budgets/form.scss b/app/assets/stylesheets/admin/budgets/form.scss index 0fef7a2e6..9d0407030 100644 --- a/app/assets/stylesheets/admin/budgets/form.scss +++ b/app/assets/stylesheets/admin/budgets/form.scss @@ -1,4 +1,5 @@ .admin .budgets-form { + @include full-width-form; > fieldset { border-top: 4px solid $admin-border-color; @@ -17,8 +18,4 @@ text-transform: uppercase; } } - - .globalize-languages { - max-width: none; - } } diff --git a/app/assets/stylesheets/mixins/layouts.scss b/app/assets/stylesheets/mixins/layouts.scss index ee5422be9..38f53974e 100644 --- a/app/assets/stylesheets/mixins/layouts.scss +++ b/app/assets/stylesheets/mixins/layouts.scss @@ -21,3 +21,9 @@ margin-top: 0; } } + +@mixin full-width-form { + .globalize-languages { + max-width: none; + } +} diff --git a/app/views/admin/budget_groups/_form.html.erb b/app/views/admin/budget_groups/_form.html.erb index b50378063..8c152c3c5 100644 --- a/app/views/admin/budget_groups/_form.html.erb +++ b/app/views/admin/budget_groups/_form.html.erb @@ -1,30 +1,23 @@ -<%= render "shared/globalize_locales", resource: group %> - -<%= translatable_form_for group, url: path do |f| %> +<%= translatable_form_for group, url: path, html: { class: "budget-groups-form" } do |f| %> + <%= render "shared/globalize_locales", resource: group %> <%= render "shared/errors", resource: group %> -
- <%= f.translatable_fields do |translations_form| %> -
- <%= translations_form.text_field :name, maxlength: 50 %> -
- <% end %> -
- - <% if group.persisted? %> -
-
- <%= f.select :max_votable_headings, - (1..group.headings.count), - hint: t("admin.budget_groups.form.max_votable_headings_info") %> -
+ <%= f.translatable_fields do |translations_form| %> +
+ <%= translations_form.text_field :name, maxlength: 50 %>
<% end %> -
-
- <%= f.submit t("admin.budget_groups.form.#{action}"), class: "button success" %> + <% if group.persisted? %> +
+ <%= f.select :max_votable_headings, + (1..group.headings.count), + hint: t("admin.budget_groups.form.max_votable_headings_info") %>
+ <% end %> + +
+ <%= f.submit t("admin.budget_groups.form.#{action}"), class: "button hollow" %>
<% end %> From e71ccee6829c3075fce0c09acf1b60454e3c0ffe Mon Sep 17 00:00:00 2001 From: Alberto Date: Sat, 21 Mar 2020 03:45:59 +0100 Subject: [PATCH 08/18] Hide maximum headings select in groups without headings --- app/views/admin/budget_groups/_form.html.erb | 2 +- spec/system/admin/budget_groups_spec.rb | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/app/views/admin/budget_groups/_form.html.erb b/app/views/admin/budget_groups/_form.html.erb index 8c152c3c5..5e86059c0 100644 --- a/app/views/admin/budget_groups/_form.html.erb +++ b/app/views/admin/budget_groups/_form.html.erb @@ -9,7 +9,7 @@
<% end %> - <% if group.persisted? %> + <% if group.persisted? && group.headings.any? %>
<%= f.select :max_votable_headings, (1..group.headings.count), diff --git a/spec/system/admin/budget_groups_spec.rb b/spec/system/admin/budget_groups_spec.rb index c74b82177..090375235 100644 --- a/spec/system/admin/budget_groups_spec.rb +++ b/spec/system/admin/budget_groups_spec.rb @@ -129,6 +129,25 @@ describe "Admin budget groups", :admin do expect(page).to have_field "Maximum number of headings in which a user can select projects", with: "2" end + describe "Select for maxium number of headings to select projects" do + scenario "is present if there are headings in the group" do + group = create(:budget_group, budget: budget) + create(:budget_heading, group: group) + + visit edit_admin_budget_group_path(budget, group) + + expect(page).to have_field "Maximum number of headings in which a user can select projects" + end + + scenario "is not present if there are no headings in the group" do + group = create(:budget_group, budget: budget) + + visit edit_admin_budget_group_path(budget, group) + + expect(page).not_to have_field "Maximum number of headings in which a user can select projects" + end + end + scenario "Changing name for current locale will update the slug if budget is in draft phase" do group = create(:budget_group, budget: budget, name: "Old English Name") From b304c640e1e343e538d449567303c27fc023bfcd Mon Sep 17 00:00:00 2001 From: Alberto Date: Sat, 21 Mar 2020 03:45:59 +0100 Subject: [PATCH 09/18] Add groups step to budget creation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Note we're keeping this section's original design (which had one button to add a new group which after being pressed was replaced by a button to cancel) but we aren't using Foundation's `data-toggle` because there were a couple of usability and accessibility issues. First, using `data-toggle` multiple times and applying it to multiple elements led to the "cancel" button not being available after submitting a form with errors. Fixing it made the code more complicated. Second, the "Add new group" button always had the `aria-expanded` attribute set to "true", so my screen reader was announcing the button as expanded even when it wasn't. I didn't manage to fix it using `data-toggle`. Finally, after pressing either the "Add new group" and "Cancel" buttons, the keyboard focus was lost since the elements disappeared. So we're simplifying the HTML and adding some custom JavaScript to be able to handle the focus and manually setting the `aria-expanded` attribute. Co-Authored-By: Javi Martín Co-Authored-By: Julian Herrero --- .../admin/budgets_wizard/creation_step.js | 20 +++ app/assets/javascripts/application.js | 2 + .../admin/budgets_wizard/creation_step.scss | 50 +++++++ .../budgets_wizard/creation_timeline.scss | 10 +- .../admin/budgets/form_component.html.erb | 6 +- .../admin/budgets/form_component.rb | 9 +- .../budgets/new_component.html.erb | 2 +- .../creation_timeline_component.html.erb | 5 +- .../creation_timeline_component.rb | 11 ++ .../groups/creation_step_component.html.erb | 19 +++ .../groups/creation_step_component.rb | 30 +++++ .../groups/edit_component.html.erb | 7 + .../budgets_wizard/groups/edit_component.rb | 22 +++ .../groups/index_component.html.erb | 9 ++ .../budgets_wizard/groups/index_component.rb | 17 +++ app/components/admin/menu_component.rb | 2 +- .../admin/table_actions_component.rb | 2 +- app/components/concerns/admin/namespace.rb | 9 ++ .../admin/budget_groups_controller.rb | 50 +------ .../budgets_wizard/budgets_controller.rb | 2 +- .../admin/budgets_wizard/groups_controller.rb | 19 +++ .../concerns/admin/budget_groups_actions.rb | 60 +++++++++ .../admin/budgets_wizard/groups/edit.html.erb | 1 + .../budgets_wizard/groups/index.html.erb | 1 + config/i18n-tasks.yml | 1 + config/locales/en/admin.yml | 6 + config/locales/es/admin.yml | 6 + config/routes/admin.rb | 4 +- .../admin/budgets_wizard/budgets_spec.rb | 19 ++- .../admin/budgets_wizard/groups_spec.rb | 127 ++++++++++++++++++ 30 files changed, 457 insertions(+), 71 deletions(-) create mode 100644 app/assets/javascripts/admin/budgets_wizard/creation_step.js create mode 100644 app/assets/stylesheets/admin/budgets_wizard/creation_step.scss create mode 100644 app/components/admin/budgets_wizard/groups/creation_step_component.html.erb create mode 100644 app/components/admin/budgets_wizard/groups/creation_step_component.rb create mode 100644 app/components/admin/budgets_wizard/groups/edit_component.html.erb create mode 100644 app/components/admin/budgets_wizard/groups/edit_component.rb create mode 100644 app/components/admin/budgets_wizard/groups/index_component.html.erb create mode 100644 app/components/admin/budgets_wizard/groups/index_component.rb create mode 100644 app/components/concerns/admin/namespace.rb create mode 100644 app/controllers/admin/budgets_wizard/groups_controller.rb create mode 100644 app/controllers/concerns/admin/budget_groups_actions.rb create mode 100644 app/views/admin/budgets_wizard/groups/edit.html.erb create mode 100644 app/views/admin/budgets_wizard/groups/index.html.erb create mode 100644 spec/system/admin/budgets_wizard/groups_spec.rb diff --git a/app/assets/javascripts/admin/budgets_wizard/creation_step.js b/app/assets/javascripts/admin/budgets_wizard/creation_step.js new file mode 100644 index 000000000..fa1ffcebb --- /dev/null +++ b/app/assets/javascripts/admin/budgets_wizard/creation_step.js @@ -0,0 +1,20 @@ +(function() { + "use strict"; + App.AdminBudgetsWizardCreationStep = { + initialize: function() { + var element, add_button, cancel_button; + + element = $(".admin .budget-creation-step"); + add_button = element.find(".add"); + cancel_button = element.find(".delete"); + + add_button.click(function() { + $(this).attr("aria-expanded", true).parent().find(":input:visible:first").focus(); + }); + + cancel_button.click(function() { + add_button.attr("aria-expanded", false).focus(); + }); + } + }; +}).call(this); diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index 774ea5c78..51a7a4d55 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -113,6 +113,7 @@ //= require columns_selector //= require budget_edit_associations //= require datepicker +//= require_tree ./admin //= require_tree ./sdg //= require_tree ./sdg_management @@ -166,6 +167,7 @@ var initialize_modules = function() { if ($("#js-columns-selector").length) { App.ColumnsSelector.initialize(); } + App.AdminBudgetsWizardCreationStep.initialize(); App.BudgetEditAssociations.initialize(); App.Datepicker.initialize(); App.SDGRelatedListSelector.initialize(); diff --git a/app/assets/stylesheets/admin/budgets_wizard/creation_step.scss b/app/assets/stylesheets/admin/budgets_wizard/creation_step.scss new file mode 100644 index 000000000..e7e860508 --- /dev/null +++ b/app/assets/stylesheets/admin/budgets_wizard/creation_step.scss @@ -0,0 +1,50 @@ +.budget-creation-step { + + .add { + @include has-fa-icon(plus-square, solid); + @include regular-button; + + font-weight: bold; + padding-left: rem-calc(10); + + &::before { + margin-right: rem-calc(12); + } + + &[aria-expanded="false"] { + ~ :not(.next-step) { + display: none; + } + } + + &[aria-expanded="true"] { + display: none; + + ~ .next-step { + display: none; + } + } + } + + .cancel { + display: block; + margin-bottom: $line-height; + } + + .next-step { + @include regular-button; + } + + a { + &.next-step { + @include button-style($success-color, auto, auto); + } + } + + p { + &.next-step { + @include button-style($secondary-color, auto, auto); + @include button-disabled; + } + } +} diff --git a/app/assets/stylesheets/admin/budgets_wizard/creation_timeline.scss b/app/assets/stylesheets/admin/budgets_wizard/creation_timeline.scss index 539abe84c..67a79ef0c 100644 --- a/app/assets/stylesheets/admin/budgets_wizard/creation_timeline.scss +++ b/app/assets/stylesheets/admin/budgets_wizard/creation_timeline.scss @@ -5,7 +5,7 @@ position: relative; li { - border-top: 4px solid $admin-border-color; + border-top: 4px solid $brand; display: inline-block; font-size: $small-font-size; font-weight: bold; @@ -13,7 +13,7 @@ text-transform: uppercase; &::before { - background: $admin-border-color; + background: $brand; border-radius: 50%; content: ""; height: 20px; @@ -23,11 +23,11 @@ width: 20px; } - &[aria-current] { - border-color: $brand; + &[aria-current] ~ * { + border-color: $admin-border-color; &::before { - background: $brand; + background: $admin-border-color; } } } diff --git a/app/components/admin/budgets/form_component.html.erb b/app/components/admin/budgets/form_component.html.erb index 64159594f..2b8898607 100644 --- a/app/components/admin/budgets/form_component.html.erb +++ b/app/components/admin/budgets/form_component.html.erb @@ -52,7 +52,11 @@
- <%= f.submit nil, class: "button success" %> + <% if budget.persisted? %> + <%= f.submit nil, class: "button success" %> + <% else %> + <%= f.submit t("admin.budgets_wizard.budgets.continue"), class: "button success expanded" %> + <% end %>
diff --git a/app/components/admin/budgets/form_component.rb b/app/components/admin/budgets/form_component.rb index cb6c3344a..35d9b6de0 100644 --- a/app/components/admin/budgets/form_component.rb +++ b/app/components/admin/budgets/form_component.rb @@ -1,6 +1,7 @@ class Admin::Budgets::FormComponent < ApplicationComponent include TranslatableFormHelper include GlobalizeHelper + include Admin::Namespace attr_reader :budget delegate :display_calculate_winners_button?, @@ -12,14 +13,6 @@ class Admin::Budgets::FormComponent < ApplicationComponent @budget = budget end - def namespace - if controller.class.name.starts_with?("Admin::BudgetsWizard") - :admin_budgets_wizard - else - helpers.namespace.to_sym - end - end - def voting_styles_select_options Budget::VOTING_STYLES.map do |style| [Budget.human_attribute_name("voting_style_#{style}"), style] diff --git a/app/components/admin/budgets_wizard/budgets/new_component.html.erb b/app/components/admin/budgets_wizard/budgets/new_component.html.erb index e2e227928..c035cbc78 100644 --- a/app/components/admin/budgets_wizard/budgets/new_component.html.erb +++ b/app/components/admin/budgets_wizard/budgets/new_component.html.erb @@ -2,5 +2,5 @@ <%= header %> -<%= render Admin::BudgetsWizard::CreationTimelineComponent.new %> +<%= render Admin::BudgetsWizard::CreationTimelineComponent.new("budget") %> <%= render Admin::Budgets::FormComponent.new(budget) %> diff --git a/app/components/admin/budgets_wizard/creation_timeline_component.html.erb b/app/components/admin/budgets_wizard/creation_timeline_component.html.erb index 3a566c277..f1ab8cd5a 100644 --- a/app/components/admin/budgets_wizard/creation_timeline_component.html.erb +++ b/app/components/admin/budgets_wizard/creation_timeline_component.html.erb @@ -1,5 +1,8 @@
    -
  1. +
  2. > <%= t("admin.budgets_wizard.creation_timeline.budget") %>
  3. +
  4. > + <%= t("admin.budgets_wizard.creation_timeline.groups") %> +
diff --git a/app/components/admin/budgets_wizard/creation_timeline_component.rb b/app/components/admin/budgets_wizard/creation_timeline_component.rb index 47e5a4d2a..e7e0e717b 100644 --- a/app/components/admin/budgets_wizard/creation_timeline_component.rb +++ b/app/components/admin/budgets_wizard/creation_timeline_component.rb @@ -1,2 +1,13 @@ class Admin::BudgetsWizard::CreationTimelineComponent < ApplicationComponent + attr_reader :step + + def initialize(step) + @step = step + end + + private + + def step_groups? + step == "groups" + end end diff --git a/app/components/admin/budgets_wizard/groups/creation_step_component.html.erb b/app/components/admin/budgets_wizard/groups/creation_step_component.html.erb new file mode 100644 index 000000000..a4254e09e --- /dev/null +++ b/app/components/admin/budgets_wizard/groups/creation_step_component.html.erb @@ -0,0 +1,19 @@ +
+ + + <%= render "/admin/budget_groups/form", group: group, path: form_path, action: "create" %> + + + + <% if next_step_path %> + <%= link_to t("admin.budgets_wizard.groups.continue"), + next_step_path, + class: "next-step" %> + <% else %> +

+ <%= t("admin.budgets_wizard.groups.continue") %> +

+ <% end %> +
diff --git a/app/components/admin/budgets_wizard/groups/creation_step_component.rb b/app/components/admin/budgets_wizard/groups/creation_step_component.rb new file mode 100644 index 000000000..00a6cb2d3 --- /dev/null +++ b/app/components/admin/budgets_wizard/groups/creation_step_component.rb @@ -0,0 +1,30 @@ +class Admin::BudgetsWizard::Groups::CreationStepComponent < ApplicationComponent + attr_reader :group, :next_step_group + + def initialize(group, next_step_group) + @group = group + @next_step_group = next_step_group + end + + private + + def budget + group.budget + end + + def show_form? + group.errors.any? + end + + def form_path + admin_budgets_wizard_budget_groups_path(budget) + end + + def next_step_path + admin_budget_group_headings_path(budget, next_step_group) if next_step_enabled? + end + + def next_step_enabled? + next_step_group.present? + end +end diff --git a/app/components/admin/budgets_wizard/groups/edit_component.html.erb b/app/components/admin/budgets_wizard/groups/edit_component.html.erb new file mode 100644 index 000000000..fc92c2114 --- /dev/null +++ b/app/components/admin/budgets_wizard/groups/edit_component.html.erb @@ -0,0 +1,7 @@ +<%= back_link_to admin_budgets_wizard_budget_groups_path(budget) %> + +<%= header %> + +<%= render Admin::BudgetsWizard::CreationTimelineComponent.new("groups") %> + +<%= render "/admin/budget_groups/form", group: group, path: form_path, action: "submit" %> diff --git a/app/components/admin/budgets_wizard/groups/edit_component.rb b/app/components/admin/budgets_wizard/groups/edit_component.rb new file mode 100644 index 000000000..cc8c8fbfe --- /dev/null +++ b/app/components/admin/budgets_wizard/groups/edit_component.rb @@ -0,0 +1,22 @@ +class Admin::BudgetsWizard::Groups::EditComponent < ApplicationComponent + include Header + attr_reader :group + + def initialize(group) + @group = group + end + + def budget + group.budget + end + + def title + budget.name + end + + private + + def form_path + admin_budgets_wizard_budget_group_path(budget, group) + end +end diff --git a/app/components/admin/budgets_wizard/groups/index_component.html.erb b/app/components/admin/budgets_wizard/groups/index_component.html.erb new file mode 100644 index 000000000..a038a3f77 --- /dev/null +++ b/app/components/admin/budgets_wizard/groups/index_component.html.erb @@ -0,0 +1,9 @@ +<%= back_link_to admin_budget_path(budget), t("admin.budget_groups.index.back") %> + +<%= header %> + +<%= render Admin::Budgets::HelpComponent.new("budget_groups") %> +<%= render Admin::BudgetsWizard::CreationTimelineComponent.new("groups") %> + +<%= render Admin::BudgetGroups::GroupsComponent.new(groups) %> +<%= render Admin::BudgetsWizard::Groups::CreationStepComponent.new(new_group, groups.first) %> diff --git a/app/components/admin/budgets_wizard/groups/index_component.rb b/app/components/admin/budgets_wizard/groups/index_component.rb new file mode 100644 index 000000000..d15414e54 --- /dev/null +++ b/app/components/admin/budgets_wizard/groups/index_component.rb @@ -0,0 +1,17 @@ +class Admin::BudgetsWizard::Groups::IndexComponent < ApplicationComponent + include Header + attr_reader :groups, :new_group + + def initialize(groups, new_group) + @groups = groups + @new_group = new_group + end + + def budget + @new_group.budget + end + + def title + budget.name + end +end diff --git a/app/components/admin/menu_component.rb b/app/components/admin/menu_component.rb index e95f9f9cc..15e15f2b1 100644 --- a/app/components/admin/menu_component.rb +++ b/app/components/admin/menu_component.rb @@ -13,7 +13,7 @@ class Admin::MenuComponent < ApplicationComponent end def budgets? - controller_name.starts_with?("budget") + controller_name.starts_with?("budget") || controller_path =~ /budgets_wizard/ end def polls? diff --git a/app/components/admin/table_actions_component.rb b/app/components/admin/table_actions_component.rb index 9a5bf5134..7cc9697c5 100644 --- a/app/components/admin/table_actions_component.rb +++ b/app/components/admin/table_actions_component.rb @@ -1,7 +1,7 @@ class Admin::TableActionsComponent < ApplicationComponent include TableActionLink + include Admin::Namespace attr_reader :record, :options - delegate :namespace, to: :helpers def initialize(record = nil, **options) @record = record diff --git a/app/components/concerns/admin/namespace.rb b/app/components/concerns/admin/namespace.rb new file mode 100644 index 000000000..e43e9a28b --- /dev/null +++ b/app/components/concerns/admin/namespace.rb @@ -0,0 +1,9 @@ +module Admin::Namespace + def namespace + if controller.class.name.starts_with?("Admin::BudgetsWizard") + :admin_budgets_wizard + else + helpers.namespace.to_sym + end + end +end diff --git a/app/controllers/admin/budget_groups_controller.rb b/app/controllers/admin/budget_groups_controller.rb index 0e2b87719..edbe24f2e 100644 --- a/app/controllers/admin/budget_groups_controller.rb +++ b/app/controllers/admin/budget_groups_controller.rb @@ -1,64 +1,22 @@ class Admin::BudgetGroupsController < Admin::BaseController - include Translatable - include FeatureFlags - feature_flag :budgets + include Admin::BudgetGroupsActions - before_action :load_budget - before_action :load_group, only: [:edit, :update, :destroy] + before_action :load_groups, only: :index def index - @groups = @budget.groups.order(:id) end def new @group = @budget.groups.new end - def edit - end - - def create - @group = @budget.groups.new(budget_group_params) - if @group.save - redirect_to groups_index, notice: t("admin.budget_groups.create.notice") - else - render :new - end - end - - def update - if @group.update(budget_group_params) - redirect_to groups_index, notice: t("admin.budget_groups.update.notice") - else - render :edit - end - end - - def destroy - if @group.headings.any? - redirect_to groups_index, alert: t("admin.budget_groups.destroy.unable_notice") - else - @group.destroy! - redirect_to groups_index, notice: t("admin.budget_groups.destroy.success_notice") - end - end - private - def load_budget - @budget = Budget.find_by_slug_or_id! params[:budget_id] - end - - def load_group - @group = @budget.groups.find_by_slug_or_id! params[:id] - end - def groups_index admin_budget_groups_path(@budget) end - def budget_group_params - valid_attributes = [:max_votable_headings] - params.require(:budget_group).permit(*valid_attributes, translation_params(Budget::Group)) + def new_action + :new end end diff --git a/app/controllers/admin/budgets_wizard/budgets_controller.rb b/app/controllers/admin/budgets_wizard/budgets_controller.rb index 1efb41379..d55bab0cd 100644 --- a/app/controllers/admin/budgets_wizard/budgets_controller.rb +++ b/app/controllers/admin/budgets_wizard/budgets_controller.rb @@ -12,7 +12,7 @@ class Admin::BudgetsWizard::BudgetsController < Admin::BaseController @budget.published = false if @budget.save - redirect_to edit_admin_budget_path(@budget), notice: t("admin.budgets.create.notice") + redirect_to admin_budgets_wizard_budget_groups_path(@budget), notice: t("admin.budgets.create.notice") else render :new end diff --git a/app/controllers/admin/budgets_wizard/groups_controller.rb b/app/controllers/admin/budgets_wizard/groups_controller.rb new file mode 100644 index 000000000..4d8c8895c --- /dev/null +++ b/app/controllers/admin/budgets_wizard/groups_controller.rb @@ -0,0 +1,19 @@ +class Admin::BudgetsWizard::GroupsController < Admin::BaseController + include Admin::BudgetGroupsActions + + before_action :load_groups, only: [:index, :create] + + def index + @group = @budget.groups.new + end + + private + + def groups_index + admin_budgets_wizard_budget_groups_path(@budget) + end + + def new_action + :index + end +end diff --git a/app/controllers/concerns/admin/budget_groups_actions.rb b/app/controllers/concerns/admin/budget_groups_actions.rb new file mode 100644 index 000000000..c26d98c1b --- /dev/null +++ b/app/controllers/concerns/admin/budget_groups_actions.rb @@ -0,0 +1,60 @@ +module Admin::BudgetGroupsActions + extend ActiveSupport::Concern + + included do + include Translatable + include FeatureFlags + feature_flag :budgets + + before_action :load_budget + before_action :load_group, only: [:edit, :update, :destroy] + end + + def edit + end + + def create + @group = @budget.groups.new(budget_group_params) + if @group.save + redirect_to groups_index, notice: t("admin.budget_groups.create.notice") + else + render new_action + end + end + + def update + if @group.update(budget_group_params) + redirect_to groups_index, notice: t("admin.budget_groups.update.notice") + else + render :edit + end + end + + def destroy + if @group.headings.any? + redirect_to groups_index, alert: t("admin.budget_groups.destroy.unable_notice") + else + @group.destroy! + redirect_to groups_index, notice: t("admin.budget_groups.destroy.success_notice") + end + end + + private + + def load_budget + @budget = Budget.find_by_slug_or_id! params[:budget_id] + end + + def load_groups + @groups = @budget.groups.order(:id) + end + + def load_group + @group = @budget.groups.find_by_slug_or_id! params[:id] + end + + def budget_group_params + valid_attributes = [:max_votable_headings] + params.require(:budget_group).permit(*valid_attributes, translation_params(Budget::Group)) + end +end diff --git a/app/views/admin/budgets_wizard/groups/edit.html.erb b/app/views/admin/budgets_wizard/groups/edit.html.erb new file mode 100644 index 000000000..eaf72fbd1 --- /dev/null +++ b/app/views/admin/budgets_wizard/groups/edit.html.erb @@ -0,0 +1 @@ +<%= render Admin::BudgetsWizard::Groups::EditComponent.new(@group) %> diff --git a/app/views/admin/budgets_wizard/groups/index.html.erb b/app/views/admin/budgets_wizard/groups/index.html.erb new file mode 100644 index 000000000..51d32c7fb --- /dev/null +++ b/app/views/admin/budgets_wizard/groups/index.html.erb @@ -0,0 +1 @@ +<%= render Admin::BudgetsWizard::Groups::IndexComponent.new(@groups, @group) %> diff --git a/config/i18n-tasks.yml b/config/i18n-tasks.yml index 71933acf9..e2d8ee8e8 100644 --- a/config/i18n-tasks.yml +++ b/config/i18n-tasks.yml @@ -147,6 +147,7 @@ ignore_unused: - "admin.hidden_proposal_notifications.index.filter*" - "admin.budgets.index.filter*" - "admin.budgets.edit.(administrators|valuators).*" + - "admin.budget_groups.index.*.help_block" - "admin.budget_investments.index.filter*" - "admin.organizations.index.filter*" - "admin.hidden_users.index.filter*" diff --git a/config/locales/en/admin.yml b/config/locales/en/admin.yml index 4f459b453..c0691855b 100644 --- a/config/locales/en/admin.yml +++ b/config/locales/en/admin.yml @@ -156,6 +156,7 @@ en: index: back: "Go back to budgets" help: "Groups are meant to organize headings. After a group is created and it contais headings, it's possible to determine in how many headings a user can vote per group." + new_button: "Add new group" budget_headings: no_headings: "There are no headings." amount: @@ -287,6 +288,11 @@ en: budgets_wizard: creation_timeline: budget: Budget + groups: Groups + budgets: + continue: "Continue to groups" + groups: + continue: "Continue to headings" milestones: index: table_id: "ID" diff --git a/config/locales/es/admin.yml b/config/locales/es/admin.yml index 89cd99119..93e3e6339 100644 --- a/config/locales/es/admin.yml +++ b/config/locales/es/admin.yml @@ -156,6 +156,7 @@ es: index: back: "Volver a presupuestos" help: "Los grupos sirven para organizar las partidas del presupuesto. Después de que un grupo sea creado y éste contenga partidas, es posible determinar el número de partidas a las que un usuario puede votar por grupo." + new_button: "Añadir un grupo nuevo" budget_headings: no_headings: "No hay partidas." amount: @@ -287,6 +288,11 @@ es: budgets_wizard: creation_timeline: budget: Presupuesto + groups: Grupos + budgets: + continue: "Continuar a grupos" + groups: + continue: "Continuar a partidas" milestones: index: table_id: "ID" diff --git a/config/routes/admin.rb b/config/routes/admin.rb index 23cd9867a..846bb8635 100644 --- a/config/routes/admin.rb +++ b/config/routes/admin.rb @@ -73,7 +73,9 @@ namespace :admin do end namespace :budgets_wizard do - resources :budgets, only: [:create, :new] + resources :budgets, only: [:create, :new] do + resources :groups, only: [:index, :create, :edit, :update, :destroy] + end end resources :milestone_statuses, only: [:index, :new, :create, :update, :edit, :destroy] diff --git a/spec/system/admin/budgets_wizard/budgets_spec.rb b/spec/system/admin/budgets_wizard/budgets_spec.rb index 13cb9a75d..d6a18651b 100644 --- a/spec/system/admin/budgets_wizard/budgets_spec.rb +++ b/spec/system/admin/budgets_wizard/budgets_spec.rb @@ -7,9 +7,12 @@ describe "Budgets wizard, first step", :admin do click_link "Create new budget" fill_in "Name", with: "M30 - Summer campaign" - click_button "Create Budget" + click_button "Continue to groups" expect(page).to have_content "New participatory budget created successfully!" + + click_link "Go back to budgets" + expect(page).to have_field "Name", with: "M30 - Summer campaign" expect(page).to have_select "Final voting style", selected: "Knapsack" end @@ -22,9 +25,12 @@ describe "Budgets wizard, first step", :admin do fill_in "Name", with: "M30 - Summer campaign" select "Approval", from: "Final voting style" - click_button "Create Budget" + click_button "Continue to groups" expect(page).to have_content "New participatory budget created successfully!" + + click_link "Go back to budgets" + expect(page).to have_field "Name", with: "M30 - Summer campaign" expect(page).to have_select "Final voting style", selected: "Approval" @@ -35,7 +41,7 @@ describe "Budgets wizard, first step", :admin do scenario "Submit the form with errors" do visit new_admin_budgets_wizard_budget_path - click_button "Create Budget" + click_button "Continue to groups" expect(page).not_to have_content "New participatory budget created successfully!" expect(page).to have_css ".is-invalid-label", text: "Name" @@ -47,7 +53,7 @@ describe "Budgets wizard, first step", :admin do visit new_admin_budgets_wizard_budget_path fill_in "Name", with: "Existing Name" - click_button "Create Budget" + click_button "Continue to groups" expect(page).not_to have_content "New participatory budget created successfully!" expect(page).to have_css(".is-invalid-label", text: "Name") @@ -71,9 +77,12 @@ describe "Budgets wizard, first step", :admin do fill_in "Name", with: "M30 - Summer campaign" - click_button "Create Budget" + click_button "Continue to groups" expect(page).to have_content "New participatory budget created successfully!" + + click_link "Go back to budgets" + expect(page).to have_content "This participatory budget is in draft mode" expect(page).to have_link "Preview budget" expect(page).to have_link "Publish budget" diff --git a/spec/system/admin/budgets_wizard/groups_spec.rb b/spec/system/admin/budgets_wizard/groups_spec.rb new file mode 100644 index 000000000..be2e84d05 --- /dev/null +++ b/spec/system/admin/budgets_wizard/groups_spec.rb @@ -0,0 +1,127 @@ +require "rails_helper" + +describe "Budgets wizard, groups step", :admin do + let(:budget) { create(:budget, :drafting) } + + describe "New" do + scenario "create group" do + visit admin_budgets_wizard_budget_groups_path(budget) + + within "#side_menu" do + expect(page).to have_css ".is-active", exact_text: "Participatory budgets" + end + + expect(page).to have_content "Continue to headings" + expect(page).not_to have_link "Continue to headings" + + click_button "Add new group" + + fill_in "Group name", with: "All City" + + click_button "Create new group" + + expect(page).to have_content "Group created successfully!" + expect(page).to have_content "All City" + expect(page).to have_button "Add new group" + expect(page).to have_link "Continue to headings" + end + + scenario "cancel creating a group" do + visit admin_budgets_wizard_budget_groups_path(budget) + + expect(page).not_to have_field "Group name" + expect(page).not_to have_button "Cancel" + expect(page).to have_content "Continue to headings" + + click_button "Add new group" + + expect(page).to have_field "Group name" + expect(page).not_to have_button "Add new group" + expect(page).not_to have_content "Continue to headings" + + click_button "Cancel" + + expect(page).to have_button "Add new group" + expect(page).not_to have_field "Group name" + expect(page).not_to have_button "Cancel" + expect(page).to have_content "Continue to headings" + end + + scenario "submit the form with errors" do + visit admin_budgets_wizard_budget_groups_path(budget) + click_button "Add new group" + + click_button "Create new group" + + expect(page).not_to have_content "Group created successfully!" + expect(page).to have_css ".is-invalid-label", text: "Group name" + expect(page).to have_css ".creation-timeline" + expect(page).to have_content "can't be blank" + expect(page).to have_button "Create new group" + expect(page).to have_button "Cancel" + expect(page).not_to have_button "Add new group" + expect(page).not_to have_content "Continue to headings" + end + end + + describe "Edit" do + scenario "update group" do + create(:budget_group, budget: budget, name: "Group wiht a typo") + + visit admin_budgets_wizard_budget_groups_path(budget) + + expect(page).to have_css ".creation-timeline" + + within("tr", text: "Group wiht a typo") { click_link "Edit" } + fill_in "Group name", with: "Group without typos" + click_button "Save group" + + expect(page).to have_content "Group updated successfully" + expect(page).to have_css ".creation-timeline" + expect(page).to have_css "td", exact_text: "Group without typos" + end + + scenario "submit the form with errors and then without errors" do + group = create(:budget_group, budget: budget, name: "Group wiht a typo") + + visit edit_admin_budgets_wizard_budget_group_path(budget, group) + fill_in "Group name", with: "" + click_button "Save group" + + expect(page).to have_css "#error_explanation" + + fill_in "Group name", with: "Group without typos" + click_button "Save group" + + expect(page).to have_content "Group updated successfully" + expect(page).to have_css ".creation-timeline" + expect(page).to have_css "td", exact_text: "Group without typos" + end + end + + describe "Destroy" do + scenario "delete a group without headings" do + create(:budget_group, budget: budget, name: "Delete me!") + + visit admin_budgets_wizard_budget_groups_path(budget) + within("tr", text: "Delete me!") { accept_confirm { click_link "Delete" } } + + expect(page).to have_content "Group deleted successfully" + expect(page).not_to have_content "Delete me!" + expect(page).to have_css ".creation-timeline" + end + + scenario "try to delete a group with headings" do + group = create(:budget_group, budget: budget, name: "Don't delete me!") + create(:budget_heading, group: group) + + visit admin_budgets_wizard_budget_groups_path(budget) + + within("tr", text: "Don't delete me!") { accept_confirm { click_link "Delete" } } + + expect(page).to have_content "You cannot delete a Group that has associated headings" + expect(page).to have_content "Don't delete me!" + expect(page).to have_css ".creation-timeline" + end + end +end From eff370b279e2a77e3a4e2f133e7e95ddb2bab004 Mon Sep 17 00:00:00 2001 From: Alberto Date: Sat, 21 Mar 2020 03:45:59 +0100 Subject: [PATCH 10/18] Improve layout in budget headings form --- .../admin/budget_headings/form.scss | 3 + .../admin/budget_headings/_form.html.erb | 68 +++++++++---------- config/locales/en/activerecord.yml | 2 +- config/locales/es/activerecord.yml | 2 +- spec/system/admin/budget_headings_spec.rb | 14 ++-- spec/system/budgets/ballots_spec.rb | 2 +- 6 files changed, 45 insertions(+), 46 deletions(-) create mode 100644 app/assets/stylesheets/admin/budget_headings/form.scss diff --git a/app/assets/stylesheets/admin/budget_headings/form.scss b/app/assets/stylesheets/admin/budget_headings/form.scss new file mode 100644 index 000000000..430601bcc --- /dev/null +++ b/app/assets/stylesheets/admin/budget_headings/form.scss @@ -0,0 +1,3 @@ +.admin .budget-headings-form { + @include full-width-form; +} diff --git a/app/views/admin/budget_headings/_form.html.erb b/app/views/admin/budget_headings/_form.html.erb index 2ac0d2c66..f1424bb10 100644 --- a/app/views/admin/budget_headings/_form.html.erb +++ b/app/views/admin/budget_headings/_form.html.erb @@ -1,44 +1,40 @@ -<%= render "shared/globalize_locales", resource: heading %> - -<%= translatable_form_for heading, url: path do |f| %> +<%= translatable_form_for heading, url: path, html: { class: "budget-headings-form" } do |f| %> + <%= render "shared/globalize_locales", resource: heading %> <%= render "shared/errors", resource: heading %> -
- <%= f.translatable_fields do |translations_form| %> -
- <%= translations_form.text_field :name, maxlength: 50 %> -
+ <%= f.translatable_fields do |translations_form| %> +
+ <%= translations_form.text_field :name, maxlength: 50 %> +
+ <% end %> + +
+ <%= f.text_field :price, maxlength: 8 %> + + <% if heading.budget.approval_voting? %> + <%= f.number_field :max_ballot_lines, + hint: t("admin.budget_headings.form.max_ballot_lines_info") %> <% end %> + + <%= f.text_field :population, + maxlength: 8, + data: { toggle_focus: "population-info" }, + hint: t("admin.budget_headings.form.population_info") %> + + <%= f.text_field :latitude, maxlength: 22 %> + <%= f.text_field :longitude, maxlength: 22 %> +

+ <%= t("admin.budget_headings.form.coordinates_info") %> +

+ + <%= f.check_box :allow_custom_content %> +

+ <%= t("admin.budget_headings.form.content_blocks_info") %> +

-
-
- <%= f.text_field :price, maxlength: 8 %> - - <% if heading.budget.approval_voting? %> - <%= f.number_field :max_ballot_lines, - hint: t("admin.budget_headings.form.max_ballot_lines_info") %> - - <% end %> - - <%= f.text_field :population, - maxlength: 8, - data: { toggle_focus: "population-info" }, - hint: t("admin.budget_headings.form.population_info") %> - - <%= f.text_field :latitude, maxlength: 22 %> - <%= f.text_field :longitude, maxlength: 22 %> -

- <%= t("admin.budget_headings.form.coordinates_info") %> -

- - <%= f.check_box :allow_custom_content %> -

- <%= t("admin.budget_headings.form.content_blocks_info") %> -

- - <%= f.submit t("admin.budget_headings.form.#{action}"), class: "button success" %> -
+
+ <%= f.submit t("admin.budget_headings.form.#{action}"), class: "button hollow" %>
<% end %> diff --git a/config/locales/en/activerecord.yml b/config/locales/en/activerecord.yml index a00013b70..af300c710 100644 --- a/config/locales/en/activerecord.yml +++ b/config/locales/en/activerecord.yml @@ -220,7 +220,7 @@ en: latitude: "Latitude (optional)" longitude: "Longitude (optional)" name: "Heading name" - price: "Amount" + price: "Money amount" population: "Population (optional)" max_ballot_lines: "Votes allowed" budget/heading/translation: diff --git a/config/locales/es/activerecord.yml b/config/locales/es/activerecord.yml index 35f62237e..6061fb128 100644 --- a/config/locales/es/activerecord.yml +++ b/config/locales/es/activerecord.yml @@ -220,7 +220,7 @@ es: latitude: "Latitud (opcional)" longitude: "Longitud (opcional)" name: "Nombre de la partida" - price: "Cantidad" + price: "Cantidad de dinero" population: "Población (opcional)" max_ballot_lines: "Votos permitidos" budget/heading/translation: diff --git a/spec/system/admin/budget_headings_spec.rb b/spec/system/admin/budget_headings_spec.rb index 710770ed5..ccf258706 100644 --- a/spec/system/admin/budget_headings_spec.rb +++ b/spec/system/admin/budget_headings_spec.rb @@ -88,7 +88,7 @@ describe "Admin budget headings", :admin do click_link "Create new heading" fill_in "Heading name", with: "All City" - fill_in "Amount", with: "1000" + fill_in "Money amount", with: "1000" fill_in "Population (optional)", with: "10000" check "Allow content block" @@ -115,7 +115,7 @@ describe "Admin budget headings", :admin do click_button "Create new heading" expect(page).not_to have_content "Heading created successfully!" - expect(page).to have_css(".is-invalid-label", text: "Amount") + expect(page).to have_css(".is-invalid-label", text: "Money amount") expect(page).to have_content "can't be blank" end @@ -134,7 +134,7 @@ describe "Admin budget headings", :admin do expect(page).to have_field "Votes allowed", with: 1 fill_in "Heading name", with: "All City" - fill_in "Amount", with: "1000" + fill_in "Money amount", with: "1000" fill_in "Votes allowed", with: 14 click_button "Create new heading" @@ -152,7 +152,7 @@ describe "Admin budget headings", :admin do within("#budget_heading_#{heading.id}") { click_link "Edit" } expect(page).to have_field "Heading name", with: heading.name - expect(page).to have_field "Amount", with: heading.price + expect(page).to have_field "Money amount", with: heading.price expect(page).to have_field "Population (optional)", with: heading.population expect(page).to have_field "Longitude (optional)", with: heading.longitude expect(page).to have_field "Latitude (optional)", with: heading.latitude @@ -204,14 +204,14 @@ describe "Admin budget headings", :admin do visit edit_admin_budget_group_heading_path(budget, group, heading) expect(page).to have_field "Heading name", with: "All City" - expect(page).to have_field "Amount", with: 1000 + expect(page).to have_field "Money amount", with: 1000 expect(page).to have_field "Population (optional)", with: 10000 expect(page).to have_field "Longitude (optional)", with: 20.50 expect(page).to have_field "Latitude (optional)", with: -10.50 expect(find_field("Allow content block")).to be_checked fill_in "Heading name", with: "Districts" - fill_in "Amount", with: "2000" + fill_in "Money amount", with: "2000" fill_in "Population (optional)", with: "20000" fill_in "Longitude (optional)", with: "-40.47" fill_in "Latitude (optional)", with: "25.25" @@ -222,7 +222,7 @@ describe "Admin budget headings", :admin do visit edit_admin_budget_group_heading_path(budget, group, heading) expect(page).to have_field "Heading name", with: "Districts" - expect(page).to have_field "Amount", with: 2000 + expect(page).to have_field "Money amount", with: 2000 expect(page).to have_field "Population (optional)", with: 20000 expect(page).to have_field "Longitude (optional)", with: -40.47 expect(page).to have_field "Latitude (optional)", with: 25.25 diff --git a/spec/system/budgets/ballots_spec.rb b/spec/system/budgets/ballots_spec.rb index 535192c8d..56678662a 100644 --- a/spec/system/budgets/ballots_spec.rb +++ b/spec/system/budgets/ballots_spec.rb @@ -637,7 +637,7 @@ describe "Ballots" do in_browser(:admin) do login_as admin_user visit edit_admin_budget_group_heading_path(budget, states, new_york) - fill_in "Amount", with: 10 + fill_in "Money amount", with: 10 click_button "Save heading" expect(page).to have_content "Heading updated successfully" From b99ce2af455144aee766450f130418a381a04374 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javi=20Mart=C3=ADn?= Date: Wed, 2 Jun 2021 01:46:50 +0200 Subject: [PATCH 11/18] Remove duplication in timeline component This way adding new steps will be easier. --- .../creation_timeline_component.html.erb | 11 +++++------ .../budgets_wizard/creation_timeline_component.rb | 10 +++++----- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/app/components/admin/budgets_wizard/creation_timeline_component.html.erb b/app/components/admin/budgets_wizard/creation_timeline_component.html.erb index f1ab8cd5a..3d0063898 100644 --- a/app/components/admin/budgets_wizard/creation_timeline_component.html.erb +++ b/app/components/admin/budgets_wizard/creation_timeline_component.html.erb @@ -1,8 +1,7 @@
    -
  1. > - <%= t("admin.budgets_wizard.creation_timeline.budget") %> -
  2. -
  3. > - <%= t("admin.budgets_wizard.creation_timeline.groups") %> -
  4. + <% steps.each do |step| %> +
  5. > + <%= t("admin.budgets_wizard.creation_timeline.#{step}") %> +
  6. + <% end %>
diff --git a/app/components/admin/budgets_wizard/creation_timeline_component.rb b/app/components/admin/budgets_wizard/creation_timeline_component.rb index e7e0e717b..6db8d61e2 100644 --- a/app/components/admin/budgets_wizard/creation_timeline_component.rb +++ b/app/components/admin/budgets_wizard/creation_timeline_component.rb @@ -1,13 +1,13 @@ class Admin::BudgetsWizard::CreationTimelineComponent < ApplicationComponent - attr_reader :step + attr_reader :current_step - def initialize(step) - @step = step + def initialize(current_step) + @current_step = current_step end private - def step_groups? - step == "groups" + def steps + %w[budget groups] end end From 0a2c70cbfe98762ea53ecb7c3819e0d31b6b83b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javi=20Mart=C3=ADn?= Date: Fri, 4 Jun 2021 00:44:16 +0200 Subject: [PATCH 12/18] Improve title in admin headings index With the word "headings" in it, it's a bit easier to know where we are. We're also using a translation since not every language in the world uses a "/" as a standard separator between two terms. --- app/views/admin/budget_headings/index.html.erb | 2 +- config/locales/en/admin.yml | 1 + config/locales/es/admin.yml | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/app/views/admin/budget_headings/index.html.erb b/app/views/admin/budget_headings/index.html.erb index f154b5ae9..48bfb4c9e 100644 --- a/app/views/admin/budget_headings/index.html.erb +++ b/app/views/admin/budget_headings/index.html.erb @@ -1,7 +1,7 @@ <%= back_link_to admin_budget_groups_path(@budget), t("admin.budget_headings.index.back") %>
-

<%= "#{@budget.name} / #{@group.name}" %>

+

<%= t("admin.budget_headings.index.title", budget: @budget.name, group: @group.name) %>

<%= link_to t("admin.budget_headings.form.create"), new_admin_budget_group_heading_path %>
diff --git a/config/locales/en/admin.yml b/config/locales/en/admin.yml index c0691855b..ec67b8e09 100644 --- a/config/locales/en/admin.yml +++ b/config/locales/en/admin.yml @@ -180,6 +180,7 @@ en: index: back: "Go back to groups" help: "Headings are meant to divide the money of the participatory budget. Here you can add headings for this group and assign the amount of money that will be used for each heading." + title: "%{budget} / %{group} headings" budget_phases: edit: description_help_text: This text will appear in the header when the phase is active diff --git a/config/locales/es/admin.yml b/config/locales/es/admin.yml index 93e3e6339..7feb7440f 100644 --- a/config/locales/es/admin.yml +++ b/config/locales/es/admin.yml @@ -180,6 +180,7 @@ es: index: back: "Volver a grupos" help: "Las partidas sirven para dividir el dinero del presupuesto participativo. Aquí puedes ir añadiendo partidas para cada grupo y establecer la cantidad de dinero que se gastará en cada partida." + title: "Partidas de %{budget} / %{group}" budget_phases: edit: description_help_text: Este texto aparecerá en la cabecera cuando la fase esté activa From f8d6ba12d7d04c50a2d085cbdca8ae3625185566 Mon Sep 17 00:00:00 2001 From: Julian Herrero Date: Sun, 22 Mar 2020 04:54:10 +0100 Subject: [PATCH 13/18] Add headings step to budget creation Co-Authored-By: decabeza --- .../headings/group_switcher.scss | 50 ++++++ .../budget_groups/groups_component.html.erb | 2 +- .../admin/budget_groups/groups_component.rb | 4 + .../creation_step_component.html.erb | 19 +++ .../budgets_wizard/creation_step_component.rb | 22 +++ .../creation_timeline_component.rb | 2 +- .../groups/creation_step_component.html.erb | 20 +-- .../groups/creation_step_component.rb | 6 +- .../headings/creation_step_component.html.erb | 3 + .../headings/creation_step_component.rb | 25 +++ .../headings/edit_component.html.erb | 7 + .../budgets_wizard/headings/edit_component.rb | 26 +++ .../group_switcher_component.html.erb | 21 +++ .../headings/group_switcher_component.rb | 25 +++ .../headings/index_component.html.erb | 10 ++ .../headings/index_component.rb | 21 +++ .../admin/budget_headings_controller.rb | 55 +------ .../budgets_wizard/headings_controller.rb | 19 +++ .../concerns/admin/budget_headings_actions.rb | 66 ++++++++ .../budgets_wizard/headings/edit.html.erb | 1 + .../budgets_wizard/headings/index.html.erb | 1 + config/i18n-tasks.yml | 1 + config/locales/en/admin.yml | 8 + config/locales/es/admin.yml | 8 + config/routes/admin.rb | 4 +- .../headings/group_switcher_component_spec.rb | 41 +++++ .../admin/budgets_wizard/headings_spec.rb | 148 ++++++++++++++++++ 27 files changed, 537 insertions(+), 78 deletions(-) create mode 100644 app/assets/stylesheets/admin/budgets_wizard/headings/group_switcher.scss create mode 100644 app/components/admin/budgets_wizard/creation_step_component.html.erb create mode 100644 app/components/admin/budgets_wizard/creation_step_component.rb create mode 100644 app/components/admin/budgets_wizard/headings/creation_step_component.html.erb create mode 100644 app/components/admin/budgets_wizard/headings/creation_step_component.rb create mode 100644 app/components/admin/budgets_wizard/headings/edit_component.html.erb create mode 100644 app/components/admin/budgets_wizard/headings/edit_component.rb create mode 100644 app/components/admin/budgets_wizard/headings/group_switcher_component.html.erb create mode 100644 app/components/admin/budgets_wizard/headings/group_switcher_component.rb create mode 100644 app/components/admin/budgets_wizard/headings/index_component.html.erb create mode 100644 app/components/admin/budgets_wizard/headings/index_component.rb create mode 100644 app/controllers/admin/budgets_wizard/headings_controller.rb create mode 100644 app/controllers/concerns/admin/budget_headings_actions.rb create mode 100644 app/views/admin/budgets_wizard/headings/edit.html.erb create mode 100644 app/views/admin/budgets_wizard/headings/index.html.erb create mode 100644 spec/components/admin/budgets_wizard/headings/group_switcher_component_spec.rb create mode 100644 spec/system/admin/budgets_wizard/headings_spec.rb diff --git a/app/assets/stylesheets/admin/budgets_wizard/headings/group_switcher.scss b/app/assets/stylesheets/admin/budgets_wizard/headings/group_switcher.scss new file mode 100644 index 000000000..e0c4109f3 --- /dev/null +++ b/app/assets/stylesheets/admin/budgets_wizard/headings/group_switcher.scss @@ -0,0 +1,50 @@ +.budget-group-switcher { + margin-bottom: $line-height; + + p { + margin-bottom: 0; + } + + > .menu > li { + + > button { + align-items: center; + border: $admin-border; + border-radius: $button-radius; + display: inline-flex; + padding: rem-calc(11) rem-calc(16); + + &:active { + color: inherit; + } + + &::after { + @include css-triangle($dropdownmenu-arrow-size, currentcolor, down); + margin-left: 0.2em; + } + } + } + + .menu.is-dropdown-submenu { + margin: 0; + min-width: 100%; + padding: 0; + + li { + margin-bottom: 0; + } + + a { + cursor: default; + padding: rem-calc(11) rem-calc(16); + width: 100%; + + &:focus, + &:hover { + @include brand-background; + text-decoration: none; + outline: none; + } + } + } +} diff --git a/app/components/admin/budget_groups/groups_component.html.erb b/app/components/admin/budget_groups/groups_component.html.erb index 00bcc18fc..2b8f1d596 100644 --- a/app/components/admin/budget_groups/groups_component.html.erb +++ b/app/components/admin/budget_groups/groups_component.html.erb @@ -18,7 +18,7 @@ <%= render Admin::TableActionsComponent.new(group) do |actions| %> <%= actions.link_to t("admin.budget_groups.headings_manage"), - admin_budget_group_headings_path(budget, group), + headings_path(actions, group), class: "headings-link" %> <% end %> diff --git a/app/components/admin/budget_groups/groups_component.rb b/app/components/admin/budget_groups/groups_component.rb index 87012734d..51bf60649 100644 --- a/app/components/admin/budget_groups/groups_component.rb +++ b/app/components/admin/budget_groups/groups_component.rb @@ -10,4 +10,8 @@ class Admin::BudgetGroups::GroupsComponent < ApplicationComponent def budget @budget ||= groups.first.budget end + + def headings_path(table_actions_component, group) + send("#{table_actions_component.namespace}_budget_group_headings_path", group.budget, group) + end end diff --git a/app/components/admin/budgets_wizard/creation_step_component.html.erb b/app/components/admin/budgets_wizard/creation_step_component.html.erb new file mode 100644 index 000000000..b6ecad267 --- /dev/null +++ b/app/components/admin/budgets_wizard/creation_step_component.html.erb @@ -0,0 +1,19 @@ +
+ + + <%= content %> + + + + <% if next_step_path %> + <%= link_to t("admin.budgets_wizard.#{i18n_namespace}.continue"), + next_step_path, + class: "next-step" %> + <% else %> +

+ <%= t("admin.budgets_wizard.#{i18n_namespace}.continue") %> +

+ <% end %> +
diff --git a/app/components/admin/budgets_wizard/creation_step_component.rb b/app/components/admin/budgets_wizard/creation_step_component.rb new file mode 100644 index 000000000..2bd220146 --- /dev/null +++ b/app/components/admin/budgets_wizard/creation_step_component.rb @@ -0,0 +1,22 @@ +class Admin::BudgetsWizard::CreationStepComponent < ApplicationComponent + attr_reader :record, :next_step_path + + def initialize(record, next_step_path) + @record = record + @next_step_path = next_step_path + end + + private + + def show_form? + record.errors.any? + end + + def i18n_namespace + i18n_namespace_with_budget.gsub("budget_", "") + end + + def i18n_namespace_with_budget + record.class.table_name + end +end diff --git a/app/components/admin/budgets_wizard/creation_timeline_component.rb b/app/components/admin/budgets_wizard/creation_timeline_component.rb index 6db8d61e2..a4f4ed24f 100644 --- a/app/components/admin/budgets_wizard/creation_timeline_component.rb +++ b/app/components/admin/budgets_wizard/creation_timeline_component.rb @@ -8,6 +8,6 @@ class Admin::BudgetsWizard::CreationTimelineComponent < ApplicationComponent private def steps - %w[budget groups] + %w[budget groups headings] end end diff --git a/app/components/admin/budgets_wizard/groups/creation_step_component.html.erb b/app/components/admin/budgets_wizard/groups/creation_step_component.html.erb index a4254e09e..9f1c1ab33 100644 --- a/app/components/admin/budgets_wizard/groups/creation_step_component.html.erb +++ b/app/components/admin/budgets_wizard/groups/creation_step_component.html.erb @@ -1,19 +1,3 @@ -
- - +<%= render Admin::BudgetsWizard::CreationStepComponent.new(group, next_step_path) do %> <%= render "/admin/budget_groups/form", group: group, path: form_path, action: "create" %> - - - - <% if next_step_path %> - <%= link_to t("admin.budgets_wizard.groups.continue"), - next_step_path, - class: "next-step" %> - <% else %> -

- <%= t("admin.budgets_wizard.groups.continue") %> -

- <% end %> -
+<% end %> diff --git a/app/components/admin/budgets_wizard/groups/creation_step_component.rb b/app/components/admin/budgets_wizard/groups/creation_step_component.rb index 00a6cb2d3..8a5e3bfa9 100644 --- a/app/components/admin/budgets_wizard/groups/creation_step_component.rb +++ b/app/components/admin/budgets_wizard/groups/creation_step_component.rb @@ -12,16 +12,12 @@ class Admin::BudgetsWizard::Groups::CreationStepComponent < ApplicationComponent group.budget end - def show_form? - group.errors.any? - end - def form_path admin_budgets_wizard_budget_groups_path(budget) end def next_step_path - admin_budget_group_headings_path(budget, next_step_group) if next_step_enabled? + admin_budgets_wizard_budget_group_headings_path(budget, next_step_group) if next_step_enabled? end def next_step_enabled? diff --git a/app/components/admin/budgets_wizard/headings/creation_step_component.html.erb b/app/components/admin/budgets_wizard/headings/creation_step_component.html.erb new file mode 100644 index 000000000..a8ca3d7b4 --- /dev/null +++ b/app/components/admin/budgets_wizard/headings/creation_step_component.html.erb @@ -0,0 +1,3 @@ +<%= render Admin::BudgetsWizard::CreationStepComponent.new(heading, next_step_path) do %> + <%= render "/admin/budget_headings/form", heading: heading, path: form_path, action: "create" %> +<% end %> diff --git a/app/components/admin/budgets_wizard/headings/creation_step_component.rb b/app/components/admin/budgets_wizard/headings/creation_step_component.rb new file mode 100644 index 000000000..8758c6bbc --- /dev/null +++ b/app/components/admin/budgets_wizard/headings/creation_step_component.rb @@ -0,0 +1,25 @@ +class Admin::BudgetsWizard::Headings::CreationStepComponent < ApplicationComponent + attr_reader :heading + + def initialize(heading) + @heading = heading + end + + private + + def budget + heading.budget + end + + def form_path + admin_budgets_wizard_budget_group_headings_path(heading.group.budget, heading.group) + end + + def next_step_path + admin_budget_path(budget) if next_step_enabled? + end + + def next_step_enabled? + budget.headings.any? + end +end diff --git a/app/components/admin/budgets_wizard/headings/edit_component.html.erb b/app/components/admin/budgets_wizard/headings/edit_component.html.erb new file mode 100644 index 000000000..5a269b582 --- /dev/null +++ b/app/components/admin/budgets_wizard/headings/edit_component.html.erb @@ -0,0 +1,7 @@ +<%= back_link_to admin_budgets_wizard_budget_group_headings_path(budget, group) %> + +<%= header %> + +<%= render Admin::BudgetsWizard::CreationTimelineComponent.new("headings") %> + +<%= render "/admin/budget_headings/form", heading: heading, path: form_path, action: "submit" %> diff --git a/app/components/admin/budgets_wizard/headings/edit_component.rb b/app/components/admin/budgets_wizard/headings/edit_component.rb new file mode 100644 index 000000000..ee6250b23 --- /dev/null +++ b/app/components/admin/budgets_wizard/headings/edit_component.rb @@ -0,0 +1,26 @@ +class Admin::BudgetsWizard::Headings::EditComponent < ApplicationComponent + include Header + attr_reader :heading + + def initialize(heading) + @heading = heading + end + + def budget + heading.budget + end + + def group + heading.group + end + + def title + heading.name + end + + private + + def form_path + admin_budgets_wizard_budget_group_heading_path(budget, group, heading) + end +end diff --git a/app/components/admin/budgets_wizard/headings/group_switcher_component.html.erb b/app/components/admin/budgets_wizard/headings/group_switcher_component.html.erb new file mode 100644 index 000000000..4fe2ba830 --- /dev/null +++ b/app/components/admin/budgets_wizard/headings/group_switcher_component.html.erb @@ -0,0 +1,21 @@ +
+ <% if other_groups.one? %> +

+ <%= t("admin.budget_headings.group_switcher.currently_showing", group: group.name) %> + <%= link_to t("admin.budget_headings.group_switcher.the_other_group", group: other_groups.first.name), + headings_path(other_groups.first) %> +

+ <% else %> +

<%= t("admin.budget_headings.group_switcher.currently_showing", group: group.name) %>

+ + <% end %> +
diff --git a/app/components/admin/budgets_wizard/headings/group_switcher_component.rb b/app/components/admin/budgets_wizard/headings/group_switcher_component.rb new file mode 100644 index 000000000..b6e490ad9 --- /dev/null +++ b/app/components/admin/budgets_wizard/headings/group_switcher_component.rb @@ -0,0 +1,25 @@ +class Admin::BudgetsWizard::Headings::GroupSwitcherComponent < ApplicationComponent + attr_reader :group + + def initialize(group) + @group = group + end + + def render? + other_groups.any? + end + + private + + def budget + group.budget + end + + def other_groups + @other_groups ||= budget.groups.sort_by_name - [group] + end + + def headings_path(group) + admin_budgets_wizard_budget_group_headings_path(budget, group) + end +end diff --git a/app/components/admin/budgets_wizard/headings/index_component.html.erb b/app/components/admin/budgets_wizard/headings/index_component.html.erb new file mode 100644 index 000000000..21e0d538d --- /dev/null +++ b/app/components/admin/budgets_wizard/headings/index_component.html.erb @@ -0,0 +1,10 @@ +<%= back_link_to admin_budgets_wizard_budget_groups_path(budget), t("admin.budget_headings.index.back") %> + +<%= header %> + +<%= render Admin::Budgets::HelpComponent.new("budget_headings") %> +<%= render Admin::BudgetsWizard::CreationTimelineComponent.new("headings") %> + +<%= render Admin::BudgetsWizard::Headings::GroupSwitcherComponent.new(group) %> +<%= render Admin::BudgetHeadings::HeadingsComponent.new(headings) %> +<%= render Admin::BudgetsWizard::Headings::CreationStepComponent.new(new_heading) %> diff --git a/app/components/admin/budgets_wizard/headings/index_component.rb b/app/components/admin/budgets_wizard/headings/index_component.rb new file mode 100644 index 000000000..76aa584a9 --- /dev/null +++ b/app/components/admin/budgets_wizard/headings/index_component.rb @@ -0,0 +1,21 @@ +class Admin::BudgetsWizard::Headings::IndexComponent < ApplicationComponent + include Header + attr_reader :headings, :new_heading + + def initialize(headings, new_heading) + @headings = headings + @new_heading = new_heading + end + + def budget + group.budget + end + + def group + new_heading.group + end + + def title + t("admin.budget_headings.index.title", budget: budget.name, group: group.name) + end +end diff --git a/app/controllers/admin/budget_headings_controller.rb b/app/controllers/admin/budget_headings_controller.rb index 6c0f43174..366641a8a 100644 --- a/app/controllers/admin/budget_headings_controller.rb +++ b/app/controllers/admin/budget_headings_controller.rb @@ -1,69 +1,20 @@ class Admin::BudgetHeadingsController < Admin::BaseController - include Translatable - include FeatureFlags - feature_flag :budgets - - before_action :load_budget - before_action :load_group - before_action :load_heading, only: [:edit, :update, :destroy] + include Admin::BudgetHeadingsActions def index - @headings = @group.headings.order(:id) end def new @heading = @group.headings.new end - def edit - end - - def create - @heading = @group.headings.new(budget_heading_params) - if @heading.save - redirect_to headings_index, notice: t("admin.budget_headings.create.notice") - else - render :new - end - end - - def update - if @heading.update(budget_heading_params) - redirect_to headings_index, notice: t("admin.budget_headings.update.notice") - else - render :edit - end - end - - def destroy - if @heading.can_be_deleted? - @heading.destroy! - redirect_to headings_index, notice: t("admin.budget_headings.destroy.success_notice") - else - redirect_to headings_index, alert: t("admin.budget_headings.destroy.unable_notice") - end - end - private - def load_budget - @budget = Budget.find_by_slug_or_id! params[:budget_id] - end - - def load_group - @group = @budget.groups.find_by_slug_or_id! params[:group_id] - end - - def load_heading - @heading = @group.headings.find_by_slug_or_id! params[:id] - end - def headings_index admin_budget_group_headings_path(@budget, @group) end - def budget_heading_params - valid_attributes = [:price, :population, :allow_custom_content, :latitude, :longitude, :max_ballot_lines] - params.require(:budget_heading).permit(*valid_attributes, translation_params(Budget::Heading)) + def new_action + :new end end diff --git a/app/controllers/admin/budgets_wizard/headings_controller.rb b/app/controllers/admin/budgets_wizard/headings_controller.rb new file mode 100644 index 000000000..5eb78e5d3 --- /dev/null +++ b/app/controllers/admin/budgets_wizard/headings_controller.rb @@ -0,0 +1,19 @@ +class Admin::BudgetsWizard::HeadingsController < Admin::BaseController + include Admin::BudgetHeadingsActions + + before_action :load_headings, only: [:index, :create] + + def index + @heading = @group.headings.new + end + + private + + def headings_index + admin_budgets_wizard_budget_group_headings_path(@budget, @group) + end + + def new_action + :index + end +end diff --git a/app/controllers/concerns/admin/budget_headings_actions.rb b/app/controllers/concerns/admin/budget_headings_actions.rb new file mode 100644 index 000000000..4bc125288 --- /dev/null +++ b/app/controllers/concerns/admin/budget_headings_actions.rb @@ -0,0 +1,66 @@ +module Admin::BudgetHeadingsActions + extend ActiveSupport::Concern + + included do + include Translatable + include FeatureFlags + feature_flag :budgets + + before_action :load_budget + before_action :load_group + before_action :load_headings, only: :index + before_action :load_heading, only: [:edit, :update, :destroy] + end + + def edit + end + + def create + @heading = @group.headings.new(budget_heading_params) + if @heading.save + redirect_to headings_index, notice: t("admin.budget_headings.create.notice") + else + render new_action + end + end + + def update + if @heading.update(budget_heading_params) + redirect_to headings_index, notice: t("admin.budget_headings.update.notice") + else + render :edit + end + end + + def destroy + if @heading.can_be_deleted? + @heading.destroy! + redirect_to headings_index, notice: t("admin.budget_headings.destroy.success_notice") + else + redirect_to headings_index, alert: t("admin.budget_headings.destroy.unable_notice") + end + end + + private + + def load_budget + @budget = Budget.find_by_slug_or_id! params[:budget_id] + end + + def load_group + @group = @budget.groups.find_by_slug_or_id! params[:group_id] + end + + def load_headings + @headings = @group.headings.order(:id) + end + + def load_heading + @heading = @group.headings.find_by_slug_or_id! params[:id] + end + + def budget_heading_params + valid_attributes = [:price, :population, :allow_custom_content, :latitude, :longitude, :max_ballot_lines] + params.require(:budget_heading).permit(*valid_attributes, translation_params(Budget::Heading)) + end +end diff --git a/app/views/admin/budgets_wizard/headings/edit.html.erb b/app/views/admin/budgets_wizard/headings/edit.html.erb new file mode 100644 index 000000000..63deb1260 --- /dev/null +++ b/app/views/admin/budgets_wizard/headings/edit.html.erb @@ -0,0 +1 @@ +<%= render Admin::BudgetsWizard::Headings::EditComponent.new(@heading) %> diff --git a/app/views/admin/budgets_wizard/headings/index.html.erb b/app/views/admin/budgets_wizard/headings/index.html.erb new file mode 100644 index 000000000..ed647b65a --- /dev/null +++ b/app/views/admin/budgets_wizard/headings/index.html.erb @@ -0,0 +1 @@ +<%= render Admin::BudgetsWizard::Headings::IndexComponent.new(@headings, @heading) %> diff --git a/config/i18n-tasks.yml b/config/i18n-tasks.yml index e2d8ee8e8..5fee91c88 100644 --- a/config/i18n-tasks.yml +++ b/config/i18n-tasks.yml @@ -148,6 +148,7 @@ ignore_unused: - "admin.budgets.index.filter*" - "admin.budgets.edit.(administrators|valuators).*" - "admin.budget_groups.index.*.help_block" + - "admin.budget_headings.index.*.help_block" - "admin.budget_investments.index.filter*" - "admin.organizations.index.filter*" - "admin.hidden_users.index.filter*" diff --git a/config/locales/en/admin.yml b/config/locales/en/admin.yml index ec67b8e09..427faae50 100644 --- a/config/locales/en/admin.yml +++ b/config/locales/en/admin.yml @@ -177,9 +177,14 @@ en: create: "Create new heading" edit: "Edit heading" submit: "Save heading" + group_switcher: + currently_showing: "Showing headings from the %{group} group." + different_group: "Manage headings from a different group" + the_other_group: "Manage headings from the %{group} group." index: back: "Go back to groups" help: "Headings are meant to divide the money of the participatory budget. Here you can add headings for this group and assign the amount of money that will be used for each heading." + new_button: "Add new heading" title: "%{budget} / %{group} headings" budget_phases: edit: @@ -290,10 +295,13 @@ en: creation_timeline: budget: Budget groups: Groups + headings: Headings budgets: continue: "Continue to groups" groups: continue: "Continue to headings" + headings: + continue: "Continue to phases" milestones: index: table_id: "ID" diff --git a/config/locales/es/admin.yml b/config/locales/es/admin.yml index 7feb7440f..b6b2d06d5 100644 --- a/config/locales/es/admin.yml +++ b/config/locales/es/admin.yml @@ -177,9 +177,14 @@ es: create: "Crear nueva partida" edit: "Editar partida" submit: "Guardar partida" + group_switcher: + currently_showing: "Mostrando las partidas del grupo: %{group}" + different_group: "Ir a partidas de otro grupo" + the_other_group: "Ir a partidas del grupo %{group}." index: back: "Volver a grupos" help: "Las partidas sirven para dividir el dinero del presupuesto participativo. Aquí puedes ir añadiendo partidas para cada grupo y establecer la cantidad de dinero que se gastará en cada partida." + new_button: "Añadir una partida nueva" title: "Partidas de %{budget} / %{group}" budget_phases: edit: @@ -290,10 +295,13 @@ es: creation_timeline: budget: Presupuesto groups: Grupos + headings: Partidas budgets: continue: "Continuar a grupos" groups: continue: "Continuar a partidas" + headings: + continue: "Continuar a fases" milestones: index: table_id: "ID" diff --git a/config/routes/admin.rb b/config/routes/admin.rb index 846bb8635..bb28dfa0f 100644 --- a/config/routes/admin.rb +++ b/config/routes/admin.rb @@ -74,7 +74,9 @@ namespace :admin do namespace :budgets_wizard do resources :budgets, only: [:create, :new] do - resources :groups, only: [:index, :create, :edit, :update, :destroy] + resources :groups, only: [:index, :create, :edit, :update, :destroy] do + resources :headings, only: [:index, :create, :edit, :update, :destroy] + end end end diff --git a/spec/components/admin/budgets_wizard/headings/group_switcher_component_spec.rb b/spec/components/admin/budgets_wizard/headings/group_switcher_component_spec.rb new file mode 100644 index 000000000..140db350d --- /dev/null +++ b/spec/components/admin/budgets_wizard/headings/group_switcher_component_spec.rb @@ -0,0 +1,41 @@ +require "rails_helper" + +describe Admin::BudgetsWizard::Headings::GroupSwitcherComponent, type: :component do + it "is not rendered for budgets with one group" do + group = create(:budget_group, budget: create(:budget)) + + render_inline Admin::BudgetsWizard::Headings::GroupSwitcherComponent.new(group) + + expect(page.text).to be_empty + expect(page).not_to have_css ".budget-group-switcher" + end + + it "renders a link to switch group for budgets with two groups" do + budget = create(:budget) + group = create(:budget_group, budget: budget, name: "Parks") + create(:budget_group, budget: budget, name: "Recreation") + + render_inline Admin::BudgetsWizard::Headings::GroupSwitcherComponent.new(group) + + expect(page).to have_content "Showing headings from the Parks group" + expect(page).to have_link "Manage headings from the Recreation group." + expect(page).not_to have_css "ul" + end + + it "renders a menu to switch group for budgets with more than two groups" do + budget = create(:budget) + group = create(:budget_group, budget: budget, name: "Parks") + create(:budget_group, budget: budget, name: "Recreation") + create(:budget_group, budget: budget, name: "Entertainment") + + render_inline Admin::BudgetsWizard::Headings::GroupSwitcherComponent.new(group) + + expect(page).to have_content "Showing headings from the Parks group" + expect(page).to have_button "Manage headings from a different group" + + within "button + ul" do + expect(page).to have_link "Recreation" + expect(page).to have_link "Entertainment" + end + end +end diff --git a/spec/system/admin/budgets_wizard/headings_spec.rb b/spec/system/admin/budgets_wizard/headings_spec.rb new file mode 100644 index 000000000..d3823347e --- /dev/null +++ b/spec/system/admin/budgets_wizard/headings_spec.rb @@ -0,0 +1,148 @@ +require "rails_helper" + +describe "Budgets wizard, headings step", :admin do + let(:budget) { create(:budget, :drafting) } + let(:group) { create(:budget_group, budget: budget, name: "Default group") } + + describe "Index" do + scenario "back to a previous step" do + visit admin_budgets_wizard_budget_group_headings_path(budget, group) + + within "#side_menu" do + expect(page).to have_css ".is-active", exact_text: "Participatory budgets" + end + + click_link "Go back to groups" + + expect(page).to have_css "tr", text: "Default group" + expect(page).to have_css ".creation-timeline" + end + + scenario "change to another group" do + economy = create(:budget_group, budget: budget, name: "Economy") + health = create(:budget_group, budget: budget, name: "Health") + create(:budget_group, budget: budget, name: "Technology") + + create(:budget_heading, group: economy, name: "Banking") + create(:budget_heading, group: health, name: "Hospitals") + + visit admin_budgets_wizard_budget_group_headings_path(budget, economy) + + within(".heading") do + expect(page).to have_content "Banking" + expect(page).not_to have_content "Hospitals" + end + + expect(page).not_to have_link "Health" + + click_button "Manage headings from a different group" + click_link "Health" + + within(".heading") do + expect(page).to have_content "Hospitals" + expect(page).not_to have_content "Banking" + end + expect(page).to have_css ".creation-timeline" + end + end + + describe "New" do + scenario "cancel creating a heading" do + visit admin_budgets_wizard_budget_group_headings_path(budget, group) + + expect(page).not_to have_field "Heading name" + expect(page).not_to have_button "Cancel" + expect(page).to have_content "Continue to phases" + + click_button "Add new heading" + + expect(page).to have_field "Heading name" + expect(page).not_to have_button "Add new heading" + expect(page).not_to have_content "Continue to phases" + + click_button "Cancel" + + expect(page).to have_button "Add new heading" + expect(page).not_to have_field "Heading name" + expect(page).not_to have_button "Cancel" + expect(page).to have_content "Continue to phases" + end + + scenario "submit the form with errors" do + visit admin_budgets_wizard_budget_group_headings_path(budget, group) + click_button "Add new heading" + + click_button "Create new heading" + + expect(page).not_to have_content "Heading created successfully!" + expect(page).to have_css(".is-invalid-label", text: "Heading name") + expect(page).to have_content "can't be blank" + expect(page).to have_button "Create new heading" + expect(page).to have_button "Cancel" + expect(page).not_to have_button "Add new heading" + expect(page).not_to have_content "Continue to phases" + end + end + + describe "Edit" do + scenario "update heading" do + create(:budget_heading, group: group, name: "Heading wiht a typo") + + visit admin_budgets_wizard_budget_group_headings_path(budget, group) + + expect(page).to have_css ".creation-timeline" + + within("tr", text: "Heading wiht a typo") { click_link "Edit" } + fill_in "Heading name", with: "Heading without typos" + click_button "Save heading" + + expect(page).to have_content "Heading updated successfully" + expect(page).to have_css ".creation-timeline" + expect(page).to have_css "td", exact_text: "Heading without typos" + end + + scenario "submit the form with errors and then without errors" do + heading = create(:budget_heading, group: group, name: "Heading wiht a typo") + + visit edit_admin_budgets_wizard_budget_group_heading_path(budget, group, heading) + fill_in "Heading name", with: "" + click_button "Save heading" + + expect(page).to have_css "#error_explanation" + expect(page).to have_css ".creation-timeline" + + fill_in "Heading name", with: "Heading without typos" + click_button "Save heading" + + expect(page).to have_content "Heading updated successfully" + expect(page).to have_css ".creation-timeline" + expect(page).to have_css "td", exact_text: "Heading without typos" + end + end + + describe "Destroy" do + scenario "delete a heading without investments" do + create(:budget_heading, group: group, name: "Delete me!") + + visit admin_budgets_wizard_budget_group_headings_path(budget, group) + within("tr", text: "Delete me!") { accept_confirm { click_link "Delete" } } + + expect(page).to have_content "Heading deleted successfully" + expect(page).not_to have_content "Delete me!" + expect(page).to have_css ".creation-timeline" + end + + scenario "try to delete a heading with investments" do + heading = create(:budget_heading, group: group, name: "Don't delete me!") + create(:budget_investment, heading: heading) + + visit admin_budgets_wizard_budget_group_headings_path(budget, group) + + within("tr", text: "Don't delete me!") { accept_confirm { click_link "Delete" } } + + expect(page).to have_content "You cannot delete a Heading that has associated investments" + expect(page).to have_content "Don't delete me!" + expect(page).to have_css ".creation-timeline" + end + end +end From 9421f1673ae12c762920aed903c04c27570bc386 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javi=20Mart=C3=ADn?= Date: Sun, 6 Jun 2021 19:29:27 +0200 Subject: [PATCH 14/18] Extract budget phases form to a component This way it'll be easier to use it in the budgets wizard section as well. --- .../budget_phases/form_component.html.erb | 49 ++++++++++++++++++ .../admin/budget_phases/form_component.rb | 10 ++++ app/views/admin/budget_phases/_form.html.erb | 50 +------------------ 3 files changed, 60 insertions(+), 49 deletions(-) create mode 100644 app/components/admin/budget_phases/form_component.html.erb create mode 100644 app/components/admin/budget_phases/form_component.rb diff --git a/app/components/admin/budget_phases/form_component.html.erb b/app/components/admin/budget_phases/form_component.html.erb new file mode 100644 index 000000000..125f2c9e4 --- /dev/null +++ b/app/components/admin/budget_phases/form_component.html.erb @@ -0,0 +1,49 @@ +<%= render "shared/globalize_locales", resource: phase %> + +<%= translatable_form_for [:admin, phase.budget, phase], html: { class: "budget-phases-form" } do |f| %> + + <%= render "shared/errors", resource: phase %> + +
+ + <%= t("admin.budget_phases.edit.duration") %> + + +

+ <%= t("admin.budget_phases.edit.duration_description") %> +

+ +
+ <%= f.date_field :starts_at, id: "start_date" %> +
+ +
+ <%= f.date_field :ends_at, id: "end_date" %> +
+
+ +
+ <%= f.check_box :enabled %> + + + <%= t("admin.budget_phases.edit.enabled_help_text") %> + +
+ + <%= f.translatable_fields do |translations_form| %> +
+ <%= translations_form.text_field :name, hint: t("admin.budget_phases.edit.name_help_text") %> +
+ +
+ <%= translations_form.text_area :description, + maxlength: Budget::Phase::DESCRIPTION_MAX_LENGTH, + class: "html-area", + hint: t("admin.budget_phases.edit.description_help_text") %> +
+ <% end %> + +
+ <%= f.submit t("admin.budget_phases.edit.save_changes"), class: "button success" %> +
+<% end %> diff --git a/app/components/admin/budget_phases/form_component.rb b/app/components/admin/budget_phases/form_component.rb new file mode 100644 index 000000000..0b890c9f4 --- /dev/null +++ b/app/components/admin/budget_phases/form_component.rb @@ -0,0 +1,10 @@ +class Admin::BudgetPhases::FormComponent < ApplicationComponent + include TranslatableFormHelper + include GlobalizeHelper + + attr_reader :phase + + def initialize(phase) + @phase = phase + end +end diff --git a/app/views/admin/budget_phases/_form.html.erb b/app/views/admin/budget_phases/_form.html.erb index f54f2aad6..e8a7ca310 100644 --- a/app/views/admin/budget_phases/_form.html.erb +++ b/app/views/admin/budget_phases/_form.html.erb @@ -1,49 +1 @@ -<%= render "shared/globalize_locales", resource: @phase %> - -<%= translatable_form_for [:admin, @phase.budget, @phase], html: { class: "budget-phases-form" } do |f| %> - - <%= render "shared/errors", resource: @phase %> - -
- - <%= t("admin.budget_phases.edit.duration") %> - - -

- <%= t("admin.budget_phases.edit.duration_description") %> -

- -
- <%= f.date_field :starts_at, id: "start_date" %> -
- -
- <%= f.date_field :ends_at, id: "end_date" %> -
-
- -
- <%= f.check_box :enabled %> - - - <%= t("admin.budget_phases.edit.enabled_help_text") %> - -
- - <%= f.translatable_fields do |translations_form| %> -
- <%= translations_form.text_field :name, hint: t("admin.budget_phases.edit.name_help_text") %> -
- -
- <%= translations_form.text_area :description, - maxlength: Budget::Phase::DESCRIPTION_MAX_LENGTH, - class: "html-area", - hint: t("admin.budget_phases.edit.description_help_text") %> -
- <% end %> - -
- <%= f.submit t("admin.budget_phases.edit.save_changes"), class: "button success" %> -
-<% end %> +<%= render Admin::BudgetPhases::FormComponent.new(@phase) %> From eb77d09425815983031e54ed2efa728ff79cbe1d Mon Sep 17 00:00:00 2001 From: Julian Herrero Date: Mon, 23 Mar 2020 19:04:38 +0700 Subject: [PATCH 15/18] Add phases step to budget creation --- .../budget_phases/form_component.html.erb | 2 +- .../admin/budget_phases/form_component.rb | 1 + .../creation_timeline_component.rb | 2 +- .../headings/creation_step_component.rb | 2 +- .../phases/edit_component.html.erb | 6 ++ .../budgets_wizard/phases/edit_component.rb | 22 +++++++ .../phases/index_component.html.erb | 12 ++++ .../budgets_wizard/phases/index_component.rb | 18 ++++++ .../admin/budget_phases_controller.rb | 25 +------- .../admin/budgets_wizard/phases_controller.rb | 12 ++++ .../concerns/admin/budget_phases_actions.rb | 36 ++++++++++++ app/views/admin/budget_phases/edit.html.erb | 6 +- .../admin/budgets_wizard/phases/edit.html.erb | 1 + .../budgets_wizard/phases/index.html.erb | 1 + config/i18n-tasks.yml | 1 + config/locales/en/admin.yml | 6 ++ config/locales/es/admin.yml | 6 ++ config/routes/admin.rb | 2 + spec/system/admin/budget_phases_spec.rb | 2 +- .../admin/budgets_wizard/phases_spec.rb | 57 +++++++++++++++++++ 20 files changed, 189 insertions(+), 31 deletions(-) create mode 100644 app/components/admin/budgets_wizard/phases/edit_component.html.erb create mode 100644 app/components/admin/budgets_wizard/phases/edit_component.rb create mode 100644 app/components/admin/budgets_wizard/phases/index_component.html.erb create mode 100644 app/components/admin/budgets_wizard/phases/index_component.rb create mode 100644 app/controllers/admin/budgets_wizard/phases_controller.rb create mode 100644 app/controllers/concerns/admin/budget_phases_actions.rb create mode 100644 app/views/admin/budgets_wizard/phases/edit.html.erb create mode 100644 app/views/admin/budgets_wizard/phases/index.html.erb create mode 100644 spec/system/admin/budgets_wizard/phases_spec.rb diff --git a/app/components/admin/budget_phases/form_component.html.erb b/app/components/admin/budget_phases/form_component.html.erb index 125f2c9e4..78f10c235 100644 --- a/app/components/admin/budget_phases/form_component.html.erb +++ b/app/components/admin/budget_phases/form_component.html.erb @@ -1,6 +1,6 @@ <%= render "shared/globalize_locales", resource: phase %> -<%= translatable_form_for [:admin, phase.budget, phase], html: { class: "budget-phases-form" } do |f| %> +<%= translatable_form_for [namespace, phase.budget, phase], html: { class: "budget-phases-form" } do |f| %> <%= render "shared/errors", resource: phase %> diff --git a/app/components/admin/budget_phases/form_component.rb b/app/components/admin/budget_phases/form_component.rb index 0b890c9f4..49c6cf36c 100644 --- a/app/components/admin/budget_phases/form_component.rb +++ b/app/components/admin/budget_phases/form_component.rb @@ -1,6 +1,7 @@ class Admin::BudgetPhases::FormComponent < ApplicationComponent include TranslatableFormHelper include GlobalizeHelper + include Admin::Namespace attr_reader :phase diff --git a/app/components/admin/budgets_wizard/creation_timeline_component.rb b/app/components/admin/budgets_wizard/creation_timeline_component.rb index a4f4ed24f..7fe7ffcf2 100644 --- a/app/components/admin/budgets_wizard/creation_timeline_component.rb +++ b/app/components/admin/budgets_wizard/creation_timeline_component.rb @@ -8,6 +8,6 @@ class Admin::BudgetsWizard::CreationTimelineComponent < ApplicationComponent private def steps - %w[budget groups headings] + %w[budget groups headings phases] end end diff --git a/app/components/admin/budgets_wizard/headings/creation_step_component.rb b/app/components/admin/budgets_wizard/headings/creation_step_component.rb index 8758c6bbc..6c07c6670 100644 --- a/app/components/admin/budgets_wizard/headings/creation_step_component.rb +++ b/app/components/admin/budgets_wizard/headings/creation_step_component.rb @@ -16,7 +16,7 @@ class Admin::BudgetsWizard::Headings::CreationStepComponent < ApplicationCompone end def next_step_path - admin_budget_path(budget) if next_step_enabled? + admin_budgets_wizard_budget_budget_phases_path(budget) if next_step_enabled? end def next_step_enabled? diff --git a/app/components/admin/budgets_wizard/phases/edit_component.html.erb b/app/components/admin/budgets_wizard/phases/edit_component.html.erb new file mode 100644 index 000000000..44131384c --- /dev/null +++ b/app/components/admin/budgets_wizard/phases/edit_component.html.erb @@ -0,0 +1,6 @@ +<%= back_link_to admin_budgets_wizard_budget_budget_phases_path(budget) %> + +<%= header %> + +<%= render Admin::BudgetsWizard::CreationTimelineComponent.new("phases") %> +<%= render "/admin/budget_phases/form" %> diff --git a/app/components/admin/budgets_wizard/phases/edit_component.rb b/app/components/admin/budgets_wizard/phases/edit_component.rb new file mode 100644 index 000000000..68645ed95 --- /dev/null +++ b/app/components/admin/budgets_wizard/phases/edit_component.rb @@ -0,0 +1,22 @@ +class Admin::BudgetsWizard::Phases::EditComponent < ApplicationComponent + include Header + attr_reader :phase + + def initialize(phase) + @phase = phase + end + + def budget + phase.budget + end + + def title + "#{t("admin.budget_phases.edit.title")} - #{phase.name}" + end + + private + + def form_path + admin_budgets_wizard_budget_budget_phases_path(budget) + end +end diff --git a/app/components/admin/budgets_wizard/phases/index_component.html.erb b/app/components/admin/budgets_wizard/phases/index_component.html.erb new file mode 100644 index 000000000..816dc0d91 --- /dev/null +++ b/app/components/admin/budgets_wizard/phases/index_component.html.erb @@ -0,0 +1,12 @@ +<%= back_link_to back_link_path, t("admin.budgets_wizard.phases.back") %> + +<%= header %> + +<%= render Admin::Budgets::HelpComponent.new("budget_phases") %> +<%= render Admin::BudgetsWizard::CreationTimelineComponent.new("phases") %> + +<%= render Admin::BudgetPhases::PhasesComponent.new(budget) %> + +<%= link_to t("admin.budgets_wizard.phases.continue"), + admin_budget_path(budget), + class: "button success" %> diff --git a/app/components/admin/budgets_wizard/phases/index_component.rb b/app/components/admin/budgets_wizard/phases/index_component.rb new file mode 100644 index 000000000..a1b680003 --- /dev/null +++ b/app/components/admin/budgets_wizard/phases/index_component.rb @@ -0,0 +1,18 @@ +class Admin::BudgetsWizard::Phases::IndexComponent < ApplicationComponent + include Header + attr_reader :budget + + def initialize(budget) + @budget = budget + end + + def title + t("admin.budget_phases.index.title", budget: budget.name) + end + + private + + def back_link_path + admin_budgets_wizard_budget_group_headings_path(budget, budget.groups.first) + end +end diff --git a/app/controllers/admin/budget_phases_controller.rb b/app/controllers/admin/budget_phases_controller.rb index 1ff8dcedb..ac2a763e1 100644 --- a/app/controllers/admin/budget_phases_controller.rb +++ b/app/controllers/admin/budget_phases_controller.rb @@ -1,28 +1,9 @@ class Admin::BudgetPhasesController < Admin::BaseController - include Translatable - - before_action :load_phase, only: [:edit, :update] - - def edit - end - - def update - if @phase.update(budget_phase_params) - notice = t("flash.actions.save_changes.notice") - redirect_to edit_admin_budget_path(@phase.budget), notice: notice - else - render :edit - end - end + include Admin::BudgetPhasesActions private - def load_phase - @phase = Budget::Phase.find(params[:id]) - end - - def budget_phase_params - valid_attributes = [:starts_at, :ends_at, :enabled] - params.require(:budget_phase).permit(*valid_attributes, translation_params(Budget::Phase)) + def phases_index + edit_admin_budget_path(@phase.budget) end end diff --git a/app/controllers/admin/budgets_wizard/phases_controller.rb b/app/controllers/admin/budgets_wizard/phases_controller.rb new file mode 100644 index 000000000..889b22c4f --- /dev/null +++ b/app/controllers/admin/budgets_wizard/phases_controller.rb @@ -0,0 +1,12 @@ +class Admin::BudgetsWizard::PhasesController < Admin::BaseController + include Admin::BudgetPhasesActions + + def index + end + + private + + def phases_index + admin_budgets_wizard_budget_budget_phases_path(@phase.budget) + end +end diff --git a/app/controllers/concerns/admin/budget_phases_actions.rb b/app/controllers/concerns/admin/budget_phases_actions.rb new file mode 100644 index 000000000..80a1af51c --- /dev/null +++ b/app/controllers/concerns/admin/budget_phases_actions.rb @@ -0,0 +1,36 @@ +module Admin::BudgetPhasesActions + extend ActiveSupport::Concern + + included do + include Translatable + + before_action :load_budget + before_action :load_phase, only: [:edit, :update] + end + + def edit + end + + def update + if @phase.update(budget_phase_params) + redirect_to phases_index, notice: t("flash.actions.save_changes.notice") + else + render :edit + end + end + + private + + def load_budget + @budget = Budget.find_by_slug_or_id!(params[:budget_id]) + end + + def load_phase + @phase = @budget.phases.find(params[:id]) + end + + def budget_phase_params + valid_attributes = [:starts_at, :ends_at, :enabled] + params.require(:budget_phase).permit(*valid_attributes, translation_params(Budget::Phase)) + end +end diff --git a/app/views/admin/budget_phases/edit.html.erb b/app/views/admin/budget_phases/edit.html.erb index 452c11f96..27fbdd585 100644 --- a/app/views/admin/budget_phases/edit.html.erb +++ b/app/views/admin/budget_phases/edit.html.erb @@ -1,5 +1 @@ -<%= back_link_to edit_admin_budget_path(@phase.budget) %> - -

<%= t("admin.budgets.edit.title") %> - <%= @phase.name %>

- -<%= render "/admin/budget_phases/form" %> +<%= render Admin::BudgetsWizard::Phases::EditComponent.new(@phase) %> diff --git a/app/views/admin/budgets_wizard/phases/edit.html.erb b/app/views/admin/budgets_wizard/phases/edit.html.erb new file mode 100644 index 000000000..27fbdd585 --- /dev/null +++ b/app/views/admin/budgets_wizard/phases/edit.html.erb @@ -0,0 +1 @@ +<%= render Admin::BudgetsWizard::Phases::EditComponent.new(@phase) %> diff --git a/app/views/admin/budgets_wizard/phases/index.html.erb b/app/views/admin/budgets_wizard/phases/index.html.erb new file mode 100644 index 000000000..1fa77a2d1 --- /dev/null +++ b/app/views/admin/budgets_wizard/phases/index.html.erb @@ -0,0 +1 @@ +<%= render Admin::BudgetsWizard::Phases::IndexComponent.new(@budget) %> diff --git a/config/i18n-tasks.yml b/config/i18n-tasks.yml index 5fee91c88..97bb35653 100644 --- a/config/i18n-tasks.yml +++ b/config/i18n-tasks.yml @@ -149,6 +149,7 @@ ignore_unused: - "admin.budgets.edit.(administrators|valuators).*" - "admin.budget_groups.index.*.help_block" - "admin.budget_headings.index.*.help_block" + - "admin.budget_phases.index.help_block" - "admin.budget_investments.index.filter*" - "admin.organizations.index.filter*" - "admin.hidden_users.index.filter*" diff --git a/config/locales/en/admin.yml b/config/locales/en/admin.yml index 427faae50..c884a88e8 100644 --- a/config/locales/en/admin.yml +++ b/config/locales/en/admin.yml @@ -188,6 +188,7 @@ en: title: "%{budget} / %{group} headings" budget_phases: edit: + title: "Edit phase" description_help_text: This text will appear in the header when the phase is active duration: "Phase's duration" duration_description: "The period of time this phase will be active." @@ -196,6 +197,7 @@ en: save_changes: Save changes index: help: "Participatory budgets have different phases. Here you can enable or disable phases and also customize each individual phase." + title: "%{budget} phases" budget_investments: index: heading_filter_all: All headings @@ -296,12 +298,16 @@ en: budget: Budget groups: Groups headings: Headings + phases: Phases budgets: continue: "Continue to groups" groups: continue: "Continue to headings" headings: continue: "Continue to phases" + phases: + back: "Go back to headings" + continue: "Finish" milestones: index: table_id: "ID" diff --git a/config/locales/es/admin.yml b/config/locales/es/admin.yml index b6b2d06d5..1ea8f48f7 100644 --- a/config/locales/es/admin.yml +++ b/config/locales/es/admin.yml @@ -194,8 +194,10 @@ es: enabled_help_text: Esta fase será pública en el calendario de fases del presupuesto y estará activa para otros propósitos name_help_text: "Este es el título de la fase que los usuarios leerán en el encabezado cuando la fase esté activa." save_changes: Guardar cambios + title: "Editar fase" index: help: "Los presupuestos participativos tienen distintas fases. Aquí puedes habilitar o deshabilitar fases y también personalizar cada una de las fases." + title: "Fases de %{budget}" budget_investments: index: heading_filter_all: Todas las partidas @@ -296,12 +298,16 @@ es: budget: Presupuesto groups: Grupos headings: Partidas + phases: Fases budgets: continue: "Continuar a grupos" groups: continue: "Continuar a partidas" headings: continue: "Continuar a fases" + phases: + back: "Volver a partidas" + continue: "Finalizar" milestones: index: table_id: "ID" diff --git a/config/routes/admin.rb b/config/routes/admin.rb index bb28dfa0f..ea97b859f 100644 --- a/config/routes/admin.rb +++ b/config/routes/admin.rb @@ -77,6 +77,8 @@ namespace :admin do resources :groups, only: [:index, :create, :edit, :update, :destroy] do resources :headings, only: [:index, :create, :edit, :update, :destroy] end + + resources :phases, as: "budget_phases", only: [:index, :edit, :update] end end diff --git a/spec/system/admin/budget_phases_spec.rb b/spec/system/admin/budget_phases_spec.rb index ce5c22c1c..36dff7ade 100644 --- a/spec/system/admin/budget_phases_spec.rb +++ b/spec/system/admin/budget_phases_spec.rb @@ -32,7 +32,7 @@ describe "Admin budget phases" do within("tr", text: "Accepting projects") { click_link "Edit phase" } end - expect(page).to have_css "h2", exact_text: "Edit Participatory budget - Accepting projects" + expect(page).to have_css "h2", exact_text: "Edit phase - Accepting projects" fill_in "Name", with: "My phase custom name" click_button "Save changes" diff --git a/spec/system/admin/budgets_wizard/phases_spec.rb b/spec/system/admin/budgets_wizard/phases_spec.rb new file mode 100644 index 000000000..d9ae34101 --- /dev/null +++ b/spec/system/admin/budgets_wizard/phases_spec.rb @@ -0,0 +1,57 @@ +require "rails_helper" + +describe "Budgets wizard, phases step", :admin do + let(:budget) { create(:budget, :drafting) } + let!(:heading) { create(:budget_heading, budget: budget) } + + describe "Index" do + scenario "back to a previous step" do + heading.update!(name: "Main Park") + + visit admin_budgets_wizard_budget_budget_phases_path(budget) + + within "#side_menu" do + expect(page).to have_css ".is-active", exact_text: "Participatory budgets" + end + + click_link "Go back to headings" + + expect(page).to have_css "tr", text: "Main Park" + expect(page).to have_css ".creation-timeline" + end + end + + describe "Edit" do + scenario "update phase" do + visit admin_budgets_wizard_budget_budget_phases_path(budget) + + expect(page).to have_css ".creation-timeline" + + within("tr", text: "Selecting projects") { click_link "Edit phase" } + fill_in "Name", with: "Choosing projects" + click_button "Save changes" + + expect(page).to have_content "Changes saved" + expect(page).to have_css ".creation-timeline" + within_table("Phases") { expect(page).to have_content "Choosing projects" } + end + + scenario "submit the form with errors and then without errors" do + phase = budget.phases.accepting + + visit edit_admin_budgets_wizard_budget_budget_phase_path(budget, phase) + fill_in "Name", with: "" + click_button "Save changes" + + expect(page).to have_css "#error_explanation" + expect(page).to have_css ".creation-timeline" + + fill_in "Name", with: "Welcoming projects" + click_button "Save changes" + + expect(page).to have_content "Changes saved" + expect(page).to have_css ".creation-timeline" + within_table("Phases") { expect(page).to have_content "Welcoming projects" } + end + end +end From f110e65f8054f620b2aa9535f7bb530d6b5e7a1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javi=20Mart=C3=ADn?= Date: Sun, 6 Jun 2021 15:57:22 +0200 Subject: [PATCH 16/18] Allow enabling/disabling phases in budgets wizard So now there's no need to edit each phase individually to enable/disable them. We aren't doing the same thing in the form to edit a budget because we aren't sure about possible usability issues. On one hand, in some tables we automatically update records when we mark a checkbox, so users might expect that. On the other hand, having a checkbox in the middle of a form which updates the database automatically is counter-intuitive, particularly when right below that table there are other checkboxes which don't update the database until the form is submitted. So, either way, chances are users would think they've updated the phases (or kept them intact) while the opposite would be true. In the form within the wizard to create a budget that problem isn't that important because there aren't any other fields in the form and it's pretty intuitive that what users do will have no effect until they press the "Finish" button. Co-Authored-By: Julian Nicolas Herrero --- app/assets/stylesheets/admin.scss | 5 ++++ .../budget_phases/phases_component.html.erb | 2 +- .../admin/budget_phases/phases_component.rb | 19 ++++++++++-- .../phases/index_component.html.erb | 9 +++--- .../admin/budgets_wizard/phases_controller.rb | 14 +++++++++ app/models/budget.rb | 1 + config/locales/en/admin.yml | 3 ++ config/locales/es/admin.yml | 3 ++ config/routes/admin.rb | 4 ++- .../admin/budgets_wizard/phases_spec.rb | 29 +++++++++++++++++++ 10 files changed, 80 insertions(+), 9 deletions(-) diff --git a/app/assets/stylesheets/admin.scss b/app/assets/stylesheets/admin.scss index 8aede0497..17e221cf7 100644 --- a/app/assets/stylesheets/admin.scss +++ b/app/assets/stylesheets/admin.scss @@ -258,6 +258,11 @@ $table-header: #ecf1f6; [type="submit"] ~ a { margin-left: $line-height / 2; } + + [type="checkbox"] { + margin-bottom: 0; + vertical-align: middle; + } } hr { diff --git a/app/components/admin/budget_phases/phases_component.html.erb b/app/components/admin/budget_phases/phases_component.html.erb index 6f663578c..787508634 100644 --- a/app/components/admin/budget_phases/phases_component.html.erb +++ b/app/components/admin/budget_phases/phases_component.html.erb @@ -26,7 +26,7 @@ <% end %> - <%= enabled_text(phase) %> + <%= enabled_cell(phase) %> <%= render Admin::TableActionsComponent.new(phase, diff --git a/app/components/admin/budget_phases/phases_component.rb b/app/components/admin/budget_phases/phases_component.rb index b27958f18..25a2d0280 100644 --- a/app/components/admin/budget_phases/phases_component.rb +++ b/app/components/admin/budget_phases/phases_component.rb @@ -1,8 +1,9 @@ class Admin::BudgetPhases::PhasesComponent < ApplicationComponent - attr_reader :budget + attr_reader :budget, :form - def initialize(budget) + def initialize(budget, form: nil) @budget = budget + @form = form end private @@ -15,6 +16,20 @@ class Admin::BudgetPhases::PhasesComponent < ApplicationComponent Admin::Budgets::DurationComponent.new(phase).dates end + def enabled_cell(phase) + if form + form.fields_for :phases, phase do |phase_fields| + phase_fields.check_box :enabled, + label: false, + aria: { + label: t("admin.budgets.edit.enable_phase", phase: phase.name) + } + end + else + enabled_text(phase) + end + end + def enabled_text(phase) if phase.enabled? tag.span t("shared.yes"), class: "budget-phase-enabled" diff --git a/app/components/admin/budgets_wizard/phases/index_component.html.erb b/app/components/admin/budgets_wizard/phases/index_component.html.erb index 816dc0d91..a97812cfb 100644 --- a/app/components/admin/budgets_wizard/phases/index_component.html.erb +++ b/app/components/admin/budgets_wizard/phases/index_component.html.erb @@ -5,8 +5,7 @@ <%= render Admin::Budgets::HelpComponent.new("budget_phases") %> <%= render Admin::BudgetsWizard::CreationTimelineComponent.new("phases") %> -<%= render Admin::BudgetPhases::PhasesComponent.new(budget) %> - -<%= link_to t("admin.budgets_wizard.phases.continue"), - admin_budget_path(budget), - class: "button success" %> +<%= form_for budget, url: update_all_admin_budgets_wizard_budget_budget_phases_path(budget) do |f| %> + <%= render Admin::BudgetPhases::PhasesComponent.new(budget, form: f) %> + <%= f.submit t("admin.budgets_wizard.phases.continue"), class: "button success" %> +<% end %> diff --git a/app/controllers/admin/budgets_wizard/phases_controller.rb b/app/controllers/admin/budgets_wizard/phases_controller.rb index 889b22c4f..5376e93b7 100644 --- a/app/controllers/admin/budgets_wizard/phases_controller.rb +++ b/app/controllers/admin/budgets_wizard/phases_controller.rb @@ -4,9 +4,23 @@ class Admin::BudgetsWizard::PhasesController < Admin::BaseController def index end + def update_all + @budget.update!(phases_params) + + redirect_to admin_budgets_path, notice: t("admin.budgets_wizard.phases.update_all.notice") + end + private def phases_index admin_budgets_wizard_budget_budget_phases_path(@phase.budget) end + + def phases_params + params.require(:budget).permit(allowed_phases_params) + end + + def allowed_phases_params + { phases_attributes: [:id, :enabled] } + end end diff --git a/app/models/budget.rb b/app/models/budget.rb index c685323f5..dba0d6e0b 100644 --- a/app/models/budget.rb +++ b/app/models/budget.rb @@ -42,6 +42,7 @@ class Budget < ApplicationRecord has_one :poll after_create :generate_phases + accepts_nested_attributes_for :phases scope :published, -> { where(published: true) } scope :drafting, -> { where.not(id: published) } diff --git a/config/locales/en/admin.yml b/config/locales/en/admin.yml index c884a88e8..8e2db9f36 100644 --- a/config/locales/en/admin.yml +++ b/config/locales/en/admin.yml @@ -106,6 +106,7 @@ en: enabled: Enabled actions: Actions edit_phase: Edit phase + enable_phase: "Enable %{phase} phase" active: Active blank_dates: Dates are blank administrators: @@ -308,6 +309,8 @@ en: phases: back: "Go back to headings" continue: "Finish" + update_all: + notice: "Phases configured successfully" milestones: index: table_id: "ID" diff --git a/config/locales/es/admin.yml b/config/locales/es/admin.yml index 1ea8f48f7..58c44349e 100644 --- a/config/locales/es/admin.yml +++ b/config/locales/es/admin.yml @@ -106,6 +106,7 @@ es: enabled: Habilitada actions: Acciones edit_phase: Editar fase + enable_phase: "Habilitar fase de %{phase}" active: Activa blank_dates: Sin fechas administrators: @@ -308,6 +309,8 @@ es: phases: back: "Volver a partidas" continue: "Finalizar" + update_all: + notice: "Fases configuradas con éxito" milestones: index: table_id: "ID" diff --git a/config/routes/admin.rb b/config/routes/admin.rb index ea97b859f..cbdaab6eb 100644 --- a/config/routes/admin.rb +++ b/config/routes/admin.rb @@ -78,7 +78,9 @@ namespace :admin do resources :headings, only: [:index, :create, :edit, :update, :destroy] end - resources :phases, as: "budget_phases", only: [:index, :edit, :update] + resources :phases, as: "budget_phases", only: [:index, :edit, :update] do + collection { patch :update_all } + end end end diff --git a/spec/system/admin/budgets_wizard/phases_spec.rb b/spec/system/admin/budgets_wizard/phases_spec.rb index d9ae34101..eb2e0b50b 100644 --- a/spec/system/admin/budgets_wizard/phases_spec.rb +++ b/spec/system/admin/budgets_wizard/phases_spec.rb @@ -19,6 +19,35 @@ describe "Budgets wizard, phases step", :admin do expect(page).to have_css "tr", text: "Main Park" expect(page).to have_css ".creation-timeline" end + + scenario "Enable and disable phases" do + visit admin_budgets_wizard_budget_budget_phases_path(budget) + + uncheck "Enable Information phase" + uncheck "Enable Reviewing voting phase" + + click_button "Finish" + + expect(page).to have_content "Phases configured successfully" + + visit edit_admin_budget_path(budget) + + within "tr", text: "Information" do + expect(page).to have_css ".budget-phase-disabled", visible: :all + end + + within "tr", text: "Reviewing voting" do + expect(page).to have_css ".budget-phase-disabled", visible: :all + end + + within "tr", text: "Accepting projects" do + expect(page).to have_css ".budget-phase-enabled", visible: :all + end + + within "tr", text: "Voting projects" do + expect(page).to have_css ".budget-phase-enabled", visible: :all + end + end end describe "Edit" do From f8008a4a6fa5770e32118bff084379b11d6d252a Mon Sep 17 00:00:00 2001 From: decabeza Date: Tue, 7 Apr 2020 12:44:45 +0200 Subject: [PATCH 17/18] Add link to remove budget on budget admin index --- .../budgets/table_actions_component.html.erb | 6 ++++- config/locales/en/admin.yml | 3 +++ config/locales/es/admin.yml | 3 +++ .../budgets/table_actions_component_spec.rb | 5 ++-- spec/system/admin/budgets_spec.rb | 25 ++++++++++++++----- 5 files changed, 33 insertions(+), 9 deletions(-) diff --git a/app/components/admin/budgets/table_actions_component.html.erb b/app/components/admin/budgets/table_actions_component.html.erb index e2d86ae80..9eba6926a 100644 --- a/app/components/admin/budgets/table_actions_component.html.erb +++ b/app/components/admin/budgets/table_actions_component.html.erb @@ -1,4 +1,8 @@ -<%= render Admin::TableActionsComponent.new(budget, actions: [:edit], edit_text: t("admin.budgets.index.edit_budget")) do %> +<%= render Admin::TableActionsComponent.new(budget, + edit_text: t("admin.budgets.index.edit_budget"), + destroy_confirmation: t("admin.actions.confirm_delete", resource_name: t("admin.budgets.shared.resource_name"), + name: budget.name) + ) do %> <%= link_to t("admin.budgets.index.budget_investments"), admin_budget_budget_investments_path(budget_id: budget.id), class: "investments-link" %> diff --git a/config/locales/en/admin.yml b/config/locales/en/admin.yml index 8e2db9f36..00cc77e7f 100644 --- a/config/locales/en/admin.yml +++ b/config/locales/en/admin.yml @@ -14,6 +14,7 @@ en: edit: Edit configure: Configure delete: Delete + confirm_delete: "Are you sure? This action will delete %{resource_name} '%{name}' and can't be undone." officing_booth: title: "You are officing the booth located at %{booth}. If this is not correct, do not continue and call the help phone number. Thank you." banners: @@ -134,6 +135,8 @@ en: calculate: Calculate Winner Investments calculated: Winners being calculated, it may take a minute. recalculate: Recalculate Winner Investments + shared: + resource_name: "the budget" budget_groups: name: "Name" headings_name: "Headings" diff --git a/config/locales/es/admin.yml b/config/locales/es/admin.yml index 58c44349e..468a6d37d 100644 --- a/config/locales/es/admin.yml +++ b/config/locales/es/admin.yml @@ -13,6 +13,7 @@ es: unmark_featured: Quitar destacado edit: Editar configure: Configurar + confirm_delete: "¿Estás seguro? Esta acción borrará %{resource_name} '%{name}' y no se puede deshacer." delete: Borrar officing_booth: title: "Estás ahora mismo en la mesa ubicada en %{booth}. Si esto no es correcto no sigas adelante y llama al teléfono de incidencias. Gracias." @@ -134,6 +135,8 @@ es: calculate: Calcular proyectos ganadores calculated: Calculando ganadores, puede tardar un minuto. recalculate: Recalcular proyectos ganadores + shared: + resource_name: "el presupuesto" budget_groups: name: "Nombre" headings_name: "Partidas" diff --git a/spec/components/admin/budgets/table_actions_component_spec.rb b/spec/components/admin/budgets/table_actions_component_spec.rb index 97f20083f..119fdffd4 100644 --- a/spec/components/admin/budgets/table_actions_component_spec.rb +++ b/spec/components/admin/budgets/table_actions_component_spec.rb @@ -8,15 +8,16 @@ describe Admin::Budgets::TableActionsComponent, type: :component do allow(ViewComponent::Base).to receive(:test_controller).and_return("Admin::BaseController") end - it "renders links to edit budget, manage investments and edit groups and manage ballots" do + it "renders links to edit and delete budget, manage investments and edit groups and manage ballots" do render_inline component - expect(page).to have_css "a", count: 5 + expect(page).to have_css "a", count: 6 expect(page).to have_link "Manage projects", href: /investments/ expect(page).to have_link "Edit headings groups", href: /groups/ expect(page).to have_link "Edit budget", href: /edit/ expect(page).to have_link "Admin ballots" expect(page).to have_link "Preview budget", href: /budgets/ + expect(page).to have_link "Delete", href: /budgets/ end it "renders link to create new poll for budgets without polls" do diff --git a/spec/system/admin/budgets_spec.rb b/spec/system/admin/budgets_spec.rb index 50f8c3de8..1f5a755d8 100644 --- a/spec/system/admin/budgets_spec.rb +++ b/spec/system/admin/budgets_spec.rb @@ -82,6 +82,22 @@ describe "Admin budgets", :admin do end end end + + scenario "Delete budget from index" do + create(:budget, name: "To be deleted") + + visit admin_budgets_path + + within "tr", text: "To be deleted" do + message = "Are you sure? This action will delete the budget 'To be deleted' and can't be undone." + + accept_confirm(message) { click_link "Delete" } + end + + expect(page).to have_content("Budget deleted successfully") + expect(page).to have_content("There are no budgets.") + expect(page).not_to have_content "To be deleted" + end end context "Publish" do @@ -115,8 +131,7 @@ describe "Admin budgets", :admin do let(:heading) { create(:budget_heading, budget: budget) } scenario "Destroy a budget without investments" do - visit admin_budgets_path - click_link "Edit budget" + visit edit_admin_budget_path(budget) click_link "Delete budget" expect(page).to have_content("Budget deleted successfully") @@ -127,8 +142,7 @@ describe "Admin budgets", :admin do budget.administrators << Administrator.first budget.valuators << create(:valuator) - visit admin_budgets_path - click_link "Edit budget" + visit edit_admin_budget_path(budget) click_link "Delete budget" expect(page).to have_content "Budget deleted successfully" @@ -138,8 +152,7 @@ describe "Admin budgets", :admin do scenario "Try to destroy a budget with investments" do create(:budget_investment, heading: heading) - visit admin_budgets_path - click_link "Edit budget" + visit edit_admin_budget_path(budget) click_link "Delete budget" expect(page).to have_content("You cannot delete a budget that has associated investments") From bdd5361f6a8bc9f7eb9c065a3e9a58507c310f7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javi=20Mart=C3=ADn?= Date: Mon, 7 Jun 2021 20:19:55 +0200 Subject: [PATCH 18/18] Allow editing the budget in budget creation wizard When users created a budget and made a typo, they could use the link to go back to edit a budget. However, after doing so, they were out of the budget creation process. So we're now letting users go back to edit the budget, fix any mistakes they might have made, and then continue to groups. --- .../admin/budgets/form_component.html.erb | 8 ++-- .../admin/budgets/form_component.rb | 6 ++- .../budgets/edit_component.html.erb | 6 +++ .../budgets_wizard/budgets/edit_component.rb | 12 +++++ .../budgets/new_component.html.erb | 2 +- .../groups/index_component.html.erb | 2 +- .../budgets_wizard/budgets_controller.rb | 17 ++++++- .../budgets_wizard/budgets/edit.html.erb | 1 + config/locales/en/admin.yml | 1 + config/locales/es/admin.yml | 1 + config/routes/admin.rb | 2 +- .../admin/budgets_wizard/budgets_spec.rb | 47 +++++++++++++++++-- 12 files changed, 92 insertions(+), 13 deletions(-) create mode 100644 app/components/admin/budgets_wizard/budgets/edit_component.html.erb create mode 100644 app/components/admin/budgets_wizard/budgets/edit_component.rb create mode 100644 app/views/admin/budgets_wizard/budgets/edit.html.erb diff --git a/app/components/admin/budgets/form_component.html.erb b/app/components/admin/budgets/form_component.html.erb index 2b8898607..57a3f8d44 100644 --- a/app/components/admin/budgets/form_component.html.erb +++ b/app/components/admin/budgets/form_component.html.erb @@ -37,7 +37,7 @@ <%= render "/admin/budgets/association", assignable_type: "valuators", assignables: valuators, form: f %> - <% if budget.persisted? %> + <% unless wizard? %>
<%= t("admin.budgets.edit.info.phases_settings") %>
@@ -52,10 +52,10 @@
- <% if budget.persisted? %> - <%= f.submit nil, class: "button success" %> - <% else %> + <% if wizard? %> <%= f.submit t("admin.budgets_wizard.budgets.continue"), class: "button success expanded" %> + <% else %> + <%= f.submit nil, class: "button success" %> <% end %>
diff --git a/app/components/admin/budgets/form_component.rb b/app/components/admin/budgets/form_component.rb index 35d9b6de0..3d57663f0 100644 --- a/app/components/admin/budgets/form_component.rb +++ b/app/components/admin/budgets/form_component.rb @@ -3,14 +3,16 @@ class Admin::Budgets::FormComponent < ApplicationComponent include GlobalizeHelper include Admin::Namespace - attr_reader :budget + attr_reader :budget, :wizard + alias_method :wizard?, :wizard delegate :display_calculate_winners_button?, :calculate_winner_button_text, :calculate_winners_admin_budget_path, to: :helpers - def initialize(budget) + def initialize(budget, wizard: false) @budget = budget + @wizard = wizard end def voting_styles_select_options diff --git a/app/components/admin/budgets_wizard/budgets/edit_component.html.erb b/app/components/admin/budgets_wizard/budgets/edit_component.html.erb new file mode 100644 index 000000000..672f3e8ca --- /dev/null +++ b/app/components/admin/budgets_wizard/budgets/edit_component.html.erb @@ -0,0 +1,6 @@ +<%= back_link_to admin_budgets_path %> + +<%= header %> + +<%= render Admin::BudgetsWizard::CreationTimelineComponent.new("budget") %> +<%= render Admin::Budgets::FormComponent.new(budget, wizard: true) %> diff --git a/app/components/admin/budgets_wizard/budgets/edit_component.rb b/app/components/admin/budgets_wizard/budgets/edit_component.rb new file mode 100644 index 000000000..7b3d686c9 --- /dev/null +++ b/app/components/admin/budgets_wizard/budgets/edit_component.rb @@ -0,0 +1,12 @@ +class Admin::BudgetsWizard::Budgets::EditComponent < ApplicationComponent + include Header + attr_reader :budget + + def initialize(budget) + @budget = budget + end + + def title + t("admin.budgets.edit.title") + end +end diff --git a/app/components/admin/budgets_wizard/budgets/new_component.html.erb b/app/components/admin/budgets_wizard/budgets/new_component.html.erb index c035cbc78..672f3e8ca 100644 --- a/app/components/admin/budgets_wizard/budgets/new_component.html.erb +++ b/app/components/admin/budgets_wizard/budgets/new_component.html.erb @@ -3,4 +3,4 @@ <%= header %> <%= render Admin::BudgetsWizard::CreationTimelineComponent.new("budget") %> -<%= render Admin::Budgets::FormComponent.new(budget) %> +<%= render Admin::Budgets::FormComponent.new(budget, wizard: true) %> diff --git a/app/components/admin/budgets_wizard/groups/index_component.html.erb b/app/components/admin/budgets_wizard/groups/index_component.html.erb index a038a3f77..04a0059aa 100644 --- a/app/components/admin/budgets_wizard/groups/index_component.html.erb +++ b/app/components/admin/budgets_wizard/groups/index_component.html.erb @@ -1,4 +1,4 @@ -<%= back_link_to admin_budget_path(budget), t("admin.budget_groups.index.back") %> +<%= back_link_to edit_admin_budgets_wizard_budget_path(budget), t("admin.budgets_wizard.groups.back") %> <%= header %> diff --git a/app/controllers/admin/budgets_wizard/budgets_controller.rb b/app/controllers/admin/budgets_wizard/budgets_controller.rb index d55bab0cd..4100d951a 100644 --- a/app/controllers/admin/budgets_wizard/budgets_controller.rb +++ b/app/controllers/admin/budgets_wizard/budgets_controller.rb @@ -8,16 +8,27 @@ class Admin::BudgetsWizard::BudgetsController < Admin::BaseController def new end + def edit + end + def create @budget.published = false if @budget.save - redirect_to admin_budgets_wizard_budget_groups_path(@budget), notice: t("admin.budgets.create.notice") + redirect_to groups_index, notice: t("admin.budgets.create.notice") else render :new end end + def update + if @budget.update(budget_params) + redirect_to groups_index, notice: t("admin.budgets.update.notice") + else + render :edit + end + end + private def budget_params @@ -29,4 +40,8 @@ class Admin::BudgetsWizard::BudgetsController < Admin::BaseController valid_attributes + [translation_params(Budget)] end + + def groups_index + admin_budgets_wizard_budget_groups_path(@budget) + end end diff --git a/app/views/admin/budgets_wizard/budgets/edit.html.erb b/app/views/admin/budgets_wizard/budgets/edit.html.erb new file mode 100644 index 000000000..51f56237c --- /dev/null +++ b/app/views/admin/budgets_wizard/budgets/edit.html.erb @@ -0,0 +1 @@ +<%= render Admin::BudgetsWizard::Budgets::EditComponent.new(@budget) %> diff --git a/config/locales/en/admin.yml b/config/locales/en/admin.yml index 00cc77e7f..144cb6503 100644 --- a/config/locales/en/admin.yml +++ b/config/locales/en/admin.yml @@ -306,6 +306,7 @@ en: budgets: continue: "Continue to groups" groups: + back: "Go back to edit budget" continue: "Continue to headings" headings: continue: "Continue to phases" diff --git a/config/locales/es/admin.yml b/config/locales/es/admin.yml index 468a6d37d..e45e5bb13 100644 --- a/config/locales/es/admin.yml +++ b/config/locales/es/admin.yml @@ -306,6 +306,7 @@ es: budgets: continue: "Continuar a grupos" groups: + back: "Volver a editar presupuesto" continue: "Continuar a partidas" headings: continue: "Continuar a fases" diff --git a/config/routes/admin.rb b/config/routes/admin.rb index cbdaab6eb..8749178b1 100644 --- a/config/routes/admin.rb +++ b/config/routes/admin.rb @@ -73,7 +73,7 @@ namespace :admin do end namespace :budgets_wizard do - resources :budgets, only: [:create, :new] do + resources :budgets, only: [:create, :new, :edit, :update] do resources :groups, only: [:index, :create, :edit, :update, :destroy] do resources :headings, only: [:index, :create, :edit, :update, :destroy] end diff --git a/spec/system/admin/budgets_wizard/budgets_spec.rb b/spec/system/admin/budgets_wizard/budgets_spec.rb index d6a18651b..ac4722bad 100644 --- a/spec/system/admin/budgets_wizard/budgets_spec.rb +++ b/spec/system/admin/budgets_wizard/budgets_spec.rb @@ -11,7 +11,7 @@ describe "Budgets wizard, first step", :admin do expect(page).to have_content "New participatory budget created successfully!" - click_link "Go back to budgets" + click_link "Go back to edit budget" expect(page).to have_field "Name", with: "M30 - Summer campaign" expect(page).to have_select "Final voting style", selected: "Knapsack" @@ -29,7 +29,7 @@ describe "Budgets wizard, first step", :admin do expect(page).to have_content "New participatory budget created successfully!" - click_link "Go back to budgets" + click_link "Go back to edit budget" expect(page).to have_field "Name", with: "M30 - Summer campaign" expect(page).to have_select "Final voting style", selected: "Approval" @@ -81,11 +81,52 @@ describe "Budgets wizard, first step", :admin do expect(page).to have_content "New participatory budget created successfully!" - click_link "Go back to budgets" + within("#side_menu") { click_link "Participatory budgets" } + within("tr", text: "M30 - Summer campaign") { click_link "Edit budget" } expect(page).to have_content "This participatory budget is in draft mode" expect(page).to have_link "Preview budget" expect(page).to have_link "Publish budget" end end + + describe "Edit" do + scenario "update budget" do + budget = create(:budget, name: "Budget wiht typo") + + visit admin_budgets_wizard_budget_groups_path(budget) + + click_link "Go back to edit budget" + + expect(page).to have_content "Edit Participatory budget" + expect(page).to have_css ".creation-timeline" + expect(page).to have_field "Name", with: "Budget wiht typo" + + fill_in "Name", with: "Budget without typos" + click_button "Continue to groups" + + expect(page).to have_content "Participatory budget updated successfully" + expect(page).to have_content "Budget without typos" + expect(page).to have_css ".creation-timeline" + expect(page).to have_content "There are no groups" + end + + scenario "submit the form with errors and then without errors" do + budget = create(:budget, name: "Budget wiht typo") + + visit edit_admin_budgets_wizard_budget_path(budget) + fill_in "Name", with: "" + click_button "Continue to groups" + + expect(page).to have_css "#error_explanation" + + fill_in "Name", with: "Budget without typos" + click_button "Continue to groups" + + expect(page).to have_content "Participatory budget updated successfully" + expect(page).to have_content "Budget without typos" + expect(page).to have_css ".creation-timeline" + expect(page).to have_content "There are no groups" + end + end end