diff --git a/app/assets/stylesheets/_consul_settings.scss b/app/assets/stylesheets/_consul_settings.scss index 54cf561d1747..34cc0b56cba2 100644 --- a/app/assets/stylesheets/_consul_settings.scss +++ b/app/assets/stylesheets/_consul_settings.scss @@ -123,3 +123,24 @@ $pagination-radius: $global-radius; $show-header-for-stacked: true; $tooltip-background-color: $brand; + +$sdg-colors: ( + 1: #e92140, + 2: #e0a33a, + 3: #4ca149, + 4: #c72032, + 5: #ef3e2e, + 6: #27c1ec, + 7: #fac312, + 8: #a61c45, + 9: #f26a30, + 10: #df1767, + 11: #f59d29, + 12: #bc8b2a, + 13: #437e47, + 14: #2296d7, + 15: #5db84a, + 16: #16699d, + 17: #15476d, + 18: #fff +); diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss index ecfd7c816edf..38c4185fcf28 100644 --- a/app/assets/stylesheets/application.scss +++ b/app/assets/stylesheets/application.scss @@ -28,5 +28,6 @@ @import "leaflet"; @import "sticky_overrides"; @import "admin/*"; +@import "sdg/**/*"; @import "sdg_management/*"; @import "widgets/**/*"; diff --git a/app/assets/stylesheets/layout.scss b/app/assets/stylesheets/layout.scss index 9e54789ff67a..9c6d2e4ba495 100644 --- a/app/assets/stylesheets/layout.scss +++ b/app/assets/stylesheets/layout.scss @@ -2790,7 +2790,8 @@ table { // ------------ .home-page, -.custom-page { +.custom-page, +.sdg-goal-show { a { @@ -2855,6 +2856,13 @@ table { } } + h2 { + &.title { + @include header-font-size(h3); + } + } + + h2, h3 { &.title { diff --git a/app/assets/stylesheets/mixins.scss b/app/assets/stylesheets/mixins.scss index 875a1b9130ee..99afec57a6e9 100644 --- a/app/assets/stylesheets/mixins.scss +++ b/app/assets/stylesheets/mixins.scss @@ -209,3 +209,11 @@ text-decoration: none; } } + +@mixin header-font-size($heading-tag) { + @each $size, $headers in $header-styles { + @include breakpoint($size) { + font-size: rem-calc(map-get(map-get($headers, $heading-tag), font-size)); + } + } +} diff --git a/app/assets/stylesheets/sdg/goals/show.css b/app/assets/stylesheets/sdg/goals/show.css new file mode 100644 index 000000000000..13b3842154a5 --- /dev/null +++ b/app/assets/stylesheets/sdg/goals/show.css @@ -0,0 +1,40 @@ +.sdg-goal-show { + @include grid-row; + + > :not(.feeds-participation) { + @include grid-column-gutter; + } + + .sdg-goal { + + > header { + color: #fff; + margin-top: $line-height / 2; + padding-left: rem-calc(24); + text-shadow: 0 0 1px $black; + } + + @each $code, $color in $sdg-colors { + &.sdg-goal-#{$code} { + > header { + background-color: $color; + } + } + } + } + + .feed-processes { + + .feed-content { + @include breakpoint(medium) { + @include grid-row-nest; + display: flex; + + > * { + @include grid-column-gutter; + width: 50%; + } + } + } + } +} diff --git a/app/components/sdg/goals/index_component.html.erb b/app/components/sdg/goals/index_component.html.erb new file mode 100644 index 000000000000..505d3f88948e --- /dev/null +++ b/app/components/sdg/goals/index_component.html.erb @@ -0,0 +1 @@ +<%= helpers.link_list(*goals.map { |goal| [goal.code_and_title, sdg_goal_path(goal.code)] }) %> diff --git a/app/components/sdg/goals/index_component.rb b/app/components/sdg/goals/index_component.rb new file mode 100644 index 000000000000..3d89921cad27 --- /dev/null +++ b/app/components/sdg/goals/index_component.rb @@ -0,0 +1,7 @@ +class SDG::Goals::IndexComponent < ApplicationComponent + attr_reader :goals + + def initialize(goals) + @goals = goals + end +end diff --git a/app/components/sdg/goals/show_component.html.erb b/app/components/sdg/goals/show_component.html.erb new file mode 100644 index 000000000000..39f92165ae3b --- /dev/null +++ b/app/components/sdg/goals/show_component.html.erb @@ -0,0 +1,19 @@ +<% provide(:title) { title } %> + +
+ <%= helpers.back_link_to sdg_goals_path %> + +
+
+

<%= title %>

+
+
+ + <%= render Widgets::Feeds::ParticipationComponent.new(feeds) %> + + <% if processes_feed %> +
+ <%= render Widgets::Feeds::ProcessesComponent.new(processes_feed) %> +
+ <% end %> +
diff --git a/app/components/sdg/goals/show_component.rb b/app/components/sdg/goals/show_component.rb new file mode 100644 index 000000000000..9cac70941e00 --- /dev/null +++ b/app/components/sdg/goals/show_component.rb @@ -0,0 +1,21 @@ +class SDG::Goals::ShowComponent < ApplicationComponent + attr_reader :goal + + def initialize(goal) + @goal = goal + end + + def feeds + SDG::Widget::Feed.for_goal(goal) + end + + def processes_feed + feeds.find { |feed| feed.kind == "processes" } + end + + private + + def title + goal.title + end +end diff --git a/app/components/widgets/feeds/debates_component.html.erb b/app/components/widgets/feeds/debates_component.html.erb index ad1ab2a6886b..cb8ec7566f6d 100644 --- a/app/components/widgets/feeds/debates_component.html.erb +++ b/app/components/widgets/feeds/debates_component.html.erb @@ -1,7 +1,9 @@ -
-
-

<%= t("welcome.feed.most_active.#{feed.kind}") %>

+
+
+

<%= t("welcome.feed.most_active.#{feed.kind}") %>

+
+
<% feed.items.each do |item| %>
"> <%= link_to item.title, url_for(item) %> @@ -9,5 +11,5 @@ <% end %>
- <%= link_to t("welcome.feed.see_all_debates"), debates_path, class: "see-all" %> -
+ <%= link_to t("welcome.feed.see_all_debates"), see_all_path, class: "see-all" %> +
diff --git a/app/components/widgets/feeds/debates_component.rb b/app/components/widgets/feeds/debates_component.rb index cdee45cbd16d..1412aeb89426 100644 --- a/app/components/widgets/feeds/debates_component.rb +++ b/app/components/widgets/feeds/debates_component.rb @@ -4,4 +4,18 @@ class Widgets::Feeds::DebatesComponent < ApplicationComponent def initialize(feed) @feed = feed end + + def see_all_path + polymorphic_path(feed.items.model, filters) + end + + private + + def filters + if feed.respond_to?(:goal) + { advanced_search: { goal: feed.goal.code }} + else + {} + end + end end diff --git a/app/components/widgets/feeds/processes_component.html.erb b/app/components/widgets/feeds/processes_component.html.erb index e492713d7118..132967436184 100644 --- a/app/components/widgets/feeds/processes_component.html.erb +++ b/app/components/widgets/feeds/processes_component.html.erb @@ -1,7 +1,9 @@ -
-
-

<%= t("welcome.feed.most_active.#{feed.kind}") %>

+
+
+

<%= t("welcome.feed.most_active.#{feed.kind}") %>

+
+
<% feed.items.each do |item| %> <%= link_to url_for(item) do %>
"> @@ -15,7 +17,7 @@

<%= t("welcome.feed.see_process") %>

<% end %> <% end %> - - <%= link_to t("welcome.feed.see_all_processes"), legislation_processes_path, class: "float-right see-all" %>
-
+ + <%= link_to t("welcome.feed.see_all_processes"), legislation_processes_path, class: "see-all" %> + diff --git a/app/components/widgets/feeds/proposals_component.html.erb b/app/components/widgets/feeds/proposals_component.html.erb index 2df0ab86150f..fd0c3b02ad0a 100644 --- a/app/components/widgets/feeds/proposals_component.html.erb +++ b/app/components/widgets/feeds/proposals_component.html.erb @@ -1,7 +1,9 @@ -
-
-

<%= t("welcome.feed.most_active.#{feed.kind}") %>

+
+
+

<%= t("welcome.feed.most_active.#{feed.kind}") %>

+
+
<% feed.items.each do |item| %>
row"> + <%= link_to t("welcome.feed.see_all_proposals"), see_all_path, class: "see-all" %> +
diff --git a/app/components/widgets/feeds/proposals_component.rb b/app/components/widgets/feeds/proposals_component.rb index d799c55160bd..6edbed128feb 100644 --- a/app/components/widgets/feeds/proposals_component.rb +++ b/app/components/widgets/feeds/proposals_component.rb @@ -1,7 +1,2 @@ -class Widgets::Feeds::ProposalsComponent < ApplicationComponent - attr_reader :feed - - def initialize(feed) - @feed = feed - end +class Widgets::Feeds::ProposalsComponent < Widgets::Feeds::DebatesComponent end diff --git a/app/controllers/sdg/goals_controller.rb b/app/controllers/sdg/goals_controller.rb new file mode 100644 index 000000000000..595add701bd4 --- /dev/null +++ b/app/controllers/sdg/goals_controller.rb @@ -0,0 +1,12 @@ +class SDG::GoalsController < ApplicationController + include FeatureFlags + feature_flag :sdg + load_and_authorize_resource find_by: :code, id_param: :code + + def index + @goals = @goals.order(:code) + end + + def show + end +end diff --git a/app/models/abilities/everyone.rb b/app/models/abilities/everyone.rb index 432099150a2f..5f9dcb3a57ce 100644 --- a/app/models/abilities/everyone.rb +++ b/app/models/abilities/everyone.rb @@ -27,6 +27,8 @@ def initialize(user) can [:read], Legislation::Question can [:read, :map, :share], Legislation::Proposal can [:search, :comments, :read, :create, :new_comment], Legislation::Annotation + + can :read, ::SDG::Goal end end end diff --git a/app/models/abilities/sdg/manager.rb b/app/models/abilities/sdg/manager.rb index 8d28e0f304dd..62237e49e89b 100644 --- a/app/models/abilities/sdg/manager.rb +++ b/app/models/abilities/sdg/manager.rb @@ -5,7 +5,6 @@ class SDG::Manager def initialize(user) merge Abilities::Common.new(user) - can :read, ::SDG::Goal can :read, ::SDG::Target end end diff --git a/app/models/sdg/widget/feed.rb b/app/models/sdg/widget/feed.rb new file mode 100644 index 000000000000..824e253017f5 --- /dev/null +++ b/app/models/sdg/widget/feed.rb @@ -0,0 +1,17 @@ +class SDG::Widget::Feed + attr_reader :feed, :goal + delegate :kind, to: :feed + + def initialize(feed, goal) + @feed = feed + @goal = goal + end + + def items + feed.items.by_goal(goal) + end + + def self.for_goal(goal) + ::Widget::Feed.active.map { |feed| new(feed, goal) } + end +end diff --git a/app/views/sdg/goals/index.html.erb b/app/views/sdg/goals/index.html.erb new file mode 100644 index 000000000000..7dfb01513f8c --- /dev/null +++ b/app/views/sdg/goals/index.html.erb @@ -0,0 +1 @@ +<%= render SDG::Goals::IndexComponent.new(@goals) %> diff --git a/app/views/sdg/goals/show.html.erb b/app/views/sdg/goals/show.html.erb new file mode 100644 index 000000000000..2eb018511796 --- /dev/null +++ b/app/views/sdg/goals/show.html.erb @@ -0,0 +1 @@ +<%= render SDG::Goals::ShowComponent.new(@goal) %> diff --git a/app/views/shared/_subnavigation.html.erb b/app/views/shared/_subnavigation.html.erb index 95553b4a4800..4a393e50a9e4 100644 --- a/app/views/shared/_subnavigation.html.erb +++ b/app/views/shared/_subnavigation.html.erb @@ -42,12 +42,20 @@ accesskey: "5" %> <% end %> + <% if feature?(:sdg) %> +
  • + <%= layout_menu_link_to t("layouts.header.sdg"), + sdg_goals_path, + controller_path.split("/").first == "sdg", + accesskey: "6" %> +
  • + <% end %> <% if feature?(:help_page) %>
  • <%= layout_menu_link_to t("layouts.header.help"), help_path, current_page?(help_path), - accesskey: "6" %> + accesskey: "7" %>
  • <% end %> diff --git a/app/views/welcome/index.html.erb b/app/views/welcome/index.html.erb index fafbcafbbc4e..94e3a04b2ba5 100644 --- a/app/views/welcome/index.html.erb +++ b/app/views/welcome/index.html.erb @@ -19,7 +19,7 @@
    <% if @cards.any? %>
    "> -

    <%= t("welcome.cards.title") %>

    +

    <%= t("welcome.cards.title") %>

    <%= render "shared/cards" %>
    diff --git a/config/locales/en/general.yml b/config/locales/en/general.yml index 5a47cdb64748..a1c7a886ce56 100644 --- a/config/locales/en/general.yml +++ b/config/locales/en/general.yml @@ -251,6 +251,7 @@ en: other: You have %{count} new notifications notifications: Notifications no_notifications: "You don't have new notifications" + sdg: "SDG" notifications: index: empty_notifications: You don't have new notifications. diff --git a/config/locales/es/general.yml b/config/locales/es/general.yml index 7f7b2b7139c4..b9da8ab10f62 100644 --- a/config/locales/es/general.yml +++ b/config/locales/es/general.yml @@ -251,6 +251,7 @@ es: other: Tienes %{count} notificaciones nuevas notifications: Notificaciones no_notifications: "No tienes notificaciones nuevas" + sdg: "ODS" notifications: index: empty_notifications: No tienes notificaciones nuevas. diff --git a/config/routes.rb b/config/routes.rb index aeb715684a31..acbdf90d143a 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -21,6 +21,7 @@ draw :poll draw :proposal draw :related_content + draw :sdg draw :sdg_management draw :tag draw :user diff --git a/config/routes/sdg.rb b/config/routes/sdg.rb new file mode 100644 index 000000000000..52525c685743 --- /dev/null +++ b/config/routes/sdg.rb @@ -0,0 +1,3 @@ +namespace :sdg do + resources :goals, param: :code, only: [:index, :show] +end diff --git a/spec/components/widgets/feeds/debates_component_spec.rb b/spec/components/widgets/feeds/debates_component_spec.rb new file mode 100644 index 000000000000..1bd90c40757d --- /dev/null +++ b/spec/components/widgets/feeds/debates_component_spec.rb @@ -0,0 +1,23 @@ +require "rails_helper" + +describe Widgets::Feeds::DebatesComponent, type: :component do + describe "#see_all_path" do + let(:feed) { Widget::Feed.new(kind: "debates") } + + it "points to the debates path for homepage feeds" do + component = Widgets::Feeds::DebatesComponent.new(feed) + + render_inline component + + expect(component.see_all_path).to eq "/debates" + end + + it "points to the debates filtered by goal for goal feeds" do + component = Widgets::Feeds::DebatesComponent.new(SDG::Widget::Feed.new(feed, SDG::Goal[6])) + + render_inline component + + expect(component.see_all_path).to eq "/debates?advanced_search#{CGI.escape("[goal]")}=6" + end + end +end diff --git a/spec/controllers/sdg/goals_spec.rb b/spec/controllers/sdg/goals_spec.rb new file mode 100644 index 000000000000..f72c283ddad8 --- /dev/null +++ b/spec/controllers/sdg/goals_spec.rb @@ -0,0 +1,13 @@ +require 'rails_helper' + +describe SDG::GoalsController do + context "featured disabled" do + before do + Setting["feature.sdg"] = false + end + + it "raises feature disabled" do + expect { get :index }.to raise_exception(FeatureFlags::FeatureDisabled) + end + end +end diff --git a/spec/models/abilities/administrator_spec.rb b/spec/models/abilities/administrator_spec.rb index f237964dc554..873768d87fd0 100644 --- a/spec/models/abilities/administrator_spec.rb +++ b/spec/models/abilities/administrator_spec.rb @@ -107,7 +107,6 @@ it { should be_able_to(:create, LocalCensusRecords::Import) } it { should be_able_to(:show, LocalCensusRecords::Import) } - it { should be_able_to(:read, SDG::Goal) } it { should be_able_to(:read, SDG::Target) } it { should be_able_to(:read, SDG::Manager) } diff --git a/spec/models/abilities/common_spec.rb b/spec/models/abilities/common_spec.rb index 33611601280e..b060f594490c 100644 --- a/spec/models/abilities/common_spec.rb +++ b/spec/models/abilities/common_spec.rb @@ -305,7 +305,6 @@ it { should be_able_to(:disable_recommendations, Proposal) } end - it { should_not be_able_to(:read, SDG::Goal) } it { should_not be_able_to(:read, SDG::Target) } it { should_not be_able_to(:read, SDG::Manager) } diff --git a/spec/models/abilities/everyone_spec.rb b/spec/models/abilities/everyone_spec.rb index a5399e25b902..81b82dcd5fa2 100644 --- a/spec/models/abilities/everyone_spec.rb +++ b/spec/models/abilities/everyone_spec.rb @@ -53,7 +53,7 @@ it { should_not be_able_to(:summary, create(:legislation_process, :open)) } it { should_not be_able_to(:summary, create(:legislation_process, :past, :not_published)) } - it { should_not be_able_to(:read, SDG::Goal) } + it { should be_able_to(:read, SDG::Goal) } it { should_not be_able_to(:read, SDG::Target) } it { should_not be_able_to(:read, SDG::Manager) } diff --git a/spec/models/abilities/moderator_spec.rb b/spec/models/abilities/moderator_spec.rb index 76258353409c..1b2bd4f48b23 100644 --- a/spec/models/abilities/moderator_spec.rb +++ b/spec/models/abilities/moderator_spec.rb @@ -109,7 +109,6 @@ it { should_not be_able_to(:comment_as_administrator, legislation_question) } end - it { should_not be_able_to(:read, SDG::Goal) } it { should_not be_able_to(:read, SDG::Target) } it { should_not be_able_to(:read, SDG::Manager) } diff --git a/spec/models/abilities/organization_spec.rb b/spec/models/abilities/organization_spec.rb index 2553b3f181f3..874a5ce40499 100644 --- a/spec/models/abilities/organization_spec.rb +++ b/spec/models/abilities/organization_spec.rb @@ -23,7 +23,6 @@ it { should be_able_to(:create, Comment) } it { should_not be_able_to(:vote, Comment) } - it { should_not be_able_to(:read, SDG::Goal) } it { should_not be_able_to(:read, SDG::Target) } it { should_not be_able_to(:read, SDG::Manager) } diff --git a/spec/models/abilities/sdg/manager.rb b/spec/models/abilities/sdg/manager.rb index 004ad0adad1f..43e1e20f4365 100644 --- a/spec/models/abilities/sdg/manager.rb +++ b/spec/models/abilities/sdg/manager.rb @@ -7,7 +7,6 @@ let(:user) { sdg_manager.user } let(:sdg_manager) { create(:sdg_manager) } - it { should be_able_to(:read, SDG::Goal) } it { should be_able_to(:read, SDG::Target) } it { should_not be_able_to(:read, SDG::Manager) } diff --git a/spec/models/abilities/valuator_spec.rb b/spec/models/abilities/valuator_spec.rb index 634dd7e9282f..328c1cb48634 100644 --- a/spec/models/abilities/valuator_spec.rb +++ b/spec/models/abilities/valuator_spec.rb @@ -40,7 +40,6 @@ it { should_not be_able_to(:comment_valuation, assigned_investment) } end - it { should_not be_able_to(:read, SDG::Goal) } it { should_not be_able_to(:read, SDG::Target) } it { should_not be_able_to(:read, SDG::Manager) } diff --git a/spec/routing/sdg_routes_spec.rb b/spec/routing/sdg_routes_spec.rb new file mode 100644 index 000000000000..24487fc46a1a --- /dev/null +++ b/spec/routing/sdg_routes_spec.rb @@ -0,0 +1,22 @@ +require "rails_helper" + +describe "SDG routes" do + it "maps goals to their code" do + expect(get("/sdg/goals/1")).to route_to( + controller: "sdg/goals", + action: "show", + code: "1" + ) + end + + it "requires using the code instead of the ID when the ID is different" do + SDG::Goal[2].destroy! + SDG::Goal.create!(code: 2) + + expect(get(sdg_goal_path(SDG::Goal[2].code))).to route_to( + controller: "sdg/goals", + action: "show", + code: "2" + ) + end +end diff --git a/spec/system/sdg/goals_spec.rb b/spec/system/sdg/goals_spec.rb new file mode 100644 index 000000000000..f6e326842b46 --- /dev/null +++ b/spec/system/sdg/goals_spec.rb @@ -0,0 +1,66 @@ +require "rails_helper" + +describe "SDG Goals" do + before do + Setting["feature.sdg"] = true + end + + describe "SDG navigation link" do + scenario "is not present when the feature is disabled" do + Setting["feature.sdg"] = false + + visit root_path + + within("#navigation_bar") { expect(page).not_to have_link "SDG" } + end + + scenario "routes to the goals index" do + visit root_path + within("#navigation_bar") { click_link "SDG" } + + expect(page).to have_current_path sdg_goals_path + end + end + + describe "Index" do + scenario "has links to SDGs" do + visit sdg_goals_path + + click_link "7. Affordable and Clean Energy" + + expect(page).to have_current_path sdg_goal_path(7) + end + end + + describe "Show" do + scenario "shows the SDG and its related content" do + goal = SDG::Goal[15] + + create(:debate, title: "Solar panels", sdg_goals: [SDG::Goal[7]]) + create(:debate, title: "Hunting ground", sdg_goals: [goal]) + create(:proposal, title: "Animal farm", sdg_goals: [goal]) + create(:proposal, title: "Sea farm", sdg_goals: [SDG::Goal[14]]) + create(:legislation_process, title: "Bullfighting regulations", sdg_goals: [goal]) + create(:legislation_process, title: "Tax regulations", sdg_goals: [SDG::Goal[10]]) + + visit sdg_goal_path(15) + + within(".sdg-goal header") { expect(page).to have_content "Life on Land" } + + within ".feed-proposals" do + expect(page).to have_content "Animal farm" + expect(page).not_to have_content "Sea farm" + end + + within ".feed-debates" do + expect(page).to have_content "Hunting ground" + expect(page).not_to have_content "Solar panels" + end + + within ".feed-processes" do + expect(page).to have_content "Bullfighting regulations" + expect(page).not_to have_content "Tax regulations" + end + end + end +end