diff --git a/app/assets/stylesheets/admin.scss b/app/assets/stylesheets/admin.scss
index e6bab74a3..cd79a951b 100644
--- a/app/assets/stylesheets/admin.scss
+++ b/app/assets/stylesheets/admin.scss
@@ -614,6 +614,16 @@ code {
}
}
+.admin-content > header {
+ align-items: flex-start;
+ display: flex;
+
+ a {
+ @include hollow-button;
+ margin-left: auto;
+ }
+}
+
.admin-content .select-geozone,
.admin-content .select-heading {
diff --git a/app/assets/stylesheets/mixins.scss b/app/assets/stylesheets/mixins.scss
index 060e31d83..875a1b913 100644
--- a/app/assets/stylesheets/mixins.scss
+++ b/app/assets/stylesheets/mixins.scss
@@ -198,3 +198,14 @@
transform: translateX(-50%);
}
}
+
+@mixin hollow-button($color: $link) {
+ @include button($style: hollow, $background: $color);
+ font-size: $base-font-size;
+ margin-bottom: 0;
+
+ &:focus,
+ &:hover {
+ text-decoration: none;
+ }
+}
diff --git a/app/components/sdg_management/header.rb b/app/components/sdg_management/header.rb
index 0a9370031..c9cb60d6e 100644
--- a/app/components/sdg_management/header.rb
+++ b/app/components/sdg_management/header.rb
@@ -1,11 +1,17 @@
module SDGManagement::Header
extend ActiveSupport::Concern
- def header
+ def header(&block)
provide(:title) do
"#{t("sdg_management.header.title")} - #{title}"
end
- tag.h2 title
+ tag.header do
+ if block_given?
+ tag.h2(title) + capture(&block)
+ else
+ tag.h2(title)
+ end
+ end
end
end
diff --git a/app/components/sdg_management/local_targets/form_component.html.erb b/app/components/sdg_management/local_targets/form_component.html.erb
new file mode 100644
index 000000000..df7c8a9ae
--- /dev/null
+++ b/app/components/sdg_management/local_targets/form_component.html.erb
@@ -0,0 +1,41 @@
+<%= back_link_to sdg_management_local_targets_path %>
+
+<%= header %>
+
+<%= render "shared/globalize_locales", resource: local_target %>
+
+<%= translatable_form_for [:sdg_managment, local_target], url: form_url do |f| %>
+ <%= render "shared/errors", resource: local_target %>
+
+
+
+ <%= f.select :target_id, target_options %>
+
+
+
+
+
+ <%= f.text_field :code, hint: t("sdg_management.local_targets.form.code") %>
+
+
+
+
+ <%= f.translatable_fields do |translations_form| %>
+
+ <%= translations_form.text_field :title %>
+
+
+
+ <%= translations_form.text_area :description,
+ rows: 4,
+ hint: t("sdg_management.local_targets.form.description") %>
+
+ <% end %>
+
+
+
+
+ <%= f.submit class: "button" %>
+
+
+<% end %>
diff --git a/app/components/sdg_management/local_targets/form_component.rb b/app/components/sdg_management/local_targets/form_component.rb
new file mode 100644
index 000000000..933e70bd1
--- /dev/null
+++ b/app/components/sdg_management/local_targets/form_component.rb
@@ -0,0 +1,44 @@
+class SDGManagement::LocalTargets::FormComponent < ApplicationComponent
+ delegate :back_link_to, to: :helpers
+ include SDGManagement::Header
+ include TranslatableFormHelper
+ include GlobalizeHelper
+
+ attr_reader :local_target
+
+ def initialize(local_target)
+ @local_target = local_target
+ end
+
+ private
+
+ def title
+ if local_target.persisted?
+ t("sdg_management.local_targets.edit.title")
+ else
+ t("sdg_management.local_targets.new.title")
+ end
+ end
+
+ def form_url
+ if local_target.persisted?
+ sdg_management_local_target_path(local_target)
+ else
+ sdg_management_local_targets_path
+ end
+ end
+
+ def target_options
+ grouped_targets = SDG::Goal.order(:code).map do |goal|
+ [
+ code_and_title(goal),
+ goal.targets.sort.map { |target| [code_and_title(target), target.id] }
+ ]
+ end
+ grouped_options_for_select(grouped_targets, local_target.target_id)
+ end
+
+ def code_and_title(resource)
+ "#{resource.code} #{resource.title}"
+ end
+end
diff --git a/app/components/sdg_management/local_targets/index_component.html.erb b/app/components/sdg_management/local_targets/index_component.html.erb
new file mode 100644
index 000000000..0512c531b
--- /dev/null
+++ b/app/components/sdg_management/local_targets/index_component.html.erb
@@ -0,0 +1,41 @@
+<%= header do %>
+ <%= link_to t("sdg_management.local_targets.index.new"),
+ new_sdg_management_local_target_path,
+ class: "new-link" %>
+<% end %>
+
+<%= render SDGManagement::SubnavigationComponent.new(current: :local_targets) do %>
+
+
+
+ |
+ <%= attribute_name(:title) %> |
+ <%= t("admin.actions.actions") %> |
+
+
+
+
+ <% local_targets.group_by(&:target).map do |target, local_targets| %>
+
+
+ <% local_targets.each do |local_target| %>
+
+
+ |
+ <%= local_target.title %>
+ |
+
+ <%= actions(local_target) %>
+ |
+
+ <% end %>
+ <% end %>
+
+
+<% end %>
diff --git a/app/components/sdg_management/local_targets/index_component.rb b/app/components/sdg_management/local_targets/index_component.rb
new file mode 100644
index 000000000..882836409
--- /dev/null
+++ b/app/components/sdg_management/local_targets/index_component.rb
@@ -0,0 +1,30 @@
+class SDGManagement::LocalTargets::IndexComponent < ApplicationComponent
+ include SDGManagement::Header
+
+ attr_reader :local_targets
+
+ def initialize(local_targets)
+ @local_targets = local_targets
+ end
+
+ private
+
+ def title
+ SDG::LocalTarget.model_name.human(count: 2).titleize
+ end
+
+ def attribute_name(attribute)
+ SDG::LocalTarget.human_attribute_name(attribute)
+ end
+
+ def header_id(object)
+ "#{dom_id(object)}_header"
+ end
+
+ def actions(local_target)
+ render Admin::TableActionsComponent.new(
+ local_target,
+ edit_path: edit_sdg_management_local_target_path(local_target),
+ destroy_path: sdg_management_local_target_path(local_target))
+ end
+end
diff --git a/app/components/sdg_management/menu_component.rb b/app/components/sdg_management/menu_component.rb
index c594bd081..a168b0442 100644
--- a/app/components/sdg_management/menu_component.rb
+++ b/app/components/sdg_management/menu_component.rb
@@ -4,6 +4,6 @@ class SDGManagement::MenuComponent < ApplicationComponent
private
def sdg?
- controller_name == "goals" || controller_name == "targets"
+ %w[goals targets local_targets].include?(controller_name)
end
end
diff --git a/app/components/sdg_management/subnavigation_component.rb b/app/components/sdg_management/subnavigation_component.rb
index 35d1a0fda..41096a618 100644
--- a/app/components/sdg_management/subnavigation_component.rb
+++ b/app/components/sdg_management/subnavigation_component.rb
@@ -8,7 +8,7 @@ class SDGManagement::SubnavigationComponent < ApplicationComponent
private
def sections
- %i[goals targets]
+ %i[goals targets local_targets]
end
def link_to_section(section)
diff --git a/app/controllers/sdg_management/local_targets_controller.rb b/app/controllers/sdg_management/local_targets_controller.rb
new file mode 100644
index 000000000..d23fe06a7
--- /dev/null
+++ b/app/controllers/sdg_management/local_targets_controller.rb
@@ -0,0 +1,50 @@
+class SDGManagement::LocalTargetsController < SDGManagement::BaseController
+ include Translatable
+
+ LocalTarget = ::SDG::LocalTarget
+
+ def index
+ @local_targets = LocalTarget.all.sort
+ end
+
+ def new
+ @local_target = LocalTarget.new
+ end
+
+ def create
+ @local_target = LocalTarget.new(local_target_params)
+
+ if @local_target.save
+ redirect_to sdg_management_local_targets_path, notice: t("sdg_management.local_targets.create.notice")
+ else
+ render :new
+ end
+ end
+
+ def edit
+ @local_target = LocalTarget.find(params[:id])
+ end
+
+ def update
+ @local_target = LocalTarget.find(params[:id])
+
+ if @local_target.update(local_target_params)
+ redirect_to sdg_management_local_targets_path, notice: t("sdg_management.local_targets.update.notice")
+ else
+ render :edit
+ end
+ end
+
+ def destroy
+ @local_target = LocalTarget.find(params[:id])
+ @local_target.destroy!
+ redirect_to sdg_management_local_targets_path, notice: t("sdg_management.local_targets.destroy.notice")
+ end
+
+ private
+
+ def local_target_params
+ translations_attributes = translation_params(LocalTarget)
+ params.require(:sdg_local_target).permit(:code, :target_id, translations_attributes)
+ end
+end
diff --git a/app/models/concerns/sdg/relatable.rb b/app/models/concerns/sdg/relatable.rb
index e85e1407c..9da20b0d5 100644
--- a/app/models/concerns/sdg/relatable.rb
+++ b/app/models/concerns/sdg/relatable.rb
@@ -4,7 +4,7 @@ module SDG::Relatable
included do
has_many :sdg_relations, as: :relatable, dependent: :destroy, class_name: "SDG::Relation"
- %w[SDG::Goal SDG::Target].each do |sdg_type|
+ %w[SDG::Goal SDG::Target SDG::LocalTarget].each do |sdg_type|
has_many sdg_type.constantize.table_name.to_sym,
through: :sdg_relations,
source: :related_sdg,
diff --git a/app/models/sdg/local_target.rb b/app/models/sdg/local_target.rb
new file mode 100644
index 000000000..8443e2a40
--- /dev/null
+++ b/app/models/sdg/local_target.rb
@@ -0,0 +1,37 @@
+class SDG::LocalTarget < ApplicationRecord
+ include Comparable
+ include SDG::Related
+
+ delegate :goal, to: :target
+
+ translates :title, touch: true
+ translates :description, touch: true
+ include Globalizable
+
+ validates_translation :title, presence: true
+ validates_translation :description, presence: true
+
+ validates :code, presence: true, uniqueness: true,
+ format: ->(local_target) { /\A#{local_target.target&.code}\.\d+/ }
+ validates :target, presence: true
+
+ belongs_to :target
+
+ def <=>(local_target)
+ return unless local_target.class == self.class
+
+ [target, numeric_subcode] <=> [local_target.target, local_target.numeric_subcode]
+ end
+
+ protected
+
+ def numeric_subcode
+ subcode.to_i
+ end
+
+ private
+
+ def subcode
+ code.split(".").last
+ end
+end
diff --git a/app/models/sdg/target.rb b/app/models/sdg/target.rb
index ad165e280..6ff87d155 100644
--- a/app/models/sdg/target.rb
+++ b/app/models/sdg/target.rb
@@ -6,6 +6,7 @@ class SDG::Target < ApplicationRecord
validates :goal, presence: true
belongs_to :goal
+ has_many :local_targets, dependent: :destroy
def title
I18n.t("sdg.goals.goal_#{goal.code}.targets.target_#{code_key}.title")
diff --git a/app/views/sdg_management/local_targets/edit.html.erb b/app/views/sdg_management/local_targets/edit.html.erb
new file mode 100644
index 000000000..07818dd63
--- /dev/null
+++ b/app/views/sdg_management/local_targets/edit.html.erb
@@ -0,0 +1 @@
+<%= render SDGManagement::LocalTargets::FormComponent.new(@local_target) %>
diff --git a/app/views/sdg_management/local_targets/index.html.erb b/app/views/sdg_management/local_targets/index.html.erb
new file mode 100644
index 000000000..de6e04103
--- /dev/null
+++ b/app/views/sdg_management/local_targets/index.html.erb
@@ -0,0 +1 @@
+<%= render SDGManagement::LocalTargets::IndexComponent.new(@local_targets) %>
diff --git a/app/views/sdg_management/local_targets/new.html.erb b/app/views/sdg_management/local_targets/new.html.erb
new file mode 100644
index 000000000..07818dd63
--- /dev/null
+++ b/app/views/sdg_management/local_targets/new.html.erb
@@ -0,0 +1 @@
+<%= render SDGManagement::LocalTargets::FormComponent.new(@local_target) %>
diff --git a/config/locales/en/activerecord.yml b/config/locales/en/activerecord.yml
index 5721fc5e5..73edfc440 100644
--- a/config/locales/en/activerecord.yml
+++ b/config/locales/en/activerecord.yml
@@ -75,6 +75,9 @@ en:
sdg/goal:
one: "goal"
other: "goals"
+ sdg/local_target:
+ one: "local target"
+ other: "local targets"
sdg/target:
one: "target"
other: "targets"
@@ -318,6 +321,13 @@ en:
code: "Code"
title: "Title"
description: "Description"
+ sdg/local_target:
+ code: "Code"
+ target_id: "Target"
+ title: "Title"
+ sdg/local_target/translation:
+ title: "Title"
+ description: "Description"
sdg/target:
code: "Code"
title: "Title"
@@ -571,6 +581,10 @@ en:
attributes:
locale:
already_translated: Already translated resource
+ sdg/local_target:
+ attributes:
+ code:
+ invalid: "must start with the same code as its target followed by a dot and end with a number"
messages:
translations_too_short: Is mandatory to provide one translation at least
record_invalid: "Validation failed: %{errors}"
diff --git a/config/locales/en/general.yml b/config/locales/en/general.yml
index 293e3b4e7..5a47cdb64 100644
--- a/config/locales/en/general.yml
+++ b/config/locales/en/general.yml
@@ -190,6 +190,7 @@ en:
local_census_records/import: Local Census Records Import
site_customization/content_block: content block
site_customization/page: page
+ sdg/local_target: local target
geozones:
none: All city
layouts:
diff --git a/config/locales/en/sdg_management.yml b/config/locales/en/sdg_management.yml
index d71b2e7be..1e075fd76 100644
--- a/config/locales/en/sdg_management.yml
+++ b/config/locales/en/sdg_management.yml
@@ -4,3 +4,19 @@ en:
title: "SDG content"
menu:
sdg_content: "Goals and Targets"
+ local_targets:
+ create:
+ notice: "Local target created successfully"
+ destroy:
+ notice: "Local target deleted successfully"
+ edit:
+ title: "Edit local target"
+ form:
+ code: "Code must start with the selected target code followed by a dot"
+ description: "This description will be shown as help to users."
+ index:
+ new: "Create local target"
+ new:
+ title: "New local target"
+ update:
+ notice: "Local target updated successfully"
diff --git a/config/locales/es/activerecord.yml b/config/locales/es/activerecord.yml
index 4abe798c7..070ec9aa4 100644
--- a/config/locales/es/activerecord.yml
+++ b/config/locales/es/activerecord.yml
@@ -75,6 +75,9 @@ es:
sdg/goal:
one: "objetivo"
other: "objetivos"
+ sdg/local_target:
+ one: "meta localizada"
+ other: "metas localizadas"
sdg/target:
one: "meta"
other: "metas"
@@ -315,6 +318,13 @@ es:
code: "Código"
title: "Título"
description: "Descripción"
+ sdg/local_target:
+ code: "Código"
+ target_id: "Meta"
+ title: "Título"
+ sdg/local_target/translation:
+ title: "Título"
+ description: "Descripción"
sdg/target:
code: "Código"
title: "Título"
@@ -573,6 +583,10 @@ es:
attributes:
locale:
already_translated: Recurso ya traducido
+ sdg/local_target:
+ attributes:
+ code:
+ invalid: "debe empezar con el código de su meta seguido de un punto y terminar con un número"
messages:
translations_too_short: El obligatorio proporcionar una traducción como mínimo
record_invalid: "Error de validación: %{errors}"
diff --git a/config/locales/es/general.yml b/config/locales/es/general.yml
index 660cc7835..7f7b2b713 100644
--- a/config/locales/es/general.yml
+++ b/config/locales/es/general.yml
@@ -190,6 +190,7 @@ es:
local_census_records/import: la importación de registros del censo local
site_customization/content_block: el bloque
site_customization/page: la página
+ sdg/local_target: la meta localizada
geozones:
none: Toda la ciudad
layouts:
diff --git a/config/locales/es/sdg_management.yml b/config/locales/es/sdg_management.yml
index 58a378678..4ee4d8893 100644
--- a/config/locales/es/sdg_management.yml
+++ b/config/locales/es/sdg_management.yml
@@ -4,3 +4,19 @@ es:
title: "Contenido ODS"
menu:
sdg_content: "Objetivos y Metas"
+ local_targets:
+ create:
+ notice: "Meta localizada creada correctamente"
+ destroy:
+ notice: "Meta localizada eliminada correctamente"
+ edit:
+ title: "Editar meta localizada"
+ form:
+ code: "El código debe comenzar por el código de la meta seleccionada seguida de un '.'"
+ description: "Este texto ser mostrará como ayuda para los usuarios."
+ index:
+ new: "Crear meta localizada"
+ new:
+ title: "Nueva meta localizada"
+ update:
+ notice: "Meta localizada asctualizada correctamente"
diff --git a/config/routes/sdg_management.rb b/config/routes/sdg_management.rb
index 50b0f45d5..cad7cbe66 100644
--- a/config/routes/sdg_management.rb
+++ b/config/routes/sdg_management.rb
@@ -3,4 +3,5 @@ namespace :sdg_management do
resources :goals, only: [:index]
resources :targets, only: [:index]
+ resources :local_targets, except: [:show]
end
diff --git a/db/dev_seeds/sdg.rb b/db/dev_seeds/sdg.rb
index 01336730e..055f64e41 100644
--- a/db/dev_seeds/sdg.rb
+++ b/db/dev_seeds/sdg.rb
@@ -1,3 +1,32 @@
section "Creating Sustainable Development Goals" do
load Rails.root.join("db", "sdg.rb")
+
+ SDG::Target.sample(30).each do |target|
+ title = "Title for default locale"
+ description = "Description for default locale"
+ rand(2..3).times do |n|
+ local_target = SDG::LocalTarget.create!(code: "#{target.code}.#{n + 1}",
+ title: title,
+ description: description,
+ target: target)
+ random_locales.map do |locale|
+ Globalize.with_locale(locale) do
+ local_target.title = "Title for locale #{locale}"
+ local_target.description = "Description for locale #{locale}"
+ local_target.save!
+ end
+ end
+ end
+ end
+
+ relatables = [Debate, Proposal, Poll, Legislation::Process, Budget::Investment]
+ relatables.map { |relatable| relatable.sample(5) }.flatten.each do |relatable|
+ Array(SDG::Goal.sample(rand(1..3))).each do |goal|
+ target = goal.targets.sample
+ local_target = target.local_targets.sample
+ relatable.sdg_goals << goal
+ relatable.sdg_targets << target
+ relatable.sdg_local_targets << local_target if local_target.present?
+ end
+ end
end
diff --git a/db/migrate/20201123124006_create_sdg_local_targets.rb b/db/migrate/20201123124006_create_sdg_local_targets.rb
new file mode 100644
index 000000000..264a9cee9
--- /dev/null
+++ b/db/migrate/20201123124006_create_sdg_local_targets.rb
@@ -0,0 +1,22 @@
+class CreateSDGLocalTargets < ActiveRecord::Migration[5.2]
+ def change
+ create_table :sdg_local_targets do |t|
+ t.references :target
+ t.string :code
+ t.timestamps
+
+ t.index :code, unique: true
+ end
+
+ create_table :sdg_local_target_translations do |t|
+ t.bigint :sdg_local_target_id, null: false
+ t.string :locale, null: false
+ t.string :title
+ t.text :description
+ t.timestamps null: false
+
+ t.index :locale
+ t.index :sdg_local_target_id
+ end
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 5b1944278..601a3cdd3 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema.define(version: 2020_11_17_200945) do
+ActiveRecord::Schema.define(version: 2020_11_23_124006) do
# These are extensions that must be enabled in order to support this database
enable_extension "pg_trgm"
@@ -1304,6 +1304,26 @@ ActiveRecord::Schema.define(version: 2020_11_17_200945) do
t.index ["code"], name: "index_sdg_goals_on_code", unique: true
end
+ create_table "sdg_local_target_translations", force: :cascade do |t|
+ t.bigint "sdg_local_target_id", null: false
+ t.string "locale", null: false
+ t.string "title"
+ t.text "description"
+ t.datetime "created_at", null: false
+ t.datetime "updated_at", null: false
+ t.index ["locale"], name: "index_sdg_local_target_translations_on_locale"
+ t.index ["sdg_local_target_id"], name: "index_sdg_local_target_translations_on_sdg_local_target_id"
+ end
+
+ create_table "sdg_local_targets", force: :cascade do |t|
+ t.bigint "target_id"
+ t.string "code"
+ t.datetime "created_at", null: false
+ t.datetime "updated_at", null: false
+ t.index ["code"], name: "index_sdg_local_targets_on_code", unique: true
+ t.index ["target_id"], name: "index_sdg_local_targets_on_target_id"
+ end
+
create_table "sdg_relations", force: :cascade do |t|
t.string "related_sdg_type"
t.bigint "related_sdg_id"
diff --git a/spec/factories/sdg.rb b/spec/factories/sdg.rb
index 5e46622e8..9495a4114 100644
--- a/spec/factories/sdg.rb
+++ b/spec/factories/sdg.rb
@@ -6,4 +6,12 @@ FactoryBot.define do
factory :sdg_target, class: "SDG::Target" do
sequence(:code, 1) { |n| "#{n}.#{n}" }
end
+
+ factory :sdg_local_target, class: "SDG::LocalTarget" do
+ code { "1.1.1" }
+ sequence(:title) { |n| "Local Target #{n} title" }
+ sequence(:description) { |n| "Help for Local Target #{n}" }
+
+ target { SDG::Target[code.rpartition(".").first] }
+ end
end
diff --git a/spec/models/sdg/local_target_spec.rb b/spec/models/sdg/local_target_spec.rb
new file mode 100644
index 000000000..24c51c683
--- /dev/null
+++ b/spec/models/sdg/local_target_spec.rb
@@ -0,0 +1,77 @@
+require "rails_helper"
+
+describe SDG::LocalTarget do
+ describe "Concerns" do
+ it_behaves_like "globalizable", :sdg_local_target
+ end
+
+ it "is valid" do
+ expect(build(:sdg_local_target)).to be_valid
+ end
+
+ it "is not valid without a title" do
+ expect(build(:sdg_local_target, title: nil)).not_to be_valid
+ end
+
+ it "is not valid without a description" do
+ expect(build(:sdg_local_target, description: nil)).not_to be_valid
+ end
+
+ it "is not valid without a code" do
+ expect(build(:sdg_local_target, code: nil, target: SDG::Target[1.1])).not_to be_valid
+ end
+
+ it "is not valid when code does not include associated target code" do
+ local_target = build(:sdg_local_target, code: "1.6.1", target: SDG::Target[1.1])
+
+ expect(local_target).not_to be_valid
+ expect(local_target.errors.full_messages).to include "Code must start with the same code as its target followed by a dot and end with a number"
+ end
+
+ it "is not valid when local target code part is not a number" do
+ local_target = build(:sdg_local_target, code: "1.1.A", target: SDG::Target[1.1])
+
+ expect(local_target).not_to be_valid
+ expect(local_target.errors.full_messages).to include "Code must start with the same code as its target followed by a dot and end with a number"
+ end
+
+ it "is not valid if code is not unique" do
+ create(:sdg_local_target, code: "1.1.1")
+ local_target = build(:sdg_local_target, code: "1.1.1")
+
+ expect(local_target).not_to be_valid
+ expect(local_target.errors.full_messages).to include "Code has already been taken"
+ end
+
+ it "is not valid without a target" do
+ expect(build(:sdg_local_target, target: nil)).not_to be_valid
+ end
+
+ describe "#goal" do
+ it "returns the target goal" do
+ local_target = create(:sdg_local_target, code: "1.1.1")
+
+ expect(local_target.goal).to eq(SDG::Goal[1])
+ end
+ end
+
+ describe "#<=>" do
+ let(:local_target,) { create(:sdg_local_target, code: "10.B.10") }
+
+ it "compares using the target first" do
+ lesser_local_target = create(:sdg_local_target, code: "10.A.1")
+ greater_local_target = create(:sdg_local_target, code: "11.1.1")
+
+ expect(local_target).to be > lesser_local_target
+ expect(local_target).to be < greater_local_target
+ end
+
+ it "compares using the local target code when the target is the same" do
+ lesser_local_target = create(:sdg_local_target, code: "10.B.9")
+ greater_local_target = create(:sdg_local_target, code: "10.B.11")
+
+ expect(local_target).to be > lesser_local_target
+ expect(local_target).to be < greater_local_target
+ end
+ end
+end
diff --git a/spec/models/sdg/relatable_spec.rb b/spec/models/sdg/relatable_spec.rb
index 4b87971be..e6fd1b8d0 100644
--- a/spec/models/sdg/relatable_spec.rb
+++ b/spec/models/sdg/relatable_spec.rb
@@ -3,8 +3,10 @@ require "rails_helper"
describe SDG::Relatable do
let(:goal) { SDG::Goal[1] }
let(:target) { SDG::Target["1.2"] }
+ let(:local_target) { create(:sdg_local_target, code: "1.2.1") }
let(:another_goal) { SDG::Goal[2] }
let(:another_target) { SDG::Target["2.3"] }
+ let(:another_local_target) { create(:sdg_local_target, code: "2.3.1") }
let(:relatable) { create(:proposal) }
@@ -44,12 +46,32 @@ describe SDG::Relatable do
end
end
+ describe "#sdg_local_targets" do
+ it "can assign local targets to a model" do
+ relatable.sdg_local_targets = [local_target, another_local_target]
+
+ expect(SDG::Relation.count).to be 2
+ expect(SDG::Relation.first.relatable).to eq relatable
+ expect(SDG::Relation.last.relatable).to eq relatable
+ expect(SDG::Relation.first.related_sdg).to eq local_target
+ expect(SDG::Relation.last.related_sdg).to eq another_local_target
+ end
+
+ it "can obtain the list of local targets" do
+ relatable.sdg_local_targets = [local_target, another_local_target]
+
+ expect(relatable.reload.sdg_local_targets).to match_array [local_target, another_local_target]
+ end
+ end
+
describe "#related_sdgs" do
it "returns all related goals and targets" do
relatable.sdg_goals = [goal, another_goal]
relatable.sdg_targets = [target, another_target]
+ relatable.sdg_local_targets = [local_target, another_local_target]
- expect(relatable.reload.related_sdgs).to match_array [goal, another_goal, target, another_target]
+ related_sdgs = [goal, another_goal, target, another_target, local_target, another_local_target]
+ expect(relatable.reload.related_sdgs).to match_array related_sdgs
end
end
end
diff --git a/spec/system/sdg_management/local_targets_spec.rb b/spec/system/sdg_management/local_targets_spec.rb
new file mode 100644
index 000000000..437ec6c22
--- /dev/null
+++ b/spec/system/sdg_management/local_targets_spec.rb
@@ -0,0 +1,100 @@
+require "rails_helper"
+
+describe "Local Targets", :js do
+ before do
+ login_as(create(:administrator).user)
+ Setting["feature.sdg"] = true
+ end
+
+ describe "Index" do
+ scenario "Visit the index" do
+ create(:sdg_local_target, code: "1.1.1", title: "Affordable food")
+
+ visit sdg_management_goals_path
+ click_link "Local Targets"
+
+ expect(page).to have_title "SDG content - Local Targets"
+ within("table tr", text: "Affordable food") do
+ expect(page).to have_link "Edit"
+ expect(page).to have_link "Delete"
+ end
+ expect(page).to have_link "Create local target"
+ end
+
+ scenario "Show local targets grouped by target" do
+ target_1 = SDG::Target["1.1"]
+ target_1_local_target = create(:sdg_local_target, code: "1.1.1", target: target_1)
+ target_2 = SDG::Target["2.1"]
+ target_2_local_target = create(:sdg_local_target, code: "2.1.1", target: target_2)
+
+ visit sdg_management_local_targets_path
+
+ expect(target_1.title).to appear_before(target_1_local_target.title)
+ expect(target_1_local_target.title).to appear_before(target_2.title)
+ expect(target_2.title).to appear_before(target_2_local_target.title)
+ end
+ end
+
+ describe "Create" do
+ scenario "Shows succesful notice when form is fullfilled correctly" do
+ visit new_sdg_management_local_target_path
+
+ target = SDG::Target["1.1"]
+ select "#{target.code} #{target.title}", from: "Target"
+ fill_in "Code", with: "1.1.1"
+ fill_in "Title", with: "Local target title"
+ fill_in "Description", with: "Local target description"
+ click_button "Create local target"
+
+ expect(page).to have_content("Local target created successfully")
+ expect(page).to have_content("1.1.1")
+ end
+
+ scenario "Shows form errors when not valid" do
+ visit new_sdg_management_local_target_path
+
+ target = SDG::Target["2.3"]
+ code_and_title = "#{target.code} #{target.title}"
+ select code_and_title, from: "Target"
+ click_button "Create local target"
+
+ expect(page).to have_content("errors prevented this local target from being saved.")
+ expect(page).to have_select("Target", selected: code_and_title)
+ end
+ end
+
+ describe "Update" do
+ let!(:local_target) { create(:sdg_local_target, code: "1.1.1") }
+
+ scenario "Shows succesful notice when form is fullfilled correctly" do
+ visit edit_sdg_management_local_target_path(local_target)
+
+ fill_in "Title", with: "Local target title update"
+ click_button "Update local target"
+
+ expect(page).to have_content("Local target updated successfully")
+ expect(page).to have_content("Local target title update")
+ end
+
+ scenario "Shows form errors when changes are not valid" do
+ visit edit_sdg_management_local_target_path(local_target)
+
+ fill_in "Title", with: ""
+ click_button "Update local target"
+
+ expect(page).to have_content("1 error prevented this local target from being saved.")
+ end
+ end
+
+ describe "Destroy" do
+ scenario "Shows succesful notice when local target is destroyed successfully" do
+ create(:sdg_local_target, code: "1.1.1")
+ visit sdg_management_local_targets_path
+
+ accept_confirm { click_link "Delete" }
+
+ expect(page).to have_content("Local target deleted successfully")
+ expect(page).not_to have_content("1.1.1")
+ end
+ end
+end