diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss index b03ac1368..32577c576 100644 --- a/app/assets/stylesheets/application.scss +++ b/app/assets/stylesheets/application.scss @@ -31,4 +31,5 @@ @import "admin/*"; @import "sdg/**/*"; @import "sdg_management/*"; +@import "sdg_management/**/*"; @import "widgets/**/*"; diff --git a/app/assets/stylesheets/layout.scss b/app/assets/stylesheets/layout.scss index 6e46858cd..966d046c0 100644 --- a/app/assets/stylesheets/layout.scss +++ b/app/assets/stylesheets/layout.scss @@ -91,12 +91,8 @@ a { } .button { + @extend %button; background: $brand; - font-size: $base-font-size; - - &:hover { - text-decoration: none !important; - } &.warning, &.warning:hover { diff --git a/app/assets/stylesheets/mixins.scss b/app/assets/stylesheets/mixins.scss index 99afec57a..906c0bc3f 100644 --- a/app/assets/stylesheets/mixins.scss +++ b/app/assets/stylesheets/mixins.scss @@ -199,17 +199,26 @@ } } -@mixin hollow-button($color: $link) { - @include button($style: hollow, $background: $color); +%button { font-size: $base-font-size; - margin-bottom: 0; &:focus, &:hover { - text-decoration: none; + text-decoration: none !important; } } +@mixin regular-button($color: $brand) { + @include button($background: $color); + @extend %button; +} + +@mixin hollow-button($color: $link) { + @include button($style: hollow, $background: $color); + @extend %button; + margin-bottom: 0; +} + @mixin header-font-size($heading-tag) { @each $size, $headers in $header-styles { @include breakpoint($size) { diff --git a/app/assets/stylesheets/sdg_management/homepage/show.scss b/app/assets/stylesheets/sdg_management/homepage/show.scss new file mode 100644 index 000000000..098e8e311 --- /dev/null +++ b/app/assets/stylesheets/sdg_management/homepage/show.scss @@ -0,0 +1,11 @@ +.phase-cards { + > header { + align-items: flex-start; + display: flex; + + a { + @include regular-button; + margin-left: auto; + } + } +} diff --git a/app/assets/stylesheets/sdg_management/menu.scss b/app/assets/stylesheets/sdg_management/menu.scss index 744234610..542ce4177 100644 --- a/app/assets/stylesheets/sdg_management/menu.scss +++ b/app/assets/stylesheets/sdg_management/menu.scss @@ -7,4 +7,12 @@ mask-image: image-url("sdg.svg"); } + + .homepage-link { + @include has-fa-icon(home, solid); + + &::before { + @extend %admin-menu-icon; + } + } } diff --git a/app/components/admin/table_actions_component.rb b/app/components/admin/table_actions_component.rb index 990f54c74..9a5bf5134 100644 --- a/app/components/admin/table_actions_component.rb +++ b/app/components/admin/table_actions_component.rb @@ -1,6 +1,7 @@ class Admin::TableActionsComponent < ApplicationComponent include TableActionLink attr_reader :record, :options + delegate :namespace, to: :helpers def initialize(record = nil, **options) @record = record @@ -18,7 +19,7 @@ class Admin::TableActionsComponent < ApplicationComponent end def edit_path - options[:edit_path] || admin_polymorphic_path(record, action: :edit) + options[:edit_path] || namespaced_polymorphic_path(namespace, record, action: :edit) end def edit_options @@ -30,7 +31,7 @@ class Admin::TableActionsComponent < ApplicationComponent end def destroy_path - options[:destroy_path] || admin_polymorphic_path(record) + options[:destroy_path] || namespaced_polymorphic_path(namespace, record) end def destroy_options diff --git a/app/components/admin/widget/cards/edit_component.html.erb b/app/components/admin/widget/cards/edit_component.html.erb new file mode 100644 index 000000000..745988b90 --- /dev/null +++ b/app/components/admin/widget/cards/edit_component.html.erb @@ -0,0 +1,3 @@ +<%= back_link_to index_path %> +<%= header %> +<%= render "admin/widget/cards/form", card: card %> diff --git a/app/components/admin/widget/cards/edit_component.rb b/app/components/admin/widget/cards/edit_component.rb new file mode 100644 index 000000000..8dd728ffe --- /dev/null +++ b/app/components/admin/widget/cards/edit_component.rb @@ -0,0 +1,19 @@ +class Admin::Widget::Cards::EditComponent < ApplicationComponent + include Header + attr_reader :card, :index_path + + def initialize(card, index_path:) + @card = card + @index_path = index_path + end + + private + + def title + if card.header? + t("admin.homepage.edit.header_title") + else + t("admin.homepage.edit.card_title") + end + end +end diff --git a/app/components/admin/widget/cards/new_component.html.erb b/app/components/admin/widget/cards/new_component.html.erb new file mode 100644 index 000000000..745988b90 --- /dev/null +++ b/app/components/admin/widget/cards/new_component.html.erb @@ -0,0 +1,3 @@ +<%= back_link_to index_path %> +<%= header %> +<%= render "admin/widget/cards/form", card: card %> diff --git a/app/components/admin/widget/cards/new_component.rb b/app/components/admin/widget/cards/new_component.rb new file mode 100644 index 000000000..810ade51a --- /dev/null +++ b/app/components/admin/widget/cards/new_component.rb @@ -0,0 +1,19 @@ +class Admin::Widget::Cards::NewComponent < ApplicationComponent + include Header + attr_reader :card, :index_path + + def initialize(card, index_path:) + @card = card + @index_path = index_path + end + + private + + def title + if card.header? + t("admin.homepage.new.header_title") + else + t("admin.homepage.new.card_title") + end + end +end diff --git a/app/components/application_component.rb b/app/components/application_component.rb index 3c5f3e47a..25c553243 100644 --- a/app/components/application_component.rb +++ b/app/components/application_component.rb @@ -1,3 +1,4 @@ class ApplicationComponent < ViewComponent::Base include SettingsHelper + delegate :back_link_to, to: :helpers end diff --git a/app/components/sdg/goals/show_component.rb b/app/components/sdg/goals/show_component.rb index 410c0e76c..1ba874a59 100644 --- a/app/components/sdg/goals/show_component.rb +++ b/app/components/sdg/goals/show_component.rb @@ -1,6 +1,5 @@ class SDG::Goals::ShowComponent < ApplicationComponent attr_reader :goal - delegate :back_link_to, to: :helpers def initialize(goal) @goal = goal diff --git a/app/components/sdg_management/homepage/show_component.html.erb b/app/components/sdg_management/homepage/show_component.html.erb new file mode 100644 index 000000000..53046b11b --- /dev/null +++ b/app/components/sdg_management/homepage/show_component.html.erb @@ -0,0 +1,15 @@ +<%= header %> + +<% phases.each do |phase| %> +
+
+

<%= phase.title %>

+ <%= link_to create_card_text(phase), new_sdg_management_sdg_phase_widget_card_path(phase) %> +
+ + <%= render Admin::Widget::Cards::TableComponent.new( + phase.cards, + no_cards_message: no_cards_message + ) %> +
+<% end %> diff --git a/app/components/sdg_management/homepage/show_component.rb b/app/components/sdg_management/homepage/show_component.rb new file mode 100644 index 000000000..e5dbaf6cf --- /dev/null +++ b/app/components/sdg_management/homepage/show_component.rb @@ -0,0 +1,23 @@ +class SDGManagement::Homepage::ShowComponent < ApplicationComponent + include Header + + attr_reader :phases + + def initialize(phases) + @phases = phases + end + + private + + def title + t("sdg_management.homepage.title") + end + + def create_card_text(phase) + t("sdg_management.homepage.create_card", phase: phase.title.downcase) + end + + def no_cards_message + t("sdg_management.homepage.no_cards") + end +end diff --git a/app/components/sdg_management/local_targets/form_component.rb b/app/components/sdg_management/local_targets/form_component.rb index 6deadbb6e..23b6aff46 100644 --- a/app/components/sdg_management/local_targets/form_component.rb +++ b/app/components/sdg_management/local_targets/form_component.rb @@ -1,5 +1,4 @@ class SDGManagement::LocalTargets::FormComponent < ApplicationComponent - delegate :back_link_to, to: :helpers include Header include TranslatableFormHelper include GlobalizeHelper diff --git a/app/components/sdg_management/local_targets/index_component.rb b/app/components/sdg_management/local_targets/index_component.rb index 1d4a435f9..90ea383b8 100644 --- a/app/components/sdg_management/local_targets/index_component.rb +++ b/app/components/sdg_management/local_targets/index_component.rb @@ -22,9 +22,6 @@ class SDGManagement::LocalTargets::IndexComponent < ApplicationComponent 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)) + render Admin::TableActionsComponent.new(local_target) end end diff --git a/app/components/sdg_management/menu_component.rb b/app/components/sdg_management/menu_component.rb index a1e156a73..543bb3863 100644 --- a/app/components/sdg_management/menu_component.rb +++ b/app/components/sdg_management/menu_component.rb @@ -4,11 +4,15 @@ class SDGManagement::MenuComponent < ApplicationComponent private def links - [goals_link, *relatable_links] + [goals_link, homepage_link, *relatable_links] end def goals_link - [t("sdg_management.menu.sdg_content"), sdg_management_goals_path, sdg?, class: "goals-link"] + [item_text("sdg_content"), sdg_management_goals_path, sdg?, class: "goals-link"] + end + + def homepage_link + [item_text("sdg_homepage"), sdg_management_homepage_path, homepage?, class: "homepage-link"] end def relatable_links @@ -16,7 +20,7 @@ class SDGManagement::MenuComponent < ApplicationComponent next unless SDG::ProcessEnabled.new(type).enabled? [ - t("sdg_management.menu.#{table_name(type)}"), + item_text(table_name(type)), relatable_type_path(type), controller_name == "relations" && params[:relatable_type] == type.tableize, class: "#{table_name(type).tr("_", "-")}-link" @@ -28,6 +32,10 @@ class SDGManagement::MenuComponent < ApplicationComponent %w[goals targets local_targets].include?(controller_name) end + def homepage? + controller_name == "homepage" + end + def relatable_type_path(type) { controller: "sdg_management/relations", @@ -39,4 +47,8 @@ class SDGManagement::MenuComponent < ApplicationComponent def table_name(type) type.constantize.table_name end + + def item_text(item) + t("sdg_management.menu.#{item}") + end end diff --git a/app/controllers/sdg_management/cards_controller.rb b/app/controllers/sdg_management/cards_controller.rb new file mode 100644 index 000000000..f3af9ed19 --- /dev/null +++ b/app/controllers/sdg_management/cards_controller.rb @@ -0,0 +1,13 @@ +class SDGManagement::CardsController < SDGManagement::BaseController + include Admin::Widget::CardsActions + helper_method :index_path + + load_and_authorize_resource :phase, class: "SDG::Phase", id_param: "sdg_phase_id" + load_and_authorize_resource :card, through: :phase, class: "Widget::Card" + + private + + def index_path + sdg_management_homepage_path + end +end diff --git a/app/controllers/sdg_management/homepage_controller.rb b/app/controllers/sdg_management/homepage_controller.rb new file mode 100644 index 000000000..4f9d67109 --- /dev/null +++ b/app/controllers/sdg_management/homepage_controller.rb @@ -0,0 +1,5 @@ +class SDGManagement::HomepageController < SDGManagement::BaseController + def show + @phases = SDG::Phase.accessible_by(current_ability).order(:kind) + end +end diff --git a/app/helpers/admin_helper.rb b/app/helpers/admin_helper.rb index dd5d9c6d4..45c508107 100644 --- a/app/helpers/admin_helper.rb +++ b/app/helpers/admin_helper.rb @@ -39,9 +39,7 @@ module AdminHelper user_roles(user).join(", ") end - private - - def namespace - controller.class.name.split("::").first.underscore - end + def namespace + controller.class.name.split("::").first.underscore + end end diff --git a/app/models/abilities/everyone.rb b/app/models/abilities/everyone.rb index 5f9dcb3a5..b32e115e3 100644 --- a/app/models/abilities/everyone.rb +++ b/app/models/abilities/everyone.rb @@ -29,6 +29,7 @@ module Abilities can [:search, :comments, :read, :create, :new_comment], Legislation::Annotation can :read, ::SDG::Goal + can :read, ::SDG::Phase end end end diff --git a/app/models/abilities/sdg/manager.rb b/app/models/abilities/sdg/manager.rb index 7dcbf6769..a3264c9f3 100644 --- a/app/models/abilities/sdg/manager.rb +++ b/app/models/abilities/sdg/manager.rb @@ -6,5 +6,7 @@ class Abilities::SDG::Manager can :read, ::SDG::Target can :manage, ::SDG::LocalTarget + can [:read, :update, :destroy], Widget::Card, cardable_type: "SDG::Phase" + can(:create, Widget::Card) { |card| card.cardable_type == "SDG::Phase" } end end diff --git a/app/models/concerns/cardable.rb b/app/models/concerns/cardable.rb new file mode 100644 index 000000000..e18435e72 --- /dev/null +++ b/app/models/concerns/cardable.rb @@ -0,0 +1,7 @@ +module Cardable + extend ActiveSupport::Concern + + included do + has_many :cards, class_name: "Widget::Card", as: :cardable, dependent: :destroy + end +end diff --git a/app/models/sdg/phase.rb b/app/models/sdg/phase.rb new file mode 100644 index 000000000..3796dcf21 --- /dev/null +++ b/app/models/sdg/phase.rb @@ -0,0 +1,13 @@ +class SDG::Phase < ApplicationRecord + include Cardable + enum kind: %w[sensitization planning monitoring] + validates :kind, presence: true, uniqueness: true + + def self.[](kind) + find_by!(kind: kind) + end + + def title + self.class.human_attribute_name("kind.#{kind}") + end +end diff --git a/app/models/site_customization/page.rb b/app/models/site_customization/page.rb index 03c4bcccc..2c2c84fd5 100644 --- a/app/models/site_customization/page.rb +++ b/app/models/site_customization/page.rb @@ -1,10 +1,6 @@ class SiteCustomization::Page < ApplicationRecord VALID_STATUSES = %w[draft published].freeze - has_many :cards, - class_name: "Widget::Card", - foreign_key: "site_customization_page_id", - inverse_of: :page - + include Cardable translates :title, touch: true translates :subtitle, touch: true translates :content, touch: true diff --git a/app/models/widget/card.rb b/app/models/widget/card.rb index 0b8ce68a3..fa8b2f8e7 100644 --- a/app/models/widget/card.rb +++ b/app/models/widget/card.rb @@ -1,9 +1,6 @@ class Widget::Card < ApplicationRecord include Imageable - belongs_to :page, - class_name: "SiteCustomization::Page", - foreign_key: "site_customization_page_id", - inverse_of: :cards + belongs_to :cardable, polymorphic: true translates :label, touch: true translates :title, touch: true @@ -18,6 +15,6 @@ class Widget::Card < ApplicationRecord end def self.body - where(header: false, site_customization_page_id: nil).order(:created_at) + where(header: false, cardable_id: nil).order(:created_at) end end diff --git a/app/views/admin/widget/cards/_form.html.erb b/app/views/admin/widget/cards/_form.html.erb index 9b0ef8c34..a2bf520a5 100644 --- a/app/views/admin/widget/cards/_form.html.erb +++ b/app/views/admin/widget/cards/_form.html.erb @@ -1,7 +1,7 @@ -<%= render "shared/globalize_locales", resource: @card %> +<%= render "shared/globalize_locales", resource: card %> -<%= translatable_form_for [:admin, @page, @card] do |f| %> - <%= render "shared/errors", resource: @card %> +<%= translatable_form_for [namespace, card.cardable, card] do |f| %> + <%= render "shared/errors", resource: card %>
<%= f.translatable_fields do |translations_form| %> @@ -30,7 +30,7 @@
- <% unless @card.header? %> + <% unless card.header? %>
<%= f.label :columns %>

<%= t("admin.site_customization.pages.cards.columns_help") %>

@@ -41,16 +41,16 @@ <% end %>
- <%= f.hidden_field :header, value: @card.header? %> + <%= f.hidden_field :header, value: card.header? %>
- <%= render "images/nested_image", imageable: @card, f: f %> + <%= render "images/nested_image", imageable: card, f: f %>
<%= f.submit( - t("admin.homepage.#{admin_submit_action(@card)}.#{@card.header? ? "submit_header" : "submit_card"}"), + t("admin.homepage.#{admin_submit_action(card)}.#{card.header? ? "submit_header" : "submit_card"}"), class: "button success" ) %>
diff --git a/app/views/admin/widget/cards/edit.html.erb b/app/views/admin/widget/cards/edit.html.erb index f4f751fd1..c95b6fb7d 100644 --- a/app/views/admin/widget/cards/edit.html.erb +++ b/app/views/admin/widget/cards/edit.html.erb @@ -1,11 +1 @@ -<%= back_link_to index_path %> - -

- <% if @card.header? %> - <%= t("admin.homepage.edit.header_title") %> - <% else %> - <%= t("admin.homepage.edit.card_title") %> - <% end %> -

- -<%= render "admin/widget/cards/form" %> +<%= render Admin::Widget::Cards::EditComponent.new(@card, index_path: index_path) %> diff --git a/app/views/admin/widget/cards/new.html.erb b/app/views/admin/widget/cards/new.html.erb index f15f0172e..d25857cef 100644 --- a/app/views/admin/widget/cards/new.html.erb +++ b/app/views/admin/widget/cards/new.html.erb @@ -1,11 +1 @@ -<%= back_link_to index_path %> - -

- <% if @card.header? %> - <%= t("admin.homepage.new.header_title") %> - <% else %> - <%= t("admin.homepage.new.card_title") %> - <% end %> -

- -<%= render "admin/widget/cards/form" %> +<%= render Admin::Widget::Cards::NewComponent.new(@card, index_path: index_path) %> diff --git a/app/views/sdg_management/homepage/show.html.erb b/app/views/sdg_management/homepage/show.html.erb new file mode 100644 index 000000000..f76456364 --- /dev/null +++ b/app/views/sdg_management/homepage/show.html.erb @@ -0,0 +1 @@ +<%= render SDGManagement::Homepage::ShowComponent.new(@phases) %> diff --git a/config/initializers/routes_hierarchy.rb b/config/initializers/routes_hierarchy.rb index fd096a64a..9b5cded6f 100644 --- a/config/initializers/routes_hierarchy.rb +++ b/config/initializers/routes_hierarchy.rb @@ -16,14 +16,23 @@ module ActionDispatch::Routing::UrlFor end def admin_polymorphic_path(resource, options = {}) + namespaced_polymorphic_path(:admin, resource, options) + end + + def sdg_management_polymorphic_path(resource, options = {}) + namespaced_polymorphic_path(:sdg_management, resource, options) + end + + def namespaced_polymorphic_path(namespace, resource, options = {}) if %w[Budget::Group Budget::Heading Poll::Booth Poll::BoothAssignment Poll::Officer - Poll::Question Poll::Question::Answer::Video Poll::Shift].include?(resource.class.name) + Poll::Question Poll::Question::Answer::Video Poll::Shift + SDG::LocalTarget].include?(resource.class.name) resolve = resolve_for(resource) resolve_options = resolve.pop - polymorphic_path([:admin, *resolve], options.merge(resolve_options)) + polymorphic_path([namespace, *resolve], options.merge(resolve_options)) else - polymorphic_path([:admin, *resource_hierarchy_for(resource)], options) + polymorphic_path([namespace, *resource_hierarchy_for(resource)], options) end end diff --git a/config/locales/en/activerecord.yml b/config/locales/en/activerecord.yml index 84264fbce..eae5e99a0 100644 --- a/config/locales/en/activerecord.yml +++ b/config/locales/en/activerecord.yml @@ -336,6 +336,10 @@ en: sdg/local_target/translation: title: "Title" description: "Description" + sdg/phase/kind: + sensitization: "Sensitization" + planning: "Planning" + monitoring: "Monitoring" sdg/target: code: "Code" title: "Title" diff --git a/config/locales/en/sdg_management.yml b/config/locales/en/sdg_management.yml index bacc84bbb..f4736fe8e 100644 --- a/config/locales/en/sdg_management.yml +++ b/config/locales/en/sdg_management.yml @@ -4,6 +4,10 @@ en: edit: "Manage goals and targets" header: title: "SDG content" + homepage: + title: "Homepage configuration" + create_card: "Create %{phase} card" + no_cards: "There are no cards for this phase" menu: budget_investments: "Participatory budgets" debates: "Debates" @@ -11,6 +15,7 @@ en: polls: "Polls" proposals: "Proposals" sdg_content: "Goals and Targets" + sdg_homepage: "SDG homepage" local_targets: create: notice: "Local target created successfully" diff --git a/config/locales/es/activerecord.yml b/config/locales/es/activerecord.yml index d59c643c4..788576928 100644 --- a/config/locales/es/activerecord.yml +++ b/config/locales/es/activerecord.yml @@ -331,6 +331,10 @@ es: sdg/local_target/translation: title: "Título" description: "Descripción" + sdg/phase/kind: + sensitization: "Sensibilización" + planning: "Planificación" + monitoring: "Seguimiento" sdg/target: code: "Código" title: "Título" diff --git a/config/locales/es/sdg_management.yml b/config/locales/es/sdg_management.yml index 23c10bd4a..488599bc5 100644 --- a/config/locales/es/sdg_management.yml +++ b/config/locales/es/sdg_management.yml @@ -4,6 +4,10 @@ es: edit: "Asignar objetivos y metas" header: title: "Contenido ODS" + homepage: + title: "Configuración de la página de inicio" + create_card: "Crear tarjeta de %{phase}" + no_cards: "No hay tarjetas para esta fase" menu: budget_investments: "Presupuestos participativos" debates: "Debates" @@ -11,6 +15,7 @@ es: polls: "Votaciones" proposals: "Propuestas" sdg_content: "Objetivos y Metas" + sdg_homepage: "Página de inicio ODS" local_targets: create: notice: "Meta localizada creada correctamente" diff --git a/config/routes/admin.rb b/config/routes/admin.rb index 4bc1f098f..197707c97 100644 --- a/config/routes/admin.rb +++ b/config/routes/admin.rb @@ -271,7 +271,7 @@ resolve "Audit" do |audit| end resolve "Widget::Card" do |card, options| - [*resource_hierarchy_for(card.page), card] + [*resource_hierarchy_for(card.cardable), card] end resolve "Budget::Group" do |group, options| diff --git a/config/routes/sdg_management.rb b/config/routes/sdg_management.rb index 6f303b45c..c8816ad56 100644 --- a/config/routes/sdg_management.rb +++ b/config/routes/sdg_management.rb @@ -4,6 +4,11 @@ namespace :sdg_management do resources :goals, only: [:index] resources :targets, only: [:index] resources :local_targets, except: [:show] + resource :homepage, controller: :homepage, only: [:show] + + resources :phases, only: [], as: :sdg_phases do + resources :cards, except: [:index, :show], as: :widget_cards + end types = SDG::Related::RELATABLE_TYPES.map(&:tableize) types_constraint = /#{types.join("|")}/ @@ -17,3 +22,7 @@ namespace :sdg_management do get "#{type}/:id/edit", to: "relations#edit", as: "edit_#{type.singularize}" end end + +resolve "SDG::LocalTarget" do |target, options| + [:local_target, options.merge(id: target)] +end diff --git a/db/dev_seeds/sdg.rb b/db/dev_seeds/sdg.rb index 055f64e41..f7fe40a3a 100644 --- a/db/dev_seeds/sdg.rb +++ b/db/dev_seeds/sdg.rb @@ -30,3 +30,9 @@ section "Creating Sustainable Development Goals" do end end end + +section "Creating SDG homepage cards" do + SDG::Phase.all.each do |phase| + Widget::Card.create!(cardable: phase, title: "#{phase.title} card") + end +end diff --git a/db/migrate/20210106132909_make_cards_polymorphic.rb b/db/migrate/20210106132909_make_cards_polymorphic.rb new file mode 100644 index 000000000..fe49baa92 --- /dev/null +++ b/db/migrate/20210106132909_make_cards_polymorphic.rb @@ -0,0 +1,8 @@ +class MakeCardsPolymorphic < ActiveRecord::Migration[5.2] + def change + change_table :widget_cards do |t| + t.rename :site_customization_page_id, :cardable_id + t.string :cardable_type, default: "SiteCustomization::Page" + end + end +end diff --git a/db/migrate/20210107125458_create_sdg_phases.rb b/db/migrate/20210107125458_create_sdg_phases.rb new file mode 100644 index 000000000..7b4e19b89 --- /dev/null +++ b/db/migrate/20210107125458_create_sdg_phases.rb @@ -0,0 +1,9 @@ +class CreateSDGPhases < ActiveRecord::Migration[5.2] + def change + create_table :sdg_phases do |t| + t.integer :kind, null: false + t.index :kind, unique: true + t.timestamps + end + end +end diff --git a/db/schema.rb b/db/schema.rb index 6a67f083f..94cfb14ca 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_12_16_132642) do +ActiveRecord::Schema.define(version: 2021_01_07_125458) do # These are extensions that must be enabled in order to support this database enable_extension "pg_trgm" @@ -1333,6 +1333,13 @@ ActiveRecord::Schema.define(version: 2020_12_16_132642) do t.index ["user_id"], name: "index_sdg_managers_on_user_id", unique: true end + create_table "sdg_phases", force: :cascade do |t| + t.integer "kind", null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["kind"], name: "index_sdg_phases_on_kind", unique: true + end + create_table "sdg_relations", force: :cascade do |t| t.string "related_sdg_type" t.bigint "related_sdg_id" @@ -1647,9 +1654,10 @@ ActiveRecord::Schema.define(version: 2020_12_16_132642) do t.boolean "header", default: false t.datetime "created_at", null: false t.datetime "updated_at", null: false - t.integer "site_customization_page_id" + t.integer "cardable_id" t.integer "columns", default: 4 - t.index ["site_customization_page_id"], name: "index_widget_cards_on_site_customization_page_id" + t.string "cardable_type", default: "SiteCustomization::Page" + t.index ["cardable_id"], name: "index_widget_cards_on_cardable_id" end create_table "widget_feeds", id: :serial, force: :cascade do |t| diff --git a/db/sdg.rb b/db/sdg.rb index f81741e77..a7cef491c 100644 --- a/db/sdg.rb +++ b/db/sdg.rb @@ -23,3 +23,5 @@ end ].each do |code| SDG::Target.where(code: code, goal: SDG::Goal.find_by!(code: code.split(".").first)).first_or_create! end + +SDG::Phase.kinds.values.each { |kind| SDG::Phase.where(kind: kind).first_or_create! } diff --git a/spec/components/admin/budgets/table_actions_component_spec.rb b/spec/components/admin/budgets/table_actions_component_spec.rb index 05312853c..36aeea537 100644 --- a/spec/components/admin/budgets/table_actions_component_spec.rb +++ b/spec/components/admin/budgets/table_actions_component_spec.rb @@ -4,6 +4,10 @@ describe Admin::Budgets::TableActionsComponent, type: :component do let(:budget) { create(:budget) } let(:component) { Admin::Budgets::TableActionsComponent.new(budget) } + before do + allow(ViewComponent::Base).to receive(:test_controller).and_return("Admin::BaseController") + end + it "renders links to edit budget, manage investments and edit groups and manage ballots" do render_inline component diff --git a/spec/components/admin/poll/officers/officers_component_spec.rb b/spec/components/admin/poll/officers/officers_component_spec.rb index f80fbb026..dab2dd04e 100644 --- a/spec/components/admin/poll/officers/officers_component_spec.rb +++ b/spec/components/admin/poll/officers/officers_component_spec.rb @@ -6,6 +6,10 @@ describe Admin::Poll::Officers::OfficersComponent, type: :component do let(:officers) { [existing_officer, new_officer] } let(:component) { Admin::Poll::Officers::OfficersComponent.new(officers) } + before do + allow(ViewComponent::Base).to receive(:test_controller).and_return("Admin::BaseController") + end + it "renders as many rows as officers" do render_inline component diff --git a/spec/components/admin/roles/table_actions_component_spec.rb b/spec/components/admin/roles/table_actions_component_spec.rb index 42382cf8b..a5bb0cd91 100644 --- a/spec/components/admin/roles/table_actions_component_spec.rb +++ b/spec/components/admin/roles/table_actions_component_spec.rb @@ -3,6 +3,10 @@ require "rails_helper" describe Admin::Roles::TableActionsComponent, type: :component do let(:user) { create(:user) } + before do + allow(ViewComponent::Base).to receive(:test_controller).and_return("Admin::BaseController") + end + it "renders link to add the role for new records" do render_inline Admin::Roles::TableActionsComponent.new(user.build_manager) diff --git a/spec/components/admin/table_actions_component_spec.rb b/spec/components/admin/table_actions_component_spec.rb index c4630c645..a643a6da1 100644 --- a/spec/components/admin/table_actions_component_spec.rb +++ b/spec/components/admin/table_actions_component_spec.rb @@ -3,6 +3,10 @@ require "rails_helper" describe Admin::TableActionsComponent, type: :component do let(:record) { create(:banner) } + before do + allow(ViewComponent::Base).to receive(:test_controller).and_return("Admin::BaseController") + end + it "renders links to edit and destroy a record by default" do render_inline Admin::TableActionsComponent.new(record) @@ -65,4 +69,18 @@ describe Admin::TableActionsComponent, type: :component do expect(page).to have_link "Edit" expect(page).to have_link "Delete" end + + context "different namespace" do + before do + allow(ViewComponent::Base).to receive(:test_controller).and_return("SDGManagement::BaseController") + end + + it "generates links to different namespaces" do + render_inline Admin::TableActionsComponent.new(create(:sdg_local_target)) + + expect(page).to have_css "a", count: 2 + expect(page).to have_css "a[href^='/sdg_management/'][href*='edit']", text: "Edit" + expect(page).to have_css "a[href^='/sdg_management/'][data-method='delete']", text: "Delete" + end + end end diff --git a/spec/components/sdg_management/menu_component_spec.rb b/spec/components/sdg_management/menu_component_spec.rb index 707f892ab..4d29fe677 100644 --- a/spec/components/sdg_management/menu_component_spec.rb +++ b/spec/components/sdg_management/menu_component_spec.rb @@ -17,6 +17,7 @@ describe SDGManagement::MenuComponent, type: :component do render_inline component expect(page).to have_link "Goals and Targets" + expect(page).to have_link "SDG homepage" expect(page).to have_link "Participatory budgets" expect(page).to have_link "Debates" expect(page).to have_link "Collaborative legislation" @@ -37,8 +38,9 @@ describe SDGManagement::MenuComponent, type: :component do it "does not generate links to any processes" do render_inline component - expect(page).to have_css "a", count: 1 + expect(page).to have_css "a", count: 2 expect(page).to have_link "Goals and Targets" + expect(page).to have_link "SDG homepage" end end @@ -54,8 +56,9 @@ describe SDGManagement::MenuComponent, type: :component do it "does not generate links to any processes" do render_inline component - expect(page).to have_css "a", count: 1 + expect(page).to have_css "a", count: 2 expect(page).to have_link "Goals and Targets" + expect(page).to have_link "SDG homepage" end end @@ -66,6 +69,7 @@ describe SDGManagement::MenuComponent, type: :component do render_inline component expect(page).to have_link "Goals and Targets" + expect(page).to have_link "SDG homepage" expect(page).to have_link "Participatory budgets" expect(page).to have_link "Collaborative legislation" expect(page).to have_link "Polls" @@ -82,6 +86,7 @@ describe SDGManagement::MenuComponent, type: :component do render_inline component expect(page).to have_link "Goals and Targets" + expect(page).to have_link "SDG homepage" expect(page).to have_link "Debates" expect(page).to have_link "Participatory budgets" expect(page).to have_link "Polls" diff --git a/spec/factories/sdg.rb b/spec/factories/sdg.rb index 9495a4114..a9dc94e4e 100644 --- a/spec/factories/sdg.rb +++ b/spec/factories/sdg.rb @@ -14,4 +14,8 @@ FactoryBot.define do target { SDG::Target[code.rpartition(".").first] } end + + factory :sdg_phase, class: "SDG::Phase" do + kind { :sensitization } + end end diff --git a/spec/lib/tasks/db_spec.rb b/spec/lib/tasks/db_spec.rb index b556b41b4..31448853c 100644 --- a/spec/lib/tasks/db_spec.rb +++ b/spec/lib/tasks/db_spec.rb @@ -9,27 +9,33 @@ describe "rake db:load_sdg" do it "populates empty databases and assigns targets correctly" do SDG::Goal.destroy_all + SDG::Phase.destroy_all run_rake_task expect(SDG::Goal.count).to eq 17 expect(SDG::Target.count).to eq 169 expect(SDG::Target["17.1"].goal.code).to eq 17 + expect(SDG::Phase.count).to eq 3 end it "does not create additional records on populated databases" do expect(SDG::Goal.count).to eq 17 expect(SDG::Target.count).to eq 169 + expect(SDG::Phase.count).to eq 3 goal_id = SDG::Goal.last.id target_id = SDG::Target.last.id + phase_id = SDG::Phase.last.id run_rake_task expect(SDG::Goal.count).to eq 17 expect(SDG::Target.count).to eq 169 + expect(SDG::Phase.count).to eq 3 expect(SDG::Goal.last.id).to eq goal_id expect(SDG::Target.last.id).to eq target_id + expect(SDG::Phase.last.id).to eq phase_id end end diff --git a/spec/models/abilities/everyone_spec.rb b/spec/models/abilities/everyone_spec.rb index 81b82dcd5..7aaa1e1c5 100644 --- a/spec/models/abilities/everyone_spec.rb +++ b/spec/models/abilities/everyone_spec.rb @@ -55,6 +55,7 @@ describe Abilities::Everyone do it { should be_able_to(:read, SDG::Goal) } it { should_not be_able_to(:read, SDG::Target) } + it { should be_able_to(:read, SDG::Phase) } it { should_not be_able_to(:read, SDG::Manager) } it { should_not be_able_to(:create, SDG::Manager) } diff --git a/spec/models/abilities/sdg/manager_spec.rb b/spec/models/abilities/sdg/manager_spec.rb index 11800ca04..fa4febb16 100644 --- a/spec/models/abilities/sdg/manager_spec.rb +++ b/spec/models/abilities/sdg/manager_spec.rb @@ -13,4 +13,9 @@ describe "Abilities::SDG::Manager" do it { should_not be_able_to(:read, SDG::Manager) } it { should_not be_able_to(:create, SDG::Manager) } it { should_not be_able_to(:delete, SDG::Manager) } + + it { should_not be_able_to(:update, create(:widget_card)) } + it { should be_able_to(:update, create(:widget_card, cardable: SDG::Phase.sample)) } + it { should_not be_able_to(:create, build(:widget_card)) } + it { should be_able_to(:create, build(:widget_card, cardable: SDG::Phase.sample)) } end diff --git a/spec/models/sdg/phase_spec.rb b/spec/models/sdg/phase_spec.rb new file mode 100644 index 000000000..160d09c25 --- /dev/null +++ b/spec/models/sdg/phase_spec.rb @@ -0,0 +1,44 @@ +require "rails_helper" + +describe SDG::Phase do + let(:phase) { build(:sdg_phase) } + before { SDG::Phase["sensitization"].destroy } + + it "is valid with a valid kind" do + phase.kind = "sensitization" + + expect(phase).to be_valid + end + + it "is not valid without a kind" do + phase.kind = nil + + expect(phase).not_to be_valid + end + + it "is not valid with a duplicate kind" do + phase.kind = "planning" + + expect(phase).not_to be_valid + end + + it "is not valid with a custom kind" do + expect { phase.kind = "improvement" }.to raise_exception(ArgumentError) + end + + describe ".[]" do + it "finds existing phases by kind" do + expect(SDG::Phase["monitoring"].kind).to eq "monitoring" + end + + it "raises an exception on empty databases" do + SDG::Phase["monitoring"].destroy! + + expect { SDG::Phase["monitoring"] }.to raise_exception ActiveRecord::RecordNotFound + end + + it "raises an exception for non-existing kinds" do + expect { SDG::Phase["improvement"] }.to raise_exception ActiveRecord::StatementInvalid + end + end +end diff --git a/spec/models/widget/card_spec.rb b/spec/models/widget/card_spec.rb index e7b01ddfe..70bb3eb93 100644 --- a/spec/models/widget/card_spec.rb +++ b/spec/models/widget/card_spec.rb @@ -34,7 +34,7 @@ describe Widget::Card do header = create(:widget_card, header: true) card1 = create(:widget_card, header: false) card2 = create(:widget_card, header: false) - page_card = create(:widget_card, header: false, page: create(:site_customization_page)) + page_card = create(:widget_card, header: false, cardable: create(:site_customization_page)) expect(Widget::Card.body).to match_array [card1, card2] expect(Widget::Card.body).not_to include(header) diff --git a/spec/routing/polymorphic_routes_spec.rb b/spec/routing/polymorphic_routes_spec.rb index 729e16d66..814e67b01 100644 --- a/spec/routing/polymorphic_routes_spec.rb +++ b/spec/routing/polymorphic_routes_spec.rb @@ -172,7 +172,7 @@ describe "Polymorphic routes" do it "routes site customization page widget cards" do page = create(:site_customization_page) - card = create(:widget_card, page: page) + card = create(:widget_card, cardable: page) expect(admin_polymorphic_path(card)).to eq admin_site_customization_page_widget_card_path(page, card) end @@ -186,6 +186,25 @@ describe "Polymorphic routes" do ) end end + + describe "sdg_management_polymorphic_path" do + include ActionDispatch::Routing::UrlFor + + it "routes local targets" do + target = create(:sdg_local_target) + + expect(sdg_management_polymorphic_path(target)).to eq sdg_management_local_target_path(target) + end + + it "routes SDG phases widget cards" do + phase = SDG::Phase.sample + card = create(:widget_card, cardable: phase) + + expect(sdg_management_polymorphic_path(card)).to eq( + sdg_management_sdg_phase_widget_card_path(phase, card) + ) + end + end end def polymorphic_path(record, options = {}) diff --git a/spec/system/admin/widgets/cards_spec.rb b/spec/system/admin/widgets/cards_spec.rb index 444968b7d..03ea2c2be 100644 --- a/spec/system/admin/widgets/cards_spec.rb +++ b/spec/system/admin/widgets/cards_spec.rb @@ -175,9 +175,9 @@ describe "Cards", :admin do end scenario "Show" do - card_1 = create(:widget_card, page: custom_page, title: "Card large", columns: 8) - card_2 = create(:widget_card, page: custom_page, title: "Card medium", columns: 4) - card_3 = create(:widget_card, page: custom_page, title: "Card small", columns: 2) + card_1 = create(:widget_card, cardable: custom_page, title: "Card large", columns: 8) + card_2 = create(:widget_card, cardable: custom_page, title: "Card medium", columns: 4) + card_3 = create(:widget_card, cardable: custom_page, title: "Card small", columns: 2) visit custom_page.url @@ -189,8 +189,8 @@ describe "Cards", :admin do end scenario "Show label only if it is present" do - card_1 = create(:widget_card, page: custom_page, title: "Card one", label: "My label") - card_2 = create(:widget_card, page: custom_page, title: "Card two") + card_1 = create(:widget_card, cardable: custom_page, title: "Card one", label: "My label") + card_2 = create(:widget_card, cardable: custom_page, title: "Card two") visit custom_page.url @@ -204,7 +204,7 @@ describe "Cards", :admin do end scenario "Edit", :js do - create(:widget_card, page: custom_page, title: "Original title") + create(:widget_card, cardable: custom_page, title: "Original title") visit admin_site_customization_page_widget_cards_path(custom_page) @@ -227,7 +227,7 @@ describe "Cards", :admin do end scenario "Destroy", :js do - create(:widget_card, page: custom_page, title: "Card title") + create(:widget_card, cardable: custom_page, title: "Card title") visit admin_site_customization_page_widget_cards_path(custom_page) diff --git a/spec/system/sdg_management/homepage_spec.rb b/spec/system/sdg_management/homepage_spec.rb new file mode 100644 index 000000000..598066adc --- /dev/null +++ b/spec/system/sdg_management/homepage_spec.rb @@ -0,0 +1,52 @@ +require "rails_helper" + +describe "SDG homepage configuration", :js do + before do + Setting["feature.sdg"] = true + login_as(create(:sdg_manager).user) + end + + describe "Show" do + scenario "Visit the index" do + visit sdg_management_root_path + + within("#side_menu") do + click_link "SDG homepage" + end + + expect(page).to have_title "SDG content - Homepage configuration" + end + + scenario "Create card" do + visit sdg_management_homepage_path + click_link "Create planning card" + + within(".translatable-fields") { fill_in "Title", with: "My planning card" } + click_button "Create card" + + within(".planning-cards") do + expect(page).to have_content "My planning card" + end + + within(".sensitization-cards") do + expect(page).to have_content "There are no cards for this phase" + end + end + + scenario "Update card" do + create(:widget_card, cardable: SDG::Phase["monitoring"], title: "My monitoring card") + + visit sdg_management_homepage_path + within(".monitoring-cards") { click_link "Edit" } + + within(".translatable-fields") { fill_in "Title", with: "Updated monitoring card" } + click_button "Save card" + + within(".monitoring-cards") do + expect(page).to have_css "tbody tr", count: 1 + expect(page).to have_content "Updated monitoring card" + expect(page).not_to have_content "My monitoring card" + end + end + end +end diff --git a/spec/system/site_customization/custom_pages_spec.rb b/spec/system/site_customization/custom_pages_spec.rb index a54c50b37..68a1745cf 100644 --- a/spec/system/site_customization/custom_pages_spec.rb +++ b/spec/system/site_customization/custom_pages_spec.rb @@ -104,7 +104,7 @@ describe "Custom Pages" do scenario "Show widget cards for that page" do custom_page = create(:site_customization_page, :published) - create(:widget_card, page: custom_page, title: "Card Highlights") + create(:widget_card, cardable: custom_page, title: "Card Highlights") visit custom_page.url