Merge pull request #2641 from consul/homepage
Add customization of homepage from admin section
This commit is contained in:
BIN
app/assets/images/welcome_process.png
Normal file
BIN
app/assets/images/welcome_process.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.8 KiB |
@@ -279,7 +279,7 @@ $sidebar-active: #f4fcd0;
|
||||
}
|
||||
|
||||
.no-margin-bottom {
|
||||
margin-bottom: 0 !important;
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
|
||||
// 02. Sidebar
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
// 21. Related content
|
||||
// 22. Images
|
||||
// 23. Maps
|
||||
// 24. Homepage
|
||||
//
|
||||
|
||||
// 01. Global styles
|
||||
@@ -2224,17 +2225,17 @@ table {
|
||||
// 19. Recommended Section Home
|
||||
// ----------------------------
|
||||
|
||||
.home-page {
|
||||
|
||||
.push {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.section-recommended {
|
||||
padding: $line-height * 2 0;
|
||||
background: #fafafa;
|
||||
border-top: 1px solid $border;
|
||||
margin-bottom: rem-calc(-48);
|
||||
padding: $line-height 0;
|
||||
|
||||
h2 {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
p {
|
||||
margin-bottom: $line-height * 2;
|
||||
}
|
||||
|
||||
@@ -2258,12 +2259,12 @@ table {
|
||||
.card {
|
||||
|
||||
.card-section {
|
||||
padding: $line-height 0;
|
||||
max-width: rem-calc(300);
|
||||
margin: 0 auto;
|
||||
max-width: none;
|
||||
padding: 0;
|
||||
|
||||
p {
|
||||
font-size: rem-calc(15);
|
||||
font-size: $base-font-size;
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
@@ -2293,18 +2294,6 @@ table {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.debates-inner {
|
||||
border-top: 4px solid $debates;
|
||||
}
|
||||
|
||||
.proposals-inner {
|
||||
border-top: 4px solid $proposals;
|
||||
}
|
||||
|
||||
.budget-investments-inner {
|
||||
border-top: 4px solid $budget;
|
||||
}
|
||||
|
||||
.debates-inner,
|
||||
.proposals-inner,
|
||||
.budget-investments-inner {
|
||||
@@ -2316,10 +2305,10 @@ table {
|
||||
}
|
||||
|
||||
h4 {
|
||||
margin-top: $line-height;
|
||||
margin-bottom: 0;
|
||||
margin-top: 0;
|
||||
margin-bottom: $line-height;
|
||||
font-size: rem-calc(18);
|
||||
min-height: rem-calc(50);
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
h5 {
|
||||
@@ -2338,9 +2327,21 @@ table {
|
||||
}
|
||||
}
|
||||
|
||||
.debates,
|
||||
.proposals {
|
||||
|
||||
a {
|
||||
display: block;
|
||||
margin-top: $line-height;
|
||||
}
|
||||
}
|
||||
|
||||
.debates-inner,
|
||||
.proposals-inner,
|
||||
.budget-investments-inner {
|
||||
border: 1px solid $border;
|
||||
padding: $line-height;
|
||||
margin-right: $line-height;
|
||||
max-height: rem-calc(500);
|
||||
|
||||
@include breakpoint(small) {
|
||||
@@ -2575,3 +2576,151 @@ table {
|
||||
color: #525252 !important;
|
||||
}
|
||||
}
|
||||
|
||||
// 24. Homepage
|
||||
// ------------
|
||||
|
||||
.home-page {
|
||||
|
||||
a {
|
||||
|
||||
p {
|
||||
|
||||
&.description {
|
||||
color: $text;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
a:hover {
|
||||
|
||||
h3 {
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
.background-header {
|
||||
clip-path: ellipse(60% 80% at 50% 0%);
|
||||
height: $line-height * 2;
|
||||
background: $highlight;
|
||||
margin-top: rem-calc(-48);
|
||||
}
|
||||
|
||||
.card {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
h3 {
|
||||
|
||||
&.title {
|
||||
display: inline-block;
|
||||
border-top: 4px solid $brand;
|
||||
min-width: rem-calc(240);
|
||||
padding-top: $line-height / 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.feeds-list {
|
||||
|
||||
.proposal {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.debate {
|
||||
margin: $line-height 0;
|
||||
}
|
||||
}
|
||||
|
||||
.feed-image {
|
||||
display: inline-block;
|
||||
height: rem-calc(120);
|
||||
margin: $line-height / 2 0;
|
||||
overflow: hidden;
|
||||
|
||||
img {
|
||||
margin-left: rem-calc(-15);
|
||||
max-width: none;
|
||||
width: rem-calc(120);
|
||||
}
|
||||
}
|
||||
|
||||
.feed-description {
|
||||
|
||||
@include breakpoint(medium) {
|
||||
padding-top: $line-height;
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: $small-font-size;
|
||||
}
|
||||
}
|
||||
|
||||
.feed-content {
|
||||
border-bottom: 1px solid $border;
|
||||
display: inline-block;
|
||||
|
||||
a {
|
||||
|
||||
&.see-all {
|
||||
font-size: $small-font-size;
|
||||
margin: $line-height 0 $line-height / 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.figure-card {
|
||||
display: flex;
|
||||
margin: 0 0 $line-height;
|
||||
position: relative;
|
||||
|
||||
@include breakpoint(medium) {
|
||||
max-height: rem-calc(185);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
a {
|
||||
|
||||
h3,
|
||||
.title {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
|
||||
img {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
figcaption {
|
||||
bottom: 0;
|
||||
color: #fff;
|
||||
font-size: rem-calc(24);
|
||||
line-height: rem-calc(24);
|
||||
text-transform: uppercase;
|
||||
padding: $line-height / 4 $line-height / 2;
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
|
||||
h3,
|
||||
.title {
|
||||
font-size: rem-calc(24);
|
||||
line-height: rem-calc(24);
|
||||
}
|
||||
|
||||
span {
|
||||
background: #fff;
|
||||
border-radius: rem-calc(4);
|
||||
color: #000;
|
||||
display: inline-block;
|
||||
font-size: $small-font-size;
|
||||
font-weight: bold;
|
||||
margin-bottom: $line-height / 4;
|
||||
padding: rem-calc(4) rem-calc(8);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -319,7 +319,8 @@
|
||||
.legislation-process-new,
|
||||
.legislation-process-edit,
|
||||
.milestone-new,
|
||||
.milestone-edit {
|
||||
.milestone-edit,
|
||||
.image-form {
|
||||
@include direct-uploads;
|
||||
}
|
||||
|
||||
|
||||
28
app/controllers/admin/homepage_controller.rb
Normal file
28
app/controllers/admin/homepage_controller.rb
Normal file
@@ -0,0 +1,28 @@
|
||||
class Admin::HomepageController < Admin::BaseController
|
||||
|
||||
def show
|
||||
load_header
|
||||
load_feeds
|
||||
load_recommendations
|
||||
load_cards
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def load_header
|
||||
@header = ::Widget::Card.header
|
||||
end
|
||||
|
||||
def load_recommendations
|
||||
@recommendations = Setting.where(key: 'feature.user.recommendations').first
|
||||
end
|
||||
|
||||
def load_cards
|
||||
@cards = ::Widget::Card.body
|
||||
end
|
||||
|
||||
def load_feeds
|
||||
@feeds = Widget::Feed.order("created_at")
|
||||
end
|
||||
|
||||
end
|
||||
@@ -11,7 +11,7 @@ class Admin::SettingsController < Admin::BaseController
|
||||
def update
|
||||
@setting = Setting.find(params[:id])
|
||||
@setting.update(settings_params)
|
||||
redirect_to admin_settings_path, notice: t("admin.settings.flash.updated")
|
||||
redirect_to request.referer, notice: t("admin.settings.flash.updated")
|
||||
end
|
||||
|
||||
def update_map
|
||||
|
||||
10
app/controllers/admin/widget/base_controller.rb
Normal file
10
app/controllers/admin/widget/base_controller.rb
Normal file
@@ -0,0 +1,10 @@
|
||||
class Admin::Widget::BaseController < Admin::BaseController
|
||||
helper_method :namespace
|
||||
|
||||
private
|
||||
|
||||
def namespace
|
||||
"admin"
|
||||
end
|
||||
|
||||
end
|
||||
51
app/controllers/admin/widget/cards_controller.rb
Normal file
51
app/controllers/admin/widget/cards_controller.rb
Normal file
@@ -0,0 +1,51 @@
|
||||
class Admin::Widget::CardsController < Admin::BaseController
|
||||
|
||||
def new
|
||||
@card = ::Widget::Card.new(header: header_card?)
|
||||
end
|
||||
|
||||
def create
|
||||
@card = ::Widget::Card.new(card_params)
|
||||
if @card.save
|
||||
notice = "Success"
|
||||
redirect_to admin_homepage_url, notice: notice
|
||||
else
|
||||
render :new
|
||||
end
|
||||
end
|
||||
|
||||
def edit
|
||||
@card = ::Widget::Card.find(params[:id])
|
||||
end
|
||||
|
||||
def update
|
||||
@card = ::Widget::Card.find(params[:id])
|
||||
if @card.update(card_params)
|
||||
notice = "Updated"
|
||||
redirect_to admin_homepage_url, notice: notice
|
||||
else
|
||||
render :edit
|
||||
end
|
||||
end
|
||||
|
||||
def destroy
|
||||
@card = ::Widget::Card.find(params[:id])
|
||||
@card.destroy
|
||||
|
||||
notice = "Removed"
|
||||
redirect_to admin_homepage_url, notice: notice
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def card_params
|
||||
params.require(:widget_card).permit(:label, :title, :description, :link_text, :link_url,
|
||||
:button_text, :button_url, :alignment, :header,
|
||||
image_attributes: [:id, :title, :attachment, :cached_attachment, :user_id, :_destroy])
|
||||
end
|
||||
|
||||
def header_card?
|
||||
params[:header_card].present?
|
||||
end
|
||||
|
||||
end
|
||||
16
app/controllers/admin/widget/feeds_controller.rb
Normal file
16
app/controllers/admin/widget/feeds_controller.rb
Normal file
@@ -0,0 +1,16 @@
|
||||
class Admin::Widget::FeedsController < Admin::BaseController
|
||||
|
||||
def update
|
||||
@feed = ::Widget::Feed.find(params[:id])
|
||||
@feed.update(feed_params)
|
||||
|
||||
render nothing: true
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def feed_params
|
||||
params.require(:widget_feed).permit(:limit)
|
||||
end
|
||||
|
||||
end
|
||||
@@ -5,6 +5,9 @@ class WelcomeController < ApplicationController
|
||||
layout "devise", only: [:welcome, :verification]
|
||||
|
||||
def index
|
||||
@header = Widget::Card.header.first
|
||||
@feeds = Widget::Feed.active
|
||||
@cards = Widget::Card.body
|
||||
end
|
||||
|
||||
def welcome
|
||||
|
||||
@@ -37,7 +37,11 @@ module AdminHelper
|
||||
end
|
||||
|
||||
def menu_customization?
|
||||
["pages", "images", "content_blocks"].include?(controller_name)
|
||||
["pages", "images", "content_blocks"].include?(controller_name) || menu_homepage?
|
||||
end
|
||||
|
||||
def menu_homepage?
|
||||
["homepage", "cards"].include?(controller_name)
|
||||
end
|
||||
|
||||
def official_level_options
|
||||
|
||||
19
app/helpers/feeds_helper.rb
Normal file
19
app/helpers/feeds_helper.rb
Normal file
@@ -0,0 +1,19 @@
|
||||
module FeedsHelper
|
||||
|
||||
def feed_debates?(feed)
|
||||
feed.kind == "debates"
|
||||
end
|
||||
|
||||
def feed_proposals?(feed)
|
||||
feed.kind == "proposals"
|
||||
end
|
||||
|
||||
def feed_processes?(feed)
|
||||
feed.kind == "processes"
|
||||
end
|
||||
|
||||
def feed_processes_enabled?
|
||||
Setting['feature.homepage.widgets.feeds.processes'].present?
|
||||
end
|
||||
|
||||
end
|
||||
7
app/helpers/settings.rb
Normal file
7
app/helpers/settings.rb
Normal file
@@ -0,0 +1,7 @@
|
||||
module SettingsHelper
|
||||
|
||||
def setting_for_widget(widget)
|
||||
Setting.where(key: 'feature.user.recommendations').first
|
||||
end
|
||||
|
||||
end
|
||||
@@ -68,6 +68,8 @@ class Image < ActiveRecord::Base
|
||||
|
||||
def validate_image_dimensions
|
||||
if attachment_of_valid_content_type?
|
||||
return true if imageable_class == Widget::Card
|
||||
|
||||
dimensions = Paperclip::Geometry.from_file(attachment.queued_for_write[:original].path)
|
||||
errors.add(:attachment, :min_image_width, required_min_width: MIN_SIZE) if dimensions.width < MIN_SIZE
|
||||
errors.add(:attachment, :min_image_height, required_min_height: MIN_SIZE) if dimensions.height < MIN_SIZE
|
||||
|
||||
13
app/models/widget/card.rb
Normal file
13
app/models/widget/card.rb
Normal file
@@ -0,0 +1,13 @@
|
||||
class Widget::Card < ActiveRecord::Base
|
||||
include Imageable
|
||||
|
||||
self.table_name = "widget_cards"
|
||||
|
||||
def self.header
|
||||
where(header: true)
|
||||
end
|
||||
|
||||
def self.body
|
||||
where(header: false).order(:created_at)
|
||||
end
|
||||
end
|
||||
37
app/models/widget/feed.rb
Normal file
37
app/models/widget/feed.rb
Normal file
@@ -0,0 +1,37 @@
|
||||
class Widget::Feed < ActiveRecord::Base
|
||||
self.table_name = "widget_feeds"
|
||||
|
||||
KINDS = %w(proposals debates processes)
|
||||
|
||||
def active?
|
||||
setting.value.present?
|
||||
end
|
||||
|
||||
def setting
|
||||
Setting.where(key: "feature.homepage.widgets.feeds.#{kind}").first
|
||||
end
|
||||
|
||||
def self.active
|
||||
KINDS.collect do |kind|
|
||||
feed = find_or_create_by(kind: kind)
|
||||
feed if feed.active?
|
||||
end.compact
|
||||
end
|
||||
|
||||
def items
|
||||
send(kind)
|
||||
end
|
||||
|
||||
def proposals
|
||||
Proposal.sort_by_hot_score.limit(limit)
|
||||
end
|
||||
|
||||
def debates
|
||||
Debate.sort_by_hot_score.limit(limit)
|
||||
end
|
||||
|
||||
def processes
|
||||
Legislation::Process.open.published.limit(limit)
|
||||
end
|
||||
|
||||
end
|
||||
@@ -167,6 +167,10 @@
|
||||
</a>
|
||||
<ul <%= "class=is-active" if menu_customization? &&
|
||||
controller.class.parent != Admin::Poll::Questions::Answers %>>
|
||||
<li <%= "class=active" if menu_homepage? %>>
|
||||
<%= link_to t("admin.menu.site_customization.homepage"), admin_homepage_path %>
|
||||
</li>
|
||||
|
||||
<li <%= "class=active" if controller_name == "pages" %>>
|
||||
<%= link_to t("admin.menu.site_customization.pages"), admin_site_customization_pages_path %>
|
||||
</li>
|
||||
@@ -179,6 +183,7 @@
|
||||
<li <%= "class=active" if controller_name == "content_blocks" %>>
|
||||
<%= link_to t("admin.menu.site_customization.content_blocks"), admin_site_customization_content_blocks_path%>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
|
||||
30
app/views/admin/homepage/_card.html.erb
Normal file
30
app/views/admin/homepage/_card.html.erb
Normal file
@@ -0,0 +1,30 @@
|
||||
<tr id="<%= dom_id(card) %>" class="homepage-card">
|
||||
<td>
|
||||
<%= card.label %><br>
|
||||
<%= card.title %>
|
||||
</td>
|
||||
<td><%= card.description %></td>
|
||||
<td>
|
||||
<%= card.link_text %><br>
|
||||
<%= card.link_url %>
|
||||
</td>
|
||||
|
||||
<!-- remove conditional once specs have image validations -->
|
||||
<td>
|
||||
<% if card.image.present? %>
|
||||
<%= link_to t("admin.shared.show_image"), card.image_url(:large),
|
||||
title: card.image.title, target: "_blank" %>
|
||||
<% end %>
|
||||
</td>
|
||||
<td>
|
||||
<%= link_to t("admin.actions.edit"),
|
||||
edit_admin_widget_card_path(card),
|
||||
class: "button hollow" %>
|
||||
|
||||
<%= link_to t("admin.actions.delete"),
|
||||
admin_widget_card_path(card),
|
||||
method: :delete,
|
||||
data: { confirm: t('admin.actions.confirm') },
|
||||
class: "button hollow alert" %>
|
||||
</td>
|
||||
</tr>
|
||||
16
app/views/admin/homepage/_cards.html.erb
Normal file
16
app/views/admin/homepage/_cards.html.erb
Normal file
@@ -0,0 +1,16 @@
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th><%= t("admin.homepage.cards.title") %></th>
|
||||
<th class="small-4"><%= t("admin.homepage.cards.description") %></th>
|
||||
<th><%= t("admin.homepage.cards.link_text") %> / <%= t("admin.homepage.cards.link_url") %></th>
|
||||
<th><%= t("admin.shared.image") %></th>
|
||||
<th class="small-2"><%= t("admin.shared.actions") %></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<% cards.each do |card| %>
|
||||
<%= render "card", card: card %>
|
||||
<% end %>
|
||||
</tbody>
|
||||
</table>
|
||||
11
app/views/admin/homepage/_feed.html.erb
Normal file
11
app/views/admin/homepage/_feed.html.erb
Normal file
@@ -0,0 +1,11 @@
|
||||
<div id="<%= dom_id(feed) %>" class="small-12 medium-6 large-4 column end">
|
||||
<div class="callout">
|
||||
<h3><%= t("admin.homepage.feeds.#{feed.kind}") %></h3>
|
||||
|
||||
<%= render "setting", setting: feed.setting %>
|
||||
|
||||
<%= form_for [:admin, feed], remote: true do |f| %>
|
||||
<%= f.select :limit, (1..10), {}, class: "js-submit-on-change" %>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
11
app/views/admin/homepage/_setting.html.erb
Normal file
11
app/views/admin/homepage/_setting.html.erb
Normal file
@@ -0,0 +1,11 @@
|
||||
<div id="<%= dom_id(setting) %>">
|
||||
<%= form_for(setting, url: admin_setting_path(setting), method: :put) do |f| %>
|
||||
|
||||
<%= f.hidden_field :value,
|
||||
value: (setting.enabled? ? "" : "active") %>
|
||||
|
||||
<%= f.submit(t("admin.settings.index.features.#{setting.enabled? ? 'disable' : 'enable'}"),
|
||||
class: "button #{setting.enabled? ? 'hollow alert' : 'success'}",
|
||||
data: {confirm: t("admin.actions.confirm")}) %>
|
||||
<% end %>
|
||||
</div>
|
||||
52
app/views/admin/homepage/show.html.erb
Normal file
52
app/views/admin/homepage/show.html.erb
Normal file
@@ -0,0 +1,52 @@
|
||||
<h2><%= t("admin.homepage.title") %></h2>
|
||||
|
||||
<p><%= t("admin.homepage.description") %></p>
|
||||
|
||||
<div id="header">
|
||||
<h3 class="inline-block"><%= t("admin.homepage.header_title") %></h3>
|
||||
|
||||
<div class="float-right">
|
||||
<%= link_to t("admin.homepage.create_header"), new_admin_widget_card_path(header_card: true), class: "button" %>
|
||||
</div>
|
||||
|
||||
<% if @header.present? %>
|
||||
<%= render "cards", cards: @header %>
|
||||
<% else %>
|
||||
<div class="callout primary clear">
|
||||
<%= t("admin.homepage.no_header") %>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<div id="cards">
|
||||
<h3 class="inline-block"><%= t("admin.homepage.cards_title") %></h3>
|
||||
|
||||
<div class="float-right">
|
||||
<%= link_to t("admin.homepage.create_card"), new_admin_widget_card_path, class: "button" %>
|
||||
</div>
|
||||
|
||||
<% if @cards.present? %>
|
||||
<%= render "cards", cards: @cards %>
|
||||
<% else %>
|
||||
<div class="callout primary clear">
|
||||
<%= t("admin.homepage.no_cards") %>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<% @feeds.each do |feed| %>
|
||||
<%= render "feed", feed: feed %>
|
||||
<% end %>
|
||||
|
||||
<div class="small-12 medium-6 large-4 column end">
|
||||
<div class="callout">
|
||||
<h3 class="inline-block"><%= t("settings.#{@recommendations.key}") %></h3>
|
||||
<div class="float-right">
|
||||
<%= render "setting", setting: @recommendations %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
1
app/views/admin/widget/_menu.html.erb
Normal file
1
app/views/admin/widget/_menu.html.erb
Normal file
@@ -0,0 +1 @@
|
||||
<%= render "admin/menu" %>
|
||||
27
app/views/admin/widget/cards/_form.html.erb
Normal file
27
app/views/admin/widget/cards/_form.html.erb
Normal file
@@ -0,0 +1,27 @@
|
||||
<%= form_for [:admin, @card] do |f| %>
|
||||
<div class="small-12 medium-6">
|
||||
<%= f.text_field :label %>
|
||||
</div>
|
||||
|
||||
<%= f.text_field :title %>
|
||||
|
||||
<%= f.text_area :description, rows: 5 %>
|
||||
|
||||
<div class="small-12 medium-6">
|
||||
<%= f.text_field :link_text %>
|
||||
</div>
|
||||
|
||||
<div class="small-12 medium-6">
|
||||
<%= f.text_field :link_url %>
|
||||
</div>
|
||||
|
||||
<%= f.hidden_field :header, value: @card.header? %>
|
||||
|
||||
<div class="image-form">
|
||||
<div class="image small-12 column">
|
||||
<%= render 'images/nested_image', imageable: @card, f: f %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<%= f.submit(t("admin.homepage.#{action_name}.#{@card.header? ? 'submit_header' : 'submit_card'}"), class: "button success") %>
|
||||
<% end %>
|
||||
9
app/views/admin/widget/cards/edit.html.erb
Normal file
9
app/views/admin/widget/cards/edit.html.erb
Normal file
@@ -0,0 +1,9 @@
|
||||
<h2>
|
||||
<% if @card.header? %>
|
||||
<%= t("admin.homepage.edit.header_title") %>
|
||||
<% else %>
|
||||
<%= t("admin.homepage.edit.card_title") %>
|
||||
<% end %>
|
||||
</h2>
|
||||
|
||||
<%= render "form" %>
|
||||
9
app/views/admin/widget/cards/new.html.erb
Normal file
9
app/views/admin/widget/cards/new.html.erb
Normal file
@@ -0,0 +1,9 @@
|
||||
<h2>
|
||||
<% if @card.header? %>
|
||||
<%= t("admin.homepage.new.header_title") %>
|
||||
<% else %>
|
||||
<%= t("admin.homepage.new.card_title") %>
|
||||
<% end %>
|
||||
</h2>
|
||||
|
||||
<%= render "form" %>
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
<div class="top-bar-title">
|
||||
<h1>
|
||||
<%= link_to namespaced_root_path do %>
|
||||
<%= link_to admin_root_path do %>
|
||||
<%= setting['org_name'] %>
|
||||
<br><small><%= namespaced_header_title %></small>
|
||||
<% end %>
|
||||
|
||||
15
app/views/welcome/_card.html.erb
Normal file
15
app/views/welcome/_card.html.erb
Normal file
@@ -0,0 +1,15 @@
|
||||
<div id="<%= dom_id(card) %>" class="card small-12 medium-6 column margin-bottom end <%= 'large-4' unless feed_processes_enabled? %>">
|
||||
<%= link_to card.link_url do %>
|
||||
<figure class="figure-card">
|
||||
<% if card.image.present? %>
|
||||
<%= image_tag(card.image_url(:large), alt: card.image.title) %>
|
||||
<% end %>
|
||||
<figcaption>
|
||||
<span><%= card.label %></span><br>
|
||||
<h3><%= card.title %></h3>
|
||||
</figcaption>
|
||||
</figure>
|
||||
<p class="description"><%= card.description %></p>
|
||||
<p><%= card.link_text %></p>
|
||||
<% end %>
|
||||
</div>
|
||||
7
app/views/welcome/_cards.html.erb
Normal file
7
app/views/welcome/_cards.html.erb
Normal file
@@ -0,0 +1,7 @@
|
||||
<h3 class="title"><%= t("welcome.cards.title") %></h3>
|
||||
|
||||
<div class="row">
|
||||
<% @cards.all.each do |card| %>
|
||||
<%= render "card", card: card %>
|
||||
<% end %>
|
||||
</div>
|
||||
48
app/views/welcome/_feeds.html.erb
Normal file
48
app/views/welcome/_feeds.html.erb
Normal file
@@ -0,0 +1,48 @@
|
||||
<div class="row margin-bottom feeds-list">
|
||||
<% @feeds.each do |feed| %>
|
||||
|
||||
<% if feed_proposals?(feed) %>
|
||||
<div class="small-12 medium-8 column margin-top">
|
||||
<div class="feed-content">
|
||||
<h3 class="title"><%= t("welcome.feed.most_active.#{feed.kind}") %></h3>
|
||||
|
||||
<% feed.items.each do |item| %>
|
||||
<div class="<%= item.class.to_s.parameterize('_') %> row">
|
||||
<% if feature?(:allow_images) && item.image.present? %>
|
||||
<div class="small-12 large-3 column">
|
||||
<div class="feed-image">
|
||||
<%= image_tag item.image_url(:thumb),
|
||||
alt: item.image.title.unicode_normalize %>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
<div class="feed-description small-12 column <%= 'large-9' if feature?(:allow_images) && item.image.present? %>">
|
||||
<strong><%= link_to item.title, url_for(item) %></strong><br>
|
||||
<p><%= item.summary %></p>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<%= link_to t("welcome.feed.see_all_proposals"), proposals_path, class: "float-right see-all" %>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<% if feed_debates?(feed) %>
|
||||
<div class="small-12 medium-4 column margin-top">
|
||||
<div class="feed-content">
|
||||
<h3 class="title"><%= t("welcome.feed.most_active.#{feed.kind}") %></h3>
|
||||
|
||||
<% feed.items.each do |item| %>
|
||||
<div class="<%= item.class.to_s.parameterize('_') %>">
|
||||
<strong><%= link_to item.title, url_for(item) %></strong>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<%= link_to t("welcome.feed.see_all_debates"), debates_path, class: "float-right see-all" %>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<% end %>
|
||||
</div>
|
||||
23
app/views/welcome/_header.html.erb
Normal file
23
app/views/welcome/_header.html.erb
Normal file
@@ -0,0 +1,23 @@
|
||||
<% if header.present? %>
|
||||
<div class="jumbo highlight">
|
||||
<div class="row">
|
||||
<div class="small-12 medium-6 column <%= 'medium-9 small-centered text-center' unless header.image.present? %>">
|
||||
<span><%= header.label %></span>
|
||||
<h1><%= header.title %></h1>
|
||||
<p class="lead"><%= header.description %></p>
|
||||
<div class="small-12 medium-6 large-4 <%= 'small-centered' unless header.image.present? %>">
|
||||
<%= link_to header.link_text, header.link_url, class: "button expanded large" %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<% if header.image.present? %>
|
||||
<div class="small-12 medium-6 column">
|
||||
<%= image_tag(header.image_url(:large),
|
||||
class: "margin",
|
||||
alt: header.image.title) %>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
<div class="background-header"></div>
|
||||
<% end %>
|
||||
29
app/views/welcome/_processes.html.erb
Normal file
29
app/views/welcome/_processes.html.erb
Normal file
@@ -0,0 +1,29 @@
|
||||
<div class="feeds-list">
|
||||
<% @feeds.each do |feed| %>
|
||||
|
||||
<% if feed_processes?(feed) %>
|
||||
<div class="card small-12 column margin-bottom">
|
||||
<div class="feed-content">
|
||||
<h3 class="title"><%= t("welcome.feed.most_active.#{feed.kind}") %></h3>
|
||||
|
||||
<% feed.items.each do |item| %>
|
||||
<%= link_to url_for(item) do %>
|
||||
<figure class="figure-card <%= item.class.to_s.parameterize('_') %>">
|
||||
<%= image_tag("welcome_process.png", alt: "") %>
|
||||
<figcaption>
|
||||
<span><%= t("welcome.feed.process_label") %></span><br>
|
||||
<h3><%= item.title %></h3>
|
||||
</figcaption>
|
||||
</figure>
|
||||
<p class="description small"><%= item.summary %></p>
|
||||
<p class="small"><%= t("welcome.feed.see_process") %></p>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
<%= link_to t("welcome.feed.see_all_processes"), legislation_processes_path, class: "float-right see-all" %>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<% end %>
|
||||
</div>
|
||||
@@ -1,8 +1,12 @@
|
||||
<div class="small-12 column section-recommended padding">
|
||||
<div id="section_recommended" class="section-recommended">
|
||||
<div class="row">
|
||||
|
||||
<h2 class="text-center"><%= t("welcome.recommended.title") %></h2>
|
||||
<div class="small-12 column carousel-image">
|
||||
<div class="small-12 column">
|
||||
<h2><%= t("welcome.recommended.title") %></h2>
|
||||
<p><%= t("welcome.recommended.help") %></p>
|
||||
</div>
|
||||
|
||||
<div class="carousel-image">
|
||||
<% if recommended_debates.any? %>
|
||||
<% carousel_size = calculate_carousel_size(recommended_debates, recommended_proposals, true) %>
|
||||
<%= render "recommended_carousel", recommendeds: recommended_debates,
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<div class="small-12 medium-4 large-4 <%= carousel_size %> column text-center <%= key %> ">
|
||||
<div class="small-12 medium-6 column <%= key %> ">
|
||||
<div class="card small-centered <%= key %>-inner">
|
||||
|
||||
<h3><%= t("welcome.recommended.#{key.underscore}.title") %></h3>
|
||||
|
||||
<div class="orbit" role="region" data-orbit data-use-m-u-i="false">
|
||||
<div class="orbit" role="region" data-orbit data-use-m-u-i="false" data-timer-delay="4000">
|
||||
<div class="orbit-wrapper">
|
||||
|
||||
<ul class="orbit-container no-bullet" tabindex="0" >
|
||||
@@ -38,5 +38,5 @@
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<%= link_to btn_text_link, btn_path_link, class: 'button hollow expanded' %>
|
||||
<%= link_to btn_text_link, btn_path_link %>
|
||||
</div>
|
||||
|
||||
@@ -9,48 +9,25 @@
|
||||
social_url: root_url %>
|
||||
<% end %>
|
||||
|
||||
<div class="jumbo highlight">
|
||||
<%= render "header", header: @header %>
|
||||
|
||||
<main>
|
||||
<%= render "feeds" %>
|
||||
|
||||
|
||||
<div class="row">
|
||||
<div class="small-12 medium-9 small-centered column text-center">
|
||||
<h1><%= t("layouts.header.open_city_title") %></h1>
|
||||
<p class="lead">
|
||||
<%= t("layouts.header.open_city_slogan_html") %>
|
||||
</p>
|
||||
<div class="small-12 medium-6 large-4 small-centered">
|
||||
<%= link_to t("shared.more_info"), help_path, class: "button expanded large" %>
|
||||
</div>
|
||||
<div class="small-12 column <%= 'medium-8' if feed_processes_enabled? %>">
|
||||
<%= render "cards" %>
|
||||
</div>
|
||||
|
||||
<div class="small-12 medium-4 column">
|
||||
<%= render "processes" %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<% if feature?("user.recommendations") && (@recommended_debates.present? || @recommended_proposals.present?) %>
|
||||
<%= render "recommended",
|
||||
recommended_debates: @recommended_debates,
|
||||
recommended_proposals: @recommended_proposals %>
|
||||
<hr>
|
||||
<% end %>
|
||||
|
||||
<% cache [locale_and_user_status, @featured_debates, @featured_proposals, 'featured'] do %>
|
||||
<main>
|
||||
<div class="small-12 column text-center">
|
||||
<div class="row margin padding">
|
||||
<div class="small-12 medium-3 column">
|
||||
<h2><%= t("welcome.debates.title") %></h2>
|
||||
<p><%= t("welcome.debates.description") %></p>
|
||||
</div>
|
||||
<div class="small-12 medium-3 column">
|
||||
<h2><%= t("welcome.proposal.title") %></h2>
|
||||
<p><%= t("welcome.proposal.description") %></p>
|
||||
</div>
|
||||
<div class="small-12 medium-3 column">
|
||||
<h2><%= t("welcome.decide.title") %></h2>
|
||||
<p><%= t("welcome.decide.description") %></p>
|
||||
</div>
|
||||
<div class="small-12 medium-3 column">
|
||||
<h2><%= t("welcome.do.title") %></h2>
|
||||
<p><%= t("welcome.do.description") %></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
<% end %>
|
||||
<% if feature?("user.recommendations") && (@recommended_debates.present? || @recommended_proposals.present?) %>
|
||||
<%= render "recommended",
|
||||
recommended_debates: @recommended_debates,
|
||||
recommended_proposals: @recommended_proposals %>
|
||||
<% end %>
|
||||
</main>
|
||||
|
||||
@@ -143,6 +143,7 @@ ignore_unused:
|
||||
- 'admin.settings.index.features.*'
|
||||
- 'admin.polls.*.submit_button'
|
||||
- 'admin.booths.*.submit_button'
|
||||
- 'admin.homepage.*'
|
||||
- 'moderation.comments.index.filter*'
|
||||
- 'moderation.comments.index.order*'
|
||||
- 'moderation.debates.index.filter*'
|
||||
|
||||
@@ -245,6 +245,14 @@ en:
|
||||
subject: Subject
|
||||
from: From
|
||||
body: Email content
|
||||
widget/card:
|
||||
label: Label (optional)
|
||||
title: Title
|
||||
description: Description
|
||||
link_text: Link text
|
||||
link_url: Link URL
|
||||
widget/feed:
|
||||
limit: Number of items
|
||||
errors:
|
||||
models:
|
||||
user:
|
||||
|
||||
@@ -501,6 +501,7 @@ en:
|
||||
stats: Statistics
|
||||
signature_sheets: Signature Sheets
|
||||
site_customization:
|
||||
homepage: Homepage
|
||||
pages: Custom Pages
|
||||
images: Custom Images
|
||||
content_blocks: Custom content blocks
|
||||
@@ -962,6 +963,8 @@ en:
|
||||
actions: Actions
|
||||
title: Title
|
||||
description: Description
|
||||
image: Image
|
||||
show_image: Show image
|
||||
spending_proposals:
|
||||
index:
|
||||
geozone_filter_all: All zones
|
||||
@@ -1216,3 +1219,31 @@ en:
|
||||
status_draft: Draft
|
||||
status_published: Published
|
||||
locale: Language
|
||||
homepage:
|
||||
title: Homepage
|
||||
description: The active modules appear in the homepage in the same order as here.
|
||||
header_title: Header
|
||||
no_header: There is no header.
|
||||
create_header: Create header
|
||||
cards_title: Cards
|
||||
create_card: Create card
|
||||
no_cards: There is no cards.
|
||||
cards:
|
||||
title: Title
|
||||
description: Description
|
||||
link_text: Link text
|
||||
link_url: Link URL
|
||||
feeds:
|
||||
proposals: Proposals
|
||||
debates: Debates
|
||||
processes: Processes
|
||||
new:
|
||||
header_title: New header
|
||||
submit_header: Create header
|
||||
card_title: New card
|
||||
submit_card: Create card
|
||||
edit:
|
||||
header_title: Edit header
|
||||
submit_header: Save header
|
||||
card_title: Edit card
|
||||
submit_card: Save card
|
||||
|
||||
@@ -235,8 +235,6 @@ en:
|
||||
my_account_link: My account
|
||||
my_activity_link: My activity
|
||||
open: open
|
||||
open_city_slogan_html: There are cities that are governed directly by their inhabitants, who <b>discuss</b> the topics they are concerned about, <b>propose</b> ideas to improve their lives and <b>decide</b> among themselves which ones will be carried out.
|
||||
open_city_title: Love the city, and it will become a city you love
|
||||
open_gov: Open government
|
||||
proposals: Proposals
|
||||
poll_questions: Voting
|
||||
@@ -631,7 +629,6 @@ en:
|
||||
previous_slide: Previous Slide
|
||||
next_slide: Next Slide
|
||||
documentation: Additional documentation
|
||||
more_info: More information
|
||||
view_mode:
|
||||
title: View mode
|
||||
cards: Cards
|
||||
@@ -787,20 +784,21 @@ en:
|
||||
one: "You can only support investment projects in %{count} district"
|
||||
other: "You can only support investment projects in %{count} districts"
|
||||
welcome:
|
||||
debates:
|
||||
description: For meeting, discussing and sharing the things that matter to us in our city.
|
||||
title: Debates
|
||||
decide:
|
||||
description: The public decides if it accepts or rejects the most supported proposals.
|
||||
title: You decide
|
||||
do:
|
||||
description: If the proposal is accepted by the majority, the City Council accepts it as its own and it gets done.
|
||||
title: It gets done
|
||||
proposal:
|
||||
description: Open space for citizen proposals about the kind of city we want to live in.
|
||||
title: You propose
|
||||
feed:
|
||||
most_active:
|
||||
debates: "Most active debates"
|
||||
proposals: "Most active proposals"
|
||||
processes: "Open processes"
|
||||
see_all_debates: See all debates
|
||||
see_all_proposals: See all proposals
|
||||
see_all_processes: See all processes
|
||||
process_label: Process
|
||||
see_process: See process
|
||||
cards:
|
||||
title: Featured
|
||||
recommended:
|
||||
title: Recommendations that may interest you
|
||||
help: "These recommendations are generated by the tags of the debates and proposals you are following."
|
||||
debates:
|
||||
title: Recommended debates
|
||||
btn_text_link: All recommended debates
|
||||
@@ -845,3 +843,6 @@ en:
|
||||
proposal: "Proposal"
|
||||
debate: "Debate"
|
||||
budget_investment: "Budget investment"
|
||||
admin/widget:
|
||||
header:
|
||||
title: Administration
|
||||
@@ -41,7 +41,7 @@ en:
|
||||
voting_allowed: Voting on investment projects
|
||||
legislation: Legislation
|
||||
user:
|
||||
recommendations: Recommendeds
|
||||
recommendations: Recommendations
|
||||
skip_verification: Skip user verification
|
||||
community: Community on proposals and investments
|
||||
map: Proposals and budget investments geolocation
|
||||
|
||||
@@ -241,6 +241,14 @@ es:
|
||||
subject: Asunto
|
||||
from: Enviado por
|
||||
body: Contenido del email
|
||||
widget/card:
|
||||
label: Etiqueta (opcional)
|
||||
title: Título
|
||||
description: Descripción
|
||||
link_text: Texto del enlace
|
||||
link_url: URL del enlace
|
||||
widget/feed:
|
||||
limit: Número de elementos
|
||||
errors:
|
||||
models:
|
||||
user:
|
||||
|
||||
@@ -501,6 +501,7 @@ es:
|
||||
stats: Estadísticas
|
||||
signature_sheets: Hojas de firmas
|
||||
site_customization:
|
||||
homepage: Homepage
|
||||
pages: Personalizar páginas
|
||||
images: Personalizar imágenes
|
||||
content_blocks: Personalizar bloques
|
||||
@@ -962,6 +963,8 @@ es:
|
||||
actions: Acciones
|
||||
title: Título
|
||||
description: Descripción
|
||||
image: Imagen
|
||||
show_image: Mostrar imagen
|
||||
spending_proposals:
|
||||
index:
|
||||
geozone_filter_all: Todos los ámbitos de actuación
|
||||
@@ -1216,3 +1219,31 @@ es:
|
||||
status_draft: Borrador
|
||||
status_published: Publicada
|
||||
locale: Idioma
|
||||
homepage:
|
||||
title: Homepage
|
||||
description: Los módulos activos aparecerán en la homepage en el mismo orden que aquí.
|
||||
header_title: Encabezado
|
||||
create_header: Crear encabezado
|
||||
no_header: No hay encabezado.
|
||||
cards_title: Tarjetas
|
||||
create_card: Crear tarjeta
|
||||
no_cards: No hay tarjetas.
|
||||
cards:
|
||||
title: Título
|
||||
description: Descripción
|
||||
link_text: Texto del enlace
|
||||
link_url: URL del enlace
|
||||
feeds:
|
||||
proposals: Propuestas
|
||||
debates: Debates
|
||||
processes: Procesos
|
||||
new:
|
||||
header_title: Nuevo encabezado
|
||||
submit_header: Crear encabezado
|
||||
card_title: Nueva tarjeta
|
||||
submit_card: Crear tarjeta
|
||||
edit:
|
||||
header_title: Editar encabezado
|
||||
submit_header: Guardar encabezado
|
||||
card_title: Editar tarjeta
|
||||
submit_card: Guardar tarjeta
|
||||
|
||||
@@ -235,8 +235,6 @@ es:
|
||||
my_account_link: Mi cuenta
|
||||
my_activity_link: Mi actividad
|
||||
open: abierto
|
||||
open_city_slogan_html: Existen ciudades gobernadas directamente por sus habitantes, que <strong>debaten</strong> sobre temas que les preocupan, <strong>proponen</strong> ideas para mejorar sus vidas y <strong>deciden</strong> entre todas y todos las que se llevan a cabo.
|
||||
open_city_title: La ciudad que quieres será la ciudad que quieras
|
||||
open_gov: Gobierno %{open}
|
||||
proposals: Propuestas
|
||||
poll_questions: Votaciones
|
||||
@@ -630,7 +628,6 @@ es:
|
||||
previous_slide: Imagen anterior
|
||||
next_slide: Siguiente imagen
|
||||
documentation: Documentación adicional
|
||||
more_info: Más información
|
||||
view_mode:
|
||||
title: Modo de vista
|
||||
cards: Tarjetas
|
||||
@@ -786,20 +783,21 @@ es:
|
||||
one: "Sólo puedes apoyar proyectos de gasto de %{count} distrito"
|
||||
other: "Sólo puedes apoyar proyectos de gasto de %{count} distritos"
|
||||
welcome:
|
||||
debates:
|
||||
description: Encontrarnos, debatir y compartir lo que nos parece importante en nuestra ciudad.
|
||||
title: Debates
|
||||
decide:
|
||||
description: La ciudadanía decide si acepta o rechaza las propuestas más apoyadas.
|
||||
title: Decides
|
||||
do:
|
||||
description: Si la propuesta es aceptada mayoritariamente, el Ayuntamiento la asume como propia y se hace.
|
||||
title: Se hace
|
||||
proposal:
|
||||
description: Espacio abierto para propuestas ciudadanas sobre el tipo de ciudad en el que queremos vivir.
|
||||
title: Propones
|
||||
feed:
|
||||
most_active:
|
||||
debates: "Debates más activos"
|
||||
proposals: "Propuestas más activas"
|
||||
processes: "Procesos abiertos"
|
||||
see_all_debates: Ver todos los debates
|
||||
see_all_proposals: Ver todas las propuestas
|
||||
see_all_processes: Ver todos los procesos
|
||||
process_label: Proceso
|
||||
see_process: Ver proceso
|
||||
cards:
|
||||
title: Destacados
|
||||
recommended:
|
||||
title: Recomendaciones que te pueden interesar
|
||||
help: "Estas recomendaciones se generan por las etiquetas de los debates y propuestas que estás siguiendo."
|
||||
debates:
|
||||
title: Debates recomendados
|
||||
btn_text_link: Todos los debates recomendados
|
||||
@@ -844,3 +842,6 @@ es:
|
||||
proposal: "Propuesta"
|
||||
debate: "Debate"
|
||||
budget_investment: "Proyecto de gasto"
|
||||
admin/widget:
|
||||
header:
|
||||
title: Administración
|
||||
@@ -179,4 +179,11 @@ namespace :admin do
|
||||
resources :images, only: [:index, :update, :destroy]
|
||||
resources :content_blocks, except: [:show]
|
||||
end
|
||||
|
||||
resource :homepage, controller: :homepage, only: [:show]
|
||||
|
||||
namespace :widget do
|
||||
resources :cards
|
||||
resources :feeds, only: [:update]
|
||||
end
|
||||
end
|
||||
|
||||
@@ -63,4 +63,8 @@ section "Creating Settings" do
|
||||
Setting.create(key: 'map_longitude', value: -3.7)
|
||||
Setting.create(key: 'map_zoom', value: 10)
|
||||
Setting.create(key: 'related_content_score_threshold', value: -0.3)
|
||||
|
||||
Setting['feature.homepage.widgets.feeds.proposals'] = true
|
||||
Setting['feature.homepage.widgets.feeds.debates'] = true
|
||||
Setting['feature.homepage.widgets.feeds.processes'] = true
|
||||
end
|
||||
|
||||
13
db/migrate/20180516091302_create_widget_cards.rb
Normal file
13
db/migrate/20180516091302_create_widget_cards.rb
Normal file
@@ -0,0 +1,13 @@
|
||||
class CreateWidgetCards < ActiveRecord::Migration
|
||||
def change
|
||||
create_table :widget_cards do |t|
|
||||
t.string :title
|
||||
t.text :description
|
||||
t.string :link_text
|
||||
t.string :link_url
|
||||
t.string :label
|
||||
t.boolean :header, default: false
|
||||
t.timestamps null: false
|
||||
end
|
||||
end
|
||||
end
|
||||
9
db/migrate/20180519132610_create_widget_feeds.rb
Normal file
9
db/migrate/20180519132610_create_widget_feeds.rb
Normal file
@@ -0,0 +1,9 @@
|
||||
class CreateWidgetFeeds < ActiveRecord::Migration
|
||||
def change
|
||||
create_table :widget_feeds do |t|
|
||||
t.string :kind
|
||||
t.integer :limit, default: 3
|
||||
t.timestamps null: false
|
||||
end
|
||||
end
|
||||
end
|
||||
20
db/schema.rb
20
db/schema.rb
@@ -11,7 +11,7 @@
|
||||
#
|
||||
# It's strongly recommended that you check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema.define(version: 20180323190027) do
|
||||
ActiveRecord::Schema.define(version: 20180519132610) do
|
||||
|
||||
# These are extensions that must be enabled in order to support this database
|
||||
enable_extension "plpgsql"
|
||||
@@ -1223,6 +1223,24 @@ ActiveRecord::Schema.define(version: 20180323190027) do
|
||||
add_index "votes", ["votable_id", "votable_type", "vote_scope"], name: "index_votes_on_votable_id_and_votable_type_and_vote_scope", using: :btree
|
||||
add_index "votes", ["voter_id", "voter_type", "vote_scope"], name: "index_votes_on_voter_id_and_voter_type_and_vote_scope", using: :btree
|
||||
|
||||
create_table "widget_cards", force: :cascade do |t|
|
||||
t.string "title"
|
||||
t.text "description"
|
||||
t.string "link_text"
|
||||
t.string "link_url"
|
||||
t.string "label"
|
||||
t.boolean "header", default: false
|
||||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
end
|
||||
|
||||
create_table "widget_feeds", force: :cascade do |t|
|
||||
t.string "kind"
|
||||
t.integer "limit", default: 3
|
||||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
end
|
||||
|
||||
add_foreign_key "administrators", "users"
|
||||
add_foreign_key "annotations", "legacy_legislations"
|
||||
add_foreign_key "annotations", "users"
|
||||
|
||||
@@ -125,3 +125,8 @@ Setting['map_zoom'] = 10
|
||||
Setting['related_content_score_threshold'] = -0.3
|
||||
|
||||
Setting["feature.user.skip_verification"] = 'true'
|
||||
|
||||
Setting['feature.homepage.widgets.feeds.proposals'] = true
|
||||
Setting['feature.homepage.widgets.feeds.debates'] = true
|
||||
Setting['feature.homepage.widgets.feeds.processes'] = true
|
||||
|
||||
|
||||
14
lib/tasks/homepage.rake
Normal file
14
lib/tasks/homepage.rake
Normal file
@@ -0,0 +1,14 @@
|
||||
namespace :homepage do
|
||||
|
||||
desc "Initialize feeds available in homepage"
|
||||
task create_feeds: :environment do
|
||||
%w(proposals debates processes).each do |kind|
|
||||
Widget::Feed.create(kind: kind)
|
||||
|
||||
Setting['feature.homepage.widgets.feeds.proposals'] = true
|
||||
Setting['feature.homepage.widgets.feeds.debates'] = true
|
||||
Setting['feature.homepage.widgets.feeds.processes'] = true
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
@@ -814,10 +814,19 @@ FactoryBot.define do
|
||||
result_publication_date Date.current + 5.days
|
||||
end
|
||||
|
||||
trait :published do
|
||||
published true
|
||||
end
|
||||
|
||||
trait :not_published do
|
||||
published false
|
||||
end
|
||||
|
||||
trait :open do
|
||||
start_date 1.week.ago
|
||||
end_date 1.week.from_now
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
factory :legislation_draft_version, class: 'Legislation::DraftVersion' do
|
||||
@@ -962,4 +971,25 @@ LOREM_IPSUM
|
||||
sequence(:body) { |n| "Body #{n}" }
|
||||
end
|
||||
|
||||
factory :widget_card, class: 'Widget::Card' do
|
||||
sequence(:title) { |n| "Title #{n}" }
|
||||
sequence(:description) { |n| "Description #{n}" }
|
||||
sequence(:link_text) { |n| "Link text #{n}" }
|
||||
sequence(:link_url) { |n| "Link url #{n}" }
|
||||
|
||||
trait :header do
|
||||
header true
|
||||
sequence(:button_text) { |n| "Button text #{n}" }
|
||||
sequence(:button_url) { |n| "Button url #{n}" }
|
||||
sequence(:alignment) { |n| "background" }
|
||||
end
|
||||
|
||||
after :create do |widget_card|
|
||||
create(:image, imageable: widget_card)
|
||||
end
|
||||
end
|
||||
|
||||
factory :widget_feed, class: 'Widget::Feed' do
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
131
spec/features/admin/homepage/homepage_spec.rb
Normal file
131
spec/features/admin/homepage/homepage_spec.rb
Normal file
@@ -0,0 +1,131 @@
|
||||
require 'rails_helper'
|
||||
|
||||
feature 'Homepage' do
|
||||
|
||||
background do
|
||||
admin = create(:administrator).user
|
||||
login_as(admin)
|
||||
|
||||
Setting['feature.homepage.widgets.feeds.proposals'] = false
|
||||
Setting['feature.homepage.widgets.feeds.debates'] = false
|
||||
Setting['feature.homepage.widgets.feeds.processes'] = false
|
||||
Setting['feature.user.recommendations'] = false
|
||||
end
|
||||
|
||||
let!(:proposals_feed) { create(:widget_feed, kind: "proposals") }
|
||||
let!(:debates_feed) { create(:widget_feed, kind: "debates") }
|
||||
let!(:processes_feed) { create(:widget_feed, kind: "processes") }
|
||||
|
||||
let(:user_recommendations) { Setting.where(key: 'feature.user.recommendations').first }
|
||||
let(:user) { create(:user) }
|
||||
|
||||
scenario "Header" do
|
||||
end
|
||||
|
||||
context "Feeds" do
|
||||
|
||||
scenario "Proposals", :js do
|
||||
5.times { create(:proposal) }
|
||||
|
||||
visit admin_homepage_path
|
||||
|
||||
within("#widget_feed_#{proposals_feed.id}") do
|
||||
select '1', from: 'widget_feed_limit'
|
||||
accept_confirm { click_button "Enable" }
|
||||
end
|
||||
|
||||
visit root_path
|
||||
|
||||
expect(page).to have_content "Most active proposals"
|
||||
expect(page).to have_css(".proposal", count: 1)
|
||||
end
|
||||
|
||||
scenario "Debates", :js do
|
||||
5.times { create(:debate) }
|
||||
|
||||
visit admin_homepage_path
|
||||
within("#widget_feed_#{debates_feed.id}") do
|
||||
select '2', from: 'widget_feed_limit'
|
||||
accept_confirm { click_button "Enable" }
|
||||
end
|
||||
|
||||
visit root_path
|
||||
|
||||
expect(page).to have_content "Most active debates"
|
||||
expect(page).to have_css(".debate", count: 2)
|
||||
end
|
||||
|
||||
scenario "Processes", :js do
|
||||
5.times { create(:legislation_process) }
|
||||
|
||||
visit admin_homepage_path
|
||||
within("#widget_feed_#{processes_feed.id}") do
|
||||
select '3', from: 'widget_feed_limit'
|
||||
accept_confirm { click_button "Enable" }
|
||||
end
|
||||
|
||||
visit root_path
|
||||
|
||||
expect(page).to have_content "Open processes"
|
||||
expect(page).to have_css(".legislation_process", count: 3)
|
||||
end
|
||||
|
||||
xscenario "Deactivate"
|
||||
|
||||
end
|
||||
|
||||
scenario "Cards" do
|
||||
card1 = create(:widget_card, label: "Card1 label",
|
||||
title: "Card1 text",
|
||||
description: "Card1 description",
|
||||
link_text: "Link1 text",
|
||||
link_url: "consul1.dev")
|
||||
|
||||
card2 = create(:widget_card, label: "Card2 label",
|
||||
title: "Card2 text",
|
||||
description: "Card2 description",
|
||||
link_text: "Link2 text",
|
||||
link_url: "consul2.dev")
|
||||
|
||||
visit root_path
|
||||
|
||||
expect(page).to have_css(".card", count: 2)
|
||||
|
||||
within("#widget_card_#{card1.id}") do
|
||||
expect(page).to have_content("Card1 label")
|
||||
expect(page).to have_content("Card1 text")
|
||||
expect(page).to have_content("Card1 description")
|
||||
expect(page).to have_content("Link1 text")
|
||||
expect(page).to have_link(href: "consul1.dev")
|
||||
expect(page).to have_css("img[alt='#{card1.image.title}']")
|
||||
end
|
||||
|
||||
within("#widget_card_#{card2.id}") do
|
||||
expect(page).to have_content("Card2 label")
|
||||
expect(page).to have_content("Card2 text")
|
||||
expect(page).to have_content("Card2 description")
|
||||
expect(page).to have_content("Link2 text")
|
||||
expect(page).to have_link(href: "consul2.dev")
|
||||
expect(page).to have_css("img[alt='#{card2.image.title}']")
|
||||
end
|
||||
end
|
||||
|
||||
scenario "Recomendations" do
|
||||
proposal1 = create(:proposal, tag_list: "Sport")
|
||||
proposal2 = create(:proposal, tag_list: "Sport")
|
||||
create(:follow, followable: proposal1, user: user)
|
||||
|
||||
visit admin_homepage_path
|
||||
within("#setting_#{user_recommendations.id}") do
|
||||
click_button "Enable"
|
||||
end
|
||||
|
||||
expect(page).to have_content "Value updated"
|
||||
|
||||
login_as(user)
|
||||
visit root_path
|
||||
|
||||
expect(page).to have_content("Recommendations that may interest you")
|
||||
end
|
||||
|
||||
end
|
||||
138
spec/features/admin/widgets/cards_spec.rb
Normal file
138
spec/features/admin/widgets/cards_spec.rb
Normal file
@@ -0,0 +1,138 @@
|
||||
require 'rails_helper'
|
||||
|
||||
feature 'Cards' do
|
||||
|
||||
background do
|
||||
admin = create(:administrator).user
|
||||
login_as(admin)
|
||||
end
|
||||
|
||||
scenario "Create", :js do
|
||||
visit admin_homepage_path
|
||||
click_link "Create card"
|
||||
|
||||
fill_in "widget_card_label", with: "Card label"
|
||||
fill_in "widget_card_title", with: "Card text"
|
||||
fill_in "widget_card_description", with: "Card description"
|
||||
fill_in "widget_card_link_text", with: "Link text"
|
||||
fill_in "widget_card_link_url", with: "consul.dev"
|
||||
attach_image_to_card
|
||||
click_button "Create card"
|
||||
|
||||
expect(page).to have_content "Success"
|
||||
expect(page).to have_css(".homepage-card", count: 1)
|
||||
|
||||
card = Widget::Card.last
|
||||
within("#widget_card_#{card.id}") do
|
||||
expect(page).to have_content "Card label"
|
||||
expect(page).to have_content "Card text"
|
||||
expect(page).to have_content "Card description"
|
||||
expect(page).to have_content "Link text"
|
||||
expect(page).to have_content "consul.dev"
|
||||
expect(page).to have_link("Show image", href: card.image_url(:large))
|
||||
end
|
||||
end
|
||||
|
||||
scenario "Index" do
|
||||
3.times { create(:widget_card) }
|
||||
|
||||
visit admin_homepage_path
|
||||
|
||||
expect(page).to have_css(".homepage-card", count: 3)
|
||||
|
||||
cards = Widget::Card.all
|
||||
cards.each do |card|
|
||||
expect(page).to have_content card.title
|
||||
expect(page).to have_content card.description
|
||||
expect(page).to have_content card.link_text
|
||||
expect(page).to have_content card.link_url
|
||||
expect(page).to have_link("Show image", href: card.image_url(:large))
|
||||
end
|
||||
end
|
||||
|
||||
scenario "Edit" do
|
||||
card = create(:widget_card)
|
||||
|
||||
visit admin_homepage_path
|
||||
|
||||
within("#widget_card_#{card.id}") do
|
||||
click_link "Edit"
|
||||
end
|
||||
|
||||
fill_in "widget_card_label", with: "Card label updated"
|
||||
fill_in "widget_card_title", with: "Card text updated"
|
||||
fill_in "widget_card_description", with: "Card description updated"
|
||||
fill_in "widget_card_link_text", with: "Link text updated"
|
||||
fill_in "widget_card_link_url", with: "consul.dev updated"
|
||||
click_button "Save card"
|
||||
|
||||
expect(page).to have_content "Updated"
|
||||
|
||||
expect(page).to have_css(".homepage-card", count: 1)
|
||||
within("#widget_card_#{Widget::Card.last.id}") do
|
||||
expect(page).to have_content "Card label updated"
|
||||
expect(page).to have_content "Card text updated"
|
||||
expect(page).to have_content "Card description updated"
|
||||
expect(page).to have_content "Link text updated"
|
||||
expect(page).to have_content "consul.dev updated"
|
||||
end
|
||||
end
|
||||
|
||||
scenario "Remove", :js do
|
||||
card = create(:widget_card)
|
||||
|
||||
visit admin_homepage_path
|
||||
|
||||
within("#widget_card_#{card.id}") do
|
||||
accept_confirm do
|
||||
click_link "Delete"
|
||||
end
|
||||
end
|
||||
|
||||
expect(page).to have_content "Removed"
|
||||
expect(page).to have_css(".homepage-card", count: 0)
|
||||
end
|
||||
|
||||
context "Header Card" do
|
||||
|
||||
scenario "Create" do
|
||||
visit admin_homepage_path
|
||||
click_link "Create header"
|
||||
|
||||
fill_in "widget_card_label", with: "Header label"
|
||||
fill_in "widget_card_title", with: "Header text"
|
||||
fill_in "widget_card_description", with: "Header description"
|
||||
fill_in "widget_card_link_text", with: "Link text"
|
||||
fill_in "widget_card_link_url", with: "consul.dev"
|
||||
click_button "Create header"
|
||||
|
||||
expect(page).to have_content "Success"
|
||||
|
||||
within("#header") do
|
||||
expect(page).to have_css(".homepage-card", count: 1)
|
||||
expect(page).to have_content "Header label"
|
||||
expect(page).to have_content "Header text"
|
||||
expect(page).to have_content "Header description"
|
||||
expect(page).to have_content "Link text"
|
||||
expect(page).to have_content "consul.dev"
|
||||
end
|
||||
|
||||
within("#cards") do
|
||||
expect(page).to have_css(".homepage-card", count: 0)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
pending "add image expectactions"
|
||||
|
||||
def attach_image_to_card
|
||||
click_link "Add image"
|
||||
image_input = all(".image").last.find("input[type=file]", visible: false)
|
||||
attach_file(
|
||||
image_input[:id],
|
||||
Rails.root.join('spec/fixtures/files/clippy.jpg'),
|
||||
make_visible: true)
|
||||
expect(page).to have_field('widget_card_image_attributes_title', with: "clippy.jpg")
|
||||
end
|
||||
end
|
||||
@@ -2,12 +2,12 @@ require 'rails_helper'
|
||||
|
||||
feature "Home" do
|
||||
|
||||
feature "For not logged users" do
|
||||
context "For not logged users" do
|
||||
|
||||
scenario 'Welcome message' do
|
||||
visit root_path
|
||||
|
||||
expect(page).to have_content "Love the city, and it will become a city you love"
|
||||
expect(page).to have_content "CONSUL"
|
||||
end
|
||||
|
||||
scenario 'Not display recommended section' do
|
||||
@@ -20,7 +20,7 @@ feature "Home" do
|
||||
|
||||
end
|
||||
|
||||
feature "For signed in users" do
|
||||
context "For signed in users" do
|
||||
|
||||
feature "Recommended" do
|
||||
|
||||
@@ -95,7 +95,10 @@ feature "Home" do
|
||||
debate = create(:debate, tag_list: "Sport")
|
||||
|
||||
visit root_path
|
||||
click_on debate.title
|
||||
|
||||
within('#section_recommended') do
|
||||
click_on debate.title
|
||||
end
|
||||
|
||||
expect(page).to have_current_path(debate_path(debate))
|
||||
end
|
||||
@@ -104,29 +107,6 @@ feature "Home" do
|
||||
visit root_path
|
||||
expect(page).not_to have_content "Recommendations that may interest you"
|
||||
end
|
||||
|
||||
feature 'Carousel size' do
|
||||
|
||||
scenario 'Display debates centered when there are no proposals' do
|
||||
debate = create(:debate, tag_list: "Sport")
|
||||
visit root_path
|
||||
expect(page).to have_selector('.medium-centered.large-centered')
|
||||
end
|
||||
|
||||
scenario 'Correct display debates and proposals' do
|
||||
proposal = create(:proposal, tag_list: "Sport")
|
||||
debates = create(:debate, tag_list: "Sport")
|
||||
|
||||
visit root_path
|
||||
|
||||
expect(page).to have_selector('.debates.medium-offset-2.large-offset-2')
|
||||
expect(page).not_to have_selector('.proposals.medium-offset-2.large-offset-2')
|
||||
expect(page).not_to have_selector('.debates.end')
|
||||
expect(page).to have_selector('.proposals.end')
|
||||
expect(page).not_to have_selector('.medium-centered.large-centered')
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -3,10 +3,16 @@ require 'rails_helper'
|
||||
feature 'Localization' do
|
||||
|
||||
scenario 'Wrong locale' do
|
||||
card = create(:widget_card, title: 'Bienvenido a CONSUL',
|
||||
description: 'Software libre para la participación ciudadana.',
|
||||
link_text: 'Más información',
|
||||
link_url: 'http://consulproject.org/',
|
||||
header: true)
|
||||
|
||||
visit root_path(locale: :es)
|
||||
visit root_path(locale: :klingon)
|
||||
|
||||
expect(page).to have_text('La ciudad que quieres será la ciudad que quieras')
|
||||
expect(page).to have_text('Bienvenido a CONSUL')
|
||||
end
|
||||
|
||||
scenario 'Available locales appear in the locale switcher' do
|
||||
|
||||
37
spec/models/widget/card_spec.rb
Normal file
37
spec/models/widget/card_spec.rb
Normal file
@@ -0,0 +1,37 @@
|
||||
require 'rails_helper'
|
||||
|
||||
describe Widget::Card do
|
||||
|
||||
let(:card) { build(:widget_card) }
|
||||
|
||||
context "validations" do
|
||||
|
||||
it "is valid" do
|
||||
expect(card).to be_valid
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
describe "#header" do
|
||||
|
||||
it "returns the header card" do
|
||||
header = create(:widget_card, header: true)
|
||||
card = create(:widget_card, header: false)
|
||||
|
||||
expect(Widget::Card.header).to eq([header])
|
||||
end
|
||||
end
|
||||
|
||||
describe "#body" do
|
||||
|
||||
it "returns cards for the homepage body" do
|
||||
header = create(:widget_card, header: true)
|
||||
card1 = create(:widget_card, header: false, title: "Card 1")
|
||||
card2 = create(:widget_card, header: false, title: "Card 2")
|
||||
card3 = create(:widget_card, header: false, title: "Card 3")
|
||||
|
||||
expect(Widget::Card.body).to eq([card1, card2, card3])
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
78
spec/models/widget/feed_spec.rb
Normal file
78
spec/models/widget/feed_spec.rb
Normal file
@@ -0,0 +1,78 @@
|
||||
require 'rails_helper'
|
||||
|
||||
describe Widget::Feed do
|
||||
|
||||
let(:feed) { build(:widget_feed) }
|
||||
|
||||
context "validations" do
|
||||
it "is valid" do
|
||||
expect(feed).to be_valid
|
||||
end
|
||||
end
|
||||
|
||||
context "kinds" do
|
||||
|
||||
describe "#proposals" do
|
||||
|
||||
it "returns the most active proposals" do
|
||||
best_proposal = create(:proposal, title: 'Best proposal')
|
||||
best_proposal.update_column(:hot_score, 10)
|
||||
|
||||
worst_proposal = create(:proposal, title: 'Worst proposal')
|
||||
worst_proposal.update_column(:hot_score, 2)
|
||||
|
||||
even_worst_proposal = create(:proposal, title: 'Worst proposal')
|
||||
even_worst_proposal.update_column(:hot_score, 1)
|
||||
|
||||
medium_proposal = create(:proposal, title: 'Medium proposal')
|
||||
medium_proposal.update_column(:hot_score, 5)
|
||||
|
||||
feed = build(:widget_feed, kind: "proposals")
|
||||
|
||||
expect(feed.proposals).to eq([best_proposal, medium_proposal, worst_proposal])
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
describe "#debates" do
|
||||
|
||||
it "returns the most active debates" do
|
||||
best_debate = create(:debate, title: 'Best debate')
|
||||
best_debate.update_column(:hot_score, 10)
|
||||
|
||||
worst_debate = create(:debate, title: 'Worst debate')
|
||||
worst_debate.update_column(:hot_score, 2)
|
||||
|
||||
even_worst_debate = create(:debate, title: 'Worst debate')
|
||||
even_worst_debate.update_column(:hot_score, 1)
|
||||
|
||||
medium_debate = create(:debate, title: 'Medium debate')
|
||||
medium_debate.update_column(:hot_score, 5)
|
||||
|
||||
feed = build(:widget_feed, kind: "debates")
|
||||
|
||||
expect(feed.debates).to eq([best_debate, medium_debate, worst_debate])
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
describe "#processes" do
|
||||
|
||||
it "returns open and published processes" do
|
||||
open_process1 = create(:legislation_process, :open, :published, title: "Open process 1")
|
||||
open_process2 = create(:legislation_process, :open, :published, title: "Open process 2")
|
||||
open_process3 = create(:legislation_process, :open, :published, title: "Open process 3")
|
||||
open_process4 = create(:legislation_process, :open, :published, title: "Open process 4")
|
||||
open_process5 = create(:legislation_process, :open, :not_published, title: "Open process 5")
|
||||
past_process = create(:legislation_process, :past, title: "Past process")
|
||||
|
||||
feed = build(:widget_feed, kind: "processes")
|
||||
|
||||
expect(feed.processes).to eq([open_process4, open_process3, open_process2])
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
Reference in New Issue
Block a user