diff --git a/app/assets/stylesheets/admin.scss b/app/assets/stylesheets/admin.scss index 4f6d1f7a4..0771f0ee1 100644 --- a/app/assets/stylesheets/admin.scss +++ b/app/assets/stylesheets/admin.scss @@ -332,7 +332,7 @@ body.admin { } } -.account-info, .login-as { +.account-info, .login-as, .info { background-color: #e7e7e7; border-radius: rem-calc(3); font-size: rem-calc(16); @@ -344,3 +344,8 @@ body.admin { font-size: rem-calc(18); } } + +.info p { + margin-bottom: 0; +} + diff --git a/app/controllers/admin/spending_proposals_controller.rb b/app/controllers/admin/spending_proposals_controller.rb index 8956f7831..3ae4872db 100644 --- a/app/controllers/admin/spending_proposals_controller.rb +++ b/app/controllers/admin/spending_proposals_controller.rb @@ -11,19 +11,26 @@ class Admin::SpendingProposalsController < Admin::BaseController end def show + end + + def edit @admins = Administrator.includes(:user).all @valuators = Valuator.includes(:user).all.order("users.username ASC") + @tags = ActsAsTaggableOn::Tag.spending_proposal_tags end - def assign_admin - @spending_proposal.update(params.require(:spending_proposal).permit(:administrator_id)) - render nothing: true + def update + if @spending_proposal.update(spending_proposal_params) + redirect_to admin_spending_proposal_path(@spending_proposal, anchor: 'classification'), notice: t("flash.actions.update.spending_proposal") + else + render :edit + end end - def assign_valuators - params[:spending_proposal] ||= {} - params[:spending_proposal][:valuator_ids] ||= [] - @spending_proposal.update(params.require(:spending_proposal).permit(valuator_ids: [])) - end + private + + def spending_proposal_params + params.require(:spending_proposal).permit(:administrator_id, :tag_list, valuator_ids: []) + end end diff --git a/app/helpers/spending_proposals_helper.rb b/app/helpers/spending_proposals_helper.rb new file mode 100644 index 000000000..43ef7311a --- /dev/null +++ b/app/helpers/spending_proposals_helper.rb @@ -0,0 +1,6 @@ +module SpendingProposalsHelper + + def spending_proposal_tags_select_options + ActsAsTaggableOn::Tag.spending_proposal_tags.pluck(:name) + end +end \ No newline at end of file diff --git a/app/models/administrator.rb b/app/models/administrator.rb index 8a34fa349..6cfafe8d9 100644 --- a/app/models/administrator.rb +++ b/app/models/administrator.rb @@ -1,6 +1,6 @@ class Administrator < ActiveRecord::Base belongs_to :user, touch: true - delegate :name, :email, to: :user + delegate :name, :email, :name_and_email, to: :user validates :user_id, presence: true, uniqueness: true end diff --git a/app/models/spending_proposal.rb b/app/models/spending_proposal.rb index cceac00c1..27842fa91 100644 --- a/app/models/spending_proposal.rb +++ b/app/models/spending_proposal.rb @@ -1,6 +1,7 @@ class SpendingProposal < ActiveRecord::Base include Measurable include Sanitizable + include Taggable apply_simple_captcha @@ -18,13 +19,17 @@ class SpendingProposal < ActiveRecord::Base validates :description, length: { maximum: SpendingProposal.description_max_length } validates :terms_of_service, acceptance: { allow_nil: false }, on: :create - scope :valuation_open, -> { where(valuation_finished: false) } - scope :without_admin, -> { valuation_open.where(administrator_id: nil) } - scope :managed, -> { valuation_open.where(valuation_assignments_count: 0).where("administrator_id IS NOT ?", nil) } - scope :valuating, -> { valuation_open.where("valuation_assignments_count > 0 AND valuation_finished = ?", false) } - scope :valuation_finished, -> { where(valuation_finished: true) } + scope :valuation_open, -> { where(valuation_finished: false) } + scope :without_admin, -> { valuation_open.where(administrator_id: nil) } + scope :managed, -> { valuation_open.where(valuation_assignments_count: 0).where("administrator_id IS NOT ?", nil) } + scope :valuating, -> { valuation_open.where("valuation_assignments_count > 0 AND valuation_finished = ?", false) } + scope :valuation_finished, -> { where(valuation_finished: true) } - scope :for_render, -> { includes(:geozone, administrator: :user, valuators: :user) } + scope :by_admin, -> (admin) { where(administrator_id: admin.presence) } + scope :by_tag, -> (tag_name) { tagged_with(tag_name) } + scope :by_valuator, -> (valuator) { where("valuation_assignments.valuator_id = ?", valuator.presence).joins(:valuation_assignments) } + + scope :for_render, -> { includes(:geozone, administrator: :user, valuators: :user) } def description super.try :html_safe @@ -33,7 +38,8 @@ class SpendingProposal < ActiveRecord::Base def self.search(params, current_filter) results = self results = results.by_geozone(params[:geozone_id]) if params[:geozone_id].present? - results = results.by_administrator(params[:administrator_id]) if params[:administrator_id].present? + results = results.by_admin(params[:administrator_id]) if params[:administrator_id].present? + results = results.by_tag(params[:tag_name]) if params[:tag_name].present? results = results.by_valuator(params[:valuator_id]) if params[:valuator_id].present? results = results.send(current_filter) if current_filter.present? results.for_render @@ -47,14 +53,6 @@ class SpendingProposal < ActiveRecord::Base end end - def self.by_administrator(administrator) - where(administrator_id: administrator.presence) - end - - def self.by_valuator(valuator) - joins(:valuation_assignments).includes(:valuators).where("valuation_assignments.valuator_id = ?", valuator.presence) - end - def feasibility case feasible when true diff --git a/app/models/user.rb b/app/models/user.rb index 1316c1348..3bf54b3ca 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -204,6 +204,10 @@ class User < ActiveRecord::Base self.update(oauth_email: nil) if oauth_email.present? end + def name_and_email + "#{name} (#{email})" + end + def save_requiring_finish_signup self.update(registering_with_oauth: true) end diff --git a/app/models/valuator.rb b/app/models/valuator.rb index 9e18d2cca..ab0ea6ece 100644 --- a/app/models/valuator.rb +++ b/app/models/valuator.rb @@ -1,6 +1,6 @@ class Valuator < ActiveRecord::Base belongs_to :user, touch: true - delegate :name, :email, to: :user + delegate :name, :email, :name_and_email, to: :user has_many :valuation_assignments, dependent: :destroy has_many :spending_proposals, through: :valuation_assignments diff --git a/app/views/admin/spending_proposals/_written_by_author.html.erb b/app/views/admin/spending_proposals/_written_by_author.html.erb new file mode 100644 index 000000000..a8eb9e758 --- /dev/null +++ b/app/views/admin/spending_proposals/_written_by_author.html.erb @@ -0,0 +1,42 @@ +
+ <%= t "admin.spending_proposals.show.heading", id: @spending_proposal.id %> +
+ +
+

<%= @spending_proposal.title %>

+ +
+
+

+ <%= t("admin.spending_proposals.show.geozone") %>: + <%= geozone_name(@spending_proposal) %> +

+
+ +
+

+ <%= t("admin.spending_proposals.show.by") %>: + <%= link_to @spending_proposal.author.name, admin_user_path(@spending_proposal.author) %> +

+
+ +
+

+ <%= t("admin.spending_proposals.show.sent") %>: + <%= l @spending_proposal.created_at, format: :datetime %> +

+
+ +
+ +<% if @spending_proposal.association_name.present? %> +

<%= t("admin.spending_proposals.show.association_name") %>: + <%= @spending_proposal.association_name %> +

+<% end %> + +<% if @spending_proposal.external_url.present? %> +

<%= text_with_links @spending_proposal.external_url %> 

+<% end %> + +<%= safe_html_with_links @spending_proposal.description %> diff --git a/app/views/admin/spending_proposals/assign_valuators.js.coffee b/app/views/admin/spending_proposals/assign_valuators.js.coffee deleted file mode 100644 index afe581e59..000000000 --- a/app/views/admin/spending_proposals/assign_valuators.js.coffee +++ /dev/null @@ -1 +0,0 @@ -$('#assigned_valuators').html("<%= j(render 'assigned_valuators') %>") \ No newline at end of file diff --git a/app/views/admin/spending_proposals/edit.html.erb b/app/views/admin/spending_proposals/edit.html.erb new file mode 100644 index 000000000..10b99acce --- /dev/null +++ b/app/views/admin/spending_proposals/edit.html.erb @@ -0,0 +1,40 @@ +<%= link_to admin_spending_proposals_path(@spending_proposal), class: 'back' do %> + <%= t("admin.spending_proposals.show.back") %> +<% end %> + +<%= render 'written_by_author' %> + +

<%= t("admin.spending_proposals.edit.classification") %>

+ +<%= form_for @spending_proposal, url: admin_spending_proposal_path(@spending_proposal) do |f| %> + + <%= f.select(:administrator_id, + @admins.collect{ |a| [a.name_and_email, a.id ] }, + { include_blank: t("admin.spending_proposals.edit.undefined") }, + class: "small-12 medium-6") %> + + <%= f.label :valuator_ids, t("admin.spending_proposals.edit.assigned_valuators") %> + <%= f.collection_check_boxes :valuator_ids, @valuators, :id, :name_and_email do |b| %> + <%= b.label { b.check_box + b.text } %> + <% end %> + + <%= f.label :tag_list, t("admin.spending_proposals.edit.tags") %> +
+ <% @tags.each do |tag| %> + <%= tag.name %> + <% end %> +
+ + <%= f.text_field :tag_list, value: @spending_proposal.tag_list.to_s, + label: false, + placeholder: t("admin.spending_proposals.edit.tags_placeholder"), + class: 'js-tag-list' %> + +

+ <%= f.submit(class: "button", value: t("admin.spending_proposals.edit.submit_button")) %> +

+ +<% end %> + +
+<%= render 'valuation/spending_proposals/written_by_valuators' %> \ No newline at end of file diff --git a/app/views/admin/spending_proposals/index.html.erb b/app/views/admin/spending_proposals/index.html.erb index 7c8bca37f..83903cc2e 100644 --- a/app/views/admin/spending_proposals/index.html.erb +++ b/app/views/admin/spending_proposals/index.html.erb @@ -1,20 +1,27 @@

<%= t("admin.spending_proposals.index.title") %>

-
+
<%= form_tag admin_spending_proposals_path, method: :get, enforce_utf8: false do %> -
+
+ <%= select_tag :administrator_id, + options_for_select(admin_select_options, params[:administrator_id]), + { prompt: t("admin.spending_proposals.index.administrator_filter_all"), + label: false, + class: "js-submit-on-change" } %> +
+
<%= select_tag :geozone_id, options_for_select(geozone_select_options.unshift([t("geozones.none"), "all"]), params[:geozone_id]), { prompt: t("admin.spending_proposals.index.geozone_filter_all"), label: false, class: "js-submit-on-change" } %>
-
- <%= select_tag :administrator_id, - options_for_select(admin_select_options, params[:administrator_id]), - { prompt: t("admin.spending_proposals.index.administrator_filter_all"), - label: false, - class: "js-submit-on-change" } %> +
+ <%= select_tag :tag_name, + options_for_select(spending_proposal_tags_select_options, params[:tag_name]), + { prompt: t("admin.spending_proposals.index.tags_filter_all"), + label: false, + class: "js-submit-on-change" } %>
<% end %>
@@ -25,7 +32,7 @@ <% @spending_proposals.each do |spending_proposal| %> - + @@ -34,23 +41,16 @@
<%= spending_proposal.id %> <% if spending_proposal.administrator.present? %> - <%= spending_proposal.administrator.name %> + <%= spending_proposal.administrator.name %> <% else %> <%= t("admin.spending_proposals.index.no_admin_assigned") %> <% end %> - <% case spending_proposal.valuators.size %> - <% when 0 %> + <% if spending_proposal.valuators.size == 0 %> <%= t("admin.spending_proposals.index.no_valuators_assigned") %> - <% when 1 %> - - <%= spending_proposal.valuators.first.name %> - <% else %> - - <%= t('admin.spending_proposals.index.valuators_assigned', count: spending_proposal.valuators.size) %> - + <%= spending_proposal.valuators.collect(&:name).join(', ') %> <% end %> diff --git a/app/views/admin/spending_proposals/show.html.erb b/app/views/admin/spending_proposals/show.html.erb index 6be63d9ff..304e6f792 100644 --- a/app/views/admin/spending_proposals/show.html.erb +++ b/app/views/admin/spending_proposals/show.html.erb @@ -1,83 +1,35 @@ -<%= link_to t("admin.spending_proposals.show.back"), :back, class: 'back' %> -

<%= t("admin.spending_proposals.show.heading") %> <%= @spending_proposal.id %>

-

<%= @spending_proposal.title %>

- -<%= safe_html_with_links @spending_proposal.description %> - -<% if @spending_proposal.external_url.present? %> -

<%= text_with_links @spending_proposal.external_url %>

+<%= link_to admin_spending_proposals_path, class: 'back' do %> + <%= t("admin.spending_proposals.show.back") %> <% end %> -

<%= t("admin.spending_proposals.show.info") %>

+<%= render 'written_by_author' %> -

<%= t("admin.spending_proposals.show.by") %>: - <%= link_to @spending_proposal.author.name, admin_user_path(@spending_proposal.author) %> -

- -<% if @spending_proposal.association_name.present? %> -

<%= t("admin.spending_proposals.show.association_name") %>: - <%= @spending_proposal.association_name %> -

-<% end %> - -

<%= t("admin.spending_proposals.show.geozone") %>: - <%= geozone_name(@spending_proposal) %> -

- -

<%= t("admin.spending_proposals.show.sent") %>: - <%= l @spending_proposal.created_at, format: :datetime %> -

- -

<%= t("admin.spending_proposals.show.responsibles") %>

+

<%= t("admin.spending_proposals.show.classification") %>

<%= t("admin.spending_proposals.show.assigned_admin") %>: - <%= form_for(@spending_proposal, url: assign_admin_admin_spending_proposal_path(@spending_proposal), remote: true, html: {id: 'administrator_assignment_form'}) do |f| %> - <%= f.select :administrator_id, @admins.collect { |a| [ "#{a.name} (#{a.email})", a.id ] }, {include_blank: t("admin.spending_proposals.show.undefined"), label: false}, class: "js-submit-on-change" %> + <%= @spending_proposal.administrator.try(:name_and_email) || t("admin.spending_proposals.show.undefined") %> +

+ +

+ <%= t("admin.spending_proposals.show.tags") %>: + + <%= @spending_proposal.tags.pluck(:name).join(', ') %> +

+ +

+ <%= t("admin.spending_proposals.show.assigned_valuators") %>: + <% if @spending_proposal.valuators.any? %> + <%= @spending_proposal.valuators.collect(&:name_and_email).join(', ') %> + <% else %> + <%= t("admin.spending_proposals.show.undefined") %> <% end %>

-

<%= t("admin.spending_proposals.show.assigned_valuators") %>:

-
- <%= render "assigned_valuators" %> -
+<%= link_to t("admin.spending_proposals.show.edit_button"), + edit_admin_spending_proposal_path(@spending_proposal, anchor: "form"), + class: "button" %> -<%= link_to t("admin.spending_proposals.show.assign_valuators"), "", class: "js-toggle-link", data: {"toggle-selector" => "#valuators-assign-list"} %> - - +

<%= t("admin.spending_proposals.show.dossier") %>

- -

<%= t("admin.spending_proposals.show.price") %> (<%= t("admin.spending_proposals.show.currency") %>): - <%= @spending_proposal.price.present? ? @spending_proposal.price : t("admin.spending_proposals.show.undefined") %> -

-

<%= t("admin.spending_proposals.show.price_first_year") %> (<%= t("admin.spending_proposals.show.currency") %>): - <%= @spending_proposal.price_first_year.present? ? @spending_proposal.price_first_year : t("admin.spending_proposals.show.undefined") %> -

- -<%= simple_format(safe_html_with_links(@spending_proposal.price_explanation.html_safe), {}, sanitize: false) if @spending_proposal.price_explanation.present? %> - -

<%= t("admin.spending_proposals.show.feasibility") %>: - <%= t("admin.spending_proposals.show.#{@spending_proposal.feasibility}") %> -

-<%= simple_format(safe_html_with_links(@spending_proposal.feasible_explanation.html_safe), {}, sanitize: false) if @spending_proposal.feasible_explanation.present? %> - -<% if @spending_proposal.valuation_finished %> -

<%= t("admin.spending_proposals.show.valuation_finished") %> -<% end %> - -<% if @spending_proposal.internal_comments.present? %> -

<%= t("admin.spending_proposals.show.internal_comments") %>

- <%= simple_format(safe_html_with_links(@spending_proposal.internal_comments.html_safe), {}, sanitize: false) %> -<% end %> \ No newline at end of file +<%= render 'valuation/spending_proposals/written_by_valuators' %> diff --git a/app/views/shared/_tags.html.erb b/app/views/shared/_tags.html.erb index 8640a4d0c..a161a8228 100644 --- a/app/views/shared/_tags.html.erb +++ b/app/views/shared/_tags.html.erb @@ -3,12 +3,12 @@ <% if taggable.tags.any? %> <% taggable.tag_list_with_limit(limit).each do |tag| %> - <%= link_to sanitize(tag.name), send("#{taggable.class.to_s.downcase.pluralize}_path", tag: tag.name) %> + <%= link_to sanitize(tag.name), send("#{taggable.class.name.underscore.pluralize}_path", tag: tag.name) %> <% end %> <% if taggable.tags_count_out_of_limit(limit) > 0 %> <%= link_to "#{taggable.tags_count_out_of_limit(limit)}+", - send("#{taggable.class.to_s.downcase}_path", taggable) %> + send("#{taggable.class.name.underscore}_path", taggable) %> <% end %> <% end %> diff --git a/app/views/valuation/spending_proposals/_written_by_valuators.html.erb b/app/views/valuation/spending_proposals/_written_by_valuators.html.erb new file mode 100644 index 000000000..8b704950e --- /dev/null +++ b/app/views/valuation/spending_proposals/_written_by_valuators.html.erb @@ -0,0 +1,53 @@ +

+ + <%= t("valuation.spending_proposals.show.price") %> + (<%= t("valuation.spending_proposals.show.currency") %>): + + <% if @spending_proposal.price.present? %> + <%= @spending_proposal.price %> + <% else %> + <%= t("valuation.spending_proposals.show.undefined") %> + <% end %> +

+ +

+ + <%= t("valuation.spending_proposals.show.price_first_year") %> + (<%= t("valuation.spending_proposals.show.currency") %>): + + + <% if @spending_proposal.price_first_year.present? %> + <%= @spending_proposal.price_first_year %> + <% else %> + <%= t("valuation.spending_proposals.show.undefined") %> + <% end %> +

+ +<%= explanation_field @spending_proposal.price_explanation %> + +

+ <%= t("valuation.spending_proposals.show.time_scope") %>: + <% if @spending_proposal.time_scope.present? %> + <%= @spending_proposal.time_scope %> + <% else %> + <%= t("valuation.spending_proposals.show.undefined") %> + <% end %> +

+ +

+ <%= t("valuation.spending_proposals.show.feasibility") %>: + <%= t("valuation.spending_proposals.show.#{@spending_proposal.feasibility}") %> +

+ +<%= explanation_field @spending_proposal.feasible_explanation %> + +<% if @spending_proposal.valuation_finished %> +

+ <%= t("valuation.spending_proposals.show.valuation_finished") %> +

+<% end %> + +<% if @spending_proposal.internal_comments.present? %> +

<%= t("valuation.spending_proposals.show.internal_comments") %>

+ <%= explanation_field @spending_proposal.internal_comments %> +<% end %> diff --git a/app/views/valuation/spending_proposals/show.html.erb b/app/views/valuation/spending_proposals/show.html.erb index 16f83dbe4..b5b71fcdf 100644 --- a/app/views/valuation/spending_proposals/show.html.erb +++ b/app/views/valuation/spending_proposals/show.html.erb @@ -32,7 +32,7 @@

<%= t("valuation.spending_proposals.show.assigned_admin") %>: <% if @spending_proposal.administrator.present? %> - <%= @spending_proposal.administrator.name %> (<%= @spending_proposal.administrator.email %>) + <%= @spending_proposal.administrator.name_and_email %> <% else %> <%= t("valuation.spending_proposals.show.undefined") %> <% end %> @@ -42,7 +42,7 @@

    <% @spending_proposal.valuators.each do |valuator| %> -
  • <%= valuator.name %> (<%= valuator.email %>)
  • +
  • <%= valuator.name_and_email %>
  • <% end %> <% if @spending_proposal.valuators.empty? %> @@ -57,36 +57,4 @@ <%= link_to t("valuation.spending_proposals.show.edit_dossier"), edit_valuation_spending_proposal_path(@spending_proposal) %>

    -

    - <%= t("valuation.spending_proposals.show.price") %> (<%= t("valuation.spending_proposals.show.currency") %>): - <%= @spending_proposal.price.presence or t("valuation.spending_proposals.show.undefined") %> -

    -

    - <%= t("valuation.spending_proposals.show.price_first_year") %> (<%= t("valuation.spending_proposals.show.currency") %>): - <%= @spending_proposal.price_first_year.presence or t("valuation.spending_proposals.show.undefined") %> -

    - -<%= explanation_field @spending_proposal.price_explanation %> - -

    - <%= t("valuation.spending_proposals.show.time_scope") %>: - <%= @spending_proposal.time_scope.presence or t("valuation.spending_proposals.show.undefined") %> -

    - -

    - <%= t("valuation.spending_proposals.show.feasibility") %>: - <%= t("valuation.spending_proposals.show.#{@spending_proposal.feasibility}") %> -

    - -<%= explanation_field @spending_proposal.feasible_explanation %> - -<% if @spending_proposal.valuation_finished %> -

    - <%= t("valuation.spending_proposals.show.valuation_finished") %> -

    -<% end %> - -<% if @spending_proposal.internal_comments.present? %> -

    <%= t("valuation.spending_proposals.show.internal_comments") %>

    - <%= simple_format_no_tags_no_sanitize(safe_html_with_links(@spending_proposal.internal_comments.html_safe)) %> -<% end %> +<%= render 'written_by_valuators' %> \ No newline at end of file diff --git a/config/initializers/acts_as_taggable_on.rb b/config/initializers/acts_as_taggable_on.rb index 40a0a5949..7b534897a 100644 --- a/config/initializers/acts_as_taggable_on.rb +++ b/config/initializers/acts_as_taggable_on.rb @@ -38,6 +38,10 @@ module ActsAsTaggableOn Tag.where("kind = 'category'").pluck(:name) end + def self.spending_proposal_tags + ActsAsTaggableOn::Tag.where('taggings.taggable_type' => 'SpendingProposal').includes(:taggings).order(:name).uniq + end + private def custom_counter_field_name_for(taggable_type) "#{taggable_type.underscore.pluralize}_count" diff --git a/config/locales/activerecord.en.yml b/config/locales/activerecord.en.yml index 9451a2875..faebcb449 100644 --- a/config/locales/activerecord.en.yml +++ b/config/locales/activerecord.en.yml @@ -77,3 +77,5 @@ en: attributes: tag_list: less_than_or_equal_to: "tags must be less than or equal to %{count}" + spending_proposal: + administrator_id: "Administrator" diff --git a/config/locales/activerecord.es.yml b/config/locales/activerecord.es.yml index c18288bdc..50dd53006 100644 --- a/config/locales/activerecord.es.yml +++ b/config/locales/activerecord.es.yml @@ -67,6 +67,8 @@ es: organization: name: "Nombre de organización" responsible_name: "Persona responsable del colectivo" + spending_proposal: + administrator_id: "Administrador" errors: models: debate: diff --git a/config/locales/admin.en.yml b/config/locales/admin.en.yml index 07f236456..3a8d1bf84 100755 --- a/config/locales/admin.en.yml +++ b/config/locales/admin.en.yml @@ -150,6 +150,7 @@ en: index: geozone_filter_all: All zones administrator_filter_all: All administrators + tags_filter_all: All tags filters: valuation_open: Open without_admin: Without assigned admin @@ -157,36 +158,30 @@ en: valuating: Under valuation valuation_finished: Valuation finished title: Investment projects for participatory budgeting - admin_assigned: Assigned administrator + assigned_admin: Assigned administrator no_admin_assigned: No admin assigned - valuators_assigned: - one: Assigned valuator - other: "%{count} valuators assigned" no_valuators_assigned: No valuators assigned show: + assigned_admin: Assigned administrator + assigned_valuators: Assigned valuators back: Back - heading: Investment project - info: Author info - association_name: Asociación - by: Sent by - sent: Sent at + classification: Clasification + heading: "Investment project %{id}" + edit_button: Edit + association_name: Association + by: By + sent: Sent geozone: Scope dossier: Dossier - price: Price - price_first_year: Cost during the first year - currency: "€" - feasibility: Feasibility - feasible: Feasible - not_feasible: Not feasible + tags: Tags + undefined: Undefined + edit: + classification: Clasification + assigned_valuators: Valuators + submit_button: Update + tags: Tags + tags_placeholder: "Write the tags you want separated by commas (,)" undefined: Undefined - valuation_finished: Valuation finished - internal_comments: Internal comments - responsibles: Responsibles - assigned_admin: Assigned admin - assigned_valuators: Assigned valuators - assign_valuators: Assign valuators - no_valuators: There is not any valuator user - assign: Assign stats: show: stats_title: Stats diff --git a/config/locales/admin.es.yml b/config/locales/admin.es.yml index 442294de0..81af24d36 100644 --- a/config/locales/admin.es.yml +++ b/config/locales/admin.es.yml @@ -150,6 +150,7 @@ es: index: geozone_filter_all: Todos los ámbitos de actuación administrator_filter_all: Todos los administradores + tags_filter_all: Todas las etiquetas filters: valuation_open: Abiertas without_admin: Sin administrador asignado @@ -157,36 +158,30 @@ es: valuating: En evaluación valuation_finished: Evaluación finalizada title: Propuestas de inversión para presupuestos participativos - admin_assigned: Administrador asignado + assigned_admin: Administrador asignado no_admin_assigned: Sin admin asignado - valuators_assigned: - one: Evaluador asignado - other: "%{count} evaluadores asignados" no_valuators_assigned: Sin evaluador show: - back: Volver - heading: Propuesta de inversión - info: Datos de envío - association_name: Asociación - by: Enviada por - sent: Fecha de creación - geozone: Ámbito - dossier: Informe - price: Coste - price_first_year: Coste en el primer año - currency: "€" - feasibility: Viabilidad - feasible: Viable - not_feasible: No viable - undefined: Sin definir - valuation_finished: Informe finalizado - internal_comments: Commentarios internos - responsibles: Responsables assigned_admin: Administrador asignado assigned_valuators: Evaluadores asignados - assign_valuators: Asignar evaluadores - no_valuators: No hay ningún usuario evaluador - assign: Asignar + back: Volver + classification: Clasificación + heading: Propuesta de inversión %{id} + edit_button: Editar + association_name: Asociación + by: Autor + sent: Fecha + geozone: Ámbito + dossier: Informe + tags: Etiquetas + undefined: Sin definir + edit: + classification: Clasificación + assigned_valuators: Evaluadores + submit_button: Actualizar + tags: Etiquetas + tags_placeholder: "Escribe las etiquetas que desees separadas por comas (,)" + undefined: Sin definir stats: show: stats_title: Estadísticas diff --git a/config/locales/responders.en.yml b/config/locales/responders.en.yml index b2640c6ac..03a8f0ec8 100755 --- a/config/locales/responders.en.yml +++ b/config/locales/responders.en.yml @@ -12,4 +12,5 @@ en: update: notice: "%{resource_name} updated successfully." debate: "Debate updated successfully." - proposal: "Proposal updated successfully." \ No newline at end of file + proposal: "Proposal updated successfully." + spending_proposal: "Investment project updated succesfully." \ No newline at end of file diff --git a/config/locales/responders.es.yml b/config/locales/responders.es.yml index 5c22856cc..ee6e011f8 100644 --- a/config/locales/responders.es.yml +++ b/config/locales/responders.es.yml @@ -12,4 +12,5 @@ es: update: notice: "%{resource_name} actualizado correctamente." debate: "Debate actualizado correctamente." - proposal: "Propuesta actualizada correctamente." \ No newline at end of file + proposal: "Propuesta actualizada correctamente." + spending_proposal: "Propuesta de inversión actualizada correctamente." \ No newline at end of file diff --git a/config/routes.rb b/config/routes.rb index 77491bb5e..f150db339 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -130,7 +130,7 @@ Rails.application.routes.draw do end end - resources :spending_proposals, only: [:index, :show] do + resources :spending_proposals, only: [:index, :show, :edit, :update] do member do patch :assign_admin patch :assign_valuators diff --git a/spec/features/admin/spending_proposals_spec.rb b/spec/features/admin/spending_proposals_spec.rb index da17c3f35..776f55d45 100644 --- a/spec/features/admin/spending_proposals_spec.rb +++ b/spec/features/admin/spending_proposals_spec.rb @@ -7,159 +7,188 @@ feature 'Admin spending proposals' do login_as(admin.user) end - scenario 'Disabled with a feature flag' do - Setting['feature.spending_proposals'] = nil - expect{ visit admin_spending_proposals_path }.to raise_exception(FeatureFlags::FeatureDisabled) - end + context "Feature flag" do - scenario 'Index shows spending proposals' do - spending_proposal = create(:spending_proposal) - visit admin_spending_proposals_path - - expect(page).to have_content(spending_proposal.title) - end - - scenario 'Index shows assignments info' do - spending_proposal1 = create(:spending_proposal) - spending_proposal2 = create(:spending_proposal) - spending_proposal3 = create(:spending_proposal) - - valuator1 = create(:valuator, user: create(:user, username: 'Olga')) - valuator2 = create(:valuator, user: create(:user, username: 'Miriam')) - admin = create(:administrator, user: create(:user, username: 'Gema')) - - spending_proposal1.valuators << valuator1 - spending_proposal2.valuator_ids = [valuator1.id, valuator2.id] - spending_proposal3.update({administrator_id: admin.id}) - - visit admin_spending_proposals_path - - within("#spending_proposal_#{spending_proposal1.id}") do - expect(page).to have_content("No admin assigned") - expect(page).to have_content("Olga") + scenario 'Disabled with a feature flag' do + Setting['feature.spending_proposals'] = nil + expect{ visit admin_spending_proposals_path }.to raise_exception(FeatureFlags::FeatureDisabled) end - within("#spending_proposal_#{spending_proposal2.id}") do - expect(page).to have_content("No admin assigned") - expect(page).to have_content("2 valuators assigned") + end + + context "Index" do + + scenario 'Displaying spending proposals' do + spending_proposal = create(:spending_proposal) + visit admin_spending_proposals_path + + expect(page).to have_content(spending_proposal.title) end - within("#spending_proposal_#{spending_proposal3.id}") do - expect(page).to have_content("Gema") - expect(page).to have_content("No valuators assigned") - end - end + scenario 'Displaying assignments info' do + spending_proposal1 = create(:spending_proposal) + spending_proposal2 = create(:spending_proposal) + spending_proposal3 = create(:spending_proposal) - scenario "Index filtering by geozone", :js do - geozone = create(:geozone, name: "District 9") - create(:spending_proposal, title: "Realocate visitors", geozone: geozone) - create(:spending_proposal, title: "Destroy the city") + valuator1 = create(:valuator, user: create(:user, username: 'Olga')) + valuator2 = create(:valuator, user: create(:user, username: 'Miriam')) + admin = create(:administrator, user: create(:user, username: 'Gema')) - visit admin_spending_proposals_path - expect(page).to have_link("Realocate visitors") - expect(page).to have_link("Destroy the city") + spending_proposal1.valuators << valuator1 + spending_proposal2.valuator_ids = [valuator1.id, valuator2.id] + spending_proposal3.update({administrator_id: admin.id}) - select "District 9", from: "geozone_id" + visit admin_spending_proposals_path - expect(page).to have_link("Realocate visitors") - expect(page).to_not have_link("Destroy the city") + within("#spending_proposal_#{spending_proposal1.id}") do + expect(page).to have_content("No admin assigned") + expect(page).to have_content("Olga") + end - select "All city", from: "geozone_id" + within("#spending_proposal_#{spending_proposal2.id}") do + expect(page).to have_content("No admin assigned") + expect(page).to have_content("Olga") + expect(page).to have_content("Miriam") + end - expect(page).to have_link("Destroy the city") - expect(page).to_not have_link("Realocate visitors") - - select "All zones", from: "geozone_id" - expect(page).to have_link("Realocate visitors") - expect(page).to have_link("Destroy the city") - end - - scenario "Index filtering by admin", :js do - user = create(:user, username: 'Admin 1') - administrator = create(:administrator, user: user) - - create(:spending_proposal, title: "Realocate visitors", administrator: administrator) - create(:spending_proposal, title: "Destroy the city") - - visit admin_spending_proposals_path - expect(page).to have_link("Realocate visitors") - expect(page).to have_link("Destroy the city") - - select "Admin 1", from: "administrator_id" - - expect(page).to have_link("Realocate visitors") - expect(page).to_not have_link("Destroy the city") - - select "All administrators", from: "administrator_id" - - expect(page).to have_link("Destroy the city") - expect(page).to have_link("Realocate visitors") - end - - scenario "Current filter is properly highlighted" do - filters_links = {'valuation_open' => 'Open', - 'without_admin' => 'Without assigned admin', - 'managed' => 'Managed', - 'valuating' => 'Under valuation', - 'valuation_finished' => 'Valuation finished'} - - visit admin_spending_proposals_path - - expect(page).to_not have_link(filters_links.values.first) - filters_links.keys.drop(1).each { |filter| expect(page).to have_link(filters_links[filter]) } - - filters_links.each_pair do |current_filter, link| - visit admin_spending_proposals_path(filter: current_filter) - - expect(page).to_not have_link(link) - - (filters_links.keys - [current_filter]).each do |filter| - expect(page).to have_link(filters_links[filter]) + within("#spending_proposal_#{spending_proposal3.id}") do + expect(page).to have_content("Gema") + expect(page).to have_content("No valuators assigned") end end - end - scenario "Index filtering by assignment status" do - assigned = create(:spending_proposal, title: "Assigned idea", administrator: create(:administrator)) - valuating = create(:spending_proposal, title: "Evaluating...") - valuating.valuators << create(:valuator) + scenario "Filtering by geozone", :js do + geozone = create(:geozone, name: "District 9") + create(:spending_proposal, title: "Realocate visitors", geozone: geozone) + create(:spending_proposal, title: "Destroy the city") - visit admin_spending_proposals_path(filter: 'valuation_open') + visit admin_spending_proposals_path + expect(page).to have_link("Realocate visitors") + expect(page).to have_link("Destroy the city") - expect(page).to have_content("Assigned idea") - expect(page).to have_content("Evaluating...") + select "District 9", from: "geozone_id" - visit admin_spending_proposals_path(filter: 'without_admin') + expect(page).to have_link("Realocate visitors") + expect(page).to_not have_link("Destroy the city") - expect(page).to have_content("Evaluating...") - expect(page).to_not have_content("Assigned idea") + select "All city", from: "geozone_id" - visit admin_spending_proposals_path(filter: 'managed') + expect(page).to have_link("Destroy the city") + expect(page).to_not have_link("Realocate visitors") - expect(page).to have_content("Assigned idea") - expect(page).to_not have_content("Evaluating...") - end + select "All zones", from: "geozone_id" + expect(page).to have_link("Realocate visitors") + expect(page).to have_link("Destroy the city") + end - scenario "Index filtering by valuation status" do - valuating = create(:spending_proposal, title: "Ongoing valuation") - valuated = create(:spending_proposal, title: "Old idea", valuation_finished: true) - valuating.valuators << create(:valuator) - valuated.valuators << create(:valuator) + scenario "Filtering by admin", :js do + user = create(:user, username: 'Admin 1') + administrator = create(:administrator, user: user) - visit admin_spending_proposals_path(filter: 'valuation_open') + create(:spending_proposal, title: "Realocate visitors", administrator: administrator) + create(:spending_proposal, title: "Destroy the city") - expect(page).to have_content("Ongoing valuation") - expect(page).to_not have_content("Old idea") + visit admin_spending_proposals_path + expect(page).to have_link("Realocate visitors") + expect(page).to have_link("Destroy the city") - visit admin_spending_proposals_path(filter: 'valuating') + select "Admin 1", from: "administrator_id" - expect(page).to have_content("Ongoing valuation") - expect(page).to_not have_content("Old idea") + expect(page).to have_link("Realocate visitors") + expect(page).to_not have_link("Destroy the city") - visit admin_spending_proposals_path(filter: 'valuation_finished') + select "All administrators", from: "administrator_id" + + expect(page).to have_link("Destroy the city") + expect(page).to have_link("Realocate visitors") + end + + scenario "Current filter is properly highlighted" do + filters_links = {'valuation_open' => 'Open', + 'without_admin' => 'Without assigned admin', + 'managed' => 'Managed', + 'valuating' => 'Under valuation', + 'valuation_finished' => 'Valuation finished'} + + visit admin_spending_proposals_path + + expect(page).to_not have_link(filters_links.values.first) + filters_links.keys.drop(1).each { |filter| expect(page).to have_link(filters_links[filter]) } + + filters_links.each_pair do |current_filter, link| + visit admin_spending_proposals_path(filter: current_filter) + + expect(page).to_not have_link(link) + + (filters_links.keys - [current_filter]).each do |filter| + expect(page).to have_link(filters_links[filter]) + end + end + end + + scenario "Filtering by assignment status" do + assigned = create(:spending_proposal, title: "Assigned idea", administrator: create(:administrator)) + valuating = create(:spending_proposal, title: "Evaluating...") + valuating.valuators << create(:valuator) + + visit admin_spending_proposals_path(filter: 'valuation_open') + + expect(page).to have_content("Assigned idea") + expect(page).to have_content("Evaluating...") + + visit admin_spending_proposals_path(filter: 'without_admin') + + expect(page).to have_content("Evaluating...") + expect(page).to_not have_content("Assigned idea") + + visit admin_spending_proposals_path(filter: 'managed') + + expect(page).to have_content("Assigned idea") + expect(page).to_not have_content("Evaluating...") + end + + scenario "Filtering by valuation status" do + valuating = create(:spending_proposal, title: "Ongoing valuation") + valuated = create(:spending_proposal, title: "Old idea", valuation_finished: true) + valuating.valuators << create(:valuator) + valuated.valuators << create(:valuator) + + visit admin_spending_proposals_path(filter: 'valuation_open') + + expect(page).to have_content("Ongoing valuation") + expect(page).to_not have_content("Old idea") + + visit admin_spending_proposals_path(filter: 'valuating') + + expect(page).to have_content("Ongoing valuation") + expect(page).to_not have_content("Old idea") + + visit admin_spending_proposals_path(filter: 'valuation_finished') + + expect(page).to_not have_content("Ongoing valuation") + expect(page).to have_content("Old idea") + end + + scenario "Filtering by tag" do + create(:spending_proposal, title: 'Educate the children', tag_list: 'Education') + create(:spending_proposal, title: 'More schools', tag_list: 'Education') + create(:spending_proposal, title: 'More hospitals', tag_list: 'Health') + + visit admin_spending_proposals_path + + expect(page).to have_css(".spending_proposal", count: 3) + expect(page).to have_content("Educate the children") + expect(page).to have_content("More schools") + expect(page).to have_content("More hospitals") + + visit admin_spending_proposals_path(tag_name: 'Education') + + expect(page).to have_css(".spending_proposal", count: 2) + expect(page).to have_content("Educate the children") + expect(page).to have_content("More schools") + expect(page).to_not have_content("More hospitals") + end - expect(page).to_not have_content("Ongoing valuation") - expect(page).to have_content("Old idea") end scenario 'Show' do @@ -188,69 +217,90 @@ feature 'Admin spending proposals' do expect(page).to have_content('1000') expect(page).to have_content('Not feasible') expect(page).to have_content('It is impossible') - expect(page).to have_select('spending_proposal[administrator_id]', selected: 'Ana (ana@admins.org)') + expect(page).to have_content('Ana (ana@admins.org)') within('#assigned_valuators') do expect(page).to have_content('Rachel (rachel@valuators.org)') end end - scenario 'Administrator assigment', :js do - spending_proposal = create(:spending_proposal) + context "Edit" do - administrator = create(:administrator, user: create(:user, username: 'Ana', email: 'ana@admins.org')) + scenario "Add administrator" do + spending_proposal = create(:spending_proposal) + administrator = create(:administrator, user: create(:user, username: 'Marta', email: 'marta@admins.org')) - visit admin_spending_proposal_path(spending_proposal) + visit admin_spending_proposal_path(spending_proposal) + click_link 'Edit' - expect(page).to have_select('spending_proposal[administrator_id]', selected: 'Undefined') - select 'Ana (ana@admins.org)', from: 'spending_proposal[administrator_id]' + select 'Marta (marta@admins.org)', from: 'spending_proposal[administrator_id]' + click_button 'Update' - visit admin_spending_proposals_path - click_link spending_proposal.title - - expect(page).to have_select('spending_proposal[administrator_id]', selected: 'Ana (ana@admins.org)') - end - - scenario 'Valuators assignments', :js do - spending_proposal = create(:spending_proposal) - - valuator1 = create(:valuator, user: create(:user, username: 'Valentina', email: 'v1@valuators.org')) - valuator2 = create(:valuator, user: create(:user, username: 'Valerian', email: 'v2@valuators.org')) - valuator3 = create(:valuator, user: create(:user, username: 'Val', email: 'v3@valuators.org')) - - visit admin_spending_proposal_path(spending_proposal) - - within('#assigned_valuators') do - expect(page).to have_content('Undefined') - expect(page).to_not have_content('Valentina (v1@valuators.org)') - expect(page).to_not have_content('Valerian (v2@valuators.org)') - expect(page).to_not have_content('Val (v3@valuators.org)') + expect(page).to have_content 'Investment project updated succesfully.' + expect(page).to have_content 'Assigned administrator: Marta' end - visit admin_spending_proposal_path(spending_proposal) + scenario "Add valuators" do + spending_proposal = create(:spending_proposal) - click_link "Assign valuators" + valuator1 = create(:valuator, user: create(:user, username: 'Valentina', email: 'v1@valuators.org')) + valuator2 = create(:valuator, user: create(:user, username: 'Valerian', email: 'v2@valuators.org')) + valuator3 = create(:valuator, user: create(:user, username: 'Val', email: 'v3@valuators.org')) - within('#valuators-assign-list') do - check "valuator_ids_#{valuator1.id}" - check "valuator_ids_#{valuator3.id}" + visit admin_spending_proposal_path(spending_proposal) + click_link 'Edit' + + check "spending_proposal_valuator_ids_#{valuator1.id}" + check "spending_proposal_valuator_ids_#{valuator3.id}" + + click_button 'Update' + + expect(page).to have_content 'Investment project updated succesfully.' + + within('#assigned_valuators') do + expect(page).to have_content('Valentina (v1@valuators.org)') + expect(page).to have_content('Val (v3@valuators.org)') + expect(page).to_not have_content('Undefined') + expect(page).to_not have_content('Valerian (v2@valuators.org)') + end end - within('#assigned_valuators') do - expect(page).to have_content('Valentina (v1@valuators.org)') - expect(page).to have_content('Val (v3@valuators.org)') - expect(page).to_not have_content('Undefined') - expect(page).to_not have_content('Valerian (v2@valuators.org)') + scenario "Adds existing tags", :js do + create(:spending_proposal, tag_list: 'Education, Health') + + spending_proposal = create(:spending_proposal) + + visit admin_spending_proposal_path(spending_proposal) + click_link 'Edit' + + find('.js-add-tag-link', text: 'Education').click + click_button 'Update' + + expect(page).to have_content 'Investment project updated succesfully.' + + within "#tags" do + expect(page).to have_content 'Education' + expect(page).to_not have_content 'Health' + end end - visit admin_spending_proposal_path(spending_proposal) + scenario "Adds non existent tags" do + spending_proposal = create(:spending_proposal) - within('#assigned_valuators') do - expect(page).to have_content('Valentina (v1@valuators.org)') - expect(page).to have_content('Val (v3@valuators.org)') - expect(page).to_not have_content('Undefined') - expect(page).to_not have_content('Valerian (v2@valuators.org)') + visit admin_spending_proposal_path(spending_proposal) + click_link 'Edit' + + fill_in 'spending_proposal_tag_list', with: 'Refugees, Solidarity' + click_button 'Update' + + expect(page).to have_content 'Investment project updated succesfully.' + + within "#tags" do + expect(page).to have_content 'Refugees' + expect(page).to have_content 'Solidarity' + end end + end end diff --git a/spec/models/spending_proposal_spec.rb b/spec/models/spending_proposal_spec.rb index 9ed96da59..2a10851b6 100644 --- a/spec/models/spending_proposal_spec.rb +++ b/spec/models/spending_proposal_spec.rb @@ -61,15 +61,15 @@ describe SpendingProposal do end end - describe "by_administrator" do + describe "by_admin" do it "should return spending proposals assigned to specific administrator" do spending_proposal1 = create(:spending_proposal, administrator_id: 33) spending_proposal2 = create(:spending_proposal) - by_administrator = SpendingProposal.by_administrator(33) + by_admin = SpendingProposal.by_admin(33) - expect(by_administrator.size).to eq(1) - expect(by_administrator.first).to eq(spending_proposal1) + expect(by_admin.size).to eq(1) + expect(by_admin.first).to eq(spending_proposal1) end end