Merge pull request #4311 from consul/sdg_cards
Add SDG cards management section
This commit is contained in:
@@ -31,4 +31,5 @@
|
||||
@import "admin/*";
|
||||
@import "sdg/**/*";
|
||||
@import "sdg_management/*";
|
||||
@import "sdg_management/**/*";
|
||||
@import "widgets/**/*";
|
||||
|
||||
@@ -91,12 +91,8 @@ a {
|
||||
}
|
||||
|
||||
.button {
|
||||
@extend %button;
|
||||
background: $brand;
|
||||
font-size: $base-font-size;
|
||||
|
||||
&:hover {
|
||||
text-decoration: none !important;
|
||||
}
|
||||
|
||||
&.warning,
|
||||
&.warning:hover {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
11
app/assets/stylesheets/sdg_management/homepage/show.scss
Normal file
11
app/assets/stylesheets/sdg_management/homepage/show.scss
Normal file
@@ -0,0 +1,11 @@
|
||||
.phase-cards {
|
||||
> header {
|
||||
align-items: flex-start;
|
||||
display: flex;
|
||||
|
||||
a {
|
||||
@include regular-button;
|
||||
margin-left: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,4 +7,12 @@
|
||||
|
||||
mask-image: image-url("sdg.svg");
|
||||
}
|
||||
|
||||
.homepage-link {
|
||||
@include has-fa-icon(home, solid);
|
||||
|
||||
&::before {
|
||||
@extend %admin-menu-icon;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
<%= back_link_to index_path %>
|
||||
<%= header %>
|
||||
<%= render "admin/widget/cards/form", card: card %>
|
||||
19
app/components/admin/widget/cards/edit_component.rb
Normal file
19
app/components/admin/widget/cards/edit_component.rb
Normal file
@@ -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
|
||||
3
app/components/admin/widget/cards/new_component.html.erb
Normal file
3
app/components/admin/widget/cards/new_component.html.erb
Normal file
@@ -0,0 +1,3 @@
|
||||
<%= back_link_to index_path %>
|
||||
<%= header %>
|
||||
<%= render "admin/widget/cards/form", card: card %>
|
||||
19
app/components/admin/widget/cards/new_component.rb
Normal file
19
app/components/admin/widget/cards/new_component.rb
Normal file
@@ -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
|
||||
@@ -1,3 +1,4 @@
|
||||
class ApplicationComponent < ViewComponent::Base
|
||||
include SettingsHelper
|
||||
delegate :back_link_to, to: :helpers
|
||||
end
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
class SDG::Goals::ShowComponent < ApplicationComponent
|
||||
attr_reader :goal
|
||||
delegate :back_link_to, to: :helpers
|
||||
|
||||
def initialize(goal)
|
||||
@goal = goal
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
<%= header %>
|
||||
|
||||
<% phases.each do |phase| %>
|
||||
<section class="phase-cards <%= phase.kind %>-cards">
|
||||
<header>
|
||||
<h3><%= phase.title %></h3>
|
||||
<%= link_to create_card_text(phase), new_sdg_management_sdg_phase_widget_card_path(phase) %>
|
||||
</header>
|
||||
|
||||
<%= render Admin::Widget::Cards::TableComponent.new(
|
||||
phase.cards,
|
||||
no_cards_message: no_cards_message
|
||||
) %>
|
||||
</section>
|
||||
<% end %>
|
||||
23
app/components/sdg_management/homepage/show_component.rb
Normal file
23
app/components/sdg_management/homepage/show_component.rb
Normal file
@@ -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
|
||||
@@ -1,5 +1,4 @@
|
||||
class SDGManagement::LocalTargets::FormComponent < ApplicationComponent
|
||||
delegate :back_link_to, to: :helpers
|
||||
include Header
|
||||
include TranslatableFormHelper
|
||||
include GlobalizeHelper
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
13
app/controllers/sdg_management/cards_controller.rb
Normal file
13
app/controllers/sdg_management/cards_controller.rb
Normal file
@@ -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
|
||||
5
app/controllers/sdg_management/homepage_controller.rb
Normal file
5
app/controllers/sdg_management/homepage_controller.rb
Normal file
@@ -0,0 +1,5 @@
|
||||
class SDGManagement::HomepageController < SDGManagement::BaseController
|
||||
def show
|
||||
@phases = SDG::Phase.accessible_by(current_ability).order(:kind)
|
||||
end
|
||||
end
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
7
app/models/concerns/cardable.rb
Normal file
7
app/models/concerns/cardable.rb
Normal file
@@ -0,0 +1,7 @@
|
||||
module Cardable
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
has_many :cards, class_name: "Widget::Card", as: :cardable, dependent: :destroy
|
||||
end
|
||||
end
|
||||
13
app/models/sdg/phase.rb
Normal file
13
app/models/sdg/phase.rb
Normal file
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 %>
|
||||
|
||||
<div class="row">
|
||||
<%= f.translatable_fields do |translations_form| %>
|
||||
@@ -30,7 +30,7 @@
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<% unless @card.header? %>
|
||||
<% unless card.header? %>
|
||||
<div class="column">
|
||||
<%= f.label :columns %>
|
||||
<p class="help-text"><%= t("admin.site_customization.pages.cards.columns_help") %></p>
|
||||
@@ -41,16 +41,16 @@
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<%= f.hidden_field :header, value: @card.header? %>
|
||||
<%= f.hidden_field :header, value: card.header? %>
|
||||
<div class="row">
|
||||
<div class="image-form">
|
||||
<div class="image small-12 column">
|
||||
<%= render "images/nested_image", imageable: @card, f: f %>
|
||||
<%= render "images/nested_image", imageable: card, f: f %>
|
||||
</div>
|
||||
</div>
|
||||
<div class="column">
|
||||
<%= 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"
|
||||
) %>
|
||||
</div>
|
||||
|
||||
@@ -1,11 +1 @@
|
||||
<%= back_link_to index_path %>
|
||||
|
||||
<h2>
|
||||
<% if @card.header? %>
|
||||
<%= t("admin.homepage.edit.header_title") %>
|
||||
<% else %>
|
||||
<%= t("admin.homepage.edit.card_title") %>
|
||||
<% end %>
|
||||
</h2>
|
||||
|
||||
<%= render "admin/widget/cards/form" %>
|
||||
<%= render Admin::Widget::Cards::EditComponent.new(@card, index_path: index_path) %>
|
||||
|
||||
@@ -1,11 +1 @@
|
||||
<%= back_link_to index_path %>
|
||||
|
||||
<h2>
|
||||
<% if @card.header? %>
|
||||
<%= t("admin.homepage.new.header_title") %>
|
||||
<% else %>
|
||||
<%= t("admin.homepage.new.card_title") %>
|
||||
<% end %>
|
||||
</h2>
|
||||
|
||||
<%= render "admin/widget/cards/form" %>
|
||||
<%= render Admin::Widget::Cards::NewComponent.new(@card, index_path: index_path) %>
|
||||
|
||||
1
app/views/sdg_management/homepage/show.html.erb
Normal file
1
app/views/sdg_management/homepage/show.html.erb
Normal file
@@ -0,0 +1 @@
|
||||
<%= render SDGManagement::Homepage::ShowComponent.new(@phases) %>
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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|
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
8
db/migrate/20210106132909_make_cards_polymorphic.rb
Normal file
8
db/migrate/20210106132909_make_cards_polymorphic.rb
Normal file
@@ -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
|
||||
9
db/migrate/20210107125458_create_sdg_phases.rb
Normal file
9
db/migrate/20210107125458_create_sdg_phases.rb
Normal file
@@ -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
|
||||
14
db/schema.rb
14
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|
|
||||
|
||||
@@ -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! }
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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) }
|
||||
|
||||
@@ -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
|
||||
|
||||
44
spec/models/sdg/phase_spec.rb
Normal file
44
spec/models/sdg/phase_spec.rb
Normal file
@@ -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
|
||||
@@ -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)
|
||||
|
||||
@@ -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 = {})
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
52
spec/system/sdg_management/homepage_spec.rb
Normal file
52
spec/system/sdg_management/homepage_spec.rb
Normal file
@@ -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
|
||||
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user