diff --git a/app/assets/javascripts/forms.js.coffee b/app/assets/javascripts/forms.js.coffee
index edf4c525f..3562335a4 100644
--- a/app/assets/javascripts/forms.js.coffee
+++ b/app/assets/javascripts/forms.js.coffee
@@ -23,8 +23,29 @@ App.Forms =
false
)
+ synchronizeInputs: ->
+ $("[name='progress_bar[percentage]']").on
+ input: ->
+ $("[name='#{this.name}']").val($(this).val())
+
+ $("[name='progress_bar[percentage]'][type='range']").trigger("input")
+
+ hideOrShowFieldsAfterSelection: ->
+ $("[name='progress_bar[kind]']").on
+ change: ->
+ title_field = $("[name^='progress_bar'][name$='[title]']").parent()
+
+ if this.value == "primary"
+ title_field.hide()
+ else
+ title_field.show()
+
+ $("[name='progress_bar[kind]']").change()
+
initialize: ->
App.Forms.disableEnter()
App.Forms.submitOnChange()
App.Forms.toggleLink()
+ App.Forms.synchronizeInputs()
+ App.Forms.hideOrShowFieldsAfterSelection()
false
diff --git a/app/assets/stylesheets/admin.scss b/app/assets/stylesheets/admin.scss
index f08d14fda..95f29e862 100644
--- a/app/assets/stylesheets/admin.scss
+++ b/app/assets/stylesheets/admin.scss
@@ -251,6 +251,13 @@ $sidebar-active: #f4fcd0;
max-width: none;
}
+ form {
+
+ .input-group-label {
+ height: $line-height * 2;
+ }
+ }
+
.menu.simple {
margin-bottom: $line-height / 2;
diff --git a/app/controllers/admin/budget_investment_progress_bars_controller.rb b/app/controllers/admin/budget_investment_progress_bars_controller.rb
new file mode 100644
index 000000000..bb4db0d79
--- /dev/null
+++ b/app/controllers/admin/budget_investment_progress_bars_controller.rb
@@ -0,0 +1,8 @@
+class Admin::BudgetInvestmentProgressBarsController < Admin::ProgressBarsController
+
+ private
+
+ def progressable
+ Budget::Investment.find(params[:budget_investment_id])
+ end
+end
diff --git a/app/controllers/admin/legislation/progress_bars_controller.rb b/app/controllers/admin/legislation/progress_bars_controller.rb
new file mode 100644
index 000000000..ba00d5e91
--- /dev/null
+++ b/app/controllers/admin/legislation/progress_bars_controller.rb
@@ -0,0 +1,14 @@
+class Admin::Legislation::ProgressBarsController < Admin::ProgressBarsController
+ include FeatureFlags
+ feature_flag :legislation
+
+ def index
+ @process = progressable
+ end
+
+ private
+
+ def progressable
+ ::Legislation::Process.find(params[:process_id])
+ end
+end
diff --git a/app/controllers/admin/progress_bars_controller.rb b/app/controllers/admin/progress_bars_controller.rb
new file mode 100644
index 000000000..a9611b364
--- /dev/null
+++ b/app/controllers/admin/progress_bars_controller.rb
@@ -0,0 +1,69 @@
+class Admin::ProgressBarsController < Admin::BaseController
+ include Translatable
+
+ before_action :load_progressable
+ before_action :load_progress_bar, only: [:edit, :update, :destroy]
+ helper_method :progress_bars_index
+
+ def index
+ end
+
+ def new
+ @progress_bar = @progressable.progress_bars.new
+ end
+
+ def create
+ @progress_bar = @progressable.progress_bars.new(progress_bar_params)
+ if @progress_bar.save
+ redirect_to progress_bars_index, notice: t("admin.progress_bars.create.notice")
+ else
+ render :new
+ end
+ end
+
+ def edit
+ end
+
+ def update
+ if @progress_bar.update(progress_bar_params)
+ redirect_to progress_bars_index, notice: t('admin.progress_bars.update.notice')
+ else
+ render :edit
+ end
+ end
+
+ def destroy
+ @progress_bar.destroy
+ redirect_to progress_bars_index, notice: t('admin.progress_bars.delete.notice')
+ end
+
+ private
+
+ def progress_bar_params
+ params.require(:progress_bar).permit(allowed_params)
+ end
+
+ def allowed_params
+ [
+ :kind,
+ :percentage,
+ translation_params(ProgressBar)
+ ]
+ end
+
+ def load_progressable
+ @progressable = progressable
+ end
+
+ def progressable
+ raise "This method must be implemented in subclass #{self.class.name}"
+ end
+
+ def load_progress_bar
+ @progress_bar = progressable.progress_bars.find(params[:id])
+ end
+
+ def progress_bars_index
+ polymorphic_path([:admin, *resource_hierarchy_for(@progressable), ProgressBar.new])
+ end
+end
diff --git a/app/controllers/admin/proposal_progress_bars_controller.rb b/app/controllers/admin/proposal_progress_bars_controller.rb
new file mode 100644
index 000000000..9259acc4b
--- /dev/null
+++ b/app/controllers/admin/proposal_progress_bars_controller.rb
@@ -0,0 +1,7 @@
+class Admin::ProposalProgressBarsController < Admin::ProgressBarsController
+
+ private
+ def progressable
+ Proposal.find(params[:proposal_id])
+ end
+end
diff --git a/app/models/concerns/milestoneable.rb b/app/models/concerns/milestoneable.rb
index 2e961c613..7f58a77bb 100644
--- a/app/models/concerns/milestoneable.rb
+++ b/app/models/concerns/milestoneable.rb
@@ -5,5 +5,7 @@ module Milestoneable
has_many :milestones, as: :milestoneable, dependent: :destroy
scope :with_milestones, -> { joins(:milestones).distinct }
+
+ has_many :progress_bars, as: :progressable
end
end
diff --git a/app/models/progress_bar.rb b/app/models/progress_bar.rb
new file mode 100644
index 000000000..7a7973723
--- /dev/null
+++ b/app/models/progress_bar.rb
@@ -0,0 +1,28 @@
+class ProgressBar < ActiveRecord::Base
+ self.inheritance_column = nil
+ RANGE = 0..100
+
+ enum kind: %i[primary secondary]
+
+ belongs_to :progressable, polymorphic: true
+
+ translates :title, touch: true
+ include Globalizable
+
+ validates :progressable, presence: true
+ validates :kind, presence: true,
+ uniqueness: {
+ scope: [:progressable_type, :progressable_id],
+ conditions: -> { primary }
+ }
+ validates :percentage, presence: true, inclusion: RANGE, numericality: { only_integer: true }
+
+ before_validation :assign_progress_bar_to_translations
+ validates_translation :title, presence: true, unless: :primary?
+
+ private
+
+ def assign_progress_bar_to_translations
+ translations.each { |translation| translation.globalized_model = self }
+ end
+end
diff --git a/app/models/progress_bar/translation.rb b/app/models/progress_bar/translation.rb
new file mode 100644
index 000000000..d61f1d47b
--- /dev/null
+++ b/app/models/progress_bar/translation.rb
@@ -0,0 +1,3 @@
+class ProgressBar::Translation < Globalize::ActiveRecord::Translation
+ delegate :primary?, to: :globalized_model
+end
diff --git a/app/views/admin/legislation/progress_bars/index.html.erb b/app/views/admin/legislation/progress_bars/index.html.erb
new file mode 100644
index 000000000..b84717689
--- /dev/null
+++ b/app/views/admin/legislation/progress_bars/index.html.erb
@@ -0,0 +1,12 @@
+<% provide :title do %>
+ <%= "#{t("admin.header.title")} - #{t("admin.menu.legislation")}" %> -
+ <%= "#{@process.title} - #{t("admin.progress_bars.index.title")}" %>
+<% end %>
+
+<%= back_link_to admin_legislation_process_milestones_path(@progressable),
+ t("admin.legislation.processes.edit.back") %>
+
+
<%= @process.title %>
+
+<%= render "admin/legislation/processes/subnav", process: @process, active: "milestones" %>
+<%= render "admin/progress_bars/progress_bars", progressable: @process %>
diff --git a/app/views/admin/milestones/_milestones.html.erb b/app/views/admin/milestones/_milestones.html.erb
index f2ef31216..1b5a4933b 100644
--- a/app/views/admin/milestones/_milestones.html.erb
+++ b/app/views/admin/milestones/_milestones.html.erb
@@ -1,4 +1,8 @@
-<%= t("admin.milestones.index.milestone") %>
+<%= t("admin.milestones.index.milestone") %>
+
+<%= link_to t("admin.progress_bars.manage"),
+ polymorphic_path([:admin, *resource_hierarchy_for(milestoneable.progress_bars.new)]),
+ class: "button hollow float-right" %>
<% if milestoneable.milestones.any? %>
diff --git a/app/views/admin/progress_bars/_form.html.erb b/app/views/admin/progress_bars/_form.html.erb
new file mode 100644
index 000000000..cdc6af59e
--- /dev/null
+++ b/app/views/admin/progress_bars/_form.html.erb
@@ -0,0 +1,31 @@
+<%= render "admin/shared/globalize_locales", resource: @progress_bar %>
+
+<%= translatable_form_for [:admin, *resource_hierarchy_for(@progress_bar)] do |f| %>
+
+
+ <%= f.enum_select :kind %>
+
+
+ <%= f.translatable_fields do |translations_form| %>
+
+ <%= translations_form.text_field :title %>
+
+ <% end %>
+
+ <% progress_options = { min: ProgressBar::RANGE.min, max: ProgressBar::RANGE.max, step: 1 } %>
+
+ <%= f.text_field :percentage, { type: :range,
+ id: "percentage_range",
+ class: "column" }.merge(progress_options) %>
+
+
+
+ <%= f.text_field :percentage, { type: :number,
+ label: false,
+ class: "input-group-field" }.merge(progress_options) %>
+ %
+
+
+
+ <%= f.submit nil, class: "button success" %>
+<% end %>
diff --git a/app/views/admin/progress_bars/_progress_bars.html.erb b/app/views/admin/progress_bars/_progress_bars.html.erb
new file mode 100644
index 000000000..3715c2f35
--- /dev/null
+++ b/app/views/admin/progress_bars/_progress_bars.html.erb
@@ -0,0 +1,57 @@
+<%= t("admin.progress_bars.index.title") %>
+
+<%= link_to t("admin.progress_bars.index.new_progress_bar"),
+ polymorphic_path(
+ [:admin, *resource_hierarchy_for(ProgressBar.new(progressable: progressable))],
+ action: :new
+ ),
+ class: "button float-right" %>
+
+<% if progressable.progress_bars.any? %>
+
+
+
+ | <%= t("admin.progress_bars.index.table_id") %> |
+ <%= t("admin.progress_bars.index.table_kind") %> |
+ <%= t("admin.progress_bars.index.table_title") %> |
+ <%= t("admin.progress_bars.index.table_percentage") %> |
+ <%= t("admin.actions.actions") %> |
+
+
+
+ <% progressable.progress_bars.each do |progress_bar| %>
+
+ |
+ <%= progress_bar.id %>
+ |
+ <%= ProgressBar.human_attribute_name("kind.#{progress_bar.kind}") %> |
+
+ <% if progress_bar.title.present? %>
+ <%= progress_bar.title %>
+ <% else %>
+ <%= t("admin.progress_bars.index.primary") %>
+ <% end %>
+ |
+
+ <%= number_to_percentage(progress_bar.percentage, strip_insignificant_zeros: true) %>
+ |
+
+ <%= link_to t("admin.actions.edit"),
+ polymorphic_path([:admin, *resource_hierarchy_for(progress_bar)],
+ action: :edit),
+ class: "button hollow" %>
+
+ <%= link_to t("admin.actions.delete"),
+ polymorphic_path([:admin, *resource_hierarchy_for(progress_bar)]),
+ method: :delete,
+ class: "button hollow alert" %>
+ |
+
+ <% end %>
+
+
+<% else %>
+
+ <%= t("admin.progress_bars.index.no_progress_bars") %>
+
+<% end %>
diff --git a/app/views/admin/progress_bars/edit.html.erb b/app/views/admin/progress_bars/edit.html.erb
new file mode 100644
index 000000000..21dd27d9a
--- /dev/null
+++ b/app/views/admin/progress_bars/edit.html.erb
@@ -0,0 +1,15 @@
+<% if @progress_bar.primary? %>
+ <% bar_title = t("admin.progress_bars.edit.title.primary") %>
+<% else %>
+ <% bar_title = t("admin.progress_bars.edit.title.secondary", title: @progress_bar.title) %>
+<% end %>
+
+<% provide :title do %>
+ <%= "#{t("admin.header.title")} - #{bar_title}" %>
+<% end %>
+
+<%= back_link_to progress_bars_index %>
+
+<%= bar_title %>
+
+<%= render "form" %>
diff --git a/app/views/admin/progress_bars/index.html.erb b/app/views/admin/progress_bars/index.html.erb
new file mode 100644
index 000000000..bcac8d7a4
--- /dev/null
+++ b/app/views/admin/progress_bars/index.html.erb
@@ -0,0 +1,9 @@
+<% provide :title do %>
+ <%= "#{t("admin.header.title")} - #{t("admin.progress_bars.index.title")}" %>
+<% end %>
+
+<%= back_link_to polymorphic_path([:admin, *resource_hierarchy_for(@progressable)]) %>
+
+
+
+<%= render "admin/progress_bars/progress_bars", progressable: @progressable %>
diff --git a/app/views/admin/progress_bars/new.html.erb b/app/views/admin/progress_bars/new.html.erb
new file mode 100644
index 000000000..8c379ac3a
--- /dev/null
+++ b/app/views/admin/progress_bars/new.html.erb
@@ -0,0 +1,9 @@
+<% provide :title do %>
+ <%= "#{t("admin.header.title")} - #{t("admin.progress_bars.new.creating")}" %>
+<% end %>
+
+<%= back_link_to progress_bars_index %>
+
+<%= t("admin.progress_bars.new.creating") %>
+
+<%= render "form" %>
diff --git a/config/initializers/foundation_rails_helper.rb b/config/initializers/foundation_rails_helper.rb
index ff5ae4618..3e121f0d9 100644
--- a/config/initializers/foundation_rails_helper.rb
+++ b/config/initializers/foundation_rails_helper.rb
@@ -5,5 +5,13 @@ module FoundationRailsHelper
super(attribute, opts)
end
end
+
+ def enum_select(attribute, options = {}, html_options = {})
+ choices = object.class.send(attribute.to_s.pluralize).keys.map do |name|
+ [object.class.human_attribute_name("#{attribute}.#{name}"), name]
+ end
+
+ select attribute, choices, options, html_options
+ end
end
end
diff --git a/config/initializers/routes_hierarchy.rb b/config/initializers/routes_hierarchy.rb
index 0a712f923..06a7acc74 100644
--- a/config/initializers/routes_hierarchy.rb
+++ b/config/initializers/routes_hierarchy.rb
@@ -9,6 +9,8 @@ module ActionDispatch::Routing::UrlFor
[resource.budget, resource]
when "Milestone"
[*resource_hierarchy_for(resource.milestoneable), resource]
+ when "ProgressBar"
+ [*resource_hierarchy_for(resource.progressable), resource]
when "Legislation::Annotation"
[resource.draft_version.process, resource.draft_version, resource]
when "Legislation::Proposal", "Legislation::Question", "Legislation::DraftVersion"
diff --git a/config/locales/en/activerecord.yml b/config/locales/en/activerecord.yml
index aab700334..90e726039 100644
--- a/config/locales/en/activerecord.yml
+++ b/config/locales/en/activerecord.yml
@@ -16,6 +16,9 @@ en:
milestone/status:
one: "Milestone Status"
other: "Milestone Statuses"
+ progress_bar:
+ one: "Progress bar"
+ other: "Progress bars"
comment:
one: "Comment"
other: "Comments"
@@ -136,6 +139,13 @@ en:
milestone/status:
name: "Name"
description: "Description (optional)"
+ progress_bar:
+ kind: "Type"
+ title: "Title"
+ percentage: "Current progress"
+ progress_bar/kind:
+ primary: "Primary"
+ secondary: "Secondary"
budget/heading:
name: "Heading name"
price: "Price"
diff --git a/config/locales/en/admin.yml b/config/locales/en/admin.yml
index 7033fefbf..a86f8a315 100644
--- a/config/locales/en/admin.yml
+++ b/config/locales/en/admin.yml
@@ -332,6 +332,29 @@ en:
notice: Milestone status created successfully
delete:
notice: Milestone status deleted successfully
+ progress_bars:
+ manage: "Manage progress bars"
+ index:
+ title: "Progress bars"
+ no_progress_bars: "There are no progress bars"
+ new_progress_bar: "Create new progress bar"
+ primary: "Primary progress bar"
+ table_id: "ID"
+ table_kind: "Type"
+ table_title: "Title"
+ table_percentage: "Current progress"
+ new:
+ creating: "Create progress bar"
+ edit:
+ title:
+ primary: "Edit primary progress bar"
+ secondary: "Edit progress bar %{title}"
+ create:
+ notice: "Progress bar created successfully!"
+ update:
+ notice: "Progress bar updated successfully"
+ delete:
+ notice: "Progress bar deleted successfully"
comments:
index:
filter: Filter
diff --git a/config/locales/es/activerecord.yml b/config/locales/es/activerecord.yml
index 2c0b210a0..45cfa15b0 100644
--- a/config/locales/es/activerecord.yml
+++ b/config/locales/es/activerecord.yml
@@ -16,6 +16,9 @@ es:
milestone/status:
one: "Estado de seguimiento"
other: "Estados de seguimiento"
+ progress_bar:
+ one: "Barra de progreso"
+ other: "Barras de progreso"
comment:
one: "Comentario"
other: "Comentarios"
@@ -136,6 +139,13 @@ es:
milestone/status:
name: "Nombre"
description: "Descripción (opcional)"
+ progress_bar:
+ kind: "Tipo"
+ title: "Título"
+ percentage: "Progreso"
+ progress_bar/kind:
+ primary: "Principal"
+ secondary: "Secundaria"
budget/heading:
name: "Nombre de la partida"
price: "Cantidad"
diff --git a/config/locales/es/admin.yml b/config/locales/es/admin.yml
index 0075225c7..d33d91211 100644
--- a/config/locales/es/admin.yml
+++ b/config/locales/es/admin.yml
@@ -332,6 +332,30 @@ es:
notice: Estado de seguimiento creado correctamente
delete:
notice: Estado de seguimiento eliminado correctamente
+ progress_bars:
+ manage: "Gestionar barras de progreso"
+ index:
+ title: "Barras de progreso"
+ no_progress_bars: "No hay barras de progreso"
+ new_progress_bar: "Crear nueva barra de progreso"
+ primary: "Barra de progreso principal"
+ table_id: "ID"
+ table_kind: "Tipo"
+ table_title: "Título"
+ table_percentage: "Progreso"
+ new:
+ creating: "Crear barra de progreso"
+ edit:
+ title:
+ primary: "Editar barra de progreso principal"
+ secondary: "Editar barra de progreso %{title}"
+ create:
+ notice: "¡Barra de progreso creada con éxito!"
+ update:
+ notice: "Barra de progreso actualizada"
+ delete:
+ notice: "Barra de progreso eliminada correctamente"
+
comments:
index:
filter: Filtro
diff --git a/config/routes/admin.rb b/config/routes/admin.rb
index 270a5061c..52740e9d6 100644
--- a/config/routes/admin.rb
+++ b/config/routes/admin.rb
@@ -31,6 +31,7 @@ namespace :admin do
resources :proposals, only: [:index, :show] do
resources :milestones, controller: "proposal_milestones"
+ resources :progress_bars, except: :show, controller: "proposal_progress_bars"
end
resources :hidden_proposals, only: :index do
@@ -67,6 +68,7 @@ namespace :admin do
resources :budget_investments, only: [:index, :show, :edit, :update] do
resources :milestones, controller: 'budget_investment_milestones'
+ resources :progress_bars, except: :show, controller: "budget_investment_progress_bars"
member { patch :toggle_selection }
end
@@ -203,6 +205,7 @@ namespace :admin do
end
resources :draft_versions
resources :milestones
+ resources :progress_bars, except: :show
resource :homepage, only: [:edit, :update]
end
end
diff --git a/db/migrate/20190103132925_create_progress_bars.rb b/db/migrate/20190103132925_create_progress_bars.rb
new file mode 100644
index 000000000..899a4819f
--- /dev/null
+++ b/db/migrate/20190103132925_create_progress_bars.rb
@@ -0,0 +1,23 @@
+class CreateProgressBars < ActiveRecord::Migration
+ def change
+ create_table :progress_bars do |t|
+ t.integer :kind
+ t.integer :percentage
+ t.references :progressable, polymorphic: true
+
+ t.timestamps null: false
+ end
+
+ reversible do |change|
+ change.up do
+ ProgressBar.create_translation_table!({
+ title: :string
+ })
+ end
+
+ change.down do
+ ProgressBar.drop_translation_table!
+ end
+ end
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index e3241fd8d..0d5ed3d1a 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -11,7 +11,7 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema.define(version: 20181206153510) do
+ActiveRecord::Schema.define(version: 20190103132925) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@@ -1088,6 +1088,26 @@ ActiveRecord::Schema.define(version: 20181206153510) do
add_index "polls", ["starts_at", "ends_at"], name: "index_polls_on_starts_at_and_ends_at", using: :btree
+ create_table "progress_bar_translations", force: :cascade do |t|
+ t.integer "progress_bar_id", null: false
+ t.string "locale", null: false
+ t.datetime "created_at", null: false
+ t.datetime "updated_at", null: false
+ t.string "title"
+ end
+
+ add_index "progress_bar_translations", ["locale"], name: "index_progress_bar_translations_on_locale", using: :btree
+ add_index "progress_bar_translations", ["progress_bar_id"], name: "index_progress_bar_translations_on_progress_bar_id", using: :btree
+
+ create_table "progress_bars", force: :cascade do |t|
+ t.integer "kind"
+ t.integer "percentage"
+ t.integer "progressable_id"
+ t.string "progressable_type"
+ t.datetime "created_at", null: false
+ t.datetime "updated_at", null: false
+ end
+
create_table "proposal_notifications", force: :cascade do |t|
t.string "title"
t.text "body"
diff --git a/spec/factories/budgets.rb b/spec/factories/budgets.rb
index da7b0f1b9..e741eb683 100644
--- a/spec/factories/budgets.rb
+++ b/spec/factories/budgets.rb
@@ -195,19 +195,6 @@ FactoryBot.define do
reason "unfeasible"
end
- factory :milestone_status, class: 'Milestone::Status' do
- sequence(:name) { |n| "Milestone status #{n} name" }
- sequence(:description) { |n| "Milestone status #{n} description" }
- end
-
- factory :milestone, class: 'Milestone' do
- association :milestoneable, factory: :budget_investment
- association :status, factory: :milestone_status
- sequence(:title) { |n| "Budget investment milestone #{n} title" }
- description 'Milestone description'
- publication_date { Date.current }
- end
-
factory :valuator_group, class: ValuatorGroup do
sequence(:name) { |n| "Valuator Group #{n}" }
end
diff --git a/spec/factories/milestones.rb b/spec/factories/milestones.rb
new file mode 100644
index 000000000..0e4071d15
--- /dev/null
+++ b/spec/factories/milestones.rb
@@ -0,0 +1,25 @@
+FactoryBot.define do
+ factory :milestone_status, class: "Milestone::Status" do
+ sequence(:name) { |n| "Milestone status #{n} name" }
+ sequence(:description) { |n| "Milestone status #{n} description" }
+ end
+
+ factory :milestone do
+ association :milestoneable, factory: :budget_investment
+ association :status, factory: :milestone_status
+ sequence(:title) { |n| "Milestone #{n} title" }
+ description "Milestone description"
+ publication_date { Date.current }
+ end
+
+ factory :progress_bar do
+ association :progressable, factory: :budget_investment
+ percentage { rand(0..100) }
+ kind :primary
+
+ trait(:secondary) do
+ kind :secondary
+ sequence(:title) { |n| "Progress bar #{n} title" }
+ end
+ end
+end
diff --git a/spec/models/progress_bar_spec.rb b/spec/models/progress_bar_spec.rb
new file mode 100644
index 000000000..46b0ce08b
--- /dev/null
+++ b/spec/models/progress_bar_spec.rb
@@ -0,0 +1,97 @@
+require "rails_helper"
+
+describe ProgressBar do
+ let(:progress_bar) { build(:progress_bar) }
+
+ it "is valid" do
+ expect(progress_bar).to be_valid
+ end
+
+ it "is valid without a title" do
+ progress_bar.title = nil
+
+ expect(progress_bar).to be_valid
+ end
+
+ it "is not valid with a custom type" do
+ expect { progress_bar.kind = "terciary" }.to raise_exception(ArgumentError)
+ end
+
+ it "is not valid without a percentage" do
+ progress_bar.percentage = nil
+
+ expect(progress_bar).not_to be_valid
+ end
+
+ it "is not valid with a non-numeric percentage" do
+ progress_bar.percentage = "High"
+
+ expect(progress_bar).not_to be_valid
+ end
+
+ it "is not valid with a non-integer percentage" do
+ progress_bar.percentage = 22.83
+
+ expect(progress_bar).not_to be_valid
+ end
+
+ it "is not valid with a negative percentage" do
+ progress_bar.percentage = -1
+
+ expect(progress_bar).not_to be_valid
+ end
+
+ it "is not valid with a percentage bigger than 100" do
+ progress_bar.percentage = 101
+
+ expect(progress_bar).not_to be_valid
+ end
+
+ it "is valid with an integer percentage within the limits" do
+ progress_bar.percentage = 0
+
+ expect(progress_bar).to be_valid
+
+ progress_bar.percentage = 100
+
+ expect(progress_bar).to be_valid
+
+ progress_bar.percentage = 83
+
+ expect(progress_bar).to be_valid
+ end
+
+ it "is not valid without a progressable" do
+ progress_bar.progressable = nil
+
+ expect(progress_bar).not_to be_valid
+ end
+
+ it "cannot have another primary progress bar for the same progressable" do
+ progress_bar.save
+ duplicate = build(:progress_bar, progressable: progress_bar.progressable)
+
+ expect(duplicate).not_to be_valid
+ end
+
+ describe "secondary progress bar" do
+ let(:progress_bar) { build(:progress_bar, :secondary) }
+
+ it "is valid" do
+ expect(progress_bar).to be_valid
+ end
+
+ it "is invalid without a title" do
+ progress_bar.title = nil
+
+ expect(progress_bar).not_to be_valid
+ end
+
+ it "can have another secondary progress bar for the same progressable" do
+ progress_bar.save
+ duplicate = build(:progress_bar, progressable: progress_bar.progressable)
+
+ expect(duplicate).to be_valid
+ end
+ end
+end
diff --git a/spec/shared/features/admin_milestoneable.rb b/spec/shared/features/admin_milestoneable.rb
index 17f43a012..c12fd7624 100644
--- a/spec/shared/features/admin_milestoneable.rb
+++ b/spec/shared/features/admin_milestoneable.rb
@@ -1,4 +1,5 @@
shared_examples "admin_milestoneable" do |factory_name, path_name|
+ it_behaves_like "progressable", factory_name, path_name
feature "Admin milestones" do
let!(:milestoneable) { create(factory_name) }
diff --git a/spec/shared/features/progressable.rb b/spec/shared/features/progressable.rb
new file mode 100644
index 000000000..b28c383c6
--- /dev/null
+++ b/spec/shared/features/progressable.rb
@@ -0,0 +1,115 @@
+shared_examples "progressable" do |factory_name, path_name|
+ let!(:progressable) { create(factory_name) }
+
+ feature "Manage progress bars" do
+ let(:progressable_path) { send(path_name, *resource_hierarchy_for(progressable)) }
+
+ let(:path) do
+ polymorphic_path([:admin, *resource_hierarchy_for(progressable.progress_bars.new)])
+ end
+
+ context "Index" do
+ scenario "Link to index path" do
+ create(:progress_bar, :secondary, progressable: progressable,
+ title: "Reading documents",
+ percentage: 20)
+
+ visit progressable_path
+ click_link "Manage progress bars"
+
+ expect(page).to have_content "Reading documents"
+ end
+
+ scenario "No progress bars" do
+ visit path
+
+ expect(page).to have_content("There are no progress bars")
+ end
+ end
+
+ context "New" do
+ scenario "Primary progress bar", :js do
+ visit path
+ click_link "Create new progress bar"
+
+ select "Primary", from: "Type"
+
+ expect(page).not_to have_field "Title"
+
+ fill_in "Current progress", with: 43
+ click_button "Create Progress bar"
+
+ expect(page).to have_content "Progress bar created successfully"
+ expect(page).to have_content "43%"
+ expect(page).to have_content "Primary"
+ expect(page).to have_content "Primary progress bar"
+ end
+
+ scenario "Secondary progress bar", :js do
+ visit path
+ click_link "Create new progress bar"
+
+ select "Secondary", from: "Type"
+ fill_in "Current progress", with: 36
+ fill_in "Title", with: "Plant trees"
+ click_button "Create Progress bar"
+
+ expect(page).to have_content "Progress bar created successfully"
+ expect(page).to have_content "36%"
+ expect(page).to have_content "Secondary"
+ expect(page).to have_content "Plant trees"
+ end
+ end
+
+ context "Edit" do
+ scenario "Primary progress bar", :js do
+ bar = create(:progress_bar, progressable: progressable)
+
+ visit path
+ within("#progress_bar_#{bar.id}") { click_link "Edit" }
+
+ expect(page).to have_field "Current progress"
+ expect(page).not_to have_field "Title"
+
+ fill_in "Current progress", with: 44
+ click_button "Update Progress bar"
+
+ expect(page).to have_content "Progress bar updated successfully"
+
+ within("#progress_bar_#{bar.id}") do
+ expect(page).to have_content "44%"
+ end
+ end
+
+ scenario "Secondary progress bar", :js do
+ bar = create(:progress_bar, :secondary, progressable: progressable)
+
+ visit path
+ within("#progress_bar_#{bar.id}") { click_link "Edit" }
+
+ fill_in "Current progress", with: 76
+ fill_in "Title", with: "Updated title"
+ click_button "Update Progress bar"
+
+ expect(page).to have_content "Progress bar updated successfully"
+
+ within("#progress_bar_#{bar.id}") do
+ expect(page).to have_content "76%"
+ expect(page).to have_content "Updated title"
+ end
+ end
+ end
+
+ context "Delete" do
+ scenario "Remove progress bar" do
+ bar = create(:progress_bar, progressable: progressable, percentage: 34)
+
+ visit path
+ within("#progress_bar_#{bar.id}") { click_link "Delete" }
+
+ expect(page).to have_content "Progress bar deleted successfully"
+ expect(page).not_to have_content "34%"
+ end
+ end
+ end
+end